From 3f92e52615ce798074e652383f3184b8c36a20f9 Mon Sep 17 00:00:00 2001 From: Daniel Schaal Date: Sat, 17 Dec 2016 08:27:18 +0100 Subject: [PATCH 001/700] Add option to run upsmon in foreground --- clients/upsmon.c | 15 +++++++++++---- docs/man/upsmon.txt | 3 +++ scripts/systemd/nut-monitor.service.in | 5 ++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index 12823fa1ee..c0e83a0ec5 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -1645,6 +1645,7 @@ static void help(const char *progname) printf(" - reload: reread configuration\n"); printf(" - stop: stop monitoring and exit\n"); printf(" -D raise debugging level\n"); + printf(" -F run in foreground\n"); printf(" -h display this help\n"); printf(" -K checks POWERDOWNFLAG, sets exit code to 0 if set\n"); printf(" -p always run privileged (disable privileged parent)\n"); @@ -1859,7 +1860,7 @@ static void check_parent(void) int main(int argc, char *argv[]) { const char *prog = xbasename(argv[0]); - int i, cmd = 0, checking_flag = 0; + int i, cmd = 0, checking_flag = 0, foreground = 0; printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); @@ -1870,7 +1871,7 @@ int main(int argc, char *argv[]) run_as_user = xstrdup(RUN_AS_USER); - while ((i = getopt(argc, argv, "+Dhic:f:pu:VK46")) != -1) { + while ((i = getopt(argc, argv, "+DFhic:f:pu:VK46")) != -1) { switch (i) { case 'c': if (!strncmp(optarg, "fsd", strlen(optarg))) @@ -1886,6 +1887,10 @@ int main(int argc, char *argv[]) break; case 'D': nut_debug_level++; + foreground = 1; + break; + case 'F': + foreground = 1; break; case 'f': free(configfile); @@ -1962,9 +1967,11 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - if (nut_debug_level < 1) { + if (!foreground) { background(); - } else { + } + + if(nut_debug_level >= 1) { upsdebugx(1, "debug level is '%d'", nut_debug_level); } diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index a330644216..2ddaf08bdf 100644 --- a/docs/man/upsmon.txt +++ b/docs/man/upsmon.txt @@ -49,6 +49,9 @@ Raise the debugging level. upsmon will run in the foreground and prints information on stdout about the monitoring process. Use this multiple times for more details. +*-F*:: +upsmon will run in the foreground. + *-K*:: Test for the shutdown flag. If it exists and contains the magic string from upsmon, then upsmon will exit with `EXIT_SUCCESS`. Any other condition diff --git a/scripts/systemd/nut-monitor.service.in b/scripts/systemd/nut-monitor.service.in index 8429bf280c..5086ae860a 100644 --- a/scripts/systemd/nut-monitor.service.in +++ b/scripts/systemd/nut-monitor.service.in @@ -3,9 +3,8 @@ Description=Network UPS Tools - power device monitor and shutdown controller After=local-fs.target network.target nut-server.service [Service] -ExecStart=@SBINDIR@/upsmon -PIDFile=@PIDPATH@/upsmon.pid -Type=forking +ExecStart=@SBINDIR@/upsmon -F +ExecReload=@SBINDIR@/upsmon -c reload [Install] WantedBy=multi-user.target From dd1bfaf98091187c1b450ef3a23437110d976055 Mon Sep 17 00:00:00 2001 From: Daniel Schaal Date: Thu, 2 Feb 2017 17:12:21 +0100 Subject: [PATCH 002/700] Add foreground option to upsd --- docs/man/upsd.txt | 3 +++ scripts/systemd/nut-server.service.in | 3 +-- server/upsd.c | 12 ++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/man/upsd.txt b/docs/man/upsd.txt index 1fddadb1d5..e21a261e79 100644 --- a/docs/man/upsd.txt +++ b/docs/man/upsd.txt @@ -46,6 +46,9 @@ are: *-D*:: Raise the debug level. Use this multiple times for additional details. +*-F*:: +Run in foreground. + *-h*:: Display the help text. diff --git a/scripts/systemd/nut-server.service.in b/scripts/systemd/nut-server.service.in index 7a786701f8..d0ab6e5700 100644 --- a/scripts/systemd/nut-server.service.in +++ b/scripts/systemd/nut-server.service.in @@ -8,9 +8,8 @@ Wants=nut-driver.service Before=nut-monitor.service [Service] -ExecStart=@SBINDIR@/upsd +ExecStart=@SBINDIR@/upsd -F ExecReload=@SBINDIR@/upsd -c reload -Type=forking [Install] WantedBy=multi-user.target diff --git a/server/upsd.c b/server/upsd.c index c6efb217b8..20a5d1d61a 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -827,6 +827,7 @@ static void help(const char *progname) printf(" - reload: reread configuration files\n"); printf(" - stop: stop process and exit\n"); printf(" -D raise debugging level\n"); + printf(" -F run in foreground\n"); printf(" -h display this help\n"); printf(" -r chroots to \n"); printf(" -q raise log level threshold\n"); @@ -890,7 +891,7 @@ void check_perms(const char *fn) int main(int argc, char **argv) { - int i, cmd = 0; + int i, cmd = 0, foreground = 0; char *chroot_path = NULL; const char *user = RUN_AS_USER; struct passwd *new_uid = NULL; @@ -906,7 +907,7 @@ int main(int argc, char **argv) printf("Network UPS Tools %s %s\n", progname, UPS_VERSION); - while ((i = getopt(argc, argv, "+h46p:qr:i:fu:Vc:D")) != -1) { + while ((i = getopt(argc, argv, "+h46p:qr:i:fu:Vc:DF")) != -1) { switch (i) { case 'h': help(progname); @@ -942,8 +943,11 @@ int main(int argc, char **argv) case 'D': nut_debug_level++; + foreground = 1; + break; + case 'F': + foreground = 1; break; - case '4': opt_af = AF_INET; break; @@ -1028,7 +1032,7 @@ int main(int argc, char **argv) /* handle upsd.users */ user_load(); - if (!nut_debug_level) { + if (!foreground) { background(); writepid(pidfn); } else { From dd3549a7893b8000197149e93c48cb5c73ddb9ad Mon Sep 17 00:00:00 2001 From: Daniel Schaal Date: Thu, 2 Feb 2017 17:19:38 +0100 Subject: [PATCH 003/700] Add foreground option to drivers --- drivers/main.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index 6c0f40af7b..f07a79a6d6 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -104,6 +104,7 @@ static void help_msg(void) printf(" -L - print parseable list of driver variables\n"); printf(" -D - raise debugging level\n"); printf(" -d - dump data to stdout after 'count' updates loop and exit\n"); + printf(" -F - run in foreground\n"); printf(" -q - raise log level threshold\n"); printf(" -h - display this help\n"); printf(" -k - force shutdown\n"); @@ -499,6 +500,7 @@ int main(int argc, char **argv) struct passwd *new_uid = NULL; int i, do_forceshutdown = 0; int update_count = 0; + int foreground = 0; atexit(exit_cleanup); @@ -518,7 +520,7 @@ int main(int argc, char **argv) /* build the driver's extra (-x) variable table */ upsdrv_makevartable(); - while ((i = getopt(argc, argv, "+a:s:kDd:hx:Lqr:u:Vi:")) != -1) { + while ((i = getopt(argc, argv, "+a:s:kDFd:hx:Lqr:u:Vi:")) != -1) { switch (i) { case 'a': upsname = optarg; @@ -536,6 +538,9 @@ int main(int argc, char **argv) case 'D': nut_debug_level++; break; + case 'F': + foreground = 1; + break; case 'd': dump_data = atoi(optarg); break; @@ -574,6 +579,9 @@ int main(int argc, char **argv) } } + if(nut_debug_level > 0 || dump_data) + foreground = 1; + argc -= optind; argv += optind; @@ -711,7 +719,7 @@ int main(int argc, char **argv) if (dstate_getinfo("ups.serial") != NULL) dstate_setinfo("device.serial", "%s", dstate_getinfo("ups.serial")); - if ( (nut_debug_level == 0) && (!dump_data) ) { + if (!foreground) { background(); writepid(pidfn); /* PID changes when backgrounding */ } From c38b45a56a9d1283657d62a9fa1ed1af5daa1ba2 Mon Sep 17 00:00:00 2001 From: Links Date: Sun, 17 Jun 2018 08:05:10 +0200 Subject: [PATCH 004/700] get PowerWalker VFI 2000 TGS working, read values correctly --- drivers/mge-hid.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mge-hid.c b/drivers/mge-hid.c index 4845059cb9..2248e6a960 100644 --- a/drivers/mge-hid.c +++ b/drivers/mge-hid.c @@ -1142,8 +1142,10 @@ static hid_info_t mge_hid2nut[] = { "battery.runtime.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.RemainingTimeLimit", NULL, "%.0f", 0, NULL }, { "battery.temperature", 0, 0, "UPS.BatterySystem.Battery.Temperature", NULL, "%s", 0, kelvin_celsius_conversion }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, + { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.1f", 0, NULL }, { "battery.voltage", 0, 0, "UPS.BatterySystem.Voltage", NULL, "%.1f", 0, NULL }, { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%s", 0, mge_battery_voltage }, + { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.2f", HU_FLAG_STATIC, NULL }, { "battery.voltage.nominal", 0, 0, "UPS.BatterySystem.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%s", HU_FLAG_STATIC, mge_battery_voltage_nominal }, { "battery.protection", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.BatterySystem.Battery.DeepDischargeProtection", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, @@ -1271,6 +1273,11 @@ static hid_info_t mge_hid2nut[] = { "input.current.nominal", 0, 0, "UPS.Flow.[1].ConfigCurrent", NULL, "%.2f", HU_FLAG_STATIC, NULL }, { "input.frequency", 0, 0, "UPS.PowerConverter.Input.[1].Frequency", NULL, "%.1f", 0, NULL }, { "input.frequency.nominal", 0, 0, "UPS.Flow.[1].ConfigFrequency", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "input.transfer.low", 0, 0, "UPS.PowerConverter.Output.ffff0057", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "input.transfer.high", 0, 0, "UPS.PowerConverter.Output.ffff0058", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL}, + { "input.frequency.transfer.low", 0, 0, "UPS.PowerConverter.Output.ffff00f9", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "input.frequency.transfer.high", 0, 0, "UPS.PowerConverter.Output.ffff00f8", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL}, + /* same as "input.transfer.boost.low" */ { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.boost.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageBoostTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, @@ -1300,6 +1307,9 @@ static hid_info_t mge_hid2nut[] = { "input.bypass.frequency", 0, 0, "UPS.PowerConverter.Input.[2].Frequency", NULL, "%.1f", 0, NULL }, { "input.bypass.frequency.nominal", 0, 0, "UPS.Flow.[2].ConfigFrequency", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "output.transfer.high", 0, 0, "UPS.PowerConverter.Output.HighVoltageTransfer", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, + { "output.transfer.low", 0, 0, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, + /* Output page */ { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.1f", 0, NULL }, { "output.L1-N.voltage", 0, 0, "UPS.PowerConverter.Output.Phase.[1].Voltage", NULL, "%.1f", 0, NULL }, From b0b39a8eb33b8723db65b2c294f4821f9462178a Mon Sep 17 00:00:00 2001 From: mbastiaan <35669404+mbastiaan@users.noreply.github.com> Date: Tue, 24 Jul 2018 23:33:58 +0200 Subject: [PATCH 005/700] Update tripplite_usb.c Added battery.charge status for 3005 protocol. Tested on SMX500RT1U, charge level seems ok-ish considering how it's calculated and the voltage jumping up and down. --- drivers/tripplite_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tripplite_usb.c b/drivers/tripplite_usb.c index 9b06407e1a..260fd796af 100644 --- a/drivers/tripplite_usb.c +++ b/drivers/tripplite_usb.c @@ -1330,7 +1330,7 @@ void upsdrv_updateinfo(void) /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ if( tl_model == TRIPP_LITE_OMNIVS || tl_model == TRIPP_LITE_OMNIVS_2001 || - tl_model == TRIPP_LITE_SMARTPRO || tl_model == TRIPP_LITE_SMART_0004 ) { + tl_model == TRIPP_LITE_SMARTPRO || tl_model == TRIPP_LITE_SMART_0004 || tl_model == TRIPP_LITE_SMART_3005) { /* dq ~= sqrt(dV) is a reasonable approximation * Results fit well against the discrete function used in the Tripp Lite * source, but give a continuous result. */ From 4a97db04e661c6aa1ef656654a8ac25d2b36b87d Mon Sep 17 00:00:00 2001 From: mbastiaan <35669404+mbastiaan@users.noreply.github.com> Date: Wed, 25 Jul 2018 10:50:55 +0200 Subject: [PATCH 006/700] Update driver.list.in Added new line for SMX500RT1U with product ID 0001. Updated line for other product IDs --- data/driver.list.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/driver.list.in b/data/driver.list.in index eb790ac91f..6a353c217d 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -1091,7 +1091,8 @@ "Tripp Lite" "ups" "3" "SMX3000RT2UTAA" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=4396 "Tripp Lite" "ups" "3" "SMX3000XLRT2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=2694 "Tripp Lite" "ups" "3" "SMX3000XLRT2UA" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=5658 -"Tripp Lite" "ups" "3" "SMX500RT1U" "USB (protocol 3005)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=2691 +"Tripp Lite" "ups" "3" "SMX500RT1U" "USB (older; product ID 0001, protocol 3005)" "tripplite_usb" # https://www.tripplite.com/support/product/part-number/SMX500RT1U +"Tripp Lite" "ups" "3" "SMX500RT1U" "USB (newer; protocol/product ID 3005)" "usbhid-ups" # https://www.tripplite.com/support/product/part-number/SMX500RT1U "Tripp Lite" "ups" "3" "SMX750SLT" "USB (protocol 3014)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3021 "Tripp Lite" "ups" "3" "SU750RTXL2U" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3194 "Tripp Lite" "ups" "3" "SU750RTXLCD2U" "USB (protocol 4004)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=5070 From a0d59c845ebc77947866028757c7b8dcd79efead Mon Sep 17 00:00:00 2001 From: mbastiaan <35669404+mbastiaan@users.noreply.github.com> Date: Wed, 25 Jul 2018 14:37:17 +0200 Subject: [PATCH 007/700] Update tripplite_usb.c Incremented driver version for pull request #584 --- drivers/tripplite_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tripplite_usb.c b/drivers/tripplite_usb.c index 260fd796af..b9d218040c 100644 --- a/drivers/tripplite_usb.c +++ b/drivers/tripplite_usb.c @@ -136,7 +136,7 @@ #include "usb-common.h" #define DRIVER_NAME "Tripp Lite OMNIVS / SMARTPRO driver" -#define DRIVER_VERSION "0.29" +#define DRIVER_VERSION "0.30" /* driver description structure */ upsdrv_info_t upsdrv_info = { From 99c32645ea5d0d9800f9db891525ad1ad54f2e1b Mon Sep 17 00:00:00 2001 From: mbastiaan <35669404+mbastiaan@users.noreply.github.com> Date: Wed, 25 Jul 2018 16:25:39 +0200 Subject: [PATCH 008/700] input_voltage_nominal, added case 6 SMX500RT1U 230V requires case 6. --- drivers/tripplite_usb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tripplite_usb.c b/drivers/tripplite_usb.c index b9d218040c..ffe1a73b23 100644 --- a/drivers/tripplite_usb.c +++ b/drivers/tripplite_usb.c @@ -467,6 +467,10 @@ void decode_v(const unsigned char *value) input_voltage_scaled = 230; break; + case 6: input_voltage_nominal = + input_voltage_scaled = 230; + break; + default: upslogx(2, "Unknown input voltage range: 0x%02x", (unsigned int)ivn); break; From 5bf8cfaee5af55413e930fd9801a7b3425ba7c9c Mon Sep 17 00:00:00 2001 From: Konstantin Gizdov Date: Wed, 12 Aug 2020 00:54:36 +0300 Subject: [PATCH 009/700] initial patch that works --- drivers/usbhid-ups.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index ea198c4db9..8791d7d97e 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -1422,7 +1422,8 @@ static void ups_status_set(void) dstate_delinfo("input.transfer.reason"); } - if (ups_status & STATUS(ONLINE)) { + if ((ups_status & STATUS(ONLINE)) && + !(ups_status & STATUS(DISCHRG))) { status_set("OL"); /* on line */ } else { status_set("OB"); /* on battery */ From 7c9a0f6946e85b13f5369e1c3d17402c2a91f2b6 Mon Sep 17 00:00:00 2001 From: Konstantin Gizdov Date: Fri, 9 Oct 2020 18:08:09 +0300 Subject: [PATCH 010/700] fix indentation style --- drivers/usbhid-ups.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 8791d7d97e..f1940da8ab 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -1423,7 +1423,7 @@ static void ups_status_set(void) } if ((ups_status & STATUS(ONLINE)) && - !(ups_status & STATUS(DISCHRG))) { + !(ups_status & STATUS(DISCHRG))) { status_set("OL"); /* on line */ } else { status_set("OB"); /* on battery */ From 39a0f19ec41776b893c0329bef341615b2124eea Mon Sep 17 00:00:00 2001 From: Konstantin Gizdov Date: Fri, 9 Oct 2020 19:21:35 +0300 Subject: [PATCH 011/700] lay the groundwork for CyberPower UT cputquirk --- drivers/usbhid-ups.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index f1940da8ab..3ecf24216e 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -114,6 +114,13 @@ bool_t use_interrupt_pipe = FALSE; #endif static time_t lastpoll; /* Timestamp the last polling */ hid_dev_handle_t udev; +/** + * CyberPower UT series sometime need a bit of help deciding their online status. + * This quirk is to enable the special handling of OL & DISCHRG at the same time + * as being OB (on battery power/no mains power) + */ +#define DEFAULT_CPUTQUIRK 0 +static int cputquirk = DEFAULT_CPUTQUIRK; /* support functions */ static hid_info_t *find_nut_info(const char *varname); @@ -705,6 +712,9 @@ void upsdrv_makevartable(void) addvar(VAR_FLAG, "pollonly", "Don't use interrupt pipe, only use polling"); + snprintf(temp, sizeof(temp), "Enable CyberPower UT series quirk (default=%s)", DEFAULT_CPUTQUIRK); + addvar(VAR_FLAG, "cputquirk", temp); + #ifndef SHUT_MODE /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ nut_usb_addvars(); @@ -1422,6 +1432,26 @@ static void ups_status_set(void) dstate_delinfo("input.transfer.reason"); } + + if ((ups_status & STATUS(ONLINE)) && (ups_status & STATUS(DISCHRG))) { + /* warning or CyberPower UT quirk */ + if (cputquirk) { + status_set("OB"); + } else if ((ups_status & STATUS(CAL))) { + status_set("OL"); + } else { + upslogx(LOG_WARNING, "%s: seems that UPS [%s] is OL+DISCHRG state now. " + "Is it calibrating or do you perhaps want to set 'cputquirk' option? " + "Some CyberPower UT series emit OL+DISCHRG when offline.", + __func__, ups->upsname) + status_set("OL"); + } + } else if ((ups_status & STATUS(ONLINE))) { + status_set("OL"); + } else { + status_set("OB"); /* on battery */ + } + if ((ups_status & STATUS(ONLINE)) && !(ups_status & STATUS(DISCHRG))) { status_set("OL"); /* on line */ From 189b20a940965d1c471e684944f36ceb02737696 Mon Sep 17 00:00:00 2001 From: Konstantin Gizdov Date: Fri, 9 Oct 2020 19:24:50 +0300 Subject: [PATCH 012/700] remove leftover lines --- drivers/usbhid-ups.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 3ecf24216e..d89405fece 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -1451,13 +1451,6 @@ static void ups_status_set(void) } else { status_set("OB"); /* on battery */ } - - if ((ups_status & STATUS(ONLINE)) && - !(ups_status & STATUS(DISCHRG))) { - status_set("OL"); /* on line */ - } else { - status_set("OB"); /* on battery */ - } if ((ups_status & STATUS(DISCHRG)) && !(ups_status & STATUS(DEPLETED))) { status_set("DISCHRG"); /* discharging */ From 1a804431c1c3733b952542cf8129c408ef436d1c Mon Sep 17 00:00:00 2001 From: Konstantin Gizdov Date: Fri, 9 Oct 2020 19:37:03 +0300 Subject: [PATCH 013/700] more compact logic --- drivers/usbhid-ups.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index d89405fece..3f5333c30b 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -1433,23 +1433,21 @@ static void ups_status_set(void) } - if ((ups_status & STATUS(ONLINE)) && (ups_status & STATUS(DISCHRG))) { + if (!(ups_status & STATUS(ONLINE))) { + status_set("OB"); /* on battery */ + } else if ((ups_status & STATUS(DISCHRG))) { /* warning or CyberPower UT quirk */ if (cputquirk) { - status_set("OB"); - } else if ((ups_status & STATUS(CAL))) { - status_set("OL"); + status_set("OB"); /* on battery */ } else { - upslogx(LOG_WARNING, "%s: seems that UPS [%s] is OL+DISCHRG state now. " - "Is it calibrating or do you perhaps want to set 'cputquirk' option? " - "Some CyberPower UT series emit OL+DISCHRG when offline.", - __func__, ups->upsname) + if (!(ups_status & STATUS(CAL))) { + upslogx(LOG_WARNING, "%s: seems that UPS [%s] is OL+DISCHRG state now. " + "Is it calibrating or do you perhaps want to set 'cputquirk' option? " + "Some CyberPower UT series emit OL+DISCHRG when offline.", + __func__, ups->upsname) + } status_set("OL"); } - } else if ((ups_status & STATUS(ONLINE))) { - status_set("OL"); - } else { - status_set("OB"); /* on battery */ } if ((ups_status & STATUS(DISCHRG)) && !(ups_status & STATUS(DEPLETED))) { From 7601fb514c02c19323d57ae67ff9375cb38f8c86 Mon Sep 17 00:00:00 2001 From: Konstantin Gizdov Date: Mon, 12 Oct 2020 01:37:49 +0300 Subject: [PATCH 014/700] address some comments --- drivers/usbhid-ups.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 3f5333c30b..054e03db62 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -119,8 +119,8 @@ hid_dev_handle_t udev; * This quirk is to enable the special handling of OL & DISCHRG at the same time * as being OB (on battery power/no mains power) */ -#define DEFAULT_CPUTQUIRK 0 -static int cputquirk = DEFAULT_CPUTQUIRK; +#define DEFAULT_ONLINEDISCHARGE 0 +static int onlinedischarge = DEFAULT_ONLINEDISCHARGE; /* support functions */ static hid_info_t *find_nut_info(const char *varname); @@ -712,8 +712,9 @@ void upsdrv_makevartable(void) addvar(VAR_FLAG, "pollonly", "Don't use interrupt pipe, only use polling"); - snprintf(temp, sizeof(temp), "Enable CyberPower UT series quirk (default=%s)", DEFAULT_CPUTQUIRK); - addvar(VAR_FLAG, "cputquirk", temp); + snprintf(temp, sizeof(temp), "Treat discharging while online as being offline (default=%s)", + DEFAULT_ONLINEDISCHARGE); + addvar(VAR_FLAG, "onlinedischarge", temp); #ifndef SHUT_MODE /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ @@ -1436,17 +1437,20 @@ static void ups_status_set(void) if (!(ups_status & STATUS(ONLINE))) { status_set("OB"); /* on battery */ } else if ((ups_status & STATUS(DISCHRG))) { - /* warning or CyberPower UT quirk */ - if (cputquirk) { + /* if online */ + if (onlinedischarge) { + /* if we treat OL+DISCHRG as being offline */ status_set("OB"); /* on battery */ } else { if (!(ups_status & STATUS(CAL))) { - upslogx(LOG_WARNING, "%s: seems that UPS [%s] is OL+DISCHRG state now. " - "Is it calibrating or do you perhaps want to set 'cputquirk' option? " - "Some CyberPower UT series emit OL+DISCHRG when offline.", + /* if in OL+DISCHRG unknowingly, warn user */ + upslogx(LOG_WARNING, "%s: seems that UPS [%s] is in OL+DISCHRG state now. " + "Is it calibrating or do you perhaps want to set 'onlinedischarge' option? " + "Some UPS models (e.g. CyberPower UT series) emit OL+DISCHRG when offline.", __func__, ups->upsname) } - status_set("OL"); + /* if we're calibrating */ + status_set("OL"); /* on line */ } } if ((ups_status & STATUS(DISCHRG)) && From ca7415fe6f315cf67194c65d39366e7da354e8af Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 7 Nov 2020 23:21:46 +0100 Subject: [PATCH 015/700] Update tripplite_usb.c drivers/tripplite_usb.c: drop trailing whitespace --- drivers/tripplite_usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tripplite_usb.c b/drivers/tripplite_usb.c index 503e35d21e..87a68bc6a8 100644 --- a/drivers/tripplite_usb.c +++ b/drivers/tripplite_usb.c @@ -467,10 +467,10 @@ void decode_v(const unsigned char *value) input_voltage_scaled = 230; break; - case 6: input_voltage_nominal = + case 6: input_voltage_nominal = input_voltage_scaled = 230; break; - + default: upslogx(2, "Unknown input voltage range: 0x%02x", (unsigned int)ivn); break; From 1f65607b8c338e39fcd69c564ced18a1f6d6a9db Mon Sep 17 00:00:00 2001 From: Alexey Kazancev Date: Mon, 26 Apr 2021 15:39:35 +0500 Subject: [PATCH 016/700] nutdrv_qx.c: add correction of runtime estimation --- drivers/nutdrv_qx.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index fb5294ca25..760e632166 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -3009,6 +3009,34 @@ static bool_t qx_ups_walk(walkmode_t mode) } + const char *val = dstate_getinfo("battery.voltage"); + + if (!val) { + upsdebugx(2, "%s: unable to get battery.voltage", __func__); + } else { + + batt.volt.act = batt.packs * strtod(val, NULL); + + if (batt.volt.low > 0 && batt.volt.high > batt.volt.low) { + + double voltage_battery_charge = (batt.volt.act - batt.volt.low) / (batt.volt.high - batt.volt.low); + + if (voltage_battery_charge < 0) { + voltage_battery_charge = 0; + } + + if (voltage_battery_charge > 1) { + voltage_battery_charge = 1; + } + + /* Correct estimated runtime remaining for old batteries */ + if(voltage_battery_charge < (batt.runt.est / batt.runt.nom)) { + batt.runt.est = voltage_battery_charge * batt.runt.nom; + } + + } + } + if (d_equal(batt.chrg.act, -1)) dstate_setinfo("battery.charge", "%.0f", 100 * batt.runt.est / batt.runt.nom); From 1a904c5ef6dd71dce55a26c93069e14bb3490b98 Mon Sep 17 00:00:00 2001 From: Alexey Kazancev Date: Thu, 6 May 2021 09:54:50 +0500 Subject: [PATCH 017/700] nutdrv_qx.c: bump version to 0.30 --- drivers/nutdrv_qx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index 760e632166..f890652672 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -33,7 +33,7 @@ * */ -#define DRIVER_VERSION "0.29" +#define DRIVER_VERSION "0.30" #include "config.h" #include "main.h" From 980c396661e7972d02cf41d5e12949b86870e83e Mon Sep 17 00:00:00 2001 From: Alexey Kazancev Date: Tue, 18 May 2021 23:43:43 +0500 Subject: [PATCH 018/700] nutdrv_qx.c: fix snr_command communication error in protocol matching phase. --- drivers/nutdrv_qx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index f890652672..d2b538e5df 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -1233,6 +1233,10 @@ static int snr_command(const char *cmd, char *buf, size_t buflen) return 0; } + /* Prepare SNR-UPS for communication. + * Without the interrupt UPS returns zeros some time, and after all the NUT return communication error. */ + usb_interrupt_read(udev, 0x81, buf, 102, 1000); + for (i = 0; command[i].str; i++) { int retry; From ebe3c2f33b000546a47e980677771334700c1ac0 Mon Sep 17 00:00:00 2001 From: Alexey Kazancev Date: Tue, 18 May 2021 22:31:40 +0500 Subject: [PATCH 019/700] nutdrv_qx.c: add batt.volt.act validation Some UPS return zero batt voltage after communication error. This validation must fix wrong runtime calculation. --- drivers/nutdrv_qx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index d2b538e5df..10137dd3be 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -3021,7 +3021,7 @@ static bool_t qx_ups_walk(walkmode_t mode) batt.volt.act = batt.packs * strtod(val, NULL); - if (batt.volt.low > 0 && batt.volt.high > batt.volt.low) { + if (batt.volt.act > 0 && batt.volt.low > 0 && batt.volt.high > batt.volt.low) { double voltage_battery_charge = (batt.volt.act - batt.volt.low) / (batt.volt.high - batt.volt.low); From a8b10845b05d9ec1ec5c22eb2fbd45a1e7724587 Mon Sep 17 00:00:00 2001 From: Alexey Kazancev Date: Tue, 18 May 2021 23:46:21 +0500 Subject: [PATCH 020/700] nutdrv_qx.c: bump version to 0.31 --- drivers/nutdrv_qx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index 10137dd3be..7a60f27005 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -33,7 +33,7 @@ * */ -#define DRIVER_VERSION "0.30" +#define DRIVER_VERSION "0.31" #include "config.h" #include "main.h" From 31f5f5b46e38fc5637f4ea9f1bd0632ac80b5ed9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 24 May 2021 16:06:22 +0200 Subject: [PATCH 021/700] Update nutdrv_qx.c Formatting and slight rewording of a comment --- drivers/nutdrv_qx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index 7a60f27005..76b8998282 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -1234,7 +1234,9 @@ static int snr_command(const char *cmd, char *buf, size_t buflen) } /* Prepare SNR-UPS for communication. - * Without the interrupt UPS returns zeros some time, and after all the NUT return communication error. */ + * Without the interrupt UPS returns zeros for some time, + * and afterwards NUT returns a communications error. + */ usb_interrupt_read(udev, 0x81, buf, 102, 1000); for (i = 0; command[i].str; i++) { From 23079adf37ed53d4dcc31f90752df8bc44b7b045 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 6 Jan 2022 01:09:02 +0200 Subject: [PATCH 022/700] under construction --- drivers/adele_cbi.c | 1006 +++++++++++++++++++++++++++++++++++++++++++ drivers/adele_cbi.h | 196 +++++++++ 2 files changed, 1202 insertions(+) create mode 100644 drivers/adele_cbi.c create mode 100644 drivers/adele_cbi.h diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c new file mode 100644 index 0000000000..f24e13b575 --- /dev/null +++ b/drivers/adele_cbi.c @@ -0,0 +1,1006 @@ +/* adele_cbi.c - Driver for adele CB/CBI UPS + * + * Copyright (C) + * 2021 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "adele_cbi.h" +#include +#include + +#define DRIVER_NAME "NUT Adele CBI driver" +#define DRIVER_VERSION "0.01" + +/* variables */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static regattr_t sigar[NUMOF_SIG_STATES]; /* array of ups signal attributes */ +static int errcnt = 0; /* modbus access error counter */ + +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ + + +/* get config vars set by -x or defined in ups.conf driver section */ +void get_config_vars(void); + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port); + +/* reconnect upon communication error */ +void modbus_reconnect(); + +/* modbus register read function */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data); + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg); + +/* read signal status */ +int get_signal_state(devstate_t state); + +/* count the time elapsed since start */ +long time_elapsed(struct timeval *start); + +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} +}; + +/* + * driver functions + */ + +/* initialize ups driver information */ +void upsdrv_initinfo(void) { + upsdebugx(2, "upsdrv_initinfo"); + + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); + + /* register instant commands */ + if (sigar[FSD_T].addr != NOTUSED) { + dstate_addcmd("load.off"); + } + + /* set callback for instant commands */ + upsh.instcmd = upscmd; +} + +/* open serial connection and connect to modbus RIO */ +void upsdrv_initups(void) +{ + int rval; + upsdebugx(2, "upsdrv_initups"); + + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response time out to 200ms */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} + +/* update UPS signal state */ +void upsdrv_updateinfo(void) +{ + int rval; + int online = -1; /* keep online state */ + errcnt = 0; + + upsdebugx(2, "upsdrv_updateinfo"); + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ + + /* + * update UPS status regarding MAINS state either via OL | OB. + * if both statuses are mapped to contacts then only OL is evaluated. + */ + if (sigar[OL_T].addr != NOTUSED) { + rval = get_signal_state(OL_T); + upsdebugx(2, "OL value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OL_T].noro)) { + status_set("OL"); + online = 1; + } else { + status_set("OB"); + online = 0; + + /* if DISCHRG state is not mapped to a contact and UPS is on + * batteries set status to DISCHRG state */ + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + + } + } else if (sigar[OB_T].addr != NOTUSED) { + rval = get_signal_state(OB_T); + upsdebugx(2, "OB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OB_T].noro)) { + status_set("OB"); + online = 0; + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } else { + status_set("OL"); + online = 1; + } + } + + /* + * update UPS status regarding CHARGING state via HB. HB is usually + * mapped to "ready" contact when closed indicates a charging state > 85% + */ + if (sigar[HB_T].addr != NOTUSED) { + rval = get_signal_state(HB_T); + upsdebugx(2, "HB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[HB_T].noro)) { + status_set("HB"); + dstate_setinfo("battery.charger.status", "resting"); + } else if (online == 1 && sigar[CHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } else if (online == 0 && sigar[DISCHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* + * update UPS status regarding DISCHARGING state via LB. LB is mapped + * to "battery low" contact. + */ + if (sigar[LB_T].addr != NOTUSED) { + rval = get_signal_state(LB_T); + upsdebugx(2, "LB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[LB_T].noro)) { + status_set("LB"); + alarm_set("Low Battery (Charge)"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[RB_T].addr != NOTUSED) { + rval = get_signal_state(RB_T); + upsdebugx(2, "RB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[RB_T].noro)) { + status_set("RB"); + alarm_set("Replace Battery"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[CHRG_T].addr != NOTUSED) { + rval = get_signal_state(CHRG_T); + upsdebugx(2, "CHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[CHRG_T].noro)) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } + } else if (sigar[DISCHRG_T].addr != NOTUSED) { + rval = get_signal_state(DISCHRG_T); + upsdebugx(2, "DISCHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[DISCHRG_T].noro)) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* check for communication errors */ + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2,"Communication errors: %d", errcnt); + dstate_datastale(); + } +} + +/* shutdown UPS */ +void upsdrv_shutdown(void) +{ + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); +} + +/* print driver usage info */ +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); + addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); + addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); + addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); +} + +/* close modbus connection and free modbus context allocated memory */ +void upsdrv_cleanup(void) +{ + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } +} + +/* + * driver support functions + */ + +/* Read a modbus register */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* write a modbus register */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* returns the time elapsed since start in milliseconds */ +long time_elapsed(struct timeval *start) +{ + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; +} + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg) +{ + int rval; + int data; + struct timeval start; + long etime; + + if (!strcasecmp(cmd, "load.off")) { + if (sigar[FSD_T].addr != NOTUSED && + (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) + ) { + data = 1 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", sigar[FSD_T].addr, data); + rval = STAT_INSTCMD_HANDLED; + } + + /* if pulse has been defined and rising edge was successful */ + if (FSD_pulse_duration != NOTUSED && rval == STAT_INSTCMD_HANDLED) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for FSD_pulse_duration ms */ + while ((etime = time_elapsed(&start)) < FSD_pulse_duration); + + data = 0 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(LOG_ERR, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d, elapsed time: %lims", + sigar[FSD_T].addr, + data, + etime + ); + rval = STAT_INSTCMD_HANDLED; + } + } + } else { + upslogx(LOG_NOTICE,"load.off: failed (FSD address undefined or invalid register type) [%s] [%s]", + cmd, + arg + ); + rval = STAT_INSTCMD_FAILED; + } + } else { + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; +} + +/* read signal state from modbus RIO, returns 0|1 state or -1 on communication error */ +int get_signal_state(devstate_t state) +{ + int rval = -1; + int reg_val; + regtype_t rtype = 0; /* register type */ + int addr = -1; /* register address */ + + /* assign register address and type */ + switch (state) { + case OL_T: + addr = sigar[OL_T].addr; + rtype = sigar[OL_T].type; + break; + case OB_T: + addr = sigar[OB_T].addr; + rtype = sigar[OB_T].type; + break; + case LB_T: + addr = sigar[LB_T].addr; + rtype = sigar[LB_T].type; + break; + case HB_T: + addr = sigar[HB_T].addr; + rtype = sigar[HB_T].type; + break; + case RB_T: + addr = sigar[RB_T].addr; + rtype = sigar[RB_T].type; + break; + case CHRG_T: + addr = sigar[CHRG_T].addr; + rtype = sigar[CHRG_T].type; + break; + case DISCHRG_T: + addr = sigar[DISCHRG_T].addr; + rtype = sigar[DISCHRG_T].type; + break; + + case BYPASS_T: + case CAL_T: + case FSD_T: + case OFF_T: + case OVER_T: + case TRIM_T: + case BOOST_T: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + break; + } + + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval > -1) { + rval = reg_val; + } + upsdebugx(3, "get_signal_state: state: %d", reg_val); + return rval; +} + +/* get driver configuration parameters */ +void get_config_vars() +{ + int i; /* local index */ + + /* initialize sigar table */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + sigar[i].addr = NOTUSED; + sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ + } + + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); + + /* check if response time out (s) is set ang get the value */ + if (testvar("mod_resp_to_s")) { + mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); + } + upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); + + /* check if response time out (us) is set ang get the value */ + if (testvar("mod_resp_to_us")) { + mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); + if (mod_resp_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } + } + upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); + + /* check if byte time out (s) is set ang get the value */ + if (testvar("mod_byte_to_s")) { + mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); + } + upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); + + /* check if byte time out (us) is set ang get the value */ + if (testvar("mod_byte_to_us")) { + mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); + if (mod_byte_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } + } + upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); + + /* check if OL address is set and get the value */ + if (testvar("OL_addr")) { + sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); + if (testvar("OL_noro")) { + sigar[OL_T].noro = (int)strtol(getval("OL_noro"), NULL, 10); + if (sigar[OL_T].noro != 1) { + sigar[OL_T].noro = 0; + } + } + } + + /* check if OL register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OL_regtype")) { + sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); + if (sigar[OL_T].type < COIL || sigar[OL_T].type > HOLDING) { + sigar[OL_T].type = INPUT_B; + } + } else { + sigar[OL_T].type = INPUT_B; + } + + /* check if OB address is set and get the value */ + if (testvar("OB_addr")) { + sigar[OB_T].addr = (int)strtol(getval("OB_addr"), NULL, 0); + } + if (testvar("OB_noro")) { + sigar[OB_T].noro = (int)strtol(getval("OB_noro"), NULL, 10); + if (sigar[OB_T].noro != 1) { + sigar[OB_T].noro = 0; + } + } + + /* check if OB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OB_regtype")) { + sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[OB_T].type < COIL || sigar[OB_T].type > HOLDING) { + sigar[OB_T].type = INPUT_B; + } + } else { + sigar[OB_T].type = INPUT_B; + } + + /* check if LB address is set and get the value */ + if (testvar("LB_addr")) { + sigar[LB_T].addr = (int)strtol(getval("LB_addr"), NULL, 0); + if (testvar("LB_noro")) { + sigar[LB_T].noro = (int)strtol(getval("LB_noro"), NULL, 10); + if (sigar[LB_T].noro != 1) { + sigar[LB_T].noro = 0; + } + } + } + + /* check if LB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("LB_regtype")) { + sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[LB_T].type < COIL || sigar[LB_T].type > HOLDING) { + sigar[LB_T].type = INPUT_B; + } + } else { + sigar[LB_T].type = INPUT_B; + } + + /* check if HB address is set and get the value */ + if (testvar("HB_addr")) { + sigar[HB_T].addr = (int)strtol(getval("HB_addr"), NULL, 0); + if (testvar("HB_noro")) { + sigar[HB_T].noro = (int)strtol(getval("HB_noro"), NULL, 10); + if (sigar[HB_T].noro != 1) { + sigar[HB_T].noro = 0; + } + } + } + + /* check if HB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("HB_regtype")) { + sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); + if (sigar[HB_T].type < COIL || sigar[HB_T].type > HOLDING) { + sigar[HB_T].type = INPUT_B; + } + } else { + sigar[HB_T].type = INPUT_B; + } + + /* check if RB address is set and get the value */ + if (testvar("RB_addr")) { + sigar[RB_T].addr = (int)strtol(getval("RB_addr"), NULL, 0); + if (testvar("RB_noro")) { + sigar[RB_T].noro = (int)strtol(getval("RB_noro"), NULL, 10); + if (sigar[RB_T].noro != 1) { + sigar[RB_T].noro = 0; + } + } + } + + /* check if RB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("RB_regtype")) { + sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); + if (sigar[RB_T].type < COIL || sigar[RB_T].type > HOLDING) { + sigar[RB_T].type = INPUT_B; + } + } else { + sigar[RB_T].type = INPUT_B; + } + + /* check if CHRG address is set and get the value */ + if (testvar("CHRG_addr")) { + sigar[CHRG_T].addr = (int)strtol(getval("CHRG_addr"), NULL, 0); + if (testvar("CHRG_noro")) { + sigar[CHRG_T].noro = (int)strtol(getval("CHRG_noro"), NULL, 10); + if (sigar[CHRG_T].noro != 1) { + sigar[CHRG_T].noro = 0; + } + } + } + + /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("CHRG_regtype")) { + sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); + if (sigar[CHRG_T].type < COIL || sigar[CHRG_T].type > HOLDING) { + sigar[CHRG_T].type = INPUT_B; + } + } else { + sigar[CHRG_T].type = INPUT_B; + } + + /* check if DISCHRG address is set and get the value */ + if (testvar("DISCHRG_addr")) { + sigar[DISCHRG_T].addr = (int)strtol(getval("DISCHRG_addr"), NULL, 0); + if (testvar("DISCHRG_noro")) { + sigar[DISCHRG_T].noro = (int)strtol(getval("DISCHRG_noro"), NULL, 10); + if (sigar[DISCHRG_T].noro != 1) { + sigar[DISCHRG_T].noro = 0; + } + } + } + + /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("DISCHRG_regtype")) { + sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); + if (sigar[DISCHRG_T].type < COIL || sigar[DISCHRG_T].type > HOLDING) { + sigar[DISCHRG_T].type = INPUT_B; + } + } else { + sigar[DISCHRG_T].type = INPUT_B; + } + + /* check if FSD address is set and get the value */ + if (testvar("FSD_addr")) { + sigar[FSD_T].addr = (int)strtol(getval("FSD_addr"), NULL, 0); + if (testvar("FSD_noro")) { + sigar[FSD_T].noro = (int)strtol(getval("FSD_noro"), NULL, 10); + if (sigar[FSD_T].noro != 1) { + sigar[FSD_T].noro = 0; + } + } + } + + /* check if FSD register type is set and get the value otherwise set to COIL */ + if (testvar("FSD_regtype")) { + sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); + if (sigar[FSD_T].type < COIL || sigar[FSD_T].type > HOLDING) { + sigar[FSD_T].type = COIL; + } + } else { + sigar[FSD_T].type = COIL; + } + + /* check if FSD pulse duration is set and get the value */ + if (testvar("FSD_pulse_duration")) { + FSD_pulse_duration = (int) strtol(getval("FSD_pulse_duration"), NULL, 10); + } + upsdebugx(2, "FSD_pulse_duration %d", FSD_pulse_duration); + + /* debug loop over signal array */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + if (sigar[i].addr != NOTUSED) { + char *signame; + switch (i) { + case OL_T: + signame = "OL"; + break; + case OB_T: + signame = "OB"; + break; + case LB_T: + signame = "LB"; + break; + case HB_T: + signame = "HB"; + break; + case RB_T: + signame = "RB"; + break; + case FSD_T: + signame = "FSD"; + break; + case CHRG_T: + signame = "CHRG"; + break; + case DISCHRG_T: + signame = "DISCHRG"; + break; + default: + signame = "NOTUSED"; + break; + } + upsdebugx(2, "%s, addr:0x%x, type:%d", signame, sigar[i].addr, sigar[i].type); + } + } +} + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port) +{ + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; +} + +/* reconnect to modbus server upon connection error */ +void modbus_reconnect() +{ + int rval; + + upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); + + /* clear current modbus context */ + modbus_close(mbctx); + modbus_free(mbctx); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} \ No newline at end of file diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h new file mode 100644 index 0000000000..838af3971d --- /dev/null +++ b/drivers/adele_cbi.h @@ -0,0 +1,196 @@ +/* adele_cbi.h - Driver for generic UPS connected via modbus RIO + * + * Copyright (C) + * 2021 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ADELE_CBI_H +#define ADELE_CBI_H + +/* UPS device details */ +#define DEVICE_MFR "ADELE" +#define DEVICE_MODEL "CB/CBI" + +/* serial access parameters */ +#define BAUD_RATE 9600 +#define PARITY 'N' +#define DATA_BIT 8 +#define STOP_BIT 1 + +/* + * modbus response and byte timeouts + * us: 1 - 999999 + */ +#define MODRESP_TIMEOUT_s 0 +#define MODRESP_TIMEOUT_us 200000 +#define MODBYTE_TIMEOUT_s 0 +#define MODBYTE_TIMEOUT_us 50000 + +/* modbus access parameters */ +#define MODBUS_SLAVE_ID 5 + +/* definition of register type */ +enum regtype { + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING +}; +typedef enum regtype regtype_t; + +/* UPS device state enum */ +enum devstate { + CHRG = 0, /* Charging status */ + BATV, /* Battery voltage */ + BCEF = 6, /* Battery charge efficiency factor (CEF) */ + BSOH, /* Battery state-of-health */ + BSOC = 9, /* Battery state-of-charge */ + BTMP = 11, /* Battery temperature in Kelvin units */ + PMNG = 15, /* Power management */ +}; +typedef enum devstate devstate_t; + +/* BIT MASKS and VALUES */ +#define CHRG_NONE 0 +#define CHRG_RECV 1 +#define CHRG_BULK 2 +#define CHRG_ABSR 3 +#define CHRG_FLOAT 4 +#define PMNG_BKUP 0 +#define PMNG_CHRG 1 +#define PMNG_BOOST 2 +#define PMNG_NCHRG 3 + +/* UPS state signal attributes */ +struct regattr { + int num; + int saddr; /* register start address */ + int xaddr; /* register hex address */ + float scale; /* scale */ + regtype_t type; /* register type */ +}; +typedef struct regattr regattr_t; + +/* ADELE CBI registers */ +regattr_t regs[] = { + {40005, 0, 0, 1, HOLDING}, /* Charging status */ + {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ + {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ + {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ + {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ + {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ + {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ + {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ + {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ + {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ + {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ + {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ + {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ + {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ + {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ + {40006, 0, 0, 1, HOLDING}, /* Power management + * 0:Backup 1:Charging 2:boost 3:Not charging + */ + {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ + {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ + {40010, 0, 0, 1, HOLDING}, /* Software ID */ + {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ + {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ + {40067, 0, 0, 1, HOLDING}, /* Product name */ + {40039, 0, 0, 1, HOLDING}, /* Device variant */ + {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ + {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ + {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ + {40020, 0, 0, 1, HOLDING}, /* Output load current */ + {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ + {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ + {40050, 0, 0, .1, HOLDING}, /* Ah charged */ + {40051, 0, 0, 1, HOLDING}, /* Total run time */ + {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ + {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ + {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ + {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ + {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ + {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ + {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ + {40056, 0, 0, 1, HOLDING}, /* Number of overtemperature inside events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low AC input voltage events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High AC input voltage events at mains input */ + {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ + {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ + {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ + {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ + {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ + {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ + {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ + {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ + {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ + {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ + {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ + {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ + {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ + {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ + {40120, 0, 0, 1, HOLDING}, /* Zero-SoC reference */ + {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ + {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ + {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ + {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ + {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ + {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ + {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ + {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ + {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ + {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ + {40082, 0, 0, 1, HOLDING}, /* Float voltage */ + {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ + {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ + {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ + {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ + {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ + {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ + {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ + {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ + {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ + {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ + {40065, 0, 0, 1, HOLDING}, /* History clear all */ + {40066, 0, 0, 1, HOLDING}, /* Factory settings */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed 1 = Backup not allowed 0 */ + {40104, 0, 0, 1, HOLDING}, /* Time buffering */ + {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ + {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ + {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ + {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ + {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ + {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ + {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ + {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ + {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ + {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ + {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ + {40043, 0, 0, 1, HOLDING}, /* Device failure */ + {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ + {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ + {40046, 0, 0, 1, HOLDING}, /* Input mains on / backup */ + {40038, 0, 0, 1, HOLDING} /* Load alarm */ +}; + +#define NUMOF_REGS 14 +#define NOTUSED -1 + +#endif /* ADELE_CBI_H */ From 1c103019429a8c8b11b3e641d61c7227eed68820 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 6 Jan 2022 01:10:09 +0200 Subject: [PATCH 023/700] Makefile.am modifications --- drivers/Makefile.am | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/Makefile.am b/drivers/Makefile.am index db73311c9e..bd8900d700 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -50,7 +50,7 @@ SERIAL_USB_DRIVERLIST = \ nutdrv_qx NEONXML_DRIVERLIST = netxml-ups MACOSX_DRIVERLIST = macosx-ups -MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 +MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 adele_cbi LINUX_I2C_DRIVERLIST = asem pijuice # distribute all drivers, even ones that are not built by default @@ -267,6 +267,8 @@ phoenixcontact_modbus_SOURCES = phoenixcontact_modbus.c phoenixcontact_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) generic_modbus_SOURCES = generic_modbus.c generic_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) +adele_cbi_SOURCES = adele_cbi.c +adele_cbi_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) # Huawei UPS2000 driver # (this is both a Modbus and a serial driver) @@ -323,7 +325,7 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h b xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ apc-pdu-mib.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h \ eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h \ - hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h + hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adele_cbi # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, From fe0db47ebedaccca1fddfbabb7793036c02f7616 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Fri, 7 Jan 2022 01:55:04 +0200 Subject: [PATCH 024/700] register status values and masks added --- drivers/adele_cbi.h | 129 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 18 deletions(-) diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 838af3971d..e2cc76643a 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -23,6 +23,8 @@ #ifndef ADELE_CBI_H #define ADELE_CBI_H +#include + /* UPS device details */ #define DEVICE_MFR "ADELE" #define DEVICE_MODEL "CB/CBI" @@ -54,36 +56,124 @@ enum regtype { }; typedef enum regtype regtype_t; -/* UPS device state enum */ -enum devstate { - CHRG = 0, /* Charging status */ - BATV, /* Battery voltage */ - BCEF = 6, /* Battery charge efficiency factor (CEF) */ - BSOH, /* Battery state-of-health */ - BSOC = 9, /* Battery state-of-charge */ - BTMP = 11, /* Battery temperature in Kelvin units */ - PMNG = 15, /* Power management */ +struct prodname { + uint16_t val; + char *name; }; -typedef enum devstate devstate_t; +typedef struct prodname prodname_t; -/* BIT MASKS and VALUES */ +/* product name */ +prodname_t prdn[] = { + {1, "CBI1235A"}, + {2, "CBI2420A"}, + {3, "CBI4810A"}, + {4, "CBI2801224"}, + {7, "CBI480W"}, + {8, "CB122410A"}, + {9, "CB480W"}, + {11, "CB12245AJ"}, + {12, "CB1235A"}, + {13, "CB2420A"}, + {14, "CB4810A"} +}; + +/* charging status */ +char *chrgs[] = { + "none", + "recovery", + "bulk", + "float" +}; + +/* + * BIT MASKS and VALUES + */ + +/* Charging status */ #define CHRG_NONE 0 #define CHRG_RECV 1 #define CHRG_BULK 2 #define CHRG_ABSR 3 #define CHRG_FLOAT 4 + +/* power management */ #define PMNG_BKUP 0 #define PMNG_CHRG 1 #define PMNG_BOOST 2 #define PMNG_NCHRG 3 +/* product name */ +#define PRDN_MAX 14 + +/* Mains status */ +#define MAINS_AVAIL 0x0001 /* available */ +#define SHUTD_REQST 0x0002 /* shutdown requested */ + +/* AC input voltage alarms */ +#define VACA_HIALRM 0x0001 /* high alarm */ +#define VACA_LOALRM 0x0002 /* low alarm */ + +/* Onboard temperature alarm */ +#define OBTA_HIALRM 1 /* high alarm */ + +/* Device failure */ +#define DEVF_RCALRM 0x0001 /* rectifier failure */ +#define DEVF_INALRM 0x0006 /* internal failure */ +#define DEVF_LFNAVL 0x0008 /* lifetest not available */ + +/* Battery temp sensor failure */ +#define BTSF_FCND 0x0001 /* connection fault */ +#define BTSF_NCND 0x0001 /* not connected */ + +/* Battery voltage alarm */ +#define BVAL_HIALRM 0x0001 /* high voltage */ +#define BVAL_LOALRM 0x0002 /* low voltage */ +#define BVAL_BSTSFL 0x0004 /* battery start with battery flat */ + +/* SoH and SoC alarms */ +#define SHSC_HIRESI 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC 0x0040 /* low state of charge */ + +/* Battery status alarms */ +#define BSTA_REVPOL 0x0001 /* reversed polarity */ +#define BSTA_NOCNND 0x0002 /* not connected */ +#define BSTA_CLSHCR 0x0004 /* cell short circuit */ +#define BSTA_SULPHD 0x0008 /* sulphated */ +#define BSTA_CHEMNS 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT 0x0020 /* connection fault */ + + +/* UPS device state enum */ +enum devstate { + CHRG = 0, /* Charging status */ + BATV, /* Battery voltage */ + BCEF = 6, /* Battery charge efficiency factor (CEF) */ + BSOH, /* Battery state-of-health */ + BSOC = 9, /* Battery state-of-charge */ + BTMP = 11, /* Battery temperature in Kelvin units */ + PMNG = 15, /* Power management */ + PRDN = 21, /* Product name */ + FSD = 80, /* Force shutdown */ + BSTA = 89, /* Battery status alarms */ + SCSH = 90, /* SoH and SoC alarms */ + BVAL = 91, /* Battery voltage alarm */ + BTSF = 92, /* Battery temp sensor failure */ + DEVF = 93, /* Device failure */ + OBTA = 94, /* On board temp alarm */ + VACA = 95, /* VAC alarms */ + MAIN = 96 /* Mains status */ +}; +typedef enum devstate devstate_t; + /* UPS state signal attributes */ struct regattr { int num; - int saddr; /* register start address */ + int saddr; /* register start address */ int xaddr; /* register hex address */ float scale; /* scale */ - regtype_t type; /* register type */ + regtype_t type; /* register type */ }; typedef struct regattr regattr_t; @@ -129,9 +219,9 @@ regattr_t regs[] = { {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ - {40056, 0, 0, 1, HOLDING}, /* Number of overtemperature inside events */ - {40054, 0, 0, 1, HOLDING}, /* Number of low AC input voltage events at mains input */ - {40055, 0, 0, 1, HOLDING}, /* Number of High AC input voltage events at mains input */ + {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ @@ -170,7 +260,10 @@ regattr_t regs[] = { {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ {40065, 0, 0, 1, HOLDING}, /* History clear all */ {40066, 0, 0, 1, HOLDING}, /* Factory settings */ - {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed 1 = Backup not allowed 0 */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed + * 1 = Backup not allowed 0 + * */ + {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ {40104, 0, 0, 1, HOLDING}, /* Time buffering */ {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ @@ -186,7 +279,7 @@ regattr_t regs[] = { {40043, 0, 0, 1, HOLDING}, /* Device failure */ {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ - {40046, 0, 0, 1, HOLDING}, /* Input mains on / backup */ + {40046, 0, 0, 1, HOLDING}, /* Mains status */ {40038, 0, 0, 1, HOLDING} /* Load alarm */ }; From ac980c7753db2270bf2b09101eb45f9b0c55e3fa Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Sat, 8 Jan 2022 02:49:31 +0200 Subject: [PATCH 025/700] structure device data, code get_dev_state, in progress --- drivers/adele_cbi.c | 202 +++++++++++++++++++++++++++++++++----------- drivers/adele_cbi.h | 112 ++++++++++++++++-------- 2 files changed, 230 insertions(+), 84 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index f24e13b575..d254d4c54b 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -1,4 +1,4 @@ -/* adele_cbi.c - Driver for adele CB/CBI UPS +/* adele_cbi.c - driver for ADELE CB/CBI DC-UPS * * Copyright (C) * 2021 Dimitris Economou @@ -62,7 +62,7 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); int upscmd(const char *cmd, const char *arg); /* read signal status */ -int get_signal_state(devstate_t state); +int get_signal_state(devreg_t state); /* count the time elapsed since start */ long time_elapsed(struct timeval *start); @@ -297,7 +297,7 @@ void upsdrv_shutdown(void) /* wait for an increasing time interval before sending shutdown command */ while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); cnt--; } switch (rval) { @@ -346,6 +346,45 @@ void upsdrv_cleanup(void) * driver support functions */ +/* initialize register start address and hex address from register number */ +void reginit() +{ + int i; /* local index */ + + for (i = 1; i < MODBUS_NUMOF_REGS; i++) { + int rnum = regs[i].num; + switch (regs[i].type) { + case COIL: + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x0 + regs[i].num - 1; + break; + case INPUT_B: + rnum -= 10000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x10000 + rnum - 1; + break; + case INPUT_R: + rnum -= 30000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x30000 + rnum - 1; + break; + case HOLDING: + rnum -= 40000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x40000 + rnum - 1; + break; + default: + upslogx(LOG_ERR, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); + upsdebugx(3, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); + } + upsdebugx(3, "register num:%d, type: %d saddr %d, xaddr x%x\n", regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr + ); + } +} + /* Read a modbus register */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) { @@ -385,7 +424,7 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); + upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); break; } if (rval == -1) { @@ -561,52 +600,116 @@ int upscmd(const char *cmd, const char *arg) return rval; } -/* read signal state from modbus RIO, returns 0|1 state or -1 on communication error */ -int get_signal_state(devstate_t state) +/* read device state, returns 0 on success or -1 on communication error */ +int get_dev_state(devreg_t regnum, devstate_t *state) { - int rval = -1; - int reg_val; - regtype_t rtype = 0; /* register type */ - int addr = -1; /* register address */ - - /* assign register address and type */ - switch (state) { - case OL_T: - addr = sigar[OL_T].addr; - rtype = sigar[OL_T].type; - break; - case OB_T: - addr = sigar[OB_T].addr; - rtype = sigar[OB_T].type; - break; - case LB_T: - addr = sigar[LB_T].addr; - rtype = sigar[LB_T].type; - break; - case HB_T: - addr = sigar[HB_T].addr; - rtype = sigar[HB_T].type; - break; - case RB_T: - addr = sigar[RB_T].addr; - rtype = sigar[RB_T].type; - break; - case CHRG_T: - addr = sigar[CHRG_T].addr; - rtype = sigar[CHRG_T].type; + int i; /* local index */ + int rval = -1; /* return value */ + uint reg_val; /* register value */ + regtype_t rtype = 0; /* register type */ + int addr = regs[regnum].xaddr; + int rtype = regs[regnum].type; + + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval == -1) { + return rval; + } + + /* process register data */ + switch (regnum) { + case CHRG: + if (reg_val == CHRG_NONE) { + state->charge.state = CHRG_NONE; + state->charge.info = chrgs_i[CHRG_NONE]; + } else if (reg_val == CHRG_RECV) { + state->charge.state = CHRG_RECV; + state->charge.info = chrgs_i[CHRG_RECV]; + } else if (reg_val == CHRG_BULK) { + state->charge.state = CHRG_BULK; + state->charge.info = chrgs_i[CHRG_BULK]; + } else if (reg_val == CHRG_ABSR) { + state->charge.state = CHRG_ABSR; + state->charge.info = chrgs_i[CHRG_ABSR]; + } else if (reg_val == CHRG_FLOAT) { + state->charge.state = CHRG_FLOAT; + state->charge.info = chrgs_i[CHRG_FLOAT]; + } break; - case DISCHRG_T: - addr = sigar[DISCHRG_T].addr; - rtype = sigar[DISCHRG_T].type; + case BATV: /* "battery.voltage" */ + case LVDC: /* "output.voltage" */ + case LCUR: /* "output.current" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ + int n = snprintf(NULL, 0, "%f", fval); + char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(fval_s, "%f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + break; + case BSOH: + case BCEF: + case VAC: /* "input.voltage" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + int n = snprintf(NULL, 0, "%d", reg_val); + char *reg_val_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + break; + case BSOC: /* "battery.charge" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = (double )reg_val * regs[BSOC].scale; + int n = snprintf(NULL, 0, "%f", fval); + char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(fval_s, "%f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + break; + case BTMP: /* "battery.temperature" */ + case OTMP: /* "ups.temperature" */ + state->reg.val16 = reg_val; + double fval = reg_val - 273.15; + int n = snprintf(NULL, 0, "%f", fval); + char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(fval_s, "%f", fval); + state->reg.strval = fval_s; + break; + case PMNG: /* "ups.status" & "battery.charge" */ + if (reg_val == PMNG_BCKUP) { + state->power.state = PMNG_BCKUP; + state->power.info = pwrmng_i[PMNG_BCKUP]; + } else if (reg_val == PMNG_CHRGN) { + state->power.state = PMNG_CHRGN; + state->power.info = pwrmng_i[PMNG_CHRGN]; + } else if (reg_val == PMNG_BOOST) { + state->power.state = PMNG_BOOST; + state->power.info = pwrmng_i[PMNG_BOOST]; + } else if (reg_val == PMNG_NCHRG) { + state->power.state = PMNG_NCHRG; + state->power.info = pwrmng_i[PMNG_NCHRG]; + } break; - - case BYPASS_T: - case CAL_T: - case FSD_T: - case OFF_T: - case OVER_T: - case TRIM_T: - case BOOST_T: + case PRDN: /* "ups.model" */ + for (i = 0; i < DEV_NUMOF_MODELS; i++) { + if (prdnm_i[i].val == reg_val) { + break; + } + } + state->product.val = reg_val; + state->product.name = prdnm_i[i].name; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" @@ -622,10 +725,7 @@ int get_signal_state(devstate_t state) break; } - rval = register_read(mbctx, addr, rtype, ®_val); - if (rval > -1) { - rval = reg_val; - } + upsdebugx(3, "get_signal_state: state: %d", reg_val); return rval; } diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index e2cc76643a..14234d23fe 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -1,4 +1,4 @@ -/* adele_cbi.h - Driver for generic UPS connected via modbus RIO +/* adele_cbi.h - Driver for ADELE CB/CBI DC-UPS * * Copyright (C) * 2021 Dimitris Economou @@ -47,6 +47,12 @@ /* modbus access parameters */ #define MODBUS_SLAVE_ID 5 +/* number of modbus registers */ +#define MODBUS_NUMOF_REGS 98 + +/* number of device models */ +#define DEV_NUMOF_MODELS 10 + /* definition of register type */ enum regtype { COIL = 0, @@ -56,14 +62,13 @@ enum regtype { }; typedef enum regtype regtype_t; +/* product name info, "device.model" */ struct prodname { uint16_t val; char *name; }; typedef struct prodname prodname_t; - -/* product name */ -prodname_t prdn[] = { +prodname_t prdnm_i[] = { {1, "CBI1235A"}, {2, "CBI2420A"}, {3, "CBI4810A"}, @@ -77,13 +82,42 @@ prodname_t prdn[] = { {14, "CB4810A"} }; -/* charging status */ -char *chrgs[] = { +/* charging status info, "battery.charger.status" */ +char *chrgs_i[] = { "none", - "recovery", - "bulk", - "float" + "recovery", /* "resting" */ + "bulk", /* "charging" */ + "absorb", /* "charging" */ + "float" /* "floating" */ +}; +struct chrgs { + int state; + char *info; +}; +typedef struct chrgs chrgs_t; + +/* power management info, "ups.status", "battery.charger.status" */ +char *pwrmng_i[] = { + "backup", /* "OB", "discharging" */ + "charging", /* "OL" */ + "boost", + "not charging" +}; +struct pwrmng { + int state; + char *info; }; +typedef struct pwrmng pwrmng_t; + +/* general modbus register value */ +struct reg { + union { + uint16_t val16; + uint8_t val8; + }; + char *strval; +}; +typedef struct reg reg_t; /* * BIT MASKS and VALUES @@ -97,8 +131,8 @@ char *chrgs[] = { #define CHRG_FLOAT 4 /* power management */ -#define PMNG_BKUP 0 -#define PMNG_CHRG 1 +#define PMNG_BCKUP 0 +#define PMNG_CHRGN 1 #define PMNG_BOOST 2 #define PMNG_NCHRG 3 @@ -145,29 +179,35 @@ char *chrgs[] = { #define BSTA_CNNFLT 0x0020 /* connection fault */ -/* UPS device state enum */ -enum devstate { - CHRG = 0, /* Charging status */ - BATV, /* Battery voltage */ +/* UPS device reg enum */ +enum devreg { + CHRG = 0, /* Charging status, "battery.charger.status" */ + BATV, /* Battery voltage, "battery.voltage" */ BCEF = 6, /* Battery charge efficiency factor (CEF) */ BSOH, /* Battery state-of-health */ - BSOC = 9, /* Battery state-of-charge */ - BTMP = 11, /* Battery temperature in Kelvin units */ - PMNG = 15, /* Power management */ - PRDN = 21, /* Product name */ - FSD = 80, /* Force shutdown */ + BSOC = 9, /* Battery state-of-charge, "battery.charge" */ + BTMP = 11, /* Battery temperature in Kelvin units, "battery.temperature" */ + PMNG = 15, /* Power management, "ups.status" */ + OTMP = 20, /* Onboard temperature, "ups.temperature" */ + PRDN, /* Product name, "ups.model" */ + VAC = 24, /* AC voltage, "input.voltage" */ + LVDC, /* Load voltage, "output.voltage" */ + LCUR, /* Load current, "output.current" */ + BINH = 79, /* Backup inhibit */ + FSD, /* Force shutdown */ + TBUF, /* Time buffering */ BSTA = 89, /* Battery status alarms */ - SCSH = 90, /* SoH and SoC alarms */ - BVAL = 91, /* Battery voltage alarm */ - BTSF = 92, /* Battery temp sensor failure */ - DEVF = 93, /* Device failure */ - OBTA = 94, /* On board temp alarm */ - VACA = 95, /* VAC alarms */ - MAIN = 96 /* Mains status */ + SCSH, /* SoH and SoC alarms */ + BVAL, /* Battery voltage alarm */ + BTSF, /* Battery temp sensor failure */ + DEVF, /* Device failure */ + OBTA, /* On board temp alarm */ + VACA, /* VAC alarms */ + MAIN /* Mains status */ }; -typedef enum devstate devstate_t; +typedef enum devreg devreg_t; -/* UPS state signal attributes */ +/* UPS register attributes */ struct regattr { int num; int saddr; /* register start address */ @@ -177,6 +217,16 @@ struct regattr { }; typedef struct regattr regattr_t; +/* UPS device state info union */ +union devstate { + prodname_t product; + chrgs_t charge; + pwrmng_t power; + reg_t reg; +}; + +typedef union devstate devstate_t; + /* ADELE CBI registers */ regattr_t regs[] = { {40005, 0, 0, 1, HOLDING}, /* Charging status */ @@ -282,8 +332,4 @@ regattr_t regs[] = { {40046, 0, 0, 1, HOLDING}, /* Mains status */ {40038, 0, 0, 1, HOLDING} /* Load alarm */ }; - -#define NUMOF_REGS 14 -#define NOTUSED -1 - #endif /* ADELE_CBI_H */ From 46bebe0eed06012fc423788926ac389a93881919 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Sun, 9 Jan 2022 03:21:08 +0200 Subject: [PATCH 026/700] alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress --- drivers/adele_cbi.c | 293 +++++++++++++++++++++++++++++++++----------- drivers/adele_cbi.h | 198 ++++++++++++++++++++++++------ 2 files changed, 382 insertions(+), 109 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index d254d4c54b..f81137297b 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -25,27 +25,29 @@ #include #include -#define DRIVER_NAME "NUT Adele CBI driver" +#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" #define DRIVER_VERSION "0.01" /* variables */ -static modbus_t *mbctx = NULL; /* modbus memory context */ -static regattr_t sigar[NUMOF_SIG_STATES]; /* array of ups signal attributes */ -static int errcnt = 0; /* modbus access error counter */ - -static char *device_mfr = DEVICE_MFR; /* device manufacturer */ -static char *device_model = DEVICE_MODEL; /* device model */ -static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ -static char ser_parity = PARITY; /* serial port parity */ -static int ser_data_bit = DATA_BIT; /* serial port data bit */ -static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ -static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ -static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ -static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ -static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ -static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static int errcnt = 0; /* modbus access error counter */ +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static char *device_type = DEVICE_TYPE; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ +/* initialize register start address and hex address from register number */ +void reginit(); + /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); @@ -61,8 +63,8 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg); -/* read signal status */ -int get_signal_state(devreg_t state); +/* get device state */ +int get_dev_state(devreg_t regnum, devstate_t *state); /* count the time elapsed since start */ long time_elapsed(struct timeval *start); @@ -82,64 +84,71 @@ upsdrv_info_t upsdrv_info = { * driver functions */ +/* read configuration variables from ups.conf and connect to ups device */ +void upsdrv_initups(void) +{ + int rval; + upsdebugx(2, "upsdrv_initups"); + + reginit(); + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response time out to 200ms */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} + /* initialize ups driver information */ -void upsdrv_initinfo(void) { +void upsdrv_initinfo(void) +{ + devstate_t ds; /* device state */ upsdebugx(2, "upsdrv_initinfo"); /* set device information */ dstate_setinfo("device.mfr", "%s", device_mfr); dstate_setinfo("device.model", "%s", device_model); + dstate_setinfo("device.type", "%s", device_type); - /* register instant commands */ - if (sigar[FSD_T].addr != NOTUSED) { - dstate_addcmd("load.off"); - } + /* read ups model */ + get_dev_state(PRDN, &ds); + dstate_setinfo("ups.model", "%s", ds.product.name); + + /* register instant commands */ + dstate_addcmd("load.off"); /* set callback for instant commands */ upsh.instcmd = upscmd; } -/* open serial connection and connect to modbus RIO */ -void upsdrv_initups(void) -{ - int rval; - upsdebugx(2, "upsdrv_initups"); - - get_config_vars(); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response time out to 200ms */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -} /* update UPS signal state */ void upsdrv_updateinfo(void) @@ -147,11 +156,20 @@ void upsdrv_updateinfo(void) int rval; int online = -1; /* keep online state */ errcnt = 0; + devstate_t ds; /* device state */ upsdebugx(2, "upsdrv_updateinfo"); status_init(); /* initialize ups.status update */ alarm_init(); /* initialize ups.alarm update */ + /* get "battery.charger.status" */ + get_dev_state(CHRG, &ds); + dstate_setinfo("battery.charger.status", "%s", ds.charge.info); + + /* get "battery.voltage" */ + get_dev_state(BATV, &ds); + dstate_setinfo("battery.charger.status", "%s", ds.reg.strval); + /* * update UPS status regarding MAINS state either via OL | OB. * if both statuses are mapped to contacts then only OL is evaluated. @@ -600,16 +618,18 @@ int upscmd(const char *cmd, const char *arg) return rval; } -/* read device state, returns 0 on success or -1 on communication error */ +/* read device state, returns 0 on success or -1 on communication error + it formats state depending on register semantics */ int get_dev_state(devreg_t regnum, devstate_t *state) { int i; /* local index */ - int rval = -1; /* return value */ + int rval; /* return value */ uint reg_val; /* register value */ - regtype_t rtype = 0; /* register type */ - int addr = regs[regnum].xaddr; - int rtype = regs[regnum].type; + regtype_t rtype; /* register type */ + int addr; /* register address */ + addr = regs[regnum].xaddr; + rtype = regs[regnum].type; rval = register_read(mbctx, addr, rtype, ®_val); if (rval == -1) { return rval; @@ -650,6 +670,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->reg.strval = "0"; } break; + case TBUF: case BSOH: case BCEF: case VAC: /* "input.voltage" */ @@ -710,6 +731,137 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->product.val = reg_val; state->product.name = prdnm_i[i].name; break; + case BSTA: + if (reg_val & BSTA_REVPOL_M) { + bsta.alrm[BSTA_REVPOL_I].actv = 1; + } else { + bsta.alrm[BSTA_REVPOL_I].actv = 0; + } + if (reg_val & BSTA_NOCNND_M) { + bsta.alrm[BSTA_NOCNND_I].actv = 1; + } else { + bsta.alrm[BSTA_NOCNND_I].actv = 0; + } + if (reg_val & BSTA_CLSHCR_M) { + bsta.alrm[BSTA_CLSHCR_I].actv = 1; + } else { + bsta.alrm[BSTA_CLSHCR_I].actv = 0; + } + if (reg_val & BSTA_SULPHD_M) { + bsta.alrm[BSTA_SULPHD_I].actv = 1; + } else { + bsta.alrm[BSTA_SULPHD_I].actv = 0; + } + if (reg_val & BSTA_CHEMNS_M) { + bsta.alrm[BSTA_CHEMNS_I].actv = 1; + } else { + bsta.alrm[BSTA_CHEMNS_I].actv = 0; + } + if (reg_val & BSTA_CNNFLT_M) { + bsta.alrm[BSTA_CNNFLT_I].actv = 1; + } else { + bsta.alrm[BSTA_CNNFLT_I].actv = 0; + } + state->alrm = &bsta; + break; + case SCSH: + if (reg_val & SHSC_HIRESI_M) { + shsc.alrm[SHSC_HIRESI_I].actv = 1; + } else { + shsc.alrm[SHSC_HIRESI_I].actv = 0; + } + if (reg_val & SHSC_LOCHEF_M) { + shsc.alrm[SHSC_LOCHEF_I].actv = 1; + } else { + shsc.alrm[SHSC_LOCHEF_I].actv = 0; + } + if (reg_val & SHSC_LOEFCP_M) { + shsc.alrm[SHSC_LOEFCP_I].actv = 1; + } else { + shsc.alrm[SHSC_LOEFCP_I].actv = 0; + } + if (reg_val & SHSC_LOWSOC_M) { + shsc.alrm[SHSC_LOWSOC_I].actv = 1; + } else { + shsc.alrm[SHSC_LOWSOC_I].actv = 0; + } + state->alrm = &shsc; + break; + case BVAL: + if (reg_val & BVAL_HIALRM_M) { + bval.alrm[BVAL_HIALRM_I].actv = 1; + } else { + bval.alrm[BVAL_HIALRM_I].actv = 0; + } + if (reg_val & BVAL_LOALRM_M) { + bval.alrm[BVAL_LOALRM_I].actv = 1; + } else { + bval.alrm[BVAL_LOALRM_I].actv = 0; + } + if (reg_val & BVAL_BSTSFL_M) { + bval.alrm[BVAL_BSTSFL_I].actv = 1; + } else { + bval.alrm[BVAL_BSTSFL_I].actv = 0; + } + state->alrm = bval; + break; + case BTSF: + if (reg_val & BTSF_FCND_M) { + btsf.alrm[BTSF_FCND_I].actv = 1; + } else { + btsf.alrm[BTSF_FCND_I].actv = 0; + } + if (reg_val & BTSF_NCND_M) { + btsf.alrm[BTSF_NCND_I].actv = 1; + } else { + btsf.alrm[BTSF_NCND_I].actv = 0; + } + state->alrm = &btsf; + break; + case DEVF: + if (reg_val & DEVF_RCALRM_M) { + devf.alrm[DEVF_RCALRM_I].actv = 1; + } else { + devf.alrm[DEVF_RCALRM_I].actv = 0; + } + if (reg_val & DEVF_INALRM_M) { + devf.alrm[DEVF_INALRM_I].actv = 1; + } else { + devf.alrm[DEVF_INALRM_I].actv = 0; + } + if (reg_val & DEVF_LFNAVL_M) { + devf.alrm[DEVF_LFNAVL_I].actv = 1; + } else { + devf.alrm[DEVF_LFNAVL_I].actv = 0; + } + state->alrm = &devf; + break; + case VACA: + if (reg_val & VACA_HIALRM_M) { + vaca.alrm[VACA_HIALRM_I].actv = 1; + } else { + vaca.alrm[VACA_HIALRM_I].actv = 0; + } + if (reg_val == VACA_LOALRM_M) { + vaca.alrm[VACA_LOALRM_I].actv = 1; + } else { + vaca.alrm[VACA_LOALRM_I].actv = 0; + } + state->alrm = &vaca; + break; + case MAIN: + if (reg_val & MAINS_AVAIL_M) { + mains.alrm[MAINS_AVAIL_I].actv = 1; + } else { + mains.alrm[MAINS_AVAIL_I].actv = 0; + } + if (reg_val == SHUTD_REQST_M) { + mains.alrm[SHUTD_REQST_I].actv = 1; + } else { + mains.alrm[SHUTD_REQST_I].actv = 0; + } + state->alrm = &mains; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" @@ -725,8 +877,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) break; } - - upsdebugx(3, "get_signal_state: state: %d", reg_val); + upsdebugx(3, "get_dev_state: state: %d", reg_val); return rval; } diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 14234d23fe..6cd05989a5 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -27,6 +27,7 @@ /* UPS device details */ #define DEVICE_MFR "ADELE" +#define DEVICE_TYPE "DC-UPS" #define DEVICE_MODEL "CB/CBI" /* serial access parameters */ @@ -85,10 +86,10 @@ prodname_t prdnm_i[] = { /* charging status info, "battery.charger.status" */ char *chrgs_i[] = { "none", - "recovery", /* "resting" */ - "bulk", /* "charging" */ - "absorb", /* "charging" */ - "float" /* "floating" */ + "resting", /* recovering */ + "charging", /* bulk */ + "charging", /* absorb */ + "floating" /* float */ }; struct chrgs { int state; @@ -119,6 +120,20 @@ struct reg { }; typedef struct reg reg_t; +/* general alarm struct */ +struct alrm { + int actv; /* active flag */ + char *descr; /* description field */ +}; +typedef struct alrm alrm_t; + +/* general alarm array */ +struct alrm_ar { + int alrm_c; /* alarm count */ + alrm_t alrm[]; /* alarm array */ +}; +typedef struct alrm_ar alrm_ar_t; + /* * BIT MASKS and VALUES */ @@ -139,45 +154,151 @@ typedef struct reg reg_t; /* product name */ #define PRDN_MAX 14 -/* Mains status */ -#define MAINS_AVAIL 0x0001 /* available */ -#define SHUTD_REQST 0x0002 /* shutdown requested */ +/* Mains alarm masks */ +#define MAINS_AVAIL_M 0x0001 /* 0: available 1: not available */ +#define SHUTD_REQST_M 0x0002 /* shutdown requested */ -/* AC input voltage alarms */ -#define VACA_HIALRM 0x0001 /* high alarm */ -#define VACA_LOALRM 0x0002 /* low alarm */ +/* Mains alarm indices */ +#define MAINS_AVAIL_I 0 /* 0: available 1: not available */ +#define SHUTD_REQST_I 1 /* shutdown requested */ + +/* AC input voltage alarm masks */ +#define VACA_HIALRM_M 0x0001 /* high alarm */ +#define VACA_LOALRM_M 0x0002 /* low alarm */ + +/* AC input voltage alarm indices */ +#define VACA_HIALRM_I 0 /* high alarm */ +#define VACA_LOALRM_I 1 /* low alarm */ /* Onboard temperature alarm */ #define OBTA_HIALRM 1 /* high alarm */ -/* Device failure */ -#define DEVF_RCALRM 0x0001 /* rectifier failure */ -#define DEVF_INALRM 0x0006 /* internal failure */ -#define DEVF_LFNAVL 0x0008 /* lifetest not available */ +/* Device failure alarm masks */ +#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ +#define DEVF_INALRM_M 0x0006 /* internal failure */ +#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ + +/* Device failure alarm indices */ +#define DEVF_RCALRM_I 0 /* rectifier failure */ +#define DEVF_INALRM_I 1 /* internal failure */ +#define DEVF_LFNAVL_I 2 /* lifetest not available */ + +/* Battery temp sensor failure alarm masks */ +#define BTSF_FCND_M 0x0001 /* connection fault */ +#define BTSF_NCND_M 0x0002 /* not connected */ + +/* Battery temp sensor failure alarm indices */ +#define BTSF_FCND_I 0 /* connection fault */ +#define BTSF_NCND_I 1 /* not connected */ + +/* Battery voltage alarm masks */ +#define BVAL_HIALRM_M 0x0001 /* high voltage */ +#define BVAL_LOALRM_M 0x0002 /* low voltage */ +#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ + +/* Battery voltage alarm indices */ +#define BVAL_HIALRM_I 0 /* high voltage */ +#define BVAL_LOALRM_I 1 /* low voltage */ +#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ + +/* SoH and SoC alarm masks */ +#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ -/* Battery temp sensor failure */ -#define BTSF_FCND 0x0001 /* connection fault */ -#define BTSF_NCND 0x0001 /* not connected */ +/* SoH and SoC alarm indices */ +#define SHSC_HIRESI_I 0 /* high internal resistance */ +#define SHSC_LOCHEF_I 1 /* low charge efficiency */ +#define SHSC_LOEFCP_I 2 /* low effective capacity */ +#define SHSC_LOWSOC_I 3 /* low state of charge */ -/* Battery voltage alarm */ -#define BVAL_HIALRM 0x0001 /* high voltage */ -#define BVAL_LOALRM 0x0002 /* low voltage */ -#define BVAL_BSTSFL 0x0004 /* battery start with battery flat */ +/* Battery status alarm masks */ +#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ +#define BSTA_NOCNND_M 0x0002 /* not connected */ +#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ +#define BSTA_SULPHD_M 0x0008 /* sulphated */ +#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT_M 0x0020 /* connection fault */ -/* SoH and SoC alarms */ -#define SHSC_HIRESI 0x0001 /* high internal resistance */ -#define SHSC_LOCHEF 0x0002 /* low charge efficiency */ -#define SHSC_LOEFCP 0x0004 /* low effective capacity */ -#define SHSC_LOWSOC 0x0040 /* low state of charge */ +/* Battery status alarm indices */ +#define BSTA_REVPOL_I 0 /* reversed polarity */ +#define BSTA_NOCNND_I 1 /* not connected */ +#define BSTA_CLSHCR_I 2 /* cell short circuit */ +#define BSTA_SULPHD_I 3 /* sulphated */ +#define BSTA_CHEMNS_I 4 /* chemistry not supported */ +#define BSTA_CNNFLT_I 5 /* connection fault */ -/* Battery status alarms */ -#define BSTA_REVPOL 0x0001 /* reversed polarity */ -#define BSTA_NOCNND 0x0002 /* not connected */ -#define BSTA_CLSHCR 0x0004 /* cell short circuit */ -#define BSTA_SULPHD 0x0008 /* sulphated */ -#define BSTA_CHEMNS 0x0010 /* chemistry not supported */ -#define BSTA_CNNFLT 0x0020 /* connection fault */ +/* input mains and shutdown alarms */ +alrm_ar_t mains = { + 2, + { + {0, "input voltage not available"}, + {0, "ups shutdown requested"} + } +}; + +/* AC input voltage alarms */ +alrm_ar_t vaca = { + 2, + { + {0, "input voltage high alarm"}, + {0, "input voltage low alarm"} + } +}; +/* device failure alarms */ +alrm_ar_t devf = { + 3, + { + {0, "UPS rectifier failure"}, + {0, "UPS internal failure"}, + {0, "UPS lifetest not available"} + } +}; + +/* battery sensor failure alarms */ +alrm_ar_t btsf = { + 2, + { + {0, "battery temp sensor connection fault"}, + {0, "battery temp sensor not connected"} + } +}; + +/* battery voltage alarms */ +alrm_ar_t bval = { + 3, + { + {0, "battery high voltage"}, + {0, "battery low voltage"}, + {0, "battery start with battery flat"} + } +}; + +/* battery SoH and SoC alarms */ +alrm_ar_t shsc = { + 4, + { + {0, "battery high internal resistance"}, + {0, "battery low charge efficiency"}, + {0, "battery low effective capacity"}, + {0, "battery low state of charge"} + } +}; + +/* battery status alarm */ +alrm_ar_t bsta = { + 6, + { + {0, "battery reversed polarity"}, + {0, "battery not connected"}, + {0, "battery cell short circuit"}, + {0, "battery sulphated"}, + {0, "battery chemistry not supported"}, + {0, "battery connection fault"} + } +}; /* UPS device reg enum */ enum devreg { @@ -195,7 +316,7 @@ enum devreg { LCUR, /* Load current, "output.current" */ BINH = 79, /* Backup inhibit */ FSD, /* Force shutdown */ - TBUF, /* Time buffering */ + TBUF, /* Time buffering, "battery runtime" */ BSTA = 89, /* Battery status alarms */ SCSH, /* SoH and SoC alarms */ BVAL, /* Battery voltage alarm */ @@ -219,10 +340,11 @@ typedef struct regattr regattr_t; /* UPS device state info union */ union devstate { - prodname_t product; - chrgs_t charge; - pwrmng_t power; - reg_t reg; + prodname_t product; /* ups model name */ + chrgs_t charge; /* charging status */ + pwrmng_t power; /* ups status */ + reg_t reg; /* state register*/ + alrm_ar_t *alrm; /* alarm statuses */ }; typedef union devstate devstate_t; From dcd932dbb1fdc50ec2e4188f7969dffcc1b96790 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Mon, 10 Jan 2022 13:33:46 +0200 Subject: [PATCH 027/700] first testing release --- drivers/adele_cbi.c | 604 +++++++++++++++----------------------------- drivers/adele_cbi.h | 12 +- 2 files changed, 214 insertions(+), 402 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index f81137297b..61eb099775 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -51,6 +51,9 @@ void reginit(); /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); +/* get device state */ +int get_dev_state(devreg_t regnum, devstate_t *state); + /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port); @@ -60,16 +63,15 @@ void modbus_reconnect(); /* modbus register read function */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); +/* modbus register write function */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg); -/* get device state */ -int get_dev_state(devreg_t regnum, devstate_t *state); - /* count the time elapsed since start */ long time_elapsed(struct timeval *start); -int register_write(modbus_t *mb, int addr, regtype_t type, void *data); /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -153,8 +155,8 @@ void upsdrv_initinfo(void) /* update UPS signal state */ void upsdrv_updateinfo(void) { - int rval; - int online = -1; /* keep online state */ + int rval; + int i; /* local index */ errcnt = 0; devstate_t ds; /* device state */ @@ -162,132 +164,198 @@ void upsdrv_updateinfo(void) status_init(); /* initialize ups.status update */ alarm_init(); /* initialize ups.alarm update */ + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ + rval = get_dev_state(MAIN, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains.alrm[MAINS_AVAIL_I].descr); + } else { + status_set("OL"); + } + if (ds.alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains.alrm[SHUTD_REQST_I].descr); + } + + /* + * update UPS status regarding battery voltage + */ + rval = get_dev_state(BVAL, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set( bval.alrm[BVAL_LOALRM_I].descr); + } + if (ds.alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set( bval.alrm[BVAL_HIALRM_I].descr); + } + if (ds.alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set( bval.alrm[BVAL_BSTSFL_I].descr); + } + + /* get "battery.voltage" */ + rval = get_dev_state(BATV, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.voltage", "%s", ds.reg.strval); + + /* + * update UPS status regarding battery charger status + */ + /* get "battery.charger.status" */ - get_dev_state(CHRG, &ds); + rval = get_dev_state(CHRG, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.charge.state == CHRG_BULK || ds.charge.state == CHRG_ABSR) { + status_set("CHRG"); + } dstate_setinfo("battery.charger.status", "%s", ds.charge.info); - /* get "battery.voltage" */ - get_dev_state(BATV, &ds); - dstate_setinfo("battery.charger.status", "%s", ds.reg.strval); + rval = get_dev_state(PMNG, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + if (ds.power.state == PMNG_BOOST) { + status_set("BOOST"); + } - /* - * update UPS status regarding MAINS state either via OL | OB. - * if both statuses are mapped to contacts then only OL is evaluated. - */ - if (sigar[OL_T].addr != NOTUSED) { - rval = get_signal_state(OL_T); - upsdebugx(2, "OL value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[OL_T].noro)) { - status_set("OL"); - online = 1; - } else { - status_set("OB"); - online = 0; + /* + * update UPS battery state of charge + */ + rval = get_dev_state(BSOC, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.charge", "%s", ds.reg.strval); - /* if DISCHRG state is not mapped to a contact and UPS is on - * batteries set status to DISCHRG state */ - if (sigar[DISCHRG_T].addr == NOTUSED) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } + /* + * update UPS AC input state + */ + rval = get_dev_state(VACA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } + rval = get_dev_state(VAC, &ds); + dstate_setinfo("input.voltage", "%s", ds.reg.strval); - } - } else if (sigar[OB_T].addr != NOTUSED) { - rval = get_signal_state(OB_T); - upsdebugx(2, "OB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[OB_T].noro)) { - status_set("OB"); - online = 0; - if (sigar[DISCHRG_T].addr == NOTUSED) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } else { - status_set("OL"); - online = 1; - } - } + /* + * update UPS onboard temperature state + */ + rval = get_dev_state(OBTA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } + rval = get_dev_state(OTMP, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("ups.temperature", "%s", ds.reg.strval); /* - * update UPS status regarding CHARGING state via HB. HB is usually - * mapped to "ready" contact when closed indicates a charging state > 85% - */ - if (sigar[HB_T].addr != NOTUSED) { - rval = get_signal_state(HB_T); - upsdebugx(2, "HB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[HB_T].noro)) { - status_set("HB"); - dstate_setinfo("battery.charger.status", "resting"); - } else if (online == 1 && sigar[CHRG_T].addr == NOTUSED && errcnt == 0) { - status_set("CHRG"); - dstate_setinfo("battery.charger.status", "charging"); - } else if (online == 0 && sigar[DISCHRG_T].addr == NOTUSED && errcnt == 0) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } + * update UPS battery temperature state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } + rval = get_dev_state(BTMP, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.temperature", "%s", ds.reg.strval); + rval = get_dev_state(TBUF, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.runtime", "%s", ds.reg.strval); - /* - * update UPS status regarding DISCHARGING state via LB. LB is mapped - * to "battery low" contact. - */ - if (sigar[LB_T].addr != NOTUSED) { - rval = get_signal_state(LB_T); - upsdebugx(2, "LB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[LB_T].noro)) { - status_set("LB"); - alarm_set("Low Battery (Charge)"); - } - } + /* + * update UPS device failure state + */ + rval = get_dev_state(DEVF, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } - /* - * update UPS status regarding battery HEALTH state via RB. RB is mapped - * to "replace battery" contact - */ - if (sigar[RB_T].addr != NOTUSED) { - rval = get_signal_state(RB_T); - upsdebugx(2, "RB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[RB_T].noro)) { - status_set("RB"); - alarm_set("Replace Battery"); - } - } + /* + * update UPS SoH and SoC states + */ + rval = get_dev_state(SCSH, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } - /* - * update UPS status regarding battery HEALTH state via RB. RB is mapped - * to "replace battery" contact - */ - if (sigar[CHRG_T].addr != NOTUSED) { - rval = get_signal_state(CHRG_T); - upsdebugx(2, "CHRG value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[CHRG_T].noro)) { - status_set("CHRG"); - dstate_setinfo("battery.charger.status", "charging"); - } - } else if (sigar[DISCHRG_T].addr != NOTUSED) { - rval = get_signal_state(DISCHRG_T); - upsdebugx(2, "DISCHRG value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[DISCHRG_T].noro)) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } + /* + * update UPS battery state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } - /* check for communication errors */ + /* + * update UPS load status + */ + rval = get_dev_state(LVDC, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("output.voltage", "%s", ds.reg.strval); + rval = get_dev_state(LCUR, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("output.current", "%s", ds.reg.strval); + + + /* check for communication errors */ if (errcnt == 0) { alarm_commit(); status_commit(); @@ -551,67 +619,24 @@ int upscmd(const char *cmd, const char *arg) { int rval; int data; - struct timeval start; - long etime; if (!strcasecmp(cmd, "load.off")) { - if (sigar[FSD_T].addr != NOTUSED && - (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) - ) { - data = 1 ^ sigar[FSD_T].noro; - rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); - if (rval == -1) { - upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - sigar[FSD_T].addr, - sigar[FSD_T].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d", sigar[FSD_T].addr, data); - rval = STAT_INSTCMD_HANDLED; - } - - /* if pulse has been defined and rising edge was successful */ - if (FSD_pulse_duration != NOTUSED && rval == STAT_INSTCMD_HANDLED) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for FSD_pulse_duration ms */ - while ((etime = time_elapsed(&start)) < FSD_pulse_duration); - - data = 0 ^ sigar[FSD_T].noro; - rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); - if (rval == -1) { - upslogx(LOG_ERR, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - sigar[FSD_T].addr, - sigar[FSD_T].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d, elapsed time: %lims", - sigar[FSD_T].addr, - data, - etime - ); - rval = STAT_INSTCMD_HANDLED; - } - } - } else { - upslogx(LOG_NOTICE,"load.off: failed (FSD address undefined or invalid register type) [%s] [%s]", - cmd, - arg - ); - rval = STAT_INSTCMD_FAILED; - } - } else { + data = 1; + rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + regs[FSD].xaddr, + regs[FSD].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); + rval = STAT_INSTCMD_HANDLED; + } + } else { upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); rval = STAT_INSTCMD_UNKNOWN; } @@ -803,7 +828,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) } else { bval.alrm[BVAL_BSTSFL_I].actv = 0; } - state->alrm = bval; + state->alrm = &bval; break; case BTSF: if (reg_val & BTSF_FCND_M) { @@ -884,14 +909,6 @@ int get_dev_state(devreg_t regnum, devstate_t *state) /* get driver configuration parameters */ void get_config_vars() { - int i; /* local index */ - - /* initialize sigar table */ - for (i = 0; i < NUMOF_SIG_STATES; i++) { - sigar[i].addr = NOTUSED; - sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ - } - /* check if device manufacturer is set ang get the value */ if (testvar("device_mfr")) { device_mfr = getval("device_mfr"); @@ -970,217 +987,6 @@ void get_config_vars() } } upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); - - /* check if OL address is set and get the value */ - if (testvar("OL_addr")) { - sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); - if (testvar("OL_noro")) { - sigar[OL_T].noro = (int)strtol(getval("OL_noro"), NULL, 10); - if (sigar[OL_T].noro != 1) { - sigar[OL_T].noro = 0; - } - } - } - - /* check if OL register type is set and get the value otherwise set to INPUT_B */ - if (testvar("OL_regtype")) { - sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); - if (sigar[OL_T].type < COIL || sigar[OL_T].type > HOLDING) { - sigar[OL_T].type = INPUT_B; - } - } else { - sigar[OL_T].type = INPUT_B; - } - - /* check if OB address is set and get the value */ - if (testvar("OB_addr")) { - sigar[OB_T].addr = (int)strtol(getval("OB_addr"), NULL, 0); - } - if (testvar("OB_noro")) { - sigar[OB_T].noro = (int)strtol(getval("OB_noro"), NULL, 10); - if (sigar[OB_T].noro != 1) { - sigar[OB_T].noro = 0; - } - } - - /* check if OB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("OB_regtype")) { - sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); - if (sigar[OB_T].type < COIL || sigar[OB_T].type > HOLDING) { - sigar[OB_T].type = INPUT_B; - } - } else { - sigar[OB_T].type = INPUT_B; - } - - /* check if LB address is set and get the value */ - if (testvar("LB_addr")) { - sigar[LB_T].addr = (int)strtol(getval("LB_addr"), NULL, 0); - if (testvar("LB_noro")) { - sigar[LB_T].noro = (int)strtol(getval("LB_noro"), NULL, 10); - if (sigar[LB_T].noro != 1) { - sigar[LB_T].noro = 0; - } - } - } - - /* check if LB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("LB_regtype")) { - sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); - if (sigar[LB_T].type < COIL || sigar[LB_T].type > HOLDING) { - sigar[LB_T].type = INPUT_B; - } - } else { - sigar[LB_T].type = INPUT_B; - } - - /* check if HB address is set and get the value */ - if (testvar("HB_addr")) { - sigar[HB_T].addr = (int)strtol(getval("HB_addr"), NULL, 0); - if (testvar("HB_noro")) { - sigar[HB_T].noro = (int)strtol(getval("HB_noro"), NULL, 10); - if (sigar[HB_T].noro != 1) { - sigar[HB_T].noro = 0; - } - } - } - - /* check if HB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("HB_regtype")) { - sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); - if (sigar[HB_T].type < COIL || sigar[HB_T].type > HOLDING) { - sigar[HB_T].type = INPUT_B; - } - } else { - sigar[HB_T].type = INPUT_B; - } - - /* check if RB address is set and get the value */ - if (testvar("RB_addr")) { - sigar[RB_T].addr = (int)strtol(getval("RB_addr"), NULL, 0); - if (testvar("RB_noro")) { - sigar[RB_T].noro = (int)strtol(getval("RB_noro"), NULL, 10); - if (sigar[RB_T].noro != 1) { - sigar[RB_T].noro = 0; - } - } - } - - /* check if RB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("RB_regtype")) { - sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); - if (sigar[RB_T].type < COIL || sigar[RB_T].type > HOLDING) { - sigar[RB_T].type = INPUT_B; - } - } else { - sigar[RB_T].type = INPUT_B; - } - - /* check if CHRG address is set and get the value */ - if (testvar("CHRG_addr")) { - sigar[CHRG_T].addr = (int)strtol(getval("CHRG_addr"), NULL, 0); - if (testvar("CHRG_noro")) { - sigar[CHRG_T].noro = (int)strtol(getval("CHRG_noro"), NULL, 10); - if (sigar[CHRG_T].noro != 1) { - sigar[CHRG_T].noro = 0; - } - } - } - - /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ - if (testvar("CHRG_regtype")) { - sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); - if (sigar[CHRG_T].type < COIL || sigar[CHRG_T].type > HOLDING) { - sigar[CHRG_T].type = INPUT_B; - } - } else { - sigar[CHRG_T].type = INPUT_B; - } - - /* check if DISCHRG address is set and get the value */ - if (testvar("DISCHRG_addr")) { - sigar[DISCHRG_T].addr = (int)strtol(getval("DISCHRG_addr"), NULL, 0); - if (testvar("DISCHRG_noro")) { - sigar[DISCHRG_T].noro = (int)strtol(getval("DISCHRG_noro"), NULL, 10); - if (sigar[DISCHRG_T].noro != 1) { - sigar[DISCHRG_T].noro = 0; - } - } - } - - /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ - if (testvar("DISCHRG_regtype")) { - sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); - if (sigar[DISCHRG_T].type < COIL || sigar[DISCHRG_T].type > HOLDING) { - sigar[DISCHRG_T].type = INPUT_B; - } - } else { - sigar[DISCHRG_T].type = INPUT_B; - } - - /* check if FSD address is set and get the value */ - if (testvar("FSD_addr")) { - sigar[FSD_T].addr = (int)strtol(getval("FSD_addr"), NULL, 0); - if (testvar("FSD_noro")) { - sigar[FSD_T].noro = (int)strtol(getval("FSD_noro"), NULL, 10); - if (sigar[FSD_T].noro != 1) { - sigar[FSD_T].noro = 0; - } - } - } - - /* check if FSD register type is set and get the value otherwise set to COIL */ - if (testvar("FSD_regtype")) { - sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); - if (sigar[FSD_T].type < COIL || sigar[FSD_T].type > HOLDING) { - sigar[FSD_T].type = COIL; - } - } else { - sigar[FSD_T].type = COIL; - } - - /* check if FSD pulse duration is set and get the value */ - if (testvar("FSD_pulse_duration")) { - FSD_pulse_duration = (int) strtol(getval("FSD_pulse_duration"), NULL, 10); - } - upsdebugx(2, "FSD_pulse_duration %d", FSD_pulse_duration); - - /* debug loop over signal array */ - for (i = 0; i < NUMOF_SIG_STATES; i++) { - if (sigar[i].addr != NOTUSED) { - char *signame; - switch (i) { - case OL_T: - signame = "OL"; - break; - case OB_T: - signame = "OB"; - break; - case LB_T: - signame = "LB"; - break; - case HB_T: - signame = "HB"; - break; - case RB_T: - signame = "RB"; - break; - case FSD_T: - signame = "FSD"; - break; - case CHRG_T: - signame = "CHRG"; - break; - case DISCHRG_T: - signame = "DISCHRG"; - break; - default: - signame = "NOTUSED"; - break; - } - upsdebugx(2, "%s, addr:0x%x, type:%d", signame, sigar[i].addr, sigar[i].type); - } - } } /* create a new modbus context based on connection type (serial or TCP) */ diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 6cd05989a5..3a823ded3a 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -54,6 +54,12 @@ /* number of device models */ #define DEV_NUMOF_MODELS 10 +/* shutdown repeat on error */ +#define FSD_REPEAT_CNT 3 + +/* shutdown repeat interval in ms */ +#define FSD_REPEAT_INTRV 1500 + /* definition of register type */ enum regtype { COIL = 0, @@ -155,11 +161,11 @@ typedef struct alrm_ar alrm_ar_t; #define PRDN_MAX 14 /* Mains alarm masks */ -#define MAINS_AVAIL_M 0x0001 /* 0: available 1: not available */ +#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ #define SHUTD_REQST_M 0x0002 /* shutdown requested */ /* Mains alarm indices */ -#define MAINS_AVAIL_I 0 /* 0: available 1: not available */ +#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ #define SHUTD_REQST_I 1 /* shutdown requested */ /* AC input voltage alarm masks */ @@ -316,7 +322,7 @@ enum devreg { LCUR, /* Load current, "output.current" */ BINH = 79, /* Backup inhibit */ FSD, /* Force shutdown */ - TBUF, /* Time buffering, "battery runtime" */ + TBUF, /* Time buffering, "battery.runtime" */ BSTA = 89, /* Battery status alarms */ SCSH, /* SoH and SoC alarms */ BVAL, /* Battery voltage alarm */ From 596fa432213e6a5d281b2ebece949cc0515fb22d Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Tue, 11 Jan 2022 01:43:36 +0200 Subject: [PATCH 028/700] ghost alarms bug fix, other bug fixes --- drivers/adele_cbi.c | 843 ++++++++++++++++++++++++-------------------- drivers/adele_cbi.h | 51 +-- 2 files changed, 500 insertions(+), 394 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index 61eb099775..852629a7de 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -1,7 +1,7 @@ /* adele_cbi.c - driver for ADELE CB/CBI DC-UPS * * Copyright (C) - * 2021 Dimitris Economou + * 2022 Dimitris Economou * * 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 @@ -25,11 +25,12 @@ #include #include -#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" +#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" #define DRIVER_VERSION "0.01" /* variables */ static modbus_t *mbctx = NULL; /* modbus memory context */ +static devstate_t *dstate = NULL; /* device state context */ static int errcnt = 0; /* modbus access error counter */ static char *device_mfr = DEVICE_MFR; /* device manufacturer */ static char *device_model = DEVICE_MODEL; /* device model */ @@ -52,7 +53,7 @@ void reginit(); void get_config_vars(void); /* get device state */ -int get_dev_state(devreg_t regnum, devstate_t *state); +int get_dev_state(devreg_t regindx, devstate_t **dstate); /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port); @@ -75,11 +76,11 @@ long time_elapsed(struct timeval *start); /* driver description structure */ upsdrv_info_t upsdrv_info = { - DRIVER_NAME, - DRIVER_VERSION, - "Dimitris Economou \n", - DRV_BETA, - {NULL} + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} }; /* @@ -92,6 +93,8 @@ void upsdrv_initups(void) int rval; upsdebugx(2, "upsdrv_initups"); + + dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); reginit(); get_config_vars(); @@ -132,56 +135,62 @@ void upsdrv_initups(void) /* initialize ups driver information */ void upsdrv_initinfo(void) { - devstate_t ds; /* device state */ - upsdebugx(2, "upsdrv_initinfo"); + devstate_t *ds = dstate; /* device state context */ + upsdebugx(2, "upsdrv_initinfo"); - /* set device information */ - dstate_setinfo("device.mfr", "%s", device_mfr); - dstate_setinfo("device.model", "%s", device_model); + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); dstate_setinfo("device.type", "%s", device_type); /* read ups model */ get_dev_state(PRDN, &ds); - dstate_setinfo("ups.model", "%s", ds.product.name); + dstate_setinfo("ups.model", "%s", ds->product.name); + upslogx(LOG_INFO, "ups.model = %s", ds->product.name); /* register instant commands */ - dstate_addcmd("load.off"); + dstate_addcmd("load.off"); - /* set callback for instant commands */ - upsh.instcmd = upscmd; + /* set callback for instant commands */ + upsh.instcmd = upscmd; } /* update UPS signal state */ void upsdrv_updateinfo(void) { - int rval; - int i; /* local index */ - errcnt = 0; - devstate_t ds; /* device state */ - - upsdebugx(2, "upsdrv_updateinfo"); - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ - - /* - * update UPS status regarding MAINS and SHUTDOWN request - * - OL: On line (mains is present) - * - OB: On battery (mains is not present) - */ + int rval; /* return value */ + int i; /* local index */ + devstate_t *ds = dstate; /* device state */ + + upsdebugx(2, "upsdrv_updateinfo"); + + errcnt = 0; /* initialize error counter to zero */ + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ + + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ rval = get_dev_state(MAIN, &ds); if (rval == -1) { errcnt++; - } - if (ds.alrm->alrm[MAINS_AVAIL_I].actv) { - status_set("OB"); - alarm_set(mains.alrm[MAINS_AVAIL_I].descr); } else { - status_set("OL"); - } - if (ds.alrm->alrm[SHUTD_REQST_I].actv) { - status_set("FSD"); - alarm_set(mains.alrm[SHUTD_REQST_I].descr); + if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains.alrm[MAINS_AVAIL_I].descr); + upslogx(LOG_INFO, "ups.status = OB"); + } else { + status_set("OL"); + upslogx(LOG_INFO, "ups.status = OL"); + } + if (ds->alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains.alrm[SHUTD_REQST_I].descr); + upslogx(LOG_INFO, "ups.status = FSD"); + } } /* @@ -190,26 +199,31 @@ void upsdrv_updateinfo(void) rval = get_dev_state(BVAL, &ds); if (rval == -1) { errcnt++; - } - if (ds.alrm->alrm[BVAL_LOALRM_I].actv) { - status_set("LB"); - alarm_set( bval.alrm[BVAL_LOALRM_I].descr); - } - if (ds.alrm->alrm[BVAL_HIALRM_I].actv) { - status_set("HB"); - alarm_set( bval.alrm[BVAL_HIALRM_I].descr); - } - if (ds.alrm->alrm[BVAL_BSTSFL_I].actv) { - alarm_set( bval.alrm[BVAL_BSTSFL_I].descr); + } else { + if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set(bval.alrm[BVAL_LOALRM_I].descr); + upslogx(LOG_INFO, "ups.status = LB"); + } + if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set(bval.alrm[BVAL_HIALRM_I].descr); + upslogx(LOG_INFO, "ups.status = HB"); + } + if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); + upslogx(LOG_INFO, "battery start with battery flat"); + } } /* get "battery.voltage" */ rval = get_dev_state(BATV, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); } - dstate_setinfo("battery.voltage", "%s", ds.reg.strval); - /* * update UPS status regarding battery charger status */ @@ -218,22 +232,27 @@ void upsdrv_updateinfo(void) rval = get_dev_state(CHRG, &ds); if (rval == -1) { errcnt++; + } else { + if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { + status_set("CHRG"); + upslogx(LOG_INFO, "ups.status = CHRG"); + } + dstate_setinfo("battery.charger.status", "%s", ds->charge.info); + upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); } - if (ds.charge.state == CHRG_BULK || ds.charge.state == CHRG_ABSR) { - status_set("CHRG"); - } - dstate_setinfo("battery.charger.status", "%s", ds.charge.info); - rval = get_dev_state(PMNG, &ds); if (rval == -1) { errcnt++; - } - if (ds.power.state == PMNG_BCKUP) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - if (ds.power.state == PMNG_BOOST) { - status_set("BOOST"); + } else { + if (ds->power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + upslogx(LOG_INFO, "ups.status = DISCHRG"); + } + if (ds->power.state == PMNG_BOOST) { + status_set("BOOST"); + upslogx(LOG_INFO, "ups.status = BOOST"); + } } /* @@ -242,8 +261,10 @@ void upsdrv_updateinfo(void) rval = get_dev_state(BSOC, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.charge", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); } - dstate_setinfo("battery.charge", "%s", ds.reg.strval); /* * update UPS AC input state @@ -251,14 +272,17 @@ void upsdrv_updateinfo(void) rval = get_dev_state(VACA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } } + rval = get_dev_state(VAC, &ds); + dstate_setinfo("input.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); } - rval = get_dev_state(VAC, &ds); - dstate_setinfo("input.voltage", "%s", ds.reg.strval); /* * update UPS onboard temperature state @@ -266,40 +290,49 @@ void upsdrv_updateinfo(void) rval = get_dev_state(OBTA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } } } rval = get_dev_state(OTMP, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("ups.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); } - dstate_setinfo("ups.temperature", "%s", ds.reg.strval); - /* * update UPS battery temperature state */ rval = get_dev_state(BSTA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } rval = get_dev_state(BTMP, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); } - dstate_setinfo("battery.temperature", "%s", ds.reg.strval); rval = get_dev_state(TBUF, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.runtime", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); } - dstate_setinfo("battery.runtime", "%s", ds.reg.strval); /* * update UPS device failure state @@ -307,10 +340,12 @@ void upsdrv_updateinfo(void) rval = get_dev_state(DEVF, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } @@ -320,10 +355,12 @@ void upsdrv_updateinfo(void) rval = get_dev_state(SCSH, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } @@ -333,10 +370,12 @@ void upsdrv_updateinfo(void) rval = get_dev_state(BSTA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } @@ -346,56 +385,58 @@ void upsdrv_updateinfo(void) rval = get_dev_state(LVDC, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("output.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); } - dstate_setinfo("output.voltage", "%s", ds.reg.strval); rval = get_dev_state(LCUR, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("output.current", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); } - dstate_setinfo("output.current", "%s", ds.reg.strval); - - /* check for communication errors */ - if (errcnt == 0) { - alarm_commit(); - status_commit(); - dstate_dataok(); - } else { - upsdebugx(2,"Communication errors: %d", errcnt); - dstate_datastale(); - } + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2, "Communication errors: %d", errcnt); + dstate_datastale(); + } } /* shutdown UPS */ void upsdrv_shutdown(void) { - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); - case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); } /* print driver usage info */ @@ -406,13 +447,13 @@ void upsdrv_help(void) /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { - addvar(VAR_VALUE, "device_mfr", "device manufacturer"); - addvar(VAR_VALUE, "device_model", "device model"); - addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); - addvar(VAR_VALUE, "ser_parity", "serial port parity"); - addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); - addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); - addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); @@ -422,10 +463,13 @@ void upsdrv_makevartable(void) /* close modbus connection and free modbus context allocated memory */ void upsdrv_cleanup(void) { - if (mbctx != NULL) { - modbus_close(mbctx); - modbus_free(mbctx); - } + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } + if (dstate != NULL) { + free(dstate); + } } /* @@ -437,7 +481,7 @@ void reginit() { int i; /* local index */ - for (i = 1; i < MODBUS_NUMOF_REGS; i++) { + for (i = 0; i < MODBUS_NUMOF_REGS; i++) { int rnum = regs[i].num; switch (regs[i].type) { case COIL: @@ -460,13 +504,14 @@ void reginit() regs[i].xaddr = 0x40000 + rnum - 1; break; default: - upslogx(LOG_ERR, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); - upsdebugx(3, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); + upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); } - upsdebugx(3, "register num:%d, type: %d saddr %d, xaddr x%x\n", regs[i].num, - regs[i].type, - regs[i].saddr, - regs[i].xaddr + upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", + regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr ); } } @@ -474,153 +519,153 @@ void reginit() /* Read a modbus register */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) { - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; - - switch (type) { - case COIL: - rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_B: - rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_R: - rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - case HOLDING: - rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); + upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); /* on BROKEN PIPE error try to reconnect */ if (errno == EPIPE) { - upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); modbus_reconnect(); } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; } /* write a modbus register */ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) { - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; - - switch (type) { - case COIL: - *(uint *)data = *(uint *)data & mask8; - rval = modbus_write_bit(mb, addr, *(uint8_t *)data); - break; - case HOLDING: - *(uint *)data = *(uint *)data & mask16; - rval = modbus_write_register(mb, addr, *(uint16_t *)data); - break; - - case INPUT_B: - case INPUT_R: + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; } /* returns the time elapsed since start in milliseconds */ long time_elapsed(struct timeval *start) { - long rval; - struct timeval end; - - rval = gettimeofday(&end, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); - } - if (start->tv_usec < end.tv_usec) { - suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; - end.tv_usec -= 1000000 * nsec; - end.tv_sec += nsec; - } - if (start->tv_usec - end.tv_usec > 1000000) { - suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; - end.tv_usec += 1000000 * nsec; - end.tv_sec -= nsec; - } - rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; - - return rval; + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; } /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg) { - int rval; - int data; + int rval; + int data; - if (!strcasecmp(cmd, "load.off")) { + if (!strcasecmp(cmd, "load.off")) { data = 1; rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); if (rval == -1) { @@ -637,32 +682,45 @@ int upscmd(const char *cmd, const char *arg) rval = STAT_INSTCMD_HANDLED; } } else { - upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_UNKNOWN; - } - return rval; + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; } /* read device state, returns 0 on success or -1 on communication error it formats state depending on register semantics */ -int get_dev_state(devreg_t regnum, devstate_t *state) +int get_dev_state(devreg_t regindx, devstate_t **dstate) { int i; /* local index */ + int n; int rval; /* return value */ - uint reg_val; /* register value */ + static char *ptr = NULL; /* temporary pointer */ + uint num; /* register number */ + uint reg_val; /* register value */ regtype_t rtype; /* register type */ int addr; /* register address */ + devstate_t *state; /* device state */ + + state = *dstate; + num = regs[regindx].num; + addr = regs[regindx].xaddr; + rtype = regs[regindx].type; - addr = regs[regnum].xaddr; - rtype = regs[regnum].type; rval = register_read(mbctx, addr, rtype, ®_val); if (rval == -1) { return rval; } - - /* process register data */ - switch (regnum) { - case CHRG: + upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", + num, + addr, + rtype, + reg_val + ); + + /* process register data */ + switch (regindx) { + case CHRG: /* "ups.charge" */ if (reg_val == CHRG_NONE) { state->charge.state = CHRG_NONE; state->charge.info = chrgs_i[CHRG_NONE]; @@ -679,21 +737,27 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->charge.state = CHRG_FLOAT; state->charge.info = chrgs_i[CHRG_FLOAT]; } - break; + upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); + break; case BATV: /* "battery.voltage" */ case LVDC: /* "output.voltage" */ case LCUR: /* "output.current" */ if (reg_val != 0) { state->reg.val16 = reg_val; double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ - int n = snprintf(NULL, 0, "%f", fval); - char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); - sprintf(fval_s, "%f", fval); + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; } else { state->reg.val16 = 0; - state->reg.strval = "0"; + state->reg.strval = "0.00"; } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case TBUF: case BSOH: @@ -701,38 +765,53 @@ int get_dev_state(devreg_t regnum, devstate_t *state) case VAC: /* "input.voltage" */ if (reg_val != 0) { state->reg.val16 = reg_val; - int n = snprintf(NULL, 0, "%d", reg_val); - char *reg_val_s = (char *)malloc(sizeof(char) * (n + 1)); + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; sprintf(reg_val_s, "%d", reg_val); state->reg.strval = reg_val_s; } else { state->reg.val16 = 0; state->reg.strval = "0"; } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case BSOC: /* "battery.charge" */ if (reg_val != 0) { state->reg.val16 = reg_val; double fval = (double )reg_val * regs[BSOC].scale; - int n = snprintf(NULL, 0, "%f", fval); - char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); - sprintf(fval_s, "%f", fval); + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; } else { state->reg.val16 = 0; - state->reg.strval = "0"; + state->reg.strval = "0.00"; } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case BTMP: /* "battery.temperature" */ case OTMP: /* "ups.temperature" */ state->reg.val16 = reg_val; double fval = reg_val - 273.15; - int n = snprintf(NULL, 0, "%f", fval); - char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); - sprintf(fval_s, "%f", fval); + n = snprintf(NULL, 0, "%.2f", fval); + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + if (ptr != NULL) { + free(ptr); + } + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; - case PMNG: /* "ups.status" & "battery.charge" */ + case PMNG: /* "ups.status" & "battery.charge" */ if (reg_val == PMNG_BCKUP) { state->power.state = PMNG_BCKUP; state->power.info = pwrmng_i[PMNG_BCKUP]; @@ -746,7 +825,8 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->power.state = PMNG_NCHRG; state->power.info = pwrmng_i[PMNG_NCHRG]; } - break; + upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); + break; case PRDN: /* "ups.model" */ for (i = 0; i < DEV_NUMOF_MODELS; i++) { if (prdnm_i[i].val == reg_val) { @@ -755,6 +835,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) } state->product.val = reg_val; state->product.name = prdnm_i[i].name; + upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); break; case BSTA: if (reg_val & BSTA_REVPOL_M) { @@ -887,76 +968,90 @@ int get_dev_state(devreg_t regnum, devstate_t *state) } state->alrm = &mains; break; + case OBTA: + if (reg_val == OBTA_HIALRM_V) { + obta.alrm[OBTA_HIALRM_I].actv = 1; + } + state->alrm = &obta; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - break; - } + state->reg.val16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + break; + } - upsdebugx(3, "get_dev_state: state: %d", reg_val); - return rval; + return rval; } /* get driver configuration parameters */ void get_config_vars() { - /* check if device manufacturer is set ang get the value */ - if (testvar("device_mfr")) { - device_mfr = getval("device_mfr"); - } - upsdebugx(2, "device_mfr %s", device_mfr); - - /* check if device model is set ang get the value */ - if (testvar("device_model")) { - device_model = getval("device_model"); - } - upsdebugx(2, "device_model %s", device_model); - - /* check if serial baud rate is set ang get the value */ - if (testvar("ser_baud_rate")) { - ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); - } - upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); - - /* check if serial parity is set ang get the value */ - if (testvar("ser_parity")) { - /* Dereference the char* we get */ - char *sp = getval("ser_parity"); - if (sp) { - /* TODO? Sanity-check the char we get? */ - ser_parity = *sp; - } else { - upsdebugx(2, "Could not determine ser_parity, will keep default"); - } - } - upsdebugx(2, "ser_parity %c", ser_parity); - - /* check if serial data bit is set ang get the value */ - if (testvar("ser_data_bit")) { - ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); - } - upsdebugx(2, "ser_data_bit %d", ser_data_bit); - - /* check if serial stop bit is set ang get the value */ - if (testvar("ser_stop_bit")) { - ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); - } - upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); - - /* check if device ID is set ang get the value */ - if (testvar("rio_slave_id")) { - rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); - } - upsdebugx(2, "rio_slave_id %d", rio_slave_id); + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); /* check if response time out (s) is set ang get the value */ if (testvar("mod_resp_to_s")) { @@ -992,29 +1087,29 @@ void get_config_vars() /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port) { - modbus_t *mb; - char *sp; - if (strstr(port, "/dev/tty") != NULL) { - mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); - } - } else if ((sp = strchr(port, ':')) != NULL) { - char *tcp_port = xmalloc(sizeof(sp)); - strcpy(tcp_port, sp + 1); - *sp = '\0'; - mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - free(tcp_port); - } else { - mb = modbus_new_tcp(port, 502); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - } - return mb; + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; } /* reconnect to modbus server upon connection error */ @@ -1022,7 +1117,7 @@ void modbus_reconnect() { int rval; - upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); + upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); /* clear current modbus context */ modbus_close(mbctx); @@ -1060,4 +1155,4 @@ void modbus_reconnect() modbus_free(mbctx); fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); } -} \ No newline at end of file +} diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 3a823ded3a..b678ff03e5 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -1,7 +1,7 @@ /* adele_cbi.h - Driver for ADELE CB/CBI DC-UPS * * Copyright (C) - * 2021 Dimitris Economou + * 2022 Dimitris Economou * * 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 @@ -62,10 +62,10 @@ /* definition of register type */ enum regtype { - COIL = 0, - INPUT_B, - INPUT_R, - HOLDING + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING }; typedef enum regtype regtype_t; @@ -75,7 +75,7 @@ struct prodname { char *name; }; typedef struct prodname prodname_t; -prodname_t prdnm_i[] = { +static prodname_t prdnm_i[] = { {1, "CBI1235A"}, {2, "CBI2420A"}, {3, "CBI4810A"}, @@ -90,7 +90,7 @@ prodname_t prdnm_i[] = { }; /* charging status info, "battery.charger.status" */ -char *chrgs_i[] = { +static char *chrgs_i[] = { "none", "resting", /* recovering */ "charging", /* bulk */ @@ -104,7 +104,7 @@ struct chrgs { typedef struct chrgs chrgs_t; /* power management info, "ups.status", "battery.charger.status" */ -char *pwrmng_i[] = { +static char *pwrmng_i[] = { "backup", /* "OB", "discharging" */ "charging", /* "OL" */ "boost", @@ -176,8 +176,11 @@ typedef struct alrm_ar alrm_ar_t; #define VACA_HIALRM_I 0 /* high alarm */ #define VACA_LOALRM_I 1 /* low alarm */ -/* Onboard temperature alarm */ -#define OBTA_HIALRM 1 /* high alarm */ +/* Onboard temperature alarm value */ +#define OBTA_HIALRM_V 1 /* high alarm */ + +/* Onboard temperature alarm index */ +#define OBTA_HIALRM_I 0 /* high alarm */ /* Device failure alarm masks */ #define DEVF_RCALRM_M 0x0001 /* rectifier failure */ @@ -236,7 +239,7 @@ typedef struct alrm_ar alrm_ar_t; #define BSTA_CNNFLT_I 5 /* connection fault */ /* input mains and shutdown alarms */ -alrm_ar_t mains = { +static alrm_ar_t mains = { 2, { {0, "input voltage not available"}, @@ -245,7 +248,7 @@ alrm_ar_t mains = { }; /* AC input voltage alarms */ -alrm_ar_t vaca = { +static alrm_ar_t vaca = { 2, { {0, "input voltage high alarm"}, @@ -254,7 +257,7 @@ alrm_ar_t vaca = { }; /* device failure alarms */ -alrm_ar_t devf = { +static alrm_ar_t devf = { 3, { {0, "UPS rectifier failure"}, @@ -264,7 +267,7 @@ alrm_ar_t devf = { }; /* battery sensor failure alarms */ -alrm_ar_t btsf = { +static alrm_ar_t btsf = { 2, { {0, "battery temp sensor connection fault"}, @@ -273,7 +276,7 @@ alrm_ar_t btsf = { }; /* battery voltage alarms */ -alrm_ar_t bval = { +static alrm_ar_t bval = { 3, { {0, "battery high voltage"}, @@ -283,7 +286,7 @@ alrm_ar_t bval = { }; /* battery SoH and SoC alarms */ -alrm_ar_t shsc = { +static alrm_ar_t shsc = { 4, { {0, "battery high internal resistance"}, @@ -294,7 +297,7 @@ alrm_ar_t shsc = { }; /* battery status alarm */ -alrm_ar_t bsta = { +static alrm_ar_t bsta = { 6, { {0, "battery reversed polarity"}, @@ -306,6 +309,14 @@ alrm_ar_t bsta = { } }; +/* onboard temperature alarm */ +static alrm_ar_t obta = { + 1, + { + {0, "onboard temperature high"} + } +}; + /* UPS device reg enum */ enum devreg { CHRG = 0, /* Charging status, "battery.charger.status" */ @@ -356,7 +367,7 @@ union devstate { typedef union devstate devstate_t; /* ADELE CBI registers */ -regattr_t regs[] = { +static regattr_t regs[] = { {40005, 0, 0, 1, HOLDING}, /* Charging status */ {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ @@ -373,8 +384,8 @@ regattr_t regs[] = { {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ {40006, 0, 0, 1, HOLDING}, /* Power management - * 0:Backup 1:Charging 2:boost 3:Not charging - */ + * 0:Backup 1:Charging 2:boost 3:Not charging + */ {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ {40010, 0, 0, 1, HOLDING}, /* Software ID */ From abc42eddc48336ba4cb1aae1e5860b29e9ca9ff5 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 6 Jan 2022 01:09:02 +0200 Subject: [PATCH 029/700] under construction --- drivers/adele_cbi.c | 1006 +++++++++++++++++++++++++++++++++++++++++++ drivers/adele_cbi.h | 196 +++++++++ 2 files changed, 1202 insertions(+) create mode 100644 drivers/adele_cbi.c create mode 100644 drivers/adele_cbi.h diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c new file mode 100644 index 0000000000..f24e13b575 --- /dev/null +++ b/drivers/adele_cbi.c @@ -0,0 +1,1006 @@ +/* adele_cbi.c - Driver for adele CB/CBI UPS + * + * Copyright (C) + * 2021 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "adele_cbi.h" +#include +#include + +#define DRIVER_NAME "NUT Adele CBI driver" +#define DRIVER_VERSION "0.01" + +/* variables */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static regattr_t sigar[NUMOF_SIG_STATES]; /* array of ups signal attributes */ +static int errcnt = 0; /* modbus access error counter */ + +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ + + +/* get config vars set by -x or defined in ups.conf driver section */ +void get_config_vars(void); + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port); + +/* reconnect upon communication error */ +void modbus_reconnect(); + +/* modbus register read function */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data); + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg); + +/* read signal status */ +int get_signal_state(devstate_t state); + +/* count the time elapsed since start */ +long time_elapsed(struct timeval *start); + +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} +}; + +/* + * driver functions + */ + +/* initialize ups driver information */ +void upsdrv_initinfo(void) { + upsdebugx(2, "upsdrv_initinfo"); + + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); + + /* register instant commands */ + if (sigar[FSD_T].addr != NOTUSED) { + dstate_addcmd("load.off"); + } + + /* set callback for instant commands */ + upsh.instcmd = upscmd; +} + +/* open serial connection and connect to modbus RIO */ +void upsdrv_initups(void) +{ + int rval; + upsdebugx(2, "upsdrv_initups"); + + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response time out to 200ms */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} + +/* update UPS signal state */ +void upsdrv_updateinfo(void) +{ + int rval; + int online = -1; /* keep online state */ + errcnt = 0; + + upsdebugx(2, "upsdrv_updateinfo"); + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ + + /* + * update UPS status regarding MAINS state either via OL | OB. + * if both statuses are mapped to contacts then only OL is evaluated. + */ + if (sigar[OL_T].addr != NOTUSED) { + rval = get_signal_state(OL_T); + upsdebugx(2, "OL value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OL_T].noro)) { + status_set("OL"); + online = 1; + } else { + status_set("OB"); + online = 0; + + /* if DISCHRG state is not mapped to a contact and UPS is on + * batteries set status to DISCHRG state */ + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + + } + } else if (sigar[OB_T].addr != NOTUSED) { + rval = get_signal_state(OB_T); + upsdebugx(2, "OB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OB_T].noro)) { + status_set("OB"); + online = 0; + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } else { + status_set("OL"); + online = 1; + } + } + + /* + * update UPS status regarding CHARGING state via HB. HB is usually + * mapped to "ready" contact when closed indicates a charging state > 85% + */ + if (sigar[HB_T].addr != NOTUSED) { + rval = get_signal_state(HB_T); + upsdebugx(2, "HB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[HB_T].noro)) { + status_set("HB"); + dstate_setinfo("battery.charger.status", "resting"); + } else if (online == 1 && sigar[CHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } else if (online == 0 && sigar[DISCHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* + * update UPS status regarding DISCHARGING state via LB. LB is mapped + * to "battery low" contact. + */ + if (sigar[LB_T].addr != NOTUSED) { + rval = get_signal_state(LB_T); + upsdebugx(2, "LB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[LB_T].noro)) { + status_set("LB"); + alarm_set("Low Battery (Charge)"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[RB_T].addr != NOTUSED) { + rval = get_signal_state(RB_T); + upsdebugx(2, "RB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[RB_T].noro)) { + status_set("RB"); + alarm_set("Replace Battery"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[CHRG_T].addr != NOTUSED) { + rval = get_signal_state(CHRG_T); + upsdebugx(2, "CHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[CHRG_T].noro)) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } + } else if (sigar[DISCHRG_T].addr != NOTUSED) { + rval = get_signal_state(DISCHRG_T); + upsdebugx(2, "DISCHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[DISCHRG_T].noro)) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* check for communication errors */ + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2,"Communication errors: %d", errcnt); + dstate_datastale(); + } +} + +/* shutdown UPS */ +void upsdrv_shutdown(void) +{ + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); +} + +/* print driver usage info */ +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); + addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); + addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); + addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); +} + +/* close modbus connection and free modbus context allocated memory */ +void upsdrv_cleanup(void) +{ + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } +} + +/* + * driver support functions + */ + +/* Read a modbus register */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* write a modbus register */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* returns the time elapsed since start in milliseconds */ +long time_elapsed(struct timeval *start) +{ + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; +} + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg) +{ + int rval; + int data; + struct timeval start; + long etime; + + if (!strcasecmp(cmd, "load.off")) { + if (sigar[FSD_T].addr != NOTUSED && + (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) + ) { + data = 1 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", sigar[FSD_T].addr, data); + rval = STAT_INSTCMD_HANDLED; + } + + /* if pulse has been defined and rising edge was successful */ + if (FSD_pulse_duration != NOTUSED && rval == STAT_INSTCMD_HANDLED) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for FSD_pulse_duration ms */ + while ((etime = time_elapsed(&start)) < FSD_pulse_duration); + + data = 0 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(LOG_ERR, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d, elapsed time: %lims", + sigar[FSD_T].addr, + data, + etime + ); + rval = STAT_INSTCMD_HANDLED; + } + } + } else { + upslogx(LOG_NOTICE,"load.off: failed (FSD address undefined or invalid register type) [%s] [%s]", + cmd, + arg + ); + rval = STAT_INSTCMD_FAILED; + } + } else { + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; +} + +/* read signal state from modbus RIO, returns 0|1 state or -1 on communication error */ +int get_signal_state(devstate_t state) +{ + int rval = -1; + int reg_val; + regtype_t rtype = 0; /* register type */ + int addr = -1; /* register address */ + + /* assign register address and type */ + switch (state) { + case OL_T: + addr = sigar[OL_T].addr; + rtype = sigar[OL_T].type; + break; + case OB_T: + addr = sigar[OB_T].addr; + rtype = sigar[OB_T].type; + break; + case LB_T: + addr = sigar[LB_T].addr; + rtype = sigar[LB_T].type; + break; + case HB_T: + addr = sigar[HB_T].addr; + rtype = sigar[HB_T].type; + break; + case RB_T: + addr = sigar[RB_T].addr; + rtype = sigar[RB_T].type; + break; + case CHRG_T: + addr = sigar[CHRG_T].addr; + rtype = sigar[CHRG_T].type; + break; + case DISCHRG_T: + addr = sigar[DISCHRG_T].addr; + rtype = sigar[DISCHRG_T].type; + break; + + case BYPASS_T: + case CAL_T: + case FSD_T: + case OFF_T: + case OVER_T: + case TRIM_T: + case BOOST_T: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + break; + } + + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval > -1) { + rval = reg_val; + } + upsdebugx(3, "get_signal_state: state: %d", reg_val); + return rval; +} + +/* get driver configuration parameters */ +void get_config_vars() +{ + int i; /* local index */ + + /* initialize sigar table */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + sigar[i].addr = NOTUSED; + sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ + } + + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); + + /* check if response time out (s) is set ang get the value */ + if (testvar("mod_resp_to_s")) { + mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); + } + upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); + + /* check if response time out (us) is set ang get the value */ + if (testvar("mod_resp_to_us")) { + mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); + if (mod_resp_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } + } + upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); + + /* check if byte time out (s) is set ang get the value */ + if (testvar("mod_byte_to_s")) { + mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); + } + upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); + + /* check if byte time out (us) is set ang get the value */ + if (testvar("mod_byte_to_us")) { + mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); + if (mod_byte_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } + } + upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); + + /* check if OL address is set and get the value */ + if (testvar("OL_addr")) { + sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); + if (testvar("OL_noro")) { + sigar[OL_T].noro = (int)strtol(getval("OL_noro"), NULL, 10); + if (sigar[OL_T].noro != 1) { + sigar[OL_T].noro = 0; + } + } + } + + /* check if OL register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OL_regtype")) { + sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); + if (sigar[OL_T].type < COIL || sigar[OL_T].type > HOLDING) { + sigar[OL_T].type = INPUT_B; + } + } else { + sigar[OL_T].type = INPUT_B; + } + + /* check if OB address is set and get the value */ + if (testvar("OB_addr")) { + sigar[OB_T].addr = (int)strtol(getval("OB_addr"), NULL, 0); + } + if (testvar("OB_noro")) { + sigar[OB_T].noro = (int)strtol(getval("OB_noro"), NULL, 10); + if (sigar[OB_T].noro != 1) { + sigar[OB_T].noro = 0; + } + } + + /* check if OB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OB_regtype")) { + sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[OB_T].type < COIL || sigar[OB_T].type > HOLDING) { + sigar[OB_T].type = INPUT_B; + } + } else { + sigar[OB_T].type = INPUT_B; + } + + /* check if LB address is set and get the value */ + if (testvar("LB_addr")) { + sigar[LB_T].addr = (int)strtol(getval("LB_addr"), NULL, 0); + if (testvar("LB_noro")) { + sigar[LB_T].noro = (int)strtol(getval("LB_noro"), NULL, 10); + if (sigar[LB_T].noro != 1) { + sigar[LB_T].noro = 0; + } + } + } + + /* check if LB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("LB_regtype")) { + sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[LB_T].type < COIL || sigar[LB_T].type > HOLDING) { + sigar[LB_T].type = INPUT_B; + } + } else { + sigar[LB_T].type = INPUT_B; + } + + /* check if HB address is set and get the value */ + if (testvar("HB_addr")) { + sigar[HB_T].addr = (int)strtol(getval("HB_addr"), NULL, 0); + if (testvar("HB_noro")) { + sigar[HB_T].noro = (int)strtol(getval("HB_noro"), NULL, 10); + if (sigar[HB_T].noro != 1) { + sigar[HB_T].noro = 0; + } + } + } + + /* check if HB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("HB_regtype")) { + sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); + if (sigar[HB_T].type < COIL || sigar[HB_T].type > HOLDING) { + sigar[HB_T].type = INPUT_B; + } + } else { + sigar[HB_T].type = INPUT_B; + } + + /* check if RB address is set and get the value */ + if (testvar("RB_addr")) { + sigar[RB_T].addr = (int)strtol(getval("RB_addr"), NULL, 0); + if (testvar("RB_noro")) { + sigar[RB_T].noro = (int)strtol(getval("RB_noro"), NULL, 10); + if (sigar[RB_T].noro != 1) { + sigar[RB_T].noro = 0; + } + } + } + + /* check if RB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("RB_regtype")) { + sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); + if (sigar[RB_T].type < COIL || sigar[RB_T].type > HOLDING) { + sigar[RB_T].type = INPUT_B; + } + } else { + sigar[RB_T].type = INPUT_B; + } + + /* check if CHRG address is set and get the value */ + if (testvar("CHRG_addr")) { + sigar[CHRG_T].addr = (int)strtol(getval("CHRG_addr"), NULL, 0); + if (testvar("CHRG_noro")) { + sigar[CHRG_T].noro = (int)strtol(getval("CHRG_noro"), NULL, 10); + if (sigar[CHRG_T].noro != 1) { + sigar[CHRG_T].noro = 0; + } + } + } + + /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("CHRG_regtype")) { + sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); + if (sigar[CHRG_T].type < COIL || sigar[CHRG_T].type > HOLDING) { + sigar[CHRG_T].type = INPUT_B; + } + } else { + sigar[CHRG_T].type = INPUT_B; + } + + /* check if DISCHRG address is set and get the value */ + if (testvar("DISCHRG_addr")) { + sigar[DISCHRG_T].addr = (int)strtol(getval("DISCHRG_addr"), NULL, 0); + if (testvar("DISCHRG_noro")) { + sigar[DISCHRG_T].noro = (int)strtol(getval("DISCHRG_noro"), NULL, 10); + if (sigar[DISCHRG_T].noro != 1) { + sigar[DISCHRG_T].noro = 0; + } + } + } + + /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("DISCHRG_regtype")) { + sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); + if (sigar[DISCHRG_T].type < COIL || sigar[DISCHRG_T].type > HOLDING) { + sigar[DISCHRG_T].type = INPUT_B; + } + } else { + sigar[DISCHRG_T].type = INPUT_B; + } + + /* check if FSD address is set and get the value */ + if (testvar("FSD_addr")) { + sigar[FSD_T].addr = (int)strtol(getval("FSD_addr"), NULL, 0); + if (testvar("FSD_noro")) { + sigar[FSD_T].noro = (int)strtol(getval("FSD_noro"), NULL, 10); + if (sigar[FSD_T].noro != 1) { + sigar[FSD_T].noro = 0; + } + } + } + + /* check if FSD register type is set and get the value otherwise set to COIL */ + if (testvar("FSD_regtype")) { + sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); + if (sigar[FSD_T].type < COIL || sigar[FSD_T].type > HOLDING) { + sigar[FSD_T].type = COIL; + } + } else { + sigar[FSD_T].type = COIL; + } + + /* check if FSD pulse duration is set and get the value */ + if (testvar("FSD_pulse_duration")) { + FSD_pulse_duration = (int) strtol(getval("FSD_pulse_duration"), NULL, 10); + } + upsdebugx(2, "FSD_pulse_duration %d", FSD_pulse_duration); + + /* debug loop over signal array */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + if (sigar[i].addr != NOTUSED) { + char *signame; + switch (i) { + case OL_T: + signame = "OL"; + break; + case OB_T: + signame = "OB"; + break; + case LB_T: + signame = "LB"; + break; + case HB_T: + signame = "HB"; + break; + case RB_T: + signame = "RB"; + break; + case FSD_T: + signame = "FSD"; + break; + case CHRG_T: + signame = "CHRG"; + break; + case DISCHRG_T: + signame = "DISCHRG"; + break; + default: + signame = "NOTUSED"; + break; + } + upsdebugx(2, "%s, addr:0x%x, type:%d", signame, sigar[i].addr, sigar[i].type); + } + } +} + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port) +{ + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; +} + +/* reconnect to modbus server upon connection error */ +void modbus_reconnect() +{ + int rval; + + upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); + + /* clear current modbus context */ + modbus_close(mbctx); + modbus_free(mbctx); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} \ No newline at end of file diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h new file mode 100644 index 0000000000..838af3971d --- /dev/null +++ b/drivers/adele_cbi.h @@ -0,0 +1,196 @@ +/* adele_cbi.h - Driver for generic UPS connected via modbus RIO + * + * Copyright (C) + * 2021 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ADELE_CBI_H +#define ADELE_CBI_H + +/* UPS device details */ +#define DEVICE_MFR "ADELE" +#define DEVICE_MODEL "CB/CBI" + +/* serial access parameters */ +#define BAUD_RATE 9600 +#define PARITY 'N' +#define DATA_BIT 8 +#define STOP_BIT 1 + +/* + * modbus response and byte timeouts + * us: 1 - 999999 + */ +#define MODRESP_TIMEOUT_s 0 +#define MODRESP_TIMEOUT_us 200000 +#define MODBYTE_TIMEOUT_s 0 +#define MODBYTE_TIMEOUT_us 50000 + +/* modbus access parameters */ +#define MODBUS_SLAVE_ID 5 + +/* definition of register type */ +enum regtype { + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING +}; +typedef enum regtype regtype_t; + +/* UPS device state enum */ +enum devstate { + CHRG = 0, /* Charging status */ + BATV, /* Battery voltage */ + BCEF = 6, /* Battery charge efficiency factor (CEF) */ + BSOH, /* Battery state-of-health */ + BSOC = 9, /* Battery state-of-charge */ + BTMP = 11, /* Battery temperature in Kelvin units */ + PMNG = 15, /* Power management */ +}; +typedef enum devstate devstate_t; + +/* BIT MASKS and VALUES */ +#define CHRG_NONE 0 +#define CHRG_RECV 1 +#define CHRG_BULK 2 +#define CHRG_ABSR 3 +#define CHRG_FLOAT 4 +#define PMNG_BKUP 0 +#define PMNG_CHRG 1 +#define PMNG_BOOST 2 +#define PMNG_NCHRG 3 + +/* UPS state signal attributes */ +struct regattr { + int num; + int saddr; /* register start address */ + int xaddr; /* register hex address */ + float scale; /* scale */ + regtype_t type; /* register type */ +}; +typedef struct regattr regattr_t; + +/* ADELE CBI registers */ +regattr_t regs[] = { + {40005, 0, 0, 1, HOLDING}, /* Charging status */ + {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ + {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ + {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ + {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ + {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ + {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ + {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ + {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ + {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ + {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ + {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ + {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ + {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ + {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ + {40006, 0, 0, 1, HOLDING}, /* Power management + * 0:Backup 1:Charging 2:boost 3:Not charging + */ + {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ + {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ + {40010, 0, 0, 1, HOLDING}, /* Software ID */ + {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ + {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ + {40067, 0, 0, 1, HOLDING}, /* Product name */ + {40039, 0, 0, 1, HOLDING}, /* Device variant */ + {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ + {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ + {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ + {40020, 0, 0, 1, HOLDING}, /* Output load current */ + {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ + {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ + {40050, 0, 0, .1, HOLDING}, /* Ah charged */ + {40051, 0, 0, 1, HOLDING}, /* Total run time */ + {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ + {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ + {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ + {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ + {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ + {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ + {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ + {40056, 0, 0, 1, HOLDING}, /* Number of overtemperature inside events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low AC input voltage events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High AC input voltage events at mains input */ + {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ + {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ + {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ + {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ + {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ + {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ + {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ + {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ + {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ + {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ + {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ + {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ + {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ + {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ + {40120, 0, 0, 1, HOLDING}, /* Zero-SoC reference */ + {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ + {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ + {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ + {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ + {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ + {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ + {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ + {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ + {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ + {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ + {40082, 0, 0, 1, HOLDING}, /* Float voltage */ + {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ + {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ + {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ + {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ + {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ + {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ + {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ + {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ + {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ + {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ + {40065, 0, 0, 1, HOLDING}, /* History clear all */ + {40066, 0, 0, 1, HOLDING}, /* Factory settings */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed 1 = Backup not allowed 0 */ + {40104, 0, 0, 1, HOLDING}, /* Time buffering */ + {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ + {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ + {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ + {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ + {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ + {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ + {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ + {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ + {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ + {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ + {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ + {40043, 0, 0, 1, HOLDING}, /* Device failure */ + {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ + {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ + {40046, 0, 0, 1, HOLDING}, /* Input mains on / backup */ + {40038, 0, 0, 1, HOLDING} /* Load alarm */ +}; + +#define NUMOF_REGS 14 +#define NOTUSED -1 + +#endif /* ADELE_CBI_H */ From dd2cb42f7eacdf7d57660f2899f030dff707e968 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 6 Jan 2022 01:10:09 +0200 Subject: [PATCH 030/700] Makefile.am modifications --- drivers/Makefile.am | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/Makefile.am b/drivers/Makefile.am index db73311c9e..bd8900d700 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -50,7 +50,7 @@ SERIAL_USB_DRIVERLIST = \ nutdrv_qx NEONXML_DRIVERLIST = netxml-ups MACOSX_DRIVERLIST = macosx-ups -MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 +MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 adele_cbi LINUX_I2C_DRIVERLIST = asem pijuice # distribute all drivers, even ones that are not built by default @@ -267,6 +267,8 @@ phoenixcontact_modbus_SOURCES = phoenixcontact_modbus.c phoenixcontact_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) generic_modbus_SOURCES = generic_modbus.c generic_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) +adele_cbi_SOURCES = adele_cbi.c +adele_cbi_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) # Huawei UPS2000 driver # (this is both a Modbus and a serial driver) @@ -323,7 +325,7 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h b xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ apc-pdu-mib.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h \ eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h \ - hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h + hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adele_cbi # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, From 02728fda19b5efa1fdd082f4ea18ca0ced5065d3 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Fri, 7 Jan 2022 01:55:04 +0200 Subject: [PATCH 031/700] register status values and masks added --- drivers/adele_cbi.h | 129 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 18 deletions(-) diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 838af3971d..e2cc76643a 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -23,6 +23,8 @@ #ifndef ADELE_CBI_H #define ADELE_CBI_H +#include + /* UPS device details */ #define DEVICE_MFR "ADELE" #define DEVICE_MODEL "CB/CBI" @@ -54,36 +56,124 @@ enum regtype { }; typedef enum regtype regtype_t; -/* UPS device state enum */ -enum devstate { - CHRG = 0, /* Charging status */ - BATV, /* Battery voltage */ - BCEF = 6, /* Battery charge efficiency factor (CEF) */ - BSOH, /* Battery state-of-health */ - BSOC = 9, /* Battery state-of-charge */ - BTMP = 11, /* Battery temperature in Kelvin units */ - PMNG = 15, /* Power management */ +struct prodname { + uint16_t val; + char *name; }; -typedef enum devstate devstate_t; +typedef struct prodname prodname_t; -/* BIT MASKS and VALUES */ +/* product name */ +prodname_t prdn[] = { + {1, "CBI1235A"}, + {2, "CBI2420A"}, + {3, "CBI4810A"}, + {4, "CBI2801224"}, + {7, "CBI480W"}, + {8, "CB122410A"}, + {9, "CB480W"}, + {11, "CB12245AJ"}, + {12, "CB1235A"}, + {13, "CB2420A"}, + {14, "CB4810A"} +}; + +/* charging status */ +char *chrgs[] = { + "none", + "recovery", + "bulk", + "float" +}; + +/* + * BIT MASKS and VALUES + */ + +/* Charging status */ #define CHRG_NONE 0 #define CHRG_RECV 1 #define CHRG_BULK 2 #define CHRG_ABSR 3 #define CHRG_FLOAT 4 + +/* power management */ #define PMNG_BKUP 0 #define PMNG_CHRG 1 #define PMNG_BOOST 2 #define PMNG_NCHRG 3 +/* product name */ +#define PRDN_MAX 14 + +/* Mains status */ +#define MAINS_AVAIL 0x0001 /* available */ +#define SHUTD_REQST 0x0002 /* shutdown requested */ + +/* AC input voltage alarms */ +#define VACA_HIALRM 0x0001 /* high alarm */ +#define VACA_LOALRM 0x0002 /* low alarm */ + +/* Onboard temperature alarm */ +#define OBTA_HIALRM 1 /* high alarm */ + +/* Device failure */ +#define DEVF_RCALRM 0x0001 /* rectifier failure */ +#define DEVF_INALRM 0x0006 /* internal failure */ +#define DEVF_LFNAVL 0x0008 /* lifetest not available */ + +/* Battery temp sensor failure */ +#define BTSF_FCND 0x0001 /* connection fault */ +#define BTSF_NCND 0x0001 /* not connected */ + +/* Battery voltage alarm */ +#define BVAL_HIALRM 0x0001 /* high voltage */ +#define BVAL_LOALRM 0x0002 /* low voltage */ +#define BVAL_BSTSFL 0x0004 /* battery start with battery flat */ + +/* SoH and SoC alarms */ +#define SHSC_HIRESI 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC 0x0040 /* low state of charge */ + +/* Battery status alarms */ +#define BSTA_REVPOL 0x0001 /* reversed polarity */ +#define BSTA_NOCNND 0x0002 /* not connected */ +#define BSTA_CLSHCR 0x0004 /* cell short circuit */ +#define BSTA_SULPHD 0x0008 /* sulphated */ +#define BSTA_CHEMNS 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT 0x0020 /* connection fault */ + + +/* UPS device state enum */ +enum devstate { + CHRG = 0, /* Charging status */ + BATV, /* Battery voltage */ + BCEF = 6, /* Battery charge efficiency factor (CEF) */ + BSOH, /* Battery state-of-health */ + BSOC = 9, /* Battery state-of-charge */ + BTMP = 11, /* Battery temperature in Kelvin units */ + PMNG = 15, /* Power management */ + PRDN = 21, /* Product name */ + FSD = 80, /* Force shutdown */ + BSTA = 89, /* Battery status alarms */ + SCSH = 90, /* SoH and SoC alarms */ + BVAL = 91, /* Battery voltage alarm */ + BTSF = 92, /* Battery temp sensor failure */ + DEVF = 93, /* Device failure */ + OBTA = 94, /* On board temp alarm */ + VACA = 95, /* VAC alarms */ + MAIN = 96 /* Mains status */ +}; +typedef enum devstate devstate_t; + /* UPS state signal attributes */ struct regattr { int num; - int saddr; /* register start address */ + int saddr; /* register start address */ int xaddr; /* register hex address */ float scale; /* scale */ - regtype_t type; /* register type */ + regtype_t type; /* register type */ }; typedef struct regattr regattr_t; @@ -129,9 +219,9 @@ regattr_t regs[] = { {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ - {40056, 0, 0, 1, HOLDING}, /* Number of overtemperature inside events */ - {40054, 0, 0, 1, HOLDING}, /* Number of low AC input voltage events at mains input */ - {40055, 0, 0, 1, HOLDING}, /* Number of High AC input voltage events at mains input */ + {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ @@ -170,7 +260,10 @@ regattr_t regs[] = { {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ {40065, 0, 0, 1, HOLDING}, /* History clear all */ {40066, 0, 0, 1, HOLDING}, /* Factory settings */ - {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed 1 = Backup not allowed 0 */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed + * 1 = Backup not allowed 0 + * */ + {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ {40104, 0, 0, 1, HOLDING}, /* Time buffering */ {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ @@ -186,7 +279,7 @@ regattr_t regs[] = { {40043, 0, 0, 1, HOLDING}, /* Device failure */ {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ - {40046, 0, 0, 1, HOLDING}, /* Input mains on / backup */ + {40046, 0, 0, 1, HOLDING}, /* Mains status */ {40038, 0, 0, 1, HOLDING} /* Load alarm */ }; From e82132992b43d2f3e95fbe71866a09dd3d5707b0 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Sat, 8 Jan 2022 02:49:31 +0200 Subject: [PATCH 032/700] structure device data, code get_dev_state, in progress --- drivers/adele_cbi.c | 202 +++++++++++++++++++++++++++++++++----------- drivers/adele_cbi.h | 112 ++++++++++++++++-------- 2 files changed, 230 insertions(+), 84 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index f24e13b575..d254d4c54b 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -1,4 +1,4 @@ -/* adele_cbi.c - Driver for adele CB/CBI UPS +/* adele_cbi.c - driver for ADELE CB/CBI DC-UPS * * Copyright (C) * 2021 Dimitris Economou @@ -62,7 +62,7 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); int upscmd(const char *cmd, const char *arg); /* read signal status */ -int get_signal_state(devstate_t state); +int get_signal_state(devreg_t state); /* count the time elapsed since start */ long time_elapsed(struct timeval *start); @@ -297,7 +297,7 @@ void upsdrv_shutdown(void) /* wait for an increasing time interval before sending shutdown command */ while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); cnt--; } switch (rval) { @@ -346,6 +346,45 @@ void upsdrv_cleanup(void) * driver support functions */ +/* initialize register start address and hex address from register number */ +void reginit() +{ + int i; /* local index */ + + for (i = 1; i < MODBUS_NUMOF_REGS; i++) { + int rnum = regs[i].num; + switch (regs[i].type) { + case COIL: + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x0 + regs[i].num - 1; + break; + case INPUT_B: + rnum -= 10000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x10000 + rnum - 1; + break; + case INPUT_R: + rnum -= 30000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x30000 + rnum - 1; + break; + case HOLDING: + rnum -= 40000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x40000 + rnum - 1; + break; + default: + upslogx(LOG_ERR, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); + upsdebugx(3, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); + } + upsdebugx(3, "register num:%d, type: %d saddr %d, xaddr x%x\n", regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr + ); + } +} + /* Read a modbus register */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) { @@ -385,7 +424,7 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); + upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); break; } if (rval == -1) { @@ -561,52 +600,116 @@ int upscmd(const char *cmd, const char *arg) return rval; } -/* read signal state from modbus RIO, returns 0|1 state or -1 on communication error */ -int get_signal_state(devstate_t state) +/* read device state, returns 0 on success or -1 on communication error */ +int get_dev_state(devreg_t regnum, devstate_t *state) { - int rval = -1; - int reg_val; - regtype_t rtype = 0; /* register type */ - int addr = -1; /* register address */ - - /* assign register address and type */ - switch (state) { - case OL_T: - addr = sigar[OL_T].addr; - rtype = sigar[OL_T].type; - break; - case OB_T: - addr = sigar[OB_T].addr; - rtype = sigar[OB_T].type; - break; - case LB_T: - addr = sigar[LB_T].addr; - rtype = sigar[LB_T].type; - break; - case HB_T: - addr = sigar[HB_T].addr; - rtype = sigar[HB_T].type; - break; - case RB_T: - addr = sigar[RB_T].addr; - rtype = sigar[RB_T].type; - break; - case CHRG_T: - addr = sigar[CHRG_T].addr; - rtype = sigar[CHRG_T].type; + int i; /* local index */ + int rval = -1; /* return value */ + uint reg_val; /* register value */ + regtype_t rtype = 0; /* register type */ + int addr = regs[regnum].xaddr; + int rtype = regs[regnum].type; + + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval == -1) { + return rval; + } + + /* process register data */ + switch (regnum) { + case CHRG: + if (reg_val == CHRG_NONE) { + state->charge.state = CHRG_NONE; + state->charge.info = chrgs_i[CHRG_NONE]; + } else if (reg_val == CHRG_RECV) { + state->charge.state = CHRG_RECV; + state->charge.info = chrgs_i[CHRG_RECV]; + } else if (reg_val == CHRG_BULK) { + state->charge.state = CHRG_BULK; + state->charge.info = chrgs_i[CHRG_BULK]; + } else if (reg_val == CHRG_ABSR) { + state->charge.state = CHRG_ABSR; + state->charge.info = chrgs_i[CHRG_ABSR]; + } else if (reg_val == CHRG_FLOAT) { + state->charge.state = CHRG_FLOAT; + state->charge.info = chrgs_i[CHRG_FLOAT]; + } break; - case DISCHRG_T: - addr = sigar[DISCHRG_T].addr; - rtype = sigar[DISCHRG_T].type; + case BATV: /* "battery.voltage" */ + case LVDC: /* "output.voltage" */ + case LCUR: /* "output.current" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ + int n = snprintf(NULL, 0, "%f", fval); + char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(fval_s, "%f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + break; + case BSOH: + case BCEF: + case VAC: /* "input.voltage" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + int n = snprintf(NULL, 0, "%d", reg_val); + char *reg_val_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + break; + case BSOC: /* "battery.charge" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = (double )reg_val * regs[BSOC].scale; + int n = snprintf(NULL, 0, "%f", fval); + char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(fval_s, "%f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + break; + case BTMP: /* "battery.temperature" */ + case OTMP: /* "ups.temperature" */ + state->reg.val16 = reg_val; + double fval = reg_val - 273.15; + int n = snprintf(NULL, 0, "%f", fval); + char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(fval_s, "%f", fval); + state->reg.strval = fval_s; + break; + case PMNG: /* "ups.status" & "battery.charge" */ + if (reg_val == PMNG_BCKUP) { + state->power.state = PMNG_BCKUP; + state->power.info = pwrmng_i[PMNG_BCKUP]; + } else if (reg_val == PMNG_CHRGN) { + state->power.state = PMNG_CHRGN; + state->power.info = pwrmng_i[PMNG_CHRGN]; + } else if (reg_val == PMNG_BOOST) { + state->power.state = PMNG_BOOST; + state->power.info = pwrmng_i[PMNG_BOOST]; + } else if (reg_val == PMNG_NCHRG) { + state->power.state = PMNG_NCHRG; + state->power.info = pwrmng_i[PMNG_NCHRG]; + } break; - - case BYPASS_T: - case CAL_T: - case FSD_T: - case OFF_T: - case OVER_T: - case TRIM_T: - case BOOST_T: + case PRDN: /* "ups.model" */ + for (i = 0; i < DEV_NUMOF_MODELS; i++) { + if (prdnm_i[i].val == reg_val) { + break; + } + } + state->product.val = reg_val; + state->product.name = prdnm_i[i].name; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" @@ -622,10 +725,7 @@ int get_signal_state(devstate_t state) break; } - rval = register_read(mbctx, addr, rtype, ®_val); - if (rval > -1) { - rval = reg_val; - } + upsdebugx(3, "get_signal_state: state: %d", reg_val); return rval; } diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index e2cc76643a..14234d23fe 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -1,4 +1,4 @@ -/* adele_cbi.h - Driver for generic UPS connected via modbus RIO +/* adele_cbi.h - Driver for ADELE CB/CBI DC-UPS * * Copyright (C) * 2021 Dimitris Economou @@ -47,6 +47,12 @@ /* modbus access parameters */ #define MODBUS_SLAVE_ID 5 +/* number of modbus registers */ +#define MODBUS_NUMOF_REGS 98 + +/* number of device models */ +#define DEV_NUMOF_MODELS 10 + /* definition of register type */ enum regtype { COIL = 0, @@ -56,14 +62,13 @@ enum regtype { }; typedef enum regtype regtype_t; +/* product name info, "device.model" */ struct prodname { uint16_t val; char *name; }; typedef struct prodname prodname_t; - -/* product name */ -prodname_t prdn[] = { +prodname_t prdnm_i[] = { {1, "CBI1235A"}, {2, "CBI2420A"}, {3, "CBI4810A"}, @@ -77,13 +82,42 @@ prodname_t prdn[] = { {14, "CB4810A"} }; -/* charging status */ -char *chrgs[] = { +/* charging status info, "battery.charger.status" */ +char *chrgs_i[] = { "none", - "recovery", - "bulk", - "float" + "recovery", /* "resting" */ + "bulk", /* "charging" */ + "absorb", /* "charging" */ + "float" /* "floating" */ +}; +struct chrgs { + int state; + char *info; +}; +typedef struct chrgs chrgs_t; + +/* power management info, "ups.status", "battery.charger.status" */ +char *pwrmng_i[] = { + "backup", /* "OB", "discharging" */ + "charging", /* "OL" */ + "boost", + "not charging" +}; +struct pwrmng { + int state; + char *info; }; +typedef struct pwrmng pwrmng_t; + +/* general modbus register value */ +struct reg { + union { + uint16_t val16; + uint8_t val8; + }; + char *strval; +}; +typedef struct reg reg_t; /* * BIT MASKS and VALUES @@ -97,8 +131,8 @@ char *chrgs[] = { #define CHRG_FLOAT 4 /* power management */ -#define PMNG_BKUP 0 -#define PMNG_CHRG 1 +#define PMNG_BCKUP 0 +#define PMNG_CHRGN 1 #define PMNG_BOOST 2 #define PMNG_NCHRG 3 @@ -145,29 +179,35 @@ char *chrgs[] = { #define BSTA_CNNFLT 0x0020 /* connection fault */ -/* UPS device state enum */ -enum devstate { - CHRG = 0, /* Charging status */ - BATV, /* Battery voltage */ +/* UPS device reg enum */ +enum devreg { + CHRG = 0, /* Charging status, "battery.charger.status" */ + BATV, /* Battery voltage, "battery.voltage" */ BCEF = 6, /* Battery charge efficiency factor (CEF) */ BSOH, /* Battery state-of-health */ - BSOC = 9, /* Battery state-of-charge */ - BTMP = 11, /* Battery temperature in Kelvin units */ - PMNG = 15, /* Power management */ - PRDN = 21, /* Product name */ - FSD = 80, /* Force shutdown */ + BSOC = 9, /* Battery state-of-charge, "battery.charge" */ + BTMP = 11, /* Battery temperature in Kelvin units, "battery.temperature" */ + PMNG = 15, /* Power management, "ups.status" */ + OTMP = 20, /* Onboard temperature, "ups.temperature" */ + PRDN, /* Product name, "ups.model" */ + VAC = 24, /* AC voltage, "input.voltage" */ + LVDC, /* Load voltage, "output.voltage" */ + LCUR, /* Load current, "output.current" */ + BINH = 79, /* Backup inhibit */ + FSD, /* Force shutdown */ + TBUF, /* Time buffering */ BSTA = 89, /* Battery status alarms */ - SCSH = 90, /* SoH and SoC alarms */ - BVAL = 91, /* Battery voltage alarm */ - BTSF = 92, /* Battery temp sensor failure */ - DEVF = 93, /* Device failure */ - OBTA = 94, /* On board temp alarm */ - VACA = 95, /* VAC alarms */ - MAIN = 96 /* Mains status */ + SCSH, /* SoH and SoC alarms */ + BVAL, /* Battery voltage alarm */ + BTSF, /* Battery temp sensor failure */ + DEVF, /* Device failure */ + OBTA, /* On board temp alarm */ + VACA, /* VAC alarms */ + MAIN /* Mains status */ }; -typedef enum devstate devstate_t; +typedef enum devreg devreg_t; -/* UPS state signal attributes */ +/* UPS register attributes */ struct regattr { int num; int saddr; /* register start address */ @@ -177,6 +217,16 @@ struct regattr { }; typedef struct regattr regattr_t; +/* UPS device state info union */ +union devstate { + prodname_t product; + chrgs_t charge; + pwrmng_t power; + reg_t reg; +}; + +typedef union devstate devstate_t; + /* ADELE CBI registers */ regattr_t regs[] = { {40005, 0, 0, 1, HOLDING}, /* Charging status */ @@ -282,8 +332,4 @@ regattr_t regs[] = { {40046, 0, 0, 1, HOLDING}, /* Mains status */ {40038, 0, 0, 1, HOLDING} /* Load alarm */ }; - -#define NUMOF_REGS 14 -#define NOTUSED -1 - #endif /* ADELE_CBI_H */ From c9ea8e30e780c822d2d89f848f8540a8313d0ad7 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Sun, 9 Jan 2022 03:21:08 +0200 Subject: [PATCH 033/700] alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress --- drivers/adele_cbi.c | 293 +++++++++++++++++++++++++++++++++----------- drivers/adele_cbi.h | 198 ++++++++++++++++++++++++------ 2 files changed, 382 insertions(+), 109 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index d254d4c54b..f81137297b 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -25,27 +25,29 @@ #include #include -#define DRIVER_NAME "NUT Adele CBI driver" +#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" #define DRIVER_VERSION "0.01" /* variables */ -static modbus_t *mbctx = NULL; /* modbus memory context */ -static regattr_t sigar[NUMOF_SIG_STATES]; /* array of ups signal attributes */ -static int errcnt = 0; /* modbus access error counter */ - -static char *device_mfr = DEVICE_MFR; /* device manufacturer */ -static char *device_model = DEVICE_MODEL; /* device model */ -static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ -static char ser_parity = PARITY; /* serial port parity */ -static int ser_data_bit = DATA_BIT; /* serial port data bit */ -static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ -static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ -static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ -static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ -static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ -static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static int errcnt = 0; /* modbus access error counter */ +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static char *device_type = DEVICE_TYPE; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ +/* initialize register start address and hex address from register number */ +void reginit(); + /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); @@ -61,8 +63,8 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg); -/* read signal status */ -int get_signal_state(devreg_t state); +/* get device state */ +int get_dev_state(devreg_t regnum, devstate_t *state); /* count the time elapsed since start */ long time_elapsed(struct timeval *start); @@ -82,64 +84,71 @@ upsdrv_info_t upsdrv_info = { * driver functions */ +/* read configuration variables from ups.conf and connect to ups device */ +void upsdrv_initups(void) +{ + int rval; + upsdebugx(2, "upsdrv_initups"); + + reginit(); + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response time out to 200ms */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} + /* initialize ups driver information */ -void upsdrv_initinfo(void) { +void upsdrv_initinfo(void) +{ + devstate_t ds; /* device state */ upsdebugx(2, "upsdrv_initinfo"); /* set device information */ dstate_setinfo("device.mfr", "%s", device_mfr); dstate_setinfo("device.model", "%s", device_model); + dstate_setinfo("device.type", "%s", device_type); - /* register instant commands */ - if (sigar[FSD_T].addr != NOTUSED) { - dstate_addcmd("load.off"); - } + /* read ups model */ + get_dev_state(PRDN, &ds); + dstate_setinfo("ups.model", "%s", ds.product.name); + + /* register instant commands */ + dstate_addcmd("load.off"); /* set callback for instant commands */ upsh.instcmd = upscmd; } -/* open serial connection and connect to modbus RIO */ -void upsdrv_initups(void) -{ - int rval; - upsdebugx(2, "upsdrv_initups"); - - get_config_vars(); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response time out to 200ms */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -} /* update UPS signal state */ void upsdrv_updateinfo(void) @@ -147,11 +156,20 @@ void upsdrv_updateinfo(void) int rval; int online = -1; /* keep online state */ errcnt = 0; + devstate_t ds; /* device state */ upsdebugx(2, "upsdrv_updateinfo"); status_init(); /* initialize ups.status update */ alarm_init(); /* initialize ups.alarm update */ + /* get "battery.charger.status" */ + get_dev_state(CHRG, &ds); + dstate_setinfo("battery.charger.status", "%s", ds.charge.info); + + /* get "battery.voltage" */ + get_dev_state(BATV, &ds); + dstate_setinfo("battery.charger.status", "%s", ds.reg.strval); + /* * update UPS status regarding MAINS state either via OL | OB. * if both statuses are mapped to contacts then only OL is evaluated. @@ -600,16 +618,18 @@ int upscmd(const char *cmd, const char *arg) return rval; } -/* read device state, returns 0 on success or -1 on communication error */ +/* read device state, returns 0 on success or -1 on communication error + it formats state depending on register semantics */ int get_dev_state(devreg_t regnum, devstate_t *state) { int i; /* local index */ - int rval = -1; /* return value */ + int rval; /* return value */ uint reg_val; /* register value */ - regtype_t rtype = 0; /* register type */ - int addr = regs[regnum].xaddr; - int rtype = regs[regnum].type; + regtype_t rtype; /* register type */ + int addr; /* register address */ + addr = regs[regnum].xaddr; + rtype = regs[regnum].type; rval = register_read(mbctx, addr, rtype, ®_val); if (rval == -1) { return rval; @@ -650,6 +670,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->reg.strval = "0"; } break; + case TBUF: case BSOH: case BCEF: case VAC: /* "input.voltage" */ @@ -710,6 +731,137 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->product.val = reg_val; state->product.name = prdnm_i[i].name; break; + case BSTA: + if (reg_val & BSTA_REVPOL_M) { + bsta.alrm[BSTA_REVPOL_I].actv = 1; + } else { + bsta.alrm[BSTA_REVPOL_I].actv = 0; + } + if (reg_val & BSTA_NOCNND_M) { + bsta.alrm[BSTA_NOCNND_I].actv = 1; + } else { + bsta.alrm[BSTA_NOCNND_I].actv = 0; + } + if (reg_val & BSTA_CLSHCR_M) { + bsta.alrm[BSTA_CLSHCR_I].actv = 1; + } else { + bsta.alrm[BSTA_CLSHCR_I].actv = 0; + } + if (reg_val & BSTA_SULPHD_M) { + bsta.alrm[BSTA_SULPHD_I].actv = 1; + } else { + bsta.alrm[BSTA_SULPHD_I].actv = 0; + } + if (reg_val & BSTA_CHEMNS_M) { + bsta.alrm[BSTA_CHEMNS_I].actv = 1; + } else { + bsta.alrm[BSTA_CHEMNS_I].actv = 0; + } + if (reg_val & BSTA_CNNFLT_M) { + bsta.alrm[BSTA_CNNFLT_I].actv = 1; + } else { + bsta.alrm[BSTA_CNNFLT_I].actv = 0; + } + state->alrm = &bsta; + break; + case SCSH: + if (reg_val & SHSC_HIRESI_M) { + shsc.alrm[SHSC_HIRESI_I].actv = 1; + } else { + shsc.alrm[SHSC_HIRESI_I].actv = 0; + } + if (reg_val & SHSC_LOCHEF_M) { + shsc.alrm[SHSC_LOCHEF_I].actv = 1; + } else { + shsc.alrm[SHSC_LOCHEF_I].actv = 0; + } + if (reg_val & SHSC_LOEFCP_M) { + shsc.alrm[SHSC_LOEFCP_I].actv = 1; + } else { + shsc.alrm[SHSC_LOEFCP_I].actv = 0; + } + if (reg_val & SHSC_LOWSOC_M) { + shsc.alrm[SHSC_LOWSOC_I].actv = 1; + } else { + shsc.alrm[SHSC_LOWSOC_I].actv = 0; + } + state->alrm = &shsc; + break; + case BVAL: + if (reg_val & BVAL_HIALRM_M) { + bval.alrm[BVAL_HIALRM_I].actv = 1; + } else { + bval.alrm[BVAL_HIALRM_I].actv = 0; + } + if (reg_val & BVAL_LOALRM_M) { + bval.alrm[BVAL_LOALRM_I].actv = 1; + } else { + bval.alrm[BVAL_LOALRM_I].actv = 0; + } + if (reg_val & BVAL_BSTSFL_M) { + bval.alrm[BVAL_BSTSFL_I].actv = 1; + } else { + bval.alrm[BVAL_BSTSFL_I].actv = 0; + } + state->alrm = bval; + break; + case BTSF: + if (reg_val & BTSF_FCND_M) { + btsf.alrm[BTSF_FCND_I].actv = 1; + } else { + btsf.alrm[BTSF_FCND_I].actv = 0; + } + if (reg_val & BTSF_NCND_M) { + btsf.alrm[BTSF_NCND_I].actv = 1; + } else { + btsf.alrm[BTSF_NCND_I].actv = 0; + } + state->alrm = &btsf; + break; + case DEVF: + if (reg_val & DEVF_RCALRM_M) { + devf.alrm[DEVF_RCALRM_I].actv = 1; + } else { + devf.alrm[DEVF_RCALRM_I].actv = 0; + } + if (reg_val & DEVF_INALRM_M) { + devf.alrm[DEVF_INALRM_I].actv = 1; + } else { + devf.alrm[DEVF_INALRM_I].actv = 0; + } + if (reg_val & DEVF_LFNAVL_M) { + devf.alrm[DEVF_LFNAVL_I].actv = 1; + } else { + devf.alrm[DEVF_LFNAVL_I].actv = 0; + } + state->alrm = &devf; + break; + case VACA: + if (reg_val & VACA_HIALRM_M) { + vaca.alrm[VACA_HIALRM_I].actv = 1; + } else { + vaca.alrm[VACA_HIALRM_I].actv = 0; + } + if (reg_val == VACA_LOALRM_M) { + vaca.alrm[VACA_LOALRM_I].actv = 1; + } else { + vaca.alrm[VACA_LOALRM_I].actv = 0; + } + state->alrm = &vaca; + break; + case MAIN: + if (reg_val & MAINS_AVAIL_M) { + mains.alrm[MAINS_AVAIL_I].actv = 1; + } else { + mains.alrm[MAINS_AVAIL_I].actv = 0; + } + if (reg_val == SHUTD_REQST_M) { + mains.alrm[SHUTD_REQST_I].actv = 1; + } else { + mains.alrm[SHUTD_REQST_I].actv = 0; + } + state->alrm = &mains; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" @@ -725,8 +877,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) break; } - - upsdebugx(3, "get_signal_state: state: %d", reg_val); + upsdebugx(3, "get_dev_state: state: %d", reg_val); return rval; } diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 14234d23fe..6cd05989a5 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -27,6 +27,7 @@ /* UPS device details */ #define DEVICE_MFR "ADELE" +#define DEVICE_TYPE "DC-UPS" #define DEVICE_MODEL "CB/CBI" /* serial access parameters */ @@ -85,10 +86,10 @@ prodname_t prdnm_i[] = { /* charging status info, "battery.charger.status" */ char *chrgs_i[] = { "none", - "recovery", /* "resting" */ - "bulk", /* "charging" */ - "absorb", /* "charging" */ - "float" /* "floating" */ + "resting", /* recovering */ + "charging", /* bulk */ + "charging", /* absorb */ + "floating" /* float */ }; struct chrgs { int state; @@ -119,6 +120,20 @@ struct reg { }; typedef struct reg reg_t; +/* general alarm struct */ +struct alrm { + int actv; /* active flag */ + char *descr; /* description field */ +}; +typedef struct alrm alrm_t; + +/* general alarm array */ +struct alrm_ar { + int alrm_c; /* alarm count */ + alrm_t alrm[]; /* alarm array */ +}; +typedef struct alrm_ar alrm_ar_t; + /* * BIT MASKS and VALUES */ @@ -139,45 +154,151 @@ typedef struct reg reg_t; /* product name */ #define PRDN_MAX 14 -/* Mains status */ -#define MAINS_AVAIL 0x0001 /* available */ -#define SHUTD_REQST 0x0002 /* shutdown requested */ +/* Mains alarm masks */ +#define MAINS_AVAIL_M 0x0001 /* 0: available 1: not available */ +#define SHUTD_REQST_M 0x0002 /* shutdown requested */ -/* AC input voltage alarms */ -#define VACA_HIALRM 0x0001 /* high alarm */ -#define VACA_LOALRM 0x0002 /* low alarm */ +/* Mains alarm indices */ +#define MAINS_AVAIL_I 0 /* 0: available 1: not available */ +#define SHUTD_REQST_I 1 /* shutdown requested */ + +/* AC input voltage alarm masks */ +#define VACA_HIALRM_M 0x0001 /* high alarm */ +#define VACA_LOALRM_M 0x0002 /* low alarm */ + +/* AC input voltage alarm indices */ +#define VACA_HIALRM_I 0 /* high alarm */ +#define VACA_LOALRM_I 1 /* low alarm */ /* Onboard temperature alarm */ #define OBTA_HIALRM 1 /* high alarm */ -/* Device failure */ -#define DEVF_RCALRM 0x0001 /* rectifier failure */ -#define DEVF_INALRM 0x0006 /* internal failure */ -#define DEVF_LFNAVL 0x0008 /* lifetest not available */ +/* Device failure alarm masks */ +#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ +#define DEVF_INALRM_M 0x0006 /* internal failure */ +#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ + +/* Device failure alarm indices */ +#define DEVF_RCALRM_I 0 /* rectifier failure */ +#define DEVF_INALRM_I 1 /* internal failure */ +#define DEVF_LFNAVL_I 2 /* lifetest not available */ + +/* Battery temp sensor failure alarm masks */ +#define BTSF_FCND_M 0x0001 /* connection fault */ +#define BTSF_NCND_M 0x0002 /* not connected */ + +/* Battery temp sensor failure alarm indices */ +#define BTSF_FCND_I 0 /* connection fault */ +#define BTSF_NCND_I 1 /* not connected */ + +/* Battery voltage alarm masks */ +#define BVAL_HIALRM_M 0x0001 /* high voltage */ +#define BVAL_LOALRM_M 0x0002 /* low voltage */ +#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ + +/* Battery voltage alarm indices */ +#define BVAL_HIALRM_I 0 /* high voltage */ +#define BVAL_LOALRM_I 1 /* low voltage */ +#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ + +/* SoH and SoC alarm masks */ +#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ -/* Battery temp sensor failure */ -#define BTSF_FCND 0x0001 /* connection fault */ -#define BTSF_NCND 0x0001 /* not connected */ +/* SoH and SoC alarm indices */ +#define SHSC_HIRESI_I 0 /* high internal resistance */ +#define SHSC_LOCHEF_I 1 /* low charge efficiency */ +#define SHSC_LOEFCP_I 2 /* low effective capacity */ +#define SHSC_LOWSOC_I 3 /* low state of charge */ -/* Battery voltage alarm */ -#define BVAL_HIALRM 0x0001 /* high voltage */ -#define BVAL_LOALRM 0x0002 /* low voltage */ -#define BVAL_BSTSFL 0x0004 /* battery start with battery flat */ +/* Battery status alarm masks */ +#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ +#define BSTA_NOCNND_M 0x0002 /* not connected */ +#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ +#define BSTA_SULPHD_M 0x0008 /* sulphated */ +#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT_M 0x0020 /* connection fault */ -/* SoH and SoC alarms */ -#define SHSC_HIRESI 0x0001 /* high internal resistance */ -#define SHSC_LOCHEF 0x0002 /* low charge efficiency */ -#define SHSC_LOEFCP 0x0004 /* low effective capacity */ -#define SHSC_LOWSOC 0x0040 /* low state of charge */ +/* Battery status alarm indices */ +#define BSTA_REVPOL_I 0 /* reversed polarity */ +#define BSTA_NOCNND_I 1 /* not connected */ +#define BSTA_CLSHCR_I 2 /* cell short circuit */ +#define BSTA_SULPHD_I 3 /* sulphated */ +#define BSTA_CHEMNS_I 4 /* chemistry not supported */ +#define BSTA_CNNFLT_I 5 /* connection fault */ -/* Battery status alarms */ -#define BSTA_REVPOL 0x0001 /* reversed polarity */ -#define BSTA_NOCNND 0x0002 /* not connected */ -#define BSTA_CLSHCR 0x0004 /* cell short circuit */ -#define BSTA_SULPHD 0x0008 /* sulphated */ -#define BSTA_CHEMNS 0x0010 /* chemistry not supported */ -#define BSTA_CNNFLT 0x0020 /* connection fault */ +/* input mains and shutdown alarms */ +alrm_ar_t mains = { + 2, + { + {0, "input voltage not available"}, + {0, "ups shutdown requested"} + } +}; + +/* AC input voltage alarms */ +alrm_ar_t vaca = { + 2, + { + {0, "input voltage high alarm"}, + {0, "input voltage low alarm"} + } +}; +/* device failure alarms */ +alrm_ar_t devf = { + 3, + { + {0, "UPS rectifier failure"}, + {0, "UPS internal failure"}, + {0, "UPS lifetest not available"} + } +}; + +/* battery sensor failure alarms */ +alrm_ar_t btsf = { + 2, + { + {0, "battery temp sensor connection fault"}, + {0, "battery temp sensor not connected"} + } +}; + +/* battery voltage alarms */ +alrm_ar_t bval = { + 3, + { + {0, "battery high voltage"}, + {0, "battery low voltage"}, + {0, "battery start with battery flat"} + } +}; + +/* battery SoH and SoC alarms */ +alrm_ar_t shsc = { + 4, + { + {0, "battery high internal resistance"}, + {0, "battery low charge efficiency"}, + {0, "battery low effective capacity"}, + {0, "battery low state of charge"} + } +}; + +/* battery status alarm */ +alrm_ar_t bsta = { + 6, + { + {0, "battery reversed polarity"}, + {0, "battery not connected"}, + {0, "battery cell short circuit"}, + {0, "battery sulphated"}, + {0, "battery chemistry not supported"}, + {0, "battery connection fault"} + } +}; /* UPS device reg enum */ enum devreg { @@ -195,7 +316,7 @@ enum devreg { LCUR, /* Load current, "output.current" */ BINH = 79, /* Backup inhibit */ FSD, /* Force shutdown */ - TBUF, /* Time buffering */ + TBUF, /* Time buffering, "battery runtime" */ BSTA = 89, /* Battery status alarms */ SCSH, /* SoH and SoC alarms */ BVAL, /* Battery voltage alarm */ @@ -219,10 +340,11 @@ typedef struct regattr regattr_t; /* UPS device state info union */ union devstate { - prodname_t product; - chrgs_t charge; - pwrmng_t power; - reg_t reg; + prodname_t product; /* ups model name */ + chrgs_t charge; /* charging status */ + pwrmng_t power; /* ups status */ + reg_t reg; /* state register*/ + alrm_ar_t *alrm; /* alarm statuses */ }; typedef union devstate devstate_t; From 32096ebbb7ea0d9d08fca0df2491eb652cb420af Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Mon, 10 Jan 2022 13:33:46 +0200 Subject: [PATCH 034/700] first testing release --- drivers/adele_cbi.c | 604 +++++++++++++++----------------------------- drivers/adele_cbi.h | 12 +- 2 files changed, 214 insertions(+), 402 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index f81137297b..61eb099775 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -51,6 +51,9 @@ void reginit(); /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); +/* get device state */ +int get_dev_state(devreg_t regnum, devstate_t *state); + /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port); @@ -60,16 +63,15 @@ void modbus_reconnect(); /* modbus register read function */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); +/* modbus register write function */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg); -/* get device state */ -int get_dev_state(devreg_t regnum, devstate_t *state); - /* count the time elapsed since start */ long time_elapsed(struct timeval *start); -int register_write(modbus_t *mb, int addr, regtype_t type, void *data); /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -153,8 +155,8 @@ void upsdrv_initinfo(void) /* update UPS signal state */ void upsdrv_updateinfo(void) { - int rval; - int online = -1; /* keep online state */ + int rval; + int i; /* local index */ errcnt = 0; devstate_t ds; /* device state */ @@ -162,132 +164,198 @@ void upsdrv_updateinfo(void) status_init(); /* initialize ups.status update */ alarm_init(); /* initialize ups.alarm update */ + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ + rval = get_dev_state(MAIN, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains.alrm[MAINS_AVAIL_I].descr); + } else { + status_set("OL"); + } + if (ds.alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains.alrm[SHUTD_REQST_I].descr); + } + + /* + * update UPS status regarding battery voltage + */ + rval = get_dev_state(BVAL, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set( bval.alrm[BVAL_LOALRM_I].descr); + } + if (ds.alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set( bval.alrm[BVAL_HIALRM_I].descr); + } + if (ds.alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set( bval.alrm[BVAL_BSTSFL_I].descr); + } + + /* get "battery.voltage" */ + rval = get_dev_state(BATV, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.voltage", "%s", ds.reg.strval); + + /* + * update UPS status regarding battery charger status + */ + /* get "battery.charger.status" */ - get_dev_state(CHRG, &ds); + rval = get_dev_state(CHRG, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.charge.state == CHRG_BULK || ds.charge.state == CHRG_ABSR) { + status_set("CHRG"); + } dstate_setinfo("battery.charger.status", "%s", ds.charge.info); - /* get "battery.voltage" */ - get_dev_state(BATV, &ds); - dstate_setinfo("battery.charger.status", "%s", ds.reg.strval); + rval = get_dev_state(PMNG, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + if (ds.power.state == PMNG_BOOST) { + status_set("BOOST"); + } - /* - * update UPS status regarding MAINS state either via OL | OB. - * if both statuses are mapped to contacts then only OL is evaluated. - */ - if (sigar[OL_T].addr != NOTUSED) { - rval = get_signal_state(OL_T); - upsdebugx(2, "OL value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[OL_T].noro)) { - status_set("OL"); - online = 1; - } else { - status_set("OB"); - online = 0; + /* + * update UPS battery state of charge + */ + rval = get_dev_state(BSOC, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.charge", "%s", ds.reg.strval); - /* if DISCHRG state is not mapped to a contact and UPS is on - * batteries set status to DISCHRG state */ - if (sigar[DISCHRG_T].addr == NOTUSED) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } + /* + * update UPS AC input state + */ + rval = get_dev_state(VACA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } + rval = get_dev_state(VAC, &ds); + dstate_setinfo("input.voltage", "%s", ds.reg.strval); - } - } else if (sigar[OB_T].addr != NOTUSED) { - rval = get_signal_state(OB_T); - upsdebugx(2, "OB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[OB_T].noro)) { - status_set("OB"); - online = 0; - if (sigar[DISCHRG_T].addr == NOTUSED) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } else { - status_set("OL"); - online = 1; - } - } + /* + * update UPS onboard temperature state + */ + rval = get_dev_state(OBTA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } + rval = get_dev_state(OTMP, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("ups.temperature", "%s", ds.reg.strval); /* - * update UPS status regarding CHARGING state via HB. HB is usually - * mapped to "ready" contact when closed indicates a charging state > 85% - */ - if (sigar[HB_T].addr != NOTUSED) { - rval = get_signal_state(HB_T); - upsdebugx(2, "HB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[HB_T].noro)) { - status_set("HB"); - dstate_setinfo("battery.charger.status", "resting"); - } else if (online == 1 && sigar[CHRG_T].addr == NOTUSED && errcnt == 0) { - status_set("CHRG"); - dstate_setinfo("battery.charger.status", "charging"); - } else if (online == 0 && sigar[DISCHRG_T].addr == NOTUSED && errcnt == 0) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } + * update UPS battery temperature state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } + rval = get_dev_state(BTMP, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.temperature", "%s", ds.reg.strval); + rval = get_dev_state(TBUF, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.runtime", "%s", ds.reg.strval); - /* - * update UPS status regarding DISCHARGING state via LB. LB is mapped - * to "battery low" contact. - */ - if (sigar[LB_T].addr != NOTUSED) { - rval = get_signal_state(LB_T); - upsdebugx(2, "LB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[LB_T].noro)) { - status_set("LB"); - alarm_set("Low Battery (Charge)"); - } - } + /* + * update UPS device failure state + */ + rval = get_dev_state(DEVF, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } - /* - * update UPS status regarding battery HEALTH state via RB. RB is mapped - * to "replace battery" contact - */ - if (sigar[RB_T].addr != NOTUSED) { - rval = get_signal_state(RB_T); - upsdebugx(2, "RB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[RB_T].noro)) { - status_set("RB"); - alarm_set("Replace Battery"); - } - } + /* + * update UPS SoH and SoC states + */ + rval = get_dev_state(SCSH, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } - /* - * update UPS status regarding battery HEALTH state via RB. RB is mapped - * to "replace battery" contact - */ - if (sigar[CHRG_T].addr != NOTUSED) { - rval = get_signal_state(CHRG_T); - upsdebugx(2, "CHRG value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[CHRG_T].noro)) { - status_set("CHRG"); - dstate_setinfo("battery.charger.status", "charging"); - } - } else if (sigar[DISCHRG_T].addr != NOTUSED) { - rval = get_signal_state(DISCHRG_T); - upsdebugx(2, "DISCHRG value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[DISCHRG_T].noro)) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } + /* + * update UPS battery state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } - /* check for communication errors */ + /* + * update UPS load status + */ + rval = get_dev_state(LVDC, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("output.voltage", "%s", ds.reg.strval); + rval = get_dev_state(LCUR, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("output.current", "%s", ds.reg.strval); + + + /* check for communication errors */ if (errcnt == 0) { alarm_commit(); status_commit(); @@ -551,67 +619,24 @@ int upscmd(const char *cmd, const char *arg) { int rval; int data; - struct timeval start; - long etime; if (!strcasecmp(cmd, "load.off")) { - if (sigar[FSD_T].addr != NOTUSED && - (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) - ) { - data = 1 ^ sigar[FSD_T].noro; - rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); - if (rval == -1) { - upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - sigar[FSD_T].addr, - sigar[FSD_T].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d", sigar[FSD_T].addr, data); - rval = STAT_INSTCMD_HANDLED; - } - - /* if pulse has been defined and rising edge was successful */ - if (FSD_pulse_duration != NOTUSED && rval == STAT_INSTCMD_HANDLED) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for FSD_pulse_duration ms */ - while ((etime = time_elapsed(&start)) < FSD_pulse_duration); - - data = 0 ^ sigar[FSD_T].noro; - rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); - if (rval == -1) { - upslogx(LOG_ERR, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - sigar[FSD_T].addr, - sigar[FSD_T].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d, elapsed time: %lims", - sigar[FSD_T].addr, - data, - etime - ); - rval = STAT_INSTCMD_HANDLED; - } - } - } else { - upslogx(LOG_NOTICE,"load.off: failed (FSD address undefined or invalid register type) [%s] [%s]", - cmd, - arg - ); - rval = STAT_INSTCMD_FAILED; - } - } else { + data = 1; + rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + regs[FSD].xaddr, + regs[FSD].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); + rval = STAT_INSTCMD_HANDLED; + } + } else { upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); rval = STAT_INSTCMD_UNKNOWN; } @@ -803,7 +828,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) } else { bval.alrm[BVAL_BSTSFL_I].actv = 0; } - state->alrm = bval; + state->alrm = &bval; break; case BTSF: if (reg_val & BTSF_FCND_M) { @@ -884,14 +909,6 @@ int get_dev_state(devreg_t regnum, devstate_t *state) /* get driver configuration parameters */ void get_config_vars() { - int i; /* local index */ - - /* initialize sigar table */ - for (i = 0; i < NUMOF_SIG_STATES; i++) { - sigar[i].addr = NOTUSED; - sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ - } - /* check if device manufacturer is set ang get the value */ if (testvar("device_mfr")) { device_mfr = getval("device_mfr"); @@ -970,217 +987,6 @@ void get_config_vars() } } upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); - - /* check if OL address is set and get the value */ - if (testvar("OL_addr")) { - sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); - if (testvar("OL_noro")) { - sigar[OL_T].noro = (int)strtol(getval("OL_noro"), NULL, 10); - if (sigar[OL_T].noro != 1) { - sigar[OL_T].noro = 0; - } - } - } - - /* check if OL register type is set and get the value otherwise set to INPUT_B */ - if (testvar("OL_regtype")) { - sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); - if (sigar[OL_T].type < COIL || sigar[OL_T].type > HOLDING) { - sigar[OL_T].type = INPUT_B; - } - } else { - sigar[OL_T].type = INPUT_B; - } - - /* check if OB address is set and get the value */ - if (testvar("OB_addr")) { - sigar[OB_T].addr = (int)strtol(getval("OB_addr"), NULL, 0); - } - if (testvar("OB_noro")) { - sigar[OB_T].noro = (int)strtol(getval("OB_noro"), NULL, 10); - if (sigar[OB_T].noro != 1) { - sigar[OB_T].noro = 0; - } - } - - /* check if OB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("OB_regtype")) { - sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); - if (sigar[OB_T].type < COIL || sigar[OB_T].type > HOLDING) { - sigar[OB_T].type = INPUT_B; - } - } else { - sigar[OB_T].type = INPUT_B; - } - - /* check if LB address is set and get the value */ - if (testvar("LB_addr")) { - sigar[LB_T].addr = (int)strtol(getval("LB_addr"), NULL, 0); - if (testvar("LB_noro")) { - sigar[LB_T].noro = (int)strtol(getval("LB_noro"), NULL, 10); - if (sigar[LB_T].noro != 1) { - sigar[LB_T].noro = 0; - } - } - } - - /* check if LB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("LB_regtype")) { - sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); - if (sigar[LB_T].type < COIL || sigar[LB_T].type > HOLDING) { - sigar[LB_T].type = INPUT_B; - } - } else { - sigar[LB_T].type = INPUT_B; - } - - /* check if HB address is set and get the value */ - if (testvar("HB_addr")) { - sigar[HB_T].addr = (int)strtol(getval("HB_addr"), NULL, 0); - if (testvar("HB_noro")) { - sigar[HB_T].noro = (int)strtol(getval("HB_noro"), NULL, 10); - if (sigar[HB_T].noro != 1) { - sigar[HB_T].noro = 0; - } - } - } - - /* check if HB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("HB_regtype")) { - sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); - if (sigar[HB_T].type < COIL || sigar[HB_T].type > HOLDING) { - sigar[HB_T].type = INPUT_B; - } - } else { - sigar[HB_T].type = INPUT_B; - } - - /* check if RB address is set and get the value */ - if (testvar("RB_addr")) { - sigar[RB_T].addr = (int)strtol(getval("RB_addr"), NULL, 0); - if (testvar("RB_noro")) { - sigar[RB_T].noro = (int)strtol(getval("RB_noro"), NULL, 10); - if (sigar[RB_T].noro != 1) { - sigar[RB_T].noro = 0; - } - } - } - - /* check if RB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("RB_regtype")) { - sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); - if (sigar[RB_T].type < COIL || sigar[RB_T].type > HOLDING) { - sigar[RB_T].type = INPUT_B; - } - } else { - sigar[RB_T].type = INPUT_B; - } - - /* check if CHRG address is set and get the value */ - if (testvar("CHRG_addr")) { - sigar[CHRG_T].addr = (int)strtol(getval("CHRG_addr"), NULL, 0); - if (testvar("CHRG_noro")) { - sigar[CHRG_T].noro = (int)strtol(getval("CHRG_noro"), NULL, 10); - if (sigar[CHRG_T].noro != 1) { - sigar[CHRG_T].noro = 0; - } - } - } - - /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ - if (testvar("CHRG_regtype")) { - sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); - if (sigar[CHRG_T].type < COIL || sigar[CHRG_T].type > HOLDING) { - sigar[CHRG_T].type = INPUT_B; - } - } else { - sigar[CHRG_T].type = INPUT_B; - } - - /* check if DISCHRG address is set and get the value */ - if (testvar("DISCHRG_addr")) { - sigar[DISCHRG_T].addr = (int)strtol(getval("DISCHRG_addr"), NULL, 0); - if (testvar("DISCHRG_noro")) { - sigar[DISCHRG_T].noro = (int)strtol(getval("DISCHRG_noro"), NULL, 10); - if (sigar[DISCHRG_T].noro != 1) { - sigar[DISCHRG_T].noro = 0; - } - } - } - - /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ - if (testvar("DISCHRG_regtype")) { - sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); - if (sigar[DISCHRG_T].type < COIL || sigar[DISCHRG_T].type > HOLDING) { - sigar[DISCHRG_T].type = INPUT_B; - } - } else { - sigar[DISCHRG_T].type = INPUT_B; - } - - /* check if FSD address is set and get the value */ - if (testvar("FSD_addr")) { - sigar[FSD_T].addr = (int)strtol(getval("FSD_addr"), NULL, 0); - if (testvar("FSD_noro")) { - sigar[FSD_T].noro = (int)strtol(getval("FSD_noro"), NULL, 10); - if (sigar[FSD_T].noro != 1) { - sigar[FSD_T].noro = 0; - } - } - } - - /* check if FSD register type is set and get the value otherwise set to COIL */ - if (testvar("FSD_regtype")) { - sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); - if (sigar[FSD_T].type < COIL || sigar[FSD_T].type > HOLDING) { - sigar[FSD_T].type = COIL; - } - } else { - sigar[FSD_T].type = COIL; - } - - /* check if FSD pulse duration is set and get the value */ - if (testvar("FSD_pulse_duration")) { - FSD_pulse_duration = (int) strtol(getval("FSD_pulse_duration"), NULL, 10); - } - upsdebugx(2, "FSD_pulse_duration %d", FSD_pulse_duration); - - /* debug loop over signal array */ - for (i = 0; i < NUMOF_SIG_STATES; i++) { - if (sigar[i].addr != NOTUSED) { - char *signame; - switch (i) { - case OL_T: - signame = "OL"; - break; - case OB_T: - signame = "OB"; - break; - case LB_T: - signame = "LB"; - break; - case HB_T: - signame = "HB"; - break; - case RB_T: - signame = "RB"; - break; - case FSD_T: - signame = "FSD"; - break; - case CHRG_T: - signame = "CHRG"; - break; - case DISCHRG_T: - signame = "DISCHRG"; - break; - default: - signame = "NOTUSED"; - break; - } - upsdebugx(2, "%s, addr:0x%x, type:%d", signame, sigar[i].addr, sigar[i].type); - } - } } /* create a new modbus context based on connection type (serial or TCP) */ diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 6cd05989a5..3a823ded3a 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -54,6 +54,12 @@ /* number of device models */ #define DEV_NUMOF_MODELS 10 +/* shutdown repeat on error */ +#define FSD_REPEAT_CNT 3 + +/* shutdown repeat interval in ms */ +#define FSD_REPEAT_INTRV 1500 + /* definition of register type */ enum regtype { COIL = 0, @@ -155,11 +161,11 @@ typedef struct alrm_ar alrm_ar_t; #define PRDN_MAX 14 /* Mains alarm masks */ -#define MAINS_AVAIL_M 0x0001 /* 0: available 1: not available */ +#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ #define SHUTD_REQST_M 0x0002 /* shutdown requested */ /* Mains alarm indices */ -#define MAINS_AVAIL_I 0 /* 0: available 1: not available */ +#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ #define SHUTD_REQST_I 1 /* shutdown requested */ /* AC input voltage alarm masks */ @@ -316,7 +322,7 @@ enum devreg { LCUR, /* Load current, "output.current" */ BINH = 79, /* Backup inhibit */ FSD, /* Force shutdown */ - TBUF, /* Time buffering, "battery runtime" */ + TBUF, /* Time buffering, "battery.runtime" */ BSTA = 89, /* Battery status alarms */ SCSH, /* SoH and SoC alarms */ BVAL, /* Battery voltage alarm */ From 7d1ae01c6fc2f9f790dd4e06a20d277aacad4cf5 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Tue, 11 Jan 2022 01:43:36 +0200 Subject: [PATCH 035/700] ghost alarms bug fix, other bug fixes --- drivers/adele_cbi.c | 843 ++++++++++++++++++++++++-------------------- drivers/adele_cbi.h | 51 +-- 2 files changed, 500 insertions(+), 394 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index 61eb099775..852629a7de 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -1,7 +1,7 @@ /* adele_cbi.c - driver for ADELE CB/CBI DC-UPS * * Copyright (C) - * 2021 Dimitris Economou + * 2022 Dimitris Economou * * 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 @@ -25,11 +25,12 @@ #include #include -#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" +#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" #define DRIVER_VERSION "0.01" /* variables */ static modbus_t *mbctx = NULL; /* modbus memory context */ +static devstate_t *dstate = NULL; /* device state context */ static int errcnt = 0; /* modbus access error counter */ static char *device_mfr = DEVICE_MFR; /* device manufacturer */ static char *device_model = DEVICE_MODEL; /* device model */ @@ -52,7 +53,7 @@ void reginit(); void get_config_vars(void); /* get device state */ -int get_dev_state(devreg_t regnum, devstate_t *state); +int get_dev_state(devreg_t regindx, devstate_t **dstate); /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port); @@ -75,11 +76,11 @@ long time_elapsed(struct timeval *start); /* driver description structure */ upsdrv_info_t upsdrv_info = { - DRIVER_NAME, - DRIVER_VERSION, - "Dimitris Economou \n", - DRV_BETA, - {NULL} + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} }; /* @@ -92,6 +93,8 @@ void upsdrv_initups(void) int rval; upsdebugx(2, "upsdrv_initups"); + + dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); reginit(); get_config_vars(); @@ -132,56 +135,62 @@ void upsdrv_initups(void) /* initialize ups driver information */ void upsdrv_initinfo(void) { - devstate_t ds; /* device state */ - upsdebugx(2, "upsdrv_initinfo"); + devstate_t *ds = dstate; /* device state context */ + upsdebugx(2, "upsdrv_initinfo"); - /* set device information */ - dstate_setinfo("device.mfr", "%s", device_mfr); - dstate_setinfo("device.model", "%s", device_model); + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); dstate_setinfo("device.type", "%s", device_type); /* read ups model */ get_dev_state(PRDN, &ds); - dstate_setinfo("ups.model", "%s", ds.product.name); + dstate_setinfo("ups.model", "%s", ds->product.name); + upslogx(LOG_INFO, "ups.model = %s", ds->product.name); /* register instant commands */ - dstate_addcmd("load.off"); + dstate_addcmd("load.off"); - /* set callback for instant commands */ - upsh.instcmd = upscmd; + /* set callback for instant commands */ + upsh.instcmd = upscmd; } /* update UPS signal state */ void upsdrv_updateinfo(void) { - int rval; - int i; /* local index */ - errcnt = 0; - devstate_t ds; /* device state */ - - upsdebugx(2, "upsdrv_updateinfo"); - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ - - /* - * update UPS status regarding MAINS and SHUTDOWN request - * - OL: On line (mains is present) - * - OB: On battery (mains is not present) - */ + int rval; /* return value */ + int i; /* local index */ + devstate_t *ds = dstate; /* device state */ + + upsdebugx(2, "upsdrv_updateinfo"); + + errcnt = 0; /* initialize error counter to zero */ + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ + + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ rval = get_dev_state(MAIN, &ds); if (rval == -1) { errcnt++; - } - if (ds.alrm->alrm[MAINS_AVAIL_I].actv) { - status_set("OB"); - alarm_set(mains.alrm[MAINS_AVAIL_I].descr); } else { - status_set("OL"); - } - if (ds.alrm->alrm[SHUTD_REQST_I].actv) { - status_set("FSD"); - alarm_set(mains.alrm[SHUTD_REQST_I].descr); + if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains.alrm[MAINS_AVAIL_I].descr); + upslogx(LOG_INFO, "ups.status = OB"); + } else { + status_set("OL"); + upslogx(LOG_INFO, "ups.status = OL"); + } + if (ds->alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains.alrm[SHUTD_REQST_I].descr); + upslogx(LOG_INFO, "ups.status = FSD"); + } } /* @@ -190,26 +199,31 @@ void upsdrv_updateinfo(void) rval = get_dev_state(BVAL, &ds); if (rval == -1) { errcnt++; - } - if (ds.alrm->alrm[BVAL_LOALRM_I].actv) { - status_set("LB"); - alarm_set( bval.alrm[BVAL_LOALRM_I].descr); - } - if (ds.alrm->alrm[BVAL_HIALRM_I].actv) { - status_set("HB"); - alarm_set( bval.alrm[BVAL_HIALRM_I].descr); - } - if (ds.alrm->alrm[BVAL_BSTSFL_I].actv) { - alarm_set( bval.alrm[BVAL_BSTSFL_I].descr); + } else { + if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set(bval.alrm[BVAL_LOALRM_I].descr); + upslogx(LOG_INFO, "ups.status = LB"); + } + if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set(bval.alrm[BVAL_HIALRM_I].descr); + upslogx(LOG_INFO, "ups.status = HB"); + } + if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); + upslogx(LOG_INFO, "battery start with battery flat"); + } } /* get "battery.voltage" */ rval = get_dev_state(BATV, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); } - dstate_setinfo("battery.voltage", "%s", ds.reg.strval); - /* * update UPS status regarding battery charger status */ @@ -218,22 +232,27 @@ void upsdrv_updateinfo(void) rval = get_dev_state(CHRG, &ds); if (rval == -1) { errcnt++; + } else { + if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { + status_set("CHRG"); + upslogx(LOG_INFO, "ups.status = CHRG"); + } + dstate_setinfo("battery.charger.status", "%s", ds->charge.info); + upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); } - if (ds.charge.state == CHRG_BULK || ds.charge.state == CHRG_ABSR) { - status_set("CHRG"); - } - dstate_setinfo("battery.charger.status", "%s", ds.charge.info); - rval = get_dev_state(PMNG, &ds); if (rval == -1) { errcnt++; - } - if (ds.power.state == PMNG_BCKUP) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - if (ds.power.state == PMNG_BOOST) { - status_set("BOOST"); + } else { + if (ds->power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + upslogx(LOG_INFO, "ups.status = DISCHRG"); + } + if (ds->power.state == PMNG_BOOST) { + status_set("BOOST"); + upslogx(LOG_INFO, "ups.status = BOOST"); + } } /* @@ -242,8 +261,10 @@ void upsdrv_updateinfo(void) rval = get_dev_state(BSOC, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.charge", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); } - dstate_setinfo("battery.charge", "%s", ds.reg.strval); /* * update UPS AC input state @@ -251,14 +272,17 @@ void upsdrv_updateinfo(void) rval = get_dev_state(VACA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } } + rval = get_dev_state(VAC, &ds); + dstate_setinfo("input.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); } - rval = get_dev_state(VAC, &ds); - dstate_setinfo("input.voltage", "%s", ds.reg.strval); /* * update UPS onboard temperature state @@ -266,40 +290,49 @@ void upsdrv_updateinfo(void) rval = get_dev_state(OBTA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } } } rval = get_dev_state(OTMP, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("ups.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); } - dstate_setinfo("ups.temperature", "%s", ds.reg.strval); - /* * update UPS battery temperature state */ rval = get_dev_state(BSTA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } rval = get_dev_state(BTMP, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); } - dstate_setinfo("battery.temperature", "%s", ds.reg.strval); rval = get_dev_state(TBUF, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.runtime", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); } - dstate_setinfo("battery.runtime", "%s", ds.reg.strval); /* * update UPS device failure state @@ -307,10 +340,12 @@ void upsdrv_updateinfo(void) rval = get_dev_state(DEVF, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } @@ -320,10 +355,12 @@ void upsdrv_updateinfo(void) rval = get_dev_state(SCSH, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } @@ -333,10 +370,12 @@ void upsdrv_updateinfo(void) rval = get_dev_state(BSTA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } @@ -346,56 +385,58 @@ void upsdrv_updateinfo(void) rval = get_dev_state(LVDC, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("output.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); } - dstate_setinfo("output.voltage", "%s", ds.reg.strval); rval = get_dev_state(LCUR, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("output.current", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); } - dstate_setinfo("output.current", "%s", ds.reg.strval); - - /* check for communication errors */ - if (errcnt == 0) { - alarm_commit(); - status_commit(); - dstate_dataok(); - } else { - upsdebugx(2,"Communication errors: %d", errcnt); - dstate_datastale(); - } + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2, "Communication errors: %d", errcnt); + dstate_datastale(); + } } /* shutdown UPS */ void upsdrv_shutdown(void) { - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); - case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); } /* print driver usage info */ @@ -406,13 +447,13 @@ void upsdrv_help(void) /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { - addvar(VAR_VALUE, "device_mfr", "device manufacturer"); - addvar(VAR_VALUE, "device_model", "device model"); - addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); - addvar(VAR_VALUE, "ser_parity", "serial port parity"); - addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); - addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); - addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); @@ -422,10 +463,13 @@ void upsdrv_makevartable(void) /* close modbus connection and free modbus context allocated memory */ void upsdrv_cleanup(void) { - if (mbctx != NULL) { - modbus_close(mbctx); - modbus_free(mbctx); - } + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } + if (dstate != NULL) { + free(dstate); + } } /* @@ -437,7 +481,7 @@ void reginit() { int i; /* local index */ - for (i = 1; i < MODBUS_NUMOF_REGS; i++) { + for (i = 0; i < MODBUS_NUMOF_REGS; i++) { int rnum = regs[i].num; switch (regs[i].type) { case COIL: @@ -460,13 +504,14 @@ void reginit() regs[i].xaddr = 0x40000 + rnum - 1; break; default: - upslogx(LOG_ERR, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); - upsdebugx(3, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); + upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); } - upsdebugx(3, "register num:%d, type: %d saddr %d, xaddr x%x\n", regs[i].num, - regs[i].type, - regs[i].saddr, - regs[i].xaddr + upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", + regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr ); } } @@ -474,153 +519,153 @@ void reginit() /* Read a modbus register */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) { - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; - - switch (type) { - case COIL: - rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_B: - rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_R: - rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - case HOLDING: - rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); + upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); /* on BROKEN PIPE error try to reconnect */ if (errno == EPIPE) { - upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); modbus_reconnect(); } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; } /* write a modbus register */ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) { - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; - - switch (type) { - case COIL: - *(uint *)data = *(uint *)data & mask8; - rval = modbus_write_bit(mb, addr, *(uint8_t *)data); - break; - case HOLDING: - *(uint *)data = *(uint *)data & mask16; - rval = modbus_write_register(mb, addr, *(uint16_t *)data); - break; - - case INPUT_B: - case INPUT_R: + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; } /* returns the time elapsed since start in milliseconds */ long time_elapsed(struct timeval *start) { - long rval; - struct timeval end; - - rval = gettimeofday(&end, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); - } - if (start->tv_usec < end.tv_usec) { - suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; - end.tv_usec -= 1000000 * nsec; - end.tv_sec += nsec; - } - if (start->tv_usec - end.tv_usec > 1000000) { - suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; - end.tv_usec += 1000000 * nsec; - end.tv_sec -= nsec; - } - rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; - - return rval; + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; } /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg) { - int rval; - int data; + int rval; + int data; - if (!strcasecmp(cmd, "load.off")) { + if (!strcasecmp(cmd, "load.off")) { data = 1; rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); if (rval == -1) { @@ -637,32 +682,45 @@ int upscmd(const char *cmd, const char *arg) rval = STAT_INSTCMD_HANDLED; } } else { - upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_UNKNOWN; - } - return rval; + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; } /* read device state, returns 0 on success or -1 on communication error it formats state depending on register semantics */ -int get_dev_state(devreg_t regnum, devstate_t *state) +int get_dev_state(devreg_t regindx, devstate_t **dstate) { int i; /* local index */ + int n; int rval; /* return value */ - uint reg_val; /* register value */ + static char *ptr = NULL; /* temporary pointer */ + uint num; /* register number */ + uint reg_val; /* register value */ regtype_t rtype; /* register type */ int addr; /* register address */ + devstate_t *state; /* device state */ + + state = *dstate; + num = regs[regindx].num; + addr = regs[regindx].xaddr; + rtype = regs[regindx].type; - addr = regs[regnum].xaddr; - rtype = regs[regnum].type; rval = register_read(mbctx, addr, rtype, ®_val); if (rval == -1) { return rval; } - - /* process register data */ - switch (regnum) { - case CHRG: + upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", + num, + addr, + rtype, + reg_val + ); + + /* process register data */ + switch (regindx) { + case CHRG: /* "ups.charge" */ if (reg_val == CHRG_NONE) { state->charge.state = CHRG_NONE; state->charge.info = chrgs_i[CHRG_NONE]; @@ -679,21 +737,27 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->charge.state = CHRG_FLOAT; state->charge.info = chrgs_i[CHRG_FLOAT]; } - break; + upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); + break; case BATV: /* "battery.voltage" */ case LVDC: /* "output.voltage" */ case LCUR: /* "output.current" */ if (reg_val != 0) { state->reg.val16 = reg_val; double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ - int n = snprintf(NULL, 0, "%f", fval); - char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); - sprintf(fval_s, "%f", fval); + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; } else { state->reg.val16 = 0; - state->reg.strval = "0"; + state->reg.strval = "0.00"; } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case TBUF: case BSOH: @@ -701,38 +765,53 @@ int get_dev_state(devreg_t regnum, devstate_t *state) case VAC: /* "input.voltage" */ if (reg_val != 0) { state->reg.val16 = reg_val; - int n = snprintf(NULL, 0, "%d", reg_val); - char *reg_val_s = (char *)malloc(sizeof(char) * (n + 1)); + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; sprintf(reg_val_s, "%d", reg_val); state->reg.strval = reg_val_s; } else { state->reg.val16 = 0; state->reg.strval = "0"; } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case BSOC: /* "battery.charge" */ if (reg_val != 0) { state->reg.val16 = reg_val; double fval = (double )reg_val * regs[BSOC].scale; - int n = snprintf(NULL, 0, "%f", fval); - char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); - sprintf(fval_s, "%f", fval); + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; } else { state->reg.val16 = 0; - state->reg.strval = "0"; + state->reg.strval = "0.00"; } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case BTMP: /* "battery.temperature" */ case OTMP: /* "ups.temperature" */ state->reg.val16 = reg_val; double fval = reg_val - 273.15; - int n = snprintf(NULL, 0, "%f", fval); - char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); - sprintf(fval_s, "%f", fval); + n = snprintf(NULL, 0, "%.2f", fval); + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + if (ptr != NULL) { + free(ptr); + } + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; - case PMNG: /* "ups.status" & "battery.charge" */ + case PMNG: /* "ups.status" & "battery.charge" */ if (reg_val == PMNG_BCKUP) { state->power.state = PMNG_BCKUP; state->power.info = pwrmng_i[PMNG_BCKUP]; @@ -746,7 +825,8 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->power.state = PMNG_NCHRG; state->power.info = pwrmng_i[PMNG_NCHRG]; } - break; + upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); + break; case PRDN: /* "ups.model" */ for (i = 0; i < DEV_NUMOF_MODELS; i++) { if (prdnm_i[i].val == reg_val) { @@ -755,6 +835,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) } state->product.val = reg_val; state->product.name = prdnm_i[i].name; + upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); break; case BSTA: if (reg_val & BSTA_REVPOL_M) { @@ -887,76 +968,90 @@ int get_dev_state(devreg_t regnum, devstate_t *state) } state->alrm = &mains; break; + case OBTA: + if (reg_val == OBTA_HIALRM_V) { + obta.alrm[OBTA_HIALRM_I].actv = 1; + } + state->alrm = &obta; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - break; - } + state->reg.val16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + break; + } - upsdebugx(3, "get_dev_state: state: %d", reg_val); - return rval; + return rval; } /* get driver configuration parameters */ void get_config_vars() { - /* check if device manufacturer is set ang get the value */ - if (testvar("device_mfr")) { - device_mfr = getval("device_mfr"); - } - upsdebugx(2, "device_mfr %s", device_mfr); - - /* check if device model is set ang get the value */ - if (testvar("device_model")) { - device_model = getval("device_model"); - } - upsdebugx(2, "device_model %s", device_model); - - /* check if serial baud rate is set ang get the value */ - if (testvar("ser_baud_rate")) { - ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); - } - upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); - - /* check if serial parity is set ang get the value */ - if (testvar("ser_parity")) { - /* Dereference the char* we get */ - char *sp = getval("ser_parity"); - if (sp) { - /* TODO? Sanity-check the char we get? */ - ser_parity = *sp; - } else { - upsdebugx(2, "Could not determine ser_parity, will keep default"); - } - } - upsdebugx(2, "ser_parity %c", ser_parity); - - /* check if serial data bit is set ang get the value */ - if (testvar("ser_data_bit")) { - ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); - } - upsdebugx(2, "ser_data_bit %d", ser_data_bit); - - /* check if serial stop bit is set ang get the value */ - if (testvar("ser_stop_bit")) { - ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); - } - upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); - - /* check if device ID is set ang get the value */ - if (testvar("rio_slave_id")) { - rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); - } - upsdebugx(2, "rio_slave_id %d", rio_slave_id); + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); /* check if response time out (s) is set ang get the value */ if (testvar("mod_resp_to_s")) { @@ -992,29 +1087,29 @@ void get_config_vars() /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port) { - modbus_t *mb; - char *sp; - if (strstr(port, "/dev/tty") != NULL) { - mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); - } - } else if ((sp = strchr(port, ':')) != NULL) { - char *tcp_port = xmalloc(sizeof(sp)); - strcpy(tcp_port, sp + 1); - *sp = '\0'; - mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - free(tcp_port); - } else { - mb = modbus_new_tcp(port, 502); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - } - return mb; + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; } /* reconnect to modbus server upon connection error */ @@ -1022,7 +1117,7 @@ void modbus_reconnect() { int rval; - upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); + upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); /* clear current modbus context */ modbus_close(mbctx); @@ -1060,4 +1155,4 @@ void modbus_reconnect() modbus_free(mbctx); fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); } -} \ No newline at end of file +} diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 3a823ded3a..b678ff03e5 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -1,7 +1,7 @@ /* adele_cbi.h - Driver for ADELE CB/CBI DC-UPS * * Copyright (C) - * 2021 Dimitris Economou + * 2022 Dimitris Economou * * 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 @@ -62,10 +62,10 @@ /* definition of register type */ enum regtype { - COIL = 0, - INPUT_B, - INPUT_R, - HOLDING + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING }; typedef enum regtype regtype_t; @@ -75,7 +75,7 @@ struct prodname { char *name; }; typedef struct prodname prodname_t; -prodname_t prdnm_i[] = { +static prodname_t prdnm_i[] = { {1, "CBI1235A"}, {2, "CBI2420A"}, {3, "CBI4810A"}, @@ -90,7 +90,7 @@ prodname_t prdnm_i[] = { }; /* charging status info, "battery.charger.status" */ -char *chrgs_i[] = { +static char *chrgs_i[] = { "none", "resting", /* recovering */ "charging", /* bulk */ @@ -104,7 +104,7 @@ struct chrgs { typedef struct chrgs chrgs_t; /* power management info, "ups.status", "battery.charger.status" */ -char *pwrmng_i[] = { +static char *pwrmng_i[] = { "backup", /* "OB", "discharging" */ "charging", /* "OL" */ "boost", @@ -176,8 +176,11 @@ typedef struct alrm_ar alrm_ar_t; #define VACA_HIALRM_I 0 /* high alarm */ #define VACA_LOALRM_I 1 /* low alarm */ -/* Onboard temperature alarm */ -#define OBTA_HIALRM 1 /* high alarm */ +/* Onboard temperature alarm value */ +#define OBTA_HIALRM_V 1 /* high alarm */ + +/* Onboard temperature alarm index */ +#define OBTA_HIALRM_I 0 /* high alarm */ /* Device failure alarm masks */ #define DEVF_RCALRM_M 0x0001 /* rectifier failure */ @@ -236,7 +239,7 @@ typedef struct alrm_ar alrm_ar_t; #define BSTA_CNNFLT_I 5 /* connection fault */ /* input mains and shutdown alarms */ -alrm_ar_t mains = { +static alrm_ar_t mains = { 2, { {0, "input voltage not available"}, @@ -245,7 +248,7 @@ alrm_ar_t mains = { }; /* AC input voltage alarms */ -alrm_ar_t vaca = { +static alrm_ar_t vaca = { 2, { {0, "input voltage high alarm"}, @@ -254,7 +257,7 @@ alrm_ar_t vaca = { }; /* device failure alarms */ -alrm_ar_t devf = { +static alrm_ar_t devf = { 3, { {0, "UPS rectifier failure"}, @@ -264,7 +267,7 @@ alrm_ar_t devf = { }; /* battery sensor failure alarms */ -alrm_ar_t btsf = { +static alrm_ar_t btsf = { 2, { {0, "battery temp sensor connection fault"}, @@ -273,7 +276,7 @@ alrm_ar_t btsf = { }; /* battery voltage alarms */ -alrm_ar_t bval = { +static alrm_ar_t bval = { 3, { {0, "battery high voltage"}, @@ -283,7 +286,7 @@ alrm_ar_t bval = { }; /* battery SoH and SoC alarms */ -alrm_ar_t shsc = { +static alrm_ar_t shsc = { 4, { {0, "battery high internal resistance"}, @@ -294,7 +297,7 @@ alrm_ar_t shsc = { }; /* battery status alarm */ -alrm_ar_t bsta = { +static alrm_ar_t bsta = { 6, { {0, "battery reversed polarity"}, @@ -306,6 +309,14 @@ alrm_ar_t bsta = { } }; +/* onboard temperature alarm */ +static alrm_ar_t obta = { + 1, + { + {0, "onboard temperature high"} + } +}; + /* UPS device reg enum */ enum devreg { CHRG = 0, /* Charging status, "battery.charger.status" */ @@ -356,7 +367,7 @@ union devstate { typedef union devstate devstate_t; /* ADELE CBI registers */ -regattr_t regs[] = { +static regattr_t regs[] = { {40005, 0, 0, 1, HOLDING}, /* Charging status */ {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ @@ -373,8 +384,8 @@ regattr_t regs[] = { {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ {40006, 0, 0, 1, HOLDING}, /* Power management - * 0:Backup 1:Charging 2:boost 3:Not charging - */ + * 0:Backup 1:Charging 2:boost 3:Not charging + */ {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ {40010, 0, 0, 1, HOLDING}, /* Software ID */ From 22735f3e84b9fb6dadfe77b44c14939b8ddc1a12 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Wed, 12 Jan 2022 22:44:26 +0200 Subject: [PATCH 036/700] read_all_regs aproach integrated --- drivers/adele_cbi.c | 42 +++++++++++++++++++++++++++++++++++++++--- drivers/adele_cbi.h | 6 ++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index 852629a7de..d255f5dca8 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -49,6 +49,9 @@ static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus by /* initialize register start address and hex address from register number */ void reginit(); +/* read all registers */ +int read_all_regs(modbus_t *mb, uint16_t *data); + /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); @@ -168,7 +171,12 @@ void upsdrv_updateinfo(void) errcnt = 0; /* initialize error counter to zero */ status_init(); /* initialize ups.status update */ alarm_init(); /* initialize ups.alarm update */ - +#if READALL_REGS == 1 + rval = read_all_regs(mbctx, regs_data); + if (rval == -1) { + errcnt++; + } else { +#endif /* * update UPS status regarding MAINS and SHUTDOWN request * - OL: On line (mains is present) @@ -396,6 +404,9 @@ void upsdrv_updateinfo(void) dstate_setinfo("output.current", "%s", ds->reg.strval); upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); } +#if READALL_REGS == 1 + } +#endif /* check for communication errors */ if (errcnt == 0) { alarm_commit(); @@ -516,6 +527,28 @@ void reginit() } } +/* read all register data image */ +int read_all_regs(modbus_t *mb, uint16_t *data) +{ + int rval; + + rval = modbus_read_registers(mb, regs[CHRG].xaddr, MAX_REGS, regs_data); + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", + modbus_strerror(errno), + regs[CHRG].xaddr, + MAX_REGS, + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } +} + /* Read a modbus register */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) { @@ -706,7 +739,10 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) num = regs[regindx].num; addr = regs[regindx].xaddr; rtype = regs[regindx].type; - +#if READALL_REGS == 1 + reg_val = regs_data[regindx]; + rval = 0; +#elif READALL_REGS == 0 rval = register_read(mbctx, addr, rtype, ®_val); if (rval == -1) { return rval; @@ -717,7 +753,7 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) rtype, reg_val ); - +#endif /* process register data */ switch (regindx) { case CHRG: /* "ups.charge" */ diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index b678ff03e5..98c81b55ee 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -51,6 +51,9 @@ /* number of modbus registers */ #define MODBUS_NUMOF_REGS 98 +/* max registers */ +#define MAX_REGS 116 + /* number of device models */ #define DEV_NUMOF_MODELS 10 @@ -366,6 +369,9 @@ union devstate { typedef union devstate devstate_t; +/* device register memory image */ +static uint16_t regs_data[MAX_REGS]; + /* ADELE CBI registers */ static regattr_t regs[] = { {40005, 0, 0, 1, HOLDING}, /* Charging status */ From 6f529d3c8244e20ba8905fcb580f6fbac731da81 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Wed, 12 Jan 2022 23:14:20 +0200 Subject: [PATCH 037/700] some fixes in read_all_regs approach --- drivers/adele_cbi.c | 11 +++++++---- drivers/adele_cbi.h | 3 +++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index d255f5dca8..a670d9fc5c 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -547,6 +547,7 @@ int read_all_regs(modbus_t *mb, uint16_t *data) modbus_reconnect(); } } + return rval; } /* Read a modbus register */ @@ -729,20 +730,22 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) int n; int rval; /* return value */ static char *ptr = NULL; /* temporary pointer */ - uint num; /* register number */ uint reg_val; /* register value */ +#if READALL_REGS == 0 + uint num; /* register number */ regtype_t rtype; /* register type */ int addr; /* register address */ +#endif devstate_t *state; /* device state */ state = *dstate; - num = regs[regindx].num; - addr = regs[regindx].xaddr; - rtype = regs[regindx].type; #if READALL_REGS == 1 reg_val = regs_data[regindx]; rval = 0; #elif READALL_REGS == 0 + num = regs[regindx].num; + addr = regs[regindx].xaddr; + rtype = regs[regindx].type; rval = register_read(mbctx, addr, rtype, ®_val); if (rval == -1) { return rval; diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 98c81b55ee..31f9d7d44e 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -54,6 +54,9 @@ /* max registers */ #define MAX_REGS 116 +/* read all regs */ +#define READALL_REGS 1 + /* number of device models */ #define DEV_NUMOF_MODELS 10 From f33deca13f357dd1d16c3153f93153f909a5ea37 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 00:22:29 +0200 Subject: [PATCH 038/700] modify regs_data memory, devreg enum rearrangement --- drivers/adele_cbi.h | 274 ++++++++++++++++++++++++-------------------- 1 file changed, 149 insertions(+), 125 deletions(-) diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 31f9d7d44e..aa2f5126c2 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -325,29 +325,29 @@ static alrm_ar_t obta = { /* UPS device reg enum */ enum devreg { - CHRG = 0, /* Charging status, "battery.charger.status" */ - BATV, /* Battery voltage, "battery.voltage" */ - BCEF = 6, /* Battery charge efficiency factor (CEF) */ - BSOH, /* Battery state-of-health */ - BSOC = 9, /* Battery state-of-charge, "battery.charge" */ - BTMP = 11, /* Battery temperature in Kelvin units, "battery.temperature" */ - PMNG = 15, /* Power management, "ups.status" */ - OTMP = 20, /* Onboard temperature, "ups.temperature" */ - PRDN, /* Product name, "ups.model" */ - VAC = 24, /* AC voltage, "input.voltage" */ - LVDC, /* Load voltage, "output.voltage" */ - LCUR, /* Load current, "output.current" */ - BINH = 79, /* Backup inhibit */ - FSD, /* Force shutdown */ - TBUF, /* Time buffering, "battery.runtime" */ - BSTA = 89, /* Battery status alarms */ - SCSH, /* SoH and SoC alarms */ - BVAL, /* Battery voltage alarm */ - BTSF, /* Battery temp sensor failure */ - DEVF, /* Device failure */ - OBTA, /* On board temp alarm */ - VACA, /* VAC alarms */ - MAIN /* Mains status */ + CHRG = 4, /* Charging status, "battery.charger.status" */ + BATV = 7, /* Battery voltage, "battery.voltage" */ + BCEF = 18, /* Battery charge efficiency factor (CEF) */ + BSOH = 20, /* Battery state-of-health */ + BSOC = 22, /* Battery state-of-charge, "battery.charge" */ + BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ + PMNG = 5, /* Power management, "ups.status" */ + OTMP = 28, /* Onboard temperature, "ups.temperature" */ + PRDN = 67, /* Product name, "ups.model" */ + VAC = 29, /* AC voltage, "input.voltage" */ + LVDC = 10, /* Load voltage, "output.voltage" */ + LCUR = 19, /* Load current, "output.current" */ + BINH = 87, /* Backup inhibit */ + FSD = 40, /* Force shutdown */ + TBUF = 103, /* Time buffering, "battery.runtime" */ + BSTA = 31, /* Battery status alarms */ + SCSH, /* SoH and SoC alarms */ + BVAL = 34, /* Battery voltage alarm */ + BTSF = 43, /* Battery temp sensor failure */ + DEVF = 42, /* Device failure */ + OBTA = 46, /* On board temp alarm */ + VACA = 44, /* VAC alarms */ + MAIN /* Mains status */ }; typedef enum devreg devreg_t; @@ -377,107 +377,131 @@ static uint16_t regs_data[MAX_REGS]; /* ADELE CBI registers */ static regattr_t regs[] = { - {40005, 0, 0, 1, HOLDING}, /* Charging status */ - {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ - {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ - {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ - {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ - {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ - {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ - {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ - {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ - {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ - {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ - {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ - {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ - {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ - {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ - {40006, 0, 0, 1, HOLDING}, /* Power management - * 0:Backup 1:Charging 2:boost 3:Not charging - */ - {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ - {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ - {40010, 0, 0, 1, HOLDING}, /* Software ID */ - {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ - {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ - {40067, 0, 0, 1, HOLDING}, /* Product name */ - {40039, 0, 0, 1, HOLDING}, /* Device variant */ - {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ - {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ - {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ - {40020, 0, 0, 1, HOLDING}, /* Output load current */ - {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ - {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ - {40050, 0, 0, .1, HOLDING}, /* Ah charged */ - {40051, 0, 0, 1, HOLDING}, /* Total run time */ - {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ - {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ - {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ - {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ - {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ - {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ - {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ - {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ - {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ - {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ - {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ - {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ - {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ - {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ - {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ - {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ - {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ - {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ - {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ - {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ - {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ - {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ - {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ - {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ - {40120, 0, 0, 1, HOLDING}, /* Zero-SoC reference */ - {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ - {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ - {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ - {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ - {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ - {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ - {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ - {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ - {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ - {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ - {40082, 0, 0, 1, HOLDING}, /* Float voltage */ - {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ - {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ - {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ - {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ - {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ - {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ - {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ - {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ - {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ - {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ - {40065, 0, 0, 1, HOLDING}, /* History clear all */ - {40066, 0, 0, 1, HOLDING}, /* Factory settings */ - {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed - * 1 = Backup not allowed 0 - * */ - {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ - {40104, 0, 0, 1, HOLDING}, /* Time buffering */ - {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ - {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ - {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ - {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ - {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ - {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ - {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ - {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ - {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ - {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ - {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ - {40043, 0, 0, 1, HOLDING}, /* Device failure */ - {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ - {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ - {40046, 0, 0, 1, HOLDING}, /* Mains status */ - {40038, 0, 0, 1, HOLDING} /* Load alarm */ + {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ + {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ + {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ + {40004, 0, 0, 1, HOLDING}, + {40005, 0, 0, 1, HOLDING}, /* Charging status */ + {40006, 0, 0, 1, HOLDING}, /* Power management + * 0:Backup 1:Charging 2:boost 3:Not charging + */ + {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ + {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ + {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ + {40010, 0, 0, 1, HOLDING}, /* Software ID */ + {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ + {40012, 0, 0, 1, HOLDING}, + {40013, 0, 0, 1, HOLDING}, + {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ + {40015, 0, 0, 1, HOLDING}, + {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ + {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ + {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ + {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ + {40020, 0, 0, 1, HOLDING}, /* Output load current */ + {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ + {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ + {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ + {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ + {40025, 0, 0, 1, HOLDING}, + {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ + {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ + {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ + {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ + {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ + {40031, 0, 0, 1, HOLDING}, + {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ + {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ + {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ + {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ + {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ + {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ + {40038, 0, 0, 1, HOLDING}, /* Load alarm */ + {40039, 0, 0, 1, HOLDING}, /* Device variant */ + {40040, 0, 0, 1, HOLDING}, + {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ + {40042, 0, 0, 1, HOLDING}, + {40043, 0, 0, 1, HOLDING}, /* Device failure */ + {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ + {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ + {40046, 0, 0, 1, HOLDING}, /* Mains status */ + {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ + {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ + {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ + {40050, 0, 0, .1, HOLDING}, /* Ah charged */ + {40051, 0, 0, 1, HOLDING}, /* Total run time */ + {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ + {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ + {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ + {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ + {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ + {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ + {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ + {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ + {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ + {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ + {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ + {40065, 0, 0, 1, HOLDING}, /* History clear all */ + {40066, 0, 0, 1, HOLDING}, /* Factory settings */ + {40067, 0, 0, 1, HOLDING}, /* Product name */ + {40068, 0, 0, 1, HOLDING}, + {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ + {40070, 0, 0, 1, HOLDING}, + {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ + {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ + {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ + {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ + {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ + {40076, 0, 0, 1, HOLDING}, + {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ + {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ + {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ + {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ + {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ + {40082, 0, 0, 1, HOLDING}, /* Float voltage */ + {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ + {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ + {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ + {40086, 0, 0, 1, HOLDING}, + {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed + * 1 = Backup not allowed + */ + {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ + {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ + {40091, 0, 0, 1, HOLDING}, + {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ + {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ + {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ + {40095, 0, 0, 1, HOLDING}, + {40096, 0, 0, 1, HOLDING}, + {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ + {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ + {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ + {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ + {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ + {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ + {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ + {40104, 0, 0, 1, HOLDING}, /* Time buffering */ + {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ + {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ + {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ + {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ + {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ + {40110, 0, 0, 1, HOLDING}, + {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ + {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ + {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ + {40114, 0, 0, 1, HOLDING}, + {40115, 0, 0, 1, HOLDING}, + {40116, 0, 0, 1, HOLDING}, + {40117, 0, 0, 1, HOLDING}, + {40118, 0, 0, 1, HOLDING}, + {40119, 0, 0, 1, HOLDING}, + {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ + + }; #endif /* ADELE_CBI_H */ From e6adb0138ec5f83a8f53d750d49b18d1e147950d Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 00:39:12 +0200 Subject: [PATCH 039/700] minor bug fixes --- drivers/adele_cbi.c | 2 +- drivers/adele_cbi.h | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index a670d9fc5c..2346d2982b 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -532,7 +532,7 @@ int read_all_regs(modbus_t *mb, uint16_t *data) { int rval; - rval = modbus_read_registers(mb, regs[CHRG].xaddr, MAX_REGS, regs_data); + rval = modbus_read_registers(mb, regs[REG_STARTIDX].xaddr, MAX_REGS, regs_data); if (rval == -1) { upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", modbus_strerror(errno), diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index aa2f5126c2..ba930bbbd7 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -52,7 +52,10 @@ #define MODBUS_NUMOF_REGS 98 /* max registers */ -#define MAX_REGS 116 +#define MAX_REGS 120 + +/* start register number */ +#define REG_STARTIDX 0 /* read all regs */ #define READALL_REGS 1 From bf207d0b763f8aadd603296674efa34fbe2664c3 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 00:55:39 +0200 Subject: [PATCH 040/700] macro name changes --- drivers/adele_cbi.c | 10 +++++++--- drivers/adele_cbi.h | 12 ++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index 2346d2982b..0389eac576 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -532,12 +532,13 @@ int read_all_regs(modbus_t *mb, uint16_t *data) { int rval; - rval = modbus_read_registers(mb, regs[REG_STARTIDX].xaddr, MAX_REGS, regs_data); + /* read all HOLDING registers */ + rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, regs_data); if (rval == -1) { upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", modbus_strerror(errno), - regs[CHRG].xaddr, - MAX_REGS, + regs[H_REG_STARTIDX].xaddr, + MAX_H_REGS, device_path ); @@ -547,6 +548,9 @@ int read_all_regs(modbus_t *mb, uint16_t *data) modbus_reconnect(); } } + + /* no COIL, INPUT_B or INPUT_R register regions to read */ + return rval; } diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index ba930bbbd7..efe0b7ccb5 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -51,13 +51,13 @@ /* number of modbus registers */ #define MODBUS_NUMOF_REGS 98 -/* max registers */ -#define MAX_REGS 120 +/* max HOLDING registers */ +#define MAX_H_REGS 120 -/* start register number */ -#define REG_STARTIDX 0 +/* start HOLDING register index */ +#define H_REG_STARTIDX 0 -/* read all regs */ +/* read all regs preprocessor flag */ #define READALL_REGS 1 /* number of device models */ @@ -376,7 +376,7 @@ union devstate { typedef union devstate devstate_t; /* device register memory image */ -static uint16_t regs_data[MAX_REGS]; +static uint16_t regs_data[MAX_H_REGS]; /* ADELE CBI registers */ static regattr_t regs[] = { From 6ea7018df4a1e18d1db61cacd7a267d31daaff6e Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 01:18:19 +0200 Subject: [PATCH 041/700] adele changed to adelsystems --- drivers/Makefile.am | 8 +- drivers/adelsystems_cbi.c | 1201 +++++++++++++++++++++++++++++++++++++ drivers/adelsystems_cbi.h | 510 ++++++++++++++++ 3 files changed, 1715 insertions(+), 4 deletions(-) create mode 100644 drivers/adelsystems_cbi.c create mode 100644 drivers/adelsystems_cbi.h diff --git a/drivers/Makefile.am b/drivers/Makefile.am index bd8900d700..937ed29ece 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -50,7 +50,7 @@ SERIAL_USB_DRIVERLIST = \ nutdrv_qx NEONXML_DRIVERLIST = netxml-ups MACOSX_DRIVERLIST = macosx-ups -MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 adele_cbi +MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 adelsystems_cbi LINUX_I2C_DRIVERLIST = asem pijuice # distribute all drivers, even ones that are not built by default @@ -267,8 +267,8 @@ phoenixcontact_modbus_SOURCES = phoenixcontact_modbus.c phoenixcontact_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) generic_modbus_SOURCES = generic_modbus.c generic_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) -adele_cbi_SOURCES = adele_cbi.c -adele_cbi_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) +adelsystems_cbi_SOURCES = adelsystems_cbi.c +adelsystems_cbi_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) # Huawei UPS2000 driver # (this is both a Modbus and a serial driver) @@ -325,7 +325,7 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h b xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ apc-pdu-mib.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h \ eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h \ - hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adele_cbi + hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adelsystems_cbi # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, diff --git a/drivers/adelsystems_cbi.c b/drivers/adelsystems_cbi.c new file mode 100644 index 0000000000..8bb283e1f8 --- /dev/null +++ b/drivers/adelsystems_cbi.c @@ -0,0 +1,1201 @@ +/* adele_cbi.c - driver for ADELE CB/CBI DC-UPS + * + * Copyright (C) + * 2022 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "adelsystems_cbi.h" +#include +#include + +#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" +#define DRIVER_VERSION "0.01" + +/* variables */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static devstate_t *dstate = NULL; /* device state context */ +static int errcnt = 0; /* modbus access error counter */ +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static char *device_type = DEVICE_TYPE; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ + + +/* initialize register start address and hex address from register number */ +void reginit(); + +/* read all registers */ +int read_all_regs(modbus_t *mb, uint16_t *data); + +/* get config vars set by -x or defined in ups.conf driver section */ +void get_config_vars(void); + +/* get device state */ +int get_dev_state(devreg_t regindx, devstate_t **dstate); + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port); + +/* reconnect upon communication error */ +void modbus_reconnect(); + +/* modbus register read function */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data); + +/* modbus register write function */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg); + +/* count the time elapsed since start */ +long time_elapsed(struct timeval *start); + + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} +}; + +/* + * driver functions + */ + +/* read configuration variables from ups.conf and connect to ups device */ +void upsdrv_initups(void) +{ + int rval; + upsdebugx(2, "upsdrv_initups"); + + + dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); + reginit(); + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response time out to 200ms */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} + +/* initialize ups driver information */ +void upsdrv_initinfo(void) +{ + devstate_t *ds = dstate; /* device state context */ + upsdebugx(2, "upsdrv_initinfo"); + + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); + dstate_setinfo("device.type", "%s", device_type); + + /* read ups model */ + get_dev_state(PRDN, &ds); + dstate_setinfo("ups.model", "%s", ds->product.name); + upslogx(LOG_INFO, "ups.model = %s", ds->product.name); + + /* register instant commands */ + dstate_addcmd("load.off"); + + /* set callback for instant commands */ + upsh.instcmd = upscmd; +} + + +/* update UPS signal state */ +void upsdrv_updateinfo(void) +{ + int rval; /* return value */ + int i; /* local index */ + devstate_t *ds = dstate; /* device state */ + + upsdebugx(2, "upsdrv_updateinfo"); + + errcnt = 0; /* initialize error counter to zero */ + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ +#if READALL_REGS == 1 + rval = read_all_regs(mbctx, regs_data); + if (rval == -1) { + errcnt++; + } else { +#endif + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ + rval = get_dev_state(MAIN, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains.alrm[MAINS_AVAIL_I].descr); + upslogx(LOG_INFO, "ups.status = OB"); + } else { + status_set("OL"); + upslogx(LOG_INFO, "ups.status = OL"); + } + if (ds->alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains.alrm[SHUTD_REQST_I].descr); + upslogx(LOG_INFO, "ups.status = FSD"); + } + } + + /* + * update UPS status regarding battery voltage + */ + rval = get_dev_state(BVAL, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set(bval.alrm[BVAL_LOALRM_I].descr); + upslogx(LOG_INFO, "ups.status = LB"); + } + if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set(bval.alrm[BVAL_HIALRM_I].descr); + upslogx(LOG_INFO, "ups.status = HB"); + } + if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); + upslogx(LOG_INFO, "battery start with battery flat"); + } + } + + /* get "battery.voltage" */ + rval = get_dev_state(BATV, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); + } + /* + * update UPS status regarding battery charger status + */ + + /* get "battery.charger.status" */ + rval = get_dev_state(CHRG, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { + status_set("CHRG"); + upslogx(LOG_INFO, "ups.status = CHRG"); + } + dstate_setinfo("battery.charger.status", "%s", ds->charge.info); + upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); + } + rval = get_dev_state(PMNG, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + upslogx(LOG_INFO, "ups.status = DISCHRG"); + } + if (ds->power.state == PMNG_BOOST) { + status_set("BOOST"); + upslogx(LOG_INFO, "ups.status = BOOST"); + } + } + + /* + * update UPS battery state of charge + */ + rval = get_dev_state(BSOC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.charge", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); + } + + /* + * update UPS AC input state + */ + rval = get_dev_state(VACA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } + } + rval = get_dev_state(VAC, &ds); + dstate_setinfo("input.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); + } + + /* + * update UPS onboard temperature state + */ + rval = get_dev_state(OBTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(OTMP, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("ups.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); + } + /* + * update UPS battery temperature state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(BTMP, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); + } + rval = get_dev_state(TBUF, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.runtime", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); + } + + /* + * update UPS device failure state + */ + rval = get_dev_state(DEVF, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS SoH and SoC states + */ + rval = get_dev_state(SCSH, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS battery state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS load status + */ + rval = get_dev_state(LVDC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("output.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); + } + rval = get_dev_state(LCUR, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("output.current", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); + } +#if READALL_REGS == 1 + } +#endif + /* check for communication errors */ + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2, "Communication errors: %d", errcnt); + dstate_datastale(); + } +} + +/* shutdown UPS */ +void upsdrv_shutdown(void) +{ + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); +} + +/* print driver usage info */ +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); + addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); + addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); + addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); +} + +/* close modbus connection and free modbus context allocated memory */ +void upsdrv_cleanup(void) +{ + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } + if (dstate != NULL) { + free(dstate); + } +} + +/* + * driver support functions + */ + +/* initialize register start address and hex address from register number */ +void reginit() +{ + int i; /* local index */ + + for (i = 0; i < MODBUS_NUMOF_REGS; i++) { + int rnum = regs[i].num; + switch (regs[i].type) { + case COIL: + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x0 + regs[i].num - 1; + break; + case INPUT_B: + rnum -= 10000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x10000 + rnum - 1; + break; + case INPUT_R: + rnum -= 30000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x30000 + rnum - 1; + break; + case HOLDING: + rnum -= 40000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x40000 + rnum - 1; + break; + default: + upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + } + upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", + regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr + ); + } +} + +/* read all register data image */ +int read_all_regs(modbus_t *mb, uint16_t *data) +{ + int rval; + + /* read all HOLDING registers */ + rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, regs_data); + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", + modbus_strerror(errno), + regs[H_REG_STARTIDX].xaddr, + MAX_H_REGS, + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + + /* no COIL, INPUT_B or INPUT_R register regions to read */ + + return rval; +} + +/* Read a modbus register */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* write a modbus register */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* returns the time elapsed since start in milliseconds */ +long time_elapsed(struct timeval *start) +{ + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; +} + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg) +{ + int rval; + int data; + + if (!strcasecmp(cmd, "load.off")) { + data = 1; + rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + regs[FSD].xaddr, + regs[FSD].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); + rval = STAT_INSTCMD_HANDLED; + } + } else { + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; +} + +/* read device state, returns 0 on success or -1 on communication error + it formats state depending on register semantics */ +int get_dev_state(devreg_t regindx, devstate_t **dstate) +{ + int i; /* local index */ + int n; + int rval; /* return value */ + static char *ptr = NULL; /* temporary pointer */ + uint reg_val; /* register value */ +#if READALL_REGS == 0 + uint num; /* register number */ + regtype_t rtype; /* register type */ + int addr; /* register address */ +#endif + devstate_t *state; /* device state */ + + state = *dstate; +#if READALL_REGS == 1 + reg_val = regs_data[regindx]; + rval = 0; +#elif READALL_REGS == 0 + num = regs[regindx].num; + addr = regs[regindx].xaddr; + rtype = regs[regindx].type; + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval == -1) { + return rval; + } + upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", + num, + addr, + rtype, + reg_val + ); +#endif + /* process register data */ + switch (regindx) { + case CHRG: /* "ups.charge" */ + if (reg_val == CHRG_NONE) { + state->charge.state = CHRG_NONE; + state->charge.info = chrgs_i[CHRG_NONE]; + } else if (reg_val == CHRG_RECV) { + state->charge.state = CHRG_RECV; + state->charge.info = chrgs_i[CHRG_RECV]; + } else if (reg_val == CHRG_BULK) { + state->charge.state = CHRG_BULK; + state->charge.info = chrgs_i[CHRG_BULK]; + } else if (reg_val == CHRG_ABSR) { + state->charge.state = CHRG_ABSR; + state->charge.info = chrgs_i[CHRG_ABSR]; + } else if (reg_val == CHRG_FLOAT) { + state->charge.state = CHRG_FLOAT; + state->charge.info = chrgs_i[CHRG_FLOAT]; + } + upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); + break; + case BATV: /* "battery.voltage" */ + case LVDC: /* "output.voltage" */ + case LCUR: /* "output.current" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0.00"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case TBUF: + case BSOH: + case BCEF: + case VAC: /* "input.voltage" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case BSOC: /* "battery.charge" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = (double )reg_val * regs[BSOC].scale; + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0.00"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case BTMP: /* "battery.temperature" */ + case OTMP: /* "ups.temperature" */ + state->reg.val16 = reg_val; + double fval = reg_val - 273.15; + n = snprintf(NULL, 0, "%.2f", fval); + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + if (ptr != NULL) { + free(ptr); + } + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case PMNG: /* "ups.status" & "battery.charge" */ + if (reg_val == PMNG_BCKUP) { + state->power.state = PMNG_BCKUP; + state->power.info = pwrmng_i[PMNG_BCKUP]; + } else if (reg_val == PMNG_CHRGN) { + state->power.state = PMNG_CHRGN; + state->power.info = pwrmng_i[PMNG_CHRGN]; + } else if (reg_val == PMNG_BOOST) { + state->power.state = PMNG_BOOST; + state->power.info = pwrmng_i[PMNG_BOOST]; + } else if (reg_val == PMNG_NCHRG) { + state->power.state = PMNG_NCHRG; + state->power.info = pwrmng_i[PMNG_NCHRG]; + } + upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); + break; + case PRDN: /* "ups.model" */ + for (i = 0; i < DEV_NUMOF_MODELS; i++) { + if (prdnm_i[i].val == reg_val) { + break; + } + } + state->product.val = reg_val; + state->product.name = prdnm_i[i].name; + upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); + break; + case BSTA: + if (reg_val & BSTA_REVPOL_M) { + bsta.alrm[BSTA_REVPOL_I].actv = 1; + } else { + bsta.alrm[BSTA_REVPOL_I].actv = 0; + } + if (reg_val & BSTA_NOCNND_M) { + bsta.alrm[BSTA_NOCNND_I].actv = 1; + } else { + bsta.alrm[BSTA_NOCNND_I].actv = 0; + } + if (reg_val & BSTA_CLSHCR_M) { + bsta.alrm[BSTA_CLSHCR_I].actv = 1; + } else { + bsta.alrm[BSTA_CLSHCR_I].actv = 0; + } + if (reg_val & BSTA_SULPHD_M) { + bsta.alrm[BSTA_SULPHD_I].actv = 1; + } else { + bsta.alrm[BSTA_SULPHD_I].actv = 0; + } + if (reg_val & BSTA_CHEMNS_M) { + bsta.alrm[BSTA_CHEMNS_I].actv = 1; + } else { + bsta.alrm[BSTA_CHEMNS_I].actv = 0; + } + if (reg_val & BSTA_CNNFLT_M) { + bsta.alrm[BSTA_CNNFLT_I].actv = 1; + } else { + bsta.alrm[BSTA_CNNFLT_I].actv = 0; + } + state->alrm = &bsta; + break; + case SCSH: + if (reg_val & SHSC_HIRESI_M) { + shsc.alrm[SHSC_HIRESI_I].actv = 1; + } else { + shsc.alrm[SHSC_HIRESI_I].actv = 0; + } + if (reg_val & SHSC_LOCHEF_M) { + shsc.alrm[SHSC_LOCHEF_I].actv = 1; + } else { + shsc.alrm[SHSC_LOCHEF_I].actv = 0; + } + if (reg_val & SHSC_LOEFCP_M) { + shsc.alrm[SHSC_LOEFCP_I].actv = 1; + } else { + shsc.alrm[SHSC_LOEFCP_I].actv = 0; + } + if (reg_val & SHSC_LOWSOC_M) { + shsc.alrm[SHSC_LOWSOC_I].actv = 1; + } else { + shsc.alrm[SHSC_LOWSOC_I].actv = 0; + } + state->alrm = &shsc; + break; + case BVAL: + if (reg_val & BVAL_HIALRM_M) { + bval.alrm[BVAL_HIALRM_I].actv = 1; + } else { + bval.alrm[BVAL_HIALRM_I].actv = 0; + } + if (reg_val & BVAL_LOALRM_M) { + bval.alrm[BVAL_LOALRM_I].actv = 1; + } else { + bval.alrm[BVAL_LOALRM_I].actv = 0; + } + if (reg_val & BVAL_BSTSFL_M) { + bval.alrm[BVAL_BSTSFL_I].actv = 1; + } else { + bval.alrm[BVAL_BSTSFL_I].actv = 0; + } + state->alrm = &bval; + break; + case BTSF: + if (reg_val & BTSF_FCND_M) { + btsf.alrm[BTSF_FCND_I].actv = 1; + } else { + btsf.alrm[BTSF_FCND_I].actv = 0; + } + if (reg_val & BTSF_NCND_M) { + btsf.alrm[BTSF_NCND_I].actv = 1; + } else { + btsf.alrm[BTSF_NCND_I].actv = 0; + } + state->alrm = &btsf; + break; + case DEVF: + if (reg_val & DEVF_RCALRM_M) { + devf.alrm[DEVF_RCALRM_I].actv = 1; + } else { + devf.alrm[DEVF_RCALRM_I].actv = 0; + } + if (reg_val & DEVF_INALRM_M) { + devf.alrm[DEVF_INALRM_I].actv = 1; + } else { + devf.alrm[DEVF_INALRM_I].actv = 0; + } + if (reg_val & DEVF_LFNAVL_M) { + devf.alrm[DEVF_LFNAVL_I].actv = 1; + } else { + devf.alrm[DEVF_LFNAVL_I].actv = 0; + } + state->alrm = &devf; + break; + case VACA: + if (reg_val & VACA_HIALRM_M) { + vaca.alrm[VACA_HIALRM_I].actv = 1; + } else { + vaca.alrm[VACA_HIALRM_I].actv = 0; + } + if (reg_val == VACA_LOALRM_M) { + vaca.alrm[VACA_LOALRM_I].actv = 1; + } else { + vaca.alrm[VACA_LOALRM_I].actv = 0; + } + state->alrm = &vaca; + break; + case MAIN: + if (reg_val & MAINS_AVAIL_M) { + mains.alrm[MAINS_AVAIL_I].actv = 1; + } else { + mains.alrm[MAINS_AVAIL_I].actv = 0; + } + if (reg_val == SHUTD_REQST_M) { + mains.alrm[SHUTD_REQST_I].actv = 1; + } else { + mains.alrm[SHUTD_REQST_I].actv = 0; + } + state->alrm = &mains; + break; + case OBTA: + if (reg_val == OBTA_HIALRM_V) { + obta.alrm[OBTA_HIALRM_I].actv = 1; + } + state->alrm = &obta; + break; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + state->reg.val16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + break; + } + + return rval; +} + +/* get driver configuration parameters */ +void get_config_vars() +{ + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); + + /* check if response time out (s) is set ang get the value */ + if (testvar("mod_resp_to_s")) { + mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); + } + upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); + + /* check if response time out (us) is set ang get the value */ + if (testvar("mod_resp_to_us")) { + mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); + if (mod_resp_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } + } + upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); + + /* check if byte time out (s) is set ang get the value */ + if (testvar("mod_byte_to_s")) { + mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); + } + upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); + + /* check if byte time out (us) is set ang get the value */ + if (testvar("mod_byte_to_us")) { + mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); + if (mod_byte_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } + } + upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); +} + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port) +{ + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; +} + +/* reconnect to modbus server upon connection error */ +void modbus_reconnect() +{ + int rval; + + upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); + + /* clear current modbus context */ + modbus_close(mbctx); + modbus_free(mbctx); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} diff --git a/drivers/adelsystems_cbi.h b/drivers/adelsystems_cbi.h new file mode 100644 index 0000000000..b27c2f1e4a --- /dev/null +++ b/drivers/adelsystems_cbi.h @@ -0,0 +1,510 @@ +/* adelsystems_cbi.h - Driver for ADELSYSTEMS CB/CBI DC-UPS + * + * Copyright (C) + * 2022 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ADELSYSTEMS_CBI_H +#define ADELSYSTEMS_CBI_H + +#include + +/* UPS device details */ +#define DEVICE_MFR "ADELSYSTEMS" +#define DEVICE_TYPE "DC-UPS" +#define DEVICE_MODEL "CB/CBI" + +/* serial access parameters */ +#define BAUD_RATE 9600 +#define PARITY 'N' +#define DATA_BIT 8 +#define STOP_BIT 1 + +/* + * modbus response and byte timeouts + * us: 1 - 999999 + */ +#define MODRESP_TIMEOUT_s 0 +#define MODRESP_TIMEOUT_us 200000 +#define MODBYTE_TIMEOUT_s 0 +#define MODBYTE_TIMEOUT_us 50000 + +/* modbus access parameters */ +#define MODBUS_SLAVE_ID 5 + +/* number of modbus registers */ +#define MODBUS_NUMOF_REGS 98 + +/* max HOLDING registers */ +#define MAX_H_REGS 120 + +/* start HOLDING register index */ +#define H_REG_STARTIDX 0 + +/* read all regs preprocessor flag */ +#define READALL_REGS 1 + +/* number of device models */ +#define DEV_NUMOF_MODELS 10 + +/* shutdown repeat on error */ +#define FSD_REPEAT_CNT 3 + +/* shutdown repeat interval in ms */ +#define FSD_REPEAT_INTRV 1500 + +/* definition of register type */ +enum regtype { + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING +}; +typedef enum regtype regtype_t; + +/* product name info, "device.model" */ +struct prodname { + uint16_t val; + char *name; +}; +typedef struct prodname prodname_t; +static prodname_t prdnm_i[] = { + {1, "CBI1235A"}, + {2, "CBI2420A"}, + {3, "CBI4810A"}, + {4, "CBI2801224"}, + {7, "CBI480W"}, + {8, "CB122410A"}, + {9, "CB480W"}, + {11, "CB12245AJ"}, + {12, "CB1235A"}, + {13, "CB2420A"}, + {14, "CB4810A"} +}; + +/* charging status info, "battery.charger.status" */ +static char *chrgs_i[] = { + "none", + "resting", /* recovering */ + "charging", /* bulk */ + "charging", /* absorb */ + "floating" /* float */ +}; +struct chrgs { + int state; + char *info; +}; +typedef struct chrgs chrgs_t; + +/* power management info, "ups.status", "battery.charger.status" */ +static char *pwrmng_i[] = { + "backup", /* "OB", "discharging" */ + "charging", /* "OL" */ + "boost", + "not charging" +}; +struct pwrmng { + int state; + char *info; +}; +typedef struct pwrmng pwrmng_t; + +/* general modbus register value */ +struct reg { + union { + uint16_t val16; + uint8_t val8; + }; + char *strval; +}; +typedef struct reg reg_t; + +/* general alarm struct */ +struct alrm { + int actv; /* active flag */ + char *descr; /* description field */ +}; +typedef struct alrm alrm_t; + +/* general alarm array */ +struct alrm_ar { + int alrm_c; /* alarm count */ + alrm_t alrm[]; /* alarm array */ +}; +typedef struct alrm_ar alrm_ar_t; + +/* + * BIT MASKS and VALUES + */ + +/* Charging status */ +#define CHRG_NONE 0 +#define CHRG_RECV 1 +#define CHRG_BULK 2 +#define CHRG_ABSR 3 +#define CHRG_FLOAT 4 + +/* power management */ +#define PMNG_BCKUP 0 +#define PMNG_CHRGN 1 +#define PMNG_BOOST 2 +#define PMNG_NCHRG 3 + +/* product name */ +#define PRDN_MAX 14 + +/* Mains alarm masks */ +#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_M 0x0002 /* shutdown requested */ + +/* Mains alarm indices */ +#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_I 1 /* shutdown requested */ + +/* AC input voltage alarm masks */ +#define VACA_HIALRM_M 0x0001 /* high alarm */ +#define VACA_LOALRM_M 0x0002 /* low alarm */ + +/* AC input voltage alarm indices */ +#define VACA_HIALRM_I 0 /* high alarm */ +#define VACA_LOALRM_I 1 /* low alarm */ + +/* Onboard temperature alarm value */ +#define OBTA_HIALRM_V 1 /* high alarm */ + +/* Onboard temperature alarm index */ +#define OBTA_HIALRM_I 0 /* high alarm */ + +/* Device failure alarm masks */ +#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ +#define DEVF_INALRM_M 0x0006 /* internal failure */ +#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ + +/* Device failure alarm indices */ +#define DEVF_RCALRM_I 0 /* rectifier failure */ +#define DEVF_INALRM_I 1 /* internal failure */ +#define DEVF_LFNAVL_I 2 /* lifetest not available */ + +/* Battery temp sensor failure alarm masks */ +#define BTSF_FCND_M 0x0001 /* connection fault */ +#define BTSF_NCND_M 0x0002 /* not connected */ + +/* Battery temp sensor failure alarm indices */ +#define BTSF_FCND_I 0 /* connection fault */ +#define BTSF_NCND_I 1 /* not connected */ + +/* Battery voltage alarm masks */ +#define BVAL_HIALRM_M 0x0001 /* high voltage */ +#define BVAL_LOALRM_M 0x0002 /* low voltage */ +#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ + +/* Battery voltage alarm indices */ +#define BVAL_HIALRM_I 0 /* high voltage */ +#define BVAL_LOALRM_I 1 /* low voltage */ +#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ + +/* SoH and SoC alarm masks */ +#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ + +/* SoH and SoC alarm indices */ +#define SHSC_HIRESI_I 0 /* high internal resistance */ +#define SHSC_LOCHEF_I 1 /* low charge efficiency */ +#define SHSC_LOEFCP_I 2 /* low effective capacity */ +#define SHSC_LOWSOC_I 3 /* low state of charge */ + +/* Battery status alarm masks */ +#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ +#define BSTA_NOCNND_M 0x0002 /* not connected */ +#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ +#define BSTA_SULPHD_M 0x0008 /* sulphated */ +#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT_M 0x0020 /* connection fault */ + +/* Battery status alarm indices */ +#define BSTA_REVPOL_I 0 /* reversed polarity */ +#define BSTA_NOCNND_I 1 /* not connected */ +#define BSTA_CLSHCR_I 2 /* cell short circuit */ +#define BSTA_SULPHD_I 3 /* sulphated */ +#define BSTA_CHEMNS_I 4 /* chemistry not supported */ +#define BSTA_CNNFLT_I 5 /* connection fault */ + +/* input mains and shutdown alarms */ +static alrm_ar_t mains = { + 2, + { + {0, "input voltage not available"}, + {0, "ups shutdown requested"} + } +}; + +/* AC input voltage alarms */ +static alrm_ar_t vaca = { + 2, + { + {0, "input voltage high alarm"}, + {0, "input voltage low alarm"} + } +}; + +/* device failure alarms */ +static alrm_ar_t devf = { + 3, + { + {0, "UPS rectifier failure"}, + {0, "UPS internal failure"}, + {0, "UPS lifetest not available"} + } +}; + +/* battery sensor failure alarms */ +static alrm_ar_t btsf = { + 2, + { + {0, "battery temp sensor connection fault"}, + {0, "battery temp sensor not connected"} + } +}; + +/* battery voltage alarms */ +static alrm_ar_t bval = { + 3, + { + {0, "battery high voltage"}, + {0, "battery low voltage"}, + {0, "battery start with battery flat"} + } +}; + +/* battery SoH and SoC alarms */ +static alrm_ar_t shsc = { + 4, + { + {0, "battery high internal resistance"}, + {0, "battery low charge efficiency"}, + {0, "battery low effective capacity"}, + {0, "battery low state of charge"} + } +}; + +/* battery status alarm */ +static alrm_ar_t bsta = { + 6, + { + {0, "battery reversed polarity"}, + {0, "battery not connected"}, + {0, "battery cell short circuit"}, + {0, "battery sulphated"}, + {0, "battery chemistry not supported"}, + {0, "battery connection fault"} + } +}; + +/* onboard temperature alarm */ +static alrm_ar_t obta = { + 1, + { + {0, "onboard temperature high"} + } +}; + +/* UPS device reg enum */ +enum devreg { + CHRG = 4, /* Charging status, "battery.charger.status" */ + BATV = 7, /* Battery voltage, "battery.voltage" */ + BCEF = 18, /* Battery charge efficiency factor (CEF) */ + BSOH = 20, /* Battery state-of-health */ + BSOC = 22, /* Battery state-of-charge, "battery.charge" */ + BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ + PMNG = 5, /* Power management, "ups.status" */ + OTMP = 28, /* Onboard temperature, "ups.temperature" */ + PRDN = 67, /* Product name, "ups.model" */ + VAC = 29, /* AC voltage, "input.voltage" */ + LVDC = 10, /* Load voltage, "output.voltage" */ + LCUR = 19, /* Load current, "output.current" */ + BINH = 87, /* Backup inhibit */ + FSD = 40, /* Force shutdown */ + TBUF = 103, /* Time buffering, "battery.runtime" */ + BSTA = 31, /* Battery status alarms */ + SCSH, /* SoH and SoC alarms */ + BVAL = 34, /* Battery voltage alarm */ + BTSF = 43, /* Battery temp sensor failure */ + DEVF = 42, /* Device failure */ + OBTA = 46, /* On board temp alarm */ + VACA = 44, /* VAC alarms */ + MAIN /* Mains status */ +}; +typedef enum devreg devreg_t; + +/* UPS register attributes */ +struct regattr { + int num; + int saddr; /* register start address */ + int xaddr; /* register hex address */ + float scale; /* scale */ + regtype_t type; /* register type */ +}; +typedef struct regattr regattr_t; + +/* UPS device state info union */ +union devstate { + prodname_t product; /* ups model name */ + chrgs_t charge; /* charging status */ + pwrmng_t power; /* ups status */ + reg_t reg; /* state register*/ + alrm_ar_t *alrm; /* alarm statuses */ +}; + +typedef union devstate devstate_t; + +/* device register memory image */ +static uint16_t regs_data[MAX_H_REGS]; + +/* ADELSYSTEMS CBI registers */ +static regattr_t regs[] = { + {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ + {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ + {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ + {40004, 0, 0, 1, HOLDING}, + {40005, 0, 0, 1, HOLDING}, /* Charging status */ + {40006, 0, 0, 1, HOLDING}, /* Power management + * 0:Backup 1:Charging 2:boost 3:Not charging + */ + {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ + {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ + {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ + {40010, 0, 0, 1, HOLDING}, /* Software ID */ + {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ + {40012, 0, 0, 1, HOLDING}, + {40013, 0, 0, 1, HOLDING}, + {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ + {40015, 0, 0, 1, HOLDING}, + {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ + {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ + {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ + {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ + {40020, 0, 0, 1, HOLDING}, /* Output load current */ + {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ + {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ + {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ + {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ + {40025, 0, 0, 1, HOLDING}, + {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ + {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ + {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ + {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ + {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ + {40031, 0, 0, 1, HOLDING}, + {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ + {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ + {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ + {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ + {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ + {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ + {40038, 0, 0, 1, HOLDING}, /* Load alarm */ + {40039, 0, 0, 1, HOLDING}, /* Device variant */ + {40040, 0, 0, 1, HOLDING}, + {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ + {40042, 0, 0, 1, HOLDING}, + {40043, 0, 0, 1, HOLDING}, /* Device failure */ + {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ + {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ + {40046, 0, 0, 1, HOLDING}, /* Mains status */ + {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ + {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ + {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ + {40050, 0, 0, .1, HOLDING}, /* Ah charged */ + {40051, 0, 0, 1, HOLDING}, /* Total run time */ + {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ + {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ + {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ + {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ + {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ + {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ + {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ + {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ + {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ + {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ + {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ + {40065, 0, 0, 1, HOLDING}, /* History clear all */ + {40066, 0, 0, 1, HOLDING}, /* Factory settings */ + {40067, 0, 0, 1, HOLDING}, /* Product name */ + {40068, 0, 0, 1, HOLDING}, + {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ + {40070, 0, 0, 1, HOLDING}, + {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ + {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ + {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ + {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ + {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ + {40076, 0, 0, 1, HOLDING}, + {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ + {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ + {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ + {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ + {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ + {40082, 0, 0, 1, HOLDING}, /* Float voltage */ + {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ + {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ + {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ + {40086, 0, 0, 1, HOLDING}, + {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed + * 1 = Backup not allowed + */ + {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ + {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ + {40091, 0, 0, 1, HOLDING}, + {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ + {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ + {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ + {40095, 0, 0, 1, HOLDING}, + {40096, 0, 0, 1, HOLDING}, + {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ + {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ + {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ + {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ + {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ + {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ + {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ + {40104, 0, 0, 1, HOLDING}, /* Time buffering */ + {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ + {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ + {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ + {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ + {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ + {40110, 0, 0, 1, HOLDING}, + {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ + {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ + {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ + {40114, 0, 0, 1, HOLDING}, + {40115, 0, 0, 1, HOLDING}, + {40116, 0, 0, 1, HOLDING}, + {40117, 0, 0, 1, HOLDING}, + {40118, 0, 0, 1, HOLDING}, + {40119, 0, 0, 1, HOLDING}, + {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ + + +}; +#endif /* ADELSYSTEMS_CBI_H */ From 654b8bd6b8ec156c21d42a223281d8af240f9350 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 01:49:38 +0200 Subject: [PATCH 042/700] filename fixes --- drivers/Makefile.am | 8 +- drivers/adelsystem_cbi.c | 1201 ++++++++++++++++++++++++++++++++++++++ drivers/adelsystem_cbi.h | 510 ++++++++++++++++ 3 files changed, 1715 insertions(+), 4 deletions(-) create mode 100644 drivers/adelsystem_cbi.c create mode 100644 drivers/adelsystem_cbi.h diff --git a/drivers/Makefile.am b/drivers/Makefile.am index 937ed29ece..952307c513 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -50,7 +50,7 @@ SERIAL_USB_DRIVERLIST = \ nutdrv_qx NEONXML_DRIVERLIST = netxml-ups MACOSX_DRIVERLIST = macosx-ups -MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 adelsystems_cbi +MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 adelsystem_cbi LINUX_I2C_DRIVERLIST = asem pijuice # distribute all drivers, even ones that are not built by default @@ -267,8 +267,8 @@ phoenixcontact_modbus_SOURCES = phoenixcontact_modbus.c phoenixcontact_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) generic_modbus_SOURCES = generic_modbus.c generic_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) -adelsystems_cbi_SOURCES = adelsystems_cbi.c -adelsystems_cbi_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) +adelsystem_cbi_SOURCES = adelsystem_cbi.c +adelsystem_cbi_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) # Huawei UPS2000 driver # (this is both a Modbus and a serial driver) @@ -325,7 +325,7 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h b xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ apc-pdu-mib.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h \ eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h \ - hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adelsystems_cbi + hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adelsystem_cbi # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c new file mode 100644 index 0000000000..7b4804a0d1 --- /dev/null +++ b/drivers/adelsystem_cbi.c @@ -0,0 +1,1201 @@ +/* adelsystem_cbi.c - driver for ADELSYSTEM CB/CBI DC-UPS + * + * Copyright (C) + * 2022 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "adelsystem_cbi.h" +#include +#include + +#define DRIVER_NAME "NUT ADELSYSTEM DC-UPS CB/CBI driver" +#define DRIVER_VERSION "0.01" + +/* variables */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static devstate_t *dstate = NULL; /* device state context */ +static int errcnt = 0; /* modbus access error counter */ +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static char *device_type = DEVICE_TYPE; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ + + +/* initialize register start address and hex address from register number */ +void reginit(); + +/* read registers' memory region */ +int read_all_regs(modbus_t *mb, uint16_t *data); + +/* get config vars set by -x or defined in ups.conf driver section */ +void get_config_vars(void); + +/* get device state */ +int get_dev_state(devreg_t regindx, devstate_t **dstate); + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port); + +/* reconnect upon communication error */ +void modbus_reconnect(); + +/* modbus register read function */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data); + +/* modbus register write function */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg); + +/* count the time elapsed since start */ +long time_elapsed(struct timeval *start); + + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} +}; + +/* + * driver functions + */ + +/* read configuration variables from ups.conf and connect to ups device */ +void upsdrv_initups(void) +{ + int rval; + upsdebugx(2, "upsdrv_initups"); + + + dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); + reginit(); + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response time out to 200ms */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} + +/* initialize ups driver information */ +void upsdrv_initinfo(void) +{ + devstate_t *ds = dstate; /* device state context */ + upsdebugx(2, "upsdrv_initinfo"); + + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); + dstate_setinfo("device.type", "%s", device_type); + + /* read ups model */ + get_dev_state(PRDN, &ds); + dstate_setinfo("ups.model", "%s", ds->product.name); + upslogx(LOG_INFO, "ups.model = %s", ds->product.name); + + /* register instant commands */ + dstate_addcmd("load.off"); + + /* set callback for instant commands */ + upsh.instcmd = upscmd; +} + + +/* update UPS signal state */ +void upsdrv_updateinfo(void) +{ + int rval; /* return value */ + int i; /* local index */ + devstate_t *ds = dstate; /* device state */ + + upsdebugx(2, "upsdrv_updateinfo"); + + errcnt = 0; /* initialize error counter to zero */ + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ +#if READALL_REGS == 1 + rval = read_all_regs(mbctx, regs_data); + if (rval == -1) { + errcnt++; + } else { +#endif + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ + rval = get_dev_state(MAIN, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains.alrm[MAINS_AVAIL_I].descr); + upslogx(LOG_INFO, "ups.status = OB"); + } else { + status_set("OL"); + upslogx(LOG_INFO, "ups.status = OL"); + } + if (ds->alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains.alrm[SHUTD_REQST_I].descr); + upslogx(LOG_INFO, "ups.status = FSD"); + } + } + + /* + * update UPS status regarding battery voltage + */ + rval = get_dev_state(BVAL, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set(bval.alrm[BVAL_LOALRM_I].descr); + upslogx(LOG_INFO, "ups.status = LB"); + } + if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set(bval.alrm[BVAL_HIALRM_I].descr); + upslogx(LOG_INFO, "ups.status = HB"); + } + if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); + upslogx(LOG_INFO, "battery start with battery flat"); + } + } + + /* get "battery.voltage" */ + rval = get_dev_state(BATV, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); + } + /* + * update UPS status regarding battery charger status + */ + + /* get "battery.charger.status" */ + rval = get_dev_state(CHRG, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { + status_set("CHRG"); + upslogx(LOG_INFO, "ups.status = CHRG"); + } + dstate_setinfo("battery.charger.status", "%s", ds->charge.info); + upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); + } + rval = get_dev_state(PMNG, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + upslogx(LOG_INFO, "ups.status = DISCHRG"); + } + if (ds->power.state == PMNG_BOOST) { + status_set("BOOST"); + upslogx(LOG_INFO, "ups.status = BOOST"); + } + } + + /* + * update UPS battery state of charge + */ + rval = get_dev_state(BSOC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.charge", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); + } + + /* + * update UPS AC input state + */ + rval = get_dev_state(VACA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } + } + rval = get_dev_state(VAC, &ds); + dstate_setinfo("input.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); + } + + /* + * update UPS onboard temperature state + */ + rval = get_dev_state(OBTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(OTMP, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("ups.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); + } + /* + * update UPS battery temperature state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(BTMP, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); + } + rval = get_dev_state(TBUF, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.runtime", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); + } + + /* + * update UPS device failure state + */ + rval = get_dev_state(DEVF, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS SoH and SoC states + */ + rval = get_dev_state(SCSH, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS battery state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS load status + */ + rval = get_dev_state(LVDC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("output.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); + } + rval = get_dev_state(LCUR, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("output.current", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); + } +#if READALL_REGS == 1 + } +#endif + /* check for communication errors */ + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2, "Communication errors: %d", errcnt); + dstate_datastale(); + } +} + +/* shutdown UPS */ +void upsdrv_shutdown(void) +{ + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); +} + +/* print driver usage info */ +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); + addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); + addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); + addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); +} + +/* close modbus connection and free modbus context allocated memory */ +void upsdrv_cleanup(void) +{ + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } + if (dstate != NULL) { + free(dstate); + } +} + +/* + * driver support functions + */ + +/* initialize register start address and hex address from register number */ +void reginit() +{ + int i; /* local index */ + + for (i = 0; i < MODBUS_NUMOF_REGS; i++) { + int rnum = regs[i].num; + switch (regs[i].type) { + case COIL: + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x0 + regs[i].num - 1; + break; + case INPUT_B: + rnum -= 10000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x10000 + rnum - 1; + break; + case INPUT_R: + rnum -= 30000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x30000 + rnum - 1; + break; + case HOLDING: + rnum -= 40000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x40000 + rnum - 1; + break; + default: + upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + } + upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", + regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr + ); + } +} + +/* read registers' memory region */ +int read_all_regs(modbus_t *mb, uint16_t *data) +{ + int rval; + + /* read all HOLDING registers */ + rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, regs_data); + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", + modbus_strerror(errno), + regs[H_REG_STARTIDX].xaddr, + MAX_H_REGS, + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + + /* no COIL, INPUT_B or INPUT_R register regions to read */ + + return rval; +} + +/* Read a modbus register */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* write a modbus register */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* returns the time elapsed since start in milliseconds */ +long time_elapsed(struct timeval *start) +{ + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; +} + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg) +{ + int rval; + int data; + + if (!strcasecmp(cmd, "load.off")) { + data = 1; + rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + regs[FSD].xaddr, + regs[FSD].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); + rval = STAT_INSTCMD_HANDLED; + } + } else { + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; +} + +/* read device state, returns 0 on success or -1 on communication error + it formats state depending on register semantics */ +int get_dev_state(devreg_t regindx, devstate_t **dstate) +{ + int i; /* local index */ + int n; + int rval; /* return value */ + static char *ptr = NULL; /* temporary pointer */ + uint reg_val; /* register value */ +#if READALL_REGS == 0 + uint num; /* register number */ + regtype_t rtype; /* register type */ + int addr; /* register address */ +#endif + devstate_t *state; /* device state */ + + state = *dstate; +#if READALL_REGS == 1 + reg_val = regs_data[regindx]; + rval = 0; +#elif READALL_REGS == 0 + num = regs[regindx].num; + addr = regs[regindx].xaddr; + rtype = regs[regindx].type; + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval == -1) { + return rval; + } + upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", + num, + addr, + rtype, + reg_val + ); +#endif + /* process register data */ + switch (regindx) { + case CHRG: /* "ups.charge" */ + if (reg_val == CHRG_NONE) { + state->charge.state = CHRG_NONE; + state->charge.info = chrgs_i[CHRG_NONE]; + } else if (reg_val == CHRG_RECV) { + state->charge.state = CHRG_RECV; + state->charge.info = chrgs_i[CHRG_RECV]; + } else if (reg_val == CHRG_BULK) { + state->charge.state = CHRG_BULK; + state->charge.info = chrgs_i[CHRG_BULK]; + } else if (reg_val == CHRG_ABSR) { + state->charge.state = CHRG_ABSR; + state->charge.info = chrgs_i[CHRG_ABSR]; + } else if (reg_val == CHRG_FLOAT) { + state->charge.state = CHRG_FLOAT; + state->charge.info = chrgs_i[CHRG_FLOAT]; + } + upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); + break; + case BATV: /* "battery.voltage" */ + case LVDC: /* "output.voltage" */ + case LCUR: /* "output.current" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0.00"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case TBUF: + case BSOH: + case BCEF: + case VAC: /* "input.voltage" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case BSOC: /* "battery.charge" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = (double )reg_val * regs[BSOC].scale; + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0.00"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case BTMP: /* "battery.temperature" */ + case OTMP: /* "ups.temperature" */ + state->reg.val16 = reg_val; + double fval = reg_val - 273.15; + n = snprintf(NULL, 0, "%.2f", fval); + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + if (ptr != NULL) { + free(ptr); + } + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case PMNG: /* "ups.status" & "battery.charge" */ + if (reg_val == PMNG_BCKUP) { + state->power.state = PMNG_BCKUP; + state->power.info = pwrmng_i[PMNG_BCKUP]; + } else if (reg_val == PMNG_CHRGN) { + state->power.state = PMNG_CHRGN; + state->power.info = pwrmng_i[PMNG_CHRGN]; + } else if (reg_val == PMNG_BOOST) { + state->power.state = PMNG_BOOST; + state->power.info = pwrmng_i[PMNG_BOOST]; + } else if (reg_val == PMNG_NCHRG) { + state->power.state = PMNG_NCHRG; + state->power.info = pwrmng_i[PMNG_NCHRG]; + } + upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); + break; + case PRDN: /* "ups.model" */ + for (i = 0; i < DEV_NUMOF_MODELS; i++) { + if (prdnm_i[i].val == reg_val) { + break; + } + } + state->product.val = reg_val; + state->product.name = prdnm_i[i].name; + upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); + break; + case BSTA: + if (reg_val & BSTA_REVPOL_M) { + bsta.alrm[BSTA_REVPOL_I].actv = 1; + } else { + bsta.alrm[BSTA_REVPOL_I].actv = 0; + } + if (reg_val & BSTA_NOCNND_M) { + bsta.alrm[BSTA_NOCNND_I].actv = 1; + } else { + bsta.alrm[BSTA_NOCNND_I].actv = 0; + } + if (reg_val & BSTA_CLSHCR_M) { + bsta.alrm[BSTA_CLSHCR_I].actv = 1; + } else { + bsta.alrm[BSTA_CLSHCR_I].actv = 0; + } + if (reg_val & BSTA_SULPHD_M) { + bsta.alrm[BSTA_SULPHD_I].actv = 1; + } else { + bsta.alrm[BSTA_SULPHD_I].actv = 0; + } + if (reg_val & BSTA_CHEMNS_M) { + bsta.alrm[BSTA_CHEMNS_I].actv = 1; + } else { + bsta.alrm[BSTA_CHEMNS_I].actv = 0; + } + if (reg_val & BSTA_CNNFLT_M) { + bsta.alrm[BSTA_CNNFLT_I].actv = 1; + } else { + bsta.alrm[BSTA_CNNFLT_I].actv = 0; + } + state->alrm = &bsta; + break; + case SCSH: + if (reg_val & SHSC_HIRESI_M) { + shsc.alrm[SHSC_HIRESI_I].actv = 1; + } else { + shsc.alrm[SHSC_HIRESI_I].actv = 0; + } + if (reg_val & SHSC_LOCHEF_M) { + shsc.alrm[SHSC_LOCHEF_I].actv = 1; + } else { + shsc.alrm[SHSC_LOCHEF_I].actv = 0; + } + if (reg_val & SHSC_LOEFCP_M) { + shsc.alrm[SHSC_LOEFCP_I].actv = 1; + } else { + shsc.alrm[SHSC_LOEFCP_I].actv = 0; + } + if (reg_val & SHSC_LOWSOC_M) { + shsc.alrm[SHSC_LOWSOC_I].actv = 1; + } else { + shsc.alrm[SHSC_LOWSOC_I].actv = 0; + } + state->alrm = &shsc; + break; + case BVAL: + if (reg_val & BVAL_HIALRM_M) { + bval.alrm[BVAL_HIALRM_I].actv = 1; + } else { + bval.alrm[BVAL_HIALRM_I].actv = 0; + } + if (reg_val & BVAL_LOALRM_M) { + bval.alrm[BVAL_LOALRM_I].actv = 1; + } else { + bval.alrm[BVAL_LOALRM_I].actv = 0; + } + if (reg_val & BVAL_BSTSFL_M) { + bval.alrm[BVAL_BSTSFL_I].actv = 1; + } else { + bval.alrm[BVAL_BSTSFL_I].actv = 0; + } + state->alrm = &bval; + break; + case BTSF: + if (reg_val & BTSF_FCND_M) { + btsf.alrm[BTSF_FCND_I].actv = 1; + } else { + btsf.alrm[BTSF_FCND_I].actv = 0; + } + if (reg_val & BTSF_NCND_M) { + btsf.alrm[BTSF_NCND_I].actv = 1; + } else { + btsf.alrm[BTSF_NCND_I].actv = 0; + } + state->alrm = &btsf; + break; + case DEVF: + if (reg_val & DEVF_RCALRM_M) { + devf.alrm[DEVF_RCALRM_I].actv = 1; + } else { + devf.alrm[DEVF_RCALRM_I].actv = 0; + } + if (reg_val & DEVF_INALRM_M) { + devf.alrm[DEVF_INALRM_I].actv = 1; + } else { + devf.alrm[DEVF_INALRM_I].actv = 0; + } + if (reg_val & DEVF_LFNAVL_M) { + devf.alrm[DEVF_LFNAVL_I].actv = 1; + } else { + devf.alrm[DEVF_LFNAVL_I].actv = 0; + } + state->alrm = &devf; + break; + case VACA: + if (reg_val & VACA_HIALRM_M) { + vaca.alrm[VACA_HIALRM_I].actv = 1; + } else { + vaca.alrm[VACA_HIALRM_I].actv = 0; + } + if (reg_val == VACA_LOALRM_M) { + vaca.alrm[VACA_LOALRM_I].actv = 1; + } else { + vaca.alrm[VACA_LOALRM_I].actv = 0; + } + state->alrm = &vaca; + break; + case MAIN: + if (reg_val & MAINS_AVAIL_M) { + mains.alrm[MAINS_AVAIL_I].actv = 1; + } else { + mains.alrm[MAINS_AVAIL_I].actv = 0; + } + if (reg_val == SHUTD_REQST_M) { + mains.alrm[SHUTD_REQST_I].actv = 1; + } else { + mains.alrm[SHUTD_REQST_I].actv = 0; + } + state->alrm = &mains; + break; + case OBTA: + if (reg_val == OBTA_HIALRM_V) { + obta.alrm[OBTA_HIALRM_I].actv = 1; + } + state->alrm = &obta; + break; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + state->reg.val16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + break; + } + + return rval; +} + +/* get driver configuration parameters */ +void get_config_vars() +{ + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); + + /* check if response time out (s) is set ang get the value */ + if (testvar("mod_resp_to_s")) { + mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); + } + upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); + + /* check if response time out (us) is set ang get the value */ + if (testvar("mod_resp_to_us")) { + mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); + if (mod_resp_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } + } + upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); + + /* check if byte time out (s) is set ang get the value */ + if (testvar("mod_byte_to_s")) { + mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); + } + upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); + + /* check if byte time out (us) is set ang get the value */ + if (testvar("mod_byte_to_us")) { + mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); + if (mod_byte_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } + } + upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); +} + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port) +{ + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; +} + +/* reconnect to modbus server upon connection error */ +void modbus_reconnect() +{ + int rval; + + upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); + + /* clear current modbus context */ + modbus_close(mbctx); + modbus_free(mbctx); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} diff --git a/drivers/adelsystem_cbi.h b/drivers/adelsystem_cbi.h new file mode 100644 index 0000000000..b3500a7afc --- /dev/null +++ b/drivers/adelsystem_cbi.h @@ -0,0 +1,510 @@ +/* adelsystem_cbi.h - Driver for ADELSYSTEM CB/CBI DC-UPS + * + * Copyright (C) + * 2022 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ADELSYSTEM_CBI_H +#define ADELSYSTEM_CBI_H + +#include + +/* UPS device details */ +#define DEVICE_MFR "ADELSYSTEM" +#define DEVICE_TYPE "DC-UPS" +#define DEVICE_MODEL "CB/CBI" + +/* serial access parameters */ +#define BAUD_RATE 9600 +#define PARITY 'N' +#define DATA_BIT 8 +#define STOP_BIT 1 + +/* + * modbus response and byte timeouts + * us: 1 - 999999 + */ +#define MODRESP_TIMEOUT_s 0 +#define MODRESP_TIMEOUT_us 200000 +#define MODBYTE_TIMEOUT_s 0 +#define MODBYTE_TIMEOUT_us 50000 + +/* modbus access parameters */ +#define MODBUS_SLAVE_ID 5 + +/* number of modbus registers */ +#define MODBUS_NUMOF_REGS 98 + +/* max HOLDING registers */ +#define MAX_H_REGS 120 + +/* start HOLDING register index */ +#define H_REG_STARTIDX 0 + +/* read all regs preprocessor flag */ +#define READALL_REGS 1 + +/* number of device models */ +#define DEV_NUMOF_MODELS 10 + +/* shutdown repeat on error */ +#define FSD_REPEAT_CNT 3 + +/* shutdown repeat interval in ms */ +#define FSD_REPEAT_INTRV 1500 + +/* definition of register type */ +enum regtype { + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING +}; +typedef enum regtype regtype_t; + +/* product name info, "device.model" */ +struct prodname { + uint16_t val; + char *name; +}; +typedef struct prodname prodname_t; +static prodname_t prdnm_i[] = { + {1, "CBI1235A"}, + {2, "CBI2420A"}, + {3, "CBI4810A"}, + {4, "CBI2801224"}, + {7, "CBI480W"}, + {8, "CB122410A"}, + {9, "CB480W"}, + {11, "CB12245AJ"}, + {12, "CB1235A"}, + {13, "CB2420A"}, + {14, "CB4810A"} +}; + +/* charging status info, "battery.charger.status" */ +static char *chrgs_i[] = { + "none", + "resting", /* recovering */ + "charging", /* bulk */ + "charging", /* absorb */ + "floating" /* float */ +}; +struct chrgs { + int state; + char *info; +}; +typedef struct chrgs chrgs_t; + +/* power management info, "ups.status", "battery.charger.status" */ +static char *pwrmng_i[] = { + "backup", /* "OB", "discharging" */ + "charging", /* "OL" */ + "boost", + "not charging" +}; +struct pwrmng { + int state; + char *info; +}; +typedef struct pwrmng pwrmng_t; + +/* general modbus register value */ +struct reg { + union { + uint16_t val16; + uint8_t val8; + }; + char *strval; +}; +typedef struct reg reg_t; + +/* general alarm struct */ +struct alrm { + int actv; /* active flag */ + char *descr; /* description field */ +}; +typedef struct alrm alrm_t; + +/* general alarm array */ +struct alrm_ar { + int alrm_c; /* alarm count */ + alrm_t alrm[]; /* alarm array */ +}; +typedef struct alrm_ar alrm_ar_t; + +/* + * BIT MASKS and VALUES + */ + +/* Charging status */ +#define CHRG_NONE 0 +#define CHRG_RECV 1 +#define CHRG_BULK 2 +#define CHRG_ABSR 3 +#define CHRG_FLOAT 4 + +/* power management */ +#define PMNG_BCKUP 0 +#define PMNG_CHRGN 1 +#define PMNG_BOOST 2 +#define PMNG_NCHRG 3 + +/* product name */ +#define PRDN_MAX 14 + +/* Mains alarm masks */ +#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_M 0x0002 /* shutdown requested */ + +/* Mains alarm indices */ +#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_I 1 /* shutdown requested */ + +/* AC input voltage alarm masks */ +#define VACA_HIALRM_M 0x0001 /* high alarm */ +#define VACA_LOALRM_M 0x0002 /* low alarm */ + +/* AC input voltage alarm indices */ +#define VACA_HIALRM_I 0 /* high alarm */ +#define VACA_LOALRM_I 1 /* low alarm */ + +/* Onboard temperature alarm value */ +#define OBTA_HIALRM_V 1 /* high alarm */ + +/* Onboard temperature alarm index */ +#define OBTA_HIALRM_I 0 /* high alarm */ + +/* Device failure alarm masks */ +#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ +#define DEVF_INALRM_M 0x0006 /* internal failure */ +#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ + +/* Device failure alarm indices */ +#define DEVF_RCALRM_I 0 /* rectifier failure */ +#define DEVF_INALRM_I 1 /* internal failure */ +#define DEVF_LFNAVL_I 2 /* lifetest not available */ + +/* Battery temp sensor failure alarm masks */ +#define BTSF_FCND_M 0x0001 /* connection fault */ +#define BTSF_NCND_M 0x0002 /* not connected */ + +/* Battery temp sensor failure alarm indices */ +#define BTSF_FCND_I 0 /* connection fault */ +#define BTSF_NCND_I 1 /* not connected */ + +/* Battery voltage alarm masks */ +#define BVAL_HIALRM_M 0x0001 /* high voltage */ +#define BVAL_LOALRM_M 0x0002 /* low voltage */ +#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ + +/* Battery voltage alarm indices */ +#define BVAL_HIALRM_I 0 /* high voltage */ +#define BVAL_LOALRM_I 1 /* low voltage */ +#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ + +/* SoH and SoC alarm masks */ +#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ + +/* SoH and SoC alarm indices */ +#define SHSC_HIRESI_I 0 /* high internal resistance */ +#define SHSC_LOCHEF_I 1 /* low charge efficiency */ +#define SHSC_LOEFCP_I 2 /* low effective capacity */ +#define SHSC_LOWSOC_I 3 /* low state of charge */ + +/* Battery status alarm masks */ +#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ +#define BSTA_NOCNND_M 0x0002 /* not connected */ +#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ +#define BSTA_SULPHD_M 0x0008 /* sulphated */ +#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT_M 0x0020 /* connection fault */ + +/* Battery status alarm indices */ +#define BSTA_REVPOL_I 0 /* reversed polarity */ +#define BSTA_NOCNND_I 1 /* not connected */ +#define BSTA_CLSHCR_I 2 /* cell short circuit */ +#define BSTA_SULPHD_I 3 /* sulphated */ +#define BSTA_CHEMNS_I 4 /* chemistry not supported */ +#define BSTA_CNNFLT_I 5 /* connection fault */ + +/* input mains and shutdown alarms */ +static alrm_ar_t mains = { + 2, + { + {0, "input voltage not available"}, + {0, "ups shutdown requested"} + } +}; + +/* AC input voltage alarms */ +static alrm_ar_t vaca = { + 2, + { + {0, "input voltage high alarm"}, + {0, "input voltage low alarm"} + } +}; + +/* device failure alarms */ +static alrm_ar_t devf = { + 3, + { + {0, "UPS rectifier failure"}, + {0, "UPS internal failure"}, + {0, "UPS lifetest not available"} + } +}; + +/* battery sensor failure alarms */ +static alrm_ar_t btsf = { + 2, + { + {0, "battery temp sensor connection fault"}, + {0, "battery temp sensor not connected"} + } +}; + +/* battery voltage alarms */ +static alrm_ar_t bval = { + 3, + { + {0, "battery high voltage"}, + {0, "battery low voltage"}, + {0, "battery start with battery flat"} + } +}; + +/* battery SoH and SoC alarms */ +static alrm_ar_t shsc = { + 4, + { + {0, "battery high internal resistance"}, + {0, "battery low charge efficiency"}, + {0, "battery low effective capacity"}, + {0, "battery low state of charge"} + } +}; + +/* battery status alarm */ +static alrm_ar_t bsta = { + 6, + { + {0, "battery reversed polarity"}, + {0, "battery not connected"}, + {0, "battery cell short circuit"}, + {0, "battery sulphated"}, + {0, "battery chemistry not supported"}, + {0, "battery connection fault"} + } +}; + +/* onboard temperature alarm */ +static alrm_ar_t obta = { + 1, + { + {0, "onboard temperature high"} + } +}; + +/* UPS device reg enum */ +enum devreg { + CHRG = 4, /* Charging status, "battery.charger.status" */ + BATV = 7, /* Battery voltage, "battery.voltage" */ + BCEF = 18, /* Battery charge efficiency factor (CEF) */ + BSOH = 20, /* Battery state-of-health */ + BSOC = 22, /* Battery state-of-charge, "battery.charge" */ + BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ + PMNG = 5, /* Power management, "ups.status" */ + OTMP = 28, /* Onboard temperature, "ups.temperature" */ + PRDN = 67, /* Product name, "ups.model" */ + VAC = 29, /* AC voltage, "input.voltage" */ + LVDC = 10, /* Load voltage, "output.voltage" */ + LCUR = 19, /* Load current, "output.current" */ + BINH = 87, /* Backup inhibit */ + FSD = 40, /* Force shutdown */ + TBUF = 103, /* Time buffering, "battery.runtime" */ + BSTA = 31, /* Battery status alarms */ + SCSH, /* SoH and SoC alarms */ + BVAL = 34, /* Battery voltage alarm */ + BTSF = 43, /* Battery temp sensor failure */ + DEVF = 42, /* Device failure */ + OBTA = 46, /* On board temp alarm */ + VACA = 44, /* VAC alarms */ + MAIN /* Mains status */ +}; +typedef enum devreg devreg_t; + +/* UPS register attributes */ +struct regattr { + int num; + int saddr; /* register start address */ + int xaddr; /* register hex address */ + float scale; /* scale */ + regtype_t type; /* register type */ +}; +typedef struct regattr regattr_t; + +/* UPS device state info union */ +union devstate { + prodname_t product; /* ups model name */ + chrgs_t charge; /* charging status */ + pwrmng_t power; /* ups status */ + reg_t reg; /* state register*/ + alrm_ar_t *alrm; /* alarm statuses */ +}; + +typedef union devstate devstate_t; + +/* device register memory image */ +static uint16_t regs_data[MAX_H_REGS]; + +/* ADELSYSTEM CBI registers */ +static regattr_t regs[] = { + {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ + {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ + {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ + {40004, 0, 0, 1, HOLDING}, + {40005, 0, 0, 1, HOLDING}, /* Charging status */ + {40006, 0, 0, 1, HOLDING}, /* Power management + * 0:Backup 1:Charging 2:boost 3:Not charging + */ + {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ + {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ + {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ + {40010, 0, 0, 1, HOLDING}, /* Software ID */ + {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ + {40012, 0, 0, 1, HOLDING}, + {40013, 0, 0, 1, HOLDING}, + {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ + {40015, 0, 0, 1, HOLDING}, + {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ + {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ + {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ + {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ + {40020, 0, 0, 1, HOLDING}, /* Output load current */ + {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ + {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ + {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ + {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ + {40025, 0, 0, 1, HOLDING}, + {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ + {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ + {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ + {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ + {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ + {40031, 0, 0, 1, HOLDING}, + {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ + {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ + {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ + {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ + {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ + {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ + {40038, 0, 0, 1, HOLDING}, /* Load alarm */ + {40039, 0, 0, 1, HOLDING}, /* Device variant */ + {40040, 0, 0, 1, HOLDING}, + {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ + {40042, 0, 0, 1, HOLDING}, + {40043, 0, 0, 1, HOLDING}, /* Device failure */ + {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ + {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ + {40046, 0, 0, 1, HOLDING}, /* Mains status */ + {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ + {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ + {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ + {40050, 0, 0, .1, HOLDING}, /* Ah charged */ + {40051, 0, 0, 1, HOLDING}, /* Total run time */ + {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ + {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ + {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ + {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ + {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ + {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ + {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ + {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ + {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ + {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ + {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ + {40065, 0, 0, 1, HOLDING}, /* History clear all */ + {40066, 0, 0, 1, HOLDING}, /* Factory settings */ + {40067, 0, 0, 1, HOLDING}, /* Product name */ + {40068, 0, 0, 1, HOLDING}, + {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ + {40070, 0, 0, 1, HOLDING}, + {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ + {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ + {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ + {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ + {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ + {40076, 0, 0, 1, HOLDING}, + {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ + {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ + {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ + {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ + {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ + {40082, 0, 0, 1, HOLDING}, /* Float voltage */ + {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ + {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ + {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ + {40086, 0, 0, 1, HOLDING}, + {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed + * 1 = Backup not allowed + */ + {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ + {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ + {40091, 0, 0, 1, HOLDING}, + {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ + {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ + {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ + {40095, 0, 0, 1, HOLDING}, + {40096, 0, 0, 1, HOLDING}, + {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ + {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ + {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ + {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ + {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ + {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ + {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ + {40104, 0, 0, 1, HOLDING}, /* Time buffering */ + {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ + {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ + {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ + {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ + {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ + {40110, 0, 0, 1, HOLDING}, + {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ + {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ + {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ + {40114, 0, 0, 1, HOLDING}, + {40115, 0, 0, 1, HOLDING}, + {40116, 0, 0, 1, HOLDING}, + {40117, 0, 0, 1, HOLDING}, + {40118, 0, 0, 1, HOLDING}, + {40119, 0, 0, 1, HOLDING}, + {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ + + +}; +#endif /* ADELSYSTEM_CBI_H */ From 89107146db55eb50531d173006f55e71212b97d2 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 11:42:24 +0200 Subject: [PATCH 043/700] delete renamed driver files --- drivers/adele_cbi.c | 1201 ------------------------------------- drivers/adele_cbi.h | 510 ---------------- drivers/adelsystems_cbi.c | 1201 ------------------------------------- drivers/adelsystems_cbi.h | 510 ---------------- 4 files changed, 3422 deletions(-) delete mode 100644 drivers/adele_cbi.c delete mode 100644 drivers/adele_cbi.h delete mode 100644 drivers/adelsystems_cbi.c delete mode 100644 drivers/adelsystems_cbi.h diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c deleted file mode 100644 index 0389eac576..0000000000 --- a/drivers/adele_cbi.c +++ /dev/null @@ -1,1201 +0,0 @@ -/* adele_cbi.c - driver for ADELE CB/CBI DC-UPS - * - * Copyright (C) - * 2022 Dimitris Economou - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "main.h" -#include "adele_cbi.h" -#include -#include - -#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" -#define DRIVER_VERSION "0.01" - -/* variables */ -static modbus_t *mbctx = NULL; /* modbus memory context */ -static devstate_t *dstate = NULL; /* device state context */ -static int errcnt = 0; /* modbus access error counter */ -static char *device_mfr = DEVICE_MFR; /* device manufacturer */ -static char *device_model = DEVICE_MODEL; /* device model */ -static char *device_type = DEVICE_TYPE; /* device model */ -static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ -static char ser_parity = PARITY; /* serial port parity */ -static int ser_data_bit = DATA_BIT; /* serial port data bit */ -static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ -static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ -static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ -static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ -static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ -static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ - - -/* initialize register start address and hex address from register number */ -void reginit(); - -/* read all registers */ -int read_all_regs(modbus_t *mb, uint16_t *data); - -/* get config vars set by -x or defined in ups.conf driver section */ -void get_config_vars(void); - -/* get device state */ -int get_dev_state(devreg_t regindx, devstate_t **dstate); - -/* create a new modbus context based on connection type (serial or TCP) */ -modbus_t *modbus_new(const char *port); - -/* reconnect upon communication error */ -void modbus_reconnect(); - -/* modbus register read function */ -int register_read(modbus_t *mb, int addr, regtype_t type, void *data); - -/* modbus register write function */ -int register_write(modbus_t *mb, int addr, regtype_t type, void *data); - -/* instant command triggered by upsd */ -int upscmd(const char *cmd, const char *arg); - -/* count the time elapsed since start */ -long time_elapsed(struct timeval *start); - - -/* driver description structure */ -upsdrv_info_t upsdrv_info = { - DRIVER_NAME, - DRIVER_VERSION, - "Dimitris Economou \n", - DRV_BETA, - {NULL} -}; - -/* - * driver functions - */ - -/* read configuration variables from ups.conf and connect to ups device */ -void upsdrv_initups(void) -{ - int rval; - upsdebugx(2, "upsdrv_initups"); - - - dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); - reginit(); - get_config_vars(); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response time out to 200ms */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -} - -/* initialize ups driver information */ -void upsdrv_initinfo(void) -{ - devstate_t *ds = dstate; /* device state context */ - upsdebugx(2, "upsdrv_initinfo"); - - /* set device information */ - dstate_setinfo("device.mfr", "%s", device_mfr); - dstate_setinfo("device.model", "%s", device_model); - dstate_setinfo("device.type", "%s", device_type); - - /* read ups model */ - get_dev_state(PRDN, &ds); - dstate_setinfo("ups.model", "%s", ds->product.name); - upslogx(LOG_INFO, "ups.model = %s", ds->product.name); - - /* register instant commands */ - dstate_addcmd("load.off"); - - /* set callback for instant commands */ - upsh.instcmd = upscmd; -} - - -/* update UPS signal state */ -void upsdrv_updateinfo(void) -{ - int rval; /* return value */ - int i; /* local index */ - devstate_t *ds = dstate; /* device state */ - - upsdebugx(2, "upsdrv_updateinfo"); - - errcnt = 0; /* initialize error counter to zero */ - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ -#if READALL_REGS == 1 - rval = read_all_regs(mbctx, regs_data); - if (rval == -1) { - errcnt++; - } else { -#endif - /* - * update UPS status regarding MAINS and SHUTDOWN request - * - OL: On line (mains is present) - * - OB: On battery (mains is not present) - */ - rval = get_dev_state(MAIN, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { - status_set("OB"); - alarm_set(mains.alrm[MAINS_AVAIL_I].descr); - upslogx(LOG_INFO, "ups.status = OB"); - } else { - status_set("OL"); - upslogx(LOG_INFO, "ups.status = OL"); - } - if (ds->alrm->alrm[SHUTD_REQST_I].actv) { - status_set("FSD"); - alarm_set(mains.alrm[SHUTD_REQST_I].descr); - upslogx(LOG_INFO, "ups.status = FSD"); - } - } - - /* - * update UPS status regarding battery voltage - */ - rval = get_dev_state(BVAL, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { - status_set("LB"); - alarm_set(bval.alrm[BVAL_LOALRM_I].descr); - upslogx(LOG_INFO, "ups.status = LB"); - } - if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { - status_set("HB"); - alarm_set(bval.alrm[BVAL_HIALRM_I].descr); - upslogx(LOG_INFO, "ups.status = HB"); - } - if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { - alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); - upslogx(LOG_INFO, "battery start with battery flat"); - } - } - - /* get "battery.voltage" */ - rval = get_dev_state(BATV, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); - } - /* - * update UPS status regarding battery charger status - */ - - /* get "battery.charger.status" */ - rval = get_dev_state(CHRG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { - status_set("CHRG"); - upslogx(LOG_INFO, "ups.status = CHRG"); - } - dstate_setinfo("battery.charger.status", "%s", ds->charge.info); - upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); - } - rval = get_dev_state(PMNG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->power.state == PMNG_BCKUP) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - upslogx(LOG_INFO, "ups.status = DISCHRG"); - } - if (ds->power.state == PMNG_BOOST) { - status_set("BOOST"); - upslogx(LOG_INFO, "ups.status = BOOST"); - } - } - - /* - * update UPS battery state of charge - */ - rval = get_dev_state(BSOC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.charge", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); - } - - /* - * update UPS AC input state - */ - rval = get_dev_state(VACA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - rval = get_dev_state(VAC, &ds); - dstate_setinfo("input.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); - } - - /* - * update UPS onboard temperature state - */ - rval = get_dev_state(OBTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(OTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("ups.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); - } - /* - * update UPS battery temperature state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(BTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); - } - rval = get_dev_state(TBUF, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.runtime", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); - } - - /* - * update UPS device failure state - */ - rval = get_dev_state(DEVF, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS SoH and SoC states - */ - rval = get_dev_state(SCSH, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS battery state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS load status - */ - rval = get_dev_state(LVDC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); - } - rval = get_dev_state(LCUR, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.current", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); - } -#if READALL_REGS == 1 - } -#endif - /* check for communication errors */ - if (errcnt == 0) { - alarm_commit(); - status_commit(); - dstate_dataok(); - } else { - upsdebugx(2, "Communication errors: %d", errcnt); - dstate_datastale(); - } -} - -/* shutdown UPS */ -void upsdrv_shutdown(void) -{ - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); - case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); -} - -/* print driver usage info */ -void upsdrv_help(void) -{ -} - -/* list flags and values that you want to receive via -x */ -void upsdrv_makevartable(void) -{ - addvar(VAR_VALUE, "device_mfr", "device manufacturer"); - addvar(VAR_VALUE, "device_model", "device model"); - addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); - addvar(VAR_VALUE, "ser_parity", "serial port parity"); - addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); - addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); - addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); - addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); - addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); - addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); - addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); -} - -/* close modbus connection and free modbus context allocated memory */ -void upsdrv_cleanup(void) -{ - if (mbctx != NULL) { - modbus_close(mbctx); - modbus_free(mbctx); - } - if (dstate != NULL) { - free(dstate); - } -} - -/* - * driver support functions - */ - -/* initialize register start address and hex address from register number */ -void reginit() -{ - int i; /* local index */ - - for (i = 0; i < MODBUS_NUMOF_REGS; i++) { - int rnum = regs[i].num; - switch (regs[i].type) { - case COIL: - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x0 + regs[i].num - 1; - break; - case INPUT_B: - rnum -= 10000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x10000 + rnum - 1; - break; - case INPUT_R: - rnum -= 30000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x30000 + rnum - 1; - break; - case HOLDING: - rnum -= 40000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x40000 + rnum - 1; - break; - default: - upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); - upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); - } - upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", - regs[i].num, - regs[i].type, - regs[i].saddr, - regs[i].xaddr - ); - } -} - -/* read all register data image */ -int read_all_regs(modbus_t *mb, uint16_t *data) -{ - int rval; - - /* read all HOLDING registers */ - rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, regs_data); - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", - modbus_strerror(errno), - regs[H_REG_STARTIDX].xaddr, - MAX_H_REGS, - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - - /* no COIL, INPUT_B or INPUT_R register regions to read */ - - return rval; -} - -/* Read a modbus register */ -int register_read(modbus_t *mb, int addr, regtype_t type, void *data) -{ - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x00FF; - uint mask16 = 0xFFFF; - - switch (type) { - case COIL: - rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_B: - rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_R: - rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - case HOLDING: - rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; -} - -/* write a modbus register */ -int register_write(modbus_t *mb, int addr, regtype_t type, void *data) -{ - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x00FF; - uint mask16 = 0xFFFF; - - switch (type) { - case COIL: - *(uint *)data = *(uint *)data & mask8; - rval = modbus_write_bit(mb, addr, *(uint8_t *)data); - break; - case HOLDING: - *(uint *)data = *(uint *)data & mask16; - rval = modbus_write_register(mb, addr, *(uint16_t *)data); - break; - - case INPUT_B: - case INPUT_R: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; -} - -/* returns the time elapsed since start in milliseconds */ -long time_elapsed(struct timeval *start) -{ - long rval; - struct timeval end; - - rval = gettimeofday(&end, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); - } - if (start->tv_usec < end.tv_usec) { - suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; - end.tv_usec -= 1000000 * nsec; - end.tv_sec += nsec; - } - if (start->tv_usec - end.tv_usec > 1000000) { - suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; - end.tv_usec += 1000000 * nsec; - end.tv_sec -= nsec; - } - rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; - - return rval; -} - -/* instant command triggered by upsd */ -int upscmd(const char *cmd, const char *arg) -{ - int rval; - int data; - - if (!strcasecmp(cmd, "load.off")) { - data = 1; - rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); - if (rval == -1) { - upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - regs[FSD].xaddr, - regs[FSD].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); - rval = STAT_INSTCMD_HANDLED; - } - } else { - upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_UNKNOWN; - } - return rval; -} - -/* read device state, returns 0 on success or -1 on communication error - it formats state depending on register semantics */ -int get_dev_state(devreg_t regindx, devstate_t **dstate) -{ - int i; /* local index */ - int n; - int rval; /* return value */ - static char *ptr = NULL; /* temporary pointer */ - uint reg_val; /* register value */ -#if READALL_REGS == 0 - uint num; /* register number */ - regtype_t rtype; /* register type */ - int addr; /* register address */ -#endif - devstate_t *state; /* device state */ - - state = *dstate; -#if READALL_REGS == 1 - reg_val = regs_data[regindx]; - rval = 0; -#elif READALL_REGS == 0 - num = regs[regindx].num; - addr = regs[regindx].xaddr; - rtype = regs[regindx].type; - rval = register_read(mbctx, addr, rtype, ®_val); - if (rval == -1) { - return rval; - } - upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", - num, - addr, - rtype, - reg_val - ); -#endif - /* process register data */ - switch (regindx) { - case CHRG: /* "ups.charge" */ - if (reg_val == CHRG_NONE) { - state->charge.state = CHRG_NONE; - state->charge.info = chrgs_i[CHRG_NONE]; - } else if (reg_val == CHRG_RECV) { - state->charge.state = CHRG_RECV; - state->charge.info = chrgs_i[CHRG_RECV]; - } else if (reg_val == CHRG_BULK) { - state->charge.state = CHRG_BULK; - state->charge.info = chrgs_i[CHRG_BULK]; - } else if (reg_val == CHRG_ABSR) { - state->charge.state = CHRG_ABSR; - state->charge.info = chrgs_i[CHRG_ABSR]; - } else if (reg_val == CHRG_FLOAT) { - state->charge.state = CHRG_FLOAT; - state->charge.info = chrgs_i[CHRG_FLOAT]; - } - upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); - break; - case BATV: /* "battery.voltage" */ - case LVDC: /* "output.voltage" */ - case LCUR: /* "output.current" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case TBUF: - case BSOH: - case BCEF: - case VAC: /* "input.voltage" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BSOC: /* "battery.charge" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - double fval = (double )reg_val * regs[BSOC].scale; - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BTMP: /* "battery.temperature" */ - case OTMP: /* "ups.temperature" */ - state->reg.val16 = reg_val; - double fval = reg_val - 273.15; - n = snprintf(NULL, 0, "%.2f", fval); - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - if (ptr != NULL) { - free(ptr); - } - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case PMNG: /* "ups.status" & "battery.charge" */ - if (reg_val == PMNG_BCKUP) { - state->power.state = PMNG_BCKUP; - state->power.info = pwrmng_i[PMNG_BCKUP]; - } else if (reg_val == PMNG_CHRGN) { - state->power.state = PMNG_CHRGN; - state->power.info = pwrmng_i[PMNG_CHRGN]; - } else if (reg_val == PMNG_BOOST) { - state->power.state = PMNG_BOOST; - state->power.info = pwrmng_i[PMNG_BOOST]; - } else if (reg_val == PMNG_NCHRG) { - state->power.state = PMNG_NCHRG; - state->power.info = pwrmng_i[PMNG_NCHRG]; - } - upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); - break; - case PRDN: /* "ups.model" */ - for (i = 0; i < DEV_NUMOF_MODELS; i++) { - if (prdnm_i[i].val == reg_val) { - break; - } - } - state->product.val = reg_val; - state->product.name = prdnm_i[i].name; - upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); - break; - case BSTA: - if (reg_val & BSTA_REVPOL_M) { - bsta.alrm[BSTA_REVPOL_I].actv = 1; - } else { - bsta.alrm[BSTA_REVPOL_I].actv = 0; - } - if (reg_val & BSTA_NOCNND_M) { - bsta.alrm[BSTA_NOCNND_I].actv = 1; - } else { - bsta.alrm[BSTA_NOCNND_I].actv = 0; - } - if (reg_val & BSTA_CLSHCR_M) { - bsta.alrm[BSTA_CLSHCR_I].actv = 1; - } else { - bsta.alrm[BSTA_CLSHCR_I].actv = 0; - } - if (reg_val & BSTA_SULPHD_M) { - bsta.alrm[BSTA_SULPHD_I].actv = 1; - } else { - bsta.alrm[BSTA_SULPHD_I].actv = 0; - } - if (reg_val & BSTA_CHEMNS_M) { - bsta.alrm[BSTA_CHEMNS_I].actv = 1; - } else { - bsta.alrm[BSTA_CHEMNS_I].actv = 0; - } - if (reg_val & BSTA_CNNFLT_M) { - bsta.alrm[BSTA_CNNFLT_I].actv = 1; - } else { - bsta.alrm[BSTA_CNNFLT_I].actv = 0; - } - state->alrm = &bsta; - break; - case SCSH: - if (reg_val & SHSC_HIRESI_M) { - shsc.alrm[SHSC_HIRESI_I].actv = 1; - } else { - shsc.alrm[SHSC_HIRESI_I].actv = 0; - } - if (reg_val & SHSC_LOCHEF_M) { - shsc.alrm[SHSC_LOCHEF_I].actv = 1; - } else { - shsc.alrm[SHSC_LOCHEF_I].actv = 0; - } - if (reg_val & SHSC_LOEFCP_M) { - shsc.alrm[SHSC_LOEFCP_I].actv = 1; - } else { - shsc.alrm[SHSC_LOEFCP_I].actv = 0; - } - if (reg_val & SHSC_LOWSOC_M) { - shsc.alrm[SHSC_LOWSOC_I].actv = 1; - } else { - shsc.alrm[SHSC_LOWSOC_I].actv = 0; - } - state->alrm = &shsc; - break; - case BVAL: - if (reg_val & BVAL_HIALRM_M) { - bval.alrm[BVAL_HIALRM_I].actv = 1; - } else { - bval.alrm[BVAL_HIALRM_I].actv = 0; - } - if (reg_val & BVAL_LOALRM_M) { - bval.alrm[BVAL_LOALRM_I].actv = 1; - } else { - bval.alrm[BVAL_LOALRM_I].actv = 0; - } - if (reg_val & BVAL_BSTSFL_M) { - bval.alrm[BVAL_BSTSFL_I].actv = 1; - } else { - bval.alrm[BVAL_BSTSFL_I].actv = 0; - } - state->alrm = &bval; - break; - case BTSF: - if (reg_val & BTSF_FCND_M) { - btsf.alrm[BTSF_FCND_I].actv = 1; - } else { - btsf.alrm[BTSF_FCND_I].actv = 0; - } - if (reg_val & BTSF_NCND_M) { - btsf.alrm[BTSF_NCND_I].actv = 1; - } else { - btsf.alrm[BTSF_NCND_I].actv = 0; - } - state->alrm = &btsf; - break; - case DEVF: - if (reg_val & DEVF_RCALRM_M) { - devf.alrm[DEVF_RCALRM_I].actv = 1; - } else { - devf.alrm[DEVF_RCALRM_I].actv = 0; - } - if (reg_val & DEVF_INALRM_M) { - devf.alrm[DEVF_INALRM_I].actv = 1; - } else { - devf.alrm[DEVF_INALRM_I].actv = 0; - } - if (reg_val & DEVF_LFNAVL_M) { - devf.alrm[DEVF_LFNAVL_I].actv = 1; - } else { - devf.alrm[DEVF_LFNAVL_I].actv = 0; - } - state->alrm = &devf; - break; - case VACA: - if (reg_val & VACA_HIALRM_M) { - vaca.alrm[VACA_HIALRM_I].actv = 1; - } else { - vaca.alrm[VACA_HIALRM_I].actv = 0; - } - if (reg_val == VACA_LOALRM_M) { - vaca.alrm[VACA_LOALRM_I].actv = 1; - } else { - vaca.alrm[VACA_LOALRM_I].actv = 0; - } - state->alrm = &vaca; - break; - case MAIN: - if (reg_val & MAINS_AVAIL_M) { - mains.alrm[MAINS_AVAIL_I].actv = 1; - } else { - mains.alrm[MAINS_AVAIL_I].actv = 0; - } - if (reg_val == SHUTD_REQST_M) { - mains.alrm[SHUTD_REQST_I].actv = 1; - } else { - mains.alrm[SHUTD_REQST_I].actv = 0; - } - state->alrm = &mains; - break; - case OBTA: - if (reg_val == OBTA_HIALRM_V) { - obta.alrm[OBTA_HIALRM_I].actv = 1; - } - state->alrm = &obta; - break; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - state->reg.val16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - break; - } - - return rval; -} - -/* get driver configuration parameters */ -void get_config_vars() -{ - /* check if device manufacturer is set ang get the value */ - if (testvar("device_mfr")) { - device_mfr = getval("device_mfr"); - } - upsdebugx(2, "device_mfr %s", device_mfr); - - /* check if device model is set ang get the value */ - if (testvar("device_model")) { - device_model = getval("device_model"); - } - upsdebugx(2, "device_model %s", device_model); - - /* check if serial baud rate is set ang get the value */ - if (testvar("ser_baud_rate")) { - ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); - } - upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); - - /* check if serial parity is set ang get the value */ - if (testvar("ser_parity")) { - /* Dereference the char* we get */ - char *sp = getval("ser_parity"); - if (sp) { - /* TODO? Sanity-check the char we get? */ - ser_parity = *sp; - } else { - upsdebugx(2, "Could not determine ser_parity, will keep default"); - } - } - upsdebugx(2, "ser_parity %c", ser_parity); - - /* check if serial data bit is set ang get the value */ - if (testvar("ser_data_bit")) { - ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); - } - upsdebugx(2, "ser_data_bit %d", ser_data_bit); - - /* check if serial stop bit is set ang get the value */ - if (testvar("ser_stop_bit")) { - ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); - } - upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); - - /* check if device ID is set ang get the value */ - if (testvar("rio_slave_id")) { - rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); - } - upsdebugx(2, "rio_slave_id %d", rio_slave_id); - - /* check if response time out (s) is set ang get the value */ - if (testvar("mod_resp_to_s")) { - mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); - } - upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); - - /* check if response time out (us) is set ang get the value */ - if (testvar("mod_resp_to_us")) { - mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); - if (mod_resp_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); - } - } - upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); - - /* check if byte time out (s) is set ang get the value */ - if (testvar("mod_byte_to_s")) { - mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); - } - upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); - - /* check if byte time out (us) is set ang get the value */ - if (testvar("mod_byte_to_us")) { - mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); - if (mod_byte_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); - } - } - upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); -} - -/* create a new modbus context based on connection type (serial or TCP) */ -modbus_t *modbus_new(const char *port) -{ - modbus_t *mb; - char *sp; - if (strstr(port, "/dev/tty") != NULL) { - mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); - } - } else if ((sp = strchr(port, ':')) != NULL) { - char *tcp_port = xmalloc(sizeof(sp)); - strcpy(tcp_port, sp + 1); - *sp = '\0'; - mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - free(tcp_port); - } else { - mb = modbus_new_tcp(port, 502); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - } - return mb; -} - -/* reconnect to modbus server upon connection error */ -void modbus_reconnect() -{ - int rval; - - upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); - - /* clear current modbus context */ - modbus_close(mbctx); - modbus_free(mbctx); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -} diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h deleted file mode 100644 index efe0b7ccb5..0000000000 --- a/drivers/adele_cbi.h +++ /dev/null @@ -1,510 +0,0 @@ -/* adele_cbi.h - Driver for ADELE CB/CBI DC-UPS - * - * Copyright (C) - * 2022 Dimitris Economou - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef ADELE_CBI_H -#define ADELE_CBI_H - -#include - -/* UPS device details */ -#define DEVICE_MFR "ADELE" -#define DEVICE_TYPE "DC-UPS" -#define DEVICE_MODEL "CB/CBI" - -/* serial access parameters */ -#define BAUD_RATE 9600 -#define PARITY 'N' -#define DATA_BIT 8 -#define STOP_BIT 1 - -/* - * modbus response and byte timeouts - * us: 1 - 999999 - */ -#define MODRESP_TIMEOUT_s 0 -#define MODRESP_TIMEOUT_us 200000 -#define MODBYTE_TIMEOUT_s 0 -#define MODBYTE_TIMEOUT_us 50000 - -/* modbus access parameters */ -#define MODBUS_SLAVE_ID 5 - -/* number of modbus registers */ -#define MODBUS_NUMOF_REGS 98 - -/* max HOLDING registers */ -#define MAX_H_REGS 120 - -/* start HOLDING register index */ -#define H_REG_STARTIDX 0 - -/* read all regs preprocessor flag */ -#define READALL_REGS 1 - -/* number of device models */ -#define DEV_NUMOF_MODELS 10 - -/* shutdown repeat on error */ -#define FSD_REPEAT_CNT 3 - -/* shutdown repeat interval in ms */ -#define FSD_REPEAT_INTRV 1500 - -/* definition of register type */ -enum regtype { - COIL = 0, - INPUT_B, - INPUT_R, - HOLDING -}; -typedef enum regtype regtype_t; - -/* product name info, "device.model" */ -struct prodname { - uint16_t val; - char *name; -}; -typedef struct prodname prodname_t; -static prodname_t prdnm_i[] = { - {1, "CBI1235A"}, - {2, "CBI2420A"}, - {3, "CBI4810A"}, - {4, "CBI2801224"}, - {7, "CBI480W"}, - {8, "CB122410A"}, - {9, "CB480W"}, - {11, "CB12245AJ"}, - {12, "CB1235A"}, - {13, "CB2420A"}, - {14, "CB4810A"} -}; - -/* charging status info, "battery.charger.status" */ -static char *chrgs_i[] = { - "none", - "resting", /* recovering */ - "charging", /* bulk */ - "charging", /* absorb */ - "floating" /* float */ -}; -struct chrgs { - int state; - char *info; -}; -typedef struct chrgs chrgs_t; - -/* power management info, "ups.status", "battery.charger.status" */ -static char *pwrmng_i[] = { - "backup", /* "OB", "discharging" */ - "charging", /* "OL" */ - "boost", - "not charging" -}; -struct pwrmng { - int state; - char *info; -}; -typedef struct pwrmng pwrmng_t; - -/* general modbus register value */ -struct reg { - union { - uint16_t val16; - uint8_t val8; - }; - char *strval; -}; -typedef struct reg reg_t; - -/* general alarm struct */ -struct alrm { - int actv; /* active flag */ - char *descr; /* description field */ -}; -typedef struct alrm alrm_t; - -/* general alarm array */ -struct alrm_ar { - int alrm_c; /* alarm count */ - alrm_t alrm[]; /* alarm array */ -}; -typedef struct alrm_ar alrm_ar_t; - -/* - * BIT MASKS and VALUES - */ - -/* Charging status */ -#define CHRG_NONE 0 -#define CHRG_RECV 1 -#define CHRG_BULK 2 -#define CHRG_ABSR 3 -#define CHRG_FLOAT 4 - -/* power management */ -#define PMNG_BCKUP 0 -#define PMNG_CHRGN 1 -#define PMNG_BOOST 2 -#define PMNG_NCHRG 3 - -/* product name */ -#define PRDN_MAX 14 - -/* Mains alarm masks */ -#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_M 0x0002 /* shutdown requested */ - -/* Mains alarm indices */ -#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_I 1 /* shutdown requested */ - -/* AC input voltage alarm masks */ -#define VACA_HIALRM_M 0x0001 /* high alarm */ -#define VACA_LOALRM_M 0x0002 /* low alarm */ - -/* AC input voltage alarm indices */ -#define VACA_HIALRM_I 0 /* high alarm */ -#define VACA_LOALRM_I 1 /* low alarm */ - -/* Onboard temperature alarm value */ -#define OBTA_HIALRM_V 1 /* high alarm */ - -/* Onboard temperature alarm index */ -#define OBTA_HIALRM_I 0 /* high alarm */ - -/* Device failure alarm masks */ -#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ -#define DEVF_INALRM_M 0x0006 /* internal failure */ -#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ - -/* Device failure alarm indices */ -#define DEVF_RCALRM_I 0 /* rectifier failure */ -#define DEVF_INALRM_I 1 /* internal failure */ -#define DEVF_LFNAVL_I 2 /* lifetest not available */ - -/* Battery temp sensor failure alarm masks */ -#define BTSF_FCND_M 0x0001 /* connection fault */ -#define BTSF_NCND_M 0x0002 /* not connected */ - -/* Battery temp sensor failure alarm indices */ -#define BTSF_FCND_I 0 /* connection fault */ -#define BTSF_NCND_I 1 /* not connected */ - -/* Battery voltage alarm masks */ -#define BVAL_HIALRM_M 0x0001 /* high voltage */ -#define BVAL_LOALRM_M 0x0002 /* low voltage */ -#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ - -/* Battery voltage alarm indices */ -#define BVAL_HIALRM_I 0 /* high voltage */ -#define BVAL_LOALRM_I 1 /* low voltage */ -#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ - -/* SoH and SoC alarm masks */ -#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ -#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ -#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ -#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ - -/* SoH and SoC alarm indices */ -#define SHSC_HIRESI_I 0 /* high internal resistance */ -#define SHSC_LOCHEF_I 1 /* low charge efficiency */ -#define SHSC_LOEFCP_I 2 /* low effective capacity */ -#define SHSC_LOWSOC_I 3 /* low state of charge */ - -/* Battery status alarm masks */ -#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ -#define BSTA_NOCNND_M 0x0002 /* not connected */ -#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ -#define BSTA_SULPHD_M 0x0008 /* sulphated */ -#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ -#define BSTA_CNNFLT_M 0x0020 /* connection fault */ - -/* Battery status alarm indices */ -#define BSTA_REVPOL_I 0 /* reversed polarity */ -#define BSTA_NOCNND_I 1 /* not connected */ -#define BSTA_CLSHCR_I 2 /* cell short circuit */ -#define BSTA_SULPHD_I 3 /* sulphated */ -#define BSTA_CHEMNS_I 4 /* chemistry not supported */ -#define BSTA_CNNFLT_I 5 /* connection fault */ - -/* input mains and shutdown alarms */ -static alrm_ar_t mains = { - 2, - { - {0, "input voltage not available"}, - {0, "ups shutdown requested"} - } -}; - -/* AC input voltage alarms */ -static alrm_ar_t vaca = { - 2, - { - {0, "input voltage high alarm"}, - {0, "input voltage low alarm"} - } -}; - -/* device failure alarms */ -static alrm_ar_t devf = { - 3, - { - {0, "UPS rectifier failure"}, - {0, "UPS internal failure"}, - {0, "UPS lifetest not available"} - } -}; - -/* battery sensor failure alarms */ -static alrm_ar_t btsf = { - 2, - { - {0, "battery temp sensor connection fault"}, - {0, "battery temp sensor not connected"} - } -}; - -/* battery voltage alarms */ -static alrm_ar_t bval = { - 3, - { - {0, "battery high voltage"}, - {0, "battery low voltage"}, - {0, "battery start with battery flat"} - } -}; - -/* battery SoH and SoC alarms */ -static alrm_ar_t shsc = { - 4, - { - {0, "battery high internal resistance"}, - {0, "battery low charge efficiency"}, - {0, "battery low effective capacity"}, - {0, "battery low state of charge"} - } -}; - -/* battery status alarm */ -static alrm_ar_t bsta = { - 6, - { - {0, "battery reversed polarity"}, - {0, "battery not connected"}, - {0, "battery cell short circuit"}, - {0, "battery sulphated"}, - {0, "battery chemistry not supported"}, - {0, "battery connection fault"} - } -}; - -/* onboard temperature alarm */ -static alrm_ar_t obta = { - 1, - { - {0, "onboard temperature high"} - } -}; - -/* UPS device reg enum */ -enum devreg { - CHRG = 4, /* Charging status, "battery.charger.status" */ - BATV = 7, /* Battery voltage, "battery.voltage" */ - BCEF = 18, /* Battery charge efficiency factor (CEF) */ - BSOH = 20, /* Battery state-of-health */ - BSOC = 22, /* Battery state-of-charge, "battery.charge" */ - BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ - PMNG = 5, /* Power management, "ups.status" */ - OTMP = 28, /* Onboard temperature, "ups.temperature" */ - PRDN = 67, /* Product name, "ups.model" */ - VAC = 29, /* AC voltage, "input.voltage" */ - LVDC = 10, /* Load voltage, "output.voltage" */ - LCUR = 19, /* Load current, "output.current" */ - BINH = 87, /* Backup inhibit */ - FSD = 40, /* Force shutdown */ - TBUF = 103, /* Time buffering, "battery.runtime" */ - BSTA = 31, /* Battery status alarms */ - SCSH, /* SoH and SoC alarms */ - BVAL = 34, /* Battery voltage alarm */ - BTSF = 43, /* Battery temp sensor failure */ - DEVF = 42, /* Device failure */ - OBTA = 46, /* On board temp alarm */ - VACA = 44, /* VAC alarms */ - MAIN /* Mains status */ -}; -typedef enum devreg devreg_t; - -/* UPS register attributes */ -struct regattr { - int num; - int saddr; /* register start address */ - int xaddr; /* register hex address */ - float scale; /* scale */ - regtype_t type; /* register type */ -}; -typedef struct regattr regattr_t; - -/* UPS device state info union */ -union devstate { - prodname_t product; /* ups model name */ - chrgs_t charge; /* charging status */ - pwrmng_t power; /* ups status */ - reg_t reg; /* state register*/ - alrm_ar_t *alrm; /* alarm statuses */ -}; - -typedef union devstate devstate_t; - -/* device register memory image */ -static uint16_t regs_data[MAX_H_REGS]; - -/* ADELE CBI registers */ -static regattr_t regs[] = { - {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ - {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ - {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ - {40004, 0, 0, 1, HOLDING}, - {40005, 0, 0, 1, HOLDING}, /* Charging status */ - {40006, 0, 0, 1, HOLDING}, /* Power management - * 0:Backup 1:Charging 2:boost 3:Not charging - */ - {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ - {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ - {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ - {40010, 0, 0, 1, HOLDING}, /* Software ID */ - {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ - {40012, 0, 0, 1, HOLDING}, - {40013, 0, 0, 1, HOLDING}, - {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ - {40015, 0, 0, 1, HOLDING}, - {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ - {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ - {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ - {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ - {40020, 0, 0, 1, HOLDING}, /* Output load current */ - {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ - {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ - {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ - {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ - {40025, 0, 0, 1, HOLDING}, - {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ - {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ - {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ - {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ - {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ - {40031, 0, 0, 1, HOLDING}, - {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ - {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ - {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ - {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ - {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ - {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ - {40038, 0, 0, 1, HOLDING}, /* Load alarm */ - {40039, 0, 0, 1, HOLDING}, /* Device variant */ - {40040, 0, 0, 1, HOLDING}, - {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ - {40042, 0, 0, 1, HOLDING}, - {40043, 0, 0, 1, HOLDING}, /* Device failure */ - {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ - {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ - {40046, 0, 0, 1, HOLDING}, /* Mains status */ - {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ - {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ - {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ - {40050, 0, 0, .1, HOLDING}, /* Ah charged */ - {40051, 0, 0, 1, HOLDING}, /* Total run time */ - {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ - {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ - {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ - {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ - {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ - {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ - {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ - {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ - {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ - {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ - {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ - {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ - {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ - {40065, 0, 0, 1, HOLDING}, /* History clear all */ - {40066, 0, 0, 1, HOLDING}, /* Factory settings */ - {40067, 0, 0, 1, HOLDING}, /* Product name */ - {40068, 0, 0, 1, HOLDING}, - {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ - {40070, 0, 0, 1, HOLDING}, - {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ - {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ - {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ - {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ - {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ - {40076, 0, 0, 1, HOLDING}, - {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ - {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ - {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ - {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ - {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ - {40082, 0, 0, 1, HOLDING}, /* Float voltage */ - {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ - {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ - {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ - {40086, 0, 0, 1, HOLDING}, - {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ - {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed - * 1 = Backup not allowed - */ - {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ - {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ - {40091, 0, 0, 1, HOLDING}, - {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ - {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ - {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ - {40095, 0, 0, 1, HOLDING}, - {40096, 0, 0, 1, HOLDING}, - {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ - {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ - {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ - {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ - {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ - {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ - {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ - {40104, 0, 0, 1, HOLDING}, /* Time buffering */ - {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ - {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ - {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ - {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ - {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ - {40110, 0, 0, 1, HOLDING}, - {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ - {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ - {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ - {40114, 0, 0, 1, HOLDING}, - {40115, 0, 0, 1, HOLDING}, - {40116, 0, 0, 1, HOLDING}, - {40117, 0, 0, 1, HOLDING}, - {40118, 0, 0, 1, HOLDING}, - {40119, 0, 0, 1, HOLDING}, - {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ - - -}; -#endif /* ADELE_CBI_H */ diff --git a/drivers/adelsystems_cbi.c b/drivers/adelsystems_cbi.c deleted file mode 100644 index 8bb283e1f8..0000000000 --- a/drivers/adelsystems_cbi.c +++ /dev/null @@ -1,1201 +0,0 @@ -/* adele_cbi.c - driver for ADELE CB/CBI DC-UPS - * - * Copyright (C) - * 2022 Dimitris Economou - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "main.h" -#include "adelsystems_cbi.h" -#include -#include - -#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" -#define DRIVER_VERSION "0.01" - -/* variables */ -static modbus_t *mbctx = NULL; /* modbus memory context */ -static devstate_t *dstate = NULL; /* device state context */ -static int errcnt = 0; /* modbus access error counter */ -static char *device_mfr = DEVICE_MFR; /* device manufacturer */ -static char *device_model = DEVICE_MODEL; /* device model */ -static char *device_type = DEVICE_TYPE; /* device model */ -static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ -static char ser_parity = PARITY; /* serial port parity */ -static int ser_data_bit = DATA_BIT; /* serial port data bit */ -static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ -static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ -static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ -static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ -static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ -static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ - - -/* initialize register start address and hex address from register number */ -void reginit(); - -/* read all registers */ -int read_all_regs(modbus_t *mb, uint16_t *data); - -/* get config vars set by -x or defined in ups.conf driver section */ -void get_config_vars(void); - -/* get device state */ -int get_dev_state(devreg_t regindx, devstate_t **dstate); - -/* create a new modbus context based on connection type (serial or TCP) */ -modbus_t *modbus_new(const char *port); - -/* reconnect upon communication error */ -void modbus_reconnect(); - -/* modbus register read function */ -int register_read(modbus_t *mb, int addr, regtype_t type, void *data); - -/* modbus register write function */ -int register_write(modbus_t *mb, int addr, regtype_t type, void *data); - -/* instant command triggered by upsd */ -int upscmd(const char *cmd, const char *arg); - -/* count the time elapsed since start */ -long time_elapsed(struct timeval *start); - - -/* driver description structure */ -upsdrv_info_t upsdrv_info = { - DRIVER_NAME, - DRIVER_VERSION, - "Dimitris Economou \n", - DRV_BETA, - {NULL} -}; - -/* - * driver functions - */ - -/* read configuration variables from ups.conf and connect to ups device */ -void upsdrv_initups(void) -{ - int rval; - upsdebugx(2, "upsdrv_initups"); - - - dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); - reginit(); - get_config_vars(); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response time out to 200ms */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -} - -/* initialize ups driver information */ -void upsdrv_initinfo(void) -{ - devstate_t *ds = dstate; /* device state context */ - upsdebugx(2, "upsdrv_initinfo"); - - /* set device information */ - dstate_setinfo("device.mfr", "%s", device_mfr); - dstate_setinfo("device.model", "%s", device_model); - dstate_setinfo("device.type", "%s", device_type); - - /* read ups model */ - get_dev_state(PRDN, &ds); - dstate_setinfo("ups.model", "%s", ds->product.name); - upslogx(LOG_INFO, "ups.model = %s", ds->product.name); - - /* register instant commands */ - dstate_addcmd("load.off"); - - /* set callback for instant commands */ - upsh.instcmd = upscmd; -} - - -/* update UPS signal state */ -void upsdrv_updateinfo(void) -{ - int rval; /* return value */ - int i; /* local index */ - devstate_t *ds = dstate; /* device state */ - - upsdebugx(2, "upsdrv_updateinfo"); - - errcnt = 0; /* initialize error counter to zero */ - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ -#if READALL_REGS == 1 - rval = read_all_regs(mbctx, regs_data); - if (rval == -1) { - errcnt++; - } else { -#endif - /* - * update UPS status regarding MAINS and SHUTDOWN request - * - OL: On line (mains is present) - * - OB: On battery (mains is not present) - */ - rval = get_dev_state(MAIN, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { - status_set("OB"); - alarm_set(mains.alrm[MAINS_AVAIL_I].descr); - upslogx(LOG_INFO, "ups.status = OB"); - } else { - status_set("OL"); - upslogx(LOG_INFO, "ups.status = OL"); - } - if (ds->alrm->alrm[SHUTD_REQST_I].actv) { - status_set("FSD"); - alarm_set(mains.alrm[SHUTD_REQST_I].descr); - upslogx(LOG_INFO, "ups.status = FSD"); - } - } - - /* - * update UPS status regarding battery voltage - */ - rval = get_dev_state(BVAL, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { - status_set("LB"); - alarm_set(bval.alrm[BVAL_LOALRM_I].descr); - upslogx(LOG_INFO, "ups.status = LB"); - } - if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { - status_set("HB"); - alarm_set(bval.alrm[BVAL_HIALRM_I].descr); - upslogx(LOG_INFO, "ups.status = HB"); - } - if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { - alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); - upslogx(LOG_INFO, "battery start with battery flat"); - } - } - - /* get "battery.voltage" */ - rval = get_dev_state(BATV, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); - } - /* - * update UPS status regarding battery charger status - */ - - /* get "battery.charger.status" */ - rval = get_dev_state(CHRG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { - status_set("CHRG"); - upslogx(LOG_INFO, "ups.status = CHRG"); - } - dstate_setinfo("battery.charger.status", "%s", ds->charge.info); - upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); - } - rval = get_dev_state(PMNG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->power.state == PMNG_BCKUP) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - upslogx(LOG_INFO, "ups.status = DISCHRG"); - } - if (ds->power.state == PMNG_BOOST) { - status_set("BOOST"); - upslogx(LOG_INFO, "ups.status = BOOST"); - } - } - - /* - * update UPS battery state of charge - */ - rval = get_dev_state(BSOC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.charge", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); - } - - /* - * update UPS AC input state - */ - rval = get_dev_state(VACA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - rval = get_dev_state(VAC, &ds); - dstate_setinfo("input.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); - } - - /* - * update UPS onboard temperature state - */ - rval = get_dev_state(OBTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(OTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("ups.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); - } - /* - * update UPS battery temperature state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(BTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); - } - rval = get_dev_state(TBUF, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.runtime", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); - } - - /* - * update UPS device failure state - */ - rval = get_dev_state(DEVF, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS SoH and SoC states - */ - rval = get_dev_state(SCSH, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS battery state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS load status - */ - rval = get_dev_state(LVDC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); - } - rval = get_dev_state(LCUR, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.current", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); - } -#if READALL_REGS == 1 - } -#endif - /* check for communication errors */ - if (errcnt == 0) { - alarm_commit(); - status_commit(); - dstate_dataok(); - } else { - upsdebugx(2, "Communication errors: %d", errcnt); - dstate_datastale(); - } -} - -/* shutdown UPS */ -void upsdrv_shutdown(void) -{ - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); - case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); -} - -/* print driver usage info */ -void upsdrv_help(void) -{ -} - -/* list flags and values that you want to receive via -x */ -void upsdrv_makevartable(void) -{ - addvar(VAR_VALUE, "device_mfr", "device manufacturer"); - addvar(VAR_VALUE, "device_model", "device model"); - addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); - addvar(VAR_VALUE, "ser_parity", "serial port parity"); - addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); - addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); - addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); - addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); - addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); - addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); - addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); -} - -/* close modbus connection and free modbus context allocated memory */ -void upsdrv_cleanup(void) -{ - if (mbctx != NULL) { - modbus_close(mbctx); - modbus_free(mbctx); - } - if (dstate != NULL) { - free(dstate); - } -} - -/* - * driver support functions - */ - -/* initialize register start address and hex address from register number */ -void reginit() -{ - int i; /* local index */ - - for (i = 0; i < MODBUS_NUMOF_REGS; i++) { - int rnum = regs[i].num; - switch (regs[i].type) { - case COIL: - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x0 + regs[i].num - 1; - break; - case INPUT_B: - rnum -= 10000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x10000 + rnum - 1; - break; - case INPUT_R: - rnum -= 30000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x30000 + rnum - 1; - break; - case HOLDING: - rnum -= 40000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x40000 + rnum - 1; - break; - default: - upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); - upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); - } - upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", - regs[i].num, - regs[i].type, - regs[i].saddr, - regs[i].xaddr - ); - } -} - -/* read all register data image */ -int read_all_regs(modbus_t *mb, uint16_t *data) -{ - int rval; - - /* read all HOLDING registers */ - rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, regs_data); - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", - modbus_strerror(errno), - regs[H_REG_STARTIDX].xaddr, - MAX_H_REGS, - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - - /* no COIL, INPUT_B or INPUT_R register regions to read */ - - return rval; -} - -/* Read a modbus register */ -int register_read(modbus_t *mb, int addr, regtype_t type, void *data) -{ - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x00FF; - uint mask16 = 0xFFFF; - - switch (type) { - case COIL: - rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_B: - rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_R: - rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - case HOLDING: - rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; -} - -/* write a modbus register */ -int register_write(modbus_t *mb, int addr, regtype_t type, void *data) -{ - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x00FF; - uint mask16 = 0xFFFF; - - switch (type) { - case COIL: - *(uint *)data = *(uint *)data & mask8; - rval = modbus_write_bit(mb, addr, *(uint8_t *)data); - break; - case HOLDING: - *(uint *)data = *(uint *)data & mask16; - rval = modbus_write_register(mb, addr, *(uint16_t *)data); - break; - - case INPUT_B: - case INPUT_R: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; -} - -/* returns the time elapsed since start in milliseconds */ -long time_elapsed(struct timeval *start) -{ - long rval; - struct timeval end; - - rval = gettimeofday(&end, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); - } - if (start->tv_usec < end.tv_usec) { - suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; - end.tv_usec -= 1000000 * nsec; - end.tv_sec += nsec; - } - if (start->tv_usec - end.tv_usec > 1000000) { - suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; - end.tv_usec += 1000000 * nsec; - end.tv_sec -= nsec; - } - rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; - - return rval; -} - -/* instant command triggered by upsd */ -int upscmd(const char *cmd, const char *arg) -{ - int rval; - int data; - - if (!strcasecmp(cmd, "load.off")) { - data = 1; - rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); - if (rval == -1) { - upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - regs[FSD].xaddr, - regs[FSD].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); - rval = STAT_INSTCMD_HANDLED; - } - } else { - upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_UNKNOWN; - } - return rval; -} - -/* read device state, returns 0 on success or -1 on communication error - it formats state depending on register semantics */ -int get_dev_state(devreg_t regindx, devstate_t **dstate) -{ - int i; /* local index */ - int n; - int rval; /* return value */ - static char *ptr = NULL; /* temporary pointer */ - uint reg_val; /* register value */ -#if READALL_REGS == 0 - uint num; /* register number */ - regtype_t rtype; /* register type */ - int addr; /* register address */ -#endif - devstate_t *state; /* device state */ - - state = *dstate; -#if READALL_REGS == 1 - reg_val = regs_data[regindx]; - rval = 0; -#elif READALL_REGS == 0 - num = regs[regindx].num; - addr = regs[regindx].xaddr; - rtype = regs[regindx].type; - rval = register_read(mbctx, addr, rtype, ®_val); - if (rval == -1) { - return rval; - } - upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", - num, - addr, - rtype, - reg_val - ); -#endif - /* process register data */ - switch (regindx) { - case CHRG: /* "ups.charge" */ - if (reg_val == CHRG_NONE) { - state->charge.state = CHRG_NONE; - state->charge.info = chrgs_i[CHRG_NONE]; - } else if (reg_val == CHRG_RECV) { - state->charge.state = CHRG_RECV; - state->charge.info = chrgs_i[CHRG_RECV]; - } else if (reg_val == CHRG_BULK) { - state->charge.state = CHRG_BULK; - state->charge.info = chrgs_i[CHRG_BULK]; - } else if (reg_val == CHRG_ABSR) { - state->charge.state = CHRG_ABSR; - state->charge.info = chrgs_i[CHRG_ABSR]; - } else if (reg_val == CHRG_FLOAT) { - state->charge.state = CHRG_FLOAT; - state->charge.info = chrgs_i[CHRG_FLOAT]; - } - upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); - break; - case BATV: /* "battery.voltage" */ - case LVDC: /* "output.voltage" */ - case LCUR: /* "output.current" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case TBUF: - case BSOH: - case BCEF: - case VAC: /* "input.voltage" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BSOC: /* "battery.charge" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - double fval = (double )reg_val * regs[BSOC].scale; - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BTMP: /* "battery.temperature" */ - case OTMP: /* "ups.temperature" */ - state->reg.val16 = reg_val; - double fval = reg_val - 273.15; - n = snprintf(NULL, 0, "%.2f", fval); - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - if (ptr != NULL) { - free(ptr); - } - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case PMNG: /* "ups.status" & "battery.charge" */ - if (reg_val == PMNG_BCKUP) { - state->power.state = PMNG_BCKUP; - state->power.info = pwrmng_i[PMNG_BCKUP]; - } else if (reg_val == PMNG_CHRGN) { - state->power.state = PMNG_CHRGN; - state->power.info = pwrmng_i[PMNG_CHRGN]; - } else if (reg_val == PMNG_BOOST) { - state->power.state = PMNG_BOOST; - state->power.info = pwrmng_i[PMNG_BOOST]; - } else if (reg_val == PMNG_NCHRG) { - state->power.state = PMNG_NCHRG; - state->power.info = pwrmng_i[PMNG_NCHRG]; - } - upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); - break; - case PRDN: /* "ups.model" */ - for (i = 0; i < DEV_NUMOF_MODELS; i++) { - if (prdnm_i[i].val == reg_val) { - break; - } - } - state->product.val = reg_val; - state->product.name = prdnm_i[i].name; - upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); - break; - case BSTA: - if (reg_val & BSTA_REVPOL_M) { - bsta.alrm[BSTA_REVPOL_I].actv = 1; - } else { - bsta.alrm[BSTA_REVPOL_I].actv = 0; - } - if (reg_val & BSTA_NOCNND_M) { - bsta.alrm[BSTA_NOCNND_I].actv = 1; - } else { - bsta.alrm[BSTA_NOCNND_I].actv = 0; - } - if (reg_val & BSTA_CLSHCR_M) { - bsta.alrm[BSTA_CLSHCR_I].actv = 1; - } else { - bsta.alrm[BSTA_CLSHCR_I].actv = 0; - } - if (reg_val & BSTA_SULPHD_M) { - bsta.alrm[BSTA_SULPHD_I].actv = 1; - } else { - bsta.alrm[BSTA_SULPHD_I].actv = 0; - } - if (reg_val & BSTA_CHEMNS_M) { - bsta.alrm[BSTA_CHEMNS_I].actv = 1; - } else { - bsta.alrm[BSTA_CHEMNS_I].actv = 0; - } - if (reg_val & BSTA_CNNFLT_M) { - bsta.alrm[BSTA_CNNFLT_I].actv = 1; - } else { - bsta.alrm[BSTA_CNNFLT_I].actv = 0; - } - state->alrm = &bsta; - break; - case SCSH: - if (reg_val & SHSC_HIRESI_M) { - shsc.alrm[SHSC_HIRESI_I].actv = 1; - } else { - shsc.alrm[SHSC_HIRESI_I].actv = 0; - } - if (reg_val & SHSC_LOCHEF_M) { - shsc.alrm[SHSC_LOCHEF_I].actv = 1; - } else { - shsc.alrm[SHSC_LOCHEF_I].actv = 0; - } - if (reg_val & SHSC_LOEFCP_M) { - shsc.alrm[SHSC_LOEFCP_I].actv = 1; - } else { - shsc.alrm[SHSC_LOEFCP_I].actv = 0; - } - if (reg_val & SHSC_LOWSOC_M) { - shsc.alrm[SHSC_LOWSOC_I].actv = 1; - } else { - shsc.alrm[SHSC_LOWSOC_I].actv = 0; - } - state->alrm = &shsc; - break; - case BVAL: - if (reg_val & BVAL_HIALRM_M) { - bval.alrm[BVAL_HIALRM_I].actv = 1; - } else { - bval.alrm[BVAL_HIALRM_I].actv = 0; - } - if (reg_val & BVAL_LOALRM_M) { - bval.alrm[BVAL_LOALRM_I].actv = 1; - } else { - bval.alrm[BVAL_LOALRM_I].actv = 0; - } - if (reg_val & BVAL_BSTSFL_M) { - bval.alrm[BVAL_BSTSFL_I].actv = 1; - } else { - bval.alrm[BVAL_BSTSFL_I].actv = 0; - } - state->alrm = &bval; - break; - case BTSF: - if (reg_val & BTSF_FCND_M) { - btsf.alrm[BTSF_FCND_I].actv = 1; - } else { - btsf.alrm[BTSF_FCND_I].actv = 0; - } - if (reg_val & BTSF_NCND_M) { - btsf.alrm[BTSF_NCND_I].actv = 1; - } else { - btsf.alrm[BTSF_NCND_I].actv = 0; - } - state->alrm = &btsf; - break; - case DEVF: - if (reg_val & DEVF_RCALRM_M) { - devf.alrm[DEVF_RCALRM_I].actv = 1; - } else { - devf.alrm[DEVF_RCALRM_I].actv = 0; - } - if (reg_val & DEVF_INALRM_M) { - devf.alrm[DEVF_INALRM_I].actv = 1; - } else { - devf.alrm[DEVF_INALRM_I].actv = 0; - } - if (reg_val & DEVF_LFNAVL_M) { - devf.alrm[DEVF_LFNAVL_I].actv = 1; - } else { - devf.alrm[DEVF_LFNAVL_I].actv = 0; - } - state->alrm = &devf; - break; - case VACA: - if (reg_val & VACA_HIALRM_M) { - vaca.alrm[VACA_HIALRM_I].actv = 1; - } else { - vaca.alrm[VACA_HIALRM_I].actv = 0; - } - if (reg_val == VACA_LOALRM_M) { - vaca.alrm[VACA_LOALRM_I].actv = 1; - } else { - vaca.alrm[VACA_LOALRM_I].actv = 0; - } - state->alrm = &vaca; - break; - case MAIN: - if (reg_val & MAINS_AVAIL_M) { - mains.alrm[MAINS_AVAIL_I].actv = 1; - } else { - mains.alrm[MAINS_AVAIL_I].actv = 0; - } - if (reg_val == SHUTD_REQST_M) { - mains.alrm[SHUTD_REQST_I].actv = 1; - } else { - mains.alrm[SHUTD_REQST_I].actv = 0; - } - state->alrm = &mains; - break; - case OBTA: - if (reg_val == OBTA_HIALRM_V) { - obta.alrm[OBTA_HIALRM_I].actv = 1; - } - state->alrm = &obta; - break; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - state->reg.val16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - break; - } - - return rval; -} - -/* get driver configuration parameters */ -void get_config_vars() -{ - /* check if device manufacturer is set ang get the value */ - if (testvar("device_mfr")) { - device_mfr = getval("device_mfr"); - } - upsdebugx(2, "device_mfr %s", device_mfr); - - /* check if device model is set ang get the value */ - if (testvar("device_model")) { - device_model = getval("device_model"); - } - upsdebugx(2, "device_model %s", device_model); - - /* check if serial baud rate is set ang get the value */ - if (testvar("ser_baud_rate")) { - ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); - } - upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); - - /* check if serial parity is set ang get the value */ - if (testvar("ser_parity")) { - /* Dereference the char* we get */ - char *sp = getval("ser_parity"); - if (sp) { - /* TODO? Sanity-check the char we get? */ - ser_parity = *sp; - } else { - upsdebugx(2, "Could not determine ser_parity, will keep default"); - } - } - upsdebugx(2, "ser_parity %c", ser_parity); - - /* check if serial data bit is set ang get the value */ - if (testvar("ser_data_bit")) { - ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); - } - upsdebugx(2, "ser_data_bit %d", ser_data_bit); - - /* check if serial stop bit is set ang get the value */ - if (testvar("ser_stop_bit")) { - ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); - } - upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); - - /* check if device ID is set ang get the value */ - if (testvar("rio_slave_id")) { - rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); - } - upsdebugx(2, "rio_slave_id %d", rio_slave_id); - - /* check if response time out (s) is set ang get the value */ - if (testvar("mod_resp_to_s")) { - mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); - } - upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); - - /* check if response time out (us) is set ang get the value */ - if (testvar("mod_resp_to_us")) { - mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); - if (mod_resp_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); - } - } - upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); - - /* check if byte time out (s) is set ang get the value */ - if (testvar("mod_byte_to_s")) { - mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); - } - upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); - - /* check if byte time out (us) is set ang get the value */ - if (testvar("mod_byte_to_us")) { - mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); - if (mod_byte_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); - } - } - upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); -} - -/* create a new modbus context based on connection type (serial or TCP) */ -modbus_t *modbus_new(const char *port) -{ - modbus_t *mb; - char *sp; - if (strstr(port, "/dev/tty") != NULL) { - mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); - } - } else if ((sp = strchr(port, ':')) != NULL) { - char *tcp_port = xmalloc(sizeof(sp)); - strcpy(tcp_port, sp + 1); - *sp = '\0'; - mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - free(tcp_port); - } else { - mb = modbus_new_tcp(port, 502); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - } - return mb; -} - -/* reconnect to modbus server upon connection error */ -void modbus_reconnect() -{ - int rval; - - upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); - - /* clear current modbus context */ - modbus_close(mbctx); - modbus_free(mbctx); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -} diff --git a/drivers/adelsystems_cbi.h b/drivers/adelsystems_cbi.h deleted file mode 100644 index b27c2f1e4a..0000000000 --- a/drivers/adelsystems_cbi.h +++ /dev/null @@ -1,510 +0,0 @@ -/* adelsystems_cbi.h - Driver for ADELSYSTEMS CB/CBI DC-UPS - * - * Copyright (C) - * 2022 Dimitris Economou - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef ADELSYSTEMS_CBI_H -#define ADELSYSTEMS_CBI_H - -#include - -/* UPS device details */ -#define DEVICE_MFR "ADELSYSTEMS" -#define DEVICE_TYPE "DC-UPS" -#define DEVICE_MODEL "CB/CBI" - -/* serial access parameters */ -#define BAUD_RATE 9600 -#define PARITY 'N' -#define DATA_BIT 8 -#define STOP_BIT 1 - -/* - * modbus response and byte timeouts - * us: 1 - 999999 - */ -#define MODRESP_TIMEOUT_s 0 -#define MODRESP_TIMEOUT_us 200000 -#define MODBYTE_TIMEOUT_s 0 -#define MODBYTE_TIMEOUT_us 50000 - -/* modbus access parameters */ -#define MODBUS_SLAVE_ID 5 - -/* number of modbus registers */ -#define MODBUS_NUMOF_REGS 98 - -/* max HOLDING registers */ -#define MAX_H_REGS 120 - -/* start HOLDING register index */ -#define H_REG_STARTIDX 0 - -/* read all regs preprocessor flag */ -#define READALL_REGS 1 - -/* number of device models */ -#define DEV_NUMOF_MODELS 10 - -/* shutdown repeat on error */ -#define FSD_REPEAT_CNT 3 - -/* shutdown repeat interval in ms */ -#define FSD_REPEAT_INTRV 1500 - -/* definition of register type */ -enum regtype { - COIL = 0, - INPUT_B, - INPUT_R, - HOLDING -}; -typedef enum regtype regtype_t; - -/* product name info, "device.model" */ -struct prodname { - uint16_t val; - char *name; -}; -typedef struct prodname prodname_t; -static prodname_t prdnm_i[] = { - {1, "CBI1235A"}, - {2, "CBI2420A"}, - {3, "CBI4810A"}, - {4, "CBI2801224"}, - {7, "CBI480W"}, - {8, "CB122410A"}, - {9, "CB480W"}, - {11, "CB12245AJ"}, - {12, "CB1235A"}, - {13, "CB2420A"}, - {14, "CB4810A"} -}; - -/* charging status info, "battery.charger.status" */ -static char *chrgs_i[] = { - "none", - "resting", /* recovering */ - "charging", /* bulk */ - "charging", /* absorb */ - "floating" /* float */ -}; -struct chrgs { - int state; - char *info; -}; -typedef struct chrgs chrgs_t; - -/* power management info, "ups.status", "battery.charger.status" */ -static char *pwrmng_i[] = { - "backup", /* "OB", "discharging" */ - "charging", /* "OL" */ - "boost", - "not charging" -}; -struct pwrmng { - int state; - char *info; -}; -typedef struct pwrmng pwrmng_t; - -/* general modbus register value */ -struct reg { - union { - uint16_t val16; - uint8_t val8; - }; - char *strval; -}; -typedef struct reg reg_t; - -/* general alarm struct */ -struct alrm { - int actv; /* active flag */ - char *descr; /* description field */ -}; -typedef struct alrm alrm_t; - -/* general alarm array */ -struct alrm_ar { - int alrm_c; /* alarm count */ - alrm_t alrm[]; /* alarm array */ -}; -typedef struct alrm_ar alrm_ar_t; - -/* - * BIT MASKS and VALUES - */ - -/* Charging status */ -#define CHRG_NONE 0 -#define CHRG_RECV 1 -#define CHRG_BULK 2 -#define CHRG_ABSR 3 -#define CHRG_FLOAT 4 - -/* power management */ -#define PMNG_BCKUP 0 -#define PMNG_CHRGN 1 -#define PMNG_BOOST 2 -#define PMNG_NCHRG 3 - -/* product name */ -#define PRDN_MAX 14 - -/* Mains alarm masks */ -#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_M 0x0002 /* shutdown requested */ - -/* Mains alarm indices */ -#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_I 1 /* shutdown requested */ - -/* AC input voltage alarm masks */ -#define VACA_HIALRM_M 0x0001 /* high alarm */ -#define VACA_LOALRM_M 0x0002 /* low alarm */ - -/* AC input voltage alarm indices */ -#define VACA_HIALRM_I 0 /* high alarm */ -#define VACA_LOALRM_I 1 /* low alarm */ - -/* Onboard temperature alarm value */ -#define OBTA_HIALRM_V 1 /* high alarm */ - -/* Onboard temperature alarm index */ -#define OBTA_HIALRM_I 0 /* high alarm */ - -/* Device failure alarm masks */ -#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ -#define DEVF_INALRM_M 0x0006 /* internal failure */ -#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ - -/* Device failure alarm indices */ -#define DEVF_RCALRM_I 0 /* rectifier failure */ -#define DEVF_INALRM_I 1 /* internal failure */ -#define DEVF_LFNAVL_I 2 /* lifetest not available */ - -/* Battery temp sensor failure alarm masks */ -#define BTSF_FCND_M 0x0001 /* connection fault */ -#define BTSF_NCND_M 0x0002 /* not connected */ - -/* Battery temp sensor failure alarm indices */ -#define BTSF_FCND_I 0 /* connection fault */ -#define BTSF_NCND_I 1 /* not connected */ - -/* Battery voltage alarm masks */ -#define BVAL_HIALRM_M 0x0001 /* high voltage */ -#define BVAL_LOALRM_M 0x0002 /* low voltage */ -#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ - -/* Battery voltage alarm indices */ -#define BVAL_HIALRM_I 0 /* high voltage */ -#define BVAL_LOALRM_I 1 /* low voltage */ -#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ - -/* SoH and SoC alarm masks */ -#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ -#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ -#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ -#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ - -/* SoH and SoC alarm indices */ -#define SHSC_HIRESI_I 0 /* high internal resistance */ -#define SHSC_LOCHEF_I 1 /* low charge efficiency */ -#define SHSC_LOEFCP_I 2 /* low effective capacity */ -#define SHSC_LOWSOC_I 3 /* low state of charge */ - -/* Battery status alarm masks */ -#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ -#define BSTA_NOCNND_M 0x0002 /* not connected */ -#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ -#define BSTA_SULPHD_M 0x0008 /* sulphated */ -#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ -#define BSTA_CNNFLT_M 0x0020 /* connection fault */ - -/* Battery status alarm indices */ -#define BSTA_REVPOL_I 0 /* reversed polarity */ -#define BSTA_NOCNND_I 1 /* not connected */ -#define BSTA_CLSHCR_I 2 /* cell short circuit */ -#define BSTA_SULPHD_I 3 /* sulphated */ -#define BSTA_CHEMNS_I 4 /* chemistry not supported */ -#define BSTA_CNNFLT_I 5 /* connection fault */ - -/* input mains and shutdown alarms */ -static alrm_ar_t mains = { - 2, - { - {0, "input voltage not available"}, - {0, "ups shutdown requested"} - } -}; - -/* AC input voltage alarms */ -static alrm_ar_t vaca = { - 2, - { - {0, "input voltage high alarm"}, - {0, "input voltage low alarm"} - } -}; - -/* device failure alarms */ -static alrm_ar_t devf = { - 3, - { - {0, "UPS rectifier failure"}, - {0, "UPS internal failure"}, - {0, "UPS lifetest not available"} - } -}; - -/* battery sensor failure alarms */ -static alrm_ar_t btsf = { - 2, - { - {0, "battery temp sensor connection fault"}, - {0, "battery temp sensor not connected"} - } -}; - -/* battery voltage alarms */ -static alrm_ar_t bval = { - 3, - { - {0, "battery high voltage"}, - {0, "battery low voltage"}, - {0, "battery start with battery flat"} - } -}; - -/* battery SoH and SoC alarms */ -static alrm_ar_t shsc = { - 4, - { - {0, "battery high internal resistance"}, - {0, "battery low charge efficiency"}, - {0, "battery low effective capacity"}, - {0, "battery low state of charge"} - } -}; - -/* battery status alarm */ -static alrm_ar_t bsta = { - 6, - { - {0, "battery reversed polarity"}, - {0, "battery not connected"}, - {0, "battery cell short circuit"}, - {0, "battery sulphated"}, - {0, "battery chemistry not supported"}, - {0, "battery connection fault"} - } -}; - -/* onboard temperature alarm */ -static alrm_ar_t obta = { - 1, - { - {0, "onboard temperature high"} - } -}; - -/* UPS device reg enum */ -enum devreg { - CHRG = 4, /* Charging status, "battery.charger.status" */ - BATV = 7, /* Battery voltage, "battery.voltage" */ - BCEF = 18, /* Battery charge efficiency factor (CEF) */ - BSOH = 20, /* Battery state-of-health */ - BSOC = 22, /* Battery state-of-charge, "battery.charge" */ - BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ - PMNG = 5, /* Power management, "ups.status" */ - OTMP = 28, /* Onboard temperature, "ups.temperature" */ - PRDN = 67, /* Product name, "ups.model" */ - VAC = 29, /* AC voltage, "input.voltage" */ - LVDC = 10, /* Load voltage, "output.voltage" */ - LCUR = 19, /* Load current, "output.current" */ - BINH = 87, /* Backup inhibit */ - FSD = 40, /* Force shutdown */ - TBUF = 103, /* Time buffering, "battery.runtime" */ - BSTA = 31, /* Battery status alarms */ - SCSH, /* SoH and SoC alarms */ - BVAL = 34, /* Battery voltage alarm */ - BTSF = 43, /* Battery temp sensor failure */ - DEVF = 42, /* Device failure */ - OBTA = 46, /* On board temp alarm */ - VACA = 44, /* VAC alarms */ - MAIN /* Mains status */ -}; -typedef enum devreg devreg_t; - -/* UPS register attributes */ -struct regattr { - int num; - int saddr; /* register start address */ - int xaddr; /* register hex address */ - float scale; /* scale */ - regtype_t type; /* register type */ -}; -typedef struct regattr regattr_t; - -/* UPS device state info union */ -union devstate { - prodname_t product; /* ups model name */ - chrgs_t charge; /* charging status */ - pwrmng_t power; /* ups status */ - reg_t reg; /* state register*/ - alrm_ar_t *alrm; /* alarm statuses */ -}; - -typedef union devstate devstate_t; - -/* device register memory image */ -static uint16_t regs_data[MAX_H_REGS]; - -/* ADELSYSTEMS CBI registers */ -static regattr_t regs[] = { - {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ - {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ - {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ - {40004, 0, 0, 1, HOLDING}, - {40005, 0, 0, 1, HOLDING}, /* Charging status */ - {40006, 0, 0, 1, HOLDING}, /* Power management - * 0:Backup 1:Charging 2:boost 3:Not charging - */ - {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ - {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ - {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ - {40010, 0, 0, 1, HOLDING}, /* Software ID */ - {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ - {40012, 0, 0, 1, HOLDING}, - {40013, 0, 0, 1, HOLDING}, - {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ - {40015, 0, 0, 1, HOLDING}, - {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ - {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ - {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ - {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ - {40020, 0, 0, 1, HOLDING}, /* Output load current */ - {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ - {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ - {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ - {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ - {40025, 0, 0, 1, HOLDING}, - {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ - {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ - {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ - {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ - {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ - {40031, 0, 0, 1, HOLDING}, - {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ - {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ - {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ - {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ - {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ - {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ - {40038, 0, 0, 1, HOLDING}, /* Load alarm */ - {40039, 0, 0, 1, HOLDING}, /* Device variant */ - {40040, 0, 0, 1, HOLDING}, - {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ - {40042, 0, 0, 1, HOLDING}, - {40043, 0, 0, 1, HOLDING}, /* Device failure */ - {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ - {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ - {40046, 0, 0, 1, HOLDING}, /* Mains status */ - {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ - {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ - {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ - {40050, 0, 0, .1, HOLDING}, /* Ah charged */ - {40051, 0, 0, 1, HOLDING}, /* Total run time */ - {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ - {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ - {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ - {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ - {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ - {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ - {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ - {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ - {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ - {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ - {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ - {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ - {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ - {40065, 0, 0, 1, HOLDING}, /* History clear all */ - {40066, 0, 0, 1, HOLDING}, /* Factory settings */ - {40067, 0, 0, 1, HOLDING}, /* Product name */ - {40068, 0, 0, 1, HOLDING}, - {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ - {40070, 0, 0, 1, HOLDING}, - {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ - {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ - {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ - {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ - {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ - {40076, 0, 0, 1, HOLDING}, - {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ - {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ - {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ - {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ - {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ - {40082, 0, 0, 1, HOLDING}, /* Float voltage */ - {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ - {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ - {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ - {40086, 0, 0, 1, HOLDING}, - {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ - {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed - * 1 = Backup not allowed - */ - {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ - {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ - {40091, 0, 0, 1, HOLDING}, - {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ - {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ - {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ - {40095, 0, 0, 1, HOLDING}, - {40096, 0, 0, 1, HOLDING}, - {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ - {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ - {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ - {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ - {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ - {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ - {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ - {40104, 0, 0, 1, HOLDING}, /* Time buffering */ - {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ - {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ - {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ - {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ - {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ - {40110, 0, 0, 1, HOLDING}, - {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ - {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ - {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ - {40114, 0, 0, 1, HOLDING}, - {40115, 0, 0, 1, HOLDING}, - {40116, 0, 0, 1, HOLDING}, - {40117, 0, 0, 1, HOLDING}, - {40118, 0, 0, 1, HOLDING}, - {40119, 0, 0, 1, HOLDING}, - {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ - - -}; -#endif /* ADELSYSTEMS_CBI_H */ From 847e66622196898baea4bbf8c6dc4e857e5ccd38 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 20:45:20 +0200 Subject: [PATCH 044/700] try to reconnect on INVALID DATA and INVALID CRC errors --- drivers/adelsystem_cbi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index 7b4804a0d1..63ae248d31 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -606,8 +606,8 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) device_path ); - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { + /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ + if (errno == EPIPE || errno == EMBBADDATA || errno == EMBBADCRC) { upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); modbus_reconnect(); } From ea29b423346207101c2542ab2c57b3c59b8b8283 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 21:15:18 +0200 Subject: [PATCH 045/700] try to reconnect on IVALID DATA and INVALID CRC from read_all_regs --- drivers/adelsystem_cbi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index 63ae248d31..409b59bddd 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -542,8 +542,8 @@ int read_all_regs(modbus_t *mb, uint16_t *data) device_path ); - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { + /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ + if (errno == EPIPE || errno == EMBBADDATA || errno == EMBBADCRC) { upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); modbus_reconnect(); } From 6fdfc8a47e04a1664e75d497c14bfb3089ee0afd Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Tue, 18 Jan 2022 16:01:10 +0200 Subject: [PATCH 046/700] rename for rebase --- drivers/{adelsystem_cbi.c => adele_cbi.c} | 0 drivers/{adelsystem_cbi.h => adele_cbi.h} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename drivers/{adelsystem_cbi.c => adele_cbi.c} (100%) rename drivers/{adelsystem_cbi.h => adele_cbi.h} (100%) diff --git a/drivers/adelsystem_cbi.c b/drivers/adele_cbi.c similarity index 100% rename from drivers/adelsystem_cbi.c rename to drivers/adele_cbi.c diff --git a/drivers/adelsystem_cbi.h b/drivers/adele_cbi.h similarity index 100% rename from drivers/adelsystem_cbi.h rename to drivers/adele_cbi.h From 6462863b8b74f1b5d8ef95119322a2da7202e37e Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 6 Jan 2022 01:09:02 +0200 Subject: [PATCH 047/700] under construction --- drivers/adele_cbi.c | 1006 +++++++++++++++++++++++++++++++++++++++++++ drivers/adele_cbi.h | 196 +++++++++ 2 files changed, 1202 insertions(+) create mode 100644 drivers/adele_cbi.c create mode 100644 drivers/adele_cbi.h diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c new file mode 100644 index 0000000000..f24e13b575 --- /dev/null +++ b/drivers/adele_cbi.c @@ -0,0 +1,1006 @@ +/* adele_cbi.c - Driver for adele CB/CBI UPS + * + * Copyright (C) + * 2021 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "adele_cbi.h" +#include +#include + +#define DRIVER_NAME "NUT Adele CBI driver" +#define DRIVER_VERSION "0.01" + +/* variables */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static regattr_t sigar[NUMOF_SIG_STATES]; /* array of ups signal attributes */ +static int errcnt = 0; /* modbus access error counter */ + +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ + + +/* get config vars set by -x or defined in ups.conf driver section */ +void get_config_vars(void); + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port); + +/* reconnect upon communication error */ +void modbus_reconnect(); + +/* modbus register read function */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data); + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg); + +/* read signal status */ +int get_signal_state(devstate_t state); + +/* count the time elapsed since start */ +long time_elapsed(struct timeval *start); + +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} +}; + +/* + * driver functions + */ + +/* initialize ups driver information */ +void upsdrv_initinfo(void) { + upsdebugx(2, "upsdrv_initinfo"); + + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); + + /* register instant commands */ + if (sigar[FSD_T].addr != NOTUSED) { + dstate_addcmd("load.off"); + } + + /* set callback for instant commands */ + upsh.instcmd = upscmd; +} + +/* open serial connection and connect to modbus RIO */ +void upsdrv_initups(void) +{ + int rval; + upsdebugx(2, "upsdrv_initups"); + + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response time out to 200ms */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} + +/* update UPS signal state */ +void upsdrv_updateinfo(void) +{ + int rval; + int online = -1; /* keep online state */ + errcnt = 0; + + upsdebugx(2, "upsdrv_updateinfo"); + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ + + /* + * update UPS status regarding MAINS state either via OL | OB. + * if both statuses are mapped to contacts then only OL is evaluated. + */ + if (sigar[OL_T].addr != NOTUSED) { + rval = get_signal_state(OL_T); + upsdebugx(2, "OL value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OL_T].noro)) { + status_set("OL"); + online = 1; + } else { + status_set("OB"); + online = 0; + + /* if DISCHRG state is not mapped to a contact and UPS is on + * batteries set status to DISCHRG state */ + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + + } + } else if (sigar[OB_T].addr != NOTUSED) { + rval = get_signal_state(OB_T); + upsdebugx(2, "OB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OB_T].noro)) { + status_set("OB"); + online = 0; + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } else { + status_set("OL"); + online = 1; + } + } + + /* + * update UPS status regarding CHARGING state via HB. HB is usually + * mapped to "ready" contact when closed indicates a charging state > 85% + */ + if (sigar[HB_T].addr != NOTUSED) { + rval = get_signal_state(HB_T); + upsdebugx(2, "HB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[HB_T].noro)) { + status_set("HB"); + dstate_setinfo("battery.charger.status", "resting"); + } else if (online == 1 && sigar[CHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } else if (online == 0 && sigar[DISCHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* + * update UPS status regarding DISCHARGING state via LB. LB is mapped + * to "battery low" contact. + */ + if (sigar[LB_T].addr != NOTUSED) { + rval = get_signal_state(LB_T); + upsdebugx(2, "LB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[LB_T].noro)) { + status_set("LB"); + alarm_set("Low Battery (Charge)"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[RB_T].addr != NOTUSED) { + rval = get_signal_state(RB_T); + upsdebugx(2, "RB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[RB_T].noro)) { + status_set("RB"); + alarm_set("Replace Battery"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[CHRG_T].addr != NOTUSED) { + rval = get_signal_state(CHRG_T); + upsdebugx(2, "CHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[CHRG_T].noro)) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } + } else if (sigar[DISCHRG_T].addr != NOTUSED) { + rval = get_signal_state(DISCHRG_T); + upsdebugx(2, "DISCHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[DISCHRG_T].noro)) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* check for communication errors */ + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2,"Communication errors: %d", errcnt); + dstate_datastale(); + } +} + +/* shutdown UPS */ +void upsdrv_shutdown(void) +{ + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); +} + +/* print driver usage info */ +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); + addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); + addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); + addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); +} + +/* close modbus connection and free modbus context allocated memory */ +void upsdrv_cleanup(void) +{ + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } +} + +/* + * driver support functions + */ + +/* Read a modbus register */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* write a modbus register */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* returns the time elapsed since start in milliseconds */ +long time_elapsed(struct timeval *start) +{ + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; +} + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg) +{ + int rval; + int data; + struct timeval start; + long etime; + + if (!strcasecmp(cmd, "load.off")) { + if (sigar[FSD_T].addr != NOTUSED && + (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) + ) { + data = 1 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", sigar[FSD_T].addr, data); + rval = STAT_INSTCMD_HANDLED; + } + + /* if pulse has been defined and rising edge was successful */ + if (FSD_pulse_duration != NOTUSED && rval == STAT_INSTCMD_HANDLED) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for FSD_pulse_duration ms */ + while ((etime = time_elapsed(&start)) < FSD_pulse_duration); + + data = 0 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(LOG_ERR, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d, elapsed time: %lims", + sigar[FSD_T].addr, + data, + etime + ); + rval = STAT_INSTCMD_HANDLED; + } + } + } else { + upslogx(LOG_NOTICE,"load.off: failed (FSD address undefined or invalid register type) [%s] [%s]", + cmd, + arg + ); + rval = STAT_INSTCMD_FAILED; + } + } else { + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; +} + +/* read signal state from modbus RIO, returns 0|1 state or -1 on communication error */ +int get_signal_state(devstate_t state) +{ + int rval = -1; + int reg_val; + regtype_t rtype = 0; /* register type */ + int addr = -1; /* register address */ + + /* assign register address and type */ + switch (state) { + case OL_T: + addr = sigar[OL_T].addr; + rtype = sigar[OL_T].type; + break; + case OB_T: + addr = sigar[OB_T].addr; + rtype = sigar[OB_T].type; + break; + case LB_T: + addr = sigar[LB_T].addr; + rtype = sigar[LB_T].type; + break; + case HB_T: + addr = sigar[HB_T].addr; + rtype = sigar[HB_T].type; + break; + case RB_T: + addr = sigar[RB_T].addr; + rtype = sigar[RB_T].type; + break; + case CHRG_T: + addr = sigar[CHRG_T].addr; + rtype = sigar[CHRG_T].type; + break; + case DISCHRG_T: + addr = sigar[DISCHRG_T].addr; + rtype = sigar[DISCHRG_T].type; + break; + + case BYPASS_T: + case CAL_T: + case FSD_T: + case OFF_T: + case OVER_T: + case TRIM_T: + case BOOST_T: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + break; + } + + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval > -1) { + rval = reg_val; + } + upsdebugx(3, "get_signal_state: state: %d", reg_val); + return rval; +} + +/* get driver configuration parameters */ +void get_config_vars() +{ + int i; /* local index */ + + /* initialize sigar table */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + sigar[i].addr = NOTUSED; + sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ + } + + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); + + /* check if response time out (s) is set ang get the value */ + if (testvar("mod_resp_to_s")) { + mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); + } + upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); + + /* check if response time out (us) is set ang get the value */ + if (testvar("mod_resp_to_us")) { + mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); + if (mod_resp_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } + } + upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); + + /* check if byte time out (s) is set ang get the value */ + if (testvar("mod_byte_to_s")) { + mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); + } + upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); + + /* check if byte time out (us) is set ang get the value */ + if (testvar("mod_byte_to_us")) { + mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); + if (mod_byte_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } + } + upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); + + /* check if OL address is set and get the value */ + if (testvar("OL_addr")) { + sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); + if (testvar("OL_noro")) { + sigar[OL_T].noro = (int)strtol(getval("OL_noro"), NULL, 10); + if (sigar[OL_T].noro != 1) { + sigar[OL_T].noro = 0; + } + } + } + + /* check if OL register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OL_regtype")) { + sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); + if (sigar[OL_T].type < COIL || sigar[OL_T].type > HOLDING) { + sigar[OL_T].type = INPUT_B; + } + } else { + sigar[OL_T].type = INPUT_B; + } + + /* check if OB address is set and get the value */ + if (testvar("OB_addr")) { + sigar[OB_T].addr = (int)strtol(getval("OB_addr"), NULL, 0); + } + if (testvar("OB_noro")) { + sigar[OB_T].noro = (int)strtol(getval("OB_noro"), NULL, 10); + if (sigar[OB_T].noro != 1) { + sigar[OB_T].noro = 0; + } + } + + /* check if OB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OB_regtype")) { + sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[OB_T].type < COIL || sigar[OB_T].type > HOLDING) { + sigar[OB_T].type = INPUT_B; + } + } else { + sigar[OB_T].type = INPUT_B; + } + + /* check if LB address is set and get the value */ + if (testvar("LB_addr")) { + sigar[LB_T].addr = (int)strtol(getval("LB_addr"), NULL, 0); + if (testvar("LB_noro")) { + sigar[LB_T].noro = (int)strtol(getval("LB_noro"), NULL, 10); + if (sigar[LB_T].noro != 1) { + sigar[LB_T].noro = 0; + } + } + } + + /* check if LB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("LB_regtype")) { + sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[LB_T].type < COIL || sigar[LB_T].type > HOLDING) { + sigar[LB_T].type = INPUT_B; + } + } else { + sigar[LB_T].type = INPUT_B; + } + + /* check if HB address is set and get the value */ + if (testvar("HB_addr")) { + sigar[HB_T].addr = (int)strtol(getval("HB_addr"), NULL, 0); + if (testvar("HB_noro")) { + sigar[HB_T].noro = (int)strtol(getval("HB_noro"), NULL, 10); + if (sigar[HB_T].noro != 1) { + sigar[HB_T].noro = 0; + } + } + } + + /* check if HB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("HB_regtype")) { + sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); + if (sigar[HB_T].type < COIL || sigar[HB_T].type > HOLDING) { + sigar[HB_T].type = INPUT_B; + } + } else { + sigar[HB_T].type = INPUT_B; + } + + /* check if RB address is set and get the value */ + if (testvar("RB_addr")) { + sigar[RB_T].addr = (int)strtol(getval("RB_addr"), NULL, 0); + if (testvar("RB_noro")) { + sigar[RB_T].noro = (int)strtol(getval("RB_noro"), NULL, 10); + if (sigar[RB_T].noro != 1) { + sigar[RB_T].noro = 0; + } + } + } + + /* check if RB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("RB_regtype")) { + sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); + if (sigar[RB_T].type < COIL || sigar[RB_T].type > HOLDING) { + sigar[RB_T].type = INPUT_B; + } + } else { + sigar[RB_T].type = INPUT_B; + } + + /* check if CHRG address is set and get the value */ + if (testvar("CHRG_addr")) { + sigar[CHRG_T].addr = (int)strtol(getval("CHRG_addr"), NULL, 0); + if (testvar("CHRG_noro")) { + sigar[CHRG_T].noro = (int)strtol(getval("CHRG_noro"), NULL, 10); + if (sigar[CHRG_T].noro != 1) { + sigar[CHRG_T].noro = 0; + } + } + } + + /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("CHRG_regtype")) { + sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); + if (sigar[CHRG_T].type < COIL || sigar[CHRG_T].type > HOLDING) { + sigar[CHRG_T].type = INPUT_B; + } + } else { + sigar[CHRG_T].type = INPUT_B; + } + + /* check if DISCHRG address is set and get the value */ + if (testvar("DISCHRG_addr")) { + sigar[DISCHRG_T].addr = (int)strtol(getval("DISCHRG_addr"), NULL, 0); + if (testvar("DISCHRG_noro")) { + sigar[DISCHRG_T].noro = (int)strtol(getval("DISCHRG_noro"), NULL, 10); + if (sigar[DISCHRG_T].noro != 1) { + sigar[DISCHRG_T].noro = 0; + } + } + } + + /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("DISCHRG_regtype")) { + sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); + if (sigar[DISCHRG_T].type < COIL || sigar[DISCHRG_T].type > HOLDING) { + sigar[DISCHRG_T].type = INPUT_B; + } + } else { + sigar[DISCHRG_T].type = INPUT_B; + } + + /* check if FSD address is set and get the value */ + if (testvar("FSD_addr")) { + sigar[FSD_T].addr = (int)strtol(getval("FSD_addr"), NULL, 0); + if (testvar("FSD_noro")) { + sigar[FSD_T].noro = (int)strtol(getval("FSD_noro"), NULL, 10); + if (sigar[FSD_T].noro != 1) { + sigar[FSD_T].noro = 0; + } + } + } + + /* check if FSD register type is set and get the value otherwise set to COIL */ + if (testvar("FSD_regtype")) { + sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); + if (sigar[FSD_T].type < COIL || sigar[FSD_T].type > HOLDING) { + sigar[FSD_T].type = COIL; + } + } else { + sigar[FSD_T].type = COIL; + } + + /* check if FSD pulse duration is set and get the value */ + if (testvar("FSD_pulse_duration")) { + FSD_pulse_duration = (int) strtol(getval("FSD_pulse_duration"), NULL, 10); + } + upsdebugx(2, "FSD_pulse_duration %d", FSD_pulse_duration); + + /* debug loop over signal array */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + if (sigar[i].addr != NOTUSED) { + char *signame; + switch (i) { + case OL_T: + signame = "OL"; + break; + case OB_T: + signame = "OB"; + break; + case LB_T: + signame = "LB"; + break; + case HB_T: + signame = "HB"; + break; + case RB_T: + signame = "RB"; + break; + case FSD_T: + signame = "FSD"; + break; + case CHRG_T: + signame = "CHRG"; + break; + case DISCHRG_T: + signame = "DISCHRG"; + break; + default: + signame = "NOTUSED"; + break; + } + upsdebugx(2, "%s, addr:0x%x, type:%d", signame, sigar[i].addr, sigar[i].type); + } + } +} + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port) +{ + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; +} + +/* reconnect to modbus server upon connection error */ +void modbus_reconnect() +{ + int rval; + + upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); + + /* clear current modbus context */ + modbus_close(mbctx); + modbus_free(mbctx); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} \ No newline at end of file diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h new file mode 100644 index 0000000000..838af3971d --- /dev/null +++ b/drivers/adele_cbi.h @@ -0,0 +1,196 @@ +/* adele_cbi.h - Driver for generic UPS connected via modbus RIO + * + * Copyright (C) + * 2021 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ADELE_CBI_H +#define ADELE_CBI_H + +/* UPS device details */ +#define DEVICE_MFR "ADELE" +#define DEVICE_MODEL "CB/CBI" + +/* serial access parameters */ +#define BAUD_RATE 9600 +#define PARITY 'N' +#define DATA_BIT 8 +#define STOP_BIT 1 + +/* + * modbus response and byte timeouts + * us: 1 - 999999 + */ +#define MODRESP_TIMEOUT_s 0 +#define MODRESP_TIMEOUT_us 200000 +#define MODBYTE_TIMEOUT_s 0 +#define MODBYTE_TIMEOUT_us 50000 + +/* modbus access parameters */ +#define MODBUS_SLAVE_ID 5 + +/* definition of register type */ +enum regtype { + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING +}; +typedef enum regtype regtype_t; + +/* UPS device state enum */ +enum devstate { + CHRG = 0, /* Charging status */ + BATV, /* Battery voltage */ + BCEF = 6, /* Battery charge efficiency factor (CEF) */ + BSOH, /* Battery state-of-health */ + BSOC = 9, /* Battery state-of-charge */ + BTMP = 11, /* Battery temperature in Kelvin units */ + PMNG = 15, /* Power management */ +}; +typedef enum devstate devstate_t; + +/* BIT MASKS and VALUES */ +#define CHRG_NONE 0 +#define CHRG_RECV 1 +#define CHRG_BULK 2 +#define CHRG_ABSR 3 +#define CHRG_FLOAT 4 +#define PMNG_BKUP 0 +#define PMNG_CHRG 1 +#define PMNG_BOOST 2 +#define PMNG_NCHRG 3 + +/* UPS state signal attributes */ +struct regattr { + int num; + int saddr; /* register start address */ + int xaddr; /* register hex address */ + float scale; /* scale */ + regtype_t type; /* register type */ +}; +typedef struct regattr regattr_t; + +/* ADELE CBI registers */ +regattr_t regs[] = { + {40005, 0, 0, 1, HOLDING}, /* Charging status */ + {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ + {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ + {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ + {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ + {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ + {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ + {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ + {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ + {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ + {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ + {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ + {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ + {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ + {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ + {40006, 0, 0, 1, HOLDING}, /* Power management + * 0:Backup 1:Charging 2:boost 3:Not charging + */ + {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ + {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ + {40010, 0, 0, 1, HOLDING}, /* Software ID */ + {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ + {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ + {40067, 0, 0, 1, HOLDING}, /* Product name */ + {40039, 0, 0, 1, HOLDING}, /* Device variant */ + {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ + {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ + {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ + {40020, 0, 0, 1, HOLDING}, /* Output load current */ + {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ + {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ + {40050, 0, 0, .1, HOLDING}, /* Ah charged */ + {40051, 0, 0, 1, HOLDING}, /* Total run time */ + {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ + {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ + {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ + {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ + {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ + {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ + {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ + {40056, 0, 0, 1, HOLDING}, /* Number of overtemperature inside events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low AC input voltage events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High AC input voltage events at mains input */ + {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ + {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ + {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ + {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ + {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ + {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ + {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ + {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ + {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ + {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ + {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ + {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ + {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ + {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ + {40120, 0, 0, 1, HOLDING}, /* Zero-SoC reference */ + {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ + {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ + {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ + {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ + {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ + {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ + {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ + {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ + {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ + {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ + {40082, 0, 0, 1, HOLDING}, /* Float voltage */ + {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ + {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ + {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ + {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ + {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ + {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ + {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ + {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ + {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ + {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ + {40065, 0, 0, 1, HOLDING}, /* History clear all */ + {40066, 0, 0, 1, HOLDING}, /* Factory settings */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed 1 = Backup not allowed 0 */ + {40104, 0, 0, 1, HOLDING}, /* Time buffering */ + {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ + {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ + {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ + {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ + {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ + {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ + {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ + {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ + {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ + {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ + {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ + {40043, 0, 0, 1, HOLDING}, /* Device failure */ + {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ + {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ + {40046, 0, 0, 1, HOLDING}, /* Input mains on / backup */ + {40038, 0, 0, 1, HOLDING} /* Load alarm */ +}; + +#define NUMOF_REGS 14 +#define NOTUSED -1 + +#endif /* ADELE_CBI_H */ From 9edbec564d8ee6734bf49dcd5c292d54d55aee5e Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 6 Jan 2022 01:10:09 +0200 Subject: [PATCH 048/700] Makefile.am modifications --- drivers/Makefile.am | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/Makefile.am b/drivers/Makefile.am index e2fd28ff4e..b62262500c 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -50,7 +50,7 @@ SERIAL_USB_DRIVERLIST = \ nutdrv_qx NEONXML_DRIVERLIST = netxml-ups MACOSX_DRIVERLIST = macosx-ups -MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 +MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 adele_cbi LINUX_I2C_DRIVERLIST = asem pijuice # distribute all drivers, even ones that are not built by default @@ -273,6 +273,8 @@ phoenixcontact_modbus_SOURCES = phoenixcontact_modbus.c phoenixcontact_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) generic_modbus_SOURCES = generic_modbus.c generic_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) +adele_cbi_SOURCES = adele_cbi.c +adele_cbi_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) # Huawei UPS2000 driver # (this is both a Modbus and a serial driver) @@ -329,7 +331,7 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h b xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ apc-pdu-mib.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h \ eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h \ - hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h + hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adele_cbi # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, From b9ea6f43e8f528fcb4ad44272d327d98a8324e7e Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Fri, 7 Jan 2022 01:55:04 +0200 Subject: [PATCH 049/700] register status values and masks added --- drivers/adele_cbi.h | 129 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 18 deletions(-) diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 838af3971d..e2cc76643a 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -23,6 +23,8 @@ #ifndef ADELE_CBI_H #define ADELE_CBI_H +#include + /* UPS device details */ #define DEVICE_MFR "ADELE" #define DEVICE_MODEL "CB/CBI" @@ -54,36 +56,124 @@ enum regtype { }; typedef enum regtype regtype_t; -/* UPS device state enum */ -enum devstate { - CHRG = 0, /* Charging status */ - BATV, /* Battery voltage */ - BCEF = 6, /* Battery charge efficiency factor (CEF) */ - BSOH, /* Battery state-of-health */ - BSOC = 9, /* Battery state-of-charge */ - BTMP = 11, /* Battery temperature in Kelvin units */ - PMNG = 15, /* Power management */ +struct prodname { + uint16_t val; + char *name; }; -typedef enum devstate devstate_t; +typedef struct prodname prodname_t; -/* BIT MASKS and VALUES */ +/* product name */ +prodname_t prdn[] = { + {1, "CBI1235A"}, + {2, "CBI2420A"}, + {3, "CBI4810A"}, + {4, "CBI2801224"}, + {7, "CBI480W"}, + {8, "CB122410A"}, + {9, "CB480W"}, + {11, "CB12245AJ"}, + {12, "CB1235A"}, + {13, "CB2420A"}, + {14, "CB4810A"} +}; + +/* charging status */ +char *chrgs[] = { + "none", + "recovery", + "bulk", + "float" +}; + +/* + * BIT MASKS and VALUES + */ + +/* Charging status */ #define CHRG_NONE 0 #define CHRG_RECV 1 #define CHRG_BULK 2 #define CHRG_ABSR 3 #define CHRG_FLOAT 4 + +/* power management */ #define PMNG_BKUP 0 #define PMNG_CHRG 1 #define PMNG_BOOST 2 #define PMNG_NCHRG 3 +/* product name */ +#define PRDN_MAX 14 + +/* Mains status */ +#define MAINS_AVAIL 0x0001 /* available */ +#define SHUTD_REQST 0x0002 /* shutdown requested */ + +/* AC input voltage alarms */ +#define VACA_HIALRM 0x0001 /* high alarm */ +#define VACA_LOALRM 0x0002 /* low alarm */ + +/* Onboard temperature alarm */ +#define OBTA_HIALRM 1 /* high alarm */ + +/* Device failure */ +#define DEVF_RCALRM 0x0001 /* rectifier failure */ +#define DEVF_INALRM 0x0006 /* internal failure */ +#define DEVF_LFNAVL 0x0008 /* lifetest not available */ + +/* Battery temp sensor failure */ +#define BTSF_FCND 0x0001 /* connection fault */ +#define BTSF_NCND 0x0001 /* not connected */ + +/* Battery voltage alarm */ +#define BVAL_HIALRM 0x0001 /* high voltage */ +#define BVAL_LOALRM 0x0002 /* low voltage */ +#define BVAL_BSTSFL 0x0004 /* battery start with battery flat */ + +/* SoH and SoC alarms */ +#define SHSC_HIRESI 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC 0x0040 /* low state of charge */ + +/* Battery status alarms */ +#define BSTA_REVPOL 0x0001 /* reversed polarity */ +#define BSTA_NOCNND 0x0002 /* not connected */ +#define BSTA_CLSHCR 0x0004 /* cell short circuit */ +#define BSTA_SULPHD 0x0008 /* sulphated */ +#define BSTA_CHEMNS 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT 0x0020 /* connection fault */ + + +/* UPS device state enum */ +enum devstate { + CHRG = 0, /* Charging status */ + BATV, /* Battery voltage */ + BCEF = 6, /* Battery charge efficiency factor (CEF) */ + BSOH, /* Battery state-of-health */ + BSOC = 9, /* Battery state-of-charge */ + BTMP = 11, /* Battery temperature in Kelvin units */ + PMNG = 15, /* Power management */ + PRDN = 21, /* Product name */ + FSD = 80, /* Force shutdown */ + BSTA = 89, /* Battery status alarms */ + SCSH = 90, /* SoH and SoC alarms */ + BVAL = 91, /* Battery voltage alarm */ + BTSF = 92, /* Battery temp sensor failure */ + DEVF = 93, /* Device failure */ + OBTA = 94, /* On board temp alarm */ + VACA = 95, /* VAC alarms */ + MAIN = 96 /* Mains status */ +}; +typedef enum devstate devstate_t; + /* UPS state signal attributes */ struct regattr { int num; - int saddr; /* register start address */ + int saddr; /* register start address */ int xaddr; /* register hex address */ float scale; /* scale */ - regtype_t type; /* register type */ + regtype_t type; /* register type */ }; typedef struct regattr regattr_t; @@ -129,9 +219,9 @@ regattr_t regs[] = { {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ - {40056, 0, 0, 1, HOLDING}, /* Number of overtemperature inside events */ - {40054, 0, 0, 1, HOLDING}, /* Number of low AC input voltage events at mains input */ - {40055, 0, 0, 1, HOLDING}, /* Number of High AC input voltage events at mains input */ + {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ @@ -170,7 +260,10 @@ regattr_t regs[] = { {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ {40065, 0, 0, 1, HOLDING}, /* History clear all */ {40066, 0, 0, 1, HOLDING}, /* Factory settings */ - {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed 1 = Backup not allowed 0 */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed + * 1 = Backup not allowed 0 + * */ + {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ {40104, 0, 0, 1, HOLDING}, /* Time buffering */ {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ @@ -186,7 +279,7 @@ regattr_t regs[] = { {40043, 0, 0, 1, HOLDING}, /* Device failure */ {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ - {40046, 0, 0, 1, HOLDING}, /* Input mains on / backup */ + {40046, 0, 0, 1, HOLDING}, /* Mains status */ {40038, 0, 0, 1, HOLDING} /* Load alarm */ }; From 8584493610d314e78a2bdfad97fe7edb378df13e Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Sat, 8 Jan 2022 02:49:31 +0200 Subject: [PATCH 050/700] structure device data, code get_dev_state, in progress --- drivers/adele_cbi.c | 202 +++++++++++++++++++++++++++++++++----------- drivers/adele_cbi.h | 112 ++++++++++++++++-------- 2 files changed, 230 insertions(+), 84 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index f24e13b575..d254d4c54b 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -1,4 +1,4 @@ -/* adele_cbi.c - Driver for adele CB/CBI UPS +/* adele_cbi.c - driver for ADELE CB/CBI DC-UPS * * Copyright (C) * 2021 Dimitris Economou @@ -62,7 +62,7 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); int upscmd(const char *cmd, const char *arg); /* read signal status */ -int get_signal_state(devstate_t state); +int get_signal_state(devreg_t state); /* count the time elapsed since start */ long time_elapsed(struct timeval *start); @@ -297,7 +297,7 @@ void upsdrv_shutdown(void) /* wait for an increasing time interval before sending shutdown command */ while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); cnt--; } switch (rval) { @@ -346,6 +346,45 @@ void upsdrv_cleanup(void) * driver support functions */ +/* initialize register start address and hex address from register number */ +void reginit() +{ + int i; /* local index */ + + for (i = 1; i < MODBUS_NUMOF_REGS; i++) { + int rnum = regs[i].num; + switch (regs[i].type) { + case COIL: + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x0 + regs[i].num - 1; + break; + case INPUT_B: + rnum -= 10000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x10000 + rnum - 1; + break; + case INPUT_R: + rnum -= 30000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x30000 + rnum - 1; + break; + case HOLDING: + rnum -= 40000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x40000 + rnum - 1; + break; + default: + upslogx(LOG_ERR, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); + upsdebugx(3, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); + } + upsdebugx(3, "register num:%d, type: %d saddr %d, xaddr x%x\n", regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr + ); + } +} + /* Read a modbus register */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) { @@ -385,7 +424,7 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); + upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); break; } if (rval == -1) { @@ -561,52 +600,116 @@ int upscmd(const char *cmd, const char *arg) return rval; } -/* read signal state from modbus RIO, returns 0|1 state or -1 on communication error */ -int get_signal_state(devstate_t state) +/* read device state, returns 0 on success or -1 on communication error */ +int get_dev_state(devreg_t regnum, devstate_t *state) { - int rval = -1; - int reg_val; - regtype_t rtype = 0; /* register type */ - int addr = -1; /* register address */ - - /* assign register address and type */ - switch (state) { - case OL_T: - addr = sigar[OL_T].addr; - rtype = sigar[OL_T].type; - break; - case OB_T: - addr = sigar[OB_T].addr; - rtype = sigar[OB_T].type; - break; - case LB_T: - addr = sigar[LB_T].addr; - rtype = sigar[LB_T].type; - break; - case HB_T: - addr = sigar[HB_T].addr; - rtype = sigar[HB_T].type; - break; - case RB_T: - addr = sigar[RB_T].addr; - rtype = sigar[RB_T].type; - break; - case CHRG_T: - addr = sigar[CHRG_T].addr; - rtype = sigar[CHRG_T].type; + int i; /* local index */ + int rval = -1; /* return value */ + uint reg_val; /* register value */ + regtype_t rtype = 0; /* register type */ + int addr = regs[regnum].xaddr; + int rtype = regs[regnum].type; + + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval == -1) { + return rval; + } + + /* process register data */ + switch (regnum) { + case CHRG: + if (reg_val == CHRG_NONE) { + state->charge.state = CHRG_NONE; + state->charge.info = chrgs_i[CHRG_NONE]; + } else if (reg_val == CHRG_RECV) { + state->charge.state = CHRG_RECV; + state->charge.info = chrgs_i[CHRG_RECV]; + } else if (reg_val == CHRG_BULK) { + state->charge.state = CHRG_BULK; + state->charge.info = chrgs_i[CHRG_BULK]; + } else if (reg_val == CHRG_ABSR) { + state->charge.state = CHRG_ABSR; + state->charge.info = chrgs_i[CHRG_ABSR]; + } else if (reg_val == CHRG_FLOAT) { + state->charge.state = CHRG_FLOAT; + state->charge.info = chrgs_i[CHRG_FLOAT]; + } break; - case DISCHRG_T: - addr = sigar[DISCHRG_T].addr; - rtype = sigar[DISCHRG_T].type; + case BATV: /* "battery.voltage" */ + case LVDC: /* "output.voltage" */ + case LCUR: /* "output.current" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ + int n = snprintf(NULL, 0, "%f", fval); + char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(fval_s, "%f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + break; + case BSOH: + case BCEF: + case VAC: /* "input.voltage" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + int n = snprintf(NULL, 0, "%d", reg_val); + char *reg_val_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + break; + case BSOC: /* "battery.charge" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = (double )reg_val * regs[BSOC].scale; + int n = snprintf(NULL, 0, "%f", fval); + char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(fval_s, "%f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + break; + case BTMP: /* "battery.temperature" */ + case OTMP: /* "ups.temperature" */ + state->reg.val16 = reg_val; + double fval = reg_val - 273.15; + int n = snprintf(NULL, 0, "%f", fval); + char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(fval_s, "%f", fval); + state->reg.strval = fval_s; + break; + case PMNG: /* "ups.status" & "battery.charge" */ + if (reg_val == PMNG_BCKUP) { + state->power.state = PMNG_BCKUP; + state->power.info = pwrmng_i[PMNG_BCKUP]; + } else if (reg_val == PMNG_CHRGN) { + state->power.state = PMNG_CHRGN; + state->power.info = pwrmng_i[PMNG_CHRGN]; + } else if (reg_val == PMNG_BOOST) { + state->power.state = PMNG_BOOST; + state->power.info = pwrmng_i[PMNG_BOOST]; + } else if (reg_val == PMNG_NCHRG) { + state->power.state = PMNG_NCHRG; + state->power.info = pwrmng_i[PMNG_NCHRG]; + } break; - - case BYPASS_T: - case CAL_T: - case FSD_T: - case OFF_T: - case OVER_T: - case TRIM_T: - case BOOST_T: + case PRDN: /* "ups.model" */ + for (i = 0; i < DEV_NUMOF_MODELS; i++) { + if (prdnm_i[i].val == reg_val) { + break; + } + } + state->product.val = reg_val; + state->product.name = prdnm_i[i].name; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" @@ -622,10 +725,7 @@ int get_signal_state(devstate_t state) break; } - rval = register_read(mbctx, addr, rtype, ®_val); - if (rval > -1) { - rval = reg_val; - } + upsdebugx(3, "get_signal_state: state: %d", reg_val); return rval; } diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index e2cc76643a..14234d23fe 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -1,4 +1,4 @@ -/* adele_cbi.h - Driver for generic UPS connected via modbus RIO +/* adele_cbi.h - Driver for ADELE CB/CBI DC-UPS * * Copyright (C) * 2021 Dimitris Economou @@ -47,6 +47,12 @@ /* modbus access parameters */ #define MODBUS_SLAVE_ID 5 +/* number of modbus registers */ +#define MODBUS_NUMOF_REGS 98 + +/* number of device models */ +#define DEV_NUMOF_MODELS 10 + /* definition of register type */ enum regtype { COIL = 0, @@ -56,14 +62,13 @@ enum regtype { }; typedef enum regtype regtype_t; +/* product name info, "device.model" */ struct prodname { uint16_t val; char *name; }; typedef struct prodname prodname_t; - -/* product name */ -prodname_t prdn[] = { +prodname_t prdnm_i[] = { {1, "CBI1235A"}, {2, "CBI2420A"}, {3, "CBI4810A"}, @@ -77,13 +82,42 @@ prodname_t prdn[] = { {14, "CB4810A"} }; -/* charging status */ -char *chrgs[] = { +/* charging status info, "battery.charger.status" */ +char *chrgs_i[] = { "none", - "recovery", - "bulk", - "float" + "recovery", /* "resting" */ + "bulk", /* "charging" */ + "absorb", /* "charging" */ + "float" /* "floating" */ +}; +struct chrgs { + int state; + char *info; +}; +typedef struct chrgs chrgs_t; + +/* power management info, "ups.status", "battery.charger.status" */ +char *pwrmng_i[] = { + "backup", /* "OB", "discharging" */ + "charging", /* "OL" */ + "boost", + "not charging" +}; +struct pwrmng { + int state; + char *info; }; +typedef struct pwrmng pwrmng_t; + +/* general modbus register value */ +struct reg { + union { + uint16_t val16; + uint8_t val8; + }; + char *strval; +}; +typedef struct reg reg_t; /* * BIT MASKS and VALUES @@ -97,8 +131,8 @@ char *chrgs[] = { #define CHRG_FLOAT 4 /* power management */ -#define PMNG_BKUP 0 -#define PMNG_CHRG 1 +#define PMNG_BCKUP 0 +#define PMNG_CHRGN 1 #define PMNG_BOOST 2 #define PMNG_NCHRG 3 @@ -145,29 +179,35 @@ char *chrgs[] = { #define BSTA_CNNFLT 0x0020 /* connection fault */ -/* UPS device state enum */ -enum devstate { - CHRG = 0, /* Charging status */ - BATV, /* Battery voltage */ +/* UPS device reg enum */ +enum devreg { + CHRG = 0, /* Charging status, "battery.charger.status" */ + BATV, /* Battery voltage, "battery.voltage" */ BCEF = 6, /* Battery charge efficiency factor (CEF) */ BSOH, /* Battery state-of-health */ - BSOC = 9, /* Battery state-of-charge */ - BTMP = 11, /* Battery temperature in Kelvin units */ - PMNG = 15, /* Power management */ - PRDN = 21, /* Product name */ - FSD = 80, /* Force shutdown */ + BSOC = 9, /* Battery state-of-charge, "battery.charge" */ + BTMP = 11, /* Battery temperature in Kelvin units, "battery.temperature" */ + PMNG = 15, /* Power management, "ups.status" */ + OTMP = 20, /* Onboard temperature, "ups.temperature" */ + PRDN, /* Product name, "ups.model" */ + VAC = 24, /* AC voltage, "input.voltage" */ + LVDC, /* Load voltage, "output.voltage" */ + LCUR, /* Load current, "output.current" */ + BINH = 79, /* Backup inhibit */ + FSD, /* Force shutdown */ + TBUF, /* Time buffering */ BSTA = 89, /* Battery status alarms */ - SCSH = 90, /* SoH and SoC alarms */ - BVAL = 91, /* Battery voltage alarm */ - BTSF = 92, /* Battery temp sensor failure */ - DEVF = 93, /* Device failure */ - OBTA = 94, /* On board temp alarm */ - VACA = 95, /* VAC alarms */ - MAIN = 96 /* Mains status */ + SCSH, /* SoH and SoC alarms */ + BVAL, /* Battery voltage alarm */ + BTSF, /* Battery temp sensor failure */ + DEVF, /* Device failure */ + OBTA, /* On board temp alarm */ + VACA, /* VAC alarms */ + MAIN /* Mains status */ }; -typedef enum devstate devstate_t; +typedef enum devreg devreg_t; -/* UPS state signal attributes */ +/* UPS register attributes */ struct regattr { int num; int saddr; /* register start address */ @@ -177,6 +217,16 @@ struct regattr { }; typedef struct regattr regattr_t; +/* UPS device state info union */ +union devstate { + prodname_t product; + chrgs_t charge; + pwrmng_t power; + reg_t reg; +}; + +typedef union devstate devstate_t; + /* ADELE CBI registers */ regattr_t regs[] = { {40005, 0, 0, 1, HOLDING}, /* Charging status */ @@ -282,8 +332,4 @@ regattr_t regs[] = { {40046, 0, 0, 1, HOLDING}, /* Mains status */ {40038, 0, 0, 1, HOLDING} /* Load alarm */ }; - -#define NUMOF_REGS 14 -#define NOTUSED -1 - #endif /* ADELE_CBI_H */ From 38ca88c6da133c96cd4c58d425cfedf3e13f1920 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Sun, 9 Jan 2022 03:21:08 +0200 Subject: [PATCH 051/700] alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress --- drivers/adele_cbi.c | 293 +++++++++++++++++++++++++++++++++----------- drivers/adele_cbi.h | 198 ++++++++++++++++++++++++------ 2 files changed, 382 insertions(+), 109 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index d254d4c54b..f81137297b 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -25,27 +25,29 @@ #include #include -#define DRIVER_NAME "NUT Adele CBI driver" +#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" #define DRIVER_VERSION "0.01" /* variables */ -static modbus_t *mbctx = NULL; /* modbus memory context */ -static regattr_t sigar[NUMOF_SIG_STATES]; /* array of ups signal attributes */ -static int errcnt = 0; /* modbus access error counter */ - -static char *device_mfr = DEVICE_MFR; /* device manufacturer */ -static char *device_model = DEVICE_MODEL; /* device model */ -static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ -static char ser_parity = PARITY; /* serial port parity */ -static int ser_data_bit = DATA_BIT; /* serial port data bit */ -static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ -static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ -static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ -static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ -static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ -static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static int errcnt = 0; /* modbus access error counter */ +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static char *device_type = DEVICE_TYPE; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ +/* initialize register start address and hex address from register number */ +void reginit(); + /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); @@ -61,8 +63,8 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg); -/* read signal status */ -int get_signal_state(devreg_t state); +/* get device state */ +int get_dev_state(devreg_t regnum, devstate_t *state); /* count the time elapsed since start */ long time_elapsed(struct timeval *start); @@ -82,64 +84,71 @@ upsdrv_info_t upsdrv_info = { * driver functions */ +/* read configuration variables from ups.conf and connect to ups device */ +void upsdrv_initups(void) +{ + int rval; + upsdebugx(2, "upsdrv_initups"); + + reginit(); + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response time out to 200ms */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} + /* initialize ups driver information */ -void upsdrv_initinfo(void) { +void upsdrv_initinfo(void) +{ + devstate_t ds; /* device state */ upsdebugx(2, "upsdrv_initinfo"); /* set device information */ dstate_setinfo("device.mfr", "%s", device_mfr); dstate_setinfo("device.model", "%s", device_model); + dstate_setinfo("device.type", "%s", device_type); - /* register instant commands */ - if (sigar[FSD_T].addr != NOTUSED) { - dstate_addcmd("load.off"); - } + /* read ups model */ + get_dev_state(PRDN, &ds); + dstate_setinfo("ups.model", "%s", ds.product.name); + + /* register instant commands */ + dstate_addcmd("load.off"); /* set callback for instant commands */ upsh.instcmd = upscmd; } -/* open serial connection and connect to modbus RIO */ -void upsdrv_initups(void) -{ - int rval; - upsdebugx(2, "upsdrv_initups"); - - get_config_vars(); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response time out to 200ms */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -} /* update UPS signal state */ void upsdrv_updateinfo(void) @@ -147,11 +156,20 @@ void upsdrv_updateinfo(void) int rval; int online = -1; /* keep online state */ errcnt = 0; + devstate_t ds; /* device state */ upsdebugx(2, "upsdrv_updateinfo"); status_init(); /* initialize ups.status update */ alarm_init(); /* initialize ups.alarm update */ + /* get "battery.charger.status" */ + get_dev_state(CHRG, &ds); + dstate_setinfo("battery.charger.status", "%s", ds.charge.info); + + /* get "battery.voltage" */ + get_dev_state(BATV, &ds); + dstate_setinfo("battery.charger.status", "%s", ds.reg.strval); + /* * update UPS status regarding MAINS state either via OL | OB. * if both statuses are mapped to contacts then only OL is evaluated. @@ -600,16 +618,18 @@ int upscmd(const char *cmd, const char *arg) return rval; } -/* read device state, returns 0 on success or -1 on communication error */ +/* read device state, returns 0 on success or -1 on communication error + it formats state depending on register semantics */ int get_dev_state(devreg_t regnum, devstate_t *state) { int i; /* local index */ - int rval = -1; /* return value */ + int rval; /* return value */ uint reg_val; /* register value */ - regtype_t rtype = 0; /* register type */ - int addr = regs[regnum].xaddr; - int rtype = regs[regnum].type; + regtype_t rtype; /* register type */ + int addr; /* register address */ + addr = regs[regnum].xaddr; + rtype = regs[regnum].type; rval = register_read(mbctx, addr, rtype, ®_val); if (rval == -1) { return rval; @@ -650,6 +670,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->reg.strval = "0"; } break; + case TBUF: case BSOH: case BCEF: case VAC: /* "input.voltage" */ @@ -710,6 +731,137 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->product.val = reg_val; state->product.name = prdnm_i[i].name; break; + case BSTA: + if (reg_val & BSTA_REVPOL_M) { + bsta.alrm[BSTA_REVPOL_I].actv = 1; + } else { + bsta.alrm[BSTA_REVPOL_I].actv = 0; + } + if (reg_val & BSTA_NOCNND_M) { + bsta.alrm[BSTA_NOCNND_I].actv = 1; + } else { + bsta.alrm[BSTA_NOCNND_I].actv = 0; + } + if (reg_val & BSTA_CLSHCR_M) { + bsta.alrm[BSTA_CLSHCR_I].actv = 1; + } else { + bsta.alrm[BSTA_CLSHCR_I].actv = 0; + } + if (reg_val & BSTA_SULPHD_M) { + bsta.alrm[BSTA_SULPHD_I].actv = 1; + } else { + bsta.alrm[BSTA_SULPHD_I].actv = 0; + } + if (reg_val & BSTA_CHEMNS_M) { + bsta.alrm[BSTA_CHEMNS_I].actv = 1; + } else { + bsta.alrm[BSTA_CHEMNS_I].actv = 0; + } + if (reg_val & BSTA_CNNFLT_M) { + bsta.alrm[BSTA_CNNFLT_I].actv = 1; + } else { + bsta.alrm[BSTA_CNNFLT_I].actv = 0; + } + state->alrm = &bsta; + break; + case SCSH: + if (reg_val & SHSC_HIRESI_M) { + shsc.alrm[SHSC_HIRESI_I].actv = 1; + } else { + shsc.alrm[SHSC_HIRESI_I].actv = 0; + } + if (reg_val & SHSC_LOCHEF_M) { + shsc.alrm[SHSC_LOCHEF_I].actv = 1; + } else { + shsc.alrm[SHSC_LOCHEF_I].actv = 0; + } + if (reg_val & SHSC_LOEFCP_M) { + shsc.alrm[SHSC_LOEFCP_I].actv = 1; + } else { + shsc.alrm[SHSC_LOEFCP_I].actv = 0; + } + if (reg_val & SHSC_LOWSOC_M) { + shsc.alrm[SHSC_LOWSOC_I].actv = 1; + } else { + shsc.alrm[SHSC_LOWSOC_I].actv = 0; + } + state->alrm = &shsc; + break; + case BVAL: + if (reg_val & BVAL_HIALRM_M) { + bval.alrm[BVAL_HIALRM_I].actv = 1; + } else { + bval.alrm[BVAL_HIALRM_I].actv = 0; + } + if (reg_val & BVAL_LOALRM_M) { + bval.alrm[BVAL_LOALRM_I].actv = 1; + } else { + bval.alrm[BVAL_LOALRM_I].actv = 0; + } + if (reg_val & BVAL_BSTSFL_M) { + bval.alrm[BVAL_BSTSFL_I].actv = 1; + } else { + bval.alrm[BVAL_BSTSFL_I].actv = 0; + } + state->alrm = bval; + break; + case BTSF: + if (reg_val & BTSF_FCND_M) { + btsf.alrm[BTSF_FCND_I].actv = 1; + } else { + btsf.alrm[BTSF_FCND_I].actv = 0; + } + if (reg_val & BTSF_NCND_M) { + btsf.alrm[BTSF_NCND_I].actv = 1; + } else { + btsf.alrm[BTSF_NCND_I].actv = 0; + } + state->alrm = &btsf; + break; + case DEVF: + if (reg_val & DEVF_RCALRM_M) { + devf.alrm[DEVF_RCALRM_I].actv = 1; + } else { + devf.alrm[DEVF_RCALRM_I].actv = 0; + } + if (reg_val & DEVF_INALRM_M) { + devf.alrm[DEVF_INALRM_I].actv = 1; + } else { + devf.alrm[DEVF_INALRM_I].actv = 0; + } + if (reg_val & DEVF_LFNAVL_M) { + devf.alrm[DEVF_LFNAVL_I].actv = 1; + } else { + devf.alrm[DEVF_LFNAVL_I].actv = 0; + } + state->alrm = &devf; + break; + case VACA: + if (reg_val & VACA_HIALRM_M) { + vaca.alrm[VACA_HIALRM_I].actv = 1; + } else { + vaca.alrm[VACA_HIALRM_I].actv = 0; + } + if (reg_val == VACA_LOALRM_M) { + vaca.alrm[VACA_LOALRM_I].actv = 1; + } else { + vaca.alrm[VACA_LOALRM_I].actv = 0; + } + state->alrm = &vaca; + break; + case MAIN: + if (reg_val & MAINS_AVAIL_M) { + mains.alrm[MAINS_AVAIL_I].actv = 1; + } else { + mains.alrm[MAINS_AVAIL_I].actv = 0; + } + if (reg_val == SHUTD_REQST_M) { + mains.alrm[SHUTD_REQST_I].actv = 1; + } else { + mains.alrm[SHUTD_REQST_I].actv = 0; + } + state->alrm = &mains; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" @@ -725,8 +877,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) break; } - - upsdebugx(3, "get_signal_state: state: %d", reg_val); + upsdebugx(3, "get_dev_state: state: %d", reg_val); return rval; } diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 14234d23fe..6cd05989a5 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -27,6 +27,7 @@ /* UPS device details */ #define DEVICE_MFR "ADELE" +#define DEVICE_TYPE "DC-UPS" #define DEVICE_MODEL "CB/CBI" /* serial access parameters */ @@ -85,10 +86,10 @@ prodname_t prdnm_i[] = { /* charging status info, "battery.charger.status" */ char *chrgs_i[] = { "none", - "recovery", /* "resting" */ - "bulk", /* "charging" */ - "absorb", /* "charging" */ - "float" /* "floating" */ + "resting", /* recovering */ + "charging", /* bulk */ + "charging", /* absorb */ + "floating" /* float */ }; struct chrgs { int state; @@ -119,6 +120,20 @@ struct reg { }; typedef struct reg reg_t; +/* general alarm struct */ +struct alrm { + int actv; /* active flag */ + char *descr; /* description field */ +}; +typedef struct alrm alrm_t; + +/* general alarm array */ +struct alrm_ar { + int alrm_c; /* alarm count */ + alrm_t alrm[]; /* alarm array */ +}; +typedef struct alrm_ar alrm_ar_t; + /* * BIT MASKS and VALUES */ @@ -139,45 +154,151 @@ typedef struct reg reg_t; /* product name */ #define PRDN_MAX 14 -/* Mains status */ -#define MAINS_AVAIL 0x0001 /* available */ -#define SHUTD_REQST 0x0002 /* shutdown requested */ +/* Mains alarm masks */ +#define MAINS_AVAIL_M 0x0001 /* 0: available 1: not available */ +#define SHUTD_REQST_M 0x0002 /* shutdown requested */ -/* AC input voltage alarms */ -#define VACA_HIALRM 0x0001 /* high alarm */ -#define VACA_LOALRM 0x0002 /* low alarm */ +/* Mains alarm indices */ +#define MAINS_AVAIL_I 0 /* 0: available 1: not available */ +#define SHUTD_REQST_I 1 /* shutdown requested */ + +/* AC input voltage alarm masks */ +#define VACA_HIALRM_M 0x0001 /* high alarm */ +#define VACA_LOALRM_M 0x0002 /* low alarm */ + +/* AC input voltage alarm indices */ +#define VACA_HIALRM_I 0 /* high alarm */ +#define VACA_LOALRM_I 1 /* low alarm */ /* Onboard temperature alarm */ #define OBTA_HIALRM 1 /* high alarm */ -/* Device failure */ -#define DEVF_RCALRM 0x0001 /* rectifier failure */ -#define DEVF_INALRM 0x0006 /* internal failure */ -#define DEVF_LFNAVL 0x0008 /* lifetest not available */ +/* Device failure alarm masks */ +#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ +#define DEVF_INALRM_M 0x0006 /* internal failure */ +#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ + +/* Device failure alarm indices */ +#define DEVF_RCALRM_I 0 /* rectifier failure */ +#define DEVF_INALRM_I 1 /* internal failure */ +#define DEVF_LFNAVL_I 2 /* lifetest not available */ + +/* Battery temp sensor failure alarm masks */ +#define BTSF_FCND_M 0x0001 /* connection fault */ +#define BTSF_NCND_M 0x0002 /* not connected */ + +/* Battery temp sensor failure alarm indices */ +#define BTSF_FCND_I 0 /* connection fault */ +#define BTSF_NCND_I 1 /* not connected */ + +/* Battery voltage alarm masks */ +#define BVAL_HIALRM_M 0x0001 /* high voltage */ +#define BVAL_LOALRM_M 0x0002 /* low voltage */ +#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ + +/* Battery voltage alarm indices */ +#define BVAL_HIALRM_I 0 /* high voltage */ +#define BVAL_LOALRM_I 1 /* low voltage */ +#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ + +/* SoH and SoC alarm masks */ +#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ -/* Battery temp sensor failure */ -#define BTSF_FCND 0x0001 /* connection fault */ -#define BTSF_NCND 0x0001 /* not connected */ +/* SoH and SoC alarm indices */ +#define SHSC_HIRESI_I 0 /* high internal resistance */ +#define SHSC_LOCHEF_I 1 /* low charge efficiency */ +#define SHSC_LOEFCP_I 2 /* low effective capacity */ +#define SHSC_LOWSOC_I 3 /* low state of charge */ -/* Battery voltage alarm */ -#define BVAL_HIALRM 0x0001 /* high voltage */ -#define BVAL_LOALRM 0x0002 /* low voltage */ -#define BVAL_BSTSFL 0x0004 /* battery start with battery flat */ +/* Battery status alarm masks */ +#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ +#define BSTA_NOCNND_M 0x0002 /* not connected */ +#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ +#define BSTA_SULPHD_M 0x0008 /* sulphated */ +#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT_M 0x0020 /* connection fault */ -/* SoH and SoC alarms */ -#define SHSC_HIRESI 0x0001 /* high internal resistance */ -#define SHSC_LOCHEF 0x0002 /* low charge efficiency */ -#define SHSC_LOEFCP 0x0004 /* low effective capacity */ -#define SHSC_LOWSOC 0x0040 /* low state of charge */ +/* Battery status alarm indices */ +#define BSTA_REVPOL_I 0 /* reversed polarity */ +#define BSTA_NOCNND_I 1 /* not connected */ +#define BSTA_CLSHCR_I 2 /* cell short circuit */ +#define BSTA_SULPHD_I 3 /* sulphated */ +#define BSTA_CHEMNS_I 4 /* chemistry not supported */ +#define BSTA_CNNFLT_I 5 /* connection fault */ -/* Battery status alarms */ -#define BSTA_REVPOL 0x0001 /* reversed polarity */ -#define BSTA_NOCNND 0x0002 /* not connected */ -#define BSTA_CLSHCR 0x0004 /* cell short circuit */ -#define BSTA_SULPHD 0x0008 /* sulphated */ -#define BSTA_CHEMNS 0x0010 /* chemistry not supported */ -#define BSTA_CNNFLT 0x0020 /* connection fault */ +/* input mains and shutdown alarms */ +alrm_ar_t mains = { + 2, + { + {0, "input voltage not available"}, + {0, "ups shutdown requested"} + } +}; + +/* AC input voltage alarms */ +alrm_ar_t vaca = { + 2, + { + {0, "input voltage high alarm"}, + {0, "input voltage low alarm"} + } +}; +/* device failure alarms */ +alrm_ar_t devf = { + 3, + { + {0, "UPS rectifier failure"}, + {0, "UPS internal failure"}, + {0, "UPS lifetest not available"} + } +}; + +/* battery sensor failure alarms */ +alrm_ar_t btsf = { + 2, + { + {0, "battery temp sensor connection fault"}, + {0, "battery temp sensor not connected"} + } +}; + +/* battery voltage alarms */ +alrm_ar_t bval = { + 3, + { + {0, "battery high voltage"}, + {0, "battery low voltage"}, + {0, "battery start with battery flat"} + } +}; + +/* battery SoH and SoC alarms */ +alrm_ar_t shsc = { + 4, + { + {0, "battery high internal resistance"}, + {0, "battery low charge efficiency"}, + {0, "battery low effective capacity"}, + {0, "battery low state of charge"} + } +}; + +/* battery status alarm */ +alrm_ar_t bsta = { + 6, + { + {0, "battery reversed polarity"}, + {0, "battery not connected"}, + {0, "battery cell short circuit"}, + {0, "battery sulphated"}, + {0, "battery chemistry not supported"}, + {0, "battery connection fault"} + } +}; /* UPS device reg enum */ enum devreg { @@ -195,7 +316,7 @@ enum devreg { LCUR, /* Load current, "output.current" */ BINH = 79, /* Backup inhibit */ FSD, /* Force shutdown */ - TBUF, /* Time buffering */ + TBUF, /* Time buffering, "battery runtime" */ BSTA = 89, /* Battery status alarms */ SCSH, /* SoH and SoC alarms */ BVAL, /* Battery voltage alarm */ @@ -219,10 +340,11 @@ typedef struct regattr regattr_t; /* UPS device state info union */ union devstate { - prodname_t product; - chrgs_t charge; - pwrmng_t power; - reg_t reg; + prodname_t product; /* ups model name */ + chrgs_t charge; /* charging status */ + pwrmng_t power; /* ups status */ + reg_t reg; /* state register*/ + alrm_ar_t *alrm; /* alarm statuses */ }; typedef union devstate devstate_t; From 3fcb9a5c31ed07804ec4f3191cefe9aef0505b00 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Mon, 10 Jan 2022 13:33:46 +0200 Subject: [PATCH 052/700] first testing release --- drivers/adele_cbi.c | 604 +++++++++++++++----------------------------- drivers/adele_cbi.h | 12 +- 2 files changed, 214 insertions(+), 402 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index f81137297b..61eb099775 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -51,6 +51,9 @@ void reginit(); /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); +/* get device state */ +int get_dev_state(devreg_t regnum, devstate_t *state); + /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port); @@ -60,16 +63,15 @@ void modbus_reconnect(); /* modbus register read function */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); +/* modbus register write function */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg); -/* get device state */ -int get_dev_state(devreg_t regnum, devstate_t *state); - /* count the time elapsed since start */ long time_elapsed(struct timeval *start); -int register_write(modbus_t *mb, int addr, regtype_t type, void *data); /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -153,8 +155,8 @@ void upsdrv_initinfo(void) /* update UPS signal state */ void upsdrv_updateinfo(void) { - int rval; - int online = -1; /* keep online state */ + int rval; + int i; /* local index */ errcnt = 0; devstate_t ds; /* device state */ @@ -162,132 +164,198 @@ void upsdrv_updateinfo(void) status_init(); /* initialize ups.status update */ alarm_init(); /* initialize ups.alarm update */ + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ + rval = get_dev_state(MAIN, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains.alrm[MAINS_AVAIL_I].descr); + } else { + status_set("OL"); + } + if (ds.alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains.alrm[SHUTD_REQST_I].descr); + } + + /* + * update UPS status regarding battery voltage + */ + rval = get_dev_state(BVAL, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set( bval.alrm[BVAL_LOALRM_I].descr); + } + if (ds.alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set( bval.alrm[BVAL_HIALRM_I].descr); + } + if (ds.alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set( bval.alrm[BVAL_BSTSFL_I].descr); + } + + /* get "battery.voltage" */ + rval = get_dev_state(BATV, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.voltage", "%s", ds.reg.strval); + + /* + * update UPS status regarding battery charger status + */ + /* get "battery.charger.status" */ - get_dev_state(CHRG, &ds); + rval = get_dev_state(CHRG, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.charge.state == CHRG_BULK || ds.charge.state == CHRG_ABSR) { + status_set("CHRG"); + } dstate_setinfo("battery.charger.status", "%s", ds.charge.info); - /* get "battery.voltage" */ - get_dev_state(BATV, &ds); - dstate_setinfo("battery.charger.status", "%s", ds.reg.strval); + rval = get_dev_state(PMNG, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + if (ds.power.state == PMNG_BOOST) { + status_set("BOOST"); + } - /* - * update UPS status regarding MAINS state either via OL | OB. - * if both statuses are mapped to contacts then only OL is evaluated. - */ - if (sigar[OL_T].addr != NOTUSED) { - rval = get_signal_state(OL_T); - upsdebugx(2, "OL value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[OL_T].noro)) { - status_set("OL"); - online = 1; - } else { - status_set("OB"); - online = 0; + /* + * update UPS battery state of charge + */ + rval = get_dev_state(BSOC, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.charge", "%s", ds.reg.strval); - /* if DISCHRG state is not mapped to a contact and UPS is on - * batteries set status to DISCHRG state */ - if (sigar[DISCHRG_T].addr == NOTUSED) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } + /* + * update UPS AC input state + */ + rval = get_dev_state(VACA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } + rval = get_dev_state(VAC, &ds); + dstate_setinfo("input.voltage", "%s", ds.reg.strval); - } - } else if (sigar[OB_T].addr != NOTUSED) { - rval = get_signal_state(OB_T); - upsdebugx(2, "OB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[OB_T].noro)) { - status_set("OB"); - online = 0; - if (sigar[DISCHRG_T].addr == NOTUSED) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } else { - status_set("OL"); - online = 1; - } - } + /* + * update UPS onboard temperature state + */ + rval = get_dev_state(OBTA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } + rval = get_dev_state(OTMP, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("ups.temperature", "%s", ds.reg.strval); /* - * update UPS status regarding CHARGING state via HB. HB is usually - * mapped to "ready" contact when closed indicates a charging state > 85% - */ - if (sigar[HB_T].addr != NOTUSED) { - rval = get_signal_state(HB_T); - upsdebugx(2, "HB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[HB_T].noro)) { - status_set("HB"); - dstate_setinfo("battery.charger.status", "resting"); - } else if (online == 1 && sigar[CHRG_T].addr == NOTUSED && errcnt == 0) { - status_set("CHRG"); - dstate_setinfo("battery.charger.status", "charging"); - } else if (online == 0 && sigar[DISCHRG_T].addr == NOTUSED && errcnt == 0) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } + * update UPS battery temperature state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } + rval = get_dev_state(BTMP, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.temperature", "%s", ds.reg.strval); + rval = get_dev_state(TBUF, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.runtime", "%s", ds.reg.strval); - /* - * update UPS status regarding DISCHARGING state via LB. LB is mapped - * to "battery low" contact. - */ - if (sigar[LB_T].addr != NOTUSED) { - rval = get_signal_state(LB_T); - upsdebugx(2, "LB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[LB_T].noro)) { - status_set("LB"); - alarm_set("Low Battery (Charge)"); - } - } + /* + * update UPS device failure state + */ + rval = get_dev_state(DEVF, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } - /* - * update UPS status regarding battery HEALTH state via RB. RB is mapped - * to "replace battery" contact - */ - if (sigar[RB_T].addr != NOTUSED) { - rval = get_signal_state(RB_T); - upsdebugx(2, "RB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[RB_T].noro)) { - status_set("RB"); - alarm_set("Replace Battery"); - } - } + /* + * update UPS SoH and SoC states + */ + rval = get_dev_state(SCSH, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } - /* - * update UPS status regarding battery HEALTH state via RB. RB is mapped - * to "replace battery" contact - */ - if (sigar[CHRG_T].addr != NOTUSED) { - rval = get_signal_state(CHRG_T); - upsdebugx(2, "CHRG value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[CHRG_T].noro)) { - status_set("CHRG"); - dstate_setinfo("battery.charger.status", "charging"); - } - } else if (sigar[DISCHRG_T].addr != NOTUSED) { - rval = get_signal_state(DISCHRG_T); - upsdebugx(2, "DISCHRG value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[DISCHRG_T].noro)) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } + /* + * update UPS battery state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } - /* check for communication errors */ + /* + * update UPS load status + */ + rval = get_dev_state(LVDC, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("output.voltage", "%s", ds.reg.strval); + rval = get_dev_state(LCUR, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("output.current", "%s", ds.reg.strval); + + + /* check for communication errors */ if (errcnt == 0) { alarm_commit(); status_commit(); @@ -551,67 +619,24 @@ int upscmd(const char *cmd, const char *arg) { int rval; int data; - struct timeval start; - long etime; if (!strcasecmp(cmd, "load.off")) { - if (sigar[FSD_T].addr != NOTUSED && - (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) - ) { - data = 1 ^ sigar[FSD_T].noro; - rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); - if (rval == -1) { - upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - sigar[FSD_T].addr, - sigar[FSD_T].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d", sigar[FSD_T].addr, data); - rval = STAT_INSTCMD_HANDLED; - } - - /* if pulse has been defined and rising edge was successful */ - if (FSD_pulse_duration != NOTUSED && rval == STAT_INSTCMD_HANDLED) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for FSD_pulse_duration ms */ - while ((etime = time_elapsed(&start)) < FSD_pulse_duration); - - data = 0 ^ sigar[FSD_T].noro; - rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); - if (rval == -1) { - upslogx(LOG_ERR, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - sigar[FSD_T].addr, - sigar[FSD_T].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d, elapsed time: %lims", - sigar[FSD_T].addr, - data, - etime - ); - rval = STAT_INSTCMD_HANDLED; - } - } - } else { - upslogx(LOG_NOTICE,"load.off: failed (FSD address undefined or invalid register type) [%s] [%s]", - cmd, - arg - ); - rval = STAT_INSTCMD_FAILED; - } - } else { + data = 1; + rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + regs[FSD].xaddr, + regs[FSD].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); + rval = STAT_INSTCMD_HANDLED; + } + } else { upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); rval = STAT_INSTCMD_UNKNOWN; } @@ -803,7 +828,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) } else { bval.alrm[BVAL_BSTSFL_I].actv = 0; } - state->alrm = bval; + state->alrm = &bval; break; case BTSF: if (reg_val & BTSF_FCND_M) { @@ -884,14 +909,6 @@ int get_dev_state(devreg_t regnum, devstate_t *state) /* get driver configuration parameters */ void get_config_vars() { - int i; /* local index */ - - /* initialize sigar table */ - for (i = 0; i < NUMOF_SIG_STATES; i++) { - sigar[i].addr = NOTUSED; - sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ - } - /* check if device manufacturer is set ang get the value */ if (testvar("device_mfr")) { device_mfr = getval("device_mfr"); @@ -970,217 +987,6 @@ void get_config_vars() } } upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); - - /* check if OL address is set and get the value */ - if (testvar("OL_addr")) { - sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); - if (testvar("OL_noro")) { - sigar[OL_T].noro = (int)strtol(getval("OL_noro"), NULL, 10); - if (sigar[OL_T].noro != 1) { - sigar[OL_T].noro = 0; - } - } - } - - /* check if OL register type is set and get the value otherwise set to INPUT_B */ - if (testvar("OL_regtype")) { - sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); - if (sigar[OL_T].type < COIL || sigar[OL_T].type > HOLDING) { - sigar[OL_T].type = INPUT_B; - } - } else { - sigar[OL_T].type = INPUT_B; - } - - /* check if OB address is set and get the value */ - if (testvar("OB_addr")) { - sigar[OB_T].addr = (int)strtol(getval("OB_addr"), NULL, 0); - } - if (testvar("OB_noro")) { - sigar[OB_T].noro = (int)strtol(getval("OB_noro"), NULL, 10); - if (sigar[OB_T].noro != 1) { - sigar[OB_T].noro = 0; - } - } - - /* check if OB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("OB_regtype")) { - sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); - if (sigar[OB_T].type < COIL || sigar[OB_T].type > HOLDING) { - sigar[OB_T].type = INPUT_B; - } - } else { - sigar[OB_T].type = INPUT_B; - } - - /* check if LB address is set and get the value */ - if (testvar("LB_addr")) { - sigar[LB_T].addr = (int)strtol(getval("LB_addr"), NULL, 0); - if (testvar("LB_noro")) { - sigar[LB_T].noro = (int)strtol(getval("LB_noro"), NULL, 10); - if (sigar[LB_T].noro != 1) { - sigar[LB_T].noro = 0; - } - } - } - - /* check if LB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("LB_regtype")) { - sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); - if (sigar[LB_T].type < COIL || sigar[LB_T].type > HOLDING) { - sigar[LB_T].type = INPUT_B; - } - } else { - sigar[LB_T].type = INPUT_B; - } - - /* check if HB address is set and get the value */ - if (testvar("HB_addr")) { - sigar[HB_T].addr = (int)strtol(getval("HB_addr"), NULL, 0); - if (testvar("HB_noro")) { - sigar[HB_T].noro = (int)strtol(getval("HB_noro"), NULL, 10); - if (sigar[HB_T].noro != 1) { - sigar[HB_T].noro = 0; - } - } - } - - /* check if HB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("HB_regtype")) { - sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); - if (sigar[HB_T].type < COIL || sigar[HB_T].type > HOLDING) { - sigar[HB_T].type = INPUT_B; - } - } else { - sigar[HB_T].type = INPUT_B; - } - - /* check if RB address is set and get the value */ - if (testvar("RB_addr")) { - sigar[RB_T].addr = (int)strtol(getval("RB_addr"), NULL, 0); - if (testvar("RB_noro")) { - sigar[RB_T].noro = (int)strtol(getval("RB_noro"), NULL, 10); - if (sigar[RB_T].noro != 1) { - sigar[RB_T].noro = 0; - } - } - } - - /* check if RB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("RB_regtype")) { - sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); - if (sigar[RB_T].type < COIL || sigar[RB_T].type > HOLDING) { - sigar[RB_T].type = INPUT_B; - } - } else { - sigar[RB_T].type = INPUT_B; - } - - /* check if CHRG address is set and get the value */ - if (testvar("CHRG_addr")) { - sigar[CHRG_T].addr = (int)strtol(getval("CHRG_addr"), NULL, 0); - if (testvar("CHRG_noro")) { - sigar[CHRG_T].noro = (int)strtol(getval("CHRG_noro"), NULL, 10); - if (sigar[CHRG_T].noro != 1) { - sigar[CHRG_T].noro = 0; - } - } - } - - /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ - if (testvar("CHRG_regtype")) { - sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); - if (sigar[CHRG_T].type < COIL || sigar[CHRG_T].type > HOLDING) { - sigar[CHRG_T].type = INPUT_B; - } - } else { - sigar[CHRG_T].type = INPUT_B; - } - - /* check if DISCHRG address is set and get the value */ - if (testvar("DISCHRG_addr")) { - sigar[DISCHRG_T].addr = (int)strtol(getval("DISCHRG_addr"), NULL, 0); - if (testvar("DISCHRG_noro")) { - sigar[DISCHRG_T].noro = (int)strtol(getval("DISCHRG_noro"), NULL, 10); - if (sigar[DISCHRG_T].noro != 1) { - sigar[DISCHRG_T].noro = 0; - } - } - } - - /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ - if (testvar("DISCHRG_regtype")) { - sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); - if (sigar[DISCHRG_T].type < COIL || sigar[DISCHRG_T].type > HOLDING) { - sigar[DISCHRG_T].type = INPUT_B; - } - } else { - sigar[DISCHRG_T].type = INPUT_B; - } - - /* check if FSD address is set and get the value */ - if (testvar("FSD_addr")) { - sigar[FSD_T].addr = (int)strtol(getval("FSD_addr"), NULL, 0); - if (testvar("FSD_noro")) { - sigar[FSD_T].noro = (int)strtol(getval("FSD_noro"), NULL, 10); - if (sigar[FSD_T].noro != 1) { - sigar[FSD_T].noro = 0; - } - } - } - - /* check if FSD register type is set and get the value otherwise set to COIL */ - if (testvar("FSD_regtype")) { - sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); - if (sigar[FSD_T].type < COIL || sigar[FSD_T].type > HOLDING) { - sigar[FSD_T].type = COIL; - } - } else { - sigar[FSD_T].type = COIL; - } - - /* check if FSD pulse duration is set and get the value */ - if (testvar("FSD_pulse_duration")) { - FSD_pulse_duration = (int) strtol(getval("FSD_pulse_duration"), NULL, 10); - } - upsdebugx(2, "FSD_pulse_duration %d", FSD_pulse_duration); - - /* debug loop over signal array */ - for (i = 0; i < NUMOF_SIG_STATES; i++) { - if (sigar[i].addr != NOTUSED) { - char *signame; - switch (i) { - case OL_T: - signame = "OL"; - break; - case OB_T: - signame = "OB"; - break; - case LB_T: - signame = "LB"; - break; - case HB_T: - signame = "HB"; - break; - case RB_T: - signame = "RB"; - break; - case FSD_T: - signame = "FSD"; - break; - case CHRG_T: - signame = "CHRG"; - break; - case DISCHRG_T: - signame = "DISCHRG"; - break; - default: - signame = "NOTUSED"; - break; - } - upsdebugx(2, "%s, addr:0x%x, type:%d", signame, sigar[i].addr, sigar[i].type); - } - } } /* create a new modbus context based on connection type (serial or TCP) */ diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 6cd05989a5..3a823ded3a 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -54,6 +54,12 @@ /* number of device models */ #define DEV_NUMOF_MODELS 10 +/* shutdown repeat on error */ +#define FSD_REPEAT_CNT 3 + +/* shutdown repeat interval in ms */ +#define FSD_REPEAT_INTRV 1500 + /* definition of register type */ enum regtype { COIL = 0, @@ -155,11 +161,11 @@ typedef struct alrm_ar alrm_ar_t; #define PRDN_MAX 14 /* Mains alarm masks */ -#define MAINS_AVAIL_M 0x0001 /* 0: available 1: not available */ +#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ #define SHUTD_REQST_M 0x0002 /* shutdown requested */ /* Mains alarm indices */ -#define MAINS_AVAIL_I 0 /* 0: available 1: not available */ +#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ #define SHUTD_REQST_I 1 /* shutdown requested */ /* AC input voltage alarm masks */ @@ -316,7 +322,7 @@ enum devreg { LCUR, /* Load current, "output.current" */ BINH = 79, /* Backup inhibit */ FSD, /* Force shutdown */ - TBUF, /* Time buffering, "battery runtime" */ + TBUF, /* Time buffering, "battery.runtime" */ BSTA = 89, /* Battery status alarms */ SCSH, /* SoH and SoC alarms */ BVAL, /* Battery voltage alarm */ From 5a110855776e56e43fa2d611987af5d1849f6526 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Tue, 11 Jan 2022 01:43:36 +0200 Subject: [PATCH 053/700] ghost alarms bug fix, other bug fixes --- drivers/adele_cbi.c | 843 ++++++++++++++++++++++++-------------------- drivers/adele_cbi.h | 51 +-- 2 files changed, 500 insertions(+), 394 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index 61eb099775..852629a7de 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -1,7 +1,7 @@ /* adele_cbi.c - driver for ADELE CB/CBI DC-UPS * * Copyright (C) - * 2021 Dimitris Economou + * 2022 Dimitris Economou * * 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 @@ -25,11 +25,12 @@ #include #include -#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" +#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" #define DRIVER_VERSION "0.01" /* variables */ static modbus_t *mbctx = NULL; /* modbus memory context */ +static devstate_t *dstate = NULL; /* device state context */ static int errcnt = 0; /* modbus access error counter */ static char *device_mfr = DEVICE_MFR; /* device manufacturer */ static char *device_model = DEVICE_MODEL; /* device model */ @@ -52,7 +53,7 @@ void reginit(); void get_config_vars(void); /* get device state */ -int get_dev_state(devreg_t regnum, devstate_t *state); +int get_dev_state(devreg_t regindx, devstate_t **dstate); /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port); @@ -75,11 +76,11 @@ long time_elapsed(struct timeval *start); /* driver description structure */ upsdrv_info_t upsdrv_info = { - DRIVER_NAME, - DRIVER_VERSION, - "Dimitris Economou \n", - DRV_BETA, - {NULL} + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} }; /* @@ -92,6 +93,8 @@ void upsdrv_initups(void) int rval; upsdebugx(2, "upsdrv_initups"); + + dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); reginit(); get_config_vars(); @@ -132,56 +135,62 @@ void upsdrv_initups(void) /* initialize ups driver information */ void upsdrv_initinfo(void) { - devstate_t ds; /* device state */ - upsdebugx(2, "upsdrv_initinfo"); + devstate_t *ds = dstate; /* device state context */ + upsdebugx(2, "upsdrv_initinfo"); - /* set device information */ - dstate_setinfo("device.mfr", "%s", device_mfr); - dstate_setinfo("device.model", "%s", device_model); + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); dstate_setinfo("device.type", "%s", device_type); /* read ups model */ get_dev_state(PRDN, &ds); - dstate_setinfo("ups.model", "%s", ds.product.name); + dstate_setinfo("ups.model", "%s", ds->product.name); + upslogx(LOG_INFO, "ups.model = %s", ds->product.name); /* register instant commands */ - dstate_addcmd("load.off"); + dstate_addcmd("load.off"); - /* set callback for instant commands */ - upsh.instcmd = upscmd; + /* set callback for instant commands */ + upsh.instcmd = upscmd; } /* update UPS signal state */ void upsdrv_updateinfo(void) { - int rval; - int i; /* local index */ - errcnt = 0; - devstate_t ds; /* device state */ - - upsdebugx(2, "upsdrv_updateinfo"); - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ - - /* - * update UPS status regarding MAINS and SHUTDOWN request - * - OL: On line (mains is present) - * - OB: On battery (mains is not present) - */ + int rval; /* return value */ + int i; /* local index */ + devstate_t *ds = dstate; /* device state */ + + upsdebugx(2, "upsdrv_updateinfo"); + + errcnt = 0; /* initialize error counter to zero */ + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ + + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ rval = get_dev_state(MAIN, &ds); if (rval == -1) { errcnt++; - } - if (ds.alrm->alrm[MAINS_AVAIL_I].actv) { - status_set("OB"); - alarm_set(mains.alrm[MAINS_AVAIL_I].descr); } else { - status_set("OL"); - } - if (ds.alrm->alrm[SHUTD_REQST_I].actv) { - status_set("FSD"); - alarm_set(mains.alrm[SHUTD_REQST_I].descr); + if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains.alrm[MAINS_AVAIL_I].descr); + upslogx(LOG_INFO, "ups.status = OB"); + } else { + status_set("OL"); + upslogx(LOG_INFO, "ups.status = OL"); + } + if (ds->alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains.alrm[SHUTD_REQST_I].descr); + upslogx(LOG_INFO, "ups.status = FSD"); + } } /* @@ -190,26 +199,31 @@ void upsdrv_updateinfo(void) rval = get_dev_state(BVAL, &ds); if (rval == -1) { errcnt++; - } - if (ds.alrm->alrm[BVAL_LOALRM_I].actv) { - status_set("LB"); - alarm_set( bval.alrm[BVAL_LOALRM_I].descr); - } - if (ds.alrm->alrm[BVAL_HIALRM_I].actv) { - status_set("HB"); - alarm_set( bval.alrm[BVAL_HIALRM_I].descr); - } - if (ds.alrm->alrm[BVAL_BSTSFL_I].actv) { - alarm_set( bval.alrm[BVAL_BSTSFL_I].descr); + } else { + if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set(bval.alrm[BVAL_LOALRM_I].descr); + upslogx(LOG_INFO, "ups.status = LB"); + } + if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set(bval.alrm[BVAL_HIALRM_I].descr); + upslogx(LOG_INFO, "ups.status = HB"); + } + if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); + upslogx(LOG_INFO, "battery start with battery flat"); + } } /* get "battery.voltage" */ rval = get_dev_state(BATV, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); } - dstate_setinfo("battery.voltage", "%s", ds.reg.strval); - /* * update UPS status regarding battery charger status */ @@ -218,22 +232,27 @@ void upsdrv_updateinfo(void) rval = get_dev_state(CHRG, &ds); if (rval == -1) { errcnt++; + } else { + if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { + status_set("CHRG"); + upslogx(LOG_INFO, "ups.status = CHRG"); + } + dstate_setinfo("battery.charger.status", "%s", ds->charge.info); + upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); } - if (ds.charge.state == CHRG_BULK || ds.charge.state == CHRG_ABSR) { - status_set("CHRG"); - } - dstate_setinfo("battery.charger.status", "%s", ds.charge.info); - rval = get_dev_state(PMNG, &ds); if (rval == -1) { errcnt++; - } - if (ds.power.state == PMNG_BCKUP) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - if (ds.power.state == PMNG_BOOST) { - status_set("BOOST"); + } else { + if (ds->power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + upslogx(LOG_INFO, "ups.status = DISCHRG"); + } + if (ds->power.state == PMNG_BOOST) { + status_set("BOOST"); + upslogx(LOG_INFO, "ups.status = BOOST"); + } } /* @@ -242,8 +261,10 @@ void upsdrv_updateinfo(void) rval = get_dev_state(BSOC, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.charge", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); } - dstate_setinfo("battery.charge", "%s", ds.reg.strval); /* * update UPS AC input state @@ -251,14 +272,17 @@ void upsdrv_updateinfo(void) rval = get_dev_state(VACA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } } + rval = get_dev_state(VAC, &ds); + dstate_setinfo("input.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); } - rval = get_dev_state(VAC, &ds); - dstate_setinfo("input.voltage", "%s", ds.reg.strval); /* * update UPS onboard temperature state @@ -266,40 +290,49 @@ void upsdrv_updateinfo(void) rval = get_dev_state(OBTA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } } } rval = get_dev_state(OTMP, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("ups.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); } - dstate_setinfo("ups.temperature", "%s", ds.reg.strval); - /* * update UPS battery temperature state */ rval = get_dev_state(BSTA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } rval = get_dev_state(BTMP, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); } - dstate_setinfo("battery.temperature", "%s", ds.reg.strval); rval = get_dev_state(TBUF, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.runtime", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); } - dstate_setinfo("battery.runtime", "%s", ds.reg.strval); /* * update UPS device failure state @@ -307,10 +340,12 @@ void upsdrv_updateinfo(void) rval = get_dev_state(DEVF, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } @@ -320,10 +355,12 @@ void upsdrv_updateinfo(void) rval = get_dev_state(SCSH, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } @@ -333,10 +370,12 @@ void upsdrv_updateinfo(void) rval = get_dev_state(BSTA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } @@ -346,56 +385,58 @@ void upsdrv_updateinfo(void) rval = get_dev_state(LVDC, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("output.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); } - dstate_setinfo("output.voltage", "%s", ds.reg.strval); rval = get_dev_state(LCUR, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("output.current", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); } - dstate_setinfo("output.current", "%s", ds.reg.strval); - - /* check for communication errors */ - if (errcnt == 0) { - alarm_commit(); - status_commit(); - dstate_dataok(); - } else { - upsdebugx(2,"Communication errors: %d", errcnt); - dstate_datastale(); - } + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2, "Communication errors: %d", errcnt); + dstate_datastale(); + } } /* shutdown UPS */ void upsdrv_shutdown(void) { - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); - case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); } /* print driver usage info */ @@ -406,13 +447,13 @@ void upsdrv_help(void) /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { - addvar(VAR_VALUE, "device_mfr", "device manufacturer"); - addvar(VAR_VALUE, "device_model", "device model"); - addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); - addvar(VAR_VALUE, "ser_parity", "serial port parity"); - addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); - addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); - addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); @@ -422,10 +463,13 @@ void upsdrv_makevartable(void) /* close modbus connection and free modbus context allocated memory */ void upsdrv_cleanup(void) { - if (mbctx != NULL) { - modbus_close(mbctx); - modbus_free(mbctx); - } + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } + if (dstate != NULL) { + free(dstate); + } } /* @@ -437,7 +481,7 @@ void reginit() { int i; /* local index */ - for (i = 1; i < MODBUS_NUMOF_REGS; i++) { + for (i = 0; i < MODBUS_NUMOF_REGS; i++) { int rnum = regs[i].num; switch (regs[i].type) { case COIL: @@ -460,13 +504,14 @@ void reginit() regs[i].xaddr = 0x40000 + rnum - 1; break; default: - upslogx(LOG_ERR, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); - upsdebugx(3, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); + upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); } - upsdebugx(3, "register num:%d, type: %d saddr %d, xaddr x%x\n", regs[i].num, - regs[i].type, - regs[i].saddr, - regs[i].xaddr + upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", + regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr ); } } @@ -474,153 +519,153 @@ void reginit() /* Read a modbus register */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) { - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; - - switch (type) { - case COIL: - rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_B: - rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_R: - rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - case HOLDING: - rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); + upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); /* on BROKEN PIPE error try to reconnect */ if (errno == EPIPE) { - upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); modbus_reconnect(); } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; } /* write a modbus register */ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) { - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; - - switch (type) { - case COIL: - *(uint *)data = *(uint *)data & mask8; - rval = modbus_write_bit(mb, addr, *(uint8_t *)data); - break; - case HOLDING: - *(uint *)data = *(uint *)data & mask16; - rval = modbus_write_register(mb, addr, *(uint16_t *)data); - break; - - case INPUT_B: - case INPUT_R: + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; } /* returns the time elapsed since start in milliseconds */ long time_elapsed(struct timeval *start) { - long rval; - struct timeval end; - - rval = gettimeofday(&end, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); - } - if (start->tv_usec < end.tv_usec) { - suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; - end.tv_usec -= 1000000 * nsec; - end.tv_sec += nsec; - } - if (start->tv_usec - end.tv_usec > 1000000) { - suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; - end.tv_usec += 1000000 * nsec; - end.tv_sec -= nsec; - } - rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; - - return rval; + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; } /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg) { - int rval; - int data; + int rval; + int data; - if (!strcasecmp(cmd, "load.off")) { + if (!strcasecmp(cmd, "load.off")) { data = 1; rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); if (rval == -1) { @@ -637,32 +682,45 @@ int upscmd(const char *cmd, const char *arg) rval = STAT_INSTCMD_HANDLED; } } else { - upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_UNKNOWN; - } - return rval; + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; } /* read device state, returns 0 on success or -1 on communication error it formats state depending on register semantics */ -int get_dev_state(devreg_t regnum, devstate_t *state) +int get_dev_state(devreg_t regindx, devstate_t **dstate) { int i; /* local index */ + int n; int rval; /* return value */ - uint reg_val; /* register value */ + static char *ptr = NULL; /* temporary pointer */ + uint num; /* register number */ + uint reg_val; /* register value */ regtype_t rtype; /* register type */ int addr; /* register address */ + devstate_t *state; /* device state */ + + state = *dstate; + num = regs[regindx].num; + addr = regs[regindx].xaddr; + rtype = regs[regindx].type; - addr = regs[regnum].xaddr; - rtype = regs[regnum].type; rval = register_read(mbctx, addr, rtype, ®_val); if (rval == -1) { return rval; } - - /* process register data */ - switch (regnum) { - case CHRG: + upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", + num, + addr, + rtype, + reg_val + ); + + /* process register data */ + switch (regindx) { + case CHRG: /* "ups.charge" */ if (reg_val == CHRG_NONE) { state->charge.state = CHRG_NONE; state->charge.info = chrgs_i[CHRG_NONE]; @@ -679,21 +737,27 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->charge.state = CHRG_FLOAT; state->charge.info = chrgs_i[CHRG_FLOAT]; } - break; + upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); + break; case BATV: /* "battery.voltage" */ case LVDC: /* "output.voltage" */ case LCUR: /* "output.current" */ if (reg_val != 0) { state->reg.val16 = reg_val; double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ - int n = snprintf(NULL, 0, "%f", fval); - char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); - sprintf(fval_s, "%f", fval); + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; } else { state->reg.val16 = 0; - state->reg.strval = "0"; + state->reg.strval = "0.00"; } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case TBUF: case BSOH: @@ -701,38 +765,53 @@ int get_dev_state(devreg_t regnum, devstate_t *state) case VAC: /* "input.voltage" */ if (reg_val != 0) { state->reg.val16 = reg_val; - int n = snprintf(NULL, 0, "%d", reg_val); - char *reg_val_s = (char *)malloc(sizeof(char) * (n + 1)); + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; sprintf(reg_val_s, "%d", reg_val); state->reg.strval = reg_val_s; } else { state->reg.val16 = 0; state->reg.strval = "0"; } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case BSOC: /* "battery.charge" */ if (reg_val != 0) { state->reg.val16 = reg_val; double fval = (double )reg_val * regs[BSOC].scale; - int n = snprintf(NULL, 0, "%f", fval); - char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); - sprintf(fval_s, "%f", fval); + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; } else { state->reg.val16 = 0; - state->reg.strval = "0"; + state->reg.strval = "0.00"; } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case BTMP: /* "battery.temperature" */ case OTMP: /* "ups.temperature" */ state->reg.val16 = reg_val; double fval = reg_val - 273.15; - int n = snprintf(NULL, 0, "%f", fval); - char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); - sprintf(fval_s, "%f", fval); + n = snprintf(NULL, 0, "%.2f", fval); + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + if (ptr != NULL) { + free(ptr); + } + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; - case PMNG: /* "ups.status" & "battery.charge" */ + case PMNG: /* "ups.status" & "battery.charge" */ if (reg_val == PMNG_BCKUP) { state->power.state = PMNG_BCKUP; state->power.info = pwrmng_i[PMNG_BCKUP]; @@ -746,7 +825,8 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->power.state = PMNG_NCHRG; state->power.info = pwrmng_i[PMNG_NCHRG]; } - break; + upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); + break; case PRDN: /* "ups.model" */ for (i = 0; i < DEV_NUMOF_MODELS; i++) { if (prdnm_i[i].val == reg_val) { @@ -755,6 +835,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) } state->product.val = reg_val; state->product.name = prdnm_i[i].name; + upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); break; case BSTA: if (reg_val & BSTA_REVPOL_M) { @@ -887,76 +968,90 @@ int get_dev_state(devreg_t regnum, devstate_t *state) } state->alrm = &mains; break; + case OBTA: + if (reg_val == OBTA_HIALRM_V) { + obta.alrm[OBTA_HIALRM_I].actv = 1; + } + state->alrm = &obta; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - break; - } + state->reg.val16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + break; + } - upsdebugx(3, "get_dev_state: state: %d", reg_val); - return rval; + return rval; } /* get driver configuration parameters */ void get_config_vars() { - /* check if device manufacturer is set ang get the value */ - if (testvar("device_mfr")) { - device_mfr = getval("device_mfr"); - } - upsdebugx(2, "device_mfr %s", device_mfr); - - /* check if device model is set ang get the value */ - if (testvar("device_model")) { - device_model = getval("device_model"); - } - upsdebugx(2, "device_model %s", device_model); - - /* check if serial baud rate is set ang get the value */ - if (testvar("ser_baud_rate")) { - ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); - } - upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); - - /* check if serial parity is set ang get the value */ - if (testvar("ser_parity")) { - /* Dereference the char* we get */ - char *sp = getval("ser_parity"); - if (sp) { - /* TODO? Sanity-check the char we get? */ - ser_parity = *sp; - } else { - upsdebugx(2, "Could not determine ser_parity, will keep default"); - } - } - upsdebugx(2, "ser_parity %c", ser_parity); - - /* check if serial data bit is set ang get the value */ - if (testvar("ser_data_bit")) { - ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); - } - upsdebugx(2, "ser_data_bit %d", ser_data_bit); - - /* check if serial stop bit is set ang get the value */ - if (testvar("ser_stop_bit")) { - ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); - } - upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); - - /* check if device ID is set ang get the value */ - if (testvar("rio_slave_id")) { - rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); - } - upsdebugx(2, "rio_slave_id %d", rio_slave_id); + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); /* check if response time out (s) is set ang get the value */ if (testvar("mod_resp_to_s")) { @@ -992,29 +1087,29 @@ void get_config_vars() /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port) { - modbus_t *mb; - char *sp; - if (strstr(port, "/dev/tty") != NULL) { - mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); - } - } else if ((sp = strchr(port, ':')) != NULL) { - char *tcp_port = xmalloc(sizeof(sp)); - strcpy(tcp_port, sp + 1); - *sp = '\0'; - mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - free(tcp_port); - } else { - mb = modbus_new_tcp(port, 502); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - } - return mb; + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; } /* reconnect to modbus server upon connection error */ @@ -1022,7 +1117,7 @@ void modbus_reconnect() { int rval; - upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); + upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); /* clear current modbus context */ modbus_close(mbctx); @@ -1060,4 +1155,4 @@ void modbus_reconnect() modbus_free(mbctx); fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); } -} \ No newline at end of file +} diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 3a823ded3a..b678ff03e5 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -1,7 +1,7 @@ /* adele_cbi.h - Driver for ADELE CB/CBI DC-UPS * * Copyright (C) - * 2021 Dimitris Economou + * 2022 Dimitris Economou * * 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 @@ -62,10 +62,10 @@ /* definition of register type */ enum regtype { - COIL = 0, - INPUT_B, - INPUT_R, - HOLDING + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING }; typedef enum regtype regtype_t; @@ -75,7 +75,7 @@ struct prodname { char *name; }; typedef struct prodname prodname_t; -prodname_t prdnm_i[] = { +static prodname_t prdnm_i[] = { {1, "CBI1235A"}, {2, "CBI2420A"}, {3, "CBI4810A"}, @@ -90,7 +90,7 @@ prodname_t prdnm_i[] = { }; /* charging status info, "battery.charger.status" */ -char *chrgs_i[] = { +static char *chrgs_i[] = { "none", "resting", /* recovering */ "charging", /* bulk */ @@ -104,7 +104,7 @@ struct chrgs { typedef struct chrgs chrgs_t; /* power management info, "ups.status", "battery.charger.status" */ -char *pwrmng_i[] = { +static char *pwrmng_i[] = { "backup", /* "OB", "discharging" */ "charging", /* "OL" */ "boost", @@ -176,8 +176,11 @@ typedef struct alrm_ar alrm_ar_t; #define VACA_HIALRM_I 0 /* high alarm */ #define VACA_LOALRM_I 1 /* low alarm */ -/* Onboard temperature alarm */ -#define OBTA_HIALRM 1 /* high alarm */ +/* Onboard temperature alarm value */ +#define OBTA_HIALRM_V 1 /* high alarm */ + +/* Onboard temperature alarm index */ +#define OBTA_HIALRM_I 0 /* high alarm */ /* Device failure alarm masks */ #define DEVF_RCALRM_M 0x0001 /* rectifier failure */ @@ -236,7 +239,7 @@ typedef struct alrm_ar alrm_ar_t; #define BSTA_CNNFLT_I 5 /* connection fault */ /* input mains and shutdown alarms */ -alrm_ar_t mains = { +static alrm_ar_t mains = { 2, { {0, "input voltage not available"}, @@ -245,7 +248,7 @@ alrm_ar_t mains = { }; /* AC input voltage alarms */ -alrm_ar_t vaca = { +static alrm_ar_t vaca = { 2, { {0, "input voltage high alarm"}, @@ -254,7 +257,7 @@ alrm_ar_t vaca = { }; /* device failure alarms */ -alrm_ar_t devf = { +static alrm_ar_t devf = { 3, { {0, "UPS rectifier failure"}, @@ -264,7 +267,7 @@ alrm_ar_t devf = { }; /* battery sensor failure alarms */ -alrm_ar_t btsf = { +static alrm_ar_t btsf = { 2, { {0, "battery temp sensor connection fault"}, @@ -273,7 +276,7 @@ alrm_ar_t btsf = { }; /* battery voltage alarms */ -alrm_ar_t bval = { +static alrm_ar_t bval = { 3, { {0, "battery high voltage"}, @@ -283,7 +286,7 @@ alrm_ar_t bval = { }; /* battery SoH and SoC alarms */ -alrm_ar_t shsc = { +static alrm_ar_t shsc = { 4, { {0, "battery high internal resistance"}, @@ -294,7 +297,7 @@ alrm_ar_t shsc = { }; /* battery status alarm */ -alrm_ar_t bsta = { +static alrm_ar_t bsta = { 6, { {0, "battery reversed polarity"}, @@ -306,6 +309,14 @@ alrm_ar_t bsta = { } }; +/* onboard temperature alarm */ +static alrm_ar_t obta = { + 1, + { + {0, "onboard temperature high"} + } +}; + /* UPS device reg enum */ enum devreg { CHRG = 0, /* Charging status, "battery.charger.status" */ @@ -356,7 +367,7 @@ union devstate { typedef union devstate devstate_t; /* ADELE CBI registers */ -regattr_t regs[] = { +static regattr_t regs[] = { {40005, 0, 0, 1, HOLDING}, /* Charging status */ {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ @@ -373,8 +384,8 @@ regattr_t regs[] = { {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ {40006, 0, 0, 1, HOLDING}, /* Power management - * 0:Backup 1:Charging 2:boost 3:Not charging - */ + * 0:Backup 1:Charging 2:boost 3:Not charging + */ {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ {40010, 0, 0, 1, HOLDING}, /* Software ID */ From 9d5ac452a0ba3048b13dede2bf021c35a62dc6c9 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 6 Jan 2022 01:09:02 +0200 Subject: [PATCH 054/700] under construction --- drivers/adele_cbi.c | 882 ++++++++++++++++++++++++++++++++++++++++++++ drivers/adele_cbi.h | 82 ++++ 2 files changed, 964 insertions(+) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index 852629a7de..18f85c0033 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -1,7 +1,14 @@ +<<<<<<< HEAD /* adele_cbi.c - driver for ADELE CB/CBI DC-UPS * * Copyright (C) * 2022 Dimitris Economou +======= +/* adele_cbi.c - Driver for adele CB/CBI UPS + * + * Copyright (C) + * 2021 Dimitris Economou +>>>>>>> under construction * * 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 @@ -25,6 +32,7 @@ #include #include +<<<<<<< HEAD #define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" #define DRIVER_VERSION "0.01" @@ -48,13 +56,38 @@ static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus by /* initialize register start address and hex address from register number */ void reginit(); +======= +#define DRIVER_NAME "NUT Adele CBI driver" +#define DRIVER_VERSION "0.01" + +/* variables */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static regattr_t sigar[NUMOF_SIG_STATES]; /* array of ups signal attributes */ +static int errcnt = 0; /* modbus access error counter */ + +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ + +>>>>>>> under construction /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); +<<<<<<< HEAD /* get device state */ int get_dev_state(devreg_t regindx, devstate_t **dstate); +======= +>>>>>>> under construction /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port); @@ -64,6 +97,7 @@ void modbus_reconnect(); /* modbus register read function */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); +<<<<<<< HEAD /* modbus register write function */ int register_write(modbus_t *mb, int addr, regtype_t type, void *data); @@ -81,12 +115,33 @@ upsdrv_info_t upsdrv_info = { "Dimitris Economou \n", DRV_BETA, {NULL} +======= +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg); + +/* read signal status */ +int get_signal_state(devstate_t state); + +/* count the time elapsed since start */ +long time_elapsed(struct timeval *start); + +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} +>>>>>>> under construction }; /* * driver functions */ +<<<<<<< HEAD /* read configuration variables from ups.conf and connect to ups device */ void upsdrv_initups(void) { @@ -405,11 +460,210 @@ void upsdrv_updateinfo(void) upsdebugx(2, "Communication errors: %d", errcnt); dstate_datastale(); } +======= +/* initialize ups driver information */ +void upsdrv_initinfo(void) { + upsdebugx(2, "upsdrv_initinfo"); + + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); + + /* register instant commands */ + if (sigar[FSD_T].addr != NOTUSED) { + dstate_addcmd("load.off"); + } + + /* set callback for instant commands */ + upsh.instcmd = upscmd; +} + +/* open serial connection and connect to modbus RIO */ +void upsdrv_initups(void) +{ + int rval; + upsdebugx(2, "upsdrv_initups"); + + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response time out to 200ms */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} + +/* update UPS signal state */ +void upsdrv_updateinfo(void) +{ + int rval; + int online = -1; /* keep online state */ + errcnt = 0; + + upsdebugx(2, "upsdrv_updateinfo"); + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ + + /* + * update UPS status regarding MAINS state either via OL | OB. + * if both statuses are mapped to contacts then only OL is evaluated. + */ + if (sigar[OL_T].addr != NOTUSED) { + rval = get_signal_state(OL_T); + upsdebugx(2, "OL value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OL_T].noro)) { + status_set("OL"); + online = 1; + } else { + status_set("OB"); + online = 0; + + /* if DISCHRG state is not mapped to a contact and UPS is on + * batteries set status to DISCHRG state */ + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + + } + } else if (sigar[OB_T].addr != NOTUSED) { + rval = get_signal_state(OB_T); + upsdebugx(2, "OB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OB_T].noro)) { + status_set("OB"); + online = 0; + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } else { + status_set("OL"); + online = 1; + } + } + + /* + * update UPS status regarding CHARGING state via HB. HB is usually + * mapped to "ready" contact when closed indicates a charging state > 85% + */ + if (sigar[HB_T].addr != NOTUSED) { + rval = get_signal_state(HB_T); + upsdebugx(2, "HB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[HB_T].noro)) { + status_set("HB"); + dstate_setinfo("battery.charger.status", "resting"); + } else if (online == 1 && sigar[CHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } else if (online == 0 && sigar[DISCHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* + * update UPS status regarding DISCHARGING state via LB. LB is mapped + * to "battery low" contact. + */ + if (sigar[LB_T].addr != NOTUSED) { + rval = get_signal_state(LB_T); + upsdebugx(2, "LB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[LB_T].noro)) { + status_set("LB"); + alarm_set("Low Battery (Charge)"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[RB_T].addr != NOTUSED) { + rval = get_signal_state(RB_T); + upsdebugx(2, "RB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[RB_T].noro)) { + status_set("RB"); + alarm_set("Replace Battery"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[CHRG_T].addr != NOTUSED) { + rval = get_signal_state(CHRG_T); + upsdebugx(2, "CHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[CHRG_T].noro)) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } + } else if (sigar[DISCHRG_T].addr != NOTUSED) { + rval = get_signal_state(DISCHRG_T); + upsdebugx(2, "DISCHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[DISCHRG_T].noro)) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* check for communication errors */ + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2,"Communication errors: %d", errcnt); + dstate_datastale(); + } +>>>>>>> under construction } /* shutdown UPS */ void upsdrv_shutdown(void) { +<<<<<<< HEAD int rval; int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ struct timeval start; @@ -437,6 +691,35 @@ void upsdrv_shutdown(void) break; } upslogx(LOG_INFO, "shutdown command executed"); +======= + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); +>>>>>>> under construction } /* print driver usage info */ @@ -447,6 +730,7 @@ void upsdrv_help(void) /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { +<<<<<<< HEAD addvar(VAR_VALUE, "device_mfr", "device manufacturer"); addvar(VAR_VALUE, "device_model", "device model"); addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); @@ -454,6 +738,15 @@ void upsdrv_makevartable(void) addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); +======= + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); +>>>>>>> under construction addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); @@ -463,6 +756,7 @@ void upsdrv_makevartable(void) /* close modbus connection and free modbus context allocated memory */ void upsdrv_cleanup(void) { +<<<<<<< HEAD if (mbctx != NULL) { modbus_close(mbctx); modbus_free(mbctx); @@ -470,12 +764,19 @@ void upsdrv_cleanup(void) if (dstate != NULL) { free(dstate); } +======= + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } +>>>>>>> under construction } /* * driver support functions */ +<<<<<<< HEAD /* initialize register start address and hex address from register number */ void reginit() { @@ -542,11 +843,40 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); *(uint *)data = *(uint *)data & mask16; break; +======= +/* Read a modbus register */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; +>>>>>>> under construction #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif +<<<<<<< HEAD /* All enum cases defined as of the time of coding * have been covered above. Handle later definitions, * memory corruptions and buggy inputs below... @@ -576,11 +906,43 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) } upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); return rval; +======= + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +>>>>>>> under construction } /* write a modbus register */ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) { +<<<<<<< HEAD int rval = -1; /* register bit masks */ @@ -599,10 +961,31 @@ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) case INPUT_B: case INPUT_R: +======= + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: +>>>>>>> under construction #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif +<<<<<<< HEAD /* All enum cases defined as of the time of coding * have been covered above. Handle later definitions, * memory corruptions and buggy inputs below... @@ -632,11 +1015,43 @@ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) } upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); return rval; +======= + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +>>>>>>> under construction } /* returns the time elapsed since start in milliseconds */ long time_elapsed(struct timeval *start) { +<<<<<<< HEAD long rval; struct timeval end; @@ -657,11 +1072,34 @@ long time_elapsed(struct timeval *start) rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; return rval; +======= + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; +>>>>>>> under construction } /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg) { +<<<<<<< HEAD int rval; int data; @@ -974,10 +1412,128 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) } state->alrm = &obta; break; +======= + int rval; + int data; + struct timeval start; + long etime; + + if (!strcasecmp(cmd, "load.off")) { + if (sigar[FSD_T].addr != NOTUSED && + (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) + ) { + data = 1 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", sigar[FSD_T].addr, data); + rval = STAT_INSTCMD_HANDLED; + } + + /* if pulse has been defined and rising edge was successful */ + if (FSD_pulse_duration != NOTUSED && rval == STAT_INSTCMD_HANDLED) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for FSD_pulse_duration ms */ + while ((etime = time_elapsed(&start)) < FSD_pulse_duration); + + data = 0 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(LOG_ERR, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d, elapsed time: %lims", + sigar[FSD_T].addr, + data, + etime + ); + rval = STAT_INSTCMD_HANDLED; + } + } + } else { + upslogx(LOG_NOTICE,"load.off: failed (FSD address undefined or invalid register type) [%s] [%s]", + cmd, + arg + ); + rval = STAT_INSTCMD_FAILED; + } + } else { + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; +} + +/* read signal state from modbus RIO, returns 0|1 state or -1 on communication error */ +int get_signal_state(devstate_t state) +{ + int rval = -1; + int reg_val; + regtype_t rtype = 0; /* register type */ + int addr = -1; /* register address */ + + /* assign register address and type */ + switch (state) { + case OL_T: + addr = sigar[OL_T].addr; + rtype = sigar[OL_T].type; + break; + case OB_T: + addr = sigar[OB_T].addr; + rtype = sigar[OB_T].type; + break; + case LB_T: + addr = sigar[LB_T].addr; + rtype = sigar[LB_T].type; + break; + case HB_T: + addr = sigar[HB_T].addr; + rtype = sigar[HB_T].type; + break; + case RB_T: + addr = sigar[RB_T].addr; + rtype = sigar[RB_T].type; + break; + case CHRG_T: + addr = sigar[CHRG_T].addr; + rtype = sigar[CHRG_T].type; + break; + case DISCHRG_T: + addr = sigar[DISCHRG_T].addr; + rtype = sigar[DISCHRG_T].type; + break; + + case BYPASS_T: + case CAL_T: + case FSD_T: + case OFF_T: + case OVER_T: + case TRIM_T: + case BOOST_T: +>>>>>>> under construction #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif +<<<<<<< HEAD /* All enum cases defined as of the time of coding * have been covered above. Handle later definitions, * memory corruptions and buggy inputs below... @@ -999,11 +1555,31 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) } return rval; +======= + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + break; + } + + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval > -1) { + rval = reg_val; + } + upsdebugx(3, "get_signal_state: state: %d", reg_val); + return rval; +>>>>>>> under construction } /* get driver configuration parameters */ void get_config_vars() { +<<<<<<< HEAD /* check if device manufacturer is set ang get the value */ if (testvar("device_mfr")) { device_mfr = getval("device_mfr"); @@ -1052,6 +1628,64 @@ void get_config_vars() rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); } upsdebugx(2, "rio_slave_id %d", rio_slave_id); +======= + int i; /* local index */ + + /* initialize sigar table */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + sigar[i].addr = NOTUSED; + sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ + } + + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); +>>>>>>> under construction /* check if response time out (s) is set ang get the value */ if (testvar("mod_resp_to_s")) { @@ -1082,11 +1716,226 @@ void get_config_vars() } } upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); +<<<<<<< HEAD +======= + + /* check if OL address is set and get the value */ + if (testvar("OL_addr")) { + sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); + if (testvar("OL_noro")) { + sigar[OL_T].noro = (int)strtol(getval("OL_noro"), NULL, 10); + if (sigar[OL_T].noro != 1) { + sigar[OL_T].noro = 0; + } + } + } + + /* check if OL register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OL_regtype")) { + sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); + if (sigar[OL_T].type < COIL || sigar[OL_T].type > HOLDING) { + sigar[OL_T].type = INPUT_B; + } + } else { + sigar[OL_T].type = INPUT_B; + } + + /* check if OB address is set and get the value */ + if (testvar("OB_addr")) { + sigar[OB_T].addr = (int)strtol(getval("OB_addr"), NULL, 0); + } + if (testvar("OB_noro")) { + sigar[OB_T].noro = (int)strtol(getval("OB_noro"), NULL, 10); + if (sigar[OB_T].noro != 1) { + sigar[OB_T].noro = 0; + } + } + + /* check if OB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OB_regtype")) { + sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[OB_T].type < COIL || sigar[OB_T].type > HOLDING) { + sigar[OB_T].type = INPUT_B; + } + } else { + sigar[OB_T].type = INPUT_B; + } + + /* check if LB address is set and get the value */ + if (testvar("LB_addr")) { + sigar[LB_T].addr = (int)strtol(getval("LB_addr"), NULL, 0); + if (testvar("LB_noro")) { + sigar[LB_T].noro = (int)strtol(getval("LB_noro"), NULL, 10); + if (sigar[LB_T].noro != 1) { + sigar[LB_T].noro = 0; + } + } + } + + /* check if LB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("LB_regtype")) { + sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[LB_T].type < COIL || sigar[LB_T].type > HOLDING) { + sigar[LB_T].type = INPUT_B; + } + } else { + sigar[LB_T].type = INPUT_B; + } + + /* check if HB address is set and get the value */ + if (testvar("HB_addr")) { + sigar[HB_T].addr = (int)strtol(getval("HB_addr"), NULL, 0); + if (testvar("HB_noro")) { + sigar[HB_T].noro = (int)strtol(getval("HB_noro"), NULL, 10); + if (sigar[HB_T].noro != 1) { + sigar[HB_T].noro = 0; + } + } + } + + /* check if HB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("HB_regtype")) { + sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); + if (sigar[HB_T].type < COIL || sigar[HB_T].type > HOLDING) { + sigar[HB_T].type = INPUT_B; + } + } else { + sigar[HB_T].type = INPUT_B; + } + + /* check if RB address is set and get the value */ + if (testvar("RB_addr")) { + sigar[RB_T].addr = (int)strtol(getval("RB_addr"), NULL, 0); + if (testvar("RB_noro")) { + sigar[RB_T].noro = (int)strtol(getval("RB_noro"), NULL, 10); + if (sigar[RB_T].noro != 1) { + sigar[RB_T].noro = 0; + } + } + } + + /* check if RB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("RB_regtype")) { + sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); + if (sigar[RB_T].type < COIL || sigar[RB_T].type > HOLDING) { + sigar[RB_T].type = INPUT_B; + } + } else { + sigar[RB_T].type = INPUT_B; + } + + /* check if CHRG address is set and get the value */ + if (testvar("CHRG_addr")) { + sigar[CHRG_T].addr = (int)strtol(getval("CHRG_addr"), NULL, 0); + if (testvar("CHRG_noro")) { + sigar[CHRG_T].noro = (int)strtol(getval("CHRG_noro"), NULL, 10); + if (sigar[CHRG_T].noro != 1) { + sigar[CHRG_T].noro = 0; + } + } + } + + /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("CHRG_regtype")) { + sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); + if (sigar[CHRG_T].type < COIL || sigar[CHRG_T].type > HOLDING) { + sigar[CHRG_T].type = INPUT_B; + } + } else { + sigar[CHRG_T].type = INPUT_B; + } + + /* check if DISCHRG address is set and get the value */ + if (testvar("DISCHRG_addr")) { + sigar[DISCHRG_T].addr = (int)strtol(getval("DISCHRG_addr"), NULL, 0); + if (testvar("DISCHRG_noro")) { + sigar[DISCHRG_T].noro = (int)strtol(getval("DISCHRG_noro"), NULL, 10); + if (sigar[DISCHRG_T].noro != 1) { + sigar[DISCHRG_T].noro = 0; + } + } + } + + /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("DISCHRG_regtype")) { + sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); + if (sigar[DISCHRG_T].type < COIL || sigar[DISCHRG_T].type > HOLDING) { + sigar[DISCHRG_T].type = INPUT_B; + } + } else { + sigar[DISCHRG_T].type = INPUT_B; + } + + /* check if FSD address is set and get the value */ + if (testvar("FSD_addr")) { + sigar[FSD_T].addr = (int)strtol(getval("FSD_addr"), NULL, 0); + if (testvar("FSD_noro")) { + sigar[FSD_T].noro = (int)strtol(getval("FSD_noro"), NULL, 10); + if (sigar[FSD_T].noro != 1) { + sigar[FSD_T].noro = 0; + } + } + } + + /* check if FSD register type is set and get the value otherwise set to COIL */ + if (testvar("FSD_regtype")) { + sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); + if (sigar[FSD_T].type < COIL || sigar[FSD_T].type > HOLDING) { + sigar[FSD_T].type = COIL; + } + } else { + sigar[FSD_T].type = COIL; + } + + /* check if FSD pulse duration is set and get the value */ + if (testvar("FSD_pulse_duration")) { + FSD_pulse_duration = (int) strtol(getval("FSD_pulse_duration"), NULL, 10); + } + upsdebugx(2, "FSD_pulse_duration %d", FSD_pulse_duration); + + /* debug loop over signal array */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + if (sigar[i].addr != NOTUSED) { + char *signame; + switch (i) { + case OL_T: + signame = "OL"; + break; + case OB_T: + signame = "OB"; + break; + case LB_T: + signame = "LB"; + break; + case HB_T: + signame = "HB"; + break; + case RB_T: + signame = "RB"; + break; + case FSD_T: + signame = "FSD"; + break; + case CHRG_T: + signame = "CHRG"; + break; + case DISCHRG_T: + signame = "DISCHRG"; + break; + default: + signame = "NOTUSED"; + break; + } + upsdebugx(2, "%s, addr:0x%x, type:%d", signame, sigar[i].addr, sigar[i].type); + } + } +>>>>>>> under construction } /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port) { +<<<<<<< HEAD modbus_t *mb; char *sp; if (strstr(port, "/dev/tty") != NULL) { @@ -1110,6 +1959,31 @@ modbus_t *modbus_new(const char *port) } } return mb; +======= + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; +>>>>>>> under construction } /* reconnect to modbus server upon connection error */ @@ -1117,7 +1991,11 @@ void modbus_reconnect() { int rval; +<<<<<<< HEAD upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); +======= + upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); +>>>>>>> under construction /* clear current modbus context */ modbus_close(mbctx); @@ -1155,4 +2033,8 @@ void modbus_reconnect() modbus_free(mbctx); fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); } +<<<<<<< HEAD +} +======= } +>>>>>>> under construction diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index b678ff03e5..b2cb9c4278 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -1,7 +1,14 @@ +<<<<<<< HEAD /* adele_cbi.h - Driver for ADELE CB/CBI DC-UPS * * Copyright (C) * 2022 Dimitris Economou +======= +/* adele_cbi.h - Driver for generic UPS connected via modbus RIO + * + * Copyright (C) + * 2021 Dimitris Economou +>>>>>>> under construction * * 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 @@ -23,11 +30,16 @@ #ifndef ADELE_CBI_H #define ADELE_CBI_H +<<<<<<< HEAD #include /* UPS device details */ #define DEVICE_MFR "ADELE" #define DEVICE_TYPE "DC-UPS" +======= +/* UPS device details */ +#define DEVICE_MFR "ADELE" +>>>>>>> under construction #define DEVICE_MODEL "CB/CBI" /* serial access parameters */ @@ -48,6 +60,7 @@ /* modbus access parameters */ #define MODBUS_SLAVE_ID 5 +<<<<<<< HEAD /* number of modbus registers */ #define MODBUS_NUMOF_REGS 98 @@ -145,11 +158,36 @@ typedef struct alrm_ar alrm_ar_t; */ /* Charging status */ +======= +/* definition of register type */ +enum regtype { + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING +}; +typedef enum regtype regtype_t; + +/* UPS device state enum */ +enum devstate { + CHRG = 0, /* Charging status */ + BATV, /* Battery voltage */ + BCEF = 6, /* Battery charge efficiency factor (CEF) */ + BSOH, /* Battery state-of-health */ + BSOC = 9, /* Battery state-of-charge */ + BTMP = 11, /* Battery temperature in Kelvin units */ + PMNG = 15, /* Power management */ +}; +typedef enum devstate devstate_t; + +/* BIT MASKS and VALUES */ +>>>>>>> under construction #define CHRG_NONE 0 #define CHRG_RECV 1 #define CHRG_BULK 2 #define CHRG_ABSR 3 #define CHRG_FLOAT 4 +<<<<<<< HEAD /* power management */ #define PMNG_BCKUP 0 @@ -368,6 +406,25 @@ typedef union devstate devstate_t; /* ADELE CBI registers */ static regattr_t regs[] = { +======= +#define PMNG_BKUP 0 +#define PMNG_CHRG 1 +#define PMNG_BOOST 2 +#define PMNG_NCHRG 3 + +/* UPS state signal attributes */ +struct regattr { + int num; + int saddr; /* register start address */ + int xaddr; /* register hex address */ + float scale; /* scale */ + regtype_t type; /* register type */ +}; +typedef struct regattr regattr_t; + +/* ADELE CBI registers */ +regattr_t regs[] = { +>>>>>>> under construction {40005, 0, 0, 1, HOLDING}, /* Charging status */ {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ @@ -384,8 +441,13 @@ static regattr_t regs[] = { {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ {40006, 0, 0, 1, HOLDING}, /* Power management +<<<<<<< HEAD * 0:Backup 1:Charging 2:boost 3:Not charging */ +======= + * 0:Backup 1:Charging 2:boost 3:Not charging + */ +>>>>>>> under construction {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ {40010, 0, 0, 1, HOLDING}, /* Software ID */ @@ -408,9 +470,15 @@ static regattr_t regs[] = { {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ +<<<<<<< HEAD {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ +======= + {40056, 0, 0, 1, HOLDING}, /* Number of overtemperature inside events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low AC input voltage events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High AC input voltage events at mains input */ +>>>>>>> under construction {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ @@ -449,10 +517,14 @@ static regattr_t regs[] = { {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ {40065, 0, 0, 1, HOLDING}, /* History clear all */ {40066, 0, 0, 1, HOLDING}, /* Factory settings */ +<<<<<<< HEAD {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed * 1 = Backup not allowed 0 * */ {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ +======= + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed 1 = Backup not allowed 0 */ +>>>>>>> under construction {40104, 0, 0, 1, HOLDING}, /* Time buffering */ {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ @@ -468,7 +540,17 @@ static regattr_t regs[] = { {40043, 0, 0, 1, HOLDING}, /* Device failure */ {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ +<<<<<<< HEAD {40046, 0, 0, 1, HOLDING}, /* Mains status */ {40038, 0, 0, 1, HOLDING} /* Load alarm */ }; +======= + {40046, 0, 0, 1, HOLDING}, /* Input mains on / backup */ + {40038, 0, 0, 1, HOLDING} /* Load alarm */ +}; + +#define NUMOF_REGS 14 +#define NOTUSED -1 + +>>>>>>> under construction #endif /* ADELE_CBI_H */ From 6c0c34edb8f18782abab6bdb027e4c00c1e5f7c0 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Fri, 7 Jan 2022 01:55:04 +0200 Subject: [PATCH 055/700] register status values and masks added --- drivers/adele_cbi.h | 138 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 126 insertions(+), 12 deletions(-) diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index b2cb9c4278..b4e6d7b717 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -31,6 +31,9 @@ #define ADELE_CBI_H <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> register status values and masks added #include /* UPS device details */ @@ -168,26 +171,52 @@ enum regtype { }; typedef enum regtype regtype_t; -/* UPS device state enum */ -enum devstate { - CHRG = 0, /* Charging status */ - BATV, /* Battery voltage */ - BCEF = 6, /* Battery charge efficiency factor (CEF) */ - BSOH, /* Battery state-of-health */ - BSOC = 9, /* Battery state-of-charge */ - BTMP = 11, /* Battery temperature in Kelvin units */ - PMNG = 15, /* Power management */ +struct prodname { + uint16_t val; + char *name; }; -typedef enum devstate devstate_t; +typedef struct prodname prodname_t; +<<<<<<< HEAD /* BIT MASKS and VALUES */ >>>>>>> under construction +======= +/* product name */ +prodname_t prdn[] = { + {1, "CBI1235A"}, + {2, "CBI2420A"}, + {3, "CBI4810A"}, + {4, "CBI2801224"}, + {7, "CBI480W"}, + {8, "CB122410A"}, + {9, "CB480W"}, + {11, "CB12245AJ"}, + {12, "CB1235A"}, + {13, "CB2420A"}, + {14, "CB4810A"} +}; + +/* charging status */ +char *chrgs[] = { + "none", + "recovery", + "bulk", + "float" +}; + +/* + * BIT MASKS and VALUES + */ + +/* Charging status */ +>>>>>>> register status values and masks added #define CHRG_NONE 0 #define CHRG_RECV 1 #define CHRG_BULK 2 #define CHRG_ABSR 3 #define CHRG_FLOAT 4 <<<<<<< HEAD +<<<<<<< HEAD /* power management */ #define PMNG_BCKUP 0 @@ -407,18 +436,87 @@ typedef union devstate devstate_t; /* ADELE CBI registers */ static regattr_t regs[] = { ======= +======= + +/* power management */ +>>>>>>> register status values and masks added #define PMNG_BKUP 0 #define PMNG_CHRG 1 #define PMNG_BOOST 2 #define PMNG_NCHRG 3 +/* product name */ +#define PRDN_MAX 14 + +/* Mains status */ +#define MAINS_AVAIL 0x0001 /* available */ +#define SHUTD_REQST 0x0002 /* shutdown requested */ + +/* AC input voltage alarms */ +#define VACA_HIALRM 0x0001 /* high alarm */ +#define VACA_LOALRM 0x0002 /* low alarm */ + +/* Onboard temperature alarm */ +#define OBTA_HIALRM 1 /* high alarm */ + +/* Device failure */ +#define DEVF_RCALRM 0x0001 /* rectifier failure */ +#define DEVF_INALRM 0x0006 /* internal failure */ +#define DEVF_LFNAVL 0x0008 /* lifetest not available */ + +/* Battery temp sensor failure */ +#define BTSF_FCND 0x0001 /* connection fault */ +#define BTSF_NCND 0x0001 /* not connected */ + +/* Battery voltage alarm */ +#define BVAL_HIALRM 0x0001 /* high voltage */ +#define BVAL_LOALRM 0x0002 /* low voltage */ +#define BVAL_BSTSFL 0x0004 /* battery start with battery flat */ + +/* SoH and SoC alarms */ +#define SHSC_HIRESI 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC 0x0040 /* low state of charge */ + +/* Battery status alarms */ +#define BSTA_REVPOL 0x0001 /* reversed polarity */ +#define BSTA_NOCNND 0x0002 /* not connected */ +#define BSTA_CLSHCR 0x0004 /* cell short circuit */ +#define BSTA_SULPHD 0x0008 /* sulphated */ +#define BSTA_CHEMNS 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT 0x0020 /* connection fault */ + + +/* UPS device state enum */ +enum devstate { + CHRG = 0, /* Charging status */ + BATV, /* Battery voltage */ + BCEF = 6, /* Battery charge efficiency factor (CEF) */ + BSOH, /* Battery state-of-health */ + BSOC = 9, /* Battery state-of-charge */ + BTMP = 11, /* Battery temperature in Kelvin units */ + PMNG = 15, /* Power management */ + PRDN = 21, /* Product name */ + FSD = 80, /* Force shutdown */ + BSTA = 89, /* Battery status alarms */ + SCSH = 90, /* SoH and SoC alarms */ + BVAL = 91, /* Battery voltage alarm */ + BTSF = 92, /* Battery temp sensor failure */ + DEVF = 93, /* Device failure */ + OBTA = 94, /* On board temp alarm */ + VACA = 95, /* VAC alarms */ + MAIN = 96 /* Mains status */ +}; +typedef enum devstate devstate_t; + /* UPS state signal attributes */ struct regattr { int num; - int saddr; /* register start address */ + int saddr; /* register start address */ int xaddr; /* register hex address */ float scale; /* scale */ - regtype_t type; /* register type */ + regtype_t type; /* register type */ }; typedef struct regattr regattr_t; @@ -470,6 +568,7 @@ regattr_t regs[] = { {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ +<<<<<<< HEAD <<<<<<< HEAD {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ @@ -479,6 +578,11 @@ regattr_t regs[] = { {40054, 0, 0, 1, HOLDING}, /* Number of low AC input voltage events at mains input */ {40055, 0, 0, 1, HOLDING}, /* Number of High AC input voltage events at mains input */ >>>>>>> under construction +======= + {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ +>>>>>>> register status values and masks added {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ @@ -518,13 +622,19 @@ regattr_t regs[] = { {40065, 0, 0, 1, HOLDING}, /* History clear all */ {40066, 0, 0, 1, HOLDING}, /* Factory settings */ <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> register status values and masks added {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed * 1 = Backup not allowed 0 * */ {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ +<<<<<<< HEAD ======= {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed 1 = Backup not allowed 0 */ >>>>>>> under construction +======= +>>>>>>> register status values and masks added {40104, 0, 0, 1, HOLDING}, /* Time buffering */ {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ @@ -540,12 +650,16 @@ regattr_t regs[] = { {40043, 0, 0, 1, HOLDING}, /* Device failure */ {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ +<<<<<<< HEAD <<<<<<< HEAD {40046, 0, 0, 1, HOLDING}, /* Mains status */ {40038, 0, 0, 1, HOLDING} /* Load alarm */ }; ======= {40046, 0, 0, 1, HOLDING}, /* Input mains on / backup */ +======= + {40046, 0, 0, 1, HOLDING}, /* Mains status */ +>>>>>>> register status values and masks added {40038, 0, 0, 1, HOLDING} /* Load alarm */ }; From cdf609e92e6971431405f5e9a460c31067e731c4 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Sat, 8 Jan 2022 02:49:31 +0200 Subject: [PATCH 056/700] structure device data, code get_dev_state, in progress --- drivers/adele_cbi.c | 180 +++++++++++++++++++++++++++++++++----------- drivers/adele_cbi.h | 114 ++++++++++++++++++++++------ 2 files changed, 229 insertions(+), 65 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index 18f85c0033..3a119ccbbe 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -1,10 +1,14 @@ <<<<<<< HEAD +<<<<<<< HEAD /* adele_cbi.c - driver for ADELE CB/CBI DC-UPS * * Copyright (C) * 2022 Dimitris Economou ======= /* adele_cbi.c - Driver for adele CB/CBI UPS +======= +/* adele_cbi.c - driver for ADELE CB/CBI DC-UPS +>>>>>>> structure device data, code get_dev_state, in progress * * Copyright (C) * 2021 Dimitris Economou @@ -120,7 +124,7 @@ upsdrv_info_t upsdrv_info = { int upscmd(const char *cmd, const char *arg); /* read signal status */ -int get_signal_state(devstate_t state); +int get_signal_state(devreg_t state); /* count the time elapsed since start */ long time_elapsed(struct timeval *start); @@ -706,7 +710,7 @@ void upsdrv_shutdown(void) /* wait for an increasing time interval before sending shutdown command */ while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); cnt--; } switch (rval) { @@ -777,12 +781,19 @@ void upsdrv_cleanup(void) */ <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> structure device data, code get_dev_state, in progress /* initialize register start address and hex address from register number */ void reginit() { int i; /* local index */ +<<<<<<< HEAD for (i = 0; i < MODBUS_NUMOF_REGS; i++) { +======= + for (i = 1; i < MODBUS_NUMOF_REGS; i++) { +>>>>>>> structure device data, code get_dev_state, in progress int rnum = regs[i].num; switch (regs[i].type) { case COIL: @@ -805,6 +816,7 @@ void reginit() regs[i].xaddr = 0x40000 + rnum - 1; break; default: +<<<<<<< HEAD upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); } @@ -813,10 +825,20 @@ void reginit() regs[i].type, regs[i].saddr, regs[i].xaddr +======= + upslogx(LOG_ERR, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); + upsdebugx(3, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); + } + upsdebugx(3, "register num:%d, type: %d saddr %d, xaddr x%x\n", regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr +>>>>>>> structure device data, code get_dev_state, in progress ); } } +<<<<<<< HEAD /* Read a modbus register */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) { @@ -844,6 +866,8 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) *(uint *)data = *(uint *)data & mask16; break; ======= +======= +>>>>>>> structure device data, code get_dev_state, in progress /* Read a modbus register */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) { @@ -915,7 +939,7 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); + upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); break; } if (rval == -1) { @@ -1482,44 +1506,108 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) return rval; } -/* read signal state from modbus RIO, returns 0|1 state or -1 on communication error */ -int get_signal_state(devstate_t state) +/* read device state, returns 0 on success or -1 on communication error */ +int get_dev_state(devreg_t regnum, devstate_t *state) { - int rval = -1; - int reg_val; - regtype_t rtype = 0; /* register type */ - int addr = -1; /* register address */ - - /* assign register address and type */ - switch (state) { - case OL_T: - addr = sigar[OL_T].addr; - rtype = sigar[OL_T].type; - break; - case OB_T: - addr = sigar[OB_T].addr; - rtype = sigar[OB_T].type; - break; - case LB_T: - addr = sigar[LB_T].addr; - rtype = sigar[LB_T].type; - break; - case HB_T: - addr = sigar[HB_T].addr; - rtype = sigar[HB_T].type; - break; - case RB_T: - addr = sigar[RB_T].addr; - rtype = sigar[RB_T].type; - break; - case CHRG_T: - addr = sigar[CHRG_T].addr; - rtype = sigar[CHRG_T].type; + int i; /* local index */ + int rval = -1; /* return value */ + uint reg_val; /* register value */ + regtype_t rtype = 0; /* register type */ + int addr = regs[regnum].xaddr; + int rtype = regs[regnum].type; + + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval == -1) { + return rval; + } + + /* process register data */ + switch (regnum) { + case CHRG: + if (reg_val == CHRG_NONE) { + state->charge.state = CHRG_NONE; + state->charge.info = chrgs_i[CHRG_NONE]; + } else if (reg_val == CHRG_RECV) { + state->charge.state = CHRG_RECV; + state->charge.info = chrgs_i[CHRG_RECV]; + } else if (reg_val == CHRG_BULK) { + state->charge.state = CHRG_BULK; + state->charge.info = chrgs_i[CHRG_BULK]; + } else if (reg_val == CHRG_ABSR) { + state->charge.state = CHRG_ABSR; + state->charge.info = chrgs_i[CHRG_ABSR]; + } else if (reg_val == CHRG_FLOAT) { + state->charge.state = CHRG_FLOAT; + state->charge.info = chrgs_i[CHRG_FLOAT]; + } break; - case DISCHRG_T: - addr = sigar[DISCHRG_T].addr; - rtype = sigar[DISCHRG_T].type; + case BATV: /* "battery.voltage" */ + case LVDC: /* "output.voltage" */ + case LCUR: /* "output.current" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ + int n = snprintf(NULL, 0, "%f", fval); + char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(fval_s, "%f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + break; + case BSOH: + case BCEF: + case VAC: /* "input.voltage" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + int n = snprintf(NULL, 0, "%d", reg_val); + char *reg_val_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + break; + case BSOC: /* "battery.charge" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = (double )reg_val * regs[BSOC].scale; + int n = snprintf(NULL, 0, "%f", fval); + char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(fval_s, "%f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + break; + case BTMP: /* "battery.temperature" */ + case OTMP: /* "ups.temperature" */ + state->reg.val16 = reg_val; + double fval = reg_val - 273.15; + int n = snprintf(NULL, 0, "%f", fval); + char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); + sprintf(fval_s, "%f", fval); + state->reg.strval = fval_s; + break; + case PMNG: /* "ups.status" & "battery.charge" */ + if (reg_val == PMNG_BCKUP) { + state->power.state = PMNG_BCKUP; + state->power.info = pwrmng_i[PMNG_BCKUP]; + } else if (reg_val == PMNG_CHRGN) { + state->power.state = PMNG_CHRGN; + state->power.info = pwrmng_i[PMNG_CHRGN]; + } else if (reg_val == PMNG_BOOST) { + state->power.state = PMNG_BOOST; + state->power.info = pwrmng_i[PMNG_BOOST]; + } else if (reg_val == PMNG_NCHRG) { + state->power.state = PMNG_NCHRG; + state->power.info = pwrmng_i[PMNG_NCHRG]; + } break; +<<<<<<< HEAD case BYPASS_T: case CAL_T: @@ -1529,6 +1617,17 @@ int get_signal_state(devstate_t state) case TRIM_T: case BOOST_T: >>>>>>> under construction +======= + case PRDN: /* "ups.model" */ + for (i = 0; i < DEV_NUMOF_MODELS; i++) { + if (prdnm_i[i].val == reg_val) { + break; + } + } + state->product.val = reg_val; + state->product.name = prdnm_i[i].name; + break; +>>>>>>> structure device data, code get_dev_state, in progress #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" @@ -1567,10 +1666,7 @@ int get_signal_state(devstate_t state) break; } - rval = register_read(mbctx, addr, rtype, ®_val); - if (rval > -1) { - rval = reg_val; - } + upsdebugx(3, "get_signal_state: state: %d", reg_val); return rval; >>>>>>> under construction diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index b4e6d7b717..6c1d2a9b0b 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -1,10 +1,14 @@ <<<<<<< HEAD +<<<<<<< HEAD /* adele_cbi.h - Driver for ADELE CB/CBI DC-UPS * * Copyright (C) * 2022 Dimitris Economou ======= /* adele_cbi.h - Driver for generic UPS connected via modbus RIO +======= +/* adele_cbi.h - Driver for ADELE CB/CBI DC-UPS +>>>>>>> structure device data, code get_dev_state, in progress * * Copyright (C) * 2021 Dimitris Economou @@ -64,12 +68,16 @@ #define MODBUS_SLAVE_ID 5 <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> structure device data, code get_dev_state, in progress /* number of modbus registers */ #define MODBUS_NUMOF_REGS 98 /* number of device models */ #define DEV_NUMOF_MODELS 10 +<<<<<<< HEAD /* shutdown repeat on error */ #define FSD_REPEAT_CNT 3 @@ -162,6 +170,8 @@ typedef struct alrm_ar alrm_ar_t; /* Charging status */ ======= +======= +>>>>>>> structure device data, code get_dev_state, in progress /* definition of register type */ enum regtype { COIL = 0, @@ -171,11 +181,13 @@ enum regtype { }; typedef enum regtype regtype_t; +/* product name info, "device.model" */ struct prodname { uint16_t val; char *name; }; typedef struct prodname prodname_t; +<<<<<<< HEAD <<<<<<< HEAD /* BIT MASKS and VALUES */ @@ -183,6 +195,9 @@ typedef struct prodname prodname_t; ======= /* product name */ prodname_t prdn[] = { +======= +prodname_t prdnm_i[] = { +>>>>>>> structure device data, code get_dev_state, in progress {1, "CBI1235A"}, {2, "CBI2420A"}, {3, "CBI4810A"}, @@ -196,13 +211,42 @@ prodname_t prdn[] = { {14, "CB4810A"} }; -/* charging status */ -char *chrgs[] = { +/* charging status info, "battery.charger.status" */ +char *chrgs_i[] = { "none", - "recovery", - "bulk", - "float" + "recovery", /* "resting" */ + "bulk", /* "charging" */ + "absorb", /* "charging" */ + "float" /* "floating" */ +}; +struct chrgs { + int state; + char *info; }; +typedef struct chrgs chrgs_t; + +/* power management info, "ups.status", "battery.charger.status" */ +char *pwrmng_i[] = { + "backup", /* "OB", "discharging" */ + "charging", /* "OL" */ + "boost", + "not charging" +}; +struct pwrmng { + int state; + char *info; +}; +typedef struct pwrmng pwrmng_t; + +/* general modbus register value */ +struct reg { + union { + uint16_t val16; + uint8_t val8; + }; + char *strval; +}; +typedef struct reg reg_t; /* * BIT MASKS and VALUES @@ -439,9 +483,14 @@ static regattr_t regs[] = { ======= /* power management */ +<<<<<<< HEAD >>>>>>> register status values and masks added #define PMNG_BKUP 0 #define PMNG_CHRG 1 +======= +#define PMNG_BCKUP 0 +#define PMNG_CHRGN 1 +>>>>>>> structure device data, code get_dev_state, in progress #define PMNG_BOOST 2 #define PMNG_NCHRG 3 @@ -488,29 +537,35 @@ static regattr_t regs[] = { #define BSTA_CNNFLT 0x0020 /* connection fault */ -/* UPS device state enum */ -enum devstate { - CHRG = 0, /* Charging status */ - BATV, /* Battery voltage */ +/* UPS device reg enum */ +enum devreg { + CHRG = 0, /* Charging status, "battery.charger.status" */ + BATV, /* Battery voltage, "battery.voltage" */ BCEF = 6, /* Battery charge efficiency factor (CEF) */ BSOH, /* Battery state-of-health */ - BSOC = 9, /* Battery state-of-charge */ - BTMP = 11, /* Battery temperature in Kelvin units */ - PMNG = 15, /* Power management */ - PRDN = 21, /* Product name */ - FSD = 80, /* Force shutdown */ + BSOC = 9, /* Battery state-of-charge, "battery.charge" */ + BTMP = 11, /* Battery temperature in Kelvin units, "battery.temperature" */ + PMNG = 15, /* Power management, "ups.status" */ + OTMP = 20, /* Onboard temperature, "ups.temperature" */ + PRDN, /* Product name, "ups.model" */ + VAC = 24, /* AC voltage, "input.voltage" */ + LVDC, /* Load voltage, "output.voltage" */ + LCUR, /* Load current, "output.current" */ + BINH = 79, /* Backup inhibit */ + FSD, /* Force shutdown */ + TBUF, /* Time buffering */ BSTA = 89, /* Battery status alarms */ - SCSH = 90, /* SoH and SoC alarms */ - BVAL = 91, /* Battery voltage alarm */ - BTSF = 92, /* Battery temp sensor failure */ - DEVF = 93, /* Device failure */ - OBTA = 94, /* On board temp alarm */ - VACA = 95, /* VAC alarms */ - MAIN = 96 /* Mains status */ + SCSH, /* SoH and SoC alarms */ + BVAL, /* Battery voltage alarm */ + BTSF, /* Battery temp sensor failure */ + DEVF, /* Device failure */ + OBTA, /* On board temp alarm */ + VACA, /* VAC alarms */ + MAIN /* Mains status */ }; -typedef enum devstate devstate_t; +typedef enum devreg devreg_t; -/* UPS state signal attributes */ +/* UPS register attributes */ struct regattr { int num; int saddr; /* register start address */ @@ -520,6 +575,16 @@ struct regattr { }; typedef struct regattr regattr_t; +/* UPS device state info union */ +union devstate { + prodname_t product; + chrgs_t charge; + pwrmng_t power; + reg_t reg; +}; + +typedef union devstate devstate_t; + /* ADELE CBI registers */ regattr_t regs[] = { >>>>>>> under construction @@ -662,9 +727,12 @@ regattr_t regs[] = { >>>>>>> register status values and masks added {40038, 0, 0, 1, HOLDING} /* Load alarm */ }; +<<<<<<< HEAD #define NUMOF_REGS 14 #define NOTUSED -1 >>>>>>> under construction +======= +>>>>>>> structure device data, code get_dev_state, in progress #endif /* ADELE_CBI_H */ From fb3b47f4f861e3f4f268dd5b523c520f43000fb1 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Sun, 9 Jan 2022 03:21:08 +0200 Subject: [PATCH 057/700] alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress --- drivers/adele_cbi.c | 264 ++++++++++++++++++++++++++++++++------------ drivers/adele_cbi.h | 200 ++++++++++++++++++++++++++------- 2 files changed, 356 insertions(+), 108 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index 3a119ccbbe..1ca94eebc6 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -36,6 +36,7 @@ #include #include +<<<<<<< HEAD <<<<<<< HEAD #define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" #define DRIVER_VERSION "0.01" @@ -62,27 +63,32 @@ static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus by void reginit(); ======= #define DRIVER_NAME "NUT Adele CBI driver" +======= +#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" +>>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress #define DRIVER_VERSION "0.01" /* variables */ -static modbus_t *mbctx = NULL; /* modbus memory context */ -static regattr_t sigar[NUMOF_SIG_STATES]; /* array of ups signal attributes */ -static int errcnt = 0; /* modbus access error counter */ - -static char *device_mfr = DEVICE_MFR; /* device manufacturer */ -static char *device_model = DEVICE_MODEL; /* device model */ -static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ -static char ser_parity = PARITY; /* serial port parity */ -static int ser_data_bit = DATA_BIT; /* serial port data bit */ -static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ -static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ -static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ -static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ -static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ -static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static int errcnt = 0; /* modbus access error counter */ +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static char *device_type = DEVICE_TYPE; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ >>>>>>> under construction +/* initialize register start address and hex address from register number */ +void reginit(); + /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); @@ -123,8 +129,8 @@ upsdrv_info_t upsdrv_info = { /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg); -/* read signal status */ -int get_signal_state(devreg_t state); +/* get device state */ +int get_dev_state(devreg_t regnum, devstate_t *state); /* count the time elapsed since start */ long time_elapsed(struct timeval *start); @@ -146,14 +152,20 @@ upsdrv_info_t upsdrv_info = { */ <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress /* read configuration variables from ups.conf and connect to ups device */ void upsdrv_initups(void) { int rval; upsdebugx(2, "upsdrv_initups"); +<<<<<<< HEAD dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); +======= +>>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress reginit(); get_config_vars(); @@ -191,6 +203,7 @@ void upsdrv_initups(void) } } +<<<<<<< HEAD /* initialize ups driver information */ void upsdrv_initinfo(void) { @@ -465,64 +478,30 @@ void upsdrv_updateinfo(void) dstate_datastale(); } ======= +======= +>>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress /* initialize ups driver information */ -void upsdrv_initinfo(void) { +void upsdrv_initinfo(void) +{ + devstate_t ds; /* device state */ upsdebugx(2, "upsdrv_initinfo"); /* set device information */ dstate_setinfo("device.mfr", "%s", device_mfr); dstate_setinfo("device.model", "%s", device_model); + dstate_setinfo("device.type", "%s", device_type); - /* register instant commands */ - if (sigar[FSD_T].addr != NOTUSED) { - dstate_addcmd("load.off"); - } + /* read ups model */ + get_dev_state(PRDN, &ds); + dstate_setinfo("ups.model", "%s", ds.product.name); + + /* register instant commands */ + dstate_addcmd("load.off"); /* set callback for instant commands */ upsh.instcmd = upscmd; } -/* open serial connection and connect to modbus RIO */ -void upsdrv_initups(void) -{ - int rval; - upsdebugx(2, "upsdrv_initups"); - - get_config_vars(); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response time out to 200ms */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -} /* update UPS signal state */ void upsdrv_updateinfo(void) @@ -530,11 +509,20 @@ void upsdrv_updateinfo(void) int rval; int online = -1; /* keep online state */ errcnt = 0; + devstate_t ds; /* device state */ upsdebugx(2, "upsdrv_updateinfo"); status_init(); /* initialize ups.status update */ alarm_init(); /* initialize ups.alarm update */ + /* get "battery.charger.status" */ + get_dev_state(CHRG, &ds); + dstate_setinfo("battery.charger.status", "%s", ds.charge.info); + + /* get "battery.voltage" */ + get_dev_state(BATV, &ds); + dstate_setinfo("battery.charger.status", "%s", ds.reg.strval); + /* * update UPS status regarding MAINS state either via OL | OB. * if both statuses are mapped to contacts then only OL is evaluated. @@ -1506,16 +1494,18 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) return rval; } -/* read device state, returns 0 on success or -1 on communication error */ +/* read device state, returns 0 on success or -1 on communication error + it formats state depending on register semantics */ int get_dev_state(devreg_t regnum, devstate_t *state) { int i; /* local index */ - int rval = -1; /* return value */ + int rval; /* return value */ uint reg_val; /* register value */ - regtype_t rtype = 0; /* register type */ - int addr = regs[regnum].xaddr; - int rtype = regs[regnum].type; + regtype_t rtype; /* register type */ + int addr; /* register address */ + addr = regs[regnum].xaddr; + rtype = regs[regnum].type; rval = register_read(mbctx, addr, rtype, ®_val); if (rval == -1) { return rval; @@ -1556,6 +1546,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->reg.strval = "0"; } break; + case TBUF: case BSOH: case BCEF: case VAC: /* "input.voltage" */ @@ -1627,7 +1618,141 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->product.val = reg_val; state->product.name = prdnm_i[i].name; break; +<<<<<<< HEAD >>>>>>> structure device data, code get_dev_state, in progress +======= + case BSTA: + if (reg_val & BSTA_REVPOL_M) { + bsta.alrm[BSTA_REVPOL_I].actv = 1; + } else { + bsta.alrm[BSTA_REVPOL_I].actv = 0; + } + if (reg_val & BSTA_NOCNND_M) { + bsta.alrm[BSTA_NOCNND_I].actv = 1; + } else { + bsta.alrm[BSTA_NOCNND_I].actv = 0; + } + if (reg_val & BSTA_CLSHCR_M) { + bsta.alrm[BSTA_CLSHCR_I].actv = 1; + } else { + bsta.alrm[BSTA_CLSHCR_I].actv = 0; + } + if (reg_val & BSTA_SULPHD_M) { + bsta.alrm[BSTA_SULPHD_I].actv = 1; + } else { + bsta.alrm[BSTA_SULPHD_I].actv = 0; + } + if (reg_val & BSTA_CHEMNS_M) { + bsta.alrm[BSTA_CHEMNS_I].actv = 1; + } else { + bsta.alrm[BSTA_CHEMNS_I].actv = 0; + } + if (reg_val & BSTA_CNNFLT_M) { + bsta.alrm[BSTA_CNNFLT_I].actv = 1; + } else { + bsta.alrm[BSTA_CNNFLT_I].actv = 0; + } + state->alrm = &bsta; + break; + case SCSH: + if (reg_val & SHSC_HIRESI_M) { + shsc.alrm[SHSC_HIRESI_I].actv = 1; + } else { + shsc.alrm[SHSC_HIRESI_I].actv = 0; + } + if (reg_val & SHSC_LOCHEF_M) { + shsc.alrm[SHSC_LOCHEF_I].actv = 1; + } else { + shsc.alrm[SHSC_LOCHEF_I].actv = 0; + } + if (reg_val & SHSC_LOEFCP_M) { + shsc.alrm[SHSC_LOEFCP_I].actv = 1; + } else { + shsc.alrm[SHSC_LOEFCP_I].actv = 0; + } + if (reg_val & SHSC_LOWSOC_M) { + shsc.alrm[SHSC_LOWSOC_I].actv = 1; + } else { + shsc.alrm[SHSC_LOWSOC_I].actv = 0; + } + state->alrm = &shsc; + break; + case BVAL: + if (reg_val & BVAL_HIALRM_M) { + bval.alrm[BVAL_HIALRM_I].actv = 1; + } else { + bval.alrm[BVAL_HIALRM_I].actv = 0; + } + if (reg_val & BVAL_LOALRM_M) { + bval.alrm[BVAL_LOALRM_I].actv = 1; + } else { + bval.alrm[BVAL_LOALRM_I].actv = 0; + } + if (reg_val & BVAL_BSTSFL_M) { + bval.alrm[BVAL_BSTSFL_I].actv = 1; + } else { + bval.alrm[BVAL_BSTSFL_I].actv = 0; + } + state->alrm = bval; + break; + case BTSF: + if (reg_val & BTSF_FCND_M) { + btsf.alrm[BTSF_FCND_I].actv = 1; + } else { + btsf.alrm[BTSF_FCND_I].actv = 0; + } + if (reg_val & BTSF_NCND_M) { + btsf.alrm[BTSF_NCND_I].actv = 1; + } else { + btsf.alrm[BTSF_NCND_I].actv = 0; + } + state->alrm = &btsf; + break; + case DEVF: + if (reg_val & DEVF_RCALRM_M) { + devf.alrm[DEVF_RCALRM_I].actv = 1; + } else { + devf.alrm[DEVF_RCALRM_I].actv = 0; + } + if (reg_val & DEVF_INALRM_M) { + devf.alrm[DEVF_INALRM_I].actv = 1; + } else { + devf.alrm[DEVF_INALRM_I].actv = 0; + } + if (reg_val & DEVF_LFNAVL_M) { + devf.alrm[DEVF_LFNAVL_I].actv = 1; + } else { + devf.alrm[DEVF_LFNAVL_I].actv = 0; + } + state->alrm = &devf; + break; + case VACA: + if (reg_val & VACA_HIALRM_M) { + vaca.alrm[VACA_HIALRM_I].actv = 1; + } else { + vaca.alrm[VACA_HIALRM_I].actv = 0; + } + if (reg_val == VACA_LOALRM_M) { + vaca.alrm[VACA_LOALRM_I].actv = 1; + } else { + vaca.alrm[VACA_LOALRM_I].actv = 0; + } + state->alrm = &vaca; + break; + case MAIN: + if (reg_val & MAINS_AVAIL_M) { + mains.alrm[MAINS_AVAIL_I].actv = 1; + } else { + mains.alrm[MAINS_AVAIL_I].actv = 0; + } + if (reg_val == SHUTD_REQST_M) { + mains.alrm[SHUTD_REQST_I].actv = 1; + } else { + mains.alrm[SHUTD_REQST_I].actv = 0; + } + state->alrm = &mains; + break; +>>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" @@ -1666,8 +1791,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) break; } - - upsdebugx(3, "get_signal_state: state: %d", reg_val); + upsdebugx(3, "get_dev_state: state: %d", reg_val); return rval; >>>>>>> under construction } diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 6c1d2a9b0b..002db0b322 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -43,10 +43,13 @@ /* UPS device details */ #define DEVICE_MFR "ADELE" #define DEVICE_TYPE "DC-UPS" +<<<<<<< HEAD ======= /* UPS device details */ #define DEVICE_MFR "ADELE" >>>>>>> under construction +======= +>>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress #define DEVICE_MODEL "CB/CBI" /* serial access parameters */ @@ -214,10 +217,10 @@ prodname_t prdnm_i[] = { /* charging status info, "battery.charger.status" */ char *chrgs_i[] = { "none", - "recovery", /* "resting" */ - "bulk", /* "charging" */ - "absorb", /* "charging" */ - "float" /* "floating" */ + "resting", /* recovering */ + "charging", /* bulk */ + "charging", /* absorb */ + "floating" /* float */ }; struct chrgs { int state; @@ -248,6 +251,20 @@ struct reg { }; typedef struct reg reg_t; +/* general alarm struct */ +struct alrm { + int actv; /* active flag */ + char *descr; /* description field */ +}; +typedef struct alrm alrm_t; + +/* general alarm array */ +struct alrm_ar { + int alrm_c; /* alarm count */ + alrm_t alrm[]; /* alarm array */ +}; +typedef struct alrm_ar alrm_ar_t; + /* * BIT MASKS and VALUES */ @@ -497,45 +514,151 @@ static regattr_t regs[] = { /* product name */ #define PRDN_MAX 14 -/* Mains status */ -#define MAINS_AVAIL 0x0001 /* available */ -#define SHUTD_REQST 0x0002 /* shutdown requested */ +/* Mains alarm masks */ +#define MAINS_AVAIL_M 0x0001 /* 0: available 1: not available */ +#define SHUTD_REQST_M 0x0002 /* shutdown requested */ -/* AC input voltage alarms */ -#define VACA_HIALRM 0x0001 /* high alarm */ -#define VACA_LOALRM 0x0002 /* low alarm */ +/* Mains alarm indices */ +#define MAINS_AVAIL_I 0 /* 0: available 1: not available */ +#define SHUTD_REQST_I 1 /* shutdown requested */ + +/* AC input voltage alarm masks */ +#define VACA_HIALRM_M 0x0001 /* high alarm */ +#define VACA_LOALRM_M 0x0002 /* low alarm */ + +/* AC input voltage alarm indices */ +#define VACA_HIALRM_I 0 /* high alarm */ +#define VACA_LOALRM_I 1 /* low alarm */ /* Onboard temperature alarm */ #define OBTA_HIALRM 1 /* high alarm */ -/* Device failure */ -#define DEVF_RCALRM 0x0001 /* rectifier failure */ -#define DEVF_INALRM 0x0006 /* internal failure */ -#define DEVF_LFNAVL 0x0008 /* lifetest not available */ +/* Device failure alarm masks */ +#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ +#define DEVF_INALRM_M 0x0006 /* internal failure */ +#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ + +/* Device failure alarm indices */ +#define DEVF_RCALRM_I 0 /* rectifier failure */ +#define DEVF_INALRM_I 1 /* internal failure */ +#define DEVF_LFNAVL_I 2 /* lifetest not available */ + +/* Battery temp sensor failure alarm masks */ +#define BTSF_FCND_M 0x0001 /* connection fault */ +#define BTSF_NCND_M 0x0002 /* not connected */ + +/* Battery temp sensor failure alarm indices */ +#define BTSF_FCND_I 0 /* connection fault */ +#define BTSF_NCND_I 1 /* not connected */ + +/* Battery voltage alarm masks */ +#define BVAL_HIALRM_M 0x0001 /* high voltage */ +#define BVAL_LOALRM_M 0x0002 /* low voltage */ +#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ + +/* Battery voltage alarm indices */ +#define BVAL_HIALRM_I 0 /* high voltage */ +#define BVAL_LOALRM_I 1 /* low voltage */ +#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ + +/* SoH and SoC alarm masks */ +#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ + +/* SoH and SoC alarm indices */ +#define SHSC_HIRESI_I 0 /* high internal resistance */ +#define SHSC_LOCHEF_I 1 /* low charge efficiency */ +#define SHSC_LOEFCP_I 2 /* low effective capacity */ +#define SHSC_LOWSOC_I 3 /* low state of charge */ + +/* Battery status alarm masks */ +#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ +#define BSTA_NOCNND_M 0x0002 /* not connected */ +#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ +#define BSTA_SULPHD_M 0x0008 /* sulphated */ +#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT_M 0x0020 /* connection fault */ + +/* Battery status alarm indices */ +#define BSTA_REVPOL_I 0 /* reversed polarity */ +#define BSTA_NOCNND_I 1 /* not connected */ +#define BSTA_CLSHCR_I 2 /* cell short circuit */ +#define BSTA_SULPHD_I 3 /* sulphated */ +#define BSTA_CHEMNS_I 4 /* chemistry not supported */ +#define BSTA_CNNFLT_I 5 /* connection fault */ + +/* input mains and shutdown alarms */ +alrm_ar_t mains = { + 2, + { + {0, "input voltage not available"}, + {0, "ups shutdown requested"} + } +}; + +/* AC input voltage alarms */ +alrm_ar_t vaca = { + 2, + { + {0, "input voltage high alarm"}, + {0, "input voltage low alarm"} + } +}; -/* Battery temp sensor failure */ -#define BTSF_FCND 0x0001 /* connection fault */ -#define BTSF_NCND 0x0001 /* not connected */ +/* device failure alarms */ +alrm_ar_t devf = { + 3, + { + {0, "UPS rectifier failure"}, + {0, "UPS internal failure"}, + {0, "UPS lifetest not available"} + } +}; -/* Battery voltage alarm */ -#define BVAL_HIALRM 0x0001 /* high voltage */ -#define BVAL_LOALRM 0x0002 /* low voltage */ -#define BVAL_BSTSFL 0x0004 /* battery start with battery flat */ +/* battery sensor failure alarms */ +alrm_ar_t btsf = { + 2, + { + {0, "battery temp sensor connection fault"}, + {0, "battery temp sensor not connected"} + } +}; -/* SoH and SoC alarms */ -#define SHSC_HIRESI 0x0001 /* high internal resistance */ -#define SHSC_LOCHEF 0x0002 /* low charge efficiency */ -#define SHSC_LOEFCP 0x0004 /* low effective capacity */ -#define SHSC_LOWSOC 0x0040 /* low state of charge */ +/* battery voltage alarms */ +alrm_ar_t bval = { + 3, + { + {0, "battery high voltage"}, + {0, "battery low voltage"}, + {0, "battery start with battery flat"} + } +}; -/* Battery status alarms */ -#define BSTA_REVPOL 0x0001 /* reversed polarity */ -#define BSTA_NOCNND 0x0002 /* not connected */ -#define BSTA_CLSHCR 0x0004 /* cell short circuit */ -#define BSTA_SULPHD 0x0008 /* sulphated */ -#define BSTA_CHEMNS 0x0010 /* chemistry not supported */ -#define BSTA_CNNFLT 0x0020 /* connection fault */ +/* battery SoH and SoC alarms */ +alrm_ar_t shsc = { + 4, + { + {0, "battery high internal resistance"}, + {0, "battery low charge efficiency"}, + {0, "battery low effective capacity"}, + {0, "battery low state of charge"} + } +}; +/* battery status alarm */ +alrm_ar_t bsta = { + 6, + { + {0, "battery reversed polarity"}, + {0, "battery not connected"}, + {0, "battery cell short circuit"}, + {0, "battery sulphated"}, + {0, "battery chemistry not supported"}, + {0, "battery connection fault"} + } +}; /* UPS device reg enum */ enum devreg { @@ -553,7 +676,7 @@ enum devreg { LCUR, /* Load current, "output.current" */ BINH = 79, /* Backup inhibit */ FSD, /* Force shutdown */ - TBUF, /* Time buffering */ + TBUF, /* Time buffering, "battery runtime" */ BSTA = 89, /* Battery status alarms */ SCSH, /* SoH and SoC alarms */ BVAL, /* Battery voltage alarm */ @@ -577,10 +700,11 @@ typedef struct regattr regattr_t; /* UPS device state info union */ union devstate { - prodname_t product; - chrgs_t charge; - pwrmng_t power; - reg_t reg; + prodname_t product; /* ups model name */ + chrgs_t charge; /* charging status */ + pwrmng_t power; /* ups status */ + reg_t reg; /* state register*/ + alrm_ar_t *alrm; /* alarm statuses */ }; typedef union devstate devstate_t; From 31dcc5812967256eb731615f751f36b8f5bb1a5f Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Mon, 10 Jan 2022 13:33:46 +0200 Subject: [PATCH 058/700] first testing release --- drivers/adele_cbi.c | 399 ++++++++++++++++++++++++-------------------- drivers/adele_cbi.h | 12 +- 2 files changed, 227 insertions(+), 184 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index 1ca94eebc6..252d7676e3 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -92,12 +92,18 @@ void reginit(); /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); +<<<<<<< HEAD <<<<<<< HEAD /* get device state */ int get_dev_state(devreg_t regindx, devstate_t **dstate); ======= >>>>>>> under construction +======= +/* get device state */ +int get_dev_state(devreg_t regnum, devstate_t *state); + +>>>>>>> first testing release /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port); @@ -107,6 +113,7 @@ void modbus_reconnect(); /* modbus register read function */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); +<<<<<<< HEAD <<<<<<< HEAD /* modbus register write function */ int register_write(modbus_t *mb, int addr, regtype_t type, void *data); @@ -126,16 +133,17 @@ upsdrv_info_t upsdrv_info = { DRV_BETA, {NULL} ======= +======= +/* modbus register write function */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + +>>>>>>> first testing release /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg); -/* get device state */ -int get_dev_state(devreg_t regnum, devstate_t *state); - /* count the time elapsed since start */ long time_elapsed(struct timeval *start); -int register_write(modbus_t *mb, int addr, regtype_t type, void *data); /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -506,8 +514,8 @@ void upsdrv_initinfo(void) /* update UPS signal state */ void upsdrv_updateinfo(void) { - int rval; - int online = -1; /* keep online state */ + int rval; + int i; /* local index */ errcnt = 0; devstate_t ds; /* device state */ @@ -515,132 +523,198 @@ void upsdrv_updateinfo(void) status_init(); /* initialize ups.status update */ alarm_init(); /* initialize ups.alarm update */ + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ + rval = get_dev_state(MAIN, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains.alrm[MAINS_AVAIL_I].descr); + } else { + status_set("OL"); + } + if (ds.alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains.alrm[SHUTD_REQST_I].descr); + } + + /* + * update UPS status regarding battery voltage + */ + rval = get_dev_state(BVAL, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set( bval.alrm[BVAL_LOALRM_I].descr); + } + if (ds.alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set( bval.alrm[BVAL_HIALRM_I].descr); + } + if (ds.alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set( bval.alrm[BVAL_BSTSFL_I].descr); + } + + /* get "battery.voltage" */ + rval = get_dev_state(BATV, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.voltage", "%s", ds.reg.strval); + + /* + * update UPS status regarding battery charger status + */ + /* get "battery.charger.status" */ - get_dev_state(CHRG, &ds); + rval = get_dev_state(CHRG, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.charge.state == CHRG_BULK || ds.charge.state == CHRG_ABSR) { + status_set("CHRG"); + } dstate_setinfo("battery.charger.status", "%s", ds.charge.info); - /* get "battery.voltage" */ - get_dev_state(BATV, &ds); - dstate_setinfo("battery.charger.status", "%s", ds.reg.strval); + rval = get_dev_state(PMNG, &ds); + if (rval == -1) { + errcnt++; + } + if (ds.power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + if (ds.power.state == PMNG_BOOST) { + status_set("BOOST"); + } - /* - * update UPS status regarding MAINS state either via OL | OB. - * if both statuses are mapped to contacts then only OL is evaluated. - */ - if (sigar[OL_T].addr != NOTUSED) { - rval = get_signal_state(OL_T); - upsdebugx(2, "OL value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[OL_T].noro)) { - status_set("OL"); - online = 1; - } else { - status_set("OB"); - online = 0; - - /* if DISCHRG state is not mapped to a contact and UPS is on - * batteries set status to DISCHRG state */ - if (sigar[DISCHRG_T].addr == NOTUSED) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } + /* + * update UPS battery state of charge + */ + rval = get_dev_state(BSOC, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.charge", "%s", ds.reg.strval); - } - } else if (sigar[OB_T].addr != NOTUSED) { - rval = get_signal_state(OB_T); - upsdebugx(2, "OB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[OB_T].noro)) { - status_set("OB"); - online = 0; - if (sigar[DISCHRG_T].addr == NOTUSED) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } else { - status_set("OL"); - online = 1; - } - } + /* + * update UPS AC input state + */ + rval = get_dev_state(VACA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } + rval = get_dev_state(VAC, &ds); + dstate_setinfo("input.voltage", "%s", ds.reg.strval); /* - * update UPS status regarding CHARGING state via HB. HB is usually - * mapped to "ready" contact when closed indicates a charging state > 85% - */ - if (sigar[HB_T].addr != NOTUSED) { - rval = get_signal_state(HB_T); - upsdebugx(2, "HB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[HB_T].noro)) { - status_set("HB"); - dstate_setinfo("battery.charger.status", "resting"); - } else if (online == 1 && sigar[CHRG_T].addr == NOTUSED && errcnt == 0) { - status_set("CHRG"); - dstate_setinfo("battery.charger.status", "charging"); - } else if (online == 0 && sigar[DISCHRG_T].addr == NOTUSED && errcnt == 0) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } + * update UPS onboard temperature state + */ + rval = get_dev_state(OBTA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } + rval = get_dev_state(OTMP, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("ups.temperature", "%s", ds.reg.strval); - /* - * update UPS status regarding DISCHARGING state via LB. LB is mapped - * to "battery low" contact. - */ - if (sigar[LB_T].addr != NOTUSED) { - rval = get_signal_state(LB_T); - upsdebugx(2, "LB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[LB_T].noro)) { - status_set("LB"); - alarm_set("Low Battery (Charge)"); - } - } + /* + * update UPS battery temperature state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } + rval = get_dev_state(BTMP, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.temperature", "%s", ds.reg.strval); + rval = get_dev_state(TBUF, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("battery.runtime", "%s", ds.reg.strval); - /* - * update UPS status regarding battery HEALTH state via RB. RB is mapped - * to "replace battery" contact - */ - if (sigar[RB_T].addr != NOTUSED) { - rval = get_signal_state(RB_T); - upsdebugx(2, "RB value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[RB_T].noro)) { - status_set("RB"); - alarm_set("Replace Battery"); - } - } + /* + * update UPS device failure state + */ + rval = get_dev_state(DEVF, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } - /* - * update UPS status regarding battery HEALTH state via RB. RB is mapped - * to "replace battery" contact - */ - if (sigar[CHRG_T].addr != NOTUSED) { - rval = get_signal_state(CHRG_T); - upsdebugx(2, "CHRG value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[CHRG_T].noro)) { - status_set("CHRG"); - dstate_setinfo("battery.charger.status", "charging"); - } - } else if (sigar[DISCHRG_T].addr != NOTUSED) { - rval = get_signal_state(DISCHRG_T); - upsdebugx(2, "DISCHRG value: %d", rval); - if (rval == -1) { - errcnt++; - } else if (rval == (1 ^ sigar[DISCHRG_T].noro)) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - } + /* + * update UPS SoH and SoC states + */ + rval = get_dev_state(SCSH, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } + + /* + * update UPS battery state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } + for (i = 0; i < ds.alrm->alrm_c; i++) { + if (ds.alrm[i].alrm->actv) { + alarm_set(ds.alrm[i].alrm->descr); + } + } - /* check for communication errors */ + /* + * update UPS load status + */ + rval = get_dev_state(LVDC, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("output.voltage", "%s", ds.reg.strval); + rval = get_dev_state(LCUR, &ds); + if (rval == -1) { + errcnt++; + } + dstate_setinfo("output.current", "%s", ds.reg.strval); + + + /* check for communication errors */ if (errcnt == 0) { alarm_commit(); status_commit(); @@ -1427,67 +1501,24 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) ======= int rval; int data; - struct timeval start; - long etime; if (!strcasecmp(cmd, "load.off")) { - if (sigar[FSD_T].addr != NOTUSED && - (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) - ) { - data = 1 ^ sigar[FSD_T].noro; - rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); - if (rval == -1) { - upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - sigar[FSD_T].addr, - sigar[FSD_T].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d", sigar[FSD_T].addr, data); - rval = STAT_INSTCMD_HANDLED; - } - - /* if pulse has been defined and rising edge was successful */ - if (FSD_pulse_duration != NOTUSED && rval == STAT_INSTCMD_HANDLED) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for FSD_pulse_duration ms */ - while ((etime = time_elapsed(&start)) < FSD_pulse_duration); - - data = 0 ^ sigar[FSD_T].noro; - rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); - if (rval == -1) { - upslogx(LOG_ERR, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - sigar[FSD_T].addr, - sigar[FSD_T].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d, elapsed time: %lims", - sigar[FSD_T].addr, - data, - etime - ); - rval = STAT_INSTCMD_HANDLED; - } - } - } else { - upslogx(LOG_NOTICE,"load.off: failed (FSD address undefined or invalid register type) [%s] [%s]", - cmd, - arg - ); - rval = STAT_INSTCMD_FAILED; - } - } else { + data = 1; + rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + regs[FSD].xaddr, + regs[FSD].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); + rval = STAT_INSTCMD_HANDLED; + } + } else { upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); rval = STAT_INSTCMD_UNKNOWN; } @@ -1693,7 +1724,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) } else { bval.alrm[BVAL_BSTSFL_I].actv = 0; } - state->alrm = bval; + state->alrm = &bval; break; case BTSF: if (reg_val & BTSF_FCND_M) { @@ -1799,6 +1830,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) /* get driver configuration parameters */ void get_config_vars() { +<<<<<<< HEAD <<<<<<< HEAD /* check if device manufacturer is set ang get the value */ if (testvar("device_mfr")) { @@ -1857,6 +1889,8 @@ void get_config_vars() sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ } +======= +>>>>>>> first testing release /* check if device manufacturer is set ang get the value */ if (testvar("device_mfr")) { device_mfr = getval("device_mfr"); @@ -1937,6 +1971,7 @@ void get_config_vars() } upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); <<<<<<< HEAD +<<<<<<< HEAD ======= /* check if OL address is set and get the value */ @@ -2150,6 +2185,8 @@ void get_config_vars() } } >>>>>>> under construction +======= +>>>>>>> first testing release } /* create a new modbus context based on connection type (serial or TCP) */ diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 002db0b322..81e64ed71d 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -81,12 +81,16 @@ #define DEV_NUMOF_MODELS 10 <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> first testing release /* shutdown repeat on error */ #define FSD_REPEAT_CNT 3 /* shutdown repeat interval in ms */ #define FSD_REPEAT_INTRV 1500 +<<<<<<< HEAD /* definition of register type */ enum regtype { COIL = 0, @@ -175,6 +179,8 @@ typedef struct alrm_ar alrm_ar_t; ======= ======= >>>>>>> structure device data, code get_dev_state, in progress +======= +>>>>>>> first testing release /* definition of register type */ enum regtype { COIL = 0, @@ -515,11 +521,11 @@ static regattr_t regs[] = { #define PRDN_MAX 14 /* Mains alarm masks */ -#define MAINS_AVAIL_M 0x0001 /* 0: available 1: not available */ +#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ #define SHUTD_REQST_M 0x0002 /* shutdown requested */ /* Mains alarm indices */ -#define MAINS_AVAIL_I 0 /* 0: available 1: not available */ +#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ #define SHUTD_REQST_I 1 /* shutdown requested */ /* AC input voltage alarm masks */ @@ -676,7 +682,7 @@ enum devreg { LCUR, /* Load current, "output.current" */ BINH = 79, /* Backup inhibit */ FSD, /* Force shutdown */ - TBUF, /* Time buffering, "battery runtime" */ + TBUF, /* Time buffering, "battery.runtime" */ BSTA = 89, /* Battery status alarms */ SCSH, /* SoH and SoC alarms */ BVAL, /* Battery voltage alarm */ From a59750724de81852293b3895118ef72c9188b6d9 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Tue, 11 Jan 2022 01:43:36 +0200 Subject: [PATCH 059/700] ghost alarms bug fix, other bug fixes --- drivers/adele_cbi.c | 522 ++++++++++++++++++++++++++++++++------------ drivers/adele_cbi.h | 53 +++-- 2 files changed, 427 insertions(+), 148 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index 252d7676e3..f06a6b40eb 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -4,6 +4,7 @@ * * Copyright (C) * 2022 Dimitris Economou +<<<<<<< HEAD ======= /* adele_cbi.c - Driver for adele CB/CBI UPS ======= @@ -13,6 +14,8 @@ * Copyright (C) * 2021 Dimitris Economou >>>>>>> under construction +======= +>>>>>>> ghost alarms bug fix, other bug fixes * * 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 @@ -36,6 +39,7 @@ #include #include +<<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD #define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" @@ -66,10 +70,14 @@ void reginit(); ======= #define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" >>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress +======= +#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" +>>>>>>> ghost alarms bug fix, other bug fixes #define DRIVER_VERSION "0.01" /* variables */ static modbus_t *mbctx = NULL; /* modbus memory context */ +static devstate_t *dstate = NULL; /* device state context */ static int errcnt = 0; /* modbus access error counter */ static char *device_mfr = DEVICE_MFR; /* device manufacturer */ static char *device_model = DEVICE_MODEL; /* device model */ @@ -101,7 +109,7 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate); >>>>>>> under construction ======= /* get device state */ -int get_dev_state(devreg_t regnum, devstate_t *state); +int get_dev_state(devreg_t regindx, devstate_t **dstate); >>>>>>> first testing release /* create a new modbus context based on connection type (serial or TCP) */ @@ -147,12 +155,20 @@ long time_elapsed(struct timeval *start); /* driver description structure */ upsdrv_info_t upsdrv_info = { +<<<<<<< HEAD DRIVER_NAME, DRIVER_VERSION, "Dimitris Economou \n", DRV_BETA, {NULL} >>>>>>> under construction +======= + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} +>>>>>>> ghost alarms bug fix, other bug fixes }; /* @@ -169,11 +185,16 @@ void upsdrv_initups(void) int rval; upsdebugx(2, "upsdrv_initups"); +<<<<<<< HEAD <<<<<<< HEAD dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); ======= >>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress +======= + + dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); +>>>>>>> ghost alarms bug fix, other bug fixes reginit(); get_config_vars(); @@ -491,56 +512,62 @@ void upsdrv_updateinfo(void) /* initialize ups driver information */ void upsdrv_initinfo(void) { - devstate_t ds; /* device state */ - upsdebugx(2, "upsdrv_initinfo"); + devstate_t *ds = dstate; /* device state context */ + upsdebugx(2, "upsdrv_initinfo"); - /* set device information */ - dstate_setinfo("device.mfr", "%s", device_mfr); - dstate_setinfo("device.model", "%s", device_model); + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); dstate_setinfo("device.type", "%s", device_type); /* read ups model */ get_dev_state(PRDN, &ds); - dstate_setinfo("ups.model", "%s", ds.product.name); + dstate_setinfo("ups.model", "%s", ds->product.name); + upslogx(LOG_INFO, "ups.model = %s", ds->product.name); /* register instant commands */ - dstate_addcmd("load.off"); + dstate_addcmd("load.off"); - /* set callback for instant commands */ - upsh.instcmd = upscmd; + /* set callback for instant commands */ + upsh.instcmd = upscmd; } /* update UPS signal state */ void upsdrv_updateinfo(void) { - int rval; - int i; /* local index */ - errcnt = 0; - devstate_t ds; /* device state */ - - upsdebugx(2, "upsdrv_updateinfo"); - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ - - /* - * update UPS status regarding MAINS and SHUTDOWN request - * - OL: On line (mains is present) - * - OB: On battery (mains is not present) - */ + int rval; /* return value */ + int i; /* local index */ + devstate_t *ds = dstate; /* device state */ + + upsdebugx(2, "upsdrv_updateinfo"); + + errcnt = 0; /* initialize error counter to zero */ + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ + + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ rval = get_dev_state(MAIN, &ds); if (rval == -1) { errcnt++; - } - if (ds.alrm->alrm[MAINS_AVAIL_I].actv) { - status_set("OB"); - alarm_set(mains.alrm[MAINS_AVAIL_I].descr); } else { - status_set("OL"); - } - if (ds.alrm->alrm[SHUTD_REQST_I].actv) { - status_set("FSD"); - alarm_set(mains.alrm[SHUTD_REQST_I].descr); + if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains.alrm[MAINS_AVAIL_I].descr); + upslogx(LOG_INFO, "ups.status = OB"); + } else { + status_set("OL"); + upslogx(LOG_INFO, "ups.status = OL"); + } + if (ds->alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains.alrm[SHUTD_REQST_I].descr); + upslogx(LOG_INFO, "ups.status = FSD"); + } } /* @@ -549,26 +576,31 @@ void upsdrv_updateinfo(void) rval = get_dev_state(BVAL, &ds); if (rval == -1) { errcnt++; - } - if (ds.alrm->alrm[BVAL_LOALRM_I].actv) { - status_set("LB"); - alarm_set( bval.alrm[BVAL_LOALRM_I].descr); - } - if (ds.alrm->alrm[BVAL_HIALRM_I].actv) { - status_set("HB"); - alarm_set( bval.alrm[BVAL_HIALRM_I].descr); - } - if (ds.alrm->alrm[BVAL_BSTSFL_I].actv) { - alarm_set( bval.alrm[BVAL_BSTSFL_I].descr); + } else { + if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set(bval.alrm[BVAL_LOALRM_I].descr); + upslogx(LOG_INFO, "ups.status = LB"); + } + if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set(bval.alrm[BVAL_HIALRM_I].descr); + upslogx(LOG_INFO, "ups.status = HB"); + } + if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); + upslogx(LOG_INFO, "battery start with battery flat"); + } } /* get "battery.voltage" */ rval = get_dev_state(BATV, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); } - dstate_setinfo("battery.voltage", "%s", ds.reg.strval); - /* * update UPS status regarding battery charger status */ @@ -577,22 +609,27 @@ void upsdrv_updateinfo(void) rval = get_dev_state(CHRG, &ds); if (rval == -1) { errcnt++; + } else { + if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { + status_set("CHRG"); + upslogx(LOG_INFO, "ups.status = CHRG"); + } + dstate_setinfo("battery.charger.status", "%s", ds->charge.info); + upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); } - if (ds.charge.state == CHRG_BULK || ds.charge.state == CHRG_ABSR) { - status_set("CHRG"); - } - dstate_setinfo("battery.charger.status", "%s", ds.charge.info); - rval = get_dev_state(PMNG, &ds); if (rval == -1) { errcnt++; - } - if (ds.power.state == PMNG_BCKUP) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - } - if (ds.power.state == PMNG_BOOST) { - status_set("BOOST"); + } else { + if (ds->power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + upslogx(LOG_INFO, "ups.status = DISCHRG"); + } + if (ds->power.state == PMNG_BOOST) { + status_set("BOOST"); + upslogx(LOG_INFO, "ups.status = BOOST"); + } } /* @@ -601,8 +638,10 @@ void upsdrv_updateinfo(void) rval = get_dev_state(BSOC, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.charge", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); } - dstate_setinfo("battery.charge", "%s", ds.reg.strval); /* * update UPS AC input state @@ -610,14 +649,17 @@ void upsdrv_updateinfo(void) rval = get_dev_state(VACA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } } + rval = get_dev_state(VAC, &ds); + dstate_setinfo("input.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); } - rval = get_dev_state(VAC, &ds); - dstate_setinfo("input.voltage", "%s", ds.reg.strval); /* * update UPS onboard temperature state @@ -625,40 +667,49 @@ void upsdrv_updateinfo(void) rval = get_dev_state(OBTA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } } } rval = get_dev_state(OTMP, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("ups.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); } - dstate_setinfo("ups.temperature", "%s", ds.reg.strval); - /* * update UPS battery temperature state */ rval = get_dev_state(BSTA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } rval = get_dev_state(BTMP, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); } - dstate_setinfo("battery.temperature", "%s", ds.reg.strval); rval = get_dev_state(TBUF, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("battery.runtime", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); } - dstate_setinfo("battery.runtime", "%s", ds.reg.strval); /* * update UPS device failure state @@ -666,10 +717,12 @@ void upsdrv_updateinfo(void) rval = get_dev_state(DEVF, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } @@ -679,10 +732,12 @@ void upsdrv_updateinfo(void) rval = get_dev_state(SCSH, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } @@ -692,10 +747,12 @@ void upsdrv_updateinfo(void) rval = get_dev_state(BSTA, &ds); if (rval == -1) { errcnt++; - } - for (i = 0; i < ds.alrm->alrm_c; i++) { - if (ds.alrm[i].alrm->actv) { - alarm_set(ds.alrm[i].alrm->descr); + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } } } @@ -705,16 +762,19 @@ void upsdrv_updateinfo(void) rval = get_dev_state(LVDC, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("output.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); } - dstate_setinfo("output.voltage", "%s", ds.reg.strval); rval = get_dev_state(LCUR, &ds); if (rval == -1) { errcnt++; + } else { + dstate_setinfo("output.current", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); } - dstate_setinfo("output.current", "%s", ds.reg.strval); - - /* check for communication errors */ +<<<<<<< HEAD if (errcnt == 0) { alarm_commit(); status_commit(); @@ -724,12 +784,25 @@ void upsdrv_updateinfo(void) dstate_datastale(); } >>>>>>> under construction +======= + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2, "Communication errors: %d", errcnt); + dstate_datastale(); + } +>>>>>>> ghost alarms bug fix, other bug fixes } /* shutdown UPS */ void upsdrv_shutdown(void) { <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> ghost alarms bug fix, other bug fixes int rval; int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ struct timeval start; @@ -757,6 +830,7 @@ void upsdrv_shutdown(void) break; } upslogx(LOG_INFO, "shutdown command executed"); +<<<<<<< HEAD ======= int rval; int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ @@ -786,6 +860,8 @@ void upsdrv_shutdown(void) } upslogx(LOG_INFO, "shutdown command executed"); >>>>>>> under construction +======= +>>>>>>> ghost alarms bug fix, other bug fixes } /* print driver usage info */ @@ -797,6 +873,9 @@ void upsdrv_help(void) void upsdrv_makevartable(void) { <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> ghost alarms bug fix, other bug fixes addvar(VAR_VALUE, "device_mfr", "device manufacturer"); addvar(VAR_VALUE, "device_model", "device model"); addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); @@ -804,6 +883,7 @@ void upsdrv_makevartable(void) addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); +<<<<<<< HEAD ======= addvar(VAR_VALUE, "device_mfr", "device manufacturer"); addvar(VAR_VALUE, "device_model", "device model"); @@ -813,6 +893,8 @@ void upsdrv_makevartable(void) addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); >>>>>>> under construction +======= +>>>>>>> ghost alarms bug fix, other bug fixes addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); @@ -823,6 +905,9 @@ void upsdrv_makevartable(void) void upsdrv_cleanup(void) { <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> ghost alarms bug fix, other bug fixes if (mbctx != NULL) { modbus_close(mbctx); modbus_free(mbctx); @@ -830,12 +915,15 @@ void upsdrv_cleanup(void) if (dstate != NULL) { free(dstate); } +<<<<<<< HEAD ======= if (mbctx != NULL) { modbus_close(mbctx); modbus_free(mbctx); } >>>>>>> under construction +======= +>>>>>>> ghost alarms bug fix, other bug fixes } /* @@ -851,11 +939,15 @@ void reginit() { int i; /* local index */ +<<<<<<< HEAD <<<<<<< HEAD for (i = 0; i < MODBUS_NUMOF_REGS; i++) { ======= for (i = 1; i < MODBUS_NUMOF_REGS; i++) { >>>>>>> structure device data, code get_dev_state, in progress +======= + for (i = 0; i < MODBUS_NUMOF_REGS; i++) { +>>>>>>> ghost alarms bug fix, other bug fixes int rnum = regs[i].num; switch (regs[i].type) { case COIL: @@ -878,6 +970,7 @@ void reginit() regs[i].xaddr = 0x40000 + rnum - 1; break; default: +<<<<<<< HEAD <<<<<<< HEAD upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); @@ -896,6 +989,16 @@ void reginit() regs[i].saddr, regs[i].xaddr >>>>>>> structure device data, code get_dev_state, in progress +======= + upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + } + upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", + regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr +>>>>>>> ghost alarms bug fix, other bug fixes ); } } @@ -933,6 +1036,7 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) /* Read a modbus register */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) { +<<<<<<< HEAD int rval = -1; /* register bit masks */ @@ -957,17 +1061,46 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) *(uint *)data = *(uint *)data & mask16; break; >>>>>>> under construction +======= + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; +>>>>>>> ghost alarms bug fix, other bug fixes #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> ghost alarms bug fix, other bug fixes /* All enum cases defined as of the time of coding * have been covered above. Handle later definitions, * memory corruptions and buggy inputs below... */ default: +<<<<<<< HEAD #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif @@ -998,37 +1131,48 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) * memory corruptions and buggy inputs below... */ default: +======= +>>>>>>> ghost alarms bug fix, other bug fixes #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); + upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); /* on BROKEN PIPE error try to reconnect */ if (errno == EPIPE) { - upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); modbus_reconnect(); } +<<<<<<< HEAD } upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); return rval; >>>>>>> under construction +======= + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +>>>>>>> ghost alarms bug fix, other bug fixes } /* write a modbus register */ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) { <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> ghost alarms bug fix, other bug fixes int rval = -1; /* register bit masks */ @@ -1047,6 +1191,7 @@ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) case INPUT_B: case INPUT_R: +<<<<<<< HEAD ======= int rval = -1; @@ -1067,11 +1212,16 @@ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) case INPUT_B: case INPUT_R: >>>>>>> under construction +======= +>>>>>>> ghost alarms bug fix, other bug fixes #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> ghost alarms bug fix, other bug fixes /* All enum cases defined as of the time of coding * have been covered above. Handle later definitions, * memory corruptions and buggy inputs below... @@ -1101,6 +1251,7 @@ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) } upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); return rval; +<<<<<<< HEAD ======= /* All enum cases defined as of the time of coding * have been covered above. Handle later definitions, @@ -1132,12 +1283,17 @@ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); return rval; >>>>>>> under construction +======= +>>>>>>> ghost alarms bug fix, other bug fixes } /* returns the time elapsed since start in milliseconds */ long time_elapsed(struct timeval *start) { <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> ghost alarms bug fix, other bug fixes long rval; struct timeval end; @@ -1158,6 +1314,7 @@ long time_elapsed(struct timeval *start) rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; return rval; +<<<<<<< HEAD ======= long rval; struct timeval end; @@ -1180,11 +1337,14 @@ long time_elapsed(struct timeval *start) return rval; >>>>>>> under construction +======= +>>>>>>> ghost alarms bug fix, other bug fixes } /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg) { +<<<<<<< HEAD <<<<<<< HEAD int rval; int data; @@ -1501,8 +1661,12 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) ======= int rval; int data; +======= + int rval; + int data; +>>>>>>> ghost alarms bug fix, other bug fixes - if (!strcasecmp(cmd, "load.off")) { + if (!strcasecmp(cmd, "load.off")) { data = 1; rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); if (rval == -1) { @@ -1519,32 +1683,45 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) rval = STAT_INSTCMD_HANDLED; } } else { - upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_UNKNOWN; - } - return rval; + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; } /* read device state, returns 0 on success or -1 on communication error it formats state depending on register semantics */ -int get_dev_state(devreg_t regnum, devstate_t *state) +int get_dev_state(devreg_t regindx, devstate_t **dstate) { int i; /* local index */ + int n; int rval; /* return value */ - uint reg_val; /* register value */ + static char *ptr = NULL; /* temporary pointer */ + uint num; /* register number */ + uint reg_val; /* register value */ regtype_t rtype; /* register type */ int addr; /* register address */ + devstate_t *state; /* device state */ + + state = *dstate; + num = regs[regindx].num; + addr = regs[regindx].xaddr; + rtype = regs[regindx].type; - addr = regs[regnum].xaddr; - rtype = regs[regnum].type; rval = register_read(mbctx, addr, rtype, ®_val); if (rval == -1) { return rval; } + upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", + num, + addr, + rtype, + reg_val + ); - /* process register data */ - switch (regnum) { - case CHRG: + /* process register data */ + switch (regindx) { + case CHRG: /* "ups.charge" */ if (reg_val == CHRG_NONE) { state->charge.state = CHRG_NONE; state->charge.info = chrgs_i[CHRG_NONE]; @@ -1561,21 +1738,27 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->charge.state = CHRG_FLOAT; state->charge.info = chrgs_i[CHRG_FLOAT]; } - break; + upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); + break; case BATV: /* "battery.voltage" */ case LVDC: /* "output.voltage" */ case LCUR: /* "output.current" */ if (reg_val != 0) { state->reg.val16 = reg_val; double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ - int n = snprintf(NULL, 0, "%f", fval); - char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); - sprintf(fval_s, "%f", fval); + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; } else { state->reg.val16 = 0; - state->reg.strval = "0"; + state->reg.strval = "0.00"; } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case TBUF: case BSOH: @@ -1583,38 +1766,53 @@ int get_dev_state(devreg_t regnum, devstate_t *state) case VAC: /* "input.voltage" */ if (reg_val != 0) { state->reg.val16 = reg_val; - int n = snprintf(NULL, 0, "%d", reg_val); - char *reg_val_s = (char *)malloc(sizeof(char) * (n + 1)); + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; sprintf(reg_val_s, "%d", reg_val); state->reg.strval = reg_val_s; } else { state->reg.val16 = 0; state->reg.strval = "0"; } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case BSOC: /* "battery.charge" */ if (reg_val != 0) { state->reg.val16 = reg_val; double fval = (double )reg_val * regs[BSOC].scale; - int n = snprintf(NULL, 0, "%f", fval); - char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); - sprintf(fval_s, "%f", fval); + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; } else { state->reg.val16 = 0; - state->reg.strval = "0"; + state->reg.strval = "0.00"; } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case BTMP: /* "battery.temperature" */ case OTMP: /* "ups.temperature" */ state->reg.val16 = reg_val; double fval = reg_val - 273.15; - int n = snprintf(NULL, 0, "%f", fval); - char *fval_s = (char *)malloc(sizeof(char) * (n + 1)); - sprintf(fval_s, "%f", fval); + n = snprintf(NULL, 0, "%.2f", fval); + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + if (ptr != NULL) { + free(ptr); + } + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; - case PMNG: /* "ups.status" & "battery.charge" */ + case PMNG: /* "ups.status" & "battery.charge" */ if (reg_val == PMNG_BCKUP) { state->power.state = PMNG_BCKUP; state->power.info = pwrmng_i[PMNG_BCKUP]; @@ -1628,6 +1826,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) state->power.state = PMNG_NCHRG; state->power.info = pwrmng_i[PMNG_NCHRG]; } +<<<<<<< HEAD break; <<<<<<< HEAD @@ -1640,6 +1839,10 @@ int get_dev_state(devreg_t regnum, devstate_t *state) case BOOST_T: >>>>>>> under construction ======= +======= + upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); + break; +>>>>>>> ghost alarms bug fix, other bug fixes case PRDN: /* "ups.model" */ for (i = 0; i < DEV_NUMOF_MODELS; i++) { if (prdnm_i[i].val == reg_val) { @@ -1648,6 +1851,7 @@ int get_dev_state(devreg_t regnum, devstate_t *state) } state->product.val = reg_val; state->product.name = prdnm_i[i].name; + upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); break; <<<<<<< HEAD >>>>>>> structure device data, code get_dev_state, in progress @@ -1783,17 +1987,30 @@ int get_dev_state(devreg_t regnum, devstate_t *state) } state->alrm = &mains; break; +<<<<<<< HEAD >>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress +======= + case OBTA: + if (reg_val == OBTA_HIALRM_V) { + obta.alrm[OBTA_HIALRM_I].actv = 1; + } + state->alrm = &obta; + break; +>>>>>>> ghost alarms bug fix, other bug fixes #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> ghost alarms bug fix, other bug fixes /* All enum cases defined as of the time of coding * have been covered above. Handle later definitions, * memory corruptions and buggy inputs below... */ default: +<<<<<<< HEAD #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif @@ -1816,15 +2033,30 @@ int get_dev_state(devreg_t regnum, devstate_t *state) * memory corruptions and buggy inputs below... */ default: +======= +>>>>>>> ghost alarms bug fix, other bug fixes #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - break; - } + state->reg.val16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + break; + } +<<<<<<< HEAD upsdebugx(3, "get_dev_state: state: %d", reg_val); return rval; >>>>>>> under construction +======= + return rval; +>>>>>>> ghost alarms bug fix, other bug fixes } /* get driver configuration parameters */ @@ -1832,6 +2064,9 @@ void get_config_vars() { <<<<<<< HEAD <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> ghost alarms bug fix, other bug fixes /* check if device manufacturer is set ang get the value */ if (testvar("device_mfr")) { device_mfr = getval("device_mfr"); @@ -1880,6 +2115,7 @@ void get_config_vars() rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); } upsdebugx(2, "rio_slave_id %d", rio_slave_id); +<<<<<<< HEAD ======= int i; /* local index */ @@ -1940,6 +2176,8 @@ void get_config_vars() } upsdebugx(2, "rio_slave_id %d", rio_slave_id); >>>>>>> under construction +======= +>>>>>>> ghost alarms bug fix, other bug fixes /* check if response time out (s) is set ang get the value */ if (testvar("mod_resp_to_s")) { @@ -2193,6 +2431,9 @@ void get_config_vars() modbus_t *modbus_new(const char *port) { <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> ghost alarms bug fix, other bug fixes modbus_t *mb; char *sp; if (strstr(port, "/dev/tty") != NULL) { @@ -2216,6 +2457,7 @@ modbus_t *modbus_new(const char *port) } } return mb; +<<<<<<< HEAD ======= modbus_t *mb; char *sp; @@ -2241,6 +2483,8 @@ modbus_t *modbus_new(const char *port) } return mb; >>>>>>> under construction +======= +>>>>>>> ghost alarms bug fix, other bug fixes } /* reconnect to modbus server upon connection error */ @@ -2248,11 +2492,15 @@ void modbus_reconnect() { int rval; +<<<<<<< HEAD <<<<<<< HEAD upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); ======= upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); >>>>>>> under construction +======= + upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); +>>>>>>> ghost alarms bug fix, other bug fixes /* clear current modbus context */ modbus_close(mbctx); @@ -2291,7 +2539,11 @@ void modbus_reconnect() fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); } <<<<<<< HEAD +<<<<<<< HEAD } ======= } >>>>>>> under construction +======= +} +>>>>>>> ghost alarms bug fix, other bug fixes diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 81e64ed71d..5e9163a82c 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -4,6 +4,7 @@ * * Copyright (C) * 2022 Dimitris Economou +<<<<<<< HEAD ======= /* adele_cbi.h - Driver for generic UPS connected via modbus RIO ======= @@ -13,6 +14,8 @@ * Copyright (C) * 2021 Dimitris Economou >>>>>>> under construction +======= +>>>>>>> ghost alarms bug fix, other bug fixes * * 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 @@ -183,10 +186,10 @@ typedef struct alrm_ar alrm_ar_t; >>>>>>> first testing release /* definition of register type */ enum regtype { - COIL = 0, - INPUT_B, - INPUT_R, - HOLDING + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING }; typedef enum regtype regtype_t; @@ -197,6 +200,7 @@ struct prodname { }; typedef struct prodname prodname_t; <<<<<<< HEAD +<<<<<<< HEAD <<<<<<< HEAD /* BIT MASKS and VALUES */ @@ -207,6 +211,9 @@ prodname_t prdn[] = { ======= prodname_t prdnm_i[] = { >>>>>>> structure device data, code get_dev_state, in progress +======= +static prodname_t prdnm_i[] = { +>>>>>>> ghost alarms bug fix, other bug fixes {1, "CBI1235A"}, {2, "CBI2420A"}, {3, "CBI4810A"}, @@ -221,7 +228,7 @@ prodname_t prdnm_i[] = { }; /* charging status info, "battery.charger.status" */ -char *chrgs_i[] = { +static char *chrgs_i[] = { "none", "resting", /* recovering */ "charging", /* bulk */ @@ -235,7 +242,7 @@ struct chrgs { typedef struct chrgs chrgs_t; /* power management info, "ups.status", "battery.charger.status" */ -char *pwrmng_i[] = { +static char *pwrmng_i[] = { "backup", /* "OB", "discharging" */ "charging", /* "OL" */ "boost", @@ -315,6 +322,7 @@ typedef struct alrm_ar alrm_ar_t; /* Onboard temperature alarm index */ #define OBTA_HIALRM_I 0 /* high alarm */ +<<<<<<< HEAD /* Device failure alarm masks */ #define DEVF_RCALRM_M 0x0001 /* rectifier failure */ @@ -538,6 +546,8 @@ static regattr_t regs[] = { /* Onboard temperature alarm */ #define OBTA_HIALRM 1 /* high alarm */ +======= +>>>>>>> ghost alarms bug fix, other bug fixes /* Device failure alarm masks */ #define DEVF_RCALRM_M 0x0001 /* rectifier failure */ @@ -596,7 +606,7 @@ static regattr_t regs[] = { #define BSTA_CNNFLT_I 5 /* connection fault */ /* input mains and shutdown alarms */ -alrm_ar_t mains = { +static alrm_ar_t mains = { 2, { {0, "input voltage not available"}, @@ -605,7 +615,7 @@ alrm_ar_t mains = { }; /* AC input voltage alarms */ -alrm_ar_t vaca = { +static alrm_ar_t vaca = { 2, { {0, "input voltage high alarm"}, @@ -614,7 +624,7 @@ alrm_ar_t vaca = { }; /* device failure alarms */ -alrm_ar_t devf = { +static alrm_ar_t devf = { 3, { {0, "UPS rectifier failure"}, @@ -624,7 +634,7 @@ alrm_ar_t devf = { }; /* battery sensor failure alarms */ -alrm_ar_t btsf = { +static alrm_ar_t btsf = { 2, { {0, "battery temp sensor connection fault"}, @@ -633,7 +643,7 @@ alrm_ar_t btsf = { }; /* battery voltage alarms */ -alrm_ar_t bval = { +static alrm_ar_t bval = { 3, { {0, "battery high voltage"}, @@ -643,7 +653,7 @@ alrm_ar_t bval = { }; /* battery SoH and SoC alarms */ -alrm_ar_t shsc = { +static alrm_ar_t shsc = { 4, { {0, "battery high internal resistance"}, @@ -654,7 +664,7 @@ alrm_ar_t shsc = { }; /* battery status alarm */ -alrm_ar_t bsta = { +static alrm_ar_t bsta = { 6, { {0, "battery reversed polarity"}, @@ -666,6 +676,14 @@ alrm_ar_t bsta = { } }; +/* onboard temperature alarm */ +static alrm_ar_t obta = { + 1, + { + {0, "onboard temperature high"} + } +}; + /* UPS device reg enum */ enum devreg { CHRG = 0, /* Charging status, "battery.charger.status" */ @@ -716,8 +734,12 @@ union devstate { typedef union devstate devstate_t; /* ADELE CBI registers */ +<<<<<<< HEAD regattr_t regs[] = { >>>>>>> under construction +======= +static regattr_t regs[] = { +>>>>>>> ghost alarms bug fix, other bug fixes {40005, 0, 0, 1, HOLDING}, /* Charging status */ {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ @@ -734,6 +756,7 @@ regattr_t regs[] = { {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ {40006, 0, 0, 1, HOLDING}, /* Power management +<<<<<<< HEAD <<<<<<< HEAD * 0:Backup 1:Charging 2:boost 3:Not charging */ @@ -741,6 +764,10 @@ regattr_t regs[] = { * 0:Backup 1:Charging 2:boost 3:Not charging */ >>>>>>> under construction +======= + * 0:Backup 1:Charging 2:boost 3:Not charging + */ +>>>>>>> ghost alarms bug fix, other bug fixes {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ {40010, 0, 0, 1, HOLDING}, /* Software ID */ From 9f44f94da67ad5e39c8727393706621c4ec68526 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Wed, 12 Jan 2022 22:44:26 +0200 Subject: [PATCH 060/700] read_all_regs aproach integrated --- drivers/adele_cbi.c | 49 ++++++++++++++++++++++++++++++++++++++++++--- drivers/adele_cbi.h | 6 ++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index f06a6b40eb..87ebd3e554 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -97,6 +97,9 @@ static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus by /* initialize register start address and hex address from register number */ void reginit(); +/* read all registers */ +int read_all_regs(modbus_t *mb, uint16_t *data); + /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); @@ -269,7 +272,12 @@ void upsdrv_updateinfo(void) errcnt = 0; /* initialize error counter to zero */ status_init(); /* initialize ups.status update */ alarm_init(); /* initialize ups.alarm update */ - +#if READALL_REGS == 1 + rval = read_all_regs(mbctx, regs_data); + if (rval == -1) { + errcnt++; + } else { +#endif /* * update UPS status regarding MAINS and SHUTDOWN request * - OL: On line (mains is present) @@ -497,6 +505,9 @@ void upsdrv_updateinfo(void) dstate_setinfo("output.current", "%s", ds->reg.strval); upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); } +#if READALL_REGS == 1 + } +#endif /* check for communication errors */ if (errcnt == 0) { alarm_commit(); @@ -570,6 +581,7 @@ void upsdrv_updateinfo(void) } } +<<<<<<< HEAD /* * update UPS status regarding battery voltage */ @@ -592,6 +604,34 @@ void upsdrv_updateinfo(void) upslogx(LOG_INFO, "battery start with battery flat"); } } +======= +/* read all register data image */ +int read_all_regs(modbus_t *mb, uint16_t *data) +{ + int rval; + + rval = modbus_read_registers(mb, regs[CHRG].xaddr, MAX_REGS, regs_data); + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", + modbus_strerror(errno), + regs[CHRG].xaddr, + MAX_REGS, + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } +} + +/* Read a modbus register */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; +>>>>>>> read_all_regs aproach integrated /* get "battery.voltage" */ rval = get_dev_state(BATV, &ds); @@ -1707,7 +1747,10 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) num = regs[regindx].num; addr = regs[regindx].xaddr; rtype = regs[regindx].type; - +#if READALL_REGS == 1 + reg_val = regs_data[regindx]; + rval = 0; +#elif READALL_REGS == 0 rval = register_read(mbctx, addr, rtype, ®_val); if (rval == -1) { return rval; @@ -1718,7 +1761,7 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) rtype, reg_val ); - +#endif /* process register data */ switch (regindx) { case CHRG: /* "ups.charge" */ diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 5e9163a82c..fc696c8a69 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -80,6 +80,9 @@ /* number of modbus registers */ #define MODBUS_NUMOF_REGS 98 +/* max registers */ +#define MAX_REGS 116 + /* number of device models */ #define DEV_NUMOF_MODELS 10 @@ -508,6 +511,9 @@ union devstate { typedef union devstate devstate_t; +/* device register memory image */ +static uint16_t regs_data[MAX_REGS]; + /* ADELE CBI registers */ static regattr_t regs[] = { ======= From 0b19b35e38d5ed55f928a293304a12ba41a9423b Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Wed, 12 Jan 2022 23:14:20 +0200 Subject: [PATCH 061/700] some fixes in read_all_regs approach --- drivers/adele_cbi.c | 11 +++++++---- drivers/adele_cbi.h | 3 +++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index 87ebd3e554..8d65624b08 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -625,6 +625,7 @@ int read_all_regs(modbus_t *mb, uint16_t *data) modbus_reconnect(); } } + return rval; } /* Read a modbus register */ @@ -1737,20 +1738,22 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) int n; int rval; /* return value */ static char *ptr = NULL; /* temporary pointer */ - uint num; /* register number */ uint reg_val; /* register value */ +#if READALL_REGS == 0 + uint num; /* register number */ regtype_t rtype; /* register type */ int addr; /* register address */ +#endif devstate_t *state; /* device state */ state = *dstate; - num = regs[regindx].num; - addr = regs[regindx].xaddr; - rtype = regs[regindx].type; #if READALL_REGS == 1 reg_val = regs_data[regindx]; rval = 0; #elif READALL_REGS == 0 + num = regs[regindx].num; + addr = regs[regindx].xaddr; + rtype = regs[regindx].type; rval = register_read(mbctx, addr, rtype, ®_val); if (rval == -1) { return rval; diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index fc696c8a69..bf60cf5bc6 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -83,6 +83,9 @@ /* max registers */ #define MAX_REGS 116 +/* read all regs */ +#define READALL_REGS 1 + /* number of device models */ #define DEV_NUMOF_MODELS 10 From 6d6258533a4b24637164cd1336235e6e8cda2c23 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 00:22:29 +0200 Subject: [PATCH 062/700] modify regs_data memory, devreg enum rearrangement --- drivers/adele_cbi.h | 175 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 152 insertions(+), 23 deletions(-) diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index bf60cf5bc6..9877dea968 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -467,29 +467,29 @@ static alrm_ar_t obta = { /* UPS device reg enum */ enum devreg { - CHRG = 0, /* Charging status, "battery.charger.status" */ - BATV, /* Battery voltage, "battery.voltage" */ - BCEF = 6, /* Battery charge efficiency factor (CEF) */ - BSOH, /* Battery state-of-health */ - BSOC = 9, /* Battery state-of-charge, "battery.charge" */ - BTMP = 11, /* Battery temperature in Kelvin units, "battery.temperature" */ - PMNG = 15, /* Power management, "ups.status" */ - OTMP = 20, /* Onboard temperature, "ups.temperature" */ - PRDN, /* Product name, "ups.model" */ - VAC = 24, /* AC voltage, "input.voltage" */ - LVDC, /* Load voltage, "output.voltage" */ - LCUR, /* Load current, "output.current" */ - BINH = 79, /* Backup inhibit */ - FSD, /* Force shutdown */ - TBUF, /* Time buffering, "battery.runtime" */ - BSTA = 89, /* Battery status alarms */ - SCSH, /* SoH and SoC alarms */ - BVAL, /* Battery voltage alarm */ - BTSF, /* Battery temp sensor failure */ - DEVF, /* Device failure */ - OBTA, /* On board temp alarm */ - VACA, /* VAC alarms */ - MAIN /* Mains status */ + CHRG = 4, /* Charging status, "battery.charger.status" */ + BATV = 7, /* Battery voltage, "battery.voltage" */ + BCEF = 18, /* Battery charge efficiency factor (CEF) */ + BSOH = 20, /* Battery state-of-health */ + BSOC = 22, /* Battery state-of-charge, "battery.charge" */ + BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ + PMNG = 5, /* Power management, "ups.status" */ + OTMP = 28, /* Onboard temperature, "ups.temperature" */ + PRDN = 67, /* Product name, "ups.model" */ + VAC = 29, /* AC voltage, "input.voltage" */ + LVDC = 10, /* Load voltage, "output.voltage" */ + LCUR = 19, /* Load current, "output.current" */ + BINH = 87, /* Backup inhibit */ + FSD = 40, /* Force shutdown */ + TBUF = 103, /* Time buffering, "battery.runtime" */ + BSTA = 31, /* Battery status alarms */ + SCSH, /* SoH and SoC alarms */ + BVAL = 34, /* Battery voltage alarm */ + BTSF = 43, /* Battery temp sensor failure */ + DEVF = 42, /* Device failure */ + OBTA = 46, /* On board temp alarm */ + VACA = 44, /* VAC alarms */ + MAIN /* Mains status */ }; typedef enum devreg devreg_t; @@ -519,6 +519,7 @@ static uint16_t regs_data[MAX_REGS]; /* ADELE CBI registers */ static regattr_t regs[] = { +<<<<<<< HEAD ======= ======= @@ -885,6 +886,134 @@ static regattr_t regs[] = { <<<<<<< HEAD {40046, 0, 0, 1, HOLDING}, /* Mains status */ {40038, 0, 0, 1, HOLDING} /* Load alarm */ +======= + {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ + {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ + {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ + {40004, 0, 0, 1, HOLDING}, + {40005, 0, 0, 1, HOLDING}, /* Charging status */ + {40006, 0, 0, 1, HOLDING}, /* Power management + * 0:Backup 1:Charging 2:boost 3:Not charging + */ + {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ + {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ + {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ + {40010, 0, 0, 1, HOLDING}, /* Software ID */ + {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ + {40012, 0, 0, 1, HOLDING}, + {40013, 0, 0, 1, HOLDING}, + {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ + {40015, 0, 0, 1, HOLDING}, + {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ + {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ + {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ + {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ + {40020, 0, 0, 1, HOLDING}, /* Output load current */ + {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ + {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ + {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ + {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ + {40025, 0, 0, 1, HOLDING}, + {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ + {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ + {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ + {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ + {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ + {40031, 0, 0, 1, HOLDING}, + {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ + {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ + {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ + {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ + {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ + {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ + {40038, 0, 0, 1, HOLDING}, /* Load alarm */ + {40039, 0, 0, 1, HOLDING}, /* Device variant */ + {40040, 0, 0, 1, HOLDING}, + {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ + {40042, 0, 0, 1, HOLDING}, + {40043, 0, 0, 1, HOLDING}, /* Device failure */ + {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ + {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ + {40046, 0, 0, 1, HOLDING}, /* Mains status */ + {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ + {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ + {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ + {40050, 0, 0, .1, HOLDING}, /* Ah charged */ + {40051, 0, 0, 1, HOLDING}, /* Total run time */ + {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ + {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ + {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ + {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ + {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ + {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ + {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ + {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ + {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ + {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ + {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ + {40065, 0, 0, 1, HOLDING}, /* History clear all */ + {40066, 0, 0, 1, HOLDING}, /* Factory settings */ + {40067, 0, 0, 1, HOLDING}, /* Product name */ + {40068, 0, 0, 1, HOLDING}, + {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ + {40070, 0, 0, 1, HOLDING}, + {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ + {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ + {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ + {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ + {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ + {40076, 0, 0, 1, HOLDING}, + {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ + {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ + {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ + {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ + {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ + {40082, 0, 0, 1, HOLDING}, /* Float voltage */ + {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ + {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ + {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ + {40086, 0, 0, 1, HOLDING}, + {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed + * 1 = Backup not allowed + */ + {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ + {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ + {40091, 0, 0, 1, HOLDING}, + {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ + {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ + {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ + {40095, 0, 0, 1, HOLDING}, + {40096, 0, 0, 1, HOLDING}, + {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ + {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ + {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ + {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ + {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ + {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ + {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ + {40104, 0, 0, 1, HOLDING}, /* Time buffering */ + {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ + {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ + {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ + {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ + {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ + {40110, 0, 0, 1, HOLDING}, + {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ + {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ + {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ + {40114, 0, 0, 1, HOLDING}, + {40115, 0, 0, 1, HOLDING}, + {40116, 0, 0, 1, HOLDING}, + {40117, 0, 0, 1, HOLDING}, + {40118, 0, 0, 1, HOLDING}, + {40119, 0, 0, 1, HOLDING}, + {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ + + +>>>>>>> modify regs_data memory, devreg enum rearrangement }; ======= {40046, 0, 0, 1, HOLDING}, /* Input mains on / backup */ From 70a4b1796a8445a3536db084819c77c8c24c60ef Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 00:39:12 +0200 Subject: [PATCH 063/700] minor bug fixes --- drivers/adele_cbi.c | 2 +- drivers/adele_cbi.h | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index 8d65624b08..5d0c8837c7 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -610,7 +610,7 @@ int read_all_regs(modbus_t *mb, uint16_t *data) { int rval; - rval = modbus_read_registers(mb, regs[CHRG].xaddr, MAX_REGS, regs_data); + rval = modbus_read_registers(mb, regs[REG_STARTIDX].xaddr, MAX_REGS, regs_data); if (rval == -1) { upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", modbus_strerror(errno), diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 9877dea968..0c1ce46999 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -81,7 +81,10 @@ #define MODBUS_NUMOF_REGS 98 /* max registers */ -#define MAX_REGS 116 +#define MAX_REGS 120 + +/* start register number */ +#define REG_STARTIDX 0 /* read all regs */ #define READALL_REGS 1 From e213ae7561a37cdb4c8f1518e2c2a410da138a0f Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 00:55:39 +0200 Subject: [PATCH 064/700] macro name changes --- drivers/adele_cbi.c | 10 +++++++--- drivers/adele_cbi.h | 12 ++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c index 5d0c8837c7..4459117472 100644 --- a/drivers/adele_cbi.c +++ b/drivers/adele_cbi.c @@ -610,12 +610,13 @@ int read_all_regs(modbus_t *mb, uint16_t *data) { int rval; - rval = modbus_read_registers(mb, regs[REG_STARTIDX].xaddr, MAX_REGS, regs_data); + /* read all HOLDING registers */ + rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, regs_data); if (rval == -1) { upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", modbus_strerror(errno), - regs[CHRG].xaddr, - MAX_REGS, + regs[H_REG_STARTIDX].xaddr, + MAX_H_REGS, device_path ); @@ -625,6 +626,9 @@ int read_all_regs(modbus_t *mb, uint16_t *data) modbus_reconnect(); } } + + /* no COIL, INPUT_B or INPUT_R register regions to read */ + return rval; } diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h index 0c1ce46999..6871a32bbd 100644 --- a/drivers/adele_cbi.h +++ b/drivers/adele_cbi.h @@ -80,13 +80,13 @@ /* number of modbus registers */ #define MODBUS_NUMOF_REGS 98 -/* max registers */ -#define MAX_REGS 120 +/* max HOLDING registers */ +#define MAX_H_REGS 120 -/* start register number */ -#define REG_STARTIDX 0 +/* start HOLDING register index */ +#define H_REG_STARTIDX 0 -/* read all regs */ +/* read all regs preprocessor flag */ #define READALL_REGS 1 /* number of device models */ @@ -518,7 +518,7 @@ union devstate { typedef union devstate devstate_t; /* device register memory image */ -static uint16_t regs_data[MAX_REGS]; +static uint16_t regs_data[MAX_H_REGS]; /* ADELE CBI registers */ static regattr_t regs[] = { From 48099c8ee49daadf3409b9aa0dbc095c17c920e6 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 01:18:19 +0200 Subject: [PATCH 065/700] adele changed to adelsystems --- drivers/Makefile.am | 8 +- drivers/adelsystems_cbi.c | 1201 +++++++++++++++++++++++++++++++++++++ drivers/adelsystems_cbi.h | 510 ++++++++++++++++ 3 files changed, 1715 insertions(+), 4 deletions(-) create mode 100644 drivers/adelsystems_cbi.c create mode 100644 drivers/adelsystems_cbi.h diff --git a/drivers/Makefile.am b/drivers/Makefile.am index b62262500c..e364c5a7e7 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -50,7 +50,7 @@ SERIAL_USB_DRIVERLIST = \ nutdrv_qx NEONXML_DRIVERLIST = netxml-ups MACOSX_DRIVERLIST = macosx-ups -MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 adele_cbi +MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 adelsystems_cbi LINUX_I2C_DRIVERLIST = asem pijuice # distribute all drivers, even ones that are not built by default @@ -273,8 +273,8 @@ phoenixcontact_modbus_SOURCES = phoenixcontact_modbus.c phoenixcontact_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) generic_modbus_SOURCES = generic_modbus.c generic_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) -adele_cbi_SOURCES = adele_cbi.c -adele_cbi_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) +adelsystems_cbi_SOURCES = adelsystems_cbi.c +adelsystems_cbi_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) # Huawei UPS2000 driver # (this is both a Modbus and a serial driver) @@ -331,7 +331,7 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h b xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ apc-pdu-mib.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h \ eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h \ - hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adele_cbi + hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adelsystems_cbi # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, diff --git a/drivers/adelsystems_cbi.c b/drivers/adelsystems_cbi.c new file mode 100644 index 0000000000..8bb283e1f8 --- /dev/null +++ b/drivers/adelsystems_cbi.c @@ -0,0 +1,1201 @@ +/* adele_cbi.c - driver for ADELE CB/CBI DC-UPS + * + * Copyright (C) + * 2022 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "adelsystems_cbi.h" +#include +#include + +#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" +#define DRIVER_VERSION "0.01" + +/* variables */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static devstate_t *dstate = NULL; /* device state context */ +static int errcnt = 0; /* modbus access error counter */ +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static char *device_type = DEVICE_TYPE; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ + + +/* initialize register start address and hex address from register number */ +void reginit(); + +/* read all registers */ +int read_all_regs(modbus_t *mb, uint16_t *data); + +/* get config vars set by -x or defined in ups.conf driver section */ +void get_config_vars(void); + +/* get device state */ +int get_dev_state(devreg_t regindx, devstate_t **dstate); + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port); + +/* reconnect upon communication error */ +void modbus_reconnect(); + +/* modbus register read function */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data); + +/* modbus register write function */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg); + +/* count the time elapsed since start */ +long time_elapsed(struct timeval *start); + + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} +}; + +/* + * driver functions + */ + +/* read configuration variables from ups.conf and connect to ups device */ +void upsdrv_initups(void) +{ + int rval; + upsdebugx(2, "upsdrv_initups"); + + + dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); + reginit(); + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response time out to 200ms */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} + +/* initialize ups driver information */ +void upsdrv_initinfo(void) +{ + devstate_t *ds = dstate; /* device state context */ + upsdebugx(2, "upsdrv_initinfo"); + + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); + dstate_setinfo("device.type", "%s", device_type); + + /* read ups model */ + get_dev_state(PRDN, &ds); + dstate_setinfo("ups.model", "%s", ds->product.name); + upslogx(LOG_INFO, "ups.model = %s", ds->product.name); + + /* register instant commands */ + dstate_addcmd("load.off"); + + /* set callback for instant commands */ + upsh.instcmd = upscmd; +} + + +/* update UPS signal state */ +void upsdrv_updateinfo(void) +{ + int rval; /* return value */ + int i; /* local index */ + devstate_t *ds = dstate; /* device state */ + + upsdebugx(2, "upsdrv_updateinfo"); + + errcnt = 0; /* initialize error counter to zero */ + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ +#if READALL_REGS == 1 + rval = read_all_regs(mbctx, regs_data); + if (rval == -1) { + errcnt++; + } else { +#endif + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ + rval = get_dev_state(MAIN, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains.alrm[MAINS_AVAIL_I].descr); + upslogx(LOG_INFO, "ups.status = OB"); + } else { + status_set("OL"); + upslogx(LOG_INFO, "ups.status = OL"); + } + if (ds->alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains.alrm[SHUTD_REQST_I].descr); + upslogx(LOG_INFO, "ups.status = FSD"); + } + } + + /* + * update UPS status regarding battery voltage + */ + rval = get_dev_state(BVAL, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set(bval.alrm[BVAL_LOALRM_I].descr); + upslogx(LOG_INFO, "ups.status = LB"); + } + if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set(bval.alrm[BVAL_HIALRM_I].descr); + upslogx(LOG_INFO, "ups.status = HB"); + } + if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); + upslogx(LOG_INFO, "battery start with battery flat"); + } + } + + /* get "battery.voltage" */ + rval = get_dev_state(BATV, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); + } + /* + * update UPS status regarding battery charger status + */ + + /* get "battery.charger.status" */ + rval = get_dev_state(CHRG, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { + status_set("CHRG"); + upslogx(LOG_INFO, "ups.status = CHRG"); + } + dstate_setinfo("battery.charger.status", "%s", ds->charge.info); + upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); + } + rval = get_dev_state(PMNG, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + upslogx(LOG_INFO, "ups.status = DISCHRG"); + } + if (ds->power.state == PMNG_BOOST) { + status_set("BOOST"); + upslogx(LOG_INFO, "ups.status = BOOST"); + } + } + + /* + * update UPS battery state of charge + */ + rval = get_dev_state(BSOC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.charge", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); + } + + /* + * update UPS AC input state + */ + rval = get_dev_state(VACA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } + } + rval = get_dev_state(VAC, &ds); + dstate_setinfo("input.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); + } + + /* + * update UPS onboard temperature state + */ + rval = get_dev_state(OBTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(OTMP, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("ups.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); + } + /* + * update UPS battery temperature state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(BTMP, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); + } + rval = get_dev_state(TBUF, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.runtime", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); + } + + /* + * update UPS device failure state + */ + rval = get_dev_state(DEVF, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS SoH and SoC states + */ + rval = get_dev_state(SCSH, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS battery state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS load status + */ + rval = get_dev_state(LVDC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("output.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); + } + rval = get_dev_state(LCUR, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("output.current", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); + } +#if READALL_REGS == 1 + } +#endif + /* check for communication errors */ + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2, "Communication errors: %d", errcnt); + dstate_datastale(); + } +} + +/* shutdown UPS */ +void upsdrv_shutdown(void) +{ + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); +} + +/* print driver usage info */ +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); + addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); + addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); + addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); +} + +/* close modbus connection and free modbus context allocated memory */ +void upsdrv_cleanup(void) +{ + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } + if (dstate != NULL) { + free(dstate); + } +} + +/* + * driver support functions + */ + +/* initialize register start address and hex address from register number */ +void reginit() +{ + int i; /* local index */ + + for (i = 0; i < MODBUS_NUMOF_REGS; i++) { + int rnum = regs[i].num; + switch (regs[i].type) { + case COIL: + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x0 + regs[i].num - 1; + break; + case INPUT_B: + rnum -= 10000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x10000 + rnum - 1; + break; + case INPUT_R: + rnum -= 30000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x30000 + rnum - 1; + break; + case HOLDING: + rnum -= 40000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x40000 + rnum - 1; + break; + default: + upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + } + upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", + regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr + ); + } +} + +/* read all register data image */ +int read_all_regs(modbus_t *mb, uint16_t *data) +{ + int rval; + + /* read all HOLDING registers */ + rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, regs_data); + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", + modbus_strerror(errno), + regs[H_REG_STARTIDX].xaddr, + MAX_H_REGS, + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + + /* no COIL, INPUT_B or INPUT_R register regions to read */ + + return rval; +} + +/* Read a modbus register */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* write a modbus register */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* returns the time elapsed since start in milliseconds */ +long time_elapsed(struct timeval *start) +{ + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; +} + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg) +{ + int rval; + int data; + + if (!strcasecmp(cmd, "load.off")) { + data = 1; + rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + regs[FSD].xaddr, + regs[FSD].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); + rval = STAT_INSTCMD_HANDLED; + } + } else { + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; +} + +/* read device state, returns 0 on success or -1 on communication error + it formats state depending on register semantics */ +int get_dev_state(devreg_t regindx, devstate_t **dstate) +{ + int i; /* local index */ + int n; + int rval; /* return value */ + static char *ptr = NULL; /* temporary pointer */ + uint reg_val; /* register value */ +#if READALL_REGS == 0 + uint num; /* register number */ + regtype_t rtype; /* register type */ + int addr; /* register address */ +#endif + devstate_t *state; /* device state */ + + state = *dstate; +#if READALL_REGS == 1 + reg_val = regs_data[regindx]; + rval = 0; +#elif READALL_REGS == 0 + num = regs[regindx].num; + addr = regs[regindx].xaddr; + rtype = regs[regindx].type; + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval == -1) { + return rval; + } + upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", + num, + addr, + rtype, + reg_val + ); +#endif + /* process register data */ + switch (regindx) { + case CHRG: /* "ups.charge" */ + if (reg_val == CHRG_NONE) { + state->charge.state = CHRG_NONE; + state->charge.info = chrgs_i[CHRG_NONE]; + } else if (reg_val == CHRG_RECV) { + state->charge.state = CHRG_RECV; + state->charge.info = chrgs_i[CHRG_RECV]; + } else if (reg_val == CHRG_BULK) { + state->charge.state = CHRG_BULK; + state->charge.info = chrgs_i[CHRG_BULK]; + } else if (reg_val == CHRG_ABSR) { + state->charge.state = CHRG_ABSR; + state->charge.info = chrgs_i[CHRG_ABSR]; + } else if (reg_val == CHRG_FLOAT) { + state->charge.state = CHRG_FLOAT; + state->charge.info = chrgs_i[CHRG_FLOAT]; + } + upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); + break; + case BATV: /* "battery.voltage" */ + case LVDC: /* "output.voltage" */ + case LCUR: /* "output.current" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0.00"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case TBUF: + case BSOH: + case BCEF: + case VAC: /* "input.voltage" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case BSOC: /* "battery.charge" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = (double )reg_val * regs[BSOC].scale; + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0.00"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case BTMP: /* "battery.temperature" */ + case OTMP: /* "ups.temperature" */ + state->reg.val16 = reg_val; + double fval = reg_val - 273.15; + n = snprintf(NULL, 0, "%.2f", fval); + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + if (ptr != NULL) { + free(ptr); + } + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case PMNG: /* "ups.status" & "battery.charge" */ + if (reg_val == PMNG_BCKUP) { + state->power.state = PMNG_BCKUP; + state->power.info = pwrmng_i[PMNG_BCKUP]; + } else if (reg_val == PMNG_CHRGN) { + state->power.state = PMNG_CHRGN; + state->power.info = pwrmng_i[PMNG_CHRGN]; + } else if (reg_val == PMNG_BOOST) { + state->power.state = PMNG_BOOST; + state->power.info = pwrmng_i[PMNG_BOOST]; + } else if (reg_val == PMNG_NCHRG) { + state->power.state = PMNG_NCHRG; + state->power.info = pwrmng_i[PMNG_NCHRG]; + } + upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); + break; + case PRDN: /* "ups.model" */ + for (i = 0; i < DEV_NUMOF_MODELS; i++) { + if (prdnm_i[i].val == reg_val) { + break; + } + } + state->product.val = reg_val; + state->product.name = prdnm_i[i].name; + upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); + break; + case BSTA: + if (reg_val & BSTA_REVPOL_M) { + bsta.alrm[BSTA_REVPOL_I].actv = 1; + } else { + bsta.alrm[BSTA_REVPOL_I].actv = 0; + } + if (reg_val & BSTA_NOCNND_M) { + bsta.alrm[BSTA_NOCNND_I].actv = 1; + } else { + bsta.alrm[BSTA_NOCNND_I].actv = 0; + } + if (reg_val & BSTA_CLSHCR_M) { + bsta.alrm[BSTA_CLSHCR_I].actv = 1; + } else { + bsta.alrm[BSTA_CLSHCR_I].actv = 0; + } + if (reg_val & BSTA_SULPHD_M) { + bsta.alrm[BSTA_SULPHD_I].actv = 1; + } else { + bsta.alrm[BSTA_SULPHD_I].actv = 0; + } + if (reg_val & BSTA_CHEMNS_M) { + bsta.alrm[BSTA_CHEMNS_I].actv = 1; + } else { + bsta.alrm[BSTA_CHEMNS_I].actv = 0; + } + if (reg_val & BSTA_CNNFLT_M) { + bsta.alrm[BSTA_CNNFLT_I].actv = 1; + } else { + bsta.alrm[BSTA_CNNFLT_I].actv = 0; + } + state->alrm = &bsta; + break; + case SCSH: + if (reg_val & SHSC_HIRESI_M) { + shsc.alrm[SHSC_HIRESI_I].actv = 1; + } else { + shsc.alrm[SHSC_HIRESI_I].actv = 0; + } + if (reg_val & SHSC_LOCHEF_M) { + shsc.alrm[SHSC_LOCHEF_I].actv = 1; + } else { + shsc.alrm[SHSC_LOCHEF_I].actv = 0; + } + if (reg_val & SHSC_LOEFCP_M) { + shsc.alrm[SHSC_LOEFCP_I].actv = 1; + } else { + shsc.alrm[SHSC_LOEFCP_I].actv = 0; + } + if (reg_val & SHSC_LOWSOC_M) { + shsc.alrm[SHSC_LOWSOC_I].actv = 1; + } else { + shsc.alrm[SHSC_LOWSOC_I].actv = 0; + } + state->alrm = &shsc; + break; + case BVAL: + if (reg_val & BVAL_HIALRM_M) { + bval.alrm[BVAL_HIALRM_I].actv = 1; + } else { + bval.alrm[BVAL_HIALRM_I].actv = 0; + } + if (reg_val & BVAL_LOALRM_M) { + bval.alrm[BVAL_LOALRM_I].actv = 1; + } else { + bval.alrm[BVAL_LOALRM_I].actv = 0; + } + if (reg_val & BVAL_BSTSFL_M) { + bval.alrm[BVAL_BSTSFL_I].actv = 1; + } else { + bval.alrm[BVAL_BSTSFL_I].actv = 0; + } + state->alrm = &bval; + break; + case BTSF: + if (reg_val & BTSF_FCND_M) { + btsf.alrm[BTSF_FCND_I].actv = 1; + } else { + btsf.alrm[BTSF_FCND_I].actv = 0; + } + if (reg_val & BTSF_NCND_M) { + btsf.alrm[BTSF_NCND_I].actv = 1; + } else { + btsf.alrm[BTSF_NCND_I].actv = 0; + } + state->alrm = &btsf; + break; + case DEVF: + if (reg_val & DEVF_RCALRM_M) { + devf.alrm[DEVF_RCALRM_I].actv = 1; + } else { + devf.alrm[DEVF_RCALRM_I].actv = 0; + } + if (reg_val & DEVF_INALRM_M) { + devf.alrm[DEVF_INALRM_I].actv = 1; + } else { + devf.alrm[DEVF_INALRM_I].actv = 0; + } + if (reg_val & DEVF_LFNAVL_M) { + devf.alrm[DEVF_LFNAVL_I].actv = 1; + } else { + devf.alrm[DEVF_LFNAVL_I].actv = 0; + } + state->alrm = &devf; + break; + case VACA: + if (reg_val & VACA_HIALRM_M) { + vaca.alrm[VACA_HIALRM_I].actv = 1; + } else { + vaca.alrm[VACA_HIALRM_I].actv = 0; + } + if (reg_val == VACA_LOALRM_M) { + vaca.alrm[VACA_LOALRM_I].actv = 1; + } else { + vaca.alrm[VACA_LOALRM_I].actv = 0; + } + state->alrm = &vaca; + break; + case MAIN: + if (reg_val & MAINS_AVAIL_M) { + mains.alrm[MAINS_AVAIL_I].actv = 1; + } else { + mains.alrm[MAINS_AVAIL_I].actv = 0; + } + if (reg_val == SHUTD_REQST_M) { + mains.alrm[SHUTD_REQST_I].actv = 1; + } else { + mains.alrm[SHUTD_REQST_I].actv = 0; + } + state->alrm = &mains; + break; + case OBTA: + if (reg_val == OBTA_HIALRM_V) { + obta.alrm[OBTA_HIALRM_I].actv = 1; + } + state->alrm = &obta; + break; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + state->reg.val16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + break; + } + + return rval; +} + +/* get driver configuration parameters */ +void get_config_vars() +{ + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); + + /* check if response time out (s) is set ang get the value */ + if (testvar("mod_resp_to_s")) { + mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); + } + upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); + + /* check if response time out (us) is set ang get the value */ + if (testvar("mod_resp_to_us")) { + mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); + if (mod_resp_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } + } + upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); + + /* check if byte time out (s) is set ang get the value */ + if (testvar("mod_byte_to_s")) { + mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); + } + upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); + + /* check if byte time out (us) is set ang get the value */ + if (testvar("mod_byte_to_us")) { + mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); + if (mod_byte_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } + } + upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); +} + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port) +{ + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; +} + +/* reconnect to modbus server upon connection error */ +void modbus_reconnect() +{ + int rval; + + upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); + + /* clear current modbus context */ + modbus_close(mbctx); + modbus_free(mbctx); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} diff --git a/drivers/adelsystems_cbi.h b/drivers/adelsystems_cbi.h new file mode 100644 index 0000000000..b27c2f1e4a --- /dev/null +++ b/drivers/adelsystems_cbi.h @@ -0,0 +1,510 @@ +/* adelsystems_cbi.h - Driver for ADELSYSTEMS CB/CBI DC-UPS + * + * Copyright (C) + * 2022 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ADELSYSTEMS_CBI_H +#define ADELSYSTEMS_CBI_H + +#include + +/* UPS device details */ +#define DEVICE_MFR "ADELSYSTEMS" +#define DEVICE_TYPE "DC-UPS" +#define DEVICE_MODEL "CB/CBI" + +/* serial access parameters */ +#define BAUD_RATE 9600 +#define PARITY 'N' +#define DATA_BIT 8 +#define STOP_BIT 1 + +/* + * modbus response and byte timeouts + * us: 1 - 999999 + */ +#define MODRESP_TIMEOUT_s 0 +#define MODRESP_TIMEOUT_us 200000 +#define MODBYTE_TIMEOUT_s 0 +#define MODBYTE_TIMEOUT_us 50000 + +/* modbus access parameters */ +#define MODBUS_SLAVE_ID 5 + +/* number of modbus registers */ +#define MODBUS_NUMOF_REGS 98 + +/* max HOLDING registers */ +#define MAX_H_REGS 120 + +/* start HOLDING register index */ +#define H_REG_STARTIDX 0 + +/* read all regs preprocessor flag */ +#define READALL_REGS 1 + +/* number of device models */ +#define DEV_NUMOF_MODELS 10 + +/* shutdown repeat on error */ +#define FSD_REPEAT_CNT 3 + +/* shutdown repeat interval in ms */ +#define FSD_REPEAT_INTRV 1500 + +/* definition of register type */ +enum regtype { + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING +}; +typedef enum regtype regtype_t; + +/* product name info, "device.model" */ +struct prodname { + uint16_t val; + char *name; +}; +typedef struct prodname prodname_t; +static prodname_t prdnm_i[] = { + {1, "CBI1235A"}, + {2, "CBI2420A"}, + {3, "CBI4810A"}, + {4, "CBI2801224"}, + {7, "CBI480W"}, + {8, "CB122410A"}, + {9, "CB480W"}, + {11, "CB12245AJ"}, + {12, "CB1235A"}, + {13, "CB2420A"}, + {14, "CB4810A"} +}; + +/* charging status info, "battery.charger.status" */ +static char *chrgs_i[] = { + "none", + "resting", /* recovering */ + "charging", /* bulk */ + "charging", /* absorb */ + "floating" /* float */ +}; +struct chrgs { + int state; + char *info; +}; +typedef struct chrgs chrgs_t; + +/* power management info, "ups.status", "battery.charger.status" */ +static char *pwrmng_i[] = { + "backup", /* "OB", "discharging" */ + "charging", /* "OL" */ + "boost", + "not charging" +}; +struct pwrmng { + int state; + char *info; +}; +typedef struct pwrmng pwrmng_t; + +/* general modbus register value */ +struct reg { + union { + uint16_t val16; + uint8_t val8; + }; + char *strval; +}; +typedef struct reg reg_t; + +/* general alarm struct */ +struct alrm { + int actv; /* active flag */ + char *descr; /* description field */ +}; +typedef struct alrm alrm_t; + +/* general alarm array */ +struct alrm_ar { + int alrm_c; /* alarm count */ + alrm_t alrm[]; /* alarm array */ +}; +typedef struct alrm_ar alrm_ar_t; + +/* + * BIT MASKS and VALUES + */ + +/* Charging status */ +#define CHRG_NONE 0 +#define CHRG_RECV 1 +#define CHRG_BULK 2 +#define CHRG_ABSR 3 +#define CHRG_FLOAT 4 + +/* power management */ +#define PMNG_BCKUP 0 +#define PMNG_CHRGN 1 +#define PMNG_BOOST 2 +#define PMNG_NCHRG 3 + +/* product name */ +#define PRDN_MAX 14 + +/* Mains alarm masks */ +#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_M 0x0002 /* shutdown requested */ + +/* Mains alarm indices */ +#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_I 1 /* shutdown requested */ + +/* AC input voltage alarm masks */ +#define VACA_HIALRM_M 0x0001 /* high alarm */ +#define VACA_LOALRM_M 0x0002 /* low alarm */ + +/* AC input voltage alarm indices */ +#define VACA_HIALRM_I 0 /* high alarm */ +#define VACA_LOALRM_I 1 /* low alarm */ + +/* Onboard temperature alarm value */ +#define OBTA_HIALRM_V 1 /* high alarm */ + +/* Onboard temperature alarm index */ +#define OBTA_HIALRM_I 0 /* high alarm */ + +/* Device failure alarm masks */ +#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ +#define DEVF_INALRM_M 0x0006 /* internal failure */ +#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ + +/* Device failure alarm indices */ +#define DEVF_RCALRM_I 0 /* rectifier failure */ +#define DEVF_INALRM_I 1 /* internal failure */ +#define DEVF_LFNAVL_I 2 /* lifetest not available */ + +/* Battery temp sensor failure alarm masks */ +#define BTSF_FCND_M 0x0001 /* connection fault */ +#define BTSF_NCND_M 0x0002 /* not connected */ + +/* Battery temp sensor failure alarm indices */ +#define BTSF_FCND_I 0 /* connection fault */ +#define BTSF_NCND_I 1 /* not connected */ + +/* Battery voltage alarm masks */ +#define BVAL_HIALRM_M 0x0001 /* high voltage */ +#define BVAL_LOALRM_M 0x0002 /* low voltage */ +#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ + +/* Battery voltage alarm indices */ +#define BVAL_HIALRM_I 0 /* high voltage */ +#define BVAL_LOALRM_I 1 /* low voltage */ +#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ + +/* SoH and SoC alarm masks */ +#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ + +/* SoH and SoC alarm indices */ +#define SHSC_HIRESI_I 0 /* high internal resistance */ +#define SHSC_LOCHEF_I 1 /* low charge efficiency */ +#define SHSC_LOEFCP_I 2 /* low effective capacity */ +#define SHSC_LOWSOC_I 3 /* low state of charge */ + +/* Battery status alarm masks */ +#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ +#define BSTA_NOCNND_M 0x0002 /* not connected */ +#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ +#define BSTA_SULPHD_M 0x0008 /* sulphated */ +#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT_M 0x0020 /* connection fault */ + +/* Battery status alarm indices */ +#define BSTA_REVPOL_I 0 /* reversed polarity */ +#define BSTA_NOCNND_I 1 /* not connected */ +#define BSTA_CLSHCR_I 2 /* cell short circuit */ +#define BSTA_SULPHD_I 3 /* sulphated */ +#define BSTA_CHEMNS_I 4 /* chemistry not supported */ +#define BSTA_CNNFLT_I 5 /* connection fault */ + +/* input mains and shutdown alarms */ +static alrm_ar_t mains = { + 2, + { + {0, "input voltage not available"}, + {0, "ups shutdown requested"} + } +}; + +/* AC input voltage alarms */ +static alrm_ar_t vaca = { + 2, + { + {0, "input voltage high alarm"}, + {0, "input voltage low alarm"} + } +}; + +/* device failure alarms */ +static alrm_ar_t devf = { + 3, + { + {0, "UPS rectifier failure"}, + {0, "UPS internal failure"}, + {0, "UPS lifetest not available"} + } +}; + +/* battery sensor failure alarms */ +static alrm_ar_t btsf = { + 2, + { + {0, "battery temp sensor connection fault"}, + {0, "battery temp sensor not connected"} + } +}; + +/* battery voltage alarms */ +static alrm_ar_t bval = { + 3, + { + {0, "battery high voltage"}, + {0, "battery low voltage"}, + {0, "battery start with battery flat"} + } +}; + +/* battery SoH and SoC alarms */ +static alrm_ar_t shsc = { + 4, + { + {0, "battery high internal resistance"}, + {0, "battery low charge efficiency"}, + {0, "battery low effective capacity"}, + {0, "battery low state of charge"} + } +}; + +/* battery status alarm */ +static alrm_ar_t bsta = { + 6, + { + {0, "battery reversed polarity"}, + {0, "battery not connected"}, + {0, "battery cell short circuit"}, + {0, "battery sulphated"}, + {0, "battery chemistry not supported"}, + {0, "battery connection fault"} + } +}; + +/* onboard temperature alarm */ +static alrm_ar_t obta = { + 1, + { + {0, "onboard temperature high"} + } +}; + +/* UPS device reg enum */ +enum devreg { + CHRG = 4, /* Charging status, "battery.charger.status" */ + BATV = 7, /* Battery voltage, "battery.voltage" */ + BCEF = 18, /* Battery charge efficiency factor (CEF) */ + BSOH = 20, /* Battery state-of-health */ + BSOC = 22, /* Battery state-of-charge, "battery.charge" */ + BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ + PMNG = 5, /* Power management, "ups.status" */ + OTMP = 28, /* Onboard temperature, "ups.temperature" */ + PRDN = 67, /* Product name, "ups.model" */ + VAC = 29, /* AC voltage, "input.voltage" */ + LVDC = 10, /* Load voltage, "output.voltage" */ + LCUR = 19, /* Load current, "output.current" */ + BINH = 87, /* Backup inhibit */ + FSD = 40, /* Force shutdown */ + TBUF = 103, /* Time buffering, "battery.runtime" */ + BSTA = 31, /* Battery status alarms */ + SCSH, /* SoH and SoC alarms */ + BVAL = 34, /* Battery voltage alarm */ + BTSF = 43, /* Battery temp sensor failure */ + DEVF = 42, /* Device failure */ + OBTA = 46, /* On board temp alarm */ + VACA = 44, /* VAC alarms */ + MAIN /* Mains status */ +}; +typedef enum devreg devreg_t; + +/* UPS register attributes */ +struct regattr { + int num; + int saddr; /* register start address */ + int xaddr; /* register hex address */ + float scale; /* scale */ + regtype_t type; /* register type */ +}; +typedef struct regattr regattr_t; + +/* UPS device state info union */ +union devstate { + prodname_t product; /* ups model name */ + chrgs_t charge; /* charging status */ + pwrmng_t power; /* ups status */ + reg_t reg; /* state register*/ + alrm_ar_t *alrm; /* alarm statuses */ +}; + +typedef union devstate devstate_t; + +/* device register memory image */ +static uint16_t regs_data[MAX_H_REGS]; + +/* ADELSYSTEMS CBI registers */ +static regattr_t regs[] = { + {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ + {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ + {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ + {40004, 0, 0, 1, HOLDING}, + {40005, 0, 0, 1, HOLDING}, /* Charging status */ + {40006, 0, 0, 1, HOLDING}, /* Power management + * 0:Backup 1:Charging 2:boost 3:Not charging + */ + {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ + {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ + {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ + {40010, 0, 0, 1, HOLDING}, /* Software ID */ + {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ + {40012, 0, 0, 1, HOLDING}, + {40013, 0, 0, 1, HOLDING}, + {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ + {40015, 0, 0, 1, HOLDING}, + {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ + {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ + {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ + {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ + {40020, 0, 0, 1, HOLDING}, /* Output load current */ + {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ + {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ + {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ + {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ + {40025, 0, 0, 1, HOLDING}, + {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ + {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ + {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ + {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ + {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ + {40031, 0, 0, 1, HOLDING}, + {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ + {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ + {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ + {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ + {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ + {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ + {40038, 0, 0, 1, HOLDING}, /* Load alarm */ + {40039, 0, 0, 1, HOLDING}, /* Device variant */ + {40040, 0, 0, 1, HOLDING}, + {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ + {40042, 0, 0, 1, HOLDING}, + {40043, 0, 0, 1, HOLDING}, /* Device failure */ + {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ + {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ + {40046, 0, 0, 1, HOLDING}, /* Mains status */ + {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ + {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ + {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ + {40050, 0, 0, .1, HOLDING}, /* Ah charged */ + {40051, 0, 0, 1, HOLDING}, /* Total run time */ + {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ + {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ + {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ + {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ + {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ + {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ + {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ + {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ + {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ + {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ + {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ + {40065, 0, 0, 1, HOLDING}, /* History clear all */ + {40066, 0, 0, 1, HOLDING}, /* Factory settings */ + {40067, 0, 0, 1, HOLDING}, /* Product name */ + {40068, 0, 0, 1, HOLDING}, + {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ + {40070, 0, 0, 1, HOLDING}, + {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ + {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ + {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ + {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ + {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ + {40076, 0, 0, 1, HOLDING}, + {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ + {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ + {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ + {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ + {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ + {40082, 0, 0, 1, HOLDING}, /* Float voltage */ + {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ + {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ + {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ + {40086, 0, 0, 1, HOLDING}, + {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed + * 1 = Backup not allowed + */ + {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ + {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ + {40091, 0, 0, 1, HOLDING}, + {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ + {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ + {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ + {40095, 0, 0, 1, HOLDING}, + {40096, 0, 0, 1, HOLDING}, + {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ + {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ + {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ + {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ + {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ + {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ + {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ + {40104, 0, 0, 1, HOLDING}, /* Time buffering */ + {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ + {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ + {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ + {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ + {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ + {40110, 0, 0, 1, HOLDING}, + {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ + {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ + {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ + {40114, 0, 0, 1, HOLDING}, + {40115, 0, 0, 1, HOLDING}, + {40116, 0, 0, 1, HOLDING}, + {40117, 0, 0, 1, HOLDING}, + {40118, 0, 0, 1, HOLDING}, + {40119, 0, 0, 1, HOLDING}, + {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ + + +}; +#endif /* ADELSYSTEMS_CBI_H */ From d9eb104e00031ba76c85f64b4437415806aea7dc Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 01:49:38 +0200 Subject: [PATCH 066/700] filename fixes --- drivers/Makefile.am | 8 +- drivers/adelsystem_cbi.c | 1201 ++++++++++++++++++++++++++++++++++++++ drivers/adelsystem_cbi.h | 510 ++++++++++++++++ 3 files changed, 1715 insertions(+), 4 deletions(-) create mode 100644 drivers/adelsystem_cbi.c create mode 100644 drivers/adelsystem_cbi.h diff --git a/drivers/Makefile.am b/drivers/Makefile.am index e364c5a7e7..664661a278 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -50,7 +50,7 @@ SERIAL_USB_DRIVERLIST = \ nutdrv_qx NEONXML_DRIVERLIST = netxml-ups MACOSX_DRIVERLIST = macosx-ups -MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 adelsystems_cbi +MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 adelsystem_cbi LINUX_I2C_DRIVERLIST = asem pijuice # distribute all drivers, even ones that are not built by default @@ -273,8 +273,8 @@ phoenixcontact_modbus_SOURCES = phoenixcontact_modbus.c phoenixcontact_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) generic_modbus_SOURCES = generic_modbus.c generic_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) -adelsystems_cbi_SOURCES = adelsystems_cbi.c -adelsystems_cbi_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) +adelsystem_cbi_SOURCES = adelsystem_cbi.c +adelsystem_cbi_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) # Huawei UPS2000 driver # (this is both a Modbus and a serial driver) @@ -331,7 +331,7 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h b xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ apc-pdu-mib.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h \ eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h \ - hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adelsystems_cbi + hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adelsystem_cbi # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c new file mode 100644 index 0000000000..7b4804a0d1 --- /dev/null +++ b/drivers/adelsystem_cbi.c @@ -0,0 +1,1201 @@ +/* adelsystem_cbi.c - driver for ADELSYSTEM CB/CBI DC-UPS + * + * Copyright (C) + * 2022 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "adelsystem_cbi.h" +#include +#include + +#define DRIVER_NAME "NUT ADELSYSTEM DC-UPS CB/CBI driver" +#define DRIVER_VERSION "0.01" + +/* variables */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static devstate_t *dstate = NULL; /* device state context */ +static int errcnt = 0; /* modbus access error counter */ +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static char *device_type = DEVICE_TYPE; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ + + +/* initialize register start address and hex address from register number */ +void reginit(); + +/* read registers' memory region */ +int read_all_regs(modbus_t *mb, uint16_t *data); + +/* get config vars set by -x or defined in ups.conf driver section */ +void get_config_vars(void); + +/* get device state */ +int get_dev_state(devreg_t regindx, devstate_t **dstate); + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port); + +/* reconnect upon communication error */ +void modbus_reconnect(); + +/* modbus register read function */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data); + +/* modbus register write function */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg); + +/* count the time elapsed since start */ +long time_elapsed(struct timeval *start); + + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} +}; + +/* + * driver functions + */ + +/* read configuration variables from ups.conf and connect to ups device */ +void upsdrv_initups(void) +{ + int rval; + upsdebugx(2, "upsdrv_initups"); + + + dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); + reginit(); + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response time out to 200ms */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} + +/* initialize ups driver information */ +void upsdrv_initinfo(void) +{ + devstate_t *ds = dstate; /* device state context */ + upsdebugx(2, "upsdrv_initinfo"); + + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); + dstate_setinfo("device.type", "%s", device_type); + + /* read ups model */ + get_dev_state(PRDN, &ds); + dstate_setinfo("ups.model", "%s", ds->product.name); + upslogx(LOG_INFO, "ups.model = %s", ds->product.name); + + /* register instant commands */ + dstate_addcmd("load.off"); + + /* set callback for instant commands */ + upsh.instcmd = upscmd; +} + + +/* update UPS signal state */ +void upsdrv_updateinfo(void) +{ + int rval; /* return value */ + int i; /* local index */ + devstate_t *ds = dstate; /* device state */ + + upsdebugx(2, "upsdrv_updateinfo"); + + errcnt = 0; /* initialize error counter to zero */ + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ +#if READALL_REGS == 1 + rval = read_all_regs(mbctx, regs_data); + if (rval == -1) { + errcnt++; + } else { +#endif + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ + rval = get_dev_state(MAIN, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains.alrm[MAINS_AVAIL_I].descr); + upslogx(LOG_INFO, "ups.status = OB"); + } else { + status_set("OL"); + upslogx(LOG_INFO, "ups.status = OL"); + } + if (ds->alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains.alrm[SHUTD_REQST_I].descr); + upslogx(LOG_INFO, "ups.status = FSD"); + } + } + + /* + * update UPS status regarding battery voltage + */ + rval = get_dev_state(BVAL, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set(bval.alrm[BVAL_LOALRM_I].descr); + upslogx(LOG_INFO, "ups.status = LB"); + } + if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set(bval.alrm[BVAL_HIALRM_I].descr); + upslogx(LOG_INFO, "ups.status = HB"); + } + if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); + upslogx(LOG_INFO, "battery start with battery flat"); + } + } + + /* get "battery.voltage" */ + rval = get_dev_state(BATV, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); + } + /* + * update UPS status regarding battery charger status + */ + + /* get "battery.charger.status" */ + rval = get_dev_state(CHRG, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { + status_set("CHRG"); + upslogx(LOG_INFO, "ups.status = CHRG"); + } + dstate_setinfo("battery.charger.status", "%s", ds->charge.info); + upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); + } + rval = get_dev_state(PMNG, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + upslogx(LOG_INFO, "ups.status = DISCHRG"); + } + if (ds->power.state == PMNG_BOOST) { + status_set("BOOST"); + upslogx(LOG_INFO, "ups.status = BOOST"); + } + } + + /* + * update UPS battery state of charge + */ + rval = get_dev_state(BSOC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.charge", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); + } + + /* + * update UPS AC input state + */ + rval = get_dev_state(VACA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } + } + rval = get_dev_state(VAC, &ds); + dstate_setinfo("input.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); + } + + /* + * update UPS onboard temperature state + */ + rval = get_dev_state(OBTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(OTMP, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("ups.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); + } + /* + * update UPS battery temperature state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(BTMP, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); + } + rval = get_dev_state(TBUF, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.runtime", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); + } + + /* + * update UPS device failure state + */ + rval = get_dev_state(DEVF, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS SoH and SoC states + */ + rval = get_dev_state(SCSH, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS battery state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS load status + */ + rval = get_dev_state(LVDC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("output.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); + } + rval = get_dev_state(LCUR, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("output.current", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); + } +#if READALL_REGS == 1 + } +#endif + /* check for communication errors */ + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2, "Communication errors: %d", errcnt); + dstate_datastale(); + } +} + +/* shutdown UPS */ +void upsdrv_shutdown(void) +{ + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); +} + +/* print driver usage info */ +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); + addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); + addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); + addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); +} + +/* close modbus connection and free modbus context allocated memory */ +void upsdrv_cleanup(void) +{ + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } + if (dstate != NULL) { + free(dstate); + } +} + +/* + * driver support functions + */ + +/* initialize register start address and hex address from register number */ +void reginit() +{ + int i; /* local index */ + + for (i = 0; i < MODBUS_NUMOF_REGS; i++) { + int rnum = regs[i].num; + switch (regs[i].type) { + case COIL: + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x0 + regs[i].num - 1; + break; + case INPUT_B: + rnum -= 10000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x10000 + rnum - 1; + break; + case INPUT_R: + rnum -= 30000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x30000 + rnum - 1; + break; + case HOLDING: + rnum -= 40000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x40000 + rnum - 1; + break; + default: + upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + } + upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", + regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr + ); + } +} + +/* read registers' memory region */ +int read_all_regs(modbus_t *mb, uint16_t *data) +{ + int rval; + + /* read all HOLDING registers */ + rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, regs_data); + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", + modbus_strerror(errno), + regs[H_REG_STARTIDX].xaddr, + MAX_H_REGS, + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + + /* no COIL, INPUT_B or INPUT_R register regions to read */ + + return rval; +} + +/* Read a modbus register */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* write a modbus register */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* returns the time elapsed since start in milliseconds */ +long time_elapsed(struct timeval *start) +{ + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; +} + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg) +{ + int rval; + int data; + + if (!strcasecmp(cmd, "load.off")) { + data = 1; + rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + regs[FSD].xaddr, + regs[FSD].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); + rval = STAT_INSTCMD_HANDLED; + } + } else { + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; +} + +/* read device state, returns 0 on success or -1 on communication error + it formats state depending on register semantics */ +int get_dev_state(devreg_t regindx, devstate_t **dstate) +{ + int i; /* local index */ + int n; + int rval; /* return value */ + static char *ptr = NULL; /* temporary pointer */ + uint reg_val; /* register value */ +#if READALL_REGS == 0 + uint num; /* register number */ + regtype_t rtype; /* register type */ + int addr; /* register address */ +#endif + devstate_t *state; /* device state */ + + state = *dstate; +#if READALL_REGS == 1 + reg_val = regs_data[regindx]; + rval = 0; +#elif READALL_REGS == 0 + num = regs[regindx].num; + addr = regs[regindx].xaddr; + rtype = regs[regindx].type; + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval == -1) { + return rval; + } + upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", + num, + addr, + rtype, + reg_val + ); +#endif + /* process register data */ + switch (regindx) { + case CHRG: /* "ups.charge" */ + if (reg_val == CHRG_NONE) { + state->charge.state = CHRG_NONE; + state->charge.info = chrgs_i[CHRG_NONE]; + } else if (reg_val == CHRG_RECV) { + state->charge.state = CHRG_RECV; + state->charge.info = chrgs_i[CHRG_RECV]; + } else if (reg_val == CHRG_BULK) { + state->charge.state = CHRG_BULK; + state->charge.info = chrgs_i[CHRG_BULK]; + } else if (reg_val == CHRG_ABSR) { + state->charge.state = CHRG_ABSR; + state->charge.info = chrgs_i[CHRG_ABSR]; + } else if (reg_val == CHRG_FLOAT) { + state->charge.state = CHRG_FLOAT; + state->charge.info = chrgs_i[CHRG_FLOAT]; + } + upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); + break; + case BATV: /* "battery.voltage" */ + case LVDC: /* "output.voltage" */ + case LCUR: /* "output.current" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0.00"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case TBUF: + case BSOH: + case BCEF: + case VAC: /* "input.voltage" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case BSOC: /* "battery.charge" */ + if (reg_val != 0) { + state->reg.val16 = reg_val; + double fval = (double )reg_val * regs[BSOC].scale; + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val16 = 0; + state->reg.strval = "0.00"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case BTMP: /* "battery.temperature" */ + case OTMP: /* "ups.temperature" */ + state->reg.val16 = reg_val; + double fval = reg_val - 273.15; + n = snprintf(NULL, 0, "%.2f", fval); + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + if (ptr != NULL) { + free(ptr); + } + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case PMNG: /* "ups.status" & "battery.charge" */ + if (reg_val == PMNG_BCKUP) { + state->power.state = PMNG_BCKUP; + state->power.info = pwrmng_i[PMNG_BCKUP]; + } else if (reg_val == PMNG_CHRGN) { + state->power.state = PMNG_CHRGN; + state->power.info = pwrmng_i[PMNG_CHRGN]; + } else if (reg_val == PMNG_BOOST) { + state->power.state = PMNG_BOOST; + state->power.info = pwrmng_i[PMNG_BOOST]; + } else if (reg_val == PMNG_NCHRG) { + state->power.state = PMNG_NCHRG; + state->power.info = pwrmng_i[PMNG_NCHRG]; + } + upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); + break; + case PRDN: /* "ups.model" */ + for (i = 0; i < DEV_NUMOF_MODELS; i++) { + if (prdnm_i[i].val == reg_val) { + break; + } + } + state->product.val = reg_val; + state->product.name = prdnm_i[i].name; + upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); + break; + case BSTA: + if (reg_val & BSTA_REVPOL_M) { + bsta.alrm[BSTA_REVPOL_I].actv = 1; + } else { + bsta.alrm[BSTA_REVPOL_I].actv = 0; + } + if (reg_val & BSTA_NOCNND_M) { + bsta.alrm[BSTA_NOCNND_I].actv = 1; + } else { + bsta.alrm[BSTA_NOCNND_I].actv = 0; + } + if (reg_val & BSTA_CLSHCR_M) { + bsta.alrm[BSTA_CLSHCR_I].actv = 1; + } else { + bsta.alrm[BSTA_CLSHCR_I].actv = 0; + } + if (reg_val & BSTA_SULPHD_M) { + bsta.alrm[BSTA_SULPHD_I].actv = 1; + } else { + bsta.alrm[BSTA_SULPHD_I].actv = 0; + } + if (reg_val & BSTA_CHEMNS_M) { + bsta.alrm[BSTA_CHEMNS_I].actv = 1; + } else { + bsta.alrm[BSTA_CHEMNS_I].actv = 0; + } + if (reg_val & BSTA_CNNFLT_M) { + bsta.alrm[BSTA_CNNFLT_I].actv = 1; + } else { + bsta.alrm[BSTA_CNNFLT_I].actv = 0; + } + state->alrm = &bsta; + break; + case SCSH: + if (reg_val & SHSC_HIRESI_M) { + shsc.alrm[SHSC_HIRESI_I].actv = 1; + } else { + shsc.alrm[SHSC_HIRESI_I].actv = 0; + } + if (reg_val & SHSC_LOCHEF_M) { + shsc.alrm[SHSC_LOCHEF_I].actv = 1; + } else { + shsc.alrm[SHSC_LOCHEF_I].actv = 0; + } + if (reg_val & SHSC_LOEFCP_M) { + shsc.alrm[SHSC_LOEFCP_I].actv = 1; + } else { + shsc.alrm[SHSC_LOEFCP_I].actv = 0; + } + if (reg_val & SHSC_LOWSOC_M) { + shsc.alrm[SHSC_LOWSOC_I].actv = 1; + } else { + shsc.alrm[SHSC_LOWSOC_I].actv = 0; + } + state->alrm = &shsc; + break; + case BVAL: + if (reg_val & BVAL_HIALRM_M) { + bval.alrm[BVAL_HIALRM_I].actv = 1; + } else { + bval.alrm[BVAL_HIALRM_I].actv = 0; + } + if (reg_val & BVAL_LOALRM_M) { + bval.alrm[BVAL_LOALRM_I].actv = 1; + } else { + bval.alrm[BVAL_LOALRM_I].actv = 0; + } + if (reg_val & BVAL_BSTSFL_M) { + bval.alrm[BVAL_BSTSFL_I].actv = 1; + } else { + bval.alrm[BVAL_BSTSFL_I].actv = 0; + } + state->alrm = &bval; + break; + case BTSF: + if (reg_val & BTSF_FCND_M) { + btsf.alrm[BTSF_FCND_I].actv = 1; + } else { + btsf.alrm[BTSF_FCND_I].actv = 0; + } + if (reg_val & BTSF_NCND_M) { + btsf.alrm[BTSF_NCND_I].actv = 1; + } else { + btsf.alrm[BTSF_NCND_I].actv = 0; + } + state->alrm = &btsf; + break; + case DEVF: + if (reg_val & DEVF_RCALRM_M) { + devf.alrm[DEVF_RCALRM_I].actv = 1; + } else { + devf.alrm[DEVF_RCALRM_I].actv = 0; + } + if (reg_val & DEVF_INALRM_M) { + devf.alrm[DEVF_INALRM_I].actv = 1; + } else { + devf.alrm[DEVF_INALRM_I].actv = 0; + } + if (reg_val & DEVF_LFNAVL_M) { + devf.alrm[DEVF_LFNAVL_I].actv = 1; + } else { + devf.alrm[DEVF_LFNAVL_I].actv = 0; + } + state->alrm = &devf; + break; + case VACA: + if (reg_val & VACA_HIALRM_M) { + vaca.alrm[VACA_HIALRM_I].actv = 1; + } else { + vaca.alrm[VACA_HIALRM_I].actv = 0; + } + if (reg_val == VACA_LOALRM_M) { + vaca.alrm[VACA_LOALRM_I].actv = 1; + } else { + vaca.alrm[VACA_LOALRM_I].actv = 0; + } + state->alrm = &vaca; + break; + case MAIN: + if (reg_val & MAINS_AVAIL_M) { + mains.alrm[MAINS_AVAIL_I].actv = 1; + } else { + mains.alrm[MAINS_AVAIL_I].actv = 0; + } + if (reg_val == SHUTD_REQST_M) { + mains.alrm[SHUTD_REQST_I].actv = 1; + } else { + mains.alrm[SHUTD_REQST_I].actv = 0; + } + state->alrm = &mains; + break; + case OBTA: + if (reg_val == OBTA_HIALRM_V) { + obta.alrm[OBTA_HIALRM_I].actv = 1; + } + state->alrm = &obta; + break; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + state->reg.val16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + break; + } + + return rval; +} + +/* get driver configuration parameters */ +void get_config_vars() +{ + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); + + /* check if response time out (s) is set ang get the value */ + if (testvar("mod_resp_to_s")) { + mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); + } + upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); + + /* check if response time out (us) is set ang get the value */ + if (testvar("mod_resp_to_us")) { + mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); + if (mod_resp_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } + } + upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); + + /* check if byte time out (s) is set ang get the value */ + if (testvar("mod_byte_to_s")) { + mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); + } + upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); + + /* check if byte time out (us) is set ang get the value */ + if (testvar("mod_byte_to_us")) { + mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); + if (mod_byte_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } + } + upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); +} + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port) +{ + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; +} + +/* reconnect to modbus server upon connection error */ +void modbus_reconnect() +{ + int rval; + + upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); + + /* clear current modbus context */ + modbus_close(mbctx); + modbus_free(mbctx); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +} diff --git a/drivers/adelsystem_cbi.h b/drivers/adelsystem_cbi.h new file mode 100644 index 0000000000..b3500a7afc --- /dev/null +++ b/drivers/adelsystem_cbi.h @@ -0,0 +1,510 @@ +/* adelsystem_cbi.h - Driver for ADELSYSTEM CB/CBI DC-UPS + * + * Copyright (C) + * 2022 Dimitris Economou + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef ADELSYSTEM_CBI_H +#define ADELSYSTEM_CBI_H + +#include + +/* UPS device details */ +#define DEVICE_MFR "ADELSYSTEM" +#define DEVICE_TYPE "DC-UPS" +#define DEVICE_MODEL "CB/CBI" + +/* serial access parameters */ +#define BAUD_RATE 9600 +#define PARITY 'N' +#define DATA_BIT 8 +#define STOP_BIT 1 + +/* + * modbus response and byte timeouts + * us: 1 - 999999 + */ +#define MODRESP_TIMEOUT_s 0 +#define MODRESP_TIMEOUT_us 200000 +#define MODBYTE_TIMEOUT_s 0 +#define MODBYTE_TIMEOUT_us 50000 + +/* modbus access parameters */ +#define MODBUS_SLAVE_ID 5 + +/* number of modbus registers */ +#define MODBUS_NUMOF_REGS 98 + +/* max HOLDING registers */ +#define MAX_H_REGS 120 + +/* start HOLDING register index */ +#define H_REG_STARTIDX 0 + +/* read all regs preprocessor flag */ +#define READALL_REGS 1 + +/* number of device models */ +#define DEV_NUMOF_MODELS 10 + +/* shutdown repeat on error */ +#define FSD_REPEAT_CNT 3 + +/* shutdown repeat interval in ms */ +#define FSD_REPEAT_INTRV 1500 + +/* definition of register type */ +enum regtype { + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING +}; +typedef enum regtype regtype_t; + +/* product name info, "device.model" */ +struct prodname { + uint16_t val; + char *name; +}; +typedef struct prodname prodname_t; +static prodname_t prdnm_i[] = { + {1, "CBI1235A"}, + {2, "CBI2420A"}, + {3, "CBI4810A"}, + {4, "CBI2801224"}, + {7, "CBI480W"}, + {8, "CB122410A"}, + {9, "CB480W"}, + {11, "CB12245AJ"}, + {12, "CB1235A"}, + {13, "CB2420A"}, + {14, "CB4810A"} +}; + +/* charging status info, "battery.charger.status" */ +static char *chrgs_i[] = { + "none", + "resting", /* recovering */ + "charging", /* bulk */ + "charging", /* absorb */ + "floating" /* float */ +}; +struct chrgs { + int state; + char *info; +}; +typedef struct chrgs chrgs_t; + +/* power management info, "ups.status", "battery.charger.status" */ +static char *pwrmng_i[] = { + "backup", /* "OB", "discharging" */ + "charging", /* "OL" */ + "boost", + "not charging" +}; +struct pwrmng { + int state; + char *info; +}; +typedef struct pwrmng pwrmng_t; + +/* general modbus register value */ +struct reg { + union { + uint16_t val16; + uint8_t val8; + }; + char *strval; +}; +typedef struct reg reg_t; + +/* general alarm struct */ +struct alrm { + int actv; /* active flag */ + char *descr; /* description field */ +}; +typedef struct alrm alrm_t; + +/* general alarm array */ +struct alrm_ar { + int alrm_c; /* alarm count */ + alrm_t alrm[]; /* alarm array */ +}; +typedef struct alrm_ar alrm_ar_t; + +/* + * BIT MASKS and VALUES + */ + +/* Charging status */ +#define CHRG_NONE 0 +#define CHRG_RECV 1 +#define CHRG_BULK 2 +#define CHRG_ABSR 3 +#define CHRG_FLOAT 4 + +/* power management */ +#define PMNG_BCKUP 0 +#define PMNG_CHRGN 1 +#define PMNG_BOOST 2 +#define PMNG_NCHRG 3 + +/* product name */ +#define PRDN_MAX 14 + +/* Mains alarm masks */ +#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_M 0x0002 /* shutdown requested */ + +/* Mains alarm indices */ +#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_I 1 /* shutdown requested */ + +/* AC input voltage alarm masks */ +#define VACA_HIALRM_M 0x0001 /* high alarm */ +#define VACA_LOALRM_M 0x0002 /* low alarm */ + +/* AC input voltage alarm indices */ +#define VACA_HIALRM_I 0 /* high alarm */ +#define VACA_LOALRM_I 1 /* low alarm */ + +/* Onboard temperature alarm value */ +#define OBTA_HIALRM_V 1 /* high alarm */ + +/* Onboard temperature alarm index */ +#define OBTA_HIALRM_I 0 /* high alarm */ + +/* Device failure alarm masks */ +#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ +#define DEVF_INALRM_M 0x0006 /* internal failure */ +#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ + +/* Device failure alarm indices */ +#define DEVF_RCALRM_I 0 /* rectifier failure */ +#define DEVF_INALRM_I 1 /* internal failure */ +#define DEVF_LFNAVL_I 2 /* lifetest not available */ + +/* Battery temp sensor failure alarm masks */ +#define BTSF_FCND_M 0x0001 /* connection fault */ +#define BTSF_NCND_M 0x0002 /* not connected */ + +/* Battery temp sensor failure alarm indices */ +#define BTSF_FCND_I 0 /* connection fault */ +#define BTSF_NCND_I 1 /* not connected */ + +/* Battery voltage alarm masks */ +#define BVAL_HIALRM_M 0x0001 /* high voltage */ +#define BVAL_LOALRM_M 0x0002 /* low voltage */ +#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ + +/* Battery voltage alarm indices */ +#define BVAL_HIALRM_I 0 /* high voltage */ +#define BVAL_LOALRM_I 1 /* low voltage */ +#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ + +/* SoH and SoC alarm masks */ +#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ + +/* SoH and SoC alarm indices */ +#define SHSC_HIRESI_I 0 /* high internal resistance */ +#define SHSC_LOCHEF_I 1 /* low charge efficiency */ +#define SHSC_LOEFCP_I 2 /* low effective capacity */ +#define SHSC_LOWSOC_I 3 /* low state of charge */ + +/* Battery status alarm masks */ +#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ +#define BSTA_NOCNND_M 0x0002 /* not connected */ +#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ +#define BSTA_SULPHD_M 0x0008 /* sulphated */ +#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT_M 0x0020 /* connection fault */ + +/* Battery status alarm indices */ +#define BSTA_REVPOL_I 0 /* reversed polarity */ +#define BSTA_NOCNND_I 1 /* not connected */ +#define BSTA_CLSHCR_I 2 /* cell short circuit */ +#define BSTA_SULPHD_I 3 /* sulphated */ +#define BSTA_CHEMNS_I 4 /* chemistry not supported */ +#define BSTA_CNNFLT_I 5 /* connection fault */ + +/* input mains and shutdown alarms */ +static alrm_ar_t mains = { + 2, + { + {0, "input voltage not available"}, + {0, "ups shutdown requested"} + } +}; + +/* AC input voltage alarms */ +static alrm_ar_t vaca = { + 2, + { + {0, "input voltage high alarm"}, + {0, "input voltage low alarm"} + } +}; + +/* device failure alarms */ +static alrm_ar_t devf = { + 3, + { + {0, "UPS rectifier failure"}, + {0, "UPS internal failure"}, + {0, "UPS lifetest not available"} + } +}; + +/* battery sensor failure alarms */ +static alrm_ar_t btsf = { + 2, + { + {0, "battery temp sensor connection fault"}, + {0, "battery temp sensor not connected"} + } +}; + +/* battery voltage alarms */ +static alrm_ar_t bval = { + 3, + { + {0, "battery high voltage"}, + {0, "battery low voltage"}, + {0, "battery start with battery flat"} + } +}; + +/* battery SoH and SoC alarms */ +static alrm_ar_t shsc = { + 4, + { + {0, "battery high internal resistance"}, + {0, "battery low charge efficiency"}, + {0, "battery low effective capacity"}, + {0, "battery low state of charge"} + } +}; + +/* battery status alarm */ +static alrm_ar_t bsta = { + 6, + { + {0, "battery reversed polarity"}, + {0, "battery not connected"}, + {0, "battery cell short circuit"}, + {0, "battery sulphated"}, + {0, "battery chemistry not supported"}, + {0, "battery connection fault"} + } +}; + +/* onboard temperature alarm */ +static alrm_ar_t obta = { + 1, + { + {0, "onboard temperature high"} + } +}; + +/* UPS device reg enum */ +enum devreg { + CHRG = 4, /* Charging status, "battery.charger.status" */ + BATV = 7, /* Battery voltage, "battery.voltage" */ + BCEF = 18, /* Battery charge efficiency factor (CEF) */ + BSOH = 20, /* Battery state-of-health */ + BSOC = 22, /* Battery state-of-charge, "battery.charge" */ + BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ + PMNG = 5, /* Power management, "ups.status" */ + OTMP = 28, /* Onboard temperature, "ups.temperature" */ + PRDN = 67, /* Product name, "ups.model" */ + VAC = 29, /* AC voltage, "input.voltage" */ + LVDC = 10, /* Load voltage, "output.voltage" */ + LCUR = 19, /* Load current, "output.current" */ + BINH = 87, /* Backup inhibit */ + FSD = 40, /* Force shutdown */ + TBUF = 103, /* Time buffering, "battery.runtime" */ + BSTA = 31, /* Battery status alarms */ + SCSH, /* SoH and SoC alarms */ + BVAL = 34, /* Battery voltage alarm */ + BTSF = 43, /* Battery temp sensor failure */ + DEVF = 42, /* Device failure */ + OBTA = 46, /* On board temp alarm */ + VACA = 44, /* VAC alarms */ + MAIN /* Mains status */ +}; +typedef enum devreg devreg_t; + +/* UPS register attributes */ +struct regattr { + int num; + int saddr; /* register start address */ + int xaddr; /* register hex address */ + float scale; /* scale */ + regtype_t type; /* register type */ +}; +typedef struct regattr regattr_t; + +/* UPS device state info union */ +union devstate { + prodname_t product; /* ups model name */ + chrgs_t charge; /* charging status */ + pwrmng_t power; /* ups status */ + reg_t reg; /* state register*/ + alrm_ar_t *alrm; /* alarm statuses */ +}; + +typedef union devstate devstate_t; + +/* device register memory image */ +static uint16_t regs_data[MAX_H_REGS]; + +/* ADELSYSTEM CBI registers */ +static regattr_t regs[] = { + {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ + {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ + {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ + {40004, 0, 0, 1, HOLDING}, + {40005, 0, 0, 1, HOLDING}, /* Charging status */ + {40006, 0, 0, 1, HOLDING}, /* Power management + * 0:Backup 1:Charging 2:boost 3:Not charging + */ + {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ + {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ + {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ + {40010, 0, 0, 1, HOLDING}, /* Software ID */ + {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ + {40012, 0, 0, 1, HOLDING}, + {40013, 0, 0, 1, HOLDING}, + {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ + {40015, 0, 0, 1, HOLDING}, + {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ + {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ + {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ + {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ + {40020, 0, 0, 1, HOLDING}, /* Output load current */ + {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ + {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ + {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ + {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ + {40025, 0, 0, 1, HOLDING}, + {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ + {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ + {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ + {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ + {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ + {40031, 0, 0, 1, HOLDING}, + {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ + {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ + {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ + {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ + {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ + {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ + {40038, 0, 0, 1, HOLDING}, /* Load alarm */ + {40039, 0, 0, 1, HOLDING}, /* Device variant */ + {40040, 0, 0, 1, HOLDING}, + {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ + {40042, 0, 0, 1, HOLDING}, + {40043, 0, 0, 1, HOLDING}, /* Device failure */ + {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ + {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ + {40046, 0, 0, 1, HOLDING}, /* Mains status */ + {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ + {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ + {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ + {40050, 0, 0, .1, HOLDING}, /* Ah charged */ + {40051, 0, 0, 1, HOLDING}, /* Total run time */ + {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ + {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ + {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ + {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ + {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ + {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ + {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ + {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ + {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ + {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ + {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ + {40065, 0, 0, 1, HOLDING}, /* History clear all */ + {40066, 0, 0, 1, HOLDING}, /* Factory settings */ + {40067, 0, 0, 1, HOLDING}, /* Product name */ + {40068, 0, 0, 1, HOLDING}, + {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ + {40070, 0, 0, 1, HOLDING}, + {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ + {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ + {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ + {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ + {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ + {40076, 0, 0, 1, HOLDING}, + {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ + {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ + {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ + {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ + {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ + {40082, 0, 0, 1, HOLDING}, /* Float voltage */ + {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ + {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ + {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ + {40086, 0, 0, 1, HOLDING}, + {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed + * 1 = Backup not allowed + */ + {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ + {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ + {40091, 0, 0, 1, HOLDING}, + {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ + {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ + {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ + {40095, 0, 0, 1, HOLDING}, + {40096, 0, 0, 1, HOLDING}, + {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ + {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ + {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ + {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ + {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ + {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ + {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ + {40104, 0, 0, 1, HOLDING}, /* Time buffering */ + {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ + {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ + {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ + {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ + {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ + {40110, 0, 0, 1, HOLDING}, + {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ + {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ + {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ + {40114, 0, 0, 1, HOLDING}, + {40115, 0, 0, 1, HOLDING}, + {40116, 0, 0, 1, HOLDING}, + {40117, 0, 0, 1, HOLDING}, + {40118, 0, 0, 1, HOLDING}, + {40119, 0, 0, 1, HOLDING}, + {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ + + +}; +#endif /* ADELSYSTEM_CBI_H */ From 9e0bfdbcf96c8e9a21df0a96312fc4fd13f84595 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 11:42:24 +0200 Subject: [PATCH 067/700] delete renamed driver files --- drivers/adele_cbi.c | 2599 ------------------------------------- drivers/adele_cbi.h | 1036 --------------- drivers/adelsystems_cbi.c | 1201 ----------------- drivers/adelsystems_cbi.h | 510 -------- 4 files changed, 5346 deletions(-) delete mode 100644 drivers/adele_cbi.c delete mode 100644 drivers/adele_cbi.h delete mode 100644 drivers/adelsystems_cbi.c delete mode 100644 drivers/adelsystems_cbi.h diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c deleted file mode 100644 index 4459117472..0000000000 --- a/drivers/adele_cbi.c +++ /dev/null @@ -1,2599 +0,0 @@ -<<<<<<< HEAD -<<<<<<< HEAD -/* adele_cbi.c - driver for ADELE CB/CBI DC-UPS - * - * Copyright (C) - * 2022 Dimitris Economou -<<<<<<< HEAD -======= -/* adele_cbi.c - Driver for adele CB/CBI UPS -======= -/* adele_cbi.c - driver for ADELE CB/CBI DC-UPS ->>>>>>> structure device data, code get_dev_state, in progress - * - * Copyright (C) - * 2021 Dimitris Economou ->>>>>>> under construction -======= ->>>>>>> ghost alarms bug fix, other bug fixes - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "main.h" -#include "adele_cbi.h" -#include -#include - -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD -#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" -#define DRIVER_VERSION "0.01" - -/* variables */ -static modbus_t *mbctx = NULL; /* modbus memory context */ -static devstate_t *dstate = NULL; /* device state context */ -static int errcnt = 0; /* modbus access error counter */ -static char *device_mfr = DEVICE_MFR; /* device manufacturer */ -static char *device_model = DEVICE_MODEL; /* device model */ -static char *device_type = DEVICE_TYPE; /* device model */ -static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ -static char ser_parity = PARITY; /* serial port parity */ -static int ser_data_bit = DATA_BIT; /* serial port data bit */ -static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ -static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ -static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ -static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ -static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ -static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ - - -/* initialize register start address and hex address from register number */ -void reginit(); -======= -#define DRIVER_NAME "NUT Adele CBI driver" -======= -#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" ->>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress -======= -#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" ->>>>>>> ghost alarms bug fix, other bug fixes -#define DRIVER_VERSION "0.01" - -/* variables */ -static modbus_t *mbctx = NULL; /* modbus memory context */ -static devstate_t *dstate = NULL; /* device state context */ -static int errcnt = 0; /* modbus access error counter */ -static char *device_mfr = DEVICE_MFR; /* device manufacturer */ -static char *device_model = DEVICE_MODEL; /* device model */ -static char *device_type = DEVICE_TYPE; /* device model */ -static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ -static char ser_parity = PARITY; /* serial port parity */ -static int ser_data_bit = DATA_BIT; /* serial port data bit */ -static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ -static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ -static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ -static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ -static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ -static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ - ->>>>>>> under construction - -/* initialize register start address and hex address from register number */ -void reginit(); - -/* read all registers */ -int read_all_regs(modbus_t *mb, uint16_t *data); - -/* get config vars set by -x or defined in ups.conf driver section */ -void get_config_vars(void); - -<<<<<<< HEAD -<<<<<<< HEAD -/* get device state */ -int get_dev_state(devreg_t regindx, devstate_t **dstate); - -======= ->>>>>>> under construction -======= -/* get device state */ -int get_dev_state(devreg_t regindx, devstate_t **dstate); - ->>>>>>> first testing release -/* create a new modbus context based on connection type (serial or TCP) */ -modbus_t *modbus_new(const char *port); - -/* reconnect upon communication error */ -void modbus_reconnect(); - -/* modbus register read function */ -int register_read(modbus_t *mb, int addr, regtype_t type, void *data); - -<<<<<<< HEAD -<<<<<<< HEAD -/* modbus register write function */ -int register_write(modbus_t *mb, int addr, regtype_t type, void *data); - -/* instant command triggered by upsd */ -int upscmd(const char *cmd, const char *arg); - -/* count the time elapsed since start */ -long time_elapsed(struct timeval *start); - - -/* driver description structure */ -upsdrv_info_t upsdrv_info = { - DRIVER_NAME, - DRIVER_VERSION, - "Dimitris Economou \n", - DRV_BETA, - {NULL} -======= -======= -/* modbus register write function */ -int register_write(modbus_t *mb, int addr, regtype_t type, void *data); - ->>>>>>> first testing release -/* instant command triggered by upsd */ -int upscmd(const char *cmd, const char *arg); - -/* count the time elapsed since start */ -long time_elapsed(struct timeval *start); - - -/* driver description structure */ -upsdrv_info_t upsdrv_info = { -<<<<<<< HEAD - DRIVER_NAME, - DRIVER_VERSION, - "Dimitris Economou \n", - DRV_BETA, - {NULL} ->>>>>>> under construction -======= - DRIVER_NAME, - DRIVER_VERSION, - "Dimitris Economou \n", - DRV_BETA, - {NULL} ->>>>>>> ghost alarms bug fix, other bug fixes -}; - -/* - * driver functions - */ - -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress -/* read configuration variables from ups.conf and connect to ups device */ -void upsdrv_initups(void) -{ - int rval; - upsdebugx(2, "upsdrv_initups"); - -<<<<<<< HEAD -<<<<<<< HEAD - - dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); -======= ->>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress -======= - - dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); ->>>>>>> ghost alarms bug fix, other bug fixes - reginit(); - get_config_vars(); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response time out to 200ms */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -} - -<<<<<<< HEAD -/* initialize ups driver information */ -void upsdrv_initinfo(void) -{ - devstate_t *ds = dstate; /* device state context */ - upsdebugx(2, "upsdrv_initinfo"); - - /* set device information */ - dstate_setinfo("device.mfr", "%s", device_mfr); - dstate_setinfo("device.model", "%s", device_model); - dstate_setinfo("device.type", "%s", device_type); - - /* read ups model */ - get_dev_state(PRDN, &ds); - dstate_setinfo("ups.model", "%s", ds->product.name); - upslogx(LOG_INFO, "ups.model = %s", ds->product.name); - - /* register instant commands */ - dstate_addcmd("load.off"); - - /* set callback for instant commands */ - upsh.instcmd = upscmd; -} - - -/* update UPS signal state */ -void upsdrv_updateinfo(void) -{ - int rval; /* return value */ - int i; /* local index */ - devstate_t *ds = dstate; /* device state */ - - upsdebugx(2, "upsdrv_updateinfo"); - - errcnt = 0; /* initialize error counter to zero */ - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ -#if READALL_REGS == 1 - rval = read_all_regs(mbctx, regs_data); - if (rval == -1) { - errcnt++; - } else { -#endif - /* - * update UPS status regarding MAINS and SHUTDOWN request - * - OL: On line (mains is present) - * - OB: On battery (mains is not present) - */ - rval = get_dev_state(MAIN, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { - status_set("OB"); - alarm_set(mains.alrm[MAINS_AVAIL_I].descr); - upslogx(LOG_INFO, "ups.status = OB"); - } else { - status_set("OL"); - upslogx(LOG_INFO, "ups.status = OL"); - } - if (ds->alrm->alrm[SHUTD_REQST_I].actv) { - status_set("FSD"); - alarm_set(mains.alrm[SHUTD_REQST_I].descr); - upslogx(LOG_INFO, "ups.status = FSD"); - } - } - - /* - * update UPS status regarding battery voltage - */ - rval = get_dev_state(BVAL, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { - status_set("LB"); - alarm_set(bval.alrm[BVAL_LOALRM_I].descr); - upslogx(LOG_INFO, "ups.status = LB"); - } - if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { - status_set("HB"); - alarm_set(bval.alrm[BVAL_HIALRM_I].descr); - upslogx(LOG_INFO, "ups.status = HB"); - } - if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { - alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); - upslogx(LOG_INFO, "battery start with battery flat"); - } - } - - /* get "battery.voltage" */ - rval = get_dev_state(BATV, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); - } - /* - * update UPS status regarding battery charger status - */ - - /* get "battery.charger.status" */ - rval = get_dev_state(CHRG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { - status_set("CHRG"); - upslogx(LOG_INFO, "ups.status = CHRG"); - } - dstate_setinfo("battery.charger.status", "%s", ds->charge.info); - upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); - } - rval = get_dev_state(PMNG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->power.state == PMNG_BCKUP) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - upslogx(LOG_INFO, "ups.status = DISCHRG"); - } - if (ds->power.state == PMNG_BOOST) { - status_set("BOOST"); - upslogx(LOG_INFO, "ups.status = BOOST"); - } - } - - /* - * update UPS battery state of charge - */ - rval = get_dev_state(BSOC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.charge", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); - } - - /* - * update UPS AC input state - */ - rval = get_dev_state(VACA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - rval = get_dev_state(VAC, &ds); - dstate_setinfo("input.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); - } - - /* - * update UPS onboard temperature state - */ - rval = get_dev_state(OBTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(OTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("ups.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); - } - /* - * update UPS battery temperature state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(BTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); - } - rval = get_dev_state(TBUF, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.runtime", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); - } - - /* - * update UPS device failure state - */ - rval = get_dev_state(DEVF, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS SoH and SoC states - */ - rval = get_dev_state(SCSH, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS battery state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS load status - */ - rval = get_dev_state(LVDC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); - } - rval = get_dev_state(LCUR, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.current", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); - } -#if READALL_REGS == 1 - } -#endif - /* check for communication errors */ - if (errcnt == 0) { - alarm_commit(); - status_commit(); - dstate_dataok(); - } else { - upsdebugx(2, "Communication errors: %d", errcnt); - dstate_datastale(); - } -======= -======= ->>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress -/* initialize ups driver information */ -void upsdrv_initinfo(void) -{ - devstate_t *ds = dstate; /* device state context */ - upsdebugx(2, "upsdrv_initinfo"); - - /* set device information */ - dstate_setinfo("device.mfr", "%s", device_mfr); - dstate_setinfo("device.model", "%s", device_model); - dstate_setinfo("device.type", "%s", device_type); - - /* read ups model */ - get_dev_state(PRDN, &ds); - dstate_setinfo("ups.model", "%s", ds->product.name); - upslogx(LOG_INFO, "ups.model = %s", ds->product.name); - - /* register instant commands */ - dstate_addcmd("load.off"); - - /* set callback for instant commands */ - upsh.instcmd = upscmd; -} - - -/* update UPS signal state */ -void upsdrv_updateinfo(void) -{ - int rval; /* return value */ - int i; /* local index */ - devstate_t *ds = dstate; /* device state */ - - upsdebugx(2, "upsdrv_updateinfo"); - - errcnt = 0; /* initialize error counter to zero */ - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ - - /* - * update UPS status regarding MAINS and SHUTDOWN request - * - OL: On line (mains is present) - * - OB: On battery (mains is not present) - */ - rval = get_dev_state(MAIN, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { - status_set("OB"); - alarm_set(mains.alrm[MAINS_AVAIL_I].descr); - upslogx(LOG_INFO, "ups.status = OB"); - } else { - status_set("OL"); - upslogx(LOG_INFO, "ups.status = OL"); - } - if (ds->alrm->alrm[SHUTD_REQST_I].actv) { - status_set("FSD"); - alarm_set(mains.alrm[SHUTD_REQST_I].descr); - upslogx(LOG_INFO, "ups.status = FSD"); - } - } - -<<<<<<< HEAD - /* - * update UPS status regarding battery voltage - */ - rval = get_dev_state(BVAL, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { - status_set("LB"); - alarm_set(bval.alrm[BVAL_LOALRM_I].descr); - upslogx(LOG_INFO, "ups.status = LB"); - } - if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { - status_set("HB"); - alarm_set(bval.alrm[BVAL_HIALRM_I].descr); - upslogx(LOG_INFO, "ups.status = HB"); - } - if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { - alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); - upslogx(LOG_INFO, "battery start with battery flat"); - } - } -======= -/* read all register data image */ -int read_all_regs(modbus_t *mb, uint16_t *data) -{ - int rval; - - /* read all HOLDING registers */ - rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, regs_data); - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", - modbus_strerror(errno), - regs[H_REG_STARTIDX].xaddr, - MAX_H_REGS, - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - - /* no COIL, INPUT_B or INPUT_R register regions to read */ - - return rval; -} - -/* Read a modbus register */ -int register_read(modbus_t *mb, int addr, regtype_t type, void *data) -{ - int rval = -1; ->>>>>>> read_all_regs aproach integrated - - /* get "battery.voltage" */ - rval = get_dev_state(BATV, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); - } - /* - * update UPS status regarding battery charger status - */ - - /* get "battery.charger.status" */ - rval = get_dev_state(CHRG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { - status_set("CHRG"); - upslogx(LOG_INFO, "ups.status = CHRG"); - } - dstate_setinfo("battery.charger.status", "%s", ds->charge.info); - upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); - } - rval = get_dev_state(PMNG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->power.state == PMNG_BCKUP) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - upslogx(LOG_INFO, "ups.status = DISCHRG"); - } - if (ds->power.state == PMNG_BOOST) { - status_set("BOOST"); - upslogx(LOG_INFO, "ups.status = BOOST"); - } - } - - /* - * update UPS battery state of charge - */ - rval = get_dev_state(BSOC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.charge", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); - } - - /* - * update UPS AC input state - */ - rval = get_dev_state(VACA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - rval = get_dev_state(VAC, &ds); - dstate_setinfo("input.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); - } - - /* - * update UPS onboard temperature state - */ - rval = get_dev_state(OBTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(OTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("ups.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); - } - /* - * update UPS battery temperature state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(BTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); - } - rval = get_dev_state(TBUF, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.runtime", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); - } - - /* - * update UPS device failure state - */ - rval = get_dev_state(DEVF, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS SoH and SoC states - */ - rval = get_dev_state(SCSH, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS battery state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS load status - */ - rval = get_dev_state(LVDC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); - } - rval = get_dev_state(LCUR, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.current", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); - } - /* check for communication errors */ -<<<<<<< HEAD - if (errcnt == 0) { - alarm_commit(); - status_commit(); - dstate_dataok(); - } else { - upsdebugx(2,"Communication errors: %d", errcnt); - dstate_datastale(); - } ->>>>>>> under construction -======= - if (errcnt == 0) { - alarm_commit(); - status_commit(); - dstate_dataok(); - } else { - upsdebugx(2, "Communication errors: %d", errcnt); - dstate_datastale(); - } ->>>>>>> ghost alarms bug fix, other bug fixes -} - -/* shutdown UPS */ -void upsdrv_shutdown(void) -{ -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> ghost alarms bug fix, other bug fixes - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); - case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); -<<<<<<< HEAD -======= - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); - case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); ->>>>>>> under construction -======= ->>>>>>> ghost alarms bug fix, other bug fixes -} - -/* print driver usage info */ -void upsdrv_help(void) -{ -} - -/* list flags and values that you want to receive via -x */ -void upsdrv_makevartable(void) -{ -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> ghost alarms bug fix, other bug fixes - addvar(VAR_VALUE, "device_mfr", "device manufacturer"); - addvar(VAR_VALUE, "device_model", "device model"); - addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); - addvar(VAR_VALUE, "ser_parity", "serial port parity"); - addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); - addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); - addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); -<<<<<<< HEAD -======= - addvar(VAR_VALUE, "device_mfr", "device manufacturer"); - addvar(VAR_VALUE, "device_model", "device model"); - addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); - addvar(VAR_VALUE, "ser_parity", "serial port parity"); - addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); - addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); - addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); ->>>>>>> under construction -======= ->>>>>>> ghost alarms bug fix, other bug fixes - addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); - addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); - addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); - addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); -} - -/* close modbus connection and free modbus context allocated memory */ -void upsdrv_cleanup(void) -{ -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> ghost alarms bug fix, other bug fixes - if (mbctx != NULL) { - modbus_close(mbctx); - modbus_free(mbctx); - } - if (dstate != NULL) { - free(dstate); - } -<<<<<<< HEAD -======= - if (mbctx != NULL) { - modbus_close(mbctx); - modbus_free(mbctx); - } ->>>>>>> under construction -======= ->>>>>>> ghost alarms bug fix, other bug fixes -} - -/* - * driver support functions - */ - -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> structure device data, code get_dev_state, in progress -/* initialize register start address and hex address from register number */ -void reginit() -{ - int i; /* local index */ - -<<<<<<< HEAD -<<<<<<< HEAD - for (i = 0; i < MODBUS_NUMOF_REGS; i++) { -======= - for (i = 1; i < MODBUS_NUMOF_REGS; i++) { ->>>>>>> structure device data, code get_dev_state, in progress -======= - for (i = 0; i < MODBUS_NUMOF_REGS; i++) { ->>>>>>> ghost alarms bug fix, other bug fixes - int rnum = regs[i].num; - switch (regs[i].type) { - case COIL: - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x0 + regs[i].num - 1; - break; - case INPUT_B: - rnum -= 10000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x10000 + rnum - 1; - break; - case INPUT_R: - rnum -= 30000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x30000 + rnum - 1; - break; - case HOLDING: - rnum -= 40000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x40000 + rnum - 1; - break; - default: -<<<<<<< HEAD -<<<<<<< HEAD - upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); - upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); - } - upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", - regs[i].num, - regs[i].type, - regs[i].saddr, - regs[i].xaddr -======= - upslogx(LOG_ERR, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); - upsdebugx(3, "Invalid register type %d for register %d\n", regs[i].type, regs[i].num); - } - upsdebugx(3, "register num:%d, type: %d saddr %d, xaddr x%x\n", regs[i].num, - regs[i].type, - regs[i].saddr, - regs[i].xaddr ->>>>>>> structure device data, code get_dev_state, in progress -======= - upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); - upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); - } - upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", - regs[i].num, - regs[i].type, - regs[i].saddr, - regs[i].xaddr ->>>>>>> ghost alarms bug fix, other bug fixes - ); - } -} - -<<<<<<< HEAD -/* Read a modbus register */ -int register_read(modbus_t *mb, int addr, regtype_t type, void *data) -{ - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x00FF; - uint mask16 = 0xFFFF; - - switch (type) { - case COIL: - rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_B: - rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_R: - rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - case HOLDING: - rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; -======= -======= ->>>>>>> structure device data, code get_dev_state, in progress -/* Read a modbus register */ -int register_read(modbus_t *mb, int addr, regtype_t type, void *data) -{ -<<<<<<< HEAD - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; - - switch (type) { - case COIL: - rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_B: - rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_R: - rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - case HOLDING: - rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; ->>>>>>> under construction -======= - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x00FF; - uint mask16 = 0xFFFF; - - switch (type) { - case COIL: - rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_B: - rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_R: - rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - case HOLDING: - rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; ->>>>>>> ghost alarms bug fix, other bug fixes - -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> ghost alarms bug fix, other bug fixes - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -<<<<<<< HEAD -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; -======= - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -======= ->>>>>>> ghost alarms bug fix, other bug fixes -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } -<<<<<<< HEAD - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; ->>>>>>> under construction -======= - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; ->>>>>>> ghost alarms bug fix, other bug fixes -} - -/* write a modbus register */ -int register_write(modbus_t *mb, int addr, regtype_t type, void *data) -{ -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> ghost alarms bug fix, other bug fixes - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x00FF; - uint mask16 = 0xFFFF; - - switch (type) { - case COIL: - *(uint *)data = *(uint *)data & mask8; - rval = modbus_write_bit(mb, addr, *(uint8_t *)data); - break; - case HOLDING: - *(uint *)data = *(uint *)data & mask16; - rval = modbus_write_register(mb, addr, *(uint16_t *)data); - break; - - case INPUT_B: - case INPUT_R: -<<<<<<< HEAD -======= - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x000F; - uint mask16 = 0x00FF; - - switch (type) { - case COIL: - *(uint *)data = *(uint *)data & mask8; - rval = modbus_write_bit(mb, addr, *(uint8_t *)data); - break; - case HOLDING: - *(uint *)data = *(uint *)data & mask16; - rval = modbus_write_register(mb, addr, *(uint16_t *)data); - break; - - case INPUT_B: - case INPUT_R: ->>>>>>> under construction -======= ->>>>>>> ghost alarms bug fix, other bug fixes -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> ghost alarms bug fix, other bug fixes - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; -<<<<<<< HEAD -======= - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; ->>>>>>> under construction -======= ->>>>>>> ghost alarms bug fix, other bug fixes -} - -/* returns the time elapsed since start in milliseconds */ -long time_elapsed(struct timeval *start) -{ -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> ghost alarms bug fix, other bug fixes - long rval; - struct timeval end; - - rval = gettimeofday(&end, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); - } - if (start->tv_usec < end.tv_usec) { - suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; - end.tv_usec -= 1000000 * nsec; - end.tv_sec += nsec; - } - if (start->tv_usec - end.tv_usec > 1000000) { - suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; - end.tv_usec += 1000000 * nsec; - end.tv_sec -= nsec; - } - rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; - - return rval; -<<<<<<< HEAD -======= - long rval; - struct timeval end; - - rval = gettimeofday(&end, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); - } - if (start->tv_usec < end.tv_usec) { - suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; - end.tv_usec -= 1000000 * nsec; - end.tv_sec += nsec; - } - if (start->tv_usec - end.tv_usec > 1000000) { - suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; - end.tv_usec += 1000000 * nsec; - end.tv_sec -= nsec; - } - rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; - - return rval; ->>>>>>> under construction -======= ->>>>>>> ghost alarms bug fix, other bug fixes -} - -/* instant command triggered by upsd */ -int upscmd(const char *cmd, const char *arg) -{ -<<<<<<< HEAD -<<<<<<< HEAD - int rval; - int data; - - if (!strcasecmp(cmd, "load.off")) { - data = 1; - rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); - if (rval == -1) { - upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - regs[FSD].xaddr, - regs[FSD].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); - rval = STAT_INSTCMD_HANDLED; - } - } else { - upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_UNKNOWN; - } - return rval; -} - -/* read device state, returns 0 on success or -1 on communication error - it formats state depending on register semantics */ -int get_dev_state(devreg_t regindx, devstate_t **dstate) -{ - int i; /* local index */ - int n; - int rval; /* return value */ - static char *ptr = NULL; /* temporary pointer */ - uint num; /* register number */ - uint reg_val; /* register value */ - regtype_t rtype; /* register type */ - int addr; /* register address */ - devstate_t *state; /* device state */ - - state = *dstate; - num = regs[regindx].num; - addr = regs[regindx].xaddr; - rtype = regs[regindx].type; - - rval = register_read(mbctx, addr, rtype, ®_val); - if (rval == -1) { - return rval; - } - upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", - num, - addr, - rtype, - reg_val - ); - - /* process register data */ - switch (regindx) { - case CHRG: /* "ups.charge" */ - if (reg_val == CHRG_NONE) { - state->charge.state = CHRG_NONE; - state->charge.info = chrgs_i[CHRG_NONE]; - } else if (reg_val == CHRG_RECV) { - state->charge.state = CHRG_RECV; - state->charge.info = chrgs_i[CHRG_RECV]; - } else if (reg_val == CHRG_BULK) { - state->charge.state = CHRG_BULK; - state->charge.info = chrgs_i[CHRG_BULK]; - } else if (reg_val == CHRG_ABSR) { - state->charge.state = CHRG_ABSR; - state->charge.info = chrgs_i[CHRG_ABSR]; - } else if (reg_val == CHRG_FLOAT) { - state->charge.state = CHRG_FLOAT; - state->charge.info = chrgs_i[CHRG_FLOAT]; - } - upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); - break; - case BATV: /* "battery.voltage" */ - case LVDC: /* "output.voltage" */ - case LCUR: /* "output.current" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case TBUF: - case BSOH: - case BCEF: - case VAC: /* "input.voltage" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BSOC: /* "battery.charge" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - double fval = (double )reg_val * regs[BSOC].scale; - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BTMP: /* "battery.temperature" */ - case OTMP: /* "ups.temperature" */ - state->reg.val16 = reg_val; - double fval = reg_val - 273.15; - n = snprintf(NULL, 0, "%.2f", fval); - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - if (ptr != NULL) { - free(ptr); - } - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case PMNG: /* "ups.status" & "battery.charge" */ - if (reg_val == PMNG_BCKUP) { - state->power.state = PMNG_BCKUP; - state->power.info = pwrmng_i[PMNG_BCKUP]; - } else if (reg_val == PMNG_CHRGN) { - state->power.state = PMNG_CHRGN; - state->power.info = pwrmng_i[PMNG_CHRGN]; - } else if (reg_val == PMNG_BOOST) { - state->power.state = PMNG_BOOST; - state->power.info = pwrmng_i[PMNG_BOOST]; - } else if (reg_val == PMNG_NCHRG) { - state->power.state = PMNG_NCHRG; - state->power.info = pwrmng_i[PMNG_NCHRG]; - } - upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); - break; - case PRDN: /* "ups.model" */ - for (i = 0; i < DEV_NUMOF_MODELS; i++) { - if (prdnm_i[i].val == reg_val) { - break; - } - } - state->product.val = reg_val; - state->product.name = prdnm_i[i].name; - upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); - break; - case BSTA: - if (reg_val & BSTA_REVPOL_M) { - bsta.alrm[BSTA_REVPOL_I].actv = 1; - } else { - bsta.alrm[BSTA_REVPOL_I].actv = 0; - } - if (reg_val & BSTA_NOCNND_M) { - bsta.alrm[BSTA_NOCNND_I].actv = 1; - } else { - bsta.alrm[BSTA_NOCNND_I].actv = 0; - } - if (reg_val & BSTA_CLSHCR_M) { - bsta.alrm[BSTA_CLSHCR_I].actv = 1; - } else { - bsta.alrm[BSTA_CLSHCR_I].actv = 0; - } - if (reg_val & BSTA_SULPHD_M) { - bsta.alrm[BSTA_SULPHD_I].actv = 1; - } else { - bsta.alrm[BSTA_SULPHD_I].actv = 0; - } - if (reg_val & BSTA_CHEMNS_M) { - bsta.alrm[BSTA_CHEMNS_I].actv = 1; - } else { - bsta.alrm[BSTA_CHEMNS_I].actv = 0; - } - if (reg_val & BSTA_CNNFLT_M) { - bsta.alrm[BSTA_CNNFLT_I].actv = 1; - } else { - bsta.alrm[BSTA_CNNFLT_I].actv = 0; - } - state->alrm = &bsta; - break; - case SCSH: - if (reg_val & SHSC_HIRESI_M) { - shsc.alrm[SHSC_HIRESI_I].actv = 1; - } else { - shsc.alrm[SHSC_HIRESI_I].actv = 0; - } - if (reg_val & SHSC_LOCHEF_M) { - shsc.alrm[SHSC_LOCHEF_I].actv = 1; - } else { - shsc.alrm[SHSC_LOCHEF_I].actv = 0; - } - if (reg_val & SHSC_LOEFCP_M) { - shsc.alrm[SHSC_LOEFCP_I].actv = 1; - } else { - shsc.alrm[SHSC_LOEFCP_I].actv = 0; - } - if (reg_val & SHSC_LOWSOC_M) { - shsc.alrm[SHSC_LOWSOC_I].actv = 1; - } else { - shsc.alrm[SHSC_LOWSOC_I].actv = 0; - } - state->alrm = &shsc; - break; - case BVAL: - if (reg_val & BVAL_HIALRM_M) { - bval.alrm[BVAL_HIALRM_I].actv = 1; - } else { - bval.alrm[BVAL_HIALRM_I].actv = 0; - } - if (reg_val & BVAL_LOALRM_M) { - bval.alrm[BVAL_LOALRM_I].actv = 1; - } else { - bval.alrm[BVAL_LOALRM_I].actv = 0; - } - if (reg_val & BVAL_BSTSFL_M) { - bval.alrm[BVAL_BSTSFL_I].actv = 1; - } else { - bval.alrm[BVAL_BSTSFL_I].actv = 0; - } - state->alrm = &bval; - break; - case BTSF: - if (reg_val & BTSF_FCND_M) { - btsf.alrm[BTSF_FCND_I].actv = 1; - } else { - btsf.alrm[BTSF_FCND_I].actv = 0; - } - if (reg_val & BTSF_NCND_M) { - btsf.alrm[BTSF_NCND_I].actv = 1; - } else { - btsf.alrm[BTSF_NCND_I].actv = 0; - } - state->alrm = &btsf; - break; - case DEVF: - if (reg_val & DEVF_RCALRM_M) { - devf.alrm[DEVF_RCALRM_I].actv = 1; - } else { - devf.alrm[DEVF_RCALRM_I].actv = 0; - } - if (reg_val & DEVF_INALRM_M) { - devf.alrm[DEVF_INALRM_I].actv = 1; - } else { - devf.alrm[DEVF_INALRM_I].actv = 0; - } - if (reg_val & DEVF_LFNAVL_M) { - devf.alrm[DEVF_LFNAVL_I].actv = 1; - } else { - devf.alrm[DEVF_LFNAVL_I].actv = 0; - } - state->alrm = &devf; - break; - case VACA: - if (reg_val & VACA_HIALRM_M) { - vaca.alrm[VACA_HIALRM_I].actv = 1; - } else { - vaca.alrm[VACA_HIALRM_I].actv = 0; - } - if (reg_val == VACA_LOALRM_M) { - vaca.alrm[VACA_LOALRM_I].actv = 1; - } else { - vaca.alrm[VACA_LOALRM_I].actv = 0; - } - state->alrm = &vaca; - break; - case MAIN: - if (reg_val & MAINS_AVAIL_M) { - mains.alrm[MAINS_AVAIL_I].actv = 1; - } else { - mains.alrm[MAINS_AVAIL_I].actv = 0; - } - if (reg_val == SHUTD_REQST_M) { - mains.alrm[SHUTD_REQST_I].actv = 1; - } else { - mains.alrm[SHUTD_REQST_I].actv = 0; - } - state->alrm = &mains; - break; - case OBTA: - if (reg_val == OBTA_HIALRM_V) { - obta.alrm[OBTA_HIALRM_I].actv = 1; - } - state->alrm = &obta; - break; -======= - int rval; - int data; -======= - int rval; - int data; ->>>>>>> ghost alarms bug fix, other bug fixes - - if (!strcasecmp(cmd, "load.off")) { - data = 1; - rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); - if (rval == -1) { - upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - regs[FSD].xaddr, - regs[FSD].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); - rval = STAT_INSTCMD_HANDLED; - } - } else { - upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_UNKNOWN; - } - return rval; -} - -/* read device state, returns 0 on success or -1 on communication error - it formats state depending on register semantics */ -int get_dev_state(devreg_t regindx, devstate_t **dstate) -{ - int i; /* local index */ - int n; - int rval; /* return value */ - static char *ptr = NULL; /* temporary pointer */ - uint reg_val; /* register value */ -#if READALL_REGS == 0 - uint num; /* register number */ - regtype_t rtype; /* register type */ - int addr; /* register address */ -#endif - devstate_t *state; /* device state */ - - state = *dstate; -#if READALL_REGS == 1 - reg_val = regs_data[regindx]; - rval = 0; -#elif READALL_REGS == 0 - num = regs[regindx].num; - addr = regs[regindx].xaddr; - rtype = regs[regindx].type; - rval = register_read(mbctx, addr, rtype, ®_val); - if (rval == -1) { - return rval; - } - upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", - num, - addr, - rtype, - reg_val - ); -#endif - /* process register data */ - switch (regindx) { - case CHRG: /* "ups.charge" */ - if (reg_val == CHRG_NONE) { - state->charge.state = CHRG_NONE; - state->charge.info = chrgs_i[CHRG_NONE]; - } else if (reg_val == CHRG_RECV) { - state->charge.state = CHRG_RECV; - state->charge.info = chrgs_i[CHRG_RECV]; - } else if (reg_val == CHRG_BULK) { - state->charge.state = CHRG_BULK; - state->charge.info = chrgs_i[CHRG_BULK]; - } else if (reg_val == CHRG_ABSR) { - state->charge.state = CHRG_ABSR; - state->charge.info = chrgs_i[CHRG_ABSR]; - } else if (reg_val == CHRG_FLOAT) { - state->charge.state = CHRG_FLOAT; - state->charge.info = chrgs_i[CHRG_FLOAT]; - } - upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); - break; - case BATV: /* "battery.voltage" */ - case LVDC: /* "output.voltage" */ - case LCUR: /* "output.current" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case TBUF: - case BSOH: - case BCEF: - case VAC: /* "input.voltage" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BSOC: /* "battery.charge" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - double fval = (double )reg_val * regs[BSOC].scale; - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BTMP: /* "battery.temperature" */ - case OTMP: /* "ups.temperature" */ - state->reg.val16 = reg_val; - double fval = reg_val - 273.15; - n = snprintf(NULL, 0, "%.2f", fval); - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - if (ptr != NULL) { - free(ptr); - } - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case PMNG: /* "ups.status" & "battery.charge" */ - if (reg_val == PMNG_BCKUP) { - state->power.state = PMNG_BCKUP; - state->power.info = pwrmng_i[PMNG_BCKUP]; - } else if (reg_val == PMNG_CHRGN) { - state->power.state = PMNG_CHRGN; - state->power.info = pwrmng_i[PMNG_CHRGN]; - } else if (reg_val == PMNG_BOOST) { - state->power.state = PMNG_BOOST; - state->power.info = pwrmng_i[PMNG_BOOST]; - } else if (reg_val == PMNG_NCHRG) { - state->power.state = PMNG_NCHRG; - state->power.info = pwrmng_i[PMNG_NCHRG]; - } -<<<<<<< HEAD - break; -<<<<<<< HEAD - - case BYPASS_T: - case CAL_T: - case FSD_T: - case OFF_T: - case OVER_T: - case TRIM_T: - case BOOST_T: ->>>>>>> under construction -======= -======= - upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); - break; ->>>>>>> ghost alarms bug fix, other bug fixes - case PRDN: /* "ups.model" */ - for (i = 0; i < DEV_NUMOF_MODELS; i++) { - if (prdnm_i[i].val == reg_val) { - break; - } - } - state->product.val = reg_val; - state->product.name = prdnm_i[i].name; - upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); - break; -<<<<<<< HEAD ->>>>>>> structure device data, code get_dev_state, in progress -======= - case BSTA: - if (reg_val & BSTA_REVPOL_M) { - bsta.alrm[BSTA_REVPOL_I].actv = 1; - } else { - bsta.alrm[BSTA_REVPOL_I].actv = 0; - } - if (reg_val & BSTA_NOCNND_M) { - bsta.alrm[BSTA_NOCNND_I].actv = 1; - } else { - bsta.alrm[BSTA_NOCNND_I].actv = 0; - } - if (reg_val & BSTA_CLSHCR_M) { - bsta.alrm[BSTA_CLSHCR_I].actv = 1; - } else { - bsta.alrm[BSTA_CLSHCR_I].actv = 0; - } - if (reg_val & BSTA_SULPHD_M) { - bsta.alrm[BSTA_SULPHD_I].actv = 1; - } else { - bsta.alrm[BSTA_SULPHD_I].actv = 0; - } - if (reg_val & BSTA_CHEMNS_M) { - bsta.alrm[BSTA_CHEMNS_I].actv = 1; - } else { - bsta.alrm[BSTA_CHEMNS_I].actv = 0; - } - if (reg_val & BSTA_CNNFLT_M) { - bsta.alrm[BSTA_CNNFLT_I].actv = 1; - } else { - bsta.alrm[BSTA_CNNFLT_I].actv = 0; - } - state->alrm = &bsta; - break; - case SCSH: - if (reg_val & SHSC_HIRESI_M) { - shsc.alrm[SHSC_HIRESI_I].actv = 1; - } else { - shsc.alrm[SHSC_HIRESI_I].actv = 0; - } - if (reg_val & SHSC_LOCHEF_M) { - shsc.alrm[SHSC_LOCHEF_I].actv = 1; - } else { - shsc.alrm[SHSC_LOCHEF_I].actv = 0; - } - if (reg_val & SHSC_LOEFCP_M) { - shsc.alrm[SHSC_LOEFCP_I].actv = 1; - } else { - shsc.alrm[SHSC_LOEFCP_I].actv = 0; - } - if (reg_val & SHSC_LOWSOC_M) { - shsc.alrm[SHSC_LOWSOC_I].actv = 1; - } else { - shsc.alrm[SHSC_LOWSOC_I].actv = 0; - } - state->alrm = &shsc; - break; - case BVAL: - if (reg_val & BVAL_HIALRM_M) { - bval.alrm[BVAL_HIALRM_I].actv = 1; - } else { - bval.alrm[BVAL_HIALRM_I].actv = 0; - } - if (reg_val & BVAL_LOALRM_M) { - bval.alrm[BVAL_LOALRM_I].actv = 1; - } else { - bval.alrm[BVAL_LOALRM_I].actv = 0; - } - if (reg_val & BVAL_BSTSFL_M) { - bval.alrm[BVAL_BSTSFL_I].actv = 1; - } else { - bval.alrm[BVAL_BSTSFL_I].actv = 0; - } - state->alrm = &bval; - break; - case BTSF: - if (reg_val & BTSF_FCND_M) { - btsf.alrm[BTSF_FCND_I].actv = 1; - } else { - btsf.alrm[BTSF_FCND_I].actv = 0; - } - if (reg_val & BTSF_NCND_M) { - btsf.alrm[BTSF_NCND_I].actv = 1; - } else { - btsf.alrm[BTSF_NCND_I].actv = 0; - } - state->alrm = &btsf; - break; - case DEVF: - if (reg_val & DEVF_RCALRM_M) { - devf.alrm[DEVF_RCALRM_I].actv = 1; - } else { - devf.alrm[DEVF_RCALRM_I].actv = 0; - } - if (reg_val & DEVF_INALRM_M) { - devf.alrm[DEVF_INALRM_I].actv = 1; - } else { - devf.alrm[DEVF_INALRM_I].actv = 0; - } - if (reg_val & DEVF_LFNAVL_M) { - devf.alrm[DEVF_LFNAVL_I].actv = 1; - } else { - devf.alrm[DEVF_LFNAVL_I].actv = 0; - } - state->alrm = &devf; - break; - case VACA: - if (reg_val & VACA_HIALRM_M) { - vaca.alrm[VACA_HIALRM_I].actv = 1; - } else { - vaca.alrm[VACA_HIALRM_I].actv = 0; - } - if (reg_val == VACA_LOALRM_M) { - vaca.alrm[VACA_LOALRM_I].actv = 1; - } else { - vaca.alrm[VACA_LOALRM_I].actv = 0; - } - state->alrm = &vaca; - break; - case MAIN: - if (reg_val & MAINS_AVAIL_M) { - mains.alrm[MAINS_AVAIL_I].actv = 1; - } else { - mains.alrm[MAINS_AVAIL_I].actv = 0; - } - if (reg_val == SHUTD_REQST_M) { - mains.alrm[SHUTD_REQST_I].actv = 1; - } else { - mains.alrm[SHUTD_REQST_I].actv = 0; - } - state->alrm = &mains; - break; -<<<<<<< HEAD ->>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress -======= - case OBTA: - if (reg_val == OBTA_HIALRM_V) { - obta.alrm[OBTA_HIALRM_I].actv = 1; - } - state->alrm = &obta; - break; ->>>>>>> ghost alarms bug fix, other bug fixes -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> ghost alarms bug fix, other bug fixes - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -<<<<<<< HEAD -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - state->reg.val16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - break; - } - - return rval; -======= - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -======= ->>>>>>> ghost alarms bug fix, other bug fixes -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - state->reg.val16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - break; - } - -<<<<<<< HEAD - upsdebugx(3, "get_dev_state: state: %d", reg_val); - return rval; ->>>>>>> under construction -======= - return rval; ->>>>>>> ghost alarms bug fix, other bug fixes -} - -/* get driver configuration parameters */ -void get_config_vars() -{ -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> ghost alarms bug fix, other bug fixes - /* check if device manufacturer is set ang get the value */ - if (testvar("device_mfr")) { - device_mfr = getval("device_mfr"); - } - upsdebugx(2, "device_mfr %s", device_mfr); - - /* check if device model is set ang get the value */ - if (testvar("device_model")) { - device_model = getval("device_model"); - } - upsdebugx(2, "device_model %s", device_model); - - /* check if serial baud rate is set ang get the value */ - if (testvar("ser_baud_rate")) { - ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); - } - upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); - - /* check if serial parity is set ang get the value */ - if (testvar("ser_parity")) { - /* Dereference the char* we get */ - char *sp = getval("ser_parity"); - if (sp) { - /* TODO? Sanity-check the char we get? */ - ser_parity = *sp; - } else { - upsdebugx(2, "Could not determine ser_parity, will keep default"); - } - } - upsdebugx(2, "ser_parity %c", ser_parity); - - /* check if serial data bit is set ang get the value */ - if (testvar("ser_data_bit")) { - ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); - } - upsdebugx(2, "ser_data_bit %d", ser_data_bit); - - /* check if serial stop bit is set ang get the value */ - if (testvar("ser_stop_bit")) { - ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); - } - upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); - - /* check if device ID is set ang get the value */ - if (testvar("rio_slave_id")) { - rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); - } - upsdebugx(2, "rio_slave_id %d", rio_slave_id); -<<<<<<< HEAD -======= - int i; /* local index */ - - /* initialize sigar table */ - for (i = 0; i < NUMOF_SIG_STATES; i++) { - sigar[i].addr = NOTUSED; - sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ - } - -======= ->>>>>>> first testing release - /* check if device manufacturer is set ang get the value */ - if (testvar("device_mfr")) { - device_mfr = getval("device_mfr"); - } - upsdebugx(2, "device_mfr %s", device_mfr); - - /* check if device model is set ang get the value */ - if (testvar("device_model")) { - device_model = getval("device_model"); - } - upsdebugx(2, "device_model %s", device_model); - - /* check if serial baud rate is set ang get the value */ - if (testvar("ser_baud_rate")) { - ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); - } - upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); - - /* check if serial parity is set ang get the value */ - if (testvar("ser_parity")) { - /* Dereference the char* we get */ - char *sp = getval("ser_parity"); - if (sp) { - /* TODO? Sanity-check the char we get? */ - ser_parity = *sp; - } else { - upsdebugx(2, "Could not determine ser_parity, will keep default"); - } - } - upsdebugx(2, "ser_parity %c", ser_parity); - - /* check if serial data bit is set ang get the value */ - if (testvar("ser_data_bit")) { - ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); - } - upsdebugx(2, "ser_data_bit %d", ser_data_bit); - - /* check if serial stop bit is set ang get the value */ - if (testvar("ser_stop_bit")) { - ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); - } - upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); - - /* check if device ID is set ang get the value */ - if (testvar("rio_slave_id")) { - rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); - } - upsdebugx(2, "rio_slave_id %d", rio_slave_id); ->>>>>>> under construction -======= ->>>>>>> ghost alarms bug fix, other bug fixes - - /* check if response time out (s) is set ang get the value */ - if (testvar("mod_resp_to_s")) { - mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); - } - upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); - - /* check if response time out (us) is set ang get the value */ - if (testvar("mod_resp_to_us")) { - mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); - if (mod_resp_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); - } - } - upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); - - /* check if byte time out (s) is set ang get the value */ - if (testvar("mod_byte_to_s")) { - mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); - } - upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); - - /* check if byte time out (us) is set ang get the value */ - if (testvar("mod_byte_to_us")) { - mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); - if (mod_byte_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); - } - } - upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); -<<<<<<< HEAD -<<<<<<< HEAD -======= - - /* check if OL address is set and get the value */ - if (testvar("OL_addr")) { - sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); - if (testvar("OL_noro")) { - sigar[OL_T].noro = (int)strtol(getval("OL_noro"), NULL, 10); - if (sigar[OL_T].noro != 1) { - sigar[OL_T].noro = 0; - } - } - } - - /* check if OL register type is set and get the value otherwise set to INPUT_B */ - if (testvar("OL_regtype")) { - sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); - if (sigar[OL_T].type < COIL || sigar[OL_T].type > HOLDING) { - sigar[OL_T].type = INPUT_B; - } - } else { - sigar[OL_T].type = INPUT_B; - } - - /* check if OB address is set and get the value */ - if (testvar("OB_addr")) { - sigar[OB_T].addr = (int)strtol(getval("OB_addr"), NULL, 0); - } - if (testvar("OB_noro")) { - sigar[OB_T].noro = (int)strtol(getval("OB_noro"), NULL, 10); - if (sigar[OB_T].noro != 1) { - sigar[OB_T].noro = 0; - } - } - - /* check if OB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("OB_regtype")) { - sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); - if (sigar[OB_T].type < COIL || sigar[OB_T].type > HOLDING) { - sigar[OB_T].type = INPUT_B; - } - } else { - sigar[OB_T].type = INPUT_B; - } - - /* check if LB address is set and get the value */ - if (testvar("LB_addr")) { - sigar[LB_T].addr = (int)strtol(getval("LB_addr"), NULL, 0); - if (testvar("LB_noro")) { - sigar[LB_T].noro = (int)strtol(getval("LB_noro"), NULL, 10); - if (sigar[LB_T].noro != 1) { - sigar[LB_T].noro = 0; - } - } - } - - /* check if LB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("LB_regtype")) { - sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); - if (sigar[LB_T].type < COIL || sigar[LB_T].type > HOLDING) { - sigar[LB_T].type = INPUT_B; - } - } else { - sigar[LB_T].type = INPUT_B; - } - - /* check if HB address is set and get the value */ - if (testvar("HB_addr")) { - sigar[HB_T].addr = (int)strtol(getval("HB_addr"), NULL, 0); - if (testvar("HB_noro")) { - sigar[HB_T].noro = (int)strtol(getval("HB_noro"), NULL, 10); - if (sigar[HB_T].noro != 1) { - sigar[HB_T].noro = 0; - } - } - } - - /* check if HB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("HB_regtype")) { - sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); - if (sigar[HB_T].type < COIL || sigar[HB_T].type > HOLDING) { - sigar[HB_T].type = INPUT_B; - } - } else { - sigar[HB_T].type = INPUT_B; - } - - /* check if RB address is set and get the value */ - if (testvar("RB_addr")) { - sigar[RB_T].addr = (int)strtol(getval("RB_addr"), NULL, 0); - if (testvar("RB_noro")) { - sigar[RB_T].noro = (int)strtol(getval("RB_noro"), NULL, 10); - if (sigar[RB_T].noro != 1) { - sigar[RB_T].noro = 0; - } - } - } - - /* check if RB register type is set and get the value otherwise set to INPUT_B */ - if (testvar("RB_regtype")) { - sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); - if (sigar[RB_T].type < COIL || sigar[RB_T].type > HOLDING) { - sigar[RB_T].type = INPUT_B; - } - } else { - sigar[RB_T].type = INPUT_B; - } - - /* check if CHRG address is set and get the value */ - if (testvar("CHRG_addr")) { - sigar[CHRG_T].addr = (int)strtol(getval("CHRG_addr"), NULL, 0); - if (testvar("CHRG_noro")) { - sigar[CHRG_T].noro = (int)strtol(getval("CHRG_noro"), NULL, 10); - if (sigar[CHRG_T].noro != 1) { - sigar[CHRG_T].noro = 0; - } - } - } - - /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ - if (testvar("CHRG_regtype")) { - sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); - if (sigar[CHRG_T].type < COIL || sigar[CHRG_T].type > HOLDING) { - sigar[CHRG_T].type = INPUT_B; - } - } else { - sigar[CHRG_T].type = INPUT_B; - } - - /* check if DISCHRG address is set and get the value */ - if (testvar("DISCHRG_addr")) { - sigar[DISCHRG_T].addr = (int)strtol(getval("DISCHRG_addr"), NULL, 0); - if (testvar("DISCHRG_noro")) { - sigar[DISCHRG_T].noro = (int)strtol(getval("DISCHRG_noro"), NULL, 10); - if (sigar[DISCHRG_T].noro != 1) { - sigar[DISCHRG_T].noro = 0; - } - } - } - - /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ - if (testvar("DISCHRG_regtype")) { - sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); - if (sigar[DISCHRG_T].type < COIL || sigar[DISCHRG_T].type > HOLDING) { - sigar[DISCHRG_T].type = INPUT_B; - } - } else { - sigar[DISCHRG_T].type = INPUT_B; - } - - /* check if FSD address is set and get the value */ - if (testvar("FSD_addr")) { - sigar[FSD_T].addr = (int)strtol(getval("FSD_addr"), NULL, 0); - if (testvar("FSD_noro")) { - sigar[FSD_T].noro = (int)strtol(getval("FSD_noro"), NULL, 10); - if (sigar[FSD_T].noro != 1) { - sigar[FSD_T].noro = 0; - } - } - } - - /* check if FSD register type is set and get the value otherwise set to COIL */ - if (testvar("FSD_regtype")) { - sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); - if (sigar[FSD_T].type < COIL || sigar[FSD_T].type > HOLDING) { - sigar[FSD_T].type = COIL; - } - } else { - sigar[FSD_T].type = COIL; - } - - /* check if FSD pulse duration is set and get the value */ - if (testvar("FSD_pulse_duration")) { - FSD_pulse_duration = (int) strtol(getval("FSD_pulse_duration"), NULL, 10); - } - upsdebugx(2, "FSD_pulse_duration %d", FSD_pulse_duration); - - /* debug loop over signal array */ - for (i = 0; i < NUMOF_SIG_STATES; i++) { - if (sigar[i].addr != NOTUSED) { - char *signame; - switch (i) { - case OL_T: - signame = "OL"; - break; - case OB_T: - signame = "OB"; - break; - case LB_T: - signame = "LB"; - break; - case HB_T: - signame = "HB"; - break; - case RB_T: - signame = "RB"; - break; - case FSD_T: - signame = "FSD"; - break; - case CHRG_T: - signame = "CHRG"; - break; - case DISCHRG_T: - signame = "DISCHRG"; - break; - default: - signame = "NOTUSED"; - break; - } - upsdebugx(2, "%s, addr:0x%x, type:%d", signame, sigar[i].addr, sigar[i].type); - } - } ->>>>>>> under construction -======= ->>>>>>> first testing release -} - -/* create a new modbus context based on connection type (serial or TCP) */ -modbus_t *modbus_new(const char *port) -{ -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> ghost alarms bug fix, other bug fixes - modbus_t *mb; - char *sp; - if (strstr(port, "/dev/tty") != NULL) { - mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); - } - } else if ((sp = strchr(port, ':')) != NULL) { - char *tcp_port = xmalloc(sizeof(sp)); - strcpy(tcp_port, sp + 1); - *sp = '\0'; - mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - free(tcp_port); - } else { - mb = modbus_new_tcp(port, 502); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - } - return mb; -<<<<<<< HEAD -======= - modbus_t *mb; - char *sp; - if (strstr(port, "/dev/tty") != NULL) { - mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); - } - } else if ((sp = strchr(port, ':')) != NULL) { - char *tcp_port = xmalloc(sizeof(sp)); - strcpy(tcp_port, sp + 1); - *sp = '\0'; - mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - free(tcp_port); - } else { - mb = modbus_new_tcp(port, 502); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - } - return mb; ->>>>>>> under construction -======= ->>>>>>> ghost alarms bug fix, other bug fixes -} - -/* reconnect to modbus server upon connection error */ -void modbus_reconnect() -{ - int rval; - -<<<<<<< HEAD -<<<<<<< HEAD - upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); -======= - upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); ->>>>>>> under construction -======= - upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); ->>>>>>> ghost alarms bug fix, other bug fixes - - /* clear current modbus context */ - modbus_close(mbctx); - modbus_free(mbctx); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -<<<<<<< HEAD -<<<<<<< HEAD -} -======= -} ->>>>>>> under construction -======= -} ->>>>>>> ghost alarms bug fix, other bug fixes diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h deleted file mode 100644 index 6871a32bbd..0000000000 --- a/drivers/adele_cbi.h +++ /dev/null @@ -1,1036 +0,0 @@ -<<<<<<< HEAD -<<<<<<< HEAD -/* adele_cbi.h - Driver for ADELE CB/CBI DC-UPS - * - * Copyright (C) - * 2022 Dimitris Economou -<<<<<<< HEAD -======= -/* adele_cbi.h - Driver for generic UPS connected via modbus RIO -======= -/* adele_cbi.h - Driver for ADELE CB/CBI DC-UPS ->>>>>>> structure device data, code get_dev_state, in progress - * - * Copyright (C) - * 2021 Dimitris Economou ->>>>>>> under construction -======= ->>>>>>> ghost alarms bug fix, other bug fixes - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef ADELE_CBI_H -#define ADELE_CBI_H - -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> register status values and masks added -#include - -/* UPS device details */ -#define DEVICE_MFR "ADELE" -#define DEVICE_TYPE "DC-UPS" -<<<<<<< HEAD -======= -/* UPS device details */ -#define DEVICE_MFR "ADELE" ->>>>>>> under construction -======= ->>>>>>> alrm_t, alrm_ar_t data structures, construction of upsdrv_updateinfo in progress -#define DEVICE_MODEL "CB/CBI" - -/* serial access parameters */ -#define BAUD_RATE 9600 -#define PARITY 'N' -#define DATA_BIT 8 -#define STOP_BIT 1 - -/* - * modbus response and byte timeouts - * us: 1 - 999999 - */ -#define MODRESP_TIMEOUT_s 0 -#define MODRESP_TIMEOUT_us 200000 -#define MODBYTE_TIMEOUT_s 0 -#define MODBYTE_TIMEOUT_us 50000 - -/* modbus access parameters */ -#define MODBUS_SLAVE_ID 5 - -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> structure device data, code get_dev_state, in progress -/* number of modbus registers */ -#define MODBUS_NUMOF_REGS 98 - -/* max HOLDING registers */ -#define MAX_H_REGS 120 - -/* start HOLDING register index */ -#define H_REG_STARTIDX 0 - -/* read all regs preprocessor flag */ -#define READALL_REGS 1 - -/* number of device models */ -#define DEV_NUMOF_MODELS 10 - -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> first testing release -/* shutdown repeat on error */ -#define FSD_REPEAT_CNT 3 - -/* shutdown repeat interval in ms */ -#define FSD_REPEAT_INTRV 1500 - -<<<<<<< HEAD -/* definition of register type */ -enum regtype { - COIL = 0, - INPUT_B, - INPUT_R, - HOLDING -}; -typedef enum regtype regtype_t; - -/* product name info, "device.model" */ -struct prodname { - uint16_t val; - char *name; -}; -typedef struct prodname prodname_t; -static prodname_t prdnm_i[] = { - {1, "CBI1235A"}, - {2, "CBI2420A"}, - {3, "CBI4810A"}, - {4, "CBI2801224"}, - {7, "CBI480W"}, - {8, "CB122410A"}, - {9, "CB480W"}, - {11, "CB12245AJ"}, - {12, "CB1235A"}, - {13, "CB2420A"}, - {14, "CB4810A"} -}; - -/* charging status info, "battery.charger.status" */ -static char *chrgs_i[] = { - "none", - "resting", /* recovering */ - "charging", /* bulk */ - "charging", /* absorb */ - "floating" /* float */ -}; -struct chrgs { - int state; - char *info; -}; -typedef struct chrgs chrgs_t; - -/* power management info, "ups.status", "battery.charger.status" */ -static char *pwrmng_i[] = { - "backup", /* "OB", "discharging" */ - "charging", /* "OL" */ - "boost", - "not charging" -}; -struct pwrmng { - int state; - char *info; -}; -typedef struct pwrmng pwrmng_t; - -/* general modbus register value */ -struct reg { - union { - uint16_t val16; - uint8_t val8; - }; - char *strval; -}; -typedef struct reg reg_t; - -/* general alarm struct */ -struct alrm { - int actv; /* active flag */ - char *descr; /* description field */ -}; -typedef struct alrm alrm_t; - -/* general alarm array */ -struct alrm_ar { - int alrm_c; /* alarm count */ - alrm_t alrm[]; /* alarm array */ -}; -typedef struct alrm_ar alrm_ar_t; - -/* - * BIT MASKS and VALUES - */ - -/* Charging status */ -======= -======= ->>>>>>> structure device data, code get_dev_state, in progress -======= ->>>>>>> first testing release -/* definition of register type */ -enum regtype { - COIL = 0, - INPUT_B, - INPUT_R, - HOLDING -}; -typedef enum regtype regtype_t; - -/* product name info, "device.model" */ -struct prodname { - uint16_t val; - char *name; -}; -typedef struct prodname prodname_t; -<<<<<<< HEAD -<<<<<<< HEAD - -<<<<<<< HEAD -/* BIT MASKS and VALUES */ ->>>>>>> under construction -======= -/* product name */ -prodname_t prdn[] = { -======= -prodname_t prdnm_i[] = { ->>>>>>> structure device data, code get_dev_state, in progress -======= -static prodname_t prdnm_i[] = { ->>>>>>> ghost alarms bug fix, other bug fixes - {1, "CBI1235A"}, - {2, "CBI2420A"}, - {3, "CBI4810A"}, - {4, "CBI2801224"}, - {7, "CBI480W"}, - {8, "CB122410A"}, - {9, "CB480W"}, - {11, "CB12245AJ"}, - {12, "CB1235A"}, - {13, "CB2420A"}, - {14, "CB4810A"} -}; - -/* charging status info, "battery.charger.status" */ -static char *chrgs_i[] = { - "none", - "resting", /* recovering */ - "charging", /* bulk */ - "charging", /* absorb */ - "floating" /* float */ -}; -struct chrgs { - int state; - char *info; -}; -typedef struct chrgs chrgs_t; - -/* power management info, "ups.status", "battery.charger.status" */ -static char *pwrmng_i[] = { - "backup", /* "OB", "discharging" */ - "charging", /* "OL" */ - "boost", - "not charging" -}; -struct pwrmng { - int state; - char *info; -}; -typedef struct pwrmng pwrmng_t; - -/* general modbus register value */ -struct reg { - union { - uint16_t val16; - uint8_t val8; - }; - char *strval; -}; -typedef struct reg reg_t; - -/* general alarm struct */ -struct alrm { - int actv; /* active flag */ - char *descr; /* description field */ -}; -typedef struct alrm alrm_t; - -/* general alarm array */ -struct alrm_ar { - int alrm_c; /* alarm count */ - alrm_t alrm[]; /* alarm array */ -}; -typedef struct alrm_ar alrm_ar_t; - -/* - * BIT MASKS and VALUES - */ - -/* Charging status */ ->>>>>>> register status values and masks added -#define CHRG_NONE 0 -#define CHRG_RECV 1 -#define CHRG_BULK 2 -#define CHRG_ABSR 3 -#define CHRG_FLOAT 4 -<<<<<<< HEAD -<<<<<<< HEAD - -/* power management */ -#define PMNG_BCKUP 0 -#define PMNG_CHRGN 1 -#define PMNG_BOOST 2 -#define PMNG_NCHRG 3 - -/* product name */ -#define PRDN_MAX 14 - -/* Mains alarm masks */ -#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_M 0x0002 /* shutdown requested */ - -/* Mains alarm indices */ -#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_I 1 /* shutdown requested */ - -/* AC input voltage alarm masks */ -#define VACA_HIALRM_M 0x0001 /* high alarm */ -#define VACA_LOALRM_M 0x0002 /* low alarm */ - -/* AC input voltage alarm indices */ -#define VACA_HIALRM_I 0 /* high alarm */ -#define VACA_LOALRM_I 1 /* low alarm */ - -/* Onboard temperature alarm value */ -#define OBTA_HIALRM_V 1 /* high alarm */ - -/* Onboard temperature alarm index */ -#define OBTA_HIALRM_I 0 /* high alarm */ -<<<<<<< HEAD - -/* Device failure alarm masks */ -#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ -#define DEVF_INALRM_M 0x0006 /* internal failure */ -#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ - -/* Device failure alarm indices */ -#define DEVF_RCALRM_I 0 /* rectifier failure */ -#define DEVF_INALRM_I 1 /* internal failure */ -#define DEVF_LFNAVL_I 2 /* lifetest not available */ - -/* Battery temp sensor failure alarm masks */ -#define BTSF_FCND_M 0x0001 /* connection fault */ -#define BTSF_NCND_M 0x0002 /* not connected */ - -/* Battery temp sensor failure alarm indices */ -#define BTSF_FCND_I 0 /* connection fault */ -#define BTSF_NCND_I 1 /* not connected */ - -/* Battery voltage alarm masks */ -#define BVAL_HIALRM_M 0x0001 /* high voltage */ -#define BVAL_LOALRM_M 0x0002 /* low voltage */ -#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ - -/* Battery voltage alarm indices */ -#define BVAL_HIALRM_I 0 /* high voltage */ -#define BVAL_LOALRM_I 1 /* low voltage */ -#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ - -/* SoH and SoC alarm masks */ -#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ -#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ -#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ -#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ - -/* SoH and SoC alarm indices */ -#define SHSC_HIRESI_I 0 /* high internal resistance */ -#define SHSC_LOCHEF_I 1 /* low charge efficiency */ -#define SHSC_LOEFCP_I 2 /* low effective capacity */ -#define SHSC_LOWSOC_I 3 /* low state of charge */ - -/* Battery status alarm masks */ -#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ -#define BSTA_NOCNND_M 0x0002 /* not connected */ -#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ -#define BSTA_SULPHD_M 0x0008 /* sulphated */ -#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ -#define BSTA_CNNFLT_M 0x0020 /* connection fault */ - -/* Battery status alarm indices */ -#define BSTA_REVPOL_I 0 /* reversed polarity */ -#define BSTA_NOCNND_I 1 /* not connected */ -#define BSTA_CLSHCR_I 2 /* cell short circuit */ -#define BSTA_SULPHD_I 3 /* sulphated */ -#define BSTA_CHEMNS_I 4 /* chemistry not supported */ -#define BSTA_CNNFLT_I 5 /* connection fault */ - -/* input mains and shutdown alarms */ -static alrm_ar_t mains = { - 2, - { - {0, "input voltage not available"}, - {0, "ups shutdown requested"} - } -}; - -/* AC input voltage alarms */ -static alrm_ar_t vaca = { - 2, - { - {0, "input voltage high alarm"}, - {0, "input voltage low alarm"} - } -}; - -/* device failure alarms */ -static alrm_ar_t devf = { - 3, - { - {0, "UPS rectifier failure"}, - {0, "UPS internal failure"}, - {0, "UPS lifetest not available"} - } -}; - -/* battery sensor failure alarms */ -static alrm_ar_t btsf = { - 2, - { - {0, "battery temp sensor connection fault"}, - {0, "battery temp sensor not connected"} - } -}; - -/* battery voltage alarms */ -static alrm_ar_t bval = { - 3, - { - {0, "battery high voltage"}, - {0, "battery low voltage"}, - {0, "battery start with battery flat"} - } -}; - -/* battery SoH and SoC alarms */ -static alrm_ar_t shsc = { - 4, - { - {0, "battery high internal resistance"}, - {0, "battery low charge efficiency"}, - {0, "battery low effective capacity"}, - {0, "battery low state of charge"} - } -}; - -/* battery status alarm */ -static alrm_ar_t bsta = { - 6, - { - {0, "battery reversed polarity"}, - {0, "battery not connected"}, - {0, "battery cell short circuit"}, - {0, "battery sulphated"}, - {0, "battery chemistry not supported"}, - {0, "battery connection fault"} - } -}; - -/* onboard temperature alarm */ -static alrm_ar_t obta = { - 1, - { - {0, "onboard temperature high"} - } -}; - -/* UPS device reg enum */ -enum devreg { - CHRG = 4, /* Charging status, "battery.charger.status" */ - BATV = 7, /* Battery voltage, "battery.voltage" */ - BCEF = 18, /* Battery charge efficiency factor (CEF) */ - BSOH = 20, /* Battery state-of-health */ - BSOC = 22, /* Battery state-of-charge, "battery.charge" */ - BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ - PMNG = 5, /* Power management, "ups.status" */ - OTMP = 28, /* Onboard temperature, "ups.temperature" */ - PRDN = 67, /* Product name, "ups.model" */ - VAC = 29, /* AC voltage, "input.voltage" */ - LVDC = 10, /* Load voltage, "output.voltage" */ - LCUR = 19, /* Load current, "output.current" */ - BINH = 87, /* Backup inhibit */ - FSD = 40, /* Force shutdown */ - TBUF = 103, /* Time buffering, "battery.runtime" */ - BSTA = 31, /* Battery status alarms */ - SCSH, /* SoH and SoC alarms */ - BVAL = 34, /* Battery voltage alarm */ - BTSF = 43, /* Battery temp sensor failure */ - DEVF = 42, /* Device failure */ - OBTA = 46, /* On board temp alarm */ - VACA = 44, /* VAC alarms */ - MAIN /* Mains status */ -}; -typedef enum devreg devreg_t; - -/* UPS register attributes */ -struct regattr { - int num; - int saddr; /* register start address */ - int xaddr; /* register hex address */ - float scale; /* scale */ - regtype_t type; /* register type */ -}; -typedef struct regattr regattr_t; - -/* UPS device state info union */ -union devstate { - prodname_t product; /* ups model name */ - chrgs_t charge; /* charging status */ - pwrmng_t power; /* ups status */ - reg_t reg; /* state register*/ - alrm_ar_t *alrm; /* alarm statuses */ -}; - -typedef union devstate devstate_t; - -/* device register memory image */ -static uint16_t regs_data[MAX_H_REGS]; - -/* ADELE CBI registers */ -static regattr_t regs[] = { -<<<<<<< HEAD -======= -======= - -/* power management */ -<<<<<<< HEAD ->>>>>>> register status values and masks added -#define PMNG_BKUP 0 -#define PMNG_CHRG 1 -======= -#define PMNG_BCKUP 0 -#define PMNG_CHRGN 1 ->>>>>>> structure device data, code get_dev_state, in progress -#define PMNG_BOOST 2 -#define PMNG_NCHRG 3 - -/* product name */ -#define PRDN_MAX 14 - -/* Mains alarm masks */ -#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_M 0x0002 /* shutdown requested */ - -/* Mains alarm indices */ -#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_I 1 /* shutdown requested */ - -/* AC input voltage alarm masks */ -#define VACA_HIALRM_M 0x0001 /* high alarm */ -#define VACA_LOALRM_M 0x0002 /* low alarm */ - -/* AC input voltage alarm indices */ -#define VACA_HIALRM_I 0 /* high alarm */ -#define VACA_LOALRM_I 1 /* low alarm */ - -/* Onboard temperature alarm */ -#define OBTA_HIALRM 1 /* high alarm */ -======= ->>>>>>> ghost alarms bug fix, other bug fixes - -/* Device failure alarm masks */ -#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ -#define DEVF_INALRM_M 0x0006 /* internal failure */ -#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ - -/* Device failure alarm indices */ -#define DEVF_RCALRM_I 0 /* rectifier failure */ -#define DEVF_INALRM_I 1 /* internal failure */ -#define DEVF_LFNAVL_I 2 /* lifetest not available */ - -/* Battery temp sensor failure alarm masks */ -#define BTSF_FCND_M 0x0001 /* connection fault */ -#define BTSF_NCND_M 0x0002 /* not connected */ - -/* Battery temp sensor failure alarm indices */ -#define BTSF_FCND_I 0 /* connection fault */ -#define BTSF_NCND_I 1 /* not connected */ - -/* Battery voltage alarm masks */ -#define BVAL_HIALRM_M 0x0001 /* high voltage */ -#define BVAL_LOALRM_M 0x0002 /* low voltage */ -#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ - -/* Battery voltage alarm indices */ -#define BVAL_HIALRM_I 0 /* high voltage */ -#define BVAL_LOALRM_I 1 /* low voltage */ -#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ - -/* SoH and SoC alarm masks */ -#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ -#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ -#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ -#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ - -/* SoH and SoC alarm indices */ -#define SHSC_HIRESI_I 0 /* high internal resistance */ -#define SHSC_LOCHEF_I 1 /* low charge efficiency */ -#define SHSC_LOEFCP_I 2 /* low effective capacity */ -#define SHSC_LOWSOC_I 3 /* low state of charge */ - -/* Battery status alarm masks */ -#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ -#define BSTA_NOCNND_M 0x0002 /* not connected */ -#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ -#define BSTA_SULPHD_M 0x0008 /* sulphated */ -#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ -#define BSTA_CNNFLT_M 0x0020 /* connection fault */ - -/* Battery status alarm indices */ -#define BSTA_REVPOL_I 0 /* reversed polarity */ -#define BSTA_NOCNND_I 1 /* not connected */ -#define BSTA_CLSHCR_I 2 /* cell short circuit */ -#define BSTA_SULPHD_I 3 /* sulphated */ -#define BSTA_CHEMNS_I 4 /* chemistry not supported */ -#define BSTA_CNNFLT_I 5 /* connection fault */ - -/* input mains and shutdown alarms */ -static alrm_ar_t mains = { - 2, - { - {0, "input voltage not available"}, - {0, "ups shutdown requested"} - } -}; - -/* AC input voltage alarms */ -static alrm_ar_t vaca = { - 2, - { - {0, "input voltage high alarm"}, - {0, "input voltage low alarm"} - } -}; - -/* device failure alarms */ -static alrm_ar_t devf = { - 3, - { - {0, "UPS rectifier failure"}, - {0, "UPS internal failure"}, - {0, "UPS lifetest not available"} - } -}; - -/* battery sensor failure alarms */ -static alrm_ar_t btsf = { - 2, - { - {0, "battery temp sensor connection fault"}, - {0, "battery temp sensor not connected"} - } -}; - -/* battery voltage alarms */ -static alrm_ar_t bval = { - 3, - { - {0, "battery high voltage"}, - {0, "battery low voltage"}, - {0, "battery start with battery flat"} - } -}; - -/* battery SoH and SoC alarms */ -static alrm_ar_t shsc = { - 4, - { - {0, "battery high internal resistance"}, - {0, "battery low charge efficiency"}, - {0, "battery low effective capacity"}, - {0, "battery low state of charge"} - } -}; - -/* battery status alarm */ -static alrm_ar_t bsta = { - 6, - { - {0, "battery reversed polarity"}, - {0, "battery not connected"}, - {0, "battery cell short circuit"}, - {0, "battery sulphated"}, - {0, "battery chemistry not supported"}, - {0, "battery connection fault"} - } -}; - -/* onboard temperature alarm */ -static alrm_ar_t obta = { - 1, - { - {0, "onboard temperature high"} - } -}; - -/* UPS device reg enum */ -enum devreg { - CHRG = 0, /* Charging status, "battery.charger.status" */ - BATV, /* Battery voltage, "battery.voltage" */ - BCEF = 6, /* Battery charge efficiency factor (CEF) */ - BSOH, /* Battery state-of-health */ - BSOC = 9, /* Battery state-of-charge, "battery.charge" */ - BTMP = 11, /* Battery temperature in Kelvin units, "battery.temperature" */ - PMNG = 15, /* Power management, "ups.status" */ - OTMP = 20, /* Onboard temperature, "ups.temperature" */ - PRDN, /* Product name, "ups.model" */ - VAC = 24, /* AC voltage, "input.voltage" */ - LVDC, /* Load voltage, "output.voltage" */ - LCUR, /* Load current, "output.current" */ - BINH = 79, /* Backup inhibit */ - FSD, /* Force shutdown */ - TBUF, /* Time buffering, "battery.runtime" */ - BSTA = 89, /* Battery status alarms */ - SCSH, /* SoH and SoC alarms */ - BVAL, /* Battery voltage alarm */ - BTSF, /* Battery temp sensor failure */ - DEVF, /* Device failure */ - OBTA, /* On board temp alarm */ - VACA, /* VAC alarms */ - MAIN /* Mains status */ -}; -typedef enum devreg devreg_t; - -/* UPS register attributes */ -struct regattr { - int num; - int saddr; /* register start address */ - int xaddr; /* register hex address */ - float scale; /* scale */ - regtype_t type; /* register type */ -}; -typedef struct regattr regattr_t; - -/* UPS device state info union */ -union devstate { - prodname_t product; /* ups model name */ - chrgs_t charge; /* charging status */ - pwrmng_t power; /* ups status */ - reg_t reg; /* state register*/ - alrm_ar_t *alrm; /* alarm statuses */ -}; - -typedef union devstate devstate_t; - -/* ADELE CBI registers */ -<<<<<<< HEAD -regattr_t regs[] = { ->>>>>>> under construction -======= -static regattr_t regs[] = { ->>>>>>> ghost alarms bug fix, other bug fixes - {40005, 0, 0, 1, HOLDING}, /* Charging status */ - {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ - {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ - {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ - {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ - {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ - {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ - {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ - {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ - {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ - {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ - {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ - {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ - {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ - {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ - {40006, 0, 0, 1, HOLDING}, /* Power management -<<<<<<< HEAD -<<<<<<< HEAD - * 0:Backup 1:Charging 2:boost 3:Not charging - */ -======= - * 0:Backup 1:Charging 2:boost 3:Not charging - */ ->>>>>>> under construction -======= - * 0:Backup 1:Charging 2:boost 3:Not charging - */ ->>>>>>> ghost alarms bug fix, other bug fixes - {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ - {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ - {40010, 0, 0, 1, HOLDING}, /* Software ID */ - {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ - {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ - {40067, 0, 0, 1, HOLDING}, /* Product name */ - {40039, 0, 0, 1, HOLDING}, /* Device variant */ - {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ - {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ - {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ - {40020, 0, 0, 1, HOLDING}, /* Output load current */ - {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ - {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ - {40050, 0, 0, .1, HOLDING}, /* Ah charged */ - {40051, 0, 0, 1, HOLDING}, /* Total run time */ - {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ - {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ - {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ - {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ - {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ - {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ - {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ -<<<<<<< HEAD -<<<<<<< HEAD - {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ - {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ - {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ -======= - {40056, 0, 0, 1, HOLDING}, /* Number of overtemperature inside events */ - {40054, 0, 0, 1, HOLDING}, /* Number of low AC input voltage events at mains input */ - {40055, 0, 0, 1, HOLDING}, /* Number of High AC input voltage events at mains input */ ->>>>>>> under construction -======= - {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ - {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ - {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ ->>>>>>> register status values and masks added - {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ - {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ - {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ - {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ - {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ - {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ - {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ - {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ - {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ - {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ - {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ - {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ - {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ - {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ - {40120, 0, 0, 1, HOLDING}, /* Zero-SoC reference */ - {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ - {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ - {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ - {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ - {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ - {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ - {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ - {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ - {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ - {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ - {40082, 0, 0, 1, HOLDING}, /* Float voltage */ - {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ - {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ - {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ - {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ - {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ - {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ - {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ - {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ - {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ - {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ - {40065, 0, 0, 1, HOLDING}, /* History clear all */ - {40066, 0, 0, 1, HOLDING}, /* Factory settings */ -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> register status values and masks added - {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed - * 1 = Backup not allowed 0 - * */ - {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ -<<<<<<< HEAD -======= - {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed 1 = Backup not allowed 0 */ ->>>>>>> under construction -======= ->>>>>>> register status values and masks added - {40104, 0, 0, 1, HOLDING}, /* Time buffering */ - {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ - {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ - {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ - {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ - {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ - {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ - {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ - {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ - {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ - {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ - {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ - {40043, 0, 0, 1, HOLDING}, /* Device failure */ - {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ - {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ -<<<<<<< HEAD -<<<<<<< HEAD - {40046, 0, 0, 1, HOLDING}, /* Mains status */ - {40038, 0, 0, 1, HOLDING} /* Load alarm */ -======= - {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ - {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ - {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ - {40004, 0, 0, 1, HOLDING}, - {40005, 0, 0, 1, HOLDING}, /* Charging status */ - {40006, 0, 0, 1, HOLDING}, /* Power management - * 0:Backup 1:Charging 2:boost 3:Not charging - */ - {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ - {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ - {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ - {40010, 0, 0, 1, HOLDING}, /* Software ID */ - {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ - {40012, 0, 0, 1, HOLDING}, - {40013, 0, 0, 1, HOLDING}, - {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ - {40015, 0, 0, 1, HOLDING}, - {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ - {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ - {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ - {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ - {40020, 0, 0, 1, HOLDING}, /* Output load current */ - {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ - {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ - {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ - {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ - {40025, 0, 0, 1, HOLDING}, - {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ - {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ - {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ - {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ - {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ - {40031, 0, 0, 1, HOLDING}, - {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ - {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ - {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ - {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ - {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ - {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ - {40038, 0, 0, 1, HOLDING}, /* Load alarm */ - {40039, 0, 0, 1, HOLDING}, /* Device variant */ - {40040, 0, 0, 1, HOLDING}, - {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ - {40042, 0, 0, 1, HOLDING}, - {40043, 0, 0, 1, HOLDING}, /* Device failure */ - {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ - {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ - {40046, 0, 0, 1, HOLDING}, /* Mains status */ - {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ - {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ - {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ - {40050, 0, 0, .1, HOLDING}, /* Ah charged */ - {40051, 0, 0, 1, HOLDING}, /* Total run time */ - {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ - {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ - {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ - {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ - {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ - {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ - {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ - {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ - {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ - {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ - {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ - {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ - {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ - {40065, 0, 0, 1, HOLDING}, /* History clear all */ - {40066, 0, 0, 1, HOLDING}, /* Factory settings */ - {40067, 0, 0, 1, HOLDING}, /* Product name */ - {40068, 0, 0, 1, HOLDING}, - {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ - {40070, 0, 0, 1, HOLDING}, - {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ - {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ - {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ - {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ - {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ - {40076, 0, 0, 1, HOLDING}, - {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ - {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ - {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ - {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ - {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ - {40082, 0, 0, 1, HOLDING}, /* Float voltage */ - {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ - {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ - {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ - {40086, 0, 0, 1, HOLDING}, - {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ - {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed - * 1 = Backup not allowed - */ - {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ - {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ - {40091, 0, 0, 1, HOLDING}, - {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ - {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ - {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ - {40095, 0, 0, 1, HOLDING}, - {40096, 0, 0, 1, HOLDING}, - {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ - {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ - {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ - {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ - {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ - {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ - {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ - {40104, 0, 0, 1, HOLDING}, /* Time buffering */ - {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ - {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ - {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ - {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ - {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ - {40110, 0, 0, 1, HOLDING}, - {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ - {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ - {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ - {40114, 0, 0, 1, HOLDING}, - {40115, 0, 0, 1, HOLDING}, - {40116, 0, 0, 1, HOLDING}, - {40117, 0, 0, 1, HOLDING}, - {40118, 0, 0, 1, HOLDING}, - {40119, 0, 0, 1, HOLDING}, - {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ - - ->>>>>>> modify regs_data memory, devreg enum rearrangement -}; -======= - {40046, 0, 0, 1, HOLDING}, /* Input mains on / backup */ -======= - {40046, 0, 0, 1, HOLDING}, /* Mains status */ ->>>>>>> register status values and masks added - {40038, 0, 0, 1, HOLDING} /* Load alarm */ -}; -<<<<<<< HEAD - -#define NUMOF_REGS 14 -#define NOTUSED -1 - ->>>>>>> under construction -======= ->>>>>>> structure device data, code get_dev_state, in progress -#endif /* ADELE_CBI_H */ diff --git a/drivers/adelsystems_cbi.c b/drivers/adelsystems_cbi.c deleted file mode 100644 index 8bb283e1f8..0000000000 --- a/drivers/adelsystems_cbi.c +++ /dev/null @@ -1,1201 +0,0 @@ -/* adele_cbi.c - driver for ADELE CB/CBI DC-UPS - * - * Copyright (C) - * 2022 Dimitris Economou - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "main.h" -#include "adelsystems_cbi.h" -#include -#include - -#define DRIVER_NAME "NUT Adele DC-UPS CB/CBI driver" -#define DRIVER_VERSION "0.01" - -/* variables */ -static modbus_t *mbctx = NULL; /* modbus memory context */ -static devstate_t *dstate = NULL; /* device state context */ -static int errcnt = 0; /* modbus access error counter */ -static char *device_mfr = DEVICE_MFR; /* device manufacturer */ -static char *device_model = DEVICE_MODEL; /* device model */ -static char *device_type = DEVICE_TYPE; /* device model */ -static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ -static char ser_parity = PARITY; /* serial port parity */ -static int ser_data_bit = DATA_BIT; /* serial port data bit */ -static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ -static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ -static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ -static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ -static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ -static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ - - -/* initialize register start address and hex address from register number */ -void reginit(); - -/* read all registers */ -int read_all_regs(modbus_t *mb, uint16_t *data); - -/* get config vars set by -x or defined in ups.conf driver section */ -void get_config_vars(void); - -/* get device state */ -int get_dev_state(devreg_t regindx, devstate_t **dstate); - -/* create a new modbus context based on connection type (serial or TCP) */ -modbus_t *modbus_new(const char *port); - -/* reconnect upon communication error */ -void modbus_reconnect(); - -/* modbus register read function */ -int register_read(modbus_t *mb, int addr, regtype_t type, void *data); - -/* modbus register write function */ -int register_write(modbus_t *mb, int addr, regtype_t type, void *data); - -/* instant command triggered by upsd */ -int upscmd(const char *cmd, const char *arg); - -/* count the time elapsed since start */ -long time_elapsed(struct timeval *start); - - -/* driver description structure */ -upsdrv_info_t upsdrv_info = { - DRIVER_NAME, - DRIVER_VERSION, - "Dimitris Economou \n", - DRV_BETA, - {NULL} -}; - -/* - * driver functions - */ - -/* read configuration variables from ups.conf and connect to ups device */ -void upsdrv_initups(void) -{ - int rval; - upsdebugx(2, "upsdrv_initups"); - - - dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); - reginit(); - get_config_vars(); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response time out to 200ms */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -} - -/* initialize ups driver information */ -void upsdrv_initinfo(void) -{ - devstate_t *ds = dstate; /* device state context */ - upsdebugx(2, "upsdrv_initinfo"); - - /* set device information */ - dstate_setinfo("device.mfr", "%s", device_mfr); - dstate_setinfo("device.model", "%s", device_model); - dstate_setinfo("device.type", "%s", device_type); - - /* read ups model */ - get_dev_state(PRDN, &ds); - dstate_setinfo("ups.model", "%s", ds->product.name); - upslogx(LOG_INFO, "ups.model = %s", ds->product.name); - - /* register instant commands */ - dstate_addcmd("load.off"); - - /* set callback for instant commands */ - upsh.instcmd = upscmd; -} - - -/* update UPS signal state */ -void upsdrv_updateinfo(void) -{ - int rval; /* return value */ - int i; /* local index */ - devstate_t *ds = dstate; /* device state */ - - upsdebugx(2, "upsdrv_updateinfo"); - - errcnt = 0; /* initialize error counter to zero */ - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ -#if READALL_REGS == 1 - rval = read_all_regs(mbctx, regs_data); - if (rval == -1) { - errcnt++; - } else { -#endif - /* - * update UPS status regarding MAINS and SHUTDOWN request - * - OL: On line (mains is present) - * - OB: On battery (mains is not present) - */ - rval = get_dev_state(MAIN, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { - status_set("OB"); - alarm_set(mains.alrm[MAINS_AVAIL_I].descr); - upslogx(LOG_INFO, "ups.status = OB"); - } else { - status_set("OL"); - upslogx(LOG_INFO, "ups.status = OL"); - } - if (ds->alrm->alrm[SHUTD_REQST_I].actv) { - status_set("FSD"); - alarm_set(mains.alrm[SHUTD_REQST_I].descr); - upslogx(LOG_INFO, "ups.status = FSD"); - } - } - - /* - * update UPS status regarding battery voltage - */ - rval = get_dev_state(BVAL, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { - status_set("LB"); - alarm_set(bval.alrm[BVAL_LOALRM_I].descr); - upslogx(LOG_INFO, "ups.status = LB"); - } - if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { - status_set("HB"); - alarm_set(bval.alrm[BVAL_HIALRM_I].descr); - upslogx(LOG_INFO, "ups.status = HB"); - } - if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { - alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); - upslogx(LOG_INFO, "battery start with battery flat"); - } - } - - /* get "battery.voltage" */ - rval = get_dev_state(BATV, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); - } - /* - * update UPS status regarding battery charger status - */ - - /* get "battery.charger.status" */ - rval = get_dev_state(CHRG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { - status_set("CHRG"); - upslogx(LOG_INFO, "ups.status = CHRG"); - } - dstate_setinfo("battery.charger.status", "%s", ds->charge.info); - upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); - } - rval = get_dev_state(PMNG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->power.state == PMNG_BCKUP) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - upslogx(LOG_INFO, "ups.status = DISCHRG"); - } - if (ds->power.state == PMNG_BOOST) { - status_set("BOOST"); - upslogx(LOG_INFO, "ups.status = BOOST"); - } - } - - /* - * update UPS battery state of charge - */ - rval = get_dev_state(BSOC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.charge", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); - } - - /* - * update UPS AC input state - */ - rval = get_dev_state(VACA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - rval = get_dev_state(VAC, &ds); - dstate_setinfo("input.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); - } - - /* - * update UPS onboard temperature state - */ - rval = get_dev_state(OBTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(OTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("ups.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); - } - /* - * update UPS battery temperature state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(BTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); - } - rval = get_dev_state(TBUF, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.runtime", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); - } - - /* - * update UPS device failure state - */ - rval = get_dev_state(DEVF, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS SoH and SoC states - */ - rval = get_dev_state(SCSH, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS battery state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS load status - */ - rval = get_dev_state(LVDC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); - } - rval = get_dev_state(LCUR, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.current", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); - } -#if READALL_REGS == 1 - } -#endif - /* check for communication errors */ - if (errcnt == 0) { - alarm_commit(); - status_commit(); - dstate_dataok(); - } else { - upsdebugx(2, "Communication errors: %d", errcnt); - dstate_datastale(); - } -} - -/* shutdown UPS */ -void upsdrv_shutdown(void) -{ - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); - case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); -} - -/* print driver usage info */ -void upsdrv_help(void) -{ -} - -/* list flags and values that you want to receive via -x */ -void upsdrv_makevartable(void) -{ - addvar(VAR_VALUE, "device_mfr", "device manufacturer"); - addvar(VAR_VALUE, "device_model", "device model"); - addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); - addvar(VAR_VALUE, "ser_parity", "serial port parity"); - addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); - addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); - addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); - addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); - addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); - addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); - addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); -} - -/* close modbus connection and free modbus context allocated memory */ -void upsdrv_cleanup(void) -{ - if (mbctx != NULL) { - modbus_close(mbctx); - modbus_free(mbctx); - } - if (dstate != NULL) { - free(dstate); - } -} - -/* - * driver support functions - */ - -/* initialize register start address and hex address from register number */ -void reginit() -{ - int i; /* local index */ - - for (i = 0; i < MODBUS_NUMOF_REGS; i++) { - int rnum = regs[i].num; - switch (regs[i].type) { - case COIL: - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x0 + regs[i].num - 1; - break; - case INPUT_B: - rnum -= 10000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x10000 + rnum - 1; - break; - case INPUT_R: - rnum -= 30000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x30000 + rnum - 1; - break; - case HOLDING: - rnum -= 40000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x40000 + rnum - 1; - break; - default: - upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); - upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); - } - upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", - regs[i].num, - regs[i].type, - regs[i].saddr, - regs[i].xaddr - ); - } -} - -/* read all register data image */ -int read_all_regs(modbus_t *mb, uint16_t *data) -{ - int rval; - - /* read all HOLDING registers */ - rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, regs_data); - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", - modbus_strerror(errno), - regs[H_REG_STARTIDX].xaddr, - MAX_H_REGS, - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - - /* no COIL, INPUT_B or INPUT_R register regions to read */ - - return rval; -} - -/* Read a modbus register */ -int register_read(modbus_t *mb, int addr, regtype_t type, void *data) -{ - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x00FF; - uint mask16 = 0xFFFF; - - switch (type) { - case COIL: - rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_B: - rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_R: - rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - case HOLDING: - rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; -} - -/* write a modbus register */ -int register_write(modbus_t *mb, int addr, regtype_t type, void *data) -{ - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x00FF; - uint mask16 = 0xFFFF; - - switch (type) { - case COIL: - *(uint *)data = *(uint *)data & mask8; - rval = modbus_write_bit(mb, addr, *(uint8_t *)data); - break; - case HOLDING: - *(uint *)data = *(uint *)data & mask16; - rval = modbus_write_register(mb, addr, *(uint16_t *)data); - break; - - case INPUT_B: - case INPUT_R: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; -} - -/* returns the time elapsed since start in milliseconds */ -long time_elapsed(struct timeval *start) -{ - long rval; - struct timeval end; - - rval = gettimeofday(&end, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); - } - if (start->tv_usec < end.tv_usec) { - suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; - end.tv_usec -= 1000000 * nsec; - end.tv_sec += nsec; - } - if (start->tv_usec - end.tv_usec > 1000000) { - suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; - end.tv_usec += 1000000 * nsec; - end.tv_sec -= nsec; - } - rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; - - return rval; -} - -/* instant command triggered by upsd */ -int upscmd(const char *cmd, const char *arg) -{ - int rval; - int data; - - if (!strcasecmp(cmd, "load.off")) { - data = 1; - rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); - if (rval == -1) { - upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - regs[FSD].xaddr, - regs[FSD].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); - rval = STAT_INSTCMD_HANDLED; - } - } else { - upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_UNKNOWN; - } - return rval; -} - -/* read device state, returns 0 on success or -1 on communication error - it formats state depending on register semantics */ -int get_dev_state(devreg_t regindx, devstate_t **dstate) -{ - int i; /* local index */ - int n; - int rval; /* return value */ - static char *ptr = NULL; /* temporary pointer */ - uint reg_val; /* register value */ -#if READALL_REGS == 0 - uint num; /* register number */ - regtype_t rtype; /* register type */ - int addr; /* register address */ -#endif - devstate_t *state; /* device state */ - - state = *dstate; -#if READALL_REGS == 1 - reg_val = regs_data[regindx]; - rval = 0; -#elif READALL_REGS == 0 - num = regs[regindx].num; - addr = regs[regindx].xaddr; - rtype = regs[regindx].type; - rval = register_read(mbctx, addr, rtype, ®_val); - if (rval == -1) { - return rval; - } - upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", - num, - addr, - rtype, - reg_val - ); -#endif - /* process register data */ - switch (regindx) { - case CHRG: /* "ups.charge" */ - if (reg_val == CHRG_NONE) { - state->charge.state = CHRG_NONE; - state->charge.info = chrgs_i[CHRG_NONE]; - } else if (reg_val == CHRG_RECV) { - state->charge.state = CHRG_RECV; - state->charge.info = chrgs_i[CHRG_RECV]; - } else if (reg_val == CHRG_BULK) { - state->charge.state = CHRG_BULK; - state->charge.info = chrgs_i[CHRG_BULK]; - } else if (reg_val == CHRG_ABSR) { - state->charge.state = CHRG_ABSR; - state->charge.info = chrgs_i[CHRG_ABSR]; - } else if (reg_val == CHRG_FLOAT) { - state->charge.state = CHRG_FLOAT; - state->charge.info = chrgs_i[CHRG_FLOAT]; - } - upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); - break; - case BATV: /* "battery.voltage" */ - case LVDC: /* "output.voltage" */ - case LCUR: /* "output.current" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case TBUF: - case BSOH: - case BCEF: - case VAC: /* "input.voltage" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BSOC: /* "battery.charge" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - double fval = (double )reg_val * regs[BSOC].scale; - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BTMP: /* "battery.temperature" */ - case OTMP: /* "ups.temperature" */ - state->reg.val16 = reg_val; - double fval = reg_val - 273.15; - n = snprintf(NULL, 0, "%.2f", fval); - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - if (ptr != NULL) { - free(ptr); - } - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case PMNG: /* "ups.status" & "battery.charge" */ - if (reg_val == PMNG_BCKUP) { - state->power.state = PMNG_BCKUP; - state->power.info = pwrmng_i[PMNG_BCKUP]; - } else if (reg_val == PMNG_CHRGN) { - state->power.state = PMNG_CHRGN; - state->power.info = pwrmng_i[PMNG_CHRGN]; - } else if (reg_val == PMNG_BOOST) { - state->power.state = PMNG_BOOST; - state->power.info = pwrmng_i[PMNG_BOOST]; - } else if (reg_val == PMNG_NCHRG) { - state->power.state = PMNG_NCHRG; - state->power.info = pwrmng_i[PMNG_NCHRG]; - } - upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); - break; - case PRDN: /* "ups.model" */ - for (i = 0; i < DEV_NUMOF_MODELS; i++) { - if (prdnm_i[i].val == reg_val) { - break; - } - } - state->product.val = reg_val; - state->product.name = prdnm_i[i].name; - upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); - break; - case BSTA: - if (reg_val & BSTA_REVPOL_M) { - bsta.alrm[BSTA_REVPOL_I].actv = 1; - } else { - bsta.alrm[BSTA_REVPOL_I].actv = 0; - } - if (reg_val & BSTA_NOCNND_M) { - bsta.alrm[BSTA_NOCNND_I].actv = 1; - } else { - bsta.alrm[BSTA_NOCNND_I].actv = 0; - } - if (reg_val & BSTA_CLSHCR_M) { - bsta.alrm[BSTA_CLSHCR_I].actv = 1; - } else { - bsta.alrm[BSTA_CLSHCR_I].actv = 0; - } - if (reg_val & BSTA_SULPHD_M) { - bsta.alrm[BSTA_SULPHD_I].actv = 1; - } else { - bsta.alrm[BSTA_SULPHD_I].actv = 0; - } - if (reg_val & BSTA_CHEMNS_M) { - bsta.alrm[BSTA_CHEMNS_I].actv = 1; - } else { - bsta.alrm[BSTA_CHEMNS_I].actv = 0; - } - if (reg_val & BSTA_CNNFLT_M) { - bsta.alrm[BSTA_CNNFLT_I].actv = 1; - } else { - bsta.alrm[BSTA_CNNFLT_I].actv = 0; - } - state->alrm = &bsta; - break; - case SCSH: - if (reg_val & SHSC_HIRESI_M) { - shsc.alrm[SHSC_HIRESI_I].actv = 1; - } else { - shsc.alrm[SHSC_HIRESI_I].actv = 0; - } - if (reg_val & SHSC_LOCHEF_M) { - shsc.alrm[SHSC_LOCHEF_I].actv = 1; - } else { - shsc.alrm[SHSC_LOCHEF_I].actv = 0; - } - if (reg_val & SHSC_LOEFCP_M) { - shsc.alrm[SHSC_LOEFCP_I].actv = 1; - } else { - shsc.alrm[SHSC_LOEFCP_I].actv = 0; - } - if (reg_val & SHSC_LOWSOC_M) { - shsc.alrm[SHSC_LOWSOC_I].actv = 1; - } else { - shsc.alrm[SHSC_LOWSOC_I].actv = 0; - } - state->alrm = &shsc; - break; - case BVAL: - if (reg_val & BVAL_HIALRM_M) { - bval.alrm[BVAL_HIALRM_I].actv = 1; - } else { - bval.alrm[BVAL_HIALRM_I].actv = 0; - } - if (reg_val & BVAL_LOALRM_M) { - bval.alrm[BVAL_LOALRM_I].actv = 1; - } else { - bval.alrm[BVAL_LOALRM_I].actv = 0; - } - if (reg_val & BVAL_BSTSFL_M) { - bval.alrm[BVAL_BSTSFL_I].actv = 1; - } else { - bval.alrm[BVAL_BSTSFL_I].actv = 0; - } - state->alrm = &bval; - break; - case BTSF: - if (reg_val & BTSF_FCND_M) { - btsf.alrm[BTSF_FCND_I].actv = 1; - } else { - btsf.alrm[BTSF_FCND_I].actv = 0; - } - if (reg_val & BTSF_NCND_M) { - btsf.alrm[BTSF_NCND_I].actv = 1; - } else { - btsf.alrm[BTSF_NCND_I].actv = 0; - } - state->alrm = &btsf; - break; - case DEVF: - if (reg_val & DEVF_RCALRM_M) { - devf.alrm[DEVF_RCALRM_I].actv = 1; - } else { - devf.alrm[DEVF_RCALRM_I].actv = 0; - } - if (reg_val & DEVF_INALRM_M) { - devf.alrm[DEVF_INALRM_I].actv = 1; - } else { - devf.alrm[DEVF_INALRM_I].actv = 0; - } - if (reg_val & DEVF_LFNAVL_M) { - devf.alrm[DEVF_LFNAVL_I].actv = 1; - } else { - devf.alrm[DEVF_LFNAVL_I].actv = 0; - } - state->alrm = &devf; - break; - case VACA: - if (reg_val & VACA_HIALRM_M) { - vaca.alrm[VACA_HIALRM_I].actv = 1; - } else { - vaca.alrm[VACA_HIALRM_I].actv = 0; - } - if (reg_val == VACA_LOALRM_M) { - vaca.alrm[VACA_LOALRM_I].actv = 1; - } else { - vaca.alrm[VACA_LOALRM_I].actv = 0; - } - state->alrm = &vaca; - break; - case MAIN: - if (reg_val & MAINS_AVAIL_M) { - mains.alrm[MAINS_AVAIL_I].actv = 1; - } else { - mains.alrm[MAINS_AVAIL_I].actv = 0; - } - if (reg_val == SHUTD_REQST_M) { - mains.alrm[SHUTD_REQST_I].actv = 1; - } else { - mains.alrm[SHUTD_REQST_I].actv = 0; - } - state->alrm = &mains; - break; - case OBTA: - if (reg_val == OBTA_HIALRM_V) { - obta.alrm[OBTA_HIALRM_I].actv = 1; - } - state->alrm = &obta; - break; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - state->reg.val16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - break; - } - - return rval; -} - -/* get driver configuration parameters */ -void get_config_vars() -{ - /* check if device manufacturer is set ang get the value */ - if (testvar("device_mfr")) { - device_mfr = getval("device_mfr"); - } - upsdebugx(2, "device_mfr %s", device_mfr); - - /* check if device model is set ang get the value */ - if (testvar("device_model")) { - device_model = getval("device_model"); - } - upsdebugx(2, "device_model %s", device_model); - - /* check if serial baud rate is set ang get the value */ - if (testvar("ser_baud_rate")) { - ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); - } - upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); - - /* check if serial parity is set ang get the value */ - if (testvar("ser_parity")) { - /* Dereference the char* we get */ - char *sp = getval("ser_parity"); - if (sp) { - /* TODO? Sanity-check the char we get? */ - ser_parity = *sp; - } else { - upsdebugx(2, "Could not determine ser_parity, will keep default"); - } - } - upsdebugx(2, "ser_parity %c", ser_parity); - - /* check if serial data bit is set ang get the value */ - if (testvar("ser_data_bit")) { - ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); - } - upsdebugx(2, "ser_data_bit %d", ser_data_bit); - - /* check if serial stop bit is set ang get the value */ - if (testvar("ser_stop_bit")) { - ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); - } - upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); - - /* check if device ID is set ang get the value */ - if (testvar("rio_slave_id")) { - rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); - } - upsdebugx(2, "rio_slave_id %d", rio_slave_id); - - /* check if response time out (s) is set ang get the value */ - if (testvar("mod_resp_to_s")) { - mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); - } - upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); - - /* check if response time out (us) is set ang get the value */ - if (testvar("mod_resp_to_us")) { - mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); - if (mod_resp_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); - } - } - upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); - - /* check if byte time out (s) is set ang get the value */ - if (testvar("mod_byte_to_s")) { - mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); - } - upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); - - /* check if byte time out (us) is set ang get the value */ - if (testvar("mod_byte_to_us")) { - mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); - if (mod_byte_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); - } - } - upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); -} - -/* create a new modbus context based on connection type (serial or TCP) */ -modbus_t *modbus_new(const char *port) -{ - modbus_t *mb; - char *sp; - if (strstr(port, "/dev/tty") != NULL) { - mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); - } - } else if ((sp = strchr(port, ':')) != NULL) { - char *tcp_port = xmalloc(sizeof(sp)); - strcpy(tcp_port, sp + 1); - *sp = '\0'; - mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - free(tcp_port); - } else { - mb = modbus_new_tcp(port, 502); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - } - return mb; -} - -/* reconnect to modbus server upon connection error */ -void modbus_reconnect() -{ - int rval; - - upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); - - /* clear current modbus context */ - modbus_close(mbctx); - modbus_free(mbctx); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -} diff --git a/drivers/adelsystems_cbi.h b/drivers/adelsystems_cbi.h deleted file mode 100644 index b27c2f1e4a..0000000000 --- a/drivers/adelsystems_cbi.h +++ /dev/null @@ -1,510 +0,0 @@ -/* adelsystems_cbi.h - Driver for ADELSYSTEMS CB/CBI DC-UPS - * - * Copyright (C) - * 2022 Dimitris Economou - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef ADELSYSTEMS_CBI_H -#define ADELSYSTEMS_CBI_H - -#include - -/* UPS device details */ -#define DEVICE_MFR "ADELSYSTEMS" -#define DEVICE_TYPE "DC-UPS" -#define DEVICE_MODEL "CB/CBI" - -/* serial access parameters */ -#define BAUD_RATE 9600 -#define PARITY 'N' -#define DATA_BIT 8 -#define STOP_BIT 1 - -/* - * modbus response and byte timeouts - * us: 1 - 999999 - */ -#define MODRESP_TIMEOUT_s 0 -#define MODRESP_TIMEOUT_us 200000 -#define MODBYTE_TIMEOUT_s 0 -#define MODBYTE_TIMEOUT_us 50000 - -/* modbus access parameters */ -#define MODBUS_SLAVE_ID 5 - -/* number of modbus registers */ -#define MODBUS_NUMOF_REGS 98 - -/* max HOLDING registers */ -#define MAX_H_REGS 120 - -/* start HOLDING register index */ -#define H_REG_STARTIDX 0 - -/* read all regs preprocessor flag */ -#define READALL_REGS 1 - -/* number of device models */ -#define DEV_NUMOF_MODELS 10 - -/* shutdown repeat on error */ -#define FSD_REPEAT_CNT 3 - -/* shutdown repeat interval in ms */ -#define FSD_REPEAT_INTRV 1500 - -/* definition of register type */ -enum regtype { - COIL = 0, - INPUT_B, - INPUT_R, - HOLDING -}; -typedef enum regtype regtype_t; - -/* product name info, "device.model" */ -struct prodname { - uint16_t val; - char *name; -}; -typedef struct prodname prodname_t; -static prodname_t prdnm_i[] = { - {1, "CBI1235A"}, - {2, "CBI2420A"}, - {3, "CBI4810A"}, - {4, "CBI2801224"}, - {7, "CBI480W"}, - {8, "CB122410A"}, - {9, "CB480W"}, - {11, "CB12245AJ"}, - {12, "CB1235A"}, - {13, "CB2420A"}, - {14, "CB4810A"} -}; - -/* charging status info, "battery.charger.status" */ -static char *chrgs_i[] = { - "none", - "resting", /* recovering */ - "charging", /* bulk */ - "charging", /* absorb */ - "floating" /* float */ -}; -struct chrgs { - int state; - char *info; -}; -typedef struct chrgs chrgs_t; - -/* power management info, "ups.status", "battery.charger.status" */ -static char *pwrmng_i[] = { - "backup", /* "OB", "discharging" */ - "charging", /* "OL" */ - "boost", - "not charging" -}; -struct pwrmng { - int state; - char *info; -}; -typedef struct pwrmng pwrmng_t; - -/* general modbus register value */ -struct reg { - union { - uint16_t val16; - uint8_t val8; - }; - char *strval; -}; -typedef struct reg reg_t; - -/* general alarm struct */ -struct alrm { - int actv; /* active flag */ - char *descr; /* description field */ -}; -typedef struct alrm alrm_t; - -/* general alarm array */ -struct alrm_ar { - int alrm_c; /* alarm count */ - alrm_t alrm[]; /* alarm array */ -}; -typedef struct alrm_ar alrm_ar_t; - -/* - * BIT MASKS and VALUES - */ - -/* Charging status */ -#define CHRG_NONE 0 -#define CHRG_RECV 1 -#define CHRG_BULK 2 -#define CHRG_ABSR 3 -#define CHRG_FLOAT 4 - -/* power management */ -#define PMNG_BCKUP 0 -#define PMNG_CHRGN 1 -#define PMNG_BOOST 2 -#define PMNG_NCHRG 3 - -/* product name */ -#define PRDN_MAX 14 - -/* Mains alarm masks */ -#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_M 0x0002 /* shutdown requested */ - -/* Mains alarm indices */ -#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_I 1 /* shutdown requested */ - -/* AC input voltage alarm masks */ -#define VACA_HIALRM_M 0x0001 /* high alarm */ -#define VACA_LOALRM_M 0x0002 /* low alarm */ - -/* AC input voltage alarm indices */ -#define VACA_HIALRM_I 0 /* high alarm */ -#define VACA_LOALRM_I 1 /* low alarm */ - -/* Onboard temperature alarm value */ -#define OBTA_HIALRM_V 1 /* high alarm */ - -/* Onboard temperature alarm index */ -#define OBTA_HIALRM_I 0 /* high alarm */ - -/* Device failure alarm masks */ -#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ -#define DEVF_INALRM_M 0x0006 /* internal failure */ -#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ - -/* Device failure alarm indices */ -#define DEVF_RCALRM_I 0 /* rectifier failure */ -#define DEVF_INALRM_I 1 /* internal failure */ -#define DEVF_LFNAVL_I 2 /* lifetest not available */ - -/* Battery temp sensor failure alarm masks */ -#define BTSF_FCND_M 0x0001 /* connection fault */ -#define BTSF_NCND_M 0x0002 /* not connected */ - -/* Battery temp sensor failure alarm indices */ -#define BTSF_FCND_I 0 /* connection fault */ -#define BTSF_NCND_I 1 /* not connected */ - -/* Battery voltage alarm masks */ -#define BVAL_HIALRM_M 0x0001 /* high voltage */ -#define BVAL_LOALRM_M 0x0002 /* low voltage */ -#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ - -/* Battery voltage alarm indices */ -#define BVAL_HIALRM_I 0 /* high voltage */ -#define BVAL_LOALRM_I 1 /* low voltage */ -#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ - -/* SoH and SoC alarm masks */ -#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ -#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ -#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ -#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ - -/* SoH and SoC alarm indices */ -#define SHSC_HIRESI_I 0 /* high internal resistance */ -#define SHSC_LOCHEF_I 1 /* low charge efficiency */ -#define SHSC_LOEFCP_I 2 /* low effective capacity */ -#define SHSC_LOWSOC_I 3 /* low state of charge */ - -/* Battery status alarm masks */ -#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ -#define BSTA_NOCNND_M 0x0002 /* not connected */ -#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ -#define BSTA_SULPHD_M 0x0008 /* sulphated */ -#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ -#define BSTA_CNNFLT_M 0x0020 /* connection fault */ - -/* Battery status alarm indices */ -#define BSTA_REVPOL_I 0 /* reversed polarity */ -#define BSTA_NOCNND_I 1 /* not connected */ -#define BSTA_CLSHCR_I 2 /* cell short circuit */ -#define BSTA_SULPHD_I 3 /* sulphated */ -#define BSTA_CHEMNS_I 4 /* chemistry not supported */ -#define BSTA_CNNFLT_I 5 /* connection fault */ - -/* input mains and shutdown alarms */ -static alrm_ar_t mains = { - 2, - { - {0, "input voltage not available"}, - {0, "ups shutdown requested"} - } -}; - -/* AC input voltage alarms */ -static alrm_ar_t vaca = { - 2, - { - {0, "input voltage high alarm"}, - {0, "input voltage low alarm"} - } -}; - -/* device failure alarms */ -static alrm_ar_t devf = { - 3, - { - {0, "UPS rectifier failure"}, - {0, "UPS internal failure"}, - {0, "UPS lifetest not available"} - } -}; - -/* battery sensor failure alarms */ -static alrm_ar_t btsf = { - 2, - { - {0, "battery temp sensor connection fault"}, - {0, "battery temp sensor not connected"} - } -}; - -/* battery voltage alarms */ -static alrm_ar_t bval = { - 3, - { - {0, "battery high voltage"}, - {0, "battery low voltage"}, - {0, "battery start with battery flat"} - } -}; - -/* battery SoH and SoC alarms */ -static alrm_ar_t shsc = { - 4, - { - {0, "battery high internal resistance"}, - {0, "battery low charge efficiency"}, - {0, "battery low effective capacity"}, - {0, "battery low state of charge"} - } -}; - -/* battery status alarm */ -static alrm_ar_t bsta = { - 6, - { - {0, "battery reversed polarity"}, - {0, "battery not connected"}, - {0, "battery cell short circuit"}, - {0, "battery sulphated"}, - {0, "battery chemistry not supported"}, - {0, "battery connection fault"} - } -}; - -/* onboard temperature alarm */ -static alrm_ar_t obta = { - 1, - { - {0, "onboard temperature high"} - } -}; - -/* UPS device reg enum */ -enum devreg { - CHRG = 4, /* Charging status, "battery.charger.status" */ - BATV = 7, /* Battery voltage, "battery.voltage" */ - BCEF = 18, /* Battery charge efficiency factor (CEF) */ - BSOH = 20, /* Battery state-of-health */ - BSOC = 22, /* Battery state-of-charge, "battery.charge" */ - BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ - PMNG = 5, /* Power management, "ups.status" */ - OTMP = 28, /* Onboard temperature, "ups.temperature" */ - PRDN = 67, /* Product name, "ups.model" */ - VAC = 29, /* AC voltage, "input.voltage" */ - LVDC = 10, /* Load voltage, "output.voltage" */ - LCUR = 19, /* Load current, "output.current" */ - BINH = 87, /* Backup inhibit */ - FSD = 40, /* Force shutdown */ - TBUF = 103, /* Time buffering, "battery.runtime" */ - BSTA = 31, /* Battery status alarms */ - SCSH, /* SoH and SoC alarms */ - BVAL = 34, /* Battery voltage alarm */ - BTSF = 43, /* Battery temp sensor failure */ - DEVF = 42, /* Device failure */ - OBTA = 46, /* On board temp alarm */ - VACA = 44, /* VAC alarms */ - MAIN /* Mains status */ -}; -typedef enum devreg devreg_t; - -/* UPS register attributes */ -struct regattr { - int num; - int saddr; /* register start address */ - int xaddr; /* register hex address */ - float scale; /* scale */ - regtype_t type; /* register type */ -}; -typedef struct regattr regattr_t; - -/* UPS device state info union */ -union devstate { - prodname_t product; /* ups model name */ - chrgs_t charge; /* charging status */ - pwrmng_t power; /* ups status */ - reg_t reg; /* state register*/ - alrm_ar_t *alrm; /* alarm statuses */ -}; - -typedef union devstate devstate_t; - -/* device register memory image */ -static uint16_t regs_data[MAX_H_REGS]; - -/* ADELSYSTEMS CBI registers */ -static regattr_t regs[] = { - {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ - {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ - {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ - {40004, 0, 0, 1, HOLDING}, - {40005, 0, 0, 1, HOLDING}, /* Charging status */ - {40006, 0, 0, 1, HOLDING}, /* Power management - * 0:Backup 1:Charging 2:boost 3:Not charging - */ - {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ - {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ - {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ - {40010, 0, 0, 1, HOLDING}, /* Software ID */ - {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ - {40012, 0, 0, 1, HOLDING}, - {40013, 0, 0, 1, HOLDING}, - {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ - {40015, 0, 0, 1, HOLDING}, - {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ - {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ - {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ - {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ - {40020, 0, 0, 1, HOLDING}, /* Output load current */ - {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ - {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ - {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ - {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ - {40025, 0, 0, 1, HOLDING}, - {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ - {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ - {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ - {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ - {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ - {40031, 0, 0, 1, HOLDING}, - {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ - {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ - {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ - {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ - {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ - {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ - {40038, 0, 0, 1, HOLDING}, /* Load alarm */ - {40039, 0, 0, 1, HOLDING}, /* Device variant */ - {40040, 0, 0, 1, HOLDING}, - {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ - {40042, 0, 0, 1, HOLDING}, - {40043, 0, 0, 1, HOLDING}, /* Device failure */ - {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ - {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ - {40046, 0, 0, 1, HOLDING}, /* Mains status */ - {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ - {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ - {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ - {40050, 0, 0, .1, HOLDING}, /* Ah charged */ - {40051, 0, 0, 1, HOLDING}, /* Total run time */ - {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ - {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ - {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ - {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ - {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ - {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ - {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ - {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ - {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ - {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ - {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ - {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ - {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ - {40065, 0, 0, 1, HOLDING}, /* History clear all */ - {40066, 0, 0, 1, HOLDING}, /* Factory settings */ - {40067, 0, 0, 1, HOLDING}, /* Product name */ - {40068, 0, 0, 1, HOLDING}, - {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ - {40070, 0, 0, 1, HOLDING}, - {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ - {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ - {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ - {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ - {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ - {40076, 0, 0, 1, HOLDING}, - {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ - {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ - {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ - {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ - {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ - {40082, 0, 0, 1, HOLDING}, /* Float voltage */ - {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ - {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ - {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ - {40086, 0, 0, 1, HOLDING}, - {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ - {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed - * 1 = Backup not allowed - */ - {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ - {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ - {40091, 0, 0, 1, HOLDING}, - {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ - {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ - {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ - {40095, 0, 0, 1, HOLDING}, - {40096, 0, 0, 1, HOLDING}, - {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ - {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ - {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ - {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ - {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ - {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ - {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ - {40104, 0, 0, 1, HOLDING}, /* Time buffering */ - {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ - {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ - {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ - {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ - {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ - {40110, 0, 0, 1, HOLDING}, - {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ - {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ - {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ - {40114, 0, 0, 1, HOLDING}, - {40115, 0, 0, 1, HOLDING}, - {40116, 0, 0, 1, HOLDING}, - {40117, 0, 0, 1, HOLDING}, - {40118, 0, 0, 1, HOLDING}, - {40119, 0, 0, 1, HOLDING}, - {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ - - -}; -#endif /* ADELSYSTEMS_CBI_H */ From 747759e0e8afe746484efb0878ab985032541604 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 20:45:20 +0200 Subject: [PATCH 068/700] try to reconnect on INVALID DATA and INVALID CRC errors --- drivers/adelsystem_cbi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index 7b4804a0d1..63ae248d31 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -606,8 +606,8 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) device_path ); - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { + /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ + if (errno == EPIPE || errno == EMBBADDATA || errno == EMBBADCRC) { upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); modbus_reconnect(); } From 5961f8fdadd948624054eeba7903839b1bcacff0 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 13 Jan 2022 21:15:18 +0200 Subject: [PATCH 069/700] try to reconnect on IVALID DATA and INVALID CRC from read_all_regs --- drivers/adelsystem_cbi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index 63ae248d31..409b59bddd 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -542,8 +542,8 @@ int read_all_regs(modbus_t *mb, uint16_t *data) device_path ); - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { + /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ + if (errno == EPIPE || errno == EMBBADDATA || errno == EMBBADCRC) { upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); modbus_reconnect(); } From 961152b824b430098f77736afe3ecdb32dbcec04 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Tue, 18 Jan 2022 16:01:10 +0200 Subject: [PATCH 070/700] rename for rebase --- drivers/{adelsystem_cbi.c => adele_cbi.c} | 0 drivers/{adelsystem_cbi.h => adele_cbi.h} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename drivers/{adelsystem_cbi.c => adele_cbi.c} (100%) rename drivers/{adelsystem_cbi.h => adele_cbi.h} (100%) diff --git a/drivers/adelsystem_cbi.c b/drivers/adele_cbi.c similarity index 100% rename from drivers/adelsystem_cbi.c rename to drivers/adele_cbi.c diff --git a/drivers/adelsystem_cbi.h b/drivers/adele_cbi.h similarity index 100% rename from drivers/adelsystem_cbi.h rename to drivers/adele_cbi.h From d8ab1aac4dad278b17818fa6cc21245eaced9502 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Tue, 18 Jan 2022 16:31:54 +0200 Subject: [PATCH 071/700] rename after rebase --- drivers/{adele_cbi.c => adelsystem_cbi.c} | 0 drivers/{adele_cbi.h => adelsystem_cbi.h} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename drivers/{adele_cbi.c => adelsystem_cbi.c} (100%) rename drivers/{adele_cbi.h => adelsystem_cbi.h} (100%) diff --git a/drivers/adele_cbi.c b/drivers/adelsystem_cbi.c similarity index 100% rename from drivers/adele_cbi.c rename to drivers/adelsystem_cbi.c diff --git a/drivers/adele_cbi.h b/drivers/adelsystem_cbi.h similarity index 100% rename from drivers/adele_cbi.h rename to drivers/adelsystem_cbi.h From 0da79d6ea32515fb753e94825bd97b2730408311 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Tue, 18 Jan 2022 16:37:47 +0200 Subject: [PATCH 072/700] delete duplicates after rebase --- drivers/adele_cbi.c | 1201 ------------------------------------------- drivers/adele_cbi.h | 510 ------------------ 2 files changed, 1711 deletions(-) delete mode 100644 drivers/adele_cbi.c delete mode 100644 drivers/adele_cbi.h diff --git a/drivers/adele_cbi.c b/drivers/adele_cbi.c deleted file mode 100644 index 409b59bddd..0000000000 --- a/drivers/adele_cbi.c +++ /dev/null @@ -1,1201 +0,0 @@ -/* adelsystem_cbi.c - driver for ADELSYSTEM CB/CBI DC-UPS - * - * Copyright (C) - * 2022 Dimitris Economou - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "main.h" -#include "adelsystem_cbi.h" -#include -#include - -#define DRIVER_NAME "NUT ADELSYSTEM DC-UPS CB/CBI driver" -#define DRIVER_VERSION "0.01" - -/* variables */ -static modbus_t *mbctx = NULL; /* modbus memory context */ -static devstate_t *dstate = NULL; /* device state context */ -static int errcnt = 0; /* modbus access error counter */ -static char *device_mfr = DEVICE_MFR; /* device manufacturer */ -static char *device_model = DEVICE_MODEL; /* device model */ -static char *device_type = DEVICE_TYPE; /* device model */ -static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ -static char ser_parity = PARITY; /* serial port parity */ -static int ser_data_bit = DATA_BIT; /* serial port data bit */ -static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ -static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ -static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ -static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ -static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ -static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ - - -/* initialize register start address and hex address from register number */ -void reginit(); - -/* read registers' memory region */ -int read_all_regs(modbus_t *mb, uint16_t *data); - -/* get config vars set by -x or defined in ups.conf driver section */ -void get_config_vars(void); - -/* get device state */ -int get_dev_state(devreg_t regindx, devstate_t **dstate); - -/* create a new modbus context based on connection type (serial or TCP) */ -modbus_t *modbus_new(const char *port); - -/* reconnect upon communication error */ -void modbus_reconnect(); - -/* modbus register read function */ -int register_read(modbus_t *mb, int addr, regtype_t type, void *data); - -/* modbus register write function */ -int register_write(modbus_t *mb, int addr, regtype_t type, void *data); - -/* instant command triggered by upsd */ -int upscmd(const char *cmd, const char *arg); - -/* count the time elapsed since start */ -long time_elapsed(struct timeval *start); - - -/* driver description structure */ -upsdrv_info_t upsdrv_info = { - DRIVER_NAME, - DRIVER_VERSION, - "Dimitris Economou \n", - DRV_BETA, - {NULL} -}; - -/* - * driver functions - */ - -/* read configuration variables from ups.conf and connect to ups device */ -void upsdrv_initups(void) -{ - int rval; - upsdebugx(2, "upsdrv_initups"); - - - dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); - reginit(); - get_config_vars(); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response time out to 200ms */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -} - -/* initialize ups driver information */ -void upsdrv_initinfo(void) -{ - devstate_t *ds = dstate; /* device state context */ - upsdebugx(2, "upsdrv_initinfo"); - - /* set device information */ - dstate_setinfo("device.mfr", "%s", device_mfr); - dstate_setinfo("device.model", "%s", device_model); - dstate_setinfo("device.type", "%s", device_type); - - /* read ups model */ - get_dev_state(PRDN, &ds); - dstate_setinfo("ups.model", "%s", ds->product.name); - upslogx(LOG_INFO, "ups.model = %s", ds->product.name); - - /* register instant commands */ - dstate_addcmd("load.off"); - - /* set callback for instant commands */ - upsh.instcmd = upscmd; -} - - -/* update UPS signal state */ -void upsdrv_updateinfo(void) -{ - int rval; /* return value */ - int i; /* local index */ - devstate_t *ds = dstate; /* device state */ - - upsdebugx(2, "upsdrv_updateinfo"); - - errcnt = 0; /* initialize error counter to zero */ - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ -#if READALL_REGS == 1 - rval = read_all_regs(mbctx, regs_data); - if (rval == -1) { - errcnt++; - } else { -#endif - /* - * update UPS status regarding MAINS and SHUTDOWN request - * - OL: On line (mains is present) - * - OB: On battery (mains is not present) - */ - rval = get_dev_state(MAIN, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { - status_set("OB"); - alarm_set(mains.alrm[MAINS_AVAIL_I].descr); - upslogx(LOG_INFO, "ups.status = OB"); - } else { - status_set("OL"); - upslogx(LOG_INFO, "ups.status = OL"); - } - if (ds->alrm->alrm[SHUTD_REQST_I].actv) { - status_set("FSD"); - alarm_set(mains.alrm[SHUTD_REQST_I].descr); - upslogx(LOG_INFO, "ups.status = FSD"); - } - } - - /* - * update UPS status regarding battery voltage - */ - rval = get_dev_state(BVAL, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { - status_set("LB"); - alarm_set(bval.alrm[BVAL_LOALRM_I].descr); - upslogx(LOG_INFO, "ups.status = LB"); - } - if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { - status_set("HB"); - alarm_set(bval.alrm[BVAL_HIALRM_I].descr); - upslogx(LOG_INFO, "ups.status = HB"); - } - if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { - alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); - upslogx(LOG_INFO, "battery start with battery flat"); - } - } - - /* get "battery.voltage" */ - rval = get_dev_state(BATV, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); - } - /* - * update UPS status regarding battery charger status - */ - - /* get "battery.charger.status" */ - rval = get_dev_state(CHRG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { - status_set("CHRG"); - upslogx(LOG_INFO, "ups.status = CHRG"); - } - dstate_setinfo("battery.charger.status", "%s", ds->charge.info); - upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); - } - rval = get_dev_state(PMNG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->power.state == PMNG_BCKUP) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - upslogx(LOG_INFO, "ups.status = DISCHRG"); - } - if (ds->power.state == PMNG_BOOST) { - status_set("BOOST"); - upslogx(LOG_INFO, "ups.status = BOOST"); - } - } - - /* - * update UPS battery state of charge - */ - rval = get_dev_state(BSOC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.charge", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); - } - - /* - * update UPS AC input state - */ - rval = get_dev_state(VACA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - rval = get_dev_state(VAC, &ds); - dstate_setinfo("input.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); - } - - /* - * update UPS onboard temperature state - */ - rval = get_dev_state(OBTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(OTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("ups.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); - } - /* - * update UPS battery temperature state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(BTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); - } - rval = get_dev_state(TBUF, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.runtime", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); - } - - /* - * update UPS device failure state - */ - rval = get_dev_state(DEVF, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS SoH and SoC states - */ - rval = get_dev_state(SCSH, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS battery state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS load status - */ - rval = get_dev_state(LVDC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); - } - rval = get_dev_state(LCUR, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.current", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); - } -#if READALL_REGS == 1 - } -#endif - /* check for communication errors */ - if (errcnt == 0) { - alarm_commit(); - status_commit(); - dstate_dataok(); - } else { - upsdebugx(2, "Communication errors: %d", errcnt); - dstate_datastale(); - } -} - -/* shutdown UPS */ -void upsdrv_shutdown(void) -{ - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); - case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); -} - -/* print driver usage info */ -void upsdrv_help(void) -{ -} - -/* list flags and values that you want to receive via -x */ -void upsdrv_makevartable(void) -{ - addvar(VAR_VALUE, "device_mfr", "device manufacturer"); - addvar(VAR_VALUE, "device_model", "device model"); - addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); - addvar(VAR_VALUE, "ser_parity", "serial port parity"); - addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); - addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); - addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); - addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); - addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); - addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); - addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); -} - -/* close modbus connection and free modbus context allocated memory */ -void upsdrv_cleanup(void) -{ - if (mbctx != NULL) { - modbus_close(mbctx); - modbus_free(mbctx); - } - if (dstate != NULL) { - free(dstate); - } -} - -/* - * driver support functions - */ - -/* initialize register start address and hex address from register number */ -void reginit() -{ - int i; /* local index */ - - for (i = 0; i < MODBUS_NUMOF_REGS; i++) { - int rnum = regs[i].num; - switch (regs[i].type) { - case COIL: - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x0 + regs[i].num - 1; - break; - case INPUT_B: - rnum -= 10000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x10000 + rnum - 1; - break; - case INPUT_R: - rnum -= 30000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x30000 + rnum - 1; - break; - case HOLDING: - rnum -= 40000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x40000 + rnum - 1; - break; - default: - upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); - upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); - } - upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", - regs[i].num, - regs[i].type, - regs[i].saddr, - regs[i].xaddr - ); - } -} - -/* read registers' memory region */ -int read_all_regs(modbus_t *mb, uint16_t *data) -{ - int rval; - - /* read all HOLDING registers */ - rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, regs_data); - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", - modbus_strerror(errno), - regs[H_REG_STARTIDX].xaddr, - MAX_H_REGS, - device_path - ); - - /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ - if (errno == EPIPE || errno == EMBBADDATA || errno == EMBBADCRC) { - upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - - /* no COIL, INPUT_B or INPUT_R register regions to read */ - - return rval; -} - -/* Read a modbus register */ -int register_read(modbus_t *mb, int addr, regtype_t type, void *data) -{ - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x00FF; - uint mask16 = 0xFFFF; - - switch (type) { - case COIL: - rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_B: - rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_R: - rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - case HOLDING: - rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ - if (errno == EPIPE || errno == EMBBADDATA || errno == EMBBADCRC) { - upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; -} - -/* write a modbus register */ -int register_write(modbus_t *mb, int addr, regtype_t type, void *data) -{ - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x00FF; - uint mask16 = 0xFFFF; - - switch (type) { - case COIL: - *(uint *)data = *(uint *)data & mask8; - rval = modbus_write_bit(mb, addr, *(uint8_t *)data); - break; - case HOLDING: - *(uint *)data = *(uint *)data & mask16; - rval = modbus_write_register(mb, addr, *(uint16_t *)data); - break; - - case INPUT_B: - case INPUT_R: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; -} - -/* returns the time elapsed since start in milliseconds */ -long time_elapsed(struct timeval *start) -{ - long rval; - struct timeval end; - - rval = gettimeofday(&end, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); - } - if (start->tv_usec < end.tv_usec) { - suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; - end.tv_usec -= 1000000 * nsec; - end.tv_sec += nsec; - } - if (start->tv_usec - end.tv_usec > 1000000) { - suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; - end.tv_usec += 1000000 * nsec; - end.tv_sec -= nsec; - } - rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; - - return rval; -} - -/* instant command triggered by upsd */ -int upscmd(const char *cmd, const char *arg) -{ - int rval; - int data; - - if (!strcasecmp(cmd, "load.off")) { - data = 1; - rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); - if (rval == -1) { - upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - regs[FSD].xaddr, - regs[FSD].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); - rval = STAT_INSTCMD_HANDLED; - } - } else { - upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_UNKNOWN; - } - return rval; -} - -/* read device state, returns 0 on success or -1 on communication error - it formats state depending on register semantics */ -int get_dev_state(devreg_t regindx, devstate_t **dstate) -{ - int i; /* local index */ - int n; - int rval; /* return value */ - static char *ptr = NULL; /* temporary pointer */ - uint reg_val; /* register value */ -#if READALL_REGS == 0 - uint num; /* register number */ - regtype_t rtype; /* register type */ - int addr; /* register address */ -#endif - devstate_t *state; /* device state */ - - state = *dstate; -#if READALL_REGS == 1 - reg_val = regs_data[regindx]; - rval = 0; -#elif READALL_REGS == 0 - num = regs[regindx].num; - addr = regs[regindx].xaddr; - rtype = regs[regindx].type; - rval = register_read(mbctx, addr, rtype, ®_val); - if (rval == -1) { - return rval; - } - upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", - num, - addr, - rtype, - reg_val - ); -#endif - /* process register data */ - switch (regindx) { - case CHRG: /* "ups.charge" */ - if (reg_val == CHRG_NONE) { - state->charge.state = CHRG_NONE; - state->charge.info = chrgs_i[CHRG_NONE]; - } else if (reg_val == CHRG_RECV) { - state->charge.state = CHRG_RECV; - state->charge.info = chrgs_i[CHRG_RECV]; - } else if (reg_val == CHRG_BULK) { - state->charge.state = CHRG_BULK; - state->charge.info = chrgs_i[CHRG_BULK]; - } else if (reg_val == CHRG_ABSR) { - state->charge.state = CHRG_ABSR; - state->charge.info = chrgs_i[CHRG_ABSR]; - } else if (reg_val == CHRG_FLOAT) { - state->charge.state = CHRG_FLOAT; - state->charge.info = chrgs_i[CHRG_FLOAT]; - } - upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); - break; - case BATV: /* "battery.voltage" */ - case LVDC: /* "output.voltage" */ - case LCUR: /* "output.current" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case TBUF: - case BSOH: - case BCEF: - case VAC: /* "input.voltage" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BSOC: /* "battery.charge" */ - if (reg_val != 0) { - state->reg.val16 = reg_val; - double fval = (double )reg_val * regs[BSOC].scale; - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BTMP: /* "battery.temperature" */ - case OTMP: /* "ups.temperature" */ - state->reg.val16 = reg_val; - double fval = reg_val - 273.15; - n = snprintf(NULL, 0, "%.2f", fval); - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - if (ptr != NULL) { - free(ptr); - } - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case PMNG: /* "ups.status" & "battery.charge" */ - if (reg_val == PMNG_BCKUP) { - state->power.state = PMNG_BCKUP; - state->power.info = pwrmng_i[PMNG_BCKUP]; - } else if (reg_val == PMNG_CHRGN) { - state->power.state = PMNG_CHRGN; - state->power.info = pwrmng_i[PMNG_CHRGN]; - } else if (reg_val == PMNG_BOOST) { - state->power.state = PMNG_BOOST; - state->power.info = pwrmng_i[PMNG_BOOST]; - } else if (reg_val == PMNG_NCHRG) { - state->power.state = PMNG_NCHRG; - state->power.info = pwrmng_i[PMNG_NCHRG]; - } - upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); - break; - case PRDN: /* "ups.model" */ - for (i = 0; i < DEV_NUMOF_MODELS; i++) { - if (prdnm_i[i].val == reg_val) { - break; - } - } - state->product.val = reg_val; - state->product.name = prdnm_i[i].name; - upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); - break; - case BSTA: - if (reg_val & BSTA_REVPOL_M) { - bsta.alrm[BSTA_REVPOL_I].actv = 1; - } else { - bsta.alrm[BSTA_REVPOL_I].actv = 0; - } - if (reg_val & BSTA_NOCNND_M) { - bsta.alrm[BSTA_NOCNND_I].actv = 1; - } else { - bsta.alrm[BSTA_NOCNND_I].actv = 0; - } - if (reg_val & BSTA_CLSHCR_M) { - bsta.alrm[BSTA_CLSHCR_I].actv = 1; - } else { - bsta.alrm[BSTA_CLSHCR_I].actv = 0; - } - if (reg_val & BSTA_SULPHD_M) { - bsta.alrm[BSTA_SULPHD_I].actv = 1; - } else { - bsta.alrm[BSTA_SULPHD_I].actv = 0; - } - if (reg_val & BSTA_CHEMNS_M) { - bsta.alrm[BSTA_CHEMNS_I].actv = 1; - } else { - bsta.alrm[BSTA_CHEMNS_I].actv = 0; - } - if (reg_val & BSTA_CNNFLT_M) { - bsta.alrm[BSTA_CNNFLT_I].actv = 1; - } else { - bsta.alrm[BSTA_CNNFLT_I].actv = 0; - } - state->alrm = &bsta; - break; - case SCSH: - if (reg_val & SHSC_HIRESI_M) { - shsc.alrm[SHSC_HIRESI_I].actv = 1; - } else { - shsc.alrm[SHSC_HIRESI_I].actv = 0; - } - if (reg_val & SHSC_LOCHEF_M) { - shsc.alrm[SHSC_LOCHEF_I].actv = 1; - } else { - shsc.alrm[SHSC_LOCHEF_I].actv = 0; - } - if (reg_val & SHSC_LOEFCP_M) { - shsc.alrm[SHSC_LOEFCP_I].actv = 1; - } else { - shsc.alrm[SHSC_LOEFCP_I].actv = 0; - } - if (reg_val & SHSC_LOWSOC_M) { - shsc.alrm[SHSC_LOWSOC_I].actv = 1; - } else { - shsc.alrm[SHSC_LOWSOC_I].actv = 0; - } - state->alrm = &shsc; - break; - case BVAL: - if (reg_val & BVAL_HIALRM_M) { - bval.alrm[BVAL_HIALRM_I].actv = 1; - } else { - bval.alrm[BVAL_HIALRM_I].actv = 0; - } - if (reg_val & BVAL_LOALRM_M) { - bval.alrm[BVAL_LOALRM_I].actv = 1; - } else { - bval.alrm[BVAL_LOALRM_I].actv = 0; - } - if (reg_val & BVAL_BSTSFL_M) { - bval.alrm[BVAL_BSTSFL_I].actv = 1; - } else { - bval.alrm[BVAL_BSTSFL_I].actv = 0; - } - state->alrm = &bval; - break; - case BTSF: - if (reg_val & BTSF_FCND_M) { - btsf.alrm[BTSF_FCND_I].actv = 1; - } else { - btsf.alrm[BTSF_FCND_I].actv = 0; - } - if (reg_val & BTSF_NCND_M) { - btsf.alrm[BTSF_NCND_I].actv = 1; - } else { - btsf.alrm[BTSF_NCND_I].actv = 0; - } - state->alrm = &btsf; - break; - case DEVF: - if (reg_val & DEVF_RCALRM_M) { - devf.alrm[DEVF_RCALRM_I].actv = 1; - } else { - devf.alrm[DEVF_RCALRM_I].actv = 0; - } - if (reg_val & DEVF_INALRM_M) { - devf.alrm[DEVF_INALRM_I].actv = 1; - } else { - devf.alrm[DEVF_INALRM_I].actv = 0; - } - if (reg_val & DEVF_LFNAVL_M) { - devf.alrm[DEVF_LFNAVL_I].actv = 1; - } else { - devf.alrm[DEVF_LFNAVL_I].actv = 0; - } - state->alrm = &devf; - break; - case VACA: - if (reg_val & VACA_HIALRM_M) { - vaca.alrm[VACA_HIALRM_I].actv = 1; - } else { - vaca.alrm[VACA_HIALRM_I].actv = 0; - } - if (reg_val == VACA_LOALRM_M) { - vaca.alrm[VACA_LOALRM_I].actv = 1; - } else { - vaca.alrm[VACA_LOALRM_I].actv = 0; - } - state->alrm = &vaca; - break; - case MAIN: - if (reg_val & MAINS_AVAIL_M) { - mains.alrm[MAINS_AVAIL_I].actv = 1; - } else { - mains.alrm[MAINS_AVAIL_I].actv = 0; - } - if (reg_val == SHUTD_REQST_M) { - mains.alrm[SHUTD_REQST_I].actv = 1; - } else { - mains.alrm[SHUTD_REQST_I].actv = 0; - } - state->alrm = &mains; - break; - case OBTA: - if (reg_val == OBTA_HIALRM_V) { - obta.alrm[OBTA_HIALRM_I].actv = 1; - } - state->alrm = &obta; - break; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcovered-switch-default" -#endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif - state->reg.val16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - break; - } - - return rval; -} - -/* get driver configuration parameters */ -void get_config_vars() -{ - /* check if device manufacturer is set ang get the value */ - if (testvar("device_mfr")) { - device_mfr = getval("device_mfr"); - } - upsdebugx(2, "device_mfr %s", device_mfr); - - /* check if device model is set ang get the value */ - if (testvar("device_model")) { - device_model = getval("device_model"); - } - upsdebugx(2, "device_model %s", device_model); - - /* check if serial baud rate is set ang get the value */ - if (testvar("ser_baud_rate")) { - ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); - } - upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); - - /* check if serial parity is set ang get the value */ - if (testvar("ser_parity")) { - /* Dereference the char* we get */ - char *sp = getval("ser_parity"); - if (sp) { - /* TODO? Sanity-check the char we get? */ - ser_parity = *sp; - } else { - upsdebugx(2, "Could not determine ser_parity, will keep default"); - } - } - upsdebugx(2, "ser_parity %c", ser_parity); - - /* check if serial data bit is set ang get the value */ - if (testvar("ser_data_bit")) { - ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); - } - upsdebugx(2, "ser_data_bit %d", ser_data_bit); - - /* check if serial stop bit is set ang get the value */ - if (testvar("ser_stop_bit")) { - ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); - } - upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); - - /* check if device ID is set ang get the value */ - if (testvar("rio_slave_id")) { - rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); - } - upsdebugx(2, "rio_slave_id %d", rio_slave_id); - - /* check if response time out (s) is set ang get the value */ - if (testvar("mod_resp_to_s")) { - mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); - } - upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); - - /* check if response time out (us) is set ang get the value */ - if (testvar("mod_resp_to_us")) { - mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); - if (mod_resp_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); - } - } - upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); - - /* check if byte time out (s) is set ang get the value */ - if (testvar("mod_byte_to_s")) { - mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); - } - upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); - - /* check if byte time out (us) is set ang get the value */ - if (testvar("mod_byte_to_us")) { - mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); - if (mod_byte_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); - } - } - upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); -} - -/* create a new modbus context based on connection type (serial or TCP) */ -modbus_t *modbus_new(const char *port) -{ - modbus_t *mb; - char *sp; - if (strstr(port, "/dev/tty") != NULL) { - mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); - } - } else if ((sp = strchr(port, ':')) != NULL) { - char *tcp_port = xmalloc(sizeof(sp)); - strcpy(tcp_port, sp + 1); - *sp = '\0'; - mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - free(tcp_port); - } else { - mb = modbus_new_tcp(port, 502); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - } - return mb; -} - -/* reconnect to modbus server upon connection error */ -void modbus_reconnect() -{ - int rval; - - upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); - - /* clear current modbus context */ - modbus_close(mbctx); - modbus_free(mbctx); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } -} diff --git a/drivers/adele_cbi.h b/drivers/adele_cbi.h deleted file mode 100644 index b3500a7afc..0000000000 --- a/drivers/adele_cbi.h +++ /dev/null @@ -1,510 +0,0 @@ -/* adelsystem_cbi.h - Driver for ADELSYSTEM CB/CBI DC-UPS - * - * Copyright (C) - * 2022 Dimitris Economou - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef ADELSYSTEM_CBI_H -#define ADELSYSTEM_CBI_H - -#include - -/* UPS device details */ -#define DEVICE_MFR "ADELSYSTEM" -#define DEVICE_TYPE "DC-UPS" -#define DEVICE_MODEL "CB/CBI" - -/* serial access parameters */ -#define BAUD_RATE 9600 -#define PARITY 'N' -#define DATA_BIT 8 -#define STOP_BIT 1 - -/* - * modbus response and byte timeouts - * us: 1 - 999999 - */ -#define MODRESP_TIMEOUT_s 0 -#define MODRESP_TIMEOUT_us 200000 -#define MODBYTE_TIMEOUT_s 0 -#define MODBYTE_TIMEOUT_us 50000 - -/* modbus access parameters */ -#define MODBUS_SLAVE_ID 5 - -/* number of modbus registers */ -#define MODBUS_NUMOF_REGS 98 - -/* max HOLDING registers */ -#define MAX_H_REGS 120 - -/* start HOLDING register index */ -#define H_REG_STARTIDX 0 - -/* read all regs preprocessor flag */ -#define READALL_REGS 1 - -/* number of device models */ -#define DEV_NUMOF_MODELS 10 - -/* shutdown repeat on error */ -#define FSD_REPEAT_CNT 3 - -/* shutdown repeat interval in ms */ -#define FSD_REPEAT_INTRV 1500 - -/* definition of register type */ -enum regtype { - COIL = 0, - INPUT_B, - INPUT_R, - HOLDING -}; -typedef enum regtype regtype_t; - -/* product name info, "device.model" */ -struct prodname { - uint16_t val; - char *name; -}; -typedef struct prodname prodname_t; -static prodname_t prdnm_i[] = { - {1, "CBI1235A"}, - {2, "CBI2420A"}, - {3, "CBI4810A"}, - {4, "CBI2801224"}, - {7, "CBI480W"}, - {8, "CB122410A"}, - {9, "CB480W"}, - {11, "CB12245AJ"}, - {12, "CB1235A"}, - {13, "CB2420A"}, - {14, "CB4810A"} -}; - -/* charging status info, "battery.charger.status" */ -static char *chrgs_i[] = { - "none", - "resting", /* recovering */ - "charging", /* bulk */ - "charging", /* absorb */ - "floating" /* float */ -}; -struct chrgs { - int state; - char *info; -}; -typedef struct chrgs chrgs_t; - -/* power management info, "ups.status", "battery.charger.status" */ -static char *pwrmng_i[] = { - "backup", /* "OB", "discharging" */ - "charging", /* "OL" */ - "boost", - "not charging" -}; -struct pwrmng { - int state; - char *info; -}; -typedef struct pwrmng pwrmng_t; - -/* general modbus register value */ -struct reg { - union { - uint16_t val16; - uint8_t val8; - }; - char *strval; -}; -typedef struct reg reg_t; - -/* general alarm struct */ -struct alrm { - int actv; /* active flag */ - char *descr; /* description field */ -}; -typedef struct alrm alrm_t; - -/* general alarm array */ -struct alrm_ar { - int alrm_c; /* alarm count */ - alrm_t alrm[]; /* alarm array */ -}; -typedef struct alrm_ar alrm_ar_t; - -/* - * BIT MASKS and VALUES - */ - -/* Charging status */ -#define CHRG_NONE 0 -#define CHRG_RECV 1 -#define CHRG_BULK 2 -#define CHRG_ABSR 3 -#define CHRG_FLOAT 4 - -/* power management */ -#define PMNG_BCKUP 0 -#define PMNG_CHRGN 1 -#define PMNG_BOOST 2 -#define PMNG_NCHRG 3 - -/* product name */ -#define PRDN_MAX 14 - -/* Mains alarm masks */ -#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_M 0x0002 /* shutdown requested */ - -/* Mains alarm indices */ -#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_I 1 /* shutdown requested */ - -/* AC input voltage alarm masks */ -#define VACA_HIALRM_M 0x0001 /* high alarm */ -#define VACA_LOALRM_M 0x0002 /* low alarm */ - -/* AC input voltage alarm indices */ -#define VACA_HIALRM_I 0 /* high alarm */ -#define VACA_LOALRM_I 1 /* low alarm */ - -/* Onboard temperature alarm value */ -#define OBTA_HIALRM_V 1 /* high alarm */ - -/* Onboard temperature alarm index */ -#define OBTA_HIALRM_I 0 /* high alarm */ - -/* Device failure alarm masks */ -#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ -#define DEVF_INALRM_M 0x0006 /* internal failure */ -#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ - -/* Device failure alarm indices */ -#define DEVF_RCALRM_I 0 /* rectifier failure */ -#define DEVF_INALRM_I 1 /* internal failure */ -#define DEVF_LFNAVL_I 2 /* lifetest not available */ - -/* Battery temp sensor failure alarm masks */ -#define BTSF_FCND_M 0x0001 /* connection fault */ -#define BTSF_NCND_M 0x0002 /* not connected */ - -/* Battery temp sensor failure alarm indices */ -#define BTSF_FCND_I 0 /* connection fault */ -#define BTSF_NCND_I 1 /* not connected */ - -/* Battery voltage alarm masks */ -#define BVAL_HIALRM_M 0x0001 /* high voltage */ -#define BVAL_LOALRM_M 0x0002 /* low voltage */ -#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ - -/* Battery voltage alarm indices */ -#define BVAL_HIALRM_I 0 /* high voltage */ -#define BVAL_LOALRM_I 1 /* low voltage */ -#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ - -/* SoH and SoC alarm masks */ -#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ -#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ -#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ -#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ - -/* SoH and SoC alarm indices */ -#define SHSC_HIRESI_I 0 /* high internal resistance */ -#define SHSC_LOCHEF_I 1 /* low charge efficiency */ -#define SHSC_LOEFCP_I 2 /* low effective capacity */ -#define SHSC_LOWSOC_I 3 /* low state of charge */ - -/* Battery status alarm masks */ -#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ -#define BSTA_NOCNND_M 0x0002 /* not connected */ -#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ -#define BSTA_SULPHD_M 0x0008 /* sulphated */ -#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ -#define BSTA_CNNFLT_M 0x0020 /* connection fault */ - -/* Battery status alarm indices */ -#define BSTA_REVPOL_I 0 /* reversed polarity */ -#define BSTA_NOCNND_I 1 /* not connected */ -#define BSTA_CLSHCR_I 2 /* cell short circuit */ -#define BSTA_SULPHD_I 3 /* sulphated */ -#define BSTA_CHEMNS_I 4 /* chemistry not supported */ -#define BSTA_CNNFLT_I 5 /* connection fault */ - -/* input mains and shutdown alarms */ -static alrm_ar_t mains = { - 2, - { - {0, "input voltage not available"}, - {0, "ups shutdown requested"} - } -}; - -/* AC input voltage alarms */ -static alrm_ar_t vaca = { - 2, - { - {0, "input voltage high alarm"}, - {0, "input voltage low alarm"} - } -}; - -/* device failure alarms */ -static alrm_ar_t devf = { - 3, - { - {0, "UPS rectifier failure"}, - {0, "UPS internal failure"}, - {0, "UPS lifetest not available"} - } -}; - -/* battery sensor failure alarms */ -static alrm_ar_t btsf = { - 2, - { - {0, "battery temp sensor connection fault"}, - {0, "battery temp sensor not connected"} - } -}; - -/* battery voltage alarms */ -static alrm_ar_t bval = { - 3, - { - {0, "battery high voltage"}, - {0, "battery low voltage"}, - {0, "battery start with battery flat"} - } -}; - -/* battery SoH and SoC alarms */ -static alrm_ar_t shsc = { - 4, - { - {0, "battery high internal resistance"}, - {0, "battery low charge efficiency"}, - {0, "battery low effective capacity"}, - {0, "battery low state of charge"} - } -}; - -/* battery status alarm */ -static alrm_ar_t bsta = { - 6, - { - {0, "battery reversed polarity"}, - {0, "battery not connected"}, - {0, "battery cell short circuit"}, - {0, "battery sulphated"}, - {0, "battery chemistry not supported"}, - {0, "battery connection fault"} - } -}; - -/* onboard temperature alarm */ -static alrm_ar_t obta = { - 1, - { - {0, "onboard temperature high"} - } -}; - -/* UPS device reg enum */ -enum devreg { - CHRG = 4, /* Charging status, "battery.charger.status" */ - BATV = 7, /* Battery voltage, "battery.voltage" */ - BCEF = 18, /* Battery charge efficiency factor (CEF) */ - BSOH = 20, /* Battery state-of-health */ - BSOC = 22, /* Battery state-of-charge, "battery.charge" */ - BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ - PMNG = 5, /* Power management, "ups.status" */ - OTMP = 28, /* Onboard temperature, "ups.temperature" */ - PRDN = 67, /* Product name, "ups.model" */ - VAC = 29, /* AC voltage, "input.voltage" */ - LVDC = 10, /* Load voltage, "output.voltage" */ - LCUR = 19, /* Load current, "output.current" */ - BINH = 87, /* Backup inhibit */ - FSD = 40, /* Force shutdown */ - TBUF = 103, /* Time buffering, "battery.runtime" */ - BSTA = 31, /* Battery status alarms */ - SCSH, /* SoH and SoC alarms */ - BVAL = 34, /* Battery voltage alarm */ - BTSF = 43, /* Battery temp sensor failure */ - DEVF = 42, /* Device failure */ - OBTA = 46, /* On board temp alarm */ - VACA = 44, /* VAC alarms */ - MAIN /* Mains status */ -}; -typedef enum devreg devreg_t; - -/* UPS register attributes */ -struct regattr { - int num; - int saddr; /* register start address */ - int xaddr; /* register hex address */ - float scale; /* scale */ - regtype_t type; /* register type */ -}; -typedef struct regattr regattr_t; - -/* UPS device state info union */ -union devstate { - prodname_t product; /* ups model name */ - chrgs_t charge; /* charging status */ - pwrmng_t power; /* ups status */ - reg_t reg; /* state register*/ - alrm_ar_t *alrm; /* alarm statuses */ -}; - -typedef union devstate devstate_t; - -/* device register memory image */ -static uint16_t regs_data[MAX_H_REGS]; - -/* ADELSYSTEM CBI registers */ -static regattr_t regs[] = { - {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ - {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ - {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ - {40004, 0, 0, 1, HOLDING}, - {40005, 0, 0, 1, HOLDING}, /* Charging status */ - {40006, 0, 0, 1, HOLDING}, /* Power management - * 0:Backup 1:Charging 2:boost 3:Not charging - */ - {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ - {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ - {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ - {40010, 0, 0, 1, HOLDING}, /* Software ID */ - {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ - {40012, 0, 0, 1, HOLDING}, - {40013, 0, 0, 1, HOLDING}, - {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ - {40015, 0, 0, 1, HOLDING}, - {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ - {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ - {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ - {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ - {40020, 0, 0, 1, HOLDING}, /* Output load current */ - {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ - {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ - {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ - {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ - {40025, 0, 0, 1, HOLDING}, - {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ - {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ - {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ - {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ - {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ - {40031, 0, 0, 1, HOLDING}, - {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ - {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ - {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ - {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ - {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ - {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ - {40038, 0, 0, 1, HOLDING}, /* Load alarm */ - {40039, 0, 0, 1, HOLDING}, /* Device variant */ - {40040, 0, 0, 1, HOLDING}, - {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ - {40042, 0, 0, 1, HOLDING}, - {40043, 0, 0, 1, HOLDING}, /* Device failure */ - {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ - {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ - {40046, 0, 0, 1, HOLDING}, /* Mains status */ - {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ - {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ - {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ - {40050, 0, 0, .1, HOLDING}, /* Ah charged */ - {40051, 0, 0, 1, HOLDING}, /* Total run time */ - {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ - {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ - {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ - {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ - {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ - {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ - {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ - {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ - {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ - {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ - {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ - {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ - {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ - {40065, 0, 0, 1, HOLDING}, /* History clear all */ - {40066, 0, 0, 1, HOLDING}, /* Factory settings */ - {40067, 0, 0, 1, HOLDING}, /* Product name */ - {40068, 0, 0, 1, HOLDING}, - {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ - {40070, 0, 0, 1, HOLDING}, - {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ - {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ - {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ - {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ - {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ - {40076, 0, 0, 1, HOLDING}, - {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ - {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ - {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ - {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ - {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ - {40082, 0, 0, 1, HOLDING}, /* Float voltage */ - {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ - {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ - {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ - {40086, 0, 0, 1, HOLDING}, - {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ - {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed - * 1 = Backup not allowed - */ - {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ - {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ - {40091, 0, 0, 1, HOLDING}, - {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ - {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ - {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ - {40095, 0, 0, 1, HOLDING}, - {40096, 0, 0, 1, HOLDING}, - {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ - {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ - {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ - {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ - {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ - {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ - {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ - {40104, 0, 0, 1, HOLDING}, /* Time buffering */ - {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ - {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ - {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ - {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ - {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ - {40110, 0, 0, 1, HOLDING}, - {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ - {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ - {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ - {40114, 0, 0, 1, HOLDING}, - {40115, 0, 0, 1, HOLDING}, - {40116, 0, 0, 1, HOLDING}, - {40117, 0, 0, 1, HOLDING}, - {40118, 0, 0, 1, HOLDING}, - {40119, 0, 0, 1, HOLDING}, - {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ - - -}; -#endif /* ADELSYSTEM_CBI_H */ From a9580e991fc356ebdd91b33f6ce5b0ccd0af29f3 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Sat, 5 Feb 2022 01:13:31 +0200 Subject: [PATCH 073/700] apply Jim's modbus changes on PR #1239 --- drivers/adelsystem_cbi.c | 231 ++++++++++++++++++++++++++------------- 1 file changed, 156 insertions(+), 75 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index 409b59bddd..e3a6bb193c 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -47,7 +47,7 @@ static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus by /* initialize register start address and hex address from register number */ -void reginit(); +void reginit(void); /* read registers' memory region */ int read_all_regs(modbus_t *mb, uint16_t *data); @@ -62,7 +62,7 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate); modbus_t *modbus_new(const char *port); /* reconnect upon communication error */ -void modbus_reconnect(); +void modbus_reconnect(void); /* modbus register read function */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); @@ -120,19 +120,51 @@ void upsdrv_initups(void) fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); } - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } + /* set modbus response timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { + /* Older libmodbus API (with timeval), and we have + * checked at configure time that we can put uint32_t + * into its fields. They are probably "long" on many + * systems as respectively time_t and suseconds_t - + * but that is not guaranteed; for more details see + * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_time.h.html + */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_resp_to_s; + to.tv_usec = mod_resp_to_us; + /* void */ modbus_set_response_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#else +# error "Can not use libmodbus API for timeouts" +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ + + /* set modbus byte time out */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_byte_to_s; + to.tv_usec = mod_byte_to_us; + /* void */ modbus_set_byte_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ - /* set modbus response time out to 200ms */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } } /* initialize ups driver information */ @@ -488,7 +520,7 @@ void upsdrv_cleanup(void) */ /* initialize register start address and hex address from register number */ -void reginit() +void reginit(void) { int i; /* local index */ @@ -515,14 +547,23 @@ void reginit() regs[i].xaddr = 0x40000 + rnum - 1; break; default: - upslogx(LOG_ERR, "Invalid register type %d for register %d", regs[i].type, regs[i].num); - upsdebugx(3, "Invalid register type %d for register %d", regs[i].type, regs[i].num); + upslogx(LOG_ERR, + "Invalid register type %d for register %d", + regs[i].type, + regs[i].num + ); + upsdebugx(3, + "Invalid register type %d for register %d", + regs[i].type, + regs[i].num + ); } - upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", - regs[i].num, - regs[i].type, - regs[i].saddr, - regs[i].xaddr + upsdebugx(3, + "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", + regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr ); } } @@ -535,11 +576,12 @@ int read_all_regs(modbus_t *mb, uint16_t *data) /* read all HOLDING registers */ rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, regs_data); if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", - modbus_strerror(errno), - regs[H_REG_STARTIDX].xaddr, - MAX_H_REGS, - device_path + upslogx(LOG_ERR, + "ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", + modbus_strerror(errno), + regs[H_REG_STARTIDX].xaddr, + MAX_H_REGS, + device_path ); /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ @@ -580,30 +622,44 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); *(uint *)data = *(uint *)data & mask16; break; - -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) # pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: + upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); + break; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) # pragma GCC diagnostic pop #endif - upsdebugx(2, "ERROR: register_read: invalid register type %d\n", type); - break; } if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path + upslogx(LOG_ERR, + "ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path ); /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ @@ -653,13 +709,14 @@ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) break; } if (rval == -1) { - upslogx(LOG_ERR,"ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path + upslogx(LOG_ERR, + "ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path ); /* on BROKEN PIPE error try to reconnect */ @@ -707,7 +764,8 @@ int upscmd(const char *cmd, const char *arg) data = 1; rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); if (rval == -1) { - upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + upslogx(2, + "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", modbus_strerror(errno), regs[FSD].xaddr, regs[FSD].type, @@ -754,11 +812,12 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) if (rval == -1) { return rval; } - upsdebugx(3, "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", - num, - addr, - rtype, - reg_val + upsdebugx(3, + "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", + num, + addr, + rtype, + reg_val ); #endif /* process register data */ @@ -1026,9 +1085,6 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) * memory corruptions and buggy inputs below... */ default: -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) -# pragma GCC diagnostic pop -#endif state->reg.val16 = reg_val; n = snprintf(NULL, 0, "%d", reg_val); if (ptr != NULL) { @@ -1037,8 +1093,11 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); ptr = reg_val_s; sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; + state->reg.strval = reg_val_s; break; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif } return rval; @@ -1156,7 +1215,7 @@ modbus_t *modbus_new(const char *port) } /* reconnect to modbus server upon connection error */ -void modbus_reconnect() +void modbus_reconnect(void) { int rval; @@ -1185,17 +1244,39 @@ void modbus_reconnect() fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); } - /* set modbus response timeout */ - rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); - } - - /* set modbus response timeout */ - rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); - } + /* set modbus response timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_resp_to_s; + to.tv_usec = mod_resp_to_us; + /* void */ modbus_set_response_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ + + /* set modbus byte timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_byte_to_s; + to.tv_usec = mod_byte_to_us; + /* void */ modbus_set_byte_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ } From f0bf93e51d4f8b45539a59aae13f4bf3971fa81e Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Sun, 6 Feb 2022 20:31:03 +0200 Subject: [PATCH 074/700] adelsystem_cbi header filename fix --- drivers/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/Makefile.am b/drivers/Makefile.am index 7fb39c66b7..d50582d2f9 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -331,7 +331,7 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h b xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ apc-pdu-mib.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h \ eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h legrand-hid.h \ - hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adelsystem_cbi + hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adelsystem_cbi.h # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, From a05a2085701196418cb133312133c1f2071b1eee Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Tue, 8 Feb 2022 22:11:55 +0200 Subject: [PATCH 075/700] fix warning for initializing structure with flexible array member --- drivers/adelsystem_cbi.c | 162 ++++++++++++++++++++++----------------- drivers/adelsystem_cbi.h | 138 ++++++++++++++++++--------------- 2 files changed, 170 insertions(+), 130 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index e3a6bb193c..51403ac360 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -46,6 +46,9 @@ static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus by static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ +/* initialize alarm structs */ +void alrminit(void); + /* initialize register start address and hex address from register number */ void reginit(void); @@ -95,9 +98,9 @@ void upsdrv_initups(void) { int rval; upsdebugx(2, "upsdrv_initups"); - - + dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); + alrminit(); reginit(); get_config_vars(); @@ -220,7 +223,7 @@ void upsdrv_updateinfo(void) } else { if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { status_set("OB"); - alarm_set(mains.alrm[MAINS_AVAIL_I].descr); + alarm_set(mains->alrm[MAINS_AVAIL_I].descr); upslogx(LOG_INFO, "ups.status = OB"); } else { status_set("OL"); @@ -228,7 +231,7 @@ void upsdrv_updateinfo(void) } if (ds->alrm->alrm[SHUTD_REQST_I].actv) { status_set("FSD"); - alarm_set(mains.alrm[SHUTD_REQST_I].descr); + alarm_set(mains->alrm[SHUTD_REQST_I].descr); upslogx(LOG_INFO, "ups.status = FSD"); } } @@ -242,16 +245,16 @@ void upsdrv_updateinfo(void) } else { if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { status_set("LB"); - alarm_set(bval.alrm[BVAL_LOALRM_I].descr); + alarm_set(bval->alrm[BVAL_LOALRM_I].descr); upslogx(LOG_INFO, "ups.status = LB"); } if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { status_set("HB"); - alarm_set(bval.alrm[BVAL_HIALRM_I].descr); + alarm_set(bval->alrm[BVAL_HIALRM_I].descr); upslogx(LOG_INFO, "ups.status = HB"); } if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { - alarm_set(bval.alrm[BVAL_BSTSFL_I].descr); + alarm_set(bval->alrm[BVAL_BSTSFL_I].descr); upslogx(LOG_INFO, "battery start with battery flat"); } } @@ -519,6 +522,27 @@ void upsdrv_cleanup(void) * driver support functions */ +/* initialize alarm structs */ +void alrminit(void) +{ + mains = alloc_alrm_ar(mains_c, sizeof(mains_ar)); + alrm_ar_init(mains, mains_ar, mains_c); + vaca = alloc_alrm_ar(vaca_c, sizeof(vaca_ar)); + alrm_ar_init(vaca, vaca_ar, vaca_c); + devf = alloc_alrm_ar(devf_c, sizeof(devf_ar)); + alrm_ar_init(devf, devf_ar, devf_c); + btsf = alloc_alrm_ar(btsf_c, sizeof(btsf_ar)); + alrm_ar_init(btsf, btsf_ar, btsf_c); + bval = alloc_alrm_ar(bval_c, sizeof(bval_ar)); + alrm_ar_init(bval, bval_ar, bval_c); + shsc = alloc_alrm_ar(shsc_c, sizeof(shsc_ar)); + alrm_ar_init(shsc, shsc_ar, shsc_c); + bsta = alloc_alrm_ar(bsta_c, sizeof(bsta_ar)); + alrm_ar_init(bsta, bsta_ar, bsta_c); + obta = alloc_alrm_ar(obta_c, sizeof(obta_ar)); + alrm_ar_init(obta, obta_ar, obta_c); +} + /* initialize register start address and hex address from register number */ void reginit(void) { @@ -574,7 +598,7 @@ int read_all_regs(modbus_t *mb, uint16_t *data) int rval; /* read all HOLDING registers */ - rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, regs_data); + rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, data); if (rval == -1) { upslogx(LOG_ERR, "ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", @@ -845,7 +869,7 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) case LVDC: /* "output.voltage" */ case LCUR: /* "output.current" */ if (reg_val != 0) { - state->reg.val16 = reg_val; + state->reg.val.ui16 = reg_val; double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ n = snprintf(NULL, 0, "%.2f", fval); if (ptr != NULL) { @@ -856,7 +880,7 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; } else { - state->reg.val16 = 0; + state->reg.val.ui16 = 0; state->reg.strval = "0.00"; } upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); @@ -866,7 +890,7 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) case BCEF: case VAC: /* "input.voltage" */ if (reg_val != 0) { - state->reg.val16 = reg_val; + state->reg.val.ui16 = reg_val; n = snprintf(NULL, 0, "%d", reg_val); if (ptr != NULL) { free(ptr); @@ -876,14 +900,14 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) sprintf(reg_val_s, "%d", reg_val); state->reg.strval = reg_val_s; } else { - state->reg.val16 = 0; + state->reg.val.ui16 = 0; state->reg.strval = "0"; } upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case BSOC: /* "battery.charge" */ if (reg_val != 0) { - state->reg.val16 = reg_val; + state->reg.val.ui16 = reg_val; double fval = (double )reg_val * regs[BSOC].scale; n = snprintf(NULL, 0, "%.2f", fval); if (ptr != NULL) { @@ -894,14 +918,14 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) sprintf(fval_s, "%.2f", fval); state->reg.strval = fval_s; } else { - state->reg.val16 = 0; + state->reg.val.ui16 = 0; state->reg.strval = "0.00"; } upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); break; case BTMP: /* "battery.temperature" */ case OTMP: /* "ups.temperature" */ - state->reg.val16 = reg_val; + state->reg.val.ui16 = reg_val; double fval = reg_val - 273.15; n = snprintf(NULL, 0, "%.2f", fval); char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); @@ -941,140 +965,140 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) break; case BSTA: if (reg_val & BSTA_REVPOL_M) { - bsta.alrm[BSTA_REVPOL_I].actv = 1; + bsta->alrm[BSTA_REVPOL_I].actv = 1; } else { - bsta.alrm[BSTA_REVPOL_I].actv = 0; + bsta->alrm[BSTA_REVPOL_I].actv = 0; } if (reg_val & BSTA_NOCNND_M) { - bsta.alrm[BSTA_NOCNND_I].actv = 1; + bsta->alrm[BSTA_NOCNND_I].actv = 1; } else { - bsta.alrm[BSTA_NOCNND_I].actv = 0; + bsta->alrm[BSTA_NOCNND_I].actv = 0; } if (reg_val & BSTA_CLSHCR_M) { - bsta.alrm[BSTA_CLSHCR_I].actv = 1; + bsta->alrm[BSTA_CLSHCR_I].actv = 1; } else { - bsta.alrm[BSTA_CLSHCR_I].actv = 0; + bsta->alrm[BSTA_CLSHCR_I].actv = 0; } if (reg_val & BSTA_SULPHD_M) { - bsta.alrm[BSTA_SULPHD_I].actv = 1; + bsta->alrm[BSTA_SULPHD_I].actv = 1; } else { - bsta.alrm[BSTA_SULPHD_I].actv = 0; + bsta->alrm[BSTA_SULPHD_I].actv = 0; } if (reg_val & BSTA_CHEMNS_M) { - bsta.alrm[BSTA_CHEMNS_I].actv = 1; + bsta->alrm[BSTA_CHEMNS_I].actv = 1; } else { - bsta.alrm[BSTA_CHEMNS_I].actv = 0; + bsta->alrm[BSTA_CHEMNS_I].actv = 0; } if (reg_val & BSTA_CNNFLT_M) { - bsta.alrm[BSTA_CNNFLT_I].actv = 1; + bsta->alrm[BSTA_CNNFLT_I].actv = 1; } else { - bsta.alrm[BSTA_CNNFLT_I].actv = 0; + bsta->alrm[BSTA_CNNFLT_I].actv = 0; } - state->alrm = &bsta; + state->alrm = bsta; break; case SCSH: if (reg_val & SHSC_HIRESI_M) { - shsc.alrm[SHSC_HIRESI_I].actv = 1; + shsc->alrm[SHSC_HIRESI_I].actv = 1; } else { - shsc.alrm[SHSC_HIRESI_I].actv = 0; + shsc->alrm[SHSC_HIRESI_I].actv = 0; } if (reg_val & SHSC_LOCHEF_M) { - shsc.alrm[SHSC_LOCHEF_I].actv = 1; + shsc->alrm[SHSC_LOCHEF_I].actv = 1; } else { - shsc.alrm[SHSC_LOCHEF_I].actv = 0; + shsc->alrm[SHSC_LOCHEF_I].actv = 0; } if (reg_val & SHSC_LOEFCP_M) { - shsc.alrm[SHSC_LOEFCP_I].actv = 1; + shsc->alrm[SHSC_LOEFCP_I].actv = 1; } else { - shsc.alrm[SHSC_LOEFCP_I].actv = 0; + shsc->alrm[SHSC_LOEFCP_I].actv = 0; } if (reg_val & SHSC_LOWSOC_M) { - shsc.alrm[SHSC_LOWSOC_I].actv = 1; + shsc->alrm[SHSC_LOWSOC_I].actv = 1; } else { - shsc.alrm[SHSC_LOWSOC_I].actv = 0; + shsc->alrm[SHSC_LOWSOC_I].actv = 0; } - state->alrm = &shsc; + state->alrm = shsc; break; case BVAL: if (reg_val & BVAL_HIALRM_M) { - bval.alrm[BVAL_HIALRM_I].actv = 1; + bval->alrm[BVAL_HIALRM_I].actv = 1; } else { - bval.alrm[BVAL_HIALRM_I].actv = 0; + bval->alrm[BVAL_HIALRM_I].actv = 0; } if (reg_val & BVAL_LOALRM_M) { - bval.alrm[BVAL_LOALRM_I].actv = 1; + bval->alrm[BVAL_LOALRM_I].actv = 1; } else { - bval.alrm[BVAL_LOALRM_I].actv = 0; + bval->alrm[BVAL_LOALRM_I].actv = 0; } if (reg_val & BVAL_BSTSFL_M) { - bval.alrm[BVAL_BSTSFL_I].actv = 1; + bval->alrm[BVAL_BSTSFL_I].actv = 1; } else { - bval.alrm[BVAL_BSTSFL_I].actv = 0; + bval->alrm[BVAL_BSTSFL_I].actv = 0; } - state->alrm = &bval; + state->alrm = bval; break; case BTSF: if (reg_val & BTSF_FCND_M) { - btsf.alrm[BTSF_FCND_I].actv = 1; + btsf->alrm[BTSF_FCND_I].actv = 1; } else { - btsf.alrm[BTSF_FCND_I].actv = 0; + btsf->alrm[BTSF_FCND_I].actv = 0; } if (reg_val & BTSF_NCND_M) { - btsf.alrm[BTSF_NCND_I].actv = 1; + btsf->alrm[BTSF_NCND_I].actv = 1; } else { - btsf.alrm[BTSF_NCND_I].actv = 0; + btsf->alrm[BTSF_NCND_I].actv = 0; } - state->alrm = &btsf; + state->alrm = btsf; break; case DEVF: if (reg_val & DEVF_RCALRM_M) { - devf.alrm[DEVF_RCALRM_I].actv = 1; + devf->alrm[DEVF_RCALRM_I].actv = 1; } else { - devf.alrm[DEVF_RCALRM_I].actv = 0; + devf->alrm[DEVF_RCALRM_I].actv = 0; } if (reg_val & DEVF_INALRM_M) { - devf.alrm[DEVF_INALRM_I].actv = 1; + devf->alrm[DEVF_INALRM_I].actv = 1; } else { - devf.alrm[DEVF_INALRM_I].actv = 0; + devf->alrm[DEVF_INALRM_I].actv = 0; } if (reg_val & DEVF_LFNAVL_M) { - devf.alrm[DEVF_LFNAVL_I].actv = 1; + devf->alrm[DEVF_LFNAVL_I].actv = 1; } else { - devf.alrm[DEVF_LFNAVL_I].actv = 0; + devf->alrm[DEVF_LFNAVL_I].actv = 0; } - state->alrm = &devf; + state->alrm = devf; break; case VACA: if (reg_val & VACA_HIALRM_M) { - vaca.alrm[VACA_HIALRM_I].actv = 1; + vaca->alrm[VACA_HIALRM_I].actv = 1; } else { - vaca.alrm[VACA_HIALRM_I].actv = 0; + vaca->alrm[VACA_HIALRM_I].actv = 0; } if (reg_val == VACA_LOALRM_M) { - vaca.alrm[VACA_LOALRM_I].actv = 1; + vaca->alrm[VACA_LOALRM_I].actv = 1; } else { - vaca.alrm[VACA_LOALRM_I].actv = 0; + vaca->alrm[VACA_LOALRM_I].actv = 0; } - state->alrm = &vaca; + state->alrm = vaca; break; case MAIN: if (reg_val & MAINS_AVAIL_M) { - mains.alrm[MAINS_AVAIL_I].actv = 1; + mains->alrm[MAINS_AVAIL_I].actv = 1; } else { - mains.alrm[MAINS_AVAIL_I].actv = 0; + mains->alrm[MAINS_AVAIL_I].actv = 0; } if (reg_val == SHUTD_REQST_M) { - mains.alrm[SHUTD_REQST_I].actv = 1; + mains->alrm[SHUTD_REQST_I].actv = 1; } else { - mains.alrm[SHUTD_REQST_I].actv = 0; + mains->alrm[SHUTD_REQST_I].actv = 0; } - state->alrm = &mains; + state->alrm = mains; break; case OBTA: if (reg_val == OBTA_HIALRM_V) { - obta.alrm[OBTA_HIALRM_I].actv = 1; + obta->alrm[OBTA_HIALRM_I].actv = 1; } - state->alrm = &obta; + state->alrm = obta; break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push @@ -1085,7 +1109,7 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) * memory corruptions and buggy inputs below... */ default: - state->reg.val16 = reg_val; + state->reg.val.ui16 = reg_val; n = snprintf(NULL, 0, "%d", reg_val); if (ptr != NULL) { free(ptr); diff --git a/drivers/adelsystem_cbi.h b/drivers/adelsystem_cbi.h index b3500a7afc..ff698c15e2 100644 --- a/drivers/adelsystem_cbi.h +++ b/drivers/adelsystem_cbi.h @@ -127,10 +127,10 @@ typedef struct pwrmng pwrmng_t; /* general modbus register value */ struct reg { - union { - uint16_t val16; - uint8_t val8; - }; + union val { + uint16_t ui16; + uint8_t ui8; + } val; char *strval; }; typedef struct reg reg_t; @@ -145,7 +145,7 @@ typedef struct alrm alrm_t; /* general alarm array */ struct alrm_ar { int alrm_c; /* alarm count */ - alrm_t alrm[]; /* alarm array */ + alrm_t *alrm; /* alarm array */ }; typedef struct alrm_ar alrm_ar_t; @@ -247,84 +247,100 @@ typedef struct alrm_ar alrm_ar_t; #define BSTA_CHEMNS_I 4 /* chemistry not supported */ #define BSTA_CNNFLT_I 5 /* connection fault */ -/* input mains and shutdown alarms */ -static alrm_ar_t mains = { - 2, - { - {0, "input voltage not available"}, - {0, "ups shutdown requested"} +/* Allocate alarm arrays */ +inline +alrm_ar_t *alloc_alrm_ar(int as, size_t n) +{ + alrm_ar_t *ret = xcalloc(sizeof(alrm_t) + n, 1); + if (ret) { + memcpy(ret, + &(alrm_ar_t const) { + .alrm_c = as + }, + sizeof(alrm_ar_t) + ); } + return ret; +} + +/* Initialize alarm arrays */ +inline +void alrm_ar_init(alrm_ar_t *ar_ptr, alrm_t *a_ptr, int as) +{ + ar_ptr->alrm_c = as; + ar_ptr->alrm = a_ptr; +} + +/* input mains and shutdown alarms */ +static alrm_t mains_ar[] = { + {0, "input voltage not available"}, + {0, "ups shutdown requested"} }; +static int mains_c = 2; +static alrm_ar_t *mains; /* AC input voltage alarms */ -static alrm_ar_t vaca = { - 2, - { - {0, "input voltage high alarm"}, - {0, "input voltage low alarm"} - } +static alrm_t vaca_ar[] = { + {0, "input voltage high alarm"}, + {0, "input voltage low alarm"} }; +static int vaca_c = 2; +static alrm_ar_t *vaca; /* device failure alarms */ -static alrm_ar_t devf = { - 3, - { - {0, "UPS rectifier failure"}, - {0, "UPS internal failure"}, - {0, "UPS lifetest not available"} - } +static alrm_t devf_ar[] = { + {0, "UPS rectifier failure"}, + {0, "UPS internal failure"}, + {0, "UPS lifetest not available"} }; +static int devf_c = 3; +static alrm_ar_t *devf; /* battery sensor failure alarms */ -static alrm_ar_t btsf = { - 2, - { - {0, "battery temp sensor connection fault"}, - {0, "battery temp sensor not connected"} - } +static alrm_t btsf_ar[] = { + {0, "battery temp sensor connection fault"}, + {0, "battery temp sensor not connected"} }; +static int btsf_c = 2; +static alrm_ar_t *btsf; /* battery voltage alarms */ -static alrm_ar_t bval = { - 3, - { - {0, "battery high voltage"}, - {0, "battery low voltage"}, - {0, "battery start with battery flat"} - } +static alrm_t bval_ar[] = { + {0, "battery high voltage"}, + {0, "battery low voltage"}, + {0, "battery start with battery flat"} }; +static int bval_c = 3; +static alrm_ar_t *bval; /* battery SoH and SoC alarms */ -static alrm_ar_t shsc = { - 4, - { - {0, "battery high internal resistance"}, - {0, "battery low charge efficiency"}, - {0, "battery low effective capacity"}, - {0, "battery low state of charge"} - } +static alrm_t shsc_ar[] = { + {0, "battery high internal resistance"}, + {0, "battery low charge efficiency"}, + {0, "battery low effective capacity"}, + {0, "battery low state of charge"} }; +static int shsc_c = 4; +static alrm_ar_t *shsc; /* battery status alarm */ -static alrm_ar_t bsta = { - 6, - { - {0, "battery reversed polarity"}, - {0, "battery not connected"}, - {0, "battery cell short circuit"}, - {0, "battery sulphated"}, - {0, "battery chemistry not supported"}, - {0, "battery connection fault"} - } +static alrm_t bsta_ar[] = { + {0, "battery reversed polarity"}, + {0, "battery not connected"}, + {0, "battery cell short circuit"}, + {0, "battery sulphated"}, + {0, "battery chemistry not supported"}, + {0, "battery connection fault"} }; +static int bsta_c = 4; +static alrm_ar_t *bsta; /* onboard temperature alarm */ -static alrm_ar_t obta = { - 1, - { - {0, "onboard temperature high"} - } +static alrm_t obta_ar[] = { + {0, "onboard temperature high"} }; +static int obta_c = 4; +static alrm_ar_t *obta; /* UPS device reg enum */ enum devreg { @@ -507,4 +523,4 @@ static regattr_t regs[] = { }; -#endif /* ADELSYSTEM_CBI_H */ +#endif /* ADELSYSTEM_CBI_H */ \ No newline at end of file From 2e6fcf1bfbc56bc1a895da2d60a20e894e7d1fa4 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Tue, 8 Feb 2022 22:39:07 +0200 Subject: [PATCH 076/700] minor fix in upsdrv_updateinfo --- drivers/adelsystem_cbi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index 51403ac360..d6e5f5485c 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -322,7 +322,11 @@ void upsdrv_updateinfo(void) upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); } } - rval = get_dev_state(VAC, &ds); + } + rval = get_dev_state(VAC, &ds); + if (rval == -1) { + errcnt++; + } else { dstate_setinfo("input.voltage", "%s", ds->reg.strval); upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); } From e42fc6fa68d2c1652f6301867b7d0834a3b6f4ee Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Tue, 8 Feb 2022 22:40:02 +0200 Subject: [PATCH 077/700] check ifndef READALL_REGS --- drivers/adelsystem_cbi.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/adelsystem_cbi.h b/drivers/adelsystem_cbi.h index ff698c15e2..a94c716b3b 100644 --- a/drivers/adelsystem_cbi.h +++ b/drivers/adelsystem_cbi.h @@ -58,7 +58,9 @@ #define H_REG_STARTIDX 0 /* read all regs preprocessor flag */ +#ifndef READALL_REGS #define READALL_REGS 1 +#endif /* number of device models */ #define DEV_NUMOF_MODELS 10 From 3a7b96de0a3b4e9904cda05c448be85ae623f3a6 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Fri, 11 Feb 2022 00:13:56 +0200 Subject: [PATCH 078/700] fix parameter hiding global variable --- drivers/adelsystem_cbi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index d6e5f5485c..dc30293706 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -59,7 +59,7 @@ int read_all_regs(modbus_t *mb, uint16_t *data); void get_config_vars(void); /* get device state */ -int get_dev_state(devreg_t regindx, devstate_t **dstate); +int get_dev_state(devreg_t regindx, devstate_t **dvstat); /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port); @@ -814,7 +814,7 @@ int upscmd(const char *cmd, const char *arg) /* read device state, returns 0 on success or -1 on communication error it formats state depending on register semantics */ -int get_dev_state(devreg_t regindx, devstate_t **dstate) +int get_dev_state(devreg_t regindx, devstate_t **dvstat) { int i; /* local index */ int n; @@ -828,7 +828,7 @@ int get_dev_state(devreg_t regindx, devstate_t **dstate) #endif devstate_t *state; /* device state */ - state = *dstate; + state = *dvstat; #if READALL_REGS == 1 reg_val = regs_data[regindx]; rval = 0; From b8be8ad0fb73f85558af90d66da4e3dbd0e03ab4 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Fri, 11 Feb 2022 01:37:26 +0200 Subject: [PATCH 079/700] coding style changes --- drivers/adelsystem_cbi.c | 2031 +++++++++++++++++++------------------- drivers/adelsystem_cbi.h | 597 +++++------ 2 files changed, 1318 insertions(+), 1310 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index dc30293706..1f43f44c13 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -1,25 +1,29 @@ -/* adelsystem_cbi.c - driver for ADELSYSTEM CB/CBI DC-UPS +/* adelsystem_cbi.c - driver for ADELSYSTEM CB/CBI DC-UPS * - * Copyright (C) - * 2022 Dimitris Economou + * Copyright (C) + * 2022 Dimitris Economou * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ +/* + * code indentation with tabstop=4 + */ + #include "main.h" #include "adelsystem_cbi.h" #include @@ -29,21 +33,21 @@ #define DRIVER_VERSION "0.01" /* variables */ -static modbus_t *mbctx = NULL; /* modbus memory context */ -static devstate_t *dstate = NULL; /* device state context */ -static int errcnt = 0; /* modbus access error counter */ -static char *device_mfr = DEVICE_MFR; /* device manufacturer */ -static char *device_model = DEVICE_MODEL; /* device model */ -static char *device_type = DEVICE_TYPE; /* device model */ -static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ -static char ser_parity = PARITY; /* serial port parity */ -static int ser_data_bit = DATA_BIT; /* serial port data bit */ -static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ -static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ -static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ -static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ -static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ -static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static devstate_t *dstate = NULL; /* device state context */ +static int errcnt = 0; /* modbus access error counter */ +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static char *device_type = DEVICE_TYPE; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ /* initialize alarm structs */ @@ -82,11 +86,11 @@ long time_elapsed(struct timeval *start); /* driver description structure */ upsdrv_info_t upsdrv_info = { - DRIVER_NAME, - DRIVER_VERSION, - "Dimitris Economou \n", - DRV_BETA, - {NULL} + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} }; /* @@ -96,32 +100,32 @@ upsdrv_info_t upsdrv_info = { /* read configuration variables from ups.conf and connect to ups device */ void upsdrv_initups(void) { - int rval; - upsdebugx(2, "upsdrv_initups"); - - dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); - alrminit(); - reginit(); - get_config_vars(); - - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } - - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } - - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); - } + int rval; + upsdebugx(2, "upsdrv_initups"); + + dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); + alrminit(); + reginit(); + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } /* set modbus response timeout */ #if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) @@ -158,7 +162,7 @@ void upsdrv_initups(void) fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); } #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) - { /* see comments above */ + { /* see comments above */ struct timeval to; memset(&to, 0, sizeof(struct timeval)); to.tv_sec = mod_byte_to_s; @@ -173,320 +177,320 @@ void upsdrv_initups(void) /* initialize ups driver information */ void upsdrv_initinfo(void) { - devstate_t *ds = dstate; /* device state context */ - upsdebugx(2, "upsdrv_initinfo"); + devstate_t *ds = dstate; /* device state context */ + upsdebugx(2, "upsdrv_initinfo"); - /* set device information */ - dstate_setinfo("device.mfr", "%s", device_mfr); - dstate_setinfo("device.model", "%s", device_model); - dstate_setinfo("device.type", "%s", device_type); + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); + dstate_setinfo("device.type", "%s", device_type); - /* read ups model */ - get_dev_state(PRDN, &ds); - dstate_setinfo("ups.model", "%s", ds->product.name); - upslogx(LOG_INFO, "ups.model = %s", ds->product.name); + /* read ups model */ + get_dev_state(PRDN, &ds); + dstate_setinfo("ups.model", "%s", ds->product.name); + upslogx(LOG_INFO, "ups.model = %s", ds->product.name); - /* register instant commands */ - dstate_addcmd("load.off"); + /* register instant commands */ + dstate_addcmd("load.off"); - /* set callback for instant commands */ - upsh.instcmd = upscmd; + /* set callback for instant commands */ + upsh.instcmd = upscmd; } /* update UPS signal state */ void upsdrv_updateinfo(void) { - int rval; /* return value */ - int i; /* local index */ - devstate_t *ds = dstate; /* device state */ + int rval; /* return value */ + int i; /* local index */ + devstate_t *ds = dstate; /* device state */ - upsdebugx(2, "upsdrv_updateinfo"); + upsdebugx(2, "upsdrv_updateinfo"); - errcnt = 0; /* initialize error counter to zero */ - status_init(); /* initialize ups.status update */ - alarm_init(); /* initialize ups.alarm update */ + errcnt = 0; /* initialize error counter to zero */ + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ #if READALL_REGS == 1 - rval = read_all_regs(mbctx, regs_data); - if (rval == -1) { - errcnt++; - } else { + rval = read_all_regs(mbctx, regs_data); + if (rval == -1) { + errcnt++; + } else { #endif - /* - * update UPS status regarding MAINS and SHUTDOWN request - * - OL: On line (mains is present) - * - OB: On battery (mains is not present) - */ - rval = get_dev_state(MAIN, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { - status_set("OB"); - alarm_set(mains->alrm[MAINS_AVAIL_I].descr); - upslogx(LOG_INFO, "ups.status = OB"); - } else { - status_set("OL"); - upslogx(LOG_INFO, "ups.status = OL"); - } - if (ds->alrm->alrm[SHUTD_REQST_I].actv) { - status_set("FSD"); - alarm_set(mains->alrm[SHUTD_REQST_I].descr); - upslogx(LOG_INFO, "ups.status = FSD"); - } - } - - /* - * update UPS status regarding battery voltage - */ - rval = get_dev_state(BVAL, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { - status_set("LB"); - alarm_set(bval->alrm[BVAL_LOALRM_I].descr); - upslogx(LOG_INFO, "ups.status = LB"); - } - if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { - status_set("HB"); - alarm_set(bval->alrm[BVAL_HIALRM_I].descr); - upslogx(LOG_INFO, "ups.status = HB"); - } - if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { - alarm_set(bval->alrm[BVAL_BSTSFL_I].descr); - upslogx(LOG_INFO, "battery start with battery flat"); - } - } - - /* get "battery.voltage" */ - rval = get_dev_state(BATV, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); - } - /* - * update UPS status regarding battery charger status - */ - - /* get "battery.charger.status" */ - rval = get_dev_state(CHRG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { - status_set("CHRG"); - upslogx(LOG_INFO, "ups.status = CHRG"); - } - dstate_setinfo("battery.charger.status", "%s", ds->charge.info); - upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); - } - rval = get_dev_state(PMNG, &ds); - if (rval == -1) { - errcnt++; - } else { - if (ds->power.state == PMNG_BCKUP) { - status_set("DISCHRG"); - dstate_setinfo("battery.charger.status", "discharging"); - upslogx(LOG_INFO, "ups.status = DISCHRG"); - } - if (ds->power.state == PMNG_BOOST) { - status_set("BOOST"); - upslogx(LOG_INFO, "ups.status = BOOST"); - } - } - - /* - * update UPS battery state of charge - */ - rval = get_dev_state(BSOC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.charge", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); - } - - /* - * update UPS AC input state - */ - rval = get_dev_state(VACA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(VAC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("input.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); - } - - /* - * update UPS onboard temperature state - */ - rval = get_dev_state(OBTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(OTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("ups.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); - } - /* - * update UPS battery temperature state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - rval = get_dev_state(BTMP, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.temperature", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); - } - rval = get_dev_state(TBUF, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("battery.runtime", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); - } - - /* - * update UPS device failure state - */ - rval = get_dev_state(DEVF, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS SoH and SoC states - */ - rval = get_dev_state(SCSH, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS battery state - */ - rval = get_dev_state(BSTA, &ds); - if (rval == -1) { - errcnt++; - } else { - for (i = 0; i < ds->alrm->alrm_c; i++) { - if (ds->alrm->alrm[i].actv) { - alarm_set(ds->alrm->alrm[i].descr); - upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); - } - } - } - - /* - * update UPS load status - */ - rval = get_dev_state(LVDC, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.voltage", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); - } - rval = get_dev_state(LCUR, &ds); - if (rval == -1) { - errcnt++; - } else { - dstate_setinfo("output.current", "%s", ds->reg.strval); - upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); - } + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ + rval = get_dev_state(MAIN, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains->alrm[MAINS_AVAIL_I].descr); + upslogx(LOG_INFO, "ups.status = OB"); + } else { + status_set("OL"); + upslogx(LOG_INFO, "ups.status = OL"); + } + if (ds->alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains->alrm[SHUTD_REQST_I].descr); + upslogx(LOG_INFO, "ups.status = FSD"); + } + } + + /* + * update UPS status regarding battery voltage + */ + rval = get_dev_state(BVAL, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set(bval->alrm[BVAL_LOALRM_I].descr); + upslogx(LOG_INFO, "ups.status = LB"); + } + if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set(bval->alrm[BVAL_HIALRM_I].descr); + upslogx(LOG_INFO, "ups.status = HB"); + } + if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set(bval->alrm[BVAL_BSTSFL_I].descr); + upslogx(LOG_INFO, "battery start with battery flat"); + } + } + + /* get "battery.voltage" */ + rval = get_dev_state(BATV, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); + } + /* + * update UPS status regarding battery charger status + */ + + /* get "battery.charger.status" */ + rval = get_dev_state(CHRG, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { + status_set("CHRG"); + upslogx(LOG_INFO, "ups.status = CHRG"); + } + dstate_setinfo("battery.charger.status", "%s", ds->charge.info); + upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); + } + rval = get_dev_state(PMNG, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + upslogx(LOG_INFO, "ups.status = DISCHRG"); + } + if (ds->power.state == PMNG_BOOST) { + status_set("BOOST"); + upslogx(LOG_INFO, "ups.status = BOOST"); + } + } + + /* + * update UPS battery state of charge + */ + rval = get_dev_state(BSOC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.charge", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); + } + + /* + * update UPS AC input state + */ + rval = get_dev_state(VACA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(VAC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("input.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); + } + + /* + * update UPS onboard temperature state + */ + rval = get_dev_state(OBTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(OTMP, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("ups.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); + } + /* + * update UPS battery temperature state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(BTMP, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); + } + rval = get_dev_state(TBUF, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.runtime", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); + } + + /* + * update UPS device failure state + */ + rval = get_dev_state(DEVF, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS SoH and SoC states + */ + rval = get_dev_state(SCSH, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS battery state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS load status + */ + rval = get_dev_state(LVDC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("output.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); + } + rval = get_dev_state(LCUR, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("output.current", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); + } #if READALL_REGS == 1 - } + } #endif - /* check for communication errors */ - if (errcnt == 0) { - alarm_commit(); - status_commit(); - dstate_dataok(); - } else { - upsdebugx(2, "Communication errors: %d", errcnt); - dstate_datastale(); - } + /* check for communication errors */ + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2, "Communication errors: %d", errcnt); + dstate_datastale(); + } } /* shutdown UPS */ void upsdrv_shutdown(void) { - int rval; - int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ - struct timeval start; - long etime; - - /* retry sending shutdown command on error */ - while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { - rval = gettimeofday(&start, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); - } - - /* wait for an increasing time interval before sending shutdown command */ - while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); - upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); - cnt--; - } - switch (rval) { - case STAT_INSTCMD_FAILED: - case STAT_INSTCMD_INVALID: - fatalx(EXIT_FAILURE, "shutdown failed"); - case STAT_INSTCMD_UNKNOWN: - fatalx(EXIT_FAILURE, "shutdown not supported"); - default: - break; - } - upslogx(LOG_INFO, "shutdown command executed"); + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); } /* print driver usage info */ @@ -497,29 +501,29 @@ void upsdrv_help(void) /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { - addvar(VAR_VALUE, "device_mfr", "device manufacturer"); - addvar(VAR_VALUE, "device_model", "device model"); - addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); - addvar(VAR_VALUE, "ser_parity", "serial port parity"); - addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); - addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); - addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); - addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); - addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); - addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); - addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); + addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); + addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); + addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); } /* close modbus connection and free modbus context allocated memory */ void upsdrv_cleanup(void) { - if (mbctx != NULL) { - modbus_close(mbctx); - modbus_free(mbctx); - } - if (dstate != NULL) { - free(dstate); - } + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } + if (dstate != NULL) { + free(dstate); + } } /* @@ -529,127 +533,127 @@ void upsdrv_cleanup(void) /* initialize alarm structs */ void alrminit(void) { - mains = alloc_alrm_ar(mains_c, sizeof(mains_ar)); - alrm_ar_init(mains, mains_ar, mains_c); - vaca = alloc_alrm_ar(vaca_c, sizeof(vaca_ar)); - alrm_ar_init(vaca, vaca_ar, vaca_c); - devf = alloc_alrm_ar(devf_c, sizeof(devf_ar)); - alrm_ar_init(devf, devf_ar, devf_c); - btsf = alloc_alrm_ar(btsf_c, sizeof(btsf_ar)); - alrm_ar_init(btsf, btsf_ar, btsf_c); - bval = alloc_alrm_ar(bval_c, sizeof(bval_ar)); - alrm_ar_init(bval, bval_ar, bval_c); - shsc = alloc_alrm_ar(shsc_c, sizeof(shsc_ar)); - alrm_ar_init(shsc, shsc_ar, shsc_c); - bsta = alloc_alrm_ar(bsta_c, sizeof(bsta_ar)); - alrm_ar_init(bsta, bsta_ar, bsta_c); - obta = alloc_alrm_ar(obta_c, sizeof(obta_ar)); - alrm_ar_init(obta, obta_ar, obta_c); + mains = alloc_alrm_ar(mains_c, sizeof(mains_ar)); + alrm_ar_init(mains, mains_ar, mains_c); + vaca = alloc_alrm_ar(vaca_c, sizeof(vaca_ar)); + alrm_ar_init(vaca, vaca_ar, vaca_c); + devf = alloc_alrm_ar(devf_c, sizeof(devf_ar)); + alrm_ar_init(devf, devf_ar, devf_c); + btsf = alloc_alrm_ar(btsf_c, sizeof(btsf_ar)); + alrm_ar_init(btsf, btsf_ar, btsf_c); + bval = alloc_alrm_ar(bval_c, sizeof(bval_ar)); + alrm_ar_init(bval, bval_ar, bval_c); + shsc = alloc_alrm_ar(shsc_c, sizeof(shsc_ar)); + alrm_ar_init(shsc, shsc_ar, shsc_c); + bsta = alloc_alrm_ar(bsta_c, sizeof(bsta_ar)); + alrm_ar_init(bsta, bsta_ar, bsta_c); + obta = alloc_alrm_ar(obta_c, sizeof(obta_ar)); + alrm_ar_init(obta, obta_ar, obta_c); } /* initialize register start address and hex address from register number */ void reginit(void) { - int i; /* local index */ - - for (i = 0; i < MODBUS_NUMOF_REGS; i++) { - int rnum = regs[i].num; - switch (regs[i].type) { - case COIL: - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x0 + regs[i].num - 1; - break; - case INPUT_B: - rnum -= 10000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x10000 + rnum - 1; - break; - case INPUT_R: - rnum -= 30000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x30000 + rnum - 1; - break; - case HOLDING: - rnum -= 40000; - regs[i].saddr = rnum - 1; - regs[i].xaddr = 0x40000 + rnum - 1; - break; - default: - upslogx(LOG_ERR, - "Invalid register type %d for register %d", - regs[i].type, - regs[i].num - ); - upsdebugx(3, - "Invalid register type %d for register %d", - regs[i].type, - regs[i].num - ); - } - upsdebugx(3, - "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", - regs[i].num, - regs[i].type, - regs[i].saddr, - regs[i].xaddr - ); - } + int i; /* local index */ + + for (i = 0; i < MODBUS_NUMOF_REGS; i++) { + int rnum = regs[i].num; + switch (regs[i].type) { + case COIL: + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x0 + regs[i].num - 1; + break; + case INPUT_B: + rnum -= 10000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x10000 + rnum - 1; + break; + case INPUT_R: + rnum -= 30000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x30000 + rnum - 1; + break; + case HOLDING: + rnum -= 40000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x40000 + rnum - 1; + break; + default: + upslogx(LOG_ERR, + "Invalid register type %d for register %d", + regs[i].type, + regs[i].num + ); + upsdebugx(3, + "Invalid register type %d for register %d", + regs[i].type, + regs[i].num + ); + } + upsdebugx(3, + "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", + regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr + ); + } } /* read registers' memory region */ -int read_all_regs(modbus_t *mb, uint16_t *data) +int read_all_regs(modbus_t *mb, uint16_t *data) { - int rval; - - /* read all HOLDING registers */ - rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, data); - if (rval == -1) { - upslogx(LOG_ERR, - "ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", - modbus_strerror(errno), - regs[H_REG_STARTIDX].xaddr, - MAX_H_REGS, - device_path - ); - - /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ - if (errno == EPIPE || errno == EMBBADDATA || errno == EMBBADCRC) { - upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - - /* no COIL, INPUT_B or INPUT_R register regions to read */ - - return rval; + int rval; + + /* read all HOLDING registers */ + rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, data); + if (rval == -1) { + upslogx(LOG_ERR, + "ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", + modbus_strerror(errno), + regs[H_REG_STARTIDX].xaddr, + MAX_H_REGS, + device_path + ); + + /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ + if (errno == EPIPE || errno == EMBBADDATA || errno == EMBBADCRC) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + + /* no COIL, INPUT_B or INPUT_R register regions to read */ + + return rval; } /* Read a modbus register */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) { - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x00FF; - uint mask16 = 0xFFFF; - - switch (type) { - case COIL: - rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_B: - rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); - *(uint *)data = *(uint *)data & mask8; - break; - case INPUT_R: - rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; - case HOLDING: - rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); - *(uint *)data = *(uint *)data & mask16; - break; + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) # pragma GCC diagnostic push #endif @@ -678,599 +682,599 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) # pragma GCC diagnostic pop #endif - } - if (rval == -1) { - upslogx(LOG_ERR, - "ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ - if (errno == EPIPE || errno == EMBBADDATA || errno == EMBBADCRC) { - upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; + } + if (rval == -1) { + upslogx(LOG_ERR, + "ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ + if (errno == EPIPE || errno == EMBBADDATA || errno == EMBBADCRC) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; } /* write a modbus register */ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) { - int rval = -1; - - /* register bit masks */ - uint mask8 = 0x00FF; - uint mask16 = 0xFFFF; - - switch (type) { - case COIL: - *(uint *)data = *(uint *)data & mask8; - rval = modbus_write_bit(mb, addr, *(uint8_t *)data); - break; - case HOLDING: - *(uint *)data = *(uint *)data & mask16; - rval = modbus_write_register(mb, addr, *(uint16_t *)data); - break; - - case INPUT_B: - case INPUT_R: + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); - break; - } - if (rval == -1) { - upslogx(LOG_ERR, - "ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", - modbus_strerror(errno), - addr, - (type == COIL) ? "COIL" : - (type == INPUT_B) ? "INPUT_B" : - (type == INPUT_R) ? "INPUT_R" : "HOLDING", - device_path - ); - - /* on BROKEN PIPE error try to reconnect */ - if (errno == EPIPE) { - upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); - modbus_reconnect(); - } - } - upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); - return rval; + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR, + "ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; } /* returns the time elapsed since start in milliseconds */ long time_elapsed(struct timeval *start) { - long rval; - struct timeval end; - - rval = gettimeofday(&end, NULL); - if (rval < 0) { - upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); - } - if (start->tv_usec < end.tv_usec) { - suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; - end.tv_usec -= 1000000 * nsec; - end.tv_sec += nsec; - } - if (start->tv_usec - end.tv_usec > 1000000) { - suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; - end.tv_usec += 1000000 * nsec; - end.tv_sec -= nsec; - } - rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; - - return rval; + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; } /* instant command triggered by upsd */ int upscmd(const char *cmd, const char *arg) { - int rval; - int data; - - if (!strcasecmp(cmd, "load.off")) { - data = 1; - rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); - if (rval == -1) { - upslogx(2, - "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", - modbus_strerror(errno), - regs[FSD].xaddr, - regs[FSD].type, - device_path - ); - upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_FAILED; - } else { - upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); - rval = STAT_INSTCMD_HANDLED; - } - } else { - upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); - rval = STAT_INSTCMD_UNKNOWN; - } - return rval; + int rval; + int data; + + if (!strcasecmp(cmd, "load.off")) { + data = 1; + rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); + if (rval == -1) { + upslogx(2, + "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + regs[FSD].xaddr, + regs[FSD].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); + rval = STAT_INSTCMD_HANDLED; + } + } else { + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; } /* read device state, returns 0 on success or -1 on communication error it formats state depending on register semantics */ int get_dev_state(devreg_t regindx, devstate_t **dvstat) { - int i; /* local index */ - int n; - int rval; /* return value */ - static char *ptr = NULL; /* temporary pointer */ - uint reg_val; /* register value */ + int i; /* local index */ + int n; + int rval; /* return value */ + static char *ptr = NULL; /* temporary pointer */ + uint reg_val; /* register value */ #if READALL_REGS == 0 - uint num; /* register number */ - regtype_t rtype; /* register type */ - int addr; /* register address */ + uint num; /* register number */ + regtype_t rtype; /* register type */ + int addr; /* register address */ #endif - devstate_t *state; /* device state */ + devstate_t *state; /* device state */ - state = *dvstat; + state = *dvstat; #if READALL_REGS == 1 - reg_val = regs_data[regindx]; - rval = 0; + reg_val = regs_data[regindx]; + rval = 0; #elif READALL_REGS == 0 - num = regs[regindx].num; - addr = regs[regindx].xaddr; - rtype = regs[regindx].type; - rval = register_read(mbctx, addr, rtype, ®_val); - if (rval == -1) { - return rval; - } - upsdebugx(3, - "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", - num, - addr, - rtype, - reg_val - ); + num = regs[regindx].num; + addr = regs[regindx].xaddr; + rtype = regs[regindx].type; + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval == -1) { + return rval; + } + upsdebugx(3, + "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", + num, + addr, + rtype, + reg_val + ); #endif - /* process register data */ - switch (regindx) { - case CHRG: /* "ups.charge" */ - if (reg_val == CHRG_NONE) { - state->charge.state = CHRG_NONE; - state->charge.info = chrgs_i[CHRG_NONE]; - } else if (reg_val == CHRG_RECV) { - state->charge.state = CHRG_RECV; - state->charge.info = chrgs_i[CHRG_RECV]; - } else if (reg_val == CHRG_BULK) { - state->charge.state = CHRG_BULK; - state->charge.info = chrgs_i[CHRG_BULK]; - } else if (reg_val == CHRG_ABSR) { - state->charge.state = CHRG_ABSR; - state->charge.info = chrgs_i[CHRG_ABSR]; - } else if (reg_val == CHRG_FLOAT) { - state->charge.state = CHRG_FLOAT; - state->charge.info = chrgs_i[CHRG_FLOAT]; - } - upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); - break; - case BATV: /* "battery.voltage" */ - case LVDC: /* "output.voltage" */ - case LCUR: /* "output.current" */ - if (reg_val != 0) { - state->reg.val.ui16 = reg_val; - double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val.ui16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case TBUF: - case BSOH: - case BCEF: - case VAC: /* "input.voltage" */ - if (reg_val != 0) { - state->reg.val.ui16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - } else { - state->reg.val.ui16 = 0; - state->reg.strval = "0"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BSOC: /* "battery.charge" */ - if (reg_val != 0) { - state->reg.val.ui16 = reg_val; - double fval = (double )reg_val * regs[BSOC].scale; - n = snprintf(NULL, 0, "%.2f", fval); - if (ptr != NULL) { - free(ptr); - } - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - } else { - state->reg.val.ui16 = 0; - state->reg.strval = "0.00"; - } - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case BTMP: /* "battery.temperature" */ - case OTMP: /* "ups.temperature" */ - state->reg.val.ui16 = reg_val; - double fval = reg_val - 273.15; - n = snprintf(NULL, 0, "%.2f", fval); - char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); - if (ptr != NULL) { - free(ptr); - } - ptr = fval_s; - sprintf(fval_s, "%.2f", fval); - state->reg.strval = fval_s; - upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); - break; - case PMNG: /* "ups.status" & "battery.charge" */ - if (reg_val == PMNG_BCKUP) { - state->power.state = PMNG_BCKUP; - state->power.info = pwrmng_i[PMNG_BCKUP]; - } else if (reg_val == PMNG_CHRGN) { - state->power.state = PMNG_CHRGN; - state->power.info = pwrmng_i[PMNG_CHRGN]; - } else if (reg_val == PMNG_BOOST) { - state->power.state = PMNG_BOOST; - state->power.info = pwrmng_i[PMNG_BOOST]; - } else if (reg_val == PMNG_NCHRG) { - state->power.state = PMNG_NCHRG; - state->power.info = pwrmng_i[PMNG_NCHRG]; - } - upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); - break; - case PRDN: /* "ups.model" */ - for (i = 0; i < DEV_NUMOF_MODELS; i++) { - if (prdnm_i[i].val == reg_val) { - break; - } - } - state->product.val = reg_val; - state->product.name = prdnm_i[i].name; - upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); - break; - case BSTA: - if (reg_val & BSTA_REVPOL_M) { - bsta->alrm[BSTA_REVPOL_I].actv = 1; - } else { - bsta->alrm[BSTA_REVPOL_I].actv = 0; - } - if (reg_val & BSTA_NOCNND_M) { - bsta->alrm[BSTA_NOCNND_I].actv = 1; - } else { - bsta->alrm[BSTA_NOCNND_I].actv = 0; - } - if (reg_val & BSTA_CLSHCR_M) { - bsta->alrm[BSTA_CLSHCR_I].actv = 1; - } else { - bsta->alrm[BSTA_CLSHCR_I].actv = 0; - } - if (reg_val & BSTA_SULPHD_M) { - bsta->alrm[BSTA_SULPHD_I].actv = 1; - } else { - bsta->alrm[BSTA_SULPHD_I].actv = 0; - } - if (reg_val & BSTA_CHEMNS_M) { - bsta->alrm[BSTA_CHEMNS_I].actv = 1; - } else { - bsta->alrm[BSTA_CHEMNS_I].actv = 0; - } - if (reg_val & BSTA_CNNFLT_M) { - bsta->alrm[BSTA_CNNFLT_I].actv = 1; - } else { - bsta->alrm[BSTA_CNNFLT_I].actv = 0; - } - state->alrm = bsta; - break; - case SCSH: - if (reg_val & SHSC_HIRESI_M) { - shsc->alrm[SHSC_HIRESI_I].actv = 1; - } else { - shsc->alrm[SHSC_HIRESI_I].actv = 0; - } - if (reg_val & SHSC_LOCHEF_M) { - shsc->alrm[SHSC_LOCHEF_I].actv = 1; - } else { - shsc->alrm[SHSC_LOCHEF_I].actv = 0; - } - if (reg_val & SHSC_LOEFCP_M) { - shsc->alrm[SHSC_LOEFCP_I].actv = 1; - } else { - shsc->alrm[SHSC_LOEFCP_I].actv = 0; - } - if (reg_val & SHSC_LOWSOC_M) { - shsc->alrm[SHSC_LOWSOC_I].actv = 1; - } else { - shsc->alrm[SHSC_LOWSOC_I].actv = 0; - } - state->alrm = shsc; - break; - case BVAL: - if (reg_val & BVAL_HIALRM_M) { - bval->alrm[BVAL_HIALRM_I].actv = 1; - } else { - bval->alrm[BVAL_HIALRM_I].actv = 0; - } - if (reg_val & BVAL_LOALRM_M) { - bval->alrm[BVAL_LOALRM_I].actv = 1; - } else { - bval->alrm[BVAL_LOALRM_I].actv = 0; - } - if (reg_val & BVAL_BSTSFL_M) { - bval->alrm[BVAL_BSTSFL_I].actv = 1; - } else { - bval->alrm[BVAL_BSTSFL_I].actv = 0; - } - state->alrm = bval; - break; - case BTSF: - if (reg_val & BTSF_FCND_M) { - btsf->alrm[BTSF_FCND_I].actv = 1; - } else { - btsf->alrm[BTSF_FCND_I].actv = 0; - } - if (reg_val & BTSF_NCND_M) { - btsf->alrm[BTSF_NCND_I].actv = 1; - } else { - btsf->alrm[BTSF_NCND_I].actv = 0; - } - state->alrm = btsf; - break; - case DEVF: - if (reg_val & DEVF_RCALRM_M) { - devf->alrm[DEVF_RCALRM_I].actv = 1; - } else { - devf->alrm[DEVF_RCALRM_I].actv = 0; - } - if (reg_val & DEVF_INALRM_M) { - devf->alrm[DEVF_INALRM_I].actv = 1; - } else { - devf->alrm[DEVF_INALRM_I].actv = 0; - } - if (reg_val & DEVF_LFNAVL_M) { - devf->alrm[DEVF_LFNAVL_I].actv = 1; - } else { - devf->alrm[DEVF_LFNAVL_I].actv = 0; - } - state->alrm = devf; - break; - case VACA: - if (reg_val & VACA_HIALRM_M) { - vaca->alrm[VACA_HIALRM_I].actv = 1; - } else { - vaca->alrm[VACA_HIALRM_I].actv = 0; - } - if (reg_val == VACA_LOALRM_M) { - vaca->alrm[VACA_LOALRM_I].actv = 1; - } else { - vaca->alrm[VACA_LOALRM_I].actv = 0; - } - state->alrm = vaca; - break; - case MAIN: - if (reg_val & MAINS_AVAIL_M) { - mains->alrm[MAINS_AVAIL_I].actv = 1; - } else { - mains->alrm[MAINS_AVAIL_I].actv = 0; - } - if (reg_val == SHUTD_REQST_M) { - mains->alrm[SHUTD_REQST_I].actv = 1; - } else { - mains->alrm[SHUTD_REQST_I].actv = 0; - } - state->alrm = mains; - break; - case OBTA: - if (reg_val == OBTA_HIALRM_V) { - obta->alrm[OBTA_HIALRM_I].actv = 1; - } - state->alrm = obta; - break; + /* process register data */ + switch (regindx) { + case CHRG: /* "ups.charge" */ + if (reg_val == CHRG_NONE) { + state->charge.state = CHRG_NONE; + state->charge.info = chrgs_i[CHRG_NONE]; + } else if (reg_val == CHRG_RECV) { + state->charge.state = CHRG_RECV; + state->charge.info = chrgs_i[CHRG_RECV]; + } else if (reg_val == CHRG_BULK) { + state->charge.state = CHRG_BULK; + state->charge.info = chrgs_i[CHRG_BULK]; + } else if (reg_val == CHRG_ABSR) { + state->charge.state = CHRG_ABSR; + state->charge.info = chrgs_i[CHRG_ABSR]; + } else if (reg_val == CHRG_FLOAT) { + state->charge.state = CHRG_FLOAT; + state->charge.info = chrgs_i[CHRG_FLOAT]; + } + upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); + break; + case BATV: /* "battery.voltage" */ + case LVDC: /* "output.voltage" */ + case LCUR: /* "output.current" */ + if (reg_val != 0) { + state->reg.val.ui16 = reg_val; + double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val.ui16 = 0; + state->reg.strval = "0.00"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case TBUF: + case BSOH: + case BCEF: + case VAC: /* "input.voltage" */ + if (reg_val != 0) { + state->reg.val.ui16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + } else { + state->reg.val.ui16 = 0; + state->reg.strval = "0"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case BSOC: /* "battery.charge" */ + if (reg_val != 0) { + state->reg.val.ui16 = reg_val; + double fval = (double )reg_val * regs[BSOC].scale; + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val.ui16 = 0; + state->reg.strval = "0.00"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case BTMP: /* "battery.temperature" */ + case OTMP: /* "ups.temperature" */ + state->reg.val.ui16 = reg_val; + double fval = reg_val - 273.15; + n = snprintf(NULL, 0, "%.2f", fval); + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + if (ptr != NULL) { + free(ptr); + } + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case PMNG: /* "ups.status" & "battery.charge" */ + if (reg_val == PMNG_BCKUP) { + state->power.state = PMNG_BCKUP; + state->power.info = pwrmng_i[PMNG_BCKUP]; + } else if (reg_val == PMNG_CHRGN) { + state->power.state = PMNG_CHRGN; + state->power.info = pwrmng_i[PMNG_CHRGN]; + } else if (reg_val == PMNG_BOOST) { + state->power.state = PMNG_BOOST; + state->power.info = pwrmng_i[PMNG_BOOST]; + } else if (reg_val == PMNG_NCHRG) { + state->power.state = PMNG_NCHRG; + state->power.info = pwrmng_i[PMNG_NCHRG]; + } + upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); + break; + case PRDN: /* "ups.model" */ + for (i = 0; i < DEV_NUMOF_MODELS; i++) { + if (prdnm_i[i].val == reg_val) { + break; + } + } + state->product.val = reg_val; + state->product.name = prdnm_i[i].name; + upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); + break; + case BSTA: + if (reg_val & BSTA_REVPOL_M) { + bsta->alrm[BSTA_REVPOL_I].actv = 1; + } else { + bsta->alrm[BSTA_REVPOL_I].actv = 0; + } + if (reg_val & BSTA_NOCNND_M) { + bsta->alrm[BSTA_NOCNND_I].actv = 1; + } else { + bsta->alrm[BSTA_NOCNND_I].actv = 0; + } + if (reg_val & BSTA_CLSHCR_M) { + bsta->alrm[BSTA_CLSHCR_I].actv = 1; + } else { + bsta->alrm[BSTA_CLSHCR_I].actv = 0; + } + if (reg_val & BSTA_SULPHD_M) { + bsta->alrm[BSTA_SULPHD_I].actv = 1; + } else { + bsta->alrm[BSTA_SULPHD_I].actv = 0; + } + if (reg_val & BSTA_CHEMNS_M) { + bsta->alrm[BSTA_CHEMNS_I].actv = 1; + } else { + bsta->alrm[BSTA_CHEMNS_I].actv = 0; + } + if (reg_val & BSTA_CNNFLT_M) { + bsta->alrm[BSTA_CNNFLT_I].actv = 1; + } else { + bsta->alrm[BSTA_CNNFLT_I].actv = 0; + } + state->alrm = bsta; + break; + case SCSH: + if (reg_val & SHSC_HIRESI_M) { + shsc->alrm[SHSC_HIRESI_I].actv = 1; + } else { + shsc->alrm[SHSC_HIRESI_I].actv = 0; + } + if (reg_val & SHSC_LOCHEF_M) { + shsc->alrm[SHSC_LOCHEF_I].actv = 1; + } else { + shsc->alrm[SHSC_LOCHEF_I].actv = 0; + } + if (reg_val & SHSC_LOEFCP_M) { + shsc->alrm[SHSC_LOEFCP_I].actv = 1; + } else { + shsc->alrm[SHSC_LOEFCP_I].actv = 0; + } + if (reg_val & SHSC_LOWSOC_M) { + shsc->alrm[SHSC_LOWSOC_I].actv = 1; + } else { + shsc->alrm[SHSC_LOWSOC_I].actv = 0; + } + state->alrm = shsc; + break; + case BVAL: + if (reg_val & BVAL_HIALRM_M) { + bval->alrm[BVAL_HIALRM_I].actv = 1; + } else { + bval->alrm[BVAL_HIALRM_I].actv = 0; + } + if (reg_val & BVAL_LOALRM_M) { + bval->alrm[BVAL_LOALRM_I].actv = 1; + } else { + bval->alrm[BVAL_LOALRM_I].actv = 0; + } + if (reg_val & BVAL_BSTSFL_M) { + bval->alrm[BVAL_BSTSFL_I].actv = 1; + } else { + bval->alrm[BVAL_BSTSFL_I].actv = 0; + } + state->alrm = bval; + break; + case BTSF: + if (reg_val & BTSF_FCND_M) { + btsf->alrm[BTSF_FCND_I].actv = 1; + } else { + btsf->alrm[BTSF_FCND_I].actv = 0; + } + if (reg_val & BTSF_NCND_M) { + btsf->alrm[BTSF_NCND_I].actv = 1; + } else { + btsf->alrm[BTSF_NCND_I].actv = 0; + } + state->alrm = btsf; + break; + case DEVF: + if (reg_val & DEVF_RCALRM_M) { + devf->alrm[DEVF_RCALRM_I].actv = 1; + } else { + devf->alrm[DEVF_RCALRM_I].actv = 0; + } + if (reg_val & DEVF_INALRM_M) { + devf->alrm[DEVF_INALRM_I].actv = 1; + } else { + devf->alrm[DEVF_INALRM_I].actv = 0; + } + if (reg_val & DEVF_LFNAVL_M) { + devf->alrm[DEVF_LFNAVL_I].actv = 1; + } else { + devf->alrm[DEVF_LFNAVL_I].actv = 0; + } + state->alrm = devf; + break; + case VACA: + if (reg_val & VACA_HIALRM_M) { + vaca->alrm[VACA_HIALRM_I].actv = 1; + } else { + vaca->alrm[VACA_HIALRM_I].actv = 0; + } + if (reg_val == VACA_LOALRM_M) { + vaca->alrm[VACA_LOALRM_I].actv = 1; + } else { + vaca->alrm[VACA_LOALRM_I].actv = 0; + } + state->alrm = vaca; + break; + case MAIN: + if (reg_val & MAINS_AVAIL_M) { + mains->alrm[MAINS_AVAIL_I].actv = 1; + } else { + mains->alrm[MAINS_AVAIL_I].actv = 0; + } + if (reg_val == SHUTD_REQST_M) { + mains->alrm[SHUTD_REQST_I].actv = 1; + } else { + mains->alrm[SHUTD_REQST_I].actv = 0; + } + state->alrm = mains; + break; + case OBTA: + if (reg_val == OBTA_HIALRM_V) { + obta->alrm[OBTA_HIALRM_I].actv = 1; + } + state->alrm = obta; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" #endif - /* All enum cases defined as of the time of coding - * have been covered above. Handle later definitions, - * memory corruptions and buggy inputs below... - */ - default: - state->reg.val.ui16 = reg_val; - n = snprintf(NULL, 0, "%d", reg_val); - if (ptr != NULL) { - free(ptr); - } - char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); - ptr = reg_val_s; - sprintf(reg_val_s, "%d", reg_val); - state->reg.strval = reg_val_s; - break; + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: + state->reg.val.ui16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic pop #endif - } + } - return rval; + return rval; } /* get driver configuration parameters */ -void get_config_vars() +void get_config_vars(void) { - /* check if device manufacturer is set ang get the value */ - if (testvar("device_mfr")) { - device_mfr = getval("device_mfr"); - } - upsdebugx(2, "device_mfr %s", device_mfr); - - /* check if device model is set ang get the value */ - if (testvar("device_model")) { - device_model = getval("device_model"); - } - upsdebugx(2, "device_model %s", device_model); - - /* check if serial baud rate is set ang get the value */ - if (testvar("ser_baud_rate")) { - ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); - } - upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); - - /* check if serial parity is set ang get the value */ - if (testvar("ser_parity")) { - /* Dereference the char* we get */ - char *sp = getval("ser_parity"); - if (sp) { - /* TODO? Sanity-check the char we get? */ - ser_parity = *sp; - } else { - upsdebugx(2, "Could not determine ser_parity, will keep default"); - } - } - upsdebugx(2, "ser_parity %c", ser_parity); - - /* check if serial data bit is set ang get the value */ - if (testvar("ser_data_bit")) { - ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); - } - upsdebugx(2, "ser_data_bit %d", ser_data_bit); - - /* check if serial stop bit is set ang get the value */ - if (testvar("ser_stop_bit")) { - ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); - } - upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); - - /* check if device ID is set ang get the value */ - if (testvar("rio_slave_id")) { - rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); - } - upsdebugx(2, "rio_slave_id %d", rio_slave_id); - - /* check if response time out (s) is set ang get the value */ - if (testvar("mod_resp_to_s")) { - mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); - } - upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); - - /* check if response time out (us) is set ang get the value */ - if (testvar("mod_resp_to_us")) { - mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); - if (mod_resp_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); - } - } - upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); - - /* check if byte time out (s) is set ang get the value */ - if (testvar("mod_byte_to_s")) { - mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); - } - upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); - - /* check if byte time out (us) is set ang get the value */ - if (testvar("mod_byte_to_us")) { - mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); - if (mod_byte_to_us > 999999) { - fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); - } - } - upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); + /* check if device manufacturer is set and get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set and get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set and get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set and get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set and get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set and get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set and get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); + + /* check if response time out (s) is set and get the value */ + if (testvar("mod_resp_to_s")) { + mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); + } + upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); + + /* check if response time out (us) is set and get the value */ + if (testvar("mod_resp_to_us")) { + mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); + if (mod_resp_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } + } + upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); + + /* check if byte time out (s) is set and get the value */ + if (testvar("mod_byte_to_s")) { + mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); + } + upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); + + /* check if byte time out (us) is set and get the value */ + if (testvar("mod_byte_to_us")) { + mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); + if (mod_byte_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } + } + upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); } /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port) { - modbus_t *mb; - char *sp; - if (strstr(port, "/dev/tty") != NULL) { - mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); - } - } else if ((sp = strchr(port, ':')) != NULL) { - char *tcp_port = xmalloc(sizeof(sp)); - strcpy(tcp_port, sp + 1); - *sp = '\0'; - mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - free(tcp_port); - } else { - mb = modbus_new_tcp(port, 502); - if (mb == NULL) { - upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); - } - } - return mb; + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; } /* reconnect to modbus server upon connection error */ void modbus_reconnect(void) { - int rval; + int rval; - upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); + upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); - /* clear current modbus context */ - modbus_close(mbctx); - modbus_free(mbctx); + /* clear current modbus context */ + modbus_close(mbctx); + modbus_free(mbctx); - /* open communication port */ - mbctx = modbus_new(device_path); - if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); - } + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } - /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); - if (rval < 0) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); - } + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } - /* connect to modbus device */ - if (modbus_connect(mbctx) == -1) { - modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); - } + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } /* set modbus response timeout */ #if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) @@ -1280,7 +1284,7 @@ void modbus_reconnect(void) fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); } #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) - { /* see comments above */ + { /* see comments above */ struct timeval to; memset(&to, 0, sizeof(struct timeval)); to.tv_sec = mod_resp_to_s; @@ -1298,7 +1302,7 @@ void modbus_reconnect(void) fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); } #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) - { /* see comments above */ + { /* see comments above */ struct timeval to; memset(&to, 0, sizeof(struct timeval)); to.tv_sec = mod_byte_to_s; @@ -1308,3 +1312,4 @@ void modbus_reconnect(void) /* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ #endif /* NUT_MODBUS_TIMEOUT_ARG_* */ } + diff --git a/drivers/adelsystem_cbi.h b/drivers/adelsystem_cbi.h index a94c716b3b..250b30469b 100644 --- a/drivers/adelsystem_cbi.h +++ b/drivers/adelsystem_cbi.h @@ -1,32 +1,36 @@ -/* adelsystem_cbi.h - Driver for ADELSYSTEM CB/CBI DC-UPS +/* adelsystem_cbi.h - Driver for ADELSYSTEM CB/CBI DC-UPS * - * Copyright (C) - * 2022 Dimitris Economou + * Copyright (C) + * 2022 Dimitris Economou * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ +/* + * code indentation with tabstop=4 + */ + #ifndef ADELSYSTEM_CBI_H #define ADELSYSTEM_CBI_H #include /* UPS device details */ -#define DEVICE_MFR "ADELSYSTEM" +#define DEVICE_MFR "ADELSYSTEM" #define DEVICE_TYPE "DC-UPS" #define DEVICE_MODEL "CB/CBI" @@ -73,81 +77,81 @@ /* definition of register type */ enum regtype { - COIL = 0, - INPUT_B, - INPUT_R, - HOLDING + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING }; typedef enum regtype regtype_t; /* product name info, "device.model" */ struct prodname { - uint16_t val; - char *name; + uint16_t val; + char *name; }; typedef struct prodname prodname_t; static prodname_t prdnm_i[] = { - {1, "CBI1235A"}, - {2, "CBI2420A"}, - {3, "CBI4810A"}, - {4, "CBI2801224"}, - {7, "CBI480W"}, - {8, "CB122410A"}, - {9, "CB480W"}, - {11, "CB12245AJ"}, - {12, "CB1235A"}, - {13, "CB2420A"}, - {14, "CB4810A"} + {1, "CBI1235A"}, + {2, "CBI2420A"}, + {3, "CBI4810A"}, + {4, "CBI2801224"}, + {7, "CBI480W"}, + {8, "CB122410A"}, + {9, "CB480W"}, + {11, "CB12245AJ"}, + {12, "CB1235A"}, + {13, "CB2420A"}, + {14, "CB4810A"} }; /* charging status info, "battery.charger.status" */ static char *chrgs_i[] = { - "none", - "resting", /* recovering */ - "charging", /* bulk */ - "charging", /* absorb */ - "floating" /* float */ + "none", + "resting", /* recovering */ + "charging", /* bulk */ + "charging", /* absorb */ + "floating" /* float */ }; struct chrgs { - int state; - char *info; + int state; + char *info; }; typedef struct chrgs chrgs_t; /* power management info, "ups.status", "battery.charger.status" */ static char *pwrmng_i[] = { - "backup", /* "OB", "discharging" */ - "charging", /* "OL" */ - "boost", - "not charging" + "backup", /* "OB", "discharging" */ + "charging", /* "OL" */ + "boost", + "not charging" }; struct pwrmng { - int state; - char *info; + int state; + char *info; }; typedef struct pwrmng pwrmng_t; /* general modbus register value */ struct reg { - union val { - uint16_t ui16; - uint8_t ui8; - } val; - char *strval; + union val { + uint16_t ui16; + uint8_t ui8; + } val; + char *strval; }; typedef struct reg reg_t; /* general alarm struct */ struct alrm { - int actv; /* active flag */ - char *descr; /* description field */ + int actv; /* active flag */ + char *descr; /* description field */ }; typedef struct alrm alrm_t; /* general alarm array */ struct alrm_ar { - int alrm_c; /* alarm count */ - alrm_t *alrm; /* alarm array */ + int alrm_c; /* alarm count */ + alrm_t *alrm; /* alarm array */ }; typedef struct alrm_ar alrm_ar_t; @@ -172,223 +176,223 @@ typedef struct alrm_ar alrm_ar_t; #define PRDN_MAX 14 /* Mains alarm masks */ -#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_M 0x0002 /* shutdown requested */ +#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_M 0x0002 /* shutdown requested */ /* Mains alarm indices */ -#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ -#define SHUTD_REQST_I 1 /* shutdown requested */ +#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_I 1 /* shutdown requested */ /* AC input voltage alarm masks */ -#define VACA_HIALRM_M 0x0001 /* high alarm */ -#define VACA_LOALRM_M 0x0002 /* low alarm */ +#define VACA_HIALRM_M 0x0001 /* high alarm */ +#define VACA_LOALRM_M 0x0002 /* low alarm */ /* AC input voltage alarm indices */ -#define VACA_HIALRM_I 0 /* high alarm */ -#define VACA_LOALRM_I 1 /* low alarm */ +#define VACA_HIALRM_I 0 /* high alarm */ +#define VACA_LOALRM_I 1 /* low alarm */ /* Onboard temperature alarm value */ -#define OBTA_HIALRM_V 1 /* high alarm */ +#define OBTA_HIALRM_V 1 /* high alarm */ /* Onboard temperature alarm index */ -#define OBTA_HIALRM_I 0 /* high alarm */ +#define OBTA_HIALRM_I 0 /* high alarm */ /* Device failure alarm masks */ -#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ -#define DEVF_INALRM_M 0x0006 /* internal failure */ -#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ +#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ +#define DEVF_INALRM_M 0x0006 /* internal failure */ +#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ /* Device failure alarm indices */ -#define DEVF_RCALRM_I 0 /* rectifier failure */ -#define DEVF_INALRM_I 1 /* internal failure */ -#define DEVF_LFNAVL_I 2 /* lifetest not available */ +#define DEVF_RCALRM_I 0 /* rectifier failure */ +#define DEVF_INALRM_I 1 /* internal failure */ +#define DEVF_LFNAVL_I 2 /* lifetest not available */ /* Battery temp sensor failure alarm masks */ -#define BTSF_FCND_M 0x0001 /* connection fault */ -#define BTSF_NCND_M 0x0002 /* not connected */ +#define BTSF_FCND_M 0x0001 /* connection fault */ +#define BTSF_NCND_M 0x0002 /* not connected */ /* Battery temp sensor failure alarm indices */ -#define BTSF_FCND_I 0 /* connection fault */ -#define BTSF_NCND_I 1 /* not connected */ +#define BTSF_FCND_I 0 /* connection fault */ +#define BTSF_NCND_I 1 /* not connected */ /* Battery voltage alarm masks */ -#define BVAL_HIALRM_M 0x0001 /* high voltage */ -#define BVAL_LOALRM_M 0x0002 /* low voltage */ -#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ +#define BVAL_HIALRM_M 0x0001 /* high voltage */ +#define BVAL_LOALRM_M 0x0002 /* low voltage */ +#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ /* Battery voltage alarm indices */ -#define BVAL_HIALRM_I 0 /* high voltage */ -#define BVAL_LOALRM_I 1 /* low voltage */ -#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ +#define BVAL_HIALRM_I 0 /* high voltage */ +#define BVAL_LOALRM_I 1 /* low voltage */ +#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ /* SoH and SoC alarm masks */ -#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ -#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ -#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ -#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ +#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ /* SoH and SoC alarm indices */ -#define SHSC_HIRESI_I 0 /* high internal resistance */ -#define SHSC_LOCHEF_I 1 /* low charge efficiency */ -#define SHSC_LOEFCP_I 2 /* low effective capacity */ -#define SHSC_LOWSOC_I 3 /* low state of charge */ +#define SHSC_HIRESI_I 0 /* high internal resistance */ +#define SHSC_LOCHEF_I 1 /* low charge efficiency */ +#define SHSC_LOEFCP_I 2 /* low effective capacity */ +#define SHSC_LOWSOC_I 3 /* low state of charge */ /* Battery status alarm masks */ -#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ -#define BSTA_NOCNND_M 0x0002 /* not connected */ -#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ -#define BSTA_SULPHD_M 0x0008 /* sulphated */ -#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ -#define BSTA_CNNFLT_M 0x0020 /* connection fault */ +#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ +#define BSTA_NOCNND_M 0x0002 /* not connected */ +#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ +#define BSTA_SULPHD_M 0x0008 /* sulphated */ +#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT_M 0x0020 /* connection fault */ /* Battery status alarm indices */ -#define BSTA_REVPOL_I 0 /* reversed polarity */ -#define BSTA_NOCNND_I 1 /* not connected */ -#define BSTA_CLSHCR_I 2 /* cell short circuit */ -#define BSTA_SULPHD_I 3 /* sulphated */ -#define BSTA_CHEMNS_I 4 /* chemistry not supported */ -#define BSTA_CNNFLT_I 5 /* connection fault */ +#define BSTA_REVPOL_I 0 /* reversed polarity */ +#define BSTA_NOCNND_I 1 /* not connected */ +#define BSTA_CLSHCR_I 2 /* cell short circuit */ +#define BSTA_SULPHD_I 3 /* sulphated */ +#define BSTA_CHEMNS_I 4 /* chemistry not supported */ +#define BSTA_CNNFLT_I 5 /* connection fault */ /* Allocate alarm arrays */ inline -alrm_ar_t *alloc_alrm_ar(int as, size_t n) +alrm_ar_t *alloc_alrm_ar(int as, size_t n) { - alrm_ar_t *ret = xcalloc(sizeof(alrm_t) + n, 1); - if (ret) { - memcpy(ret, - &(alrm_ar_t const) { - .alrm_c = as - }, - sizeof(alrm_ar_t) - ); - } - return ret; + alrm_ar_t *ret = xcalloc(sizeof(alrm_t) + n, 1); + if (ret) { + memcpy(ret, + &(alrm_ar_t const) { + .alrm_c = as + }, + sizeof(alrm_ar_t) + ); + } + return ret; } /* Initialize alarm arrays */ inline -void alrm_ar_init(alrm_ar_t *ar_ptr, alrm_t *a_ptr, int as) +void alrm_ar_init(alrm_ar_t *ar_ptr, alrm_t *a_ptr, int as) { - ar_ptr->alrm_c = as; - ar_ptr->alrm = a_ptr; + ar_ptr->alrm_c = as; + ar_ptr->alrm = a_ptr; } /* input mains and shutdown alarms */ static alrm_t mains_ar[] = { - {0, "input voltage not available"}, - {0, "ups shutdown requested"} + {0, "input voltage not available"}, + {0, "ups shutdown requested"} }; static int mains_c = 2; static alrm_ar_t *mains; /* AC input voltage alarms */ static alrm_t vaca_ar[] = { - {0, "input voltage high alarm"}, - {0, "input voltage low alarm"} + {0, "input voltage high alarm"}, + {0, "input voltage low alarm"} }; static int vaca_c = 2; static alrm_ar_t *vaca; /* device failure alarms */ static alrm_t devf_ar[] = { - {0, "UPS rectifier failure"}, - {0, "UPS internal failure"}, - {0, "UPS lifetest not available"} + {0, "UPS rectifier failure"}, + {0, "UPS internal failure"}, + {0, "UPS lifetest not available"} }; static int devf_c = 3; static alrm_ar_t *devf; /* battery sensor failure alarms */ static alrm_t btsf_ar[] = { - {0, "battery temp sensor connection fault"}, - {0, "battery temp sensor not connected"} + {0, "battery temp sensor connection fault"}, + {0, "battery temp sensor not connected"} }; static int btsf_c = 2; static alrm_ar_t *btsf; /* battery voltage alarms */ static alrm_t bval_ar[] = { - {0, "battery high voltage"}, - {0, "battery low voltage"}, - {0, "battery start with battery flat"} + {0, "battery high voltage"}, + {0, "battery low voltage"}, + {0, "battery start with battery flat"} }; static int bval_c = 3; static alrm_ar_t *bval; /* battery SoH and SoC alarms */ static alrm_t shsc_ar[] = { - {0, "battery high internal resistance"}, - {0, "battery low charge efficiency"}, - {0, "battery low effective capacity"}, - {0, "battery low state of charge"} + {0, "battery high internal resistance"}, + {0, "battery low charge efficiency"}, + {0, "battery low effective capacity"}, + {0, "battery low state of charge"} }; static int shsc_c = 4; static alrm_ar_t *shsc; /* battery status alarm */ static alrm_t bsta_ar[] = { - {0, "battery reversed polarity"}, - {0, "battery not connected"}, - {0, "battery cell short circuit"}, - {0, "battery sulphated"}, - {0, "battery chemistry not supported"}, - {0, "battery connection fault"} + {0, "battery reversed polarity"}, + {0, "battery not connected"}, + {0, "battery cell short circuit"}, + {0, "battery sulphated"}, + {0, "battery chemistry not supported"}, + {0, "battery connection fault"} }; static int bsta_c = 4; static alrm_ar_t *bsta; /* onboard temperature alarm */ static alrm_t obta_ar[] = { - {0, "onboard temperature high"} + {0, "onboard temperature high"} }; static int obta_c = 4; static alrm_ar_t *obta; /* UPS device reg enum */ enum devreg { - CHRG = 4, /* Charging status, "battery.charger.status" */ - BATV = 7, /* Battery voltage, "battery.voltage" */ - BCEF = 18, /* Battery charge efficiency factor (CEF) */ - BSOH = 20, /* Battery state-of-health */ - BSOC = 22, /* Battery state-of-charge, "battery.charge" */ - BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ - PMNG = 5, /* Power management, "ups.status" */ - OTMP = 28, /* Onboard temperature, "ups.temperature" */ - PRDN = 67, /* Product name, "ups.model" */ - VAC = 29, /* AC voltage, "input.voltage" */ - LVDC = 10, /* Load voltage, "output.voltage" */ - LCUR = 19, /* Load current, "output.current" */ - BINH = 87, /* Backup inhibit */ - FSD = 40, /* Force shutdown */ - TBUF = 103, /* Time buffering, "battery.runtime" */ - BSTA = 31, /* Battery status alarms */ - SCSH, /* SoH and SoC alarms */ - BVAL = 34, /* Battery voltage alarm */ - BTSF = 43, /* Battery temp sensor failure */ - DEVF = 42, /* Device failure */ - OBTA = 46, /* On board temp alarm */ - VACA = 44, /* VAC alarms */ - MAIN /* Mains status */ + CHRG = 4, /* Charging status, "battery.charger.status" */ + BATV = 7, /* Battery voltage, "battery.voltage" */ + BCEF = 18, /* Battery charge efficiency factor (CEF) */ + BSOH = 20, /* Battery state-of-health */ + BSOC = 22, /* Battery state-of-charge, "battery.charge" */ + BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ + PMNG = 5, /* Power management, "ups.status" */ + OTMP = 28, /* Onboard temperature, "ups.temperature" */ + PRDN = 67, /* Product name, "ups.model" */ + VAC = 29, /* AC voltage, "input.voltage" */ + LVDC = 10, /* Load voltage, "output.voltage" */ + LCUR = 19, /* Load current, "output.current" */ + BINH = 87, /* Backup inhibit */ + FSD = 40, /* Force shutdown */ + TBUF = 103, /* Time buffering, "battery.runtime" */ + BSTA = 31, /* Battery status alarms */ + SCSH, /* SoH and SoC alarms */ + BVAL = 34, /* Battery voltage alarm */ + BTSF = 43, /* Battery temp sensor failure */ + DEVF = 42, /* Device failure */ + OBTA = 46, /* On board temp alarm */ + VACA = 44, /* VAC alarms */ + MAIN /* Mains status */ }; typedef enum devreg devreg_t; /* UPS register attributes */ struct regattr { - int num; - int saddr; /* register start address */ - int xaddr; /* register hex address */ - float scale; /* scale */ - regtype_t type; /* register type */ + int num; + int saddr; /* register start address */ + int xaddr; /* register hex address */ + float scale; /* scale */ + regtype_t type; /* register type */ }; typedef struct regattr regattr_t; /* UPS device state info union */ union devstate { - prodname_t product; /* ups model name */ - chrgs_t charge; /* charging status */ - pwrmng_t power; /* ups status */ - reg_t reg; /* state register*/ - alrm_ar_t *alrm; /* alarm statuses */ + prodname_t product; /* ups model name */ + chrgs_t charge; /* charging status */ + pwrmng_t power; /* ups status */ + reg_t reg; /* state register*/ + alrm_ar_t *alrm; /* alarm statuses */ }; typedef union devstate devstate_t; @@ -398,131 +402,130 @@ static uint16_t regs_data[MAX_H_REGS]; /* ADELSYSTEM CBI registers */ static regattr_t regs[] = { - {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ - {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ - {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ - {40004, 0, 0, 1, HOLDING}, - {40005, 0, 0, 1, HOLDING}, /* Charging status */ - {40006, 0, 0, 1, HOLDING}, /* Power management - * 0:Backup 1:Charging 2:boost 3:Not charging - */ - {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ - {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ - {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ - {40010, 0, 0, 1, HOLDING}, /* Software ID */ - {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ - {40012, 0, 0, 1, HOLDING}, - {40013, 0, 0, 1, HOLDING}, - {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ - {40015, 0, 0, 1, HOLDING}, - {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ - {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ - {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ - {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ - {40020, 0, 0, 1, HOLDING}, /* Output load current */ - {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ - {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ - {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ - {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ - {40025, 0, 0, 1, HOLDING}, - {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ - {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ - {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ - {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ - {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ - {40031, 0, 0, 1, HOLDING}, - {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ - {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ - {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ - {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ - {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ - {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ - {40038, 0, 0, 1, HOLDING}, /* Load alarm */ - {40039, 0, 0, 1, HOLDING}, /* Device variant */ - {40040, 0, 0, 1, HOLDING}, - {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ - {40042, 0, 0, 1, HOLDING}, - {40043, 0, 0, 1, HOLDING}, /* Device failure */ - {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ - {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ - {40046, 0, 0, 1, HOLDING}, /* Mains status */ - {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ - {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ - {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ - {40050, 0, 0, .1, HOLDING}, /* Ah charged */ - {40051, 0, 0, 1, HOLDING}, /* Total run time */ - {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ - {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ - {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ - {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ - {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ - {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ - {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ - {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ - {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ - {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ - {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ - {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ - {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ - {40065, 0, 0, 1, HOLDING}, /* History clear all */ - {40066, 0, 0, 1, HOLDING}, /* Factory settings */ - {40067, 0, 0, 1, HOLDING}, /* Product name */ - {40068, 0, 0, 1, HOLDING}, - {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ - {40070, 0, 0, 1, HOLDING}, - {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ - {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ - {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ - {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ - {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ - {40076, 0, 0, 1, HOLDING}, - {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ - {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ - {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ - {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ - {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ - {40082, 0, 0, 1, HOLDING}, /* Float voltage */ - {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ - {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ - {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ - {40086, 0, 0, 1, HOLDING}, - {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ - {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed - * 1 = Backup not allowed - */ - {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ - {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ - {40091, 0, 0, 1, HOLDING}, - {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ - {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ - {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ - {40095, 0, 0, 1, HOLDING}, - {40096, 0, 0, 1, HOLDING}, - {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ - {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ - {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ - {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ - {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ - {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ - {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ - {40104, 0, 0, 1, HOLDING}, /* Time buffering */ - {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ - {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ - {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ - {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ - {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ - {40110, 0, 0, 1, HOLDING}, - {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ - {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ - {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ - {40114, 0, 0, 1, HOLDING}, - {40115, 0, 0, 1, HOLDING}, - {40116, 0, 0, 1, HOLDING}, - {40117, 0, 0, 1, HOLDING}, - {40118, 0, 0, 1, HOLDING}, - {40119, 0, 0, 1, HOLDING}, - {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ - - + {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ + {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ + {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ + {40004, 0, 0, 1, HOLDING}, + {40005, 0, 0, 1, HOLDING}, /* Charging status */ + {40006, 0, 0, 1, HOLDING}, /* Power management + * 0:Backup 1:Charging 2:boost 3:Not charging + */ + {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ + {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ + {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ + {40010, 0, 0, 1, HOLDING}, /* Software ID */ + {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ + {40012, 0, 0, 1, HOLDING}, + {40013, 0, 0, 1, HOLDING}, + {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ + {40015, 0, 0, 1, HOLDING}, + {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ + {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ + {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ + {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ + {40020, 0, 0, 1, HOLDING}, /* Output load current */ + {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ + {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ + {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ + {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ + {40025, 0, 0, 1, HOLDING}, + {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ + {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ + {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ + {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ + {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ + {40031, 0, 0, 1, HOLDING}, + {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ + {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ + {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ + {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ + {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ + {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ + {40038, 0, 0, 1, HOLDING}, /* Load alarm */ + {40039, 0, 0, 1, HOLDING}, /* Device variant */ + {40040, 0, 0, 1, HOLDING}, + {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ + {40042, 0, 0, 1, HOLDING}, + {40043, 0, 0, 1, HOLDING}, /* Device failure */ + {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ + {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ + {40046, 0, 0, 1, HOLDING}, /* Mains status */ + {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ + {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ + {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ + {40050, 0, 0, .1, HOLDING}, /* Ah charged */ + {40051, 0, 0, 1, HOLDING}, /* Total run time */ + {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ + {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ + {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ + {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ + {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ + {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ + {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ + {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ + {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ + {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ + {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ + {40065, 0, 0, 1, HOLDING}, /* History clear all */ + {40066, 0, 0, 1, HOLDING}, /* Factory settings */ + {40067, 0, 0, 1, HOLDING}, /* Product name */ + {40068, 0, 0, 1, HOLDING}, + {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ + {40070, 0, 0, 1, HOLDING}, + {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ + {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ + {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ + {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ + {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ + {40076, 0, 0, 1, HOLDING}, + {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ + {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ + {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ + {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ + {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ + {40082, 0, 0, 1, HOLDING}, /* Float voltage */ + {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ + {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ + {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ + {40086, 0, 0, 1, HOLDING}, + {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed + * 1 = Backup not allowed + */ + {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ + {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ + {40091, 0, 0, 1, HOLDING}, + {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ + {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ + {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ + {40095, 0, 0, 1, HOLDING}, + {40096, 0, 0, 1, HOLDING}, + {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ + {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ + {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ + {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ + {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ + {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ + {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ + {40104, 0, 0, 1, HOLDING}, /* Time buffering */ + {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ + {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ + {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ + {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ + {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ + {40110, 0, 0, 1, HOLDING}, + {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ + {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ + {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ + {40114, 0, 0, 1, HOLDING}, + {40115, 0, 0, 1, HOLDING}, + {40116, 0, 0, 1, HOLDING}, + {40117, 0, 0, 1, HOLDING}, + {40118, 0, 0, 1, HOLDING}, + {40119, 0, 0, 1, HOLDING}, + {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ }; -#endif /* ADELSYSTEM_CBI_H */ \ No newline at end of file +#endif /* ADELSYSTEM_CBI_H */ + From b67ed5aa143ffb176280258fb2fa538eb70af326 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Mar 2019 16:31:50 +0100 Subject: [PATCH 080/700] drivers/main.c : allow to configure debug_min=NUM to ease service debugging --- drivers/main.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/main.c b/drivers/main.c index a3f7026449..c926698fd5 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -321,6 +321,20 @@ static int main_arg(char *var, char *val) if (!strcmp(var, "desc")) return 1; /* handled */ + /* Allow each driver to specify its minimal debugging level - + * admins can set more with command-line args, but can't set + * less without changing config. Should help debug of services. */ + if (!strcmp(var, "debug_min")) { + int lvl = -1; // typeof common/common.c: int nut_debug_level + if ( str_to_int (val, &lvl, 10) && lvl >= 0 ) { + if ( nut_debug_level < lvl ) + nut_debug_level = lvl; + } else { + upslogx(LOG_INFO, "WARNING : Invalid debug_min value found in ups.conf"); + } + return 1; /* handled */ + } + return 0; /* unhandled, pass it through to the driver */ } @@ -353,6 +367,18 @@ static void do_global_args(const char *var, const char *val) do_synchronous=0; } + /* Allow to specify its minimal debugging level for all drivers - + * admins can set more with command-line args, but can't set + * less without changing config. Should help debug of services. */ + if (!strcmp(var, "debug_min")) { + int lvl = -1; // typeof common/common.c: int nut_debug_level + if ( str_to_int (val, &lvl, 10) && lvl >= 0 ) { + if ( nut_debug_level < lvl ) + nut_debug_level = lvl; + } else { + upslogx(LOG_INFO, "WARNING : Invalid debug_min value found in ups.conf global settings"); + } + } /* unrecognized */ } From 81368e28743a715338af55157f926b381483cd5b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Mar 2019 14:52:33 +0100 Subject: [PATCH 081/700] driver/main.c : separate toggles for driver debugging and backgrounding --- drivers/main.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index c926698fd5..3e9864ca87 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -52,6 +52,17 @@ static char *chroot_path = NULL, *user = NULL; /* signal handling */ int exit_flag = 0; +/* should this driver instance go to background (default) + * or stay foregrounded (default if -D options are set on + * command line)? + * Value is tri-state: + * -1 (default) Background the driver process + * 0 User required to not background explicitly, + * or passed -D and current value was -1 + * 1 User required to background even if with -D + */ +static int background_flag = -1; + /* everything else */ static char *pidfn = NULL; static int dump_data = 0; /* Store the update_count requested */ @@ -112,8 +123,10 @@ static void help_msg(void) printf(" -V - print version, then exit\n"); printf(" -L - print parseable list of driver variables\n"); - printf(" -D - raise debugging level\n"); + printf(" -D - raise debugging level (and stay foreground by default)\n"); printf(" -d - dump data to stdout after 'count' updates loop and exit\n"); + printf(" -f - stay foregrounded even if no debugging is enabled\n"); + printf(" -b - stay backgrounded even if debugging is bumped\n"); printf(" -q - raise log level threshold\n"); printf(" -h - display this help\n"); printf(" -k - force shutdown\n"); @@ -572,7 +585,7 @@ int main(int argc, char **argv) /* build the driver's extra (-x) variable table */ upsdrv_makevartable(); - while ((i = getopt(argc, argv, "+a:s:kDd:hx:Lqr:u:Vi:")) != -1) { + while ((i = getopt(argc, argv, "+a:s:kfbDd:hx:Lqr:u:Vi:")) != -1) { switch (i) { case 'a': upsname = optarg; @@ -587,8 +600,18 @@ int main(int argc, char **argv) upsname = optarg; upsname_found = 1; break; + case 'f': + background_flag = 0; + break; + case 'b': + background_flag = 1; + break; case 'D': nut_debug_level++; + if ( background_flag < 0 ) { + /* Only flop from default - stay foreground with debug on */ + background_flag = 0; + } break; case 'd': dump_data = atoi(optarg); @@ -670,7 +693,7 @@ int main(int argc, char **argv) fatal_with_errno(EXIT_FAILURE, "Can't chdir to %s", dflt_statepath()); /* Setup signals to communicate with driver once backgrounded. */ - if ((nut_debug_level == 0) && (!do_forceshutdown)) { + if ((background_flag != 0) && (!do_forceshutdown)) { char buffer[SMALLBUF]; setup_signals(); @@ -785,7 +808,7 @@ int main(int argc, char **argv) if (dstate_getinfo("ups.serial") != NULL) dstate_setinfo("device.serial", "%s", dstate_getinfo("ups.serial")); - if ( (nut_debug_level == 0) && (!dump_data) ) { + if ( (background_flag != 0) && (!dump_data) ) { background(); writepid(pidfn); /* PID changes when backgrounding */ } From 0144a38e8ab965f0e96eb6ac8a2517ccd1ce01ea Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Mar 2019 23:03:04 +0100 Subject: [PATCH 082/700] drivers/main.c : rearrange handling of configured nut_debug_level_global vs nut_debug_level_driver vs ultimately used nut_debug_level --- drivers/main.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index 3e9864ca87..3b7a6e6e2d 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -63,6 +63,16 @@ int exit_flag = 0; */ static int background_flag = -1; +/* Users can pass a -D[...] option to enable debugging. + * For the service tracing purposes, also the ups.conf + * can define a debug_min value in the global or device + * section, to set the minimal debug level (CLI provided + * value less than that would not have effect, can have + * more. + */ +static int nut_debug_level_global = -1; +static int nut_debug_level_driver = -1; + /* everything else */ static char *pidfn = NULL; static int dump_data = 0; /* Store the update_count requested */ @@ -340,10 +350,9 @@ static int main_arg(char *var, char *val) if (!strcmp(var, "debug_min")) { int lvl = -1; // typeof common/common.c: int nut_debug_level if ( str_to_int (val, &lvl, 10) && lvl >= 0 ) { - if ( nut_debug_level < lvl ) - nut_debug_level = lvl; + nut_debug_level_driver = lvl; } else { - upslogx(LOG_INFO, "WARNING : Invalid debug_min value found in ups.conf"); + upslogx(LOG_INFO, "WARNING : Invalid debug_min value found in ups.conf for the driver"); } return 1; /* handled */ } @@ -386,8 +395,7 @@ static void do_global_args(const char *var, const char *val) if (!strcmp(var, "debug_min")) { int lvl = -1; // typeof common/common.c: int nut_debug_level if ( str_to_int (val, &lvl, 10) && lvl >= 0 ) { - if ( nut_debug_level < lvl ) - nut_debug_level = lvl; + nut_debug_level_global = lvl; } else { upslogx(LOG_INFO, "WARNING : Invalid debug_min value found in ups.conf global settings"); } @@ -678,6 +686,21 @@ int main(int argc, char **argv) "Try -h for help."); } + /* CLI debug level can not be smaller than debug_min specified + * in ups.conf, and value specified for a driver config section + * overrides the global one. + */ + { + int nut_debug_level_upsconf = -1 ; + if ( nut_debug_level_global >= 0 && nut_debug_level_driver >= 0 ) { + nut_debug_level_upsconf = nut_debug_level_driver; + } else { + if ( nut_debug_level_global >= 0 ) nut_debug_level_upsconf = nut_debug_level_global; + if ( nut_debug_level_driver >= 0 ) nut_debug_level_upsconf = nut_debug_level_driver; + } + if ( nut_debug_level_upsconf > nut_debug_level ) + nut_debug_level = nut_debug_level_upsconf; + } upsdebugx(1, "debug level is '%d'", nut_debug_level); new_uid = get_user_pwent(user); From 55c16bbfac667d465b9f429e15b24d50c11bde78 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 15:09:48 +0100 Subject: [PATCH 083/700] drivers/main.c: update comments --- drivers/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index 79c89d40cc..173d3a70e2 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -721,8 +721,9 @@ int main(int argc, char **argv) become_user(new_uid); - /* Only switch to statepath if we're not powering off or just dumping data, for discovery */ - /* This avoid case where ie /var is umounted */ + /* Only switch to statepath if we're not powering off + * or not just dumping data (for discovery) */ + /* This avoids case where ie /var is unmounted already */ if ((!do_forceshutdown) && (!dump_data) && (chdir(dflt_statepath()))) fatal_with_errno(EXIT_FAILURE, "Can't chdir to %s", dflt_statepath()); From d764eb3b7fd9b9a427f869c7e4dea4c24f00a7a7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 17:03:37 +0100 Subject: [PATCH 084/700] docs/man/nutupsdrv.txt: document -F/-B for fore/back-ground enforcement, and that -D/-d only default to foregrounding now (can be overridden) --- docs/man/nutupsdrv.txt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/man/nutupsdrv.txt b/docs/man/nutupsdrv.txt index 015dba5f5b..aef9c6b627 100644 --- a/docs/man/nutupsdrv.txt +++ b/docs/man/nutupsdrv.txt @@ -69,8 +69,8 @@ this option should be used for specific needs only. *-D*:: Raise the debugging level. Use this multiple times to see more details. -Running a driver in debug mode will prevent it from backgrounding after -startup. It will keep on logging information to the console until it +Running a driver in debug mode will (by default) prevent it from backgrounding +after startup. It will keep on logging information to the console until it receives a SIGINT (usually Ctrl-C) or SIGTERM signal. + The level of debugging needed depends both on the driver and the @@ -82,6 +82,7 @@ either too limited or too verbose to be of any use. *-d* 'update_count':: Dump the data tree (in upsc-like format) to stdout after running the driver update loop for 'update_count' times and exit. +By default this prevents the driver process from backgrounding after startup. Note that the driver banner will be printed too, so when using this option in scripts, don't forget to trim the first line. @@ -90,6 +91,14 @@ Raise log level threshold. Use this multiple times to log more details. + The debugging comment above also applies here. +*-F*:: +Enforce running the driver as a foreground process, regardless of debugging +or data-dumping settings. + +*-B*:: +Enforce running the driver as a background process, regardless of debugging +or data-dumping settings. + *-i* 'interval':: Set the poll interval for the device. The default value is 2 (in seconds). From c83d55e5d07e6f1cc144fc8b81f7492f7ca43064 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 17:41:15 +0100 Subject: [PATCH 085/700] clients/upsmon.c: align -F/-B options with drivers/main.c --- clients/upsmon.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index e1928c460f..0704711aaa 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -1789,8 +1789,9 @@ static void help(const char *arg_progname) printf(" - fsd: shutdown all primary-mode UPSes (use with caution)\n"); printf(" - reload: reread configuration\n"); printf(" - stop: stop monitoring and exit\n"); - printf(" -D raise debugging level\n"); - printf(" -F run in foreground\n"); + printf(" -D raise debugging level (and stay foreground by default)\n"); + printf(" -F stay foregrounded even if no debugging is enabled\n"); + printf(" -B stay backgrounded even if debugging is bumped\n"); printf(" -h display this help\n"); printf(" -K checks POWERDOWNFLAG, sets exit code to 0 if set\n"); printf(" -p always run privileged (disable privileged parent)\n"); @@ -2021,7 +2022,7 @@ static void check_parent(void) int main(int argc, char *argv[]) { const char *prog = xbasename(argv[0]); - int i, cmd = 0, checking_flag = 0, foreground = 0; + int i, cmd = 0, checking_flag = 0, foreground = -1; printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); @@ -2032,7 +2033,7 @@ int main(int argc, char *argv[]) run_as_user = xstrdup(RUN_AS_USER); - while ((i = getopt(argc, argv, "+DFhic:f:pu:VK46")) != -1) { + while ((i = getopt(argc, argv, "+DFBhic:f:pu:VK46")) != -1) { switch (i) { case 'c': if (!strncmp(optarg, "fsd", strlen(optarg))) @@ -2048,11 +2049,13 @@ int main(int argc, char *argv[]) break; case 'D': nut_debug_level++; - foreground = 1; break; case 'F': foreground = 1; break; + case 'B': + foreground = 0; + break; case 'f': free(configfile); configfile = xstrdup(optarg); @@ -2089,6 +2092,14 @@ int main(int argc, char *argv[]) } } + if (foreground < 0) { + if (nut_debug_level > 0) { + foreground = 1; + } else { + foreground = 0; + } + } + if (cmd) { sendsignal(prog, cmd); exit(EXIT_SUCCESS); From a67361150cd8199c660f47566c88fe628565cf88 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 17:43:39 +0100 Subject: [PATCH 086/700] docs/man/upsmon.txt: document -F/-B for fore/back-ground enforcement, and that -D only defaults to foregrounding now (can be overridden) --- docs/man/upsmon.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index 1bc3c1885f..5006d40e49 100644 --- a/docs/man/upsmon.txt +++ b/docs/man/upsmon.txt @@ -13,7 +13,7 @@ SYNOPSIS *upsmon* -c 'command' -*upsmon* [-D] [-K] [-p] [-u 'user'] +*upsmon* [-D] [-F | -B] [-K] [-p] [-u 'user'] DESCRIPTION ----------- @@ -45,12 +45,15 @@ commands are: "reloading nuances" below if this doesn't work. *-D*:: -Raise the debugging level. upsmon will run in the foreground and prints -information on stdout about the monitoring process. Use this multiple -times for more details. +Raise the debugging level. upsmon will run in the foreground by default, +and will print information on stdout about the monitoring process. +Use this option multiple times for more details. *-F*:: -upsmon will run in the foreground. +upsmon will run in the foreground, regardless of debugging settings. + +*-B*:: +upsmon will run in the background, regardless of debugging settings. *-K*:: Test for the shutdown flag. If it exists and contains the magic string From f781fbef824c58c0a49fdac857d22fe217673335 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 17:49:57 +0100 Subject: [PATCH 087/700] server/upsd.c: align -F/-B options with drivers/main.c --- server/upsd.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/server/upsd.c b/server/upsd.c index 798677ebbb..28b605e83e 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -1173,8 +1173,9 @@ static void help(const char *arg_progname) printf(" commands:\n"); printf(" - reload: reread configuration files\n"); printf(" - stop: stop process and exit\n"); - printf(" -D raise debugging level\n"); - printf(" -F run in foreground\n"); + printf(" -D raise debugging level (and stay foreground by default)\n"); + printf(" -F stay foregrounded even if no debugging is enabled\n"); + printf(" -B stay backgrounded even if debugging is bumped\n"); printf(" -h display this help\n"); printf(" -r chroots to \n"); printf(" -q raise log level threshold\n"); @@ -1246,7 +1247,7 @@ void check_perms(const char *fn) int main(int argc, char **argv) { - int i, cmd = 0, cmdret = 0, foreground = 0; + int i, cmd = 0, cmdret = 0, foreground = -1; char *chroot_path = NULL; const char *user = RUN_AS_USER; struct passwd *new_uid = NULL; @@ -1262,7 +1263,7 @@ int main(int argc, char **argv) printf("Network UPS Tools %s %s\n", progname, UPS_VERSION); - while ((i = getopt(argc, argv, "+h46p:qr:i:fu:Vc:DF")) != -1) { + while ((i = getopt(argc, argv, "+h46p:qr:i:fu:Vc:DFB")) != -1) { switch (i) { case 'p': case 'i': @@ -1302,11 +1303,14 @@ int main(int argc, char **argv) case 'D': nut_debug_level++; - foreground = 1; break; case 'F': foreground = 1; break; + case 'B': + foreground = 0; + break; + case '4': opt_af = AF_INET; break; @@ -1321,6 +1325,14 @@ int main(int argc, char **argv) } } + if (foreground < 0) { + if (nut_debug_level > 0) { + foreground = 1; + } else { + foreground = 0; + } + } + if (cmd) { cmdret = sendsignalfn(pidfn, cmd); exit((cmdret == 0)?EXIT_SUCCESS:EXIT_FAILURE); From 8a692b1959a372f28158c99f0611d7f7430132aa Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 17:50:07 +0100 Subject: [PATCH 088/700] docs/man/upsd.txt: document -F/-B for fore/back-ground enforcement, and that -D only defaults to foregrounding now (can be overridden) --- docs/man/upsd.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/man/upsd.txt b/docs/man/upsd.txt index 237718d0c1..771668a688 100644 --- a/docs/man/upsd.txt +++ b/docs/man/upsd.txt @@ -44,10 +44,15 @@ are: *stop*;; stop process and exit *-D*:: -Raise the debug level. Use this multiple times for additional details. +Raise the debugging level. upsd will run in the foreground by default, +and will print information on stdout about the monitoring process. +Use this option multiple times for more details. *-F*:: -Run in foreground. +upsd will run in the foreground, regardless of debugging settings. + +*-B*:: +upsd will run in the background, regardless of debugging settings. *-h*:: Display the help text. From 67ae33751d587d7b83d7c421880e40e82d6eb694 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 17:51:23 +0100 Subject: [PATCH 089/700] scripts/systemd/nut-monitor.service.in: with daemon not backgrounding, "Type=forking" no longer applies --- scripts/systemd/nut-monitor.service.in | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/systemd/nut-monitor.service.in b/scripts/systemd/nut-monitor.service.in index 457eef63ff..4ee216e160 100644 --- a/scripts/systemd/nut-monitor.service.in +++ b/scripts/systemd/nut-monitor.service.in @@ -21,7 +21,6 @@ SyslogIdentifier=%N ExecStart=@SBINDIR@/upsmon -F ExecReload=@SBINDIR@/upsmon -c reload PIDFile=@PIDPATH@/upsmon.pid -Type=forking [Install] WantedBy=nut.target From 28896166b564b9872bb11eaa30c563c2791f674f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 18:02:36 +0100 Subject: [PATCH 090/700] clients/upslog.c: align -F/-B options with drivers/main.c --- clients/upslog.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/clients/upslog.c b/clients/upslog.c index 1d8b2aec65..6115998b13 100644 --- a/clients/upslog.c +++ b/clients/upslog.c @@ -123,7 +123,9 @@ static void help(const char *prog) printf(" -f - Log format. See below for details.\n"); printf(" - Use -f \"\" so your shell doesn't break it up.\n"); printf(" -i - Time between updates, in seconds\n"); - printf(" -l - Log file name, or - for stdout\n"); + printf(" -l - Log file name, or - for stdout (foreground by default)\n"); + printf(" -F - stay foregrounded even if logging into a file\n"); + printf(" -B - stay backgrounded even if logging to stdout\n"); printf(" -p - Base name for PID file (defaults to \"%s\")\n", prog); printf(" -s - Monitor UPS - @[:]\n"); printf(" - Example: -s myups@server\n"); @@ -391,7 +393,7 @@ static void run_flist(void) int main(int argc, char **argv) { - int interval = 30, i; + int interval = 30, i, foreground = -1; const char *prog = xbasename(argv[0]); time_t now, nextpoll = 0; const char *user = NULL; @@ -403,7 +405,7 @@ int main(int argc, char **argv) printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); - while ((i = getopt(argc, argv, "+hs:l:i:f:u:Vp:")) != -1) { + while ((i = getopt(argc, argv, "+hs:l:i:f:u:Vp:FB")) != -1) { switch(i) { case 'h': help(prog); @@ -437,6 +439,14 @@ int main(int argc, char **argv) case 'p': pidfilebase = optarg; break; + + case 'F': + foreground = 1; + break; + + case 'B': + foreground = 0; + break; } } @@ -501,8 +511,17 @@ int main(int argc, char **argv) open_syslog(prog); - if (logfile != stdout) + if (foreground < 0) { + if (logfile == stdout) { + foreground = 1; + } else { + foreground = 0; + } + } + + if (!foreground) { background(); + } setup_signals(); From d875f1fa0a6d9144e8bbd10622fa25efe4d6db21 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 18:03:10 +0100 Subject: [PATCH 091/700] docs/man/upslog.txt: fix typo "upsmon" => "upslog" --- docs/man/upslog.txt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/man/upslog.txt b/docs/man/upslog.txt index 4eaa572503..c8e1964fec 100644 --- a/docs/man/upslog.txt +++ b/docs/man/upslog.txt @@ -65,7 +65,14 @@ the linkman:upsclient[3] library. Store the results in this file. + -You can use "-" for stdout, but upslog will remain in the foreground. +You can use "-" for stdout, but upslog will remain in the foreground +by default. + +*-F*:: +upslog will run in the foreground, regardless of logging target. + +*-B*:: +upslog will run in the background, regardless of logging target. *-s* 'ups':: Monitor this UPS. The format for this option is @@ -73,7 +80,7 @@ Monitor this UPS. The format for this option is *-u* 'username':: -If started as root, upsmon will *setuid*(2) to the user id +If started as root, upslog will *setuid*(2) to the user id associated with 'username' for security. + If 'username' is not defined, it will use the value that was compiled into the From 813e22234d7ffe1b16cc1de280f33a21589ea1d5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 18:51:26 +0100 Subject: [PATCH 092/700] drivers/main.c: comment typo fixes --- drivers/main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index 173d3a70e2..038294ded6 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -67,8 +67,8 @@ static int background_flag = -1; * For the service tracing purposes, also the ups.conf * can define a debug_min value in the global or device * section, to set the minimal debug level (CLI provided - * value less than that would not have effect, can have - * more. + * value less than that would not have effect, can only + * have more). */ static int nut_debug_level_global = -1; static int nut_debug_level_driver = -1; @@ -699,7 +699,8 @@ int main(int argc, char **argv) /* CLI debug level can not be smaller than debug_min specified * in ups.conf, and value specified for a driver config section - * overrides the global one. + * overrides the global one. Note that non-zero debug_min does + * not impact foreground running mode. */ { int nut_debug_level_upsconf = -1 ; From 74bd64dee04f0b27f4912e71e345bf0fc5d12778 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 19:07:16 +0100 Subject: [PATCH 093/700] clients/upsmon.c: support "debug_min" from upsmon.conf --- clients/upsmon.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index 0704711aaa..941b36d3b0 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -89,6 +89,14 @@ static int opt_af = AF_UNSPEC; static struct sigaction sa; static sigset_t nut_upsmon_sigmask; +/* Users can pass a -D[...] option to enable debugging. + * For the service tracing purposes, also the upsmon.conf + * can define a debug_min value in the global section, + * to set the minimal debug level (CLI provided value less + * than that would not have effect, can only have more). + */ +static int nut_debug_level_global = -1; + static void setflag(int *val, int flag) { *val |= flag; @@ -1296,6 +1304,18 @@ static int parse_conf_arg(size_t numargs, char **arg) return 1; } + /* DEBUG_MIN (NUM) */ + /* debug_min (NUM) also acceptable, to be on par with ups.conf */ + if (!strcasecmp(arg[0], "DEBUG_MIN")) { + int lvl = -1; // typeof common/common.c: int nut_debug_level + if ( str_to_int (arg[1], &lvl, 10) && lvl >= 0 ) { + nut_debug_level_global = lvl; + } else { + upslogx(LOG_INFO, "WARNING : Invalid DEBUG_MIN value found in upsmon.conf global settings"); + } + return 1; + } + /* using up to arg[2] below */ if (numargs < 3) return 0; @@ -2121,6 +2141,14 @@ int main(int argc, char *argv[]) loadconfig(); + /* CLI debug level can not be smaller than debug_min specified + * in upsmon.conf. Note that non-zero debug_min does not impact + * foreground running mode. + */ + if (nut_debug_level_global > nut_debug_level) + nut_debug_level = nut_debug_level_global; + upsdebugx(1, "debug level is '%d'", nut_debug_level); + if (checking_flag) exit(check_pdflag()); @@ -2147,7 +2175,7 @@ int main(int argc, char *argv[]) background(); } - if(nut_debug_level >= 1) { + if (nut_debug_level >= 1) { upsdebugx(1, "debug level is '%d'", nut_debug_level); } From e316c5324eb0bcb13b38dad22b6f762169a191b9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 19:40:44 +0100 Subject: [PATCH 094/700] server/upsd.c, conf.c: support "debug_min" from upsd.conf --- server/conf.c | 20 ++++++++++++++++++++ server/conf.h | 1 + server/upsd.c | 8 ++++++++ 3 files changed, 29 insertions(+) diff --git a/server/conf.c b/server/conf.c index 639b750220..de973bdc1c 100644 --- a/server/conf.c +++ b/server/conf.c @@ -28,6 +28,14 @@ static ups_t *upstable = NULL; int num_ups = 0; +/* Users can pass a -D[...] option to enable debugging. + * For the service tracing purposes, also the upsd.conf + * can define a debug_min value in the global section, + * to set the minimal debug level (CLI provided value less + * than that would not have effect, can only have more). + */ +int nut_debug_level_global = -1; + /* add another UPS for monitoring from ups.conf */ static void ups_create(const char *fn, const char *name, const char *desc) { @@ -136,6 +144,18 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) if (numargs < 2) return 0; + /* DEBUG_MIN (NUM) */ + /* debug_min (NUM) also acceptable, to be on par with ups.conf */ + if (!strcasecmp(arg[0], "DEBUG_MIN")) { + int lvl = -1; // typeof common/common.c: int nut_debug_level + if ( str_to_int (arg[1], &lvl, 10) && lvl >= 0 ) { + nut_debug_level_global = lvl; + } else { + upslogx(LOG_INFO, "DEBUG_MIN has non numeric or negative value in upsd.conf"); + } + return 1; + } + /* MAXAGE */ if (!strcmp(arg[0], "MAXAGE")) { if (isdigit(arg[1][0])) { diff --git a/server/conf.h b/server/conf.h index 40341389fe..432b7f9c3e 100644 --- a/server/conf.h +++ b/server/conf.h @@ -51,6 +51,7 @@ void delete_acls(void); void delete_access(void); extern int num_ups; +extern int nut_debug_level_global; #ifdef __cplusplus /* *INDENT-OFF* */ diff --git a/server/upsd.c b/server/upsd.c index 28b605e83e..68e7e7ba85 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -1377,6 +1377,14 @@ int main(int argc, char **argv) /* handle upsd.conf */ load_upsdconf(0); /* 0 = initial */ + /* CLI debug level can not be smaller than debug_min specified + * in upsd.conf. Note that non-zero debug_min does not impact + * foreground running mode. + */ + if (nut_debug_level_global > nut_debug_level) + nut_debug_level = nut_debug_level_global; + upsdebugx(1, "debug level is '%d'", nut_debug_level); + { /* scope */ /* As documented above, the ALLOW_NO_DEVICE can be provided via * envvars and then has higher priority than an upsd.conf setting From a9837b0bf76c38b1508999a1239411b005a93715 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 19:15:41 +0100 Subject: [PATCH 095/700] docs/man/ups.conf.txt: document optional "debug_min" setting --- docs/man/ups.conf.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/man/ups.conf.txt b/docs/man/ups.conf.txt index 41a194c306..a6a2235590 100644 --- a/docs/man/ups.conf.txt +++ b/docs/man/ups.conf.txt @@ -116,6 +116,13 @@ of the driver behavior. Optional. Overrides the compiled-in default unprivileged username. See the discussion of the `-u` option in linkman:nutupsdrv[8] for details. +*debug_min* 'INTEGER':: + +Optional. Specify a minimum debug level for all driver daemons, e.g. for +troubleshooting a deployment, without impacting foreground or background +running mode directly. Command-line option `-D` can only increase this +verbosity level. + UPS FIELDS ---------- *driver*:: @@ -225,6 +232,14 @@ the outside world, internally in the UPS the original value is used. All other fields are passed through to the hardware-specific part of the driver. See those manuals for the list of what is allowed. +*debug_min* 'INTEGER':: + +Optional. Specify a minimum debug level for this driver daemon, e.g. for +troubleshooting a deployment, without impacting foreground or background +running mode directly. If the global `debug_min` is also set, this +driver-level setting overrides it. Command-line option `-D` can only +increase this verbosity level. + INTEGRATION ----------- From 43d287e631c5ebd6c770170d8700ffd53e812aec Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 19:15:57 +0100 Subject: [PATCH 096/700] docs/man/upsmon.conf.txt: document optional "DEBUG_MIN" setting --- docs/man/upsmon.conf.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index 4551c95628..ac92401a60 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -393,6 +393,13 @@ support SSL, so don't use it unless all of them have it running. When compiled with NSS support of SSL, can be overridden for host specified with a CERTHOST directive. +*DEBUG_MIN* 'INTEGER':: + +Optionally specify a minimum debug level for `upsmon` daemon, e.g. for +troubleshooting a deployment, without impacting foreground or background +running mode directly. Command-line option `-D` can only increase this +verbosity level. + SEE ALSO -------- linkman:upsmon[8], linkman:upsd[8], linkman:nutupsdrv[8]. From 2c134b5908954a915b8b2ab9fc577c2693ad5885 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 19:24:42 +0100 Subject: [PATCH 097/700] conf/ups.conf.sample: document optional "debug_min" setting --- conf/ups.conf.sample | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/conf/ups.conf.sample b/conf/ups.conf.sample index 46b43b7816..8d9e833367 100644 --- a/conf/ups.conf.sample +++ b/conf/ups.conf.sample @@ -64,6 +64,17 @@ # Set maxretry to 3 by default, this should mitigate race with slow devices: maxretry = 3 +# These directives can be set outside and inside a driver definition, with +# slightly different meanings per context: +# +# debug_min: OPTIONAL. Specify a minimum debug level for all driver daemons +# (when specified at global level), or for this driver daemon +# (when specified in a driver section), e.g. for troubleshooting +# a deployment. This does not directly impact the foreground or +# background running mode. If both the global and driver level +# `debug_min` are set, the driver-level setting takes precedence. +# Command-line option `-D` can only increase this verbosity level. + # These directives are common to all drivers that support ups.conf: # # driver: REQUIRED. Specify the program to run to talk to this UPS. From 811c0eef2dbfeb856f611e4bb65aee6e874fe213 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 19:25:02 +0100 Subject: [PATCH 098/700] conf/upsmon.conf.sample.in: document optional "DEBUG_MIN" setting --- conf/upsmon.conf.sample.in | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/conf/upsmon.conf.sample.in b/conf/upsmon.conf.sample.in index de38bff66f..dedbb92afb 100644 --- a/conf/upsmon.conf.sample.in +++ b/conf/upsmon.conf.sample.in @@ -430,3 +430,13 @@ FINALDELAY 5 # support SSL, so don't use it unless all of them have it running. # When compiled with NSS support of SSL, can be overridden for host # specified with a CERTHOST directive. + +# -------------------------------------------------------------------------- +# DEBUG_MIN - specify minimal debugging level for upsmon daemon +# e.g. DEBUG_MIN 6 +# +# Optionally specify a minimum debug level for `upsmon` daemon, e.g. for +# troubleshooting a deployment, without impacting foreground or background +# running mode directly, and without need to edit init-scripts or service +# unit definitions. Note that command-line option `-D` can only increase +# this verbosity level. From 15ddad3df8a1c401782517f18804b113342f4341 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 19:45:25 +0100 Subject: [PATCH 099/700] conf/upsd.conf.sample: document optional "DEBUG_MIN" setting --- conf/upsd.conf.sample | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/conf/upsd.conf.sample b/conf/upsd.conf.sample index 1c7e3c5fe7..9d6a9cd1f4 100644 --- a/conf/upsd.conf.sample +++ b/conf/upsd.conf.sample @@ -145,3 +145,13 @@ # # Unless you have really ancient clients, you probably want to enable this. # Currently disabled by default to ensure compatibility with existing setups. + +# ======================================================================= +# DEBUG_MIN +# DEBUG_MIN 2 +# +# Optionally specify a minimum debug level for `upsd` data daemon, e.g. for +# troubleshooting a deployment, without impacting foreground or background +# running mode directly, and without need to edit init-scripts or service +# unit definitions. Note that command-line option `-D` can only increase +# this verbosity level. From 48704ac4a6fc4c217f36e47bf004b33d9b06da81 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 19:45:31 +0100 Subject: [PATCH 100/700] docs/man/upsd.conf.txt: document optional "DEBUG_MIN" setting --- docs/man/upsd.conf.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/man/upsd.conf.txt b/docs/man/upsd.conf.txt index 412d21ea1f..cef48a3f1d 100644 --- a/docs/man/upsd.conf.txt +++ b/docs/man/upsd.conf.txt @@ -121,6 +121,13 @@ Possible values are : - '1' to require to all clients a certificate - '2' to require to all clients a valid certificate +"DEBUG_MIN 'INTEGER'":: + +Optionally specify a minimum debug level for `upsd` data daemon, e.g. for +troubleshooting a deployment, without impacting foreground or background +running mode directly. Command-line option `-D` can only increase this +verbosity level. + SEE ALSO -------- From 0798fe17d4f0a1fade97d51365df85e53eb776f7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 18:29:35 +0100 Subject: [PATCH 101/700] NEWS: announce fore-/back-grounding options for daemons in NUT v2.7.5 --- NEWS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NEWS b/NEWS index ac9220c69d..c3c899cb27 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,13 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: for them to complete initializing. This matters on systems that monitor from dozens to hundreds of devices. + - Daemons such as upsd, upsmon, upslog, and device drivers previously + implied that enabled debugging (or upslog to stdout) means foreground + running, otherwise the daemon was always sent to the background. + Now there are explicit options for this (`-F`/`-B`), although default + behavior is retained. This change is used for simplified service unit + definitions. + - Improvements for device discovery or driver "lock-picking", including general support for: * "Standalone" mode (`-s` option), to monitor a device which is not From d6127bf67b98cf92279f73057e5262195a99a5c6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 18:38:07 +0100 Subject: [PATCH 102/700] NEWS: announce "debug_min" options for daemons in NUT v2.7.5 --- NEWS | 5 +++++ docs/nut.dict | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index c3c899cb27..0922578ffd 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,11 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: server initially without any device configurations and reloading it later to apply config changes on the fly [PR #766] + - Add support for `debug_min=NUM` setting (ups.conf, upsd.conf, upsmon.conf) + to specify the minimum debug verbosity for daemons. This allows "in-vivo" + troubleshooting of service daemons without editing init scripts or service + unit definitions. + - Improve support for upsdrvctl for managing of numerous device configs, including default "maxretry=3" and a "nowait" option to complete the "start of everything" mode after triggering the drivers and not waiting diff --git a/docs/nut.dict b/docs/nut.dict index 1b118fcadb..fd55086ee9 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2863 utf-8 +personal_ws-1.1 en 2864 utf-8 AAS ACFAIL ACFREQ @@ -2801,6 +2801,7 @@ vid vin virsh virtualization +vivo vo vod voltronic From 18488f045110df87585baa2fd1e3c4e8cb181cde Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Feb 2022 19:56:12 +0100 Subject: [PATCH 103/700] docs/man/upsd.conf.txt: document DISABLE_WEAK_SSL --- docs/man/upsd.conf.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/man/upsd.conf.txt b/docs/man/upsd.conf.txt index cef48a3f1d..40f9786a5d 100644 --- a/docs/man/upsd.conf.txt +++ b/docs/man/upsd.conf.txt @@ -121,6 +121,14 @@ Possible values are : - '1' to require to all clients a certificate - '2' to require to all clients a valid certificate +"DISABLE_WEAK_SSL 'BOOLEAN'":: + +Tell upsd to disable older/weak SSL/TLS protocols and ciphers. +With relatively recent versions of OpenSSL or NSS it will be restricted +to TLSv1.2 or better. +Unless you have really ancient clients, you probably want to enable this. +Currently disabled by default to ensure compatibility with existing setups. + "DEBUG_MIN 'INTEGER'":: Optionally specify a minimum debug level for `upsd` data daemon, e.g. for From aefcdc0ada50a3c256c4ffe069ca935d3129b113 Mon Sep 17 00:00:00 2001 From: Nick Briggs Date: Sun, 13 Feb 2022 12:25:08 -0800 Subject: [PATCH 104/700] Fix incorrect limits for input and config voltages in APC report descriptor The Back-UPS XS 1400U has been observed to report input/config voltage limits that are appropriate for the North American 120V region even though the unit is operating in the European or other 220+V region. This change diagnoses the the problem by checking if the logical maximum values for UPS.Input.Voltage and UPS.Input.ConfigVoltage are consistent with the UPS.Input.HighVoltageTransfer and if not, increases them. A similar problem was reported for CPS units in the EU region. - introduces #defines for all standard usages in power system and battery device pages. - moves cps-hid.c FindReport() to hidparser.c as FindObject_with_ID_Node(). - updates cps-hid.c to account for new defines/function name - adds apc_fix_report_desc() to implement change for APC UPS units --- drivers/apc-hid.c | 68 ++++++++++++++++- drivers/cps-hid.c | 30 +------- drivers/hidparser.c | 27 +++++++ drivers/hidparser.h | 1 + drivers/hidtypes.h | 179 +++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 275 insertions(+), 30 deletions(-) diff --git a/drivers/apc-hid.c b/drivers/apc-hid.c index 4482216db7..903ad8d8d2 100644 --- a/drivers/apc-hid.c +++ b/drivers/apc-hid.c @@ -27,6 +27,7 @@ */ #include "main.h" /* for getval() */ +#include "hidparser.h" /* for FindObject_with_ID_Node() */ #include "usbhid-ups.h" #include "apc-hid.h" #include "usb-common.h" @@ -499,6 +500,71 @@ static int apc_claim(HIDDevice_t *hd) { } } +/* apc_fix_report_desc + * + * The Back-UPS XS 1400U reports incorrect logical min/max values for the + * UPS.Input.ConfigVoltage and UPS.Input.Voltage when operating in a + * 220-240V region. Detect this and fix it. + * This same fix may be applicable to other APC UPS units as well, though + * the report IDs may be different. + */ +static int apc_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { + HIDData_t *pData; + + int vendorID = pDev->VendorID; + int productID = pDev->ProductID; + if (vendorID != APC_VENDORID || productID != 0x0002) { + return 0; + } + + upsdebugx(3, "Attempting Report Descriptor fix for UPS: Vendor: %04x, Product: %04x", vendorID, productID); + + /* Look at the High Voltage Transfer logical max value: + * If the HVT logmax is greater than the configured or input voltage limit + * then the configured/input voltage limits are probably incorrect. + * Arbitrarily set the input voltage logical min/max to 0 .. 2*HVT logmax and the + * configured (nominal) input voltage logical max to 255 (it's a single byte value) + + * Path: UPS.Input.ConfigVoltage, Type: Feature, ReportID: 0x30, Offset: 0, Size: 8 + * Path: UPS.Input.Voltage, Type: Feature, ReportID: 0x31, Offset: 0, Size: 16 + * Path: UPS.Input.HighVoltageTransfer, Type: Feature, ReportID: 0x33, Offset: 0, Size: 16 + */ + + if ((pData=FindObject_with_ID_Node(pDesc_arg, 0x33, USAGE_POW_HIGH_VOLTAGE_TRANSFER))) { + long hvt_logmin = pData->LogMin; + long hvt_logmax = pData->LogMax; + upsdebugx(4, "Report Descriptor: highVoltageTransfer LogMin: %ld LogMax: %ld", hvt_logmin, hvt_logmax); + + if ((pData=FindObject_with_ID_Node(pDesc_arg, 0x31, USAGE_POW_VOLTAGE))) { + long voltage_logmin = pData->LogMin; + long voltage_logmax = pData->LogMax; + upsdebugx(4, "Report Descriptor: voltage LogMin: %ld LogMax: %ld", + voltage_logmin, voltage_logmax); + + if (hvt_logmax > voltage_logmax) { + pData->LogMin = 0; /* a reasonable lower limit for voltage */ + pData->LogMax = hvt_logmax * 2; /* it may be smoking at this point */ + upsdebugx(3, "Fixing Report Descriptor. Set voltage LogMin = %ld, LogMax = %ld", + pData->LogMin , pData->LogMax); + } + } + if ((pData=FindObject_with_ID_Node(pDesc_arg, 0x30, USAGE_POW_CONFIG_VOLTAGE))) { + long cvoltage_logmin = pData->LogMin; + long cvoltage_logmax = pData->LogMax; + upsdebugx(4, "Report Descriptor: configVoltage LogMin: %ld LogMax: %ld", + cvoltage_logmin, cvoltage_logmax); + + if (hvt_logmax > cvoltage_logmax) { + pData->LogMax = 255; + upsdebugx(3, "Fixing Report Descriptor. Set configVoltage LogMin = %ld, LogMax = %ld", + pData->LogMin , pData->LogMax); + } + } + return 1; + } + return 0; +} + subdriver_t apc_subdriver = { APC_HID_VERSION, apc_claim, @@ -507,5 +573,5 @@ subdriver_t apc_subdriver = { apc_format_model, apc_format_mfr, apc_format_serial, - fix_report_desc, + apc_fix_report_desc, }; diff --git a/drivers/cps-hid.c b/drivers/cps-hid.c index 06fc595bd3..71f9a5bcbe 100644 --- a/drivers/cps-hid.c +++ b/drivers/cps-hid.c @@ -25,6 +25,7 @@ #include "main.h" /* for getval() */ #include "nut_float.h" +#include "hidparser.h" /* for FindObject_with_ID_Node() */ #include "usbhid-ups.h" #include "cps-hid.h" #include "usb-common.h" @@ -276,29 +277,6 @@ static int cps_claim(HIDDevice_t *hd) { * voltage limits as being more appropriate. */ -static HIDData_t *FindReport(HIDDesc_t *pDesc_arg, uint8_t ReportID, HIDNode_t node) -{ - size_t i; - - for (i = 0; i < pDesc_arg->nitems; i++) { - HIDData_t *pData = &pDesc_arg->item[i]; - - if (pData->ReportID != ReportID) { - continue; - } - - HIDPath_t * pPath = &pData->Path; - uint8_t size = pPath->Size; - if (size == 0 || pPath->Node[size-1] != node) { - continue; - } - - return pData; - } - - return NULL; -} - static int cps_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { HIDData_t *pData; @@ -315,12 +293,12 @@ static int cps_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { * To fix it Set both the input and output voltages to pre-defined settings. */ - if ((pData=FindReport(pDesc_arg, 16, (PAGE_POWER_DEVICE<<16)+USAGE_HIGHVOLTAGETRANSFER))) { + if ((pData=FindObject_with_ID_Node(pDesc_arg, 16, USAGE_POW_HIGH_VOLTAGE_TRANSFER))) { long hvt_logmin = pData->LogMin; long hvt_logmax = pData->LogMax; upsdebugx(4, "Report Descriptor: hvt input LogMin: %ld LogMax: %ld", hvt_logmin, hvt_logmax); - if ((pData=FindReport(pDesc_arg, 18, (PAGE_POWER_DEVICE<<16)+USAGE_VOLTAGE))) { + if ((pData=FindObject_with_ID_Node(pDesc_arg, 18, USAGE_POW_VOLTAGE))) { long output_logmin = pData->LogMin; long output_logmax = pData->LogMax; upsdebugx(4, "Report Descriptor: output LogMin: %ld LogMax: %ld", @@ -331,7 +309,7 @@ static int cps_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { pData->LogMax = CPS_VOLTAGE_LOGMAX; upsdebugx(3, "Fixing Report Descriptor. Set Output Voltage LogMin = %d, LogMax = %d", CPS_VOLTAGE_LOGMIN , CPS_VOLTAGE_LOGMAX); - if ((pData=FindReport(pDesc_arg, 15, (PAGE_POWER_DEVICE<<16)+USAGE_VOLTAGE))) { + if ((pData=FindObject_with_ID_Node(pDesc_arg, 15, USAGE_POW_VOLTAGE))) { long input_logmin = pData->LogMin; long input_logmax = pData->LogMax; upsdebugx(4, "Report Descriptor: input LogMin: %ld LogMax: %ld", diff --git a/drivers/hidparser.c b/drivers/hidparser.c index 48fb1b0bf3..0d7b53f80e 100644 --- a/drivers/hidparser.c +++ b/drivers/hidparser.c @@ -442,6 +442,33 @@ HIDData_t *FindObject_with_ID(HIDDesc_t *pDesc_arg, uint8_t ReportID, uint8_t Of return NULL; } +/* + * FindObject_with_ID_Node + * Get pData item with given ReportID and Node. Return NULL if not found. + * -------------------------------------------------------------------------- */ +HIDData_t *FindObject_with_ID_Node(HIDDesc_t *pDesc_arg, uint8_t ReportID, HIDNode_t Node) +{ + size_t i; + + for (i = 0; i < pDesc_arg->nitems; i++) { + HIDData_t *pData = &pDesc_arg->item[i]; + + if (pData->ReportID != ReportID) { + continue; + } + + HIDPath_t * pPath = &pData->Path; + uint8_t size = pPath->Size; + if (size == 0 || pPath->Node[size-1] != Node) { + continue; + } + + return pData; + } + + return NULL; +} + /* * GetValue * Extract data from a report stored in Buf. diff --git a/drivers/hidparser.h b/drivers/hidparser.h index d104b1138b..1a2a3a7045 100644 --- a/drivers/hidparser.h +++ b/drivers/hidparser.h @@ -63,6 +63,7 @@ HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc_arg, HIDPath_t *Path, uint8_t T HIDData_t *FindObject_with_ID(HIDDesc_t *pDesc_arg, uint8_t ReportID, uint8_t Offset, uint8_t Type); +HIDData_t *FindObject_with_ID_Node(HIDDesc_t *pDesc_arg, uint8_t ReportID, HIDNode_t Node); /* * GetValue * -------------------------------------------------------------------------- */ diff --git a/drivers/hidtypes.h b/drivers/hidtypes.h index 213fe4613d..bbc16634cf 100644 --- a/drivers/hidtypes.h +++ b/drivers/hidtypes.h @@ -93,10 +93,183 @@ extern "C" { /* Usage Pages */ #define PAGE_POWER_DEVICE 0x84 +#define PAGE_BATTERY_SYSTEM 0x85 + +/* Usage within Power Device page */ +#define USAGE_POW_UNDEFINED 0x00840000 +#define USAGE_POW_I_NAME 0x00840001 +#define USAGE_POW_PRESENT_STATUS 0x00840002 +#define USAGE_POW_CHANGED_STATUS 0x00840003 +#define USAGE_POW_UPS 0x00840004 +#define USAGE_POW_POWER_SUPPLY 0x00840005 +#define USAGE_POW_PERIPHERAL_DEVICE 0x00840006 +#define USAGE_POW_BATTERY_SYSTEM 0x00840010 +#define USAGE_POW_BATTERY_SYSTEM_ID 0x00840011 +#define USAGE_POW_BATTERY 0x00840012 +#define USAGE_POW_BATTERY_ID 0x00840013 +#define USAGE_POW_CHARGER 0x00840014 +#define USAGE_POW_CHARGER_ID 0x00840015 +#define USAGE_POW_POWER_CONVERTER 0x00840016 +#define USAGE_POW_POWER_CONVERTER_ID 0x00840017 +#define USAGE_POW_OUTLET_SYSTEM 0x00840018 +#define USAGE_POW_OUTLET_SYSTEM_ID 0x00840019 +#define USAGE_POW_INPUT 0x0084001A +#define USAGE_POW_INPUT_ID 0x0084001B +#define USAGE_POW_OUTPUT 0x0084001C +#define USAGE_POW_OUTPUT_ID 0x0084001D +#define USAGE_POW_FLOW 0x0084001E +#define USAGE_POW_FLOW_ID 0x0084001F +#define USAGE_POW_OUTLET 0x00840020 +#define USAGE_POW_OUTLET_ID 0x00840021 +#define USAGE_POW_GANG 0x00840022 +#define USAGE_POW_GANG_ID 0x00840023 +#define USAGE_POW_POWER_SUMMARY 0x00840024 +#define USAGE_POW_POWER_SUMMARY_ID 0x00840025 +#define USAGE_POW_VOLTAGE 0x00840030 +#define USAGE_POW_CURRENT 0x00840031 +#define USAGE_POW_FREQUENCY 0x00840032 +#define USAGE_POW_APPARENT_POWER 0x00840033 +#define USAGE_POW_ACTIVE_POWER 0x00840034 +#define USAGE_POW_PERCENT_LOAD 0x00840035 +#define USAGE_POW_TEMPERATURE 0x00840036 +#define USAGE_POW_HUMIDITY 0x00840037 +#define USAGE_POW_BAD_COUNT 0x00840038 +#define USAGE_POW_CONFIG_VOLTAGE 0x00840040 +#define USAGE_POW_CONFIG_CURRENT 0x00840041 +#define USAGE_POW_CONFIG_FREQUENCY 0x00840042 +#define USAGE_POW_CONFIG_APPARENT_POWER 0x00840043 +#define USAGE_POW_CONFIG_ACTIVE_POWER 0x00840044 +#define USAGE_POW_CONFIG_PERCENT_LOAD 0x00840045 +#define USAGE_POW_CONFIG_TEMPERATURE 0x00840046 +#define USAGE_POW_CONFIG_HUMIDITY 0x00840047 +#define USAGE_POW_SWITCH_ON_CONTROL 0x00840050 +#define USAGE_POW_SWITCH_OFF_CONTROL 0x00840051 +#define USAGE_POW_TOGGLE_CONTROL 0x00840052 +#define USAGE_POW_LOW_VOLTAGE_TRANSFER 0x00840053 +#define USAGE_POW_HIGH_VOLTAGE_TRANSFER 0x00840054 +#define USAGE_POW_DELAY_BEFORE_REBOOT 0x00840055 +#define USAGE_POW_DELAY_BEFORE_STARTUP 0x00840056 +#define USAGE_POW_DELAY_BEFORE_SHUTDOWN 0x00840057 +#define USAGE_POW_TEST 0x00840058 +#define USAGE_POW_MODULE_RESET 0x00840059 +#define USAGE_POW_AUDIBLE_ALARM_CONTROL 0x0084005A +#define USAGE_POW_PRESENT 0x00840060 +#define USAGE_POW_GOOD 0x00840061 +#define USAGE_POW_INTERNAL_FAILURE 0x00840062 +#define USAGE_POW_VOLTAGE_OUT_OF_RANGE 0x00840063 +#define USAGE_POW_FREQUENCY_OUT_OF_RANGE 0x00840064 +#define USAGE_POW_OVERLOAD 0x00840065 +#define USAGE_POW_OVER_CHARGED 0x00840066 +#define USAGE_POW_OVER_TEMPERATURE 0x00840067 +#define USAGE_POW_SHUTDOWN_REQUESTED 0x00840068 +#define USAGE_POW_SHUTDOWN_IMMINENT 0x00840069 +#define USAGE_POW_SWITCH_ON_OFF 0x0084006B +#define USAGE_POW_SWITCHABLE 0x0084006C +#define USAGE_POW_USED 0x0084006D +#define USAGE_POW_BOOST 0x0084006E +#define USAGE_POW_BUCK 0x0084006F +#define USAGE_POW_INITIALIZED 0x00840070 +#define USAGE_POW_TESTED 0x00840071 +#define USAGE_POW_AWAITING_POWER 0x00840072 +#define USAGE_POW_COMMUNICATION_LOST 0x00840073 +#define USAGE_POW_I_MANUFACTURER 0x008400FD +#define USAGE_POW_I_PRODUCT 0x008400FE +#define USAGE_POW_I_SERIAL_NUMBER 0x008400FF + +/* Usage within Battery System page */ +#define USAGE_BAT_UNDEFINED 0x00850000 +#define USAGE_BAT_SMB_BATTERY_MODE 0x00850001 +#define USAGE_BAT_SMB_BATTERY_STATUS 0x00850002 +#define USAGE_BAT_SMB_ALARM_WARNING 0x00850003 +#define USAGE_BAT_SMB_CHARGER_MODE 0x00850004 +#define USAGE_BAT_SMB_CHARGER_STATUS 0x00850005 +#define USAGE_BAT_SMB_CHARGER_SPEC_INFO 0x00850006 +#define USAGE_BAT_SMB_SELECTOR_STATE 0x00850007 +#define USAGE_BAT_SMB_SELECTOR_PRESETS 0x00850008 +#define USAGE_BAT_SMB_SELECTOR_INFO 0x00850009 +#define USAGE_BAT_OPTIONAL_MFG_FUNCTION_1 0x00850010 +#define USAGE_BAT_OPTIONAL_MFG_FUNCTION_2 0x00850011 +#define USAGE_BAT_OPTIONAL_MFG_FUNCTION_3 0x00850012 +#define USAGE_BAT_OPTIONAL_MFG_FUNCTION_4 0x00850013 +#define USAGE_BAT_OPTIONAL_MFG_FUNCTION_5 0x00850014 +#define USAGE_BAT_CONNECTION_TO_SMBUS 0x00850015 +#define USAGE_BAT_OUTPUT_CONNECTION 0x00850016 +#define USAGE_BAT_CHARGER_CONNECTION 0x00850017 +#define USAGE_BAT_BATTERY_INSERTION 0x00850018 +#define USAGE_BAT_USE_NEXT 0x00850019 +#define USAGE_BAT_OK_TO_USE 0x0085001A +#define USAGE_BAT_BATTERY_SUPPORTED 0x0085001B +#define USAGE_BAT_SELECTOR_REVISION 0x0085001C +#define USAGE_BAT_CHARGING_INDICATOR 0x0085001D +#define USAGE_BAT_MANUFACTURER_ACCESS 0x00850028 +#define USAGE_BAT_REMAINING_CAPACITY_LIMIT 0x00850029 +#define USAGE_BAT_REMAINING_TIME_LIMIT 0x0085002A +#define USAGE_BAT_AT_RATE 0x0085002B +#define USAGE_BAT_CAPACITY_MODE 0x0085002C +#define USAGE_BAT_BROADCAST_TO_CHARGER 0x0085002D +#define USAGE_BAT_PRIMARY_BATTERY 0x0085002E +#define USAGE_BAT_CHARGE_CONTROLLER 0x0085002F +#define USAGE_BAT_TERMINATE_CHARGE 0x00850040 +#define USAGE_BAT_TERMINATE_DISCHARGE 0x00850041 +#define USAGE_BAT_BELOW_REMAINING_CAPACITY_LIMIT 0x00850042 +#define USAGE_BAT_REMAINING_TIME_LIMIT_EXPIRED 0x00850043 +#define USAGE_BAT_CHARGING 0x00850044 +#define USAGE_BAT_DISCHARGING 0x00850045 +#define USAGE_BAT_FULLY_CHARGED 0x00850046 +#define USAGE_BAT_FULLY_DISCHARGED 0x00850047 +#define USAGE_BAT_CONDITIONING_FLAG 0x00850048 +#define USAGE_BAT_AT_RATE_OK 0x00850049 +#define USAGE_BAT_SMB_ERROR_CODE 0x0085004A +#define USAGE_BAT_NEED_REPLACEMENT 0x0085004B +#define USAGE_BAT_AT_RATE_TIME_TO_FULL 0x00850060 +#define USAGE_BAT_AT_RATE_TIME_TO_EMPTY 0x00850061 +#define USAGE_BAT_AVERAGE_CURRENT 0x00850062 +#define USAGE_BAT_MAX_ERROR 0x00850063 +#define USAGE_BAT_RELATIVE_STATE_OF_CHARGE 0x00850064 +#define USAGE_BAT_ABSOLUTE_STATE_OF_CHARGE 0x00850065 +#define USAGE_BAT_REMAINING_CAPACITY 0x00850066 +#define USAGE_BAT_FULL_CHARGE_CAPACITY 0x00850067 +#define USAGE_BAT_RUN_TIME_TO_EMPTY 0x00850068 +#define USAGE_BAT_AVERAGE_TIME_TO_EMPTY 0x00850069 +#define USAGE_BAT_AVERAGE_TIME_TO_FULL 0x0085006A +#define USAGE_BAT_CYCLE_COUNT 0x0085006B +#define USAGE_BAT_BATT_PACK_MODEL_LEVEL 0x00850080 +#define USAGE_BAT_INTERNAL_CHARGE_CONTROLLER 0x00850081 +#define USAGE_BAT_PRIMARY_BATTERY_SUPPORT 0x00850082 +#define USAGE_BAT_DESIGN_CAPACITY 0x00850083 +#define USAGE_BAT_SPECIFICATION_INFO 0x00850084 +#define USAGE_BAT_MANUFACTURER_DATE 0x00850085 +#define USAGE_BAT_SERIAL_NUMBER 0x00850086 +#define USAGE_BAT_I_MANUFACTURER_NAME 0x00850087 +#define USAGE_BAT_I_DEVICE_NAME 0x00850088 +#define USAGE_BAT_I_DEVICE_CHEMISTRY 0x00850089 +#define USAGE_BAT_MANUFACTURER_DATA 0x0085008A +#define USAGE_BAT_RECHARGEABLE 0x0085008B +#define USAGE_BAT_WARNING_CAPACITY_LIMIT 0x0085008C +#define USAGE_BAT_CAPACITY_GRANULARITY_1 0x0085008D +#define USAGE_BAT_CAPACITY_GRANULARITY_2 0x0085008E +#define USAGE_BAT_I_OEMINFORMATION 0x0085008F +#define USAGE_BAT_INHIBIT_CHARGE 0x008500C0 +#define USAGE_BAT_ENABLE_POLLING 0x008500C1 +#define USAGE_BAT_RESET_TO_ZERO 0x008500C2 +#define USAGE_BAT_AC_PRESENT 0x008500D0 +#define USAGE_BAT_BATTERY_PRESENT 0x008500D1 +#define USAGE_BAT_POWER_FAIL 0x008500D2 +#define USAGE_BAT_ALARM_INHIBITED 0x008500D3 +#define USAGE_BAT_THERMISTOR_UNDER_RANGE 0x008500D4 +#define USAGE_BAT_THERMISTOR_HOT 0x008500D5 +#define USAGE_BAT_THERMISTOR_COLD 0x008500D6 +#define USAGE_BAT_THERMISTOR_OVER_RANGE 0x008500D7 +#define USAGE_BAT_VOLTAGE_OUT_OF_RANGE 0x008500D8 +#define USAGE_BAT_CURRENT_OUT_OF_RANGE 0x008500D9 +#define USAGE_BAT_CURRENT_NOT_REGULATED 0x008500DA +#define USAGE_BAT_VOLTAGE_NOT_REGULATED 0x008500DB +#define USAGE_BAT_MASTER_MODE 0x008500DC +#define USAGE_BAT_CHARGER_SELECTOR_SUPPORT 0x008500F0 +#define USAGE_BAT_CHARGER_SPEC 0x008500F1 +#define USAGE_BAT_LEVEL_2 0x008500F2 +#define USAGE_BAT_LEVEL_3 0x008500F3 -/* Usages */ -#define USAGE_HIGHVOLTAGETRANSFER 0x54 -#define USAGE_VOLTAGE 0x30 /* * HIDNode_t struct * From 0905ef070acde06d4c3a9c3c3a2e6f4b44f12fd1 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sun, 13 Feb 2022 20:31:17 +0000 Subject: [PATCH 105/700] NUT-Monitor: Run py2to3-3.10 --- scripts/python/app/NUT-Monitor.in | 48 +++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/scripts/python/app/NUT-Monitor.in b/scripts/python/app/NUT-Monitor.in index 95c28d87ce..296b88bcaa 100755 --- a/scripts/python/app/NUT-Monitor.in +++ b/scripts/python/app/NUT-Monitor.in @@ -37,7 +37,7 @@ import platform import time import threading import optparse -import ConfigParser +import configparser import locale import gettext import PyNUT @@ -48,7 +48,7 @@ gobject.threads_init() class interface : - DESIRED_FAVORITES_DIRECTORY_MODE = 0700 + DESIRED_FAVORITES_DIRECTORY_MODE = 0o700 __widgets = {} __callbacks = {} @@ -211,7 +211,7 @@ class interface : self.gui_status_message( _("Welcome to NUT Monitor") ) if ( cmd_opts.favorite != None ) : - if ( self.__favorites.has_key( cmd_opts.favorite ) ) : + if ( cmd_opts.favorite in self.__favorites ) : self.__gui_load_favorite( fav_name=cmd_opts.favorite ) self.connect_to_ups() else : @@ -293,7 +293,7 @@ class interface : nut_handler = PyNUT.PyNUTClient( host=host, port=port, login=login, password=password ) upses = nut_handler.GetUPSList() - ups_list = upses.keys() + ups_list = list(upses.keys()) ups_list.sort() # If UPS list contains something, clear it @@ -368,7 +368,7 @@ class interface : # Remove the dummy combobox entry on list dialog_interface.get_widget("combobox2").remove_text( 0 ) - favs = self.__favorites.keys() + favs = list(self.__favorites.keys()) favs.sort() for current in favs : dialog_interface.get_widget("combobox2").append_text( current ) @@ -397,7 +397,7 @@ class interface : # Method called when user selects a favorite from the favorites menu def __gui_load_favorite( self, fav_name="" ) : - if ( self.__favorites.has_key( fav_name ) ) : + if ( fav_name in self.__favorites ) : # If auth is activated, process it before other fields to avoir weird # reactions with the 'check_gui_fields' function. if ( self.__favorites[fav_name].get("auth", False ) ) : @@ -497,7 +497,7 @@ class interface : self.__fav_menu_items = list() - items = self.__favorites.keys() + items = list(self.__favorites.keys()) items.sort() for current in items : @@ -520,7 +520,7 @@ class interface : # to avoid creating entries with the same name. def __gui_add_favorite_check_gui_fields( self, widget=None ) : fav_name = widget.get_text() - if ( len( fav_name ) > 0 ) and ( fav_name not in self.__favorites.keys() ) : + if ( len( fav_name ) > 0 ) and ( fav_name not in list(self.__favorites.keys()) ) : self.__widgets["favorites_dialog_button_add"].set_sensitive( True ) else : self.__widgets["favorites_dialog_button_add"].set_sensitive( False ) @@ -537,7 +537,7 @@ class interface : if ( not stat.S_IMODE( os.stat( self.__favorites_path ).st_mode ) == self.DESIRED_FAVORITES_DIRECTORY_MODE ) : # unsafe pre-1.2 directory found os.chmod( self.__favorites_path, self.DESIRED_FAVORITES_DIRECTORY_MODE ) - conf = ConfigParser.ConfigParser() + conf = configparser.ConfigParser() conf.read( self.__favorites_file ) for current in conf.sections() : # Check if mandatory fields are present @@ -564,7 +564,7 @@ class interface : except : # If the password is not in base64, let the field empty - print( _("Error parsing favorites, password for '%s' is not in base64\nSkipping password for this entry") % current ) + print(( _("Error parsing favorites, password for '%s' is not in base64\nSkipping password for this entry") % current )) fav_data["password"] = "" else : fav_data["auth"] = False @@ -586,10 +586,10 @@ class interface : except : self.gui_status_message( _("Error while creating configuration folder (%s)") % sys.exc_info()[1] ) - save_conf = ConfigParser.ConfigParser() - for current in self.__favorites.keys() : + save_conf = configparser.ConfigParser() + for current in list(self.__favorites.keys()) : save_conf.add_section( current ) - for k, v in self.__favorites[ current ].iteritems() : + for k, v in self.__favorites[ current ].items() : save_conf.set( current, k, v ) try : @@ -678,7 +678,7 @@ class interface : srv_upses = self.__ups_handler.GetUPSList() self.__current_ups = self.__widgets["ups_list_combo"].get_active_text() - if not srv_upses.has_key( self.__current_ups ) : + if self.__current_ups not in srv_upses : self.gui_status_message( _("Device '%s' not found on server") % self.__current_ups ) self.gui_status_notification( _("Device '%s' not found on server") % self.__current_ups, "warning.png" ) return @@ -692,7 +692,7 @@ class interface : self.__widgets["ups_params_box"].hide() commands = self.__ups_handler.GetUPSCommands( self.__current_ups ) - self.__ups_commands = commands.keys() + self.__ups_commands = list(commands.keys()) self.__ups_commands.sort() # Refresh UPS commands combo box @@ -726,8 +726,8 @@ class interface : self.__widgets["ups_vars_tree_store"].clear() - for k,v in vars.iteritems() : - if ( rwvars.has_key( k ) ) : + for k,v in vars.items() : + if ( k in rwvars ) : icon_file = os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "var-rw.png" ) else : icon_file = os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "var-ro.png" ) @@ -814,7 +814,7 @@ class gui_updater( threading.Thread ) : was_online = False # Check for additionnal information - for k,v in status_mapper.iteritems() : + for k,v in status_mapper.items() : if vars.get("ups.status").find(k) != -1 : if ( text_right != "" ) : text_right += " - %s" % v @@ -830,15 +830,15 @@ class gui_updater( threading.Thread ) : status_text += text_right text_right += "\n" - if ( vars.has_key( "ups.mfr" ) ) : + if ( "ups.mfr" in vars ) : text_left += "%s\n\n" % _("Model :") text_right += "%s\n%s\n" % ( vars.get("ups.mfr",""), vars.get("ups.model","") ) - if ( vars.has_key( "ups.temperature" ) ) : + if ( "ups.temperature" in vars ) : text_left += "%s\n" % _("Temperature :") text_right += "%s\n" % int( float( vars.get( "ups.temperature", 0 ) ) ) - if ( vars.has_key( "battery.voltage" ) ) : + if ( "battery.voltage" in vars ) : text_left += "%s\n" % _("Battery voltage :") text_right += "%sv\n" % vars.get( "battery.voltage", 0 ) @@ -846,7 +846,7 @@ class gui_updater( threading.Thread ) : self.__parent_class._interface__widgets["ups_status_right"].set_markup( text_right[:-1] ) # UPS load and battery charge progress bars - if ( vars.has_key( "battery.charge" ) ) : + if ( "battery.charge" in vars ) : charge = vars.get( "battery.charge", "0" ) self.__parent_class._interface__widgets["progress_battery_charge"].set_fraction( float( charge ) / 100.0 ) self.__parent_class._interface__widgets["progress_battery_charge"].set_text( "%s %%" % int( float( charge ) ) ) @@ -855,7 +855,7 @@ class gui_updater( threading.Thread ) : self.__parent_class._interface__widgets["progress_battery_charge"].set_fraction( 0.0 ) self.__parent_class._interface__widgets["progress_battery_charge"].set_text( _("Not available") ) - if ( vars.has_key( "ups.load" ) ) : + if ( "ups.load" in vars ) : load = vars.get( "ups.load", "0" ) self.__parent_class._interface__widgets["progress_battery_load"].set_fraction( float( load ) / 100.0 ) self.__parent_class._interface__widgets["progress_battery_load"].set_text( "%s %%" % int( float( load ) ) ) @@ -864,7 +864,7 @@ class gui_updater( threading.Thread ) : self.__parent_class._interface__widgets["progress_battery_load"].set_fraction( 0.0 ) self.__parent_class._interface__widgets["progress_battery_load"].set_text( _("Not available") ) - if ( vars.has_key( "battery.runtime" ) ) : + if ( "battery.runtime" in vars ) : autonomy = int( float( vars.get( "battery.runtime", 0 ) ) ) if ( autonomy >= 3600 ) : From 3718f46ed61f946bc96ce712f19eccefc66902d6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 14 Feb 2022 11:08:39 +0100 Subject: [PATCH 106/700] drivers/upsdrvctl: add "-d" option to pass debug level to drivers Closes: #1036 (Note: that issue discusses other possible improvements around this subject, which are synergetic with this one) --- docs/man/upsdrvctl.txt | 3 ++ drivers/upsdrvctl.c | 72 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/docs/man/upsdrvctl.txt b/docs/man/upsdrvctl.txt index da0593142d..aa09a962e1 100644 --- a/docs/man/upsdrvctl.txt +++ b/docs/man/upsdrvctl.txt @@ -57,6 +57,9 @@ This may be set in ups.conf with "user" in the global section. *-D*:: Raise the debug level. Use this multiple times for additional details. +*-d*:: +Pass the selected debug level from `upsdrvctl` to launched drivers. + COMMANDS -------- diff --git a/drivers/upsdrvctl.c b/drivers/upsdrvctl.c index a02a7c31b4..968677e186 100644 --- a/drivers/upsdrvctl.c +++ b/drivers/upsdrvctl.c @@ -30,6 +30,7 @@ #include "common.h" #include "upsconf.h" #include "attribute.h" +#include "nut_stdint.h" typedef struct { char *upsname; @@ -62,6 +63,9 @@ static char *driverpath = NULL; /* passthrough to the drivers: chroot path and new user name */ static char *pt_root = NULL, *pt_user = NULL; + /* flag to pass nut_debug_level to launched drivers (as their -D... args) */ +static int nut_debug_level_passthrough = 0; + void do_upsconf_args(char *upsname, char *var, char *val) { ups_t *tmp, *last; @@ -278,8 +282,8 @@ static void forkexec(char *const argv[], const ups_t *ups) static void start_driver(const ups_t *ups) { - char *argv[8]; - char dfn[SMALLBUF]; + char *argv[9]; + char dfn[SMALLBUF], dbg[SMALLBUF]; int ret, arg = 0; int initial_exec_error = exec_error, drv_maxretry = maxretry; struct stat fs; @@ -293,6 +297,63 @@ static void start_driver(const ups_t *ups) fatal_with_errno(EXIT_FAILURE, "Can't start %s", dfn); argv[arg++] = dfn; + + if (nut_debug_level_passthrough > 0 + && nut_debug_level > 0 + && sizeof(dbg) > 3 + ) { + size_t d, m; + + /* cut-off point: buffer size or requested debug level */ + m = sizeof(dbg) - 1; /* leave a place for '\0' */ + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wtautological-compare" +# pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + /* Different platforms, different sizes, none fits all... */ + /* can we fit this many 'D's? */ + if ((uintmax_t)SIZE_MAX > (uintmax_t)nut_debug_level /* else can't assign, requested debug level is huge */ + && (size_t)nut_debug_level + 1 < m + ) { +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif + /* need even fewer (leave a place for '-'): */ + m = (size_t)nut_debug_level + 1; + } else { + upsdebugx(1, "Requested debugging level %d is too " + "high for pass-through args, truncated to %zu", + nut_debug_level, + (m - 1) /* count off '-' (and '\0' already) chars */ + ); + } + + dbg[0] = '-'; + for (d = 1; d < m ; d++) { + dbg[d] = 'D'; + } + dbg[d] = '\0'; + argv[arg++] = dbg; + } + argv[arg++] = (char *)"-a"; /* FIXME: cast away const */ argv[arg++] = ups->upsname; @@ -349,6 +410,7 @@ static void help(const char *progname) printf(" -t testing mode - prints actions without doing them\n"); printf(" -u drivers started will switch from root to \n"); printf(" -D raise debugging level\n"); + printf(" -d pass debugging level from upsdrvctl to driver\n"); printf(" start start all UPS drivers in ups.conf\n"); printf(" start only start driver for UPS \n"); printf(" stop stop all UPS drivers in ups.conf\n"); @@ -476,7 +538,7 @@ int main(int argc, char **argv) UPS_VERSION); prog = argv[0]; - while ((i = getopt(argc, argv, "+htu:r:DV")) != -1) { + while ((i = getopt(argc, argv, "+htu:r:DdV")) != -1) { switch(i) { case 'r': pt_root = optarg; @@ -497,6 +559,10 @@ int main(int argc, char **argv) nut_debug_level++; break; + case 'd': + nut_debug_level_passthrough = 1; + break; + case 'h': default: help(prog); From 1c4edca806ef101418624de70a185a720c7213f6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 14 Feb 2022 14:47:29 +0100 Subject: [PATCH 107/700] drivers/upsdrvctl.c: only warn about passing debugging through to actual drivers if (nut_debug_level_passthrough == 0) --- drivers/upsdrvctl.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/upsdrvctl.c b/drivers/upsdrvctl.c index 968677e186..1f0bcd261f 100644 --- a/drivers/upsdrvctl.c +++ b/drivers/upsdrvctl.c @@ -582,14 +582,16 @@ int main(int argc, char **argv) nut_debug_level = 2; } - upsdebugx(2, "\n" - "If you're not a NUT core developer, chances are that you're told to enable debugging\n" - "to see why a driver isn't working for you. We're sorry for the confusion, but this is\n" - "the 'upsdrvctl' wrapper, not the driver you're interested in.\n\n" - "Below you'll find one or more lines starting with 'exec:' followed by an absolute\n" - "path to the driver binary and some command line option. This is what the driver\n" - "starts and you need to copy and paste that line and append the debug flags to that\n" - "line (less the 'exec:' prefix).\n"); + if (nut_debug_level_passthrough == 0) { + upsdebugx(2, "\n" + "If you're not a NUT core developer, chances are that you're told to enable debugging\n" + "to see why a driver isn't working for you. We're sorry for the confusion, but this is\n" + "the 'upsdrvctl' wrapper, not the driver you're interested in.\n\n" + "Below you'll find one or more lines starting with 'exec:' followed by an absolute\n" + "path to the driver binary and some command line option. This is what the driver\n" + "starts and you need to copy and paste that line and append the debug flags to that\n" + "line (less the 'exec:' prefix).\n"); + } if (!strcmp(argv[0], "start")) command = &start_driver; From 13b86cbe09e074f7c967c9c727e333a95f9f03d2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 14 Feb 2022 14:49:40 +0100 Subject: [PATCH 108/700] drivers/upsdrvctl.c: suggest "-d" when warning about passing debugging through to actual drivers --- drivers/upsdrvctl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/upsdrvctl.c b/drivers/upsdrvctl.c index 1f0bcd261f..a33fcd1596 100644 --- a/drivers/upsdrvctl.c +++ b/drivers/upsdrvctl.c @@ -590,7 +590,9 @@ int main(int argc, char **argv) "Below you'll find one or more lines starting with 'exec:' followed by an absolute\n" "path to the driver binary and some command line option. This is what the driver\n" "starts and you need to copy and paste that line and append the debug flags to that\n" - "line (less the 'exec:' prefix).\n"); + "line (less the 'exec:' prefix).\n\n" + "Alternately, provide an additional '-d' (lower-case) parameter to 'upsdrvctl' to\n" + "pass its current debug level to the launched driver.\n"); } if (!strcmp(argv[0], "start")) From b8faf7092253d676a8e08d6b288dca9bce298503 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 14 Feb 2022 15:46:36 +0100 Subject: [PATCH 109/700] drivers/main.c: allow to specify a user for each driver via ups.conf Closes: #1288 --- docs/man/ups.conf.txt | 14 ++++++++++++-- drivers/main.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/docs/man/ups.conf.txt b/docs/man/ups.conf.txt index 41a194c306..1def89c86d 100644 --- a/docs/man/ups.conf.txt +++ b/docs/man/ups.conf.txt @@ -113,8 +113,9 @@ of the driver behavior. *user*:: -Optional. Overrides the compiled-in default unprivileged username. See the -discussion of the `-u` option in linkman:nutupsdrv[8] for details. +Optional. Overrides the compiled-in default unprivileged username for +all NUT device drivers. See the discussion of the `-u` option in +linkman:nutupsdrv[8] for details. UPS FIELDS ---------- @@ -131,6 +132,15 @@ Required. This is the serial port where the UPS is connected. On a Linux system, the first serial port usually is '/dev/ttyS0'. On FreeBSD and similar systems, it probably will be '/dev/cuaa0'. +*user*:: + +Optional. Overrides the compiled-in (and/or global-section) default +unprivileged username for a particular NUT device driver. See the +discussion of the `-u` option in linkman:nutupsdrv[8] for details. +This may be specifically useful for ensuring access to dynamic device +filesystem nodes, such as USB (or serial-over-USB) hot-plug support, +or with device filesystems re-generated by an OS for every reboot. + *sdorder*:: Optional. When you have multiple UPSes on your system, you usually need to diff --git a/drivers/main.c b/drivers/main.c index a3f7026449..b7f80365c8 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -48,6 +48,7 @@ static vartab_t *vartab_h = NULL; /* variables possibly set by the global part of ups.conf */ time_t poll_interval = 2; static char *chroot_path = NULL, *user = NULL; +static int user_from_cmdline = 0; /* signal handling */ int exit_flag = 0; @@ -298,6 +299,24 @@ static int main_arg(char *var, char *val) return 1; /* handled */ } + /* user specified at the driver level overrides that on global level + * or the built-in default + */ + if (!strcmp(var, "user")) { + if (user_from_cmdline) { + upsdebugx(0, "User '%s' specified in driver section " + "was ignored due to '%s' specified on command line", + val, user); + } else { + upsdebugx(1, "Overriding previously specified user '%s' " + "with '%s' specified for driver section", + user, val); + free(user); + user = xstrdup(val); + } + return 1; /* handled */ + } + if (!strcmp(var, "sddelay")) { upslogx(LOG_INFO, "Obsolete value sddelay found in ups.conf"); return 1; /* handled */ @@ -342,8 +361,17 @@ static void do_global_args(const char *var, const char *val) } if (!strcmp(var, "user")) { - free(user); - user = xstrdup(val); + if (user_from_cmdline) { + upsdebugx(0, "User specified in global section '%s' " + "was ignored due to '%s' specified on command line", + val, user); + } else { + upsdebugx(1, "Overriding previously specified user '%s' " + "with '%s' specified in global section", + user, val); + free(user); + user = xstrdup(val); + } } if (!strcmp(var, "synchronous")) { @@ -591,8 +619,19 @@ int main(int argc, char **argv) chroot_path = xstrdup(optarg); break; case 'u': + if (user_from_cmdline) { + upsdebugx(1, "Previously specified user for drivers '%s' " + "was ignored due to '%s' specified on command line" + " (again?)", + user, optarg); + } else { + upsdebugx(1, "Built-in default user for drivers '%s' " + "was ignored due to '%s' specified on command line", + user, optarg); + } free(user); user = xstrdup(optarg); + user_from_cmdline = 1; break; case 'V': /* already printed the banner, so exit */ From dca3b599beca09647e64cfbf0ff085b0e5e12038 Mon Sep 17 00:00:00 2001 From: Nick Briggs Date: Mon, 14 Feb 2022 17:37:54 -0800 Subject: [PATCH 110/700] Only indicate report descriptor fixed if it was actually changed. --- drivers/apc-hid.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/apc-hid.c b/drivers/apc-hid.c index 903ad8d8d2..892b8c7406 100644 --- a/drivers/apc-hid.c +++ b/drivers/apc-hid.c @@ -510,6 +510,7 @@ static int apc_claim(HIDDevice_t *hd) { */ static int apc_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { HIDData_t *pData; + int res = 0; int vendorID = pDev->VendorID; int productID = pDev->ProductID; @@ -546,6 +547,7 @@ static int apc_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { pData->LogMax = hvt_logmax * 2; /* it may be smoking at this point */ upsdebugx(3, "Fixing Report Descriptor. Set voltage LogMin = %ld, LogMax = %ld", pData->LogMin , pData->LogMax); + res = 1; } } if ((pData=FindObject_with_ID_Node(pDesc_arg, 0x30, USAGE_POW_CONFIG_VOLTAGE))) { @@ -558,11 +560,11 @@ static int apc_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { pData->LogMax = 255; upsdebugx(3, "Fixing Report Descriptor. Set configVoltage LogMin = %ld, LogMax = %ld", pData->LogMin , pData->LogMax); + res = 1; } } - return 1; } - return 0; + return res; } subdriver_t apc_subdriver = { From 10a411bff1d9d30397d5e08962f920b5659ef0e1 Mon Sep 17 00:00:00 2001 From: Nick Briggs Date: Mon, 14 Feb 2022 22:48:59 -0800 Subject: [PATCH 111/700] Update APC HID driver version --- drivers/apc-hid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/apc-hid.c b/drivers/apc-hid.c index 892b8c7406..a1982595c1 100644 --- a/drivers/apc-hid.c +++ b/drivers/apc-hid.c @@ -32,7 +32,7 @@ #include "apc-hid.h" #include "usb-common.h" -#define APC_HID_VERSION "APC HID 0.97" +#define APC_HID_VERSION "APC HID 0.98" /* APC */ #define APC_VENDORID 0x051d From 933090d2d2847e720bb5ff12e47006795919e133 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Feb 2022 09:44:54 +0100 Subject: [PATCH 112/700] drivers/apc-hid.c: apc_fix_report_desc(): normalize indentations --- drivers/apc-hid.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/apc-hid.c b/drivers/apc-hid.c index a1982595c1..fd97ed2780 100644 --- a/drivers/apc-hid.c +++ b/drivers/apc-hid.c @@ -510,7 +510,7 @@ static int apc_claim(HIDDevice_t *hd) { */ static int apc_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { HIDData_t *pData; - int res = 0; + int res = 0; int vendorID = pDev->VendorID; int productID = pDev->ProductID; @@ -523,12 +523,12 @@ static int apc_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { /* Look at the High Voltage Transfer logical max value: * If the HVT logmax is greater than the configured or input voltage limit * then the configured/input voltage limits are probably incorrect. - * Arbitrarily set the input voltage logical min/max to 0 .. 2*HVT logmax and the - * configured (nominal) input voltage logical max to 255 (it's a single byte value) + * Arbitrarily set the input voltage logical min/max to 0 .. 2*HVT logmax and the + * configured (nominal) input voltage logical max to 255 (it's a single byte value) - * Path: UPS.Input.ConfigVoltage, Type: Feature, ReportID: 0x30, Offset: 0, Size: 8 - * Path: UPS.Input.Voltage, Type: Feature, ReportID: 0x31, Offset: 0, Size: 16 - * Path: UPS.Input.HighVoltageTransfer, Type: Feature, ReportID: 0x33, Offset: 0, Size: 16 + * Path: UPS.Input.ConfigVoltage, Type: Feature, ReportID: 0x30, Offset: 0, Size: 8 + * Path: UPS.Input.Voltage, Type: Feature, ReportID: 0x31, Offset: 0, Size: 16 + * Path: UPS.Input.HighVoltageTransfer, Type: Feature, ReportID: 0x33, Offset: 0, Size: 16 */ if ((pData=FindObject_with_ID_Node(pDesc_arg, 0x33, USAGE_POW_HIGH_VOLTAGE_TRANSFER))) { @@ -543,13 +543,13 @@ static int apc_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { voltage_logmin, voltage_logmax); if (hvt_logmax > voltage_logmax) { - pData->LogMin = 0; /* a reasonable lower limit for voltage */ + pData->LogMin = 0; /* a reasonable lower limit for voltage */ pData->LogMax = hvt_logmax * 2; /* it may be smoking at this point */ upsdebugx(3, "Fixing Report Descriptor. Set voltage LogMin = %ld, LogMax = %ld", pData->LogMin , pData->LogMax); - res = 1; - } - } + res = 1; + } + } if ((pData=FindObject_with_ID_Node(pDesc_arg, 0x30, USAGE_POW_CONFIG_VOLTAGE))) { long cvoltage_logmin = pData->LogMin; long cvoltage_logmax = pData->LogMax; @@ -560,9 +560,9 @@ static int apc_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { pData->LogMax = 255; upsdebugx(3, "Fixing Report Descriptor. Set configVoltage LogMin = %ld, LogMax = %ld", pData->LogMin , pData->LogMax); - res = 1; - } - } + res = 1; + } + } } return res; } From cfd7af3bd629b102d2247fc9b8511a14c8bfb3c2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Feb 2022 10:33:27 +0100 Subject: [PATCH 113/700] docs/hid-subdrivers.txt: document where USAGE_POW_ and USAGE_BAT_ macros come from --- docs/hid-subdrivers.txt | 30 ++++++++++++++++++++++++++++++ docs/nut.dict | 3 ++- drivers/hidtypes.h | 1 + 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/hid-subdrivers.txt b/docs/hid-subdrivers.txt index e48ecf7b95..ea5105a24e 100644 --- a/docs/hid-subdrivers.txt +++ b/docs/hid-subdrivers.txt @@ -127,6 +127,36 @@ shutdown.restart), and conversions of manufacturer specific data formats. +Usage macros in drivers/hidtypes.h +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `drivers/hidtypes.h` header provides a number of macro names +for entries in the standard usage tables for Power Device +`USAGE_POW_` and Battery System `USAGE_BAT_` +data pages. + +If NUT codebase would ever need to refresh those macros, here is +some background information (based on NUT issue #1189 and PR #1290): + +These data were parsed from (a very slightly updated version of) +https://github.com/abend0c1/hidrdd/blob/master/rd.conf file, which +incorporates the complete USB-IF usage definitions for Power Device +and Battery System pages (among many others), so we didn't have to +extract the names and values from the USB-IF standards documents +(did check it all by eye though). + +The file was processed with the following chain of commands: + +------ +:; grep -e '^0084' -e '^0085' rd.conf \ + | sed 's/,.*$//;s/ *$//' \ + | sed 's/ /_/g;s/_/ /' \ + | tr '[:lower:]' '[:upper:]' \ + | sed 's/\(0085.... \)/\1USAGE_BAT_/;s/\(0084.... \)/\1USAGE_POW_/;s/\([A-Z_]*\)_PAGE/PAGE_\1/' \ + | awk '{print "#define "$2" 0x"$1}' +------ + + Writing a subdriver ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/nut.dict b/docs/nut.dict index cc8afd5791..480e7fa005 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2863 utf-8 +personal_ws-1.1 en 2864 utf-8 AAS ACFAIL ACFREQ @@ -1843,6 +1843,7 @@ hh hibernate's hiddev hidparser +hidtypes hidups highbattery highfrequency diff --git a/drivers/hidtypes.h b/drivers/hidtypes.h index bbc16634cf..87d42df3c9 100644 --- a/drivers/hidtypes.h +++ b/drivers/hidtypes.h @@ -92,6 +92,7 @@ extern "C" { #define ATTR_NVOL_VOL 0x80 /* Usage Pages */ +/* For more details, please see docs/hid-subdrivers.txt */ #define PAGE_POWER_DEVICE 0x84 #define PAGE_BATTERY_SYSTEM 0x85 From d701928d1f5e6b137dab11c1d1fa70fd2812b9cd Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Feb 2022 10:33:53 +0100 Subject: [PATCH 114/700] docs/hid-subdrivers.txt: describe tech for "Fixing report descriptors" --- docs/hid-subdrivers.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/hid-subdrivers.txt b/docs/hid-subdrivers.txt index ea5105a24e..edf2b84395 100644 --- a/docs/hid-subdrivers.txt +++ b/docs/hid-subdrivers.txt @@ -225,6 +225,25 @@ subdrivers are written: - tripplite-hid.c/h +Fixing report descriptors +~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is a fact of life that fellow developers make mistakes, and firmware +authors do too. In some cases there are inconsistencies about bytes seen +on the wire vs. their logical values, such value range and signedness if +interpreting them according to standard. + +NUT drivers now include a way to detect and fix up known issues in such +flawed USB report descriptors, side-stepping the standard similarly where +deemed needed. A pointer to such hook method is part of the `subdriver_t` +structure detailing each `usbhid-ups` subdriver nuances, defaulting to +a `fix_report_desc()` trivial implementation. + +For some practical examples, see e.g. `apc_fix_report_desc()` method in the +`drivers/apc-hid.c` file, and `cps_fix_report_desc()` in `drivers/cps-hid.c` +file. + + Shutting down the UPS ~~~~~~~~~~~~~~~~~~~~~ From b6f208917f236b3eb6ded16088b9e2fab4d4f0d6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Feb 2022 12:23:35 +0100 Subject: [PATCH 115/700] drivers/main.c: trace do_global_args() and main_arg() var/val stream --- drivers/main.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/main.c b/drivers/main.c index b7f80365c8..cbc8f229ce 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -275,6 +275,11 @@ static int main_arg(char *var, char *val) { /* flags for main */ + upsdebugx(3, "%s: var='%s' val='%s'", + __func__, + var ? var : "", /* null should not happen... but... */ + val ? val : ""); + if (!strcmp(var, "nolock")) { do_lock_port = 0; dstate_setinfo("driver.flag.nolock", "enabled"); @@ -288,7 +293,7 @@ static int main_arg(char *var, char *val) /* any other flags are for the driver code */ if (!val) - return 0; + return 0; /* unhandled, pass it through to the driver */ /* variables for main: port */ @@ -345,6 +350,11 @@ static int main_arg(char *var, char *val) static void do_global_args(const char *var, const char *val) { + upsdebugx(3, "%s: var='%s' val='%s'", + __func__, + var ? var : "", /* null should not happen... but... */ + val ? val : ""); + if (!strcmp(var, "pollinterval")) { int ipv = atoi(val); if (ipv > 0) { From a1de29df3119eefd5e0630f70a5a9b68c7145cde Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Feb 2022 12:36:04 +0100 Subject: [PATCH 116/700] drivers/main.c: re-word debug message for "-u name" overriding built-in or configured values --- drivers/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index cbc8f229ce..60844dc488 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -635,8 +635,9 @@ int main(int argc, char **argv) " (again?)", user, optarg); } else { - upsdebugx(1, "Built-in default user for drivers '%s' " - "was ignored due to '%s' specified on command line", + upsdebugx(1, "Built-in default or configured user " + "for drivers '%s' was ignored due to '%s' " + "specified on command line", user, optarg); } free(user); From 168388f49ceaee556d94d62940d284cf5e765c20 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Feb 2022 15:41:35 +0100 Subject: [PATCH 117/700] drivers/main.c + dstate.{c,h}: refactor dstate_init() to return a copy of the "sockname" path used --- drivers/dstate.c | 5 ++++- drivers/dstate.h | 2 +- drivers/main.c | 7 +++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 9076ee91b3..0e89a76f8d 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -602,7 +602,7 @@ static void sock_close(void) /* interface */ -void dstate_init(const char *prog, const char *devname) +char * dstate_init(const char *prog, const char *devname) { char sockname[SMALLBUF]; @@ -625,6 +625,9 @@ void dstate_init(const char *prog, const char *devname) sockfd = sock_open(sockname); upsdebugx(2, "dstate_init: sock %s open on fd %d", sockname, sockfd); + + /* NOTE: Caller must free this string */ + return xstrdup(sockname); } /* returns 1 if timeout expired or data is available on UPS fd, 0 otherwise */ diff --git a/drivers/dstate.h b/drivers/dstate.h index 76a645de3c..94829dfcd3 100644 --- a/drivers/dstate.h +++ b/drivers/dstate.h @@ -50,7 +50,7 @@ typedef struct conn_s { * Defaults to nonblocking, for backward compatibility */ extern int do_synchronous; -void dstate_init(const char *prog, const char *devname); +char * dstate_init(const char *prog, const char *devname); int dstate_poll_fds(struct timeval timeout, int extrafd); int dstate_setinfo(const char *var, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); diff --git a/drivers/main.c b/drivers/main.c index 60844dc488..4acef15e20 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -4,6 +4,7 @@ 1999 Russell Kroll 2005 - 2017 Arnaud Quette 2017 Eaton (author: Emilien Kia ) + 2017 - 2022 Jim Klimov 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 @@ -791,8 +792,10 @@ int main(int argc, char **argv) /* now we can start servicing requests */ /* Only write pid if we're not just dumping data, for discovery */ - if (!dump_data) - dstate_init(progname, upsname); + if (!dump_data) { + char * sockname = dstate_init(progname, upsname); + free(sockname); + } /* The poll_interval may have been changed from the default */ dstate_setinfo("driver.parameter.pollinterval", "%jd", (intmax_t)poll_interval); From 70fd66d2b27d206ee6e56e3a242b6076f30113c4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Feb 2022 16:17:41 +0100 Subject: [PATCH 118/700] drivers/main.c: support setting group name for socket file (ups.conf, CLI -g arg) Closes: #1296 --- docs/man/nutupsdrv.txt | 5 ++ docs/man/ups.conf.txt | 20 +++++++ drivers/main.c | 116 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 137 insertions(+), 4 deletions(-) diff --git a/docs/man/nutupsdrv.txt b/docs/man/nutupsdrv.txt index 015dba5f5b..ae30fd8073 100644 --- a/docs/man/nutupsdrv.txt +++ b/docs/man/nutupsdrv.txt @@ -132,6 +132,11 @@ USB-based drivers. However, you will want to remove this option later in order to avoid permission conflicts between the driver and the unprivileged copy of linkman:upsd[8]. +*-g* 'groupname':: +Override the unprivileged group name that the driver may use after startup +to set permissions for the filesystem socket so `upsd` may still access it +if the run-time `user` of the driver normally would deny that access. + *-x* 'var'='val':: Define a variable called 'var' with the value of 'var' in the driver. This varies from driver to driver - see the specific man pages diff --git a/docs/man/ups.conf.txt b/docs/man/ups.conf.txt index 1def89c86d..80cacce02b 100644 --- a/docs/man/ups.conf.txt +++ b/docs/man/ups.conf.txt @@ -117,6 +117,16 @@ Optional. Overrides the compiled-in default unprivileged username for all NUT device drivers. See the discussion of the `-u` option in linkman:nutupsdrv[8] for details. +*group*:: + +Optional. Overrides the compiled-in (and/or global-section) default +unprivileged group name for all NUT device drivers, used for the +socket file access. See the discussion of the `-g` option in +linkman:nutupsdrv[8] for details. +This may be specifically useful for ensuring access to dynamic device +filesystem nodes, such as USB (or serial-over-USB) hot-plug support, +or with device filesystems re-generated by an OS for every reboot. + UPS FIELDS ---------- *driver*:: @@ -141,6 +151,16 @@ This may be specifically useful for ensuring access to dynamic device filesystem nodes, such as USB (or serial-over-USB) hot-plug support, or with device filesystems re-generated by an OS for every reboot. +*group*:: + +Optional. Overrides the compiled-in (and/or global-section) default +unprivileged group name for a particular NUT device driver, used for +the socket file access. See the discussion of the `-g` option in +linkman:nutupsdrv[8] for details. +This may be specifically useful for ensuring access to dynamic device +filesystem nodes, such as USB (or serial-over-USB) hot-plug support, +or with device filesystems re-generated by an OS for every reboot. + *sdorder*:: Optional. When you have multiple UPSes on your system, you usually need to diff --git a/drivers/main.c b/drivers/main.c index 4acef15e20..97ef1afe8b 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -27,6 +27,11 @@ #include "dstate.h" #include "attribute.h" +#include +#include +#include +#include + /* data which may be useful to the drivers */ int upsfd = -1; char *device_path = NULL; @@ -46,10 +51,12 @@ static int upsname_found = 0; static vartab_t *vartab_h = NULL; -/* variables possibly set by the global part of ups.conf */ +/* variables possibly set by the global part of ups.conf + * user and group may be set globally or per-driver + */ time_t poll_interval = 2; -static char *chroot_path = NULL, *user = NULL; -static int user_from_cmdline = 0; +static char *chroot_path = NULL, *user = NULL, *group = NULL; +static int user_from_cmdline = 0, group_from_cmdline = 0; /* signal handling */ int exit_flag = 0; @@ -122,6 +129,7 @@ static void help_msg(void) printf(" -i - poll interval\n"); printf(" -r - chroot to \n"); printf(" -u - switch to (if started as root)\n"); + printf(" -g - set pipe access to (if started as root)\n"); printf(" -x = - set driver variable to \n"); printf(" - example: -x cable=940-0095B\n\n"); @@ -323,6 +331,21 @@ static int main_arg(char *var, char *val) return 1; /* handled */ } + if (!strcmp(var, "group")) { + if (group_from_cmdline) { + upsdebugx(0, "Group '%s' specified in driver section " + "was ignored due to '%s' specified on command line", + val, group); + } else { + upsdebugx(1, "Overriding previously specified group '%s' " + "with '%s' specified for driver section", + group, val); + free(group); + group = xstrdup(val); + } + return 1; /* handled */ + } + if (!strcmp(var, "sddelay")) { upslogx(LOG_INFO, "Obsolete value sddelay found in ups.conf"); return 1; /* handled */ @@ -385,6 +408,20 @@ static void do_global_args(const char *var, const char *val) } } + if (!strcmp(var, "group")) { + if (group_from_cmdline) { + upsdebugx(0, "Group specified in global section '%s' " + "was ignored due to '%s' specified on command line", + val, group); + } else { + upsdebugx(1, "Overriding previously specified group '%s' " + "with '%s' specified in global section", + group, val); + free(group); + group = xstrdup(val); + } + } + if (!strcmp(var, "synchronous")) { if (!strcmp(val, "yes")) do_synchronous=1; @@ -522,6 +559,7 @@ static void exit_cleanup(void) free(chroot_path); free(device_path); free(user); + free(group); if (pidfn) { unlink(pidfn); @@ -572,6 +610,9 @@ int main(int argc, char **argv) /* pick up a default from configure --with-user */ user = xstrdup(RUN_AS_USER); /* xstrdup: this gets freed at exit */ + /* pick up a default from configure --with-group */ + group = xstrdup(RUN_AS_GROUP); /* xstrdup: this gets freed at exit */ + progname = xbasename(argv[0]); open_syslog(progname); @@ -585,7 +626,7 @@ int main(int argc, char **argv) /* build the driver's extra (-x) variable table */ upsdrv_makevartable(); - while ((i = getopt(argc, argv, "+a:s:kDd:hx:Lqr:u:Vi:")) != -1) { + while ((i = getopt(argc, argv, "+a:s:kDd:hx:Lqr:u:g:Vi:")) != -1) { switch (i) { case 'a': upsname = optarg; @@ -645,6 +686,22 @@ int main(int argc, char **argv) user = xstrdup(optarg); user_from_cmdline = 1; break; + case 'g': + if (group_from_cmdline) { + upsdebugx(1, "Previously specified group for drivers '%s' " + "was ignored due to '%s' specified on command line" + " (again?)", + group, optarg); + } else { + upsdebugx(1, "Built-in default or configured group " + "for drivers '%s' was ignored due to '%s' " + "specified on command line", + group, optarg); + } + free(group); + group = xstrdup(optarg); + group_from_cmdline = 1; + break; case 'V': /* already printed the banner, so exit */ exit(EXIT_SUCCESS); @@ -794,6 +851,57 @@ int main(int argc, char **argv) /* Only write pid if we're not just dumping data, for discovery */ if (!dump_data) { char * sockname = dstate_init(progname, upsname); + /* Normally we stick to the built-in account info, + * so if they were not over-ridden - no-op here: + */ + if (strcmp(group, RUN_AS_GROUP) + || strcmp(user, RUN_AS_USER) + ) { + /* Tune group access permission to the pipe, + * so that upsd can access it (using the + * specified or retained default group): + */ + struct group *grp = getgrnam(group); + upsdebugx(1, "Group and/or user account for this driver " + "was customized ('%s/%s') compared to built-in " + "defaults. Fixing socket '%s' ownership/access.", + user, group, sockname); + + if (grp == NULL) { + upsdebugx(1, "WARNING: could not resolve " + "group name '%s': %s", + group, strerror(errno) + ); + } else { + struct stat statbuf; + mode_t mode; + if (chown(sockname, -1, grp->gr_gid)) { + upsdebugx(1, "WARNING: chown failed: %s", + strerror(errno) + ); + } + + if (stat(sockname, &statbuf)) { + /* Logically we'd fail chown above if file + * does not exist or is not accessible, but + * practically we only need stat for chmod + */ + upsdebugx(1, "WARNING: stat failed: %s", + strerror(errno) + ); + } else { + /* chmod g+rw sockname */ + mode = statbuf.st_mode; + mode |= S_IWGRP; + mode |= S_IRGRP; + if (chmod(sockname, mode)) { + upsdebugx(1, "WARNING: chmod failed: %s", + strerror(errno) + ); + } + } + } + } free(sockname); } From a9eb781fe412cd1151849a2f3bb49e245ad25395 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Feb 2022 16:28:00 +0100 Subject: [PATCH 119/700] drivers/main.c: report whether we succeeded or failed chown/chmod for sockname --- drivers/main.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/main.c b/drivers/main.c index 97ef1afe8b..dfc6501f3b 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -857,6 +857,7 @@ int main(int argc, char **argv) if (strcmp(group, RUN_AS_GROUP) || strcmp(user, RUN_AS_USER) ) { + int allOk = 1; /* Tune group access permission to the pipe, * so that upsd can access it (using the * specified or retained default group): @@ -872,6 +873,7 @@ int main(int argc, char **argv) "group name '%s': %s", group, strerror(errno) ); + allOk = 0; } else { struct stat statbuf; mode_t mode; @@ -879,6 +881,7 @@ int main(int argc, char **argv) upsdebugx(1, "WARNING: chown failed: %s", strerror(errno) ); + allOk = 0; } if (stat(sockname, &statbuf)) { @@ -889,6 +892,7 @@ int main(int argc, char **argv) upsdebugx(1, "WARNING: stat failed: %s", strerror(errno) ); + allOk = 0; } else { /* chmod g+rw sockname */ mode = statbuf.st_mode; @@ -898,9 +902,23 @@ int main(int argc, char **argv) upsdebugx(1, "WARNING: chmod failed: %s", strerror(errno) ); + allOk = 0; } } } + + if (allOk) { + upsdebugx(1, "Group access for this driver successfully fixed"); + } else { + upsdebugx(0, "WARNING: Needed to fix group access " + "to filesystem socket of this driver, but failed; " + "run the driver with more debugging to see how exactly.\n" + "Consumers of the socket, such as upsd data server, " + "can fail to interact with the driver and represent " + "the device: %s", + sockname); + } + } free(sockname); } From a954d30bafcffb9a5d204c5503be120fbd8d1c62 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Feb 2022 16:41:33 +0100 Subject: [PATCH 120/700] configure.ac: report default/detected RUN_AS_USER/RUN_AS_GROUP values in help; check if "nobody" group is not resolvable when "nogroup" us to use it as default RUN_AS_GROUP instead --- configure.ac | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index ae18c5b0c0..08fb080c83 100644 --- a/configure.ac +++ b/configure.ac @@ -101,6 +101,9 @@ fi RUN_AS_USER="nobody" RUN_AS_GROUP="nobody" +AS_IF([test -n "`getent group nogroup`" && ! test -n "`getent group "${RUN_AS_GROUP}"`"], + [RUN_AS_GROUP="nogroup"] +) PIDPATH="/var/run" dnl Define directory where LIBOBJS replacement functions are @@ -1647,7 +1650,7 @@ AC_MSG_RESULT(${PORT}) AC_MSG_CHECKING(user to run as) AC_ARG_WITH(user, - AS_HELP_STRING([--with-user=username], [user for programs started as root (nobody)]), + AS_HELP_STRING([--with-user=username], [user for programs started as root (${RUN_AS_USER})]), [ case "${withval}" in yes|no) @@ -1666,7 +1669,7 @@ AC_MSG_RESULT(${RUN_AS_USER}) AC_MSG_CHECKING(group membership of user to run as) AC_ARG_WITH(group, - AS_HELP_STRING([--with-group=groupname], [group membership of user for programs started as root (nogroup)]), + AS_HELP_STRING([--with-group=groupname], [group membership of user for programs started as root (${RUN_AS_GROUP})]), [ case "${withval}" in yes|no) From 7113a89c4f881496e11c98d4b9f452ff3ee570d3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Feb 2022 16:57:47 +0100 Subject: [PATCH 121/700] drivers/main.c: fix user:group separator in debug message --- drivers/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/main.c b/drivers/main.c index dfc6501f3b..242e39c7f2 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -864,7 +864,7 @@ int main(int argc, char **argv) */ struct group *grp = getgrnam(group); upsdebugx(1, "Group and/or user account for this driver " - "was customized ('%s/%s') compared to built-in " + "was customized ('%s:%s') compared to built-in " "defaults. Fixing socket '%s' ownership/access.", user, group, sockname); From cae9d585b1e4c87a803fd646a78d234b183507c4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 10:39:38 +0000 Subject: [PATCH 122/700] include/common.h, common.c: extend sendsignalfn() return value for more error types --- common/common.c | 9 ++++++--- include/common.h | 8 +++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/common/common.c b/common/common.c index 472d15ecac..7cfd54cc6d 100644 --- a/common/common.c +++ b/common/common.c @@ -302,7 +302,10 @@ void writepid(const char *name) umask(mask); } -/* open pidfn, get the pid, then send it sig */ +/* open pidfn, get the pid, then send it sig + * returns negative codes for errors, or + * zero for a successfully sent signal + */ int sendsignalfn(const char *pidfn, int sig) { char buf[SMALLBUF]; @@ -313,13 +316,13 @@ int sendsignalfn(const char *pidfn, int sig) pidf = fopen(pidfn, "r"); if (!pidf) { upslog_with_errno(LOG_NOTICE, "fopen %s", pidfn); - return -1; + return -3; } if (fgets(buf, sizeof(buf), pidf) == NULL) { upslogx(LOG_NOTICE, "Failed to read pid from %s", pidfn); fclose(pidf); - return -1; + return -2; } { /* scoping */ diff --git a/include/common.h b/include/common.h index 67deaff905..66be3be4fa 100644 --- a/include/common.h +++ b/include/common.h @@ -112,7 +112,13 @@ int snprintfcat(char *dst, size_t size, const char *fmt, ...) /* Report maximum platform value for the pid_t */ pid_t get_max_pid_t(void); -/* open , get the pid, then send it */ +/* open , get the pid, then send it + * returns zero for successfully sent signal, + * negative for errors: + * -3 PID file not found + * -2 PID file not parsable + * -1 Error sending signal + */ int sendsignalfn(const char *pidfn, int sig); const char *xbasename(const char *file); From dfd955fd9c72c347c34ec8a0991d03e185c2d969 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 10:45:41 +0000 Subject: [PATCH 123/700] common/common.c: sendsignalfn(): if we use sig==0 to probe that a process runs, no need to send that twice --- common/common.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/common/common.c b/common/common.c index 7cfd54cc6d..e90279a193 100644 --- a/common/common.c +++ b/common/common.c @@ -349,13 +349,15 @@ int sendsignalfn(const char *pidfn, int sig) return -1; } - /* now actually send it */ - ret = kill(pid, sig); - - if (ret < 0) { - perror("kill"); - fclose(pidf); - return -1; + if (sig != 0) { + /* now actually send it */ + ret = kill(pid, sig); + + if (ret < 0) { + perror("kill"); + fclose(pidf); + return -1; + } } fclose(pidf); From 3c06eca39e17e5d770267d1b4ebb19fd53d89d78 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 10:48:29 +0000 Subject: [PATCH 124/700] server/upsd.c: handle extended return values from sendsignalfn() to tell more about the error --- server/upsd.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/server/upsd.c b/server/upsd.c index 68e7e7ba85..e53c1eb6bb 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -1341,10 +1341,26 @@ int main(int argc, char **argv) /* otherwise, we are being asked to start. * so check if a previous instance is running by sending signal '0' * (Ie 'kill 0') */ - if (sendsignalfn(pidfn, 0) == 0) { + cmdret = sendsignalfn(pidfn, 0); + switch (cmdret) { + case 0: printf("Fatal error: A previous upsd instance is already running!\n"); printf("Either stop the previous instance first, or use the 'reload' command.\n"); exit(EXIT_FAILURE); + + case -3: + case -2: + upslogx(LOG_WARNING, "Could not %s PID file '%s' " + "to see if previous upsd instance is " + "already running!\n", + (cmdret == -3 ? "find" : "parse"), + pidfn); + break; + + case -1: + default: + /* Just failed to send signal, no competitor running */ + break; } argc -= optind; From f04c0268db773c8cbea9c8efb0ba4cbadb2f79be Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 10:55:10 +0000 Subject: [PATCH 125/700] server/upsd.c: warn about not saving a pid file --- server/upsd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server/upsd.c b/server/upsd.c index e53c1eb6bb..0eb6e7083f 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -1458,6 +1458,7 @@ int main(int argc, char **argv) background(); writepid(pidfn); } else { + upslogx(LOG_WARNING, "Running as foreground process, not saving a PID file"); memset(pidfn, 0, sizeof(pidfn)); } From b4f4f28557f82a0f8c7c0b7e018a60f040fd8d6f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 11:18:26 +0000 Subject: [PATCH 126/700] include/common.h, common.c: refactor sendsignalfn() into parsepid() and sendsignalpid() reusable methods --- common/common.c | 97 ++++++++++++++++++++++++++++++++---------------- include/common.h | 8 ++++ 2 files changed, 72 insertions(+), 33 deletions(-) diff --git a/common/common.c b/common/common.c index e90279a193..a1ed3a2c9c 100644 --- a/common/common.c +++ b/common/common.c @@ -302,41 +302,17 @@ void writepid(const char *name) umask(mask); } -/* open pidfn, get the pid, then send it sig - * returns negative codes for errors, or +/* send sig to pid, returns -1 for error, or * zero for a successfully sent signal */ -int sendsignalfn(const char *pidfn, int sig) +int sendsignalpid(pid_t pid, int sig) { - char buf[SMALLBUF]; - FILE *pidf; - pid_t pid = -1; int ret; - pidf = fopen(pidfn, "r"); - if (!pidf) { - upslog_with_errno(LOG_NOTICE, "fopen %s", pidfn); - return -3; - } - - if (fgets(buf, sizeof(buf), pidf) == NULL) { - upslogx(LOG_NOTICE, "Failed to read pid from %s", pidfn); - fclose(pidf); - return -2; - } - - { /* scoping */ - intmax_t _pid = strtol(buf, (char **)NULL, 10); /* assuming 10 digits for a long */ - if (_pid <= get_max_pid_t()) { - pid = (pid_t)_pid; - } else { - upslogx(LOG_NOTICE, "Received a pid number too big for a pid_t: %" PRIdMAX, _pid); - } - } - - if (pid < 2) { - upslogx(LOG_NOTICE, "Ignoring invalid pid number %" PRIdMAX, (intmax_t) pid); - fclose(pidf); + if (pid < 2 || pid > get_max_pid_t()) { + upslogx(LOG_NOTICE, + "Ignoring invalid pid number %" PRIdMAX, + (intmax_t) pid); return -1; } @@ -345,7 +321,6 @@ int sendsignalfn(const char *pidfn, int sig) if (ret < 0) { perror("kill"); - fclose(pidf); return -1; } @@ -355,15 +330,71 @@ int sendsignalfn(const char *pidfn, int sig) if (ret < 0) { perror("kill"); - fclose(pidf); return -1; } } - fclose(pidf); return 0; } +/* parses string buffer into a pid_t if it passes + * a few sanity checks; returns -1 on error + */ +pid_t parsepid(const char *buf) +{ + pid_t pid = -1; + + /* assuming 10 digits for a long */ + intmax_t _pid = strtol(buf, (char **)NULL, 10); + if (_pid <= get_max_pid_t()) { + pid = (pid_t)_pid; + } else { + upslogx(LOG_NOTICE, "Received a pid number too big for a pid_t: %" PRIdMAX, _pid); + } + + return pid; +} + +/* open pidfn, get the pid, then send it sig + * returns negative codes for errors, or + * zero for a successfully sent signal + */ +int sendsignalfn(const char *pidfn, int sig) +{ + char buf[SMALLBUF]; + FILE *pidf; + pid_t pid = -1; + int ret = -1; + + pidf = fopen(pidfn, "r"); + if (!pidf) { + upslog_with_errno(LOG_NOTICE, "fopen %s", pidfn); + return -3; + } + + if (fgets(buf, sizeof(buf), pidf) == NULL) { + upslogx(LOG_NOTICE, "Failed to read pid from %s", pidfn); + fclose(pidf); + return -2; + } + /* TOTHINK: Original code only closed pidf before + * exiting the method, on error or "normally". + * Why not here? Do we want an (exclusive?) hold + * on it while being active in the method? + */ + + /* this method actively reports errors, if any */ + pid = parsepid(buf); + + if (pid >= 0) { + /* this method actively reports errors, if any */ + ret = sendsignalpid(pid, sig); + } + + fclose(pidf); + return ret; +} + int snprintfcat(char *dst, size_t size, const char *fmt, ...) { va_list ap; diff --git a/include/common.h b/include/common.h index 66be3be4fa..7358faeec8 100644 --- a/include/common.h +++ b/include/common.h @@ -103,6 +103,10 @@ void chroot_start(const char *path); /* write a pid file - is a full pathname *or* just the program name */ void writepid(const char *name); +/* parses string buffer into a pid_t if it passes + * a few sanity checks; returns -1 on error */ +pid_t parsepid(const char *buf); + /* send a signal to another running process */ int sendsignal(const char *progname, int sig); @@ -112,6 +116,10 @@ int snprintfcat(char *dst, size_t size, const char *fmt, ...) /* Report maximum platform value for the pid_t */ pid_t get_max_pid_t(void); +/* send sig to pid after some sanity checks, returns + * -1 for error, or zero for a successfully sent signal */ +int sendsignalpid(pid_t pid, int sig); + /* open , get the pid, then send it * returns zero for successfully sent signal, * negative for errors: From 0f194029d9cc6b9fd274008c9bf043e3f4c6ae58 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 11:19:15 +0000 Subject: [PATCH 127/700] server/upsd.c: add "-P PID" arg for commands [for #1299] --- docs/man/upsd.txt | 5 +++++ server/upsd.c | 21 ++++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/docs/man/upsd.txt b/docs/man/upsd.txt index 771668a688..e2524c1c90 100644 --- a/docs/man/upsd.txt +++ b/docs/man/upsd.txt @@ -43,6 +43,11 @@ are: *reload*;; reread configuration files *stop*;; stop process and exit +*-P* 'pid':: +Send the command signal above using specified PID number, rather than +consulting the PID file. This can help define service units which +start `upsd` as a foreground process so it does not create a PID file. + *-D*:: Raise the debugging level. upsd will run in the foreground by default, and will print information on stdout about the monitoring process. diff --git a/server/upsd.c b/server/upsd.c index 0eb6e7083f..8342d4bdff 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -1173,6 +1173,7 @@ static void help(const char *arg_progname) printf(" commands:\n"); printf(" - reload: reread configuration files\n"); printf(" - stop: stop process and exit\n"); + printf(" -P send the signal above to specified PID (bypassing PID file)\n"); printf(" -D raise debugging level (and stay foreground by default)\n"); printf(" -F stay foregrounded even if no debugging is enabled\n"); printf(" -B stay backgrounded even if debugging is bumped\n"); @@ -1251,6 +1252,7 @@ int main(int argc, char **argv) char *chroot_path = NULL; const char *user = RUN_AS_USER; struct passwd *new_uid = NULL; + pid_t oldpid = -1; progname = xbasename(argv[0]); @@ -1263,7 +1265,7 @@ int main(int argc, char **argv) printf("Network UPS Tools %s %s\n", progname, UPS_VERSION); - while ((i = getopt(argc, argv, "+h46p:qr:i:fu:Vc:DFB")) != -1) { + while ((i = getopt(argc, argv, "+h46p:qr:i:fu:Vc:P:DFB")) != -1) { switch (i) { case 'p': case 'i': @@ -1301,6 +1303,11 @@ int main(int argc, char **argv) help(progname); break; + case 'P': + if ((oldpid = parsepid(optarg)) < 0) + help(progname); + break; + case 'D': nut_debug_level++; break; @@ -1334,14 +1341,22 @@ int main(int argc, char **argv) } if (cmd) { - cmdret = sendsignalfn(pidfn, cmd); + if (oldpid < 0) { + cmdret = sendsignalfn(pidfn, cmd); + } else { + cmdret = sendsignalpid(oldpid, cmd); + } exit((cmdret == 0)?EXIT_SUCCESS:EXIT_FAILURE); } /* otherwise, we are being asked to start. * so check if a previous instance is running by sending signal '0' * (Ie 'kill 0') */ - cmdret = sendsignalfn(pidfn, 0); + if (oldpid < 0) { + cmdret = sendsignalfn(pidfn, 0); + } else { + cmdret = sendsignalpid(oldpid, 0); + } switch (cmdret) { case 0: printf("Fatal error: A previous upsd instance is already running!\n"); From 9af2a4feb10b57f575c4483dfe28e96674e14221 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 11:37:40 +0000 Subject: [PATCH 128/700] upsd.c: add -FF option to stay foregrounded AND write the PID file, use it in systemd/nut-server.service [for #1299] --- docs/man/upsd.txt | 2 ++ scripts/systemd/nut-server.service.in | 2 +- server/upsd.c | 17 ++++++++++++++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/man/upsd.txt b/docs/man/upsd.txt index e2524c1c90..3dc2aebcf3 100644 --- a/docs/man/upsd.txt +++ b/docs/man/upsd.txt @@ -47,6 +47,7 @@ are: Send the command signal above using specified PID number, rather than consulting the PID file. This can help define service units which start `upsd` as a foreground process so it does not create a PID file. +See also `-FF` option as an alternative. *-D*:: Raise the debugging level. upsd will run in the foreground by default, @@ -55,6 +56,7 @@ Use this option multiple times for more details. *-F*:: upsd will run in the foreground, regardless of debugging settings. +Specify twice (`-FF` or `-F -F`) to save the PID file even in this mode. *-B*:: upsd will run in the background, regardless of debugging settings. diff --git a/scripts/systemd/nut-server.service.in b/scripts/systemd/nut-server.service.in index 0ad8a5e092..15d54f7585 100644 --- a/scripts/systemd/nut-server.service.in +++ b/scripts/systemd/nut-server.service.in @@ -20,7 +20,7 @@ PartOf=nut.target [Service] EnvironmentFile=-@CONFPATH@/nut.conf SyslogIdentifier=%N -ExecStart=@SBINDIR@/upsd -F +ExecStart=@SBINDIR@/upsd -FF ExecReload=@SBINDIR@/upsd -c reload [Install] diff --git a/server/upsd.c b/server/upsd.c index 8342d4bdff..4a709aa8c1 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -1176,6 +1176,7 @@ static void help(const char *arg_progname) printf(" -P send the signal above to specified PID (bypassing PID file)\n"); printf(" -D raise debugging level (and stay foreground by default)\n"); printf(" -F stay foregrounded even if no debugging is enabled\n"); + printf(" -FF stay foregrounded and still save the PID file\n"); printf(" -B stay backgrounded even if debugging is bumped\n"); printf(" -h display this help\n"); printf(" -r chroots to \n"); @@ -1312,7 +1313,12 @@ int main(int argc, char **argv) nut_debug_level++; break; case 'F': - foreground = 1; + if (foreground > 0) { + /* specified twice to save PID file anyway */ + foreground = 2; + } else { + foreground = 1; + } break; case 'B': foreground = 0; @@ -1473,8 +1479,13 @@ int main(int argc, char **argv) background(); writepid(pidfn); } else { - upslogx(LOG_WARNING, "Running as foreground process, not saving a PID file"); - memset(pidfn, 0, sizeof(pidfn)); + if (foreground == 2) { + upslogx(LOG_WARNING, "Running as foreground process, but saving a PID file anyway"); + writepid(pidfn); + } else { + upslogx(LOG_WARNING, "Running as foreground process, not saving a PID file"); + memset(pidfn, 0, sizeof(pidfn)); + } } /* initialize SSL (keyfile must be readable by nut user) */ From 35ea592a88ce4ea09ba041219ac6ecf3a27358f4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 11:59:44 +0000 Subject: [PATCH 129/700] clients/upsmon.c: add "-P pid" arg handling, and report result of sendsignal(), like in upsd [for #1299, #123] --- clients/upsmon.c | 42 +++++++++++++++++++++++++++++++++++++----- docs/man/upsmon.txt | 8 +++++++- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index 941b36d3b0..430258cd77 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -1809,6 +1809,7 @@ static void help(const char *arg_progname) printf(" - fsd: shutdown all primary-mode UPSes (use with caution)\n"); printf(" - reload: reread configuration\n"); printf(" - stop: stop monitoring and exit\n"); + printf(" -P send the signal above to specified PID (bypassing PID file)\n"); printf(" -D raise debugging level (and stay foreground by default)\n"); printf(" -F stay foregrounded even if no debugging is enabled\n"); printf(" -B stay backgrounded even if debugging is bumped\n"); @@ -2042,7 +2043,8 @@ static void check_parent(void) int main(int argc, char *argv[]) { const char *prog = xbasename(argv[0]); - int i, cmd = 0, checking_flag = 0, foreground = -1; + int i, cmd = 0, cmdret = -1, checking_flag = 0, foreground = -1; + pid_t oldpid = -1; printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); @@ -2053,7 +2055,7 @@ int main(int argc, char *argv[]) run_as_user = xstrdup(RUN_AS_USER); - while ((i = getopt(argc, argv, "+DFBhic:f:pu:VK46")) != -1) { + while ((i = getopt(argc, argv, "+DFBhic:P:f:pu:VK46")) != -1) { switch (i) { case 'c': if (!strncmp(optarg, "fsd", strlen(optarg))) @@ -2067,6 +2069,12 @@ int main(int argc, char *argv[]) if (cmd == 0) help(argv[0]); break; + + case 'P': + if ((oldpid = parsepid(optarg)) < 0) + help(argv[0]); + break; + case 'D': nut_debug_level++; break; @@ -2121,17 +2129,41 @@ int main(int argc, char *argv[]) } if (cmd) { - sendsignal(prog, cmd); - exit(EXIT_SUCCESS); + if (oldpid < 0) { + cmdret = sendsignal(prog, cmd); + } else { + cmdret = sendsignalpid(oldpid, cmd); + } + /* exit(EXIT_SUCCESS); */ + exit((cmdret == 0)?EXIT_SUCCESS:EXIT_FAILURE); } /* otherwise, we are being asked to start. * so check if a previous instance is running by sending signal '0' * (Ie 'kill 0') */ - if (sendsignal(prog, 0) == 0) { + if (oldpid < 0) { + cmdret = sendsignal(prog, 0); + } else { + cmdret = sendsignalpid(oldpid, 0); + } + switch (cmdret) { + case 0: printf("Fatal error: A previous upsmon instance is already running!\n"); printf("Either stop the previous instance first, or use the 'reload' command.\n"); exit(EXIT_FAILURE); + + case -3: + case -2: + upslogx(LOG_WARNING, "Could not %s PID file " + "to see if previous upsmon instance is " + "already running!\n", + (cmdret == -3 ? "find" : "parse")); + break; + + case -1: + default: + /* Just failed to send signal, no competitor running */ + break; } argc -= optind; diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index 5006d40e49..2f226d5f42 100644 --- a/docs/man/upsmon.txt +++ b/docs/man/upsmon.txt @@ -11,7 +11,7 @@ SYNOPSIS *upsmon* -h -*upsmon* -c 'command' +*upsmon* -c 'command' [-P 'pid'] *upsmon* [-D] [-F | -B] [-K] [-p] [-u 'user'] @@ -44,6 +44,12 @@ commands are: *reload*;; reread linkman:upsmon.conf[5] configuration file. See "reloading nuances" below if this doesn't work. +*-P* 'pid':: +Send the command signal above using specified PID number, rather than +consulting the PID file. This can help define service units which +start main `upsmon` as a foreground process so it does not have to +rely on a PID file. + *-D*:: Raise the debugging level. upsmon will run in the foreground by default, and will print information on stdout about the monitoring process. From 1afbd2b010e45b5e7be99e6eca5693dfceebcb56 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 13:45:10 +0000 Subject: [PATCH 130/700] server/conf.c: allow upsd to reload config and apply its debug_min setting --- server/conf.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/server/conf.c b/server/conf.c index de973bdc1c..8bb694e1a8 100644 --- a/server/conf.c +++ b/server/conf.c @@ -333,6 +333,13 @@ void load_upsdconf(int reloading) return; } + if (reloading) { + /* if upsd.conf added or changed + * (or commented away) the debug_min + * setting, detect that */ + nut_debug_level_global = -1; + } + while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", @@ -359,6 +366,15 @@ void load_upsdconf(int reloading) } + if (reloading) { + if (nut_debug_level_global > -1) { + upslogx(LOG_INFO, + "Applying debug_min=%d from upsd.conf", + nut_debug_level_global); + nut_debug_level = nut_debug_level_global; + } + } + pconf_finish(&ctx); } From 1bdc4fbb685a07fc0033d3abed6ebb856369e739 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 13:46:27 +0000 Subject: [PATCH 131/700] scripts/systemd/nut-server.service.in: reload upsd without PID file [for #1299] --- scripts/systemd/nut-server.service.in | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/systemd/nut-server.service.in b/scripts/systemd/nut-server.service.in index 15d54f7585..713f5c4273 100644 --- a/scripts/systemd/nut-server.service.in +++ b/scripts/systemd/nut-server.service.in @@ -20,8 +20,10 @@ PartOf=nut.target [Service] EnvironmentFile=-@CONFPATH@/nut.conf SyslogIdentifier=%N -ExecStart=@SBINDIR@/upsd -FF -ExecReload=@SBINDIR@/upsd -c reload +# Note: foreground mode by default skips writing a PID file (and +# needs Type=simple); can use "-FF" here to create one anyway: +ExecStart=@SBINDIR@/upsd -F +ExecReload=@SBINDIR@/upsd -c reload -P $MAINPID [Install] WantedBy=nut.target From db56595493f8d844d76bb39c5daf4854b3d2f3ed Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 15:19:44 +0100 Subject: [PATCH 132/700] common/common.c: writepid(): debug-trace creation of a PID file --- common/common.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/common.c b/common/common.c index a1ed3a2c9c..bb00aafdc9 100644 --- a/common/common.c +++ b/common/common.c @@ -293,7 +293,9 @@ void writepid(const char *name) pidf = fopen(fn, "w"); if (pidf) { - fprintf(pidf, "%d\n", (int) getpid()); + intmax_t pid = (intmax_t)getpid(); + upsdebugx(1, "Saving PID %jd into %s", pid, fn); + fprintf(pidf, "%jd\n", pid); fclose(pidf); } else { upslog_with_errno(LOG_NOTICE, "writepid: fopen %s", fn); From fcb8b5ef9cf00705782dcdcabd1500ac0efb46d1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 15:50:52 +0100 Subject: [PATCH 133/700] clients/upsmon.c: allow upsmon to reload config and apply its debug_min setting --- clients/upsmon.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/clients/upsmon.c b/clients/upsmon.c index 430258cd77..174099c24e 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -1977,9 +1977,21 @@ static void reload_conf(void) /* reset paranoia checker */ totalpv = 0; + /* if upsmon.conf added or changed + * (or commented away) the debug_min + * setting, detect that */ + nut_debug_level_global = -1; + /* reread upsmon.conf */ loadconfig(); + if (nut_debug_level_global > -1) { + upslogx(LOG_INFO, + "Applying debug_min=%d from upsmon.conf", + nut_debug_level_global); + nut_debug_level = nut_debug_level_global; + } + /* go through the utype_t struct again */ tmp = firstups; From fccbc771fe134b03ac5cefe8376ce62fcdd0bd6f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 15:12:43 +0000 Subject: [PATCH 134/700] clients/upsmon.c: whitespace fix --- clients/upsmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index 174099c24e..66a3eaf367 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -1279,7 +1279,7 @@ static int parse_conf_arg(size_t numargs, char **arg) } /* RUN_AS_USER */ - if (!strcmp(arg[0], "RUN_AS_USER")) { + if (!strcmp(arg[0], "RUN_AS_USER")) { free(run_as_user); run_as_user = xstrdup(arg[1]); return 1; From 7115eb1e846ac3297891e5e9dfdd4ccdd75d3c97 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 15:13:11 +0000 Subject: [PATCH 135/700] clients/upsmon.c: restructure config reload for debug_min to be more similar to that in upsd.c --- clients/upsmon.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index 66a3eaf367..ab0245ffe1 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -1393,6 +1393,13 @@ static void loadconfig(void) fatalx(EXIT_FAILURE, "%s", ctx.errmsg); } + if (reload_flag == 1) { + /* if upsmon.conf added or changed + * (or commented away) the debug_min + * setting, detect that */ + nut_debug_level_global = -1; + } + while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", @@ -1419,6 +1426,15 @@ static void loadconfig(void) } } + if (reload_flag == 1) { + if (nut_debug_level_global > -1) { + upslogx(LOG_INFO, + "Applying debug_min=%d from upsmon.conf", + nut_debug_level_global); + nut_debug_level = nut_debug_level_global; + } + } + pconf_finish(&ctx); } @@ -1977,21 +1993,9 @@ static void reload_conf(void) /* reset paranoia checker */ totalpv = 0; - /* if upsmon.conf added or changed - * (or commented away) the debug_min - * setting, detect that */ - nut_debug_level_global = -1; - /* reread upsmon.conf */ loadconfig(); - if (nut_debug_level_global > -1) { - upslogx(LOG_INFO, - "Applying debug_min=%d from upsmon.conf", - nut_debug_level_global); - nut_debug_level = nut_debug_level_global; - } - /* go through the utype_t struct again */ tmp = firstups; From aae4fe6e39f4eb09cd81084c76253f9a7c09a2cc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 16:43:27 +0100 Subject: [PATCH 136/700] upsd and upsmon configuration sample files and man pages: add note about run-time config reload with DEBUG_MIN setting in sight --- conf/upsd.conf.sample | 11 +++++++++++ conf/upsmon.conf.sample.in | 11 +++++++++++ docs/man/upsd.conf.txt | 11 +++++++++++ docs/man/upsmon.conf.txt | 11 +++++++++++ 4 files changed, 44 insertions(+) diff --git a/conf/upsd.conf.sample b/conf/upsd.conf.sample index 9d6a9cd1f4..fb420e3a78 100644 --- a/conf/upsd.conf.sample +++ b/conf/upsd.conf.sample @@ -155,3 +155,14 @@ # running mode directly, and without need to edit init-scripts or service # unit definitions. Note that command-line option `-D` can only increase # this verbosity level. +# +# NOTE: if the running daemon receives a `reload` command, presence of the +# `DEBUG_MIN NUMBER` value in the configuration file can be used to tune +# debugging verbosity in the running service daemon (it is recommended to +# comment it away or set the minimum to explicit zero when done, to avoid +# huge journals and I/O system abuse). Keep in mind that for this run-time +# tuning, the `DEBUG_MIN` value *present* in *reloaded* configuration files +# is applied instantly and overrides any previously set value, from file +# or CLI options, regardless of older logging level being higher or lower +# than the newly found number; a missing (or commented away) value however +# does not change the previously active logging verbosity. diff --git a/conf/upsmon.conf.sample.in b/conf/upsmon.conf.sample.in index dedbb92afb..aeec849eda 100644 --- a/conf/upsmon.conf.sample.in +++ b/conf/upsmon.conf.sample.in @@ -440,3 +440,14 @@ FINALDELAY 5 # running mode directly, and without need to edit init-scripts or service # unit definitions. Note that command-line option `-D` can only increase # this verbosity level. +# +# NOTE: if the running daemon receives a `reload` command, presence of the +# `DEBUG_MIN NUMBER` value in the configuration file can be used to tune +# debugging verbosity in the running service daemon (it is recommended to +# comment it away or set the minimum to explicit zero when done, to avoid +# huge journals and I/O system abuse). Keep in mind that for this run-time +# tuning, the `DEBUG_MIN` value *present* in *reloaded* configuration files +# is applied instantly and overrides any previously set value, from file +# or CLI options, regardless of older logging level being higher or lower +# than the newly found number; a missing (or commented away) value however +# does not change the previously active logging verbosity. diff --git a/docs/man/upsd.conf.txt b/docs/man/upsd.conf.txt index 40f9786a5d..67e41dbf46 100644 --- a/docs/man/upsd.conf.txt +++ b/docs/man/upsd.conf.txt @@ -135,6 +135,17 @@ Optionally specify a minimum debug level for `upsd` data daemon, e.g. for troubleshooting a deployment, without impacting foreground or background running mode directly. Command-line option `-D` can only increase this verbosity level. ++ +NOTE: if the running daemon receives a `reload` command, presence of the +`DEBUG_MIN NUMBER` value in the configuration file can be used to tune +debugging verbosity in the running service daemon (it is recommended to +comment it away or set the minimum to explicit zero when done, to avoid +huge journals and I/O system abuse). Keep in mind that for this run-time +tuning, the `DEBUG_MIN` value *present* in *reloaded* configuration files +is applied instantly and overrides any previously set value, from file +or CLI options, regardless of older logging level being higher or lower +than the newly found number; a missing (or commented away) value however +does not change the previously active logging verbosity. SEE ALSO -------- diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index ac92401a60..8aa44443cc 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -399,6 +399,17 @@ Optionally specify a minimum debug level for `upsmon` daemon, e.g. for troubleshooting a deployment, without impacting foreground or background running mode directly. Command-line option `-D` can only increase this verbosity level. ++ +NOTE: if the running daemon receives a `reload` command, presence of the +`DEBUG_MIN NUMBER` value in the configuration file can be used to tune +debugging verbosity in the running service daemon (it is recommended to +comment it away or set the minimum to explicit zero when done, to avoid +huge journals and I/O system abuse). Keep in mind that for this run-time +tuning, the `DEBUG_MIN` value *present* in *reloaded* configuration files +is applied instantly and overrides any previously set value, from file +or CLI options, regardless of older logging level being higher or lower +than the newly found number; a missing (or commented away) value however +does not change the previously active logging verbosity. SEE ALSO -------- From bb8757fb83bdf0f27308a37fcb3d474c3796755d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 19:11:54 +0100 Subject: [PATCH 137/700] drivers/libhid.c: fix misfire of fightwarn commit 58e5b49 (string_to_path(): range-check...) Closes: #1286 --- drivers/libhid.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/libhid.c b/drivers/libhid.c index 39fc22d982..58c9b2e71a 100644 --- a/drivers/libhid.c +++ b/drivers/libhid.c @@ -904,10 +904,18 @@ static int string_to_path(const char *string, HIDPath_t *path, usage_tables_t *u for (token = strtok_r(buf, ".", &last); token != NULL; token = strtok_r(NULL, ".", &last)) { /* lookup tables first (to override defaults) */ - if ((usage = hid_lookup_usage(token, utab)) >= 0) + /* Note/FIXME?: we happen to process "usage" as a "signed long" + * while the HIDNode_t behind it is (currently) uint32_t. + * The method below returns `-1` for entries not found; however + * half of our permissible range may seem negative and be valid. + */ + if ((usage = hid_lookup_usage(token, utab)) != -1) { path->Node[i++] = (HIDNode_t)usage; continue; + } else { + /* hid_lookup_usage() logs for itself: ... -> not found in lookup table */ + /*upsdebugx(5, "string_to_path: badvalue (usage): %ld negative", usage);*/ } /* translate unnamed path components such as "ff860024" */ @@ -916,6 +924,9 @@ static int string_to_path(const char *string, HIDPath_t *path, usage_tables_t *u long l = strtol(token, NULL, 16); /* Note: currently per hidtypes.h, HIDNode_t == uint32_t */ if (l < 0 || (uintmax_t)l > (uintmax_t)UINT32_MAX) { + upsdebugx(5, "string_to_path: badvalue (pathcomp): " + "%ld negative or %ju too large", + l, (uintmax_t)l); goto badvalue; } path->Node[i++] = (HIDNode_t)l; @@ -927,6 +938,9 @@ static int string_to_path(const char *string, HIDPath_t *path, usage_tables_t *u { int l = atoi(token + 1); /* +1: skip the bracket */ if (l < 0 || (uintmax_t)l > (uintmax_t)UINT32_MAX) { + upsdebugx(5, "string_to_path: badvalue(indexed): " + "%d negative or %ju too large", + l, (uintmax_t)l); goto badvalue; } path->Node[i++] = 0x00ff0000 + (HIDNode_t)l; From 9d166fd2172f5a39b327726e07aa0d4bc0a1434f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Feb 2022 20:45:44 +0100 Subject: [PATCH 138/700] drivers/libhid.c: string_to_path(): report hid_lookup_usage() miss --- drivers/libhid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/libhid.c b/drivers/libhid.c index 58c9b2e71a..624050dfb5 100644 --- a/drivers/libhid.c +++ b/drivers/libhid.c @@ -915,7 +915,8 @@ static int string_to_path(const char *string, HIDPath_t *path, usage_tables_t *u continue; } else { /* hid_lookup_usage() logs for itself: ... -> not found in lookup table */ - /*upsdebugx(5, "string_to_path: badvalue (usage): %ld negative", usage);*/ + upsdebugx(5, "string_to_path: hid_lookup_usage failed, " + "checking if token %s is a raw value", token); } /* translate unnamed path components such as "ff860024" */ From 84304a9d1c28a7cec2b676480657e857a910784c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 17 Feb 2022 11:47:53 +0100 Subject: [PATCH 139/700] scripts/systemd/nut-driver@.service.in: make sure drivers always try to start and connect --- scripts/systemd/nut-driver@.service.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/systemd/nut-driver@.service.in b/scripts/systemd/nut-driver@.service.in index 86131bdb38..0a6b3d996a 100644 --- a/scripts/systemd/nut-driver@.service.in +++ b/scripts/systemd/nut-driver@.service.in @@ -29,6 +29,14 @@ EnvironmentFile=-@CONFPATH@/nut.conf SyslogIdentifier=%N ExecStart=/bin/sh -c 'NUTDEV="`@NUT_LIBEXECDIR@/nut-driver-enumerator.sh --get-device-for-service %i`" && [ -n "$NUTDEV" ] || { echo "FATAL: Could not find a NUT device section for service unit %i" >&2 ; exit 1 ; } ; @SBINDIR@/upsdrvctl start "$NUTDEV"' ExecStop=/bin/sh -c 'NUTDEV="`@NUT_LIBEXECDIR@/nut-driver-enumerator.sh --get-device-for-service %i`" && [ -n "$NUTDEV" ] || { echo "FATAL: Could not find a NUT device section for service unit %i" >&2 ; exit 1 ; } ; @SBINDIR@/upsdrvctl stop "$NUTDEV"' +Restart=always +# Protract the "hold-off" interval, so if the device connection is +# lost, the driver does not reapidly restart and fail too many times, +# and then systemd would keep the unit failed without further retries. +# Notably, this helps start "dummy-ups" drivers retranslating local +# devices (so getting a chicken-and-egg problem for driver-upsd-driver +# orderly series of initializations). More details in NUT issue #779. +RestartSec=15s Type=forking # Note: If you customize the "maxstartdelay" in ups.conf or in your # NUT compilation defaults, so it exceeds the default systemd unit From 5131ceec23b170492af9d0e14de6d54ffecfdb4f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 17 Feb 2022 11:53:41 +0100 Subject: [PATCH 140/700] scripts/systemd/nut-driver@.service.in: comment about "Before=nut-driver.target" constraint --- scripts/systemd/nut-driver@.service.in | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scripts/systemd/nut-driver@.service.in b/scripts/systemd/nut-driver@.service.in index 0a6b3d996a..ba957f30e9 100644 --- a/scripts/systemd/nut-driver@.service.in +++ b/scripts/systemd/nut-driver@.service.in @@ -1,7 +1,20 @@ [Unit] Description=Network UPS Tools - device driver for %I After=local-fs.target + +# Note: If the "Before" line below is uncommented, the target unit +# would only become initialized after the driver units are all in +# a final state (active, failed, ...) and would allow nut-server +# (upsd) to start up and represent those devices on the network. +# With this constraint commented away, the nut-server should start +# earlier, but may initially report some devices as Not connected +# (they should appear when drivers complete their initialization - +# e.g. snmp walks of large MIBs can take a while): +#Before=nut-driver.target + +# Propagate stopping of the target: PartOf=nut-driver.target + # Note: The choice of "network.target" allows to schedule this unit # roughly when the network stack of this OS is ready (e.g. that the # subsequent `upsd` will have a `0.0.0.0` or a `localhost` to bind From e0cfde9982861d607658092455f8967a35cbeac3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 17 Feb 2022 11:58:57 +0100 Subject: [PATCH 141/700] scripts/systemd/nut-driver@.service.in: make sure drivers always try to start and connect - how ever many attempts that takes --- scripts/systemd/nut-driver@.service.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/systemd/nut-driver@.service.in b/scripts/systemd/nut-driver@.service.in index ba957f30e9..3f1d028d61 100644 --- a/scripts/systemd/nut-driver@.service.in +++ b/scripts/systemd/nut-driver@.service.in @@ -42,6 +42,8 @@ EnvironmentFile=-@CONFPATH@/nut.conf SyslogIdentifier=%N ExecStart=/bin/sh -c 'NUTDEV="`@NUT_LIBEXECDIR@/nut-driver-enumerator.sh --get-device-for-service %i`" && [ -n "$NUTDEV" ] || { echo "FATAL: Could not find a NUT device section for service unit %i" >&2 ; exit 1 ; } ; @SBINDIR@/upsdrvctl start "$NUTDEV"' ExecStop=/bin/sh -c 'NUTDEV="`@NUT_LIBEXECDIR@/nut-driver-enumerator.sh --get-device-for-service %i`" && [ -n "$NUTDEV" ] || { echo "FATAL: Could not find a NUT device section for service unit %i" >&2 ; exit 1 ; } ; @SBINDIR@/upsdrvctl stop "$NUTDEV"' +# Restart really always, do not stop trying: +StartLimitInterval=0 Restart=always # Protract the "hold-off" interval, so if the device connection is # lost, the driver does not reapidly restart and fail too many times, From 3261524a68bcfba7ce255378510c11840e951b23 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 17 Feb 2022 13:42:16 +0100 Subject: [PATCH 142/700] clients/upsrw.c, upscmd.c: fix sanity-check of tracking_id length (forgot the nul-byte) --- clients/upscmd.c | 5 +++-- clients/upsrw.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/clients/upscmd.c b/clients/upscmd.c index 7134671103..c18d00ced4 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -197,9 +197,10 @@ static void do_cmd(char **argv, const int argc) #pragma GCC diagnostic ignored "-Wformat-truncation" #endif /* From the check above, we know that we have exactly UUID4_LEN chars - * (aka sizeof(tracking_id)) in the buf after "OK TRACKING " prefix. + * (aka sizeof(tracking_id)) in the buf after "OK TRACKING " prefix, + * plus the null-byte. */ - assert (UUID4_LEN == snprintf(tracking_id, sizeof(tracking_id), "%s", buf + strlen("OK TRACKING "))); + assert (UUID4_LEN == 1 + snprintf(tracking_id, sizeof(tracking_id), "%s", buf + strlen("OK TRACKING "))); #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION #pragma GCC diagnostic pop #endif diff --git a/clients/upsrw.c b/clients/upsrw.c index d2fe1d9efb..fb49829e11 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -122,9 +122,10 @@ static void do_set(const char *varname, const char *newval) #pragma GCC diagnostic ignored "-Wformat-truncation" #endif /* From the check above, we know that we have exactly UUID4_LEN chars - * (aka sizeof(tracking_id)) in the buf after "OK TRACKING " prefix. + * (aka sizeof(tracking_id)) in the buf after "OK TRACKING " prefix, + * plus the null-byte. */ - assert (UUID4_LEN == snprintf(tracking_id, sizeof(tracking_id), "%s", buf + strlen("OK TRACKING "))); + assert (UUID4_LEN == 1 + snprintf(tracking_id, sizeof(tracking_id), "%s", buf + strlen("OK TRACKING "))); #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION #pragma GCC diagnostic pop #endif From 059977eb2100950221a5bbdfa7ea5a4362eab751 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 17 Feb 2022 23:09:59 +0200 Subject: [PATCH 143/700] man page added --- docs/man/Makefile.am | 9 ++- docs/man/adelsystem_cbi.txt | 110 ++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 docs/man/adelsystem_cbi.txt diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 3c0fb6aecc..ebd0208774 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -617,11 +617,13 @@ HTML_MACOSX_MANS = macosx-ups.html SRC_MODBUS_PAGES = phoenixcontact_modbus.txt \ generic_modbus.txt \ - huawei-ups2000.txt + huawei-ups2000.txt \ + adelsystem_cbi.txt if WITH_MANS MAN_MODBUS_PAGES = phoenixcontact_modbus.8 \ generic_modbus.8 \ - huawei-ups2000.8 + huawei-ups2000.8 \ + adelsystem_cbi.8 endif if WITH_MODBUS @@ -630,7 +632,8 @@ endif HTML_MODBUS_MANS = phoenixcontact_modbus.html \ generic_modbus.html \ - huawei-ups2000.html + huawei-ups2000.html \ + adelsystem_cbi.html SRC_LINUX_I2C_PAGES = asem.txt pijuice.txt if WITH_MANS diff --git a/docs/man/adelsystem_cbi.txt b/docs/man/adelsystem_cbi.txt new file mode 100644 index 0000000000..414f6ef443 --- /dev/null +++ b/docs/man/adelsystem_cbi.txt @@ -0,0 +1,110 @@ +ADELSYSTEM_CBI(8) +================= + +NAME +---- + +adelsystem_cbi - Driver for the ADELSYSTEM CB/CBI DC-UPS + +SYNOPSIS +-------- + +*adelsystem_cbi* -h + +*adelsystem_cbi* -a 'DEVICE_NAME' ['OPTIONS'] + +NOTE: This man page only documents the specific features of the *adelsystem_cbi* +driver. For information about the core driver, see linkman:nutupsdrv[8]. + +SUPPORTED HARDWARE +------------------ + +This is the driver for the adelsystem cb/cbi dc-ups devices. + +The driver has been tested against CBI2801224A, all in one 12/24Vdc DC-UPS. + +More information about this UPS can be found here: :: +https://www.adelsystem.com/en/products/dc-ups-/ + + +EXTRA ARGUMENTS +--------------- + +This driver supports the following optional settings in the +linkman:ups.conf[5] file: + +Serial: +~~~~~~ + +*ser_baud_rate*='value':: +A integer specifying the serial port baud rate (default 9600). + +*ser_data_bit*='value':: +A integer specifying the serial port data bit (default 8). + +*ser_parity*='value':: +A character specifying the serial port parity (default N). + +*ser_stop_bit*='value':: +An integer specifying the serial port stop bit (default 1). + +Modbus: +~~~~~~ + +*dev_slave_id*='value':: +An integer specifying the device modbus slave ID (default 1). + + +---- + +CONFIGURATION +------------- + +Here is an example of adelsystem_cbi driver configuration in *ups.conf* file: +---- +[adelsystem_cbi] + driver = adelsystem_cbi + port = /dev/ttyUSB0 + desc = "adelsystem cb/cbi ups driver" + # serial settings + ser_baud_rate = 9600 + ser_parity = N + ser_data_bit = 8 + ser_stop_bit = 1 + # modbus slave id + dev_slave_id = 5 +---- + +INSTANT COMMANDS +---------------- + +This driver support the following instant commands: + +load.off:: +executes "instant poweroff" + +INSTALLATION +------------ + +This driver is not built by default. You can build it by installing +libmodbus and running `configure --with-modbus=yes`. + +You also need to give proper permissions on the local serial device +file (/dev/ttyUSB0 for example) to allow the run-time NUT driver user +account to access it. + +AUTHOR +------ +Dimitris Economou + +SEE ALSO +-------- +The core driver: +~~~~~~~~~~~~~~~~ +linkman:nutupsdrv[8], linkman:ups.conf[5] + +Internet resources: +~~~~~~~~~~~~~~~~~~~ +The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +libmodbus home page: http://libmodbus.org From 5e4ec80d1c5012238ed4454b590d902bf9200370 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 17 Feb 2022 23:10:45 +0200 Subject: [PATCH 144/700] clean up, fix typos --- drivers/adelsystem_cbi.c | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index 1f43f44c13..01bc935266 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -43,7 +43,7 @@ static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ static char ser_parity = PARITY; /* serial port parity */ static int ser_data_bit = DATA_BIT; /* serial port data bit */ static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ -static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static int dev_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ @@ -115,10 +115,10 @@ void upsdrv_initups(void) } /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); + rval = modbus_set_slave(mbctx, dev_slave_id); if (rval < 0) { modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", dev_slave_id); } /* connect to modbus device */ @@ -501,13 +501,11 @@ void upsdrv_help(void) /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { - addvar(VAR_VALUE, "device_mfr", "device manufacturer"); - addvar(VAR_VALUE, "device_model", "device model"); addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); addvar(VAR_VALUE, "ser_parity", "serial port parity"); addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); - addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "dev_slave_id", "device modbus slave ID"); addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); @@ -1138,17 +1136,6 @@ int get_dev_state(devreg_t regindx, devstate_t **dvstat) /* get driver configuration parameters */ void get_config_vars(void) { - /* check if device manufacturer is set and get the value */ - if (testvar("device_mfr")) { - device_mfr = getval("device_mfr"); - } - upsdebugx(2, "device_mfr %s", device_mfr); - - /* check if device model is set and get the value */ - if (testvar("device_model")) { - device_model = getval("device_model"); - } - upsdebugx(2, "device_model %s", device_model); /* check if serial baud rate is set and get the value */ if (testvar("ser_baud_rate")) { @@ -1182,10 +1169,10 @@ void get_config_vars(void) upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); /* check if device ID is set and get the value */ - if (testvar("rio_slave_id")) { - rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + if (testvar("dev_slave_id")) { + dev_slave_id = (int)strtol(getval("dev_slave_id"), NULL, 10); } - upsdebugx(2, "rio_slave_id %d", rio_slave_id); + upsdebugx(2, "dev_slave_id %d", dev_slave_id); /* check if response time out (s) is set and get the value */ if (testvar("mod_resp_to_s")) { @@ -1264,10 +1251,10 @@ void modbus_reconnect(void) } /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); + rval = modbus_set_slave(mbctx, dev_slave_id); if (rval < 0) { modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", dev_slave_id); } /* connect to modbus device */ From 2f701d07837c4051604ad037af3f13f7d26ac2de Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 17 Feb 2022 23:10:50 +0200 Subject: [PATCH 145/700] clean up, fix typos --- drivers/adelsystem_cbi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/adelsystem_cbi.h b/drivers/adelsystem_cbi.h index 250b30469b..a3b63633d5 100644 --- a/drivers/adelsystem_cbi.h +++ b/drivers/adelsystem_cbi.h @@ -32,7 +32,7 @@ /* UPS device details */ #define DEVICE_MFR "ADELSYSTEM" #define DEVICE_TYPE "DC-UPS" -#define DEVICE_MODEL "CB/CBI" +#define DEVICE_MODEL "CBI2801224A" /* serial access parameters */ #define BAUD_RATE 9600 From 8f961ddfcfa35657209519e228964b253ffd8660 Mon Sep 17 00:00:00 2001 From: Dimitris Economou Date: Thu, 17 Feb 2022 23:28:34 +0200 Subject: [PATCH 146/700] handle covered-switch-default and switch-enum warnings --- drivers/adelsystem_cbi.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index 01bc935266..6b9d5e64e7 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -576,6 +576,14 @@ void reginit(void) regs[i].saddr = rnum - 1; regs[i].xaddr = 0x40000 + rnum - 1; break; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ default: upslogx(LOG_ERR, "Invalid register type %d for register %d", @@ -586,7 +594,10 @@ void reginit(void) "Invalid register type %d for register %d", regs[i].type, regs[i].num - ); + ); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif } upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", @@ -1106,6 +1117,9 @@ int get_dev_state(devreg_t regindx, devstate_t **dvstat) } state->alrm = obta; break; + case BINH: + case FSD: + break; #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wcovered-switch-default" @@ -1129,7 +1143,6 @@ int get_dev_state(devreg_t regindx, devstate_t **dvstat) # pragma GCC diagnostic pop #endif } - return rval; } From 03727d883bfe12a778310b8dcdaaa4972223cd29 Mon Sep 17 00:00:00 2001 From: Viktor Kuzmin Date: Fri, 18 Feb 2022 11:09:26 +0300 Subject: [PATCH 147/700] Fix individual drivers configuration --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 08fb080c83..63f0b96261 100644 --- a/configure.ac +++ b/configure.ac @@ -638,9 +638,9 @@ AC_ARG_WITH(drivers, dnl Break once, maybe a driver hits several categories ]) done - AS_IF([test -z "${DRV_HITS}"], - [AC_MSG_ERROR([Requested driver '$DRV' is not defined in drivers/Makefile.am (or configure.ac has a bug/lag detecting driver lists)])]) done + AS_IF([test -z "${DRV_HITS}"], + [AC_MSG_ERROR([Requested driver '$DRV' is not defined in drivers/Makefile.am (or configure.ac has a bug/lag detecting driver lists)])]) done ]) From 9ace9bd3bc1ea4c644addc24a741b9524db360b9 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Fri, 16 Sep 2016 16:00:17 +0200 Subject: [PATCH 148/700] snmp-ups: add sysContact and sysLocation support The generic MIB-2 provides system contact and location information that are now publish respectively as device.contact and device.location --- drivers/snmp-ups.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index a3470188f0..98077ac758 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -150,7 +150,7 @@ static const char *mibname; static const char *mibvers; #define DRIVER_NAME "Generic SNMP UPS driver" -#define DRIVER_VERSION "1.18" +#define DRIVER_VERSION "1.19" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -675,6 +675,19 @@ void upsdrv_initups(void) dstate_addcmd("shutdown.stayoff"); } + /* Publish sysContact and sysLocation for all subdrivers */ + /* sysContact.0 */ + if (nut_snmp_get_str(".1.3.6.1.2.1.1.4.0", model, sizeof(model), NULL) == TRUE) + dstate_setinfo("device.contact", "%s", model); + else + upsdebugx(2, "Can't get and publish sysContact for device.contact"); + + /* sysLocation.0 */ + if (nut_snmp_get_str(".1.3.6.1.2.1.1.6.0", model, sizeof(model), NULL) == TRUE) + dstate_setinfo("device.location", "%s", model); + else + upsdebugx(2, "Can't get and publish sysLocation for device.location"); + /* set shutdown and autostart delay */ set_delays(); } From 7cfb760f7e65c201fb378d076388dab3ef1facfd Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Fri, 16 Sep 2016 16:02:44 +0200 Subject: [PATCH 149/700] snmp-ups: update todo list The addition of sysContact and sysLocation support is now addressed centrally --- drivers/snmp-ups.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index eb7c28d13c..4c870138d3 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -30,7 +30,6 @@ */ /* TODO list: -- add syscontact/location (to all mib.h or centralized?) - complete shutdown - add enum values to OIDs. - optimize network flow by: From 90ca7a3c77c9609927141f5f1dd56325a4c422eb Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 17 Feb 2022 12:52:40 +0100 Subject: [PATCH 150/700] drivers/*-mib.c: define standard-MIB entries for device.description, contact, location (RW STRING) --- drivers/apc-ats-mib.c | 7 ++++++- drivers/apc-mib.c | 7 ++++++- drivers/baytech-mib.c | 8 +++++++- drivers/bestpower-mib.c | 8 +++++++- drivers/compaq-mib.c | 8 +++++++- drivers/cyberpower-mib.c | 8 +++++++- drivers/delta_ups-mib.c | 7 ++++++- drivers/eaton-ats16-nm2-mib.c | 7 ++++++- drivers/eaton-ats16-nmc-mib.c | 7 ++++++- drivers/eaton-pdu-genesis2-mib.c | 8 +++++++- drivers/eaton-pdu-marlin-mib.c | 7 ++++++- drivers/eaton-pdu-pulizzi-mib.c | 8 +++++++- drivers/eaton-pdu-revelation-mib.c | 8 +++++++- drivers/emerson-avocent-pdu-mib.c | 6 ++++++ drivers/hpe-pdu-mib.c | 7 ++++++- drivers/huawei-mib.c | 7 ++++++- drivers/ietf-mib.c | 8 +++++++- drivers/mge-mib.c | 7 ++++++- drivers/netvision-mib.c | 8 +++++++- drivers/powerware-mib.c | 8 +++++++- drivers/raritan-pdu-mib.c | 8 +++++++- drivers/raritan-px2-mib.c | 7 ++++++- drivers/xppc-mib.c | 8 +++++++- 23 files changed, 150 insertions(+), 22 deletions(-) diff --git a/drivers/apc-ats-mib.c b/drivers/apc-ats-mib.c index 3f567590b7..56a6779c8e 100644 --- a/drivers/apc-ats-mib.c +++ b/drivers/apc-ats-mib.c @@ -24,7 +24,7 @@ #include "apc-ats-mib.h" -#define APC_ATS_MIB_VERSION "0.5" +#define APC_ATS_MIB_VERSION "0.6" #define APC_ATS_SYSOID ".1.3.6.1.4.1.318.1.3.11" #define APC_ATS_OID_MODEL_NAME ".1.3.6.1.4.1.318.1.1.8.1.5.0" @@ -59,6 +59,11 @@ static info_lkp_t apc_ats_outletgroups_status_info[] = { /* APC ATS Snmp2NUT lookup table */ static snmp_info_t apc_ats_mib[] = { + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device collection */ { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /* ats2IdentManufacturer.0 = STRING: EATON */ diff --git a/drivers/apc-mib.c b/drivers/apc-mib.c index e0294a2fcf..4901d4da4f 100644 --- a/drivers/apc-mib.c +++ b/drivers/apc-mib.c @@ -26,7 +26,7 @@ #include "apc-mib.h" -#define APCC_MIB_VERSION "1.5" +#define APCC_MIB_VERSION "1.6" #define APC_UPS_DEVICE_MODEL ".1.3.6.1.4.1.318.1.1.1.1.1.1.0" /* FIXME: Find a better oid_auto_check vs sysOID for this one? */ @@ -151,6 +151,11 @@ static info_lkp_t apcc_transfer_reasons[] = { static snmp_info_t apcc_mib[] = { + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* info elements. */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "APC", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/baytech-mib.c b/drivers/baytech-mib.c index 6dfdca8050..43098cdb6e 100644 --- a/drivers/baytech-mib.c +++ b/drivers/baytech-mib.c @@ -24,7 +24,7 @@ #include "baytech-mib.h" /* NOTE: last badly versioned release was "4032" but should be "X.Y[Z]"! */ -#define BAYTECH_MIB_VERSION "0.4033" +#define BAYTECH_MIB_VERSION "0.4034" /* Baytech MIB */ #define BAYTECH_OID_MIB ".1.3.6.1.4.1.4779" @@ -40,6 +40,12 @@ static info_lkp_t baytech_outlet_status_info[] = { /* Snmp2NUT lookup table for BayTech MIBs */ static snmp_info_t baytech_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device page */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "BayTech", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/bestpower-mib.c b/drivers/bestpower-mib.c index 8512dfeb79..51ba1a62c3 100644 --- a/drivers/bestpower-mib.c +++ b/drivers/bestpower-mib.c @@ -22,7 +22,7 @@ #include "bestpower-mib.h" -#define BESTPOWER_MIB_VERSION "0.3" +#define BESTPOWER_MIB_VERSION "0.4" #define BESTPOWER_OID_MODEL_NAME ".1.3.6.1.4.1.2947.1.1.2.0" /* @@ -42,6 +42,12 @@ static info_lkp_t bestpower_power_status[] = { /* Snmp2NUT lookup table for Best Power MIB */ static snmp_info_t bestpower_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device page */ { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ups", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/compaq-mib.c b/drivers/compaq-mib.c index e379320c27..114299aa70 100644 --- a/drivers/compaq-mib.c +++ b/drivers/compaq-mib.c @@ -30,7 +30,7 @@ #include "compaq-mib.h" -#define CPQPOWER_MIB_VERSION "1.65" +#define CPQPOWER_MIB_VERSION "1.66" #define DEFAULT_ONDELAY "30" #define DEFAULT_OFFDELAY "20" @@ -173,6 +173,12 @@ static info_lkp_t cpqpower_outlet_switchability_info[] = { /* Snmp2NUT lookup table */ static snmp_info_t cpqpower_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* UPS page */ /* info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_MFR_NAME, "HP/Compaq", SU_FLAG_STATIC, NULL }, diff --git a/drivers/cyberpower-mib.c b/drivers/cyberpower-mib.c index 1a186585e6..9caa8dbe42 100644 --- a/drivers/cyberpower-mib.c +++ b/drivers/cyberpower-mib.c @@ -24,7 +24,7 @@ #include "cyberpower-mib.h" -#define CYBERPOWER_MIB_VERSION "0.4" +#define CYBERPOWER_MIB_VERSION "0.5" #define CYBERPOWER_OID_MODEL_NAME ".1.3.6.1.4.1.3808.1.1.1.1.1.1.0" /* CPS-MIB::ups */ @@ -63,6 +63,12 @@ static info_lkp_t cyberpower_battrepl_status[] = { /* Snmp2NUT lookup table for CyberPower MIB */ static snmp_info_t cyberpower_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device page */ { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ups", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/delta_ups-mib.c b/drivers/delta_ups-mib.c index f1741c0e33..770b0d7e80 100644 --- a/drivers/delta_ups-mib.c +++ b/drivers/delta_ups-mib.c @@ -25,7 +25,7 @@ #include "delta_ups-mib.h" -#define DELTA_UPS_MIB_VERSION "0.4" +#define DELTA_UPS_MIB_VERSION "0.5" #define DELTA_UPS_SYSOID ".1.3.6.1.4.1.2254.2.4" @@ -87,6 +87,11 @@ static snmp_info_t delta_ups_mib[] = { * }; */ + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* dupsIdentManufacturer.0 = STRING: "Socomec" */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.1.0", NULL, SU_FLAG_OK, NULL }, /* dupsIdentModel.0 = STRING: "NETYS RT 1/1 UPS" */ diff --git a/drivers/eaton-ats16-nm2-mib.c b/drivers/eaton-ats16-nm2-mib.c index 0e0aa175a9..256fd4359e 100644 --- a/drivers/eaton-ats16-nm2-mib.c +++ b/drivers/eaton-ats16-nm2-mib.c @@ -25,7 +25,7 @@ #include "eaton-ats16-nm2-mib.h" -#define EATON_ATS16_NM2_MIB_VERSION "0.20" +#define EATON_ATS16_NM2_MIB_VERSION "0.21" #define EATON_ATS16_NM2_SYSOID ".1.3.6.1.4.1.534.10.2" /* newer Network-M2 */ #define EATON_ATS16_NM2_MODEL ".1.3.6.1.4.1.534.10.2.1.2.0" @@ -81,6 +81,11 @@ static info_lkp_t eaton_ats16_nm2_output_status_info[] = { /* EATON_ATS Snmp2NUT lookup table */ static snmp_info_t eaton_ats16_nm2_mib[] = { + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device collection */ { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /* ats2IdentManufacturer.0 = STRING: EATON */ diff --git a/drivers/eaton-ats16-nmc-mib.c b/drivers/eaton-ats16-nmc-mib.c index bc9c7a9f3a..3b75c0a5dd 100644 --- a/drivers/eaton-ats16-nmc-mib.c +++ b/drivers/eaton-ats16-nmc-mib.c @@ -25,7 +25,7 @@ #include "eaton-ats16-nmc-mib.h" -#define EATON_ATS16_NMC_MIB_VERSION "0.19" +#define EATON_ATS16_NMC_MIB_VERSION "0.20" #define EATON_ATS16_NMC_SYSOID ".1.3.6.1.4.1.705.1" /* legacy NMC */ #define EATON_ATS16_NMC_MODEL ".1.3.6.1.4.1.534.10.2.1.2.0" @@ -81,6 +81,11 @@ static info_lkp_t eaton_ats16_nmc_output_status_info[] = { /* EATON_ATS_NMC Snmp2NUT lookup table */ static snmp_info_t eaton_ats16_nmc_mib[] = { + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device collection */ { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /* ats2IdentManufacturer.0 = STRING: EATON */ diff --git a/drivers/eaton-pdu-genesis2-mib.c b/drivers/eaton-pdu-genesis2-mib.c index 24e881f1cf..22ff01e1fe 100644 --- a/drivers/eaton-pdu-genesis2-mib.c +++ b/drivers/eaton-pdu-genesis2-mib.c @@ -29,7 +29,7 @@ #include "eaton-pdu-genesis2-mib.h" -#define EATON_APHEL_GENESIS2_MIB_VERSION "0.51" +#define EATON_APHEL_GENESIS2_MIB_VERSION "0.52" /* APHEL-GENESIS-II-MIB (monitored ePDU) * ************************************* @@ -48,6 +48,12 @@ /* Snmp2NUT lookup table for GenesisII MIB */ static snmp_info_t eaton_aphel_genesisII_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device page */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index fe22ff803d..170517027b 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -34,7 +34,7 @@ /* Eaton PDU-MIB - Marlin MIB * ************************** */ -#define EATON_MARLIN_MIB_VERSION "0.58" +#define EATON_MARLIN_MIB_VERSION "0.59" #define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" #define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" @@ -232,6 +232,11 @@ static info_lkp_t marlin_outlet_group_phase_info[] = { /* Snmp2NUT lookup table for Eaton Marlin MIB */ static snmp_info_t eaton_marlin_mib[] = { + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device collection */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/eaton-pdu-pulizzi-mib.c b/drivers/eaton-pdu-pulizzi-mib.c index 52fa09a78c..65de73ef52 100644 --- a/drivers/eaton-pdu-pulizzi-mib.c +++ b/drivers/eaton-pdu-pulizzi-mib.c @@ -42,7 +42,7 @@ /* Pulizzi Switched ePDU */ -#define EATON_PULIZZI_SW_MIB_VERSION "0.4" +#define EATON_PULIZZI_SW_MIB_VERSION "0.5" #define PULIZZI_SW_OID_MIB ".1.3.6.1.4.1.20677.3.1.1" #define PULIZZI_SW_OID_MODEL_NAME ".1.3.6.1.4.1.20677.2.1.1.0" @@ -67,6 +67,12 @@ static info_lkp_t pulizzi_sw_outlet_switchability_info[] = { /* Snmp2NUT lookup table for Eaton Pulizzi Switched ePDU MIB */ static snmp_info_t eaton_pulizzi_switched_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device page */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/eaton-pdu-revelation-mib.c b/drivers/eaton-pdu-revelation-mib.c index c38e566246..8c1cd8340f 100644 --- a/drivers/eaton-pdu-revelation-mib.c +++ b/drivers/eaton-pdu-revelation-mib.c @@ -29,7 +29,7 @@ #include "eaton-pdu-revelation-mib.h" -#define EATON_APHEL_REVELATION_MIB_VERSION "0.51" +#define EATON_APHEL_REVELATION_MIB_VERSION "0.52" /* APHEL PDU-MIB - Revelation MIB (Managed ePDU) * ********************************************* */ @@ -86,6 +86,12 @@ static info_lkp_t revelation_outlet_switchability_info[] = { /* Snmp2NUT lookup table for Eaton Revelation MIB */ static snmp_info_t eaton_aphel_revelation_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device collection */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/emerson-avocent-pdu-mib.c b/drivers/emerson-avocent-pdu-mib.c index 33ff172991..096078ce4d 100644 --- a/drivers/emerson-avocent-pdu-mib.c +++ b/drivers/emerson-avocent-pdu-mib.c @@ -76,6 +76,12 @@ static info_lkp_t avocent_outlet_status_info[] = { }; static snmp_info_t emerson_avocent_pdu_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device page */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Avocent", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/hpe-pdu-mib.c b/drivers/hpe-pdu-mib.c index 70a0c7a206..bf429878b0 100644 --- a/drivers/hpe-pdu-mib.c +++ b/drivers/hpe-pdu-mib.c @@ -24,7 +24,7 @@ #include "hpe-pdu-mib.h" #include "dstate.h" -#define HPE_EPDU_MIB_VERSION "0.32" +#define HPE_EPDU_MIB_VERSION "0.33" #define HPE_EPDU_MIB_SYSOID ".1.3.6.1.4.1.232.165.7" #define HPE_EPDU_OID_MODEL_NAME ".1.3.6.1.4.1.232.165.7.1.2.1.3.0" @@ -176,6 +176,11 @@ static info_lkp_t hpe_pdu_outlet_group_phase_info[] = { /* Snmp2NUT lookup table for HPE PDU MIB */ static snmp_info_t hpe_pdu_mib[] = { + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device collection */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "HPE", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/huawei-mib.c b/drivers/huawei-mib.c index 00cd6e4ed7..9ff193b7fa 100644 --- a/drivers/huawei-mib.c +++ b/drivers/huawei-mib.c @@ -21,7 +21,7 @@ #include "huawei-mib.h" -#define HUAWEI_MIB_VERSION "0.3" +#define HUAWEI_MIB_VERSION "0.4" #define HUAWEI_SYSOID ".1.3.6.1.4.1.8072.3.2.10" #define HUAWEI_UPSMIB ".1.3.6.1.4.1.2011" @@ -141,6 +141,11 @@ static snmp_info_t huawei_mib[] = { * }; */ + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* UPS page */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Huawei", SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/ietf-mib.c b/drivers/ietf-mib.c index 3c6b922315..dd69a1e723 100644 --- a/drivers/ietf-mib.c +++ b/drivers/ietf-mib.c @@ -26,7 +26,7 @@ #include "ietf-mib.h" -#define IETF_MIB_VERSION "1.53" +#define IETF_MIB_VERSION "1.54" /* SNMP OIDs set */ #define IETF_OID_UPS_MIB "1.3.6.1.2.1.33.1." @@ -111,6 +111,12 @@ static info_lkp_t ietf_beeper_status_info[] = { /* Snmp2NUT lookup table info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar */ static snmp_info_t ietf_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* The Device Identification group */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.1.0", "Generic", SU_FLAG_STATIC, NULL }, /* upsIdentManufacturer */ { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.2.0", "Generic SNMP UPS", SU_FLAG_STATIC, NULL }, /* upsIdentModel */ diff --git a/drivers/mge-mib.c b/drivers/mge-mib.c index e987c5612f..4430112b0b 100644 --- a/drivers/mge-mib.c +++ b/drivers/mge-mib.c @@ -27,7 +27,7 @@ #include "mge-mib.h" -#define MGE_MIB_VERSION "0.53" +#define MGE_MIB_VERSION "0.54" /* TODO: * - MGE PDU MIB and sysOID (".1.3.6.1.4.1.705.2") */ @@ -147,6 +147,11 @@ static info_lkp_t mge_power_source_info[] = { /* Snmp2NUT lookup table */ static snmp_info_t mge_mib[] = { + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* UPS page */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Eaton", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.1.1.0", "Generic SNMP UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, diff --git a/drivers/netvision-mib.c b/drivers/netvision-mib.c index db035985c6..51f4073655 100644 --- a/drivers/netvision-mib.c +++ b/drivers/netvision-mib.c @@ -25,7 +25,7 @@ #include "netvision-mib.h" -#define NETVISION_MIB_VERSION "0.43" +#define NETVISION_MIB_VERSION "0.44" #define NETVISION_SYSOID ".1.3.6.1.4.1.4555.1.1.1" @@ -115,6 +115,12 @@ static info_lkp_t netvision_output_info[] = { /* Snmp2NUT lookup table */ static snmp_info_t netvision_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTAGENTSWVERSION, "SOCOMEC SICON UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTMODEL, diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index 2521a8e811..2d739347e0 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -25,7 +25,7 @@ #include "powerware-mib.h" -#define PW_MIB_VERSION "0.94" +#define PW_MIB_VERSION "0.95" /* TODO: more sysOID and MIBs support: * @@ -197,6 +197,12 @@ static info_lkp_t pw_yes_no_info[] = { /* Snmp2NUT lookup table */ static snmp_info_t pw_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* FIXME: miss device page! */ /* UPS page */ /* info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar */ diff --git a/drivers/raritan-pdu-mib.c b/drivers/raritan-pdu-mib.c index 65b1d2a788..f4e52ac440 100644 --- a/drivers/raritan-pdu-mib.c +++ b/drivers/raritan-pdu-mib.c @@ -25,7 +25,7 @@ #include "raritan-pdu-mib.h" -#define RARITAN_MIB_VERSION "0.7" +#define RARITAN_MIB_VERSION "0.8" /* Raritan MIB * this one uses the same MIB as Eaton Revelation, @@ -48,6 +48,12 @@ static info_lkp_t raritan_pdu_outlet_status_info[] = { /* Snmp2NUT lookup table for Raritan MIB */ static snmp_info_t raritan_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device page */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Raritan", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/raritan-px2-mib.c b/drivers/raritan-px2-mib.c index c49b046610..5f7ca2d8e2 100644 --- a/drivers/raritan-px2-mib.c +++ b/drivers/raritan-px2-mib.c @@ -23,7 +23,7 @@ #include "raritan-px2-mib.h" -#define RARITAN_PX2_MIB_VERSION "0.3" +#define RARITAN_PX2_MIB_VERSION "0.4" #define RARITAN_PX2_MIB_SYSOID ".1.3.6.1.4.1.13742.6" #define RARITAN_PX2_OID_MODEL_NAME ".1.3.6.1.4.1.13742.6.3.2.1.1.3.1" @@ -67,6 +67,11 @@ static info_lkp_t raritanpx2_outlet_switchability_info[] = { /* PDU2-MIB Snmp2NUT lookup table */ static snmp_info_t raritan_px2_mib[] = { + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* pduManufacturer.1 = STRING: Raritan */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.2.1", "Raritan", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, diff --git a/drivers/xppc-mib.c b/drivers/xppc-mib.c index 706a550173..0fe1ec0880 100644 --- a/drivers/xppc-mib.c +++ b/drivers/xppc-mib.c @@ -24,7 +24,7 @@ #include "xppc-mib.h" -#define XPPC_MIB_VERSION "0.3" +#define XPPC_MIB_VERSION "0.4" #define XPPC_SYSOID ".1.3.6.1.4.1.935" @@ -98,6 +98,12 @@ static snmp_info_t xppc_mib[] = { * { 0, NULL } * }; */ + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Tripp Lite / Phoenixtec", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, From b8387fa105df03b0dbc952469abf05fee67e0383 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 17 Feb 2022 12:53:02 +0100 Subject: [PATCH 151/700] drivers/xppc-mib.c: whitespace fix --- drivers/xppc-mib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/xppc-mib.c b/drivers/xppc-mib.c index 0fe1ec0880..cfbc450b89 100644 --- a/drivers/xppc-mib.c +++ b/drivers/xppc-mib.c @@ -104,8 +104,8 @@ static snmp_info_t xppc_mib[] = { { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Tripp Lite / Phoenixtec", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Tripp Lite / Phoenixtec", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /* upsBaseIdentModel.0 = STRING: "Intelligent" */ { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.935.1.1.1.1.1.1.0", "Generic Phoenixtec SNMP device", SU_FLAG_OK, NULL }, From 795e57407b7728f7c1800fd81afea916d7024b21 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 17 Feb 2022 12:53:43 +0100 Subject: [PATCH 152/700] drivers/apc-mib: whitespace fix --- drivers/apc-mib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/apc-mib.c b/drivers/apc-mib.c index 4901d4da4f..d640d2c6ef 100644 --- a/drivers/apc-mib.c +++ b/drivers/apc-mib.c @@ -192,7 +192,7 @@ static snmp_info_t apcc_mib[] = { { "input.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.4.0", "", SU_FLAG_OK, NULL }, { "input.transfer.low", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.3.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, { "input.transfer.high", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.2.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, - { "input.transfer.reason", ST_FLAG_STRING, 1, APCC_OID_TRANSFERREASON, "", SU_TYPE_INT | SU_FLAG_OK, apcc_transfer_reasons }, + { "input.transfer.reason", ST_FLAG_STRING, 1, APCC_OID_TRANSFERREASON, "", SU_TYPE_INT | SU_FLAG_OK, apcc_transfer_reasons }, { "input.sensitivity", ST_FLAG_STRING | ST_FLAG_RW, 1, APCC_OID_SENSITIVITY, "", SU_TYPE_INT | SU_FLAG_OK, apcc_sensitivity_modes }, { "ups.power", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.9.0", "", SU_FLAG_OK, NULL }, { "ups.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.8.0", "", SU_FLAG_OK, NULL }, From 1701e9e4870a59706b5955d892bd9998fdb8189f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 17 Feb 2022 12:54:03 +0100 Subject: [PATCH 153/700] drivers/eaton-pdu-marlin-mib.c: whitespace fix --- drivers/eaton-pdu-marlin-mib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 170517027b..729c98f1d0 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -217,7 +217,7 @@ static const char *marlin_outlet_group_phase_fun(void *raw_outlet_group_nb) snprintf(marlin_scratch_buf, sizeof(marlin_scratch_buf), "L%i", phases_nb); if (phases_nb < 1 || phases_nb > 3) upsdebugx(3, "WARNING: %s got %i phases which is an unexpected amount", - __func__, phases_nb); + __func__, phases_nb); return marlin_scratch_buf; } From 806d267333d7ca7f0df948914b70425ffd4fb77d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 17 Feb 2022 12:54:20 +0100 Subject: [PATCH 154/700] drivers/emerson-avocent-pdu-mib.c: whitespace fix --- drivers/emerson-avocent-pdu-mib.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/emerson-avocent-pdu-mib.c b/drivers/emerson-avocent-pdu-mib.c index 096078ce4d..cffe5554d3 100644 --- a/drivers/emerson-avocent-pdu-mib.c +++ b/drivers/emerson-avocent-pdu-mib.c @@ -25,16 +25,16 @@ #include "emerson-avocent-pdu-mib.h" -#define EMERSON_AVOCENT_MIB_VERSION "1.2" +#define EMERSON_AVOCENT_MIB_VERSION "1.3" #define EMERSON_AVOCENT_SYSOID ".1.3.6.1.4.1.10418.17.1.7" #define EMERSON_AVOCENT_OID_MODEL_NAME ".1.3.6.1.4.1.10418.17.2.1.2.0" /* FIXME: Avocent PM's seem to have 3 temperature sensors (index 1, 2, 3) * for the embedded temperature (equivalent to ups.temperature) */ -#define AVOCENT_OID_UNIT_TEMPERATURE ".1.3.6.1.4.1.10418.17.2.5.3.1.17.1.1" +#define AVOCENT_OID_UNIT_TEMPERATURE ".1.3.6.1.4.1.10418.17.2.5.3.1.17.1.1" /* Same as above for humidity... */ -#define AVOCENT_OID_UNIT_HUMIDITY ".1.3.6.1.4.1.10418.17.2.5.3.1.24.1" +#define AVOCENT_OID_UNIT_HUMIDITY ".1.3.6.1.4.1.10418.17.2.5.3.1.24.1" #define AVOCENT_OID_OUTLET_COUNT ".1.3.6.1.4.1.10418.17.2.5.3.1.8.%i.%i" @@ -42,7 +42,7 @@ #define AVOCENT_OID_UNIT_CURRENT ".1.3.6.1.4.1.10418.17.2.5.3.1.10.1.1" /* FIXME: This is actually pmPowerMgmtPDUTableVoltage1Value */ #define AVOCENT_OID_UNIT_VOLTAGE ".1.3.6.1.4.1.10418.17.2.5.3.1.31.1.1" -#define AVOCENT_OID_UNIT_MACADDR ".1.3.6.1.2.1.2.2.1.6.1" +#define AVOCENT_OID_UNIT_MACADDR ".1.3.6.1.2.1.2.2.1.6.1" #ifdef OPENGEAR_MULTIPLE_BANKS #define AVOCENT_OID_OUTLET_ID ".1.3.6.1.4.1.10418.17.2.5.5.1.3" From 1e19509bab253a8f5679916e7e3300f5e20698b0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 17 Feb 2022 13:01:45 +0100 Subject: [PATCH 155/700] drivers/snmp-ups.c: use "hard-coded" IETF MIB for read-only access to sysContact and sysLocation only if mib2nut does not yet define a better value --- drivers/snmp-ups.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 98077ac758..b5ad0f8b7c 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -675,18 +675,30 @@ void upsdrv_initups(void) dstate_addcmd("shutdown.stayoff"); } - /* Publish sysContact and sysLocation for all subdrivers */ - /* sysContact.0 */ - if (nut_snmp_get_str(".1.3.6.1.2.1.1.4.0", model, sizeof(model), NULL) == TRUE) - dstate_setinfo("device.contact", "%s", model); - else - upsdebugx(2, "Can't get and publish sysContact for device.contact"); + /* Publish sysContact and sysLocation (from IETF standard paths) + * for all subdrivers that do not have one defined in their mapping + * tables (note: for lack of better knowledge, defined as read-only + * entries here) */ + + if (NULL == dstate_getinfo("device.contact")) { + /* sysContact.0 */ + if (nut_snmp_get_str(".1.3.6.1.2.1.1.4.0", model, sizeof(model), NULL) == TRUE) { + upsdebugx(2, "Using IETF-MIB default to get and publish sysContact for device.contact"); + dstate_setinfo("device.contact", "%s", model); + } else { + upsdebugx(2, "Can't get and publish sysContact for device.contact"); + } + } - /* sysLocation.0 */ - if (nut_snmp_get_str(".1.3.6.1.2.1.1.6.0", model, sizeof(model), NULL) == TRUE) - dstate_setinfo("device.location", "%s", model); - else - upsdebugx(2, "Can't get and publish sysLocation for device.location"); + if (NULL == dstate_getinfo("device.location")) { + /* sysLocation.0 */ + if (nut_snmp_get_str(".1.3.6.1.2.1.1.6.0", model, sizeof(model), NULL) == TRUE) { + upsdebugx(2, "Using IETF-MIB default to get and publish sysLocation for device.location"); + dstate_setinfo("device.location", "%s", model); + } else { + upsdebugx(2, "Can't get and publish sysLocation for device.location"); + } + } /* set shutdown and autostart delay */ set_delays(); From ebb42f1cfc7d40e61c92c4de36edf5913bac9a2f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 17 Feb 2022 13:04:45 +0100 Subject: [PATCH 156/700] drivers/snmp-ups.c: use "hard-coded" IETF MIB for read-only access to sysDescr also (only if mib2nut does not yet define a better value) --- drivers/snmp-ups.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index b5ad0f8b7c..b019a09f4c 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -675,11 +675,21 @@ void upsdrv_initups(void) dstate_addcmd("shutdown.stayoff"); } - /* Publish sysContact and sysLocation (from IETF standard paths) + /* Publish sysDescr, sysContact and sysLocation (from IETF standard paths) * for all subdrivers that do not have one defined in their mapping * tables (note: for lack of better knowledge, defined as read-only * entries here) */ + if (NULL == dstate_getinfo("device.description")) { + /* sysDescr.0 */ + if (nut_snmp_get_str(".1.3.6.1.2.1.1.1.0", model, sizeof(model), NULL) == TRUE) { + upsdebugx(2, "Using IETF-MIB default to get and publish sysDescr for device.description"); + dstate_setinfo("device.description", "%s", model); + } else { + upsdebugx(2, "Can't get and publish sysDescr for device.description"); + } + } + if (NULL == dstate_getinfo("device.contact")) { /* sysContact.0 */ if (nut_snmp_get_str(".1.3.6.1.2.1.1.4.0", model, sizeof(model), NULL) == TRUE) { From 70155872bcba25bc662a594b4d063f63b6392c29 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 17 Feb 2022 14:03:30 +0100 Subject: [PATCH 157/700] drivers/snmp-ups.c: su_setOID(): fix mis-interpretation of "device*" as always a daisy-chain --- drivers/snmp-ups.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index b019a09f4c..da35223aff 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -3349,9 +3349,15 @@ static int su_setOID(int mode, const char *varname, const char *val) memset(setOID, 0, SU_INFOSIZE); memset(template_count_var, 0, SU_BUFSIZE); - /* Check if it's a daisychain setting */ - if (!strncmp(varname, "device", 6)) { - /* Extract the device number */ + /* Check if it's a daisychain setting (device.x.varname) */ + if (!strncmp(varname, "device", 6) + && varname[7] >= '0' + && varname[7] <= '9' + ) { + /* Extract the (single-digit) device number + * TODO: use strtol() or similar to support multi-digit + * chains and offset tmp_varname inside varname properly + */ daisychain_device_number = atoi(&varname[7]); /* Point at the command, without the "device.x" prefix */ tmp_varname = strdup(&varname[9]); From 2d4eaadeedbebb6db66a5d364fa969502f407a2d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 18 Feb 2022 14:00:27 +0100 Subject: [PATCH 158/700] drivers/snmp-ups.c: fix "device.varname" for daisychain 'master' units * su_setinfo(): should not expose master-specific data (like device.contact coming from IETF un-templated data points not aware about daisy chains) as if it were the data specific to every device in the chain; note that for defaulted data (like device.type="pdu" hardcoded with NULL OID) we still expose it across the board * su_setOID(): "upsrw ... device.contact" should change the daisychain master device (for IETF un-templated data; but in this commit - always) --- drivers/snmp-ups.c | 73 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 22 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index da35223aff..536266e770 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -1536,7 +1536,15 @@ void su_setinfo(snmp_info_t *su_info_p, const char *value) /* pre-fill with the device name for checking */ snprintf(info_type, 128, "device.%i", current_device_number); - if ((daisychain_enabled == TRUE) && (devices_count > 1)) { + /* Daisy-chain template magic should only apply to defaulted + * entries (oid==null) or templated entries (contains .%i) + * NOTE: For setting the values (or commands) from clients + * like `upsrw`, see su_setOID() method. Here we change our + * device state records based on readings from a device. + */ + if ((daisychain_enabled == TRUE) && (devices_count > 1) + && (su_info_p->OID == NULL || strstr(su_info_p->OID, ".%i") != NULL) + ) { /* Only append "device.X" for master and slaves, if not already done! */ if ((current_device_number > 0) && (strstr(su_info_p->info_type, info_type) == NULL)) { /* Special case: we remove "device" from the device collection not to @@ -3349,27 +3357,48 @@ static int su_setOID(int mode, const char *varname, const char *val) memset(setOID, 0, SU_INFOSIZE); memset(template_count_var, 0, SU_BUFSIZE); - /* Check if it's a daisychain setting (device.x.varname) */ - if (!strncmp(varname, "device", 6) - && varname[7] >= '0' - && varname[7] <= '9' - ) { - /* Extract the (single-digit) device number - * TODO: use strtol() or similar to support multi-digit - * chains and offset tmp_varname inside varname properly - */ - daisychain_device_number = atoi(&varname[7]); - /* Point at the command, without the "device.x" prefix */ - tmp_varname = strdup(&varname[9]); - snprintf(template_count_var, 10, "%s", varname); - - upsdebugx(2, "%s: got a daisychain %s (%s) for device %i", - __func__, (mode==SU_MODE_INSTCMD)?"command":"setting", - tmp_varname, daisychain_device_number); - - if (daisychain_device_number > devices_count) - upsdebugx(2, "%s: item is out of bound (%i / %ld)", - __func__, daisychain_device_number, devices_count); + /* Check if it's a daisychain setting (device.x.varname), + * or a non-daisy setting/value/cmd for the "master" unit + * (as un-numbered device.varname), or something else? + */ + if (!strncmp(varname, "device", 6)) { + if (varname[7] >= '0' && varname[7] <= '9') { + /* Extract the (single-digit) device number + * TODO: use strtol() or similar to support multi-digit + * chains and offset tmp_varname inside varname properly + */ + daisychain_device_number = atoi(&varname[7]); + /* Point at the command, without the "device.x" prefix */ + tmp_varname = strdup(&varname[9]); + snprintf(template_count_var, 10, "%s", varname); + + upsdebugx(2, "%s: got a daisychain %s (%s) for device %i", + __func__, (mode==SU_MODE_INSTCMD)?"command":"setting", + tmp_varname, daisychain_device_number); + + if (daisychain_device_number > devices_count) + upsdebugx(2, "%s: item is out of bound (%i / %ld)", + __func__, daisychain_device_number, devices_count); + } + else { + /* TODO: Ideally, check if "OID" contains ".%i" template text like: + * (su_info_p->OID == NULL || strstr(su_info_p->OID, ".%i") != NULL) + * like we do in su_setinfo(); eventually we might get vendor + * MIBs that do expose device.contact/location/description + * for each link in the chain... + */ + if (daisychain_enabled == TRUE) { + daisychain_device_number = 1; + upsdebugx(2, "%s: got an un-numbered daisychain %s (%s), " + "directing it to 'master' device %i", + __func__, (mode==SU_MODE_INSTCMD)?"command":"setting", + varname, daisychain_device_number); + } else { + /* No daisy, no poppy */ + daisychain_device_number = 0; + } + tmp_varname = strdup(varname); + } } else { daisychain_device_number = 0; From 539f7e1998ae81d19d2bbbb6b94e4d05a8d0ffc1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 18 Feb 2022 14:08:51 +0100 Subject: [PATCH 159/700] drivers/snmp-ups.c: su_setinfo(): fix comment --- drivers/snmp-ups.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 536266e770..6f545ded6d 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -1527,7 +1527,8 @@ void su_setinfo(snmp_info_t *su_info_p, const char *value) char info_type[128]; /* We tweak incoming "su_info_p->info_type" value in some cases */ /* FIXME: Replace hardcoded 128 with a macro above (use {SU_}LARGEBUF?), - *and same macro or sizeof(info_type) below? */ + * and same macro or sizeof(info_type) below (also more 128 cases below)? + */ upsdebugx(1, "entering %s(%s, %s)", __func__, su_info_p->info_type, (value)?value:""); From 013c07ec3a48332fd01adcf478cfed0292c62ce7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 18 Feb 2022 14:15:41 +0100 Subject: [PATCH 160/700] drivers/snmp-ups.c: su_setOID(): import (commented away - not deemed necessary so far) fallback for e.g. "device.x.contact" is not found as a "contact" --- drivers/snmp-ups.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 6f545ded6d..db7a777d23 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -3416,9 +3416,19 @@ static int su_setOID(int mode, const char *varname, const char *val) } /* Check if it is outlet / outlet.group, or standard variable */ - if (strncmp(tmp_varname, "outlet", 6)) + if (strncmp(tmp_varname, "outlet", 6)) { su_info_p = su_find_info(tmp_varname); - else { + /* what if e.g. "device.x.contact" is not found as a "contact"? */ +/* + if (!su_info_p && strcmp(tmp_varname, varname)) { + upsdebugx(2, + "%s: did not find info for daisychained entry %s, " + "retrying with original varname %s", + __func__, tmp_varname, varname); + su_info_p = su_find_info(varname); + } +*/ + } else { snmp_info_t *tmp_info_p; /* Point the outlet or outlet group number in the string */ const char *item_number_ptr = NULL; From 40a7f1f9133def05d691ee24e405792f36b93874 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 18 Feb 2022 14:23:31 +0100 Subject: [PATCH 161/700] drivers/snmp-ups.c: su_setOID(): handle e.g. "device.contact" as either "device.0.contact" for "all devices" if OID is templated or NULL, or "device.1.contact" for "master device" in other cases --- drivers/snmp-ups.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index db7a777d23..394fff1f19 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -3382,18 +3382,32 @@ static int su_setOID(int mode, const char *varname, const char *val) __func__, daisychain_device_number, devices_count); } else { - /* TODO: Ideally, check if "OID" contains ".%i" template text like: - * (su_info_p->OID == NULL || strstr(su_info_p->OID, ".%i") != NULL) + /* Note: below we check if "OID" contains ".%i" template text * like we do in su_setinfo(); eventually we might get vendor * MIBs that do expose device.contact/location/description * for each link in the chain... */ if (daisychain_enabled == TRUE) { - daisychain_device_number = 1; + /* Is the original "device.varname" backed by a templated + * OID string, so we can commonly set e.g. device.contact + * for everything in the chain? + */ + su_info_p = su_find_info(varname); + if (su_info_p + && (su_info_p->OID == NULL || strstr(su_info_p->OID, ".%i") != NULL) + ) { + /* is templated or defaulted */ + daisychain_device_number = 0; + } else { + daisychain_device_number = 1; + } + upsdebugx(2, "%s: got an un-numbered daisychain %s (%s), " - "directing it to 'master' device %i", + "directing it to %s", __func__, (mode==SU_MODE_INSTCMD)?"command":"setting", - varname, daisychain_device_number); + varname, + (daisychain_device_number==1)?"'master' device":"all devices" + ); } else { /* No daisy, no poppy */ daisychain_device_number = 0; From f1165d2b52eba0e7d88db0a4ad3dfaf9e42b79d4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 18 Feb 2022 19:06:15 +0100 Subject: [PATCH 162/700] Add tools/nut-dumpdiff.sh helper --- tools/Makefile.am | 2 +- tools/nut-dumpdiff.sh | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100755 tools/nut-dumpdiff.sh diff --git a/tools/Makefile.am b/tools/Makefile.am index 455c915707..90951953b3 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -13,7 +13,7 @@ SUBDIRS = . nut-scanner PYTHON = @PYTHON@ -EXTRA_DIST = nut-usbinfo.pl nut-recorder.sh nut-ddl-dump.sh \ +EXTRA_DIST = nut-usbinfo.pl nut-recorder.sh nut-ddl-dump.sh nut-dumpdiff.sh \ gitlog2changelog.py.in nut-snmpinfo.py.in driver-list-format.sh GENERATED_SNMP_FILES = nut-scanner/nutscan-snmp.h diff --git a/tools/nut-dumpdiff.sh b/tools/nut-dumpdiff.sh new file mode 100755 index 0000000000..f80fe689b8 --- /dev/null +++ b/tools/nut-dumpdiff.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# This script is intended to simplify comparison of NUT data dumps, +# such as those collected by drivers with `-d 1` argument, or by +# the `upsc` client, ignoring irrelevant variations (e.g. numbers). +# +# TODO: Make more portable than bash and GNU toolkits +# +# Subject to same license as the NUT Project. +# +# Copyright (C) +# 2022 Jim Klimov +# + +if [ $# = 2 ] && [ -s "$1" ] && [ -s "$2" ]; then + echo "=== $0: comparing '$1' (-) vs '$2' (+)" >&2 +else + echo "=== $0: aborting: requires two filenames to compare as arguments" >&2 + exit 1 +fi + +# Pre-sort just in case: +DATA1="`sort -n < "$1"`" +DATA2="`sort -n < "$2"`" + +diff -bu <(echo "$DATA1") <(echo "$DATA2") \ +| grep -E '^[+-][^+-]' \ +| grep -vE '^[^:]*: [0-9][0-9.]*$' From 00dd078759b28f7d7096c9d2ff89dc6eea4357cf Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 18 Feb 2022 19:17:04 +0100 Subject: [PATCH 163/700] tools/nut-dumpdiff.sh: extend to not strip ALL numeric values but ones that are likely measurements --- tools/nut-dumpdiff.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/nut-dumpdiff.sh b/tools/nut-dumpdiff.sh index f80fe689b8..0a8451ac1f 100755 --- a/tools/nut-dumpdiff.sh +++ b/tools/nut-dumpdiff.sh @@ -23,6 +23,11 @@ fi DATA1="`sort -n < "$1"`" DATA2="`sort -n < "$2"`" +# Strip away same-context lines, +# and lines with values that are decimal numbers, +# and lines with multi-digit numbers without a decimal point +# (assuming differences in shorter counters may be important) diff -bu <(echo "$DATA1") <(echo "$DATA2") \ | grep -E '^[+-][^+-]' \ -| grep -vE '^[^:]*: [0-9][0-9.]*$' +| grep -vE '^[^:]*: [0-9][0-9]*\.[0-9][0-9]*$' \ +| grep -vE '^[^:]*: [0-9][0-9]*$' From 60363eb16ea63109c1efe0aee3a43e8de017377c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 18 Feb 2022 19:21:53 +0100 Subject: [PATCH 164/700] tools/nut-dumpdiff.sh: revise to strip just numeric measurements of (*.power|voltage|current) --- tools/nut-dumpdiff.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/nut-dumpdiff.sh b/tools/nut-dumpdiff.sh index 0a8451ac1f..8bbab3a501 100755 --- a/tools/nut-dumpdiff.sh +++ b/tools/nut-dumpdiff.sh @@ -24,10 +24,9 @@ DATA1="`sort -n < "$1"`" DATA2="`sort -n < "$2"`" # Strip away same-context lines, -# and lines with values that are decimal numbers, -# and lines with multi-digit numbers without a decimal point -# (assuming differences in shorter counters may be important) +# and lines with measurements that are either decimal numbers +# or multi-digit numbers without a decimal point (assuming +# differences in shorter numbers or counters may be important) diff -bu <(echo "$DATA1") <(echo "$DATA2") \ | grep -E '^[+-][^+-]' \ -| grep -vE '^[^:]*: [0-9][0-9]*\.[0-9][0-9]*$' \ -| grep -vE '^[^:]*: [0-9][0-9]*$' +| grep -vE '^[^:]*(power|voltage|current): ([0-9][0-9]*|[0-9][0-9]*\.[0-9][0-9]*)$' From 14157060429515b8466e9e3fedcf2b1b96cd5ed1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 18 Feb 2022 19:22:14 +0100 Subject: [PATCH 165/700] docs/documentation.txt: document tools/nut-dumpdiff.sh helper --- docs/documentation.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/documentation.txt b/docs/documentation.txt index b8d946aa00..c588e12174 100644 --- a/docs/documentation.txt +++ b/docs/documentation.txt @@ -63,6 +63,17 @@ or as a pull request against the link:https://github.com/networkupstools/nut-ddl[NUT Devices Dumps Library] following the naming and other rules described in the DDL documentation page. +Data dumps collected by the tools above, or by `upsc` client, or by drivers +in exploratory data-dumping mode (with `-d 1` argument), can be compared by +ifdef::website[] +link:https://raw.githubusercontent.com/networkupstools/nut/master/tools/nut-dumpdiff.sh[`tools/nut-dumpdiff.sh`] +endif::website[] +ifndef::website[] +`./tools/nut-dumpdiff.sh` +endif::website[] +script from the main NUT codebase, which strips away lines with only numeric +values (aiming to minimize the risk of losing meaningful changes like counters). + Offsite Links ------------- From afb5d23d53b4b7c87263af7fb3583f885af14bf8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 18 Feb 2022 20:02:31 +0100 Subject: [PATCH 166/700] tools/nut-dumpdiff.sh: revise to also strip (load|temperature|humidity) measurements --- tools/nut-dumpdiff.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/nut-dumpdiff.sh b/tools/nut-dumpdiff.sh index 8bbab3a501..f7a2002fc1 100755 --- a/tools/nut-dumpdiff.sh +++ b/tools/nut-dumpdiff.sh @@ -29,4 +29,7 @@ DATA2="`sort -n < "$2"`" # differences in shorter numbers or counters may be important) diff -bu <(echo "$DATA1") <(echo "$DATA2") \ | grep -E '^[+-][^+-]' \ -| grep -vE '^[^:]*(power|voltage|current): ([0-9][0-9]*|[0-9][0-9]*\.[0-9][0-9]*)$' +| grep -vE '^[^:]*(power|load|voltage|current|temperature|humidity): ([0-9][0-9]*|[0-9][0-9]*\.[0-9][0-9]*)$' + +# Note: up to user to post-filter, "^driver.version.*:" +# may be deemed irrelevant as well From 6063ec7f431bcb1f01a00835c5715d06d97b90d8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 18 Feb 2022 22:52:09 +0100 Subject: [PATCH 167/700] tools/nut-dumpdiff.sh: revise to also strip frequency measurements --- tools/nut-dumpdiff.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/nut-dumpdiff.sh b/tools/nut-dumpdiff.sh index f7a2002fc1..0dcb4707e2 100755 --- a/tools/nut-dumpdiff.sh +++ b/tools/nut-dumpdiff.sh @@ -29,7 +29,7 @@ DATA2="`sort -n < "$2"`" # differences in shorter numbers or counters may be important) diff -bu <(echo "$DATA1") <(echo "$DATA2") \ | grep -E '^[+-][^+-]' \ -| grep -vE '^[^:]*(power|load|voltage|current|temperature|humidity): ([0-9][0-9]*|[0-9][0-9]*\.[0-9][0-9]*)$' +| grep -vE '^[^:]*(power|load|voltage|current|frequency|temperature|humidity): ([0-9][0-9]*|[0-9][0-9]*\.[0-9][0-9]*)$' # Note: up to user to post-filter, "^driver.version.*:" # may be deemed irrelevant as well From b7e8401c1779b62805be4393329d5e7a336da00c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 11:45:44 +0100 Subject: [PATCH 168/700] drivers/snmp-ups.c: su_setinfo(): this is not the place to check for ".%i" after all --- drivers/snmp-ups.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 394fff1f19..01ec31d381 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -1538,14 +1538,28 @@ void su_setinfo(snmp_info_t *su_info_p, const char *value) snprintf(info_type, 128, "device.%i", current_device_number); /* Daisy-chain template magic should only apply to defaulted - * entries (oid==null) or templated entries (contains .%i) + * entries (oid==null) or templated entries (contains .%i); + * however at this point we see exact OIDs handed down from + * su_ups_get() which instantiates a template (if needed - + * and knows it was a template) and calls su_setinfo(). * NOTE: For setting the values (or commands) from clients * like `upsrw`, see su_setOID() method. Here we change our * device state records based on readings from a device. */ - if ((daisychain_enabled == TRUE) && (devices_count > 1) - && (su_info_p->OID == NULL || strstr(su_info_p->OID, ".%i") != NULL) - ) { + if ((daisychain_enabled == TRUE) && (devices_count > 1)) { + if (su_info_p->OID != NULL + && strstr(su_info_p->OID, ".%i") != NULL + ) { + /* Only inform, do not react so far, + * need more understanding if and when + * such situation might happen at all: + */ + upsdebugx(5, "%s: in a daisy-chained device, " + "got a templated OID %s for type %s", + __func__, su_info_p->OID, + su_info_p->info_type); + } + /* Only append "device.X" for master and slaves, if not already done! */ if ((current_device_number > 0) && (strstr(su_info_p->info_type, info_type) == NULL)) { /* Special case: we remove "device" from the device collection not to From 175af7b3cf9b12b4bb983f5ffd9fa7ee5531f302 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 12:35:47 +0100 Subject: [PATCH 169/700] drivers/snmp-ups.c: su_setinfo(): trace diags for different code-paths about daisy-chained OID decisions --- drivers/snmp-ups.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 01ec31d381..2abd96d44e 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -1565,19 +1565,39 @@ void su_setinfo(snmp_info_t *su_info_p, const char *value) /* Special case: we remove "device" from the device collection not to * get "device.X.device.", but "device.X." */ if (!strncmp(su_info_p->info_type, "device.", 7)) { + upsdebugx(6, "%s: in a daisy-chained device, " + "OID %s: TRIM 'device.' from type %s (value %s)", + __func__, su_info_p->OID, + su_info_p->info_type, (value)?value:""); snprintf(info_type, 128, "device.%i.%s", current_device_number, su_info_p->info_type + 7); } else { + upsdebugx(6, "%s: in a daisy-chained device, " + "OID %s is templated: for type %s (value %s)", + __func__, su_info_p->OID, + su_info_p->info_type, (value)?value:""); snprintf(info_type, 128, "device.%i.%s", current_device_number, su_info_p->info_type); } } - else + else { + upsdebugx(6, "%s: in a daisy-chained device, " + "OID %s: for type %s (value %s) " + "device %d is not positive or type already " + "contains the prepared expectation: %s", + __func__, su_info_p->OID, + su_info_p->info_type, (value)?value:"", + current_device_number, + info_type + ); snprintf(info_type, 128, "%s", su_info_p->info_type); + } } - else + else { + upsdebugx(6, "%s: NOT in a daisy-chained device", __func__); snprintf(info_type, 128, "%s", su_info_p->info_type); + } upsdebugx(1, "%s: using info_type '%s'", __func__, info_type); From debe93ea881ad823a06628bfc14384ecf625eecd Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 12:36:53 +0100 Subject: [PATCH 170/700] drivers/snmp-ups.c: wrap long lines, add comments --- drivers/snmp-ups.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 2abd96d44e..b3a637c987 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2173,7 +2173,8 @@ static bool_t is_multiple_template(const char *OID_template) * Note: remember to adapt info_type, OID and optionaly dfl */ static snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *new_instance) { - upsdebugx(1, "%s(%s)", __func__, info_template ? info_template->info_type : "n/a"); + upsdebugx(1, "%s(%s)", __func__, + info_template ? info_template->info_type : "n/a"); /* sanity check */ if (info_template == NULL) @@ -2181,6 +2182,8 @@ static snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *ne if (new_instance == NULL) new_instance = (snmp_info_t *)xmalloc(sizeof(snmp_info_t)); + /* TOTHINK: Should there be an "else" to free() + * the fields which we (re-)allocate below? */ new_instance->info_type = (char *)xmalloc(SU_INFOSIZE); if (new_instance->info_type) @@ -2190,8 +2193,10 @@ static snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *ne if (new_instance->OID) memset((char *)new_instance->OID, 0, SU_INFOSIZE); } - else + else { new_instance->OID = NULL; + } + new_instance->info_flags = info_template->info_flags; new_instance->info_len = info_template->info_len; /* FIXME: check if we need to adapt this one... */ @@ -2636,7 +2641,8 @@ bool_t get_and_process_data(int mode, snmp_info_t *su_info_p) { bool_t status = FALSE; - upsdebugx(1, "%s: %s (%s)", __func__, su_info_p->info_type, su_info_p->OID); + upsdebugx(1, "%s: %s (%s)", __func__, + su_info_p->info_type, su_info_p->OID); /* ok, update this element. */ status = su_ups_get(su_info_p); @@ -3162,7 +3168,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) /* adapt info_type */ if (su_info_p->info_type != NULL) { - snprintf((char *)tmp_info_p->info_type, SU_INFOSIZE, "%s", su_info_p->info_type); + snprintf((char *)tmp_info_p->info_type, + SU_INFOSIZE, "%s", + su_info_p->info_type); } else { free_info(tmp_info_p); @@ -3299,7 +3307,8 @@ bool_t su_ups_get(snmp_info_t *su_info_p) } if (su_info_p->info_flags & ST_FLAG_STRING) { - status = nut_snmp_get_str(su_info_p->OID, buf, sizeof(buf), su_info_p->oid2info); + status = nut_snmp_get_str(su_info_p->OID, buf, + sizeof(buf), su_info_p->oid2info); if (status == TRUE) { if (quirk_symmetra_threephase) { if (!strcasecmp(su_info_p->info_type, "input.transfer.low") @@ -3351,8 +3360,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) su_setinfo(su_info_p, buf); upsdebugx(2, "=> value: %s", buf); } - else + else { upsdebugx(2, "=> Failed"); + } free_info(tmp_info_p); return status; From bce01f0cee632946857318ca34034fe849f83b7c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 12:43:53 +0100 Subject: [PATCH 171/700] drivers/snmp-ups.c: get_and_process_data(), su_ups_get(): add tracing logs to make sense of daisy-chain OID adaptations --- drivers/snmp-ups.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index b3a637c987..9a3b531c9b 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2646,6 +2646,7 @@ bool_t get_and_process_data(int mode, snmp_info_t *su_info_p) /* ok, update this element. */ status = su_ups_get(su_info_p); + upsdebugx(4, "%s: su_ups_get returned %d", __func__, status); /* set stale flag if data is stale, clear if not. */ if (status == TRUE) { @@ -2656,6 +2657,7 @@ bool_t get_and_process_data(int mode, snmp_info_t *su_info_p) } if(su_info_p->flags & SU_FLAG_UNIQUE) { /* We should be the only provider of this */ + upsdebugx(4, "%s: unique flag", __func__); disable_competition(su_info_p); su_info_p->flags &= ~SU_FLAG_UNIQUE; } @@ -3142,8 +3144,13 @@ bool_t su_ups_get(snmp_info_t *su_info_p) /* Check if this is a daisychain template */ if ((format_char = strchr(su_info_p->OID, '%')) != NULL) { + upsdebugx(3, "%s: calling instantiate_info() for " + "daisy-chain template", __func__); tmp_info_p = instantiate_info(su_info_p, tmp_info_p); if (tmp_info_p != NULL) { + upsdebugx(3, "%s: instantiate_info() returned " + "non-null OID: %s", + __func__, tmp_info_p->OID); /* adapt the OID */ if (su_info_p->OID != NULL) { #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL @@ -3160,6 +3167,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL #pragma GCC diagnostic pop #endif + upsdebugx(3, "%s: OID %s adapted into %s", + __func__, su_info_p->OID, + tmp_info_p->OID); } else { free_info(tmp_info_p); @@ -3187,6 +3197,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) if (!strcasecmp(su_info_p->info_type, "ups.status")) { /* FIXME: daisychain status support! */ + upsdebugx(2, "%s: requesting nut_snmp_get_int() for " + "ups.status, with%s daisy template originally", + __func__, (format_char!=NULL ? "" : "out")); status = nut_snmp_get_int(su_info_p->OID, &value); if (status == TRUE) { @@ -3207,6 +3220,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) upsdebugx(2, "Processing alarm: %s", su_info_p->info_type); /* FIXME: daisychain alarms support! */ + upsdebugx(2, "%s: requesting nut_snmp_get_int() for " + "some alarm, with%s daisy template originally", + __func__, (format_char!=NULL ? "" : "out")); status = nut_snmp_get_int(su_info_p->OID, &value); if (status == TRUE) { @@ -3224,6 +3240,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) * present, this means that the alarm condition is TRUE. * Only present in powerware-mib.c for now */ if (!strcasecmp(su_info_p->info_type, "ups.alarms")) { + upsdebugx(2, "%s: requesting nut_snmp_get_int() for " + "ups.alarms, with%s daisy template originally", + __func__, (format_char!=NULL ? "" : "out")); status = nut_snmp_get_int(su_info_p->OID, &value); if (status == TRUE) { upsdebugx(2, "=> %ld alarms present", value); @@ -3275,6 +3294,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) if (!strcasecmp(su_info_p->info_type, "ambient.temperature")) { float temp=0; + upsdebugx(2, "%s: requesting nut_snmp_get_int() for " + "ambient.temperature, with%s daisy template originally", + __func__, (format_char!=NULL ? "" : "out")); status = nut_snmp_get_int(su_info_p->OID, &value); if(status != TRUE) { @@ -3307,6 +3329,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) } if (su_info_p->info_flags & ST_FLAG_STRING) { + upsdebugx(2, "%s: requesting nut_snmp_get_str(), " + "with%s daisy template originally", + __func__, (format_char!=NULL ? "" : "out")); status = nut_snmp_get_str(su_info_p->OID, buf, sizeof(buf), su_info_p->oid2info); if (status == TRUE) { @@ -3326,6 +3351,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) } } } else { + upsdebugx(2, "%s: requesting nut_snmp_get_int(), " + "with%s daisy template originally", + __func__, (format_char!=NULL ? "" : "out")); status = nut_snmp_get_int(su_info_p->OID, &value); if (status == TRUE) { if ((su_info_p->flags&SU_FLAG_NEGINVALID && value<0) From 9d6419cb3fd9febfb772741670b002ed9d0eba87 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 14:04:43 +0100 Subject: [PATCH 172/700] drivers/snmp-ups.c: su_ups_get(): fake current_device_number=1 to su_setinfo() when walking a daisy-chain with non-templated OID --- drivers/snmp-ups.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 9a3b531c9b..5fb9883b80 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -3138,6 +3138,7 @@ bool_t su_ups_get(snmp_info_t *su_info_p) alarms_info_t * alarms; int index = 0; char *format_char = NULL; + int saved_current_device_number = -1; snmp_info_t *tmp_info_p = NULL; upsdebugx(2, "%s: %s %s", __func__, su_info_p->info_type, su_info_p->OID); @@ -3194,6 +3195,41 @@ bool_t su_ups_get(snmp_info_t *su_info_p) return FALSE; } } + else { + /* Non-templated OID, still may be aimed at a + * daisy-chained device (master of the chain + * makes sense for IETF device.contact etc.). + * BUT: It could be a direct request for earlier + * resolved OID. So check for "device.N." too. + */ + if (daisychain_enabled == TRUE + && devices_count > 1 + && current_device_number > 0 + ) { + /* So we had a literal OID string, originally + * Check for "device.N." in the string: */ + char * varname = su_info_p->info_type; + if (!strncmp(varname, "device.", 7) + && (varname[7] >= '0' && varname[7] <= '9') + ) { + upsdebugx(2, "%s: keeping original " + "current device == %d for " + "non-templated daisy value", + __func__, current_device_number); + } else { + upsdebugx(2, "%s: would fake " + "current device == 1 for " + "non-templated daisy value " + "instead of %d", + __func__, current_device_number); + saved_current_device_number = current_device_number; + } + /* At this point we applied no hacks yet, + * just stashed a non-negative value into + * saved_current_device_number + */ + } + } if (!strcasecmp(su_info_p->info_type, "ups.status")) { /* FIXME: daisychain status support! */ @@ -3385,7 +3421,13 @@ bool_t su_ups_get(snmp_info_t *su_info_p) } if (status == TRUE) { + if (saved_current_device_number >= 0) { + current_device_number = 1; + } su_setinfo(su_info_p, buf); + if (saved_current_device_number >= 0) { + current_device_number = saved_current_device_number; + } upsdebugx(2, "=> value: %s", buf); } else { From c7a11887ddda38599fdfaf53af477ca6c6dc7eda Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 14:38:30 +0100 Subject: [PATCH 173/700] drivers/snmp-ups.c: upsdrv_initups(): comment and log that IETF-MIB fallback defaults for contact/location/description are only read once (not updated while driver runs) --- drivers/snmp-ups.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 5fb9883b80..1238c9ea04 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -678,12 +678,12 @@ void upsdrv_initups(void) /* Publish sysDescr, sysContact and sysLocation (from IETF standard paths) * for all subdrivers that do not have one defined in their mapping * tables (note: for lack of better knowledge, defined as read-only - * entries here) */ + * entries here, and also read-once - not updated during driver uptime) */ if (NULL == dstate_getinfo("device.description")) { /* sysDescr.0 */ if (nut_snmp_get_str(".1.3.6.1.2.1.1.1.0", model, sizeof(model), NULL) == TRUE) { - upsdebugx(2, "Using IETF-MIB default to get and publish sysDescr for device.description"); + upsdebugx(2, "Using IETF-MIB default to get and publish sysDescr for device.description (once)"); dstate_setinfo("device.description", "%s", model); } else { upsdebugx(2, "Can't get and publish sysDescr for device.description"); @@ -693,7 +693,7 @@ void upsdrv_initups(void) if (NULL == dstate_getinfo("device.contact")) { /* sysContact.0 */ if (nut_snmp_get_str(".1.3.6.1.2.1.1.4.0", model, sizeof(model), NULL) == TRUE) { - upsdebugx(2, "Using IETF-MIB default to get and publish sysContact for device.contact"); + upsdebugx(2, "Using IETF-MIB default to get and publish sysContact for device.contact (once)"); dstate_setinfo("device.contact", "%s", model); } else { upsdebugx(2, "Can't get and publish sysContact for device.contact"); @@ -703,7 +703,7 @@ void upsdrv_initups(void) if (NULL == dstate_getinfo("device.location")) { /* sysLocation.0 */ if (nut_snmp_get_str(".1.3.6.1.2.1.1.6.0", model, sizeof(model), NULL) == TRUE) { - upsdebugx(2, "Using IETF-MIB default to get and publish sysLocation for device.location"); + upsdebugx(2, "Using IETF-MIB default to get and publish sysLocation for device.location (once)"); dstate_setinfo("device.location", "%s", model); } else { upsdebugx(2, "Can't get and publish sysLocation for device.location"); From 630739e0f3fa6efa67b519e34d3c6a16a0aac135 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 14:40:08 +0100 Subject: [PATCH 174/700] drivers/snmp-ups.c: upsdrv_initups(): check that there is no daisychain master entry before defining IETF-MIB fallback defaults for contact/location/description --- drivers/snmp-ups.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 1238c9ea04..679411c64f 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -680,7 +680,9 @@ void upsdrv_initups(void) * tables (note: for lack of better knowledge, defined as read-only * entries here, and also read-once - not updated during driver uptime) */ - if (NULL == dstate_getinfo("device.description")) { + if (NULL == dstate_getinfo("device.description") + && NULL == dstate_getinfo("device.1.description") + ) { /* sysDescr.0 */ if (nut_snmp_get_str(".1.3.6.1.2.1.1.1.0", model, sizeof(model), NULL) == TRUE) { upsdebugx(2, "Using IETF-MIB default to get and publish sysDescr for device.description (once)"); @@ -690,7 +692,9 @@ void upsdrv_initups(void) } } - if (NULL == dstate_getinfo("device.contact")) { + if (NULL == dstate_getinfo("device.contact") + && NULL == dstate_getinfo("device.1.contact") + ) { /* sysContact.0 */ if (nut_snmp_get_str(".1.3.6.1.2.1.1.4.0", model, sizeof(model), NULL) == TRUE) { upsdebugx(2, "Using IETF-MIB default to get and publish sysContact for device.contact (once)"); @@ -700,7 +704,9 @@ void upsdrv_initups(void) } } - if (NULL == dstate_getinfo("device.location")) { + if (NULL == dstate_getinfo("device.location") + && NULL == dstate_getinfo("device.1.location") + ) { /* sysLocation.0 */ if (nut_snmp_get_str(".1.3.6.1.2.1.1.6.0", model, sizeof(model), NULL) == TRUE) { upsdebugx(2, "Using IETF-MIB default to get and publish sysLocation for device.location (once)"); From b952e8ce3b5f31a70e159a678b639db3ded26025 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 18:48:01 +0000 Subject: [PATCH 175/700] drivers/dstate.c: send_to_all/send_to_one: extend failed-send tracing --- drivers/dstate.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 0e89a76f8d..53b8d1917b 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -213,7 +213,10 @@ static void send_to_all(const char *fmt, ...) ret = write(conn->fd, buf, buflen); if ((ret < 1) || (ret != (ssize_t)buflen)) { - upsdebugx(1, "write %zd bytes to socket %d failed", buflen, conn->fd); + upsdebugx(1, "%s: write %zd bytes to socket %d failed " + "(ret=%zd): %s", + __func__, buflen, conn->fd, ret, strerror(errno)); + upsdebugx(6, "failed write: %s", buf); sock_disconnect(conn); } } @@ -261,7 +264,10 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) ret = write(conn->fd, buf, strlen(buf)); if ((ret < 1) || (ret != (ssize_t)buflen)) { - upsdebugx(1, "write %zd bytes to socket %d failed", buflen, conn->fd); + upsdebugx(1, "%s: write %zd bytes to socket %d failed " + "(ret=%zd): %s", + __func__, buflen, conn->fd, ret, strerror(errno)); + upsdebugx(6, "failed write: %s", buf); sock_disconnect(conn); return 0; /* failed */ } From 233b717f5f5e8f24c1e940cc56d868b3e9d51f94 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 18:48:29 +0000 Subject: [PATCH 176/700] drivers/dstate.c: send_to_one(): consistently use "buflen" --- drivers/dstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 53b8d1917b..167706f0c5 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -261,7 +261,7 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) if (ret <= INT_MAX) upsdebugx(5, "%s: %.*s", __func__, (int)(ret-1), buf); - ret = write(conn->fd, buf, strlen(buf)); + ret = write(conn->fd, buf, buflen); if ((ret < 1) || (ret != (ssize_t)buflen)) { upsdebugx(1, "%s: write %zd bytes to socket %d failed " From 1de376c5a3810515016113cda8f9d748f75d7d8b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 18:49:58 +0000 Subject: [PATCH 177/700] drivers/snmp-ups.c upsdrv_updateinfo(); server/upsd.c driver_free() mainloop(): trace connection faults and reconnections better --- drivers/snmp-ups.c | 2 ++ server/upsd.c | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 679411c64f..c763a9de93 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -273,9 +273,11 @@ void upsdrv_updateinfo(void) /* update all dynamic info fields */ if (snmp_ups_walk(SU_WALKMODE_UPDATE)) { + upsdebugx(1, "%s: pollfreq: Data OK", __func__); dstate_dataok(); } else { + upsdebugx(1, "%s: pollfreq: Data STALE", __func__); dstate_datastale(); } diff --git a/server/upsd.c b/server/upsd.c index 4a709aa8c1..ba8e23759f 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -669,6 +669,9 @@ static void driver_free(void) upstype_t *ups, *unext; for (ups = firstups; ups; ups = unext) { + upsdebugx(1, "%s: forgetting UPS [%s] (FD %d)", + __func__, ups->name, ups->sock_fd); + unext = ups->next; if (ups->sock_fd != -1) { @@ -985,7 +988,11 @@ static void mainloop(void) /* see if we need to (re)connect to the socket */ if (ups->sock_fd < 0) { + upsdebugx(1, "%s: UPS [%s] is not currently connected", + __func__, ups->name); ups->sock_fd = sstate_connect(ups); + upsdebugx(1, "%s: UPS [%s] is now connected as FD %d", + __func__, ups->name, ups->sock_fd); continue; } From e2829cd07684da36ed49ebc5226c009477581d9c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 18:56:04 +0000 Subject: [PATCH 178/700] drivers/snmp-ups.c: su_setOID(): uncomment the fallback for "device.x.contact" not found as a "contact", and add another to strip "device.1." => "device." to allow setting non-templated values to daisy-chain master --- drivers/snmp-ups.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index c763a9de93..2a8894ddfd 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -3555,16 +3555,36 @@ static int su_setOID(int mode, const char *varname, const char *val) if (strncmp(tmp_varname, "outlet", 6)) { su_info_p = su_find_info(tmp_varname); /* what if e.g. "device.x.contact" is not found as a "contact"? */ -/* if (!su_info_p && strcmp(tmp_varname, varname)) { upsdebugx(2, "%s: did not find info for daisychained entry %s, " "retrying with original varname %s", __func__, tmp_varname, varname); su_info_p = su_find_info(varname); + + /* Still nothing? Try to revert from "device.1." + * as a daisychain master? */ + if (!su_info_p + && daisychain_enabled == TRUE + && devices_count > 1 + && daisychain_device_number == 1 + && !strncmp(varname, "device.1.", 9) + ) { + char tmp_buf[SU_INFOSIZE]; + snprintf(tmp_buf, sizeof(tmp_buf), + "device.%s", (varname + 9)); + su_info_p = su_find_info(tmp_buf); + if (su_info_p) { + upsdebugx(2, "%s: finally found " + "as daisy master revert %s", + __func__, tmp_buf); + free(tmp_varname); + tmp_varname = strdup(tmp_buf); + } + } } -*/ } else { + /* is indeed an outlet.* or device.x.outlet.* */ snmp_info_t *tmp_info_p; /* Point the outlet or outlet group number in the string */ const char *item_number_ptr = NULL; From f5e3f3ddc5e6bd4f6a276c3638b3e138d32849a4 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 21 Feb 2022 02:51:37 +0000 Subject: [PATCH 179/700] NUT-Monitor: Port to Python3+PyQt5 --- scripts/python/Makefile.am | 5 +- scripts/python/README | 2 +- scripts/python/app/NUT-Monitor.in | 718 ++++++++-------- scripts/python/app/README | 2 +- scripts/python/app/gui-1.3.glade | 1073 ------------------------ scripts/python/app/gui-1.3.glade.h | 51 -- scripts/python/app/nut-monitor.desktop | 2 +- scripts/python/app/ui/aboutdialog1.ui | 108 +++ scripts/python/app/ui/dialog1.ui | 89 ++ scripts/python/app/ui/dialog2.ui | 98 +++ scripts/python/app/ui/window1.ui | 473 +++++++++++ 11 files changed, 1150 insertions(+), 1471 deletions(-) delete mode 100644 scripts/python/app/gui-1.3.glade delete mode 100644 scripts/python/app/gui-1.3.glade.h create mode 100644 scripts/python/app/ui/aboutdialog1.ui create mode 100644 scripts/python/app/ui/dialog1.ui create mode 100644 scripts/python/app/ui/dialog2.ui create mode 100644 scripts/python/app/ui/window1.ui diff --git a/scripts/python/Makefile.am b/scripts/python/Makefile.am index 8a50571230..b53da4576c 100644 --- a/scripts/python/Makefile.am +++ b/scripts/python/Makefile.am @@ -1,7 +1,10 @@ # Network UPS Tools: data/html EXTRA_DIST = README \ - app/gui-1.3.glade \ + app/ui/aboutdialog1.ui \ + app/ui/dialog1.ui \ + app/ui/dialog2.ui \ + app/ui/window1.ui \ app/NUT-Monitor.in \ app/nut-monitor.appdata.xml \ app/nut-monitor.desktop \ diff --git a/scripts/python/README b/scripts/python/README index 15932d9908..4c6b322b98 100644 --- a/scripts/python/README +++ b/scripts/python/README @@ -27,6 +27,6 @@ PyNUT class, along with its resources. To install it, you will either need to keep the files together, or to install: - NUT-Monitor to /usr/bin, /usr/X11R6/bin/ or something like that, -- gui.glade to /usr/share/nut-monitor/, +- *.ui to /usr/share/nut-monitor/, - nut-monitor.png to something like /usr/share/pixmaps/ - and nut-monitor.desktop to /usr/share/applications diff --git a/scripts/python/app/NUT-Monitor.in b/scripts/python/app/NUT-Monitor.in index 296b88bcaa..c4a648ee30 100755 --- a/scripts/python/app/NUT-Monitor.in +++ b/scripts/python/app/NUT-Monitor.in @@ -26,9 +26,15 @@ # # 2015-02-14 Michal Fincham - Version 1.3.1 # Corrected unsafe permissions on ~/.nut-monitor (Debian #777706) +# +# 2022-02-20 Luke Dashjr - Version 2.0 +# Port to Python 3 with PyQt5. -import gtk, gtk.glade, gobject +import PyQt5.uic +from PyQt5.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * import sys import base64 import os, os.path @@ -43,9 +49,6 @@ import gettext import PyNUT -# Activate threadings on glib -gobject.threads_init() - class interface : DESIRED_FAVORITES_DIRECTORY_MODE = 0o700 @@ -57,7 +60,7 @@ class interface : __favorites_path = "" __fav_menu_items = list() __window_visible = True - __glade_file = None + __ui_file = None __connected = False __ups_handler = None __ups_commands = None @@ -66,7 +69,7 @@ class interface : __gui_thread = None __current_ups = None - def __init__( self ) : + def __init__( self, argv ) : # Before anything, parse command line options if any present... opt_parser = optparse.OptionParser() @@ -76,126 +79,102 @@ class interface : ( cmd_opts, args ) = opt_parser.parse_args() - self.__glade_file = os.path.join( os.path.dirname( sys.argv[0] ), "gui-1.3.glade" ) - - self.__widgets["interface"] = gtk.glade.XML( self.__glade_file, "window1", APP ) - self.__widgets["main_window"] = self.__widgets["interface"].get_widget("window1") - self.__widgets["status_bar"] = self.__widgets["interface"].get_widget("statusbar2") - self.__widgets["ups_host_entry"] = self.__widgets["interface"].get_widget("entry1") - self.__widgets["ups_port_entry"] = self.__widgets["interface"].get_widget("spinbutton1") - self.__widgets["ups_refresh_button"] = self.__widgets["interface"].get_widget("button1") - self.__widgets["ups_authentication_check"] = self.__widgets["interface"].get_widget("checkbutton1") - self.__widgets["ups_authentication_frame"] = self.__widgets["interface"].get_widget("hbox1") - self.__widgets["ups_authentication_login"] = self.__widgets["interface"].get_widget("entry2") - self.__widgets["ups_authentication_password"] = self.__widgets["interface"].get_widget("entry3") - self.__widgets["ups_list_combo"] = self.__widgets["interface"].get_widget("combobox1") - self.__widgets["ups_commands_button"] = self.__widgets["interface"].get_widget("button8") - self.__widgets["ups_connect"] = self.__widgets["interface"].get_widget("button2") - self.__widgets["ups_disconnect"] = self.__widgets["interface"].get_widget("button7") - self.__widgets["ups_params_box"] = self.__widgets["interface"].get_widget("vbox6") - self.__widgets["ups_infos"] = self.__widgets["interface"].get_widget("notebook1") - self.__widgets["ups_vars_tree"] = self.__widgets["interface"].get_widget("treeview1") - self.__widgets["ups_vars_refresh"] = self.__widgets["interface"].get_widget("button9") - self.__widgets["ups_status_image"] = self.__widgets["interface"].get_widget("image1") - self.__widgets["ups_status_left"] = self.__widgets["interface"].get_widget("label10") - self.__widgets["ups_status_right"] = self.__widgets["interface"].get_widget("label11") - self.__widgets["ups_status_time"] = self.__widgets["interface"].get_widget("label15") - self.__widgets["menu_favorites_root"] = self.__widgets["interface"].get_widget("menuitem3") - self.__widgets["menu_favorites"] = self.__widgets["interface"].get_widget("menu2") - self.__widgets["menu_favorites_add"] = self.__widgets["interface"].get_widget("menuitem4") - self.__widgets["menu_favorites_del"] = self.__widgets["interface"].get_widget("menuitem5") - self.__widgets["progress_battery_charge"] = self.__widgets["interface"].get_widget("progressbar1") - self.__widgets["progress_battery_load"] = self.__widgets["interface"].get_widget("progressbar2") + self.__app = QApplication( argv ) + + self.__ui_file = self.__find_res_file( 'ui', "window1.ui" ) + + self.__widgets["interface"] = PyQt5.uic.loadUi( self.__ui_file ) + self.__widgets["main_window"] = self.__widgets["interface"] + self.__widgets["status_bar"] = self.__widgets["interface"].statusbar2 + self.__widgets["ups_host_entry"] = self.__widgets["interface"].entry1 + self.__widgets["ups_port_entry"] = self.__widgets["interface"].spinbutton1 + self.__widgets["ups_refresh_button"] = self.__widgets["interface"].button1 + self.__widgets["ups_authentication_check"] = self.__widgets["interface"].checkbutton1 + self.__widgets["ups_authentication_frame"] = self.__widgets["interface"].hbox1 + self.__widgets["ups_authentication_login"] = self.__widgets["interface"].entry2 + self.__widgets["ups_authentication_password"] = self.__widgets["interface"].entry3 + self.__widgets["ups_list_combo"] = self.__widgets["interface"].combobox1 + self.__widgets["ups_commands_combo"] = self.__widgets["interface"].ups_commands_combo + self.__widgets["ups_commands_button"] = self.__widgets["interface"].button8 + self.__widgets["ups_connect"] = self.__widgets["interface"].button2 + self.__widgets["ups_disconnect"] = self.__widgets["interface"].button7 + self.__widgets["ups_params_box"] = self.__widgets["interface"].vbox6 + self.__widgets["ups_infos"] = self.__widgets["interface"].notebook1 + self.__widgets["ups_vars_tree"] = self.__widgets["interface"].treeview1 + self.__widgets["ups_vars_refresh"] = self.__widgets["interface"].button9 + self.__widgets["ups_status_image"] = self.__widgets["interface"].image1 + self.__widgets["ups_status_left"] = self.__widgets["interface"].label10 + self.__widgets["ups_status_right"] = self.__widgets["interface"].label11 + self.__widgets["ups_status_time"] = self.__widgets["interface"].label15 + self.__widgets["menu_favorites_root"] = self.__widgets["interface"].menu2 + self.__widgets["menu_favorites"] = self.__widgets["interface"].menu2 + self.__widgets["menu_favorites_add"] = self.__widgets["interface"].menuitem4 + self.__widgets["menu_favorites_del"] = self.__widgets["interface"].menuitem5 + self.__widgets["progress_battery_charge"] = self.__widgets["interface"].progressbar1 + self.__widgets["progress_battery_load"] = self.__widgets["interface"].progressbar2 # Create the tray icon and connect it to the show/hide method... - self.__widgets["status_icon"] = gtk.StatusIcon() - self.__widgets["status_icon"].set_from_file( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "on_line.png" ) ) - self.__widgets["status_icon"].set_visible( True ) - self.__widgets["status_icon"].connect( "activate", self.tray_activated ) - - self.__widgets["ups_status_image"].set_from_file( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "on_line.png" ) ) - - # Define interface callbacks actions - self.__callbacks = { "on_window1_destroy" : self.quit, - "on_imagemenuitem1_activate" : self.gui_about_dialog, - "on_imagemenuitem5_activate" : self.quit, - "on_entry1_changed" : self.__check_gui_fields, - "on_entry2_changed" : self.__check_gui_fields, - "on_entry3_changed" : self.__check_gui_fields, - "on_checkbutton1_toggled" : self.__check_gui_fields, - "on_spinbutton1_value_changed" : self.__check_gui_fields, - "on_button1_clicked" : self.__update_ups_list, - "on_button2_clicked" : self.connect_to_ups, - "on_button7_clicked" : self.disconnect_from_ups, - "on_button9_clicked" : self.__gui_update_ups_vars_view, - "on_menuitem4_activate" : self.__gui_add_favorite, - "on_menuitem5_activate" : self.__gui_delete_favorite, - "on_treeview1_button_press_event" : self.__gui_ups_vars_selected - } - - # Connect the callbacks - self.__widgets["interface"].signal_autoconnect( self.__callbacks ) + self.__widgets["status_icon"] = QSystemTrayIcon( QIcon( self.__find_res_file( "pixmaps", "on_line.png" ) ) ) + self.__widgets["status_icon"].setVisible( True ) + self.__widgets["status_icon"].activated.connect( self.tray_activated ) + + self.__widgets["ups_status_image"].setPixmap( QPixmap( self.__find_res_file( "pixmaps", "on_line.png" ) ) ) + + # Connect interface callbacks actions + self.__widgets["main_window"].destroyed.connect( self.quit ) + self.__widgets["interface"].imagemenuitem1.triggered.connect( self.gui_about_dialog ) + self.__widgets["interface"].imagemenuitem5.triggered.connect( self.quit ) + self.__widgets["ups_host_entry"].textChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_authentication_login"].textChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_authentication_password"].textChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_authentication_check"].stateChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_port_entry"].valueChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_refresh_button"].clicked.connect( self.__update_ups_list ) + self.__widgets["ups_connect"].clicked.connect( self.connect_to_ups ) + self.__widgets["ups_disconnect"].clicked.connect( self.disconnect_from_ups ) + self.__widgets["ups_vars_refresh"].clicked.connect( self.__gui_update_ups_vars_view ) + self.__widgets["menu_favorites_add"].triggered.connect( self.__gui_add_favorite ) + self.__widgets["menu_favorites_del"].triggered.connect( self.__gui_delete_favorite ) + self.__widgets["ups_vars_tree"].doubleClicked.connect( self.__gui_ups_vars_selected ) # Remove the dummy combobox entry on UPS List and Commands - self.__widgets["ups_list_combo"].remove_text( 0 ) + self.__widgets["ups_list_combo"].removeItem( 0 ) # Set UPS vars treeview properties ----------------------------- - store = gtk.ListStore( gtk.gdk.Pixbuf, gobject.TYPE_STRING, gobject.TYPE_STRING ) - self.__widgets["ups_vars_tree"].set_model( store ) - self.__widgets["ups_vars_tree"].set_headers_visible( True ) + store = QStandardItemModel( 0, 3, self.__widgets["ups_vars_tree"] ) + self.__widgets["ups_vars_tree"].setModel( store ) + self.__widgets["ups_vars_tree"].setHeaderHidden( False ) + self.__widgets["ups_vars_tree"].setRootIsDecorated( False ) # Column 0 - cr = gtk.CellRendererPixbuf() - column = gtk.TreeViewColumn( '', cr ) - column.add_attribute( cr, 'pixbuf', 0 ) - self.__widgets["ups_vars_tree"].append_column( column ) + store.setHeaderData( 0, Qt.Horizontal, '' ) # Column 1 - cr = gtk.CellRendererText() - cr.set_property( 'editable', False ) - column = gtk.TreeViewColumn( _('Var name'), cr ) - column.set_sort_column_id( 1 ) - column.add_attribute( cr, 'text', 1 ) - self.__widgets["ups_vars_tree"].append_column( column ) + store.setHeaderData( 1, Qt.Horizontal, _('Var name') ) # Column 2 - cr = gtk.CellRendererText() - cr.set_property( 'editable', False ) - column = gtk.TreeViewColumn( _('Value'), cr ) - column.add_attribute( cr, 'text', 2 ) - self.__widgets["ups_vars_tree"].append_column( column ) + store.setHeaderData( 2, Qt.Horizontal, _('Value') ) + self.__widgets["ups_vars_tree"].header().setStretchLastSection( True ) - self.__widgets["ups_vars_tree"].get_model().set_sort_column_id( 1, gtk.SORT_ASCENDING ) + self.__widgets["ups_vars_tree"].sortByColumn( 1, Qt.AscendingOrder ) self.__widgets["ups_vars_tree_store"] = store - self.__widgets["ups_vars_tree"].set_size_request( -1, 50 ) + self.__widgets["ups_vars_tree"].setMinimumSize( 0, 50 ) #--------------------------------------------------------------- # UPS Commands combo box creation ------------------------------ - container = self.__widgets["ups_commands_button"].get_parent() - self.__widgets["ups_commands_button"].destroy() - self.__widgets["ups_commands_combo"] = gtk.ComboBox() + ups_commands_height = self.__widgets["ups_commands_combo"].size().height() * 2 + self.__widgets["ups_commands_combo"].setMinimumSize(0, ups_commands_height) + self.__widgets["ups_commands_combo"].setCurrentIndex( 0 ) - list_store = gtk.ListStore( gobject.TYPE_STRING ) + self.__widgets["ups_commands_button"].setMinimumSize(0, ups_commands_height) + self.__widgets["ups_commands_button"].clicked.connect( self.__gui_send_ups_command ) - self.__widgets["ups_commands_combo"].set_model( list_store ) - cell_renderer = gtk.CellRendererText() - cell_renderer.set_property( "xalign", 0 ) - self.__widgets["ups_commands_combo"].pack_start( cell_renderer, True ) - self.__widgets["ups_commands_combo"].add_attribute( cell_renderer, "markup", 0 ) - - container.pack_start( self.__widgets["ups_commands_combo"], True ) - self.__widgets["ups_commands_combo"].set_active( 0 ) - self.__widgets["ups_commands_combo"].show_all() - - self.__widgets["ups_commands_button"] = gtk.Button( stock=gtk.STOCK_EXECUTE ) - container.pack_start( self.__widgets["ups_commands_button"], True ) - self.__widgets["ups_commands_button"].show() - self.__widgets["ups_commands_button"].connect( "clicked", self.__gui_send_ups_command ) - - self.__widgets["ups_commands_combo_store"] = list_store + self.__widgets["ups_commands_combo_store"] = self.__widgets["ups_commands_combo"] #--------------------------------------------------------------- + self.gui_init_unconnected() + if ( cmd_opts.hidden != True ) : self.__widgets["main_window"].show() @@ -216,45 +195,70 @@ class interface : self.connect_to_ups() else : # Try to scan localhost for available ups and connect to it if there is only one - self.__widgets["ups_host_entry"].set_text( "localhost" ) + self.__widgets["ups_host_entry"].setText( "localhost" ) self.__update_ups_list() - if ( len( self.__widgets["ups_list_combo"].get_model() ) == 1 ) : + if self.__widgets["ups_list_combo"].count() == 1: self.connect_to_ups() + def exec( self ) : + self.__app.exec() + + def __find_res_file( self, ftype, filename ) : + filename = os.path.join( ftype, filename ) + # TODO: Skip checking application directory if installed + path = os.path.join( os.path.dirname( sys.argv[0] ), filename ) + if os.path.exists(path): + return path + path = QStandardPaths.locate(QStandardPaths.AppDataLocation, filename) + if os.path.exists(path): + return path + raise RuntimeError("Cannot find %s resource %s" % (ftype, filename)) + + def __find_icon_file( self ) : + filename = 'nut-monitor.png' + # TODO: Skip checking application directory if installed + path = os.path.join( os.path.dirname( sys.argv[0] ), "icons", "256x256", filename ) + if os.path.exists(path): + return path + path = QStandardPaths.locate(QStandardPaths.AppDataLocation, os.path.join( "icons", "hicolor", "256x256", "apps", filename ) ) + if os.path.exists(path): + return path + raise RuntimeError("Cannot find %s resource %s" % ('icon', filename)) + # Check if correct fields are filled to enable connection to the UPS def __check_gui_fields( self, widget=None ) : # If UPS list contains something, clear it - if self.__widgets["ups_list_combo"].get_active() != -1 : - self.__widgets["ups_list_combo"].get_model().clear() - self.__widgets["ups_connect"].set_sensitive( False ) - self.__widgets["menu_favorites_add"].set_sensitive( False ) + if self.__widgets["ups_list_combo"].currentIndex() != -1 : + self.__widgets["ups_list_combo"].clear() + self.__widgets["ups_connect"].setEnabled( False ) + self.__widgets["menu_favorites_add"].setEnabled( False ) # Host/Port selection - if len( self.__widgets["ups_host_entry"].get_text() ) > 0 : + if len( self.__widgets["ups_host_entry"].text() ) > 0 : sensitive = True # If authentication is selected, check that we have a login and password - if self.__widgets["ups_authentication_check"].get_active() : - if len( self.__widgets["ups_authentication_login"].get_text() ) == 0 : + if self.__widgets["ups_authentication_check"].isChecked() : + if len( self.__widgets["ups_authentication_login"].text() ) == 0 : sensitive = False - if len( self.__widgets["ups_authentication_password"].get_text() ) == 0 : + if len( self.__widgets["ups_authentication_password"].text() ) == 0 : sensitive = False - self.__widgets["ups_refresh_button"].set_sensitive( sensitive ) + self.__widgets["ups_refresh_button"].setEnabled( sensitive ) if not sensitive : - self.__widgets["ups_connect"].set_sensitive( False ) - self.__widgets["menu_favorites_add"].set_sensitive( False ) + self.__widgets["ups_connect"].setEnabled( False ) + self.__widgets["menu_favorites_add"].setEnabled( False ) else : - self.__widgets["ups_refresh_button"].set_sensitive( False ) - self.__widgets["ups_connect"].set_sensitive( False ) - self.__widgets["menu_favorites_add"].set_sensitive( False ) + self.__widgets["ups_refresh_button"].setEnabled( False ) + self.__widgets["ups_connect"].setEnabled( False ) + self.__widgets["menu_favorites_add"].setEnabled( False ) # Use authentication fields... - if self.__widgets["ups_authentication_check"].get_active() : - self.__widgets["ups_authentication_frame"].set_sensitive( True ) + if self.__widgets["ups_authentication_check"].isChecked() : + self.__widgets["ups_authentication_frame"].setEnabled( True ) else : - self.__widgets["ups_authentication_frame"].set_sensitive( False ) + self.__widgets["ups_authentication_frame"].setEnabled( False ) self.gui_status_message() @@ -271,41 +275,41 @@ class interface : #------------------------------------------------------------------- # Change the status icon and tray icon def change_status_icon( self, icon="on_line", blink=False ) : - self.__widgets["status_icon"].set_from_file( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "%s.png" % icon ) ) - self.__widgets["ups_status_image"].set_from_file( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "%s.png" % icon ) ) - self.__widgets["status_icon"].set_blinking( blink ) + self.__widgets["status_icon"].setIcon( QIcon( self.__find_res_file( "pixmaps", "%s.png" % icon ) ) ) + self.__widgets["ups_status_image"].setPixmap( QPixmap( self.__find_res_file( "pixmaps", "%s.png" % icon ) ) ) + # TODO self.__widgets["status_icon"].set_blinking( blink ) #------------------------------------------------------------------- # This method connects to the NUT server and retrieve availables UPSes # using connection parameters (host, port, login, pass...) def __update_ups_list( self, widget=None ) : - host = self.__widgets["ups_host_entry"].get_text() - port = int( self.__widgets["ups_port_entry"].get_value() ) + host = self.__widgets["ups_host_entry"].text() + port = int( self.__widgets["ups_port_entry"].value() ) login = None password = None - if self.__widgets["ups_authentication_check"].get_active() : - login = self.__widgets["ups_authentication_login"].get_text() - password = self.__widgets["ups_authentication_password"].get_text() + if self.__widgets["ups_authentication_check"].isChecked() : + login = self.__widgets["ups_authentication_login"].text() + password = self.__widgets["ups_authentication_password"].text() try : nut_handler = PyNUT.PyNUTClient( host=host, port=port, login=login, password=password ) upses = nut_handler.GetUPSList() - ups_list = list(upses.keys()) + ups_list = list(key.decode('ascii') for key in upses.keys()) ups_list.sort() # If UPS list contains something, clear it - self.__widgets["ups_list_combo"].get_model().clear() + self.__widgets["ups_list_combo"].clear() for current in ups_list : - self.__widgets["ups_list_combo"].append_text( current ) + self.__widgets["ups_list_combo"].addItem( current ) - self.__widgets["ups_list_combo"].set_active( 0 ) + self.__widgets["ups_list_combo"].setCurrentIndex( 0 ) - self.__widgets["ups_connect"].set_sensitive( True ) - self.__widgets["menu_favorites_add"].set_sensitive( True ) + self.__widgets["ups_connect"].setEnabled( True ) + self.__widgets["menu_favorites_add"].setEnabled( True ) self.gui_status_message( _("Found {0} devices on {1}").format( len( ups_list ), host ) ) @@ -321,73 +325,70 @@ class interface : self.gui_status_message( _("Disconnecting from device") ) self.disconnect_from_ups() - gtk.main_quit() + self.__app.quit() #------------------------------------------------------------------- # Method called when user wants to add a new favorite entry. It # displays a dialog to enable user to select the name of the favorite def __gui_add_favorite( self, widget=None ) : - dialog_interface = gtk.glade.XML( self.__glade_file, "dialog1" ) - dialog = dialog_interface.get_widget( "dialog1" ) - - self.__widgets["favorites_dialog_button_add"] = dialog_interface.get_widget("button3") + dialog_ui_file = self.__find_res_file( 'ui', "dialog1.ui" ) + dialog = PyQt5.uic.loadUi( dialog_ui_file ) # Define interface callbacks actions - callbacks = { "on_entry4_changed" : self.__gui_add_favorite_check_gui_fields } - dialog_interface.signal_autoconnect( callbacks ) - - self.__widgets["main_window"].set_sensitive( False ) - rc = dialog.run() - if rc == 1 : + def check_entry(val): + if self.__gui_add_favorite_check_gui_fields(val): + dialog.buttonBox.button(QDialogButtonBox.Ok).setEnabled( True ) + else: + dialog.buttonBox.button(QDialogButtonBox.Ok).setEnabled( False ) + dialog.entry4.textChanged.connect( check_entry ) + + self.__widgets["main_window"].setEnabled( False ) + rc = dialog.exec() + if rc == QDialog.Accepted : fav_data = {} - fav_data["host"] = self.__widgets["ups_host_entry"].get_text() - fav_data["port"] = "%d" % self.__widgets["ups_port_entry"].get_value() - fav_data["ups"] = self.__widgets["ups_list_combo"].get_active_text() - fav_data["auth"] = self.__widgets["ups_authentication_check"].get_active() + fav_data["host"] = self.__widgets["ups_host_entry"].text() + fav_data["port"] = "%d" % self.__widgets["ups_port_entry"].value() + fav_data["ups"] = self.__widgets["ups_list_combo"].currentText() + fav_data["auth"] = self.__widgets["ups_authentication_check"].isChecked() if fav_data["auth"] : - fav_data["login"] = self.__widgets["ups_authentication_login"].get_text() - fav_data["password"] = base64.b64encode( self.__widgets["ups_authentication_password"].get_text() ) + fav_data["login"] = self.__widgets["ups_authentication_login"].text() + fav_data["password"] = base64.b64encode( self.__widgets["ups_authentication_password"].text().encode('ascii') ).decode('ascii') - fav_name = dialog_interface.get_widget("entry4").get_text() + fav_name = dialog.entry4.text() self.__favorites[ fav_name ] = fav_data self.__gui_refresh_favorites_menu() # Save all favorites self.__save_favorites() - dialog.destroy() - self.__widgets["main_window"].set_sensitive( True ) + self.__widgets["main_window"].setEnabled( True ) #------------------------------------------------------------------- # Method called when user wants to delete an entry from favorites def __gui_delete_favorite( self, widget=None ) : - - dialog_interface = gtk.glade.XML( self.__glade_file, "dialog2" ) - dialog = dialog_interface.get_widget( "dialog2" ) + dialog_ui_file = self.__find_res_file( 'ui', "dialog2.ui" ) + dialog = PyQt5.uic.loadUi( dialog_ui_file ) # Remove the dummy combobox entry on list - dialog_interface.get_widget("combobox2").remove_text( 0 ) + dialog.combobox2.removeItem( 0 ) favs = list(self.__favorites.keys()) favs.sort() for current in favs : - dialog_interface.get_widget("combobox2").append_text( current ) + dialog.combobox2.addItem( current ) - dialog_interface.get_widget("combobox2").set_active( 0 ) + dialog.combobox2.setCurrentIndex( 0 ) - self.__widgets["main_window"].set_sensitive( False ) - rc = dialog.run() - fav_name = dialog_interface.get_widget("combobox2").get_active_text() - dialog.destroy() - self.__widgets["main_window"].set_sensitive( True ) + self.__widgets["main_window"].setEnabled( False ) + rc = dialog.exec() + fav_name = dialog.combobox2.currentText() + self.__widgets["main_window"].setEnabled( True ) - if ( rc == 1 ) : + if ( rc == QDialog.Accepted ) : # Remove entry, show confirmation dialog - md = gtk.MessageDialog( None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("Are you sure that you want to remove this favorite ?") ) - resp = md.run() - md.destroy() + resp = QMessageBox.question( None, self.__widgets["main_window"].windowTitle(), _("Are you sure that you want to remove this favorite ?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes ) - if ( resp == gtk.RESPONSE_YES ) : + if ( resp == QMessageBox.Yes ) : del self.__favorites[ fav_name ] self.__gui_refresh_favorites_menu() self.__save_favorites() @@ -401,37 +402,35 @@ class interface : # If auth is activated, process it before other fields to avoir weird # reactions with the 'check_gui_fields' function. if ( self.__favorites[fav_name].get("auth", False ) ) : - self.__widgets["ups_authentication_check"].set_active( True ) - self.__widgets["ups_authentication_login"].set_text( self.__favorites[fav_name].get("login","") ) - self.__widgets["ups_authentication_password"].set_text( self.__favorites[fav_name].get("password","") ) + self.__widgets["ups_authentication_check"].setChecked( True ) + self.__widgets["ups_authentication_login"].setText( self.__favorites[fav_name].get("login","") ) + self.__widgets["ups_authentication_password"].setText( self.__favorites[fav_name].get("password","") ) - self.__widgets["ups_host_entry"].set_text( self.__favorites[fav_name].get("host","") ) - self.__widgets["ups_port_entry"].set_value( float(self.__favorites[fav_name].get("port",3493.0)) ) + self.__widgets["ups_host_entry"].setText( self.__favorites[fav_name].get("host","") ) + self.__widgets["ups_port_entry"].setValue( int( self.__favorites[fav_name].get( "port", 3493 ) ) ) # Clear UPS list and add current UPS name - self.__widgets["ups_list_combo"].get_model().clear() + self.__widgets["ups_list_combo"].clear() - self.__widgets["ups_list_combo"].append_text( self.__favorites[fav_name].get("ups","") ) - self.__widgets["ups_list_combo"].set_active( 0 ) + self.__widgets["ups_list_combo"].addItem( self.__favorites[fav_name].get("ups","") ) + self.__widgets["ups_list_combo"].setCurrentIndex( 0 ) # Activate the connect button - self.__widgets["ups_connect"].set_sensitive( True ) + self.__widgets["ups_connect"].setEnabled( True ) self.gui_status_message( _("Loaded '%s'") % fav_name ) #------------------------------------------------------------------- # Send the selected command to the UPS def __gui_send_ups_command( self, widget=None ) : - offset = self.__widgets["ups_commands_combo"].get_active() - cmd = self.__ups_commands[ offset ] + offset = self.__widgets["ups_commands_combo"].currentIndex() + cmd = self.__ups_commands[ offset ].decode('ascii') - md = gtk.MessageDialog( None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("Are you sure that you want to send\n'%s' to the device ?") % cmd ) - self.__widgets["main_window"].set_sensitive( False ) - resp = md.run() - md.destroy() - self.__widgets["main_window"].set_sensitive( True ) + self.__widgets["main_window"].setEnabled( False ) + resp = QMessageBox.question( None, self.__widgets["main_window"].windowTitle(), _("Are you sure that you want to send '%s' to the device ?") % cmd, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes ) + self.__widgets["main_window"].setEnabled( True ) - if ( resp == gtk.RESPONSE_YES ) : + if ( resp == QMessageBox.Yes ) : try : self.__ups_handler.RunUPSCommand( self.__current_ups, cmd ) self.gui_status_message( _("Sent '{0}' command to {1}").format( cmd, self.__current_ups ) ) @@ -442,36 +441,27 @@ class interface : #------------------------------------------------------------------- # Method called when user clicks on the UPS vars treeview. If the user # performs a double click on a RW var, the GUI shows the update var dialog. - def __gui_ups_vars_selected( self, widget, event ) : - # Check if it's a double click... - if ( (event.button == 1) and (event.type == gtk.gdk._2BUTTON_PRESS) ) : - treeselection = self.__widgets["ups_vars_tree"].get_selection() - (model,iter) = treeselection.get_selected() + def __gui_ups_vars_selected( self, index ) : + if True : + model = self.__widgets["ups_vars_tree_store"] try : - ups_var = model.get_value( iter, 1 ) + ups_var = model.data( index.siblingAtColumn(1) ).encode('ascii') if ( ups_var in self.__ups_rw_vars ) : # The selected var is RW, then we can show the update dialog - dialog_interface = gtk.glade.XML( self.__glade_file, "dialog3" ) - dialog = dialog_interface.get_widget( "dialog3" ) - lab = dialog_interface.get_widget( "label9" ) - lab.set_markup( _("Enter a new value for the variable.\n\n{0} = {1} (current value)").format( ups_var, self.__ups_rw_vars.get(ups_var)) ) + cur_val = self.__ups_rw_vars.get(ups_var).decode('ascii') - str = dialog_interface.get_widget( "entry5" ) - str.set_text( self.__ups_rw_vars.get(ups_var) ) + self.__widgets["main_window"].setEnabled( False ) + new_val, rc = QInputDialog.getText( None, self.__widgets["main_window"].windowTitle(), _("Enter a new value for the variable.

{0} = {1} (current value)").format( ups_var, cur_val), QLineEdit.Normal, cur_val ) + self.__widgets["main_window"].setEnabled( True ) - self.__widgets["main_window"].set_sensitive( False ) - rc = dialog.run() - new_val = str.get_text() - dialog.destroy() - self.__widgets["main_window"].set_sensitive( True ) - - if ( rc == 1 ) : + if ( rc ) : try : - self.__ups_handler.SetRWVar( ups=self.__current_ups, var=ups_var, value=new_val ) + self.__ups_handler.SetRWVar( ups=self.__current_ups, var=ups_var.decode('ascii'), value=new_val ) self.gui_status_message( _("Updated variable on %s") % self.__current_ups ) # Change the value on the local dict to update the GUI + new_val = new_val.encode('ascii') self.__ups_vars[ups_var] = new_val self.__ups_rw_vars[ups_var] = new_val self.__gui_update_ups_vars_view() @@ -493,7 +483,7 @@ class interface : # Refresh the content of the favorites menu according to the defined favorites def __gui_refresh_favorites_menu( self ) : for current in self.__fav_menu_items : - current.destroy() + self.__widgets["menu_favorites"].removeAction(current) self.__fav_menu_items = list() @@ -501,29 +491,27 @@ class interface : items.sort() for current in items : - menu_item = gtk.MenuItem( current ) - menu_item.show() + menu_item = QAction( current ) self.__fav_menu_items.append( menu_item ) - self.__widgets["menu_favorites"].append( menu_item ) + self.__widgets["menu_favorites"].addAction( menu_item ) - menu_item.connect_object( "activate", self.__gui_load_favorite, current ) + menu_item.triggered.connect( lambda: self.__gui_load_favorite( current ) ) if len( items ) > 0 : - self.__widgets["menu_favorites_del"].set_sensitive( True ) + self.__widgets["menu_favorites_del"].setEnabled( True ) else : - self.__widgets["menu_favorites_del"].set_sensitive( False ) + self.__widgets["menu_favorites_del"].setEnabled( False ) #------------------------------------------------------------------- # In 'add favorites' dialog, this method compares the content of the # text widget representing the name of the new favorite with existing # ones. If they match, the 'add' button will be set to non sensitive # to avoid creating entries with the same name. - def __gui_add_favorite_check_gui_fields( self, widget=None ) : - fav_name = widget.get_text() + def __gui_add_favorite_check_gui_fields( self, fav_name ) : if ( len( fav_name ) > 0 ) and ( fav_name not in list(self.__favorites.keys()) ) : - self.__widgets["favorites_dialog_button_add"].set_sensitive( True ) + return True else : - self.__widgets["favorites_dialog_button_add"].set_sensitive( False ) + return False #------------------------------------------------------------------- # Load and parse favorites @@ -560,7 +548,7 @@ class interface : fav_data["login"] = conf.get( current, "login" ) try : - fav_data["password"] = base64.decodestring( conf.get( current, "password" ) ) + fav_data["password"] = base64.decodebytes( conf.get( current, "password" ).encode('ascii') ).decode('ascii') except : # If the password is not in base64, let the field empty @@ -582,7 +570,7 @@ class interface : # If path does not exists, try to create it if ( not os.path.exists( self.__favorites_file ) ) : try : - os.makedirs( self.__favorites_path, mode=self.DESIRED_FAVORITES_DIRECTORY_MODE ) + os.makedirs( self.__favorites_path, mode=self.DESIRED_FAVORITES_DIRECTORY_MODE, exist_ok=True ) except : self.gui_status_message( _("Error while creating configuration folder (%s)") % sys.exc_info()[1] ) @@ -590,6 +578,8 @@ class interface : for current in list(self.__favorites.keys()) : save_conf.add_section( current ) for k, v in self.__favorites[ current ].items() : + if isinstance( v, bool ) : + v = str( v ) save_conf.set( current, k, v ) try : @@ -604,67 +594,87 @@ class interface : #------------------------------------------------------------------- # Display the about dialog def gui_about_dialog( self, widget=None ) : - dialog_interface = gtk.glade.XML( self.__glade_file, "aboutdialog1" ) - dialog = dialog_interface.get_widget( "aboutdialog1" ) - - self.__widgets["main_window"].set_sensitive( False ) - dialog.run() - dialog.destroy() - self.__widgets["main_window"].set_sensitive( True ) + self.__widgets["main_window"].adjustSize() + dialog_ui_file = self.__find_res_file( 'ui', "aboutdialog1.ui" ) + + dialog = PyQt5.uic.loadUi( dialog_ui_file ) + dialog.icon.setPixmap( QPixmap( self.__find_icon_file() ) ) + + credits_button = QPushButton( dialog ) + credits_button.setText( _("C&redits") ) + credits_button.setIcon( dialog.style().standardIcon( QStyle.SP_MessageBoxInformation ) ) + credits_button.clicked.connect( self.gui_about_credits ) + + licence_button = QPushButton( dialog ) + licence_button.setText( _("&Licence") ) + licence_button.clicked.connect( self.gui_about_licence ) + + dialog.buttonBox.addButton( credits_button, QDialogButtonBox.HelpRole ) + dialog.buttonBox.addButton( licence_button, QDialogButtonBox.HelpRole ) + + self.__widgets["main_window"].setEnabled( False ) + dialog.exec() + self.__widgets["main_window"].setEnabled( True ) + + def gui_about_credits( self ) : + QMessageBox.about( None, _("Credits"), _(""" +Written by: +David Goncalves + +Translated by: +David Goncalves - Français +Daniele Pezzini - Italiano + """).strip() ) + + def gui_about_licence( self ) : + QMessageBox.about( None, _("Licence"), _(""" +Copyright (C) 2010 David Goncalves + +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 +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + """).strip() ) #------------------------------------------------------------------- # Display a message on the status bar. The message is also set as # tooltip to enable users to see long messages. def gui_status_message( self, msg="" ) : - context_id = self.__widgets["status_bar"].get_context_id("Infos") - self.__widgets["status_bar"].pop( context_id ) - - if ( platform.system() == "Windows" ) : - text = msg.decode("cp1250").encode("utf8") - else : - text = msg + text = msg - message_id = self.__widgets["status_bar"].push( context_id, text.replace("\n", "") ) - self.__widgets["status_bar"].set_tooltip_text( text ) + message_id = self.__widgets["status_bar"].showMessage( text.replace("\n", "") ) + self.__widgets["status_bar"].setToolTip( text ) #------------------------------------------------------------------- - # Display a notification using PyNotify with an optional icon + # Display a notification using QSystemTrayIcon with an optional icon def gui_status_notification( self, message="", icon_file="" ) : - # Try to init pynotify - try : - import pynotify - pynotify.init( "NUT Monitor" ) - - if ( icon_file != "" ) : - icon = "file://%s" % os.path.abspath( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", icon_file ) ) - else : - icon = None - - notif = pynotify.Notification( "NUT Monitor", message, icon ) - notif.show() - - except : - pass + if ( icon_file != "" ) : + icon = QIcon( os.path.abspath( self.__find_res_file( "pixmaps", icon_file ) ) ) + else : + icon = None - #------------------------------------------------------------------- - # Let GTK refresh GUI :) - def refresh_gui( self ) : - while gtk.events_pending() : - gtk.main_iteration( False ) - return( True ) + self.__widgets["status_icon"].showMessage( "NUT Monitor", message, icon ) #------------------------------------------------------------------- # Connect to the selected UPS using parameters (host,port,login,pass) def connect_to_ups( self, widget=None ) : - host = self.__widgets["ups_host_entry"].get_text() - port = int( self.__widgets["ups_port_entry"].get_value() ) + host = self.__widgets["ups_host_entry"].text() + port = int( self.__widgets["ups_port_entry"].value() ) login = None password = None - if self.__widgets["ups_authentication_check"].get_active() : - login = self.__widgets["ups_authentication_login"].get_text() - password = self.__widgets["ups_authentication_password"].get_text() + if self.__widgets["ups_authentication_check"].isChecked() : + login = self.__widgets["ups_authentication_login"].text() + password = self.__widgets["ups_authentication_password"].text() try : self.__ups_handler = PyNUT.PyNUTClient( host=host, port=port, login=login, password=password ) @@ -676,9 +686,9 @@ class interface : # Check if selected UPS exists on server... srv_upses = self.__ups_handler.GetUPSList() - self.__current_ups = self.__widgets["ups_list_combo"].get_active_text() + self.__current_ups = self.__widgets["ups_list_combo"].currentText() - if self.__current_ups not in srv_upses : + if self.__current_ups.encode('ascii') not in srv_upses : self.gui_status_message( _("Device '%s' not found on server") % self.__current_ups ) self.gui_status_notification( _("Device '%s' not found on server") % self.__current_ups, "warning.png" ) return @@ -687,8 +697,8 @@ class interface : self.__widgets["ups_connect"].hide() self.__widgets["ups_disconnect"].show() self.__widgets["ups_infos"].show() - self.__widgets["ups_params_box"].set_sensitive( False ) - self.__widgets["menu_favorites_root"].set_sensitive( False ) + self.__widgets["ups_params_box"].setEnabled( False ) + self.__widgets["menu_favorites_root"].setEnabled( False ) self.__widgets["ups_params_box"].hide() commands = self.__ups_handler.GetUPSCommands( self.__current_ups ) @@ -698,9 +708,10 @@ class interface : # Refresh UPS commands combo box self.__widgets["ups_commands_combo_store"].clear() for desc in self.__ups_commands : - self.__widgets["ups_commands_combo_store"].append( [ "%s\n%s" % ( desc, commands[desc] ) ] ) + # TODO: Style as "%s
%s" + self.__widgets["ups_commands_combo_store"].addItem( "%s\n%s" % ( desc.decode('ascii'), commands[desc].decode('ascii') ) ) - self.__widgets["ups_commands_combo"].set_active( 0 ) + self.__widgets["ups_commands_combo"].setCurrentIndex( 0 ) # Update UPS vars manually before the thread self.__ups_vars = self.__ups_handler.GetUPSVars( self.__current_ups ) @@ -708,7 +719,8 @@ class interface : self.__gui_update_ups_vars_view() # Try to resize the main window... - self.__widgets["main_window"].resize( 1, 1 ) + # FIXME: For some reason, calling this immediately doesn't work right + QTimer.singleShot(10, self.__widgets["main_window"].adjustSize) # Start the GUI updater thread self.__gui_thread = gui_updater( self ) @@ -724,33 +736,43 @@ class interface : vars = self.__ups_vars rwvars = self.__ups_rw_vars - self.__widgets["ups_vars_tree_store"].clear() + self.__widgets["ups_vars_tree_store"].removeRows(0, self.__widgets["ups_vars_tree_store"].rowCount()) for k,v in vars.items() : if ( k in rwvars ) : - icon_file = os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "var-rw.png" ) + icon_file = self.__find_res_file( "pixmaps", "var-rw.png" ) else : - icon_file = os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "var-ro.png" ) - - icon = gtk.gdk.pixbuf_new_from_file( icon_file ) - self.__widgets["ups_vars_tree_store"].append( [ icon, k, v ] ) + icon_file = self.__find_res_file( "pixmaps", "var-ro.png" ) + icon = QIcon( icon_file ) + item_icon = QStandardItem(icon, '') + item_icon.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) + item_var_name = QStandardItem( k.decode('ascii') ) + item_var_name.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) + item_var_val = QStandardItem( v.decode('ascii') ) + item_var_val.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) + self.__widgets["ups_vars_tree_store"].appendRow( (item_icon, item_var_name, item_var_val) ) + self.__widgets["ups_vars_tree"].resizeColumnToContents( 0 ) + self.__widgets["ups_vars_tree"].resizeColumnToContents( 1 ) - #------------------------------------------------------------------- - # Disconnect from the UPS - def disconnect_from_ups( self, widget=None ) : + def gui_init_unconnected( self ) : self.__connected = False self.__widgets["ups_connect"].show() self.__widgets["ups_disconnect"].hide() self.__widgets["ups_infos"].hide() - self.__widgets["ups_params_box"].set_sensitive( True ) - self.__widgets["menu_favorites_root"].set_sensitive( True ) - self.__widgets["status_icon"].set_tooltip_markup( _("Not connected") ) + self.__widgets["ups_params_box"].setEnabled( True ) + self.__widgets["menu_favorites_root"].setEnabled( True ) + self.__widgets["status_icon"].setToolTip( _("Not connected") ) self.__widgets["ups_params_box"].show() # Try to resize the main window... - self.__widgets["main_window"].resize( 1, 1 ) + self.__widgets["main_window"].adjustSize() + + #------------------------------------------------------------------- + # Disconnect from the UPS + def disconnect_from_ups( self, widget=None ) : + self.gui_init_unconnected() # Stop the GUI updater thread self.__gui_thread.stop_thread() @@ -763,7 +785,7 @@ class interface : #----------------------------------------------------------------------- # GUI Updater class # This class updates the main gui with data from connected UPS -class gui_updater( threading.Thread ) : +class gui_updater : __parent_class = None __stop_thread = False @@ -772,23 +794,28 @@ class gui_updater( threading.Thread ) : threading.Thread.__init__( self ) self.__parent_class = parent_class - def run( self ) : + def start( self ) : + self.__timer = QTimer() + self.__timer.timeout.connect(self.__update) + self.__timer.start(1000) + + def __update( self ) : ups = self.__parent_class._interface__current_ups was_online = True # Define a dict containing different UPS status - status_mapper = { "LB" : "%s" % _("Low batteries"), - "RB" : "%s" % _("Replace batteries !"), - "BYPASS" : "Bypass %s" % _("(no battery protection)"), - "CAL" : _("Performing runtime calibration"), - "OFF" : "%s (%s)" % ( _("Offline"), _("not providing power to the load") ), - "OVER" : "%s (%s)" % ( _("Overloaded !"), _("there is too much load for device") ), - "TRIM" : _("Triming (UPS is triming incoming voltage)"), - "BOOST" : _("Boost (UPS is boosting incoming voltage)") + status_mapper = { b"LB" : "%s" % _("Low batteries"), + b"RB" : "%s" % _("Replace batteries !"), + b"BYPASS" : "Bypass %s" % _("(no battery protection)"), + b"CAL" : _("Performing runtime calibration"), + b"OFF" : "%s (%s)" % ( _("Offline"), _("not providing power to the load") ), + b"OVER" : "%s (%s)" % ( _("Overloaded !"), _("there is too much load for device") ), + b"TRIM" : _("Triming (UPS is triming incoming voltage)"), + b"BOOST" : _("Boost (UPS is boosting incoming voltage)") } - while not self.__stop_thread : + if not self.__stop_thread : try : vars = self.__parent_class._interface__ups_handler.GetUPSVars( ups ) self.__parent_class._interface__ups_vars = vars @@ -798,16 +825,16 @@ class gui_updater( threading.Thread ) : text_right = "" status_text = "" - text_left += "%s\n" % _("Device status :") + text_left += "%s
" % _("Device status :") - if ( vars.get("ups.status").find("OL") != -1 ) : - text_right += "%s" % _("Online") + if ( vars.get(b"ups.status").find(b"OL") != -1 ) : + text_right += "%s" % _("Online") if not was_online : self.__parent_class.change_status_icon( "on_line", blink=False ) was_online = True - if ( vars.get("ups.status").find("OB") != -1 ) : - text_right += "%s" % _("On batteries") + if ( vars.get(b"ups.status").find(b"OB") != -1 ) : + text_right += "%s" % _("On batteries") if was_online : self.__parent_class.change_status_icon( "on_battery", blink=True ) self.__parent_class.gui_status_notification( _("Device is running on batteries"), "on_battery.png" ) @@ -815,80 +842,85 @@ class gui_updater( threading.Thread ) : # Check for additionnal information for k,v in status_mapper.items() : - if vars.get("ups.status").find(k) != -1 : + if vars.get(b"ups.status").find(k) != -1 : if ( text_right != "" ) : text_right += " - %s" % v else : text_right += "%s" % v # CHRG and DISCHRG cannot be trated with the previous loop ;) - if ( vars.get("ups.status").find("DISCHRG") != -1 ) : + if ( vars.get(b"ups.status").find(b"DISCHRG") != -1 ) : text_right += " - %s" % _("discharging") - elif ( vars.get("ups.status").find("CHRG") != -1 ) : + elif ( vars.get(b"ups.status").find(b"CHRG") != -1 ) : text_right += " - %s" % _("charging") status_text += text_right - text_right += "\n" + text_right += "
" - if ( "ups.mfr" in vars ) : - text_left += "%s\n\n" % _("Model :") - text_right += "%s\n%s\n" % ( vars.get("ups.mfr",""), vars.get("ups.model","") ) + if ( b"ups.mfr" in vars ) : + text_left += "%s

" % _("Model :") + text_right += "%s
%s
" % ( + vars.get(b"ups.mfr",b"").decode('ascii'), + vars.get(b"ups.model",b"").decode('ascii'), + ) - if ( "ups.temperature" in vars ) : - text_left += "%s\n" % _("Temperature :") - text_right += "%s\n" % int( float( vars.get( "ups.temperature", 0 ) ) ) + if ( b"ups.temperature" in vars ) : + text_left += "%s
" % _("Temperature :") + text_right += "%s
" % int( float( vars.get( b"ups.temperature", 0 ) ) ) - if ( "battery.voltage" in vars ) : - text_left += "%s\n" % _("Battery voltage :") - text_right += "%sv\n" % vars.get( "battery.voltage", 0 ) + if ( b"battery.voltage" in vars ) : + text_left += "%s
" % _("Battery voltage :") + text_right += "%sv
" % (vars.get( b"battery.voltage", 0 ).decode('ascii'),) - self.__parent_class._interface__widgets["ups_status_left"].set_markup( text_left[:-1] ) - self.__parent_class._interface__widgets["ups_status_right"].set_markup( text_right[:-1] ) + self.__parent_class._interface__widgets["ups_status_left"].setText( text_left[:-4] ) + self.__parent_class._interface__widgets["ups_status_right"].setText( text_right[:-4] ) # UPS load and battery charge progress bars - if ( "battery.charge" in vars ) : - charge = vars.get( "battery.charge", "0" ) - self.__parent_class._interface__widgets["progress_battery_charge"].set_fraction( float( charge ) / 100.0 ) - self.__parent_class._interface__widgets["progress_battery_charge"].set_text( "%s %%" % int( float( charge ) ) ) - status_text += "\n%s %s%%" % ( _("Battery charge :"), int( float( charge ) ) ) + self.__parent_class._interface__widgets["progress_battery_charge"].setRange( 0, 100 ) + if ( b"battery.charge" in vars ) : + charge = vars.get( b"battery.charge", "0" ) + self.__parent_class._interface__widgets["progress_battery_charge"].setValue( int( float( charge ) ) ) + self.__parent_class._interface__widgets["progress_battery_charge"].resetFormat() + status_text += "
%s %s%%" % ( _("Battery charge :"), int( float( charge ) ) ) else : - self.__parent_class._interface__widgets["progress_battery_charge"].set_fraction( 0.0 ) - self.__parent_class._interface__widgets["progress_battery_charge"].set_text( _("Not available") ) - - if ( "ups.load" in vars ) : - load = vars.get( "ups.load", "0" ) - self.__parent_class._interface__widgets["progress_battery_load"].set_fraction( float( load ) / 100.0 ) - self.__parent_class._interface__widgets["progress_battery_load"].set_text( "%s %%" % int( float( load ) ) ) - status_text += "\n%s %s%%" % ( _("UPS load :"), int( float( load ) ) ) + self.__parent_class._interface__widgets["progress_battery_charge"].setValue( 0 ) + self.__parent_class._interface__widgets["progress_battery_charge"].setFormat( _("Not available") ) + # FIXME: Some themes don't draw text, so swap it with a QLabel? + + self.__parent_class._interface__widgets["progress_battery_load"].setRange( 0, 100 ) + if ( b"ups.load" in vars ) : + load = vars.get( b"ups.load", "0" ) + self.__parent_class._interface__widgets["progress_battery_load"].setValue( int( float( load ) ) ) + self.__parent_class._interface__widgets["progress_battery_load"].resetFormat() + status_text += "
%s %s%%" % ( _("UPS load :"), int( float( load ) ) ) else : - self.__parent_class._interface__widgets["progress_battery_load"].set_fraction( 0.0 ) - self.__parent_class._interface__widgets["progress_battery_load"].set_text( _("Not available") ) + self.__parent_class._interface__widgets["progress_battery_load"].setValue( 0 ) + self.__parent_class._interface__widgets["progress_battery_load"].setFormat( _("Not available") ) + # FIXME: Some themes don't draw text, so swap it with a QLabel? - if ( "battery.runtime" in vars ) : - autonomy = int( float( vars.get( "battery.runtime", 0 ) ) ) + if ( b"battery.runtime" in vars ) : + autonomy = int( float( vars.get( b"battery.runtime", 0 ) ) ) if ( autonomy >= 3600 ) : info = time.strftime( _("%H hours %M minutes %S seconds"), time.gmtime( autonomy ) ) elif ( autonomy > 300 ) : info = time.strftime( _("%M minutes %S seconds"), time.gmtime( autonomy ) ) else : - info = time.strftime( _("%M minutes %S seconds"), time.gmtime( autonomy ) ) + info = time.strftime( _("%M minutes %S seconds"), time.gmtime( autonomy ) ) else : info = _("Not available") - self.__parent_class._interface__widgets["ups_status_time"].set_markup( info ) + self.__parent_class._interface__widgets["ups_status_time"].setText( info ) # Display UPS status as tooltip for tray icon - self.__parent_class._interface__widgets["status_icon"].set_tooltip_markup( status_text ) + self.__parent_class._interface__widgets["status_icon"].setToolTip( status_text ) except : self.__parent_class.gui_status_message( _("Error from '{0}' ({1})").format( ups, sys.exc_info()[1] ) ) self.__parent_class.gui_status_notification( _("Error from '{0}'\n{1}").format( ups, sys.exc_info()[1] ), "warning.png" ) - time.sleep( 1 ) - def stop_thread( self ) : - self.__stop_thread = True + self.__timer.stop() #----------------------------------------------------------------------- @@ -903,10 +935,10 @@ if __name__ == "__main__" : gettext.textdomain( APP ) _ = gettext.gettext - for module in ( gettext, gtk.glade ) : + for module in ( gettext, ) : module.bindtextdomain( APP, DIR ) module.textdomain( APP ) - gui = interface() - gtk.main() + gui = interface(sys.argv) + gui.exec() diff --git a/scripts/python/app/README b/scripts/python/app/README index b7acd299fa..7bee3a2977 100644 --- a/scripts/python/app/README +++ b/scripts/python/app/README @@ -1,7 +1,7 @@ NUT-Monitor is a graphical application to access and manage UPSes connected to a NUT (Network UPS Tools) server. -This application (written in Python + GTK) uses the python-pynut class +This application (written in Python + Qt) uses the python-pynut class (available at http://www.lestat.st) David Goncalves diff --git a/scripts/python/app/gui-1.3.glade b/scripts/python/app/gui-1.3.glade deleted file mode 100644 index eb42aba3b0..0000000000 --- a/scripts/python/app/gui-1.3.glade +++ /dev/null @@ -1,1073 +0,0 @@ - - - - - - NUT Monitor - center - battery - - - - True - - - True - - - True - _File - True - - - True - - - gtk-about - True - True - True - - - - - - True - - - - - gtk-quit - True - True - True - - - - - - - - - - True - F_avorites - True - - - True - - - gtk-add - True - False - True - True - - - - - - gtk-delete - True - False - True - True - - - - - - True - - - - - - - - - False - 0 - - - - - True - - - True - 0.5 - in - - - True - 2 - - - True - - - True - - - True - 2 - 3 - - - True - 1 - Host / Port : - - - GTK_FILL - - - - - True - 1 - Device : - - - 1 - 2 - GTK_FILL - - - - - True - True - - - - - 1 - 2 - - - - - True - True - 5 - - 5 - 3493 0 65535 1 10 0 - True - - - - 2 - 3 - GTK_FILL - - - - - True - None - - - 1 - 2 - 1 - 2 - - - - - gtk-refresh - True - False - True - True - True - - - - 2 - 3 - 1 - 2 - GTK_FILL - GTK_FILL - - - - - False - False - 0 - - - - - Use authentication - True - True - False - True - - - - False - 1 - - - - - True - False - - - True - 1 - Login / Password : - - - 0 - - - - - True - True - - - - - 1 - - - - - True - True - False - - - - - 2 - - - - - False - 2 - - - - - True - - - False - 4 - 3 - - - - - 0 - - - - - True - - - gtk-connect - True - False - True - True - True - - - - 0 - - - - - gtk-disconnect - True - True - True - - - - 1 - - - - - False - 1 - - - - - - - - - True - NUT Server - True - - - label_item - - - - - False - 0 - - - - - True - True - - - True - 2 - - - True - - - True - gtk-missing-image - 6 - - - False - 0 - - - - - True - 0 - in - - - True - 2 - 2 - 2 - 2 - - - True - - - True - 1 - 0 - 2 - label - right - - - False - 0 - - - - - True - 0 - 0 - 2 - label - - - 1 - - - - - - - - - - label_item - - - - - 1 - - - - - 0 - - - - - True - 4 - 2 - 4 - - - True - 1 - 2 - Battery charge : - True - right - - - GTK_FILL - - - - - True - 1 - 2 - Current load : - True - right - - - 1 - 2 - GTK_FILL - - - - - True - - - 1 - 2 - - - - - True - - - 1 - 2 - 1 - 2 - - - - - True - 1 - 2 - Remaining time : - right - - - 2 - 3 - GTK_FILL - - - - - True - 0 - in - - - True - - - True - N/A - True - - - - - - - - label_item - - - - - 1 - 2 - 2 - 3 - - - - - True - 1 - 2 - Device commands : - True - right - - - 3 - 4 - GTK_FILL - - - - - True - - - - - - gtk-execute - True - True - True - True - - - - False - False - 1 - - - - - 1 - 2 - 3 - 4 - - - - - False - 1 - - - - - - - - - - True - 2 - Device status - - - False - tab - - - - - True - 2 - - - True - 0 - in - - - True - 1 - 1 - 1 - 1 - - - True - True - automatic - automatic - - - True - True - - - - - - - - - - - label_item - - - - - 0 - - - - - gtk-refresh - True - True - True - True - - - - False - 1 - - - - - 1 - - - - - True - Device vars - - - 1 - False - tab - - - - - 1 - - - - - 1 - - - - - True - 2 - - - False - 2 - - - - - - - 5 - True - center-always - normal - - - True - 2 - - - True - 0 - in - - - True - 4 - 4 - 4 - 4 - - - True - - - True - Enter a name for this favorite - -<span color="#808080">You cannot re-use a name from another entry</span> - - True - - - 0 - - - - - True - True - - - - - False - 1 - - - - - - - - - - label_item - - - - - 1 - - - - - True - spread - - - gtk-add - 1 - True - False - True - True - True - - - False - False - 0 - - - - - gtk-cancel - True - True - True - True - - - False - False - 1 - - - - - False - end - 0 - - - - - - - 5 - True - center-always - normal - - - True - 2 - - - True - 0 - in - - - True - 4 - 4 - 4 - 4 - - - True - - - True - -Please select the favorite that you -want to delete from list... - - True - - - 0 - - - - - True - 0 - <None> - - - False - 1 - - - - - - - - - - label_item - - - - - 1 - - - - - True - spread - - - gtk-delete - 1 - True - True - True - True - - - False - False - 0 - - - - - gtk-cancel - True - True - True - True - - - False - False - 1 - - - - - False - end - 0 - - - - - - - 5 - True - center-always - normal - - - True - 2 - - - True - 0 - in - - - True - 4 - 4 - 4 - 4 - - - True - - - True - 4 - - - True - gtk-edit - 6 - - - False - 4 - 0 - - - - - True - Enter a new value for the variable. - - True - - - 2 - 1 - - - - - 0 - - - - - True - True - - - - False - 1 - - - - - - - - - - label_item - - - - - 1 - - - - - True - spread - - - gtk-save - 1 - True - True - True - True - - - False - False - 0 - - - - - gtk-cancel - True - True - True - True - - - False - False - 1 - - - - - False - end - 0 - - - - - - - 5 - True - center-always - normal - NUT-Monitor - 1.3.1 - Copyright (c) 2010 David Goncalves - GUI to manage devices connected a NUT server. - -For more information about NUT (Network UPS Tools) -please visit the author's website. - -http://www.networkupstools.org - - http://www.lestat.st/informatique/projets/nut-monitor-en - http://www.lestat.st - Copyright (C) 2010 David Goncalves <david@lestat.st> - -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 -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - David Goncalves <david@lestat.st> - David Goncalves <david@lestat.st> - Français -Daniele Pezzini <hyouko@gmail.com> - Italiano - - - True - 2 - - - - - - True - spread - - - False - end - 0 - - - - - - diff --git a/scripts/python/app/gui-1.3.glade.h b/scripts/python/app/gui-1.3.glade.h deleted file mode 100644 index 8b9c95f164..0000000000 --- a/scripts/python/app/gui-1.3.glade.h +++ /dev/null @@ -1,51 +0,0 @@ -char *s = N_("NUT Monitor"); -char *s = N_("_File"); -char *s = N_("F_avorites"); -char *s = N_("Host / Port : "); -char *s = N_("Device : "); -char *s = N_("None"); -char *s = N_("Use authentication"); -char *s = N_("Login / Password : "); -char *s = N_(" NUT Server "); -char *s = N_("label"); -char *s = N_("Battery charge :"); -char *s = N_("Current load :"); -char *s = N_("Remaining time :"); -char *s = N_("N/A"); -char *s = N_("Device commands :"); -char *s = N_("Device status"); -char *s = N_("Device vars"); -char *s = N_("Enter a name for this favorite\n" - "\n" - "You cannot re-use a name from another entry\n" - ""); -char *s = N_("\n" - "Please select the favorite that you\n" - "want to delete from list...\n" - ""); -char *s = N_(""); -char *s = N_("Enter a new value for the variable.\n" - ""); -char *s = N_("Copyright (c) 2010 David Goncalves"); -char *s = N_("GUI to manage devices connected a NUT server.\n" - "\n" - "For more information about NUT (Network UPS Tools)\n" - "please visit the author's website.\n" - "\n" - "http://www.networkupstools.org\n" - ""); -char *s = N_("http://www.lestat.st"); -char *s = N_("Copyright (C) 2010 David Goncalves \n" - "\n" - "This program is free software: you can redistribute it and/or modify\n" - "it under the terms of the GNU General Public License as published by\n" - "the Free Software Foundation; either version 3 of the License, or\n" - "(at your option) any later version.\n" - "\n" - "This program is distributed in the hope that it will be useful,\n" - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - "GNU General Public License for more details.\n" - "\n" - "You should have received a copy of the GNU General Public License\n" - "along with this program. If not, see ."); diff --git a/scripts/python/app/nut-monitor.desktop b/scripts/python/app/nut-monitor.desktop index b4ccf398ae..b63b6e7123 100644 --- a/scripts/python/app/nut-monitor.desktop +++ b/scripts/python/app/nut-monitor.desktop @@ -4,7 +4,7 @@ Name[fr]=Moniteur NUT Comment=Network UPS Tools GUI client Comment[fr]=Client graphique pour NUT (Network UPS Tools) Comment[it]=Client grafico per NUT (Network UPS Tools) -Categories=System;Monitor;HardwareSettings;Settings;GTK +Categories=System;Monitor;HardwareSettings;Settings;Qt Exec=NUT-Monitor Icon=nut-monitor Terminal=false diff --git a/scripts/python/app/ui/aboutdialog1.ui b/scripts/python/app/ui/aboutdialog1.ui new file mode 100644 index 0000000000..c6a62148d7 --- /dev/null +++ b/scripts/python/app/ui/aboutdialog1.ui @@ -0,0 +1,108 @@ + + + aboutdialog1 + + + About NUT-Monitor + + + true + + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + <h1>NUT-Monitor 1.3.1</h1> +<p>GUI to manage devices connected a NUT server.</p> +<p>For more information about NUT (Network UPS Tools) please visit the author's website.</p> +<p style="margin-bottom: 1.5em">http://www.networkupstools.org</p> +<p style=" font-size:8pt;">Copyright (c) 2010 David Goncalves</p> +<p><a href="http://www.lestat.st/informatique/projets/nut-monitor-en">http://www.lestat.st</a></p> + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + aboutdialog1 + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + aboutdialog1 + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/scripts/python/app/ui/dialog1.ui b/scripts/python/app/ui/dialog1.ui new file mode 100644 index 0000000000..6cf652b1f7 --- /dev/null +++ b/scripts/python/app/ui/dialog1.ui @@ -0,0 +1,89 @@ + + + dialog1 + + + + 0 + 0 + 297 + 133 + + + + Dialog + + + true + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Enter a name for this favorite<br><br><font color="#808080">You cannot re-use a name from another entry</font> + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + dialog1 + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + dialog1 + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/scripts/python/app/ui/dialog2.ui b/scripts/python/app/ui/dialog2.ui new file mode 100644 index 0000000000..d31542e206 --- /dev/null +++ b/scripts/python/app/ui/dialog2.ui @@ -0,0 +1,98 @@ + + + dialog2 + + + + 0 + 0 + 229 + 116 + + + + Dialog + + + true + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Please select the favorite that you want to delete from list... + + + true + + + + + + + + None + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + dialog2 + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + dialog2 + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/scripts/python/app/ui/window1.ui b/scripts/python/app/ui/window1.ui new file mode 100644 index 0000000000..1950464463 --- /dev/null +++ b/scripts/python/app/ui/window1.ui @@ -0,0 +1,473 @@ + + + window1 + + + + 0 + 0 + 560 + 465 + + + + NUT Monitor + + + + ../../../../../.designer/backup../../../../../.designer/backup + + + + + + + NUT Server + + + Qt::AlignCenter + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + 65535 + + + 3493 + + + + + + + + + + Device : + + + + + + + + None + + + + + + + + Host / Port : + + + + + + + false + + + &Refresh + + + + + + + + + + + + Use authentication + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Login / Password : + + + + + + + + + + QLineEdit::Password + + + + + + + + + + QFrame::HLine + + + QFrame::Sunken + + + + + + + + + + + + false + + + C&onnect + + + + + + + + + + &Disconnect + + + + + + + + + + + + + + + 0 + + + + Device status + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + label + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + label + + + + + + + + + + + + + + Remaining time : + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Battery charge : + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 24 + + + + + + + Device commands : + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 24 + + + + + + + Current load : + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + N/A + + + Qt::AlignCenter + + + + + + + + + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + + + + + + &Execute + + + + + + + + + + + + + + + Device vars + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + &Refresh + + + + + + + + + + + + + + + + + + + 0 + 0 + 560 + 24 + + + + + &File + + + + + + + + F&avorites + + + + + + + + + + + + + + + &About + + + + + + + + &Quit + + + Ctrl+Q + + + + + false + + + + + + &Add + + + + + false + + + + + + &Delete + + + + + + From 4d9a2d889acd85af1682348d2763b824b78b65c8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 22 Feb 2022 11:33:17 +0100 Subject: [PATCH 180/700] scripts/subdriver/gen-snmp-subdriver.sh: generate "standard MIB items" into new MIB mappings Kudos to @aquette for the catch in PR review --- scripts/subdriver/gen-snmp-subdriver.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/subdriver/gen-snmp-subdriver.sh b/scripts/subdriver/gen-snmp-subdriver.sh index 62ea1a55b3..d135e1dff5 100755 --- a/scripts/subdriver/gen-snmp-subdriver.sh +++ b/scripts/subdriver/gen-snmp-subdriver.sh @@ -241,6 +241,13 @@ generate_C() { * { 0, NULL } * }; */ + + /* standard MIB items; if the vendor MIB contains better OIDs for + * this (e.g. with daisy-chain support), consider adding those here + */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, EOF # extract OID string paths, one by one From fc36fe8988cb64e91e1a603045ad3052b70548a7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 12:36:53 +0100 Subject: [PATCH 181/700] drivers/snmp-ups.c: wrap long lines, add comments --- drivers/snmp-ups.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index a3470188f0..55775e9199 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2095,7 +2095,8 @@ static bool_t is_multiple_template(const char *OID_template) * Note: remember to adapt info_type, OID and optionaly dfl */ static snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *new_instance) { - upsdebugx(1, "%s(%s)", __func__, info_template ? info_template->info_type : "n/a"); + upsdebugx(1, "%s(%s)", __func__, + info_template ? info_template->info_type : "n/a"); /* sanity check */ if (info_template == NULL) @@ -2103,6 +2104,8 @@ static snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *ne if (new_instance == NULL) new_instance = (snmp_info_t *)xmalloc(sizeof(snmp_info_t)); + /* TOTHINK: Should there be an "else" to free() + * the fields which we (re-)allocate below? */ new_instance->info_type = (char *)xmalloc(SU_INFOSIZE); if (new_instance->info_type) @@ -2112,8 +2115,10 @@ static snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *ne if (new_instance->OID) memset((char *)new_instance->OID, 0, SU_INFOSIZE); } - else + else { new_instance->OID = NULL; + } + new_instance->info_flags = info_template->info_flags; new_instance->info_len = info_template->info_len; /* FIXME: check if we need to adapt this one... */ @@ -2558,7 +2563,8 @@ bool_t get_and_process_data(int mode, snmp_info_t *su_info_p) { bool_t status = FALSE; - upsdebugx(1, "%s: %s (%s)", __func__, su_info_p->info_type, su_info_p->OID); + upsdebugx(1, "%s: %s (%s)", __func__, + su_info_p->info_type, su_info_p->OID); /* ok, update this element. */ status = su_ups_get(su_info_p); @@ -3084,7 +3090,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) /* adapt info_type */ if (su_info_p->info_type != NULL) { - snprintf((char *)tmp_info_p->info_type, SU_INFOSIZE, "%s", su_info_p->info_type); + snprintf((char *)tmp_info_p->info_type, + SU_INFOSIZE, "%s", + su_info_p->info_type); } else { free_info(tmp_info_p); @@ -3221,7 +3229,8 @@ bool_t su_ups_get(snmp_info_t *su_info_p) } if (su_info_p->info_flags & ST_FLAG_STRING) { - status = nut_snmp_get_str(su_info_p->OID, buf, sizeof(buf), su_info_p->oid2info); + status = nut_snmp_get_str(su_info_p->OID, buf, + sizeof(buf), su_info_p->oid2info); if (status == TRUE) { if (quirk_symmetra_threephase) { if (!strcasecmp(su_info_p->info_type, "input.transfer.low") @@ -3273,8 +3282,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) su_setinfo(su_info_p, buf); upsdebugx(2, "=> value: %s", buf); } - else + else { upsdebugx(2, "=> Failed"); + } free_info(tmp_info_p); return status; From 4d3ce685bfc456148195a2272219d221dae0baae Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 18:48:01 +0000 Subject: [PATCH 182/700] drivers/dstate.c: send_to_all/send_to_one: extend failed-send tracing --- drivers/dstate.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 0e89a76f8d..53b8d1917b 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -213,7 +213,10 @@ static void send_to_all(const char *fmt, ...) ret = write(conn->fd, buf, buflen); if ((ret < 1) || (ret != (ssize_t)buflen)) { - upsdebugx(1, "write %zd bytes to socket %d failed", buflen, conn->fd); + upsdebugx(1, "%s: write %zd bytes to socket %d failed " + "(ret=%zd): %s", + __func__, buflen, conn->fd, ret, strerror(errno)); + upsdebugx(6, "failed write: %s", buf); sock_disconnect(conn); } } @@ -261,7 +264,10 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) ret = write(conn->fd, buf, strlen(buf)); if ((ret < 1) || (ret != (ssize_t)buflen)) { - upsdebugx(1, "write %zd bytes to socket %d failed", buflen, conn->fd); + upsdebugx(1, "%s: write %zd bytes to socket %d failed " + "(ret=%zd): %s", + __func__, buflen, conn->fd, ret, strerror(errno)); + upsdebugx(6, "failed write: %s", buf); sock_disconnect(conn); return 0; /* failed */ } From 3c0b83ed5ef7c14ab0fdf8378cf99d933542e35a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 18:48:29 +0000 Subject: [PATCH 183/700] drivers/dstate.c: send_to_one(): consistently use "buflen" --- drivers/dstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 53b8d1917b..167706f0c5 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -261,7 +261,7 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) if (ret <= INT_MAX) upsdebugx(5, "%s: %.*s", __func__, (int)(ret-1), buf); - ret = write(conn->fd, buf, strlen(buf)); + ret = write(conn->fd, buf, buflen); if ((ret < 1) || (ret != (ssize_t)buflen)) { upsdebugx(1, "%s: write %zd bytes to socket %d failed " From 99f5954173b2c361f2d217da916e838d0432760e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Feb 2022 18:49:58 +0000 Subject: [PATCH 184/700] drivers/snmp-ups.c upsdrv_updateinfo(); server/upsd.c driver_free() mainloop(): trace connection faults and reconnections better --- drivers/snmp-ups.c | 2 ++ server/upsd.c | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 55775e9199..5463530396 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -273,9 +273,11 @@ void upsdrv_updateinfo(void) /* update all dynamic info fields */ if (snmp_ups_walk(SU_WALKMODE_UPDATE)) { + upsdebugx(1, "%s: pollfreq: Data OK", __func__); dstate_dataok(); } else { + upsdebugx(1, "%s: pollfreq: Data STALE", __func__); dstate_datastale(); } diff --git a/server/upsd.c b/server/upsd.c index 4a709aa8c1..ba8e23759f 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -669,6 +669,9 @@ static void driver_free(void) upstype_t *ups, *unext; for (ups = firstups; ups; ups = unext) { + upsdebugx(1, "%s: forgetting UPS [%s] (FD %d)", + __func__, ups->name, ups->sock_fd); + unext = ups->next; if (ups->sock_fd != -1) { @@ -985,7 +988,11 @@ static void mainloop(void) /* see if we need to (re)connect to the socket */ if (ups->sock_fd < 0) { + upsdebugx(1, "%s: UPS [%s] is not currently connected", + __func__, ups->name); ups->sock_fd = sstate_connect(ups); + upsdebugx(1, "%s: UPS [%s] is now connected as FD %d", + __func__, ups->name, ups->sock_fd); continue; } From b1fa7ed2509c26e9cd2ccc0b32aa9bfa443f6b1a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 22 Feb 2022 19:11:04 +0000 Subject: [PATCH 185/700] drivers/snmp-ups.c: snmp_ups_walk(): fix taxonomy of "processing daisy-chain device" debug log --- drivers/snmp-ups.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 5463530396..866560cdc1 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -5,7 +5,7 @@ * Copyright (C) * 2002 - 2014 Arnaud Quette * 2015 - 2021 Eaton (author: Arnaud Quette ) - * 2016 - 2021 Eaton (author: Jim Klimov ) + * 2016 - 2022 Eaton (author: Jim Klimov ) * 2002 - 2006 Dmitry Frolov * J.W. Hoogervorst * Niels Baggesen @@ -2894,16 +2894,23 @@ bool_t snmp_ups_walk(int mode) /* Loop through all mapping entries for the current_device_number */ for (su_info_p = &snmp_info[0]; (su_info_p != NULL && su_info_p->info_type != NULL) ; su_info_p++) { - /* FIXME: + /* NOTE: Effectively below we do this: * switch(current_device_number) { - * case 0: devtype = "daisychain whole" - * case 1: devtype = "daisychain master" - * default: devtype = "daisychain slave" + * case 0: devtype = "daisychain whole" + * case 1: devtype = "daisychain master" + * default: devtype = "daisychain slave" + * } + * with a consideration for directly-addressable + * slave devices (can be seen in chain via master, + * but also queryable alone with an IP connection) */ if (daisychain_enabled == TRUE) { - upsdebugx(1, "%s: processing device %i (%s)", __func__, - current_device_number, - (current_device_number == 1)?"master":"slave"); /* FIXME: daisychain */ + upsdebugx(1, "%s: processing daisy-chain device %i (%s)", + __func__, current_device_number, + (current_device_number == 1) + ? (devices_count > 1 ? "master" : "single") + : (current_device_number > 1 ? "slave" : "whole") + ); } /* Check if we are asked to stop (reactivity++) */ From d91ff4b4a9eb2f94345810e775260ccccc9c039a Mon Sep 17 00:00:00 2001 From: Thanos Chatziathanassiou Date: Tue, 22 Feb 2022 21:46:25 +0200 Subject: [PATCH 186/700] Added socomec_jbus implementation --- docs/man/socomec_jbus.txt | 189 +++++++++++++++ drivers/Makefile.am | 7 +- drivers/socomec_jbus.c | 492 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 687 insertions(+), 1 deletion(-) create mode 100644 docs/man/socomec_jbus.txt create mode 100644 drivers/socomec_jbus.c diff --git a/docs/man/socomec_jbus.txt b/docs/man/socomec_jbus.txt new file mode 100644 index 0000000000..acefa4f3e2 --- /dev/null +++ b/docs/man/socomec_jbus.txt @@ -0,0 +1,189 @@ +Socomec_jbus(8) +================== + +NAME +---- + +socomec_jbus - Driver for Socomec JBUS UPS with +RS-232 serial Modbus connection. + +SYNOPSIS +-------- + +*socomec_jbus* -h + +*socomec_jbus* -a 'DEVICE_NAME' ['OPTIONS'] + +NOTE: This man page only documents the hardware-specific features of the +socomec_jbus driver. For information about the core driver, see +linkman:nutupsdrv[8]. + +SUPPORTED HARDWARE +------------------ + +This driver supports Socomec JBUS series, online (double conversion) +UPS with the following characteristics. + +1. Single phase and 3-phase UPS + +2. Connection: RS-232 + +My netvision died out on me and appears impossible to procure another, so +I set about to talk to the UPS directly. The documentation yielded mild +success for the DIGYS I have handy over here. + +Currently, it has only been tested on the following model. + +* DIGYS 3/3 !%kVA + +In theory, any Socomec JBUS model should work. It should be discovered +as ``Unknown Socomec JBUS'' with a numeric id that I'll need to add it +to the list of supported UPSs. + +Sadly, Socomec document only mentions DELPHYS MX and DELPHYS MX elite and +I have the id of my own DIGYS, so chances are, your model will not be +recognized but will probably mostly work. Please report successful +or unsuccessful results to the bug tracker or the mailing list. +Make sure to include the full model number of your UPS manually +in your report. + +socomec_jbus uses the libmodbus project, for Modbus implementation. + +CABLING +------- + +The UPS has an RS-232 port which should be connected with a NULL-modem +cable to a PC serial port. The UPS, mine at least, has curiously a female +DB9 connector, so if you construct the cable yourself, use a male DB9 on +one end. + +It only uses RX,TX and ground, so do not strain too much with DTR et all + +RS-232 is supported on all operating systems, either via a built-in serial +port on your computer, or by using an external USB-to-RS-232 converter. If +you plan to use an USB-to-RS-232 converter, make sure it's supported by your +operating system. + + +INSTALLATION +------------ + +This driver is not built by default. You can build it by installing libmodbus +(with development packages) and running + + configure --with-serial=yes --with-modbus=yes + +You also need to give proper (R/W) permissions on the local serial device file +to allow the NUT driver run-time user to access it. This may need additional +setup for start-up scripting, udev or upower rules, to apply the rights on every +boot -- especially if your device nodes are tracked by a virtual filesystem. + +For example, a USB-to-serial converter can be identified as `/dev/ttyACM0` +or `/dev/ttyUSB0` on Linux, or `/dev/ttyU0` on FreeBSD (note the capital "U"). +A built-in serial port can be identified as `/dev/ttyS0` on Linux or one of +`/dev/cua*` names on FreeBSD. + +EXTRA ARGUMENTS +--------------- +This driver supports the following optional settings in the +linkman:ups.conf[5] file: + +*offdelay=*'value':: +Time to wait before shutting down the UPS (seconds), acceptable range is +6 seconds (0.1 minutes) to 5940 seconds (99 minutes). Defaults to 60 seconds. +Must be a multiple of 6 seconds. To ensure your system has adequate time +to shut down after a power failure, it's highly recommended to adjust +*offdelay*. + +*rebootdelay=*'value':: +Time to wait before rebooting the UPS (seconds), acceptable range is +6 seconds (0.1 minutes) to 5940 seconds (99 minutes). Defaults to 60 seconds. +Must be a multiple of 6 seconds. This is used by the *shutdown.reboot.graceful* +instant command. If you've adjusted *offdelay*, you should also adjust +*rebootdelay*. + +*ondelay=*'value':: +Time to wait before switching on the UPS (seconds), acceptable range is +60 seconds (1 minutes) to 5940 seconds (99 minutes). Defaults to 60 seconds. +Must be a multiple of 60 seconds (not 6 seconds). You don't need to adjust +this delay unless you have special requirements. + +NOTE: Due to hardware limitation, in this driver, *ondelay* is respected +only when line power is available. If a power failure has occurred, the +UPS and the load is always immediately switched on, as soon (or as late) +as line power is restored. + +INSTANT COMMANDS +---------------- + +This driver does not (yet?) support any commands sent to the UPS, mostly +because I'm too afraid to meddle with the UPS that is currently powering +half of our datacenter. + +VARIABLES +--------- + +This driver does not support writable runtime variables (see linkman:upsrw[8]): +for the same reasons. +Both should be trivial to implement, but since I've already found one or two +inconsistencies in the documentation, I'm witholding from trying them. + +KNOWN ISSUES AND BUGS +--------------------- + +Well, it is an alpha release at best, so no promises. Mostly based on the work of +Yifeng Li on the huawei-ups2000 in that very same source tree. + +Battery status has a non-fatal read failure +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's usually harmless and can be safely ignored. It's only logged for +informative purposes (*LOG_INFO*), not as a warning or error. + +Data stale +~~~~~~~~~~~ + +Under certain circumstances, some registers can return invalid values +and trigger a "data stale" error. Once a data stale error has occurred, +you should see error messages similar to the example below in the system +log. + + socomec_jbus: modbus_read_input_registers(addr:XXXX, count:Z): Illegal data address + upsd: Data for UPS [huawei] is stale - check driver + upsd: UPS [huawei] data is no longer stale + +So far all known problems have been fixed by the author, but an unknown one +cannot be ruled out. If you have encountered "data stale" problems during +normal uses, please file a bug report with full logs attached. + +Before troubleshooting or reporting a problem, it's important to check +your *dmesg* log for USB connect and disconnect events to avoid wasting +time on the NUT driver when the actual problem is USB. For example, if +someone yanks the cable out of the USB port, or if a new USB device is +plugged into a USB host/hub that is struggling to power its ports +(common on single-board computers like Raspberry Pi), or if you have +flaky cabling or EMI noise, the serial converter can get disconnected +from USB, at least briefly. This creates a permanent data stale, the driver +must be restarted (plugging the USB back won't fix it, since the driver +is still using the nonexistent serial device). These USB problems usually +have nothing to do with NUT. If it's the case, you should solve the +underlying USB problem - check the cable, check the converter, try a +powered USB hub, try a full-speed USB isolator, etc. + +AUTHOR +------ +Thanos Chatziathanassiou + +SEE ALSO +-------- +The core driver: +~~~~~~~~~~~~~~~~ +linkman:nutupsdrv[8] + +Internet resources: +~~~~~~~~~~~~~~~~~~~ +The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ + +Socomec JBUS/Modbus Reference Guide: https://www.socomec.com/files/live/sites/systemsite/files/GB-JBUS-MODBUS-for-Delphys-MP-and-Delphys-MX-operating-manual.pdf + +libmodbus home page: http://libmodbus.org diff --git a/drivers/Makefile.am b/drivers/Makefile.am index fc1f2f4e6f..804603ab22 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -50,7 +50,7 @@ SERIAL_USB_DRIVERLIST = \ nutdrv_qx NEONXML_DRIVERLIST = netxml-ups MACOSX_DRIVERLIST = macosx-ups -MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 +MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 socomec_jbus LINUX_I2C_DRIVERLIST = asem pijuice POWERMAN_DRIVERLIST = powerman-pdu IPMI_DRIVERLIST = nut-ipmipsu @@ -281,6 +281,11 @@ generic_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) huawei_ups2000_SOURCES = huawei-ups2000.c huawei_ups2000_LDADD = $(LDADD_DRIVERS_SERIAL) $(LIBMODBUS_LIBS) +# Socomec JBUS driver +# (this is a Modbus driver) +socomec_jbus_SOURCES = socomec_jbus.c +socomec_jbus_LDADD = $(LDADD_DRIVERS_SERIAL) $(LIBMODBUS_LIBS) + # Linux I2C drivers asem_LDADD = $(LDADD_DRIVERS) asem_SOURCES = asem.c diff --git a/drivers/socomec_jbus.c b/drivers/socomec_jbus.c new file mode 100644 index 0000000000..fd1f865310 --- /dev/null +++ b/drivers/socomec_jbus.c @@ -0,0 +1,492 @@ +/* socomec_jbus.c - Driver for Socomec JBUS UPS + * + * Copyright (C) + * 2021 Thanos Chatziathanassiou + * + * Based on documentation found freely on + * https://www.socomec.com/files/live/sites/systemsite/files/GB-JBUS-MODBUS-for-Delphys-MP-and-Delphys-MX-operating-manual.pdf + * but with dubious legal license. The document itself states: + * ``CAUTION : “This is a product for restricted sales distribution to informed partners. + * Installation restrictions or additional measures may be needed to prevent disturbances'' + * YMMV + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include + +#define DRIVER_NAME "Socomec jbus driver" +#define DRIVER_VERSION "0.06" + +#define CHECK_BIT(var,pos) ((var) & (1<<(pos))) +#define MODBUS_SLAVE_ID 1 +#define BATTERY_RUNTIME_CRITICAL 15 + +/* Variables */ +static modbus_t *modbus_ctx = NULL; + +static int mrir(modbus_t * arg_ctx, int addr, int nb, uint16_t * dest); + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Thanos Chatziathanassiou \n", + DRV_BETA, + {NULL} +}; + +void upsdrv_initinfo(void) +{ + upsdebugx(2, "upsdrv_initinfo"); + + uint16_t tab_reg[12]; + int r; + + dstate_setinfo("device.mfr", "socomec jbus"); + dstate_setinfo("device.model", "Socomec Generic"); + + upsdebugx(2, "initial read"); + + /* + this is a neat trick, but not really helpful right now + https://stackoverflow.com/questions/25811662/spliting-an-hex-into-2-hex-values/41733170#41733170 + uint8_t *lowbyte; + uint8_t *hibyte; + */ + + r = mrir(modbus_ctx, 0x1000, 12, tab_reg); + + if (r == -1) { + fatalx(EXIT_FAILURE, "failed to read UPS code from JBUS. r is %d error %s", r, modbus_strerror(errno)); + } + + upsdebugx(2, "read UPS Code %d", tab_reg[0]); + + if (tab_reg[1]) { + upsdebugx(2, "read UPS Power %d (kVA * 10)", tab_reg[1]); + dstate_setinfo("ups.power", "%u", tab_reg[1]*100 ); + } + + /* known Socomec Models */ + switch (tab_reg[0]) { + case 130: + dstate_setinfo("ups.model", "%s", "DIGYS"); + break; + + case 515: + dstate_setinfo("ups.model", "%s", "DELPHYS MX"); + break; + + case 516: + dstate_setinfo("ups.model", "%s", "DELPHYS MX elite"); + break; + + default: + dstate_setinfo("ups.model", "Unknown Socomec JBUS. Send id %u and specify the model", tab_reg[0]); + } + + if (tab_reg[3] && tab_reg[4] && tab_reg[5] && tab_reg[6] && tab_reg[7]) { + dstate_setinfo("ups.serial", "%c%c%c%c%c%c%c%c%c%c", + (tab_reg[3]&0xFF), (tab_reg[3]>>8), + (tab_reg[4]&0xFF), (tab_reg[4]>>8), + (tab_reg[5]&0xFF), (tab_reg[5]>>8), + (tab_reg[6]&0xFF), (tab_reg[6]>>8), + (tab_reg[7]&0xFF), (tab_reg[7]>>8) + ); + } + + /* upsh.instcmd = instcmd; */ + /* upsh.setvar = setvar; */ +} + +void upsdrv_updateinfo(void) +{ + upsdebugx(2, "upsdrv_updateinfo"); + + uint16_t tab_reg[64]; + int r; + + status_init(); + + /* ups configuration */ + r = mrir(modbus_ctx, 0x10E0, 32, tab_reg); + + if (r == -1 || !tab_reg[0]) { + upsdebugx(2, "Did not receive any data from the UPS at 0x10E0 ! Going stale r is %d error %s", r, modbus_strerror(errno)); + dstate_datastale(); + return; + } + + dstate_setinfo("input.voltage", "%u", tab_reg[0]); + dstate_setinfo("output.voltage", "%u", tab_reg[1]); + dstate_setinfo("input.frequency", "%u", tab_reg[2]); + dstate_setinfo("output.frequency", "%u", tab_reg[3]); + + upsdebugx(2, "battery capacity (Ah * 10) %u", tab_reg[8]); + upsdebugx(2, "battery elements %u", tab_reg[9]); + + /* time and date */ + r = mrir(modbus_ctx, 0x1360, 4, tab_reg); + if (r == -1) { + upsdebugx(2, "Did not receive any data from the UPS at 0x1360 ! Ignoring ? r is %d error %s", r, modbus_strerror(errno)); + } + + dstate_setinfo("ups.time", "%02d:%02d:%02d", (tab_reg[1]&0xFF), (tab_reg[0]>>8), (tab_reg[0]&0xFF) ); + dstate_setinfo("ups.date", "%04d/%02d/%02d", (tab_reg[3]+2000), (tab_reg[2]>>8), (tab_reg[1]>>8) ); + + /* ups status */ + r = mrir(modbus_ctx, 0x1020, 6, tab_reg); + + if (r == -1) { + upsdebugx(2, "Did not receive any data from the UPS at 0x1020 ! Ignoring ? r is %d error %s", r, modbus_strerror(errno)); + /* + dstate_datastale(); + return; + */ + } + + if (CHECK_BIT(tab_reg[0], 0)) + upsdebugx(2, "Rectifier Input supply present"); + if (CHECK_BIT(tab_reg[0], 1)) + upsdebugx(2, "Inverter ON "); + if (CHECK_BIT(tab_reg[0], 2)) + upsdebugx(2, "Rectifier ON"); + if (CHECK_BIT(tab_reg[0], 3)) + upsdebugx(2, "Load protected by inverter"); + if (CHECK_BIT(tab_reg[0], 4)) + upsdebugx(2, "Load on automatic bypass"); + if (CHECK_BIT(tab_reg[0], 5)) + upsdebugx(2, "Load on battery"); + if (CHECK_BIT(tab_reg[0], 6)) + upsdebugx(2, "Remote controls disable"); + if (CHECK_BIT(tab_reg[0], 7)) + upsdebugx(2, "Eco-mode ON"); + + if (CHECK_BIT(tab_reg[0], 14)) + upsdebugx(2, "Battery Test failed"); + if (CHECK_BIT(tab_reg[0], 15)) + upsdebugx(2, "Battery near end of backup time"); + if (CHECK_BIT(tab_reg[0], 16)) + upsdebugx(2, "Battery disacharged"); + + if (CHECK_BIT(tab_reg[1], 0)) + upsdebugx(2, "Battery OK"); + if (CHECK_BIT(tab_reg[1], 10)) + upsdebugx(2, "Bypass input supply present"); + if (CHECK_BIT(tab_reg[1], 11)) + upsdebugx(2, "Battery charging"); + if (CHECK_BIT(tab_reg[1], 12)) + upsdebugx(2, "Bypass input frequency out of tolerance"); + + if (CHECK_BIT(tab_reg[2], 0)) + upsdebugx(2, "Unit operating"); + + if (CHECK_BIT(tab_reg[3], 0)) + upsdebugx(2, "Maintenance mode active"); + + if (CHECK_BIT(tab_reg[4], 0)) + upsdebugx(2, "Boost charge ON"); + if (CHECK_BIT(tab_reg[4], 2)) + upsdebugx(2, "Inverter switch closed"); + if (CHECK_BIT(tab_reg[4], 3)) + upsdebugx(2, "Bypass breaker closed"); + if (CHECK_BIT(tab_reg[4], 4)) + upsdebugx(2, "Maintenance bypass breaker closed"); + if (CHECK_BIT(tab_reg[4], 5)) + upsdebugx(2, "Remote maintenance bypass breaker closed"); + if (CHECK_BIT(tab_reg[4], 6)) + upsdebugx(2, "Output breaker closed (Q3)"); + if (CHECK_BIT(tab_reg[4], 9)) + upsdebugx(2, "Unit working"); + if (CHECK_BIT(tab_reg[4], 12)) + upsdebugx(2, "normal mode active"); + + /* alarms */ + r = mrir(modbus_ctx, 0x1040, 4, tab_reg); + + alarm_init(); + + if (r == -1) { + upsdebugx(2, "Did not receive any data from the UPS at 0x1040 ! Ignoring ? r is %d error %s", r, modbus_strerror(errno)); + /* + dstate_datastale(); + return; + */ + } + + if (CHECK_BIT(tab_reg[0], 0)) { + upsdebugx(2, "General Alarm"); + alarm_set("General Alarm present."); + } + if (CHECK_BIT(tab_reg[0], 1)) { + upsdebugx(2, "Battery failure"); + alarm_set("Battery failure."); + } + if (CHECK_BIT(tab_reg[0], 2)) { + upsdebugx(2, "UPS overload"); + alarm_set("Overload fault."); + } + if (CHECK_BIT(tab_reg[0], 4)) { + upsdebugx(2, "Control failure (com, internal supply...)"); + alarm_set("Control failure (com, internal supply...)"); + } + if (CHECK_BIT(tab_reg[0], 5)) { + upsdebugx(2, "Rectifier input supply out of tolerance "); + alarm_set("Rectifier input supply out of tolerance."); + } + if (CHECK_BIT(tab_reg[0], 6)) { + upsdebugx(2, "Bypass input supply out of tolerance "); + alarm_set("Bypass input supply out of tolerance."); + } + if (CHECK_BIT(tab_reg[0], 7)) { + upsdebugx(2, "Over temperature alarm "); + alarm_set("Over temperature fault."); + } + if (CHECK_BIT(tab_reg[0], 8)) { + upsdebugx(2, "Maintenance bypass closed"); + alarm_set("Maintenance bypass closed."); + } + if (CHECK_BIT(tab_reg[0], 10)) { + upsdebugx(2, "Battery charger fault"); + alarm_set("Battery charger fault."); + } + + if (CHECK_BIT(tab_reg[1], 1)) + upsdebugx(2, "Improper condition of use"); + if (CHECK_BIT(tab_reg[1], 2)) + upsdebugx(2, "Inverter stopped for overload (or bypass transfer)"); + if (CHECK_BIT(tab_reg[1], 3)) + upsdebugx(2, "Microprocessor control system"); + if (CHECK_BIT(tab_reg[1], 5)) + upsdebugx(2, "Synchronisation fault (PLL fault)"); + if (CHECK_BIT(tab_reg[1], 6)) + upsdebugx(2, "Rectifier input supply fault"); + if (CHECK_BIT(tab_reg[1], 7)) + upsdebugx(2, "Rectifier preventive alarm"); + if (CHECK_BIT(tab_reg[1], 9)) + upsdebugx(2, "Inverter preventive alarm"); + if (CHECK_BIT(tab_reg[1], 10)) + upsdebugx(2, "Charger general alarm"); + if (CHECK_BIT(tab_reg[1], 13)) + upsdebugx(2, "Bypass preventive alarm"); + if (CHECK_BIT(tab_reg[1], 15)) { + upsdebugx(2, "Imminent STOP"); + alarm_set("Imminent STOP."); + } + + if (CHECK_BIT(tab_reg[2], 12)) { + upsdebugx(2, "Servicing alarm"); + alarm_set("Servicing alarm."); + } + if (CHECK_BIT(tab_reg[2], 15)) + upsdebugx(2, "Battery room alarm"); + + if (CHECK_BIT(tab_reg[3], 0)) { + upsdebugx(2, "Maintenance bypass alarm"); + alarm_set("Maintenance bypass."); + } + if (CHECK_BIT(tab_reg[3], 1)) { + upsdebugx(2, "Battery discharged"); + alarm_set("Battery discharged."); + } + if (CHECK_BIT(tab_reg[3], 3)) + upsdebugx(2, "Synoptic alarm"); + if (CHECK_BIT(tab_reg[3], 4)) { + upsdebugx(2, "Critical Rectifier fault"); + alarm_set("Critical Rectifier fault."); + } + if (CHECK_BIT(tab_reg[3], 6)) { + upsdebugx(2, "Critical Inverter fault"); + alarm_set("Critical Inverter fault."); + } + if (CHECK_BIT(tab_reg[3], 10)) + upsdebugx(2, "ESD activated"); + if (CHECK_BIT(tab_reg[3], 11)) { + upsdebugx(2, "Battery circuit open"); + alarm_set("Battery circuit open."); + } + if (CHECK_BIT(tab_reg[3], 14)) { + upsdebugx(2, "Bypass critical alarm"); + alarm_set("Bypass critical alarm."); + } + + /* measurements */ + r = mrir(modbus_ctx, 0x1060, 48, tab_reg); + + if (r == -1) { + upsdebugx(2, "Did not receive any data from the UPS at 0x1060 ! Ignoring ? r is %d error %s", r, modbus_strerror(errno)); + /* + dstate_datastale(); + return; + */ + } + + if (tab_reg[1] == 0xFFFF && tab_reg[2] == 0xFFFF) { + /* this a 1-phase model */ + dstate_setinfo("input.phases", "1" ); + dstate_setinfo("ups.load", "%u", tab_reg[0] ); + + dstate_setinfo("input.bypass.voltage", "%u", tab_reg[6] ); + + dstate_setinfo("output.voltage", "%u", tab_reg[9] ); + + if (tab_reg[15] != 0xFFFF) + dstate_setinfo("output.current", "%u", tab_reg[15] ); + } + else { + /* this a 3-phase model */ + dstate_setinfo("input.phases", "3" ); + + dstate_setinfo("ups.load", "%u", tab_reg[3] ); + + dstate_setinfo("ups.L1.load", "%u", tab_reg[0] ); + dstate_setinfo("ups.L2.load", "%u", tab_reg[1] ); + dstate_setinfo("ups.L3.load", "%u", tab_reg[2] ); + + dstate_setinfo("input.bypass.L1-N.voltage", "%u", tab_reg[6] ); + dstate_setinfo("input.bypass.L2-N.voltage", "%u", tab_reg[7] ); + dstate_setinfo("input.bypass.L3-N.voltage", "%u", tab_reg[8] ); + + dstate_setinfo("output.L1-N.voltage", "%u", tab_reg[9] ); + dstate_setinfo("output.L2-N.voltage", "%u", tab_reg[10] ); + dstate_setinfo("output.L3-N.voltage", "%u", tab_reg[11] ); + + if (tab_reg[15] != 0xFFFF) + dstate_setinfo("output.L1.current", "%u", tab_reg[15] ); + + if (tab_reg[16] != 0xFFFF) + dstate_setinfo("output.L2.current", "%u", tab_reg[16] ); + + if (tab_reg[17] != 0xFFFF) + dstate_setinfo("output.L3.current", "%u", tab_reg[17] ); + } + + dstate_setinfo("battery.charge", "%u", tab_reg[4] ); + dstate_setinfo("battery.capacity", "%u", (tab_reg[5]/10) ); + dstate_setinfo("battery.voltage", "%.2f", (double) (tab_reg[20]) / 10); + dstate_setinfo("battery.current", "%.2f", (double) (tab_reg[24]) / 10 ); + dstate_setinfo("battery.runtime", "%u", tab_reg[23] ); + + dstate_setinfo("input.bypass.frequency", "%u", (tab_reg[18]/10) ); + dstate_setinfo("output.frequency", "%u", (tab_reg[19]/10) ); + + if (tab_reg[22] != 0xFFFF) { + dstate_setinfo("ambient.1.present", "yes"); + dstate_setinfo("ambient.1.temperature", "%u", tab_reg[22] ); + } + + if (tab_reg[23] == 0xFFFF) { + /* battery.runtime == 0xFFFF means we're on mains */ + status_set("OL"); + } + else if (tab_reg[23] > BATTERY_RUNTIME_CRITICAL) { + /* we still have mora than BATTERY_RUNTIME_CRITICAL min left ? */ + status_set("OB"); + } + else { + status_set("LB"); + } + + /*TODO: + --essential + ups.status TRIM/BOOST/OVER + ups.alarm + + --dangerous + ups.shutdown + shutdown.return + shutdown.stop + shutdown.reboot + shutdown.reboot.graceful + bypass.start + beeper.enable + beeper.disable + */ + + alarm_commit(); + status_commit(); + dstate_dataok(); + + return; +} + +void upsdrv_shutdown(void) + __attribute__((noreturn)); + +void upsdrv_shutdown(void) +{ + fatalx(EXIT_FAILURE, "shutdown not supported"); +} + +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ +} + +void upsdrv_initups(void) +{ + int r; + upsdebugx(2, "upsdrv_initups"); + + modbus_ctx = modbus_new_rtu(device_path, 9600, 'N', 8, 1); + if (modbus_ctx == NULL) + fatalx(EXIT_FAILURE, "Unable to create the libmodbus context"); + + r = modbus_set_slave(modbus_ctx, MODBUS_SLAVE_ID); /* slave ID */ + if (r < 0) { + modbus_free(modbus_ctx); + fatalx(EXIT_FAILURE, "Invalid modbus slave ID %d",MODBUS_SLAVE_ID); + } + + if (modbus_connect(modbus_ctx) == -1) { + modbus_free(modbus_ctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + +} + +void upsdrv_cleanup(void) +{ + if (modbus_ctx != NULL) { + modbus_close(modbus_ctx); + modbus_free(modbus_ctx); + } +} + +/* Modbus Read Input Registers */ +static int mrir(modbus_t * arg_ctx, int addr, int nb, uint16_t * dest) +{ + int r, i; + + /* zero out the thing, because we might have reused it */ + for (i=0; i Date: Wed, 23 Feb 2022 10:59:53 +0000 Subject: [PATCH 187/700] drivers/snmp-ups.c: update comment for current_device_number --- drivers/snmp-ups.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 866560cdc1..b4004d60c6 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -137,7 +137,7 @@ static int quirk_symmetra_threephase = 0; /* Number of device(s): standard is "1", but daisychain means more than 1 */ static long devices_count = 1; -static int current_device_number = 0; /* global var to handle daisychain iterations - changed by loops in snmp_ups_walk() and su_addcmd() */ +static int current_device_number = 0; /* global var to handle daisychain iterations - changed by loops in snmp_ups_walk() and su_addcmd(); may be 0 for addressing certain values across all chain devices via master (1) */ static bool_t daisychain_enabled = FALSE; /* global var to handle daisychain iterations */ static daisychain_info_t **daisychain_info = NULL; From 9d0384d0ecd652a63c2d6cad303f608ef0e2c07d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Feb 2022 12:05:31 +0000 Subject: [PATCH 188/700] drivers/snmp-ups.c: snmp_ups_walk(): only skip "device.0" if in daisy-chain mode --- drivers/snmp-ups.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index b4004d60c6..56eaaa86c5 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2933,7 +2933,7 @@ bool_t snmp_ups_walk(int mode) * then we'd skip it still (unitary device is at current_device_number == 1)... */ /* skip the whole-daisychain for now */ - if (current_device_number == 0) { + if (daisychain_enabled == TRUE && current_device_number == 0) { upsdebugx(1, "Skipping daisychain device.0 for now..."); continue; } From 84a4fae588f764d096e16a0e50260f1ade501fe2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Feb 2022 12:09:57 +0000 Subject: [PATCH 189/700] drivers/snmp-ups.c: update heading comments for daisychain related variables --- drivers/snmp-ups.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 56eaaa86c5..648b70f9b0 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -135,10 +135,23 @@ int g_pwr_battery; int pollfreq; /* polling frequency */ static int quirk_symmetra_threephase = 0; -/* Number of device(s): standard is "1", but daisychain means more than 1 */ +/* Number of device(s): standard is "1", but talking + * to a daisychain (master device) means more than 1 + * (a directly addressable member of a daisy chain + * would be seen as a single-device chain though) + */ static long devices_count = 1; -static int current_device_number = 0; /* global var to handle daisychain iterations - changed by loops in snmp_ups_walk() and su_addcmd(); may be 0 for addressing certain values across all chain devices via master (1) */ -static bool_t daisychain_enabled = FALSE; /* global var to handle daisychain iterations */ +/* global var to handle daisychain iterations - + * changed by loops in snmp_ups_walk() and su_addcmd(); + * may be 0 for addressing certain values/commands + * across all chain devices via master (1); + * also may be 0 for non-daisychained devices + */ +static int current_device_number = 0; +/* global var to handle daisychain iterations - + * made TRUE if we resolved a "device.count" value + */ +static bool_t daisychain_enabled = FALSE; static daisychain_info_t **daisychain_info = NULL; /* pointer to the Snmp2Nut lookup table */ From 6f9cb30c74f1c0654fc3b937d9811bbb123b22bd Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Feb 2022 14:28:04 +0000 Subject: [PATCH 190/700] drivers/snmp-ups.c: snmp_ups_walk(): only log "Skipping daisychain device.0" when in daisy-chain context (skip anyway even for singular devices where we should also walk .1 anyway) --- drivers/snmp-ups.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 648b70f9b0..871f3f8c96 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2946,8 +2946,12 @@ bool_t snmp_ups_walk(int mode) * then we'd skip it still (unitary device is at current_device_number == 1)... */ /* skip the whole-daisychain for now */ - if (daisychain_enabled == TRUE && current_device_number == 0) { - upsdebugx(1, "Skipping daisychain device.0 for now..."); + if (current_device_number == 0) { + /* for a standalone device, we walk <= device_count + * above, so hitting both .0 and .1 queries + */ + if (daisychain_enabled == TRUE) + upsdebugx(1, "Skipping daisychain device.0 for now..."); continue; } From 34b816a04cf1b02063d5913625f52733d0e8fead Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Feb 2022 15:01:01 +0000 Subject: [PATCH 191/700] drivers/snmp-ups.c: snmp_ups_walk(): log walking an "unitary" device if not a daisy-chain, for balance --- drivers/snmp-ups.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 871f3f8c96..d72b5198ec 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2924,6 +2924,9 @@ bool_t snmp_ups_walk(int mode) ? (devices_count > 1 ? "master" : "single") : (current_device_number > 1 ? "slave" : "whole") ); + } else { + upsdebugx(1, "%s: processing unitary device (%i)", + __func__, current_device_number); } /* Check if we are asked to stop (reactivity++) */ From 533a93004bd44c30fb437431a81eedbad7cb86b9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Feb 2022 15:06:35 +0000 Subject: [PATCH 192/700] drivers/snmp-ups.c: snmp_ups_walk(): for "unitary" (non-daisychain member) devices, walk a ".1" device only (should end up querying non-templated OIDs anyway) - avoid walking .0 partially and .1 again for same data --- drivers/snmp-ups.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index d72b5198ec..57de92b26b 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2889,8 +2889,12 @@ bool_t snmp_ups_walk(int mode) * for the whole (#0) virtual device, so it *seems* similar to unitary. */ - for (current_device_number = 0 ; current_device_number <= devices_count ; current_device_number++) + for (current_device_number = (daisychain_enabled == FALSE && devices_count == 1 ? 1 : 0) ; + current_device_number <= devices_count; current_device_number++) { + + upsdebugx(0, "%s: walking device.%d", __func__, current_device_number); + /* reinit the alarm buffer, before */ if (devices_count > 1) device_alarm_init(); @@ -2949,12 +2953,8 @@ bool_t snmp_ups_walk(int mode) * then we'd skip it still (unitary device is at current_device_number == 1)... */ /* skip the whole-daisychain for now */ - if (current_device_number == 0) { - /* for a standalone device, we walk <= device_count - * above, so hitting both .0 and .1 queries - */ - if (daisychain_enabled == TRUE) - upsdebugx(1, "Skipping daisychain device.0 for now..."); + if (current_device_number == 0 && daisychain_enabled == TRUE) { + upsdebugx(1, "Skipping daisychain device.0 for now..."); continue; } From 8a882ca554938577d7990ee973d62edf326173c0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Feb 2022 16:27:10 +0000 Subject: [PATCH 193/700] drivers/snmp-ups.c: update comment for snmp_ups_walk() for "single" device --- drivers/snmp-ups.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 57de92b26b..ca59f742c9 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2920,6 +2920,13 @@ bool_t snmp_ups_walk(int mode) * with a consideration for directly-addressable * slave devices (can be seen in chain via master, * but also queryable alone with an IP connection) + * NOTE: until proven otherwise, "single" may mean + * both (either) a daisy-chain enabled master device + * without further connected "slave" devices, and + * a directly addressable (IP-connected) "slave". + * Possibly also an ePDU etc. that serves a MIB + * which resolves "device.count" with the selected + * subdriver. */ if (daisychain_enabled == TRUE) { upsdebugx(1, "%s: processing daisy-chain device %i (%s)", From dc706f96527533e422a85424d0fc1e72afef236a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Feb 2022 22:35:58 +0000 Subject: [PATCH 194/700] drivers/dstate.c: send_to_all() send_to_one(): clarify "disconnecting" when logging that write failed --- drivers/dstate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 167706f0c5..6c51642a62 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -214,7 +214,7 @@ static void send_to_all(const char *fmt, ...) if ((ret < 1) || (ret != (ssize_t)buflen)) { upsdebugx(1, "%s: write %zd bytes to socket %d failed " - "(ret=%zd): %s", + "(ret=%zd), disconnecting: %s", __func__, buflen, conn->fd, ret, strerror(errno)); upsdebugx(6, "failed write: %s", buf); sock_disconnect(conn); @@ -265,7 +265,7 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) if ((ret < 1) || (ret != (ssize_t)buflen)) { upsdebugx(1, "%s: write %zd bytes to socket %d failed " - "(ret=%zd): %s", + "(ret=%zd), disconnecting: %s", __func__, buflen, conn->fd, ret, strerror(errno)); upsdebugx(6, "failed write: %s", buf); sock_disconnect(conn); From 5f5cb4f43a7811ae2ba8823b5dbe77aea5ab8dde Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Feb 2022 01:05:47 +0000 Subject: [PATCH 195/700] drivers/dstate.c: send_to_one(): try to sleep and resend failed posting; log the faults (and successes) at level 1 --- drivers/dstate.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/dstate.c b/drivers/dstate.c index 6c51642a62..9d872ebac9 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -261,7 +261,19 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) if (ret <= INT_MAX) upsdebugx(5, "%s: %.*s", __func__, (int)(ret-1), buf); +/* + upsdebugx(0, "%s: writing %zd bytes to socket %d: %s", + __func__, buflen, conn->fd, buf); +*/ ret = write(conn->fd, buf, buflen); + if (ret < 0) { + /* Hacky bugfix: throttle down for upsd to read that */ + upsdebugx(1, "%s: had to throttle down to retry " + "writing %zd bytes to socket %d: %s", + __func__, buflen, conn->fd, buf); + usleep(200); + ret = write(conn->fd, buf, buflen); + } if ((ret < 1) || (ret != (ssize_t)buflen)) { upsdebugx(1, "%s: write %zd bytes to socket %d failed " @@ -270,6 +282,10 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) upsdebugx(6, "failed write: %s", buf); sock_disconnect(conn); return 0; /* failed */ + } else { + upsdebugx(1, "%s: write %zd bytes to socket %d succeeded " + "(ret=%zd): %s", + __func__, buflen, conn->fd, ret, buf); } return 1; /* OK */ From 461df7a9f248fed99779980976d7cafb50f6a873 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Feb 2022 09:03:26 +0000 Subject: [PATCH 196/700] drivers/snmp-ups.c: snmp_ups_walk(): log "walking device %d" at level 1 now, and without a dot-number (to avoid confusion) --- drivers/snmp-ups.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index ca59f742c9..1a3c22856d 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2893,7 +2893,8 @@ bool_t snmp_ups_walk(int mode) current_device_number <= devices_count; current_device_number++) { - upsdebugx(0, "%s: walking device.%d", __func__, current_device_number); + upsdebugx(1, "%s: walking device %d", + __func__, current_device_number); /* reinit the alarm buffer, before */ if (devices_count > 1) From 06ac9caf28f38ff7a311c989961800128bc2d803 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Feb 2022 11:58:09 +0000 Subject: [PATCH 197/700] drivers/dstate.c: send_to_one(): log more details if throttling down, and if it helped --- drivers/dstate.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 9d872ebac9..1de84921d0 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -269,10 +269,16 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) if (ret < 0) { /* Hacky bugfix: throttle down for upsd to read that */ upsdebugx(1, "%s: had to throttle down to retry " - "writing %zd bytes to socket %d: %s", - __func__, buflen, conn->fd, buf); + "writing %zd bytes to socket %d " + "(ret=%zd, errno=%d, strerror=%s): %s", + __func__, buflen, conn->fd, + ret, errno, strerror(errno), + buf); usleep(200); ret = write(conn->fd, buf, buflen); + if (ret == (ssize_t)buflen) { + upsdebugx(1, "%s: throttling down helped", __func__); + } } if ((ret < 1) || (ret != (ssize_t)buflen)) { From f58a92186c4b6b05cc26f879c3fbc5113bdf4fc7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Feb 2022 11:58:44 +0000 Subject: [PATCH 198/700] drivers/dstate.c: send_to_one(): log at level 0 if write failed (twice maybe) and so driver is disconnecting --- drivers/dstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 1de84921d0..c840e0623f 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -282,7 +282,7 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) } if ((ret < 1) || (ret != (ssize_t)buflen)) { - upsdebugx(1, "%s: write %zd bytes to socket %d failed " + upsdebugx(0, "%s: write %zd bytes to socket %d failed " "(ret=%zd), disconnecting: %s", __func__, buflen, conn->fd, ret, strerror(errno)); upsdebugx(6, "failed write: %s", buf); From b14fe1d888528c4a115556e39af158b480c799a9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Feb 2022 12:36:35 +0000 Subject: [PATCH 199/700] driver/dstate.c, main.c: extend do_synchronous to have an "auto" mode (now by default) so reconnections would be sync --- conf/ups.conf.sample | 3 ++- docs/man/ups.conf.txt | 12 ++++++++---- drivers/dstate.c | 26 ++++++++++++++++++++++++-- drivers/main.c | 13 ++++++++++--- 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/conf/ups.conf.sample b/conf/ups.conf.sample index 8d9e833367..f85c08cc14 100644 --- a/conf/ups.conf.sample +++ b/conf/ups.conf.sample @@ -110,7 +110,8 @@ maxretry = 3 # The default is 45 seconds. # # synchronous: optional. The driver work by default in asynchronous -# mode (i.e *synchronous=no*). This means that all data +# mode (like *no*) with fallback to synchronous if sending +# fails (i.e *synchronous=auto*). This means that all data # are pushed by the driver on the communication socket to # upsd (Unix socket on Unix, Named pipe on Windows) without # waiting for these data to be actually consumed. With diff --git a/docs/man/ups.conf.txt b/docs/man/ups.conf.txt index 23425b369f..3998d8925e 100644 --- a/docs/man/ups.conf.txt +++ b/docs/man/ups.conf.txt @@ -93,8 +93,9 @@ provided in the respective driver man pages. *synchronous*:: -Optional. The driver work by default in asynchronous mode (i.e -*synchronous=no*). This means that all data are pushed by the driver +Optional. The drivers work by default in asynchronous mode initially +but can fall back to synchronous mode if writes to server socket failed +(i.e *synchronous=auto*). This means that all data are pushed by the driver on the communication socket to upsd (Unix socket on Unix, Named pipe on Windows) without waiting for these data to be actually consumed. With some HW, such as ePDUs, that can produce a lot of data, @@ -108,8 +109,11 @@ By enabling the 'synchronous' flag (value = 'yes'), the driver will wait for data to be consumed by upsd, prior to publishing more. This can be enabled either globally or per driver. + -The default is 'no' (i.e. asynchronous mode) for backward compatibility -of the driver behavior. +The default of 'auto' acts like 'no' (i.e. asynchronous mode) for backward +compatibility of the driver behavior, until communications fail with a +"Resource temporarily unavailable" condition, which happens when the +driver has many data points to send in a burst, and the server can not +handle that quickly enough so the buffer fills up. *user*:: diff --git a/drivers/dstate.c b/drivers/dstate.c index c840e0623f..5b9c95b80f 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -287,6 +287,18 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) __func__, buflen, conn->fd, ret, strerror(errno)); upsdebugx(6, "failed write: %s", buf); sock_disconnect(conn); + + /* TOTHINK: Maybe fallback elsewhere in other cases? */ + if (ret < 0 && errno == EAGAIN && do_synchronous == -1) { + upsdebugx(0, "%s: synchronous mode was 'auto', " + "will try 'on' for next connections", + __func__); + do_synchronous = 1; + } + + dstate_setinfo("driver.parameter.synchronous", "%s", + (do_synchronous==1)?"yes":((do_synchronous==0)?"no":"auto")); + return 0; /* failed */ } else { upsdebugx(1, "%s: write %zd bytes to socket %d succeeded " @@ -315,8 +327,15 @@ static void sock_connect(int sock) return; } - /* enable nonblocking I/O */ - if (!do_synchronous) { + /* enable nonblocking I/O? + * -1 = auto (try async, allow fallback to sync) + * 0 = async + * 1 = sync + */ + if (do_synchronous < 1) { + upsdebugx(0, "%s: enabling asynchronous mode (%s)", + __func__, (do_synchronous<0)?"auto":"fixed"); + ret = fcntl(fd, F_GETFL, 0); if (ret < 0) { @@ -333,6 +352,9 @@ static void sock_connect(int sock) return; } } + else { + upsdebugx(0, "%s: keeping default synchronous mode", __func__); + } conn = xcalloc(1, sizeof(*conn)); conn->fd = fd; diff --git a/drivers/main.c b/drivers/main.c index 7581c27bf5..b8bb0308c2 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -43,8 +43,9 @@ int extrafd = -1; /* for ser_open */ int do_lock_port = 1; -/* for dstate->sock_connect, default to asynchronous */ -int do_synchronous = 0; +/* for dstate->sock_connect, default to effectively + * asynchronous (0) with fallback to synchronous (1) */ +int do_synchronous = -1; /* for detecting -a values that don't match anything */ static int upsname_found = 0; @@ -378,6 +379,9 @@ static int main_arg(char *var, char *val) if (!strcmp(var, "synchronous")) { if (!strcmp(val, "yes")) do_synchronous=1; + else + if (!strcmp(val, "auto")) + do_synchronous=-1; else do_synchronous=0; @@ -461,6 +465,9 @@ static void do_global_args(const char *var, const char *val) if (!strcmp(var, "synchronous")) { if (!strcmp(val, "yes")) do_synchronous=1; + else + if (!strcmp(val, "auto")) + do_synchronous=-1; else do_synchronous=0; } @@ -1013,7 +1020,7 @@ int main(int argc, char **argv) /* The synchronous option may have been changed from the default */ dstate_setinfo("driver.parameter.synchronous", "%s", - (do_synchronous==1)?"yes":"no"); + (do_synchronous==1)?"yes":((do_synchronous==0)?"no":"auto")); /* remap the device.* info from ups.* for the transition period */ if (dstate_getinfo("ups.mfr") != NULL) From ba08359a64049746ed4b7bc0926f0bf11bfb5a17 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Feb 2022 12:50:06 +0000 Subject: [PATCH 200/700] drivers/dstate.c: send_to_one()/send_to_all(): make the reconnection WARNING more visible --- drivers/dstate.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index 5b9c95b80f..e7394baef0 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -213,8 +213,8 @@ static void send_to_all(const char *fmt, ...) ret = write(conn->fd, buf, buflen); if ((ret < 1) || (ret != (ssize_t)buflen)) { - upsdebugx(1, "%s: write %zd bytes to socket %d failed " - "(ret=%zd), disconnecting: %s", + upsdebugx(0, "WARNING: %s: write %zd bytes to " + "socket %d failed (ret=%zd), disconnecting: %s", __func__, buflen, conn->fd, ret, strerror(errno)); upsdebugx(6, "failed write: %s", buf); sock_disconnect(conn); @@ -282,8 +282,8 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) } if ((ret < 1) || (ret != (ssize_t)buflen)) { - upsdebugx(0, "%s: write %zd bytes to socket %d failed " - "(ret=%zd), disconnecting: %s", + upsdebugx(0, "WARNING: %s: write %zd bytes to " + "socket %d failed (ret=%zd), disconnecting: %s", __func__, buflen, conn->fd, ret, strerror(errno)); upsdebugx(6, "failed write: %s", buf); sock_disconnect(conn); From dac35c1b911cc3293e4b635088a5ebc4257d2cba Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Feb 2022 12:53:19 +0000 Subject: [PATCH 201/700] drivers/dstate.c: send_to_one()/send_to_all(): make the success-report less verbose (level 6) --- drivers/dstate.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index e7394baef0..fe3bf1ed02 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -218,6 +218,10 @@ static void send_to_all(const char *fmt, ...) __func__, buflen, conn->fd, ret, strerror(errno)); upsdebugx(6, "failed write: %s", buf); sock_disconnect(conn); + } else { + upsdebugx(6, "%s: write %zd bytes to socket %d succeeded " + "(ret=%zd): %s", + __func__, buflen, conn->fd, ret, buf); } } } @@ -301,7 +305,7 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) return 0; /* failed */ } else { - upsdebugx(1, "%s: write %zd bytes to socket %d succeeded " + upsdebugx(6, "%s: write %zd bytes to socket %d succeeded " "(ret=%zd): %s", __func__, buflen, conn->fd, ret, buf); } From 237d183b0f6f5c77bd1b5d1945ed5b1218ba5e7a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Feb 2022 12:56:22 +0000 Subject: [PATCH 202/700] drivers/dstate.c: send_to_all(): port do_synchronous auto=>on fallback from send_to_one() --- drivers/dstate.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/dstate.c b/drivers/dstate.c index fe3bf1ed02..8ef82a6009 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -218,6 +218,17 @@ static void send_to_all(const char *fmt, ...) __func__, buflen, conn->fd, ret, strerror(errno)); upsdebugx(6, "failed write: %s", buf); sock_disconnect(conn); + + /* TOTHINK: Maybe fallback elsewhere in other cases? */ + if (ret < 0 && errno == EAGAIN && do_synchronous == -1) { + upsdebugx(0, "%s: synchronous mode was 'auto', " + "will try 'on' for next connections", + __func__); + do_synchronous = 1; + } + + dstate_setinfo("driver.parameter.synchronous", "%s", + (do_synchronous==1)?"yes":((do_synchronous==0)?"no":"auto")); } else { upsdebugx(6, "%s: write %zd bytes to socket %d succeeded " "(ret=%zd): %s", From f145a65b27de985877d08aa7956461091c4b51f4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Feb 2022 14:06:47 +0000 Subject: [PATCH 203/700] NEWS: Update synchronous=auto for NUT v2.7.5 --- NEWS | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/NEWS b/NEWS index f65d1e01ee..f2c13b0ee6 100644 --- a/NEWS +++ b/NEWS @@ -33,6 +33,16 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: for them to complete initializing. This matters on systems that monitor from dozens to hundreds of devices. + - Drivers support a new value for `synchronous` setting, which is the + new default now: `auto`. Initially after driver start-up this mode + acts as the older default `off`, but would fall back to `on` in case + the driver fails to send reports to `upsd` by overflowing the socket + buffer in async mode -- so the next connections of this driver uptime + would be synchronized (potentially slower, but safer -- blocking on + writes to the data server). This adaptation would primarily impact + and benefit devices with many (hundreds of) data points, such as + ePDUs and daisy chains. [issue #1309, PR #1315] + - Daemons such as upsd, upsmon, upslog, and device drivers previously implied that enabled debugging (or upslog to stdout) means foreground running, otherwise the daemon was always sent to the background. From ead46290d1637084392b94b216fb2b622f3fa7e8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Feb 2022 21:54:24 +0100 Subject: [PATCH 204/700] Update nut.dict Update for Adelsystem CBI --- docs/nut.dict | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/nut.dict b/docs/nut.dict index 8a1212fd0c..7285afaf45 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2864 utf-8 +personal_ws-1.1 en 2868 utf-8 AAS ACFAIL ACFREQ @@ -12,6 +12,7 @@ ADDRCONFIG ADDRINFO ADK ADKK +ADELSYSTEM AEC AEF AEG @@ -153,6 +154,7 @@ ByPass CA's CABAC CAs +CBI CBLimit CCC CCCC @@ -1330,6 +1332,7 @@ addenum addinfo addr addrange +adelsystem adkorte adm admin's @@ -1498,6 +1501,7 @@ byv cablepower calloc cb +cbi cbl cblimit ccache From f17deb4c153c637c291661e1c217ffed8d69195f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 25 Feb 2022 18:12:36 +0100 Subject: [PATCH 205/700] drivers/Makefile.am: drop duplicate line (42ity) --- drivers/Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/Makefile.am b/drivers/Makefile.am index 67650a9f99..78085cf4e9 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -365,7 +365,6 @@ dist_noinst_HEADERS = apc-mib.h apc-iem-mib.h apc-hid.h arduino-hid.h \ nutdrv_qx_voltronic.h nutdrv_qx_voltronic-qs.h nutdrv_qx_voltronic-qs-hex.h nutdrv_qx_zinto.h \ xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ apc-pdu-mib.h ever-hid.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h eaton-pdu-marlin-helpers.h \ - apc-pdu-mib.h ever-hid.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h \ eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h legrand-hid.h \ hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h From 5419eeed099d24a4b9e83200f7aadd7afe1e86b7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 25 Feb 2022 21:06:14 +0100 Subject: [PATCH 206/700] drivers/eaton-pdu-marlin-mib.c: break long lines --- drivers/eaton-pdu-marlin-mib.c | 442 ++++++++++++++++++++++----------- 1 file changed, 300 insertions(+), 142 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 42ac3552c3..3c36de630a 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -852,26 +852,35 @@ static info_lkp_t marlin_device_count_info[] = { static snmp_info_t eaton_marlin_mib[] = { /* standard MIB items */ - { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, - { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, - { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.1.0", + NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.4.0", + NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.6.0", + NULL, SU_FLAG_OK, NULL }, /* Device collection */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.4.%i", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.3.%i", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, /* For daisychain, there is only 1 physical interface! */ - { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", + { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.2.1.2.2.1.6.2", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, /* Daisychained devices support */ @@ -887,23 +896,26 @@ static snmp_info_t eaton_marlin_mib[] = { * Take string "unitsPresent" (ex: "0,3,4,5"), and count the amount * of "," separators+1 using an inline function */ /* FIXME: inline func */ - { "device.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.1.0", + { "device.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.1.0", "0", SU_FLAG_STATIC | SU_FLAG_UNIQUE, &marlin_device_count_info[0] /* devices_count */ }, #endif /* Notes: this older/fallback definition is used to: * - estimate the number of devices, based on the below OID iteration capabilities * - determine the base index of the SNMP OID (ie 0 or 1) */ - { "device.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", + { "device.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", "1", SU_FLAG_STATIC #if WITH_SNMP_LKP_FUN | SU_FLAG_UNIQUE #endif - , NULL /* devices_count */ }, + , NULL /* devices_count */ }, /* UPS collection */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, @@ -918,8 +930,9 @@ static snmp_info_t eaton_marlin_mib[] = { { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.5.%i", "", SU_FLAG_OK, NULL }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /* FIXME: needs a date reformating callback * 2011-8-29,16:27:25.0,+1:0 * Hex-STRING: 07 DB 08 1D 10 0C 36 00 2B 01 00 00 @@ -945,7 +958,8 @@ static snmp_info_t eaton_marlin_mib[] = { */ /* Note: the below gives the number of input, not the number of phase(s)! */ /* inputCount.0; Value (Integer): 1 - { "input.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.20.0", + { "input.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.20.0", NULL, SU_FLAG_STATIC, NULL }, */ /* Note: for daisychain mode, we must handle phase(s) per device, * not as a whole. In case of daisychain, support of the UNIQUE @@ -953,29 +967,38 @@ static snmp_info_t eaton_marlin_mib[] = { * value wins. If a more-preferable OID is not implemented by device, * this is ok - the previous available value remains in place. */ /* inputType.%i.1 = INTEGER: singlePhase (1) */ - { "input.phases", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.2.%i.1", - NULL, SU_FLAG_STATIC, &marlin_input_type_info[0] }, + { "input.phases", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.2.%i.1", + NULL, SU_FLAG_STATIC, + &marlin_input_type_info[0] }, /* Frequency is measured globally */ - { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", + { "input.frequency", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", NULL, 0, NULL }, { "input.frequency.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.4.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_frequency_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_frequency_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.4.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_frequency_alarm_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_frequency_alarm_info[0] }, /* inputCurrentPercentLoad (measured globally) * Current percent load, based on the rated current capacity */ /* FIXME: input.load is mapped on input.L1.load for both single and 3phase !!! */ - { "input.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", + { "input.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L1.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", + { "input.L1.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L2.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.2", + { "input.L2.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L3.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.3", + { "input.L3.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, /* FIXME: @@ -985,15 +1008,18 @@ static snmp_info_t eaton_marlin_mib[] = { * Voltage has to be expressed either phase-phase or phase-neutral * This is depending on OID inputVoltageMeasType * INTEGER {singlePhase (1),phase1toN (2),phase2toN (3),phase3toN (4),phase1to2 (5),phase2to3 (6),phase3to1 (7) - * => RFC input.Lx.voltage.context */ - { "input.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", + * => RFC input.Lx.voltage.context */ + { "input.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", NULL, 0, NULL }, { "input.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, { "input.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.1", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -1011,10 +1037,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L1.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, { "input.L1.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.1", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -1032,10 +1060,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L2.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, { "input.L2.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.2", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -1053,10 +1083,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L3.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, { "input.L3.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.3", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -1079,10 +1111,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, { "input.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.1", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -1103,10 +1137,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L1.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, { "input.L1.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.1", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -1127,10 +1163,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L2.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, { "input.L2.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.2", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -1151,10 +1189,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L3.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, { "input.L3.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.3", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -1168,79 +1208,98 @@ static snmp_info_t eaton_marlin_mib[] = { ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.%i.1.3", NULL, SU_FLAG_NEGINVALID, NULL }, /* Sum of all phases realpower, valid for Shark 1ph/3ph only */ - { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.4.%i.1", + { "input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.4.%i.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, /* Fallback 1: Sum of all phases realpower, valid for Marlin 3ph only */ - { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", + { "input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, /* Fallback 2: Sum of the phase realpower, valid for Marlin 1ph only */ - { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", + { "input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L1.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.1", + { "input.L1.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L2.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", + { "input.L2.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L3.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.3", + { "input.L3.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, /* Sum of all phases apparent power, valid for Shark 1ph/3ph only */ - { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.3.%i.1", + { "input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.3.%i.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, /* Fallback 1: Sum of all phases realpower, valid for Marlin 3ph only */ - { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", + { "input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, /* Fallback 2: Sum of the phase realpower, valid for Marlin 1ph only */ - { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", + { "input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L1.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.1", + { "input.L1.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L2.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", + { "input.L2.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L3.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.3", + { "input.L3.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, /* Input feed: a feed (A or B) is tied to an input, and this * sub-collection describes the properties of an actual cable. */ /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags */ - /* { "input.feed.%i.id", 0, 1, "???.%i.%i", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, */ + /* { "input.feed.%i.id", 0, 1, "???.%i.%i", NULL, SU_FLAG_NEGINVALID, NULL }, */ /* Feed name(s) of the ePDU power input(s), can be set by user (FIXME: rename to .desc?) * inputFeedName.0.1 = Value (OctetString): Feed A */ /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => refreshed from time to time or upon call to setvar */ /* { "input.%i.feed.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, * ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.%i", - * NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL, NULL }, + * NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, */ { "input.feed.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.1", - NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL /*, NULL */ }, + NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, /* Feed color (integer RGB) * inputFeedColor.0.1 = Gauge32: 0 (black) */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ -/* { "input.%i.feed.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.%i", - * NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL, NULL }, +/* { "input.%i.feed.color", 0, 1, + * ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.%i", + * NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, */ - { "input.feed.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.1", - NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL /*, NULL */ }, + { "input.feed.color", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.1", + NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, /* inputPowerCapacity.0.1 = INTEGER: 2300 */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ - { "input.realpower.nominal", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.9.%i.1", - NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL /*, NULL */ }, + { "input.realpower.nominal", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.9.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, /* Ambient collection */ /* EMP001 (legacy) mapping */ /* Note: this is still published, beside from the new daisychained version! */ { "ambient.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.3.%i.1", - NULL, SU_FLAG_OK, &marlin_ambient_presence_info[0] }, + NULL, SU_FLAG_OK, + &marlin_ambient_presence_info[0] }, { "ambient.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_temperature_alarms_info[0] }, - { "ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.4.%i.1", + NULL, SU_FLAG_OK, + &marlin_threshold_temperature_alarms_info[0] }, + { "ambient.temperature", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.1.1.4.%i.1", NULL, SU_FLAG_OK, NULL }, /* Low and high threshold use the respective critical levels */ { "ambient.temperature.low", ST_FLAG_RW, 0.1, @@ -1263,11 +1322,14 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, { "ambient.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.2.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.2.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_humidity_alarms_info[0] }, - { "ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.4.%i.1", + NULL, SU_FLAG_OK, + &marlin_threshold_humidity_alarms_info[0] }, + { "ambient.humidity", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.2.1.4.%i.1", NULL, SU_FLAG_OK, NULL }, /* Low and high threshold use the respective critical levels */ { "ambient.humidity.low", ST_FLAG_RW, 0.1, @@ -1291,40 +1353,67 @@ static snmp_info_t eaton_marlin_mib[] = { /* Dry contacts on TH module */ { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.3.1.4.%i.1", - NULL, SU_FLAG_OK, &marlin_ambient_drycontacts_info[0] }, + NULL, SU_FLAG_OK, + &marlin_ambient_drycontacts_info[0] }, { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.3.1.4.%i.2", - NULL, SU_FLAG_OK, &marlin_ambient_drycontacts_info[0] }, + NULL, SU_FLAG_OK, + &marlin_ambient_drycontacts_info[0] }, /* EMP002 (EATON EMP MIB) mapping, including daisychain support */ /* Warning: indexes start at '1' not '0'! */ /* sensorCount.0 */ - { "ambient.count", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.1.0", "0", SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.count", ST_FLAG_RW, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.1.0", + "0", SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* CommunicationStatus.n */ - { "ambient.%i.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.1.4.1.1.%i", - NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_emp002_ambient_presence_info[0] }, + { "ambient.%i.present", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.1.4.1.1.%i", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_emp002_ambient_presence_info[0] }, /* sensorName.n: OctetString EMPDT1H1C2 @1 */ - { "ambient.%i.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.name", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorManufacturer.n */ - { "ambient.%i.mfr", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.mfr", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorModel.n */ - { "ambient.%i.model", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.model", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorSerialNumber.n */ - { "ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.serial", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorUuid.n */ - { "ambient.%i.id", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.id", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorAddress.n */ - { "ambient.%i.address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.address", 0, 1, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorMonitoredBy.n */ - { "ambient.%i.parent.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.5.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.parent.serial", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.5.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorFirmwareVersion.n */ - { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* temperatureUnit.1 * MUST be before the temperature data reading! */ - { "ambient.%i.temperature.unit", 0, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &eaton_sensor_temperature_unit_info[0] }, + { "ambient.%i.temperature.unit", 0, 1.0, + ".1.3.6.1.4.1.534.6.8.1.2.5.0", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &eaton_sensor_temperature_unit_info[0] }, /* temperatureValue.n.1 */ - { "ambient.%i.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + { "ambient.%i.temperature", 0, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, #if WITH_SNMP_LKP_FUN &eaton_sensor_temperature_read_info[0] #else @@ -1333,73 +1422,122 @@ static snmp_info_t eaton_marlin_mib[] = { }, { "ambient.%i.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_status_info[0] }, + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_temperature_alarms_info[0] }, + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_temperature_alarms_info[0] }, /* FIXME: ambient.n.temperature.{minimum,maximum} */ /* temperatureThresholdLowCritical.n.1 */ - { "ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* temperatureThresholdLowWarning.n.1 */ - { "ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* temperatureThresholdHighWarning.n.1 */ - { "ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* temperatureThresholdHighCritical.n.1 */ - { "ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* humidityValue.n.1 */ - { "ambient.%i.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.humidity", 0, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, { "ambient.%i.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_status_info[0] }, + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_humidity_alarms_info[0] }, + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_humidity_alarms_info[0] }, /* FIXME: consider ambient.n.humidity.{minimum,maximum} */ /* humidityThresholdLowCritical.n.1 */ - { "ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* humidityThresholdLowWarning.n.1 */ - { "ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* humidityThresholdHighWarning.n.1 */ - { "ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* humidityThresholdHighCritical.n.1 */ - { "ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* digitalInputName.n.{1,2} */ - { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, - { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* digitalInputPolarity.n */ - { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_ambient_drycontacts_polarity_info[0] }, - { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_polarity_info[0] }, /* XUPS-MIB::xupsContactState.n */ - { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_ambient_drycontacts_state_info[0] }, - { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_state_info[0] }, /* Outlet collection */ - { "outlet.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.22.%i", + { "outlet.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.22.%i", "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "outlet.id", 0, 1, NULL, + { "outlet.id", 0, 1, + NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, + NULL, + "All outlets", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /* UnitType * used to depict the overall outlets switchability of the unit on G3 and newer ePDU*/ { "outlet.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.10.%i", - "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE, &marlin_unit_switchability_info[0] }, + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE, + &marlin_unit_switchability_info[0] }, /* Ugly hack for older G2 ePDU: check the first outlet to determine unit switchability */ { "outlet.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.1", - "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_TYPE_DAISY_1, &g2_unit_outlet_switchability_info[0] }, + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_TYPE_DAISY_1, + &g2_unit_outlet_switchability_info[0] }, /* The below ones are the same as the input.* equivalent */ /* FIXME: transition period, TO BE REMOVED, moved to input.* */ - { "outlet.frequency", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", + { "outlet.frequency", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", NULL, 0, NULL }, - { "outlet.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", + { "outlet.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", NULL, 0, NULL }, - { "outlet.current", 0, 0.01, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.1", + { "outlet.current", 0, 0.01, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.1", NULL, 0, NULL }, - { "outlet.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", + { "outlet.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", NULL, 0, NULL }, - { "outlet.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", + { "outlet.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", NULL, 0, NULL }, /* outlet template definition @@ -1415,7 +1553,8 @@ static snmp_info_t eaton_marlin_mib[] = { NULL }, { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.2.%i.%i", - NULL, SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, &marlin_outlet_status_info[0] }, + NULL, SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_outlet_status_info[0] }, /* Numeric identifier of the outlet, tied to the whole unit */ /* NOTE: For daisychain devices ATM the last listed value presented by * the SNMP device is kept by the driver - no SU_FLAG_UNIQUE here yet. @@ -1428,7 +1567,8 @@ static snmp_info_t eaton_marlin_mib[] = { */ { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.7.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, + NULL }, /* outletID: Outlet physical name, related to its number in the group * ex: first outlet of the second group (B) is B1 */ { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, @@ -1466,14 +1606,17 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.6", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.3.%i.%i", + { "outlet.%i.current", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.4.1.3.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, { "outlet.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.4.1.4.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_status_info[0] }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_status_info[0] }, { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.4.1.4.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_current_alarms_info[0] }, { "outlet.%i.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.5.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, @@ -1486,16 +1629,20 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.%i.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.8.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.6.5.1.3.%i.%i", + { "outlet.%i.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.6.5.1.3.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.2.%i.%i", + { "outlet.%i.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.3.1.2.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, { "outlet.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.3.1.3.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_status_info[0] }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_status_info[0] }, { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.3.1.3.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_voltage_alarms_info[0] }, { "outlet.%i.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.4.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, @@ -1508,19 +1655,23 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.%i.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.7.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.6.5.1.2.%i.%i", + { "outlet.%i.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.6.5.1.2.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, /* outletControlSwitchable */ { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.9.%i.%i", - "no", SU_OUTLET | SU_FLAG_UNIQUE | SU_TYPE_DAISY_1, &marlin_outlet_switchability_info[0] }, + "no", SU_OUTLET | SU_FLAG_UNIQUE | SU_TYPE_DAISY_1, + &marlin_outlet_switchability_info[0] }, /* FIXME: handle non switchable units (only measurements), which do not expose this OID */ { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", - "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_FLAG_OK | SU_TYPE_DAISY_1, &g2_unit_outlet_switchability_info[0] }, + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_FLAG_OK | SU_TYPE_DAISY_1, + &g2_unit_outlet_switchability_info[0] }, { "outlet.%i.type", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.5.%i.%i", - "unknown", SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, &marlin_outlet_type_info[0] }, + "unknown", SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_outlet_type_info[0] }, /* TODO: handle statistics * outletWh.0.1 @@ -1528,7 +1679,8 @@ static snmp_info_t eaton_marlin_mib[] = { */ /* Outlet groups collection */ - { "outlet.group.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.21.%i", + { "outlet.group.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.21.%i", "0", SU_FLAG_STATIC | SU_TYPE_DAISY_1, NULL }, /* outlet groups template definition * Indexes start from 1, ie outlet.group.1 => .1 */ @@ -1675,20 +1827,23 @@ static snmp_info_t eaton_marlin_mib[] = { * we currently use "0", so instant On | Off | Reboot... */ /* no counterpart found! { "outlet.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, + NULL, SU_TYPE_CMD, NULL }, { "outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, + NULL, SU_TYPE_CMD, NULL }, { "outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, */ + NULL, SU_TYPE_CMD, NULL }, */ /* Delays handling: * 0-n :Time in seconds until the group command is issued * -1:Cancel a pending group-level Off/On/Reboot command */ - { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", + { "outlet.%i.load.off", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", + { "outlet.%i.load.on", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", + { "outlet.%i.load.cycle", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, /* Per-outlet shutdown / startup delay (configuration point, not the timers) @@ -1698,10 +1853,10 @@ static snmp_info_t eaton_marlin_mib[] = { */ { "outlet.%i.delay.shutdown", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.10.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL /*, NULL */ }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, { "outlet.%i.delay.start", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.7.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL /*, NULL */ }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, /* Per-outlet shutdown / startup timers * outletControlOffCmd.0.1 = INTEGER: -1 @@ -1709,17 +1864,20 @@ static snmp_info_t eaton_marlin_mib[] = { */ { "outlet.%i.timer.shutdown", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL /*, NULL */ }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, { "outlet.%i.timer.start", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL /*, NULL */ }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, /* Delayed version, parameter is mandatory (so dfl is NULL)! */ - { "outlet.%i.load.off.delay", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", + { "outlet.%i.load.off.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.on.delay", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", + { "outlet.%i.load.on.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.cycle.delay", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", + { "outlet.%i.load.cycle.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, /* Delays handling: From 76affc10980f182e9fed55158f43a9decffdfdd5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 15:42:00 +0100 Subject: [PATCH 207/700] drivers/eaton-pdu-marlin-mib.c: re-apply fix from "open" to "opened" ambient dry contacts --- drivers/eaton-pdu-marlin-mib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 3c36de630a..fa843bfdc8 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -368,7 +368,7 @@ static info_lkp_t marlin_ambient_drycontacts_info[] = { , NULL, NULL, NULL, NULL #endif }, - { 0, "open" + { 0, "opened" #if WITH_SNMP_LKP_FUN , NULL, NULL, NULL, NULL #endif From 7de296a5f46e107972df316a7ed21c810014cc66 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 15:57:30 +0000 Subject: [PATCH 208/700] Regenerate DMF files for sysConfig/sysLocation/sysDesc fields --- scripts/DMF/dmfsnmp/apc-ats-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/apc-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/baytech-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/bestpower-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/compaq-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/cyberpower-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/delta_ups-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/eaton-ats16-nm2-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/eaton-ats16-nmc-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/eaton-pdu-genesis2-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf | 3 +++ scripts/DMF/dmfsnmp/eaton-pdu-pulizzi-mib.dmf | 7 +++++-- scripts/DMF/dmfsnmp/eaton-pdu-revelation-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/emerson-avocent-pdu-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/hpe-pdu-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/huawei-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/ietf-mib.dmf | 7 +++++-- scripts/DMF/dmfsnmp/mge-mib.dmf | 3 +++ scripts/DMF/dmfsnmp/netvision-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/powerware-mib.dmf | 3 +++ scripts/DMF/dmfsnmp/raritan-pdu-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/raritan-px2-mib.dmf | 5 ++++- scripts/DMF/dmfsnmp/xppc-mib.dmf | 5 ++++- 23 files changed, 91 insertions(+), 22 deletions(-) diff --git a/scripts/DMF/dmfsnmp/apc-ats-mib.dmf b/scripts/DMF/dmfsnmp/apc-ats-mib.dmf index 6cfdcf557a..02920054da 100644 --- a/scripts/DMF/dmfsnmp/apc-ats-mib.dmf +++ b/scripts/DMF/dmfsnmp/apc-ats-mib.dmf @@ -23,6 +23,9 @@ + + + @@ -52,6 +55,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/apc-mib.dmf b/scripts/DMF/dmfsnmp/apc-mib.dmf index 9f6cf29968..a71b037600 100644 --- a/scripts/DMF/dmfsnmp/apc-mib.dmf +++ b/scripts/DMF/dmfsnmp/apc-mib.dmf @@ -56,6 +56,9 @@ + + + @@ -182,6 +185,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/baytech-mib.dmf b/scripts/DMF/dmfsnmp/baytech-mib.dmf index 3fe64450cd..92afb2c5d5 100644 --- a/scripts/DMF/dmfsnmp/baytech-mib.dmf +++ b/scripts/DMF/dmfsnmp/baytech-mib.dmf @@ -10,6 +10,9 @@ + + + @@ -34,6 +37,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/bestpower-mib.dmf b/scripts/DMF/dmfsnmp/bestpower-mib.dmf index 933b6b3db4..d7730551b6 100644 --- a/scripts/DMF/dmfsnmp/bestpower-mib.dmf +++ b/scripts/DMF/dmfsnmp/bestpower-mib.dmf @@ -8,6 +8,9 @@ + + + @@ -21,6 +24,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/compaq-mib.dmf b/scripts/DMF/dmfsnmp/compaq-mib.dmf index e70d320fc7..2a0acfd2a5 100644 --- a/scripts/DMF/dmfsnmp/compaq-mib.dmf +++ b/scripts/DMF/dmfsnmp/compaq-mib.dmf @@ -54,6 +54,9 @@ + + + @@ -129,6 +132,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/cyberpower-mib.dmf b/scripts/DMF/dmfsnmp/cyberpower-mib.dmf index 72105a2395..2294862899 100644 --- a/scripts/DMF/dmfsnmp/cyberpower-mib.dmf +++ b/scripts/DMF/dmfsnmp/cyberpower-mib.dmf @@ -27,6 +27,9 @@ + + + @@ -65,6 +68,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/delta_ups-mib.dmf b/scripts/DMF/dmfsnmp/delta_ups-mib.dmf index ea9f592c07..08c9a387f1 100644 --- a/scripts/DMF/dmfsnmp/delta_ups-mib.dmf +++ b/scripts/DMF/dmfsnmp/delta_ups-mib.dmf @@ -20,6 +20,9 @@ + + + @@ -37,6 +40,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/eaton-ats16-nm2-mib.dmf b/scripts/DMF/dmfsnmp/eaton-ats16-nm2-mib.dmf index 35dc7188db..c0f7032f6a 100644 --- a/scripts/DMF/dmfsnmp/eaton-ats16-nm2-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-ats16-nm2-mib.dmf @@ -79,6 +79,9 @@ + + + @@ -142,6 +145,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/eaton-ats16-nmc-mib.dmf b/scripts/DMF/dmfsnmp/eaton-ats16-nmc-mib.dmf index 750afcc3ac..2f29a11a62 100644 --- a/scripts/DMF/dmfsnmp/eaton-ats16-nmc-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-ats16-nmc-mib.dmf @@ -47,6 +47,9 @@ + + + @@ -82,6 +85,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/eaton-pdu-genesis2-mib.dmf b/scripts/DMF/dmfsnmp/eaton-pdu-genesis2-mib.dmf index 66f6d26ac4..7a46b9b80d 100644 --- a/scripts/DMF/dmfsnmp/eaton-pdu-genesis2-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-pdu-genesis2-mib.dmf @@ -4,6 +4,9 @@ + + + @@ -22,6 +25,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf b/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf index 5b5e415fde..e04a6b1855 100644 --- a/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf @@ -145,6 +145,9 @@ + + + diff --git a/scripts/DMF/dmfsnmp/eaton-pdu-pulizzi-mib.dmf b/scripts/DMF/dmfsnmp/eaton-pdu-pulizzi-mib.dmf index b1ebff4b12..b028af3ed3 100644 --- a/scripts/DMF/dmfsnmp/eaton-pdu-pulizzi-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-pdu-pulizzi-mib.dmf @@ -12,6 +12,9 @@ + + + @@ -39,7 +42,7 @@ - - + + diff --git a/scripts/DMF/dmfsnmp/eaton-pdu-revelation-mib.dmf b/scripts/DMF/dmfsnmp/eaton-pdu-revelation-mib.dmf index 85ff486795..16be1dd55a 100644 --- a/scripts/DMF/dmfsnmp/eaton-pdu-revelation-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-pdu-revelation-mib.dmf @@ -16,6 +16,9 @@ + + + @@ -56,6 +59,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/emerson-avocent-pdu-mib.dmf b/scripts/DMF/dmfsnmp/emerson-avocent-pdu-mib.dmf index 197bf126ea..65812e4d05 100644 --- a/scripts/DMF/dmfsnmp/emerson-avocent-pdu-mib.dmf +++ b/scripts/DMF/dmfsnmp/emerson-avocent-pdu-mib.dmf @@ -8,6 +8,9 @@ + + + @@ -38,6 +41,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/hpe-pdu-mib.dmf b/scripts/DMF/dmfsnmp/hpe-pdu-mib.dmf index 50c8ed6874..d17c9daba6 100644 --- a/scripts/DMF/dmfsnmp/hpe-pdu-mib.dmf +++ b/scripts/DMF/dmfsnmp/hpe-pdu-mib.dmf @@ -116,6 +116,9 @@ + + + @@ -277,6 +280,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/huawei-mib.dmf b/scripts/DMF/dmfsnmp/huawei-mib.dmf index b615b388b9..b553030991 100644 --- a/scripts/DMF/dmfsnmp/huawei-mib.dmf +++ b/scripts/DMF/dmfsnmp/huawei-mib.dmf @@ -62,6 +62,9 @@ + + + @@ -114,6 +117,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/ietf-mib.dmf b/scripts/DMF/dmfsnmp/ietf-mib.dmf index 97f50d374b..3b8129ff8c 100644 --- a/scripts/DMF/dmfsnmp/ietf-mib.dmf +++ b/scripts/DMF/dmfsnmp/ietf-mib.dmf @@ -46,6 +46,9 @@ + + + @@ -133,7 +136,7 @@ - - + + diff --git a/scripts/DMF/dmfsnmp/mge-mib.dmf b/scripts/DMF/dmfsnmp/mge-mib.dmf index 1f9e525c42..55a56bed28 100644 --- a/scripts/DMF/dmfsnmp/mge-mib.dmf +++ b/scripts/DMF/dmfsnmp/mge-mib.dmf @@ -72,6 +72,9 @@ + + + diff --git a/scripts/DMF/dmfsnmp/netvision-mib.dmf b/scripts/DMF/dmfsnmp/netvision-mib.dmf index 85df18d3e4..ead26b5704 100644 --- a/scripts/DMF/dmfsnmp/netvision-mib.dmf +++ b/scripts/DMF/dmfsnmp/netvision-mib.dmf @@ -26,6 +26,9 @@ + + + @@ -72,6 +75,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/powerware-mib.dmf b/scripts/DMF/dmfsnmp/powerware-mib.dmf index d5fc6286b8..2c73ec7994 100644 --- a/scripts/DMF/dmfsnmp/powerware-mib.dmf +++ b/scripts/DMF/dmfsnmp/powerware-mib.dmf @@ -168,6 +168,9 @@ + + + diff --git a/scripts/DMF/dmfsnmp/raritan-pdu-mib.dmf b/scripts/DMF/dmfsnmp/raritan-pdu-mib.dmf index a5416f7b62..131cda3221 100644 --- a/scripts/DMF/dmfsnmp/raritan-pdu-mib.dmf +++ b/scripts/DMF/dmfsnmp/raritan-pdu-mib.dmf @@ -10,6 +10,9 @@ + + + @@ -43,6 +46,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/raritan-px2-mib.dmf b/scripts/DMF/dmfsnmp/raritan-px2-mib.dmf index e53e45c5ae..be462f51bf 100644 --- a/scripts/DMF/dmfsnmp/raritan-px2-mib.dmf +++ b/scripts/DMF/dmfsnmp/raritan-px2-mib.dmf @@ -35,6 +35,9 @@ + + + @@ -63,6 +66,6 @@ - + diff --git a/scripts/DMF/dmfsnmp/xppc-mib.dmf b/scripts/DMF/dmfsnmp/xppc-mib.dmf index a03973eceb..2c1144f158 100644 --- a/scripts/DMF/dmfsnmp/xppc-mib.dmf +++ b/scripts/DMF/dmfsnmp/xppc-mib.dmf @@ -20,6 +20,9 @@ + + + @@ -31,6 +34,6 @@ - + From 644d67718d74d09fd76872b2c35b3f59357276c5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 17:17:32 +0100 Subject: [PATCH 209/700] scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf: re-apply fix from "open" to "opened" ambient dry contacts --- scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf b/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf index e04a6b1855..3c39737a2b 100644 --- a/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf +++ b/scripts/DMF/dmfsnmp/eaton-pdu-marlin-mib.dmf @@ -11,7 +11,7 @@ - + From dca9db106f0e822d27507efdc9adecd2705ba9d8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 11 Oct 2017 03:24:09 +0200 Subject: [PATCH 210/700] snmp-ups.[ch] : introduce SU_FLAG_SEMI_STATIC --- drivers/snmp-ups.c | 29 +++++++++++++++++++++++++++++ drivers/snmp-ups.h | 11 +++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 9d40fc57cd..3b0a96d5e7 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -133,6 +133,9 @@ struct snmp_session g_snmp_sess, *g_snmp_sess_p; const char *OID_pwr_status; int g_pwr_battery; int pollfreq; /* polling frequency */ +int semistaticfreq; /* semistatic entry update frequency */ +static int semistatic_countdown = 0; + static int quirk_symmetra_threephase = 0; /* Number of device(s): standard is "1", but talking @@ -368,6 +371,8 @@ void upsdrv_makevartable(void) "Set SNMP version (default=v1, allowed: v2c,v3)"); addvar(VAR_VALUE, SU_VAR_POLLFREQ, "Set polling frequency in seconds, to reduce network flow (default=30)"); + addvar(VAR_VALUE, SU_VAR_SEMISTATICFREQ, + "Set semistatic value update frequency in update cycles, to reduce network flow (default=10)"); addvar(VAR_VALUE, SU_VAR_RETRIES, "Specifies the number of Net-SNMP retries to be used in the requests (default=5)"); addvar(VAR_VALUE, SU_VAR_TIMEOUT, @@ -597,6 +602,17 @@ void upsdrv_initups(void) else pollfreq = DEFAULT_POLLFREQ; + /* init semistatic update frequency */ + if (getval(SU_VAR_SEMISTATICFREQ)) + semistaticfreq = atoi(getval(SU_VAR_SEMISTATICFREQ)); + else + semistaticfreq = DEFAULT_SEMISTATICFREQ; + if (semistaticfreq < 1) { + upsdebugx(1, "Bad %s value provided, setting to default", SU_VAR_SEMISTATICFREQ); + semistaticfreq = DEFAULT_SEMISTATICFREQ; + } + semistatic_countdown = semistaticfreq; + /* Get UPS Model node to see if there's a MIB */ /* FIXME: extend and use match_model_OID(char *model) */ su_info_p = su_find_info("ups.model"); @@ -2956,6 +2972,12 @@ bool_t snmp_ups_walk(int mode) snmp_info_t *su_info_p; bool_t status = FALSE; + if (mode == SU_WALKMODE_UPDATE) { + semistatic_countdown--; + if (semistatic_countdown < 0) + semistatic_countdown = semistaticfreq; + } + /* Loop through all device(s) */ /* Note: considering "unitary" and "daisy-chained" devices, we have * several variables (and their values) that can come into play: @@ -3063,6 +3085,13 @@ bool_t snmp_ups_walk(int mode) if ((mode == SU_WALKMODE_UPDATE) && !(su_info_p->flags & SU_FLAG_OK)) continue; + /* skip semi-static elements in update mode: only parse when countdown reaches 0 */ + if ((mode == SU_WALKMODE_UPDATE) && (su_info_p->flags & SU_FLAG_SEMI_STATIC)) { + if (semistatic_countdown != 0) + continue; + upsdebugx(1, "Refreshing semi-static entry %s", su_info_p->OID); + } + /* skip static elements in update mode */ if ((mode == SU_WALKMODE_UPDATE) && (su_info_p->flags & SU_FLAG_STATIC)) continue; diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index 4c870138d3..705c6c7b35 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -90,6 +90,7 @@ #define DEFAULT_POLLFREQ 30 /* in seconds */ #define DEFAULT_NETSNMP_RETRIES 5 #define DEFAULT_NETSNMP_TIMEOUT 1 /* in seconds */ +#define DEFAULT_SEMISTATICFREQ 10 /* in snmpwalk update cycles */ /* use explicit booleans */ #ifndef FALSE @@ -157,7 +158,7 @@ typedef struct { info_lkp_t *oid2info; /* lookup table between OID and NUT values */ } snmp_info_t; -/* "flags" bits 0..8 (and 9 reserved for DMF) */ +/* "flags" bits 0..9 */ #define SU_FLAG_OK (1UL << 0) /* show element to upsd - * internal to snmp driver */ #define SU_FLAG_STATIC (1UL << 1) /* retrieve info only once. */ @@ -175,9 +176,9 @@ typedef struct { #define SU_FLAG_NAINVALID (1UL << 7) /* Invalid if "N/A" value */ #define SU_CMD_OFFSET (1UL << 8) /* Add +1 to the OID index */ -/* Reserved slot -- to import from DMF branch codebase: -//#define SU_FLAG_SEMI_STATIC (1UL << 9)*/ /* Refresh this entry once in several walks -// * (for R/W values user can set on device, like descriptions or contacts) */ +#define SU_FLAG_SEMI_STATIC (1UL << 9) /* Refresh this entry once in several walks + * (for R/W values user can set on device, + * like descriptions or contacts) */ /* Notes on outlet templates usage: * - outlet.count MUST exist and MUST be declared before any outlet template @@ -255,6 +256,7 @@ typedef struct { #define SU_VAR_VERSION "snmp_version" #define SU_VAR_RETRIES "snmp_retries" #define SU_VAR_TIMEOUT "snmp_timeout" +#define SU_VAR_SEMISTATICFREQ "semistaticfreq" #define SU_VAR_MIBS "mibs" #define SU_VAR_POLLFREQ "pollfreq" /* SNMP v3 related parameters */ @@ -366,6 +368,7 @@ extern const char *OID_pwr_status; extern int g_pwr_battery; extern int pollfreq; /* polling frequency */ extern int input_phases, output_phases, bypass_phases; +extern int semistaticfreq; /* semistatic entry update frequency */ /* pointer to the Snmp2Nut lookup table */ extern mib2nut_info_t *mib2nut_info; From a00572cc39050541fe6d262256e0a73a070bfeb5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 13 May 2016 17:48:12 +0200 Subject: [PATCH 211/700] drivers/apc-iem-mib.h: split some macro definitions away from apc-mib.h and apc-mib.c and snmp-ups.c --- drivers/Makefile.am | 2 +- drivers/apc-iem-mib.h | 19 +++++++++++++++++++ drivers/apc-mib.c | 6 ------ drivers/apc-mib.h | 10 +--------- 4 files changed, 21 insertions(+), 16 deletions(-) create mode 100644 drivers/apc-iem-mib.h diff --git a/drivers/Makefile.am b/drivers/Makefile.am index fc1f2f4e6f..d6ea8c9c50 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -313,7 +313,7 @@ nutdrv_qx_SOURCES += $(NUTDRV_QX_SUBDRIVERS) # tracking (which is automatic), but to ensure these files are # distributed by "make dist". -dist_noinst_HEADERS = apc-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h bcmxcp_ser.h \ +dist_noinst_HEADERS = apc-mib.h apc-iem-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h bcmxcp_ser.h \ bcmxcp_io.h belkin.h belkin-hid.h bestpower-mib.h blazer.h cps-hid.h dstate.h \ dummy-ups.h explore-hid.h gamatronic.h genericups.h \ hidparser.h hidtypes.h ietf-mib.h libhid.h libshut.h nut_libusb.h liebert-hid.h \ diff --git a/drivers/apc-iem-mib.h b/drivers/apc-iem-mib.h new file mode 100644 index 0000000000..65904e68c0 --- /dev/null +++ b/drivers/apc-iem-mib.h @@ -0,0 +1,19 @@ +#ifndef APC_IEM_MIB_H +#define APC_IEM_MIB_H + +/* + * FIXME: The below is needed because the main driver body uses this to determine + * whether a conversion from Fahrenheit to Celsius is needed (which really should + * be solved in subdriver specific formatting functions, like we do in usbhid-ups + * This is used in both snmp-ups.c and apc.c logics. + */ + +/* IEM ambient variables */ +/* IEM: integrated environment monitor probe */ + +#define APCC_OID_IEM_TEMP ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1" +#define APCC_OID_IEM_TEMP_UNIT ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.5.1" +#define APCC_IEM_FAHRENHEIT 2 +#define APCC_OID_IEM_HUMID ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.6.1" + +#endif /* APC_IEM_MIB_H */ diff --git a/drivers/apc-mib.c b/drivers/apc-mib.c index d640d2c6ef..1ac0a27816 100644 --- a/drivers/apc-mib.c +++ b/drivers/apc-mib.c @@ -276,13 +276,7 @@ static snmp_info_t apcc_mib[] = { { "ambient.humidity", 0, 1, ".1.3.6.1.4.1.318.1.1.2.1.2.0", "", SU_FLAG_OK, NULL }, { "ambient.1.humidity.alarm.high", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.6.1", "", SU_FLAG_OK, NULL }, { "ambient.1.humidity.alarm.low", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.7.1", "", SU_FLAG_OK, NULL }, - - /* IEM ambient variables */ /* IEM: integrated environment monitor probe */ -#define APCC_OID_IEM_TEMP ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1" -#define APCC_OID_IEM_TEMP_UNIT ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.5.1" -#define APCC_IEM_FAHRENHEIT 2 -#define APCC_OID_IEM_HUMID ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.6.1" { "ambient.temperature", 0, 1, APCC_OID_IEM_TEMP, "", SU_FLAG_OK, NULL }, { "ambient.humidity", 0, 1, APCC_OID_IEM_HUMID, "", SU_FLAG_OK, NULL }, diff --git a/drivers/apc-mib.h b/drivers/apc-mib.h index e5aeb07220..0b510ea4f6 100644 --- a/drivers/apc-mib.h +++ b/drivers/apc-mib.h @@ -3,15 +3,7 @@ #include "main.h" #include "snmp-ups.h" - -/* - * FIXME: The below is needed because the main driver body uses this to determine - * whether a conversion from Fahrenheit to Celsius is needed (which really should - * be solved in subdriver specific formatting functions, like we do in usbhid-ups - */ -#define APCC_OID_IEM_TEMP ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1" -#define APCC_OID_IEM_TEMP_UNIT ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.5.1" -#define APCC_IEM_FAHRENHEIT 2 +#include "apc-iem-mib.h" extern mib2nut_info_t apc; From bdfa8777c2a2ef9d7364eb382be60e7e4f5cdca0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Aug 2017 19:40:28 +0200 Subject: [PATCH 212/700] Expel the helper function that interacts with dstate from eaton-pdu-marlin-mib.c to eaton-pdu-marlin-helpers.c/.h --- drivers/Makefile.am | 4 +-- drivers/eaton-pdu-marlin-helpers.c | 58 ++++++++++++++++++++++++++++++ drivers/eaton-pdu-marlin-helpers.h | 31 ++++++++++++++++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 drivers/eaton-pdu-marlin-helpers.c create mode 100644 drivers/eaton-pdu-marlin-helpers.h diff --git a/drivers/Makefile.am b/drivers/Makefile.am index d6ea8c9c50..6bc4f4262d 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -232,7 +232,7 @@ snmp_ups_SOURCES = snmp-ups.c snmp-ups-helpers.c \ baytech-mib.c bestpower-mib.c \ compaq-mib.c cyberpower-mib.c \ delta_ups-mib.c \ - eaton-pdu-genesis2-mib.c eaton-pdu-marlin-mib.c \ + eaton-pdu-genesis2-mib.c eaton-pdu-marlin-mib.c eaton-pdu-marlin-helpers.c \ eaton-pdu-pulizzi-mib.c eaton-pdu-revelation-mib.c \ eaton-ats16-nmc-mib.c eaton-ats16-nm2-mib.c apc-ats-mib.c eaton-ats30-mib.c \ emerson-avocent-pdu-mib.c \ @@ -329,7 +329,7 @@ dist_noinst_HEADERS = apc-mib.h apc-iem-mib.h apc-hid.h arduino-hid.h baytech-mi nutdrv_qx_megatec.h nutdrv_qx_megatec-old.h nutdrv_qx_mustek.h nutdrv_qx_q1.h nutdrv_qx_hunnox.h \ nutdrv_qx_voltronic.h nutdrv_qx_voltronic-qs.h nutdrv_qx_voltronic-qs-hex.h nutdrv_qx_zinto.h \ xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ - apc-pdu-mib.h ever-hid.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h \ + apc-pdu-mib.h ever-hid.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h eaton-pdu-marlin-helpers.h \ eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h legrand-hid.h \ hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c new file mode 100644 index 0000000000..ae806e086f --- /dev/null +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -0,0 +1,58 @@ +/* eaton-pdu-marlin-helpers.c - helper routines for eaton-pdu-marlin-mib.c + * to monitor Eaton ePDUs branded as: + * G2 Marlin SW / MI / MO / MA + * G3 Shark SW / MI / MO / MA + * + * Copyright (C) 2017 + * Arnaud Quette + * Copyright (C) 2017 + * Jim Klimov + * + * Supported by Eaton + * and previously MGE Office Protection Systems + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dstate.h" + +static char marlin_scratch_buf[20]; + +/* Compute the phase to which an outlet group is connected + * WRT the number of phase(s) and the outlet group number. + * Note that the group type (marlin_outlet_group_type_info) is + * not considered since this applies to any kind of group */ +static const char *marlin_outlet_group_phase_fun(int outlet_group_nb) +{ + const char* str_phases_nb = dstate_getinfo("input.phases"); + int phases_nb = 1; + if (str_phases_nb) { + phases_nb = atoi(str_phases_nb); + if (phases_nb == 1) { + return "L1"; + } + else { /* 3ph assumed, 2ph PDU don't exist! */ + if (outlet_group_nb > 3) + snprintf(marlin_scratch_buf, 3, "L%i", (outlet_group_nb -3)); + else + snprintf(marlin_scratch_buf, 3, "L%i", outlet_group_nb); + + return marlin_scratch_buf; + } + } + return NULL; +} diff --git a/drivers/eaton-pdu-marlin-helpers.h b/drivers/eaton-pdu-marlin-helpers.h new file mode 100644 index 0000000000..b26e1793b0 --- /dev/null +++ b/drivers/eaton-pdu-marlin-helpers.h @@ -0,0 +1,31 @@ +/* eaton-pdu-marlin-helpers.h - helper for subdriver to monitor certain + * Eaton ePDU SNMP devices with NUT + * + * Copyright (C) + * 2017 Arnaud Quette + * 2017 Jim Klimov + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EATON_EPDU_MARLIN_HELPERS_H +#define EATON_EPDU_MARLIN_HELPERS_H + +#include "main.h" +#include "snmp-ups.h" + +static const char *marlin_outlet_group_phase_fun(int outlet_group_nb); + +#endif /* EATON_EPDU_MARLIN_HELPERS_H */ From 19b52b97a68d30cfed606c4408e3104a88875cf8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Aug 2017 12:11:43 +0200 Subject: [PATCH 213/700] eaton-pdu-marlin-helpers.c : update comments for marlin_outlet_group_phase_fun() --- drivers/eaton-pdu-marlin-helpers.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index ae806e086f..e7b1a82f56 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -28,14 +28,22 @@ * */ +#include +#include +#include + #include "dstate.h" static char marlin_scratch_buf[20]; /* Compute the phase to which an outlet group is connected * WRT the number of phase(s) and the outlet group number. - * Note that the group type (marlin_outlet_group_type_info) is - * not considered since this applies to any kind of group */ + * Note that the group type (marlin_outlet_group_type_info) + * is not considered since this applies to any kind of group. + * This trick limits input phase to electrical groups only + * (not outlet-section nor user-defined!), and for now, there + * is a maximum of 6 gangs (electrical groups). + */ static const char *marlin_outlet_group_phase_fun(int outlet_group_nb) { const char* str_phases_nb = dstate_getinfo("input.phases"); @@ -45,9 +53,9 @@ static const char *marlin_outlet_group_phase_fun(int outlet_group_nb) if (phases_nb == 1) { return "L1"; } - else { /* 3ph assumed, 2ph PDU don't exist! */ + else { /* 3ph assumed, 2ph PDU don't exist - at least not in Eaton Marlin lineup! */ if (outlet_group_nb > 3) - snprintf(marlin_scratch_buf, 3, "L%i", (outlet_group_nb -3)); + snprintf(marlin_scratch_buf, 3, "L%i", (outlet_group_nb - 3)); /* FIXME: For more than 6 ports, maybe "nb % 3"? */ else snprintf(marlin_scratch_buf, 3, "L%i", outlet_group_nb); From b2802f8ac246df973f5b18bb1f145a2ba8d18870 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Aug 2017 12:27:57 +0200 Subject: [PATCH 214/700] eaton-pdu-marlin-helpers.c/h fix --- drivers/eaton-pdu-marlin-helpers.c | 3 ++- drivers/eaton-pdu-marlin-helpers.h | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index e7b1a82f56..f11f0a02d3 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -32,6 +32,7 @@ #include #include +#include "eaton-pdu-marlin-helpers.h" #include "dstate.h" static char marlin_scratch_buf[20]; @@ -44,7 +45,7 @@ static char marlin_scratch_buf[20]; * (not outlet-section nor user-defined!), and for now, there * is a maximum of 6 gangs (electrical groups). */ -static const char *marlin_outlet_group_phase_fun(int outlet_group_nb) +const char *marlin_outlet_group_phase_fun(int outlet_group_nb) { const char* str_phases_nb = dstate_getinfo("input.phases"); int phases_nb = 1; diff --git a/drivers/eaton-pdu-marlin-helpers.h b/drivers/eaton-pdu-marlin-helpers.h index b26e1793b0..d19f7d65c6 100644 --- a/drivers/eaton-pdu-marlin-helpers.h +++ b/drivers/eaton-pdu-marlin-helpers.h @@ -23,9 +23,6 @@ #ifndef EATON_EPDU_MARLIN_HELPERS_H #define EATON_EPDU_MARLIN_HELPERS_H -#include "main.h" -#include "snmp-ups.h" - -static const char *marlin_outlet_group_phase_fun(int outlet_group_nb); +const char *marlin_outlet_group_phase_fun(int outlet_group_nb); #endif /* EATON_EPDU_MARLIN_HELPERS_H */ From e9d45cccc4de5ff8209f303d1c6d54923f7b8a55 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Aug 2017 12:14:02 +0200 Subject: [PATCH 215/700] snmp-ups / eaton-marlin : introduce WITH_SNMP_LKP_FUN to separate codebases that support these callbacks from those that currently do not --- drivers/eaton-pdu-marlin-mib.c | 110 ++++++++++++++++++++++----------- drivers/snmp-ups.c | 2 + drivers/snmp-ups.h | 21 +++++++ 3 files changed, 98 insertions(+), 35 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 729c98f1d0..87d011e1f8 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -29,7 +29,9 @@ */ #include "eaton-pdu-marlin-mib.h" -#include "dstate.h" +#if WITH_SNMP_LKP_FUN +#include "eaton-pdu-marlin-helpers.h" +#endif /* Eaton PDU-MIB - Marlin MIB * ************************** */ @@ -192,43 +194,52 @@ static info_lkp_t marlin_input_type_info[] = { { 0, NULL, NULL, NULL } }; -static char marlin_scratch_buf[20]; - -/* Compute the phase to which an outlet group is connected - * WRT the number of phase(s) and the outlet group number. - * Note that the group type (marlin_outlet_group_type_info) is - * not considered since this applies to any kind of group */ -static const char *marlin_outlet_group_phase_fun(void *raw_outlet_group_nb) -{ - int outlet_group_nb = *((int *)raw_outlet_group_nb); - const char* str_phases_nb = dstate_getinfo("input.phases"); - int phases_nb = 1; - if (str_phases_nb && (outlet_group_nb >= 0) ) { - phases_nb = atoi(str_phases_nb); - if (phases_nb == 1) { - return "L1"; - } - else { /* 3ph assumed, 2ph PDU don't exist! */ - if (outlet_group_nb > 3) - phases_nb = (outlet_group_nb - 3); - else - phases_nb = outlet_group_nb; - - snprintf(marlin_scratch_buf, sizeof(marlin_scratch_buf), "L%i", phases_nb); - if (phases_nb < 1 || phases_nb > 3) - upsdebugx(3, "WARNING: %s got %i phases which is an unexpected amount", - __func__, phases_nb); - - return marlin_scratch_buf; - } - } - return NULL; -} +#if WITH_SNMP_LKP_FUN +/* Note: marlin_outlet_group_phase_fun() is defined in eaton-pdu-marlin-helpers.c + * Future work for DMF might provide a same-named routine via LUA-C gateway. + */ static info_lkp_t marlin_outlet_group_phase_info[] = { { 1, "dummy", marlin_outlet_group_phase_fun, NULL }, { 0, NULL, NULL, NULL } }; +#else /* if not WITH_SNMP_LKP_FUN: */ + +/* FIXME: For now, DMF codebase falls back to old implementation with static + * lookup/mapping tables for this, which can easily go into the DMF XML file. + */ + +/* Ugly trick which limits input phase to electrical groups */ +static info_lkp_t marlin_outlet_group_phase1_info[] = { + /* { 0, NULL }, unknown */ + { 1, "L1" }, /* breaker1pole */ + { 2, "L1" }, /* breaker2pole */ + { 3, "L1" }, /* breaker3pole */ + /* { 4, NULL }, outlet-section */ + /* { 5, NULL }, user-defined */ + { 0, NULL } +}; +static info_lkp_t marlin_outlet_group_phase2_info[] = { + /* { 0, NULL }, unknown */ + { 1, "L2" }, /* breaker1pole */ + { 2, "L2" }, /* breaker2pole */ + { 3, "L2" }, /* breaker3pole */ + /* { 4, NULL }, outlet-section */ + /* { 5, NULL }, user-defined */ + { 0, NULL } +}; +static info_lkp_t marlin_outlet_group_phase3_info[] = { + /* { 0, NULL }, unknown */ + { 1, "L3" }, /* breaker1pole */ + { 2, "L3" }, /* breaker2pole */ + { 3, "L3" }, /* breaker3pole */ + /* { 4, NULL }, outlet-section */ + /* { 5, NULL }, user-defined */ +}; + +#endif /* WITH_SNMP_LKP_FUN */ + + /* Snmp2NUT lookup table for Eaton Marlin MIB */ static snmp_info_t eaton_marlin_mib[] = { @@ -761,11 +772,40 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.group.%i.type", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &marlin_outlet_group_type_info[0] }, + &marlin_outlet_group_type_info[0], NULL }, +#if WITH_SNMP_LKP_FUN { "outlet.group.%i.phase", 0, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &marlin_outlet_group_phase_info[0] }, + &marlin_outlet_group_phase_info[0], NULL }, +#else /* not WITH_SNMP_LKP_FUN */ + /* ugly trick which limits input phase to electrical groups only (not outlet-section nor user-defined!) + * For now, there is a maximum of 6 gangs (electrical groups) */ + { "outlet.group.1.phase", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.1", + NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, + &marlin_outlet_group_phase1_info[0], NULL }, + { "outlet.group.2.phase", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.2", + NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, + &marlin_outlet_group_phase2_info[0], NULL }, + { "outlet.group.3.phase", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.3", + NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, + &marlin_outlet_group_phase3_info[0], NULL }, + { "outlet.group.4.phase", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.4", + NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, + &marlin_outlet_group_phase1_info[0], NULL }, + { "outlet.group.5.phase", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.5", + NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, + &marlin_outlet_group_phase2_info[0], NULL }, + { "outlet.group.6.phase", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.6", + NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, + &marlin_outlet_group_phase3_info[0], NULL }, +#endif // WITH_SNMP_LKP_FUN /* groupControlStatus.0.1 = Integer: on (1) */ { "outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.6.1.2.%i.%i", diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 3b0a96d5e7..7d9cfbbccb 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2102,6 +2102,7 @@ const char *su_find_infoval(info_lkp_t *oid2info, void *raw_value) info_lkp_t *info_lkp; long value = *((long *)raw_value); +#ifdef WITH_SNMP_LKP_FUN /* First test if we have a generic lookup function */ if ( (oid2info != NULL) && (oid2info->fun_vp2s != NULL) ) { upsdebugx(2, "%s: using generic lookup function", __func__); @@ -2109,6 +2110,7 @@ const char *su_find_infoval(info_lkp_t *oid2info, void *raw_value) upsdebugx(2, "%s: got value '%s'", __func__, retvalue); return retvalue; } +#endif // WITH_SNMP_LKP_FUN /* Otherwise, use the simple values mapping */ for (info_lkp = oid2info; (info_lkp != NULL) && diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index 705c6c7b35..508c5f4e8f 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -109,10 +109,30 @@ typedef int bool_t; /* typedef void (*interpreter)(char *, char *, int); */ +#ifndef WITH_SNMP_LKP_FUN +/* Recent addition of fun/nuf hooks in info_lkp_t is not well handled by + * all corners of the codebase, e.g. not by DMF. So at least until that + * is fixed, (TODO) we enable those bits of code only optionally during + * a build for particular usage. Conversely, experimenters can define + * this macro to a specific value while building the codebase and see + * what happens under different conditions ;) + */ +# if WITH_DMFMIB +# define WITH_SNMP_LKP_FUN 0 +# else +# define WITH_SNMP_LKP_FUN 1 +# endif +#endif + /* for lookup between OID values and INFO_ value */ typedef struct { int oid_value; /* SNMP OID value */ const char *info_value; /* NUT INFO_* value */ +#if WITH_SNMP_LKP_FUN +/* FIXME: Currently we do not have a way to provide custom C code + * via DMF - keep old approach until we get the ability, e.g. by + * requiring a LUA implementation to be passed alongside C lookups. + */ /* * Currently there are a few cases using a "fun_vp2s" type of lookup * function, while the "nuf_s2l" type was added for completeness but @@ -124,6 +144,7 @@ typedef struct { */ const char *(*fun_vp2s)(void *snmp_value); /* optional SNMP to NUT mapping function, converting a pointer to SNMP data (e.g. numeric or string) into a NUT string */ long (*nuf_s2l)(const char *nut_value); /* optional NUT to SNMP mapping function, converting a NUT string into SNMP numeric data */ +#endif /* WITH_SNMP_LKP_FUN */ } info_lkp_t; /* Structure containing info about one item that can be requested From a8f6e7a9022f0a530071250389865108d5d4ae14 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 30 Aug 2017 14:51:45 +0200 Subject: [PATCH 216/700] Fix typo in OID, noticed by aquette Signed-off-by: Jim Klimov --- drivers/eaton-pdu-marlin-mib.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 87d011e1f8..4a5325b44d 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -782,27 +782,27 @@ static snmp_info_t eaton_marlin_mib[] = { /* ugly trick which limits input phase to electrical groups only (not outlet-section nor user-defined!) * For now, there is a maximum of 6 gangs (electrical groups) */ { "outlet.group.1.phase", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.1", + ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.1", NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, &marlin_outlet_group_phase1_info[0], NULL }, { "outlet.group.2.phase", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.2", + ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.2", NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, &marlin_outlet_group_phase2_info[0], NULL }, { "outlet.group.3.phase", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.3", + ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.3", NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, &marlin_outlet_group_phase3_info[0], NULL }, { "outlet.group.4.phase", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.4", + ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.4", NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, &marlin_outlet_group_phase1_info[0], NULL }, { "outlet.group.5.phase", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.5", + ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.5", NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, &marlin_outlet_group_phase2_info[0], NULL }, { "outlet.group.6.phase", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.6", + ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.6", NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, &marlin_outlet_group_phase3_info[0], NULL }, #endif // WITH_SNMP_LKP_FUN From 02fb1dc5e5fd29473d2bc3c2e230023aee6a2360 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 9 Oct 2017 21:24:49 +0200 Subject: [PATCH 217/700] eaton-pdu-marlin-mib.c : add basic listing of newly defined OIDs (not a full solution - some mapping functions are needed and revision of MIB data types) --- docs/nut-names.txt | 2 + drivers/eaton-pdu-marlin-mib.c | 109 +++++++++++++++++++++++++++++---- 2 files changed, 100 insertions(+), 11 deletions(-) diff --git a/docs/nut-names.txt b/docs/nut-names.txt index 8c14a87e20..187f7b7efb 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -632,6 +632,8 @@ Some specific data to outlet groups exists: |================================================================================= | Name | Description | Example value | outlet.group.n.type | Type of outlet group (OPAQUE) | outlet-section +| outlet.group.n.color | Color-coding of the outlets + in this group (OPAQUE) | yellow | outlet.group.n.count | Number of outlets in the group | 12 | outlet.group.n.phase | Electrical phase to which the physical outlet group (Gang) is diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 4a5325b44d..9c02354f33 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -36,7 +36,7 @@ /* Eaton PDU-MIB - Marlin MIB * ************************** */ -#define EATON_MARLIN_MIB_VERSION "0.59" +#define EATON_MARLIN_MIB_VERSION "0.60" #define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" #define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" @@ -265,12 +265,28 @@ static snmp_info_t eaton_marlin_mib[] = { /* For daisychain, there is only 1 physical interface! */ { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - /* Daisychained devices support - * Notes: this definition is used to: + + /* Daisychained devices support */ + /* FIXME : Should this be a static value, or can we expect the amount of + * daisy-chained devices to change without restart of the driver by user? + * If this is a critical matter, should a detected change of amount of + * daisy-chained devices, outlet counts, etc. cause restart/reinit of + * this running driver instance? + */ + /* Number of daisychained units is processed according to present units + * in the chain with new G3 firmware (02.00.0051, since autumn 2017): + * Take string "unitsPresent" (ex: "0,3,4,5"), and count the amount + * of "," separators+1 using an inline function */ + /* FIXME: inline func */ + { "device.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.1.0", + "1", SU_FLAG_STATIC | SU_FLAG_UNIQUE, + NULL, NULL /* devices_count */ }, + /* Notes: this older/fallback definition is used to: * - estimate the number of devices, based on the below OID iteration capabilities * - determine the base index of the SNMP OID (ie 0 or 1) */ { "device.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", - "1", SU_FLAG_STATIC, NULL }, + "1", SU_FLAG_STATIC | SU_FLAG_UNIQUE, NULL, + NULL /* devices_count */ }, /* UPS collection */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", @@ -558,6 +574,27 @@ static snmp_info_t eaton_marlin_mib[] = { { "input.L3.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* Feed to which the ePDU is connected + * ??? // FIXME-Check on real device + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags */ + /* { "input.feed.%i.id", 0, 1, "???.%i.%i", NULL, SU_FLAG_NEGINVALID, NULL }, */ + /* Feed name + * inputFeedName.0.1 = Value (OctetString): A1 // FIXME-Check on real device + */ + { "input.feed.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* Feed A / B color + * inputFeedColor.0.1 = Value (OctetString): 0A8B9C // FIXME-Check on real device + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream */ + { "input.feed.%i.color", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* ePDU "nominal power", inputPowerCapacity with .1.3.6.1.4.1.534.6.6.7.3.4.1.9.x.1.y using "input.power.nominal" (need RFC on NUT) + * TODO: sample says of ...x.1.y and no %i in the key name... + * inputPowerCapacity // FIXME-Check on real device + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream */ + { "input.power.nominal", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.9.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* Ambient collection */ { "ambient.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.3.%i.1", @@ -669,9 +706,18 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, /* outletID: Outlet physical name, related to its number in the group * ex: first outlet of the second group (B) is B1 */ + /* Outlet physical name OID in new G3 firmware (02.00.0051) + * outletPhysicalName.0.1 = Value (OctetString): A1 // FIXME-Check on real device + */ + { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.1.1.6.%i.%i", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + NULL, NULL }, + /* Fallback in firmwares issued before Sep 2017: */ { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.2.%i.%i", - NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + NULL, NULL }, /* FIXME: the last part of the OID gives the group number (i.e. %i.1 means "group 1") * Need to address that, without multiple declaration (%i.%i, SU_OUTLET | SU_OUTLET_GROUP)? */ { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, @@ -765,18 +811,41 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, /* groupName.0.1 = OctetString: Factory Group 1 */ /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => refreshed from time to time or upon call to setvar */ - { "outlet.group.%i.name", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + /* User-friendly (writeable) description of the outlet group: */ + { "outlet.group.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.3.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* Outlet-group physical name + * groupPhysicalName.0.1 = Value (OctetString): GRP1 // FIXME-Check on real device + */ + { "outlet.group.%i.name", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.8.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* Outlet-group color + * groupBkgColor.0.1 = Value (OctetString): 0A8B9C // FIXME-Check on real device + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream */ + { "outlet.group.%i.color", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.7.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, /* groupType.0.1 = Integer: outletSection (4) */ { "outlet.group.%i.type", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, &marlin_outlet_group_type_info[0], NULL }, #if WITH_SNMP_LKP_FUN + /* Phase to which an outlet-group is connected (Caution: the value is numeric, and need to append "L" -- TODO) + * groupPhaseID // FIXME-Check on real device + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags (daisy?) */ + /* FIXME: inline func */ + { "outlet.group.%i.phase", 0, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.10.%i.%i", + NULL, SU_FLAG_UNIQUE | SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL, NULL }, { "outlet.group.%i.phase", 0, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL, SU_FLAG_UNIQUE | SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, &marlin_outlet_group_phase_info[0], NULL }, #else /* not WITH_SNMP_LKP_FUN */ /* ugly trick which limits input phase to electrical groups only (not outlet-section nor user-defined!) @@ -885,6 +954,13 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.group.%i.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.5.5.1.2.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* Input to which an outlet-group is connected + * groupInputID // FIXME-Check on real device + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags (daisy?) */ + { "outlet.group.%i.input", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.9.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, /* instant commands. */ /* Notes: @@ -895,11 +971,11 @@ static snmp_info_t eaton_marlin_mib[] = { * we currently use "0", so instant On | Off | Reboot... */ /* no counterpart found! { "outlet.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, + NULL, SU_TYPE_CMD, NULL }, { "outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, + NULL, SU_TYPE_CMD, NULL }, { "outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", - NULL, SU_TYPE_CMD, NULL, NULL }, */ + NULL, SU_TYPE_CMD, NULL }, */ /* Delays handling: * 0-n :Time in seconds until the group command is issued @@ -918,7 +994,18 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.%i.load.cycle.delay", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - /* Delays handling: + /* Per outlet shutdown / startup delay (configuration point, not the timers) + * outletControlShutoffDelay // FIXME-Check on real device + * outletControlSequenceDelay // FIXME-Check on real device ; verify OID is one component shorter? + */ + { "outlet.%i.delay.shutdown", ST_FLAG_RW, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.10.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.delay.start", ST_FLAG_RW, 1, + ".1.3.6.1.4.1.534.6.6.7.6.1.7.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + + /* TODO: handle delays * 0-n :Time in seconds until the group command is issued * -1:Cancel a pending group-level Off/On/Reboot command */ /* groupControlOffCmd.0.1 = Integer: -1 */ From 456e6231d248c2c616e735393a731005478fa393 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 9 Oct 2017 21:44:35 +0200 Subject: [PATCH 218/700] eaton-pdu-marlin-mib.c / drivers/eaton-pdu-marlin-helpers.[ch] : Implement conversion func for "outlet.group.%i.phase" --- drivers/eaton-pdu-marlin-helpers.c | 10 ++++++++++ drivers/eaton-pdu-marlin-helpers.h | 1 + drivers/eaton-pdu-marlin-mib.c | 4 ++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index f11f0a02d3..0570f60ae1 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -65,3 +65,13 @@ const char *marlin_outlet_group_phase_fun(int outlet_group_nb) } return NULL; } + +/* Take the value received from MIB, convert to string and add a prefix */ +const char *marlin_outlet_group_phase_prefix_fun(int outlet_group_input_phase) +{ + if (outlet_group_input_phase >= 1 && outlet_group_input_phase <= 3) { + snprintf(marlin_scratch_buf, 3, "L%i", outlet_group_input_phase); + return marlin_scratch_buf; + } + return NULL; +} diff --git a/drivers/eaton-pdu-marlin-helpers.h b/drivers/eaton-pdu-marlin-helpers.h index d19f7d65c6..182cc05173 100644 --- a/drivers/eaton-pdu-marlin-helpers.h +++ b/drivers/eaton-pdu-marlin-helpers.h @@ -24,5 +24,6 @@ #define EATON_EPDU_MARLIN_HELPERS_H const char *marlin_outlet_group_phase_fun(int outlet_group_nb); +const char *marlin_outlet_group_phase_prefix_fun(int outlet_group_input_phase); #endif /* EATON_EPDU_MARLIN_HELPERS_H */ diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 9c02354f33..95a0d015c3 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -200,6 +200,7 @@ static info_lkp_t marlin_input_type_info[] = { */ static info_lkp_t marlin_outlet_group_phase_info[] = { { 1, "dummy", marlin_outlet_group_phase_fun, NULL }, + { 2, "dummytwoo", marlin_outlet_group_phase_prefix_fun, NULL }, { 0, NULL, NULL, NULL } }; @@ -838,11 +839,10 @@ static snmp_info_t eaton_marlin_mib[] = { * groupPhaseID // FIXME-Check on real device */ /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags (daisy?) */ - /* FIXME: inline func */ { "outlet.group.%i.phase", 0, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.10.%i.%i", NULL, SU_FLAG_UNIQUE | SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - NULL, NULL }, + &marlin_outlet_group_phase_info[1], NULL }, { "outlet.group.%i.phase", 0, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.%i", NULL, SU_FLAG_UNIQUE | SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, From a76861f4143dd869cbc8a53852320eb3ecb67757 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 9 Oct 2017 22:53:29 +0200 Subject: [PATCH 219/700] eaton-pdu-marlin-mib.c / drivers/eaton-pdu-marlin-helpers.[ch] : Implement conversion func for "device.count" which returns a comma-separated list --- drivers/eaton-pdu-marlin-helpers.c | 18 ++++++++++++++++++ drivers/eaton-pdu-marlin-helpers.h | 2 ++ drivers/eaton-pdu-marlin-mib.c | 22 +++++++++++++++++----- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index 0570f60ae1..5a96a3b6cf 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -75,3 +75,21 @@ const char *marlin_outlet_group_phase_prefix_fun(int outlet_group_input_phase) } return NULL; } + +/* Take string "unitsPresent" (ex: "0,3,4,5"), and count the amount + * of "," separators+1 using an inline function */ +const int marlin_device_count_fun(const char *daisy_dev_list) +{ + int count = 0, i; + for (i=0; daisy_dev_list[i] != '\0'; i++) { + if (daisy_dev_list[i] == ',') { + /* Each comma means a new device in the list */ + count ++; + } + } + if (i>0) { + /* Non-empty string => at least one device */ + count ++; + } + return count; +} diff --git a/drivers/eaton-pdu-marlin-helpers.h b/drivers/eaton-pdu-marlin-helpers.h index 182cc05173..603bea19f4 100644 --- a/drivers/eaton-pdu-marlin-helpers.h +++ b/drivers/eaton-pdu-marlin-helpers.h @@ -26,4 +26,6 @@ const char *marlin_outlet_group_phase_fun(int outlet_group_nb); const char *marlin_outlet_group_phase_prefix_fun(int outlet_group_input_phase); +const int marlin_device_count_fun(const char *daisy_dev_list); + #endif /* EATON_EPDU_MARLIN_HELPERS_H */ diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 95a0d015c3..79b7e8ccd6 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -204,6 +204,11 @@ static info_lkp_t marlin_outlet_group_phase_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t marlin_device_count_info[] = { + { 1, "dummy", NULL, marlin_device_count_fun }, + { 0, NULL, NULL, NULL } +}; + #else /* if not WITH_SNMP_LKP_FUN: */ /* FIXME: For now, DMF codebase falls back to old implementation with static @@ -274,20 +279,27 @@ static snmp_info_t eaton_marlin_mib[] = { * daisy-chained devices, outlet counts, etc. cause restart/reinit of * this running driver instance? */ +#if WITH_SNMP_LKP_FUN /* Number of daisychained units is processed according to present units * in the chain with new G3 firmware (02.00.0051, since autumn 2017): * Take string "unitsPresent" (ex: "0,3,4,5"), and count the amount * of "," separators+1 using an inline function */ /* FIXME: inline func */ - { "device.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.1.0", + { "device.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.1.0", "1", SU_FLAG_STATIC | SU_FLAG_UNIQUE, - NULL, NULL /* devices_count */ }, + &marlin_device_count_info[0] /* devices_count */ }, +#endif /* Notes: this older/fallback definition is used to: * - estimate the number of devices, based on the below OID iteration capabilities * - determine the base index of the SNMP OID (ie 0 or 1) */ - { "device.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", - "1", SU_FLAG_STATIC | SU_FLAG_UNIQUE, NULL, - NULL /* devices_count */ }, + { "device.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", + "1", SU_FLAG_STATIC +#if WITH_SNMP_LKP_FUN + | SU_FLAG_UNIQUE +#endif + , NULL /* devices_count */ }, /* UPS collection */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", From 298445f168504aab4dfac5e68af09d0b20c228b6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 11 Oct 2017 00:32:52 +0200 Subject: [PATCH 220/700] eaton-pdu-marlin-mib.c : Revised added OIDs with a single-group ePDU Also update line-breaks for readability and 80-col standard --- drivers/eaton-pdu-marlin-mib.c | 200 ++++++++++++++++++++++----------- 1 file changed, 134 insertions(+), 66 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 79b7e8ccd6..7ef6dfae52 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -250,26 +250,35 @@ static info_lkp_t marlin_outlet_group_phase3_info[] = { static snmp_info_t eaton_marlin_mib[] = { /* standard MIB items */ - { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, - { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, - { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.1.0", + NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.4.0", + NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.6.0", + NULL, SU_FLAG_OK, NULL }, /* Device collection */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.4.%i", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.3.%i", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, /* For daisychain, there is only 1 physical interface! */ - { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", + { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.2.1.2.2.1.6.2", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, /* Daisychained devices support */ @@ -287,7 +296,7 @@ static snmp_info_t eaton_marlin_mib[] = { /* FIXME: inline func */ { "device.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.1.0", - "1", SU_FLAG_STATIC | SU_FLAG_UNIQUE, + "0", SU_FLAG_STATIC | SU_FLAG_UNIQUE, &marlin_device_count_info[0] /* devices_count */ }, #endif /* Notes: this older/fallback definition is used to: @@ -318,8 +327,9 @@ static snmp_info_t eaton_marlin_mib[] = { { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.5.%i", "", SU_FLAG_OK, NULL }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /* FIXME: needs a date reformating callback * 2011-8-29,16:27:25.0,+1:0 * Hex-STRING: 07 DB 08 1D 10 0C 36 00 2B 01 00 00 @@ -338,15 +348,18 @@ static snmp_info_t eaton_marlin_mib[] = { */ /* Note: the below gives the number of input, not the number of phase(s)! */ /* inputCount.0; Value (Integer): 1 - { "input.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.20.0", + { "input.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.20.0", NULL, SU_FLAG_STATIC, NULL }, */ /* Note: for daisychain mode, we must handle phase(s) per device, not as a whole */ /* inputType.%i.1 = INTEGER: singlePhase (1) */ - { "input.phases", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.2.%i.1", + { "input.phases", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.2.%i.1", NULL, SU_FLAG_STATIC, &marlin_input_type_info[0] }, /* Frequency is measured globally */ - { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", + { "input.frequency", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", NULL, 0, NULL }, { "input.frequency.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.4.%i.1", @@ -358,13 +371,17 @@ static snmp_info_t eaton_marlin_mib[] = { /* inputCurrentPercentLoad (measured globally) * Current percent load, based on the rated current capacity */ /* FIXME: input.load is mapped on input.L1.load for both single and 3phase !!! */ - { "input.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", + { "input.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L1.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", + { "input.L1.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L2.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.2", + { "input.L2.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L3.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.3", + { "input.L3.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, /* FIXME: @@ -557,53 +574,85 @@ static snmp_info_t eaton_marlin_mib[] = { ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.%i.1.3", NULL, SU_FLAG_NEGINVALID, NULL }, /* Sum of all phases realpower, valid for Shark 1ph/3ph only */ - { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.4.%i.1", + { "input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.4.%i.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, /* Fallback 1: Sum of all phases realpower, valid for Marlin 3ph only */ - { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", + { "input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, /* Fallback 2: Sum of the phase realpower, valid for Marlin 1ph only */ - { "input.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", + { "input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L1.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.1", + { "input.L1.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L2.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", + { "input.L2.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L3.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.3", + { "input.L3.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, /* Sum of all phases apparent power, valid for Shark 1ph/3ph only */ - { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.3.%i.1", + { "input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.3.%i.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, /* Fallback 1: Sum of all phases realpower, valid for Marlin 3ph only */ - { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", + { "input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, /* Fallback 2: Sum of the phase realpower, valid for Marlin 1ph only */ - { "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", + { "input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L1.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.1", + { "input.L1.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L2.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", + { "input.L2.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - { "input.L3.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.3", + { "input.L3.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - /* Feed to which the ePDU is connected - * ??? // FIXME-Check on real device + /* Input feeds need 2 static declarations with last %i in [1,2]: + * iterators are currently available for daisychain devs, outlets, + * and groups - but not for inputs nor feeds. */ + /* Feed ID to which the ePDU is connected + * ??? // FIXME: Numeric value not provided by FW at this time */ /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags */ /* { "input.feed.%i.id", 0, 1, "???.%i.%i", NULL, SU_FLAG_NEGINVALID, NULL }, */ - /* Feed name - * inputFeedName.0.1 = Value (OctetString): A1 // FIXME-Check on real device + /* Feed name(s) of the ePDU power input(s), can be set by user (FIXME: rename to .desc?) + * inputFeedName.0.1 = Value (OctetString): Feed A */ - { "input.feed.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - /* Feed A / B color - * inputFeedColor.0.1 = Value (OctetString): 0A8B9C // FIXME-Check on real device + /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => refreshed from time to time or upon call to setvar */ +/* { "input.feed.%i.name", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + * ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.%i", + * NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + */ + { "input.feed.1.name", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.1", + NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + { "input.feed.2.name", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.2", + NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + /* Feed A / B color (integer RGB) + * inputFeedColor.0.1 = Gauge32: 0 (black) */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ - { "input.feed.%i.color", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - /* ePDU "nominal power", inputPowerCapacity with .1.3.6.1.4.1.534.6.6.7.3.4.1.9.x.1.y using "input.power.nominal" (need RFC on NUT) +/* { "input.feed.%i.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.%i", + * NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + */ + { "input.feed.1.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.1", + NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + { "input.feed.2.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.2", + NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + /* ePDU "nominal power", inputPowerCapacity with .1.3.6.1.4.1.534.6.6.7.3.4.1.9.x.1.y + * using "input.power.nominal" (need RFC on NUT) * TODO: sample says of ...x.1.y and no %i in the key name... - * inputPowerCapacity // FIXME-Check on real device + * inputPowerCapacity = INTEGER: -1 // FIXME: Test on HW that provides it */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ { "input.power.nominal", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.9.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, @@ -675,11 +724,15 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, SU_FLAG_OK, &marlin_ambient_drycontacts_info[0] }, /* Outlet collection */ - { "outlet.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.22.%i", + { "outlet.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.22.%i", "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "outlet.id", 0, 1, NULL, + { "outlet.id", 0, 1, + NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, + NULL, + "All outlets", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /* UnitType * used to depict the overall outlets switchability of the unit on G3 and newer ePDU*/ @@ -692,15 +745,20 @@ static snmp_info_t eaton_marlin_mib[] = { "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_TYPE_DAISY_1, &g2_unit_outlet_switchability_info[0] }, /* The below ones are the same as the input.* equivalent */ /* FIXME: transition period, TO BE REMOVED, moved to input.* */ - { "outlet.frequency", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", + { "outlet.frequency", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", NULL, 0, NULL }, - { "outlet.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", + { "outlet.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", NULL, 0, NULL }, - { "outlet.current", 0, 0.01, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.1", + { "outlet.current", 0, 0.01, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.1", NULL, 0, NULL }, - { "outlet.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", + { "outlet.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", NULL, 0, NULL }, - { "outlet.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", + { "outlet.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", NULL, 0, NULL }, /* outlet template definition @@ -828,14 +886,16 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.group.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.3.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, - /* Outlet-group physical name - * groupPhysicalName.0.1 = Value (OctetString): GRP1 // FIXME-Check on real device + /* Outlet-group physical name, a read-only string, + * is named groupDesignator (other MIBs groupPhysicalName) + * groupPhysicalName.0.1 = Value (OctetString): A + * groupDesignator.0.2 = Value (OctetString): B */ { "outlet.group.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.8.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, - /* Outlet-group color - * groupBkgColor.0.1 = Value (OctetString): 0A8B9C // FIXME-Check on real device + /* Outlet-group color: groupColor (other MIBs groupBkgColor) + * groupColor.0.1 = Value (Gauge32): 16051527 (0xF4ED47) */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ { "outlet.group.%i.color", ST_FLAG_STRING, SU_INFOSIZE, @@ -847,8 +907,9 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, &marlin_outlet_group_type_info[0], NULL }, #if WITH_SNMP_LKP_FUN - /* Phase to which an outlet-group is connected (Caution: the value is numeric, and need to append "L" -- TODO) - * groupPhaseID // FIXME-Check on real device + /* Phase to which an outlet-group is connected + * (Caution: the value is numeric, and need to append "L" -- TODO) + * groupPhaseID: // FIXME-Check on real device */ /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags (daisy?) */ { "outlet.group.%i.phase", 0, SU_INFOSIZE, @@ -967,9 +1028,9 @@ static snmp_info_t eaton_marlin_mib[] = { ".1.3.6.1.4.1.534.6.6.7.5.5.1.2.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, /* Input to which an outlet-group is connected - * groupInputID // FIXME-Check on real device + * groupInputIndex.0.1 = Integer: 1 */ - /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags (daisy?) */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream */ { "outlet.group.%i.input", 0, 1, ".1.3.6.1.4.1.534.6.6.7.5.1.1.9.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, @@ -992,29 +1053,36 @@ static snmp_info_t eaton_marlin_mib[] = { /* Delays handling: * 0-n :Time in seconds until the group command is issued * -1:Cancel a pending group-level Off/On/Reboot command */ - { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", + { "outlet.%i.load.off", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", + { "outlet.%i.load.on", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", + { "outlet.%i.load.cycle", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, /* Delayed version, parameter is mandatory (so dfl is NULL)! */ - { "outlet.%i.load.off.delay", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", + { "outlet.%i.load.off.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.on.delay", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", + { "outlet.%i.load.on.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.cycle.delay", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", + { "outlet.%i.load.cycle.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - /* Per outlet shutdown / startup delay (configuration point, not the timers) - * outletControlShutoffDelay // FIXME-Check on real device - * outletControlSequenceDelay // FIXME-Check on real device ; verify OID is one component shorter? + /* Per-outlet shutdown / startup delay (configuration point, not the timers) + * outletControlShutoffDelay.0.3 = INTEGER: 120 + * outletControlSequenceDelay.0.8 = INTEGER: 8 + * (by default each output socket startup is delayed by its number in seconds) */ { "outlet.%i.delay.shutdown", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.10.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, { "outlet.%i.delay.start", ST_FLAG_RW, 1, - ".1.3.6.1.4.1.534.6.6.7.6.1.7.%i.%i", + ".1.3.6.1.4.1.534.6.6.7.6.6.1.7.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, /* TODO: handle delays From 4ef45b112e3ebc8b7fd7278be2b056235978a970 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 13 Oct 2017 11:10:08 +0200 Subject: [PATCH 221/700] eaton-pdu-marlin-mib.c : fix OID and raise questions on input.power.nominal --- drivers/eaton-pdu-marlin-mib.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 7ef6dfae52..2c1af6e59d 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -652,10 +652,15 @@ static snmp_info_t eaton_marlin_mib[] = { /* ePDU "nominal power", inputPowerCapacity with .1.3.6.1.4.1.534.6.6.7.3.4.1.9.x.1.y * using "input.power.nominal" (need RFC on NUT) * TODO: sample says of ...x.1.y and no %i in the key name... - * inputPowerCapacity = INTEGER: -1 // FIXME: Test on HW that provides it + * TODO: is this daisy-chainable? Are there more values for multi-input + * devices? How to name it "right" then? + * inputPowerCapacity.0.1 = INTEGER: -1 + * $ snmpwalk -O0n -v1 -c public EMAB33.localdomain .1.3.6.1.4.1.534.6.6.7.3.5.1.9 + * .1.3.6.1.4.1.534.6.6.7.3.5.1.9.0.1 = INTEGER: 2300 */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ - { "input.power.nominal", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.9.%i.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "input.power.nominal", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.9.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, /* Ambient collection */ { "ambient.present", ST_FLAG_STRING, SU_INFOSIZE, From 5cfbde7743f92d29fd388f47df19cccc2573086d Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Fri, 13 Oct 2017 14:29:58 +0200 Subject: [PATCH 222/700] Problem: Need to fix published feed variables Solution: Attach 1 feed to the current input Signed-off-by: Arnaud Quette --- drivers/eaton-pdu-marlin-mib.c | 36 ++++++++++------------------------ 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 2c1af6e59d..a96fe4886c 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -616,48 +616,32 @@ static snmp_info_t eaton_marlin_mib[] = { ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - /* Input feeds need 2 static declarations with last %i in [1,2]: + /* Input feed: a feed (A or B) is tied to an input. * iterators are currently available for daisychain devs, outlets, - * and groups - but not for inputs nor feeds. */ - /* Feed ID to which the ePDU is connected - * ??? // FIXME: Numeric value not provided by FW at this time - */ + * and groups - but not for inputs. */ /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags */ /* { "input.feed.%i.id", 0, 1, "???.%i.%i", NULL, SU_FLAG_NEGINVALID, NULL }, */ /* Feed name(s) of the ePDU power input(s), can be set by user (FIXME: rename to .desc?) * inputFeedName.0.1 = Value (OctetString): Feed A */ /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => refreshed from time to time or upon call to setvar */ -/* { "input.feed.%i.name", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, +/* { "input.%i.feed.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, * ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.%i", * NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, */ - { "input.feed.1.name", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + { "input.feed.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.1", - NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, - { "input.feed.2.name", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.2", - NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, - /* Feed A / B color (integer RGB) + NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + /* Feed color (integer RGB) * inputFeedColor.0.1 = Gauge32: 0 (black) */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ -/* { "input.feed.%i.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.%i", - * NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, +/* { "input.%i.feed.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.%i", + * NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL}, */ - { "input.feed.1.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.1", - NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, - { "input.feed.2.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.2", + { "input.feed.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, - /* ePDU "nominal power", inputPowerCapacity with .1.3.6.1.4.1.534.6.6.7.3.4.1.9.x.1.y - * using "input.power.nominal" (need RFC on NUT) - * TODO: sample says of ...x.1.y and no %i in the key name... - * TODO: is this daisy-chainable? Are there more values for multi-input - * devices? How to name it "right" then? - * inputPowerCapacity.0.1 = INTEGER: -1 - * $ snmpwalk -O0n -v1 -c public EMAB33.localdomain .1.3.6.1.4.1.534.6.6.7.3.5.1.9 - * .1.3.6.1.4.1.534.6.6.7.3.5.1.9.0.1 = INTEGER: 2300 - */ + /* inputPowerCapacity.0.1 = INTEGER: 2300 */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ { "input.power.nominal", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.9.%i.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, From 6ab6607e4b245bda789d475a60cfe96ab4f7c20a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 13 Oct 2017 14:59:52 +0200 Subject: [PATCH 223/700] eaton-pdu-marlin-mib.c : updated comments about input/feed relationship, and daisychain implications --- drivers/eaton-pdu-marlin-mib.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index a96fe4886c..7c6f306a2d 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -342,16 +342,27 @@ static snmp_info_t eaton_marlin_mib[] = { */ /* Input collection */ + /* Note: a larger ePDU can have several inputs. The "%i" iterators + * in key names are currently available for daisychain devs, outlets, + * and groups - but not for inputs. These would likely evolve later + * to "input.%i.something" with default (non-%i) same as .1 instance. + * At this time only a single-input (or first of several inputs) is + * supported by this mapping. + */ /* Historically, some of these data were previously published as * outlet.{realpower,...} * However, it's more suitable and logic to have these on input.{...} */ /* Note: the below gives the number of input, not the number of phase(s)! */ /* inputCount.0; Value (Integer): 1 - { "input.count", 0, 1, + { "input.phases", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.20.0", - NULL, SU_FLAG_STATIC, NULL }, */ - /* Note: for daisychain mode, we must handle phase(s) per device, not as a whole */ + NULL, SU_FLAG_STATIC | SU_FLAG_SETINT, NULL, &input_phases }, */ + /* Note: for daisychain mode, we must handle phase(s) per device, + * not as a whole. In case of daisychain, support of the UNIQUE + * field is not yet implemented (FIXME) so the last resolved OID + * value wins. If a more-preferable OID is not implemented by device, + * this is ok - the previous available value remains in place. */ /* inputType.%i.1 = INTEGER: singlePhase (1) */ { "input.phases", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.2.%i.1", @@ -616,9 +627,9 @@ static snmp_info_t eaton_marlin_mib[] = { ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, - /* Input feed: a feed (A or B) is tied to an input. - * iterators are currently available for daisychain devs, outlets, - * and groups - but not for inputs. */ + /* Input feed: a feed (A or B) is tied to an input, and this + * sub-collection describes the properties of an actual cable. + */ /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags */ /* { "input.feed.%i.id", 0, 1, "???.%i.%i", NULL, SU_FLAG_NEGINVALID, NULL }, */ /* Feed name(s) of the ePDU power input(s), can be set by user (FIXME: rename to .desc?) From 184f7fc5b1918f0bcdaff31284da80a5bf887476 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 11 Oct 2017 02:13:10 +0200 Subject: [PATCH 224/700] eaton-pdu-marlin-* : add support for extended fun/nuf l2s/s2l conversions --- drivers/eaton-pdu-marlin-helpers.c | 14 +++++++------- drivers/eaton-pdu-marlin-helpers.h | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index 5a96a3b6cf..45e3f83845 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -45,7 +45,7 @@ static char marlin_scratch_buf[20]; * (not outlet-section nor user-defined!), and for now, there * is a maximum of 6 gangs (electrical groups). */ -const char *marlin_outlet_group_phase_fun(int outlet_group_nb) +const char *marlin_outlet_group_phase_fun(long outlet_group_nb) { const char* str_phases_nb = dstate_getinfo("input.phases"); int phases_nb = 1; @@ -56,9 +56,9 @@ const char *marlin_outlet_group_phase_fun(int outlet_group_nb) } else { /* 3ph assumed, 2ph PDU don't exist - at least not in Eaton Marlin lineup! */ if (outlet_group_nb > 3) - snprintf(marlin_scratch_buf, 3, "L%i", (outlet_group_nb - 3)); /* FIXME: For more than 6 ports, maybe "nb % 3"? */ + snprintf(marlin_scratch_buf, 3, "L%li", (outlet_group_nb - 3)); /* FIXME: For more than 6 ports, maybe "nb % 3"? */ else - snprintf(marlin_scratch_buf, 3, "L%i", outlet_group_nb); + snprintf(marlin_scratch_buf, 3, "L%li", outlet_group_nb); return marlin_scratch_buf; } @@ -67,10 +67,10 @@ const char *marlin_outlet_group_phase_fun(int outlet_group_nb) } /* Take the value received from MIB, convert to string and add a prefix */ -const char *marlin_outlet_group_phase_prefix_fun(int outlet_group_input_phase) +const char *marlin_outlet_group_phase_prefix_fun(long outlet_group_input_phase) { if (outlet_group_input_phase >= 1 && outlet_group_input_phase <= 3) { - snprintf(marlin_scratch_buf, 3, "L%i", outlet_group_input_phase); + snprintf(marlin_scratch_buf, 3, "L%li", outlet_group_input_phase); return marlin_scratch_buf; } return NULL; @@ -78,9 +78,9 @@ const char *marlin_outlet_group_phase_prefix_fun(int outlet_group_input_phase) /* Take string "unitsPresent" (ex: "0,3,4,5"), and count the amount * of "," separators+1 using an inline function */ -const int marlin_device_count_fun(const char *daisy_dev_list) +long marlin_device_count_fun(const char *daisy_dev_list) { - int count = 0, i; + long count = 0, i; for (i=0; daisy_dev_list[i] != '\0'; i++) { if (daisy_dev_list[i] == ',') { /* Each comma means a new device in the list */ diff --git a/drivers/eaton-pdu-marlin-helpers.h b/drivers/eaton-pdu-marlin-helpers.h index 603bea19f4..a64c9538c0 100644 --- a/drivers/eaton-pdu-marlin-helpers.h +++ b/drivers/eaton-pdu-marlin-helpers.h @@ -23,9 +23,9 @@ #ifndef EATON_EPDU_MARLIN_HELPERS_H #define EATON_EPDU_MARLIN_HELPERS_H -const char *marlin_outlet_group_phase_fun(int outlet_group_nb); -const char *marlin_outlet_group_phase_prefix_fun(int outlet_group_input_phase); +const char *marlin_outlet_group_phase_fun(long outlet_group_nb); +const char *marlin_outlet_group_phase_prefix_fun(long outlet_group_input_phase); -const int marlin_device_count_fun(const char *daisy_dev_list); +long marlin_device_count_fun(const char *daisy_dev_list); #endif /* EATON_EPDU_MARLIN_HELPERS_H */ From a2b8b5e0eae031ef0ebfcd04e06f5d370f53f912 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Tue, 17 Oct 2017 08:33:22 +0200 Subject: [PATCH 225/700] snmp-ups: simplify Eaton ePDU group phase handling Signed-off-by: Arnaud Quette --- drivers/eaton-pdu-marlin-helpers.c | 41 ------------- drivers/eaton-pdu-marlin-helpers.h | 3 - drivers/eaton-pdu-marlin-mib.c | 93 +++++++----------------------- 3 files changed, 22 insertions(+), 115 deletions(-) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index 45e3f83845..9e86574011 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -35,47 +35,6 @@ #include "eaton-pdu-marlin-helpers.h" #include "dstate.h" -static char marlin_scratch_buf[20]; - -/* Compute the phase to which an outlet group is connected - * WRT the number of phase(s) and the outlet group number. - * Note that the group type (marlin_outlet_group_type_info) - * is not considered since this applies to any kind of group. - * This trick limits input phase to electrical groups only - * (not outlet-section nor user-defined!), and for now, there - * is a maximum of 6 gangs (electrical groups). - */ -const char *marlin_outlet_group_phase_fun(long outlet_group_nb) -{ - const char* str_phases_nb = dstate_getinfo("input.phases"); - int phases_nb = 1; - if (str_phases_nb) { - phases_nb = atoi(str_phases_nb); - if (phases_nb == 1) { - return "L1"; - } - else { /* 3ph assumed, 2ph PDU don't exist - at least not in Eaton Marlin lineup! */ - if (outlet_group_nb > 3) - snprintf(marlin_scratch_buf, 3, "L%li", (outlet_group_nb - 3)); /* FIXME: For more than 6 ports, maybe "nb % 3"? */ - else - snprintf(marlin_scratch_buf, 3, "L%li", outlet_group_nb); - - return marlin_scratch_buf; - } - } - return NULL; -} - -/* Take the value received from MIB, convert to string and add a prefix */ -const char *marlin_outlet_group_phase_prefix_fun(long outlet_group_input_phase) -{ - if (outlet_group_input_phase >= 1 && outlet_group_input_phase <= 3) { - snprintf(marlin_scratch_buf, 3, "L%li", outlet_group_input_phase); - return marlin_scratch_buf; - } - return NULL; -} - /* Take string "unitsPresent" (ex: "0,3,4,5"), and count the amount * of "," separators+1 using an inline function */ long marlin_device_count_fun(const char *daisy_dev_list) diff --git a/drivers/eaton-pdu-marlin-helpers.h b/drivers/eaton-pdu-marlin-helpers.h index a64c9538c0..50a1d5e37e 100644 --- a/drivers/eaton-pdu-marlin-helpers.h +++ b/drivers/eaton-pdu-marlin-helpers.h @@ -23,9 +23,6 @@ #ifndef EATON_EPDU_MARLIN_HELPERS_H #define EATON_EPDU_MARLIN_HELPERS_H -const char *marlin_outlet_group_phase_fun(long outlet_group_nb); -const char *marlin_outlet_group_phase_prefix_fun(long outlet_group_input_phase); - long marlin_device_count_fun(const char *daisy_dev_list); #endif /* EATON_EPDU_MARLIN_HELPERS_H */ diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 7c6f306a2d..ed104fe603 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -194,16 +194,28 @@ static info_lkp_t marlin_input_type_info[] = { { 0, NULL, NULL, NULL } }; -#if WITH_SNMP_LKP_FUN -/* Note: marlin_outlet_group_phase_fun() is defined in eaton-pdu-marlin-helpers.c - * Future work for DMF might provide a same-named routine via LUA-C gateway. - */ static info_lkp_t marlin_outlet_group_phase_info[] = { - { 1, "dummy", marlin_outlet_group_phase_fun, NULL }, - { 2, "dummytwoo", marlin_outlet_group_phase_prefix_fun, NULL }, + { 0, "unknown", NULL, NULL }, /* unknown */ + { 1, "1", NULL, NULL }, /* singlePhase */ + { 2, "1-N", NULL, NULL }, /* phase1toN */ + { 3, "2-N", NULL, NULL }, /* phase2toN */ + { 4, "3-N", NULL, NULL }, /* phase3toN */ + { 5, "1-2", NULL, NULL }, /* phase1to2 */ + { 6, "2-3", NULL, NULL }, /* phase2to3 */ + { 7, "3-1", NULL, NULL }, /* phase3to1 */ { 0, NULL, NULL, NULL } }; +#if WITH_SNMP_LKP_FUN +/* Note: marlin_device_count_fun() is defined in eaton-pdu-marlin-helpers.c + * Future work for DMF might provide a same-named routine via LUA-C gateway. + */ + +# if WITH_SNMP_LKP_FUN_DUMMY +long marlin_device_count_fun(const char *daisy_dev_list) + { return 1; } +# endif /* WITH_SNMP_LKP_FUN_DUMMY */ + static info_lkp_t marlin_device_count_info[] = { { 1, "dummy", NULL, marlin_device_count_fun }, { 0, NULL, NULL, NULL } @@ -215,34 +227,6 @@ static info_lkp_t marlin_device_count_info[] = { * lookup/mapping tables for this, which can easily go into the DMF XML file. */ -/* Ugly trick which limits input phase to electrical groups */ -static info_lkp_t marlin_outlet_group_phase1_info[] = { - /* { 0, NULL }, unknown */ - { 1, "L1" }, /* breaker1pole */ - { 2, "L1" }, /* breaker2pole */ - { 3, "L1" }, /* breaker3pole */ - /* { 4, NULL }, outlet-section */ - /* { 5, NULL }, user-defined */ - { 0, NULL } -}; -static info_lkp_t marlin_outlet_group_phase2_info[] = { - /* { 0, NULL }, unknown */ - { 1, "L2" }, /* breaker1pole */ - { 2, "L2" }, /* breaker2pole */ - { 3, "L2" }, /* breaker3pole */ - /* { 4, NULL }, outlet-section */ - /* { 5, NULL }, user-defined */ - { 0, NULL } -}; -static info_lkp_t marlin_outlet_group_phase3_info[] = { - /* { 0, NULL }, unknown */ - { 1, "L3" }, /* breaker1pole */ - { 2, "L3" }, /* breaker2pole */ - { 3, "L3" }, /* breaker3pole */ - /* { 4, NULL }, outlet-section */ - /* { 5, NULL }, user-defined */ -}; - #endif /* WITH_SNMP_LKP_FUN */ @@ -906,48 +890,15 @@ static snmp_info_t eaton_marlin_mib[] = { ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, &marlin_outlet_group_type_info[0], NULL }, -#if WITH_SNMP_LKP_FUN - /* Phase to which an outlet-group is connected - * (Caution: the value is numeric, and need to append "L" -- TODO) - * groupPhaseID: // FIXME-Check on real device + /* Phase to which an outlet-group is connected: + * We use the following OID, which gives the voltage measurement type + * groupVoltageMeasType.0.1; Value (Integer): singlePhase (1) */ /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags (daisy?) */ { "outlet.group.%i.phase", 0, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.10.%i.%i", + ".1.3.6.1.4.1.534.6.6.7.5.3.1.2.%i.%i", NULL, SU_FLAG_UNIQUE | SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, &marlin_outlet_group_phase_info[1], NULL }, - { "outlet.group.%i.phase", 0, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.%i", - NULL, SU_FLAG_UNIQUE | SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &marlin_outlet_group_phase_info[0], NULL }, -#else /* not WITH_SNMP_LKP_FUN */ - /* ugly trick which limits input phase to electrical groups only (not outlet-section nor user-defined!) - * For now, there is a maximum of 6 gangs (electrical groups) */ - { "outlet.group.1.phase", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.1", - NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, - &marlin_outlet_group_phase1_info[0], NULL }, - { "outlet.group.2.phase", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.2", - NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, - &marlin_outlet_group_phase2_info[0], NULL }, - { "outlet.group.3.phase", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.3", - NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, - &marlin_outlet_group_phase3_info[0], NULL }, - { "outlet.group.4.phase", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.4", - NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, - &marlin_outlet_group_phase1_info[0], NULL }, - { "outlet.group.5.phase", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.5", - NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, - &marlin_outlet_group_phase2_info[0], NULL }, - { "outlet.group.6.phase", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.6", - NULL, SU_FLAG_STATIC | SU_TYPE_DAISY_1, - &marlin_outlet_group_phase3_info[0], NULL }, -#endif // WITH_SNMP_LKP_FUN /* groupControlStatus.0.1 = Integer: on (1) */ { "outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.6.1.2.%i.%i", From 88839b43a09b43970c7edf602523b17ac90558fd Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Tue, 17 Oct 2017 10:45:36 +0200 Subject: [PATCH 226/700] snmp-ups: fix Eaton ePDU group phase handling Remove the not needed SU_FLAG_UNIQUE and the erroneous value lookup structure index Signed-off-by: Arnaud Quette --- drivers/eaton-pdu-marlin-mib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index ed104fe603..5681c2ad90 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -897,8 +897,8 @@ static snmp_info_t eaton_marlin_mib[] = { /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags (daisy?) */ { "outlet.group.%i.phase", 0, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.3.1.2.%i.%i", - NULL, SU_FLAG_UNIQUE | SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &marlin_outlet_group_phase_info[1], NULL }, + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &marlin_outlet_group_phase_info[0], NULL }, /* groupControlStatus.0.1 = Integer: on (1) */ { "outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.6.1.2.%i.%i", From 61c5e2120c83a7c3e53ac0d8cadc911661769185 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 25 Feb 2022 21:14:11 +0100 Subject: [PATCH 227/700] drivers/eaton-pdu-marlin-mib.c: break long lines --- drivers/eaton-pdu-marlin-mib.c | 142 ++++++++++++++++++++++----------- 1 file changed, 95 insertions(+), 47 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 5681c2ad90..9b0e8508bb 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -341,7 +341,9 @@ static snmp_info_t eaton_marlin_mib[] = { /* inputCount.0; Value (Integer): 1 { "input.phases", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.20.0", - NULL, SU_FLAG_STATIC | SU_FLAG_SETINT, NULL, &input_phases }, */ + NULL, SU_FLAG_STATIC | SU_FLAG_SETINT, NULL + //, &input_phases + }, */ /* Note: for daisychain mode, we must handle phase(s) per device, * not as a whole. In case of daisychain, support of the UNIQUE * field is not yet implemented (FIXME) so the last resolved OID @@ -350,7 +352,8 @@ static snmp_info_t eaton_marlin_mib[] = { /* inputType.%i.1 = INTEGER: singlePhase (1) */ { "input.phases", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.2.%i.1", - NULL, SU_FLAG_STATIC, &marlin_input_type_info[0] }, + NULL, SU_FLAG_STATIC, + &marlin_input_type_info[0] }, /* Frequency is measured globally */ { "input.frequency", 0, 0.1, @@ -358,10 +361,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.frequency.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.4.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_frequency_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_frequency_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.4.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_frequency_alarm_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_frequency_alarm_info[0] }, /* inputCurrentPercentLoad (measured globally) * Current percent load, based on the rated current capacity */ @@ -391,10 +396,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, { "input.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.1", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -412,10 +419,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L1.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, { "input.L1.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.1", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -433,10 +442,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L2.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, { "input.L2.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.2", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -454,10 +465,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L3.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, { "input.L3.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.3", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -480,10 +493,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, { "input.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.1", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -504,10 +519,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L1.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, { "input.L1.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.1", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -528,10 +545,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L2.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.2", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, { "input.L2.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.2", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -552,10 +571,12 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, 0, NULL }, { "input.L3.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.3", - NULL, SU_FLAG_OK, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, { "input.L3.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.3", NULL, SU_FLAG_NEGINVALID, NULL }, @@ -631,27 +652,34 @@ static snmp_info_t eaton_marlin_mib[] = { * inputFeedColor.0.1 = Gauge32: 0 (black) */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ -/* { "input.%i.feed.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.%i", +/* { "input.%i.feed.color", 0, 1, + * ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.%i", * NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL}, */ - { "input.feed.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.1", + { "input.feed.color", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, /* inputPowerCapacity.0.1 = INTEGER: 2300 */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ - { "input.power.nominal", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.9.%i.1", + { "input.power.nominal", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.9.%i.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, /* Ambient collection */ { "ambient.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.3.%i.1", - NULL, SU_FLAG_OK, &marlin_ambient_presence_info[0] }, + NULL, SU_FLAG_OK, + &marlin_ambient_presence_info[0] }, { "ambient.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_temperature_alarms_info[0] }, - { "ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.4.%i.1", + NULL, SU_FLAG_OK, + &marlin_threshold_temperature_alarms_info[0] }, + { "ambient.temperature", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.1.1.4.%i.1", NULL, SU_FLAG_OK, NULL }, /* Low and high threshold use the respective critical levels */ { "ambient.temperature.low", ST_FLAG_RW, 0.1, @@ -674,11 +702,14 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, { "ambient.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.2.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_status_info[0] }, + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.2.1.5.%i.1", - NULL, SU_FLAG_OK, &marlin_threshold_humidity_alarms_info[0] }, - { "ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.4.%i.1", + NULL, SU_FLAG_OK, + &marlin_threshold_humidity_alarms_info[0] }, + { "ambient.humidity", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.2.1.4.%i.1", NULL, SU_FLAG_OK, NULL }, /* Low and high threshold use the respective critical levels */ { "ambient.humidity.low", ST_FLAG_RW, 0.1, @@ -702,10 +733,12 @@ static snmp_info_t eaton_marlin_mib[] = { /* Dry contacts on TH module */ { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.3.1.4.%i.1", - NULL, SU_FLAG_OK, &marlin_ambient_drycontacts_info[0] }, + NULL, SU_FLAG_OK, + &marlin_ambient_drycontacts_info[0] }, { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.3.1.4.%i.2", - NULL, SU_FLAG_OK, &marlin_ambient_drycontacts_info[0] }, + NULL, SU_FLAG_OK, + &marlin_ambient_drycontacts_info[0] }, /* Outlet collection */ { "outlet.count", 0, 1, @@ -722,11 +755,13 @@ static snmp_info_t eaton_marlin_mib[] = { * used to depict the overall outlets switchability of the unit on G3 and newer ePDU*/ { "outlet.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.10.%i", - "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE, &marlin_unit_switchability_info[0] }, + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE, + &marlin_unit_switchability_info[0] }, /* Ugly hack for older G2 ePDU: check the first outlet to determine unit switchability */ { "outlet.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.1", - "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_TYPE_DAISY_1, &g2_unit_outlet_switchability_info[0] }, + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_TYPE_DAISY_1, + &g2_unit_outlet_switchability_info[0] }, /* The below ones are the same as the input.* equivalent */ /* FIXME: transition period, TO BE REMOVED, moved to input.* */ { "outlet.frequency", 0, 0.1, @@ -754,7 +789,8 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.2.%i.%i", - NULL, SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, &marlin_outlet_status_info[0] }, + NULL, SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_outlet_status_info[0] }, /* Numeric identifier of the outlet, tied to the whole unit */ { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.7.%i.%i", @@ -793,14 +829,17 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.6", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.3.%i.%i", + { "outlet.%i.current", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.4.1.3.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, { "outlet.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.4.1.4.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_status_info[0] }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_status_info[0] }, { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.4.1.4.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_current_alarms_info[0] }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_current_alarms_info[0] }, { "outlet.%i.current.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.5.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, @@ -813,16 +852,20 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.%i.current.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.8.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.6.5.1.3.%i.%i", + { "outlet.%i.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.6.5.1.3.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.2.%i.%i", + { "outlet.%i.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.3.1.2.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, { "outlet.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.3.1.3.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_status_info[0] }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_status_info[0] }, { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.3.1.3.%i.%i", - NULL, SU_OUTLET | SU_TYPE_DAISY_1, &marlin_threshold_voltage_alarms_info[0] }, + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_voltage_alarms_info[0] }, { "outlet.%i.voltage.low.warning", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.4.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, @@ -835,19 +878,23 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.%i.voltage.high.critical", ST_FLAG_RW, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.7.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.6.5.1.2.%i.%i", + { "outlet.%i.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.6.5.1.2.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, /* outletControlSwitchable */ { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.9.%i.%i", - "no", SU_OUTLET | SU_FLAG_UNIQUE | SU_TYPE_DAISY_1, &marlin_outlet_switchability_info[0] }, + "no", SU_OUTLET | SU_FLAG_UNIQUE | SU_TYPE_DAISY_1, + &marlin_outlet_switchability_info[0] }, /* FIXME: handle non switchable units (only measurements), which do not expose this OID */ { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", - "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_FLAG_OK | SU_TYPE_DAISY_1, &g2_unit_outlet_switchability_info[0] }, + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_FLAG_OK | SU_TYPE_DAISY_1, + &g2_unit_outlet_switchability_info[0] }, { "outlet.%i.type", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.5.%i.%i", - "unknown", SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, &marlin_outlet_type_info[0] }, + "unknown", SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_outlet_type_info[0] }, /* TODO: handle statistics * outletWh.0.1 @@ -855,7 +902,8 @@ static snmp_info_t eaton_marlin_mib[] = { */ /* Outlet groups collection */ - { "outlet.group.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.21.%i", + { "outlet.group.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.21.%i", "0", SU_FLAG_STATIC | SU_TYPE_DAISY_1, NULL }, /* outlet groups template definition * Indexes start from 1, ie outlet.group.1 => .1 */ From 6bf88fbc094a4c9160184bdf41e92265ba808efb Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 25 Feb 2022 21:33:40 +0100 Subject: [PATCH 228/700] drivers/eaton-pdu-marlin-mib.c: break long lines like in FTY --- drivers/eaton-pdu-marlin-mib.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 9b0e8508bb..24941698fc 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -295,8 +295,9 @@ static snmp_info_t eaton_marlin_mib[] = { , NULL /* devices_count */ }, /* UPS collection */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, @@ -392,7 +393,8 @@ static snmp_info_t eaton_marlin_mib[] = { * This is depending on OID inputVoltageMeasType * INTEGER {singlePhase (1),phase1toN (2),phase2toN (3),phase3toN (4),phase1to2 (5),phase2to3 (6),phase3to1 (7) * => RFC input.Lx.voltage.context */ - { "input.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", + { "input.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", NULL, 0, NULL }, { "input.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", @@ -654,7 +656,7 @@ static snmp_info_t eaton_marlin_mib[] = { /* FIXME: RFC on key name is needed when backporting to NUT upstream */ /* { "input.%i.feed.color", 0, 1, * ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.%i", - * NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL}, + * NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, */ { "input.feed.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.1", @@ -786,7 +788,8 @@ static snmp_info_t eaton_marlin_mib[] = { /* outletName: Outlet friendly name, which can be modified by the user */ { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.3.%i.%i", - NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + NULL }, { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.2.%i.%i", NULL, SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, @@ -794,7 +797,8 @@ static snmp_info_t eaton_marlin_mib[] = { /* Numeric identifier of the outlet, tied to the whole unit */ { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.7.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, + NULL }, /* outletID: Outlet physical name, related to its number in the group * ex: first outlet of the second group (B) is B1 */ /* Outlet physical name OID in new G3 firmware (02.00.0051) @@ -803,12 +807,12 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.6.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, - NULL, NULL }, + NULL }, /* Fallback in firmwares issued before Sep 2017: */ { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.2.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, - NULL, NULL }, + NULL }, /* FIXME: the last part of the OID gives the group number (i.e. %i.1 means "group 1") * Need to address that, without multiple declaration (%i.%i, SU_OUTLET | SU_OUTLET_GROUP)? */ { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, @@ -917,7 +921,8 @@ static snmp_info_t eaton_marlin_mib[] = { /* User-friendly (writeable) description of the outlet group: */ { "outlet.group.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.3.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, /* Outlet-group physical name, a read-only string, * is named groupDesignator (other MIBs groupPhysicalName) * groupPhysicalName.0.1 = Value (OctetString): A @@ -925,19 +930,21 @@ static snmp_info_t eaton_marlin_mib[] = { */ { "outlet.group.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.8.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, /* Outlet-group color: groupColor (other MIBs groupBkgColor) * groupColor.0.1 = Value (Gauge32): 16051527 (0xF4ED47) */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ { "outlet.group.%i.color", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.7.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, /* groupType.0.1 = Integer: outletSection (4) */ { "outlet.group.%i.type", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &marlin_outlet_group_type_info[0], NULL }, + &marlin_outlet_group_type_info[0] }, /* Phase to which an outlet-group is connected: * We use the following OID, which gives the voltage measurement type * groupVoltageMeasType.0.1; Value (Integer): singlePhase (1) @@ -946,7 +953,7 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.group.%i.phase", 0, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.3.1.2.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, - &marlin_outlet_group_phase_info[0], NULL }, + &marlin_outlet_group_phase_info[0] }, /* groupControlStatus.0.1 = Integer: on (1) */ { "outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.6.1.2.%i.%i", From 3c553301e84e0c020d7d7548b91b0a35b909d784 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 25 Feb 2022 21:33:59 +0100 Subject: [PATCH 229/700] drivers/eaton-pdu-marlin-mib.c: relocate outlet.%i.load.off.delay etc like in FTY --- drivers/eaton-pdu-marlin-mib.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 24941698fc..7b62aefc5c 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -1068,16 +1068,6 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - /* Delayed version, parameter is mandatory (so dfl is NULL)! */ - { "outlet.%i.load.off.delay", 0, 1, - ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", - NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.on.delay", 0, 1, - ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", - NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - { "outlet.%i.load.cycle.delay", 0, 1, - ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", - NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, /* Per-outlet shutdown / startup delay (configuration point, not the timers) * outletControlShutoffDelay.0.3 = INTEGER: 120 @@ -1091,7 +1081,18 @@ static snmp_info_t eaton_marlin_mib[] = { ".1.3.6.1.4.1.534.6.6.7.6.6.1.7.%i.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - /* TODO: handle delays + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + { "outlet.%i.load.off.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", + NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.load.on.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", + NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.load.cycle.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", + NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + + /* Delays handling: * 0-n :Time in seconds until the group command is issued * -1:Cancel a pending group-level Off/On/Reboot command */ /* groupControlOffCmd.0.1 = Integer: -1 */ From bcee36df8565648b05066bc5e95d3321d952644a Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Mon, 30 Oct 2017 12:47:10 +0100 Subject: [PATCH 230/700] eaton-pdu-marlin-mib.c/dmf: add outlet timers Add support for shutdown and start timers Signed-off-by: Arnaud Quette --- drivers/eaton-pdu-marlin-mib.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 7b62aefc5c..2cca3f23f7 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -1092,6 +1092,17 @@ static snmp_info_t eaton_marlin_mib[] = { ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* Per-outlet shutdown / startup timers + * outletControlOffCmd.0.1 = INTEGER: -1 + * outletControlOnCmd.0.1 = INTEGER: -1 + */ + { "outlet.%i.timer.shutdown", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.timer.start", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* Delays handling: * 0-n :Time in seconds until the group command is issued * -1:Cancel a pending group-level Off/On/Reboot command */ From 84d0d52181e7622572b3a45e6bb91bd5121ba5d0 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Tue, 26 Jun 2018 10:58:07 +0200 Subject: [PATCH 231/700] snmp-ups: Eaton ePDU input.power.nominal is realpower Fix data name, since the published value is in Watts, so realpower, not power Signed-off-by: Arnaud Quette --- docs/nut-names.txt | 2 ++ drivers/eaton-pdu-marlin-mib.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/nut-names.txt b/docs/nut-names.txt index 187f7b7efb..58db63e4de 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -241,6 +241,8 @@ input: Incoming line/power information of full) | 25 | input.realpower | Current sum value of all (ePDU) phases real power (W) | 300 +| input.realpower.nominal | Nominal sum value of all (ePDU) + phases real power (W) | 850 | input.power | Current sum value of all (ePDU) phases apparent power (VA) | 500 | input.source | The current input power source | 1 diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 2cca3f23f7..a2cec03e8e 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -36,7 +36,7 @@ /* Eaton PDU-MIB - Marlin MIB * ************************** */ -#define EATON_MARLIN_MIB_VERSION "0.60" +#define EATON_MARLIN_MIB_VERSION "0.61" #define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" #define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" @@ -663,7 +663,7 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, /* inputPowerCapacity.0.1 = INTEGER: 2300 */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ - { "input.power.nominal", 0, 1.0, + { "input.realpower.nominal", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.5.1.9.%i.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, From f917d3dc5b320a350fdfb9d23fc0a279398b15ba Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Thu, 2 Aug 2018 11:09:15 +0200 Subject: [PATCH 232/700] snmp-ups: Eaton feed color is semi static Signed-off-by: Arnaud Quette --- drivers/eaton-pdu-marlin-mib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index a2cec03e8e..e8253d1aa4 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -36,7 +36,7 @@ /* Eaton PDU-MIB - Marlin MIB * ************************** */ -#define EATON_MARLIN_MIB_VERSION "0.61" +#define EATON_MARLIN_MIB_VERSION "0.62" #define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" #define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" @@ -660,7 +660,7 @@ static snmp_info_t eaton_marlin_mib[] = { */ { "input.feed.color", 0, 1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.1", - NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, /* inputPowerCapacity.0.1 = INTEGER: 2300 */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ { "input.realpower.nominal", 0, 1.0, From fe37b2243bb46bb79f96c228295f208dff0c128d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 25 Feb 2022 22:51:21 +0100 Subject: [PATCH 233/700] drivers/snmp-ups.h: avoid "#if" with possibly not-defined macro --- drivers/snmp-ups.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index 508c5f4e8f..0f5f8c1d53 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -117,7 +117,7 @@ typedef int bool_t; * this macro to a specific value while building the codebase and see * what happens under different conditions ;) */ -# if WITH_DMFMIB +# if (defined WITH_DMFMIB) && (WITH_DMFMIB != 0) # define WITH_SNMP_LKP_FUN 0 # else # define WITH_SNMP_LKP_FUN 1 From f4c44f414c8b6c9b1a7432005cac767e39915bc3 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Fri, 3 Nov 2017 12:14:34 +0100 Subject: [PATCH 234/700] Add support for dry contacts to Eaton ATS16 and UPS Add support for the 2 GPI accessible through EMP001 environmental sensor, connected to a UPS or ATS16. The same is already available for Eaton ePDU. This affect the snmp-ups driver (eaton_ats16 and pw/pxgx_ups MIBs), and the netxml-ups driver Signed-off-by: Arnaud Quette --- drivers/eaton-ats16-nm2-mib.c | 20 +++++++++++++++++++- drivers/eaton-ats16-nmc-mib.c | 15 ++++++++++----- drivers/mge-mib.c | 13 ++++++++++++- drivers/mge-xml.c | 23 ++++++++++++++++++++++- drivers/powerware-mib.c | 8 +++++++- 5 files changed, 70 insertions(+), 9 deletions(-) diff --git a/drivers/eaton-ats16-nm2-mib.c b/drivers/eaton-ats16-nm2-mib.c index 256fd4359e..1980c30039 100644 --- a/drivers/eaton-ats16-nm2-mib.c +++ b/drivers/eaton-ats16-nm2-mib.c @@ -25,7 +25,7 @@ #include "eaton-ats16-nm2-mib.h" -#define EATON_ATS16_NM2_MIB_VERSION "0.21" +#define EATON_ATS16_NM2_MIB_VERSION "0.22" #define EATON_ATS16_NM2_SYSOID ".1.3.6.1.4.1.534.10.2" /* newer Network-M2 */ #define EATON_ATS16_NM2_MODEL ".1.3.6.1.4.1.534.10.2.1.2.0" @@ -78,6 +78,15 @@ static info_lkp_t eaton_ats16_nm2_output_status_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t eaton_ats16_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 1, "opened", NULL, NULL }, + { 2, "closed", NULL, NULL }, + { 3, "opened", NULL, NULL }, /* openWithNotice */ + { 4, "closed", NULL, NULL }, /* closedWithNotice */ + { 0, NULL, NULL, NULL } +}; + /* EATON_ATS Snmp2NUT lookup table */ static snmp_info_t eaton_ats16_nm2_mib[] = { @@ -167,6 +176,15 @@ static snmp_info_t eaton_ats16_nm2_mib[] = { { "ambient.humidity.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.7.0", NULL, SU_FLAG_OK, NULL }, /* ats2EnvRemoteHumidityUpperLimit.0 = INTEGER: 90 percent */ { "ambient.humidity.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.8.0", NULL, SU_FLAG_OK, NULL }, + /* Dry contacts on EMP001 TH module */ + /* ats2ContactState.1 = INTEGER: open(1) */ + { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.10.2.5.4.1.3.1", + NULL, SU_FLAG_OK, &eaton_ats16_ambient_drycontacts_info[0] }, + /* ats2ContactState.2 = INTEGER: open(1) */ + { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.10.2.5.4.1.3.2", + NULL, SU_FLAG_OK, &eaton_ats16_ambient_drycontacts_info[0] }, #if 0 /* FIXME: Remaining data to be processed */ /* ats2InputStatusDephasing.0 = INTEGER: normal(1) */ diff --git a/drivers/eaton-ats16-nmc-mib.c b/drivers/eaton-ats16-nmc-mib.c index 3b75c0a5dd..0897ea056c 100644 --- a/drivers/eaton-ats16-nmc-mib.c +++ b/drivers/eaton-ats16-nmc-mib.c @@ -25,7 +25,7 @@ #include "eaton-ats16-nmc-mib.h" -#define EATON_ATS16_NMC_MIB_VERSION "0.20" +#define EATON_ATS16_NMC_MIB_VERSION "0.21" #define EATON_ATS16_NMC_SYSOID ".1.3.6.1.4.1.705.1" /* legacy NMC */ #define EATON_ATS16_NMC_MODEL ".1.3.6.1.4.1.534.10.2.1.2.0" @@ -167,6 +167,15 @@ static snmp_info_t eaton_ats16_nmc_mib[] = { { "ambient.humidity.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.7.0", NULL, SU_FLAG_OK, NULL }, /* ats2EnvRemoteHumidityUpperLimit.0 = INTEGER: 90 percent */ { "ambient.humidity.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.8.0", NULL, SU_FLAG_OK, NULL }, + /* Dry contacts on EMP001 TH module */ + /* ats2ContactState.1 = INTEGER: open(1) */ + { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.10.2.5.4.1.3.1", + NULL, SU_FLAG_OK, &eaton_ats16_ambient_drycontacts_info[0] }, + /* ats2ContactState.2 = INTEGER: open(1) */ + { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.10.2.5.4.1.3.2", + NULL, SU_FLAG_OK, &eaton_ats16_ambient_drycontacts_info[0]}, #if 0 /* FIXME: Remaining data to be processed */ /* ats2InputStatusDephasing.0 = INTEGER: normal(1) */ @@ -236,10 +245,6 @@ static snmp_info_t eaton_ats16_nmc_mib[] = { { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.1", NULL, SU_FLAG_OK, NULL }, /* ats2ContactType.2 = INTEGER: notUsed(4) */ { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.2", NULL, SU_FLAG_OK, NULL }, - /* ats2ContactState.1 = INTEGER: open(1) */ - { "unmapped.ats2ContactState", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.3.1", NULL, SU_FLAG_OK, NULL }, - /* ats2ContactState.2 = INTEGER: open(1) */ - { "unmapped.ats2ContactState", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.3.2", NULL, SU_FLAG_OK, NULL }, /* ats2ContactDescr.1 = STRING: Input #1 */ { "unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.1", NULL, SU_FLAG_OK, NULL }, /* ats2ContactDescr.2 = STRING: Input #2 */ diff --git a/drivers/mge-mib.c b/drivers/mge-mib.c index 4430112b0b..a683379b3b 100644 --- a/drivers/mge-mib.c +++ b/drivers/mge-mib.c @@ -27,7 +27,7 @@ #include "mge-mib.h" -#define MGE_MIB_VERSION "0.54" +#define MGE_MIB_VERSION "0.55" /* TODO: * - MGE PDU MIB and sysOID (".1.3.6.1.4.1.705.2") */ @@ -132,6 +132,13 @@ static info_lkp_t mge_power_source_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t mge_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 1, "closed", NULL, NULL }, + { 2, "open", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + /* Parameters default values */ #define DEFAULT_ONDELAY "30" /* Delay between return of utility power */ /* and powering up of load, in seconds */ @@ -244,6 +251,10 @@ static snmp_info_t mge_mib[] = { /* Ambient page: Environment Sensor (ref 66 846) */ { "ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.705.1.8.1.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, { "ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.705.1.8.2.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, + /* upsmgEnvironmentInput1State.1 */ + { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.8.7.1.9.1", "", SU_TYPE_INT | SU_FLAG_OK, mge_ambient_drycontacts_info }, + /* upsmgEnvironmentInput1State.1 */ + { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.8.7.1.10.1", "", SU_TYPE_INT | SU_FLAG_OK, mge_ambient_drycontacts_info }, /* Outlet page */ { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, diff --git a/drivers/mge-xml.c b/drivers/mge-xml.c index 8a1a3cc497..449e212b55 100644 --- a/drivers/mge-xml.c +++ b/drivers/mge-xml.c @@ -35,7 +35,7 @@ #include "mge-xml.h" #include "main.h" /* for testvar() */ -#define MGE_XML_VERSION "MGEXML/0.32" +#define MGE_XML_VERSION "MGEXML/0.33" #define MGE_XML_INITUPS "/" #define MGE_XML_INITINFO "/mgeups/product.xml /product.xml /ws/product.xml" @@ -577,6 +577,25 @@ static const char *mge_ambient_info(const char *arg_val) } } +static const char *mge_drycontact_info(const char *val) +{ + /* these values should theoretically be obtained through + * Environment.Input[1].State[x].Description + * Examples: + * open + * closed + */ + switch (atoi(val)) + { + case 0: + return "open"; + case 1: + return "closed"; + default: + return NULL; + } +} + static const char *mge_timer_shutdown(const char *delay_before_shutoff) { if (atoi(delay_before_shutoff) > -1 ) { @@ -1059,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 }, diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index 2d739347e0..90403239dd 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -25,7 +25,7 @@ #include "powerware-mib.h" -#define PW_MIB_VERSION "0.95" +#define PW_MIB_VERSION "0.96" /* TODO: more sysOID and MIBs support: * @@ -365,6 +365,12 @@ static snmp_info_t pw_mib[] = { { "ambient.humidity.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.11.0", "", 0, NULL }, /* XUPS-MIB::xupsEnvRemoteHumidityUpperLimit.0 */ { "ambient.humidity.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.12.0", "", 0, NULL }, + /* XUPS-MIB::xupsContactState.1 */ + { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.1.6.8.1.3.1", "", 0, &pw_ambient_drycontacts_info[0] }, + /* XUPS-MIB::xupsContactState.2 */ + { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.1.6.8.1.3.2", "", 0, &pw_ambient_drycontacts_info[0] }, /* instant commands */ { "test.battery.start.quick", 0, 1, PW_OID_BATTEST_START, "", From d3b6ea4d891363037e0058b5ed0d9135f17fcd53 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Fri, 17 Nov 2017 15:05:29 +0100 Subject: [PATCH 235/700] Modify "open" to "opened" for dry contacts status While "open" is the best adjective for the opposite of "closed", and thus suitable for GPI status, this may lead to confusion with the GPO actions "open|close" Vs the GPI status "opened|closed". These last are also not inapropriate, since they can refer to the fact that the GPI state has change due to some external action or event Signed-off-by: Arnaud Quette --- drivers/eaton-ats16-nmc-mib.c | 10 +++++++++- drivers/eaton-pdu-marlin-mib.c | 2 +- drivers/mge-mib.c | 2 +- drivers/mge-xml.c | 4 ++-- drivers/powerware-mib.c | 9 +++++++++ 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/eaton-ats16-nmc-mib.c b/drivers/eaton-ats16-nmc-mib.c index 0897ea056c..8cf2496bfd 100644 --- a/drivers/eaton-ats16-nmc-mib.c +++ b/drivers/eaton-ats16-nmc-mib.c @@ -78,9 +78,17 @@ static info_lkp_t eaton_ats16_nmc_output_status_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t eaton_ats16_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 1, "opened", NULL, NULL }, + { 2, "closed", NULL, NULL }, + { 3, "opened", NULL, NULL }, /* openWithNotice */ + { 4, "closed", NULL, NULL }, /* closedWithNotice */ + { 0, NULL, NULL, NULL } +}; + /* EATON_ATS_NMC Snmp2NUT lookup table */ static snmp_info_t eaton_ats16_nmc_mib[] = { - /* standard MIB items */ { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index e8253d1aa4..5573cd1a0e 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -129,7 +129,7 @@ static info_lkp_t marlin_threshold_frequency_status_info[] = { static info_lkp_t marlin_ambient_drycontacts_info[] = { { -1, "unknown", NULL, NULL }, - { 0, "open", NULL, NULL }, + { 0, "opened", NULL, NULL }, { 1, "closed", NULL, NULL }, { 0, NULL, NULL, NULL } }; diff --git a/drivers/mge-mib.c b/drivers/mge-mib.c index a683379b3b..d0a844d84f 100644 --- a/drivers/mge-mib.c +++ b/drivers/mge-mib.c @@ -135,7 +135,7 @@ static info_lkp_t mge_power_source_info[] = { static info_lkp_t mge_ambient_drycontacts_info[] = { { -1, "unknown", NULL, NULL }, { 1, "closed", NULL, NULL }, - { 2, "open", NULL, NULL }, + { 2, "opened", NULL, NULL }, { 0, NULL, NULL, NULL } }; diff --git a/drivers/mge-xml.c b/drivers/mge-xml.c index 449e212b55..af9c60b2d5 100644 --- a/drivers/mge-xml.c +++ b/drivers/mge-xml.c @@ -35,7 +35,7 @@ #include "mge-xml.h" #include "main.h" /* for testvar() */ -#define MGE_XML_VERSION "MGEXML/0.33" +#define MGE_XML_VERSION "MGEXML/0.34" #define MGE_XML_INITUPS "/" #define MGE_XML_INITINFO "/mgeups/product.xml /product.xml /ws/product.xml" @@ -588,7 +588,7 @@ static const char *mge_drycontact_info(const char *val) switch (atoi(val)) { case 0: - return "open"; + return "opened"; case 1: return "closed"; default: diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index 90403239dd..af7665daff 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -194,6 +194,15 @@ static info_lkp_t pw_yes_no_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t pw_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 1, "opened", NULL, NULL }, + { 2, "closed", NULL, NULL }, + { 3, "opened", NULL, NULL }, /* openWithNotice */ + { 4, "closed", NULL, NULL }, /* closedWithNotice */ + { 0, NULL, NULL, NULL } +}; + /* Snmp2NUT lookup table */ static snmp_info_t pw_mib[] = { From 29c6dea2702c5b76a6bbc42fd5e27e2eacc633a9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 12:41:40 +0100 Subject: [PATCH 236/700] drivers/eaton-pdu-marlin-mib.c: align with "snmp-ups: Simplify the mapping structure" changes for input.phases=>input.count --- drivers/eaton-pdu-marlin-mib.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 5573cd1a0e..0e43bcd100 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -340,11 +340,9 @@ static snmp_info_t eaton_marlin_mib[] = { */ /* Note: the below gives the number of input, not the number of phase(s)! */ /* inputCount.0; Value (Integer): 1 - { "input.phases", 0, 1, + { "input.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.20.0", - NULL, SU_FLAG_STATIC | SU_FLAG_SETINT, NULL - //, &input_phases - }, */ + NULL, SU_FLAG_STATIC, NULL }, */ /* Note: for daisychain mode, we must handle phase(s) per device, * not as a whole. In case of daisychain, support of the UNIQUE * field is not yet implemented (FIXME) so the last resolved OID From 53ad5cff36cfee38e938faa7184f41645991e13a Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Tue, 7 Aug 2018 09:13:33 +0200 Subject: [PATCH 237/700] snmp-ups: support newer Genepi management cards * duplicate some OIDs, with refinement to point at the first index (i.e ".0" or ".1.0") since otherwise the agent doesn't respond to queries. This could be fixed at the snmp-ups level later * fixed "ups.type" (power topology of the UPS) which was pointing at the output.source or ups.mode Signed-off-by: Arnaud Quette --- drivers/powerware-mib.c | 95 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 11 deletions(-) diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index af7665daff..f17f79178f 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -25,7 +25,7 @@ #include "powerware-mib.h" -#define PW_MIB_VERSION "0.96" +#define PW_MIB_VERSION "0.97" /* TODO: more sysOID and MIBs support: * @@ -35,7 +35,8 @@ * PXGX 1000 cards (PDU/RPP/RPM): Get pduNumPanels ".1.3.6.1.4.1.534.6.6.4.1.1.1.4.0" */ -/* Powerware UPS (Ingrasys X-SLOT and BD-SLOT) */ +/* Powerware UPS (Ingrasys X-SLOT and BD-SLOT) + * Eaton Gigabit Network Card (Genepi) */ #define POWERWARE_SYSOID ".1.3.6.1.4.1.534.1" /* Powerware UPS newer PXGX UPS cards (BladeUPS, ...) */ #define EATON_PXGX_SYSOID ".1.3.6.1.4.1.534.2.12" @@ -78,7 +79,6 @@ #define PW_OID_CONT_ONT_DEL "1.3.6.1.4.1.534.1.9.4" /* XUPS-MIB::xupsControlOutputOnTrapDelay */ #define PW_OID_CONT_LOAD_SHED_AND_RESTART "1.3.6.1.4.1.534.1.9.6" /* XUPS-MIB::xupsLoadShedSecsWithRestart */ -#define PW_OID_CONF_OVOLTAGE "1.3.6.1.4.1.534.1.10.1.0" /* XUPS-MIB::xupsConfigOutputVoltage.0 */ #define PW_OID_CONF_IVOLTAGE "1.3.6.1.4.1.534.1.10.2.0" /* XUPS-MIB::xupsConfigInputVoltage.0 */ #define PW_OID_CONF_POWER "1.3.6.1.4.1.534.1.10.3.0" /* XUPS-MIB::xupsConfigOutputWatts.0 */ #define PW_OID_CONF_FREQ "1.3.6.1.4.1.534.1.10.4.0" /* XUPS-MIB::xupsConfigOutputFreq.0 */ @@ -135,6 +135,9 @@ static info_lkp_t pw_pwr_info[] = { { 0, NULL, NULL, NULL } }; +/* FIXME: mapped to ups.type, but should be output.source or ups.mode (need RFC) + * to complement the above ups.status + * along with having ups.type as described hereafter*/ static info_lkp_t pw_mode_info[] = { { 1, "", NULL, NULL }, { 2, "", NULL, NULL }, @@ -146,7 +149,8 @@ static info_lkp_t pw_mode_info[] = { { 8, "parallel capacity", NULL, NULL }, { 9, "parallel redundancy", NULL, NULL }, { 10, "high efficiency", NULL, NULL }, - /* Extended status values */ + /* Extended status values, + * FIXME: check for source and completion */ { 240, "" /* battery (0xF0) */, NULL, NULL }, { 100, "" /* maintenanceBypass (0x64) */, NULL, NULL }, { 96, "" /* Bypass (0x60) */, NULL, NULL }, @@ -157,6 +161,33 @@ static info_lkp_t pw_mode_info[] = { { 0, NULL, NULL, NULL } }; +/* FIXME: may be standardized + * extracted from bcmxcp.c->BCMXCP_TOPOLOGY_*, Make some common definitions */ +static info_lkp_t pw_topology_info[] = { + { 0x0000, "", NULL, NULL }, /* None; use the Table of Elements */ + { 0x0010, "Off-line switcher, Single Phase", NULL, NULL }, + { 0x0020, "Line-Interactive UPS, Single Phase", NULL, NULL }, + { 0x0021, "Line-Interactive UPS, Two Phase", NULL, NULL }, + { 0x0022, "Line-Interactive UPS, Three Phase", NULL, NULL }, + { 0x0030, "Dual AC Input, On-Line UPS, Single Phase", NULL, NULL }, + { 0x0031, "Dual AC Input, On-Line UPS, Two Phase", NULL, NULL }, + { 0x0032, "Dual AC Input, On-Line UPS, Three Phase", NULL, NULL }, + { 0x0040, "On-Line UPS, Single Phase", NULL, NULL }, + { 0x0041, "On-Line UPS, Two Phase", NULL, NULL }, + { 0x0042, "On-Line UPS, Three Phase", NULL, NULL }, + { 0x0050, "Parallel Redundant On-Line UPS, Single Phase", NULL, NULL }, + { 0x0051, "Parallel Redundant On-Line UPS, Two Phase", NULL, NULL }, + { 0x0052, "Parallel Redundant On-Line UPS, Three Phase", NULL, NULL }, + { 0x0060, "Parallel for Capacity On-Line UPS, Single Phase", NULL, NULL }, + { 0x0061, "Parallel for Capacity On-Line UPS, Two Phase", NULL, NULL }, + { 0x0062, "Parallel for Capacity On-Line UPS, Three Phase", NULL, NULL }, + { 0x0102, "System Bypass Module, Three Phase", NULL, NULL }, + { 0x0122, "Hot-Tie Cabinet, Three Phase", NULL, NULL }, + { 0x0200, "Outlet Controller, Single Phase", NULL, NULL }, + { 0x0222, "Dual AC Input Static Switch Module, 3 Phase", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + /* Legacy implementation */ static info_lkp_t pw_battery_abm_status[] = { { 1, "CHRG", NULL, NULL }, @@ -198,7 +229,7 @@ static info_lkp_t pw_ambient_drycontacts_info[] = { { -1, "unknown", NULL, NULL }, { 1, "opened", NULL, NULL }, { 2, "closed", NULL, NULL }, - { 3, "opened", NULL, NULL }, /* openWithNotice */ + { 3, "opened", NULL, NULL }, /* openWithNotice */ { 4, "closed", NULL, NULL }, /* closedWithNotice */ { 0, NULL, NULL, NULL } }; @@ -228,8 +259,14 @@ static snmp_info_t pw_mib[] = { SU_FLAG_STATIC, NULL }, { "ups.load", 0, 1.0, PW_OID_OUT_LOAD, "", SU_OUTPUT_1, NULL }, + /* FIXME: should be removed in favor of output.power */ { "ups.power", 0, 1.0, PW_OID_OUT_POWER ".1", "", 0, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputWatts.1.0; Value (Integer): 300 */ + { "ups.power", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.4.1.0", "", + 0, NULL }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "OFF", SU_STATUS_PWR, &pw_pwr_info[0] }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_ALARM_OB, "", @@ -238,10 +275,16 @@ static snmp_info_t pw_mib[] = { SU_STATUS_BATT, &pw_alarm_lb[0] }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATT_STATUS, "", SU_STATUS_BATT, &pw_battery_abm_status[0] }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "", + /* FIXME: should be ups.mode or output.source (need RFC) */ + { "experimental.ups.type", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "", SU_FLAG_STATIC | SU_FLAG_OK, &pw_mode_info[0] }, + /* xupsTopologyType.0; Value (Integer): 32 */ + { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.13.1.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, &pw_topology_info[0] }, + /* FIXME: should be removed in favor of their output. equivalent! */ { "ups.realpower.nominal", 0, 1.0, PW_OID_CONF_POWER, "", 0, NULL }, + /* FIXME: should be removed in favor of output.power.nominal */ { "ups.power.nominal", 0, 1.0, IETF_OID_CONF_OUT_VA, "", 0, NULL }, /* XUPS-MIB::xupsEnvAmbientTemp.0 */ @@ -278,7 +321,10 @@ static snmp_info_t pw_mib[] = { /* XUPS-MIB::xupsConfigOutputFreq.0 */ { "output.frequency.nominal", 0, 0.1, "1.3.6.1.4.1.534.1.10.4.0", "", 0, NULL }, /* XUPS-MIB::xupsOutputVoltage.1 */ - { "output.voltage", 0, 1.0, ".1.3.6.1.4.1.534.1.4.4.1.2.1", "", SU_OUTPUT_1, NULL }, + { "output.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.2.1", "", SU_OUTPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputVoltage.1.0; Value (Integer): 230 */ + { "output.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.2.1.0", "", SU_OUTPUT_1, NULL }, /* XUPS-MIB::xupsConfigOutputVoltage.0 */ { "output.voltage.nominal", 0, 1.0, "1.3.6.1.4.1.534.1.10.1.0", "", 0, NULL }, /* XUPS-MIB::xupsConfigLowOutputVoltageLimit.0 */ @@ -287,8 +333,20 @@ static snmp_info_t pw_mib[] = { { "output.voltage.high", 0, 1.0, ".1.3.6.1.4.1.534.1.10.7.0", "", 0, NULL }, { "output.current", 0, 1.0, PW_OID_OUT_CURRENT ".1", "", SU_OUTPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputCurrent.1.0; Value (Integer): 0 */ + { "output.current", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.3.1.0", "", + SU_OUTPUT_1, NULL }, { "output.realpower", 0, 1.0, PW_OID_OUT_POWER ".1", "", SU_OUTPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* Name/OID: xupsOutputWatts.1.0; Value (Integer): 1200 */ + { "output.realpower", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.4.1.0", "", + 0, NULL }, + /* Duplicate of "ups.realpower.nominal" + * FIXME: map either ups or output, but not both (or have an auto-remap) */ + { "output.realpower.nominal", 0, 1.0, PW_OID_CONF_POWER, "", + 0, NULL }, { "output.L1-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".1", "", SU_OUTPUT_3, NULL }, { "output.L2-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".2", "", @@ -314,8 +372,6 @@ static snmp_info_t pw_mib[] = { SU_OUTPUT_3, NULL }, { "output.L3.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".3", "", SU_OUTPUT_3, NULL }, - { "output.voltage.nominal", 0, 1.0, PW_OID_CONF_OVOLTAGE, "", - 0, NULL }, /* Input page */ { "input.phases", 0, 1.0, PW_OID_IN_LINES, "", @@ -324,6 +380,12 @@ static snmp_info_t pw_mib[] = { 0, NULL }, { "input.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".0", "", SU_INPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsInputVoltage.1.0; Value (Integer): 245 */ + { "input.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.3.4.1.2.1.0", "", + SU_INPUT_1, NULL }, + + /* XUPS-MIB::xupsConfigInputVoltage.0 */ { "input.voltage.nominal", 0, 1.0, "1.3.6.1.4.1.534.1.10.2.0", "", 0, NULL }, { "input.current", 0, 0.1, PW_OID_IN_CURRENT ".0", "", @@ -354,6 +416,10 @@ static snmp_info_t pw_mib[] = { { "input.bypass.frequency", 0, 0.1, PW_OID_BY_FREQUENCY, "", 0, NULL }, { "input.bypass.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".0", "", SU_INPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsBypassVoltage.1.0; Value (Integer): 244 */ + { "input.bypass.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.5.3.1.2.1.0", "", + SU_INPUT_1, NULL }, { "input.bypass.L1-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".1", "", SU_INPUT_3, NULL }, { "input.bypass.L2-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".2", "", @@ -376,10 +442,17 @@ static snmp_info_t pw_mib[] = { { "ambient.humidity.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.12.0", "", 0, NULL }, /* XUPS-MIB::xupsContactState.1 */ { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.1.6.8.1.3.1", "", 0, &pw_ambient_drycontacts_info[0] }, + "1.3.6.1.4.1.534.1.6.8.1.3.1", "", 0, &pw_ambient_drycontacts_info[0] }, + /* Duplicate of the above entry, but pointing at the first index */ + /* FIXME: check snmp-ups->get_oid() for the walk/check on ".0" */ + { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, + "1.3.6.1.4.1.534.1.6.8.1.3.1.0", "", 0, &pw_ambient_drycontacts_info[0] }, /* XUPS-MIB::xupsContactState.2 */ { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.1.6.8.1.3.2", "", 0, &pw_ambient_drycontacts_info[0] }, + "1.3.6.1.4.1.534.1.6.8.1.3.2", "", 0, &pw_ambient_drycontacts_info[0] }, + /* Duplicate of the above entry, but pointing at the first index */ + { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, + "1.3.6.1.4.1.534.1.6.8.1.3.2.0", "", 0, &pw_ambient_drycontacts_info[0] }, /* instant commands */ { "test.battery.start.quick", 0, 1, PW_OID_BATTEST_START, "", From 8647d18b076bca31924dbd53798ea0dd84752e8a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 7 Aug 2018 20:46:28 +0200 Subject: [PATCH 238/700] powerware-mib.c : fix fallout from "snmp-ups: support newer Genepi management cards" (unused variable warning) --- drivers/powerware-mib.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index f17f79178f..d27d396b5a 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -135,9 +135,20 @@ static info_lkp_t pw_pwr_info[] = { { 0, NULL, NULL, NULL } }; -/* FIXME: mapped to ups.type, but should be output.source or ups.mode (need RFC) +/* FIXME: mapped to (experimental.)ups.type, but + * should be output.source or ups.mode (need RFC) * to complement the above ups.status * along with having ups.type as described hereafter*/ +/* FIXME: should be used by ups.mode or output.source (need RFC); + * Note: this define is not set via project options; code was hidden with + * original commit to "snmp-ups: support newer Genepi management cards"; + * un-hidden to make it "experimental.*" namespace during backporting + */ +#ifndef USE_PW_MODE_INFO +# define USE_PW_MODE_INFO 1 +#endif + +#if USE_PW_MODE_INFO static info_lkp_t pw_mode_info[] = { { 1, "", NULL, NULL }, { 2, "", NULL, NULL }, @@ -160,6 +171,7 @@ static info_lkp_t pw_mode_info[] = { { 16, "" /* none (0x10) */, NULL, NULL }, { 0, NULL, NULL, NULL } }; +#endif /* USE_PW_MODE_INFO */ /* FIXME: may be standardized * extracted from bcmxcp.c->BCMXCP_TOPOLOGY_*, Make some common definitions */ @@ -275,9 +287,13 @@ static snmp_info_t pw_mib[] = { SU_STATUS_BATT, &pw_alarm_lb[0] }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATT_STATUS, "", SU_STATUS_BATT, &pw_battery_abm_status[0] }, +#if USE_PW_MODE_INFO /* FIXME: should be ups.mode or output.source (need RFC) */ + /* Note: this define is not set via project options; code hidden with + * commit to "snmp-ups: support newer Genepi management cards" */ { "experimental.ups.type", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "", SU_FLAG_STATIC | SU_FLAG_OK, &pw_mode_info[0] }, +#endif /* USE_PW_MODE_INFO */ /* xupsTopologyType.0; Value (Integer): 32 */ { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.13.1.0", "", SU_FLAG_STATIC | SU_FLAG_OK, &pw_topology_info[0] }, From a8e12c0082da08287370b73d606eb8d8e13b36b5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 11 Oct 2017 21:44:05 +0200 Subject: [PATCH 239/700] eaton-pdu-marlin-mib.c : preferred templated outlet.%i.name goes last in MIB --- drivers/eaton-pdu-marlin-mib.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 0e43bcd100..b876fb40fc 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -792,25 +792,37 @@ static snmp_info_t eaton_marlin_mib[] = { ".1.3.6.1.4.1.534.6.6.7.6.6.1.2.%i.%i", NULL, SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, &marlin_outlet_status_info[0] }, + /* Numeric identifier of the outlet, tied to the whole unit */ { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.534.6.6.7.6.6.1.7.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - /* outletID: Outlet physical name, related to its number in the group - * ex: first outlet of the second group (B) is B1 */ - /* Outlet physical name OID in new G3 firmware (02.00.0051) - * outletPhysicalName.0.1 = Value (OctetString): A1 // FIXME-Check on real device + + /* NOTE: For daisychain devices ATM the last listed value presented by + * the SNMP device is kept by the driver - no SU_FLAG_UNIQUE here yet. + * Verified that a non-implemented OID does not publish empty values. */ + /* Fallback in firmwares issued before Sep 2017 is to use the + * outletID: Outlet physical name, related to its number in the group + * ex: first outlet of the second group (B) is B1, or can default to + * the outlet number (represented as string) and is a read-only string + * outletID.0.8 = Value (OctetString): "8" */ { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.6.1.1.6.%i.%i", + ".1.3.6.1.4.1.534.6.6.7.6.1.1.2.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - /* Fallback in firmwares issued before Sep 2017: */ + /* Preferred: Outlet physical name OID in new G3 firmware (02.00.0051) + * is named outletDesignator (other MIBs outletPhysicalName) + * and is a read-only string provided by firmware + * outletDesignator.0.1 = Value (OctetString): "A1" + * outletPhysicalName.0.16 = Value (OctetString): "A16" + */ { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, - ".1.3.6.1.4.1.534.6.6.7.6.1.1.2.%i.%i", + ".1.3.6.1.4.1.534.6.6.7.6.1.1.6.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* FIXME: the last part of the OID gives the group number (i.e. %i.1 means "group 1") * Need to address that, without multiple declaration (%i.%i, SU_OUTLET | SU_OUTLET_GROUP)? */ { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, From 27b92d9f7584719f248caddae669c8867a6fc7ac Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 13:16:50 +0100 Subject: [PATCH 240/700] drivers/eaton-pdu-marlin-mib.c: align comments around outlet.%i.name with FTY version --- drivers/eaton-pdu-marlin-mib.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index b876fb40fc..37a3056d95 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -794,11 +794,6 @@ static snmp_info_t eaton_marlin_mib[] = { &marlin_outlet_status_info[0] }, /* Numeric identifier of the outlet, tied to the whole unit */ - { "outlet.%i.id", 0, 1, - ".1.3.6.1.4.1.534.6.6.7.6.6.1.7.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, - NULL }, - /* NOTE: For daisychain devices ATM the last listed value presented by * the SNMP device is kept by the driver - no SU_FLAG_UNIQUE here yet. * Verified that a non-implemented OID does not publish empty values. */ @@ -808,6 +803,12 @@ static snmp_info_t eaton_marlin_mib[] = { * the outlet number (represented as string) and is a read-only string * outletID.0.8 = Value (OctetString): "8" */ + { "outlet.%i.id", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.7.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, + NULL }, + + /* Fallback in firmwares issued before Sep 2017: */ { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.2.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, From b8591f8db840d4bd3e04940de42659061f510ec9 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Mon, 9 Sep 2019 16:43:52 +0200 Subject: [PATCH 241/700] snmp-ups: support for daisychained ambient sensor Signed-off-by: Arnaud Quette --- drivers/snmp-ups.c | 114 ++++++++++++++++++++++++++++++++++++++++++--- drivers/snmp-ups.h | 22 ++++++--- 2 files changed, 123 insertions(+), 13 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 7d9cfbbccb..b1c982ad0a 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -166,7 +166,7 @@ static const char *mibname; static const char *mibvers; #define DRIVER_NAME "Generic SNMP UPS driver" -#define DRIVER_VERSION "1.19" +#define DRIVER_VERSION "1.20" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -193,8 +193,12 @@ static int template_index_base = -1; static int device_template_index_base = -1; /* OID index of the 1rst daisychained device */ static int outlet_template_index_base = -1; static int outletgroup_template_index_base = -1; +static int ambient_template_index_base = -1; static int device_template_offset = -1; +/* Temperature handling, to convert back to Celsius */ +int temperature_unit = TEMPERATURE_UNKNOWN; + /* sysOID location */ #define SYSOID_OID ".1.3.6.1.2.1.1.2.0" @@ -2208,7 +2212,7 @@ static bool_t is_multiple_template(const char *OID_template) } /* Instantiate an snmp_info_t from a template. - * Useful for outlet and outlet.group templates. + * Useful for device, outlet, outlet.group and ambient templates. * Note: remember to adapt info_type, OID and optionaly dfl */ static snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *new_instance) { @@ -2292,6 +2296,9 @@ static int base_snmp_template_index(const snmp_info_t *su_info_p) case SU_DAISY: template_index_base = device_template_index_base; break; + case SU_AMBIENT_TEMPLATE: + template_index_base = ambient_template_index_base; + break; default: /* we should never fall here! */ upsdebugx(3, "%s: unknown template type '%" PRI_SU_FLAGS "' for %s", @@ -2356,6 +2363,8 @@ static int base_snmp_template_index(const snmp_info_t *su_info_p) outlet_template_index_base = base_index; else if (su_info_p->flags & SU_OUTLET_GROUP) outletgroup_template_index_base = base_index; + else if (su_info_p->flags & SU_AMBIENT_TEMPLATE) + ambient_template_index_base = base_index; else device_template_index_base = base_index; } @@ -2464,6 +2473,7 @@ static bool_t process_template(int mode, const char* type, snmp_info_t *su_info_ else { template_count = atoi(dstate_getinfo(template_count_var)); } + upsdebugx(1, "%i instances found...", template_count); /* Only instantiate templates if needed! */ if (template_count > 0) { @@ -2476,6 +2486,7 @@ static bool_t process_template(int mode, const char* type, snmp_info_t *su_info_ cur_template_number < (template_count + base_snmp_index) ; cur_template_number++) { + upsdebugx(1, "Processing instance %i/%i...", cur_template_number, template_count); /* Special processing for daisychain: * append 'device.x' to the NUT variable name, except for the * whole daisychain ("device.0") */ @@ -2515,7 +2526,7 @@ static bool_t process_template(int mode, const char* type, snmp_info_t *su_info_ #endif } } - else /* Outlet and outlet groups templates */ + else if (!strncmp(type, "outlet", 6)) /* Outlet and outlet groups templates */ { /* Get the index of the current template instance */ cur_nut_index = cur_template_number; @@ -2552,6 +2563,44 @@ static bool_t process_template(int mode, const char* type, snmp_info_t *su_info_ su_info_p->info_type, cur_nut_index); } } + else if (!strncmp(type, "ambient", 7)) + { + /* FIXME: can be grouped with outlet* above */ + /* Get the index of the current template instance */ + cur_nut_index = cur_template_number; + + /* Special processing for daisychain */ + if (daisychain_enabled == TRUE) { + /* Only publish on the daisychain host */ + if ( (su_info_p->flags & SU_TYPE_DAISY_MASTER_ONLY) + && (current_device_number != 1) ) { + upsdebugx(2, "discarding variable due to daisychain master flag"); + continue; + } + + /* Device(s) 1-N (master + slave(s)) need to append 'device.x' */ + if ((devices_count > 1) && (current_device_number > 0)) { + memset(&tmp_buf[0], 0, SU_INFOSIZE); + strcat(&tmp_buf[0], "device.%i."); + strcat(&tmp_buf[0], su_info_p->info_type); + + upsdebugx(4, "FORMATTING STRING = %s", &tmp_buf[0]); + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + &tmp_buf[0], current_device_number, cur_nut_index); + } + else { + /* FIXME: daisychain-whole, what to do? */ + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + su_info_p->info_type, cur_nut_index); + } + } + else { + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + su_info_p->info_type, cur_nut_index); + } + } + else + upsdebugx(4, "Error: unknown template type '%s", type); /* check if default value is also a template */ if ((cur_info_p.dfl != NULL) && @@ -2580,9 +2629,14 @@ static bool_t process_template(int mode, const char* type, snmp_info_t *su_info_ snprintf((char *)cur_info_p.OID, SU_INFOSIZE, su_info_p->OID, current_device_number + device_template_offset, cur_template_number); } - else { + else if (su_info_p->flags & SU_TYPE_DAISY_2) { snprintf((char *)cur_info_p.OID, SU_INFOSIZE, - su_info_p->OID, cur_template_number + device_template_offset, current_device_number - device_template_offset); + su_info_p->OID, cur_template_number + device_template_offset, + current_device_number - device_template_offset); + } + else { + /* Note: no device daisychain templating (SU_TYPE_DAISY_MASTER_ONLY)! */ + snprintf((char *)cur_info_p.OID, SU_INFOSIZE, su_info_p->OID, cur_template_number); } } else { @@ -2638,6 +2692,10 @@ snmp_info_flags_t get_template_type(const char* varname) upsdebugx(4, "device template"); return SU_DAISY; } + else if (!strncmp(varname, "ambient", 7)) { + upsdebugx(4, "ambient template"); + return SU_AMBIENT_TEMPLATE; + } else { upsdebugx(2, "Unknown template type: %s", varname); return 0; @@ -2657,6 +2715,8 @@ int extract_template_number(snmp_info_flags_t template_type, const char* varname item_number_ptr = &varname[6]; else if (template_type & SU_DAISY) item_number_ptr = &varname[6]; + else if (template_type & SU_AMBIENT_TEMPLATE) + item_number_ptr = &varname[7]; else return -1; @@ -3103,7 +3163,8 @@ bool_t snmp_ups_walk(int mode) * Not applicable to outlets (need SU_FLAG_STATIC tagging) */ if ((su_info_p->flags & SU_FLAG_ABSENT) && !(su_info_p->flags & SU_OUTLET) - && !(su_info_p->flags & SU_OUTLET_GROUP)) + && !(su_info_p->flags & SU_OUTLET_GROUP) + && !(su_info_p->flags & SU_AMBIENT_TEMPLATE)) { if (mode == SU_WALKMODE_INIT) { @@ -3175,6 +3236,13 @@ bool_t snmp_ups_walk(int mode) else status = process_template(mode, "outlet.group", su_info_p); } + else if (su_info_p->flags & SU_AMBIENT_TEMPLATE) { + /* Skip commands after init */ + if ((SU_TYPE(su_info_p) == SU_TYPE_CMD) && (mode == SU_WALKMODE_UPDATE)) + continue; + else + status = process_template(mode, "ambient", su_info_p); + } else { /* if (daisychain_enabled == TRUE) { status = process_template(mode, "device", su_info_p); @@ -3990,3 +4058,37 @@ void read_mibconf(char *mib) } pconf_finish(&ctx); } + +/*********************************************************************** + * Subdrivers shared helpers functions + **********************************************************************/ + +static char su_scratch_buf[20]; + +/* Process temperature value according to 'temperature_unit' */ +const char *su_temperature_read_fun(long snmp_value) +{ + memset(su_scratch_buf, 0, sizeof(su_scratch_buf)); + long celsius_value = snmp_value; + + switch (temperature_unit) { + case TEMPERATURE_KELVIN: + celsius_value = (snmp_value / 10) - 273.15; + snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", celsius_value); + break; + case TEMPERATURE_CELSIUS: + snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", (snmp_value / 10)); + break; + case TEMPERATURE_FARHENHEIT: + celsius_value = (((snmp_value / 10) - 32) * 5) / 9; + snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", celsius_value); + break; + case TEMPERATURE_UNKNOWN: + default: + upsdebugx(1, "%s: not a known temperature unit for conversion!", __func__); + break; + } + upsdebugx(2, "%s: %.1ld => %s", __func__, (snmp_value / 10), su_scratch_buf); + return su_scratch_buf; +} + diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index 0f5f8c1d53..c3dc8decfd 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -244,17 +244,14 @@ typedef struct { /* "flags" bits 21..23 (and 24 reserved for DMF) */ #define SU_TYPE_DAISY_1 (1UL << 21) /* Daisychain index is the 1st specifier */ #define SU_TYPE_DAISY_2 (1UL << 22) /* Daisychain index is the 2nd specifier */ -#define SU_TYPE_DAISY(t) ((t)->flags & (3UL << 21)) /* Mask the 2 SU_TYPE_DAISY_* but not SU_DAISY */ +#define SU_TYPE_DAISY(t) ((t)->flags & (11UL << 21)) /* Mask the SU_TYPE_DAISY_{1,2,MASTER_ONLY} but not SU_DAISY */ #define SU_DAISY (1UL << 23) /* Daisychain template definition - set at run-time for devices with detected "device.count" over 1 */ -/* NOTE: Previously SU_DAISY had same bit-flag value as SU_TYPE_DAISY_2*/ -/* Reserved slot -- to import from DMF branch codebase -// (and change SU_TYPE_DAISY to 11UL<<21 for the 3 types then): -//#define SU_TYPE_DAISY_MASTER_ONLY (1UL << 24)*/ /* Only valid for daisychain master (device.1) */ +/* NOTE: Previously SU_DAISY had same bit-flag value as SU_TYPE_DAISY_2 */ +#define SU_TYPE_DAISY_MASTER_ONLY (1UL << 24) /* Only valid for daisychain master (device.1) */ /* Free slot: (1UL << 25) */ -/* Reserved slot -- to import from DMF branch codebase: -//#define SU_AMBIENT_TEMPLATE (1UL << 26)*/ /* ambient template definition */ +#define SU_AMBIENT_TEMPLATE (1UL << 26) /* ambient template definition */ /* Reserved slot -- to import from DMF branch codebase: //#define SU_FLAG_FUNCTION (1UL << 27) @@ -372,6 +369,17 @@ extern info_lkp_t su_convert_to_iso_date_info[]; /* Name the mapping location in that array for consumers to reference */ #define FUNMAP_USDATE_TO_ISODATE 0 +/* Process temperature value according to 'temperature_unit' */ +const char *su_temperature_read_fun(long snmp_value); + +/* Temperature handling, to convert back to Celsius (NUT standard) */ +extern int temperature_unit; + +#define TEMPERATURE_UNKNOWN 0 +#define TEMPERATURE_CELSIUS 1 +#define TEMPERATURE_KELVIN 2 +#define TEMPERATURE_FARHENHEIT 3 + /***************************************************** * End of Subdrivers shared helpers functions *****************************************************/ From ebcac3ba4833910a46caf6765e2882b8acf437ed Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Mon, 9 Sep 2019 16:45:12 +0200 Subject: [PATCH 242/700] SNMP Eaton ePDU: support for EMPDT1H1C2 Signed-off-by: Arnaud Quette --- drivers/eaton-pdu-marlin-helpers.c | 28 ++++++- drivers/eaton-pdu-marlin-helpers.h | 3 +- drivers/eaton-pdu-marlin-mib.c | 123 ++++++++++++++++++++++++++++- 3 files changed, 151 insertions(+), 3 deletions(-) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index 9e86574011..cbbaed9307 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -3,7 +3,7 @@ * G2 Marlin SW / MI / MO / MA * G3 Shark SW / MI / MO / MA * - * Copyright (C) 2017 + * Copyright (C) 2017-2019 * Arnaud Quette * Copyright (C) 2017 * Jim Klimov @@ -34,6 +34,9 @@ #include "eaton-pdu-marlin-helpers.h" #include "dstate.h" +/* Allow access to temperature_unit */ +#include "snmp-ups.h" + /* Take string "unitsPresent" (ex: "0,3,4,5"), and count the amount * of "," separators+1 using an inline function */ @@ -52,3 +55,26 @@ long marlin_device_count_fun(const char *daisy_dev_list) } return count; } + +/* Temperature unit consideration */ +const char *eaton_sensor_temperature_unit_fun(long snmp_value) +{ + switch (snmp_value) { + case 0: + /* store the value, for temperature processing */ + temperature_unit = TEMPERATURE_KELVIN; + return "kelvin"; + case 1: + /* store the value, for temperature processing */ + temperature_unit = TEMPERATURE_CELSIUS; + return "celsius"; + case 2: + /* store the value, for temperature processing */ + temperature_unit = TEMPERATURE_FARHENHEIT; + return "farhenheit"; + default: + /* store the value, for temperature processing */ + temperature_unit = TEMPERATURE_UNKNOWN; + return "unknown"; + } +} diff --git a/drivers/eaton-pdu-marlin-helpers.h b/drivers/eaton-pdu-marlin-helpers.h index 50a1d5e37e..070896e22f 100644 --- a/drivers/eaton-pdu-marlin-helpers.h +++ b/drivers/eaton-pdu-marlin-helpers.h @@ -2,7 +2,7 @@ * Eaton ePDU SNMP devices with NUT * * Copyright (C) - * 2017 Arnaud Quette + * 2017-2019 Arnaud Quette * 2017 Jim Klimov * * This program is free software; you can redistribute it and/or modify @@ -24,5 +24,6 @@ #define EATON_EPDU_MARLIN_HELPERS_H long marlin_device_count_fun(const char *daisy_dev_list); +const char *eaton_sensor_temperature_unit_fun(long snmp_value); #endif /* EATON_EPDU_MARLIN_HELPERS_H */ diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 37a3056d95..a384decd52 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -36,7 +36,7 @@ /* Eaton PDU-MIB - Marlin MIB * ************************** */ -#define EATON_MARLIN_MIB_VERSION "0.62" +#define EATON_MARLIN_MIB_VERSION "0.63" #define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" #define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" @@ -206,6 +206,57 @@ static info_lkp_t marlin_outlet_group_phase_info[] = { { 0, NULL, NULL, NULL } }; +#if WITH_SNMP_LKP_FUN +/* Note: eaton_sensor_temperature_unit_fun() is defined in eaton-pdu-marlin-helpers.c + * Future work for DMF might provide a same-named routine via LUA-C gateway. + */ + +#if WITH_SNMP_LKP_FUN_DUMMY +/* Temperature unit consideration */ +const char *eaton_sensor_temperature_unit_fun(long snmp_value) + { return "unknown"; } +/* FIXME: please DMF, though this should be in snmp-ups.c or equiv. */ +const char *su_temperature_read_fun(long snmp_value) + { return "dummy"; }; +#endif // WITH_SNMP_LKP_FUN_DUMMY + +static info_lkp_t eaton_sensor_temperature_unit_info[] = { + { 0, "dummy", eaton_sensor_temperature_unit_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_sensor_temperature_read_info[] = { + { 0, "dummy", su_temperature_read_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +#else // if not WITH_SNMP_LKP_FUN: + +/* FIXME: For now, DMF codebase falls back to old implementation with static + * lookup/mapping tables for this, which can easily go into the DMF XML file. + */ +static info_lkp_t eaton_sensor_temperature_unit_info[] = { + { 0, "kelvin", NULL, NULL }, + { 1, "celsius", NULL, NULL }, + { 2, "farhenheit", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +#endif // WITH_SNMP_LKP_FUN + +/* Extracted from powerware-mib.c ; try to commonalize */ +static info_lkp_t ambient_drycontacts_polarity_info[] = { + { 0, "normal-opened", NULL, NULL }, + { 1, "normal-closed", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t ambient_drycontacts_state_info[] = { + { 0, "active", NULL, NULL }, + { 1, "inactive", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + #if WITH_SNMP_LKP_FUN /* Note: marlin_device_count_fun() is defined in eaton-pdu-marlin-helpers.c * Future work for DMF might provide a same-named routine via LUA-C gateway. @@ -666,6 +717,8 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, /* Ambient collection */ + /* EMP001 (legacy) mapping */ + /* Note: this is still published, beside from the new daisychained version! */ { "ambient.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.7.1.1.3.%i.1", NULL, SU_FLAG_OK, @@ -740,6 +793,74 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, SU_FLAG_OK, &marlin_ambient_drycontacts_info[0] }, + + /* EMP002 (EATON EMP MIB) mapping, including daisychain support */ + /* Warning: indexes start at '1' not '0'! */ + /* sensorCount.0 */ + { "ambient.count", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.1.0", "0", SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorName.n: OctetString EMPDT1H1C2 @1 */ + { "ambient.%i.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorManufacturer.n */ + { "ambient.%i.mfr", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorModel.n */ + { "ambient.%i.model", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorSerialNumber.n */ + { "ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorFirmwareVersion.n */ + { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* temperatureUnit.1 + * MUST be before the temperature data reading! */ + { "ambient.%i.temperature.unit", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &eaton_sensor_temperature_unit_info[0] }, + /* temperatureValue.n.1 */ + { "ambient.%i.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, +#if WITH_SNMP_LKP_FUN + &eaton_sensor_temperature_read_info[0] +#else + NULL +#endif + }, + { "ambient.%i.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_temperature_alarms_info[0] }, + /* FIXME: ambient.n.temperature.{minimum,maximum} */ + /* temperatureThresholdLowCritical.n.1 */ + { "ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* temperatureThresholdLowWarning.n.1 */ + { "ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* temperatureThresholdHighWarning.n.1 */ + { "ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* temperatureThresholdHighCritical.n.1 */ + { "ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* humidityValue.n.1 */ + { "ambient.%i.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_humidity_alarms_info[0] }, + /* FIXME: consider ambient.n.humidity.{minimum,maximum} */ + /* humidityThresholdLowCritical.n.1 */ + { "ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* humidityThresholdLowWarning.n.1 */ + { "ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* humidityThresholdHighWarning.n.1 */ + { "ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* humidityThresholdHighCritical.n.1 */ + { "ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* digitalInputName.n.{1,2} */ + { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* digitalInputPolarity.n */ + { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &ambient_drycontacts_polarity_info[0] }, + /* XUPS-MIB::xupsContactState.n */ + { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &ambient_drycontacts_state_info[0] }, + /* Outlet collection */ { "outlet.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.22.%i", From 23054dfa45ff87ced114e3042382be12bc737913 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 11 Oct 2017 02:56:42 +0200 Subject: [PATCH 243/700] eaton-pdu-marlin-helpers.c : in device_count helper do not assume a trailing comma (as last char) as an extra device --- drivers/eaton-pdu-marlin-helpers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index cbbaed9307..7663379ada 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -49,8 +49,8 @@ long marlin_device_count_fun(const char *daisy_dev_list) count ++; } } - if (i>0) { - /* Non-empty string => at least one device */ + if (i>0 && (daisy_dev_list[i-1] != ',') ) { + /* Non-empty string => at least one device, and no trailing commas */ count ++; } return count; From 1cfb80912a77b0304b31d61f26a21d4002ff0d7d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 15:12:51 +0100 Subject: [PATCH 244/700] drivers/eaton-pdu-marlin-helpers.c: fix whitespace --- drivers/eaton-pdu-marlin-helpers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index 7663379ada..1bea9a5563 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -43,13 +43,13 @@ long marlin_device_count_fun(const char *daisy_dev_list) { long count = 0, i; - for (i=0; daisy_dev_list[i] != '\0'; i++) { + for (i = 0; daisy_dev_list[i] != '\0'; i++) { if (daisy_dev_list[i] == ',') { /* Each comma means a new device in the list */ count ++; } } - if (i>0 && (daisy_dev_list[i-1] != ',') ) { + if (i > 0 && (daisy_dev_list[i - 1] != ',') ) { /* Non-empty string => at least one device, and no trailing commas */ count ++; } From 952ab95ab07cf2fb9c1975a7f1e84d0fdbfed086 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Mon, 9 Sep 2019 16:46:34 +0200 Subject: [PATCH 245/700] SNMP Eaton Gb Network Card: support for EMPDT1H1C2 Signed-off-by: Arnaud Quette --- drivers/powerware-mib.c | 179 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 163 insertions(+), 16 deletions(-) diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index d27d396b5a..f46ee9b375 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -4,7 +4,7 @@ * Copyright (C) * 2005-2006 Olli Savia * 2005-2006 Niels Baggesen - * 2015-2021 Eaton (author: Arnaud Quette ) + * 2015-2019 Eaton (author: Arnaud Quette ) * * 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 @@ -24,8 +24,12 @@ */ #include "powerware-mib.h" +#if WITH_SNMP_LKP_FUN +/* FIXME: shared helper code, need to be put in common */ +#include "eaton-pdu-marlin-helpers.h" +#endif -#define PW_MIB_VERSION "0.97" +#define PW_MIB_VERSION "0.98" /* TODO: more sysOID and MIBs support: * @@ -79,6 +83,7 @@ #define PW_OID_CONT_ONT_DEL "1.3.6.1.4.1.534.1.9.4" /* XUPS-MIB::xupsControlOutputOnTrapDelay */ #define PW_OID_CONT_LOAD_SHED_AND_RESTART "1.3.6.1.4.1.534.1.9.6" /* XUPS-MIB::xupsLoadShedSecsWithRestart */ +#define PW_OID_CONF_OVOLTAGE "1.3.6.1.4.1.534.1.10.1.0" /* XUPS-MIB::xupsConfigOutputVoltage.0 */ #define PW_OID_CONF_IVOLTAGE "1.3.6.1.4.1.534.1.10.2.0" /* XUPS-MIB::xupsConfigInputVoltage.0 */ #define PW_OID_CONF_POWER "1.3.6.1.4.1.534.1.10.3.0" /* XUPS-MIB::xupsConfigOutputWatts.0 */ #define PW_OID_CONF_FREQ "1.3.6.1.4.1.534.1.10.4.0" /* XUPS-MIB::xupsConfigOutputFreq.0 */ @@ -246,6 +251,85 @@ static info_lkp_t pw_ambient_drycontacts_info[] = { { 0, NULL, NULL, NULL } }; +#if WITH_SNMP_LKP_FUN +/* Note: eaton_sensor_temperature_unit_fun() is defined in powerware-helpers.c + * Future work for DMF might provide a same-named routine via LUA-C gateway. + */ + +#if WITH_SNMP_LKP_FUN_DUMMY +/* Temperature unit consideration */ +const char *eaton_sensor_temperature_unit_fun(long snmp_value) + { return "unknown"; } +/* FIXME: please DMF, though this should be in snmp-ups.c or equiv. */ +const char *su_temperature_read_fun(long snmp_value) + { return "dummy"; }; +#endif // WITH_SNMP_LKP_FUN_DUMMY + +static info_lkp_t eaton_sensor_temperature_unit_info[] = { + { 0, "dummy", eaton_sensor_temperature_unit_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_sensor_temperature_read_info[] = { + { 0, "dummy", su_temperature_read_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +#else // if not WITH_SNMP_LKP_FUN: + +/* FIXME: For now, DMF codebase falls back to old implementation with static + * lookup/mapping tables for this, which can easily go into the DMF XML file. + */ +static info_lkp_t eaton_sensor_temperature_unit_info[] = { + { 0, "kelvin", NULL, NULL }, + { 1, "celsius", NULL, NULL }, + { 2, "farhenheit", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +#endif // WITH_SNMP_LKP_FUN + +static info_lkp_t ambient_drycontacts_polarity_info[] = { + { 0, "normal-opened", NULL, NULL }, + { 1, "normal-closed", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t ambient_drycontacts_state_info[] = { + { 0, "active", NULL, NULL }, + { 1, "inactive", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_status_info */ +static info_lkp_t pw_threshold_status_info[] = { + { 0, "good", NULL, NULL }, /* No threshold triggered */ + { 1, "warning-low", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "critical-low", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "warning-high", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "critical-high", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +/* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_xxx_alarms_info */ +static info_lkp_t pw_threshold_temperature_alarms_info[] = { + { 0, "", NULL, NULL }, /* No threshold triggered */ + { 1, "low temperature warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "low temperature critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "high temperature warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "high temperature critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t pw_threshold_humidity_alarms_info[] = { + { 0, "", NULL, NULL }, /* No threshold triggered */ + { 1, "low humidity warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "low humidity critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "high humidity warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "high humidity critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + /* Snmp2NUT lookup table */ static snmp_info_t pw_mib[] = { @@ -388,6 +472,8 @@ static snmp_info_t pw_mib[] = { SU_OUTPUT_3, NULL }, { "output.L3.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".3", "", SU_OUTPUT_3, NULL }, + { "output.voltage.nominal", 0, 1.0, PW_OID_CONF_OVOLTAGE, "", + 0, NULL }, /* Input page */ { "input.phases", 0, 1.0, PW_OID_IN_LINES, "", @@ -443,7 +529,8 @@ static snmp_info_t pw_mib[] = { { "input.bypass.L3-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".3", "", SU_INPUT_3, NULL }, - /* Ambient page */ + /* Ambient collection */ + /* EMP001 (legacy) mapping */ /* XUPS-MIB::xupsEnvRemoteTemp.0 */ { "ambient.temperature", 0, 1.0, "1.3.6.1.4.1.534.1.6.5.0", "", 0, NULL }, /* XUPS-MIB::xupsEnvRemoteTempLowerLimit.0 */ @@ -456,19 +543,79 @@ static snmp_info_t pw_mib[] = { { "ambient.humidity.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.11.0", "", 0, NULL }, /* XUPS-MIB::xupsEnvRemoteHumidityUpperLimit.0 */ { "ambient.humidity.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.12.0", "", 0, NULL }, - /* XUPS-MIB::xupsContactState.1 */ - { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, - "1.3.6.1.4.1.534.1.6.8.1.3.1", "", 0, &pw_ambient_drycontacts_info[0] }, - /* Duplicate of the above entry, but pointing at the first index */ - /* FIXME: check snmp-ups->get_oid() for the walk/check on ".0" */ - { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, - "1.3.6.1.4.1.534.1.6.8.1.3.1.0", "", 0, &pw_ambient_drycontacts_info[0] }, - /* XUPS-MIB::xupsContactState.2 */ - { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, - "1.3.6.1.4.1.534.1.6.8.1.3.2", "", 0, &pw_ambient_drycontacts_info[0] }, - /* Duplicate of the above entry, but pointing at the first index */ - { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, - "1.3.6.1.4.1.534.1.6.8.1.3.2.0", "", 0, &pw_ambient_drycontacts_info[0] }, + /* XUPS-MIB::xupsContactDescr.n */ + { "ambient.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.4.1", "", 0, NULL }, + { "ambient.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.4.2", "", 0, NULL }, + /* XUPS-MIB::xupsContactState.n */ + { "ambient.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.3.1", "", 0, &pw_ambient_drycontacts_info[0] }, + { "ambient.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.3.2", "", 0, &pw_ambient_drycontacts_info[0] }, + + /* EMP002 (EATON EMP MIB) mapping, including daisychain support */ + /* Warning: indexes start at '1' not '0'! */ + /* sensorCount.0 */ + { "ambient.count", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.1.0", "", 0, NULL }, + /* sensorName.n: OctetString EMPDT1H1C2 @1 */ + { "ambient.%i.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorManufacturer.n */ + { "ambient.%i.mfr", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorModel.n */ + { "ambient.%i.model", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorSerialNumber.n */ + { "ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorFirmwareVersion.n */ + { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* temperatureUnit.1 + * MUST be before the temperature data reading! */ + { "ambient.%i.temperature.unit", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE, &eaton_sensor_temperature_unit_info[0] }, + /* temperatureValue.n.1 */ + { "ambient.%i.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, +#if WITH_SNMP_LKP_FUN + &eaton_sensor_temperature_read_info[0] +#else + NULL +#endif + }, + { "ambient.%i.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_temperature_alarms_info[0] }, + /* FIXME: ambient.n.temperature.{minimum,maximum} */ + /* temperatureThresholdLowCritical.n.1 */ + { "ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* temperatureThresholdLowWarning.n.1 */ + { "ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* temperatureThresholdHighWarning.n.1 */ + { "ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* temperatureThresholdHighCritical.n.1 */ + { "ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* humidityValue.n.1 */ + { "ambient.%i.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + { "ambient.%i.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_humidity_alarms_info[0] }, + /* FIXME: consider ambient.n.humidity.{minimum,maximum} */ + /* humidityThresholdLowCritical.n.1 */ + { "ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* humidityThresholdLowWarning.n.1 */ + { "ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* humidityThresholdHighWarning.n.1 */ + { "ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* humidityThresholdHighCritical.n.1 */ + { "ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* digitalInputName.n.{1,2} */ + { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", "", SU_AMBIENT_TEMPLATE, NULL }, + /* digitalInputPolarity.n */ + { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &ambient_drycontacts_polarity_info[0] }, + /* XUPS-MIB::xupsContactState.n */ + { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &ambient_drycontacts_state_info[0] }, /* instant commands */ { "test.battery.start.quick", 0, 1, PW_OID_BATTEST_START, "", From 8e181ef403497249369efe15b9e277e8b381fad0 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Thu, 12 Sep 2019 14:58:35 +0200 Subject: [PATCH 246/700] Fix compilation warning Signed-off-by: Arnaud Quette --- drivers/eaton-pdu-marlin-mib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index a384decd52..de30b38a1f 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -217,7 +217,7 @@ const char *eaton_sensor_temperature_unit_fun(long snmp_value) { return "unknown"; } /* FIXME: please DMF, though this should be in snmp-ups.c or equiv. */ const char *su_temperature_read_fun(long snmp_value) - { return "dummy"; }; + { return "dummy"; } #endif // WITH_SNMP_LKP_FUN_DUMMY static info_lkp_t eaton_sensor_temperature_unit_info[] = { From 43d8c6608029e118372171755ccd486913fb687b Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Thu, 12 Sep 2019 18:00:10 +0200 Subject: [PATCH 247/700] Typo fix: sorry Mr Fahrenheit And thanks to Jim Klimov for the review! Signed-off-by: Arnaud Quette --- drivers/eaton-pdu-marlin-helpers.c | 4 ++-- drivers/eaton-pdu-marlin-mib.c | 2 +- drivers/powerware-mib.c | 2 +- drivers/snmp-ups.c | 2 +- drivers/snmp-ups.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index 1bea9a5563..dd2baa50a6 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -70,8 +70,8 @@ const char *eaton_sensor_temperature_unit_fun(long snmp_value) return "celsius"; case 2: /* store the value, for temperature processing */ - temperature_unit = TEMPERATURE_FARHENHEIT; - return "farhenheit"; + temperature_unit = TEMPERATURE_FAHRENHEIT; + return "fahrenheit"; default: /* store the value, for temperature processing */ temperature_unit = TEMPERATURE_UNKNOWN; diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index de30b38a1f..1709d17d21 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -238,7 +238,7 @@ static info_lkp_t eaton_sensor_temperature_read_info[] = { static info_lkp_t eaton_sensor_temperature_unit_info[] = { { 0, "kelvin", NULL, NULL }, { 1, "celsius", NULL, NULL }, - { 2, "farhenheit", NULL, NULL }, + { 2, "fahrenheit", NULL, NULL }, { 0, NULL, NULL, NULL } }; diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index f46ee9b375..5aae5fcda9 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -283,7 +283,7 @@ static info_lkp_t eaton_sensor_temperature_read_info[] = { static info_lkp_t eaton_sensor_temperature_unit_info[] = { { 0, "kelvin", NULL, NULL }, { 1, "celsius", NULL, NULL }, - { 2, "farhenheit", NULL, NULL }, + { 2, "fahrenheit", NULL, NULL }, { 0, NULL, NULL, NULL } }; diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index b1c982ad0a..fcec2c09da 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -4079,7 +4079,7 @@ const char *su_temperature_read_fun(long snmp_value) case TEMPERATURE_CELSIUS: snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", (snmp_value / 10)); break; - case TEMPERATURE_FARHENHEIT: + case TEMPERATURE_FAHRENHEIT: celsius_value = (((snmp_value / 10) - 32) * 5) / 9; snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", celsius_value); break; diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index c3dc8decfd..c71b371f6a 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -378,7 +378,7 @@ extern int temperature_unit; #define TEMPERATURE_UNKNOWN 0 #define TEMPERATURE_CELSIUS 1 #define TEMPERATURE_KELVIN 2 -#define TEMPERATURE_FARHENHEIT 3 +#define TEMPERATURE_FAHRENHEIT 3 /***************************************************** * End of Subdrivers shared helpers functions From 2c1fb1d10a24ef6d6ae623a40106e9cbb729b978 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 25 May 2016 10:07:08 +0200 Subject: [PATCH 248/700] powerware-mib - renamed "ietf_*" and "eaton_*" lookups to have "pw_*" prefix --- drivers/powerware-mib.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index 5aae5fcda9..932238405b 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -256,46 +256,46 @@ static info_lkp_t pw_ambient_drycontacts_info[] = { * Future work for DMF might provide a same-named routine via LUA-C gateway. */ -#if WITH_SNMP_LKP_FUN_DUMMY +# if WITH_SNMP_LKP_FUN_DUMMY /* Temperature unit consideration */ const char *eaton_sensor_temperature_unit_fun(long snmp_value) { return "unknown"; } /* FIXME: please DMF, though this should be in snmp-ups.c or equiv. */ const char *su_temperature_read_fun(long snmp_value) { return "dummy"; }; -#endif // WITH_SNMP_LKP_FUN_DUMMY +# endif /* WITH_SNMP_LKP_FUN_DUMMY */ -static info_lkp_t eaton_sensor_temperature_unit_info[] = { +static info_lkp_t pw_sensor_temperature_unit_info[] = { { 0, "dummy", eaton_sensor_temperature_unit_fun, NULL }, { 0, NULL, NULL, NULL } }; -static info_lkp_t eaton_sensor_temperature_read_info[] = { +static info_lkp_t pw_sensor_temperature_read_info[] = { { 0, "dummy", su_temperature_read_fun, NULL }, { 0, NULL, NULL, NULL } }; -#else // if not WITH_SNMP_LKP_FUN: +#else /* if not WITH_SNMP_LKP_FUN: */ /* FIXME: For now, DMF codebase falls back to old implementation with static * lookup/mapping tables for this, which can easily go into the DMF XML file. */ -static info_lkp_t eaton_sensor_temperature_unit_info[] = { +static info_lkp_t pw_sensor_temperature_unit_info[] = { { 0, "kelvin", NULL, NULL }, { 1, "celsius", NULL, NULL }, { 2, "fahrenheit", NULL, NULL }, { 0, NULL, NULL, NULL } }; -#endif // WITH_SNMP_LKP_FUN +#endif /* WITH_SNMP_LKP_FUN */ -static info_lkp_t ambient_drycontacts_polarity_info[] = { +static info_lkp_t pw_ambient_drycontacts_polarity_info[] = { { 0, "normal-opened", NULL, NULL }, { 1, "normal-closed", NULL, NULL }, { 0, NULL, NULL, NULL } }; -static info_lkp_t ambient_drycontacts_state_info[] = { +static info_lkp_t pw_ambient_drycontacts_state_info[] = { { 0, "active", NULL, NULL }, { 1, "inactive", NULL, NULL }, { 0, NULL, NULL, NULL } @@ -566,11 +566,11 @@ static snmp_info_t pw_mib[] = { { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE, NULL }, /* temperatureUnit.1 * MUST be before the temperature data reading! */ - { "ambient.%i.temperature.unit", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE, &eaton_sensor_temperature_unit_info[0] }, + { "ambient.%i.temperature.unit", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE, &pw_sensor_temperature_unit_info[0] }, /* temperatureValue.n.1 */ { "ambient.%i.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, #if WITH_SNMP_LKP_FUN - &eaton_sensor_temperature_read_info[0] + &pw_sensor_temperature_read_info[0] #else NULL #endif @@ -611,11 +611,11 @@ static snmp_info_t pw_mib[] = { { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", "", SU_AMBIENT_TEMPLATE, NULL }, /* digitalInputPolarity.n */ - { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &ambient_drycontacts_polarity_info[0] }, - { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_polarity_info[0] }, /* XUPS-MIB::xupsContactState.n */ - { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &ambient_drycontacts_state_info[0] }, - { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_state_info[0] }, /* instant commands */ { "test.battery.start.quick", 0, 1, PW_OID_BATTEST_START, "", From 41415a8758dc436525da88959208a794e34b3aef Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Thu, 19 Sep 2019 09:18:32 +0200 Subject: [PATCH 249/700] SNMP Eaton ePDU: always return celsius for temperature since the value reading is always adapted to celsius Signed-off-by: Arnaud Quette --- drivers/eaton-pdu-marlin-helpers.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index dd2baa50a6..468eefcaf3 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -56,25 +56,29 @@ long marlin_device_count_fun(const char *daisy_dev_list) return count; } -/* Temperature unit consideration */ +/* Temperature unit consideration: + * only store the device unit, for converting to Celsius. + * Don't publish the device unit, since NUT will publish + * as Celsius in all cases */ const char *eaton_sensor_temperature_unit_fun(long snmp_value) { switch (snmp_value) { case 0: /* store the value, for temperature processing */ temperature_unit = TEMPERATURE_KELVIN; - return "kelvin"; + break; case 1: /* store the value, for temperature processing */ temperature_unit = TEMPERATURE_CELSIUS; - return "celsius"; + break; case 2: /* store the value, for temperature processing */ temperature_unit = TEMPERATURE_FAHRENHEIT; - return "fahrenheit"; + break; default: /* store the value, for temperature processing */ temperature_unit = TEMPERATURE_UNKNOWN; - return "unknown"; + break; } + return "celsius"; } From 33a66d29c88347a411b235e7bf966f134a541a15 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 15:16:36 +0100 Subject: [PATCH 250/700] drivers/eaton-pdu-marlin-helpers.{c,h}: adjust eaton_sensor_temperature_unit_fun() API to that used in NUT master branch --- drivers/eaton-pdu-marlin-helpers.c | 3 ++- drivers/eaton-pdu-marlin-helpers.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index 468eefcaf3..2ed3aae517 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -60,8 +60,9 @@ long marlin_device_count_fun(const char *daisy_dev_list) * only store the device unit, for converting to Celsius. * Don't publish the device unit, since NUT will publish * as Celsius in all cases */ -const char *eaton_sensor_temperature_unit_fun(long snmp_value) +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) { + long snmp_value = *((long*)raw_snmp_value); switch (snmp_value) { case 0: /* store the value, for temperature processing */ diff --git a/drivers/eaton-pdu-marlin-helpers.h b/drivers/eaton-pdu-marlin-helpers.h index 070896e22f..fc9890e519 100644 --- a/drivers/eaton-pdu-marlin-helpers.h +++ b/drivers/eaton-pdu-marlin-helpers.h @@ -24,6 +24,6 @@ #define EATON_EPDU_MARLIN_HELPERS_H long marlin_device_count_fun(const char *daisy_dev_list); -const char *eaton_sensor_temperature_unit_fun(long snmp_value); +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value); #endif /* EATON_EPDU_MARLIN_HELPERS_H */ From 3e25ddbcb29697ee224a7acd7eeb88f13c0e6fa0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 15:25:34 +0100 Subject: [PATCH 251/700] drivers/snmp-ups.{c,h}: adjust su_temperature_read_fun() API to that used in NUT master branch --- drivers/eaton-pdu-marlin-mib.c | 4 ++-- drivers/powerware-mib.c | 6 +++--- drivers/snmp-ups.c | 6 ++++-- drivers/snmp-ups.h | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 1709d17d21..03a8dadf12 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -213,10 +213,10 @@ static info_lkp_t marlin_outlet_group_phase_info[] = { #if WITH_SNMP_LKP_FUN_DUMMY /* Temperature unit consideration */ -const char *eaton_sensor_temperature_unit_fun(long snmp_value) +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) { return "unknown"; } /* FIXME: please DMF, though this should be in snmp-ups.c or equiv. */ -const char *su_temperature_read_fun(long snmp_value) +const char *su_temperature_read_fun(void *raw_snmp_value) { return "dummy"; } #endif // WITH_SNMP_LKP_FUN_DUMMY diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index 932238405b..cca31c925e 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -258,11 +258,11 @@ static info_lkp_t pw_ambient_drycontacts_info[] = { # if WITH_SNMP_LKP_FUN_DUMMY /* Temperature unit consideration */ -const char *eaton_sensor_temperature_unit_fun(long snmp_value) +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) { return "unknown"; } /* FIXME: please DMF, though this should be in snmp-ups.c or equiv. */ -const char *su_temperature_read_fun(long snmp_value) - { return "dummy"; }; +const char *su_temperature_read_fun(void *raw_snmp_value) + { return "dummy"; } # endif /* WITH_SNMP_LKP_FUN_DUMMY */ static info_lkp_t pw_sensor_temperature_unit_info[] = { diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index fcec2c09da..8d8ca05230 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -4066,11 +4066,13 @@ void read_mibconf(char *mib) static char su_scratch_buf[20]; /* Process temperature value according to 'temperature_unit' */ -const char *su_temperature_read_fun(long snmp_value) +const char *su_temperature_read_fun(void *raw_snmp_value) { - memset(su_scratch_buf, 0, sizeof(su_scratch_buf)); + const long snmp_value = *((long*)raw_snmp_value); long celsius_value = snmp_value; + memset(su_scratch_buf, 0, sizeof(su_scratch_buf)); + switch (temperature_unit) { case TEMPERATURE_KELVIN: celsius_value = (snmp_value / 10) - 273.15; diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index c71b371f6a..0f8c06b75f 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -370,7 +370,7 @@ extern info_lkp_t su_convert_to_iso_date_info[]; #define FUNMAP_USDATE_TO_ISODATE 0 /* Process temperature value according to 'temperature_unit' */ -const char *su_temperature_read_fun(long snmp_value); +const char *su_temperature_read_fun(void *raw_snmp_value); /* Temperature handling, to convert back to Celsius (NUT standard) */ extern int temperature_unit; From 8ab3116687806084577515f6f031602d84fe0ab6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 15:46:47 +0100 Subject: [PATCH 252/700] drivers/eaton-pdu-marlin-mib.c: update comments and implem for dummy eaton_sensor_temperature_unit_fun()/su_temperature_read_fun() from 42ity/nut --- drivers/eaton-pdu-marlin-mib.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 03a8dadf12..39de2d77c7 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -208,16 +208,23 @@ static info_lkp_t marlin_outlet_group_phase_info[] = { #if WITH_SNMP_LKP_FUN /* Note: eaton_sensor_temperature_unit_fun() is defined in eaton-pdu-marlin-helpers.c - * Future work for DMF might provide a same-named routine via LUA-C gateway. + * and su_temperature_read_fun() is in snmp-ups.c + * Future work for DMF might provide same-named routines via LUA-C gateway. */ #if WITH_SNMP_LKP_FUN_DUMMY /* Temperature unit consideration */ -const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) - { return "unknown"; } +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "unknown"; +} /* FIXME: please DMF, though this should be in snmp-ups.c or equiv. */ -const char *su_temperature_read_fun(void *raw_snmp_value) - { return "dummy"; } +const char *su_temperature_read_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "dummy"; +} #endif // WITH_SNMP_LKP_FUN_DUMMY static info_lkp_t eaton_sensor_temperature_unit_info[] = { @@ -245,13 +252,13 @@ static info_lkp_t eaton_sensor_temperature_unit_info[] = { #endif // WITH_SNMP_LKP_FUN /* Extracted from powerware-mib.c ; try to commonalize */ -static info_lkp_t ambient_drycontacts_polarity_info[] = { +static info_lkp_t marlin_ambient_drycontacts_polarity_info[] = { { 0, "normal-opened", NULL, NULL }, { 1, "normal-closed", NULL, NULL }, { 0, NULL, NULL, NULL } }; -static info_lkp_t ambient_drycontacts_state_info[] = { +static info_lkp_t marlin_ambient_drycontacts_state_info[] = { { 0, "active", NULL, NULL }, { 1, "inactive", NULL, NULL }, { 0, NULL, NULL, NULL } @@ -855,11 +862,11 @@ static snmp_info_t eaton_marlin_mib[] = { { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* digitalInputPolarity.n */ - { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &ambient_drycontacts_polarity_info[0] }, - { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_ambient_drycontacts_polarity_info[0] }, /* XUPS-MIB::xupsContactState.n */ - { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &ambient_drycontacts_state_info[0] }, - { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_ambient_drycontacts_state_info[0] }, /* Outlet collection */ { "outlet.count", 0, 1, From 0a29f942080749fdfc286b00a1786bcc4c36d04e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 15:50:38 +0100 Subject: [PATCH 253/700] drivers/powerware-mib.c: update comments and implem for dummy eaton_sensor_temperature_unit_fun()/su_temperature_read_fun() from 42ity/nut --- drivers/powerware-mib.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index cca31c925e..db03898262 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -252,17 +252,24 @@ static info_lkp_t pw_ambient_drycontacts_info[] = { }; #if WITH_SNMP_LKP_FUN -/* Note: eaton_sensor_temperature_unit_fun() is defined in powerware-helpers.c - * Future work for DMF might provide a same-named routine via LUA-C gateway. +/* Note: eaton_sensor_temperature_unit_fun() is defined in eaton-pdu-marlin-helpers.c + * and su_temperature_read_fun() is in snmp-ups.c + * Future work for DMF might provide same-named routines via LUA-C gateway. */ # if WITH_SNMP_LKP_FUN_DUMMY /* Temperature unit consideration */ -const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) - { return "unknown"; } +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "unknown"; +} /* FIXME: please DMF, though this should be in snmp-ups.c or equiv. */ -const char *su_temperature_read_fun(void *raw_snmp_value) - { return "dummy"; } +const char *su_temperature_read_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "dummy"; +}; # endif /* WITH_SNMP_LKP_FUN_DUMMY */ static info_lkp_t pw_sensor_temperature_unit_info[] = { From e280e84cb6ab7f27efaaf0dc2d7d02c8ab6932b0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 13 Oct 2021 03:34:23 +0200 Subject: [PATCH 254/700] drivers/eaton-pdu-marlin-mib.c: update for new fun_vp2s() type args From 968f5593f408f4ddb8dbb8da48e66966772cc2d2 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Thu, 19 Sep 2019 14:51:56 +0200 Subject: [PATCH 255/700] SNMP Eaton EMP002: handle sensor presence Sensor may not be present (or connected). However, the values (temperature, humidity, ...) are still available, but should not be considered Signed-off-by: Arnaud Quette --- drivers/eaton-pdu-marlin-mib.c | 12 +++++++++++- drivers/powerware-mib.c | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 39de2d77c7..96abcbc427 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -36,7 +36,7 @@ /* Eaton PDU-MIB - Marlin MIB * ************************** */ -#define EATON_MARLIN_MIB_VERSION "0.63" +#define EATON_MARLIN_MIB_VERSION "0.64" #define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" #define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" @@ -112,6 +112,13 @@ static info_lkp_t marlin_ambient_presence_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t marlin_emp002_ambient_presence_info[] = { + { 0, "unknown", NULL, NULL }, + { 2, "yes", NULL, NULL }, /* communicationOK */ + { 3, "no", NULL, NULL }, /* communicationLost */ + { 0, NULL, NULL, NULL } +}; + static info_lkp_t marlin_threshold_status_info[] = { { 0, "good", NULL, NULL }, /* No threshold triggered */ { 1, "warning-low", NULL, NULL }, /* Warning low threshold triggered */ @@ -805,6 +812,9 @@ static snmp_info_t eaton_marlin_mib[] = { /* Warning: indexes start at '1' not '0'! */ /* sensorCount.0 */ { "ambient.count", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.1.0", "0", SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* CommunicationStatus.n */ + { "ambient.%i.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.1.4.1.1.%i", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_emp002_ambient_presence_info[0] }, /* sensorName.n: OctetString EMPDT1H1C2 @1 */ { "ambient.%i.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorManufacturer.n */ diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index db03898262..a2ad7ba0c1 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -29,7 +29,7 @@ #include "eaton-pdu-marlin-helpers.h" #endif -#define PW_MIB_VERSION "0.98" +#define PW_MIB_VERSION "0.99" /* TODO: more sysOID and MIBs support: * @@ -308,6 +308,13 @@ static info_lkp_t pw_ambient_drycontacts_state_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t pw_emp002_ambient_presence_info[] = { + { 0, "unknown", NULL, NULL }, + { 2, "yes", NULL, NULL }, /* communicationOK */ + { 3, "no", NULL, NULL }, /* communicationLost */ + { 0, NULL, NULL, NULL } +}; + /* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_status_info */ static info_lkp_t pw_threshold_status_info[] = { { 0, "good", NULL, NULL }, /* No threshold triggered */ @@ -561,6 +568,9 @@ static snmp_info_t pw_mib[] = { /* Warning: indexes start at '1' not '0'! */ /* sensorCount.0 */ { "ambient.count", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.1.0", "", 0, NULL }, + /* CommunicationStatus.n */ + { "ambient.%i.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.1.4.1.1.%i", + NULL, SU_AMBIENT_TEMPLATE, &pw_emp002_ambient_presence_info[0] }, /* sensorName.n: OctetString EMPDT1H1C2 @1 */ { "ambient.%i.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", "", SU_AMBIENT_TEMPLATE, NULL }, /* sensorManufacturer.n */ From 4153cd1ef5cdc9a71708bb407c6d7f0dd717eb15 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Thu, 7 Nov 2019 16:54:32 +0100 Subject: [PATCH 256/700] SNMP Eaton Gb Network Card: various data completion * fix reading of input.voltage, related to the ending ".0", * fix existing commands handling, * add support for the load segment (managed outlets), including status information and commands Signed-off-by: Arnaud Quette --- drivers/powerware-mib.c | 62 ++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index a2ad7ba0c1..cab6fadf8a 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -29,7 +29,7 @@ #include "eaton-pdu-marlin-helpers.h" #endif -#define PW_MIB_VERSION "0.99" +#define PW_MIB_VERSION "0.100" /* TODO: more sysOID and MIBs support: * @@ -77,8 +77,8 @@ #define PW_OID_BATTEST_START "1.3.6.1.4.1.534.1.8.1" /* XUPS-MIB::xupsTestBattery set to startTest(1) to initiate test*/ -#define PW_OID_CONT_OFFDELAY "1.3.6.1.4.1.534.1.9.1" /* XUPS-MIB::xupsControlOutputOffDelay */ -#define PW_OID_CONT_ONDELAY "1.3.6.1.4.1.534.1.9.2" /* XUPS-MIB::xupsControlOutputOnDelay */ +#define PW_OID_CONT_OFFDELAY "1.3.6.1.4.1.534.1.9.1.0" /* XUPS-MIB::xupsControlOutputOffDelay */ +#define PW_OID_CONT_ONDELAY "1.3.6.1.4.1.534.1.9.2.0" /* XUPS-MIB::xupsControlOutputOnDelay */ #define PW_OID_CONT_OFFT_DEL "1.3.6.1.4.1.534.1.9.3" /* XUPS-MIB::xupsControlOutputOffTrapDelay */ #define PW_OID_CONT_ONT_DEL "1.3.6.1.4.1.534.1.9.4" /* XUPS-MIB::xupsControlOutputOnTrapDelay */ #define PW_OID_CONT_LOAD_SHED_AND_RESTART "1.3.6.1.4.1.534.1.9.6" /* XUPS-MIB::xupsLoadShedSecsWithRestart */ @@ -242,6 +242,18 @@ static info_lkp_t pw_yes_no_info[] = { { 0, NULL, NULL, NULL } }; +static info_lkp_t pw_outlet_status_info[] = { + { 1, "on", NULL, NULL }, + { 2, "off", NULL, NULL }, + { 3, "on", NULL, NULL }, /* pendingOff, transitional status */ + { 4, "off", NULL, NULL }, /* pendingOn, transitional status */ + /* { 5, "", NULL, NULL }, unknown */ + /* { 6, "", NULL, NULL }, reserved */ + { 7, "off", NULL, NULL }, /* Failed in Closed position */ + { 8, "on", NULL, NULL }, /* Failed in Open position */ + { 0, NULL, NULL, NULL } +}; + static info_lkp_t pw_ambient_drycontacts_info[] = { { -1, "unknown", NULL, NULL }, { 1, "opened", NULL, NULL }, @@ -497,11 +509,10 @@ static snmp_info_t pw_mib[] = { { "input.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".0", "", SU_INPUT_1, NULL }, /* Duplicate of the above entry, but pointing at the first index */ - /* xupsInputVoltage.1.0; Value (Integer): 245 */ - { "input.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.3.4.1.2.1.0", "", + /* xupsInputVoltage.1[.0]; Value (Integer): 245 */ + { "input.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.3.4.1.2.1", "", SU_INPUT_1, NULL }, - /* XUPS-MIB::xupsConfigInputVoltage.0 */ { "input.voltage.nominal", 0, 1.0, "1.3.6.1.4.1.534.1.10.2.0", "", 0, NULL }, { "input.current", 0, 0.1, PW_OID_IN_CURRENT ".0", "", @@ -543,6 +554,17 @@ static snmp_info_t pw_mib[] = { { "input.bypass.L3-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".3", "", SU_INPUT_3, NULL }, + /* Outlet page */ + /* XUPS-MIB::xupsNumReceptacles; Value (Integer): 2 */ + { "outlet.count", 0, 1, ".1.3.6.1.4.1.534.1.12.1.0", NULL, SU_FLAG_STATIC, NULL }, + /* XUPS-MIB::xupsRecepIndex.X; Value (Integer): X */ + { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.1.%i", NULL, SU_FLAG_STATIC | SU_OUTLET, NULL }, + /* This MIB does not provide outlets switchability info. So map to a nearby + OID, for data activation, and map all values to "yes" */ + { "outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.1.%i", NULL, SU_FLAG_STATIC | SU_OUTLET, NULL }, + /* XUPS-MIB::xupsRecepStatus.X; Value (Integer): 1 */ + { "outlet.%i.status", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.2.%i", NULL, SU_OUTLET, &pw_outlet_status_info[0] }, + /* Ambient collection */ /* EMP001 (legacy) mapping */ /* XUPS-MIB::xupsEnvRemoteTemp.0 */ @@ -643,17 +665,37 @@ static snmp_info_t pw_mib[] = { /* Cancel output off, by writing 0 to xupsControlOutputOffDelay */ { "shutdown.stop", 0, 0, PW_OID_CONT_OFFDELAY, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* XUPS-MIB::xupsControlOutputOffDelay */ /* load off after 1 sec, shortest possible delay; 0 cancels */ - { "load.off", 0, 1, PW_OID_CONT_OFFDELAY, "", + { "load.off", 0, 1, PW_OID_CONT_OFFDELAY, "1", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "load.off.delay", 0, DEFAULT_OFFDELAY, PW_OID_CONT_OFFDELAY, "", + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + { "load.off.delay", 0, 1, PW_OID_CONT_OFFDELAY, NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* XUPS-MIB::xupsControlOutputOnDelay */ /* load on after 1 sec, shortest possible delay; 0 cancels */ - { "load.on", 0, 1, PW_OID_CONT_ONDELAY, "", + { "load.on", 0, 1, PW_OID_CONT_ONDELAY, "1", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "load.on.delay", 0, DEFAULT_ONDELAY, PW_OID_CONT_ONDELAY, "", + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + { "load.on.delay", 0, 1, PW_OID_CONT_ONDELAY, NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* Delays handling: + * 0-n :Time in seconds until the command is issued + * -1:Cancel a pending Off/On command */ + /* XUPS-MIB::xupsRecepOffDelaySecs.n */ + { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.3.%i", + "0", SU_TYPE_CMD | SU_OUTLET, NULL }, + /* XUPS-MIB::xupsRecepOnDelaySecs.n */ + { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.4.%i", + "0", SU_TYPE_CMD | SU_OUTLET, NULL }, + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + { "outlet.%i.load.off.delay", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.3.%i", + NULL, SU_TYPE_CMD | SU_OUTLET, NULL }, + /* XUPS-MIB::xupsRecepOnDelaySecs.n */ + { "outlet.%i.load.on.delay", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.4.%i", + NULL, SU_TYPE_CMD | SU_OUTLET, NULL }, + { "ups.alarms", 0, 1.0, PW_OID_ALARMS, "", 0, NULL }, From 8930755f05e1723112c7d8b9d470b82b29a86dfb Mon Sep 17 00:00:00 2001 From: "Clappier, Eric" Date: Mon, 14 Dec 2020 15:39:56 +0100 Subject: [PATCH 257/700] Add modbus_address and uuid in snmp-ups/pw driver --- drivers/powerware-mib.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index cab6fadf8a..2d85446afb 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -601,6 +601,10 @@ static snmp_info_t pw_mib[] = { { "ambient.%i.model", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", "", SU_AMBIENT_TEMPLATE, NULL }, /* sensorSerialNumber.n */ { "ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorUuid.n */ + { "ambient.%i.uuid", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorAddress.n */ + { "ambient.%i.modbus_address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE, NULL }, /* sensorFirmwareVersion.n */ { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE, NULL }, /* temperatureUnit.1 From e093c199c69e7b4d5aacd2d849d164eb4f35db98 Mon Sep 17 00:00:00 2001 From: "Clappier, Eric" Date: Tue, 15 Dec 2020 12:01:29 +0100 Subject: [PATCH 258/700] Change uid and modbus address name according nut nomenclature --- drivers/eaton-pdu-marlin-mib.c | 6 +++++- drivers/powerware-mib.c | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 96abcbc427..ebb4f3f037 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -36,7 +36,7 @@ /* Eaton PDU-MIB - Marlin MIB * ************************** */ -#define EATON_MARLIN_MIB_VERSION "0.64" +#define EATON_MARLIN_MIB_VERSION "0.65" #define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" #define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" @@ -823,6 +823,10 @@ static snmp_info_t eaton_marlin_mib[] = { { "ambient.%i.model", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorSerialNumber.n */ { "ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorUuid.n */ + { "ambient.%i.id", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorAddress.n */ + { "ambient.%i.address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorFirmwareVersion.n */ { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* temperatureUnit.1 diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index 2d85446afb..f2541411a4 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -29,7 +29,7 @@ #include "eaton-pdu-marlin-helpers.h" #endif -#define PW_MIB_VERSION "0.100" +#define PW_MIB_VERSION "0.101" /* TODO: more sysOID and MIBs support: * @@ -602,9 +602,9 @@ static snmp_info_t pw_mib[] = { /* sensorSerialNumber.n */ { "ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE, NULL }, /* sensorUuid.n */ - { "ambient.%i.uuid", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + { "ambient.%i.id", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE, NULL }, /* sensorAddress.n */ - { "ambient.%i.modbus_address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + { "ambient.%i.address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE, NULL }, /* sensorFirmwareVersion.n */ { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE, NULL }, /* temperatureUnit.1 From 111462040231bfd00946caed657e92c3a7650504 Mon Sep 17 00:00:00 2001 From: "Clappier, Eric" Date: Mon, 14 Dec 2020 15:39:06 +0100 Subject: [PATCH 259/700] Add modbus_address and uuid in eaton_epdu driver From e2b51e58496ea820084704e91dba9f792a7b2cf7 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Tue, 15 Dec 2020 14:49:40 +0100 Subject: [PATCH 260/700] snmp-ups: Restore legacy Eaton ePDU switchability info Use a hack to also have switchability for both the unit and its outlets on legacy Eaton G2 ePDU Signed-off-by: Arnaud Quette --- drivers/eaton-pdu-marlin-mib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index ebb4f3f037..b6ae9ce061 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -36,7 +36,7 @@ /* Eaton PDU-MIB - Marlin MIB * ************************** */ -#define EATON_MARLIN_MIB_VERSION "0.65" +#define EATON_MARLIN_MIB_VERSION "0.66" #define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" #define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" @@ -902,7 +902,7 @@ static snmp_info_t eaton_marlin_mib[] = { /* Ugly hack for older G2 ePDU: check the first outlet to determine unit switchability */ { "outlet.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.1", - "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_TYPE_DAISY_1, + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_FLAG_OK | SU_TYPE_DAISY_1, &g2_unit_outlet_switchability_info[0] }, /* The below ones are the same as the input.* equivalent */ /* FIXME: transition period, TO BE REMOVED, moved to input.* */ From 7d0640f4da3b134c4b3426c76ea858247e59fcc1 Mon Sep 17 00:00:00 2001 From: "Clappier, Eric" Date: Wed, 16 Dec 2020 16:01:04 +0100 Subject: [PATCH 261/700] Add ambient.n.parent.serial for marlin epdu Regarding dropped code from snmp-ups.c - per discussion in https://github.com/42ity/nut/pull/117 this was not a typo: > It is an intentional change for oid read value issue. > This allows to actually retrieve an indirection value > (when an Oid points at an Oid, like stood) --- drivers/eaton-pdu-marlin-mib.c | 2 ++ drivers/snmp-ups.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index b6ae9ce061..36fdc7435c 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -827,6 +827,8 @@ static snmp_info_t eaton_marlin_mib[] = { { "ambient.%i.id", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorAddress.n */ { "ambient.%i.address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorMonitoredBy.n */ + { "ambient.%i.parent.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.5.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorFirmwareVersion.n */ { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* temperatureUnit.1 diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 8d8ca05230..cabb46c685 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -1271,8 +1271,6 @@ static bool_t decode_str(struct snmp_pdu *pdu, char *buf, size_t buf_len, info_l snprintf(buf, buf_len, "%s", oid_leaf+1); upsdebugx(3, "Fallback value: %s", buf); } - else - snprintf(buf, buf_len, "%s", tmp_buf); break; default: return FALSE; From 6f647c278f9f34f310a1a93ef8833a0c83551c69 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Tue, 26 Jan 2021 12:07:02 +0100 Subject: [PATCH 262/700] snmp-ups: fix regression on Eaton ePDU Fix a regression that caused a mis-determination of the SNMP base OID index (0 or 1, should be 1). This in turn caused a mis-iteration over the outlets, from 0 to N-1 instead of 1 to N, which resulted in the missing Nth outlet (last outlet of the PDU). This also caused some data refresh issues Signed-off-by: Arnaud Quette --- drivers/eaton-pdu-marlin-mib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 36fdc7435c..d9aaadf042 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -36,7 +36,7 @@ /* Eaton PDU-MIB - Marlin MIB * ************************** */ -#define EATON_MARLIN_MIB_VERSION "0.66" +#define EATON_MARLIN_MIB_VERSION "0.67" #define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" #define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" @@ -904,7 +904,7 @@ static snmp_info_t eaton_marlin_mib[] = { /* Ugly hack for older G2 ePDU: check the first outlet to determine unit switchability */ { "outlet.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.1", - "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_FLAG_OK | SU_TYPE_DAISY_1, + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_TYPE_DAISY_1, &g2_unit_outlet_switchability_info[0] }, /* The below ones are the same as the input.* equivalent */ /* FIXME: transition period, TO BE REMOVED, moved to input.* */ From 95da25b9a661dfdd91fa0f2087bf1d8174975519 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 16:03:46 +0100 Subject: [PATCH 263/700] Move su_temperature_read_fun() from drivers/snmp-ups.c to snmp-ups-helpers.c --- drivers/snmp-ups-helpers.c | 32 +++++++++++++++++++++++++++++++ drivers/snmp-ups.c | 39 -------------------------------------- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/drivers/snmp-ups-helpers.c b/drivers/snmp-ups-helpers.c index ce3f2abcff..b4116ff6f8 100644 --- a/drivers/snmp-ups-helpers.c +++ b/drivers/snmp-ups-helpers.c @@ -45,6 +45,9 @@ static char su_scratch_buf[255]; +/* Temperature handling, to convert back to Celsius */ +int temperature_unit = TEMPERATURE_UNKNOWN; + /* Convert a US formated date (mm/dd/yyyy) to an ISO 8601 Calendar date (yyyy-mm-dd) */ const char *su_usdate_to_isodate_info_fun(void *raw_date) { @@ -72,3 +75,32 @@ info_lkp_t su_convert_to_iso_date_info[] = { { 1, "dummy", su_usdate_to_isodate_info_fun, NULL }, { 0, NULL, NULL, NULL } }; + +/* Process temperature value according to 'temperature_unit' */ +const char *su_temperature_read_fun(void *raw_snmp_value) +{ + const long snmp_value = *((long*)raw_snmp_value); + long celsius_value = snmp_value; + + memset(su_scratch_buf, 0, sizeof(su_scratch_buf)); + + switch (temperature_unit) { + case TEMPERATURE_KELVIN: + celsius_value = (snmp_value / 10) - 273.15; + snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", celsius_value); + break; + case TEMPERATURE_CELSIUS: + snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", (snmp_value / 10)); + break; + case TEMPERATURE_FAHRENHEIT: + celsius_value = (((snmp_value / 10) - 32) * 5) / 9; + snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", celsius_value); + break; + case TEMPERATURE_UNKNOWN: + default: + upsdebugx(1, "%s: not a known temperature unit for conversion!", __func__); + break; + } + upsdebugx(2, "%s: %.1ld => %s", __func__, (snmp_value / 10), su_scratch_buf); + return su_scratch_buf; +} diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index cabb46c685..6016b73eec 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -196,9 +196,6 @@ static int outletgroup_template_index_base = -1; static int ambient_template_index_base = -1; static int device_template_offset = -1; -/* Temperature handling, to convert back to Celsius */ -int temperature_unit = TEMPERATURE_UNKNOWN; - /* sysOID location */ #define SYSOID_OID ".1.3.6.1.2.1.1.2.0" @@ -4056,39 +4053,3 @@ void read_mibconf(char *mib) } pconf_finish(&ctx); } - -/*********************************************************************** - * Subdrivers shared helpers functions - **********************************************************************/ - -static char su_scratch_buf[20]; - -/* Process temperature value according to 'temperature_unit' */ -const char *su_temperature_read_fun(void *raw_snmp_value) -{ - const long snmp_value = *((long*)raw_snmp_value); - long celsius_value = snmp_value; - - memset(su_scratch_buf, 0, sizeof(su_scratch_buf)); - - switch (temperature_unit) { - case TEMPERATURE_KELVIN: - celsius_value = (snmp_value / 10) - 273.15; - snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", celsius_value); - break; - case TEMPERATURE_CELSIUS: - snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", (snmp_value / 10)); - break; - case TEMPERATURE_FAHRENHEIT: - celsius_value = (((snmp_value / 10) - 32) * 5) / 9; - snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", celsius_value); - break; - case TEMPERATURE_UNKNOWN: - default: - upsdebugx(1, "%s: not a known temperature unit for conversion!", __func__); - break; - } - upsdebugx(2, "%s: %.1ld => %s", __func__, (snmp_value / 10), su_scratch_buf); - return su_scratch_buf; -} - From eb7d25094a7c5dda3d78907ee357951ef377be8d Mon Sep 17 00:00:00 2001 From: "Clappier, Eric" Date: Mon, 1 Feb 2021 08:49:45 +0100 Subject: [PATCH 264/700] Fix dry contacts status for EMP02 --- drivers/eaton-pdu-marlin-mib.c | 6 +++--- drivers/powerware-mib.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index d9aaadf042..9ee3f934e5 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -36,7 +36,7 @@ /* Eaton PDU-MIB - Marlin MIB * ************************** */ -#define EATON_MARLIN_MIB_VERSION "0.67" +#define EATON_MARLIN_MIB_VERSION "0.68" #define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" #define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" @@ -266,8 +266,8 @@ static info_lkp_t marlin_ambient_drycontacts_polarity_info[] = { }; static info_lkp_t marlin_ambient_drycontacts_state_info[] = { - { 0, "active", NULL, NULL }, - { 1, "inactive", NULL, NULL }, + { 0, "inactive", NULL, NULL }, + { 1, "active", NULL, NULL }, { 0, NULL, NULL, NULL } }; diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index f2541411a4..019f70c057 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -29,7 +29,7 @@ #include "eaton-pdu-marlin-helpers.h" #endif -#define PW_MIB_VERSION "0.101" +#define PW_MIB_VERSION "0.102" /* TODO: more sysOID and MIBs support: * @@ -315,8 +315,8 @@ static info_lkp_t pw_ambient_drycontacts_polarity_info[] = { }; static info_lkp_t pw_ambient_drycontacts_state_info[] = { - { 0, "active", NULL, NULL }, - { 1, "inactive", NULL, NULL }, + { 0, "inactive", NULL, NULL }, + { 1, "active", NULL, NULL }, { 0, NULL, NULL, NULL } }; From 50dcc410cdeb243a0b4171ea85854b76c19b9197 Mon Sep 17 00:00:00 2001 From: "Clappier, Eric" Date: Mon, 14 Jun 2021 16:43:56 +0200 Subject: [PATCH 265/700] Add missing outlet objects for master --- drivers/powerware-mib.c | 4 ++++ drivers/snmp-ups.c | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index 019f70c057..18d147b4dc 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -555,6 +555,10 @@ static snmp_info_t pw_mib[] = { SU_INPUT_3, NULL }, /* Outlet page */ + /* Master outlet id always equal to 0 */ + { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC , NULL }, + /* XUPS-MIB:: xupsSwitchable.0 */ + { "outlet.switchable", 0, 1, ".1.3.6.1.4.1.534.1.9.7.0", NULL, SU_FLAG_STATIC , &pw_yes_no_info[0] }, /* XUPS-MIB::xupsNumReceptacles; Value (Integer): 2 */ { "outlet.count", 0, 1, ".1.3.6.1.4.1.534.1.12.1.0", NULL, SU_FLAG_STATIC, NULL }, /* XUPS-MIB::xupsRecepIndex.X; Value (Integer): X */ diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 6016b73eec..4911aad357 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -3281,7 +3281,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) upsdebugx(2, "%s: %s %s", __func__, su_info_p->info_type, su_info_p->OID); /* Check if this is a daisychain template */ - if ((format_char = strchr(su_info_p->OID, '%')) != NULL) { + if (su_info_p->OID != NULL + && (format_char = strchr(su_info_p->OID, '%')) != NULL + ) { upsdebugx(3, "%s: calling instantiate_info() for " "daisy-chain template", __func__); tmp_info_p = instantiate_info(su_info_p, tmp_info_p); @@ -3501,7 +3503,12 @@ bool_t su_ups_get(snmp_info_t *su_info_p) return TRUE; } - if (su_info_p->info_flags & ST_FLAG_STRING) { + /* special treatment for element without oid but with default value */ + if (su_info_p->OID == NULL && su_info_p->dfl != NULL) { + status = TRUE; + strncpy(buf, su_info_p->dfl, sizeof(buf)); + } + else if (su_info_p->info_flags & ST_FLAG_STRING) { upsdebugx(2, "%s: requesting nut_snmp_get_str(), " "with%s daisy template originally", __func__, (format_char!=NULL ? "" : "out")); From 6e83f1f052f9283d6b8ff81f90c458eeeecb225e Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Mon, 15 Nov 2021 17:19:33 +0100 Subject: [PATCH 266/700] Fix regression on Eaton EMP002 temperature reading (SNMP) Following the recent addition of the "String reformating function" (su_find_strval()), a regression appeared on a corner case: when flagging a data with ST_FLAG_STRING, while the SNMP OID is an int, and when there is a fun_vp2s() conversion function, a double conversion is applied, resulting in no value published. This was limited to one data (temperature.unit) Signed-off-by: Arnaud Quette --- drivers/eaton-pdu-marlin-mib.c | 4 ++-- drivers/powerware-mib.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 9ee3f934e5..ca0e007dc1 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -36,7 +36,7 @@ /* Eaton PDU-MIB - Marlin MIB * ************************** */ -#define EATON_MARLIN_MIB_VERSION "0.68" +#define EATON_MARLIN_MIB_VERSION "0.69" #define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" #define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" @@ -833,7 +833,7 @@ static snmp_info_t eaton_marlin_mib[] = { { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* temperatureUnit.1 * MUST be before the temperature data reading! */ - { "ambient.%i.temperature.unit", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &eaton_sensor_temperature_unit_info[0] }, + { "ambient.%i.temperature.unit", 0, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &eaton_sensor_temperature_unit_info[0] }, /* temperatureValue.n.1 */ { "ambient.%i.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, #if WITH_SNMP_LKP_FUN diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index 18d147b4dc..5dcc2444d5 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -29,7 +29,7 @@ #include "eaton-pdu-marlin-helpers.h" #endif -#define PW_MIB_VERSION "0.102" +#define PW_MIB_VERSION "0.103" /* TODO: more sysOID and MIBs support: * @@ -613,7 +613,7 @@ static snmp_info_t pw_mib[] = { { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE, NULL }, /* temperatureUnit.1 * MUST be before the temperature data reading! */ - { "ambient.%i.temperature.unit", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE, &pw_sensor_temperature_unit_info[0] }, + { "ambient.%i.temperature.unit", 0, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE, &pw_sensor_temperature_unit_info[0] }, /* temperatureValue.n.1 */ { "ambient.%i.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, #if WITH_SNMP_LKP_FUN From 79dd9579165676cdccf7d336b4b47f86d6a9c3d0 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Mon, 22 Nov 2021 14:30:01 +0100 Subject: [PATCH 267/700] Eaton XML/PDC: add External Battery Module count Signed-off-by: Arnaud Quette --- drivers/mge-xml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mge-xml.c b/drivers/mge-xml.c index af9c60b2d5..73aa43f0e3 100644 --- a/drivers/mge-xml.c +++ b/drivers/mge-xml.c @@ -35,7 +35,7 @@ #include "mge-xml.h" #include "main.h" /* for testvar() */ -#define MGE_XML_VERSION "MGEXML/0.34" +#define MGE_XML_VERSION "MGEXML/0.35" #define MGE_XML_INITUPS "/" #define MGE_XML_INITINFO "/mgeups/product.xml /product.xml /ws/product.xml" From 9a8c11bb42479945225c9433706166b9546ace33 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 12 Jan 2022 15:12:38 +0100 Subject: [PATCH 268/700] Eaton NMC: fix the non publication of real/power with 3ph power and realpower for 3ph Lx were not publishing values when they were 0 Signed-off-by: Arnaud Quette --- drivers/mge-xml.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mge-xml.c b/drivers/mge-xml.c index 73aa43f0e3..fc2f8d4b3b 100644 --- a/drivers/mge-xml.c +++ b/drivers/mge-xml.c @@ -35,7 +35,7 @@ #include "mge-xml.h" #include "main.h" /* for testvar() */ -#define MGE_XML_VERSION "MGEXML/0.35" +#define MGE_XML_VERSION "MGEXML/0.36" #define MGE_XML_INITUPS "/" #define MGE_XML_INITINFO "/mgeups/product.xml /product.xml /ws/product.xml" From 015619e316c9351eb889358686123d9a89ef1a5c Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Thu, 25 Nov 2021 08:45:46 +0100 Subject: [PATCH 269/700] Eaton SNMP: also publish ups.load for 3phase Though there is output.Lx.power.percent, for 3phase, it's desirable to also have the standard ups.load Signed-off-by: Arnaud Quette --- drivers/powerware-mib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index 5dcc2444d5..e4b0296010 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -29,7 +29,7 @@ #include "eaton-pdu-marlin-helpers.h" #endif -#define PW_MIB_VERSION "0.103" +#define PW_MIB_VERSION "0.104" /* TODO: more sysOID and MIBs support: * @@ -380,7 +380,7 @@ static snmp_info_t pw_mib[] = { { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_IDENT, "", SU_FLAG_STATIC, NULL }, { "ups.load", 0, 1.0, PW_OID_OUT_LOAD, "", - SU_OUTPUT_1, NULL }, + 0, NULL }, /* FIXME: should be removed in favor of output.power */ { "ups.power", 0, 1.0, PW_OID_OUT_POWER ".1", "", 0, NULL }, From f799e083c3bd9aed989455064e14346d7b02fdf3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 17 Nov 2021 11:19:23 +0100 Subject: [PATCH 270/700] drivers/eaton-pdu-marlin-helpers.c: include config.h first --- drivers/eaton-pdu-marlin-helpers.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index 2ed3aae517..a386e11095 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -28,6 +28,8 @@ * */ +#include "config.h" /* must be the first header */ + #include #include #include From a27079588f75e007be92fc1f51be5240772f8c6a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 11 Nov 2021 11:03:48 +0100 Subject: [PATCH 271/700] drivers/snmp-ups.c: avoid stringop-truncation warning --- drivers/snmp-ups.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 4911aad357..5235b0f099 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -3506,7 +3506,7 @@ bool_t su_ups_get(snmp_info_t *su_info_p) /* special treatment for element without oid but with default value */ if (su_info_p->OID == NULL && su_info_p->dfl != NULL) { status = TRUE; - strncpy(buf, su_info_p->dfl, sizeof(buf)); + strncpy(buf, su_info_p->dfl, sizeof(buf) - 1); } else if (su_info_p->info_flags & ST_FLAG_STRING) { upsdebugx(2, "%s: requesting nut_snmp_get_str(), " From c2f50f01c520419c2d4443a1f3a67704cd80c05d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 16:52:40 +0100 Subject: [PATCH 272/700] drivers/snmp-ups.c: avoid stringop-truncation warning: make sure string is finite --- drivers/snmp-ups.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 5235b0f099..ecc85b7047 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -3506,7 +3506,9 @@ bool_t su_ups_get(snmp_info_t *su_info_p) /* special treatment for element without oid but with default value */ if (su_info_p->OID == NULL && su_info_p->dfl != NULL) { status = TRUE; + /* FIXME: strlcpy() would fit here safer; not used in NUT yet */ strncpy(buf, su_info_p->dfl, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; } else if (su_info_p->info_flags & ST_FLAG_STRING) { upsdebugx(2, "%s: requesting nut_snmp_get_str(), " From 8df3a971a00533f1bd672e7d7da10d6647a61aae Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 25 Feb 2022 21:06:14 +0100 Subject: [PATCH 273/700] drivers/eaton-pdu-marlin-mib.c: break long lines --- drivers/eaton-pdu-marlin-mib.c | 137 ++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 36 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index ca0e007dc1..59732ccdba 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -455,7 +455,7 @@ static snmp_info_t eaton_marlin_mib[] = { * Voltage has to be expressed either phase-phase or phase-neutral * This is depending on OID inputVoltageMeasType * INTEGER {singlePhase (1),phase1toN (2),phase2toN (3),phase3toN (4),phase1to2 (5),phase2to3 (6),phase3to1 (7) - * => RFC input.Lx.voltage.context */ + * => RFC input.Lx.voltage.context */ { "input.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", NULL, 0, NULL }, @@ -708,7 +708,7 @@ static snmp_info_t eaton_marlin_mib[] = { /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => refreshed from time to time or upon call to setvar */ /* { "input.%i.feed.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, * ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.%i", - * NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + * NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, */ { "input.feed.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.1", @@ -811,31 +811,56 @@ static snmp_info_t eaton_marlin_mib[] = { /* EMP002 (EATON EMP MIB) mapping, including daisychain support */ /* Warning: indexes start at '1' not '0'! */ /* sensorCount.0 */ - { "ambient.count", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.1.0", "0", SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.count", ST_FLAG_RW, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.1.0", + "0", SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* CommunicationStatus.n */ - { "ambient.%i.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.1.4.1.1.%i", - NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_emp002_ambient_presence_info[0] }, + { "ambient.%i.present", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.1.4.1.1.%i", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_emp002_ambient_presence_info[0] }, /* sensorName.n: OctetString EMPDT1H1C2 @1 */ - { "ambient.%i.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.name", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorManufacturer.n */ - { "ambient.%i.mfr", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.mfr", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorModel.n */ - { "ambient.%i.model", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.model", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorSerialNumber.n */ - { "ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.serial", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorUuid.n */ - { "ambient.%i.id", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.id", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorAddress.n */ - { "ambient.%i.address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.address", 0, 1, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorMonitoredBy.n */ - { "ambient.%i.parent.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.5.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.parent.serial", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.5.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* sensorFirmwareVersion.n */ - { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* temperatureUnit.1 * MUST be before the temperature data reading! */ - { "ambient.%i.temperature.unit", 0, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &eaton_sensor_temperature_unit_info[0] }, + { "ambient.%i.temperature.unit", 0, 1.0, + ".1.3.6.1.4.1.534.6.8.1.2.5.0", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &eaton_sensor_temperature_unit_info[0] }, /* temperatureValue.n.1 */ - { "ambient.%i.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + { "ambient.%i.temperature", 0, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, #if WITH_SNMP_LKP_FUN &eaton_sensor_temperature_read_info[0] #else @@ -844,45 +869,83 @@ static snmp_info_t eaton_marlin_mib[] = { }, { "ambient.%i.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_status_info[0] }, + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_temperature_alarms_info[0] }, + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_temperature_alarms_info[0] }, /* FIXME: ambient.n.temperature.{minimum,maximum} */ /* temperatureThresholdLowCritical.n.1 */ - { "ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* temperatureThresholdLowWarning.n.1 */ - { "ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* temperatureThresholdHighWarning.n.1 */ - { "ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* temperatureThresholdHighCritical.n.1 */ - { "ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* humidityValue.n.1 */ - { "ambient.%i.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.humidity", 0, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, { "ambient.%i.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_status_info[0] }, + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_status_info[0] }, { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", - NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_threshold_humidity_alarms_info[0] }, + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_humidity_alarms_info[0] }, /* FIXME: consider ambient.n.humidity.{minimum,maximum} */ /* humidityThresholdLowCritical.n.1 */ - { "ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* humidityThresholdLowWarning.n.1 */ - { "ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* humidityThresholdHighWarning.n.1 */ - { "ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* humidityThresholdHighCritical.n.1 */ - { "ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* digitalInputName.n.{1,2} */ - { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, - { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, /* digitalInputPolarity.n */ - { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_ambient_drycontacts_polarity_info[0] }, - { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_polarity_info[0] }, /* XUPS-MIB::xupsContactState.n */ - { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_ambient_drycontacts_state_info[0] }, - { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, &marlin_ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_state_info[0] }, /* Outlet collection */ { "outlet.count", 0, 1, @@ -1187,14 +1250,16 @@ static snmp_info_t eaton_marlin_mib[] = { /* groupVA.0.1 = Integer: 3132 */ { "outlet.group.%i.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.5.5.1.2.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, /* Input to which an outlet-group is connected * groupInputIndex.0.1 = Integer: 1 */ /* FIXME: RFC on key name is needed when backporting to NUT upstream */ { "outlet.group.%i.input", 0, 1, ".1.3.6.1.4.1.534.6.6.7.5.1.1.9.%i.%i", - NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, /* instant commands. */ /* Notes: From b49634383be855ad396ff8c98f87ddc0c78d27fd Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 18:44:30 +0100 Subject: [PATCH 274/700] drivers/eaton-pdu-marlin-mib.c: resync comments and use of SU_FLAG_SEMI_STATIC --- drivers/eaton-pdu-marlin-mib.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c index 59732ccdba..bed4bbef53 100644 --- a/drivers/eaton-pdu-marlin-mib.c +++ b/drivers/eaton-pdu-marlin-mib.c @@ -990,10 +990,14 @@ static snmp_info_t eaton_marlin_mib[] = { /* outlet template definition * Indexes start from 1, ie outlet.1 => .1 */ /* Note: the first definition is used to determine the base index (ie 0 or 1) */ - /* outletName: Outlet friendly name, which can be modified by the user */ + /* Outlet friendly name, which can be modified by the user + * outletName: = OctetString: "Outlet A16" + */ + /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => + * refreshed from time to time or upon call to setvar */ { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.3.%i.%i", - NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.2.%i.%i", @@ -1015,7 +1019,7 @@ static snmp_info_t eaton_marlin_mib[] = { NULL, SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, - /* Fallback in firmwares issued before Sep 2017: */ + /* Fallback in firmwares issued before Sep 2017 (outletID): */ { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.2.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, @@ -1134,12 +1138,15 @@ static snmp_info_t eaton_marlin_mib[] = { { "outlet.group.%i.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, - /* groupName.0.1 = OctetString: Factory Group 1 */ - /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => refreshed from time to time or upon call to setvar */ - /* User-friendly (writeable) description of the outlet group: */ + /* User-friendly (writeable) description of the outlet group: + * groupName.0.1 = OctetString: Factory Group 1 + * groupName.0.2 = OctetString: Branch Circuit B + */ + /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => + * refreshed from time to time or upon call to setvar */ { "outlet.group.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.5.1.1.3.%i.%i", - NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL, SU_FLAG_SEMI_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, /* Outlet-group physical name, a read-only string, * is named groupDesignator (other MIBs groupPhysicalName) From 3aefd12cde469449ad8490471ff2f2381a865580 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Wed, 25 Sep 2019 12:58:49 +0200 Subject: [PATCH 275/700] Fix false positive when communication is lost Signed-off-by: Arnaud Quette --- drivers/snmp-ups.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index ecc85b7047..da65fe79bc 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -166,7 +166,7 @@ static const char *mibname; static const char *mibvers; #define DRIVER_NAME "Generic SNMP UPS driver" -#define DRIVER_VERSION "1.20" +#define DRIVER_VERSION "1.21" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -186,6 +186,12 @@ upsdrv_info_t upsdrv_info = { static time_t lastpoll = 0; +/* Communication status handling */ +#define COMM_UNKNOWN 0 +#define COMM_OK 1 +#define COMM_LOST 2 +static int comm_status = COMM_UNKNOWN; + /* template OIDs index start with 0 or 1 (estimated stable for a MIB), * automatically guessed at the first pass */ static int template_index_base = -1; @@ -267,9 +273,11 @@ su_addcmd(su_info_p); /* initialize all other INFO_ fields from list */ if (snmp_ups_walk(SU_WALKMODE_INIT) == TRUE) { dstate_dataok(); + comm_status = COMM_OK; } else { dstate_datastale(); + comm_status = COMM_LOST; } /* setup handlers for instcmd and setvar functions */ @@ -292,10 +300,12 @@ void upsdrv_updateinfo(void) if (snmp_ups_walk(SU_WALKMODE_UPDATE)) { upsdebugx(1, "%s: pollfreq: Data OK", __func__); dstate_dataok(); + comm_status = COMM_OK; } else { upsdebugx(1, "%s: pollfreq: Data STALE", __func__); dstate_datastale(); + comm_status = COMM_LOST; } /* Commit status first, otherwise in daisychain mode, "device.0" may @@ -311,8 +321,11 @@ void upsdrv_updateinfo(void) lastpoll = time(NULL); } else { - /* Just tell everything is ok to upsd */ - dstate_dataok(); + /* Just tell the same status to upsd */ + if (comm_status == COMM_OK) + dstate_dataok(); + else + dstate_datastale(); } } @@ -2298,7 +2311,7 @@ static int base_snmp_template_index(const snmp_info_t *su_info_p) /* we should never fall here! */ upsdebugx(3, "%s: unknown template type '%" PRI_SU_FLAGS "' for %s", __func__, template_type, su_info_p->info_type); - } + } base_index = template_index_base; if (template_index_base == -1) From 406250e2e117f272bedb302f23f3a292aa683596 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 13 Oct 2021 02:22:43 +0200 Subject: [PATCH 276/700] drivers/snmp-ups.c: su_find_strval() should consider #if WITH_SNMP_LKP_FUN --- drivers/snmp-ups.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index da65fe79bc..61687b095e 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2097,6 +2097,7 @@ long su_find_valinfo(info_lkp_t *oid2info, const char* value) /* String reformating function */ const char *su_find_strval(info_lkp_t *oid2info, void *value) { +#if WITH_SNMP_LKP_FUN /* First test if we have a generic lookup function */ if ( (oid2info != NULL) && (oid2info->fun_vp2s != NULL) ) { upsdebugx(2, "%s: using generic lookup function (string reformatting)", __func__); @@ -2105,6 +2106,9 @@ const char *su_find_strval(info_lkp_t *oid2info, void *value) return retvalue; } upsdebugx(1, "%s: no result value for this OID string value (%s)", __func__, (char*)value); +#else + upsdebugx(1, "%s: no mapping function for this OID string value (%s)", __func__, (char*)value); +#endif // WITH_SNMP_LKP_FUN return NULL; } From 7a128cc063c45bdb931d654f3100314bedca52b4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 11 Oct 2017 21:51:38 +0200 Subject: [PATCH 277/700] snmp-ups.c : publish device.count==1 too --- drivers/snmp-ups.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 61687b095e..7fc7010553 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2841,10 +2841,9 @@ bool_t daisychain_init() daisychain_enabled = FALSE; upsdebugx(1, "Devices count is less than 1!"); upsdebugx(1, "Falling back to 1 device and disabling daisychain support!"); - } - - /* Publish the device(s) count */ - if (devices_count > 1) { + } else { + /* Publish the device(s) count - even if just one + * device was recognized at this moment */ dstate_setinfo("device.count", "%ld", devices_count); /* Also publish the default value for mfr and a forged model From 6af7233a3ae66050f7ee6d2148b8e7188c88eb7d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 19:06:32 +0100 Subject: [PATCH 278/700] drivers/snmp-ups.c: whitespace fix --- drivers/snmp-ups.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 7fc7010553..48fa11de72 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -251,8 +251,8 @@ void upsdrv_initinfo(void) && !(su_info_p->flags & SU_OUTLET_GROUP)) { /* first check that this OID actually exists */ -/* FIXME: daisychain commands support! */ -su_addcmd(su_info_p); + /* FIXME: daisychain commands support! */ + su_addcmd(su_info_p); /* if (nut_snmp_get(su_info_p->OID) != NULL) { dstate_addcmd(su_info_p->info_type); @@ -2118,7 +2118,7 @@ const char *su_find_infoval(info_lkp_t *oid2info, void *raw_value) info_lkp_t *info_lkp; long value = *((long *)raw_value); -#ifdef WITH_SNMP_LKP_FUN +#if WITH_SNMP_LKP_FUN /* First test if we have a generic lookup function */ if ( (oid2info != NULL) && (oid2info->fun_vp2s != NULL) ) { upsdebugx(2, "%s: using generic lookup function", __func__); @@ -2315,7 +2315,7 @@ static int base_snmp_template_index(const snmp_info_t *su_info_p) /* we should never fall here! */ upsdebugx(3, "%s: unknown template type '%" PRI_SU_FLAGS "' for %s", __func__, template_type, su_info_p->info_type); - } + } base_index = template_index_base; if (template_index_base == -1) From 32612832c3d7ca2f735051caa028ac8c9d63f901 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 17 Nov 2021 11:21:26 +0100 Subject: [PATCH 279/700] drivers/snmp-ups.c: su_find_strval(): mark NUT_UNUSED_VARIABLE(oid2info) when not WITH_SNMP_LKP_FUN --- drivers/snmp-ups.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 48fa11de72..9b02be8f09 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2107,6 +2107,7 @@ const char *su_find_strval(info_lkp_t *oid2info, void *value) } upsdebugx(1, "%s: no result value for this OID string value (%s)", __func__, (char*)value); #else + NUT_UNUSED_VARIABLE(oid2info); upsdebugx(1, "%s: no mapping function for this OID string value (%s)", __func__, (char*)value); #endif // WITH_SNMP_LKP_FUN return NULL; From 161fd3e0549d4b0aff699a0214c94ab7302d30b8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 18:44:40 +0000 Subject: [PATCH 280/700] drivers/eaton-pdu-marlin-helpers.c: marlin_device_count_fun(): add debug trace --- drivers/eaton-pdu-marlin-helpers.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c index a386e11095..421045f4c8 100644 --- a/drivers/eaton-pdu-marlin-helpers.c +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -36,15 +36,16 @@ #include "eaton-pdu-marlin-helpers.h" #include "dstate.h" +#include "common.h" /* Allow access to temperature_unit */ #include "snmp-ups.h" - /* Take string "unitsPresent" (ex: "0,3,4,5"), and count the amount * of "," separators+1 using an inline function */ long marlin_device_count_fun(const char *daisy_dev_list) { long count = 0, i; + for (i = 0; daisy_dev_list[i] != '\0'; i++) { if (daisy_dev_list[i] == ',') { /* Each comma means a new device in the list */ @@ -55,6 +56,9 @@ long marlin_device_count_fun(const char *daisy_dev_list) /* Non-empty string => at least one device, and no trailing commas */ count ++; } + + upsdebugx(3, "%s: counted devices in '%s', got %ld", + __func__, daisy_dev_list, count); return count; } From cb7f65a051be79025fe539794087b0a9c97a07ae Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 18:45:00 +0000 Subject: [PATCH 281/700] drivers/snmp-ups.c: daisychain_init(): add debug trace --- drivers/snmp-ups.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 9b02be8f09..a314db66a4 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2816,6 +2816,8 @@ bool_t daisychain_init() daisychain_enabled = TRUE; /* Try to get the OID value, if it's not a template */ + upsdebugx(3, "OID for device.count is %s", + su_info_p->OID ? su_info_p->OID : ""); if ((su_info_p->OID != NULL) && (strstr(su_info_p->OID, "%i") == NULL)) { From 2d97ab0ef29a60f46080eb80469cdff440006f03 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 27 Feb 2022 19:58:48 +0100 Subject: [PATCH 282/700] drivers/snmp-ups.c: extend daisychain initialization to use optional mapping function Note: ported sub-set of "snmp-ups.[ch] : add support for extended fun/nuf l2s/s2l conversions and use it for daisychain initialization" without the actual 2x2 "fun/nuf l2s/s2l" support here. --- drivers/snmp-ups.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index a314db66a4..9cbcf9c9af 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -2821,14 +2821,38 @@ bool_t daisychain_init() if ((su_info_p->OID != NULL) && (strstr(su_info_p->OID, "%i") == NULL)) { - if (nut_snmp_get_int(su_info_p->OID, &devices_count) == TRUE) - upsdebugx(1, "There are %ld device(s) present", devices_count); - else - { - upsdebugx(1, "Error: can't get the number of device(s) present!"); - upsdebugx(1, "Falling back to 1 device!"); - devices_count = 1; +#if WITH_SNMP_LKP_FUN + devices_count = -1; + /* First test if we have a generic lookup function + * FIXME: Check if the field type is a string? + */ + /* TODO: backport the 2x2 mapping function support + * and this would be "fun_s2l" in resulting codebase + */ + if ( (su_info_p->oid2info != NULL) && (su_info_p->oid2info->nuf_s2l != NULL) ) { + char buf[1024]; + upsdebugx(2, "%s: using generic string-to-long lookup function", __func__); + if (TRUE == nut_snmp_get_str(su_info_p->OID, buf, sizeof(buf), su_info_p->oid2info)) { + devices_count = su_info_p->oid2info->nuf_s2l(buf); + upsdebugx(2, "%s: got value '%ld'", __func__, devices_count); + } + } + + if (devices_count == -1) { +#endif /* WITH_SNMP_LKP_FUN */ + + if (nut_snmp_get_int(su_info_p->OID, &devices_count) == TRUE) + upsdebugx(1, "There are %ld device(s) present", devices_count); + else + { + upsdebugx(1, "Error: can't get the number of device(s) present!"); + upsdebugx(1, "Falling back to 1 device!"); + devices_count = 1; + } + +#if WITH_SNMP_LKP_FUN } +#endif /* WITH_SNMP_LKP_FUN */ } /* Otherwise (template), use the guesstimation function to get * the number of devices present */ From f2fb70d2a2384ce80d772fd97a76d5c0e1b0e241 Mon Sep 17 00:00:00 2001 From: Nick Briggs Date: Mon, 28 Feb 2022 12:32:19 -0800 Subject: [PATCH 283/700] Cast to unsigned type when interpreting HID descriptor length bytes (libusb 0.1) The libusb 0.1 interface definition declares a (signed) char type for control messages. The HID descriptor length contained within a control message is intended to be interpreted as a pair of unsigned bytes so we must cast to uint8_t when doing the arithmetic rather than trip over the sign bit. Closes #1261, closes #1312. --- drivers/libusb0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/libusb0.c b/drivers/libusb0.c index adf098ba31..d51313ec38 100644 --- a/drivers/libusb0.c +++ b/drivers/libusb0.c @@ -377,7 +377,7 @@ static int libusb_open(usb_dev_handle **udevp, upsdebug_hex(3, "HID descriptor, method 1", buf, 9); - rdlen1 = buf[7] | (buf[8] << 8); + rdlen1 = (uint8_t)buf[7] | ((uint8_t)buf[8] << 8); } if (rdlen1 < -1) { @@ -407,7 +407,7 @@ static int libusb_open(usb_dev_handle **udevp, ) { p = (usb_ctrl_char *)&iface->extra[i]; upsdebug_hex(3, "HID descriptor, method 2", p, 9); - rdlen2 = p[7] | (p[8] << 8); + rdlen2 = (uint8_t)p[7] | ((uint8_t)p[8] << 8); break; } } From 5bcf73fc2eee7ec34bc352c548f45aa65823031e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 2 Mar 2022 22:11:34 +0000 Subject: [PATCH 284/700] drivers/mge-xml.c: mge_drycontact_info(): avoid shadowing global variable --- drivers/mge-xml.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mge-xml.c b/drivers/mge-xml.c index fc2f8d4b3b..4177b693d6 100644 --- a/drivers/mge-xml.c +++ b/drivers/mge-xml.c @@ -577,7 +577,7 @@ static const char *mge_ambient_info(const char *arg_val) } } -static const char *mge_drycontact_info(const char *val) +static const char *mge_drycontact_info(const char *arg_val) { /* these values should theoretically be obtained through * Environment.Input[1].State[x].Description @@ -585,7 +585,7 @@ static const char *mge_drycontact_info(const char *val) * open * closed */ - switch (atoi(val)) + switch (atoi(arg_val)) { case 0: return "opened"; From ed23b34f01b99972908bdd9d18d71a15ee532710 Mon Sep 17 00:00:00 2001 From: Nita Vesa Date: Sun, 27 Feb 2022 19:13:48 +0200 Subject: [PATCH 285/700] APC HID UPS: Add ability to set battery battery.mfr.date Some APC UPSes allow for setting battery.mfr.date, so it can be easily referenced later, even if there were no stickers or papers left to refer to. This patch has been tested to work with APC Back-UPS ES 550G. Signed-off-by: Nita Vesa --- drivers/apc-hid.c | 20 ++++++++++++++++++-- drivers/usbhid-ups.c | 18 ++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/apc-hid.c b/drivers/apc-hid.c index fd97ed2780..36afaf12c2 100644 --- a/drivers/apc-hid.c +++ b/drivers/apc-hid.c @@ -128,8 +128,24 @@ static const char *apc_date_conversion_fun(double value) return buf; } +static double apc_date_conversion_reverse(const char *date_string) +{ + int year, month, day; + long date; + + sscanf(date_string, "%04d/%02d/%02d", &year, &month, &day); + if(year >= 2070 || month > 12 || day > 31) + return 0; + year %= 100; + date = ((year / 10 & 0x0F) << 4) + (year % 10); + date += ((month / 10 & 0x0F) << 20) + ((month % 10) << 16); + date += ((day / 10 & 0x0F) << 12) + ((day % 10) << 8); + + return (double) date; +} + static info_lkp_t apc_date_conversion[] = { - { 0, NULL, apc_date_conversion_fun, NULL } + { 0, NULL, apc_date_conversion_fun, apc_date_conversion_reverse } }; /* This was determined empirically from observing a BackUPS LS 500 */ @@ -318,7 +334,7 @@ static hid_info_t apc_hid2nut[] = { { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.1f", 0, NULL }, /* Back-UPS 500 */ { "battery.temperature", 0, 0, "UPS.Battery.Temperature", NULL, "%s", 0, kelvin_celsius_conversion }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", 0, stringid_conversion }, - { "battery.mfr.date", 0, 0, "UPS.Battery.ManufacturerDate", NULL, "%s", 0, date_conversion }, + { "battery.mfr.date", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Battery.ManufacturerDate", NULL, "%s", HU_FLAG_SEMI_STATIC, date_conversion }, { "battery.mfr.date", 0, 0, "UPS.PowerSummary.APCBattReplaceDate", NULL, "%s", 0, apc_date_conversion }, /* Back-UPS 500, Back-UPS ES/CyberFort 500 */ { "battery.date", 0, 0, "UPS.Battery.APCBattReplaceDate", NULL, "%s", 0, apc_date_conversion }, /* Observed values: 0x0 on Back-UPS ES 650, 0x92501 on Back-UPS BF500 whose manufacture date was 2005/01/20 - this makes little sense but at least it's a valid date. */ diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index f08700d4db..7ac92a6018 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -436,9 +436,23 @@ static const char *date_conversion_fun(double value) return buf; } -/* FIXME? Do we need an inverse "nuf()" here? */ +static double date_conversion_reverse(const char* date_string) +{ + long year, month, day; + long date; + + sscanf(date_string, "%04ld/%02ld/%02ld", &year, &month, &day); + if(year - 1980 > 127 || month > 12 || day > 31) + return 0; + date = (year - 1980) << 9; + date += month << 5; + date += day; + + return (double) date; +} + info_lkp_t date_conversion[] = { - { 0, NULL, date_conversion_fun, NULL } + { 0, NULL, date_conversion_fun, date_conversion_reverse } }; /* returns statically allocated string - must not use it again before From d710fd814ed056ab3e682568edb61368c6aa42a3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 2 Mar 2022 23:32:08 +0000 Subject: [PATCH 286/700] drivers/snmp-ups.h: make sure WITH_SNMP_LKP_FUN_DUMMY is defined --- drivers/snmp-ups.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index 0f8c06b75f..c1549870ff 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -124,6 +124,10 @@ typedef int bool_t; # endif #endif +#ifndef WITH_SNMP_LKP_FUN_DUMMY +# define WITH_SNMP_LKP_FUN_DUMMY 0 +#endif + /* for lookup between OID values and INFO_ value */ typedef struct { int oid_value; /* SNMP OID value */ From d9c86ded1a48a4955c6d1e1a76fad8acf9cf8e11 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 7 Mar 2022 18:19:22 +0100 Subject: [PATCH 287/700] drivers/blazer_usb.c: upsdrv_initups(): address (usb_ctrl_charbuf)tbuf as (unsigned char) in bit maths --- drivers/blazer_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/blazer_usb.c b/drivers/blazer_usb.c index 78e271bf6d..8213b04b36 100644 --- a/drivers/blazer_usb.c +++ b/drivers/blazer_usb.c @@ -684,7 +684,7 @@ void upsdrv_initups(void) * This should allow automatic application of the workaround */ ret = usb_get_string(udev, 0, 0, (usb_ctrl_charbuf)tbuf, sizeof(tbuf)); if (ret >= 4) { - langid = tbuf[2] | (tbuf[3] << 8); + langid = (unsigned char)tbuf[2] | ((unsigned char)tbuf[3] << 8); upsdebugx(1, "First supported language ID: 0x%x (please report to the NUT maintainer!)", langid); } } From 87d35411e52ce815b50f9f998d5d676ef6ac2791 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 7 Mar 2022 18:21:09 +0100 Subject: [PATCH 288/700] drivers/libshut.c: BYTESWAP(): cast "in" to (uint16_t) for bit maths and use a full-width mask --- drivers/libshut.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/libshut.c b/drivers/libshut.c index 4376d588a4..7caab8dab5 100644 --- a/drivers/libshut.c +++ b/drivers/libshut.c @@ -334,7 +334,7 @@ static int shut_control_msg( /* Data portability */ /* realign packet data according to Endianess */ -#define BYTESWAP(in) (((in & 0xFF) << 8) + ((in & 0xFF00) >> 8)) +#define BYTESWAP(in) ((((uint16_t)in & 0x00FF) << 8) + (((uint16_t)in & 0xFF00) >> 8)) static void align_request(struct shut_ctrltransfer_s *ctrl) { #if (defined (WORDS_BIGENDIAN)) && (WORDS_BIGENDIAN) From d359fd33e93375c6c28dd7d3c5c0bb700be0b39f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 7 Mar 2022 18:22:19 +0100 Subject: [PATCH 289/700] drivers/libshut.c: shut_checksum(): address (usb_ctrl_charbuf)buf as (unsigned char) in bit maths --- drivers/libshut.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/libshut.c b/drivers/libshut.c index 7caab8dab5..817fda4642 100644 --- a/drivers/libshut.c +++ b/drivers/libshut.c @@ -893,7 +893,7 @@ static unsigned char shut_checksum( unsigned char chk=0; for(i=0; i Date: Mon, 7 Mar 2022 19:38:11 +0100 Subject: [PATCH 290/700] drivers/libusb{1,0}.c: {nut_}libusb_open(): cast rdlen calculations to uint16_t (or wider) [follow-up from #1320] --- drivers/libusb0.c | 4 ++-- drivers/libusb1.c | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/libusb0.c b/drivers/libusb0.c index d51313ec38..3fd54999ce 100644 --- a/drivers/libusb0.c +++ b/drivers/libusb0.c @@ -377,7 +377,7 @@ static int libusb_open(usb_dev_handle **udevp, upsdebug_hex(3, "HID descriptor, method 1", buf, 9); - rdlen1 = (uint8_t)buf[7] | ((uint8_t)buf[8] << 8); + rdlen1 = ((uint16_t)buf[7] & 0x00FF) | (((uint16_t)buf[8] & 0x00FF) << 8); } if (rdlen1 < -1) { @@ -407,7 +407,7 @@ static int libusb_open(usb_dev_handle **udevp, ) { p = (usb_ctrl_char *)&iface->extra[i]; upsdebug_hex(3, "HID descriptor, method 2", p, 9); - rdlen2 = (uint8_t)p[7] | ((uint8_t)p[8] << 8); + rdlen2 = ((uint16_t)p[7] & 0x00FF) | (((uint16_t)p[8] & 0x00FF) << 8); break; } } diff --git a/drivers/libusb1.c b/drivers/libusb1.c index dd07435c33..7bc9cf755a 100644 --- a/drivers/libusb1.c +++ b/drivers/libusb1.c @@ -141,7 +141,9 @@ static int nut_libusb_open(libusb_device_handle **udevp, #if (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER) || (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP) int retries; #endif - int rdlen1, rdlen2; /* report descriptor length, method 1+2 */ + /* libusb-1.0 usb_ctrl_charbufsize is uint16_t and we + * want the rdlen vars signed - so taking a wider type */ + int32_t rdlen1, rdlen2; /* report descriptor length, method 1+2 */ USBDeviceMatcher_t *m; libusb_device **devlist; ssize_t devcount = 0; @@ -159,7 +161,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, /* report descriptor */ unsigned char rdbuf[MAX_REPORT_SIZE]; - int rdlen; + int32_t rdlen; /* libusb base init */ if (libusb_init(NULL) < 0) { @@ -425,7 +427,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, upsdebug_hex(3, "HID descriptor, method 1", buf, 9); - rdlen1 = buf[7] | (buf[8] << 8); + rdlen1 = ((uint16_t)buf[7] & 0x00FF) | (((uint16_t)buf[8] & 0x00FF) << 8); } if (rdlen1 < -1) { @@ -450,7 +452,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, if (i+9 <= if_desc->extra_length && if_desc->extra[i] >= 9 && if_desc->extra[i+1] == 0x21) { p = &if_desc->extra[i]; upsdebug_hex(3, "HID descriptor, method 2", p, 9); - rdlen2 = p[7] | (p[8] << 8); + rdlen2 = ((uint16_t)p[7] & 0x00FF) | (((uint16_t)p[8] & 0x00FF) << 8); break; } } From aedcc7700df6c7b4df50ccacdd11d02c7cd96fd2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 7 Mar 2022 19:50:58 +0100 Subject: [PATCH 291/700] drivers/nutdrv_qx.c: upsdrv_initups(): cast langid calculations to uint16_t (or wider) [similar to #1320] --- drivers/nutdrv_qx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index f1addea839..4f292aee15 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -3058,7 +3058,7 @@ void upsdrv_initups(void) ret = usb_get_string(udev, 0, 0, (usb_ctrl_charbuf)tbuf, sizeof(tbuf)); if (ret >= 4) { - langid = tbuf[2] | (tbuf[3] << 8); + langid = ((uint16_t)tbuf[2] & 0x00FF) | (((uint16_t)tbuf[3] & 0x00FF) << 8); upsdebugx(1, "First supported language ID: 0x%x " "(please report to the NUT maintainer!)", From 581ba9e63cc0856d6d57da7cffc95e5d38a73b62 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 7 Mar 2022 19:55:17 +0100 Subject: [PATCH 292/700] drivers/riello_usb.c: whitespace fix --- drivers/riello_usb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/riello_usb.c b/drivers/riello_usb.c index d6f27e2be2..ef318d6067 100644 --- a/drivers/riello_usb.c +++ b/drivers/riello_usb.c @@ -95,9 +95,9 @@ static int cypress_setfeatures() /* Write features report */ ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, - 0x09, /* HID_REPORT_SET = 0x09 */ - 0 + (0x03 << 8), /* HID_REPORT_TYPE_FEATURE */ - 0, (usb_ctrl_charbuf) bufOut, 0x5, 1000); + 0x09, /* HID_REPORT_SET = 0x09 */ + 0 + (0x03 << 8), /* HID_REPORT_TYPE_FEATURE */ + 0, (usb_ctrl_charbuf) bufOut, 0x5, 1000); if (ret <= 0) { upsdebugx(3, "send: %s", ret ? nut_usb_strerror(ret) : "error"); From 643a09694abee881499870aad7215cf6ee221015 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 7 Mar 2022 19:55:43 +0100 Subject: [PATCH 293/700] drivers/riello_usb.c: Get_USB_Packet(): cast bit maths to (unsigned char) to err on safe side --- drivers/riello_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/riello_usb.c b/drivers/riello_usb.c index ef318d6067..2c7097a86c 100644 --- a/drivers/riello_usb.c +++ b/drivers/riello_usb.c @@ -197,7 +197,7 @@ static int Get_USB_Packet(uint8_t *buffer) } /* copy to buffer */ - size = inBuf[0] & 0x07; + size = (unsigned char)(inBuf[0]) & 0x07; if (size) memcpy(buffer, &inBuf[1], size); From d1b6935d6490532b272f37c746f70ce1110c3bc3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 7 Mar 2022 19:59:25 +0100 Subject: [PATCH 294/700] drivers/tripplite_usb.c: fix (commented-away) hard_shutdown() to "unsigned char" and bit maths similar to soft_shutdown() --- drivers/tripplite_usb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/tripplite_usb.c b/drivers/tripplite_usb.c index 8b18786cd1..f2890dd0d4 100644 --- a/drivers/tripplite_usb.c +++ b/drivers/tripplite_usb.c @@ -743,10 +743,11 @@ static int soft_shutdown(void) static int hard_shutdown(void) { int ret; - char buf[256], cmd_N[]="N\0x", cmd_K[] = "K\0"; + unsigned char buf[256], cmd_N[]="N\0x", cmd_K[] = "K\0"; - cmd_N[2] = offdelay; - cmd_N[1] = offdelay >> 8; + /* FIXME: Assumes memory layout / endianness? */ + cmd_N[2] = (unsigned char)(offdelay & 0x00FF); + cmd_N[1] = (unsigned char)(offdelay >> 8); upsdebugx(3, "hard_shutdown(offdelay=%d): N", offdelay); ret = send_cmd(cmd_N, sizeof(cmd_N), buf, sizeof(buf)); From d683b8b94f85744213729f2d68a5e6776f5904f7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 8 Mar 2022 07:45:26 +0200 Subject: [PATCH 295/700] drivers/libusb{0,1}.c: simplify back the bit-maths to cast into uint8_t following discussion in #1320 --- drivers/libusb0.c | 4 ++-- drivers/libusb1.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/libusb0.c b/drivers/libusb0.c index 3fd54999ce..4723f3a1e3 100644 --- a/drivers/libusb0.c +++ b/drivers/libusb0.c @@ -377,7 +377,7 @@ static int libusb_open(usb_dev_handle **udevp, upsdebug_hex(3, "HID descriptor, method 1", buf, 9); - rdlen1 = ((uint16_t)buf[7] & 0x00FF) | (((uint16_t)buf[8] & 0x00FF) << 8); + rdlen1 = ((uint8_t)buf[7]) | (((uint8_t)buf[8]) << 8); } if (rdlen1 < -1) { @@ -407,7 +407,7 @@ static int libusb_open(usb_dev_handle **udevp, ) { p = (usb_ctrl_char *)&iface->extra[i]; upsdebug_hex(3, "HID descriptor, method 2", p, 9); - rdlen2 = ((uint16_t)p[7] & 0x00FF) | (((uint16_t)p[8] & 0x00FF) << 8); + rdlen2 = ((uint8_t)p[7]) | (((uint8_t)p[8]) << 8); break; } } diff --git a/drivers/libusb1.c b/drivers/libusb1.c index 7bc9cf755a..44d8dc96d5 100644 --- a/drivers/libusb1.c +++ b/drivers/libusb1.c @@ -427,7 +427,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, upsdebug_hex(3, "HID descriptor, method 1", buf, 9); - rdlen1 = ((uint16_t)buf[7] & 0x00FF) | (((uint16_t)buf[8] & 0x00FF) << 8); + rdlen1 = ((uint8_t)buf[7]) | (((uint8_t)buf[8]) << 8); } if (rdlen1 < -1) { @@ -452,7 +452,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, if (i+9 <= if_desc->extra_length && if_desc->extra[i] >= 9 && if_desc->extra[i+1] == 0x21) { p = &if_desc->extra[i]; upsdebug_hex(3, "HID descriptor, method 2", p, 9); - rdlen2 = ((uint16_t)p[7] & 0x00FF) | (((uint16_t)p[8] & 0x00FF) << 8); + rdlen2 = ((uint8_t)p[7]) | (((uint8_t)p[8]) << 8); break; } } From b5b1585f4e6cac62af894a72642dc07c9043cfb6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 8 Mar 2022 07:45:54 +0200 Subject: [PATCH 296/700] drivers/nutdrv_qx.c: simplify back the bit-maths to cast into uint8_t following discussion in #1320 --- drivers/nutdrv_qx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index 4f292aee15..d047523230 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -3058,7 +3058,7 @@ void upsdrv_initups(void) ret = usb_get_string(udev, 0, 0, (usb_ctrl_charbuf)tbuf, sizeof(tbuf)); if (ret >= 4) { - langid = ((uint16_t)tbuf[2] & 0x00FF) | (((uint16_t)tbuf[3] & 0x00FF) << 8); + langid = ((uint8_t)tbuf[2]) | (((uint8_t)tbuf[3]) << 8); upsdebugx(1, "First supported language ID: 0x%x " "(please report to the NUT maintainer!)", From d2a125cf5875e58cc98672d1ff3a768b49ddbf1a Mon Sep 17 00:00:00 2001 From: Thanos Chatziathanassiou Date: Tue, 8 Mar 2022 11:40:25 +0200 Subject: [PATCH 297/700] some documentatyion fixes --- docs/man/socomec_jbus.txt | 76 +++++++++++++-------------------------- 1 file changed, 24 insertions(+), 52 deletions(-) diff --git a/docs/man/socomec_jbus.txt b/docs/man/socomec_jbus.txt index acefa4f3e2..8133b6607b 100644 --- a/docs/man/socomec_jbus.txt +++ b/docs/man/socomec_jbus.txt @@ -28,13 +28,14 @@ UPS with the following characteristics. 2. Connection: RS-232 -My netvision died out on me and appears impossible to procure another, so -I set about to talk to the UPS directly. The documentation yielded mild -success for the DIGYS I have handy over here. +These are typically provided with a Netvision WEB/SNMP management card / external +box that would be better served by the linkman:snmp-ups[8] driver. +In case netvision isn't available, you can hook up the UPS directly via the +serial port and use this driver. Currently, it has only been tested on the following model. -* DIGYS 3/3 !%kVA +* DIGYS 3/3 15kVA In theory, any Socomec JBUS model should work. It should be discovered as ``Unknown Socomec JBUS'' with a numeric id that I'll need to add it @@ -53,11 +54,9 @@ CABLING ------- The UPS has an RS-232 port which should be connected with a NULL-modem -cable to a PC serial port. The UPS, mine at least, has curiously a female -DB9 connector, so if you construct the cable yourself, use a male DB9 on -one end. - -It only uses RX,TX and ground, so do not strain too much with DTR et all +cable to a PC serial port. The UPS tested has a female DB9 connector, +so if you construct the cable yourself, make note of the connector type to +avoid using gender changers. RS-232 is supported on all operating systems, either via a built-in serial port on your computer, or by using an external USB-to-RS-232 converter. If @@ -68,8 +67,9 @@ operating system. INSTALLATION ------------ -This driver is not built by default. You can build it by installing libmodbus -(with development packages) and running +This driver should be built by default if libmodbus and development headers are +available. You can force the configure script to build it with the following +arguments: configure --with-serial=yes --with-modbus=yes @@ -83,42 +83,10 @@ or `/dev/ttyUSB0` on Linux, or `/dev/ttyU0` on FreeBSD (note the capital "U"). A built-in serial port can be identified as `/dev/ttyS0` on Linux or one of `/dev/cua*` names on FreeBSD. -EXTRA ARGUMENTS ---------------- -This driver supports the following optional settings in the -linkman:ups.conf[5] file: - -*offdelay=*'value':: -Time to wait before shutting down the UPS (seconds), acceptable range is -6 seconds (0.1 minutes) to 5940 seconds (99 minutes). Defaults to 60 seconds. -Must be a multiple of 6 seconds. To ensure your system has adequate time -to shut down after a power failure, it's highly recommended to adjust -*offdelay*. - -*rebootdelay=*'value':: -Time to wait before rebooting the UPS (seconds), acceptable range is -6 seconds (0.1 minutes) to 5940 seconds (99 minutes). Defaults to 60 seconds. -Must be a multiple of 6 seconds. This is used by the *shutdown.reboot.graceful* -instant command. If you've adjusted *offdelay*, you should also adjust -*rebootdelay*. - -*ondelay=*'value':: -Time to wait before switching on the UPS (seconds), acceptable range is -60 seconds (1 minutes) to 5940 seconds (99 minutes). Defaults to 60 seconds. -Must be a multiple of 60 seconds (not 6 seconds). You don't need to adjust -this delay unless you have special requirements. - -NOTE: Due to hardware limitation, in this driver, *ondelay* is respected -only when line power is available. If a power failure has occurred, the -UPS and the load is always immediately switched on, as soon (or as late) -as line power is restored. - INSTANT COMMANDS ---------------- -This driver does not (yet?) support any commands sent to the UPS, mostly -because I'm too afraid to meddle with the UPS that is currently powering -half of our datacenter. +This driver does not (yet?) support sending commands to the UPS. VARIABLES --------- @@ -131,14 +99,18 @@ inconsistencies in the documentation, I'm witholding from trying them. KNOWN ISSUES AND BUGS --------------------- -Well, it is an alpha release at best, so no promises. Mostly based on the work of -Yifeng Li on the huawei-ups2000 in that very same source tree. +Well, it is an alpha release at best, but so far appears to report the UPS status +reliably. Mostly based on the work of Yifeng Li on the huawei-ups2000 +in that very same source tree. -Battery status has a non-fatal read failure -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Read failure on some JBUS addresses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It's usually harmless and can be safely ignored. It's only logged for -informative purposes (*LOG_INFO*), not as a warning or error. +The driver polls all documented JBUS addresses and it is quite possible that your +UPS model does not support one of them. (eg the Digys does not support address +0x1020 which should provide the current UPS status). +This should be logged with LOG_ERR from modbus_read_input_registers() +along with the address that produced the error. Data stale ~~~~~~~~~~~ @@ -149,8 +121,8 @@ you should see error messages similar to the example below in the system log. socomec_jbus: modbus_read_input_registers(addr:XXXX, count:Z): Illegal data address - upsd: Data for UPS [huawei] is stale - check driver - upsd: UPS [huawei] data is no longer stale + upsd: Data for UPS [socomec] is stale - check driver + upsd: UPS [socomec] data is no longer stale So far all known problems have been fixed by the author, but an unknown one cannot be ruled out. If you have encountered "data stale" problems during From 20da521c50e8bbd1913516a6855a160ccbf22c09 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 8 Mar 2022 07:58:03 +0200 Subject: [PATCH 298/700] tests/getvaluetest.c: check different methods to combine two "wire" bytes into a lenght word (follows up for #1320), hopefully all work the same on all architectures --- tests/getvaluetest.c | 102 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/tests/getvaluetest.c b/tests/getvaluetest.c index 0723969f00..769df06ff2 100644 --- a/tests/getvaluetest.c +++ b/tests/getvaluetest.c @@ -10,6 +10,7 @@ * * Copyright (C) * 2021 Nick Briggs + * 2022 Jim Klimov * * 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 @@ -34,6 +35,7 @@ #include #include #include "hidtypes.h" +#include "usb-common.h" #include "common.h" void GetValue(const unsigned char *Buf, HIDData_t *pData, long *pValue); @@ -125,6 +127,106 @@ static int RunBuiltInTests(char *argv[]) { exitStatus = 1; } } + + /* Emulate rdlen calculations in libusb{0,1}.c or + * langid calculations in nutdrv_qx.c; in these + * cases we take two bytes (cast from usb_ctrl_char + * type, may be signed depending on used API version) + * from the protocol buffer, and build a platform + * dependent representation of a two-byte word. + */ + usb_ctrl_char bufC[2]; + signed char bufS[2]; + unsigned char bufU[2]; + int rdlen; + + /* Example from issue https://github.com/networkupstools/nut/issues/1261 + * where resulting length 0x01a9 should be "425" but ended up "-87" */ + bufC[0] = (usb_ctrl_char)0xa9; + bufC[1] = (usb_ctrl_char)0x01; + + bufS[0] = (signed char)0xa9; + bufS[1] = (signed char)0x01; + + bufU[0] = (unsigned char)0xa9; + bufU[1] = (unsigned char)0x01; + + /* Check different conversion methods and hope current build CPU, + * C implementation etc. do not mess up bit-shifting vs rotation, + * zeroing high bits, int type width extension et al. If something + * is mismatched below, the production NUT code may need adaptations + * for that platform to count stuff correctly! + */ + printf("\nTesting bit-shifting approaches used in codebase to get 2-byte int lengths from wire bytes:\n"); + printf("(Expected correct value is '425', incorrect '-87' or other)\n"); + +#define REPORT_VERDICT(expected) { if (expected) { printf(" - PASS\n"); } else { printf(" - FAIL\n"); exitStatus = 1; } } + + rdlen = bufC[0] | (bufC[1] << 8); + printf(" * reference: no casting, usb_ctrl_char :\t%d\t(depends on libusb API built against)", rdlen); + REPORT_VERDICT (rdlen == 425 || rdlen == -87) + + rdlen = bufS[0] | (bufS[1] << 8); + printf(" * reference: no casting, signed char :\t%d\t(expected '-87' here)", rdlen); + REPORT_VERDICT (rdlen == -87) + + rdlen = bufU[0] | (bufU[1] << 8); + printf(" * reference: no casting, unsigned char :\t%d\t(expected '425')", rdlen); + REPORT_VERDICT (rdlen == 425) + + + rdlen = (uint8_t)bufC[0] | ((uint8_t)bufC[1] << 8); + printf(" * uint8_t casting, usb_ctrl_char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = (uint8_t)bufS[0] | ((uint8_t)bufS[1] << 8); + printf(" * uint8_t casting, signed char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = (uint8_t)bufU[0] | ((uint8_t)bufU[1] << 8); + printf(" * uint8_t casting, unsigned char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + + rdlen = ((uint8_t)bufC[0]) | (((uint8_t)bufC[1]) << 8); + printf(" * uint8_t casting with parentheses, usb_ctrl_char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = ((uint8_t)bufS[0]) | (((uint8_t)bufS[1]) << 8); + printf(" * uint8_t casting with parentheses, signed char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = ((uint8_t)bufU[0]) | (((uint8_t)bufU[1]) << 8); + printf(" * uint8_t casting with parentheses, unsigned char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + + rdlen = ((uint16_t)(bufC[0]) & 0x00FF) | (((uint16_t)(bufC[1]) & 0x00FF) << 8); + printf(" * uint16_t casting with 8-bit mask, usb_ctrl_char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = ((uint16_t)(bufS[0]) & 0x00FF) | (((uint16_t)(bufS[1]) & 0x00FF) << 8); + printf(" * uint16_t casting with 8-bit mask, signed char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = ((uint16_t)(bufU[0]) & 0x00FF) | (((uint16_t)(bufU[1]) & 0x00FF) << 8); + printf(" * uint16_t casting with 8-bit mask, unsigned char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + + rdlen = 256 * (uint8_t)(bufC[1]) + (uint8_t)(bufC[0]); + printf(" * uint8_t casting with multiplication, usb_ctrl_char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = 256 * (uint8_t)(bufS[1]) + (uint8_t)(bufS[0]); + printf(" * uint8_t casting with multiplication, signed char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = 256 * (uint8_t)(bufU[1]) + (uint8_t)(bufU[0]); + printf(" * uint8_t casting with multiplication, unsigned char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + return (exitStatus); } From 97109ec8c9e49d8ea36203c9841697bfae442eb4 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Thu, 10 Mar 2022 11:55:16 +0100 Subject: [PATCH 299/700] SNMP subdriver generation script: fixes and improvements Signed-off-by: Arnaud Quette --- scripts/subdriver/gen-snmp-subdriver.sh | 33 ++++++++++++------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/scripts/subdriver/gen-snmp-subdriver.sh b/scripts/subdriver/gen-snmp-subdriver.sh index d135e1dff5..9da31736b5 100755 --- a/scripts/subdriver/gen-snmp-subdriver.sh +++ b/scripts/subdriver/gen-snmp-subdriver.sh @@ -3,13 +3,13 @@ # an auxiliary script to produce a "stub" snmp-ups subdriver from # SNMP data from a real agent or from dump files # -# Version: 0.12 +# Version: 0.13 # # See also: docs/snmp-subdrivers.txt # # Copyright (C) # 2011 - 2012 Arnaud Quette -# 2015 - 2019 Arnaud Quette +# 2015 - 2022 Eaton (author: Arnaud Quette ) # 2011 Jim Klimov # # This program is free software; you can redistribute it and/or modify @@ -131,13 +131,15 @@ get_snmp_data() { } generate_C() { - # create file names + # create file names, lowercase LDRIVER=`echo $DRIVER | tr A-Z a-z` UDRIVER=`echo $DRIVER | tr a-z A-Z` + # keep dashes in name for files CFILE="$LDRIVER-mib.c" HFILE="$LDRIVER-mib.h" - - #FIXME: LDRIVER & UDRIVER => replace - by _ + # but replace with underscores for the structures and defines + LDRIVER=`echo $LDRIVER | tr - _` + UDRIVER=`echo $UDRIVER | tr - _` # generate header file echo "Creating $HFILE" @@ -245,10 +247,10 @@ generate_C() { /* standard MIB items; if the vendor MIB contains better OIDs for * this (e.g. with daisy-chain support), consider adding those here */ - { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, - { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, - { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, EOF + printf "\t{ \"device.description\", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, \".1.3.6.1.2.1.1.1.0\", NULL, SU_FLAG_OK, NULL },\n" >> ${CFILE} + printf "\t{ \"device.contact\", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, \".1.3.6.1.2.1.1.4.0\", NULL, SU_FLAG_OK, NULL },\n" >> ${CFILE} + printf "\t{ \"device.location\", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, \".1.3.6.1.2.1.1.6.0\", NULL, SU_FLAG_OK, NULL },\n" >> ${CFILE} # extract OID string paths, one by one LINENB="0" @@ -270,14 +272,8 @@ generate_C() { done < ${STRWALKFILE} >> ${CFILE} # append footer - cat >> "$CFILE" <<-EOF - - /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL } - }; - - mib2nut_info_t ${LDRIVER} = { "${LDRIVER}", ${UDRIVER}_MIB_VERSION, NULL, NULL, ${LDRIVER}_mib, ${UDRIVER}_DEVICE_SYSOID }; - EOF + printf "\t/* end of structure. */\n\t{ NULL, 0, 0, NULL, NULL, 0, NULL }\n};" >> "$CFILE" + printf "mib2nut_info_t ${LDRIVER} = { \"${LDRIVER}\", ${UDRIVER}_MIB_VERSION, NULL, NULL, ${LDRIVER}_mib, ${UDRIVER}_SYSOID };" >> "$CFILE" } # process command line options @@ -385,6 +381,9 @@ else while IFS=' = ' read NUM_OID OID_VALUE do STR_OID=`snmptranslate -Os -m ALL -M+. $NUM_OID 2>/dev/null` + # Uncomment the below line to get debug logs + #echo "Got: $STR_OID = $OID_VALUE" + echo -n "." echo "$STR_OID = $OID_VALUE" >> $STRWALKFILE done < $NUMWALKFILE echo " done" @@ -442,7 +441,7 @@ STRWALKFILE=${TMP_STRWALKFILE} NUM_OID_COUNT="`cat $NUMWALKFILE | wc -l`" STR_OID_COUNT="`cat $STRWALKFILE | wc -l`" -echo "COUNT = $NUM_OID_COUNT / $NUM_OID_COUNT" +echo "SNMP OIDs extracted = $NUM_OID_COUNT / $NUM_OID_COUNT" generate_C From f1d2a708e56f964a284799419e6af57ce3e161b1 Mon Sep 17 00:00:00 2001 From: Arnaud Quette Date: Thu, 10 Mar 2022 13:59:44 +0100 Subject: [PATCH 300/700] SNMP subdriver generation script: minor reformatting Signed-off-by: Arnaud Quette --- scripts/subdriver/gen-snmp-subdriver.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/subdriver/gen-snmp-subdriver.sh b/scripts/subdriver/gen-snmp-subdriver.sh index 9da31736b5..e1e463cf92 100755 --- a/scripts/subdriver/gen-snmp-subdriver.sh +++ b/scripts/subdriver/gen-snmp-subdriver.sh @@ -272,8 +272,8 @@ generate_C() { done < ${STRWALKFILE} >> ${CFILE} # append footer - printf "\t/* end of structure. */\n\t{ NULL, 0, 0, NULL, NULL, 0, NULL }\n};" >> "$CFILE" - printf "mib2nut_info_t ${LDRIVER} = { \"${LDRIVER}\", ${UDRIVER}_MIB_VERSION, NULL, NULL, ${LDRIVER}_mib, ${UDRIVER}_SYSOID };" >> "$CFILE" + printf "\n\t/* end of structure. */\n\t{ NULL, 0, 0, NULL, NULL, 0, NULL }\n};\n\n" >> "$CFILE" + printf "mib2nut_info_t ${LDRIVER} = { \"${LDRIVER}\", ${UDRIVER}_MIB_VERSION, NULL, NULL, ${LDRIVER}_mib, ${UDRIVER}_SYSOID };\n" >> "$CFILE" } # process command line options From 37467839f0042010511480c99dbe3c4a89b34bd9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 10 Mar 2022 23:18:39 +0100 Subject: [PATCH 301/700] Update gen-snmp-subdriver.sh Quote variable expansions; replace `echo -n` with a more portable `printf` --- scripts/subdriver/gen-snmp-subdriver.sh | 82 ++++++++++++------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/scripts/subdriver/gen-snmp-subdriver.sh b/scripts/subdriver/gen-snmp-subdriver.sh index e1e463cf92..6402cdcfe6 100755 --- a/scripts/subdriver/gen-snmp-subdriver.sh +++ b/scripts/subdriver/gen-snmp-subdriver.sh @@ -90,30 +90,30 @@ MODE=0 NAME=gen-snmp-subdriver TMPDIR="${TEMPDIR:-/tmp}" SYSOID_NUMBER=".1.3.6.1.2.1.1.2.0" -DEBUG=`mktemp "$TMPDIR/$NAME-DEBUG.XXXXXX"` -DFL_NUMWALKFILE=`mktemp "$TMPDIR/$NAME-NUMWALK.XXXXXX"` -DFL_STRWALKFILE=`mktemp "$TMPDIR/$NAME-STRWALK.XXXXXX"` -TMP_NUMWALKFILE=`mktemp "$TMPDIR/$NAME-TMP-NUMWALK.XXXXXX"` -TMP_STRWALKFILE=`mktemp "$TMPDIR/$NAME-TMP-STRWALK.XXXXXX"` +DEBUG="`mktemp "$TMPDIR/$NAME-DEBUG.XXXXXX"`" +DFL_NUMWALKFILE="`mktemp "$TMPDIR/$NAME-NUMWALK.XXXXXX"`" +DFL_STRWALKFILE="`mktemp "$TMPDIR/$NAME-STRWALK.XXXXXX"`" +TMP_NUMWALKFILE="`mktemp "$TMPDIR/$NAME-TMP-NUMWALK.XXXXXX"`" +TMP_STRWALKFILE="`mktemp "$TMPDIR/$NAME-TMP-STRWALK.XXXXXX"`" get_snmp_data() { # 1) get the sysOID (points the mfr specif MIB), apart if there's an override if [ -z "$SYSOID" ] then - SYSOID=`snmpget -On -v1 -c $COMMUNITY -Ov $HOSTNAME $SYSOID_NUMBER | cut -d' ' -f2` + SYSOID="`snmpget -On -v1 -c "$COMMUNITY" -Ov "$HOSTNAME" "$SYSOID_NUMBER" | cut -d' ' -f2`" echo "sysOID retrieved: ${SYSOID}" else echo "Using the provided sysOID override ($SYSOID)" fi - DEVICE_SYSOID=$SYSOID + DEVICE_SYSOID="$SYSOID" OID_COUNT=0 - while (test $OID_COUNT -eq 0) + while (test "$OID_COUNT" -eq 0) do # 2) get the content of the mfr specif MIB echo "Retrieving SNMP information. This may take some time" - snmpwalk -On -v1 -c $COMMUNITY $HOSTNAME $SYSOID 2>/dev/null 1> $DFL_NUMWALKFILE - snmpwalk -Os -v1 -m ALL -M$MIBS_DIRLIST -c $COMMUNITY $HOSTNAME $SYSOID 2>/dev/null 1> $DFL_STRWALKFILE + snmpwalk -On -v1 -c "$COMMUNITY" "$HOSTNAME" "$SYSOID" 2>/dev/null 1> "$DFL_NUMWALKFILE" + snmpwalk -Os -v1 -m ALL -M"$MIBS_DIRLIST" -c "$COMMUNITY" "$HOSTNAME" "$SYSOID" 2>/dev/null 1> "$DFL_STRWALKFILE" # 3) test return value of the walk, and possibly ramp-up the path to get something. # The sysOID mechanism only works if we're pointed somehow in the right direction @@ -132,14 +132,14 @@ get_snmp_data() { generate_C() { # create file names, lowercase - LDRIVER=`echo $DRIVER | tr A-Z a-z` - UDRIVER=`echo $DRIVER | tr a-z A-Z` + LDRIVER="`echo "$DRIVER" | tr A-Z a-z`" + UDRIVER="`echo "$DRIVER" | tr a-z A-Z`" # keep dashes in name for files CFILE="$LDRIVER-mib.c" HFILE="$LDRIVER-mib.h" # but replace with underscores for the structures and defines - LDRIVER=`echo $LDRIVER | tr - _` - UDRIVER=`echo $UDRIVER | tr - _` + LDRIVER="`echo "$LDRIVER" | tr - _`" + UDRIVER="`echo "$UDRIVER" | tr - _`" # generate header file echo "Creating $HFILE" @@ -248,17 +248,17 @@ generate_C() { * this (e.g. with daisy-chain support), consider adding those here */ EOF - printf "\t{ \"device.description\", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, \".1.3.6.1.2.1.1.1.0\", NULL, SU_FLAG_OK, NULL },\n" >> ${CFILE} - printf "\t{ \"device.contact\", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, \".1.3.6.1.2.1.1.4.0\", NULL, SU_FLAG_OK, NULL },\n" >> ${CFILE} - printf "\t{ \"device.location\", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, \".1.3.6.1.2.1.1.6.0\", NULL, SU_FLAG_OK, NULL },\n" >> ${CFILE} + printf "\t{ \"device.description\", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, \".1.3.6.1.2.1.1.1.0\", NULL, SU_FLAG_OK, NULL },\n" >> "${CFILE}" + printf "\t{ \"device.contact\", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, \".1.3.6.1.2.1.1.4.0\", NULL, SU_FLAG_OK, NULL },\n" >> "${CFILE}" + printf "\t{ \"device.location\", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, \".1.3.6.1.2.1.1.6.0\", NULL, SU_FLAG_OK, NULL },\n" >> "${CFILE}" # extract OID string paths, one by one LINENB="0" while IFS= read -r line; do LINENB="`expr $LINENB + 1`" FULL_STR_OID="$line" - STR_OID="`echo $line | cut -d'.' -f1`" - echo $line | grep STRING > /dev/null + STR_OID="`echo "$line" | cut -d'.' -f1`" + echo "$line" | grep STRING > /dev/null if [ $? -eq 0 ]; then ST_FLAG_TYPE="ST_FLAG_STRING" SU_INFOSIZE="SU_INFOSIZE" @@ -267,9 +267,9 @@ generate_C() { SU_INFOSIZE="1" fi # get the matching numeric OID - NUM_OID="`sed -n ${LINENB}p ${NUMWALKFILE} | cut -d' ' -f1`" + NUM_OID="`sed -n "${LINENB}p" "${NUMWALKFILE}" | cut -d' ' -f1`" printf "\t/* ${FULL_STR_OID} */\n\t{ \"unmapped.${STR_OID}\", ${ST_FLAG_TYPE}, ${SU_INFOSIZE}, \"${NUM_OID}\", NULL, SU_FLAG_OK, NULL },\n" - done < ${STRWALKFILE} >> ${CFILE} + done < "${STRWALKFILE}" >> "${CFILE}" # append footer printf "\n\t/* end of structure. */\n\t{ NULL, 0, 0, NULL, NULL, 0, NULL }\n};\n\n" >> "$CFILE" @@ -322,8 +322,8 @@ if [ -z "$NUMWALKFILE" ]; then # mode 1: directly get SNMP data from a real agent echo "Mode 1 selected" MODE=1 - NUMWALKFILE=$DFL_NUMWALKFILE - STRWALKFILE=$DFL_STRWALKFILE + NUMWALKFILE="$DFL_NUMWALKFILE" + STRWALKFILE="$DFL_STRWALKFILE" # check if Net SNMP is available if [ -z "`command -v snmpget`" -o -z "`command -v snmpwalk`" ] && \ @@ -335,7 +335,7 @@ if [ -z "$NUMWALKFILE" ]; then while [ -z "$HOSTNAME" ]; do printf "\n\tPlease enter the SNMP host IP address or name.\n" read -p "SNMP host IP name or address: " HOSTNAME < /dev/tty - if echo $HOSTNAME | egrep -q '[^a-zA-Z0-9]'; then + if echo "$HOSTNAME" | egrep -q '[^a-zA-Z0-9]'; then echo "Please use only letters and digits" HOSTNAME="" fi @@ -351,9 +351,9 @@ else # then use snmptranslate to get the string OIDs and generated the string SNMP walk echo "Mode 3 selected" MODE=3 - RAWWALKFILE=$NUMWALKFILE - NUMWALKFILE=$DFL_NUMWALKFILE - STRWALKFILE=$DFL_STRWALKFILE + RAWWALKFILE="$NUMWALKFILE" + NUMWALKFILE="$DFL_NUMWALKFILE" + STRWALKFILE="$DFL_STRWALKFILE" # check for actual file existence if [ ! -f "$RAWWALKFILE" ]; then @@ -362,7 +362,7 @@ else fi # Extract the sysOID # Format is "1.3.6.1.2.1.1.2.0 = OID: 1.3.6.1.4.1.4555.1.1.1" - DEVICE_SYSOID=`grep 1.3.6.1.2.1.1.2.0 $RAWWALKFILE | cut -d' ' -f4` + DEVICE_SYSOID="`grep 1.3.6.1.2.1.1.2.0 "$RAWWALKFILE" | cut -d' ' -f4`" if [ -n "$DEVICE_SYSOID" ]; then echo "Found sysOID $DEVICE_SYSOID" else @@ -373,19 +373,19 @@ else # Switch to the entry point, and extract the subtree # Extract the numeric walk echo -n "Extracting numeric SNMP walk..." - grep $DEVICE_SYSOID $RAWWALKFILE | egrep -v "1.3.6.1.2.1.1.2.0" 2>/dev/null 1> $NUMWALKFILE + grep "$DEVICE_SYSOID" "$RAWWALKFILE" | egrep -v "1.3.6.1.2.1.1.2.0" 2>/dev/null 1> "$NUMWALKFILE" echo " done" # Create the string walk from a translation of the numeric one echo -n "Converting string SNMP walk..." while IFS=' = ' read NUM_OID OID_VALUE do - STR_OID=`snmptranslate -Os -m ALL -M+. $NUM_OID 2>/dev/null` + STR_OID="`snmptranslate -Os -m ALL -M+. "$NUM_OID" 2>/dev/null`" # Uncomment the below line to get debug logs #echo "Got: $STR_OID = $OID_VALUE" - echo -n "." - echo "$STR_OID = $OID_VALUE" >> $STRWALKFILE - done < $NUMWALKFILE + printf "." + echo "$STR_OID = $OID_VALUE" >> "$STRWALKFILE" + done < "$NUMWALKFILE" echo " done" else # mode 2: get data from files @@ -398,7 +398,7 @@ else Please enter the value of sysOID, as displayed by snmp-ups. For example '.1.3.6.1.4.1.2254.2.4'. You can get it using: snmpget -v1 -c XXX $SYSOID_NUMBER" read -p "Value of sysOID: " SYSOID < /dev/tty - if echo $SYSOID | egrep -q '[^0-9.]'; then + if echo "$SYSOID" | egrep -q '[^0-9.]'; then echo "Please use only the numeric form, with dots and digits" SYSOID="" fi @@ -425,21 +425,21 @@ while [ -z "$DRIVER" ]; do Please enter a name for this driver. Use only letters and numbers. Use natural (upper- and lowercase) capitalization, e.g., 'Belkin', 'APC'." read -p "Name of subdriver: " DRIVER < /dev/tty - if echo $DRIVER | egrep -q '[^a-zA-Z0-9]'; then + if echo "$DRIVER" | egrep -q '[^a-zA-Z0-9]'; then echo "Please use only letters and digits" DRIVER="" fi done # remove blank and "End of MIB" lines -egrep -e "^[[:space:]]?$" -e "End of MIB" -v ${NUMWALKFILE} > ${TMP_NUMWALKFILE} -egrep -e "^[[:space:]]?$" -e "End of MIB" -v ${STRWALKFILE} > ${TMP_STRWALKFILE} -NUMWALKFILE=${TMP_NUMWALKFILE} -STRWALKFILE=${TMP_STRWALKFILE} +egrep -e "^[[:space:]]?$" -e "End of MIB" -v "${NUMWALKFILE}" > "${TMP_NUMWALKFILE}" +egrep -e "^[[:space:]]?$" -e "End of MIB" -v "${STRWALKFILE}" > "${TMP_STRWALKFILE}" +NUMWALKFILE="${TMP_NUMWALKFILE}" +STRWALKFILE="${TMP_STRWALKFILE}" # FIXME: sanity checks (! -z contents -a same `wc -l`) -NUM_OID_COUNT="`cat $NUMWALKFILE | wc -l`" -STR_OID_COUNT="`cat $STRWALKFILE | wc -l`" +NUM_OID_COUNT="`cat "$NUMWALKFILE" | wc -l`" +STR_OID_COUNT="`cat "$STRWALKFILE" | wc -l`" echo "SNMP OIDs extracted = $NUM_OID_COUNT / $NUM_OID_COUNT" From da52ff5b66870b9f7b26cc182b7a8b76e8198af3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 10 Mar 2022 19:38:23 +0200 Subject: [PATCH 302/700] gen-snmp-subdriver.sh: fix valid chars HOSTNAME input (e.g. IP) --- scripts/subdriver/gen-snmp-subdriver.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/subdriver/gen-snmp-subdriver.sh b/scripts/subdriver/gen-snmp-subdriver.sh index 6402cdcfe6..f7a60984d6 100755 --- a/scripts/subdriver/gen-snmp-subdriver.sh +++ b/scripts/subdriver/gen-snmp-subdriver.sh @@ -335,8 +335,8 @@ if [ -z "$NUMWALKFILE" ]; then while [ -z "$HOSTNAME" ]; do printf "\n\tPlease enter the SNMP host IP address or name.\n" read -p "SNMP host IP name or address: " HOSTNAME < /dev/tty - if echo "$HOSTNAME" | egrep -q '[^a-zA-Z0-9]'; then - echo "Please use only letters and digits" + if echo "$HOSTNAME" | egrep -q '[^a-zA-Z0-9.-]'; then + echo "Please use only letters, digits, dash and period character" HOSTNAME="" fi done From 7457817cf627dacacb4594dda5e1ba63b4c6e40b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 10 Mar 2022 19:47:09 +0200 Subject: [PATCH 303/700] gen-snmp-subdriver.sh: put back indented CFILE markup (fix here-docs, instead of unmaintainable printf) --- scripts/subdriver/gen-snmp-subdriver.sh | 28 ++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/scripts/subdriver/gen-snmp-subdriver.sh b/scripts/subdriver/gen-snmp-subdriver.sh index f7a60984d6..dc5fb492a9 100755 --- a/scripts/subdriver/gen-snmp-subdriver.sh +++ b/scripts/subdriver/gen-snmp-subdriver.sh @@ -142,6 +142,7 @@ generate_C() { UDRIVER="`echo "$UDRIVER" | tr - _`" # generate header file + # NOTE: with <<-EOF leading TABs are all stripped echo "Creating $HFILE" cat > "$HFILE" <<-EOF /* ${HFILE} - subdriver to monitor ${DRIVER} SNMP devices with NUT @@ -176,7 +177,8 @@ generate_C() { EOF # generate source file - # create header + # create heading boilerblate + # NOTE: with <<-EOF leading TABs are all stripped echo "Creating $CFILE" cat > "$CFILE" <<-EOF /* ${CFILE} - subdriver to monitor ${DRIVER} SNMP devices with NUT @@ -248,9 +250,13 @@ generate_C() { * this (e.g. with daisy-chain support), consider adding those here */ EOF - printf "\t{ \"device.description\", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, \".1.3.6.1.2.1.1.1.0\", NULL, SU_FLAG_OK, NULL },\n" >> "${CFILE}" - printf "\t{ \"device.contact\", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, \".1.3.6.1.2.1.1.4.0\", NULL, SU_FLAG_OK, NULL },\n" >> "${CFILE}" - printf "\t{ \"device.location\", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, \".1.3.6.1.2.1.1.6.0\", NULL, SU_FLAG_OK, NULL },\n" >> "${CFILE}" + + # Same file, indented text (TABs not stripped): + cat >> "$CFILE" <> "${CFILE}" - # append footer - printf "\n\t/* end of structure. */\n\t{ NULL, 0, 0, NULL, NULL, 0, NULL }\n};\n\n" >> "$CFILE" - printf "mib2nut_info_t ${LDRIVER} = { \"${LDRIVER}\", ${UDRIVER}_MIB_VERSION, NULL, NULL, ${LDRIVER}_mib, ${UDRIVER}_SYSOID };\n" >> "$CFILE" + # append footer (TABs not stripped): + cat >> "$CFILE" < Date: Fri, 11 Mar 2022 15:30:26 +0100 Subject: [PATCH 304/700] NEWS: rename pending NUT release from 2.7.5 to 2.8.0 (config files using new keywords are not backwards-compatible, not usable by old binaries) --- NEWS | 7 ++++++- UPGRADING | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index f2c13b0ee6..a9c9ce22ce 100644 --- a/NEWS +++ b/NEWS @@ -5,7 +5,12 @@ ChangeLog file (generated for release archives), or to the Git version control history for "live" codebase. --------------------------------------------------------------------------- -Release notes for NUT 2.7.5 - what's new since 2.7.4: +Release notes for NUT 2.8.0 - what's new since 2.7.4: + + - New (optional) keywords for configuration files were added, + so existing NUT 2.7.x builds would not accept them if some + deployments switch versions back and forth -- due to this, + semantically the version was bumped to NUT 2.8.x. - Add support for openssl-1.1.0 (Arjen de Korte) diff --git a/UPGRADING b/UPGRADING index f184a86864..854a1130fd 100644 --- a/UPGRADING +++ b/UPGRADING @@ -7,7 +7,7 @@ This file lists changes that affect users who installed older versions of this software. When upgrading from an older version, be sure to check this file to see if you need to make changes to your system. -Changes from 2.7.4 to 2.7.5 +Changes from 2.7.4 to 2.8.0 --------------------------- - Note to distribution packagers: this version hopefully learns from many From b7688e036f9e2c4621bc3e69ec20288463b84950 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 16:22:43 +0100 Subject: [PATCH 305/700] clients/upsmon.c: apply_for_primary(): request elevation by PRIMARY, fall back to MASTER (for older upsd builds) --- clients/upsmon.c | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/clients/upsmon.c b/clients/upsmon.c index ab0245ffe1..4b65fc1795 100644 --- a/clients/upsmon.c +++ b/clients/upsmon.c @@ -217,9 +217,14 @@ static void do_notify(const utype_t *ups, int ntype) * we do not need to try becoming a primary). This currently * propagates further as the return value of do_upsd_auth(). */ +/* TODO: Includes API change in NUT 2.8.0 to replace deprecated + * keywords "MASTER" with "PRIMARY", and "SLAVE" with "SECONDARY", + * (and backwards-compatible alias handling) + */ static int apply_for_primary(utype_t *ups) { char buf[SMALLBUF]; + char upscli_readraw_error; /* don't bother if we're not configured as a primary for this ups */ if (!flag_isset(ups->status, ST_PRIMARY)) @@ -232,10 +237,13 @@ static int apply_for_primary(utype_t *ups) return 0; } - /* TODO: Use PRIMARY first but if talking to older server, retry with MASTER */ - snprintf(buf, sizeof(buf), "MASTER %s\n", ups->upsname); + /* Use PRIMARY first but if talking to older server, retry with MASTER */ + snprintf(buf, sizeof(buf), "PRIMARY %s\n", ups->upsname); if (upscli_sendline(&ups->conn, buf, strlen(buf)) < 0) { + /* File descriptor not suitable, net_write() errors, etc. + * Not connected to issues with PRIMARY vs. MASTER keyword. + */ upslogx(LOG_ALERT, "Can't set primary managerial mode on UPS [%s] - %s", ups->sys, upscli_strerror(&ups->conn)); return 0; @@ -245,8 +253,35 @@ static int apply_for_primary(utype_t *ups) if (!strncmp(buf, "OK", 2)) return 1; - /* not ERR, but not caught by readline either? */ + /* Try the older keyword */ + upsdebugx(3, + "%s: Server did not grant PRIMARY mode on UPS [%s], " + "retry with older MASTER keyword", + __func__, ups->upsname); + snprintf(buf, sizeof(buf), "MASTER %s\n", ups->upsname); + + if (upscli_sendline(&ups->conn, buf, strlen(buf)) < 0) { + upslogx(LOG_ALERT, "Can't set primary managerial mode on UPS [%s] - %s", + ups->sys, upscli_strerror(&ups->conn)); + return 0; + } + + if (upscli_readline(&ups->conn, buf, sizeof(buf)) == 0) { + if (!strncmp(buf, "OK", 2)) + return 1; + upscli_readraw_error = 0; + } + else { + upscli_readraw_error = 1; + } + } + else { + upscli_readraw_error = 1; + } + + if (upscli_readraw_error == 0) { + /* not ERR, but not caught by readline either? */ upslogx(LOG_ALERT, "Primary managerial privileges unavailable on UPS [%s]", ups->sys); upslogx(LOG_ALERT, "Response: [%s]", buf); @@ -261,9 +296,6 @@ static int apply_for_primary(utype_t *ups) } /* authenticate to upsd, plus do LOGIN and MASTER if applicable */ -/* TODO: API change pending to replace deprecated MASTER with PRIMARY - * and SLAVE with SECONDARY (and backwards-compatible alias handling) - */ static int do_upsd_auth(utype_t *ups) { char buf[SMALLBUF]; From 2be0e2b1ed76eb31b8b6dfd29cff9cdf19cd2bce Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 17:42:54 +0100 Subject: [PATCH 306/700] server/netuser.c: net_login(): instrument with upsdebugx() --- server/netuser.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/netuser.c b/server/netuser.c index 2c3a9bcb49..e2c55f0759 100644 --- a/server/netuser.c +++ b/server/netuser.c @@ -53,6 +53,8 @@ void net_login(nut_ctype_t *client, size_t numarg, const char **arg) /* make sure this is a valid user */ if (!user_checkaction(client->username, client->password, "LOGIN")) { + upsdebugx(3, "%s: not a valid user: %s", + __func__, client->username); send_err(client, NUT_ERR_ACCESS_DENIED); return; } From e8400e133502de0d40f93feed250f7f37ece29cc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 17:43:21 +0100 Subject: [PATCH 307/700] server/upsd.c: check_command(): instrument with upsdebugx() --- server/upsd.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/upsd.c b/server/upsd.c index ba8e23759f..629915003a 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -453,17 +453,22 @@ int ups_available(const upstype_t *ups, nut_ctype_t *client) static void check_command(int cmdnum, nut_ctype_t *client, size_t numarg, const char **arg) { + upsdebugx(6, "Entering %s: %s", __func__, numarg > 0 ? arg[0] : "<>"); + if (netcmds[cmdnum].flags & FLAG_USER) { + /* command requires previous authentication */ #ifdef HAVE_WRAP struct request_info req; #endif /* HAVE_WRAP */ if (!client->username) { + upsdebugx(1, "%s: client not logged in yet", __func__); send_err(client, NUT_ERR_USERNAME_REQUIRED); return; } if (!client->password) { + upsdebugx(1, "%s: client not logged in yet", __func__); send_err(client, NUT_ERR_PASSWORD_REQUIRED); return; } @@ -473,13 +478,18 @@ static void check_command(int cmdnum, nut_ctype_t *client, size_t numarg, fromhost(&req); if (!hosts_access(&req)) { - /* tcp-wrappers says access should be denied */ + upsdebugx(1, + "%s: while authenticating %s found that " + "tcp-wrappers says access should be denied", + __func__, client->username); send_err(client, NUT_ERR_ACCESS_DENIED); return; } #endif /* HAVE_WRAP */ } + upsdebugx(6, "%s: Calling command handler for %s", __func__, numarg > 0 ? arg[0] : "<>"); + /* looks good - call the command */ netcmds[cmdnum].func(client, (numarg < 2) ? 0 : (numarg - 1), (numarg > 1) ? &arg[1] : NULL); } From b9e3f6c05249117c624a00a6dafe07de98c82127 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 18:04:15 +0100 Subject: [PATCH 308/700] netcmds.h, netuser.{c,h}, PyNUT.py.in: deprecate protocol "MASTER" command in favor of "PRIMARY" [issue #840] --- scripts/python/module/PyNUT.py.in | 15 ++++++++---- server/netcmds.h | 5 ++-- server/netuser.c | 40 +++++++++++++++++++++++-------- server/netuser.h | 8 +++++-- 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/scripts/python/module/PyNUT.py.in b/scripts/python/module/PyNUT.py.in index 3379239278..dea4053bd3 100644 --- a/scripts/python/module/PyNUT.py.in +++ b/scripts/python/module/PyNUT.py.in @@ -272,17 +272,22 @@ Returns OK on success or raises an error Returns OK on success or raises an error -TODO: API change pending to replace MASTER with PRIMARY +NOTE: API changed since NUT 2.8.0 to replace MASTER with PRIMARY (and backwards-compatible alias handling) """ if self.__debug : - print( "[DEBUG] MASTER called..." ) + print( "[DEBUG] "PRIMARY called..." ) - self.__srv_handler.write( ("MASTER %s\n" % ups).encode('ascii') ) + self.__srv_handler.write( ("PRIMARY %s\n" % ups).encode('ascii') ) result = self.__srv_handler.read_until( b"\n" ) - if ( result != b"OK MASTER-GRANTED\n" ) : - raise PyNUTError( ( "Master level function are not available", "" ) ) + if ( result != b"OK PRIMARY-GRANTED\n" ) : + if self.__debug : + print( "[DEBUG] Retrying: "MASTER called..." ) + self.__srv_handler.write( ("MASTER %s\n" % ups).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n" ) + if ( result != b"OK MASTER-GRANTED\n" ) : + raise PyNUTError( ( "Primary level functions are not available", "" ) ) if self.__debug : print( "[DEBUG] FSD called..." ) diff --git a/server/netcmds.h b/server/netcmds.h index c8bb31aa13..891237f948 100644 --- a/server/netcmds.h +++ b/server/netcmds.h @@ -61,9 +61,10 @@ static struct { { "LOGIN", net_login, FLAG_USER }, { "LOGOUT", net_logout, 0 }, - /* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. + /* NOTE: Protocol in NUT 2.8.0 allows to handle + * master/primary to rename/alias the routine. */ + { "PRIMARY", net_primary, FLAG_USER }, { "MASTER", net_master, FLAG_USER }, { "FSD", net_fsd, FLAG_USER }, diff --git a/server/netuser.c b/server/netuser.c index e2c55f0759..3ce563433e 100644 --- a/server/netuser.c +++ b/server/netuser.c @@ -85,34 +85,54 @@ void net_logout(nut_ctype_t *client, size_t numarg, const char **arg) client->last_heard = 0; } -/* MASTER */ -/* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. +/* NOTE: Protocol updated since NUT 2.8.0 to handle master/primary + * and API bumped, to rename/alias the routine. */ -void net_master(nut_ctype_t *client, size_t numarg, const char **arg) +static int do_net_primary(nut_ctype_t *client, size_t numarg, const char **arg) { upstype_t *ups; if (numarg != 1) { send_err(client, NUT_ERR_INVALID_ARGUMENT); - return; + return -1; } ups = get_ups_ptr(arg[0]); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); - return; + return -1; } - /* make sure this user is allowed to do MASTER */ - if (!user_checkaction(client->username, client->password, "MASTER")) { + /* make sure this user is allowed to do PRIMARY or MASTER */ + if (!user_checkaction(client->username, client->password, "PRIMARY") + && !user_checkaction(client->username, client->password, "MASTER") + ) { send_err(client, NUT_ERR_ACCESS_DENIED); - return; + return -1; } /* this is just an access level check */ - sendback(client, "OK MASTER-GRANTED\n"); + /* sendback() will be worded by caller below */ + return 0; + sendback(client, "OK PRIMARY-GRANTED\n"); +} + +/* MASTER (deprecated) */ +void net_master(nut_ctype_t *client, size_t numarg, const char **arg) { + /* Allow existing binaries linked against this file to still work */ + upsdebugx(1, "WARNING: net_master() is deprecated in favor of net_primary() since NUT 2.8.0"); + if (0 == do_net_primary(client, numarg, arg)) { + sendback(client, "OK MASTER-GRANTED\n"); + } +} + +/* PRIMARY (since NUT 2.8.0) */ +void net_primary(nut_ctype_t *client, size_t numarg, const char **arg) +{ + if (0 == do_net_primary(client, numarg, arg)) { + sendback(client, "OK PRIMARY-GRANTED\n"); + } } /* USERNAME */ diff --git a/server/netuser.h b/server/netuser.h index 069c1f6200..7532b3cce9 100644 --- a/server/netuser.h +++ b/server/netuser.h @@ -33,10 +33,14 @@ extern "C" { void net_login(nut_ctype_t *client, size_t numarg, const char **arg); void net_logout(nut_ctype_t *client, size_t numarg, const char **arg); -/* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. + +/* NOTE: Since NUT 2.8.0 we handle master as alias for primary + * Header keyword kept for building older consumers, but + * the implementation will warn that it is deprecated. */ void net_master(nut_ctype_t *client, size_t numarg, const char **arg); +void net_primary(nut_ctype_t *client, size_t numarg, const char **arg); + void net_username(nut_ctype_t *client, size_t numarg, const char **arg); void net_password(nut_ctype_t *client, size_t numarg, const char **arg); From 968331a9c125f3b146a29db5f330c723fdaedf38 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 18:39:55 +0100 Subject: [PATCH 309/700] NEWS: update for "PRIMARY" netcmd support (alias of "MASTER") [issue #840] --- NEWS | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index a9c9ce22ce..6e57a3d7bf 100644 --- a/NEWS +++ b/NEWS @@ -290,11 +290,25 @@ Release notes for NUT 2.8.0 - what's new since 2.7.4: ISO 8601 Calendar date "YYYY-MM-DD" was added to snmp-ups.c [PR #1078] - Master/Slave terminology was deprecated in favor of Primary/Secondary - modes of upsmon client. Respective keywords in the configuration are - supported as backwards-compatible settings, but obsoleted values are - no longer documented. [For details see issue #840 and several pull - requests referenced from it, and discussions on NUT mailing lists] - * Note: protocol keyword support currently was not updated + modes of `upsmon` client: + * Respective keywords in the configuration files (`upsd.users` and + `upsmon.conf`) are supported as backwards-compatible settings, + but the obsoleted values are no longer documented. + * Protocol keyword support was similarly updated, with `upsmon` now + first trying to elevate privileges with `PRIMARY ` request, + and falling back to `MASTER ` just in case it talks to an + older build of an `upsd` server. + * For the principle of least surprise, NUT codebase still exposes the + `net_master()` (as handler for `MASTER` net command) in header and + C code for the sake of existing linked binaries, and returns the + `OK MASTER-GRANTED` line to the older client that invoked it. + * Newly introduced `net_primary()` (as handler for `PRIMARY` net command) + calls the exact same application logic, but returns `OK PRIMARY-GRANTED` + line to the client. + * Python binding updated to handle both cases, as the only found in-tree + protocol consumer of the full-line text. + * For more details see issue #840 and several pull requests referenced + from it, and discussions on NUT mailing lists. - Build fixes: * In general, numerous fixes were applied to ensure portability and avoid From b57e7a6e606a19e2c97e95584a96a71253d167ae Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 18:46:11 +0100 Subject: [PATCH 310/700] docs/net-protocol.txt: update for "PRIMARY" netcmd support (alias of "MASTER") [issue #840] --- docs/net-protocol.txt | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index 6186b6e1bb..a29bcdc8f7 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -489,8 +489,11 @@ NOTE: You probably shouldn't send this command unless you are upsmon, or a upsmon replacement. -MASTER ------- +PRIMARY (since NUT 2.8.0) or MASTER (deprecated) +------------------------------------------------ + +NOTE: This command was renamed in NUT 2.8.0 to "PRIMARY" with the older +name "MASTER" kept as deprecated alias for compatibility. Form: @@ -498,19 +501,26 @@ Form: Response: - OK (upon success) + OK MASTER-GRANTED (upon success) + +Form: + + PRIMARY + +Response: + + OK PRIMARY-GRANTED (upon success) or <> NOTE: This requires "upsmon primary" in upsd.users +NOTE: Previously documented response was just `OK` -- clients checking +that server reply *starts with* that keyword would handle all cases. + This function doesn't do much by itself. It is used by upsmon to make sure that primary-mode functions like FSD are available if necessary. -NOTE: This command will be eventually renamed to "PRIMARY" with the older -name "MASTER" kept as deprecated alias for compatibility; a protocol bump -or special backwards-compatibility handling may be needed for that however. - FSD --- From 9b4a01bf235b8c22404cc69f00c38f7481e9b7dc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 19:03:05 +0100 Subject: [PATCH 311/700] server/netuser.c: net_master(): provide more details about client using deprecated commands --- server/netuser.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/server/netuser.c b/server/netuser.c index 3ce563433e..f96216bed0 100644 --- a/server/netuser.c +++ b/server/netuser.c @@ -121,7 +121,13 @@ static int do_net_primary(nut_ctype_t *client, size_t numarg, const char **arg) /* MASTER (deprecated) */ void net_master(nut_ctype_t *client, size_t numarg, const char **arg) { /* Allow existing binaries linked against this file to still work */ - upsdebugx(1, "WARNING: net_master() is deprecated in favor of net_primary() since NUT 2.8.0"); + upsdebugx(1, + "WARNING: Client %s@%s " + "requested MASTER level for device %s - " + "which is deprecated in favor of PRIMARY " + "since NUT 2.8.0", + client->username, client->addr, + (numarg > 0) ? arg[0] : ""); if (0 == do_net_primary(client, numarg, arg)) { sendback(client, "OK MASTER-GRANTED\n"); } From b1b50e0c6a88bc0e6478f99095f28fe1a8bc0ef5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 19:14:03 +0100 Subject: [PATCH 312/700] NEWS: added socomec_jbus driver for NUT v2.7.5 --- NEWS | 4 ++++ docs/nut.dict | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index f2c13b0ee6..aa373bfbf3 100644 --- a/NEWS +++ b/NEWS @@ -100,6 +100,10 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: RS-232 Modbus device support of Huawei UPS2000/2000A (1kVA-3kVA) series, and possibly some related FSP UPS models. [PR #954] + - socomec_jbus: added new driver for modbus-based JBUS protocol over serial + RS-232 for Socomec UPS (tested with a DIGYS 3/3 15kVA model, working + on Linux x86-64 and Raspberry Pi 3 ARM). [PR #1313] + - generic_modbus: added new driver for TCP and Serial Modbus device support. The driver has been tested against PULS UPS (model UB40.241) via MOXA ioLogikR1212 (RS485) and ioLogikE1212 (TCP/IP), and configuration diff --git a/docs/nut.dict b/docs/nut.dict index 8a1212fd0c..c1c111be84 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2864 utf-8 +personal_ws-1.1 en 2868 utf-8 AAS ACFAIL ACFREQ @@ -242,6 +242,7 @@ DES DESTDIR DF DHEA +DIGYS DISCHRG DMF DN @@ -486,6 +487,7 @@ Invter IoT Ioannou JAWAN +JBUS JBus JKL JRE @@ -1941,6 +1943,7 @@ ivtscd jNUT jNut jNutWebAPI +jbus jdk jenkins jessie @@ -2524,6 +2527,7 @@ snprintf snprintfcat snr sockdebug +socomec solaris solis somepass From a7e62be413e0f70227d158f9435c416e83e96dfa Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 19:16:11 +0100 Subject: [PATCH 313/700] NEWS: added battery.mfr.date APC HID UPS setting for NUT v2.7.5 --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index aa373bfbf3..87bea42cdf 100644 --- a/NEWS +++ b/NEWS @@ -177,6 +177,7 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: [PR #1199, issue #732] * add new ever-hid subdriver to support EVER UPS devices (Sinline RT Series, Sinline RT XL Series, ECO PRO AVR CDS Series) [PR #431] + * add ability to set `battery.mfr.date` for APC HID UPS [PR #1318] - usbhid-ups / mge-shut: compute a realpower output load approximation for Eaton UPS when the needed data is not present From 4f5456981429a824e7351d9fde2aaba9da7f1ecb Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 15:30:26 +0100 Subject: [PATCH 314/700] NEWS: rename pending NUT release from 2.7.5 to 2.8.0 (config files using new keywords are not backwards-compatible, not usable by old binaries) --- NEWS | 7 ++++++- UPGRADING | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 87bea42cdf..7438bd25cc 100644 --- a/NEWS +++ b/NEWS @@ -5,7 +5,12 @@ ChangeLog file (generated for release archives), or to the Git version control history for "live" codebase. --------------------------------------------------------------------------- -Release notes for NUT 2.7.5 - what's new since 2.7.4: +Release notes for NUT 2.8.0 - what's new since 2.7.4: + + - New (optional) keywords for configuration files were added, + so existing NUT 2.7.x builds would not accept them if some + deployments switch versions back and forth -- due to this, + semantically the version was bumped to NUT 2.8.x. - Add support for openssl-1.1.0 (Arjen de Korte) diff --git a/UPGRADING b/UPGRADING index f184a86864..854a1130fd 100644 --- a/UPGRADING +++ b/UPGRADING @@ -7,7 +7,7 @@ This file lists changes that affect users who installed older versions of this software. When upgrading from an older version, be sure to check this file to see if you need to make changes to your system. -Changes from 2.7.4 to 2.7.5 +Changes from 2.7.4 to 2.8.0 --------------------------- - Note to distribution packagers: this version hopefully learns from many From 0c38d43b1b93c2d9dad562f08e994d99ae57fd6e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 20:02:20 +0100 Subject: [PATCH 315/700] server/netuser.c: copy-paste typo fix --- server/netuser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/netuser.c b/server/netuser.c index f96216bed0..6f4c677688 100644 --- a/server/netuser.c +++ b/server/netuser.c @@ -115,7 +115,6 @@ static int do_net_primary(nut_ctype_t *client, size_t numarg, const char **arg) /* this is just an access level check */ /* sendback() will be worded by caller below */ return 0; - sendback(client, "OK PRIMARY-GRANTED\n"); } /* MASTER (deprecated) */ @@ -128,6 +127,7 @@ void net_master(nut_ctype_t *client, size_t numarg, const char **arg) { "since NUT 2.8.0", client->username, client->addr, (numarg > 0) ? arg[0] : ""); + if (0 == do_net_primary(client, numarg, arg)) { sendback(client, "OK MASTER-GRANTED\n"); } From 3811a17fdaaadc2d43286685cbba6fecbb47e0d2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 22:36:00 +0100 Subject: [PATCH 316/700] docs/config-prereqs.txt: rearrange packages to not require heavy ones by default dependency footprint (aspell, docs generation and libgd) --- docs/config-prereqs.txt | 73 ++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/docs/config-prereqs.txt b/docs/config-prereqs.txt index 7b9a0865d6..9aa32fe027 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -100,8 +100,18 @@ source versions on both Debian 8 Jessie and Debian 11 Buster). valgrind \ cppcheck \ pkg-config \ - gcc g++ clang \ - asciidoc source-highlight python3-pygments dblatex aspell \ + gcc g++ clang + +# For spell-checking: +:; apt-get install \ + aspell aspell-en + +# For other doc types (man-page, PDF, HTML) generation - massive packages (TEX, X11): +:; apt-get install \ + asciidoc source-highlight python3-pygments dblatex + +# For CGI graph generation - massive packages (X11): +:; apt-get install \ libgd-dev # NOTE: Some older Debian-like distributions, could ship "libcrypto-dev" @@ -216,8 +226,18 @@ https://unix.stackexchange.com/questions/475584/cannot-install-busybox-on-centos valgrind \ cppcheck \ pkgconfig \ - gcc gcc-c++ clang \ - asciidoc source-highlight python-pygments dblatex aspell \ + gcc gcc-c++ clang + +# For spell-checking: +:; yum install \ + aspell aspell-en + +# For other doc types (man-page, PDF, HTML) generation - massive packages (TEX, X11): +:; yum install \ + asciidoc source-highlight python-pygments dblatex + +# For CGI graph generation - massive packages (X11): +:; yum install \ gd-devel # NOTE: "libusbx" is the CentOS way of naming "libusb-1.0" @@ -277,8 +297,18 @@ below. valgrind \ cppcheck \ pkgconf \ - gcc clang \ - asciidoc source-highlight textproc/py-pygments dblatex en-aspell aspell \ + gcc clang + +# For spell-checking: +:; pkg install \ + aspell en-aspell + +# For other doc types (man-page, PDF, HTML) generation - massive packages (TEX, X11): +:; pkg install \ + asciidoc source-highlight textproc/py-pygments dblatex + +# For CGI graph generation - massive packages (X11): +:; pkg install \ libgd :; pkg install \ @@ -367,8 +397,18 @@ networked repository (4.2.x vs 4.9.x) valgrind \ cppcheck \ pkgconf \ - gcc clang \ - asciidoc source-highlight py-pygments dblatex aspell \ + gcc clang + +# For spell-checking: +:; pkg_add \ + aspell + +# For other doc types (man-page, PDF, HTML) generation - massive packages (TEX, X11): +:; pkg_add \ + asciidoc source-highlight py-pygments dblatex + +# For CGI graph generation - massive packages (X11): +:; pkg_add \ gd # Need to find proper package name and/or mirror for this: @@ -447,10 +487,21 @@ Typical tooling would include: gnu-make autoconf automake libltdl libtool \ valgrind \ pkg-config \ - gnu-binutils developer/linker \ - asciidoc libxslt aspell text/aspell/en \ + gnu-binutils developer/linker + +# For spell-checking: +:; pkg install \ + aspell text/aspell/en + +# For other doc types (man-page, PDF, HTML) generation - massive packages (TEX, X11): +:; pkg install \ + asciidoc libxslt \ docbook/dtds docbook/dsssl docbook/xsl docbook docbook/sgml-common pygments-39 \ - graphviz expect gd graphviz-tcl + graphviz expect graphviz-tcl + +# For CGI graph generation - massive packages (X11): +:; pkg install \ + gd :; pkg install \ openssl library/mozilla-nss \ From 1d34b84a2184a08715c4da61ace63230551ff726 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 22:37:11 +0000 Subject: [PATCH 317/700] drivers/belkinunv.c: instcmd(): find use for "r" to at least report failed cmds --- drivers/belkinunv.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/belkinunv.c b/drivers/belkinunv.c index 0a46be4951..c508563c13 100644 --- a/drivers/belkinunv.c +++ b/drivers/belkinunv.c @@ -1196,35 +1196,43 @@ int instcmd(const char *cmdname, const char *extra) if (!strcasecmp(cmdname, "test.failure.start")) { r = belkin_nut_write_int(REG_TESTSTATUS, 2); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "test.failure.stop")) { r = belkin_nut_write_int(REG_TESTSTATUS, 3); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "test.battery.start")) { r = belkin_nut_write_int(REG_TESTSTATUS, 1); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "test.battery.stop")) { r = belkin_nut_write_int(REG_TESTSTATUS, 3); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "beeper.disable")) { r = belkin_nut_write_int(REG_ALARMSTATUS, 1); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "beeper.enable")) { r = belkin_nut_write_int(REG_ALARMSTATUS, 2); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "beeper.mute")) { r = belkin_nut_write_int(REG_ALARMSTATUS, 3); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "shutdown.stayoff")) { r = belkin_nut_write_int(REG_RESTARTTIMER, 0); r |= belkin_nut_write_int(REG_SHUTDOWNTIMER, 1); /* 1 second */ + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "shutdown.reboot")) { @@ -1236,11 +1244,13 @@ int instcmd(const char *cmdname, const char *extra) the UPS will stay off between 60 and 120 seconds */ r = belkin_nut_write_int(REG_RESTARTTIMER, 2); /* 2 minutes */ r |= belkin_nut_write_int(REG_SHUTDOWNTIMER, 1); /* 1 second */ + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "shutdown.reboot.graceful")) { r = belkin_nut_write_int(REG_RESTARTTIMER, 2); /* 2 minutes */ r |= belkin_nut_write_int(REG_SHUTDOWNTIMER, 40); /* 40 seconds */ + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "reset.input.minmax")) { From c84a7fae39bc88f25ad39d885cde320e85b0332e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 22:38:07 +0000 Subject: [PATCH 318/700] drivers/riello.c: riello_parse_re(): assign Pout#W and Pout#VA from pom_long not pom_word --- drivers/riello.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/riello.c b/drivers/riello.c index 69bfe8db1d..8671225304 100644 --- a/drivers/riello.c +++ b/drivers/riello.c @@ -645,42 +645,42 @@ void riello_parse_re(uint8_t* buffer, TRielloData* data) pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); - data->Pout1W = pom_word; + data->Pout1W = pom_long; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); - data->Pout2W = pom_word; + data->Pout2W = pom_long; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); - data->Pout3W = pom_word; + data->Pout3W = pom_long; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); - data->Pout1VA = pom_word; + data->Pout1VA = pom_long; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); - data->Pout2VA = pom_word; + data->Pout2VA = pom_long; pom_long = (buffer[j++]-0x30)*65536; pom_long += (buffer[j++]-0x30)*4096; pom_long += (buffer[j++]-0x30)*256; pom_long += (buffer[j++]-0x30)*16; pom_long += (buffer[j++]-0x30); - data->Pout3VA = pom_word; + data->Pout3VA = pom_long; } void riello_parse_rc(uint8_t* buffer, TRielloData* data) From 523e2a145046230bc48dee42e549edd25e571bc0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 11 Mar 2022 22:38:46 +0000 Subject: [PATCH 319/700] m4/nut_compiler_family.m4: quiesce -Wreserved-identifier (clang 13+) that acts up on system headers --- m4/nut_compiler_family.m4 | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/m4/nut_compiler_family.m4 b/m4/nut_compiler_family.m4 index 4204b3c079..55849c5d93 100644 --- a/m4/nut_compiler_family.m4 +++ b/m4/nut_compiler_family.m4 @@ -102,13 +102,11 @@ dnl -fdiagnostics-color=ARG: help find where bugs are in the wall of text (gcc) dnl First check for this to avoid failing on unused include paths etc: NUT_CHECK_COMPILE_FLAG([-Qunused-arguments]) -dnl # Future: test for something else? -dnl m4_foreach_w([TESTCOMPILERFLAG], [ -dnl -Qunused-arguments -dnl -Wno-unknown-warning-option -dnl ], [ -dnl NUT_CHECK_COMPILE_FLAG([TESTCOMPILERFLAG]) -dnl ]) + m4_foreach_w([TESTCOMPILERFLAG], [ + -Wno-reserved-identifier + ], [ + NUT_CHECK_COMPILE_FLAG([TESTCOMPILERFLAG]) + ]) dnl Note: each m4_foreach_w arg must be named uniquely dnl Note: Seems -fcolor-diagnostics is clang-only and sometimes From 74e4013b4234a625896325d591d87270e2931945 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 12 Mar 2022 12:30:03 +0000 Subject: [PATCH 320/700] ci_build.sh: expand a bit CI_OS_HINT detection on linux systems --- ci_build.sh | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/ci_build.sh b/ci_build.sh index 80d77c89b5..66e7f1a366 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -184,15 +184,26 @@ done if [ -z "$CI_OS_NAME" ]; then # Check for dynaMatrix node labels support and map into a simple # classification styled after (compatible with) that in Travis CI - for CI_OS_HINT in "$OS_FAMILY-$OS_DISTRO" "`uname -o`" "`uname -s -r -v`" "`uname -a`" ; do + for CI_OS_HINT in \ + "$OS_FAMILY-$OS_DISTRO" \ + "`grep = /etc/os-release 2>/dev/null`" \ + "`cat /etc/release 2>/dev/null`" \ + "`uname -o`" \ + "`uname -s -r -v`" \ + "`uname -a`" \ + ; do [ -z "$CI_OS_HINT" -o "$CI_OS_HINT" = "-" ] || break done case "`echo "$CI_OS_HINT" | tr 'A-Z' 'a-z'`" in *freebsd*) CI_OS_NAME="freebsd" ;; - *debian*|*linux*) + *debian*|*ubuntu*) CI_OS_NAME="debian" ;; + *centos*|*fedora*|*redhat*|*rhel*) + CI_OS_NAME="centos" ;; + *linux*) + CI_OS_NAME="linux" ;; *windows*) CI_OS_NAME="windows" ;; *[Mm]ac*|*arwin*|*[Oo][Ss][Xx]*) From 3352e9dcccdc433bcc24f2651bd6d3a7e6a44ba2 Mon Sep 17 00:00:00 2001 From: juantonio Date: Sat, 12 Mar 2022 13:30:35 +0100 Subject: [PATCH 321/700] Add tested descriptors for Salicru Twin Pro 2 --- drivers/salicru-hid.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/salicru-hid.c b/drivers/salicru-hid.c index c93f52a845..b4539e373a 100644 --- a/drivers/salicru-hid.c +++ b/drivers/salicru-hid.c @@ -204,6 +204,26 @@ static hid_info_t salicru_hid2nut[] = { { "beeper.mute", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, */ + /* Salicru Twin Pro 2 Descriptors Sensors*/ + { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", NULL, "%.0f", 0, NULL }, + { "ups.test.result", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "%s", 0, test_read_info }, + { "ups.realpower.nominal", 0, 0, "UPS.Flow.[4].ConfigActivePower", NULL, "%.0f", 0, NULL }, + { "ups.realpower", 0, 0, "UPS.PowerConverter.Output.ActivePower", NULL, "%.0f", 0, NULL }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, 0, overload_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.OverTemperature", NULL, NULL, 0, overheat_info }, + { "input.frequency", 0, 0, "UPS.PowerConverter.Input.[1].Frequency", NULL, "%.1f", 0, NULL }, + { "input.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Voltage", NULL, "%.1f", 0, NULL }, + { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", NULL, "%.1f", 0, NULL }, + { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.1f", 0, NULL }, + { "output.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.0f", 0, NULL }, + { "ups.test.result", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "%s", 0, test_read_info }, + + /* Salicru Twin Pro 2 Descriptors Instants commands*/ + { "test.battery.start.quick", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "1", HU_TYPE_CMD, NULL }, + { "test.battery.start.deep", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "2", HU_TYPE_CMD, NULL }, + { "test.battery.stop", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "3", HU_TYPE_CMD, NULL }, + + /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; From 6f8e6205e4a161bbb0e2dfc1b30788b165bd6706 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 12 Mar 2022 12:31:41 +0000 Subject: [PATCH 322/700] ci_build.sh: report OS_* envvar values if passed by caller --- ci_build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci_build.sh b/ci_build.sh index 66e7f1a366..7f3a7ec0af 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -469,7 +469,7 @@ fi echo "Processing BUILD_TYPE='${BUILD_TYPE}' ..." echo "Build host settings:" -set | egrep '^(CI_.*|CANBUILD_.*|NODE_LABELS|MAKE|C.*FLAGS|LDFLAGS|CC|CXX|DO_.*|BUILD_.*)=' || true +set | egrep '^(CI_.*|OS_*|CANBUILD_.*|NODE_LABELS|MAKE|C.*FLAGS|LDFLAGS|CC|CXX|DO_.*|BUILD_.*)=' || true uname -a echo "LONG_BIT:`getconf LONG_BIT` WORD_BIT:`getconf WORD_BIT`" || true if command -v xxd >/dev/null ; then xxd -c 1 -l 6 | tail -1; else if command -v od >/dev/null; then od -N 1 -j 5 -b | head -1 ; else hexdump -s 5 -n 1 -C | head -1; fi; fi < /bin/ls 2>/dev/null | awk '($2 == 1){print "Endianness: LE"}; ($2 == 2){print "Endianness: BE"}' || true From 0d9a0f96769678bb32eaad6a87447ad77734a9de Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 12 Mar 2022 13:57:34 +0000 Subject: [PATCH 323/700] Introduce m4/ax_run_or_link_ifelse.m4 to facilitate cross-builds [#1289] --- configure.ac | 8 ++++---- m4/ax_c_pragmas.m4 | 3 ++- m4/ax_run_or_link_ifelse.m4 | 21 +++++++++++++++++++++ m4/nut_compiler_family.m4 | 6 ++++-- 4 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 m4/ax_run_or_link_ifelse.m4 diff --git a/configure.ac b/configure.ac index 63f0b96261..50542fcaf9 100644 --- a/configure.ac +++ b/configure.ac @@ -261,7 +261,7 @@ CODE_STRINGINCL='#ifdef HAVE_STRING_H AC_CACHE_CHECK([for strcasecmp(s1,s2)], [ac_cv_func_strcasecmp], - [AC_RUN_IFELSE( + [AX_RUN_OR_LINK_IFELSE( [AC_LANG_PROGRAM([$CODE_STRINGINCL], [if (strcasecmp("STR1", "str1") != 0) return 1])], [ac_cv_func_strcasecmp=yes], [ac_cv_func_strcasecmp=no] @@ -273,7 +273,7 @@ AS_IF([test x"${ac_cv_func_strcasecmp}" = xyes], AC_CACHE_CHECK([for strncasecmp(s1,s2,n)], [ac_cv_func_strncasecmp], - [AC_RUN_IFELSE( + [AX_RUN_OR_LINK_IFELSE( [AC_LANG_PROGRAM([$CODE_STRINGINCL], [if (strncasecmp("STR1", "strX", 2) != 0) return 1])], [ac_cv_func_strncasecmp=yes], [ac_cv_func_strncasecmp=no] @@ -285,7 +285,7 @@ AS_IF([test x"${ac_cv_func_strncasecmp}" = xyes], AC_CACHE_CHECK([for strdup(s)], [ac_cv_func_strdup], - [AC_RUN_IFELSE( + [AX_RUN_OR_LINK_IFELSE( [AC_LANG_PROGRAM([$CODE_STRINGINCL], [[int res = 0; char *t = "Test"; @@ -2070,7 +2070,7 @@ return res ? 0 : 1; my_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$myCXXFLAGS $pkg_cv_CPPUNIT_CFLAGS $pkg_cv_CPPUNIT_LIBS" AC_LANG_PUSH([C++]) - AC_RUN_IFELSE([AC_LANG_PROGRAM([[${CPLUSPLUS_DECL}]], [[${CPLUSPLUS_MAIN}]])], + AX_RUN_OR_LINK_IFELSE([AC_LANG_PROGRAM([[${CPLUSPLUS_DECL}]], [[${CPLUSPLUS_MAIN}]])], [have_cppunit=yes], [have_cppunit=no]) CXXFLAGS="$my_CXXFLAGS" AC_LANG_POP([C++]) diff --git a/m4/ax_c_pragmas.m4 b/m4/ax_c_pragmas.m4 index 28b39bbb07..934573470b 100644 --- a/m4/ax_c_pragmas.m4 +++ b/m4/ax_c_pragmas.m4 @@ -911,13 +911,14 @@ fi AC_DEFUN([AX_C_PRINTF_STRING_NULL], [ if test -z "${nut_have_ax_c_printf_string_null_seen}"; then nut_have_ax_c_printf_string_null_seen="yes" + AC_REQUIRE([AX_RUN_OR_LINK_IFELSE])dnl dnl ### To be sure, bolt the language AC_LANG_PUSH([C]) AC_CACHE_CHECK([for practical support to pritnf("%s", NULL)], [ax_cv__printf_string_null], - [AC_RUN_IFELSE( + [AX_RUN_OR_LINK_IFELSE( [AC_LANG_PROGRAM([dnl #include #include diff --git a/m4/ax_run_or_link_ifelse.m4 b/m4/ax_run_or_link_ifelse.m4 new file mode 100644 index 0000000000..0e36c7efa2 --- /dev/null +++ b/m4/ax_run_or_link_ifelse.m4 @@ -0,0 +1,21 @@ +dnl By default, AC_RUN_IFELSE() fails if it detects cross-compilation +dnl but it provides the fourth argument to customize the handling. +dnl In our case, we would fall back to trying just to link then - +dnl the result would not be as relevant regarding run-time behavior +dnl of the code, but at least we would know that API and ABI are ok. + +dnl Per original /usr/share/autoconf/autoconf/general.m4 which makes +dnl a similar wrapper: +dnl # AC_TRY_RUN(PROGRAM, +dnl # [ACTION-IF-TRUE], [ACTION-IF-FALSE], +dnl # [ACTION-IF-CROSS-COMPILING = RUNTIME-ERROR]) +dnl # ------------------------------------------------------- +AC_DEFUN([AX_RUN_OR_LINK_IFELSE], +[ + AC_RUN_IFELSE([$1], [$2], [$3], + [ + AC_MSG_WARN([Current build is a cross-build, so not running test binaries, just linking them]) + AC_LINK_IFELSE([$1], [$2], [$3]) + ] + ) +]) diff --git a/m4/nut_compiler_family.m4 b/m4/nut_compiler_family.m4 index 55849c5d93..1d4f0891aa 100644 --- a/m4/nut_compiler_family.m4 +++ b/m4/nut_compiler_family.m4 @@ -61,6 +61,8 @@ AC_DEFUN([NUT_COMPILER_FAMILY], AC_DEFUN([NUT_CHECK_COMPILE_FLAG], [ + AC_REQUIRE([AX_RUN_OR_LINK_IFELSE])dnl + dnl Note: per https://stackoverflow.com/questions/52557417/how-to-check-support-compile-flag-in-autoconf-for-clang dnl the -Werror below is needed to detect "warnings" about unsupported options COMPILERFLAG="$1" @@ -73,7 +75,7 @@ dnl complain if they are forwarded unknown flags accepted by the front-end. AC_LANG_PUSH([C]) AX_CHECK_COMPILE_FLAG([${COMPILERFLAG}], [CFLAGS="$CFLAGS ${COMPILERFLAG}" - AC_RUN_IFELSE([AC_LANG_PROGRAM([],[])], + AX_RUN_OR_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [], [CFLAGS="$SAVED_CFLAGS"]) ], [], [-Werror]) AC_LANG_POP([C]) @@ -81,7 +83,7 @@ dnl complain if they are forwarded unknown flags accepted by the front-end. AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([${COMPILERFLAG}], [CXXFLAGS="$CXXFLAGS ${COMPILERFLAG}" - AC_RUN_IFELSE([AC_LANG_PROGRAM([],[])], + AX_RUN_OR_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [], [CXXFLAGS="$SAVED_CXXFLAGS"]) ], [], [-Werror]) AC_LANG_POP([C++]) From a55c47f378b940612271b4128bb3fa23b1a2d68f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 12 Mar 2022 14:15:54 +0000 Subject: [PATCH 324/700] drivers/snmp-ups.h: net-snmp after v5.9.1 does not declare ONE_SEC that our code uses; stash the definiton --- drivers/snmp-ups.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index c1549870ff..f6ac635ee9 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -80,6 +80,12 @@ #include #include +#ifndef ONE_SEC +/* This macro name disappeared from net-snmp sources and headers + * after v5.9 tag, and was replaced by explicit expression below: */ +# define ONE_SEC (1000L * 1000L) +#endif + /* Force numeric OIDs by disabling MIB loading */ #ifdef DISABLE_MIB_LOADING # undef DISABLE_MIB_LOADING From 84687bff9472bbdf6bec830fe99ea5752f8b333e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 12 Mar 2022 14:59:28 +0000 Subject: [PATCH 325/700] m4/nut_check_libnetsnmp.m4: improve checks for actually present priv/auth protocols [#1289] Great thanks to @eklinedi on GitHub for finding the issue and investigating ways to fix it --- m4/nut_check_libnetsnmp.m4 | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/m4/nut_check_libnetsnmp.m4 b/m4/nut_check_libnetsnmp.m4 index 836d09da9f..1f89b5af2c 100644 --- a/m4/nut_check_libnetsnmp.m4 +++ b/m4/nut_check_libnetsnmp.m4 @@ -164,6 +164,9 @@ oid * pProto = usmAES128PrivProtocol; #include #include oid * pProto = usmDESPrivProtocol; +#ifdef NETSNMP_DISABLE_DES +#error "NETSNMP_DISABLE_DES is defined" +#endif ], [] )], @@ -179,6 +182,9 @@ oid * pProto = usmDESPrivProtocol; #include #include oid * pProto = usmHMAC256SHA384AuthProtocol; +#ifndef HAVE_EVP_SHA384 +#error "HAVE_EVP_SHA384 is NOT defined" +#endif ], [] )], @@ -194,6 +200,9 @@ oid * pProto = usmHMAC256SHA384AuthProtocol; #include #include oid * pProto = usmHMAC384SHA512AuthProtocol; +#ifndef HAVE_EVP_SHA384 +#error "HAVE_EVP_SHA384 is NOT defined" +#endif ], [] )], @@ -209,6 +218,9 @@ oid * pProto = usmHMAC384SHA512AuthProtocol; #include #include oid * pProto = usmHMAC192SHA256AuthProtocol; +#ifndef HAVE_EVP_SHA224 +#error "HAVE_EVP_SHA224 is NOT defined" +#endif ], [] )], @@ -224,6 +236,9 @@ oid * pProto = usmHMAC192SHA256AuthProtocol; #include #include oid * pProto = usmAES192PrivProtocol; +#ifndef NETSNMP_DRAFT_BLUMENTHAL_AES_04 +#error "NETSNMP_DRAFT_BLUMENTHAL_AES_04 is NOT defined" +#endif ], [] )], @@ -239,6 +254,9 @@ oid * pProto = usmAES192PrivProtocol; #include #include oid * pProto = usmAES256PrivProtocol; +#ifndef NETSNMP_DRAFT_BLUMENTHAL_AES_04 +#error "NETSNMP_DRAFT_BLUMENTHAL_AES_04 is NOT defined" +#endif ], [] )], @@ -254,6 +272,9 @@ oid * pProto = usmAES256PrivProtocol; #include #include oid * pProto = usmHMACMD5AuthProtocol; +#ifdef NETSNMP_DISABLE_MD5 +#error "NETSNMP_DISABLE_MD5 is defined" +#endif ], [] )], From c04472dc624be90d9e8f4b33a452e2064d1be16f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 12 Mar 2022 16:57:59 +0100 Subject: [PATCH 326/700] Update salicru-hid.c Cosmetic fixes --- drivers/salicru-hid.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/salicru-hid.c b/drivers/salicru-hid.c index b4539e373a..b49b0990e5 100644 --- a/drivers/salicru-hid.c +++ b/drivers/salicru-hid.c @@ -86,7 +86,7 @@ static hid_info_t salicru_hid2nut[] = { { "experimental.ups.powersummary.fullchargecapacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL }, #endif /* DEBUG */ -/* A few more unknow fields +/* A few more unknown fields 0.043266 [D1] Path: UPS.ff010004.ff010024.ff0100d0, Type: Feature, ReportID: 0x19, Offset: 0, Size: 8, Value: 0.1 0.043766 [D1] Path: UPS.ff010004.ff010024.ff0100d1, Type: Feature, ReportID: 0x1a, Offset: 0, Size: 8, Value: 0 0.044308 [D1] Path: UPS.ff01001d.ff010019.ff010020, Type: Feature, ReportID: 0x25, Offset: 0, Size: 1, Value: 0 @@ -204,7 +204,7 @@ static hid_info_t salicru_hid2nut[] = { { "beeper.mute", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, */ - /* Salicru Twin Pro 2 Descriptors Sensors*/ + /* Salicru Twin Pro 2 Descriptors: Sensors */ { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", NULL, "%.0f", 0, NULL }, { "ups.test.result", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "%s", 0, test_read_info }, { "ups.realpower.nominal", 0, 0, "UPS.Flow.[4].ConfigActivePower", NULL, "%.0f", 0, NULL }, @@ -218,12 +218,11 @@ static hid_info_t salicru_hid2nut[] = { { "output.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.0f", 0, NULL }, { "ups.test.result", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "%s", 0, test_read_info }, - /* Salicru Twin Pro 2 Descriptors Instants commands*/ + /* Salicru Twin Pro 2 Descriptors: Instant commands */ { "test.battery.start.quick", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "1", HU_TYPE_CMD, NULL }, { "test.battery.start.deep", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "2", HU_TYPE_CMD, NULL }, { "test.battery.stop", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "3", HU_TYPE_CMD, NULL }, - /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; From 1c6c675d2c1df58608f938a2dbafb34cb99f5cce Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 12 Mar 2022 16:11:18 +0000 Subject: [PATCH 327/700] ci_build.sh: use PKG_CONFIG variable if provided (fall back to pkg-config from PATH, but only for this script itself) [#710] --- ci_build.sh | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/ci_build.sh b/ci_build.sh index 7f3a7ec0af..30d71b81bb 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -245,6 +245,13 @@ if [ -z "${CANBUILD_LIBGD_CGI-}" ]; then # See also below for some compiler-dependent decisions fi +if [ -z "${PKG_CONFIG-}" ]; then + # Default to using one from PATH, if any - mostly for config tuning done + # below in this script + # DO NOT "export" it here so ./configure script can find one for the build + PKG_CONFIG="pkg-config" +fi + configure_nut() { local CONFIGURE_SCRIPT=./configure if [[ "$CI_OS_NAME" == "windows" ]] ; then @@ -739,7 +746,7 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp if [ "${CANBUILD_LIBGD_CGI-}" != "no" ] && [ "${BUILD_LIBGD_CGI-}" != "auto" ] ; then # Currently --with-all implies this, but better be sure to # really build everything we can to be certain it builds: - if pkg-config --exists libgd || pkg-config --exists libgd2 || pkg-config --exists libgd3 || pkg-config --exists gdlib ; then + if $PKG_CONFIG --exists libgd || $PKG_CONFIG --exists libgd2 || $PKG_CONFIG --exists libgd3 || $PKG_CONFIG --exists gdlib ; then CONFIG_OPTS+=("--with-cgi=yes") else # Note: CI-wise, our goal IS to test as much as we can @@ -890,7 +897,8 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp fi if [ "$NO_PKG_CONFIG" == "true" ] && [ "$CI_OS_NAME" = "linux" ] && (command -v dpkg) ; then - echo "NO_PKG_CONFIG==true : BUTCHER pkg-config for this test case" >&2 + # This should be done in scratch containers... + echo "NO_PKG_CONFIG==true : BUTCHER pkg-config package for this test case" >&2 sudo dpkg -r --force all pkg-config fi @@ -981,15 +989,15 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp # Technically, let caller provide this setting explicitly if [ -z "$NUT_SSL_VARIANTS" ] ; then NUT_SSL_VARIANTS="auto" - if pkg-config --exists nss && pkg-config --exists openssl && [ "${BUILD_SSL_ONCE-}" != "true" ] ; then + if $PKG_CONFIG --exists nss && $PKG_CONFIG --exists openssl && [ "${BUILD_SSL_ONCE-}" != "true" ] ; then # Try builds for both cases as they are ifdef-ed # TODO: Extend if we begin to care about different # major versions of openssl (with their APIs), etc. NUT_SSL_VARIANTS="openssl nss" else if [ "${BUILD_SSL_ONCE-}" != "true" ]; then - pkg-config --exists nss 2>/dev/null && NUT_SSL_VARIANTS="nss" - pkg-config --exists openssl 2>/dev/null && NUT_SSL_VARIANTS="openssl" + $PKG_CONFIG --exists nss 2>/dev/null && NUT_SSL_VARIANTS="nss" + $PKG_CONFIG --exists openssl 2>/dev/null && NUT_SSL_VARIANTS="openssl" fi # else leave at "auto", if we skipped building # two variants while having two possibilities fi @@ -1002,12 +1010,12 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp if [ -z "$NUT_USB_VARIANTS" ] ; then # Check preferred version first, in case BUILD_USB_ONCE==true - if pkg-config --exists libusb-1.0 ; then + if $PKG_CONFIG --exists libusb-1.0 ; then NUT_USB_VARIANTS="1.0" fi # TODO: Is there anywhere a `pkg-config --exists libusb-0.1`? - if pkg-config --exists libusb || ( command -v libusb-config || which libusb-config ) 2>/dev/null >/dev/null ; then + if $PKG_CONFIG --exists libusb || ( command -v libusb-config || which libusb-config ) 2>/dev/null >/dev/null ; then if [ -z "$NUT_USB_VARIANTS" ] ; then NUT_USB_VARIANTS="0.1" else From fe08cce9431ff162e13c4a79866fe083d33ae037 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 12 Mar 2022 16:11:52 +0000 Subject: [PATCH 328/700] ci_build.sh: check for "gd" as one of names gdlib can go by --- ci_build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci_build.sh b/ci_build.sh index 30d71b81bb..2ed7553d72 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -746,7 +746,7 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp if [ "${CANBUILD_LIBGD_CGI-}" != "no" ] && [ "${BUILD_LIBGD_CGI-}" != "auto" ] ; then # Currently --with-all implies this, but better be sure to # really build everything we can to be certain it builds: - if $PKG_CONFIG --exists libgd || $PKG_CONFIG --exists libgd2 || $PKG_CONFIG --exists libgd3 || $PKG_CONFIG --exists gdlib ; then + if $PKG_CONFIG --exists libgd || $PKG_CONFIG --exists libgd2 || $PKG_CONFIG --exists libgd3 || $PKG_CONFIG --exists gdlib || $PKG_CONFIG --exists gd ; then CONFIG_OPTS+=("--with-cgi=yes") else # Note: CI-wise, our goal IS to test as much as we can From f655269f9b46ce477932c623584b8c7536d8730f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 12 Mar 2022 16:22:43 +0000 Subject: [PATCH 329/700] ci_build.sh: add support for CI_CROSSBUILD_HOST and/or CI_CROSSBUILD_TARGET settings [#1294, #1289, #1334] --- ci_build.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ci_build.sh b/ci_build.sh index 2ed7553d72..ebbc6cc081 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -674,6 +674,15 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp CONFIG_OPTS+=("--with-valgrind=no") fi + if [ -n "${CI_CROSSBUILD_TARGET-}" ] || [ -n "${CI_CROSSBUILD_HOST-}" ] ; then + # at least one is e.g. "arm-linux-gnueabihf" + [ -z "${CI_CROSSBUILD_TARGET-}" ] && CI_CROSSBUILD_TARGET="${CI_CROSSBUILD_HOST}" + [ -z "${CI_CROSSBUILD_HOST-}" ] && CI_CROSSBUILD_HOST="${CI_CROSSBUILD_TARGET}" + echo "NOTE: Cross-build was requested, passing options to configure this for target '${CI_CROSSBUILD_TARGET}' host '${CI_CROSSBUILD_HOST}' (note you may need customized PKG_CONFIG_PATH)" >&2 + CONFIG_OPTS+=("--host=${CI_CROSSBUILD_HOST}") + CONFIG_OPTS+=("--target=${CI_CROSSBUILD_TARGET}") + fi + # This flag is primarily linked with (lack of) docs generation enabled # (or not) in some BUILD_TYPE scenarios or workers. Initial value may # be set by caller, but codepaths below have the final word. From 93d62d7a589463c6a5c23c75f1bd07a9709d9c7b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Mar 2022 13:45:44 +0000 Subject: [PATCH 330/700] docs/config-prereqs.txt: fix typos in instructions for OpenIndiana, and update for new toolkits available in 2022 --- docs/config-prereqs.txt | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/docs/config-prereqs.txt b/docs/config-prereqs.txt index 9aa32fe027..cac8461c8a 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -469,7 +469,7 @@ scripts), better use this routine to test the config/build: Note the lack of `pkg-config` also precludes libcppunit tests, although they also tend to mis-compile/mis-link with GCC (while CLANG seems okay). -OpenIndiana 2021.04 +OpenIndiana 2021.10 ~~~~~~~~~~~~~~~~~~~ Note that due to IPS and `pkg(5)`, a version of python is part of baseline @@ -507,6 +507,7 @@ Typical tooling would include: openssl library/mozilla-nss \ library/augeas python/augeas \ libusb-1 libusbugen system/library/usb/libusb system/header/header-usb driver/usb/ugen \ + libmodbus \ neon \ net-snmp \ powerman \ @@ -526,23 +527,41 @@ Typical tooling would include: ### Maybe - after it gets fixed for GCC builds/linkage :; pkg install \ cppunit +---- -### Not yet in distro, PR pending: +For extra compiler coverage, we can install a large selection of versions: +---- :; pkg install \ - libmodbus + gcc-48 gcc-49 gcc-5 gcc-6 gcc-7 gcc-9 gcc-10 gcc-11 \ + clang-80 clang-90 \ + developer/clang-13 runtime/clang-13 \ + ccache + +# Get clang-cpp-X visible in standard PATH (for CI to reference the right one): +:; (cd /usr/bin && for X in 8 9 13 ; do \ + ln -s ../clang/$X.0/bin/clang-cpp clang-cpp-$X ; \ + done) + +# If /usr/lib/ccache/ symlinks to these do not appear, call the service: +:; svcadm refresh ccache-update-symlinks ---- -For extra compiler coverage, can also set up `gcc-4.4.4-il` (used to build -the OS, or was recently, and is a viable example of an old GCC baseline); +We can also include a `gcc-4.4.4-il` (used to build the illumos OS ecosystems, +at least until recently, which is a viable example of an old GCC baseline); but note that so far it conflicts with libgd builds at `configure --with-cgi` stage: + ---- :; pkg install \ - illumos-gcc@4.4.4 \ - gcc-48 gcc-49 gcc-5 gcc-6 gcc-7 gcc-9 gcc-10 \ - clang-80 clang-90 + illumos-gcc@4.4.4 + +# Make it visible in standard PATH +:; (cd /usr/bin && for T in gcc g++ cpp ; do \ + ln -s ../../opt/gcc/4.4.4/bin/$T $T-4.4.4 ; \ + done) -:; svcadm refresh clang-update-symlinks +# If /usr/lib/ccache/ symlinks to these do not appear, call the service: +:; svcadm refresh ccache-update-symlinks ---- OI currently also does not build cppunit-based tests well, at least @@ -556,7 +575,8 @@ from third-party repositories (e.g. SFE) and/or build from sources. No pre-packaged `cppcheck` was found, either. NOTE: For Jenkins agents, also need to `pkg install developer/java/openjdk8` -(or 11+). +(or `pkg install runtime/java/openjdk11` for JRE 11 -- currently the OS does +not offer in-distro JDK version 11+). OmniOS CE (as of release 151036) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 4bd51a041ed448ab2bc27f506de24b99cbc5b4e9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Mar 2022 22:18:31 +0200 Subject: [PATCH 331/700] docs/config-prereqs.txt: update instructions for symlinks on OI --- docs/config-prereqs.txt | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/config-prereqs.txt b/docs/config-prereqs.txt index 2aba7ef26f..a2d17398aa 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -541,12 +541,15 @@ For extra compiler coverage, we can install a large selection of versions: :; pkg install \ developer/clang-13 runtime/clang-13 \ -# Get clang-cpp-X visible in standard PATH (for CI to reference the right one): -:; (cd /usr/bin && for X in 8 9 13 ; do \ - ln -s ../clang/$X.0/bin/clang-cpp clang-cpp-$X ; \ - done) - -# If /usr/lib/ccache/ symlinks to these do not appear, call the service: +# Get clang-cpp-X visible in standard PATH (for CI to reference the right one), +# and make sure other frontends are exposed with versions (not all OI distro +# releases have such symlinks packaged right), e.g.: +:; (cd /usr/bin && for X in 8 9 13 ; do for T in "" "++" "-cpp"; do \ + ln -fs "../clang/$X.0/bin/clang$T" "clang${T}-${X}" ; \ + done; done) + +# If /usr/lib/ccache/ symlinks to compilers do not appear after package +# installation, or if you had to add links like above, call the service: :; svcadm refresh ccache-update-symlinks ---- From 345a254abad5811fcbbae630c9ea6d2deedc56c4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Mar 2022 22:22:04 +0200 Subject: [PATCH 332/700] docs/config-prereqs.txt: mark aspell as recommended (it is not big) --- docs/config-prereqs.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/config-prereqs.txt b/docs/config-prereqs.txt index a2d17398aa..4e3b4a3c66 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -102,7 +102,7 @@ source versions on both Debian 8 Jessie and Debian 11 Buster). pkg-config \ gcc g++ clang -# For spell-checking: +# For spell-checking, highly recommended if you would propose pull requests: :; apt-get install \ aspell aspell-en @@ -228,7 +228,7 @@ https://unix.stackexchange.com/questions/475584/cannot-install-busybox-on-centos pkgconfig \ gcc gcc-c++ clang -# For spell-checking: +# For spell-checking, highly recommended if you would propose pull requests: :; yum install \ aspell aspell-en @@ -299,7 +299,7 @@ below. pkgconf \ gcc clang -# For spell-checking: +# For spell-checking, highly recommended if you would propose pull requests: :; pkg install \ aspell en-aspell @@ -399,7 +399,7 @@ networked repository (4.2.x vs 4.9.x) pkgconf \ gcc clang -# For spell-checking: +# For spell-checking, highly recommended if you would propose pull requests: :; pkg_add \ aspell @@ -489,7 +489,7 @@ Typical tooling would include: pkg-config \ gnu-binutils developer/linker -# For spell-checking: +# For spell-checking, highly recommended if you would propose pull requests: :; pkg install \ aspell text/aspell/en From 899ec2a05fdc3d5618da846277c4f19c369cf43d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Mar 2022 22:48:50 +0000 Subject: [PATCH 333/700] docs/config-prereqs.txt: currently refrain from clang-13 in OI --- docs/config-prereqs.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/config-prereqs.txt b/docs/config-prereqs.txt index cac8461c8a..2aba7ef26f 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -534,9 +534,13 @@ For extra compiler coverage, we can install a large selection of versions: :; pkg install \ gcc-48 gcc-49 gcc-5 gcc-6 gcc-7 gcc-9 gcc-10 gcc-11 \ clang-80 clang-90 \ - developer/clang-13 runtime/clang-13 \ ccache +# As of this writing, clang-13 refused to link (claiming issues with +# --fuse-ld which was never specified) on OI; maybe later it will: +:; pkg install \ + developer/clang-13 runtime/clang-13 \ + # Get clang-cpp-X visible in standard PATH (for CI to reference the right one): :; (cd /usr/bin && for X in 8 9 13 ; do \ ln -s ../clang/$X.0/bin/clang-cpp clang-cpp-$X ; \ From e2c49076b9b0dc26c829688c838d299ddc683d88 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Mar 2022 22:41:31 +0200 Subject: [PATCH 334/700] ci_build.sh: if we only build USB variant(s), do not shy away from testing some SSL and whatever drivers we can --- ci_build.sh | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/ci_build.sh b/ci_build.sh index ebbc6cc081..1ec80b9cc9 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -1215,8 +1215,10 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp # Quietly build one scenario, whatever we can (or not) # configure regarding USB and other features NUT_USB_VARIANT=auto - ( CONFIG_OPTS+=("--without-all") - CONFIG_OPTS+=("--without-ssl") + ( if [ "$NUT_SSL_VARIANTS" != "auto" ] ; then + CONFIG_OPTS+=("--without-all") + CONFIG_OPTS+=("--without-ssl") + fi CONFIG_OPTS+=("--with-serial=auto") CONFIG_OPTS+=("--with-usb") configure_nut @@ -1224,8 +1226,10 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp ;; no) echo "=== Building without USB support (check mixed drivers coded for Serial/USB support)..." - ( CONFIG_OPTS+=("--without-all") - CONFIG_OPTS+=("--without-ssl") + ( if [ "$NUT_SSL_VARIANTS" != "auto" ] ; then + CONFIG_OPTS+=("--without-all") + CONFIG_OPTS+=("--without-ssl") + fi CONFIG_OPTS+=("--with-serial=auto") CONFIG_OPTS+=("--without-usb") configure_nut @@ -1233,8 +1237,10 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp ;; libusb-*) echo "=== Building with NUT_USB_VARIANT='${NUT_USB_VARIANT}' ..." - ( CONFIG_OPTS+=("--without-all") - CONFIG_OPTS+=("--without-ssl") + ( if [ "$NUT_SSL_VARIANTS" != "auto" ] ; then + CONFIG_OPTS+=("--without-all") + CONFIG_OPTS+=("--without-ssl") + fi CONFIG_OPTS+=("--with-serial=auto") CONFIG_OPTS+=("--with-usb=${NUT_USB_VARIANT}") configure_nut @@ -1242,8 +1248,10 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp ;; *) echo "=== Building with NUT_USB_VARIANT='${NUT_USB_VARIANT}' ..." - ( CONFIG_OPTS+=("--without-all") - CONFIG_OPTS+=("--without-ssl") + ( if [ "$NUT_SSL_VARIANTS" != "auto" ] ; then + CONFIG_OPTS+=("--without-all") + CONFIG_OPTS+=("--without-ssl") + fi CONFIG_OPTS+=("--with-serial=auto") CONFIG_OPTS+=("--with-usb=libusb-${NUT_USB_VARIANT}") configure_nut From 656d3792f6b47e8bba3cddc8230ed8d45ddb1eb5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Mar 2022 00:07:19 +0200 Subject: [PATCH 335/700] drivers/adelsystem_cbi.c: extend pragmas for covered/requred "default" case to work with clang-3.4 --- drivers/adelsystem_cbi.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c index 6b9d5e64e7..cda2696848 100644 --- a/drivers/adelsystem_cbi.c +++ b/drivers/adelsystem_cbi.c @@ -576,9 +576,20 @@ void reginit(void) regs[i].saddr = rnum - 1; regs[i].xaddr = 0x40000 + rnum - 1; break; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) # pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT # pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" #endif /* All enum cases defined as of the time of coding * have been covered above. Handle later definitions, @@ -595,9 +606,12 @@ void reginit(void) regs[i].type, regs[i].num ); -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) # pragma GCC diagnostic pop -#endif +#endif } upsdebugx(3, "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", @@ -1120,9 +1134,21 @@ int get_dev_state(devreg_t regindx, devstate_t **dvstat) case BINH: case FSD: break; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) # pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT # pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" #endif /* All enum cases defined as of the time of coding * have been covered above. Handle later definitions, @@ -1139,7 +1165,10 @@ int get_dev_state(devreg_t regindx, devstate_t **dvstat) sprintf(reg_val_s, "%d", reg_val); state->reg.strval = reg_val_s; break; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) # pragma GCC diagnostic pop #endif } From a84ef6c983df469d0ed69781c1f97fe6696f49ba Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Mar 2022 00:09:34 +0200 Subject: [PATCH 336/700] drivers/adelsystem_cbi.h: functions implemented in a header should be static --- drivers/adelsystem_cbi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/adelsystem_cbi.h b/drivers/adelsystem_cbi.h index a3b63633d5..023586b7c9 100644 --- a/drivers/adelsystem_cbi.h +++ b/drivers/adelsystem_cbi.h @@ -254,7 +254,7 @@ typedef struct alrm_ar alrm_ar_t; #define BSTA_CNNFLT_I 5 /* connection fault */ /* Allocate alarm arrays */ -inline +static inline alrm_ar_t *alloc_alrm_ar(int as, size_t n) { alrm_ar_t *ret = xcalloc(sizeof(alrm_t) + n, 1); @@ -270,7 +270,7 @@ alrm_ar_t *alloc_alrm_ar(int as, size_t n) } /* Initialize alarm arrays */ -inline +static inline void alrm_ar_init(alrm_ar_t *ar_ptr, alrm_t *a_ptr, int as) { ar_ptr->alrm_c = as; From 2bc1d19ae61cc4c4b85faef695d98416dc405091 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Mar 2022 00:13:22 +0200 Subject: [PATCH 337/700] NEWS: added driver adelsystem_cbi for NUT v2.7.5 --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 4aa441f798..fc02ecfc15 100644 --- a/NEWS +++ b/NEWS @@ -109,6 +109,10 @@ Release notes for NUT 2.8.0 - what's new since 2.7.4: RS-232 for Socomec UPS (tested with a DIGYS 3/3 15kVA model, working on Linux x86-64 and Raspberry Pi 3 ARM). [PR #1313] + - adelsystem_cbi: added new driver for ADELSYSTEM CBI2801224A, an all-in-one + 12/24Vdc DC-UPS, which supports the modbus RTU communication protocol + [PR #1282] + - generic_modbus: added new driver for TCP and Serial Modbus device support. The driver has been tested against PULS UPS (model UB40.241) via MOXA ioLogikR1212 (RS485) and ioLogikE1212 (TCP/IP), and configuration From 316d17bcf2afaa55eee6df3c37aec38a79343744 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Mar 2022 22:53:51 +0100 Subject: [PATCH 338/700] drivers/usbhid-ups.c: try to close libusb handle before reconnecting (from discussion of issue #414) --- drivers/usbhid-ups.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 7ac92a6018..b8651cce93 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -1,7 +1,7 @@ /* usbhid-ups.c - Driver for USB and serial (MGE SHUT) HID UPS units * * Copyright (C) - * 2003-2012 Arnaud Quette + * 2003-2022 Arnaud Quette * 2005 John Stamp * 2005-2006 Peter Selinger * 2007-2009 Arjen de Korte @@ -28,7 +28,7 @@ */ #define DRIVER_NAME "Generic HID driver" -#define DRIVER_VERSION "0.45" +#define DRIVER_VERSION "0.46" #include "main.h" #include "libhid.h" @@ -147,7 +147,7 @@ bool_t use_interrupt_pipe = TRUE; bool_t use_interrupt_pipe = FALSE; #endif static time_t lastpoll; /* Timestamp the last polling */ -hid_dev_handle_t udev; +hid_dev_handle_t udev = NULL; /* support functions */ static hid_info_t *find_nut_info(const char *varname); @@ -1544,6 +1544,10 @@ static int reconnect_ups(void) upsdebugx(4, "= device has been disconnected, try to reconnect ="); upsdebugx(4, "=================================================="); + /* Try to close the previous handle */ + if (udev) + comm_driver->close(udev); + ret = comm_driver->open(&udev, &curDevice, subdriver_matcher, NULL); if (ret > 0) { From 0fa5687aef27ba7ada1cd8aeec6963560c08f00f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 15 Mar 2022 23:08:57 +0200 Subject: [PATCH 339/700] drivers/libusb{0,1}.c: retry getting Manufacturer/Product/Serial a few times if failed on the first Kudos to Sam Varshavchik for proposing this code change in discussion https://github.com/networkupstools/nut/issues/414 --- drivers/libusb0.c | 49 +++++++++++++++++++++++----------- drivers/libusb1.c | 67 +++++++++++++++++++++++++++++------------------ 2 files changed, 75 insertions(+), 41 deletions(-) diff --git a/drivers/libusb0.c b/drivers/libusb0.c index 4723f3a1e3..de22c48d89 100644 --- a/drivers/libusb0.c +++ b/drivers/libusb0.c @@ -34,7 +34,7 @@ #include "nut_libusb.h" #define USB_DRIVER_NAME "USB communication driver (libusb 0.1)" -#define USB_DRIVER_VERSION "0.42" +#define USB_DRIVER_VERSION "0.43" /* driver description structure */ upsdrv_info_t comm_upsdrv_info = { @@ -46,6 +46,7 @@ upsdrv_info_t comm_upsdrv_info = { }; #define MAX_REPORT_SIZE 0x1800 +#define MAX_RETRY 3 static void libusb_close(usb_dev_handle *udev); @@ -166,9 +167,7 @@ static int libusb_open(usb_dev_handle **udevp, USBDevice_t *hd, usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen) ) { -#ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP int retries; -#endif usb_ctrl_charbufsize rdlen1, rdlen2; /* report descriptor length, method 1+2 */ USBDeviceMatcher_t *m; struct usb_device *dev; @@ -239,26 +238,44 @@ static int libusb_open(usb_dev_handle **udevp, curDevice->bcdDevice = dev->descriptor.bcdDevice; if (dev->descriptor.iManufacturer) { - ret = usb_get_string_simple(udev, dev->descriptor.iManufacturer, - string, sizeof(string)); - if (ret > 0) { - curDevice->Vendor = xstrdup(string); + retries = MAX_RETRY; + while (retries > 0) { + ret = usb_get_string_simple(udev, dev->descriptor.iManufacturer, + string, sizeof(string)); + if (ret > 0) { + curDevice->Vendor = xstrdup(string); + break; + } + retries--; + upsdebugx(1, "%s get iManufacturer failed, retrying...", __func__); } } if (dev->descriptor.iProduct) { - ret = usb_get_string_simple(udev, dev->descriptor.iProduct, - string, sizeof(string)); - if (ret > 0) { - curDevice->Product = xstrdup(string); + retries = MAX_RETRY; + while (retries > 0) { + ret = usb_get_string_simple(udev, dev->descriptor.iProduct, + string, sizeof(string)); + if (ret > 0) { + curDevice->Product = xstrdup(string); + break; + } + retries--; + upsdebugx(1, "%s get iProduct failed, retrying...", __func__); } } if (dev->descriptor.iSerialNumber) { - ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, - string, sizeof(string)); - if (ret > 0) { - curDevice->Serial = xstrdup(string); + retries = MAX_RETRY; + while (retries > 0) { + ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, + string, sizeof(string)); + if (ret > 0) { + curDevice->Serial = xstrdup(string); + break; + } + retries--; + upsdebugx(1, "%s get iSerialNumber failed, retrying...", __func__); } } @@ -307,7 +324,7 @@ static int libusb_open(usb_dev_handle **udevp, /* this method requires at least libusb 0.1.8: * it force device claiming by unbinding * attached driver... From libhid */ - retries = 3; + retries = MAX_RETRY; while (usb_claim_interface(udev, usb_subdriver.hid_rep_index) < 0) { upsdebugx(2, "failed to claim USB device: %s", diff --git a/drivers/libusb1.c b/drivers/libusb1.c index 44d8dc96d5..d9dc2d67e4 100644 --- a/drivers/libusb1.c +++ b/drivers/libusb1.c @@ -33,7 +33,7 @@ #include "nut_stdint.h" #define USB_DRIVER_NAME "USB communication driver (libusb 1.0)" -#define USB_DRIVER_VERSION "0.42" +#define USB_DRIVER_VERSION "0.43" /* driver description structure */ upsdrv_info_t comm_upsdrv_info = { @@ -45,6 +45,7 @@ upsdrv_info_t comm_upsdrv_info = { }; #define MAX_REPORT_SIZE 0x1800 +#define MAX_RETRY 3 static void nut_libusb_close(libusb_device_handle *udev); @@ -138,9 +139,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, USBDevice_t *hd, usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen) ) { -#if (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER) || (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP) int retries; -#endif /* libusb-1.0 usb_ctrl_charbufsize is uint16_t and we * want the rdlen vars signed - so taking a wider type */ int32_t rdlen1, rdlen2; /* report descriptor length, method 1+2 */ @@ -227,38 +226,56 @@ static int nut_libusb_open(libusb_device_handle **udevp, curDevice->bcdDevice = dev_desc.bcdDevice; if (dev_desc.iManufacturer) { - ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iManufacturer, - (unsigned char*)string, sizeof(string)); - if (ret > 0) { - curDevice->Vendor = strdup(string); - if (curDevice->Vendor == NULL) { - libusb_free_device_list(devlist, 1); - fatal_with_errno(EXIT_FAILURE, "Out of memory"); + retries = MAX_RETRY; + while (retries > 0) { + ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iManufacturer, + (unsigned char*)string, sizeof(string)); + if (ret > 0) { + curDevice->Vendor = strdup(string); + if (curDevice->Vendor == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + break; } + retries--; + upsdebugx(1, "%s get iManufacturer failed, retrying...", __func__); } } if (dev_desc.iProduct) { - ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iProduct, - (unsigned char*)string, sizeof(string)); - if (ret > 0) { - curDevice->Product = strdup(string); - if (curDevice->Product == NULL) { - libusb_free_device_list(devlist, 1); - fatal_with_errno(EXIT_FAILURE, "Out of memory"); + retries = MAX_RETRY; + while (retries > 0) { + ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iProduct, + (unsigned char*)string, sizeof(string)); + if (ret > 0) { + curDevice->Product = strdup(string); + if (curDevice->Product == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + break; } + retries--; + upsdebugx(1, "%s get iProduct failed, retrying...", __func__); } } if (dev_desc.iSerialNumber) { - ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iSerialNumber, - (unsigned char*)string, sizeof(string)); - if (ret > 0) { - curDevice->Serial = strdup(string); - if (curDevice->Serial == NULL) { - libusb_free_device_list(devlist, 1); - fatal_with_errno(EXIT_FAILURE, "Out of memory"); + retries = MAX_RETRY; + while (retries > 0) { + ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iSerialNumber, + (unsigned char*)string, sizeof(string)); + if (ret > 0) { + curDevice->Serial = strdup(string); + if (curDevice->Serial == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + break; } + retries--; + upsdebugx(1, "%s get iSerialNumber failed, retrying...", __func__); } } @@ -343,7 +360,7 @@ static int nut_libusb_open(libusb_device_handle **udevp, #if (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER) || (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP) /* Then, try the explicit detach method. * This function is available on FreeBSD 10.1-10.3 */ - retries = 3; + retries = MAX_RETRY; while ((ret = libusb_claim_interface(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) { upsdebugx(2, "failed to claim USB device: %s", libusb_strerror((enum libusb_error)ret)); From 3eb056b2eaea2204a3120aca28bb0b2240808b7f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 16 Mar 2022 19:54:25 +0100 Subject: [PATCH 340/700] drivers/libhid.h, usbhid-ups.c: define HID_DEV_HANDLE_CLOSED usable for both SHUT and USB cases --- drivers/libhid.h | 2 ++ drivers/usbhid-ups.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/libhid.h b/drivers/libhid.h index d422002b6a..23e34a1389 100644 --- a/drivers/libhid.h +++ b/drivers/libhid.h @@ -42,12 +42,14 @@ typedef char HIDDeviceMatcher_t; typedef usb_dev_handle hid_dev_handle_t; typedef shut_communication_subdriver_t communication_subdriver_t; + #define HID_DEV_HANDLE_CLOSED (hid_dev_handle_t)(-1) #else #include "nut_libusb.h" /* includes usb-common.h */ typedef USBDevice_t HIDDevice_t; typedef USBDeviceMatcher_t HIDDeviceMatcher_t; typedef usb_dev_handle * hid_dev_handle_t; typedef usb_communication_subdriver_t communication_subdriver_t; + #define HID_DEV_HANDLE_CLOSED (hid_dev_handle_t)(NULL) #endif /* use explicit booleans */ diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index b8651cce93..999651864b 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -147,7 +147,7 @@ bool_t use_interrupt_pipe = TRUE; bool_t use_interrupt_pipe = FALSE; #endif static time_t lastpoll; /* Timestamp the last polling */ -hid_dev_handle_t udev = NULL; +hid_dev_handle_t udev = HID_DEV_HANDLE_CLOSED; /* support functions */ static hid_info_t *find_nut_info(const char *varname); From 111da7410f3286eefa0555f80d1602562faee0a2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Mar 2022 15:29:29 +0100 Subject: [PATCH 341/700] drivers/nutdrv_qx.c: snr_command(): adapt to usb_ctrl_charbuf --- drivers/nutdrv_qx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index 6dc414f02a..7ecbd151b6 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -1571,7 +1571,9 @@ static int snr_command(const char *cmd, char *buf, size_t buflen) * Without the interrupt UPS returns zeros for some time, * and afterwards NUT returns a communications error. */ - usb_interrupt_read(udev, 0x81, buf, 102, 1000); + usb_interrupt_read(udev, + 0x81, + (usb_ctrl_charbuf)buf, 102, 1000); for (i = 0; command[i].str; i++) { From b7fe4a12d2a3d042deab935ed0b590ee7ad3043b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Mar 2022 15:33:17 +0100 Subject: [PATCH 342/700] ci_build.sh: when parsing BUILD_TYPE=fightwarn, do not default NUT_SSL_VARIANTS and NUT_USB_VARIANTS to "auto" - so building just one variant at most, even if agent supports more --- ci_build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci_build.sh b/ci_build.sh index 1ec80b9cc9..e1c545f876 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -46,10 +46,10 @@ if [ "$BUILD_TYPE" = fightwarn ]; then # SSL implementations since their ifdef-driven codebases differ and # emit varied warnings. But so far would be nice to get the majority # of shared codebase clean first: - [ -n "$NUT_SSL_VARIANTS" ] || NUT_SSL_VARIANTS=auto + #[ -n "$NUT_SSL_VARIANTS" ] || NUT_SSL_VARIANTS=auto # Similarly for libusb implementations with varying support - [ -n "$NUT_USB_VARIANTS" ] || NUT_USB_VARIANTS=auto + #[ -n "$NUT_USB_VARIANTS" ] || NUT_USB_VARIANTS=auto fi # Set this to enable verbose profiling From d974eb9a85b0a1134d1bec48c79e62b61891a898 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Mar 2022 15:38:17 +0100 Subject: [PATCH 343/700] drivers/nutdrv_qx.c: fix trailing whitespace --- drivers/nutdrv_qx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index 7ecbd151b6..7f70106df2 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -1670,7 +1670,7 @@ static int ablerex_command(const char *cmd, char *buf, size_t buflen) __func__, buflen); buflen = (INT_MAX - 1); } - + int retry; for (retry = 0; retry < 3; retry++) { @@ -3768,7 +3768,7 @@ static bool_t qx_ups_walk(walkmode_t mode) if (!val) { upsdebugx(2, "%s: unable to get battery.voltage", __func__); } else { - + batt.volt.act = batt.packs * strtod(val, NULL); if (batt.volt.act > 0 && batt.volt.low > 0 && batt.volt.high > batt.volt.low) { From 93fd9a7e2d0a42dae737c49aa186a82f93ace620 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Mar 2022 15:42:29 +0100 Subject: [PATCH 344/700] drivers/nutdrv_qx.c: comment about source and data for estimated runtime corrections --- drivers/nutdrv_qx.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index 7f70106df2..b8f9af8bef 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -3768,6 +3768,10 @@ static bool_t qx_ups_walk(walkmode_t mode) if (!val) { upsdebugx(2, "%s: unable to get battery.voltage", __func__); } else { + /* For age-corrected estimates below, + * see theory and experimental graphs at + * https://github.com/networkupstools/nut/pull/1027 + */ batt.volt.act = batt.packs * strtod(val, NULL); @@ -3783,8 +3787,13 @@ static bool_t qx_ups_walk(walkmode_t mode) voltage_battery_charge = 1; } - /* Correct estimated runtime remaining for old batteries */ - if(voltage_battery_charge < (batt.runt.est / batt.runt.nom)) { + /* Correct estimated runtime remaining for old batteries: + * this value replacement only happens if the actual + * voltage_battery_charge is smaller than expected by + * previous (load-based) estimation, thus adapting to a + * battery too old and otherwise behaving non-linearly + */ + if (voltage_battery_charge < (batt.runt.est / batt.runt.nom)) { batt.runt.est = voltage_battery_charge * batt.runt.nom; } From 01384214734b25c247d80680e5f61f53e1ce64f3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Mar 2022 15:45:29 +0100 Subject: [PATCH 345/700] NEWS: nutdrv_qx: enhanced estimation of remaining battery runtime for NUT v2.7.5 --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index fc02ecfc15..e9a00a7e01 100644 --- a/NEWS +++ b/NEWS @@ -73,6 +73,9 @@ Release notes for NUT 2.8.0 - what's new since 2.7.4: * Emerson Avocent PM3000 PDU (SNMP) * HPE ePDU (SNMP) + - nutdrv_qx: enhanced estimation of remaining battery runtime based + on speed of voltage drop, which varies as they age [PR #1027] + - nutdrv_qx: several subdrivers added or improved, including: * "snr" subdriver with USB connection, for SNR-UPS-LID-XXXX [PR #1008]. Note that end-users should reference explicitly the `snr` subdriver From 45afede21c6d7ee26173e515de989d93f6f0656b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 19 Mar 2022 15:52:39 +0100 Subject: [PATCH 346/700] drivers/nutdrv_qx.c: qx_ups_walk(): log the change of estimated remaining runtime --- drivers/nutdrv_qx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index b8f9af8bef..f151ed5017 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -3794,7 +3794,10 @@ static bool_t qx_ups_walk(walkmode_t mode) * battery too old and otherwise behaving non-linearly */ if (voltage_battery_charge < (batt.runt.est / batt.runt.nom)) { + double estPrev = batt.runt.est; batt.runt.est = voltage_battery_charge * batt.runt.nom; + upsdebugx(3, "%s: updating batt.runt.est from '%g' to '%g'", + __func__, estPrev, batt.runt.est); } } From ee0d004a1379ab5433686397a3a6164cf2ce0ecc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 21 Mar 2022 14:18:32 +0000 Subject: [PATCH 347/700] scripts/subdriver/gen-snmp-subdriver.sh: fix back markup for mib2nut_info_t lines Follow-up for #1327 Thanks to @aquette --- scripts/subdriver/gen-snmp-subdriver.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/subdriver/gen-snmp-subdriver.sh b/scripts/subdriver/gen-snmp-subdriver.sh index dc5fb492a9..f7ccf442ff 100755 --- a/scripts/subdriver/gen-snmp-subdriver.sh +++ b/scripts/subdriver/gen-snmp-subdriver.sh @@ -284,7 +284,7 @@ EOF { NULL, 0, 0, NULL, NULL, 0, NULL } }; - mib2nut_info_t ${LDRIVER} = { "${LDRIVER}", ${UDRIVER}_MIB_VERSION, NULL, NULL, ${LDRIVER}_mib, ${UDRIVER}_DEVICE_SYSOID }; +mib2nut_info_t ${LDRIVER} = { "${LDRIVER}", ${UDRIVER}_MIB_VERSION, NULL, NULL, ${LDRIVER}_mib, ${UDRIVER}_DEVICE_SYSOID }; EOF return From 1d561fc8f1020fa00001e3455b5c20195d433d52 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 08:18:27 +0000 Subject: [PATCH 348/700] configure.ac: bump NUT_NETVERSION to 1.3 to match docs/net-protocol.txt --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 50542fcaf9..92d6c0c94c 100644 --- a/configure.ac +++ b/configure.ac @@ -50,7 +50,7 @@ AC_DEFINE_UNQUOTED(TREE_VERSION, "${TREE_VERSION}", [NUT tree version]) dnl Should not be necessary, since old servers have well-defined errors for dnl unsupported commands: -NUT_NETVERSION="1.2" +NUT_NETVERSION="1.3" AC_DEFINE_UNQUOTED(NUT_NETVERSION, "${NUT_NETVERSION}", [NUT network protocol version]) From 89e6f9a6d805f848bbf087d79d8185a06c79c40e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 08:19:09 +0000 Subject: [PATCH 349/700] docs/net-protocol.txt: bump next release from 2.7.5 to 2.8.0 --- docs/net-protocol.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index a29bcdc8f7..cb4d8d6ce5 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -44,7 +44,7 @@ NUT network protocol, over the time: |1.1 |>= 1.5.0 |Original protocol (without old commands) .2+|1.2 .2+|>= 2.6.4 |Add "LIST CLIENTS" and "NETVER" commands |Add ranges of values for writable variables -.2+|1.3 .2+|>= 2.7.5 |Add "cmdparam" to "INSTCMD" +.2+|1.3 .2+|>= 2.8.0 |Add "cmdparam" to "INSTCMD" |Add "TRACKING" commands (GET, SET) |=============================================================================== From 46f96ef9fd87e7b71a1cff161fcf70e89117ac61 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 08:19:38 +0000 Subject: [PATCH 350/700] docs/net-protocol.txt: document "PRIMARY" as alias to "MASTER" --- docs/net-protocol.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index cb4d8d6ce5..f8a104ee14 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -46,6 +46,9 @@ NUT network protocol, over the time: |Add ranges of values for writable variables .2+|1.3 .2+|>= 2.8.0 |Add "cmdparam" to "INSTCMD" |Add "TRACKING" commands (GET, SET) + |Add "PRIMARY" as alias to older "MASTER" + | (implementation tested to be backwards + | compatible in `upsd` and `upsmon`) |=============================================================================== NOTE: any new version of the protocol implies an update of NUT_NETVERSION From ada4fd72bbac58d61b027e445a9383dc9b8f4558 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 08:24:09 +0000 Subject: [PATCH 351/700] docs, NEWS, UPGRADING: rename 2.7.5 to 2.8.0 in text that appeared after 2.7.4 --- NEWS | 2 +- UPGRADING | 2 +- docs/config-notes.txt | 2 +- docs/man/upscmd.txt | 2 +- docs/man/upsrw.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index fc02ecfc15..3e344593d8 100644 --- a/NEWS +++ b/NEWS @@ -373,7 +373,7 @@ Release notes for NUT 2.8.0 - what's new since 2.7.4: few changes also happened in header files installed for builds configured `--with-dev` and so may impact `upsclient` and `nutclient` (C++) consumers. At the very least, binaries for those consumers should be rebuilt to remain - stable with NUT 2.7.5 and not mismatch int-type sizes and other arguments. + stable with NUT 2.8.0 and not mismatch int-type sizes and other arguments. - As usual, more bugfixes, cleanup and improvements, on both source code and documentation. diff --git a/UPGRADING b/UPGRADING index 854a1130fd..a25c73e472 100644 --- a/UPGRADING +++ b/UPGRADING @@ -25,7 +25,7 @@ Changes from 2.7.4 to 2.8.0 few changes also happened in header files installed for builds configured `--with-dev` and so may impact `upsclient` and `nutclient` (C++) consumers. At the very least, binaries for those consumers should be rebuilt to remain - stable with NUT 2.7.5 and not mismatch int-type sizes and other arguments. + stable with NUT 2.8.0 and not mismatch int-type sizes and other arguments. - libusb-1.0: NUT now defaults to building against libusb-1.0 API version if the configure script finds the development headers, falling back to diff --git a/docs/config-notes.txt b/docs/config-notes.txt index 1c1f9fae14..df4ac937a9 100644 --- a/docs/config-notes.txt +++ b/docs/config-notes.txt @@ -209,7 +209,7 @@ This can be a big issue on systems which monitor multiple devices, such as big servers with multiple power sources, or administrative workstations which monitor a datacenter full of UPSes. -For this reason, NUT starting with version 2.7.5 supports startup of its +For this reason, NUT starting with version 2.8.0 supports startup of its drivers as independent instances of a `nut-driver` service under the Linux systemd and Solaris/illumos SMF service-management frameworks (corresponding files and scripts may be not pre-installed in packaging for other systems). diff --git a/docs/man/upscmd.txt b/docs/man/upscmd.txt index c6c48992c8..9492973758 100644 --- a/docs/man/upscmd.txt +++ b/docs/man/upscmd.txt @@ -45,7 +45,7 @@ like -u, and you will be prompted for it if necessary. *-w*:: Wait for the completion of command execution by the driver and return its actual result from the device. Note that this feature requires that both upsd -and the driver support TRACKING (NUT version 2.7.5 or higher) or it will +and the driver support TRACKING (NUT version 2.8.0 or higher) or it will otherwise fail. The command will also block until an actual result is provided from the driver, or the timeout is reached (see *-t*). diff --git a/docs/man/upsrw.txt b/docs/man/upsrw.txt index 54a79f4e92..5081edf218 100644 --- a/docs/man/upsrw.txt +++ b/docs/man/upsrw.txt @@ -61,7 +61,7 @@ like -u, and you will be prompted for it if necessary. *-w*:: Wait for the completion of setting execution by the driver and return its actual result from the device. Note that this feature requires that both upsd -and the driver support TRACKING (NUT version 2.7.5 or higher) or it will +and the driver support TRACKING (NUT version 2.8.0 or higher) or it will otherwise fail. The command will also block until an actual result is provided from the driver, or the timeout is reached (see *-t*). From d64b418f472edaaec198bf525bbf668bcdcd7568 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 10:19:01 +0000 Subject: [PATCH 352/700] configure.ac: document practical requirement for AC_PREREQ(2.64) --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 92d6c0c94c..72120922c6 100644 --- a/configure.ac +++ b/configure.ac @@ -18,6 +18,7 @@ AC_PREFIX_DEFAULT(/usr/local/ups) AM_INIT_AUTOMAKE([subdir-objects]) dnl we need Autoconf 2.61 or better to enable features of Posix that are extensions to C +dnl (and actually 2.64 or better for m4/ax_check_compile_flag.m4 when it is sourced) AC_MSG_CHECKING(for autoconf macro to enable system extensions) m4_version_prereq(2.61, [ AC_MSG_RESULT(yes) From c89b3d350ee59bb84c49ccbc55a9698ec2b14902 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 12:37:15 +0100 Subject: [PATCH 353/700] m4/nut_check_libgd.m4: fix detection of gdImagePng() with additional link requirements --- m4/nut_check_libgd.m4 | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/m4/nut_check_libgd.m4 b/m4/nut_check_libgd.m4 index 858ae021f4..b6f58efcb1 100644 --- a/m4/nut_check_libgd.m4 +++ b/m4/nut_check_libgd.m4 @@ -1,9 +1,9 @@ dnl Check for LIBGD compiler flags. On success, set nut_have_libgd="yes" dnl and set LIBGD_CFLAGS and LIBGD_LDFLAGS. On failure, set dnl nut_have_libgd="no". This macro can be run multiple times, but will -dnl do the checking only once. +dnl do the checking only once. -AC_DEFUN([NUT_CHECK_LIBGD], +AC_DEFUN([NUT_CHECK_LIBGD], [ if test -z "${nut_have_libgd_seen}"; then nut_have_libgd_seen=yes @@ -117,7 +117,21 @@ if test -z "${nut_have_libgd_seen}"; then dnl check if gd is usable AC_CHECK_HEADERS(gd.h gdfontmb.h, [nut_have_libgd=yes], [nut_have_libgd=no], [AC_INCLUDES_DEFAULT]) - AC_SEARCH_LIBS(gdImagePng, gd, [], [nut_have_libgd=no]) + AC_SEARCH_LIBS(gdImagePng, gd, [], [ + dnl If using pkg-config, query additionally for Libs.private + dnl to pull -L/usr/X11R6/lib or whatever current OS wants + AC_MSG_CHECKING([for more gd library flags]) + AS_IF([test -n "${with_gd_libs}" || test x"$have_PKG_CONFIG" != xyes], [nut_have_libgd=no], [ + _LIBS_PRIVATE="`$PKG_CONFIG --silence-errors --libs gdlib --static 2>/dev/null`" + AS_IF([test -z "${_LIBS_PRIVATE}"], [nut_have_libgd=no], [ + AC_MSG_CHECKING([with gdlib.pc Libs.private]) + LDFLAGS="$LDFLAGS $_LIBS_PRIVATE" + unset ac_cv_search_gdImagePng + AC_SEARCH_LIBS(gdImagePng, gd, [], [nut_have_libgd=no]) + ]) + unset _LIBS_PRIVATE + ]) + ]) if test "${nut_have_libgd}" = "yes"; then AC_DEFINE(HAVE_LIBGD, 1, [Define if you have Boutell's libgd installed]) From de1b6fbd84181a547ce85c8116c75f940f9a3faf Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 12:37:51 +0100 Subject: [PATCH 354/700] configure.ac: specify AC_PREREQ([2.64]) --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index 50542fcaf9..80800575a0 100644 --- a/configure.ac +++ b/configure.ac @@ -26,6 +26,8 @@ m4_version_prereq(2.61, [ AC_MSG_RESULT(no) ]) +AC_PREREQ([2.64]) + dnl Use "./configure --enable-maintainer-mode" to keep Makefile.in and Makefile dnl in sync after Git updates. AM_MAINTAINER_MODE From 92d747a91bddfaba562b0d0e94656950233e92fe Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 12:47:10 +0100 Subject: [PATCH 355/700] m4/ax_c_pragmas.m4: introduce [HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE --- m4/ax_c_pragmas.m4 | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/m4/ax_c_pragmas.m4 b/m4/ax_c_pragmas.m4 index 934573470b..53c1aebf8e 100644 --- a/m4/ax_c_pragmas.m4 +++ b/m4/ax_c_pragmas.m4 @@ -323,6 +323,33 @@ dnl ### [CFLAGS="${CFLAGS_SAVED} -Werror=pragmas -Werror=unknown-warning" AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ARRAY_BOUNDS_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Warray-bounds" (outside functions)]) ]) + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wtautological-type-limit-compare"], + [ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare=yes], + [ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wtautological-type-limit-compare"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wtautological-type-limit-compare"]], [])], + [ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" (outside functions)]) + ]) + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"], [ax_cv__pragma__gcc__diags_ignored_tautological_constant_out_of_range_compare], [AC_COMPILE_IFELSE( From 43c04fab4b55ce1efbd03eb5196a3394a7090c60 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 12:48:31 +0100 Subject: [PATCH 356/700] drivers/libhid.c: make use of HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE (clang-6.0.0 of OpenBSD 6.4) --- drivers/libhid.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/libhid.c b/drivers/libhid.c index 624050dfb5..f06cbeadc7 100644 --- a/drivers/libhid.c +++ b/drivers/libhid.c @@ -171,7 +171,7 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa } r = max_report_size ? sizeof(rbuf->data[id]) : rbuf->len[id]; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) # pragma GCC diagnostic push #endif #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS @@ -183,6 +183,9 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE # pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" #endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +#endif #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE #pragma GCC diagnostic ignored "-Wunreachable-code" #endif @@ -192,6 +195,7 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa #pragma clang diagnostic ignored "-Wunreachable-code" #pragma clang diagnostic ignored "-Wtautological-compare" #pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#pragma clang diagnostic ignored "-Wtautological-type-limit-compare" #endif if ((uintmax_t)r > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { upsdebugx(2, @@ -220,7 +224,7 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa #ifdef __clang__ #pragma clang diagnostic pop #endif -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) # pragma GCC diagnostic pop #endif @@ -281,7 +285,7 @@ static int set_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t SetValue(pData, rbuf->data[id], Value); -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) # pragma GCC diagnostic push #endif #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS @@ -293,6 +297,9 @@ static int set_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE # pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" #endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +#endif #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE #pragma GCC diagnostic ignored "-Wunreachable-code" #endif @@ -302,6 +309,7 @@ static int set_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t #pragma clang diagnostic ignored "-Wunreachable-code" #pragma clang diagnostic ignored "-Wtautological-compare" #pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#pragma clang diagnostic ignored "-Wtautological-type-limit-compare" #endif if ((uintmax_t)r > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { upsdebugx(2, @@ -324,7 +332,7 @@ static int set_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t #ifdef __clang__ #pragma clang diagnostic pop #endif -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) # pragma GCC diagnostic pop #endif @@ -543,7 +551,7 @@ char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_ { usb_ctrl_strindex idx; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) # pragma GCC diagnostic push #endif #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS @@ -555,6 +563,9 @@ char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_ #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE # pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" #endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +#endif #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE #pragma GCC diagnostic ignored "-Wunreachable-code" #endif @@ -564,6 +575,7 @@ char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_ #pragma clang diagnostic ignored "-Wunreachable-code" #pragma clang diagnostic ignored "-Wtautological-compare" #pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#pragma clang diagnostic ignored "-Wtautological-type-limit-compare" #endif if ((uintmax_t)Index > (uintmax_t)USB_CTRL_STRINDEX_MAX || (intmax_t)Index < (intmax_t)USB_CTRL_STRINDEX_MIN @@ -602,7 +614,7 @@ char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_ #ifdef __clang__ #pragma clang diagnostic pop #endif -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) # pragma GCC diagnostic pop #endif @@ -685,7 +697,7 @@ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize) /* needs libusb-0.1.8 to work => use ifdef and autoconf */ r = interrupt_size ? interrupt_size : sizeof(buf); -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) # pragma GCC diagnostic push #endif #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS @@ -697,6 +709,9 @@ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize) #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE # pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" #endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +#endif #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE #pragma GCC diagnostic ignored "-Wunreachable-code" #endif @@ -706,6 +721,7 @@ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize) #pragma clang diagnostic ignored "-Wunreachable-code" #pragma clang diagnostic ignored "-Wtautological-compare" #pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#pragma clang diagnostic ignored "-Wtautological-type-limit-compare" #endif if ((uintmax_t)r > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { /* FIXME: Should we try here, or plain abort? */ @@ -733,7 +749,7 @@ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize) #ifdef __clang__ #pragma clang diagnostic pop #endif -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) # pragma GCC diagnostic pop #endif From ac213b67067f2c833897fe21f68933e2fedbc166 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 12:59:48 +0100 Subject: [PATCH 357/700] m4/nut_compiler_family.m4: enable back the "-isystem" tuning to not complain about packaged third-party headers --- m4/nut_compiler_family.m4 | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/m4/nut_compiler_family.m4 b/m4/nut_compiler_family.m4 index 1d4f0891aa..b6762bd7d5 100644 --- a/m4/nut_compiler_family.m4 +++ b/m4/nut_compiler_family.m4 @@ -142,11 +142,18 @@ dnl AS_IF([test "x$GCC" = xyes], [CFLAGS="$CFLAGS -Wno-unknown-warning"]) dnl AS_IF([test "x$GXX" = xyes], [CXXFLAGS="$CXXFLAGS -Wno-unknown-warning"]) dnl # There should be no need to include standard system paths (and possibly -dnl # confuse the compiler assumptions - along with its provided headers): -dnl # AS_IF([test "x$CLANGCC" = xyes -o "x$GCC" = xyes], -dnl # [CFLAGS="-isystem /usr/include -isystem /usr/local/include $CFLAGS"]) -dnl # AS_IF([test "x$CLANGXX" = xyes -o "x$GXX" = xyes], -dnl # [CXXFLAGS="-isystem /usr/include -isystem /usr/local/include $CXXFLAGS"]) +dnl # confuse the compiler assumptions - along with its provided headers)... +dnl # ideally; in practice however cppunit, net-snmp and some system include +dnl # files do cause grief to picky compiler settings (more so from third +dnl # party packages shipped via /usr/local/... namespace): + AS_IF([test "x$CLANGCC" = xyes -o "x$GCC" = xyes], [ +dnl # CFLAGS="-isystem /usr/include $CFLAGS" + CFLAGS="-isystem /usr/local/include $CFLAGS" + ]) + AS_IF([test "x$CLANGXX" = xyes -o "x$GXX" = xyes], [ +dnl # CXXFLAGS="-isystem /usr/include $CXXFLAGS" + CXXFLAGS="-isystem /usr/local/include $CXXFLAGS" + ]) dnl # Default to avoid noisy warnings on older compilers dnl # (gcc-4.x, clang-3.x) due to their preference of From 5d5854e778fcd87d69abe7fa080edc07f5044524 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 13:36:05 +0100 Subject: [PATCH 358/700] ci_build.sh: comment BUILD_WARNOPT setting for BUILD_TYPE="fightwarn"* shortcuts --- ci_build.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ci_build.sh b/ci_build.sh index e1c545f876..8c4d2bd798 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -38,7 +38,9 @@ if [ "$BUILD_TYPE" = fightwarn ]; then BUILD_TYPE=default-all-errors BUILD_WARNFATAL=yes - # Current fightwarn goal is to have no warnings at preset level below: + # Current fightwarn goal is to have no warnings at preset level below, + # or at the level defaulted with configure.ac (perhaps considering the + # compiler version, etc.): #[ -n "$BUILD_WARNOPT" ] || BUILD_WARNOPT=hard [ -n "$BUILD_WARNOPT" ] || BUILD_WARNOPT=medium From c8a3e004934550a833e07e20366c2ffcf946df6d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 15:52:09 +0100 Subject: [PATCH 359/700] ci_build.sh: set BUILD_WARNOPT and BUILD_WARNFATAL to "auto" for BUILD_TYPE="fightwarn"* shortcuts (survive antique compilers, default to medium/fatal normally) --- ci_build.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ci_build.sh b/ci_build.sh index 8c4d2bd798..7c36133030 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -36,13 +36,17 @@ if [ "$BUILD_TYPE" = fightwarn ]; then # For CFLAGS/CXXFLAGS keep caller or compiler defaults # (including C/C++ revision) BUILD_TYPE=default-all-errors - BUILD_WARNFATAL=yes + #BUILD_WARNFATAL=yes + # configure => "yes" except for antique compilers + BUILD_WARNFATAL=auto # Current fightwarn goal is to have no warnings at preset level below, # or at the level defaulted with configure.ac (perhaps considering the # compiler version, etc.): #[ -n "$BUILD_WARNOPT" ] || BUILD_WARNOPT=hard - [ -n "$BUILD_WARNOPT" ] || BUILD_WARNOPT=medium + #[ -n "$BUILD_WARNOPT" ] || BUILD_WARNOPT=medium + # configure => default to medium, detect by compiler type + [ -n "$BUILD_WARNOPT" ] || BUILD_WARNOPT=auto # Eventually this constraint would be removed to check all present # SSL implementations since their ifdef-driven codebases differ and From 8028cd9980341ff090461c2e083c1c725247ae8a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 15:53:32 +0100 Subject: [PATCH 360/700] configure.ac: pre-set nut_enable_warnings=auto by default (not hardcoded "medium") --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 80800575a0..f6db6caf53 100644 --- a/configure.ac +++ b/configure.ac @@ -729,7 +729,7 @@ NUT_ARG_WITH([doc], [build and install documentation (see docs/configure.txt for dnl NOTE: Until X-Mas 2021, the default was "legacy" (now "medium") NUT_ARG_ENABLE([warnings], [enable warning presets that were picked as useful in maintainership and CI practice (variants include gcc-minimal, gcc-medium, gcc-hard, clang-minimal, clang-medium, clang-hard, all; auto-choosers: hard, medium, minimal, yes=auto='gcc or clang or all at hardcoded default difficulty')], - [medium]) + [auto]) NUT_ARG_ENABLE([Werror], [fail the build if compiler emits any warnings (treat them as errors)], [no]) From 7a970ed872129fa31dcbacb1397833bf7a10db5c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 15:54:17 +0100 Subject: [PATCH 361/700] configure.ac: report initial nut_enable_warnings (default or argument) --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index f6db6caf53..7b6588249e 100644 --- a/configure.ac +++ b/configure.ac @@ -2510,7 +2510,7 @@ dnl C89/C90/ANSI mode to be less noisy. Keep this in mind if changing the dnl default "nut_warning_difficulty" and/or the case handling below. dnl NOTE: Until X-Mas 2021, the default was "minimal" (now "medium") nut_warning_difficulty="medium" -AC_MSG_CHECKING([whether to pre-set warnings]) +AC_MSG_CHECKING([whether to pre-set warnings (from '${nut_enable_warnings}')]) AS_CASE(["${nut_enable_warnings}"], [no|all|gcc-legacy|gcc-minimal|clang-minimal|gcc-medium|clang-medium|gcc-hard|clang-hard], [], [clang], [nut_enable_warnings="${nut_enable_warnings}-${nut_warning_difficulty}"], From 7c1118c3ac0013ba64f5ec553b7eee270604354f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 23 Mar 2022 15:55:43 +0100 Subject: [PATCH 362/700] configure.ac: with nut_enable_warnings=auto, avoid fatal warnings with GCC 4.3 or older (no support for diags pragmas) --- configure.ac | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 7b6588249e..69b7a9105f 100644 --- a/configure.ac +++ b/configure.ac @@ -2524,7 +2524,14 @@ AS_CASE(["${nut_enable_warnings}"], [AS_IF([test "${GCC}" = "yes"], [ AS_CASE(["${CFLAGS}"], [*89*|*90*|*ansi*], [nut_enable_warnings="gcc-minimal"], - [nut_enable_warnings="gcc-${nut_warning_difficulty}"] + [AS_CASE(["`$CC --version | grep -i gcc`"], + [*" "1.*|*" "2.*|3.*|*" "4.0*|*" "4.1*|*" "4.2*|*" "4.3*], [ + AC_MSG_WARN([Very old GCC in use, disabling warnings]) + AS_IF([test x"${nut_enable_Werror}" = xauto], [nut_enable_Werror=no]) + nut_enable_warnings="no"], + [*" "4.4*|*" "4.5*|*" "4.6*|*" "4.7*|*" "4.8*], [nut_enable_warnings="gcc-legacy"], + [nut_enable_warnings="gcc-${nut_warning_difficulty}"] + )] )], [nut_enable_warnings="all"]) ]) ], From 9e5b99e4ea1173c7ecc7d4e10d90961e6276fd17 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Mar 2022 10:25:38 +0100 Subject: [PATCH 363/700] Jenkinsfile-dynamatrix: make use of dsbcStageTimeoutSettings --- Jenkinsfile-dynamatrix | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Jenkinsfile-dynamatrix b/Jenkinsfile-dynamatrix index 051cef1b84..f2f01eb9e6 100644 --- a/Jenkinsfile-dynamatrix +++ b/Jenkinsfile-dynamatrix @@ -58,6 +58,14 @@ import org.nut.dynamatrix.*; dynacfgPipeline.failFast = //true // false + // How long can a single "slow-build stage" run before we + // consider that the build agent is stuck or network dropped? + // The dynamatrix should try to re-schedule this scenario then. + dynacfgPipeline.dsbcStageTimeoutSettings = [ + time: 2, + unit: 'HOURS' + ] + // Note: this setting causes a lot of noise in build summary page and // parent job definition (PR, branch...) overview page on Jenkins, // by reporting dozens of lines for each analyzer ID ever published. From f2a333cd8305c889d2df374491ce4167d91257f2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Mar 2022 14:33:19 +0100 Subject: [PATCH 364/700] drivers/libhid.c: clang-3.4 does not know "-Wtautological-type-limit-compare" either --- drivers/libhid.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/libhid.c b/drivers/libhid.c index f06cbeadc7..cbc0d8e0ad 100644 --- a/drivers/libhid.c +++ b/drivers/libhid.c @@ -195,7 +195,6 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa #pragma clang diagnostic ignored "-Wunreachable-code" #pragma clang diagnostic ignored "-Wtautological-compare" #pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" -#pragma clang diagnostic ignored "-Wtautological-type-limit-compare" #endif if ((uintmax_t)r > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { upsdebugx(2, @@ -309,7 +308,6 @@ static int set_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t #pragma clang diagnostic ignored "-Wunreachable-code" #pragma clang diagnostic ignored "-Wtautological-compare" #pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" -#pragma clang diagnostic ignored "-Wtautological-type-limit-compare" #endif if ((uintmax_t)r > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { upsdebugx(2, @@ -575,7 +573,6 @@ char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_ #pragma clang diagnostic ignored "-Wunreachable-code" #pragma clang diagnostic ignored "-Wtautological-compare" #pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" -#pragma clang diagnostic ignored "-Wtautological-type-limit-compare" #endif if ((uintmax_t)Index > (uintmax_t)USB_CTRL_STRINDEX_MAX || (intmax_t)Index < (intmax_t)USB_CTRL_STRINDEX_MIN @@ -721,7 +718,6 @@ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize) #pragma clang diagnostic ignored "-Wunreachable-code" #pragma clang diagnostic ignored "-Wtautological-compare" #pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" -#pragma clang diagnostic ignored "-Wtautological-type-limit-compare" #endif if ((uintmax_t)r > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { /* FIXME: Should we try here, or plain abort? */ From a300a28fc0e81e15e7a630bac11af345292d0e21 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Mar 2022 14:43:22 +0100 Subject: [PATCH 365/700] configure.ac: for very old GCC where we are not in control of warnings, make them non-fatal always (for "auto" level) --- configure.ac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 69b7a9105f..dc705e2323 100644 --- a/configure.ac +++ b/configure.ac @@ -2527,7 +2527,8 @@ AS_CASE(["${nut_enable_warnings}"], [AS_CASE(["`$CC --version | grep -i gcc`"], [*" "1.*|*" "2.*|3.*|*" "4.0*|*" "4.1*|*" "4.2*|*" "4.3*], [ AC_MSG_WARN([Very old GCC in use, disabling warnings]) - AS_IF([test x"${nut_enable_Werror}" = xauto], [nut_enable_Werror=no]) + dnl #AS_IF([test x"${nut_enable_Werror}" = xauto], [nut_enable_Werror="no"]) + nut_enable_Werror="no" nut_enable_warnings="no"], [*" "4.4*|*" "4.5*|*" "4.6*|*" "4.7*|*" "4.8*], [nut_enable_warnings="gcc-legacy"], [nut_enable_warnings="gcc-${nut_warning_difficulty}"] From bfe4b7bbb51beccbb3a2193b632b03fcd41ece89 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 24 Mar 2022 15:10:50 +0100 Subject: [PATCH 366/700] Jenkinsfile-dynamatrix: avoid even running axisCombos_GCC_TOO_OLD where we require fatal warnings --- Jenkinsfile-dynamatrix | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/Jenkinsfile-dynamatrix b/Jenkinsfile-dynamatrix index 051cef1b84..bc507543cb 100644 --- a/Jenkinsfile-dynamatrix +++ b/Jenkinsfile-dynamatrix @@ -136,6 +136,9 @@ import org.nut.dynamatrix.*; dynacfgPipeline.axisCombos_ARCH32x64 = [~/BITS=32/, ~/ARCH_BITS=64/] dynacfgPipeline.axisCombos_ARCH64x32 = [~/BITS=64/, ~/ARCH_BITS=32/] + // Avoid requiring success on GCC so old we can't manage warnings by CLI or pragmas: + dynacfgPipeline.axisCombos_GCC_TOO_OLD = [~/COMPILER=GCC/, ~/GCCVER=([0123]\.|4\.[0123])/] + // Some (but not all) builds skip strict-C standard due to // current build failures with its requirements dynacfgPipeline.axisCombos_STRICT_C = [~/CSTDVARIANT=c/] @@ -405,7 +408,8 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + + [dynacfgPipeline.axisCombos_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -604,7 +608,8 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + + [dynacfgPipeline.axisCombos_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -641,7 +646,8 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesLabels': 'replace', 'commonLabelExpr': 'replace', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + + [dynacfgPipeline.axisCombos_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -679,7 +685,8 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesLabels': 'replace', 'commonLabelExpr': 'replace', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + + [dynacfgPipeline.axisCombos_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -752,7 +759,8 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + + [dynacfgPipeline.axisCombos_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -789,7 +797,8 @@ set | sort -n """ runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + - [dynacfgPipeline.axisCombos_COMPILER_NOT_GCC] + [dynacfgPipeline.axisCombos_COMPILER_NOT_GCC] + + [dynacfgPipeline.axisCombos_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -899,7 +908,8 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + + [dynacfgPipeline.axisCombos_GCC_TOO_OLD] //+ [ [~/COMPILER=GCC/, ~/CSTDVERSION_KEY=(?!89)/] ] ], body) }, // getParStages @@ -940,7 +950,8 @@ set | sort -n """ mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + [ [~/COMPILER=GCC/, ~/CSTDVERSION_KEY=(?!89)/] - ] + ] + + [dynacfgPipeline.axisCombos_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -978,7 +989,8 @@ set | sort -n """ mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + [ [~/COMPILER=(?!GCC)/] - ] + ] + + [dynacfgPipeline.axisCombos_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -1006,6 +1018,7 @@ set | sort -n """ ], allowedFailure: [ dynacfgPipeline.axisCombos_STRICT_C, + dynacfgPipeline.axisCombos_GCC_TOO_OLD, [~/BUILD_WARNOPT=hard/] ], runAllowedFailure: true, @@ -1044,6 +1057,7 @@ set | sort -n """ ], allowedFailure: [ dynacfgPipeline.axisCombos_STRICT_C, + dynacfgPipeline.axisCombos_GCC_TOO_OLD, [~/BUILD_WARNOPT=hard/] ], runAllowedFailure: true, @@ -1079,6 +1093,7 @@ set | sort -n """ ], allowedFailure: [ dynacfgPipeline.axisCombos_STRICT_C, + dynacfgPipeline.axisCombos_GCC_TOO_OLD, [~/BUILD_WARNOPT=hard/] ], runAllowedFailure: true, @@ -1124,6 +1139,7 @@ set | sort -n """ excludeCombos: [ dynacfgPipeline.axisCombos_ARCH32x64, dynacfgPipeline.axisCombos_ARCH64x32, + dynacfgPipeline.axisCombos_GCC_TOO_OLD, dynacfgPipeline.axisCombos_NOT_WINDOWS ] ], body) From 98fc7163f565e7adaaee32cfd12e03e58e481473 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 25 Mar 2022 10:55:11 +0100 Subject: [PATCH 367/700] Jenkinsfile-dynamatrix: fix scenarios with "non-fatal warnings" that had BUILD_WARNFATAL=yes anyway (copy-paste issue) --- Jenkinsfile-dynamatrix | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Jenkinsfile-dynamatrix b/Jenkinsfile-dynamatrix index bc507543cb..f3b6d835e3 100644 --- a/Jenkinsfile-dynamatrix +++ b/Jenkinsfile-dynamatrix @@ -751,7 +751,7 @@ set | sort -n """ dynamatrixAxesCommonEnv: [ ['LANG=C','LC_ALL=C','TZ=UTC', 'BUILD_TYPE=default-all-errors', - 'BUILD_WARNFATAL=yes','BUILD_WARNOPT=auto' + 'BUILD_WARNFATAL=no','BUILD_WARNOPT=auto' ] ], allowedFailure: [ @@ -759,8 +759,7 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + - [dynacfgPipeline.axisCombos_GCC_TOO_OLD] + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -788,7 +787,7 @@ set | sort -n """ dynamatrixAxesCommonEnv: [ ['LANG=C','LC_ALL=C','TZ=UTC', 'BUILD_TYPE=default-all-errors', - 'BUILD_WARNFATAL=yes','BUILD_WARNOPT=auto' + 'BUILD_WARNFATAL=no','BUILD_WARNOPT=auto' ] ], allowedFailure: [ @@ -797,8 +796,7 @@ set | sort -n """ runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + - [dynacfgPipeline.axisCombos_COMPILER_NOT_GCC] + - [dynacfgPipeline.axisCombos_GCC_TOO_OLD] + [dynacfgPipeline.axisCombos_COMPILER_NOT_GCC] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build From ffef1c4714f9c6fdb1cefe98d244ed41613b4984 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 25 Mar 2022 10:57:08 +0100 Subject: [PATCH 368/700] Jenkinsfile-dynamatrix: define axisCombos_COMPILER_GCC_TOO_OLD near other compiler-related combos --- Jenkinsfile-dynamatrix | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Jenkinsfile-dynamatrix b/Jenkinsfile-dynamatrix index f3b6d835e3..e2793cc7c2 100644 --- a/Jenkinsfile-dynamatrix +++ b/Jenkinsfile-dynamatrix @@ -32,6 +32,9 @@ import org.nut.dynamatrix.*; dynacfgPipeline.axisCombos_COMPILER_GCC = [~/COMPILER=GCC/] dynacfgPipeline.axisCombos_COMPILER_NOT_GCC = [~/COMPILER=(?!GCC)/] + // Avoid requiring success on GCC so old we can't manage warnings by CLI or pragmas: + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD = [~/COMPILER=GCC/, ~/GCCVER=([0123]\.|4\.[0123])/] + // Beside the flag here, the pre-defined C89/C90/ANSI scenarios // should only get considered in branches named ~/fightwarn.*89.*/ // or PRs to those (for non-GCC builds): @@ -136,9 +139,6 @@ import org.nut.dynamatrix.*; dynacfgPipeline.axisCombos_ARCH32x64 = [~/BITS=32/, ~/ARCH_BITS=64/] dynacfgPipeline.axisCombos_ARCH64x32 = [~/BITS=64/, ~/ARCH_BITS=32/] - // Avoid requiring success on GCC so old we can't manage warnings by CLI or pragmas: - dynacfgPipeline.axisCombos_GCC_TOO_OLD = [~/COMPILER=GCC/, ~/GCCVER=([0123]\.|4\.[0123])/] - // Some (but not all) builds skip strict-C standard due to // current build failures with its requirements dynacfgPipeline.axisCombos_STRICT_C = [~/CSTDVARIANT=c/] @@ -409,7 +409,7 @@ set | sort -n """ runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + - [dynacfgPipeline.axisCombos_GCC_TOO_OLD] + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -609,7 +609,7 @@ set | sort -n """ runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + - [dynacfgPipeline.axisCombos_GCC_TOO_OLD] + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -647,7 +647,7 @@ set | sort -n """ runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesLabels': 'replace', 'commonLabelExpr': 'replace', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + - [dynacfgPipeline.axisCombos_GCC_TOO_OLD] + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -686,7 +686,7 @@ set | sort -n """ runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesLabels': 'replace', 'commonLabelExpr': 'replace', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + - [dynacfgPipeline.axisCombos_GCC_TOO_OLD] + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -907,7 +907,7 @@ set | sort -n """ runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + - [dynacfgPipeline.axisCombos_GCC_TOO_OLD] + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] //+ [ [~/COMPILER=GCC/, ~/CSTDVERSION_KEY=(?!89)/] ] ], body) }, // getParStages @@ -949,7 +949,7 @@ set | sort -n """ excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + [ [~/COMPILER=GCC/, ~/CSTDVERSION_KEY=(?!89)/] ] + - [dynacfgPipeline.axisCombos_GCC_TOO_OLD] + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -988,7 +988,7 @@ set | sort -n """ excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + [ [~/COMPILER=(?!GCC)/] ] + - [dynacfgPipeline.axisCombos_GCC_TOO_OLD] + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build @@ -1016,7 +1016,7 @@ set | sort -n """ ], allowedFailure: [ dynacfgPipeline.axisCombos_STRICT_C, - dynacfgPipeline.axisCombos_GCC_TOO_OLD, + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD, [~/BUILD_WARNOPT=hard/] ], runAllowedFailure: true, @@ -1055,7 +1055,7 @@ set | sort -n """ ], allowedFailure: [ dynacfgPipeline.axisCombos_STRICT_C, - dynacfgPipeline.axisCombos_GCC_TOO_OLD, + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD, [~/BUILD_WARNOPT=hard/] ], runAllowedFailure: true, @@ -1091,7 +1091,7 @@ set | sort -n """ ], allowedFailure: [ dynacfgPipeline.axisCombos_STRICT_C, - dynacfgPipeline.axisCombos_GCC_TOO_OLD, + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD, [~/BUILD_WARNOPT=hard/] ], runAllowedFailure: true, @@ -1137,7 +1137,7 @@ set | sort -n """ excludeCombos: [ dynacfgPipeline.axisCombos_ARCH32x64, dynacfgPipeline.axisCombos_ARCH64x32, - dynacfgPipeline.axisCombos_GCC_TOO_OLD, + dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD, dynacfgPipeline.axisCombos_NOT_WINDOWS ] ], body) From 3209b31b7016c4ae2cb5574a6db5b234a6db74b4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 25 Mar 2022 12:14:45 +0100 Subject: [PATCH 369/700] m4/ax_check_compile_flag.m4: extend to check conftest.err log about unsupported flags --- m4/ax_check_compile_flag.m4 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 index bd753b34d7..a72fc05246 100644 --- a/m4/ax_check_compile_flag.m4 +++ b/m4/ax_check_compile_flag.m4 @@ -24,10 +24,13 @@ # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. # +# NOTE: This implementation was extended with check for compiler complaints +# # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans +# Copyright (c) 2022 Jim Klimov # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice @@ -43,7 +46,12 @@ AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], - [AS_VAR_SET(CACHEVAR,[yes])], + [dnl Toolkit per se did not return an error code; but did it complain? + dnl Below are a few strings typical for some versions of GCC and CLANG + dnl This relies on AC_COMPILE_IFELSE implementation retaining conftest.err + AS_IF([grep -E '(unrecognized.* option|did you mean|unknown argument:)' < conftest.err >/dev/null 2>/dev/null], + [AS_VAR_SET(CACHEVAR,[no])],dnl Hit a complaint, flag is not supported after all + [AS_VAR_SET(CACHEVAR,[yes])])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) AS_VAR_IF(CACHEVAR,yes, From cbec184bb191c3232a454e077b5e35f3e8c2484a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 25 Mar 2022 12:50:48 +0100 Subject: [PATCH 370/700] m4/nut_compiler_family.m4: avoid configure noise message --- m4/nut_compiler_family.m4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/m4/nut_compiler_family.m4 b/m4/nut_compiler_family.m4 index b6762bd7d5..a9280942ee 100644 --- a/m4/nut_compiler_family.m4 +++ b/m4/nut_compiler_family.m4 @@ -61,7 +61,10 @@ AC_DEFUN([NUT_COMPILER_FAMILY], AC_DEFUN([NUT_CHECK_COMPILE_FLAG], [ - AC_REQUIRE([AX_RUN_OR_LINK_IFELSE])dnl +dnl Note: with this line uncommented, builds report +dnl sed: 0: conftest.c: No such file or directory +dnl so seemingly try to parse the method without args: + dnl### AC_REQUIRE([AX_RUN_OR_LINK_IFELSE]) dnl Note: per https://stackoverflow.com/questions/52557417/how-to-check-support-compile-flag-in-autoconf-for-clang dnl the -Werror below is needed to detect "warnings" about unsupported options From b47f962ebb2cd1196e87fe87c6c1ab561edf6658 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 25 Mar 2022 21:50:42 +0100 Subject: [PATCH 371/700] Jenkinsfile-dynamatrix: avoid failure-prone build toolkits for manpage tests --- Jenkinsfile-dynamatrix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile-dynamatrix b/Jenkinsfile-dynamatrix index e2793cc7c2..aadc9bdde6 100644 --- a/Jenkinsfile-dynamatrix +++ b/Jenkinsfile-dynamatrix @@ -724,7 +724,8 @@ set | sort -n """ ], runAllowedFailure: true, mergeMode: [ 'excludeCombos': 'merge', 'dynamatrixAxesLabels': 'replace', 'commonLabelExpr': 'replace', 'dynamatrixAxesCommonEnv': 'replace' ], // NOTE: We might want to replace other fields, but excludeCombos must be merged to filter compiler versions vs language standards as centrally defined! - excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + excludeCombos: dynacfgPipeline.excludeCombos_DEFAULT_CPPUNIT_STRICT_C + + [dynacfgPipeline.axisCombos_COMPILER_GCC_TOO_OLD] ], body) }, // getParStages 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build From 42c84e97c2ae21fbafe713322bfc118611cac131 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 25 Mar 2022 22:04:37 +0100 Subject: [PATCH 372/700] docs/config-prereqs.txt: update openbsd tools for man-page builds --- docs/config-prereqs.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/config-prereqs.txt b/docs/config-prereqs.txt index 4e3b4a3c66..752738e4c3 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -405,7 +405,8 @@ networked repository (4.2.x vs 4.9.x) # For other doc types (man-page, PDF, HTML) generation - massive packages (TEX, X11): :; pkg_add \ - asciidoc source-highlight py-pygments dblatex + asciidoc source-highlight py-pygments dblatex \ + docbook2x docbook-to-man # For CGI graph generation - massive packages (X11): :; pkg_add \ From 2f9973898f651a15f493e52f4b575233d491e18a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 26 Mar 2022 14:28:09 +0100 Subject: [PATCH 373/700] ci_build.sh: default to clang if also available when default gcc is too old --- ci_build.sh | 70 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/ci_build.sh b/ci_build.sh index 7c36133030..4597233c3f 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -536,11 +536,25 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp EXTRA_CXXFLAGS="" is_gnucc() { - if [ -n "$1" ] && "$1" --version 2>&1 | grep 'Free Software Foundation' > /dev/null ; then true ; else false ; fi + if [ -n "$1" ] && LANG=C "$1" --version 2>&1 | grep 'Free Software Foundation' > /dev/null ; then true ; else false ; fi } is_clang() { - if [ -n "$1" ] && "$1" --version 2>&1 | grep 'clang version' > /dev/null ; then true ; else false ; fi + if [ -n "$1" ] && LANG=C "$1" --version 2>&1 | grep 'clang version' > /dev/null ; then true ; else false ; fi + } + + filter_version() { + # Starting with number like "6.0.0" or "7.5.0-il-0" is fair game, + # but a "gcc-4.4.4-il-4" (starting with "gcc") is not + sed -e 's,^.* \([0-9][0-9]*\.[0-9][^ ),]*\).*$,\1,' -e 's, .*$,,' | grep -E '^[0-9]' | head -1 + } + + ver_gnucc() { + [ -n "$1" ] && LANG=C "$1" --version 2>&1 | grep -i gcc | filter_version + } + + ver_clang() { + [ -n "$1" ] && LANG=C "$1" --version 2>&1 | grep -i 'clang' | filter_version } COMPILER_FAMILY="" @@ -553,6 +567,8 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp export CC CXX fi else + # Generally we prefer GCC unless it is very old so we can't impact + # its warnings and complaints. if is_gnucc "gcc" && is_gnucc "g++" ; then # Autoconf would pick this by default COMPILER_FAMILY="GCC" @@ -564,17 +580,45 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp [ -n "$CC" ] || CC=cc [ -n "$CXX" ] || CXX=c++ export CC CXX - elif is_clang "clang" && is_clang "clang++" ; then - # Autoconf would pick this by default - COMPILER_FAMILY="CLANG" - [ -n "$CC" ] || CC=clang - [ -n "$CXX" ] || CXX=clang++ - export CC CXX - elif is_clang "cc" && is_clang "c++" ; then - COMPILER_FAMILY="CLANG" - [ -n "$CC" ] || CC=cc - [ -n "$CXX" ] || CXX=c++ - export CC CXX + fi + + if ( [ "$COMPILER_FAMILY" = "GCC" ] && \ + case "`ver_gnucc "$CC"`" in + [123].*) true ;; + 4.[0123][.,-]*) true ;; + 4.[0123]) true ;; + *) false ;; + esac && \ + case "`ver_gnucc "$CXX"`" in + [123].*) true ;; + 4.[0123][.,-]*) true ;; + 4.[0123]) true ;; + *) false ;; + esac + ) ; then + echo "NOTE: default GCC here is very old, do we have a CLANG instead?.." >&2 + COMPILER_FAMILY="GCC_OLD" + fi + + if [ -z "$COMPILER_FAMILY" ] || [ "$COMPILER_FAMILY" = "GCC_OLD" ]; then + if is_clang "clang" && is_clang "clang++" ; then + # Autoconf would pick this by default + [ "$COMPILER_FAMILY" = "GCC_OLD" ] && CC="" && CXX="" + COMPILER_FAMILY="CLANG" + [ -n "$CC" ] || CC=clang + [ -n "$CXX" ] || CXX=clang++ + export CC CXX + elif is_clang "cc" && is_clang "c++" ; then + [ "$COMPILER_FAMILY" = "GCC_OLD" ] && CC="" && CXX="" + COMPILER_FAMILY="CLANG" + [ -n "$CC" ] || CC=cc + [ -n "$CXX" ] || CXX=c++ + export CC CXX + fi + fi + + if [ "$COMPILER_FAMILY" = "GCC_OLD" ]; then + COMPILER_FAMILY="GCC" fi fi From 0ae8f0f97c3daeeb2d924170205915f9d725a443 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 28 Mar 2022 15:11:18 +0200 Subject: [PATCH 374/700] drivers/usbhid-ups.c: fix format string for "onlinedischarge" help --- drivers/usbhid-ups.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index d0fb572796..54a2d4dfc2 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -797,7 +797,8 @@ void upsdrv_makevartable(void) addvar(VAR_FLAG, "pollonly", "Don't use interrupt pipe, only use polling"); - snprintf(temp, sizeof(temp), "Treat discharging while online as being offline (default=%s)", + snprintf(temp, sizeof(temp), + "Treat discharging while online as being offline (default=%d)", DEFAULT_ONLINEDISCHARGE); addvar(VAR_FLAG, "onlinedischarge", temp); From 4bdccb163ee796deed71b431509f7f910a851491 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 28 Mar 2022 15:27:16 +0200 Subject: [PATCH 375/700] drivers/usbhid-ups.c: fix var usage for upsname --- drivers/usbhid-ups.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 54a2d4dfc2..7b4b00facd 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -1644,7 +1644,7 @@ static void ups_status_set(void) upslogx(LOG_WARNING, "%s: seems that UPS [%s] is in OL+DISCHRG state now. " "Is it calibrating or do you perhaps want to set 'onlinedischarge' option? " "Some UPS models (e.g. CyberPower UT series) emit OL+DISCHRG when offline.", - __func__, ups->upsname) + __func__, upsname); } /* if we're calibrating */ status_set("OL"); /* on line */ From 1ea3a3bcd49965829126bd21deead470dcf67736 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 28 Mar 2022 15:32:56 +0200 Subject: [PATCH 376/700] docs/man/usbhid-ups.txt: document onlinedischarge --- docs/man/usbhid-ups.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/man/usbhid-ups.txt b/docs/man/usbhid-ups.txt index 0677b6784a..e38d040ee8 100644 --- a/docs/man/usbhid-ups.txt +++ b/docs/man/usbhid-ups.txt @@ -89,6 +89,12 @@ If this flag is set, the driver will not use Interrupt In transfers during the shorter "pollinterval" cycles (not recommended, but needed if these reports are broken on your UPS). +*onlinedischarge*:: +If this flag is set, the driver will treat `OL+DISCHRG` status as offline. +For most devices this combination means calibration or similar maintenance; +however some UPS models (e.g. CyberPower UT series) emit `OL+DISCHRG` when +wall power is lost -- and need this option to handle shutdowns. + *vendor*='regex':: *product*='regex':: *serial*='regex':: From daee74b40286a7eecbf4ae4525bd95ce5574674c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 28 Mar 2022 15:34:37 +0200 Subject: [PATCH 377/700] NEWS: added usbhid-ups onlinedischarge for NUT v2.7.5 --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 93b4251ab2..c7ca4aba45 100644 --- a/NEWS +++ b/NEWS @@ -169,6 +169,8 @@ Release notes for NUT 2.8.0 - what's new since 2.7.4: subdriver rather than polluting the main code with UPS specific exceptions, and applied fixes for known mistakes in (some releases of firmware for) CyberPower CPS*EPFCLCD [issue #439, PR #1245] + * added `onlinedischarge` option for UPSes that report `OL+DISCHRG` + when wall power is lost [PR #811] * CPS HID: add input.frequency and output.frequency * OpenUPS2: only check OEM Information string once (fewer log messages) * Liebert GXT4 USB VID:PID [10AF:0000] From b6cab69fde5c1f41b177ab6ea481233a949d8450 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 28 Mar 2022 15:37:09 +0200 Subject: [PATCH 378/700] NEWS: clarify that NUT 2.8.0 is new name for old planned NUT 2.7.5 --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index c7ca4aba45..63b93c7754 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,9 @@ control history for "live" codebase. --------------------------------------------------------------------------- Release notes for NUT 2.8.0 - what's new since 2.7.4: +NOTE: Earlier discussions (mailing list threads, GitHub issues, etc.) could +refer to this change set (too long in the making) as NUT 2.7.5. + - New (optional) keywords for configuration files were added, so existing NUT 2.7.x builds would not accept them if some deployments switch versions back and forth -- due to this, From 5e707e47012abb1407349dd93d7fabb7965b9ba3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 28 Mar 2022 15:38:16 +0200 Subject: [PATCH 379/700] docs/nut.dict: add onlinedischarge --- docs/nut.dict | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/nut.dict b/docs/nut.dict index 00edc9775f..4917da8bab 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2868 utf-8 +personal_ws-1.1 en 2869 utf-8 AAS ACFAIL ACFREQ @@ -2255,6 +2255,7 @@ onclick ondelay oneac online +onlinedischarge ont ontd ontimedays From ba9220c3c046b693efc6887d7f0f41410fd452e9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 28 Mar 2022 15:45:31 +0200 Subject: [PATCH 380/700] drivers/usbhid-ups.c: drop DEFAULT_ONLINEDISCHARGE to match "VAR_FLAG" semantics --- drivers/usbhid-ups.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 7b4b00facd..973263a4ef 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -152,10 +152,9 @@ hid_dev_handle_t udev = HID_DEV_HANDLE_CLOSED; /** * CyberPower UT series sometime need a bit of help deciding their online status. * This quirk is to enable the special handling of OL & DISCHRG at the same time - * as being OB (on battery power/no mains power) + * as being OB (on battery power/no mains power). Enabled by device config flag. */ -#define DEFAULT_ONLINEDISCHARGE 0 -static int onlinedischarge = DEFAULT_ONLINEDISCHARGE; +static int onlinedischarge = 0; /* support functions */ static hid_info_t *find_nut_info(const char *varname); @@ -797,10 +796,8 @@ void upsdrv_makevartable(void) addvar(VAR_FLAG, "pollonly", "Don't use interrupt pipe, only use polling"); - snprintf(temp, sizeof(temp), - "Treat discharging while online as being offline (default=%d)", - DEFAULT_ONLINEDISCHARGE); - addvar(VAR_FLAG, "onlinedischarge", temp); + addvar(VAR_FLAG, "onlinedischarge", + "Set to treat discharging while online as being offline"); #ifndef SHUT_MODE /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ From 50775cf6eccde64d94992da290e132d944c99507 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 28 Mar 2022 15:49:54 +0200 Subject: [PATCH 381/700] drivers/usbhid-ups.c: set the onlinedischarge variable based on flag presence --- drivers/usbhid-ups.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 973263a4ef..02905cea2f 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -1072,6 +1072,12 @@ void upsdrv_initups(void) if (testvar("interruptonly")) { interrupt_only = 1; } + + /* Activate Cyberpower tweaks */ + if (testvar("onlinedischarge")) { + onlinedischarge = 1; + } + val = getval("interruptsize"); if (val) { int ipv = atoi(val); From 77b9b8e9ad37caef1e0e3149cd382e0a97a22f7a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 28 Mar 2022 15:53:20 +0200 Subject: [PATCH 382/700] ci_build.sh: do not make noise about non-ubiquitous options to "uname" program --- ci_build.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ci_build.sh b/ci_build.sh index 4597233c3f..8a6f97e669 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -194,9 +194,10 @@ if [ -z "$CI_OS_NAME" ]; then "$OS_FAMILY-$OS_DISTRO" \ "`grep = /etc/os-release 2>/dev/null`" \ "`cat /etc/release 2>/dev/null`" \ - "`uname -o`" \ - "`uname -s -r -v`" \ + "`uname -o 2>/dev/null`" \ + "`uname -s -r -v 2>/dev/null`" \ "`uname -a`" \ + "`uname`" \ ; do [ -z "$CI_OS_HINT" -o "$CI_OS_HINT" = "-" ] || break done From 7efa550e7d1a9009be5a74973606598f0cdd6c04 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 28 Mar 2022 15:55:12 +0200 Subject: [PATCH 383/700] ci_build.sh: recognize more "*bsd" CI_OS_NAME values --- ci_build.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ci_build.sh b/ci_build.sh index 8a6f97e669..ef17d9dd46 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -205,6 +205,10 @@ if [ -z "$CI_OS_NAME" ]; then case "`echo "$CI_OS_HINT" | tr 'A-Z' 'a-z'`" in *freebsd*) CI_OS_NAME="freebsd" ;; + *openbsd*) + CI_OS_NAME="openbsd" ;; + *netbsd*) + CI_OS_NAME="netbsd" ;; *debian*|*ubuntu*) CI_OS_NAME="debian" ;; *centos*|*fedora*|*redhat*|*rhel*) From 8b72ac9fc23e1501f76028461fb58c28031dd1ef Mon Sep 17 00:00:00 2001 From: Benjamin Berg Date: Mon, 28 Mar 2022 17:19:41 +0200 Subject: [PATCH 384/700] tools/nut-usbinfo.pl: Use hwdb for UPower rules --- scripts/upower/95-upower-hid.hwdb | 200 +++++++++++++++++++++++++++++ scripts/upower/95-upower-hid.rules | 194 +--------------------------- tools/nut-usbinfo.pl | 49 +++---- 3 files changed, 218 insertions(+), 225 deletions(-) create mode 100644 scripts/upower/95-upower-hid.hwdb diff --git a/scripts/upower/95-upower-hid.hwdb b/scripts/upower/95-upower-hid.hwdb new file mode 100644 index 0000000000..a4eff0f58c --- /dev/null +++ b/scripts/upower/95-upower-hid.hwdb @@ -0,0 +1,200 @@ +############################################################################################################## +# Uninterruptible Power Supplies with USB HID interfaces +# +# This file was automatically generated by NUT: +# https://github.com/networkupstools/nut/ +# +# To keep up to date, monitor upstream NUT +# https://github.com/networkupstools/nut/commits/master/scripts/upower/95-upower-hid.hwdb +# or checkout the NUT repository and call 'tools/nut-usbinfo.pl' + +# Hewlett Packard +usb:v03F0p0001* +usb:v03F0p1F06* +usb:v03F0p1F08* +usb:v03F0p1F09* +usb:v03F0p1F0A* +usb:v03F0p1FE0* +usb:v03F0p1FE1* +usb:v03F0p1FE2* +usb:v03F0p1FE3* +usb:v03F0p1FE5* +usb:v03F0p1FE6* +usb:v03F0p1FE7* +usb:v03F0p1FE8* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Hewlett Packard + +# Eaton +usb:v0463p0001* +usb:v0463pFFFF* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Eaton + +# Dell +usb:v047CpFFFF* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Dell + +# ST Microelectronics +usb:v0483pA113* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=ST Microelectronics + +# IBM +usb:v04B3p0001* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=IBM + +# Minibox +usb:v04D8pD004* +usb:v04D8pD005* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Minibox + +# Belkin +usb:v050Dp0375* +usb:v050Dp0551* +usb:v050Dp0750* +usb:v050Dp0751* +usb:v050Dp0900* +usb:v050Dp0910* +usb:v050Dp0912* +usb:v050Dp0980* +usb:v050Dp0F51* +usb:v050Dp1100* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Belkin + +# APC +usb:v051Dp0000* +usb:v051Dp0002* +usb:v051Dp0003* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=APC + +# Powerware +usb:v0592p0004* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Powerware + +# Delta UPS +usb:v05DDp041B* +usb:v05DDpA011* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Delta UPS + +# Phoenixtec Power Co., Ltd +usb:v06DApFFFF* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Phoenixtec Power Co., Ltd + +# iDowell +usb:v075Dp0300* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=iDowell + +# Cyber Power Systems +usb:v0764p0005* +usb:v0764p0501* +usb:v0764p0601* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Cyber Power Systems + +# TrippLite +usb:v09AEp1003* +usb:v09AEp1007* +usb:v09AEp1008* +usb:v09AEp1009* +usb:v09AEp1010* +usb:v09AEp1330* +usb:v09AEp2005* +usb:v09AEp2007* +usb:v09AEp2008* +usb:v09AEp2009* +usb:v09AEp2010* +usb:v09AEp2011* +usb:v09AEp2012* +usb:v09AEp2013* +usb:v09AEp2014* +usb:v09AEp3008* +usb:v09AEp3009* +usb:v09AEp3010* +usb:v09AEp3011* +usb:v09AEp3012* +usb:v09AEp3013* +usb:v09AEp3014* +usb:v09AEp3015* +usb:v09AEp3016* +usb:v09AEp3024* +usb:v09AEp4001* +usb:v09AEp4002* +usb:v09AEp4003* +usb:v09AEp4004* +usb:v09AEp4005* +usb:v09AEp4006* +usb:v09AEp4007* +usb:v09AEp4008* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=TrippLite + +# PowerCOM +usb:v0D9Fp0001* +usb:v0D9Fp0004* +usb:v0D9Fp00A2* +usb:v0D9Fp00A3* +usb:v0D9Fp00A4* +usb:v0D9Fp00A5* +usb:v0D9Fp00A6* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=PowerCOM + +# Liebert +usb:v10AFp0001* +usb:v10AFp0004* +usb:v10AFp0008* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Liebert + +# Legrand +usb:v1CB0p0032* +usb:v1CB0p0038* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Legrand + +# Arduino +usb:v2341p0036* +usb:v2341p8036* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Arduino + +# Arduino +usb:v2A03p0036* +usb:v2A03p0040* +usb:v2A03p8036* +usb:v2A03p8040* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Arduino + +# AEG +usb:v2B2DpFFFF* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=AEG + +# Ever +usb:v2E51pFFFF* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Ever + +# Salicru +usb:v2E66p0201* +usb:v2E66p0202* +usb:v2E66p0203* +usb:v2E66p0300* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Salicru + +# Powervar +usb:v4234p0002* + UPOWER_BATTERY_TYPE=ups + UPOWER_VENDOR=Powervar diff --git a/scripts/upower/95-upower-hid.rules b/scripts/upower/95-upower-hid.rules index e709b7e15c..c6e2d06800 100644 --- a/scripts/upower/95-upower-hid.rules +++ b/scripts/upower/95-upower-hid.rules @@ -1,192 +1,2 @@ -############################################################################################################## -# Uninterruptible Power Supplies with USB HID interfaces -# -# This file was automatically generated by NUT: -# https://github.com/networkupstools/nut/ -# -# To keep up to date, monitor upstream NUT -# https://github.com/networkupstools/nut/commits/master/scripts/upower/95-upower-hid.rules -# or checkout the NUT repository and call 'tools/nut-usbinfo.pl' - -# newer hiddev are part of the usbmisc class -SUBSYSTEM=="usbmisc", GOTO="up_hid_chkdev" -# only support USB, else ignore -SUBSYSTEM!="usb", GOTO="up_hid_end" - -# if usbraw device, ignore -LABEL="up_hid_chkdev" -KERNEL!="hiddev*", GOTO="up_hid_end" - -# if an interface, ignore -ENV{DEVTYPE}=="usb_interface", GOTO="up_hid_end" - -ATTRS{idVendor}=="03f0", ENV{UPOWER_VENDOR}="Hewlett Packard" -ATTRS{idVendor}=="0463", ENV{UPOWER_VENDOR}="Eaton" -ATTRS{idVendor}=="047c", ENV{UPOWER_VENDOR}="Dell" -ATTRS{idVendor}=="0483", ENV{UPOWER_VENDOR}="ST Microelectronics" -ATTRS{idVendor}=="04b3", ENV{UPOWER_VENDOR}="IBM" -ATTRS{idVendor}=="04d8", ENV{UPOWER_VENDOR}="Minibox" -ATTRS{idVendor}=="050d", ENV{UPOWER_VENDOR}="Belkin" -ATTRS{idVendor}=="051d", ENV{UPOWER_VENDOR}="APC" -ATTRS{idVendor}=="0592", ENV{UPOWER_VENDOR}="Powerware" -ATTRS{idVendor}=="05dd", ENV{UPOWER_VENDOR}="Delta UPS" -ATTRS{idVendor}=="06da", ENV{UPOWER_VENDOR}="Phoenixtec Power Co., Ltd" -ATTRS{idVendor}=="075d", ENV{UPOWER_VENDOR}="iDowell" -ATTRS{idVendor}=="0764", ENV{UPOWER_VENDOR}="Cyber Power Systems" -ATTRS{idVendor}=="09ae", ENV{UPOWER_VENDOR}="TrippLite" -ATTRS{idVendor}=="0d9f", ENV{UPOWER_VENDOR}="PowerCOM" -ATTRS{idVendor}=="10af", ENV{UPOWER_VENDOR}="Liebert" -ATTRS{idVendor}=="1cb0", ENV{UPOWER_VENDOR}="Legrand" -ATTRS{idVendor}=="2341", ENV{UPOWER_VENDOR}="Arduino" -ATTRS{idVendor}=="2A03", ENV{UPOWER_VENDOR}="Arduino" -ATTRS{idVendor}=="2b2d", ENV{UPOWER_VENDOR}="AEG" -ATTRS{idVendor}=="2e51", ENV{UPOWER_VENDOR}="Ever" -ATTRS{idVendor}=="2e66", ENV{UPOWER_VENDOR}="Salicru" -ATTRS{idVendor}=="4234", ENV{UPOWER_VENDOR}="Powervar" - -# Hewlett Packard -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="0001", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1f06", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1f08", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1f09", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1f0a", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe0", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe1", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe2", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe3", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe5", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe6", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe7", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe8", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Eaton -ATTRS{idVendor}=="0463", ATTRS{idProduct}=="0001", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0463", ATTRS{idProduct}=="ffff", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Dell -ATTRS{idVendor}=="047c", ATTRS{idProduct}=="ffff", ENV{UPOWER_BATTERY_TYPE}="ups" - -# ST Microelectronics -ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a113", ENV{UPOWER_BATTERY_TYPE}="ups" - -# IBM -ATTRS{idVendor}=="04b3", ATTRS{idProduct}=="0001", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Minibox -ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="d004", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="d005", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Belkin -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0375", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0551", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0750", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0751", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0900", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0910", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0912", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0980", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0f51", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="1100", ENV{UPOWER_BATTERY_TYPE}="ups" - -# APC -ATTRS{idVendor}=="051d", ATTRS{idProduct}=="0000", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="051d", ATTRS{idProduct}=="0002", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="051d", ATTRS{idProduct}=="0003", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Powerware -ATTRS{idVendor}=="0592", ATTRS{idProduct}=="0004", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Delta UPS -ATTRS{idVendor}=="05dd", ATTRS{idProduct}=="041b", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="05dd", ATTRS{idProduct}=="a011", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Phoenixtec Power Co., Ltd -ATTRS{idVendor}=="06da", ATTRS{idProduct}=="ffff", ENV{UPOWER_BATTERY_TYPE}="ups" - -# iDowell -ATTRS{idVendor}=="075d", ATTRS{idProduct}=="0300", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Cyber Power Systems -ATTRS{idVendor}=="0764", ATTRS{idProduct}=="0005", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0764", ATTRS{idProduct}=="0501", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0764", ATTRS{idProduct}=="0601", ENV{UPOWER_BATTERY_TYPE}="ups" - -# TrippLite -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="1003", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="1007", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="1008", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="1009", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="1010", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="1330", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2005", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2007", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2008", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2009", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2010", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2011", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2012", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2013", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2014", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3008", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3009", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3010", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3011", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3012", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3013", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3014", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3015", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3016", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3024", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4001", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4002", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4003", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4004", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4005", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4006", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4007", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4008", ENV{UPOWER_BATTERY_TYPE}="ups" - -# PowerCOM -ATTRS{idVendor}=="0d9f", ATTRS{idProduct}=="0001", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0d9f", ATTRS{idProduct}=="0004", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0d9f", ATTRS{idProduct}=="00a2", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0d9f", ATTRS{idProduct}=="00a3", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0d9f", ATTRS{idProduct}=="00a4", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0d9f", ATTRS{idProduct}=="00a5", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0d9f", ATTRS{idProduct}=="00a6", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Liebert -ATTRS{idVendor}=="10af", ATTRS{idProduct}=="0001", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="10af", ATTRS{idProduct}=="0004", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="10af", ATTRS{idProduct}=="0008", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Legrand -ATTRS{idVendor}=="1cb0", ATTRS{idProduct}=="0032", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="1cb0", ATTRS{idProduct}=="0038", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Arduino -ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0036", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2341", ATTRS{idProduct}=="8036", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Arduino -ATTRS{idVendor}=="2A03", ATTRS{idProduct}=="0036", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2A03", ATTRS{idProduct}=="0040", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2A03", ATTRS{idProduct}=="8036", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2A03", ATTRS{idProduct}=="8040", ENV{UPOWER_BATTERY_TYPE}="ups" - -# AEG -ATTRS{idVendor}=="2b2d", ATTRS{idProduct}=="ffff", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Ever -ATTRS{idVendor}=="2e51", ATTRS{idProduct}=="ffff", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Salicru -ATTRS{idVendor}=="2e66", ATTRS{idProduct}=="0201", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2e66", ATTRS{idProduct}=="0202", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2e66", ATTRS{idProduct}=="0203", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="2e66", ATTRS{idProduct}=="0300", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Powervar -ATTRS{idVendor}=="4234", ATTRS{idProduct}=="0002", ENV{UPOWER_BATTERY_TYPE}="ups" - -LABEL="up_hid_end" +# Copy some attributes from the USB device to the hiddev device +SUBSYSTEM=="usbmisc", SUBSYSTEMS=="usb", KERNEL=="hiddev*", IMPORT{parent}="UPOWER_*", IMPORT{parent}="ID_VENDOR", IMPORT{parent}="ID_PRODUCT" diff --git a/tools/nut-usbinfo.pl b/tools/nut-usbinfo.pl index 63cd074484..9fd07a1bd2 100755 --- a/tools/nut-usbinfo.pl +++ b/tools/nut-usbinfo.pl @@ -52,12 +52,7 @@ my $output_devd="$TOP_BUILDDIR/scripts/devd/nut-usb.conf.in"; # UPower output file -my $outputUPower="$TOP_BUILDDIR/scripts/upower/95-upower-hid.rules"; - -# tmp output, to allow generating the ENV{UPOWER_VENDOR} header list -my $tmpOutputUPower; -# mfr header flag -my $upowerMfrHeaderDone = 0; +my $outputUPower="$TOP_BUILDDIR/scripts/upower/95-upower-hid.hwdb"; # NUT device scanner - C header my $outputDevScanner = "$TOP_BUILDDIR/tools/nut-scanner/nutscan-usb.h"; @@ -125,17 +120,8 @@ sub gen_usb_files print $outputUPower '# This file was automatically generated by NUT:'."\n"; print $outputUPower '# https://github.com/networkupstools/nut/'."\n#\n"; print $outputUPower '# To keep up to date, monitor upstream NUT'."\n"; - print $outputUPower '# https://github.com/networkupstools/nut/commits/master/scripts/upower/95-upower-hid.rules'."\n"; - print $outputUPower "# or checkout the NUT repository and call 'tools/nut-usbinfo.pl'\n\n"; - print $outputUPower '# newer hiddev are part of the usbmisc class'."\n"; - print $outputUPower 'SUBSYSTEM=="usbmisc", GOTO="up_hid_chkdev"'."\n"; - print $outputUPower '# only support USB, else ignore'."\n"; - print $outputUPower 'SUBSYSTEM!="usb", GOTO="up_hid_end"'."\n\n"; - print $outputUPower '# if usbraw device, ignore'."\n"; - print $outputUPower 'LABEL="up_hid_chkdev"'."\n"; - print $outputUPower 'KERNEL!="hiddev*", GOTO="up_hid_end"'."\n\n"; - print $outputUPower '# if an interface, ignore'."\n"; - print $outputUPower 'ENV{DEVTYPE}=="usb_interface", GOTO="up_hid_end"'."\n\n"; + print $outputUPower '# https://github.com/networkupstools/nut/commits/master/scripts/upower/95-upower-hid.hwdb'."\n"; + print $outputUPower "# or checkout the NUT repository and call 'tools/nut-usbinfo.pl'\n"; # Device scanner header open my $outputDevScanner, ">$outputDevScanner" || die "error $outputDevScanner : $!"; @@ -183,7 +169,7 @@ sub gen_usb_files # UPower vendor header flag - $upowerMfrHeaderDone = 0; + my $upowerVendorHasDevices = 0; foreach my $productId (sort { lc $a cmp lc $b } keys %{$vendor{$vendorId}}) { @@ -212,33 +198,30 @@ sub gen_usb_files # UPower device entry (only for USB/HID devices!) if ($vendor{$vendorId}{$productId}{"driver"} eq "usbhid-ups") { - if ($upowerMfrHeaderDone == 0) - { - # UPower vendor header + if (!$upowerVendorHasDevices) { if ($vendorName{$vendorId}) { - $tmpOutputUPower = $tmpOutputUPower."\n# ".$vendorName{$vendorId}."\n"; + print $outputUPower "\n# ".$vendorName{$vendorId}."\n"; } - print $outputUPower "ATTRS{idVendor}==\"".removeHexPrefix($vendorId)."\", ENV{UPOWER_VENDOR}=\"".$vendorName{$vendorId}."\"\n"; - $upowerMfrHeaderDone = 1; + $upowerVendorHasDevices = 1; } - $tmpOutputUPower = $tmpOutputUPower."ATTRS{idVendor}==\"".removeHexPrefix($vendorId); - $tmpOutputUPower = $tmpOutputUPower."\", ATTRS{idProduct}==\"".removeHexPrefix($productId)."\","; - $tmpOutputUPower = $tmpOutputUPower.' ENV{UPOWER_BATTERY_TYPE}="ups"'."\n"; + print $outputUPower "usb:v".uc(removeHexPrefix($vendorId))."p".uc(removeHexPrefix($productId))."*\n"; } # Device scanner entry print $outputDevScanner "\t{ ".$vendorId.', '.$productId.", \"".$vendor{$vendorId}{$productId}{"driver"}."\" },\n"; } + + if ($upowerVendorHasDevices) { + print $outputUPower " UPOWER_BATTERY_TYPE=ups\n"; + if ($vendorName{$vendorId}) { + print $outputUPower " UPOWER_VENDOR=".$vendorName{$vendorId}."\n"; + } + } + } # Udev footer print $outUdev "\n".'LABEL="nut-usbups_rules_end"'."\n"; - # UPower... - # ...flush device table - print $outputUPower $tmpOutputUPower; - # ...and print footer - print $outputUPower "\n".'LABEL="up_hid_end"'."\n"; - # Device scanner footer print $outputDevScanner "\n\t/* Terminating entry */\n\t{ 0, 0, NULL }\n};\n#endif /* DEVSCAN_USB_H */\n\n"; } From ad9c2bdbf61770aabdd1b0bcbdc19ee0c2385907 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 29 Mar 2022 20:19:54 +0200 Subject: [PATCH 385/700] Update pull_request_template.md --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 084ac806ce..288472971e 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -45,7 +45,7 @@ (several vendors do use same interface chips for unrelated protocols). - [ ] For new USB devices, built and committed the changes for the - `scripts/upower/95-upower-hid.rules` file + `scripts/upower/95-upower-hid.hwdb` file - [ ] Proposed NUT data mapping is aligned with existing `docs/nut-names.txt` file. If the device exposes useful data points not listed in the file, the From 530ed1aa320e91a25d0d704e05edca9b238b7bce Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 30 Mar 2022 18:46:06 +0000 Subject: [PATCH 386/700] docs/config-prereqs.txt: drop bogus command (copy-paste typo) --- docs/config-prereqs.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/config-prereqs.txt b/docs/config-prereqs.txt index 752738e4c3..cb9c3aa4b2 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -412,9 +412,6 @@ networked repository (4.2.x vs 4.9.x) :; pkg_add \ gd -# Need to find proper package name and/or mirror for this: -:; pkg_add clang - :; pkg_add \ cppunit \ openssl nss \ From a801ed472235baf8c937973829689880609ab40d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 30 Mar 2022 18:46:28 +0000 Subject: [PATCH 387/700] docs/config-prereqs.txt: add chapter for NetBSD 9.2 builder setup --- docs/config-prereqs.txt | 118 ++++++++++++++++++++++++++++++++++++++++ docs/nut.dict | 13 ++++- 2 files changed, 128 insertions(+), 3 deletions(-) diff --git a/docs/config-prereqs.txt b/docs/config-prereqs.txt index cb9c3aa4b2..377033cec2 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -467,6 +467,124 @@ scripts), better use this routine to test the config/build: Note the lack of `pkg-config` also precludes libcppunit tests, although they also tend to mis-compile/mis-link with GCC (while CLANG seems okay). +NetBSD 9.2 +~~~~~~~~~~ + +Instructions below assume that `pkgin` tool (pkg-src component to +"install binary packages") is present on the system. Text below +was prepared with a VM where "everything" was installed from the +ISO image, including compilers and X11. It is possible that some +packages provided this way differ from those served by `pkgin`, +or on the contrary, that the list of suggested tool installation +below would not include something a bare-minimum system would +require to build NUT. + +Note that `PATH` for builds on NetBSD should include `local` and +`pkg`; the default after installation of the test system was: + +---- +:; PATH="/sbin:/usr/sbin:/bin:/usr/bin:/usr/pkg/sbin:/usr/pkg/bin:/usr/X11R7/bin:/usr/local/sbin:/usr/local/bin" +:; export PATH +---- + +NOTE: You may want to reference `ccache` even before all that, +as detailed below: + +---- +:; PATH="/usr/lib/ccache:$PATH" +:; export PATH +---- + +To use the `ci_build.sh` don't forget `bash` which is not part of OpenBSD +base installation. It is not required for "legacy" builds arranged by just +`autogen.sh` and `configure` scripts. + +---- +:; pkgin install \ + git python27 python39 curl \ + make gmake autoconf automake libltdl libtool \ + cppcheck \ + pkgconf \ + gcc7 clang + +;; ( cd /usr/pkg/bin && ( ln -fs python2.7 python2 ; ln -fs python3.9 python3 ) ) + +# For spell-checking, highly recommended if you would propose pull requests: +:; pkgin install \ + aspell aspell-en + +# For man-page doc types, footprint on this platform is moderate: +:; pkgin install \ + asciidoc + +# For other doc types (PDF, HTML) generation - massive packages (TEX, X11): +:; pkgin install \ + source-highlight py39-pygments dblatex + +# For CGI graph generation - massive packages (X11): +:; pkgin install \ + gd openmp + +:; pkgin install \ + cppunit \ + openssl nss \ + augeas \ + libusb libusb1 \ + neon \ + net-snmp \ + avahi + +# Select a LUA-5.1 (or possibly 5.2?) version +:; pkgin install \ + lua51 + +:; pkgin install \ + bash dash ast-ksh oksh +---- + +NOTE: 9TBD) On NetBSD 9.2 this package complains that it requires +OS ABI 9.0, or that `CHECK_OSABI=no` is set in `pkg_install.conf`. +Such file was not found in the test system... ++ +---- +:; pkgin install \ + openipmi +---- + +Recommended: For compatibility with common setups on other operating +systems, can add dash-number suffixed symlinks to compiler tools (e.g. +`gcc-7` beside the `gcc` installed by package) near the original +binaries and into `/usr/lib/ccache`: +---- +:; ( cd /usr/bin && for TOOL in cpp gcc g++ ; do \ + ln -s "$TOOL" "$TOOL-7" ; \ + done ) + +# Note that the one delivered binary is `clang-13` and many (unnumbered) +# symlinks to it. For NUT CI style of support for builds with many +# compilers, complete the known numbers: +:; ( cd /usr/pkg/bin && for TOOL in clang-cpp clang++ ; do \ + ln -s clang-13 "$TOOL-13" ; \ + done ) + +:; pkgin install ccache +:; ( mkdir -p /usr/lib/ccache && cd /usr/lib/ccache && \ + for TOOL in cpp gcc g++ clang ; do \ + for VER in "" "-7" ; do \ + ln -s ../../pkg/bin/ccache "$TOOL$VER" ; \ + done ; \ + done ; \ + for TOOL in clang clang++ clang-cpp ; do \ + for VER in "" "-13" ; do \ + ln -s ../../pkg/bin/ccache "$TOOL$VER" ; \ + done ; \ + done ; \ + ) +---- + +NOTE: For Jenkins agents, also need to `pkgin install openjdk11` (will be +in `JAVA_HOME=/usr/pkg/java/openjdk11`). + OpenIndiana 2021.10 ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/nut.dict b/docs/nut.dict index 4917da8bab..ec16db3739 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,5 +1,6 @@ -personal_ws-1.1 en 2869 utf-8 +personal_ws-1.1 en 2883 utf-8 AAS +ABI ACFAIL ACFREQ ACK @@ -10,9 +11,9 @@ ACrms ADDR ADDRCONFIG ADDRINFO +ADELSYSTEM ADK ADKK -ADELSYSTEM AEC AEF AEG @@ -57,7 +58,6 @@ AlmCPol AlmEnbl Amplon Ampère -Amplon Andreas Andreassen Andrzej @@ -747,6 +747,7 @@ OMNIVSINT ONF ONV OOM +OSABI OSF OSs OUTPUTV @@ -1236,6 +1237,7 @@ VER VERFW VFI VIB +VM VMIN VMM VMware @@ -1390,6 +1392,7 @@ ascii asciidoc asem aspell +ast async atcl ats @@ -2245,6 +2248,7 @@ offtimedays oftd oids ok +oksh ol oldmac oldmge @@ -2262,8 +2266,10 @@ ontimedays ontiniedays ooce openSUSE +openipmi openjdk openlog +openmp opensolaris openssh openssl @@ -2311,6 +2317,7 @@ pinouts pkg pkgconf pkgconfig +pkgin plaintext plugin plugnplay From 53d15c7d7f97fc3469da3fac3f5357043b885e7a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 30 Mar 2022 20:59:46 +0000 Subject: [PATCH 388/700] autogen.sh: detect more python-x.y filenames --- autogen.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/autogen.sh b/autogen.sh index 19a583dd92..2cbe356d0d 100755 --- a/autogen.sh +++ b/autogen.sh @@ -15,7 +15,14 @@ if [ -n "${PYTHON-}" ] ; then } else PYTHON="" - for P in python python3 python2 ; do + for P in python python3 python2 \ + python-3.10 python3.10 \ + python-3.9 python3.9 \ + python-3.7 python3.7 \ + python-3.5 python3.5 \ + python-3.4 python3.4 \ + python-2.7 python2.7 \ + ; do if (command -v "$P" >/dev/null) && $P -c "import re,glob,codecs" ; then PYTHON="$P" break From 52e238035a853f4655f6de081a7cb0b0c517a309 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 30 Mar 2022 21:00:12 +0000 Subject: [PATCH 389/700] autogen.sh: suggest to export PYTHON=python-x.y --- autogen.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/autogen.sh b/autogen.sh index 2cbe356d0d..e521e45a12 100755 --- a/autogen.sh +++ b/autogen.sh @@ -52,6 +52,7 @@ then touch scripts/augeas/nutupsconf.aug.in scripts/augeas/nutupsconf.aug.in.AUTOGEN_WITHOUT else echo "Aborting $0! To avoid this, please export WITHOUT_NUT_AUGEAS=true and re-run" >&2 + echo "or better yet, export PYTHON=python-x.y and re-run" >&2 exit 1 fi fi From f0bea566c19736e57266bee204225df834d4937e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 31 Mar 2022 07:33:28 +0000 Subject: [PATCH 390/700] Cast (size_t)(char) for isprint(), isspace(), isdigit(), toupper(), etc. who may be macros and use it as array subscript (due to sys/ctype_inline.h) --- common/parseconf.c | 5 +++-- common/str.c | 10 +++++----- drivers/apcsmart-old.c | 2 +- drivers/apcsmart.c | 8 ++++---- drivers/mge-utalk.c | 5 +++-- drivers/serial.c | 2 +- drivers/snmp-ups.c | 2 +- server/conf.c | 11 ++++++----- 8 files changed, 24 insertions(+), 21 deletions(-) diff --git a/common/parseconf.c b/common/parseconf.c index fe8f172b96..7c284b750f 100644 --- a/common/parseconf.c +++ b/common/parseconf.c @@ -89,6 +89,7 @@ #include "parseconf.h" #include "attribute.h" +#include "nut_stdint.h" /* possible states */ @@ -241,7 +242,7 @@ static int findwordstart(PCONF_CTX_t *ctx) return STATE_FINDEOL; /* space = not in a word yet, so loop back */ - if (isspace(ctx->ch)) + if (isspace((size_t)ctx->ch)) return STATE_FINDWORDSTART; /* \ = literal = accept the next char blindly */ @@ -341,7 +342,7 @@ static int collect(PCONF_CTX_t *ctx) } /* space means the word is done */ - if (isspace(ctx->ch)) { + if (isspace((size_t)ctx->ch)) { endofword(ctx); return STATE_FINDWORDSTART; diff --git a/common/str.c b/common/str.c index a59f9d979d..a23d93f78c 100644 --- a/common/str.c +++ b/common/str.c @@ -118,7 +118,7 @@ char *str_ltrim_space(char *string) while ( *string != '\0' && - isspace(*string) + isspace((size_t)*string) ) memmove(string, string + 1, strlen(string)); @@ -139,7 +139,7 @@ char *str_rtrim_space(char *string) while ( ptr >= string && - isspace(*ptr) + isspace((size_t)*ptr) ) *ptr-- = '\0'; @@ -438,7 +438,7 @@ int str_to_long_strict(const char *string, long *number, const int base) if ( string == NULL || *string == '\0' || - isspace(*string) + isspace((size_t)*string) ) { errno = EINVAL; return 0; @@ -504,7 +504,7 @@ int str_to_ulong_strict(const char *string, unsigned long *number, const int bas *string == '\0' || *string == '+' || *string == '-' || - isspace(*string) + isspace((size_t)*string) ) { errno = EINVAL; return 0; @@ -568,7 +568,7 @@ int str_to_double_strict(const char *string, double *number, const int base) if ( string == NULL || *string == '\0' || - isspace(*string) + isspace((size_t)*string) ) { errno = EINVAL; return 0; diff --git a/drivers/apcsmart-old.c b/drivers/apcsmart-old.c index 34a82a56ec..0a2f451382 100644 --- a/drivers/apcsmart-old.c +++ b/drivers/apcsmart-old.c @@ -536,7 +536,7 @@ static void protocol_verify(unsigned char cmd) if (found) return; - if (isprint(cmd)) + if (isprint((size_t)cmd)) upsdebugx(1, "protocol_verify: 0x%02x [%c] unrecognized", cmd, cmd); else diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 0122c924b3..bb82e191dc 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -111,7 +111,7 @@ static const char *prtchr(char x) static char info[32]; curr = (curr + 8) & 0x1F; - snprintf(info + curr, 8, isprint(x) ? "%c" : "0x%02x", x); + snprintf(info + curr, 8, isprint((size_t)x) ? "%c" : "0x%02x", x); return info + curr; } @@ -1712,7 +1712,7 @@ void upsdrv_shutdown(void) ups_status = APC_STAT_LB | APC_STAT_OB; } - if (testvar("advorder") && toupper(*getval("advorder")) != 'N') + if (testvar("advorder") && toupper((size_t)*getval("advorder")) != 'N') upsdrv_shutdown_advanced(); else upsdrv_shutdown_simple(); @@ -2040,10 +2040,10 @@ static int instcmd(const char *cmd, const char *ext) if (!ext || !*ext) return sdcmd_S(0); - if (toupper(*ext) == 'A') + if (toupper((size_t)*ext) == 'A') return sdcmd_AT(ext + 3); - if (toupper(*ext) == 'C') + if (toupper((size_t)*ext) == 'C') return sdcmd_CS(0); } diff --git a/drivers/mge-utalk.c b/drivers/mge-utalk.c index 19c36616e5..86d272f023 100644 --- a/drivers/mge-utalk.c +++ b/drivers/mge-utalk.c @@ -60,6 +60,7 @@ #include "main.h" #include "serial.h" #include "mge-utalk.h" +#include "nut_stdint.h" /* --------------------------------------------------------------- */ /* Define "technical" constants */ @@ -908,7 +909,7 @@ static ssize_t mge_command(char *reply, size_t replylen, const char *fmt, ...) /* send command */ for (p = command; *p; p++) { - if ( isprint(*p & 0xFF) ) + if ( isprint((unsigned char)*p & 0xFF) ) upsdebugx(4, "mge_command: sending [%c]", *p); else upsdebugx(4, "mge_command: sending [%02X]", *p); @@ -922,7 +923,7 @@ static ssize_t mge_command(char *reply, size_t replylen, const char *fmt, ...) /* send terminating string */ for (p = MGE_COMMAND_ENDCHAR; *p; p++) { - if ( isprint(*p & 0xFF) ) + if ( isprint((unsigned char)*p & 0xFF) ) upsdebugx(4, "mge_command: sending [%c]", *p); else upsdebugx(4, "mge_command: sending [%02X]", *p); diff --git a/drivers/serial.c b/drivers/serial.c index 693f15d8c4..60af06f37d 100644 --- a/drivers/serial.c +++ b/drivers/serial.c @@ -485,7 +485,7 @@ ssize_t ser_flush_in(int fd, const char *ignset, int verbose) if (verbose == 0) continue; - if (isprint(ch & 0xFF)) + if (isprint((unsigned char)ch & 0xFF)) upslogx(LOG_INFO, "ser_flush_in: read char %c", ch); else upslogx(LOG_INFO, "ser_flush_in: read char 0x%02x", ch); diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 9cbcf9c9af..fad0997643 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -1222,7 +1222,7 @@ static bool_t decode_str(struct snmp_pdu *pdu, char *buf, size_t buf_len, info_l int hex = 0, x; unsigned char *cp; for(cp = pdu->variables->val.string, x = 0; x < (int)pdu->variables->val_len; x++, cp++) { - if (!(isprint(*cp) || isspace(*cp))) { + if (!(isprint((size_t)*cp) || isspace((size_t)*cp))) { hex = 1; } } diff --git a/server/conf.c b/server/conf.c index 8bb694e1a8..3dd2696236 100644 --- a/server/conf.c +++ b/server/conf.c @@ -23,6 +23,7 @@ #include "sstate.h" #include "user.h" #include "netssl.h" +#include "nut_stdint.h" #include static ups_t *upstable = NULL; @@ -158,7 +159,7 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) /* MAXAGE */ if (!strcmp(arg[0], "MAXAGE")) { - if (isdigit(arg[1][0])) { + if (isdigit((size_t)arg[1][0])) { maxage = atoi(arg[1]); return 1; } @@ -170,7 +171,7 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) /* TRACKINGDELAY */ if (!strcmp(arg[0], "TRACKINGDELAY")) { - if (isdigit(arg[1][0])) { + if (isdigit((size_t)arg[1][0])) { tracking_delay = atoi(arg[1]); return 1; } @@ -182,7 +183,7 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) /* ALLOW_NO_DEVICE */ if (!strcmp(arg[0], "ALLOW_NO_DEVICE")) { - if (isdigit(arg[1][0])) { + if (isdigit((size_t)arg[1][0])) { allow_no_device = (atoi(arg[1]) != 0); /* non-zero arg is true here */ return 1; } @@ -195,7 +196,7 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) /* MAXCONN */ if (!strcmp(arg[0], "MAXCONN")) { - if (isdigit(arg[1][0])) { + if (isdigit((size_t)arg[1][0])) { /* FIXME: Check for overflows (and int size of nfds_t vs. long) - see get_max_pid_t() for example */ maxconn = (nfds_t)atol(arg[1]); return 1; @@ -237,7 +238,7 @@ static int parse_upsd_conf_args(size_t numargs, char **arg) #ifdef WITH_CLIENT_CERTIFICATE_VALIDATION /* CERTREQUEST (0 | 1 | 2) */ if (!strcmp(arg[0], "CERTREQUEST")) { - if (isdigit(arg[1][0])) { + if (isdigit((size_t)arg[1][0])) { certrequest = atoi(arg[1]); return 1; } From aeb8ef415121ae764e14887175d6d607a08c3491 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 31 Mar 2022 07:52:47 +0000 Subject: [PATCH 391/700] m4/nut_compiler_family.m4: treat /usr/pkg/include as -isystem --- m4/nut_compiler_family.m4 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/m4/nut_compiler_family.m4 b/m4/nut_compiler_family.m4 index a9280942ee..8cdc972b95 100644 --- a/m4/nut_compiler_family.m4 +++ b/m4/nut_compiler_family.m4 @@ -151,11 +151,17 @@ dnl # files do cause grief to picky compiler settings (more so from third dnl # party packages shipped via /usr/local/... namespace): AS_IF([test "x$CLANGCC" = xyes -o "x$GCC" = xyes], [ dnl # CFLAGS="-isystem /usr/include $CFLAGS" - CFLAGS="-isystem /usr/local/include $CFLAGS" + AS_IF([test -d /usr/local/include], + [CFLAGS="-isystem /usr/local/include $CFLAGS"]) + AS_IF([test -d /usr/pkg/include], + [CFLAGS="-isystem /usr/pkg/include $CFLAGS"]) ]) AS_IF([test "x$CLANGXX" = xyes -o "x$GXX" = xyes], [ dnl # CXXFLAGS="-isystem /usr/include $CXXFLAGS" - CXXFLAGS="-isystem /usr/local/include $CXXFLAGS" + AS_IF([test -d /usr/local/include], + [CXXFLAGS="-isystem /usr/local/include $CXXFLAGS"]) + AS_IF([test -d /usr/pkg/include], + [CXXFLAGS="-isystem /usr/pkg/include $CXXFLAGS"]) ]) dnl # Default to avoid noisy warnings on older compilers From 0be7c9201ac13b75d5bd0825a6ff249a99bd48a2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 31 Mar 2022 08:03:54 +0000 Subject: [PATCH 392/700] ci_build.sh: in BUILD_TYPE=default-all-errors report visibly when no failures happened --- ci_build.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci_build.sh b/ci_build.sh index ef17d9dd46..dec9d397e5 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -1401,6 +1401,8 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp if [ "$RES_ALLERRORS" != 0 ]; then # Leading space is included in FAILED echo "FAILED build(s) with code ${RES_ALLERRORS}:${FAILED}" >&2 + else + echo "(and no build scenarios had failed)" >&2 fi echo "Initially estimated ${BUILDSTODO_INITIAL} variations for BUILD_TYPE='$BUILD_TYPE'" >&2 From 6b787c1e9e04f2fddb69d7f2fce26e11e6d73bac Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 31 Mar 2022 08:06:15 +0000 Subject: [PATCH 393/700] ci_build.sh: introduce shortcut for BUILD_TYPE=fightwarn-all --- ci_build.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ci_build.sh b/ci_build.sh index dec9d397e5..6075165f8b 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -18,6 +18,29 @@ set -e # CI_REQUIRE_GOOD_GITIGNORE="false" CI_FAILFAST=true DO_CLEAN_CHECK=no BUILD_TYPE=fightwarn ./ci_build.sh case "$BUILD_TYPE" in fightwarn) ;; # for default compiler + fightwarn-all) + # This recipe allows to test with different (default-named) + # compiler suites if available. Primary goal is to see whether + # everything is building ok on a given platform, with one shot. + TRIED_BUILD=false + if (command -v gcc) >/dev/null ; then + TRIED_BUILD=true + BUILD_TYPE=fightwarn-gcc "$0" || exit + else + echo "SKIPPING BUILD_TYPE=fightwarn-gcc: compiler not found" >&2 + fi + if (command -v clang) >/dev/null ; then + TRIED_BUILD=true + BUILD_TYPE=fightwarn-clang "$0" || exit + else + echo "SKIPPING BUILD_TYPE=fightwarn-clang: compiler not found" >&2 + fi + if ! $TRIED_BUILD ; then + echo "FAILED to run: no default-named compilers were found" >&2 + exit 1 + fi + exit 0 + ;; fightwarn-gcc) CC="gcc" CXX="g++" From 1a17e2aa68423530d9644f316bac8e9fc3335874 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 31 Mar 2022 10:40:05 +0000 Subject: [PATCH 394/700] {.,docs,docs/man}/Makefile.am: add "all-man" target to build all possible man pages (vs ones for enabled drivers), and weave this and check targets to parent makefiles --- Makefile.am | 9 ++++++++- docs/Makefile.am | 7 +++++-- docs/man/Makefile.am | 6 +++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Makefile.am b/Makefile.am index 68882955f6..b42d639cab 100644 --- a/Makefile.am +++ b/Makefile.am @@ -108,7 +108,14 @@ spellcheck spellcheck-interactive: (cd $(builddir)/data && $(MAKE) -s $@) || RES=$$? ; \ exit $$RES -doc spellcheck-sortdict: +# Note: the "all-docs" and "check-docs" targets may require tools not +# found by `configure` script (and so avoided by conventional recipes) +# such as PDF generators, so it should only be called at developer's +# discretion, choice and risk. The "check-man" targets covers source +# texts, man pages and HTML rendering of man pages, as enabled by tools. +doc spellcheck-sortdict \ +all-docs check-docs \ +man all-man man-man check-man man-html all-html: cd $(srcdir)/docs && $(MAKE) $@ # This target adds syntax-checking for committed shell script files, diff --git a/docs/Makefile.am b/docs/Makefile.am index 3b8d433a61..1b93081736 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -87,7 +87,7 @@ doc: @DOC_BUILD_LIST@ # This target can be called by developers to go around the configure # script choices at their risk (e.g. missing tools are possible): -docs: pdf html-single html-chunked man +docs: pdf html-single html-chunked man-man html-man all-docs: docs @@ -130,7 +130,10 @@ check-html-chunked: $(ASCIIDOC_HTML_CHUNKED) # Note: usually the results from man-page check will be reported twice: # once as a SUBDIRS child makefile, and once via DOC_CHECK_LIST expansion -check-man: +# Note: default `make all` in the man directory caters to drivers etc. +# chosen during configure script execution. The "all-man" and "all-html" +# rules build everything documented. +check-man all-man man-man all-html html-man: cd $(top_builddir)/docs/man/ && $(MAKE) -f Makefile $@ man: diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index cc667814e0..11e1597b16 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -735,7 +735,11 @@ HTML_MANS = \ all: -html-man: $(HTML_MANS) index.html +all-html html-man: $(HTML_MANS) index.html + +# Have a way to build all man pages, not just those that fit currently +# configured drivers, daemons, developer aspect, etc. +all-man man-man: $(MAN_MANS) if WITH_MANS if ! SKIP_MANS From e78afb502a2833e81ed505ea2743ec0b211140c5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 1 Apr 2022 12:01:02 +0200 Subject: [PATCH 395/700] Add "PROTVER" as alias to "NETVER" for NUT v2.8.0 Closes: #1347 --- NEWS | 4 ++++ docs/net-protocol.txt | 2 ++ docs/nut.dict | 3 ++- server/netcmds.h | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 63b93c7754..8320cf3a27 100644 --- a/NEWS +++ b/NEWS @@ -276,6 +276,10 @@ refer to this change set (too long in the making) as NUT 2.7.5. - upsrw: display the variable type beside ENUM / RANGE + - Added `PROTVER` as alias to `NETVER` to report the protocol version in use. + Note that NUT codebase itself does not use this value and handles commands + and reported errors individually [issue #1347] + - Implement status tracking for instant commands (instcmd) and variables settings (setvar): this allows to get the actual execution status from the driver, and is available in libraries and upscmd / upsrw [PR #659] diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index f8a104ee14..4e9b744851 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -49,6 +49,7 @@ NUT network protocol, over the time: |Add "PRIMARY" as alias to older "MASTER" | (implementation tested to be backwards | compatible in `upsd` and `upsmon`) + |Add "PROTVER" as alias to older "NETVER" |=============================================================================== NOTE: any new version of the protocol implies an update of NUT_NETVERSION @@ -627,6 +628,7 @@ Other commands - HELP: lists the commands supported by this server - VER: shows the version of the server currently in use - NETVER: shows the version of the network protocol currently in use + (aliased as PROTVER since NUT v2.8.0, or formal protocol version 1.3) These three are not intended to be used directly by programs. Humans can make use of this program by using telnet or netcat. If you use diff --git a/docs/nut.dict b/docs/nut.dict index ec16db3739..ed577271c2 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2883 utf-8 +personal_ws-1.1 en 2884 utf-8 AAS ABI ACFAIL @@ -817,6 +817,7 @@ PPPPPPPPPP PR PR'ed PROGS +PROTVER PRs PSA PSD diff --git a/server/netcmds.h b/server/netcmds.h index 891237f948..6a5a632d01 100644 --- a/server/netcmds.h +++ b/server/netcmds.h @@ -50,6 +50,7 @@ static struct { } netcmds[] = { { "VER", net_ver, 0 }, { "NETVER", net_netver, 0 }, + { "PROTVER", net_netver, 0 }, /* aliased since NUT 2.8.0 */ { "HELP", net_help, 0 }, { "STARTTLS", net_starttls, 0 }, From e3218bad66bf1bb362d07b458c77ad6b09a2e4f9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 1 Apr 2022 18:42:25 +0200 Subject: [PATCH 396/700] docs/config-prereqs.txt: fix typos in OpenIndiana chapter --- docs/config-prereqs.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/config-prereqs.txt b/docs/config-prereqs.txt index 752738e4c3..bf4de9256f 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -540,7 +540,7 @@ For extra compiler coverage, we can install a large selection of versions: # As of this writing, clang-13 refused to link (claiming issues with # --fuse-ld which was never specified) on OI; maybe later it will: :; pkg install \ - developer/clang-13 runtime/clang-13 \ + developer/clang-13 runtime/clang-13 # Get clang-cpp-X visible in standard PATH (for CI to reference the right one), # and make sure other frontends are exposed with versions (not all OI distro @@ -551,7 +551,7 @@ For extra compiler coverage, we can install a large selection of versions: # If /usr/lib/ccache/ symlinks to compilers do not appear after package # installation, or if you had to add links like above, call the service: -:; svcadm refresh ccache-update-symlinks +:; svcadm restart ccache-update-symlinks ---- We can also include a `gcc-4.4.4-il` (used to build the illumos OS ecosystems, @@ -569,7 +569,7 @@ stage: done) # If /usr/lib/ccache/ symlinks to these do not appear, call the service: -:; svcadm refresh ccache-update-symlinks +:; svcadm restart ccache-update-symlinks ---- OI currently also does not build cppunit-based tests well, at least From f59619266136d14c614ef7a54be5c113acaad7a5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 1 Apr 2022 19:13:05 +0200 Subject: [PATCH 397/700] scripts/python/module/PyNUT.py.in: fix typo (fallout of #840) --- scripts/python/module/PyNUT.py.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/python/module/PyNUT.py.in b/scripts/python/module/PyNUT.py.in index dea4053bd3..226c0aef84 100644 --- a/scripts/python/module/PyNUT.py.in +++ b/scripts/python/module/PyNUT.py.in @@ -277,13 +277,13 @@ NOTE: API changed since NUT 2.8.0 to replace MASTER with PRIMARY """ if self.__debug : - print( "[DEBUG] "PRIMARY called..." ) + print( "[DEBUG] PRIMARY called..." ) self.__srv_handler.write( ("PRIMARY %s\n" % ups).encode('ascii') ) result = self.__srv_handler.read_until( b"\n" ) if ( result != b"OK PRIMARY-GRANTED\n" ) : if self.__debug : - print( "[DEBUG] Retrying: "MASTER called..." ) + print( "[DEBUG] Retrying: MASTER called..." ) self.__srv_handler.write( ("MASTER %s\n" % ups).encode('ascii') ) result = self.__srv_handler.read_until( b"\n" ) if ( result != b"OK MASTER-GRANTED\n" ) : From cb8877629c06f58972683811e1285633921b67e0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 1 Apr 2022 19:14:58 +0200 Subject: [PATCH 398/700] scripts/python/README: clarify that `test_nutclient.py` requires an `upsd` running --- scripts/python/README | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/python/README b/scripts/python/README index 15932d9908..74a43db4f9 100644 --- a/scripts/python/README +++ b/scripts/python/README @@ -19,7 +19,8 @@ To install the PyNUT module on Debian/Ubuntu, copy it to: This directory also contains test_nutclient.py, which is a PyNUT test program. For this to be fully functional, you will need to adapt the login, password and -upsname to fit your configuration. +upsname to fit your configuration. A NUT data server should be running for the +test program to verify connection and protocol support. * "app": this directory contains the NUT-Monitor application, that uses the From 8ab6aad1d81a73fb6d29a33e8e35148023a0c4bd Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sun, 13 Feb 2022 20:31:17 +0000 Subject: [PATCH 399/700] NUT-Monitor: Run py2to3-3.10 --- scripts/python/app/NUT-Monitor.in | 48 +++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/scripts/python/app/NUT-Monitor.in b/scripts/python/app/NUT-Monitor.in index 95c28d87ce..296b88bcaa 100755 --- a/scripts/python/app/NUT-Monitor.in +++ b/scripts/python/app/NUT-Monitor.in @@ -37,7 +37,7 @@ import platform import time import threading import optparse -import ConfigParser +import configparser import locale import gettext import PyNUT @@ -48,7 +48,7 @@ gobject.threads_init() class interface : - DESIRED_FAVORITES_DIRECTORY_MODE = 0700 + DESIRED_FAVORITES_DIRECTORY_MODE = 0o700 __widgets = {} __callbacks = {} @@ -211,7 +211,7 @@ class interface : self.gui_status_message( _("Welcome to NUT Monitor") ) if ( cmd_opts.favorite != None ) : - if ( self.__favorites.has_key( cmd_opts.favorite ) ) : + if ( cmd_opts.favorite in self.__favorites ) : self.__gui_load_favorite( fav_name=cmd_opts.favorite ) self.connect_to_ups() else : @@ -293,7 +293,7 @@ class interface : nut_handler = PyNUT.PyNUTClient( host=host, port=port, login=login, password=password ) upses = nut_handler.GetUPSList() - ups_list = upses.keys() + ups_list = list(upses.keys()) ups_list.sort() # If UPS list contains something, clear it @@ -368,7 +368,7 @@ class interface : # Remove the dummy combobox entry on list dialog_interface.get_widget("combobox2").remove_text( 0 ) - favs = self.__favorites.keys() + favs = list(self.__favorites.keys()) favs.sort() for current in favs : dialog_interface.get_widget("combobox2").append_text( current ) @@ -397,7 +397,7 @@ class interface : # Method called when user selects a favorite from the favorites menu def __gui_load_favorite( self, fav_name="" ) : - if ( self.__favorites.has_key( fav_name ) ) : + if ( fav_name in self.__favorites ) : # If auth is activated, process it before other fields to avoir weird # reactions with the 'check_gui_fields' function. if ( self.__favorites[fav_name].get("auth", False ) ) : @@ -497,7 +497,7 @@ class interface : self.__fav_menu_items = list() - items = self.__favorites.keys() + items = list(self.__favorites.keys()) items.sort() for current in items : @@ -520,7 +520,7 @@ class interface : # to avoid creating entries with the same name. def __gui_add_favorite_check_gui_fields( self, widget=None ) : fav_name = widget.get_text() - if ( len( fav_name ) > 0 ) and ( fav_name not in self.__favorites.keys() ) : + if ( len( fav_name ) > 0 ) and ( fav_name not in list(self.__favorites.keys()) ) : self.__widgets["favorites_dialog_button_add"].set_sensitive( True ) else : self.__widgets["favorites_dialog_button_add"].set_sensitive( False ) @@ -537,7 +537,7 @@ class interface : if ( not stat.S_IMODE( os.stat( self.__favorites_path ).st_mode ) == self.DESIRED_FAVORITES_DIRECTORY_MODE ) : # unsafe pre-1.2 directory found os.chmod( self.__favorites_path, self.DESIRED_FAVORITES_DIRECTORY_MODE ) - conf = ConfigParser.ConfigParser() + conf = configparser.ConfigParser() conf.read( self.__favorites_file ) for current in conf.sections() : # Check if mandatory fields are present @@ -564,7 +564,7 @@ class interface : except : # If the password is not in base64, let the field empty - print( _("Error parsing favorites, password for '%s' is not in base64\nSkipping password for this entry") % current ) + print(( _("Error parsing favorites, password for '%s' is not in base64\nSkipping password for this entry") % current )) fav_data["password"] = "" else : fav_data["auth"] = False @@ -586,10 +586,10 @@ class interface : except : self.gui_status_message( _("Error while creating configuration folder (%s)") % sys.exc_info()[1] ) - save_conf = ConfigParser.ConfigParser() - for current in self.__favorites.keys() : + save_conf = configparser.ConfigParser() + for current in list(self.__favorites.keys()) : save_conf.add_section( current ) - for k, v in self.__favorites[ current ].iteritems() : + for k, v in self.__favorites[ current ].items() : save_conf.set( current, k, v ) try : @@ -678,7 +678,7 @@ class interface : srv_upses = self.__ups_handler.GetUPSList() self.__current_ups = self.__widgets["ups_list_combo"].get_active_text() - if not srv_upses.has_key( self.__current_ups ) : + if self.__current_ups not in srv_upses : self.gui_status_message( _("Device '%s' not found on server") % self.__current_ups ) self.gui_status_notification( _("Device '%s' not found on server") % self.__current_ups, "warning.png" ) return @@ -692,7 +692,7 @@ class interface : self.__widgets["ups_params_box"].hide() commands = self.__ups_handler.GetUPSCommands( self.__current_ups ) - self.__ups_commands = commands.keys() + self.__ups_commands = list(commands.keys()) self.__ups_commands.sort() # Refresh UPS commands combo box @@ -726,8 +726,8 @@ class interface : self.__widgets["ups_vars_tree_store"].clear() - for k,v in vars.iteritems() : - if ( rwvars.has_key( k ) ) : + for k,v in vars.items() : + if ( k in rwvars ) : icon_file = os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "var-rw.png" ) else : icon_file = os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "var-ro.png" ) @@ -814,7 +814,7 @@ class gui_updater( threading.Thread ) : was_online = False # Check for additionnal information - for k,v in status_mapper.iteritems() : + for k,v in status_mapper.items() : if vars.get("ups.status").find(k) != -1 : if ( text_right != "" ) : text_right += " - %s" % v @@ -830,15 +830,15 @@ class gui_updater( threading.Thread ) : status_text += text_right text_right += "\n" - if ( vars.has_key( "ups.mfr" ) ) : + if ( "ups.mfr" in vars ) : text_left += "%s\n\n" % _("Model :") text_right += "%s\n%s\n" % ( vars.get("ups.mfr",""), vars.get("ups.model","") ) - if ( vars.has_key( "ups.temperature" ) ) : + if ( "ups.temperature" in vars ) : text_left += "%s\n" % _("Temperature :") text_right += "%s\n" % int( float( vars.get( "ups.temperature", 0 ) ) ) - if ( vars.has_key( "battery.voltage" ) ) : + if ( "battery.voltage" in vars ) : text_left += "%s\n" % _("Battery voltage :") text_right += "%sv\n" % vars.get( "battery.voltage", 0 ) @@ -846,7 +846,7 @@ class gui_updater( threading.Thread ) : self.__parent_class._interface__widgets["ups_status_right"].set_markup( text_right[:-1] ) # UPS load and battery charge progress bars - if ( vars.has_key( "battery.charge" ) ) : + if ( "battery.charge" in vars ) : charge = vars.get( "battery.charge", "0" ) self.__parent_class._interface__widgets["progress_battery_charge"].set_fraction( float( charge ) / 100.0 ) self.__parent_class._interface__widgets["progress_battery_charge"].set_text( "%s %%" % int( float( charge ) ) ) @@ -855,7 +855,7 @@ class gui_updater( threading.Thread ) : self.__parent_class._interface__widgets["progress_battery_charge"].set_fraction( 0.0 ) self.__parent_class._interface__widgets["progress_battery_charge"].set_text( _("Not available") ) - if ( vars.has_key( "ups.load" ) ) : + if ( "ups.load" in vars ) : load = vars.get( "ups.load", "0" ) self.__parent_class._interface__widgets["progress_battery_load"].set_fraction( float( load ) / 100.0 ) self.__parent_class._interface__widgets["progress_battery_load"].set_text( "%s %%" % int( float( load ) ) ) @@ -864,7 +864,7 @@ class gui_updater( threading.Thread ) : self.__parent_class._interface__widgets["progress_battery_load"].set_fraction( 0.0 ) self.__parent_class._interface__widgets["progress_battery_load"].set_text( _("Not available") ) - if ( vars.has_key( "battery.runtime" ) ) : + if ( "battery.runtime" in vars ) : autonomy = int( float( vars.get( "battery.runtime", 0 ) ) ) if ( autonomy >= 3600 ) : From ad6d25afffa20bfc61a01403c942b5f28edf7ccf Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 21 Feb 2022 02:51:37 +0000 Subject: [PATCH 400/700] NUT-Monitor: Port to Python3+PyQt5 --- scripts/python/Makefile.am | 5 +- scripts/python/README | 2 +- scripts/python/app/NUT-Monitor.in | 718 ++++++++-------- scripts/python/app/README | 2 +- scripts/python/app/gui-1.3.glade | 1073 ------------------------ scripts/python/app/gui-1.3.glade.h | 51 -- scripts/python/app/nut-monitor.desktop | 2 +- scripts/python/app/ui/aboutdialog1.ui | 108 +++ scripts/python/app/ui/dialog1.ui | 89 ++ scripts/python/app/ui/dialog2.ui | 98 +++ scripts/python/app/ui/window1.ui | 473 +++++++++++ 11 files changed, 1150 insertions(+), 1471 deletions(-) delete mode 100644 scripts/python/app/gui-1.3.glade delete mode 100644 scripts/python/app/gui-1.3.glade.h create mode 100644 scripts/python/app/ui/aboutdialog1.ui create mode 100644 scripts/python/app/ui/dialog1.ui create mode 100644 scripts/python/app/ui/dialog2.ui create mode 100644 scripts/python/app/ui/window1.ui diff --git a/scripts/python/Makefile.am b/scripts/python/Makefile.am index 8a50571230..b53da4576c 100644 --- a/scripts/python/Makefile.am +++ b/scripts/python/Makefile.am @@ -1,7 +1,10 @@ # Network UPS Tools: data/html EXTRA_DIST = README \ - app/gui-1.3.glade \ + app/ui/aboutdialog1.ui \ + app/ui/dialog1.ui \ + app/ui/dialog2.ui \ + app/ui/window1.ui \ app/NUT-Monitor.in \ app/nut-monitor.appdata.xml \ app/nut-monitor.desktop \ diff --git a/scripts/python/README b/scripts/python/README index 74a43db4f9..ed741d24d9 100644 --- a/scripts/python/README +++ b/scripts/python/README @@ -28,6 +28,6 @@ PyNUT class, along with its resources. To install it, you will either need to keep the files together, or to install: - NUT-Monitor to /usr/bin, /usr/X11R6/bin/ or something like that, -- gui.glade to /usr/share/nut-monitor/, +- *.ui to /usr/share/nut-monitor/, - nut-monitor.png to something like /usr/share/pixmaps/ - and nut-monitor.desktop to /usr/share/applications diff --git a/scripts/python/app/NUT-Monitor.in b/scripts/python/app/NUT-Monitor.in index 296b88bcaa..c4a648ee30 100755 --- a/scripts/python/app/NUT-Monitor.in +++ b/scripts/python/app/NUT-Monitor.in @@ -26,9 +26,15 @@ # # 2015-02-14 Michal Fincham - Version 1.3.1 # Corrected unsafe permissions on ~/.nut-monitor (Debian #777706) +# +# 2022-02-20 Luke Dashjr - Version 2.0 +# Port to Python 3 with PyQt5. -import gtk, gtk.glade, gobject +import PyQt5.uic +from PyQt5.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * import sys import base64 import os, os.path @@ -43,9 +49,6 @@ import gettext import PyNUT -# Activate threadings on glib -gobject.threads_init() - class interface : DESIRED_FAVORITES_DIRECTORY_MODE = 0o700 @@ -57,7 +60,7 @@ class interface : __favorites_path = "" __fav_menu_items = list() __window_visible = True - __glade_file = None + __ui_file = None __connected = False __ups_handler = None __ups_commands = None @@ -66,7 +69,7 @@ class interface : __gui_thread = None __current_ups = None - def __init__( self ) : + def __init__( self, argv ) : # Before anything, parse command line options if any present... opt_parser = optparse.OptionParser() @@ -76,126 +79,102 @@ class interface : ( cmd_opts, args ) = opt_parser.parse_args() - self.__glade_file = os.path.join( os.path.dirname( sys.argv[0] ), "gui-1.3.glade" ) - - self.__widgets["interface"] = gtk.glade.XML( self.__glade_file, "window1", APP ) - self.__widgets["main_window"] = self.__widgets["interface"].get_widget("window1") - self.__widgets["status_bar"] = self.__widgets["interface"].get_widget("statusbar2") - self.__widgets["ups_host_entry"] = self.__widgets["interface"].get_widget("entry1") - self.__widgets["ups_port_entry"] = self.__widgets["interface"].get_widget("spinbutton1") - self.__widgets["ups_refresh_button"] = self.__widgets["interface"].get_widget("button1") - self.__widgets["ups_authentication_check"] = self.__widgets["interface"].get_widget("checkbutton1") - self.__widgets["ups_authentication_frame"] = self.__widgets["interface"].get_widget("hbox1") - self.__widgets["ups_authentication_login"] = self.__widgets["interface"].get_widget("entry2") - self.__widgets["ups_authentication_password"] = self.__widgets["interface"].get_widget("entry3") - self.__widgets["ups_list_combo"] = self.__widgets["interface"].get_widget("combobox1") - self.__widgets["ups_commands_button"] = self.__widgets["interface"].get_widget("button8") - self.__widgets["ups_connect"] = self.__widgets["interface"].get_widget("button2") - self.__widgets["ups_disconnect"] = self.__widgets["interface"].get_widget("button7") - self.__widgets["ups_params_box"] = self.__widgets["interface"].get_widget("vbox6") - self.__widgets["ups_infos"] = self.__widgets["interface"].get_widget("notebook1") - self.__widgets["ups_vars_tree"] = self.__widgets["interface"].get_widget("treeview1") - self.__widgets["ups_vars_refresh"] = self.__widgets["interface"].get_widget("button9") - self.__widgets["ups_status_image"] = self.__widgets["interface"].get_widget("image1") - self.__widgets["ups_status_left"] = self.__widgets["interface"].get_widget("label10") - self.__widgets["ups_status_right"] = self.__widgets["interface"].get_widget("label11") - self.__widgets["ups_status_time"] = self.__widgets["interface"].get_widget("label15") - self.__widgets["menu_favorites_root"] = self.__widgets["interface"].get_widget("menuitem3") - self.__widgets["menu_favorites"] = self.__widgets["interface"].get_widget("menu2") - self.__widgets["menu_favorites_add"] = self.__widgets["interface"].get_widget("menuitem4") - self.__widgets["menu_favorites_del"] = self.__widgets["interface"].get_widget("menuitem5") - self.__widgets["progress_battery_charge"] = self.__widgets["interface"].get_widget("progressbar1") - self.__widgets["progress_battery_load"] = self.__widgets["interface"].get_widget("progressbar2") + self.__app = QApplication( argv ) + + self.__ui_file = self.__find_res_file( 'ui', "window1.ui" ) + + self.__widgets["interface"] = PyQt5.uic.loadUi( self.__ui_file ) + self.__widgets["main_window"] = self.__widgets["interface"] + self.__widgets["status_bar"] = self.__widgets["interface"].statusbar2 + self.__widgets["ups_host_entry"] = self.__widgets["interface"].entry1 + self.__widgets["ups_port_entry"] = self.__widgets["interface"].spinbutton1 + self.__widgets["ups_refresh_button"] = self.__widgets["interface"].button1 + self.__widgets["ups_authentication_check"] = self.__widgets["interface"].checkbutton1 + self.__widgets["ups_authentication_frame"] = self.__widgets["interface"].hbox1 + self.__widgets["ups_authentication_login"] = self.__widgets["interface"].entry2 + self.__widgets["ups_authentication_password"] = self.__widgets["interface"].entry3 + self.__widgets["ups_list_combo"] = self.__widgets["interface"].combobox1 + self.__widgets["ups_commands_combo"] = self.__widgets["interface"].ups_commands_combo + self.__widgets["ups_commands_button"] = self.__widgets["interface"].button8 + self.__widgets["ups_connect"] = self.__widgets["interface"].button2 + self.__widgets["ups_disconnect"] = self.__widgets["interface"].button7 + self.__widgets["ups_params_box"] = self.__widgets["interface"].vbox6 + self.__widgets["ups_infos"] = self.__widgets["interface"].notebook1 + self.__widgets["ups_vars_tree"] = self.__widgets["interface"].treeview1 + self.__widgets["ups_vars_refresh"] = self.__widgets["interface"].button9 + self.__widgets["ups_status_image"] = self.__widgets["interface"].image1 + self.__widgets["ups_status_left"] = self.__widgets["interface"].label10 + self.__widgets["ups_status_right"] = self.__widgets["interface"].label11 + self.__widgets["ups_status_time"] = self.__widgets["interface"].label15 + self.__widgets["menu_favorites_root"] = self.__widgets["interface"].menu2 + self.__widgets["menu_favorites"] = self.__widgets["interface"].menu2 + self.__widgets["menu_favorites_add"] = self.__widgets["interface"].menuitem4 + self.__widgets["menu_favorites_del"] = self.__widgets["interface"].menuitem5 + self.__widgets["progress_battery_charge"] = self.__widgets["interface"].progressbar1 + self.__widgets["progress_battery_load"] = self.__widgets["interface"].progressbar2 # Create the tray icon and connect it to the show/hide method... - self.__widgets["status_icon"] = gtk.StatusIcon() - self.__widgets["status_icon"].set_from_file( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "on_line.png" ) ) - self.__widgets["status_icon"].set_visible( True ) - self.__widgets["status_icon"].connect( "activate", self.tray_activated ) - - self.__widgets["ups_status_image"].set_from_file( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "on_line.png" ) ) - - # Define interface callbacks actions - self.__callbacks = { "on_window1_destroy" : self.quit, - "on_imagemenuitem1_activate" : self.gui_about_dialog, - "on_imagemenuitem5_activate" : self.quit, - "on_entry1_changed" : self.__check_gui_fields, - "on_entry2_changed" : self.__check_gui_fields, - "on_entry3_changed" : self.__check_gui_fields, - "on_checkbutton1_toggled" : self.__check_gui_fields, - "on_spinbutton1_value_changed" : self.__check_gui_fields, - "on_button1_clicked" : self.__update_ups_list, - "on_button2_clicked" : self.connect_to_ups, - "on_button7_clicked" : self.disconnect_from_ups, - "on_button9_clicked" : self.__gui_update_ups_vars_view, - "on_menuitem4_activate" : self.__gui_add_favorite, - "on_menuitem5_activate" : self.__gui_delete_favorite, - "on_treeview1_button_press_event" : self.__gui_ups_vars_selected - } - - # Connect the callbacks - self.__widgets["interface"].signal_autoconnect( self.__callbacks ) + self.__widgets["status_icon"] = QSystemTrayIcon( QIcon( self.__find_res_file( "pixmaps", "on_line.png" ) ) ) + self.__widgets["status_icon"].setVisible( True ) + self.__widgets["status_icon"].activated.connect( self.tray_activated ) + + self.__widgets["ups_status_image"].setPixmap( QPixmap( self.__find_res_file( "pixmaps", "on_line.png" ) ) ) + + # Connect interface callbacks actions + self.__widgets["main_window"].destroyed.connect( self.quit ) + self.__widgets["interface"].imagemenuitem1.triggered.connect( self.gui_about_dialog ) + self.__widgets["interface"].imagemenuitem5.triggered.connect( self.quit ) + self.__widgets["ups_host_entry"].textChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_authentication_login"].textChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_authentication_password"].textChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_authentication_check"].stateChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_port_entry"].valueChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_refresh_button"].clicked.connect( self.__update_ups_list ) + self.__widgets["ups_connect"].clicked.connect( self.connect_to_ups ) + self.__widgets["ups_disconnect"].clicked.connect( self.disconnect_from_ups ) + self.__widgets["ups_vars_refresh"].clicked.connect( self.__gui_update_ups_vars_view ) + self.__widgets["menu_favorites_add"].triggered.connect( self.__gui_add_favorite ) + self.__widgets["menu_favorites_del"].triggered.connect( self.__gui_delete_favorite ) + self.__widgets["ups_vars_tree"].doubleClicked.connect( self.__gui_ups_vars_selected ) # Remove the dummy combobox entry on UPS List and Commands - self.__widgets["ups_list_combo"].remove_text( 0 ) + self.__widgets["ups_list_combo"].removeItem( 0 ) # Set UPS vars treeview properties ----------------------------- - store = gtk.ListStore( gtk.gdk.Pixbuf, gobject.TYPE_STRING, gobject.TYPE_STRING ) - self.__widgets["ups_vars_tree"].set_model( store ) - self.__widgets["ups_vars_tree"].set_headers_visible( True ) + store = QStandardItemModel( 0, 3, self.__widgets["ups_vars_tree"] ) + self.__widgets["ups_vars_tree"].setModel( store ) + self.__widgets["ups_vars_tree"].setHeaderHidden( False ) + self.__widgets["ups_vars_tree"].setRootIsDecorated( False ) # Column 0 - cr = gtk.CellRendererPixbuf() - column = gtk.TreeViewColumn( '', cr ) - column.add_attribute( cr, 'pixbuf', 0 ) - self.__widgets["ups_vars_tree"].append_column( column ) + store.setHeaderData( 0, Qt.Horizontal, '' ) # Column 1 - cr = gtk.CellRendererText() - cr.set_property( 'editable', False ) - column = gtk.TreeViewColumn( _('Var name'), cr ) - column.set_sort_column_id( 1 ) - column.add_attribute( cr, 'text', 1 ) - self.__widgets["ups_vars_tree"].append_column( column ) + store.setHeaderData( 1, Qt.Horizontal, _('Var name') ) # Column 2 - cr = gtk.CellRendererText() - cr.set_property( 'editable', False ) - column = gtk.TreeViewColumn( _('Value'), cr ) - column.add_attribute( cr, 'text', 2 ) - self.__widgets["ups_vars_tree"].append_column( column ) + store.setHeaderData( 2, Qt.Horizontal, _('Value') ) + self.__widgets["ups_vars_tree"].header().setStretchLastSection( True ) - self.__widgets["ups_vars_tree"].get_model().set_sort_column_id( 1, gtk.SORT_ASCENDING ) + self.__widgets["ups_vars_tree"].sortByColumn( 1, Qt.AscendingOrder ) self.__widgets["ups_vars_tree_store"] = store - self.__widgets["ups_vars_tree"].set_size_request( -1, 50 ) + self.__widgets["ups_vars_tree"].setMinimumSize( 0, 50 ) #--------------------------------------------------------------- # UPS Commands combo box creation ------------------------------ - container = self.__widgets["ups_commands_button"].get_parent() - self.__widgets["ups_commands_button"].destroy() - self.__widgets["ups_commands_combo"] = gtk.ComboBox() + ups_commands_height = self.__widgets["ups_commands_combo"].size().height() * 2 + self.__widgets["ups_commands_combo"].setMinimumSize(0, ups_commands_height) + self.__widgets["ups_commands_combo"].setCurrentIndex( 0 ) - list_store = gtk.ListStore( gobject.TYPE_STRING ) + self.__widgets["ups_commands_button"].setMinimumSize(0, ups_commands_height) + self.__widgets["ups_commands_button"].clicked.connect( self.__gui_send_ups_command ) - self.__widgets["ups_commands_combo"].set_model( list_store ) - cell_renderer = gtk.CellRendererText() - cell_renderer.set_property( "xalign", 0 ) - self.__widgets["ups_commands_combo"].pack_start( cell_renderer, True ) - self.__widgets["ups_commands_combo"].add_attribute( cell_renderer, "markup", 0 ) - - container.pack_start( self.__widgets["ups_commands_combo"], True ) - self.__widgets["ups_commands_combo"].set_active( 0 ) - self.__widgets["ups_commands_combo"].show_all() - - self.__widgets["ups_commands_button"] = gtk.Button( stock=gtk.STOCK_EXECUTE ) - container.pack_start( self.__widgets["ups_commands_button"], True ) - self.__widgets["ups_commands_button"].show() - self.__widgets["ups_commands_button"].connect( "clicked", self.__gui_send_ups_command ) - - self.__widgets["ups_commands_combo_store"] = list_store + self.__widgets["ups_commands_combo_store"] = self.__widgets["ups_commands_combo"] #--------------------------------------------------------------- + self.gui_init_unconnected() + if ( cmd_opts.hidden != True ) : self.__widgets["main_window"].show() @@ -216,45 +195,70 @@ class interface : self.connect_to_ups() else : # Try to scan localhost for available ups and connect to it if there is only one - self.__widgets["ups_host_entry"].set_text( "localhost" ) + self.__widgets["ups_host_entry"].setText( "localhost" ) self.__update_ups_list() - if ( len( self.__widgets["ups_list_combo"].get_model() ) == 1 ) : + if self.__widgets["ups_list_combo"].count() == 1: self.connect_to_ups() + def exec( self ) : + self.__app.exec() + + def __find_res_file( self, ftype, filename ) : + filename = os.path.join( ftype, filename ) + # TODO: Skip checking application directory if installed + path = os.path.join( os.path.dirname( sys.argv[0] ), filename ) + if os.path.exists(path): + return path + path = QStandardPaths.locate(QStandardPaths.AppDataLocation, filename) + if os.path.exists(path): + return path + raise RuntimeError("Cannot find %s resource %s" % (ftype, filename)) + + def __find_icon_file( self ) : + filename = 'nut-monitor.png' + # TODO: Skip checking application directory if installed + path = os.path.join( os.path.dirname( sys.argv[0] ), "icons", "256x256", filename ) + if os.path.exists(path): + return path + path = QStandardPaths.locate(QStandardPaths.AppDataLocation, os.path.join( "icons", "hicolor", "256x256", "apps", filename ) ) + if os.path.exists(path): + return path + raise RuntimeError("Cannot find %s resource %s" % ('icon', filename)) + # Check if correct fields are filled to enable connection to the UPS def __check_gui_fields( self, widget=None ) : # If UPS list contains something, clear it - if self.__widgets["ups_list_combo"].get_active() != -1 : - self.__widgets["ups_list_combo"].get_model().clear() - self.__widgets["ups_connect"].set_sensitive( False ) - self.__widgets["menu_favorites_add"].set_sensitive( False ) + if self.__widgets["ups_list_combo"].currentIndex() != -1 : + self.__widgets["ups_list_combo"].clear() + self.__widgets["ups_connect"].setEnabled( False ) + self.__widgets["menu_favorites_add"].setEnabled( False ) # Host/Port selection - if len( self.__widgets["ups_host_entry"].get_text() ) > 0 : + if len( self.__widgets["ups_host_entry"].text() ) > 0 : sensitive = True # If authentication is selected, check that we have a login and password - if self.__widgets["ups_authentication_check"].get_active() : - if len( self.__widgets["ups_authentication_login"].get_text() ) == 0 : + if self.__widgets["ups_authentication_check"].isChecked() : + if len( self.__widgets["ups_authentication_login"].text() ) == 0 : sensitive = False - if len( self.__widgets["ups_authentication_password"].get_text() ) == 0 : + if len( self.__widgets["ups_authentication_password"].text() ) == 0 : sensitive = False - self.__widgets["ups_refresh_button"].set_sensitive( sensitive ) + self.__widgets["ups_refresh_button"].setEnabled( sensitive ) if not sensitive : - self.__widgets["ups_connect"].set_sensitive( False ) - self.__widgets["menu_favorites_add"].set_sensitive( False ) + self.__widgets["ups_connect"].setEnabled( False ) + self.__widgets["menu_favorites_add"].setEnabled( False ) else : - self.__widgets["ups_refresh_button"].set_sensitive( False ) - self.__widgets["ups_connect"].set_sensitive( False ) - self.__widgets["menu_favorites_add"].set_sensitive( False ) + self.__widgets["ups_refresh_button"].setEnabled( False ) + self.__widgets["ups_connect"].setEnabled( False ) + self.__widgets["menu_favorites_add"].setEnabled( False ) # Use authentication fields... - if self.__widgets["ups_authentication_check"].get_active() : - self.__widgets["ups_authentication_frame"].set_sensitive( True ) + if self.__widgets["ups_authentication_check"].isChecked() : + self.__widgets["ups_authentication_frame"].setEnabled( True ) else : - self.__widgets["ups_authentication_frame"].set_sensitive( False ) + self.__widgets["ups_authentication_frame"].setEnabled( False ) self.gui_status_message() @@ -271,41 +275,41 @@ class interface : #------------------------------------------------------------------- # Change the status icon and tray icon def change_status_icon( self, icon="on_line", blink=False ) : - self.__widgets["status_icon"].set_from_file( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "%s.png" % icon ) ) - self.__widgets["ups_status_image"].set_from_file( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "%s.png" % icon ) ) - self.__widgets["status_icon"].set_blinking( blink ) + self.__widgets["status_icon"].setIcon( QIcon( self.__find_res_file( "pixmaps", "%s.png" % icon ) ) ) + self.__widgets["ups_status_image"].setPixmap( QPixmap( self.__find_res_file( "pixmaps", "%s.png" % icon ) ) ) + # TODO self.__widgets["status_icon"].set_blinking( blink ) #------------------------------------------------------------------- # This method connects to the NUT server and retrieve availables UPSes # using connection parameters (host, port, login, pass...) def __update_ups_list( self, widget=None ) : - host = self.__widgets["ups_host_entry"].get_text() - port = int( self.__widgets["ups_port_entry"].get_value() ) + host = self.__widgets["ups_host_entry"].text() + port = int( self.__widgets["ups_port_entry"].value() ) login = None password = None - if self.__widgets["ups_authentication_check"].get_active() : - login = self.__widgets["ups_authentication_login"].get_text() - password = self.__widgets["ups_authentication_password"].get_text() + if self.__widgets["ups_authentication_check"].isChecked() : + login = self.__widgets["ups_authentication_login"].text() + password = self.__widgets["ups_authentication_password"].text() try : nut_handler = PyNUT.PyNUTClient( host=host, port=port, login=login, password=password ) upses = nut_handler.GetUPSList() - ups_list = list(upses.keys()) + ups_list = list(key.decode('ascii') for key in upses.keys()) ups_list.sort() # If UPS list contains something, clear it - self.__widgets["ups_list_combo"].get_model().clear() + self.__widgets["ups_list_combo"].clear() for current in ups_list : - self.__widgets["ups_list_combo"].append_text( current ) + self.__widgets["ups_list_combo"].addItem( current ) - self.__widgets["ups_list_combo"].set_active( 0 ) + self.__widgets["ups_list_combo"].setCurrentIndex( 0 ) - self.__widgets["ups_connect"].set_sensitive( True ) - self.__widgets["menu_favorites_add"].set_sensitive( True ) + self.__widgets["ups_connect"].setEnabled( True ) + self.__widgets["menu_favorites_add"].setEnabled( True ) self.gui_status_message( _("Found {0} devices on {1}").format( len( ups_list ), host ) ) @@ -321,73 +325,70 @@ class interface : self.gui_status_message( _("Disconnecting from device") ) self.disconnect_from_ups() - gtk.main_quit() + self.__app.quit() #------------------------------------------------------------------- # Method called when user wants to add a new favorite entry. It # displays a dialog to enable user to select the name of the favorite def __gui_add_favorite( self, widget=None ) : - dialog_interface = gtk.glade.XML( self.__glade_file, "dialog1" ) - dialog = dialog_interface.get_widget( "dialog1" ) - - self.__widgets["favorites_dialog_button_add"] = dialog_interface.get_widget("button3") + dialog_ui_file = self.__find_res_file( 'ui', "dialog1.ui" ) + dialog = PyQt5.uic.loadUi( dialog_ui_file ) # Define interface callbacks actions - callbacks = { "on_entry4_changed" : self.__gui_add_favorite_check_gui_fields } - dialog_interface.signal_autoconnect( callbacks ) - - self.__widgets["main_window"].set_sensitive( False ) - rc = dialog.run() - if rc == 1 : + def check_entry(val): + if self.__gui_add_favorite_check_gui_fields(val): + dialog.buttonBox.button(QDialogButtonBox.Ok).setEnabled( True ) + else: + dialog.buttonBox.button(QDialogButtonBox.Ok).setEnabled( False ) + dialog.entry4.textChanged.connect( check_entry ) + + self.__widgets["main_window"].setEnabled( False ) + rc = dialog.exec() + if rc == QDialog.Accepted : fav_data = {} - fav_data["host"] = self.__widgets["ups_host_entry"].get_text() - fav_data["port"] = "%d" % self.__widgets["ups_port_entry"].get_value() - fav_data["ups"] = self.__widgets["ups_list_combo"].get_active_text() - fav_data["auth"] = self.__widgets["ups_authentication_check"].get_active() + fav_data["host"] = self.__widgets["ups_host_entry"].text() + fav_data["port"] = "%d" % self.__widgets["ups_port_entry"].value() + fav_data["ups"] = self.__widgets["ups_list_combo"].currentText() + fav_data["auth"] = self.__widgets["ups_authentication_check"].isChecked() if fav_data["auth"] : - fav_data["login"] = self.__widgets["ups_authentication_login"].get_text() - fav_data["password"] = base64.b64encode( self.__widgets["ups_authentication_password"].get_text() ) + fav_data["login"] = self.__widgets["ups_authentication_login"].text() + fav_data["password"] = base64.b64encode( self.__widgets["ups_authentication_password"].text().encode('ascii') ).decode('ascii') - fav_name = dialog_interface.get_widget("entry4").get_text() + fav_name = dialog.entry4.text() self.__favorites[ fav_name ] = fav_data self.__gui_refresh_favorites_menu() # Save all favorites self.__save_favorites() - dialog.destroy() - self.__widgets["main_window"].set_sensitive( True ) + self.__widgets["main_window"].setEnabled( True ) #------------------------------------------------------------------- # Method called when user wants to delete an entry from favorites def __gui_delete_favorite( self, widget=None ) : - - dialog_interface = gtk.glade.XML( self.__glade_file, "dialog2" ) - dialog = dialog_interface.get_widget( "dialog2" ) + dialog_ui_file = self.__find_res_file( 'ui', "dialog2.ui" ) + dialog = PyQt5.uic.loadUi( dialog_ui_file ) # Remove the dummy combobox entry on list - dialog_interface.get_widget("combobox2").remove_text( 0 ) + dialog.combobox2.removeItem( 0 ) favs = list(self.__favorites.keys()) favs.sort() for current in favs : - dialog_interface.get_widget("combobox2").append_text( current ) + dialog.combobox2.addItem( current ) - dialog_interface.get_widget("combobox2").set_active( 0 ) + dialog.combobox2.setCurrentIndex( 0 ) - self.__widgets["main_window"].set_sensitive( False ) - rc = dialog.run() - fav_name = dialog_interface.get_widget("combobox2").get_active_text() - dialog.destroy() - self.__widgets["main_window"].set_sensitive( True ) + self.__widgets["main_window"].setEnabled( False ) + rc = dialog.exec() + fav_name = dialog.combobox2.currentText() + self.__widgets["main_window"].setEnabled( True ) - if ( rc == 1 ) : + if ( rc == QDialog.Accepted ) : # Remove entry, show confirmation dialog - md = gtk.MessageDialog( None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("Are you sure that you want to remove this favorite ?") ) - resp = md.run() - md.destroy() + resp = QMessageBox.question( None, self.__widgets["main_window"].windowTitle(), _("Are you sure that you want to remove this favorite ?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes ) - if ( resp == gtk.RESPONSE_YES ) : + if ( resp == QMessageBox.Yes ) : del self.__favorites[ fav_name ] self.__gui_refresh_favorites_menu() self.__save_favorites() @@ -401,37 +402,35 @@ class interface : # If auth is activated, process it before other fields to avoir weird # reactions with the 'check_gui_fields' function. if ( self.__favorites[fav_name].get("auth", False ) ) : - self.__widgets["ups_authentication_check"].set_active( True ) - self.__widgets["ups_authentication_login"].set_text( self.__favorites[fav_name].get("login","") ) - self.__widgets["ups_authentication_password"].set_text( self.__favorites[fav_name].get("password","") ) + self.__widgets["ups_authentication_check"].setChecked( True ) + self.__widgets["ups_authentication_login"].setText( self.__favorites[fav_name].get("login","") ) + self.__widgets["ups_authentication_password"].setText( self.__favorites[fav_name].get("password","") ) - self.__widgets["ups_host_entry"].set_text( self.__favorites[fav_name].get("host","") ) - self.__widgets["ups_port_entry"].set_value( float(self.__favorites[fav_name].get("port",3493.0)) ) + self.__widgets["ups_host_entry"].setText( self.__favorites[fav_name].get("host","") ) + self.__widgets["ups_port_entry"].setValue( int( self.__favorites[fav_name].get( "port", 3493 ) ) ) # Clear UPS list and add current UPS name - self.__widgets["ups_list_combo"].get_model().clear() + self.__widgets["ups_list_combo"].clear() - self.__widgets["ups_list_combo"].append_text( self.__favorites[fav_name].get("ups","") ) - self.__widgets["ups_list_combo"].set_active( 0 ) + self.__widgets["ups_list_combo"].addItem( self.__favorites[fav_name].get("ups","") ) + self.__widgets["ups_list_combo"].setCurrentIndex( 0 ) # Activate the connect button - self.__widgets["ups_connect"].set_sensitive( True ) + self.__widgets["ups_connect"].setEnabled( True ) self.gui_status_message( _("Loaded '%s'") % fav_name ) #------------------------------------------------------------------- # Send the selected command to the UPS def __gui_send_ups_command( self, widget=None ) : - offset = self.__widgets["ups_commands_combo"].get_active() - cmd = self.__ups_commands[ offset ] + offset = self.__widgets["ups_commands_combo"].currentIndex() + cmd = self.__ups_commands[ offset ].decode('ascii') - md = gtk.MessageDialog( None, gtk.DIALOG_MODAL, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, _("Are you sure that you want to send\n'%s' to the device ?") % cmd ) - self.__widgets["main_window"].set_sensitive( False ) - resp = md.run() - md.destroy() - self.__widgets["main_window"].set_sensitive( True ) + self.__widgets["main_window"].setEnabled( False ) + resp = QMessageBox.question( None, self.__widgets["main_window"].windowTitle(), _("Are you sure that you want to send '%s' to the device ?") % cmd, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes ) + self.__widgets["main_window"].setEnabled( True ) - if ( resp == gtk.RESPONSE_YES ) : + if ( resp == QMessageBox.Yes ) : try : self.__ups_handler.RunUPSCommand( self.__current_ups, cmd ) self.gui_status_message( _("Sent '{0}' command to {1}").format( cmd, self.__current_ups ) ) @@ -442,36 +441,27 @@ class interface : #------------------------------------------------------------------- # Method called when user clicks on the UPS vars treeview. If the user # performs a double click on a RW var, the GUI shows the update var dialog. - def __gui_ups_vars_selected( self, widget, event ) : - # Check if it's a double click... - if ( (event.button == 1) and (event.type == gtk.gdk._2BUTTON_PRESS) ) : - treeselection = self.__widgets["ups_vars_tree"].get_selection() - (model,iter) = treeselection.get_selected() + def __gui_ups_vars_selected( self, index ) : + if True : + model = self.__widgets["ups_vars_tree_store"] try : - ups_var = model.get_value( iter, 1 ) + ups_var = model.data( index.siblingAtColumn(1) ).encode('ascii') if ( ups_var in self.__ups_rw_vars ) : # The selected var is RW, then we can show the update dialog - dialog_interface = gtk.glade.XML( self.__glade_file, "dialog3" ) - dialog = dialog_interface.get_widget( "dialog3" ) - lab = dialog_interface.get_widget( "label9" ) - lab.set_markup( _("Enter a new value for the variable.\n\n{0} = {1} (current value)").format( ups_var, self.__ups_rw_vars.get(ups_var)) ) + cur_val = self.__ups_rw_vars.get(ups_var).decode('ascii') - str = dialog_interface.get_widget( "entry5" ) - str.set_text( self.__ups_rw_vars.get(ups_var) ) + self.__widgets["main_window"].setEnabled( False ) + new_val, rc = QInputDialog.getText( None, self.__widgets["main_window"].windowTitle(), _("Enter a new value for the variable.

{0} = {1} (current value)").format( ups_var, cur_val), QLineEdit.Normal, cur_val ) + self.__widgets["main_window"].setEnabled( True ) - self.__widgets["main_window"].set_sensitive( False ) - rc = dialog.run() - new_val = str.get_text() - dialog.destroy() - self.__widgets["main_window"].set_sensitive( True ) - - if ( rc == 1 ) : + if ( rc ) : try : - self.__ups_handler.SetRWVar( ups=self.__current_ups, var=ups_var, value=new_val ) + self.__ups_handler.SetRWVar( ups=self.__current_ups, var=ups_var.decode('ascii'), value=new_val ) self.gui_status_message( _("Updated variable on %s") % self.__current_ups ) # Change the value on the local dict to update the GUI + new_val = new_val.encode('ascii') self.__ups_vars[ups_var] = new_val self.__ups_rw_vars[ups_var] = new_val self.__gui_update_ups_vars_view() @@ -493,7 +483,7 @@ class interface : # Refresh the content of the favorites menu according to the defined favorites def __gui_refresh_favorites_menu( self ) : for current in self.__fav_menu_items : - current.destroy() + self.__widgets["menu_favorites"].removeAction(current) self.__fav_menu_items = list() @@ -501,29 +491,27 @@ class interface : items.sort() for current in items : - menu_item = gtk.MenuItem( current ) - menu_item.show() + menu_item = QAction( current ) self.__fav_menu_items.append( menu_item ) - self.__widgets["menu_favorites"].append( menu_item ) + self.__widgets["menu_favorites"].addAction( menu_item ) - menu_item.connect_object( "activate", self.__gui_load_favorite, current ) + menu_item.triggered.connect( lambda: self.__gui_load_favorite( current ) ) if len( items ) > 0 : - self.__widgets["menu_favorites_del"].set_sensitive( True ) + self.__widgets["menu_favorites_del"].setEnabled( True ) else : - self.__widgets["menu_favorites_del"].set_sensitive( False ) + self.__widgets["menu_favorites_del"].setEnabled( False ) #------------------------------------------------------------------- # In 'add favorites' dialog, this method compares the content of the # text widget representing the name of the new favorite with existing # ones. If they match, the 'add' button will be set to non sensitive # to avoid creating entries with the same name. - def __gui_add_favorite_check_gui_fields( self, widget=None ) : - fav_name = widget.get_text() + def __gui_add_favorite_check_gui_fields( self, fav_name ) : if ( len( fav_name ) > 0 ) and ( fav_name not in list(self.__favorites.keys()) ) : - self.__widgets["favorites_dialog_button_add"].set_sensitive( True ) + return True else : - self.__widgets["favorites_dialog_button_add"].set_sensitive( False ) + return False #------------------------------------------------------------------- # Load and parse favorites @@ -560,7 +548,7 @@ class interface : fav_data["login"] = conf.get( current, "login" ) try : - fav_data["password"] = base64.decodestring( conf.get( current, "password" ) ) + fav_data["password"] = base64.decodebytes( conf.get( current, "password" ).encode('ascii') ).decode('ascii') except : # If the password is not in base64, let the field empty @@ -582,7 +570,7 @@ class interface : # If path does not exists, try to create it if ( not os.path.exists( self.__favorites_file ) ) : try : - os.makedirs( self.__favorites_path, mode=self.DESIRED_FAVORITES_DIRECTORY_MODE ) + os.makedirs( self.__favorites_path, mode=self.DESIRED_FAVORITES_DIRECTORY_MODE, exist_ok=True ) except : self.gui_status_message( _("Error while creating configuration folder (%s)") % sys.exc_info()[1] ) @@ -590,6 +578,8 @@ class interface : for current in list(self.__favorites.keys()) : save_conf.add_section( current ) for k, v in self.__favorites[ current ].items() : + if isinstance( v, bool ) : + v = str( v ) save_conf.set( current, k, v ) try : @@ -604,67 +594,87 @@ class interface : #------------------------------------------------------------------- # Display the about dialog def gui_about_dialog( self, widget=None ) : - dialog_interface = gtk.glade.XML( self.__glade_file, "aboutdialog1" ) - dialog = dialog_interface.get_widget( "aboutdialog1" ) - - self.__widgets["main_window"].set_sensitive( False ) - dialog.run() - dialog.destroy() - self.__widgets["main_window"].set_sensitive( True ) + self.__widgets["main_window"].adjustSize() + dialog_ui_file = self.__find_res_file( 'ui', "aboutdialog1.ui" ) + + dialog = PyQt5.uic.loadUi( dialog_ui_file ) + dialog.icon.setPixmap( QPixmap( self.__find_icon_file() ) ) + + credits_button = QPushButton( dialog ) + credits_button.setText( _("C&redits") ) + credits_button.setIcon( dialog.style().standardIcon( QStyle.SP_MessageBoxInformation ) ) + credits_button.clicked.connect( self.gui_about_credits ) + + licence_button = QPushButton( dialog ) + licence_button.setText( _("&Licence") ) + licence_button.clicked.connect( self.gui_about_licence ) + + dialog.buttonBox.addButton( credits_button, QDialogButtonBox.HelpRole ) + dialog.buttonBox.addButton( licence_button, QDialogButtonBox.HelpRole ) + + self.__widgets["main_window"].setEnabled( False ) + dialog.exec() + self.__widgets["main_window"].setEnabled( True ) + + def gui_about_credits( self ) : + QMessageBox.about( None, _("Credits"), _(""" +Written by: +David Goncalves + +Translated by: +David Goncalves - Français +Daniele Pezzini - Italiano + """).strip() ) + + def gui_about_licence( self ) : + QMessageBox.about( None, _("Licence"), _(""" +Copyright (C) 2010 David Goncalves + +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 +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + """).strip() ) #------------------------------------------------------------------- # Display a message on the status bar. The message is also set as # tooltip to enable users to see long messages. def gui_status_message( self, msg="" ) : - context_id = self.__widgets["status_bar"].get_context_id("Infos") - self.__widgets["status_bar"].pop( context_id ) - - if ( platform.system() == "Windows" ) : - text = msg.decode("cp1250").encode("utf8") - else : - text = msg + text = msg - message_id = self.__widgets["status_bar"].push( context_id, text.replace("\n", "") ) - self.__widgets["status_bar"].set_tooltip_text( text ) + message_id = self.__widgets["status_bar"].showMessage( text.replace("\n", "") ) + self.__widgets["status_bar"].setToolTip( text ) #------------------------------------------------------------------- - # Display a notification using PyNotify with an optional icon + # Display a notification using QSystemTrayIcon with an optional icon def gui_status_notification( self, message="", icon_file="" ) : - # Try to init pynotify - try : - import pynotify - pynotify.init( "NUT Monitor" ) - - if ( icon_file != "" ) : - icon = "file://%s" % os.path.abspath( os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", icon_file ) ) - else : - icon = None - - notif = pynotify.Notification( "NUT Monitor", message, icon ) - notif.show() - - except : - pass + if ( icon_file != "" ) : + icon = QIcon( os.path.abspath( self.__find_res_file( "pixmaps", icon_file ) ) ) + else : + icon = None - #------------------------------------------------------------------- - # Let GTK refresh GUI :) - def refresh_gui( self ) : - while gtk.events_pending() : - gtk.main_iteration( False ) - return( True ) + self.__widgets["status_icon"].showMessage( "NUT Monitor", message, icon ) #------------------------------------------------------------------- # Connect to the selected UPS using parameters (host,port,login,pass) def connect_to_ups( self, widget=None ) : - host = self.__widgets["ups_host_entry"].get_text() - port = int( self.__widgets["ups_port_entry"].get_value() ) + host = self.__widgets["ups_host_entry"].text() + port = int( self.__widgets["ups_port_entry"].value() ) login = None password = None - if self.__widgets["ups_authentication_check"].get_active() : - login = self.__widgets["ups_authentication_login"].get_text() - password = self.__widgets["ups_authentication_password"].get_text() + if self.__widgets["ups_authentication_check"].isChecked() : + login = self.__widgets["ups_authentication_login"].text() + password = self.__widgets["ups_authentication_password"].text() try : self.__ups_handler = PyNUT.PyNUTClient( host=host, port=port, login=login, password=password ) @@ -676,9 +686,9 @@ class interface : # Check if selected UPS exists on server... srv_upses = self.__ups_handler.GetUPSList() - self.__current_ups = self.__widgets["ups_list_combo"].get_active_text() + self.__current_ups = self.__widgets["ups_list_combo"].currentText() - if self.__current_ups not in srv_upses : + if self.__current_ups.encode('ascii') not in srv_upses : self.gui_status_message( _("Device '%s' not found on server") % self.__current_ups ) self.gui_status_notification( _("Device '%s' not found on server") % self.__current_ups, "warning.png" ) return @@ -687,8 +697,8 @@ class interface : self.__widgets["ups_connect"].hide() self.__widgets["ups_disconnect"].show() self.__widgets["ups_infos"].show() - self.__widgets["ups_params_box"].set_sensitive( False ) - self.__widgets["menu_favorites_root"].set_sensitive( False ) + self.__widgets["ups_params_box"].setEnabled( False ) + self.__widgets["menu_favorites_root"].setEnabled( False ) self.__widgets["ups_params_box"].hide() commands = self.__ups_handler.GetUPSCommands( self.__current_ups ) @@ -698,9 +708,10 @@ class interface : # Refresh UPS commands combo box self.__widgets["ups_commands_combo_store"].clear() for desc in self.__ups_commands : - self.__widgets["ups_commands_combo_store"].append( [ "%s\n%s" % ( desc, commands[desc] ) ] ) + # TODO: Style as "%s
%s" + self.__widgets["ups_commands_combo_store"].addItem( "%s\n%s" % ( desc.decode('ascii'), commands[desc].decode('ascii') ) ) - self.__widgets["ups_commands_combo"].set_active( 0 ) + self.__widgets["ups_commands_combo"].setCurrentIndex( 0 ) # Update UPS vars manually before the thread self.__ups_vars = self.__ups_handler.GetUPSVars( self.__current_ups ) @@ -708,7 +719,8 @@ class interface : self.__gui_update_ups_vars_view() # Try to resize the main window... - self.__widgets["main_window"].resize( 1, 1 ) + # FIXME: For some reason, calling this immediately doesn't work right + QTimer.singleShot(10, self.__widgets["main_window"].adjustSize) # Start the GUI updater thread self.__gui_thread = gui_updater( self ) @@ -724,33 +736,43 @@ class interface : vars = self.__ups_vars rwvars = self.__ups_rw_vars - self.__widgets["ups_vars_tree_store"].clear() + self.__widgets["ups_vars_tree_store"].removeRows(0, self.__widgets["ups_vars_tree_store"].rowCount()) for k,v in vars.items() : if ( k in rwvars ) : - icon_file = os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "var-rw.png" ) + icon_file = self.__find_res_file( "pixmaps", "var-rw.png" ) else : - icon_file = os.path.join( os.path.dirname( sys.argv[0] ), "pixmaps", "var-ro.png" ) - - icon = gtk.gdk.pixbuf_new_from_file( icon_file ) - self.__widgets["ups_vars_tree_store"].append( [ icon, k, v ] ) + icon_file = self.__find_res_file( "pixmaps", "var-ro.png" ) + icon = QIcon( icon_file ) + item_icon = QStandardItem(icon, '') + item_icon.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) + item_var_name = QStandardItem( k.decode('ascii') ) + item_var_name.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) + item_var_val = QStandardItem( v.decode('ascii') ) + item_var_val.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) + self.__widgets["ups_vars_tree_store"].appendRow( (item_icon, item_var_name, item_var_val) ) + self.__widgets["ups_vars_tree"].resizeColumnToContents( 0 ) + self.__widgets["ups_vars_tree"].resizeColumnToContents( 1 ) - #------------------------------------------------------------------- - # Disconnect from the UPS - def disconnect_from_ups( self, widget=None ) : + def gui_init_unconnected( self ) : self.__connected = False self.__widgets["ups_connect"].show() self.__widgets["ups_disconnect"].hide() self.__widgets["ups_infos"].hide() - self.__widgets["ups_params_box"].set_sensitive( True ) - self.__widgets["menu_favorites_root"].set_sensitive( True ) - self.__widgets["status_icon"].set_tooltip_markup( _("Not connected") ) + self.__widgets["ups_params_box"].setEnabled( True ) + self.__widgets["menu_favorites_root"].setEnabled( True ) + self.__widgets["status_icon"].setToolTip( _("Not connected") ) self.__widgets["ups_params_box"].show() # Try to resize the main window... - self.__widgets["main_window"].resize( 1, 1 ) + self.__widgets["main_window"].adjustSize() + + #------------------------------------------------------------------- + # Disconnect from the UPS + def disconnect_from_ups( self, widget=None ) : + self.gui_init_unconnected() # Stop the GUI updater thread self.__gui_thread.stop_thread() @@ -763,7 +785,7 @@ class interface : #----------------------------------------------------------------------- # GUI Updater class # This class updates the main gui with data from connected UPS -class gui_updater( threading.Thread ) : +class gui_updater : __parent_class = None __stop_thread = False @@ -772,23 +794,28 @@ class gui_updater( threading.Thread ) : threading.Thread.__init__( self ) self.__parent_class = parent_class - def run( self ) : + def start( self ) : + self.__timer = QTimer() + self.__timer.timeout.connect(self.__update) + self.__timer.start(1000) + + def __update( self ) : ups = self.__parent_class._interface__current_ups was_online = True # Define a dict containing different UPS status - status_mapper = { "LB" : "%s" % _("Low batteries"), - "RB" : "%s" % _("Replace batteries !"), - "BYPASS" : "Bypass %s" % _("(no battery protection)"), - "CAL" : _("Performing runtime calibration"), - "OFF" : "%s (%s)" % ( _("Offline"), _("not providing power to the load") ), - "OVER" : "%s (%s)" % ( _("Overloaded !"), _("there is too much load for device") ), - "TRIM" : _("Triming (UPS is triming incoming voltage)"), - "BOOST" : _("Boost (UPS is boosting incoming voltage)") + status_mapper = { b"LB" : "%s" % _("Low batteries"), + b"RB" : "%s" % _("Replace batteries !"), + b"BYPASS" : "Bypass %s" % _("(no battery protection)"), + b"CAL" : _("Performing runtime calibration"), + b"OFF" : "%s (%s)" % ( _("Offline"), _("not providing power to the load") ), + b"OVER" : "%s (%s)" % ( _("Overloaded !"), _("there is too much load for device") ), + b"TRIM" : _("Triming (UPS is triming incoming voltage)"), + b"BOOST" : _("Boost (UPS is boosting incoming voltage)") } - while not self.__stop_thread : + if not self.__stop_thread : try : vars = self.__parent_class._interface__ups_handler.GetUPSVars( ups ) self.__parent_class._interface__ups_vars = vars @@ -798,16 +825,16 @@ class gui_updater( threading.Thread ) : text_right = "" status_text = "" - text_left += "%s\n" % _("Device status :") + text_left += "%s
" % _("Device status :") - if ( vars.get("ups.status").find("OL") != -1 ) : - text_right += "%s" % _("Online") + if ( vars.get(b"ups.status").find(b"OL") != -1 ) : + text_right += "%s" % _("Online") if not was_online : self.__parent_class.change_status_icon( "on_line", blink=False ) was_online = True - if ( vars.get("ups.status").find("OB") != -1 ) : - text_right += "%s" % _("On batteries") + if ( vars.get(b"ups.status").find(b"OB") != -1 ) : + text_right += "%s" % _("On batteries") if was_online : self.__parent_class.change_status_icon( "on_battery", blink=True ) self.__parent_class.gui_status_notification( _("Device is running on batteries"), "on_battery.png" ) @@ -815,80 +842,85 @@ class gui_updater( threading.Thread ) : # Check for additionnal information for k,v in status_mapper.items() : - if vars.get("ups.status").find(k) != -1 : + if vars.get(b"ups.status").find(k) != -1 : if ( text_right != "" ) : text_right += " - %s" % v else : text_right += "%s" % v # CHRG and DISCHRG cannot be trated with the previous loop ;) - if ( vars.get("ups.status").find("DISCHRG") != -1 ) : + if ( vars.get(b"ups.status").find(b"DISCHRG") != -1 ) : text_right += " - %s" % _("discharging") - elif ( vars.get("ups.status").find("CHRG") != -1 ) : + elif ( vars.get(b"ups.status").find(b"CHRG") != -1 ) : text_right += " - %s" % _("charging") status_text += text_right - text_right += "\n" + text_right += "
" - if ( "ups.mfr" in vars ) : - text_left += "%s\n\n" % _("Model :") - text_right += "%s\n%s\n" % ( vars.get("ups.mfr",""), vars.get("ups.model","") ) + if ( b"ups.mfr" in vars ) : + text_left += "%s

" % _("Model :") + text_right += "%s
%s
" % ( + vars.get(b"ups.mfr",b"").decode('ascii'), + vars.get(b"ups.model",b"").decode('ascii'), + ) - if ( "ups.temperature" in vars ) : - text_left += "%s\n" % _("Temperature :") - text_right += "%s\n" % int( float( vars.get( "ups.temperature", 0 ) ) ) + if ( b"ups.temperature" in vars ) : + text_left += "%s
" % _("Temperature :") + text_right += "%s
" % int( float( vars.get( b"ups.temperature", 0 ) ) ) - if ( "battery.voltage" in vars ) : - text_left += "%s\n" % _("Battery voltage :") - text_right += "%sv\n" % vars.get( "battery.voltage", 0 ) + if ( b"battery.voltage" in vars ) : + text_left += "%s
" % _("Battery voltage :") + text_right += "%sv
" % (vars.get( b"battery.voltage", 0 ).decode('ascii'),) - self.__parent_class._interface__widgets["ups_status_left"].set_markup( text_left[:-1] ) - self.__parent_class._interface__widgets["ups_status_right"].set_markup( text_right[:-1] ) + self.__parent_class._interface__widgets["ups_status_left"].setText( text_left[:-4] ) + self.__parent_class._interface__widgets["ups_status_right"].setText( text_right[:-4] ) # UPS load and battery charge progress bars - if ( "battery.charge" in vars ) : - charge = vars.get( "battery.charge", "0" ) - self.__parent_class._interface__widgets["progress_battery_charge"].set_fraction( float( charge ) / 100.0 ) - self.__parent_class._interface__widgets["progress_battery_charge"].set_text( "%s %%" % int( float( charge ) ) ) - status_text += "\n%s %s%%" % ( _("Battery charge :"), int( float( charge ) ) ) + self.__parent_class._interface__widgets["progress_battery_charge"].setRange( 0, 100 ) + if ( b"battery.charge" in vars ) : + charge = vars.get( b"battery.charge", "0" ) + self.__parent_class._interface__widgets["progress_battery_charge"].setValue( int( float( charge ) ) ) + self.__parent_class._interface__widgets["progress_battery_charge"].resetFormat() + status_text += "
%s %s%%" % ( _("Battery charge :"), int( float( charge ) ) ) else : - self.__parent_class._interface__widgets["progress_battery_charge"].set_fraction( 0.0 ) - self.__parent_class._interface__widgets["progress_battery_charge"].set_text( _("Not available") ) - - if ( "ups.load" in vars ) : - load = vars.get( "ups.load", "0" ) - self.__parent_class._interface__widgets["progress_battery_load"].set_fraction( float( load ) / 100.0 ) - self.__parent_class._interface__widgets["progress_battery_load"].set_text( "%s %%" % int( float( load ) ) ) - status_text += "\n%s %s%%" % ( _("UPS load :"), int( float( load ) ) ) + self.__parent_class._interface__widgets["progress_battery_charge"].setValue( 0 ) + self.__parent_class._interface__widgets["progress_battery_charge"].setFormat( _("Not available") ) + # FIXME: Some themes don't draw text, so swap it with a QLabel? + + self.__parent_class._interface__widgets["progress_battery_load"].setRange( 0, 100 ) + if ( b"ups.load" in vars ) : + load = vars.get( b"ups.load", "0" ) + self.__parent_class._interface__widgets["progress_battery_load"].setValue( int( float( load ) ) ) + self.__parent_class._interface__widgets["progress_battery_load"].resetFormat() + status_text += "
%s %s%%" % ( _("UPS load :"), int( float( load ) ) ) else : - self.__parent_class._interface__widgets["progress_battery_load"].set_fraction( 0.0 ) - self.__parent_class._interface__widgets["progress_battery_load"].set_text( _("Not available") ) + self.__parent_class._interface__widgets["progress_battery_load"].setValue( 0 ) + self.__parent_class._interface__widgets["progress_battery_load"].setFormat( _("Not available") ) + # FIXME: Some themes don't draw text, so swap it with a QLabel? - if ( "battery.runtime" in vars ) : - autonomy = int( float( vars.get( "battery.runtime", 0 ) ) ) + if ( b"battery.runtime" in vars ) : + autonomy = int( float( vars.get( b"battery.runtime", 0 ) ) ) if ( autonomy >= 3600 ) : info = time.strftime( _("%H hours %M minutes %S seconds"), time.gmtime( autonomy ) ) elif ( autonomy > 300 ) : info = time.strftime( _("%M minutes %S seconds"), time.gmtime( autonomy ) ) else : - info = time.strftime( _("%M minutes %S seconds"), time.gmtime( autonomy ) ) + info = time.strftime( _("%M minutes %S seconds"), time.gmtime( autonomy ) ) else : info = _("Not available") - self.__parent_class._interface__widgets["ups_status_time"].set_markup( info ) + self.__parent_class._interface__widgets["ups_status_time"].setText( info ) # Display UPS status as tooltip for tray icon - self.__parent_class._interface__widgets["status_icon"].set_tooltip_markup( status_text ) + self.__parent_class._interface__widgets["status_icon"].setToolTip( status_text ) except : self.__parent_class.gui_status_message( _("Error from '{0}' ({1})").format( ups, sys.exc_info()[1] ) ) self.__parent_class.gui_status_notification( _("Error from '{0}'\n{1}").format( ups, sys.exc_info()[1] ), "warning.png" ) - time.sleep( 1 ) - def stop_thread( self ) : - self.__stop_thread = True + self.__timer.stop() #----------------------------------------------------------------------- @@ -903,10 +935,10 @@ if __name__ == "__main__" : gettext.textdomain( APP ) _ = gettext.gettext - for module in ( gettext, gtk.glade ) : + for module in ( gettext, ) : module.bindtextdomain( APP, DIR ) module.textdomain( APP ) - gui = interface() - gtk.main() + gui = interface(sys.argv) + gui.exec() diff --git a/scripts/python/app/README b/scripts/python/app/README index b7acd299fa..7bee3a2977 100644 --- a/scripts/python/app/README +++ b/scripts/python/app/README @@ -1,7 +1,7 @@ NUT-Monitor is a graphical application to access and manage UPSes connected to a NUT (Network UPS Tools) server. -This application (written in Python + GTK) uses the python-pynut class +This application (written in Python + Qt) uses the python-pynut class (available at http://www.lestat.st) David Goncalves diff --git a/scripts/python/app/gui-1.3.glade b/scripts/python/app/gui-1.3.glade deleted file mode 100644 index eb42aba3b0..0000000000 --- a/scripts/python/app/gui-1.3.glade +++ /dev/null @@ -1,1073 +0,0 @@ - - - - - - NUT Monitor - center - battery - - - - True - - - True - - - True - _File - True - - - True - - - gtk-about - True - True - True - - - - - - True - - - - - gtk-quit - True - True - True - - - - - - - - - - True - F_avorites - True - - - True - - - gtk-add - True - False - True - True - - - - - - gtk-delete - True - False - True - True - - - - - - True - - - - - - - - - False - 0 - - - - - True - - - True - 0.5 - in - - - True - 2 - - - True - - - True - - - True - 2 - 3 - - - True - 1 - Host / Port : - - - GTK_FILL - - - - - True - 1 - Device : - - - 1 - 2 - GTK_FILL - - - - - True - True - - - - - 1 - 2 - - - - - True - True - 5 - - 5 - 3493 0 65535 1 10 0 - True - - - - 2 - 3 - GTK_FILL - - - - - True - None - - - 1 - 2 - 1 - 2 - - - - - gtk-refresh - True - False - True - True - True - - - - 2 - 3 - 1 - 2 - GTK_FILL - GTK_FILL - - - - - False - False - 0 - - - - - Use authentication - True - True - False - True - - - - False - 1 - - - - - True - False - - - True - 1 - Login / Password : - - - 0 - - - - - True - True - - - - - 1 - - - - - True - True - False - - - - - 2 - - - - - False - 2 - - - - - True - - - False - 4 - 3 - - - - - 0 - - - - - True - - - gtk-connect - True - False - True - True - True - - - - 0 - - - - - gtk-disconnect - True - True - True - - - - 1 - - - - - False - 1 - - - - - - - - - True - NUT Server - True - - - label_item - - - - - False - 0 - - - - - True - True - - - True - 2 - - - True - - - True - gtk-missing-image - 6 - - - False - 0 - - - - - True - 0 - in - - - True - 2 - 2 - 2 - 2 - - - True - - - True - 1 - 0 - 2 - label - right - - - False - 0 - - - - - True - 0 - 0 - 2 - label - - - 1 - - - - - - - - - - label_item - - - - - 1 - - - - - 0 - - - - - True - 4 - 2 - 4 - - - True - 1 - 2 - Battery charge : - True - right - - - GTK_FILL - - - - - True - 1 - 2 - Current load : - True - right - - - 1 - 2 - GTK_FILL - - - - - True - - - 1 - 2 - - - - - True - - - 1 - 2 - 1 - 2 - - - - - True - 1 - 2 - Remaining time : - right - - - 2 - 3 - GTK_FILL - - - - - True - 0 - in - - - True - - - True - N/A - True - - - - - - - - label_item - - - - - 1 - 2 - 2 - 3 - - - - - True - 1 - 2 - Device commands : - True - right - - - 3 - 4 - GTK_FILL - - - - - True - - - - - - gtk-execute - True - True - True - True - - - - False - False - 1 - - - - - 1 - 2 - 3 - 4 - - - - - False - 1 - - - - - - - - - - True - 2 - Device status - - - False - tab - - - - - True - 2 - - - True - 0 - in - - - True - 1 - 1 - 1 - 1 - - - True - True - automatic - automatic - - - True - True - - - - - - - - - - - label_item - - - - - 0 - - - - - gtk-refresh - True - True - True - True - - - - False - 1 - - - - - 1 - - - - - True - Device vars - - - 1 - False - tab - - - - - 1 - - - - - 1 - - - - - True - 2 - - - False - 2 - - - - - - - 5 - True - center-always - normal - - - True - 2 - - - True - 0 - in - - - True - 4 - 4 - 4 - 4 - - - True - - - True - Enter a name for this favorite - -<span color="#808080">You cannot re-use a name from another entry</span> - - True - - - 0 - - - - - True - True - - - - - False - 1 - - - - - - - - - - label_item - - - - - 1 - - - - - True - spread - - - gtk-add - 1 - True - False - True - True - True - - - False - False - 0 - - - - - gtk-cancel - True - True - True - True - - - False - False - 1 - - - - - False - end - 0 - - - - - - - 5 - True - center-always - normal - - - True - 2 - - - True - 0 - in - - - True - 4 - 4 - 4 - 4 - - - True - - - True - -Please select the favorite that you -want to delete from list... - - True - - - 0 - - - - - True - 0 - <None> - - - False - 1 - - - - - - - - - - label_item - - - - - 1 - - - - - True - spread - - - gtk-delete - 1 - True - True - True - True - - - False - False - 0 - - - - - gtk-cancel - True - True - True - True - - - False - False - 1 - - - - - False - end - 0 - - - - - - - 5 - True - center-always - normal - - - True - 2 - - - True - 0 - in - - - True - 4 - 4 - 4 - 4 - - - True - - - True - 4 - - - True - gtk-edit - 6 - - - False - 4 - 0 - - - - - True - Enter a new value for the variable. - - True - - - 2 - 1 - - - - - 0 - - - - - True - True - - - - False - 1 - - - - - - - - - - label_item - - - - - 1 - - - - - True - spread - - - gtk-save - 1 - True - True - True - True - - - False - False - 0 - - - - - gtk-cancel - True - True - True - True - - - False - False - 1 - - - - - False - end - 0 - - - - - - - 5 - True - center-always - normal - NUT-Monitor - 1.3.1 - Copyright (c) 2010 David Goncalves - GUI to manage devices connected a NUT server. - -For more information about NUT (Network UPS Tools) -please visit the author's website. - -http://www.networkupstools.org - - http://www.lestat.st/informatique/projets/nut-monitor-en - http://www.lestat.st - Copyright (C) 2010 David Goncalves <david@lestat.st> - -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 -the Free Software Foundation; either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. - David Goncalves <david@lestat.st> - David Goncalves <david@lestat.st> - Français -Daniele Pezzini <hyouko@gmail.com> - Italiano - - - True - 2 - - - - - - True - spread - - - False - end - 0 - - - - - - diff --git a/scripts/python/app/gui-1.3.glade.h b/scripts/python/app/gui-1.3.glade.h deleted file mode 100644 index 8b9c95f164..0000000000 --- a/scripts/python/app/gui-1.3.glade.h +++ /dev/null @@ -1,51 +0,0 @@ -char *s = N_("NUT Monitor"); -char *s = N_("_File"); -char *s = N_("F_avorites"); -char *s = N_("Host / Port : "); -char *s = N_("Device : "); -char *s = N_("None"); -char *s = N_("Use authentication"); -char *s = N_("Login / Password : "); -char *s = N_(" NUT Server "); -char *s = N_("label"); -char *s = N_("Battery charge :"); -char *s = N_("Current load :"); -char *s = N_("Remaining time :"); -char *s = N_("N/A"); -char *s = N_("Device commands :"); -char *s = N_("Device status"); -char *s = N_("Device vars"); -char *s = N_("Enter a name for this favorite\n" - "\n" - "You cannot re-use a name from another entry\n" - ""); -char *s = N_("\n" - "Please select the favorite that you\n" - "want to delete from list...\n" - ""); -char *s = N_(""); -char *s = N_("Enter a new value for the variable.\n" - ""); -char *s = N_("Copyright (c) 2010 David Goncalves"); -char *s = N_("GUI to manage devices connected a NUT server.\n" - "\n" - "For more information about NUT (Network UPS Tools)\n" - "please visit the author's website.\n" - "\n" - "http://www.networkupstools.org\n" - ""); -char *s = N_("http://www.lestat.st"); -char *s = N_("Copyright (C) 2010 David Goncalves \n" - "\n" - "This program is free software: you can redistribute it and/or modify\n" - "it under the terms of the GNU General Public License as published by\n" - "the Free Software Foundation; either version 3 of the License, or\n" - "(at your option) any later version.\n" - "\n" - "This program is distributed in the hope that it will be useful,\n" - "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" - "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" - "GNU General Public License for more details.\n" - "\n" - "You should have received a copy of the GNU General Public License\n" - "along with this program. If not, see ."); diff --git a/scripts/python/app/nut-monitor.desktop b/scripts/python/app/nut-monitor.desktop index b4ccf398ae..b63b6e7123 100644 --- a/scripts/python/app/nut-monitor.desktop +++ b/scripts/python/app/nut-monitor.desktop @@ -4,7 +4,7 @@ Name[fr]=Moniteur NUT Comment=Network UPS Tools GUI client Comment[fr]=Client graphique pour NUT (Network UPS Tools) Comment[it]=Client grafico per NUT (Network UPS Tools) -Categories=System;Monitor;HardwareSettings;Settings;GTK +Categories=System;Monitor;HardwareSettings;Settings;Qt Exec=NUT-Monitor Icon=nut-monitor Terminal=false diff --git a/scripts/python/app/ui/aboutdialog1.ui b/scripts/python/app/ui/aboutdialog1.ui new file mode 100644 index 0000000000..c6a62148d7 --- /dev/null +++ b/scripts/python/app/ui/aboutdialog1.ui @@ -0,0 +1,108 @@ + + + aboutdialog1 + + + About NUT-Monitor + + + true + + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + <h1>NUT-Monitor 1.3.1</h1> +<p>GUI to manage devices connected a NUT server.</p> +<p>For more information about NUT (Network UPS Tools) please visit the author's website.</p> +<p style="margin-bottom: 1.5em">http://www.networkupstools.org</p> +<p style=" font-size:8pt;">Copyright (c) 2010 David Goncalves</p> +<p><a href="http://www.lestat.st/informatique/projets/nut-monitor-en">http://www.lestat.st</a></p> + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + aboutdialog1 + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + aboutdialog1 + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/scripts/python/app/ui/dialog1.ui b/scripts/python/app/ui/dialog1.ui new file mode 100644 index 0000000000..6cf652b1f7 --- /dev/null +++ b/scripts/python/app/ui/dialog1.ui @@ -0,0 +1,89 @@ + + + dialog1 + + + + 0 + 0 + 297 + 133 + + + + Dialog + + + true + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Enter a name for this favorite<br><br><font color="#808080">You cannot re-use a name from another entry</font> + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + dialog1 + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + dialog1 + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/scripts/python/app/ui/dialog2.ui b/scripts/python/app/ui/dialog2.ui new file mode 100644 index 0000000000..d31542e206 --- /dev/null +++ b/scripts/python/app/ui/dialog2.ui @@ -0,0 +1,98 @@ + + + dialog2 + + + + 0 + 0 + 229 + 116 + + + + Dialog + + + true + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Please select the favorite that you want to delete from list... + + + true + + + + + + + + None + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + dialog2 + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + dialog2 + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/scripts/python/app/ui/window1.ui b/scripts/python/app/ui/window1.ui new file mode 100644 index 0000000000..1950464463 --- /dev/null +++ b/scripts/python/app/ui/window1.ui @@ -0,0 +1,473 @@ + + + window1 + + + + 0 + 0 + 560 + 465 + + + + NUT Monitor + + + + ../../../../../.designer/backup../../../../../.designer/backup + + + + + + + NUT Server + + + Qt::AlignCenter + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + 65535 + + + 3493 + + + + + + + + + + Device : + + + + + + + + None + + + + + + + + Host / Port : + + + + + + + false + + + &Refresh + + + + + + + + + + + + Use authentication + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Login / Password : + + + + + + + + + + QLineEdit::Password + + + + + + + + + + QFrame::HLine + + + QFrame::Sunken + + + + + + + + + + + + false + + + C&onnect + + + + + + + + + + &Disconnect + + + + + + + + + + + + + + + 0 + + + + Device status + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + label + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + label + + + + + + + + + + + + + + Remaining time : + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Battery charge : + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 24 + + + + + + + Device commands : + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 24 + + + + + + + Current load : + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + N/A + + + Qt::AlignCenter + + + + + + + + + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + + + + + + &Execute + + + + + + + + + + + + + + + Device vars + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + &Refresh + + + + + + + + + + + + + + + + + + + 0 + 0 + 560 + 24 + + + + + &File + + + + + + + + F&avorites + + + + + + + + + + + + + + + &About + + + + + + + + &Quit + + + Ctrl+Q + + + + + false + + + + + + &Add + + + + + false + + + + + + &Delete + + + + + + From e14d1d65d380b88f5898d072796d93d695ebe8fe Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 2 Apr 2022 00:35:55 +0200 Subject: [PATCH 401/700] Rename modernized NUT-Monitor UI app to NUT-Monitor-py3qt5 --- configure.ac | 2 +- scripts/python/Makefile.am | 4 ++-- .../app/{NUT-Monitor.in => NUT-Monitor-py3qt5.in} | 2 +- scripts/python/app/nut-monitor-py3qt5.desktop | 11 +++++++++++ scripts/python/app/nut-monitor.desktop | 11 ----------- 5 files changed, 15 insertions(+), 15 deletions(-) rename scripts/python/app/{NUT-Monitor.in => NUT-Monitor-py3qt5.in} (99%) create mode 100644 scripts/python/app/nut-monitor-py3qt5.desktop delete mode 100644 scripts/python/app/nut-monitor.desktop diff --git a/configure.ac b/configure.ac index 93f734d1e8..fdfd2099d6 100644 --- a/configure.ac +++ b/configure.ac @@ -2895,7 +2895,7 @@ AC_MSG_NOTICE([Generating templated script files that should be marked executabl m4_foreach_w([SCRIPTFILE], [ scripts/Aix/nut.init scripts/HP-UX/postinstall - scripts/python/app/NUT-Monitor + scripts/python/app/NUT-Monitor-py3qt5 scripts/augeas/gen-nutupsconf-aug.py scripts/python/module/test_nutclient.py scripts/upsdrvsvcctl/nut-driver-enumerator.sh diff --git a/scripts/python/Makefile.am b/scripts/python/Makefile.am index b53da4576c..44886e1020 100644 --- a/scripts/python/Makefile.am +++ b/scripts/python/Makefile.am @@ -5,9 +5,9 @@ EXTRA_DIST = README \ app/ui/dialog1.ui \ app/ui/dialog2.ui \ app/ui/window1.ui \ - app/NUT-Monitor.in \ + app/NUT-Monitor-py3qt5.in \ app/nut-monitor.appdata.xml \ - app/nut-monitor.desktop \ + app/nut-monitor-py3qt5.desktop \ app/icons/48x48/nut-monitor.png \ app/icons/64x64/nut-monitor.png \ app/icons/256x256/nut-monitor.png \ diff --git a/scripts/python/app/NUT-Monitor.in b/scripts/python/app/NUT-Monitor-py3qt5.in similarity index 99% rename from scripts/python/app/NUT-Monitor.in rename to scripts/python/app/NUT-Monitor-py3qt5.in index c4a648ee30..cf1c3b6187 100755 --- a/scripts/python/app/NUT-Monitor.in +++ b/scripts/python/app/NUT-Monitor-py3qt5.in @@ -1,4 +1,4 @@ -#!@PYTHON@ +#!@PYTHON3@ # -*- coding: utf-8 -*- # 2009-12-27 David Goncalves - Version 1.2 diff --git a/scripts/python/app/nut-monitor-py3qt5.desktop b/scripts/python/app/nut-monitor-py3qt5.desktop new file mode 100644 index 0000000000..66fcd8632d --- /dev/null +++ b/scripts/python/app/nut-monitor-py3qt5.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Name=NUT Monitor +Name[fr]=Moniteur NUT +Comment=Network UPS Tools GUI client (Py3Qt5) +Comment[fr]=Client graphique pour NUT (Network UPS Tools, Py3Qt5) +Comment[it]=Client grafico per NUT (Network UPS Tools, Py3Qt5) +Categories=System;Monitor;HardwareSettings;Settings;Qt +Exec=NUT-Monitor-py3qt5 +Icon=nut-monitor +Terminal=false +Type=Application diff --git a/scripts/python/app/nut-monitor.desktop b/scripts/python/app/nut-monitor.desktop deleted file mode 100644 index b63b6e7123..0000000000 --- a/scripts/python/app/nut-monitor.desktop +++ /dev/null @@ -1,11 +0,0 @@ -[Desktop Entry] -Name=NUT Monitor -Name[fr]=Moniteur NUT -Comment=Network UPS Tools GUI client -Comment[fr]=Client graphique pour NUT (Network UPS Tools) -Comment[it]=Client grafico per NUT (Network UPS Tools) -Categories=System;Monitor;HardwareSettings;Settings;Qt -Exec=NUT-Monitor -Icon=nut-monitor -Terminal=false -Type=Application From 4adbd805643e76474439f7e7ac84993f7c5b0388 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 2 Apr 2022 00:14:35 +0200 Subject: [PATCH 402/700] Rename NUT-Monitor UI app to NUT-Monitor-py2gtk2 --- configure.ac | 2 +- scripts/python/Makefile.am | 6 +++--- .../app/{NUT-Monitor.in => NUT-Monitor-py2gtk2.in} | 4 ++-- scripts/python/app/nut-monitor-py2gtk2.desktop | 11 +++++++++++ scripts/python/app/nut-monitor.desktop | 11 ----------- scripts/python/app/{ => ui}/gui-1.3.glade | 0 scripts/python/app/{ => ui}/gui-1.3.glade.h | 0 7 files changed, 17 insertions(+), 17 deletions(-) rename scripts/python/app/{NUT-Monitor.in => NUT-Monitor-py2gtk2.in} (99%) create mode 100644 scripts/python/app/nut-monitor-py2gtk2.desktop delete mode 100644 scripts/python/app/nut-monitor.desktop rename scripts/python/app/{ => ui}/gui-1.3.glade (100%) rename scripts/python/app/{ => ui}/gui-1.3.glade.h (100%) diff --git a/configure.ac b/configure.ac index 93f734d1e8..0ef5b09bb9 100644 --- a/configure.ac +++ b/configure.ac @@ -2895,7 +2895,7 @@ AC_MSG_NOTICE([Generating templated script files that should be marked executabl m4_foreach_w([SCRIPTFILE], [ scripts/Aix/nut.init scripts/HP-UX/postinstall - scripts/python/app/NUT-Monitor + scripts/python/app/NUT-Monitor-py2gtk2 scripts/augeas/gen-nutupsconf-aug.py scripts/python/module/test_nutclient.py scripts/upsdrvsvcctl/nut-driver-enumerator.sh diff --git a/scripts/python/Makefile.am b/scripts/python/Makefile.am index 8a50571230..804dcdb46a 100644 --- a/scripts/python/Makefile.am +++ b/scripts/python/Makefile.am @@ -1,10 +1,10 @@ # Network UPS Tools: data/html EXTRA_DIST = README \ - app/gui-1.3.glade \ - app/NUT-Monitor.in \ + app/ui/gui-1.3.glade \ + app/NUT-Monitor-py2gtk2.in \ app/nut-monitor.appdata.xml \ - app/nut-monitor.desktop \ + app/nut-monitor-py2gtk2.desktop \ app/icons/48x48/nut-monitor.png \ app/icons/64x64/nut-monitor.png \ app/icons/256x256/nut-monitor.png \ diff --git a/scripts/python/app/NUT-Monitor.in b/scripts/python/app/NUT-Monitor-py2gtk2.in similarity index 99% rename from scripts/python/app/NUT-Monitor.in rename to scripts/python/app/NUT-Monitor-py2gtk2.in index 95c28d87ce..d845e87058 100755 --- a/scripts/python/app/NUT-Monitor.in +++ b/scripts/python/app/NUT-Monitor-py2gtk2.in @@ -1,4 +1,4 @@ -#!@PYTHON@ +#!@PYTHON2@ # -*- coding: utf-8 -*- # 2009-12-27 David Goncalves - Version 1.2 @@ -76,7 +76,7 @@ class interface : ( cmd_opts, args ) = opt_parser.parse_args() - self.__glade_file = os.path.join( os.path.dirname( sys.argv[0] ), "gui-1.3.glade" ) + self.__glade_file = os.path.join( os.path.dirname( sys.argv[0] ), "ui/gui-1.3.glade" ) self.__widgets["interface"] = gtk.glade.XML( self.__glade_file, "window1", APP ) self.__widgets["main_window"] = self.__widgets["interface"].get_widget("window1") diff --git a/scripts/python/app/nut-monitor-py2gtk2.desktop b/scripts/python/app/nut-monitor-py2gtk2.desktop new file mode 100644 index 0000000000..130cd06fcb --- /dev/null +++ b/scripts/python/app/nut-monitor-py2gtk2.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Name=NUT Monitor +Name[fr]=Moniteur NUT +Comment=Network UPS Tools GUI client (Py2Gtk2) +Comment[fr]=Client graphique pour NUT (Network UPS Tools, Py2Gtk2) +Comment[it]=Client grafico per NUT (Network UPS Tools, Py2Gtk2) +Categories=System;Monitor;HardwareSettings;Settings;GTK +Exec=NUT-Monitor-py2gtk2 +Icon=nut-monitor +Terminal=false +Type=Application diff --git a/scripts/python/app/nut-monitor.desktop b/scripts/python/app/nut-monitor.desktop deleted file mode 100644 index b4ccf398ae..0000000000 --- a/scripts/python/app/nut-monitor.desktop +++ /dev/null @@ -1,11 +0,0 @@ -[Desktop Entry] -Name=NUT Monitor -Name[fr]=Moniteur NUT -Comment=Network UPS Tools GUI client -Comment[fr]=Client graphique pour NUT (Network UPS Tools) -Comment[it]=Client grafico per NUT (Network UPS Tools) -Categories=System;Monitor;HardwareSettings;Settings;GTK -Exec=NUT-Monitor -Icon=nut-monitor -Terminal=false -Type=Application diff --git a/scripts/python/app/gui-1.3.glade b/scripts/python/app/ui/gui-1.3.glade similarity index 100% rename from scripts/python/app/gui-1.3.glade rename to scripts/python/app/ui/gui-1.3.glade diff --git a/scripts/python/app/gui-1.3.glade.h b/scripts/python/app/ui/gui-1.3.glade.h similarity index 100% rename from scripts/python/app/gui-1.3.glade.h rename to scripts/python/app/ui/gui-1.3.glade.h From 9d89327b730d72111ff0f1c63ea94e40c7fb303c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 2 Apr 2022 00:28:19 +0200 Subject: [PATCH 403/700] m4/nut_check_python.m4: extend list of some python interpreter filenames --- m4/nut_check_python.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/m4/nut_check_python.m4 b/m4/nut_check_python.m4 index 99843d1f71..6357286874 100644 --- a/m4/nut_check_python.m4 +++ b/m4/nut_check_python.m4 @@ -50,7 +50,7 @@ AC_DEFUN([NUT_CHECK_PYTHON2], PYTHON2="" AS_CASE([${nut_with_python2}], - [auto|yes|""], [AC_CHECK_PROGS([PYTHON2], [python2 python], [_python2_runtime])], + [auto|yes|""], [AC_CHECK_PROGS([PYTHON2], [python2 python2.7 python-2.7 python], [_python2_runtime])], [no], [PYTHON2="no"], [PYTHON2="${nut_with_python2}"] ) @@ -92,7 +92,7 @@ AC_DEFUN([NUT_CHECK_PYTHON3], PYTHON3="" AS_CASE([${nut_with_python3}], - [auto|yes|""], [AC_CHECK_PROGS([PYTHON3], [python3 python], [_python3_runtime])], + [auto|yes|""], [AC_CHECK_PROGS([PYTHON3], [python3 python3.9 python-3.9 python3.7 python-3.7 python3.5 python-3.5 python], [_python3_runtime])], [no], [PYTHON3="no"], [PYTHON3="${nut_with_python3}"] ) From 57af06ceb4a09b8d79da865a71f6b84b9fb5221b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 2 Apr 2022 01:02:19 +0200 Subject: [PATCH 404/700] scripts/python/app/README: document how to run UI app in-place --- scripts/python/app/README | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/scripts/python/app/README b/scripts/python/app/README index 6a618186a1..e5515ab33b 100644 --- a/scripts/python/app/README +++ b/scripts/python/app/README @@ -5,4 +5,16 @@ This application (variants written in Python 2 + GTK2, and in Python 3 + Qt5) uses the python-pynut class (available at http://www.lestat.st), delivered as PyNUT in the NUT source tree. -David Goncalves +Refer to your OS packaging and/or install custom modules with `pip` (or `pip3`) +to get required dependencies (GTK + GObject or QT5). + +For quick tests (e.g. during development), you can run the clients like this: +```` +:; PYTHONPATH=../module/ python2 ./NUT-Monitor-py2gtk2.in +```` +or: +```` +:; PYTHONPATH=../module/ python3 ./NUT-Monitor-py3qt5.in +```` + +Originally authored by David Goncalves From a2227d34fd19e65d77f5e0c125237b6a73bd8824 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 2 Apr 2022 01:20:58 +0200 Subject: [PATCH 405/700] scripts/python/app/README: document how to get localized UI in dev/testing --- scripts/python/app/README | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/python/app/README b/scripts/python/app/README index e5515ab33b..712d5cf8c8 100644 --- a/scripts/python/app/README +++ b/scripts/python/app/README @@ -17,4 +17,9 @@ or: :; PYTHONPATH=../module/ python3 ./NUT-Monitor-py3qt5.in ```` +For localized UI, also `export LANG=fr_FR.UTF-8` or `export LANG=ru_RU.UTF-8` +(see and feel welcome to improve the choice of languages in `locale` directory). + +NOTE: Currently localization only works for Python 2 client, PRs are welcome. + Originally authored by David Goncalves From 014eba13c951126ddef400e5b757cbd1d34152af Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 2 Apr 2022 11:44:12 +0200 Subject: [PATCH 406/700] GitIgnore newly named variants of NUT-Monitor-py2gtk2 and NUT-Monitor-py3qt5 --- scripts/python/app/.gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/python/app/.gitignore b/scripts/python/app/.gitignore index 4472991c5e..8759eda67a 100644 --- a/scripts/python/app/.gitignore +++ b/scripts/python/app/.gitignore @@ -1 +1,7 @@ +# Retain the names for possible publication symlinks: /NUT-Monitor +/nut-monitor.desktop + +# Final scripts generated from .in templates: +/NUT-Monitor-py2gtk2 +/NUT-Monitor-py3qt5 From 5643f5023a6e5615f81c93f9d9e4bb696631df1f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 2 Apr 2022 12:31:40 +0200 Subject: [PATCH 407/700] Introduce new NUT-Monitor name-holder as a wrapping script to pick usable implementation --- scripts/python/app/.gitignore | 4 -- scripts/python/app/NUT-Monitor | 67 ++++++++++++++++++++++++++ scripts/python/app/nut-monitor.desktop | 11 +++++ 3 files changed, 78 insertions(+), 4 deletions(-) create mode 100755 scripts/python/app/NUT-Monitor create mode 100644 scripts/python/app/nut-monitor.desktop diff --git a/scripts/python/app/.gitignore b/scripts/python/app/.gitignore index 8759eda67a..d617cc83a2 100644 --- a/scripts/python/app/.gitignore +++ b/scripts/python/app/.gitignore @@ -1,7 +1,3 @@ -# Retain the names for possible publication symlinks: -/NUT-Monitor -/nut-monitor.desktop - # Final scripts generated from .in templates: /NUT-Monitor-py2gtk2 /NUT-Monitor-py3qt5 diff --git a/scripts/python/app/NUT-Monitor b/scripts/python/app/NUT-Monitor new file mode 100755 index 0000000000..d1ed939b24 --- /dev/null +++ b/scripts/python/app/NUT-Monitor @@ -0,0 +1,67 @@ +#!/bin/sh + +# This script wraps selection of a NUT-Monitor implementation usable +# on the current system, using the historic name for users to have +# a single simple call. +# +# Copyright (C): +# 2022 Jim Klimov +# +# License: GPLv2+ + +# Currently we have issues using localization for py3qt5 variant, +# so if both seem functional, the wrapper would call py2gtk2: +PREFER_PY2=true + +# Detect which variant of NUT-Monitor we can run on the local system: +PYTHON_PY2GTK2="`head -1 "$0"-py2gtk2 | sed 's,^#!,,`" || PYTHON_PY2GTK2="" +PYTHON_PY3QT5="`head -1 "$0"-py3qt5 | sed 's,^#!,,`" || PYTHON_PY3QT5="" +SCRIPTDIR="`dirname "$0"`" && SCRIPTDIR="`cd "$SCRIPTDIR" && pwd`" || SCRIPTDIR="./" + +if [ -n "$PYTHON_PY2GTK2" ] \ +&& (command -v $PYTHON_PY2GTK2) >/dev/null 2>/dev/null \ +&& $PYTHON_PY2GTK2 -c "import re,glob,codecs,gtk,gtk.glade,gobject,ConfigParser" >/dev/null 2>/dev/null \ +; then + echo "PYTHON_PY2GTK2 is usable as: $PYTHON_PY2GTK2" >&2 +else + PYTHON_PY2GTK2="" +fi + +if [ -n "$PYTHON_PY3QT5" ] \ +&& (command -v $PYTHON_PY3QT5) >/dev/null 2>/dev/null \ +&& $PYTHON_PY3QT5 -c "import re,glob,codecs,PyQt5.uic,configparser" >/dev/null 2>/dev/null \ +; then + echo "PYTHON_PY3QT5 is usable as: $PYTHON_PY3QT5" >&2 +else + PYTHON_PY3QT5="" +fi + +for P in "$PYTHON_PY2GTK2" "$PYTHON_PY3QT5" ; do + [ -n "$P" ] || continue + + # If running from source tree... + if ! $P -c "import PyNUT" >/dev/null 2>/dev/null \ + && PYTHONPATH="${SCRIPTDIR}/../module" $P -c "import PyNUT" >/dev/null 2>/dev/null \ + ; then + PYTHONPATH="${SCRIPTDIR}/../module" + export PYTHONPATH + fi +done + +if [ -n "$PYTHON_PY2GTK2" ] && [ -n "$PYTHON_PY3QT5" ] ; then + if $PREFER_PY2 ; then + exec "$0"-py2gtk2 "$@" + else + exec "$0"-py3qt3 "$@" + fi +else + if [ -n "$PYTHON_PY2GTK2" ] ; then + exec "$0"-py2gtk2 "$@" + fi + if [ -n "$PYTHON_PY3QT3" ] ; then + exec "$0"-py3qt3 "$@" + fi +fi + +echo "ERROR: No usable Python interpreter version (with needed modules) was found" >&2 +exit 1 diff --git a/scripts/python/app/nut-monitor.desktop b/scripts/python/app/nut-monitor.desktop new file mode 100644 index 0000000000..e1e71a41b6 --- /dev/null +++ b/scripts/python/app/nut-monitor.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Name=NUT Monitor +Name[fr]=Moniteur NUT +Comment=Network UPS Tools GUI client +Comment[fr]=Client graphique pour NUT (Network UPS Tools) +Comment[it]=Client grafico per NUT (Network UPS Tools) +Categories=System;Monitor;HardwareSettings;Settings +Exec=NUT-Monitor +Icon=nut-monitor +Terminal=false +Type=Application From 71698735394e2f5d3795cc4571377c5e4e55bd58 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 2 Apr 2022 12:51:50 +0200 Subject: [PATCH 408/700] scripts/python/app/README: update with sections; list Desktop menu integration and Kudos --- scripts/python/app/README | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/scripts/python/app/README b/scripts/python/app/README index 712d5cf8c8..163c4ae47b 100644 --- a/scripts/python/app/README +++ b/scripts/python/app/README @@ -1,6 +1,12 @@ +NUT-Monitor +=========== + NUT-Monitor is a graphical application to access and manage UPSes connected to a NUT (Network UPS Tools) server. +Dependencies +------------ + This application (variants written in Python 2 + GTK2, and in Python 3 + Qt5) uses the python-pynut class (available at http://www.lestat.st), delivered as PyNUT in the NUT source tree. @@ -8,6 +14,9 @@ as PyNUT in the NUT source tree. Refer to your OS packaging and/or install custom modules with `pip` (or `pip3`) to get required dependencies (GTK + GObject or QT5). +Path to PyNUT module +-------------------- + For quick tests (e.g. during development), you can run the clients like this: ```` :; PYTHONPATH=../module/ python2 ./NUT-Monitor-py2gtk2.in @@ -17,9 +26,30 @@ or: :; PYTHONPATH=../module/ python3 ./NUT-Monitor-py3qt5.in ```` +Localization +------------ + For localized UI, also `export LANG=fr_FR.UTF-8` or `export LANG=ru_RU.UTF-8` (see and feel welcome to improve the choice of languages in `locale` directory). NOTE: Currently localization only works for Python 2 client, PRs are welcome. -Originally authored by David Goncalves +Desktop menu integration +------------------------ + +This component ships both implementation-specific `nut-monitor-py2gtk2.desktop` +and `nut-monitor-py3qt5.desktop` files which allows a user to have icons for +both variants separately, as well as the legacy-named `nut-monitor.desktop` +for running the wrapper script `NUT-Monitor` which picks an implementation best +suited for current run-time circumstances. + +Kudos +----- + +NUT-Monitor and PyNUT (for Python 2 syntax) were originally authored +by David Goncalves + +NUT-Monitor was converted to Python 3 + QT5 by Luke Dashjr + +PyNUT was extended, and two variants of NUT-Monitor converged and wrapped +for Python 2+3 dual support by Jim Klimov From 47277da510b82189c171b317285648fb6d830cbe Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 2 Apr 2022 18:24:02 +0200 Subject: [PATCH 409/700] NEWS: detail the split of NUT-Monitor and new wrapper script --- NEWS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 4ae2858706..61f9b8b590 100644 --- a/NEWS +++ b/NEWS @@ -157,7 +157,9 @@ refer to this change set (too long in the making) as NUT 2.7.5. - Separated NUT-Monitor UI into two applications, NUT-Monitor-py2gtk2 and NUT-Monitor-py3qt5, suitable for two generations of Python ecosystem - with their great differences [PR #1310] + with their great differences; `NUT-Monitor` name is retained for wrapper + script which calls one of these, such that the current system can execute + [PRs #1310, #1354] - Various USB driver families: expanded device-matching with "device" in addition to "bus" and generic USB fields. This is needed to support From 6670358c67c25fe2350e78d89f782d3cfc1363b1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 2 Apr 2022 20:17:41 +0200 Subject: [PATCH 410/700] UPGRADING: make note of systemd and Py2/3 changes on top for packagers to see --- UPGRADING | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/UPGRADING b/UPGRADING index a25c73e472..9187eeb75b 100644 --- a/UPGRADING +++ b/UPGRADING @@ -15,7 +15,11 @@ Changes from 2.7.4 to 2.8.0 remain, please consider making pull requests for upstream NUT codebase to share the fixes consistently across the ecosystem. Also note that some new types of drivers (so package groups with unique dependencies) - could have appeared since your packaging was written (e.g. with modbus). + could have appeared since your packaging was written (e.g. with modbus), + as well as new features in systemd integration (`nut-driver@instances` + and the `nut-driver-enumerator` to manage their population), as well as + updated Python 2 and Python 3 support (again, maybe dictating different + package groups) as detailed below. - Due to changes needed to resolve build warnings, mostly about mismatching data types for some variables, some structure definitions and API signatures From d61f0e7ab893299d5bfff91e962d7920acb980e0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 2 Apr 2022 20:19:19 +0200 Subject: [PATCH 411/700] UPGRADING: detail the Py2/Py3 packages --- UPGRADING | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/UPGRADING b/UPGRADING index 9187eeb75b..1be850f20b 100644 --- a/UPGRADING +++ b/UPGRADING @@ -89,6 +89,19 @@ Changes from 2.7.4 to 2.8.0 - Python: scripts have been updated to work with Python 3 as well as 2. + * PyNUT module (protocol binding) supports both Python generations. + + * NUT-Monitor (desktop UI client) got separated into two projects: + one with support for Python2 and GTK2, and another for Python3 and Qt5. + On operating systems that serve both environments, either of these + implementation should be usable. For distributions that deprecated + and removed Python2 support, it is a point to consider in NUT packages + and their build-time and installation dependencies. + The historic filenames for desktop integration (`NUT-Monitor` script + and `nut-monitor.desktop`) are still delivered, but now cover a wrapper + script which detects the environment capabilities and launches the best + suitable UI implementation (if both are available). + - apcsmart: updates to CS "hack" (see docs/man/apcsmart.txt for details) - upsdebugx(): added `[D#]` prefix to log entries with level > 0 From a0bfc7c66f8a0fbe396d795aad962f05a77c0b50 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 2 Apr 2022 19:02:20 +0000 Subject: [PATCH 412/700] scripts/python/app/NUT-Monitor: fix typo, thank CI --- scripts/python/app/NUT-Monitor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/python/app/NUT-Monitor b/scripts/python/app/NUT-Monitor index d1ed939b24..cab270f574 100755 --- a/scripts/python/app/NUT-Monitor +++ b/scripts/python/app/NUT-Monitor @@ -14,8 +14,8 @@ PREFER_PY2=true # Detect which variant of NUT-Monitor we can run on the local system: -PYTHON_PY2GTK2="`head -1 "$0"-py2gtk2 | sed 's,^#!,,`" || PYTHON_PY2GTK2="" -PYTHON_PY3QT5="`head -1 "$0"-py3qt5 | sed 's,^#!,,`" || PYTHON_PY3QT5="" +PYTHON_PY2GTK2="`head -1 "$0"-py2gtk2 | sed 's,^#!,,'`" || PYTHON_PY2GTK2="" +PYTHON_PY3QT5="`head -1 "$0"-py3qt5 | sed 's,^#!,,'`" || PYTHON_PY3QT5="" SCRIPTDIR="`dirname "$0"`" && SCRIPTDIR="`cd "$SCRIPTDIR" && pwd`" || SCRIPTDIR="./" if [ -n "$PYTHON_PY2GTK2" ] \ From ef67f4c017546034da46ceeca5aa79f5300ad573 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 4 Apr 2022 10:11:49 +0000 Subject: [PATCH 413/700] docs/config-prereqs.txt: clarify that non-GNU makes should work well --- docs/config-prereqs.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/config-prereqs.txt b/docs/config-prereqs.txt index 4f78001077..5fb51db64e 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -65,7 +65,9 @@ NOTE: To build older releases, such as "vanilla" NUT 2.7.4, you may need to: generated `configure` script gets interpreted by it) * Generally you may have better results with GNU Make newer than 3.81 - than with other make implementations + than with other make implementations; however, builds are regularly + tested by CI with Sun dmake and BSD make as well, so recipes should + not expect GNU-only syntax and constructs to work * For intensive rebuilds, `ccache` is recommended From 6a826c45aa80c4ea04631fc393f95e63542571c9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 4 Apr 2022 10:12:32 +0000 Subject: [PATCH 414/700] docs/config-prereqs.txt: clarify pre-installation of python and perl --- docs/config-prereqs.txt | 69 ++++++++++++++++++++++++++++++++++++++--- docs/nut.dict | 3 +- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/docs/config-prereqs.txt b/docs/config-prereqs.txt index 5fb51db64e..405160f258 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -97,13 +97,21 @@ source versions on both Debian 8 Jessie and Debian 11 Buster). # NOTE: Older Debian-like distributions may lack a "libtool-bin" :; apt-get install \ ccache time \ - git python curl \ + git python perl curl \ make autoconf automake libltdl-dev libtool-bin libtool \ valgrind \ cppcheck \ pkg-config \ gcc g++ clang +# NOTE: For python, you may eventually have to specify a variant like this +# (numbers depending on default or additional packages of your distro): +# :; apt-get install python2 python2.7 python-is-python2 +# and/or: +# :; apt-get install python3 python3.9 +# You can find a list of what is (pre-)installed with: +# :; dpkg -l | grep -Ei 'perl|python' + # For spell-checking, highly recommended if you would propose pull requests: :; apt-get install \ aspell aspell-en @@ -223,13 +231,21 @@ https://unix.stackexchange.com/questions/475584/cannot-install-busybox-on-centos :; yum install \ ccache time \ file systemd-devel \ - git python curl \ + git python perl curl \ make autoconf automake libtool-ltdl-devel libtool \ valgrind \ cppcheck \ pkgconfig \ gcc gcc-c++ clang +# NOTE: For python, you may eventually have to specify a variant like this +# (numbers depending on default or additional packages of your distro): +# :; yum install python-2.7.5 +# and/or: +# :; yum install python3 python3-3.6.8 +# You can find a list of what is (pre-)installed with: +# :; rpm -qa | grep -Ei 'perl|python' + # For spell-checking, highly recommended if you would propose pull requests: :; yum install \ aspell aspell-en @@ -294,13 +310,21 @@ below. ---- :; pkg install \ - git python curl \ + git python perl5 curl \ gmake autoconf automake autotools libltdl libtool \ valgrind \ cppcheck \ pkgconf \ gcc clang +# NOTE: For python, you may eventually have to specify a variant like this +# (numbers depending on default or additional packages of your distro): +# :; pkg install python2 python27 +# and/or: +# :; pkg install python3 python37 +# You can find a list of what is (pre-)installed with: +# :; pkg info | grep -Ei 'perl|python' + # For spell-checking, highly recommended if you would propose pull requests: :; pkg install \ aspell en-aspell @@ -401,6 +425,17 @@ networked repository (4.2.x vs 4.9.x) pkgconf \ gcc clang +# NOTE: For python, you may eventually have to specify a variant like this +# (numbers depending on default or additional packages of your distro): +# :; yum install python-2.7.15p0 +# and/or: +# :; yum install python-3.6.6p1 +# although you might succeed specifying shorter names and the packager +# will offer a list of matching variants. +# NOTE: "perl" is not currently a package, but seemingly part of base OS. +# You can find a list of what is (pre-)installed with: +# :; pkg_info | grep -Ei 'perl|python' + # For spell-checking, highly recommended if you would propose pull requests: :; pkg_add \ aspell @@ -503,13 +538,15 @@ base installation. It is not required for "legacy" builds arranged by just ---- :; pkgin install \ - git python27 python39 curl \ + git python27 python39 perl curl \ make gmake autoconf automake libltdl libtool \ cppcheck \ pkgconf \ gcc7 clang ;; ( cd /usr/pkg/bin && ( ln -fs python2.7 python2 ; ln -fs python3.9 python3 ) ) +# You can find a list of what is (pre-)installed with: +# :; pkgin list | grep -Ei 'perl|python' # For spell-checking, highly recommended if you would propose pull requests: :; pkgin install \ @@ -544,7 +581,7 @@ base installation. It is not required for "legacy" builds arranged by just bash dash ast-ksh oksh ---- -NOTE: 9TBD) On NetBSD 9.2 this package complains that it requires +NOTE: (TBD) On NetBSD 9.2 this package complains that it requires OS ABI 9.0, or that `CHECK_OSABI=no` is set in `pkg_install.conf`. Such file was not found in the test system... + @@ -607,6 +644,20 @@ Typical tooling would include: pkg-config \ gnu-binutils developer/linker +# NOTE: For python, some suitable version should be available since `pkg(5)` +# tool is written in it. Similarly, many system tools are written in perl +# so some version should be installed. You may specify additional variants +# like this (numbers depending on default or additional packages of your +# distro; recommended to group `pkg` calls with many packages at once to +# save processing time for calculating a build strategy): +# :; pkg install runtime/python-27 +# and/or: +# :; pkg install runtime/python-37 runtime/python-35 runtime/python-39 +# Similarly for perl variants, e.g.: +# :; pkg install runtime/perl-522 runtime/perl-524 runtime/perl-534 +# You can find a list of what is available in remote repositories with: +# :; pkg info -r | grep -Ei 'perl|python' + # For spell-checking, highly recommended if you would propose pull requests: :; pkg install \ aspell text/aspell/en @@ -719,11 +770,19 @@ Note you may need not just the "Core" IPS package publisher, but also the :; pkg install \ developer/build/autoconf developer/build/automake developer/build/libtool \ build-essential ccache git developer/pkg-config \ + runtime/perl \ asciidoc \ libgd :; pkg install \ net-snmp + +# NOTE: For python, some suitable version should be available since `pkg(5)` +# tool is written in it. You may specify an additional variant like this +# (numbers depending on default or additional packages of your distro): +# :; pkg install runtime/python-37 +# You can find a list of what is available in remote repositories with: +# :; pkg info -r | grep -Ei 'perl|python' ---- OmniOS lacks a pre-packaged libusb, however the binary build from contemporary diff --git a/docs/nut.dict b/docs/nut.dict index d9a8cc1768..e30e7a6568 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2884 utf-8 +personal_ws-1.1 en 2885 utf-8 AAS ABI ACFAIL @@ -1672,6 +1672,7 @@ distros dl dll dlopen +dmake dmesg dnf dnl From 3223ee6243f04e259ed8708098e42cbd22aa6cca Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 4 Apr 2022 10:24:29 +0000 Subject: [PATCH 415/700] README: clarify use of github --- README | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README b/README index 9fe1c00fc5..f71210262f 100644 --- a/README +++ b/README @@ -508,7 +508,11 @@ The past stable trees were 1.0, 1.2, 1.4, 2.0, 2.2 and 2.4, with the latest stable tree designated 2.6. The development trees were 1.1, 1.3, 1.5, 2.1 and 2.3. As of the 2.4 release, there is no real development branch anymore since the code is available through a revision control -system (namely Subversion) and snapshots. +system (namely Subversion) and snapshots. Since 2.7 line of releases, +sources are tracked in Git revision control system, with the project +ecosystem being hosted on GitHub, and improvements or other contributions +merged through common pull request approach and custom NUT CI testing +on multiple platforms. Major release jumps are mostly due to large changes to the features list. There have also been a number of architectural changes which From 495279b15f95c2942492bfd465bcd9891ce97b3c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 4 Apr 2022 10:25:02 +0000 Subject: [PATCH 416/700] README: trim trailing spaces --- README | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README b/README index f71210262f..c57dd89e97 100644 --- a/README +++ b/README @@ -102,7 +102,7 @@ This package is broken down into several categories: - *clients* - They talk to upsd and do things with the status data. - *cgi-bin* - Special class of clients that you can use with your web server. - *scripts* - Contains various scripts, like the Perl and Python binding, -integration bits and applications. +integration bits and applications. Drivers ------- @@ -249,7 +249,7 @@ Power distribution unit management NUT also provides an advanced support for power distribution units. You should read the <> -chapter to learn more about when to use this feature. +chapter to learn more about when to use this feature. Network Server -------------- @@ -522,11 +522,11 @@ may not be noticeable to most users, but which can impact developers. Backwards and Forwards Compatibility ------------------------------------ -The old network code spans a range from about 0.41.1 when TCP support +The old network code spans a range from about 0.41.1 when TCP support was introduced up to the recent 1.4 series. It used variable names like STATUS, UTILITY, and LOADPCT. Many of these names go back to the earliest prototypes of this software from 1997. At that point there -was no way to know that so many drivers would come along and introduce +was no way to know that so many drivers would come along and introduce so many new variables and commands. The resulting mess grew out of control over the years. @@ -558,9 +558,9 @@ Here's a table to make it easier to visualize: |============================================= Version 2.0, and more recent, do not contain backwards compatibility for -the old protocol and variable/command names. As a result, 2.0 clients can't -talk to anything older than a 1.4 server. If you ask a 2.0 client to -fetch "STATUS", it will fail. You'll have to ask for "ups.status" +the old protocol and variable/command names. As a result, 2.0 clients can't +talk to anything older than a 1.4 server. If you ask a 2.0 client to +fetch "STATUS", it will fail. You'll have to ask for "ups.status" instead. Authors of separate monitoring programs should have used the 1.4 series From 41372b27b04afb5eba02c76394ff1a9ee5e97e40 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 4 Apr 2022 10:27:59 +0000 Subject: [PATCH 417/700] INSTALL.nut: trim trailing spaces --- INSTALL.nut | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/INSTALL.nut b/INSTALL.nut index 4708b56684..85dfbc0230 100644 --- a/INSTALL.nut +++ b/INSTALL.nut @@ -52,7 +52,7 @@ long as you are consistent. The process for doing this varies from one system to the next, and explaining how to add users is beyond the scope of this document. -For the purposes of this document, the user name and group name +For the purposes of this document, the user name and group name will be 'ups' and 'nut' respectively. Be sure the new user is a member of the new group! If you forget to @@ -87,7 +87,7 @@ docs/configure.txt or './configure --help' for all the available options. If you alter paths with additional switches, be sure to use those -new paths while reading the rest of the steps. +new paths while reading the rest of the steps. Reference: <> from the User Manual. @@ -100,7 +100,7 @@ Build the programs This will build the NUT client and server programs and the selected drivers. It will also build any other features that were -selected during <> step above. +selected during <> step above. Installation @@ -175,7 +175,7 @@ permissions for the USB device, you may need to set up (operating system dependent) hotplugging scripts. Sample scripts and information are provided in the scripts/hotplug and scripts/udev directories. For most users, the hotplugging scripts -will be installed automatically by "make install". +will be installed automatically by "make install". (If you want to try if a driver works without setting up hotplugging, you can add the "-u root" option to upsd, upsmon, and @@ -299,7 +299,7 @@ To install NUT as a package execute: Port ^^^^ -The port is located under +sysutils/nut+. +The port is located under +sysutils/nut+. Use +make config+ to select configuration options, e.g. to build the optional CGI scripts. To install it, use: From 8a5386343110f61e56469a3cbfceabbc8629485f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 4 Apr 2022 10:37:52 +0000 Subject: [PATCH 418/700] INSTALL.nut: add another perspective on packages vs source builds; refer to config-prereqs.txt --- INSTALL.nut | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/INSTALL.nut b/INSTALL.nut index 85dfbc0230..cd8ce6b860 100644 --- a/INSTALL.nut +++ b/INSTALL.nut @@ -1,11 +1,13 @@ Installation instructions ========================= -This chapter describe the various methods for installing Network UPS Tools. +This chapter describes the various methods for installing Network UPS Tools. Whenever it is possible, prefer <>. Packagers have done an excellent and hard work at improving NUT integration into -their system. +their system. On the other hand, distributions and appliances tend to package +"official releases" of projects such as NUT, and so do not deliver latest and +greatest fixes, new drivers, bugs and other features. [[Installing_source]] Installing from source @@ -16,6 +18,13 @@ These are the essential steps for compiling and installing this software. The NUT linkdoc:packager-guide[Packager Guide], which presents the best practices for installing and integrating NUT, is also a good reading. +The link:config-prereqs.txt[Prerequisites for building NUT on different OSes] +document suggests prerequisite packages with tools and dependencies +available and needed to build and test as much as possible of NUT on +numerous platforms, written from perspective of CI testing (if you +are interested in getting updated drivers for a particular device, +you might select a sub-set of those suggestions). + [NOTE] .Keep in mind that... ================================================================================ From 51e6b886ad430e6026f38af6a593941f91647ff3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 5 Apr 2022 22:33:54 +0000 Subject: [PATCH 419/700] docs/man/upsmon.conf.txt: fix markup (pluses make emphasis and xmllint goes upset) --- docs/man/upsmon.conf.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index 8aa44443cc..7a851bac64 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -226,7 +226,7 @@ REPLBATT;; The UPS battery is bad and needs to be replaced NOCOMM;; A UPS is unavailable (can't be contacted for monitoring) -*NOTIFYFLAG* 'type' 'flag'[\+'flag'][+'flag']...:: +*NOTIFYFLAG* 'type' 'flag'[+'flag']...:: By default, upsmon sends walls global messages to all logged in users) via /bin/wall and writes to the syslog when things happen. You can From 80be1346a03b262bb0846e4fd386b45eb2962465 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 7 Apr 2022 08:59:54 +0000 Subject: [PATCH 420/700] docs/nut.dict: update spellchecker --- docs/nut.dict | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/nut.dict b/docs/nut.dict index e30e7a6568..dc9a0f7c26 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2885 utf-8 +personal_ws-1.1 en 2898 utf-8 AAS ABI ACFAIL @@ -37,6 +37,8 @@ ATT ATTRS ATX ATs +AUTOCOMMIT +AUTOPUSH AVL AVR AVRLCD @@ -289,6 +291,7 @@ EG EL ELCD EMI +EMP ENDFOR ENV EOF @@ -452,6 +455,7 @@ ID's IDEN IDentifiers IFBETWEEN +IFF IFSUPP IGN IMG @@ -750,6 +754,7 @@ OOM OSABI OSF OSs +OUTDIR OUTPUTV OUTVOLT OV @@ -832,6 +837,7 @@ PULS PV PWLv PWR +PXG PaaS Pac Parisi @@ -894,6 +900,7 @@ Priv Procomm ProductID Prynych +Pulizzi PwrOut PyGTK PyNUT @@ -934,6 +941,7 @@ README REDi REPLBATT REQSSL +RESPIN RETPCT RK RMCARD @@ -1662,6 +1670,7 @@ devscan dfl dhcp dialout +difftool dipsw dir disp @@ -2101,6 +2110,7 @@ mecer megatec memset merchantability +mergetool metadata metasys methodOfFlowControl @@ -2377,6 +2387,7 @@ prog prtconf psu pty +pulizzi pw pwl pwmib @@ -2515,6 +2526,7 @@ shutdowntime shutup si siemens +sig sigaction sigmask signedness From a473ee0d769c2c9c14ba654074339029cd5d9459 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 7 Apr 2022 10:10:48 +0000 Subject: [PATCH 421/700] docs/nut.dict: update spellchecker --- docs/nut.dict | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/nut.dict b/docs/nut.dict index dc9a0f7c26..a07db10450 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2898 utf-8 +personal_ws-1.1 en 2899 utf-8 AAS ABI ACFAIL @@ -2848,6 +2848,7 @@ wf wget whitespace wiki +winnutclient wmnut wordformat workflow From efddee07f8347f0f6fa5392a8d894cf97f464d9d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 7 Apr 2022 12:54:58 +0000 Subject: [PATCH 422/700] drivers/usbhid-ups.c: instcmd(): wrap some long lines --- drivers/usbhid-ups.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 02905cea2f..d2ff4bb372 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -578,18 +578,22 @@ int instcmd(const char *cmdname, const char *extradata) if (!strcasecmp(cmdname, "beeper.off")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, - "The 'beeper.off' command has been renamed to 'beeper.disable'"); + "The 'beeper.off' command has been " + "renamed to 'beeper.disable'"); return instcmd("beeper.disable", NULL); } if (!strcasecmp(cmdname, "beeper.on")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, - "The 'beeper.on' command has been renamed to 'beeper.enable'"); + "The 'beeper.on' command has been " + "renamed to 'beeper.enable'"); return instcmd("beeper.enable", NULL); } - upsdebugx(1, "instcmd(%s, %s)", cmdname, extradata ? extradata : "[NULL]"); + upsdebugx(1, "instcmd(%s, %s)", + cmdname, + extradata ? extradata : "[NULL]"); /* Retrieve and check netvar & item_path */ hidups_item = find_nut_info(cmdname); @@ -597,7 +601,8 @@ int instcmd(const char *cmdname, const char *extradata) /* Check for fallback if not found */ if (hidups_item == NULL) { - upsdebugx(3, "%s: cmdname '%s' not found; checking for alternatives", + upsdebugx(3, "%s: cmdname '%s' not found; " + "checking for alternatives", __func__, cmdname); if (!strcasecmp(cmdname, "load.on")) { @@ -611,7 +616,8 @@ int instcmd(const char *cmdname, const char *extradata) if (!strcasecmp(cmdname, "shutdown.return")) { int ret; - /* Ensure "ups.start.auto" is set to "yes", if supported */ + /* Ensure "ups.start.auto" is set to "yes", + * if supported */ if (dstate_getinfo("ups.start.auto")) { setvar("ups.start.auto", "yes"); } From ef3c19d49637a8145b9f941f77a4ec1999994b0b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 7 Apr 2022 12:55:36 +0000 Subject: [PATCH 423/700] drivers/usbhid-ups.c: instcmd(): do not risk NULL-dereference debugging about "using Path" too early [#1346] --- drivers/usbhid-ups.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index d2ff4bb372..4479b29574 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -597,7 +597,6 @@ int instcmd(const char *cmdname, const char *extradata) /* Retrieve and check netvar & item_path */ hidups_item = find_nut_info(cmdname); - upsdebugx(3, "%s: using Path '%s'", __func__, hidups_item->hidpath); /* Check for fallback if not found */ if (hidups_item == NULL) { From f01a08ec290ef1717f2e5b20a77b420a64ddc914 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 7 Apr 2022 12:56:05 +0000 Subject: [PATCH 424/700] drivers/usbhid-ups.c: instcmd(): do not risk NULL-dereference debugging about "using Path" with a NULL hidpath (just in case) [#1346] --- drivers/usbhid-ups.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 4479b29574..f6e28c3b9c 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -659,7 +659,10 @@ int instcmd(const char *cmdname, const char *extradata) return STAT_INSTCMD_INVALID; } - upsdebugx(3, "%s: using Path '%s'", __func__, hidups_item->hidpath); + upsdebugx(3, "%s: using Path '%s'", + __func__, + (hidups_item->hidpath ? hidups_item->hidpath : "[NULL]") + ); /* Check if the item is an instant command */ if (!(hidups_item->hidflags & HU_TYPE_CMD)) { From b2e3f33b10dd901aef06f845196c4543f58ae728 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 8 Apr 2022 14:33:52 +0200 Subject: [PATCH 425/700] drivers/liebert-hid.c: get PowerWalker VFI 2000 TGS working, read values correctly This reverts commit c38b45a56a9d1283657d62a9fa1ed1af5daa1ba2 which added such support into mge-hid.c (at risk to proper support of earlier handled devices by various vendors), and transplants the new lines into liebert-hid.c See issue #560 for a HID walk from the device in question; data like that may help expand liebert-hid.c later (its table seems to map a lot less field names than mge-hid.c, but not sure if "true" Liebert/Phoenixtec HID devices support the rest). --- drivers/liebert-hid.c | 13 +++++++++++-- drivers/mge-hid.c | 10 ---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/liebert-hid.c b/drivers/liebert-hid.c index 1f05ebdd48..23851d8850 100644 --- a/drivers/liebert-hid.c +++ b/drivers/liebert-hid.c @@ -4,6 +4,7 @@ * 2003 - 2008 Arnaud Quette * 2005 - 2006 Peter Selinger * 2007 Charles Lepple + * 2018 Markus "Links2004" * * 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 @@ -26,7 +27,7 @@ #include "liebert-hid.h" #include "usb-common.h" -#define LIEBERT_HID_VERSION "Phoenixtec/Liebert HID 0.4" +#define LIEBERT_HID_VERSION "Phoenixtec/Liebert HID 0.41" /* FIXME: experimental flag to be put in upsdrv_info */ /* Phoenixtec Power Co., Ltd */ @@ -79,7 +80,7 @@ static hid_info_t liebert_hid2nut[] = { #endif { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.2f", 0, NULL }, - { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.2f", 0, NULL }, + { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.2f", HU_FLAG_STATIC, NULL }, { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", 0, stringid_conversion }, @@ -89,9 +90,17 @@ static hid_info_t liebert_hid2nut[] = { { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.1f", 0, NULL }, { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", NULL, "%.2f", 0, NULL }, + { "output.transfer.high", 0, 0, "UPS.PowerConverter.Output.HighVoltageTransfer", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, + { "output.transfer.low", 0, 0, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, + { "input.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Voltage", NULL, "%.1f", 0, NULL }, { "input.frequency", 0, 0, "UPS.PowerConverter.Input.[1].Frequency", NULL, "%.2f", 0, NULL }, + { "input.transfer.low", 0, 0, "UPS.PowerConverter.Output.ffff0057", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "input.transfer.high", 0, 0, "UPS.PowerConverter.Output.ffff0058", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL}, + { "input.frequency.transfer.low", 0, 0, "UPS.PowerConverter.Output.ffff00f9", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "input.frequency.transfer.high", 0, 0, "UPS.PowerConverter.Output.ffff00f8", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL}, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, "%.0f", HU_FLAG_QUICK_POLL, online_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, "%.0f", HU_FLAG_QUICK_POLL, lowbatt_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, "%.0f", HU_FLAG_QUICK_POLL, charging_info }, diff --git a/drivers/mge-hid.c b/drivers/mge-hid.c index c7342d1419..b07c5d82e7 100644 --- a/drivers/mge-hid.c +++ b/drivers/mge-hid.c @@ -1178,10 +1178,8 @@ static hid_info_t mge_hid2nut[] = { "battery.runtime.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.RemainingTimeLimit", NULL, "%.0f", 0, NULL }, { "battery.temperature", 0, 0, "UPS.BatterySystem.Battery.Temperature", NULL, "%s", 0, kelvin_celsius_conversion }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, - { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.1f", 0, NULL }, { "battery.voltage", 0, 0, "UPS.BatterySystem.Voltage", NULL, "%.1f", 0, NULL }, { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%s", 0, mge_battery_voltage }, - { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.2f", HU_FLAG_STATIC, NULL }, { "battery.voltage.nominal", 0, 0, "UPS.BatterySystem.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%s", HU_FLAG_STATIC, mge_battery_voltage_nominal }, { "battery.protection", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.BatterySystem.Battery.DeepDischargeProtection", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, @@ -1311,11 +1309,6 @@ static hid_info_t mge_hid2nut[] = { "input.current.nominal", 0, 0, "UPS.Flow.[1].ConfigCurrent", NULL, "%.2f", HU_FLAG_STATIC, NULL }, { "input.frequency", 0, 0, "UPS.PowerConverter.Input.[1].Frequency", NULL, "%.1f", 0, NULL }, { "input.frequency.nominal", 0, 0, "UPS.Flow.[1].ConfigFrequency", NULL, "%.0f", HU_FLAG_STATIC, NULL }, - { "input.transfer.low", 0, 0, "UPS.PowerConverter.Output.ffff0057", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, - { "input.transfer.high", 0, 0, "UPS.PowerConverter.Output.ffff0058", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL}, - { "input.frequency.transfer.low", 0, 0, "UPS.PowerConverter.Output.ffff00f9", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, - { "input.frequency.transfer.high", 0, 0, "UPS.PowerConverter.Output.ffff00f8", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL}, - /* same as "input.transfer.boost.low" */ { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.boost.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageBoostTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, @@ -1345,9 +1338,6 @@ static hid_info_t mge_hid2nut[] = { "input.bypass.frequency", 0, 0, "UPS.PowerConverter.Input.[2].Frequency", NULL, "%.1f", 0, NULL }, { "input.bypass.frequency.nominal", 0, 0, "UPS.Flow.[2].ConfigFrequency", NULL, "%.0f", HU_FLAG_STATIC, NULL }, - { "output.transfer.high", 0, 0, "UPS.PowerConverter.Output.HighVoltageTransfer", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, - { "output.transfer.low", 0, 0, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, - /* Output page */ { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.1f", 0, NULL }, { "output.L1-N.voltage", 0, 0, "UPS.PowerConverter.Output.Phase.[1].Voltage", NULL, "%.1f", 0, NULL }, From 1b71fd46b70bddcfdf8c57575dda5a57ccdb45c3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 8 Apr 2022 14:45:15 +0200 Subject: [PATCH 426/700] NEWS: improve PowerWalker VFI 2000 TGS support in NUT v2.7.5 --- NEWS | 3 +++ docs/nut.dict | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index dba6042ae9..9dabbcd9f8 100644 --- a/NEWS +++ b/NEWS @@ -183,6 +183,9 @@ refer to this change set (too long in the making) as NUT 2.7.5. * CPS HID: add input.frequency and output.frequency * OpenUPS2: only check OEM Information string once (fewer log messages) * Liebert GXT4 USB VID:PID [10AF:0000] + * add battery voltage and input/output transfer voltage and frequency + in Liebert/Phoenixtec HID mapping, to support PowerWalker VFI 2000 TGS + better [PR #564, issue #560] * add a little delay between multicommands [PR #1228] * fix Eaton/MGE mapping for beeper handling * add IBM USB VID diff --git a/docs/nut.dict b/docs/nut.dict index a07db10450..2020532b76 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2899 utf-8 +personal_ws-1.1 en 2900 utf-8 AAS ABI ACFAIL @@ -1143,6 +1143,7 @@ TCIOFLUSH TCSANOW TEMPC TEMPF +TGS TIMELEFT TIOCM TIOCMBIC From 889b3ba73876cf3cb86dbd92fdcf9d2cd0fe4976 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 8 Apr 2022 15:32:58 +0200 Subject: [PATCH 427/700] NEWS: tripplite_usb recognizes "3005" protocol for NUT v2.7.5 --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index ac9220c69d..41f576d8da 100644 --- a/NEWS +++ b/NEWS @@ -208,6 +208,8 @@ Release notes for NUT 2.7.5 - what's new since 2.7.4: and in particular to manage power separately on one or two outlet groups [PR #1048] + - tripplite_usb: updated to recognize the "3005" protocol [PR #584] + - libnutclient: introduce getDevicesVariableValues() to improve performances when querying many devices (up to 15 times faster) From 5952bc345715fe795c6e8ad816c976e3f4194ea2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 8 Apr 2022 15:41:42 +0200 Subject: [PATCH 428/700] docs/man/usbhid-ups.txt: update for "some PowerWalker models" --- docs/man/usbhid-ups.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/man/usbhid-ups.txt b/docs/man/usbhid-ups.txt index e38d040ee8..20c48948a2 100644 --- a/docs/man/usbhid-ups.txt +++ b/docs/man/usbhid-ups.txt @@ -31,6 +31,7 @@ At the present time, usbhid-ups supports: - some Belkin models, - some Cyber Power Systems models, - some Powercom models, + - some PowerWalker models, - some TrippLite models. For a more complete list, refer to the NUT hardware compatibility list, From ea406f4af20d744327835973b7f78601136bbad6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 8 Apr 2022 15:42:51 +0200 Subject: [PATCH 429/700] data/driver.list.in: update for "PowerWalker VFI 2000 TGS" via "usbhid-ups" [#564] --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index b9cd0a3908..9bbff925aa 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -919,6 +919,7 @@ "PowerWalker" "ups" "2" "Online VFI 1000RT/1500RT/2000RT/3000RT/6000RT/10000RT LCD" "" "blazer_usb" "PowerWalker" "ups" "2" "Line-Interactive VI 1000RT/1500RT/2000RT/3000RT LCD" "" "blazer_usb" "PowerWalker" "ups" "2" "VFI 1000 CG PF1" "" "nutdrv_qx" # https://powerwalker.com/?page=product&item=10122108&lang=en +"PowerWalker" "ups" "2" "VFI 2000 TGS" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/560 and https://github.com/networkupstools/nut/pull/564 "Powerware" "ups" "4" "3110" "" "genericups upstype=7" "Powerware" "ups" "4" "3115" "" "genericups upstype=11" From d30ffffb8f5558310653180c531835ec45a94b5b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 8 Apr 2022 16:01:11 +0200 Subject: [PATCH 430/700] drivers/mge-hid.c: mge_claim(): test that VendorID==PHOENIXTEC 0x06da has also Vendor or Product containing "AEG" --- drivers/mge-hid.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/mge-hid.c b/drivers/mge-hid.c index b07c5d82e7..15989bb5b5 100644 --- a/drivers/mge-hid.c +++ b/drivers/mge-hid.c @@ -1566,6 +1566,21 @@ static int mge_claim(HIDDevice_t *hd) { * not a UPS, so don't use possibly_supported here */ return 0; + + case PHOENIXTEC: + /* The vendorid 0x06da is primarily handled by + * liebert-hid, except for (maybe) AEG PROTECT NAS + * branded devices */ + if (hd->Vendor && strstr(hd->Vendor, "AEG")) { + return 1; + } + if (hd->Product && strstr(hd->Product, "AEG")) { + return 1; + } + + /* Let liebert-hid grab this */ + return 0; + default: /* Valid for Eaton */ /* by default, reject, unless the productid option is given */ if (getval("productid")) { @@ -1576,6 +1591,21 @@ static int mge_claim(HIDDevice_t *hd) { } case SUPPORTED: + + switch (hd->VendorID) + { + case PHOENIXTEC: /* see comments above */ + if (hd->Vendor && strstr(hd->Vendor, "AEG")) { + return 1; + } + if (hd->Product && strstr(hd->Product, "AEG")) { + return 1; + } + + /* Let liebert-hid grab this */ + return 0; + } + return 1; case NOT_SUPPORTED: From fafa7dedf56f10188b06e44563c59df69c0208ca Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 8 Apr 2022 16:13:07 +0200 Subject: [PATCH 431/700] NEWS: note Liebert/Phoenixtec vs MGE HID handling of VendorID 0x06da --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index dba6042ae9..9bc8332587 100644 --- a/NEWS +++ b/NEWS @@ -180,6 +180,10 @@ refer to this change set (too long in the making) as NUT 2.7.5. of firmware for) CyberPower CPS*EPFCLCD [issue #439, PR #1245] * added `onlinedischarge` option for UPSes that report `OL+DISCHRG` when wall power is lost [PR #811] + * changed detection of VendorID 0x06da handling of which is claimed + by Liebert/Phoenixtec HID historically, and MGE HID (for AEG PROTECT + NAS UPSes) since NUT 2.7.4, so that the higher-priority MGE subdriver + would not grab each and all of the devices exposing that ID [PR #1357] * CPS HID: add input.frequency and output.frequency * OpenUPS2: only check OEM Information string once (fewer log messages) * Liebert GXT4 USB VID:PID [10AF:0000] From 81e2218760950dba22224c35f5a4b2860fc861e3 Mon Sep 17 00:00:00 2001 From: Stuart Henderson Date: Sat, 9 Apr 2022 13:14:47 +0000 Subject: [PATCH 432/700] reinstate handling for STATUS(ONLINE) in usbhid-ups The recent onlinedischarge changes removed support for some STATUS(ONLINE) cases, affecting at least CP1300EPFCLCD - this brings them back. --- drivers/usbhid-ups.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index f6e28c3b9c..f678d3792f 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -1660,6 +1660,8 @@ static void ups_status_set(void) /* if we're calibrating */ status_set("OL"); /* on line */ } + } else if ((ups_status & STATUS(ONLINE))) { + status_set("OL"); } if ((ups_status & STATUS(DISCHRG)) && !(ups_status & STATUS(DEPLETED))) { From b99d14154126876e04b4167f5b684586ea78b6d9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 10 Apr 2022 10:51:59 +0000 Subject: [PATCH 433/700] drivers/mge-hid.c: bump version for #1357 --- drivers/mge-hid.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mge-hid.c b/drivers/mge-hid.c index 15989bb5b5..55d56cf4bf 100644 --- a/drivers/mge-hid.c +++ b/drivers/mge-hid.c @@ -38,7 +38,7 @@ #include "mge-hid.h" #include "nut_float.h" -#define MGE_HID_VERSION "MGE HID 1.45" +#define MGE_HID_VERSION "MGE HID 1.46" /* (prev. MGE Office Protection Systems, prev. MGE UPS SYSTEMS) */ /* Eaton */ @@ -56,6 +56,8 @@ /* AEG */ #define AEG_VENDORID 0x2b2d +/* Note that normally this VID is handled by Liebert/Phoenixtec HID mapping, + * here it is just for for AEG PROTECT NAS devices: */ /* Phoenixtec Power Co., Ltd */ #define PHOENIXTEC 0x06da From ef4735c98f2c2210d384a0ce77d85329fd1d9af7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 10 Apr 2022 10:53:48 +0000 Subject: [PATCH 434/700] drivers/tripplite_usb.c: bump version for #584 --- drivers/tripplite_usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tripplite_usb.c b/drivers/tripplite_usb.c index 48cc0d0243..7162414598 100644 --- a/drivers/tripplite_usb.c +++ b/drivers/tripplite_usb.c @@ -136,7 +136,7 @@ #include "usb-common.h" #define DRIVER_NAME "Tripp Lite OMNIVS / SMARTPRO driver" -#define DRIVER_VERSION "0.32" +#define DRIVER_VERSION "0.33" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -507,7 +507,7 @@ static void decode_v(const unsigned char *value) input_voltage_scaled = 230; break; - case 6: input_voltage_nominal = + case 6: input_voltage_nominal = input_voltage_scaled = 230; break; From 0a8075572cec6d8ad5105af7e86d871d2bb31d67 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 10 Apr 2022 10:55:04 +0000 Subject: [PATCH 435/700] drivers/usbhid-ups.c: bump version for #1356 and #1359 --- drivers/usbhid-ups.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index f678d3792f..86d3950084 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -28,7 +28,7 @@ */ #define DRIVER_NAME "Generic HID driver" -#define DRIVER_VERSION "0.46" +#define DRIVER_VERSION "0.47" #include "main.h" #include "libhid.h" From c3f63194e7e6a79e3ee96a6761d540d0b11bb022 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 10 Apr 2022 11:08:31 +0000 Subject: [PATCH 436/700] docs/FAQ.txt: suggest how to pick drivers for bogus vendor IDs --- docs/FAQ.txt | 20 ++++++++++++++++++++ docs/nut.dict | 5 ++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/FAQ.txt b/docs/FAQ.txt index 59f0ba6105..c8c7965902 100644 --- a/docs/FAQ.txt +++ b/docs/FAQ.txt @@ -551,6 +551,26 @@ Refer to the 'driver-name' (8) man page for more information. You can also consult the Hardware Compatibility List (HCL) and filter on USB: http://www.networkupstools.org/stable-hcl.html?connection=USB +== My USB UPS has a bogus Vendor ID 0x0001 and Product ID 0x0000, what driver supports it? + +Unfortunately, many devices are made without registering as a Vendor with +the corresponding standards body, and use generic USB chips for interfacing +with a computer (roughly similar to using a network interface card with a +random MAC address and PCI ID, and thus poorly identifiable device specifics +needed to automatically load some certain driver). Often they also lack a +unique serial number field, so monitoring several devices is problematic. + +One frequent case is with devices identifying as "Fry's Electronics" and/or +"MEC0003", if those data are served at all, or plain "0001/0000" in ID field. +In some cases they are accompanied by "UPSmart" software with a "MEGA(USB)" +connection option that works for Windows users. + +Your best bet is to search for community discussions of issues on NUT GitHub +at https://github.com/networkupstools/nut/issues?q=is%3Aissue and try options +there. Devices with these chips were known to connect with drivers for such +unrelated protocols as Megatec Qx (different sub-drivers, often `fabula` or +`hunnox`), ATCL, or USB-HID. + == My USB UPS is supported but doesn't work! On Linux, udev rules are provided to set the correct permissions on device diff --git a/docs/nut.dict b/docs/nut.dict index 2020532b76..13000d3a0e 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2900 utf-8 +personal_ws-1.1 en 2903 utf-8 AAS ABI ACFAIL @@ -600,6 +600,7 @@ MAXPARMAKES MBATTCHG MCOL MCU +MEC MEGATAEC MH MIBs @@ -792,6 +793,7 @@ PBTn PBTnn PC PC's +PCI PDC PDUs PDX @@ -1216,6 +1218,7 @@ UPSTEMP UPScode UPSes UPSilon +UPSmart UPSmon UPSonic UPSs From 9248fd0a4bc9d8d29ab4b9a4a80c5ab4f5031159 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 10 Apr 2022 11:27:23 +0000 Subject: [PATCH 437/700] ci_build.sh: use "gmake" for "./ci_build.sh spellcheck" if available and no particular MAKE was requested --- ci_build.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ci_build.sh b/ci_build.sh index 6075165f8b..36c72546f9 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -111,7 +111,7 @@ esac # (allowing to rebuild interactively and investigate that set-up)? [ -n "${CI_FAILFAST-}" ] || CI_FAILFAST=false -[ -n "$MAKE" ] || MAKE=make +[ -n "$MAKE" ] || [ "$1" = spellcheck ] || MAKE=make [ -n "$GGREP" ] || GGREP=grep [ -n "$MAKE_FLAGS_QUIET" ] || MAKE_FLAGS_QUIET="VERBOSE=0 V=0 -s" @@ -498,6 +498,11 @@ optional_dist_clean_check() { if [ "$1" = spellcheck ] && [ -z "$BUILD_TYPE" ] ; then # Note: this is a little hack to reduce typing # and scrolling in (docs) developer iterations. + if [ -z "${MAKE-}" ] && (command -v gmake) >/dev/null 2>/dev/null ; then + # GNU make processes quiet mode better, which helps with this use-case + MAKE=gmake + export MAKE + fi if [ -s Makefile ] && [ -s docs/Makefile ]; then echo "Processing quick and quiet spellcheck with already existing recipe files, will only report errors if any ..." build_to_only_catch_errors_target spellcheck ; exit From a6b29f36fbc6acc8d2e2221dbf7c1053392232b5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 10 Apr 2022 11:57:52 +0000 Subject: [PATCH 438/700] docs/FAQ.txt: suggest checking Linux USB HID Quirk [#630] --- docs/FAQ.txt | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ docs/nut.dict | 9 ++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/docs/FAQ.txt b/docs/FAQ.txt index c8c7965902..c6d9d955a9 100644 --- a/docs/FAQ.txt +++ b/docs/FAQ.txt @@ -624,6 +624,55 @@ There is a rudimentary locking mechanism in NUT, but there is a chance that the packages might not use the same directory as the NUT default, and the conflict will be reported by the kernel. +== Why does my (Eaton 5E) USB UPS on Linux connect but quickly disconnects soon? + +This issue was extensively investigated by NUT community members in +https://github.com/networkupstools/nut/issues/630 and resulted in a +chain of distribution bugs logged such as +https://bugzilla.redhat.com/show_bug.cgi?id=1715504 + +The gist of it is that some versions of Linux kernel used an "USB HID quirk" +for certain devices, see Linux kernel source `drivers/hid/hid-quirks.c` file, +including MGE/Eaton vendor ID (0x0463) based on an older device a contributor +had issues with. Firmware in newer devices no longer had the bug which needed +the "quirk" and misbehaved when it was enabled. While newer (and much older) +Linux kernels should not have the problem, with the quirk removed according +to https://lkml.org/lkml/2018/11/26/580 having the issue in the field really +depends on the combination of Linux kernel and device firmware that meet. + +Either way, it seems that problematic combinations preclude Linux from seeing +the device as a `hid-generic` first, to hand it over to a NUT driver. + +This quirk can be tuned with a kernel boot parameter (via GRUB etc.): + + usbhid.quirks=0x0463:0xffff:0x08 + +to re-enable the NOGET quirk. + +For context, according to https://bugzilla.redhat.com/show_bug.cgi?id=1875532 +the symptoms for the problem look like this: + + # Plug in the UPS and observe the dmesg logs, + # the following continuously appears: + [ 93.568082] usb 1-6: new full-speed USB device number 9 using xhci_hcd + [ 94.311469] usb 1-6: New USB device found, idVendor=0463, idProduct=ffff, bcdDevice= 0.01 + [ 94.311475] usb 1-6: New USB device strings: Mfr=1, Product=2, SerialNumber=0 + [ 94.311483] usb 1-6: Product: 5E + [ 94.311486] usb 1-6: Manufacturer: EATON + [ 96.269989] hid-generic 0003:0463:FFFF.000A: hiddev96,hidraw2: USB HID v1.10 Device [EATON 5E] on usb-0000:00:14.0-6/input0 + [ 107.369425] hid-generic 0003:0463:FFFF.000A: usb_submit_urb(ctrl) failed: -1 + [ 107.369469] hid-generic 0003:0463:FFFF.000A: timeout initializing reports + [ 112.828826] usb 1-6: USB disconnect, device number 9 + [ 113.284452] usb 1-6: new full-speed USB device number 10 using xhci_hcd + [ 114.027693] usb 1-6: New USB device found, idVendor=0463, idProduct=ffff, bcdDevice= 0.01 + [ 114.027698] usb 1-6: New USB device strings: Mfr=1, Product=2, SerialNumber=0 + [ 114.027701] usb 1-6: Product: 5E + [ 114.027704] usb 1-6: Manufacturer: EATON + [ 115.984222] hid-generic 0003:0463:FFFF.000B: hiddev96,hidraw2: USB HID v1.10 Device [EATON 5E] on usb-0000:00:14.0-6/input0 + [ 126.825756] hid-generic 0003:0463:FFFF.000B: usb_submit_urb(ctrl) failed: -1 + [ 126.825775] hid-generic 0003:0463:FFFF.000B: timeout initializing reports + [ 132.527809] usb 1-6: USB disconnect, device number 10 + == Why doesn't my package work? Or a variation like... diff --git a/docs/nut.dict b/docs/nut.dict index 13000d3a0e..8190dc75a7 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2903 utf-8 +personal_ws-1.1 en 2910 utf-8 AAS ABI ACFAIL @@ -697,6 +697,7 @@ NOAUTH NOCOMM NOCOMMWARNTIME NOCONF +NOGET NOMBATTV NOMINV NOMOUTV @@ -1081,6 +1082,7 @@ SendEnv Senoidal Sep Sequentializing +SerialNumber Serv Shara Shaul @@ -1627,6 +1629,7 @@ csh cshdelay css cts +ctrl ctypes cua cuaa @@ -1863,11 +1866,13 @@ hardcoded hasFeature hb hcl +hcd hg hh hibernate's hiddev hidparser +hidraw hidtypes hidups highbattery @@ -2788,6 +2793,7 @@ upstype upsuser upswired uptime +urb urpmi usb usbconfig @@ -2878,6 +2884,7 @@ xfff xffff xfmrresistance xh +xhci xhtml xmalloc xml From f28ec588e332061f92b51c9000e2226ae649998f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 10 Apr 2022 12:11:35 +0000 Subject: [PATCH 439/700] docs/FAQ.txt: add more reasons and symptoms that a running driver disappears --- docs/FAQ.txt | 24 ++++++++++++++++++++++++ docs/nut.dict | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/FAQ.txt b/docs/FAQ.txt index c6d9d955a9..7c5c9f8eea 100644 --- a/docs/FAQ.txt +++ b/docs/FAQ.txt @@ -673,6 +673,30 @@ the symptoms for the problem look like this: [ 126.825775] hid-generic 0003:0463:FFFF.000B: timeout initializing reports [ 132.527809] usb 1-6: USB disconnect, device number 10 +A similar report on the driver side may look like: + + usbhid-ups[4554]: libusb_get_report: Input/output error + upsd[4591]: Data for UPS [eaton] is stale - check driver + usbhid-ups[4554]: Can't claim USB device [0463:ffff]: No such file or directory + upsd[4591]: Can't connect to UPS [eaton] (usbhid-ups-eaton): No such file or directory + upsmon[5148]: Poll UPS [eaton@localhost] failed - Driver not connected + upsmon[5148]: Communications with UPS eaton@localhost lost + +Other similar looking issues may include improper setup of udev, upower +and similar frameworks to hand over the device from the OS to a driver +daemon; competition with other software probing USB devices (ModemManager +was mentioned in this context), including running several copies of the +NUT drivers trying to use same port (e.g. one started by services and +another manually as you tried to debug the problems). + +Software quirks aside, please do test with a different USB cable and/or port +on the computer. These were known to cause grief beyond what can be fixed +with a few key words ;) + +Finally, sometimes the issue is on the OS side (and/or USB chipset), to +the point that the USB driver can not be unloaded and re-attached until +you power cycle the system. + == Why doesn't my package work? Or a variation like... diff --git a/docs/nut.dict b/docs/nut.dict index 8190dc75a7..903e735ed4 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2910 utf-8 +personal_ws-1.1 en 2911 utf-8 AAS ABI ACFAIL @@ -662,6 +662,7 @@ MiniGuard Minislot Moar Modbus +ModemManager MonAMI MonUPS Monett From b58296ae29ec33b3c69b5086b958f797577167e9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 10 Apr 2022 12:52:07 +0000 Subject: [PATCH 440/700] Report deprecation messages when starting older Qx drivers And document similarly in their man pages (bestups, blazer, masterguard) --- docs/man/bestups.txt | 13 +++++++++++++ docs/man/blazer-common.txt | 12 ++++++++++++ docs/man/masterguard.txt | 18 ++++++++++++++---- drivers/bestups.c | 9 +++++++++ drivers/blazer.c | 9 +++++++++ drivers/masterguard.c | 14 ++++++++++++++ 6 files changed, 71 insertions(+), 4 deletions(-) diff --git a/docs/man/bestups.txt b/docs/man/bestups.txt index 9d80e69d64..61496c5750 100644 --- a/docs/man/bestups.txt +++ b/docs/man/bestups.txt @@ -13,6 +13,19 @@ This man page only documents the hardware-specific features of the bestups driver. For information about the core driver, see linkman:nutupsdrv[8]. +NOTE +---- + +Please note that this driver is deprecated and will not receive +new development. If it works for managing your devices -- fine, +but if you are running it to try setting up a new device, please +consider the newer linkman:nutdrv_qx[8] instead, which should +handle all 'Q*' protocol variants for NUT. ++ +Please do also report if your device works with this driver, +but linkman:nutdrv_qx[8] would not actually support it with any +subdriver! + SUPPORTED HARDWARE ------------------ *bestups* was designed to monitor Best Power UPS hardware like the Fortress, diff --git a/docs/man/blazer-common.txt b/docs/man/blazer-common.txt index 68d00814a9..834f77785e 100644 --- a/docs/man/blazer-common.txt +++ b/docs/man/blazer-common.txt @@ -4,6 +4,18 @@ This man page only documents the hardware-specific features of the blazer driver. For information about the core driver, see linkman:nutupsdrv[8]. +NOTE +---- + +Please note that this driver is deprecated and will not receive +new development. If it works for managing your devices -- fine, +but if you are running it to try setting up a new device, please +consider the newer linkman:nutdrv_qx[8] instead, which should +handle all 'Q*' protocol variants for NUT. ++ +Please do also report if your device works with this driver, +but linkman:nutdrv_qx[8] would not actually support it with any +subdriver! SUPPORTED HARDWARE ------------------ diff --git a/docs/man/masterguard.txt b/docs/man/masterguard.txt index 3a4982316e..0e07a9de2e 100644 --- a/docs/man/masterguard.txt +++ b/docs/man/masterguard.txt @@ -5,14 +5,24 @@ NAME ---- masterguard - Driver for Masterguard UPS equipment -NOTES ------ +NOTE +---- This man page only documents the hardware-specific features of the masterguard driver. For information about the core driver, see linkman:nutupsdrv[8]. -There's a much newer and more comprehensive driver based on the Q* -framework that also supports USB, see linkman:nutdrv_qx[8]. +NOTE +---- + +Please note that this driver is deprecated and will not receive +new development. If it works for managing your devices -- fine, +but if you are running it to try setting up a new device, please +consider the newer linkman:nutdrv_qx[8] instead, which should +handle all 'Q*' protocol variants for NUT. ++ +Please do also report if your device works with this driver, +but linkman:nutdrv_qx[8] would not actually support it with any +subdriver! SUPPORTED HARDWARE ------------------ diff --git a/drivers/bestups.c b/drivers/bestups.c index c0d56d7b48..4f63aa5146 100644 --- a/drivers/bestups.c +++ b/drivers/bestups.c @@ -436,6 +436,15 @@ void upsdrv_makevartable(void) void upsdrv_initups(void) { + upsdebugx(0, + "Please note that this driver is deprecated and will not receive\n" + "new development. If it works for managing your devices - fine,\n" + "but if you are running it to try setting up a new device, please\n" + "consider the newer nutdrv_qx instead, which should handle all 'Qx'\n" + "protocol variants for NUT. (Please also report if your device works\n" + "with this driver, but nutdrv_qx would not actually support it with\n" + "any subdriver!)\n"); + upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); } diff --git a/drivers/blazer.c b/drivers/blazer.c index 6068bb4839..6e06b4236f 100644 --- a/drivers/blazer.c +++ b/drivers/blazer.c @@ -696,6 +696,15 @@ void blazer_initinfo(void) const char *protocol = getval("protocol"); int retry; + upsdebugx(0, + "Please note that this driver is deprecated and will not receive\n" + "new development. If it works for managing your devices - fine,\n" + "but if you are running it to try setting up a new device, please\n" + "consider the newer nutdrv_qx instead, which should handle all 'Qx'\n" + "protocol variants for NUT. (Please also report if your device works\n" + "with this driver, but nutdrv_qx would not actually support it with\n" + "any subdriver!)\n"); + for (proto = 0; command[proto].status; proto++) { int ret = -1; diff --git a/drivers/masterguard.c b/drivers/masterguard.c index 89609ba2e7..46364f6f50 100644 --- a/drivers/masterguard.c +++ b/drivers/masterguard.c @@ -4,6 +4,11 @@ masterguard.c created on 15.8.2001 + OBSOLETION WARNING: Please to not base new development on this + codebase, instead create a new subdriver for nutdrv_qx which + generally covers all Megatec/Qx protocol family and aggregates + device support from such legacy drivers over time. + 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 the Free Software Foundation; either version 2 of the License, or @@ -562,6 +567,15 @@ void upsdrv_initups(void) int fail = 0; int good = 0; + upsdebugx(0, + "Please note that this driver is deprecated and will not receive\n" + "new development. If it works for managing your devices - fine,\n" + "but if you are running it to try setting up a new device, please\n" + "consider the newer nutdrv_qx instead, which should handle all 'Qx'\n" + "protocol variants for NUT. (Please also report if your device works\n" + "with this driver, but nutdrv_qx would not actually support it with\n" + "any subdriver!)\n"); + /* setup serial port */ upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); From 122df4d8489ef3e35ba97e1868a3cdb29f01dc4d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 10 Apr 2022 18:22:16 +0000 Subject: [PATCH 441/700] UPGRADING: detail about "OBSOLETION WARNING" for "Megatec Q*" --- UPGRADING | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/UPGRADING b/UPGRADING index 1be850f20b..c04c2a54b8 100644 --- a/UPGRADING +++ b/UPGRADING @@ -50,10 +50,11 @@ Changes from 2.7.4 to 2.8.0 - oldmge-shut has been removed, and replaced by mge-shut. -- New drivers for devices with "Qx" family of protocols should be developed - as sub-drivers in the `nutdrv_qx` framework for USB and Serial connected - devices, not as updates/clones of older e.g. `blazer` family and `bestups`. - Sources of such older drivers were marked with "OBSOLETION WARNING". +- New drivers for devices with "Qx" (also known as "Megatec Q*") family of + protocols should be developed as sub-drivers in the `nutdrv_qx` framework + for USB and Serial connected devices, not as updates/clones of older e.g. + `blazer` family and `bestups`. Sources, man pages and start-up messages + of such older drivers were marked with "OBSOLETION WARNING". - liebert-esp2: some multi-phase variable names have been updated to match the rest of NUT. From d1205bac7ba438aeeb2174128e72b4fb9afb8dfb Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 10 Apr 2022 18:25:28 +0000 Subject: [PATCH 442/700] docs/FAQ.txt: clarify about nutdrv_qx --- docs/FAQ.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/FAQ.txt b/docs/FAQ.txt index 7c5c9f8eea..ff8038db67 100644 --- a/docs/FAQ.txt +++ b/docs/FAQ.txt @@ -544,7 +544,11 @@ There are several driver to support USB models. - usbhid-ups supports various manufacturers complying to the HID Power Device Class (PDC) standard, - tripplite_usb supports various older Tripp-Lite units (with USB ProductID 0001) - bcmxcp_usb supports various Powerware units, -- nutdrv_qx and blazer_usb support various manufacturers that use the Megatec / Q1 protocol. +- blazer_usb supports various manufacturers that use the Megatec / Q1 protocol. +- nutdrv_qx supports various manufacturers that use the Megatec / Q* protocol + family. This is the driver slated to receive all further development in this + area, it was specially designed to support many more sub-drivers and has + added a lot over time, so please do try it first nowadays. Refer to the 'driver-name' (8) man page for more information. @@ -568,7 +572,7 @@ connection option that works for Windows users. Your best bet is to search for community discussions of issues on NUT GitHub at https://github.com/networkupstools/nut/issues?q=is%3Aissue and try options there. Devices with these chips were known to connect with drivers for such -unrelated protocols as Megatec Qx (different sub-drivers, often `fabula` or +unrelated protocols as Megatec Q* (different sub-drivers, often `fabula` or `hunnox`), ATCL, or USB-HID. == My USB UPS is supported but doesn't work! From 7cf436a3de1409de8761d97636bebf0f69ce36ba Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 10 Apr 2022 20:16:57 +0000 Subject: [PATCH 443/700] docs/man: be sure to keep a blank line after SYNOPSIS and other titles [#1362] --- docs/daisychain.txt | 1 + docs/man/adelsystem_cbi.txt | 7 ++++-- docs/man/al175.txt | 10 ++++++++ docs/man/apcsmart-old.txt | 2 ++ docs/man/apcsmart.txt | 1 + docs/man/apcupsd-ups.txt | 19 ++++++++++----- docs/man/asem.txt | 14 +++++++---- docs/man/bcmxcp.txt | 11 +++++++++ docs/man/bcmxcp_usb.txt | 12 ++++++++++ docs/man/belkin.txt | 4 ++++ docs/man/belkinunv.txt | 22 ++++++++++++------ docs/man/bestfcom.txt | 14 ++++++----- docs/man/bestfortress.txt | 14 +++++++---- docs/man/bestuferrups.txt | 11 +++++---- docs/man/bestups.txt | 11 ++++++--- docs/man/blazer-common.txt | 10 ++++---- docs/man/clone.txt | 9 ++++++++ docs/man/dummy-ups.txt | 6 +++++ docs/man/etapro.txt | 8 +++++++ docs/man/everups.txt | 6 +++++ docs/man/gamatronic.txt | 6 +++++ docs/man/generic_modbus.txt | 7 ++++-- docs/man/genericups.txt | 12 ++++++++-- docs/man/hosts.conf.txt | 1 + docs/man/huawei-ups2000.txt | 15 +++++++----- docs/man/isbmex.txt | 4 ++++ docs/man/ivtscd.txt | 8 +++++++ docs/man/libnutclient.txt | 2 ++ docs/man/libnutclient_commands.txt | 1 + docs/man/libnutclient_devices.txt | 1 + docs/man/libnutclient_general.txt | 1 + docs/man/libnutclient_misc.txt | 1 + docs/man/libnutclient_tcp.txt | 1 + docs/man/libnutclient_variables.txt | 1 + docs/man/libupsclient-config.txt | 3 +++ docs/man/liebert-esp2.txt | 12 ++++++++-- docs/man/liebert.txt | 4 ++++ docs/man/macosx-ups.txt | 10 ++++++-- docs/man/masterguard.txt | 7 ++++++ docs/man/metasys.txt | 6 +++++ docs/man/mge-shut.txt | 2 ++ docs/man/mge-utalk.txt | 20 ++++++++-------- docs/man/microdowell.txt | 8 +++++-- docs/man/microsol-apc.txt | 10 +++++--- docs/man/netxml-ups.txt | 11 ++++++++- docs/man/nut-driver-enumerator.txt | 3 +++ docs/man/nut-ipmipsu.txt | 7 ++++-- docs/man/nut-recorder.txt | 13 ++++++++--- docs/man/nut-scanner.txt | 8 ++++--- docs/man/nut.conf.txt | 6 +++-- docs/man/nutdrv_atcl_usb.txt | 9 ++++++++ docs/man/nutdrv_siemens_sitop.txt | 13 +++++++++++ docs/man/nutscan.txt | 8 ++++++- docs/man/nutscan_add_device_to_device.txt | 11 +++++---- docs/man/nutscan_add_option_to_device.txt | 11 +++++---- docs/man/nutscan_cidr_to_ip.txt | 1 + docs/man/nutscan_display_parsable.txt | 1 + docs/man/nutscan_display_ups_conf.txt | 1 + docs/man/nutscan_free_device.txt | 1 + docs/man/nutscan_get_serial_ports_list.txt | 1 + docs/man/nutscan_init.txt | 1 + docs/man/nutscan_new_device.txt | 1 + docs/man/nutscan_scan_avahi.txt | 7 +++++- docs/man/nutscan_scan_eaton_serial.txt | 1 + docs/man/nutscan_scan_ipmi.txt | 1 + docs/man/nutscan_scan_nut.txt | 1 + docs/man/nutscan_scan_snmp.txt | 1 + docs/man/nutscan_scan_usb.txt | 1 + docs/man/nutscan_scan_xml_http.txt | 1 + docs/man/nutupsdrv.txt | 16 ++++++++++++- docs/man/oneac.txt | 6 +++++ docs/man/optiups.txt | 7 ++++-- docs/man/phoenixcontact_modbus.txt | 7 ++++-- docs/man/pijuice.txt | 8 +++++++ docs/man/powercom.txt | 27 +++++++++++++++++----- docs/man/powerman-pdu.txt | 10 ++++++-- docs/man/powerpanel.txt | 12 ++++++++++ docs/man/rhino.txt | 8 ++++++- docs/man/richcomm_usb.txt | 11 +++++++-- docs/man/riello_ser.txt | 2 ++ docs/man/riello_usb.txt | 2 ++ docs/man/safenet.txt | 13 +++++++++-- docs/man/skel.txt | 8 +++++++ docs/man/snmp-ups.txt | 9 ++++++++ docs/man/socomec_jbus.txt | 11 +++++---- docs/man/solis.txt | 6 ++++- docs/man/tripplite.txt | 12 +++++++++- docs/man/tripplite_usb.txt | 24 +++++++++++-------- docs/man/tripplitesu.txt | 8 +++++++ docs/man/ups.conf.txt | 7 +++++- docs/man/upsc.txt | 8 ++++--- docs/man/upscli_add_host_cert.txt | 2 ++ docs/man/upscli_cleanup.txt | 2 ++ docs/man/upscli_connect.txt | 2 ++ docs/man/upscli_disconnect.txt | 2 ++ docs/man/upscli_get.txt | 10 +++++++- docs/man/upscli_init.txt | 2 ++ docs/man/upscli_list_next.txt | 1 + docs/man/upscli_list_start.txt | 1 + docs/man/upscli_readline.txt | 1 + docs/man/upscli_sendline.txt | 1 - docs/man/upscli_splitname.txt | 1 + docs/man/upsclient.txt | 2 ++ docs/man/upscmd.txt | 8 +++++-- docs/man/upscode2.txt | 15 ++++++++---- docs/man/upsd.conf.txt | 5 ++-- docs/man/upsd.txt | 5 ++++ docs/man/upsd.users.txt | 6 +++-- docs/man/upsdrvctl.txt | 3 +++ docs/man/upsdrvsvcctl.txt | 3 +++ docs/man/upsimage.cgi.txt | 5 ++-- docs/man/upslog.txt | 3 +++ docs/man/upsmon.conf.txt | 2 ++ docs/man/upsmon.txt | 4 ++++ docs/man/upsrw.txt | 2 ++ docs/man/upssched.conf.txt | 1 + docs/man/upssched.txt | 2 ++ docs/man/upsset.cgi.txt | 1 + docs/man/upsset.conf.txt | 2 ++ docs/man/upsstats.cgi.txt | 1 + docs/man/upsstats.html.txt | 1 + docs/man/usbhid-ups.txt | 2 ++ docs/man/victronups.txt | 16 +++++++++---- docs/nut-names.txt | 2 ++ docs/packager-guide.txt | 3 +++ 125 files changed, 648 insertions(+), 148 deletions(-) diff --git a/docs/daisychain.txt b/docs/daisychain.txt index f752839541..dd7b1531f1 100644 --- a/docs/daisychain.txt +++ b/docs/daisychain.txt @@ -82,6 +82,7 @@ device(s) that have alarms vs. nothing?) Example ^^^^^^^ + Here is an example excerpt of three PDUs, connected in daisychain mode, with one master and two slaves: diff --git a/docs/man/adelsystem_cbi.txt b/docs/man/adelsystem_cbi.txt index 414f6ef443..ef22e5caa7 100644 --- a/docs/man/adelsystem_cbi.txt +++ b/docs/man/adelsystem_cbi.txt @@ -95,16 +95,19 @@ account to access it. AUTHOR ------ + Dimitris Economou SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8], linkman:ups.conf[5] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ -libmodbus home page: http://libmodbus.org +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* libmodbus home page: http://libmodbus.org diff --git a/docs/man/al175.txt b/docs/man/al175.txt index 4fcd2216ef..7ba616b730 100644 --- a/docs/man/al175.txt +++ b/docs/man/al175.txt @@ -3,16 +3,19 @@ AL175(8) NAME ---- + al175 - Driver for Eltek UPS models with AL175 alarm module NOTE ---- + This man page only documents the hardware-specific features of the *al175* driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + The *al175* driver is known to work with the following UPSes: Eltek MPSU4000 with AL175 alarm module @@ -26,11 +29,13 @@ See documentation supplied with your hardware on how to do it. EXTRA ARGUMENTS --------------- + This driver does not support any extra settings in the linkman:ups.conf[5]. INSTANT COMMANDS ---------------- + This driver supports some extra commands (see linkman:upscmd[8]): *test.battery.start*:: @@ -41,6 +46,7 @@ Stop a battery test. VARIABLES --------- + Besides status, this driver reads UPS state into following variables: - *ups.test.result* @@ -53,12 +59,14 @@ Besides status, this driver reads UPS state into following variables: KNOWN ISSUES AND BUGS --------------------- + * Shutdown is not supported. FIXME * The driver was reworked to meet the project code quality criteria, without testing on real hardware. Some bugs may have crept in. AUTHOR ------ + Kirill Smelkov SEE ALSO @@ -66,8 +74,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/apcsmart-old.txt b/docs/man/apcsmart-old.txt index f2c8f4ae51..72f7f8e917 100644 --- a/docs/man/apcsmart-old.txt +++ b/docs/man/apcsmart-old.txt @@ -96,8 +96,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/apcsmart.txt b/docs/man/apcsmart.txt index ff20bf9a10..2f2f14ffa1 100644 --- a/docs/man/apcsmart.txt +++ b/docs/man/apcsmart.txt @@ -384,6 +384,7 @@ linkman:solis[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ // vim: tw=80 ai si ts=8 sts=4 sw=4 et : diff --git a/docs/man/apcupsd-ups.txt b/docs/man/apcupsd-ups.txt index 74321de793..1017bd2f84 100644 --- a/docs/man/apcupsd-ups.txt +++ b/docs/man/apcupsd-ups.txt @@ -3,17 +3,20 @@ APCUPSD-UPS(8) NAME ---- + apcupsd-ups - Driver for apcupsd client access NOTE ---- + This man page only documents the specific features of the *apcupsd-ups* driver. For information about the core driver, see linkman:nutupsdrv[8]. DESCRIPTION ----------- -This driver is a client to *apcupsd*. + +This driver is a client to *apcupsd* (another UPS monitoring project). *apcupsd-ups* acts as an *apcupsd* client, simply forwarding data. This can be useful in cases where both protocols are required in a network, @@ -37,13 +40,15 @@ For instance: BACKGROUND ---------- -This driver was originally written in one evening to allow interoperating with *apcupsd*. +This driver was originally written in one evening to allow interoperating +with *apcupsd*. SUPPORTED VARIABLES ------------------- -The following variables are translated from *apcupsd* to NUT. All times should be -converted to seconds (please file a bug if you notice a mismatch in units). +The following variables are translated from *apcupsd* to NUT. +All times should be converted to seconds (please file a bug +if you notice a mismatch in units). [width="50%",cols="m,m",options="header"] |=============================== @@ -85,6 +90,7 @@ converted to seconds (please file a bug if you notice a mismatch in units). LIMITATIONS ----------- + Access to *apcupsd* is strictly read only: no commands can be issued. This stems from the design of *apcupsd*, where the settings are changed in *apctest*. In order to run *apctest*, *apcupsd* must be stopped (and *apcupsd* @@ -92,6 +98,7 @@ exposes the UPS to the network). AUTHOR ------ + Andreas Steinmetz SEE ALSO @@ -102,6 +109,6 @@ linkman:nutupsdrv[8] Internet Resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ -The apcupsd home page: http://www.apcupsd.org/ +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The apcupsd home page: http://www.apcupsd.org/ diff --git a/docs/man/asem.txt b/docs/man/asem.txt index 5501275ad0..867112266c 100644 --- a/docs/man/asem.txt +++ b/docs/man/asem.txt @@ -3,16 +3,19 @@ ASEM(8) NAME ---- + asem - driver for UPS in ASEM PB1300 NOTE ---- + This man page only documents the hardware-specific features of the *asem* driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + The *asem* driver supports the UPS in ASEM PB1300 embedded PCs. Likely other I2C devices from the same manufacturer will work too, since this is a "custom" charger. @@ -41,6 +44,7 @@ Set the high battery threshold to 'num' volts. INSTALLATION ------------ + This driver is specific to the Linux I2C API, and requires the lm_sensors libi2c-dev or its equivalent to compile. @@ -60,11 +64,13 @@ DIAGNOSTICS KNOWN ISSUES AND BUGS --------------------- + The driver shutdown function is not implemented, so other arrangements must be made to turn off the UPS. AUTHORS ------- + Giuseppe Corbelli SEE ALSO @@ -72,12 +78,12 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -PB1300 specifications: http://www.asem.it/en/products/industrial-automation/box-pcs/performance/pb1300/ - -BQ2060 datasheet: http://www.ti.com/lit/ds/symlink/bq2060.pdf -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* PB1300 specifications: http://www.asem.it/en/products/industrial-automation/box-pcs/performance/pb1300/ +* BQ2060 datasheet: http://www.ti.com/lit/ds/symlink/bq2060.pdf +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/bcmxcp.txt b/docs/man/bcmxcp.txt index 6ad8555432..037e1b0f48 100644 --- a/docs/man/bcmxcp.txt +++ b/docs/man/bcmxcp.txt @@ -8,12 +8,14 @@ bcmxcp - Driver for UPSes supporting the serial BCM/XCP protocol NOTE ---- + This man page only documents the hardware-specific features of the bcmxcp driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver should recognize all serial BCM/XCP-compatible UPSes. It has been developed and tested on Powerware PW5115 and PW9120 hardware. If your UPS has a USB connection, you may also consult the linkman:bcmxcp_usb[8] driver @@ -21,6 +23,7 @@ documentation. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]. @@ -36,11 +39,13 @@ connected with. If not included in the config, it defaults to baud-hunting. DEFAULT VALUES FOR THE EXTRA ARGUMENTS -------------------------------------- + - *shutdown_delay =* '120' - *baud_rate =* 'none' INSTANT COMMANDS ---------------- + This driver supports the following Instant Commands: *shutdown.return*:: @@ -67,22 +72,28 @@ Access the config register to change settings. BUGS ---- + None known. AUTHOR ------ + Tore Ørpetveit SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] The USB BCM/XCP driver: ~~~~~~~~~~~~~~~~~~~~~~~ + linkman:bcmxcp_usb[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/bcmxcp_usb.txt b/docs/man/bcmxcp_usb.txt index 25bb5cd1ed..5f18c8bd0a 100644 --- a/docs/man/bcmxcp_usb.txt +++ b/docs/man/bcmxcp_usb.txt @@ -3,10 +3,12 @@ BCMXCP_USB(8) NAME ---- + bcmxcp_usb - Experimental driver for UPSes supporting the BCM/XCP protocol over USB NOTE ---- + This man page only documents the hardware-specific features of the bcmxcp_usb driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -14,6 +16,7 @@ This driver is a variant of the serial driver bcmxcp and uses the same core code SUPPORTED HARDWARE ------------------ + This driver should recognize all BCM/XCP-compatible UPSes that are connected via USB. It has been developed and tested on Powerware PW3501 hardware. It also has been tested on PW5110 hardware. @@ -30,10 +33,12 @@ shutdown command and actually shutting off. DEFAULT VALUES FOR THE EXTRA ARGUMENTS -------------------------------------- + *shutdown_delay =*'120' INSTANT COMMANDS ---------------- + This driver supports the following Instant Commands: *shutdown.return*:: @@ -56,6 +61,7 @@ BCM/XCP supports reporting of UPS statistics data. EXPERIMENTAL DRIVER ------------------- + This driver has been tagged experimental, even if it has been reported to be stable. Thus it is not suitable for production systems and it is not built by default. This is mainly due to the fact that it is a @@ -63,6 +69,7 @@ new driver. INSTALLATION ------------ + This driver is not built by default. You can build it by using "configure --with-usb=yes". Note that it will also install other USB drivers. @@ -74,6 +81,7 @@ must have execution flag set (ie using chmod +x ...). IMPLEMENTATION -------------- + bcmxcp_usb only supports 1 UPS at this time. You can put the "auto" value for port in `ups.conf`, i.e.: @@ -83,6 +91,7 @@ bcmxcp_usb only supports 1 UPS at this time. You can put the KNOWN ISSUES AND BUGS --------------------- + "Got EPERM: Operation not permitted upon driver startup" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -92,6 +101,7 @@ hotplug so that it applies these changes. AUTHOR ------ + Tore Ørpetveit , Wolfgang Ocker @@ -100,8 +110,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/belkin.txt b/docs/man/belkin.txt index 51760d9907..57e032042d 100644 --- a/docs/man/belkin.txt +++ b/docs/man/belkin.txt @@ -15,6 +15,7 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + The *belkin* driver is known to support the Regulator Pro 525 (F6C525-SER). Other similar models such as the 425 and 625 should also work. @@ -46,10 +47,12 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Other Belkin drivers: ~~~~~~~~~~~~~~~~~~~~~ + linkman:belkinunv[8], linkman:blazer_ser[8], linkman:blazer_usb[8], @@ -57,4 +60,5 @@ linkman:usbhid-ups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/belkinunv.txt b/docs/man/belkinunv.txt index 81517fbd5c..0f0cdb071b 100644 --- a/docs/man/belkinunv.txt +++ b/docs/man/belkinunv.txt @@ -8,6 +8,7 @@ belkinunv - Driver for Belkin "Universal UPS" and compatible NOTE ---- + This man page only documents the hardware-specific features of the belkin driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -18,6 +19,7 @@ drivers you should use. SUPPORTED HARDWARE ------------------ + The belkinunv driver is known to work with the Belkin Universal UPS models F6C800-UNV and F6C120-UNV, and is expected to work with other Belkin Universal UPS models. The driver only supports serial @@ -32,6 +34,7 @@ are supported using the linkman:genericups[8] driver with SOFT SHUTDOWN WORKAROUND ------------------------ + One problem with the Belkin Universal UPS is that it cannot enter a soft shutdown (shut down the load until AC power returns) unless the batteries are completely depleted. Thus, one cannot just shut off the @@ -73,6 +76,7 @@ startup scripts. OPTIONS ------- + See also linkman:nutupsdrv[8] for generic options. Never use the *-k* option with this driver; it does not work properly. @@ -126,6 +130,7 @@ line. VARIABLES --------- + *battery.charge*:: *battery.runtime*:: @@ -327,15 +332,22 @@ EXTRA ARGUMENTS This driver does not support any extra settings in linkman:ups.conf[5]. +AUTHOR +------ + +Peter Selinger + SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Other Belkin drivers: ~~~~~~~~~~~~~~~~~~~~~ + linkman:belkinunv[8], linkman:blazer_ser[8], linkman:blazer_usb[8], @@ -343,11 +355,7 @@ linkman:usbhid-ups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ - - The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - - The documentation for the protocol used by this UPS: -link:http://www.mscs.dal.ca/~selinger/ups/belkin-universal-ups.html[belkin-universal-ups.html] -AUTHOR ------- - -Peter Selinger +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The documentation for the protocol used by this UPS: + link:http://www.mscs.dal.ca/~selinger/ups/belkin-universal-ups.html[belkin-universal-ups.html] diff --git a/docs/man/bestfcom.txt b/docs/man/bestfcom.txt index 5213132ec5..67001851ca 100644 --- a/docs/man/bestfcom.txt +++ b/docs/man/bestfcom.txt @@ -8,12 +8,14 @@ bestfcom - Driver for Best Power Fortress/Ferrups NOTE ---- + This man page only documents the hardware-specific features of the bestfcom driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + Best Power Fortress/Ferrups implementing the Fortress UPS Protocol (f-command set). @@ -25,21 +27,21 @@ linkman:ups.conf[5]. AUTHORS ------- -Kent Polk (bestfcom) - -Andreas Wrede, John Stone (bestuferrups) -Grant Taylor (bestfort) - -Russell Kroll (bestups) +* Kent Polk (bestfcom) +* Andreas Wrede, John Stone (bestuferrups) +* Grant Taylor (bestfort) +* Russell Kroll (bestups) SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/bestfortress.txt b/docs/man/bestfortress.txt index 8bc969ac94..aac0b8b448 100644 --- a/docs/man/bestfortress.txt +++ b/docs/man/bestfortress.txt @@ -15,10 +15,12 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports old Best Fortress UPS equipment using a serial connection. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]: @@ -28,22 +30,26 @@ Set the speed of the serial connection - 1200, 2400, 4800 or 9600. *max_load*='VA':: Set the full-scale value of the *ups.load* variable. -AUTHOR ------- -Holger Dietze , -Stuart D. Gathman +AUTHORS +------- + +* Holger Dietze +* Stuart D. Gathman SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] The newer Best Power drivers: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + linkman:bestups[8], linkman:bestuferrups[8], linkman:bestfcom[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/bestuferrups.txt b/docs/man/bestuferrups.txt index a84ebe4666..cfe857db89 100644 --- a/docs/man/bestuferrups.txt +++ b/docs/man/bestuferrups.txt @@ -8,12 +8,14 @@ bestuferrups - Driver for Best Power Micro-Ferrups NOTE ---- + This man page only documents the hardware-specific features of the bestuferrups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + Best Power Micro-Ferrups ME3100, probably other similar models too. EXTRA ARGUMENTS @@ -24,19 +26,20 @@ linkman:ups.conf[5]. AUTHORS ------- -Andreas Wrede, John Stone (bestuferrups) - -Grant Taylor (bestfort) -Russell Kroll (bestups) +* Andreas Wrede, John Stone (bestuferrups) +* Grant Taylor (bestfort) +* Russell Kroll (bestups) SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/bestups.txt b/docs/man/bestups.txt index 9d80e69d64..f23c119651 100644 --- a/docs/man/bestups.txt +++ b/docs/man/bestups.txt @@ -15,6 +15,7 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + *bestups* was designed to monitor Best Power UPS hardware like the Fortress, Fortress Telecom, Axxium Rackmount and Patriot Pro. It also recognizes and supports SOLA units such as the 325, 520 and 620. In addition, the @@ -101,17 +102,21 @@ things such as the perpetual 98.7% charge on the author's Fortress 750, even when it's been charging for weeks. You can use `nombattvolt=` in linkman:ups.conf[8] to fix this. -AUTHOR ------- -Russell Kroll, Jason White +AUTHORS +------- + +* Russell Kroll +* Jason White SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/blazer-common.txt b/docs/man/blazer-common.txt index 68d00814a9..5d202893de 100644 --- a/docs/man/blazer-common.txt +++ b/docs/man/blazer-common.txt @@ -1,5 +1,6 @@ NOTE ---- + This man page only documents the hardware-specific features of the blazer driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -304,8 +305,8 @@ The temperature and load value is known to be bogus in some models. AUTHORS ------- -Arjen de Korte , -Alexander Gordeev +* Arjen de Korte +* Alexander Gordeev SEE ALSO @@ -323,7 +324,6 @@ linkman:nutupsdrv[8], linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8] Internet Resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - -The NUT HCL: http://www.networkupstools.org/stable-hcl.html +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The NUT HCL: http://www.networkupstools.org/stable-hcl.html diff --git a/docs/man/clone.txt b/docs/man/clone.txt index b6be30831e..e9331f8244 100644 --- a/docs/man/clone.txt +++ b/docs/man/clone.txt @@ -3,22 +3,26 @@ CLONE(8) NAME ---- + clone - UPS driver clone NOTE ---- + This man page only documents the specific features of the clone driver. For information about the core driver, see linkman:nutupsdrv[8]. DESCRIPTION ----------- + This driver, which sits on top of another driver socket, allows users to group clients to a particular outlet of a device and deal with this output as if it was a normal UPS. EXTRA ARGUMENTS --------------- + This driver supports the following settings: *load.off*='command':: @@ -58,6 +62,7 @@ Set the remaining battery runtime when the clone UPS switches to LB IMPLEMENTATION -------------- + The port specification in the linkman:ups.conf[5] reference the driver socket that the "real" UPS driver is using. For example: @@ -75,6 +80,7 @@ socket that the "real" UPS driver is using. For example: IMPORTANT --------- + Unlike a real UPS, you should *not* configure a upsmon primary mode for this driver. When a upsmon primary sees the OB LB flags and tells the upsd server it is OK to initiate the shutdown sequence, the server will latch the FSD @@ -88,6 +94,7 @@ FSD flag if needed without the help of a upsmon primary. CAVEATS ------- + The clone UPS will follow the status on the real UPS driver. You can only make the clone UPS shutdown earlier than the real UPS driver, not later. If the real UPS driver initiates a shutdown, the clone UPS driver will @@ -100,6 +107,7 @@ warning. AUTHOR ------ + Arjen de Korte SEE ALSO @@ -112,4 +120,5 @@ linkman:nutupsdrv[8] Internet Resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/dummy-ups.txt b/docs/man/dummy-ups.txt index bf1ae18da8..992bb97a76 100644 --- a/docs/man/dummy-ups.txt +++ b/docs/man/dummy-ups.txt @@ -3,16 +3,19 @@ DUMMY-UPS(8) NAME ---- + dummy-ups - Driver for multi-purpose UPS emulation NOTE ---- + This man page only documents the specific features of the *dummy-ups* driver. For information about the core driver, see linkman:nutupsdrv[8]. DESCRIPTION ----------- + This program is a multi-purpose UPS emulation tool. Its behavior depends on the running mode: "dummy" or "repeater". @@ -165,11 +168,13 @@ which allows one to build a virtual device, composed of several other devices BUGS ---- + Instant commands are not yet supported in Dummy Mode, and data need name/value checking enforcement, as well as boundaries or enumeration definition. AUTHOR ------ + Arnaud Quette SEE ALSO @@ -182,4 +187,5 @@ linkman:nutupsdrv[8] Internet Resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/etapro.txt b/docs/man/etapro.txt index 3b827e2a3f..86d921286a 100644 --- a/docs/man/etapro.txt +++ b/docs/man/etapro.txt @@ -3,33 +3,41 @@ ETAPRO(8) NAME ---- + etapro - Driver for ETA UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the etapro driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports ETA UPS equipment with the "PRO" option for smart mode. EXTRA ARGUMENTS --------------- + This driver does not support any extra settings in the linkman:ups.conf[5]. AUTHOR ------ + Marek Michalkiewicz SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/everups.txt b/docs/man/everups.txt index ca02d29a92..7700982051 100644 --- a/docs/man/everups.txt +++ b/docs/man/everups.txt @@ -3,16 +3,19 @@ EVERUPS(8) NAME ---- + everups - Driver for Ever UPS models NOTE ---- + This man page only documents the hardware-specific features of the everups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver should recognize the NET *-DPC and AP *-PRO models. EXTRA ARGUMENTS @@ -30,6 +33,7 @@ don't sleep and force a reboot. AUTHOR ------ + Bartek Szady SEE ALSO @@ -37,8 +41,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/gamatronic.txt b/docs/man/gamatronic.txt index ac9db3fde7..95122954ba 100644 --- a/docs/man/gamatronic.txt +++ b/docs/man/gamatronic.txt @@ -3,16 +3,19 @@ GAMATRONIC(8) NAME ---- + gamatronic - Driver for Gamatronic UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the gamatronic driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + Various - Rebuilt to work with Gamatronic UPS Units, but should recognize any UPS that speaks the SEC protocol at 1200-19200 bps. @@ -24,6 +27,7 @@ linkman:ups.conf[5]. AUTHOR ------ + Nadav Moskovitch SEE ALSO @@ -31,8 +35,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/generic_modbus.txt b/docs/man/generic_modbus.txt index 3302b974c0..37f9de60c7 100644 --- a/docs/man/generic_modbus.txt +++ b/docs/man/generic_modbus.txt @@ -229,16 +229,19 @@ HB etc) by writing over those registers. AUTHOR ------ + Dimitris Economou SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8], linkman:ups.conf[5] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ -libmodbus home page: http://libmodbus.org +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* libmodbus home page: http://libmodbus.org diff --git a/docs/man/genericups.txt b/docs/man/genericups.txt index b948d0e3dc..54c4a6b01d 100644 --- a/docs/man/genericups.txt +++ b/docs/man/genericups.txt @@ -3,15 +3,18 @@ GENERICUPS(8) NAME ---- + genericups - Driver for contact-closure UPS equipment NOTE ---- + This man page only documents the specific features of the genericups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports hardware from many different manufacturers as it only uses the very simplest of signaling schemes. Contact closure refers to a kind of interface where basic high/low signals are provided to indicate @@ -23,11 +26,13 @@ a smarter UPS. CABLING ------- + Cabling is different for every kind of UPS. See the table below for information on what is known to work with a given UPS type. EXTRA ARGUMENTS --------------- + This driver supports the following settings in the linkman:ups.conf[5]: upstype='type':: @@ -89,6 +94,7 @@ recognizes a low battery condition when DCD is not held high. TYPE INFORMATION ---------------- + The essence of a UPS definition in this driver is how it uses the serial lines that are available. These are the abbreviations you will see below: @@ -392,8 +398,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] -Internet resources -~~~~~~~~~~~~~~~~~~ +Internet resources: +~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/hosts.conf.txt b/docs/man/hosts.conf.txt index 273ae98368..8d86ea8789 100644 --- a/docs/man/hosts.conf.txt +++ b/docs/man/hosts.conf.txt @@ -36,4 +36,5 @@ linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/huawei-ups2000.txt b/docs/man/huawei-ups2000.txt index 9f5d7d473e..af3835055c 100644 --- a/docs/man/huawei-ups2000.txt +++ b/docs/man/huawei-ups2000.txt @@ -342,20 +342,23 @@ Guide. AUTHOR ------ + Yifeng Li SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - -Huawei UPS2000-A (1 kVA-3 kVA) User Manual: https://support.huawei.com/enterprise/en/doc/EDOC1000084260 - -Huawei UPS2000 (1 kVA-3 kVA) Modbus Protocol Development Guide: https://support.huawei.com/enterprise/en/doc/EDOC1000110696 -libmodbus home page: http://libmodbus.org +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* Huawei UPS2000-A (1 kVA-3 kVA) User Manual: + https://support.huawei.com/enterprise/en/doc/EDOC1000084260 +* Huawei UPS2000 (1 kVA-3 kVA) Modbus Protocol Development Guide: + https://support.huawei.com/enterprise/en/doc/EDOC1000110696 +* libmodbus home page: http://libmodbus.org diff --git a/docs/man/isbmex.txt b/docs/man/isbmex.txt index 9b97d1629a..985f0ff429 100644 --- a/docs/man/isbmex.txt +++ b/docs/man/isbmex.txt @@ -15,6 +15,7 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports SOLA/BASIC Mexico ISBMEX protocol UPS equipment. EXTRA ARGUMENTS @@ -25,6 +26,7 @@ linkman:ups.conf[5]. AUTHOR ------ + Edscott Wilson Garcia SEE ALSO @@ -32,8 +34,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/ivtscd.txt b/docs/man/ivtscd.txt index 6a2c7c4beb..0696c87847 100644 --- a/docs/man/ivtscd.txt +++ b/docs/man/ivtscd.txt @@ -3,32 +3,40 @@ IVTSCD(8) NAME ---- + ivtscd - driver for the IVT Solar Controller Device NOTE ---- + This man page only documents the hardware-specific features of the *ivtscd* driver. For information about the core driver, see linkman:nutupsdrv[8]. DESCRIPTION ----------- + This driver allows to access the IVT SCD-series devices. EXTRA ARGUMENTS --------------- + This driver does not support any extra argument. AUTHOR ------ + Arjen de Korte SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/libnutclient.txt b/docs/man/libnutclient.txt index d23b705210..668eb8349a 100644 --- a/docs/man/libnutclient.txt +++ b/docs/man/libnutclient.txt @@ -39,10 +39,12 @@ See the `nutclient.h` header for more information. ERROR HANDLING -------------- + There is currently no specific mechanism around error handling. SEE ALSO -------- + linkman:libnutclient_devices[3] linkman:libnutclient_commands[3] linkman:libnutclient_general[3] diff --git a/docs/man/libnutclient_commands.txt b/docs/man/libnutclient_commands.txt index e3826dc33a..cf2e9fce85 100644 --- a/docs/man/libnutclient_commands.txt +++ b/docs/man/libnutclient_commands.txt @@ -42,6 +42,7 @@ The *nutclient_execute_device_command* intend to execute the instant command. SEE ALSO -------- + linkman:libnutclient[3] linkman:libnutclient_devices[3] linkman:libnutclient_general[3] diff --git a/docs/man/libnutclient_devices.txt b/docs/man/libnutclient_devices.txt index dd69101a12..bca57b9046 100644 --- a/docs/man/libnutclient_devices.txt +++ b/docs/man/libnutclient_devices.txt @@ -35,6 +35,7 @@ The returned description string must be freed. SEE ALSO -------- + linkman:libnutclient[3] linkman:libnutclient_commands[3] linkman:libnutclient_devices[3] diff --git a/docs/man/libnutclient_general.txt b/docs/man/libnutclient_general.txt index 909549af72..4b01aff60f 100644 --- a/docs/man/libnutclient_general.txt +++ b/docs/man/libnutclient_general.txt @@ -42,4 +42,5 @@ It also frees all pointed strings. SEE ALSO -------- + linkman:libnutclient[3] diff --git a/docs/man/libnutclient_misc.txt b/docs/man/libnutclient_misc.txt index 54624065a6..52caf614a4 100644 --- a/docs/man/libnutclient_misc.txt +++ b/docs/man/libnutclient_misc.txt @@ -51,4 +51,5 @@ flag on the device. SEE ALSO -------- + linkman:libnutclient[3] diff --git a/docs/man/libnutclient_tcp.txt b/docs/man/libnutclient_tcp.txt index 86e6ba5798..8dc4d39e32 100644 --- a/docs/man/libnutclient_tcp.txt +++ b/docs/man/libnutclient_tcp.txt @@ -50,5 +50,6 @@ The *nutclient_tcp_get_timeout()* function retrieve the timeout duration for I/O SEE ALSO -------- + linkman:libnutclient[3] linkman:libnutclient_general[3] diff --git a/docs/man/libnutclient_variables.txt b/docs/man/libnutclient_variables.txt index 0ca5e4968e..b2de4269a4 100644 --- a/docs/man/libnutclient_variables.txt +++ b/docs/man/libnutclient_variables.txt @@ -59,6 +59,7 @@ The *nutclient_set_device_variable_values* intend to set multiple values of the SEE ALSO -------- + linkman:libnutclient[3] linkman:libnutclient_devices[3] linkman:libnutclient_general[3] diff --git a/docs/man/libupsclient-config.txt b/docs/man/libupsclient-config.txt index 938f0faf40..0ff39a520d 100644 --- a/docs/man/libupsclient-config.txt +++ b/docs/man/libupsclient-config.txt @@ -8,6 +8,7 @@ libupsclient-config - script to get information about the installed version of l SYNOPSIS -------- + *libupsclient-config* [--version] [--libs] [--cflags] DESCRIPTION @@ -33,6 +34,7 @@ Print the compiler flags that are necessary to compile a *libupsclient* program. AUTHORS ------- + This manual page was written by Arnaud Quette . SEE ALSO @@ -42,5 +44,6 @@ linkman:upsclient[3] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/liebert-esp2.txt b/docs/man/liebert-esp2.txt index 7b71425131..8d96c82c31 100644 --- a/docs/man/liebert-esp2.txt +++ b/docs/man/liebert-esp2.txt @@ -8,12 +8,14 @@ liebert-esp2 - Driver for Liebert UPS, using the ESP-II serial protocol NOTE ---- + This man page only documents the hardware-specific features of the liebert-esp2 driver. For information about the core driver, see linkman:nutupsdrv[8]. SPECIAL CABLING NOTE -------------------- + Be aware that an RS-232 cable with ONLY the RX, TX and ground pin must be used when interfacing with GXT2 series UPS units (and possibly others), since the handshaking lines are used for purposes other than RS-232 flow control. @@ -23,8 +25,9 @@ the proper cable/wiring with the diagram provided in the manual with your UPS. SUPPORTED HARDWARE ------------------ + Tested to work on the following units: -Liebert GXT2-6000RT208 +* Liebert GXT2-6000RT208 This is an experimental driver. You have been warned. @@ -41,15 +44,20 @@ Set the speed of the serial connection - 1200, 2400 (default), 4800, 9600 or 192 AUTHOR ------ -Richard Gregory , Arjen de Korte , Nash Kaminski + +* Richard Gregory +* Arjen de Korte +* Nash Kaminski SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/liebert.txt b/docs/man/liebert.txt index dfaa795838..b91365989b 100644 --- a/docs/man/liebert.txt +++ b/docs/man/liebert.txt @@ -8,12 +8,14 @@ liebert - Driver for Liebert contact-closure UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the liebert driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports some Liebert UPS equipment with a contact-closure interface. This includes the UPStation GXT2 with their contact-closure cable. The smart mode ("Multilink") cable is not supported by this @@ -42,8 +44,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/macosx-ups.txt b/docs/man/macosx-ups.txt index ee30671fd1..3b3130eefd 100644 --- a/docs/man/macosx-ups.txt +++ b/docs/man/macosx-ups.txt @@ -3,16 +3,19 @@ MACOSX-UPS(8) NAME ---- + macosx-ups - monitor for Mac OS X built-in UPS and battery driver NOTE ---- + This man page only documents the hardware-specific features of the *macosx-ups* driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + *macosx-ups* supports any USB HID Power Device Class (PDC) UPS which is matched by the Mac OS X built-in drivers. It also can monitor a laptop internal battery as though it were an UPS. @@ -23,6 +26,7 @@ Preferences, this driver should be able to monitor it. EXTRA ARGUMENTS ---------------- + *port*=auto:: Due to changes in the way that Mac OS X lists power sources, the *port* parameter no longer has any effect. The rest of NUT still requires a value here, @@ -62,6 +66,7 @@ hardware only) or linkman:usbhid-ups[8]. AUTHORS ------- + Charles Lepple SEE ALSO @@ -71,10 +76,11 @@ linkman:usbhid-ups[8], *pmset*(8), *regex*(3) The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ -The apcupsd home page: http://www.apcupsd.org/ +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The apcupsd home page: http://www.apcupsd.org/ diff --git a/docs/man/masterguard.txt b/docs/man/masterguard.txt index 3a4982316e..48bd2908ff 100644 --- a/docs/man/masterguard.txt +++ b/docs/man/masterguard.txt @@ -3,10 +3,12 @@ MASTERGUARD(8) NAME ---- + masterguard - Driver for Masterguard UPS equipment NOTES ----- + This man page only documents the hardware-specific features of the masterguard driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -16,6 +18,7 @@ framework that also supports USB, see linkman:nutdrv_qx[8]. SUPPORTED HARDWARE ------------------ + This driver supports Masterguard UPS equipment (serial connection only). EXTRA ARGUMENTS @@ -26,6 +29,7 @@ Cancel the shutdown procedure. AUTHOR ------ + Michael Spanier SEE ALSO @@ -33,12 +37,15 @@ SEE ALSO Newer driver: ~~~~~~~~~~~~~ + linkman:nutdrv_qx[8] The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/metasys.txt b/docs/man/metasys.txt index 77f9b36e1c..d134fe5fc8 100644 --- a/docs/man/metasys.txt +++ b/docs/man/metasys.txt @@ -32,15 +32,18 @@ The driver should support all the common features of the ups models: CABLING ------- + The needed cable is a standard pin-to-pin serial cable with at least pins 2, 3, and 5 (on DB9 connector) connected. EXTRA ARGUMENTS --------------- + This driver supports no extra arguments from linkman:ups.conf[5]. BUGS ---- + This driver has been tested on Meta System HF Millennium 820 and ally HF 1000 only. @@ -49,6 +52,7 @@ UPS are really welcome. AUTHOR ------ + Fabio Di Niro SEE ALSO @@ -56,8 +60,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/mge-shut.txt b/docs/man/mge-shut.txt index 097d4cce98..91d96b7fa8 100644 --- a/docs/man/mge-shut.txt +++ b/docs/man/mge-shut.txt @@ -90,8 +90,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/mge-utalk.txt b/docs/man/mge-utalk.txt index 049e4c7582..3e862f50d2 100644 --- a/docs/man/mge-utalk.txt +++ b/docs/man/mge-utalk.txt @@ -75,24 +75,26 @@ This is due to the fact that these models don't support too much polling. To solve this problem, add "pollinterval=20" in ups.conf, and change the value of MAXAGE to 25 in upsd.conf, and DEADTIME to 25 in upsmon.conf. -AUTHOR ------- +AUTHORS +------- -Hans Ekkehard Plesser, -Arnaud Quette, -Martin Loyer, -Patrick Agrain, -Nicholas Reilly, -Dave Abbott, -Marek Kralewski +* Hans Ekkehard Plesser +* Arnaud Quette +* Martin Loyer +* Patrick Agrain +* Nicholas Reilly +* Dave Abbott +* Marek Kralewski SEE ALSO -------- The core driver ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/microdowell.txt b/docs/man/microdowell.txt index 9277379486..a23ead2aff 100644 --- a/docs/man/microdowell.txt +++ b/docs/man/microdowell.txt @@ -15,8 +15,9 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -This driver was developed for the Enterprise Nxx and Bxx models. Other -Microdowell models may work, too. + +This driver was developed for the Enterprise Nxx and Bxx models. +Other Microdowell models may work, too. EXTRA ARGUMENTS --------------- @@ -26,6 +27,7 @@ linkman:ups.conf[5]. AUTHOR ------ + Elio Corbolante SEE ALSO @@ -33,8 +35,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/microsol-apc.txt b/docs/man/microsol-apc.txt index 9bc3756199..82c3d8277c 100644 --- a/docs/man/microsol-apc.txt +++ b/docs/man/microsol-apc.txt @@ -15,6 +15,7 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports the following UPS models: * APC Back-UPS BZ1500-BR @@ -53,17 +54,20 @@ models, but is untested. AUTHOR ------ -Ygor A. S. Regados , -Roberto P. Velloso , -Silvino B. Magalhães + +* Ygor A. S. Regados +* Roberto P. Velloso +* Silvino B. Magalhães SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/netxml-ups.txt b/docs/man/netxml-ups.txt index 2517cd7a83..acc672ee08 100644 --- a/docs/man/netxml-ups.txt +++ b/docs/man/netxml-ups.txt @@ -3,18 +3,21 @@ netxml-ups(8) NAME ---- + netxml-ups - Driver for Eaton / MGE Network Management Card / Proxy (XML/HTTP Protocol) equipment NOTE ---- + This man page only documents the hardware-specific features of the netxml-ups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -netxml-ups support all recent Eaton / MGE models which use a Network + +netxml-ups supports all recent Eaton / MGE models which use a Network Management Card or Proxy (MGE XML/HTTP protocol based). This applies to both Eaton (previously MGE Office Protection Systems) and to MGE UPS SYSTEMS. Supported card and proxy models are: @@ -32,6 +35,7 @@ parameter. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]: @@ -77,6 +81,7 @@ particular driver instance restores the old behavior for those measurements. IMPLEMENTATION -------------- + The hostname of the UPS is specified with the "port" value in *ups.conf*, i.e.: @@ -94,6 +99,7 @@ linkman:ups.conf[5]) to at least 5 seconds. KNOWN ISSUES ------------ + Don't connect to the UPS through a proxy. Although it would be trivial to add support for proxies, this is not recommended and don't ask for it. Not only because it will prevent the driver to make a persistent connection to the UPS, @@ -102,6 +108,7 @@ whatever reason), the driver will no longer be able to reach the UPS. AUTHORS ------- + Arjen de Korte SEE ALSO @@ -109,8 +116,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nut-driver-enumerator.txt b/docs/man/nut-driver-enumerator.txt index 39fef960fa..acf8dd255c 100644 --- a/docs/man/nut-driver-enumerator.txt +++ b/docs/man/nut-driver-enumerator.txt @@ -8,6 +8,7 @@ nut-driver-enumerator - tool to map NUT device entries to service instances SYNOPSIS -------- + *nut-driver-enumerator.sh* -h *nut-driver-enumerator.sh* (no args) @@ -135,8 +136,10 @@ Absent or unreadable `ups.conf` file SEE ALSO -------- + linkman:upsdrvsvcctl[8], linkman:ups.conf[5] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nut-ipmipsu.txt b/docs/man/nut-ipmipsu.txt index b5f3ba40e6..5191ea28d9 100644 --- a/docs/man/nut-ipmipsu.txt +++ b/docs/man/nut-ipmipsu.txt @@ -100,16 +100,19 @@ Here is an example output for a Dell r610 server: AUTHOR ------ + Arnaud Quette SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ -GNU FreeIPMI home page: http://www.gnu.org/software/freeipmi/ +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* GNU FreeIPMI home page: http://www.gnu.org/software/freeipmi/ diff --git a/docs/man/nut-recorder.txt b/docs/man/nut-recorder.txt index e20223942c..edde4ae0bb 100644 --- a/docs/man/nut-recorder.txt +++ b/docs/man/nut-recorder.txt @@ -1,17 +1,19 @@ NUT-RECORDER(8) =============== - NAME ---- + nut-recorder - utility to record device status and values changes SYNOPSIS -------- + *nut-recorder* 'device-name' [output-file] [interval] DESCRIPTION ----------- + *nut-recorder* is an utility to record sequences from running devices (such as power failures, or any other value changes) from upsd, and dump it in a .seq format. @@ -21,6 +23,7 @@ to replay the sequence. OPTIONS ------- + 'device-name':: Record the changes of this device. The format for this option is @@ -60,14 +63,18 @@ You can then define a dummy device in linkman:ups.conf[5]: AUTHOR ------ + Arnaud Quette SEE ALSO -------- +The dummy-ups driver: +~~~~~~~~~~~~~~~~~~~~~ + linkman:dummy-ups[8] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nut-scanner.txt b/docs/man/nut-scanner.txt index 175c6f48df..95603603dd 100644 --- a/docs/man/nut-scanner.txt +++ b/docs/man/nut-scanner.txt @@ -1,13 +1,14 @@ NUT-SCANNER(8) ============== - NAME ---- + nut-scanner - scan communication buses for NUT devices SYNOPSIS -------- + *nut-scanner* -h *nut-scanner* ['OPTIONS'] @@ -29,6 +30,7 @@ both during compilation and runtime, then SNMP discovery will be available. OPTIONS ------- + *-h*:: Display the help text. @@ -213,7 +215,7 @@ SEE ALSO linkman:ups.conf[5] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nut.conf.txt b/docs/man/nut.conf.txt index 8a0bf7ad68..f8157ee135 100644 --- a/docs/man/nut.conf.txt +++ b/docs/man/nut.conf.txt @@ -3,6 +3,7 @@ NUT.CONF(5) NAME ---- + nut.conf - UPS definitions for Network UPS Tools DESCRIPTION @@ -104,6 +105,7 @@ SEE ALSO linkman:ups.conf[5], linkman:upsd.conf[5], linkman:upsd.users[5], linkman:upsmon.conf[5] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nutdrv_atcl_usb.txt b/docs/man/nutdrv_atcl_usb.txt index cd99e2b77f..bbd8d7d376 100644 --- a/docs/man/nutdrv_atcl_usb.txt +++ b/docs/man/nutdrv_atcl_usb.txt @@ -3,15 +3,18 @@ NUTDRV_ATCL_USB(8) NAME ---- + nutdrv_atcl_usb - Driver for 'ATCL FOR UPS' equipment NOTE ---- + This man page only documents the specific features of the nutdrv_atcl_usb driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver is for UPS hardware which identifies itself as USB idVendor 0001 and idProduct 0000, and iManufacturer +ATCL FOR UPS+. Known manufacturers include Kanji and Plexus. The UPS interface seems to be a generic USB-to-serial @@ -35,6 +38,7 @@ devices, such as linkman:nutdrv_qx[8]. BUGS ---- + The UPS returns the same code for "load power is off" as for "on line power". This condition will not be observed if the NUT `upsmon` in primary mode runs on the box powered by the UPS, but may be an issue if the UPS is monitored @@ -53,6 +57,7 @@ kind that allows detection and proper load cycling on command. AUTHORS ------- + Charles Lepple SEE ALSO @@ -60,16 +65,20 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] The generic serial driver: ~~~~~~~~~~~~~~~~~~~~~~~~~~ + linkman:genericups[8] The Qx driver: ~~~~~~~~~~~~~~ + linkman:nutdrv_qx[8] (`fuji` subdriver) Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nutdrv_siemens_sitop.txt b/docs/man/nutdrv_siemens_sitop.txt index 291096130b..4ffd0ccfb9 100644 --- a/docs/man/nutdrv_siemens_sitop.txt +++ b/docs/man/nutdrv_siemens_sitop.txt @@ -3,16 +3,19 @@ NUTDRV_SIEMENS_SITOP(8) NAME ---- + nutdrv_siemens_sitop - driver for the Siemens SITOP UPS500 series UPS NOTE ---- + This man page only documents the hardware-specific features of the *nutdrv_siemens_sitop* driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + *nutdrv_siemens_sitop* supports Siemens UPS models from the SITOP UPS500 series. Some models have a serial port, others have a USB port. The models with USB port actually contain a serial-over-USB chip, @@ -26,6 +29,7 @@ with USB port (Siemens product number 6EP1933-2EC41). DEVICE SETTINGS --------------- + The UPS is configured via DIP-switches. For correct functioning in combination with NUT, set the DIP-switches to the following: @@ -54,6 +58,7 @@ supply power from its batteries. USB driver ---------- + The USB-versions of the UPS contain an FTDI USB-to-serial converter chip. It is programmed with a non-standard product ID (for example _0403:e0e3_), but can still be used with the normal ftdi_sio driver. @@ -85,6 +90,7 @@ SUBSYSTEM=="tty" ATTRS{idVendor}=="0403", ATTRS{idProduct}=="e0e3" SYMLINK+="tty POLLING ------- + The UPS does not have a special 'get status' command. Instead, it continuously sends out status update messages (tens of messages per second). Every *pollinterval*, these messages are read from the serial port buffer. @@ -94,6 +100,7 @@ value is 1 (second). EXTRA ARGUMENTS --------------- + This driver supports the following optional settings: *max_polls_without_data*='num':: @@ -126,6 +133,7 @@ has been removed for at least 1 second, and has been re-applied. INSTALLATION ------------ + Make sure that your operating system has created a serial device for the UPS. See the section *USB driver* for more information. @@ -136,6 +144,7 @@ or by adding the NUT user to a user group that can access serial devices DIAGNOSTICS ----------- + You can verify the correct functioning of the hardware, by monitoring the serial port with a terminal program, for example picocom: @@ -156,6 +165,7 @@ To exit picocom, use Ctrl-A Ctrl-X. KNOWN ISSUES AND BUGS --------------------- + *Untested models*:: As mentioned under *Supported hardware*, this driver has not been tested with all models in the SITOP UPS500 series. @@ -174,6 +184,7 @@ It is not sure if the serial models are affected by this issue as well. AUTHORS ------- + Matthijs H. ten Berge SEE ALSO @@ -181,8 +192,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/nutscan.txt b/docs/man/nutscan.txt index 90ac366813..8d1d4c8024 100644 --- a/docs/man/nutscan.txt +++ b/docs/man/nutscan.txt @@ -44,11 +44,13 @@ Helper functions are also provided to output data using standard formats: ERROR HANDLING -------------- + There is currently no specific mechanism for error handling. SEE ALSO -------- + linkman:nut-scanner[8], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], @@ -56,5 +58,9 @@ linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_parsable[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_add_option_to_device[3], -linkman:nutscan_cidr_to_ip[3], +linkman:nutscan_cidr_to_ip[3] + +Internet resources: +~~~~~~~~~~~~~~~~~~~ + http://avahi.org/ diff --git a/docs/man/nutscan_add_device_to_device.txt b/docs/man/nutscan_add_device_to_device.txt index 5ce06c2f06..9ca02b968b 100644 --- a/docs/man/nutscan_add_device_to_device.txt +++ b/docs/man/nutscan_add_device_to_device.txt @@ -20,11 +20,11 @@ DESCRIPTION The `nutscan_device_t` contains the following variables: nutscan_device_type_t type; - char * driver; - char * port; - nutscan_options_t opt; - struct nutscan_device * prev; - struct nutscan_device * next; + char * driver; + char * port; + nutscan_options_t opt; + struct nutscan_device * prev; + struct nutscan_device * next; This is a double linked list of device. Each device is described by its `type`, its `driver` name, its `port` and any number of optional data. @@ -37,6 +37,7 @@ The *nutscan_add_device_to_device()* functions returns a pointer to a device con SEE ALSO -------- + linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], diff --git a/docs/man/nutscan_add_option_to_device.txt b/docs/man/nutscan_add_option_to_device.txt index 6cb5cc1fec..c5cdda59ea 100644 --- a/docs/man/nutscan_add_option_to_device.txt +++ b/docs/man/nutscan_add_option_to_device.txt @@ -20,11 +20,11 @@ DESCRIPTION The `nutscan_device_t` contains the following variables: nutscan_device_type_t type; - char * driver; - char * port; - nutscan_options_t opt; - struct nutscan_device * prev; - struct nutscan_device * next; + char * driver; + char * port; + nutscan_options_t opt; + struct nutscan_device * prev; + struct nutscan_device * next; This is a double linked list of device. Each device is described by its `type`, its `driver` name, its `port` and any number of optional data. @@ -32,6 +32,7 @@ The *nutscan_add_option_to_device()* adds an optional data in the given device. SEE ALSO -------- + linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], diff --git a/docs/man/nutscan_cidr_to_ip.txt b/docs/man/nutscan_cidr_to_ip.txt index 99d83327d1..b5f26e14ec 100644 --- a/docs/man/nutscan_cidr_to_ip.txt +++ b/docs/man/nutscan_cidr_to_ip.txt @@ -25,6 +25,7 @@ The *nutscan_cidr_to_ip()* function returns 0 if an error occurred (invalid 'cid SEE ALSO -------- + linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], diff --git a/docs/man/nutscan_display_parsable.txt b/docs/man/nutscan_display_parsable.txt index 6e2cdb8fe2..904cb4a631 100644 --- a/docs/man/nutscan_display_parsable.txt +++ b/docs/man/nutscan_display_parsable.txt @@ -26,6 +26,7 @@ The *nutscan_display_parsable()* function displays all NUT devices in 'device' t SEE ALSO -------- + linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], diff --git a/docs/man/nutscan_display_ups_conf.txt b/docs/man/nutscan_display_ups_conf.txt index 4d714c9309..7a3e34a750 100644 --- a/docs/man/nutscan_display_ups_conf.txt +++ b/docs/man/nutscan_display_ups_conf.txt @@ -20,6 +20,7 @@ The *nutscan_display_ups_conf()* function displays all NUT devices in 'device' t SEE ALSO -------- + linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], diff --git a/docs/man/nutscan_free_device.txt b/docs/man/nutscan_free_device.txt index 1479f385a0..a9177feb6c 100644 --- a/docs/man/nutscan_free_device.txt +++ b/docs/man/nutscan_free_device.txt @@ -20,6 +20,7 @@ The *nutscan_free_device()* function free a `nutscan_device_type_t` structure. D SEE ALSO -------- + linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], diff --git a/docs/man/nutscan_get_serial_ports_list.txt b/docs/man/nutscan_get_serial_ports_list.txt index d13923d34f..c441122963 100644 --- a/docs/man/nutscan_get_serial_ports_list.txt +++ b/docs/man/nutscan_get_serial_ports_list.txt @@ -33,6 +33,7 @@ The *nutscan_get_serial_ports_list()* function returns NULL if an error occurred SEE ALSO -------- + linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], diff --git a/docs/man/nutscan_init.txt b/docs/man/nutscan_init.txt index f23fe0b9ce..248aa0005e 100644 --- a/docs/man/nutscan_init.txt +++ b/docs/man/nutscan_init.txt @@ -31,6 +31,7 @@ Note that if a method is reported as unavailable by those variables, the call to SEE ALSO -------- + linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], diff --git a/docs/man/nutscan_new_device.txt b/docs/man/nutscan_new_device.txt index 81ab4dbf18..6a58a97a45 100644 --- a/docs/man/nutscan_new_device.txt +++ b/docs/man/nutscan_new_device.txt @@ -25,6 +25,7 @@ The *nutscan_new_device()* function returns the newly allocated `nutscan_device_ SEE ALSO -------- + linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3] diff --git a/docs/man/nutscan_scan_avahi.txt b/docs/man/nutscan_scan_avahi.txt index 2c830981a7..96bfa04aa7 100644 --- a/docs/man/nutscan_scan_avahi.txt +++ b/docs/man/nutscan_scan_avahi.txt @@ -33,6 +33,7 @@ if no device is found. SEE ALSO -------- + linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_snmp[3], @@ -40,5 +41,9 @@ linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], linkman:nutscan_add_device_to_device[3], linkman:nutscan_cidr_to_ip[3], -linkman:nutscan_scan_eaton_serial[3], +linkman:nutscan_scan_eaton_serial[3] + +Internet resources: +~~~~~~~~~~~~~~~~~~~ + http://avahi.org/ diff --git a/docs/man/nutscan_scan_eaton_serial.txt b/docs/man/nutscan_scan_eaton_serial.txt index cc7c8e2e3f..479f979dd0 100644 --- a/docs/man/nutscan_scan_eaton_serial.txt +++ b/docs/man/nutscan_scan_eaton_serial.txt @@ -30,6 +30,7 @@ The *nutscan_scan_eaton_serial()* function returns a pointer to a `nutscan_devic SEE ALSO -------- + linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_avahi[3], diff --git a/docs/man/nutscan_scan_ipmi.txt b/docs/man/nutscan_scan_ipmi.txt index 5999152fbb..760413eb6c 100644 --- a/docs/man/nutscan_scan_ipmi.txt +++ b/docs/man/nutscan_scan_ipmi.txt @@ -27,6 +27,7 @@ The *nutscan_scan_ipmi()* function is not implemented yet. SEE ALSO -------- + linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], diff --git a/docs/man/nutscan_scan_nut.txt b/docs/man/nutscan_scan_nut.txt index d0b93b700d..20fd29aaea 100644 --- a/docs/man/nutscan_scan_nut.txt +++ b/docs/man/nutscan_scan_nut.txt @@ -31,6 +31,7 @@ The *nutscan_scan_nut()* function returns a pointer to a `nutscan_device_t` stru SEE ALSO -------- + linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_avahi[3], diff --git a/docs/man/nutscan_scan_snmp.txt b/docs/man/nutscan_scan_snmp.txt index 8f91f85911..b4c843ed2c 100644 --- a/docs/man/nutscan_scan_snmp.txt +++ b/docs/man/nutscan_scan_snmp.txt @@ -57,6 +57,7 @@ The *nutscan_scan_snmp()* function returns a pointer to a `nutscan_device_t` str SEE ALSO -------- + linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], diff --git a/docs/man/nutscan_scan_usb.txt b/docs/man/nutscan_scan_usb.txt index dfeb0d4468..3c9001ea87 100644 --- a/docs/man/nutscan_scan_usb.txt +++ b/docs/man/nutscan_scan_usb.txt @@ -27,6 +27,7 @@ The *nutscan_scan_usb()* function returns a pointer to a `nutscan_device_t` stru SEE ALSO -------- + linkman:nutscan_init[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], diff --git a/docs/man/nutscan_scan_xml_http.txt b/docs/man/nutscan_scan_xml_http.txt index 59e421e732..61371dbf3c 100644 --- a/docs/man/nutscan_scan_xml_http.txt +++ b/docs/man/nutscan_scan_xml_http.txt @@ -27,6 +27,7 @@ The *nutscan_scan_xml_http()* function returns a pointer to a `nutscan_device_t` SEE ALSO -------- + linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], diff --git a/docs/man/nutupsdrv.txt b/docs/man/nutupsdrv.txt index 0c092c5078..f5f2b7c9da 100644 --- a/docs/man/nutupsdrv.txt +++ b/docs/man/nutupsdrv.txt @@ -50,6 +50,7 @@ options and parameters that generally are not needed by normal users. OPTIONS ------- + *-h*:: Display a help message without doing anything else. This will also list possible values for '-x' in that driver, and other help text that the @@ -171,6 +172,7 @@ production use. FILES ----- + ups.conf:: Required configuration file. This contains all details on which drivers to start and where the hardware is attached. @@ -202,19 +204,29 @@ SEE ALSO -------- Server: +~~~~~~~ + linkman:upsd[8] Clients: +~~~~~~~~ + linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8], linkman:upslog[8], linkman:upsmon[8] CGI programs: +~~~~~~~~~~~~~ + linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] Driver control: -linkman:upsdrvctl[8] +~~~~~~~~~~~~~~~ + +linkman:upsdrvctl[8], linkman:upsdrvsvcctl[8] Drivers: +~~~~~~~~ + linkman:al175[8] linkman:apcsmart[8], linkman:bcmxcp[8], @@ -259,4 +271,6 @@ linkman:upscode2[8], linkman:victronups[8] Internet resources: +~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/oneac.txt b/docs/man/oneac.txt index dfa5b0852d..c2f86301a6 100644 --- a/docs/man/oneac.txt +++ b/docs/man/oneac.txt @@ -28,6 +28,7 @@ linkman:genericups[8] driver. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5] file: @@ -39,6 +40,7 @@ Change shutdown delay time from 0 second default. INSTANT COMMANDS ---------------- + This driver supports the following Instant Commands. (See linkman:upscmd[8]) @@ -97,6 +99,7 @@ Mutes the UPS beeper/buzzer for the current alarm condition(s). Writable Variables ------------------ + See linkman:upsrw[8] to see what variables are writable for the UPS. NOTE: If your UPS supports writing battery.runtime.low, the new set value @@ -110,6 +113,7 @@ defined range (for example: tap change or switch to inverter). AUTHOR ------ + Bill Elliot , Eric Lawson @@ -118,8 +122,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/optiups.txt b/docs/man/optiups.txt index 055d26ad1d..61a9678970 100644 --- a/docs/man/optiups.txt +++ b/docs/man/optiups.txt @@ -76,8 +76,9 @@ BUGS On the 420E, `ups.serial` and `ups.temperature` are unsupported features. This is not a bug in NUT or the NUT driver, just the way things are with this UPS. -AUTHOR ------- +AUTHORS +------- + Russell Kroll, Scott Heavner, Matthias Goebl SEE ALSO @@ -85,8 +86,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/phoenixcontact_modbus.txt b/docs/man/phoenixcontact_modbus.txt index 9711937d46..07b2843a84 100644 --- a/docs/man/phoenixcontact_modbus.txt +++ b/docs/man/phoenixcontact_modbus.txt @@ -75,16 +75,19 @@ This driver doesn't support any instant commands. AUTHOR ------ + Spiros Ioannou SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ -libmodbus home page: http://libmodbus.org +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* libmodbus home page: http://libmodbus.org diff --git a/docs/man/pijuice.txt b/docs/man/pijuice.txt index 1643208f34..761f62b396 100644 --- a/docs/man/pijuice.txt +++ b/docs/man/pijuice.txt @@ -3,10 +3,12 @@ PIJUICE(8) NAME ---- + pijuice - driver for UPS in PiJuice HAT NOTE ---- + This man page only documents the hardware-specific features of the *pijuice* driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -17,6 +19,7 @@ may not fully apply to PiJuice HAT, patches from experts are welcome. SUPPORTED HARDWARE ------------------ + The *pijuice* driver supports the portable PiJuice HAT UPS for Raspberry Pi embedded PCs. @@ -30,6 +33,7 @@ On the PiJuice HAT, this should be `/dev/i2c-1`. INSTALLATION ------------ + NOTE: This section was copied from `asem` driver manpage and may not fully apply to PiJuice HAT, patches are welcome. @@ -52,6 +56,7 @@ DIAGNOSTICS KNOWN ISSUES AND BUGS --------------------- + NOTE: This section was copied from `asem` driver manpage and may not fully apply to PiJuice HAT, patches are welcome. @@ -60,6 +65,7 @@ made to turn off the UPS. AUTHORS ------- + Andrew Anderson SEE ALSO @@ -67,10 +73,12 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + Initial pull requests adding this driver: * https://github.com/networkupstools/nut/pull/730 diff --git a/docs/man/powercom.txt b/docs/man/powercom.txt index ce4dbd73d0..db7e936f7e 100644 --- a/docs/man/powercom.txt +++ b/docs/man/powercom.txt @@ -8,12 +8,14 @@ powercom - UPS driver for serial Powercom/Trust/Advice UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the powercom driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports many similar kinds of serial UPS hardware (as well as a few USB UPS models with USB-to-serial adapters). The most common ones are the Trust 425/625, Powercom, and Advice Partner/King PR750. Others using the same @@ -24,6 +26,7 @@ see the NUT Hardware Compatibility List (HCL). EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5] file: @@ -116,6 +119,7 @@ system startup -- just when the power consumption tends to be high. DEFAULT VALUES FOR THE EXTRA ARGUMENTS -------------------------------------- + linevoltage = 230 manufacturer = PowerCom modelname = Unknown @@ -128,6 +132,7 @@ values for ALL models. Trust ~~~~~ + numOfBytesFromUPS = 11 methodOfFlowControl = dtr0rts1 validationSequence = {{5,0},{7,0},{8,0}} @@ -139,6 +144,7 @@ Trust KP625AP ~~~~~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = dtr0rts1 validationSequence = {{5,0x80},{7,0},{8,0}} @@ -150,6 +156,7 @@ KP625AP Egys ~~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0x80},{7,0},{8,0}} @@ -161,6 +168,7 @@ Egys IMP ~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0xFF},{7,0},{8,0}} @@ -168,6 +176,7 @@ IMP KIN ~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{11,0x4b},{8,0},{8,0}} @@ -175,6 +184,7 @@ KIN BNT ~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{11,0x42},{8,0},{8,0}} @@ -182,6 +192,7 @@ BNT BNT-other ~~~~~~~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{8,0},{8,0},{8,0}} @@ -193,25 +204,29 @@ BNT-other OPTI ~~~~ + numOfBytesFromUPS = 16 methodOfFlowControl = no_flow_control validationSequence = {{5,0xFF},{7,0},{8,0}} shutdownArguments = {{1,30},y} -AUTHOR ------- -Peter Bieringer , -Alexey Sidorov , -Keven L. Ates , -Rouben Tchakhmakhtchian +AUTHORS +------- + +* Peter Bieringer +* Alexey Sidorov +* Keven L. Ates +* Rouben Tchakhmakhtchian SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/powerman-pdu.txt b/docs/man/powerman-pdu.txt index 0feec852e7..0332ea64d9 100644 --- a/docs/man/powerman-pdu.txt +++ b/docs/man/powerman-pdu.txt @@ -38,6 +38,7 @@ This driver is not built by default. You can build it by using UPS COMMANDS ------------ + The following instant commands (see linkman:upscmd[8]) are available for each outlet of the PDU, with *X* standing for the outlet number: @@ -55,6 +56,7 @@ Cycle the outlet (power off then power on, possibly with a delay). IMPLEMENTATION -------------- + The hostname of the Powerman server is specified using the "port" value in *ups.conf*, i.e.: @@ -66,22 +68,26 @@ The port used to reach 'powermand' is optional if the default port is used. KNOWN ISSUES ------------ + In the current NUT version (2.4.1), ups.status is still exposed, with the value "WAIT". Some other values from the ups collection are also exposed. AUTHOR ------ + Arnaud Quette SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ -The PowerMan home page: http://powerman.sourceforge.net/ +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The PowerMan home page: https://github.com/chaos/powerman diff --git a/docs/man/powerpanel.txt b/docs/man/powerpanel.txt index 1cfe18cdb9..4e8450d0bc 100644 --- a/docs/man/powerpanel.txt +++ b/docs/man/powerpanel.txt @@ -8,12 +8,14 @@ powerpanel - Driver for serial PowerPanel Plus compatible UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the powerpanel driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports CyberPower BC1200, PR2200 and many other similar devices, both for the text and binary protocols. The driver will autodetect which protocol is used. @@ -24,6 +26,7 @@ network cards via SNMP. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in linkman:ups.conf[5]: *protocol=*['text,binary']:: @@ -51,6 +54,7 @@ seconds will be truncated to 6 seconds intervals, values above 60 seconds to VARIABLES --------- + Depending on the type of your UPS unit, some of the following variables may be changed with linkman:upsrw[8]. If the driver can't read a variable from the UPS, it will not be made available. @@ -72,6 +76,7 @@ writable: allow cold start from battery COMMANDS -------- + Depending on the type of your UPS unit, some of the following commands may be available. @@ -88,6 +93,7 @@ must verify that these work as expected (see <<_shutdown_issues,Shutdown Issues> SUPPORT STATUS -------------- + Vendor support is absent for this driver, so if you need some features that are currently not available, provide ample documentation on what the driver should sent to the UPS in order to make this work. If more information @@ -98,6 +104,7 @@ isn't willing to share this with us. SHUTDOWN ISSUES --------------- + If the *shutdown.return* command on your UPS doesn't seem to work, chances are that your UPS is an older model. Try a couple of different settings for 'offdelay'. If no value in the range 6..600 works, your @@ -114,6 +121,7 @@ supported through the text protocol are affected by this. KNOWN PROBLEMS -------------- + The CyberPower OP series don't offer direct voltage, charge, frequency and temperature readings. Instead, they will return a binary value that needs conversion to the actual value. @@ -128,6 +136,7 @@ instrument. AUTHORS ------- + Arjen de Korte , Doug Reynolds SEE ALSO @@ -135,13 +144,16 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Other drivers: ~~~~~~~~~~~~~~ + linkman:usbhid-ups[8], linkman:snmp-ups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/rhino.txt b/docs/man/rhino.txt index 443dec85e7..bdb30f892a 100644 --- a/docs/man/rhino.txt +++ b/docs/man/rhino.txt @@ -3,17 +3,20 @@ RHINO(8) NAME ---- + rhino - Driver for Brazilian Microsol RHINO UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the rhino driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -This driver has been tested with : + +This driver has been tested with: * Rhino 6000 VA * Rhino 7500 VA @@ -45,6 +48,7 @@ COMMANDS AUTHOR ------ + Silvino B. Magalhães SEE ALSO @@ -52,8 +56,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/richcomm_usb.txt b/docs/man/richcomm_usb.txt index 69578e74d5..fe53b14920 100644 --- a/docs/man/richcomm_usb.txt +++ b/docs/man/richcomm_usb.txt @@ -3,16 +3,19 @@ RICHCOMM_USB(8) NAME ---- + richcomm_usb - Driver UPS equipment using Richcomm dry-contact to USB solution NOTE ---- + This man page only documents the specific features of the richcomm_usb driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + The Richcomm dry-contact to USB solution is a generic interface that is used to upgrade an existing (RS-232) contact closure UPS interface to USB. As such, all the limitations of the underlying contact closure interface @@ -21,6 +24,7 @@ OB, and LB. See also linkman:genericups[8]. BUGS ---- + Most contact-closure UPSes will not power down the load if the line power is present. This can create a race when using secondary linkman:upsmon[8] systems. See the linkman:upsmon[8] man page for more information. @@ -31,20 +35,23 @@ UPS of some kind that allows detection and proper load cycling on command. AUTHORS ------- -Peter van Valderen , -Dirk Teurlings +* Peter van Valderen +* Dirk Teurlings SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] The generic serial driver: ~~~~~~~~~~~~~~~~~~~~~~~~~~ + linkman:genericups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/riello_ser.txt b/docs/man/riello_ser.txt index 45a399b902..1b75c1add5 100644 --- a/docs/man/riello_ser.txt +++ b/docs/man/riello_ser.txt @@ -36,8 +36,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/riello_usb.txt b/docs/man/riello_usb.txt index d4afbfedb7..73bf5e9f3a 100644 --- a/docs/man/riello_usb.txt +++ b/docs/man/riello_usb.txt @@ -35,8 +35,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/safenet.txt b/docs/man/safenet.txt index f6f5a5a34c..00aa02f929 100644 --- a/docs/man/safenet.txt +++ b/docs/man/safenet.txt @@ -3,21 +3,25 @@ SAFENET(8) NAME ---- + safenet - Driver for SafeNet compatible UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the safenet driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports UPS equipment which can be controlled via SafeNet v1.0 for Windows (serial interface only). EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5] file: @@ -38,8 +42,9 @@ Time to wait before switching on the UPS (minutes). Defaults to 1 minute. *offdelay=*'value':: Time to wait before shutting down the UPS (seconds). Defaults to 30 seconds. -UPSCMD ------- +INSTANT COMMANDS +---------------- + This driver supports some instant commands (see linkman:upscmd[8]): *test.battery.start*:: @@ -73,6 +78,7 @@ and *ondelay*. KNOWN PROBLEMS -------------- + If you run the *shutdown.return* command with mains present, the output may stay on or switch off and not back on again. The *shutdown.reboot* command will unconditionally switch on the load again (with or without mains @@ -87,6 +93,7 @@ return when the power comes back. AUTHOR ------ + Arjen de Korte SEE ALSO @@ -94,8 +101,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/skel.txt b/docs/man/skel.txt index 7cae6669c9..8e310943e0 100644 --- a/docs/man/skel.txt +++ b/docs/man/skel.txt @@ -3,10 +3,12 @@ SKEL(8) NAME ---- + skel - skeleton driver man page NOTE ---- + This man page only documents the hardware-specific features of the *skel* driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -31,6 +33,7 @@ apropos(8) database is properly rebuilt. SUPPORTED HARDWARE ------------------ + *skel* supports ... ////////////////////////////////////////// @@ -44,6 +47,7 @@ CABLING EXTRA ARGUMENTS --------------- + This driver also supports the following optional settings: *option1*='num':: @@ -86,6 +90,7 @@ encountered when implementing the protocol for your UPS. KNOWN ISSUES AND BUGS --------------------- + *Got "EPERM: Operation not permitted" upon driver startup*:: You have forgotten to install the udev files, as explained @@ -100,6 +105,7 @@ some form of contact information so that users can report bugs. AUTHORS ------- + J Random User ////////////////////////////////////////// @@ -114,8 +120,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/snmp-ups.txt b/docs/man/snmp-ups.txt index 23781be617..3a9b0ea513 100644 --- a/docs/man/snmp-ups.txt +++ b/docs/man/snmp-ups.txt @@ -8,6 +8,7 @@ snmp-ups - Multi-MIB Driver for SNMP UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the snmp-ups driver. For information about the core driver, see linkman:nutupsdrv[8]. @@ -149,6 +150,7 @@ run-time supported list. REQUIREMENTS ------------ + You will need to install the Net-SNMP package from http://www.net-snmp.org/ before building this driver. @@ -156,6 +158,7 @@ SNMP v3 also requires OpenSSL support from http://www.openssl.org. LIMITATIONS ----------- + Shutdown ~~~~~~~~ @@ -168,12 +171,14 @@ supported command. INSTALLATION ------------ + This driver is only built if the Net-SNMP development files are present at configuration time. You can also force it to be built by using +configure --with-snmp=yes+ before calling make. EXAMPLES -------- + The hostname of the UPS is specified with the "port" value in `ups.conf`, and may include a non-standard (161) remote peer port: @@ -197,6 +202,7 @@ The hostname of the UPS is specified with the "port" value in AUTHORS ------- + Arnaud Quette, Dmitry Frolov @@ -205,12 +211,15 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] NUT SNMP Protocols Library ~~~~~~~~~~~~~~~~~~~~~~~~~~ + Available at: http://www.networkupstools.org/ups-protocols.html#_snmp Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/socomec_jbus.txt b/docs/man/socomec_jbus.txt index 8133b6607b..ef913099c3 100644 --- a/docs/man/socomec_jbus.txt +++ b/docs/man/socomec_jbus.txt @@ -144,18 +144,21 @@ powered USB hub, try a full-speed USB isolator, etc. AUTHOR ------ + Thanos Chatziathanassiou SEE ALSO -------- + The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - -Socomec JBUS/Modbus Reference Guide: https://www.socomec.com/files/live/sites/systemsite/files/GB-JBUS-MODBUS-for-Delphys-MP-and-Delphys-MX-operating-manual.pdf -libmodbus home page: http://libmodbus.org +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* Socomec JBUS/Modbus Reference Guide: + https://www.socomec.com/files/live/sites/systemsite/files/GB-JBUS-MODBUS-for-Delphys-MP-and-Delphys-MX-operating-manual.pdf +* libmodbus home page: http://libmodbus.org diff --git a/docs/man/solis.txt b/docs/man/solis.txt index eb732439de..532112b25a 100644 --- a/docs/man/solis.txt +++ b/docs/man/solis.txt @@ -15,7 +15,8 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -This driver has been tested with : + +This driver has been tested with: * Solis 1000 VA * Solis 1500 VA @@ -55,6 +56,7 @@ connect to the UPS, but some values are read incorrectly. AUTHOR ------ + Silvino B. Magalhães SEE ALSO @@ -62,8 +64,10 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/tripplite.txt b/docs/man/tripplite.txt index 9305551eae..9ec37f295d 100644 --- a/docs/man/tripplite.txt +++ b/docs/man/tripplite.txt @@ -3,22 +3,26 @@ TRIPPLITE(8) NAME ---- + tripplite - Driver for Tripp-Lite SmartPro UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the tripplite driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver should work on the SmartPro line, including the SMART700 and SMART700SER. It only supports SmartPro models that communicate using the serial port. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]: @@ -36,25 +40,31 @@ reboot command. The default value is 60 (in seconds). KNOWN ISSUES AND BUGS --------------------- + Battery charge information may not be correct for all UPSes. It is tuned to be correct for a SMART700SER. Other models may not provide correct information. Information from the manufacturer would be helpful. AUTHORS ------- -Rickard E. (Rik) Faith, Nicholas Kain + +* Rickard E. (Rik) Faith +* Nicholas Kain SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Other drivers for Tripp-Lite hardware: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + linkman:tripplitesu[8], linkman:tripplite_usb[8], linkman:usbhid-ups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/tripplite_usb.txt b/docs/man/tripplite_usb.txt index d71537f676..134788199f 100644 --- a/docs/man/tripplite_usb.txt +++ b/docs/man/tripplite_usb.txt @@ -1,19 +1,21 @@ TRIPPLITE_USB(8) ================ - NAME ---- + tripplite_usb - Driver for older Tripp Lite USB UPSes (not PDC HID) SYNOPSIS -------- + *tripplite_usb* -h *tripplite_usb* -a 'UPS_NAME' ['OPTIONS'] SUPPORTED HARDWARE ------------------ + This driver should work with older Tripp Lite UPSes which are detected as USB HID-class devices, but are not true HID Power-Device Class devices. So far, the devices supported by tripplite_usb have product ID 0001, and the newer @@ -152,11 +154,13 @@ exposed as a USB string descriptor, there is no easy way to use this ID to distinguish between multiple UPS units on a single machine. The UPS would need to be claimed by the driver in order to read this ID. -AUTHOR ------- -Written by Charles Lepple, based on the linkman:tripplite[8] driver by Rickard E. (Rik) -Faith and Nicholas Kain. Please do not email the authors directly - use the -nut-upsdev mailing list. +AUTHORS +------- + +Written by Charles Lepple, based on the linkman:tripplite[8] driver +by Rickard E. (Rik) Faith and Nicholas Kain. + +Please do not email the authors directly - use the nut-upsdev mailing list. A Tripp Lite OMNIVS1000 was graciously donated to the NUT project by Bradley Feldman (http://www.bradleyloritheo.com) @@ -166,19 +170,21 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Other drivers for Tripp-Lite hardware: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + linkman:tripplite[8], linkman:tripplitesu[8], linkman:usbhid-ups[8] Other tools: ~~~~~~~~~~~~ -regex(7), lsusb(8) +regex(7), lsusb(8) -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/tripplitesu.txt b/docs/man/tripplitesu.txt index 6416699a1c..9194a1229d 100644 --- a/docs/man/tripplitesu.txt +++ b/docs/man/tripplitesu.txt @@ -3,20 +3,24 @@ TRIPPLITESU(8) NAME ---- + tripplitesu - Driver for Tripp-Lite SmartOnline (SU) UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the tripplitesu driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports the Tripp Lite SmartOnline family (via the serial port). EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]: @@ -27,6 +31,7 @@ until there are only a few seconds left. Common values are around 25--30. AUTHOR ------ + Allan N. Hessenflow SEE ALSO @@ -34,12 +39,15 @@ SEE ALSO The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Other drivers for Tripp-Lite hardware: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + linkman:tripplite[8], linkman:tripplite_usb[8], linkman:usbhid-ups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/ups.conf.txt b/docs/man/ups.conf.txt index 3998d8925e..10c060754c 100644 --- a/docs/man/ups.conf.txt +++ b/docs/man/ups.conf.txt @@ -3,6 +3,7 @@ UPS.CONF(5) NAME ---- + ups.conf - UPS definitions for Network UPS Tools DESCRIPTION @@ -140,6 +141,7 @@ verbosity level. UPS FIELDS ---------- + *driver*:: Required. This specifies which program will be monitoring this UPS. You @@ -289,8 +291,11 @@ from linkman:upsc[8] or similar as "snoopy@doghouse". SEE ALSO -------- -linkman:upsd[8], linkman:nutupsdrv[8], linkman:upsdrvctl[8] + +linkman:upsd[8], linkman:nutupsdrv[8], linkman:upsdrvctl[8], +linkman:upsdrvsvcctl[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsc.txt b/docs/man/upsc.txt index 11628867a9..2e89c5071a 100644 --- a/docs/man/upsc.txt +++ b/docs/man/upsc.txt @@ -1,13 +1,14 @@ UPSC(8) ======= - NAME ---- + upsc - example lightweight UPS client SYNOPSIS -------- + *upsc* -l | -L ['host'] *upsc* 'ups' ['variable'] @@ -23,6 +24,7 @@ want to include the full interface. OPTIONS ------- + *-l* 'host':: List all UPS names configured at 'host', one name per line. The hostname @@ -113,7 +115,7 @@ SEE ALSO linkman:upsd[8] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upscli_add_host_cert.txt b/docs/man/upscli_add_host_cert.txt index 87684df732..e558baf0ad 100644 --- a/docs/man/upscli_add_host_cert.txt +++ b/docs/man/upscli_add_host_cert.txt @@ -16,6 +16,7 @@ SYNOPSIS DESCRIPTION ----------- + The *upscli_add_host_cert()* function register a security rule associated to the 'hostname'. All connections to this host use this rule. @@ -34,5 +35,6 @@ RETURN VALUE SEE ALSO -------- + linkman:upscli_init[3], linkman:upscli_connect[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] diff --git a/docs/man/upscli_cleanup.txt b/docs/man/upscli_cleanup.txt index 611c3de051..1798a4f868 100644 --- a/docs/man/upscli_cleanup.txt +++ b/docs/man/upscli_cleanup.txt @@ -15,6 +15,7 @@ SYNOPSIS DESCRIPTION ----------- + The *upscli_cleanup()* function flushes SSL caches and frees memory used internally in upsclient module. @@ -25,5 +26,6 @@ The *upscli_cleanup()* function returns 1 on success, or -1 if an error occurs. SEE ALSO -------- + linkman:upscli_init[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] diff --git a/docs/man/upscli_connect.txt b/docs/man/upscli_connect.txt index 6662ae00fa..c97eb75185 100644 --- a/docs/man/upscli_connect.txt +++ b/docs/man/upscli_connect.txt @@ -15,6 +15,7 @@ SYNOPSIS DESCRIPTION ----------- + The *upscli_connect()* function takes the pointer 'ups' to a `UPSCONN_t` state structure and opens a TCP connection to the 'host' on the given 'port'. @@ -45,6 +46,7 @@ returns 0 on success, or -1 if an error occurs. SEE ALSO -------- + linkman:upscli_disconnect[3], linkman:upscli_fd[3], linkman:upscli_splitaddr[3], linkman:upscli_splitname[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], diff --git a/docs/man/upscli_disconnect.txt b/docs/man/upscli_disconnect.txt index 9ce36b458c..c8f11d1c7f 100644 --- a/docs/man/upscli_disconnect.txt +++ b/docs/man/upscli_disconnect.txt @@ -15,6 +15,7 @@ SYNOPSIS DESCRIPTION ----------- + The *upscli_disconnect()* function takes the pointer 'ups' to a `UPSCONN_t` state structure, shuts down the connection to the server, and frees dynamic memory used by the state structure. The `UPSCONN_t` structure @@ -31,5 +32,6 @@ error occurs. SEE ALSO -------- + linkman:upscli_connect[3], linkman:upscli_fd[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] diff --git a/docs/man/upscli_get.txt b/docs/man/upscli_get.txt index ff453fccf4..68d221d62f 100644 --- a/docs/man/upscli_get.txt +++ b/docs/man/upscli_get.txt @@ -3,7 +3,8 @@ UPSCLI_GET(3) NAME ---- -upscli_get - retrieve data from a UPS + +upscli_get - retrieve data from an UPS SYNOPSIS -------- @@ -15,6 +16,7 @@ SYNOPSIS DESCRIPTION ----------- + The *upscli_get()* function takes the pointer 'ups' to a `UPSCONN_t` state structure, and the pointer 'query' to an array of 'numq' query elements. It builds a properly-formatted request from @@ -40,6 +42,7 @@ Some examples are: QUERY FORMATTING ---------------- + To generate a request for `GET NUMLOGINS su700`, you would populate query and numq as follows: @@ -55,6 +58,7 @@ is handled for you inside this function. ANSWER FORMATTING ----------------- + The raw response from upsd to the above query would be `NUMLOGINS su700 1`. Since this is split up for you, the values work out like this: @@ -69,6 +73,7 @@ Notice that the value which you seek typically starts at answer[numq]. ERROR CHECKING -------------- + This function will check your query against the response from linkman:upsd[8]. For example, if you send "VAR" "su700" "ups.status", it will expect to see those at the beginning of the response. @@ -79,6 +84,7 @@ happens, linkman:upscli_upserror[3] will return 'UPSCLI_ERR_PROTOCOL'. ANSWER ARRAY LIFETIME --------------------- + The pointers contained within the 'answer' array are only valid until the next call to a 'upsclient' function which references them. If you need to use data from multiple calls, you must copy it somewhere @@ -95,6 +101,7 @@ access after that point is also undefined. RETURN VALUE ------------ + The *upscli_get()* function returns 0 on success, or -1 if an error occurs. @@ -110,5 +117,6 @@ will allow the *upscli_get()* call to return an error in that case: SEE ALSO -------- + linkman:upscli_list_start[3], linkman:upscli_list_next[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] diff --git a/docs/man/upscli_init.txt b/docs/man/upscli_init.txt index 07c8b5f13f..1bf1343617 100644 --- a/docs/man/upscli_init.txt +++ b/docs/man/upscli_init.txt @@ -16,6 +16,7 @@ SYNOPSIS DESCRIPTION ----------- + The *upscli_init()* function initialize upsclient module and set many SSL-related properties: 'certverify' to 1 makes certificate verification required for all SSL connections and 'certpath' is the location of @@ -53,6 +54,7 @@ The *upscli_init()* function returns 1 on success, or -1 if an error occurs. SEE ALSO -------- + linkman:upscli_add_host_cert[3], linkman:upscli_cleanup[3], linkman:upscli_disconnect[3], linkman:upscli_fd[3], linkman:upscli_splitaddr[3], linkman:upscli_splitname[3], diff --git a/docs/man/upscli_list_next.txt b/docs/man/upscli_list_next.txt index b44d5efdf4..88c53f6c6d 100644 --- a/docs/man/upscli_list_next.txt +++ b/docs/man/upscli_list_next.txt @@ -66,5 +66,6 @@ its first call in that case. SEE ALSO -------- + linkman:upscli_list_start[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] diff --git a/docs/man/upscli_list_start.txt b/docs/man/upscli_list_start.txt index b5d7dd9f5f..b6b9b62817 100644 --- a/docs/man/upscli_list_start.txt +++ b/docs/man/upscli_list_start.txt @@ -69,6 +69,7 @@ When this happens, linkman:upscli_upserror[3] will return RETURN VALUE ------------ + The *upscli_list_start()* function returns 0 on success, or -1 if an error occurs. diff --git a/docs/man/upscli_readline.txt b/docs/man/upscli_readline.txt index 6c4db6ccf2..a2c082777e 100644 --- a/docs/man/upscli_readline.txt +++ b/docs/man/upscli_readline.txt @@ -18,6 +18,7 @@ SYNOPSIS DESCRIPTION ----------- + The *upscli_readline()* and *upscli_readline_timeout()* functions take the pointer 'ups' to a `UPSCONN_t` state structure, receive a single line from the server, and copy up to 'buflen' bytes of the response into the buffer 'buf'. diff --git a/docs/man/upscli_sendline.txt b/docs/man/upscli_sendline.txt index 5e4e7edb6d..bc0bcba6c5 100644 --- a/docs/man/upscli_sendline.txt +++ b/docs/man/upscli_sendline.txt @@ -9,7 +9,6 @@ upscli_sendline, upscli_sendline_timeout - send a single command to a UPS SYNOPSIS -------- - #include int upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen); diff --git a/docs/man/upscli_splitname.txt b/docs/man/upscli_splitname.txt index 8990f0cc0c..d40e642982 100644 --- a/docs/man/upscli_splitname.txt +++ b/docs/man/upscli_splitname.txt @@ -37,6 +37,7 @@ Definitions without an explicit port value receive the default value of MEMORY USAGE ------------ + You must *free*(3) the pointers to 'upsname' and 'hostname' when you are done with them to avoid memory leaks. diff --git a/docs/man/upsclient.txt b/docs/man/upsclient.txt index 54210d7f79..0c2ac15ad2 100644 --- a/docs/man/upsclient.txt +++ b/docs/man/upsclient.txt @@ -60,6 +60,7 @@ in memory and file descriptor leaks in your program. ERROR HANDLING -------------- + In the event of an error, linkman:upscli_strerror[3] will provide human-readable details on what happened. linkman:upscli_upserror[3] may also be used to retrieve the error number. These numbers are defined in @@ -67,6 +68,7 @@ also be used to retrieve the error number. These numbers are defined in SEE ALSO -------- + linkman:libupsclient-config[1], linkman:upscli_init[3], linkman:upscli_cleanup[3], linkman:upscli_add_host_cert[3], linkman:upscli_connect[3], linkman:upscli_disconnect[3], linkman:upscli_fd[3], diff --git a/docs/man/upscmd.txt b/docs/man/upscmd.txt index 9492973758..b8c80d8274 100644 --- a/docs/man/upscmd.txt +++ b/docs/man/upscmd.txt @@ -3,10 +3,12 @@ UPSCMD(8) NAME ---- + upscmd - UPS administration program for instant commands SYNOPSIS -------- + *upscmd* -h *upscmd* -l 'ups' @@ -102,8 +104,10 @@ It involves magic cookies. SEE ALSO -------- + linkman:upsd[8], linkman:upsrw[8] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upscode2.txt b/docs/man/upscode2.txt index b03763a84b..d3a9ec604a 100644 --- a/docs/man/upscode2.txt +++ b/docs/man/upscode2.txt @@ -3,22 +3,26 @@ UPSCODE2(8) NAME ---- + upscode2 - Driver for UPScode II compatible UPS equipment NOTE ---- + This man page only documents the hardware-specific features of the upscode2 driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + This driver supports UPS equipment which can be controlled via the UPScode II protocol. This is mainly Fiskars, Powerware equipment, but also some (probably OEM'ed) products from Compaq. EXTRA ARGUMENTS --------------- + This driver supports the following optional settings in the linkman:ups.conf[5]: @@ -82,18 +86,21 @@ have not returned a value for 'battery.charge'. Therefore, the driver will guestimate a value based on the nominal battery min/max and the current battery voltage. -AUTHOR ------- -Håvard Lygre , -Niels Baggesen +AUTHORS +------- + +* Håvard Lygre SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsd.conf.txt b/docs/man/upsd.conf.txt index 67e41dbf46..410910e99e 100644 --- a/docs/man/upsd.conf.txt +++ b/docs/man/upsd.conf.txt @@ -152,6 +152,7 @@ SEE ALSO linkman:upsd[8], linkman:nutupsdrv[8], linkman:upsd.users[5] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsd.txt b/docs/man/upsd.txt index 3dc2aebcf3..9a4a887a56 100644 --- a/docs/man/upsd.txt +++ b/docs/man/upsd.txt @@ -8,6 +8,7 @@ upsd - UPS information server SYNOPSIS -------- + *upsd* -h *upsd* ['OPTIONS'] @@ -156,15 +157,18 @@ SEE ALSO Clients: ~~~~~~~~ + linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8], linkman:upslog[8], linkman:upsmon[8] CGI programs: ~~~~~~~~~~~~~ + linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] Drivers: ~~~~~~~~ + linkman:nutupsdrv[8], linkman:apcsmart[8], linkman:belkin[8], linkman:belkinunv[8], linkman:bestuferrups[8], linkman:bestups[8], @@ -177,4 +181,5 @@ linkman:tripplite[8], linkman:tripplitesu[8], linkman:victronups[8], Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsd.users.txt b/docs/man/upsd.users.txt index 10bbdba262..6b287d02e5 100644 --- a/docs/man/upsd.users.txt +++ b/docs/man/upsd.users.txt @@ -3,6 +3,7 @@ UPSD.USERS(5) NAME ---- + upsd.users - Administrative user definitions for NUT upsd DESCRIPTION @@ -84,7 +85,8 @@ SEE ALSO linkman:upsd[8], linkman:upsd.conf[5] -INTERNET RESOURCES ------------------- +Internet resources: +~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsdrvctl.txt b/docs/man/upsdrvctl.txt index aa09a962e1..46ec658d7c 100644 --- a/docs/man/upsdrvctl.txt +++ b/docs/man/upsdrvctl.txt @@ -8,6 +8,7 @@ upsdrvctl - UPS driver controller SYNOPSIS -------- + *upsdrvctl* -h *upsdrvctl* ['OPTIONS'] {start | stop | shutdown} ['ups'] @@ -103,8 +104,10 @@ background. SEE ALSO -------- + linkman:upsdrvsvcctl[8], linkman:nutupsdrv[8], linkman:upsd[8], linkman:ups.conf[5] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsdrvsvcctl.txt b/docs/man/upsdrvsvcctl.txt index d0a866e11d..813756faf4 100644 --- a/docs/man/upsdrvsvcctl.txt +++ b/docs/man/upsdrvsvcctl.txt @@ -8,6 +8,7 @@ upsdrvsvcctl - UPS driver service instance controller SYNOPSIS -------- + *upsdrvsvcctl* -h *upsdrvsvcctl* ['OPTIONS'] {start | stop } ['ups'] @@ -189,9 +190,11 @@ Look for `/var/svc/log/system-power-nut-driver:instance-name.log` file. SEE ALSO -------- + linkman:upsdrvctl[8], linkman:nutupsdrv[8], linkman:upsd[8], linkman:nut-driver-enumerator[8], linkman:ups.conf[5] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsimage.cgi.txt b/docs/man/upsimage.cgi.txt index 9ede74a723..bdf23b83f6 100644 --- a/docs/man/upsimage.cgi.txt +++ b/docs/man/upsimage.cgi.txt @@ -45,6 +45,5 @@ linkman:upsd[8], linkman:upsstats.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -The gd home page: http://libgd.bitbucket.org - -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The gd home page: http://libgd.bitbucket.org diff --git a/docs/man/upslog.txt b/docs/man/upslog.txt index c8e1964fec..f405104888 100644 --- a/docs/man/upslog.txt +++ b/docs/man/upslog.txt @@ -115,13 +115,16 @@ SEE ALSO Server: ~~~~~~~ + linkman:upsd[8] Clients: ~~~~~~~~ + linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8], linkman:upsmon[8], linkman:upssched[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsmon.conf.txt b/docs/man/upsmon.conf.txt index 7a851bac64..1bd18ab242 100644 --- a/docs/man/upsmon.conf.txt +++ b/docs/man/upsmon.conf.txt @@ -413,8 +413,10 @@ does not change the previously active logging verbosity. SEE ALSO -------- + linkman:upsmon[8], linkman:upsd[8], linkman:nutupsdrv[8]. Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index 2f226d5f42..9e65fa5466 100644 --- a/docs/man/upsmon.txt +++ b/docs/man/upsmon.txt @@ -450,17 +450,21 @@ SEE ALSO Server: ~~~~~~~ + linkman:upsd[8] Clients: ~~~~~~~~ + linkman:upsc[8], linkman:upscmd[8], linkman:upsrw[8], linkman:upsmon[8] CGI programs: ~~~~~~~~~~~~~ + linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsrw.txt b/docs/man/upsrw.txt index 5081edf218..3f95235b38 100644 --- a/docs/man/upsrw.txt +++ b/docs/man/upsrw.txt @@ -112,8 +112,10 @@ confusing. SEE ALSO -------- + linkman:upsd[8], linkman:upscmd[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upssched.conf.txt b/docs/man/upssched.conf.txt index baa7f4f43b..52a028c77d 100644 --- a/docs/man/upssched.conf.txt +++ b/docs/man/upssched.conf.txt @@ -107,4 +107,5 @@ linkman:upssched[8], linkman:upsmon[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upssched.txt b/docs/man/upssched.txt index f15581e9ab..0fb033b973 100644 --- a/docs/man/upssched.txt +++ b/docs/man/upssched.txt @@ -8,6 +8,7 @@ upssched - Timer helper for scheduling events from upsmon SYNOPSIS -------- + *upssched* NOTE: *upssched* should be run from linkman:upsmon[8] via the NOTIFYCMD. @@ -112,4 +113,5 @@ linkman:upsmon[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsset.cgi.txt b/docs/man/upsset.cgi.txt index 79f5786d15..78912dda12 100644 --- a/docs/man/upsset.cgi.txt +++ b/docs/man/upsset.cgi.txt @@ -94,4 +94,5 @@ SEE ALSO Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsset.conf.txt b/docs/man/upsset.conf.txt index 2058a24907..02cd996714 100644 --- a/docs/man/upsset.conf.txt +++ b/docs/man/upsset.conf.txt @@ -55,8 +55,10 @@ web server, don't blame me. SEE ALSO -------- + linkman:upsset.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsstats.cgi.txt b/docs/man/upsstats.cgi.txt index 141cfefb24..e79e5395ca 100644 --- a/docs/man/upsstats.cgi.txt +++ b/docs/man/upsstats.cgi.txt @@ -59,4 +59,5 @@ linkman:upsimage.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/upsstats.html.txt b/docs/man/upsstats.html.txt index 9d30841f10..ec869f480a 100644 --- a/docs/man/upsstats.html.txt +++ b/docs/man/upsstats.html.txt @@ -232,4 +232,5 @@ linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/usbhid-ups.txt b/docs/man/usbhid-ups.txt index 20c48948a2..457b1687d9 100644 --- a/docs/man/usbhid-ups.txt +++ b/docs/man/usbhid-ups.txt @@ -261,8 +261,10 @@ SEE ALSO The core driver ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources ~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/victronups.txt b/docs/man/victronups.txt index 05be10c1b6..73982ed24d 100644 --- a/docs/man/victronups.txt +++ b/docs/man/victronups.txt @@ -3,16 +3,19 @@ VICTRONUPS(8) NAME ---- + victronups - Driver for IMV/Victron UPS unit Match, Match Lite, NetUps NOTE ---- + This man page only documents the hardware-specific features of the the victronups driver. For information about the core driver, see linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ + The victronups driver should recognize all Victron models that use a serial protocol at 1200 bps. These include Match Lite, Match and the NetUps line. @@ -29,6 +32,7 @@ docs/cables/victron.txt EXTRA ARGUMENTS --------------- + This driver supports the following optional setting in the linkman:ups.conf[5]: @@ -41,20 +45,24 @@ Set delay before shutdown on UPS BUGS ---- + The protocol for this UPS is not officially documented. -AUTHOR ------- -Radek Benedikt , -Daniel Prynych +AUTHORS +------- + +* Radek Benedikt +* Daniel Prynych SEE ALSO -------- The core driver: ~~~~~~~~~~~~~~~~ + linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ + The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/nut-names.txt b/docs/nut-names.txt index 58db63e4de..8ff3a9ddd9 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -274,11 +274,13 @@ broken down to their base components. Phase Count Determination ^^^^^^^^^^^^^^^^^^^^^^^^^ + input.phases (3 for three-phase, absent or 1 for 1phase) output.phases (as for input.phases) DOMAINs ^^^^^^^ + Any input or output is considered a valid DOMAIN. input (should really be called input.mains, but keep this for compat) diff --git a/docs/packager-guide.txt b/docs/packager-guide.txt index 4730dcb2bb..b9175969a6 100644 --- a/docs/packager-guide.txt +++ b/docs/packager-guide.txt @@ -130,6 +130,7 @@ The following packagers should be interested in working on this subject: Possible use cases ------------------ + - standalone (1 system + 1-n UPS) - network server (same as standalone, but serving data to network clients) - network monitoring client @@ -162,6 +163,7 @@ This standard was created by: Overview of the package tree ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + FIXME: make a dependency graph - <> @@ -383,6 +385,7 @@ TO BE CONTINUED Configuration option ^^^^^^^^^^^^^^^^^^^^ + name= "ups" or "nut" ./configure \ --prefix=/ \ From e2d9523e82f809700905b75f0fd7e5631b99d5be Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 12 Apr 2022 09:00:18 +0000 Subject: [PATCH 444/700] Makefile.am: define dependencies between recipes for dist* and package; tarball, sig and checksum filenames --- Makefile.am | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index b42d639cab..5c169e8faf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -215,11 +215,15 @@ tools/gitlog2changelog.py: tools/gitlog2changelog.py.in # ---------------------------------------------------------------------- # Maintainers targets: distribution signature and hashes -dist-sig: +nut-@PACKAGE_VERSION@.tar.gz: dist +nut-@PACKAGE_VERSION@.tar.gz.sig: dist-sig +nut-@PACKAGE_VERSION@.tar.gz.md5 nut-@PACKAGE_VERSION@.tar.gz.sha256: dist-hash + +dist-sig: nut-@PACKAGE_VERSION@.tar.gz rm -f nut-@PACKAGE_VERSION@.tar.gz.sig gpg --detach-sign nut-@PACKAGE_VERSION@.tar.gz -dist-hash: +dist-hash: nut-@PACKAGE_VERSION@.tar.gz md5sum nut-@PACKAGE_VERSION@.tar.gz > nut-@PACKAGE_VERSION@.tar.gz.md5 sha256sum nut-@PACKAGE_VERSION@.tar.gz > nut-@PACKAGE_VERSION@.tar.gz.sha256 @@ -295,7 +299,7 @@ MAINTAINERCLEANFILES_PACKAGES += *.p5p MAINTAINERCLEANFILES += $(MAINTAINERCLEANFILES_DISTBALL) MAINTAINERCLEANFILES += $(MAINTAINERCLEANFILES_PACKAGES) -package: +package: dist DESTDIR="$(abs_builddir)/_install_pkgprotodir" ; export DESTDIR; \ rm -rf "$$DESTDIR"; \ case "`uname -s`" in \ From ed8cc45217986971fa990c4a8bd47ee4bc9148d5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 13 Apr 2022 17:51:16 +0200 Subject: [PATCH 445/700] docs/asciidoc.txt: update link to the Asciidoc Manual (and project now on github) --- docs/asciidoc.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/asciidoc.txt b/docs/asciidoc.txt index 6977051524..1f7b391e05 100644 --- a/docs/asciidoc.txt +++ b/docs/asciidoc.txt @@ -6,8 +6,8 @@ Charles Lepple Intro ----- -See http://www.methods.co.nz/asciidoc/[The AsciiDoc Manual] for more -information. +See the https://asciidoc-py.github.io/userguide.html[AsciiDoc User Guide] +for more information. Works in Progress ----------------- From 4234b226bfc99c13d544e8599ebf56a8329c0d3b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 13:57:06 +0200 Subject: [PATCH 446/700] ci_build.sh: use "gmake" for "./ci_build.sh spellcheck" if available and no particular MAKE was requested - fall back to "make" if "gmake" is not available --- ci_build.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ci_build.sh b/ci_build.sh index 36c72546f9..e69f937eda 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -498,9 +498,13 @@ optional_dist_clean_check() { if [ "$1" = spellcheck ] && [ -z "$BUILD_TYPE" ] ; then # Note: this is a little hack to reduce typing # and scrolling in (docs) developer iterations. - if [ -z "${MAKE-}" ] && (command -v gmake) >/dev/null 2>/dev/null ; then - # GNU make processes quiet mode better, which helps with this use-case - MAKE=gmake + if [ -z "${MAKE-}" ] ; then + if (command -v gmake) >/dev/null 2>/dev/null ; then + # GNU make processes quiet mode better, which helps with this use-case + MAKE=gmake + else + MAKE=make + fi export MAKE fi if [ -s Makefile ] && [ -s docs/Makefile ]; then From 3911529e5cfc5e537477db57b45652b6d1bd0041 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 14:01:12 +0200 Subject: [PATCH 447/700] docs/ci-farm-lxc-setup.txt: fix formatting, extend on Jenkins agent setup --- ci_build.sh | 1 + docs/ci-farm-lxc-setup.txt | 298 ++++++++++++++++++++++++++++--------- docs/nut.dict | 25 +++- 3 files changed, 248 insertions(+), 76 deletions(-) diff --git a/ci_build.sh b/ci_build.sh index e69f937eda..db5a8355f7 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -503,6 +503,7 @@ if [ "$1" = spellcheck ] && [ -z "$BUILD_TYPE" ] ; then # GNU make processes quiet mode better, which helps with this use-case MAKE=gmake else + # Use system default, there should be one MAKE=make fi export MAKE diff --git a/docs/ci-farm-lxc-setup.txt b/docs/ci-farm-lxc-setup.txt index 6ad461498a..0e7fa7f078 100644 --- a/docs/ci-farm-lxc-setup.txt +++ b/docs/ci-farm-lxc-setup.txt @@ -33,14 +33,16 @@ Common preparations * Prepare an LVM partition (or preferably some other tech like ZFS) as `/srv/libvirt` and create a `/srv/libvirt/rootfs` to hold the containers -* Prepare `/home/abuild` on the host system (preferably in ZFS with dedup); +* Prepare `/home/abuild` on the host system (preferably in ZFS with + lightweight compression like lz4 -- and optionally, only if the amount + of available system RAM permits, with deduplication; otherwise avoid it); account user and group ID numbers are `399` as on the rest of the CI farm (historically, inherited from OBS workers) ** It may help to generate an ssh key without a passphrase for `abuild` that it would trust, to sub-login from CI agent sessions into the container. Then again, it may be not required if CI logs into the - host by SSH using authorized_keys and an SSH Agent, and the inner + host by SSH using `authorized_keys` and an SSH Agent, and the inner ssh client would forward that auth channel to the original agent. + ------ @@ -75,7 +77,7 @@ Setup a container Note that completeness of qemu CPU emulation varies, so not all distros can be installed, e.g. "s390x" failed for both debian10 and debian11 to -set up the openssh-server package, or once even to run /bin/true (seems +set up the `openssh-server` package, or once even to run `/bin/true` (seems to have installed an older release though, to match the outdated emulation?) While the `lxc-create` tool does not really specify the error cause and @@ -83,19 +85,22 @@ deletes the directories after failure, it shows the pathname where it writes the log (also deleted). Before re-trying the container creation, this file can be watched with e.g. `tail -F /var/cache/lxc/.../debootstrap.log` -NOTE: You can find the list of LXC "template" definitions on your system +[NOTE] +====== +You can find the list of LXC "template" definitions on your system by looking at the contents of the `/usr/share/lxc/templates/` directory, e.g. a script named `lxc-debian` for the "debian" template. You can see further options for each "template" by invoking its help action, e.g.: -+ ------ :; lxc-create -t debian -h ------ +====== * Install containers like this: + ------ -:; lxc-create -n jenkins-debian11-mips64el -P /srv/libvirt/rootfs -t debian -- \ +:; lxc-create -P /srv/libvirt/rootfs \ + -n jenkins-debian11-mips64el -t debian -- \ -r bullseye -a mips64el ------ ** to specify a particular mirror (not everyone hosts everything -- @@ -107,21 +112,24 @@ further options for each "template" by invoking its help action, e.g.: + ------ :; MIRROR="http://ftp.br.debian.org/debian/" \ - lxc-create -n jenkins-debian10-mips -P /srv/libvirt/rootfs -t debian -- \ + lxc-create -P /srv/libvirt/rootfs \ + -n jenkins-debian10-mips -t debian -- \ -r buster -a mips ------ ** ...or for EOLed distros, use the archive, e.g.: + ------ :; MIRROR="http://archive.debian.org/debian-archive/debian/" \ - lxc-create -n jenkins-debian8-s390x -P /srv/libvirt/rootfs -t debian -- \ + lxc-create -P /srv/libvirt/rootfs \ + -n jenkins-debian8-s390x -t debian -- \ -r jessie -a s390x ------ ** ...Alternatively, other distributions can be used (as supported by your LXC scripts, typically in `/usr/share/debootstrap/scripts`), e.g. Ubuntu: + ------ -:; lxc-create -n jenkins-ubuntu1804-s390x -P /srv/libvirt/rootfs -t ubuntu -- \ +:; lxc-create -P /srv/libvirt/rootfs \ + -n jenkins-ubuntu1804-s390x -t ubuntu -- \ -r bionic -a s390x ------ ** For distributions with a different packaging mechanism from that on the @@ -132,15 +140,19 @@ further options for each "template" by invoking its help action, e.g.: Otherwise, you risk seeing something like this: + ------ -root@debian:~# lxc-create -n jenkins-centos7-x86-64 -P /srv/libvirt/rootfs \ - -t centos -- -R 7 -a x86_64 -Host CPE ID from /etc/os-release: +root@debian:~# lxc-create -P /srv/libvirt/rootfs \ + -n jenkins-centos7-x86-64 -t centos -- \ + -R 7 -a x86_64 + +Host CPE ID from /etc/os-release: 'yum' command is missing -lxc-create: jenkins-centos7-x86-64: lxccontainer.c: create_run_template: 1616 Failed to create container from template -lxc-create: jenkins-centos7-x86-64: tools/lxc_create.c: main: 319 Failed to create container jenkins-centos7-x86-64 +lxc-create: jenkins-centos7-x86-64: lxccontainer.c: + create_run_template: 1616 Failed to create container from template +lxc-create: jenkins-centos7-x86-64: tools/lxc_create.c: + main: 319 Failed to create container jenkins-centos7-x86-64 ------ + - Note also that with such "third-party" distributions you may face other +Note also that with such "third-party" distributions you may face other issues; for example, the CentOS helper did not generate some fields in the `config` file that were needed for conversion into libvirt "domxml" (as found by trial and error, and comparison to other `config` files): @@ -150,7 +162,7 @@ lxc.uts.name = jenkins-centos7-x86-64 lxc.arch = x86_64 ------ + - Also note the container/system naming without underscore in "x86_64" -- +Also note the container/system naming without underscore in "x86_64" -- the deployed system discards the character when assigning its hostname. Using "amd64" is another reasonable choice here. @@ -174,13 +186,15 @@ new host reservation! > /tmp/x && virsh define /tmp/x ------ + -NOTE: You may want to tune the default generic 64MB RAM allocation, so your - launched QEMU containers are not OOM-killed as they exceeded their memory - cgroup limit. In practice they do not eat that much resident memory, just - want to have it addressable by VMM, I guess (swap is not very used either), - at least not until active builds start (then it depends on `make` program - parallelism level you allow, e.g. by `MAXPARMAKES` envvar for `ci_build.sh`, - and on the number of Jenkins "executors" assigned to the build agent). +NOTE: You may want to tune the default generic 64MB RAM allocation, + so your launched QEMU containers are not OOM-killed as they exceeded + their memory `cgroup` limit. In practice they do not eat *that much* + resident memory, just want to have it addressable by VMM, I guess + (swap is not very used either), at least not until active builds + start (and then it depends on compiler appetite and `make` program + parallelism level you allow, e.g. by pre-exporting `MAXPARMAKES` + environment variable for `ci_build.sh`, and on the number of Jenkins + "executors" assigned to the build agent). + ** It may be needed to revert the generated "os/arch" to `x86_64` (and let QEMU handle the rest) in the `/tmp/x` file, and re-try the definition: @@ -189,8 +203,9 @@ NOTE: You may want to tune the default generic 64MB RAM allocation, so your :; virsh define /tmp/x ------ -* Then `virsh edit jenkins-debian11-armhf` (and other containers) to bind-mount - the common `/home/abuild`, adding this tag to their "devices": +* Then execute `virsh edit jenkins-debian11-armhf` (and same for other + containers) to bind-mount the common `/home/abuild` location, adding + this tag to their "devices": + ------ @@ -198,8 +213,8 @@ NOTE: You may want to tune the default generic 64MB RAM allocation, so your ------ -** Note that generated XML might not conform to current LXC schema so it - fails validation during save; this can be bypassed with "i" when it asks. +** Note that generated XML might not conform to current LXC schema, so it + fails validation during save; this can be bypassed with `i` when it asks. One such case was however with indeed invalid contents, the "dir:" schema removed by example above. @@ -260,8 +275,14 @@ Shepherd the herd ------ :; for ALTROOT in /srv/libvirt/rootfs/*/rootfs/ ; do \ echo "=== $ALTROOT :" >&2; \ - grep eth0 "$ALTROOT/etc/issue" || ( echo '\S{NAME} \S{VERSION_ID} \n \l@\b ; Current IP(s): \4{eth0} \4{eth1} \4{eth2} \4{eth3}' >> "$ALTROOT/etc/issue" ) ; \ - grep eth0 "$ALTROOT/etc/issue.net" || ( echo '\S{NAME} \S{VERSION_ID} \n \l@\b ; Current IP(s): \4{eth0} \4{eth1} \4{eth2} \4{eth3}' >> "$ALTROOT/etc/issue.net" ) ; \ + grep eth0 "$ALTROOT/etc/issue" || ( printf '%s %s\n' \ + '\S{NAME} \S{VERSION_ID} \n \l@\b ;' \ + 'Current IP(s): \4{eth0} \4{eth1} \4{eth2} \4{eth3}' \ + >> "$ALTROOT/etc/issue" ) ; \ + grep eth0 "$ALTROOT/etc/issue.net" || ( printf '%s %s\n' \ + '\S{NAME} \S{VERSION_ID} \n \l@\b ;' \ + 'Current IP(s): \4{eth0} \4{eth1} \4{eth2} \4{eth3}' \ + >> "$ALTROOT/etc/issue.net" ) ; \ groupadd -R "$ALTROOT" -g 399 abuild ; \ useradd -R "$ALTROOT" -u 399 -g abuild -M -N -s /bin/bash abuild \ || useradd -R "$ALTROOT" -u 399 -g 399 -M -N -s /bin/bash abuild \ @@ -277,7 +298,9 @@ Shepherd the herd } ; \ if [ -s "$ALTROOT/etc/ssh/sshd_config" ]; then \ grep 'AcceptEnv \*' "$ALTROOT/etc/ssh/sshd_config" || ( \ - ( echo ""; echo "# For CI: Allow passing any envvars:"; echo 'AcceptEnv *' ) \ + ( echo "" ; \ + echo "# For CI: Allow passing any envvars:"; \ + echo 'AcceptEnv *' ) \ >> "$ALTROOT/etc/ssh/sshd_config" \ ) ; \ fi ; \ @@ -287,10 +310,11 @@ Shepherd the herd Note that for some reason, in some of those other-arch distros `useradd` fails to find the group anyway; then we have to "manually" add them. -* Let the host know names/IPs of containers you assigned: +* Let the host know and resolve the names/IPs of containers you assigned: + ------ -:; grep -v '#' /etc/lxc/dnsmasq-hosts.conf | while IFS=, read N I ; do \ +:; grep -v '#' /etc/lxc/dnsmasq-hosts.conf \ + | while IFS=, read N I ; do \ getent hosts "$N" >&2 || echo "$I $N" ; \ done >> /etc/hosts ------ @@ -307,8 +331,9 @@ order to conserve space and run-time stress. Still, if there are significant version outliers (such as using an older distribution due to vCPU requirements), it can be installed fully just -to ensure non-regression -- that when adapting Makefile rule definitions -to modern tools, we do not lose ability to build with older ones. +to ensure non-regression -- e.g. that when adapting `Makefile` rule +definitions or compiler arguments to modern toolkits, we do not lose +the ability to build with older ones. For this, `chroot` from the host system can be used, e.g. to improve the interactive usability for a population of Debian(-compatible) containers @@ -324,15 +349,17 @@ may be not yet configured or still struggling to access the Internet): Similarly for `yum`-managed systems (CentOS and relatives), though specific package names can differ, and additional package repositories may need to -be enabled first (see link:config-prereqs.txt for details). +be enabled first (see link:config-prereqs.txt[config-prereqs.txt] for more +details such as recommended package names). Note that technically `(sudo) chroot ...` can also be used from the CI worker account on the host system to build in the prepared filesystems without the -overhead of running containers and several copies of Jenkins `agent.jar`. +overhead of running containers as complete operating environments with any +standard services and several copies of Jenkins `agent.jar` in them. -Also note that set-up of some packages, including the `ca-certificates` and -the JDK/JRE, require that the `/proc` filesystem is usable in the chroot. -This can be achieved with e.g.: +Also note that externally-driven set-up of some packages, including the +`ca-certificates` and the JDK/JRE, require that the `/proc` filesystem +is usable in the chroot environment. This can be achieved with e.g.: ------ :; for ALTROOT in /srv/libvirt/rootfs/*/rootfs/ ; do \ for D in proc ; do \ @@ -346,34 +373,69 @@ This can be achieved with e.g.: TODO: Test and document a working NAT and firewall setup for this, to allow SSH access to the containers via dedicated TCP ports exposed on the host. + Troubleshooting ~~~~~~~~~~~~~~~ -Q: Container won't start, its `virsh console` says something like: +* Q: Container won't start, its `virsh console` says something like: + ------ Failed to create symlink /sys/fs/cgroup/net_cls: Operation not permitted ------ - A: According to https://bugzilla.redhat.com/show_bug.cgi?id=1770763 -(skip to the end for summary) this can happen when a newer Linux host system -with cgroupsv2 runs an older guest distro that only knows about cgroupsv1, -such as CentOS 7. One workaround is to ensure that the guest systemd does -not try to join host facilities, by setting an explicit empty list: + (skip to the end for summary) this can happen when a newer Linux + host system with `cgroupsv2` capabilities runs an older guest distro + which only knows about `cgroupsv1`, such as when hosting a CentOS 7 + container on a Debian 11 server. +** One workaround is to ensure that the guest `systemd` does not try to + "join" host facilities, by setting an explicit empty list for that: + ------ :; echo 'JoinControllers=' >> "$ALTROOT/etc/systemd/system.conf" ------ +** Another approach is to upgrade `systemd` related packages in the guest + container. This may require additional "backport" repositories or + similar means, possibly maintained not by distribution itself but by + other community members, and arguably would logically compromise the + idea of non-regression builds in the old environment "as is". + +* Q: Server was set up with ZFS as recommended, and lots of I/O hit the + disk even when application writes are negligible ++ +A: This was seen on some servers and generally derives from data layout + and how ZFS maintains the tree structure of blocks. A small application + write (such as a new log line) means a new empty data block allocation, + an old block release, and bubble up through the whole metadata tree to + complete the transaction (grouped as TXG to flush to disk). ++ +** One solution is to use discardable build workspaces in RAM-backed + storage like `/dev/shm` (`tmpfs`) on Linux, or `/tmp` (`swap`) on + illumos hosting systems, and only use persistent storage for the home + directory with `.ccache` and `.gitcache-dynamatrix` directories. +** Another solution is to reduce the frequency of TXG sync from modern + default of 5 sec to conservative 30-60 sec. Check how to set the + `zfs_txg_timeout` on your platform. + Connecting Jenkins to the containers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------------------ + +To properly cooperate with the +https://github.com/networkupstools/jenkins-dynamatrix[jenkins-dynamatrix] +project driving regular NUT CI builds, each build environment should be +exposed as an individual agent with labels describing its capabilities. -To cooperate with the jenkins-dynamatrix driving NUT CI builds, each build -environment should be exposed as an individual agent with labels describing -its capabilities. +Agent Labels +~~~~~~~~~~~~ -Labels -^^^^^^ +With the `jenkins-dynamatrix`, agent labels are used to calculate a large +"slow build" matrix to cover numerous scenarios for what can be tested +with the current population of the CI farm, across operating systems, +`make`, shell and compiler implementations and versions, and C/C++ language +revisions, to name a few common "axes" involved. + +Labels for QEMU +^^^^^^^^^^^^^^^ Emulated-CPU container builds are CPU-intensive, so for them we define as few capabilities as possible: here CI is more interested in checking how @@ -384,9 +446,9 @@ which is more efficient to test on native platforms. Still, we are interested in results from different compiler suites, so specify at least one version of each. -NOTE: Currently the NUT Jenkinsfile-dynamatrix only looks at various -`COMPILER` variants for this use-case, disregarding the versions and -just using one that the environment defaults to. +NOTE: Currently the NUT `Jenkinsfile-dynamatrix` only looks at various +`COMPILER` variants for `qemu-nut-builder` use-cases, disregarding the +versions and just using one that the environment defaults to. The reduced set of labels for QEMU workers looks like: @@ -398,8 +460,11 @@ COMPILER=GCC COMPILER=CLANG ARCH64=ppc64le ARCH_BITS=64 ------ +Labels for native builds +^^^^^^^^^^^^^^^^^^^^^^^^ + For contrast, a "real" build agent's set of labels, depending on -presence or known lack of some capabilities, looks like: +presence or known lack of some capabilities, looks something like this: ------ doc-builder nut-builder nut-builder:alldrv NUT_BUILD_CAPS=docs:man NUT_BUILD_CAPS=docs:all @@ -414,13 +479,19 @@ PYTHON=python2.7 PYTHON=python3.8 ------ Generic agent attributes -^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~ * Name: e.g. `ci-debian-altroot--jenkins-debian10-arm64` (note the pattern for "Conflicts With" detailed below) * Remote root directory: preferably unique per agent, to avoid surprises; e.g.: `/home/abuild/jenkins-nut-altroots/jenkins-debian10-armel` +** Note it may help that the system home directory itself is shared between + co-located containers, so that the `.ccache` or `.gitcache-dynamatrix` + are available to all builders with identical contents +** If RAM permits, the Jenkins Agent working directory may be placed in + a temporary filesystem not backed by disk (e.g. `/dev/shm` on modern + Linux distributions); roughly estimate 300Mb per executor for NUT builds. * Usage: "Only build jobs with label expressions matching this node" @@ -429,7 +500,7 @@ Generic agent attributes ** `PATH+LOCAL` => `/usr/lib/ccache` Where to run agent.jar -^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~ Depending on circumstances of the container, there are several options available to the NUT CI farm: @@ -438,8 +509,9 @@ available to the NUT CI farm: => the container may be exposed as a standalone host for direct SSH access (usually by NAT, exposing SSH on a dedicated port of the host; or by first connecting the Jenkins controller with the host as an SSH Build Agent, and - then calling SSH to the container as a prefix for running the agent), so - ultimately the build `agent.jar` would run in the container. + then calling SSH to the container as a prefix for running the agent; or + by using Jenkins Swarm agents), so ultimately the build `agent.jar` JVM + would run in the container. Filesystem for the `abuild` account may be or not be shared with the host. * Java can not run in the container (crashes on emulated CPU, or is too old @@ -454,18 +526,35 @@ available to the NUT CI farm: * Java is inefficient in the container (operations like un-stashing the source succeed but take minutes instead of seconds) => either of the above +Using Jenkins SSH Build Agents +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This is a typical use-case for tightly integrated build farms under common +management, where the Jenkins controller can log by SSH into systems which +act as its build agents. It injects and launches the `agent.jar` to execute +child processes for the builds, and maintains a tunnel to communicate. + Methods below involving SSH assume that you have configured a password-less -key authentication from the host machine to the `abuild` account in container. -This can be an `ssh-keygen` result posted into `authorized_keys`, or a trusted -key passed by a chain of ssh agents from a Jenkins credential for connection -to the container-hoster into the container. +key authentication from the host machine to the `abuild` account in each +guest build environment container. +This can be an `ssh-keygen` result posted into `authorized_keys`, or a +trusted key passed by a chain of ssh agents from a Jenkins Credential +for connection to the container-hoster into the container. +The private SSH key involved may be secured by a pass-phrase, as long as +your Jenkins Credential storage knows it too. +Note that for the approaches explored below, the containers are not +directly exposed for log-in from any external network. * For passing the agent through an SSH connection from host to container, so that the `agent.jar` runs inside the container environment, configure: ** Launch method: "Agents via SSH" -** Host, Credentials, Port: as suitable for accessing the container hoster +** Host, Credentials, Port: as suitable for accessing the container-hoster ++ +NOTE: The container-hoster should have accessed the guest container from + the account used for intermediate access, e.g. `abuild`, so that its + `.ssh/known_hosts` file would trust the SSH server on the container. ** Prefix Start Agent Command: content depends on the container name, but generally looks like the example below to report some info about @@ -485,18 +574,18 @@ ssh jenkins-debian10-amd64 '( java -version & uname -a ; getconf LONG_BIT; getco * The other option is to run the `agent.jar` on the host, for all the network and filesystem magic the agent does, and only execute shell steps in the container. The solution relies on overridden `sh` step - implementation in the jenkins-dynamatrix shared library that uses a + implementation in the `jenkins-dynamatrix` shared library that uses a magic `CI_WRAP_SH` environment variable to execute a pipe into the container. Such pipes can be `ssh` or `chroot` with appropriate host setup described above. + NOTE: In case of ssh piping, remember that the container's -`/etc/ssh/sshd_config` should `AcceptEnv *` and the SSH -server should be restarted after such change. + `/etc/ssh/sshd_config` should `AcceptEnv *` and the SSH + server should be restarted after such configuration change. ** Launch method: "Agents via SSH" -** Host, Credentials, Port: as suitable for accessing the container hoster +** Host, Credentials, Port: as suitable for accessing the container-hoster ** Prefix Start Agent Command: content depends on the container name, but generally looks like the example below to report some info about @@ -513,18 +602,48 @@ echo PING > /dev/tcp/jenkins-debian11-ppc64el/22 && * Node properties / Environment variables: -** `CI_WRAP_SH` => `ssh -o SendEnv='*' "jenkins-debian11-ppc64el" /bin/sh -xe ` +** `CI_WRAP_SH` => ++ +------ +ssh -o SendEnv='*' "jenkins-debian11-ppc64el" /bin/sh -xe +------ + +Using Jenkins Swarm Agents +^^^^^^^^^^^^^^^^^^^^^^^^^^ +This approach allows remote systems to participate in the NUT CI farm by +dialing in and so defining an agent. A single contributing system may be +running a number of containers or virtual machines set up following the +instructions above, and each of those would be a separate build agent. + +Such systems should be "dedicated" to contribution in the sense that +they should be up and connected for days, and sometimes tasks would land. + +Configuration files maintained on the Swarm Agent system dictate which +labels or how many executors it would expose, etc. Credentials to access +the NUT CI farm Jenkins controller to register as an agent should be +arranged with the farm maintainers, and currently involve a GitHub account +with Jenkins role assignment for such access, and a token for authentication. + +The https://github.com/networkupstools/jenkins-swarm-nutci[jenkins-swarm-nutci] +repository contains example code from such setup with a back-up server +experiment for the NUT CI farm, including auto-start method scripts for +Linux systemd and upstart, illumos SMF, and OpenBSD rcctl. Sequentializing the stress -^^^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Running one agent at a time +^^^^^^^^^^^^^^^^^^^^^^^^^^^ Another aspect of farm management is that emulation is a slow and intensive operation, so we can not run all agents and execute builds at the same time. -The current solution relies on https://github.com/jenkinsci/jenkins/pull/5764 -to allow build agents to conflict with each other -- one picks up a job from -the queue and blocks others from starting; when it is done, another may start. +The current solution relies on +https://github.com/jimklimov/conflict-aware-ondemand-retention-strategy-plugin +to allow co-located build agents to "conflict" with each other -- when one +picks up a job from the queue, it blocks neighbors from starting; when it +is done, another may start. Containers can be configured with "Availability => On demand", with shorter cycle to switch over faster (the core code sleeps a minute between attempts): @@ -539,3 +658,36 @@ cycle to switch over faster (the core code sleeps a minute between attempts): Also, the "executors" count should be reduced to the amount of compilers in that system (usually 2) and so avoid extra stress of scheduling too many emulated-CPU builds at once. + +Sequentializing the git cache access +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +As part of the `jenkins-dynamatrix` optional optimizations, the NUT CI +recipe invoked via `Jenkinsfile-dynamatrix` maintains persistent git +reference repositories that can be used to cache NUT codebase (including +the tested commits) and so considerably speed up workspace preparation +when running numerous build scenarios on the same agent. + +Such `.gitcache-dynamatrix` cache directories are located in the build +workspace location (unique for each agent), but on a system with numerous +containers these names can be symlinks pointing to a shared location. + +To avoid collisions with several executors updating the same cache with +new commits, critical access windows are sequentialized with the use of +https://github.com/jenkinsci/lockable-resources-plugin[Lockable Resources +plugin]. On the `jenkins-dynamatrix` side this is facilitated by labels: +------ +DYNAMATRIX_UNSTASH_PREFERENCE=scm-ws:nut-ci-src +DYNAMATRIX_REFREPO_WORKSPACE_LOCKNAME=gitcache-dynamatrix:SHARED_HYPERVISOR_NAME +------ + +* The `DYNAMATRIX_UNSTASH_PREFERENCE` tells the `jenkins-dynamatrix` library + code which checkout/unstash strategy to use on a particular build agent + (following values defined in the library; `scm-ws` means SCM caching + under the agent workspace location, `nut-ci-src` names the cache for + this project); +* The `DYNAMATRIX_REFREPO_WORKSPACE_LOCKNAME` specifies a semi-unique + string: it should be same for all co-located agents which use the same + shared cache location, e.g. guests on the same hypervisor; and it should + be different for unrelated cache locations, e.g. different hypervisors + and stand-alone machines. diff --git a/docs/nut.dict b/docs/nut.dict index 903e735ed4..db17ae2e87 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2911 utf-8 +personal_ws-1.1 en 2930 utf-8 AAS ABI ACFAIL @@ -498,6 +498,7 @@ JBus JKL JRE JSON +JVM JW Jageson Jarosch @@ -564,6 +565,7 @@ LISTRW LISTVARS LOADPCT LOCKFN +LOCKNAME LOTRANS LUA LVM @@ -943,6 +945,7 @@ RDNT RDWR README REDi +REFREPO REPLBATT REQSSL RESPIN @@ -1164,6 +1167,7 @@ TST TT TTT TXF +TXG TXV TXVxx TapSwDly @@ -1206,6 +1210,7 @@ UID UIDA UINT UNKCOMMAND +UNSTASH UNV UPGUARDS UPM @@ -1629,8 +1634,8 @@ crw csh cshdelay css -cts ctrl +cts ctypes cua cuaa @@ -1663,6 +1668,7 @@ decrement decrypt dedb dedup +deduplication defun dep dephasing @@ -1681,6 +1687,7 @@ dialout difftool dipsw dir +discardable disp distcheck distclean @@ -1839,6 +1846,7 @@ getent getenv getopt getvar +gitcache github gitignore gitk @@ -1866,8 +1874,8 @@ hal hardcoded hasFeature hb -hcl hcd +hcl hg hh hibernate's @@ -2087,6 +2095,7 @@ lxcbr lxccontainer lxml lxyz +lz mA mDNS mS @@ -2425,6 +2434,7 @@ raritan ratedva ratedwatts rb +rcctl readline readonly realpower @@ -2487,6 +2497,7 @@ salicru sbin sbindir scd +scm screenshot screenshots scriptname @@ -2509,6 +2520,7 @@ selftest sendback sendline sendmail +sequentialized ser seria serialno @@ -2528,6 +2540,7 @@ setvar's sfr sgml sgs +shm shutdownArguments shutdowndebounce shutdowndelay @@ -2688,6 +2701,7 @@ tiocm tios tmp tmpfiles +tmpfs toolchain toolkits topbot @@ -2713,6 +2727,7 @@ tuple turnon tw tx +txg txt typedef uA @@ -2744,6 +2759,7 @@ unmapped unmounts unpowered unshutup +unstash updateinfo upexia upower @@ -2864,9 +2880,11 @@ wmnut wordformat workflow workspace +workspaces writability writeinfo writeups +ws xAAAA xCC xD @@ -2902,6 +2920,7 @@ xxxxAP youruid yyy zaac +zfs zinto zlib zsh From 31f239663919fc6ca60e9f5d12f413209c57869d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 14:55:59 +0200 Subject: [PATCH 448/700] docs/config-notes.txt: reformat and extend the NOTE for USB drivers ignoring port option [#1368] --- docs/config-notes.txt | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/config-notes.txt b/docs/config-notes.txt index df4ac937a9..80d58aa7ca 100644 --- a/docs/config-notes.txt +++ b/docs/config-notes.txt @@ -146,19 +146,29 @@ A typical device without any extra settings looks like this: port = /dev/ttyS1 desc = "Workstation" -NOTE: USB drivers (usbhid-ups, bcmxcp_usb, tripplite_usb, blazer_usb and -richcomm_usb) are special cases and ignore the 'port' value. +[NOTE] +====== +USB drivers (such as `usbhid-ups` for non-SHUT mode, `nutdrv_qx` for +non-serial mode, `bcmxcp_usb`, `tripplite_usb`, `blazer_usb`, `riello_usb` +and `richcomm_usb`) are special cases and ignore the 'port' value. + You must still set this value, but it does not matter what you set -it to; a common and good practice is to set 'port' to *auto*, but you can -put whatever you like. If you only own one USB UPS, the driver will -find it automatically. If you own more than one, refer to the driver's -manual page for more information on matching a specific device. +it to; a common and good practice is to set 'port' to *auto*, but you +can put whatever you like. + +If you only own one USB UPS, the driver will find it automatically. + +If you own more than one, refer to the driver's manual page for more +information on matching a specific device. +====== References: linkman:ups.conf[5], linkman:nutupsdrv[8], linkman:bcmxcp_usb[8], linkman:blazer[8], +linkman:nutdrv_qx[8], linkman:richcomm_usb[8], +linkman:riello_usb[8], linkman:tripplite_usb[8], linkman:usbhid-ups[8] From b56409de7a2ed682bbf9312ff7f174b03d334440 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 15:20:15 +0200 Subject: [PATCH 449/700] docs/config-notes.txt: document ALLOW_NO_DEVICE for upsd.conf [#766, #837] --- docs/config-notes.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/config-notes.txt b/docs/config-notes.txt index 80d58aa7ca..179f75e187 100644 --- a/docs/config-notes.txt +++ b/docs/config-notes.txt @@ -330,6 +330,13 @@ is stale, then your ups.conf is not configured correctly, or you have a driver that isn't working properly. You must fix this before going on to the next step. +NOTE: Normally `upsd` requires that at least one driver section is +defined in the 'ups.conf' file, and refuses to start otherwise. +If you intentionally do not have any driver sections defined (yet) +but still want the data server to run, respond and report zero devices +(e.g. on an automatically managed monitoring deployment), you can enable +the `ALLOW_NO_DEVICE true` option in the 'upsd.conf' file. + On operating systems with service management frameworks, the data server life-cycle is managed by `nut-server` service. From ffe4181ba7201194f03152d51663ef23a8b7a910 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 16:55:26 +0200 Subject: [PATCH 450/700] docs/config-notes.txt: fix formatting, extend some notes and warnings, and service-driven life-cycle --- docs/config-notes.txt | 398 +++++++++++++++++++++++++----------------- 1 file changed, 235 insertions(+), 163 deletions(-) diff --git a/docs/config-notes.txt b/docs/config-notes.txt index 179f75e187..a87ae500ae 100644 --- a/docs/config-notes.txt +++ b/docs/config-notes.txt @@ -112,32 +112,35 @@ configure <>. image:images/simple.png[] -On operating systems with service management frameworks (such as Linux systemd -and Solaris/illumos SMF), the driver, data server and monitoring client daemons' -life-cycle is managed respectively by `nut-driver` (multi-instance), `nut-server` -and `nut-monitor` services. These are in turn wrapped by an "umbrella" service -(or systemd "target") conveniently called `nut` which allows to start or stop -those of the bundled services, which are enabled on a particular deployment. +On operating systems with service management frameworks (such as Linux +systemd and Solaris/illumos SMF), the life-cycle of driver, data server +and monitoring client daemons is managed respectively by `nut-driver` +(multi-instance service), `nut-server` and `nut-monitor` services. +These are in turn wrapped by an "umbrella" service (or systemd "target") +conveniently called `nut` which allows to easily start or stop all those +of the bundled services, which are enabled on a particular deployment. [[Driver_configuration]] Driver configuration ~~~~~~~~~~~~~~~~~~~~ -Create one section per UPS in ups.conf +Create one section per UPS in 'ups.conf' -NOTE: The default path for a source installation is `/usr/local/ups/etc`, while -packaged installation will vary. For example, `/etc/nut` is used on Debian and -derivatives, while `/etc/ups` or `/etc/upsd` is used on RedHat and derivatives. +NOTE: The default path for a source installation is `/usr/local/ups/etc`, +while packaged installation will vary. +For example, `/etc/nut` is used on Debian and derivatives, +while `/etc/ups` or `/etc/upsd` is used on RedHat and derivatives. -To find out which driver to use, check the <>, -or `data/driver.list`. +To find out which driver to use, check the +<>, +or `data/driver.list(.in)` source file. Once you have picked a driver, create a section for your UPS in -`ups.conf`. You must supply values for "driver" and "port". +'ups.conf'. You must supply values at least for "driver" and "port". Some drivers may require other flags or settings. The "desc" value is optional, but is recommended to provide a better description of -what your UPS is supporting. +what useful load your UPS is feeding. A typical device without any extra settings looks like this: @@ -195,16 +198,21 @@ reference, a successful start of the `usbhid-ups` driver looks like this: If the driver doesn't start cleanly, make sure you have picked the right one for your hardware. You might need to try other drivers -by changing the "driver=" value in ups.conf. +by changing the "driver=" value in 'ups.conf'. Be sure to check the driver's man page to see if it needs any extra -settings in `ups.conf` to detect your hardware. +settings in 'ups.conf' to detect your hardware. If it says `can't bind /var/state/ups/...` or similar, then your state path probably isn't writable by the driver. Check the -<>. +<> vs. the +user account your driver starts as. -After making changes, try the <> step again. +After making changes, try the <> +step again. + +Driver(s) as a service +~~~~~~~~~~~~~~~~~~~~~~ On operating systems with init-scripts managing life-cycle of the operating environment, the `upsdrvctl` program is also commonly used in those scripts. @@ -233,7 +241,7 @@ disable startup of drivers). In both cases, a service named `nut-driver-enumerator` is registered, and when it is (re-)started it scans the currently defined device sections in -`ups.conf` and the currently defined instances of `nut-driver` service, +'ups.conf' and the currently defined instances of `nut-driver` service, and brings them in sync (adding or removing service instances), and if there were changes -- it restarts the corresponding drivers (via service instances) as well as the data server which only reads the list of sections @@ -267,18 +275,19 @@ of such section names, and the `upsdrvsvcctl` script supports this to map the user-friendly NUT configuration section names to actual service names that it would manage. -References: man pages: linkman:nutupsdrv[8], linkman:upsdrvctl[8] +References: man pages: linkman:nutupsdrv[8], linkman:upsdrvctl[8], +linkman:upsdrvsvcctl[8] Data server configuration (upsd) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Configure upsd, which serves data from the drivers to the clients. +Configure `upsd`, which serves data from the drivers to the clients. -First, edit upsd.conf to allow access to your client systems. By -default, upsd will only listen to localhost port 3493/tcp. If you want +First, edit 'upsd.conf' to allow access to your client systems. By +default, `upsd` will only listen to `localhost` port 3493/tcp. If you want to connect to it from other machines, you must specify each interface you -want upsd to listen on for connections, optionally with a port number. +want `upsd` to listen on for connections, optionally with a port number. LISTEN 127.0.0.1 3493 LISTEN ::1 3493 @@ -286,15 +295,15 @@ want upsd to listen on for connections, optionally with a port number. NOTE: Refer to the NUT user manual <> for information on how to access and secure upsd clients connections. -Next, create upsd.users. For now, this can be an empty file. +Next, create 'upsd.users'. For now, this can be an empty file. You can come back and add more to it later when it's time to -configure upsmon or run one of the management tools. +configure `upsmon` or run one of the management tools. Do not make either file world-readable, since they both hold access control data and passwords. They just need to be readable by the user you created in the preparation process. -The suggested configuration is to chown it to root, chgrp it to the +The suggested configuration is to `chown` it to `root`, `chgrp` it to the group you created, then make it readable by the group. chown root:nut upsd.conf upsd.users @@ -321,12 +330,12 @@ A successful run looks like this: listening on ::1 port 3493 Connected to UPS [eaton]: usbhid-ups-eaton -upsd prints dots while it waits for the driver to respond. Your +`upsd` prints dots while it waits for the driver to respond. Your system may print more or less depending on how many drivers you have and how fast they are. -NOTE: if upsd says that it can't connect to a UPS or that the data -is stale, then your ups.conf is not configured correctly, or you +NOTE: If `upsd` says that it can't connect to a UPS or that the data +is stale, then your 'ups.conf' is not configured correctly, or you have a driver that isn't working properly. You must fix this before going on to the next step. @@ -349,6 +358,7 @@ Status data ^^^^^^^^^^^ Make sure that the UPS is providing good status data. +You can use the `upsc` command-line client for this: upsc myupsname@localhost ups.status @@ -356,8 +366,8 @@ You should see just one line in response: OL -OL means your system is running on line power. If it says something -else (like OB -- on battery, or LB -- low battery), your driver was +`OL` means your system is running on line power. If it says something +else (like `OB` -- on battery, or `LB` -- low battery), your driver was probably misconfigured during the <> step. If you reconfigure the driver, use `upsdrvctl stop` to stop it, then start it again as shown in the <> step. @@ -373,8 +383,8 @@ Look at all of the status data which is being monitored. upsc myupsname@localhost What happens now depends on the kind of device and driver you have. -In the list, you should see ups.status with the same value you got -above. A sample run on a UPS (Eaton Ellipse MAX 1100) looks like this: +In the list, you should see `ups.status` with the same value you got +above. A sample run on an UPS (Eaton Ellipse MAX 1100) looks like this: battery.charge: 100 battery.charge.low: 20 @@ -432,10 +442,14 @@ NOTE: This step is not necessary if you installed from packages. Edit your startup scripts, and make sure `upsdrvctl` and `upsd` are run every time your system starts. In newer versions of NUT, you may have a -`nut.conf` file which sets the `MODE` variable for bundled init-scripts, +'nut.conf' file which sets the `MODE` variable for bundled init-scripts, to facilitate enabling of certain features in the specific end-user deployments. +If you installed from source, check the `scripts` directory for reference +init-scripts, as well as systemd or SMF service methods and manifests. + + [[UPS_shutdown]] Configuring automatic shutdowns for low battery events ------------------------------------------------------ @@ -452,53 +466,78 @@ Shutdown design When your UPS batteries get low, the operating system needs to be brought down cleanly. Also, the UPS load should be turned off so that all devices -that are attached to it are forcibly rebooted. +that are attached to it are forcibly rebooted, and subsequently start in +the predictable order and state suitable for your data center. -Here are the steps that occur when a critical power event happens: +Here are the steps that occur when a critical power event happens, +for the simpler case of one UPS device feeding one or several systems: 1. The UPS goes on battery -2. The UPS reaches low battery (a "critical" UPS), that is to say - upsc displays: +2. The UPS reaches low battery (a "critical" UPS), that is to say, + `upsc` displays: + ups.status: OB LB + -The exact behavior depends on the specific device, and is related to: +The exact behavior depends on the specific device, and is related to +such settings and readings as: - - battery.charge and battery.charge.low - - battery.runtime and battery.runtime.low + - `battery.charge` and `battery.charge.low` + - `battery.runtime` and `battery.runtime.low` -3. The upsmon primary notices and sets "FSD" -- the "forced shutdown" - flag to tell all secondary systems that it will soon power down - the load. +3. The `upsmon` primary notices the "critical UPS" situation and sets + "FSD" -- the "forced shutdown" flag to tell all secondary systems + that it will soon power down the load. ++ +[WARNING] +========= +By design, since we require power-cycling the load and don't +want some systems to be powered off while others remain running +if the "wall power" returns at the wrong moment as usual, the "FSD" +flag can not be removed from the data server unless its daemon is +restarted. If we do take the first step in critical mode, then we +intend to go all the way -- shut down all the servers gracefully, +and power down the UPS. + +Keep in mind that some UPS devices and corresponding drivers would +latch the "FSD" again even if "wall power" is available, but the +remaining battery charge is below a threshold configured as "safe" +in the device (usually if you manually power on the UPS after a long +power outage). This is by design of respective UPS vendors, since +in such situation they can not guarantee that if a new power outage +happens, their UPS would safely shut down your systems again. +So it is deemed better and safer to stay dark until batteries +become sufficiently charged. +========= + (If you have no secondary systems, skip to step 6) -4. upsmon secondary systems see "FSD" and: +4. `upsmon` secondary systems see "FSD" and: - - generate a NOTIFY_SHUTDOWN event - - wait FINALDELAY seconds -- typically 5 - - call their SHUTDOWNCMD - - disconnect from upsd + - generate a `NOTIFY_SHUTDOWN` event + - wait `FINALDELAY` seconds -- typically `5` + - call their `SHUTDOWNCMD` + - disconnect from `upsd` -5. The upsmon primary system waits up to HOSTSYNC seconds (typically 15) - for the secondary systems to disconnect from upsd. If any are still - connected after this time, upsmon primary stops waiting and proceeds +5. The `upsmon` primary system waits up to `HOSTSYNC` seconds (typically `15`) + for the secondary systems to disconnect from `upsd`. If any are still + connected after this time, `upsmon` primary stops waiting and proceeds with the shutdown process. -6. The upsmon primary: +6. The `upsmon` primary: - - generates a NOTIFY_SHUTDOWN event - - waits FINALDELAY seconds -- typically 5 - - creates the POWERDOWNFLAG file -- usually `/etc/killpower` - - calls the SHUTDOWNCMD + - generates a `NOTIFY_SHUTDOWN` event + - waits `FINALDELAY` seconds -- typically `5` + - creates the `POWERDOWNFLAG` file in its local filesystem -- + usually `/etc/killpower` + - calls the `SHUTDOWNCMD` -7. On most systems, init takes over, kills your processes, syncs and +7. On most systems, `init` takes over, kills your processes, syncs and unmounts some filesystems, and remounts some read-only. -8. init then runs your shutdown script. This checks for the - POWERDOWNFLAG, finds it, and tells the UPS driver(s) to power off - the load. +8. `init` then runs your shutdown script. This checks for the + `POWERDOWNFLAG`, finds it, and tells the UPS driver(s) to power off + the load by sending commands to the connected UPS device(s) they manage. 9. All the systems lose power. @@ -506,6 +545,16 @@ The exact behavior depends on the specific device, and is related to: 11. All systems reboot and go back to work. +/////////////////////////////////// +https://github.com/networkupstools/nut/issues/1370 + +TODO: Check other docs and code to spell out expected behavior with +multiple UPS devices (when not all of them go critical or even on battery) +and servers with multiple inputs. + +Does the `upsmon` primary system power-cycle a "critical" UPS if that +is not the only one feeding it, so it is not shutting down now? +/////////////////////////////////// How you set it up ~~~~~~~~~~~~~~~~~ @@ -514,11 +563,13 @@ How you set it up NUT user creation ^^^^^^^^^^^^^^^^^ -Create a upsd user for upsmon to use while monitoring this UPS. +Create a `upsd` user for `upsmon` to use while monitoring this UPS. -Edit upsd.users and create a new section. upsmon will connect -to upsd and use this user name (in brackets) and password to -authenticate. This example is for a user called "monuser": +Edit 'upsd.users' and create a new section. The `upsmon` will connect +to `upsd` and use these user name (in brackets) and password to +authenticate (as specified in its configuration via `MONITOR` line). + +This example is for defining a user called "monuser": [monuser] password = mypass @@ -530,8 +581,8 @@ References: linkman:upsd[8], linkman:upsd.users[5] Reloading the data server ^^^^^^^^^^^^^^^^^^^^^^^^^ -Reload upsd. Depending on your configuration, you may be able to -do this without stopping upsd: +Reload `upsd`. Depending on your configuration, you may be able to +do this without stopping the `upsd` daemon process: upsd -c reload @@ -540,16 +591,20 @@ If that doesn't work (check the syslog), just restart it: upsd -c stop upsd -NOTE: if you want to make reloading work later, see the entry in the -link:FAQ.html[FAQ] about starting upsd as a different user. +For systems with integrated service management (Linux systemd, +illumos/Solaris SMF) their corresponding `reload` or `refresh` +service actions should handle this as well. + +NOTE: If you want to make reloading work later, see the entry in the +link:FAQ.html[FAQ] about starting `upsd` as a different user. Power Off flag file ^^^^^^^^^^^^^^^^^^^ -Set the POWERDOWNFLAG location for upsmon. +Set the `POWERDOWNFLAG` location for `upsmon`. -In upsmon.conf, add a POWERDOWNFLAG directive with a filename. -upsmon will create this file when the UPS needs to be powered off +In 'upsmon.conf', add a `POWERDOWNFLAG` directive with a filename. +The `upsmon` will create this file when the UPS needs to be powered off during a power failure when low battery is reached. We will test for the presence of this file in a later step. @@ -562,27 +617,27 @@ linkman:upsmon.conf[5] Securing upsmon.conf ^^^^^^^^^^^^^^^^^^^^ -The recommended setting is to have it owned by root:nut, then make it readable -by the group and not world. This file contains passwords that could be used by -an attacker to start a shutdown, so keep it secure. +The recommended setting is to have it owned by `root:nut`, then make it +readable by the group and not by the world. This file contains passwords +that could be used by an attacker to start a shutdown, so keep it secure. chown root:nut upsmon.conf chmod 0640 upsmon.conf -This step has been placed early in the process so you secure this file before -adding sensitive data in the next step. +This step has been placed early in the process so you secure this file +before adding sensitive data in the next step. Create a MONITOR directive for upsmon ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Edit upsmon.conf and create a MONITOR line with the UPS definition +Edit 'upsmon.conf' and create a `MONITOR` line with the UPS definition (@), username and password from the <> step, and the "primary" or "secondary" setting. -If this system is the UPS manager (i.e., it's connected to this UPS directly -and can manage it using a suitable NUT driver), its upsmon is the primary: +If this system is the UPS manager (i.e. it's connected to this UPS directly +and can manage it using a suitable NUT driver), its `upsmon` is the primary: MONITOR myupsname@mybox 1 monuser mypass primary @@ -591,31 +646,33 @@ system is the primary, then this one is a secondary: MONITOR myupsname@mybox 1 monuser mypass secondary -The number "1" here is the power value. This should always be set -to 1 unless you have a very special (read: expensive) system with +The number `1` here is the "power value". This should always be set +to 1, unless you have a very special (read: expensive) system with redundant power supplies. In such cases, refer to the User Manual: - <>, - <>. -Note that the power value may also be 0 for a monitoring system which -only observes the UPS status but is not impacted by its power events, -and so does not shut down when the UPS does. +Note that the "power value" may also be 0 for a monitoring (administrative) +system which only observes the remote UPS status but is not impacted by its +power events, and so does not shut down when the UPS does. References: linkman:upsmon[8], linkman:upsmon.conf[5] Define a SHUTDOWNCMD for upsmon ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Still in upsmon.conf, add a directive that tells upsmon how to shut down your -system. This example seems to work on most systems: +Still in 'upsmon.conf', add a directive that tells `upsmon` how to +shut down your system. This example seems to work on most systems: SHUTDOWNCMD "/sbin/shutdown -h +0" Notice the presence of "quotes" here to keep it together. -If your system has special needs, you may want to set this to a script which -does local shutdown tasks before calling init. +If your system has special needs (e.g. system-provided shutdown handler +is ungracefully time constrained), you may want to set this to a script +which does customized local shutdown tasks before calling `init` or +`shutdown` programs to handle the system side of this operation. Start upsmon @@ -631,8 +688,8 @@ life-cycle is managed by `nut-monitor` service. Checking upsmon ^^^^^^^^^^^^^^^ -Look for messages in the syslog to indicate success. It should look something -like this: +Look for messages in the `syslog` to indicate success. +It should look something like this: May 29 01:11:27 mybox upsmon[102]: Startup successful May 29 01:11:28 mybox upsd[100]: Client monuser@192.168.50.1 @@ -649,10 +706,12 @@ NOTE: This step is not need if you installed from packages. Edit your startup scripts, and add a call to `upsmon`. -Make sure `upsmon` starts when your system comes up. Do it after `upsdrvctl` -and `upsd`, or it will complain about not being able to contact the server. +Make sure `upsmon` starts when your system comes up. +On systems with `upsmon` primary (also running the data server), +do it after `upsdrvctl` and `upsd`, or it will complain about not +being able to contact the server. -You may delete the POWERDOWNFLAG in the startup scripts, but it is not +You may delete the `POWERDOWNFLAG` in the startup scripts, but it is not necessary. `upsmon` will clear that file for you when it starts. NOTE: Init script examples are provide in the 'scripts' directory of @@ -666,13 +725,14 @@ NOTE: This step is not need if you installed from packages. Edit your shutdown scripts, and add `upsdrvctl shutdown`. -You should configure your system to power down the UPS after the filesystems -are remounted read-only. Have it look for the presence of the POWERDOWNFLAG -(from linkman:upsmon.conf[5]), using this as an example: +You should configure your system to power down the UPS after the +filesystems are remounted read-only. Have it look for the presence +of the `POWERDOWNFLAG` (from linkman:upsmon.conf[5]), using this +as an example: ------------------------------------------------------------------------------ - if (test -f /etc/killpower) + if (/sbin/upsmon -K) then echo "Killing the power, bye!" /sbin/upsdrvctl shutdown @@ -688,13 +748,20 @@ are remounted read-only. Have it look for the presence of the POWERDOWNFLAG [WARNING] ============================================================================== -- Be careful that upsdrvctl command will probably power off your machine. +- Be careful that `upsdrvctl shutdown` command will probably power off +your machine and others fed by the UPS(es) which it manages. Don't use it unless your system is ready to be halted by force. If you run RAID, read the <<_raid_warning,RAID warning>> below! -- Make sure the filesystem(s) containing upsdrvctl, ups.conf and your UPS -driver(s) are mounted (possibly in read-only mode) when the system gets to +- Make sure the filesystem(s) containing `upsdrvctl`, `upsmon`, +the `POWERDOWNFLAG` file, 'ups.conf' and your UPS driver(s) are +mounted (possibly in read-only mode) when the system gets to this point. Otherwise it won't be able to figure out what to do. + +- If for some reason you can not ensure `upsmon` program is executable +at this point, your script can `(test -f /etc/killpower)` in a somewhat +non-portable manner, instead of asking `upsmon -K` for the verdict +according to its current configuration. ============================================================================== @@ -708,8 +775,8 @@ on your systems before leaving them unattended. A successful sequence is one where the OS halts before the battery runs out, and the system restarts when power returns. -The first step is to see how upsdrvctl will behave without actually -turning off the power. To do so, use the '-t' argument: +The first step is to see how `upsdrvctl` will behave without actually +turning off the power. To do so, use the `-t` argument: upsdrvctl -t shutdown @@ -749,35 +816,38 @@ is turned off and waits for the power to return. Once the power is back, the system reboots, pulls the snapshot back in, and keeps going from there. If the user happened to be away when it happened, they may return and have no idea that their system actually -shut down completely in the middle. +shut down completely in the middle (although network connections will drop). -In order for this to work, you need to shutdown NUT (UPS driver, upsd -server and upsmon client) in the suspend script and start them again in -the resume script. Don't try to keep them running. The upsd server +In order for this to work, you need to shutdown NUT (UPS driver, `upsd` +server and `upsmon` client) in the `suspend` script and start them again in +the `resume` script. Don't try to keep them running. The `upsd` server will latch the FSD state (so it won't be usable after resuming) and so -will the upsmon client. Some drivers may work after resuming, but many -don't and some UPSs will require re-initialization, so it's best not -to keep this running either. +will the `upsmon` client. Some drivers may work after resuming, but many +don't and some UPS devices will require re-initialization, so it's best not +to keep them running either. + +After stopping NUT driver, server and client you'll have to send the UPS +the command to shutdown only if the `POWERDOWNFLAG` is present. Note +that most likely you'll have to allow for a grace period after calling +`upsdrvctl shutdown` since the system will still have to take a +snapshot of itself after that. Not all drivers and devices support this, +so before going down this road, make sure that the one you're using does. -After stopping driver, server and client you'll have to send the UPS -the command to shutdown only if the POWERDOWNFLAG is present. Note -that most likely you'll have to allow for a grace period after sending -'upsdrvctl shutdown' since the system will still have to take a -snapshot of itself after that. Not all drivers support this, so before -going down this road, make sure that the one you're using does. +- see if you can query or configure settings named like `load.off.delay`, + `ups.delay.shutdown`, `offdelay` and/or `shutdown_delay` RAID warning ~~~~~~~~~~~~ -If you run any sort of RAID equipment, make sure your arrays are either halted -(if possible) or switched to "read-only" mode. Otherwise you may suffer a long -resync once the system comes back up. +If you run any sort of RAID equipment, make sure your arrays are +either halted (if possible) or switched to "read-only" mode. +Otherwise you may suffer a long resync once the system comes back up. -The kernel may not ever run its final shutdown procedure, so you must take care -of all array shutdowns in userspace before upsdrvctl runs. +The kernel may not ever run its final shutdown procedure, so you must take +care of all array shutdowns in userspace before `upsdrvctl shutdown` runs. -If you use software RAID (md) on Linux, get mdadm and try using -'mdadm --readonly' to put your arrays in a safe state. This has to +If you use software RAID (md) on Linux, get `mdadm` and try using +`mdadm --readonly` to put your arrays in a safe state. This has to happen after your shutdown scripts have remounted the filesystems. On hardware RAID or other kernels, you have to do some detective work. It may @@ -799,8 +869,8 @@ be configured using some general descriptions. There are two main elements: -1. There's a UPS attached to a communication (serial, USB or network) port on -this system. +1. There's a UPS attached to a communication (serial, USB or network) port + on this system. 2. This system depends on a UPS for power. You can play "mix and match" with those two to arrive at these descriptions @@ -810,36 +880,37 @@ for individual hosts: - B: 2 but not 1 - C: 1 and 2 -A small to medium sized data room usually has one C and a bunch of Bs. -This means that there's a system (type C) hooked to the UPS which depends -on it for power. There are also some other systems in there (type B) +A small to medium sized data room usually has one 'C' and a bunch of 'Bs'. +This means that there's a system (type 'C') hooked to the UPS which depends +on it for power. There are also some other systems in there (type 'B') which depend on that same UPS for power, but aren't directly connected to -it. +it communications-wise. Larger data rooms or those with multiple UPSes may have several "clusters" -of the "single C, many Bs" depending on how it's all wired. +of the "single 'C', many 'Bs'" depending on how it's all wired. -Finally, there's a special case. Type A systems are connected to a UPS's -serial port, but don't depend on it for power. This usually happens when -a UPS is physically close to a box and can reach the serial port, but -the wiring is such that it doesn't actually feed it. +Finally, there's a special case. Type 'A' systems are connected to +an UPS's communication port, but don't depend on it for power. +This usually happens when an UPS is physically close to a box and can +reach the serial port, but the power wiring is such that it doesn't +actually feed that box. Once you identify a system's type, use this list to decide which of the programs need to be run for monitoring: -- A: driver and upsd -- B: upsmon (in secondary mode) -- C: driver, upsd, and upsmon (in primary mode, as the UPS manager) +- A: driver and `upsd` +- B: `upsmon` (in secondary mode) +- C: driver, `upsd`, and `upsmon` (in primary mode, as the UPS manager) + +image:images/advanced.png[] To further complicate things, you can have a system that is hooked to multiple UPSes, but only depends on one for power. This particular -situation makes it an "A" relative to one UPS, and a "C" relative to the +situation makes it an `A` relative to one UPS, and a `C` relative to the other. The software can handle this -- you just have to tell it what to do. NOTE: NUT can also serve as a data proxy to increase the number of clients, -or share the communication load between several upsd instances. - -image:images/advanced.png[] +or share the communication load between several `upsd` instances. If you are running large server-class systems that have more than one power feed, see the next section for information on how to handle it @@ -849,8 +920,8 @@ properly. Typical setups for big servers with UPS redundancy -------------------------------------------------- -By using multiple MONITOR statements in upsmon.conf, you can configure an -environment where a large machine with redundant power monitors multiple +By using multiple `MONITOR` statements in 'upsmon.conf', you can configure +an environment where a large machine with redundant power monitors multiple separate UPSes. image:images/bigbox.png[] @@ -867,29 +938,30 @@ Two UPSes, 'Alpha' and 'Beta', are each driving two of the power supplies This means that either 'Alpha' *or* 'Beta' can totally shut down and the server will be able to keep running. -The upsmon.conf configuration that reflect this is the following: +The 'upsmon.conf' configuration which reflects this is the following: MONITOR ups-alpha@myhost 2 monuser mypass primary - MONITOR ups-beta@myhost 2 monuser mypass primary + MONITOR ups-beta@myhost 2 monuser mypass primary MINSUPPLIES 2 -With that configuration, upsmon will only shut down when both UPS reaches -a critical (on battery + low battery) condition, since 'Alpha' and 'Beta' -provide the same power value. +With such configuration, `upsmon` on this system will only shut down when +both UPS devices reach a critical (on battery + low battery) condition, +since 'Alpha' and 'Beta' each provide the same power value. As an added bonus, this means you can move a running server from one UPS to another (for maintenance purpose for example) without bringing it down since the minimum sufficient power will be provided at all times. -The MINSUPPLIES line tells upsmon that we need at least 2 power supplies +The `MINSUPPLIES` line tells `upsmon` that we need at least 2 power supplies to be receiving power from a good UPS (on line or on battery, just not -on battery and low battery). +on battery *and* low battery). -NOTE: we could have used a 'Power Value' of 1 for both UPS, and MINSUPPLIES -set to 1 too. These values are purely arbitrary, so you are free to use your -own rules. Here, we have linked these values to the number of power supplies -that each UPS is feeding (2) since this maps better to physical topology and -allows to throw a third or fourth UPS into the mix without much headache. +NOTE: We could have used a 'Power Value' of `1` for both UPS, and have +`MINSUPPLIES` set to `1` too. These values are purely arbitrary, so +you are free to use your own rules. Here, we have linked these values +to the number of power supplies that each UPS is feeding (2) since this +maps better to physical topology and allows to throw a third or fourth +UPS into the mix without much configuration headache. Multiple UPS shutdowns ordering @@ -897,11 +969,11 @@ Multiple UPS shutdowns ordering If you have multiple UPSes connected to your system, chances are that you need to shut them down in a specific order. The goal is to shut down -everything but the one keeping upsmon alive at first, then you do that one -last. +everything but the one keeping `upsmon` alive at first, then you do that +one last. To set the order in which your UPSes receive the shutdown commands, define -the 'sdorder' value in your ups.conf. +the `sdorder` value in your 'ups.conf' device sections. [bigone] driver = usbhid-ups @@ -919,11 +991,11 @@ the 'sdorder' value in your ups.conf. sdorder = 0 The order runs from 0 to the highest number available. So, for this -configuration, the order of shutdowns would be 'misc', 'littleguy', and then -'bigone'. +configuration, the order of shutdowns would be 'misc', 'littleguy', +and then 'bigone'. -NOTE: If you have a UPS that shouldn't be shutdown when running 'upsdrvctl -shutdown', set the *sdorder* to *-1*. +NOTE: If you have a UPS that shouldn't be powered off when running +`upsdrvctl shutdown`, set its `sdorder` to `-1`. Other redundancy configurations From efab99d71031a8ba2b3e3c0ba00935a898675574 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 17:08:22 +0200 Subject: [PATCH 451/700] docs/solaris-usb.txt: add a NOTE for USB drivers ignoring port option [#1368] --- docs/solaris-usb.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/solaris-usb.txt b/docs/solaris-usb.txt index ee658805b8..19cc408309 100644 --- a/docs/solaris-usb.txt +++ b/docs/solaris-usb.txt @@ -170,7 +170,13 @@ usb8/1 connected configured ok Usually the driver mapping should set up the "friendly" device nodes under `/dev/` tree as well (symlinks to real entries in `/devices/`) so for NUT drivers you would specify a `port=/dev/usb/463.ffff/0` for -your new `driver=usbhid-ups` section. +your new driver section in `ups.conf`. + +NOTE: As detailed in link:config-notes.txt[], the "natively USB" drivers +(including `usbhid-ups` and `nutdrv_qx`) match the device by ID and/or +strings it reports, and so effectively require but ignore the `port` +option -- so it is commonly configured as `port=auto`. +Drivers used for SHUT or serial protocols do need the device path. For some serial-to-USB converter chips however it was noted that while the device driver is attached, and the `/device/...` path is exposed From b97fba7686efe3846c71f79417fb1a152dc2be4e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 17:08:45 +0200 Subject: [PATCH 452/700] docs/solaris-usb.txt: fix formatting, extend some notes --- docs/solaris-usb.txt | 58 ++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/docs/solaris-usb.txt b/docs/solaris-usb.txt index 19cc408309..6e8de528d9 100644 --- a/docs/solaris-usb.txt +++ b/docs/solaris-usb.txt @@ -26,11 +26,14 @@ Connect the power device using its USB port to your computer. Run `prtconf -v | less` to see the details of device connections, and search for its probable strings (vendor, model, serial number). -Two examples follow: -In this example, no suitable driver was attached "out of the box": +Two examples follow: -```` +* In this example, no suitable driver was attached "out of the box": ++ +---- +:; prtconf -v +... input (driver not attached) Hardware properties: name='driver-minor' type=int items=1 @@ -64,15 +67,17 @@ In this example, no suitable driver was attached "out of the box": value=00000002 name='assigned-address' type=int items=1 value=00000003 -```` +---- -In the following example, a "hid power" driver was attached, giving +* In the following example, a "hid power" driver was attached, giving some usability to the device although not enough for NUT to interact well (at least, according to the helpful notes in the https://web.archive.org/web/20140126045707/http://barbz.com.au/blog/?p=407 blog entry): - -```` ++ +---- +:; prtconf -v +... input, instance #1 Driver properties: name='pm-components' type=string items=3 dev=none @@ -114,34 +119,34 @@ blog entry): dev_path=/pci@0,0/pci8086,7270@1d/hub@1/input@3:hid_0_1 spectype=chr type=minor dev_link=/dev/usb/hid0 -```` +---- You can also check with `cfgadm` if the device is at least somehow visible (if not, there can be hardware issues in play). For example, if there is a physical link but no recognized driver was attached, the device would show up as "unconfigured": -```` -# cfgadm | grep usb- +---- +:; cfgadm | grep usb- usb8/1 usb-input connected unconfigured ok -```` +---- If you conclude that a change is needed, you would need to unload the existing copy of the "ugen" driver and set it up to handle the -device patterns that you find in 'compatible' values from `prtconf`, -e.g. for monitoring the devices from listings above: +device patterns that you find in 'compatible' values from `prtconf`. +For example, to monitor the devices from listings above, you would run: -```` -rem_drv ugen -add_drv -i '"usb463,ffff.100"' -m '* 0666 root sys' ugen -```` +---- +:; rem_drv ugen +:; add_drv -i '"usb463,ffff.100"' -m '* 0666 root sys' ugen +---- or -```` -rem_drv ugen -add_drv -i '"usb665,5161.2"' -m '* 0666 root sys' ugen -```` +---- +:; rem_drv ugen +:; add_drv -i '"usb665,5161.2"' -m '* 0666 root sys' ugen +---- Note that there are many patterns in the 'compatible' line which allow for narrower or wider catchment. It is recommended to match @@ -158,14 +163,14 @@ this access to the account that the NUT driver would run as. After proper driver binding, `cfgadm` should expose the details: -```` +---- # cfgadm -lv ... usb8/1 connected configured ok Mfg: EATON Product: Eaton 9PX NConfigs: 1 Config: 0 unavailable usb-input n /devices/pci@0,0/pci103c,1309@1d,2:1 ... -```` +---- Usually the driver mapping should set up the "friendly" device nodes under `/dev/` tree as well (symlinks to real entries in `/devices/`) @@ -184,10 +189,10 @@ in the `dmesg` output (saved to `/var/adm/messages`) the `/dev/...` symlinks are not created. In this case you can pass the low-level name of the character-device node as the "port" option, e.g.: -```` +---- ./mge-shut -s 9px-ser -DDDDD -d2 -u root \ -x port=/devices/pci@0,0/pci103c,1309@1a,2/device@1:0 -```` +---- libusb version and binary ------------------------- @@ -228,5 +233,6 @@ with code which was successfully used when developing this documentation: * https://github.com/OpenIndiana/oi-userland/pull/5382 * (TO CHECK) https://github.com/OpenIndiana/oi-userland/pull/5277 -Binaries from builds made in OpenIndiana using the recipe from PR #5382 +Binaries from builds made in OpenIndiana using the recipe from +https://github.com/OpenIndiana/oi-userland/pull/5382[PR #5382] above were successfully directly used on contemporary OmniOS CE as well. From c25b30bac625fa0c7ef9ac7274e974b961ecaee7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 17:14:08 +0200 Subject: [PATCH 453/700] docs/solaris-usb.txt: drop suggestions about building special libusb-1.0/0.1 branches --- docs/solaris-usb.txt | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/docs/solaris-usb.txt b/docs/solaris-usb.txt index 6e8de528d9..1eea303069 100644 --- a/docs/solaris-usb.txt +++ b/docs/solaris-usb.txt @@ -209,23 +209,16 @@ as an UPS), especially when using USB hubs and chips where hardware vendors had cut a few corners too many, which were addressed in a newer rewrite of the library as libusb-1.0. -Subsequently as at least the illumos-based distributions evolved to +Subsequently just as at least the illumos-based distributions evolved to include the new library and certain patches for it, and the library itself matured, the NUT project also added an ability to build with -libusb-1.0 either directly or using its 0.1-compat API. +libusb-1.0 either directly or using its 0.1-compat API (available since +NUT 2.8.0 release). -Currently this is not part of the common codebase and thus tagged -releases, but is experimented in several competing GitHub branches -until one gets chosen as the best to integrate: - -* https://github.com/networkupstools/nut/issues/300 - Please port to libusb 1.0 #300 -* https://github.com/networkupstools/nut/tree/libusb-1.0 -* https://github.com/networkupstools/nut/tree/libusb-1.0+0.1 -* https://github.com/networkupstools/nut/tree/libusb-compat-1.0 - -If your "standard" build of NUT has problems connecting to your -USB UPS, consider building one of those branches using the recent -library available for your distribution. +If your "standard" build of NUT has problems connecting to your USB UPS +(libusb binary variant should be visible in driver debug messages), +consider re-building NUT from source with the other option using the +recent library build as available for your distribution. In this context, note the OpenIndiana libusb-1 package pull requests with code which was successfully used when developing this documentation: From c96a60aa0ef9e4b9416dced4076922bac10765be Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 16:28:16 +0000 Subject: [PATCH 454/700] docs/Makefile.am: have "images/" in A2X_OUTDIR (fallout of #1152) Closes: #1365 --- docs/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Makefile.am b/docs/Makefile.am index 1b93081736..21f97e7878 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -197,6 +197,7 @@ DOCBUILD_BEGIN = { \ rm -rf "./$${A2X_OUTDIR}" || true ; \ test -d "$@" && rm -rf "$@" || true ; \ mkdir -p "./$${A2X_OUTDIR}" || exit ; \ + ln -s ../../images "./$${A2X_OUTDIR}" ; \ else A2X_OUTDIR='.' ; fi; \ if test -s "${builddir}/docbook-xsl.css" \ && test -r "${builddir}/docbook-xsl.css" \ From ceb4c280f79c9ddcec0b7a25f40ce7957f0d641f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 16:27:29 +0000 Subject: [PATCH 455/700] docs/Makefile.am: A2X_COMMON_OPTS: use "--attribute=..." consistently --- docs/Makefile.am | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/Makefile.am b/docs/Makefile.am index 21f97e7878..fd825cdfeb 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -162,15 +162,16 @@ solaris-usb.html solaris-usb.chunked solaris-usb.pdf: solaris-usb.txt asciidoc.c # generating the chunked HTML. In this case, export the environment # variable ASCIIDOC_VERBOSE to "-v", ie: # $ ASCIIDOC_VERBOSE=-v make -A2X_COMMON_OPTS = $(ASCIIDOC_VERBOSE) --attribute icons \ +A2X_COMMON_OPTS = $(ASCIIDOC_VERBOSE) \ + --attribute=icons \ --xsltproc-opts="--nonet" \ --xsltproc-opts="--stringparam nut.localdate \"`TZ=UTC date +%Y-%m-%d`\"" \ --xsltproc-opts="--stringparam nut.localtime \"`TZ=UTC date +%H:%M:%S`\"" \ --xsltproc-opts="--stringparam nut.nutversion \"@PACKAGE_VERSION@\"" \ - --attribute iconsdir=$(srcdir)/images \ + --attribute=iconsdir="$(srcdir)/images" \ --attribute=badges \ --attribute=external_title \ - --attribute tree_version=@TREE_VERSION@ \ + --attribute=tree_version="@TREE_VERSION@" \ -a toc -a numbered --destination-dir=$${A2X_OUTDIR} # NOTE: a2x newer than 8.6.8 says "--destination-dir" is only valid for HTML. # As of version 8.6.9 it lies, and the argument is required for our distcheck From b9ebe49137d5efb614aaf4f99f953c3336ff869f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 18:43:22 +0200 Subject: [PATCH 456/700] docs/config-prereqs.txt: fix formatting, extend some notes --- docs/config-prereqs.txt | 241 +++++++++++++++++++++++----------------- 1 file changed, 137 insertions(+), 104 deletions(-) diff --git a/docs/config-prereqs.txt b/docs/config-prereqs.txt index 405160f258..45b1058430 100644 --- a/docs/config-prereqs.txt +++ b/docs/config-prereqs.txt @@ -34,29 +34,32 @@ General call to Test the ability to configure and build Check out from git, generate files and configure to tailor to your build environment, and build some tests: ----- +------ :; mkdir -p nut && cd nut && \ - git clone https://github.com/networkupstools/nut/ -b fightwarn . + git clone https://github.com/networkupstools/nut/ -b master . :; ./autogen.sh && \ ./configure --with-doc=all --with-all --with-cgi && \ make all && make check && make spellcheck ----- +------ -NOTE: You can toggle some build options to check different dependency +You can toggle some `configure` options to check different dependency variants, e.g. `--with-ssl=nss` vs. `--with-ssl=openssl` -NOTE: For reproducible runs of various pre-sets of configuration during +For reproducible runs of various pre-sets of configuration during development, take a look at `ci_build.sh` script and different `BUILD_TYPE` (and other) environment variable settings that it supports. A minimal run -with it is just: -+ ----- +with it is just to call the script, e.g.: + +------ :; mkdir -p nut && cd nut && \ git clone https://github.com/networkupstools/nut/ -b fightwarn . :; ./ci_build.sh ----- +------ -NOTE: To build older releases, such as "vanilla" NUT 2.7.4, you may need to: +[NOTE] +====== +To build older releases, such as "vanilla" NUT 2.7.4 and older, +you may need to address some nuances: * Ensure that `python` in `PATH` points to a python-2.x implementation (`master` branch is fixed to work with python 2 and 3) @@ -69,29 +72,48 @@ NOTE: To build older releases, such as "vanilla" NUT 2.7.4, you may need to: tested by CI with Sun dmake and BSD make as well, so recipes should not expect GNU-only syntax and constructs to work -* For intensive rebuilds, `ccache` is recommended +* Parallel builds should be okay in current development version and + since NUT 2.8.0 (is a bug to log and fix, if not), but they may be + failure-prone in 2.7.4 and earlier releases +====== + +For intensive rebuilds, `ccache` is recommended. + -* Parallel builds should be okay in current development version (is a bug - to log, if not), but may be failure-prone in 2.7.4 and earlier releases +Build prerequisites to make NUT from scratch on various Operating Systems +------------------------------------------------------------------------- Debian 10/11 ~~~~~~~~~~~~ -Being a popular baseline among Linux distributions, Debian is an important -build target. Related common operating systems include Ubuntu and customized -distros for Raspberry Pi, as well as many others. The package list below -should largely apply to those as well, however note that some well-known -package names tend to differ. A few of those are noted below. +Being a popular baseline among Linux distributions, Debian is an +important build target. Related common operating systems include +Ubuntu and customized distros for Raspberry Pi, Proxmox, as well +as many others. -NOTE: While Debian distros I've seen (8 to 11) provide a "libusb-dev" +The package list below should largely apply to those as well, +however note that some well-known package names tend to differ. +A few of those are noted below. + +[NOTE] +====== +While Debian distros I've seen (8 to 11) provide a "libusb-dev" for libusb-0.1 headers, the binary library package name is specifically versioned package by default of the current release (e.g. "libusb-0.1-4"), while names of both the library and development packages for libusb-1.0 -must be determined with `apt-cache search 'libusb.*1\.0.*` yielding e.g. -"libusb-1.0-0-dev" (string name was seen with different actual package -source versions on both Debian 8 Jessie and Debian 11 Buster). +must be determined with: +------ +:; apt-cache search 'libusb.*1\.0.* +------ +yielding e.g. "libusb-1.0-0-dev" (string name was seen with different +actual package source versions on both Debian 8 "Jessie" and +Debian 11 "Buster"). +====== ----- +Debian-like package installations commonly start with an update of +metadata about recently published package revisions: + +------ :; apt-get update # NOTE: Older Debian-like distributions may lack a "libtool-bin" @@ -144,35 +166,34 @@ source versions on both Debian 8 Jessie and Debian 11 Buster). :; apt-get install \ bash dash ksh busybox ----- +------ Alternatives that can depend on your system's other packaging choices: ----- +------ :; apt-get install libneon27-dev # ... or :; apt-get install libneon27-gnutls-dev ----- +------ Over time, Debian and Ubuntu had different packages and libraries providing the actual methods for I2C; if your system lacks the `libi2c` (and so fails to `./configure --with-all`), try adding the following packages: -+ ----- +------ :; apt-get install build-essential git-core libi2c-dev i2c-tools lm-sensors ----- +------ For cross-builds (note that not everything supports multilib approach, limiting standard package installations to one or another implementation; in that case local containers each with one ARCH may be a better choice, with `qemu-user-static` playing a role to "natively" run the other-ARCH complete environments): ----- +------ :; apt-get install \ gcc-multilib g++-multilib \ crossbuild-essential \ gcc-10:armhf gcc-10-base:armhf \ qemu-user-static ----- +------ NOTE: For Jenkins agents, also need to `apt-get install openjdk-11-jdk-headless` (technically, needs at least JRE 8+). You may have to ensure `/proc` is mounted @@ -189,12 +210,14 @@ well. These systems typically use the RPM package manager, using directly For CentOS 7 it seems that not all repositories are equally good; some of the software below is only served by EPEL (Extra Packages for Enterprise Linux), as detailed at: + * https://docs.fedoraproject.org/en-US/epel/ * https://www.redhat.com/en/blog/whats-epel-and-how-do-i-use-it * https://pkgs.org/download/epel-release You may have to specify a mirror as the `baseurl` in a `/etc/yum.repos.d/...` file (as the aged distributions become less served by mirrors), such as: + * https://www.mirrorservice.org/sites/dl.fedoraproject.org/pub/epel/7/x86_64/ + ------ @@ -205,7 +228,7 @@ file (as the aged distributions become less served by mirrors), such as: # lines, and comment away the mirrorlist= lines (if yum hiccups otherwise) ------ -General developer system helpers mentioned in `ci-farm-lxc-setup.txt`: +General developer system helpers mentioned in link:ci-farm-lxc-setup.txt[]: ------ :; yum update @@ -214,8 +237,8 @@ General developer system helpers mentioned in `ci-farm-lxc-setup.txt`: ------ NOTE: Below we request to install generic `python` per system defaults. -You may request specifically `python2` or `python3` (or both): NUT should -be compatible with both (2.7+ at least). +You may request specifically `python2` or `python3` (or both): current +NUT should be compatible with both (2.7+ at least). NOTE: On CentOS, `libusb` means 0.1.x and `libusbx` means 1.x.x API version. @@ -223,10 +246,6 @@ NOTE: On CentOS, it seems that development against libi2c/smbus is not supported. Neither the suitable devel packages were found, nor i2c-based drivers in distro packaging of NUT. Resolution and doc PRs are welcome. -NOTE: `busybox` is not packaged for CentOS 7 release; a static binary can -be downloaded if needed. For more details, see -https://unix.stackexchange.com/questions/475584/cannot-install-busybox-on-centos - ------ :; yum install \ ccache time \ @@ -283,14 +302,18 @@ https://unix.stackexchange.com/questions/475584/cannot-install-busybox-on-centos :; yum install \ bash dash ksh ----- +------ + +NOTE: `busybox` is not packaged for CentOS 7 release; a static binary can +be downloaded if needed. For more details, see +https://unix.stackexchange.com/questions/475584/cannot-install-busybox-on-centos CentOS packaging for 64-bit systems delivers the directory for dispatching compiler symlinks as `/usr/lib64/ccache`. You can set it up same way as for other described environments by adding a symlink `/usr/lib/ccache`: ----- +------ :; ln -s ../lib64/ccache/ "$ALTROOT"/usr/lib/ ----- +------ NOTE: For Jenkins agents, also need to `yum install java-11-openjdk-headless` (technically, needs at least JRE 8+). @@ -298,17 +321,17 @@ NOTE: For Jenkins agents, also need to `yum install java-11-openjdk-headless` FreeBSD 12.2 ~~~~~~~~~~~~ -Note that `PATH` for builds on BSD should include `local`: +Note that `PATH` for builds on BSD should include `/usr/local/...`: ----- +------ :; PATH=/usr/local/libexec/ccache:/usr/local/bin:/usr/bin:$PATH :; export PATH ----- +------ NOTE: You may want to reference `ccache` even before all that, as detailed below. ----- +------ :; pkg install \ git python perl5 curl \ gmake autoconf automake autotools libltdl libtool \ @@ -353,13 +376,13 @@ below. :; pkg install \ bash dash busybox ksh93 ----- +------ Recommended: ----- +------ :; pkg install ccache :; ccache-update-links ----- +------ For compatibility with common setups on other operating systems, can symlink `/usr/local/libexec/ccache` as `/usr/lib/ccache` and possibly add dash-number @@ -371,26 +394,27 @@ and do note its further OS configuration suggestions for special filesystem mounts. Due to BSD specific paths *when not using* an implementation of `pkg-config` -or `pkgconf` (so guessing of flags is left to administrator -- TBD in NUT m4 -scripts), better use this routine to test the config/build: +or `pkgconf` (so guessing of flags is left to administrator -- TBD in NUT +`m4` scripts), better use this routine to test the config/build: ---- :; ./configure --with-doc=all --with-all --with-cgi \ --without-avahi --without-powerman --without-modbus \ - ### CPPFLAGS="-I/usr/local/include -I/usr/include" LDFLAGS="-L/usr/local/lib -L/usr/lib" + ### CPPFLAGS="-I/usr/local/include -I/usr/include" \ + ### LDFLAGS="-L/usr/local/lib -L/usr/lib" ---- -Note the lack of `pkg-config` also precludes libcppunit tests, although they -also tend to mis-compile/mis-link with GCC (while CLANG seems okay). +Note the lack of `pkg-config` also precludes `libcppunit` tests, although +they also tend to mis-compile/mis-link with GCC (while CLANG seems okay). OpenBSD 6.4 ~~~~~~~~~~~ -Note that `PATH` for builds on BSD should include `local`: +Note that `PATH` for builds on BSD should include `/usr/local/...`: ----- +------ :; PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH :; export PATH ----- +------ NOTE: You may want to reference `ccache` even before all that, as detailed below. @@ -416,7 +440,7 @@ at least not via http://ftp.netbsd.hu/mirrors/openbsd/6.4/packages/amd64/ mirror. The `gcc` version on CD image differed notably from that in the networked repository (4.2.x vs 4.9.x) ----- +------ :; pkg_add \ git python curl \ gmake autoconf automake libltdl libtool \ @@ -464,26 +488,29 @@ networked repository (4.2.x vs 4.9.x) :; pkg_add \ bash dash busybox ksh93 ----- +------ -NOTE: With OpenBSD 6.4, building against freeipmi failed: its libtool +[NOTE] +====== +With OpenBSD 6.4, building against freeipmi failed: its libtool recipes referenced `-largp` which did not get installed in the system. Maybe some more packages are needed explicitly? -+ ----- + +------ :; pkg_add \ freeipmi ----- +------ +====== Recommended: ----- +------ :; pkg_add ccache :; ( mkdir -p /usr/lib/ccache && cd /usr/lib/ccache && \ for TOOL in cpp gcc g++ clang clang++ clang-cpp ; do \ ln -s ../../local/bin/ccache "$TOOL" ; \ done ; \ ) ----- +------ For compatibility with common setups on other operating systems, can add dash-number suffixed symlinks to compiler tools (e.g. `gcc-4.2.1` beside @@ -493,16 +520,17 @@ NOTE: For Jenkins agents, also need to `pkg_add jre` or `pkg_add jdk` (pick version 1.8 or 8, or 11+). Due to BSD specific paths *when not using* an implementation of `pkg-config` -or `pkgconf` (so guessing of flags is left to administrator -- TBD in NUT m4 -scripts), better use this routine to test the config/build: ----- +or `pkgconf` (so guessing of flags is left to administrator -- TBD in NUT +`m4` scripts), better use this routine to test the config/build: +------ :; ./configure --with-doc=all --with-all --with-cgi \ --without-avahi --without-powerman --without-modbus \ - ### CPPFLAGS="-I/usr/local/include -I/usr/include" LDFLAGS="-L/usr/local/lib -L/usr/lib" ----- + ### CPPFLAGS="-I/usr/local/include -I/usr/include" + ### LDFLAGS="-L/usr/local/lib -L/usr/lib" +------ -Note the lack of `pkg-config` also precludes libcppunit tests, although they -also tend to mis-compile/mis-link with GCC (while CLANG seems okay). +Note the lack of `pkg-config` also precludes `libcppunit` tests, although +they also tend to mis-compile/mis-link with GCC (while CLANG seems okay). NetBSD 9.2 ~~~~~~~~~~ @@ -519,24 +547,24 @@ require to build NUT. Note that `PATH` for builds on NetBSD should include `local` and `pkg`; the default after installation of the test system was: ----- +------ :; PATH="/sbin:/usr/sbin:/bin:/usr/bin:/usr/pkg/sbin:/usr/pkg/bin:/usr/X11R7/bin:/usr/local/sbin:/usr/local/bin" :; export PATH ----- +------ NOTE: You may want to reference `ccache` even before all that, as detailed below: ----- +------ :; PATH="/usr/lib/ccache:$PATH" :; export PATH ----- +------ To use the `ci_build.sh` don't forget `bash` which is not part of OpenBSD base installation. It is not required for "legacy" builds arranged by just `autogen.sh` and `configure` scripts. ----- +------ :; pkgin install \ git python27 python39 perl curl \ make gmake autoconf automake libltdl libtool \ @@ -579,22 +607,25 @@ base installation. It is not required for "legacy" builds arranged by just :; pkgin install \ bash dash ast-ksh oksh ----- +------ -NOTE: (TBD) On NetBSD 9.2 this package complains that it requires +[NOTE] +====== +(TBD) On NetBSD 9.2 this package complains that it requires OS ABI 9.0, or that `CHECK_OSABI=no` is set in `pkg_install.conf`. Such file was not found in the test system... -+ ----- + +------ :; pkgin install \ openipmi ----- +------ +====== Recommended: For compatibility with common setups on other operating systems, can add dash-number suffixed symlinks to compiler tools (e.g. `gcc-7` beside the `gcc` installed by package) near the original binaries and into `/usr/lib/ccache`: ----- +------ :; ( cd /usr/bin && for TOOL in cpp gcc g++ ; do \ ln -s "$TOOL" "$TOOL-7" ; \ done ) @@ -619,7 +650,7 @@ binaries and into `/usr/lib/ccache`: done ; \ done ; \ ) ----- +------ NOTE: For Jenkins agents, also need to `pkgin install openjdk11` (will be in `JAVA_HOME=/usr/pkg/java/openjdk11`). @@ -636,7 +667,7 @@ To build older NUT releases (2.7.4 and before), you may need to explicitly Typical tooling would include: ----- +------ :; pkg install \ git curl wget \ gnu-make autoconf automake libltdl libtool \ @@ -696,10 +727,12 @@ Typical tooling would include: ### Maybe - after it gets fixed for GCC builds/linkage :; pkg install \ cppunit ----- +------ -For extra compiler coverage, we can install a large selection of versions: ----- +For extra compiler coverage, we can install a large selection of versions, +although to meet NUT CI farm expectations we also need to expose "numbered" +filenames, as automated below: +------ :; pkg install \ gcc-48 gcc-49 gcc-5 gcc-6 gcc-7 gcc-9 gcc-10 gcc-11 \ clang-80 clang-90 \ @@ -720,14 +753,14 @@ For extra compiler coverage, we can install a large selection of versions: # If /usr/lib/ccache/ symlinks to compilers do not appear after package # installation, or if you had to add links like above, call the service: :; svcadm restart ccache-update-symlinks ----- +------ -We can also include a `gcc-4.4.4-il` (used to build the illumos OS ecosystems, -at least until recently, which is a viable example of an old GCC baseline); -but note that so far it conflicts with libgd builds at `configure --with-cgi` -stage: +We can even include a `gcc-4.4.4-il` version (used to build the illumos OS +ecosystems, at least until recently, which is a viable example of an old +GCC baseline); but note that so far it conflicts with `libgd` builds at +`./configure --with-cgi` stage (its binaries require newer ecosystem): ----- +------ :; pkg install \ illumos-gcc@4.4.4 @@ -738,9 +771,9 @@ stage: # If /usr/lib/ccache/ symlinks to these do not appear, call the service: :; svcadm restart ccache-update-symlinks ----- +------ -OI currently also does not build cppunit-based tests well, at least +OI currently also does not build `cppunit`-based tests well, at least not with GCC (they segfault at run-time with `ostream` issues); a CLANG build works for that however. @@ -766,7 +799,7 @@ with that project's design goals. Note you may need not just the "Core" IPS package publisher, but also the "Extra" one. See OmniOS CE web site for setup details. ----- +------ :; pkg install \ developer/build/autoconf developer/build/automake developer/build/libtool \ build-essential ccache git developer/pkg-config \ @@ -783,16 +816,16 @@ Note you may need not just the "Core" IPS package publisher, but also the # :; pkg install runtime/python-37 # You can find a list of what is available in remote repositories with: # :; pkg info -r | grep -Ei 'perl|python' ----- +------ OmniOS lacks a pre-packaged libusb, however the binary build from contemporary OpenIndiana can be used (copy the header files and the library+symlinks for all architectures you would need). -You may need to set up `ccache` with the same dir used in other OS recipes; -assuming your Build Essentials pulled GCC 9, and ccache is under `/opt/ooce` -namespace, that would be like: ----- +You may need to set up `ccache` with the same `/usr/lib/ccache` dir used +in other OS recipes. Assuming your Build Essentials pulled GCC 9 version, +and ccache is under `/opt/ooce` namespace, that would be like: +------ :; mkdir -p /usr/lib/ccache :; cd /usr/lib/ccache :; ln -fs ../../../opt/ooce/bin/ccache gcc @@ -801,14 +834,14 @@ namespace, that would be like: :; ln -fs ../../../opt/ooce/bin/ccache gcc-9 :; ln -fs ../../../opt/ooce/bin/ccache g++-9 :; ln -fs ../../../opt/ooce/bin/ccache gcpp-9 ----- +------ Given that many of the dependencies can get installed into that namespace, you may have to specify where `pkg-config` will look for them (note that library and binary paths can be architecture bitness-dependent): ----- +------ :; ./configure PKG_CONFIG_PATH="/opt/ooce/lib/amd64/pkgconfig" --with-cgi ----- +------ Note also that the minimal footprint nature of OmniOS CE precludes building any large scope easily, so avoid docs and "all drivers" unless you provide From f6504f591a513a3e298b83a42293b0cccf5dd235 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 18:54:15 +0200 Subject: [PATCH 457/700] docs/configure.txt: fix formatting, add driver type titles, extend some notes --- docs/configure.txt | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/docs/configure.txt b/docs/configure.txt index 0c35a72550..2d7c7f093b 100644 --- a/docs/configure.txt +++ b/docs/configure.txt @@ -10,13 +10,20 @@ current and complete listing for the current version of the codebase. Driver selection ---------------- +Serial drivers +~~~~~~~~~~~~~~ + --with-serial +USB drivers +~~~~~~~~~~~ + Build and install the serial drivers (default: yes) --with-usb Build and install the USB drivers (default: auto-detect) + Note that you need to install the libusb development package or files, and that both libusb 0.1 and 1.0 are supported. In case both are available, libusb 1.0 takes precedence, and will be used by default. @@ -26,9 +33,13 @@ If you do specify the version to use (or `yes` for auto-detection), this option would fail if requested (or any) libusb version was not found. The default `auto` value would not fail in such case. +SNMP drivers +~~~~~~~~~~~~ + --with-snmp Build and install the SNMP drivers (default: auto-detect) + Note that you need to install libsnmp development package or files. --with-net-snmp-config @@ -37,55 +48,76 @@ In addition to the `--with-snmp` option above, this one allows to provide a custom program name (in `PATH`) or complete pathname to `net-snmp-config` (may have copies named per architecture, e.g. `net-snmp-config-32` and `net-snmp-config-64`). + This may be needed on build systems which support multiple architectures, or in cases where your distribution names this program differently. With a default value of `yes` it would mean preference of this program, compared to information from `pkg-config`, if both are available. +XML drivers and features +~~~~~~~~~~~~~~~~~~~~~~~~ + --with-neon Build and install the XML drivers (default: auto-detect) + Note that you need to install neon development package or files. +LLNC CHAOS Powerman driver +~~~~~~~~~~~~~~~~~~~~~~~~~~ + --with-powerman Build and install Powerman PDU client driver (default: auto-detect) + This allows to interact with the Powerman daemon, and the numerous Power Distribution Units (PDU) supported by the project. Note that you need to install powerman development package or files. +IPMI drivers +~~~~~~~~~~~~ + --with-ipmi --with-freeipmi Build and install IPMI PSU driver (default: auto-detect) + This allows to monitor numerous Power Supply Units (PSU) found on servers. -Note that you need to install freeipmi (0.8.5 or higher, for nut-scanner ; and -1.0.1 or higher, for nut-ipmipsu) development package or files. + +Note that you need to install freeipmi (0.8.5 or higher, for nut-scanner; +and 1.0.1 or higher, for nut-ipmipsu) development package or files. + +I2C bus drivers +~~~~~~~~~~~~~~~ --with-linux_i2c Build and install i2c drivers (default: auto-detect) + Note that you need to install libi2c development package or files. +Manual selection of drivers +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + --with-drivers=,,... Specify exactly which driver or drivers to build and install (this works for serial, usb, and snmp drivers, and overrides the preceding three options). -As of the time of this writing (2010), there are 46 UPS drivers +As of the time of original writing (2010), there are 46 UPS drivers available. Most users will only need one, a few will need two or three, and very few people will need all of them. To save time during the compile and disk space later on, you can use this option to just build and install a subset of the drivers. -To select mge-shut and usbhid-ups, you'd do this: +For example, to select `mge-shut` and `usbhid-ups`, you'd do this: --with-drivers=apcsmart,usbhid-ups If you need to build more drivers later on, you will need to rerun -configure with a different list. To make it build all of the -drivers from scratch again, run 'make clean' before starting. +`configure` with a different list. To make it build all of the +drivers from scratch again, run `make clean` before starting. Optional features From aa2f9b789563e1cf7089ca751a603d9cb4c2632a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 18:54:29 +0200 Subject: [PATCH 458/700] docs/configure.txt: link to powerman project --- docs/configure.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/configure.txt b/docs/configure.txt index 2d7c7f093b..f736d6d6ac 100644 --- a/docs/configure.txt +++ b/docs/configure.txt @@ -71,7 +71,9 @@ LLNC CHAOS Powerman driver Build and install Powerman PDU client driver (default: auto-detect) This allows to interact with the Powerman daemon, and the numerous -Power Distribution Units (PDU) supported by the project. +Power Distribution Units (PDU) supported by the +https://github.com/chaos/powerman[powerman] project. + Note that you need to install powerman development package or files. IPMI drivers From b203b124333acb1eddec8d395bf93412a4c632e9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 18:54:42 +0200 Subject: [PATCH 459/700] docs/configure.txt: add a section on modbus drivers --- docs/configure.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/configure.txt b/docs/configure.txt index f736d6d6ac..291dd4c3b7 100644 --- a/docs/configure.txt +++ b/docs/configure.txt @@ -98,6 +98,15 @@ Build and install i2c drivers (default: auto-detect) Note that you need to install libi2c development package or files. +Modbus drivers +~~~~~~~~~~~~~~ + + --with-modbus + +Build and install modbus (Serial, TCP) drivers (default: auto-detect) + +Note that you need to install libmodbus development package or files. + Manual selection of drivers ~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 1307c684f84c506bf3d0de9a64ca4d5b2a8a0ee0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 19:39:36 +0200 Subject: [PATCH 460/700] docs/configure.txt: fix formatting, add titles, extend some notes --- docs/configure.txt | 301 ++++++++++++++++++++++++++++----------------- 1 file changed, 191 insertions(+), 110 deletions(-) diff --git a/docs/configure.txt b/docs/configure.txt index 291dd4c3b7..b039e87f7a 100644 --- a/docs/configure.txt +++ b/docs/configure.txt @@ -134,53 +134,79 @@ drivers from scratch again, run `make clean` before starting. Optional features ----------------- +CGI client interface +~~~~~~~~~~~~~~~~~~~~ + --with-cgi (default: no) Build and install the optional CGI programs, HTML files, and sample CGI configuration files. This is not enabled by default, as they -are only useful on web servers. See data/html/README for additional +are only useful on web servers. See link:data/html/README[] for additional information on how to set up CGI programs. +Pretty documentation and man pages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + --with-doc= (default: no) Build and install NUT documentation file(s). -The possible values are "html-single" for single page HTML, "html-chunked" -for multi pages HTML, "pdf" for a PDF file, and "man" for the usual manpages. +This feature requires AsciiDoc 8.6.3 or newer (see https://asciidoc.org). + +The possible documentation type values are: + +* `html-single` for single page HTML, +* `html-chunked` for multi-paged HTML, +* `pdf` for a PDF file, and +* `man` for the usual manpages. -If the "--with-doc" argument is passed without a list, or lists just "=yes" -or "=all", it enables all supported formats with a "=yes". +Other values understood for this option are listed below: -An (explicit!) "--with-doc=auto" argument tries to enable all supported -formats with an "=auto". +* If the `--with-doc` argument is passed without a list, or specifies + just `=yes` or `=all`, it enables all supported formats with a `=yes` + to require them. -A "--with-doc=no" quietly skips generation of all types of documentation, -including manpages. +* An (explicit!) `--with-doc=auto` argument tries to enable all supported + formats with an `=auto` but should not fail the build if something + can not be generated. + +* A `--with-doc=no` quietly skips generation of all types of documentation, + including manpages. + +* `--with-doc=skip` is used to configure some of the `make distcheck*` + scenarios to re-use man page files built and distributed by the main + build and not waste time on re-generation of those. Multiple documentation format values can be specified, separated with comma. -Each such value can be suffixed with "=yes" to require building of the this -documentation format (abort configuration if tools are missing), "=auto" to +Each such value can be suffixed with `=yes` to require building of this one +documentation format (abort configuration if tools are missing), `=auto` to detect and enable if we can build it on this system (and not abort if we -can not), and "=no" (or "=skip") to explicitly skip generation of this +can not), and `=no` (or `=skip`) to explicitly skip generation of this document format even if we do have the tools to build it. If a document format is mentioned in the list without a suffix, then it is -treated as a "=yes" requirement. - -Verbose output can be enabled using: ASCIIDOC_VERBOSE=-v make +treated as a `=yes` requirement. -This feature requires AsciiDoc 8.6.3 (http://www.methods.co.nz/asciidoc). +Verbose output can be enabled using: `ASCIIDOC_VERBOSE=-v make` Example valid formats of this flag: -* `--with-doc` + +* `--with-doc` without an argument, effectively same as `--with-doc=yes` * `--with-doc=` is a valid empty list, effectively same as `--with-doc=no` * `--with-doc=auto` * `--with-doc=pdf,html-chunked` * `--with-doc=man=no,pdf=auto,html-single` +Development files +~~~~~~~~~~~~~~~~~ + --with-dev (default: no) -Build and install the upsclient and nutclient library and header files. +Build and install the upsclient and nutclient library and header files, to +build further projects against NUT (such as wmNUT client and many others). + +I want it all! +~~~~~~~~~~~~~~ --with-all (no default) @@ -188,42 +214,71 @@ Build and install all of the above (the serial, USB, SNMP, XML/HTTP and PowerMan drivers, the CGI programs and HTML files, and the upsclient library). +Networking transport security +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + --with-ssl (default: auto-detect) --with-nss (default: auto-detect) --with-openssl (default: auto-detect) Enable SSL support, using either Mozilla NSS or OpenSSL. + If both are present, and nothing was specified, OpenSSL support will -be preferred. Read docs/security.txt for instructions on SSL support. +be preferred. + +Read link:docs/security.txt[] for instructions on SSL support. + +NOTE: Currently the two implementations differ in supported features. + +Networking access security +~~~~~~~~~~~~~~~~~~~~~~~~~~ --with-wrap (default: auto-detect) -Enable libwrap (tcp-wrappers) support. Refer to upsd man page for -more information. +Enable libwrap (tcp-wrappers) support. + +Refer to linkman:upsd[8] man page for more information. + +Networking IPv6 +~~~~~~~~~~~~~~~ --with-ipv6 (default: auto-detect) Enable IPv6 support. +AVAHI/mDNS +~~~~~~~~~~ + --with-avahi (default: auto-detect) Build and install Avahi support, to publish NUT server availability using mDNS protocol. This requires Avahi development files for the Core and Client parts. +LibLTDL +~~~~~~~ + --with-libltdl (default: auto-detect) Enable libltdl (Libtool dlopen abstraction) support. -This is required to build nut-scanner. + +This is required to build `nut-scanner` which loads third-party libraries +dynamically, based on requested scanning options. This allows to build and +package the tool without requiring all possible dependencies to be installed +in each run-time environment. Other configuration options --------------------------- +NUT data server port +~~~~~~~~~~~~~~~~~~~~ + --with-port=PORT -Change the TCP port used by the network code. Default is 3493. +Change the TCP port used by the network code. Default is 3493 +as registered with IANA. -Ancient versions of upsd used port 3305. NUT 2.0 and up use a +Ancient versions of `upsd` used port 3305. NUT 2.0 and up use a substantially different network protocol and are not able to communicate with anything older than the 1.4 series. @@ -231,41 +286,48 @@ If you have to monitor a mixed environment, use the last 1.4 version, as it contains compatibility code for both the old "REQ" and the new "GET" versions of the protocol. +Daemon user accounts +~~~~~~~~~~~~~~~~~~~~ + --with-user= --with-group= -Programs started as root will setuid() to for somewhat -safer operation. You can override this with -u in several -programs, including upsdrvctl (and all drivers by extension), upsd, -and upsmon. The "user" directive in ups.conf overrides this at run +Programs started as `root` will `setuid()` to `` for somewhat +safer operation. You can override this with `-u ` in several +programs, including `upsdrvctl` (and all drivers by extension), `upsd`, +and `upsmon`. The "user" directive in `ups.conf` overrides this at run time for the drivers. -NOTE: upsmon does not totally drop root because it may need to +NOTE: `upsmon` does not totally drop `root` because it may need to initiate a shutdown. There is always at least a stub process -remaining with root powers. The network code runs in another +remaining with `root` powers. The network code runs in another (separate) process as the new user. -The is used for the permissions of some files, +The `` is used for the permissions of some files, particularly the hotplugging rules for USB. The idea is that the device files for any UPS devices should be readable and writable by members of that group. -The default value for both the username and groupname is "nobody". +The default value for both the username and groupname is `nobody` +(or `nogroup` on systems that have it when `configure` script runs). This was done since it's slightly better than staying around as -root. Running things as nobody is not a good idea, since it's a +`root`. Running things as `nobody` is not a good idea, since it's a hack for NFS access. You should create at least one separate user for this software. -If you use one of the --with-user and --with-group options, then +If you use one of the `--with-user` and `--with-group` options, then you have to use the other one too. -See the INSTALL.nut document and the FAQ for more on this topic. +See the link:INSTALL.nut[] document and the FAQ for more on this topic. + +Syslog facility +~~~~~~~~~~~~~~~ --with-logfacility=FACILITY Change the facility used when writing to the log file. Read the man -page for openlog to get some idea of what's available on your system. -Default is LOG_DAEMON. +page for `openlog` to get some idea of what's available on your system. +Default is `LOG_DAEMON`. Installation directories @@ -275,111 +337,122 @@ Installation directories This is a fairly standard option with GNU autoconf, and it sets the base path for most of the other install directories. The default -is /usr/local/ups, which puts everything but the state sockets in one -easy place. +is `/usr/local/ups`, which puts everything but the state sockets in one +easy place, and does not conflict with usual distribution packaging. If you like having things to be at more of a "system" level, setting -the prefix to /usr/local or even /usr might be better. +the prefix to `/usr/local` or even `/usr` might be better. --exec_prefix=PATH This sets the base path for architecture dependent files. By -default, it is the same as . +default, it is the same as ``. --sysconfdir=PATH Changes the location where NUT's configuration files are stored. -By default this path is /etc. Setting this to /etc or -/etc/ups might be useful. +By default this path is `/etc`. Setting this to `/etc/nut` or +`/etc/ups` might be useful. -The NUT_CONFPATH environment variable overrides this at run time. +The `NUT_CONFPATH` environment variable overrides this at run time. - --bindir=PATH --sbindir=PATH + --bindir=PATH Where executable files will be installed. Files that are normally -executed by root (upsd, upsmon, upssched) go to sbindir, all others -to bindir. The defaults are /bin and /sbin. +executed by root (`upsd`, `upsmon`, `upssched`) go to ``, +all others to ``. The defaults are `/sbin` and +`/bin` respectively. + +See also `--with-drvpath` below. + + --with-drvpath=PATH + +The UPS drivers will be installed to this path. By default they +install to `/bin`, i.e. `/usr/local/ups/bin`. + +You would want a location that remains mounted when most of the system +is prepared to turn off, so some distributions package NUT drivers into +`/lib/nut` or similar. See link:config-notes.txt[] detailing how to +set up system shutdown. + +The `driverpath` global directive in the `ups.conf` file overrides this +at run time. --datadir=PATH Change the data directory, i.e., where architecture independent -read-only data is installed. By default this is /share, -i.e., /usr/local/ups/share. At the moment, this directory only -holds two files -- the optional cmdvartab and driver.list. +read-only data is installed. By default this is `/share`, +i.e. `/usr/local/ups/share`. At the moment, this directory only +holds two files -- the optional `cmdvartab` and `driver.list`. --mandir=PATH Sets the base directories for the man pages. The default is -/man, i.e., /usr/local/ups/man. +`/man`, i.e. `/usr/local/ups/man`. --includedir=PATH Sets the path for include files to be installed when `--with-dev` is -selected. For example, upsclient.h is installed here. The default -is /include. +selected. For example, `upsclient.h` is installed here. The default +is `/include`. --libdir=PATH -Sets the installation path for libraries. This is just the -upsclient library for now. The default is /lib. +Sets the installation path for libraries. Depending on the build +configuration, this can include the `libupsclient`, `libnutclient`, +`libnutclientsub`, `libnutscan` and their pkg-config metadata (see +`--with-pkgconfig-dir` option). The default is `/lib`. - --with-drvpath=PATH + --with-pkgconfig-dir=PATH -The UPS drivers will be installed to this path. By default they -install to "/bin", i.e., /usr/local/ups/bin. +Where to install pkg-config `*.pc` files. This option only has an +effect if `--with-dev` is selected, and causes a pkg-config file to +be installed in the named location. The default is +`/pkgconfig`. -The "driverpath" global directive in the ups.conf file overrides this -at run time. +Use `--without-pkgconfig-dir` to disable this feature altogether. --with-cgipath=PATH The CGI programs will be installed to this path. By default, they -install to "/cgi-bin", which is usually /usr/local/ups/cgi-bin. +install to `/cgi-bin`, which is usually +`/usr/local/ups/cgi-bin`. -If you set the prefix to something like /usr, you should set the -cgipath to something else, because /usr/cgi-bin is pretty ugly and +NOTE: If you set the prefix to something like `/usr`, you should set the +`cgipath` to something else, because `/usr/cgi-bin` is pretty ugly and non-standard. The CGI programs are not built or installed by default. Use -"./configure --with-cgi" to request that they are built and +`./configure --with-cgi` to request that they are built and installed. --with-htmlpath=PATH HTML files will be installed to this path. By default, this is -"/html". Note that HTML files are only installed if ---with-cgi is selected. - - --with-pkgconfig-dir=PATH - -Where to install pkg-config *.pc files. This option only has an -effect if `--with-dev` is selected, and causes a pkg-config file to -be installed in the named location. The default is -/pkgconfig. - -Use --without-pkgconfig-dir to disable this feature altogether. +`/html`. Note that HTML files are only installed if +`--with-cgi` is selected. --with-hotplug-dir=PATH -Where to install Linux 2.4 hotplugging rules. The default is -/etc/hotplug, if that directory exists, and not to install it +Where to install Linux 2.4 hotplugging rules. The default is to use +`/etc/hotplug`, if that directory exists, and to not install it otherwise. Note that this installation directory is not a -subdirectory of by default. When installing NUT as a +subdirectory of `` by default. When installing NUT as a non-root user, you may have to override this option. -Use --without-hotplug-dir to disable this feature altogether. +Use `--without-hotplug-dir` to disable this feature altogether. --with-udev-dir=PATH Where to install Linux 2.6 hotplugging rules, for kernels that have -the "udev" mechanism. The default is /etc/udev, if that directory -exists, and not to install it otherwise. Note that this -installation directory is not a subdirectory of by +the "udev" mechanism. The default is to use `/etc/udev`, if that +directory exists, and to not install it otherwise. Note that this +installation directory is not a subdirectory of `` by default. When installing NUT as a non-root user, you may have to override this option. -Use --without-udev-dir to disable this feature altogether. +Use `--without-udev-dir` to disable this feature altogether. --with-systemdsystemunitdir=PATH @@ -387,13 +460,13 @@ Where to install Linux systemd unit definitions. Useless and harmless on other OSes, including Linux distributions without systemd, just adding a little noise to configure script output. -Use --with-systemdsystemunitdir=auto (default) to detect the settings +Use `--with-systemdsystemunitdir=auto` (default) to detect the settings using pkg-config if possible. -Use --with-systemdsystemunitdir(=yes) to require detection of these +Use `--with-systemdsystemunitdir(=yes)` to require detection of these settings with pkg-config, or fail configuration if not possible. -Use --with-systemdsystemunitdir=no to disable this feature altogether. +Use `--with-systemdsystemunitdir=no` to disable this feature altogether. --with-systemdshutdowndir=PATH @@ -401,9 +474,9 @@ Where to install Linux systemd unit definitions for shutdown handling. Useless and harmless on other OSes, including Linux distributions without systemd, just adding a little noise to configure script output. -Use --with-systemdshutdowndir to detect the settings using pkg-config. +Use `--with-systemdshutdowndir` to detect the settings using pkg-config. -Use --with-systemdshutdowndir=no to disable this feature altogether. +Use `--with-systemdshutdowndir=no` to disable this feature altogether. --with-systemdtmpfilesdir=PATH @@ -412,16 +485,17 @@ automatically created locations for PID, state and similar run-time files). Useless and harmless on other OSes, including Linux distributions without systemd, just adding a little noise to configure script output. -Use --with-systemdtmpfilesdir to detect the settings using pkg-config. +Use `--with-systemdtmpfilesdir` to detect the settings using pkg-config. -Use --with-systemdtmpfilesdir=no to disable this feature altogether. +Use `--with-systemdtmpfilesdir=no` to disable this feature altogether. --with-augeas-lenses-dir=PATH -Where to install Augeas configuration-management lenses. Only useful and valid -if you use Augeas to parse and modify configuration files. The default is -/usr/share/augeas/lenses, if that directory exists, and not to install it -otherwise. +Where to install Augeas configuration-management lenses. + +Only useful and valid if you use Augeas to parse and modify configuration +files. The default is to use `/usr/share/augeas/lenses`, if that directory +exists, and to not install it otherwise. Directories used by NUT at run-time @@ -430,29 +504,30 @@ Directories used by NUT at run-time --with-pidpath=PATH Changes the directory where pid files are stored. By default this is -/var/run. Certain programs like upsmon will leave files here. +`/var/run`. Certain programs like `upsmon` will leave files here. --with-altpidpath=PATH -Programs that normally don't have root powers, like the drivers and -upsd, write their pid files here. By default this is whatever the -statepath is, as those programs should be able to write there. +Programs that normally don't have `root` powers, like the drivers and +`upsd`, write their pid files here. By default this is whatever the +statepath (below) is, as those programs should be able to write there. -The NUT_ALTPIDPATH environment variable overrides this at run time. +The `NUT_ALTPIDPATH` environment variable overrides this at run time. --with-statepath=PATH -Change the default location of the state sockets created by the -drivers. - -The NUT_STATEPATH environment variable overrides this at run time. +Change the default location of the state sockets created by the drivers +to interact with the data server `upsd`. Default is `/var/state/ups`. -Default is /var/state/ups. +The `NUT_STATEPATH` environment variable overrides this at run time. Things the compiler might need to find -------------------------------------- +LibGD +~~~~~ + --with-pkg-config This option allows to provide a custom program name (in `PATH`) or a @@ -463,16 +538,16 @@ may also want to set `PKG_CONFIG_PATH` to match your current build. --with-gd-includes="-I/foo/bar" -If you installed gd in some place where your C preprocessor can't -find the header files, use this switch to add additional -I flags. +If you installed `libgd` in some place where your C preprocessor can't +find the header files, use this switch to add additional `-I` flags. --with-gd-libs="-L/foo/bar -labcd -lxyz" -If your copy of gd isn't linking properly, use this to give the -proper -L and -l flags to make it work. See LIBS= in gd's Makefile. +If your copy of `libgd` isn't linking properly, use this to give the +proper `-L` and `-l` flags to make it work. See `LIBS=` in gd's `Makefile`. -NOTE: the --with-gd switches are not necessary if you have gd 2.0.8 -or higher installed properly. The gdlib-config script or pkgconfig +NOTE: the `--with-gd` switches are not necessary if you have gd 2.0.8 +or higher installed properly. The `gdlib-config` script or pkg-config manifest will be detected and used by default in that situation. --with-gdlib-config @@ -482,6 +557,9 @@ a complete pathname to `gdlib-config`. This may be needed on build systems which support multiple architectures, or in cases where your distribution names this program differently. +LibUSB +~~~~~~ + --with-libusb-config This option allows to provide a custom program name (in `PATH`) or @@ -491,6 +569,9 @@ build systems which support multiple architectures or provide several versions of libusb, or in cases where your distribution names this program differently. +Various +~~~~~~~ + --with-ssl-includes, --with-usb-includes, --with-snmp-includes, --with-neon-includes, --with-libltdl-includes, --with-powerman-includes="-I/foo/bar" From 94a2412589fd2f48166539a1a15ed2887884602b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 19:44:05 +0200 Subject: [PATCH 461/700] drivers/snmp-ups.h: update comment to clarify the meaning of SU_TYPE_DAISY_1 and SU_TYPE_DAISY_2 --- drivers/snmp-ups.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index f6ac635ee9..6ac670a16f 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -252,8 +252,8 @@ typedef struct { * templates, such as outlets / outlets groups, which already have a format * string specifier */ /* "flags" bits 21..23 (and 24 reserved for DMF) */ -#define SU_TYPE_DAISY_1 (1UL << 21) /* Daisychain index is the 1st specifier */ -#define SU_TYPE_DAISY_2 (1UL << 22) /* Daisychain index is the 2nd specifier */ +#define SU_TYPE_DAISY_1 (1UL << 21) /* Daisychain index is the 1st %i specifier in a template with more than one */ +#define SU_TYPE_DAISY_2 (1UL << 22) /* Daisychain index is the 2nd %i specifier in a template with more than one */ #define SU_TYPE_DAISY(t) ((t)->flags & (11UL << 21)) /* Mask the SU_TYPE_DAISY_{1,2,MASTER_ONLY} but not SU_DAISY */ #define SU_DAISY (1UL << 23) /* Daisychain template definition - set at run-time for devices with detected "device.count" over 1 */ /* NOTE: Previously SU_DAISY had same bit-flag value as SU_TYPE_DAISY_2 */ From e11bf563c096b2d6a3991903bc00e366c7d9160b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 19:47:49 +0200 Subject: [PATCH 462/700] docs/daisychain.txt: fix formatting --- docs/daisychain.txt | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/docs/daisychain.txt b/docs/daisychain.txt index dd7b1531f1..c67d2875da 100644 --- a/docs/daisychain.txt +++ b/docs/daisychain.txt @@ -165,7 +165,7 @@ in the daisychain, then you would have to adapt it the following way: Templates with multiple definitions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -If there exist already templates in the mapping structure, such as for +If there already exist templates in the mapping structure, such as for single outlets and outlet-groups, you also need to specify the position of the daisychain device index in the OID strings for all entries in the mapping table, to indicate where the daisychain insertion point is exactly. @@ -180,8 +180,9 @@ You would have to translate it to: { "outlet.%i.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.3.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL, NULL }, -SU_TYPE_DAISY_1 indicates that the daisychain index is the 1st specifier -("%i") in the string. If it is the second one, use SU_TYPE_DAISY_2. +`SU_TYPE_DAISY_1` flag indicates that the daisychain index is the first +`%i` specifier in the OID template string. If it is the second one, use +`SU_TYPE_DAISY_2`. Devices alarms handling @@ -190,26 +191,30 @@ Devices alarms handling Two functions are available to handle alarms on daisychain devices in your driver: -* device_alarm_init(): clear the current alarm buffer -* device_alarm_commit(const int device_number): commit the current alarm +* `device_alarm_init()`: clear the current alarm buffer +* `device_alarm_commit(const int device_number)`: commit the current alarm buffer to "device..ups.alarm", and increase the count of alarms. If the current alarms buffer is empty, the count of alarm is decreased, and the variable "device..ups.alarm" is removed from publication. Once the alarm count reaches "0", the main (device.0) ups.status will also remove the "ALARM" flag. -NOTE: when implementing a new driver, the following functions have to be +[NOTE] +====== +When implementing a new driver, the following functions have to be called: -* "alarm_init()" at the beginning of the main update loop, for the whole + +* `alarm_init()` at the beginning of the main update loop, for the whole daisychain. This will set the alarm count to "0", and reinitialize all alarms, -* "device_alarm_init()" at the beginning of the per-device update loop. +* `device_alarm_init()` at the beginning of the per-device update loop. This will only clear the alarms for the current device, -* "device_alarm_commit()" at the end of the per-device update loop. +* `device_alarm_commit()` at the end of the per-device update loop. This will flush the current alarms for the current device, -* also "device_alarm_init()" at the end of the per-device update loop. +* also `device_alarm_init()` at the end of the per-device update loop. This will clear the current alarms, and ensure that this buffer will not be considered by other subsequent devices, -- "alarm_commit()" at the end of the main update loop, for the whole +* `alarm_commit()` at the end of the main update loop, for the whole daisychain. This will take care of publishing or not the "ALARM" flag -in the main ups.status (device.0, root collection). +in the main ups.status (`device.0`, root collection). +====== From 148ae15518cdfc2e6f6225243a63c6f69a313138 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 20:03:12 +0200 Subject: [PATCH 463/700] docs/design.txt: fix formatting; note FIXMEs for documenting TRACKING support --- docs/design.txt | 75 +++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/docs/design.txt b/docs/design.txt index 18b3e00bca..9655f01005 100644 --- a/docs/design.txt +++ b/docs/design.txt @@ -39,7 +39,7 @@ The SERVER will connect to the socket of each DRIVER and will request a dump at that time. It retains this data in local storage for later use. It continues to listen on the socket for additional updates. -This protocol is documented in sock-protocol.txt. +This protocol is documented in link:sock-protocol.txt[]. From the server ~~~~~~~~~~~~~~~ @@ -50,14 +50,14 @@ immediately. When a request for data arrives from a CLIENT, the SERVER looks through the internal storage for that UPS and returns the requested data if it is available. -The format for requests from the CLIENT is documented in protocol.txt. +The format for requests from the CLIENT is documented in link:protocol.txt[]. Instant commands ---------------- -Instant commands is the term given to a set of actions that result in +"Instant commands" is the term given to a set of actions that result in something happening to the UPS. Some of the common ones are -test.battery.start to initiate a battery test and test.panel.start to +`test.battery.start` to initiate a battery test and `test.panel.start` to test the front panel of the UPS. They are passed to the SERVER from a CLIENT using an authenticated @@ -69,9 +69,12 @@ information. At this point, there is no confirmation to the SERVER of the command's execution. This is (still) planned for a future release. This has been delayed since returning a response involves some potentially interesting -timing issues. Remember that upsd services clients in a round-robin +timing issues. Remember that `upsd` services clients in a round-robin fashion, so all queries must be lightweight and speedy. +NOTE: FIXME: Wasn't "TRACKING" mechanism for "INSTCMD/SET VAR" introduced +to address just this? See https://github.com/networkupstools/nut/pull/659 + Setting variables ----------------- @@ -88,6 +91,9 @@ Like the instant commands, there is currently no acknowledgement of the command's completion from the DRIVER. This, too, is planned for a future release. +NOTE: FIXME: Wasn't "TRACKING" mechanism for "INSTCMD/SET VAR" introduced +to address just this? See https://github.com/networkupstools/nut/pull/659 + Example data path ----------------- @@ -97,24 +103,24 @@ delivering the alpha message to the admin. 1. EQUIPMENT reports on battery by setting flag in status register -2. DRIVER notices this flag and stores it in the ups.status variable as +2. DRIVER notices this flag and stores it in the `ups.status` variable as OB. This update gets pushed out to any listeners via the sockets. -3. SERVER upsd sees activity on the socket, reads it, parses it, and +3. SERVER `upsd` sees activity on the socket, reads it, parses it, and commits the new data to its local version of the status variable. -4. CLIENT upsmon does a routine poll of SERVER for "ups.status" and - gets "OB". +4. CLIENT `upsmon` does a routine poll of SERVER for `ups.status` and + gets `OB`. -5. CLIENT upsmon then invokes its NOTIFYCMD which is upssched. +5. CLIENT `upsmon` then invokes its `NOTIFYCMD` which is `upssched`. -6. upssched starts up a daemon to handle a timer which will expire about +6. `upssched` starts up a daemon to handle a timer which will expire about 30 seconds into the future. 7. 30 seconds later, the timer expires since the UPS is still on battery, - and upssched calls the CMDSCRIPT upssched-cmd. + and so `upssched` calls the `CMDSCRIPT` which is `upssched-cmd`. -8. upssched-cmd parses the args and calls sendmail. +8. `upssched-cmd` parses the args and calls `sendmail`. 9. Avian carriers, smoke signals, SMTP, and some magic result in the message getting from the pager company's gateway to a transmitter @@ -122,66 +128,66 @@ delivering the alpha message to the admin. This scenario requires some configuration, obviously: -1. There's a UPS driver running. +1. There's an UPS driver running. (Whatever applies for the hardware) -2. upsd has a valid UPS entry in ups.conf for this UPS. +2. `upsd` has a valid UPS entry in 'ups.conf' for this UPS. [myups] driver = nutupsdrv port = /dev/ttySx -3. upsd has a valid user for upsmon in upsd.users. +3. `upsd` has a valid user for `upsmon` in 'upsd.users' file. [monuser] password = somepass upsmon primary -4. upsmon is set to monitor this UPS in upsmon.conf. +4. `upsmon` is set to monitor this UPS with this user in 'upsmon.conf' file. MONITOR myups@localhost 1 monuser somepass primary -5. upsmon is set to EXEC the NOTIFYCMD for the ONBATT condition in - upsmon.conf. +5. `upsmon` is set to `EXEC` the `NOTIFYCMD` for the `ONBATT` condition in + 'upsmon.conf' file. NOTIFYFLAG ONBATT EXEC -6. upsmon calls upssched as the NOTIFYCMD in upsmon.conf. +6. `upsmon` calls `upssched` as the `NOTIFYCMD` in 'upsmon.conf' file. NOTIFYCMD /path/to/upssched -7. upssched has a 30 second timer for ONBATT in upssched.conf. +7. `upssched` has a 30 second timer for `ONBATT` in 'upssched.conf' file. AT ONBATT * START-TIMER upsonbatt 30 -8. upssched calls upssched-cmd as the CMDSCRIPT in upssched.conf. +8. `upssched` calls `upssched-cmd` as the `CMDSCRIPT` in 'upssched.conf'. CMDSCRIPT /path/to/upssched-cmd -9. upssched-cmd knows what to do with "upsonbatt" as its first argument - (A quick case..esac construct, see the examples) +9. `upssched-cmd` knows what to do with `upsonbatt` keyword as its first + argument (a quick `case..esac` construct, see the examples) History ------- The oldest versions of this software (1998) had no separation between -the driver and the network server and only supported the latest APC +the driver and the network server, and only supported the latest APC Smart-UPS hardware as a result. The network protocol used brittle binary structs. This had numerous bad implications for compatibility and portability. After the driver and server were separated, data was shared through the state file concept. Status was written into a static array (the "info -array") by drivers, and that array was stored on disk. upsd would +array") by drivers, and that array was stored on disk. The `upsd` would periodically read that file into a local copy of that array. Shared memory mode was added a bit later, and that removed some of the lag from the status updates. Unfortunately, it didn't have any locking originally, and the possibility for corruption due to races existed. -mmap() support was added at some point after that, and became the -default. The drivers and upsd would mmap() the file into memory and +`mmap()` support was added at some point after that, and became the +default. The drivers and `upsd` would `mmap()` the file into memory and read or write from it. Locking was done using the state file as the token, so contention problems were avoided. This method was relatively quick, but it involved at least 3 copies of the data (driver, disk/mmap, @@ -194,12 +200,13 @@ connections and push updates asynchronously to any listeners. They also recognize a few commands. Drivers also dampen updates, and only push them out when something actually changes. -As a result, upsd no longer has to poll any files on the disk, and can -just select() all of its fds and wait for activity. When one of them is -active, it reads the fd and parses the results. Updates from the -hardware now get to upsd about as fast as they possibly can. +As a result, `upsd` no longer has to poll any files on the disk, and can +just `select()` all of its file descriptors (fds) and wait for activity. +When one of them is active, it reads the fd and parses the results. +Updates from the hardware now get to `upsd` about as fast as they possibly +can. -Drivers used to call setinfo() to change the local array, and then would -call writeinfo() to push the array onto the disk, or into the +Drivers used to call `setinfo()` to change the local array, and then would +call `writeinfo()` to push the array onto the disk, or into the mmap/shared memory space. This introduced a lag since many drivers poll quite a few variables during an update. From 842fe0fcecf17138f38e1aa16e4cc05e4c71136e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 20:08:56 +0200 Subject: [PATCH 464/700] docs/developer-guide.txt: fix formatting; update URL to evolution500.seq --- docs/developer-guide.txt | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/developer-guide.txt b/docs/developer-guide.txt index b40629f56b..5cd8f2c2f8 100644 --- a/docs/developer-guide.txt +++ b/docs/developer-guide.txt @@ -73,18 +73,19 @@ This mode allows to simulate any kind of devices, even non existing ones. Using this method, you can either replay a real life sequence, <>, or directly interact -through upsrw or by editing the device file, to modify the variables values. +through `upsrw` or by editing the device file, to modify the variables' +values. Here is an example to setup a device simulation: - install NUT as usual, if not already done -- get a simulation file (.dev) or sequence (.seq), or generate one using the -<>. Sample files are provided in the 'data' -directory of the NUT source. You can also download these from the development -repository, such as the -link:http://anonscm.debian.org/viewvc/nut/trunk/data/evolution500.seq?revision=2778&view=co[evolution500.seq]. -- copy the simulation file to your sysconfig directory, like /etc/nut or -/etc/ups +- get a simulation file (`.dev`) or sequence (`.seq`), or generate one using + the <>. Sample files are provided in the + `data` directory of the NUT source. You can also download these from + the development repository, such as the + link:https://github.com/networkupstools/nut/raw/master/data/evolution500.seq[evolution500.seq]. +- copy the simulation file to your sysconfig directory, like `/etc/nut` + or `/etc/ups` - configure NUT for simulation (linkman:ups.conf[5]): + [dummy] @@ -92,7 +93,7 @@ link:http://anonscm.debian.org/viewvc/nut/trunk/data/evolution500.seq?revision=2 port = evolution500.dev desc = "dummy-ups in dummy mode" + -- now start NUT, at least dummy-ups and upsd: +- now start NUT, at least `dummy-ups` and `upsd`: + $ upsdrvctl start dummy $ upsd @@ -102,13 +103,13 @@ link:http://anonscm.debian.org/viewvc/nut/trunk/data/evolution500.seq?revision=2 $ upsc dummy ... + -- you can also use upsrw to modify the data: +- you can also use `upsrw` to modify the data in memory: + $ upsrw -s ups.status="OB LB" -u user -p password dummy + -- or directly edit /etc/nut/evolution500.seq. In this case, modification will -only apply according to the TIMER events and the current position in the -sequence. +- or directly edit your copy of `/etc/nut/evolution500.seq`. + In this case, modification will only apply according to the `TIMER` + events and the current position in the sequence. For more information, refer to linkman:dummy-ups[8] manual page. @@ -118,11 +119,11 @@ For more information, refer to linkman:dummy-ups[8] manual page. Device recording ---------------- -To complete dummy-ups, NUT provides a device recorder script called -'nut-recorder.sh' and located in the 'tools/' directory of the +To complete `dummy-ups`, NUT provides a device recorder script called +`nut-recorder.sh` and located in the 'tools/' directory of the NUT source tree. -This script uses 'upsc' to record device information, and stores +This script uses `upsc` to record device information, and stores these in a differential fashion every 5 seconds (by default). Its usage is the following: @@ -134,8 +135,8 @@ For example, to record information from the device 'myups' every 10 seconds: nut-recorder.sh myups@localhost myups.seq 10 During the recording, you will want to generate power events, such as power -failure and restoration. These will be tracked in the simulation files, and be -eventually be replayed by the <> driver. +failure and restoration. These will be tracked in the simulation files, and +be eventually be replayed by the <> driver. NUT core development and maintenance From 33c135e3f40d1078e90d08bae265b2e973fbd071 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 20:38:51 +0200 Subject: [PATCH 465/700] docs/developers.txt: fix formatting, extend some notes --- docs/developers.txt | 167 ++++++++++++++++++++++++++------------------ 1 file changed, 100 insertions(+), 67 deletions(-) diff --git a/docs/developers.txt b/docs/developers.txt index 57088290dc..11f8daae49 100644 --- a/docs/developers.txt +++ b/docs/developers.txt @@ -40,7 +40,7 @@ Debugging information The `upsdebug_with_errno()`, `upsdebugx()`, `upsdebug_hex()` and `upsdebug_ascii()` routines use the global `nut_debug_level`, so you -don't have to mess around with `printf()`s and `if`s yourself. +don't have to mess around with `printf()`'s and `if`'s yourself. Use them. Memory allocation @@ -53,21 +53,21 @@ Don't use the raw calls directly. Config file parsing ~~~~~~~~~~~~~~~~~~~ -The configuration parser, called parseconf, is now up to its fourth +The configuration parser, called `parseconf`, is now up to its fourth major version. It has multiple entry points, and can handle many different jobs. It's usually used for parsing files, but it can also take input a line at a time or even a character at a time. -You must initialize a context buffer with pconf_init before using any -other parseconf function. pconf_encode is the only exception, since it -operates on a buffer you supply and is an auxiliary function. +You must initialize a context buffer with `pconf_init()` before using any +other `parseconf` function. `pconf_encode()` is the only exception, since +it operates on a buffer you supply and is an auxiliary function. Escaping special characters and quoting multiple-word elements is all handled by the state machine. Using the same code for all config files avoids code duplication. NOTE: this does not apply to drivers. Driver authors should use the -`upsdrv_makevartable()` scheme to pick up values from ups.conf. +`upsdrv_makevartable()` scheme to pick up values from 'ups.conf' file. Drivers should not have their own config files. Drivers may have their own data files, such as lists of hardware, @@ -79,13 +79,13 @@ hardware support to a driver without recompiling. vs. ~~~~~~~~~~~~~~~~~~~~~~~~~ -This is already handled by autoconf, so just include "timehead.h" and you -will get the right headers on every system. +This is already handled by autoconf, so just `#include "timehead.h"` and +you will get the right headers on every system. Device drivers -- main.c ------------------------ -The device drivers use main.c as their core. +The device drivers use `main.c` as their core. To write a new driver, you create a file with a series of support functions that will be called by main. These all have names that start @@ -93,7 +93,7 @@ with `upsdrv_`, and they will be called at different times by main depending on what needs to happen. See the <> for information on writing -drivers, and also refer to the skeletal driver in skel.c. +drivers, and also refer to the skeletal driver in `skel.c`. Portability ----------- @@ -124,7 +124,7 @@ function do_stuff(void) While this will compile and run on these newer versions, it will fail miserably for anyone on an older system. That means you must not use -it. gcc only warns about this with -pedantic. +it. `gcc` only warns about this with `-pedantic` flag. Another feature that does not work on some compilers (e.g. conforming to "ANSI C"/C89/C90 standard) is initial variable declaration inside a @@ -362,12 +362,15 @@ party dependencies and a large matrix of `CFLAGS` and compiler versions last known to work or to not (yet) work on operating systems available to that CI solution. -NOTE: The cloud Travis CI offering became effectively defunct for +[NOTE] +====== +The cloud Travis CI offering became effectively defunct for open-source projects in mid-2021, so the `.travis.yml` file in NUT codebase is not actively maintained. -+ + Local private deployments of Travis CI are possible, so if anybody does use it and has updated markup to share, they are welcome to post PRs. +====== The NUT project on GitHub has integration with Travis CI to test a large set of compiler and option combinations, covering different versions of @@ -450,15 +453,15 @@ find ways to drop out of them when we can't go any further. There's another way to program this involving a big else chunk and a bunch of braces, and it can be hard to follow. You can read this from top to bottom and have a pretty good idea of what's going on without having to -track too much { } nesting and indenting. +track too much `{ }` nesting and indenting. -We don't really care for pretentiousVariableNamingSchemes, but you can +We don't really care for `pretentiousVariableNamingSchemes`, but you can probably get away with it in your own driver that we will never have to touch. If your function or variable names start pushing important code off the right margin of the screen, expect them to meet the byte chainsaw sooner or later. -All types defined with typedef should end in "_t", because this is +All types defined with typedef should end in `_t`, because this is easier to read, and it enables tools (such as indent and emacs) to display the source code correctly. @@ -483,6 +486,14 @@ One common example for this is multi-line if condition: something_else) { -------------------------------------------------------------------------------- +which may be written without mixing tabs and spaces to indent, as: + +-------------------------------------------------------------------------------- + if (something + && something_else + ) { +-------------------------------------------------------------------------------- + Another example is tables of definitions that are better aligned with (non-leading) spaces at least between names and values not too many characters wide; it still helps to align the columns with spaces at @@ -510,13 +521,13 @@ Line breaks It is better to have lines that are longer than 80 characters than to wrap lines in random places. This makes it easier to work with tools -such as "grep", and it also lets each developer choose their own +such as `grep`, and it also lets each developer choose their own window size and tab setting without being stuck to one particular choice. Of course, this does not mean that lines should be made unnecessarily long when there is a better alternative (see the note on -pretentiousVariableNamingSchemes above). Certainly there should not +`pretentiousVariableNamingSchemes` above). Certainly there should not be more than one statement per line. Please do not use ------------------------------------------------------------------------------- @@ -575,7 +586,7 @@ possible to set a tab stop to be 3 spaces, rather than the usual 8. (Note that in the saved file, one indentation level will still correspond to one tab stop; the difference is only how the file is rendered on screen). It is even possible to set this on a -per-directory basis, by putting something like this into your .emacs +per-directory basis, by putting something like this into your `.emacs` file: ------------------------------------------------------------------------------- @@ -598,12 +609,12 @@ file: Finishing touches ~~~~~~~~~~~~~~~~~ -We like code that uses const and static liberally. If you don't need to -expose a function or global variable to the outside world, static is +We like code that uses `const` and `static` liberally. If you don't need +to expose a function or global variable to the outside world, `static` is your friend. If nobody should edit the contents of some buffer that's -behind a pointer, const keeps them honest. +behind a pointer, `const` keeps them honest. -We always compile with -Wall, so things like const and static help you +We always compile with `-Wall`, so things like `const` and `static` help you find implementation flaws. Functions that attempt to modify a constant or access something outside their scope will throw a warning or even fail to compile in some cases. This is what we want. @@ -690,9 +701,12 @@ and many people have put a lot of time and energy to improve it. Submitting patches ------------------ -Small patches that arrive in unified format (diff -u) as plain text -attachments with no HTML and a brief summary at the top are the easiest -to handle. +Current preference for suggesting changes is to open a pull request on +GitHub for the https://github.com/networkupstools/nut/ project. + +For some cases, small patches that arrive by mailing list in unified +format (`diff -u`) as plain text attachments with no HTML and a brief +summary at the top are easy to handle, but sadly also easy to overlook. If a patch is sent to the nut-upsdev mailing list, it stands a better chance of being seen immediately. However, it is likely to be dropped @@ -736,14 +750,21 @@ the driver and hardware. The same remark goes for device entries: if you add support for new models, please remember to also complete the hardware compatibility list, present -in data/driver.list.in. This will be used to generate both textual, static -HTML and dynamic searchable HTML for the website. +in link:data/driver.list.in[]. This will be used to generate both textual, +static HTML and dynamic searchable HTML for the website. Finally, don't forget about fame and glory: if you added or substantially updated a driver, your copyright belongs in the heading comment (along with existing ones). For vendor backed (or sponsored) contributions we -welcome an entry in the docs/acknowledgements.txt file as well, to track -and know the industry players who help make NUT better and more useful. +welcome an entry in the link:docs/acknowledgements.txt[] file as well, +to track and know the industry players who help make NUT better and more +useful. + +It is nice to update the link:NEWS[] file for significant development +to be seen as part of next release, as well as to update the +link:UPGRADING[] file for potentially breaking changes and similar +heads-up notes for third-party teams (distribution packagers, clients +and bindings, etc.) Source code management ---------------------- @@ -946,12 +967,13 @@ link:http://git-scm.com/course/svn.html[following link] may be of use. Building the Code ----------------- -For a developer, the NUT build process starts with `./autogen.sh`. This script -generates the `./configure` script that end users typically invoke to build -NUT. If you are making a number of changes to the NUT source tree, configuring -with the `--enable-maintainer-mode` flag will ensure that after you change -`Makefile.am`, the `Makefile.in` and `Makefile` get regenerated. At a -minimum, you will need: +For a developer, the NUT build process starts with `./autogen.sh`. + +This script generates the `./configure` script that end users typically +invoke to build NUT. If you are making a number of changes to the NUT +source tree, configuring with the `--enable-maintainer-mode` flag will +ensure that after you change a `Makefile.am`, nearby `Makefile.in` and +`Makefile` get regenerated. At a minimum, you will need at least: * autoconf * automake @@ -959,41 +981,52 @@ minimum, you will need: * Python * Perl -After running `./autogen.sh`, you can pass your local configuration options to -`./configure` and run `make` from the top-level directory. To avoid the need -for root privileges when testing new NUT code, you may wish to use -`--prefix=$HOME/local/nut --with-statepath=/tmp`. You can also keep -compilation times down by only building the driver you are currently working -on: `--with-drivers=driver1,dummy-ups`. +[NOTE] +====== +See the link:config-prereqs.txt[] for better detailed package lists for +different operating systems. + +See `ci_build.sh` for automating many practical scenarios, for easier +iterations. +====== -Before pushing your commits upstream, please run +make distcheck-light+. +Even if you do not use your distribution's packages of NUT, installing the +distribution's list of build dependencies for NUT can reduce the amount of +trial-and-error when installing dependencies. For instance, in Debian, you +can run `apt-get build-dep nut` to install all of the auto* tools as well +as any development libraries and headers. + +After running `./autogen.sh`, you can pass your local configuration +options to `./configure` and run `make` from the top-level directory. +To avoid the need for root privileges when testing new NUT code, you +may wish to use `--prefix=$HOME/local/nut --with-statepath=/tmp`. +You can also keep compilation times down by only building the driver +which you are currently working on: `--with-drivers=driver1,dummy-ups`. + +Before pushing your commits upstream, please run `make distcheck-light`. This checks that the Makefiles are not broken, that all the relevant files are distributed, and that there are no compilation or installation errors. Note that unless you specifically pass `--with-doc=skip` to `configure`, this requires all of the dependencies necessary to build the documentation -to be locally installed on your system, including asciidoc, a2x, xsltproc, -dblatex and any additional XSL stylesheets. - -Running +make distcheck-light+ is especially important if you have added or -removed files, or updated configure.ac or some Makefile.am. Remember: simply -adding a file to Git does not mean it will be distributed. To distribute a -file, you must update the corresponding Makefile.am. - -There is also +make distcheck+, which runs an even stricter set of -tests than +make distcheck-light+, but will not work unless you have all the -optional libraries and features installed. - -Finally note, that since 2017 the GitHub upstream project is monitored by -Travis CI (in addition to multi-platform buildbots which occasionally do not -work). This means that if your posted improvements are based on current NUT +to be locally installed on your system, including `asciidoc`, `a2x`, +`xsltproc`, `dblatex` and any additional XSL stylesheets. + +Running `make distcheck-light` is especially important if you have added or +removed files, or updated `configure.ac` or some `Makefile.am` file. +Remember: simply adding a file to Git does not mean it will be distributed. +To distribute a file, you must update the corresponding `Makefile.am` with +`EXTRA_DIST` entry and possibly other recipe handling. + +There is also `make distcheck`, which runs an even stricter set of +tests than `make distcheck-light`, but will not work unless you have all +of the optional third-party libraries and features installed. + +Finally note, that since 2017 the GitHub upstream project is monitored +by Travis CI (in addition to earlier multi-platform buildbots which +occasionally do not work), replaced since 2021 by a dedicated NUT CI farm. +This means that if your posted improvements are based on current NUT "master" branch, the resulting pull request should get tested for a number of scenarios automatically. If your code adds a substantial feature, consider -extending the `.travis.yml` and/or `ci_build.sh` scripts in the workspace -root to add another `BUILD_TYPE` to the matrix of tests run in parallel. - -Even if you do not use your distribution's packages of NUT, installing the -distribution's list of build dependencies for NUT can reduce the amount of -trial-and-error when installing dependencies. For instance, in Debian, you can -run `apt-get build-dep nut` to install all of the auto* tools as well as any -development libraries and headers. - +extending the `Jenkinsfile-dynamatrix` and/or `ci_build.sh` scripts in the +workspace root to add another `BUILD_TYPE` to the matrix of tests run in +parallel. From 5d14ee613509d169a8c33149f74c3698583bd58f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 20:39:50 +0200 Subject: [PATCH 466/700] Update nut.dict --- docs/nut.dict | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/nut.dict b/docs/nut.dict index db17ae2e87..eb2b831c7a 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2930 utf-8 +personal_ws-1.1 en 2939 utf-8 AAS ABI ACFAIL @@ -563,6 +563,7 @@ LINEV LISTINSTCMD LISTRW LISTVARS +LLNC LOADPCT LOCKFN LOCKNAME @@ -579,6 +580,9 @@ Laventhol Legrand Lepple Levente +LibGD +LibLTDL +LibUSB LineA LineB LineSens @@ -905,6 +909,7 @@ PresentStatus Priv Procomm ProductID +Proxmox Prynych Pulizzi PwrOut @@ -2032,6 +2037,7 @@ libneon libnss libnut libnutclient +libnutclientsub libnutconfig libnutscan libpng @@ -2231,6 +2237,7 @@ nobody's nobt nodtk noflag +nogroup nohang noimp noinst @@ -2310,6 +2317,7 @@ oq os ostream otherprotocols +otheruser outliers pF pacman @@ -2876,6 +2884,7 @@ wget whitespace wiki winnutclient +wmNUT wmnut wordformat workflow From f63f659cea6562ba140f67964ce1f5451c787093 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 20:50:52 +0200 Subject: [PATCH 467/700] docs/FAQ.txt: fix formatting, extend some notes --- docs/FAQ.txt | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/FAQ.txt b/docs/FAQ.txt index ff8038db67..9c59f100b4 100644 --- a/docs/FAQ.txt +++ b/docs/FAQ.txt @@ -177,8 +177,8 @@ Besides, if upsmon were rolled into upsd, upsd would get even bigger than it is now. You'd have one less process, but the RAM consumption would be pretty close to now. -See the "Data Room" section in docs/config-notes.txt for more configuration -ideas and explanations. +See the "Data Room" section in link:docs/config-notes.txt[] for +more configuration ideas and explanations. *Answer 2* @@ -305,7 +305,10 @@ that point. You *want* the system to die without reaching the part where the kernel tells it to shut down. A possible script might look like this: +------ # other shutdown stuff here (mount -o remount,ro ...) + # `upsmon -K` if available on still mounted filesystems + # at this point is more portable than the `test` below if (test -f /etc/killpower) then @@ -318,6 +321,7 @@ might look like this: fi halt -p +------ The other solution is to change your BIOS setting to "always power on" instead of "last state", assuming that's possible. @@ -349,14 +353,15 @@ cat some magic characters at /dev/adb to enable "server mode". This would instruct the system to reboot while unattended. From Usenet post <6boftzxz51.fsf@ecc-office.sp.cs.cmu.edu>: - +------ # Send packet over the ADB bus to the PowerMac CUDA chip # telling it to reboot automatically when power is restored # after a power failure. cat /etc/local/autoboot.adb > /dev/adb - autoboot.adb contains these three bytes (in hex): 01 13 01 + # autoboot.adb contains these three bytes (in hex): 01 13 01 +------ Later PowerPC Macs with a PMU and the appropriate kernel driver can achieve the same effect with the following command: @@ -442,7 +447,7 @@ update your startup scripts so it uses this procedure on your next boot. If you like this, you'll probably also find the chroot process to -be useful and interesting. See security.txt for more details. +be useful and interesting. See link:security.txt[] for more details. == What's the point of that 'security domains' concept above? @@ -452,8 +457,8 @@ to that one user account. Direct access to the serial device is not possible, since that is owned by another user. There is also the possibility of running the drivers and upsd in a -chroot jail. See the chroot.txt provided in the source -distribution for an example implementation. +chroot jail. See the chroot option in link:security.txt[], `upsd` +and driver documentation. Why give would-be vandals any sort of help? @@ -465,8 +470,8 @@ using this technique. You probably don't want to do this, since it doesn't maximize your runtime on battery. Assuming you have a good reason for it (see -the next entry), then look at scheduling.txt or the upssched(8) man -page for some ideas. +the next entry), then look at link:scheduling.txt[] or the +linkman:upssched[8] man page for some ideas. ///////////////////////////////////////////////////////////////// TODO: figure out how to link to the upssched man page above. @@ -494,7 +499,7 @@ with no visible interruption in service. If you purposely shut down early, you guarantee an interruption in service by bringing down the box. -See upssched.txt for information on how you can shutdown early if +See link:upssched.txt[] for information on how you can shutdown early if this is what you really want to do. == The CGI programs report 'access to that host is not authorized' -- what's going on? @@ -1037,6 +1042,10 @@ they won't be stuck in the halted state with the UPS running on line power. Implement this by modifying your shutdown script like this: +------ + # `upsmon -K` if available on still mounted filesystems + # at this point is more portable than the `test` below + if (test -f /etc/killpower) then /sbin/upsdrvctl shutdown @@ -1046,3 +1055,4 @@ Implement this by modifying your shutdown script like this: # uh oh, we never got shut down! (power race?) reboot fi +------ From bc92a94b1ba8d6f1fe21b23793bfa47f62923505 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:03:57 +0200 Subject: [PATCH 468/700] docs/net-protocol.txt: fix version table formatting --- docs/net-protocol.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/net-protocol.txt b/docs/net-protocol.txt index 4e9b744851..0af943381d 100644 --- a/docs/net-protocol.txt +++ b/docs/net-protocol.txt @@ -44,16 +44,16 @@ NUT network protocol, over the time: |1.1 |>= 1.5.0 |Original protocol (without old commands) .2+|1.2 .2+|>= 2.6.4 |Add "LIST CLIENTS" and "NETVER" commands |Add ranges of values for writable variables -.2+|1.3 .2+|>= 2.8.0 |Add "cmdparam" to "INSTCMD" +.4+|1.3 .4+|>= 2.8.0 |Add "cmdparam" to "INSTCMD" |Add "TRACKING" commands (GET, SET) |Add "PRIMARY" as alias to older "MASTER" - | (implementation tested to be backwards - | compatible in `upsd` and `upsmon`) + (implementation tested to be backwards + compatible in `upsd` and `upsmon`) |Add "PROTVER" as alias to older "NETVER" |=============================================================================== -NOTE: any new version of the protocol implies an update of NUT_NETVERSION -in *configure.ac*. +NOTE: Any new version of the protocol implies an update of `NUT_NETVERSION` +in 'configure.ac' file. GET From 9b37adc59b390f41fd020f1747edc8dda0293867 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:05:21 +0200 Subject: [PATCH 469/700] docs/new-clients.txt: fix sample source formatting --- docs/new-clients.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/new-clients.txt b/docs/new-clients.txt index 2e1f90cf96..1b07900071 100644 --- a/docs/new-clients.txt +++ b/docs/new-clients.txt @@ -51,6 +51,7 @@ and commands with an object-oriented API in C++ and C. For more information, refer to the linkman:libnutclient[3] manual page. +------ #include #include #include @@ -128,6 +129,7 @@ For more information, refer to the linkman:libnutclient[3] manual page. delete client; return 0; } +------ Configuration helpers @@ -182,6 +184,7 @@ of the results. The Perl class instance encapsulates a single UPS, where the Python class instance represents a connection to the server (which may service multiple UPS units). +------ use UPS::Nut; $ups = new UPS::Nut( NAME => "myups", @@ -204,10 +207,10 @@ multiple UPS units). ; print $other_ups{MFR}, " ", $other_ups{MODEL}, "\n"; +------ Java ---- The NUT Java support has been externalized. It is available at https://github.com/networkupstools/jnut - From 76e82ee89b2b5bf6e096ada0f6001e45a708ab56 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:13:23 +0200 Subject: [PATCH 470/700] docs/nut-names.txt: fix table formatting --- docs/nut-names.txt | 55 ++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/docs/nut-names.txt b/docs/nut-names.txt index 8ff3a9ddd9..efe1ae28f4 100644 --- a/docs/nut-names.txt +++ b/docs/nut-names.txt @@ -87,11 +87,14 @@ during a transition period. The ups.* data will then be removed. | device.macaddr | Physical network address of the device | 68:b5:99:f5:89:27 | device.uptime | Device uptime in seconds | 1782 | device.count | Total number of daisychained devices | 1 -|===================================================================================== +|==================================================================================== -NOTE: When present, device.count implies daisychain support. For more +[NOTE] +====== +When present, `device.count` implies daisychain support. For more information, refer to the <> chapter of the user manual and developer guide. +====== ups: General unit information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -172,10 +175,11 @@ ups: General unit information shutdown ability (poweroff) | enabled |=============================================================================== -NOTE: When present, the value of *ups.start.auto* has an impact on shutdown.* -commands. For the sake of coherence, shutdown commands will set *ups.start.auto* -to the right value before issuing the command. That is, shutdown.stayoff will first -set *ups.start.auto* to *no*, while shutdown.return will set it to *yes*. +NOTE: When present, the value of `ups.start.auto` has an impact on +`shutdown.*` commands. For the sake of coherence, shutdown commands +will set `ups.start.auto` to the right value before issuing the command. +That is, `shutdown.stayoff` will first set `ups.start.auto` to `no`, +while `shutdown.return` will set it to `yes`. NOTE: When possible, time-stamps and dates should be expressed as detailed above in the Time and Date format chapter. @@ -275,22 +279,23 @@ broken down to their base components. Phase Count Determination ^^^^^^^^^^^^^^^^^^^^^^^^^ -input.phases (3 for three-phase, absent or 1 for 1phase) -output.phases (as for input.phases) +`input.phases` (3 for three-phase, absent or 1 for 1phase) + +`output.phases` (as for `input.phases`) DOMAINs ^^^^^^^ Any input or output is considered a valid DOMAIN. -input (should really be called input.mains, but keep this for compat) -input.bypass -input.servicebypass +* input (should really be called input.mains, but keep this for compat) +** input.bypass +** input.servicebypass -output (should really be called output.load, but keep this for compat) -output.bypass -output.inverter -output.servicebypass +* output (should really be called output.load, but keep this for compat) +** output.bypass +** output.inverter +** output.servicebypass Specification (SPEC) ^^^^^^^^^^^^^^^^^^^^ @@ -299,6 +304,7 @@ Voltage, current, frequency, etc are considered to be a specification of the measurement. With this notation, the old 1phase naming scheme becomes DOMAIN.SPEC + Example: `input.current` CONTEXT @@ -309,6 +315,7 @@ measurements in more detail. We call this the CONTEXT. With this notation, the naming scheme becomes DOMAIN.CONTEXT.SPEC when in three-phase mode. + Example: `input.L1.current` Valid CONTEXTs @@ -469,15 +476,15 @@ battery: Any battery details |=============================================================================== NOTE: -battery.charger.status replaces the historic flags CHRG and DISCHRG that were -exposed through ups.status. battery.charger.status can have one of the -following value: - -- charging: battery is charging, -- discharging: battery is discharging, -- floating: battery has completed its charge cycle, and waiting to go to resting -mode, -- resting: the battery is fully charged, and not charging nor discharging. +`battery.charger.status` replaces the historic flags `CHRG` and `DISCHRG` +that were exposed through `ups.status`. +The `battery.charger.status` can have one of the following values: + +- `charging`: battery is charging, +- `discharging`: battery is discharging, +- `floating`: battery has completed its charge cycle, + and waiting to go to resting mode, +- `resting`: the battery is fully charged, and not charging nor discharging. NOTE: When possible, time-stamps and dates should be expressed as detailed above in the Time and Date format chapter. From 98f4015c984f5c059cc177bfea3341352d8d4155 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:14:50 +0200 Subject: [PATCH 471/700] Fix remaining links to asciidoc.org --- TODO | 2 +- docs/nut-qa.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index 20cbbded5b..81a601f2d6 100644 --- a/TODO +++ b/TODO @@ -13,7 +13,7 @@ Roadmap ^^^ This release is focused on the website and documentation rewrite, using -the excellent link:http://www.methods.co.nz/asciidoc[AsciiDoc]. +the excellent link:https://asciidoc.org/[AsciiDoc]. 2.8 ^^^ diff --git a/docs/nut-qa.txt b/docs/nut-qa.txt index 8d237d182d..c5cb5207fa 100644 --- a/docs/nut-qa.txt +++ b/docs/nut-qa.txt @@ -11,7 +11,7 @@ Documentation ------------- The documentation toolchain uses -link:http://www.methods.co.nz/asciidoc/[AsciiDoc] to output both HTML pages and +link:https://asciidoc.org/[AsciiDoc] to output both HTML pages and manual pages (troff). This single point of control fills many gaps, suppresses many redundancies, and optimizes documentation management in general. From 86f47eb9e02d592efc7744c392fd64bafecfec5f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:19:26 +0200 Subject: [PATCH 472/700] docs/nut-qa.txt: list a few more tools we use --- docs/nut-qa.txt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/nut-qa.txt b/docs/nut-qa.txt index c5cb5207fa..b4b69167a0 100644 --- a/docs/nut-qa.txt +++ b/docs/nut-qa.txt @@ -30,8 +30,9 @@ link:http://aspell.net[Aspell], both interactively (using 'make spellcheck-interactive') and automatically in Buildbot (using 'make spellcheck'). -NOTE: A NUT dictionary is also available (docs/nut.dict), providing a glossary -of terms related to power devices and management. +NOTE: A NUT dictionary is also available (docs/nut.dict), providing a +glossary of terms related to power devices and management, as well as +partial terms, technical jargon and author names. Source code ----------- @@ -67,7 +68,9 @@ mailing list. //////////////////////////////////////////////////////////////////////////////// - link:http://buildbot.networkupstools.org/public/nut/[Buildbot] - and the new dedicated Jenkins incarnation of the NUT CI Farm with "legacy UI" + for older take on multi-platform builds, and the + +- new dedicated Jenkins incarnation of the NUT CI Farm with "legacy UI" for link:https://ci.networkupstools.org/job/nut/job/nut/job/master/[main branch] and link:https://ci.networkupstools.org/job/nut/job/nut/view/change-requests/[PRs], also accessible at the slower but slicker-looking Blue Ocean user interface for @@ -89,9 +92,12 @@ mailing list, and fixed quickly. - a project portal with trackers for bugs, feature request, patches and tasks +- LGTM.COM automatically checking C/C++ and Python code + - Static code analysis: * link:https://scan.coverity.com/projects/networkupstools-nut[Coverity Scan overview of NUT] * status: image:https://scan.coverity.com/projects/8829/badge.svg[Coverity Scan Build Status] + * cppcheck as part of NUT CI farm builds and reports NUT QA also relies on external tools and trackers, like: From ff94c8b34d99712a78cf261818321578f291c507 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:26:20 +0200 Subject: [PATCH 473/700] docs/packager-guide.txt: reshuffle known names --- docs/packager-guide.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/packager-guide.txt b/docs/packager-guide.txt index b9175969a6..0a65fb46d6 100644 --- a/docs/packager-guide.txt +++ b/docs/packager-guide.txt @@ -106,7 +106,10 @@ Packagers involved The following packagers are working on this subject: - Debian (and derivatives): Arnaud Quette -- SUSE/Novell: Stanislav Brabec +- SUSE/Novell: Stanislav Brabec +- Solaris, OpenSolaris, OpenIndiana and related illumos distributions: + Jim Klimov +- MacOS: Charles Lepple NOTE: the people below should be contacted to (re)launch discussions! @@ -120,9 +123,7 @@ The following packagers should be interested in working on this subject: - OpenBSD: <> - PLD: Andrzej Zawadzki - E-Smith: Charlie Brady -- Solaris, OpenSolaris, OpenIndiana and related illumos distributions: Jim Klimov - Windows: check with WinNUT author?! -- MacOS: <> => Charles Lepple? <> - HP-UX: <> - IBM AIX: <> @@ -406,4 +407,3 @@ html-path ... ------------------------------------------------------------------------ - From 331aa7cfdda1bd470c629857e48cb5afee830eb9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:26:35 +0200 Subject: [PATCH 474/700] docs/snmp-subdrivers.txt: small typo --- docs/snmp-subdrivers.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/snmp-subdrivers.txt b/docs/snmp-subdrivers.txt index f010ff8960..d71c35f628 100644 --- a/docs/snmp-subdrivers.txt +++ b/docs/snmp-subdrivers.txt @@ -226,7 +226,7 @@ displays the textual OID name. For example, the following entry: /* upsMIB.upsObjects.upsIdent.upsIdentModel = STRING: "Dell UPS Tower 1920W HV" */ { "unmapped.upsidentmodel", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.1.0", NULL, SU_FLAG_OK, NULL }, -Many time, only the first field will need to be modified, to map to an actual +Many times, only the first field will need to be modified, to map to an actual NUT variable name. Check the <> section first From dc9b9a8c091edd10c1fa880c0071922f65c11fe7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:26:48 +0200 Subject: [PATCH 475/700] docs/sock-protocol.txt: fix formatting --- docs/sock-protocol.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/sock-protocol.txt b/docs/sock-protocol.txt index e4701e5234..32cd5bf2e7 100644 --- a/docs/sock-protocol.txt +++ b/docs/sock-protocol.txt @@ -185,6 +185,7 @@ INSTCMD INSTCMD load.on 10 TRACKING 1bd31808-cb49-4aec-9d75-d056e6f018d2 NOTE: + * is an additional and optional parameter for the command, * "TRACKING " can be provided to track commands execution status, if TRACKING was set to ON on upsd. In this case, driver will later return @@ -199,6 +200,7 @@ SET SET ups.id "Data room" TRACKING 2dedb58a-3b91-4fab-831f-c8af4b90760a NOTE: + * "TRACKING " can be provided to track commands execution status, if TRACKING was set to ON on upsd. In this case, driver will later return the execution status, using TRACKING. From 28813ff0ccb7c2ebcdb2f1f89f2463c2324e6c40 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:29:02 +0200 Subject: [PATCH 476/700] docs/man/adelsystem_cbi.txt: fix formatting --- docs/man/adelsystem_cbi.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/man/adelsystem_cbi.txt b/docs/man/adelsystem_cbi.txt index ef22e5caa7..e8d1bd9e03 100644 --- a/docs/man/adelsystem_cbi.txt +++ b/docs/man/adelsystem_cbi.txt @@ -55,8 +55,6 @@ Modbus: An integer specifying the device modbus slave ID (default 1). ----- - CONFIGURATION ------------- From 8022ad74c437513df5dbe4e8feee14ed83860e28 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:34:26 +0200 Subject: [PATCH 477/700] docs/man/blazer-common.txt, masterguard.txt, bestups.txt: fix formatting [#1361] --- docs/man/bestups.txt | 2 +- docs/man/blazer-common.txt | 3 +-- docs/man/masterguard.txt | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/man/bestups.txt b/docs/man/bestups.txt index b5557d7da9..f2904bf2d1 100644 --- a/docs/man/bestups.txt +++ b/docs/man/bestups.txt @@ -21,7 +21,7 @@ new development. If it works for managing your devices -- fine, but if you are running it to try setting up a new device, please consider the newer linkman:nutdrv_qx[8] instead, which should handle all 'Q*' protocol variants for NUT. -+ + Please do also report if your device works with this driver, but linkman:nutdrv_qx[8] would not actually support it with any subdriver! diff --git a/docs/man/blazer-common.txt b/docs/man/blazer-common.txt index 615bfd9b69..dc53dc8feb 100644 --- a/docs/man/blazer-common.txt +++ b/docs/man/blazer-common.txt @@ -13,7 +13,7 @@ new development. If it works for managing your devices -- fine, but if you are running it to try setting up a new device, please consider the newer linkman:nutdrv_qx[8] instead, which should handle all 'Q*' protocol variants for NUT. -+ + Please do also report if your device works with this driver, but linkman:nutdrv_qx[8] would not actually support it with any subdriver! @@ -338,4 +338,3 @@ Internet Resources: * The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ * The NUT HCL: http://www.networkupstools.org/stable-hcl.html - diff --git a/docs/man/masterguard.txt b/docs/man/masterguard.txt index 81ddc512fc..373fc2f450 100644 --- a/docs/man/masterguard.txt +++ b/docs/man/masterguard.txt @@ -21,7 +21,7 @@ new development. If it works for managing your devices -- fine, but if you are running it to try setting up a new device, please consider the newer linkman:nutdrv_qx[8] instead, which should handle all 'Q*' protocol variants for NUT. -+ + Please do also report if your device works with this driver, but linkman:nutdrv_qx[8] would not actually support it with any subdriver! From 6b701438dd58735ef004aa82abbc0afe09306eb4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:35:36 +0200 Subject: [PATCH 478/700] docs/man/clone.txt: fix sample source formatting --- docs/man/clone.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/man/clone.txt b/docs/man/clone.txt index e9331f8244..42780e56cd 100644 --- a/docs/man/clone.txt +++ b/docs/man/clone.txt @@ -66,6 +66,7 @@ IMPLEMENTATION The port specification in the linkman:ups.conf[5] reference the driver socket that the "real" UPS driver is using. For example: +------ [realups] driver = usbhid-ups port = auto @@ -77,6 +78,7 @@ socket that the "real" UPS driver is using. For example: load.off = outlet.1.load.off load.status = outlet.1.status [...] +------ IMPORTANT --------- From 09f4902e36b88ce48b5811ca7a6f578f7400ec86 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:40:50 +0200 Subject: [PATCH 479/700] docs/man/liebert-esp2.txt: fix formatting --- docs/man/liebert-esp2.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/man/liebert-esp2.txt b/docs/man/liebert-esp2.txt index 8d96c82c31..be57914f14 100644 --- a/docs/man/liebert-esp2.txt +++ b/docs/man/liebert-esp2.txt @@ -27,6 +27,7 @@ SUPPORTED HARDWARE ------------------ Tested to work on the following units: + * Liebert GXT2-6000RT208 This is an experimental driver. You have been warned. From d553af3f035c24211838b06de1c66536f3fd6acd Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:42:55 +0200 Subject: [PATCH 480/700] docs/man/mge-utalk.txt: fix formatting --- docs/man/mge-utalk.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/man/mge-utalk.txt b/docs/man/mge-utalk.txt index 3e862f50d2..4dd0384b02 100644 --- a/docs/man/mge-utalk.txt +++ b/docs/man/mge-utalk.txt @@ -22,14 +22,14 @@ SUPPORTED HARDWARE mge-utalk supports the following legacy units, using the MGE UTalk protocol: - Pulsar ESV+, - Pulsar ES+, - Pulsar EL, - Pulsar EX, - Pulsar EXtreme, - Comet EXtreme, - Comet (Utalk Serial Card, ref 66060), - Galaxy (Utalk Serial Card, ref 66060). +* Pulsar ESV+ +* Pulsar ES+ +* Pulsar EL +* Pulsar EX +* Pulsar EXtreme +* Comet EXtreme +* Comet (Utalk Serial Card, ref 66060) +* Galaxy (Utalk Serial Card, ref 66060) This driver also support some newer models with backward UTalk compatibility, such as Pulsar Evolution and Pulsar EXtreme C. As these models also support From 737342a8bc9e7e3cebb2fd1f6aacbbad64278a4b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:44:38 +0200 Subject: [PATCH 481/700] docs/man/nut.conf.txt: fix sample source formatting --- docs/man/nut.conf.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/man/nut.conf.txt b/docs/man/nut.conf.txt index f8157ee135..7da7bdf36c 100644 --- a/docs/man/nut.conf.txt +++ b/docs/man/nut.conf.txt @@ -83,6 +83,7 @@ Please read http://bugs.debian.org/358696 for more details. EXAMPLE ------- +------ # /etc/nut/nut.conf. See nut.conf(5) MODE=none @@ -92,6 +93,7 @@ EXAMPLE UPSMON_OPTIONS="" # POWEROFF_WAIT=15m +------ INTEGRATION ----------- From 34a2a4b42699610c406f7480c77d9afaaa41b812 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:47:33 +0200 Subject: [PATCH 482/700] docs/man/nutdrv_atcl_usb.txt: fix formatting --- docs/man/nutdrv_atcl_usb.txt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/man/nutdrv_atcl_usb.txt b/docs/man/nutdrv_atcl_usb.txt index bbd8d7d376..b81d8eb716 100644 --- a/docs/man/nutdrv_atcl_usb.txt +++ b/docs/man/nutdrv_atcl_usb.txt @@ -17,13 +17,17 @@ SUPPORTED HARDWARE This driver is for UPS hardware which identifies itself as USB idVendor 0001 and idProduct 0000, and iManufacturer +ATCL FOR UPS+. Known manufacturers -include Kanji and Plexus. The UPS interface seems to be a generic USB-to-serial -chip, and for hardware manufactured by Kanji and Plexus, the microcontroller +include Kanji and Plexus. + +The UPS interface seems to be a generic USB-to-serial chip, and for +hardware manufactured by Kanji and Plexus, the microcontroller appears to emulate a traditional contact-closure interface. This translates into only three states in ups.status: *OL*, *OB* and *OB LB* (similar to -linkman:genericups[8]), with no other dynamic status values reported. Note that -these USB identifiers (including the iManufacturer string) have also been seen -on devices that are supported by the `fuji` subdriver of linkman:nutdrv_qx[8]. +linkman:genericups[8]), with no other dynamic status values reported. + +Note that these USB identifiers (including the iManufacturer string) +have also been seen on devices that are supported by the `fuji` +subdriver of linkman:nutdrv_qx[8]. EXTRA ARGUMENTS --------------- From 55107f6f53f4819e7080ea27987d6ada3ecffa9c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:49:21 +0200 Subject: [PATCH 483/700] docs/man/nutdrv_qx.txt: fix formatting --- docs/man/nutdrv_qx.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/man/nutdrv_qx.txt b/docs/man/nutdrv_qx.txt index b2b8a6da1f..b3f9c3a01b 100644 --- a/docs/man/nutdrv_qx.txt +++ b/docs/man/nutdrv_qx.txt @@ -21,6 +21,7 @@ The *nutdrv_qx* driver is known to work with various UPSes from 'Armac', 'Blazer', 'Energy Sistem', 'Fenton Technologies', 'General Electric', 'Hunnox', 'Masterguard', 'Mustek', 'Powercool', 'Voltronic Power' (rebranded by many, many - have I said many? - others... + Long story short: if your UPS came with a software called 'Viewpower', chances are high that it works with this driver with one of the <<_extra_arguments,'voltronic*' protocols or with the 'mecer' one>>), @@ -794,13 +795,13 @@ If you want to know the explanation of that bit you can either watch the log or AUTHORS ------- -Daniele Pezzini , -Arnaud Quette , -John Stamp , -Peter Selinger , -Arjen de Korte , -Alexander Gordeev , -Edgar Fuß +* Daniele Pezzini +* Arnaud Quette +* John Stamp +* Peter Selinger +* Arjen de Korte +* Alexander Gordeev +* Edgar Fuß SEE ALSO @@ -823,4 +824,3 @@ Internet Resources: The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ The NUT HCL: http://www.networkupstools.org/stable-hcl.html - From 3cbe7f1712d5bbf018ffd0c7771634e1114d5ce8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:52:38 +0200 Subject: [PATCH 484/700] docs/man/pijuice.txt: fix formatting --- docs/man/pijuice.txt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/man/pijuice.txt b/docs/man/pijuice.txt index 761f62b396..801646af60 100644 --- a/docs/man/pijuice.txt +++ b/docs/man/pijuice.txt @@ -79,11 +79,10 @@ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -Initial pull requests adding this driver: +* Initial pull requests adding this driver: +** https://github.com/networkupstools/nut/pull/730 +** https://github.com/PiSupply/PiJuice/issues/124 -* https://github.com/networkupstools/nut/pull/730 -* https://github.com/PiSupply/PiJuice/issues/124 +* Product home page: https://uk.pi-supply.com/products/pijuice-standard -Product home page: https://uk.pi-supply.com/products/pijuice-standard - -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ From 68fd3dc8d1e7523c11558f2769aa273e42034270 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 21:56:03 +0200 Subject: [PATCH 485/700] docs/man/Makefile.am: list the socomec_jbus.txt --- docs/man/Makefile.am | 3 +++ docs/nut.dict | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 11e1597b16..96b354427b 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -621,11 +621,13 @@ HTML_MACOSX_MANS = macosx-ups.html SRC_MODBUS_PAGES = phoenixcontact_modbus.txt \ generic_modbus.txt \ huawei-ups2000.txt \ + socomec_jbus.txt \ adelsystem_cbi.txt if WITH_MANS MAN_MODBUS_PAGES = phoenixcontact_modbus.8 \ generic_modbus.8 \ huawei-ups2000.8 \ + socomec_jbus.8 \ adelsystem_cbi.8 endif @@ -636,6 +638,7 @@ endif HTML_MODBUS_MANS = phoenixcontact_modbus.html \ generic_modbus.html \ huawei-ups2000.html \ + socomec_jbus.html \ adelsystem_cbi.html SRC_LINUX_I2C_PAGES = asem.txt pijuice.txt diff --git a/docs/nut.dict b/docs/nut.dict index eb2b831c7a..4acd5cde31 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2939 utf-8 +personal_ws-1.1 en 2942 utf-8 AAS ABI ACFAIL @@ -241,6 +241,7 @@ DEBUGOUT DELCMD DELENUM DELINFO +DELPHYS DELRANGE DES DESTDIR @@ -277,6 +278,7 @@ DiSplay Diehl Dietze Digitus +Digys Dimitris Dly Dmitry @@ -624,6 +626,7 @@ MQ MSI MSII MSIII +MX MacKenzie's MacOS Maccelari From a0aef5e9063a6fc33e0a93eaacb94b9223794e8c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 22:02:14 +0200 Subject: [PATCH 486/700] docs/man/socomec_jbus.txt: fix formatting and typos --- docs/man/socomec_jbus.txt | 46 ++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/docs/man/socomec_jbus.txt b/docs/man/socomec_jbus.txt index ef913099c3..bc192d634e 100644 --- a/docs/man/socomec_jbus.txt +++ b/docs/man/socomec_jbus.txt @@ -1,5 +1,5 @@ -Socomec_jbus(8) -================== +SOCOMEC_JBUS(8) +=============== NAME ---- @@ -28,10 +28,10 @@ UPS with the following characteristics. 2. Connection: RS-232 -These are typically provided with a Netvision WEB/SNMP management card / external -box that would be better served by the linkman:snmp-ups[8] driver. -In case netvision isn't available, you can hook up the UPS directly via the -serial port and use this driver. +These are typically provided with a Netvision WEB/SNMP management +card / external box that would be better served by the linkman:snmp-ups[8] +driver. In case netvision isn't available, you can hook up the UPS directly +via the serial port and use this driver. Currently, it has only been tested on the following model. @@ -67,16 +67,17 @@ operating system. INSTALLATION ------------ -This driver should be built by default if libmodbus and development headers are -available. You can force the configure script to build it with the following -arguments: +This driver should be built by default if libmodbus and development headers +are available. You can force the `configure` script to build it with the +following arguments: configure --with-serial=yes --with-modbus=yes -You also need to give proper (R/W) permissions on the local serial device file -to allow the NUT driver run-time user to access it. This may need additional -setup for start-up scripting, udev or upower rules, to apply the rights on every -boot -- especially if your device nodes are tracked by a virtual filesystem. +You also need to give proper (R/W) permissions on the local serial device +file to allow the NUT driver run-time user to access it. This may need +additional setup for start-up scripting, udev or upower rules, to apply +the rights on every boot -- especially if your device nodes are tracked +by a virtual filesystem. For example, a USB-to-serial converter can be identified as `/dev/ttyACM0` or `/dev/ttyUSB0` on Linux, or `/dev/ttyU0` on FreeBSD (note the capital "U"). @@ -94,22 +95,22 @@ VARIABLES This driver does not support writable runtime variables (see linkman:upsrw[8]): for the same reasons. Both should be trivial to implement, but since I've already found one or two -inconsistencies in the documentation, I'm witholding from trying them. +inconsistencies in the documentation, I'm withholding from trying them. KNOWN ISSUES AND BUGS --------------------- -Well, it is an alpha release at best, but so far appears to report the UPS status -reliably. Mostly based on the work of Yifeng Li on the huawei-ups2000 -in that very same source tree. +Well, it is an alpha release at best, but so far appears to report the UPS +status reliably. Mostly based on the work of Yifeng Li on +the huawei-ups2000 in that very same source tree. Read failure on some JBUS addresses ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The driver polls all documented JBUS addresses and it is quite possible that your -UPS model does not support one of them. (eg the Digys does not support address -0x1020 which should provide the current UPS status). -This should be logged with LOG_ERR from modbus_read_input_registers() +The driver polls all documented JBUS addresses and it is quite possible +that your UPS model does not support one of them (e.g. the Digys does not +support address 0x1020 which should provide the current UPS status). +This should be logged with LOG_ERR from modbus_read_input_registers() along with the address that produced the error. Data stale @@ -120,7 +121,8 @@ and trigger a "data stale" error. Once a data stale error has occurred, you should see error messages similar to the example below in the system log. - socomec_jbus: modbus_read_input_registers(addr:XXXX, count:Z): Illegal data address + socomec_jbus: modbus_read_input_registers(addr:XXXX, count:Z): + Illegal data address upsd: Data for UPS [socomec] is stale - check driver upsd: UPS [socomec] data is no longer stale From 7b189d246882b46d8f3cba7d20a312c0a165de3c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 22:03:09 +0200 Subject: [PATCH 487/700] docs/man/snmp-ups.txt: fix sample source formatting --- docs/man/snmp-ups.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/man/snmp-ups.txt b/docs/man/snmp-ups.txt index 3a9b0ea513..4179d8db01 100644 --- a/docs/man/snmp-ups.txt +++ b/docs/man/snmp-ups.txt @@ -182,6 +182,7 @@ EXAMPLES The hostname of the UPS is specified with the "port" value in `ups.conf`, and may include a non-standard (161) remote peer port: +------ [snmpv1] driver = snmp-ups port = snmp-ups.example.com @@ -199,6 +200,7 @@ The hostname of the UPS is specified with the "port" value in authPassword = myauthenticationpassphrase privPassword = myprivatepassphrase desc = "Example SNMP v3 device, with the highest security level" +------ AUTHORS ------- From 955522f4bb412f6943d628e65263840e05f75326 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 22:04:00 +0200 Subject: [PATCH 488/700] docs/man/riello_usb.txt: fix formatting --- docs/man/riello_usb.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/man/riello_usb.txt b/docs/man/riello_usb.txt index 73bf5e9f3a..1c4ffd6e43 100644 --- a/docs/man/riello_usb.txt +++ b/docs/man/riello_usb.txt @@ -1,5 +1,5 @@ RIELLO_USB(8) -=========== +============= NAME ---- From 2ee91931ede5b6eef089f333f1e2abbceb4a023b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 22:04:22 +0200 Subject: [PATCH 489/700] docs/man/riello_ser.txt: fix formatting --- docs/man/riello_ser.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/man/riello_ser.txt b/docs/man/riello_ser.txt index 1b75c1add5..cbc326b0a6 100644 --- a/docs/man/riello_ser.txt +++ b/docs/man/riello_ser.txt @@ -1,5 +1,5 @@ RIELLO_SER(8) -=========== +============= NAME ---- From d2f5ef2d37dd912335bba90cb057d023f4ce52d7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 22:06:33 +0200 Subject: [PATCH 490/700] docs/man/tripplite_usb.txt: fix formatting --- docs/man/tripplite_usb.txt | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/docs/man/tripplite_usb.txt b/docs/man/tripplite_usb.txt index 134788199f..cad5a80f22 100644 --- a/docs/man/tripplite_usb.txt +++ b/docs/man/tripplite_usb.txt @@ -16,16 +16,19 @@ SYNOPSIS SUPPORTED HARDWARE ------------------ -This driver should work with older Tripp Lite UPSes which are detected as USB -HID-class devices, but are not true HID Power-Device Class devices. So far, -the devices supported by tripplite_usb have product ID 0001, and the newer -units (such as those with "LCD" in the model name) with product ID 2001 require -the linkman:usbhid-ups[8] driver instead. Please report success or failure to -the nut-upsuser mailing list. A key piece of information is the protocol -number, returned in `ups.firmware.aux`. Also, be sure to turn on debugging ('-DDD') -for more informative log messages. If your Tripp Lite UPS uses a serial port, -you may wish to investigate the linkman:tripplite[8] or linkman:tripplitesu[8] -driver. +This driver should work with older Tripp Lite UPSes which are detected +as USB HID-class devices, but are not true HID Power-Device Class devices. +So far, the devices supported by `tripplite_usb` have product ID 0001, +and the newer units (such as those with "LCD" in the model name) with +product ID 2001 require the linkman:usbhid-ups[8] driver instead. + +Please report success or failure to the nut-upsuser mailing list. +A key piece of information is the protocol number, returned in +`ups.firmware.aux`. Also, be sure to turn on debugging ('-DDD') +for more informative log messages. + +If your Tripp Lite UPS uses a serial port, you may wish to investigate +the linkman:tripplite[8] or linkman:tripplitesu[8] drivers. This driver has been tested with the following models: @@ -187,4 +190,3 @@ Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - From ec1f3ad4f91f47d71cfd7ae5698399bf2a08c078 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 22:14:51 +0200 Subject: [PATCH 491/700] docs/man/upsmon.txt: reword a dangling sentence --- docs/man/upsmon.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/man/upsmon.txt b/docs/man/upsmon.txt index 9e65fa5466..cb202fcc33 100644 --- a/docs/man/upsmon.txt +++ b/docs/man/upsmon.txt @@ -175,7 +175,7 @@ also receives the notification message (see below) as the first (and only) argument, so you can deliver a pre-formatted message too. Note that the NOTIFYCMD will only be called for a given event when you set -the EXEC flag by using the notify flags, below: +the EXEC flag by using the notify flags, as detailed below. NOTIFY FLAGS ------------ From 12886ed20810919dfbaf2a4e6b438ff00c124638 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 22:21:15 +0200 Subject: [PATCH 492/700] docs/man/upscli*.txt: fix sample source formatting --- docs/man/upscli_get.txt | 8 ++++++-- docs/man/upscli_init.txt | 2 +- docs/man/upscli_list_start.txt | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/man/upscli_get.txt b/docs/man/upscli_get.txt index 68d221d62f..28711d3c0c 100644 --- a/docs/man/upscli_get.txt +++ b/docs/man/upscli_get.txt @@ -46,12 +46,14 @@ QUERY FORMATTING To generate a request for `GET NUMLOGINS su700`, you would populate query and numq as follows: +------ size_t numq; const char *query[2]; query[0] = "NUMLOGINS"; query[1] = "su700"; numq = 2; +------ All escaping of special characters and quoting of elements with spaces is handled for you inside this function. @@ -59,17 +61,19 @@ is handled for you inside this function. ANSWER FORMATTING ----------------- -The raw response from upsd to the above query would be `NUMLOGINS su700 1`. +The raw response from `upsd` to the above query would be `NUMLOGINS su700 1`. Since this is split up for you, the values work out like this: +------ size_t numa; numa = 3; answer[0] = "NUMLOGINS" answer[1] = "su700" answer[2] = "1" +------ -Notice that the value which you seek typically starts at answer[numq]. +Notice that the value which you seek typically starts at `answer[numq]`. ERROR CHECKING -------------- diff --git a/docs/man/upscli_init.txt b/docs/man/upscli_init.txt index 1bf1343617..156f7672cc 100644 --- a/docs/man/upscli_init.txt +++ b/docs/man/upscli_init.txt @@ -29,7 +29,7 @@ the same hash value (thus file name), the ".0" can be incremented to ".1" and so on, as needed. The bash command for creating links in this manner would be: -ln -s ca.pem ./$(openssl x509 -hash -noout -in ca.pem).0 + ln -s ca.pem ./$(openssl x509 -hash -noout -in ca.pem).0 Alternatively, the c_rehash utility (provided by openssl-perl) can take a directory and iterate it to link all certificates found in that directory, diff --git a/docs/man/upscli_list_start.txt b/docs/man/upscli_list_start.txt index b6b9b62817..c564c36e0e 100644 --- a/docs/man/upscli_list_start.txt +++ b/docs/man/upscli_list_start.txt @@ -47,12 +47,14 @@ To see the list of variables on a UPS called 'su700', the protocol command would be `LIST VAR su700`. To start that list with this function, you would populate query and numq as follows: +------ size_t numq; const char *query[2]; query[0] = "VAR"; query[1] = "su700"; numq = 2; +------ All escaping of special characters and quoting of elements with spaces are handled for you inside this function. @@ -80,4 +82,3 @@ linkman:upscli_fd[3], linkman:upscli_get[3], linkman:upscli_readline[3], linkman:upscli_sendline[3], linkman:upscli_ssl[3], linkman:upscli_strerror[3], linkman:upscli_upserror[3] - From 6a74e79ba407460503669267c47399de0d83000b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 14 Apr 2022 22:27:43 +0200 Subject: [PATCH 493/700] docs/man/nutscan*.txt: fix formatting and typos --- docs/man/nutscan_display_parsable.txt | 17 +++++++++------ docs/man/nutscan_display_ups_conf.txt | 7 ++++-- docs/man/nutscan_free_device.txt | 7 ++++-- docs/man/nutscan_get_serial_ports_list.txt | 25 ++++++++++++++++------ 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/docs/man/nutscan_display_parsable.txt b/docs/man/nutscan_display_parsable.txt index 904cb4a631..e395a2b431 100644 --- a/docs/man/nutscan_display_parsable.txt +++ b/docs/man/nutscan_display_parsable.txt @@ -4,7 +4,8 @@ NUTSCAN_DISPLAY_PARSABLE(3) NAME ---- -nutscan_display_parsable - Display the specified `nutscan_device_t` structure on stdout. +nutscan_display_parsable - Display the specified `nutscan_device_t` +structure on stdout. SYNOPSIS -------- @@ -16,13 +17,17 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_display_parsable()* function displays all NUT devices in 'device' to stdout. It displays them in a way that can be easily parsed which is: +The *nutscan_display_parsable()* function displays all NUT devices in +'device' to stdout. It displays them in a way that can be easily parsed +which is: -:driver="",port=""[,="",="",...] + :driver="",port=""[,="",="",...] - may be one of USB, SNMP, XML, NUT, IPMI or AVAHI. - is the name of the driver's binary corresponding to this device. - and depend on , see the corresponding driver's man page. +* may be one of USB, SNMP, XML, NUT, IPMI or AVAHI. +* is the name of the driver's binary corresponding + to this device. +* and depend on , + see the corresponding driver's man page. SEE ALSO -------- diff --git a/docs/man/nutscan_display_ups_conf.txt b/docs/man/nutscan_display_ups_conf.txt index 7a3e34a750..d95f47a7b4 100644 --- a/docs/man/nutscan_display_ups_conf.txt +++ b/docs/man/nutscan_display_ups_conf.txt @@ -4,7 +4,8 @@ NUTSCAN_DISPLAY_UPS_CONF(3) NAME ---- -nutscan_display_ups_conf - Display the specified `nutscan_device_t` structure on stdout. +nutscan_display_ups_conf - Display the specified `nutscan_device_t` +structure on stdout. SYNOPSIS -------- @@ -16,7 +17,9 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_display_ups_conf()* function displays all NUT devices in 'device' to stdout. It displays them in a way that it can be directly copied into the ups.conf file. +The *nutscan_display_ups_conf()* function displays all NUT devices in +'device' to stdout. It displays them in a way that it can be directly +copied into the 'ups.conf' file. SEE ALSO -------- diff --git a/docs/man/nutscan_free_device.txt b/docs/man/nutscan_free_device.txt index a9177feb6c..cd2a4ff655 100644 --- a/docs/man/nutscan_free_device.txt +++ b/docs/man/nutscan_free_device.txt @@ -4,7 +4,8 @@ NUTSCAN_FREE_DEVICE(3) NAME ---- -nutscan_free_device - Free a nutscan_device_t structure created by nutscan_new_device. +nutscan_free_device - Free a `nutscan_device_t` structure created by +`nutscan_new_device`. SYNOPSIS -------- @@ -16,7 +17,9 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_free_device()* function free a `nutscan_device_type_t` structure. Doing so, it free the whole linked list, not only the given device. +The *nutscan_free_device()* function can free a `nutscan_device_type_t` +structure. Doing so, it frees the whole linked list, not only the given +device. SEE ALSO -------- diff --git a/docs/man/nutscan_get_serial_ports_list.txt b/docs/man/nutscan_get_serial_ports_list.txt index c441122963..848a22c948 100644 --- a/docs/man/nutscan_get_serial_ports_list.txt +++ b/docs/man/nutscan_get_serial_ports_list.txt @@ -16,20 +16,33 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_get_serial_ports_list()* function returns a null terminated array of string generated from a port range. +The *nutscan_get_serial_ports_list()* function returns a null +terminated array of strings generated from a port range. 'ports_range' may be one of: - - a single character from 0 to 9 or a to z representing a serial communication port depending on the operating system. For instance "0" is converted to /dev/ttyS0 and /dev/ttyUSB0 on Linux; "1" is converted to COM1 on Windows; "c" is converted to /dev/ttyc on Solaris... - - a range of character in the form "X-Y". For instance "0-1" will be converted to /dev/ttyS0, /dev/ttyS1, /dev/ttyUSB0 and /dev/ttyUSB1 on Linux; "1-3" will be converted to COM1, COM2 and COM3 on Windows; "a-c" will be converted to /dev/ttya, /dev/ttyb and /dev/ttyc on Solaris. + - a single character from 0 to 9 or a to z representing a serial + communication port depending on the operating system. For instance + "0" is converted to /dev/ttyS0 and /dev/ttyUSB0 on Linux; + "1" is converted to COM1 on Windows; + "c" is converted to /dev/ttyc on Solaris... + - a range of character in the form "X-Y". For instance + "0-1" will be converted to /dev/ttyS0, /dev/ttyS1, /dev/ttyUSB0 + and /dev/ttyUSB1 on Linux; + "1-3" will be converted to COM1, COM2 and COM3 on Windows; + "a-c" will be converted to /dev/ttya, /dev/ttyb and /dev/ttyc on Solaris. - a single port name (/dev/ttyS5, COM4...). - - a list of port names separated with comas : "/dev/ttyS0,/dev/ttyS2,/dev/ttyS4" or "COM1,COM3"... + - a list of port names separated with commas: + "/dev/ttyS0,/dev/ttyS2,/dev/ttyS4" or "COM1,COM3"... -The returned array can be used in a call to *nutscan_scan_eaton_serial* to get the serial device on a system. +The returned array can be used in a call to *nutscan_scan_eaton_serial* +to get the serial device on a system. RETURN VALUE ------------ -The *nutscan_get_serial_ports_list()* function returns NULL if an error occurred (invalid port range) or a pointer to a null terminated array of string on success. +The *nutscan_get_serial_ports_list()* function returns NULL if an error +occurred (invalid port range) or a pointer to a null terminated array +of strings on success. SEE ALSO -------- From 1847ec549c5ebf726cc58130e30f461b70916722 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 10:26:26 +0200 Subject: [PATCH 494/700] GitIgnore more intermediate XML files from docs generation --- docs/.gitignore | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/.gitignore b/docs/.gitignore index 00076fcd45..555adc4b4a 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,14 +1,24 @@ +# Intended build products /*.chunked/ /*.html /*.pdf + +# Temporary build resources (can come from OS) /docbook-xsl.css + +# Intermediate files from docs-building /ups-html.txt /user-manual.xml /developer-guide.xml +/packager-guide.xml +/solaris-usb.xml +/cables.xml /docinfo.xml /FAQ.xml /nut-dmf.xml /nut-names.xml + +# Temporary stuff nut.dict.sorted /*.bak /*.bak-* From 3226db44080e14d79e97dc02ae1c2068a2923eb9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 10:28:18 +0200 Subject: [PATCH 495/700] GitIgnore IDEA metadata files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index ff67167786..9d63328b36 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,5 @@ __pycache__/ # Debuggers and IDEs .gdb_history /nbproject +/.idea +/*.iml From 2138076c026f0cda6537e7a7b9b4ff3aa01e00f6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 10:38:03 +0200 Subject: [PATCH 496/700] ci_build.sh: add notes to Consider `--enable-maintainer-mode` --- ci_build.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ci_build.sh b/ci_build.sh index db5a8355f7..375c01084e 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -741,6 +741,9 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp CONFIG_OPTS+=("--with-devd-dir=${BUILD_PREFIX}/etc/devd") CONFIG_OPTS+=("--with-hotplug-dir=${BUILD_PREFIX}/etc/hotplug") + # TODO: Consider `--enable-maintainer-mode` to add recipes that + # would quickly regenerate Makefile(.in) if you edit Makefile.am + if [ -n "${PYTHON-}" ]; then # WARNING: Watch out for whitespaces, not handled here! CONFIG_OPTS+=("--with-python=${PYTHON}") @@ -1507,6 +1510,8 @@ bindings) ./autogen.sh + # TODO: Consider `--enable-maintainer-mode` to add recipes that + # would quickly regenerate Makefile(.in) if you edit Makefile.am # NOTE: Default NUT "configure" actually insists on some features, # like serial port support unless told otherwise, or docs if possible. # Below we aim for really fast iterations of C/C++ development so From 4a6dd536b1c5fd016e4cdd0bac0c58026945a2e2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 11:00:48 +0200 Subject: [PATCH 497/700] docs/Makefile.am: DOCBUILD_BEGIN: symlink images/ ONLY for PDF generation --- docs/Makefile.am | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/Makefile.am b/docs/Makefile.am index fd825cdfeb..a593070518 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -191,6 +191,10 @@ A2X_COMMON_OPTS = $(ASCIIDOC_VERBOSE) \ # As a brutal workaround for the former problem, we chmod. For second one we # might try magic with .SEQUENTIAL recipe hints, but that is gmake-dependent. +# Note that empirically it treats "destination-dir" as the source root for +# PDF generation (even though it claims the argument is ignored for non-HTML +# targets) so we have to provide the "images/" in this case. ONLY for PDF! + # Note we only remove the original target (if present), if it is a directory - # e.g. created by "html-chunked" targets. DOCBUILD_BEGIN = { \ @@ -198,7 +202,9 @@ DOCBUILD_BEGIN = { \ rm -rf "./$${A2X_OUTDIR}" || true ; \ test -d "$@" && rm -rf "$@" || true ; \ mkdir -p "./$${A2X_OUTDIR}" || exit ; \ - ln -s ../../images "./$${A2X_OUTDIR}" ; \ + case "$${A2X_OUTDIR}" in \ + tmp/pdf.*) ln -s ../../images "./$${A2X_OUTDIR}" ;; \ + esac; \ else A2X_OUTDIR='.' ; fi; \ if test -s "${builddir}/docbook-xsl.css" \ && test -r "${builddir}/docbook-xsl.css" \ From d1bf65142f0ac94d07999f216f912e3e24dd967d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 11:26:33 +0200 Subject: [PATCH 498/700] docs/man/upsd.txt, nutupsdrv.txt: reformat lists of drivers and tools to bullet lists --- docs/man/nutupsdrv.txt | 100 ++++++++++++++++++++++------------------- docs/man/upsd.txt | 43 +++++++++++++----- 2 files changed, 84 insertions(+), 59 deletions(-) diff --git a/docs/man/nutupsdrv.txt b/docs/man/nutupsdrv.txt index f5f2b7c9da..49bbcf538f 100644 --- a/docs/man/nutupsdrv.txt +++ b/docs/man/nutupsdrv.txt @@ -206,69 +206,75 @@ SEE ALSO Server: ~~~~~~~ -linkman:upsd[8] +- linkman:upsd[8] Clients: ~~~~~~~~ -linkman:upsc[8], linkman:upscmd[8], -linkman:upsrw[8], linkman:upslog[8], linkman:upsmon[8] +- linkman:upsc[8] +- linkman:upscmd[8] +- linkman:upsrw[8] +- linkman:upslog[8] +- linkman:upsmon[8] CGI programs: ~~~~~~~~~~~~~ -linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] +- linkman:upsset.cgi[8] +- linkman:upsstats.cgi[8] +- linkman:upsimage.cgi[8] Driver control: ~~~~~~~~~~~~~~~ -linkman:upsdrvctl[8], linkman:upsdrvsvcctl[8] +- linkman:upsdrvctl[8] +- linkman:upsdrvsvcctl[8] Drivers: ~~~~~~~~ -linkman:al175[8] -linkman:apcsmart[8], -linkman:bcmxcp[8], -linkman:bcmxcp_usb[8], -linkman:belkin[8], -linkman:belkinunv[8], -linkman:bestfcom[8], -linkman:bestuferrups[8], -linkman:bestups[8], -linkman:blazer_ser[8], -linkman:blazer_usb[8], -linkman:cyberpower[8], -linkman:dummy-ups[8], -linkman:etapro[8], -linkman:everups[8], -linkman:gamatronic[8], -linkman:genericups[8], -linkman:isbmex[8], -linkman:liebert[8], -linkman:masterguard[8], -linkman:metasys[8], -linkman:mge-shut[8], -linkman:mge-utalk[8], -linkman:mge-xml[8], -linkman:nitram[8], -linkman:nutdrv_qx[8], -linkman:oneac[8], -linkman:optiups[8], -linkman:powercom[8], -linkman:powerman-pdu[8], -linkman:powerpanel[8], -linkman:rhino[8], -linkman:richcomm_usb[8], -linkman:safenet[8], -linkman:snmp-ups[8], -linkman:solis[8], -linkman:tripplite[8], -linkman:tripplitesu[8], -linkman:tripplite_usb[8], -linkman:usbhid-ups[8], -linkman:upscode2[8], -linkman:victronups[8] +- linkman:al175[8] +- linkman:apcsmart[8] +- linkman:bcmxcp[8] +- linkman:bcmxcp_usb[8] +- linkman:belkin[8] +- linkman:belkinunv[8] +- linkman:bestfcom[8] +- linkman:bestuferrups[8] +- linkman:bestups[8] +- linkman:blazer_ser[8] +- linkman:blazer_usb[8] +- linkman:cyberpower[8] +- linkman:dummy-ups[8] +- linkman:etapro[8] +- linkman:everups[8] +- linkman:gamatronic[8] +- linkman:genericups[8] +- linkman:isbmex[8] +- linkman:liebert[8] +- linkman:masterguard[8] +- linkman:metasys[8] +- linkman:mge-shut[8] +- linkman:mge-utalk[8] +- linkman:mge-xml[8] +- linkman:nitram[8] +- linkman:nutdrv_qx[8] +- linkman:oneac[8] +- linkman:optiups[8] +- linkman:powercom[8] +- linkman:powerman-pdu[8] +- linkman:powerpanel[8] +- linkman:rhino[8] +- linkman:richcomm_usb[8] +- linkman:safenet[8] +- linkman:snmp-ups[8] +- linkman:solis[8] +- linkman:tripplite[8] +- linkman:tripplitesu[8] +- linkman:tripplite_usb[8] +- linkman:usbhid-ups[8] +- linkman:upscode2[8] +- linkman:victronups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/man/upsd.txt b/docs/man/upsd.txt index 9a4a887a56..cf3356f4d8 100644 --- a/docs/man/upsd.txt +++ b/docs/man/upsd.txt @@ -158,26 +158,45 @@ SEE ALSO Clients: ~~~~~~~~ -linkman:upsc[8], linkman:upscmd[8], -linkman:upsrw[8], linkman:upslog[8], linkman:upsmon[8] +- linkman:upsc[8] +- linkman:upscmd[8] +- linkman:upsrw[8] +- linkman:upslog[8] +- linkman:upsmon[8] CGI programs: ~~~~~~~~~~~~~ -linkman:upsset.cgi[8], linkman:upsstats.cgi[8], linkman:upsimage.cgi[8] +- linkman:upsset.cgi[8] +- linkman:upsstats.cgi[8] +- linkman:upsimage.cgi[8] Drivers: ~~~~~~~~ -linkman:nutupsdrv[8], -linkman:apcsmart[8], linkman:belkin[8], linkman:belkinunv[8], -linkman:bestuferrups[8], linkman:bestups[8], -linkman:cyberpower[8], linkman:energizerups[8], linkman:etapro[8], -linkman:everups[8], linkman:genericups[8], -linkman:isbmex[8], linkman:liebert[8], linkman:masterguard[8], -linkman:mge-shut[8], linkman:mge-utalk[8], linkman:oneac[8], -linkman:powercom[8], linkman:safenet[8], linkman:snmp-ups[8], -linkman:tripplite[8], linkman:tripplitesu[8], linkman:victronups[8], +- linkman:nutupsdrv[8] +- linkman:apcsmart[8] +- linkman:belkin[8] +- linkman:belkinunv[8] +- linkman:bestuferrups[8] +- linkman:bestups[8] +- linkman:cyberpower[8] +- linkman:energizerups[8] +- linkman:etapro[8] +- linkman:everups[8] +- linkman:genericups[8] +- linkman:isbmex[8] +- linkman:liebert[8] +- linkman:masterguard[8] +- linkman:mge-shut[8] +- linkman:mge-utalk[8] +- linkman:oneac[8] +- linkman:powercom[8] +- linkman:safenet[8] +- linkman:snmp-ups[8] +- linkman:tripplite[8] +- linkman:tripplitesu[8] +- linkman:victronups[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ From 1309fab2cc29925500406dedf1bbf90de248c2e7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 11:27:24 +0200 Subject: [PATCH 499/700] docs/man/index.txt: generate current list of drivers, separate "Driver Control" title --- docs/man/index.txt | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/docs/man/index.txt b/docs/man/index.txt index 61b802caa9..9027c89762 100644 --- a/docs/man/index.txt +++ b/docs/man/index.txt @@ -47,14 +47,23 @@ CGI programs - linkman:upsstats.cgi[8] [[Drivers]] -Drivers -~~~~~~~ +Driver control: +~~~~~~~~~~~~~~~ - linkman:upsdrvctl[8] - linkman:upsdrvsvcctl[8] - linkman:nut-driver-enumerator[8] +Drivers: +~~~~~~~~ + +///////////////////////////////////////// +:; (cd docs/man && ls -1 *.8 | grep -E -v '(upsc|upsd|upsdrvsvcctl|upsdrvctl|upscmd|upssched|upsrw|upsmon|upslog|nut-driver-enumerator|nut-scanner|nut-recorder|.*cgi)\.8' | sed 's,^\(.*\)\.8$,- linkman:\1[8],') +///////////////////////////////////////// + +- linkman:adelsystem_cbi[8] - linkman:al175[8] +- linkman:apcsmart-old[8] - linkman:apcsmart[8] - linkman:apcupsd-ups[8] - linkman:asem[8] @@ -73,20 +82,25 @@ Drivers - linkman:etapro[8] - linkman:everups[8] - linkman:gamatronic[8] +- linkman:generic_modbus[8] - linkman:genericups[8] +- linkman:huawei-ups2000[8] - linkman:isbmex[8] - linkman:ivtscd[8] -- linkman:liebert[8] - linkman:liebert-esp2[8] +- linkman:liebert[8] - linkman:macosx-ups[8] - linkman:masterguard[8] - linkman:metasys[8] - linkman:mge-shut[8] - linkman:mge-utalk[8] - linkman:microdowell[8] +- linkman:microsol-apc[8] - linkman:netxml-ups[8] +- linkman:nut-ipmipsu[8] - linkman:nutdrv_atcl_usb[8] - linkman:nutdrv_qx[8] +- linkman:nutdrv_siemens_sitop[8] - linkman:nutupsdrv[8] - linkman:oneac[8] - linkman:optiups[8] @@ -97,14 +111,17 @@ Drivers - linkman:powerpanel[8] - linkman:rhino[8] - linkman:richcomm_usb[8] +- linkman:riello_ser[8] +- linkman:riello_usb[8] - linkman:safenet[8] - linkman:snmp-ups[8] +- linkman:socomec_jbus[8] - linkman:solis[8] - linkman:tripplite[8] -- linkman:tripplitesu[8] - linkman:tripplite_usb[8] -- linkman:usbhid-ups[8] +- linkman:tripplitesu[8] - linkman:upscode2[8] +- linkman:usbhid-ups[8] - linkman:victronups[8] [[Developer_man]] From fa173ecae499b332cdac7734b906ef54e4b5041c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 11:32:19 +0200 Subject: [PATCH 500/700] docs/man/upsd.txt, nutupsdrv.txt: generate current list of drivers --- docs/man/nutupsdrv.txt | 37 ++++++++++++++++++++++++---- docs/man/upsd.txt | 56 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 85 insertions(+), 8 deletions(-) diff --git a/docs/man/nutupsdrv.txt b/docs/man/nutupsdrv.txt index 49bbcf538f..60a335f437 100644 --- a/docs/man/nutupsdrv.txt +++ b/docs/man/nutupsdrv.txt @@ -229,51 +229,78 @@ Driver control: - linkman:upsdrvctl[8] - linkman:upsdrvsvcctl[8] +- linkman:nut-driver-enumerator[8] Drivers: ~~~~~~~~ +///////////////////////////////////////// +:; (cd docs/man && ls -1 *.8 | grep -E -v '(upsc|upsd|upsdrvsvcctl|upsdrvctl|upscmd|upssched|upsrw|upsmon|upslog|nut-driver-enumerator|nut-scanner|nut-recorder|.*cgi)\.8' | sed 's,^\(.*\)\.8$,- linkman:\1[8],') +///////////////////////////////////////// + +- linkman:adelsystem_cbi[8] - linkman:al175[8] +- linkman:apcsmart-old[8] - linkman:apcsmart[8] +- linkman:apcupsd-ups[8] +- linkman:asem[8] - linkman:bcmxcp[8] - linkman:bcmxcp_usb[8] - linkman:belkin[8] - linkman:belkinunv[8] - linkman:bestfcom[8] +- linkman:bestfortress[8] - linkman:bestuferrups[8] - linkman:bestups[8] - linkman:blazer_ser[8] - linkman:blazer_usb[8] -- linkman:cyberpower[8] +- linkman:clone[8] - linkman:dummy-ups[8] - linkman:etapro[8] - linkman:everups[8] - linkman:gamatronic[8] +- linkman:generic_modbus[8] - linkman:genericups[8] +- linkman:huawei-ups2000[8] - linkman:isbmex[8] +- linkman:ivtscd[8] +- linkman:liebert-esp2[8] - linkman:liebert[8] +- linkman:macosx-ups[8] - linkman:masterguard[8] - linkman:metasys[8] - linkman:mge-shut[8] - linkman:mge-utalk[8] -- linkman:mge-xml[8] -- linkman:nitram[8] +- linkman:microdowell[8] +- linkman:microsol-apc[8] +- linkman:netxml-ups[8] +- linkman:nut-ipmipsu[8] +- linkman:nutdrv_atcl_usb[8] - linkman:nutdrv_qx[8] +- linkman:nutdrv_siemens_sitop[8] +//////////////////////// +- linkman:nutupsdrv[8] +//////////////////////// - linkman:oneac[8] - linkman:optiups[8] +- linkman:phoenixcontact_modbus[8] +- linkman:pijuice[8] - linkman:powercom[8] - linkman:powerman-pdu[8] - linkman:powerpanel[8] - linkman:rhino[8] - linkman:richcomm_usb[8] +- linkman:riello_ser[8] +- linkman:riello_usb[8] - linkman:safenet[8] - linkman:snmp-ups[8] +- linkman:socomec_jbus[8] - linkman:solis[8] - linkman:tripplite[8] -- linkman:tripplitesu[8] - linkman:tripplite_usb[8] -- linkman:usbhid-ups[8] +- linkman:tripplitesu[8] - linkman:upscode2[8] +- linkman:usbhid-ups[8] - linkman:victronups[8] Internet resources: diff --git a/docs/man/upsd.txt b/docs/man/upsd.txt index cf3356f4d8..da76e329e2 100644 --- a/docs/man/upsd.txt +++ b/docs/man/upsd.txt @@ -171,31 +171,81 @@ CGI programs: - linkman:upsstats.cgi[8] - linkman:upsimage.cgi[8] +Driver control: +~~~~~~~~~~~~~~~ + +- linkman:upsdrvctl[8] +- linkman:upsdrvsvcctl[8] +- linkman:nut-driver-enumerator[8] + Drivers: ~~~~~~~~ -- linkman:nutupsdrv[8] +///////////////////////////////////////// +:; (cd docs/man && ls -1 *.8 | grep -E -v '(upsc|upsd|upsdrvsvcctl|upsdrvctl|upscmd|upssched|upsrw|upsmon|upslog|nut-driver-enumerator|nut-scanner|nut-recorder|.*cgi)\.8' | sed 's,^\(.*\)\.8$,- linkman:\1[8],') +///////////////////////////////////////// + +- linkman:adelsystem_cbi[8] +- linkman:al175[8] +- linkman:apcsmart-old[8] - linkman:apcsmart[8] +- linkman:apcupsd-ups[8] +- linkman:asem[8] +- linkman:bcmxcp[8] +- linkman:bcmxcp_usb[8] - linkman:belkin[8] - linkman:belkinunv[8] +- linkman:bestfcom[8] +- linkman:bestfortress[8] - linkman:bestuferrups[8] - linkman:bestups[8] -- linkman:cyberpower[8] -- linkman:energizerups[8] +- linkman:blazer_ser[8] +- linkman:blazer_usb[8] +- linkman:clone[8] +- linkman:dummy-ups[8] - linkman:etapro[8] - linkman:everups[8] +- linkman:gamatronic[8] +- linkman:generic_modbus[8] - linkman:genericups[8] +- linkman:huawei-ups2000[8] - linkman:isbmex[8] +- linkman:ivtscd[8] +- linkman:liebert-esp2[8] - linkman:liebert[8] +- linkman:macosx-ups[8] - linkman:masterguard[8] +- linkman:metasys[8] - linkman:mge-shut[8] - linkman:mge-utalk[8] +- linkman:microdowell[8] +- linkman:microsol-apc[8] +- linkman:netxml-ups[8] +- linkman:nut-ipmipsu[8] +- linkman:nutdrv_atcl_usb[8] +- linkman:nutdrv_qx[8] +- linkman:nutdrv_siemens_sitop[8] +- linkman:nutupsdrv[8] - linkman:oneac[8] +- linkman:optiups[8] +- linkman:phoenixcontact_modbus[8] +- linkman:pijuice[8] - linkman:powercom[8] +- linkman:powerman-pdu[8] +- linkman:powerpanel[8] +- linkman:rhino[8] +- linkman:richcomm_usb[8] +- linkman:riello_ser[8] +- linkman:riello_usb[8] - linkman:safenet[8] - linkman:snmp-ups[8] +- linkman:socomec_jbus[8] +- linkman:solis[8] - linkman:tripplite[8] +- linkman:tripplite_usb[8] - linkman:tripplitesu[8] +- linkman:upscode2[8] +- linkman:usbhid-ups[8] - linkman:victronups[8] Internet resources: From e1bd74fe8ce73f7643b1bf4a2c7f3be60a7645a1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 11:37:47 +0200 Subject: [PATCH 501/700] docs/man/upsd.txt, nutupsdrv.txt, index.txt: move common nutupsdrv[8] from the bulk of driver list --- docs/man/index.txt | 5 +++-- docs/man/nutupsdrv.txt | 5 +---- docs/man/upsd.txt | 5 +++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/man/index.txt b/docs/man/index.txt index 9027c89762..1adaf1feb3 100644 --- a/docs/man/index.txt +++ b/docs/man/index.txt @@ -57,8 +57,10 @@ Driver control: Drivers: ~~~~~~~~ +- linkman:nutupsdrv[8] + ///////////////////////////////////////// -:; (cd docs/man && ls -1 *.8 | grep -E -v '(upsc|upsd|upsdrvsvcctl|upsdrvctl|upscmd|upssched|upsrw|upsmon|upslog|nut-driver-enumerator|nut-scanner|nut-recorder|.*cgi)\.8' | sed 's,^\(.*\)\.8$,- linkman:\1[8],') +:; (cd docs/man && ls -1 *.8 | grep -E -v '(upsc|upsd|upsdrvsvcctl|upsdrvctl|upscmd|upssched|upsrw|upsmon|upslog|nut-driver-enumerator|nut-scanner|nut-recorder|nutupsdrv|.*cgi)\.8' | sed 's,^\(.*\)\.8$,- linkman:\1[8],') ///////////////////////////////////////// - linkman:adelsystem_cbi[8] @@ -101,7 +103,6 @@ Drivers: - linkman:nutdrv_atcl_usb[8] - linkman:nutdrv_qx[8] - linkman:nutdrv_siemens_sitop[8] -- linkman:nutupsdrv[8] - linkman:oneac[8] - linkman:optiups[8] - linkman:phoenixcontact_modbus[8] diff --git a/docs/man/nutupsdrv.txt b/docs/man/nutupsdrv.txt index 60a335f437..118eab0494 100644 --- a/docs/man/nutupsdrv.txt +++ b/docs/man/nutupsdrv.txt @@ -235,7 +235,7 @@ Drivers: ~~~~~~~~ ///////////////////////////////////////// -:; (cd docs/man && ls -1 *.8 | grep -E -v '(upsc|upsd|upsdrvsvcctl|upsdrvctl|upscmd|upssched|upsrw|upsmon|upslog|nut-driver-enumerator|nut-scanner|nut-recorder|.*cgi)\.8' | sed 's,^\(.*\)\.8$,- linkman:\1[8],') +:; (cd docs/man && ls -1 *.8 | grep -E -v '(upsc|upsd|upsdrvsvcctl|upsdrvctl|upscmd|upssched|upsrw|upsmon|upslog|nut-driver-enumerator|nut-scanner|nut-recorder|nutupsdrv|.*cgi)\.8' | sed 's,^\(.*\)\.8$,- linkman:\1[8],') ///////////////////////////////////////// - linkman:adelsystem_cbi[8] @@ -278,9 +278,6 @@ Drivers: - linkman:nutdrv_atcl_usb[8] - linkman:nutdrv_qx[8] - linkman:nutdrv_siemens_sitop[8] -//////////////////////// -- linkman:nutupsdrv[8] -//////////////////////// - linkman:oneac[8] - linkman:optiups[8] - linkman:phoenixcontact_modbus[8] diff --git a/docs/man/upsd.txt b/docs/man/upsd.txt index da76e329e2..9a47d163de 100644 --- a/docs/man/upsd.txt +++ b/docs/man/upsd.txt @@ -181,8 +181,10 @@ Driver control: Drivers: ~~~~~~~~ +- linkman:nutupsdrv[8] + ///////////////////////////////////////// -:; (cd docs/man && ls -1 *.8 | grep -E -v '(upsc|upsd|upsdrvsvcctl|upsdrvctl|upscmd|upssched|upsrw|upsmon|upslog|nut-driver-enumerator|nut-scanner|nut-recorder|.*cgi)\.8' | sed 's,^\(.*\)\.8$,- linkman:\1[8],') +:; (cd docs/man && ls -1 *.8 | grep -E -v '(upsc|upsd|upsdrvsvcctl|upsdrvctl|upscmd|upssched|upsrw|upsmon|upslog|nut-driver-enumerator|nut-scanner|nut-recorder|nutupsdrv|.*cgi)\.8' | sed 's,^\(.*\)\.8$,- linkman:\1[8],') ///////////////////////////////////////// - linkman:adelsystem_cbi[8] @@ -225,7 +227,6 @@ Drivers: - linkman:nutdrv_atcl_usb[8] - linkman:nutdrv_qx[8] - linkman:nutdrv_siemens_sitop[8] -- linkman:nutupsdrv[8] - linkman:oneac[8] - linkman:optiups[8] - linkman:phoenixcontact_modbus[8] From f93f642c58d819cefcc39b6d274bb7aafe6c026f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 12:22:19 +0200 Subject: [PATCH 502/700] docs/man/index.txt, upsd.txt, nutupsdrv.txt: generate the up-to-date list of drivers, current for each iteration --- docs/man/.gitignore | 2 ++ docs/man/Makefile.am | 57 ++++++++++++++++++++++++++------- docs/man/index.txt | 71 ++---------------------------------------- docs/man/nutupsdrv.txt | 70 ++--------------------------------------- docs/man/upsd.txt | 71 ++---------------------------------------- 5 files changed, 54 insertions(+), 217 deletions(-) diff --git a/docs/man/.gitignore b/docs/man/.gitignore index 0afd404808..daf5c8b157 100644 --- a/docs/man/.gitignore +++ b/docs/man/.gitignore @@ -4,3 +4,5 @@ /*.8 /*.html /tmp/ +/linkman-driver-names.txt +/linkman-drivertool-names.txt diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 96b354427b..a904aa1b07 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -49,14 +49,20 @@ HTML_CONF_MANS = \ upsmon.conf.html \ upssched.conf.html +# NOTE: Currently SRC_DRIVERTOOL_PAGES are a separate list to generate +# a linkman-drivertool-names.txt file, but historically remain part of +# MAN/HTML_CLIENT_PAGES in the bigger picture. +SRC_DRIVERTOOL_PAGES = \ + nut-driver-enumerator.txt \ + upsdrvctl.txt \ + upsdrvsvcctl.txt + SRC_CLIENT_PAGES = \ + $(SRC_DRIVERTOOL_PAGES) \ nutupsdrv.txt \ - nut-driver-enumerator.txt \ upsc.txt \ upscmd.txt \ upsd.txt \ - upsdrvctl.txt \ - upsdrvsvcctl.txt \ upslog.txt \ upsmon.txt \ upsrw.txt \ @@ -677,14 +683,7 @@ MAN_MANS += \ $(MAN_LINUX_I2C_PAGES) endif -# distribute everything, even those not installed by default -# Note that 'dist' target requires AsciiDoc! -SRC_ALL_PAGES = \ - $(SRC_CONF_PAGES) \ - $(SRC_CLIENT_PAGES) \ - $(SRC_TOOL_PAGES) \ - $(SRC_CGI_PAGES) \ - $(SRC_DEV_PAGES) \ +SRC_DRIVERS_PAGES = \ $(SRC_SERIAL_PAGES) \ $(SRC_SNMP_PAGES) \ $(SRC_USB_LIBUSB_PAGES) \ @@ -696,6 +695,16 @@ SRC_ALL_PAGES = \ $(SRC_MODBUS_PAGES) \ $(SRC_LINUX_I2C_PAGES) +# distribute everything, even those not installed by default +# Note that 'dist' target requires AsciiDoc! +SRC_ALL_PAGES = \ + $(SRC_CONF_PAGES) \ + $(SRC_CLIENT_PAGES) \ + $(SRC_TOOL_PAGES) \ + $(SRC_CGI_PAGES) \ + $(SRC_DEV_PAGES) \ + $(SRC_DRIVERS_PAGES) + EXTRA_DIST = \ $(SRC_ALL_PAGES) \ $(MAN_MANS) \ @@ -736,6 +745,32 @@ HTML_MANS = \ $(HTML_MODBUS_MANS) \ $(HTML_LINUX_I2C_MANS) +# Note: target documents, except nutupsdrv.txt itself, start the +# list of drivers with `- linkman:nutupsdrv[8]` entry +linkman-driver-names.txt: + @(LC_ALL=C; LANG=C; export LC_ALL LANG; \ + ls -1 $(SRC_DRIVERS_PAGES) | grep -vE '^nutupsdrv\.txt$$' | sort -n | uniq \ + | sed 's,^\(.*\)\.txt$$,- linkman:\1[8],' ; \ + ) > "$@" + +linkman-drivertool-names.txt: + @(LC_ALL=C; LANG=C; export LC_ALL LANG; \ + ls -1 $(SRC_DRIVERTOOL_PAGES) | sort -n | uniq \ + | sed 's,^\(.*\)\.txt$$,- linkman:\1[8],' ; \ + ) > "$@" + +index.txt index.html \ +upsd.txt upsd.html upsd.8 upsd.pdf \ +nutupsdrv.txt nutupsdrv.html nutupsdrv.8 nutupsdrv.pdf : linkman-driver-names.txt linkman-drivertool-names.txt + +# These files are generated when we build from git source so not tracked in +# git, but we want it as part of tarball just in case the end-user's build +# does not enable something related to docs: +EXTRA_DIST += linkman-driver-names.txt +EXTRA_DIST += linkman-drivertool-names.txt + +DISTCLEANFILES = linkman-driver-names.txt linkman-drivertool-names.txt + all: all-html html-man: $(HTML_MANS) index.html diff --git a/docs/man/index.txt b/docs/man/index.txt index 1adaf1feb3..a06b294c26 100644 --- a/docs/man/index.txt +++ b/docs/man/index.txt @@ -50,80 +50,13 @@ CGI programs Driver control: ~~~~~~~~~~~~~~~ -- linkman:upsdrvctl[8] -- linkman:upsdrvsvcctl[8] -- linkman:nut-driver-enumerator[8] +include::linkman-drivertool-names.txt[] Drivers: ~~~~~~~~ - linkman:nutupsdrv[8] - -///////////////////////////////////////// -:; (cd docs/man && ls -1 *.8 | grep -E -v '(upsc|upsd|upsdrvsvcctl|upsdrvctl|upscmd|upssched|upsrw|upsmon|upslog|nut-driver-enumerator|nut-scanner|nut-recorder|nutupsdrv|.*cgi)\.8' | sed 's,^\(.*\)\.8$,- linkman:\1[8],') -///////////////////////////////////////// - -- linkman:adelsystem_cbi[8] -- linkman:al175[8] -- linkman:apcsmart-old[8] -- linkman:apcsmart[8] -- linkman:apcupsd-ups[8] -- linkman:asem[8] -- linkman:bcmxcp[8] -- linkman:bcmxcp_usb[8] -- linkman:belkin[8] -- linkman:belkinunv[8] -- linkman:bestfcom[8] -- linkman:bestfortress[8] -- linkman:bestuferrups[8] -- linkman:bestups[8] -- linkman:blazer_ser[8] -- linkman:blazer_usb[8] -- linkman:clone[8] -- linkman:dummy-ups[8] -- linkman:etapro[8] -- linkman:everups[8] -- linkman:gamatronic[8] -- linkman:generic_modbus[8] -- linkman:genericups[8] -- linkman:huawei-ups2000[8] -- linkman:isbmex[8] -- linkman:ivtscd[8] -- linkman:liebert-esp2[8] -- linkman:liebert[8] -- linkman:macosx-ups[8] -- linkman:masterguard[8] -- linkman:metasys[8] -- linkman:mge-shut[8] -- linkman:mge-utalk[8] -- linkman:microdowell[8] -- linkman:microsol-apc[8] -- linkman:netxml-ups[8] -- linkman:nut-ipmipsu[8] -- linkman:nutdrv_atcl_usb[8] -- linkman:nutdrv_qx[8] -- linkman:nutdrv_siemens_sitop[8] -- linkman:oneac[8] -- linkman:optiups[8] -- linkman:phoenixcontact_modbus[8] -- linkman:pijuice[8] -- linkman:powercom[8] -- linkman:powerman-pdu[8] -- linkman:powerpanel[8] -- linkman:rhino[8] -- linkman:richcomm_usb[8] -- linkman:riello_ser[8] -- linkman:riello_usb[8] -- linkman:safenet[8] -- linkman:snmp-ups[8] -- linkman:socomec_jbus[8] -- linkman:solis[8] -- linkman:tripplite[8] -- linkman:tripplite_usb[8] -- linkman:tripplitesu[8] -- linkman:upscode2[8] -- linkman:usbhid-ups[8] -- linkman:victronups[8] +include::linkman-driver-names.txt[] [[Developer_man]] Developer manual pages diff --git a/docs/man/nutupsdrv.txt b/docs/man/nutupsdrv.txt index 118eab0494..fe86d71945 100644 --- a/docs/man/nutupsdrv.txt +++ b/docs/man/nutupsdrv.txt @@ -227,78 +227,12 @@ CGI programs: Driver control: ~~~~~~~~~~~~~~~ -- linkman:upsdrvctl[8] -- linkman:upsdrvsvcctl[8] -- linkman:nut-driver-enumerator[8] +include::linkman-drivertool-names.txt[] Drivers: ~~~~~~~~ -///////////////////////////////////////// -:; (cd docs/man && ls -1 *.8 | grep -E -v '(upsc|upsd|upsdrvsvcctl|upsdrvctl|upscmd|upssched|upsrw|upsmon|upslog|nut-driver-enumerator|nut-scanner|nut-recorder|nutupsdrv|.*cgi)\.8' | sed 's,^\(.*\)\.8$,- linkman:\1[8],') -///////////////////////////////////////// - -- linkman:adelsystem_cbi[8] -- linkman:al175[8] -- linkman:apcsmart-old[8] -- linkman:apcsmart[8] -- linkman:apcupsd-ups[8] -- linkman:asem[8] -- linkman:bcmxcp[8] -- linkman:bcmxcp_usb[8] -- linkman:belkin[8] -- linkman:belkinunv[8] -- linkman:bestfcom[8] -- linkman:bestfortress[8] -- linkman:bestuferrups[8] -- linkman:bestups[8] -- linkman:blazer_ser[8] -- linkman:blazer_usb[8] -- linkman:clone[8] -- linkman:dummy-ups[8] -- linkman:etapro[8] -- linkman:everups[8] -- linkman:gamatronic[8] -- linkman:generic_modbus[8] -- linkman:genericups[8] -- linkman:huawei-ups2000[8] -- linkman:isbmex[8] -- linkman:ivtscd[8] -- linkman:liebert-esp2[8] -- linkman:liebert[8] -- linkman:macosx-ups[8] -- linkman:masterguard[8] -- linkman:metasys[8] -- linkman:mge-shut[8] -- linkman:mge-utalk[8] -- linkman:microdowell[8] -- linkman:microsol-apc[8] -- linkman:netxml-ups[8] -- linkman:nut-ipmipsu[8] -- linkman:nutdrv_atcl_usb[8] -- linkman:nutdrv_qx[8] -- linkman:nutdrv_siemens_sitop[8] -- linkman:oneac[8] -- linkman:optiups[8] -- linkman:phoenixcontact_modbus[8] -- linkman:pijuice[8] -- linkman:powercom[8] -- linkman:powerman-pdu[8] -- linkman:powerpanel[8] -- linkman:rhino[8] -- linkman:richcomm_usb[8] -- linkman:riello_ser[8] -- linkman:riello_usb[8] -- linkman:safenet[8] -- linkman:snmp-ups[8] -- linkman:socomec_jbus[8] -- linkman:solis[8] -- linkman:tripplite[8] -- linkman:tripplite_usb[8] -- linkman:tripplitesu[8] -- linkman:upscode2[8] -- linkman:usbhid-ups[8] -- linkman:victronups[8] +include::linkman-driver-names.txt[] Internet resources: ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/man/upsd.txt b/docs/man/upsd.txt index 9a47d163de..af322bef9d 100644 --- a/docs/man/upsd.txt +++ b/docs/man/upsd.txt @@ -174,80 +174,13 @@ CGI programs: Driver control: ~~~~~~~~~~~~~~~ -- linkman:upsdrvctl[8] -- linkman:upsdrvsvcctl[8] -- linkman:nut-driver-enumerator[8] +include::linkman-drivertool-names.txt[] Drivers: ~~~~~~~~ - linkman:nutupsdrv[8] - -///////////////////////////////////////// -:; (cd docs/man && ls -1 *.8 | grep -E -v '(upsc|upsd|upsdrvsvcctl|upsdrvctl|upscmd|upssched|upsrw|upsmon|upslog|nut-driver-enumerator|nut-scanner|nut-recorder|nutupsdrv|.*cgi)\.8' | sed 's,^\(.*\)\.8$,- linkman:\1[8],') -///////////////////////////////////////// - -- linkman:adelsystem_cbi[8] -- linkman:al175[8] -- linkman:apcsmart-old[8] -- linkman:apcsmart[8] -- linkman:apcupsd-ups[8] -- linkman:asem[8] -- linkman:bcmxcp[8] -- linkman:bcmxcp_usb[8] -- linkman:belkin[8] -- linkman:belkinunv[8] -- linkman:bestfcom[8] -- linkman:bestfortress[8] -- linkman:bestuferrups[8] -- linkman:bestups[8] -- linkman:blazer_ser[8] -- linkman:blazer_usb[8] -- linkman:clone[8] -- linkman:dummy-ups[8] -- linkman:etapro[8] -- linkman:everups[8] -- linkman:gamatronic[8] -- linkman:generic_modbus[8] -- linkman:genericups[8] -- linkman:huawei-ups2000[8] -- linkman:isbmex[8] -- linkman:ivtscd[8] -- linkman:liebert-esp2[8] -- linkman:liebert[8] -- linkman:macosx-ups[8] -- linkman:masterguard[8] -- linkman:metasys[8] -- linkman:mge-shut[8] -- linkman:mge-utalk[8] -- linkman:microdowell[8] -- linkman:microsol-apc[8] -- linkman:netxml-ups[8] -- linkman:nut-ipmipsu[8] -- linkman:nutdrv_atcl_usb[8] -- linkman:nutdrv_qx[8] -- linkman:nutdrv_siemens_sitop[8] -- linkman:oneac[8] -- linkman:optiups[8] -- linkman:phoenixcontact_modbus[8] -- linkman:pijuice[8] -- linkman:powercom[8] -- linkman:powerman-pdu[8] -- linkman:powerpanel[8] -- linkman:rhino[8] -- linkman:richcomm_usb[8] -- linkman:riello_ser[8] -- linkman:riello_usb[8] -- linkman:safenet[8] -- linkman:snmp-ups[8] -- linkman:socomec_jbus[8] -- linkman:solis[8] -- linkman:tripplite[8] -- linkman:tripplite_usb[8] -- linkman:tripplitesu[8] -- linkman:upscode2[8] -- linkman:usbhid-ups[8] -- linkman:victronups[8] +include::linkman-driver-names.txt[] Internet resources: ~~~~~~~~~~~~~~~~~~~ From 34d476d267de9412c0c24380e302052196005c9b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 12:41:10 +0200 Subject: [PATCH 503/700] docs/support.txt: mention github instead of subversion --- docs/support.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/support.txt b/docs/support.txt index 0cb07afce2..c7d6165bcb 100644 --- a/docs/support.txt +++ b/docs/support.txt @@ -65,12 +65,13 @@ In this case, be sure to include the following information: - OS name and version, - exact NUT version, -- NUT installation method: from source tarball, package or Subversion, +- NUT installation method: package, or a custom build from source tarball + or GitHub (which fork, branch, PR), - exact device name and related information (manufacturing date, web -pointers, ...), + pointers, ...), - complete problem description, with any relevant traces, like system -log excerpts, and driver debug output. You can obtain the latter using -the following command, running as root and after having stopped NUT: + log excerpts, and driver debug output. You can obtain the latter using + the following command, running as root and after having stopped NUT: /path/to/driver -DD -a From 9a5ce9a9ce5909caac2ae42a8626c6a00093743a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 16:32:42 +0200 Subject: [PATCH 504/700] docs/man/belkinunv.txt: add link to protocol description copy on NUT site --- docs/man/belkinunv.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/man/belkinunv.txt b/docs/man/belkinunv.txt index 0f0cdb071b..4d8d8c288f 100644 --- a/docs/man/belkinunv.txt +++ b/docs/man/belkinunv.txt @@ -359,3 +359,5 @@ Internet resources: * The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ * The documentation for the protocol used by this UPS: link:http://www.mscs.dal.ca/~selinger/ups/belkin-universal-ups.html[belkin-universal-ups.html] + (link:https://networkupstools.org/protocols/belkin-universal.html[replica + on NUT site]) From 8e1d1d552ab03d09961ab1a3fa2bcdda407a23b2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 16:51:19 +0200 Subject: [PATCH 505/700] docs/man/phoenixcontact_modbus.txt: wrap long lines --- docs/man/phoenixcontact_modbus.txt | 38 ++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/docs/man/phoenixcontact_modbus.txt b/docs/man/phoenixcontact_modbus.txt index 07b2843a84..e173089d61 100644 --- a/docs/man/phoenixcontact_modbus.txt +++ b/docs/man/phoenixcontact_modbus.txt @@ -20,8 +20,10 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -This driver should support the PhoenixContact QUINT-UPS industrial DC UPS, model 2320461 and all compatible models. -More information about this UPS can be found here: https://www.phoenixcontact.com/online/portal/us?uri=pxc-oc-itemdetail:pid=2320461 +This driver should support the PhoenixContact QUINT-UPS industrial DC UPS, +model 2320461 and all compatible models. More information about this UPS +can be found here: +https://www.phoenixcontact.com/online/portal/us?uri=pxc-oc-itemdetail:pid=2320461 phoenixcontact_modbus uses the libmodbus project, for Modbus implementation. @@ -30,28 +32,40 @@ How to configure the UPS Note: this UPS and its manual refers to Low-Batt as "Shutdown Event". -You need the "IFS-RS232-DATACABLE" to communicate with the UPS in linux as the IFS-USB cable doesn't seem to be supported. FYI communication parameters are: 115200,E,8,1. -You also need the UPS-CONF Windows software (free; download from their site), to configure the UPS signals and timers. +You need the "IFS-RS232-DATACABLE" to communicate with the UPS in Linux +as the IFS-USB cable doesn't seem to be supported. FYI communication +parameters are: 115200,E,8,1. + +You also need the UPS-CONF Windows software (free; download from their +site), to configure the UPS signals and timers. 1. Run the UPS-CONF 2. Go to Settings->Time Setting 3. Choose "state of charge shutdown delay" 4. Choose Remote starts PC-Shutdown in Mains and Battery mode 5. On the PC-Shutdown enter the maximum value (5 minutes) -6. On the PC-Restart delay enter the time you want the UPS to leave the output power off before restarting (e.g. 60 seconds), after mains power is restored. +6. On the PC-Restart delay enter the time you want the UPS to leave + the output power off before restarting (e.g. 60 seconds), after + mains power is restored. 7. On the UPS, turn the screw to the "PC-MODE" position Configuring the above way ensures that: * When power is lost, UPS constantly calculates remaining battery time - * When remaining battery time is less than 5 minutes (PC-Shutdown setting), it will raise the "Shutdown" event (seen as LOW-BATT in NUT) - * From that moment even if input power is restored, the UPS will cut the output power to its load after 5 minutes - * When the input power is restored, the UPS will restore output power after 60 seconds (PC-RESTART delay setting). + * When remaining battery time is less than 5 minutes (PC-Shutdown setting), + it will raise the "Shutdown" event (seen as LOW-BATT in NUT) + * From that moment even if input power is restored, the UPS will cut the + output power to its load after 5 minutes + * When the input power is restored, the UPS will restore output power + after 60 seconds (PC-RESTART delay setting). Meaning of settings: - * PC-Shutdown: How long before output cutoff the UPS will raise the "shutdown event" signal. Max value for this is 5 minutes. So PC should be able to shutdown within 5 minutes. - * PC-Restart: How long to delay output power after power is restored. Max is 60 seconds. + * PC-Shutdown: How long before output cutoff the UPS will raise + the "shutdown event" signal. Max value for this is 5 minutes. + So PC should be able to shutdown within 5 minutes. + * PC-Restart: How long to delay output power after power is restored. + Max is 60 seconds. EXTRA ARGUMENTS @@ -62,8 +76,8 @@ This driver doesn't support any optional settings. INSTALLATION ------------ -This driver is not built by default. You can build it by installing libmodbus -and running `configure --with-modbus=yes`. +This driver is not built by default. You can build it by installing +libmodbus and running `configure --with-modbus=yes`. You also need to give proper permissions on the local serial device file (/dev/ttyS0 for example) to allow the NUT user to access it. From 4b8db64372a2ca582183854ad937a2bb21ff87f6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 16:52:43 +0200 Subject: [PATCH 506/700] docs/man/optiups.txt: wrap long lines --- docs/man/optiups.txt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/man/optiups.txt b/docs/man/optiups.txt index 61a9678970..287b22e9fc 100644 --- a/docs/man/optiups.txt +++ b/docs/man/optiups.txt @@ -56,13 +56,14 @@ sent to your system logs each time NUT polls the UPS. If you specify *fake_lowbatt*:: -This forces the low battery flag true. Without it, if you want to test your -UPS, you will have to unplug it and wait until the battery drops to a low/critical -voltage level before NUT will respond and power down your system. With the flag, -NUT should power down the system soon after you pull the plug. When you are done -testing, you should remove this flag. +This forces the low battery flag true. Without it, if you want to test +your UPS, you will have to unplug it and wait until the battery drops +to a low/critical voltage level before NUT will respond and power down +your system. With the flag, NUT should power down the system soon after +you pull the plug. When you are done testing, you should remove this flag. + -For basic shutdown configuration testing, the command 'upsmon -c fsd' is preferred. +For basic shutdown configuration testing, the command 'upsmon -c fsd' is +preferred. *powerup*:: @@ -73,8 +74,9 @@ This will also power-up your equipment connected to the UPS! BUGS ---- -On the 420E, `ups.serial` and `ups.temperature` are unsupported features. This -is not a bug in NUT or the NUT driver, just the way things are with this UPS. +On the 420E, `ups.serial` and `ups.temperature` are unsupported features. +This is not a bug in NUT or the NUT driver, just the way things are with +this UPS. AUTHORS ------- From 883e7d833a7dda17bf5b840abf62c43fd2412f68 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 17:00:45 +0200 Subject: [PATCH 507/700] docs/man/snmp-ups.txt: wrap long lines and reword a bit --- docs/man/snmp-ups.txt | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/docs/man/snmp-ups.txt b/docs/man/snmp-ups.txt index 4179d8db01..17333f94a4 100644 --- a/docs/man/snmp-ups.txt +++ b/docs/man/snmp-ups.txt @@ -16,16 +16,20 @@ linkman:nutupsdrv[8]. SUPPORTED HARDWARE ------------------ -The snmp-ups driver automatically detects and supports a wide range of devices by loading various MIBS, such as: +The snmp-ups driver automatically detects and supports a wide range of +devices by loading various MIBS, such as: *ietf*:: -UPS that is RFC 1628 (UPS MIB) compliant, e.g. MGE UPS SYSTEMS, Liebert, perhaps others (default) +UPS that is RFC 1628 (UPS MIB) compliant, e.g. MGE UPS SYSTEMS, Liebert, +perhaps others (default) *mge*:: -MGE UPS SYSTEMS and MGE Office Protection Systems devices with SNMP cards (ref 66062, 66045, 66074 and 66244) +MGE UPS SYSTEMS and MGE Office Protection Systems devices with SNMP cards +(ref 66062, 66045, 66074 and 66244) *apcc*:: -APC AP9605, AP9606, AP9617, and AP9618 APC network management cards, as well as any others supporting the APC POWERNET MIB +APC AP9605, AP9606, AP9617, and AP9618 APC network management cards, +as well as any others supporting the APC POWERNET MIB *netvision*:: Socomec Sicon UPS with Netvision Web/SNMP management card/external box @@ -55,7 +59,8 @@ Various BayTech PDUs HP/Compaq AF401A management card, perhaps others *cyberpower*:: -Cyberpower RMCARD201. Should also support RMCARD100 (net version), RMCARD202 and RMCARD301 +Cyberpower RMCARD201. Should also support RMCARD100 (net version), +RMCARD202 and RMCARD301 *huawei*:: Huawei UPS5000-E, perhaps others @@ -86,15 +91,20 @@ $ snmp-ups -a snmp-test -x mibs=--list ---- *mibs*='name':: -Set MIB compliance (default=auto, allowed entries: refer to SUPPORTED HARDWARE above). +Set MIB compliance (default=auto, allowed entries: refer to SUPPORTED +HARDWARE above). ++ With "auto", the driver will try a select set of SNMP objects until it finds -one that the device responds to. Note that since NUT 2.6.2, snmp-ups has a new -method that uses sysObjectID (which is a pointer to the preferred MIB of the -device) to detect supported devices. This renders void the use of "mibs" option. +one that the device responds to. ++ +Note that since NUT 2.6.2, snmp-ups has a new method that uses sysObjectID +(which is a pointer to the preferred MIB of the device) to detect supported +devices. This renders void the *requirement* to use the "mibs" option. *community*='name':: Set community name (default = public). -Note that a RW community name is required to change UPS settings (as for a powerdown). +Note that a RW community name is required to change UPS settings and +send commands (as for a powerdown). *snmp_version*='version':: Set SNMP version (default = v1, allowed: v2c, v3) @@ -113,8 +123,8 @@ Convert from three phase line-to-line voltage to line-to-neutral voltage *pollfreq*='num':: Set polling interval for full updates, in seconds, to reduce SNMP network traffic relative to the quick updates performed every "pollinterval" (the -latter option is described in linkman:ups.conf[5]). The default value is 30 (in -seconds). +latter option is described in linkman:ups.conf[5]). +The default value is 30 (in seconds). *notransferoids*:: Disable the monitoring of the low and high voltage transfer OIDs in From 1f21e750f24162b4665194952a06b075563da43a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 17:01:04 +0200 Subject: [PATCH 508/700] docs/man/snmp-ups.txt: list AUTHORS as a bulleted list and add Jim Klimov --- docs/man/snmp-ups.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/man/snmp-ups.txt b/docs/man/snmp-ups.txt index 17333f94a4..f45462b3e3 100644 --- a/docs/man/snmp-ups.txt +++ b/docs/man/snmp-ups.txt @@ -215,8 +215,9 @@ The hostname of the UPS is specified with the "port" value in AUTHORS ------- -Arnaud Quette, Dmitry Frolov - +* Arnaud Quette +* Dmitry Frolov +* Jim Klimov SEE ALSO -------- From 1f38c92c3ab549ad78d99e74ea8e03fdfb7fe6b9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 17:01:39 +0200 Subject: [PATCH 509/700] docs/man/nut-driver-enumerator.txt: list Jim Klimov as the AUTHOR --- docs/man/nut-driver-enumerator.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/man/nut-driver-enumerator.txt b/docs/man/nut-driver-enumerator.txt index acf8dd255c..6fe92dbafa 100644 --- a/docs/man/nut-driver-enumerator.txt +++ b/docs/man/nut-driver-enumerator.txt @@ -134,6 +134,11 @@ Bad inputs, e.g. unrecognized service management framework *2*:: Absent or unreadable `ups.conf` file +AUTHOR +------ + +Jim Klimov + SEE ALSO -------- From e97060dab60a8f580b2ad88d2df85c81b65b375e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 17:01:52 +0200 Subject: [PATCH 510/700] docs/man/upsdrvsvcctl.txt: list Jim Klimov as the AUTHOR --- docs/man/upsdrvsvcctl.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/man/upsdrvsvcctl.txt b/docs/man/upsdrvsvcctl.txt index 813756faf4..fdace1f9bd 100644 --- a/docs/man/upsdrvsvcctl.txt +++ b/docs/man/upsdrvsvcctl.txt @@ -188,6 +188,11 @@ copy of the journals stored in the filesystem. *Solaris SMF*:: Look for `/var/svc/log/system-power-nut-driver:instance-name.log` file. +AUTHOR +------ + +Jim Klimov + SEE ALSO -------- From 6d194b827feeaed1faaea4125b0180ab3a52efaa Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 17:15:58 +0200 Subject: [PATCH 511/700] docs/man/*.txt: reformat AUTHOR(S) and INTERNET RESOURCES blocks to common style --- docs/man/apcsmart-old.txt | 16 ++++++++++------ docs/man/apcsmart.txt | 16 ++++++++++------ docs/man/asem.txt | 7 ++++--- docs/man/bcmxcp_usb.txt | 8 ++++---- docs/man/liebert-esp2.txt | 4 ++-- docs/man/macosx-ups.txt | 4 ++-- docs/man/microsol-apc.txt | 4 ++-- docs/man/nut.conf.txt | 2 +- docs/man/nutdrv_atcl_usb.txt | 4 ++-- docs/man/nutdrv_qx.txt | 5 ++--- docs/man/nutdrv_siemens_sitop.txt | 4 ++-- docs/man/oneac.txt | 8 ++++---- docs/man/optiups.txt | 4 +++- docs/man/upscode2.txt | 2 +- docs/man/usbhid-ups.txt | 6 +++++- 15 files changed, 54 insertions(+), 40 deletions(-) diff --git a/docs/man/apcsmart-old.txt b/docs/man/apcsmart-old.txt index 72f7f8e917..6ca4cffe35 100644 --- a/docs/man/apcsmart-old.txt +++ b/docs/man/apcsmart-old.txt @@ -83,13 +83,17 @@ other unexpected values have occasionally slipped through. APC UPS models with both USB and serial ports require a power cycle when switching from USB communication to serial, and perhaps vice versa. -AUTHOR ------- +AUTHORS AND HISTORY +------------------- + Nigel Metheringham (drawing -heavily on the original apcsmart driver by Russell Kroll). This driver -was called newapc for a time and was renamed in the 1.5 series. In 2.6.2 -the driver was renamed to apcsmart-old, being superseded by updated version -with new features. +heavily on the original `apcsmart` driver by Russell Kroll). + +This driver was called `newapc` for a time and was renamed in +the 1.5 series. + +In 2.6.2 the driver was renamed to `apcsmart-old`, being superseded +by updated version with new features. SEE ALSO -------- diff --git a/docs/man/apcsmart.txt b/docs/man/apcsmart.txt index 2f2f14ffa1..503ff57701 100644 --- a/docs/man/apcsmart.txt +++ b/docs/man/apcsmart.txt @@ -367,14 +367,18 @@ other unexpected values have occasionally slipped through. APC UPS models with both USB and serial ports require a power cycle when switching from USB communication to serial, and perhaps vice versa. -AUTHOR ------- +AUTHORS AND HISTORY +------------------- Nigel Metheringham (drawing -heavily on the original apcsmart driver by Russell Kroll). This driver -was called newapc for a time and was renamed in the 1.5 series. In 2.6.2 -it was renamed to apcsmart-old, being superseded by updated version with -new features, which is maintained by Michal Soltys +heavily on the original `apcsmart` driver by Russell Kroll). + +This driver was called `newapc` for a time and was renamed in +the 1.5 series. + +In 2.6.2 it was renamed to `apcsmart-old`, being superseded by +updated version with new features, which is maintained by Michal +Soltys SEE ALSO -------- diff --git a/docs/man/asem.txt b/docs/man/asem.txt index 867112266c..b45cf11a36 100644 --- a/docs/man/asem.txt +++ b/docs/man/asem.txt @@ -68,8 +68,8 @@ KNOWN ISSUES AND BUGS The driver shutdown function is not implemented, so other arrangements must be made to turn off the UPS. -AUTHORS -------- +AUTHOR +------ Giuseppe Corbelli @@ -84,6 +84,7 @@ linkman:nutupsdrv[8] Internet resources: ~~~~~~~~~~~~~~~~~~~ -* PB1300 specifications: http://www.asem.it/en/products/industrial-automation/box-pcs/performance/pb1300/ +* PB1300 specifications: + http://www.asem.it/en/products/industrial-automation/box-pcs/performance/pb1300/ * BQ2060 datasheet: http://www.ti.com/lit/ds/symlink/bq2060.pdf * The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ diff --git a/docs/man/bcmxcp_usb.txt b/docs/man/bcmxcp_usb.txt index 5f18c8bd0a..2eae9f5e91 100644 --- a/docs/man/bcmxcp_usb.txt +++ b/docs/man/bcmxcp_usb.txt @@ -99,11 +99,11 @@ You have forgotten to install the hotplug files, as explained in the INSTALLATION section above. Don't forget to restart hotplug so that it applies these changes. -AUTHOR ------- +AUTHORS +------- -Tore Ørpetveit , -Wolfgang Ocker +* Tore Ørpetveit +* Wolfgang Ocker SEE ALSO -------- diff --git a/docs/man/liebert-esp2.txt b/docs/man/liebert-esp2.txt index be57914f14..e71fb72c39 100644 --- a/docs/man/liebert-esp2.txt +++ b/docs/man/liebert-esp2.txt @@ -43,8 +43,8 @@ This driver supports the following optional settings in linkman:ups.conf[5]: *baudrate=*'num':: Set the speed of the serial connection - 1200, 2400 (default), 4800, 9600 or 19200. -AUTHOR ------- +AUTHORS +------- * Richard Gregory * Arjen de Korte diff --git a/docs/man/macosx-ups.txt b/docs/man/macosx-ups.txt index 3b3130eefd..2338be2722 100644 --- a/docs/man/macosx-ups.txt +++ b/docs/man/macosx-ups.txt @@ -64,8 +64,8 @@ the battery voltage, as well as current and frequency, are typically not shown. It may be possible to monitor these values with *apcupsd* (for APC hardware only) or linkman:usbhid-ups[8]. -AUTHORS -------- +AUTHOR +------ Charles Lepple diff --git a/docs/man/microsol-apc.txt b/docs/man/microsol-apc.txt index 82c3d8277c..74e3225445 100644 --- a/docs/man/microsol-apc.txt +++ b/docs/man/microsol-apc.txt @@ -52,8 +52,8 @@ real values, needing model-specific post-processing by the driver. Monitoring of UPS state (on-battery/on-line, critical battery) should work for other models, but is untested. -AUTHOR ------- +AUTHORS +------- * Ygor A. S. Regados * Roberto P. Velloso diff --git a/docs/man/nut.conf.txt b/docs/man/nut.conf.txt index 7da7bdf36c..90f7106a41 100644 --- a/docs/man/nut.conf.txt +++ b/docs/man/nut.conf.txt @@ -99,7 +99,7 @@ INTEGRATION ----------- An init script, such as /etc/init.d/nut, is expected to source this -file in order to determine which component(s) has to be started. +file in order to determine which components have to be started. SEE ALSO -------- diff --git a/docs/man/nutdrv_atcl_usb.txt b/docs/man/nutdrv_atcl_usb.txt index b81d8eb716..e955a01068 100644 --- a/docs/man/nutdrv_atcl_usb.txt +++ b/docs/man/nutdrv_atcl_usb.txt @@ -59,8 +59,8 @@ systems. See the linkman:upsmon[8] man page for more information. The solution to this problem is to upgrade to a smart protocol UPS of some kind that allows detection and proper load cycling on command. -AUTHORS -------- +AUTHOR +------ Charles Lepple diff --git a/docs/man/nutdrv_qx.txt b/docs/man/nutdrv_qx.txt index b3f9c3a01b..905b5615f8 100644 --- a/docs/man/nutdrv_qx.txt +++ b/docs/man/nutdrv_qx.txt @@ -821,6 +821,5 @@ linkman:upsrw[8] Internet Resources: ~~~~~~~~~~~~~~~~~~~ -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - -The NUT HCL: http://www.networkupstools.org/stable-hcl.html +* The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ +* The NUT HCL: http://www.networkupstools.org/stable-hcl.html diff --git a/docs/man/nutdrv_siemens_sitop.txt b/docs/man/nutdrv_siemens_sitop.txt index 4ffd0ccfb9..193f99df44 100644 --- a/docs/man/nutdrv_siemens_sitop.txt +++ b/docs/man/nutdrv_siemens_sitop.txt @@ -182,8 +182,8 @@ During normal operation, no commands are sent to the UPS at all usability. It is not sure if the serial models are affected by this issue as well. -AUTHORS -------- +AUTHOR +------ Matthijs H. ten Berge diff --git a/docs/man/oneac.txt b/docs/man/oneac.txt index c2f86301a6..beb257ebc7 100644 --- a/docs/man/oneac.txt +++ b/docs/man/oneac.txt @@ -111,11 +111,11 @@ those values are used to create an allowable output range. The UPS will do what it can to keep the output voltage value within the defined range (for example: tap change or switch to inverter). -AUTHOR ------- +AUTHORS +------- -Bill Elliot , -Eric Lawson +* Bill Elliot +* Eric Lawson SEE ALSO -------- diff --git a/docs/man/optiups.txt b/docs/man/optiups.txt index 287b22e9fc..1e1d90417a 100644 --- a/docs/man/optiups.txt +++ b/docs/man/optiups.txt @@ -81,7 +81,9 @@ this UPS. AUTHORS ------- -Russell Kroll, Scott Heavner, Matthias Goebl +* Russell Kroll +* Scott Heavner +* Matthias Goebl SEE ALSO -------- diff --git a/docs/man/upscode2.txt b/docs/man/upscode2.txt index d3a9ec604a..21c7b02492 100644 --- a/docs/man/upscode2.txt +++ b/docs/man/upscode2.txt @@ -89,7 +89,7 @@ battery voltage. AUTHORS ------- -* Håvard Lygre * Niels Baggesen SEE ALSO diff --git a/docs/man/usbhid-ups.txt b/docs/man/usbhid-ups.txt index 457b1687d9..99ca9db78e 100644 --- a/docs/man/usbhid-ups.txt +++ b/docs/man/usbhid-ups.txt @@ -253,8 +253,12 @@ AUTHORS ------- Originally sponsored by MGE UPS SYSTEMS. + Now sponsored by Eaton -Arnaud Quette, Peter Selinger, Arjen de Korte + +* Arnaud Quette +* Peter Selinger +* Arjen de Korte SEE ALSO -------- From ef0a2940badcc4237b55cd155fc89c6223d5d2cf Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 17:17:10 +0200 Subject: [PATCH 512/700] docs/nut.dict: spellcheck drivertool --- docs/nut.dict | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/nut.dict b/docs/nut.dict index 4acd5cde31..0501369526 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2942 utf-8 +personal_ws-1.1 en 2943 utf-8 AAS ABI ACFAIL @@ -1719,6 +1719,7 @@ dq driverexec drivername driverpath +drivertool drv drvctl drvpath From 6845406c5bf0672fdfb393e8971d6b4543206138 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 22:06:33 +0200 Subject: [PATCH 513/700] docs/man/nutscan*.txt: wrap long lines, minor rephrase, fix timeouts to useconds_t --- docs/man/nutscan.txt | 17 +++++--- docs/man/nutscan_add_device_to_device.txt | 21 ++++++++-- docs/man/nutscan_add_option_to_device.txt | 19 +++++++-- docs/man/nutscan_cidr_to_ip.txt | 15 +++++++- docs/man/nutscan_free_device.txt | 5 +++ docs/man/nutscan_get_serial_ports_list.txt | 5 +++ docs/man/nutscan_init.txt | 16 ++++++-- docs/man/nutscan_new_device.txt | 11 +++++- docs/man/nutscan_scan_avahi.txt | 19 +++++---- docs/man/nutscan_scan_eaton_serial.txt | 13 +++++-- docs/man/nutscan_scan_nut.txt | 19 +++++++-- docs/man/nutscan_scan_snmp.txt | 45 ++++++++++++++++------ docs/man/nutscan_scan_usb.txt | 4 +- docs/man/nutscan_scan_xml_http.txt | 9 ++++- 14 files changed, 167 insertions(+), 51 deletions(-) diff --git a/docs/man/nutscan.txt b/docs/man/nutscan.txt index 8d1d4c8024..4eac97b901 100644 --- a/docs/man/nutscan.txt +++ b/docs/man/nutscan.txt @@ -13,7 +13,8 @@ The Network UPS Tools (NUT) *nutscan* library provides the same discovery related features that are also offered by linkman:nut-scanner[8]. It enables the discovery of supported NUT devices (USB, SNMP, Eaton XML/HTTP -and IPMI) and NUT servers (using Avahi, or the classic connection method). +and IPMI) and NUT servers (either using Avahi, or the classic connection +method). DISCOVERY FUNCTIONS @@ -28,13 +29,16 @@ Then, to discover new devices, use the appropriate function: - linkman:nutscan_scan_usb[3] for supported USB devices, - linkman:nutscan_scan_snmp[3] for supported SNMP agents, - linkman:nutscan_scan_xml_http[3] for Eaton Network Management Card, -- linkman:nutscan_scan_nut[3] for NUT servers (upsd), using the classic method, -- linkman:nutscan_scan_avahi[3] for NUT servers (upsd), using the mDNS (Avahi) method, +- linkman:nutscan_scan_nut[3] for NUT servers (upsd), using the classic + method (search for port), +- linkman:nutscan_scan_avahi[3] for NUT servers (upsd), using the mDNS + (Avahi) method, - linkman:nutscan_scan_ipmi[3] for supported IPMI PSU. -All of these functions return a list of devices found, using the nutscan_device_t -structure. This structure is described in linkman:nutscan_add_device_to_device[3]. +All of these functions return a list of devices found, using the +`nutscan_device_t` structure. This structure is described in +linkman:nutscan_add_device_to_device[3]. Helper functions are also provided to output data using standard formats: @@ -57,7 +61,8 @@ linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_parsable[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], -linkman:nutscan_add_device_to_device[3], linkman:nutscan_add_option_to_device[3], +linkman:nutscan_add_device_to_device[3], +linkman:nutscan_add_option_to_device[3], linkman:nutscan_cidr_to_ip[3] Internet resources: diff --git a/docs/man/nutscan_add_device_to_device.txt b/docs/man/nutscan_add_device_to_device.txt index 9ca02b968b..bd34087e26 100644 --- a/docs/man/nutscan_add_device_to_device.txt +++ b/docs/man/nutscan_add_device_to_device.txt @@ -11,7 +11,9 @@ SYNOPSIS #include - nutscan_device_t * nutscan_add_device_to_device(nutscan_device_t * first, nutscan_device_t * second); + nutscan_device_t * nutscan_add_device_to_device( + nutscan_device_t * first, + nutscan_device_t * second); DESCRIPTION @@ -26,14 +28,25 @@ The `nutscan_device_t` contains the following variables: struct nutscan_device * prev; struct nutscan_device * next; -This is a double linked list of device. Each device is described by its `type`, its `driver` name, its `port` and any number of optional data. +This is a double linked list of device. Each device is described by its +`type`, its `driver` name, its `port` and any number of optional data. -The *nutscan_add_device_to_device()* concatenates 'first' and 'second' devices to a unique device. No new device is created, the two linked list are simply linked to each other. So 'first' and 'second' devices are likely to be modified by this function. +The *nutscan_add_device_to_device()* concatenates 'first' and 'second' +devices to a unique device. No new device is created, the two linked +lists are simply linked to each other. So 'first' and 'second' devices +are likely to be modified by this function. RETURN VALUE ------------ -The *nutscan_add_device_to_device()* functions returns a pointer to a device containing both passed devices. Note that it's not a new device, so it is either 'first' or 'second' which is returned. +The *nutscan_add_device_to_device()* functions returns a pointer to a +device containing both passed devices. Note that it's not a new device, +so it is either 'first' or 'second' which is returned. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-device.h' file. SEE ALSO -------- diff --git a/docs/man/nutscan_add_option_to_device.txt b/docs/man/nutscan_add_option_to_device.txt index c5cdda59ea..a9efcbd4a6 100644 --- a/docs/man/nutscan_add_option_to_device.txt +++ b/docs/man/nutscan_add_option_to_device.txt @@ -11,7 +11,10 @@ SYNOPSIS #include - void nutscan_add_option_to_device(nutscan_device_t * device,char * option_name, char * value); + void nutscan_add_option_to_device( + nutscan_device_t * device, + char * option_name, + char * value); DESCRIPTION @@ -26,9 +29,19 @@ The `nutscan_device_t` contains the following variables: struct nutscan_device * prev; struct nutscan_device * next; -This is a double linked list of device. Each device is described by its `type`, its `driver` name, its `port` and any number of optional data. +This is a double linked list of device. Each device is described by +its `type`, its `driver` name, its `port` and any number of optional data. -The *nutscan_add_option_to_device()* adds an optional data in the given device. Optional data are made of an 'option_name' and an associated 'value'. Copies of 'option_name' and 'value' are stored in the device, so the caller can safely free both of them. +The *nutscan_add_option_to_device()* adds an optional data in the +given device. Optional data are made of an 'option_name' and an +associated 'value'. Copies of 'option_name' and 'value' are stored +in the device, so the caller can safely free both of the original +strings used as arguments. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-device.h' file. SEE ALSO -------- diff --git a/docs/man/nutscan_cidr_to_ip.txt b/docs/man/nutscan_cidr_to_ip.txt index b5f26e14ec..5b6787fb7d 100644 --- a/docs/man/nutscan_cidr_to_ip.txt +++ b/docs/man/nutscan_cidr_to_ip.txt @@ -16,12 +16,23 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_cidr_to_ip()* function converts a range of IP address in the CIDR format given as a string in 'cidr', to two IPs in strings pointed by 'start_ip' and 'stop_ip' which can be used as input parameters in the scanning functions of the libnutscan API. It is the caller's responsibility to free 'start_ip' and 'stop_ip' strings. +The *nutscan_cidr_to_ip()* function converts a range of IP address in +the CIDR format given as a string in 'cidr', to two IPs in strings +pointed by 'start_ip' and 'stop_ip' which can be used as input +parameters in the scanning functions of the libnutscan API. + +It is the caller's responsibility to free 'start_ip' and 'stop_ip' strings. RETURN VALUE ------------ -The *nutscan_cidr_to_ip()* function returns 0 if an error occurred (invalid 'cidr' address) or 1 if successful. +The *nutscan_cidr_to_ip()* function returns 0 if an error occurred +(invalid 'cidr' address) or 1 if successful. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-ip.h' file. SEE ALSO -------- diff --git a/docs/man/nutscan_free_device.txt b/docs/man/nutscan_free_device.txt index cd2a4ff655..c9d4192b28 100644 --- a/docs/man/nutscan_free_device.txt +++ b/docs/man/nutscan_free_device.txt @@ -21,6 +21,11 @@ The *nutscan_free_device()* function can free a `nutscan_device_type_t` structure. Doing so, it frees the whole linked list, not only the given device. +NOTES +----- + +Technically, the function is currently defined in 'nutscan-device.h' file. + SEE ALSO -------- diff --git a/docs/man/nutscan_get_serial_ports_list.txt b/docs/man/nutscan_get_serial_ports_list.txt index 848a22c948..8535edd4d7 100644 --- a/docs/man/nutscan_get_serial_ports_list.txt +++ b/docs/man/nutscan_get_serial_ports_list.txt @@ -44,6 +44,11 @@ The *nutscan_get_serial_ports_list()* function returns NULL if an error occurred (invalid port range) or a pointer to a null terminated array of strings on success. +NOTES +----- + +Technically, the function is currently defined in 'nutscan-serial.h' file. + SEE ALSO -------- diff --git a/docs/man/nutscan_init.txt b/docs/man/nutscan_init.txt index 248aa0005e..61ef3dcc52 100644 --- a/docs/man/nutscan_init.txt +++ b/docs/man/nutscan_init.txt @@ -16,9 +16,13 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_init()* function must be called at least once before using any other function of the nutscan library. +The *nutscan_init()* function must be called at least once before using +any other function of the nutscan library. -It updates the following global variables which can be used by nutscan library user to know which scan methods are available at run-time. This depends on the libraries installed on the system: +It updates the following global variables which can be used by nutscan +library user to know which scan methods are available at run-time. + +This depends on the libraries installed on the system: nutscan_avail_avahi = 1 : AVAHI scan is available nutscan_avail_ipmi = 1 : IPMI scan is available @@ -27,7 +31,13 @@ It updates the following global variables which can be used by nutscan library u nutscan_avail_usb = 1 : USB method is available nutscan_avail_xml_http = 1 : XML HTTP method is available -Note that if a method is reported as unavailable by those variables, the call to the corresponding nutscan_scan_* function will always return NULL. +Note that if a method is reported as unavailable by those variables, the +call to the corresponding `nutscan_scan_*` function will always return NULL. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-init.h' file. SEE ALSO -------- diff --git a/docs/man/nutscan_new_device.txt b/docs/man/nutscan_new_device.txt index 6a58a97a45..57ff82d40d 100644 --- a/docs/man/nutscan_new_device.txt +++ b/docs/man/nutscan_new_device.txt @@ -16,12 +16,19 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_new_device()* function allocates a new `nutscan_device_type_t` structure. +The *nutscan_new_device()* function allocates a new `nutscan_device_type_t` +structure. RETURN VALUE ------------ -The *nutscan_new_device()* function returns the newly allocated `nutscan_device_type_t` structure +The *nutscan_new_device()* function returns the newly allocated +`nutscan_device_type_t` structure. + +NOTES +----- + +Technically, the function is currently defined in 'nutscan-device.h' file. SEE ALSO -------- diff --git a/docs/man/nutscan_scan_avahi.txt b/docs/man/nutscan_scan_avahi.txt index 96bfa04aa7..72582767dc 100644 --- a/docs/man/nutscan_scan_avahi.txt +++ b/docs/man/nutscan_scan_avahi.txt @@ -10,26 +10,29 @@ SYNOPSIS -------- #include + #include /* useconds_t */ - nutscan_device_t * nutscan_scan_avahi(long usec_timeout); + nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout); DESCRIPTION ----------- -The *nutscan_scan_avahi()* function tries to detect the NUT service via mDNS, -and its associated devices. It uses the Avahi library to do so. +The *nutscan_scan_avahi()* function tries to detect the NUT service via +mDNS, and its associated devices. It uses the Avahi library to do so. You MUST call linkman:nutscan_init[3] before using this function. -This function waits up to 'usec_timeout' microseconds before considering an IP -address to be unresponsive. +This function can query perspective IP addresses using NUT protocol, and +in this case it waits up to 'usec_timeout' microseconds before considering +an IP address to be unresponsive. RETURN VALUE ------------ -The *nutscan_scan_avahi()* function returns a pointer to a `nutscan_device_t` -structure containing all found devices. It returns NULL if an error occurs, or -if no device is found. +The *nutscan_scan_avahi()* function returns a pointer to a +`nutscan_device_t` structure containing all found devices. + +It returns NULL if an error occurs, or if no device is found. SEE ALSO -------- diff --git a/docs/man/nutscan_scan_eaton_serial.txt b/docs/man/nutscan_scan_eaton_serial.txt index 479f979dd0..735886de5d 100644 --- a/docs/man/nutscan_scan_eaton_serial.txt +++ b/docs/man/nutscan_scan_eaton_serial.txt @@ -4,7 +4,8 @@ NUTSCAN_SCAN_EATON_SERIAL(3) NAME ---- -nutscan_scan_eaton_serial - Scan serial ports for Eaton devices (XCP, SHUT and Q1). +nutscan_scan_eaton_serial - Scan serial ports for Eaton devices (XCP, SHUT +and Q1). SYNOPSIS -------- @@ -16,8 +17,10 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_scan_eaton_serial()* function tries to detect NUT devices which are -compatible with Eaton's serial device protocols (SHUT, XCP and Q1 (aka blazer)). +The *nutscan_scan_eaton_serial()* function tries to detect NUT devices +which are compatible with Eaton's serial device protocols (SHUT, XCP +and Q1 (aka blazer)). + 'ports_list' is a NULL terminated array of pointers to strings containing serial device name (/dev/ttyS0, COM1, /dev/ttya...) @@ -26,7 +29,9 @@ You MUST call linkman:nutscan_init[3] before using this function. RETURN VALUE ------------ -The *nutscan_scan_eaton_serial()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. +The *nutscan_scan_eaton_serial()* function returns a pointer to +a `nutscan_device_t` structure containing all found devices or +NULL if an error occurs or no device is found. SEE ALSO -------- diff --git a/docs/man/nutscan_scan_nut.txt b/docs/man/nutscan_scan_nut.txt index 20fd29aaea..c19f94e26f 100644 --- a/docs/man/nutscan_scan_nut.txt +++ b/docs/man/nutscan_scan_nut.txt @@ -10,24 +10,35 @@ SYNOPSIS -------- #include + #include /* useconds_t */ - nutscan_device_t * nutscan_scan_nut(const char * startIP, const char * stopIP, const char * port, long usec_timeout); + nutscan_device_t * nutscan_scan_nut( + const char * startIP, + const char * stopIP, + const char * port, + useconds_t usec_timeout); DESCRIPTION ----------- -The *nutscan_scan_nut()* function try to detect available NUT services and their associated devices. It issues a NUT request on every IP ranging from 'startIP' to 'stopIP'. 'startIP' is mandatory, 'stopIP' is optional. Those IP may be either IPv4 or IPv6 addresses or host names. +The *nutscan_scan_nut()* function try to detect available NUT services +and their associated devices. It issues a NUT request on every IP ranging +from 'startIP' to 'stopIP'. 'startIP' is mandatory, 'stopIP' is optional. +Those IP arguments may be either IPv4 or IPv6 addresses or host names. You MUST call linkman:nutscan_init[3] before using this function. A specific 'port' number may be passed, or NULL to use the default NUT port. -This function waits up to 'usec_timeout' microseconds before considering an IP address does not respond to NUT queries. +This function waits up to 'usec_timeout' microseconds before considering +an IP address does not respond to NUT queries. RETURN VALUE ------------ -The *nutscan_scan_nut()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. +The *nutscan_scan_nut()* function returns a pointer to a `nutscan_device_t` +structure containing all found devices or NULL if an error occurs or no +device is found. SEE ALSO -------- diff --git a/docs/man/nutscan_scan_snmp.txt b/docs/man/nutscan_scan_snmp.txt index b4c843ed2c..d08ee21943 100644 --- a/docs/man/nutscan_scan_snmp.txt +++ b/docs/man/nutscan_scan_snmp.txt @@ -10,21 +10,31 @@ SYNOPSIS -------- #include + #include /* useconds_t */ - nutscan_device_t * nutscan_scan_snmp(const char * start_ip,const char * stop_ip,long timeout, nutscan_snmp_t * sec); + nutscan_device_t * nutscan_scan_snmp( + const char * start_ip, + const char * stop_ip, + useconds_t timeout, + nutscan_snmp_t * sec); DESCRIPTION ----------- -The *nutscan_scan_snmp()* function try to detect NUT compatible SNMP devices. It tries SNMP queries on every IP ranging from 'start_ip' to 'stop_ip'. Those IP may be either IPv4 or IPv6 addresses or host names. +The *nutscan_scan_snmp()* function try to detect NUT compatible SNMP +devices. It tries SNMP queries on every IP ranging from 'start_ip' to +'stop_ip'. Those IP arguments may be either IPv4 or IPv6 addresses or +host names. You MUST call linkman:nutscan_init[3] before using this function. -This function waits up to 'timeout' microseconds before considering an IP address does not respond to SNMP queries. +This function waits up to 'timeout' microseconds before considering +an IP address does not respond to SNMP queries. A valid `nutscan_snmp_t` structure must be passed to this function. -The `nutscan_snmp_t` structure contains the following members which must be filled as described below: +The `nutscan_snmp_t` structure contains the following members which +must be filled as described below: char * 'community'; char * 'secLevel'; @@ -36,24 +46,35 @@ The `nutscan_snmp_t` structure contains the following members which must be fill If 'community' is not NULL, SNMP v1 request are sent using this 'community'. -If 'community' is NULL and 'secLevel' is NULL, SNMP v1 is selected and 'community' is set to "public". +If 'community' is NULL and 'secLevel' is NULL, SNMP v1 is selected +and 'community' is set to "public". -In the other cases, SNMP v3 is used. 'secLevel' may be one of `SNMP_SEC_LEVEL_NOAUTH`, `SNMP_SEC_LEVEL_AUTHNOPRIV` or `SNMP_SEC_LEVEL_AUTHPRIV`. 'secName' is the security name and must be non NULL. +In the other cases, SNMP v3 is used. 'secLevel' may be one +of `SNMP_SEC_LEVEL_NOAUTH`, `SNMP_SEC_LEVEL_AUTHNOPRIV` +or `SNMP_SEC_LEVEL_AUTHPRIV`. +'secName' is the security name and must be non NULL. -If 'secLevel' is set to `SNMP_SEC_LEVEL_AUTHNOPRIV`, 'authPassword' must be non NULL. +If 'secLevel' is set to `SNMP_SEC_LEVEL_AUTHNOPRIV`, 'authPassword' +must be non NULL. -If 'secLevel' is set to `SNMP_SEC_LEVEL_AUTHPRIV`, 'authPassword' and 'privPassword' must be non NULL. +If 'secLevel' is set to `SNMP_SEC_LEVEL_AUTHPRIV`, 'authPassword' +and 'privPassword' must be non NULL. -If 'authProtocol' is NULL, MD5 protocol is used. Else you can set 'authProtocol' to either "MD5" or "SHA". +If 'authProtocol' is NULL, MD5 protocol is used. +Else you can set 'authProtocol' to either "MD5" or "SHA". -If 'privProtocol' is NULL, DES protocol is used. Else you can set 'privProtocol' to either "AES" or "DES". +If 'privProtocol' is NULL, DES protocol is used. +Else you can set 'privProtocol' to either "AES" or "DES". -'peername' and 'handle' are used internally and do not need any initialization. +'peername' and 'handle' are used internally and do not need any +initialization. RETURN VALUE ------------ -The *nutscan_scan_snmp()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. +The *nutscan_scan_snmp()* function returns a pointer to a +`nutscan_device_t` structure containing all found devices +or NULL if an error occurs or no device is found. SEE ALSO -------- diff --git a/docs/man/nutscan_scan_usb.txt b/docs/man/nutscan_scan_usb.txt index 3c9001ea87..d80dd3bdfa 100644 --- a/docs/man/nutscan_scan_usb.txt +++ b/docs/man/nutscan_scan_usb.txt @@ -23,7 +23,9 @@ You MUST call linkman:nutscan_init[3] before using this function. RETURN VALUE ------------ -The *nutscan_scan_usb()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. +The *nutscan_scan_usb()* function returns a pointer to a `nutscan_device_t` +structure containing all found devices or NULL if an error occurs or no +device is found. SEE ALSO -------- diff --git a/docs/man/nutscan_scan_xml_http.txt b/docs/man/nutscan_scan_xml_http.txt index 61371dbf3c..9ddf553d5a 100644 --- a/docs/man/nutscan_scan_xml_http.txt +++ b/docs/man/nutscan_scan_xml_http.txt @@ -16,14 +16,19 @@ SYNOPSIS DESCRIPTION ----------- -The *nutscan_scan_xml_http()* function try to detect NUT compatible XML/HTTP devices. It does this by issuing a broadcast message on currently configured network interfaces. It waits up to 'usec_timeout' microseconds for a response from potential devices. +The *nutscan_scan_xml_http()* function tries to detect NUT compatible +XML/HTTP devices. It does this by issuing a broadcast message on +currently configured network interfaces. It waits up to 'usec_timeout' +microseconds for a response from potential devices. You MUST call linkman:nutscan_init[3] before using this function. RETURN VALUE ------------ -The *nutscan_scan_xml_http()* function returns a pointer to a `nutscan_device_t` structure containing all found devices or NULL if an error occurs or no device is found. +The *nutscan_scan_xml_http()* function returns a pointer to +a `nutscan_device_t` structure containing all found devices +or NULL if an error occurs or no device is found. SEE ALSO -------- From 1a8985b212fd38ad4204a2b40dbdf402550ce695 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 22:07:21 +0200 Subject: [PATCH 514/700] docs/man/nutscan_scan_ipmi.txt: document the method which now is implemented --- docs/man/nutscan_scan_ipmi.txt | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/man/nutscan_scan_ipmi.txt b/docs/man/nutscan_scan_ipmi.txt index 760413eb6c..31bc230ac5 100644 --- a/docs/man/nutscan_scan_ipmi.txt +++ b/docs/man/nutscan_scan_ipmi.txt @@ -11,15 +11,34 @@ SYNOPSIS #include - nutscan_device_t * nutscan_scan_ipmi(void); + nutscan_device_t * nutscan_scan_ipmi( + const char * startIP, + const char * stopIP, + nutscan_ipmi_t * sec); DESCRIPTION ----------- -The *nutscan_scan_ipmi()* function is not implemented yet. +The *nutscan_scan_ipmi()* function tries to detect IPMI manageable devices. + +If 'start_ip' is NULL, the function searches for a local PSU. + +Otherwise, it searches for remote hosts that would serve IPMI protocols, +and would try to authenticate using the data in 'sec' structure. +It issues a NUT request on every IP ranging from 'startIP' to 'stopIP', +where 'stopIP' is optional. Those IP arguments may be either IPv4 or IPv6 +addresses or host names. You MUST call linkman:nutscan_init[3] before using this function. +BUGS +---- + +Design or implementation of this function may be incomplete: until +recently, this man page stated that it was not implemented yet. +Source code is present, although some blocks are commented away +or hidden with `#if 0`. + RETURN VALUE ------------ From 0345d043e4391ed22bc6eedb1b1074dd9f69f12c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 22:16:12 +0200 Subject: [PATCH 515/700] docs/man/nutscan_scan_xml_http_range.txt: document the method which got extended from original nutscan_scan_xml_http() doing just a local broadcast --- docs/man/Makefile.am | 6 +-- docs/man/index.txt | 2 +- docs/man/nutscan.txt | 4 +- docs/man/nutscan_add_device_to_device.txt | 2 +- docs/man/nutscan_add_option_to_device.txt | 2 +- docs/man/nutscan_cidr_to_ip.txt | 2 +- docs/man/nutscan_display_parsable.txt | 2 +- docs/man/nutscan_display_ups_conf.txt | 2 +- docs/man/nutscan_free_device.txt | 2 +- docs/man/nutscan_get_serial_ports_list.txt | 2 +- docs/man/nutscan_init.txt | 2 +- docs/man/nutscan_new_device.txt | 2 +- docs/man/nutscan_scan_avahi.txt | 2 +- docs/man/nutscan_scan_eaton_serial.txt | 2 +- docs/man/nutscan_scan_ipmi.txt | 2 +- docs/man/nutscan_scan_nut.txt | 2 +- docs/man/nutscan_scan_snmp.txt | 2 +- docs/man/nutscan_scan_usb.txt | 2 +- docs/man/nutscan_scan_xml_http.txt | 42 ----------------- docs/man/nutscan_scan_xml_http_range.txt | 54 ++++++++++++++++++++++ 20 files changed, 75 insertions(+), 63 deletions(-) delete mode 100644 docs/man/nutscan_scan_xml_http.txt create mode 100644 docs/man/nutscan_scan_xml_http_range.txt diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index a904aa1b07..ea2486e3fa 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -172,7 +172,7 @@ SRC_DEV_PAGES = \ nutscan.txt \ nutscan_scan_snmp.txt \ nutscan_scan_usb.txt \ - nutscan_scan_xml_http.txt \ + nutscan_scan_xml_http_range.txt \ nutscan_scan_nut.txt \ nutscan_scan_avahi.txt \ nutscan_scan_ipmi.txt \ @@ -285,7 +285,7 @@ MAN3_DEV_PAGES = \ nutscan.3 \ nutscan_scan_snmp.3 \ nutscan_scan_usb.3 \ - nutscan_scan_xml_http.3 \ + nutscan_scan_xml_http_range.3 \ nutscan_scan_nut.3 \ nutscan_scan_avahi.3 \ nutscan_scan_ipmi.3 \ @@ -347,7 +347,7 @@ HTML_DEV_MANS = \ nutscan.html \ nutscan_scan_snmp.html \ nutscan_scan_usb.html \ - nutscan_scan_xml_http.html \ + nutscan_scan_xml_http_range.html \ nutscan_scan_nut.html \ nutscan_scan_avahi.html \ nutscan_scan_ipmi.html \ diff --git a/docs/man/index.txt b/docs/man/index.txt index a06b294c26..c7a4bb53cc 100644 --- a/docs/man/index.txt +++ b/docs/man/index.txt @@ -100,7 +100,7 @@ Device discovery library - linkman:nutscan[3] - linkman:nutscan_scan_usb[3] - linkman:nutscan_scan_snmp[3] -- linkman:nutscan_scan_xml_http[3] +- linkman:nutscan_scan_xml_http_range[3] - linkman:nutscan_scan_nut[3] - linkman:nutscan_scan_avahi[3] - linkman:nutscan_scan_ipmi[3] diff --git a/docs/man/nutscan.txt b/docs/man/nutscan.txt index 4eac97b901..99cb2471e2 100644 --- a/docs/man/nutscan.txt +++ b/docs/man/nutscan.txt @@ -28,7 +28,7 @@ Then, to discover new devices, use the appropriate function: - linkman:nutscan_scan_usb[3] for supported USB devices, - linkman:nutscan_scan_snmp[3] for supported SNMP agents, -- linkman:nutscan_scan_xml_http[3] for Eaton Network Management Card, +- linkman:nutscan_scan_xml_http_range[3] for Eaton Network Management Card, - linkman:nutscan_scan_nut[3] for NUT servers (upsd), using the classic method (search for port), - linkman:nutscan_scan_avahi[3] for NUT servers (upsd), using the mDNS @@ -57,7 +57,7 @@ SEE ALSO linkman:nut-scanner[8], linkman:nutscan_scan_usb[3], linkman:nutscan_scan_snmp[3], -linkman:nutscan_scan_xml_http[3], linkman:nutscan_scan_nut[3], +linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_parsable[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_new_device[3], linkman:nutscan_free_device[3], diff --git a/docs/man/nutscan_add_device_to_device.txt b/docs/man/nutscan_add_device_to_device.txt index bd34087e26..6f5aed610a 100644 --- a/docs/man/nutscan_add_device_to_device.txt +++ b/docs/man/nutscan_add_device_to_device.txt @@ -51,7 +51,7 @@ Technically, the function is currently defined in 'nutscan-device.h' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], diff --git a/docs/man/nutscan_add_option_to_device.txt b/docs/man/nutscan_add_option_to_device.txt index a9efcbd4a6..2dbf6690c0 100644 --- a/docs/man/nutscan_add_option_to_device.txt +++ b/docs/man/nutscan_add_option_to_device.txt @@ -46,7 +46,7 @@ Technically, the function is currently defined in 'nutscan-device.h' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], diff --git a/docs/man/nutscan_cidr_to_ip.txt b/docs/man/nutscan_cidr_to_ip.txt index 5b6787fb7d..5f18213eeb 100644 --- a/docs/man/nutscan_cidr_to_ip.txt +++ b/docs/man/nutscan_cidr_to_ip.txt @@ -37,7 +37,7 @@ Technically, the function is currently defined in 'nutscan-ip.h' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_parsable[3], linkman:nutscan_display_ups_conf[3] diff --git a/docs/man/nutscan_display_parsable.txt b/docs/man/nutscan_display_parsable.txt index e395a2b431..9e275153be 100644 --- a/docs/man/nutscan_display_parsable.txt +++ b/docs/man/nutscan_display_parsable.txt @@ -32,7 +32,7 @@ which is: SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_display_ups_conf.txt b/docs/man/nutscan_display_ups_conf.txt index d95f47a7b4..b79c90fa0c 100644 --- a/docs/man/nutscan_display_ups_conf.txt +++ b/docs/man/nutscan_display_ups_conf.txt @@ -24,7 +24,7 @@ copied into the 'ups.conf' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_free_device.txt b/docs/man/nutscan_free_device.txt index c9d4192b28..4420f362e2 100644 --- a/docs/man/nutscan_free_device.txt +++ b/docs/man/nutscan_free_device.txt @@ -29,7 +29,7 @@ Technically, the function is currently defined in 'nutscan-device.h' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], diff --git a/docs/man/nutscan_get_serial_ports_list.txt b/docs/man/nutscan_get_serial_ports_list.txt index 8535edd4d7..8b38bedbbf 100644 --- a/docs/man/nutscan_get_serial_ports_list.txt +++ b/docs/man/nutscan_get_serial_ports_list.txt @@ -52,7 +52,7 @@ Technically, the function is currently defined in 'nutscan-serial.h' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_eaton_serial[3], diff --git a/docs/man/nutscan_init.txt b/docs/man/nutscan_init.txt index 61ef3dcc52..98b37a2adf 100644 --- a/docs/man/nutscan_init.txt +++ b/docs/man/nutscan_init.txt @@ -43,7 +43,7 @@ SEE ALSO -------- linkman:nutscan_init[3], linkman:nutscan_scan_usb[3], -linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_new_device.txt b/docs/man/nutscan_new_device.txt index 57ff82d40d..cbcfd9259b 100644 --- a/docs/man/nutscan_new_device.txt +++ b/docs/man/nutscan_new_device.txt @@ -33,7 +33,7 @@ Technically, the function is currently defined in 'nutscan-device.h' file. SEE ALSO -------- -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_scan_snmp[3] linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3] diff --git a/docs/man/nutscan_scan_avahi.txt b/docs/man/nutscan_scan_avahi.txt index 72582767dc..01f02e9e92 100644 --- a/docs/man/nutscan_scan_avahi.txt +++ b/docs/man/nutscan_scan_avahi.txt @@ -38,7 +38,7 @@ SEE ALSO -------- linkman:nutscan_init[3], -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_scan_eaton_serial.txt b/docs/man/nutscan_scan_eaton_serial.txt index 735886de5d..068db12877 100644 --- a/docs/man/nutscan_scan_eaton_serial.txt +++ b/docs/man/nutscan_scan_eaton_serial.txt @@ -37,7 +37,7 @@ SEE ALSO -------- linkman:nutscan_init[3], -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_scan_ipmi.txt b/docs/man/nutscan_scan_ipmi.txt index 31bc230ac5..9d463f477d 100644 --- a/docs/man/nutscan_scan_ipmi.txt +++ b/docs/man/nutscan_scan_ipmi.txt @@ -48,7 +48,7 @@ SEE ALSO -------- linkman:nutscan_init[3], -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_scan_nut.txt b/docs/man/nutscan_scan_nut.txt index c19f94e26f..7d0b543c84 100644 --- a/docs/man/nutscan_scan_nut.txt +++ b/docs/man/nutscan_scan_nut.txt @@ -44,7 +44,7 @@ SEE ALSO -------- linkman:nutscan_init[3], -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_scan_snmp.txt b/docs/man/nutscan_scan_snmp.txt index d08ee21943..ecc893ffb9 100644 --- a/docs/man/nutscan_scan_snmp.txt +++ b/docs/man/nutscan_scan_snmp.txt @@ -80,7 +80,7 @@ SEE ALSO -------- linkman:nutscan_init[3], -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_scan_usb.txt b/docs/man/nutscan_scan_usb.txt index d80dd3bdfa..6253326607 100644 --- a/docs/man/nutscan_scan_usb.txt +++ b/docs/man/nutscan_scan_usb.txt @@ -31,7 +31,7 @@ SEE ALSO -------- linkman:nutscan_init[3], -linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http[3], +linkman:nutscan_scan_snmp[3], linkman:nutscan_scan_xml_http_range[3], linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], diff --git a/docs/man/nutscan_scan_xml_http.txt b/docs/man/nutscan_scan_xml_http.txt deleted file mode 100644 index 9ddf553d5a..0000000000 --- a/docs/man/nutscan_scan_xml_http.txt +++ /dev/null @@ -1,42 +0,0 @@ -NUTSCAN_SCAN_XML_HTTP(3) -======================== - -NAME ----- - -nutscan_scan_xml_http - Scan network for XML/HTTP devices. - -SYNOPSIS --------- - - #include - - nutscan_device_t * nutscan_scan_xml_http(long usec_timeout); - -DESCRIPTION ------------ - -The *nutscan_scan_xml_http()* function tries to detect NUT compatible -XML/HTTP devices. It does this by issuing a broadcast message on -currently configured network interfaces. It waits up to 'usec_timeout' -microseconds for a response from potential devices. - -You MUST call linkman:nutscan_init[3] before using this function. - -RETURN VALUE ------------- - -The *nutscan_scan_xml_http()* function returns a pointer to -a `nutscan_device_t` structure containing all found devices -or NULL if an error occurs or no device is found. - -SEE ALSO --------- - -linkman:nutscan_init[3], -linkman:nutscan_scan_usb[3], linkman:nutscan_scan_snmp[3], -linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], -linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], -linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], -linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], -linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3] diff --git a/docs/man/nutscan_scan_xml_http_range.txt b/docs/man/nutscan_scan_xml_http_range.txt new file mode 100644 index 0000000000..15cb2af5c1 --- /dev/null +++ b/docs/man/nutscan_scan_xml_http_range.txt @@ -0,0 +1,54 @@ +NUTSCAN_SCAN_XML_HTTP_RANGE(3) +============================== + +NAME +---- + +nutscan_scan_xml_http_range - Scan network for XML/HTTP devices. + +SYNOPSIS +-------- + + #include + #include /* useconds_t */ + + nutscan_device_t * nutscan_scan_xml_http_range( + const char * start_ip, + const char * end_ip, + useconds_t usec_timeout, + nutscan_xml_t * sec) + +DESCRIPTION +----------- + +The *nutscan_scan_xml_http_range()* function tries to detect NUT compatible +XML/HTTP devices. + +If 'start_ip' is NULL, the function does this by issuing a broadcast message +on currently configured network interfaces. + +Otherwise, it queries every IP ranging from 'start_ip' to 'stop_ip'. +Those IP arguments may be either IPv4 or IPv6 addresses or host names. + +It waits up to 'usec_timeout' microseconds for a response from potential +devices. + +You MUST call linkman:nutscan_init[3] before using this function. + +RETURN VALUE +------------ + +The *nutscan_scan_xml_http_range()* function returns a pointer to +a `nutscan_device_t` structure containing all found devices +or NULL if an error occurs or no device is found. + +SEE ALSO +-------- + +linkman:nutscan_init[3], +linkman:nutscan_scan_usb[3], linkman:nutscan_scan_snmp[3], +linkman:nutscan_scan_nut[3], linkman:nutscan_scan_avahi[3], +linkman:nutscan_scan_ipmi[3], linkman:nutscan_display_ups_conf[3], +linkman:nutscan_display_parsable[3], linkman:nutscan_new_device[3], +linkman:nutscan_free_device[3], linkman:nutscan_add_option_to_device[3], +linkman:nutscan_add_device_to_device[3], linkman:nutscan_scan_eaton_serial[3] From 2baa2e8140640c0e6f661d602b206fc30d49fd99 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 22:20:03 +0200 Subject: [PATCH 516/700] docs/man/nutscan*.txt: document methods with (void) arg list as such --- docs/man/nutscan_init.txt | 2 +- docs/man/nutscan_new_device.txt | 2 +- docs/man/nutscan_scan_usb.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/man/nutscan_init.txt b/docs/man/nutscan_init.txt index 98b37a2adf..17e6ddf91d 100644 --- a/docs/man/nutscan_init.txt +++ b/docs/man/nutscan_init.txt @@ -11,7 +11,7 @@ SYNOPSIS #include - void nutscan_init(); + void nutscan_init(void); DESCRIPTION ----------- diff --git a/docs/man/nutscan_new_device.txt b/docs/man/nutscan_new_device.txt index cbcfd9259b..81cf26d164 100644 --- a/docs/man/nutscan_new_device.txt +++ b/docs/man/nutscan_new_device.txt @@ -11,7 +11,7 @@ SYNOPSIS #include - nutscan_device_t * nutscan_new_device(); + nutscan_device_t * nutscan_new_device(void); DESCRIPTION ----------- diff --git a/docs/man/nutscan_scan_usb.txt b/docs/man/nutscan_scan_usb.txt index 6253326607..878c712a92 100644 --- a/docs/man/nutscan_scan_usb.txt +++ b/docs/man/nutscan_scan_usb.txt @@ -11,7 +11,7 @@ SYNOPSIS #include - nutscan_device_t * nutscan_scan_usb(); + nutscan_device_t * nutscan_scan_usb(void); DESCRIPTION ----------- From 9f8c6532ca130b20a22efc528a09b9ae773f71a1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 22:41:16 +0200 Subject: [PATCH 517/700] docs/man/upscli*.txt: wrap long lines, minor rephrase --- docs/man/upscli_add_host_cert.txt | 6 +++--- docs/man/upscli_cleanup.txt | 3 ++- docs/man/upscli_disconnect.txt | 7 ++++--- docs/man/upscli_fd.txt | 10 ++++++---- docs/man/upscli_get.txt | 31 ++++++++++++++++++------------- docs/man/upscli_init.txt | 4 ++-- docs/man/upscli_list_next.txt | 4 ++-- docs/man/upscli_readline.txt | 11 ++++++----- docs/man/upscli_sendline.txt | 8 ++++---- docs/man/upscli_splitaddr.txt | 1 - 10 files changed, 47 insertions(+), 38 deletions(-) diff --git a/docs/man/upscli_add_host_cert.txt b/docs/man/upscli_add_host_cert.txt index e558baf0ad..6d9050d3c1 100644 --- a/docs/man/upscli_add_host_cert.txt +++ b/docs/man/upscli_add_host_cert.txt @@ -24,9 +24,9 @@ The rule is composed of the certificate name 'certname 'expected for the host, 'certverify' if the certificate must be validated for the host and 'forcessl' if a secured connection must be used to connect to the host. -Note: This call only functions if upsclient has been compiled with NSS support. -If instead it was compiled with OpenSSL support, this function contains an empty -definition and will take no action when called. +Note: This call only functions if upsclient has been compiled with NSS +support. If instead it was compiled with OpenSSL support, this function +contains an empty definition and will take no action when called. RETURN VALUE ------------ diff --git a/docs/man/upscli_cleanup.txt b/docs/man/upscli_cleanup.txt index 1798a4f868..60431ec30c 100644 --- a/docs/man/upscli_cleanup.txt +++ b/docs/man/upscli_cleanup.txt @@ -22,7 +22,8 @@ used internally in upsclient module. RETURN VALUE ------------ -The *upscli_cleanup()* function returns 1 on success, or -1 if an error occurs. +The *upscli_cleanup()* function returns 1 on success, or -1 if an error +occurs. SEE ALSO -------- diff --git a/docs/man/upscli_disconnect.txt b/docs/man/upscli_disconnect.txt index c8f11d1c7f..76a8e61100 100644 --- a/docs/man/upscli_disconnect.txt +++ b/docs/man/upscli_disconnect.txt @@ -17,9 +17,10 @@ DESCRIPTION ----------- The *upscli_disconnect()* function takes the pointer 'ups' to a -`UPSCONN_t` state structure, shuts down the connection to the server, and -frees dynamic memory used by the state structure. The `UPSCONN_t` structure -is no longer valid after this function is called. +`UPSCONN_t` state structure, shuts down the connection to the server, +and frees dynamic memory used by the state structure. + +The `UPSCONN_t` structure is no longer valid after this function is called. This function must be called, or your program will leak memory and file descriptors. diff --git a/docs/man/upscli_fd.txt b/docs/man/upscli_fd.txt index c42b098d1a..1fd2971c08 100644 --- a/docs/man/upscli_fd.txt +++ b/docs/man/upscli_fd.txt @@ -16,9 +16,9 @@ SYNOPSIS DESCRIPTION ----------- -The *upscli_fd()* function takes the pointer 'ups' to a -`UPSCONN_t` state structure and returns the value of the file descriptor -for that connection, if any. +The *upscli_fd()* function takes the pointer 'ups' to a `UPSCONN_t` +state structure and returns the value of the file descriptor for +that connection, if any. This may be useful for determining if the connection to linkman:upsd[8] has been lost. @@ -27,7 +27,9 @@ RETURN VALUE ------------ The *upscli_fd()* function returns the file descriptor, which -may be any non-negative number. It returns -1 if an error occurs. +may be any non-negative number. + +It returns -1 if an error occurs. SEE ALSO -------- diff --git a/docs/man/upscli_get.txt b/docs/man/upscli_get.txt index 28711d3c0c..0d524fe9ba 100644 --- a/docs/man/upscli_get.txt +++ b/docs/man/upscli_get.txt @@ -22,15 +22,17 @@ The *upscli_get()* function takes the pointer 'ups' to a 'numq' query elements. It builds a properly-formatted request from those elements and transmits it to linkman:upsd[8]. -Upon success, the response will be split into separate components. A -pointer to those components will be returned in 'answer'. The -number of usable answer components will be returned in 'numa'. +Upon success, the response will be split into separate components. +A pointer to those components will be returned in 'answer'. +The number of usable answer components will be returned in 'numa'. USES ---- -This function implements the "GET" command in the protocol. As a -result, you can use it to request many different things from the server. +This function implements the "GET" command in the protocol. +As a result, you can use it to request many different things +from the server. + Some examples are: * GET NUMLOGINS @@ -62,6 +64,7 @@ ANSWER FORMATTING ----------------- The raw response from `upsd` to the above query would be `NUMLOGINS su700 1`. + Since this is split up for you, the values work out like this: ------ @@ -79,8 +82,8 @@ ERROR CHECKING -------------- This function will check your query against the response from -linkman:upsd[8]. For example, if you send "VAR" "su700" "ups.status", it -will expect to see those at the beginning of the response. +linkman:upsd[8]. For example, if you send "VAR" "su700" "ups.status", +it will expect to see those at the beginning of the response. If the results from *upsd* do not pass this case-insensitive test against your request, this function will return an error. When this @@ -100,8 +103,8 @@ which were returned by the most recent call. You also must not attempt to use more than 'numa' elements in 'answer'. Such behavior is undefined, and may yield bogus data or a crash. -The array will be deleted after calling linkman:upscli_disconnect[3]. Any -access after that point is also undefined. +The array will be deleted after calling linkman:upscli_disconnect[3]. +Any access after that point is also undefined. RETURN VALUE ------------ @@ -109,10 +112,12 @@ RETURN VALUE The *upscli_get()* function returns 0 on success, or -1 if an error occurs. -If *upsd* disconnects, you may need to handle or ignore `SIGPIPE` in order to -prevent your program from terminating the next time that the library writes to -the disconnected socket. The following code in your initialization function -will allow the *upscli_get()* call to return an error in that case: +If *upsd* disconnects, you may need to handle or ignore `SIGPIPE` +in order to prevent your program from terminating the next time that +the library writes to the disconnected socket. + +The following code in your initialization function will allow the +*upscli_get()* call to return an error in that case: #include ... diff --git a/docs/man/upscli_init.txt b/docs/man/upscli_init.txt index 156f7672cc..16cd0e9690 100644 --- a/docs/man/upscli_init.txt +++ b/docs/man/upscli_init.txt @@ -35,8 +35,8 @@ Alternatively, the c_rehash utility (provided by openssl-perl) can take a directory and iterate it to link all certificates found in that directory, in the manner described above. -If compiled with NSS, certpath refers to a directory -containing database files. +If compiled with NSS, certpath refers to a directory containing database +files. If compiled with NSS and using SSL, you can specify 'certname' the name of the certificate to send to upsd and 'certpasswd' the password used diff --git a/docs/man/upscli_list_next.txt b/docs/man/upscli_list_next.txt index 88c53f6c6d..9612136cde 100644 --- a/docs/man/upscli_list_next.txt +++ b/docs/man/upscli_list_next.txt @@ -42,8 +42,8 @@ ANSWER FORMATTING ----------------- The contents of 'numa' and 'answer' work just like a call to -linkman:upscli_get[3]. The values returned by linkman:upsd[8] are identical to -a single item request, so this is not surprising. +linkman:upscli_get[3]. The values returned by linkman:upsd[8] are +identical to a single item request, so this is not surprising. ERROR CHECKING -------------- diff --git a/docs/man/upscli_readline.txt b/docs/man/upscli_readline.txt index a2c082777e..6b0905478f 100644 --- a/docs/man/upscli_readline.txt +++ b/docs/man/upscli_readline.txt @@ -19,9 +19,10 @@ SYNOPSIS DESCRIPTION ----------- -The *upscli_readline()* and *upscli_readline_timeout()* functions take the -pointer 'ups' to a `UPSCONN_t` state structure, receive a single line from the -server, and copy up to 'buflen' bytes of the response into the buffer 'buf'. +The *upscli_readline()* and *upscli_readline_timeout()* functions take +the pointer 'ups' to a `UPSCONN_t` state structure, receive a single +line from the server, and copy up to 'buflen' bytes of the response +into the buffer 'buf'. Some parsing of the string occurs during reception. In particular, ERR messages from linkman:upsd[8] are detected and will cause this @@ -35,8 +36,8 @@ freedom, and uses NUT default network timeout (5 seconds). RETURN VALUE ------------ -The *upscli_readline()* and *upscli_readline_timeout()* functions return 0 on -success, or -1 if an error occurs. +The *upscli_readline()* and *upscli_readline_timeout()* functions +return 0 on success, or -1 if an error occurs. SEE ALSO -------- diff --git a/docs/man/upscli_sendline.txt b/docs/man/upscli_sendline.txt index bc0bcba6c5..9a92a4b4da 100644 --- a/docs/man/upscli_sendline.txt +++ b/docs/man/upscli_sendline.txt @@ -20,8 +20,8 @@ DESCRIPTION ----------- The *upscli_sendline()* and *upscli_sendline_timeout()* functions take the -pointer 'ups' to a `UPSCONN_t` state structure and transmit a buffer 'buf' of -size 'buflen' to the server. +pointer 'ups' to a `UPSCONN_t` state structure and transmit a buffer 'buf' +of size 'buflen' to the server. The data in 'buf' must be a fully formatted protocol command as no parsing of the buffer occurs within this function. @@ -34,8 +34,8 @@ freedom, and uses an immediate timeout (0 second). RETURN VALUE ------------ -The *upscli_sendline()* and *upscli_sendline_timeout()* functions return 0 on -success, or -1 if an error occurs. +The *upscli_sendline()* and *upscli_sendline_timeout()* functions +return 0 on success, or -1 if an error occurs. SEE ALSO -------- diff --git a/docs/man/upscli_splitaddr.txt b/docs/man/upscli_splitaddr.txt index 03fd9ff253..b7054939f6 100644 --- a/docs/man/upscli_splitaddr.txt +++ b/docs/man/upscli_splitaddr.txt @@ -9,7 +9,6 @@ upscli_splitaddr - split a listening address into its components SYNOPSIS -------- - #include int upscli_splitaddr(const char *buf, char **hostname, From aaf73ca7762058b8b290da90ffe073e50fcddcb2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 22:41:46 +0200 Subject: [PATCH 518/700] docs/man/upscli_strerror.txt: fix return type modifier to const --- docs/man/upscli_strerror.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/man/upscli_strerror.txt b/docs/man/upscli_strerror.txt index fa6da66e93..b9b8462a1f 100644 --- a/docs/man/upscli_strerror.txt +++ b/docs/man/upscli_strerror.txt @@ -11,7 +11,7 @@ SYNOPSIS #include - char *upscli_strerror(UPSCONN_t *ups); + const char *upscli_strerror(UPSCONN_t *ups); DESCRIPTION ----------- From b4554aef374a50847af8d372a1a20d2e895bfcdf Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 22:49:19 +0200 Subject: [PATCH 519/700] docs/man/libnutclient_commands.txt: wrap long lines, minor rephrase, bullet points; added optional param arg in API --- docs/man/libnutclient_commands.txt | 49 ++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/docs/man/libnutclient_commands.txt b/docs/man/libnutclient_commands.txt index cf2e9fce85..6d3b83abc1 100644 --- a/docs/man/libnutclient_commands.txt +++ b/docs/man/libnutclient_commands.txt @@ -4,9 +4,11 @@ LIBNUTCLIENT_COMMANDS(3) NAME ---- -libnutclient_commands, nutclient_get_device_commands, nutclient_has_device_command, -nutclient_get_device_command_description, nutclient_execute_device_command - -Instant command related functions in Network UPS Tools high-level client access library +libnutclient_commands, nutclient_get_device_commands, +nutclient_has_device_command, nutclient_get_device_command_description, +nutclient_execute_device_command - +Instant command related functions in Network UPS Tools high-level client +access library SYNOPSIS -------- @@ -15,30 +17,51 @@ SYNOPSIS typedef void* NUTCLIENT_t; - strarr nutclient_get_device_commands(NUTCLIENT_t client, const char* dev); - int nutclient_has_device_command(NUTCLIENT_t client, const char* dev, const char* cmd); - char* nutclient_get_device_command_description(NUTCLIENT_t client, const char* dev, const char* cmd); - void nutclient_execute_device_command(NUTCLIENT_t client, const char* dev, const char* cmd); + strarr nutclient_get_device_commands( + NUTCLIENT_t client, + const char* dev); + + int nutclient_has_device_command( + NUTCLIENT_t client, + const char* dev, const char* cmd); + + char* nutclient_get_device_command_description( + NUTCLIENT_t client, + const char* dev, const char* cmd); + + void nutclient_execute_device_command( + NUTCLIENT_t client, + const char* dev, const char* cmd, + const char* param=""); DESCRIPTION ----------- These functions allow to manage instant commands of devices. -The *nutclient_get_device_commands()* function retrieve the list of command names for a device. +* The *nutclient_get_device_commands()* function retrieves + the list of command names for a device. ++ The returned strarr must be freed by 'strarr_free'. -The *nutclient_has_device_command* function test if the specified command is supported by the device. +* The *nutclient_has_device_command* function tests if the + specified command is supported by the device. ++ Return 1 is supported and 0 if not. -The *nutclient_get_device_command_description* function retrieve the command description, if any. +* The *nutclient_get_device_command_description* function + retrieves the command description, if any. ++ The returned string must be freed. -The *nutclient_execute_device_command* intend to execute the instant command. +* The *nutclient_execute_device_command* intends to execute + the instant command, with an optional parameter. + +Common arguments: -'dev' is the device name. +* 'dev' is the device name. -'cmd' is the instant command name. +* 'cmd' is the instant command name. SEE ALSO -------- From c65a0e23ab695610b50b83602b8cc7ced33893a1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 22:51:37 +0200 Subject: [PATCH 520/700] docs/man/libnutclient_devices.txt: wrap long lines, minor rephrase, bullet points --- docs/man/libnutclient_devices.txt | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/docs/man/libnutclient_devices.txt b/docs/man/libnutclient_devices.txt index bca57b9046..6dfaffa83f 100644 --- a/docs/man/libnutclient_devices.txt +++ b/docs/man/libnutclient_devices.txt @@ -4,8 +4,10 @@ LIBNUTCLIENT_DEVICES(3) NAME ---- -libnutclient_devices, nutclient_get_devices, nutclient_has_device, nutclient_get_device_description - -Device related functions in Network UPS Tools high-level client access library +libnutclient_devices, nutclient_get_devices, nutclient_has_device, +nutclient_get_device_description - +Device related functions in Network UPS Tools high-level client access +library SYNOPSIS -------- @@ -15,7 +17,9 @@ SYNOPSIS typedef void* NUTCLIENT_t; strarr nutclient_get_devices(NUTCLIENT_t client); + int nutclient_has_device(NUTCLIENT_t client, const char* dev); + char* nutclient_get_device_description(NUTCLIENT_t client, const char* dev); DESCRIPTION @@ -23,15 +27,22 @@ DESCRIPTION These functions allow to manage devices. -The *nutclient_get_devices()* function retrieve the list of devices monitored by a client. +* The *nutclient_get_devices()* function retrieves the list of devices + monitored by a client. ++ The returned strarr must be freed by 'strarr_free'. -The *nutclient_has_device()* function test if a device is monitored by a client. +* The *nutclient_has_device()* function tests if a device is monitored + by a client. -The *nutclient_get_device_description()* function retrieve the device description. +* The *nutclient_get_device_description()* function retrieves the device + description. ++ The returned description string must be freed. -'dev' is the device name. +Common arguments: + +* 'dev' is the device name. SEE ALSO -------- @@ -41,4 +52,3 @@ linkman:libnutclient_commands[3] linkman:libnutclient_devices[3] linkman:libnutclient_general[3] linkman:libnutclient_variables[3] - From f1ee8f58bb94a918fff35c6934de7e93b4b1f9e9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 22:53:02 +0200 Subject: [PATCH 521/700] docs/man/libnutclient_devices.txt, libnutclient_commands.txt: synopsis for strarr --- docs/man/libnutclient_commands.txt | 2 ++ docs/man/libnutclient_devices.txt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/docs/man/libnutclient_commands.txt b/docs/man/libnutclient_commands.txt index 6d3b83abc1..33a90ca559 100644 --- a/docs/man/libnutclient_commands.txt +++ b/docs/man/libnutclient_commands.txt @@ -17,6 +17,8 @@ SYNOPSIS typedef void* NUTCLIENT_t; + typedef char** strarr; + strarr nutclient_get_device_commands( NUTCLIENT_t client, const char* dev); diff --git a/docs/man/libnutclient_devices.txt b/docs/man/libnutclient_devices.txt index 6dfaffa83f..700673f04a 100644 --- a/docs/man/libnutclient_devices.txt +++ b/docs/man/libnutclient_devices.txt @@ -16,6 +16,8 @@ SYNOPSIS typedef void* NUTCLIENT_t; + typedef char** strarr; + strarr nutclient_get_devices(NUTCLIENT_t client); int nutclient_has_device(NUTCLIENT_t client, const char* dev); From ddbc8083988d2d2320df6e000c7bc96d5086700b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 15 Apr 2022 23:11:59 +0200 Subject: [PATCH 522/700] docs/man/libnutclient*.txt: wrap long lines, minor rephrase, bullet points --- docs/man/libnutclient_general.txt | 4 +- docs/man/libnutclient_misc.txt | 13 ++++-- docs/man/libnutclient_tcp.txt | 33 ++++++++++----- docs/man/libnutclient_variables.txt | 66 ++++++++++++++++++++--------- docs/man/libupsclient-config.txt | 4 +- docs/nut.dict | 3 +- 6 files changed, 86 insertions(+), 37 deletions(-) diff --git a/docs/man/libnutclient_general.txt b/docs/man/libnutclient_general.txt index 4b01aff60f..7d41acf136 100644 --- a/docs/man/libnutclient_general.txt +++ b/docs/man/libnutclient_general.txt @@ -5,7 +5,8 @@ NAME ---- libnutclient_general, nutclient_destroy, strarr_alloc, strarr_free - -General and utility functions in Network UPS Tools high-level client access library +General and utility functions in Network UPS Tools high-level client +access library SYNOPSIS -------- @@ -19,6 +20,7 @@ SYNOPSIS typedef char** strarr; strarr strarr_alloc(unsigned short count); + void strarr_free(strarr arr); DESCRIPTION diff --git a/docs/man/libnutclient_misc.txt b/docs/man/libnutclient_misc.txt index 52caf614a4..8ef5ef91a1 100644 --- a/docs/man/libnutclient_misc.txt +++ b/docs/man/libnutclient_misc.txt @@ -16,11 +16,18 @@ SYNOPSIS typedef void* NUTCLIENT_t; - void nutclient_authenticate(NUTCLIENT_t client, const char* login, const char* passwd); + void nutclient_authenticate( + NUTCLIENT_t client, + const char* login, const char* passwd); + void nutclient_logout(NUTCLIENT_t client); + void nutclient_device_login(NUTCLIENT_t client, const char* dev); + int nutclient_get_device_num_logins(NUTCLIENT_t client, const char* dev); + void nutclient_device_master(NUTCLIENT_t client, const char* dev); + void nutclient_device_forced_shutdown(NUTCLIENT_t client, const char* dev); DESCRIPTION @@ -28,9 +35,9 @@ DESCRIPTION The *nutclient_authenticate()* function authenticates the user. -'login' is the user name. +* 'login' is the user name. -'passwd' is the user password. +* 'passwd' is the user password. The *nutclient_logout()* function disconnects gracefully from the server. diff --git a/docs/man/libnutclient_tcp.txt b/docs/man/libnutclient_tcp.txt index 8dc4d39e32..367f7f4107 100644 --- a/docs/man/libnutclient_tcp.txt +++ b/docs/man/libnutclient_tcp.txt @@ -7,7 +7,8 @@ NAME libnutclient_tcp, nutclient_tcp_create_client, nutclient_tcp_is_connected, nutclient_tcp_disconnect, nutclient_tcp_reconnect, nutclient_tcp_set_timeout, nutclient_tcp_get_timeout - -TCP protocol related function for Network UPS Tools high-level client access library +TCP protocol related function for Network UPS Tools high-level client +access library SYNOPSIS -------- @@ -16,35 +17,47 @@ SYNOPSIS typedef NUTCLIENT_t NUTCLIENT_TCP_t; - NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, unsigned short port); + NUTCLIENT_TCP_t nutclient_tcp_create_client( + const char* host, unsigned short port); + int nutclient_tcp_is_connected(NUTCLIENT_TCP_t client); + void nutclient_tcp_disconnect(NUTCLIENT_TCP_t client); + int nutclient_tcp_reconnect(NUTCLIENT_TCP_t client); + void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, long timeout); + long nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client); DESCRIPTION ----------- -These functions allow to manage connections to linkman:upsd[8] using NUT TCP protocol. +These functions allow to manage connections to linkman:upsd[8] +using NUT TCP protocol. -The *nutclient_tcp_create_client()* function create the 'NUTCLIENT_TCP_t' context and -intend to connect to upsd at 'host' and 'port'. The context must be freed by 'nutclient_destroy()' +The *nutclient_tcp_create_client()* function create the 'NUTCLIENT_TCP_t' +context and intend to connect to upsd at 'host' and 'port'. +The context must be freed by 'nutclient_destroy()' -'host' can be a sever name or a valid IPv4 or IPv6 address like "localhost", "127.0.0.1" or "::1". +* 'host' can be a sever name or a valid IPv4 or IPv6 address like +"localhost", "127.0.0.1" or "::1". -'port' is a valid TCP port, generally 3493. +* 'port' is a valid TCP port, generally 3493. The *nutclient_tcp_is_connected()* function test if the connection is valid. -The *nutclient_tcp_disconnect()* function force to disconnect the specified connection. +The *nutclient_tcp_disconnect()* function force to disconnect the specified +connection. The *nutclient_tcp_reconnect()* function force to reconnect a connection, disconnecting it if needed. -The *nutclient_tcp_set_timeout()* function set the timeout duration for I/O operations. +The *nutclient_tcp_set_timeout()* function set the timeout duration +for I/O operations. -The *nutclient_tcp_get_timeout()* function retrieve the timeout duration for I/O operations. +The *nutclient_tcp_get_timeout()* function retrieve the timeout duration +for I/O operations. 'timeout' values are specified in seconds, negatives values for blocking. diff --git a/docs/man/libnutclient_variables.txt b/docs/man/libnutclient_variables.txt index b2de4269a4..3d90eed1d9 100644 --- a/docs/man/libnutclient_variables.txt +++ b/docs/man/libnutclient_variables.txt @@ -6,9 +6,11 @@ NAME libnutclient_variables, nutclient_get_device_variables, nutclient_get_device_rw_variables, nutclient_has_device_variable, -nutclient_get_device_variable_description, nutclient_get_device_variable_values, +nutclient_get_device_variable_description, +nutclient_get_device_variable_values, nutclient_set_device_variable_value, nutclient_set_device_variable_values - -Variable related functions in Network UPS Tools high-level client access library +Variable related functions in Network UPS Tools high-level client access +library SYNOPSIS -------- @@ -17,45 +19,69 @@ SYNOPSIS typedef void* NUTCLIENT_t; - strarr nutclient_get_device_variables(NUTCLIENT_t client, const char* dev); - strarr nutclient_get_device_rw_variables(NUTCLIENT_t client, const char* dev); - int nutclient_has_device_variable(NUTCLIENT_t client, const char* dev, const char* var); - char* nutclient_get_device_variable_description(NUTCLIENT_t client, const char* dev, const char* var); - strarr nutclient_get_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var); - void nutclient_set_device_variable_value(NUTCLIENT_t client, const char* dev, const char* var, const char* value); - void nutclient_set_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var, const strarr values); + typedef char** strarr; + + strarr nutclient_get_device_variables(NUTCLIENT_t client, + const char* dev); + + strarr nutclient_get_device_rw_variables(NUTCLIENT_t client, + const char* dev); + + int nutclient_has_device_variable(NUTCLIENT_t client, + const char* dev, const char* var); + + char* nutclient_get_device_variable_description(NUTCLIENT_t client, + const char* dev, const char* var); + + strarr nutclient_get_device_variable_values(NUTCLIENT_t client, + const char* dev, const char* var); + + void nutclient_set_device_variable_value(NUTCLIENT_t client, + const char* dev, const char* var, const char* value); + + void nutclient_set_device_variable_values(NUTCLIENT_t client, + const char* dev, const char* var, const strarr values); DESCRIPTION ----------- These functions allow to manage variables of devices. -The *nutclient_get_device_variables()* function retrieve the list of variables names for a device. +The *nutclient_get_device_variables()* function retrieve the list +of variables names for a device. The returned strarr must be freed by 'strarr_free'. -The *nutclient_get_device_rw_variables* function retrieve the list of read-write variables names for a device. +The *nutclient_get_device_rw_variables* function retrieve the list +of read-write variables names for a device. The returned strarr must be freed by 'strarr_free'. -The *nutclient_has_device_variable* function test if the specified variable is supported by the device. +The *nutclient_has_device_variable* function test if the specified +variable is supported by the device. Return 1 is supported and 0 if not. -The *nutclient_get_device_variable_description* function retrieve the variable description, if any. +The *nutclient_get_device_variable_description* function retrieve +the variable description, if any. The returned string must be freed. -The *nutclient_get_device_variable_values* returns variable values (generally only one). +The *nutclient_get_device_variable_values* returns variable values +(generally only one). The returned strarr must be freed by 'strarr_free'. -The *nutclient_set_device_variable_value* intend to set the value of the specified variable. +The *nutclient_set_device_variable_value* intend to set the value +of the specified variable. + +The *nutclient_set_device_variable_values* intend to set multiple +values of the specified variable. -The *nutclient_set_device_variable_values* intend to set multiple values of the specified variable. +Common arguments: -'dev' is the device name. +* 'dev' is the device name. -'var' is the variable name. +* 'var' is the variable name. -'value' is the variable value. +* 'value' is the variable value. -'values' is the variable array of values. +* 'values' is the variable array of values. SEE ALSO -------- diff --git a/docs/man/libupsclient-config.txt b/docs/man/libupsclient-config.txt index 0ff39a520d..526276c301 100644 --- a/docs/man/libupsclient-config.txt +++ b/docs/man/libupsclient-config.txt @@ -4,7 +4,8 @@ LIBUPSCLIENT-CONFIG(1) NAME ---- -libupsclient-config - script to get information about the installed version of libupsclient +libupsclient-config - script to get information about the installed +version of libupsclient SYNOPSIS -------- @@ -46,4 +47,3 @@ Internet resources: ~~~~~~~~~~~~~~~~~~~ The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - diff --git a/docs/nut.dict b/docs/nut.dict index 0501369526..109f58db26 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2943 utf-8 +personal_ws-1.1 en 2944 utf-8 AAS ABI ACFAIL @@ -2325,6 +2325,7 @@ otheruser outliers pF pacman +param paramkeywords parsable parseconf From 6bdf7667914709ea904cf43964897d3d02f85fb6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Apr 2022 22:19:53 +0200 Subject: [PATCH 523/700] ci_build.sh: "cat" the "git diff" --- ci_build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci_build.sh b/ci_build.sh index 375c01084e..49f60e821f 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -428,7 +428,7 @@ check_gitignore() { ; then echo "FATAL: There are changes in $FILE_DESCR files listed above - tracked sources should be updated in the PR (even if generated - not all builders can do so), and build products should be added to a .gitignore file, everything made should be cleaned and no tracked files should be removed!" >&2 if [ "$GIT_DIFF_SHOW" = true ]; then - git diff -- "${FILE_GLOB}" || true + PAGER=cat git diff -- "${FILE_GLOB}" || true fi echo "===" return 1 From c327825d0c55354ceb1265a85ef7e9fdcac8fd73 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Apr 2022 22:44:12 +0200 Subject: [PATCH 524/700] docs/man/Makefile.am: generate linkman-drivertool*names.txt independent of current dir being srcdir --- docs/man/Makefile.am | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index ea2486e3fa..64296d794f 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -749,13 +749,16 @@ HTML_MANS = \ # list of drivers with `- linkman:nutupsdrv[8]` entry linkman-driver-names.txt: @(LC_ALL=C; LANG=C; export LC_ALL LANG; \ - ls -1 $(SRC_DRIVERS_PAGES) | grep -vE '^nutupsdrv\.txt$$' | sort -n | uniq \ + for F in $(SRC_DRIVERS_PAGES) ; do echo "$$F" ; done \ + | grep -vE '^nutupsdrv\.txt$$' \ + | sort -n | uniq \ | sed 's,^\(.*\)\.txt$$,- linkman:\1[8],' ; \ ) > "$@" linkman-drivertool-names.txt: @(LC_ALL=C; LANG=C; export LC_ALL LANG; \ - ls -1 $(SRC_DRIVERTOOL_PAGES) | sort -n | uniq \ + for F in $(SRC_DRIVERTOOL_PAGES) ; do echo "$$F" ; done \ + | sort -n | uniq \ | sed 's,^\(.*\)\.txt$$,- linkman:\1[8],' ; \ ) > "$@" From e44020e7b8c4fb1495c1914ef0061836d0fea5c3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Apr 2022 22:45:34 +0200 Subject: [PATCH 525/700] docs/man/Makefile.am: avoid spurious regeneration of linkman-drivertool*names.txt whenever we reference them --- docs/man/Makefile.am | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 64296d794f..dd002538f8 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -747,8 +747,10 @@ HTML_MANS = \ # Note: target documents, except nutupsdrv.txt itself, start the # list of drivers with `- linkman:nutupsdrv[8]` entry +# To regenerate these files, do `make clean` first linkman-driver-names.txt: - @(LC_ALL=C; LANG=C; export LC_ALL LANG; \ + @if test -s "$@" || test -s "$(srcdir)/$(@F)" ; then exit 0 ; fi ; \ + (LC_ALL=C; LANG=C; export LC_ALL LANG; \ for F in $(SRC_DRIVERS_PAGES) ; do echo "$$F" ; done \ | grep -vE '^nutupsdrv\.txt$$' \ | sort -n | uniq \ @@ -756,7 +758,8 @@ linkman-driver-names.txt: ) > "$@" linkman-drivertool-names.txt: - @(LC_ALL=C; LANG=C; export LC_ALL LANG; \ + @if test -s "$@" || test -s "$(srcdir)/$(@F)" ; then exit 0 ; fi ; \ + (LC_ALL=C; LANG=C; export LC_ALL LANG; \ for F in $(SRC_DRIVERTOOL_PAGES) ; do echo "$$F" ; done \ | sort -n | uniq \ | sed 's,^\(.*\)\.txt$$,- linkman:\1[8],' ; \ From 483bcae02f8b019b5268074418892ff767900905 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Apr 2022 23:02:19 +0200 Subject: [PATCH 526/700] docs/man/Makefile.am: ensure linkman-drivertool*names.txt are in A2X_OUTDIR --- docs/man/Makefile.am | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index dd002538f8..f6900f62e0 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -872,6 +872,10 @@ DOCBUILD_BEGIN = { \ rm -rf "./$${A2X_OUTDIR}" || true ; \ test -d "$@" && rm -rf "$@" || true ; \ mkdir -p "./$${A2X_OUTDIR}" || exit ; \ + for F in linkman-driver-names.txt linkman-drivertool-names.txt ; do \ + if [ -s "./$$F" ] ; then ln -f -s "../../$$F" "./$${A2X_OUTDIR}/" ; else \ + if [ -s "$(abs_srcdir)/$$F" ] ; then ln -f -s "$(abs_srcdir)/$$F" "./$${A2X_OUTDIR}/" ; fi ; fi ; \ + done ; \ else A2X_OUTDIR='.' ; fi; \ if test -s "${builddir}/docbook-xsl.css" \ && test -r "${builddir}/docbook-xsl.css" \ @@ -881,12 +885,15 @@ DOCBUILD_BEGIN = { \ } # Note that documents with sub-pages (see LIBNUTCLIENT_*_DEPS above) -# may generate miltiple files in one go... so we move "*" and hope +# may generate multiple files in one go... so we move "*" and hope # for no required hidden files (or would have to `find` them all). DOCBUILD_END = { \ if test -n "$${A2X_OUTDIR}" && test "$${A2X_OUTDIR}" != '.' ; then \ chmod -R u+w "./$${A2X_OUTDIR}" || true ; \ test -d "$@" && rm -rf "$@" || true ; \ + for F in linkman-driver-names.txt linkman-drivertool-names.txt ; do \ + rm -f "./$${A2X_OUTDIR}/$$F" || true ; \ + done ; \ mv -f "./$${A2X_OUTDIR}/$(@F)" ./ || exit ; \ mv -f "./$${A2X_OUTDIR}/"*.* ./ 2>/dev/null || true ; \ rm -rf "./$${A2X_OUTDIR}" ; \ From 9f8f83ad21550b85fe916585dac251bd578be019 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Apr 2022 23:13:26 +0200 Subject: [PATCH 527/700] docs/man/Makefile.am: ensure linkman-drivertool*names.txt are in builddir --- docs/man/Makefile.am | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index f6900f62e0..ff91010bf7 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -749,7 +749,12 @@ HTML_MANS = \ # list of drivers with `- linkman:nutupsdrv[8]` entry # To regenerate these files, do `make clean` first linkman-driver-names.txt: - @if test -s "$@" || test -s "$(srcdir)/$(@F)" ; then exit 0 ; fi ; \ + @if test x"$(srcdir)" != x"$(builddir)" ; then \ + if ! test -s "$(builddir)/$@" && test -s "$(srcdir)/$(@F)" ; then \ + ln -fs "$(srcdir)/$(@F)" "$(builddir)/" ; \ + fi ; \ + fi + @if test -s "$@" ; then exit 0 ; fi ; \ (LC_ALL=C; LANG=C; export LC_ALL LANG; \ for F in $(SRC_DRIVERS_PAGES) ; do echo "$$F" ; done \ | grep -vE '^nutupsdrv\.txt$$' \ @@ -758,7 +763,12 @@ linkman-driver-names.txt: ) > "$@" linkman-drivertool-names.txt: - @if test -s "$@" || test -s "$(srcdir)/$(@F)" ; then exit 0 ; fi ; \ + @if test x"$(srcdir)" != x"$(builddir)" ; then \ + if ! test -s "$(builddir)/$@" && test -s "$(srcdir)/$(@F)" ; then \ + ln -fs "$(srcdir)/$(@F)" "$(builddir)/" ; \ + fi ; \ + fi + @if test -s "$@" ; then exit 0 ; fi ; \ (LC_ALL=C; LANG=C; export LC_ALL LANG; \ for F in $(SRC_DRIVERTOOL_PAGES) ; do echo "$$F" ; done \ | sort -n | uniq \ From 3fb31752281c7c1604496bc691718dec63bdd758 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 16 Apr 2022 23:27:30 +0200 Subject: [PATCH 528/700] docs/man/Makefile.am: DOCBUILD_BEGIN: work around older BSD make not seeing sources not present in builddir It seems to resolve single-source documents well, but fails to find the upsd.txt which depends on linkman*-names.txt and build a proper relative path to it. --- docs/man/Makefile.am | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index ff91010bf7..c4178f2518 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -892,6 +892,11 @@ DOCBUILD_BEGIN = { \ && ! test -w "${builddir}/docbook-xsl.css" \ ; then chmod u+w "${builddir}/docbook-xsl.css" ; fi ; \ chmod -R u+w "./$${A2X_OUTDIR}" || true ; \ + if test x"$(srcdir)" != x"$(builddir)" ; then \ + if ! test -s "$(builddir)/$<" && test -s "$(srcdir)/$( Date: Sun, 17 Apr 2022 00:31:49 +0200 Subject: [PATCH 529/700] docs/man/Makefile.am: abstract filenames into LINKMAN_INCLUDE_GENERATED macro --- docs/man/Makefile.am | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index c4178f2518..22fefe2f10 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -747,7 +747,9 @@ HTML_MANS = \ # Note: target documents, except nutupsdrv.txt itself, start the # list of drivers with `- linkman:nutupsdrv[8]` entry -# To regenerate these files, do `make clean` first +# To regenerate these files, do `make distclean` first +LINKMAN_INCLUDE_GENERATED = linkman-driver-names.txt linkman-drivertool-names.txt + linkman-driver-names.txt: @if test x"$(srcdir)" != x"$(builddir)" ; then \ if ! test -s "$(builddir)/$@" && test -s "$(srcdir)/$(@F)" ; then \ @@ -775,6 +777,8 @@ linkman-drivertool-names.txt: | sed 's,^\(.*\)\.txt$$,- linkman:\1[8],' ; \ ) > "$@" +# Dependencies are about particular filenames, since over time +# we might have several use-cases for LINKMAN_INCLUDE_GENERATED: index.txt index.html \ upsd.txt upsd.html upsd.8 upsd.pdf \ nutupsdrv.txt nutupsdrv.html nutupsdrv.8 nutupsdrv.pdf : linkman-driver-names.txt linkman-drivertool-names.txt @@ -782,10 +786,9 @@ nutupsdrv.txt nutupsdrv.html nutupsdrv.8 nutupsdrv.pdf : linkman-driver-names.tx # These files are generated when we build from git source so not tracked in # git, but we want it as part of tarball just in case the end-user's build # does not enable something related to docs: -EXTRA_DIST += linkman-driver-names.txt -EXTRA_DIST += linkman-drivertool-names.txt +EXTRA_DIST += $(LINKMAN_INCLUDE_GENERATED) -DISTCLEANFILES = linkman-driver-names.txt linkman-drivertool-names.txt +DISTCLEANFILES = $(LINKMAN_INCLUDE_GENERATED) all: @@ -882,7 +885,7 @@ DOCBUILD_BEGIN = { \ rm -rf "./$${A2X_OUTDIR}" || true ; \ test -d "$@" && rm -rf "$@" || true ; \ mkdir -p "./$${A2X_OUTDIR}" || exit ; \ - for F in linkman-driver-names.txt linkman-drivertool-names.txt ; do \ + for F in $(LINKMAN_INCLUDE_GENERATED) ; do \ if [ -s "./$$F" ] ; then ln -f -s "../../$$F" "./$${A2X_OUTDIR}/" ; else \ if [ -s "$(abs_srcdir)/$$F" ] ; then ln -f -s "$(abs_srcdir)/$$F" "./$${A2X_OUTDIR}/" ; fi ; fi ; \ done ; \ @@ -906,7 +909,7 @@ DOCBUILD_END = { \ if test -n "$${A2X_OUTDIR}" && test "$${A2X_OUTDIR}" != '.' ; then \ chmod -R u+w "./$${A2X_OUTDIR}" || true ; \ test -d "$@" && rm -rf "$@" || true ; \ - for F in linkman-driver-names.txt linkman-drivertool-names.txt ; do \ + for F in $(LINKMAN_INCLUDE_GENERATED) ; do \ rm -f "./$${A2X_OUTDIR}/$$F" || true ; \ done ; \ mv -f "./$${A2X_OUTDIR}/$(@F)" ./ || exit ; \ From 0f697ab9cfdc4a4d9724888b1af22e20db1125ba Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Apr 2022 16:40:34 +0200 Subject: [PATCH 530/700] docs/man/Makefile.am: `$( Date: Mon, 18 Apr 2022 17:17:37 +0200 Subject: [PATCH 531/700] clients/nutclient.{cpp,h}: fix port type from unsigned short to uint16_t Closes: #1375 --- clients/nutclient.cpp | 14 +++++++------- clients/nutclient.h | 11 ++++++----- docs/man/libnutclient_tcp.txt | 3 ++- docs/nut.dict | 4 +++- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/clients/nutclient.cpp b/clients/nutclient.cpp index 34179527f5..b1d07a83fe 100644 --- a/clients/nutclient.cpp +++ b/clients/nutclient.cpp @@ -131,7 +131,7 @@ class Socket Socket(); ~Socket(); - void connect(const std::string& host, int port); + void connect(const std::string& host, uint16_t port); void disconnect(); bool isConnected()const; @@ -169,7 +169,7 @@ void Socket::setTimeout(long timeout) _tv.tv_sec = timeout; } -void Socket::connect(const std::string& host, int port) +void Socket::connect(const std::string& host, uint16_t port) { int sock_fd; struct addrinfo hints, *res, *ai; @@ -186,7 +186,7 @@ void Socket::connect(const std::string& host, int port) throw nut::UnknownHostException(); } - snprintf(sport, sizeof(sport), "%hu", static_cast(port)); + snprintf(sport, sizeof(sport), "%ju", static_cast(port)); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; @@ -561,7 +561,7 @@ _socket(new internal::Socket) // Do not connect now } -TcpClient::TcpClient(const std::string& host, int port): +TcpClient::TcpClient(const std::string& host, uint16_t port): Client(), _timeout(0), _socket(new internal::Socket) @@ -574,7 +574,7 @@ TcpClient::~TcpClient() delete _socket; } -void TcpClient::connect(const std::string& host, int port) +void TcpClient::connect(const std::string& host, uint16_t port) { _host = host; _port = port; @@ -591,7 +591,7 @@ std::string TcpClient::getHost()const return _host; } -int TcpClient::getPort()const +uint16_t TcpClient::getPort()const { return _port; } @@ -1566,7 +1566,7 @@ strarr stringvector_to_strarr(const std::vector& strset) return arr; } -NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, unsigned short port) +NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, uint16_t port) { nut::TcpClient* client = new nut::TcpClient; try diff --git a/clients/nutclient.h b/clients/nutclient.h index 1458779bdd..4a9d4d2254 100644 --- a/clients/nutclient.h +++ b/clients/nutclient.h @@ -28,6 +28,7 @@ #include #include #include +#include /* See include/common.h for details behind this */ #ifndef NUT_UNUSED_VARIABLE @@ -367,7 +368,7 @@ class TcpClient : public Client * \param host Server host name. * \param port Server port. */ - TcpClient(const std::string& host, int port = 3493); + TcpClient(const std::string& host, uint16_t port = 3493); ~TcpClient() override; /** @@ -375,7 +376,7 @@ class TcpClient : public Client * \param host Server host name. * \param port Server port. */ - void connect(const std::string& host, int port = 3493); + void connect(const std::string& host, uint16_t port = 3493); /** * Connect to the server. @@ -414,7 +415,7 @@ class TcpClient : public Client * Retriueve the port of host of the server the client is connected to. * \return Server port */ - int getPort()const; + uint16_t getPort()const; virtual void authenticate(const std::string& user, const std::string& passwd) override; virtual void logout() override; @@ -466,7 +467,7 @@ class TcpClient : public Client private: std::string _host; - int _port; + uint16_t _port; long _timeout; internal::Socket* _socket; }; @@ -1018,7 +1019,7 @@ typedef NUTCLIENT_t NUTCLIENT_TCP_t; * \param port Host port. * \return New client or nullptr if failed. */ -NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, unsigned short port); +NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, uint16_t port); /** * Test if a nut TCP client is connected. * \param client Nut TCP client handle. diff --git a/docs/man/libnutclient_tcp.txt b/docs/man/libnutclient_tcp.txt index 367f7f4107..c849b76cf1 100644 --- a/docs/man/libnutclient_tcp.txt +++ b/docs/man/libnutclient_tcp.txt @@ -14,11 +14,12 @@ SYNOPSIS -------- #include + #include /* uint16_t */ typedef NUTCLIENT_t NUTCLIENT_TCP_t; NUTCLIENT_TCP_t nutclient_tcp_create_client( - const char* host, unsigned short port); + const char* host, uint16_t port); int nutclient_tcp_is_connected(NUTCLIENT_TCP_t client); diff --git a/docs/nut.dict b/docs/nut.dict index 109f58db26..698b28fa98 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2944 utf-8 +personal_ws-1.1 en 2946 utf-8 AAS ABI ACFAIL @@ -1642,6 +1642,8 @@ crw csh cshdelay css +cstdint +ctime ctrl cts ctypes From 4f760bac2a81d05b2743812117b2aa81c81d028a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Apr 2022 18:12:30 +0200 Subject: [PATCH 532/700] clients/upsclient.{c,h} docs/man/upscli_{read,send}line.txt: fix timeout from long to time_t Closes: #1373 --- clients/upsclient.c | 8 ++++---- clients/upsclient.h | 4 ++-- docs/man/upscli_readline.txt | 3 ++- docs/man/upscli_sendline.txt | 3 ++- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/clients/upsclient.c b/clients/upsclient.c index afe815ec4c..9cbb3a014e 100644 --- a/clients/upsclient.c +++ b/clients/upsclient.c @@ -630,7 +630,7 @@ static ssize_t upscli_select_read(const int fd, void *buf, const size_t buflen, # pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" #endif /* internal: abstract the SSL calls for the other functions */ -static ssize_t net_read(UPSCONN_t *ups, char *buf, size_t buflen, const long timeout) +static ssize_t net_read(UPSCONN_t *ups, char *buf, size_t buflen, const time_t timeout) { ssize_t ret = -1; @@ -714,7 +714,7 @@ static ssize_t upscli_select_write(const int fd, const void *buf, const size_t b # pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" #endif /* internal: abstract the SSL calls for the other functions */ -static ssize_t net_write(UPSCONN_t *ups, const char *buf, size_t buflen, const long timeout) +static ssize_t net_write(UPSCONN_t *ups, const char *buf, size_t buflen, const time_t timeout) { ssize_t ret = -1; @@ -1441,7 +1441,7 @@ int upscli_list_next(UPSCONN_t *ups, size_t numq, const char **query, return 1; } -ssize_t upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, const long timeout) +ssize_t upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, const time_t timeout) { ssize_t ret; @@ -1479,7 +1479,7 @@ ssize_t upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen) return upscli_sendline_timeout(ups, buf, buflen, 0); } -ssize_t upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, const long timeout) +ssize_t upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, const time_t timeout) { ssize_t ret; size_t recv; diff --git a/clients/upsclient.h b/clients/upsclient.h index 6608d1be44..a8ad41ab88 100644 --- a/clients/upsclient.h +++ b/clients/upsclient.h @@ -86,10 +86,10 @@ int upscli_list_start(UPSCONN_t *ups, size_t numq, const char **query); int upscli_list_next(UPSCONN_t *ups, size_t numq, const char **query, size_t *numa, char ***answer); -ssize_t upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, const long timeout); +ssize_t upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, const time_t timeout); ssize_t upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen); -ssize_t upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, const long timeout); +ssize_t upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, const time_t timeout); ssize_t upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen); int upscli_splitname(const char *buf, char **upsname, char **hostname, diff --git a/docs/man/upscli_readline.txt b/docs/man/upscli_readline.txt index 6b0905478f..120ad21b78 100644 --- a/docs/man/upscli_readline.txt +++ b/docs/man/upscli_readline.txt @@ -10,11 +10,12 @@ SYNOPSIS -------- #include + #include /* or on some platforms */ int upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen); int upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, - const long timeout); + const time_t timeout); DESCRIPTION ----------- diff --git a/docs/man/upscli_sendline.txt b/docs/man/upscli_sendline.txt index 9a92a4b4da..895f0baed0 100644 --- a/docs/man/upscli_sendline.txt +++ b/docs/man/upscli_sendline.txt @@ -10,11 +10,12 @@ SYNOPSIS -------- #include + #include /* or on some platforms */ int upscli_sendline(UPSCONN_t *ups, const char *buf, size_t buflen); int upscli_sendline_timeout(UPSCONN_t *ups, const char *buf, size_t buflen, - const long timeout); + const time_t timeout); DESCRIPTION ----------- From 9e4a8ec3867d88290ac70f468e0aa17064364595 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Apr 2022 18:15:15 +0200 Subject: [PATCH 533/700] clients/upsclient.h: fix whitespace --- clients/upsclient.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clients/upsclient.h b/clients/upsclient.h index a8ad41ab88..2102b1bee8 100644 --- a/clients/upsclient.h +++ b/clients/upsclient.h @@ -21,8 +21,8 @@ #define UPSCLIENT_H_SEEN #ifdef WITH_OPENSSL - #include - #include + #include + #include #elif defined(WITH_NSS) /* WITH_OPENSSL */ #include #include From 7829f4d94cd7fdc67d108a0f3c5b90412f373e87 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Apr 2022 18:36:58 +0200 Subject: [PATCH 534/700] docs/man/libnutclient_variables.txt: fix English --- docs/man/libnutclient_variables.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/man/libnutclient_variables.txt b/docs/man/libnutclient_variables.txt index 3d90eed1d9..fbeeb3d981 100644 --- a/docs/man/libnutclient_variables.txt +++ b/docs/man/libnutclient_variables.txt @@ -47,19 +47,19 @@ DESCRIPTION These functions allow to manage variables of devices. -The *nutclient_get_device_variables()* function retrieve the list +The *nutclient_get_device_variables()* function retrieves the list of variables names for a device. The returned strarr must be freed by 'strarr_free'. -The *nutclient_get_device_rw_variables* function retrieve the list +The *nutclient_get_device_rw_variables* function retrieves the list of read-write variables names for a device. The returned strarr must be freed by 'strarr_free'. -The *nutclient_has_device_variable* function test if the specified +The *nutclient_has_device_variable* function tests if the specified variable is supported by the device. Return 1 is supported and 0 if not. -The *nutclient_get_device_variable_description* function retrieve +The *nutclient_get_device_variable_description* function retrieves the variable description, if any. The returned string must be freed. @@ -67,10 +67,10 @@ The *nutclient_get_device_variable_values* returns variable values (generally only one). The returned strarr must be freed by 'strarr_free'. -The *nutclient_set_device_variable_value* intend to set the value +The *nutclient_set_device_variable_value* intends to set the value of the specified variable. -The *nutclient_set_device_variable_values* intend to set multiple +The *nutclient_set_device_variable_values* intends to set multiple values of the specified variable. Common arguments: From 0d61b63182aedc3be2388126bb05c9f804b352b0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Apr 2022 18:16:01 +0200 Subject: [PATCH 535/700] clients/upsclient.{c,h} and many clients + dummy-ups: fix port from int to uint16_t Closes: #1379 --- clients/upsc.c | 4 +++- clients/upsclient.c | 20 +++++++++++--------- clients/upsclient.h | 23 ++++++++++++++++++----- clients/upscmd.c | 3 ++- clients/upsimage.c | 4 ++-- clients/upslog.c | 6 ++++-- clients/upsmon.h | 2 +- clients/upsrw.c | 3 ++- clients/upsset.c | 3 ++- clients/upsstats.c | 8 ++++---- docs/man/upscli_connect.txt | 2 +- drivers/dummy-ups.c | 3 ++- 12 files changed, 52 insertions(+), 29 deletions(-) diff --git a/clients/upsc.c b/clients/upsc.c index 01959ae646..550b74a94c 100644 --- a/clients/upsc.c +++ b/clients/upsc.c @@ -25,6 +25,7 @@ #include #include +#include "nut_stdint.h" #include "upsclient.h" static char *upsname = NULL, *hostname = NULL; @@ -208,7 +209,8 @@ static void clean_exit(void) int main(int argc, char **argv) { - int i, port; + int i; + uint16_t port; int varlist = 0, clientlist = 0, verbose = 0; const char *prog = xbasename(argv[0]); diff --git a/clients/upsclient.c b/clients/upsclient.c index 9cbb3a014e..2787783abe 100644 --- a/clients/upsclient.c +++ b/clients/upsclient.c @@ -38,10 +38,10 @@ #include #include -#include "upsclient.h" #include "common.h" #include "nut_stdint.h" #include "timehead.h" +#include "upsclient.h" /* WA for Solaris/i386 bug: non-blocking connect sets errno to ENOENT */ #if (defined NUT_PLATFORM_SOLARIS) @@ -948,7 +948,7 @@ static int upscli_sslinit(UPSCONN_t *ups, int verifycert) #endif /* WITH_SSL */ -int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,struct timeval * timeout) +int upscli_tryconnect(UPSCONN_t *ups, const char *host, uint16_t port, int flags, struct timeval * timeout) { int sock_fd; struct addrinfo hints, *res, *ai; @@ -974,7 +974,7 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru return -1; } - snprintf(sport, sizeof(sport), "%hu", (unsigned short int)port); + snprintf(sport, sizeof(sport), "%ju", (uintmax_t)port); memset(&hints, 0, sizeof(hints)); @@ -1150,7 +1150,7 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags,stru return 0; } -int upscli_connect(UPSCONN_t *ups, const char *host, int port, int flags) +int upscli_connect(UPSCONN_t *ups, const char *host, uint16_t port, int flags) { return upscli_tryconnect(ups,host,port,flags,NULL); } @@ -1538,7 +1538,7 @@ ssize_t upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen) } /* split upsname[@hostname[:port]] into separate components */ -int upscli_splitname(const char *buf, char **upsname, char **hostname, int *port) +int upscli_splitname(const char *buf, char **upsname, char **hostname, uint16_t *port) { char *s, tmp[SMALLBUF], *last = NULL; @@ -1574,9 +1574,10 @@ int upscli_splitname(const char *buf, char **upsname, char **hostname, int *port } /* split hostname[:port] into separate components */ -int upscli_splitaddr(const char *buf, char **hostname, int *port) +int upscli_splitaddr(const char *buf, char **hostname, uint16_t *port) { char *s, tmp[SMALLBUF], *last = NULL; + long l; /* paranoia */ if ((!buf) || (!hostname) || (!port)) { @@ -1619,12 +1620,13 @@ int upscli_splitaddr(const char *buf, char **hostname, int *port) } } - /* FIXME: This assumes but does not check that "long" port - * fits in an "int" and is in IP range (under 65535) */ - if ((*(++s) == '\0') || ((*port = (int)strtol(s, NULL, 10)) < 1 )) { + /* Check that "long" port fits in an "uint16_t" so is in IP range + * (under 65536) */ + if ((*(++s) == '\0') || ((l = strtol(s, NULL, 10)) < 1 ) || (l > 65535)) { fprintf(stderr, "upscli_splitaddr: no port specified after ':' separator\n"); return -1; } + *port = (uint16_t)l; return 0; } diff --git a/clients/upsclient.h b/clients/upsclient.h index 2102b1bee8..08574b8f8b 100644 --- a/clients/upsclient.h +++ b/clients/upsclient.h @@ -28,6 +28,19 @@ #include #endif /* WITH_OPENSSL | WITH_NSS */ +/* Not including nut_stdint.h because this is part of end-user API */ +#if defined HAVE_INTTYPES_H + #include +#endif + +#if defined HAVE_STDINT_H + #include +#endif + +#if defined HAVE_LIMITS_H + #include +#endif + #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { @@ -41,7 +54,7 @@ extern "C" { typedef struct { char *host; - int port; + uint16_t port; int fd; int flags; int upserror; @@ -71,8 +84,8 @@ const char *upscli_strerror(UPSCONN_t *ups); int upscli_init(int certverify, const char *certpath, const char *certname, const char *certpasswd); int upscli_cleanup(void); -int upscli_tryconnect(UPSCONN_t *ups, const char *host, int port, int flags, struct timeval *tv); -int upscli_connect(UPSCONN_t *ups, const char *host, int port, int flags); +int upscli_tryconnect(UPSCONN_t *ups, const char *host, uint16_t port, int flags, struct timeval *tv); +int upscli_connect(UPSCONN_t *ups, const char *host, uint16_t port, int flags); void upscli_add_host_cert(const char* hostname, const char* certname, int certverify, int forcessl); @@ -93,9 +106,9 @@ ssize_t upscli_readline_timeout(UPSCONN_t *ups, char *buf, size_t buflen, const ssize_t upscli_readline(UPSCONN_t *ups, char *buf, size_t buflen); int upscli_splitname(const char *buf, char **upsname, char **hostname, - int *port); + uint16_t *port); -int upscli_splitaddr(const char *buf, char **hostname, int *port); +int upscli_splitaddr(const char *buf, char **hostname, uint16_t *port); int upscli_disconnect(UPSCONN_t *ups); diff --git a/clients/upscmd.c b/clients/upscmd.c index c18d00ced4..c057303d01 100644 --- a/clients/upscmd.c +++ b/clients/upscmd.c @@ -270,7 +270,8 @@ static void clean_exit(void) int main(int argc, char **argv) { - int i, port; + int i; + uint16_t port; ssize_t ret; int have_un = 0, have_pw = 0, cmdlist = 0; char buf[SMALLBUF * 2], username[SMALLBUF], password[SMALLBUF]; diff --git a/clients/upsimage.c b/clients/upsimage.c index e6d0c62398..d49156c5b0 100644 --- a/clients/upsimage.c +++ b/clients/upsimage.c @@ -37,20 +37,20 @@ */ #include "common.h" +#include "nut_stdint.h" #include "upsclient.h" #include "cgilib.h" #include #include #include -#include "nut_stdint.h" #include "upsimagearg.h" #define MAX_CGI_STRLEN 64 static char *monhost = NULL, *cmd = NULL; -static int port; +static uint16_t port; static char *upsname, *hostname; static UPSCONN_t ups; diff --git a/clients/upslog.c b/clients/upslog.c index 6115998b13..a0ebeaa40b 100644 --- a/clients/upslog.c +++ b/clients/upslog.c @@ -37,9 +37,11 @@ #include "config.h" #include "timehead.h" +#include "nut_stdint.h" #include "upslog.h" - static int port, reopen_flag = 0, exit_flag = 0; + static int reopen_flag = 0, exit_flag = 0; + static uint16_t port; static char *upsname, *hostname; static UPSCONN_t ups; @@ -405,7 +407,7 @@ int main(int argc, char **argv) printf("Network UPS Tools %s %s\n", prog, UPS_VERSION); - while ((i = getopt(argc, argv, "+hs:l:i:f:u:Vp:FB")) != -1) { + while ((i = getopt(argc, argv, "+hs:l:i:f:u:Vp:FB")) != -1) { switch(i) { case 'h': help(prog); diff --git a/clients/upsmon.h b/clients/upsmon.h index 23315139b9..d60720b9c3 100644 --- a/clients/upsmon.h +++ b/clients/upsmon.h @@ -49,7 +49,7 @@ typedef struct { char *sys; /* raw system name from .conf */ char *upsname; /* just upsname */ char *hostname; /* just hostname */ - int port; /* just the port */ + uint16_t port; /* just the port */ unsigned int pv; /* power value from conf */ char *un; /* username (optional for now) */ diff --git a/clients/upsrw.c b/clients/upsrw.c index fb49829e11..c4add32c6b 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -629,7 +629,8 @@ static void print_rwlist(void) int main(int argc, char **argv) { - int i, port; + int i; + uint16_t port; const char *prog = xbasename(argv[0]); char *password = NULL, *username = NULL, *setvar = NULL; diff --git a/clients/upsset.c b/clients/upsset.c index ac8f2854ae..788b201b9f 100644 --- a/clients/upsset.c +++ b/clients/upsset.c @@ -24,6 +24,7 @@ #include #include +#include "nut_stdint.h" #include "upsclient.h" #include "cgilib.h" #include "parseconf.h" @@ -44,7 +45,7 @@ static char *monups, *username, *password, *function, *upscommand; /* set once the MAGIC_ENABLE_STRING is found in the upsset.conf */ static int magic_string_set = 0; -static int port; +static uint16_t port; static char *upsname, *hostname; static UPSCONN_t ups; diff --git a/clients/upsstats.c b/clients/upsstats.c index c572f8e684..4b7f7547e5 100644 --- a/clients/upsstats.c +++ b/clients/upsstats.c @@ -19,14 +19,14 @@ */ #include "common.h" +#include "nut_stdint.h" +#include "timehead.h" #include "upsclient.h" #include "status.h" #include "cgilib.h" #include "parseconf.h" -#include "timehead.h" #include "upsstats.h" #include "upsimagearg.h" -#include "nut_stdint.h" #define MAX_CGI_STRLEN 128 #define MAX_PARSE_ARGS 16 @@ -37,7 +37,7 @@ static int use_celsius = 1, refreshdelay = -1, treemode = 0; /* from cgilib's checkhost() */ static char *monhostdesc = NULL; -static int port; +static uint16_t port; static char *upsname, *hostname; static char *upsimgpath="upsimage.cgi", *upsstatpath="upsstats.cgi"; static UPSCONN_t ups; @@ -351,7 +351,7 @@ static void ups_connect(void) { static ulist_t *lastups = NULL; char *newups, *newhost; - int newport; + uint16_t newport; /* try to minimize reconnects */ if (lastups) { diff --git a/docs/man/upscli_connect.txt b/docs/man/upscli_connect.txt index c97eb75185..c2e3ddef27 100644 --- a/docs/man/upscli_connect.txt +++ b/docs/man/upscli_connect.txt @@ -11,7 +11,7 @@ SYNOPSIS #include - int upscli_connect(UPSCONN_t *ups, const char *host, int port, int flags); + int upscli_connect(UPSCONN_t *ups, const char *host, uint16_t port, int flags); DESCRIPTION ----------- diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index 857f5d5e23..06b1c53e23 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -38,6 +38,7 @@ #include "main.h" #include "parseconf.h" +#include "nut_stdint.h" #include "upsclient.h" #include "dummy-ups.h" @@ -79,7 +80,7 @@ static int upsclient_update_vars(void); /* connection information */ static char *client_upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; -static int port; +static uint16_t port; /* Driver functions */ From 758786d1fb3e2d93ffdff8a830677200e9b6e5ed Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Apr 2022 19:15:19 +0200 Subject: [PATCH 536/700] clients/nutclient.{cpp,h} docs/man/libnutclient_tcp.txt: fix timeout from long to time_t Closes: #1376 --- clients/nutclient.cpp | 12 ++++++------ clients/nutclient.h | 11 ++++++----- docs/man/libnutclient_tcp.txt | 5 +++-- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/clients/nutclient.cpp b/clients/nutclient.cpp index b1d07a83fe..c64457493b 100644 --- a/clients/nutclient.cpp +++ b/clients/nutclient.cpp @@ -135,7 +135,7 @@ class Socket void disconnect(); bool isConnected()const; - void setTimeout(long timeout); + void setTimeout(time_t timeout); bool hasTimeout()const{return _tv.tv_sec>=0;} size_t read(void* buf, size_t sz); @@ -164,7 +164,7 @@ Socket::~Socket() disconnect(); } -void Socket::setTimeout(long timeout) +void Socket::setTimeout(time_t timeout) { _tv.tv_sec = timeout; } @@ -606,12 +606,12 @@ void TcpClient::disconnect() _socket->disconnect(); } -void TcpClient::setTimeout(long timeout) +void TcpClient::setTimeout(time_t timeout) { _timeout = timeout; } -long TcpClient::getTimeout()const +time_t TcpClient::getTimeout()const { return _timeout; } @@ -1635,7 +1635,7 @@ int nutclient_tcp_reconnect(NUTCLIENT_TCP_t client) return -1; } -void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, long timeout) +void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, time_t timeout) { if(client) { @@ -1647,7 +1647,7 @@ void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, long timeout) } } -long nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client) +time_t nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client) { if(client) { diff --git a/clients/nutclient.h b/clients/nutclient.h index 4a9d4d2254..1ba1ef7fc1 100644 --- a/clients/nutclient.h +++ b/clients/nutclient.h @@ -29,6 +29,7 @@ #include #include #include +#include /* See include/common.h for details behind this */ #ifndef NUT_UNUSED_VARIABLE @@ -398,13 +399,13 @@ class TcpClient : public Client * Set the timeout in seconds. * \param timeout Timeout n seconds, negative to block operations. */ - void setTimeout(long timeout); + void setTimeout(time_t timeout); /** * Retrieve the timeout. * \returns Current timeout in seconds. */ - long getTimeout()const; + time_t getTimeout()const; /** * Retriueve the host name of the server the client is connected to. @@ -468,7 +469,7 @@ class TcpClient : public Client private: std::string _host; uint16_t _port; - long _timeout; + time_t _timeout; internal::Socket* _socket; }; @@ -1042,12 +1043,12 @@ int nutclient_tcp_reconnect(NUTCLIENT_TCP_t client); * Set the timeout value for the TCP connection. * \param timeout Timeout in seconds, negative for blocking. */ -void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, long timeout); +void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, time_t timeout); /** * Retrieve the timeout value for the TCP connection. * \return Timeout value in seconds. */ -long nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client); +time_t nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client); /** \} */ diff --git a/docs/man/libnutclient_tcp.txt b/docs/man/libnutclient_tcp.txt index c849b76cf1..299fce22b9 100644 --- a/docs/man/libnutclient_tcp.txt +++ b/docs/man/libnutclient_tcp.txt @@ -15,6 +15,7 @@ SYNOPSIS #include #include /* uint16_t */ + #include /* time_t */ typedef NUTCLIENT_t NUTCLIENT_TCP_t; @@ -27,9 +28,9 @@ SYNOPSIS int nutclient_tcp_reconnect(NUTCLIENT_TCP_t client); - void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, long timeout); + void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, time_t timeout); - long nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client); + time_t nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client); DESCRIPTION ----------- From eb5eea54e5d81bb241a8e08c729a945e5cee361a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Apr 2022 19:34:34 +0200 Subject: [PATCH 537/700] clients/nutclient{mem}.{cpp,h} docs/man/libnutclient_misc.txt: add PRIMARY handling [#840] Closes: #1374 --- clients/nutclient.cpp | 43 +++++++++++++++++++++++++++------- clients/nutclient.h | 12 +++++++--- clients/nutclientmem.cpp | 11 ++++++--- clients/nutclientmem.h | 3 +++ docs/man/libnutclient_misc.txt | 8 ++++--- 5 files changed, 60 insertions(+), 17 deletions(-) diff --git a/clients/nutclient.cpp b/clients/nutclient.cpp index c64457493b..0ad3693cbb 100644 --- a/clients/nutclient.cpp +++ b/clients/nutclient.cpp @@ -811,14 +811,21 @@ void TcpClient::deviceLogin(const std::string& dev) detectError(sendQuery("LOGIN " + dev)); } -/* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. +/* NOTE: "master" is deprecated since NUT v2.8.0 in favor of "primary". + * For the sake of old/new server/client interoperability, + * practical implementations should try to use one and fall + * back to the other, and only fail if both return "ERR". */ void TcpClient::deviceMaster(const std::string& dev) { detectError(sendQuery("MASTER " + dev)); } +void TcpClient::devicePrimary(const std::string& dev) +{ + detectError(sendQuery("PRIMARY " + dev)); +} + void TcpClient::deviceForcedShutdown(const std::string& dev) { detectError(sendQuery("FSD " + dev)); @@ -1313,15 +1320,20 @@ void Device::login() getClient()->deviceLogin(getName()); } -/* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. - */ +/* Note: "master" is deprecated, but supported + * for mixing old/new client/server combos: */ void Device::master() { if (!isOk()) throw NutException("Invalid device"); getClient()->deviceMaster(getName()); } +void Device::primary() +{ + if (!isOk()) throw NutException("Invalid device"); + getClient()->devicePrimary(getName()); +} + void Device::forcedShutdown() { } @@ -1725,9 +1737,8 @@ int nutclient_get_device_num_logins(NUTCLIENT_t client, const char* dev) return -1; } -/* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. - */ +/* Note: "master" is deprecated, but supported + * for mixing old/new client/server combos: */ void nutclient_device_master(NUTCLIENT_t client, const char* dev) { if(client) @@ -1744,6 +1755,22 @@ void nutclient_device_master(NUTCLIENT_t client, const char* dev) } } +void nutclient_device_primary(NUTCLIENT_t client, const char* dev) +{ + if(client) + { + nut::Client* cl = static_cast(client); + if(cl) + { + try + { + cl->devicePrimary(dev); + } + catch(...){} + } + } +} + void nutclient_device_forced_shutdown(NUTCLIENT_t client, const char* dev) { if(client) diff --git a/clients/nutclient.h b/clients/nutclient.h index 1ba1ef7fc1..32ead0fe39 100644 --- a/clients/nutclient.h +++ b/clients/nutclient.h @@ -324,15 +324,18 @@ class Client */ virtual void deviceLogin(const std::string& dev) = 0; /** - * Retrieve the number of user longged in the specified device. + * Retrieve the number of user logged-in for the specified device. * \param dev Device name. * \return Number of logged-in users. */ virtual int deviceGetNumLogins(const std::string& dev) = 0; - /* FIXME: Protocol update needed to handle master/primary alias - * and probably an API bump also, to rename/alias the routine. + /* NOTE: "master" is deprecated since NUT v2.8.0 in favor of "primary". + * For the sake of old/new server/client interoperability, + * practical implementations should try to use one and fall + * back to the other, and only fail if both return "ERR". */ virtual void deviceMaster(const std::string& dev) = 0; + virtual void devicePrimary(const std::string& dev) = 0; virtual void deviceForcedShutdown(const std::string& dev) = 0; /** @@ -443,6 +446,7 @@ class TcpClient : public Client * and probably an API bump also, to rename/alias the routine. */ virtual void deviceMaster(const std::string& dev) override; + virtual void devicePrimary(const std::string& dev) override; virtual void deviceForcedShutdown(const std::string& dev) override; virtual int deviceGetNumLogins(const std::string& dev) override; @@ -614,6 +618,7 @@ class Device * and probably an API bump also, to rename/alias the routine. */ void master(); + void primary(); void forcedShutdown(); /** * Retrieve the number of logged user for the device. @@ -875,6 +880,7 @@ int nutclient_get_device_num_logins(NUTCLIENT_t client, const char* dev); * and probably an API bump also, to rename/alias the routine. */ void nutclient_device_master(NUTCLIENT_t client, const char* dev); +void nutclient_device_primary(NUTCLIENT_t client, const char* dev); /** * Set the FSD flag for the device. diff --git a/clients/nutclientmem.cpp b/clients/nutclientmem.cpp index 4d214a5368..df97cb3ce1 100644 --- a/clients/nutclientmem.cpp +++ b/clients/nutclientmem.cpp @@ -180,12 +180,20 @@ void MemClientStub::deviceLogin(const std::string& dev) throw NutException("Not implemented"); } +/* Note: "master" is deprecated, but supported + * for mixing old/new client/server combos: */ void MemClientStub::deviceMaster(const std::string& dev) { NUT_UNUSED_VARIABLE(dev); throw NutException("Not implemented"); } +void MemClientStub::devicePrimary(const std::string& dev) +{ + NUT_UNUSED_VARIABLE(dev); + throw NutException("Not implemented"); +} + void MemClientStub::deviceForcedShutdown(const std::string& dev) { NUT_UNUSED_VARIABLE(dev); @@ -242,6 +250,3 @@ NUTCLIENT_MEM_t nutclient_mem_create_client() } } /* extern "C" */ - - - diff --git a/clients/nutclientmem.h b/clients/nutclientmem.h index 6788a2bf60..3bd22439fa 100644 --- a/clients/nutclientmem.h +++ b/clients/nutclientmem.h @@ -69,7 +69,10 @@ class MemClientStub : public Client virtual TrackingID executeDeviceCommand(const std::string& dev, const std::string& name, const std::string& param="") override; virtual void deviceLogin(const std::string& dev) override; + /* Note: "master" is deprecated, but supported + * for mixing old/new client/server combos: */ virtual void deviceMaster(const std::string& dev) override; + virtual void devicePrimary(const std::string& dev) override; virtual void deviceForcedShutdown(const std::string& dev) override; virtual int deviceGetNumLogins(const std::string& dev) override; diff --git a/docs/man/libnutclient_misc.txt b/docs/man/libnutclient_misc.txt index 8ef5ef91a1..2635db4722 100644 --- a/docs/man/libnutclient_misc.txt +++ b/docs/man/libnutclient_misc.txt @@ -26,6 +26,8 @@ SYNOPSIS int nutclient_get_device_num_logins(NUTCLIENT_t client, const char* dev); + void nutclient_device_primary(NUTCLIENT_t client, const char* dev); + /* OBSOLETED name: */ void nutclient_device_master(NUTCLIENT_t client, const char* dev); void nutclient_device_forced_shutdown(NUTCLIENT_t client, const char* dev); @@ -47,9 +49,9 @@ is drawing power from this UPS. The *nutclient_get_device_num_logins()* function retrieves the number of clients which have been logged for this device. -The *nutclient_device_master()* function makes sure that primary-mode -functions like FSD are available if necessary. (Note: API change may be -pending to rename/alias the deprecated function name) +The *nutclient_device_master()* and *nutclient_device_primary()* (note: +the former is obsoleted since NUT v2.8.0 in favor of the latter) functions +make sure that primary-mode functions like FSD are available if necessary. The *nutclient_device_forced_shutdown()* function sets the "forced shutdown" flag on the device. From 2c25a2d27980a7b3ef795dc77a0138218b5f3ea2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Apr 2022 19:41:17 +0200 Subject: [PATCH 538/700] clients/nutclient.{cpp,h}: deviceMaster()/devicePrimary(): add fallback handling with the other keyword [#840, #1374] --- clients/nutclient.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/clients/nutclient.cpp b/clients/nutclient.cpp index 0ad3693cbb..6dfbe3a4bb 100644 --- a/clients/nutclient.cpp +++ b/clients/nutclient.cpp @@ -818,12 +818,30 @@ void TcpClient::deviceLogin(const std::string& dev) */ void TcpClient::deviceMaster(const std::string& dev) { - detectError(sendQuery("MASTER " + dev)); + try { + detectError(sendQuery("MASTER " + dev)); + } catch (NutException &exOrig) { + try { + detectError(sendQuery("PRIMARY " + dev)); + } catch (NutException &exRetry) { + NUT_UNUSED_VARIABLE(exRetry); + throw exOrig; + } + } } void TcpClient::devicePrimary(const std::string& dev) { - detectError(sendQuery("PRIMARY " + dev)); + try { + detectError(sendQuery("PRIMARY " + dev)); + } catch (NutException &exOrig) { + try { + detectError(sendQuery("MASTER " + dev)); + } catch (NutException &exRetry) { + NUT_UNUSED_VARIABLE(exRetry); + throw exOrig; + } + } } void TcpClient::deviceForcedShutdown(const std::string& dev) From c79cd071676d22d79941c8ed404a8c193ccab7af Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Apr 2022 13:37:57 +0200 Subject: [PATCH 539/700] conf/ups.conf.sample: update with info from docs/man/ups.conf.txt --- conf/ups.conf.sample | 104 +++++++++++++++++++++++++++++++------------ 1 file changed, 76 insertions(+), 28 deletions(-) diff --git a/conf/ups.conf.sample b/conf/ups.conf.sample index f85c08cc14..a86a845854 100644 --- a/conf/ups.conf.sample +++ b/conf/ups.conf.sample @@ -45,7 +45,7 @@ # These directives are used by upsdrvctl only and should be specified outside # of a driver definition: # -# maxretry: Optional. Specify the number of attempts to start the driver(s), +# maxretry: OPTIONAL. Specify the number of attempts to start the driver(s), # in case of failure, before giving up. A delay of 'retrydelay' is # inserted between each attempt. Caution should be taken when using # this option, since it can impact the time taken by your system to @@ -53,13 +53,28 @@ # # The built-in default is 1 attempt. # -# retrydelay: Optional. Specify the delay between each restart attempt of the +# retrydelay: OPTIONAL. Specify the delay between each restart attempt of the # driver(s), as specified by 'maxretry'. Caution should be taken # when using this option, since it can impact the time taken by your # system to start. # # The default is 5 seconds. # +# chroot: OPTIONAL. Used for securing. See man page for details. +# +# driverpath: OPTIONAL. Used for custom setups. See man page for details. +# +# nowait: OPTIONAL. Tell upsdrvctl to not wait at all for the driver(s) +# to execute the requested command. Fire and forget. +# +# pollinterval: OPTIONAL. The status of the UPS will be refreshed after a +# maximum delay which is controlled by this setting (default +# 2 seconds). This may be useful if the driver is creating too +# much of a load on your system or network. +# Note that some drivers also have an option called *pollfreq* +# which controls how frequently some of the less critical +# parameters are polled. See respective driver man pages. +# # Set maxretry to 3 by default, this should mitigate race with slow devices: maxretry = 3 @@ -67,6 +82,14 @@ maxretry = 3 # These directives can be set outside and inside a driver definition, with # slightly different meanings per context: # +# maxstartdelay: OPTIONAL. This can be set as a global variable +# above your first UPS definition and it can also be +# set in a UPS section. This value controls how long +# upsdrvctl will wait for the driver to finish starting. +# This keeps your system from getting stuck due to a +# broken driver or UPS. +# The default is 45 seconds. +# # debug_min: OPTIONAL. Specify a minimum debug level for all driver daemons # (when specified at global level), or for this driver daemon # (when specified in a driver section), e.g. for troubleshooting @@ -74,6 +97,29 @@ maxretry = 3 # background running mode. If both the global and driver level # `debug_min` are set, the driver-level setting takes precedence. # Command-line option `-D` can only increase this verbosity level. +# +# user, group: OPTIONAL. Overrides the compiled-in (also global-section, +# when used in driver section) default unprivileged user/group +# name for NUT device driver. Impacts access rights used for +# the socket file access (group) and communication ports (user). +# +# synchronous: OPTIONAL. The driver work by default in asynchronous +# mode (like *no*) with fallback to synchronous if sending +# fails (i.e *synchronous=auto*). This means that all data +# are pushed by the driver on the communication socket to +# upsd (Unix socket on Unix, Named pipe on Windows) without +# waiting for these data to be actually consumed. With +# some HW, such as ePDUs, that can produce a lot of data, +# asynchronous mode may cause some congestion, resulting in +# the socket to be full, and the driver to appear as not +# connected. By enabling the 'synchronous' flag +# (value = 'yes'), the driver will wait for data to be +# consumed by upsd, prior to publishing more. This can be +# enabled either globally or per driver. +# +# The default is 'no' (i.e. asynchronous mode) for backward +# compatibility of the driver behavior. +# # These directives are common to all drivers that support ups.conf: # @@ -83,13 +129,15 @@ maxretry = 3 # port: REQUIRED. The serial port where your UPS is connected. # /dev/ttyS0 is usually the first port on Linux boxes, for example. # -# sdorder: optional. When you have multiple UPSes on your system, you +# sdorder: OPTIONAL. When you have multiple UPSes on your system, you # usually need to turn them off in a certain order. upsdrvctl # shuts down all the 0s, then the 1s, 2s, and so on. To exclude # a UPS from the shutdown sequence, set this to -1. # # The default value for this parameter is 0. # +# desc: optional, to keep a note of the UPS purpose, location, etc. +# # nolock: optional, and not recommended for use in this file. # # If you put nolock in here, the driver will not lock the @@ -100,31 +148,31 @@ maxretry = 3 # This is only intended to be used on systems where locking # absolutely must be disabled for the software to work. # -# maxstartdelay: optional. This can be set as a global variable -# above your first UPS definition and it can also be -# set in a UPS section. This value controls how long -# upsdrvctl will wait for the driver to finish starting. -# This keeps your system from getting stuck due to a -# broken driver or UPS. -# -# The default is 45 seconds. -# -# synchronous: optional. The driver work by default in asynchronous -# mode (like *no*) with fallback to synchronous if sending -# fails (i.e *synchronous=auto*). This means that all data -# are pushed by the driver on the communication socket to -# upsd (Unix socket on Unix, Named pipe on Windows) without -# waiting for these data to be actually consumed. With -# some HW, such as ePDUs, that can produce a lot of data, -# asynchronous mode may cause some congestion, resulting in -# the socket to be full, and the driver to appear as not -# connected. By enabling the 'synchronous' flag -# (value = 'yes'), the driver will wait for data to be -# consumed by upsd, prior to publishing more. This can be -# enabled either globally or per driver. -# -# The default is 'no' (i.e. asynchronous mode) for backward -# compatibility of the driver behavior. +# ignorelb: OPTIONAL. Ignore low battery condition reported by device, +# and evaluate remaining battery charge or runtime instead. +# See man page for details. +# +# usb_set_altinterface(=num): OPTIONAL. Require that NUT calls this method +# to set the interface, even if 0 (default). Some devices require +# the call to initialize; others however can get stuck due to it - +# so it is not called by default. Yet others can be composite +# devices which use a non-zero interface to represent the UPS. +# +# default.: OPTIONAL. Set a default value for which is +# used in case the UPS doesn't provide a value, but which will be +# overwritten if a value is available from the UPS, e.g.: +# default.input.voltage.nominal = 230 +# will report the nominal input voltage to be 230, unless the UPS +# eventually tells us differently. +# +# override.: OPTIONAL. Set a value for that overrides +# (for NUT) any value that may be read from the UPS. +# Used for overriding values from the UPS that are clearly wrong +# (e.g. some devices report wrong values for battery voltage): +# override.battery.voltage.nominal = 12 +# Use with caution! This will only change the appearance of the +# variable to the outside world (and NUT calculations), internally +# in the UPS the original value is used. # # Anything else is passed through to the hardware-specific part of # the driver. From c95a10bb1f7836f34d5aa6e819610a99f23a4e31 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Apr 2022 15:05:52 +0200 Subject: [PATCH 540/700] test_nutclient.py.in: allow to override NUT_PORT, NUT_USER, NUT_PASS --- scripts/python/module/test_nutclient.py.in | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/python/module/test_nutclient.py.in b/scripts/python/module/test_nutclient.py.in index fbfd0249e5..22e6adb3d5 100755 --- a/scripts/python/module/test_nutclient.py.in +++ b/scripts/python/module/test_nutclient.py.in @@ -5,12 +5,17 @@ import PyNUT import sys +import os if __name__ == "__main__" : + NUT_PORT = int(os.getenv('NUT_PORT', '3493')) + NUT_USER = os.getenv('NUT_USER', None) + NUT_PASS = os.getenv('NUT_PASS', None) print( "PyNUTClient test..." ) - nut = PyNUT.PyNUTClient( debug=True ) - #nut = PyNUT.PyNUTClient( login="upsadmin", password="upsadmin", debug=True ) + #nut = PyNUT.PyNUTClient( debug=True, port=NUT_PORT ) + nut = PyNUT.PyNUTClient( login=NUT_USER, password=NUT_PASS, debug=True, port=NUT_PORT ) + #nut = PyNUT.PyNUTClient( login="upsadmin", password="upsadmin", debug=True, port=NUT_PORT ) print( 80*"-" + "\nTesting 'GetUPSList' :") result = nut.GetUPSList( ) From b5696f0aacc7b872e3b48bf20ef60ba5871750e5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Apr 2022 15:51:12 +0200 Subject: [PATCH 541/700] upsrw: accept "-l" for listing Closes: #1382 --- clients/upsrw.c | 11 +++++++++-- docs/man/upsrw.txt | 8 +++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/clients/upsrw.c b/clients/upsrw.c index c4add32c6b..2789f9ac71 100644 --- a/clients/upsrw.c +++ b/clients/upsrw.c @@ -51,6 +51,7 @@ static void usage(const char *prog) printf(" -h display this help text\n"); printf(" -s specify variable to be changed\n"); printf(" use -s VAR=VALUE to avoid prompting for value\n"); + printf(" -l show all possible read/write variables.\n"); printf(" -u set username for command authentication\n"); printf(" -p set password for command authentication\n"); printf(" -w wait for the completion of setting by the driver\n"); @@ -59,7 +60,7 @@ static void usage(const char *prog) printf("\n"); printf(" UPS identifier - [@[:]]\n"); printf("\n"); - printf("Call without -s to show all possible read/write variables.\n"); + printf("Call without -s to show all possible read/write variables (same as -l).\n"); } static void clean_exit(void) @@ -634,12 +635,18 @@ int main(int argc, char **argv) const char *prog = xbasename(argv[0]); char *password = NULL, *username = NULL, *setvar = NULL; - while ((i = getopt(argc, argv, "+hs:p:t:u:wV")) != -1) { + while ((i = getopt(argc, argv, "+hls:p:t:u:wV")) != -1) { switch (i) { case 's': setvar = optarg; break; + case 'l': + if (setvar) { + upslogx(LOG_WARNING, "Listing mode requested, overriding setvar specified earlier!"); + setvar = NULL; + } + break; case 'p': password = optarg; break; diff --git a/docs/man/upsrw.txt b/docs/man/upsrw.txt index 3f95235b38..ebcd2056d1 100644 --- a/docs/man/upsrw.txt +++ b/docs/man/upsrw.txt @@ -9,7 +9,7 @@ upsrw - UPS variable administration tool SYNOPSIS -------- -*upsrw* 'ups' +*upsrw* [-l] 'ups' *upsrw* -h @@ -48,6 +48,12 @@ length limit. Others are enumerated types and can only be set to one of those values. Others may be within an allowed range of values. Refer to the list to know what's available in your hardware. +*-l*:: +Just display the list of the variables and their possible values. ++ +Same as default activity without '-s' argument, provided for CLI similarity +with other tools. + *-u* 'username':: Set the NUT username for the connection to the server. This is optional, and you will be prompted for this when using the -s option if you don't From 22a32f1a30be1cec1731e1be9ead403222296320 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 19 Apr 2022 16:52:30 +0200 Subject: [PATCH 542/700] NIT: NUT Integration Tests (usable PoC) [another take at issue #3] --- tests/NIT/.gitignore | 1 + tests/NIT/README | 5 + tests/NIT/nit.sh | 461 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 467 insertions(+) create mode 100644 tests/NIT/.gitignore create mode 100644 tests/NIT/README create mode 100755 tests/NIT/nit.sh diff --git a/tests/NIT/.gitignore b/tests/NIT/.gitignore new file mode 100644 index 0000000000..ceeb05b410 --- /dev/null +++ b/tests/NIT/.gitignore @@ -0,0 +1 @@ +/tmp diff --git a/tests/NIT/README b/tests/NIT/README new file mode 100644 index 0000000000..4ccce2110e --- /dev/null +++ b/tests/NIT/README @@ -0,0 +1,5 @@ +NUT Integration Test suite +========================== + +This suite aims to simplify running `upsd`, a `dummy-ups` driver and +a few clients to query them. diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh new file mode 100755 index 0000000000..d201e1b757 --- /dev/null +++ b/tests/NIT/nit.sh @@ -0,0 +1,461 @@ +#!/bin/sh + +# NUT Integration Test suite, assumes the codebase was built and +# arranges running of the binaries to test driver-client-server +# ability to start and their interactions. +# +# Note: currently it is a PoC-quality mess that gets the job done +# but could be refactored for better maintainability and generic +# approach. Part of the goal was to let this script set up the +# sandbox to run tests which could be defined in other files. +# +# Design note: written with dumbed-down POSIX shell syntax, to +# properly work in whatever different OSes have (bash, dash, +# ksh, busybox sh...) +# +# Copyright +# 2022 Jim Klimov +# +# License: GPLv2+ + +TZ=UTC +LANG=C +LC_ALL=C +export TZ LANG LC_ALL + +log_separator() { + echo "" >&2 + echo "================================" >&2 +} + +log_debug() { + echo "[DEBUG] $@" >&2 +} + +log_info() { + echo "[INFO] $@" >&2 +} + +log_error() { + echo "[ERROR] $@" >&2 +} + +die() { + echo "[FATAL] $@" >&2 + exit 1 +} + +# Note: current directory is assumed to be writeable for temporary +# data, e.g. the $(builddir) from the Makefile. Static resources +# from the source codebase are where the script resides, e.g. +# the $(srcdir) from the Makefile. If we are not in the source +# tree, tests would use binaries in PATH (e.g. packaged install). +BUILDDIR="`pwd`" +TOP_BUILDDIR="" +case "${BUILDDIR}" in + */tests/NIT) + TOP_BUILDDIR="`cd "${BUILDDIR}"/../.. && pwd`" ;; +esac + +SRCDIR="`dirname "$0"`" +SRCDIR="`cd "$SCRIPTDIR" && pwd`" +TOP_SRCDIR="" +case "${SRCDIR}" in + */tests/NIT) + TOP_SRCDIR="`cd "${SRCDIR}"/../.. && pwd`" ;; +esac + +# No fuss about LD_LIBRARY_PATH: for binaries that need it, +# PATH entries below would contain libtool wrapper scripts; +# for other builds we use system default or caller's env. +PATH_ADD="${BUILDDIR}" +if [ x"${SRCDIR}" != x"${BUILDDIR}" ]; then + PATH_ADD="${PATH_ADD}:${SRCDIR}" +fi + +if [ x"${TOP_BUILDDIR}" != x ]; then + PATH_ADD="${PATH_ADD}:${TOP_BUILDDIR}/clients:${TOP_BUILDDIR}/drivers:${TOP_BUILDDIR}/server:${TOP_BUILDDIR}/tools:${TOP_BUILDDIR}/tools/nut-scanner" +fi + +if [ x"${TOP_SRCDIR}" != x ]; then + PATH_ADD="${PATH_ADD}:${TOP_SRCDIR}/clients:${TOP_SRCDIR}/drivers:${TOP_SRCDIR}/server:${TOP_SRCDIR}/tools:${TOP_SRCDIR}/tools/nut-scanner" +fi + +PATH="${PATH_ADD}:${PATH}" +export PATH +unset PATH_ADD + +log_debug "Using PATH='$PATH'" + +PID_UPSD="" +PID_DUMMYUPS="" +PID_DUMMYUPS1="" +PID_DUMMYUPS2="" + +rm -rf "$BUILDDIR/tmp" || true + +mkdir -p "$BUILDDIR/tmp/etc" "$BUILDDIR/tmp/run" && chmod 750 "$BUILDDIR/tmp/run" \ +|| die "Failed to create temporary FS structure for the NIT" + +trap 'RES=$?; if [ -n "$PID_UPSD$PID_DUMMYUPS$PID_DUMMYUPS1$PID_DUMMYUPS2" ] ; then kill -15 $PID_UPSD $PID_DUMMYUPS $PID_DUMMYUPS1 $PID_DUMMYUPS2 ; fi; exit $RES;' 0 1 2 3 15 + +NUT_STATEPATH="$BUILDDIR/tmp/run" +NUT_ALTPIDPATH="$BUILDDIR/tmp/run" +NUT_CONFPATH="$BUILDDIR/tmp/etc" +export NUT_STATEPATH NUT_ALTPIDPATH NUT_CONFPATH + +# TODO: Find a portable way to grab a random unprivileged port? +NUT_PORT="34931" + +### upsd.conf: ################################################## + +generatecfg_upsd_trivial() { + # Populate the configs for the run + cat > "$NUT_CONFPATH/upsd.conf" << EOF +STATEPATH "$NUT_STATEPATH" +LISTEN localhost $NUT_PORT +EOF + [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: upsd.conf" + chmod 640 "$NUT_CONFPATH/upsd.conf" +} + +generatecfg_upsd_nodev() { + generatecfg_upsd_trivial + echo "ALLOW_NO_DEVICE true" >> "$NUT_CONFPATH/upsd.conf" \ + || die "Failed to populate temporary FS structure for the NIT: upsd.conf" +} + +### upsd.users: ################################################## + +generatecfg_upsdusers_trivial() { + cat > "$NUT_CONFPATH/upsd.users" << EOF +[admin] + password = mypass + actions = SET + instcmds = ALL + +[tester] + password = "pass words" + instcmds = test.battery.start + instcmds = test.battery.stop + +[dummy-admin-m] + password = 'P@ssW0rdAdm' + upsmon master + +[dummy-admin] + password = 'P@ssW0rdAdm' + upsmon primary + +[dummy-user-s] + password = 'P@ssW0rd' + upsmon slave + +[dummy-user] + password = 'P@ssW0rd' + upsmon secondary +EOF + [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: upsd.users" + chmod 640 "$NUT_CONFPATH/upsd.users" +} + +### upsmon.conf: ################################################## + +generatecfg_upsmon_trivial() { + # Populate the configs for the run + ( echo 'MINSUPPLIES 0' > "$NUT_CONFPATH/upsmon.conf" || exit + echo 'SHUTDOWNCMD "echo TESTING_DUMMY_SHUTDOWN_NOW"' >> "$NUT_CONFPATH/upsmon.conf" || exit + ) || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" + chmod 640 "$NUT_CONFPATH/upsmon.conf" +} + +generatecfg_upsmon_master() { + generatecfg_upsmon_trivial + echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-admin-m' 'P@ssW0rdAdm' master" >> "$NUT_CONFPATH/upsmon.conf" \ + || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" +} + +generatecfg_upsmon_primary() { + generatecfg_upsmon_trivial + echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-admin' 'P@ssW0rdAdm' primary" >> "$NUT_CONFPATH/upsmon.conf" \ + || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" +} + +generatecfg_upsmon_slave() { + generatecfg_upsmon_trivial + echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-user-s' 'P@ssW0rd' slave" >> "$NUT_CONFPATH/upsmon.conf" \ + || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" +} + +generatecfg_upsmon_secondary() { + generatecfg_upsmon_trivial + echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-user' 'P@ssW0rd' secondary" >> "$NUT_CONFPATH/upsmon.conf" \ + || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" +} + +### ups.conf: ################################################## + +generatecfg_ups_trivial() { + # Populate the configs for the run + ( echo 'maxretry = 3' > "$NUT_CONFPATH/ups.conf" || exit + if [ x"${TOP_BUILDDIR}" != x ]; then + echo "driverpath = '${TOP_BUILDDIR}/drivers'" >> "$NUT_CONFPATH/ups.conf" || exit + fi + ) || die "Failed to populate temporary FS structure for the NIT: ups.conf" + chmod 640 "$NUT_CONFPATH/ups.conf" +} + +generatecfg_ups_dummy() { + generatecfg_ups_trivial + + cat > "$NUT_CONFPATH/dummy.dev" << EOF +ups.status: OB +TIMER 5 +ups.status: OL +TIMER 5 +EOF + [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: dummy.dev" + + cat >> "$NUT_CONFPATH/ups.conf" << EOF +[dummy] + driver = dummy-ups + desc = "Crash Dummy" + port = dummy.dev +EOF + [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: ups.conf" + + if [ x"${TOP_SRCDIR}" != x ]; then + cp "${TOP_SRCDIR}/data/evolution500.seq" "${TOP_SRCDIR}/data/epdu-managed.dev" "$NUT_CONFPATH/" + + cat >> "$NUT_CONFPATH/ups.conf" << EOF +[UPS1] + driver = dummy-ups + desc = "Example event sequence" + port = evolution500.seq + +[UPS2] + driver = dummy-ups + desc = "Example ePDU data dump" + port = epdu-managed.dev +EOF + [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: ups.conf" + fi + +} + +##################################################### + +FAILED=0 +PASSED=0 + +log_separator +log_info "Test UPSD without configs at all" +upsd -F +if [ "$?" = 0 ]; then + log_error "upsd should fail without configs" + FAILED="`expr $FAILED + 1`" +else + log_info "OK, upsd failed to start in wrong conditions" + PASSED="`expr $PASSED + 1`" +fi + +log_separator +log_info "Test UPSD without driver config file" +generatecfg_upsd_trivial +upsd -F +if [ "$?" = 0 ]; then + log_error "upsd should fail without driver config file" + FAILED="`expr $FAILED + 1`" +else + log_info "OK, upsd failed to start in wrong conditions" + PASSED="`expr $PASSED + 1`" +fi + +log_separator +log_info "Test UPSD without drivers defined in config file" +generatecfg_upsd_trivial +generatecfg_ups_trivial +upsd -F +if [ "$?" = 0 ]; then + log_error "upsd should fail without drivers defined in config file" + FAILED="`expr $FAILED + 1`" +else + log_info "OK, upsd failed to start in wrong conditions" + PASSED="`expr $PASSED + 1`" +fi + +log_separator +log_info "Test UPSD allowed to run without driver configs" +generatecfg_upsd_nodev +generatecfg_upsdusers_trivial +generatecfg_ups_trivial +upsd -F & +PID_UPSD="$!" +sleep 2 +if [ -d "/proc/$PID_UPSD" ] || kill -0 "$PID_UPSD"; then + log_info "OK, upsd is running" + PASSED="`expr $PASSED + 1`" + + log_separator + log_info "Test that UPSD responds to UPSC" + OUT="`upsc -l localhost:$NUT_PORT`" || die "upsd does not respond: $OUT" + if [ -n "$OUT" ] ; then + log_error "got reply for upsc listing when none was expected: $OUT" + FAILED="`expr $FAILED + 1`" + else + log_info "OK, empty response as expected" + PASSED="`expr $PASSED + 1`" + fi +else + log_error "upsd was expected to be running although no devices are defined" + FAILED="`expr $FAILED + 1`" +fi +kill -15 $PID_UPSD +wait $PID_UPSD + +log_separator +log_info "Test starting UPSD and a driver" +generatecfg_upsd_nodev +generatecfg_upsdusers_trivial +generatecfg_ups_dummy + +log_info "Starting UPSD alone first" +upsd -F & +PID_UPSD="$!" +sleep 5 + +EXPECTED_UPSLIST='dummy' +if [ x"${TOP_SRCDIR}" != x ]; then + EXPECTED_UPSLIST="$EXPECTED_UPSLIST +UPS1 +UPS2" +fi + +log_info "Query listing from UPSD by UPSC (driver not running yet)" +OUT="`upsc -l localhost:$NUT_PORT`" || die "upsd does not respond: $OUT" +if [ x"$OUT" != x"$EXPECTED_UPSLIST" ] ; then + log_error "got this reply for upsc listing when '$EXPECTED_UPSLIST' was expected: $OUT" + FAILED="`expr $FAILED + 1`" +else + PASSED="`expr $PASSED + 1`" +fi + +log_info "Query driver state from UPSD by UPSC (driver not running yet)" +OUT="`upsc dummy@localhost:$NUT_PORT 2>&1`" && { + log_error "upsc was supposed to answer with error exit code: $OUT" + FAILED="`expr $FAILED + 1`" +} +if [ x"$OUT" != x'Error: Driver not connected' ] ; then + log_error "got reply for upsc query when 'Error: Driver not connected' was expected: $OUT" + FAILED="`expr $FAILED + 1`" +else + PASSED="`expr $PASSED + 1`" +fi + +log_info "Starting dummy-ups driver(s) now" +#upsdrvctl -F start dummy & +dummy-ups -a dummy -F & +PID_DUMMYUPS="$!" + +if [ x"${TOP_SRCDIR}" != x ]; then + dummy-ups -a UPS1 -F & + PID_DUMMYUPS1="$!" + + dummy-ups -a UPS2 -F & + PID_DUMMYUPS2="$!" +fi + +sleep 5 + +(ps -ef || ps -xawwu) 2>/dev/null | grep -E '(ups|nut|dummy)' || true + +log_info "Query driver state from UPSD by UPSC after driver startup" +upsc dummy@localhost:$NUT_PORT || die "upsd does not respond" + +OUT="`upsc dummy@localhost:$NUT_PORT device.model`" || die "upsd does not respond: $OUT" +if [ x"$OUT" != x"Dummy UPS" ] ; then + log_error "got this reply for upsc query when 'Dummy UPS' was expected: $OUT" + FAILED="`expr $FAILED + 1`" +else + PASSED="`expr $PASSED + 1`" +fi + +log_info "Query driver state from UPSD by UPSC for bogus info" +OUT="`upsc dummy@localhost:$NUT_PORT ups.bogus.value 2>&1`" && { + log_error "upsc was supposed to answer with error exit code: $OUT" + FAILED="`expr $FAILED + 1`" +} +if [ x"$OUT" != x'Error: Variable not supported by UPS' ] ; then + log_error "got reply for upsc query when 'Error: Variable not supported by UPS' was expected: $OUT" + FAILED="`expr $FAILED + 1`" +else + PASSED="`expr $PASSED + 1`" +fi + +log_separator +log_info "Test that dummy-ups TIMER action changes the reported state" +# Driver is set up to flip ups.status every 5 sec, so check every 3 +OUT1="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT1" ; sleep 3 +OUT2="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT2" ; sleep 3 +OUT3="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT3" ; sleep 3 +if echo "$OUT1$OUT2$OUT3" | grep "OB" && echo "$OUT1$OUT2$OUT3" | grep "OL" ; then + log_info "OK, ups.status flips over time" + PASSED="`expr $PASSED + 1`" +else + log_error "ups.status did not flip over time" + FAILED="`expr $FAILED + 1`" +fi + +if [ x"${TOP_BUILDDIR}" != x ]; then + # That script says it expects data/evolution500.seq (as the UPS1 dummy) + # but the dummy data does not currently let issue the commands and + # setvars tested from python script. + log_separator + log_info "Call Python module test suite: PyNUT (NUT Python bindings) without login credentials" + if ( export NUT_PORT + "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py" + ) ; then + log_info "OK, PyNUT did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "PyNUT complained, check above" + FAILED="`expr $FAILED + 1`" + fi + + log_separator + log_info "Call Python module test suite: PyNUT (NUT Python bindings) with login credentials" + if ( + NUT_USER='admin' + NUT_PASS='mypass' + export NUT_USER NUT_PASS NUT_PORT + "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py" + ) ; then + log_info "OK, PyNUT did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "PyNUT complained, check above" + FAILED="`expr $FAILED + 1`" + fi +fi + +# TODO: Make and run C++ client tests + +# TODO: Some upsmon tests? + +# Allow to leave the sandbox daemons running for a while, +# to experiment with them interactively: +if [ -n "${DEBUG_SLEEP-}" ] ; then + if [ "${DEBUG_SLEEP-}" -gt 0 ] ; then + sleep "${DEBUG_SLEEP}" + else + sleep 60 + fi +fi + +log_separator +log_info "OVERALL: PASSED=$PASSED FAILED=$FAILED" +if [ "$PASSED" = 0 ] || [ "$FAILED" != 0 ] ; then + die "Some test scenarios failed!" +else + log_info "SUCCESS" +fi From d38dd62cc09d1d03297bafb750d91f8b17d3afbc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 10:48:21 +0200 Subject: [PATCH 543/700] tests/NIT/nit.sh: document DEBUG_SLEEP a bit --- tests/NIT/nit.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index d201e1b757..ffa495730c 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -9,6 +9,9 @@ # approach. Part of the goal was to let this script set up the # sandbox to run tests which could be defined in other files. # +# Caller can export envvars to impact the script behavior, e.g.: +# DEBUG_SLEEP=60 to sleep after tests, with driver+server running +# # Design note: written with dumbed-down POSIX shell syntax, to # properly work in whatever different OSes have (bash, dash, # ksh, busybox sh...) @@ -445,11 +448,13 @@ fi # Allow to leave the sandbox daemons running for a while, # to experiment with them interactively: if [ -n "${DEBUG_SLEEP-}" ] ; then + log_info "Sleeping now as asked, so you can play with the driver and server (port $NUT_PORT) running" if [ "${DEBUG_SLEEP-}" -gt 0 ] ; then sleep "${DEBUG_SLEEP}" else sleep 60 fi + log_info "Sleep finished" fi log_separator From f2d6a049bef4c435beb9eb7f4a46ef3aec24ac05 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 10:50:46 +0200 Subject: [PATCH 544/700] tests/Makefile.am: call NIT suite --- tests/Makefile.am | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index 61e5e3191b..e52281bddb 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -12,6 +12,12 @@ AM_CXXFLAGS = -I$(top_srcdir)/include check_PROGRAMS = $(TESTS) +check-NIT: NIT/nit.sh + mkdir -p "$(builddir)/NIT" + cd "$(builddir)/NIT" && "$(abs_srcdir)/NIT/nit.sh" + +check-local: check-NIT + nutlogtest_SOURCES = nutlogtest.c nutlogtest_LDADD = $(top_builddir)/common/libcommon.la @@ -91,5 +97,10 @@ MAINTAINERCLEANFILES = Makefile.in .dirstamp # NOTE: Do not clean ".deps" in SUBDIRS of the main project, # the root Makefile.am takes care of that! -#clean-local: +clean-local: + if [ x"$(abs_builddir)" = x"$(abs_srcdir)" ]; then \ + rm -rf "$(builddir)/NIT/tmp" ; \ + else \ + rm -rf "$(builddir)/NIT" ; \ + fi # rm -rf $(builddir)/.deps From bf5158a96d2d103debd5f8818b808a565b8fd2cf Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 10:58:35 +0200 Subject: [PATCH 545/700] tests/NIT/nit.sh: bail out quickly if got no daemons to run --- tests/NIT/nit.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index ffa495730c..97877848a3 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -90,6 +90,10 @@ unset PATH_ADD log_debug "Using PATH='$PATH'" +for PROG in upsd upsc dummy-ups upsmon ; do + (command -v ${PROG}) || die "Useless setup: ${PROG} not found in PATH" +done + PID_UPSD="" PID_DUMMYUPS="" PID_DUMMYUPS1="" From 34a0581fe2b1a42df04cd0a7609ed4e1edbfc79e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 11:07:08 +0200 Subject: [PATCH 546/700] tests/NIT/nit.sh: sanity checks for BUILDDIR --- tests/NIT/nit.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 97877848a3..142ae4eeb6 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -58,7 +58,11 @@ TOP_BUILDDIR="" case "${BUILDDIR}" in */tests/NIT) TOP_BUILDDIR="`cd "${BUILDDIR}"/../.. && pwd`" ;; + *) log_info "Current directory is not a .../tests/NIT" ;; esac +if ! test -w "${BUILDDIR}" ; then + log_error "BUILDDIR='${BUILDDIR}' is not writeable, tests may fail below" +fi SRCDIR="`dirname "$0"`" SRCDIR="`cd "$SCRIPTDIR" && pwd`" From 7bc4b66ad078bf8fff5e7d9476a5b2003b1c3157 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 11:07:25 +0200 Subject: [PATCH 547/700] tests/NIT/nit.sh: typo fix for SRCDIR --- tests/NIT/nit.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 142ae4eeb6..4ed744ca83 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -65,7 +65,7 @@ if ! test -w "${BUILDDIR}" ; then fi SRCDIR="`dirname "$0"`" -SRCDIR="`cd "$SCRIPTDIR" && pwd`" +SRCDIR="`cd "$SRCDIR" && pwd`" TOP_SRCDIR="" case "${SRCDIR}" in */tests/NIT) From 989c44569775245461a54394dfeaefd3b55a2a3c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 11:18:45 +0200 Subject: [PATCH 548/700] tests/NIT/nit.sh: make some debug noise optional --- tests/NIT/nit.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 4ed744ca83..1a5a56f44d 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -10,6 +10,7 @@ # sandbox to run tests which could be defined in other files. # # Caller can export envvars to impact the script behavior, e.g.: +# DEBUG=true to print debug messages, running processes, etc. # DEBUG_SLEEP=60 to sleep after tests, with driver+server running # # Design note: written with dumbed-down POSIX shell syntax, to @@ -31,8 +32,14 @@ log_separator() { echo "================================" >&2 } +shouldDebug() { + [ -n "$DEBUG" ] || [ -n "$DEBUG_SLEEP" ] +} + log_debug() { - echo "[DEBUG] $@" >&2 + if shouldDebug ; then + echo "[DEBUG] $@" >&2 + fi } log_info() { @@ -378,7 +385,9 @@ fi sleep 5 -(ps -ef || ps -xawwu) 2>/dev/null | grep -E '(ups|nut|dummy)' || true +if shouldDebug ; then + (ps -ef || ps -xawwu) 2>/dev/null | grep -E '(ups|nut|dummy)' || true +fi log_info "Query driver state from UPSD by UPSC after driver startup" upsc dummy@localhost:$NUT_PORT || die "upsd does not respond" From 41f60017a732976d9fed70a8845da308d10afbba Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 11:44:24 +0200 Subject: [PATCH 549/700] docs/nut-qa.txt: mention NUT NIT and update links to Ubuntu QART --- docs/nut-qa.txt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/nut-qa.txt b/docs/nut-qa.txt index b4b69167a0..481c989350 100644 --- a/docs/nut-qa.txt +++ b/docs/nut-qa.txt @@ -118,16 +118,20 @@ FIXME (POST): the installation, upgrade and removal testing processes. - a runtime testing suite, which automates the inter-layer communication testing -(driver -- upsd -- upsmon / clients), that is part of Ubuntu. -link:http://bazaar.launchpad.net/~ubuntu-bugcontrol/qa-regression-testing/master/view/head:/scripts/test-nut.py[The NUT testing script] -is available in the link:https://code.edge.launchpad.net/qa-regression-testing[Ubuntu QA Regression Testing suite]. -It installs NUT, configures it with the dummy-ups driver, changes a few data and -checks that these are well propagated with upsc. + (driver -- upsd -- upsmon / clients), that is part of Ubuntu. + link:https://git.launchpad.net/ubuntu/+source/nut/tree/debian/tests/test-nut.py[The NUT testing script] + is available in the link:https://code.edge.launchpad.net/qa-regression-testing[Ubuntu QA Regression Testing suite]. ++ + It installs NUT packages, configures it with the dummy-ups driver, changes + a few data points and checks that these are well propagated with upsc. + +- similar approach is explored in NIT (NUT Integration Testing) suite, + which is part of the codebase and `make check` activities - link:https://bugzilla.redhat.com/buglist.cgi?component=nut[Redhat / Fedora Bug tracker] - link:https://www.openhub.net/p/nut[Black Duck Open Hub] (formerly Ohloh.net) -provides metrics on NUT source code base and activity. + provides metrics on NUT source code base and activity. Runtime quality ~~~~~~~~~~~~~~~ From 88f5f1779d073bb4da7c1670bec2a3a7721ebcaf Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 11:45:51 +0200 Subject: [PATCH 550/700] tests/Makefile.am: help check-NIT pass in distcheck --- tests/Makefile.am | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index e52281bddb..e0efa30475 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -12,9 +12,11 @@ AM_CXXFLAGS = -I$(top_srcdir)/include check_PROGRAMS = $(TESTS) -check-NIT: NIT/nit.sh +# NUT Integration Testing suite +EXTRA_DIST += NIT/nit.sh NIT/README +check-NIT: $(abs_top_srcdir)/tests/NIT/nit.sh mkdir -p "$(builddir)/NIT" - cd "$(builddir)/NIT" && "$(abs_srcdir)/NIT/nit.sh" + cd "$(builddir)/NIT" && "$<" check-local: check-NIT From c43214ed23327cc9abad4f3ef9a6a4092c5aa02b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 12:11:01 +0200 Subject: [PATCH 551/700] tests/NIT/README: update a bit --- tests/NIT/README | 15 ++++++++++++--- tests/NIT/nit.sh | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/NIT/README b/tests/NIT/README index 4ccce2110e..e804b3063c 100644 --- a/tests/NIT/README +++ b/tests/NIT/README @@ -1,5 +1,14 @@ -NUT Integration Test suite -========================== +NUT Integration Testing suite +============================= This suite aims to simplify running `upsd`, a `dummy-ups` driver and -a few clients to query them. +a few clients to query them, as part of regular `make check` routine +or separately with existing binaries (should not impact any existing +installation data, processes or communications). + +See also +link:https://git.launchpad.net/ubuntu/+source/nut/tree/debian/tests/test-nut.py[The NUT testing script] +available in the +link:https://code.edge.launchpad.net/qa-regression-testing[Ubuntu QA Regression Testing suite] +doing a similar job with NUT installed from packages and configuring +it via files in standard path names. diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 1a5a56f44d..4cd8db9671 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -1,6 +1,6 @@ #!/bin/sh -# NUT Integration Test suite, assumes the codebase was built and +# NUT Integration Testing suite, assumes the codebase was built and # arranges running of the binaries to test driver-client-server # ability to start and their interactions. # From 367e1da0768aa793e5c716fd7a7ec440f8b3d261 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 12:19:27 +0200 Subject: [PATCH 552/700] tests/Makefile.am: help check-NIT pass in distcheck (cleaner) --- tests/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index e0efa30475..1317067dcc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -14,9 +14,9 @@ check_PROGRAMS = $(TESTS) # NUT Integration Testing suite EXTRA_DIST += NIT/nit.sh NIT/README -check-NIT: $(abs_top_srcdir)/tests/NIT/nit.sh +check-NIT: NIT/nit.sh mkdir -p "$(builddir)/NIT" - cd "$(builddir)/NIT" && "$<" + cd "$(builddir)/NIT" && "$(abs_srcdir)/NIT/nit.sh" check-local: check-NIT From 86fdf7c269639d2e7961bccaf95e161874c74ffc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 12:44:52 +0200 Subject: [PATCH 553/700] tests/NIT: warn in docs/comments that starting pwd should be the BUILDDIR --- tests/NIT/README | 3 +++ tests/NIT/nit.sh | 2 ++ 2 files changed, 5 insertions(+) diff --git a/tests/NIT/README b/tests/NIT/README index e804b3063c..30da730fdc 100644 --- a/tests/NIT/README +++ b/tests/NIT/README @@ -6,6 +6,9 @@ a few clients to query them, as part of regular `make check` routine or separately with existing binaries (should not impact any existing installation data, processes or communications). +WARNING: Current working directory when starting the script should be +the location where it may create temporary data (e.g. the BUILDDIR). + See also link:https://git.launchpad.net/ubuntu/+source/nut/tree/debian/tests/test-nut.py[The NUT testing script] available in the diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 4cd8db9671..89b99f9b2b 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -9,6 +9,8 @@ # approach. Part of the goal was to let this script set up the # sandbox to run tests which could be defined in other files. # +# WARNING: Current working directory when starting the script should be +# the location where it may create temporary data (e.g. the BUILDDIR). # Caller can export envvars to impact the script behavior, e.g.: # DEBUG=true to print debug messages, running processes, etc. # DEBUG_SLEEP=60 to sleep after tests, with driver+server running From fdbf4cdb330d1385b790a1d6400ea64657064784 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 13:10:17 +0200 Subject: [PATCH 554/700] Makefile.am: let "make check-NIT" from root dir --- Makefile.am | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile.am b/Makefile.am index 5c169e8faf..2b0eb120bf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -118,6 +118,9 @@ all-docs check-docs \ man all-man man-man check-man man-html all-html: cd $(srcdir)/docs && $(MAKE) $@ +check-NIT: + cd $(builddir)/tests && $(MAKE) $@ + # This target adds syntax-checking for committed shell script files, # to avoid surprises and delays in finding fatal typos after packaging ### From c7505e33005c675b949cf844f89ddd6c63aafeb0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 13:10:48 +0200 Subject: [PATCH 555/700] Makefile.am: fix "make check-docs" and siblings from root dir --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 2b0eb120bf..ec1650d527 100644 --- a/Makefile.am +++ b/Makefile.am @@ -116,7 +116,7 @@ spellcheck spellcheck-interactive: doc spellcheck-sortdict \ all-docs check-docs \ man all-man man-man check-man man-html all-html: - cd $(srcdir)/docs && $(MAKE) $@ + cd $(builddir)/docs && $(MAKE) $@ check-NIT: cd $(builddir)/tests && $(MAKE) $@ From e198f2e049a0799a82544f4760d7fe96dc26ba80 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 13:49:32 +0200 Subject: [PATCH 556/700] configure.ac: fix detection of cppunit on some platforms Note for posterity: CFLAGS go before source, LDFLAGS+LIBS after source, in g++ cmdline --- configure.ac | 8 +++++++- tests/Makefile.am | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 350952a5aa..b7f0798859 100644 --- a/configure.ac +++ b/configure.ac @@ -2071,11 +2071,17 @@ return res ? 0 : 1; ' my_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$myCXXFLAGS $pkg_cv_CPPUNIT_CFLAGS $pkg_cv_CPPUNIT_LIBS" + my_LDFLAGS="$LDFLAGS" + my_LIBS="$LIBS" + CXXFLAGS="$myCXXFLAGS $pkg_cv_CPPUNIT_CFLAGS $pkg_cv_CPPUNIT_CXXFLAGS" + LDFLAGS="$my_LDFLAGS $pkg_cv_CPPUNIT_LDFLAGS" + LIBS="$my_LIBS $pkg_cv_CPPUNIT_LIBS" AC_LANG_PUSH([C++]) AX_RUN_OR_LINK_IFELSE([AC_LANG_PROGRAM([[${CPLUSPLUS_DECL}]], [[${CPLUSPLUS_MAIN}]])], [have_cppunit=yes], [have_cppunit=no]) CXXFLAGS="$my_CXXFLAGS" + LDFLAGS="$my_LDFLAGS" + LIBS="$my_LIBS" AC_LANG_POP([C++]) unset CPLUSPLUS_MAIN unset CPLUSPLUS_DECL diff --git a/tests/Makefile.am b/tests/Makefile.am index 1317067dcc..57057ab0c4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -66,7 +66,7 @@ check-local: $(check_PROGRAMS) endif cppunittest_CXXFLAGS = $(AM_CXXFLAGS) $(CPPUNIT_CFLAGS) $(CPPUNIT_CXXFLAGS) $(CPPUNIT_NUT_CXXFLAGS) $(CXXFLAGS) -cppunittest_LDFLAGS = $(CPPUNIT_LIBS) +cppunittest_LDFLAGS = $(CPPUNIT_LDFLAGS) $(CPPUNIT_LIBS) cppunittest_LDADD = $(top_builddir)/clients/libnutclient.la $(top_builddir)/clients/libnutclientstub.la cppunittest_SOURCES = $(CPPUNITTESTSRC) $(CPPUNITTESTERSRC) From b136206b33537575add99d04be1e4317bf9b0fa1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 13:56:17 +0200 Subject: [PATCH 557/700] Add tests/NIT/Makefile.am for straightforward integration of the test suite --- Makefile.am | 2 +- configure.ac | 1 + tests/Makefile.am | 17 +++++------------ tests/NIT/.gitignore | 1 + tests/NIT/Makefile.am | 13 +++++++++++++ 5 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 tests/NIT/Makefile.am diff --git a/Makefile.am b/Makefile.am index ec1650d527..eeccdaa0d8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -119,7 +119,7 @@ man all-man man-man check-man man-html all-html: cd $(builddir)/docs && $(MAKE) $@ check-NIT: - cd $(builddir)/tests && $(MAKE) $@ + cd $(builddir)/tests/NIT && $(MAKE) $@ # This target adds syntax-checking for committed shell script files, # to avoid surprises and delays in finding fatal typos after packaging diff --git a/configure.ac b/configure.ac index b7f0798859..7da3c471e9 100644 --- a/configure.ac +++ b/configure.ac @@ -2894,6 +2894,7 @@ AC_CONFIG_FILES([ tools/Makefile tools/nut-scanner/Makefile tests/Makefile + tests/NIT/Makefile Makefile ]) diff --git a/tests/Makefile.am b/tests/Makefile.am index 57057ab0c4..91debb8ae1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,7 @@ # Network UPS Tools: tests +SUBDIRS = . NIT + all: $(TESTS) EXTRA_DIST = nut-driver-enumerator-test.sh nut-driver-enumerator-test--ups.conf @@ -13,12 +15,8 @@ AM_CXXFLAGS = -I$(top_srcdir)/include check_PROGRAMS = $(TESTS) # NUT Integration Testing suite -EXTRA_DIST += NIT/nit.sh NIT/README -check-NIT: NIT/nit.sh - mkdir -p "$(builddir)/NIT" - cd "$(builddir)/NIT" && "$(abs_srcdir)/NIT/nit.sh" - -check-local: check-NIT +check-NIT: + cd "$(builddir)/NIT" && $(MAKE) $@ nutlogtest_SOURCES = nutlogtest.c nutlogtest_LDADD = $(top_builddir)/common/libcommon.la @@ -99,10 +97,5 @@ MAINTAINERCLEANFILES = Makefile.in .dirstamp # NOTE: Do not clean ".deps" in SUBDIRS of the main project, # the root Makefile.am takes care of that! -clean-local: - if [ x"$(abs_builddir)" = x"$(abs_srcdir)" ]; then \ - rm -rf "$(builddir)/NIT/tmp" ; \ - else \ - rm -rf "$(builddir)/NIT" ; \ - fi +#clean-local: # rm -rf $(builddir)/.deps diff --git a/tests/NIT/.gitignore b/tests/NIT/.gitignore index ceeb05b410..77091adc00 100644 --- a/tests/NIT/.gitignore +++ b/tests/NIT/.gitignore @@ -1 +1,2 @@ /tmp +/Makefile.in diff --git a/tests/NIT/Makefile.am b/tests/NIT/Makefile.am new file mode 100644 index 0000000000..a417be2bd9 --- /dev/null +++ b/tests/NIT/Makefile.am @@ -0,0 +1,13 @@ +EXTRA_DIST = nit.sh README + +check: check-NIT + +# Run in builddir, use script from srcdir +check-NIT: $(abs_srcdir)/nit.sh + @cd .. && ( $(MAKE) -s cppnit || echo "OPTIONAL C++ test client test will be skipped" ) + "$<" + +MAINTAINERCLEANFILES = Makefile.in .dirstamp + +clean-local: + rm -rf tmp From 99fa71a9c5fc5cf3421b837430ebff9a89c2a3f0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 14:01:38 +0200 Subject: [PATCH 558/700] tests/NIT/nit.sh: let caller customize NUT_PORT --- tests/NIT/nit.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 89b99f9b2b..ef2b0baacb 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -14,6 +14,7 @@ # Caller can export envvars to impact the script behavior, e.g.: # DEBUG=true to print debug messages, running processes, etc. # DEBUG_SLEEP=60 to sleep after tests, with driver+server running +# NUT_PORT=12345 custom port for upsd to listen and clients to query # # Design note: written with dumbed-down POSIX shell syntax, to # properly work in whatever different OSes have (bash, dash, @@ -125,7 +126,8 @@ NUT_CONFPATH="$BUILDDIR/tmp/etc" export NUT_STATEPATH NUT_ALTPIDPATH NUT_CONFPATH # TODO: Find a portable way to grab a random unprivileged port? -NUT_PORT="34931" +[ -n "${NUT_PORT-}" ] && [ "$NUT_PORT" -gt 0 ] && [ "$NUT_PORT" -lt 65536 ] \ +|| NUT_PORT="34931" ### upsd.conf: ################################################## From 56d64eb1821a61f3af473f31e0fe00ac1d596a20 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 14:45:34 +0200 Subject: [PATCH 559/700] tests/NIT/nit.sh: embed weak randomization of NUT_PORT At least this little magic is better than a hardcoded number: if several CI executors run `make check` at once on same host, they now have a decent chance to not collide (not guaranteed though) --- tests/NIT/nit.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index ef2b0baacb..391cb2842a 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -125,9 +125,16 @@ NUT_ALTPIDPATH="$BUILDDIR/tmp/run" NUT_CONFPATH="$BUILDDIR/tmp/etc" export NUT_STATEPATH NUT_ALTPIDPATH NUT_CONFPATH -# TODO: Find a portable way to grab a random unprivileged port? +# TODO: Find a portable way to (check and) grab a random unprivileged port? [ -n "${NUT_PORT-}" ] && [ "$NUT_PORT" -gt 0 ] && [ "$NUT_PORT" -lt 65536 ] \ -|| NUT_PORT="34931" +|| { + DELTA1="`date +%S`" || DELTA1=0 + DELTA2="`expr $$ % 99`" || DELTA2=0 + + NUT_PORT="`expr 34931 + $DELTA1 + $DELTA2`" \ + && [ "$NUT_PORT" -gt 0 ] && [ "$NUT_PORT" -lt 65536 ] \ + || NUT_PORT=34931 +} ### upsd.conf: ################################################## From ff470c9e0a5ec7a24286ed77655f04bf332e2939 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 15:46:23 +0200 Subject: [PATCH 560/700] tests/NIT/nit.sh: check that test_nutclient.py was generated before running it; export NUT_PORT just once --- tests/NIT/nit.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 391cb2842a..67551284f6 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -437,13 +437,17 @@ else FAILED="`expr $FAILED + 1`" fi -if [ x"${TOP_BUILDDIR}" != x ]; then +# We optionally make python module (if interpreter is found): +if [ x"${TOP_BUILDDIR}" != x ] \ +&& [ -x "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py" ] \ +; then # That script says it expects data/evolution500.seq (as the UPS1 dummy) # but the dummy data does not currently let issue the commands and # setvars tested from python script. log_separator log_info "Call Python module test suite: PyNUT (NUT Python bindings) without login credentials" - if ( export NUT_PORT + if ( unset NUT_USER || true + unset NUT_PASS || true "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py" ) ; then log_info "OK, PyNUT did not complain" @@ -458,7 +462,7 @@ if [ x"${TOP_BUILDDIR}" != x ]; then if ( NUT_USER='admin' NUT_PASS='mypass' - export NUT_USER NUT_PASS NUT_PORT + export NUT_USER NUT_PASS "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py" ) ; then log_info "OK, PyNUT did not complain" From 5c4a0d843f852fc301d058de2b5cb9bacd307c05 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 18:40:39 +0200 Subject: [PATCH 561/700] tests/NIT/nit.sh: refactor upsd.users passwords as shell vars to ease reuse --- tests/NIT/nit.sh | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 67551284f6..23919cca86 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -156,32 +156,37 @@ generatecfg_upsd_nodev() { ### upsd.users: ################################################## +TESTPASS_ADMIN='mypass' +TESTPASS_TESTER='pass words' +TESTPASS_UPSMON_PRIMARY='P@ssW0rdAdm' +TESTPASS_UPSMON_SECONDARY='P@ssW0rd' + generatecfg_upsdusers_trivial() { cat > "$NUT_CONFPATH/upsd.users" << EOF [admin] - password = mypass + password = $TESTPASS_ADMIN actions = SET instcmds = ALL [tester] - password = "pass words" + password = "${TESTPASS_TESTER}" instcmds = test.battery.start instcmds = test.battery.stop [dummy-admin-m] - password = 'P@ssW0rdAdm' + password = "${TESTPASS_UPSMON_PRIMARY}" upsmon master [dummy-admin] - password = 'P@ssW0rdAdm' + password = "${TESTPASS_UPSMON_PRIMARY}" upsmon primary [dummy-user-s] - password = 'P@ssW0rd' + password = "${TESTPASS_UPSMON_SECONDARY}" upsmon slave [dummy-user] - password = 'P@ssW0rd' + password = "${TESTPASS_UPSMON_SECONDARY}" upsmon secondary EOF [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: upsd.users" @@ -200,25 +205,25 @@ generatecfg_upsmon_trivial() { generatecfg_upsmon_master() { generatecfg_upsmon_trivial - echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-admin-m' 'P@ssW0rdAdm' master" >> "$NUT_CONFPATH/upsmon.conf" \ + echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-admin-m' '${TESTPASS_UPSMON_PRIMARY}' master" >> "$NUT_CONFPATH/upsmon.conf" \ || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" } generatecfg_upsmon_primary() { generatecfg_upsmon_trivial - echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-admin' 'P@ssW0rdAdm' primary" >> "$NUT_CONFPATH/upsmon.conf" \ + echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-admin' '${TESTPASS_UPSMON_PRIMARY}' primary" >> "$NUT_CONFPATH/upsmon.conf" \ || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" } generatecfg_upsmon_slave() { generatecfg_upsmon_trivial - echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-user-s' 'P@ssW0rd' slave" >> "$NUT_CONFPATH/upsmon.conf" \ + echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-user-s' '${TESTPASS_UPSMON_SECONDARY}' slave" >> "$NUT_CONFPATH/upsmon.conf" \ || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" } generatecfg_upsmon_secondary() { generatecfg_upsmon_trivial - echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-user' 'P@ssW0rd' secondary" >> "$NUT_CONFPATH/upsmon.conf" \ + echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-user' '${TESTPASS_UPSMON_SECONDARY}' secondary" >> "$NUT_CONFPATH/upsmon.conf" \ || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" } @@ -461,7 +466,7 @@ if [ x"${TOP_BUILDDIR}" != x ] \ log_info "Call Python module test suite: PyNUT (NUT Python bindings) with login credentials" if ( NUT_USER='admin' - NUT_PASS='mypass' + NUT_PASS="${TESTPASS_ADMIN}" export NUT_USER NUT_PASS "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py" ) ; then From 4b85508658a387e6c513d9144d38dd19aa4f492b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 18:41:23 +0200 Subject: [PATCH 562/700] tests/NIT/nit.sh: log OVERALL results before DEBUG_SLEEP --- tests/NIT/nit.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 23919cca86..f7d77ce541 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -482,9 +482,13 @@ fi # TODO: Some upsmon tests? +log_separator +log_info "OVERALL: PASSED=$PASSED FAILED=$FAILED" + # Allow to leave the sandbox daemons running for a while, # to experiment with them interactively: if [ -n "${DEBUG_SLEEP-}" ] ; then + log_separator log_info "Sleeping now as asked, so you can play with the driver and server (port $NUT_PORT) running" if [ "${DEBUG_SLEEP-}" -gt 0 ] ; then sleep "${DEBUG_SLEEP}" @@ -492,10 +496,9 @@ if [ -n "${DEBUG_SLEEP-}" ] ; then sleep 60 fi log_info "Sleep finished" + log_separator fi -log_separator -log_info "OVERALL: PASSED=$PASSED FAILED=$FAILED" if [ "$PASSED" = 0 ] || [ "$FAILED" != 0 ] ; then die "Some test scenarios failed!" else From 7dfc154343db2e91618b28791d0e4baba1877453 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 15:19:04 +0200 Subject: [PATCH 563/700] NutActiveClientTest: introduce C++ NutActiveClientTest for NIT and similar uses --- clients/nutclient.h | 5 + tests/Makefile.am | 17 +- tests/NIT/nit.sh | 68 ++++++- tests/cpputest-client.cpp | 383 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 467 insertions(+), 6 deletions(-) create mode 100644 tests/cpputest-client.cpp diff --git a/clients/nutclient.h b/clients/nutclient.h index 32ead0fe39..e1020f2b67 100644 --- a/clients/nutclient.h +++ b/clients/nutclient.h @@ -360,6 +360,11 @@ class Client */ class TcpClient : public Client { + /* We have a number of direct-call methods we do not expose + * generally, but still want covered with integration tests + */ + friend class NutActiveClientTest; + public: /** * Construct a nut TcpClient object. diff --git a/tests/Makefile.am b/tests/Makefile.am index 91debb8ae1..8f5f4c4f91 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -49,7 +49,9 @@ CPPUNITTESTSRC = example.cpp nutclienttest.cpp # The test driver which orchestrates running those tests above CPPUNITTESTERSRC = cpputest.cpp -TESTS_CXX11 = cppunittest +CPPCLIENTTESTSRC = cpputest-client.cpp + +TESTS_CXX11 = cppunittest cppnit if HAVE_CXX11 if HAVE_CPPUNIT @@ -68,6 +70,11 @@ cppunittest_LDFLAGS = $(CPPUNIT_LDFLAGS) $(CPPUNIT_LIBS) cppunittest_LDADD = $(top_builddir)/clients/libnutclient.la $(top_builddir)/clients/libnutclientstub.la cppunittest_SOURCES = $(CPPUNITTESTSRC) $(CPPUNITTESTERSRC) +cppnit_CXXFLAGS = $(AM_CXXFLAGS) $(CPPUNIT_CFLAGS) $(CPPUNIT_CXXFLAGS) $(CPPUNIT_NUT_CXXFLAGS) $(CXXFLAGS) +cppnit_LDFLAGS = $(CPPUNIT_LDFLAGS) $(CPPUNIT_LIBS) +cppnit_LDADD = $(top_builddir)/clients/libnutclient.la $(top_builddir)/clients/libnutclientstub.la +cppnit_SOURCES = $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) + # Make sure out-of-dir C++ dependencies exist (especially when dev-building # only some parts of NUT): $(top_builddir)/clients/libnutclient.la \ @@ -75,16 +82,16 @@ $(top_builddir)/clients/libnutclientstub.la: dummy @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) else !HAVE_CPPUNIT -# Just redistribute test source into tarball +# Just redistribute test source into tarball if not building tests -EXTRA_DIST += $(CPPUNITTESTSRC) $(CPPUNITTESTERSRC) +EXTRA_DIST += $(CPPUNITTESTSRC) $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) endif !HAVE_CPPUNIT else !HAVE_CXX11 -# Just redistribute test source into tarball +# Just redistribute test source into tarball if not building C++ at all -EXTRA_DIST += example.cpp cpputest.cpp +EXTRA_DIST += $(CPPUNITTESTSRC) $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) endif !HAVE_CXX11 diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index f7d77ce541..6a7b742825 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -135,6 +135,7 @@ export NUT_STATEPATH NUT_ALTPIDPATH NUT_CONFPATH && [ "$NUT_PORT" -gt 0 ] && [ "$NUT_PORT" -lt 65536 ] \ || NUT_PORT=34931 } +export NUT_PORT ### upsd.conf: ################################################## @@ -478,7 +479,72 @@ if [ x"${TOP_BUILDDIR}" != x ] \ fi fi -# TODO: Make and run C++ client tests +# We optionally make and here can run C++ client tests: +if [ x"${TOP_BUILDDIR}" != x ] && [ -x "${TOP_BUILDDIR}/tests/cppnit" ] ; then + log_separator + log_info "Call libnutclient test suite: cppnit without login credentials" + if ( unset NUT_USER || true + unset NUT_PASS || true + "${TOP_BUILDDIR}/tests/cppnit" + ) ; then + log_info "OK, cppnit did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "cppnit complained, check above" + FAILED="`expr $FAILED + 1`" + fi + + log_separator + log_info "Call libnutclient test suite: cppnit with login credentials: simple admin" + if ( + NUT_USER='admin' + NUT_PASS="${TESTPASS_ADMIN}" + NUT_SETVAR_DEVICE='dummy' + unset NUT_PRIMARY_DEVICE + export NUT_USER NUT_PASS NUT_SETVAR_DEVICE + "${TOP_BUILDDIR}/tests/cppnit" + ) ; then + log_info "OK, cppnit did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "cppnit complained, check above" + FAILED="`expr $FAILED + 1`" + fi + + log_separator + log_info "Call libnutclient test suite: cppnit with login credentials: upsmon-primary" + if ( + NUT_USER='dummy-admin' + NUT_PASS="${TESTPASS_UPSMON_PRIMARY}" + NUT_PRIMARY_DEVICE='dummy' + unset NUT_SETVAR_DEVICE + export NUT_USER NUT_PASS NUT_PRIMARY_DEVICE + "${TOP_BUILDDIR}/tests/cppnit" + ) ; then + log_info "OK, cppnit did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "cppnit complained, check above" + FAILED="`expr $FAILED + 1`" + fi + + log_separator + log_info "Call libnutclient test suite: cppnit with login credentials: upsmon-master" + if ( + NUT_USER='dummy-admin-m' + NUT_PASS="${TESTPASS_UPSMON_PRIMARY}" + NUT_PRIMARY_DEVICE='dummy' + unset NUT_SETVAR_DEVICE + export NUT_USER NUT_PASS NUT_PRIMARY_DEVICE + "${TOP_BUILDDIR}/tests/cppnit" + ) ; then + log_info "OK, cppnit did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "cppnit complained, check above" + FAILED="`expr $FAILED + 1`" + fi +fi # TODO: Some upsmon tests? diff --git a/tests/cpputest-client.cpp b/tests/cpputest-client.cpp new file mode 100644 index 0000000000..5f8e739b2d --- /dev/null +++ b/tests/cpputest-client.cpp @@ -0,0 +1,383 @@ +/* cpputest-client - CppUnit libnutclient active test + + Module for NUT `cpputest` runner, to check client-server interactions + as part of NIT (NUT Integration Testing) suite and similar scenarios. + This is an "active" NUT client talking to an `upsd` on $NUT_PORT, + as opposed to nutclienttest.cpp which unit-tests the class API etc. + in isolated-binary fashion. + + Copyright (C) 2022 Jim Klimov + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "common.h" + +/* Current CPPUnit offends the honor of C++98 */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS || defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS) +#pragma GCC diagnostic push +# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS +# pragma GCC diagnostic ignored "-Wglobal-constructors" +# endif +# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS +# pragma GCC diagnostic ignored "-Wexit-time-destructors" +# endif +#endif + +#include +#include +#include +#include +#include + +namespace nut { + +class NutActiveClientTest : public CppUnit::TestFixture +{ + /* Note: is "friend" of nut::TcpClient class + * to test a few of its protected methods */ + + CPPUNIT_TEST_SUITE( NutActiveClientTest ); + CPPUNIT_TEST( test_query_ver ); + CPPUNIT_TEST( test_list_ups ); + CPPUNIT_TEST( test_auth_user ); + CPPUNIT_TEST( test_auth_primary ); + CPPUNIT_TEST_SUITE_END(); + +private: + /* Fed by caller via envvars: */ + uint16_t NUT_PORT = 0; + std::string NUT_USER = ""; + std::string NUT_PASS = ""; + std::string NUT_PRIMARY_DEVICE = ""; + std::string NUT_SETVAR_DEVICE = ""; + +public: + void setUp() override; + void tearDown() override; + + void test_query_ver(); + void test_list_ups(); + void test_auth_user(); + void test_auth_primary(); +}; + +// Registers the fixture into the 'registry' +CPPUNIT_TEST_SUITE_REGISTRATION( NutActiveClientTest ); + +} // namespace nut {} + +#ifndef _NUTCLIENTTEST_BUILD +# define _NUTCLIENTTEST_BUILD 1 +#endif + +#include "../clients/nutclient.h" +#include "../clients/nutclientmem.h" + +namespace nut { + +extern "C" { +strarr stringset_to_strarr(const std::set& strset); +strarr stringvector_to_strarr(const std::vector& strset); +} // extern "C" + +void NutActiveClientTest::setUp() +{ + /* NUT_PORT etc. are provided by external test suite driver */ + char * s; + + s = std::getenv("NUT_PORT"); + if (s) { + long l = atol(s); + if (l < 1 || l > 65535) { + throw std::runtime_error("NUT_PORT specified by caller is out of range"); + } + NUT_PORT = static_cast(l); + } else { + throw std::runtime_error("NUT_PORT not specified by caller, NIT should call this test"); + } + + s = std::getenv("NUT_USER"); + if (s) { + NUT_USER = s; + } // else stays empty + + s = std::getenv("NUT_PASS"); + if (s) { + NUT_PASS = s; + } // else stays empty + + s = std::getenv("NUT_PRIMARY_DEVICE"); + if (s) { + NUT_PRIMARY_DEVICE = s; + } // else stays empty + + s = std::getenv("NUT_SETVAR_DEVICE"); + if (s) { + NUT_SETVAR_DEVICE = s; + } // else stays empty +} + +void NutActiveClientTest::tearDown() +{ +} + +void NutActiveClientTest::test_query_ver() { + nut::TcpClient c("localhost", NUT_PORT); + std::string s; + + std::cerr << "[D] C++ NUT Client lib test running against Data Server at: " + << c.getHost() << ':' << c.getPort() << std::endl; + + CPPUNIT_ASSERT_MESSAGE( + "TcpClient is not connected after constructor", + c.isConnected()); + + /* Note: generic client code can not use protected methods + * like low-level sendQuery(), list(), get() and some more, + * but this NutActiveClientTest is a friend of TcpClient: + */ + s = c.sendQuery("VER"); + std::cerr << "[D] Got Data Server VER: " << s << std::endl; + + try { + s = c.sendQuery("PROTVER"); + std::cerr << "[D] Got PROTVER: " << s << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Did not get PROTVER: " << ex.what() << std::endl; + } + + try { + s = c.sendQuery("NETVER"); + std::cerr << "[D] Got NETVER (obsolete): " << s << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Did not get NETVER (obsolete): " << ex.what() << std::endl; + } + + try { + c.logout(); + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not get LOGOUT: " << ex.what() << std::endl; + } + + try { + c.disconnect(); + } + catch(nut::NutException& ex) + { + /* NUT_UNUSED_VARIABLE(ex); */ + std::cerr << "[D] Could not get disconnect(): " << ex.what() << std::endl; + } +} + +void NutActiveClientTest::test_list_ups() { + nut::TcpClient c("localhost", NUT_PORT); + std::set devs; + bool noException = true; + + try { + devs = c.getDeviceNames(); + std::cerr << "[D] Got device list (" << devs.size() << "): ["; + for (std::set::iterator it = devs.begin(); + it != devs.end(); it++ + ) { + if (it != devs.begin()) { + std::cerr << ", "; + } + std::cerr << '"' << *it << '"'; + } + std::cerr << "]" << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not device list: " << ex.what() << std::endl; + noException = false; + } + + c.logout(); + c.disconnect(); + + CPPUNIT_ASSERT_MESSAGE( + "Failed to list UPS with TcpClient: threw NutException", + noException); +} + +void NutActiveClientTest::test_auth_user() { + if (NUT_USER.empty()) { + std::cerr << "[D] SKIPPING test_auth_user()" << std::endl; + return; + } + + nut::TcpClient c("localhost", NUT_PORT); + bool noException = true; + try { + c.authenticate(NUT_USER, NUT_PASS); + std::cerr << "[D] Authenticated without exceptions" << std::endl; + /* Note: no high hopes here, credentials are checked by server + * when running critical commands, not at auth request itself */ + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not authenticate as a simple user: " << ex.what() << std::endl; + noException = false; + } + + if (!NUT_SETVAR_DEVICE.empty()) { + try { + TrackingResult tres; + TrackingID tid; + std::string nutVar = "ups.status"; /* Has a risk of flip-flop with NIT dummy setup */ + std::string s = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + std::string sTest = s + "-test"; + + tid = c.setDeviceVariable(NUT_SETVAR_DEVICE, nutVar, sTest); + while ( (tres = c.getTrackingResult(tid)) == PENDING) { + usleep(100); + } + if (tres != SUCCESS) { + std::cerr << "[D] Failed to set device variable: " + << "tracking result is " << tres << std::endl; + noException = false; + } + /* Check what we got after set */ + std::string s2 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + + /* Fix it back */ + tid = c.setDeviceVariable(NUT_SETVAR_DEVICE, nutVar, s); + while ( (tres = c.getTrackingResult(tid)) == PENDING) { + usleep(100); + } + if (tres != SUCCESS) { + std::cerr << "[D] Failed to set device variable: " + << "tracking result is " << tres << std::endl; + noException = false; + } + std::string s3 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + + if (s3 != s) { + std::cerr << "[D] Final device variable value '" << s3 + << "' differs from original '" << s + << "'" << std::endl; + noException = false; + } + + if (s2 == s) { + std::cerr << "[D] Tweaked device variable value '" << s2 + << "' does not differ from original '" << s + << "'" << std::endl; + noException = false; + } + + if (noException) { + std::cerr << "[D] Tweaked device variable value OK" << std::endl; + } + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Failed to set device variable: " + << ex.what() << std::endl; + noException = false; + } + } else { + std::cerr << "[D] SKIPPING test_auth_user() active test " + << "(got no NUT_SETVAR_DEVICE to poke)" << std::endl; + } + + c.logout(); + c.disconnect(); + + CPPUNIT_ASSERT_MESSAGE( + "Failed to auth as user with TcpClient or tweak device variable", + noException); +} + +void NutActiveClientTest::test_auth_primary() { + if (NUT_USER.empty() || NUT_PRIMARY_DEVICE.empty()) { + std::cerr << "[D] SKIPPING test_auth_primary()" << std::endl; + return; + } + + nut::TcpClient c("localhost", NUT_PORT); + bool noException = true; + try { + c.authenticate(NUT_USER, NUT_PASS); + std::cerr << "[D] Authenticated without exceptions" << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not authenticate as an upsmon primary user: " + << ex.what() << std::endl; + noException = false; + } + + try { + Device d = c.getDevice(NUT_PRIMARY_DEVICE); + bool gotPrimary = false; + bool gotMaster = false; + + try { + c.deviceMaster(NUT_PRIMARY_DEVICE); + gotMaster = true; + std::cerr << "[D] Elevated as MASTER without exceptions" << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not elevate as MASTER for " + << "NUT_PRIMARY_DEVICE " << NUT_PRIMARY_DEVICE << ": " + << ex.what() << std::endl; + } + + try { + c.devicePrimary(NUT_PRIMARY_DEVICE); + gotPrimary = true; + std::cerr << "[D] Elevated as PRIMARY without exceptions" << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not elevate as PRIMARY for " + << "NUT_PRIMARY_DEVICE " << NUT_PRIMARY_DEVICE << ": " + << ex.what() << std::endl; + } + + if (!gotMaster && !gotPrimary) + noException = false; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] NUT_PRIMARY_DEVICE " << NUT_PRIMARY_DEVICE + << " not found on Data Server: " + << ex.what() << std::endl; + noException = false; + } + + c.logout(); + c.disconnect(); + + CPPUNIT_ASSERT_MESSAGE( + "Failed to auth as user with TcpClient: threw NutException", + noException); +} + +} // namespace nut {} + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS || defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS) +#pragma GCC diagnostic pop +#endif From 7c1e7da22bb191d6a7a6804323fad755d26c5b6e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 18:55:21 +0200 Subject: [PATCH 564/700] tests/Makefile.am: do not run "cppnit" as part of TESTS, only give a recipe to build it --- tests/Makefile.am | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index 8f5f4c4f91..dda545f8dc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -51,7 +51,7 @@ CPPUNITTESTERSRC = cpputest.cpp CPPCLIENTTESTSRC = cpputest-client.cpp -TESTS_CXX11 = cppunittest cppnit +TESTS_CXX11 = cppunittest if HAVE_CXX11 if HAVE_CPPUNIT @@ -60,6 +60,9 @@ if HAVE_CPPUNIT TESTS += $(TESTS_CXX11) +# Note: we only build it, but do not run directly (NIT prepares the sandbox) +check_PROGRAMS += cppnit + if WITH_VALGRIND check-local: $(check_PROGRAMS) RES=0; for P in $^ ; do $(VALGRIND) ./$$P || { RES=$$? ; echo "FAILED: $(VALGRIND) ./$$P" >&2; }; done; exit $$RES @@ -86,6 +89,9 @@ else !HAVE_CPPUNIT EXTRA_DIST += $(CPPUNITTESTSRC) $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) +cppnit: + @echo "SKIP: $@ not implemented without C++11 and CPPUNIT enabled" >&2 ; exit 1 + endif !HAVE_CPPUNIT else !HAVE_CXX11 @@ -93,6 +99,9 @@ else !HAVE_CXX11 EXTRA_DIST += $(CPPUNITTESTSRC) $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) +cppnit: + @echo "SKIP: $@ not implemented without C++11 and CPPUNIT enabled" >&2 ; exit 1 + endif !HAVE_CXX11 dummy: From f61185422a67a569b1f6e069f3ba81438c74c9e9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 19:00:25 +0200 Subject: [PATCH 565/700] tests/NIT/nit.sh: Avoid dummies with TIMER flip-flops for cppnit if we can --- tests/NIT/nit.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 6a7b742825..79d2f293f7 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -499,7 +499,13 @@ if [ x"${TOP_BUILDDIR}" != x ] && [ -x "${TOP_BUILDDIR}/tests/cppnit" ] ; then if ( NUT_USER='admin' NUT_PASS="${TESTPASS_ADMIN}" - NUT_SETVAR_DEVICE='dummy' + if [ x"${TOP_SRCDIR}" != x ]; then + # Avoid dummies with TIMER flip-flops + NUT_SETVAR_DEVICE='UPS2' + else + # Risks failure when lauching sub-test at the wrong second + NUT_SETVAR_DEVICE='dummy' + fi unset NUT_PRIMARY_DEVICE export NUT_USER NUT_PASS NUT_SETVAR_DEVICE "${TOP_BUILDDIR}/tests/cppnit" From 02b66f33313606c4b39a0b476be66f76079f1f40 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 20:48:45 +0200 Subject: [PATCH 566/700] drivers/main.c: accept "lt-PROGNAME" to run libtool-wrapped builds during development --- drivers/main.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/main.c b/drivers/main.c index b8bb0308c2..759a132541 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -519,9 +519,17 @@ void do_upsconf_args(char *confupsname, char *var, char *val) /* don't let the user shoot themselves in the foot */ if (!strcmp(var, "driver")) { - if (strcmp(val, progname) != 0) + /* Accomodate for libtool wrapped developer iterations */ + char buf[PATH_MAX]; + if (snprintfcat(buf, sizeof(buf), "lt-%s", progname) < 0) + buf[0] = '\0'; + + if (strcmp(val, progname) != 0 + && strcmp(val, buf) != 0 + ) { fatalx(EXIT_FAILURE, "Error: UPS [%s] is for driver %s, but I'm %s!\n", confupsname, val, progname); + } return; } From 4b6ee21a37ecf8b136b1f2101ba5d375c331e47f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 20:49:58 +0200 Subject: [PATCH 567/700] tests/NIT/Makefile.am: use more portable clumsier code --- tests/NIT/Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/NIT/Makefile.am b/tests/NIT/Makefile.am index a417be2bd9..2a25eb952b 100644 --- a/tests/NIT/Makefile.am +++ b/tests/NIT/Makefile.am @@ -3,9 +3,10 @@ EXTRA_DIST = nit.sh README check: check-NIT # Run in builddir, use script from srcdir +# Avoid running "$<" - not all make implementations handle that check-NIT: $(abs_srcdir)/nit.sh @cd .. && ( $(MAKE) -s cppnit || echo "OPTIONAL C++ test client test will be skipped" ) - "$<" + "$(abs_srcdir)/nit.sh" MAINTAINERCLEANFILES = Makefile.in .dirstamp From 896a59f4f0bb0aaf62c726728e79a4f63484a436 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 20 Apr 2022 21:36:07 +0200 Subject: [PATCH 568/700] tests/NIT/nit.sh: report issues more usefully for troubleshooting CI --- tests/NIT/nit.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 79d2f293f7..59c1552d89 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -68,7 +68,7 @@ TOP_BUILDDIR="" case "${BUILDDIR}" in */tests/NIT) TOP_BUILDDIR="`cd "${BUILDDIR}"/../.. && pwd`" ;; - *) log_info "Current directory is not a .../tests/NIT" ;; + *) log_info "Current directory '${BUILDDIR}' is not a .../tests/NIT" ;; esac if ! test -w "${BUILDDIR}" ; then log_error "BUILDDIR='${BUILDDIR}' is not writeable, tests may fail below" @@ -80,6 +80,7 @@ TOP_SRCDIR="" case "${SRCDIR}" in */tests/NIT) TOP_SRCDIR="`cd "${SRCDIR}"/../.. && pwd`" ;; + *) log_info "Script source directory '${SRCDIR}' is not a .../tests/NIT" ;; esac # No fuss about LD_LIBRARY_PATH: for binaries that need it, @@ -105,7 +106,7 @@ unset PATH_ADD log_debug "Using PATH='$PATH'" for PROG in upsd upsc dummy-ups upsmon ; do - (command -v ${PROG}) || die "Useless setup: ${PROG} not found in PATH" + (command -v ${PROG}) || die "Useless setup: ${PROG} not found in PATH: ${PATH}" done PID_UPSD="" From 5d96a0dd4da3cc846cca82c42a3c2dd835ae109d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 07:25:03 +0000 Subject: [PATCH 569/700] tests/NIT/nit.sh: spell out 127.0.0.1 and ::1 if detected supported on local system --- tests/NIT/nit.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 59c1552d89..0ec68f9514 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -148,6 +148,14 @@ LISTEN localhost $NUT_PORT EOF [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: upsd.conf" chmod 640 "$NUT_CONFPATH/upsd.conf" + + # Some systems listining on symbolic "localhost" actually + # only bind to IPv6, and Python telnetlib resolves IPv4 + # and fails its connection tests. Others fare well with + # both addresses in one command. + for LH in 127.0.0.1 '::1' ; do + ping -c 1 "$LH" 2>/dev/null >/dev/null && { echo "LISTEN $LH $NUT_PORT" >> "$NUT_CONFPATH/upsd.conf"; } + done } generatecfg_upsd_nodev() { From ea16c142a1d7976839823b6e21f9b999f4a24db3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 07:28:10 +0000 Subject: [PATCH 570/700] tests/NIT/nit.sh: spell out 127.0.0.1 and ::1 if detected supported on local system - do not rely on ping CLI alone --- tests/NIT/nit.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 0ec68f9514..b405864aa5 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -154,7 +154,12 @@ EOF # and fails its connection tests. Others fare well with # both addresses in one command. for LH in 127.0.0.1 '::1' ; do - ping -c 1 "$LH" 2>/dev/null >/dev/null && { echo "LISTEN $LH $NUT_PORT" >> "$NUT_CONFPATH/upsd.conf"; } + if ( + ( cat /etc/hosts || getent hosts ) | grep "$LH" \ + || ping -c 1 "$LH" + ) 2>/dev/null >/dev/null ; then + echo "LISTEN $LH $NUT_PORT" >> "$NUT_CONFPATH/upsd.conf" + fi done } From 9ea37b9a02b9bd5351bb517c29492bfaec9df09c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 07:28:30 +0000 Subject: [PATCH 571/700] tests/NIT/nit.sh: report DEBUG_SLEEP mode more usefully --- tests/NIT/nit.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index b405864aa5..e186a86460 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -575,7 +575,8 @@ log_info "OVERALL: PASSED=$PASSED FAILED=$FAILED" # to experiment with them interactively: if [ -n "${DEBUG_SLEEP-}" ] ; then log_separator - log_info "Sleeping now as asked, so you can play with the driver and server (port $NUT_PORT) running" + log_info "Sleeping now as asked, so you can play with the driver and server running; hint: export NUT_PORT=$NUT_PORT" + log_separator if [ "${DEBUG_SLEEP-}" -gt 0 ] ; then sleep "${DEBUG_SLEEP}" else From ceae0a129f879d26295a630c98173709a9bf0348 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 07:39:23 +0000 Subject: [PATCH 572/700] tests/cpputest-client.cpp: test_auth_user(): bail out if "ups.status" reply is empty --- tests/cpputest-client.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/cpputest-client.cpp b/tests/cpputest-client.cpp index 5f8e739b2d..6168de09ad 100644 --- a/tests/cpputest-client.cpp +++ b/tests/cpputest-client.cpp @@ -248,6 +248,12 @@ void NutActiveClientTest::test_auth_user() { std::string s = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; std::string sTest = s + "-test"; + std::cerr << "[D] Got initial device '" << NUT_SETVAR_DEVICE + << "' variable '" << nutVar << "' value: " << s << std::endl; + CPPUNIT_ASSERT_MESSAGE( + "Did not expect empty value here", + !s.empty()); + tid = c.setDeviceVariable(NUT_SETVAR_DEVICE, nutVar, sTest); while ( (tres = c.getTrackingResult(tid)) == PENDING) { usleep(100); From 554154bd39b9f04c654584b428d499f8de2d94c9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 07:46:59 +0000 Subject: [PATCH 573/700] tests/NIT/nit.sh: make sure ups.status is not pre-defined empty in test sandbox --- tests/NIT/nit.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index e186a86460..9c3edb6892 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -288,6 +288,13 @@ EOF port = epdu-managed.dev EOF [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: ups.conf" + + # HACK: Avoid empty ups.status that may be present in example docs + # FIXME: Might we actually want that value (un-)set for tests?.. + for F in "$NUT_CONFPATH/"*.dev "$NUT_CONFPATH/"*.seq ; do + sed -e 's,^ups.status: *$,ups.status: OL BOOST,' -i "$F" + grep -E '^ups.status:' "$F" >/dev/null || { echo "ups.status: OL BOOST" >> "$F"; } + done fi } From 089267e15250c12149787224442e4e69f3a4b033 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 09:15:46 +0000 Subject: [PATCH 574/700] include/str.h, common/str.c: add str_ends_with() --- common/str.c | 13 +++++++++++++ include/str.h | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/common/str.c b/common/str.c index a23d93f78c..295f450804 100644 --- a/common/str.c +++ b/common/str.c @@ -614,3 +614,16 @@ int str_to_double_strict(const char *string, double *number, const int base) return 1; } + +int str_ends_with(const char *s, const char *suff) { + size_t slen; + size_t sufflen; + + if (!s) return 0; /* null string does not end with anything */ + if (!suff) return 1; /* null suffix tails anything */ + + slen = strlen(s); + sufflen = strlen(suff); + + return (slen >= sufflen) && (!memcmp(s + slen - sufflen, suff, sufflen)); +} diff --git a/include/str.h b/include/str.h index 92edbda257..500c0785a4 100644 --- a/include/str.h +++ b/include/str.h @@ -129,6 +129,10 @@ int str_to_ulong_strict(const char *string, unsigned long *number, const int bas int str_to_double(const char *string, double *number, const int base); int str_to_double_strict(const char *string, double *number, const int base); +/* Return non-zero if string s ends exactly with suff + * Note: s=NULL always fails the test; otherwise suff=NULL always matches + */ +int str_ends_with(const char *s, const char *suff); #ifdef __cplusplus /* *INDENT-OFF* */ } From 57a99d9baec79d62180a2f559d16fea081564805 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 09:19:26 +0000 Subject: [PATCH 575/700] drivers/dummy-ups.c: refactor "mode" from random defines to an enum --- drivers/dummy-ups.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index 06b1c53e23..39a6ab62d2 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -55,12 +55,23 @@ upsdrv_info_t upsdrv_info = { NULL } }; -#define MODE_NONE 0 -#define MODE_DUMMY 1 /* use the embedded defintion or a definition file */ -#define MODE_REPEATER 2 /* use libupsclient to repeat an UPS */ -#define MODE_META 3 /* consolidate data from several UPSs (TBS) */ +enum drivermode { + MODE_NONE = 0, -static int mode = MODE_NONE; + /* use the embedded defintion or a definition file, parsed in a + * loop again and again (often with TIMER lines to delay changes) + */ + MODE_DUMMY_LOOP, + + /* use libupsclient to repeat another UPS */ + MODE_REPEATER, + + /* consolidate data from several UPSs (TBS) */ + MODE_META +}; +typedef enum drivermode drivermode_t; + +static drivermode_t mode = MODE_NONE; /* parseconf context, for dummy mode using a file */ static PCONF_CTX_t *ctx = NULL; @@ -90,7 +101,7 @@ void upsdrv_initinfo(void) switch (mode) { - case MODE_DUMMY: + case MODE_DUMMY_LOOP: /* Initialise basic essential variables */ for ( item = nut_data ; item->info_type != NULL ; item++ ) { @@ -114,6 +125,7 @@ void upsdrv_initinfo(void) dstate_dataok(); break; + case MODE_META: case MODE_REPEATER: /* Obtain the target name */ @@ -142,6 +154,7 @@ void upsdrv_initinfo(void) } /* FIXME: commands and settable variable! */ break; + case MODE_NONE: default: fatalx(EXIT_FAILURE, "no suitable definition found!"); @@ -159,11 +172,12 @@ void upsdrv_updateinfo(void) switch (mode) { - case MODE_DUMMY: + case MODE_DUMMY_LOOP: /* Now get user's defined variables */ if (parse_data_file(upsfd) >= 0) dstate_dataok(); break; + case MODE_META: case MODE_REPEATER: if (upsclient_update_vars() > 0) @@ -184,6 +198,7 @@ void upsdrv_updateinfo(void) } } break; + case MODE_NONE: default: break; @@ -236,7 +251,7 @@ void upsdrv_initups(void) else { upsdebugx(1, "Dummy (simulation) mode"); - mode = MODE_DUMMY; + mode = MODE_DUMMY_LOOP; dstate_setinfo("driver.parameter.mode", "dummy"); } } From c887590023b81705aced3f578c24f96c755d2117 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 13:56:17 +0000 Subject: [PATCH 576/700] docs/man/dummy-ups.txt: small rewording --- docs/man/dummy-ups.txt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/man/dummy-ups.txt b/docs/man/dummy-ups.txt index 992bb97a76..9aa431277d 100644 --- a/docs/man/dummy-ups.txt +++ b/docs/man/dummy-ups.txt @@ -41,6 +41,12 @@ the UPS. This arrangement can also help with networked UPSes, whose network management cards can be overwhelmed with a farm of servers directly polling SNMP or other protocols every few seconds. +//////////////////////////////////////// +Future intention: Meta mode to aggregate several drivers as one device +e.g. to represent same UPS with Serial + USB + SNMP links, and/or cover +an SNMP UPS that supports different data in different MIBs. +//////////////////////////////////////// + IMPLEMENTATION -------------- @@ -68,7 +74,7 @@ only to modify values of these variables), and has the same format as an linkman:upsc[8] dump (`: `). So you can easily create definition files from an existing UPS using `upsc > file.dev`. -Note that the Network UPS project provides a +Note that the Network UPS project provides an extensive link:https://networkupstools.org/ddl/index.html[DDL (Devices Dumps Library)] with files which can be used for modelling real devices. Entries for the DDL library are best prepared with the @@ -81,8 +87,8 @@ available: `device.*`, `driver.*`, `ups.mfr`, `ups.model`, `ups.status` as filled by the driver itself. Some sample definition files are available in the `data` directory of the -NUT source tree, and generally in the sysconfig directory of your system -distribution. +NUT source tree, and generally in the sysconfig or share directory of your +system distribution. Since *dummy-ups* will loop on reading this file, you can dynamically modify it with some external process to "interact" with the driver. This will avoid From 4a7741f65fa5c2c00db127e1e283ebff59305276 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 13:55:34 +0000 Subject: [PATCH 577/700] dummy-ups: separate MODE_DUMMY_ONCE from MODE_DUMMY_LOOP Closes: #1385 --- NEWS | 4 ++ UPGRADING | 10 +++++ docs/man/dummy-ups.txt | 53 +++++++++++++++++++--- drivers/dummy-ups.c | 100 ++++++++++++++++++++++++++++++++++++++--- 4 files changed, 155 insertions(+), 12 deletions(-) diff --git a/NEWS b/NEWS index 71a267eddc..4b5e5f099b 100644 --- a/NEWS +++ b/NEWS @@ -302,6 +302,10 @@ refer to this change set (too long in the making) as NUT 2.7.5. - Add support for extra parameter for instant commands, both in library and in upscmd + - dummy-ups can now specify `mode` as a driver argument, and separates the + notion of `dummy-once` (new default for `*.dev` files) vs. `dummy-loop` + (legacy default for `*.seq` and others) [issue #1385] + - new protocol variables: * `input.phase.shift` * `outlet.N.name` diff --git a/UPGRADING b/UPGRADING index c04c2a54b8..5780608a6a 100644 --- a/UPGRADING +++ b/UPGRADING @@ -88,6 +88,16 @@ Changes from 2.7.4 to 2.8.0 search code in common/common.c. Please file an issue if this does not work with your platform. +- dummy-ups can now specify `mode` as a driver argument, and separates the + notion of `dummy-once` (new default for `*.dev` files) vs. `dummy-loop` + (legacy default for `*.seq` and others) [issue #1385] + + * Note this can break third-party test scripts which expected `*.dev` + files to work as a looping sequence with a `TIMER` keywords to change + values slowly; now such files should get processed to the end once. + Specify `mode=dummy-loop` driver option or rename the data file used + in the `port` option for legacy behavior. + - Python: scripts have been updated to work with Python 3 as well as 2. * PyNUT module (protocol binding) supports both Python generations. diff --git a/docs/man/dummy-ups.txt b/docs/man/dummy-ups.txt index 9aa431277d..5f7f417245 100644 --- a/docs/man/dummy-ups.txt +++ b/docs/man/dummy-ups.txt @@ -53,6 +53,9 @@ IMPLEMENTATION The `port` specification in `ups.conf` depends on the running mode, and allows the driver to select the right mode of operation. +Since NUT v2.8.0, the `mode` specification in `ups.conf` allows users to +override the mode of operation which would be otherwise guessed by the driver. + Dummy Mode ~~~~~~~~~~ @@ -61,6 +64,39 @@ In this context, `port` in the `ups.conf` block defines a file name for the path name. In the latter case the NUT sysconfig directory (i.e. `/etc/nut`, `/usr/local/ups/etc`, ...) is prepended. +Since NUT v2.8.0 two aspects of this mode are differentiated: + +* `dummy-once` reads the specified file once to the end (interrupting for + `TIMER` lines, etc.) and does not re-process it; + this allows use/test cases to `upsrw` variables in the driver instance + and they remain in memory until the driver is restarted; ++ +Since NUT v2.8.0 `dummy-once` is assigned by default to files with a `*.dev` + naming pattern. + +* `dummy-loop` reads the specified file again and again, with a short sleep + between the processing cycles; for sequence files using a `TIMER` keyword + (see below), or for use/test cases which modify file contents with external + means, this allows an impression of a device whose state changes over time. ++ +Before NUT v2.8.0 this was the only aspect, so a simple `dummy` mode value + maps to this behavior for backwards compatibility. ++ +Since NUT v2.8.0 `dummy-loop` is assigned by default to files with a `*.seq` + naming pattern, and `dummy` is assigned by default to files with other + naming patterns that the driver could not classify. + +[NOTE] +====== +Said defaulting based on filename pattern can break third-party +test scripts which earlier expected `*.dev` files to work as a +looping sequence with a `TIMER` keywords to change values slowly. +Now such files should get processed to the end once. + +Specify `mode=dummy-loop` driver option or rename the data file +used in the `port` option for legacy behavior. +====== + For instance: [dummy] @@ -90,10 +126,13 @@ Some sample definition files are available in the `data` directory of the NUT source tree, and generally in the sysconfig or share directory of your system distribution. -Since *dummy-ups* will loop on reading this file, you can dynamically modify -it with some external process to "interact" with the driver. This will avoid -message spam into your system log files, if you are using NUT default -configuration. +Since *dummy-ups* will usually loop on reading this file, you can dynamically +modify it with some external process to "interact" with the driver. +This will avoid message spam into your system log files, if you are +using NUT default configuration. + +NOTE: By default since NUT v2.8.0, it will not loop on files in `dummy-once` +mode, e.g. those with a `.dev` extension. You can also use the `TIMER ` instruction to create scheduled event sequences (such files are traditionally named with the `.seq` extension). @@ -107,9 +146,9 @@ between "OL", "OB" and "OB LB" every minute: ups.status: OB LB TIMER 60 -It is wise to end the script with a `TIMER` keyword. Otherwise *dummy-ups* -will directly go back to the beginning of the file and, in particular, forget -any values you could have just set with `upsrw`. +It is wise to end the script for `dummy-loop` mode with a `TIMER` keyword. +Otherwise `dummy-ups` will directly go back to the beginning of the file +and, in particular, forget any values you could have just set with `upsrw`. Note that to avoid CPU overload with an infinite loop, the driver "sleeps" a bit between file-reading cycles (currently this delay is hardcoded to one diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index 39a6ab62d2..8ccf3dc12d 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -60,9 +60,21 @@ enum drivermode { /* use the embedded defintion or a definition file, parsed in a * loop again and again (often with TIMER lines to delay changes) + * Default mode for files with *.seq naming pattern + * (legacy-compatibility note: and other patterns except *.dev) */ MODE_DUMMY_LOOP, + /* use the embedded defintion or a definition file, parsed once + * + * This allows to spin up a dummy device with initial readings + * and retain in memory whatever SET VAR was sent by clients later. + * This is also less stressful on system resources to run the dummy. + * + * Default mode for files with *.dev naming pattern + */ + MODE_DUMMY_ONCE, + /* use libupsclient to repeat another UPS */ MODE_REPEATER, @@ -101,6 +113,7 @@ void upsdrv_initinfo(void) switch (mode) { + case MODE_DUMMY_ONCE: case MODE_DUMMY_LOOP: /* Initialise basic essential variables */ for ( item = nut_data ; item->info_type != NULL ; item++ ) @@ -178,6 +191,18 @@ void upsdrv_updateinfo(void) dstate_dataok(); break; + case MODE_DUMMY_ONCE: + /* less stress on the sys */ + if (ctx == NULL) { + upsdebugx(2, "upsdrv_updateinfo: NO-OP: input file was already read once to the end"); + dstate_dataok(); + } else { + /* initial parsing interrupted by e.g. TIMER line */ + if (parse_data_file(upsfd) >= 0) + dstate_dataok(); + } + break; + case MODE_META: case MODE_REPEATER: if (upsclient_update_vars() > 0) @@ -236,13 +261,30 @@ void upsdrv_help(void) void upsdrv_makevartable(void) { + addvar(VAR_VALUE, "mode", "Specify mode instead of guessing it from port value (dummy = dummy-loop, dummy-once, repeater)"); /* meta */ } void upsdrv_initups(void) { + const char *val; + + val = dstate_getinfo("driver.parameter.mode"); + if (val) { + if (!strcmp(val, "dummy-loop") + && !strcmp(val, "dummy-once") + && !strcmp(val, "dummy") + && !strcmp(val, "repeater") + /* && !strcmp(val, "meta") */ + ) { + fatalx(EXIT_FAILURE, "Unsupported mode was specified: %s", val); + } + } + /* check the running mode... */ - if (strchr(device_path, '@')) - { + if ( (!val && strchr(device_path, '@')) + || (val && !strcmp(val, "repeater")) + /*|| (val && !strcmp(val, "meta")) */ + ) { upsdebugx(1, "Repeater mode"); mode = MODE_REPEATER; dstate_setinfo("driver.parameter.mode", "repeater"); @@ -250,9 +292,57 @@ void upsdrv_initups(void) } else { - upsdebugx(1, "Dummy (simulation) mode"); - mode = MODE_DUMMY_LOOP; - dstate_setinfo("driver.parameter.mode", "dummy"); + mode = MODE_NONE; + + if (val) { + if (!strcmp(val, "dummy-loop")) { + upsdebugx(2, "Dummy (simulation) mode looping infinitely was explicitly requested"); + mode = MODE_DUMMY_LOOP; + } else + if (!strcmp(val, "dummy-once")) { + upsdebugx(2, "Dummy (simulation) mode with data read once was explicitly requested"); + mode = MODE_DUMMY_ONCE; + } else + if (!strcmp(val, "dummy")) { + upsdebugx(2, "Dummy (simulation) mode default (looping infinitely) was explicitly requested"); + mode = MODE_DUMMY_LOOP; + } + } + + if (mode == MODE_NONE) { + if (str_ends_with(device_path, ".seq")) { + upsdebugx(2, "Dummy (simulation) mode with a sequence file name pattern (looping infinitely)"); + mode = MODE_DUMMY_LOOP; + } else if (str_ends_with(device_path, ".dev")) { + upsdebugx(2, "Dummy (simulation) mode with a device data dump file name pattern (read once)"); + mode = MODE_DUMMY_ONCE; + } + } + + /* Report decisions similar to those above, + * just a bit shorter and at another level */ + switch (mode) { + case MODE_DUMMY_ONCE: + upsdebugx(1, "Dummy (simulation) mode using data read once"); + dstate_setinfo("driver.parameter.mode", "dummy-once"); + break; + + case MODE_DUMMY_LOOP: + upsdebugx(1, "Dummy (simulation) mode looping infinitely"); + dstate_setinfo("driver.parameter.mode", "dummy-loop"); + break; + + default: + /* This was the only mode until MODE_DUMMY_LOOP + * got split from MODE_DUMMY_ONCE in NUT v2.8.0 + * so we keep the previously known mode string + * and it remains default when we are not sure + */ + upsdebugx(1, "Dummy (simulation) mode default (looping infinitely)"); + mode = MODE_DUMMY_LOOP; + dstate_setinfo("driver.parameter.mode", "dummy"); + break; + } } } From 13d9f96f71ca2be5a2ef0c079c30a48ad117ff8a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 15:30:06 +0000 Subject: [PATCH 578/700] dummy-ups: do re-read a "dummy-once" file, though only if it changes while the driver runs r the commit message for your changes. Lines starting --- NEWS | 4 ++-- UPGRADING | 6 ++++-- docs/man/dummy-ups.txt | 5 ++++- drivers/dummy-ups.c | 26 +++++++++++++++++++++++++- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 4b5e5f099b..895b2edfbf 100644 --- a/NEWS +++ b/NEWS @@ -303,8 +303,8 @@ refer to this change set (too long in the making) as NUT 2.7.5. in upscmd - dummy-ups can now specify `mode` as a driver argument, and separates the - notion of `dummy-once` (new default for `*.dev` files) vs. `dummy-loop` - (legacy default for `*.seq` and others) [issue #1385] + notion of `dummy-once` (new default for `*.dev` files that do not change) + vs. `dummy-loop` (legacy default for `*.seq` and others) [issue #1385] - new protocol variables: * `input.phase.shift` diff --git a/UPGRADING b/UPGRADING index 5780608a6a..1584e92f4e 100644 --- a/UPGRADING +++ b/UPGRADING @@ -89,14 +89,16 @@ Changes from 2.7.4 to 2.8.0 with your platform. - dummy-ups can now specify `mode` as a driver argument, and separates the - notion of `dummy-once` (new default for `*.dev` files) vs. `dummy-loop` - (legacy default for `*.seq` and others) [issue #1385] + notion of `dummy-once` (new default for `*.dev` files that do not change) + vs. `dummy-loop` (legacy default for `*.seq` and others) [issue #1385] * Note this can break third-party test scripts which expected `*.dev` files to work as a looping sequence with a `TIMER` keywords to change values slowly; now such files should get processed to the end once. Specify `mode=dummy-loop` driver option or rename the data file used in the `port` option for legacy behavior. + Use/Test-cases which modified such files content externally should + not be impacted. - Python: scripts have been updated to work with Python 3 as well as 2. diff --git a/docs/man/dummy-ups.txt b/docs/man/dummy-ups.txt index 5f7f417245..92560fa280 100644 --- a/docs/man/dummy-ups.txt +++ b/docs/man/dummy-ups.txt @@ -95,6 +95,9 @@ Now such files should get processed to the end once. Specify `mode=dummy-loop` driver option or rename the data file used in the `port` option for legacy behavior. + +Use/Test-cases which modified such files content externally should +not be impacted. ====== For instance: @@ -132,7 +135,7 @@ This will avoid message spam into your system log files, if you are using NUT default configuration. NOTE: By default since NUT v2.8.0, it will not loop on files in `dummy-once` -mode, e.g. those with a `.dev` extension. +mode, e.g. those with a `.dev` extension, unless their timestamp changes. You can also use the `TIMER ` instruction to create scheduled event sequences (such files are traditionally named with the `.seq` extension). diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index 8ccf3dc12d..f96f0d0d46 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include "main.h" @@ -88,6 +89,7 @@ static drivermode_t mode = MODE_NONE; /* parseconf context, for dummy mode using a file */ static PCONF_CTX_t *ctx = NULL; static time_t next_update = -1; +static struct stat datafile_stat; #define MAX_STRING_SIZE 128 @@ -193,7 +195,25 @@ void upsdrv_updateinfo(void) case MODE_DUMMY_ONCE: /* less stress on the sys */ - if (ctx == NULL) { + if (ctx == NULL && next_update == -1) { + struct stat fs; + + if (0 != fstat (upsfd, &fs) && 0 != stat (device_path, &fs)) { + upsdebugx(2, "Can't open %s currently", device_path); + /* retry ASAP until we get a file */ + memset(&datafile_stat, 0, sizeof(struct stat)); + next_update = 1; + } else { + if (datafile_stat.st_mtim.tv_sec != fs.st_mtim.tv_sec) { + upsdebugx(2, "upsdrv_updateinfo: input file was already read once to the end, but changed later - re-reading"); + /* updated file => retry ASAP */ + next_update = 1; + datafile_stat = fs; + } + } + } + + if (ctx == NULL && next_update == -1) { upsdebugx(2, "upsdrv_updateinfo: NO-OP: input file was already read once to the end"); dstate_dataok(); } else { @@ -343,6 +363,10 @@ void upsdrv_initups(void) dstate_setinfo("driver.parameter.mode", "dummy"); break; } + + if (0 != fstat (upsfd, &datafile_stat) && 0 != stat (device_path, &datafile_stat)) { + upsdebugx(2, "Can't open %s currently", device_path); + } } } From 94c6f39f2aa825fabe4865c3188a329e6ecb3d9b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 15:53:33 +0000 Subject: [PATCH 579/700] tests/NIT/nit.sh: extend with mode=... for dummy-ups [#1385] --- tests/NIT/nit.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 9c3edb6892..2eb079f5d2 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -270,6 +270,7 @@ EOF driver = dummy-ups desc = "Crash Dummy" port = dummy.dev + mode = dummy-loop EOF [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: ups.conf" @@ -286,11 +287,13 @@ EOF driver = dummy-ups desc = "Example ePDU data dump" port = epdu-managed.dev + mode = dummy-once EOF [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: ups.conf" # HACK: Avoid empty ups.status that may be present in example docs # FIXME: Might we actually want that value (un-)set for tests?.. + # TODO: Check if the problem was with dummy-ups looping? [#1385] for F in "$NUT_CONFPATH/"*.dev "$NUT_CONFPATH/"*.seq ; do sed -e 's,^ups.status: *$,ups.status: OL BOOST,' -i "$F" grep -E '^ups.status:' "$F" >/dev/null || { echo "ups.status: OL BOOST" >> "$F"; } From c713a66e16e4dae5d39c874dcfbffbf702cf1077 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 18:44:50 +0000 Subject: [PATCH 580/700] drivers/dummy-ups.c: cover exhaustive case(enum) with pragmas for diametral warnings --- drivers/dummy-ups.c | 78 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index f96f0d0d46..1e6cd28ed1 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -171,8 +171,33 @@ void upsdrv_initinfo(void) break; case MODE_NONE: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ default: fatalx(EXIT_FAILURE, "no suitable definition found!"); +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif } upsh.instcmd = instcmd; @@ -245,8 +270,33 @@ void upsdrv_updateinfo(void) break; case MODE_NONE: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ default: break; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif } } @@ -352,6 +402,28 @@ void upsdrv_initups(void) dstate_setinfo("driver.parameter.mode", "dummy-loop"); break; + case MODE_NONE: + case MODE_REPEATER: + case MODE_META: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ default: /* This was the only mode until MODE_DUMMY_LOOP * got split from MODE_DUMMY_ONCE in NUT v2.8.0 @@ -362,6 +434,12 @@ void upsdrv_initups(void) mode = MODE_DUMMY_LOOP; dstate_setinfo("driver.parameter.mode", "dummy"); break; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif } if (0 != fstat (upsfd, &datafile_stat) && 0 != stat (device_path, &datafile_stat)) { From 583c3c63a206800d388a53bcebbe5a117beef72c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 20:03:18 +0000 Subject: [PATCH 581/700] tests/NIT/nit.sh: "sed -i" requires an extension (may be empty, in BSD must be a separate CLI token) --- tests/NIT/nit.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 2eb079f5d2..a2fd868e00 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -295,7 +295,7 @@ EOF # FIXME: Might we actually want that value (un-)set for tests?.. # TODO: Check if the problem was with dummy-ups looping? [#1385] for F in "$NUT_CONFPATH/"*.dev "$NUT_CONFPATH/"*.seq ; do - sed -e 's,^ups.status: *$,ups.status: OL BOOST,' -i "$F" + sed -e 's,^ups.status: *$,ups.status: OL BOOST,' -i '' "$F" grep -E '^ups.status:' "$F" >/dev/null || { echo "ups.status: OL BOOST" >> "$F"; } done fi From f367203a67d5bc9b5ac5f1c10b4eac000fb14570 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 21:54:55 +0000 Subject: [PATCH 582/700] tests/NIT/nit.sh: "sed -i" requires an extension (may be empty, in BSD must be a separate CLI token, in others must be same token - not empty is ok for all) --- tests/NIT/nit.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index a2fd868e00..f611d6921c 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -295,7 +295,7 @@ EOF # FIXME: Might we actually want that value (un-)set for tests?.. # TODO: Check if the problem was with dummy-ups looping? [#1385] for F in "$NUT_CONFPATH/"*.dev "$NUT_CONFPATH/"*.seq ; do - sed -e 's,^ups.status: *$,ups.status: OL BOOST,' -i '' "$F" + sed -e 's,^ups.status: *$,ups.status: OL BOOST,' -i'.bak' "$F" grep -E '^ups.status:' "$F" >/dev/null || { echo "ups.status: OL BOOST" >> "$F"; } done fi From 52eed6773dad92277eb034af555100b3fc772c7d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 22:10:24 +0000 Subject: [PATCH 583/700] drivers/main.c: fix the handling of libtool-named binaries --- drivers/main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index 759a132541..408af0931a 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -519,13 +519,15 @@ void do_upsconf_args(char *confupsname, char *var, char *val) /* don't let the user shoot themselves in the foot */ if (!strcmp(var, "driver")) { - /* Accomodate for libtool wrapped developer iterations */ + /* Accomodate for libtool wrapped developer iterations + * running e.g. `drivers/.libs/lt-dummy-ups` filenames + */ char buf[PATH_MAX]; - if (snprintfcat(buf, sizeof(buf), "lt-%s", progname) < 0) + if (snprintfcat(buf, sizeof(buf), "lt-%s", val) < 0) buf[0] = '\0'; if (strcmp(val, progname) != 0 - && strcmp(val, buf) != 0 + && strcmp(buf, progname) != 0 ) { fatalx(EXIT_FAILURE, "Error: UPS [%s] is for driver %s, but I'm %s!\n", confupsname, val, progname); From f28f35e6f3b91b2080efc781fcda8c46d621bd2e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 22:22:26 +0000 Subject: [PATCH 584/700] tests/NIT/nit.sh: if "Error: Driver not connected" retry with UPSD started after drivers are running --- tests/NIT/nit.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index f611d6921c..5b4a9039b4 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -431,7 +431,18 @@ if shouldDebug ; then fi log_info "Query driver state from UPSD by UPSC after driver startup" -upsc dummy@localhost:$NUT_PORT || die "upsd does not respond" +upsc dummy@localhost:$NUT_PORT || { + # Should not get to this, except on very laggy systems maybe + log_error "Query failed, retrying with UPSD started after drivers" + + kill -15 $PID_UPSD + wait $PID_UPSD + upsd -F & + PID_UPSD="$!" + + sleep 5 + upsc dummy@localhost:$NUT_PORT || die "upsd does not respond" +} OUT="`upsc dummy@localhost:$NUT_PORT device.model`" || die "upsd does not respond: $OUT" if [ x"$OUT" != x"Dummy UPS" ] ; then From 89990bc139583435669ae29dddd490495e92cd76 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 22:37:05 +0000 Subject: [PATCH 585/700] drivers/main.c: better fix for handling of libtool-named binaries --- drivers/main.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index 408af0931a..f9938c766c 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -526,9 +526,17 @@ void do_upsconf_args(char *confupsname, char *var, char *val) if (snprintfcat(buf, sizeof(buf), "lt-%s", val) < 0) buf[0] = '\0'; - if (strcmp(val, progname) != 0 - && strcmp(buf, progname) != 0 - ) { + upsdebugx(6, "progname: check '%s' vs '%s' vs '%s'", progname, val, buf); + if (strcmp(buf, progname) == 0) { + upsdebugx(1, "Seems this driver binary %s is a libtool " + "wrapped build for driver %s", progname, val); + /* progname points to xbasename(argv[0]) in-place; + * roll the pointer forward a bit, we know we can: + */ + progname = progname + strlen("lt-"); + } + + if (strcmp(val, progname) != 0) { fatalx(EXIT_FAILURE, "Error: UPS [%s] is for driver %s, but I'm %s!\n", confupsname, val, progname); } From e6f9e80271e91e849736466f554c37a7b1fd1215 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 22:40:43 +0000 Subject: [PATCH 586/700] tests/NIT/nit.sh: name the "dummy.seq" so it loops by default --- tests/NIT/nit.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 5b4a9039b4..555e2ef335 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -257,20 +257,20 @@ generatecfg_ups_trivial() { generatecfg_ups_dummy() { generatecfg_ups_trivial - cat > "$NUT_CONFPATH/dummy.dev" << EOF + cat > "$NUT_CONFPATH/dummy.seq" << EOF ups.status: OB TIMER 5 ups.status: OL TIMER 5 EOF - [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: dummy.dev" + [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: dummy.seq" cat >> "$NUT_CONFPATH/ups.conf" << EOF [dummy] driver = dummy-ups desc = "Crash Dummy" - port = dummy.dev - mode = dummy-loop + port = dummy.seq + #mode = dummy-loop EOF [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: ups.conf" From f5890ba6b10fcd3a230dc6749d8175e30c3dade2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Thu, 21 Apr 2022 22:57:37 +0000 Subject: [PATCH 587/700] drivers/dummy-ups.c: fix dummy-once mode for relative path in port --- drivers/dummy-ups.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index 1e6cd28ed1..c75cc1e479 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -222,9 +222,15 @@ void upsdrv_updateinfo(void) /* less stress on the sys */ if (ctx == NULL && next_update == -1) { struct stat fs; + char fn[SMALLBUF]; - if (0 != fstat (upsfd, &fs) && 0 != stat (device_path, &fs)) { - upsdebugx(2, "Can't open %s currently", device_path); + if (device_path[0] == '/') + snprintf(fn, sizeof(fn), "%s", device_path); + else + snprintf(fn, sizeof(fn), "%s/%s", confpath(), device_path); + + if (0 != fstat (upsfd, &fs) && 0 != stat (fn, &fs)) { + upsdebugx(2, "Can't open %s currently", fn); /* retry ASAP until we get a file */ memset(&datafile_stat, 0, sizeof(struct stat)); next_update = 1; @@ -362,6 +368,7 @@ void upsdrv_initups(void) } else { + char fn[SMALLBUF]; mode = MODE_NONE; if (val) { @@ -442,6 +449,11 @@ void upsdrv_initups(void) #endif } + if (device_path[0] == '/') + snprintf(fn, sizeof(fn), "%s", device_path); + else + snprintf(fn, sizeof(fn), "%s/%s", confpath(), device_path); + if (0 != fstat (upsfd, &datafile_stat) && 0 != stat (device_path, &datafile_stat)) { upsdebugx(2, "Can't open %s currently", device_path); } From c75aab52db30a0ca98c83d6a75489f2d750a11f3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 00:04:24 +0000 Subject: [PATCH 588/700] scripts/python/Makefile.am: clean *.pyc and __pycache__/ if present --- scripts/python/Makefile.am | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/python/Makefile.am b/scripts/python/Makefile.am index cd1fe99c77..313ace8c58 100644 --- a/scripts/python/Makefile.am +++ b/scripts/python/Makefile.am @@ -35,3 +35,6 @@ EXTRA_DIST += $(EXTRA_DIST_PY2GTK2) EXTRA_DIST += $(EXTRA_DIST_PY3QT5) MAINTAINERCLEANFILES = Makefile.in .dirstamp + +clean-local: + rm -rf *.pyc __pycache__ */*.pyc */__pycache__ */*/*.pyc */*/__pycache__ From 9ea5b45399f62c9d71b5c9136f310d7931e9933d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 00:28:17 +0000 Subject: [PATCH 589/700] tests/cpputest-client.cpp: rename "s1" original value reading --- tests/cpputest-client.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/cpputest-client.cpp b/tests/cpputest-client.cpp index 6168de09ad..c2b2cf4ca3 100644 --- a/tests/cpputest-client.cpp +++ b/tests/cpputest-client.cpp @@ -245,14 +245,14 @@ void NutActiveClientTest::test_auth_user() { TrackingResult tres; TrackingID tid; std::string nutVar = "ups.status"; /* Has a risk of flip-flop with NIT dummy setup */ - std::string s = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; - std::string sTest = s + "-test"; + std::string s1 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + std::string sTest = s1 + "-test"; std::cerr << "[D] Got initial device '" << NUT_SETVAR_DEVICE - << "' variable '" << nutVar << "' value: " << s << std::endl; + << "' variable '" << nutVar << "' value: " << s1 << std::endl; CPPUNIT_ASSERT_MESSAGE( "Did not expect empty value here", - !s.empty()); + !s1.empty()); tid = c.setDeviceVariable(NUT_SETVAR_DEVICE, nutVar, sTest); while ( (tres = c.getTrackingResult(tid)) == PENDING) { @@ -267,7 +267,7 @@ void NutActiveClientTest::test_auth_user() { std::string s2 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; /* Fix it back */ - tid = c.setDeviceVariable(NUT_SETVAR_DEVICE, nutVar, s); + tid = c.setDeviceVariable(NUT_SETVAR_DEVICE, nutVar, s1); while ( (tres = c.getTrackingResult(tid)) == PENDING) { usleep(100); } @@ -278,16 +278,16 @@ void NutActiveClientTest::test_auth_user() { } std::string s3 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; - if (s3 != s) { + if (s3 != s1) { std::cerr << "[D] Final device variable value '" << s3 - << "' differs from original '" << s + << "' differs from original '" << s1 << "'" << std::endl; noException = false; } - if (s2 == s) { + if (s2 == s1) { std::cerr << "[D] Tweaked device variable value '" << s2 - << "' does not differ from original '" << s + << "' does not differ from original '" << s1 << "'" << std::endl; noException = false; } From 407385f82dc598b3e167d020c957c4757232dfb3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 00:28:41 +0000 Subject: [PATCH 590/700] tests/cpputest-client.cpp: trace values read back after setting --- tests/cpputest-client.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/cpputest-client.cpp b/tests/cpputest-client.cpp index c2b2cf4ca3..95295f5885 100644 --- a/tests/cpputest-client.cpp +++ b/tests/cpputest-client.cpp @@ -265,6 +265,7 @@ void NutActiveClientTest::test_auth_user() { } /* Check what we got after set */ std::string s2 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + std::cerr << "[D] Read back: " << s2 << std::endl; /* Fix it back */ tid = c.setDeviceVariable(NUT_SETVAR_DEVICE, nutVar, s1); @@ -277,6 +278,7 @@ void NutActiveClientTest::test_auth_user() { noException = false; } std::string s3 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + std::cerr << "[D] Read back: " << s3 << std::endl; if (s3 != s1) { std::cerr << "[D] Final device variable value '" << s3 From ae94e3ddb74bc66299bc8241151f1a1c0603d006 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 00:36:35 +0000 Subject: [PATCH 591/700] tests/NIT/nit.sh: allow to tweak NUT_DEBUG_MIN --- tests/NIT/nit.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 555e2ef335..8d04c758e9 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -14,6 +14,7 @@ # Caller can export envvars to impact the script behavior, e.g.: # DEBUG=true to print debug messages, running processes, etc. # DEBUG_SLEEP=60 to sleep after tests, with driver+server running +# NUT_DEBUG_MIN=3 to set (minimum) debug level for drivers, upsd... # NUT_PORT=12345 custom port for upsd to listen and clients to query # # Design note: written with dumbed-down POSIX shell syntax, to @@ -161,6 +162,10 @@ EOF echo "LISTEN $LH $NUT_PORT" >> "$NUT_CONFPATH/upsd.conf" fi done + + if [ -n "${NUT_DEBUG_MIN-}" ] ; then + echo "DEBUG_MIN ${NUT_DEBUG_MIN}" >> "$NUT_CONFPATH/upsd.conf" || exit + fi } generatecfg_upsd_nodev() { @@ -214,6 +219,10 @@ generatecfg_upsmon_trivial() { # Populate the configs for the run ( echo 'MINSUPPLIES 0' > "$NUT_CONFPATH/upsmon.conf" || exit echo 'SHUTDOWNCMD "echo TESTING_DUMMY_SHUTDOWN_NOW"' >> "$NUT_CONFPATH/upsmon.conf" || exit + + if [ -n "${NUT_DEBUG_MIN-}" ] ; then + echo "DEBUG_MIN ${NUT_DEBUG_MIN}" >> "$NUT_CONFPATH/upsmon.conf" || exit + fi ) || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" chmod 640 "$NUT_CONFPATH/upsmon.conf" } @@ -250,8 +259,12 @@ generatecfg_ups_trivial() { if [ x"${TOP_BUILDDIR}" != x ]; then echo "driverpath = '${TOP_BUILDDIR}/drivers'" >> "$NUT_CONFPATH/ups.conf" || exit fi + if [ -n "${NUT_DEBUG_MIN-}" ] ; then + echo "debug_min = ${NUT_DEBUG_MIN}" >> "$NUT_CONFPATH/ups.conf" || exit + fi ) || die "Failed to populate temporary FS structure for the NIT: ups.conf" chmod 640 "$NUT_CONFPATH/ups.conf" + } generatecfg_ups_dummy() { From 9c680a2cbf1f4eed3a1417c2595479d8480c5441 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 11:13:30 +0000 Subject: [PATCH 592/700] drivers/dstate.c: whitespace fix --- drivers/dstate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index dd0eeac05f..88fb7db42f 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -281,9 +281,9 @@ static int send_to_one(conn_t *conn, const char *fmt, ...) __func__, buflen, conn->fd, buf); */ - ret = write(conn->fd, buf, buflen); + ret = write(conn->fd, buf, buflen); - if (ret < 0) { + if (ret < 0) { /* Hacky bugfix: throttle down for upsd to read that */ upsdebugx(1, "%s: had to throttle down to retry " "writing %zd bytes to socket %d " From 672d265abec426f39b8c66621e3227c6d5625518 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 11:13:56 +0000 Subject: [PATCH 593/700] drivers/dstate.c: log start of processing in sock_arg() --- drivers/dstate.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/dstate.c b/drivers/dstate.c index 88fb7db42f..5ba27f35f7 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -481,6 +481,9 @@ static void send_tracking(conn_t *conn, const char *id, int value) static int sock_arg(conn_t *conn, size_t numarg, char **arg) { + upsdebugx(6, "Driver on %s is now handling %s with %zu args", + sockfn, numarg ? arg[0] : "", numarg); + if (numarg < 1) { return 0; } From 2f54ed4f7cbae7cb690e66f96a525943643d93ee Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 11:27:32 +0000 Subject: [PATCH 594/700] tests/NIT/Makefile.am: extend with check-NIT-devel for iterating --- Makefile.am | 2 +- tests/Makefile.am | 2 +- tests/NIT/Makefile.am | 9 ++++++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index eeccdaa0d8..2ea7d7fb4f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -118,7 +118,7 @@ all-docs check-docs \ man all-man man-man check-man man-html all-html: cd $(builddir)/docs && $(MAKE) $@ -check-NIT: +check-NIT check-NIT-devel: cd $(builddir)/tests/NIT && $(MAKE) $@ # This target adds syntax-checking for committed shell script files, diff --git a/tests/Makefile.am b/tests/Makefile.am index dda545f8dc..e9cfd70770 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -15,7 +15,7 @@ AM_CXXFLAGS = -I$(top_srcdir)/include check_PROGRAMS = $(TESTS) # NUT Integration Testing suite -check-NIT: +check-NIT check-NIT-devel: cd "$(builddir)/NIT" && $(MAKE) $@ nutlogtest_SOURCES = nutlogtest.c diff --git a/tests/NIT/Makefile.am b/tests/NIT/Makefile.am index 2a25eb952b..c1fc4c3183 100644 --- a/tests/NIT/Makefile.am +++ b/tests/NIT/Makefile.am @@ -5,9 +5,16 @@ check: check-NIT # Run in builddir, use script from srcdir # Avoid running "$<" - not all make implementations handle that check-NIT: $(abs_srcdir)/nit.sh - @cd .. && ( $(MAKE) -s cppnit || echo "OPTIONAL C++ test client test will be skipped" ) "$(abs_srcdir)/nit.sh" +# Make sure pre-requisites for NIT are fresh as we iterate +check-NIT-devel: $(abs_srcdir)/nit.sh + @cd .. && ( $(MAKE) -s cppnit || echo "OPTIONAL C++ test client test will be skipped" ) + @cd "$(top_builddir)/clients" && $(MAKE) -s upsc upsrw upsmon + @cd "$(top_builddir)/server" && $(MAKE) -s upsd + @cd "$(top_builddir)/drivers" && $(MAKE) -s dummy-ups + @$(MAKE) check-NIT + MAINTAINERCLEANFILES = Makefile.in .dirstamp clean-local: From 6150de35b1b38e918ce82898b3c43be5b8444ce6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 11:17:51 +0000 Subject: [PATCH 595/700] tests/NIT/nit.sh: refactor stop_daemons() --- tests/NIT/nit.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 8d04c758e9..317d0c73a9 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -120,7 +120,14 @@ rm -rf "$BUILDDIR/tmp" || true mkdir -p "$BUILDDIR/tmp/etc" "$BUILDDIR/tmp/run" && chmod 750 "$BUILDDIR/tmp/run" \ || die "Failed to create temporary FS structure for the NIT" -trap 'RES=$?; if [ -n "$PID_UPSD$PID_DUMMYUPS$PID_DUMMYUPS1$PID_DUMMYUPS2" ] ; then kill -15 $PID_UPSD $PID_DUMMYUPS $PID_DUMMYUPS1 $PID_DUMMYUPS2 ; fi; exit $RES;' 0 1 2 3 15 +stop_daemons() { + if [ -n "$PID_UPSD$PID_DUMMYUPS$PID_DUMMYUPS1$PID_DUMMYUPS2" ] ; then + log_info "Stopping test daemons" + kill -15 $PID_UPSD $PID_DUMMYUPS $PID_DUMMYUPS1 $PID_DUMMYUPS2 2>/dev/null + fi +} + +trap 'RES=$?; stop_daemons; exit $RES;' 0 1 2 3 15 NUT_STATEPATH="$BUILDDIR/tmp/run" NUT_ALTPIDPATH="$BUILDDIR/tmp/run" From d86c94af0065bd97257adbf75e6c98fd1a3b5ab6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 11:18:38 +0000 Subject: [PATCH 596/700] tests/NIT/nit.sh: refactor test cases into smaller routines and groups, and allow to select NIT_CASE optionally --- tests/NIT/nit.sh | 475 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 325 insertions(+), 150 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 317d0c73a9..1915033aee 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -324,187 +324,276 @@ EOF ##################################################### +isPidAlive() { + [ -n "$1" ] && [ "$1" -gt 0 ] || return + [ -d "/proc/$1" ] || kill -0 "$1" 2>/dev/null +} + FAILED=0 PASSED=0 -log_separator -log_info "Test UPSD without configs at all" -upsd -F -if [ "$?" = 0 ]; then - log_error "upsd should fail without configs" - FAILED="`expr $FAILED + 1`" -else - log_info "OK, upsd failed to start in wrong conditions" - PASSED="`expr $PASSED + 1`" -fi - -log_separator -log_info "Test UPSD without driver config file" -generatecfg_upsd_trivial -upsd -F -if [ "$?" = 0 ]; then - log_error "upsd should fail without driver config file" - FAILED="`expr $FAILED + 1`" -else - log_info "OK, upsd failed to start in wrong conditions" - PASSED="`expr $PASSED + 1`" -fi - -log_separator -log_info "Test UPSD without drivers defined in config file" -generatecfg_upsd_trivial -generatecfg_ups_trivial -upsd -F -if [ "$?" = 0 ]; then - log_error "upsd should fail without drivers defined in config file" - FAILED="`expr $FAILED + 1`" -else - log_info "OK, upsd failed to start in wrong conditions" - PASSED="`expr $PASSED + 1`" -fi +testcase_upsd_no_configs_at_all() { + log_separator + log_info "Test UPSD without configs at all" + upsd -F + if [ "$?" = 0 ]; then + log_error "upsd should fail without configs" + FAILED="`expr $FAILED + 1`" + else + log_info "OK, upsd failed to start in wrong conditions" + PASSED="`expr $PASSED + 1`" + fi +} -log_separator -log_info "Test UPSD allowed to run without driver configs" -generatecfg_upsd_nodev -generatecfg_upsdusers_trivial -generatecfg_ups_trivial -upsd -F & -PID_UPSD="$!" -sleep 2 -if [ -d "/proc/$PID_UPSD" ] || kill -0 "$PID_UPSD"; then - log_info "OK, upsd is running" - PASSED="`expr $PASSED + 1`" +testcase_upsd_no_configs_driver_file() { + log_separator + log_info "Test UPSD without driver config file" + generatecfg_upsd_trivial + upsd -F + if [ "$?" = 0 ]; then + log_error "upsd should fail without driver config file" + FAILED="`expr $FAILED + 1`" + else + log_info "OK, upsd failed to start in wrong conditions" + PASSED="`expr $PASSED + 1`" + fi +} +testcase_upsd_no_configs_in_driver_file() { log_separator - log_info "Test that UPSD responds to UPSC" - OUT="`upsc -l localhost:$NUT_PORT`" || die "upsd does not respond: $OUT" - if [ -n "$OUT" ] ; then - log_error "got reply for upsc listing when none was expected: $OUT" + log_info "Test UPSD without drivers defined in config file" + generatecfg_upsd_trivial + generatecfg_ups_trivial + upsd -F + if [ "$?" = 0 ]; then + log_error "upsd should fail without drivers defined in config file" FAILED="`expr $FAILED + 1`" else - log_info "OK, empty response as expected" + log_info "OK, upsd failed to start in wrong conditions" PASSED="`expr $PASSED + 1`" fi -else - log_error "upsd was expected to be running although no devices are defined" - FAILED="`expr $FAILED + 1`" -fi -kill -15 $PID_UPSD -wait $PID_UPSD +} -log_separator -log_info "Test starting UPSD and a driver" -generatecfg_upsd_nodev -generatecfg_upsdusers_trivial -generatecfg_ups_dummy +testcase_upsd_allow_no_device() { + log_separator + log_info "Test UPSD allowed to run without driver configs" + generatecfg_upsd_nodev + generatecfg_upsdusers_trivial + generatecfg_ups_trivial + upsd -F & + PID_UPSD="$!" + sleep 2 + if isPidAlive "$PID_UPSD"; then + log_info "OK, upsd is running" + PASSED="`expr $PASSED + 1`" + + log_separator + log_info "Test that UPSD responds to UPSC" + OUT="`upsc -l localhost:$NUT_PORT`" || die "upsd does not respond: $OUT" + if [ -n "$OUT" ] ; then + log_error "got reply for upsc listing when none was expected: $OUT" + FAILED="`expr $FAILED + 1`" + else + log_info "OK, empty response as expected" + PASSED="`expr $PASSED + 1`" + fi + else + log_error "upsd was expected to be running although no devices are defined" + FAILED="`expr $FAILED + 1`" + fi + kill -15 $PID_UPSD + wait $PID_UPSD +} -log_info "Starting UPSD alone first" -upsd -F & -PID_UPSD="$!" -sleep 5 +testgroup_upsd_invalid_configs() { + testcase_upsd_no_configs_at_all + testcase_upsd_no_configs_driver_file + testcase_upsd_no_configs_in_driver_file +} -EXPECTED_UPSLIST='dummy' -if [ x"${TOP_SRCDIR}" != x ]; then - EXPECTED_UPSLIST="$EXPECTED_UPSLIST -UPS1 -UPS2" -fi +testgroup_upsd_questionable_configs() { + testcase_upsd_allow_no_device +} -log_info "Query listing from UPSD by UPSC (driver not running yet)" -OUT="`upsc -l localhost:$NUT_PORT`" || die "upsd does not respond: $OUT" -if [ x"$OUT" != x"$EXPECTED_UPSLIST" ] ; then - log_error "got this reply for upsc listing when '$EXPECTED_UPSLIST' was expected: $OUT" - FAILED="`expr $FAILED + 1`" -else - PASSED="`expr $PASSED + 1`" -fi +######################################################### +### Tests in a common sandbox with driver(s) + server ### +######################################################### -log_info "Query driver state from UPSD by UPSC (driver not running yet)" -OUT="`upsc dummy@localhost:$NUT_PORT 2>&1`" && { - log_error "upsc was supposed to answer with error exit code: $OUT" - FAILED="`expr $FAILED + 1`" +SANDBOX_CONFIG_GENERATED=false +sandbox_generate_configs() { + if $SANDBOX_CONFIG_GENERATED ; then return ; fi + + log_info "Generating configs for sandbox" + generatecfg_upsd_nodev + generatecfg_upsdusers_trivial + generatecfg_ups_dummy + SANDBOX_CONFIG_GENERATED=true } -if [ x"$OUT" != x'Error: Driver not connected' ] ; then - log_error "got reply for upsc query when 'Error: Driver not connected' was expected: $OUT" - FAILED="`expr $FAILED + 1`" -else - PASSED="`expr $PASSED + 1`" -fi -log_info "Starting dummy-ups driver(s) now" -#upsdrvctl -F start dummy & -dummy-ups -a dummy -F & -PID_DUMMYUPS="$!" +sandbox_forget_configs() { + SANDBOX_CONFIG_GENERATED=false + if [ -z "${DEBUG_SLEEP-}" ] ; then + stop_daemons + fi +} -if [ x"${TOP_SRCDIR}" != x ]; then - dummy-ups -a UPS1 -F & - PID_DUMMYUPS1="$!" +sandbox_start_upsd() { + if isPidAlive "$PID_UPSD" ; then + return 0 + fi - dummy-ups -a UPS2 -F & - PID_DUMMYUPS2="$!" -fi + sandbox_generate_configs -sleep 5 + log_info "Starting UPSD for sandbox" + upsd -F & + PID_UPSD="$!" + sleep 5 +} -if shouldDebug ; then - (ps -ef || ps -xawwu) 2>/dev/null | grep -E '(ups|nut|dummy)' || true -fi +sandbox_start_drivers() { + if isPidAlive "$PID_DUMMYUPS" \ + && { [ x"${TOP_SRCDIR}" != x ] && isPidAlive "$PID_DUMMYUPS1" && isPidAlive "$PID_DUMMYUPS2" \ + || [ x"${TOP_SRCDIR}" = x ] ; } \ + ; then + # All drivers expected for this environment are already running + return 0 + fi -log_info "Query driver state from UPSD by UPSC after driver startup" -upsc dummy@localhost:$NUT_PORT || { - # Should not get to this, except on very laggy systems maybe - log_error "Query failed, retrying with UPSD started after drivers" + sandbox_generate_configs - kill -15 $PID_UPSD + log_info "Starting dummy-ups driver(s) for sandbox" + #upsdrvctl -F start dummy & + dummy-ups -a dummy -F & + PID_DUMMYUPS="$!" + + if [ x"${TOP_SRCDIR}" != x ]; then + dummy-ups -a UPS1 -F & + PID_DUMMYUPS1="$!" + + dummy-ups -a UPS2 -F & + PID_DUMMYUPS2="$!" + fi + + sleep 5 + + if shouldDebug ; then + (ps -ef || ps -xawwu) 2>/dev/null | grep -E '(ups|nut|dummy)' || true + fi +} + +testcase_sandbox_start_upsd_alone() { + log_separator + log_info "Test starting UPSD but not a driver before it" + sandbox_start_upsd + + EXPECTED_UPSLIST='dummy' + if [ x"${TOP_SRCDIR}" != x ]; then + EXPECTED_UPSLIST="$EXPECTED_UPSLIST +UPS1 +UPS2" + fi + + log_info "Query listing from UPSD by UPSC (driver not running yet)" + OUT="`upsc -l localhost:$NUT_PORT`" || die "upsd does not respond: $OUT" + if [ x"$OUT" != x"$EXPECTED_UPSLIST" ] ; then + log_error "got this reply for upsc listing when '$EXPECTED_UPSLIST' was expected: $OUT" + FAILED="`expr $FAILED + 1`" + else + PASSED="`expr $PASSED + 1`" + fi + + log_info "Query driver state from UPSD by UPSC (driver not running yet)" + OUT="`upsc dummy@localhost:$NUT_PORT 2>&1`" && { + log_error "upsc was supposed to answer with error exit code: $OUT" + FAILED="`expr $FAILED + 1`" + } + if [ x"$OUT" != x'Error: Driver not connected' ] ; then + log_error "got reply for upsc query when 'Error: Driver not connected' was expected: $OUT" + FAILED="`expr $FAILED + 1`" + else + PASSED="`expr $PASSED + 1`" + fi +} + +testcase_sandbox_start_upsd_after_drivers() { + # Historically this is a fallback from testcase_sandbox_start_drivers_after_upsd + kill -15 $PID_UPSD 2>/dev/null wait $PID_UPSD upsd -F & PID_UPSD="$!" + sandbox_start_drivers + sandbox_start_upsd + sleep 5 upsc dummy@localhost:$NUT_PORT || die "upsd does not respond" } -OUT="`upsc dummy@localhost:$NUT_PORT device.model`" || die "upsd does not respond: $OUT" -if [ x"$OUT" != x"Dummy UPS" ] ; then - log_error "got this reply for upsc query when 'Dummy UPS' was expected: $OUT" - FAILED="`expr $FAILED + 1`" -else - PASSED="`expr $PASSED + 1`" -fi +testcase_sandbox_start_drivers_after_upsd() { + #sandbox_start_upsd + testcase_sandbox_start_upsd_alone + sandbox_start_drivers + + log_info "Query driver state from UPSD by UPSC after driver startup" + upsc dummy@localhost:$NUT_PORT || { + # Should not get to this, except on very laggy systems maybe + log_error "Query failed, retrying with UPSD started after drivers" + testcase_start_upsd_after_drivers + } +} -log_info "Query driver state from UPSD by UPSC for bogus info" -OUT="`upsc dummy@localhost:$NUT_PORT ups.bogus.value 2>&1`" && { - log_error "upsc was supposed to answer with error exit code: $OUT" - FAILED="`expr $FAILED + 1`" +testcase_sandbox_upsc_query_model() { + OUT="`upsc dummy@localhost:$NUT_PORT device.model`" || die "upsd does not respond: $OUT" + if [ x"$OUT" != x"Dummy UPS" ] ; then + log_error "got this reply for upsc query when 'Dummy UPS' was expected: $OUT" + FAILED="`expr $FAILED + 1`" + else + PASSED="`expr $PASSED + 1`" + fi } -if [ x"$OUT" != x'Error: Variable not supported by UPS' ] ; then - log_error "got reply for upsc query when 'Error: Variable not supported by UPS' was expected: $OUT" - FAILED="`expr $FAILED + 1`" -else - PASSED="`expr $PASSED + 1`" -fi -log_separator -log_info "Test that dummy-ups TIMER action changes the reported state" -# Driver is set up to flip ups.status every 5 sec, so check every 3 -OUT1="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT1" ; sleep 3 -OUT2="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT2" ; sleep 3 -OUT3="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT3" ; sleep 3 -if echo "$OUT1$OUT2$OUT3" | grep "OB" && echo "$OUT1$OUT2$OUT3" | grep "OL" ; then - log_info "OK, ups.status flips over time" - PASSED="`expr $PASSED + 1`" -else - log_error "ups.status did not flip over time" - FAILED="`expr $FAILED + 1`" -fi +testcase_sandbox_upsc_query_bogus() { + log_info "Query driver state from UPSD by UPSC for bogus info" + OUT="`upsc dummy@localhost:$NUT_PORT ups.bogus.value 2>&1`" && { + log_error "upsc was supposed to answer with error exit code: $OUT" + FAILED="`expr $FAILED + 1`" + } + if [ x"$OUT" != x'Error: Variable not supported by UPS' ] ; then + log_error "got reply for upsc query when 'Error: Variable not supported by UPS' was expected: $OUT" + FAILED="`expr $FAILED + 1`" + else + PASSED="`expr $PASSED + 1`" + fi +} -# We optionally make python module (if interpreter is found): -if [ x"${TOP_BUILDDIR}" != x ] \ -&& [ -x "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py" ] \ -; then - # That script says it expects data/evolution500.seq (as the UPS1 dummy) - # but the dummy data does not currently let issue the commands and - # setvars tested from python script. +testcase_sandbox_upsc_query_timer() { + log_separator + log_info "Test that dummy-ups TIMER action changes the reported state" + # Driver is set up to flip ups.status every 5 sec, so check every 3 + OUT1="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT1" ; sleep 3 + OUT2="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT2" ; sleep 3 + OUT3="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT3" ; sleep 3 + if echo "$OUT1$OUT2$OUT3" | grep "OB" && echo "$OUT1$OUT2$OUT3" | grep "OL" ; then + log_info "OK, ups.status flips over time" + PASSED="`expr $PASSED + 1`" + else + log_error "ups.status did not flip over time" + FAILED="`expr $FAILED + 1`" + fi +} + +isTestablePython() { + # We optionally make python module (if interpreter is found): + if [ x"${TOP_BUILDDIR}" = x ] \ + || [ ! -x "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py" ] \ + ; then + return 1 + fi + return 0 +} + +testcase_sandbox_python_without_credentials() { + isTestablePython || return 0 log_separator log_info "Call Python module test suite: PyNUT (NUT Python bindings) without login credentials" if ( unset NUT_USER || true @@ -517,7 +606,14 @@ if [ x"${TOP_BUILDDIR}" != x ] \ log_error "PyNUT complained, check above" FAILED="`expr $FAILED + 1`" fi +} +testcase_sandbox_python_with_credentials() { + isTestablePython || return 0 + + # That script says it expects data/evolution500.seq (as the UPS1 dummy) + # but the dummy data does not currently let issue the commands and + # setvars tested from python script. log_separator log_info "Call Python module test suite: PyNUT (NUT Python bindings) with login credentials" if ( @@ -532,10 +628,29 @@ if [ x"${TOP_BUILDDIR}" != x ] \ log_error "PyNUT complained, check above" FAILED="`expr $FAILED + 1`" fi -fi +} + +testcases_sandbox_python() { + isTestablePython || return 0 + testcase_sandbox_python_without_credentials + testcase_sandbox_python_with_credentials +} + +#################################### + +isTestableCppNIT() { + # We optionally make and here can run C++ client tests: + if [ x"${TOP_BUILDDIR}" = x ] \ + || [ ! -x "${TOP_BUILDDIR}/tests/cppnit" ] \ + ; then + return 1 + fi + return 0 +} + +testcase_sandbox_cppnit_without_creds() { + isTestableCppNIT || return 0 -# We optionally make and here can run C++ client tests: -if [ x"${TOP_BUILDDIR}" != x ] && [ -x "${TOP_BUILDDIR}/tests/cppnit" ] ; then log_separator log_info "Call libnutclient test suite: cppnit without login credentials" if ( unset NUT_USER || true @@ -548,6 +663,10 @@ if [ x"${TOP_BUILDDIR}" != x ] && [ -x "${TOP_BUILDDIR}/tests/cppnit" ] ; then log_error "cppnit complained, check above" FAILED="`expr $FAILED + 1`" fi +} + +testcase_sandbox_cppnit_simple_admin() { + isTestableCppNIT || return 0 log_separator log_info "Call libnutclient test suite: cppnit with login credentials: simple admin" @@ -571,6 +690,10 @@ if [ x"${TOP_BUILDDIR}" != x ] && [ -x "${TOP_BUILDDIR}/tests/cppnit" ] ; then log_error "cppnit complained, check above" FAILED="`expr $FAILED + 1`" fi +} + +testcase_sandbox_cppnit_upsmon_primary() { + isTestableCppNIT || return 0 log_separator log_info "Call libnutclient test suite: cppnit with login credentials: upsmon-primary" @@ -588,6 +711,10 @@ if [ x"${TOP_BUILDDIR}" != x ] && [ -x "${TOP_BUILDDIR}/tests/cppnit" ] ; then log_error "cppnit complained, check above" FAILED="`expr $FAILED + 1`" fi +} + +testcase_sandbox_cppnit_upsmon_master() { + isTestableCppNIT || return 0 log_separator log_info "Call libnutclient test suite: cppnit with login credentials: upsmon-master" @@ -605,10 +732,58 @@ if [ x"${TOP_BUILDDIR}" != x ] && [ -x "${TOP_BUILDDIR}/tests/cppnit" ] ; then log_error "cppnit complained, check above" FAILED="`expr $FAILED + 1`" fi -fi +} + +testcases_sandbox_cppnit() { + isTestableCppNIT || return 0 + testcase_sandbox_cppnit_without_creds + testcase_sandbox_cppnit_upsmon_primary + testcase_sandbox_cppnit_upsmon_master + testcase_sandbox_cppnit_simple_admin +} # TODO: Some upsmon tests? +testgroup_sandbox() { + testcase_sandbox_start_drivers_after_upsd + testcase_sandbox_upsc_query_model + testcase_sandbox_upsc_query_bogus + testcase_sandbox_upsc_query_timer + testcases_sandbox_python + testcases_sandbox_cppnit + + sandbox_forget_configs +} + +testgroup_sandbox_python() { + # Arrange for quick test iterations + testcase_sandbox_start_drivers_after_upsd + testcases_sandbox_python + sandbox_forget_configs +} + +testgroup_sandbox_cppnit() { + # Arrange for quick test iterations + testcase_sandbox_start_drivers_after_upsd + testcases_sandbox_cppnit + sandbox_forget_configs +} + +################################################################ + +case "${NIT_CASE}" in + cppnit) testgroup_sandbox_cppnit ;; + python) testgroup_sandbox_python ;; + testcase_*|testgroup_*|testcases_*|testgroups_*) + "${NIT_CASE}" ;; + "") # Default test groups: + testgroup_upsd_invalid_configs + testgroup_upsd_questionable_configs + testgroup_sandbox + ;; + *) die "Unsupported NIT_CASE='$NIT_CASE' was requested" ;; +esac + log_separator log_info "OVERALL: PASSED=$PASSED FAILED=$FAILED" From d8baa60f8fbe8a8e41b7fff8bf4ac0a83f5ace35 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 12:08:43 +0000 Subject: [PATCH 597/700] Update nut.dict --- docs/nut.dict | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/nut.dict b/docs/nut.dict index 698b28fa98..02d3335d06 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2946 utf-8 +personal_ws-1.1 en 2950 utf-8 AAS ABI ACFAIL @@ -1912,6 +1912,8 @@ httpd https huawei hunnox +hypervisor +hypervisors iBox iDowell iManufacturer @@ -2318,6 +2320,7 @@ openmp opensolaris openssh openssl +optimizations optiups oq os @@ -2711,6 +2714,7 @@ th timehead timeline timername +timestamp timeticks tiocm tios From 7c2d7dd9596823a7aaac7593cc2b69ae70820419 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 12:10:13 +0000 Subject: [PATCH 598/700] GitIgnore tests/cppnit binary and logs --- tests/.gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/.gitignore b/tests/.gitignore index cdee50ce56..23e0f10359 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,6 +1,9 @@ /cppunittest /cppunittest.log /cppunittest.trs +/cppnit +/cppnit.log +/cppnit.trs /test-suite.log /selftest-rw/* /nutlogtest From f02cad2d5337286498d959433e0d9c2d127a35a4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 12:21:20 +0000 Subject: [PATCH 599/700] tests/cpputest-client.cpp: allow up to 10 seconds for SET VAR to propagate into read-back value --- tests/cpputest-client.cpp | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/tests/cpputest-client.cpp b/tests/cpputest-client.cpp index 95295f5885..9a6169a1df 100644 --- a/tests/cpputest-client.cpp +++ b/tests/cpputest-client.cpp @@ -244,6 +244,7 @@ void NutActiveClientTest::test_auth_user() { try { TrackingResult tres; TrackingID tid; + int i; std::string nutVar = "ups.status"; /* Has a risk of flip-flop with NIT dummy setup */ std::string s1 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; std::string sTest = s1 + "-test"; @@ -264,8 +265,23 @@ void NutActiveClientTest::test_auth_user() { noException = false; } /* Check what we got after set */ - std::string s2 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; - std::cerr << "[D] Read back: " << s2 << std::endl; + /* Note that above we told the server to tell the driver + * to set a dstate entry; below we ask the server to ask + * the driver and relay the answer to us. The dummy-ups + * driver may also be in a sleeping state between cycles. + * Data propagation may be not instantaneous, so we loop + * for a while to see the expected value (or give up). + */ + std::string s2; + for (i = 0; i < 100 ; i++) { + s2 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + if (s2 == sTest) + break; + usleep(100000); + } + std::cerr << "[D] Read back: " << s2 + << " after " << (100 * i) << "msec" + << std::endl; /* Fix it back */ tid = c.setDeviceVariable(NUT_SETVAR_DEVICE, nutVar, s1); @@ -277,8 +293,16 @@ void NutActiveClientTest::test_auth_user() { << "tracking result is " << tres << std::endl; noException = false; } - std::string s3 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; - std::cerr << "[D] Read back: " << s3 << std::endl; + std::string s3; + for (i = 0; i < 100 ; i++) { + s3 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + if (s3 == s1) + break; + usleep(100000); + } + std::cerr << "[D] Read back: " << s3 + << " after " << (100 * i) << "msec" + << std::endl; if (s3 != s1) { std::cerr << "[D] Final device variable value '" << s3 From baaf46822a898b35db20774a76aa654f29a78590 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 12:52:59 +0000 Subject: [PATCH 600/700] tests/NIT/nit.sh: testcase_sandbox_start_drivers_after_upsd(): wait for UPS1 and UPS2 to respond --- tests/NIT/nit.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 1915033aee..b4f648dbff 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -540,6 +540,17 @@ testcase_sandbox_start_drivers_after_upsd() { log_error "Query failed, retrying with UPSD started after drivers" testcase_start_upsd_after_drivers } + + if [ x"${TOP_SRCDIR}" != x ]; then + log_info "Wait for dummy UPSes with larger data sets to initialize" + for U in UPS1 UPS2 ; do + while ! upsc $U@localhost:$NUT_PORT ups.status ; do + sleep 1 + done + done + fi + + log_info "Expected drivers are now responding via UPSD" } testcase_sandbox_upsc_query_model() { From 92c7322330f283058a798b09dd701e0e0a008cf5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 12:53:30 +0000 Subject: [PATCH 601/700] tests/NIT/nit.sh: add testgroup_sandbox_cppnit_simple_admin() to troubleshoot dummy-ups data propagation --- tests/NIT/nit.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index b4f648dbff..e0bd649f34 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -780,6 +780,13 @@ testgroup_sandbox_cppnit() { sandbox_forget_configs } +testgroup_sandbox_cppnit_simple_admin() { + # Arrange for quick test iterations + testcase_sandbox_start_drivers_after_upsd + testcase_sandbox_cppnit_simple_admin + sandbox_forget_configs +} + ################################################################ case "${NIT_CASE}" in From d340a0b74b4fe4b1c939f522d079dcde0b918959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Rapkiewicz?= Date: Fri, 22 Apr 2022 17:39:36 +0200 Subject: [PATCH 602/700] Confirm support for ECO Pro AVR CDS series EVER UPS This commit confirms that EVER UPS line 'ECO Pro AVR CDS' is supported by NUT, from nowon, nut-scanner will recognize EVER Ups as follows: [nutdev1] driver = "usbhid-ups" port = "auto" vendorid = "2E51" productid = "0000" product = "ECO PRO AVR CDS" serial = "XXXXXXXXXXXXXXXXXXXXXXXXX" vendor = "EVER" bus = "003" --- data/driver.list.in | 1 + drivers/ever-hid.c | 1 + scripts/upower/95-upower-hid.hwdb | 1 + 3 files changed, 3 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 916123ecf6..7ea022f221 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -379,6 +379,7 @@ "EVER" "ups" "2" "DUO II Pro series" "USB port" "blazer_usb" "EVER" "ups" "2" "POWERLINE RT 1-3kVA series" "USB port" "blazer_usb" "EVER" "ups" "2" "POWERLINE RT 6-10kVA series" "USB port" "blazer_usb" +"EVER" "ups" "4" "ECO Pro AVR CDS series" "USB port" "usbhid-up" "Exide" "ups" "1" "NetUPS SE" "" "genericups upstype=15" "Exide" "ups" "4" "NetUPS SE 450/700/1000/1500" "" "upscode2" diff --git a/drivers/ever-hid.c b/drivers/ever-hid.c index 866fff96c3..9f6a674571 100644 --- a/drivers/ever-hid.c +++ b/drivers/ever-hid.c @@ -47,6 +47,7 @@ static usb_device_id_t ever_usb_device_table[] = { { USB_DEVICE(STMICRO_VENDORID, 0xa113), NULL }, { USB_DEVICE(EVER_VENDORID, 0xffff), NULL}, + { USB_DEVICE(EVER_VENDORID, 0x0000), NULL}, /* Terminating entry */ { 0, 0, NULL } diff --git a/scripts/upower/95-upower-hid.hwdb b/scripts/upower/95-upower-hid.hwdb index a4eff0f58c..e97372ce34 100644 --- a/scripts/upower/95-upower-hid.hwdb +++ b/scripts/upower/95-upower-hid.hwdb @@ -182,6 +182,7 @@ usb:v2B2DpFFFF* UPOWER_VENDOR=AEG # Ever +usb:v2E51p0000* usb:v2E51pFFFF* UPOWER_BATTERY_TYPE=ups UPOWER_VENDOR=Ever From 4fb0c6895177f590f0c4ba2cff30cda8960f90ea Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 13:41:48 +0000 Subject: [PATCH 603/700] tests/NIT/nit.sh: ignore message from NSS builds Those tend to add lines like Init SSL without certificate database so we should better grep for expected char sequence --- tests/NIT/nit.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index e0bd649f34..8f034939bc 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -507,8 +507,8 @@ UPS2" log_error "upsc was supposed to answer with error exit code: $OUT" FAILED="`expr $FAILED + 1`" } - if [ x"$OUT" != x'Error: Driver not connected' ] ; then - log_error "got reply for upsc query when 'Error: Driver not connected' was expected: $OUT" + if ! echo "$OUT" | grep 'Error: Driver not connected' ; then + log_error "got reply for upsc query when 'Error: Driver not connected' was expected: '$OUT'" FAILED="`expr $FAILED + 1`" else PASSED="`expr $PASSED + 1`" @@ -569,7 +569,7 @@ testcase_sandbox_upsc_query_bogus() { log_error "upsc was supposed to answer with error exit code: $OUT" FAILED="`expr $FAILED + 1`" } - if [ x"$OUT" != x'Error: Variable not supported by UPS' ] ; then + if ! echo "$OUT" | grep 'Error: Variable not supported by UPS' ; then log_error "got reply for upsc query when 'Error: Variable not supported by UPS' was expected: $OUT" FAILED="`expr $FAILED + 1`" else From b23925c7704feaadb4a9744f5b5d7fd100334290 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 23:43:25 +0000 Subject: [PATCH 604/700] tests/NIT/nit.sh: testcase_sandbox_upsc_query_timer(): for slower testers out there, wait one more cycle --- tests/NIT/nit.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 8f034939bc..bcd2f0f8d3 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -584,7 +584,8 @@ testcase_sandbox_upsc_query_timer() { OUT1="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT1" ; sleep 3 OUT2="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT2" ; sleep 3 OUT3="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT3" ; sleep 3 - if echo "$OUT1$OUT2$OUT3" | grep "OB" && echo "$OUT1$OUT2$OUT3" | grep "OL" ; then + OUT4="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT4" ; sleep 3 + if echo "$OUT1$OUT2$OUT3$OUT4" | grep "OB" && echo "$OUT1$OUT2$OUT3$OUT4" | grep "OL" ; then log_info "OK, ups.status flips over time" PASSED="`expr $PASSED + 1`" else From 07ae174b6c3f7d7eaeeea0427eff4ca869c64c55 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Fri, 22 Apr 2022 23:45:29 +0000 Subject: [PATCH 605/700] tests/NIT/nit.sh: testcase_sandbox_upsc_query_timer(): do not waste time if we did get the answer we want --- tests/NIT/nit.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index bcd2f0f8d3..6d89753aa4 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -582,9 +582,17 @@ testcase_sandbox_upsc_query_timer() { log_info "Test that dummy-ups TIMER action changes the reported state" # Driver is set up to flip ups.status every 5 sec, so check every 3 OUT1="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT1" ; sleep 3 - OUT2="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT2" ; sleep 3 - OUT3="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT3" ; sleep 3 - OUT4="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT4" ; sleep 3 + OUT2="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT2" + OUT3="" + OUT4="" + if [ x"$OUT1" = x"$OUT2" ]; then + sleep 3 + OUT3="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT3" + if [ x"$OUT2" = x"$OUT3" ]; then + sleep 3 + OUT4="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT4" + fi + fi if echo "$OUT1$OUT2$OUT3$OUT4" | grep "OB" && echo "$OUT1$OUT2$OUT3$OUT4" | grep "OL" ; then log_info "OK, ups.status flips over time" PASSED="`expr $PASSED + 1`" From dfd537bebfa56c5dbaf47b2ebd056ed105686257 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 09:56:07 +0000 Subject: [PATCH 606/700] driver.list.in: Add HCL info for nJoy Keen 600 (USB) Closes: #867 --- data/driver.list.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 896540c18e..80a6586203 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -823,6 +823,9 @@ "Nitram" "ups" "1" "Elite 2002" "" "genericups upstype=16" "Nitram" "ups" "1" "Elite 2005" "" "powerpanel" +"nJoy" "ups" "2" "Keen 600" "USB" "blazer_ser port=/dev/ttyUSB0" # https://www.njoy.ro/UPS/keen-600 +"nJoy" "ups" "2" "Keen 600" "USB" "nutdrv_qx port=/dev/ttyUSB0" # https://www.njoy.ro/UPS/keen-600 + "Novex" "ups" "1" "NUPS-650" "USB" "blazer_usb protocol=megatec" # http://komp.1k.by/periphery-ups/novex/Novex_NUPS_650-130052.html "Numeric" "ups" "2" "3000 SW" "" "blazer_ser" From 332df19bbbe34c5d590985f57d486b059cc1061d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 14:00:58 +0200 Subject: [PATCH 607/700] docs/man/dummy-ups.txt + clone.txt: cross-link two man pages to remind about similar use-cases --- docs/man/clone.txt | 25 +++++++++++++++++++++++++ docs/man/dummy-ups.txt | 29 +++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/docs/man/clone.txt b/docs/man/clone.txt index 42780e56cd..4d5f2f1f0a 100644 --- a/docs/man/clone.txt +++ b/docs/man/clone.txt @@ -107,6 +107,22 @@ drivers are not affected, so if you tell the real UPS driver to shutdown the outlet of the clone UPS driver, your clients will lose power without warning. +If you use service management frameworks like systemd or SMF to manage the +dependencies between driver instances and the data server, and some of these +drivers are `dummy-ups` in repeater mode (or `clone` drivers) representing +data from another driver running on the same system, then you may have to +set up special dependencies (e.g. with systemd "drop-in" snippet files) +to allow your `nut-server` to start after the "real" device drivers and +before such repeater drivers (without a responding server, they would fail +to start anyway). This may also need special care in `upsd.conf` and/or +`ups.conf` files to not block the system start-up for too long while the +repeater driver has not started. + +////////////////////////////////////// +TODO later: declare the driver as "optional", see +https://github.com/networkupstools/nut/issues/1389 +////////////////////////////////////// + AUTHOR ------ @@ -120,6 +136,15 @@ linkman:upsrw[1], linkman:ups.conf[5], linkman:nutupsdrv[8] +Dummy driver: +~~~~~~~~~~~~~ + +The "repeater" mode of 'dummy-ups' driver is in some ways similar to the +'clone' driver, by relaying information from a locally or remotely running +"real" device driver (and NUT data server). + +linkman:dummy-ups[8] + Internet Resources: ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/man/dummy-ups.txt b/docs/man/dummy-ups.txt index 92560fa280..4b51eac343 100644 --- a/docs/man/dummy-ups.txt +++ b/docs/man/dummy-ups.txt @@ -220,6 +220,25 @@ BUGS Instant commands are not yet supported in Dummy Mode, and data need name/value checking enforcement, as well as boundaries or enumeration definition. +CAVEATS +------- + +If you use service management frameworks like systemd or SMF to manage the +dependencies between driver instances and the data server, and some of these +drivers are `dummy-ups` in repeater mode (or `clone` drivers) representing +data from another driver running on the same system, then you may have to +set up special dependencies (e.g. with systemd "drop-in" snippet files) +to allow your `nut-server` to start after the "real" device drivers and +before such repeater drivers (without a responding server, they would fail +to start anyway). This may also need special care in `upsd.conf` and/or +`ups.conf` files to not block the system start-up for too long while the +repeater driver has not started. + +////////////////////////////////////// +TODO later: declare the driver as "optional", see +https://github.com/networkupstools/nut/issues/1389 +////////////////////////////////////// + AUTHOR ------ @@ -233,6 +252,16 @@ linkman:upsrw[1], linkman:ups.conf[5], linkman:nutupsdrv[8] +Clone driver: +~~~~~~~~~~~~~ + +The "repeater" mode of 'dummy-ups' driver is in some ways similar to the +'clone' driver, which sits on top of another driver socket, and allows +users to group clients to a particular outlet of a device and deal with +this output as if it were a normal UPS. + +linkman:clone[8] + Internet Resources: ~~~~~~~~~~~~~~~~~~~ From 689b231891cd542a744ffefd460dac63393f9898 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 14:02:46 +0200 Subject: [PATCH 608/700] docs/man/dummy-ups.txt: update notes for dummy-once mode --- docs/man/dummy-ups.txt | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/man/dummy-ups.txt b/docs/man/dummy-ups.txt index 4b51eac343..ce4609c482 100644 --- a/docs/man/dummy-ups.txt +++ b/docs/man/dummy-ups.txt @@ -17,7 +17,11 @@ DESCRIPTION ----------- This program is a multi-purpose UPS emulation tool. -Its behavior depends on the running mode: "dummy" or "repeater". +Its general behavior depends on the running mode: "dummy" ("dummy-once" +or "dummy-loop"), or "repeater". +//////////////////////////////////////// +...or "meta" eventually. +//////////////////////////////////////// Dummy Mode ~~~~~~~~~~ @@ -30,6 +34,9 @@ through script files. It can be configured, launched and used as any other "real" NUT driver. This mode is mostly useful for development and testing purposes. +NOTE: See below about the differences of `dummy-once` vs. `dummy-loop` +modes -- the former may be more suitable for "interactive" uses and tests. + Repeater Mode ~~~~~~~~~~~~~ @@ -102,10 +109,15 @@ not be impacted. For instance: - [dummy] + [dummy1] driver = dummy-ups port = evolution500.seq - desc = "dummy-ups in dummy mode" + desc = "dummy-ups in dummy-loop mode" + + [dummy2] + driver = dummy-ups + port = epdu-managed.dev + desc = "dummy-ups in dummy-once mode" This file is generally named `something.dev` or `something.seq`. It contains a list of all valid variables and associated values (you can later use `upsrw` From 1c04ba64460fb597907f45721e796b70d5b56d9f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 14:03:07 +0200 Subject: [PATCH 609/700] docs/man/dummy-ups.txt: update notes for dummy-once mode ability to re-read file only if it changes --- docs/man/dummy-ups.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/man/dummy-ups.txt b/docs/man/dummy-ups.txt index ce4609c482..b7f07d1d91 100644 --- a/docs/man/dummy-ups.txt +++ b/docs/man/dummy-ups.txt @@ -74,9 +74,11 @@ path name. In the latter case the NUT sysconfig directory (i.e. `/etc/nut`, Since NUT v2.8.0 two aspects of this mode are differentiated: * `dummy-once` reads the specified file once to the end (interrupting for - `TIMER` lines, etc.) and does not re-process it; - this allows use/test cases to `upsrw` variables in the driver instance - and they remain in memory until the driver is restarted; + `TIMER` lines, etc.) and does not re-process it until the filesystem + timestamp of the data file is changed; this reduces run-time stress if + you test with a lot of dummy devices, and allows use/test cases to + `upsrw` variables into the driver instance -- and they remain in memory + until the driver is restarted (or the file is touched or modified); + Since NUT v2.8.0 `dummy-once` is assigned by default to files with a `*.dev` naming pattern. From 21f6b7a0561d52cd5a63484bdcf3c9e42918734e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 14:03:30 +0200 Subject: [PATCH 610/700] docs/man/dummy-ups.txt: highlight that upsrw can not define new variables --- docs/man/dummy-ups.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/man/dummy-ups.txt b/docs/man/dummy-ups.txt index b7f07d1d91..df37498d6f 100644 --- a/docs/man/dummy-ups.txt +++ b/docs/man/dummy-ups.txt @@ -205,7 +205,9 @@ linkman:upsrw[1] and linkman:upscmd[1] commands. Note that in simulation mode, new variables can be added on the fly, but only by adding these to the definition file (and waiting for it to be re-read). -Conversely, if you need to remove variable (such as transient ones, like +That is, the driver should not allow to define a new variable via `upsrw`. + +Conversely, if you need to remove a variable (such as transient ones, like `ups.alarm`), simply update these by setting an empty value. As a result, they will get removed from the data. From 1e7261b0e1aa3ad78db56b785061a15e26468ea9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 14:03:44 +0200 Subject: [PATCH 611/700] docs/man/dummy-ups.txt: re-wrap long lines and reword --- docs/man/dummy-ups.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/man/dummy-ups.txt b/docs/man/dummy-ups.txt index df37498d6f..cf1bd6e4bd 100644 --- a/docs/man/dummy-ups.txt +++ b/docs/man/dummy-ups.txt @@ -218,17 +218,18 @@ BACKGROUND ---------- Dummy Mode was originally written in one evening to replace the previous -dummycons testing driver, which was too limited, and required a terminal for -interaction. +'dummycons' testing driver, which was too limited, and required a terminal +for interaction. *dummy-ups* is useful for NUT client development, and other testing purposes. -It also helps the NUT Quality Assurance effort, by automating some tests on the -NUT framework. +It also helps the NUT Quality Assurance effort, by automating some tests on +the NUT framework. It now offers a repeater mode. This will help in building the Meta UPS approach, which allows one to build a virtual device, composed of several other devices -(either UPS, PDUs). +(either UPS, PDUs), or perhaps represent the same device which supports +several communication protocols and different media (Serial, USB, SNMP...) BUGS ---- From 9bad686ed5276eac8b3f284ac6761a5110cf62c1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 14:11:58 +0200 Subject: [PATCH 612/700] tests/NIT/Makefile.am + configure.ac: introduce --enable-check-NIT --- configure.ac | 6 ++++++ docs/configure.txt | 19 +++++++++++++++++++ docs/nut-qa.txt | 4 +++- tests/NIT/Makefile.am | 5 +++++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 7da3c471e9..b45e03e172 100644 --- a/configure.ac +++ b/configure.ac @@ -1217,6 +1217,12 @@ esac AM_CONDITIONAL(WITH_CPPCHECK, test "${WITH_CPPCHECK}" = "yes") +dnl ---------------------------------------------------------------------- +dnl checks related to --enable-check-NIT + +NUT_ARG_ENABLE([check-NIT], [Run check-NIT among default checks], [no]) +AM_CONDITIONAL(WITH_CHECK_NIT, test "${nut_enable_check_NIT}" = "yes") + dnl ---------------------------------------------------------------------- dnl checks related to --with-doc diff --git a/docs/configure.txt b/docs/configure.txt index b039e87f7a..c681f74791 100644 --- a/docs/configure.txt +++ b/docs/configure.txt @@ -205,6 +205,25 @@ Development files Build and install the upsclient and nutclient library and header files, to build further projects against NUT (such as wmNUT client and many others). +Options for developers +~~~~~~~~~~~~~~~~~~~~~~ + + --enable-check-NIT (default: no) + +Add `make check-NIT` to default activity of `make check` to run the +NUT Integration Testing suite. This is potentially dangerous (e.g. due +to port conflicts when running many such tests in same environment), +so not active by default. + + --enable-maintainer-mode (default: no) + +Use maintainer mode to keep `Makefile.in` and `Makefile` in sync with +ever-changing `Makefile.am` content after Git updates or editing. + + --enable-cppcheck (default: no) + +Activate recipes for static analysis with `cppcheck` tools (if available). + I want it all! ~~~~~~~~~~~~~~ diff --git a/docs/nut-qa.txt b/docs/nut-qa.txt index 481c989350..c728650831 100644 --- a/docs/nut-qa.txt +++ b/docs/nut-qa.txt @@ -126,7 +126,9 @@ FIXME (POST): a few data points and checks that these are well propagated with upsc. - similar approach is explored in NIT (NUT Integration Testing) suite, - which is part of the codebase and `make check` activities + which is part of the codebase and automated with `make check-NIT`; + it can also be added to default `make check` activities by running + `configure --enable-check-NIT` - link:https://bugzilla.redhat.com/buglist.cgi?component=nut[Redhat / Fedora Bug tracker] diff --git a/tests/NIT/Makefile.am b/tests/NIT/Makefile.am index c1fc4c3183..6ce64a9ec8 100644 --- a/tests/NIT/Makefile.am +++ b/tests/NIT/Makefile.am @@ -1,6 +1,11 @@ EXTRA_DIST = nit.sh README +if WITH_CHECK_NIT check: check-NIT +else +check: + @echo "NO-OP: $@ in `pwd` is inactive by default. Run 'configure --enable-check-NIT' or 'make check-NIT' explicitly" >&2 +endif # Run in builddir, use script from srcdir # Avoid running "$<" - not all make implementations handle that From 06c513dcc67e211c130b02a98b40588c6760345a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 15:27:08 +0000 Subject: [PATCH 613/700] tests/NIT/nit.sh: limit how long we wait for UPS1/UPS2 to begin responding --- tests/NIT/nit.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 6d89753aa4..0d9e6026ed 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -544,8 +544,12 @@ testcase_sandbox_start_drivers_after_upsd() { if [ x"${TOP_SRCDIR}" != x ]; then log_info "Wait for dummy UPSes with larger data sets to initialize" for U in UPS1 UPS2 ; do + COUNTDOWN=60 while ! upsc $U@localhost:$NUT_PORT ups.status ; do sleep 1 + COUNTDOWN="`expr $COUNTDOWN - 1`" + # Systemic error, e.g. could not create socket file? + [ "$COUNTDOWN" -lt 1 ] && die "Dummy driver did not start or respond in time" done done fi From 0389a47efa7da956669d47f55380e97bd701140f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 16:02:39 +0000 Subject: [PATCH 614/700] docs/man/clone.txt + dummy-ups.txt: clarify the dependency caveats Clone drivers do not require to run after upsd, but may need to start after the "real" driver whose socket thay leech onto. --- docs/man/clone.txt | 12 +++--------- docs/man/dummy-ups.txt | 8 ++++---- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/docs/man/clone.txt b/docs/man/clone.txt index 4d5f2f1f0a..555e5c7478 100644 --- a/docs/man/clone.txt +++ b/docs/man/clone.txt @@ -108,15 +108,9 @@ the outlet of the clone UPS driver, your clients will lose power without warning. If you use service management frameworks like systemd or SMF to manage the -dependencies between driver instances and the data server, and some of these -drivers are `dummy-ups` in repeater mode (or `clone` drivers) representing -data from another driver running on the same system, then you may have to -set up special dependencies (e.g. with systemd "drop-in" snippet files) -to allow your `nut-server` to start after the "real" device drivers and -before such repeater drivers (without a responding server, they would fail -to start anyway). This may also need special care in `upsd.conf` and/or -`ups.conf` files to not block the system start-up for too long while the -repeater driver has not started. +dependencies between driver instances and other units, then you may have +to set up special dependencies (e.g. with systemd "drop-in" snippet files) +to queue your `clone` drivers to start after the "real" device drivers. ////////////////////////////////////// TODO later: declare the driver as "optional", see diff --git a/docs/man/dummy-ups.txt b/docs/man/dummy-ups.txt index cf1bd6e4bd..e3ed2f656a 100644 --- a/docs/man/dummy-ups.txt +++ b/docs/man/dummy-ups.txt @@ -240,10 +240,10 @@ checking enforcement, as well as boundaries or enumeration definition. CAVEATS ------- -If you use service management frameworks like systemd or SMF to manage the -dependencies between driver instances and the data server, and some of these -drivers are `dummy-ups` in repeater mode (or `clone` drivers) representing -data from another driver running on the same system, then you may have to +If you use service management frameworks like systemd or SMF to manage +the dependencies between driver instances and the data server, and some +of these drivers are `dummy-ups` in repeater mode representing data +from another driver running on the same system, then you may have to set up special dependencies (e.g. with systemd "drop-in" snippet files) to allow your `nut-server` to start after the "real" device drivers and before such repeater drivers (without a responding server, they would fail From cd0a608888c10a27ddcbc1d1a591c5e927b4e26c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 16:22:38 +0000 Subject: [PATCH 615/700] check_unix_socket_filename() to meaningfully abort when our paths are too long --- clients/upssched.c | 3 +++ common/common.c | 24 +++++++++++++++++++++++- drivers/clone-outlet.c | 19 +++++++++++++++++-- drivers/clone.c | 19 +++++++++++++++++-- drivers/dstate.c | 2 ++ include/common.h | 3 +++ server/sockdebug.c | 2 ++ server/sstate.c | 2 ++ 8 files changed, 69 insertions(+), 5 deletions(-) diff --git a/clients/upssched.c b/clients/upssched.c index f35fe09ec7..8bb3547479 100644 --- a/clients/upssched.c +++ b/clients/upssched.c @@ -272,6 +272,7 @@ static int open_sock(void) int ret, fd; struct sockaddr_un ssaddr; + check_unix_socket_filename(pipefn); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) @@ -632,6 +633,8 @@ static int try_connect(void) int pipefd, ret; struct sockaddr_un saddr; + check_unix_socket_filename(pipefn); + memset(&saddr, '\0', sizeof(saddr)); saddr.sun_family = AF_UNIX; snprintf(saddr.sun_path, sizeof(saddr.sun_path), "%s", pipefn); diff --git a/common/common.c b/common/common.c index bb00aafdc9..f664d99b60 100644 --- a/common/common.c +++ b/common/common.c @@ -1,7 +1,7 @@ /* common.c - common useful functions Copyright (C) 2000 Russell Kroll - Copyright (C) 2021 Jim Klimov + Copyright (C) 2021-2022 Jim Klimov 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 @@ -26,6 +26,7 @@ #include #include #include +#include /* the reason we define UPS_VERSION as a static string, rather than a macro, is to make dependency tracking easier (only common.o depends @@ -571,6 +572,27 @@ const char * altpidpath(void) #endif } +/* Die with a standard message if socket filename is too long */ +void check_unix_socket_filename(const char *fn) { + struct sockaddr_un ssaddr; + if (strlen(fn) < sizeof(ssaddr.sun_path)) + return; + + /* Avoid useless truncated pathnames that + * other driver instances would conflict + * with, and upsd can not discover. + * Note this is quite short on many OSes + * varying 104-108 bytes (UNIX_PATH_MAX) + * as opposed to PATH_MAX or MAXPATHLEN + * typically of a kilobyte range. + */ + fatalx(EXIT_FAILURE, + "Can't create a unix domain socket: pathname '%s' " + "is too long (%zu) for 'struct sockaddr_un->sun_path' " + "on this system (%zu)", + fn, strlen(fn), sizeof(ssaddr.sun_path)); +} + /* logs the formatted string to any configured logging devices + the output of strerror(errno) */ void upslog_with_errno(int priority, const char *fmt, ...) { diff --git a/drivers/clone-outlet.c b/drivers/clone-outlet.c index 81374358ad..127909c564 100644 --- a/drivers/clone-outlet.c +++ b/drivers/clone-outlet.c @@ -20,6 +20,7 @@ #include "main.h" #include "parseconf.h" +#include "nut_stdint.h" #include #include @@ -143,13 +144,27 @@ static int parse_args(size_t numargs, char **arg) static int sstate_connect(void) { ssize_t ret; - int fd; + int fd, len; const char *dumpcmd = "DUMPALL\n"; struct sockaddr_un sa; memset(&sa, '\0', sizeof(sa)); sa.sun_family = AF_UNIX; - snprintf(sa.sun_path, sizeof(sa.sun_path), "%s/%s", dflt_statepath(), device_path); + len = snprintf(sa.sun_path, sizeof(sa.sun_path), "%s/%s", dflt_statepath(), device_path); + + if (len < 0) { + fatalx(EXIT_FAILURE, "Can't create a unix domain socket: " + "failed to prepare the pathname"); + } + if ((uintmax_t)len >= (uintmax_t)sizeof(sa.sun_path)) { + fatalx(EXIT_FAILURE, + "Can't create a unix domain socket: pathname '%s/%s' " + "is too long (%zu) for 'struct sockaddr_un->sun_path' " + "on this system (%zu)", + dflt_statepath(), device_path, + strlen(dflt_statepath()) + 1 + strlen(device_path), + sizeof(sa.sun_path)); + } fd = socket(AF_UNIX, SOCK_STREAM, 0); diff --git a/drivers/clone.c b/drivers/clone.c index ab0824d9da..00a95b4c6d 100644 --- a/drivers/clone.c +++ b/drivers/clone.c @@ -22,6 +22,7 @@ #include "main.h" #include "parseconf.h" #include "attribute.h" +#include "nut_stdint.h" #include #include @@ -159,13 +160,27 @@ static int parse_args(size_t numargs, char **arg) static int sstate_connect(void) { ssize_t ret; - int fd; + int fd, len; const char *dumpcmd = "DUMPALL\n"; struct sockaddr_un sa; memset(&sa, '\0', sizeof(sa)); sa.sun_family = AF_UNIX; - snprintf(sa.sun_path, sizeof(sa.sun_path), "%s/%s", dflt_statepath(), device_path); + len = snprintf(sa.sun_path, sizeof(sa.sun_path), "%s/%s", dflt_statepath(), device_path); + + if (len < 0) { + fatalx(EXIT_FAILURE, "Can't create a unix domain socket: " + "failed to prepare the pathname"); + } + if ((uintmax_t)len >= (uintmax_t)sizeof(sa.sun_path)) { + fatalx(EXIT_FAILURE, + "Can't create a unix domain socket: pathname '%s/%s' " + "is too long (%zu) for 'struct sockaddr_un->sun_path' " + "on this system (%zu)", + dflt_statepath(), device_path, + strlen(dflt_statepath()) + 1 + strlen(device_path), + sizeof(sa.sun_path)); + } fd = socket(AF_UNIX, SOCK_STREAM, 0); diff --git a/drivers/dstate.c b/drivers/dstate.c index 5ba27f35f7..b37e88987b 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -109,6 +109,8 @@ static int sock_open(const char *fn) int ret, fd; struct sockaddr_un ssaddr; + check_unix_socket_filename(fn); + fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { diff --git a/include/common.h b/include/common.h index 7358faeec8..ff48ecdc3e 100644 --- a/include/common.h +++ b/include/common.h @@ -144,6 +144,9 @@ const char * dflt_statepath(void); /* Return the alternate path for pid files */ const char * altpidpath(void); +/* Die with a standard message if socket filename is too long */ +void check_unix_socket_filename(const char *fn); + /* upslog*() messages are sent to syslog always; * their life after that is out of NUT's control */ void upslog_with_errno(int priority, const char *fmt, ...) diff --git a/server/sockdebug.c b/server/sockdebug.c index 031e02cc36..f9fe4767c5 100644 --- a/server/sockdebug.c +++ b/server/sockdebug.c @@ -48,6 +48,8 @@ static int socket_connect(const char *sockfn) int ret, fd; struct sockaddr_un sa; + check_unix_socket_filename(sockfn); + memset(&sa, '\0', sizeof(sa)); sa.sun_family = AF_UNIX; snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", sockfn); diff --git a/server/sstate.c b/server/sstate.c index f33df625ba..167bc09b2f 100644 --- a/server/sstate.c +++ b/server/sstate.c @@ -181,6 +181,8 @@ int sstate_connect(upstype_t *ups) ssize_t ret; struct sockaddr_un sa; + check_unix_socket_filename(ups->fn); + memset(&sa, '\0', sizeof(sa)); sa.sun_family = AF_UNIX; snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", ups->fn); From 7391e7d410b88024ad419198fc5a5cc30ed894d2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 17:16:04 +0000 Subject: [PATCH 616/700] tests/NIT/nit.sh: refactor with TESTDIR --- tests/NIT/nit.sh | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 0d9e6026ed..1e77175511 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -115,9 +115,22 @@ PID_DUMMYUPS="" PID_DUMMYUPS1="" PID_DUMMYUPS2="" -rm -rf "$BUILDDIR/tmp" || true +TESTDIR="$BUILDDIR/tmp" +# Technically the limit is sizeof(sockaddr.sun_path) for complete socket +# pathname, which varies 104-108 chars max on systems seen in CI farm; +# we reserve 17 chars for "/dummy-ups-dummy" longest filename. +if [ `echo "$TESTDIR" | wc -c` -gt 80 ]; then + log_info "'$TESTDIR' is too long to store AF_UNIX socket files, will mktemp" + if ! ( [ -n "${TMPDIR-}" ] && [ -d "${TMPDIR-}" ] && [ -w "${TMPDIR-}" ] ) ; then + if [ -d /dev/shm ] && [ -w /dev/shm ]; then TMPDIR=/dev/shm ; else TMPDIR=/tmp ; fi + fi + TESTDIR="`mktemp -d "${TMPDIR}/nit-tmp.$$.XXXXXX"`" || die "Failed to mktemp" +else + rm -rf "${TESTDIR}" || true +fi +log_info "Using '$TESTDIR' for generated configs and state files" -mkdir -p "$BUILDDIR/tmp/etc" "$BUILDDIR/tmp/run" && chmod 750 "$BUILDDIR/tmp/run" \ +mkdir -p "${TESTDIR}/etc" "${TESTDIR}/run" && chmod 750 "${TESTDIR}/run" \ || die "Failed to create temporary FS structure for the NIT" stop_daemons() { @@ -129,9 +142,9 @@ stop_daemons() { trap 'RES=$?; stop_daemons; exit $RES;' 0 1 2 3 15 -NUT_STATEPATH="$BUILDDIR/tmp/run" -NUT_ALTPIDPATH="$BUILDDIR/tmp/run" -NUT_CONFPATH="$BUILDDIR/tmp/etc" +NUT_STATEPATH="${TESTDIR}/run" +NUT_ALTPIDPATH="${TESTDIR}/run" +NUT_CONFPATH="${TESTDIR}/etc" export NUT_STATEPATH NUT_ALTPIDPATH NUT_CONFPATH # TODO: Find a portable way to (check and) grab a random unprivileged port? From 795f51a621be1d9efbdae869d7fe43a584f9f2a1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 17:16:27 +0000 Subject: [PATCH 617/700] tests/NIT/nit.sh: remove mktemp-ed TESTDIR when we exit --- tests/NIT/nit.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index 1e77175511..fdfff0b02d 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -140,7 +140,7 @@ stop_daemons() { fi } -trap 'RES=$?; stop_daemons; exit $RES;' 0 1 2 3 15 +trap 'RES=$?; stop_daemons; if [ "${TESTDIR}" != "${BUILDDIR}/tmp" ] ; then rm -rf "${TESTDIR}" ; fi; exit $RES;' 0 1 2 3 15 NUT_STATEPATH="${TESTDIR}/run" NUT_ALTPIDPATH="${TESTDIR}/run" From 8fa10ec09806686d2dfcd1a6f01cef3d5114d4ed Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 17:16:37 +0000 Subject: [PATCH 618/700] tests/NIT/nit.sh: fix renaming typo --- tests/NIT/nit.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh index fdfff0b02d..f404cb9f9f 100755 --- a/tests/NIT/nit.sh +++ b/tests/NIT/nit.sh @@ -551,7 +551,7 @@ testcase_sandbox_start_drivers_after_upsd() { upsc dummy@localhost:$NUT_PORT || { # Should not get to this, except on very laggy systems maybe log_error "Query failed, retrying with UPSD started after drivers" - testcase_start_upsd_after_drivers + testcase_sandbox_start_upsd_after_drivers } if [ x"${TOP_SRCDIR}" != x ]; then From 4481e47044156b11ea28541de7d329a1f0c73b7f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 17:19:02 +0000 Subject: [PATCH 619/700] ci_build.sh: "--enable-check-NIT" for CI builds --- ci_build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci_build.sh b/ci_build.sh index 49f60e821f..8328ebeb65 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -743,6 +743,7 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp # TODO: Consider `--enable-maintainer-mode` to add recipes that # would quickly regenerate Makefile(.in) if you edit Makefile.am + CONFIG_OPTS+=("--enable-check-NIT") if [ -n "${PYTHON-}" ]; then # WARNING: Watch out for whitespaces, not handled here! From 4bce037646154b8253d753959c9fd1314cc2deb1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 17:19:30 +0000 Subject: [PATCH 620/700] ci_build.sh: "--enable-check-NIT" and "--enable-maintainer-mode" for developer builds without a BUILD_TYPE --- ci_build.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ci_build.sh b/ci_build.sh index 8328ebeb65..8dc0f1d54e 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -1511,15 +1511,16 @@ bindings) ./autogen.sh - # TODO: Consider `--enable-maintainer-mode` to add recipes that - # would quickly regenerate Makefile(.in) if you edit Makefile.am # NOTE: Default NUT "configure" actually insists on some features, # like serial port support unless told otherwise, or docs if possible. # Below we aim for really fast iterations of C/C++ development so # enable whatever is auto-detectable (except docs), and highlight # any warnings if we can: #./configure - ./configure --enable-Wcolor --with-all=auto --with-cgi=auto --with-serial=auto --with-dev=auto --with-doc=skip + ./configure --enable-Wcolor \ + --with-all=auto --with-cgi=auto --with-serial=auto \ + --with-dev=auto --with-doc=skip \ + --enable-check-NIT --enable-maintainer-mode # NOTE: Currently parallel builds are expected to succeed (as far # as recipes are concerned), and the builds without a BUILD_TYPE From f9fc4b95bd7c0ea6f68f18ef0547ec8224532556 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 17:30:48 +0000 Subject: [PATCH 621/700] configure.ac: fix nut_enable_check_NIT --- configure.ac | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b45e03e172..c774a316c5 100644 --- a/configure.ac +++ b/configure.ac @@ -1220,7 +1220,24 @@ AM_CONDITIONAL(WITH_CPPCHECK, test "${WITH_CPPCHECK}" = "yes") dnl ---------------------------------------------------------------------- dnl checks related to --enable-check-NIT -NUT_ARG_ENABLE([check-NIT], [Run check-NIT among default checks], [no]) +AC_MSG_CHECKING(whether to run NIT among default make check target) +nut_enable_check_NIT="no" +AC_ARG_ENABLE([check-NIT], + AS_HELP_STRING([--enable-check-NIT], [Run check-NIT among default checks (no)]), +[ + case "${withval}" in + no) + AC_MSG_RESULT(no) + ;; + *) + AC_MSG_RESULT(yes) + nut_enable_check_NIT="yes" + ;; + esac +], [ + AC_MSG_RESULT(no) +]) + AM_CONDITIONAL(WITH_CHECK_NIT, test "${nut_enable_check_NIT}" = "yes") dnl ---------------------------------------------------------------------- From 00d38c3064e1ff0c8f0c2cea78135f846cc54f65 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 20:42:01 +0200 Subject: [PATCH 622/700] Update driver.list.in --- data/driver.list.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/driver.list.in b/data/driver.list.in index 7ea022f221..896540c18e 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -379,7 +379,7 @@ "EVER" "ups" "2" "DUO II Pro series" "USB port" "blazer_usb" "EVER" "ups" "2" "POWERLINE RT 1-3kVA series" "USB port" "blazer_usb" "EVER" "ups" "2" "POWERLINE RT 6-10kVA series" "USB port" "blazer_usb" -"EVER" "ups" "4" "ECO Pro AVR CDS series" "USB port" "usbhid-up" +"EVER" "ups" "4" "ECO Pro AVR CDS series" "USB port" "usbhid-ups" "Exide" "ups" "1" "NetUPS SE" "" "genericups upstype=15" "Exide" "ups" "4" "NetUPS SE 450/700/1000/1500" "" "upscode2" From bbd46b61fb1f13506f6d444b1a0fbd20c79c68e6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 18 Apr 2022 20:11:49 +0200 Subject: [PATCH 623/700] drivers/cyberpower-mib.c: update cyberpower_power_status[] with new values [#1377] --- drivers/cyberpower-mib.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/cyberpower-mib.c b/drivers/cyberpower-mib.c index 9caa8dbe42..aa43295a31 100644 --- a/drivers/cyberpower-mib.c +++ b/drivers/cyberpower-mib.c @@ -24,20 +24,26 @@ #include "cyberpower-mib.h" -#define CYBERPOWER_MIB_VERSION "0.5" +#define CYBERPOWER_MIB_VERSION "0.51" #define CYBERPOWER_OID_MODEL_NAME ".1.3.6.1.4.1.3808.1.1.1.1.1.1.0" /* CPS-MIB::ups */ #define CYBERPOWER_SYSOID ".1.3.6.1.4.1.3808.1.1.1" +/* https://www.cyberpowersystems.com/products/software/mib-files/ */ +/* Per CPS MIB 2.9 upsBaseOutputStatus OBJECT-TYPE: */ static info_lkp_t cyberpower_power_status[] = { - { 2, "OL", NULL, NULL }, - { 3, "OB", NULL, NULL }, - { 4, "OL BOOST", NULL, NULL }, - { 5, "OFF", NULL, NULL }, - { 7, "OL", NULL, NULL }, - { 1, "NULL", NULL, NULL }, - { 6, "OFF", NULL, NULL }, + { 1, "NULL", NULL, NULL }, /* unknown */ + { 2, "OL", NULL, NULL }, /* onLine */ + { 3, "OB", NULL, NULL }, /* onBattery */ + { 4, "OL BOOST", NULL, NULL }, /* onBoost */ + { 5, "OFF", NULL, NULL }, /* onSleep */ + { 6, "OFF", NULL, NULL }, /* off */ + { 7, "OL", NULL, NULL }, /* rebooting */ + { 8, "OL", NULL, NULL }, /* onECO */ + { 9, "OL BYPASS", NULL, NULL }, /* onBypass */ + { 10, "OL TRIM", NULL, NULL }, /* onBuck */ + { 11, "OL OVER", NULL, NULL }, /* onOverload */ { 0, NULL, NULL, NULL } } ; From 78ef33df07be82a1d744dd77d7f7859d4a925624 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 19:29:26 +0000 Subject: [PATCH 624/700] drivers/main.c: simplify detection of PROGNAME="lt-DRIVERNAME" making it more reliable for some platforms --- drivers/main.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/main.c b/drivers/main.c index f9938c766c..63d329b7c2 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -522,18 +522,17 @@ void do_upsconf_args(char *confupsname, char *var, char *val) /* Accomodate for libtool wrapped developer iterations * running e.g. `drivers/.libs/lt-dummy-ups` filenames */ - char buf[PATH_MAX]; - if (snprintfcat(buf, sizeof(buf), "lt-%s", val) < 0) - buf[0] = '\0'; - - upsdebugx(6, "progname: check '%s' vs '%s' vs '%s'", progname, val, buf); - if (strcmp(buf, progname) == 0) { - upsdebugx(1, "Seems this driver binary %s is a libtool " + size_t tmplen = strlen("lt-"); + if (strncmp("lt-", progname, tmplen) == 0 + && strcmp(val, progname + tmplen) == 0) { + /* debug level may be not initialized yet, and situation + * should not happen in end-user builds, so ok to yell: */ + upsdebugx(0, "Seems this driver binary %s is a libtool " "wrapped build for driver %s", progname, val); /* progname points to xbasename(argv[0]) in-place; * roll the pointer forward a bit, we know we can: */ - progname = progname + strlen("lt-"); + progname = progname + tmplen; } if (strcmp(val, progname) != 0) { From 84052e5b3d1ecd387abc6bdf62025489cee4d1b9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 24 Apr 2022 08:35:35 +0000 Subject: [PATCH 625/700] ci_build.sh: disable check-NIT by default in CI runs for now --- ci_build.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ci_build.sh b/ci_build.sh index 8dc0f1d54e..2dff9f53ae 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -743,7 +743,9 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp # TODO: Consider `--enable-maintainer-mode` to add recipes that # would quickly regenerate Makefile(.in) if you edit Makefile.am - CONFIG_OPTS+=("--enable-check-NIT") + # TODO: Resolve port-collision reliably (for multi-executor agents) + # and enable the test for CI runs. Bonus for making it quieter. + CONFIG_OPTS+=("--enable-check-NIT=no") if [ -n "${PYTHON-}" ]; then # WARNING: Watch out for whitespaces, not handled here! From 9c5fffc1829be36847ba4823ff7e544842861d33 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 24 Apr 2022 13:18:45 +0200 Subject: [PATCH 626/700] drivers/usb-common.{h,c} and USB-capable drivers: introduce warn_if_bad_usb_port_filename() Closes: #1368 --- drivers/bcmxcp_usb.c | 1 + drivers/blazer_usb.c | 3 ++- drivers/nutdrv_qx.c | 2 ++ drivers/richcomm_usb.c | 2 ++ drivers/riello_usb.c | 2 ++ drivers/tripplite_usb.c | 2 ++ drivers/usb-common.c | 35 +++++++++++++++++++++++++++++++++++ drivers/usb-common.h | 5 +++++ drivers/usbhid-ups.c | 1 + 9 files changed, 52 insertions(+), 1 deletion(-) diff --git a/drivers/bcmxcp_usb.c b/drivers/bcmxcp_usb.c index 49cf8ade58..e219714ce0 100644 --- a/drivers/bcmxcp_usb.c +++ b/drivers/bcmxcp_usb.c @@ -499,6 +499,7 @@ usb_dev_handle *nutusb_open(const char *port) int ret = 0; upsdebugx(1, "entering nutusb_open()"); + warn_if_bad_usb_port_filename(device_path); /* Initialize Libusb */ #if WITH_LIBUSB_1_0 diff --git a/drivers/blazer_usb.c b/drivers/blazer_usb.c index 8213b04b36..fc3f78bc6b 100644 --- a/drivers/blazer_usb.c +++ b/drivers/blazer_usb.c @@ -584,9 +584,10 @@ void upsdrv_initups(void) int ret, langid; char tbuf[255]; /* Some devices choke on size > 255 */ char *regex_array[7]; - char *subdrv = getval("subdriver"); + warn_if_bad_usb_port_filename(device_path); + regex_array[0] = getval("vendorid"); regex_array[1] = getval("productid"); regex_array[2] = getval("vendor"); diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index f151ed5017..337b42a4b6 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -2945,6 +2945,8 @@ void upsdrv_initups(void) /* USB */ #ifdef QX_USB + warn_if_bad_usb_port_filename(device_path); + #ifndef TESTING int ret, langid; char tbuf[255]; /* Some devices choke on size > 255 */ diff --git a/drivers/richcomm_usb.c b/drivers/richcomm_usb.c index f052225c74..f8da32e3de 100644 --- a/drivers/richcomm_usb.c +++ b/drivers/richcomm_usb.c @@ -571,6 +571,8 @@ void upsdrv_initups(void) char reply[REPLY_PACKETSIZE]; int i; + warn_if_bad_usb_port_filename(device_path); + for (i = 0; usb_device_open(&udev, &usbdevice, &device_matcher, &driver_callback) < 0; i++) { if ((i < 32) && (sleep(5) == 0)) { diff --git a/drivers/riello_usb.c b/drivers/riello_usb.c index 2c7097a86c..417ab89b0e 100644 --- a/drivers/riello_usb.c +++ b/drivers/riello_usb.c @@ -846,6 +846,8 @@ void upsdrv_initups(void) char *subdrv = getval("subdriver"); + warn_if_bad_usb_port_filename(device_path); + regex_array[0] = getval("vendorid"); regex_array[1] = getval("productid"); regex_array[2] = getval("vendor"); diff --git a/drivers/tripplite_usb.c b/drivers/tripplite_usb.c index 7162414598..cbaf73b6ae 100644 --- a/drivers/tripplite_usb.c +++ b/drivers/tripplite_usb.c @@ -1566,6 +1566,8 @@ void upsdrv_initups(void) char *value; int r; + warn_if_bad_usb_port_filename(device_path); + /* process the UPS selection options */ regex_array[0] = NULL; /* handled by USB IDs device table */ regex_array[1] = getval("productid"); diff --git a/drivers/usb-common.c b/drivers/usb-common.c index 1dedfb0757..5f2dc168d1 100644 --- a/drivers/usb-common.c +++ b/drivers/usb-common.c @@ -462,3 +462,38 @@ void USBFreeRegexMatcher(USBDeviceMatcher_t *matcher) free(data); free(matcher); } + +void warn_if_bad_usb_port_filename(const char *fn) { + /* USB drivers ignore the 'port' setting - log a notice + * if it is not "auto". Note: per se, ignoring the port + * (or internally the device_path variable from main.c) + * is currently not a bug and is actually documented at + * docs/config-notes.txt; however it is not something + * evident to users during troubleshooting a device. + * Documentation and common practice recommend port=auto + * so here we warn during driver start if it has some + * other value and users might think it is honoured. + */ + + if (!fn) { + upslogx(LOG_WARNING, + "WARNING: %s(): port argument was not " + "specified to the driver", + __func__); + return; + } + + if (!strcmp(fn, "auto")) + return; + + upslogx(LOG_WARNING, + "WARNING: %s(): port argument specified to\n" + " the driver is \"%s\" but USB drivers do " + "not use it and rely on\n" + " libusb walking all devices and matching " + "their identification metadata.\n" + " NUT documentation recommends port=\"auto\" " + "for USB devices to avoid confusion.", + __func__, fn); + return; +} diff --git a/drivers/usb-common.h b/drivers/usb-common.h index f3465ad2bc..cc9e8ceeda 100644 --- a/drivers/usb-common.h +++ b/drivers/usb-common.h @@ -513,4 +513,9 @@ int is_usb_device_supported(usb_device_id_t *usb_device_id_list, void nut_usb_addvars(void); +/* Tell the users that port="auto" should be used for USB, + * and other values are quietly ignored. Implemented once + * here, to use in several USB-capable drivers. */ +void warn_if_bad_usb_port_filename(const char *fn); + #endif /* NUT_USB_COMMON_H */ diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 86d3950084..dfe65a3e28 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -1017,6 +1017,7 @@ void upsdrv_initups(void) char *regex_array[7]; upsdebugx(1, "upsdrv_initups (non-SHUT)..."); + warn_if_bad_usb_port_filename(device_path); subdriver_matcher = &subdriver_matcher_struct; From d2aaebfc2b708f6d8c050e0c305618ba92dd9d5c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 24 Apr 2022 11:36:12 +0000 Subject: [PATCH 627/700] configure.ac: fix enableval vs withval --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index c774a316c5..4b4c0418d8 100644 --- a/configure.ac +++ b/configure.ac @@ -1225,7 +1225,7 @@ nut_enable_check_NIT="no" AC_ARG_ENABLE([check-NIT], AS_HELP_STRING([--enable-check-NIT], [Run check-NIT among default checks (no)]), [ - case "${withval}" in + case "${enableval}" in no) AC_MSG_RESULT(no) ;; @@ -1760,7 +1760,7 @@ AC_MSG_CHECKING(whether to strip debug symbols) AC_ARG_ENABLE(strip, AS_HELP_STRING([--enable-strip], [Strip debugging symbols from binaries (no)]), [ - case "${withval}" in + case "${enableval}" in no) AC_MSG_RESULT(no) ;; From 2c713dad42794b6aa8dc03d9d2bc8841c5668a40 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 24 Apr 2022 17:11:25 +0200 Subject: [PATCH 628/700] docs/man/victronups.txt: update link to cable pinout Closes: #1371 --- docs/man/victronups.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/man/victronups.txt b/docs/man/victronups.txt index 73982ed24d..17be94d7aa 100644 --- a/docs/man/victronups.txt +++ b/docs/man/victronups.txt @@ -28,7 +28,8 @@ CABLING If your Victron cable is broken or missing, use this diagram to build a clone: -docs/cables/victron.txt +* https://github.com/networkupstools/nut/blob/master/docs/cables/ge-imv-victron.txt +* link:docs/cables/ge-imv-victron.txt[] EXTRA ARGUMENTS --------------- From b065dfa326fa24a34bce801aed8f59e8fc0f6b68 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 24 Apr 2022 17:21:04 +0200 Subject: [PATCH 629/700] docs/nut-qa.txt: mention "make check-NIT-devel" helper target --- docs/nut-qa.txt | 4 ++++ docs/nut.dict | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/nut-qa.txt b/docs/nut-qa.txt index c728650831..ad50881fe8 100644 --- a/docs/nut-qa.txt +++ b/docs/nut-qa.txt @@ -130,6 +130,10 @@ FIXME (POST): it can also be added to default `make check` activities by running `configure --enable-check-NIT` + * Note that developers updating components which directly impact NIT runs + may benefit from `make check-NIT-devel` target, to rebuild the `upsd`, + `dummy-ups`, `cppnit` and other programs used in the test as they iterate. + - link:https://bugzilla.redhat.com/buglist.cgi?component=nut[Redhat / Fedora Bug tracker] - link:https://www.openhub.net/p/nut[Black Duck Open Hub] (formerly Ohloh.net) diff --git a/docs/nut.dict b/docs/nut.dict index 02d3335d06..9788d0f6fc 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2950 utf-8 +personal_ws-1.1 en 2951 utf-8 AAS ABI ACFAIL @@ -1629,6 +1629,7 @@ coverity cp cpp cppcheck +cppnit cppunit cpqpower cpsups From eb07738b12b1454c06d969a49c99ebf5a3f5ca95 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 24 Apr 2022 22:23:17 +0200 Subject: [PATCH 630/700] docs/man/Makefile.am: introduce A2X_VERBOSE --- docs/man/Makefile.am | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 2385bec40e..87eba00863 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -899,7 +899,8 @@ DOCBUILD_BEGIN = { \ if ! test -s "$(builddir)/$<" && test -s "$(srcdir)/`basename $<`" ; then \ ln -fs "$(srcdir)/`basename $<`" "$(builddir)/" ; \ fi ; \ - fi ; \ + fi ; \ + A2X_VERBOSE="$(ASCIIDOC_VERBOSE)"; if [ "$(V)" = 1 ]; then A2X_VERBOSE="--verbose"; fi; \ } # Note that documents with sub-pages (see LIBNUTCLIENT_*_DEPS above) @@ -922,16 +923,16 @@ DOCBUILD_END = { \ @A2X_OUTDIR="tmp/man-html.$(@F).$$$$" ; \ echo " DOC-MAN-HTML Generating $@"; \ $(DOCBUILD_BEGIN) ; RES=0; \ - $(ASCIIDOC) --backend=xhtml11 \ - --attribute localdate=`TZ=UTC date +%Y-%m-%d` \ - --attribute localtime=`TZ=UTC date +%H:%M:%S` \ + $(ASCIIDOC) --backend=xhtml11 $${A2X_VERBOSE} \ + --attribute localdate="`TZ=UTC date +%Y-%m-%d`" \ + --attribute localtime="`TZ=UTC date +%H:%M:%S`" \ --attribute nutversion="@PACKAGE_VERSION@" \ -o "$${A2X_OUTDIR}/$(@F)" $< || RES=$$? ; \ $(DOCBUILD_END) ; exit $$RES ### Prior to Asciidoc ~8.6.8, the --destination-dir flag didn't seem to affect the location of the intermediate .xml file. ### This parameter is currently required; see docs/Makefile.am for more detail. -A2X_MANPAGE_OPTS = --doctype manpage --format manpage \ +A2X_MANPAGE_OPTS = --doctype manpage --format manpage $${A2X_VERBOSE} \ --xsltproc-opts="--nonet" \ --attribute mansource="Network UPS Tools" \ --attribute manversion="@PACKAGE_VERSION@" \ From 42c5750af1fdf6c435aad3604794f8c14509042e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 24 Apr 2022 22:26:56 +0200 Subject: [PATCH 631/700] docs/man/Makefile.am: refactor LINKMAN_INCLUDE_CONSUMERS --- docs/man/Makefile.am | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 87eba00863..3ba1155f67 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -749,6 +749,7 @@ HTML_MANS = \ # list of drivers with `- linkman:nutupsdrv[8]` entry # To regenerate these files, do `make distclean` first LINKMAN_INCLUDE_GENERATED = linkman-driver-names.txt linkman-drivertool-names.txt +LINKMAN_INCLUDE_CONSUMERS = index.txt upsd.txt nutupsdrv.txt linkman-driver-names.txt: @if test x"$(srcdir)" != x"$(builddir)" ; then \ @@ -779,9 +780,7 @@ linkman-drivertool-names.txt: # Dependencies are about particular filenames, since over time # we might have several use-cases for LINKMAN_INCLUDE_GENERATED: -index.txt index.html \ -upsd.txt upsd.html upsd.8 upsd.pdf \ -nutupsdrv.txt nutupsdrv.html nutupsdrv.8 nutupsdrv.pdf : linkman-driver-names.txt linkman-drivertool-names.txt +$(LINKMAN_INCLUDE_CONSUMERS): linkman-driver-names.txt linkman-drivertool-names.txt # These files are generated when we build from git source so not tracked in # git, but we want it as part of tarball just in case the end-user's build From 1813af3f9819c4698ea458ad12085c2052066cb0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 24 Apr 2022 22:28:39 +0200 Subject: [PATCH 632/700] docs/man/nutupsdrv.txt + index.txt + upsd.txt: use {builddir}/linkman-*.txt for included data --- docs/man/Makefile.am | 13 +++++++++---- docs/man/index.txt | 4 ++-- docs/man/nutupsdrv.txt | 4 ++-- docs/man/upsd.txt | 4 ++-- docs/nut.dict | 3 ++- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am index 3ba1155f67..0d693a355f 100644 --- a/docs/man/Makefile.am +++ b/docs/man/Makefile.am @@ -783,10 +783,7 @@ linkman-drivertool-names.txt: $(LINKMAN_INCLUDE_CONSUMERS): linkman-driver-names.txt linkman-drivertool-names.txt # These files are generated when we build from git source so not tracked in -# git, but we want it as part of tarball just in case the end-user's build -# does not enable something related to docs: -EXTRA_DIST += $(LINKMAN_INCLUDE_GENERATED) - +# git, and not part of tarball (to be in builddir) - so not in EXTRA_DIST. DISTCLEANFILES = $(LINKMAN_INCLUDE_GENERATED) all: @@ -918,6 +915,10 @@ DOCBUILD_END = { \ fi ; \ } +### Regarding absolute paths in attributes below: by default asciidoc +### resolves include paths relative to the main document, so while we +### see the relative builddir as "." in the makefile, for included +### data it would mean the source directory where the .txt resides. .txt.html: @A2X_OUTDIR="tmp/man-html.$(@F).$$$$" ; \ echo " DOC-MAN-HTML Generating $@"; \ @@ -926,6 +927,8 @@ DOCBUILD_END = { \ --attribute localdate="`TZ=UTC date +%Y-%m-%d`" \ --attribute localtime="`TZ=UTC date +%H:%M:%S`" \ --attribute nutversion="@PACKAGE_VERSION@" \ + --attribute srcdir="$(abs_srcdir)" \ + --attribute builddir="$(abs_builddir)" \ -o "$${A2X_OUTDIR}/$(@F)" $< || RES=$$? ; \ $(DOCBUILD_END) ; exit $$RES @@ -936,6 +939,8 @@ A2X_MANPAGE_OPTS = --doctype manpage --format manpage $${A2X_VERBOSE} \ --attribute mansource="Network UPS Tools" \ --attribute manversion="@PACKAGE_VERSION@" \ --attribute manmanual="NUT Manual" \ + --attribute srcdir="$(abs_srcdir)" \ + --attribute builddir="$(abs_builddir)" \ --destination-dir="$${A2X_OUTDIR}" .txt.1: diff --git a/docs/man/index.txt b/docs/man/index.txt index c7a4bb53cc..65a5caf286 100644 --- a/docs/man/index.txt +++ b/docs/man/index.txt @@ -50,13 +50,13 @@ CGI programs Driver control: ~~~~~~~~~~~~~~~ -include::linkman-drivertool-names.txt[] +include::{builddir}/linkman-drivertool-names.txt[] Drivers: ~~~~~~~~ - linkman:nutupsdrv[8] -include::linkman-driver-names.txt[] +include::{builddir}/linkman-driver-names.txt[] [[Developer_man]] Developer manual pages diff --git a/docs/man/nutupsdrv.txt b/docs/man/nutupsdrv.txt index fe86d71945..01ddbf57a7 100644 --- a/docs/man/nutupsdrv.txt +++ b/docs/man/nutupsdrv.txt @@ -227,12 +227,12 @@ CGI programs: Driver control: ~~~~~~~~~~~~~~~ -include::linkman-drivertool-names.txt[] +include::{builddir}/linkman-drivertool-names.txt[] Drivers: ~~~~~~~~ -include::linkman-driver-names.txt[] +include::{builddir}/linkman-driver-names.txt[] Internet resources: ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/man/upsd.txt b/docs/man/upsd.txt index af322bef9d..25d92ca171 100644 --- a/docs/man/upsd.txt +++ b/docs/man/upsd.txt @@ -174,13 +174,13 @@ CGI programs: Driver control: ~~~~~~~~~~~~~~~ -include::linkman-drivertool-names.txt[] +include::{builddir}/linkman-drivertool-names.txt[] Drivers: ~~~~~~~~ - linkman:nutupsdrv[8] -include::linkman-driver-names.txt[] +include::{builddir}/linkman-driver-names.txt[] Internet resources: ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/nut.dict b/docs/nut.dict index 9788d0f6fc..30dc289ed9 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2951 utf-8 +personal_ws-1.1 en 2952 utf-8 AAS ABI ACFAIL @@ -1527,6 +1527,7 @@ buflen bugfix bugfixes buildbots +builddir bullseye busybox bv From 0ee503970594a58ecc9ac3fb6cd72d2d85aa1432 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 24 Apr 2022 22:51:22 +0200 Subject: [PATCH 633/700] ci_build.sh: introduce CI_BUILDDIR to handle out-of-tree builds other than distcheck --- ci_build.sh | 73 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/ci_build.sh b/ci_build.sh index 2dff9f53ae..9a7dcc622c 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -10,12 +10,18 @@ ################################################################################ set -e +SCRIPTDIR="`dirname "$0"`" +SCRIPTDIR="`cd "$SCRIPTDIR" && pwd`" # Quick hijack for interactive development like this: # BUILD_TYPE=fightwarn-clang ./ci_build.sh # or to quickly hit the first-found errors in a larger matrix # (and then easily `make` to iterate fixes), like this: # CI_REQUIRE_GOOD_GITIGNORE="false" CI_FAILFAST=true DO_CLEAN_CHECK=no BUILD_TYPE=fightwarn ./ci_build.sh +# For out-of-tree builds you can specify a CI_BUILDDIR (absolute or relative +# to SCRIPTDIR - not current path), or just call .../ci_build.sh while being +# in a different directory and then it would be used with a warning. This may +# require that you `make distclean` the original source checkout first. case "$BUILD_TYPE" in fightwarn) ;; # for default compiler fightwarn-all) @@ -111,6 +117,34 @@ esac # (allowing to rebuild interactively and investigate that set-up)? [ -n "${CI_FAILFAST-}" ] || CI_FAILFAST=false +# By default we configure and build in the same directory as source; +# and a `make distcheck` handles how we build from a tarball. +# However there are also cases where source is prepared (autogen) once, +# but is built in various directories with different configurations. +# This is something to test via CI, that recipes are not broken for +# such use-case. Note the path should be in .gitignore, e.g. equal to +# or under ./tmp/ for CI_REQUIRE_GOOD_GITIGNORE sanity checks to pass. +case "${CI_BUILDDIR-}" in + "") # Not set, likeliest case + CI_BUILDDIR="`pwd`" + if [ x"${SCRIPTDIR}" = x"${CI_BUILDDIR}" ] ; then + CI_BUILDDIR="." + else + echo "=== WARNING: This build will use '${CI_BUILDDIR}'" + echo "=== for an out-of-tree build of NUT with sources located" + echo "=== in '${SCRIPTDIR}'" + echo "=== PRESS CTRL+C NOW if you did not mean this! (Sleeping 5 sec)" + + sleep 5 + fi + ;; + ".") ;; # Is SCRIPTDIR, in-tree build + /*) ;; # Absolute path located somewhere else + *) # Non-trivial, relative to SCRIPTDIR, may not exist yet + CI_BUILDDIR="${SCRIPTDIR}/${CI_BUILDDIR}" + ;; +esac + [ -n "$MAKE" ] || [ "$1" = spellcheck ] || MAKE=make [ -n "$GGREP" ] || GGREP=grep @@ -282,15 +316,17 @@ fi if [ -z "${PKG_CONFIG-}" ]; then # Default to using one from PATH, if any - mostly for config tuning done # below in this script - # DO NOT "export" it here so ./configure script can find one for the build + # DO NOT "export" it here so configure script can find one for the build PKG_CONFIG="pkg-config" fi configure_nut() { - local CONFIGURE_SCRIPT=./configure + local CONFIGURE_SCRIPT="configure" + cd "$SCRIPTDIR" + if [[ "$CI_OS_NAME" == "windows" ]] ; then find . -ls - CONFIGURE_SCRIPT=./configure.bat + CONFIGURE_SCRIPT="configure.bat" fi if [ ! -s "$CONFIGURE_SCRIPT" ]; then @@ -306,6 +342,15 @@ configure_nut() { fi || exit fi + if [ "${CI_BUILDDIR}" != "." ]; then + # Per above, we always start this routine in absolute $SCRIPTDIR + echo "=== Running NUT build out-of-tree in ${CI_BUILDDIR}" + mkdir -p "${CI_BUILDDIR}" && cd "${CI_BUILDDIR}" || exit + CONFIGURE_SCRIPT="${SCRIPTDIR}/${CONFIGURE_SCRIPT}" + else + CONFIGURE_SCRIPT="./${CONFIGURE_SCRIPT}" + fi + # Help copy-pasting build setups from CI logs to terminal: local CONFIG_OPTS_STR="`for F in "${CONFIG_OPTS[@]}" ; do echo "'$F' " ; done`" ### | tr '\n' ' '`" while : ; do # Note the CI_SHELL_IS_FLAKY=true support below @@ -327,9 +372,9 @@ configure_nut() { # Real-life story from the trenches: there are weird systems # which fail ./configure in random spots not due to script's # quality. Then we'd just loop here. - echo "WOULD BE FATAL: FAILED ($RES_CFG) to ./configure ${CONFIG_OPTS[*]} -- but asked to loop trying" >&2 + echo "WOULD BE FATAL: FAILED ($RES_CFG) to $CONFIGURE_SCRIPT ${CONFIG_OPTS[*]} -- but asked to loop trying" >&2 else - echo "FATAL: FAILED ($RES_CFG) to ./configure ${CONFIG_OPTS[*]}" >&2 + echo "FATAL: FAILED ($RES_CFG) to $CONFIGURE_SCRIPT ${CONFIG_OPTS[*]}" >&2 echo "If you are sure this is not a fault of scripting or config option, try" >&2 echo " CI_SHELL_IS_FLAKY=true $0" exit $RES_CFG @@ -1232,6 +1277,7 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp } echo "=== Configured NUT_SSL_VARIANT='$NUT_SSL_VARIANT', $BUILDSTODO build variants (including this one) remaining to complete; trying to build..." + cd "${CI_BUILDDIR}" build_to_only_catch_errors && { SUCCEEDED="${SUCCEEDED} NUT_SSL_VARIANT=${NUT_SSL_VARIANT}[build]" } || { @@ -1364,6 +1410,7 @@ default|default-alldrv|default-alldrv:no-distcheck|default-all-errors|default-sp } echo "=== Configured NUT_USB_VARIANT='$NUT_USB_VARIANT', $BUILDSTODO build variants (including this one) remaining to complete; trying to build..." + cd "${CI_BUILDDIR}" build_to_only_catch_errors && { SUCCEEDED="${SUCCEEDED} NUT_USB_VARIANT=${NUT_USB_VARIANT}[build]" } || { @@ -1504,6 +1551,8 @@ bindings) sleep 5 fi echo "" + + cd "${SCRIPTDIR}" if [ -s Makefile ]; then # Let initial clean-up be at default verbosity echo "=== Starting initial clean-up (from old build products)" @@ -1513,13 +1562,21 @@ bindings) ./autogen.sh + if [ "${CI_BUILDDIR}" != "." ]; then + # Per above, we always start this routine in absolute $SCRIPTDIR + echo "=== Running NUT build out-of-tree in ${CI_BUILDDIR}" + mkdir -p "${CI_BUILDDIR}" && cd "${CI_BUILDDIR}" || exit + CONFIGURE_SCRIPT="${SCRIPTDIR}/configure" + else + CONFIGURE_SCRIPT="./configure" + fi + # NOTE: Default NUT "configure" actually insists on some features, # like serial port support unless told otherwise, or docs if possible. # Below we aim for really fast iterations of C/C++ development so # enable whatever is auto-detectable (except docs), and highlight - # any warnings if we can: - #./configure - ./configure --enable-Wcolor \ + # any warnings if we can. + ${CONFIGURE_SCRIPT} --enable-Wcolor \ --with-all=auto --with-cgi=auto --with-serial=auto \ --with-dev=auto --with-doc=skip \ --enable-check-NIT --enable-maintainer-mode From ee4b98b2326957d0f626ea762a80fa9777012f18 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 24 Apr 2022 23:25:48 +0200 Subject: [PATCH 634/700] ci_build.sh: prepare for CI_BUILDDIR=obj --- .gitignore | 1 + ci_build.sh | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9d63328b36..660f49f9dc 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ .libs/ .inst/ /tmp/ +/obj/ /_install_pkgprotodir/ Makefile Makefile.in diff --git a/ci_build.sh b/ci_build.sh index 9a7dcc622c..996d71dd65 100755 --- a/ci_build.sh +++ b/ci_build.sh @@ -18,10 +18,12 @@ SCRIPTDIR="`cd "$SCRIPTDIR" && pwd`" # or to quickly hit the first-found errors in a larger matrix # (and then easily `make` to iterate fixes), like this: # CI_REQUIRE_GOOD_GITIGNORE="false" CI_FAILFAST=true DO_CLEAN_CHECK=no BUILD_TYPE=fightwarn ./ci_build.sh +# # For out-of-tree builds you can specify a CI_BUILDDIR (absolute or relative # to SCRIPTDIR - not current path), or just call .../ci_build.sh while being # in a different directory and then it would be used with a warning. This may -# require that you `make distclean` the original source checkout first. +# require that you `make distclean` the original source checkout first: +# CI_BUILDDIR=obj BUILD_TYPE=default-all-errors ./ci_build.sh case "$BUILD_TYPE" in fightwarn) ;; # for default compiler fightwarn-all) @@ -123,7 +125,8 @@ esac # but is built in various directories with different configurations. # This is something to test via CI, that recipes are not broken for # such use-case. Note the path should be in .gitignore, e.g. equal to -# or under ./tmp/ for CI_REQUIRE_GOOD_GITIGNORE sanity checks to pass. +# or under ./tmp/ or ./obj/ for the CI_REQUIRE_GOOD_GITIGNORE sanity +# checks to pass. case "${CI_BUILDDIR-}" in "") # Not set, likeliest case CI_BUILDDIR="`pwd`" From 6213c98d21576f55ed515f8e4d56a671fa77a8fb Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sun, 24 Apr 2022 23:40:07 +0200 Subject: [PATCH 635/700] Jenkinsfile-dynamatrix: run some tests with CI_BUILDDIR --- Jenkinsfile-dynamatrix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Jenkinsfile-dynamatrix b/Jenkinsfile-dynamatrix index a5975c6b31..9f50debd77 100644 --- a/Jenkinsfile-dynamatrix +++ b/Jenkinsfile-dynamatrix @@ -983,6 +983,9 @@ set | sort -n """ ['LANG=C','LC_ALL=C','TZ=UTC', 'BUILD_TYPE=default-all-errors', 'BUILD_WARNFATAL=yes', + // Build in a subdirectory to check that out-of-dir + // builds are healthy too + 'CI_BUILDDIR=obj', // NOTE: "gcc-hard" warnings are still not as picky // as "clang-hard" and do not differ much from the // "gcc-medium" definition currently: From a5a8ac441a41ea1029c5f5c84011d6e8517c8f83 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 25 Apr 2022 10:25:57 +0200 Subject: [PATCH 636/700] Jenkinsfile-dynamatrix: add a CI_BUILDDIR=obj into docs scenario; name "out-of-tree builds" as such --- Jenkinsfile-dynamatrix | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile-dynamatrix b/Jenkinsfile-dynamatrix index 9f50debd77..09212c40cc 100644 --- a/Jenkinsfile-dynamatrix +++ b/Jenkinsfile-dynamatrix @@ -661,7 +661,7 @@ set | sort -n """ 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build ] // one slowBuild filter configuration - ,[name: 'A build with all docs types on capable systems, and a distcheck (must pass)', + ,[name: 'An out-of-tree build with all docs types on capable systems, and a distcheck (must pass)', // TODO: This is a recipe (and target OS) test for ability to build // the docs without error; it should not iterate compilers (maybe // iterate docs tools though, if we were to support many backends?) @@ -685,6 +685,9 @@ set | sort -n """ ], dynamatrixAxesCommonEnv: [ ['LANG=C','LC_ALL=C','TZ=UTC', + // Build in a subdirectory to check that out-of-dir + // builds are healthy too + 'CI_BUILDDIR=obj', 'BUILD_WARNFATAL=yes','BUILD_WARNOPT=minimal' ] ], @@ -964,7 +967,7 @@ set | sort -n """ 'bodyParStages': dynacfgPipeline.slowBuildDefaultBody_ci_build ] // one slowBuild filter configuration - ,[name: 'GNU C standard builds with fatal warnings with GCC, without distcheck and docs (must pass)', + ,[name: 'GNU C standard out-of-tree builds with fatal warnings with GCC, without distcheck and docs (must pass)', disabled: dynacfgPipeline.disableSlowBuildCIBuild, //branchRegexSource: ~/^(PR-.+|fightwarn.*)$/, //branchRegexTarget: dynacfgPipeline.branchStableRegex, From 6ba7db1f51161ae4f0d4ae477f851ab3bcc397b4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 25 Apr 2022 09:15:05 +0000 Subject: [PATCH 637/700] docs/nut.dict: add uniq --- docs/nut.dict | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/nut.dict b/docs/nut.dict index 30dc289ed9..49e5ee71f9 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2952 utf-8 +personal_ws-1.1 en 2953 utf-8 AAS ABI ACFAIL @@ -2774,6 +2774,7 @@ undervoltage unescaped uninstall uninterruptible +uniq unistd unitidentify unmapped From 922bc7188bd8a55f3a56b710a3aa41f822c2c556 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 25 Apr 2022 09:20:15 +0000 Subject: [PATCH 638/700] docs/nut.dict: add wc --- docs/nut.dict | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/nut.dict b/docs/nut.dict index 49e5ee71f9..730ec9314f 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2953 utf-8 +personal_ws-1.1 en 2954 utf-8 AAS ABI ACFAIL @@ -2891,6 +2891,7 @@ voltronic von wDescriptorLength wakeup +wc wchar webserver wf From e195389cc637e4540ce8a1265d25bef186dfa6a4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 10:02:57 +0000 Subject: [PATCH 639/700] driver.list.in: Add HCL info for nJoy Aten PRO 3000 (SNMP) Closes: #1281 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 80a6586203..0c41d43d64 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -825,6 +825,7 @@ "nJoy" "ups" "2" "Keen 600" "USB" "blazer_ser port=/dev/ttyUSB0" # https://www.njoy.ro/UPS/keen-600 "nJoy" "ups" "2" "Keen 600" "USB" "nutdrv_qx port=/dev/ttyUSB0" # https://www.njoy.ro/UPS/keen-600 +"nJoy" "ups" "3" "Aten PRO 3000" "SNMP" "snmp-ups" # At least basic status is served; https://www.njoy.global/product/aten-pro-3000 "Novex" "ups" "1" "NUPS-650" "USB" "blazer_usb protocol=megatec" # http://komp.1k.by/periphery-ups/novex/Novex_NUPS_650-130052.html From 93eea0251f27f64dd76f1294d1f3143d40aeb368 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 10:19:45 +0000 Subject: [PATCH 640/700] driver.list.in: Add HCL info for Hunnox devices (and new protocol subdriver) (USB) [#638] --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 0c41d43d64..7d6a227082 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -465,6 +465,8 @@ "Huawei" "ups" "4" "UPS5000-E" "" "snmp-ups" +"Hunnox" "ups" "2" "HNX-850" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 + "IBM" "ups" "5" "Various" "USB port" "usbhid-ups" "IBM" "ups" "5" "Various" "Serial port" "mge-shut" "IBM" "pdu" "1" "Blade Center Management Module" "15 outlets" "powerman-pdu (experimental)" From dc0ae878836a890264f204b72381a769ab73cc76 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 10:30:11 +0000 Subject: [PATCH 641/700] driver.list.in: Add HCL info for DigiTECH 650VA (USB) Closes: #674 --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 7d6a227082..e9d7e26fc7 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -276,6 +276,8 @@ "Digital Loggers" "pdu" "1" "LPC, EPCR2, DIN" "8 outlets" "powerman-pdu (experimental)" +"DigiTECH" "ups" "2" "Computer 650VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/674 (may need longer pollinterval) + "Digitus" "ups" "1" "DN-170014" "USB" "richcomm_usb" # http://www.digitus.info/en/products/professional-network/security-and-surveillance/power-supply/uninterrruptable-power-supplies/ups-uninterruptible-power-systems-dn-170014/section/prof/ "Digitus" "ups" "2" "DN-170020" "" "blazer_ser" From 3929bdf63cb77fc0ea5d79a635e7d298859b462c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 10:34:44 +0000 Subject: [PATCH 642/700] driver.list.in: Add HCL info for Crown CMU-SP1200IEC (USB) Closes: #1014 --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index e9d7e26fc7..925d2e94d2 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -195,6 +195,8 @@ "COVER ENERGY SA" "ups" "2" "COVER PRM 1K/2K/3K/6K/10K EC" "" "blazer_usb" "COVER ENERGY SA" "ups" "2" "COVER PRM 6K/10K PR" "" "blazer_usb" +"Crown" "ups" "2" "CMU-SP1200IEC" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/1014 + "Cyber Power Systems" "ups" "1" "550SL" "" "genericups upstype=7" "Cyber Power Systems" "ups" "1" "725SL" "" "genericups upstype=7" "Cyber Power Systems" "ups" "1" "CPS1100AVR" "" "powerpanel" From ecc5acd59f7103109a162b8c213c006663f73e65 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 11:28:53 +0000 Subject: [PATCH 643/700] driver.list.in: Add HCL info for Greencell Micropower 600 (USB) Closes: #1080 --- data/driver.list.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 925d2e94d2..1fdce84d35 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -435,6 +435,9 @@ "Grafenthal" "ups" "2" "PR-3000-HS" "SNMP/Web Minislot card (ref 149G0006)" "snmp-ups" # http://grafenthal.de/produkte/usv/online/pr-hs-serie/pr-3000-hs/?L=3et8 +"Greencell" "ups" "2" "Micropower 600" "USB" "nutdrv_qx" # https://github.com/networkupstools/nut/issues/1080 +"Greencell" "ups" "2" "Micropower 600" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/1080 + "Gtec" "ups" "2" "ZP120N-1K / ZP120N-1KS / ZP120N-2K / ZP120N-2KS / ZP120N-3K / ZP120N-3KS" "" "blazer_usb" "Gtec" "ups" "2" "ZP120N-6K / ZP120N-6KS / ZP120N-10K-11 / ZP120N-10KS-11" "" "blazer_usb" "Gtec" "ups" "2" "ZP120N-10K-31-00 / ZP120N-10K-31-07 / ZP120N-10K-31-09 / ZP120N-10K-31-99 / ZP120N-20K" "USB port" "blazer_usb" From f69a7d6150ebda61f830472c708e55f6f79de979 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 11:28:53 +0000 Subject: [PATCH 644/700] driver.list.in: Add HCL info for CPC, ARES and Powercool models with hunnox protocol (USB) Closes: #537 --- data/driver.list.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 1fdce84d35..6c0b420ae1 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -96,6 +96,8 @@ "Appro" "pdu" "1" "SWPDU" "48 outlets" "powerman-pdu (experimental)" +"ARES" "ups" "2" "AR265i" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 did not explicitly mention the driver parameters, but other reports in the discussion did + "ARTronic" "ups" "2" "ARTon Millenium 1/2/3/6/10 kVA" "Serial" "blazer_ser" "ARTronic" "ups" "2" "ARTon Millenium 3.1 10/15/20 kVA" "Serial" "blazer_ser" "ARTronic" "ups" "2" "ARTon Titanium 6/10 kVA" "Serial" "blazer_ser" @@ -195,6 +197,8 @@ "COVER ENERGY SA" "ups" "2" "COVER PRM 1K/2K/3K/6K/10K EC" "" "blazer_usb" "COVER ENERGY SA" "ups" "2" "COVER PRM 6K/10K PR" "" "blazer_usb" +"CPC" "ups" "2" "RACK850VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 product=MEC0003 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/537 + "Crown" "ups" "2" "CMU-SP1200IEC" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/1014 "Cyber Power Systems" "ups" "1" "550SL" "" "genericups upstype=7" @@ -905,6 +909,10 @@ "Powercom" "ups" "1" "BNT-xxxAP" "USB (product id: 0001)" "usbhid-ups (experimental)" "Powercool" "ups" "1" "350VA to 1600VA" "USB" "nutdrv_atcl_usb" +"Powercool" "ups" "2" "650VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 product=MEC0003 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/537 +"Powercool" "ups" "2" "650VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 bus=001 product=MEC0003 protocol=hunnox langid_fix=0x0409 novendor norating noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/537 +"Powercool" "ups" "2" "1500VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 product=MEC0003 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/537 - e.g. battery percentage is not being returned +"Powercool" "ups" "2" "2000VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 product=MEC0003 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/537 "POWEREX" "ups" "2" "VI 1000 LED" "" "blazer_usb" From 2b239931856b0918f4b1c4e2c95bb5a3d7716899 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 11:39:16 +0000 Subject: [PATCH 645/700] driver.list.in: Add HCL info for DEXP MIX 850VA (USB) Closes: #721 --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 6c0b420ae1..4e9a877e59 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -280,6 +280,8 @@ "Deltec" "ups" "1" "PowerRite Pro II" "" "genericups upstype=15" "Deltec" "ups" "4" "PRM 450/700/1000/1500" "" "upscode2" +"DEXP" "ups" "2" "MIX 850VA" "USB" "blazer_usb langid_fix=0x0409 runtimecal=240,100,720,50 default.battery.voltage.high=2.27 default.battery.voltage.low=1.72" # https://github.com/networkupstools/nut/issues/721 + "Digital Loggers" "pdu" "1" "LPC, EPCR2, DIN" "8 outlets" "powerman-pdu (experimental)" "DigiTECH" "ups" "2" "Computer 650VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/674 (may need longer pollinterval) From 3b9e50d1b3c9ecc22c11ca16696a3766cd4c2018 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 12:00:17 +0000 Subject: [PATCH 646/700] driver.list.in: Add HCL info for Huawei UPS2000 series (modbus) [#954 #1066 #1198 #1017] --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 4e9a877e59..3fac5f4c7c 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -477,6 +477,7 @@ "HPE" "pdu" "5" "Various (SNMP mode)" "" "snmp-ups" "Huawei" "ups" "4" "UPS5000-E" "" "snmp-ups" +"Huawei" "ups" "3" "UPS2000-G and UPS2000-A series" "MODBUS (USB with Linux 5.12+, or Serial RS-232)" "huawei-ups2000" # https://github.com/networkupstools/nut/issues/1066 https://github.com/networkupstools/nut/pull/1198 https://github.com/networkupstools/nut/pull/954 https://github.com/networkupstools/nut/issues/1017 "Hunnox" "ups" "2" "HNX-850" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 From 4c3e193ff9908964e785958511782f56a9bad18b Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 12:09:08 +0000 Subject: [PATCH 647/700] driver.list.in: Update comment for Tripp_Lite SMX500RT1U (USB) [#584] --- data/driver.list.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/driver.list.in b/data/driver.list.in index 3fac5f4c7c..8b605aacaa 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -1197,7 +1197,7 @@ "Tripp Lite" "ups" "3" "SMX3000RT2UTAA" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=4396 "Tripp Lite" "ups" "3" "SMX3000XLRT2U" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtSeriesID=933&txtModelID=2694 "Tripp Lite" "ups" "3" "SMX3000XLRT2UA" "USB (protocol 3015)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=5658 -"Tripp Lite" "ups" "3" "SMX500RT1U" "USB (older; product ID 0001, protocol 3005)" "tripplite_usb" # https://www.tripplite.com/support/product/part-number/SMX500RT1U +"Tripp Lite" "ups" "3" "SMX500RT1U" "USB (older; product ID 0001, protocol 3005)" "tripplite_usb" # https://www.tripplite.com/support/product/part-number/SMX500RT1U https://github.com/networkupstools/nut/pull/584 "Tripp Lite" "ups" "3" "SMX500RT1U" "USB (newer; protocol/product ID 3005)" "usbhid-ups" # https://www.tripplite.com/support/product/part-number/SMX500RT1U "Tripp Lite" "ups" "3" "SMX750SLT" "USB (protocol 3014)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3021 "Tripp Lite" "ups" "3" "SU750RTXL2U" "USB (protocol 4001)" "usbhid-ups" # http://www.tripplite.com/en/products/model.cfm?txtModelID=3194 From 54d65e9f676fd69c0df736223e4babd6bee2c0ac Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 12:18:03 +0000 Subject: [PATCH 648/700] driver.list.in: Add HCL info for APC AP9584 Serial-to-USB kit Closes: #181 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 8b605aacaa..2829368617 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -58,6 +58,7 @@ "AEG Power Solutions" "ups" "3" "PROTECT NAS" "" "usbhid-ups" "AEG Power Solutions" "ups" "3" "PROTECT B" "" "usbhid-ups" +"APC" "ups" "3" "APC AP9584 Serial-to-USB kit" "USB" "usbhid-ups" # Allows USB connections to Serial-port APC devices, https://github.com/networkupstools/nut/pull/181 "APC" "ups" "2" "Back-UPS 1200BR (Microsol)" "" "solis" "APC" "ups" "2" "Back-UPS BZ2200BI-BR (Microsol)" "" "solis" "APC" "ups" "1" "Back-UPS Pro" "" "apcsmart" From 82a0fe889a2e9caaff5f566767a73e54a279f00c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 13:02:10 +0000 Subject: [PATCH 649/700] driver.list.in: Add HCL info for Ablerex MARS MS3000RT (Serial) Closes: #449 --- data/driver.list.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/driver.list.in b/data/driver.list.in index 2829368617..7813497a51 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -35,9 +35,10 @@ # Duplicate text in the last field will be cooked out during the conversion # to HTML with ROWSPAN magic. They must be an exact match for this to work. -"Ablerex" "ups" "2" "MS-RT" "" "blazer_ser" "Ablerex" "ups" "2" "625L" "USB" "blazer_usb" "Ablerex" "ups" "2" "Hope Office 400/600" "" "blazer_ser" +"Ablerex" "ups" "2" "MARS MS3000RT" "" "blazer_ser" +"Ablerex" "ups" "2" "MS-RT" "" "blazer_ser" "Ablerex" "ups" "2" "MP series" "USB" "nutdrv_qx" "Ablerex" "ups" "2" "ARES Plus series" "USB" "nutdrv_qx" "Ablerex" "ups" "2" "MSII series" "USB" "nutdrv_qx" @@ -45,7 +46,6 @@ "Ablerex" "ups" "2" "GRs series" "USB" "nutdrv_qx" "Ablerex" "ups" "2" "GRs Plus series" "USB" "nutdrv_qx" - "ActivePower" "ups" "2" "400VA" "" "blazer_ser" "ActivePower" "ups" "2" "1400VA" "" "blazer_ser" "ActivePower" "ups" "2" "2000VA" "" "blazer_ser" From 2d2726d3969a69495b5f8c379366986030fd2e1f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 13:04:05 +0000 Subject: [PATCH 650/700] driver.list.in: Add HCL info for Guardian LCD 1500 AP (IGA1500LCD) (Serial) Closes: #449 --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 7813497a51..c4840987ab 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -452,6 +452,8 @@ "Gtec" "ups" "2" "ZP120N-10K-31-00 / ZP120N-10K-31-07 / ZP120N-10K-31-09 / ZP120N-10K-31-99 / ZP120N-20K" "Serial port" "blazer_ser" "Gtec" "ups" "2" "AP160N-1K / AP160LCD-1K-KS / AP160N-2K / AP160LCD-2K-KS / AP160N-3K / AP160LCD-3K-KS / AP160N-6K-PDU / AP160N-10K-PDU" "Serial port" "blazer_ser" +"Guardian" "ups" "2" "LCD 1500 AP (IGA1500LCD)" "Serial" "nutdrv_qx" + "HP" "ups" "1" "PowerTrust 2997A" "HP 5061-2575 cable" "apcsmart" "HP" "ups" "3" "T750 G2" "Serial port" "bcmxcp" "HP" "ups" "3" "T1000 G3" "Serial port" "bcmxcp" From 0ea1f1e3c75764e0cc3ca0ce4817c7ad4e4e31e3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 13:28:20 +0000 Subject: [PATCH 651/700] driver.list.in: Add HCL info for CyberPower Systems EC850LCD (USB) Closes: #622 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index c4840987ab..4cc48fac99 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -238,6 +238,7 @@ "Cyber Power Systems" "ups" "3" "CP825AVR-G / LE825G" "USB" "usbhid-ups" # http://www.cyberpowersystems.com/products/ups-systems/retail-products/LE825G.html "Cyber Power Systems" "ups" "3" "EC350G" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/products/ups/ecologic/ec350g "Cyber Power Systems" "ups" "3" "EC750G" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/products/ups/desktop/ec750g +"Cyber Power Systems" "ups" "3" "EC850LCD" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/ec850lcd/ https://github.com/networkupstools/nut/issues/622 "Cyber Power Systems" "ups" "3" "OR2200LCDRM2U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "OR700LCDRM1U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "PR1500RT2U" "" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/new-smart-app-sinewave/pr1500rt2u/ From 9eca15b7c50663ee18465223319bc45d2d3500d9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 13:29:53 +0000 Subject: [PATCH 652/700] driver.list.in: Add HCL info for CyberPower Systems OR500LCDRM1U (USB) Closes: #578 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 4cc48fac99..ac9fe3a394 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -241,6 +241,7 @@ "Cyber Power Systems" "ups" "3" "EC850LCD" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/ec850lcd/ https://github.com/networkupstools/nut/issues/622 "Cyber Power Systems" "ups" "3" "OR2200LCDRM2U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "OR700LCDRM1U" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "3" "OR500LCDRM1U" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/or500lcdrm1u/ https://github.com/networkupstools/nut/issues/578 "Cyber Power Systems" "ups" "3" "PR1500RT2U" "" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/new-smart-app-sinewave/pr1500rt2u/ "Cyber Power Systems" "ups" "3" "PR6000LCDRTXL5U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "PR2200LCDRT2U" "" "snmp-ups" From d3ca02a115afc3b1419817a6ee620d193dc0ce71 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 14:12:41 +0000 Subject: [PATCH 653/700] driver.list.in: Add HCL info for CyberPower Systems UT2200E (USB) Closes: #556 --- data/driver.list.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/driver.list.in b/data/driver.list.in index ac9fe3a394..7216b8d574 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -242,7 +242,8 @@ "Cyber Power Systems" "ups" "3" "OR2200LCDRM2U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "OR700LCDRM1U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "OR500LCDRM1U" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/or500lcdrm1u/ https://github.com/networkupstools/nut/issues/578 -"Cyber Power Systems" "ups" "3" "PR1500RT2U" "" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/new-smart-app-sinewave/pr1500rt2u/ +"Cyber Power Systems" "ups" "3" "UT2200E" "USB" "usbhid-ups" # https://www.cyberpower.com/ww/en/product/sku/UT2200E https://github.com/networkupstools/nut/issues/556 +"Cyber Power Systems" "ups" "3" "PR1500RT2U" "" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/new-smart-app-sinewave/pr1500rt2u/ "Cyber Power Systems" "ups" "3" "PR6000LCDRTXL5U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "PR2200LCDRT2U" "" "snmp-ups" "Cyber Power Systems" "ups" "3" "RMCARD100" "" "snmp-ups" From df28dbbd87231a43381e821e2789a4500d0c1841 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 14:16:35 +0000 Subject: [PATCH 654/700] driver.list.in: Add HCL info for CyberPower Systems BR1000ELCD (USB) Closes: #552 --- data/driver.list.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/driver.list.in b/data/driver.list.in index 7216b8d574..c6c06bbafd 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -220,7 +220,7 @@ "Cyber Power Systems" "ups" "1" "PR2200" "" "powerpanel" "Cyber Power Systems" "ups" "1" "Power99" "" "genericups upstype=7" "Cyber Power Systems" "ups" "2" "AE550" "USB" "usbhid-ups" -"Cyber Power Systems" "ups" "2" "CP1000AVRLCD" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "2" "BR1000ELCD" "USB" "usbhid-ups" # https://www.cyberpower.com/eu/en/product/sku/BR1000ELCD https://github.com/networkupstools/nut/issues/552 "Cyber Power Systems" "ups" "2" "CP1350AVRLCD" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CP1500AVRLCD" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CP900AVR" "USB" "usbhid-ups" From e896de37fd11d49235859334261f236fb454e327 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 655/700] driver.list.in: Add HCL info for CyberPower Systems CP1500PFCLCD (USB) Closes: #520 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index c6c06bbafd..6826e01063 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -223,6 +223,7 @@ "Cyber Power Systems" "ups" "2" "BR1000ELCD" "USB" "usbhid-ups" # https://www.cyberpower.com/eu/en/product/sku/BR1000ELCD https://github.com/networkupstools/nut/issues/552 "Cyber Power Systems" "ups" "2" "CP1350AVRLCD" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CP1500AVRLCD" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "2" "CP1500PFCLCD" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/cp1500pfclcd/ https://github.com/networkupstools/nut/issues/520 "Cyber Power Systems" "ups" "2" "CP900AVR" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CPS685AVR" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CPS800AVR" "USB" "usbhid-ups" From fdd757df057b3589c4d8b66adcee9574a34618a0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 656/700] driver.list.in: Add HCL info for CyberPower Systems RT650EI (USB) Closes: #453 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 6826e01063..5122c4d481 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -243,6 +243,7 @@ "Cyber Power Systems" "ups" "3" "OR2200LCDRM2U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "OR700LCDRM1U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "OR500LCDRM1U" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/or500lcdrm1u/ https://github.com/networkupstools/nut/issues/578 +"Cyber Power Systems" "ups" "3" "RT650EI" "USB" "usbhid-ups" # http://www.cyberpowersystems.de/produkte/backup-usv-serien/rt-serie.html https://github.com/networkupstools/nut/issues/453 "Cyber Power Systems" "ups" "3" "UT2200E" "USB" "usbhid-ups" # https://www.cyberpower.com/ww/en/product/sku/UT2200E https://github.com/networkupstools/nut/issues/556 "Cyber Power Systems" "ups" "3" "PR1500RT2U" "" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/new-smart-app-sinewave/pr1500rt2u/ "Cyber Power Systems" "ups" "3" "PR6000LCDRTXL5U" "USB" "usbhid-ups" From a4086d9d4e582ceecb234977517cfe85453184d7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 657/700] driver.list.in: Add HCL info for CyberPower Systems BL1250U (USB) Closes: #1012 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 5122c4d481..9ede1c4b77 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -220,6 +220,7 @@ "Cyber Power Systems" "ups" "1" "PR2200" "" "powerpanel" "Cyber Power Systems" "ups" "1" "Power99" "" "genericups upstype=7" "Cyber Power Systems" "ups" "2" "AE550" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "3" "BL1250U" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/battery-backup/bl1250u/ https://github.com/networkupstools/nut/issues/1012 "Cyber Power Systems" "ups" "2" "BR1000ELCD" "USB" "usbhid-ups" # https://www.cyberpower.com/eu/en/product/sku/BR1000ELCD https://github.com/networkupstools/nut/issues/552 "Cyber Power Systems" "ups" "2" "CP1350AVRLCD" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CP1500AVRLCD" "USB" "usbhid-ups" From c100ef4932d228eed644149a9dfed91de8013353 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 658/700] driver.list.in: Add HCL info for CyberPower Systems CP850PFCLCD (USB) Closes: #605 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 9ede1c4b77..d777f29a8f 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -224,6 +224,7 @@ "Cyber Power Systems" "ups" "2" "BR1000ELCD" "USB" "usbhid-ups" # https://www.cyberpower.com/eu/en/product/sku/BR1000ELCD https://github.com/networkupstools/nut/issues/552 "Cyber Power Systems" "ups" "2" "CP1350AVRLCD" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CP1500AVRLCD" "USB" "usbhid-ups" +"Cyber Power Systems" "ups" "2" "CP850PFCLCD" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/605 "Cyber Power Systems" "ups" "2" "CP1500PFCLCD" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/cp1500pfclcd/ https://github.com/networkupstools/nut/issues/520 "Cyber Power Systems" "ups" "2" "CP900AVR" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "2" "CPS685AVR" "USB" "usbhid-ups" From 17083e00d4c17833b42837e65ace40e50535f0f5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 659/700] driver.list.in: Update comment for CyberPower Systems PR1500RT2U [#1191] --- data/driver.list.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/driver.list.in b/data/driver.list.in index d777f29a8f..590fcbac49 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -247,7 +247,7 @@ "Cyber Power Systems" "ups" "3" "OR500LCDRM1U" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/or500lcdrm1u/ https://github.com/networkupstools/nut/issues/578 "Cyber Power Systems" "ups" "3" "RT650EI" "USB" "usbhid-ups" # http://www.cyberpowersystems.de/produkte/backup-usv-serien/rt-serie.html https://github.com/networkupstools/nut/issues/453 "Cyber Power Systems" "ups" "3" "UT2200E" "USB" "usbhid-ups" # https://www.cyberpower.com/ww/en/product/sku/UT2200E https://github.com/networkupstools/nut/issues/556 -"Cyber Power Systems" "ups" "3" "PR1500RT2U" "" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/new-smart-app-sinewave/pr1500rt2u/ +"Cyber Power Systems" "ups" "3" "PR1500RT2U" "" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/new-smart-app-sinewave/pr1500rt2u/ https://github.com/networkupstools/nut/issues/1191 "Cyber Power Systems" "ups" "3" "PR6000LCDRTXL5U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "PR2200LCDRT2U" "" "snmp-ups" "Cyber Power Systems" "ups" "3" "RMCARD100" "" "snmp-ups" From 6704b2fd43dd6979acfbac05febf1b36fb64eafc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 660/700] driver.list.in: Update for CyberPower Systems OR1500ERM1U [#1338] --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 590fcbac49..0b3f524fae 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -242,6 +242,7 @@ "Cyber Power Systems" "ups" "3" "EC350G" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/products/ups/ecologic/ec350g "Cyber Power Systems" "ups" "3" "EC750G" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/products/ups/desktop/ec750g "Cyber Power Systems" "ups" "3" "EC850LCD" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/ec850lcd/ https://github.com/networkupstools/nut/issues/622 +"Cyber Power Systems" "ups" "3" "OR1500ERM1U" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/1338 "Cyber Power Systems" "ups" "3" "OR2200LCDRM2U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "OR700LCDRM1U" "USB" "usbhid-ups" "Cyber Power Systems" "ups" "3" "OR500LCDRM1U" "USB" "usbhid-ups" # https://www.cyberpowersystems.com/product/ups/or500lcdrm1u/ https://github.com/networkupstools/nut/issues/578 From 4be9a31226d9044bfee21fc9eb69929f0ec409bd Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 661/700] docs/man/usbhid-ups.txt: document some caveats with CyberPower UPSes [issues #520, #1394] --- docs/man/usbhid-ups.txt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/man/usbhid-ups.txt b/docs/man/usbhid-ups.txt index 99ca9db78e..bd5f7d1c26 100644 --- a/docs/man/usbhid-ups.txt +++ b/docs/man/usbhid-ups.txt @@ -243,6 +243,33 @@ If you do the same without mains present, it should do the same, but in this case, the outputs shall remain off until mains power is applied again. +UPS cuts power too soon +~~~~~~~~~~~~~~~~~~~~~~~ + +Note that many Cyber Power Systems (CPS) models tend to divide `offdelay` +by 60 and round down, so the minimum advisable value is 60 (seconds) to avoid +powering off immediately after NUT sends the shutdown command to the UPS. + +UPS does not set battery.charge.low but says OK +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Note that many Cyber Power Systems (CPS) models tend to allow only certain +values for `battery.charge.low` and anything outside of the set of allowed +values are rounded or ignored. + +A shell loop like this can help you map out the allowed values: + +==== + for i in `seq 90 -1 0`; do echo "set to $i"; \ + upsrw -s battery.charge.low=$i -u * -p * cps-big; \ + sleep 1; upsc cps-big battery.charge.low; echo ""; \ + done +==== + +For example, for CPS PR1000LCDRTXL2U model, the only allowed values are +`[60,55,50,45,40,35,30,25,20]` and in some cases, your UPS may effectively +not support a value of 10 for the `battery.charge.low` setting. + HISTORY ------- From 4b14087ca15db1f5f2ca7d1e409cdd8fbcfbc6c4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 662/700] driver.list.in: Add HCL info for Liebert PowerSure PSA 500 series (USB) Closes: #601 --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 0b3f524fae..be3ee33997 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -603,6 +603,8 @@ "Liebert" "ups" "1" "GXT2-3000RT230" "" "liebert-esp2 (experimental)" "Liebert" "ups" "2" "PowerSure Personal XT" "USB" "usbhid-ups" "Liebert" "ups" "2" "PowerSure PSA" "USB" "usbhid-ups" +"Liebert" "ups" "2" "PowerSure PSA 500" "USB" "usbhid-ups" +"Liebert" "ups" "2" "PowerSure PSA500MT3-230U" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/601 "Liebert" "ups" "2" "PowerSure PSI 1440" "USB" "usbhid-ups" # http://www.emersonnetworkpower.com/en-US/Products/ACPower/Pages/LiebertPowerSurePSILineInteractiveUPS10003000VA.aspx "LNXI" "pdu" "1" "Icebox" "10 outlets" "powerman-pdu (experimental)" From abd6280fb5fb8d049458ae4d226ad25c9cd38617 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 663/700] driver.list.in: Update comment for APC Back-UPS Pro 1000, Model BX1000M (USB) [nut#139] --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index be3ee33997..2a159280ee 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -73,6 +73,7 @@ "APC" "ups" "2" "Back-UPS ES/CyberFort 350" "USB" "usbhid-ups" "APC" "ups" "2" "Back-UPS BF500" "USB" "usbhid-ups" "APC" "ups" "2" "BACK-UPS XS LCD" "USB" "usbhid-ups" +"APC" "ups" "2" "Back-UPS XS 1000M (Back-UPS Pro 1000, Model BX1000M)" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/139 "APC" "ups" "2" "Smart-UPS (USB)" "USB" "usbhid-ups" "APC" "ups" "1" "Back-UPS" "940-0095A/C cables" "genericups upstype=1" "APC" "ups" "1" "Back-UPS" "940-0020B/C cables" "genericups upstype=2" From 0bc17b98ee582c53207b29f22f9f46b13eda8486 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 664/700] driver.list.in: Add HCL info for APC SMC2200BI-BR (USB) Closes: #557 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 2a159280ee..b9aff33a67 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -74,6 +74,7 @@ "APC" "ups" "2" "Back-UPS BF500" "USB" "usbhid-ups" "APC" "ups" "2" "BACK-UPS XS LCD" "USB" "usbhid-ups" "APC" "ups" "2" "Back-UPS XS 1000M (Back-UPS Pro 1000, Model BX1000M)" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/139 +"APC" "ups" "2" "SMC2200BI-BR" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/557 "APC" "ups" "2" "Smart-UPS (USB)" "USB" "usbhid-ups" "APC" "ups" "1" "Back-UPS" "940-0095A/C cables" "genericups upstype=1" "APC" "ups" "1" "Back-UPS" "940-0020B/C cables" "genericups upstype=2" From 707e6f1012f73268b9f7755a57704bf2ea37e7a2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 665/700] driver.list.in: Add HCL info for Salicru SPS ONE (USB) Closes: #554 --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index b9aff33a67..13d29d7189 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -1040,6 +1040,8 @@ "Rucelf" "ups" "2" "Rucelf UPOII-3000-96-EL" "" "blazer_ser" # http://www.rucelf.ua/en/catalog/upoii-3000-96-el/ +"Salicru" "ups" "2" "SPS ONE 700VA" "USB" "blazer_usb" +"Salicru" "ups" "2" "SPS ONE 2000VA" "USB" "nutdrv_qx" # https://www.salicru.com/en/ups/sps-one.html https://github.com/networkupstools/nut/issues/554 "Salicru" "ups" "2" "SPS 650/850 HOME" "usb" "usbhid-ups (experimental)" "Salicru" "ups" "2" "SLC TWIN PRO2" "usb" "usbhid-ups (experimental)" # https://github.com/networkupstools/nut/issues/450 "Salicru" "ups" "2" "SLC-1500-TWIN PRO3" "usb" "usbhid-ups (experimental)" # https://github.com/networkupstools/nut/issues/1142 From 4f8dbac3ab8908cde3884db81e3be0dc5057deaa Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 666/700] driver.list.in: Add HCL info for Eaton 9PX 2000 RT (USB) Closes: #540 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 13d29d7189..1895344a89 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -345,6 +345,7 @@ "Eaton" "ups" "5" "5P" "Serial port" "mge-shut" "Eaton" "ups" "5" "9SX" "Serial port" "mge-shut" "Eaton" "ups" "5" "9PX" "Serial port" "mge-shut" +"Eaton" "ups" "5" "9PX 2000 RT" "USB port" "mge-shut" # https://github.com/networkupstools/nut/issues/540 "Eaton" "ups" "5" "9PX Split Phase 6/8/10 kVA" "Serial port" "mge-shut" "Eaton" "ups" "5" "9PX" "SNMP/Web card" "netxml-ups" "Eaton" "ups" "5" "9PX Split Phase 6/8/10 kVA" "SNMP/Web card" "netxml-ups" From 5414c340f7453e57fa3bc6ffabbad7e7e52fe841 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 667/700] driver.list.in: Add HCL info for Powercom Raptor 2000 and RPT-600AP (USB) Closes: #633 --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 1895344a89..2945fdfd9e 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -926,6 +926,8 @@ "Powercom" "ups" "5" "(various)" "USB (2009 models, product id: 00a?)" "usbhid-ups (experimental)" "Powercom" "ups" "5" "BNT-xxxAP" "USB (product id: 0004)" "usbhid-ups (experimental)" "Powercom" "ups" "1" "BNT-xxxAP" "USB (product id: 0001)" "usbhid-ups (experimental)" +"Powercom" "ups" "3" "RPT-600AP" "USB" "usbhid-ups" # http://pcmups.com.tw/eA/html/product/show.php?num=226&root=13&kind=105&page=1&keyword= https://github.com/networkupstools/nut/issues/633 +"Powercom" "ups" "3" "Raptor 2000" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/633 "Powercool" "ups" "1" "350VA to 1600VA" "USB" "nutdrv_atcl_usb" "Powercool" "ups" "2" "650VA" "USB" "nutdrv_qx port=auto vendorid=0001 productid=0000 product=MEC0003 protocol=hunnox langid_fix=0x0409 novendor noscanlangid" # https://github.com/networkupstools/nut/pull/638 caveats at https://github.com/networkupstools/nut/issues/537 From 7061ee7b394da722580f74f8a868b85085dff363 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 668/700] driver.list.in: Add HCL info for Elsist UPS Nemo2.0 160 (USB) Closes: #719 --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 2945fdfd9e..2ae03cce29 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -382,6 +382,8 @@ "Electrys" "ups" "2" "UPS 2500" "" "nutdrv_qx or blazer_ser" +"Elsist" "ups" "2" "Nemo2.0 160" "USB" "blazer_usb" # http://www.naicon.com/company/wp-content/uploads/2017/05/Manual-NEMO2.0-Ver.02-Eng.pdf https://github.com/networkupstools/nut/issues/719 + "Emerson" "pdu" "3" "PM3000 metered & switched" "" "snmp-ups" "Energy Sistem" "ups" "2" "(various)" "" "blazer_ser" From 82149da0a2c9419a26bfda128f8d732e5ad5f443 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 669/700] driver.list.in: Add HCL info for Tecnoware UPS ERA PLUS 1100 (USB) Closes: #747 --- data/driver.list.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/driver.list.in b/data/driver.list.in index 2ae03cce29..85cdca7d16 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -1117,7 +1117,8 @@ "Sysgration" "ups" "2" "UPGUARDS Pro650" "" "blazer_ser" "Tecnoware" "ups" "2" "Easy Power 1200" "" "blazer_ser" -"Tecnoware" "ups" "2" "UPS ERA LCD 0.65" "" "blazer_usb langid_fix=0x409" +"Tecnoware" "ups" "2" "UPS ERA LCD 0.65" "USB" "blazer_usb langid_fix=0x409" +"Tecnoware" "ups" "2" "UPS ERA PLUS 1100" "USB" "blazer_usb" # https://www.tecnoware.com/Prodotti/FGCERAPL1100/ups-era-plus-1100.aspx https://github.com/networkupstools/nut/issues/747 "Tripp Lite" "ups" "1" "(various)" "Lan 2.2 interface - black 73-0844 cable" "genericups upstype=5" "Tripp Lite" "ups" "2" "1500 LCD" "USB" "usbhid-ups" From 6d4adc9a8785030ea03825eef61d384720ae0f41 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 670/700] driver.list.in: Add HCL info for Advice Top V Pro 6-10K (USB) Closes: #744 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 85cdca7d16..047724c08d 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -51,6 +51,7 @@ "ActivePower" "ups" "2" "2000VA" "" "blazer_ser" "Advice" "ups" "2" "TopGuard 2000" "" "blazer_ser" +"Advice" "ups" "2" "Top V Pro 6-10K" "USB" "blazer_usb" # https://www.advice.co.il/en/advice-products/ups-systems/ups-online-systems/one-phase-ups-5k-10k/%D7%90%D7%9C-%D7%A4%D7%A1%D7%A7-%D7%90%D7%95%D7%9F-%D7%9C%D7%99%D7%99%D7%9F-top-v-pro-6-10k-detail https://github.com/networkupstools/nut/issues/744 "AEC" "ups" "1" "MiniGuard UPS 700" "Megatec M2501 cable" "genericups upstype=21" From a66b049add516ef98745fc8d606069e69dba49c3 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 671/700] driver.list.in: Add HCL info for Advice PRS850 and PRV700 Pro (USB) from DDL --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 047724c08d..f2daf84513 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -52,6 +52,8 @@ "Advice" "ups" "2" "TopGuard 2000" "" "blazer_ser" "Advice" "ups" "2" "Top V Pro 6-10K" "USB" "blazer_usb" # https://www.advice.co.il/en/advice-products/ups-systems/ups-online-systems/one-phase-ups-5k-10k/%D7%90%D7%9C-%D7%A4%D7%A1%D7%A7-%D7%90%D7%95%D7%9F-%D7%9C%D7%99%D7%99%D7%9F-top-v-pro-6-10k-detail https://github.com/networkupstools/nut/issues/744 +"Advice" "ups" "2" "PRS850" "USB" "blazer_usb" +"Advice" "ups" "2" "PRV700 Pro" "USB" "blazer_usb" "AEC" "ups" "1" "MiniGuard UPS 700" "Megatec M2501 cable" "genericups upstype=21" From a22696e1e94088f4d8d517b7473fba078debf9cc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 672/700] driver.list.in: Add HCL info for V7 UPS1RM2U1500-1E UPS 1500VA Rack Mount 2U (USB) Closes: #716 --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index f2daf84513..03ea261547 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -1275,6 +1275,8 @@ "UPSonic" "ups" "2" "PrOffice 650" "USB" "blazer_usb" "UPSonic" "ups" "2" "DS-800" "USB" "blazer_usb" +"V7" "ups" "2" "UPS1RM2U1500-1E" "USB" "blazer_usb" # http://www.v7world.com/uk/ups-1500va-rack-mount-2u-eu.html https://github.com/networkupstools/nut/issues/716 + "Various" "ups" "4" "(various)" "SEC protocol" "gamatronic" "Various" "ups" "1" "(various)" "Generic RUPS model" "genericups upstype=4" "Various" "ups" "1" "(various)" "Generic RUPS 2000 (Megatec M2501 cable)" "genericups upstype=21" From 3ae656a034d28a265e5786ab8cf5baffb21cda6f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 673/700] driver.list.in: Add HCL info for Energy Technologies DPK1/1-3 (Serial) Closes: #762 --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 03ea261547..1594dd08ca 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -391,6 +391,8 @@ "Energy Sistem" "ups" "2" "(various)" "" "blazer_ser" +"Energy Technologies" "ups" "2" "DPK1/1-3" "Serial" "blazer_ser" # https://www.tensy.ru/podderzhka/dokumentatsiya/ibp-serii-dpk/ https://github.com/networkupstools/nut/issues/762 + "ETA" "ups" "1" "mini+UPS" "WinNT/Upsoft cable" "genericups upstype=7" "ETA" "ups" "1" "mini+UPS PRO" "UPS Explorer cable" "etapro" From 10e51e908a9da6b77ba51c23dd4494b95b16efa5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 674/700] driver.list.in: Add HCL info for Ippon Back Basic 850 Euro (USB) Closes: #802 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 1594dd08ca..4bdc92d512 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -531,6 +531,7 @@ "IPMI" "pdu" "1" "" "" "powerman-pdu (experimental)" +"Ippon" "ups" "2" "Back Basic 850 Euro" "USB" "blazer_usb (experimental)" # https://github.com/networkupstools/nut/issues/802 "Ippon" "ups" "2" "Back Power Pro 400/500/600/700/800" "" "blazer_ser" "Ippon" "ups" "2" "Back Power Pro 400/500/600/700/800" "USB" "blazer_usb (experimental)" "Ippon" "ups" "2" "Back Comfo Pro 600/800" "" "blazer_ser" From 3a065bfcc633f453dd5fe4b3bef12054bf586488 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 675/700] driver.list.in: Add HCL info for Digitus DN-170076 (USB) Closes: #948 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 4bdc92d512..149169b831 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -302,6 +302,7 @@ "Digitus" "ups" "1" "DN-170014" "USB" "richcomm_usb" # http://www.digitus.info/en/products/professional-network/security-and-surveillance/power-supply/uninterrruptable-power-supplies/ups-uninterruptible-power-systems-dn-170014/section/prof/ "Digitus" "ups" "2" "DN-170020" "" "blazer_ser" +"Digitus" "ups" "2" "DN-170076" "USB" "nutdrv_qx" # https://www.digitus.info/en/products/network-and-server-cabinets/power-supply/uninterruptible-power-supplies/dn-170076/ https://github.com/networkupstools/nut/issues/948 "Dynamix" "ups" "2" "UPS1700D" "" "blazer_ser" "Dynamix" "ups" "2" "UPS-650VA" "" "blazer_ser" From 92df976e84a655784c2f08fe63ec80b71d5c30a1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 676/700] driver.list.in: Add HCL info for Aviem Pro 2000VA (USB) Closes: #827 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 149169b831..c098fa5340 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -136,6 +136,7 @@ "Atlantis Land" "ups" "2" "(various)" "USB" "nutdrv_qx" "Aviem Systems" "ups" "2" "Aviem Power RT 1000-3000VA" "" "blazer_ser" +"Aviem Systems" "ups" "2" "Aviem Pro 2000VA" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/827 "Baytech" "pdu" "1" "RPC3" "8 outlets" "powerman-pdu (experimental)" "Baytech" "pdu" "1" "RPC3-20NC" "8 outlets" "powerman-pdu (experimental)" From cc46f192837452c265568233874a45909c3ae4f0 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 677/700] driver.list.in: Add HCL info for Kebo UPS-1000D (USB) Closes: #981 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index c098fa5340..7866bba44e 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -556,6 +556,7 @@ "Kanji" "ups" "1" "800 VA" "USB" "nutdrv_atcl_usb" "Kebo" "ups" "2" "1200D/D Series" "" "blazer_ser" +"Kebo" "ups" "2" "UPS-1000D" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/981 "KOLFF" "ups" "2" "BLACK NOVA 1K/2K/3K/6K/10K/20K TOWER" "" "blazer_usb" "KOLFF" "ups" "2" "BLACK NOVA 1K/2K/3K/6K/10K/20K XL TOWER" "" "blazer_usb" From 10f9f5506bd54f5da17feede4bd36ab65c2db080 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 678/700] driver.list.in: Add HCL info for Kebo UPS-650VA (USB) from DDL --- data/driver.list.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/driver.list.in b/data/driver.list.in index 7866bba44e..d1a84b116e 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -556,7 +556,8 @@ "Kanji" "ups" "1" "800 VA" "USB" "nutdrv_atcl_usb" "Kebo" "ups" "2" "1200D/D Series" "" "blazer_ser" -"Kebo" "ups" "2" "UPS-1000D" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/981 +"Kebo" "ups" "2" "UPS-1000D (UPS-1000VA)" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/981 +"Kebo" "ups" "2" "UPS-650VA" "USB" "megatec_usb" "KOLFF" "ups" "2" "BLACK NOVA 1K/2K/3K/6K/10K/20K TOWER" "" "blazer_usb" "KOLFF" "ups" "2" "BLACK NOVA 1K/2K/3K/6K/10K/20K XL TOWER" "" "blazer_usb" From 79c3d6bfd8cdd5d11d54f051c23ce4cb6d0b4eb4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 679/700] driver.list.in: Add HCL info for Digitus DN-170040, DN-170041, and Voltronic Power GalleonX9-RT LCD-1-3K (USB) Closes: #1251 --- data/driver.list.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index d1a84b116e..239041352f 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -303,6 +303,10 @@ "Digitus" "ups" "1" "DN-170014" "USB" "richcomm_usb" # http://www.digitus.info/en/products/professional-network/security-and-surveillance/power-supply/uninterrruptable-power-supplies/ups-uninterruptible-power-systems-dn-170014/section/prof/ "Digitus" "ups" "2" "DN-170020" "" "blazer_ser" +"Digitus" "ups" "2" "DN-170040" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/1251 +"Digitus" "ups" "2" "DN-170041" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/1251 +"Digitus" "ups" "2" "DN-170040" "USB" "nutdrv_qx" # https://github.com/networkupstools/nut/issues/1251 +"Digitus" "ups" "2" "DN-170041" "USB" "nutdrv_qx" # https://github.com/networkupstools/nut/issues/1251 "Digitus" "ups" "2" "DN-170076" "USB" "nutdrv_qx" # https://www.digitus.info/en/products/network-and-server-cabinets/power-supply/uninterruptible-power-supplies/dn-170076/ https://github.com/networkupstools/nut/issues/948 "Dynamix" "ups" "2" "UPS1700D" "" "blazer_ser" @@ -1314,6 +1318,8 @@ "Voltronic Power" "ups" "2" "Frigate TX 1KVA" "USB" "nutdrv_qx" "Voltronic Power" "ups" "2" "Galleon 1KVA" "Serial" "nutdrv_qx" "Voltronic Power" "ups" "2" "Galleon 1KVA" "USB" "nutdrv_qx" +"Voltronic Power" "ups" "2" "Galleon X9-RT LCD-1-3K" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/1251 +"Voltronic Power" "ups" "2" "Galleon X9-RT LCD-1-3K" "USB" "nutdrv_qx" # https://github.com/networkupstools/nut/issues/1251 "Voltronic Power" "ups" "2" "Imperial 1KVA" "Serial" "nutdrv_qx" "Voltronic Power" "ups" "2" "Imperial 1KVA" "USB" "nutdrv_qx" "Voltronic Power" "ups" "2" "Prosine 800" "Serial" "nutdrv_qx" From acc1f79fac1d101f91dcf93aa5f7858c023670db Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 680/700] driver.list.in: Add HCL info for Santak MT*-PRO / Castle C*K (Serial) Closes: #1039 --- data/driver.list.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 239041352f..f024b46d04 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -1067,6 +1067,9 @@ "Salicru" "ups" "2" "SLC TWINPRO3" "usb" "usbhid-ups (experimental)" "Salicru" "ups" "2" "SLC TWIN RT3" "usb" "usbhid-ups (experimental)" +"Santak" "ups" "2" "Castle C*K" "Serial" "blazer_ser" # https://github.com/networkupstools/nut/issues/1039 +"Santak" "ups" "2" "MT*-PRO" "Serial" "blazer_ser" # https://github.com/networkupstools/nut/issues/1039 + "Siemens" "ups" "4" "SITOP UPS500" "serial" "nutdrv_siemens_sitop (experimental, untested)" "Siemens" "ups" "4" "SITOP UPS500" "USB" "nutdrv_siemens_sitop (experimental)" From f3a70a352060167d8bd7e36895f19e5f343c962c Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 681/700] driver.list.in: Add HCL info for Online-UPS Xanto S700 [#1279] --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index f024b46d04..7b6cd9b328 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -902,6 +902,7 @@ "Online" "ups" "2" "Zinto A" "" "blazer_usb" "Online" "ups" "1" "Zinto D" "" "optiups" "Online" "ups" "2" "Yunto YQ450" "" "blazer_usb" +"Online" "ups" "2" "Xanto S700" "/dev/ttyUSB0" "nutdrv_qx" # https://github.com/networkupstools/nut/issues/1279 "OnLite" "ups" "2" "AQUA" "50" "blazer_ser" From a0aaf921c143897a0103694fa9a453e529e597f9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 682/700] driver.list.in: Add HCL info for UPS Phasak model 9465, renamed as P6N (USB) Closes: #1187 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 7b6cd9b328..3f926736e5 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -914,6 +914,7 @@ "Orvaldi Power Protection" "ups" "2" "750 / 900SP" "" "blazer_usb" "Phasak" "ups" "2" "400VA / 600VA" "" "blazer_ser" +"Phasak" "ups" "2" "9465 or P6N" "USB" "nutdrv_qx" # https://github.com/networkupstools/nut/issues/1187 => Voltronic-QS-Hex protocol detected "PhoenixContact" "ups" "4" "QUINT-UPS/24DC" "2320461" "phoenixcontact_modbus" #https://www.phoenixcontact.com/online/portal/us?uri=pxc-oc-itemdetail:pid=2320461 From 504ed1696433fc6ff7cb8b3d8f317a6f6ac81684 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 683/700] driver.list.in: Add HCL info for PowerWalker VI 1200 SHL (USB) Closes: #1270 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 3f926736e5..f23785a12b 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -981,6 +981,7 @@ "PowerWalker" "ups" "2" "Line-Interactive VI 1000RT/1500RT/2000RT/3000RT LCD" "" "blazer_usb" "PowerWalker" "ups" "2" "VFI 1000 CG PF1" "" "nutdrv_qx" # https://powerwalker.com/?page=product&item=10122108&lang=en "PowerWalker" "ups" "2" "VFI 2000 TGS" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/560 and https://github.com/networkupstools/nut/pull/564 +"PowerWalker" "ups" "2" "VI 1200 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/1270 "Powerware" "ups" "4" "3110" "" "genericups upstype=7" "Powerware" "ups" "4" "3115" "" "genericups upstype=11" From dc45f54f7f395354bcb129c6da66c19845288a52 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 684/700] driver.list.in: Add HCL info for PowerWalker VI 3000 SCL (USB) Closes: #971 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index f23785a12b..089e23804e 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -982,6 +982,7 @@ "PowerWalker" "ups" "2" "VFI 1000 CG PF1" "" "nutdrv_qx" # https://powerwalker.com/?page=product&item=10122108&lang=en "PowerWalker" "ups" "2" "VFI 2000 TGS" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/560 and https://github.com/networkupstools/nut/pull/564 "PowerWalker" "ups" "2" "VI 1200 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/1270 +"PowerWalker" "ups" "2" "VI 3000 SCL" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/971 "Powerware" "ups" "4" "3110" "" "genericups upstype=7" "Powerware" "ups" "4" "3115" "" "genericups upstype=11" From cb839593050b583c70d7ae4dc5164ea1093439fb Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 685/700] driver.list.in: Add HCL info for PowerWalker Basic VI 1000 SB (USB) Closes: #818 --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 089e23804e..5648d20207 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -982,6 +982,8 @@ "PowerWalker" "ups" "2" "VFI 1000 CG PF1" "" "nutdrv_qx" # https://powerwalker.com/?page=product&item=10122108&lang=en "PowerWalker" "ups" "2" "VFI 2000 TGS" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/560 and https://github.com/networkupstools/nut/pull/564 "PowerWalker" "ups" "2" "VI 1200 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/1270 +"PowerWalker" "ups" "2" "Basic VI 1000 SB" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/818 +"PowerWalker" "ups" "2" "PR1500LCDRT2U" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/818 "PowerWalker" "ups" "2" "VI 3000 SCL" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/971 "Powerware" "ups" "4" "3110" "" "genericups upstype=7" From 91584c9d00cf679dcd39a489425f64ca57a00827 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 686/700] driver.list.in: Add HCL info for PowerWalker VI 650 SH (USB) Closes: #473 --- data/driver.list.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index 5648d20207..7d339211e7 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -981,6 +981,7 @@ "PowerWalker" "ups" "2" "Line-Interactive VI 1000RT/1500RT/2000RT/3000RT LCD" "" "blazer_usb" "PowerWalker" "ups" "2" "VFI 1000 CG PF1" "" "nutdrv_qx" # https://powerwalker.com/?page=product&item=10122108&lang=en "PowerWalker" "ups" "2" "VFI 2000 TGS" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/560 and https://github.com/networkupstools/nut/pull/564 +"PowerWalker" "ups" "2" "VI 650 SH" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/483 "PowerWalker" "ups" "2" "VI 1200 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/1270 "PowerWalker" "ups" "2" "Basic VI 1000 SB" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/818 "PowerWalker" "ups" "2" "PR1500LCDRT2U" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/818 @@ -1136,6 +1137,7 @@ "Sweex" "ups" "2" "INTELLIGENT UPS 1500VA P220" "USB" "blazer_usb (USB ID 0665:5161)" # http://www.sweex.com/en/notebook-pc-accessoires/ups/PP220/ "Syndome" "ups" "2" "Era 500VA" "USB" "blazer_usb" +"Syndome" "ups" "2" "Atom LCD-1000" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/483 "Sysgration" "ups" "2" "UPGUARDS Pro650" "" "blazer_ser" From f668490ce46af1d6a03930517a1ef0cd1bda52ce Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 687/700] driver.list.in: Add HCL info for PowerWalker VI 2200 SHL (USB) Closes: #756 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 7d339211e7..c6beb958a0 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -983,6 +983,7 @@ "PowerWalker" "ups" "2" "VFI 2000 TGS" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/560 and https://github.com/networkupstools/nut/pull/564 "PowerWalker" "ups" "2" "VI 650 SH" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/483 "PowerWalker" "ups" "2" "VI 1200 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/1270 +"PowerWalker" "ups" "2" "VI 2200 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/756 "PowerWalker" "ups" "2" "Basic VI 1000 SB" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/818 "PowerWalker" "ups" "2" "PR1500LCDRT2U" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/818 "PowerWalker" "ups" "2" "VI 3000 SCL" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/971 From fefa25276153048955cb9d477437b5acff404823 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 688/700] driver.list.in: Add HCL info for PowerWalker VI 2200 SH and 650 SHL (USB) Closes: #646 --- data/driver.list.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/driver.list.in b/data/driver.list.in index c6beb958a0..9abe510b6c 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -982,8 +982,11 @@ "PowerWalker" "ups" "2" "VFI 1000 CG PF1" "" "nutdrv_qx" # https://powerwalker.com/?page=product&item=10122108&lang=en "PowerWalker" "ups" "2" "VFI 2000 TGS" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/560 and https://github.com/networkupstools/nut/pull/564 "PowerWalker" "ups" "2" "VI 650 SH" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/483 +"PowerWalker" "ups" "2" "VI 650/850 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/646 "PowerWalker" "ups" "2" "VI 1200 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/1270 "PowerWalker" "ups" "2" "VI 2200 SHL" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/756 +"PowerWalker" "ups" "2" "VI 1200 SH" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/646 +"PowerWalker" "ups" "2" "VI 2200 SH" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/646 "PowerWalker" "ups" "2" "Basic VI 1000 SB" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/818 "PowerWalker" "ups" "2" "PR1500LCDRT2U" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/818 "PowerWalker" "ups" "2" "VI 3000 SCL" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/971 From 83d999c4e6e255ee327e80c92b030eed5fe3298e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Sat, 23 Apr 2022 22:47:58 +0000 Subject: [PATCH 689/700] driver.list.in: Add HCL info for PowerWalker VI 750T/HID (USB) Closes: #774 --- data/driver.list.in | 1 + 1 file changed, 1 insertion(+) diff --git a/data/driver.list.in b/data/driver.list.in index 9abe510b6c..ddc10ff3a9 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -990,6 +990,7 @@ "PowerWalker" "ups" "2" "Basic VI 1000 SB" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/818 "PowerWalker" "ups" "2" "PR1500LCDRT2U" "USB" "usbhid-ups" # https://github.com/networkupstools/nut/issues/818 "PowerWalker" "ups" "2" "VI 3000 SCL" "USB" "blazer_usb" # https://github.com/networkupstools/nut/issues/971 +"PowerWalker" "ups" "2" "VI 750T/HID" "USB" "usbhid-ups" # https://powerwalker.com/?page=select&cat=VI_THID&lang=en https://github.com/networkupstools/nut/issues/774 "Powerware" "ups" "4" "3110" "" "genericups upstype=7" "Powerware" "ups" "4" "3115" "" "genericups upstype=11" From 62900a489761e2a1b40ac05a18d64de0d22cb2f9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 26 Apr 2022 15:54:07 +0200 Subject: [PATCH 690/700] drivers/libusb{0,1}.c: report why we could not open any HID devices Closes: #477 --- drivers/libusb0.c | 41 ++++++++++++++++++++++++++++++++++++++++- drivers/libusb1.c | 20 ++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/drivers/libusb0.c b/drivers/libusb0.c index de22c48d89..643a0a71f2 100644 --- a/drivers/libusb0.c +++ b/drivers/libusb0.c @@ -180,6 +180,9 @@ static int libusb_open(usb_dev_handle **udevp, usb_ctrl_char *p; char string[256]; int i; + int count_open_EACCESS = 0; + int count_open_errors = 0; + int count_open_attempts = 0; /* report descriptor */ usb_ctrl_char rdbuf[MAX_REPORT_SIZE]; @@ -201,6 +204,8 @@ static int libusb_open(usb_dev_handle **udevp, for (dev = bus->devices; dev; dev = dev->next) { /* int if_claimed = 0; */ + count_open_attempts++; + upsdebugx(2, "Checking device (%04X/%04X) (%s/%s)", dev->descriptor.idVendor, dev->descriptor.idProduct, bus->dirname, dev->filename); @@ -211,10 +216,30 @@ static int libusb_open(usb_dev_handle **udevp, /* open the device */ *udevp = udev = usb_open(dev); if (!udev) { + /* It seems that with libusb-0.1 API we + * can only evaluate the string value of + * usb_strerror() return values - in the + * library source there is magic about + * tracking errors in their string buffer + * or as a printable errno, and no reliably + * usable way to learn of an EACCESS or + * other situation diagnostics otherwise. + * So we have to search for sub-strings + * and hope for locale to be right... + */ + char *libusb_error = usb_strerror(); upsdebugx(1, "Failed to open device (%04X/%04X), skipping: %s", dev->descriptor.idVendor, dev->descriptor.idProduct, - usb_strerror()); + libusb_error); + + count_open_errors++; + if (strcasestr(libusb_error, "Access denied") + || strcasestr(libusb_error, "insufficient permissions") + ) { + count_open_EACCESS++; + } + continue; } @@ -523,6 +548,20 @@ static int libusb_open(usb_dev_handle **udevp, upsdebugx(2, "libusb0: No appropriate HID device found"); fflush(stdout); + if (count_open_attempts == 0) { + upslogx(LOG_WARNING, + "libusb0: Could not open any HID devices: " + "no USB buses found"); + } + else + if (count_open_errors > 0 + && count_open_errors == count_open_EACCESS + ) { + upslogx(LOG_WARNING, + "libusb0: Could not open any HID devices: " + "insufficient permissions on everything"); + } + return -1; } diff --git a/drivers/libusb1.c b/drivers/libusb1.c index d9dc2d67e4..55c0b0332c 100644 --- a/drivers/libusb1.c +++ b/drivers/libusb1.c @@ -157,6 +157,8 @@ static int nut_libusb_open(libusb_device_handle **udevp, const unsigned char *p; char string[256]; int i; + int count_open_EACCESS = 0; + int count_open_errors = 0; /* report descriptor */ unsigned char rdbuf[MAX_REPORT_SIZE]; @@ -197,6 +199,10 @@ static int nut_libusb_open(libusb_device_handle **udevp, dev_desc.idVendor, dev_desc.idProduct, libusb_strerror((enum libusb_error)ret)); + count_open_errors++; + if (ret == LIBUSB_ERROR_ACCESS) { + count_open_EACCESS++; + } continue; } udev = *udevp; @@ -590,6 +596,20 @@ static int nut_libusb_open(libusb_device_handle **udevp, upsdebugx(2, "libusb1: No appropriate HID device found"); fflush(stdout); + if (devcount < 1) { + upslogx(LOG_WARNING, + "libusb1: Could not open any HID devices: " + "no USB buses found"); + } + else + if (count_open_errors > 0 + || count_open_errors == count_open_EACCESS + ) { + upslogx(LOG_WARNING, + "libusb1: Could not open any HID devices: " + "insufficient permissions on everything"); + } + return -1; } From e6e842833b4ed58f9090e2139473ca8bac389de1 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 26 Apr 2022 20:28:31 +0200 Subject: [PATCH 691/700] docs/download.txt: deprecate buildbot tarballs --- docs/download.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/download.txt b/docs/download.txt index a4852bf8b2..e2327638bd 100644 --- a/docs/download.txt +++ b/docs/download.txt @@ -81,13 +81,23 @@ Snapshots GitHub has several download links for repository snapshots (for particular tags or branches), but you will need a number of tools such as autoconf, automake -and libtool to use these snapshots. +and libtool to use these snapshots to generate the `configure` script and some +other files. + +After you `configure` the source workspace, a `make dist-hash` recipe would +create the snapshot tarballs which do not require the auto* tools, and their +checksum files, such as those available on the NUT website and attached to +link:https://github.com/networkupstools/nut/releases[GitHub Releases page]. + +///////// +TODO: #1400 to replace this with a NUT CI farm service to publish the tarballs If our Buildbot instance is behaving, you can download a snapshot which does not require auto* tools from this link:http://buildbot.networkupstools.org/snapshots[builder]. Look for the latest *[tarball]* link towards the top of the page, and be sure to check the 'Build ##' link to verify the branch name. +///////// Older versions ~~~~~~~~~~~~~~ From 464f6416362c2318d24c6d99f434af2213c90137 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 26 Apr 2022 20:58:15 +0200 Subject: [PATCH 692/700] docs/download.txt: update links to distro packaging recipes and their results --- docs/download.txt | 78 ++++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/docs/download.txt b/docs/download.txt index e2327638bd..b6017ba3f9 100644 --- a/docs/download.txt +++ b/docs/download.txt @@ -68,12 +68,10 @@ use Git or <>. Browse code ^^^^^^^^^^^ -You can also browse the "vanilla NUT" code at -link:https://github.com/networkupstools/nut[GitHub], -or at packaging sources of operating system distributions such as: - -* link:https://salsa.debian.org/debian/nut/[Debian Salsa mirror] -* link:https://src.fedoraproject.org/rpms/nut/tree/rawhide[Fedora Rawhide mirror] +You can browse the "vanilla NUT" code at the +link:https://github.com/networkupstools/nut/[Main GitHub repository for NUT sources], +and some possibly modified copies as part of packaging recipe +sources of operating system distributions, as listed below. [[Snapshots]] Snapshots @@ -110,45 +108,69 @@ Binary packages NOTE: The only official releases from this project are source code. -NUT is already available in the following systems: +NUT is already available in the following operating systems (and likely more): + +- link:https://repology.org/project/nut/versions[Repology report on NUT] + lists 745 entries about NUT, as of this writing - Linux: -link:https://aur.archlinux.org/packages/network-ups-tools[Arch Linux], -link:http://packages.debian.org/nut[Debian], -link:http://packages.gentoo.org/package/sys-power/nut[Gentoo Linux], -Mandriva, -link:https://apps.fedoraproject.org/packages/nut[Red Hat / Fedora], -link:http://software.opensuse.org/package/nut[Novell SUSE / openSUSE], -link:https://github.com/openwrt/packages/tree/master/net/nut[OpenWrt], -link:http://sotirov-bg.net/slackpack/search.cgi?q=nut[Slackware], -link:http://packages.ubuntu.com/nut[Ubuntu], -link:https://github.com/voidlinux/xbps-packages/blob/master/srcpkgs/network-ups-tools/template[Void Linux]. + + * link:https://github.com/42ity/nut/tree/FTY/obs[42ITy.org packaging recipes for Debian-based releases] + * link:https://salsa.debian.org/debian/nut/[Debian Salsa recipes] + and link:http://packages.debian.org/nut[Debian packages] + * link:http://packages.ubuntu.com/nut[Ubuntu packages] + * link:https://src.fedoraproject.org/rpms/nut/tree/rawhide[Fedora Rawhide recipes] + and link:https://src.fedoraproject.org/rpms/nut[Red Hat / Fedora packages] + * link:https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=network-ups-tools-git[Arch Linux recipe] + and link:https://aur.archlinux.org/packages/network-ups-tools-git[Arch Linux package info] + * link:https://gitweb.gentoo.org/repo/gentoo.git/tree/sys-power/nut[Gentoo Linux recipe] + and link:http://packages.gentoo.org/package/sys-power/nut[Gentoo Linux package info] + * link:https://build.opensuse.org/package/show/openSUSE%3AFactory/nut[Novell SUSE / openSUSE official package base recipe] + and link:https://build.opensuse.org/package/show/hardware/nut[Novell SUSE / openSUSE official package development recipe], + and link:http://software.opensuse.org/package/nut[Novell SUSE / openSUSE official package overview] + * link:https://build.opensuse.org/search?search_text=nut[Numerous other recipes on Open Build System (not only by SUSE)] + * link:https://github.com/openwrt/packages/tree/master/net/nut[OpenWRT recipes] + * link:http://sotirov-bg.net/slackpack/search.cgi?q=nut[Slackware package overview] + * link:https://github.com/void-linux/void-packages/tree/master/srcpkgs/network-ups-tools[Void Linux recipes] - BSD systems: -link:http://www.FreeBSD.org/cgi/ports.cgi?query=^nut-&stype=name[FreeBSD], -link:http://pkgsrc.se/sysutils/ups-nut[NetBSD], -link:http://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/sysutils/nut/[OpenBSD], -link:http://doc.freenas.org/9.3/freenas_services.html#ups[FreeNAS]. + + * link:https://cgit.freebsd.org/ports/tree/sysutils/nut-devel[FreeBSD package recipe (devel)], + link:https://cgit.freebsd.org/ports/tree/sysutils/nut[FreeBSD package recipe] + and link:http://www.FreeBSD.org/cgi/ports.cgi?query=^nut-&stype=name[FreeBSD package overview] + * link:cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/sysutils/ups-nut/[NetBSD recipe] and link:http://pkgsrc.se/sysutils/ups-nut[NetBSD package overview] + * link:http://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/sysutils/nut/[OpenBSD recipe] + * link:https://github.com/freenas/iocage-ports/tree/master/sysutils/nut[FreeNAS iocage-ports recipe], + link:http://doc.freenas.org/9.3/freenas_services.html#ups[FreeNAS 9.3 docs on UPS integration] + and link:https://www.ixsystems.com/documentation/freenas/11.3-U5/services.html#ups[FreeNAS 11.3-U5 docs on UPS integration] - Mac OS X: -link:http://pdb.finkproject.org/pdb/package.php/nut[Fink], -link:http://trac.macports.org/browser/trunk/dports/sysutils/nut/Portfile[MacPorts] + + * link:https://github.com/fink/fink-distributions/blob/master/10.9-libcxx/stable/main/finkinfo/net/nut.info[Fink recipe] + and link:http://pdb.finkproject.org/pdb/package.php/nut[Fink package overview] + * link:http://trac.macports.org/browser/trunk/dports/sysutils/nut/Portfile[MacPorts recipe] + +- illumos/Solaris: + + * link:https://github.com/OpenIndiana/oi-userland/tree/oi/hipster/components/sysutils/nut[OpenIndiana oi-userland recipe] + and link:https://pkg.openindiana.org/hipster/en/search.shtml?token=nut&action=Search[OpenIndiana latest rolling builds] - Windows (complete port, Beta): -link:http://www.networkupstools.org/package/windows/NUT-Installer-2.6.5-6.msi[Windows MSI installer 2.6.5-6] + + * link:http://www.networkupstools.org/package/windows/NUT-Installer-2.6.5-6.msi[Windows MSI installer 2.6.5-6] Java packages ------------- -The jNut package has been split into its own -link:https://github.com/networkupstools/jNut[GitHub repository]. +- The jNut package has been split into its own + link:https://github.com/networkupstools/jNut[GitHub repository]. - NUT Java support (client side, Beta) -link:http://www.networkupstools.org/package/java/jNut-0.2-SNAPSHOT.tar.gz[jNUT 0.2-SNAPSHOT] + link:http://www.networkupstools.org/package/java/jNut-0.2-SNAPSHOT.tar.gz[jNUT 0.2-SNAPSHOT] - NUT Java Web support (client side using REST, Beta) -link:http://www.networkupstools.org/package/java/jNutWebAPI-0.2-SNAPSHOT-src.tar.gz[jNutWebAPI 0.2-SNAPSHOT (sources)] + link:http://www.networkupstools.org/package/java/jNutWebAPI-0.2-SNAPSHOT-src.tar.gz[jNutWebAPI 0.2-SNAPSHOT (sources)] Virtualization packages ----------------------- From d2b337ab8d3121511aca5f001714ad7eaba69af2 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 26 Apr 2022 20:59:18 +0200 Subject: [PATCH 693/700] docs/download.txt: link to distro packaging wiki page on NUT github --- docs/download.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/download.txt b/docs/download.txt index b6017ba3f9..c6d2a63a39 100644 --- a/docs/download.txt +++ b/docs/download.txt @@ -108,7 +108,8 @@ Binary packages NOTE: The only official releases from this project are source code. -NUT is already available in the following operating systems (and likely more): +NUT is already available in the following operating systems (and +link:https://github.com/networkupstools/nut/wiki/Links-to-distribution-packaging-recipes-and-repository-sections[likely more]): - link:https://repology.org/project/nut/versions[Repology report on NUT] lists 745 entries about NUT, as of this writing From 53af2dc4d137e0f2920012d3e86a04c267720238 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Tue, 26 Apr 2022 21:18:05 +0200 Subject: [PATCH 694/700] docs/nut.dict: update dict --- docs/nut.dict | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/nut.dict b/docs/nut.dict index 730ec9314f..38a1885e5b 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 2954 utf-8 +personal_ws-1.1 en 2956 utf-8 AAS ABI ACFAIL @@ -1978,6 +1978,7 @@ io ioLogik ioLogikE ioLogikR +iocage iostream ip ipE @@ -2847,6 +2848,7 @@ usec useconds useradd userid +userland usermap username usernames From 6043b8db64736a805a44dfc34b0c8aaee5f81f40 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 4 Apr 2022 10:54:07 +0000 Subject: [PATCH 695/700] tools/nut-scanner/Makefile.am, clients/Makefile.am: bump "version-info" for NUT v2.8.0 Development since 2.7.4 release included changes to code structure, fought warnings, and might impact ABI/API by the changes to arguments (specific integer types and size_t vs. architecture-dependent choice). --- clients/Makefile.am | 4 ++-- tools/nut-scanner/Makefile.am | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clients/Makefile.am b/clients/Makefile.am index 266e546bd2..e9d7e3c394 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -73,12 +73,12 @@ endif # object .so names would differ) # libupsclient version information -libupsclient_la_LDFLAGS = -version-info 5:0:0 -export-symbols-regex ^upscli_ +libupsclient_la_LDFLAGS = -version-info 6:0:0 -export-symbols-regex ^upscli_ if HAVE_CXX11 # libnutclient version information and build libnutclient_la_SOURCES = nutclient.h nutclient.cpp -libnutclient_la_LDFLAGS = -version-info 1:0:0 +libnutclient_la_LDFLAGS = -version-info 2:0:0 # Needed in not-standalone builds with -DHAVE_NUTCOMMON=1 # which is defined for in-tree CXX builds above: libnutclient_la_LIBADD = $(top_builddir)/common/libcommonclient.la diff --git a/tools/nut-scanner/Makefile.am b/tools/nut-scanner/Makefile.am index 692cdbcdc9..667b40b5a4 100644 --- a/tools/nut-scanner/Makefile.am +++ b/tools/nut-scanner/Makefile.am @@ -65,7 +65,7 @@ libnutscan_la_LIBADD += $(top_builddir)/common/libcommonclient.la # object .so names would differ) # # libnutscan version information -libnutscan_la_LDFLAGS = $(SERLIBS) -version-info 1:0:0 +libnutscan_la_LDFLAGS = $(SERLIBS) -version-info 2:0:0 # libnutscan exported symbols regex # WARNING: Since the library includes parts of libcommon (as much as needed From ff16dabca191e5fd8ddc20137317bdebee554d8d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 4 Apr 2022 11:04:28 +0000 Subject: [PATCH 696/700] configure.ac: cut the release of NUT v2.8.0 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4b4c0418d8..3aa26c24eb 100644 --- a/configure.ac +++ b/configure.ac @@ -5,7 +5,7 @@ dnl +------------------------------------------------------------------+ dnl NUT version number is defined here, with a Git suffixed macro like dnl NUT_VERSION_MACRO "2.7.4-2838-gdfc3ac08" dnl in include/nut_version.h (generated by make) -AC_INIT([nut],[2.7.4.1],[https://github.com/networkupstools/nut/issues]) +AC_INIT([nut],[2.8.0],[https://github.com/networkupstools/nut/issues]) AC_CONFIG_AUX_DIR([.]) AC_CONFIG_SRCDIR(server/upsd.c) AC_CONFIG_MACRO_DIR([m4]) From d81f4c5ed61e23fdc25111515fcdd075b9ddf5b8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 4 Apr 2022 10:54:07 +0000 Subject: [PATCH 697/700] tools/nut-scanner/Makefile.am, clients/Makefile.am: bump "version-info" for NUT v2.8.0 Development since 2.7.4 release included changes to code structure, fought warnings, and might impact ABI/API by the changes to arguments (specific integer types and size_t vs. architecture-dependent choice). --- clients/Makefile.am | 4 ++-- tools/nut-scanner/Makefile.am | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clients/Makefile.am b/clients/Makefile.am index 266e546bd2..e9d7e3c394 100644 --- a/clients/Makefile.am +++ b/clients/Makefile.am @@ -73,12 +73,12 @@ endif # object .so names would differ) # libupsclient version information -libupsclient_la_LDFLAGS = -version-info 5:0:0 -export-symbols-regex ^upscli_ +libupsclient_la_LDFLAGS = -version-info 6:0:0 -export-symbols-regex ^upscli_ if HAVE_CXX11 # libnutclient version information and build libnutclient_la_SOURCES = nutclient.h nutclient.cpp -libnutclient_la_LDFLAGS = -version-info 1:0:0 +libnutclient_la_LDFLAGS = -version-info 2:0:0 # Needed in not-standalone builds with -DHAVE_NUTCOMMON=1 # which is defined for in-tree CXX builds above: libnutclient_la_LIBADD = $(top_builddir)/common/libcommonclient.la diff --git a/tools/nut-scanner/Makefile.am b/tools/nut-scanner/Makefile.am index 692cdbcdc9..667b40b5a4 100644 --- a/tools/nut-scanner/Makefile.am +++ b/tools/nut-scanner/Makefile.am @@ -65,7 +65,7 @@ libnutscan_la_LIBADD += $(top_builddir)/common/libcommonclient.la # object .so names would differ) # # libnutscan version information -libnutscan_la_LDFLAGS = $(SERLIBS) -version-info 1:0:0 +libnutscan_la_LDFLAGS = $(SERLIBS) -version-info 2:0:0 # libnutscan exported symbols regex # WARNING: Since the library includes parts of libcommon (as much as needed From c7a01a2c97528a440ea681e6ec63b9b834ed3df5 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 4 Apr 2022 11:04:28 +0000 Subject: [PATCH 698/700] configure.ac: cut the release of NUT v2.8.0 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4b4c0418d8..3aa26c24eb 100644 --- a/configure.ac +++ b/configure.ac @@ -5,7 +5,7 @@ dnl +------------------------------------------------------------------+ dnl NUT version number is defined here, with a Git suffixed macro like dnl NUT_VERSION_MACRO "2.7.4-2838-gdfc3ac08" dnl in include/nut_version.h (generated by make) -AC_INIT([nut],[2.7.4.1],[https://github.com/networkupstools/nut/issues]) +AC_INIT([nut],[2.8.0],[https://github.com/networkupstools/nut/issues]) AC_CONFIG_AUX_DIR([.]) AC_CONFIG_SRCDIR(server/upsd.c) AC_CONFIG_MACRO_DIR([m4]) From 29c41fc66ff7fb33af9f7dc43e78bd9b109b2ddf Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 4 Apr 2022 11:04:56 +0000 Subject: [PATCH 699/700] configure.ac: prepare for next iterations after release of NUT v2.8.0 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 3aa26c24eb..e3809da1f1 100644 --- a/configure.ac +++ b/configure.ac @@ -5,7 +5,7 @@ dnl +------------------------------------------------------------------+ dnl NUT version number is defined here, with a Git suffixed macro like dnl NUT_VERSION_MACRO "2.7.4-2838-gdfc3ac08" dnl in include/nut_version.h (generated by make) -AC_INIT([nut],[2.8.0],[https://github.com/networkupstools/nut/issues]) +AC_INIT([nut],[2.8.0.1],[https://github.com/networkupstools/nut/issues]) AC_CONFIG_AUX_DIR([.]) AC_CONFIG_SRCDIR(server/upsd.c) AC_CONFIG_MACRO_DIR([m4]) From 8e1573bcf8fc794c3e51c5cf8a83285f6d9df69a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 4 Apr 2022 11:04:56 +0000 Subject: [PATCH 700/700] configure.ac: prepare for next iterations after release of NUT v2.8.0 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 3aa26c24eb..e3809da1f1 100644 --- a/configure.ac +++ b/configure.ac @@ -5,7 +5,7 @@ dnl +------------------------------------------------------------------+ dnl NUT version number is defined here, with a Git suffixed macro like dnl NUT_VERSION_MACRO "2.7.4-2838-gdfc3ac08" dnl in include/nut_version.h (generated by make) -AC_INIT([nut],[2.8.0],[https://github.com/networkupstools/nut/issues]) +AC_INIT([nut],[2.8.0.1],[https://github.com/networkupstools/nut/issues]) AC_CONFIG_AUX_DIR([.]) AC_CONFIG_SRCDIR(server/upsd.c) AC_CONFIG_MACRO_DIR([m4])