Hi,
Thanks for the update, but this patch still not contains the fixes for:
- usb serial cable
- and the shutdown process to properly shutdown the ups.
I merged your patch with our patch (and test it) and attach it to this
mail. IMHO it sould have to apply upstream.
Can someone apply it?
Thanks.

On 02/28/2011 02:22 PM, Spiros Ioannou wrote:
> Just wondering,
> Why haven't you included my changes of the liebert-esp2.c?
> 
> Summary is here:
> http://lists.alioth.debian.org/pipermail/nut-upsdev/2010-April/004802.html
> 
> -support for 1 & 3-phase input & output (and all combinations)
> -support for most input & output & bypass & nominal variables
> (current, power (watt+VA), voltage, frequency)
> -support for value multiplier tables
> 
> -Spiros
> 
> 
> On Fri, Jan 28, 2011 at 12:11, Farkas Levente <[email protected]> wrote:
>> hi,
>> it seems our latest liebertgxt2 patch still missing from the just
>> released 2.6.0. i'm attached the patch to the 2.6.0. this patch:
>> - add the possibility to properly shutdown the ups,
>> - can be used through usb-serial adapter cable too.
>> could you apply it to the source.
>> regards.
>>
>> --
>>  Levente                               "Si vis pacem para bellum!"
>>
>> _______________________________________________
>> Nut-upsdev mailing list
>> [email protected]
>> http://lists.alioth.debian.org/mailman/listinfo/nut-upsdev
>>


-- 
  Levente                               "Si vis pacem para bellum!"
/* liebert-esp2.c - driver for Liebert UPS, using the ESP-II protocol
 *
 *  Copyright (C)
 *  2009	Richard Gregory <r.gregory liverpool ac uk>
 *
 *  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 "serial.h"
#include "timehead.h"
#include "nut_stdint.h"

#define sivann
#define IsBitSet(val, bit) ((val) & (1 << (bit)))

#define DRIVER_NAME	"Liebert ESP-II serial UPS driver"
#define DRIVER_VERSION	"0.03"
#define UPS_SHUTDOWN_DELAY 12 //it means UPS will be shutdown 120 sec
#define SHUTDOWN_CMD_LEN  8

/* values for sending to UPS */
enum mult_enum {
        M_10,
        M_0_1,
        M_VOLTAGE_I,
        M_VOLTAGE_O,
        M_VOLTAGE_B,
        M_CURRENT_I,
        M_CURRENT_O,
        M_CURRENT_B,
        M_LOAD_VA,
        M_LOAD_WATT,
        M_FREQUENCY,
        M_VOLT_DC,
        M_TEMPERATURE,
        M_CURRENT_DC ,
        M_BAT_RUNTIME,
        M_NOMPOWER,
        M_POWER,
        M_REALPOWER,
        M_LOADPERC      
};

static float multi[19]={
  10.0, 
  0.1, 
  0.1, /* volt */
  0.1,
  0.1,
  0.1, /* curr */
  0.1,
  0.1,
  100.0, /* va */
  100.0, /* W */
  0.01,  /* FREQ */
  0.1,   /* V DC*/
  0.1,   /* TEMP*/
  0.01,  /* CUR DC*/
  60.0,  /* BAT RUNTIME*/
  100.0, /* NOMPOWER*/
  100.0, /* POWER*/
  100.0, /* REAL POWER*/
  1.0    /* LOADPERC*/
};

static int instcmd(const char *cmdname, const char *extra);
static int setvar(const char *varname, const char *val);

/* driver description structure */
upsdrv_info_t upsdrv_info = {
	DRIVER_NAME,
	DRIVER_VERSION,
	"Richard Gregory <r.gregory liv ac uk>\n" \
	"Robert Jobbagy <jobbagy.robert at gmail dot com",
	DRV_EXPERIMENTAL,
	{ NULL }
};

static const unsigned char
	/* Bit field information provided by Spiros Ioannou */
	/* Ordered on MSB to LSB. Shown as DESCRIPTION (bit number), starting at 0. */
	cmd_bitfield1[]		= { 1,148,2,1,1,153 },	/* ON_BATTERY(8), INPUT_OVERVOLTAGE(7), BATTERY_TEST_STATE(6), OVERTEMP_WARNING(5), INRUSH_LIMIT_ON(4), UTILITY_STATE(3), ON_INVERTER(2), DC_DC_CONVERTER_STATE(1), PFC_ON(0) */
	cmd_bitfield2[]		= { 1,148,2,1,2,154 },	/* BUCK_ON (9), DIAG_LINK_SET(7), BOOST_ON(6), REPLACE_BATTERY(5), BATTERY_LIFE_ENHANCER_ON(4), BATTERY_CHARGED (1), ON_BYPASS (0) */
	cmd_bitfield3[]		= { 1,148,2,1,3,155 },	/* CHECK_AIR_FILTER (10), BAD_BYPASS_PWR (8), OUTPUT_OVERVOLTAGE (7), OUTPUT_UNDERVOLTAGE (6), LOW_BATTERY (5), CHARGER_FAIL (3), SHUTDOWN_PENDING (2), BAD_INPUT_FREQ (1), UPS_OVERLOAD (0) */
	cmd_bitfield7[]		= { 1,148,2,1,7,159 },	/* AMBIENT_OVERTEMP (2) */
	cmd_battestres[]	= { 1,148,2,1,12,164 },	/* BATTERY_TEST_RESULT */
	cmd_selftestres[]	= { 1,148,2,1,13,165 },	/* SELF_TEST_RESULT */
	cmd_upstype[] 		= { 1,136,2,1,1,141},   /* type bits + number of phases in bit groups*/
	cmd_scaling1[] 		= { 1,131,2,1,2,137},   /* part of multiplier information*/

	/* Shutdown commands by Robert Jobbagy */	
	cmd_setOutOffMode[]	= { 1,156,4,1,6,0,1,169},/* UPS OutOffMode command */
	cmd_setOutOffDelay[] = {1,156,4,1,5,0,UPS_SHUTDOWN_DELAY,167+UPS_SHUTDOWN_DELAY}, /* UPS Shutdown with delay */
	cmd_sysLoadKey[]    = {1,156,2,1,7,167}, /*UPS SysLoadKey */
	cmd_shutdown[]		= {1,156,4,1,136,76,76,194}; /* UPS shutdown */

