Imported Upstream version 2.7.3
This commit is contained in:
parent
a356b56d11
commit
fd413a3168
283 changed files with 14978 additions and 6511 deletions
|
@ -44,6 +44,7 @@ USB_LIBUSB_DRIVERLIST = usbhid-ups bcmxcp_usb tripplite_usb \
|
|||
USB_DRIVERLIST = $(USB_LIBUSB_DRIVERLIST)
|
||||
NEONXML_DRIVERLIST = netxml-ups
|
||||
MACOSX_DRIVERLIST = macosx-ups
|
||||
LINUX_I2C_DRIVERLIST = asem
|
||||
|
||||
# distribute all drivers, even ones that are not built by default
|
||||
EXTRA_PROGRAMS = $(SERIAL_DRIVERLIST) $(SNMP_DRIVERLIST) $(USB_DRIVERLIST) $(NEONXML_DRIVERLIST) $(MACOSX_DRIVERLIST)
|
||||
|
@ -74,6 +75,9 @@ endif
|
|||
if WITH_MACOSX
|
||||
driverexec_PROGRAMS += $(MACOSX_DRIVERLIST)
|
||||
endif
|
||||
if WITH_LINUX_I2C
|
||||
driverexec_PROGRAMS += $(LINUX_I2C_DRIVERLIST)
|
||||
endif
|
||||
else
|
||||
driverexec_PROGRAMS += skel
|
||||
endif
|
||||
|
@ -171,7 +175,7 @@ tripplite_usb_SOURCES = tripplite_usb.c libusb.c usb-common.c
|
|||
tripplite_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm
|
||||
|
||||
bcmxcp_usb_SOURCES = bcmxcp_usb.c bcmxcp.c usb-common.c
|
||||
bcmxcp_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS)
|
||||
bcmxcp_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm
|
||||
|
||||
blazer_usb_SOURCES = blazer.c blazer_usb.c libusb.c usb-common.c
|
||||
blazer_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm
|
||||
|
@ -219,6 +223,10 @@ macosx_ups_LDADD = $(LDADD_DRIVERS)
|
|||
macosx_ups_LDFLAGS = $(LDFLAGS) -framework IOKit -framework CoreFoundation
|
||||
macosx_ups_SOURCES = macosx-ups.c
|
||||
|
||||
# Asem
|
||||
asem_LDADD = $(LDADD_DRIVERS)
|
||||
asem_SOURCES = asem.c
|
||||
|
||||
# nutdrv_qx USB/Serial
|
||||
nutdrv_qx_SOURCES = nutdrv_qx.c
|
||||
nutdrv_qx_LDADD = $(LDADD_DRIVERS) -lm
|
||||
|
@ -232,9 +240,10 @@ nutdrv_qx_CFLAGS += -DQX_USB
|
|||
nutdrv_qx_SOURCES += libusb.c usb-common.c
|
||||
nutdrv_qx_LDADD += $(LIBUSB_LIBS)
|
||||
endif
|
||||
NUTDRV_QX_SUBDRIVERS = nutdrv_qx_blazer-common.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_voltronic-qs.c nutdrv_qx_zinto.c
|
||||
NUTDRV_QX_SUBDRIVERS = nutdrv_qx_bestups.c nutdrv_qx_blazer-common.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_voltronic-qs.c nutdrv_qx_voltronic-qs-hex.c nutdrv_qx_zinto.c
|
||||
nutdrv_qx_SOURCES += $(NUTDRV_QX_SUBDRIVERS)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
@ -252,9 +261,9 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h baytech-mib.h bcmxcp.h \
|
|||
safenet.h serial.h snmp-ups.h solis.h tripplite.h tripplite-hid.h \
|
||||
upshandler.h usb-common.h usbhid-ups.h powercom-hid.h compaq-mib.h idowell-hid.h \
|
||||
apcsmart.h apcsmart_tabs.h apcsmart-old.h apcupsd-ups.h cyberpower-mib.h riello.h openups-hid.h \
|
||||
delta_ups-mib.h nutdrv_qx.h nutdrv_qx_blazer-common.h nutdrv_qx_mecer.h \
|
||||
delta_ups-mib.h nutdrv_qx.h nutdrv_qx_bestups.h nutdrv_qx_blazer-common.h nutdrv_qx_mecer.h \
|
||||
nutdrv_qx_megatec.h nutdrv_qx_megatec-old.h nutdrv_qx_mustek.h nutdrv_qx_q1.h \
|
||||
nutdrv_qx_voltronic.h nutdrv_qx_voltronic-qs.h nutdrv_qx_zinto.h \
|
||||
nutdrv_qx_voltronic.h nutdrv_qx_voltronic-qs.h nutdrv_qx_voltronic-qs-hex.h nutdrv_qx_zinto.h \
|
||||
xppc-mib.h
|
||||
|
||||
# Define a dummy library so that Automake builds rules for the
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -79,6 +79,8 @@ static void *general_apc_check(USBDevice_t *device)
|
|||
|
||||
/* USB IDs device table */
|
||||
static usb_device_id_t apc_usb_device_table[] = {
|
||||
/* APC AP9584 Serial->USB kit */
|
||||
{ USB_DEVICE(APC_VENDORID, 0x0000), NULL },
|
||||
/* various models */
|
||||
{ USB_DEVICE(APC_VENDORID, 0x0002), general_apc_check },
|
||||
/* various 5G models */
|
||||
|
|
|
@ -1004,7 +1004,7 @@ static void apc_getcaps(int qco)
|
|||
|
||||
/* make sure setvar knows what this is */
|
||||
vt->flags |= APC_RW | APC_ENUM;
|
||||
} else if (vt->flags & APC_PACK)
|
||||
} else if (vt && (vt->flags & APC_PACK))
|
||||
/*
|
||||
* Currently we assume - basing on the following
|
||||
* feedback:
|
||||
|
@ -1208,7 +1208,7 @@ static int getbaseinfo(void)
|
|||
{
|
||||
unsigned int i;
|
||||
int ret, qco;
|
||||
char *cmds, temp[APC_LBUF];
|
||||
char *cmds, *tail, temp[APC_LBUF];
|
||||
|
||||
/*
|
||||
* try firmware lookup first; we could start with 'a', but older models
|
||||
|
@ -1243,9 +1243,16 @@ static int getbaseinfo(void)
|
|||
/*
|
||||
* returned set is verified for validity above, so just extract
|
||||
* what's interesting for us
|
||||
*
|
||||
* the known format is:
|
||||
* ver.alerts.commands[.stuff]
|
||||
*/
|
||||
cmds = strrchr(temp, '.');
|
||||
for (i = 1; i < strlen(cmds); i++)
|
||||
cmds = strchr(temp, '.');
|
||||
cmds = strchr(cmds + 1, '.');
|
||||
tail = strchr(++cmds, '.');
|
||||
if (tail)
|
||||
*tail = 0;
|
||||
for (i = 0; i < strlen(cmds); i++)
|
||||
protocol_verify(cmds[i]);
|
||||
deprecate_vars();
|
||||
|
||||
|
@ -1691,7 +1698,7 @@ static int setvar_enum(apc_vartab_t *vt, const char *val)
|
|||
return STAT_SET_HANDLED; /* FUTURE: no change */
|
||||
}
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (apc_write(APC_NEXTVAL) != 1)
|
||||
return STAT_SET_FAILED;
|
||||
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
#define APC_GOSMART 'Y'
|
||||
#define APC_GODUMB 'R'
|
||||
#define APC_CMDSET 'a'
|
||||
#define APC_CMDSET_FMT "^[0-9]\\.[^.]*\\.[^.]+$"
|
||||
#define APC_CMDSET_FMT "^[0-9]\\.[^.]*\\.[^.]+(\\.[^.]+)?$"
|
||||
#define APC_CAPS '\032' /* ^Z */
|
||||
#define APC_NEXTVAL '-'
|
||||
#define APC_FW_OLD 'V'
|
||||
|
|
373
drivers/asem.c
Normal file
373
drivers/asem.c
Normal file
|
@ -0,0 +1,373 @@
|
|||
/* asem.c - driver for ASEM PB 1300 hardware, accessible through i2c.
|
||||
|
||||
Copyright (C) 2014 Giuseppe Corbelli <giuseppe.corbelli@copanitalia.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
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
|
||||
|
||||
ASEM SPA contributed with support and documentation.
|
||||
Copan Italia SPA funded the development.
|
||||
|
||||
There are 2 versions of the charger. Older one is based on Max1667,
|
||||
newer one is a custom solution. Both are on address 0x09.
|
||||
To be compatible with both versions just read bit 15 of address 0x13
|
||||
to have online/on battery status.
|
||||
Battery monitor is a BQ2060 at address 0x0B.
|
||||
|
||||
Beware that the SystemIO memory used by the i2c controller is reserved by ACPI.
|
||||
On Linux, as of 3.5.x kernel only a native driver (i2c_i801) is available,
|
||||
so you need to boot with acpi_enforce_resources=lax option.
|
||||
*/
|
||||
|
||||
/* Depends on i2c-dev.h, Linux only */
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#ifndef __STR__
|
||||
# define __STR__(x) #x
|
||||
#endif
|
||||
#ifndef __XSTR__
|
||||
# define __XSTR__(x) __STR__(x)
|
||||
#endif
|
||||
|
||||
#define DRIVER_NAME "ASEM"
|
||||
#define DRIVER_VERSION "0.10"
|
||||
|
||||
/* Valid on ASEM PB1300 UPS */
|
||||
#define BQ2060_ADDRESS 0x0B
|
||||
#define CHARGER_ADDRESS 0x09
|
||||
|
||||
#define CMD_DEVICENAME 0x21
|
||||
|
||||
#define LOW_BATTERY_THRESHOLD 25
|
||||
#define HIGH_BATTERY_THRESHOLD 75
|
||||
|
||||
#define ACCESS_DEVICE(fd, address) \
|
||||
if (ioctl(fd, I2C_SLAVE, address) < 0) { \
|
||||
fatal_with_errno(EXIT_FAILURE, "Failed to acquire bus access and/or talk to slave 0x%02X", address); \
|
||||
}
|
||||
|
||||
static unsigned long lb_threshold = LOW_BATTERY_THRESHOLD;
|
||||
static unsigned long hb_threshold = HIGH_BATTERY_THRESHOLD;
|
||||
|
||||
static char *valid_devicename_data[] = {
|
||||
"ASEM SPA",
|
||||
NULL
|
||||
};
|
||||
|
||||
upsdrv_info_t upsdrv_info = {
|
||||
DRIVER_NAME,
|
||||
DRIVER_VERSION,
|
||||
"Giuseppe Corbelli <giuseppe.corbelli@copanitalia.com>",
|
||||
DRV_EXPERIMENTAL,
|
||||
{NULL}
|
||||
};
|
||||
|
||||
void upsdrv_initinfo(void)
|
||||
{
|
||||
__s32 i2c_status;
|
||||
__u8 buffer[10];
|
||||
unsigned short year, month, day;
|
||||
|
||||
ACCESS_DEVICE(upsfd, BQ2060_ADDRESS);
|
||||
|
||||
/* Set capacity mode in mA(h) */
|
||||
i2c_status = i2c_smbus_read_word_data(upsfd, 0x03);
|
||||
if (i2c_status == -1) {
|
||||
fatal_with_errno(EXIT_FAILURE, "Could not read BatteryMode word data");
|
||||
}
|
||||
/* Clear 15th bit */
|
||||
i2c_status = i2c_smbus_write_word_data(upsfd, 0x03, i2c_status & ~0x8000);
|
||||
if (i2c_status == -1) {
|
||||
fatal_with_errno(EXIT_FAILURE, "Could not set BatteryMode word data");
|
||||
}
|
||||
|
||||
/* Device name */
|
||||
memset(buffer, 0, 10);
|
||||
i2c_status = i2c_smbus_read_block_data(upsfd, 0x21, buffer);
|
||||
if (i2c_status == -1) {
|
||||
fatal_with_errno(EXIT_FAILURE, "Could not read DeviceName block data");
|
||||
}
|
||||
upsdebugx(1, "UPS model %s", (char *) buffer);
|
||||
dstate_setinfo("ups.model", "%s", (char *) buffer);
|
||||
|
||||
/* Manufacturing date */
|
||||
i2c_status = i2c_smbus_read_word_data(upsfd, 0x1B);
|
||||
if (i2c_status == -1) {
|
||||
fatal_with_errno(EXIT_FAILURE, "Could not read ManufactureDate word data");
|
||||
}
|
||||
/* (Year - 1980) * 512 */
|
||||
year = (i2c_status >> 9) & 0x000000FF;
|
||||
/* Month * 32 */
|
||||
month = (i2c_status >> 4) & 0x0000001F;
|
||||
day = i2c_status & 0x0000001F;
|
||||
upsdebugx(1, "UPS manufacturing date %d-%02d-%02d (%d)", year + 1980, month, day, i2c_status);
|
||||
dstate_setinfo("ups.mfr.date", "%d-%02d-%02d", year + 1980, month, day);
|
||||
|
||||
/* Device chemistry */
|
||||
memset(buffer, 0, 10);
|
||||
i2c_status = i2c_smbus_read_block_data(upsfd, 0x22, buffer);
|
||||
if (i2c_status == -1) {
|
||||
fatal_with_errno(EXIT_FAILURE, "Could not read DeviceChemistry block data");
|
||||
}
|
||||
upsdebugx(1, "Battery chemistry %s", (char *) buffer);
|
||||
dstate_setinfo("battery.type", "%s", (char *) buffer);
|
||||
|
||||
/* Serial number */
|
||||
i2c_status = i2c_smbus_read_word_data(upsfd, 0x1C);
|
||||
if (i2c_status == -1) {
|
||||
fatal_with_errno(EXIT_FAILURE, "Could not read SerialNumber block data");
|
||||
}
|
||||
upsdebugx(1, "Serial Number %d", i2c_status);
|
||||
dstate_setinfo("ups.serial", "%d", i2c_status);
|
||||
}
|
||||
|
||||
void upsdrv_updateinfo(void)
|
||||
{
|
||||
static char online;
|
||||
static char discharging;
|
||||
static char fully_charged;
|
||||
static unsigned short charge_percentage;
|
||||
static unsigned short voltage;
|
||||
static unsigned short capacity;
|
||||
static signed short current;
|
||||
static __s32 i2c_status;
|
||||
static __s32 temperature;
|
||||
static __s32 runtime_to_empty;
|
||||
|
||||
ACCESS_DEVICE(upsfd, CHARGER_ADDRESS);
|
||||
/* Charger only supplies online/offline status */
|
||||
i2c_status = i2c_smbus_read_word_data(upsfd, 0x13);
|
||||
if (i2c_status == -1) {
|
||||
dstate_datastale();
|
||||
upslogx(LOG_ERR, "Could not read charger status word at address 0x13");
|
||||
return;
|
||||
}
|
||||
online = (i2c_status & 0x8000) != 0;
|
||||
upsdebugx(3, "Charger status 0x%02X, online %d", i2c_status, online);
|
||||
|
||||
ACCESS_DEVICE(upsfd, BQ2060_ADDRESS);
|
||||
i2c_status = i2c_smbus_read_word_data(upsfd, 0x16);
|
||||
if (i2c_status == -1) {
|
||||
dstate_datastale();
|
||||
upslogx(LOG_ERR, "Could not read bq2060 status word at address 0x16");
|
||||
return;
|
||||
}
|
||||
upsdebugx(3, "bq2060 status 0x04%X", i2c_status);
|
||||
/* Busy, leave data as stale, try next time */
|
||||
if (i2c_status & 0x0001) {
|
||||
dstate_datastale();
|
||||
upslogx(LOG_NOTICE, "bq2060 is busy");
|
||||
return;
|
||||
}
|
||||
/* Error, leave data as stale, try next time */
|
||||
if (i2c_status & 0x000F) {
|
||||
dstate_datastale();
|
||||
upslogx(LOG_WARNING, "bq2060 returned error code 0x%02X", i2c_status & 0x000F);
|
||||
return;
|
||||
}
|
||||
|
||||
discharging = (i2c_status & 0x0040);
|
||||
fully_charged = (i2c_status & 0x0020);
|
||||
|
||||
/* Charge percentage */
|
||||
i2c_status = i2c_smbus_read_word_data(upsfd, 0x0D);
|
||||
if (i2c_status == -1) {
|
||||
dstate_datastale();
|
||||
upslogx(LOG_ERR, "Could not read charge percentage from bq2060 at address 0x0D");
|
||||
return;
|
||||
}
|
||||
charge_percentage = i2c_status & 0xFFFF;
|
||||
upsdebugx(3, "Charge percentage %03d", charge_percentage);
|
||||
|
||||
/* Battery voltage in mV */
|
||||
i2c_status = i2c_smbus_read_word_data(upsfd, 0x09);
|
||||
if (i2c_status == -1) {
|
||||
dstate_datastale();
|
||||
upslogx(LOG_ERR, "Could not read voltage from bq2060 at address 0x09");
|
||||
return;
|
||||
}
|
||||
voltage = i2c_status & 0x0000FFFF;
|
||||
upsdebugx(3, "Battery voltage %d mV", voltage);
|
||||
|
||||
/* Temperature in °K */
|
||||
temperature = i2c_smbus_read_word_data(upsfd, 0x08);
|
||||
if (temperature == -1) {
|
||||
dstate_datastale();
|
||||
upslogx(LOG_ERR, "Could not read temperature from bq2060 at address 0x08");
|
||||
return;
|
||||
}
|
||||
upsdebugx(3, "Temperature %4.1f K", temperature / 10.0);
|
||||
|
||||
/* Current load in mA, positive for charge, negative for discharge */
|
||||
i2c_status = i2c_smbus_read_word_data(upsfd, 0x0A);
|
||||
if (i2c_status == -1) {
|
||||
dstate_datastale();
|
||||
upslogx(LOG_ERR, "Could not read current from bq2060 at address 0x0A");
|
||||
return;
|
||||
}
|
||||
current = i2c_status & 0x0000FFFF;
|
||||
upsdebugx(3, "Current %d mA", current);
|
||||
|
||||
/* Current capacity */
|
||||
i2c_status = i2c_smbus_read_word_data(upsfd, 0x0F);
|
||||
if (i2c_status == -1) {
|
||||
dstate_datastale();
|
||||
upslogx(LOG_ERR, "Could not read RemainingCapacity word data");
|
||||
return;
|
||||
}
|
||||
capacity = i2c_status & 0x0000FFFF;
|
||||
upsdebugx(3, "Current capacity %d mAh", capacity);
|
||||
|
||||
/* Expected runtime capacity, averaged by gauge */
|
||||
runtime_to_empty = i2c_smbus_read_word_data(upsfd, 0x12);
|
||||
if (runtime_to_empty == -1) {
|
||||
dstate_datastale();
|
||||
upslogx(LOG_ERR, "Could not read AverageTimeToEmpty word data");
|
||||
return;
|
||||
}
|
||||
upsdebugx(3, "Expected run-time to empty %d m", runtime_to_empty);
|
||||
|
||||
status_init();
|
||||
status_set(online ? "OL" : "OB");
|
||||
if (!discharging & !fully_charged)
|
||||
status_set("CHRG");
|
||||
else if (discharging && current < 0)
|
||||
status_set("DISCHRG");
|
||||
|
||||
if (charge_percentage >= hb_threshold)
|
||||
status_set("HB");
|
||||
else if (charge_percentage <= lb_threshold)
|
||||
status_set("LB");
|
||||
|
||||
/* In V */
|
||||
dstate_setinfo("battery.voltage", "%2.3f", voltage / 1000.0);
|
||||
/* In mAh */
|
||||
dstate_setinfo("battery.current", "%2.3f", current / 1000.0);
|
||||
dstate_setinfo("battery.charge", "%d", charge_percentage);
|
||||
/* In mAh */
|
||||
dstate_setinfo("battery.capacity", "%2.3f", capacity / 1000.0);
|
||||
/* In °C */
|
||||
dstate_setinfo("ups.temperature", "%4.1f", (temperature / 10.0) - 273.15);
|
||||
/* In seconds */
|
||||
dstate_setinfo("battery.runtime", "%d", runtime_to_empty * 60);
|
||||
status_commit();
|
||||
dstate_dataok();
|
||||
}
|
||||
|
||||
void upsdrv_shutdown(void)
|
||||
{
|
||||
/* tell the UPS to shut down, then return - DO NOT SLEEP HERE */
|
||||
|
||||
/* maybe try to detect the UPS here, but try a shutdown even if
|
||||
it doesn't respond at first if possible */
|
||||
|
||||
/* replace with a proper shutdown function */
|
||||
fatalx(EXIT_FAILURE, "shutdown not supported");
|
||||
|
||||
/* you may have to check the line status since the commands
|
||||
for toggling power are frequently different for OL vs. OB */
|
||||
|
||||
/* OL: this must power cycle the load if possible */
|
||||
|
||||
/* OB: the load must remain off until the power returns */
|
||||
}
|
||||
|
||||
void upsdrv_help(void)
|
||||
{
|
||||
/* Redundant */
|
||||
printf("\nASEM options\n");
|
||||
printf(" HIGH/low battery thresholds\n");
|
||||
printf(" lb = " __XSTR__(LOW_BATTERY_THRESHOLD) " (battery is low under this level)\n");
|
||||
printf(" hb = " __XSTR__(HIGH_BATTERY_THRESHOLD) " (battery is high above this level)\n");
|
||||
}
|
||||
|
||||
/* list flags and values that you want to receive via -x */
|
||||
void upsdrv_makevartable(void)
|
||||
{
|
||||
addvar(VAR_VALUE, "lb", "Low battery threshold, default " __XSTR__(LOW_BATTERY_THRESHOLD));
|
||||
addvar(VAR_VALUE, "hb", "High battery threshold, default " __XSTR__(HIGH_BATTERY_THRESHOLD));
|
||||
}
|
||||
|
||||
void upsdrv_initups(void)
|
||||
{
|
||||
__s32 i2c_status;
|
||||
__u8 DeviceName_buffer[10];
|
||||
unsigned int i;
|
||||
unsigned long x;
|
||||
char *DeviceName;
|
||||
char *option;
|
||||
|
||||
upsfd = open(device_path, O_RDWR);
|
||||
if (upsfd < 0) {
|
||||
fatal_with_errno(EXIT_FAILURE, "Could not open device port '%s'", device_path);
|
||||
}
|
||||
|
||||
ACCESS_DEVICE(upsfd, BQ2060_ADDRESS);
|
||||
|
||||
/* Get ManufacturerName */
|
||||
memset(DeviceName_buffer, 0, 10);
|
||||
i2c_status = i2c_smbus_read_block_data(upsfd, 0x20, DeviceName_buffer);
|
||||
if (i2c_status == -1) {
|
||||
fatal_with_errno(EXIT_FAILURE, "Could not read DeviceName block data");
|
||||
}
|
||||
i = 0;
|
||||
while ( (DeviceName = valid_devicename_data[i++]) ) {
|
||||
if (0 == memcmp(DeviceName, DeviceName_buffer, i2c_status))
|
||||
break;
|
||||
}
|
||||
if (!DeviceName) {
|
||||
fatal_with_errno(EXIT_FAILURE, "Device '%s' unknown", (char *) DeviceName_buffer);
|
||||
}
|
||||
upsdebugx(1, "Found device '%s' on port '%s'", (char *) DeviceName, device_path);
|
||||
dstate_setinfo("ups.mfr", "%s", (char *) DeviceName);
|
||||
|
||||
option = getval("lb");
|
||||
if (option) {
|
||||
x = strtoul(option, NULL, 0);
|
||||
if ((x == 0) && (errno != 0)) {
|
||||
upslogx(LOG_WARNING, "Invalid value specified for low battery threshold: '%s'", option);
|
||||
} else {
|
||||
lb_threshold = x;
|
||||
}
|
||||
}
|
||||
option = getval("hb");
|
||||
if (option) {
|
||||
x = strtoul(option, NULL, 0);
|
||||
if ((x == 0) && (errno != 0)) {
|
||||
upslogx(LOG_WARNING, "Invalid value specified for high battery threshold: '%s'", option);
|
||||
} else if ((x < 1) || (x > 100)) {
|
||||
upslogx(LOG_WARNING, "Invalid value specified for high battery threshold: '%s' (must be 1 < hb <= 100)", option);
|
||||
} else {
|
||||
hb_threshold = x;
|
||||
}
|
||||
}
|
||||
/* Invalid values specified */
|
||||
if (lb_threshold > hb_threshold) {
|
||||
upslogx(LOG_WARNING, "lb > hb specified in options. Returning to defaults.");
|
||||
lb_threshold = LOW_BATTERY_THRESHOLD;
|
||||
hb_threshold = HIGH_BATTERY_THRESHOLD;
|
||||
}
|
||||
|
||||
upslogx(LOG_NOTICE, "High battery threshold is %lu, low battery threshold is %lu", lb_threshold, hb_threshold);
|
||||
}
|
||||
|
||||
void upsdrv_cleanup(void)
|
||||
{
|
||||
close(upsfd);
|
||||
}
|
915
drivers/bcmxcp.c
915
drivers/bcmxcp.c
File diff suppressed because it is too large
Load diff
|
@ -77,6 +77,22 @@
|
|||
#define PW_TURN_OFF_DELAY 3
|
||||
#define PW_TURN_ON_DELAY 4
|
||||
|
||||
/* Config vars*/
|
||||
#define PW_CONF_BYPASS_FREQ_DEV_LIMIT 0x01
|
||||
#define PW_CONF_LOW_DEV_LIMIT 0x02
|
||||
#define PW_CONF_HIGH_DEV_LIMIT 0x03
|
||||
#define PW_CONF_PHASE_DEV_LIMIT 0x04
|
||||
#define PW_CONF_LOW_BATT 0x05
|
||||
#define PW_CONF_BEEPER 0x06
|
||||
#define PW_CONF_RETURN_DELAY 0x07
|
||||
#define PW_CONF_RETURN_CAP 0x08
|
||||
#define PW_CONF_MAX_TEMP 0x0a
|
||||
#define PW_CONF_NOMINAL_OUT_VOLTAGE 0x0b
|
||||
#define PW_CONF_SLEEP_TH_LOAD 0x0d
|
||||
#define PW_CONF_SLEEP_DELAY 0x0e
|
||||
#define PW_CONF_BATT_STRINGS 0x0f
|
||||
#define PW_CONF_REQ 0xff
|
||||
|
||||
/* Config block offsets */
|
||||
#define BCMXCP_CONFIG_BLOCK_MACHINE_TYPE_CODE 0
|
||||
#define BCMXCP_CONFIG_BLOCK_MODEL_NUMBER 2
|
||||
|
@ -97,6 +113,18 @@
|
|||
#define BCMXCP_CONFIG_BLOCK_PART_NUMBER 48
|
||||
#define BCMXCP_CONFIG_BLOCK_SERIAL_NUMBER 64
|
||||
|
||||
/*Battery block offsets*/
|
||||
|
||||
#define BCMXCP_BATTDATA_BLOCK_BATT_TEST_STATUS 0
|
||||
#define BCMXCP_BATTDATA_BLOCK_BATT_VOLTS_T1 1
|
||||
#define BCMXCP_BATTDATA_BLOCK_BATT_VOLTS_T2 5
|
||||
#define BCMXCP_BATTDATA_BLOCK_TEST_DURATION 9
|
||||
#define BCMXCP_BATTDATA_BLOCK_UTIL_VOLT 10
|
||||
#define BCMXCP_BATTDATA_BLOCK_INPUT_CURRENT 14
|
||||
#define BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS 18
|
||||
/*BATT_TEST_STATUS for external strings (1 byte each) if BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS == 0 no external test statuses at all*/
|
||||
/*next - number of ABM Statuses - at least 1 for internal batteries*/
|
||||
|
||||
/* Index for Extende Limits block offsets */
|
||||
#define BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_VOLTAGE 0
|
||||
#define BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_FREQ 2
|
||||
|
@ -114,6 +142,8 @@
|
|||
#define BCMXCP_EXT_LIMITS_BLOCK_BATT_CAPACITY_RETURN 24
|
||||
#define BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_LOW 25
|
||||
#define BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_HIGE 26
|
||||
#define BCMXCP_EXT_LIMITS_BLOCK_SLEEP_TH_LOAD 29
|
||||
#define BCMXCP_EXT_LIMITS_BLOCK_SLEEP_DELAY 30
|
||||
|
||||
/* Indexes for meter map */
|
||||
#define BCMXCP_METER_MAP_OUTPUT_VOLTS_AB 0 /* mapped */
|
||||
|
|
|
@ -29,20 +29,20 @@
|
|||
#include "belkin-hid.h"
|
||||
#include "usb-common.h"
|
||||
|
||||
#define BELKIN_HID_VERSION "Belkin HID 0.16"
|
||||
#define BELKIN_HID_VERSION "Belkin/Liebert HID 0.17"
|
||||
|
||||
/* Belkin */
|
||||
#define BELKIN_VENDORID 0x050d
|
||||
|
||||
/* Liebert */
|
||||
#define LIEBERT_VENDORID 0x10af
|
||||
/* Note that there are at least two Liebert firmware types which both report
|
||||
|
||||
/*! USB IDs device table.
|
||||
* Note that there are at least two Liebert firmware types which both report
|
||||
* a VID:PID of 10af:0001. The newer ones tend not to have the Belkin broken
|
||||
* Usage Pages (and therefore use standard HID PDC paths) but they have
|
||||
* incorrect exponents for some fields.
|
||||
*/
|
||||
|
||||
/* USB IDs device table */
|
||||
static usb_device_id_t belkin_usb_device_table[] = {
|
||||
/* F6C800-UNV */
|
||||
{ USB_DEVICE(BELKIN_VENDORID, 0x0980), NULL },
|
||||
|
@ -60,6 +60,8 @@ static usb_device_id_t belkin_usb_device_table[] = {
|
|||
{ USB_DEVICE(BELKIN_VENDORID, 0x0751), NULL },
|
||||
/* F6H375-USB */
|
||||
{ USB_DEVICE(BELKIN_VENDORID, 0x0375), NULL },
|
||||
/* Regulator PRO-USB */
|
||||
{ USB_DEVICE(BELKIN_VENDORID, 0x0f51), NULL },
|
||||
/* F6C1100-UNV, F6C1200-UNV */
|
||||
{ USB_DEVICE(BELKIN_VENDORID, 0x1100), NULL },
|
||||
|
||||
|
@ -67,6 +69,8 @@ static usb_device_id_t belkin_usb_device_table[] = {
|
|||
{ USB_DEVICE(LIEBERT_VENDORID, 0x0001), NULL },
|
||||
/* Liebert PowerSure PSI 1440 */
|
||||
{ USB_DEVICE(LIEBERT_VENDORID, 0x0004), NULL },
|
||||
/* Liebert GXT3 */
|
||||
{ USB_DEVICE(LIEBERT_VENDORID, 0x0008), NULL },
|
||||
|
||||
/* Terminating entry */
|
||||
{ -1, -1, NULL }
|
||||
|
@ -521,6 +525,22 @@ static hid_info_t belkin_hid2nut[] = {
|
|||
yet implemented) workaround, see the belkinunv(8) man page.
|
||||
-PS 2005/08/28 */
|
||||
|
||||
#if 0
|
||||
/* added for debugging Liebert GXT3 : */
|
||||
{ "unmapped.ups.powersummary.iserialnumber", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%.0f", 0, NULL },
|
||||
{ "unmapped.ups.powersummary.imanufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%.0f", 0, NULL },
|
||||
{ "unmapped.ups.powersummary.ioeminformation", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", 0, stringid_conversion },
|
||||
{ "unmapped.ups.powersummary.designcapacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", 0, NULL },
|
||||
{ "unmapped.ups.powersummary.remainingtimelimit", 0, 0, "UPS.PowerSummary.RemainingTimeLimit", NULL, "%.0f", 0, NULL },
|
||||
{ "unmapped.ups.powersummary.capacitymode", 0, 0, "UPS.PowerSummary.CapacityMode", NULL, "%.0f", 0, NULL },
|
||||
{ "unmapped.ups.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL },
|
||||
{ "unmapped.ups.powersummary.batterypresent", 0, 0, "UPS.PowerSummary.BatteryPresent", NULL, "%.0f", 0, NULL },
|
||||
{ "unmapped.ups.powersummary.fullchargecapacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL },
|
||||
{ "unmapped.ups.powersummary.capacitygranularity1", 0, 0, "UPS.PowerSummary.CapacityGranularity1", NULL, "%.0f", 0, NULL },
|
||||
{ "unmapped.ups.powersummary.capacitygranularity2", 0, 0, "UPS.PowerSummary.CapacityGranularity2", NULL, "%.0f", 0, NULL },
|
||||
{ "unmapped.ups.powersummary.iproduct", 0, 0, "UPS.PowerSummary.iProduct", NULL, "%.0f", 0, NULL },
|
||||
#endif
|
||||
|
||||
/* end of structure. */
|
||||
{ NULL, 0, 0, NULL, NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
|
|
@ -498,14 +498,7 @@ void upsdrv_help(void)
|
|||
void upsdrv_makevartable(void)
|
||||
{
|
||||
addvar(VAR_VALUE, "subdriver", "Serial-over-USB subdriver selection");
|
||||
addvar(VAR_VALUE, "vendorid", "Regular expression to match UPS Manufacturer numerical ID (4 digits hexadecimal)");
|
||||
addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product numerical ID (4 digits hexadecimal)");
|
||||
|
||||
addvar(VAR_VALUE, "vendor", "Regular expression to match UPS Manufacturer string");
|
||||
addvar(VAR_VALUE, "product", "Regular expression to match UPS Product string");
|
||||
addvar(VAR_VALUE, "serial", "Regular expression to match UPS Serial number");
|
||||
|
||||
addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name");
|
||||
nut_usb_addvars();
|
||||
|
||||
addvar(VAR_VALUE, "langid_fix", "Apply the language ID workaround to the krauler subdriver (0x409 or 0x4095)");
|
||||
|
||||
|
|
|
@ -83,15 +83,19 @@
|
|||
#define CPQPOWER_OID_ALARM_LB ".1.3.6.1.4.1.232.165.3.7.4.0" /* UPS-MIB::upsLowBattery */
|
||||
|
||||
|
||||
/* Not used, as no longer supported by MIB ver. 1.76 (Github issue 118)
|
||||
static info_lkp_t cpqpower_alarm_ob[] = {
|
||||
{ 1, "OB" },
|
||||
{ 0, "NULL" }
|
||||
} ;
|
||||
};
|
||||
*/
|
||||
|
||||
/* Not used, as no longer supported by MIB ver. 1.76 (Github issue 118)
|
||||
static info_lkp_t cpqpower_alarm_lb[] = {
|
||||
{ 1, "LB" },
|
||||
{ 0, "NULL" }
|
||||
} ;
|
||||
};
|
||||
*/
|
||||
|
||||
/* Defines for CPQPOWER_OID_POWER_STATUS (1) */
|
||||
static info_lkp_t cpqpower_pwr_info[] = {
|
||||
|
|
|
@ -28,29 +28,36 @@
|
|||
#include "cps-hid.h"
|
||||
#include "usb-common.h"
|
||||
|
||||
#define CPS_HID_VERSION "CyberPower HID 0.3"
|
||||
#define CPS_HID_VERSION "CyberPower HID 0.4"
|
||||
|
||||
/* Cyber Power Systems */
|
||||
#define CPS_VENDORID 0x0764
|
||||
|
||||
/*
|
||||
/*! Battery voltage scale factor.
|
||||
* For some devices, the reported battery voltage is off by factor
|
||||
* of 1.5 so we need to apply a scale factor to it to get the real
|
||||
* battery voltage. By default, the factor is 1 (no scaling).
|
||||
*/
|
||||
static double battery_scale = 1;
|
||||
static int might_need_battery_scale = 0;
|
||||
static int battery_scale_checked = 0;
|
||||
|
||||
/*! If the ratio of the battery voltage to the nominal battery voltage exceeds
|
||||
* this factor, we assume that the battery voltage needs to be scaled by 2/3.
|
||||
*/
|
||||
static const double battery_voltage_sanity_check = 1.4;
|
||||
|
||||
static void *cps_battery_scale(USBDevice_t *device)
|
||||
{
|
||||
battery_scale = 0.667;
|
||||
might_need_battery_scale = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* USB IDs device table */
|
||||
static usb_device_id_t cps_usb_device_table[] = {
|
||||
/* 900AVR/BC900D, CP1200AVR/BC1200D */
|
||||
/* 900AVR/BC900D */
|
||||
{ USB_DEVICE(CPS_VENDORID, 0x0005), NULL },
|
||||
/* Dynex DX-800U? */
|
||||
/* Dynex DX-800U?, CP1200AVR/BC1200D, CP825AVR-G, CP1000AVRLCD, CP1000PFCLCD, CP1500C, CP550HG, etc. */
|
||||
{ USB_DEVICE(CPS_VENDORID, 0x0501), &cps_battery_scale },
|
||||
/* OR2200LCDRM2U, OR700LCDRM1U, PR6000LCDRTXL5U */
|
||||
{ USB_DEVICE(CPS_VENDORID, 0x0601), NULL },
|
||||
|
@ -59,12 +66,48 @@ static usb_device_id_t cps_usb_device_table[] = {
|
|||
{ -1, -1, NULL }
|
||||
};
|
||||
|
||||
/*! Adjusts @a battery_scale if voltage is well above nominal.
|
||||
*/
|
||||
static void cps_adjust_battery_scale(double batt_volt)
|
||||
{
|
||||
const char *batt_volt_nom_str;
|
||||
double batt_volt_nom;
|
||||
|
||||
if(battery_scale_checked) {
|
||||
return;
|
||||
}
|
||||
|
||||
batt_volt_nom_str = dstate_getinfo("battery.voltage.nominal");
|
||||
if(!batt_volt_nom_str) {
|
||||
upsdebugx(2, "%s: 'battery.voltage.nominal' not available yet; skipping scale determination", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
batt_volt_nom = strtod(batt_volt_nom_str, NULL);
|
||||
if(batt_volt_nom == 0) {
|
||||
upsdebugx(3, "%s: 'battery.voltage.nominal' is %s", __func__, batt_volt_nom_str);
|
||||
return;
|
||||
}
|
||||
|
||||
if( (batt_volt / batt_volt_nom) > battery_voltage_sanity_check ) {
|
||||
upslogx(LOG_INFO, "%s: battery readings will be scaled by 2/3", __func__);
|
||||
battery_scale = 2.0/3;
|
||||
}
|
||||
|
||||
battery_scale_checked = 1;
|
||||
}
|
||||
|
||||
/* returns statically allocated string - must not use it again before
|
||||
done with result! */
|
||||
static const char *cps_battvolt_fun(double value)
|
||||
{
|
||||
static char buf[8];
|
||||
|
||||
if(might_need_battery_scale) {
|
||||
cps_adjust_battery_scale(value);
|
||||
}
|
||||
|
||||
upsdebugx(5, "%s: battery_scale = %.3f", __func__, battery_scale);
|
||||
snprintf(buf, sizeof(buf), "%.1f", battery_scale * value);
|
||||
|
||||
return buf;
|
||||
|
|
|
@ -238,23 +238,24 @@ static void sock_connect(int sock)
|
|||
}
|
||||
|
||||
/* enable nonblocking I/O */
|
||||
if (!do_synchronous) {
|
||||
ret = fcntl(fd, F_GETFL, 0);
|
||||
|
||||
ret = fcntl(fd, F_GETFL, 0);
|
||||
if (ret < 0) {
|
||||
upslog_with_errno(LOG_ERR, "fcntl get on unix fd failed");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
upslog_with_errno(LOG_ERR, "fcntl get on unix fd failed");
|
||||
close(fd);
|
||||
return;
|
||||
ret = fcntl(fd, F_SETFL, ret | O_NDELAY);
|
||||
|
||||
if (ret < 0) {
|
||||
upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on unix fd failed");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ret = fcntl(fd, F_SETFL, ret | O_NDELAY);
|
||||
|
||||
if (ret < 0) {
|
||||
upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on unix fd failed");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
conn = xcalloc(1, sizeof(*conn));
|
||||
conn->fd = fd;
|
||||
|
||||
|
|
|
@ -41,6 +41,10 @@ typedef struct conn_s {
|
|||
|
||||
extern struct ups_handler upsh;
|
||||
|
||||
/* asynchronous (nonblocking) Vs synchronous (blocking) I/O
|
||||
* Defaults to nonblocking, for backward compatibility */
|
||||
extern int do_synchronous;
|
||||
|
||||
void 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, ...)
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
/* eaton-mib.c - data to monitor Eaton Aphel PDUs (Basic and Complex)
|
||||
/* eaton-mib.c - data to monitor Eaton ePDUs:
|
||||
* G1 Aphel based ePDUs (Basic and Complex)
|
||||
* G1 Pulizzi Monitored and Switched ePDUs
|
||||
* G2 Marlin SW / MI / MO / MA
|
||||
* G3 Shark SW / MI / MO / MA
|
||||
*
|
||||
* Copyright (C) 2008 - 2012
|
||||
* Copyright (C) 2008 - 2015
|
||||
* Arnaud Quette <arnaud.quette@gmail.com>
|
||||
* Arnaud Quette <ArnaudQuette@Eaton.com>
|
||||
*
|
||||
|
@ -30,7 +34,8 @@
|
|||
|
||||
/* APHEL-GENESIS-II-MIB (monitored ePDU)
|
||||
* *************************************
|
||||
* Note: we should also be able to support this one using netxml-ups!
|
||||
* Note: There is also a basic XML interface, but not worth
|
||||
* implementing in netxml-ups!
|
||||
*/
|
||||
|
||||
#define APHEL1_OID_MIB ".1.3.6.1.4.1.17373"
|
||||
|
@ -212,7 +217,7 @@ static snmp_info_t eaton_aphel_revelation_mib[] = {
|
|||
/* Eaton PDU-MIB - Marlin MIB
|
||||
* ************************** */
|
||||
|
||||
#define EATON_MARLIN_MIB_VERSION "0.06"
|
||||
#define EATON_MARLIN_MIB_VERSION "0.10"
|
||||
#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"
|
||||
|
||||
|
@ -220,7 +225,7 @@ static info_lkp_t marlin_outlet_status_info[] = {
|
|||
{ 0, "off" },
|
||||
{ 1, "on" },
|
||||
{ 2, "pendingOff" }, /* transitional status */
|
||||
{ 3, "pendingOn" }, /* transitional status */
|
||||
{ 3, "pendingOn" }, /* transitional status */
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
@ -234,6 +239,7 @@ static info_lkp_t outlet_switchability_info[] = {
|
|||
|
||||
/* Snmp2NUT lookup table for Eaton Marlin MIB */
|
||||
static snmp_info_t eaton_marlin_mib[] = {
|
||||
|
||||
/* Device page */
|
||||
{ "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON",
|
||||
SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL },
|
||||
|
@ -277,12 +283,61 @@ static snmp_info_t eaton_marlin_mib[] = {
|
|||
*/
|
||||
|
||||
/* Input page */
|
||||
/* Historically, some of these data were previously published as
|
||||
* outlet.{realpower,...}
|
||||
* However, it's more suitable and logic to have these on input.{...}
|
||||
*/
|
||||
{ "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_OK, NULL, NULL },
|
||||
/* inputType.0.1 singlePhase (1) iso.3.6.1.4.1.534.6.6.7.3.1.1.2.0.1 */
|
||||
/* FIXME: to be implemented
|
||||
* inputType.0.1 iso.3.6.1.4.1.534.6.6.7.3.1.1.2.0.1
|
||||
* singlePhase (1), ... split phase, three phase delta, or three phase wye
|
||||
*/
|
||||
|
||||
/* Frequency is measured globally */
|
||||
{ "input.frequency", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.0.1", NULL, 0, NULL, NULL },
|
||||
|
||||
/* 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.0.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL },
|
||||
{ "input.L1.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.0.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL },
|
||||
{ "input.L2.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.0.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL },
|
||||
{ "input.L3.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.0.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL },
|
||||
|
||||
/* FIXME:
|
||||
* - Voltage is only mesured per phase, as mV!
|
||||
* so input.voltage == input.L1.voltage for both single and 3phase
|
||||
* - As per NUT namespace (http://www.networkupstools.org/docs/developer-guide.chunked/apas01.html#_valid_contexts)
|
||||
* 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)*/
|
||||
{ "input.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.0.1.1", NULL, 0, NULL, NULL },
|
||||
/* FIXME: check multiplier */
|
||||
{ "input.current", 0, 0.01, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.1", NULL, 0, NULL, NULL },
|
||||
{ "input.L1.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.0.1.1", NULL, 0, NULL, NULL },
|
||||
{ "input.L2.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.0.1.2", NULL, 0, NULL, NULL },
|
||||
{ "input.L3.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.0.1.3", NULL, 0, NULL, NULL },
|
||||
|
||||
/* FIXME:
|
||||
* - input.current is mapped on input.L1.current for both single and 3phase !!! */
|
||||
{ "input.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.1", NULL, 0, NULL, NULL },
|
||||
{ "input.L1.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.1", NULL, 0, NULL, NULL },
|
||||
{ "input.L2.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.2", NULL, 0, NULL, NULL },
|
||||
{ "input.L3.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.3", NULL, 0, NULL, 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.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL, 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.0.1.4", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL, 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.0.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL },
|
||||
{ "input.L1.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.0.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL },
|
||||
{ "input.L2.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.0.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL },
|
||||
{ "input.L3.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.0.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL },
|
||||
/* Sum of all phases apparent power, valid for 3phase only */
|
||||
{ "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.4", NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL, NULL },
|
||||
/* Sum of the phase apparent power, valid for 1phase only (fallback for also publishing) */
|
||||
{ "input.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL },
|
||||
{ "input.L1.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL },
|
||||
{ "input.L2.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.2", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL },
|
||||
{ "input.L3.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.3", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL },
|
||||
|
||||
/* Ambient page */
|
||||
/* We use critical levels, for both temperature and humidity,
|
||||
|
@ -300,13 +355,12 @@ static snmp_info_t eaton_marlin_mib[] = {
|
|||
SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL },
|
||||
{ "outlet.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.22.0", "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL },
|
||||
/* 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.0.1", NULL, 0, NULL, NULL },
|
||||
{ "outlet.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.0.1.1", NULL, 0, NULL, NULL },
|
||||
{ "outlet.current", 0, 0.01, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.1", NULL, 0, NULL, NULL },
|
||||
/* There is also a .2 available (ie .1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.2) */
|
||||
{ "outlet.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.0.1.2", NULL, 0, NULL, NULL },
|
||||
/* There is also a .2 available (ie .1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.2) */
|
||||
{ "outlet.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.1", NULL, 0, NULL, NULL },
|
||||
{ "outlet.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.0.1.4", NULL, 0, NULL, NULL },
|
||||
{ "outlet.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.4", NULL, 0, NULL, NULL },
|
||||
|
||||
/* outlet template definition
|
||||
* Indexes start from 1, ie outlet.1 => <OID>.1 */
|
||||
|
|
|
@ -39,19 +39,30 @@ upsdrv_info_t upsdrv_info = {
|
|||
|
||||
static void parse_output_signals(const char *value, int *line)
|
||||
{
|
||||
int old_line = *line;
|
||||
/* parse signals the serial port can output */
|
||||
|
||||
*line = 0;
|
||||
|
||||
upsdebugx(4, "%s: enter", __func__);
|
||||
|
||||
/* Note: for future drivers, please use strtok() or similar tokenizing
|
||||
* methods, such that it is easier to spot configuration mistakes. With
|
||||
* this code, a misspelled control line may go unnoticed. I'd fix it
|
||||
* The Right Way (tm), but these UPSes are ancient.
|
||||
*/
|
||||
if (strstr(value, "DTR") && !strstr(value, "-DTR")) {
|
||||
upsdebugx(3, "%s: override DTR", __func__);
|
||||
*line |= TIOCM_DTR;
|
||||
}
|
||||
|
||||
if (strstr(value, "RTS") && !strstr(value, "-RTS")) {
|
||||
upsdebugx(3, "%s: override RTS", __func__);
|
||||
*line |= TIOCM_RTS;
|
||||
}
|
||||
|
||||
if (strstr(value, "ST")) {
|
||||
upsdebugx(3, "%s: override ST", __func__);
|
||||
*line |= TIOCM_ST;
|
||||
}
|
||||
|
||||
|
@ -70,20 +81,32 @@ static void parse_output_signals(const char *value, int *line)
|
|||
if (strstr(value, "DSR")) {
|
||||
fatalx(EXIT_FAILURE, "Can't override output with DSR (not an output)");
|
||||
}
|
||||
|
||||
if(*line == old_line) {
|
||||
upslogx(LOG_NOTICE, "%s: output overrides specified, but no effective difference - check for typos?", __func__);
|
||||
}
|
||||
|
||||
upsdebugx(4, "%s: exit", __func__);
|
||||
}
|
||||
|
||||
static void parse_input_signals(const char *value, int *line, int *val)
|
||||
{
|
||||
/* parse signals the serial port can input */
|
||||
int old_line = *line, old_val = *val;
|
||||
|
||||
*line = 0;
|
||||
*val = 0;
|
||||
|
||||
upsdebugx(4, "%s: enter", __func__);
|
||||
|
||||
if (strstr(value, "CTS")) {
|
||||
*line |= TIOCM_CTS;
|
||||
|
||||
if (!strstr(value, "-CTS")) {
|
||||
upsdebugx(3, "%s: override CTS (active low)", __func__);
|
||||
*val |= TIOCM_CTS;
|
||||
} else {
|
||||
upsdebugx(3, "%s: override CTS", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,7 +114,10 @@ static void parse_input_signals(const char *value, int *line, int *val)
|
|||
*line |= TIOCM_CD;
|
||||
|
||||
if (!strstr(value, "-DCD")) {
|
||||
upsdebugx(3, "%s: override DCD (active low)", __func__);
|
||||
*val |= TIOCM_CD;
|
||||
} else {
|
||||
upsdebugx(3, "%s: override DCD", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +125,10 @@ static void parse_input_signals(const char *value, int *line, int *val)
|
|||
*line |= TIOCM_RNG;
|
||||
|
||||
if (!strstr(value, "-RNG")) {
|
||||
upsdebugx(3, "%s: override RNG (active low)", __func__);
|
||||
*val |= TIOCM_RNG;
|
||||
} else {
|
||||
upsdebugx(3, "%s: override RNG", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,7 +136,10 @@ static void parse_input_signals(const char *value, int *line, int *val)
|
|||
*line |= TIOCM_DSR;
|
||||
|
||||
if (!strstr(value, "-DSR")) {
|
||||
upsdebugx(3, "%s: override DSR (active low)", __func__);
|
||||
*val |= TIOCM_DSR;
|
||||
} else {
|
||||
upsdebugx(3, "%s: override DSR", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,6 +154,12 @@ static void parse_input_signals(const char *value, int *line, int *val)
|
|||
if (strstr(value, "ST")) {
|
||||
fatalx(EXIT_FAILURE, "Can't override input with ST (not an input)");
|
||||
}
|
||||
|
||||
if((*line == old_line) && (*val == old_val)) {
|
||||
upslogx(LOG_NOTICE, "%s: input overrides specified, but no effective difference - check for typos?", __func__);
|
||||
}
|
||||
|
||||
upsdebugx(4, "%s: exit", __func__);
|
||||
}
|
||||
|
||||
void upsdrv_initinfo(void)
|
||||
|
|
|
@ -59,6 +59,10 @@ static double exponent(double a, int8_t b);
|
|||
/* Tweak flag for APC Back-UPS */
|
||||
int max_report_size = 0;
|
||||
|
||||
/* Tweaks for Powercom, at least */
|
||||
int interrupt_only = 0;
|
||||
int unsigned interrupt_size = 0;
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* report buffering system */
|
||||
|
||||
|
@ -148,7 +152,7 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa
|
|||
int id = pData->ReportID;
|
||||
int r;
|
||||
|
||||
if (rbuf->ts[id] + age > time(NULL)) {
|
||||
if (interrupt_only || rbuf->ts[id] + age > time(NULL)) {
|
||||
/* buffered report is still good; nothing to do */
|
||||
upsdebug_hex(3, "Report[buf]", rbuf->data[id], rbuf->len[id]);
|
||||
return 0;
|
||||
|
@ -346,7 +350,7 @@ HIDData_t *HIDGetItemData(const char *hidpath, usage_tables_t *utab)
|
|||
}
|
||||
|
||||
/* Get info on object (reportID, offset and size) */
|
||||
return FindObject_with_Path(pDesc, &Path, ITEM_FEATURE);
|
||||
return FindObject_with_Path(pDesc, &Path, interrupt_only ? ITEM_INPUT:ITEM_FEATURE);
|
||||
}
|
||||
|
||||
char *HIDGetDataItem(const HIDData_t *hiddata, usage_tables_t *utab)
|
||||
|
@ -479,7 +483,7 @@ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize)
|
|||
HIDData_t *pData;
|
||||
|
||||
/* needs libusb-0.1.8 to work => use ifdef and autoconf */
|
||||
buflen = comm_driver->get_interrupt(udev, buf, sizeof(buf), 250);
|
||||
buflen = comm_driver->get_interrupt(udev, buf, interrupt_size ? interrupt_size:sizeof(buf), 250);
|
||||
if (buflen <= 0) {
|
||||
return buflen; /* propagate "error" or "no event" code */
|
||||
}
|
||||
|
|
|
@ -89,6 +89,9 @@ typedef struct reportbuf_s {
|
|||
|
||||
extern reportbuf_t *reportbuf; /* buffer for most recent reports */
|
||||
|
||||
extern int interrupt_only;
|
||||
extern unsigned int interrupt_size;
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
|
|
|
@ -49,6 +49,23 @@ upsdrv_info_t comm_upsdrv_info = {
|
|||
|
||||
static void libusb_close(usb_dev_handle *udev);
|
||||
|
||||
/*! Add USB-related driver variables with addvar().
|
||||
* This removes some code duplication across the USB drivers.
|
||||
*/
|
||||
void nut_usb_addvars(void)
|
||||
{
|
||||
/* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */
|
||||
addvar(VAR_VALUE, "vendor", "Regular expression to match UPS Manufacturer string");
|
||||
addvar(VAR_VALUE, "product", "Regular expression to match UPS Product string");
|
||||
addvar(VAR_VALUE, "serial", "Regular expression to match UPS Serial number");
|
||||
|
||||
addvar(VAR_VALUE, "vendorid", "Regular expression to match UPS Manufacturer numerical ID (4 digits hexadecimal)");
|
||||
addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product numerical ID (4 digits hexadecimal)");
|
||||
|
||||
addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name");
|
||||
addvar(VAR_VALUE, "usb_set_altinterface", "Force redundant call to usb_set_altinterface() (value=bAlternateSetting; default=0)");
|
||||
}
|
||||
|
||||
/* From usbutils: workaround libusb API goofs: "byte" should never be sign extended;
|
||||
* using "char" is trouble. Likewise, sizes should never be negative.
|
||||
*/
|
||||
|
@ -70,6 +87,45 @@ static inline int matches(USBDeviceMatcher_t *matcher, USBDevice_t *device) {
|
|||
return matcher->match_function(device, matcher->privdata);
|
||||
}
|
||||
|
||||
/*! If needed, set the USB alternate interface.
|
||||
*
|
||||
* In NUT 2.7.2 and earlier, the following call was made unconditionally:
|
||||
* usb_set_altinterface(udev, 0);
|
||||
*
|
||||
* Although harmless on Linux and *BSD, this extra call prevents old Tripp Lite
|
||||
* devices from working on Mac OS X (presumably the OS is already setting
|
||||
* altinterface to 0).
|
||||
*/
|
||||
static int nut_usb_set_altinterface(usb_dev_handle *udev)
|
||||
{
|
||||
int altinterface = 0, ret = 0;
|
||||
char *alt_string, *endp = NULL;
|
||||
|
||||
if(testvar("usb_set_altinterface")) {
|
||||
alt_string = getval("usb_set_altinterface");
|
||||
if(alt_string) {
|
||||
altinterface = (int)strtol(alt_string, &endp, 10);
|
||||
if(endp && !(endp[0] == 0)) {
|
||||
upslogx(LOG_WARNING, "%s: '%s' is not a valid number", __func__, alt_string);
|
||||
}
|
||||
if(altinterface < 0 || altinterface > 255) {
|
||||
upslogx(LOG_WARNING, "%s: setting bAlternateInterface to %d will probably not work", __func__, altinterface);
|
||||
}
|
||||
}
|
||||
/* set default interface */
|
||||
upsdebugx(2, "%s: calling usb_set_altinterface(udev, %d)", __func__, altinterface);
|
||||
ret = usb_set_altinterface(udev, altinterface);
|
||||
if(ret != 0) {
|
||||
upslogx(LOG_WARNING, "%s: usb_set_altinterface(udev, %d) returned %d (%s)",
|
||||
__func__, altinterface, ret, usb_strerror() );
|
||||
}
|
||||
upslogx(LOG_NOTICE, "%s: usb_set_altinterface() should not be necessary - please email the nut-upsdev list with information about your UPS.", __func__);
|
||||
} else {
|
||||
upsdebugx(3, "%s: skipped usb_set_altinterface(udev, 0)", __func__);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define usb_control_msg typesafe_control_msg
|
||||
|
||||
/* On success, fill in the curDevice structure and return the report
|
||||
|
@ -220,8 +276,7 @@ static int libusb_open(usb_dev_handle **udevp, USBDevice_t *curDevice, USBDevice
|
|||
}
|
||||
#endif
|
||||
|
||||
/* set default interface */
|
||||
usb_set_altinterface(udev, 0);
|
||||
nut_usb_set_altinterface(udev);
|
||||
|
||||
if (!callback) {
|
||||
return 1;
|
||||
|
|
|
@ -26,13 +26,18 @@
|
|||
#include "liebert-hid.h"
|
||||
#include "usb-common.h"
|
||||
|
||||
#define LIEBERT_HID_VERSION "Liebert HID 0.3"
|
||||
#define LIEBERT_HID_VERSION "Phoenixtec/Liebert HID 0.3"
|
||||
/* FIXME: experimental flag to be put in upsdrv_info */
|
||||
|
||||
/* Phoenixtec Power Co., Ltd */
|
||||
#define LIEBERT_VENDORID 0x06da
|
||||
|
||||
/* USB IDs device table */
|
||||
/*! USB IDs device table.
|
||||
*
|
||||
* Note that this subdriver was named before the USB VendorID was determined to
|
||||
* actually belong to Phoenixtec. The belkin-hid.c file covers the other
|
||||
* Liebert units which share some of the same incorrect exponents as the
|
||||
* Belkin HID firmware. */
|
||||
static usb_device_id_t liebert_usb_device_table[] = {
|
||||
/* various models */
|
||||
{ USB_DEVICE(LIEBERT_VENDORID, 0xffff), NULL },
|
||||
|
|
|
@ -31,6 +31,9 @@
|
|||
/* for ser_open */
|
||||
int do_lock_port = 1;
|
||||
|
||||
/* for dstate->sock_connect, default to asynchronous */
|
||||
int do_synchronous = 0;
|
||||
|
||||
/* for detecting -a values that don't match anything */
|
||||
static int upsname_found = 0;
|
||||
|
||||
|
@ -250,7 +253,7 @@ void addvar(int vartype, const char *name, const char *desc)
|
|||
/* handle -x / ups.conf config details that are for this part of the code */
|
||||
static int main_arg(char *var, char *val)
|
||||
{
|
||||
/* flags for main: just 'nolock' for now */
|
||||
/* flags for main */
|
||||
|
||||
if (!strcmp(var, "nolock")) {
|
||||
do_lock_port = 0;
|
||||
|
@ -281,6 +284,16 @@ static int main_arg(char *var, char *val)
|
|||
return 1; /* handled */
|
||||
}
|
||||
|
||||
/* allow per-driver overrides of the global setting */
|
||||
if (!strcmp(var, "synchronous")) {
|
||||
if (!strcmp(val, "yes"))
|
||||
do_synchronous=1;
|
||||
else
|
||||
do_synchronous=0;
|
||||
|
||||
return 1; /* handled */
|
||||
}
|
||||
|
||||
/* only for upsdrvctl - ignored here */
|
||||
if (!strcmp(var, "sdorder"))
|
||||
return 1; /* handled */
|
||||
|
@ -309,6 +322,13 @@ static void do_global_args(const char *var, const char *val)
|
|||
user = xstrdup(val);
|
||||
}
|
||||
|
||||
if (!strcmp(var, "synchronous")) {
|
||||
if (!strcmp(val, "yes"))
|
||||
do_synchronous=1;
|
||||
else
|
||||
do_synchronous=0;
|
||||
}
|
||||
|
||||
|
||||
/* unrecognized */
|
||||
}
|
||||
|
@ -519,6 +539,7 @@ int main(int argc, char **argv)
|
|||
chroot_path = xstrdup(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
free(user);
|
||||
user = xstrdup(optarg);
|
||||
break;
|
||||
case 'V':
|
||||
|
@ -660,6 +681,10 @@ int main(int argc, char **argv)
|
|||
/* The poll_interval may have been changed from the default */
|
||||
dstate_setinfo("driver.parameter.pollinterval", "%d", poll_interval);
|
||||
|
||||
/* The synchronous option may have been changed from the default */
|
||||
dstate_setinfo("driver.parameter.synchronous", "%s",
|
||||
(do_synchronous==1)?"yes":"no");
|
||||
|
||||
/* remap the device.* info from ups.* for the transition period */
|
||||
if (dstate_getinfo("ups.mfr") != NULL)
|
||||
dstate_setinfo("device.mfr", "%s", dstate_getinfo("ups.mfr"));
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/* mge-hid.c - data to monitor Eaton / MGE HID (USB and serial) devices
|
||||
*
|
||||
* Copyright (C) 2003 - 2012
|
||||
* Arnaud Quette <arnaud.quette@free.fr>
|
||||
* Copyright (C)
|
||||
* 2003 - 2015 Arnaud Quette <arnaud.quette@free.fr>
|
||||
* 2015 Arnaud Quette <ArnaudQuette@Eaton.com>
|
||||
*
|
||||
* Sponsored by MGE UPS SYSTEMS <http://www.mgeups.com>
|
||||
*
|
||||
|
@ -36,7 +37,7 @@
|
|||
#include "usbhid-ups.h"
|
||||
#include "mge-hid.h"
|
||||
|
||||
#define MGE_HID_VERSION "MGE HID 1.33"
|
||||
#define MGE_HID_VERSION "MGE HID 1.38"
|
||||
|
||||
/* (prev. MGE Office Protection Systems, prev. MGE UPS SYSTEMS) */
|
||||
/* Eaton */
|
||||
|
@ -121,6 +122,223 @@ static int country_code = COUNTRY_UNKNOWN;
|
|||
|
||||
static char mge_scratch_buf[20];
|
||||
|
||||
/* ABM - Advanced Battery Monitoring
|
||||
***********************************
|
||||
* Synthesis table
|
||||
* HID data | Charger in ABM mode | Charger in Constant mode
|
||||
* UPS.BatterySystem.Charger.ABMEnable | 1 | 0
|
||||
* UPS.PowerSummary.PresentStatus.ACPresent | On utility | On battery | On utility | On battery
|
||||
* Charger ABM mode | Charging | Floating | Resting | Discharging | Disabled | Disabled
|
||||
* UPS.BatterySystem.Charger.Mode | 1 | 3 | 4 | 2 | 6 | 6
|
||||
* UPS.PowerSummary.PresentStatus.Charging | 1 | 1 | 1 | 0 | 1 | 0
|
||||
* UPS.PowerSummary.PresentStatus.Discharging | 0 | 0 | 0 | 1 | 0 | 1
|
||||
*
|
||||
* Notes (from David G. Miller) to understand ABM status:
|
||||
* When supporting ABM, when a UPS powers up or returns from battery, or
|
||||
* ends the ABM rest mode, it enters charge mode.
|
||||
* Some UPSs run a different charger reference voltage during charge mode
|
||||
* but all the newer models should not be doing that, but basically once
|
||||
* the battery voltage reaches the charger reference level (should be 2.3
|
||||
* volts/cell), the charger is considered in float mode. Some UPSs will not
|
||||
* annunciate float mode until the charger power starts falling from the maximum
|
||||
* level indicating the battery is truly at the float voltage or in float mode.
|
||||
* The %charge level is based on battery voltage and the charge mode timer
|
||||
* (should be 48 hours) and some UPSs add in a value that’s related to charger
|
||||
* power output. So you can have UPS that enters float mode with anywhere
|
||||
* from 80% or greater battery capacity.
|
||||
* float mode is not important from the software’s perspective, it’s there to
|
||||
* help determine if the charger is advancing correctly.
|
||||
* So in float mode, the charger is charging the battery, so by definition you
|
||||
* can assert the CHRG flag in NUT when in “float” mode or “charge” mode.
|
||||
* When in “rest” mode the charger is not delivering anything to the battery,
|
||||
* but it will when the ABM cycle(28 days) ends, or a battery discharge occurs
|
||||
* and utility returns. This is when the ABM status should be “resting”.
|
||||
* If a battery failure is detected that disables the charger, it should be
|
||||
* reporting “off” in the ABM charger status.
|
||||
* Of course when delivering load power from the battery, the ABM status is
|
||||
* discharging.
|
||||
*/
|
||||
|
||||
#define ABM_UNKNOWN -1
|
||||
#define ABM_DISABLED 0
|
||||
#define ABM_ENABLED 1
|
||||
|
||||
/* Internal flag to process battery status (CHRG/DISCHRG) and ABM */
|
||||
static int advanced_battery_monitoring = ABM_UNKNOWN;
|
||||
|
||||
/* Used to store internally if ABM is enabled or not */
|
||||
static const char *eaton_abm_enabled_fun(double value)
|
||||
{
|
||||
advanced_battery_monitoring = value;
|
||||
|
||||
upsdebugx(2, "ABM is %s", (advanced_battery_monitoring==1)?"enabled":"disabled");
|
||||
|
||||
/* Return NULL, not to get the value published! */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static info_lkp_t eaton_abm_enabled_info[] = {
|
||||
{ 0, "dummy", eaton_abm_enabled_fun },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
|
||||
/* Note 1: This point will need more clarification! */
|
||||
# if 0
|
||||
/* Used to store internally if ABM is enabled or not (for legacy units) */
|
||||
static const char *eaton_abm_enabled_legacy_fun(double value)
|
||||
{
|
||||
advanced_battery_monitoring = value;
|
||||
|
||||
upsdebugx(2, "ABM is %s (legacy data)", (advanced_battery_monitoring==1)?"enabled":"disabled");
|
||||
|
||||
/* Return NULL, not to get the value published! */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static info_lkp_t eaton_abm_enabled_legacy_info[] = {
|
||||
{ 0, "dummy", eaton_abm_enabled_legacy_fun },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
#endif /* if 0 */
|
||||
|
||||
/* Used to process ABM flags, for battery.charger.status */
|
||||
static const char *eaton_abm_status_fun(double value)
|
||||
{
|
||||
/* Don't process if ABM is disabled */
|
||||
if (advanced_battery_monitoring == ABM_DISABLED) {
|
||||
/* Clear any previously published data, in case
|
||||
* the user has switched off ABM */
|
||||
dstate_delinfo("battery.charger.status");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
upsdebugx(2, "ABM numeric status: %i", (int)value);
|
||||
|
||||
switch ((long)value)
|
||||
{
|
||||
case 1:
|
||||
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "charging");
|
||||
break;
|
||||
case 2:
|
||||
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "discharging");
|
||||
break;
|
||||
case 3:
|
||||
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "floating");
|
||||
break;
|
||||
case 4:
|
||||
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "resting");
|
||||
break;
|
||||
case 6: /* ABM Charger Disabled */
|
||||
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "off");
|
||||
break;
|
||||
case 5: /* Undefined - ABM is not activated */
|
||||
default:
|
||||
/* Return NULL, not to get the value published! */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
upsdebugx(2, "ABM string status: %s", mge_scratch_buf);
|
||||
|
||||
return mge_scratch_buf;
|
||||
}
|
||||
|
||||
static info_lkp_t eaton_abm_status_info[] = {
|
||||
{ 1, "dummy", eaton_abm_status_fun },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
|
||||
/* Used to process ABM flags, for ups.status (CHRG/DISCHRG/RB) */
|
||||
static const char *eaton_abm_chrg_dischrg_fun(double value)
|
||||
{
|
||||
/* Don't process if ABM is disabled */
|
||||
if (advanced_battery_monitoring == ABM_DISABLED)
|
||||
return NULL;
|
||||
|
||||
switch ((long)value)
|
||||
{
|
||||
case 1: /* charging status */
|
||||
case 3: /* floating status */
|
||||
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "chrg");
|
||||
break;
|
||||
case 2:
|
||||
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "dischrg");
|
||||
break;
|
||||
case 6: /* ABM Charger Disabled */
|
||||
case 4: /* resting, nothing to publish! (?) */
|
||||
case 5: /* Undefined - ABM is not activated */
|
||||
default:
|
||||
/* Return NULL, not to get the value published! */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
upsdebugx(2, "ABM CHRG/DISCHRG legacy string status (ups.status): %s", mge_scratch_buf);
|
||||
|
||||
return mge_scratch_buf;
|
||||
}
|
||||
|
||||
static info_lkp_t eaton_abm_chrg_dischrg_info[] = {
|
||||
{ 1, "dummy", eaton_abm_chrg_dischrg_fun },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
|
||||
/* ABM also implies that standard CHRG/DISCHRG are processed according
|
||||
* to weither ABM is enabled or not...
|
||||
* If ABM is disabled, we publish these legacy status
|
||||
* Otherwise, we don't publish on ups.status, but only battery.charger.status */
|
||||
/* FIXME: we may prefer to publish the CHRG/DISCHRG status
|
||||
* on battery.charger.status?! */
|
||||
static const char *eaton_abm_check_dischrg_fun(double value)
|
||||
{
|
||||
if (advanced_battery_monitoring == ABM_DISABLED)
|
||||
{
|
||||
if (value == 1) {
|
||||
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "dischrg");
|
||||
}
|
||||
else {
|
||||
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "!dischrg");
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Else, ABM is enabled, we should return NULL,
|
||||
* not to get the value published!
|
||||
* However, clear flags that would persist in case of prior
|
||||
* publication in ABM-disabled mode */
|
||||
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "!dischrg");
|
||||
}
|
||||
return mge_scratch_buf;
|
||||
}
|
||||
|
||||
static info_lkp_t eaton_discharging_info[] = {
|
||||
{ 1, "dummy", eaton_abm_check_dischrg_fun },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
|
||||
static const char *eaton_abm_check_chrg_fun(double value)
|
||||
{
|
||||
if (advanced_battery_monitoring == ABM_DISABLED)
|
||||
{
|
||||
if (value == 1) {
|
||||
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "chrg");
|
||||
}
|
||||
else {
|
||||
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "!chrg");
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Else, ABM is enabled, we should return NULL,
|
||||
* not to get the value published!
|
||||
* However, clear flags that would persist in case of prior
|
||||
* publication in ABM-disabled mode */
|
||||
snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "!chrg");
|
||||
}
|
||||
return mge_scratch_buf;
|
||||
}
|
||||
|
||||
static info_lkp_t eaton_charging_info[] = {
|
||||
{ 1, "dummy", eaton_abm_check_chrg_fun },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
|
||||
/* The HID path 'UPS.PowerSummary.Time' reports Unix time (ie the number of
|
||||
* seconds since 1970-01-01 00:00:00. This has to be split between ups.date and
|
||||
* ups.time */
|
||||
|
@ -880,6 +1098,21 @@ static hid_info_t mge_hid2nut[] =
|
|||
{ "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 },
|
||||
{ "battery.energysave", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].EnergySaving", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info },
|
||||
{ "battery.energysave.load", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].ConfigPercentLoad", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL },
|
||||
/* Current implementation */
|
||||
{ "battery.energysave.delay", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].EnergySaving.ShutdownTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL },
|
||||
/* Newer implementation */
|
||||
{ "battery.energysave.delay", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].ShutdownTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL },
|
||||
{ "battery.energysave.realpower", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].ConfigActivePower", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL },
|
||||
/* ABM (Advanced Battery Monitoring) processing
|
||||
* Must be processed before the BOOL status */
|
||||
/* Not published, just to store in internal var. advanced_battery_monitoring */
|
||||
{ "battery.charger.status", 0, 0, "UPS.BatterySystem.Charger.ABMEnable", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_enabled_info },
|
||||
/* Same as the one above, but for legacy units */
|
||||
/* Refer to Note 1 (This point will need more clarification!)
|
||||
{ "battery.charger.status", 0, 0, "UPS.BatterySystem.Charger.PresentStatus.Used", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_enabled_legacy_info }, */
|
||||
/* This data is the actual ABM status information */
|
||||
{ "battery.charger.status", 0, 0, "UPS.BatterySystem.Charger.Mode", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_status_info },
|
||||
|
||||
/* UPS page */
|
||||
{ "ups.efficiency", 0, 0, "UPS.PowerConverter.Output.Efficiency", NULL, "%.0f", 0, NULL },
|
||||
|
@ -922,8 +1155,11 @@ static hid_info_t mge_hid2nut[] =
|
|||
{ "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info },
|
||||
{ "BOOL", 0, 0, "UPS.PowerConverter.Input.[3].PresentStatus.Used", NULL, NULL, 0, mge_onbatt_info },
|
||||
{ "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.Used", NULL, NULL, 0, online_info },
|
||||
{ "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info },
|
||||
{ "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info },
|
||||
/* These 2 ones are used when ABM is disabled */
|
||||
{ "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, eaton_discharging_info },
|
||||
{ "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, eaton_charging_info },
|
||||
/* And this one when ABM is enabled (same as battery.charger.status) */
|
||||
{ "BOOL", 0, 0, "UPS.BatterySystem.Charger.Mode", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_chrg_dischrg_info },
|
||||
/* FIXME: on Dell, the above requires an "AND" with "UPS.BatterySystem.Charger.Mode = 1" */
|
||||
{ "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info },
|
||||
/* Output overload, Level 1 (FIXME: add the level?) */
|
||||
|
@ -940,11 +1176,6 @@ static hid_info_t mge_hid2nut[] =
|
|||
{ "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.VoltageOutOfRange", NULL, NULL, 0, vrange_info },
|
||||
{ "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.FrequencyOutOfRange", NULL, NULL, 0, frange_info },
|
||||
{ "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Good", NULL, NULL, 0, off_info },
|
||||
#if 0
|
||||
/* TODO: UPS.BatterySystem.Charger.PresentStatus.Used is related to ABM */
|
||||
{ "BOOL", 0, 0, "UPS.BatterySystem.Charger.PresentStatus.Used", NULL, NULL, 0, off_info },
|
||||
/* FIXME: on Dell, the above requires an "AND" with "UPS.BatterySystem.Charger.Mode = 4 (ABM Resting)" */
|
||||
#endif
|
||||
{ "BOOL", 0, 0, "UPS.PowerConverter.Input.[2].PresentStatus.Used", NULL, NULL, 0, bypass_auto_info }, /* Automatic bypass */
|
||||
{ "BOOL", 0, 0, "UPS.PowerConverter.Input.[4].PresentStatus.Used", NULL, NULL, 0, bypass_manual_info }, /* Manual bypass */
|
||||
{ "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FanFailure", NULL, NULL, 0, fanfail_info },
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright (C)
|
||||
* 2004 Thanos Chatziathanassiou <tchatzi@arx.net>
|
||||
* 2012 Manuel Bouyer <bouyer@NetBSD.org>
|
||||
* 2015 Arnaud Quette <arnaud.quette@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -24,7 +25,7 @@
|
|||
|
||||
#include "netvision-mib.h"
|
||||
|
||||
#define NETVISION_MIB_VERSION "0.3"
|
||||
#define NETVISION_MIB_VERSION "0.4"
|
||||
|
||||
#define NETVISION_SYSOID ".1.3.6.1.4.1.4555.1.1.1"
|
||||
|
||||
|
@ -46,6 +47,13 @@ static info_lkp_t netvision_batt_info[] = {
|
|||
{ 0, "NULL" }
|
||||
};
|
||||
|
||||
/* Battery status: upsAlarmOnBattery */
|
||||
static info_lkp_t netvision_onbatt_info[] = {
|
||||
{ 0, "OL" }, /* Online */
|
||||
{ 1, "OB" }, /* On battery */
|
||||
{ 0, "NULL" }
|
||||
};
|
||||
|
||||
#define NETVISION_OID_SECONDSONBATTERY ".1.3.6.1.4.1.4555.1.1.1.1.2.2.0"
|
||||
#define NETVISION_OID_BATT_RUNTIME_REMAINING ".1.3.6.1.4.1.4555.1.1.1.1.2.3.0"
|
||||
#define NETVISION_OID_BATT_CHARGE ".1.3.6.1.4.1.4555.1.1.1.1.2.4.0"
|
||||
|
@ -119,6 +127,9 @@ static snmp_info_t netvision_mib[] = {
|
|||
SU_FLAG_OK | SU_STATUS_BATT, &netvision_batt_info[0] },
|
||||
{ "ups.status", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_OUTPUT_SOURCE, "",
|
||||
SU_FLAG_OK | SU_STATUS_PWR, &netvision_output_info[0] },
|
||||
/* upsAlarmOnBattery */
|
||||
{ "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4555.1.1.1.1.6.3.2.0", "",
|
||||
SU_FLAG_OK | SU_STATUS_PWR, &netvision_onbatt_info[0] },
|
||||
|
||||
/* ups load */
|
||||
{ "ups.load", 0, 1, NETVISION_OID_OUT_LOAD_PCT_P1, 0, SU_INPUT_1, NULL },
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#define DRIVER_VERSION "0.06"
|
||||
#define DRIVER_VERSION "0.17"
|
||||
|
||||
#include "main.h"
|
||||
|
||||
|
@ -62,6 +62,7 @@
|
|||
|
||||
/* == Subdrivers == */
|
||||
/* Include all known subdrivers */
|
||||
#include "nutdrv_qx_bestups.h"
|
||||
#include "nutdrv_qx_mecer.h"
|
||||
#include "nutdrv_qx_megatec.h"
|
||||
#include "nutdrv_qx_megatec-old.h"
|
||||
|
@ -69,14 +70,17 @@
|
|||
#include "nutdrv_qx_q1.h"
|
||||
#include "nutdrv_qx_voltronic.h"
|
||||
#include "nutdrv_qx_voltronic-qs.h"
|
||||
#include "nutdrv_qx_voltronic-qs-hex.h"
|
||||
#include "nutdrv_qx_zinto.h"
|
||||
|
||||
/* Master list of avaiable subdrivers */
|
||||
/* Master list of available subdrivers */
|
||||
static subdriver_t *subdriver_list[] = {
|
||||
&voltronic_subdriver,
|
||||
&voltronic_qs_subdriver,
|
||||
&voltronic_qs_hex_subdriver,
|
||||
&mustek_subdriver,
|
||||
&megatec_old_subdriver,
|
||||
&bestups_subdriver,
|
||||
&mecer_subdriver,
|
||||
&megatec_subdriver,
|
||||
&zinto_subdriver,
|
||||
|
@ -439,9 +443,10 @@ static int cypress_command(const char *cmd, char *buf, size_t buflen)
|
|||
ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x200, 0, &tmp[i], 8, 5000);
|
||||
|
||||
if (ret <= 0) {
|
||||
upsdebugx(3, "send: %s", ret ? usb_strerror() : "timeout");
|
||||
upsdebugx(3, "send: %s (%d)", ret ? usb_strerror() : "timeout", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp);
|
||||
|
@ -449,7 +454,7 @@ static int cypress_command(const char *cmd, char *buf, size_t buflen)
|
|||
/* Read reply */
|
||||
memset(buf, 0, buflen);
|
||||
|
||||
for (i = 0; (i <= buflen-8) && (strchr(buf, '\r') == NULL); i += ret) {
|
||||
for (i = 0; (i <= buflen-8) && (memchr(buf, '\r', buflen) == NULL); i += ret) {
|
||||
|
||||
/* Read data in 8-byte chunks */
|
||||
/* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */
|
||||
|
@ -457,9 +462,13 @@ static int cypress_command(const char *cmd, char *buf, size_t buflen)
|
|||
|
||||
/* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */
|
||||
if (ret <= 0) {
|
||||
upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout");
|
||||
upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "read [% 3d]", (int)i);
|
||||
upsdebug_hex(5, tmp, &buf[i], ret);
|
||||
|
||||
}
|
||||
|
||||
upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
|
||||
|
@ -490,11 +499,12 @@ static int phoenix_command(const char *cmd, char *buf, size_t buflen)
|
|||
}
|
||||
|
||||
if (ret < 0) {
|
||||
upsdebugx(3, "flush: %s", usb_strerror());
|
||||
upsdebugx(3, "flush: %s (%d)", usb_strerror(), ret);
|
||||
break;
|
||||
}
|
||||
|
||||
upsdebug_hex(4, "dump", tmp, ret);
|
||||
|
||||
}
|
||||
|
||||
/* Send command */
|
||||
|
@ -508,9 +518,10 @@ static int phoenix_command(const char *cmd, char *buf, size_t buflen)
|
|||
ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x200, 0, &tmp[i], 8, 1000);
|
||||
|
||||
if (ret <= 0) {
|
||||
upsdebugx(3, "send: %s", ret ? usb_strerror() : "timeout");
|
||||
upsdebugx(3, "send: %s (%d)", ret ? usb_strerror() : "timeout", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp);
|
||||
|
@ -518,7 +529,7 @@ static int phoenix_command(const char *cmd, char *buf, size_t buflen)
|
|||
/* Read reply */
|
||||
memset(buf, 0, buflen);
|
||||
|
||||
for (i = 0; (i <= buflen-8) && (strchr(buf, '\r') == NULL); i += ret) {
|
||||
for (i = 0; (i <= buflen-8) && (memchr(buf, '\r', buflen) == NULL); i += ret) {
|
||||
|
||||
/* Read data in 8-byte chunks */
|
||||
/* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */
|
||||
|
@ -526,9 +537,13 @@ static int phoenix_command(const char *cmd, char *buf, size_t buflen)
|
|||
|
||||
/* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */
|
||||
if (ret <= 0) {
|
||||
upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout");
|
||||
upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "read [% 3d]", (int)i);
|
||||
upsdebug_hex(5, tmp, &buf[i], ret);
|
||||
|
||||
}
|
||||
|
||||
upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
|
||||
|
@ -539,8 +554,8 @@ static int phoenix_command(const char *cmd, char *buf, size_t buflen)
|
|||
static int ippon_command(const char *cmd, char *buf, size_t buflen)
|
||||
{
|
||||
char tmp[64];
|
||||
int ret, len;
|
||||
size_t i;
|
||||
int ret;
|
||||
size_t i, len;
|
||||
|
||||
/* Send command */
|
||||
snprintf(tmp, sizeof(tmp), "%s", cmd);
|
||||
|
@ -551,9 +566,10 @@ static int ippon_command(const char *cmd, char *buf, size_t buflen)
|
|||
ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x2, 0, &tmp[i], 8, 1000);
|
||||
|
||||
if (ret <= 0) {
|
||||
upsdebugx(3, "send: %s", (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out");
|
||||
upsdebugx(3, "send: %s (%d)", (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp);
|
||||
|
@ -563,23 +579,36 @@ static int ippon_command(const char *cmd, char *buf, size_t buflen)
|
|||
|
||||
/* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */
|
||||
if (ret <= 0) {
|
||||
upsdebugx(3, "read: %s", (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out");
|
||||
upsdebugx(3, "read: %s (%d)", (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* As Ippon will always return 64 bytes in response, we have to
|
||||
* calculate and return length of actual response data here.
|
||||
* Empty response will look like 0x00 0x0D, otherwise it will be
|
||||
* data string terminated by 0x0D.
|
||||
*/
|
||||
len = (int)strcspn(tmp, "\r");
|
||||
upsdebugx(3, "read: %.*s", len, tmp);
|
||||
if (len > 0) {
|
||||
len ++;
|
||||
/* As Ippon will always return 64 bytes in response, we have to calculate and return length of actual response data here.
|
||||
* Empty response will look like 0x00 0x0D, otherwise it will be data string terminated by 0x0D. */
|
||||
|
||||
for (i = 0, len = 0; i < (size_t)ret; i++) {
|
||||
|
||||
if (tmp[i] != '\r')
|
||||
continue;
|
||||
|
||||
len = ++i;
|
||||
break;
|
||||
|
||||
}
|
||||
snprintf(buf, buflen, "%.*s", len, tmp);
|
||||
return len;
|
||||
|
||||
/* Just in case there wasn't any '\r', fallback to string length, if any */
|
||||
if (!len)
|
||||
len = strlen(tmp);
|
||||
|
||||
upsdebug_hex(5, "read", tmp, (int)len);
|
||||
upsdebugx(3, "read: %.*s", (int)strcspn(tmp, "\r"), tmp);
|
||||
|
||||
len = len < buflen ? len : buflen - 1;
|
||||
|
||||
memset(buf, 0, buflen);
|
||||
memcpy(buf, tmp, len);
|
||||
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
/* Krauler communication subdriver */
|
||||
|
@ -623,13 +652,12 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen)
|
|||
if (langid_fix != -1) {
|
||||
/* Apply langid_fix value */
|
||||
ret = usb_get_string(udev, command[i].index, langid_fix, buf, buflen);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ret = usb_get_string_simple(udev, command[i].index, buf, buflen);
|
||||
}
|
||||
|
||||
if (ret <= 0) {
|
||||
upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout");
|
||||
upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -673,7 +701,9 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen)
|
|||
/* Replace the first byte of what we received with the correct one */
|
||||
buf[0] = command[i].prefix;
|
||||
|
||||
upsdebug_hex(5, "read", buf, ret);
|
||||
upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@ -687,6 +717,219 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen)
|
|||
return snprintf(buf, buflen, "%s", cmd);
|
||||
}
|
||||
|
||||
/* Fabula communication subdriver */
|
||||
static int fabula_command(const char *cmd, char *buf, size_t buflen)
|
||||
{
|
||||
const struct {
|
||||
const char *str; /* Megatec command */
|
||||
const int index; /* Fabula string index for this command */
|
||||
} commands[] = {
|
||||
{ "Q1\r", 0x03, }, /* Status */
|
||||
{ "F\r", 0x0d, }, /* Ratings */
|
||||
{ "I\r", 0x0c, }, /* Vendor infos */
|
||||
{ "Q\r", 0x07, }, /* Beeper toggle */
|
||||
{ "C\r", 0x0a, }, /* Cancel shutdown/Load on [0x(0..F)A]*/
|
||||
{ NULL }
|
||||
};
|
||||
int i, ret, index = 0;
|
||||
|
||||
upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd);
|
||||
|
||||
for (i = 0; commands[i].str; i++) {
|
||||
|
||||
if (strcmp(cmd, commands[i].str))
|
||||
continue;
|
||||
|
||||
index = commands[i].index;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (!index) {
|
||||
|
||||
int val2 = -1;
|
||||
double val1 = -1;
|
||||
|
||||
/* Shutdowns */
|
||||
if (
|
||||
sscanf(cmd, "S%lfR%d\r", &val1, &val2) == 2 ||
|
||||
sscanf(cmd, "S%lf\r", &val1) == 1
|
||||
) {
|
||||
|
||||
double delay;
|
||||
|
||||
/* 0x(1+)0 -> shutdown.stayoff (SnR0000)
|
||||
* 0x(1+)8 -> shutdown.return (Sn[Rm], m != 0) [delay before restart is always 10 seconds]
|
||||
* +0x10 (16dec) = next megatec delay (min .5 = hex 0x1*; max 10 = hex 0xF*) -> n < 1 ? -> n += .1; n >= 1 ? -> n += 1 */
|
||||
|
||||
/* delay: [.5..10] (-> seconds: [30..600]) */
|
||||
delay = val1 < .5 ? .5 : val1 > 10 ? 10 : val1;
|
||||
|
||||
if (delay < 1)
|
||||
index = 16 + round((delay - .5) * 10) * 16;
|
||||
else
|
||||
index = 96 + (delay - 1) * 16;
|
||||
|
||||
/* shutdown.return (Sn[Rm], m != 0) */
|
||||
if (val2)
|
||||
index += 8;
|
||||
|
||||
/* Unknown commands */
|
||||
} else {
|
||||
|
||||
/* Echo the unknown command back */
|
||||
upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd);
|
||||
return snprintf(buf, buflen, "%s", cmd);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
upsdebugx(4, "command index: 0x%02x", index);
|
||||
|
||||
/* Send command/Read reply */
|
||||
ret = usb_get_string_simple(udev, index, buf, buflen);
|
||||
|
||||
if (ret <= 0) {
|
||||
upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
upsdebug_hex(5, "read", buf, ret);
|
||||
upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
|
||||
|
||||
/* The UPS always replies "UPS No Ack" when a supported command is issued (either if it fails or if it succeeds).. */
|
||||
if (!strcasecmp(buf, "UPS No Ack"))
|
||||
/* ..because of that, always return 0 (as if it was a timeout): queries will see it as a failure, instant commands ('megatec' protocol) as a success */
|
||||
return 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Fuji communication subdriver */
|
||||
static int fuji_command(const char *cmd, char *buf, size_t buflen)
|
||||
{
|
||||
unsigned char tmp[8];
|
||||
char command[SMALLBUF] = "",
|
||||
read[SMALLBUF] = "";
|
||||
int ret, answer_len, val2;
|
||||
double val1;
|
||||
size_t i;
|
||||
const struct {
|
||||
const char *command; /* Megatec command */
|
||||
const int answer_len; /* Expected length of the answer to the ongoing query */
|
||||
} query[] = {
|
||||
{ "Q1", 47 },
|
||||
{ "F", 22 },
|
||||
{ "I", 39 },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
/*
|
||||
* Queries (b1..b8) sent (as a 8-bytes interrupt) to the UPS adopt the following scheme:
|
||||
*
|
||||
* b1: 0x80
|
||||
* b2: 0x06
|
||||
* b3: <LEN>
|
||||
* b4: 0x03
|
||||
* b5..bn: <COMMAND>
|
||||
* bn+1..b7: [<PADDING>]
|
||||
* b8: <ANSWER_LEN>
|
||||
*
|
||||
* Where:
|
||||
* <LEN> Length (in Hex) of the command (without the trailing CR) + 1
|
||||
* <COMMAND> Command/query (without the trailing CR)
|
||||
* [<PADDING>] 0x00 padding to the 7th byte
|
||||
* <ANSWER_LEN> Expected length (in Hex) of the answer to the ongoing query (0 when no reply is expected, i.e. commands)
|
||||
*
|
||||
* Replies to queries (commands are followed by action without any reply) are sent from the UPS (in 8-byte chunks) with 0x00 padding after the trailing CR to full 8 bytes.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Send command */
|
||||
|
||||
/* Remove the CR */
|
||||
snprintf(command, sizeof(command), "%.*s", (int)strcspn(cmd, "\r"), cmd);
|
||||
|
||||
/* Length of the command that will be sent to the UPS can be at most: 8 - 5 (0x80, 0x06, <LEN>, 0x03, <ANSWER_LEN>) = 3.
|
||||
* As a consequence also 'SnRm' commands (shutdown.{return,stayoff} and load.off) are not supported.
|
||||
* So, map all the 'SnRm' shutdown.returns (m != 0) as the corresponding 'Sn' commands, meanwhile ignoring ups.delay.start and making the UPS turn on the load as soon as power is back. */
|
||||
if (sscanf(cmd, "S%lfR%d\r", &val1, &val2) == 2 && val2) {
|
||||
upsdebugx(4, "%s: trimming '%s' to '%.*s'", __func__, command, 3, command);
|
||||
command[3] = 0;
|
||||
}
|
||||
/* Too long command */
|
||||
if (strlen(command) > 3) {
|
||||
/* Be 'megatec-y': echo the unsupported command back */
|
||||
upsdebugx(3, "%s: unsupported command %s", __func__, command);
|
||||
return snprintf(buf, buflen, "%s", cmd);
|
||||
}
|
||||
|
||||
/* Expected length of the answer to the ongoing query (0 when no reply is expected, i.e. commands) */
|
||||
answer_len = 0;
|
||||
for (i = 0; query[i].command; i++) {
|
||||
|
||||
if (strcmp(command, query[i].command))
|
||||
continue;
|
||||
|
||||
answer_len = query[i].answer_len;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
memset(tmp, 0, sizeof(tmp));
|
||||
|
||||
/* 0x80 */
|
||||
tmp[0] = 0x80;
|
||||
/* 0x06 */
|
||||
tmp[1] = 0x06;
|
||||
/* <LEN> */
|
||||
tmp[2] = strlen(command) + 1;
|
||||
/* 0x03 */
|
||||
tmp[3] = 0x03;
|
||||
/* <COMMAND> */
|
||||
memcpy(&tmp[4], command, strlen(command));
|
||||
/* <ANSWER_LEN> */
|
||||
tmp[7] = answer_len;
|
||||
|
||||
upsdebug_hex(4, "command", (char *)tmp, 8);
|
||||
|
||||
/* Write data */
|
||||
ret = usb_interrupt_write(udev, USB_ENDPOINT_OUT | 2, (char *)tmp, 8, USB_TIMEOUT);
|
||||
|
||||
if (ret <= 0) {
|
||||
upsdebugx(3, "send: %s (%d)", ret ? usb_strerror() : "timeout", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
upsdebugx(3, "send: %s", command);
|
||||
|
||||
/* Read reply */
|
||||
|
||||
memset(buf, 0, buflen);
|
||||
|
||||
for (i = 0; (i <= buflen - 8) && (memchr(buf, '\r', buflen) == NULL); i += ret) {
|
||||
|
||||
/* Read data in 8-byte chunks */
|
||||
ret = usb_interrupt_read(udev, USB_ENDPOINT_IN | 1, &buf[i], 8, 1000);
|
||||
|
||||
/* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */
|
||||
if (ret <= 0) {
|
||||
upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
snprintf(read, sizeof(read), "read [%3d]", (int)i);
|
||||
upsdebug_hex(5, read, &buf[i], ret);
|
||||
|
||||
}
|
||||
|
||||
upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
|
||||
|
||||
/* As Fuji units return the reply in 8-byte chunks always padded to the 8th byte with 0x00, we need to calculate and return the length of the actual response here. */
|
||||
return (int)strlen(buf);
|
||||
}
|
||||
|
||||
static void *cypress_subdriver(USBDevice_t *device)
|
||||
{
|
||||
subdriver_command = &cypress_command;
|
||||
|
@ -711,31 +954,87 @@ static void *phoenix_subdriver(USBDevice_t *device)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* USB VendorID/ProductID match - note: rightmost comment is used for naming rules by tools/nut-usbinfo.pl */
|
||||
static usb_device_id_t qx_usb_id[] = {
|
||||
{ USB_DEVICE(0x05b8, 0x0000), &cypress_subdriver }, /* Agiler UPS */
|
||||
{ USB_DEVICE(0x0001, 0x0000), &krauler_subdriver }, /* Krauler UP-M500VA */
|
||||
{ USB_DEVICE(0xffff, 0x0000), &krauler_subdriver }, /* Ablerex 625L USB */
|
||||
{ USB_DEVICE(0x0665, 0x5161), &cypress_subdriver }, /* Belkin F6C1200-UNV/Voltronic Power UPSes */
|
||||
{ USB_DEVICE(0x06da, 0x0002), &cypress_subdriver }, /* Online Yunto YQ450 */
|
||||
{ USB_DEVICE(0x06da, 0x0003), &ippon_subdriver }, /* Mustek Powermust */
|
||||
{ USB_DEVICE(0x06da, 0x0004), &cypress_subdriver }, /* Phoenixtec Innova 3/1 T */
|
||||
{ USB_DEVICE(0x06da, 0x0005), &cypress_subdriver }, /* Phoenixtec Innova RT */
|
||||
{ USB_DEVICE(0x06da, 0x0201), &cypress_subdriver }, /* Phoenixtec Innova T */
|
||||
{ USB_DEVICE(0x06da, 0x0601), &phoenix_subdriver }, /* Online Zinto A */
|
||||
{ USB_DEVICE(0x0f03, 0x0001), &cypress_subdriver }, /* Unitek Alpha 1200Sx */
|
||||
{ USB_DEVICE(0x14f0, 0x00c9), &phoenix_subdriver }, /* GE EP series */
|
||||
static void *fabula_subdriver(USBDevice_t *device)
|
||||
{
|
||||
subdriver_command = &fabula_command;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *fuji_subdriver(USBDevice_t *device)
|
||||
{
|
||||
subdriver_command = &fuji_command;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* USB device match structure */
|
||||
typedef struct {
|
||||
const int vendorID; /* USB device's VendorID */
|
||||
const int productID; /* USB device's ProductID */
|
||||
const char *vendor; /* USB device's iManufacturer string */
|
||||
const char *product; /* USB device's iProduct string */
|
||||
void *(*fun)(USBDevice_t *); /* Handler for specific processing */
|
||||
} qx_usb_device_id_t;
|
||||
|
||||
/* USB VendorID/ProductID/iManufacturer/iProduct match - note: rightmost comment is used for naming rules by tools/nut-usbinfo.pl */
|
||||
static qx_usb_device_id_t qx_usb_id[] = {
|
||||
{ USB_DEVICE(0x05b8, 0x0000), NULL, NULL, &cypress_subdriver }, /* Agiler UPS */
|
||||
{ USB_DEVICE(0xffff, 0x0000), NULL, NULL, &krauler_subdriver }, /* Ablerex 625L USB */
|
||||
{ USB_DEVICE(0x0665, 0x5161), NULL, NULL, &cypress_subdriver }, /* Belkin F6C1200-UNV/Voltronic Power UPSes */
|
||||
{ USB_DEVICE(0x06da, 0x0002), NULL, NULL, &cypress_subdriver }, /* Online Yunto YQ450 */
|
||||
{ USB_DEVICE(0x06da, 0x0003), NULL, NULL, &ippon_subdriver }, /* Mustek Powermust */
|
||||
{ USB_DEVICE(0x06da, 0x0004), NULL, NULL, &cypress_subdriver }, /* Phoenixtec Innova 3/1 T */
|
||||
{ USB_DEVICE(0x06da, 0x0005), NULL, NULL, &cypress_subdriver }, /* Phoenixtec Innova RT */
|
||||
{ USB_DEVICE(0x06da, 0x0201), NULL, NULL, &cypress_subdriver }, /* Phoenixtec Innova T */
|
||||
{ USB_DEVICE(0x06da, 0x0601), NULL, NULL, &phoenix_subdriver }, /* Online Zinto A */
|
||||
{ USB_DEVICE(0x0f03, 0x0001), NULL, NULL, &cypress_subdriver }, /* Unitek Alpha 1200Sx */
|
||||
{ USB_DEVICE(0x14f0, 0x00c9), NULL, NULL, &phoenix_subdriver }, /* GE EP series */
|
||||
{ USB_DEVICE(0x0001, 0x0000), "MEC", "MEC0003", &fabula_subdriver }, /* Fideltronik/MEC LUPUS 500 USB */
|
||||
{ USB_DEVICE(0x0001, 0x0000), "ATCL FOR UPS", "ATCL FOR UPS", &fuji_subdriver }, /* Fuji UPSes */
|
||||
{ USB_DEVICE(0x0001, 0x0000), NULL, NULL, &krauler_subdriver }, /* Krauler UP-M500VA */
|
||||
/* End of list */
|
||||
{ -1, -1, NULL }
|
||||
{ -1, -1, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static int qx_is_usb_device_supported(qx_usb_device_id_t *usb_device_id_list, USBDevice_t *device)
|
||||
{
|
||||
int retval = NOT_SUPPORTED;
|
||||
qx_usb_device_id_t *usbdev;
|
||||
|
||||
for (usbdev = usb_device_id_list; usbdev->vendorID != -1; usbdev++) {
|
||||
|
||||
if (usbdev->vendorID != device->VendorID)
|
||||
continue;
|
||||
|
||||
/* Flag as possibly supported if we see a known vendor */
|
||||
retval = POSSIBLY_SUPPORTED;
|
||||
|
||||
if (usbdev->productID != device->ProductID)
|
||||
continue;
|
||||
|
||||
if (usbdev->vendor && (!device->Vendor || strcasecmp(usbdev->vendor, device->Vendor)))
|
||||
continue;
|
||||
|
||||
if (usbdev->product && (!device->Product || strcasecmp(usbdev->product, device->Product)))
|
||||
continue;
|
||||
|
||||
/* Call the specific handler, if it exists */
|
||||
if (usbdev->fun != NULL)
|
||||
(*usbdev->fun)(device);
|
||||
|
||||
return SUPPORTED;
|
||||
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int device_match_func(USBDevice_t *hd, void *privdata)
|
||||
{
|
||||
if (subdriver_command) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (is_usb_device_supported(qx_usb_id, hd))
|
||||
switch (qx_is_usb_device_supported(qx_usb_id, hd))
|
||||
{
|
||||
case SUPPORTED:
|
||||
return 1;
|
||||
|
@ -837,7 +1136,7 @@ int instcmd(const char *cmdname, const char *extradata)
|
|||
return STAT_INSTCMD_INVALID;
|
||||
}
|
||||
|
||||
/* If extradata is empty, use the default value from the blazer to NUT table */
|
||||
/* If extradata is empty, use the default value from the QX to NUT table */
|
||||
extradata = extradata ? extradata : item->dfl;
|
||||
snprintf(value, sizeof(value), "%s", extradata ? extradata : "");
|
||||
|
||||
|
@ -1264,14 +1563,8 @@ void upsdrv_makevartable(void)
|
|||
|
||||
#ifdef QX_USB
|
||||
addvar(VAR_VALUE, "subdriver", "Serial-over-USB subdriver selection");
|
||||
addvar(VAR_VALUE, "vendorid", "Regular expression to match UPS Manufacturer numerical ID (4 digits hexadecimal)");
|
||||
addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product numerical ID (4 digits hexadecimal)");
|
||||
|
||||
addvar(VAR_VALUE, "vendor", "Regular expression to match UPS Manufacturer string");
|
||||
addvar(VAR_VALUE, "product", "Regular expression to match UPS Product string");
|
||||
addvar(VAR_VALUE, "serial", "Regular expression to match UPS Serial number");
|
||||
|
||||
addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name");
|
||||
/* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */
|
||||
nut_usb_addvars();
|
||||
|
||||
addvar(VAR_VALUE, "langid_fix", "Apply the language ID workaround to the krauler subdriver (0x409 or 0x4095)");
|
||||
#endif /* QX_USB */
|
||||
|
@ -1545,6 +1838,8 @@ void upsdrv_initups(void)
|
|||
{ "phoenix", &phoenix_command },
|
||||
{ "ippon", &ippon_command },
|
||||
{ "krauler", &krauler_command },
|
||||
{ "fabula", &fabula_command },
|
||||
{ "fuji", &fuji_command },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
@ -1794,7 +2089,7 @@ static int qx_command(const char *cmd, char *buf, size_t buflen)
|
|||
ret = ser_send(upsfd, "%s", cmd);
|
||||
|
||||
if (ret <= 0) {
|
||||
upsdebugx(3, "send: %s", ret ? strerror(errno) : "timeout");
|
||||
upsdebugx(3, "send: %s (%d)", ret ? strerror(errno) : "timeout", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1803,10 +2098,11 @@ static int qx_command(const char *cmd, char *buf, size_t buflen)
|
|||
ret = ser_get_buf(upsfd, buf, buflen, SER_WAIT_SEC, 0);
|
||||
|
||||
if (ret <= 0) {
|
||||
upsdebugx(3, "read: %s", ret ? strerror(errno) : "timeout");
|
||||
upsdebugx(3, "read: %s (%d)", ret ? strerror(errno) : "timeout", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
upsdebug_hex(5, "read", buf, ret);
|
||||
upsdebugx(3, "read: '%.*s'", (int)strcspn(buf, "\r"), buf);
|
||||
|
||||
#ifdef QX_USB
|
||||
|
@ -1833,6 +2129,22 @@ static int qx_command(const char *cmd, char *buf, size_t buflen)
|
|||
}
|
||||
|
||||
upsdebugx(3, "read: '%.*s'", (int)strcspn(testing[i].answer, "\r"), testing[i].answer);
|
||||
|
||||
/* If requested to do so and this is the case, try to preserve inner '\0's (treat answer as a sequence of bytes) */
|
||||
if (testing[i].answer_len > 0 && strlen(testing[i].answer) < (size_t)testing[i].answer_len) {
|
||||
|
||||
size_t len;
|
||||
|
||||
len = buflen <= (size_t)testing[i].answer_len ? buflen - 1 : (size_t)testing[i].answer_len;
|
||||
len = len <= sizeof(testing[i].answer) ? len : sizeof(testing[i].answer);
|
||||
|
||||
memcpy(buf, testing[i].answer, len);
|
||||
upsdebug_hex(4, "read", buf, (int)len);
|
||||
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
return snprintf(buf, buflen, "%s", testing[i].answer);
|
||||
|
||||
}
|
||||
|
@ -2038,6 +2350,10 @@ static bool_t qx_ups_walk(walkmode_t mode)
|
|||
batt.chrg.act = -1;
|
||||
}
|
||||
|
||||
/* Clear data from previous_item */
|
||||
memset(previous_item.command, 0, sizeof(previous_item.command));
|
||||
memset(previous_item.answer, 0, sizeof(previous_item.answer));
|
||||
|
||||
/* 3 modes: QX_WALKMODE_INIT, QX_WALKMODE_QUICK_UPDATE and QX_WALKMODE_FULL_UPDATE */
|
||||
|
||||
/* Device data walk */
|
||||
|
@ -2133,15 +2449,22 @@ static bool_t qx_ups_walk(walkmode_t mode)
|
|||
retcode = qx_process_answer(item, strlen(item->answer));
|
||||
|
||||
/* ..otherwise: execute command to get answer from the UPS */
|
||||
} else
|
||||
} else {
|
||||
|
||||
retcode = qx_process(item, NULL);
|
||||
|
||||
}
|
||||
|
||||
/* Record item as previous_item */
|
||||
snprintf(previous_item.command, sizeof(previous_item.command), "%s", item->command);
|
||||
snprintf(previous_item.answer, sizeof(previous_item.answer), "%s", item->answer);
|
||||
|
||||
if (retcode) {
|
||||
|
||||
/* Clear data from the item */
|
||||
memset(item->answer, 0, sizeof(item->answer));
|
||||
memset(item->value, 0, sizeof(item->value));
|
||||
|
||||
if (item->qxflags & QX_FLAG_QUICK_POLL)
|
||||
return FALSE;
|
||||
|
||||
|
@ -2149,10 +2472,6 @@ static bool_t qx_ups_walk(walkmode_t mode)
|
|||
/* Skip this item from now on */
|
||||
item->qxflags |= QX_FLAG_SKIP;
|
||||
|
||||
/* Clear data from the item */
|
||||
snprintf(item->answer, sizeof(item->answer), "%s", "");
|
||||
snprintf(item->value, sizeof(item->value), "%s", "");
|
||||
|
||||
/* Don't know what happened, try again later... */
|
||||
continue;
|
||||
|
||||
|
@ -2162,13 +2481,19 @@ static bool_t qx_ups_walk(walkmode_t mode)
|
|||
retcode = ups_infoval_set(item);
|
||||
|
||||
/* Clear data from the item */
|
||||
snprintf(item->answer, sizeof(item->answer), "%s", "");
|
||||
snprintf(item->value, sizeof(item->value), "%s", "");
|
||||
memset(item->answer, 0, sizeof(item->answer));
|
||||
memset(item->value, 0, sizeof(item->value));
|
||||
|
||||
/* Uh-oh! Some error! */
|
||||
if (retcode == -1)
|
||||
if (retcode == -1) {
|
||||
|
||||
if (item->qxflags & QX_FLAG_QUICK_POLL)
|
||||
return FALSE;
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
/* Set var flags/range/enum (not for ups.{alarm.status}, hence the retcode check) */
|
||||
if (retcode && mode == QX_WALKMODE_INIT) {
|
||||
qx_set_var(item);
|
||||
|
@ -2176,10 +2501,6 @@ static bool_t qx_ups_walk(walkmode_t mode)
|
|||
|
||||
}
|
||||
|
||||
/* Clear data from previous_item */
|
||||
snprintf(previous_item.command, sizeof(previous_item.command), "%s", "");
|
||||
snprintf(previous_item.answer, sizeof(previous_item.answer), "%s", "");
|
||||
|
||||
/* Update battery guesstimation */
|
||||
if (mode == QX_WALKMODE_FULL_UPDATE && (batt.runt.act == -1 || batt.chrg.act == -1)) {
|
||||
|
||||
|
@ -2345,14 +2666,26 @@ int qx_process(item_t *item, const char *command)
|
|||
/* Send the command */
|
||||
int len = qx_command(command ? command : item->command, buf, sizeof(buf));
|
||||
|
||||
snprintf(item->answer, sizeof(item->answer), "%s", buf);
|
||||
memset(item->answer, 0, sizeof(item->answer));
|
||||
memcpy(item->answer, buf, sizeof(item->answer) <= sizeof(buf) ? sizeof(item->answer) - 1 : sizeof(buf));
|
||||
|
||||
/* Preprocess the answer */
|
||||
if (item->preprocess_answer != NULL) {
|
||||
len = item->preprocess_answer(item, len);
|
||||
if (len == -1) {
|
||||
upsdebugx(4, "%s: failed to preprocess answer [%s]", __func__, item->info_type);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Process the answer to get the value */
|
||||
return qx_process_answer(item, len);
|
||||
}
|
||||
|
||||
/* Process the value we got back (set status bits and set the value of other parameters). */
|
||||
/* Return -1 on failure, 0 for a status update and 1 in all other cases */
|
||||
/* Process the value we got back (set status bits and set the value of other parameters) and execute:
|
||||
* - item-specific preprocessing, if any, otherwise
|
||||
* - the standard preprocessing (including trimming if QX_FLAG_TRIM is set).
|
||||
* Return -1 on failure, 0 for a status update and 1 in all other cases */
|
||||
int ups_infoval_set(item_t *item)
|
||||
{
|
||||
char value[SMALLBUF] = "";
|
||||
|
@ -2385,35 +2718,8 @@ int ups_infoval_set(item_t *item)
|
|||
snprintf(value, sizeof(value), "%s", item->value);
|
||||
|
||||
/* Cover most of the cases: either left/right filled with hashes, spaces or a mix of both */
|
||||
if (item->qxflags & QX_FLAG_TRIM) {
|
||||
|
||||
char buf[SMALLBUF];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", ltrim(value, ' '));
|
||||
snprintf(value, sizeof(value), "%s", buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", rtrim(value, ' '));
|
||||
snprintf(value, sizeof(value), "%s", buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", ltrim(value, '#'));
|
||||
snprintf(value, sizeof(value), "%s", buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", rtrim(value, '#'));
|
||||
snprintf(value, sizeof(value), "%s", buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", ltrim(value, ' '));
|
||||
snprintf(value, sizeof(value), "%s", buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", rtrim(value, ' '));
|
||||
snprintf(value, sizeof(value), "%s", buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", ltrim(value, '#'));
|
||||
snprintf(value, sizeof(value), "%s", buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", rtrim(value, '#'));
|
||||
snprintf(value, sizeof(value), "%s", buf);
|
||||
|
||||
}
|
||||
if (item->qxflags & QX_FLAG_TRIM)
|
||||
rtrim_m(ltrim_m(value, "# "), "# ");
|
||||
|
||||
if (strcasecmp(item->dfl, "%s")) {
|
||||
|
||||
|
|
|
@ -71,7 +71,8 @@ typedef struct item_t {
|
|||
* If QX_FLAG_SETVAR is set the value given by the user will be checked against these infos. */
|
||||
const char *command; /* Command sent to the UPS to get answer/to execute an instant command/to set a variable */
|
||||
|
||||
char answer[SMALLBUF]; /* Answer from the UPS, filled at runtime */
|
||||
char answer[SMALLBUF]; /* Answer from the UPS, filled at runtime.
|
||||
* If you expect a nonvalid C string (e.g.: inner '\0's) or need to perform actions before the answer is used (and treated as a null-terminated string), you should set a preprocess_answer() function */
|
||||
const int answer_len; /* Expected min length of the answer. Set it to 0 if there’s no minimum length to look after. */
|
||||
const char leading; /* Expected leading character of the answer (optional) */
|
||||
|
||||
|
@ -87,6 +88,10 @@ typedef struct item_t {
|
|||
|
||||
unsigned long qxflags; /* Driver's own flags */
|
||||
|
||||
int (*preprocess_answer)(struct item_t *item, const int len); /* Function to preprocess the answer we got from the UPS before we do anything else (e.g. for CRC, decoding, ...)
|
||||
* This function is given the currently processed item (item) with the answer we got from the UPS unmolested and already stored in item->answer and the length of that answer (len).
|
||||
* Return -1 in case of errors, else the length of the newly allocated item->answer (from now on, treated as a null-terminated string). */
|
||||
|
||||
int (*preprocess)(struct item_t *item, char *value, size_t valuelen); /* Function to preprocess the data from/to the UPS
|
||||
* This function is given the currently processed item (item), a char array (value) and its size_t (valuelen).
|
||||
* Return -1 in case of errors, else 0.
|
||||
|
@ -111,11 +116,17 @@ typedef struct item_t {
|
|||
|
||||
#define MAXTRIES 3 /* Max number of retries */
|
||||
|
||||
#ifdef TESTING
|
||||
/* Testing struct */
|
||||
typedef struct {
|
||||
const char *cmd; /* Command to match */
|
||||
const char *answer; /* Answer for that command */
|
||||
const char *cmd; /* Command to match */
|
||||
const char answer[SMALLBUF]; /* Answer for that command.
|
||||
* Note: if 'answer' contains inner '\0's, in order to preserve them, 'answer_len' as well as an item_t->preprocess_answer() function must be set */
|
||||
const int answer_len; /* Answer length:
|
||||
* - if set to -1 -> auto calculate answer length (treat 'answer' as a null-terminated string)
|
||||
* - otherwise -> use the provided length (if reasonable) and preserve inner '\0's (treat 'answer' as a sequence of bytes till the item_t->preprocess_answer() function gets called) */
|
||||
} testing_t;
|
||||
#endif /* TESTING */
|
||||
|
||||
/* Subdriver interface */
|
||||
typedef struct {
|
||||
|
@ -148,7 +159,8 @@ int setvar(const char *varname, const char *val);
|
|||
item_t *find_nut_info(const char *varname, const unsigned long flag, const unsigned long noflag);
|
||||
/* Send 'command' or, if it is NULL, send the command stored in the item to the UPS and process the reply. Return -1 on errors, 0 on success. */
|
||||
int qx_process(item_t *item, const char *command);
|
||||
/* Process the value we got back from the UPS (set status bits and set the value of other parameters), calling its preprocess function, if any. Return -1 on failure, 0 for a status update and 1 in all other cases. */
|
||||
/* Process the value we got back from the UPS (set status bits and set the value of other parameters), calling its preprocess function, if any, otherwise executing the standard preprocessing (including trimming if QX_FLAG_TRIM is set).
|
||||
* Return -1 on failure, 0 for a status update and 1 in all other cases. */
|
||||
int ups_infoval_set(item_t *item);
|
||||
/* Return the currently processed status so that it can be checked with one of the status_bit_t passed to the STATUS() macro. */
|
||||
int qx_status(void);
|
||||
|
|
705
drivers/nutdrv_qx_bestups.c
Normal file
705
drivers/nutdrv_qx_bestups.c
Normal file
|
@ -0,0 +1,705 @@
|
|||
/* nutdrv_qx_bestups.c - Subdriver for Best Power/Sola Australia UPSes
|
||||
*
|
||||
* Copyright (C)
|
||||
* 2014 Daniele Pezzini <hyouko@gmail.com>
|
||||
* Based on:
|
||||
* bestups.c - Copyright (C)
|
||||
* 1999 Russell Kroll <rkroll@exploits.org>
|
||||
* Jason White <jdwhite@jdwhite.org>
|
||||
*
|
||||
* 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 "nutdrv_qx.h"
|
||||
#include "nutdrv_qx_blazer-common.h"
|
||||
|
||||
#include "nutdrv_qx_bestups.h"
|
||||
|
||||
#define BESTUPS_VERSION "BestUPS 0.04"
|
||||
|
||||
/* Support functions */
|
||||
static int bestups_claim(void);
|
||||
static void bestups_initups(void);
|
||||
static void bestups_makevartable(void);
|
||||
|
||||
/* Answer preprocess functions */
|
||||
static int bestups_preprocess_id_answer(item_t *item, const int len);
|
||||
|
||||
/* Preprocess functions */
|
||||
static int bestups_process_setvar(item_t *item, char *value, size_t valuelen);
|
||||
static int bestups_process_bbb_status_bit(item_t *item, char *value, size_t valuelen);
|
||||
static int bestups_manufacturer(item_t *item, char *value, size_t valuelen);
|
||||
static int bestups_model(item_t *item, char *value, size_t valuelen);
|
||||
static int bestups_batt_runtime(item_t *item, char *value, size_t valuelen);
|
||||
static int bestups_batt_packs(item_t *item, char *value, size_t valuelen);
|
||||
static int bestups_get_pins_shutdown_mode(item_t *item, char *value, size_t valuelen);
|
||||
static int bestups_voltage_settings(item_t *item, char *value, size_t valuelen);
|
||||
|
||||
/* ups.conf settings */
|
||||
static int pins_shutdown_mode;
|
||||
|
||||
/* General settings */
|
||||
static int inverted_bbb_bit = 0;
|
||||
|
||||
|
||||
/* == Ranges/enums == */
|
||||
|
||||
/* Range for ups.delay.start */
|
||||
info_rw_t bestups_r_ondelay[] = {
|
||||
{ "60", 0 },
|
||||
{ "599940", 0 },
|
||||
{ "", 0 }
|
||||
};
|
||||
|
||||
/* Range for ups.delay.shutdown */
|
||||
static info_rw_t bestups_r_offdelay[] = {
|
||||
{ "12", 0 },
|
||||
{ "5940", 0 },
|
||||
{ "", 0 }
|
||||
};
|
||||
|
||||
/* Range for number of battery packs */
|
||||
static info_rw_t bestups_r_batt_packs[] = {
|
||||
{ "0", 0 },
|
||||
{ "5", 0 },
|
||||
{ "", 0 }
|
||||
};
|
||||
|
||||
/* Range for pin shutdown mode */
|
||||
static info_rw_t bestups_r_pins_shutdown_mode[] = {
|
||||
{ "0", 0 },
|
||||
{ "6", 0 },
|
||||
{ "", 0 }
|
||||
};
|
||||
|
||||
|
||||
/* == qx2nut lookup table == */
|
||||
static item_t bestups_qx2nut[] = {
|
||||
|
||||
/* Query UPS for status
|
||||
* > [Q1\r]
|
||||
* < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r]
|
||||
* 01234567890123456789012345678901234567890123456
|
||||
* 0 1 2 3 4
|
||||
*/
|
||||
|
||||
{ "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL },
|
||||
{ "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL },
|
||||
{ "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL },
|
||||
{ "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL },
|
||||
{ "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL },
|
||||
{ "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL },
|
||||
/* Status bits */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
/* { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, blazer_process_status_bits }, *//* Beeper status: not supported; always 0 */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, bestups_process_bbb_status_bit }, /* Bypass/Boost or Buck Active - keep this one at the end as it needs the processed data from the previous items */
|
||||
|
||||
/* Query UPS for ratings and model infos
|
||||
* > [ID\r]
|
||||
* < [FOR,750,120,120,20.0,27.6\r] case #1: length = 26
|
||||
* < [FOR,1500,120,120,20.0,27.6\r] case #2: length = 27
|
||||
* < [FOR,3000,120,120,20.0,100.6\r] case #3: length = 28
|
||||
* < [FOR, 750,120,120,20.0, 27.6\r] after being preprocessed: length = 28
|
||||
* 0123456789012345678901234567
|
||||
* 0 1 2
|
||||
*/
|
||||
|
||||
{ "device.mfr", 0, NULL, "ID\r", "", 28, 0, "", 0, 2, "%s", QX_FLAG_STATIC, bestups_preprocess_id_answer, bestups_manufacturer },
|
||||
{ "device.model", 0, NULL, "ID\r", "", 28, 0, "", 0, 2, "%s", QX_FLAG_STATIC, bestups_preprocess_id_answer, bestups_model },
|
||||
{ "ups.power.nominal", 0, NULL, "ID\r", "", 28, 0, "", 4, 7, "%.0f", QX_FLAG_STATIC, bestups_preprocess_id_answer, NULL },
|
||||
{ "input.voltage.nominal", 0, NULL, "ID\r", "", 28, 0, "", 9, 11, "%.0f", QX_FLAG_STATIC, bestups_preprocess_id_answer, NULL },
|
||||
{ "output.voltage.nominal", 0, NULL, "ID\r", "", 28, 0, "", 13, 15, "%.0f", QX_FLAG_STATIC, bestups_preprocess_id_answer, NULL },
|
||||
{ "battery.voltage.low", 0, NULL, "ID\r", "", 28, 0, "", 17, 20, "%.1f", QX_FLAG_SEMI_STATIC, bestups_preprocess_id_answer, NULL },
|
||||
{ "battery.voltage.high", 0, NULL, "ID\r", "", 28, 0, "", 22, 26, "%.1f", QX_FLAG_SEMI_STATIC, bestups_preprocess_id_answer, NULL },
|
||||
|
||||
/* Query UPS for battery runtime (not available on the Patriot Pro/Sola 320 model series)
|
||||
* > [RT\r]
|
||||
* < [025\r]
|
||||
* 0123
|
||||
* 0
|
||||
*/
|
||||
|
||||
{ "battery.runtime", 0, NULL, "RT\r", "", 4, 0, "", 0, 2, "%.0f", QX_FLAG_SKIP, NULL, bestups_batt_runtime },
|
||||
|
||||
/* Query UPS for number of battery packs (available only on the Axxium/Sola 620 model series)
|
||||
* > [BP?\r]
|
||||
* < [02\r]
|
||||
* 012
|
||||
* 0
|
||||
*/
|
||||
|
||||
{ "battery.packs", ST_FLAG_RW, bestups_r_batt_packs, "BP?\r", "", 3, 0, "", 0, 1, "%d", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, bestups_batt_packs },
|
||||
|
||||
/* Set number of battery packs to n (integer, 0-5) (available only on the Axxium/Sola 620 model series)
|
||||
* > [BPn\r]
|
||||
* < []
|
||||
*/
|
||||
|
||||
{ "battery.packs", 0, bestups_r_batt_packs, "BP%.0f\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, bestups_process_setvar },
|
||||
|
||||
/* Query UPS for shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power’s EPS-0059)
|
||||
* > [SS?\r]
|
||||
* < [0\r]
|
||||
* 01
|
||||
* 0
|
||||
*/
|
||||
|
||||
{ "pins_shutdown_mode", ST_FLAG_RW, bestups_r_pins_shutdown_mode, "SS?\r", "", 2, 0, "", 0, 0, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT, NULL, bestups_get_pins_shutdown_mode },
|
||||
|
||||
/* Set shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power’s EPS-0059) to n (integer, 0-6)
|
||||
* > [SSn\r]
|
||||
* < []
|
||||
*/
|
||||
|
||||
{ "pins_shutdown_mode", 0, bestups_r_pins_shutdown_mode, "SS%.0f\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, bestups_process_setvar },
|
||||
|
||||
/* Query UPS for voltage settings
|
||||
* > [M\r]
|
||||
* < [0\r]
|
||||
* 01
|
||||
* 0
|
||||
*/
|
||||
|
||||
{ "input.transfer.low", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, bestups_voltage_settings },
|
||||
{ "input.transfer.boost.low", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, bestups_voltage_settings },
|
||||
{ "input.transfer.boost.high", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, bestups_voltage_settings },
|
||||
{ "input.voltage.nominal", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, bestups_voltage_settings },
|
||||
{ "output.voltage.nominal", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, bestups_voltage_settings },
|
||||
{ "input.transfer.trim.low", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, bestups_voltage_settings },
|
||||
{ "input.transfer.trim.high", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, bestups_voltage_settings },
|
||||
{ "input.transfer.high", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, bestups_voltage_settings },
|
||||
|
||||
/* Instant commands */
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
|
||||
/* Server-side settable vars */
|
||||
{ "ups.delay.start", ST_FLAG_RW, bestups_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, bestups_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
|
||||
/* End of structure. */
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
/* == Testing table == */
|
||||
#ifdef TESTING
|
||||
static testing_t bestups_testing[] = {
|
||||
{ "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00100000\r", -1 },
|
||||
{ "ID\r", "FOR,750,120,120,20.0,27.6\r", -1 },
|
||||
{ "RT\r", "015\r", -1 },
|
||||
{ "BP?\r", "02\r", -1 },
|
||||
{ "BP1\r", "", -1 },
|
||||
{ "SS?\r", "0\r", -1 },
|
||||
{ "SS2\r", "", -1 },
|
||||
{ "M\r", "0\r", -1 },
|
||||
{ "S03\r", "", -1 },
|
||||
{ "C\r", "", -1 },
|
||||
{ "S02R0005\r", "", -1 },
|
||||
{ "S.5R0001\r", "", -1 },
|
||||
{ "T04\r", "", -1 },
|
||||
{ "TL\r", "", -1 },
|
||||
{ "T\r", "", -1 },
|
||||
{ "CT\r", "", -1 },
|
||||
{ NULL }
|
||||
};
|
||||
#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 bestups_claim(void)
|
||||
{
|
||||
/* We need at least Q1 and ID to run this subdriver */
|
||||
|
||||
item_t *item = find_nut_info("input.voltage", 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;
|
||||
|
||||
/* UPS Model */
|
||||
item = find_nut_info("device.model", 0, 0);
|
||||
|
||||
/* Don't know what happened */
|
||||
if (!item) {
|
||||
dstate_delinfo("input.voltage");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No reply/Unable to get value */
|
||||
if (qx_process(item, NULL)) {
|
||||
dstate_delinfo("input.voltage");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unable to process value */
|
||||
if (ups_infoval_set(item) != 1) {
|
||||
dstate_delinfo("input.voltage");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Subdriver-specific initups */
|
||||
static void bestups_initups(void)
|
||||
{
|
||||
blazer_initups_light(bestups_qx2nut);
|
||||
}
|
||||
|
||||
/* Subdriver-specific flags/vars */
|
||||
static void bestups_makevartable(void)
|
||||
{
|
||||
addvar(VAR_VALUE, "pins_shutdown_mode", "Set shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power’s EPS-0059) to n (integer, 0-6)");
|
||||
|
||||
blazer_makevartable_light();
|
||||
}
|
||||
|
||||
|
||||
/* == Answer preprocess functions == */
|
||||
|
||||
/* Preprocess the answer we got back from the UPS when queried with 'ID\r': make data begin always at the same indexes */
|
||||
static int bestups_preprocess_id_answer(item_t *item, const int len)
|
||||
{
|
||||
int i;
|
||||
char refined[SMALLBUF] = "",
|
||||
rawval[SMALLBUF] = "",
|
||||
*token,
|
||||
*saveptr = NULL;
|
||||
|
||||
if (len <= 0)
|
||||
return len;
|
||||
|
||||
if (len < 25 || len > 27) {
|
||||
upsdebugx(4, "%s: wrong length [%s: %d]", __func__, item->info_type, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* e.g.:
|
||||
* 1. item->answer = "FOR,750,120,120,20.0,27.6\r"; len = 26
|
||||
* 2. item->answer = "FOR,1500,120,120,20.0,27.6\r"; len = 27
|
||||
* 3. item->answer = "FOR,3000,120,120,20.0,100.6\r"; len = 28 */
|
||||
upsdebugx(4, "read: '%.*s'", (int)strcspn(item->answer, "\r"), item->answer);
|
||||
|
||||
snprintf(rawval, sizeof(rawval), "%s", item->answer);
|
||||
|
||||
for (i = 1, token = strtok_r(rawval, ",", &saveptr); token != NULL; i++, token = strtok_r(NULL, ",", &saveptr)) {
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 1:
|
||||
snprintf(refined, sizeof(refined), "%s", token);
|
||||
continue;
|
||||
case 2: /* Output power */
|
||||
snprintfcat(refined, sizeof(refined), ",%4s", token);
|
||||
continue;
|
||||
case 6: /* Battery voltage at full charge (+ trailing CR) */
|
||||
snprintfcat(refined, sizeof(refined), ",%6s", token);
|
||||
continue;
|
||||
default:
|
||||
snprintfcat(refined, sizeof(refined), ",%s", token);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (i != 7 || strlen(refined) != 28) {
|
||||
upsdebugx(2, "noncompliant reply: '%.*s'", (int)strcspn(refined, "\r"), refined);
|
||||
return -1;
|
||||
}
|
||||
|
||||
upsdebugx(4, "read: '%.*s'", (int)strcspn(refined, "\r"), refined);
|
||||
|
||||
/* e.g.: item->answer = "FOR, 750,120,120,20.0, 27.6\r"; len = 28 */
|
||||
return snprintf(item->answer, sizeof(item->answer), "%s", refined);
|
||||
}
|
||||
|
||||
|
||||
/* == Preprocess functions == */
|
||||
|
||||
/* *SETVAR(/NONUT)* Preprocess setvars */
|
||||
static int bestups_process_setvar(item_t *item, char *value, size_t valuelen)
|
||||
{
|
||||
if (!strlen(value)) {
|
||||
upsdebugx(2, "%s: value not given for %s", __func__, item->info_type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
double val = strtod(value, NULL);
|
||||
|
||||
if (!strcasecmp(item->info_type, "pins_shutdown_mode")) {
|
||||
|
||||
if (val == pins_shutdown_mode) {
|
||||
upslogx(LOG_INFO, "%s is already set to %.0f", item->info_type, val);
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
snprintf(value, valuelen, item->command, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Bypass/Boost or Buck status */
|
||||
static int bestups_process_bbb_status_bit(item_t *item, char *value, size_t valuelen)
|
||||
{
|
||||
/* Bypass/Boost/Buck bit is not reliable when a battery test, shutdown or on battery condition occurs: always ignore it in these cases */
|
||||
if (!(qx_status() & STATUS(OL)) || (qx_status() & (STATUS(CAL) | STATUS(FSD)))) {
|
||||
|
||||
if (item->value[0] == '1')
|
||||
item->value[0] = '0';
|
||||
|
||||
return blazer_process_status_bits(item, value, valuelen);
|
||||
|
||||
}
|
||||
|
||||
/* UPSes with inverted bypass/boost/buck bit */
|
||||
if (inverted_bbb_bit) {
|
||||
|
||||
if (item->value[0] == '1')
|
||||
item->value[0] = '0';
|
||||
|
||||
else if (item->value[0] == '0')
|
||||
item->value[0] = '1';
|
||||
|
||||
}
|
||||
|
||||
return blazer_process_status_bits(item, value, valuelen);
|
||||
}
|
||||
|
||||
/* Identify UPS manufacturer */
|
||||
static int bestups_manufacturer(item_t *item, char *value, size_t valuelen)
|
||||
{
|
||||
/* Best Power devices */
|
||||
if (
|
||||
!strcmp(item->value, "AX1") ||
|
||||
!strcmp(item->value, "FOR") ||
|
||||
!strcmp(item->value, "FTC") ||
|
||||
!strcmp(item->value, "PR2") ||
|
||||
!strcmp(item->value, "PRO")
|
||||
) {
|
||||
snprintf(value, valuelen, item->dfl, "Best Power");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sola Australia devices */
|
||||
if (
|
||||
!strcmp(item->value, "325") ||
|
||||
!strcmp(item->value, "520") ||
|
||||
!strcmp(item->value, "620")
|
||||
) {
|
||||
snprintf(value, valuelen, item->dfl, "Sola Australia");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unknown devices */
|
||||
snprintf(value, valuelen, item->dfl, "Unknown");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Identify UPS model and unskip qx2nut table's items accordingly */
|
||||
static int bestups_model(item_t *item, char *value, size_t valuelen)
|
||||
{
|
||||
item_t *unskip;
|
||||
|
||||
/* Best Power devices */
|
||||
|
||||
if (!strcmp(item->value, "AX1")) {
|
||||
|
||||
snprintf(value, valuelen, item->dfl, "Axxium Rackmount");
|
||||
|
||||
} else if (!strcmp(item->value, "FOR")) {
|
||||
|
||||
snprintf(value, valuelen, item->dfl, "Fortress");
|
||||
|
||||
} else if (!strcmp(item->value, "FTC")) {
|
||||
|
||||
snprintf(value, valuelen, item->dfl, "Fortress Telecom");
|
||||
|
||||
} else if (!strcmp(item->value, "PR2")) {
|
||||
|
||||
snprintf(value, valuelen, item->dfl, "Patriot Pro II");
|
||||
inverted_bbb_bit = 1;
|
||||
|
||||
} else if (!strcmp(item->value, "PRO")) {
|
||||
|
||||
snprintf(value, valuelen, item->dfl, "Patriot Pro");
|
||||
inverted_bbb_bit = 1;
|
||||
|
||||
/* Sola Australia devices */
|
||||
} else if (
|
||||
!strcmp(item->value, "320") ||
|
||||
!strcmp(item->value, "325") ||
|
||||
!strcmp(item->value, "520") ||
|
||||
!strcmp(item->value, "525") ||
|
||||
!strcmp(item->value, "620")
|
||||
) {
|
||||
|
||||
snprintf(value, valuelen, "Sola %s", item->value);
|
||||
|
||||
/* Unknown devices */
|
||||
} else {
|
||||
|
||||
snprintf(value, valuelen, item->dfl, "Unknown (%s)", item->value);
|
||||
upslogx(LOG_INFO, "Unknown model detected - please report this ID: '%s'", item->value);
|
||||
|
||||
}
|
||||
|
||||
/* Unskip qx2nut table's items according to the UPS model */
|
||||
|
||||
/* battery.runtime var is not available on the Patriot Pro/Sola 320 model series: leave it skipped in these cases, otherwise unskip it */
|
||||
if (strcmp(item->value, "PRO") && strcmp(item->value, "320")) {
|
||||
|
||||
unskip = find_nut_info("battery.runtime", 0, 0);
|
||||
|
||||
/* Don't know what happened */
|
||||
if (!unskip)
|
||||
return -1;
|
||||
|
||||
unskip->qxflags &= ~QX_FLAG_SKIP;
|
||||
|
||||
}
|
||||
|
||||
/* battery.packs var is available only on the Axxium/Sola 620 model series: unskip it in these cases */
|
||||
if (!strcmp(item->value, "AX1") || !strcmp(item->value, "620")) {
|
||||
|
||||
unskip = find_nut_info("battery.packs", 0, QX_FLAG_SETVAR);
|
||||
|
||||
/* Don't know what happened */
|
||||
if (!unskip)
|
||||
return -1;
|
||||
|
||||
unskip->qxflags &= ~QX_FLAG_SKIP;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Battery runtime */
|
||||
static int bestups_batt_runtime(item_t *item, char *value, size_t valuelen)
|
||||
{
|
||||
double runtime;
|
||||
|
||||
if (strspn(item->value, "0123456789 .") != strlen(item->value)) {
|
||||
upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Battery runtime is reported by the UPS in minutes, NUT expects seconds */
|
||||
runtime = strtod(item->value, NULL) * 60;
|
||||
|
||||
snprintf(value, valuelen, item->dfl, runtime);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Battery packs */
|
||||
static int bestups_batt_packs(item_t *item, char *value, size_t valuelen)
|
||||
{
|
||||
item_t *unskip;
|
||||
|
||||
if (strspn(item->value, "0123456789 ") != strlen(item->value)) {
|
||||
upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(value, valuelen, item->dfl, strtol(item->value, NULL, 10));
|
||||
|
||||
/* Unskip battery.packs setvar */
|
||||
unskip = find_nut_info("battery.packs", QX_FLAG_SETVAR, 0);
|
||||
|
||||
/* Don't know what happened */
|
||||
if (!unskip)
|
||||
return -1;
|
||||
|
||||
unskip->qxflags &= ~QX_FLAG_SKIP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* *NONUT* Get shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power’s EPS-0059) as set in the UPS */
|
||||
static int bestups_get_pins_shutdown_mode(item_t *item, char *value, size_t valuelen)
|
||||
{
|
||||
item_t *unskip;
|
||||
|
||||
if (strspn(item->value, "0123456789") != strlen(item->value)) {
|
||||
upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pins_shutdown_mode = strtol(item->value, NULL, 10);
|
||||
|
||||
snprintf(value, valuelen, item->dfl, pins_shutdown_mode);
|
||||
|
||||
/* We were not asked by the user to change the value */
|
||||
if ((item->qxflags & QX_FLAG_NONUT) && !getval(item->info_type))
|
||||
return 0;
|
||||
|
||||
/* Unskip setvar */
|
||||
unskip = find_nut_info(item->info_type, QX_FLAG_SETVAR, 0);
|
||||
|
||||
/* Don't know what happened */
|
||||
if (!unskip)
|
||||
return -1;
|
||||
|
||||
unskip->qxflags &= ~QX_FLAG_SKIP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Voltage settings */
|
||||
static int bestups_voltage_settings(item_t *item, char *value, size_t valuelen)
|
||||
{
|
||||
int index, val;
|
||||
const char *nominal_voltage;
|
||||
const struct {
|
||||
const int low; /* Low voltage -> input.transfer.low / input.transfer.boost.low */
|
||||
const int boost; /* Boost voltage -> input.transfer.boost.high */
|
||||
const int nominal; /* Nominal voltage -> input.voltage.nominal / output.voltage.nominal */
|
||||
const int buck; /* Buck voltage -> input.transfer.trim.low */
|
||||
const int high; /* High voltage -> input.transfer.high / input.transfer.trim.high */
|
||||
} voltage_settings[] = {
|
||||
/* U models voltage limits, for:
|
||||
* - Fortress (750U, 1050U, 1425U, 1800U and 2250U)
|
||||
* - Fortress Rackmount (750, 1050, 1425, 1800, and 2250 VA)
|
||||
* - Patriot Pro II (400U, 750U, and 1000U) */
|
||||
/* M low boost nominal buck high */
|
||||
/* 0 */ { 96, 109, 120, 130, 146 }, /* LEDs lit: 2,3,4 (Default) */
|
||||
/* 1 */ { 96, 109, 120, 138, 156 }, /* LEDs lit: 1,3,4 */
|
||||
/* 2 */ { 90, 104, 120, 130, 146 }, /* LEDs lit: 2,3,5 */
|
||||
/* 3 */ { 90, 104, 120, 138, 156 }, /* LEDs lit: 1,3,5 */
|
||||
/* 4 */ { 90, 104, 110, 120, 130 }, /* LEDs lit: 3,4,5 */
|
||||
/* 5 */ { 90, 104, 110, 130, 146 }, /* LEDs lit: 2,4,5 */
|
||||
/* 6 */ { 90, 96, 110, 120, 130 }, /* LEDs lit: 3,4,6 */
|
||||
/* 7 */ { 90, 96, 110, 130, 146 }, /* LEDs lit: 2,4,6 */
|
||||
/* 8 */ { 96, 109, 128, 146, 156 }, /* LEDs lit: 1,2,4 */
|
||||
/* 9 */ { 90, 104, 128, 146, 156 }, /* LEDs lit: 1,2,5 */
|
||||
|
||||
/* E models voltage limits, for:
|
||||
* - Fortress (750E, 1050E, 1425E, and 2250E)
|
||||
* - Fortress Rackmount (750, 1050, 1425, and 2250 VA)
|
||||
* - Patriot Pro II (400E, 750E, and 1000E) */
|
||||
/* M low boost nominal buck high */
|
||||
/* 0 */ { 200, 222, 240, 250, 284 }, /* LEDs lit: 2,3,4 */
|
||||
/* 1 */ { 200, 222, 240, 264, 290 }, /* LEDs lit: 1,3,4 */
|
||||
/* 2 */ { 188, 210, 240, 250, 284 }, /* LEDs lit: 2,3,5 */
|
||||
/* 3 */ { 188, 210, 240, 264, 290 }, /* LEDs lit: 1,3,5 */
|
||||
/* 4 */ { 188, 210, 230, 244, 270 }, /* LEDs lit: 3,4,5 (Default) */
|
||||
/* 5 */ { 188, 210, 230, 250, 284 }, /* LEDs lit: 2,4,5 */
|
||||
/* 6 */ { 180, 200, 230, 244, 270 }, /* LEDs lit: 3,4,6 */
|
||||
/* 7 */ { 180, 200, 230, 250, 284 }, /* LEDs lit: 2,4,6 */
|
||||
/* 8 */ { 165, 188, 208, 222, 244 }, /* LEDs lit: 4,5,6 */
|
||||
/* 9 */ { 165, 188, 208, 244, 270 } /* LEDs lit: 3,5,6 */
|
||||
};
|
||||
|
||||
if (strspn(item->value, "0123456789") != strlen(item->value)) {
|
||||
upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
index = strtol(item->value, NULL, 10);
|
||||
|
||||
if (index < 0 || index > 9) {
|
||||
upsdebugx(2, "%s: value '%d' out of range [0..9]", __func__, index);
|
||||
return -1;
|
||||
}
|
||||
|
||||
nominal_voltage = dstate_getinfo("input.voltage.nominal");
|
||||
|
||||
if (!nominal_voltage)
|
||||
nominal_voltage = dstate_getinfo("output.voltage.nominal");
|
||||
|
||||
if (!nominal_voltage) {
|
||||
upsdebugx(2, "%s: unable to get nominal voltage", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* E models */
|
||||
if (strtol(nominal_voltage, NULL, 10) > 160)
|
||||
index += 10;
|
||||
|
||||
if (!strcasecmp(item->info_type, "input.transfer.low") || !strcasecmp(item->info_type, "input.transfer.boost.low")) {
|
||||
|
||||
val = voltage_settings[index].low;
|
||||
|
||||
} else if (!strcasecmp(item->info_type, "input.transfer.boost.high")) {
|
||||
|
||||
val = voltage_settings[index].boost;
|
||||
|
||||
} else if (!strcasecmp(item->info_type, "input.voltage.nominal") || !strcasecmp(item->info_type, "output.voltage.nominal")) {
|
||||
|
||||
val = voltage_settings[index].nominal;
|
||||
|
||||
} else if (!strcasecmp(item->info_type, "input.transfer.trim.low")) {
|
||||
|
||||
val = voltage_settings[index].buck;
|
||||
|
||||
} else if (!strcasecmp(item->info_type, "input.transfer.trim.high") || !strcasecmp(item->info_type, "input.transfer.high")) {
|
||||
|
||||
val = voltage_settings[index].high;
|
||||
|
||||
} else {
|
||||
|
||||
/* Don't know what happened */
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
snprintf(value, valuelen, item->dfl, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* == Subdriver interface == */
|
||||
subdriver_t bestups_subdriver = {
|
||||
BESTUPS_VERSION,
|
||||
bestups_claim,
|
||||
bestups_qx2nut,
|
||||
bestups_initups,
|
||||
NULL,
|
||||
bestups_makevartable,
|
||||
NULL,
|
||||
NULL,
|
||||
#ifdef TESTING
|
||||
bestups_testing,
|
||||
#endif /* TESTING */
|
||||
};
|
29
drivers/nutdrv_qx_bestups.h
Normal file
29
drivers/nutdrv_qx_bestups.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* nutdrv_qx_bestups.h - Subdriver for Best Power/Sola Australia UPSes
|
||||
*
|
||||
* Copyright (C)
|
||||
* 2014 Daniele Pezzini <hyouko@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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 NUTDRV_QX_BESTUPS_H
|
||||
#define NUTDRV_QX_BESTUPS_H
|
||||
|
||||
#include "nutdrv_qx.h"
|
||||
|
||||
extern subdriver_t bestups_subdriver;
|
||||
|
||||
#endif /* NUTDRV_QX_BESTUPS_H */
|
|
@ -42,8 +42,8 @@ info_rw_t blazer_r_offdelay[] = {
|
|||
/* == Support functions == */
|
||||
|
||||
/* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */
|
||||
int blazer_claim(void) {
|
||||
|
||||
int blazer_claim(void)
|
||||
{
|
||||
/* To tell whether the UPS is supported or not, we'll check both status (Q1/QS/D) and vendor (I/FW?) - provided that we were not told not to do it with the ups.conf flag 'novendor'. */
|
||||
|
||||
item_t *item = find_nut_info("input.voltage", 0, 0);
|
||||
|
@ -85,13 +85,12 @@ int blazer_claim(void) {
|
|||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0.
|
||||
* NOTE: this 'light' version only checks for status (Q1/QS/D/..) */
|
||||
int blazer_claim_light(void) {
|
||||
|
||||
int blazer_claim_light(void)
|
||||
{
|
||||
/* To tell whether the UPS is supported or not, we'll check just status (Q1/QS/D/..). */
|
||||
|
||||
item_t *item = find_nut_info("input.voltage", 0, 0);
|
||||
|
@ -109,7 +108,6 @@ int blazer_claim_light(void) {
|
|||
return 0;
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* Subdriver-specific flags/vars */
|
||||
|
@ -117,18 +115,28 @@ void blazer_makevartable(void)
|
|||
{
|
||||
addvar(VAR_FLAG, "norating", "Skip reading rating information from UPS");
|
||||
addvar(VAR_FLAG, "novendor", "Skip reading vendor information from UPS");
|
||||
|
||||
blazer_makevartable_light();
|
||||
}
|
||||
|
||||
/* Subdriver-specific flags/vars
|
||||
* NOTE: this 'light' version only handles vars/flags related to UPS status query (Q1/QS/D/...) */
|
||||
void blazer_makevartable_light(void)
|
||||
{
|
||||
addvar(VAR_FLAG, "ignoresab", "Ignore 'Shutdown Active' bit in UPS status");
|
||||
}
|
||||
|
||||
/* Subdriver-specific initups */
|
||||
void blazer_initups(item_t *qx2nut)
|
||||
{
|
||||
int nr, nv;
|
||||
int nr, nv, isb;
|
||||
item_t *item;
|
||||
|
||||
nr = testvar("norating");
|
||||
nv = testvar("novendor");
|
||||
isb = testvar("ignoresab");
|
||||
|
||||
if (!nr && !nv)
|
||||
if (!nr && !nv && !isb)
|
||||
return;
|
||||
|
||||
for (item = qx2nut; item->info_type != NULL; item++) {
|
||||
|
@ -148,6 +156,33 @@ void blazer_initups(item_t *qx2nut)
|
|||
item->qxflags |= QX_FLAG_SKIP;
|
||||
}
|
||||
|
||||
/* ignoresab */
|
||||
if (isb && !strcasecmp(item->info_type, "ups.status") && item->from == 44 && item->to == 44) {
|
||||
upsdebugx(2, "%s: skipping %s ('Shutdown Active' bit)", __func__, item->info_type);
|
||||
item->qxflags |= QX_FLAG_SKIP;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Subdriver-specific initups
|
||||
* NOTE: this 'light' version only checks for status (Q1/QS/D/..) related items */
|
||||
void blazer_initups_light(item_t *qx2nut)
|
||||
{
|
||||
item_t *item;
|
||||
|
||||
if (!testvar("ignoresab"))
|
||||
return;
|
||||
|
||||
for (item = qx2nut; item->info_type != NULL; item++) {
|
||||
|
||||
if (strcasecmp(item->info_type, "ups.status") || item->from != 44 || item->to != 44)
|
||||
continue;
|
||||
|
||||
upsdebugx(2, "%s: skipping %s ('Shutdown Active' bit)", __func__, item->info_type);
|
||||
item->qxflags |= QX_FLAG_SKIP;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,14 +385,10 @@ int blazer_process_status_bits(item_t *item, char *value, size_t valuelen)
|
|||
|
||||
case 44: /* Shutdown Active */
|
||||
|
||||
if (item->value[0] == '1') {
|
||||
if (!strcasecmp(item->info_type, "ups.status"))
|
||||
val = "FSD";
|
||||
else /* ups.alarm */
|
||||
val = "Shutdown imminent!";
|
||||
} else if (!strcasecmp(item->info_type, "ups.status")) {
|
||||
if (item->value[0] == '1')
|
||||
val = "FSD";
|
||||
else
|
||||
val = "!FSD";
|
||||
}
|
||||
break;
|
||||
|
||||
case 45: /* Beeper status - ups.beeper.status */
|
||||
|
|
|
@ -26,7 +26,9 @@
|
|||
|
||||
/* Support functions */
|
||||
void blazer_makevartable(void);
|
||||
void blazer_makevartable_light(void);
|
||||
void blazer_initups(item_t *qx2nut);
|
||||
void blazer_initups_light(item_t *qx2nut);
|
||||
int blazer_claim(void);
|
||||
int blazer_claim_light(void);
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include "nutdrv_qx_mecer.h"
|
||||
|
||||
#define MECER_VERSION "Mecer 0.02"
|
||||
#define MECER_VERSION "Mecer 0.05"
|
||||
|
||||
/* Support functions */
|
||||
static int mecer_claim(void);
|
||||
|
@ -46,7 +46,7 @@ static item_t mecer_qx2nut[] = {
|
|||
* 0
|
||||
*/
|
||||
|
||||
{ "ups.firmware.aux", 0, NULL, "QPI\r", "", 6, '(', "", 1, 4, "%s", QX_FLAG_STATIC, voltronic_p98_protocol },
|
||||
{ "ups.firmware.aux", 0, NULL, "QPI\r", "", 6, '(', "", 1, 4, "%s", QX_FLAG_STATIC, NULL, voltronic_p98_protocol },
|
||||
|
||||
/*
|
||||
* > [Q1\r]
|
||||
|
@ -55,23 +55,22 @@ static item_t mecer_qx2nut[] = {
|
|||
* 0 1 2 3 4
|
||||
*/
|
||||
|
||||
{ "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL },
|
||||
{ "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL },
|
||||
{ "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL },
|
||||
{ "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL },
|
||||
{ "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL },
|
||||
{ "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL },
|
||||
{ "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL },
|
||||
{ "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL },
|
||||
{ "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL },
|
||||
{ "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL },
|
||||
{ "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL },
|
||||
{ "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL },
|
||||
/* Status bits */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, 0, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, blazer_process_status_bits }, /* Beeper status */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, blazer_process_status_bits }, /* Beeper status */
|
||||
|
||||
/*
|
||||
* > [F\r]
|
||||
|
@ -80,10 +79,10 @@ static item_t mecer_qx2nut[] = {
|
|||
* 0 1 2
|
||||
*/
|
||||
|
||||
{ "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL },
|
||||
{ "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL },
|
||||
|
||||
/*
|
||||
* > [I\r]
|
||||
|
@ -92,49 +91,49 @@ static item_t mecer_qx2nut[] = {
|
|||
* 0 1 2 3
|
||||
*/
|
||||
|
||||
{ "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
{ "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
{ "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
|
||||
/* Instant commands
|
||||
* The UPS will reply '(ACK\r' in case of success, '(NAK\r' if the command is rejected or invalid */
|
||||
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.start", 0, NULL, "T%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, mecer_process_test_battery },
|
||||
{ "test.battery.start.deep", 0, NULL, "TL\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.stop", 0, NULL, "CT\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start", 0, NULL, "T%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, mecer_process_test_battery },
|
||||
{ "test.battery.start.deep", 0, NULL, "TL\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.stop", 0, NULL, "CT\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
|
||||
/* Server-side settable vars */
|
||||
{ "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
|
||||
/* End of structure. */
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL }
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
/* == Testing table == */
|
||||
#ifdef TESTING
|
||||
static testing_t mecer_testing[] = {
|
||||
{ "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r" },
|
||||
{ "QPI\r", "(PI98\r" },
|
||||
{ "F\r", "#230.0 000 024.0 50.0\r" },
|
||||
{ "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r" },
|
||||
{ "Q\r", "(ACK\r" },
|
||||
{ "S03\r", "(NAK\r" },
|
||||
{ "C\r", "(NAK\r" },
|
||||
{ "S02R0005\r", "(ACK\r" },
|
||||
{ "S.5R0000\r", "(ACK\r" },
|
||||
{ "T04\r", "(NAK\r" },
|
||||
{ "TL\r", "(ACK\r" },
|
||||
{ "T\r", "(NAK\r" },
|
||||
{ "CT\r", "(ACK\r" },
|
||||
{ "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 },
|
||||
{ "QPI\r", "(PI98\r", -1 },
|
||||
{ "F\r", "#230.0 000 024.0 50.0\r", -1 },
|
||||
{ "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 },
|
||||
{ "Q\r", "(ACK\r", -1 },
|
||||
{ "S03\r", "(NAK\r", -1 },
|
||||
{ "C\r", "(NAK\r", -1 },
|
||||
{ "S02R0005\r", "(ACK\r", -1 },
|
||||
{ "S.5R0000\r", "(ACK\r", -1 },
|
||||
{ "T04\r", "(NAK\r", -1 },
|
||||
{ "TL\r", "(ACK\r", -1 },
|
||||
{ "T\r", "(NAK\r", -1 },
|
||||
{ "CT\r", "(ACK\r", -1 },
|
||||
{ NULL }
|
||||
};
|
||||
#endif /* TESTING */
|
||||
|
@ -145,7 +144,6 @@ static testing_t mecer_testing[] = {
|
|||
/* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */
|
||||
static int mecer_claim(void)
|
||||
{
|
||||
|
||||
/* Apart from status (Q1), try to identify protocol (QPI, for Voltronic Power P98 units) or whether the UPS uses '(ACK\r'/'(NAK\r' replies */
|
||||
|
||||
item_t *item = find_nut_info("input.voltage", 0, 0);
|
||||
|
@ -198,15 +196,12 @@ static int mecer_claim(void)
|
|||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* Subdriver-specific initups */
|
||||
static void mecer_initups(void)
|
||||
{
|
||||
|
||||
blazer_initups(mecer_qx2nut);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include "nutdrv_qx_megatec-old.h"
|
||||
|
||||
#define MEGATEC_OLD_VERSION "Megatec/old 0.02"
|
||||
#define MEGATEC_OLD_VERSION "Megatec/old 0.05"
|
||||
|
||||
/* qx2nut lookup table */
|
||||
static item_t megatec_old_qx2nut[] = {
|
||||
|
@ -37,23 +37,22 @@ static item_t megatec_old_qx2nut[] = {
|
|||
* 0 1 2 3 4
|
||||
*/
|
||||
|
||||
{ "input.voltage", 0, NULL, "D\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "D\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL },
|
||||
{ "output.voltage", 0, NULL, "D\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL },
|
||||
{ "ups.load", 0, NULL, "D\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL },
|
||||
{ "input.frequency", 0, NULL, "D\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL },
|
||||
{ "battery.voltage", 0, NULL, "D\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL },
|
||||
{ "ups.temperature", 0, NULL, "D\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL },
|
||||
{ "input.voltage", 0, NULL, "D\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "D\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL },
|
||||
{ "output.voltage", 0, NULL, "D\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL },
|
||||
{ "ups.load", 0, NULL, "D\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL },
|
||||
{ "input.frequency", 0, NULL, "D\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL },
|
||||
{ "battery.voltage", 0, NULL, "D\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL },
|
||||
{ "ups.temperature", 0, NULL, "D\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL },
|
||||
/* Status bits */
|
||||
{ "ups.status", 0, NULL, "D\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "D\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "D\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "D\r", "", 47, '(', "", 41, 41, NULL, 0, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "D\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "D\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.alarm", 0, NULL, "D\r", "", 47, '(', "", 44, 44, NULL, 0, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.status", 0, NULL, "D\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "D\r", "", 47, '(', "", 45, 45, "%s", 0, blazer_process_status_bits }, /* Beeper status */
|
||||
{ "ups.status", 0, NULL, "D\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "D\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "D\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "D\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "D\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "D\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.status", 0, NULL, "D\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "D\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, blazer_process_status_bits }, /* Beeper status */
|
||||
|
||||
/*
|
||||
* > [F\r]
|
||||
|
@ -62,10 +61,10 @@ static item_t megatec_old_qx2nut[] = {
|
|||
* 0 1 2
|
||||
*/
|
||||
|
||||
{ "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL },
|
||||
{ "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL },
|
||||
|
||||
/*
|
||||
* > [I\r]
|
||||
|
@ -74,45 +73,45 @@ static item_t megatec_old_qx2nut[] = {
|
|||
* 0 1 2 3
|
||||
*/
|
||||
|
||||
{ "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
{ "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
{ "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
|
||||
/* Instant commands */
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
|
||||
/* Server-side settable vars */
|
||||
{ "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
|
||||
/* End of structure. */
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL }
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
/* Testing table */
|
||||
#ifdef TESTING
|
||||
static testing_t megatec_old_testing[] = {
|
||||
{ "D\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r" },
|
||||
{ "F\r", "#230.0 000 024.0 50.0\r" },
|
||||
{ "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r" },
|
||||
{ "Q\r", "" },
|
||||
{ "S03\r", "" },
|
||||
{ "C\r", "" },
|
||||
{ "S02R0005\r", "" },
|
||||
{ "S.5R0000\r", "" },
|
||||
{ "T04\r", "" },
|
||||
{ "TL\r", "" },
|
||||
{ "T\r", "" },
|
||||
{ "CT\r", "" },
|
||||
{ "D\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 },
|
||||
{ "F\r", "#230.0 000 024.0 50.0\r", -1 },
|
||||
{ "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 },
|
||||
{ "Q\r", "", -1 },
|
||||
{ "S03\r", "", -1 },
|
||||
{ "C\r", "", -1 },
|
||||
{ "S02R0005\r", "", -1 },
|
||||
{ "S.5R0000\r", "", -1 },
|
||||
{ "T04\r", "", -1 },
|
||||
{ "TL\r", "", -1 },
|
||||
{ "T\r", "", -1 },
|
||||
{ "CT\r", "", -1 },
|
||||
{ NULL }
|
||||
};
|
||||
#endif /* TESTING */
|
||||
|
@ -120,9 +119,7 @@ static testing_t megatec_old_testing[] = {
|
|||
/* Subdriver-specific initups */
|
||||
static void megatec_old_initups(void)
|
||||
{
|
||||
|
||||
blazer_initups(megatec_old_qx2nut);
|
||||
|
||||
}
|
||||
|
||||
/* Subdriver interface */
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include "nutdrv_qx_megatec.h"
|
||||
|
||||
#define MEGATEC_VERSION "Megatec 0.01"
|
||||
#define MEGATEC_VERSION "Megatec 0.04"
|
||||
|
||||
/* qx2nut lookup table */
|
||||
static item_t megatec_qx2nut[] = {
|
||||
|
@ -37,23 +37,22 @@ static item_t megatec_qx2nut[] = {
|
|||
* 0 1 2 3 4
|
||||
*/
|
||||
|
||||
{ "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL },
|
||||
{ "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL },
|
||||
{ "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL },
|
||||
{ "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL },
|
||||
{ "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL },
|
||||
{ "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL },
|
||||
{ "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL },
|
||||
{ "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL },
|
||||
{ "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL },
|
||||
{ "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL },
|
||||
{ "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL },
|
||||
{ "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL },
|
||||
/* Status bits */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, 0, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, blazer_process_status_bits }, /* Beeper status */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, blazer_process_status_bits }, /* Beeper status */
|
||||
|
||||
/*
|
||||
* > [F\r]
|
||||
|
@ -62,10 +61,10 @@ static item_t megatec_qx2nut[] = {
|
|||
* 0 1 2
|
||||
*/
|
||||
|
||||
{ "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL },
|
||||
{ "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL },
|
||||
|
||||
/*
|
||||
* > [I\r]
|
||||
|
@ -74,45 +73,45 @@ static item_t megatec_qx2nut[] = {
|
|||
* 0 1 2 3
|
||||
*/
|
||||
|
||||
{ "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
{ "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
{ "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
|
||||
/* Instant commands */
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
|
||||
/* Server-side settable vars */
|
||||
{ "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
|
||||
/* End of structure. */
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL }
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
/* Testing table */
|
||||
#ifdef TESTING
|
||||
static testing_t megatec_testing[] = {
|
||||
{ "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r" },
|
||||
{ "F\r", "#230.0 000 024.0 50.0\r" },
|
||||
{ "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r" },
|
||||
{ "Q\r", "" },
|
||||
{ "S03\r", "" },
|
||||
{ "C\r", "" },
|
||||
{ "S02R0005\r", "" },
|
||||
{ "S.5R0000\r", "" },
|
||||
{ "T04\r", "" },
|
||||
{ "TL\r", "" },
|
||||
{ "T\r", "" },
|
||||
{ "CT\r", "" },
|
||||
{ "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 },
|
||||
{ "F\r", "#230.0 000 024.0 50.0\r", -1 },
|
||||
{ "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 },
|
||||
{ "Q\r", "", -1 },
|
||||
{ "S03\r", "", -1 },
|
||||
{ "C\r", "", -1 },
|
||||
{ "S02R0005\r", "", -1 },
|
||||
{ "S.5R0000\r", "", -1 },
|
||||
{ "T04\r", "", -1 },
|
||||
{ "TL\r", "", -1 },
|
||||
{ "T\r", "", -1 },
|
||||
{ "CT\r", "", -1 },
|
||||
{ NULL }
|
||||
};
|
||||
#endif /* TESTING */
|
||||
|
@ -120,9 +119,7 @@ static testing_t megatec_testing[] = {
|
|||
/* Subdriver-specific initups */
|
||||
static void megatec_initups(void)
|
||||
{
|
||||
|
||||
blazer_initups(megatec_qx2nut);
|
||||
|
||||
}
|
||||
|
||||
/* Subdriver interface */
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include "nutdrv_qx_mustek.h"
|
||||
|
||||
#define MUSTEK_VERSION "Mustek 0.02"
|
||||
#define MUSTEK_VERSION "Mustek 0.05"
|
||||
|
||||
/* qx2nut lookup table */
|
||||
static item_t mustek_qx2nut[] = {
|
||||
|
@ -37,23 +37,22 @@ static item_t mustek_qx2nut[] = {
|
|||
* 0 1 2 3 4
|
||||
*/
|
||||
|
||||
{ "input.voltage", 0, NULL, "QS\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "QS\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL },
|
||||
{ "output.voltage", 0, NULL, "QS\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL },
|
||||
{ "ups.load", 0, NULL, "QS\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL },
|
||||
{ "input.frequency", 0, NULL, "QS\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL },
|
||||
{ "battery.voltage", 0, NULL, "QS\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL },
|
||||
{ "ups.temperature", 0, NULL, "QS\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL },
|
||||
{ "input.voltage", 0, NULL, "QS\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "QS\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL },
|
||||
{ "output.voltage", 0, NULL, "QS\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL },
|
||||
{ "ups.load", 0, NULL, "QS\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL },
|
||||
{ "input.frequency", 0, NULL, "QS\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL },
|
||||
{ "battery.voltage", 0, NULL, "QS\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL },
|
||||
{ "ups.temperature", 0, NULL, "QS\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL },
|
||||
/* Status bits */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "QS\r", "", 47, '(', "", 41, 41, NULL, 0, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "QS\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.alarm", 0, NULL, "QS\r", "", 47, '(', "", 44, 44, NULL, 0, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "QS\r", "", 47, '(', "", 45, 45, "%s", 0, blazer_process_status_bits }, /* Beeper status */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "QS\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "QS\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "QS\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, blazer_process_status_bits }, /* Beeper status */
|
||||
|
||||
/*
|
||||
* > [F\r]
|
||||
|
@ -62,10 +61,10 @@ static item_t mustek_qx2nut[] = {
|
|||
* 0 1 2
|
||||
*/
|
||||
|
||||
{ "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL },
|
||||
{ "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL },
|
||||
|
||||
/*
|
||||
* > [I\r]
|
||||
|
@ -74,45 +73,45 @@ static item_t mustek_qx2nut[] = {
|
|||
* 0 1 2 3
|
||||
*/
|
||||
|
||||
{ "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
{ "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
{ "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
|
||||
/* Instant commands */
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
|
||||
/* Server-side settable vars */
|
||||
{ "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
|
||||
/* End of structure. */
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL }
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
/* Testing table */
|
||||
#ifdef TESTING
|
||||
static testing_t mustek_testing[] = {
|
||||
{ "QS\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r" },
|
||||
{ "F\r", "#230.0 000 024.0 50.0\r" },
|
||||
{ "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r" },
|
||||
{ "Q\r", "" },
|
||||
{ "S03\r", "" },
|
||||
{ "C\r", "" },
|
||||
{ "S02R0005\r", "" },
|
||||
{ "S.5R0000\r", "" },
|
||||
{ "T04\r", "" },
|
||||
{ "TL\r", "" },
|
||||
{ "T\r", "" },
|
||||
{ "CT\r", "" },
|
||||
{ "QS\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 },
|
||||
{ "F\r", "#230.0 000 024.0 50.0\r", -1 },
|
||||
{ "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 },
|
||||
{ "Q\r", "", -1 },
|
||||
{ "S03\r", "", -1 },
|
||||
{ "C\r", "", -1 },
|
||||
{ "S02R0005\r", "", -1 },
|
||||
{ "S.5R0000\r", "", -1 },
|
||||
{ "T04\r", "", -1 },
|
||||
{ "TL\r", "", -1 },
|
||||
{ "T\r", "", -1 },
|
||||
{ "CT\r", "", -1 },
|
||||
{ NULL }
|
||||
};
|
||||
#endif /* TESTING */
|
||||
|
@ -120,9 +119,7 @@ static testing_t mustek_testing[] = {
|
|||
/* Subdriver-specific initups */
|
||||
static void mustek_initups(void)
|
||||
{
|
||||
|
||||
blazer_initups(mustek_qx2nut);
|
||||
|
||||
}
|
||||
|
||||
/* Subdriver interface */
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
#include "nutdrv_qx_q1.h"
|
||||
|
||||
#define Q1_VERSION "Q1 0.02"
|
||||
#define Q1_VERSION "Q1 0.05"
|
||||
|
||||
/* qx2nut lookup table */
|
||||
static item_t q1_qx2nut[] = {
|
||||
|
@ -47,69 +47,74 @@ static item_t q1_qx2nut[] = {
|
|||
* 0 1 2 3 4
|
||||
*/
|
||||
|
||||
{ "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL },
|
||||
{ "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL },
|
||||
{ "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL },
|
||||
{ "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL },
|
||||
{ "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL },
|
||||
{ "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL },
|
||||
{ "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL },
|
||||
{ "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL },
|
||||
{ "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL },
|
||||
{ "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL },
|
||||
{ "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL },
|
||||
{ "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL },
|
||||
/* Status bits */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, 0, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, blazer_process_status_bits }, /* Beeper status */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, blazer_process_status_bits }, /* Beeper status */
|
||||
|
||||
/* Instant commands */
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
|
||||
/* Server-side settable vars */
|
||||
{ "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
|
||||
/* End of structure. */
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL }
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
/* Testing table */
|
||||
#ifdef TESTING
|
||||
static testing_t q1_testing[] = {
|
||||
{ "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r" },
|
||||
{ "Q\r", "" },
|
||||
{ "S03\r", "" },
|
||||
{ "C\r", "" },
|
||||
{ "S02R0005\r", "" },
|
||||
{ "S.5R0000\r", "" },
|
||||
{ "T04\r", "" },
|
||||
{ "TL\r", "" },
|
||||
{ "T\r", "" },
|
||||
{ "CT\r", "" },
|
||||
{ "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 },
|
||||
{ "Q\r", "", -1 },
|
||||
{ "S03\r", "", -1 },
|
||||
{ "C\r", "", -1 },
|
||||
{ "S02R0005\r", "", -1 },
|
||||
{ "S.5R0000\r", "", -1 },
|
||||
{ "T04\r", "", -1 },
|
||||
{ "TL\r", "", -1 },
|
||||
{ "T\r", "", -1 },
|
||||
{ "CT\r", "", -1 },
|
||||
{ NULL }
|
||||
};
|
||||
#endif /* TESTING */
|
||||
|
||||
/* Subdriver-specific initups */
|
||||
static void q1_initups(void)
|
||||
{
|
||||
blazer_initups_light(q1_qx2nut);
|
||||
}
|
||||
|
||||
/* Subdriver interface */
|
||||
subdriver_t q1_subdriver = {
|
||||
Q1_VERSION,
|
||||
blazer_claim_light,
|
||||
q1_qx2nut,
|
||||
q1_initups,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
blazer_makevartable_light,
|
||||
"ACK",
|
||||
NULL,
|
||||
#ifdef TESTING
|
||||
|
|
415
drivers/nutdrv_qx_voltronic-qs-hex.c
Normal file
415
drivers/nutdrv_qx_voltronic-qs-hex.c
Normal file
|
@ -0,0 +1,415 @@
|
|||
/* nutdrv_qx_voltronic-qs-hex.c - Subdriver for Voltronic Power UPSes with QS-Hex protocol
|
||||
*
|
||||
* Copyright (C)
|
||||
* 2014 Daniele Pezzini <hyouko@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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 "nutdrv_qx.h"
|
||||
#include "nutdrv_qx_blazer-common.h"
|
||||
|
||||
#include "nutdrv_qx_voltronic-qs-hex.h"
|
||||
|
||||
#define VOLTRONIC_QS_HEX_VERSION "Voltronic-QS-Hex 0.03"
|
||||
|
||||
/* Support functions */
|
||||
static int voltronic_qs_hex_claim(void);
|
||||
static void voltronic_qs_hex_initups(void);
|
||||
|
||||
/* Answer preprocess functions */
|
||||
static int voltronic_qs_hex_preprocess_qs_answer(item_t *item, const int len);
|
||||
static int voltronic_qs_hex_status_char_to_binary(const unsigned char value);
|
||||
|
||||
/* Preprocess functions */
|
||||
static int voltronic_qs_hex_protocol(item_t *item, char *value, size_t valuelen);
|
||||
static int voltronic_qs_hex_input_output_voltage(item_t *item, char *value, size_t valuelen);
|
||||
static int voltronic_qs_hex_input_output_voltage(item_t *item, char *value, size_t valuelen);
|
||||
static int voltronic_qs_hex_load(item_t *item, char *value, size_t valuelen);
|
||||
static int voltronic_qs_hex_frequency(item_t *item, char *value, size_t valuelen);
|
||||
static int voltronic_qs_hex_battery_voltage(item_t *item, char *value, size_t valuelen);
|
||||
|
||||
|
||||
/* == Ranges == */
|
||||
|
||||
/* Range for ups.delay.start */
|
||||
static info_rw_t voltronic_qs_hex_r_ondelay[] = {
|
||||
{ "60", 0 },
|
||||
{ "599940", 0 },
|
||||
{ "", 0 }
|
||||
};
|
||||
|
||||
/* Range for ups.delay.shutdown */
|
||||
static info_rw_t voltronic_qs_hex_r_offdelay[] = {
|
||||
{ "12", 0 },
|
||||
{ "540", 0 },
|
||||
{ "", 0 }
|
||||
};
|
||||
|
||||
|
||||
/* == qx2nut lookup table == */
|
||||
static item_t voltronic_qs_hex_qx2nut[] = {
|
||||
|
||||
/* Query UPS for protocol
|
||||
* > [M\r]
|
||||
* < [P\r]
|
||||
* 01
|
||||
* 0
|
||||
*/
|
||||
|
||||
{ "ups.firmware.aux", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "PMV-%s", QX_FLAG_STATIC, NULL, voltronic_qs_hex_protocol },
|
||||
|
||||
/* Query UPS for status
|
||||
* > [QS\r]
|
||||
* < [#6C01 35 6C01 35 03 519A 1312D0 E6 1E 00001001\r] (after being preprocessed)
|
||||
* 01234567890123456789012345678901234567890123456
|
||||
* 0 1 2 3 4
|
||||
*/
|
||||
|
||||
{ "input.voltage", 0, NULL, "QS\r", "", 47, '#', "", 1, 7, "%.1f", 0, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_input_output_voltage },
|
||||
{ "output.voltage", 0, NULL, "QS\r", "", 47, '#', "", 9, 15, "%.1f", 0, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_input_output_voltage },
|
||||
{ "ups.load", 0, NULL, "QS\r", "", 47, '#', "", 17, 18, "%d", 0, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_load },
|
||||
{ "output.frequency", 0, NULL, "QS\r", "", 47, '#', "", 20, 30, "%.1f", 0, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_frequency },
|
||||
{ "battery.voltage", 0, NULL, "QS\r", "", 47, '#', "", 32, 36, "%.2f", 0, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_battery_voltage },
|
||||
/* Status bits */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "QS\r", "", 47, '#', "", 41, 41, NULL, 0, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "QS\r", "", 47, '#', "", 42, 42, "%s", QX_FLAG_STATIC, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "QS\r", "", 47, '#', "", 45, 45, "%s", 0, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Beeper status */
|
||||
|
||||
/* Instant commands */
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL },
|
||||
|
||||
/* Server-side settable vars */
|
||||
{ "ups.delay.start", ST_FLAG_RW, voltronic_qs_hex_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, voltronic_qs_hex_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
|
||||
/* End of structure. */
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
/* == Testing table == */
|
||||
#ifdef TESTING
|
||||
static testing_t voltronic_qs_hex_testing[] = {
|
||||
{ "QS\r", "#\x6C\x01 \x35 \x6C\x01 \x35 \x03 \x51\x9A \x28\x02\x12\xD0 \xE6 \x1E \x09\r", 27 },
|
||||
{ "M\r", "P\r", -1 },
|
||||
{ "Q\r", "", -1 },
|
||||
{ "S03\r", "", -1 },
|
||||
{ "C\r", "", -1 },
|
||||
{ "S02R0005\r", "", -1 },
|
||||
{ "S.5R0000\r", "N\r", -1 },
|
||||
{ "T04\r", "", -1 },
|
||||
{ "TL\r", "", -1 },
|
||||
{ "T\r", "", -1 },
|
||||
{ "CT\r", "", -1 },
|
||||
{ NULL }
|
||||
};
|
||||
#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 voltronic_qs_hex_claim(void)
|
||||
{
|
||||
/* We need at least M and QS to run this subdriver */
|
||||
|
||||
/* UPS Protocol */
|
||||
item_t *item = find_nut_info("ups.firmware.aux", 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/Protocol not supported */
|
||||
if (ups_infoval_set(item) != 1)
|
||||
return 0;
|
||||
|
||||
item = find_nut_info("input.voltage", 0, 0);
|
||||
|
||||
/* Don't know what happened */
|
||||
if (!item) {
|
||||
dstate_delinfo("ups.firmware.aux");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No reply/Unable to get value */
|
||||
if (qx_process(item, NULL)) {
|
||||
dstate_delinfo("ups.firmware.aux");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unable to process value */
|
||||
if (ups_infoval_set(item) != 1) {
|
||||
dstate_delinfo("ups.firmware.aux");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Subdriver-specific initups */
|
||||
static void voltronic_qs_hex_initups(void)
|
||||
{
|
||||
blazer_initups_light(voltronic_qs_hex_qx2nut);
|
||||
}
|
||||
|
||||
|
||||
/* == Answer preprocess functions == */
|
||||
|
||||
/* Preprocess the answer we got back from the UPS when queried with 'QS\r' */
|
||||
static int voltronic_qs_hex_preprocess_qs_answer(item_t *item, const int len)
|
||||
{
|
||||
int i, token;
|
||||
char refined[SMALLBUF] = "";
|
||||
|
||||
if (len <= 0)
|
||||
return len;
|
||||
|
||||
if (item->answer[0] != '#') {
|
||||
upsdebugx(4, "%s: wrong leading character [%s: 0x%0x]", __func__, item->info_type, item->answer[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(refined, sizeof(refined), "%s", "#");
|
||||
|
||||
/* e.g.: item->answer = "#\x6C\x01 \x35 \x6C\x01 \x35 \x03 \x51\x9A \x28\x02\x12\xD0 \xE6 \x1E \x09\r" */
|
||||
upsdebug_hex(4, "read", item->answer, len);
|
||||
|
||||
for (i = 1, token = 1; i < len; i++) {
|
||||
|
||||
/* New token */
|
||||
if (item->answer[i] == 0x20) {
|
||||
snprintfcat(refined, sizeof(refined), "%s", " ");
|
||||
token++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 'Unescape' raw data */
|
||||
if (item->answer[i] == 0x28 && i < len) {
|
||||
|
||||
switch (item->answer[i + 1])
|
||||
{
|
||||
case 0x00: /* Escaped because: CR */
|
||||
snprintfcat(refined, sizeof(refined), "%02x", 0x0D);
|
||||
break;
|
||||
case 0x01: /* Escaped because: XON */
|
||||
snprintfcat(refined, sizeof(refined), "%02x", 0x11);
|
||||
break;
|
||||
case 0x02: /* Escaped because: XOFF */
|
||||
snprintfcat(refined, sizeof(refined), "%02x", 0x13);
|
||||
break;
|
||||
case 0x03: /* Escaped because: LF */
|
||||
snprintfcat(refined, sizeof(refined), "%02x", 0x0A);
|
||||
break;
|
||||
case 0x04: /* Escaped because: space */
|
||||
snprintfcat(refined, sizeof(refined), "%02x", 0x20);
|
||||
break;
|
||||
default:
|
||||
if (token != 10)
|
||||
snprintfcat(refined, sizeof(refined), "%02x", ((unsigned char *)item->answer)[i]);
|
||||
else
|
||||
snprintfcat(refined, sizeof(refined), "%08d", voltronic_qs_hex_status_char_to_binary(((unsigned char *)item->answer)[i]));
|
||||
continue;
|
||||
}
|
||||
|
||||
i++;
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
/* Trailing CR */
|
||||
if (item->answer[i] == 0x0D)
|
||||
break;
|
||||
|
||||
if (token != 10)
|
||||
snprintfcat(refined, sizeof(refined), "%02x", ((unsigned char *)item->answer)[i]);
|
||||
else
|
||||
snprintfcat(refined, sizeof(refined), "%08d", voltronic_qs_hex_status_char_to_binary(((unsigned char *)item->answer)[i]));
|
||||
|
||||
}
|
||||
|
||||
if (token != 10 || strlen(refined) != 46) {
|
||||
upsdebugx(2, "noncompliant reply: %s", refined);
|
||||
return -1;
|
||||
}
|
||||
|
||||
upsdebugx(4, "read: %s", refined);
|
||||
|
||||
/* e.g.: item->answer = "#6C01 35 6C01 35 03 519A 1312D0 E6 1E 00001001" */
|
||||
return snprintf(item->answer, sizeof(item->answer), "%s\r", refined);
|
||||
}
|
||||
|
||||
/* Transform the QS 'status' char into its binary form (as an int) */
|
||||
static int voltronic_qs_hex_status_char_to_binary(const unsigned char value)
|
||||
{
|
||||
unsigned char remainder = value;
|
||||
int ret = 0,
|
||||
power = 1;
|
||||
|
||||
while (remainder) {
|
||||
|
||||
if (remainder & 1)
|
||||
ret += power;
|
||||
|
||||
power *= 10;
|
||||
remainder >>= 1;
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* == Preprocess functions == */
|
||||
|
||||
/* Protocol used by the UPS */
|
||||
static int voltronic_qs_hex_protocol(item_t *item, char *value, size_t valuelen)
|
||||
{
|
||||
item_t *unskip;
|
||||
|
||||
if (strcasecmp(item->value, "P") && strcasecmp(item->value, "T") && strcasecmp(item->value, "V")) {
|
||||
upsdebugx(2, "%s: invalid protocol [%s]", __func__, item->value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(value, valuelen, item->dfl, item->value);
|
||||
|
||||
/* 'P' UPSes don't support 'T\r' command (battery test) -> leave test.battery.start.quick skipped */
|
||||
if (!strcasecmp(item->value, "P"))
|
||||
return 0;
|
||||
|
||||
/* Unskip test.battery.start.quick */
|
||||
unskip = find_nut_info("test.battery.start.quick", QX_FLAG_CMD, 0);
|
||||
|
||||
/* Don't know what happened */
|
||||
if (!unskip)
|
||||
return -1;
|
||||
|
||||
unskip->qxflags &= ~QX_FLAG_SKIP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Input/Output voltage */
|
||||
int voltronic_qs_hex_input_output_voltage(item_t *item, char *value, size_t valuelen)
|
||||
{
|
||||
int val;
|
||||
double ret;
|
||||
char *str_end, buf[SMALLBUF] = "";
|
||||
|
||||
if (strspn(item->value, "0123456789ABCDEFabcdef ") != strlen(item->value)) {
|
||||
upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
val = strtol(item->value, &str_end, 16) * strtol(str_end, NULL, 16) / 51;
|
||||
snprintf(buf, sizeof(buf), "%06x", val);
|
||||
|
||||
ret = strtol(buf + 4, NULL, 16) / 256.0;
|
||||
buf[4] = '\0';
|
||||
ret += strtol(buf, NULL, 16);
|
||||
|
||||
snprintf(value, valuelen, item->dfl, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Device load */
|
||||
int voltronic_qs_hex_load(item_t *item, char *value, size_t valuelen)
|
||||
{
|
||||
if (strspn(item->value, "0123456789ABCDEFabcdef") != strlen(item->value)) {
|
||||
upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(value, valuelen, item->dfl, strtol(item->value, NULL, 16));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Output frequency */
|
||||
int voltronic_qs_hex_frequency(item_t *item, char *value, size_t valuelen)
|
||||
{
|
||||
double val1, val2, ret;
|
||||
char *str_end;
|
||||
|
||||
if (strspn(item->value, "0123456789ABCDEFabcdef ") != strlen(item->value)) {
|
||||
upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
val1 = strtol(item->value, &str_end, 16);
|
||||
val2 = strtol(str_end, NULL, 16);
|
||||
|
||||
ret = val2 / val1;
|
||||
ret = ret > 99.9 ? 99.9 : ret;
|
||||
|
||||
snprintf(value, valuelen, item->dfl, ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Battery voltage */
|
||||
int voltronic_qs_hex_battery_voltage(item_t *item, char *value, size_t valuelen)
|
||||
{
|
||||
int val1, val2;
|
||||
char *str_end;
|
||||
|
||||
if (strspn(item->value, "0123456789ABCDEFabcdef ") != strlen(item->value)) {
|
||||
upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
val1 = strtol(item->value, &str_end, 16);
|
||||
val2 = strtol(str_end, NULL, 16);
|
||||
|
||||
snprintf(value, valuelen, item->dfl, (val1 * val2) / 510.0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* == Subdriver interface == */
|
||||
subdriver_t voltronic_qs_hex_subdriver = {
|
||||
VOLTRONIC_QS_HEX_VERSION,
|
||||
voltronic_qs_hex_claim,
|
||||
voltronic_qs_hex_qx2nut,
|
||||
voltronic_qs_hex_initups,
|
||||
NULL,
|
||||
blazer_makevartable_light,
|
||||
NULL,
|
||||
"N\r",
|
||||
#ifdef TESTING
|
||||
voltronic_qs_hex_testing,
|
||||
#endif /* TESTING */
|
||||
};
|
29
drivers/nutdrv_qx_voltronic-qs-hex.h
Normal file
29
drivers/nutdrv_qx_voltronic-qs-hex.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* nutdrv_qx_voltronic-qs-hex.h - Subdriver for Voltronic Power UPSes with QS-Hex protocol
|
||||
*
|
||||
* Copyright (C)
|
||||
* 2014 Daniele Pezzini <hyouko@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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 NUTDRV_QX_VOLTRONIC_QS_HEX_H
|
||||
#define NUTDRV_QX_VOLTRONIC_QS_HEX_H
|
||||
|
||||
#include "nutdrv_qx.h"
|
||||
|
||||
extern subdriver_t voltronic_qs_hex_subdriver;
|
||||
|
||||
#endif /* NUTDRV_QX_VOLTRONIC_QS_HEX_H */
|
|
@ -25,10 +25,11 @@
|
|||
|
||||
#include "nutdrv_qx_voltronic-qs.h"
|
||||
|
||||
#define VOLTRONIC_QS_VERSION "Voltronic-QS 0.01"
|
||||
#define VOLTRONIC_QS_VERSION "Voltronic-QS 0.04"
|
||||
|
||||
/* Support functions */
|
||||
static int voltronic_qs_claim(void);
|
||||
static void voltronic_qs_initups(void);
|
||||
|
||||
/* Preprocess functions */
|
||||
static int voltronic_qs_protocol(item_t *item, char *value, size_t valuelen);
|
||||
|
@ -61,7 +62,7 @@ static item_t voltronic_qs_qx2nut[] = {
|
|||
* 0
|
||||
*/
|
||||
|
||||
{ "ups.firmware.aux", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%s", QX_FLAG_STATIC, voltronic_qs_protocol },
|
||||
{ "ups.firmware.aux", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%s", QX_FLAG_STATIC, NULL, voltronic_qs_protocol },
|
||||
|
||||
/* Query UPS for status
|
||||
* > [QS\r]
|
||||
|
@ -70,23 +71,22 @@ static item_t voltronic_qs_qx2nut[] = {
|
|||
* 0 1 2 3 4
|
||||
*/
|
||||
|
||||
{ "input.voltage", 0, NULL, "QS\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "QS\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL },
|
||||
{ "output.voltage", 0, NULL, "QS\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL },
|
||||
{ "ups.load", 0, NULL, "QS\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL },
|
||||
{ "input.frequency", 0, NULL, "QS\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL },
|
||||
{ "battery.voltage", 0, NULL, "QS\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL },
|
||||
{ "ups.temperature", 0, NULL, "QS\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL },
|
||||
{ "input.voltage", 0, NULL, "QS\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "QS\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL },
|
||||
{ "output.voltage", 0, NULL, "QS\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL },
|
||||
{ "ups.load", 0, NULL, "QS\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL },
|
||||
{ "input.frequency", 0, NULL, "QS\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL },
|
||||
{ "battery.voltage", 0, NULL, "QS\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL },
|
||||
{ "ups.temperature", 0, NULL, "QS\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL },
|
||||
/* Status bits */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "QS\r", "", 47, '(', "", 41, 41, NULL, 0, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "QS\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.alarm", 0, NULL, "QS\r", "", 47, '(', "", 44, 44, NULL, 0, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "QS\r", "", 47, '(', "", 45, 45, "%s", 0, blazer_process_status_bits }, /* Beeper status */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "QS\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "QS\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "QS\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, blazer_process_status_bits }, /* Beeper status */
|
||||
|
||||
/* Query UPS for ratings
|
||||
* > [F\r]
|
||||
|
@ -95,40 +95,40 @@ static item_t voltronic_qs_qx2nut[] = {
|
|||
* 0 1 2
|
||||
*/
|
||||
|
||||
{ "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL },
|
||||
{ "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL },
|
||||
|
||||
/* Instant commands */
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
|
||||
/* Server-side settable vars */
|
||||
{ "ups.delay.start", ST_FLAG_RW, voltronic_qs_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, voltronic_qs_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.start", ST_FLAG_RW, voltronic_qs_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, voltronic_qs_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
|
||||
/* End of structure. */
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL }
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
/* == Testing table == */
|
||||
#ifdef TESTING
|
||||
static testing_t voltronic_qs_testing[] = {
|
||||
{ "QS\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r" },
|
||||
{ "F\r", "#230.0 000 024.0 50.0\r" },
|
||||
{ "M\r", "V\r" },
|
||||
{ "Q\r", "" },
|
||||
{ "C\r", "" },
|
||||
{ "S02R0005\r", "" },
|
||||
{ "S.5R0000\r", "" },
|
||||
{ "T\r", "" },
|
||||
{ "QS\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 },
|
||||
{ "F\r", "#230.0 000 024.0 50.0\r", -1 },
|
||||
{ "M\r", "V\r", -1 },
|
||||
{ "Q\r", "", -1 },
|
||||
{ "C\r", "", -1 },
|
||||
{ "S02R0005\r", "", -1 },
|
||||
{ "S.5R0000\r", "", -1 },
|
||||
{ "T\r", "", -1 },
|
||||
{ NULL }
|
||||
};
|
||||
#endif /* TESTING */
|
||||
|
@ -139,7 +139,6 @@ static testing_t voltronic_qs_testing[] = {
|
|||
/* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */
|
||||
static int voltronic_qs_claim(void)
|
||||
{
|
||||
|
||||
/* We need at least M and QS to run this subdriver */
|
||||
|
||||
/* UPS Protocol */
|
||||
|
@ -178,7 +177,12 @@ static int voltronic_qs_claim(void)
|
|||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Subdriver-specific initups */
|
||||
static void voltronic_qs_initups(void)
|
||||
{
|
||||
blazer_initups_light(voltronic_qs_qx2nut);
|
||||
}
|
||||
|
||||
|
||||
|
@ -203,9 +207,9 @@ subdriver_t voltronic_qs_subdriver = {
|
|||
VOLTRONIC_QS_VERSION,
|
||||
voltronic_qs_claim,
|
||||
voltronic_qs_qx2nut,
|
||||
voltronic_qs_initups,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
blazer_makevartable_light,
|
||||
"ACK",
|
||||
"(NAK\r",
|
||||
#ifdef TESTING
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include "nutdrv_qx_zinto.h"
|
||||
|
||||
#define ZINTO_VERSION "Zinto 0.01"
|
||||
#define ZINTO_VERSION "Zinto 0.04"
|
||||
|
||||
/* qx2nut lookup table */
|
||||
static item_t zinto_qx2nut[] = {
|
||||
|
@ -37,23 +37,22 @@ static item_t zinto_qx2nut[] = {
|
|||
* 0 1 2 3 4
|
||||
*/
|
||||
|
||||
{ "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL },
|
||||
{ "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL },
|
||||
{ "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL },
|
||||
{ "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL },
|
||||
{ "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL },
|
||||
{ "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL },
|
||||
{ "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL },
|
||||
{ "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL },
|
||||
{ "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL },
|
||||
{ "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL },
|
||||
{ "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL },
|
||||
{ "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL },
|
||||
{ "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL },
|
||||
/* Status bits */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, 0, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, blazer_process_status_bits }, /* Beeper status */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Battery Low */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */
|
||||
{ "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, blazer_process_status_bits }, /* UPS Failed */
|
||||
{ "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, blazer_process_status_bits }, /* UPS Type */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Test in Progress */
|
||||
{ "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, blazer_process_status_bits }, /* Shutdown Active */
|
||||
{ "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, blazer_process_status_bits }, /* Beeper status */
|
||||
|
||||
/*
|
||||
* > [F\r]
|
||||
|
@ -62,10 +61,10 @@ static item_t zinto_qx2nut[] = {
|
|||
* 0 1 2
|
||||
*/
|
||||
|
||||
{ "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL },
|
||||
{ "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL },
|
||||
{ "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL },
|
||||
{ "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL },
|
||||
|
||||
/*
|
||||
* > [FW?\r]
|
||||
|
@ -74,45 +73,45 @@ static item_t zinto_qx2nut[] = {
|
|||
* 0 1 2 3
|
||||
*/
|
||||
|
||||
{ "device.mfr", 0, NULL, "FW?\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "device.model", 0, NULL, "FW?\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "ups.firmware", 0, NULL, "FW?\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL },
|
||||
{ "device.mfr", 0, NULL, "FW?\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
{ "device.model", 0, NULL, "FW?\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
{ "ups.firmware", 0, NULL, "FW?\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL },
|
||||
|
||||
/* Instant commands */
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, blazer_process_command },
|
||||
{ "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL },
|
||||
{ "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, blazer_process_command },
|
||||
{ "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
{ "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL },
|
||||
|
||||
/* Server-side settable vars */
|
||||
{ "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, blazer_process_setvar },
|
||||
{ "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
{ "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, blazer_process_setvar },
|
||||
|
||||
/* End of structure. */
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL }
|
||||
{ NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
/* Testing table */
|
||||
#ifdef TESTING
|
||||
static testing_t zinto_testing[] = {
|
||||
{ "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r" },
|
||||
{ "F\r", "#230.0 000 024.0 50.0\r" },
|
||||
{ "FW?\r", "#NOT_A_LIVE_UPS TESTING TESTING \r" },
|
||||
{ "Q\r", "" },
|
||||
{ "S03\r", "" },
|
||||
{ "C\r", "" },
|
||||
{ "S02R0005\r", "" },
|
||||
{ "S.5R0000\r", "" },
|
||||
{ "T04\r", "" },
|
||||
{ "TL\r", "" },
|
||||
{ "T\r", "" },
|
||||
{ "CT\r", "" },
|
||||
{ "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 },
|
||||
{ "F\r", "#230.0 000 024.0 50.0\r", -1 },
|
||||
{ "FW?\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 },
|
||||
{ "Q\r", "", -1 },
|
||||
{ "S03\r", "", -1 },
|
||||
{ "C\r", "", -1 },
|
||||
{ "S02R0005\r", "", -1 },
|
||||
{ "S.5R0000\r", "", -1 },
|
||||
{ "T04\r", "", -1 },
|
||||
{ "TL\r", "", -1 },
|
||||
{ "T\r", "", -1 },
|
||||
{ "CT\r", "", -1 },
|
||||
{ NULL }
|
||||
};
|
||||
#endif /* TESTING */
|
||||
|
@ -120,9 +119,7 @@ static testing_t zinto_testing[] = {
|
|||
/* Subdriver-specific initups */
|
||||
static void zinto_initups(void)
|
||||
{
|
||||
|
||||
blazer_initups(zinto_qx2nut);
|
||||
|
||||
}
|
||||
|
||||
/* Subdriver interface */
|
||||
|
|
|
@ -26,17 +26,53 @@
|
|||
#include "main.h" /* for getval() */
|
||||
#include "usb-common.h"
|
||||
|
||||
#define OPENUPS_HID_VERSION "openUPS HID 0.1"
|
||||
#define OPENUPS_HID_VERSION "openUPS HID 0.4"
|
||||
|
||||
/* Minibox */
|
||||
#define OPENUPS_VENDORID 0x04d8
|
||||
|
||||
/* constants for converting HID read values to real values */
|
||||
static const double vin_scale_d004 = 0.03545 * 100;
|
||||
static const double vout_scale_d004 = 0.02571 * 100;
|
||||
/* static const double vbat_scale = 0.00857 * 100; */
|
||||
static const double ccharge_scale_d004 = 0.8274 / 10;
|
||||
static const double cdischarge_scale_d004 = 16.113 / 10;
|
||||
|
||||
static double vin_scale = 1;
|
||||
static double vout_scale= 1;
|
||||
static double ccharge_scale = 1;
|
||||
static double cdischarge_scale = 1;
|
||||
|
||||
static char openups_scratch_buf[20];
|
||||
|
||||
static void *get_voltage_multiplier(USBDevice_t *device)
|
||||
{
|
||||
|
||||
switch(device->ProductID) {
|
||||
case 0xd004:
|
||||
vin_scale = vin_scale_d004;
|
||||
vout_scale= vout_scale_d004;
|
||||
ccharge_scale= ccharge_scale_d004;
|
||||
cdischarge_scale= cdischarge_scale_d004;
|
||||
break;
|
||||
case 0xd005:
|
||||
vin_scale = 0.1;
|
||||
vout_scale = 0.1;
|
||||
ccharge_scale = 0.1; /* unverified */
|
||||
cdischarge_scale = 0.1; /* unverified */
|
||||
break;
|
||||
}
|
||||
|
||||
upsdebugx(1, "vin_scale = %g; vout_scale = %g\n", vin_scale, vout_scale);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* USB IDs device table */
|
||||
static usb_device_id_t openups_usb_device_table[] = {
|
||||
static /* const */ usb_device_id_t openups_usb_device_table[] = {
|
||||
/* openUPS Intelligent UPS (minimum required firmware 1.4) */
|
||||
{USB_DEVICE(OPENUPS_VENDORID, 0xd004), NULL},
|
||||
{USB_DEVICE(OPENUPS_VENDORID, 0xd004), get_voltage_multiplier},
|
||||
{USB_DEVICE(OPENUPS_VENDORID, 0xd005), get_voltage_multiplier},
|
||||
|
||||
/* Terminating entry */
|
||||
{-1, -1, NULL}
|
||||
|
@ -45,7 +81,7 @@ static usb_device_id_t openups_usb_device_table[] = {
|
|||
/* Thermistor table used for temperature lookups
|
||||
* taken from the windows monitoring application
|
||||
*/
|
||||
static unsigned int therm_tbl[] =
|
||||
static const unsigned int therm_tbl[] =
|
||||
{
|
||||
(unsigned int)0x31,
|
||||
(unsigned int)0x40,
|
||||
|
@ -83,7 +119,7 @@ static unsigned int therm_tbl[] =
|
|||
(unsigned int)0x3CC
|
||||
};
|
||||
|
||||
static unsigned int therm_tbl_size = sizeof(therm_tbl)/sizeof(therm_tbl[0]);
|
||||
static const unsigned int therm_tbl_size = sizeof(therm_tbl)/sizeof(therm_tbl[0]);
|
||||
|
||||
static const char *openups_charging_fun(double value);
|
||||
static const char *openups_discharging_fun(double value);
|
||||
|
@ -225,7 +261,7 @@ static const char *openups_temperature_fun(double value)
|
|||
unsigned int d1 = therm_tbl[pos];
|
||||
unsigned int d2 = therm_tbl[pos + 1];
|
||||
|
||||
float temp = (float) (thermistor - d1) * (t2 - t1) / (d2 - d1) + t1;
|
||||
double temp = (double) (thermistor - d1) * (t2 - t1) / (d2 - d1) + t1;
|
||||
snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.2f", temp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,12 +26,7 @@
|
|||
|
||||
#include "usbhid-ups.h"
|
||||
|
||||
/* constants for converting HID read values to real values */
|
||||
static const float vin_scale = 0.03545 * 100;
|
||||
static const float vout_scale = 0.02571 * 100;
|
||||
static const float vbat_scale = 0.00857 * 100;
|
||||
static const float ccharge_scale = 0.8274 / 10;
|
||||
static const float cdischarge_scale = 16.113 / 10;
|
||||
/* Don't put non-extern definitions here - this file gets included by usbhid-ups.c */
|
||||
|
||||
extern subdriver_t openups_subdriver;
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "powercom-hid.h"
|
||||
#include "usb-common.h"
|
||||
|
||||
#define POWERCOM_HID_VERSION "PowerCOM HID 0.4"
|
||||
#define POWERCOM_HID_VERSION "PowerCOM HID 0.5"
|
||||
/* FIXME: experimental flag to be put in upsdrv_info */
|
||||
|
||||
/* PowerCOM */
|
||||
|
@ -45,6 +45,7 @@ static usb_device_id_t powercom_usb_device_table[] = {
|
|||
{ USB_DEVICE(POWERCOM_VENDORID, 0x00a6), NULL },
|
||||
/* PowerCOM Vanguard and BNT-xxxAP */
|
||||
{ USB_DEVICE(POWERCOM_VENDORID, 0x0004), NULL },
|
||||
{ USB_DEVICE(POWERCOM_VENDORID, 0x0001), NULL },
|
||||
|
||||
/* Terminating entry */
|
||||
{ -1, -1, NULL }
|
||||
|
@ -130,12 +131,160 @@ static info_lkp_t powercom_beeper_info[] = {
|
|||
{ 0, NULL, NULL }
|
||||
};
|
||||
|
||||
static const char *powercom_voltage_conversion_fun(double value)
|
||||
{
|
||||
static char buf[20];
|
||||
snprintf(buf, sizeof(buf), "%0.0f", value * 4);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static info_lkp_t powercom_voltage_conversion[] = {
|
||||
{ 0, NULL, powercom_voltage_conversion_fun }
|
||||
};
|
||||
|
||||
static const char *powercom_upsfail_conversion_fun(double value)
|
||||
{
|
||||
if ((long)value & 0x0001) {
|
||||
return "fanfail";
|
||||
} else {
|
||||
return "!fanfail";
|
||||
}
|
||||
}
|
||||
|
||||
static info_lkp_t powercom_upsfail_conversion[] = {
|
||||
{ 0, NULL, powercom_upsfail_conversion_fun }
|
||||
};
|
||||
|
||||
static const char *powercom_replacebatt_conversion_fun(double value)
|
||||
{
|
||||
if ((long)value & 0x0002) {
|
||||
return "replacebatt";
|
||||
} else {
|
||||
return "!replacebatt";
|
||||
}
|
||||
}
|
||||
|
||||
static info_lkp_t powercom_replacebatt_conversion[] = {
|
||||
{ 0, NULL, powercom_replacebatt_conversion_fun }
|
||||
};
|
||||
|
||||
static const char *powercom_test_conversion_fun(double value)
|
||||
{
|
||||
if ((long)value & 0x0004) {
|
||||
return "cal";
|
||||
} else {
|
||||
return "!cal";
|
||||
}
|
||||
}
|
||||
|
||||
static info_lkp_t powercom_test_conversion[] = {
|
||||
{ 0, NULL, powercom_test_conversion_fun }
|
||||
};
|
||||
|
||||
static const char *powercom_shutdownimm_conversion_fun(double value)
|
||||
{
|
||||
if ((long)value & 0x0010) {
|
||||
return "shutdownimm";
|
||||
} else {
|
||||
return "!shutdownimm";
|
||||
}
|
||||
}
|
||||
|
||||
static info_lkp_t powercom_shutdownimm_conversion[] = {
|
||||
{ 0, NULL, powercom_shutdownimm_conversion_fun }
|
||||
};
|
||||
|
||||
static const char *powercom_online_conversion_fun(double value)
|
||||
{
|
||||
if ((long)value & 0x0001) {
|
||||
return "!online";
|
||||
} else {
|
||||
return "online";
|
||||
}
|
||||
}
|
||||
|
||||
static info_lkp_t powercom_online_conversion[] = {
|
||||
{ 0, NULL, powercom_online_conversion_fun }
|
||||
};
|
||||
|
||||
static const char *powercom_lowbatt_conversion_fun(double value)
|
||||
{
|
||||
if ((long)value & 0x0002) {
|
||||
return "lowbatt";
|
||||
} else {
|
||||
return "!lowbatt";
|
||||
}
|
||||
}
|
||||
|
||||
static info_lkp_t powercom_lowbatt_conversion[] = {
|
||||
{ 0, NULL, powercom_lowbatt_conversion_fun }
|
||||
};
|
||||
|
||||
static const char *powercom_trim_conversion_fun(double value)
|
||||
{
|
||||
if (((long)value & 0x0018) == 0x0008) {
|
||||
return "trim";
|
||||
} else {
|
||||
return "!trim";
|
||||
}
|
||||
}
|
||||
|
||||
static info_lkp_t powercom_trim_conversion[] = {
|
||||
{ 0, NULL, powercom_trim_conversion_fun }
|
||||
};
|
||||
|
||||
static const char *powercom_boost_conversion_fun(double value)
|
||||
{
|
||||
if (((long)value & 0x0018) == 0x0018) {
|
||||
return "boost";
|
||||
} else {
|
||||
return "!boost";
|
||||
}
|
||||
}
|
||||
|
||||
static info_lkp_t powercom_boost_conversion[] = {
|
||||
{ 0, NULL, powercom_boost_conversion_fun }
|
||||
};
|
||||
|
||||
static const char *powercom_overload_conversion_fun(double value)
|
||||
{
|
||||
if ((long)value & 0x0020) {
|
||||
return "overload";
|
||||
} else {
|
||||
return "!overload";
|
||||
}
|
||||
}
|
||||
|
||||
static info_lkp_t powercom_overload_conversion[] = {
|
||||
{ 0, NULL, powercom_overload_conversion_fun }
|
||||
};
|
||||
|
||||
/* --------------------------------------------------------------- */
|
||||
/* Vendor-specific usage table */
|
||||
/* --------------------------------------------------------------- */
|
||||
|
||||
/* POWERCOM usage table */
|
||||
static usage_lkp_t powercom_usage_lkp[] = {
|
||||
{ "PowercomUPS", 0x00020004 },
|
||||
{ "PowercomBatterySystem", 0x00020010 },
|
||||
{ "PowercomPowerConverter", 0x00020016 },
|
||||
{ "PowercomInput", 0x0002001a },
|
||||
{ "PowercomOutput", 0x0002001c },
|
||||
{ "PowercomVoltage", 0x00020030 },
|
||||
{ "PowercomFrequency", 0x00020032 },
|
||||
{ "PowercomPercentLoad", 0x00020035 },
|
||||
{ "PowercomTemperature", 0x00020036 },
|
||||
{ "PowercomDelayBeforeStartup", 0x00020056 },
|
||||
{ "PowercomDelayBeforeShutdown", 0x00020057 },
|
||||
{ "PowercomTest", 0x00020058 },
|
||||
{ "PowercomShutdownRequested", 0x00020068 },
|
||||
{ "PowercomInternalChargeController", 0x00020081 },
|
||||
{ "PowercomPrimaryBatterySupport", 0x00020082 },
|
||||
{ "PowercomDesignCapacity", 0x00020083 },
|
||||
{ "PowercomSpecificationInfo", 0x00020084 },
|
||||
{ "PowercomManufacturerDate", 0x00020085 },
|
||||
{ "PowercomSerialNumber", 0x00020086 },
|
||||
{ "PowercomManufacturerName", 0x00020087 },
|
||||
{ "POWERCOM1", 0x0084002f },
|
||||
{ "POWERCOM2", 0xff860060 },
|
||||
{ "POWERCOM3", 0xff860080 },
|
||||
|
@ -268,6 +417,56 @@ static hid_info_t powercom_hid2nut[] = {
|
|||
{ "shutdown.return", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_shutdown_info },
|
||||
{ "shutdown.stayoff", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_stayoff_info },
|
||||
|
||||
{ "ups.serial", 0, 0, "PowercomUPS.PowercomSerialNumber", NULL, "%s", 0, stringid_conversion },
|
||||
{ "ups.mfr", 0, 0, "PowercomUPS.PowercomManufacturerName", NULL, "%s", 0, stringid_conversion },
|
||||
/* { "UPS.DesignCapacity", 0, 0, "PowercomUPS.PowercomDesignCapacity", NULL, "%.0f", 0, NULL }, is always 255 */
|
||||
{ "ups.mfr.date", 0, 0, "PowercomUPS.PowercomManufacturerDate", NULL, "%s", 0, date_conversion },
|
||||
{ "battery.temperature", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomTemperature", NULL, "%.0f", 0, NULL },
|
||||
{ "battery.charge", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomVoltage", NULL, "%.0f", 0, NULL },
|
||||
/* { "UPS.BatterySystem.SpecificationInfo", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomSpecificationInfo", NULL, "%.0f", 0, NULL }, */
|
||||
{ "input.frequency", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomInput.PowercomFrequency", NULL, "%.0f", 0, NULL },
|
||||
{ "input.voltage", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomInput.PowercomVoltage", NULL, "%.0f", 0, powercom_voltage_conversion },
|
||||
{ "output.voltage", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomVoltage", NULL, "%.0f", 0, powercom_voltage_conversion },
|
||||
{ "ups.load", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPercentLoad", NULL, "%.0f", 0, NULL },
|
||||
/* flags: 4 - Testing, 8 - Probably mute (it's set on battery with muted beeper and sometimes during ups test)
|
||||
* bit 0 UPS fault (1 = FAILT)
|
||||
* bit 1 Battery status (1 = BAD, 0 = NORMAL)
|
||||
* bit 2 Test mode (1 = TEST, 0 = NORMAL)
|
||||
* bit 3 X
|
||||
* bit 4 Pre-SD count mode (1 = ACTIVE)
|
||||
* bit 5 Schedule count mode (1 = ACTIVE)
|
||||
* bit 6 Disable NO LOAD SHUTDOWN (1 = ACTIVE)
|
||||
* bit 7 0
|
||||
*/
|
||||
/* { "UPS.PowerConverter.Output.InternalChargeController", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, "%.0f", 0, NULL }, */
|
||||
{ "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_upsfail_conversion },
|
||||
{ "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_replacebatt_conversion },
|
||||
{ "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_test_conversion },
|
||||
{ "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_shutdownimm_conversion },
|
||||
/* flags: 1 - On battery, 2 - Low Battery, 8 - Trim, 8+16 - Boost
|
||||
* bit 0 is line fail (1 = INV, 0 = LINE)
|
||||
* bit 1 is low battery (1 = BAT_ LOW, 0 = NORMAL)
|
||||
* bit 2 X
|
||||
* bit 3 AVR status (1 = AVR, 0 = NO_AVR)
|
||||
* bit 4 AVR mode (1 = BOOST, 0 = BUCK)
|
||||
* bit 5 Load status (1 = OVER LOAD, 0 = NORMAL)
|
||||
* bit 6 X
|
||||
* bit 7 SD mode display
|
||||
*/
|
||||
/* { "UPS.PowerConverter.Output.PrimaryBatterySupport", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, "%.0f", 0, NULL }, */
|
||||
{ "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_online_conversion },
|
||||
/* Low battery status may not work */
|
||||
{ "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_lowbatt_conversion },
|
||||
{ "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_trim_conversion },
|
||||
{ "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_boost_conversion },
|
||||
{ "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_overload_conversion },
|
||||
{ "output.frequency", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomFrequency", NULL, "%.0f", 0, NULL },
|
||||
{ "ups.test.result", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomTest", NULL, "%s", 0, test_read_info },
|
||||
/* { "UPS.PowerConverter.ShutdownRequested", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomShutdownRequested", NULL, "%.0f", 0, NULL }, */
|
||||
{ "ups.delay.shutdown", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeShutdown", NULL, "%.0f", 0, NULL },
|
||||
{ "ups.delay.start", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeStartup", NULL, "%.0f", 0, NULL },
|
||||
{ "load.off", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeShutdown", NULL, "0", HU_TYPE_CMD, NULL },
|
||||
|
||||
/* end of structure. */
|
||||
{ NULL, 0, 0, NULL, NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
@ -307,6 +506,10 @@ static int powercom_claim(HIDDevice_t *hd)
|
|||
return 0;
|
||||
|
||||
case SUPPORTED:
|
||||
if (hd->ProductID == 0x0001) {
|
||||
interrupt_only = 1;
|
||||
interrupt_size = 8;
|
||||
}
|
||||
return 1;
|
||||
|
||||
case NOT_SUPPORTED:
|
||||
|
|
|
@ -64,6 +64,11 @@
|
|||
* - Added support for OptiUPS VS 575C
|
||||
* This probably also works with others, but I don't have their model numbers.
|
||||
*
|
||||
* rev 0.15: VSE NN <metanoite@rambler.ru>
|
||||
* - Fixed UPS type assignment for Powercom Imperial USB series manufactured since 2009.
|
||||
*
|
||||
* Tested on: IMP-625AP
|
||||
*
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
@ -908,17 +913,24 @@ void upsdrv_initups(void)
|
|||
if (!ups_getinfo()) return;
|
||||
/* Give "BNT-other" a chance! */
|
||||
if (raw_data[MODELNAME]==0x42 || raw_data[MODELNAME]==0x4B || raw_data[MODELNAME]==0x4F){
|
||||
model=BNTmodels[raw_data[MODELNUMBER]/16];
|
||||
if (!strcmp(types[type].name, "BNT-other"))
|
||||
types[type].name="BNT-other";
|
||||
else if (raw_data[MODELNAME]==0x42)
|
||||
types[type].name="BNT";
|
||||
else if (raw_data[MODELNAME]==0x4B){
|
||||
types[type].name="KIN";
|
||||
model=KINmodels[raw_data[MODELNUMBER]/16];
|
||||
} else if (raw_data[MODELNAME]==0x4F){
|
||||
types[type].name="OPTI";
|
||||
model=OPTImodels[raw_data[MODELNUMBER]/16];
|
||||
/* Give "IMP" a chance also! */
|
||||
if (raw_data[UPSVERSION]==0xFF){
|
||||
types[type].name="IMP";
|
||||
model=IMPmodels[raw_data[MODELNUMBER]/16];
|
||||
}
|
||||
else {
|
||||
model=BNTmodels[raw_data[MODELNUMBER]/16];
|
||||
if (!strcmp(types[type].name, "BNT-other"))
|
||||
types[type].name="BNT-other";
|
||||
else if (raw_data[MODELNAME]==0x42)
|
||||
types[type].name="BNT";
|
||||
else if (raw_data[MODELNAME]==0x4B){
|
||||
types[type].name="KIN";
|
||||
model=KINmodels[raw_data[MODELNUMBER]/16];
|
||||
} else if (raw_data[MODELNAME]==0x4F){
|
||||
types[type].name="OPTI";
|
||||
model=OPTImodels[raw_data[MODELNUMBER]/16];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (raw_data[UPSVERSION]==0xFF){
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
/* powerware-mib.c - data to monitor Powerware UPS with NUT
|
||||
* (using MIBs described in stdupsv1.mib and Xups.mib)
|
||||
*
|
||||
* Copyright (C) 2005-2006
|
||||
* Olli Savia <ops@iki.fi>
|
||||
* Niels Baggesen <niels@baggesen.net>
|
||||
* Copyright (C)
|
||||
* 2005-2006 Olli Savia <ops@iki.fi>
|
||||
* 2005-2006 Niels Baggesen <niels@baggesen.net>
|
||||
* 2015 Arnaud Quette <ArnaudQuette@Eaton.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -24,7 +25,7 @@
|
|||
|
||||
#include "powerware-mib.h"
|
||||
|
||||
#define PW_MIB_VERSION "0.6.2"
|
||||
#define PW_MIB_VERSION "0.7"
|
||||
|
||||
/* TODO: more sysOID and MIBs support:
|
||||
*
|
||||
|
@ -134,6 +135,7 @@ static info_lkp_t pw_mode_info[] = {
|
|||
{ 0, "NULL" }
|
||||
};
|
||||
|
||||
/* Legacy implementation */
|
||||
static info_lkp_t pw_battery_abm_status[] = {
|
||||
{ 1, "CHRG" },
|
||||
{ 2, "DISCHRG" },
|
||||
|
@ -143,13 +145,13 @@ static info_lkp_t pw_battery_abm_status[] = {
|
|||
{ 0, "NULL" }
|
||||
} ;
|
||||
|
||||
static info_lkp_t pw_batt_info[] = {
|
||||
{ 1, "" },
|
||||
{ 2, "" },
|
||||
{ 3, "Battery Floating" }, /* battery floating - can we put that stuff somewhere so one actually access that information? */
|
||||
{ 4, "Battery Resting" }, /* battery resting - could come handy if support asks what
|
||||
state the batteries are in... pw_batt_info doesn't get used */
|
||||
{ 5, "unknown" }, /* unknown */
|
||||
static info_lkp_t eaton_abm_status_info[] = {
|
||||
{ 1, "charging" },
|
||||
{ 2, "discharging" },
|
||||
{ 3, "floating" },
|
||||
{ 4, "resting" },
|
||||
{ 5, "unknown" }, /* Undefined - ABM is not activated */
|
||||
{ 6, "disabled" }, /* ABM Charger Disabled */
|
||||
{ 0, "NULL" }
|
||||
};
|
||||
|
||||
|
@ -200,8 +202,8 @@ static snmp_info_t pw_mib[] = {
|
|||
0, NULL },
|
||||
{ "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATTEST_RES, "",
|
||||
0, &pw_batt_test_info[0] },
|
||||
{ "vendor.specific.abmstatus", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATT_STATUS, "",
|
||||
SU_STATUS_BATT, &pw_batt_info[0] },
|
||||
{ "battery.charger.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATT_STATUS, "",
|
||||
SU_STATUS_BATT, &eaton_abm_status_info[0] },
|
||||
|
||||
/* Battery page */
|
||||
{ "battery.charge", 0, 1.0, PW_OID_BATT_CHARGE, "",
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#include "riello.h"
|
||||
|
||||
#define DRIVER_NAME "Riello serial driver"
|
||||
#define DRIVER_VERSION "0.02"
|
||||
#define DRIVER_VERSION "0.03"
|
||||
|
||||
/* driver description structure */
|
||||
upsdrv_info_t upsdrv_info = {
|
||||
|
@ -188,12 +188,13 @@ int get_ups_nominal()
|
|||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Get nominal Ko: bad CRC or Checksum");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* mandatory */
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Get nominal Ko: command not supported");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Get nominal Ok: received byte %u", buf_ptr_length);
|
||||
|
@ -227,12 +228,13 @@ int get_ups_status()
|
|||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Get status Ko: bad CRC or Checksum");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* mandatory */
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Get status Ko: command not supported");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Get status Ok: received byte %u", buf_ptr_length);
|
||||
|
@ -259,12 +261,13 @@ int get_ups_extended()
|
|||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Get extended Ko: bad CRC or Checksum");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* optonal */
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Get extended Ko: command not supported");
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Get extended Ok: received byte %u", buf_ptr_length);
|
||||
|
@ -291,12 +294,13 @@ int get_ups_statuscode()
|
|||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Get statuscode Ko: bad CRC or Checksum");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* optional */
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Get statuscode Ko: command not supported");
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Get statuscode Ok: received byte %u", buf_ptr_length);
|
||||
|
@ -333,12 +337,13 @@ int get_ups_sentr()
|
|||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Get sentry Ko: bad CRC or Checksum");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* mandatory */
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Get sentry Ko: command not supported");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Get sentry Ok: received byte %u", buf_ptr_length);
|
||||
|
@ -359,68 +364,74 @@ int riello_instcmd(const char *cmdname, const char *extra)
|
|||
delay = 0;
|
||||
riello_init_serial();
|
||||
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
length = riello_prepare_cs(bufOut, gpser_error_control, delay);
|
||||
else
|
||||
length = riello_prepare_shutsentr(bufOut, delay);
|
||||
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0)
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0) {
|
||||
upsdebugx (3, "Command load.off communication error");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
else {
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.off Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.off Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.off Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.off Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command load.off Ok");
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
if (!strcasecmp(cmdname, "load.off.delay")) {
|
||||
delay_char = dstate_getinfo("ups.delay.shutdown");
|
||||
delay_char = dstate_getinfo("ups.delay.shutdown");
|
||||
delay = atoi(delay_char);
|
||||
riello_init_serial();
|
||||
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
length = riello_prepare_cs(bufOut, gpser_error_control, delay);
|
||||
else
|
||||
length = riello_prepare_shutsentr(bufOut, delay);
|
||||
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0)
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0) {
|
||||
upsdebugx (3, "Command load.off delay communication error");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
else {
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.off.delay Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.off.delay Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.off.delay Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.off.delay Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command load.off delay Ok");
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
if (!strcasecmp(cmdname, "load.on")) {
|
||||
delay = 0;
|
||||
riello_init_serial();
|
||||
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
length = riello_prepare_cr(bufOut, gpser_error_control, delay);
|
||||
else {
|
||||
length = riello_prepare_setrebsentr(bufOut, delay);
|
||||
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0)
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0) {
|
||||
upsdebugx (3, "Command load.on communication error");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
|
@ -436,169 +447,184 @@ int riello_instcmd(const char *cmdname, const char *extra)
|
|||
length = riello_prepare_rebsentr(bufOut, delay);
|
||||
}
|
||||
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0)
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0) {
|
||||
upsdebugx (3, "Command load.on communication error");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
else {
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.on Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.on Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.on Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.on Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command load.on Ok");
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
if (!strcasecmp(cmdname, "load.on.delay")) {
|
||||
delay_char = dstate_getinfo("ups.delay.reboot");
|
||||
delay_char = dstate_getinfo("ups.delay.reboot");
|
||||
delay = atoi(delay_char);
|
||||
riello_init_serial();
|
||||
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
length = riello_prepare_cr(bufOut, gpser_error_control, delay);
|
||||
else {
|
||||
length = riello_prepare_setrebsentr(bufOut, delay);
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0)
|
||||
length = riello_prepare_setrebsentr(bufOut, delay);
|
||||
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0) {
|
||||
upsdebugx (3, "Command load.on delay communication error");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.on Ko: bad CRC or Checksum");
|
||||
upsdebugx (3, "Command load.on delay Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.on Ko: command not supported");
|
||||
upsdebugx (3, "Command load.on delay Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
length = riello_prepare_rebsentr(bufOut, delay);
|
||||
}
|
||||
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0)
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0) {
|
||||
upsdebugx (3, "Command load.on delay communication error");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
else {
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.on.delay Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.on.delay Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.on.delay Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.on.delay Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command load.on delay Ok");
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!strcasecmp(cmdname, "shutdown.return")) {
|
||||
delay_char = dstate_getinfo("ups.delay.shutdown");
|
||||
delay_char = dstate_getinfo("ups.delay.shutdown");
|
||||
delay = atoi(delay_char);
|
||||
riello_init_serial();
|
||||
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
length = riello_prepare_cs(bufOut, gpser_error_control, delay);
|
||||
else
|
||||
length = riello_prepare_shutsentr(bufOut, delay);
|
||||
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0)
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0) {
|
||||
upsdebugx (3, "Command shutdown.return communication error");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
else {
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command shutdown.return Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command shutdown.return Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command shutdown.return Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command shutdown.return Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command shutdown.return Ok");
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcasecmp(cmdname, "shutdown.stop")) {
|
||||
riello_init_serial();
|
||||
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
length = riello_prepare_cd(bufOut, gpser_error_control);
|
||||
else
|
||||
length = riello_prepare_cancelsentr(bufOut);
|
||||
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0)
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0) {
|
||||
upsdebugx (3, "Command shutdown.stop communication error");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
else {
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command shutdown.stop Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command shutdown.stop Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command shutdown.stop Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command shutdown.stop Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command shutdown.stop Ok");
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
if (!strcasecmp(cmdname, "test.panel.start")) {
|
||||
riello_init_serial();
|
||||
length = riello_prepare_tp(bufOut, gpser_error_control);
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0)
|
||||
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0) {
|
||||
upsdebugx (3, "Command test.panel.start communication error");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
else {
|
||||
riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command panel.start Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command panel.start Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command test.panel.start Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command test.panel.start Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command test.panel.start Ok");
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
if (!strcasecmp(cmdname, "test.battery.start")) {
|
||||
riello_init_serial();
|
||||
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
length = riello_prepare_tb(bufOut, gpser_error_control);
|
||||
else
|
||||
length = riello_prepare_tbsentr(bufOut);
|
||||
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0)
|
||||
if (ser_send_buf(upsfd, bufOut, length) == 0) {
|
||||
upsdebugx (3, "Command test.battery.start communication error");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
else {
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command battery.start Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command battery.start Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
riello_serialcomm(&bufIn[0], typeRielloProtocol);
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command battery.start Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command battery.start Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command test.battery.start Ok");
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
|
||||
|
@ -737,37 +763,40 @@ void upsdrv_updateinfo(void)
|
|||
{
|
||||
uint8_t getextendedOK;
|
||||
static int countlost = 0;
|
||||
int stat;
|
||||
|
||||
if (countlost < COUNTLOST)
|
||||
upsdebugx(1, "countlost %d",countlost);
|
||||
|
||||
if (countlost > 0){
|
||||
upsdebugx(1, "Communication with UPS is lost: status read failed!");
|
||||
else if (countlost == COUNTLOST)
|
||||
upslogx(LOG_WARNING, "Communication with UPS is lost: status read failed!");
|
||||
else
|
||||
dstate_datastale();
|
||||
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER) {
|
||||
if (get_ups_status() != 0) {
|
||||
if (countlost <= COUNTLOST)
|
||||
countlost++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (countlost == COUNTLOST) {
|
||||
dstate_datastale();
|
||||
upslogx(LOG_WARNING, "Communication with UPS is lost: status read failed!");
|
||||
}
|
||||
}
|
||||
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER)
|
||||
stat = get_ups_status();
|
||||
else
|
||||
stat = get_ups_sentr();
|
||||
|
||||
if (stat < 0) {
|
||||
if (countlost < COUNTLOST)
|
||||
countlost++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeRielloProtocol == DEV_RIELLOGPSER) {
|
||||
if (get_ups_extended() == 0)
|
||||
getextendedOK = 1;
|
||||
else
|
||||
getextendedOK = 0;
|
||||
}
|
||||
else {
|
||||
if (get_ups_sentr() != 0) {
|
||||
if (countlost <= COUNTLOST)
|
||||
countlost++;
|
||||
return;
|
||||
}
|
||||
else
|
||||
getextendedOK = 1;
|
||||
}
|
||||
else
|
||||
getextendedOK = 1;
|
||||
|
||||
if (countlost > COUNTLOST)
|
||||
if (countlost == COUNTLOST)
|
||||
upslogx(LOG_NOTICE, "Communication with UPS is re-established!");
|
||||
|
||||
dstate_setinfo("input.frequency", "%.2f", DevData.Finp/10.0);
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
#include "riello.h"
|
||||
|
||||
#define DRIVER_NAME "Riello USB driver"
|
||||
#define DRIVER_VERSION "0.02"
|
||||
#define DRIVER_VERSION "0.03"
|
||||
|
||||
/* driver description structure */
|
||||
upsdrv_info_t upsdrv_info = {
|
||||
|
@ -105,14 +105,14 @@ static int cypress_setfeatures()
|
|||
return ret;
|
||||
}
|
||||
|
||||
uint8_t Send_USB_Packet(uint8_t *send_str, uint16_t numbytes)
|
||||
int Send_USB_Packet(uint8_t *send_str, uint16_t numbytes)
|
||||
{
|
||||
uint8_t USB_buff_pom[10];
|
||||
int i, err, size, errno;
|
||||
|
||||
/* is input correct ? */
|
||||
if ((!send_str) || (!numbytes))
|
||||
return 1;
|
||||
return -1;
|
||||
|
||||
size = 7;
|
||||
|
||||
|
@ -130,11 +130,8 @@ uint8_t Send_USB_Packet(uint8_t *send_str, uint16_t numbytes)
|
|||
err = usb_bulk_write(udev, 0x2, (char*) USB_buff_pom, 8, 1000);
|
||||
|
||||
if (err < 0) {
|
||||
if (err==-13 || errno==19)
|
||||
upsdebugx(3, "USB device disconnected !");
|
||||
else
|
||||
upsdebugx(3, "USB: Send_USB_Packet: send_usb_packet, err = %08x %s ", err, strerror(errno));
|
||||
return 2;
|
||||
upsdebugx(3, "USB: Send_USB_Packet: send_usb_packet, err = %d %s ", err, strerror(errno));
|
||||
return err;
|
||||
}
|
||||
ussleep(USB_WRITE_DELAY);
|
||||
}
|
||||
|
@ -163,18 +160,15 @@ uint8_t Send_USB_Packet(uint8_t *send_str, uint16_t numbytes)
|
|||
err = usb_bulk_write(udev, 0x2, (char*) USB_buff_pom, 8, 1000);
|
||||
|
||||
if (err < 0) {
|
||||
if (err==-13 || errno==19)
|
||||
upsdebugx(3, "USB device disconnected !");
|
||||
else
|
||||
upsdebugx(3, "USB: Send_USB_Packet: send_usb_packet, err = %08x %s ", err, strerror(errno));
|
||||
return 2;
|
||||
upsdebugx(3, "USB: Send_USB_Packet: send_usb_packet, err = %d %s ", err, strerror(errno));
|
||||
return err;
|
||||
}
|
||||
ussleep(USB_WRITE_DELAY);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
uint8_t Get_USB_Packet(uint8_t *buffer)
|
||||
int Get_USB_Packet(uint8_t *buffer)
|
||||
{
|
||||
char inBuf[10];
|
||||
int err, size, errno, ep;
|
||||
|
@ -185,23 +179,12 @@ uint8_t Get_USB_Packet(uint8_t *buffer)
|
|||
ep = 0x81 | USB_ENDPOINT_IN;
|
||||
err = usb_bulk_read(udev, ep, (char*) inBuf, size, 1000);
|
||||
|
||||
upsdebugx(3, "read: %02X %02X %02X %02X %02X %02X %02X %02X", inBuf[0], inBuf[1], inBuf[2], inBuf[3], inBuf[4], inBuf[5], inBuf[6], inBuf[7]);
|
||||
|
||||
if (err == 0) {
|
||||
if (err==-13 || err==-19 || errno==19)
|
||||
upsdebugx(3, "USB device disconnected !");
|
||||
else {
|
||||
switch (errno) {
|
||||
case 11: /* resource temp. not available */
|
||||
case 16: /* device busy */
|
||||
/* ignore it */
|
||||
break;
|
||||
default:
|
||||
upsdebugx(3, "USB: Get_USB_Packet: send_usb_packet, err = %08x %s ", err, strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
if (err > 0)
|
||||
upsdebugx(3, "read: %02X %02X %02X %02X %02X %02X %02X %02X", inBuf[0], inBuf[1], inBuf[2], inBuf[3], inBuf[4], inBuf[5], inBuf[6], inBuf[7]);
|
||||
|
||||
if (err < 0){
|
||||
upsdebugx(3, "USB: Get_USB_Packet: send_usb_packet, err = %d %s ", err, strerror(errno));
|
||||
return err;
|
||||
}
|
||||
|
||||
/* copy to buffer */
|
||||
|
@ -214,18 +197,18 @@ uint8_t Get_USB_Packet(uint8_t *buffer)
|
|||
|
||||
static int cypress_command(uint8_t *buffer, uint8_t *buf, uint16_t length, uint16_t buflen)
|
||||
{
|
||||
int loop = 0;
|
||||
int ret, i = 0;
|
||||
uint8_t USB_buff[BUFFER_SIZE];
|
||||
|
||||
/* read to flush buffer */
|
||||
/* ret = Get_USB_Packet(buf+i);*/
|
||||
riello_init_serial();
|
||||
|
||||
/* send packet */
|
||||
ret = Send_USB_Packet(buffer, length);
|
||||
|
||||
if (ret > 0) {
|
||||
upsdebugx(3, "send: %s", ret ? usb_strerror() : "timeout");
|
||||
if (ret < 0) {
|
||||
upsdebugx(3, "Cypress_command send: err %d", ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -243,7 +226,7 @@ static int cypress_command(uint8_t *buffer, uint8_t *buf, uint16_t length, uint1
|
|||
* will happen after successfully writing a command to the UPS)
|
||||
*/
|
||||
if (ret < 0) {
|
||||
upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout");
|
||||
upsdebugx(3, "Cypress_command read: err %d", ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -251,6 +234,14 @@ static int cypress_command(uint8_t *buffer, uint8_t *buf, uint16_t length, uint1
|
|||
commbyte = USB_buff[i];
|
||||
riello_parse_serialport(DEV_RIELLOGPSER, buf, gpser_error_control);
|
||||
}
|
||||
|
||||
loop++;
|
||||
if (loop>300){
|
||||
wait_packet=0;
|
||||
upsdebugx(1, "wait_packet reset");
|
||||
}
|
||||
|
||||
ussleep(10);
|
||||
}
|
||||
|
||||
upsdebugx(3, "in read: %u", buf_ptr_length);
|
||||
|
@ -302,6 +293,30 @@ static USBDeviceMatcher_t device_matcher = {
|
|||
};
|
||||
|
||||
|
||||
/*
|
||||
* Callback that is called by usb_device_open() that handles USB device
|
||||
* settings prior to accepting the devide. At the very least claim the
|
||||
* device here. Detaching the kernel driver will be handled by the
|
||||
* caller, don't do this here. Return < 0 on error, 0 or higher on
|
||||
* success.
|
||||
*/
|
||||
static int driver_callback(usb_dev_handle *handle, USBDevice_t *device, unsigned char *rdbuf, int rdlen)
|
||||
{
|
||||
/*if (usb_set_configuration(handle, 1) < 0) {
|
||||
upslogx(LOG_WARNING, "Can't set USB configuration: %s", usb_strerror());
|
||||
return -1;
|
||||
} */
|
||||
|
||||
if (usb_claim_interface(handle, 0) < 0) {
|
||||
upslogx(LOG_WARNING, "Can't claim USB interface: %s", usb_strerror());
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TODO: HID SET_IDLE to 0 (not necessary?) */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic command processing function. Send a command and read a reply.
|
||||
* Returns < 0 on error, 0 on timeout and the number of bytes read on
|
||||
|
@ -312,18 +327,23 @@ int riello_command(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t buflen)
|
|||
int ret;
|
||||
|
||||
if (udev == NULL) {
|
||||
ret = usb->open(&udev, &usbdevice, reopen_matcher, NULL);
|
||||
ret = usb->open(&udev, &usbdevice, reopen_matcher, &driver_callback);
|
||||
|
||||
if (ret < 1) {
|
||||
upsdebugx (3, "riello_command err udev NULL : %d ", ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
upsdrv_initinfo(); //reconekt usb cable
|
||||
}
|
||||
|
||||
ret = (*subdriver_command)(cmd, buf, length, buflen);
|
||||
if (ret >= 0) {
|
||||
upsdebugx (3, "riello_command ok: %u", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
upsdebugx (3, "riello_command err: %d", ret);
|
||||
|
||||
switch (ret)
|
||||
{
|
||||
case -EBUSY: /* Device or resource busy */
|
||||
|
@ -354,37 +374,49 @@ int riello_command(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t buflen)
|
|||
break;
|
||||
|
||||
case -ETIMEDOUT: /* Connection timed out */
|
||||
upsdebugx (3, "riello_command err: Resource temporarily unavailable");
|
||||
|
||||
|
||||
case -EOVERFLOW: /* Value too large for defined data type */
|
||||
#ifdef EPROTO
|
||||
case -EPROTO: /* Protocol error */
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_ups_nominal()
|
||||
{
|
||||
|
||||
uint8_t recv, length;
|
||||
uint8_t length;
|
||||
int recv;
|
||||
|
||||
length = riello_prepare_gn(&bufOut[0], gpser_error_control);
|
||||
|
||||
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_GN);
|
||||
|
||||
if (recv < 0){
|
||||
upsdebugx (3, "Get nominal err: read byte: %d", recv);
|
||||
return recv;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Get nominal Ko: bad CRC or Checksum");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* mandatory */
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Get nominal Ko: command not supported");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Get nominal Ok: read byte: %u", recv);
|
||||
upsdebugx (3, "Get nominal Ok: read byte: %d", recv);
|
||||
|
||||
riello_parse_gn(&bufIn[0], &DevData);
|
||||
|
||||
|
@ -393,7 +425,8 @@ int get_ups_nominal()
|
|||
|
||||
int get_ups_status()
|
||||
{
|
||||
uint8_t recv, numread, length;
|
||||
uint8_t numread, length;
|
||||
int recv;
|
||||
|
||||
length = riello_prepare_rs(&bufOut[0], gpser_error_control);
|
||||
|
||||
|
@ -406,17 +439,23 @@ int get_ups_status()
|
|||
|
||||
recv = riello_command(&bufOut[0], &bufIn[0], length, numread);
|
||||
|
||||
if (recv < 0){
|
||||
upsdebugx (3, "Get status err: read byte: %d", recv);
|
||||
return recv;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Get status Ko: bad CRC or Checksum");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* mandatory */
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Get status Ko: command not supported");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Get status Ok: read byte: %u", recv);
|
||||
upsdebugx (3, "Get status Ok: read byte: %d", recv);
|
||||
|
||||
riello_parse_rs(&bufIn[0], &DevData, numread);
|
||||
|
||||
|
@ -425,23 +464,30 @@ int get_ups_status()
|
|||
|
||||
int get_ups_extended()
|
||||
{
|
||||
uint8_t recv, length;
|
||||
uint8_t length;
|
||||
int recv;
|
||||
|
||||
length = riello_prepare_re(&bufOut[0], gpser_error_control);
|
||||
|
||||
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_RE);
|
||||
|
||||
if (recv < 0){
|
||||
upsdebugx (3, "Get extended err: read byte: %d", recv);
|
||||
return recv;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Get extended Ko: bad CRC or Checksum");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* optional */
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Get extended Ko: command not supported");
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Get extended Ok: read byte: %u", recv);
|
||||
upsdebugx (3, "Get extended Ok: read byte: %d", recv);
|
||||
|
||||
riello_parse_re(&bufIn[0], &DevData);
|
||||
|
||||
|
@ -450,23 +496,30 @@ int get_ups_extended()
|
|||
|
||||
int get_ups_statuscode()
|
||||
{
|
||||
uint8_t recv, length;
|
||||
uint8_t length;
|
||||
int recv;
|
||||
|
||||
length = riello_prepare_rc(&bufOut[0], gpser_error_control);
|
||||
|
||||
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_RC);
|
||||
|
||||
if (recv < 0){
|
||||
upsdebugx (3, "Get statuscode err: read byte: %d", recv);
|
||||
return recv;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Get statuscode Ko: bad CRC or Checksum");
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* optional */
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Get statuscode Ko: command not supported");
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Get statuscode Ok: read byte: %u", recv);
|
||||
upsdebugx (3, "Get statuscode Ok: read byte: %d", recv);
|
||||
|
||||
riello_parse_rc(&bufIn[0], &DevData);
|
||||
|
||||
|
@ -475,7 +528,8 @@ int get_ups_statuscode()
|
|||
|
||||
int riello_instcmd(const char *cmdname, const char *extra)
|
||||
{
|
||||
uint8_t length, recv;
|
||||
uint8_t length;
|
||||
int recv;
|
||||
uint16_t delay;
|
||||
const char *delay_char;
|
||||
|
||||
|
@ -483,175 +537,201 @@ int riello_instcmd(const char *cmdname, const char *extra)
|
|||
|
||||
if (!strcasecmp(cmdname, "load.off")) {
|
||||
delay = 0;
|
||||
|
||||
length = riello_prepare_cs(bufOut, gpser_error_control, delay);
|
||||
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
|
||||
if (recv > 0) {
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.off Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.off Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
else
|
||||
if (recv < 0) {
|
||||
upsdebugx (3, "Command load.off err: read byte: %d", recv);
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.off Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.off Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command load.off Ok: read byte: %d", recv);
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
if (!strcasecmp(cmdname, "load.off.delay")) {
|
||||
delay_char = dstate_getinfo("ups.delay.shutdown");
|
||||
delay_char = dstate_getinfo("ups.delay.shutdown");
|
||||
delay = atoi(delay_char);
|
||||
|
||||
length = riello_prepare_cs(bufOut, gpser_error_control, delay);
|
||||
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
|
||||
if (recv > 0) {
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.off.delay Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.off.delay Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
else
|
||||
if (recv < 0) {
|
||||
upsdebugx (3, "Command load.off.delay err: read byte: %d", recv);
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.off.delay Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.off.delay Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command load.off.delay Ok: read byte: %d", recv);
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
if (!strcasecmp(cmdname, "load.on")) {
|
||||
delay = 0;
|
||||
|
||||
length = riello_prepare_cr(bufOut, gpser_error_control, delay);
|
||||
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
|
||||
if (recv > 0) {
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.on Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.on Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
else
|
||||
if (recv < 0) {
|
||||
upsdebugx (3, "Command load.on err: read byte: %d", recv);
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.on Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.on Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command load.on Ok: read byte: %d", recv);
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
if (!strcasecmp(cmdname, "load.on.delay")) {
|
||||
delay_char = dstate_getinfo("ups.delay.reboot");
|
||||
delay_char = dstate_getinfo("ups.delay.reboot");
|
||||
delay = atoi(delay_char);
|
||||
|
||||
length = riello_prepare_cr(bufOut, gpser_error_control, delay);
|
||||
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
|
||||
if (recv > 0) {
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.on.delay Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.on.delay Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
else
|
||||
if (recv < 0) {
|
||||
upsdebugx (3, "Command load.on.delay err: read byte: %d", recv);
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command load.on.delay Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command load.on.delay Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command load.on.delay Ok: read byte: %d", recv);
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!strcasecmp(cmdname, "shutdown.return")) {
|
||||
delay_char = dstate_getinfo("ups.delay.shutdown");
|
||||
delay_char = dstate_getinfo("ups.delay.shutdown");
|
||||
delay = atoi(delay_char);
|
||||
|
||||
length = riello_prepare_cs(bufOut, gpser_error_control, delay);
|
||||
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
|
||||
if (recv > 0) {
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command shutdown.return Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command shutdown.return Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
else
|
||||
if (recv < 0) {
|
||||
upsdebugx (3, "Command shutdown.return err: read byte: %d", recv);
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command shutdown.return Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command shutdown.return Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command shutdown.return Ok: read byte: %d", recv);
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcasecmp(cmdname, "shutdown.stop")) {
|
||||
length = riello_prepare_cd(bufOut, gpser_error_control);
|
||||
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
|
||||
if (recv > 0) {
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command shutdown.stop Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command shutdown.stop Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
else
|
||||
if (recv < 0) {
|
||||
upsdebugx (3, "Command shutdown.stop err: read byte: %d", recv);
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command shutdown.stop Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command shutdown.stop Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command shutdown.stop Ok: read byte: %d", recv);
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
if (!strcasecmp(cmdname, "test.panel.start")) {
|
||||
length = riello_prepare_tp(bufOut, gpser_error_control);
|
||||
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
|
||||
if (recv > 0) {
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command test.panel.start Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command test.panel.start Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
else
|
||||
if (recv < 0) {
|
||||
upsdebugx (3, "Command test.panel.start err: read byte: %d", recv);
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command test.panel.start Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command test.panel.start Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command test.panel.start Ok: read byte: %d", recv);
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
if (!strcasecmp(cmdname, "test.battery.start")) {
|
||||
length = riello_prepare_tb(bufOut, gpser_error_control);
|
||||
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
|
||||
if (recv > 0) {
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command test.battery.start Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command test.battery.start Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
else
|
||||
if (recv < 0) {
|
||||
upsdebugx (3, "Command test.battery.start err: read byte: %d", recv);
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Command test.battery.start Ko: bad CRC or Checksum");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundnak) {
|
||||
upsdebugx (3, "Command test.battery.start Ko: command not supported");
|
||||
return STAT_INSTCMD_FAILED;
|
||||
}
|
||||
|
||||
upsdebugx (3, "Command test.battery.start Ok: read byte: %d", recv);
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
}
|
||||
|
||||
upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
|
||||
|
@ -671,6 +751,11 @@ int start_ups_comm()
|
|||
|
||||
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_GI);
|
||||
|
||||
if (recv < 0) {
|
||||
upsdebugx (3, "Get identif err: read byte: %d", recv);
|
||||
return recv;
|
||||
}
|
||||
|
||||
if (!wait_packet && foundbadcrc) {
|
||||
upsdebugx (3, "Get identif Ko: bad CRC or Checksum");
|
||||
return 1;
|
||||
|
@ -681,10 +766,9 @@ int start_ups_comm()
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
upsdebugx (3, "Get identif Ok: read byte: %u", recv);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void upsdrv_help(void)
|
||||
|
@ -755,11 +839,10 @@ void upsdrv_initups(void)
|
|||
fatalx(EXIT_FAILURE, "invalid regular expression: %s", regex_array[ret]);
|
||||
}
|
||||
|
||||
|
||||
/* link the matchers */
|
||||
regex_matcher->next = &device_matcher;
|
||||
|
||||
ret = usb->open(&udev, &usbdevice, regex_matcher, NULL);
|
||||
ret = usb->open(&udev, &usbdevice, regex_matcher, &driver_callback);
|
||||
if (ret < 0) {
|
||||
fatalx(EXIT_FAILURE,
|
||||
"No supported devices found. Please check your device availability with 'lsusb'\n"
|
||||
|
@ -894,16 +977,25 @@ void upsdrv_updateinfo(void)
|
|||
{
|
||||
uint8_t getextendedOK;
|
||||
static int countlost = 0;
|
||||
int stat;
|
||||
|
||||
if (countlost < COUNTLOST)
|
||||
upsdebugx(1, "countlost %d",countlost);
|
||||
|
||||
if (countlost > 0){
|
||||
upsdebugx(1, "Communication with UPS is lost: status read failed!");
|
||||
else if (countlost == COUNTLOST)
|
||||
upslogx(LOG_WARNING, "Communication with UPS is lost: status read failed!");
|
||||
else
|
||||
dstate_datastale();
|
||||
|
||||
if (get_ups_status() != 0) {
|
||||
if (countlost <= COUNTLOST)
|
||||
if (countlost == COUNTLOST) {
|
||||
dstate_datastale();
|
||||
upslogx(LOG_WARNING, "Communication with UPS is lost: status read failed!");
|
||||
}
|
||||
}
|
||||
|
||||
stat = get_ups_status();
|
||||
|
||||
upsdebugx(1, "get_ups_status() %d",stat );
|
||||
|
||||
if (stat < 0) {
|
||||
if (countlost < COUNTLOST)
|
||||
countlost++;
|
||||
return;
|
||||
}
|
||||
|
@ -913,7 +1005,7 @@ void upsdrv_updateinfo(void)
|
|||
else
|
||||
getextendedOK = 0;
|
||||
|
||||
if (countlost > COUNTLOST)
|
||||
if (countlost == COUNTLOST)
|
||||
upslogx(LOG_NOTICE, "Communication with UPS is re-established!");
|
||||
|
||||
dstate_setinfo("input.frequency", "%.2f", DevData.Finp/10.0);
|
||||
|
@ -963,23 +1055,23 @@ void upsdrv_updateinfo(void)
|
|||
/* LowBatt */
|
||||
if ((riello_test_bit(&DevData.StatusCode[0], 1)) &&
|
||||
(riello_test_bit(&DevData.StatusCode[0], 0)))
|
||||
status_set("LB");
|
||||
status_set("LB");
|
||||
|
||||
/* Standby */
|
||||
if (!riello_test_bit(&DevData.StatusCode[0], 3))
|
||||
status_set("OFF");
|
||||
status_set("OFF");
|
||||
|
||||
/* On Bypass */
|
||||
if (riello_test_bit(&DevData.StatusCode[1], 3))
|
||||
status_set("BYPASS");
|
||||
status_set("BYPASS");
|
||||
|
||||
/* Overload */
|
||||
if (riello_test_bit(&DevData.StatusCode[4], 2))
|
||||
status_set("OVER");
|
||||
status_set("OVER");
|
||||
|
||||
/* Buck */
|
||||
if (riello_test_bit(&DevData.StatusCode[1], 0))
|
||||
status_set("TRIM");
|
||||
status_set("TRIM");
|
||||
|
||||
/* Boost */
|
||||
if (riello_test_bit(&DevData.StatusCode[1], 1))
|
||||
|
@ -991,7 +1083,7 @@ void upsdrv_updateinfo(void)
|
|||
|
||||
/* Charging battery */
|
||||
if (riello_test_bit(&DevData.StatusCode[2], 2))
|
||||
status_set("CHRG");
|
||||
status_set("CHRG");
|
||||
|
||||
status_commit();
|
||||
|
||||
|
@ -1011,7 +1103,8 @@ void upsdrv_updateinfo(void)
|
|||
|
||||
poll_interval = 2;
|
||||
|
||||
countlost = 0;
|
||||
countlost = 0;
|
||||
|
||||
/* if (get_ups_statuscode() != 0)
|
||||
upsdebugx(2, "Communication is lost");
|
||||
else {
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
#include "timehead.h"
|
||||
|
||||
#define DRIVER_NAME "Microsol Solis UPS driver"
|
||||
#define DRIVER_VERSION "0.61"
|
||||
#define DRIVER_VERSION "0.62"
|
||||
|
||||
/* driver description structure */
|
||||
upsdrv_info_t upsdrv_info = {
|
||||
|
@ -610,7 +610,7 @@ static void ScanReceivePack( void )
|
|||
|
||||
/* Autonomy */
|
||||
|
||||
if( ( Autonomy < 5 ) )
|
||||
if( Autonomy < 5 )
|
||||
LowBatt = true;
|
||||
else
|
||||
LowBatt = false;
|
||||
|
@ -618,7 +618,7 @@ static void ScanReceivePack( void )
|
|||
UpsPowerFactor = 700;
|
||||
|
||||
/* input 110V or 220v */
|
||||
if( ( InputValue == 0 ) ) {
|
||||
if( InputValue == 0 ) {
|
||||
InDownLim = 75;
|
||||
InUpLim = 150;
|
||||
NomInVolt = 110;
|
||||
|
@ -702,7 +702,7 @@ CommReceive(const char *bufptr, int size)
|
|||
|
||||
int i, CheckSum, i_end;
|
||||
|
||||
if( ( size==25 ) )
|
||||
if( size==25 )
|
||||
Waiting = 0;
|
||||
|
||||
switch( Waiting )
|
||||
|
@ -727,7 +727,8 @@ CommReceive(const char *bufptr, int size)
|
|||
ser_flush_in(upsfd,"",0); /* clean port */
|
||||
|
||||
/* correct package */
|
||||
if( ( (RecPack[0] & 0xF0) == 0xA0 )
|
||||
/* 0xA0 is original solis.c; 0xB0 is for APC-branded Microsol units */
|
||||
if( ( ( (RecPack[0] & 0xF0) == 0xA0 ) || (RecPack[0] & 0xF0) == 0xB0)
|
||||
&& ( RecPack[ 24 ] == 254 )
|
||||
&& ( RecPack[ 23 ] == CheckSum ) ) {
|
||||
|
||||
|
@ -742,7 +743,11 @@ CommReceive(const char *bufptr, int size)
|
|||
|
||||
switch( SolisModel )
|
||||
{
|
||||
case 10:
|
||||
case 10: /* Added for APC-Branded Microsol units */
|
||||
{
|
||||
ScanReceivePack();
|
||||
break;
|
||||
}
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
|
@ -755,6 +760,7 @@ CommReceive(const char *bufptr, int size)
|
|||
default:
|
||||
{
|
||||
printf( M_UNKN );
|
||||
ScanReceivePack(); // Scan anyway.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -884,26 +890,30 @@ static void getbaseinfo(void)
|
|||
|
||||
switch( SolisModel )
|
||||
{
|
||||
case 10:
|
||||
case 10: /* Added for APC-Microsol units */
|
||||
{
|
||||
Model = "Back-UPS 1200 BR";
|
||||
break;
|
||||
}
|
||||
case 11:
|
||||
case 12:
|
||||
{
|
||||
strcpy(Model, "Solis 1.0");
|
||||
Model = "Solis 1.0";
|
||||
break;
|
||||
}
|
||||
case 13:
|
||||
{
|
||||
strcpy(Model, "Solis 1.5");
|
||||
Model = "Solis 1.5";
|
||||
break;
|
||||
}
|
||||
case 14:
|
||||
{
|
||||
strcpy(Model, "Solis 2.0");
|
||||
Model = "Solis 2.0";
|
||||
break;
|
||||
}
|
||||
case 15:
|
||||
{
|
||||
strcpy(Model, "Solis 3.0");
|
||||
Model = "Solis 3.0";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -309,7 +309,7 @@ unsigned char DumpPack[242];
|
|||
*/
|
||||
|
||||
/* Identification */
|
||||
char Model[12];
|
||||
const char *Model;
|
||||
int SolisModel, imodel;
|
||||
int InputValue, Out220;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
Copyright (C) 1999 Russell Kroll <rkroll@exploits.org>
|
||||
Copyright (C) 2001 Rickard E. (Rik) Faith <faith@alephnull.com>
|
||||
Copyright (C) 2004 Nicholas J. Kain <nicholas@kain.us>
|
||||
Copyright (C) 2005-2008 Charles Lepple <clepple+nut@gmail.com>
|
||||
Copyright (C) 2005-2008, 2014 Charles Lepple <clepple+nut@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -125,6 +125,9 @@
|
|||
* :S -- enables remote reboot/remote power on
|
||||
*/
|
||||
|
||||
/* Watchdog for 3005 is 15 - 255 seconds.
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include "libusb.h"
|
||||
#include <math.h>
|
||||
|
@ -133,7 +136,7 @@
|
|||
#include "usb-common.h"
|
||||
|
||||
#define DRIVER_NAME "Tripp Lite OMNIVS / SMARTPRO driver"
|
||||
#define DRIVER_VERSION "0.20"
|
||||
#define DRIVER_VERSION "0.29"
|
||||
|
||||
/* driver description structure */
|
||||
upsdrv_info_t upsdrv_info = {
|
||||
|
@ -188,9 +191,38 @@ static enum tl_model_t {
|
|||
TRIPP_LITE_OMNIVS,
|
||||
TRIPP_LITE_OMNIVS_2001,
|
||||
TRIPP_LITE_SMARTPRO,
|
||||
TRIPP_LITE_SMART_0004
|
||||
TRIPP_LITE_SMART_0004,
|
||||
TRIPP_LITE_SMART_3005
|
||||
} tl_model = TRIPP_LITE_UNKNOWN;
|
||||
|
||||
/*! Are the values encoded in ASCII or binary?
|
||||
* TODO: Add 3004?
|
||||
*/
|
||||
static int is_binary_protocol()
|
||||
{
|
||||
switch(tl_model) {
|
||||
case TRIPP_LITE_SMART_3005:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Is this the "SMART" family of protocols?
|
||||
* TODO: Add 3004?
|
||||
*/
|
||||
static int is_smart_protocol()
|
||||
{
|
||||
switch(tl_model) {
|
||||
case TRIPP_LITE_SMARTPRO:
|
||||
case TRIPP_LITE_SMART_0004:
|
||||
case TRIPP_LITE_SMART_3005:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*!@brief If a character is not printable, return a dot. */
|
||||
#define toprint(x) (isalnum((unsigned)x) ? (x) : '.')
|
||||
|
||||
|
@ -201,7 +233,7 @@ static enum tl_model_t {
|
|||
#define SEND_WAIT_NSEC (1000*1000*100)
|
||||
|
||||
#define MAX_RECV_TRIES 10
|
||||
#define RECV_WAIT_MSEC 1000 /*! was 100 for OMNIVS; SMARTPRO units need longer */
|
||||
#define RECV_WAIT_MSEC 1000 /*!< was 100 for OMNIVS; SMARTPRO units need longer */
|
||||
|
||||
#define MAX_RECONNECT_TRIES 10
|
||||
|
||||
|
@ -307,6 +339,36 @@ static int hex2d(const unsigned char *start, unsigned int len)
|
|||
return strtol((char *)buf, NULL, 16);
|
||||
}
|
||||
|
||||
/*!@brief Convert N characters from big-endian binary to decimal
|
||||
*
|
||||
* @param start Beginning of string to convert
|
||||
* @param len Maximum number of characters to consider (max 32)
|
||||
*
|
||||
* @a len characters of @a start are shifted into an accumulator.
|
||||
*
|
||||
* We assume len < sizeof(int), and that value > 0.
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
static unsigned int bin2d(const unsigned char *start, unsigned int len)
|
||||
{
|
||||
unsigned int value = 0, index = 0;
|
||||
for(index = 0; index < len; index++) {
|
||||
value <<= 8;
|
||||
value |= start[index];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int hex_or_bin2d(const unsigned char *start, unsigned int len)
|
||||
{
|
||||
if(is_binary_protocol()) {
|
||||
return bin2d(start, len);
|
||||
}
|
||||
return hex2d(start, len);
|
||||
}
|
||||
|
||||
/*!@brief Dump message in both hex and ASCII
|
||||
*
|
||||
* @param[in] msg Buffer to dump
|
||||
|
@ -349,7 +411,7 @@ enum tl_model_t decode_protocol(unsigned int proto)
|
|||
{
|
||||
switch(proto) {
|
||||
case 0x0004:
|
||||
upslogx(3, "Using older SMART protocol (%x)", proto);
|
||||
upslogx(3, "Using older SMART protocol (%04x)", proto);
|
||||
return TRIPP_LITE_SMART_0004;
|
||||
case 0x1001:
|
||||
upslogx(3, "Using OMNIVS protocol (%x)", proto);
|
||||
|
@ -360,8 +422,11 @@ enum tl_model_t decode_protocol(unsigned int proto)
|
|||
case 0x3003:
|
||||
upslogx(3, "Using SMARTPRO protocol (%x)", proto);
|
||||
return TRIPP_LITE_SMARTPRO;
|
||||
case 0x3005:
|
||||
upslogx(3, "Using binary SMART protocol (%x)", proto);
|
||||
return TRIPP_LITE_SMART_3005;
|
||||
default:
|
||||
printf("Unknown protocol (%x)", proto);
|
||||
printf("Unknown protocol (%04x)", proto);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -371,7 +436,15 @@ enum tl_model_t decode_protocol(unsigned int proto)
|
|||
void decode_v(const unsigned char *value)
|
||||
{
|
||||
unsigned char ivn, lb;
|
||||
int bv = hex2d(value+2, 2);
|
||||
int bv;
|
||||
|
||||
if(is_binary_protocol()) {
|
||||
/* 0x00 0x0c -> 12V ? */
|
||||
battery_voltage_nominal = (value[2] << 8) | value[3];
|
||||
} else {
|
||||
bv = hex2d(value+2, 2);
|
||||
battery_voltage_nominal = bv * 6;
|
||||
}
|
||||
|
||||
ivn = value[1];
|
||||
lb = value[4];
|
||||
|
@ -381,6 +454,7 @@ void decode_v(const unsigned char *value)
|
|||
input_voltage_scaled = 100;
|
||||
break;
|
||||
|
||||
case 2: /* protocol 3005 */
|
||||
case '1': input_voltage_nominal =
|
||||
input_voltage_scaled = 120;
|
||||
break;
|
||||
|
@ -398,16 +472,19 @@ void decode_v(const unsigned char *value)
|
|||
break;
|
||||
}
|
||||
|
||||
battery_voltage_nominal = bv * 6;
|
||||
|
||||
if( (lb >= '0') && (lb <= '9') ) {
|
||||
switchable_load_banks = lb - '0';
|
||||
} else {
|
||||
if( lb != 'X' ) {
|
||||
upslogx(2, "Unknown number of switchable load banks: 0x%02x",
|
||||
if(is_binary_protocol()) {
|
||||
switchable_load_banks = lb;
|
||||
} else {
|
||||
if( lb != 'X' ) {
|
||||
upslogx(2, "Unknown number of switchable load banks: 0x%02x",
|
||||
(unsigned int)lb);
|
||||
}
|
||||
}
|
||||
}
|
||||
upsdebugx(2, "Switchable load banks: %d", switchable_load_banks);
|
||||
}
|
||||
|
||||
void upsdrv_initinfo(void);
|
||||
|
@ -581,6 +658,7 @@ static int soft_shutdown(void)
|
|||
int ret;
|
||||
unsigned char buf[256], cmd_N[]="N\0x", cmd_G[] = "G";
|
||||
|
||||
/* Already binary: */
|
||||
cmd_N[2] = offdelay;
|
||||
cmd_N[1] = offdelay >> 8;
|
||||
upsdebugx(3, "soft_shutdown(offdelay=%d): N", offdelay);
|
||||
|
@ -652,8 +730,22 @@ static int control_outlet(int outlet_id, int state)
|
|||
} else {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case TRIPP_LITE_SMART_3005:
|
||||
snprintf(k_cmd, sizeof(k_cmd)-1, "N%c", 5);
|
||||
ret = send_cmd((unsigned char *)k_cmd, strlen(k_cmd) + 1, (unsigned char *)buf, sizeof buf);
|
||||
snprintf(k_cmd, sizeof(k_cmd)-1, "K%c%c", outlet_id, state & 1);
|
||||
ret = send_cmd((unsigned char *)k_cmd, strlen(k_cmd) + 1, (unsigned char *)buf, sizeof buf);
|
||||
|
||||
if(ret != 8) {
|
||||
upslogx(LOG_ERR, "Could not set outlet %d to state %d, ret = %d", outlet_id, state, ret);
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
upslogx(LOG_ERR, "control_outlet unimplemented for this UPS model");
|
||||
upslogx(LOG_ERR, "control_outlet unimplemented for protocol %04x", tl_model);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -664,7 +756,7 @@ static int instcmd(const char *cmdname, const char *extra)
|
|||
{
|
||||
unsigned char buf[10];
|
||||
|
||||
if(tl_model == TRIPP_LITE_SMARTPRO || tl_model == TRIPP_LITE_SMART_0004) {
|
||||
if(is_smart_protocol()) {
|
||||
if (!strcasecmp(cmdname, "test.battery.start")) {
|
||||
send_cmd((const unsigned char *)"A", 2, buf, sizeof buf);
|
||||
return STAT_INSTCMD_HANDLED;
|
||||
|
@ -964,7 +1056,7 @@ void upsdrv_initinfo(void)
|
|||
dstate_setaux("ups.delay.reboot", 3);
|
||||
#endif
|
||||
|
||||
if(tl_model == TRIPP_LITE_SMARTPRO || tl_model == TRIPP_LITE_SMART_0004) {
|
||||
if(is_smart_protocol()) {
|
||||
dstate_addcmd("test.battery.start");
|
||||
dstate_addcmd("reset.input.minmax");
|
||||
}
|
||||
|
@ -1002,10 +1094,12 @@ void upsdrv_updateinfo(void)
|
|||
unsigned char b_value[9], d_value[9], l_value[9], s_value[9],
|
||||
m_value[9], t_value[9];
|
||||
int bp, freq;
|
||||
double bv;
|
||||
double bv_12V = 0.0; /*!< battery voltage, relative to a 12V battery */
|
||||
double battery_voltage; /*!< the total battery voltage */
|
||||
|
||||
unsigned int s_value_1;
|
||||
|
||||
int ret;
|
||||
unsigned battery_charge;
|
||||
|
||||
status_init();
|
||||
|
||||
|
@ -1055,8 +1149,14 @@ void upsdrv_updateinfo(void)
|
|||
|
||||
/* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */
|
||||
|
||||
if(tl_model == TRIPP_LITE_SMARTPRO || tl_model == TRIPP_LITE_OMNIVS_2001 || tl_model == TRIPP_LITE_SMART_0004) {
|
||||
switch(s_value[2]) {
|
||||
if(is_smart_protocol() || tl_model == TRIPP_LITE_OMNIVS_2001) {
|
||||
|
||||
unsigned int s_value_2 = s_value[2];
|
||||
|
||||
if(is_binary_protocol()) {
|
||||
s_value_2 += '0';
|
||||
}
|
||||
switch(s_value_2) {
|
||||
case '0':
|
||||
dstate_setinfo("battery.test.status", "Battery OK");
|
||||
break;
|
||||
|
@ -1096,16 +1196,27 @@ void upsdrv_updateinfo(void)
|
|||
}
|
||||
}
|
||||
|
||||
/* This may not be right... */
|
||||
#if 0
|
||||
/* Apparently, this value changes more frequently when the
|
||||
* battery is discharged, but it does not track the actual
|
||||
* state-of-charge. See battery.charge calculation below.
|
||||
*/
|
||||
if(tl_model == TRIPP_LITE_SMARTPRO) {
|
||||
unsigned battery_charge;
|
||||
battery_charge = (unsigned)(s_value[5]);
|
||||
dstate_setinfo("battery.charge", "%u", battery_charge);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */
|
||||
|
||||
switch(s_value[1]) {
|
||||
s_value_1 = s_value[1];
|
||||
if(is_binary_protocol()) {
|
||||
s_value_1 += '0';
|
||||
}
|
||||
|
||||
switch(s_value_1) {
|
||||
case '0':
|
||||
status_set("LB");
|
||||
break;
|
||||
|
@ -1134,28 +1245,18 @@ void upsdrv_updateinfo(void)
|
|||
return;
|
||||
}
|
||||
|
||||
dstate_setinfo("input.voltage", "%.2f", hex2d(b_value+1, 4)/30.0);
|
||||
dstate_setinfo("input.voltage", "%.2f", hex2d(b_value+1, 4)/3600.0*input_voltage_scaled);
|
||||
|
||||
bv = hex2d(b_value+5, 2)/16.0;
|
||||
bv_12V = hex2d(b_value+5, 2)/16.0;
|
||||
|
||||
/* 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. */
|
||||
if (bv >= V_interval[1])
|
||||
bp = 100;
|
||||
else if (bv <= V_interval[0])
|
||||
bp = 10;
|
||||
else
|
||||
bp = (int)(100*sqrt((bv - V_interval[0])
|
||||
/ (V_interval[1] - V_interval[0])));
|
||||
|
||||
dstate_setinfo("battery.voltage", "%.2f", bv);
|
||||
dstate_setinfo("battery.charge", "%3d", bp);
|
||||
/* TODO: use battery_voltage_nominal, even though it is most likely 12V */
|
||||
dstate_setinfo("battery.voltage", "%.2f", bv_12V);
|
||||
}
|
||||
|
||||
/* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */
|
||||
|
||||
if( tl_model == TRIPP_LITE_SMARTPRO || tl_model == TRIPP_LITE_SMART_0004 ) {
|
||||
if( is_smart_protocol() ) {
|
||||
|
||||
ret = send_cmd(d_msg, sizeof(d_msg), d_value, sizeof(d_value));
|
||||
if(ret <= 0) {
|
||||
dstate_datastale();
|
||||
|
@ -1164,17 +1265,19 @@ void upsdrv_updateinfo(void)
|
|||
}
|
||||
|
||||
dstate_setinfo("input.voltage", "%d",
|
||||
hex2d(d_value+1, 2) * input_voltage_scaled / 120);
|
||||
hex_or_bin2d(d_value+1, 2) * input_voltage_scaled / 120);
|
||||
|
||||
bv = hex2d(d_value+3, 2) * battery_voltage_nominal / 120.0 ;
|
||||
/* TODO: factor out the two constants */
|
||||
bv_12V = hex_or_bin2d(d_value+3, 2) / 10.0 ;
|
||||
battery_voltage = bv_12V * battery_voltage_nominal / 12.0;
|
||||
|
||||
dstate_setinfo("battery.voltage", "%.2f", bv);
|
||||
dstate_setinfo("battery.voltage", "%.2f", battery_voltage);
|
||||
|
||||
/* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */
|
||||
|
||||
ret = send_cmd(m_msg, sizeof(m_msg), m_value, sizeof(m_value));
|
||||
|
||||
if(m_value[5] != 0x0d) { /* we only expect 4 hex digits */
|
||||
if(m_value[5] != 0x0d) { /* we only expect 4 hex/binary digits */
|
||||
dstate_setinfo("ups.debug.M", "%s", hexascdump(m_value+1, 7));
|
||||
}
|
||||
|
||||
|
@ -1184,8 +1287,8 @@ void upsdrv_updateinfo(void)
|
|||
return;
|
||||
}
|
||||
|
||||
dstate_setinfo("input.voltage.minimum", "%3d", hex2d(m_value+1, 2));
|
||||
dstate_setinfo("input.voltage.maximum", "%3d", hex2d(m_value+3, 2));
|
||||
dstate_setinfo("input.voltage.minimum", "%3d", hex_or_bin2d(m_value+1, 2) * input_voltage_scaled / 120);
|
||||
dstate_setinfo("input.voltage.maximum", "%3d", hex_or_bin2d(m_value+3, 2) * input_voltage_scaled / 120);
|
||||
|
||||
/* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */
|
||||
|
||||
|
@ -1216,8 +1319,30 @@ void upsdrv_updateinfo(void)
|
|||
dstate_setinfo("input.frequency", "%.1f", freq / 10.0);
|
||||
}
|
||||
|
||||
/* I'm guessing this is a calibration constant of some sort. */
|
||||
dstate_setinfo("ups.temperature", "%.1f", (unsigned)(hex2d(t_value+1, 2)) * 0.3636 - 21);
|
||||
if( tl_model == TRIPP_LITE_SMART_3005 ) {
|
||||
dstate_setinfo("ups.temperature", "%d", (unsigned)(hex2d(t_value+1, 1)));
|
||||
} else {
|
||||
/* I'm guessing this is a calibration constant of some sort. */
|
||||
dstate_setinfo("ups.temperature", "%.1f", (unsigned)(hex2d(t_value+1, 2)) * 0.3636 - 21);
|
||||
}
|
||||
}
|
||||
|
||||
/* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */
|
||||
|
||||
if( tl_model == TRIPP_LITE_OMNIVS || tl_model == TRIPP_LITE_OMNIVS_2001 ||
|
||||
tl_model == TRIPP_LITE_SMARTPRO || tl_model == TRIPP_LITE_SMART_0004 ) {
|
||||
/* 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. */
|
||||
if (bv_12V >= V_interval[1])
|
||||
bp = 100;
|
||||
else if (bv_12V <= V_interval[0])
|
||||
bp = 10;
|
||||
else
|
||||
bp = (int)(100*sqrt((bv_12V - V_interval[0])
|
||||
/ (V_interval[1] - V_interval[0])));
|
||||
|
||||
dstate_setinfo("battery.charge", "%3d", bp);
|
||||
}
|
||||
|
||||
/* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */
|
||||
|
@ -1232,7 +1357,7 @@ void upsdrv_updateinfo(void)
|
|||
switch(tl_model) {
|
||||
case TRIPP_LITE_OMNIVS:
|
||||
case TRIPP_LITE_OMNIVS_2001:
|
||||
dstate_setinfo("output.voltage", "%.1f", hex2d(l_value+1, 4)/2.0);
|
||||
dstate_setinfo("output.voltage", "%.1f", hex2d(l_value+1, 4)/240.0*input_voltage_scaled);
|
||||
break;
|
||||
case TRIPP_LITE_SMARTPRO:
|
||||
dstate_setinfo("ups.load", "%d", hex2d(l_value+1, 2));
|
||||
|
@ -1272,16 +1397,20 @@ void upsdrv_makevartable(void)
|
|||
{
|
||||
char msg[256];
|
||||
|
||||
snprintf(msg, sizeof msg, "Set shutdown delay, in seconds (default=%d).",
|
||||
snprintf(msg, sizeof msg, "Set shutdown delay, in seconds (default=%d)",
|
||||
DEFAULT_OFFDELAY);
|
||||
addvar(VAR_VALUE, "offdelay", msg);
|
||||
|
||||
/* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */
|
||||
addvar(VAR_VALUE, "vendor", "Regular expression to match UPS Manufacturer string");
|
||||
addvar(VAR_VALUE, "product", "Regular expression to match UPS Product string");
|
||||
addvar(VAR_VALUE, "serial", "Regular expression to match UPS Serial number");
|
||||
addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product numerical ID (4 digits hexadecimal)");
|
||||
addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name");
|
||||
/* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */
|
||||
nut_usb_addvars();
|
||||
|
||||
snprintf(msg, sizeof msg, "Minimum battery voltage, corresponding to 10%% charge (default=%.1f)",
|
||||
MIN_VOLT);
|
||||
addvar(VAR_VALUE, "battery_min", msg);
|
||||
|
||||
snprintf(msg, sizeof msg, "Maximum battery voltage, corresponding to 100%% charge (default=%.1f)",
|
||||
MAX_VOLT);
|
||||
addvar(VAR_VALUE, "battery_max", msg);
|
||||
|
||||
#if 0
|
||||
snprintf(msg, sizeof msg, "Set start delay, in seconds (default=%d).",
|
||||
|
@ -1301,6 +1430,7 @@ void upsdrv_makevartable(void)
|
|||
void upsdrv_initups(void)
|
||||
{
|
||||
char *regex_array[6];
|
||||
char *value;
|
||||
int r;
|
||||
|
||||
/* process the UPS selection options */
|
||||
|
@ -1343,8 +1473,24 @@ void upsdrv_initups(void)
|
|||
/* link the two matchers */
|
||||
reopen_matcher->next = regex_matcher;
|
||||
|
||||
if (getval("offdelay"))
|
||||
offdelay = atoi(getval("offdelay"));
|
||||
value = getval("offdelay");
|
||||
if (value) {
|
||||
offdelay = atoi(value);
|
||||
upsdebugx(2, "Setting 'offdelay' to %d", offdelay);
|
||||
}
|
||||
|
||||
value = getval("battery_min");
|
||||
if (value) {
|
||||
V_interval[0] = atof(value);
|
||||
upsdebugx(2, "Setting 'battery_min' to %.g", V_interval[0]);
|
||||
}
|
||||
|
||||
value = getval("battery_max");
|
||||
if (value) {
|
||||
V_interval[1] = atof(value);
|
||||
upsdebugx(2, "Setting 'battery_max' to %.g", V_interval[1]);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (getval("startdelay"))
|
||||
startdelay = atoi(getval("startdelay"));
|
||||
|
|
|
@ -93,4 +93,6 @@ typedef struct {
|
|||
int is_usb_device_supported(usb_device_id_t *usb_device_id_list,
|
||||
USBDevice_t *device);
|
||||
|
||||
void nut_usb_addvars(void);
|
||||
|
||||
#endif /* NUT_USB_COMMON_H */
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
*/
|
||||
|
||||
#define DRIVER_NAME "Generic HID driver"
|
||||
#define DRIVER_VERSION "0.38"
|
||||
#define DRIVER_VERSION "0.39"
|
||||
|
||||
#include "main.h"
|
||||
#include "libhid.h"
|
||||
|
@ -737,14 +737,12 @@ void upsdrv_makevartable(void)
|
|||
|
||||
#ifndef SHUT_MODE
|
||||
/* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */
|
||||
addvar(VAR_VALUE, "vendor", "Regular expression to match UPS Manufacturer string");
|
||||
addvar(VAR_VALUE, "product", "Regular expression to match UPS Product string");
|
||||
addvar(VAR_VALUE, "serial", "Regular expression to match UPS Serial number");
|
||||
addvar(VAR_VALUE, "vendorid", "Regular expression to match UPS Manufacturer numerical ID (4 digits hexadecimal)");
|
||||
addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product numerical ID (4 digits hexadecimal)");
|
||||
addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name");
|
||||
nut_usb_addvars();
|
||||
|
||||
addvar(VAR_FLAG, "explore", "Diagnostic matching of unsupported UPS");
|
||||
addvar(VAR_FLAG, "maxreport", "Activate tweak for buggy APC Back-UPS firmware");
|
||||
addvar(VAR_FLAG, "interruptonly", "Don't use polling, only use interrupt pipe");
|
||||
addvar(VAR_VALUE, "interruptsize", "Number of bytes to read from interrupt pipe");
|
||||
#else
|
||||
addvar(VAR_VALUE, "notification", "Set notification type, (ignored, only for backward compatibility)");
|
||||
#endif
|
||||
|
@ -792,7 +790,23 @@ void upsdrv_updateinfo(void)
|
|||
/* Get HID notifications on Interrupt pipe first */
|
||||
if (use_interrupt_pipe == TRUE) {
|
||||
evtCount = HIDGetEvents(udev, event, MAX_EVENT_NUM);
|
||||
upsdebugx(1, "Got %i HID objects...", (evtCount >= 0) ? evtCount : 0);
|
||||
switch (evtCount)
|
||||
{
|
||||
case -EBUSY: /* Device or resource busy */
|
||||
upslog_with_errno(LOG_CRIT, "Got disconnected by another driver");
|
||||
case -EPERM: /* Operation not permitted */
|
||||
case -ENODEV: /* No such device */
|
||||
case -EACCES: /* Permission denied */
|
||||
case -EIO: /* I/O error */
|
||||
case -ENXIO: /* No such device or address */
|
||||
case -ENOENT: /* No such file or directory */
|
||||
/* Uh oh, got to reconnect! */
|
||||
hd = NULL;
|
||||
return;
|
||||
default:
|
||||
upsdebugx(1, "Got %i HID objects...", (evtCount >= 0) ? evtCount : 0);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
evtCount = 0;
|
||||
upsdebugx(1, "Not using interrupt pipe...");
|
||||
|
@ -812,7 +826,7 @@ void upsdrv_updateinfo(void)
|
|||
}
|
||||
|
||||
/* Skip Input reports, if we don't use the Feature report */
|
||||
item = find_hid_info(FindObject_with_Path(pDesc, &(event[i]->Path), ITEM_FEATURE));
|
||||
item = find_hid_info(FindObject_with_Path(pDesc, &(event[i]->Path), interrupt_only ? ITEM_INPUT:ITEM_FEATURE));
|
||||
if (!item) {
|
||||
upsdebugx(3, "NUT doesn't use this HID object");
|
||||
continue;
|
||||
|
@ -948,6 +962,15 @@ void upsdrv_initups(void)
|
|||
upsdebugx(1, "Detected a UPS: %s/%s", hd->Vendor ? hd->Vendor : "unknown",
|
||||
hd->Product ? hd->Product : "unknown");
|
||||
|
||||
/* Activate Powercom tweaks */
|
||||
if (testvar("interruptonly")) {
|
||||
interrupt_only = 1;
|
||||
}
|
||||
val = getval("interruptsize");
|
||||
if (val) {
|
||||
interrupt_size = atoi(val);
|
||||
}
|
||||
|
||||
if (hid_ups_walk(HU_WALKMODE_INIT) == FALSE) {
|
||||
fatalx(EXIT_FAILURE, "Can't initialize data from HID UPS");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue