1 /* upscode2.c - model specific routines for UPSes using the UPScode II
2 		command set.  This includes PowerWare, Fiskars,
3 		Compaq (PowerWare OEM?), some IBM (PowerWare OEM?)
4 
5    Copyright (C) 2002 H?vard Lygre <hklygre@online.no>
6    Copyright (C) 2004-2006 Niels Baggesen <niels@baggesen.net>
7    Copyright (C) 2006 Niklas Edmundsson <nikke@acc.umu.se>
8 
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 
23 */
24 
25 /*
26  * Currently testing against
27  * Fiskars PowerRite Max
28  * Fiskars PowerServer 10
29  * Fiskars PowerServer 30
30  * Powerware Profile (9150)
31  * Powerware 9305
32  *
33  * Also tested against
34  * Compaq T1500h (Per J?nsson <per.jonsson@bth.se>)
35  * Powerware 9120 (Gorm J. Siiger <gjs@sonnit.dk>)
36  * Fiskars PowerServer 10 (Per Larsson <tucker@algonet.se>)
37  */
38 
39 #include "main.h"
40 #include "serial.h"
41 #include "timehead.h"
42 #include "nut_stdint.h"
43 
44 #include <math.h>
45 
46 #define DRIVER_NAME	"UPScode II UPS driver"
47 #define DRIVER_VERSION	"0.89"
48 
49 /* driver description structure */
50 upsdrv_info_t	upsdrv_info = {
51 	DRIVER_NAME,
52 	DRIVER_VERSION,
53 	"H K Lygre, <hklygre@online.no>\n" \
54 	"Niels Baggesen <niels@baggesen.net>\n" \
55 	"Niklas Edmundsson <nikke@acc.umu.se>",
56 	DRV_EXPERIMENTAL,
57 	{ NULL }
58 };
59 
60 #define ENDCHAR		'\n'
61 
62 /* default values */
63 #define OUT_PACE_USEC	200
64 #define INP_TIMO_SEC	2
65 #define FULL_UPDATE_TIMER	60
66 
67 #define UPSC_BUFLEN	256  /* Size of response buffers from UPS */
68 
69 /* Status messages from UPS */
70 #define UPSC_STAT_ONLINE       (1UL << 0)
71 #define UPSC_STAT_ONBATT       (1UL << 1)
72 #define UPSC_STAT_LOBATT       (1UL << 2)
73 #define UPSC_STAT_REPLACEBATT  (1UL << 3)
74 #define UPSC_STAT_BOOST        (1UL << 4)
75 #define UPSC_STAT_TRIM         (1UL << 5)
76 #define UPSC_STAT_OVERLOAD     (1UL << 6)
77 #define UPSC_STAT_CALIBRATION  (1UL << 7)
78 #define UPSC_STAT_OFF          (1UL << 8)
79 #define UPSC_STAT_BYPASS       (1UL << 9)
80 #define UPSC_STAT_NOTINIT      (1UL << 31)
81 
82 
83 typedef enum {
84 	t_ignore,	/* Ignore this response  */
85 	t_value,	/* Sets a NUT variable */
86 	t_final,        /* Marks the end of UPS data for this command */
87 	t_string,	/* Set a NUT string variable */
88 	t_finstr,	/* Set a NUT string variable, and marks end of data */
89 	t_setval,	/* Sets a variable in the driver and possibly in NUT */
90 	t_setrecip,	/* Sets a driver variable to 1/value and possibly NUT */
91 	t_setpct,
92 	t_setrecpct,
93 	t_status,	/* Sets a status bit */
94 	t_alarm,	/* Sets an alarm message */
95 	t_list		/* The value must be matched in the list */
96 } type_t;
97 
98 
99 typedef struct simple_s {
100 	const char *code;
101 	type_t type;
102 	const char *desc;
103 	int status;
104 	float *aux;
105 	struct simple_s *stats;
106 } simple_t;
107 
108 typedef struct {
109 	const char *cmd;
110 	const char *upsc;
111 	const char *upsp;
112 	int enabled;
113 } cmd_t;
114 
115 
116 static int
117 	can_upda = 0,
118 	can_upbs = 0,
119 	can_upid = 0,
120 	can_uppm = 0,
121 	can_updt = 0,
122 	can_uptm = 0,
123 	can_upsd = 0,
124 	can_uppc = 0;
125 
126 static char has_uppm_p[100];
127 
128 static int
129 	input_timeout_sec = INP_TIMO_SEC,
130 	output_pace_usec = OUT_PACE_USEC,
131 	full_update_timer = FULL_UPDATE_TIMER,
132 	use_crlf = 0,
133 	use_pre_lf = 0,
134 	buffer_empty = 0;
135 
136 static uint32_t
137 	status = UPSC_STAT_NOTINIT;
138 
139 static time_t
140 	last_full = 0;
141 
142 static float
143 	batt_volt_low = 0,
144 	batt_volt_nom = 0,
145 	batt_volt_high = 0,
146 	batt_volt = 0,
147 	batt_cap_nom = -1,
148 	batt_charge = -1,
149 	batt_current = -1,
150 	batt_disch_curr_max = 0,
151 	batt_runtime = -1,
152 	batt_runtime_max = -1,
153 	kilo_to_unity = 1000.0,
154 	outpwr_factor = 1000.0,
155 	nom_out_current = -1,
156 	max_out_current = -1;
157 
158 /* To get average battery current */
159 #define NUM_BATTHIST 60
160 	static float batthist[NUM_BATTHIST];
161 	static int numbatthist=0, lastbatthist=0;
162 
163 static int
164 	inited_phaseinfo = 0,
165 	num_inphases = 1,
166 	num_outphases = 1;
167 
168 
169 /* Status codes for the STAT and STMF status responses */
170 static simple_t att[] = {
171 	{ "00", t_ignore  },
172 	{ "AC", t_alarm,  "Aux contact failure", 0 },
173 	{ "BA", t_alarm,  "Batteries disconnected", 0 },
174 	{ "BC", t_alarm,  "Backfeed contact failure", 0 },
175 	{ "BD", t_alarm,  "Abnormal battery discharge", 0 },
176 	{ "BF", t_alarm,  "Battery fuse  failure", 0 },
177 	{ "BL", t_status, "Battery low limit", UPSC_STAT_LOBATT },
178 	{ "BO", t_alarm,  "Battery over voltage", 0 },
179 	{ "BP", t_alarm,  "Bypass fuse failure", 0 },
180 	{ "BR", t_alarm,  "Abnormal battery recharge", 0 },
181 	{ "BT", t_alarm,  "Battery over temperature", 0 },
182 	{ "BX", t_alarm,  "Bypass unavailable", 0 },
183 	{ "BY", t_alarm,  "Battery failure", 0 },
184 	{ "CE", t_alarm,  "Configuration error", 0 },
185 	{ "CM", t_alarm,  "Battery converter failure", 0 },
186 	{ "CT", t_alarm,  "Cabinet over temperature", 0 },
187 	{ "DO", t_alarm,  "DC over voltage", 0 },
188 	{ "DU", t_alarm,  "DC under voltage", 0 },
189 	{ "EP", t_alarm,  "Emergency power off", 0 },
190 	{ "FF", t_alarm,  "Fan failure", 0 },
191 	{ "FH", t_alarm,  "Line frequency high", 0 },
192 	{ "FL", t_alarm,  "Line frequency low", 0 },
193 	{ "FT", t_alarm,  "Filter over temperature", 0 },
194 	{ "GF", t_alarm,  "Ground failure", 0 },
195 	{ "HT", t_alarm,  "Charger over temperature", 0 },
196 	{ "IB", t_alarm,  "Internal data bus failure", 0 },
197 	{ "IF", t_alarm,  "Inverter fuse failure", 0 },
198 	{ "IM", t_alarm,  "Inverter failure", 0 },
199 	{ "IO", t_alarm,  "Inverter over voltage", 0 },
200 	{ "IP", t_alarm,  "Internal power supply failure", 0 },
201 	{ "IT", t_alarm,  "Inverter over temperature", 0 },
202 	{ "IU", t_alarm,  "Inverter under voltage", 0 },
203 	{ "IV", t_alarm,  "Inverter off", 0 },
204 	{ "LR", t_alarm,  "Loss of redundancy", 0 },
205 	{ "NF", t_alarm,  "Neutral fault", 0 },
206 	{ "OD", t_status, "UPS not supplying load", UPSC_STAT_OFF },
207 	{ "OF", t_alarm,  "Oscillator failure", 0 },
208 	{ "OL", t_status, "Overload", UPSC_STAT_OVERLOAD },
209 	{ "OR", t_alarm,  "Redundancy overload", 0 },
210 	{ "OV", t_alarm,  "Abnormal output voltage", 0 },
211 	{ "OW", t_alarm,  "Output failure", 0 },
212 	{ "PB", t_alarm,  "Parallel bus failure", 0 },
213 	{ "PE", t_alarm,  "Phase rotation error", 0 },
214 	{ "RE", t_alarm,  "Rectifier off", 0 },
215 	{ "RF", t_alarm,  "Rectifier fuse failure", 0 },
216 	{ "RM", t_alarm,  "Rectifier failure", 0 },
217 	{ "RT", t_alarm,  "Rectifier over temperature", 0 },
218 	{ "SM", t_alarm,  "Static switch failure", 0 },
219 	{ "ST", t_alarm,  "Static switch over temperature", 0 },
220 	{ "TT", t_alarm,  "Trafo over temperature", 0 },
221 	{ "UD", t_alarm,  "UPS disabled", 0 },
222 	{ "UO", t_alarm,  "Utility over voltage", 0 },
223 	{ "US", t_alarm,  "Unsynchronized", 0 },
224 	{ "UU", t_alarm,  "Utility under voltage", 0 },
225 	{ "VE", t_alarm,  "internal voltage error", 0 },
226 	{ NULL }
227 };
228 
229 
230 /* Status code for the STLR response */
231 static simple_t stlr[] = {
232 	{ "NO", t_ignore },
233 	{ "SD", t_status, NULL, UPSC_STAT_TRIM },
234 	{ "SU", t_status, NULL, UPSC_STAT_BOOST },
235 	{ "DU", t_status, NULL, UPSC_STAT_BOOST },
236 	{ NULL }
237 };
238 
239 
240 /* Status code for the STEA and STEM responses */
241 static simple_t env[] = {
242 	{ "HH", t_ignore, "Humidity high", 0 },
243 	{ "HL", t_ignore, "Humidity low", 0 },
244 	{ "TH", t_ignore, "Temperature high", 0 },
245 	{ "TL", t_ignore, "Temperature low", 0 },
246 	{ "01", t_ignore, "Environment alarm 1", 0 },
247 	{ "02", t_ignore, "Environment alarm 2", 0 },
248 	{ "03", t_ignore, "Environment alarm 3", 0 },
249 	{ "04", t_ignore, "Environment alarm 4", 0 },
250 	{ "05", t_ignore, "Environment alarm 5", 0 },
251 	{ "06", t_ignore, "Environment alarm 6", 0 },
252 	{ "07", t_ignore, "Environment alarm 7", 0 },
253 	{ "08", t_ignore, "Environment alarm 8", 0 },
254 	{ "09", t_ignore, "Environment alarm 9", 0 },
255 	{ NULL }
256 };
257 
258 
259 /* Responses for UPSS and UPDS */
260 static simple_t simple[] = {
261 	{ "STAT", t_list,   NULL, 0, 0, att },
262 	{ "STBO", t_status, NULL, UPSC_STAT_ONBATT },
263 	{ "STBL", t_status, NULL, UPSC_STAT_LOBATT },
264 	{ "STBM", t_ignore },
265 	{ "STBP", t_status, NULL, UPSC_STAT_BYPASS },
266 	{ "STEA", t_list,   NULL, 0, 0, env },
267 	{ "STEM", t_list,   NULL, 0, 0, env },
268 	{ "STLR", t_list,   NULL, 0, 0, stlr },
269 	{ "STMF", t_list,   NULL, 0, 0, att },
270 	{ "STOK", t_ignore },
271 	{ "STUF", t_status, NULL, UPSC_STAT_ONBATT },
272 
273 	{ "BTIME", t_setval, NULL, 0, &batt_runtime },
274 
275 	{ "METE1", t_value, "ambient.temperature" },
276 	{ "MERH1", t_value, "ambient.humidity" },
277 
278 	{ "MIFFF", t_value, "input.frequency" },
279 	{ "MIIL1", t_value, "input.current" },
280 	{ "MIIL2", t_value, "input.L2.current" },
281 	{ "MIIL3", t_value, "input.L3.current" },
282 	{ "MIPL1", t_value, "input.realpower" },
283 	{ "MIPL2", t_value, "input.L2.realpower" },
284 	{ "MIPL3", t_value, "input.L3.realpower" },
285 	{ "MISL1", t_value, "input.power" },
286 	{ "MISL2", t_value, "input.L2.power" },
287 	{ "MISL3", t_value, "input.L3.power" },
288 	{ "MIUL1", t_value, "input.voltage" },
289 	{ "MIUL2", t_value, "input.L2-N.voltage" },
290 	{ "MIUL3", t_value, "input.L3-N.voltage" },
291 	{ "MIU12", t_value, "input.L1-L2.voltage" },
292 	{ "MIU23", t_value, "input.L2-L3.voltage" },
293 	{ "MIU31", t_value, "input.L3-L1.voltage" },
294 
295 	{ "MBCH1", t_setval, NULL, 0, &batt_charge }, /* battery.charge */
296 	{ "MBIII", t_setval, "battery.current", 0, &batt_current },
297 	{ "MBINE", t_ignore, /* "battery.current.negative" */ },
298 	{ "MBIPO", t_ignore, /* "battery.current.positive" */ },
299 	{ "MBUNE", t_ignore, /* "battery.voltage.negative" */ },
300 	{ "MBUPO", t_ignore, /* "battery.voltage.positive" */},
301 	{ "MBUUU", t_setval, "battery.voltage", 0, &batt_volt },
302 
303 	{ "MLUNE", t_ignore, /* "dc.voltage.negative" */ },
304 	{ "MLUPO", t_ignore, /* "dc.voltage.positive" */ },
305 	{ "MLUUU", t_ignore, /* "dc.voltage" */ },
306 
307 	{ "MOFFF", t_final, "output.frequency" },
308 	{ "MOIL1", t_value, "output.current" },
309 	{ "MOIL2", t_value, "output.L2.current" },
310 	{ "MOIL3", t_value, "output.L3.current" },
311 	{ "MOIP1", t_value, "output.current.peak" },
312 	{ "MOIP2", t_value, "output.L2.current.peak" },
313 	{ "MOIP3", t_value, "output.L3.current.peak" },
314 	{ "MOPL1", t_value, "output.realpower", 0, &kilo_to_unity },
315 	{ "MOPL2", t_value, "output.L2.realpower", 0, &kilo_to_unity },
316 	{ "MOPL3", t_value, "output.L3.realpower", 0, &kilo_to_unity },
317 	{ "MOSL1", t_value, "output.power" },
318 	{ "MOSL2", t_value, "output.L2.power" },
319 	{ "MOSL3", t_value, "output.L3.power" },
320 	{ "MOUL1", t_value, "output.voltage" },
321 	{ "MOUL2", t_value, "output.L2-N.voltage" },
322 	{ "MOUL3", t_value, "output.L3-N.voltage" },
323 	{ "MOU12", t_value, "output.L1-L2.voltage" },
324 	{ "MOU23", t_value, "output.L2-L3.voltage" },
325 	{ "MOU31", t_value, "output.L3-L1.voltage" },
326 
327 	{ "MPUL1", t_value, "input.bypass.L1-N.voltage" },
328 	{ "MPUL2", t_value, "input.bypass.L2-N.voltage" },
329 	{ "MPUL3", t_value, "input.bypass.L3-N.voltage" },
330 
331 	{ "MUTE1", t_value, "ups.temperature" },
332 	{ NULL }
333 };
334 
335 
336 /* Responses for UPDV */
337 static simple_t nominal[] = {
338 	{ "NIUHH", t_value, "input.voltage.maximum" },
339 	{ "NIULL", t_value, "input.voltage.minimum" },
340 	{ "NIUNN", t_value, "input.voltage.nominal" },
341 
342 	{ "NIIHH", t_value, "input.current.maximum" },
343 	{ "NIILL", t_value, "input.current.minimum" },
344 	{ "NIINN", t_value, "input.current.nominal" },
345 
346 	{ "NIPHH", t_value, "input.realpower.maximum" },
347 	{ "NIPNN", t_value, "input.realpower.nominal" },
348 
349 	{ "NISHH", t_value, "input.power.maximum" },
350 	{ "NISNN", t_value, "input.power.nominal" },
351 
352 	{ "NBAHN", t_setval, "battery.capacity.nominal", 0, &batt_cap_nom },
353 	{ "NBIHH", t_ignore, "battery charge current maximum" },
354 	{ "NBILL", t_setval, NULL, 0, &batt_disch_curr_max},
355 	{ "NBINN", t_value, "battery.current.nominal" },
356 	{ "NBTHH", t_setval, NULL, 0, &batt_runtime_max},
357 	{ "NBUHH", t_setval, "battery.voltage.maximum", 0, &batt_volt_high },
358 	{ "NBULL", t_setval, "battery.voltage.minimum", 0, &batt_volt_low },
359 	{ "NBUNN", t_setval,  "battery.voltage.nominal", 0, &batt_volt_nom },
360 
361 	{ "NOFHH", t_value,  "output.frequency.maximum" },
362 	{ "NOFLL", t_final,  "output.frequency.minimum" },
363 	{ "NOIHH", t_setval,  "output.current.maximum", 0, &max_out_current },
364 	{ "NOINN", t_setval, "output.current.nominal", 0, &nom_out_current },
365 	{ "NOPNN", t_value, "output.realpower.nominal", 0, &outpwr_factor },
366 	{ "NOSNN", t_value,  "ups.power.nominal", 0, &outpwr_factor },
367 	{ "NOUHH", t_value,  "output.voltage.maximum" },
368 	{ "NOULL", t_value,  "output.voltage.minimum" },
369 	{ "NOUNN", t_value,  "output.voltage.nominal" },
370 
371 	{ "NUTEH", t_value,  "ups.temperature.maximum" },
372 	{ NULL }
373 };
374 
375 
376 /* Status responses for UPBS command */
377 static simple_t battery[] = {
378 	{ "MBTE1", t_value, "battery.1.temperature" },
379 	{ "MBIN1", t_ignore, NULL /* aging index */ },
380 	{ "BDAT1", t_string, "battery.1.date" },
381 	{ "MBTE2", t_value, "battery.2.temperature.2" },
382 	{ "MBIN2", t_ignore, NULL },
383 	{ "BDAT2", t_string, "battery.2.date" },
384 	{ "MBTE3", t_value, "battery.3.temperature" },
385 	{ "MBIN3", t_ignore, NULL },
386 	{ "BDAT3", t_string, "battery.3.date" },
387 	{ "MBTE4", t_value, "battery.4.temperature" },
388 	{ "MBIN4", t_ignore, NULL },
389 	{ "BDAT4", t_string, "battery.4.date" },
390 	{ "MBTE5", t_value, "battery.5.temperature" },
391 	{ "MBIN5", t_ignore, NULL },
392 	{ "BDAT5", t_string, "battery.5.date" },
393 	{ "MBTE6", t_value, "battery.6.temperature" },
394 	{ "MBIN6", t_ignore, NULL },
395 	{ "BDAT6", t_string, "battery.6.date" },
396 	{ "MBTE7", t_value, "battery.7.temperature" },
397 	{ "MBIN7", t_ignore, NULL },
398 	{ "BDAT7", t_string, "battery.7.date" },
399 	{ "MBTE8", t_value, "battery.8.temperature" },
400 	{ "MBIN8", t_ignore, NULL },
401 	{ "BDAT8", t_finstr, "battery.8.date" },
402 	{ NULL }
403 };
404 
405 
406 static cmd_t commands[] = {
407 	{ "load.off",			NULL, NULL },
408 	{ "load.on",			NULL, NULL },
409 	{ "shutdown.return",		"UPPF", "IJHLDMGCIU" },
410 	{ "shutdown.stayoff",		"UPPD", "LGGNLMDPGV" },
411 	{ "shutdown.stop",		"UPPU", NULL },
412 	{ "shutdown.reboot",		"UPPC", "IJHLDMGCIU" },
413 	{ "shutdown.reboot.graceful",	NULL, NULL },
414 	{ "test.panel.start",		"UPIS", NULL },
415 	{ "test.panel.stop",		NULL, NULL },
416 	{ "test.failure.start",		NULL, NULL },
417 	{ "test.failure.stop",		NULL, NULL },
418 	{ "test.battery.start",		"UPBT", "1" },
419 	{ "test.battery.stop",		NULL, NULL },
420 	{ "calibrate.start",		NULL, NULL },
421 	{ "calibrate.stop",		NULL, NULL },
422 	{ "bypass.start",		NULL, NULL },
423 	{ "bypass.stop",		NULL, NULL },
424 	{ "reset.input.minmax",		NULL, NULL },
425 	{ "reset.watchdog",		NULL, NULL },
426 	{ "beeper.enable",		NULL, NULL },
427 	{ "beeper.disable",		NULL, NULL },
428 	{ "beeper.on",			NULL, NULL },
429 	{ "beeper.off",			NULL, NULL },
430 	{ NULL }
431 };
432 
433 
434 static cmd_t variables[] = {
435 	{ "ups.delay.reboot",		"UPCD", "ACCD" },
436 	{ "ups.delay.shutdown",		"UPSD", "ACSD" },
437 	{ NULL }
438 };
439 
440 
441 static int instcmd (const char *auxcmd, const char *data);
442 static int setvar (const char *var, const char *data);
443 static void upsc_setstatus(unsigned int status);
444 static void upsc_flush_input(void);
445 static void upsc_getbaseinfo(void);
446 static int upsc_commandlist(void);
447 static int upsc_getparams(const char *cmd, const simple_t *table);
448 static int upsc_getvalue(const char *cmd, const char *param,
449 	const char *resp, const char *var, char *ret);
450 static int upscsend(const char *cmd);
451 static int upscrecv(char *buf);
452 static int upsc_simple(const simple_t *sp, const char *var, const char *val);
453 static void check_uppm(void);
454 static float batt_charge_pct(void);
455 
456 
457 
upsdrv_help(void)458 void upsdrv_help(void)
459 {
460 }
461 
462 
upsdrv_initups(void)463 void upsdrv_initups(void)
464 {
465 	struct termios tio;
466 	int baud = B1200;
467 	char *str;
468 
469 	if ((str = getval("baudrate")) != NULL) {
470 		int temp = atoi(str);
471 		switch (temp) {
472 		case   300:
473 			baud =   B300; break;
474 		case   600:
475 			baud =   B600; break;
476 		case  1200:
477 			baud =  B1200; break;
478 		case  2400:
479 			baud =  B2400; break;
480 		case  4800:
481 			baud =  B4800; break;
482 		case  9600:
483 			baud =  B9600; break;
484 		case 19200:
485 			baud = B19200; break;
486 		case 38400:
487 			baud = B38400; break;
488 		default:
489 			fatalx(EXIT_FAILURE, "Unrecognized baudrate: %s", str);
490 		}
491 		upsdebugx(1, "baud_rate = %d", temp);
492 	}
493 	upsfd = ser_open(device_path);
494 	ser_set_speed(upsfd, device_path, baud);
495 
496 	if (tcgetattr(upsfd, &tio) != 0)
497 		fatal_with_errno(EXIT_FAILURE, "tcgetattr(%s)", device_path);
498 	tio.c_lflag = ICANON;
499 	tio.c_iflag |= IGNCR;	/* Ignore CR */
500 	tio.c_cc[VMIN] = 0;
501 	tio.c_cc[VTIME] = 0;
502 	tcsetattr(upsfd, TCSANOW, &tio);
503 
504 	if ((str = getval("input_timeout")) != NULL) {
505 		int temp = atoi(str);
506 		if (temp <= 0)
507 			fatalx(EXIT_FAILURE, "Bad input_timeout parameter: %s", str);
508 		input_timeout_sec = temp;
509 	}
510 	upsdebugx(1, "input_timeout = %d Sec", input_timeout_sec);
511 
512 	if ((str = getval("output_pace")) != NULL) {
513 		int temp = atoi(str);
514 		if (temp <= 0)
515 			fatalx(EXIT_FAILURE, "Bad output_pace parameter: %s", str);
516 		output_pace_usec = temp;
517 	}
518 	upsdebugx(1, "output_pace = %d uSec", output_pace_usec);
519 
520 	if ((str = getval("full_update_timer")) != NULL) {
521 		int temp = atoi(str);
522 		if (temp <= 0)
523 			fatalx(EXIT_FAILURE, "Bad full_update_timer parameter: %s", str);
524 		full_update_timer = temp;
525 	}
526 	upsdebugx(1, "full_update_timer = %d Sec", full_update_timer);
527 
528 	use_crlf = testvar("use_crlf");
529 	upsdebugx(1, "use_crlf = %d", use_crlf);
530 	use_pre_lf = testvar("use_pre_lf");
531 	upsdebugx(1, "use_pre_lf = %d", use_pre_lf);
532 }
533 
534 
upsdrv_initinfo(void)535 void upsdrv_initinfo(void)
536 {
537 	if (!upsc_commandlist()) {
538 		upslogx(LOG_ERR, "No contact with UPS, delaying init.");
539 		status = UPSC_STAT_NOTINIT;
540 		return;
541 	} else {
542 		status = 0;
543 	}
544 
545 	upsc_getbaseinfo();
546 	if (can_upda) {
547 		upsc_flush_input();
548 		upscsend("UPDA");
549 	}
550 	if (can_upid) {
551 		upsc_getvalue("UPID", NULL, "ACID", "ups.id", NULL);
552 	}
553 	if (can_uppm) {
554 		check_uppm();
555 	}
556 
557 	/* make sure we have some sensible defaults */
558 	setvar("ups.delay.shutdown", "10");
559 	setvar("ups.delay.reboot", "60");
560 
561 	upsh.instcmd = instcmd;
562 	upsh.setvar = setvar;
563 }
564 
565 
566 /* Change a variable name in a table */
change_name(simple_t * sp,const char * oldname,const char * newname)567 static void change_name(simple_t *sp,
568 		const char *oldname, const char *newname)
569 {
570 	while(sp->code) {
571 		if (sp->desc && !strcmp(sp->desc, oldname)) {
572 			sp->desc = strdup(newname);
573 			if (dstate_getinfo(oldname)) {
574 				dstate_setinfo(newname, "%s",
575 						dstate_getinfo(oldname));
576 			}
577 			dstate_delinfo(oldname);
578 			upsdebugx(1, "Changing name: %s => %s", oldname, newname);
579 			break;
580 		}
581 		sp++;
582 	}
583 }
584 
calc_upsload(void)585 static float calc_upsload(void) {
586 
587 	float 	load=-1, nom_out_power=-1, nom_out_realpower=-1, maxcurr, tmp;
588 	const char	*s;
589 
590 	/* Some UPSen (Fiskars 9000 for example) only reports current, and
591 	 * only the max current */
592 	if (nom_out_current > 0) {
593 		maxcurr = nom_out_current;
594 	}
595 	else {
596 		maxcurr = max_out_current;
597 	}
598 
599 	if (maxcurr > 0) {
600 		if ((s=dstate_getinfo("output.L1.current")) ||
601 				(s=dstate_getinfo("output.current"))) {
602 			if (sscanf(s, "%f", &tmp) == 1) {
603 				load = tmp/maxcurr;
604 			}
605 		}
606 		if ((s=dstate_getinfo("output.L2.current"))) {
607 			if (sscanf(s, "%f", &tmp) == 1) {
608 				tmp=tmp/maxcurr;
609 				if (tmp>load) {
610 					load = tmp;
611 				}
612 			}
613 		}
614 		if ((s=dstate_getinfo("output.L3.current"))) {
615 			if (sscanf(s, "%f", &tmp) == 1) {
616 				tmp=tmp/maxcurr;
617 				if (tmp>load) {
618 					load = tmp;
619 				}
620 			}
621 		}
622 	}
623 
624 	/* This is aggregated (all phases) */
625 	if ((s=dstate_getinfo("ups.power.nominal"))) {
626 		if (sscanf(s, "%f", &nom_out_power) != 1) {
627 			nom_out_power = -1;
628 		}
629 	}
630 
631 	if (nom_out_power > 0) {
632 		if ((s=dstate_getinfo("output.L1.power"))) {
633 			if (sscanf(s, "%f", &tmp) == 1) {
634 				tmp /= (nom_out_power/num_outphases);
635 				if (tmp>load) {
636 					load = tmp;
637 				}
638 				dstate_setinfo("output.L1.power.percent",
639 						"%.1f", tmp*100);
640 			}
641 		}
642 		if ((s=dstate_getinfo("output.L2.power"))) {
643 			if (sscanf(s, "%f", &tmp) == 1) {
644 				tmp /= (nom_out_power/num_outphases);
645 				if (tmp>load) {
646 					load = tmp;
647 				}
648 				dstate_setinfo("output.L2.power.percent",
649 						"%.1f", tmp*100);
650 			}
651 		}
652 		if ((s=dstate_getinfo("output.L3.power"))) {
653 			if (sscanf(s, "%f", &tmp) == 1) {
654 				tmp /= (nom_out_power/num_outphases);
655 				if (tmp>load) {
656 					load = tmp;
657 				}
658 				dstate_setinfo("output.L3.power.percent",
659 						"%.1f", tmp*100);
660 			}
661 		}
662 	}
663 
664 	/* This is aggregated (all phases) */
665 	if ((s=dstate_getinfo("output.realpower.nominal"))) {
666 		if (sscanf(s, "%f", &nom_out_realpower) != 1) {
667 			nom_out_realpower = -1;
668 		}
669 	}
670 	if (nom_out_realpower >= 0) {
671 		if ((s=dstate_getinfo("output.L1.realpower"))) {
672 			if (sscanf(s, "%f", &tmp) == 1) {
673 				tmp /= (nom_out_realpower/num_outphases);
674 				if (tmp>load) {
675 					load = tmp;
676 				}
677 				dstate_setinfo("output.L1.realpower.percent",
678 						"%.1f", tmp*100);
679 			}
680 		}
681 		if ((s=dstate_getinfo("output.L2.realpower"))) {
682 			if (sscanf(s, "%f", &tmp) == 1) {
683 				tmp /= (nom_out_realpower/num_outphases);
684 				if (tmp>load) {
685 					load = tmp;
686 				}
687 				dstate_setinfo("output.L2.realpower.percent",
688 						"%.1f", tmp*100);
689 			}
690 		}
691 		if ((s=dstate_getinfo("output.L3.realpower"))) {
692 			if (sscanf(s, "%f", &tmp) == 1) {
693 				tmp /= (nom_out_realpower/num_outphases);
694 				if (tmp>load) {
695 					load = tmp;
696 				}
697 				dstate_setinfo("output.L3.realpower.percent",
698 						"%.1f", tmp*100);
699 			}
700 		}
701 	}
702 
703 	return load;
704 }
705 
706 
upsdrv_updateinfo(void)707 void upsdrv_updateinfo(void)
708 {
709 	time_t now;
710 	int ok;
711 	float load;
712 
713 	if (status & UPSC_STAT_NOTINIT) {
714 		upsdrv_initinfo();
715 	}
716 
717 	if (status & UPSC_STAT_NOTINIT) {
718 		return;
719 	}
720 
721 	status = 0;
722 
723 	ok = upsc_getparams("UPDS", simple);
724 
725 	time(&now);
726 	if (ok && now - last_full > full_update_timer) {
727 		last_full = now;
728 		ok = upsc_getparams("UPDV", nominal);
729 		if (ok && can_upbs)
730 			ok = upsc_getparams("UPBS", battery);
731 	}
732 
733 	if (!ok) {
734 		dstate_datastale();
735 		last_full = 0;
736 		return;
737 	}
738 
739 	if (!inited_phaseinfo) {
740 		if (dstate_getinfo("input.L3-L1.voltage") ||
741 				dstate_getinfo("input.L3-N.voltage")) {
742 			num_inphases = 3;
743 
744 			change_name(simple,
745 				"input.current", "input.L1.current");
746 			change_name(simple,
747 				"input.realpower", "input.L1.realpower");
748 			change_name(simple,
749 				"input.power", "input.L1.power");
750 			change_name(simple,
751 				"input.voltage", "input.L1-N.voltage");
752 		}
753 		if (dstate_getinfo("output.L3-L1.voltage") ||
754 				dstate_getinfo("output.L3-N.voltage")) {
755 			const char *s;
756 
757 			num_outphases = 3;
758 
759 			if ((s=dstate_getinfo("ups.model")) &&
760 					(!strncmp(s, "UPS9075", 7) ||
761 					!strncmp(s, "UPS9100", 7) ||
762 					!strncmp(s, "UPS9150", 7) ||
763 					!strncmp(s, "UPS9200", 7) ||
764 					!strncmp(s, "UPS9250", 7) ||
765 					!strncmp(s, "UPS9300", 7) ||
766 					!strncmp(s, "UPS9400", 7) ||
767 					!strncmp(s, "UPS9500", 7) ||
768 					!strncmp(s, "UPS9600", 7)) ) {
769 				/* Insert kludges for Fiskars UPS9000 here */
770 				upslogx(LOG_INFO, "Fiskars UPS9000 detected, protocol kludges activated");
771 				batt_volt_nom = 384;
772 				dstate_setinfo("battery.voltage.nominal", "%.0f", batt_volt_nom);
773 
774 			}
775 			else {
776 				outpwr_factor *= 3;
777 			}
778 
779 			change_name(simple,
780 				"output.current", "output.L1.current");
781 			change_name(simple,
782 				"output.current.peak", "output.L1.current.peak");
783 			change_name(simple,
784 				"output.realpower", "output.L1.realpower");
785 			change_name(simple,
786 				"output.power", "output.L1.power");
787 			change_name(simple,
788 				"output.voltage", "output.L1-N.voltage");
789 		}
790 
791 		dstate_setinfo("input.phases", "%d", num_inphases);
792 		dstate_setinfo("output.phases", "%d", num_outphases);
793 
794 		inited_phaseinfo=1;
795 	}
796 
797 	load = calc_upsload();
798 
799 	if (load >= 0) {
800 		upsdebugx(2, "ups.load: %.1f", load*100);
801 		dstate_setinfo("ups.load", "%.1f", load*100);
802 	}
803 	else {
804 		upsdebugx(2, "ups.load: No value");
805 	}
806 
807 	/* TODO/FIXME: Set UPS date/time on startup and daily if needed */
808 	if (can_updt) {
809 		char dtbuf[UPSC_BUFLEN];
810 		if (upsc_getvalue("UPDT", "0", "ACDT", NULL, dtbuf)) {
811 			dstate_setinfo("ups.date", "%s", dtbuf);
812 		}
813 	}
814 	if (can_uptm) {
815 		char tmbuf[UPSC_BUFLEN];
816 		if (upsc_getvalue("UPTM", "0", "ACTM", NULL, tmbuf)) {
817 			dstate_setinfo("ups.time", "%s", tmbuf);
818 		}
819 	}
820 
821 
822 	if (batt_charge < 0) {
823 		if (batt_current < 0) {
824 			/* Reset battery current history if discharging */
825 			numbatthist = lastbatthist = 0;
826 		}
827 		batt_charge = batt_charge_pct();
828 	}
829 	if (batt_charge >= 0) {
830 		dstate_setinfo("battery.charge", "%.1f", batt_charge);
831 	}
832 	else {
833 		dstate_delinfo("battery.charge");
834 	}
835 
836 	/* 9999 == unknown value */
837 	if (batt_runtime >= 0 && batt_runtime < 9999) {
838 		dstate_setinfo("battery.runtime", "%.0f", batt_runtime*60);
839 	}
840 	else if (load > 0 && batt_disch_curr_max != 0) {
841 		float est_battcurr = load * abs(batt_disch_curr_max);
842 		/* Peukert equation */
843 		float runtime = (batt_cap_nom*3600)/pow(est_battcurr, 1.35);
844 
845 		upsdebugx(2, "Calculated runtime: %.0f seconds", runtime);
846 		if (batt_runtime_max > 0 && runtime > batt_runtime_max*60) {
847 			runtime = batt_runtime_max*60;
848 		}
849 		dstate_setinfo("battery.runtime", "%.0f", runtime);
850 
851 	}
852 	else if (batt_runtime_max > 0) {
853 		/* Show max possible runtime as reported by UPS */
854 		dstate_setinfo("battery.runtime", "%.0f", batt_runtime_max*60);
855 	}
856 	else {
857 		dstate_delinfo("battery.runtime");
858 	}
859 	/* Some UPSen only provides this when on battery, so reset between
860 	 * each iteration to make sure we use the right value */
861 	batt_charge = -1;
862 	batt_runtime = -1;
863 
864 
865 	if (!(status & UPSC_STAT_ONBATT))
866 		status |= UPSC_STAT_ONLINE;
867 
868 	upsc_setstatus(status);
869 
870 	dstate_dataok();
871 	ser_comm_good();
872 }
873 
874 
upsdrv_shutdown(void)875 void upsdrv_shutdown(void)
876 {
877 	upslogx(LOG_EMERG, "Shutting down...");
878 
879 	/* send shutdown command twice, just to be sure */
880 	instcmd("shutdown.reboot", NULL);
881 	sleep(1);
882 	instcmd("shutdown.reboot", NULL);
883 	sleep(1);
884 }
885 
886 
instcmd(const char * auxcmd,const char * data)887 static int instcmd (const char *auxcmd, const char *data)
888 {
889 	cmd_t *cp;
890 
891 	if (!strcasecmp(auxcmd, "beeper.off")) {
892 		/* compatibility mode for old command */
893 		upslogx(LOG_WARNING,
894 			"The 'beeper.off' command has been renamed to 'beeper.disable'");
895 		return instcmd("beeper.disable", NULL);
896 	}
897 
898 	if (!strcasecmp(auxcmd, "beeper.on")) {
899 		/* compatibility mode for old command */
900 		upslogx(LOG_WARNING,
901 			"The 'beeper.on' command has been renamed to 'beeper.enable'");
902 		return instcmd("beeper.enable", NULL);
903 	}
904 
905 	upsdebugx(1, "Instcmd: %s %s", auxcmd, data ? data : "\"\"");
906 
907 	for (cp = commands; cp->cmd; cp++) {
908 		if (strcasecmp(cp->cmd, auxcmd)) {
909 			continue;
910 		}
911 		upscsend(cp->upsc);
912 		if (cp->upsp) {
913 			upscsend(cp->upsp);
914 		} else if (data) {
915 			upscsend(data);
916 		}
917 		return STAT_INSTCMD_HANDLED;
918 	}
919 
920 	upslogx(LOG_INFO, "instcmd: unknown command %s", auxcmd);
921 	return STAT_INSTCMD_UNKNOWN;
922 }
923 
924 
setvar(const char * var,const char * data)925 static int setvar (const char *var, const char *data)
926 {
927 	cmd_t *cp;
928 
929 	upsdebugx(1, "Setvar: %s %s", var, data);
930 
931 	for (cp = variables; cp->cmd; cp++) {
932 		if (strcasecmp(cp->cmd, var)) {
933 			continue;
934 		}
935 		upsc_getvalue(cp->upsc, data, cp->upsp, cp->cmd, NULL);
936 		return STAT_SET_HANDLED;
937 	}
938 
939 	upslogx(LOG_INFO, "Setvar: unsettable variable %s", var);
940 	return STAT_SET_UNKNOWN;
941 }
942 
943 
944 /* list flags and values that you want to receive via -x */
upsdrv_makevartable(void)945 void upsdrv_makevartable(void)
946 {
947 	addvar(VAR_VALUE, "manufacturer", "manufacturer [unknown]");
948 	addvar(VAR_VALUE, "baudrate", "Serial interface baudrate [1200]");
949 	addvar(VAR_VALUE, "input_timeout", "Command response timeout [2]");
950 	addvar(VAR_VALUE, "output_pace", "Output character delay in usecs [200]");
951 	addvar(VAR_VALUE, "full_update", "Delay between full value downloads [60]");
952 	addvar(VAR_FLAG, "use_crlf", "Use CR-LF to terminate commands to UPS");
953 	addvar(VAR_FLAG, "use_pre_lf", "Use LF to introduce commands to UPS");
954 }
955 
956 
upsdrv_cleanup(void)957 void upsdrv_cleanup(void)
958 {
959 	ser_close(upsfd, device_path);
960 }
961 
962 
963 /*
964  * Generate status string from bitfield
965  */
upsc_setstatus(unsigned int status)966 static void upsc_setstatus(unsigned int status)
967 {
968 
969 	/*
970 	 * I'll look for all available statuses, even though they might not be
971 	 *  supported in the UPScode II protocol.
972 	 */
973 
974 	status_init();
975 
976 	if (status & UPSC_STAT_ONLINE)
977 		status_set("OL");
978 	if (status & UPSC_STAT_ONBATT)
979 		status_set("OB");
980 	if (status & UPSC_STAT_LOBATT)
981 		status_set("LB");
982 	if (status & UPSC_STAT_REPLACEBATT)
983 		status_set("RB");
984 	if (status & UPSC_STAT_BOOST)
985 		status_set("BOOST");
986 	if (status & UPSC_STAT_TRIM)
987 		status_set("TRIM");
988 	if (status & UPSC_STAT_OVERLOAD)
989 		status_set("OVER");
990 	if (status & UPSC_STAT_CALIBRATION)
991 		status_set("CAL");
992 	if (status & UPSC_STAT_OFF)
993 		status_set("OFF");
994 	if (status & UPSC_STAT_BYPASS)
995 		status_set("BYPASS");
996 
997 	status_commit();
998 }
999 
1000 
1001 /* Add \r to end of command and send to UPS */
1002 /* returns < 0 on errors, 0 on timeout and > 0 on success. */
upscsend(const char * cmd)1003 static int upscsend(const char *cmd)
1004 {
1005 	int	res;
1006 
1007 	res = ser_send_pace(upsfd, output_pace_usec, "%s%s%s",
1008 		use_pre_lf ? "\n" : "",
1009 		cmd,
1010 		use_crlf ? "\r\n" : "\r");
1011 
1012 	if (res < 0) {
1013 		upsdebug_with_errno(3, "upscsend");
1014 	} else if (res == 0) {
1015 		upsdebugx(3, "upscsend: Timeout");
1016 	} else {
1017 		upsdebugx(3, "upscsend: '%s'", cmd);
1018 	}
1019 
1020 	return res;
1021 }
1022 
1023 
1024 /* Return a string read from UPS */
1025 /* returns < 0 on errors, 0 on timeout and > 0 on success. */
upscrecv(char * buf)1026 static int upscrecv(char *buf)
1027 {
1028 	int	res;
1029 
1030 	/* NOTE: the serial port is set to use Canonical Mode Input Processing,
1031 	   which means ser_get_buf() either returns one line terminated with
1032 	   ENDCHAR, an error or times out. */
1033 
1034 	while (1) {
1035 		res = ser_get_buf(upsfd, buf, UPSC_BUFLEN, input_timeout_sec, 0);
1036 		if (res != 1) {
1037 			break;
1038 		}
1039 
1040 		/* Only one character, must be ENDCHAR */
1041 		upsdebugx(3, "upscrecv: Empty line");
1042 	}
1043 
1044 	if (res < 0) {
1045 		upsdebug_with_errno(3, "upscrecv");
1046 	} else if (res == 0) {
1047 		upsdebugx(3, "upscrecv: Timeout");
1048 	} else {
1049 		upsdebugx(3, "upscrecv: %u bytes:\t'%s'", res-1, str_rtrim(buf, ENDCHAR));
1050 	}
1051 
1052 	return res;
1053 }
1054 
1055 
upsc_flush_input(void)1056 static void upsc_flush_input(void)
1057 {
1058 /*
1059 	char buf[UPSC_BUFLEN];
1060 
1061 	do {
1062 		upscrecv(buf);
1063 		if (strlen(buf) > 0)
1064 			upsdebugx(1, "Skipping input: %s", buf);
1065 	} while (strlen(buf) > 0);
1066 */
1067 	ser_flush_in(upsfd, "", nut_debug_level);
1068 }
1069 
1070 
1071 /* check which commands this ups supports.
1072  * Returns TRUE if command list was recieved, FALSE otherwise */
upsc_commandlist(void)1073 static int upsc_commandlist(void)
1074 {
1075 	char buf[UPSC_BUFLEN];
1076 	cmd_t *cp;
1077 
1078 	upsc_flush_input();
1079 	upscsend("UPCL");
1080 	while (1) {
1081 		upscrecv(buf);
1082 		if (strlen(buf) == 0) {
1083 			upslogx(LOG_ERR, "Missing UPCL after UPCL");
1084 			return 0;
1085 		}
1086 		upsdebugx(2, "Supports command: %s", buf);
1087 
1088 		if (strcmp(buf, "UPBS") == 0)
1089 			can_upbs = 1;
1090 		else if (strcmp(buf, "UPPM") == 0)
1091 			can_uppm = 1;
1092 		else if (strcmp(buf, "UPID") == 0)
1093 			can_upid = 1;
1094 		else if (strcmp(buf, "UPDA") == 0)
1095 			can_upda = 1;
1096 		else if (strcmp(buf, "UPDT") == 0)
1097 			can_updt = 1;
1098 		else if (strcmp(buf, "UPTM") == 0)
1099 			can_uptm = 1;
1100 		else if (strcmp(buf, "UPSD") == 0)
1101 			can_upsd = 1;
1102 		else if (strcmp(buf, "UPPC") == 0)
1103 			can_uppc = 1;
1104 
1105 		for (cp = commands; cp->cmd; cp++) {
1106 			if (cp->upsc && strcmp(cp->upsc, buf) == 0) {
1107 				upsdebugx(1, "instcmd: %s %s", cp->cmd, cp->upsc);
1108 				dstate_addcmd(cp->cmd);
1109 				cp->enabled = 1;
1110 	            break;
1111 			}
1112 		}
1113 
1114 		for (cp = variables; cp->cmd; cp++) {
1115 			if (cp->upsc && strcmp(cp->upsc, buf) == 0) {
1116 				upsdebugx(1, "setvar: %s %s", cp->cmd, cp->upsc);
1117 				cp->enabled = 1;
1118 				break;
1119 			}
1120 		}
1121 
1122 		if (strcmp(buf, "UPCL") == 0)
1123 			break;
1124 	}
1125 
1126 	for (cp = variables; cp->cmd; cp++) {
1127 		if (cp->enabled) {
1128 			upsc_getvalue(cp->upsc, "0000", cp->upsp, cp->cmd, NULL);
1129 			dstate_setflags(cp->cmd, ST_FLAG_RW | ST_FLAG_STRING);
1130 			dstate_setaux(cp->cmd, 7);
1131 		}
1132 	}
1133 
1134 	return 1;
1135 }
1136 
1137 
1138 /* get limits and parameters */
upsc_getparams(const char * cmd,const simple_t * table)1139 static int upsc_getparams(const char *cmd, const simple_t *table)
1140 {
1141 	char var[UPSC_BUFLEN];
1142 	char val[UPSC_BUFLEN];
1143 	int first = 1;
1144 
1145 	upsc_flush_input();
1146 
1147 	upscsend(cmd);
1148 	buffer_empty = 0;
1149 	while (!buffer_empty) {
1150 		upscrecv(var);
1151 		if (strlen(var) == 0) {
1152 			if (first)
1153 				upscrecv(var);
1154 			if (strlen(var) == 0) {
1155 				ser_comm_fail("Empty string from UPS for %s!",
1156 					cmd);
1157 				break;
1158 			}
1159 		}
1160 		first = 0;
1161 		upscrecv(val);
1162 		if (strlen(val) == 0) {
1163 			ser_comm_fail("Empty value from UPS for %s %s!", cmd, var);
1164 			break;
1165 		}
1166 		upsdebugx(2, "Parameter %s %s", var, val);
1167 		if (!upsc_simple(table, var, val))
1168 			upslogx(LOG_ERR, "Unknown response to %s: %s %s",
1169 				cmd, var, val);
1170 	}
1171 	return buffer_empty;
1172 }
1173 
1174 
check_uppm(void)1175 static void check_uppm(void)
1176 {
1177 	int i, last = 0;
1178 	char var[UPSC_BUFLEN];
1179 	char val[UPSC_BUFLEN];
1180 
1181 	upsc_flush_input();
1182 	upscsend("UPPM");
1183 	upscsend("0");
1184 	upscrecv(var);
1185 	if (strcmp(var, "ACPM"))
1186 		upslogx(LOG_ERR, "Bad response to UPPM: %s", var);
1187 	while (1) {
1188 		int val, stat;
1189 		upscrecv(var);
1190 		if (strlen(var) == 0)
1191 			break;
1192 		upsdebugx(2, "UPPM available: %s", var);
1193 		stat = sscanf(var, "P%2d", &val);
1194 		if (stat != 1) {
1195 			upslogx(LOG_ERR, "Bad response to UPPM: %s", var);
1196 			return;
1197 		}
1198 		has_uppm_p[val] = 1;
1199 		if (val > last)
1200 			last = val;
1201 	}
1202 
1203 	for (i = 0; i <= last; i++) {
1204 		if (!has_uppm_p[i])
1205 			continue;
1206 		upscsend("UPPM");
1207 		snprintf(var, sizeof(var), "P%.2d", i);
1208 		upscsend(var);
1209 		upscrecv(val);
1210 		if (strcmp(val, "ACPM")) {
1211 			upslogx(LOG_ERR, "Bad response to UPPM %s: %s", var, val);
1212 			continue;
1213 		}
1214 		upscrecv(var);
1215 		upsdebugx(1, "UPPM value: %s", var);
1216 	}
1217 }
1218 
1219 
upsc_getvalue(const char * cmd,const char * param,const char * resp,const char * nutvar,char * ret)1220 static int upsc_getvalue(const char *cmd, const char *param,
1221 			const char *resp, const char *nutvar, char *ret)
1222 {
1223 	char var[UPSC_BUFLEN];
1224 	char val[UPSC_BUFLEN];
1225 
1226 	upsdebugx(2, "Request value: %s %s", cmd, param ? param : "\"\"");
1227 	upscsend(cmd);
1228 	if (param)
1229 		upscsend(param);
1230 	upscrecv(var);
1231 	upscrecv(val);
1232 	upsdebugx(2, "Got value: %s %s", var, val);
1233 	if (strcmp(var, resp)) {
1234 		upslogx(LOG_ERR, "Bad response to %s %s: %s %s",
1235 			cmd, param ? param : "\"\"", var, val);
1236 		return 0;
1237 	}
1238 	else {
1239 		if (nutvar)
1240 			dstate_setinfo(nutvar, "%s", val);
1241 		if (ret)
1242 			strcpy(ret, val);
1243 	}
1244 	return 1;
1245 }
1246 
1247 
upsc_getbaseinfo(void)1248 static void upsc_getbaseinfo(void)
1249 {
1250 	char *buf;
1251 
1252 	dstate_setinfo("ups.mfr", "%s",
1253 		((buf = getval("manufacturer")) != NULL) ? buf : "unknown");
1254 
1255 	if (!upsc_getvalue("UPTP", NULL, "NNAME", "ups.model", NULL))
1256 		upsc_getvalue("UPTP", NULL, "NNAME", "ups.model", NULL);
1257 	upsc_getvalue("UPSN", "0", "ACSN", "ups.serial", NULL);
1258 }
1259 
1260 
upsc_simple(const simple_t * sp,const char * var,const char * val)1261 static int upsc_simple(const simple_t *sp, const char *var, const char *val)
1262 {
1263 	int stat;
1264 	float fval;
1265 
1266 	while (sp->code) {
1267 		if (strcmp(sp->code, var) == 0) {
1268 			switch (sp->type) {
1269 			case t_setval:
1270 				stat = sscanf(val, "%f", &fval);
1271 				if (stat != 1)
1272 					upslogx(LOG_ERR, "Bad float: %s %s", var, val);
1273 				if (sp->desc)
1274 					dstate_setinfo(sp->desc, "%.2f", fval);
1275 				*sp->aux = fval;
1276 				break;
1277 			case t_setrecip:
1278 				stat = sscanf(val, "%f", &fval);
1279 				if (stat != 1)
1280 					upslogx(LOG_ERR, "Bad float: %s %s", var, val);
1281 				if (sp->desc)
1282 					dstate_setinfo(sp->desc, "%s", val);
1283 				*sp->aux = 1/fval;
1284 				break;
1285 			case t_setpct:
1286 				stat = sscanf(val, "%f", &fval);
1287 				if (stat != 1)
1288 					upslogx(LOG_ERR, "Bad float: %s %s", var, val);
1289 				*sp->aux = fval*100;
1290 				if (sp->desc)
1291 					dstate_setinfo(sp->desc, "%s", val);
1292 				break;
1293 			case t_setrecpct:
1294 				stat = sscanf(val, "%f", &fval);
1295 				if (stat != 1)
1296 					upslogx(LOG_ERR, "Bad float: %s %s", var, val);
1297 				*sp->aux = 1/fval*100;
1298 				if (sp->desc)
1299 					dstate_setinfo(sp->desc, "%s", val);
1300 				break;
1301 			case t_final:
1302 				buffer_empty = 1;
1303 			case t_value:
1304 				if (!sp->desc) {
1305 					break;
1306 				}
1307 				if (sscanf(val, "%f", &fval) == 1) {
1308 					if (sp->aux != NULL) {
1309 						fval *= *(sp->aux);
1310 					}
1311 					dstate_setinfo(sp->desc, "%.2f", fval);
1312 				}
1313 				else {
1314 					upslogx(LOG_ERR, "Bad float in %s: %s", var, val);
1315 					dstate_setinfo(sp->desc, "%s", val);
1316 				}
1317 				break;
1318 			case t_finstr:
1319 				buffer_empty = 1;
1320 			case t_string:
1321 				if (!sp->desc) {
1322 					break;
1323 				}
1324 				dstate_setinfo(sp->desc, "%s", val);
1325 				break;
1326 			case t_status:
1327 				if (strcmp(val, "00") == 0)
1328 					;
1329 				else if (strcmp(val, "11") == 0)
1330 					status |= sp->status;
1331 				else
1332 					upslogx(LOG_ERR, "Unknown status value: '%s' '%s'", var, val);
1333 				break;
1334 			case t_alarm:
1335 				if (strcmp(val, "00") == 0)
1336 					;
1337 				else if (strcmp(val, "11") == 0)
1338 					status |= sp->status;
1339 				else
1340 					upslogx(LOG_ERR, "Unknown alarm value: '%s' '%s'", var, val);
1341 				break;
1342 			case t_ignore:
1343 				upsdebugx(3, "Ignored value: %s %s", var, val);
1344 				break;
1345 			case t_list:
1346 				if (!upsc_simple(sp->stats, val, "11"))
1347 					upslogx(LOG_ERR, "Unknown value: %s %s",
1348 						var, val);
1349 				break;
1350 			default:
1351 				upslogx(LOG_ERR, "Unknown type for %s", var);
1352 				break;
1353 			}
1354 			return 1;
1355 		}
1356 		sp++;
1357 	}
1358 	return 0;
1359 }
1360 
1361 
batt_charge_pct(void)1362 static float batt_charge_pct(void)
1363 {
1364 	float chg=-1;
1365 
1366 	/* This is only a rough estimate of charge status while charging.
1367 	 * When on battery something like Peukert's equation should be used */
1368 	if (batt_current >= 0) {
1369 		float maxcurr = 10;	/* Assume 10A max as default */
1370 		float avgcurr = 0;
1371 		int i;
1372 
1373 		batthist[lastbatthist] = batt_current;
1374 		lastbatthist = (lastbatthist+1) % NUM_BATTHIST;
1375 		if (numbatthist < NUM_BATTHIST) {
1376 			numbatthist++;
1377 		}
1378 		for(i=0; i<numbatthist; i++) {
1379 			avgcurr += batthist[i];
1380 		}
1381 		avgcurr /= numbatthist;
1382 
1383 		if (batt_cap_nom > 0) {
1384 			/* Estimate max charge current based on battery size */
1385 			maxcurr = batt_cap_nom * 0.3;
1386 		}
1387 		chg = maxcurr - avgcurr;
1388 		chg *= (100/maxcurr);
1389 	}
1390 	/* Old method, assumes battery high/low-voltages provided by UPS are
1391 	 * applicable to battery charge, but they usually aren't */
1392 	else if (batt_volt_low > 0 && batt_volt_high > 0 && batt_volt > 0) {
1393 		if (batt_volt > batt_volt_high) {
1394 			chg=100;
1395 		}
1396 		else if (batt_volt < batt_volt_low) {
1397 			chg=0;
1398 		}
1399 		else {
1400 			chg = (batt_volt - batt_volt_low) /
1401 				(batt_volt_high - batt_volt_low);
1402 			chg*=100;
1403 		}
1404 	}
1405 	else {
1406 		return -1;
1407 	}
1408 
1409 	if (chg < 0) {
1410 		chg = 0;
1411 	}
1412 	else if (chg > 100) {
1413 		chg = 100;
1414 	}
1415 
1416 	return chg;
1417 }
1418 
1419 /*
1420 vim:noet:ts=8:sw=8:cindent
1421 */
1422