static int num_inphases = 1, num_outphases = 1;

static char cksum(const char *buf, const size_t len)
 {
	char	sum = 0;
	size_t	i;

	for (i = 0; i < len; i++) {
		sum += buf[i];
	}

	return sum;
}

static int do_command(const unsigned char *command, char *reply, int cmd_len)
{
	int	ret;

	ret = ser_send_buf(upsfd, command, cmd_len);
	if (ret < 0) {
		upsdebug_with_errno(2, "send");
		return -1;
	} else if (ret < cmd_len) {
		upsdebug_hex(2, "send: truncated", command, ret);
		return -1;
	}

	upsdebug_hex(2, "send", command, ret);

	ret = ser_get_buf_len(upsfd, reply, 8, 1, 0);//it needs that this driver works with USB to Serial cable
	if (ret < 0) {
		upsdebug_with_errno(2, "read");
		return -1;
	} else if (ret < 6) {
		upsdebug_hex(2, "read: truncated", reply, ret);
		return -1;
	} else if (reply[7] != cksum(reply, 7)) {
		upsdebug_hex(2, "read: checksum error", reply, ret);
		return -1;
	}

	upsdebug_hex(2, "read", reply, ret);
	return ret;
}

void upsdrv_initinfo(void)
{
	struct {
		const char	*var;
		unsigned char	len;
	} vartab[] = {
		{ "ups.model",15 },
		{ "ups.firmware",8 },
		{ "ups.serial",10 },
		{ "ups.mfr.date",4 },
		{ NULL }
	};

	char	buf[LARGEBUF];
	int	i,bitn,vari,ret=0,offset=4,readok=0;
		char	command[6], reply[8];
	unsigned int	value;

	dstate_setinfo("ups.mfr", "%s", "Liebert");

	for (vari = 0; vartab[vari].var; vari++) {
		upsdebugx(1, "reading: %s", vartab[vari].var);

		for (i = 0; i < vartab[vari].len; i++) {
			snprintf(command, sizeof(command), "\x01\x88\x02\x01%c", i+offset);
		command[5] = cksum(command, 5);

		ret = do_command((unsigned char *)command, reply, 6);
			if (ret < 8) {
				upsdebug_hex(2, "send: truncated", command, ret);
				break;
			}

			buf[i<<1] = reply[6];
			buf[(i<<1)+1] = reply[5];
	}

	buf[i<<1] = 0;
		upsdebugx(1, "return: %d (8=success)", ret);

		if (ret == 8) { /* last command successful */
			dstate_setinfo(vartab[vari].var,"%s",buf);
			readok++;
		}
		offset+=vartab[vari].len;
	} /* for */
	if (!readok) {
		fatalx(EXIT_FAILURE, "ESP-II capable UPS not detected");
	}

	/* determine number of input & output phases and ups type */
	memcpy(command,cmd_upstype,6);
	ret = do_command((unsigned char *)command, reply, 6);
	if (ret < 8) {
	    upsdebug_hex(2, "send: phase detection: truncated", command, ret);
	}
	else {
	      /* input: from bit 0 to bit 1 (2 bits) */
	      for (value=0,bitn=0;bitn<2;bitn++) {
		    if (IsBitSet(reply[6],(unsigned short int)bitn)) /* bit range measurement on LSByte*/
			  value+=(1<<(unsigned short int)(bitn - 0));
	      }
	      num_inphases=value;
	      dstate_setinfo("input.phases", "%d", value);

	      /* output: from bit 4 to bit 5  (2 bits)*/
	      for (value=0,bitn=4;bitn<6;bitn++) {
		    if (IsBitSet(reply[6],(unsigned short int)bitn)) /* bit range measurement on LSByte*/
			  value+=(1<<(unsigned short int)(bitn - 4));
	      }
	      num_outphases=value;
	      dstate_setinfo("output.phases", "%d", value);

	      if (reply[5] & (1<<4)) {	/* ISOFFLINE */
		    dstate_setinfo("ups.type", "offline") ;
	      }
	      else if (reply[5] & (1<<5)) { /* ISINTERACTIVE */
		    dstate_setinfo("ups.type", "line-interactive") ;
	      }
	      else {
		    dstate_setinfo("ups.type", "online") ;
	      }
	}

	/* determine scaling */
	/* full scaling output not defined yet, but we can differentiate sets of 
	 * multipliers based on a sample scaling reading */
	memcpy(command,cmd_scaling1,6);
	ret = do_command((unsigned char *)command, reply, 6);
	if (ret < 8) {
	    upsdebug_hex(2, "send: scaling detection: truncated", command, ret);
	}
	else { /* add here multipliers that differentiate between models */
	  switch (reply[6]) {
	    case 1: /* GXT-2 */
	      multi[M_FREQUENCY]=0.1;
	      multi[M_VOLT_DC]=1.0;
	      multi[M_POWER]=1.0;
	      multi[M_NOMPOWER]=1.0;
	      break;
	    case 2: /* NXe */
	      multi[M_FREQUENCY]=0.01;
	      multi[M_VOLT_DC]=0.1;
	      multi[M_POWER]=100.0;
	      multi[M_NOMPOWER]=100.0;
	      break;
	    default: /* the default values from definition of multi will be used */
	      break;
	  }
	}

	upsh.instcmd = instcmd;
	upsh.setvar = setvar;
}

