Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Q2 and Q6 protocols implementation #2798

Merged
merged 11 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion NEWS.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ https://github.com/networkupstools/nut/milestone/11
* added Visench C1K (using serial port converter with USB ID `1a86:7523`)
as known supported by `nutdrv_qx` (Megatec protocol) since at least
NUT v2.7.4 release. [#2395]
* introduced `innovart31` protocol support for Innova RT 3/1 UPSes. [#2712]
* introduced `innovart31` protocol support for Innova RT 3/1 UPSes. [#2712, #2798]
* introduced `q2` and `q6` protocol support; currently also based/tested
on Innova devices, but other models than RT 3/1. [#2798]
* extended Voltronic protocol to support longer numbers as remaining
`battery.runtime` value. [#2765]

Expand Down
25 changes: 16 additions & 9 deletions docs/man/nutdrv_qx.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ If you set stayoff in linkman:ups.conf[5] when FSD arises the UPS will call a *s

*protocol =* 'string'::
Skip autodetection of the protocol to use and only use the one specified.
Supported values: 'bestups', 'hunnox', 'innovart31', 'masterguard', 'mecer', 'megatec', 'megatec/old', 'mustek', 'q1', 'voltronic', 'voltronic-qs', 'voltronic-qs-hex' and 'zinto'.
Supported values: 'bestups', 'hunnox', 'innovart31', 'masterguard', 'mecer', 'megatec', 'megatec/old', 'mustek', 'q1', 'q2', 'q6', 'voltronic', 'voltronic-qs', 'voltronic-qs-hex' and 'zinto'.
+
Run the driver program with the `--help` option to see the exact list of
`protocol` values it would currently recognize.
Expand Down Expand Up @@ -159,17 +159,17 @@ If not specified, the driver defaults to 10%.
Only used if *runtimecal* is also specified.


BESTUPS, INNOVART31, MECER, MEGATEC, MEGATEC/OLD, MUSTEK, Q1, VOLTRONIC-QS, VOLTRONIC-QS-HEX, ZINTO PROTOCOLS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BESTUPS, INNOVART31, MECER, MEGATEC, MEGATEC/OLD, MUSTEK, Q1, Q2, Q6, VOLTRONIC-QS, VOLTRONIC-QS-HEX, ZINTO PROTOCOLS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

*ignoresab*::
Some UPSes incorrectly report the `Shutdown Active' bit as always on, consequently making the driver believe the UPS is nearing a shutdown (and, as a result, ups.status always contains +FSD+... and you know what this means).
Setting this flag will make the driver ignore the `Shutdown Active' bit.


[[old-blazer-protocols-options]]
MECER, MEGATAEC, MEGATEC/OLD, MUSTEK, ZINTO PROTOCOLS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
MECER, MEGATEC, MEGATEC/OLD, MUSTEK, ZINTO PROTOCOLS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

*ondelay*::
The acceptable range is +0..599940+ seconds.
Expand Down Expand Up @@ -208,8 +208,8 @@ Safeguard against talking to the wrong one of several identical UPSes on the sam
Note that when changing *ups.id* (through linkman:upsrw[8]) the driver will continue to talk to the UPS with the new 'slave address', but won't claim it again on restart until the *slave_addr* parameter is adjusted.


INNOVART31, Q1 PROTOCOLS
~~~~~~~~~~~~~~~~~~~~~~~~
INNOVART31, Q1, Q2, Q6 PROTOCOLS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

*ondelay*::
The acceptable range is +0..599940+ seconds.
Expand All @@ -218,6 +218,13 @@ The acceptable range is +0..599940+ seconds.
The acceptable range is +12..600+ seconds.


Q2, Q6 PROTOCOLS
~~~~~~~~~~~~~~~~

*nooutstats*::
Some UPSes don't support `WA` command which returns output load stats. Using this flag will make the driver ignore these requests.


VOLTRONIC-QS, VOLTRONIC-QS-HEX PROTOCOLS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -451,8 +458,8 @@ Stop a running battery test.
(Not available on some hardware)


BESTUPS, INNOVART31, MECER, MEGATEC, MEGATEC/OLD, MUSTEK, Q1, ZINTO PROTOCOLS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BESTUPS, INNOVART31, MECER, MEGATEC, MEGATEC/OLD, MUSTEK, Q1, Q2, Q6, ZINTO PROTOCOLS
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

*test.battery.start* 'value'::
Perform a battery test for the duration of 'value' seconds (truncated to 60 seconds) [+60..5940+].
Expand Down
2 changes: 1 addition & 1 deletion docs/nut.dict
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,6 @@ MBATTCHG
MCU
MDigest
MEC
MEGATAEC
MERCHANTABILITY
MF
MH
Expand Down Expand Up @@ -2482,6 +2481,7 @@ nolock
nombattvolt
noncommercially
noout
nooutstats
norating
noro
noscanlangid
Expand Down
2 changes: 2 additions & 0 deletions docs/nutdrv_qx-subdrivers.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,8 @@ For more details, have a look at the currently available subdrivers:
- +nutdrv_qx_megatec-old.+{+c+,+h+}
- +nutdrv_qx_mustek.+{+c+,+h+}
- +nutdrv_qx_q1.+{+c+,+h+}
- +nutdrv_qx_q2.+{+c+,+h+}
- +nutdrv_qx_q6.+{+c+,+h+}
- +nutdrv_qx_voltronic.+{+c+,+h+}
- +nutdrv_qx_voltronic-qs.+{+c+,+h+}
- +nutdrv_qx_voltronic-qs-hex.+{+c+,+h+}
Expand Down
4 changes: 2 additions & 2 deletions drivers/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ NUTDRV_QX_SUBDRIVERS = nutdrv_qx_bestups.c nutdrv_qx_blazer-common.c \
nutdrv_qx_innovart31.c \
nutdrv_qx_masterguard.c \
nutdrv_qx_mecer.c nutdrv_qx_megatec.c nutdrv_qx_megatec-old.c \
nutdrv_qx_mustek.c nutdrv_qx_q1.c nutdrv_qx_voltronic.c \
nutdrv_qx_mustek.c nutdrv_qx_q1.c nutdrv_qx_q2.c nutdrv_qx_q6.c nutdrv_qx_voltronic.c \
nutdrv_qx_voltronic-qs.c nutdrv_qx_voltronic-qs-hex.c nutdrv_qx_zinto.c \
nutdrv_qx_hunnox.c nutdrv_qx_ablerex.c
nutdrv_qx_SOURCES += $(NUTDRV_QX_SUBDRIVERS)
Expand All @@ -408,7 +408,7 @@ dist_noinst_HEADERS = \
nutdrv_qx_innovart31.h \
nutdrv_qx_masterguard.h \
nutdrv_qx_mecer.h nutdrv_qx_ablerex.h \
nutdrv_qx_megatec.h nutdrv_qx_megatec-old.h nutdrv_qx_mustek.h nutdrv_qx_q1.h nutdrv_qx_hunnox.h \
nutdrv_qx_megatec.h nutdrv_qx_megatec-old.h nutdrv_qx_mustek.h nutdrv_qx_q1.h nutdrv_qx_q2.h nutdrv_qx_q6.h nutdrv_qx_hunnox.h \
nutdrv_qx_voltronic.h nutdrv_qx_voltronic-qs.h nutdrv_qx_voltronic-qs-hex.h nutdrv_qx_zinto.h \
upsdrvquery.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 \
Expand Down
19 changes: 18 additions & 1 deletion drivers/nutdrv_qx.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
# define DRIVER_NAME "Generic Q* Serial driver"
#endif /* QX_USB */

#define DRIVER_VERSION "0.39"
#define DRIVER_VERSION "0.40"

#ifdef QX_SERIAL
# include "serial.h"
Expand All @@ -77,6 +77,8 @@
#include "nutdrv_qx_megatec-old.h"
#include "nutdrv_qx_mustek.h"
#include "nutdrv_qx_q1.h"
#include "nutdrv_qx_q2.h"
#include "nutdrv_qx_q6.h"
#include "nutdrv_qx_voltronic.h"
#include "nutdrv_qx_voltronic-qs.h"
#include "nutdrv_qx_voltronic-qs-hex.h"
Expand All @@ -99,6 +101,8 @@ static subdriver_t *subdriver_list[] = {
&hunnox_subdriver,
&ablerex_subdriver,
&innovart31_subdriver,
&q2_subdriver,
&q6_subdriver,
/* Fallback Q1 subdriver */
&q1_subdriver,
NULL
Expand Down Expand Up @@ -258,6 +262,19 @@ int qx_multiply_battvolt(item_t *item, char *value, const size_t valuelen) {
return 0;
}

/* Convert kilo-values to their full representation */
int qx_multiply_x1000(item_t *item, char *value, const size_t valuelen) {
float s = 0;

if (sscanf(item->value, "%f", &s) != 1) {
upsdebugx(2, "unparsable ss.ss %s", item->value);
return -1;
}

snprintf(value, valuelen, "%.2f", s * 1000.0);
return 0;
}

/* Fill batt.volt.act and guesstimate the battery charge
* if it isn't already available. */
static int qx_battery(void)
Expand Down
3 changes: 3 additions & 0 deletions drivers/nutdrv_qx.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ void update_status(const char *nutvalue);
/* Let subdrivers reference this: for devices that report "battery.voltage" of a single cell/pack, optionally multiply that into representing the whole assembly */
int qx_multiply_battvolt(item_t *item, char *value, const size_t valuelen);

/* Convert kilo-values to their full representation */
int qx_multiply_x1000(item_t *item, char *value, const size_t valuelen);

/* Data for processing status values */
#define STATUS(x) ((unsigned int)1U<<x)

Expand Down
2 changes: 1 addition & 1 deletion drivers/nutdrv_qx_blazer-common.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* nutdrv_qx_blazer-common.c - Common functions/settings for nutdrv_qx_{innovart31,mecer,megatec,megatec-old,mustek,q1,voltronic-qs,zinto}.{c,h}
/* nutdrv_qx_blazer-common.c - Common functions/settings for nutdrv_qx_{innovart31,mecer,megatec,megatec-old,mustek,q1,q2,q6,voltronic-qs,zinto}.{c,h}
*
* Copyright (C)
* 2013 Daniele Pezzini <[email protected]>
Expand Down
2 changes: 1 addition & 1 deletion drivers/nutdrv_qx_blazer-common.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* nutdrv_qx_blazer-common.h - Common functions/settings for nutdrv_qx_{innovart31,mecer,megatec,megatec-old,mustek,q1,voltronic-qs,zinto}.{c,h}
/* nutdrv_qx_blazer-common.h - Common functions/settings for nutdrv_qx_{innovart31,mecer,megatec,megatec-old,mustek,q1,q2,q6,voltronic-qs,zinto}.{c,h}
*
* Copyright (C)
* 2013 Daniele Pezzini <[email protected]>
Expand Down
81 changes: 55 additions & 26 deletions drivers/nutdrv_qx_innovart31.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,13 @@

#include "nutdrv_qx_innovart31.h"

#define INNOVART31_VERSION "INNOVART31 0.01"
#define INNOVART31_VERSION "INNOVART31 0.02"

/* Internal function: used to convert kilo-values to their full representation */
static int innovart31_x1000(item_t *item, char *value, const size_t valuelen) {
float s = 0;
/* Support functions */
static int innovart31_claim(void);
static void innovart31_initups(void);
static void innovart31_initinfo(void);

if (sscanf(item->value, "%f", &s) != 1) {
upsdebugx(2, "unparsable ss.ss %s", item->value);
return -1;
}

snprintf(value, valuelen, "%.2f", s * 1000.0);
return 0;
}
/* qx2nut lookup table */
static item_t innovart31_qx2nut[] = {

Expand All @@ -50,19 +43,15 @@ static item_t innovart31_qx2nut[] = {
*/

/* Common parameters */
{ "input.voltage", 0, NULL, "Q6\r", "", 110, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL },
{ "input.L1-N.voltage", 0, NULL, "Q6\r", "", 110, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL },
{ "input.L2-N.voltage", 0, NULL, "Q6\r", "", 110, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL },
{ "input.L3-N.voltage", 0, NULL, "Q6\r", "", 110, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL },
{ "input.frequency", 0, NULL, "Q6\r", "", 110, '(', "", 19, 22, "%.1f", 0, NULL, NULL, NULL },
{ "output.voltage", 0, NULL, "Q6\r", "", 110, '(', "", 24, 28, "%.1f", 0, NULL, NULL, NULL },
/* { "output.L1-N.voltage", 0, NULL, "Q6\r", "", 110, '(', "", 24, 28, "%.1f", 0, NULL, NULL, NULL },
{ "output.L2-N.voltage", 0, NULL, "Q6\r", "", 110, '(', "", 30, 34, "%.1f", 0, NULL, NULL, NULL },
{ "output.L3-N.voltage", 0, NULL, "Q6\r", "", 110, '(', "", 36, 40, "%.1f", 0, NULL, NULL, NULL },*/
{ "output.frequency", 0, NULL, "Q6\r", "", 110, '(', "", 42, 45, "%.1f", 0, NULL, NULL, NULL },
{ "ups.load", 0, NULL, "Q6\r", "", 110, '(', "", 47, 49, "%.0f", 0, NULL, NULL, NULL },
{ "ups.temperature", 0, NULL, "Q6\r", "", 110, '(', "", 71, 74, "%.1f", 0, NULL, NULL, NULL },
{ "battery.voltage", 0, NULL, "Q6\r", "", 110, '(', "", 59, 63, "%.2f", 0, NULL, NULL, qx_multiply_battvolt },
{ "battery.voltage", 0, NULL, "Q6\r", "", 110, '(', "", 59, 63, "%.1f", 0, NULL, NULL, qx_multiply_battvolt },
{ "battery.runtime", 0, NULL, "Q6\r", "", 110, '(', "", 76, 80, "%.0f", 0, NULL, NULL, NULL },
{ "battery.charge", 0, NULL, "Q6\r", "", 110, '(', "", 82, 84, "%.0f", 0, NULL, NULL, NULL },

Expand All @@ -75,8 +64,8 @@ static item_t innovart31_qx2nut[] = {

/* Output consumption parameters */
{ "output.current", 0, NULL, "WA\r", "", 80, '(', "", 49, 53, "%.1f", 0, NULL, NULL, NULL },
{ "ups.realpower", 0, NULL, "WA\r", "", 80, '(', "", 37, 41, "%.1f", 0, NULL, NULL, innovart31_x1000 },
{ "ups.power", 0, NULL, "WA\r", "", 80, '(', "", 43, 47, "%.1f", 0, NULL, NULL, innovart31_x1000 },
{ "ups.realpower", 0, NULL, "WA\r", "", 80, '(', "", 37, 41, "%.1f", 0, NULL, NULL, qx_multiply_x1000 },
{ "ups.power", 0, NULL, "WA\r", "", 80, '(', "", 43, 47, "%.1f", 0, NULL, NULL, qx_multiply_x1000 },

/*
* > [BPS\r]
Expand All @@ -87,9 +76,6 @@ static item_t innovart31_qx2nut[] = {

/* Bypass parameters */
{ "input.bypass.voltage", 0, NULL, "BPS\r", "", 24, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL },
/* { "input.bypass.L1-N.voltage", 0, NULL, "BPS\r", "", 24, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL },
{ "input.bypass.L2-N.voltage", 0, NULL, "BPS\r", "", 24, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL },
{ "input.bypass.L3-N.voltage", 0, NULL, "BPS\r", "", 24, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL },*/
{ "input.bypass.frequency", 0, NULL, "BPS\r", "", 24, '(', "", 19, 22, "%.1f", 0, NULL, NULL, NULL },

/*
Expand Down Expand Up @@ -165,8 +151,8 @@ static item_t innovart31_qx2nut[] = {
/* Testing table */
#ifdef TESTING
static testing_t innovart31_testing[] = {
{ "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 },
{ "Q6\r", "(227.0 225.6 230.0 50.0 229.9 000.0 000.0 49.9 007 000 000 327.8 000.0 23.0 06932 100 32 00000000 00000000 11\r", -1 },
{ "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 },
{ "WA\r", "(001.4 000.0 000.0 001.4 000.0 000.0 001.4 001.4 006.5 000.0 000.0 007 00000000\r", -1 },
{ "BPS\r", "(230.4 000.0 000.0 49.9\r", -1 },
{ "F\r", "#230.0 087 288.0 50.0\r", -1 },
Expand All @@ -184,10 +170,53 @@ static testing_t innovart31_testing[] = {
};
#endif /* TESTING */

/* == Support functions == */

/* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0 */
static int innovart31_claim(void)
{
/* We need Q6, Q1, WA, BPS, F, FW? and SASV07? to use this subdriver */
struct {
char *var;
char *cmd;
} mandatory[] = {
{ "input.L1-N.voltage", "Q6" },
{ "ups.type", "Q1" },
{ "output.current", "WA" },
{ "input.bypass.voltage", "BPS" },
{ "input.voltage.nominal", "F" },
{ "ups.firmware", "FW?" },
{ "ups.serial", "SASV07?" },
{ NULL, NULL}
};
int vari;
char *sp;
item_t *item;

for (vari = 0; mandatory[vari].var; vari++) {
sp = mandatory[vari].var;
item = find_nut_info(sp, 0, 0);

/* Don't know what happened */
if (!item)
return 0;

/* No reply/Unable to get value */
if (qx_process(item, NULL))
return 0;

/* Unable to process value */
if (ups_infoval_set(item) != 1)
return 0;
}

return 1;
}

/* Subdriver-specific initups */
static void innovart31_initups(void)
{
blazer_initups(innovart31_qx2nut);
blazer_initups_light(innovart31_qx2nut);
}

/* Subdriver-specific initinfo */
Expand All @@ -201,11 +230,11 @@ static void innovart31_initinfo(void)
/* Subdriver interface */
subdriver_t innovart31_subdriver = {
INNOVART31_VERSION,
blazer_claim,
innovart31_claim,
innovart31_qx2nut,
innovart31_initups,
innovart31_initinfo,
blazer_makevartable,
blazer_makevartable_light,
"ACK",
"NAK\r",
#ifdef TESTING
Expand Down
Loading