/*vim ts=4*/ /* * NUT Oneac EG and ON model specific drivers for UPS units using * the Oneac Advanced Interface. If your UPS is equipped with the * Oneac Basic Interface, use the genericups driver */ /* Copyright (C) 2003 by Eric Lawson 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 */ /* 28 November 2003. Eric Lawson * More or less complete re-write for NUT 1.5.9 * This was somewhat easier than trying to beat the old driver code * into submission */ #include "main.h" #include "serial.h" #include "oneac.h" #define DRIVER_NAME "Oneac EG/ON UPS driver" #define DRIVER_VERSION "0.51" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Eric Lawson ", DRV_EXPERIMENTAL, { NULL } }; #define SECS 2 /*wait time*/ #define USEC 0 /*rest of wait time*/ void do_battery_test(void) { char buffer[256]; if (getval("testtime") == NULL) snprintf(buffer, 3, "%s", DEFAULT_BAT_TEST_TIME); else { snprintf(buffer, 3, "%s", getval("testtime")); /*the UPS wants this value to always be two characters long*/ /*so put a zero in front of the string, if needed.... */ if (strlen(buffer) < 2) { buffer[2] = '\0'; buffer[1] = buffer[0]; buffer[0] = '0'; } } ser_send(upsfd,"%s%s%s",BAT_TEST_PREFIX,buffer,COMMAND_END); } /**************************************************************** *below are the commands that are called by main (part of the * *Above, are functions used only in this oneac driver * ***************************************************************/ int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "test.failure.start")) { ser_send(upsfd,"%s%s",SIM_PWR_FAIL,COMMAND_END); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.start")) { do_battery_test(); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send(upsfd,"%s00%s",BAT_TEST_PREFIX,COMMAND_END); return STAT_INSTCMD_HANDLED; } if (!strcasecmp(cmdname, "reset.input.minmax")) { ser_send(upsfd,"%c%s",RESET_MIN_MAX, COMMAND_END); return STAT_INSTCMD_HANDLED; } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } void upsdrv_initinfo(void) { int i; char buffer[256], buffer2[32]; ser_flush_in(upsfd,"",0); ser_send(upsfd,"%c%s",GET_MFR,COMMAND_END); ser_get_line(upsfd, buffer, sizeof(buffer),ENDCHAR,IGNCHARS,SECS,USEC); if(strncmp(buffer,MFGR, sizeof(MFGR))) fatalx(EXIT_FAILURE, "Unable to connect to ONEAC UPS on %s\n",device_path); dstate_setinfo("ups.mfr", "%s", buffer); dstate_addcmd("test.battery.start"); dstate_addcmd("test.battery.stop"); dstate_addcmd("test.failure.start"); dstate_addcmd("reset.input.minmax"); upsh.instcmd = instcmd; /*set some stuff that shouldn't change after initialization*/ /*this stuff is common to both the EG and ON family of UPS */ /*firmware revision*/ ser_send(upsfd,"%c%s", GET_VERSION, COMMAND_END); ser_get_line(upsfd, buffer, sizeof(buffer),ENDCHAR,IGNCHARS,SECS,USEC); dstate_setinfo("ups.firmware", "%.3s",buffer); /*nominal AC frequency setting --either 50 or 60*/ ser_send(upsfd,"%c%s", GET_NOM_FREQ, COMMAND_END); ser_get_line(upsfd,buffer,sizeof(buffer),ENDCHAR,IGNCHARS,SECS,USEC); dstate_setinfo("input.frequency", "%.2s", buffer); /*UPS Model (either ON, or EG series of UPS)*/ ser_send(upsfd,"%c%s", GET_FAMILY,COMMAND_END); ser_get_line(upsfd,buffer,sizeof(buffer),ENDCHAR,IGNCHARS,SECS,USEC); dstate_setinfo("ups.model", "%.2s",buffer); printf("Found %.2s family of Oneac UPS\n", buffer); if ((strncmp(buffer,FAMILY_ON,2) != 0 && strncmp(buffer,FAMILY_ON_EXT,2) != 0) || strncmp(buffer,FAMILY_EG,2) == 0) printf("Unknown family of UPS. Assuming EG capabilities.\n"); /*Get the actual model string for ON UPS reported as OZ family*/ if (strncmp (dstate_getinfo("ups.model"), FAMILY_ON_EXT, 2) == 0) { ser_flush_in(upsfd,"",0); ser_send(upsfd,"%c%s",GET_ALL_EXT_2,COMMAND_END); ser_get_line(upsfd, buffer, sizeof(buffer),ENDCHAR,IGNCHARS,SECS,USEC); /*UPS Model (full string)*/ memset(buffer2, '\0', 32); strncpy(buffer2,&buffer[5], 10); for (i = 9; i >= 0 && buffer2[i] == ' '; --i) { buffer2[i] = '\0'; } dstate_setinfo("ups.model", "%s", buffer2); printf("Found %.10s UPS\n", buffer2); } /*The ON (OZ) series of UPS supports more stuff than does the EG. *Take care of the ON (OZ) only stuff here */ if(strncmp (dstate_getinfo("ups.model"), FAMILY_ON, 2) == 0 || strncmp (dstate_getinfo("ups.model"), FAMILY_ON_EXT, 2) == 0) { /*now set the ON specific "static" parameters*/ /*nominal input voltage*/ ser_send(upsfd,"%c%s",GET_NOM_VOLTAGE,COMMAND_END); ser_get_line(upsfd,buffer,sizeof(buffer),ENDCHAR,IGNCHARS,SECS,USEC); switch (buffer[0]) { case V120AC: dstate_setinfo("output.voltage.nominal", "120"); break; case V230AC: dstate_setinfo("output.voltage.nominal", "240"); break; default: upslogx(LOG_INFO,"Oneac: " "Invalid voltage parameter from UPS"); } } } void upsdrv_updateinfo(void) { char buffer[256]; int ret_value; ser_flush_in(upsfd,"",0); /*just in case*/ ser_send (upsfd,"%c%s",GET_ALL,COMMAND_END); ret_value = ser_get_line(upsfd,buffer,sizeof(buffer),ENDCHAR, IGNCHARS,SECS,USEC); upsdebugx (2,"upsrecv_updateinfo: upsrecv returned: %s\n",buffer); if (ret_value < 1) { ser_comm_fail("Oneac UPS Comm failure on port %s",device_path); dstate_datastale(); } else { status_init(); /*take care of the UPS status information*/ switch (buffer[12]) { case NORMAL : status_set("OL"); break; case ON_BAT_LOW_LINE : case ON_BAT_HI_LINE : status_set("OB"); break; case LO_BAT_LOW_LINE : case LO_BAT_HI_LINE : status_set("OB LB"); break; case TOO_HOT : status_set("OVER OB LB"); break; case FIX_ME : dstate_setinfo("ups.test.result","UPS Internal Failure"); break; case BAD_BAT : status_set("RB"); break; default : /*cry for attention, fake a status*/ /*Would another status be better?*/ upslogx (LOG_ERR, "Oneac: Unknown UPS status"); status_set("OL"); } /*take care of the reason why the UPS last transfered to battery*/ switch (buffer[13]) { case XFER_BLACKOUT : dstate_setinfo("input.transfer.reason", "Blackout"); break; case XFER_LOW_VOLT : dstate_setinfo("input.transfer.reason", "Low Input Voltage"); break; case XFER_HI_VOLT : dstate_setinfo("input.transfer.reason", "High Input Voltage"); break; case NO_VALUE_YET : dstate_setinfo("input.transfer.reason", "No transfer yet."); break; default : upslogx(LOG_INFO,"Oneac: Unknown reason for UPS battery" " transfer"); } /* now update info for only the ON family of UPS*/ if (strncmp(dstate_getinfo("ups.model"), FAMILY_ON, 2) == 0) { dstate_setinfo("ups.load", "0%.2s",buffer+31); /*battery charge*/ if(buffer[10] == YES) dstate_setinfo("battery.charge", "0%.2s",buffer+33); else dstate_setinfo("battery.charge", "100"); dstate_setinfo("input.voltage", "%.3s",buffer+35); dstate_setinfo("input.voltage.minimum", "%.3s",buffer+38); dstate_setinfo("input.voltage.maximum", "%.3s",buffer+41); dstate_setinfo("output.voltage", "%.3s",buffer+44); if (buffer[47] == YES) status_set("BOOST"); } status_commit(); dstate_dataok(); ser_comm_good(); } } void upsdrv_shutdown(void) { ser_send(upsfd,"%s",SHUTDOWN); } void upsdrv_help(void) { printf("\n---------\nNOTE:\n"); printf("You must set the UPS interface card DIP switch to 9600BPS\n"); } void upsdrv_makevartable(void) { addvar(VAR_VALUE,"testtime", "Change battery test time from 2 minute default."); } void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B9600); /*get the UPS in the right frame of mind*/ ser_send(upsfd,"%s", COMMAND_END); sleep (1); ser_send(upsfd,"%s", COMMAND_END); sleep (1); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); }