void upsdrv_updateinfo(void)
{
	typedef struct {
		const unsigned char	cmd[6];
		const char	*var;
		const char	*fmt;
		const int	multindex;
	} cmd_s;

	static cmd_s vartab[] = { /* common vars */
		{ { 1,149,2,1,1,154 },	"battery.runtime", "%.0f", M_BAT_RUNTIME },
		{ { 1,149,2,1,2,155 },	"battery.voltage", "%.1f", M_VOLT_DC },
		{ { 1,149,2,1,3,156 },	"battery.current", "%.2f", M_CURRENT_DC },
		{ { 1,161,2,1,13,178 },	"battery.voltage.nominal", "%.1f", M_VOLT_DC },
		{ { 1,149,2,1,12,165 },	"battery.temperature", "%.1f", M_TEMPERATURE },
		{ { 1,149,2,1,14,167 },	"ups.temperature", "%.1f", M_TEMPERATURE },
		{ { 1,161,2,1,8,173 },	"ups.power.nominal", "%.0f", M_NOMPOWER },
		{ { 1,161,2,1,4,169 },	"ups.delay.start", "%.0f", M_10 },
		{ { 1,161,2,1,14,179  },"battery.runtime.low", "%.0f", M_BAT_RUNTIME },
		{ { 1,149,2,1,8,161 },	"input.frequency", "%.1f", M_FREQUENCY },
		{ { 1,149,2,1,10,163 },	"input.bypass.frequency", "%.1f", M_FREQUENCY },
		{ { 1,161,2,1,9,174 },	"input.frequency.nominal", "%.1f", M_FREQUENCY },
		{ { 1,149,2,1,9,162 },	"output.frequency", "%.1f", M_FREQUENCY },
		{ { 1,161,2,1,10,175 },	"output.frequency.nominal", "%.1f", M_FREQUENCY },
		{ { 0 }, NULL, NULL, 0 }
	};

	static cmd_s vartab1o[] = { /* 1-phase out */
		{ { 1,149,2,1,7,160 },	"ups.load", "%.0f", M_LOADPERC },
		{ { 1,149,2,1,6,159 },	"ups.power", "%.0f", M_POWER },
		{ { 1,149,2,1,5,158 },	"ups.realpower", "%.0f", M_POWER },
		{ { 1,144,2,1,3,151 },	"output.voltage", "%.1f", M_VOLTAGE_O },
		{ { 1,144,2,1,4,152 },	"output.current", "%.1f", M_CURRENT_O },
		{ { 0 }, NULL, NULL, 0 }
	};

	static cmd_s vartab1i[] = { /* 1-phase in*/
		{ { 1,144,2,1,1,149 },	"input.voltage", "%.1f", M_VOLTAGE_I },
		{ { 1,144,2,1,5,153 },	"input.bypass.voltage", "%.1f", M_VOLTAGE_B },
		{ { 1,144,2,1,6,154 },	"input.bypass.current", "%.1f", M_CURRENT_B },
		{ { 0 }, NULL, NULL, 0 }
	};

	static cmd_s vartab3o[] = { /*3-phase out */
		{ { 1,144,2,1,24,172 },	"ups.L1.load", "%.0f", M_LOADPERC },
		{ { 1,145,2,1,24,173 },	"ups.L2.load", "%.0f", M_LOADPERC },
		{ { 1,146,2,1,24,174 },	"ups.L3.load", "%.0f", M_LOADPERC },
		{ { 1,144,2,1,22,170 },	"ups.L1.power", "%.0f", M_POWER },
		{ { 1,145,2,1,22,171 },	"ups.L2.power", "%.0f", M_POWER },
		{ { 1,146,2,1,22,172 },	"ups.L3.power", "%.0f", M_POWER },
		{ { 1,144,2,1,21,169 },	"ups.L1.realpower", "%.0f", M_POWER },
		{ { 1,145,2,1,21,170 },	"ups.L2.realpower", "%.0f", M_POWER },
		{ { 1,146,2,1,21,171 },	"ups.L3.realpower", "%.0f", M_POWER },
		{ { 1,144,2,1,3,151 },	"output.L1-N.voltage", "%.1f", M_VOLTAGE_O },
		{ { 1,145,2,1,3,152 },	"output.L2-N.voltage", "%.1f", M_VOLTAGE_O },
		{ { 1,146,2,1,3,153 },	"output.L3-N.voltage", "%.1f", M_VOLTAGE_O },
		{ { 1,144,2,1,14,162 },	"output.L1.crestfactor", "%.1f", M_0_1 },
		{ { 1,145,2,1,14,163 },	"output.L2.crestfactor", "%.1f", M_0_1 },
		{ { 1,146,2,1,14,164 },	"output.L3.crestfactor", "%.1f", M_0_1 },
		{ { 0 }, NULL, NULL, 0 }
	};

	static cmd_s vartab3i[] = { /*3-phase in */
		{ { 1,144,2,1,1,149 },	"input.L1-N.voltage", "%.1f", M_VOLTAGE_I },
		{ { 1,145,2,1,1,150 },	"input.L2-N.voltage", "%.1f", M_VOLTAGE_I },
		{ { 1,146,2,1,1,151 },	"input.L3-N.voltage", "%.1f", M_VOLTAGE_I },

		{ { 1,144,2,1,5,153 },	"input.L1-N.bypass.voltage", "%.1f", M_VOLTAGE_B },
		{ { 1,145,2,1,5,154 },	"input.L2-N.bypass.voltage", "%.1f", M_VOLTAGE_B },
		{ { 1,146,2,1,5,155 },	"input.L3-N.bypass.voltage", "%.1f", M_VOLTAGE_B },

		{ { 1,144,2,1,6,154 },	"input.L1-N.bypass.current", "%.1f", M_CURRENT_B },
		{ { 1,145,2,1,6,155 },	"input.L2-N.bypass.current", "%.1f", M_CURRENT_B },
		{ { 1,146,2,1,6,156 },	"input.L3-N.bypass.current", "%.1f", M_CURRENT_B },

		{ { 1,144,2,1,2,150 },	"input.L1.current", "%.1f", M_CURRENT_I },
		{ { 1,145,2,1,2,151 },	"input.L2.current", "%.1f", M_CURRENT_I },
		{ { 1,146,2,1,2,152 },	"input.L3.current", "%.1f", M_CURRENT_I },
		{ { 0 }, NULL, NULL, 0 }
	};
	
	static cmd_s * cmdin_p;
	static cmd_s * cmdout_p;

	const char	*val;
	char	reply[8];
	int	ret, i;

	for (i = 0; vartab[i].var; i++) {
		int16_t	val;
		ret = do_command(vartab[i].cmd, reply, 6);
		if (ret < 8) {
			continue;
		}
		val = (unsigned char)reply[5];
		val <<= 8;
		val += (unsigned char)reply[6];
		dstate_setinfo(vartab[i].var, vartab[i].fmt, val * multi[vartab[i].multindex]);
	}

	if (num_inphases>1){
	    cmdin_p=vartab3i;
	} 
	else {
	    cmdin_p=vartab1i;
	}

	if (num_outphases>1){
	    cmdout_p=vartab3o;
	}
	else {
	    cmdout_p=vartab1o;
	}

	for (i = 0; cmdin_p[i].var; i++) {
		int16_t	val;
		ret = do_command(cmdin_p[i].cmd, reply, 6);
		if (ret < 8) {
			continue;
		}
		val = (unsigned char)reply[5];
		val <<= 8;
		val += (unsigned char)reply[6];
		dstate_setinfo(cmdin_p[i].var, cmdin_p[i].fmt, val * multi[cmdin_p[i].multindex]);
	}

	for (i = 0; cmdout_p[i].var; i++) {
		int16_t	val;
		ret = do_command(cmdout_p[i].cmd, reply, 6);
		if (ret < 8) {
			continue;
		}
		val = (unsigned char)reply[5];
		val <<= 8;
		val += (unsigned char)reply[6];
		dstate_setinfo(cmdout_p[i].var, cmdout_p[i].fmt, val * multi[cmdout_p[i].multindex]);
	}

	status_init();

	ret = do_command(cmd_bitfield1, reply, 6);
	if (ret < 8) {
		upslogx(LOG_ERR, "Failed reading bitfield #1");
		dstate_datastale();
		return;
	}

	if (reply[5] & (1<<0)) {	/* ON_BATTERY */
		status_set("OB");
	} else {
		status_set("OL");
	}

	val = dstate_getinfo("battery.current");
	if (val) {
		if (atof(val) > 0.05) {
			status_set("CHRG");
		}
		if (atof(val) < -0.05) {
			status_set("DISCHRG");
		}
	}

	ret = do_command(cmd_bitfield2, reply, 6);
	if (ret < 8) {
		upslogx(LOG_ERR, "Failed reading bitfield #2");
		dstate_datastale();
		return;
	}

	if (reply[6] & (1<<0)) {	/* ON_BYPASS */
		status_set("BYPASS");
	}

	if (reply[6] & (1<<5)) {	/* REPLACE_BATTERY */
		status_set("RB");
	}

	if (reply[6] & (1<<6)) {	/* BOOST_ON */
		status_set("BOOST");
	}

	if (reply[5] & (1<<1)) {	/* BUCK_ON */
		status_set("TRIM");
	}

	ret = do_command(cmd_bitfield3, reply, 6);
	if (ret < 8) {
		upslogx(LOG_ERR, "Failed reading bitfield #3");
		dstate_datastale();
		return;
	}

	if (reply[6] & (1<<0) ) {	/* UPS_OVERLOAD */
		status_set("OVER");
	}

	if (reply[6] & (1<<5) ) {	/* LOW_BATTERY */
		status_set("LB");
	}

	status_commit();

	dstate_dataok();
}

