#include #include #include #include #include #include #include #include #include #include #include char version[]="0.6, 10-Apr-2010"; /* Spiros Ioannou, sivann xat gmail.com 2009 v0.2: compiles with -pedantic, fixes some segfaults. v0.3: more debug output v0.4: fixed negative values, BATTERY_CURRENT divider v0.5: fixed MSByte (byteH) status detection, added support for bit-rage values (INPUT_HOT_COUNT) v0.6: getopt, added lots of measurement parameters, support for float divisors/multipliers based on the work of R.Gregory (http://www.csc.liv.ac.uk/~greg/projects/liebertserial/) Compile with: gcc -O2 upsesp2.c -o upsesp2 Run with: ./upsesp2 /dev/ttyS0 Replace ttyS0 with your serial port (ttyS1, ...etc). TODO: support UPS commands (shutdown, restart-after x seconds, etc). More sniffing needed. */ //#define BAUDRATE B2400 #define BAUDRATE B9600 /***********************************************************************************************/ #define _POSIX_SOURCE 1 /* POSIX compliant source */ #define FALSE 0 #define TRUE 1 #define NCMD 500 /*max number of commands*/ #define MAXAGE 5 /*max seconds to accept data as current*/ #define IsBitSet(val, bit) ((val) & (1 << (bit))) /* port stuff */ int fd; int debug; struct termios oldtio,newtio; char str[256]; /***************************************************/ typedef struct { char descr[120]; unsigned char type; /*0:measurement, 1:ascii, 2:status*/ unsigned char length; /*for ascii, how many cmds*/ float rfmt; /*unit divider for measurement, bit position for status*/ unsigned char cmd[6]; /*UPS command bytes*/ unsigned char rbyteH; /*H byte result*/ unsigned char rbyteL; /*L byte result*/ char rstr[256]; /*string result from multiple commands (lenght>1)*/ time_t when; /*time of last result*/ unsigned char init; /*used to check if struct block has something valid*/ unsigned char supported; /*if this feature is supported*/ } cmd_s; /*fill struct*/ /*fs(0,&c[0],"lala1", 1,2,3,4,5,6, 11,22,33,44,55,66);*/ void fs( cmd_s * c, char * descr, unsigned char type, unsigned char length, float rfmt, unsigned int c0, unsigned int c1, unsigned int c2, unsigned int c3, unsigned int c4, unsigned int c5) { snprintf(c->descr,120,"%s",descr); c->cmd[0]=c0;c->cmd[1]=c1;c->cmd[2]=c2;c->cmd[3]=c3;c->cmd[4]=c4;c->cmd[5]=c5; c->rstr[0]=0; c->type=type; c->when=0; c->rfmt=rfmt; c->length=length; c->init=1; c->supported=1; /*supported until proven otherwise (UPS doesn't repond) */ } /* Initialize cmd_s structure with UPS commands and expected reply format */ void initcmd(cmd_s *c) { /*Format:*/ /*STRINGID, type,length,rfmt, cmd*/ /*type: 0:measurement, 1:ascii, 2:status */ /*length: string length for ascii info*/ /*rmft: unit divider (measurement) OR bit position (status) */ /*cmd: UPS commands. Checksums (last byte) will be recalculated on write */ fs(c++,"DISCHARGE_COUNT", 0,0,1, 1,150,2,1,1 ,000); fs(c++,"CYCLE_DURATION", 0,0,1, 1,150,2,1,2 ,000); fs(c++,"AMP_HOURS_DRAWN", 0,0,1, 1,150,2,1,3 ,000); fs(c++,"KILOWATT_HOURS_DRAWN", 0,0,1, 1,150,2,1,4 ,000); fs(c++,"WATT_HOURS_DRAWN", 0,0,1, 1,150,2,1,5 ,000); fs(c++,"RESET", 0,0,1, 1,150,2,1,6 ,000); fs(c++,"PHASE0_INPUT_VOLTAGE_LX_RMS", 0,0,10, 1,144,2,1,1 ,000); fs(c++,"PHASE0_INPUT_CURRENT_RMS", 0,0,10, 1,144,2,1,2 ,000); fs(c++,"PHASE0_OUTPUT_VOLTAGE_LX_RMS", 0,0,10, 1,144,2,1,3 ,000); fs(c++,"PHASE0_OUTPUT_CURRENT_RMS", 0,0,10, 1,144,2,1,4 ,000); fs(c++,"PHASE0_BYPASS_VOLTAGE_RMS", 0,0,10, 1,144,2,1,5 ,000); fs(c++,"PHASE0_BYPASS_CURRENT_RMS", 0,0,10, 1,144,2,1,6 ,000); fs(c++,"PHASE0_INPUT_VOLTAGE_LL_MAX", 0,0,10, 1,144,2,1,7 ,000); fs(c++,"PHASE0_INPUT_VOLTAGE_LL_MIN", 0,0,10, 1,144,2,1,8 ,000); fs(c++,"PHASE0_OUTPUT_VOLTAGE_LL_MAX", 0,0,10, 1,144,2,1,9 ,000); fs(c++,"PHASE0_OUTPUT_VOLTAGE_LL_MIN", 0,0,10, 1,144,2,1,10 ,000); fs(c++,"PHASE0_OUTPUT_VOLTAGE_LN_RMS", 0,0,10, 1,144,2,1,11 ,000); fs(c++,"PHASE0_OUTPUT_VOLTAGE_LL_THD", 0,0,10, 1,144,2,1,12 ,000); fs(c++,"PHASE0_OUTPUT_CURRENT_THD", 0,0,10, 1,144,2,1,13 ,000); fs(c++,"PHASE0_OUTPUT_CURRENT_CREST_FACTOR", 0,0,10, 1,144,2,1,14 ,000); fs(c++,"PHASE0_OUTPUT_CURRENT_K_FACTOR", 0,0,10, 1,144,2,1,15 ,000); fs(c++,"PHASE0_OUTPUT_VOLTAGE_LL_RMS", 0,0,10, 1,144,2,1,16 ,000); fs(c++,"PHASE0_INPUT_VOLTAGE_LN_RMS", 0,0,10, 1,144,2,1,17 ,000); fs(c++,"PHASE0_INPUT_VOLTAGE_LL_RMS", 0,0,10, 1,144,2,1,18 ,000); fs(c++,"PHASE0_BYPASS_VOLTAGE_LN_RMS", 0,0,10, 1,144,2,1,19 ,000); fs(c++,"PHASE0_BYPASS_VOLTAGE_LL_RMS", 0,0,10, 1,144,2,1,20 ,000); fs(c++,"PHASE0_OUT_WATT_PH_A", 0,0,0.01, 1,144,2,1,21 ,000); fs(c++,"PHASE0_OUT_VA_PH_A", 0,0,0.01, 1,144,2,1,22 ,000); fs(c++,"PHASE0_OUT_VAR_PH_A", 0,0,0.01, 1,144,2,1,23 ,000); fs(c++,"PHASE0_OUT_PERCENT_LOAD_PH_A", 0,0,1, 1,144,2,1,24 ,000); fs(c++,"PHASE0_OUT_POWER_FACTOR_PH_A", 0,0,100, 1,144,2,1,25 ,000); fs(c++,"PHASE0_OUT_POWER_FACTOR_LDLG_PH_A", 0,0,100, 1,144,2,1,26 ,000); fs(c++,"PHASE0_INP_POWER_FACTOR_PH_A", 0,0,100, 1,144,2,1,27 ,000); fs(c++,"PHASE0_INP_POWER_FACTOR_LDLG_PH_A", 0,0,100, 1,144,2,1,28 ,000); fs(c++,"PHASE1_INPUT_VOLTAGE_LX_RMS", 0,0,10, 1,145,2,1,1 ,000); fs(c++,"PHASE1_INPUT_CURRENT_RMS", 0,0,10, 1,145,2,1,2 ,000); fs(c++,"PHASE1_OUTPUT_VOLTAGE_LX_RMS", 0,0,10, 1,145,2,1,3 ,000); fs(c++,"PHASE1_OUTPUT_CURRENT_RMS", 0,0,10, 1,145,2,1,4 ,000); fs(c++,"PHASE1_BYPASS_VOLTAGE_RMS", 0,0,10, 1,145,2,1,5 ,000); fs(c++,"PHASE1_BYPASS_CURRENT_RMS", 0,0,10, 1,145,2,1,6 ,000); fs(c++,"PHASE1_INPUT_VOLTAGE_LL_MAX", 0,0,10, 1,145,2,1,7 ,000); fs(c++,"PHASE1_INPUT_VOLTAGE_LL_MIN", 0,0,10, 1,145,2,1,8 ,000); fs(c++,"PHASE1_OUTPUT_VOLTAGE_LL_MAX", 0,0,10, 1,145,2,1,9 ,000); fs(c++,"PHASE1_OUTPUT_VOLTAGE_LL_MIN", 0,0,10, 1,145,2,1,10 ,000); fs(c++,"PHASE1_OUTPUT_VOLTAGE_LN_RMS", 0,0,10, 1,145,2,1,11 ,000); fs(c++,"PHASE1_OUTPUT_VOLTAGE_LL_THD", 0,0,10, 1,145,2,1,12 ,000); fs(c++,"PHASE1_OUTPUT_CURRENT_THD", 0,0,10, 1,145,2,1,13 ,000); fs(c++,"PHASE1_OUTPUT_CURRENT_CREST_FACTOR", 0,0,10, 1,145,2,1,14 ,000); fs(c++,"PHASE1_OUTPUT_CURRENT_K_FACTOR", 0,0,10, 1,145,2,1,15 ,000); fs(c++,"PHASE1_OUTPUT_VOLTAGE_LL_RMS", 0,0,10, 1,145,2,1,16 ,000); fs(c++,"PHASE1_INPUT_VOLTAGE_LN_RMS", 0,0,10, 1,145,2,1,17 ,000); fs(c++,"PHASE1_INPUT_VOLTAGE_LL_RMS", 0,0,10, 1,145,2,1,18 ,000); fs(c++,"PHASE1_BYPASS_VOLTAGE_LN_RMS", 0,0,10, 1,145,2,1,19 ,000); fs(c++,"PHASE1_BYPASS_VOLTAGE_LL_RMS", 0,0,10, 1,145,2,1,20 ,000); fs(c++,"PHASE1_OUT_WATT_PH_B", 0,0,0.01, 1,145,2,1,21 ,000); fs(c++,"PHASE1_OUT_VA_PH_B", 0,0,0.01, 1,145,2,1,22 ,000); fs(c++,"PHASE1_OUT_VAR_PH_B", 0,0,0.01, 1,145,2,1,23 ,000); fs(c++,"PHASE1_OUT_PERCENT_LOAD_PH_B", 0,0,1, 1,145,2,1,24 ,000); fs(c++,"PHASE1_OUT_POWER_FACTOR_PH_B", 0,0,100, 1,145,2,1,25 ,000); fs(c++,"PHASE1_OUT_POWER_FACTOR_LDLG_PH_B", 0,0,100, 1,145,2,1,26 ,000); fs(c++,"PHASE1_INP_POWER_FACTOR_PH_B", 0,0,100, 1,145,2,1,27 ,000); fs(c++,"PHASE1_INP_POWER_FACTOR_LDLG_PH_B", 0,0,100, 1,145,2,1,28 ,000); fs(c++,"PHASE2_INPUT_VOLTAGE_LX_RMS", 0,0,10, 1,146,2,1,1 ,000); fs(c++,"PHASE2_INPUT_CURRENT_RMS", 0,0,10, 1,146,2,1,2 ,000); fs(c++,"PHASE2_OUTPUT_VOLTAGE_LX_RMS", 0,0,10, 1,146,2,1,3 ,000); fs(c++,"PHASE2_OUTPUT_CURRENT_RMS", 0,0,10, 1,146,2,1,4 ,000); fs(c++,"PHASE2_BYPASS_VOLTAGE_RMS", 0,0,10, 1,146,2,1,5 ,000); fs(c++,"PHASE2_BYPASS_CURRENT_RMS", 0,0,10, 1,146,2,1,6 ,000); fs(c++,"PHASE2_INPUT_VOLTAGE_LL_MAX", 0,0,10, 1,146,2,1,7 ,000); fs(c++,"PHASE2_INPUT_VOLTAGE_LL_MIN", 0,0,10, 1,146,2,1,8 ,000); fs(c++,"PHASE2_OUTPUT_VOLTAGE_LL_MAX", 0,0,10, 1,146,2,1,9 ,000); fs(c++,"PHASE2_OUTPUT_VOLTAGE_LL_MIN", 0,0,10, 1,146,2,1,10 ,000); fs(c++,"PHASE2_OUTPUT_VOLTAGE_LN_RMS", 0,0,10, 1,146,2,1,11 ,000); fs(c++,"PHASE2_OUTPUT_VOLTAGE_LL_THD", 0,0,10, 1,146,2,1,12 ,000); fs(c++,"PHASE2_OUTPUT_CURRENT_THD", 0,0,10, 1,146,2,1,13 ,000); fs(c++,"PHASE2_OUTPUT_CURRENT_CREST_FACTOR", 0,0,10, 1,146,2,1,14 ,000); fs(c++,"PHASE2_OUTPUT_CURRENT_K_FACTOR", 0,0,10, 1,146,2,1,15 ,000); fs(c++,"PHASE2_OUTPUT_VOLTAGE_LL_RMS", 0,0,10, 1,146,2,1,16 ,000); fs(c++,"PHASE2_INPUT_VOLTAGE_LN_RMS", 0,0,10, 1,146,2,1,17 ,000); fs(c++,"PHASE2_INPUT_VOLTAGE_LL_RMS", 0,0,10, 1,146,2,1,18 ,000); fs(c++,"PHASE2_BYPASS_VOLTAGE_LN_RMS", 0,0,10, 1,146,2,1,19 ,000); fs(c++,"PHASE2_BYPASS_VOLTAGE_LL_RMS", 0,0,10, 1,146,2,1,20 ,000); fs(c++,"PHASE2_OUT_WATT_PH_C", 0,0,0.01, 1,146,2,1,21 ,000); fs(c++,"PHASE2_OUT_VA_PH_C", 0,0,0.01, 1,146,2,1,22 ,000); fs(c++,"PHASE2_OUT_VAR_PH_C", 0,0,0.01, 1,146,2,1,23 ,000); fs(c++,"PHASE2_OUT_PERCENT_LOAD_PH_C", 0,0,1, 1,146,2,1,24 ,000); fs(c++,"PHASE2_OUT_POWER_FACTOR_PH_C", 0,0,100, 1,146,2,1,25 ,000); fs(c++,"PHASE2_OUT_POWER_FACTOR_LDLG_PH_C", 0,0,100, 1,146,2,1,26 ,000); fs(c++,"PHASE2_INP_POWER_FACTOR_PH_C", 0,0,100, 1,146,2,1,27 ,000); fs(c++,"PHASE2_INP_POWER_FACTOR_LDLG_PH_C", 0,0,100, 1,146,2,1,28 ,000); fs(c++,"LOCATION", 1,15,0, 1,161,2,1,19 ,000); fs(c++,"CONTACT", 1,8,0, 1,133,2,1,3 ,000); fs(c++,"PHONE_NUMBER", 1,10,0, 1,133,2,1,11 ,000); fs(c++,"MODEL_NAME", 1,15,0, 1,136,2,1,4 ,000); fs(c++,"FIRMWARE_REV", 1,8,0, 1,136,2,1,19 ,000); fs(c++,"SERIAL_NUMBER", 1,10,0, 1,136,2,1,27 ,000); fs(c++,"MFG_DATE", 1,4,0, 1,136,2,1,37 ,000); fs(c++,"INPUT_HOT_COUNT", 2,2,0, 1,136,2,1,1 ,000); fs(c++,"INPUT_SPLIT" ,2,1,2, 1,136,2,1,1 ,000); fs(c++,"OUTPUT_HOT_COUNT", 2,2,4, 1,136,2,1,1 ,000); fs(c++,"OUTPUT_SPLIT" ,2,1,6, 1,136,2,1,1 ,000); fs(c++,"BYPASS_HOT_COUNT", 2,2,8, 1,136,2,1,1 ,000); fs(c++,"BYPASS_SPLIT" ,2,1,10, 1,136,2,1,1 ,000); fs(c++,"OFFLINE_UPS" ,2,1,12, 1,136,2,1,1 ,000); fs(c++,"INTERACTIVE_UPS" ,2,1,13, 1,136,2,1,1 ,000); fs(c++,"DUAL_INPUT_UPS" ,2,1,14, 1,136,2,1,1 ,000); fs(c++,"SUB_MODULE_COUNT", 2,5,0, 1,136,2,1,2 ,000); fs(c++,"OUTLET_MAP", 0,0,1, 1,136,2,1,3 ,000); fs(c++,"BATTERY_COUNT", 0,0,1, 1,136,2,1,41 ,000); fs(c++,"POWER_MODULE_COUNT", 2,8,0, 1,136,2,1,42 ,000); fs(c++,"BATTERY_MODULE_COUNT", 2,8,8, 1,136,2,1,42 ,000); fs(c++,"REDUNDANT_CONTROL_MODULE_PRESENT" ,2,1,0, 1,136,2,1,43 ,000); fs(c++,"FREQUENCY_CONVERSION" ,2,1,1, 1,136,2,1,43 ,000); fs(c++,"VOLTAGE_CONVERSION" ,2,1,2, 1,136,2,1,43 ,000); fs(c++,"HAS_INVERTER_TEMP" ,2,1,0, 1,137,2,1,25 ,000); fs(c++,"HAS_BATTERY_TEMP" ,2,1,1, 1,137,2,1,25 ,000); fs(c++,"HAS_PFC_TEMP" ,2,1,2, 1,137,2,1,25 ,000); fs(c++,"HAS_AMBIENT_TEMP" ,2,1,3, 1,137,2,1,25 ,000); fs(c++,"HAS_AUX1_TEMP" ,2,1,4, 1,137,2,1,25 ,000); fs(c++,"HAS_AUX2_TEMP" ,2,1,5, 1,137,2,1,25 ,000); fs(c++,"BATTERY_TIME_REMAIN", 0,0,0.016666666, 1,149,2,1,1 ,000); fs(c++,"BATTERY_VOLTAGE", 0,0,10, 1,149,2,1,2 ,000); fs(c++,"BATTERY_CURRENT", 0,0,100, 1,149,2,1,3 ,000); fs(c++,"BATTERY_CAPACITY", 0,0,1, 1,149,2,1,4 ,000); fs(c++,"LOAD_WATTS", 0,0,1, 1,149,2,1,5 ,000); fs(c++,"LOAD_VA", 0,0,1, 1,149,2,1,6 ,000); fs(c++,"LOAD_PERCENT", 0,0,1, 1,149,2,1,7 ,000); fs(c++,"INPUT_FREQUENCY", 0,0,100, 1,149,2,1,8 ,000); fs(c++,"OUTPUT_FREQUENCY", 0,0,100, 1,149,2,1,9 ,000); fs(c++,"BYPASS_FREQUENCY", 0,0,100, 1,149,2,1,10 ,000); fs(c++,"INVERTER_TEMP", 0,0,10, 1,149,2,1,11 ,000); fs(c++,"BATTERY_TEMP", 0,0,10, 1,149,2,1,12 ,000); fs(c++,"PFC_TEMP", 0,0,10, 1,149,2,1,13 ,000); fs(c++,"AMBIENT_TEMP", 0,0,10, 1,149,2,1,14 ,000); fs(c++,"AUX_TEMP_1", 0,0,10, 1,149,2,1,15 ,000); fs(c++,"AUX_TEMP_2", 0,0,10, 1,149,2,1,16 ,000); fs(c++,"XFRMR_TEMP", 0,0,10, 1,149,2,1,17 ,000); fs(c++,"INP_POWER_FACTOR", 0,0,100, 1,149,2,1,19 ,000); fs(c++,"INP_POWER_FACTOR_LDLG", 0,0,100, 1,149,2,1,20 ,000); fs(c++,"REREAD", 0,0,1, 1,161,2,1,1 ,000); fs(c++,"CURRENT_MODULE_ID", 0,0,1, 1,161,2,1,2 ,000); fs(c++,"AUTO_RESTART", 0,0,1, 1,161,2,1,3 ,000); fs(c++,"AUTO_RESTART_DELAY_SECS", 0,0,0.1, 1,161,2,1,4 ,000); fs(c++,"NOMINAL_INPUT_VOLTAGE", 0,0,10, 1,161,2,1,5 ,000); fs(c++,"NOMINAL_OUTPUT_VOLTAGE", 0,0,10, 1,161,2,1,6 ,000); fs(c++,"NOMINAL_BYPASS_VOLTAGE", 0,0,10, 1,161,2,1,7 ,000); fs(c++,"NOMINAL_VA", 0,0,0.01, 1,161,2,1,8 ,000); fs(c++,"NOMINAL_INPUT_FREQ", 0,0,100, 1,161,2,1,9 ,000); fs(c++,"NOMINAL_OUTPUT_FREQ", 0,0,100, 1,161,2,1,10 ,000); fs(c++,"NOMINAL_POWER_FACTOR", 0,0,100, 1,161,2,1,11 ,000); /* snmp gives different results */ fs(c++,"NOMINAL_INPUT_CURRENT", 0,0,10, 1,161,2,1,12 ,000); fs(c++,"NOMINAL_BATTERY_VOLTAGE", 0,0,10, 1,161,2,1,13 ,000); fs(c++,"LOW_BATTERY_TIME", 0,0,0.0166666, 1,161,2,1,14 ,000); /*output is normaly in minutes */ fs(c++,"SYSTEM_CAPACITY", 0,0,1, 1,161,2,1,34 ,000); fs(c++,"SYSTEM_FRAME_CAPACITY", 0,0,1, 1,161,2,1,35 ,000); fs(c++,"AUTO_RESTART_PERCENT", 0,0,1, 1,161,2,1,36 ,000); fs(c++,"PARALLEL_MODULE_MODE", 0,0,1, 1,161,2,1,37 ,000); fs(c++,"PARALLEL_MODULE_COUNT", 0,0,1, 1,161,2,1,38 ,000); fs(c++,"PARALLEL_REDUN_COUNT", 0,0,1, 1,161,2,1,39 ,000); fs(c++,"PARALLEL_MODULE_ID", 0,0,1, 1,161,2,1,40 ,000); fs(c++,"PARALLEL_LBS_MODE", 0,0,1, 1,161,2,1,41 ,000); fs(c++,"PARALLEL_ECO_MODE", 0,0,1, 1,161,2,1,42 ,000); fs(c++,"NOM_BAT_CAPACITY", 0,0,1, 1,162,2,1,1 ,000); fs(c++,"BAT_FLOAT_VOLTAGE", 0,0,10, 1,162,2,1,2 ,000); fs(c++,"NOM_DC1_VOLTAGE", 0,0,10, 1,162,2,1,3 ,000); fs(c++,"NOM_DC2_VOLTAGE", 0,0,10, 1,162,2,1,4 ,000); fs(c++,"BAT_EOD_VOLTAGE", 0,0,10, 1,162,2,1,5 ,000); fs(c++,"BYPASS_LOW_VOLTAGE_FAIL", 0,0,1, 1,162,2,1,6 ,000); fs(c++,"BYPASS_LOW_VOLTAGE_WARN", 0,0,1, 1,162,2,1,7 ,000); fs(c++,"BYPASS_HIGH_VOLTAGE_WARN", 0,0,1, 1,162,2,1,8 ,000); fs(c++,"BYPASS_HIGH_VOLTAGE_FAIL", 0,0,1, 1,162,2,1,9 ,000); fs(c++,"OUTPUT_LOW_VOLTAGE_FAIL", 0,0,1, 1,162,2,1,10 ,000); fs(c++,"OUTPUT_LOW_VOLTAGE_WARN", 0,0,1, 1,162,2,1,11 ,000); fs(c++,"OUTPUT_HIGH_VOLTAGE_FAIL", 0,0,1, 1,162,2,1,12 ,000); fs(c++,"OUTPUT_HIGH_VOLTAGE_WARN", 0,0,1, 1,162,2,1,13 ,000); fs(c++,"BAT_HIGH_VOLTAGE_WARN", 0,0,1, 1,162,2,1,14 ,000); fs(c++,"BAT_HIGH_VOLTAGE_FAIL", 0,0,1, 1,162,2,1,15 ,000); fs(c++,"DC1_LOW_VOLTAGE_FAIL", 0,0,1, 1,162,2,1,16 ,000); fs(c++,"DC1_LOW_VOLTAGE_WARN", 0,0,1, 1,162,2,1,17 ,000); fs(c++,"DC1_HIGH_VOLTAGE_WARN", 0,0,1, 1,162,2,1,18 ,000); fs(c++,"DC1_HIGH_VOLTAGE_FAIL", 0,0,1, 1,162,2,1,19 ,000); fs(c++,"DC2_LOW_VOLTAGE_FAIL", 0,0,1, 1,162,2,1,20 ,000); fs(c++,"DC2_LOW_VOLTAGE_WARN", 0,0,1, 1,162,2,1,21 ,000); fs(c++,"DC2_HIGH_VOLTAGE_WARN", 0,0,1, 1,162,2,1,22 ,000); fs(c++,"DC2_HIGH_VOLTAGE_FAIL", 0,0,1, 1,162,2,1,23 ,000); fs(c++,"AMBIENT_TEMP_WARN", 0,0,1, 1,162,2,1,30 ,000); fs(c++,"INV_TEMP_WARN", 0,0,1, 1,162,2,1,31 ,000); fs(c++,"BAT_TEMP_WARN", 0,0,1, 1,162,2,1,32 ,000); fs(c++,"TEMP_FAIL", 0,0,1, 1,162,2,1,33 ,000); fs(c++,"CONVERTER_TEMP_WARN", 0,0,1, 1,162,2,1,34 ,000); fs(c++,"OVERTEMP_WARNING" ,2,1,5, 1,148,2,1,1 ,000); fs(c++,"AUDIBLE_ALARM_ENABLE", 0,0,1, 1,162,2,1,35 ,000); fs(c++,"BAT_AUTO_TEST_ENABLE", 0,0,1, 1,162,2,1,36 ,000); fs(c++,"MIN_REDUNDANT_PWR_MODULES", 2,7,0, 1,162,2,1,37 ,000); fs(c++,"MIN_REDUNDANT_BAT_MODULES", 2,7,8, 1,162,2,1,37 ,000); fs(c++,"STANDBY" ,2,1,15, 1,162,2,1,37 ,000); fs(c++,"MAX_LOAD_VA", 0,0,1, 1,162,2,1,38 ,000); fs(c++,"BAT_TEST_INTERVAL", 0,0,1, 1,162,2,1,39 ,000); fs(c++,"BAT_TEST_START_TIME", 0,0,1, 1,162,2,1,40 ,000); fs(c++,"PFC_ON" ,2,1,0, 1,148,2,1,1 ,000); fs(c++,"DC_DC_CONVERTER_STATE" ,2,1,1, 1,148,2,1,1 ,000); fs(c++,"ON_INVERTER" ,2,1,2, 1,148,2,1,1 ,000); fs(c++,"UTILITY_STATE" ,2,1,3, 1,148,2,1,1 ,000); fs(c++,"INRUSH_LIMIT_ON" ,2,1,4, 1,148,2,1,1 ,000); fs(c++,"BATTERY_TEST_STATE" ,2,1,6, 1,148,2,1,1 ,000); fs(c++,"INPUT_OVERVOLTAGE" ,2,1,7, 1,148,2,1,1 ,000); fs(c++,"ON_BATTERY" ,2,1,8, 1,148,2,1,1 ,000); fs(c++,"ON_BYPASS" ,2,1,0, 1,148,2,1,2 ,000); fs(c++,"BATTERY_CHARGED" ,2,1,1, 1,148,2,1,2 ,000); fs(c++,"BATTERY_LIFE_ENHANCER_ON" ,2,1,4, 1,148,2,1,2 ,000); fs(c++,"REPLACE_BATTERY" ,2,1,5, 1,148,2,1,2 ,000); fs(c++,"BOOST_ON" ,2,1,6, 1,148,2,1,2 ,000); fs(c++,"DIAG_LINK_SET" ,2,1,7, 1,148,2,1,2 ,000); fs(c++,"BUCK_ON" ,2,1,9, 1,148,2,1,2 ,000); fs(c++,"UPS_OVERLOAD" ,2,1,0, 1,148,2,1,3 ,000); fs(c++,"BAD_INPUT_FREQ" ,2,1,1, 1,148,2,1,3 ,000); fs(c++,"SHUTDOWN_PENDING" ,2,1,2, 1,148,2,1,3 ,000); fs(c++,"CHARGER_FAIL" ,2,1,3, 1,148,2,1,3 ,000); fs(c++,"LOW_BATTERY" ,2,1,5, 1,148,2,1,3 ,000); fs(c++,"OUTPUT_UNDERVOLTAGE" ,2,1,6, 1,148,2,1,3 ,000); fs(c++,"OUTPUT_OVERVOLTAGE" ,2,1,7, 1,148,2,1,3 ,000); fs(c++,"BAD_BYPASS_PWR" ,2,1,8, 1,148,2,1,3 ,000); fs(c++,"CHECK_AIR_FILTER" ,2,1,10, 1,148,2,1,3 ,000); fs(c++,"AMBIENT_OVERTEMP" ,2,1,2, 1,148,2,1,7 ,000); fs(c++,"BATTERY_TEST_RESULT", 0,0,1, 1,148,2,1,12 ,000); fs(c++,"SELF_TEST_RESULT", 0,0,1, 1,148,2,1,13 ,000); fs(c++,"FAILED_POWER_MODULES", 2,8,0, 1,148,2,1,18 ,000); fs(c++,"FAILED_BATTERY_MODULES", 2,8,8, 1,148,2,1,18 ,000); fs(c++,"MAIN_CONTROL_MODULE_FAILED" ,2,1,0, 1,148,2,1,19 ,000); fs(c++,"REDUNDANT_CONTROL_MODULE_FAILED" ,2,1,1, 1,148,2,1,19 ,000); fs(c++,"UI_MODULE_FAILED" ,2,1,2, 1,148,2,1,19 ,000); fs(c++,"REDUNDANT_POWER_MODULE_ALARM" ,2,1,3, 1,148,2,1,19 ,000); fs(c++,"REDUNDANT_BATTERY_MODULE_ALARM" ,2,1,4, 1,148,2,1,19 ,000); fs(c++,"USER_MAX_LOAD_ALARM" ,2,1,5, 1,148,2,1,19 ,000); fs(c++,"TRANSFORMER_OVERTEMP_ALARM" ,2,1,6, 1,148,2,1,19 ,000); fs(c++,"INTERNAL_COMMS_LOST" ,2,1,7, 1,148,2,1,19 ,000); fs(c++,"NEW_EVENT_HISTORY" ,2,1,8, 1,148,2,1,19 ,000); fs(c++,"PWR_MOD_FAILED" ,2,1,9, 1,148,2,1,19 ,000); fs(c++,"BAT_MOD_FAILED" ,2,1,10, 1,148,2,1,19 ,000); fs(c++,"OPTION_CARD_FAIL_1" ,2,1,0, 1,148,2,1,20 ,000); fs(c++,"OPTION_CARD_FAIL_2" ,2,1,1, 1,148,2,1,20 ,000); fs(c++,"OPTION_CARD_FAIL_3" ,2,1,2, 1,148,2,1,20 ,000); fs(c++,"OPTION_CARD_FAIL_4" ,2,1,3, 1,148,2,1,20 ,000); fs(c++,"OPTION_CARD_FAIL_5" ,2,1,4, 1,148,2,1,20 ,000); fs(c++,"OPTION_CARD_FAIL_6" ,2,1,5, 1,148,2,1,20 ,000); fs(c++,"OPTION_CARD_FAIL_7" ,2,1,6, 1,148,2,1,20 ,000); fs(c++,"OPTION_CARD_FAIL_8" ,2,1,7, 1,148,2,1,20 ,000); fs(c++,"SUMMARY_ALARM" ,2,1,0, 1,148,2,1,22 ,000); fs(c++,"RECT_UV_STARTUP_FAIL" ,2,1,0, 1,148,2,1,24 ,000); fs(c++,"RECT_FAULT" ,2,1,1, 1,148,2,1,24 ,000); fs(c++,"RECT_OVER_CURRENT" ,2,1,2, 1,148,2,1,24 ,000); fs(c++,"RECT_OVER_TEMP" ,2,1,3, 1,148,2,1,24 ,000); fs(c++,"RECT_INDCTR_OVER_TEMP" ,2,1,4, 1,148,2,1,24 ,000); fs(c++,"RECT_COMM_FAIL" ,2,1,5, 1,148,2,1,24 ,000); fs(c++,"INV_SHUTDOWN_LOW_DC" ,2,1,6, 1,148,2,1,24 ,000); fs(c++,"INV_FAULT" ,2,1,7, 1,148,2,1,24 ,000); fs(c++,"INV_OVER_CURRENT" ,2,1,8, 1,148,2,1,24 ,000); fs(c++,"INV_OVER_TEMP" ,2,1,9, 1,148,2,1,24 ,000); fs(c++,"INV_INDCTR_OVER_TEMP" ,2,1,10, 1,148,2,1,24 ,000); fs(c++,"INV_COMM_FAIL" ,2,1,11, 1,148,2,1,24 ,000); fs(c++,"INV_DC_OFFSET_OVR" ,2,1,12, 1,148,2,1,24 ,000); fs(c++,"INV_CONTACTOR_FAIL" ,2,1,13, 1,148,2,1,24 ,000); fs(c++,"BAT_CHARGE_STATE", 0,0,1, 1,148,2,1,25 ,000); fs(c++,"BAT_FAULT" ,2,1,1, 1,148,2,1,25 ,000); fs(c++,"BAT_CONTACTOR_FAIL" ,2,1,2, 1,148,2,1,25 ,000); fs(c++,"CONVERTER_OVER_TEMP" ,2,1,4, 1,148,2,1,25 ,000); fs(c++,"CONVERTER_OVER_AMPS" ,2,1,5, 1,148,2,1,25 ,000); fs(c++,"CONVERTER_FAIL" ,2,1,6, 1,148,2,1,25 ,000); fs(c++,"BALANCER_OVER_TEMP" ,2,1,7, 1,148,2,1,25 ,000); fs(c++,"BALANCER_FAULT" ,2,1,8, 1,148,2,1,25 ,000); fs(c++,"BALANCER_OVER_CURRENT" ,2,1,9, 1,148,2,1,25 ,000); fs(c++,"BY_CB_OPEN" ,2,1,10, 1,148,2,1,25 ,000); fs(c++,"LOAD_IMPACT_XFER" ,2,1,11, 1,148,2,1,25 ,000); fs(c++,"OPERATION_FAULT" ,2,1,12, 1,148,2,1,25 ,000); fs(c++,"OUT_FUSE_BLOWN" ,2,1,13, 1,148,2,1,25 ,000); fs(c++,"ON_JOINT_MODE" ,2,1,14, 1,148,2,1,25 ,000); fs(c++,"GENERATOR_DISC", 0,0,1, 1,148,2,1,25 ,000); fs(c++,"ROTARY_BREAKER", 0,0,1, 1,148,2,1,26 ,000); fs(c++,"MAIN_NEUTRAL_LOST" ,2,1,4, 1,148,2,1,26 ,000); fs(c++,"LOAD_SOURCE", 0,0,1, 1,148,2,1,27 ,000); fs(c++,"PARALLEL_LOW_BAT_WARN" ,2,1,4, 1,148,2,1,27 ,000); fs(c++,"PARALLEL_LOAD_SHARE_FAULT" ,2,1,5, 1,148,2,1,27 ,000); fs(c++,"PARALLEL_FAULT" ,2,1,6, 1,148,2,1,27 ,000); fs(c++,"PARALLEL_CONNECT_FAULT" ,2,1,7, 1,148,2,1,27 ,000); fs(c++,"PARALLEL_COMM_FAIL" ,2,1,8, 1,148,2,1,27 ,000); fs(c++,"PARALLEL_SYS_OVER_LOAD" ,2,1,9, 1,148,2,1,27 ,000); fs(c++,"PARALLEL_SYS_XFER" ,2,1,10, 1,148,2,1,27 ,000); fs(c++,"SYSVIEW_OUT_WATT_PH_A", 0,0,0.01, 1,154,2,1,1 ,000); fs(c++,"SYSVIEW_OUT_VA_PH_A", 0,0,0.01, 1,154,2,1,2 ,000); fs(c++,"SYSVIEW_OUT_VAR_PH_A", 0,0,0.01, 1,154,2,1,3 ,000); fs(c++,"SYSVIEW_OUT_WATT_PH_B", 0,0,0.01, 1,154,2,1,4 ,000); fs(c++,"SYSVIEW_OUT_VA_PH_B", 0,0,0.01, 1,154,2,1,5 ,000); fs(c++,"SYSVIEW_OUT_VAR_PH_B", 0,0,0.01, 1,154,2,1,6 ,000); fs(c++,"SYSVIEW_OUT_WATT_PH_C", 0,0,0.01, 1,154,2,1,7 ,000); fs(c++,"SYSVIEW_OUT_VA_PH_C", 0,0,0.01, 1,154,2,1,8 ,000); fs(c++,"SYSVIEW_OUT_VAR_PH_C", 0,0,0.01, 1,154,2,1,9 ,000); c->init=0; /*on first non-init element */ } int GetCmdCount(cmd_s * c) { int i; for (i=0;(iinit;i++) ; return i; } /*return array position of command "descr"*/ int GetCmdIdbyDesc(cmd_s * c,char *descr) { int i; for (i=0;idescr)) return i; c++; } return -1; } /*return array position of the same status UPS command with the most recent data *this is since each status command includes several statuses, and we don't want *to re-query the UPS for each of those statuses if the status is recent enough*/ int GetRecentStatusId(cmd_s *c,unsigned char bit4) { int i,r=-1; time_t maxt=0; for (i=0;(iinit;i++) { if ((c->cmd[1]!=148) || (c->cmd[4]!=bit4)) continue; else if (c->when>maxt) { maxt=c->when; r=i; } } return r; } /***************************************************/ void sgnl_ignore(int status) { fprintf(stderr,"Signaled\n"); } /*calculate checksum*/ unsigned char cksum(unsigned char *buf, int len) { unsigned char sum=0; while(len-->0) sum+=*buf++; return(sum); } int timedwrite(int fd, unsigned char *buf, int len, int msec) { fd_set wfds; struct timeval tv; int retval; int writ=0; while(writ=0 && FD_ISSET(fd, &wfds) ) { int res = write(fd,buf+writ,len-writ); if( res>0 ) writ+=res; else return(-1); } if( retval<=0 ) return(-1); } return(writ); } int timedread(int fd, unsigned char *buf, int len, int msec) { fd_set rfds; struct timeval tv; int retval; int red=0; while(red=0 && FD_ISSET(fd, &rfds) ) { int res = read(fd,buf+red,len-red); if( res>0 ) red+=res; else return(-1); } if( retval<=0 ) return(-1); } return(red); } /* Send a command and read a measurement */ int SendCmd_M(int fd, unsigned char *cmd, unsigned char *rbyteH, unsigned char *rbyteL, char * descr,int rfmt) { unsigned char buf[8]; int res; cmd[5]=cksum(cmd,5); if (debug) fprintf(stderr,"\nSendingM: %03d,%03d,%03d,%03d,%03d,%03d\t%s,bit/divider:%d\n",cmd[0],cmd[1],cmd[2],cmd[3],cmd[4],cmd[5],descr,rfmt); res=timedwrite(fd,cmd,6,500); if(res!=6){ fprintf(stderr,"port write error, cmd:"); fprintf(stderr,"%03d,%03d,%03d,%03d,%03d,%03d\n",cmd[0],cmd[1],cmd[2],cmd[3],cmd[4],cmd[5]); return 65000; /*values will never be that high, values with MSB >128 are negative*/ } res=timedread(fd,buf,8,200); if (debug) fprintf(stderr,"READ : %03d,%03d,%03d,%03d,%03d, %03d,%03d (%03d %03d), %03d\n", buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6], buf[5],buf[6], buf[7]); if(res!=8){ fprintf(stderr,"read count error (%d<>8),cmd:",res); fprintf(stderr,"%03d,%03d,%03d,%03d,%03d,%03d\n",cmd[0],cmd[1],cmd[2],cmd[3],cmd[4],cmd[5]); return 65000; } if(buf[7]!=cksum(buf,7)){ fprintf(stderr,"checksum error for cmd:"); fprintf(stderr,"%03d,%03d,%03d,%03d,%03d,%03d\n",cmd[0],cmd[1],cmd[2],cmd[3],cmd[4],cmd[5]); return 65000; } res= (signed short int)(((short)buf[5])*256+buf[6]); *rbyteH=(unsigned short)buf[5]; *rbyteL=(unsigned short)buf[6]; return res; } /* Send a series of consecutive commands and read text response (2 chars each)*/ int SendCmd_T(int fd, unsigned char *cmd, char *chrs, int len, char * descr) { unsigned char buf[8], lcmd[7]; int wres,rres; buf[0]=0; *chrs=0; memcpy(lcmd,cmd,6); for(;len>0;len--) { lcmd[5]=cksum(lcmd,5); if (debug) fprintf(stderr,"\nSendingT: %03d,%03d,%03d,%03d,%03d,%03d\t%s,length:%d\n",lcmd[0],lcmd[1],lcmd[2],lcmd[3],lcmd[4],lcmd[5],descr,len); wres=timedwrite(fd,lcmd,6,200); if (wres!=6) return -1; rres=timedread(fd,buf,8,200); if (debug) fprintf(stderr,"READ : %03d,%03d,%03d,%03d,%03d,%03d,%03d,%03d\n",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]); if (rres!=8) return -1; *chrs++=buf[6]; *chrs++=buf[5]; if (buf[7]!=cksum(buf,7)) return -1; lcmd[4]++; } *chrs=0; return 1; } void portsetup(char * port) { fd = open(port, O_RDWR | O_NOCTTY ); if (fd < 0) { fprintf(stderr,"error %d\n",errno); sprintf(str,"%s:%s",port,strerror(errno)); fprintf(stderr,"%s\n",str); exit(errno); } tcgetattr(fd,&oldtio); /* save current port settings */ bzero(&newtio, sizeof(newtio)); newtio.c_cflag = BAUDRATE /*| CRTSCTS*/ | CS8 | CLOCAL | CREAD; newtio.c_iflag = IGNPAR | IGNBRK; newtio.c_oflag = 0; /* set input mode (non-canonical, no echo,...) */ newtio.c_lflag = 0; newtio.c_cc[VTIME] = 0; /* inter-character timer unused */ newtio.c_cc[VMIN] = 1; /* blocking read until 5 chars received */ tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); } /*Read data from UPS corresponding to c->cmd and fill-in result in struct cmd_s*/ int ReadCmd(cmd_s * c) { int r,res; int j; unsigned char rbyteH,rbyteL; char buf[255]; if (c->supported==0) { /*read failed on 1st try, thus marked as not supported*/ /*printf("UNSUPPORTED item %s:skipping\n",c->descr);*/ return 0; } if (c->type==0) { /*measurement*/ /*todo: check if time is fresh, and if not then SendCmd. Same for strings*/ res=SendCmd_M(fd, c->cmd, &rbyteH, &rbyteL,c->descr,c->rfmt); if(res!=65000) { signed short int sh; c->when=time(0); c->rbyteL=rbyteL; c->rbyteH=rbyteH; sh=(signed short int)(256*c->rbyteH+c->rbyteL); printf("%s: %.1f\n",c->descr,(signed short int)(256*c->rbyteH+c->rbyteL)/(float)c->rfmt); } else { printf("Error getting %s:marking as not supported\n",c->descr); c->supported=0; } } else if (c->type==1) { /*ascii*/ res=SendCmd_T(fd, c->cmd, buf, c->length,c->descr); if (res!=65000) { strcpy(c->rstr,buf); c->when=time(0); printf("%s: %s\n",c->descr,c->rstr); } else { printf("Error getting %s:marking as not supported\n",c->descr); c->supported=0; } } else if ((c->type==2)&& (c->length<2)) { /*status*/ r=GetRecentStatusId(&c[0],c->cmd[4]); if ((r<0) || (c->when-time(0)>MAXAGE)) { /*not previous data or old data*/ res=SendCmd_M(fd, c->cmd, &rbyteH, &rbyteL,c->descr,c->rfmt); if (res!=65000) { c->when=time(0); c->rbyteL=rbyteL; c->rbyteH=rbyteH; if (c->rfmt<8) (j=IsBitSet(c->rbyteL,(unsigned short int)c->rfmt)); else (j=IsBitSet(c->rbyteH,(unsigned short int)c->rfmt-8) ); if (j) printf("%s: YES\n",c->descr); else printf("%s: NO\n",c->descr); } else { printf("Error getting %s:marking as not supported\n",c->descr); c->supported=0; } } else { /*found recent data from other poll*/ if (c->rfmt<8) (j=IsBitSet(c[r].rbyteL,(unsigned short int)c->rfmt)); else (j=IsBitSet(c[r].rbyteH,(unsigned short int)c[r].rfmt-8) ); if (j) printf("%s: YES(cached)\n",c->descr); else printf("%s: NO(cached)\n",c->descr); } } else if ((c->type==2)&& (c->length>=2)) { /*measurement in bits*/ res=SendCmd_M(fd, c->cmd, &rbyteH, &rbyteL,c->descr,c->rfmt); if (res!=65000) { int bitn; unsigned int value; value=0; c->when=time(0); c->rbyteL=rbyteL; c->rbyteH=rbyteH; if (c->rfmt<8){ for (bitn=c->rfmt;bitn<(c->rfmt+c->length);bitn++) { if (IsBitSet(rbyteL,(unsigned short int)bitn)) value+=(1<<(unsigned short int)(bitn-c->rfmt)); } } else { int rf=c->rfmt-8; for (bitn=rf;bitn<(rf+c->length);bitn++) { if (IsBitSet(rbyteH,(unsigned short int)bitn)) value+=1<<(bitn-rf); } } printf("%s: %d\n",c->descr,(signed short int)(value)); } else { printf("Error getting %s:marking as not supported\n",c->descr); c->supported=0; } } return c->supported; } void usage () { fprintf(stderr,"upsesp2 version %s:\n",version); fprintf(stderr,"Usage:\n"); fprintf(stderr,"\t\tupsesp2 [-d] [-l] [-p parameter] \n"); fprintf(stderr,"\t\t-d\tprint debug information\n"); fprintf(stderr,"\t\t-l\tloop indefinitely\n"); fprintf(stderr,"\t\t-p\tquery only specified UPS parameter.\n"); fprintf(stderr,"\n\t\texample: upsesp2 -c OUTPUT_VOLTAGE /dev/ttyS1\n"); } int main(int argc, char **argv) { int i; unsigned int ncmd; cmd_s c[NCMD]; int reqcmd=-1; /*if requesting specific parameter*/ int opt; int doloop=0; setvbuf(stdout, NULL, _IONBF, 0); initcmd(&c[0]); /* fill in struct table */ ncmd=GetCmdCount(&c[0]); /*count number of commands*/ while ((opt = getopt(argc, argv, "ldp:")) != -1) { switch (opt) { case 'd': debug = 1; break; case 'l': doloop = 1; break; case 'p': reqcmd=GetCmdIdbyDesc(&c[0],optarg); /*get position of requested parameter in c[]*/ break; default: /* '?' */ usage(); exit(EXIT_FAILURE); } } if (optind >= argc) { usage(); fprintf(stderr, "\nerror: expected serial device (e.g. /dev/ttyS0) after options\n"); exit(EXIT_FAILURE); } printf("device specified = %s\n", argv[optind]); portsetup(argv[optind]); /*setup serial port*/ signal( SIGALRM, &sgnl_ignore ); /*for select*/ if (reqcmd>=0) { /* if requesting a single ups command (measurement/status)*/ ReadCmd(&c[reqcmd]); } else while (1) { for (i=0;i