Skip to content

Commit

Permalink
Merge pull request #2798 from dvdesolve/q2q6_proto
Browse files Browse the repository at this point in the history
Q2 and Q6 protocols implementation
  • Loading branch information
jimklimov authored Feb 10, 2025
2 parents 356079f + c4dac84 commit 373241b
Show file tree
Hide file tree
Showing 14 changed files with 763 additions and 42 deletions.
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

0 comments on commit 373241b

Please sign in to comment.