void upsdrv_shutdown(void)
{
	char reply[8];
	
	if(!(do_command(cmd_setOutOffMode, reply, 8) != -1) &&
	(do_command(cmd_setOutOffDelay, reply, 8) != -1) &&
	(do_command(cmd_sysLoadKey, reply, 6) != -1) &&
	(do_command(cmd_shutdown, reply, 8) != -1))
	upslogx(LOG_ERR, "Failed to shutdown UPS");
}

static int instcmd(const char *cmdname, const char *extra)
{
/*
	if (!strcasecmp(cmdname, "test.battery.stop")) {
		ser_send_buf(upsfd, ...);
		return STAT_INSTCMD_HANDLED;
	}
 */
	upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
	return STAT_INSTCMD_UNKNOWN;
}

static int setvar(const char *varname, const char *val)
{
/*
	if (!strcasecmp(varname, "ups.test.interval")) {

	ser_send_buf(upsfd, ...);
		return STAT_SET_HANDLED;
	}
 */
	upslogx(LOG_NOTICE, "setvar: unknown variable [%s]", varname);
	return STAT_SET_UNKNOWN;
}

void upsdrv_help(void)
{
}

/* list flags and values that you want to receive via -x */
void upsdrv_makevartable(void)
{
	addvar (VAR_VALUE, "baudrate", "serial line speed");
}

void upsdrv_initups(void)
{
	const char *val = getval("baudrate");
	speed_t baudrate = B2400;

	if (val) {
		switch (atoi(val))
		{
		case 1200:
			baudrate = B1200;
			break;
		case 2400:
			baudrate = B2400;
			break;
		case 4800:
			baudrate = B4800;
			break;
		case 9600:
			baudrate = B9600;
			break;
		case 19200:
			baudrate = B19200;
			break;
		default:
			fatalx(EXIT_FAILURE, "Baudrate [%s] unsupported", val);
		}
	}

	upsfd = ser_open(device_path);
	ser_set_speed(upsfd, device_path, baudrate);
}

void upsdrv_cleanup(void)
{
	ser_close(upsfd, device_path);
}
diff -up ./drivers/liebert-esp2.c.esp2 ./drivers/liebert-esp2.c
--- ./drivers/liebert-esp2.c.esp2       2010-12-24 14:24:29.000000000 +0100
+++ ./drivers/liebert-esp2.c    2011-02-28 15:27:01.000000000 +0100
@@ -23,8 +23,58 @@
 #include "timehead.h"
 #include "nut_stdint.h"
 
+#define sivann
+#define IsBitSet(val, bit) ((val) & (1 << (bit)))
+
 #define DRIVER_NAME    "Liebert ESP-II serial UPS driver"
-#define DRIVER_VERSION "0.02"
+#define DRIVER_VERSION "0.03"
+#define UPS_SHUTDOWN_DELAY 12 //it means UPS will be shutdown 120 sec
+#define SHUTDOWN_CMD_LEN  8
+
+/* values for sending to UPS */
+enum mult_enum {
+        M_10,
+        M_0_1,
+        M_VOLTAGE_I,
+        M_VOLTAGE_O,
+        M_VOLTAGE_B,
+        M_CURRENT_I,
+        M_CURRENT_O,
+        M_CURRENT_B,
+        M_LOAD_VA,
+        M_LOAD_WATT,
+        M_FREQUENCY,
+        M_VOLT_DC,
+        M_TEMPERATURE,
+        M_CURRENT_DC ,
+        M_BAT_RUNTIME,
+        M_NOMPOWER,
+        M_POWER,
+        M_REALPOWER,
+        M_LOADPERC      
+};
+
+static float multi[19]={
+  10.0, 
+  0.1, 
+  0.1, /* volt */
+  0.1,
+  0.1,
+  0.1, /* curr */
+  0.1,
+  0.1,
+  100.0, /* va */
+  100.0, /* W */
+  0.01,  /* FREQ */
+  0.1,   /* V DC*/
+  0.1,   /* TEMP*/
+  0.01,  /* CUR DC*/
+  60.0,  /* BAT RUNTIME*/
+  100.0, /* NOMPOWER*/
+  100.0, /* POWER*/
+  100.0, /* REAL POWER*/
+  1.0    /* LOADPERC*/
+};
 
 static int instcmd(const char *cmdname, const char *extra);
 static int setvar(const char *varname, const char *val);
@@ -33,7 +83,8 @@ static int setvar(const char *varname, c
 upsdrv_info_t upsdrv_info = {
        DRIVER_NAME,
        DRIVER_VERSION,
-       "Richard Gregory <r.gregory liv ac uk>",
+       "Richard Gregory <r.gregory liv ac uk>\n" \
+       "Robert Jobbagy <jobbagy.robert at gmail dot com",
        DRV_EXPERIMENTAL,
        { NULL }
 };
@@ -46,7 +97,17 @@ static const unsigned char
        cmd_bitfield3[]         = { 1,148,2,1,3,155 },  /* CHECK_AIR_FILTER 
(10), BAD_BYPASS_PWR (8), OUTPUT_OVERVOLTAGE (7), OUTPUT_UNDERVOLTAGE (6), 
LOW_BATTERY (5), CHARGER_FAIL (3), SHUTDOWN_PENDING (2), BAD_INPUT_FREQ (1), 
UPS_OVERLOAD (0) */
        cmd_bitfield7[]         = { 1,148,2,1,7,159 },  /* AMBIENT_OVERTEMP (2) 
*/
        cmd_battestres[]        = { 1,148,2,1,12,164 }, /* BATTERY_TEST_RESULT 
*/
-       cmd_selftestres[]       = { 1,148,2,1,13,165 }; /* SELF_TEST_RESULT */
+       cmd_selftestres[]       = { 1,148,2,1,13,165 }, /* SELF_TEST_RESULT */
+       cmd_upstype[]           = { 1,136,2,1,1,141},   /* type bits + number 
of phases in bit groups*/
+       cmd_scaling1[]          = { 1,131,2,1,2,137},   /* part of multiplier 
information*/
+
+       /* Shutdown commands by Robert Jobbagy */       
+       cmd_setOutOffMode[]     = { 1,156,4,1,6,0,1,169},/* UPS OutOffMode 
command */
+       cmd_setOutOffDelay[] = 
{1,156,4,1,5,0,UPS_SHUTDOWN_DELAY,167+UPS_SHUTDOWN_DELAY}, /* UPS Shutdown with 
delay */
+       cmd_sysLoadKey[]    = {1,156,2,1,7,167}, /*UPS SysLoadKey */
+       cmd_shutdown[]          = {1,156,4,1,136,76,76,194}; /* UPS shutdown */
+
+static int num_inphases = 1, num_outphases = 1;
 
 static char cksum(const char *buf, const size_t len)
  {
@@ -60,22 +121,22 @@ static char cksum(const char *buf, const
        return sum;
 }
 
-static int do_command(const unsigned char *command, char *reply)
+static int do_command(const unsigned char *command, char *reply, int cmd_len)
 {
        int     ret;
 
-       ret = ser_send_buf(upsfd, command, 6);
+       ret = ser_send_buf(upsfd, command, cmd_len);
        if (ret < 0) {
                upsdebug_with_errno(2, "send");
                return -1;
-       } else if (ret < 6) {
+       } else if (ret < cmd_len) {
                upsdebug_hex(2, "send: truncated", command, ret);
                return -1;
        }
 
        upsdebug_hex(2, "send", command, ret);
 
-       ret = ser_get_buf(upsfd, reply, 8, 1, 0);
+       ret = ser_get_buf_len(upsfd, reply, 8, 1, 0);//it needs that this 
driver works with USB to Serial cable
        if (ret < 0) {
                upsdebug_with_errno(2, "read");
                return -1;
@@ -95,49 +156,111 @@ void upsdrv_initinfo(void)
 {
        struct {
                const char      *var;
-               const int       len;
+               unsigned char   len;
        } vartab[] = {
-               { "ups.model", 15 },
-               { "ups.firmware", 8 },
-               { "ups.serial", 10 },
-               { "ups.mfr.date", 4 },
+               { "ups.model",15 },
+               { "ups.firmware",8 },
+               { "ups.serial",10 },
+               { "ups.mfr.date",4 },
                { NULL }
        };
 
        char    buf[LARGEBUF];
-       int     i, index;
+       int     i,bitn,vari,ret=0,offset=4,readok=0;
+               char    command[6], reply[8];
+       unsigned int    value;
 
        dstate_setinfo("ups.mfr", "%s", "Liebert");
 
-       for (i = 0; i < 37; i++) {
-               char    command[6], reply[8];
-               int     ret;
+       for (vari = 0; vartab[vari].var; vari++) {
+               upsdebugx(1, "reading: %s", vartab[vari].var);
 
-               snprintf(command, sizeof(command), "\x01\x88\x02\x01%c", i+4);
+               for (i = 0; i < vartab[vari].len; i++) {
+                       snprintf(command, sizeof(command), 
"\x01\x88\x02\x01%c", i+offset);
                command[5] = cksum(command, 5);
 
-               ret = do_command((unsigned char *)command, reply);
-               if (ret == 8) {
+               ret = do_command((unsigned char *)command, reply, 6);
+                       if (ret < 8) {
+                               upsdebug_hex(2, "send: truncated", command, 
ret);
+                               break;
+                       }
+
                        buf[i<<1] = reply[6];
                        buf[(i<<1)+1] = reply[5];
-               } else {
-                       buf[i<<1] = '\0';
-                       buf[(i<<1)+1] = '\0';
-               }
        }
 
        buf[i<<1] = 0;
+               upsdebugx(1, "return: %d (8=success)", ret);
 
-       if (strlen(buf) == 0) {
+               if (ret == 8) { /* last command successful */
+                       dstate_setinfo(vartab[vari].var,"%s",buf);
+                       readok++;
+               }
+               offset+=vartab[vari].len;
+       } /* for */
+       if (!readok) {
                fatalx(EXIT_FAILURE, "ESP-II capable UPS not detected");
        }
 
-       for (index = 0, i = 0; vartab[i].var; index += vartab[i].len, i++) {
-               char    val[SMALLBUF];
-
-               snprintf(val, sizeof(val), "%.*s", vartab[i].len, &buf[index]);
-
-               dstate_setinfo(vartab[i].var, "%s", rtrim(val, ' '));
+       /* determine number of input & output phases and ups type */
+       memcpy(command,cmd_upstype,6);
+       ret = do_command((unsigned char *)command, reply, 6);
+       if (ret < 8) {
+           upsdebug_hex(2, "send: phase detection: truncated", command, ret);
+       }
+       else {
+             /* input: from bit 0 to bit 1 (2 bits) */
+             for (value=0,bitn=0;bitn<2;bitn++) {
+                   if (IsBitSet(reply[6],(unsigned short int)bitn)) /* bit 
range measurement on LSByte*/
+                         value+=(1<<(unsigned short int)(bitn - 0));
+             }
+             num_inphases=value;
+             dstate_setinfo("input.phases", "%d", value);
+
+             /* output: from bit 4 to bit 5  (2 bits)*/
+             for (value=0,bitn=4;bitn<6;bitn++) {
+                   if (IsBitSet(reply[6],(unsigned short int)bitn)) /* bit 
range measurement on LSByte*/
+                         value+=(1<<(unsigned short int)(bitn - 4));
+             }
+             num_outphases=value;
+             dstate_setinfo("output.phases", "%d", value);
+
+             if (reply[5] & (1<<4)) {  /* ISOFFLINE */
+                   dstate_setinfo("ups.type", "offline") ;
+             }
+             else if (reply[5] & (1<<5)) { /* ISINTERACTIVE */
+                   dstate_setinfo("ups.type", "line-interactive") ;
+             }
+             else {
+                   dstate_setinfo("ups.type", "online") ;
+             }
+       }
+
+       /* determine scaling */
+       /* full scaling output not defined yet, but we can differentiate sets 
of 
+        * multipliers based on a sample scaling reading */
+       memcpy(command,cmd_scaling1,6);
+       ret = do_command((unsigned char *)command, reply, 6);
+       if (ret < 8) {
+           upsdebug_hex(2, "send: scaling detection: truncated", command, ret);
+       }
+       else { /* add here multipliers that differentiate between models */
+         switch (reply[6]) {
+           case 1: /* GXT-2 */
+             multi[M_FREQUENCY]=0.1;
+             multi[M_VOLT_DC]=1.0;
+             multi[M_POWER]=1.0;
+             multi[M_NOMPOWER]=1.0;
+             break;
+           case 2: /* NXe */
+             multi[M_FREQUENCY]=0.01;
+             multi[M_VOLT_DC]=0.1;
+             multi[M_POWER]=100.0;
+             multi[M_NOMPOWER]=100.0;
+             break;
+           default: /* the default values from definition of multi will be 
used */
+             break;
+         }
        }
 
        upsh.instcmd = instcmd;
@@ -146,55 +269,145 @@ void upsdrv_initinfo(void)
 
 void upsdrv_updateinfo(void)
 {
-       struct {
+       typedef struct {
                const unsigned char     cmd[6];
                const char      *var;
                const char      *fmt;
-               const double    mult;
-       } vartab[] = {
-               { { 1,149,2,1,4,157 },  "battery.charge", "%.0f", 1.0 },
-               { { 1,149,2,1,1,154 },  "battery.runtime", "%.0f", 60 },
-               { { 1,149,2,1,2,155 },  "battery.voltage", "%.1f", 0.1 },
-               { { 1,149,2,1,3,156 },  "battery.current", "%.2f", 0.01 },
-               { { 1,149,2,1,12,165 }, "battery.temperature", "%.1f", 0.1 },
-               { { 1,161,2,1,13,178 }, "battery.voltage.nominal", "%.1f", 0.1 
},
-               { { 1,149,2,1,7,160 },  "ups.load", "%.0f", 1.0 },
-               { { 1,149,2,1,6,159 },  "ups.power", "%.0f", 1.0 },
-               { { 1,161,2,1,8,173 },  "ups.power.nominal", "%.0f", 1.0 },
-               { { 1,149,2,1,5,158 },  "ups.realpower", "%.0f", 1.0 },
-               { { 1,149,2,1,14,167 }, "ups.temperature", "%.1f", 0.1 },
-               { { 1,144,2,1,1,149 },  "input.voltage", "%.1f", 0.1 },
-               { { 1,149,2,1,8,161 },  "input.frequency", "%.1f", 0.1 },
-               { { 1,149,2,1,10,163 }, "input.frequency.nominal", "%.1f", 0.1 
},
-               { { 1,144,2,1,5,153 },  "input.bypass.voltage", "%.1f", 0.1 },
-               { { 1,144,2,1,3,151 },  "output.voltage", "%.1f", 0.1 },
-               { { 1,149,2,1,9,162 },  "output.frequency", "%.1f", 0.1 },
-               { { 1,144,2,1,4,152 },  "output.current", "%.1f", 0.1 },
+               const int       multindex;
+       } cmd_s;
+
+       static cmd_s vartab[] = { /* common vars */
+               { { 1,149,2,1,1,154 },  "battery.runtime", "%.0f", 
M_BAT_RUNTIME },
+               { { 1,149,2,1,2,155 },  "battery.voltage", "%.1f", M_VOLT_DC },
+               { { 1,149,2,1,3,156 },  "battery.current", "%.2f", M_CURRENT_DC 
},
+               { { 1,161,2,1,13,178 }, "battery.voltage.nominal", "%.1f", 
M_VOLT_DC },
+               { { 1,149,2,1,12,165 }, "battery.temperature", "%.1f", 
M_TEMPERATURE },
+               { { 1,149,2,1,14,167 }, "ups.temperature", "%.1f", 
M_TEMPERATURE },
+               { { 1,161,2,1,8,173 },  "ups.power.nominal", "%.0f", M_NOMPOWER 
},
+               { { 1,161,2,1,4,169 },  "ups.delay.start", "%.0f", M_10 },
+               { { 1,161,2,1,14,179  },"battery.runtime.low", "%.0f", 
M_BAT_RUNTIME },
+               { { 1,149,2,1,8,161 },  "input.frequency", "%.1f", M_FREQUENCY 
},
+               { { 1,149,2,1,10,163 }, "input.bypass.frequency", "%.1f", 
M_FREQUENCY },
+               { { 1,161,2,1,9,174 },  "input.frequency.nominal", "%.1f", 
M_FREQUENCY },
+               { { 1,149,2,1,9,162 },  "output.frequency", "%.1f", M_FREQUENCY 
},
+               { { 1,161,2,1,10,175 }, "output.frequency.nominal", "%.1f", 
M_FREQUENCY },
+               { { 0 }, NULL, NULL, 0 }
+       };
+
+       static cmd_s vartab1o[] = { /* 1-phase out */
+               { { 1,149,2,1,7,160 },  "ups.load", "%.0f", M_LOADPERC },
+               { { 1,149,2,1,6,159 },  "ups.power", "%.0f", M_POWER },
+               { { 1,149,2,1,5,158 },  "ups.realpower", "%.0f", M_POWER },
+               { { 1,144,2,1,3,151 },  "output.voltage", "%.1f", M_VOLTAGE_O },
+               { { 1,144,2,1,4,152 },  "output.current", "%.1f", M_CURRENT_O },
+               { { 0 }, NULL, NULL, 0 }
+       };
+
+       static cmd_s vartab1i[] = { /* 1-phase in*/
+               { { 1,144,2,1,1,149 },  "input.voltage", "%.1f", M_VOLTAGE_I },
+               { { 1,144,2,1,5,153 },  "input.bypass.voltage", "%.1f", 
M_VOLTAGE_B },
+               { { 1,144,2,1,6,154 },  "input.bypass.current", "%.1f", 
M_CURRENT_B },
+               { { 0 }, NULL, NULL, 0 }
+       };
+
+       static cmd_s vartab3o[] = { /*3-phase out */
+               { { 1,144,2,1,24,172 }, "ups.L1.load", "%.0f", M_LOADPERC },
+               { { 1,145,2,1,24,173 }, "ups.L2.load", "%.0f", M_LOADPERC },
+               { { 1,146,2,1,24,174 }, "ups.L3.load", "%.0f", M_LOADPERC },
+               { { 1,144,2,1,22,170 }, "ups.L1.power", "%.0f", M_POWER },
+               { { 1,145,2,1,22,171 }, "ups.L2.power", "%.0f", M_POWER },
+               { { 1,146,2,1,22,172 }, "ups.L3.power", "%.0f", M_POWER },
+               { { 1,144,2,1,21,169 }, "ups.L1.realpower", "%.0f", M_POWER },
+               { { 1,145,2,1,21,170 }, "ups.L2.realpower", "%.0f", M_POWER },
+               { { 1,146,2,1,21,171 }, "ups.L3.realpower", "%.0f", M_POWER },
+               { { 1,144,2,1,3,151 },  "output.L1-N.voltage", "%.1f", 
M_VOLTAGE_O },
+               { { 1,145,2,1,3,152 },  "output.L2-N.voltage", "%.1f", 
M_VOLTAGE_O },
+               { { 1,146,2,1,3,153 },  "output.L3-N.voltage", "%.1f", 
M_VOLTAGE_O },
+               { { 1,144,2,1,14,162 }, "output.L1.crestfactor", "%.1f", M_0_1 
},
+               { { 1,145,2,1,14,163 }, "output.L2.crestfactor", "%.1f", M_0_1 
},
+               { { 1,146,2,1,14,164 }, "output.L3.crestfactor", "%.1f", M_0_1 
},
                { { 0 }, NULL, NULL, 0 }
        };
 
+       static cmd_s vartab3i[] = { /*3-phase in */
+               { { 1,144,2,1,1,149 },  "input.L1-N.voltage", "%.1f", 
M_VOLTAGE_I },
+               { { 1,145,2,1,1,150 },  "input.L2-N.voltage", "%.1f", 
M_VOLTAGE_I },
+               { { 1,146,2,1,1,151 },  "input.L3-N.voltage", "%.1f", 
M_VOLTAGE_I },
+
+               { { 1,144,2,1,5,153 },  "input.L1-N.bypass.voltage", "%.1f", 
M_VOLTAGE_B },
+               { { 1,145,2,1,5,154 },  "input.L2-N.bypass.voltage", "%.1f", 
M_VOLTAGE_B },
+               { { 1,146,2,1,5,155 },  "input.L3-N.bypass.voltage", "%.1f", 
M_VOLTAGE_B },
+
+               { { 1,144,2,1,6,154 },  "input.L1-N.bypass.current", "%.1f", 
M_CURRENT_B },
+               { { 1,145,2,1,6,155 },  "input.L2-N.bypass.current", "%.1f", 
M_CURRENT_B },
+               { { 1,146,2,1,6,156 },  "input.L3-N.bypass.current", "%.1f", 
M_CURRENT_B },
+
+               { { 1,144,2,1,2,150 },  "input.L1.current", "%.1f", M_CURRENT_I 
},
+               { { 1,145,2,1,2,151 },  "input.L2.current", "%.1f", M_CURRENT_I 
},
+               { { 1,146,2,1,2,152 },  "input.L3.current", "%.1f", M_CURRENT_I 
},
+               { { 0 }, NULL, NULL, 0 }
+       };
+       
+       static cmd_s * cmdin_p;
+       static cmd_s * cmdout_p;
+
        const char      *val;
        char    reply[8];
        int     ret, i;
 
        for (i = 0; vartab[i].var; i++) {
                int16_t val;
-
-               ret = do_command(vartab[i].cmd, reply);
+               ret = do_command(vartab[i].cmd, reply, 6);
                if (ret < 8) {
                        continue;
                }
+               val = (unsigned char)reply[5];
+               val <<= 8;
+               val += (unsigned char)reply[6];
+               dstate_setinfo(vartab[i].var, vartab[i].fmt, val * 
multi[vartab[i].multindex]);
+       }
 
+       if (num_inphases>1){
+           cmdin_p=vartab3i;
+       } 
+       else {
+           cmdin_p=vartab1i;
+       }
+
+       if (num_outphases>1){
+           cmdout_p=vartab3o;
+       }
+       else {
+           cmdout_p=vartab1o;
+       }
+
+       for (i = 0; cmdin_p[i].var; i++) {
+               int16_t val;
+               ret = do_command(cmdin_p[i].cmd, reply, 6);
+               if (ret < 8) {
+                       continue;
+               }
                val = (unsigned char)reply[5];
                val <<= 8;
                val += (unsigned char)reply[6];
+               dstate_setinfo(cmdin_p[i].var, cmdin_p[i].fmt, val * 
multi[cmdin_p[i].multindex]);
+       }
 
-               dstate_setinfo(vartab[i].var, vartab[i].fmt, val * 
vartab[i].mult);
+       for (i = 0; cmdout_p[i].var; i++) {
+               int16_t val;
+               ret = do_command(cmdout_p[i].cmd, reply, 6);
+               if (ret < 8) {
+                       continue;
+               }
+               val = (unsigned char)reply[5];
+               val <<= 8;
+               val += (unsigned char)reply[6];
+               dstate_setinfo(cmdout_p[i].var, cmdout_p[i].fmt, val * 
multi[cmdout_p[i].multindex]);
        }
 
        status_init();
 
-       ret = do_command(cmd_bitfield1, reply);
+       ret = do_command(cmd_bitfield1, reply, 6);
        if (ret < 8) {
                upslogx(LOG_ERR, "Failed reading bitfield #1");
                dstate_datastale();
@@ -217,7 +430,7 @@ void upsdrv_updateinfo(void)
                }
        }
 
-       ret = do_command(cmd_bitfield2, reply);
+       ret = do_command(cmd_bitfield2, reply, 6);
        if (ret < 8) {
                upslogx(LOG_ERR, "Failed reading bitfield #2");
                dstate_datastale();
@@ -240,7 +453,7 @@ void upsdrv_updateinfo(void)
                status_set("TRIM");
        }
 
-       ret = do_command(cmd_bitfield3, reply);
+       ret = do_command(cmd_bitfield3, reply, 6);
        if (ret < 8) {
                upslogx(LOG_ERR, "Failed reading bitfield #3");
                dstate_datastale();
@@ -262,8 +475,13 @@ void upsdrv_updateinfo(void)
 
 void upsdrv_shutdown(void)
 {
-       /* replace with a proper shutdown function */
-       fatalx(EXIT_FAILURE, "shutdown not supported");
+       char reply[8];
+       
+       if(!(do_command(cmd_setOutOffMode, reply, 8) != -1) &&
+       (do_command(cmd_setOutOffDelay, reply, 8) != -1) &&
+       (do_command(cmd_sysLoadKey, reply, 6) != -1) &&
+       (do_command(cmd_shutdown, reply, 8) != -1))
+       upslogx(LOG_ERR, "Failed to shutdown UPS");
 }
 
 static int instcmd(const char *cmdname, const char *extra)
_______________________________________________
Nut-upsdev mailing list
[email protected]
http://lists.alioth.debian.org/mailman/listinfo/nut-upsdev

Reply via email to