1 /*
2  * apctest.c
3  *
4  * A cable tester program for apcctrl.
5  *
6  * Hacked from apcctrl.c by Kern Sibbald, Sept 2000
7  */
8 
9 /*
10  * Copyright (C) 2000-2004 Kern Sibbald
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of version 2 of the GNU General
14  * Public License as published by the Free Software Foundation.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public
22  * License along with this program; if not, write to the Free
23  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24  * MA 02110-1335, USA.
25  */
26 #include "apc.h"
27 #include <termios.h>
28 #include <math.h>
29 
30 
31 
32 UPSINFO *core_ups;
33 UPSINFO *ups;
34 
35 int le_bit = TIOCM_LE;
36 int st_bit = TIOCM_ST;
37 int sr_bit = TIOCM_SR;
38 int dtr_bit = TIOCM_DTR;
39 int rts_bit = TIOCM_RTS;
40 int cts_bit = TIOCM_CTS;
41 int cd_bit = TIOCM_CD;
42 int rng_bit = TIOCM_RNG;
43 int dsr_bit = TIOCM_DSR;
44 
45 struct termios oldtio;
46 struct termios newtio;
47 
48 /* Forward referenced functions */
49 static void do_dumb_testing(void);
50 static void test1(void);
51 static void test2(void);
52 static void test3(void);
53 static void test4(void);
54 static void test5(void);
55 static void test6(void);
56 static void guess(void);
57 
58 static void do_brazil_testing(void);
59 
60 static void do_smart_testing(void);
61 
62 #ifdef HAVE_APCSMART_DRIVER
63 #include "drivers/apcsmart/apcsmart.h"
64 static void smart_test1(void);
65 static void smart_calibration(void);
66 static void monitor_calibration_progress(int monitor);
67 static void terminate_calibration(int ask);
68 static void program_smart_eeprom(void);
69 static void print_eeprom_values(UPSINFO *ups);
70 static void smart_ttymode(void);
71 static void parse_eeprom_cmds(char *eprom, char locale);
72 static void print_valid_eeprom_values(UPSINFO *ups);
73 #endif
74 
75 static void do_usb_testing(void);
76 
77 #ifdef HAVE_USB_DRIVER
78 
79 /* USB driver functions */
80 #include "drivers/usb/usb.h"
81 
82 /* Our own test functions */
83 static void usb_kill_power_test(void);
84 static void usb_get_self_test_result(void);
85 static void usb_run_self_test(void);
86 static int usb_get_battery_date(void);
87 static void usb_set_battery_date(void);
88 static void usb_get_manf_date(void);
89 static void usb_set_alarm(void);
90 static void usb_set_sens(void);
91 static void usb_set_xferv(int lowhigh);
92 static void usb_calibration();
93 static void usb_test_alarm(void);
94 static int usb_get_self_test_interval(void);
95 static void usb_set_self_test_interval(void);
96 #endif
97 
98 static void do_modbus_testing(void);
99 
100 #ifdef HAVE_MODBUS_DRIVER
101 
102 /* MODBUS driver functions */
103 #include "drivers/modbus/modbus.h"
104 
105 /* MODBUS register mapping */
106 #include "drivers/modbus/mapping.h"
107 using namespace APCModbusMapping;
108 
109 /* Our own test functions */
110 static void modbus_kill_power_test(void);
111 static void modbus_get_self_test_result(void);
112 static void modbus_run_self_test(void);
113 static uint64_t modbus_get_battery_date(void);
114 static void modbus_set_battery_date(void);
115 static void modbus_get_manf_date(void);
116 //static void modbus_set_alarm(void);
117 //static void modbus_set_sens(void);
118 //static void modbus_set_xferv(int lowhigh);
119 static void modbus_calibration();
120 static void modbus_test_alarm(void);
121 //static int modbus_get_self_test_interval(void);
122 //static void modbus_set_self_test_interval(void);
123 #endif
124 
125 static void strip_trailing_junk(char *cmd);
126 static char *get_cmd(const char *prompt);
127 static int write_file(char *buf);
128 
129 
130 /* Static variables */
131 static int normal, no_cable, no_power, low_batt;
132 static int test1_done = 0;
133 static int test2_done = 0;
134 static int test3_done = 0;
135 static int test4_done = 0;
136 static int test5_done = 0;
137 
138 #define smart_poll(a, ups) \
139 		((ApcSmartUpsDriver*)((ups)->driver))->smart_poll(a)
140 
141 #define getline(a,b,ups) \
142 		((ApcSmartUpsDriver*)((ups)->driver))->getline(a,b)
143 
144 #define writechar(a,ups) \
145 		((ApcSmartUpsDriver*)((ups)->driver))->writechar(a)
146 
147 #define apcsmart_ups_program_eeprom(ups,ci,cmd) \
148 		(ups)->driver->program_eeprom(ci, cmd)
149 
150 #define usb_read_int_from_ups(ups, ci, result) \
151 		((UsbUpsDriver*)((ups)->driver))->read_int_from_ups(ci, result)
152 
153 #define usb_write_int_to_ups(ups, ci, val, text) \
154 		((UsbUpsDriver*)((ups)->driver))->write_int_to_ups(ci, val, text)
155 
156 #define modbus_read_int_from_ups(ups, ci, result) \
157 		((ModbusUpsDriver*)((ups)->driver))->read_int_from_ups(ci, result)
158 
159 #define modbus_write_int_to_ups(ups, ci, val) \
160 		((ModbusUpsDriver*)((ups)->driver))->write_int_to_ups(ci, val)
161 
162 #define modbus_write_string_to_ups(ups, ci, val) \
163 		((ModbusUpsDriver*)((ups)->driver))->write_string_to_ups(ci, val)
164 
165 /* Print a message, and also write it to an output file */
pmsg(const char * fmt,...)166 static void pmsg(const char *fmt, ...)
167 {
168 	char buf[3000];
169 	va_list arg_ptr;
170 
171 	va_start(arg_ptr, fmt);
172 	avsnprintf(buf, sizeof(buf), (char *)fmt, arg_ptr);
173 	va_end(arg_ptr);
174 	printf("%s", buf);
175 	fflush(stdout);
176 	write_file(buf);
177 }
178 
179 /* Write output into "log" file */
write_file(char * buf)180 static int write_file(char *buf)
181 {
182 	static int out_fd = -1;
183 
184 	if (out_fd == -1) {
185 		out_fd = open("apctest.output", O_WRONLY | O_CREAT | O_APPEND, 0644);
186 		if (out_fd < 0) {
187 			printf("Could not create apctest.output: %s\n", strerror(errno));
188 			return -1;
189 		}
190 	}
191 	return write(out_fd, buf, strlen(buf));
192 }
193 
194 /* Print out current time */
ptime(void)195 static void ptime(void)
196 {
197 	char dt[MAXSTRING];
198 	time_t now = time(NULL);
199 
200 	strftime(dt, MAXSTRING, "%Y-%m-%d %T ", localtime(&now));
201 	pmsg(dt);
202 }
203 
204 
205 /*
206  * The terminate function; trapping signals allows apctest
207  * to exit and cleanly close logfiles, and reset the tty back
208  * to its original settings. You may want to add this
209  * to all configurations.  Of course, the file descriptors
210  * must be global for this to work, I also set them to
211  * NULL initially to allow terminate to determine whether
212  * it should close them.
213  */
apctest_terminate(int sig)214 void apctest_terminate(int sig)
215 {
216 	if (sig != 0) {
217 		ptime();
218 		pmsg("apctest exiting, signal %u\n", sig);
219 	}
220 
221 	clear_files();
222 
223 	device_close(ups);
224 
225 	delete_lockfile(ups);
226 
227 	clean_threads();
228 
229 	closelog();
230 	destroy_ups(ups);
231 	_exit(0);
232 }
233 
apctest_error_cleanup(UPSINFO * ups)234 void apctest_error_cleanup(UPSINFO *ups)
235 {
236 	device_close(ups);
237 	delete_lockfile(ups);
238 	clean_threads();
239 	pmsg("apctest error termination completed\n");
240 	closelog();
241 	destroy_ups(ups);
242 	exit(1);
243 }
244 
245 /*
246  * Subroutine error_out prints FATAL ERROR with file,
247  * line number, and the error message then cleans up
248  * and exits. It is normally called from the Error_abort
249  * define, which inserts the file and line number.
250  */
apctest_error_out(const char * file,int line,const char * fmt,va_list arg_ptr)251 void apctest_error_out(const char *file, int line, const char *fmt, va_list arg_ptr)
252 {
253 	char buf[256];
254 	int i;
255 
256 	asnprintf(buf, sizeof(buf),
257 			"apctest FATAL ERROR in %s at line %d\n", file, line);
258 	i = strlen(buf);
259 
260 	avsnprintf((char *)&buf[i], sizeof(buf) - i, (char *)fmt, arg_ptr);
261 
262 	pmsg(buf);
263 
264 	apctest_error_cleanup(core_ups);     /* finish the work */
265 }
266 
267 
268 /* Main program */
269 
270 /* This application must be linked as console app. */
271 
main(int argc,char * argv[])272 int main(int argc, char *argv[])
273 {
274 	/* Set specific error_* handlers. */
275 	error_out = apctest_error_out;
276 
277 	/*
278 	 * Default config file. If we set a config file in startup switches, it
279 	 * will be re-filled by parse_options()
280 	 */
281 	cfgfile = APCCONF;
282 
283 	ups = new_ups();                /* get new ups */
284 	if (!ups)
285 		Error_abort("%s: init_ipc failed.\n", argv[0]);
286 
287 	init_ups_struct(ups);
288 	core_ups = ups;                 /* this is our core ups structure */
289 
290 	/* parse_options is self messaging on errors, so we need only to exit() */
291 	if (parse_options(argc, argv))
292 		exit(1);
293 
294 #ifdef HAVE_MINGW
295 	system("chcp 65001");
296 #endif
297 
298 	pmsg("\n\n");
299 	ptime();
300 	pmsg("apctest " APCCTRL_RELEASE " (" ADATE ") " APCCTRL_HOST "\n");
301 
302 	pmsg("Checking configuration ...\n");
303 	check_for_config(ups, cfgfile);
304 
305 	attach_driver(ups);
306 	if (ups->driver == NULL)
307 		Error_abort("apctest cannot continue without a valid driver.\n");
308 
309 	//   pmsg("Attached to driver: %s\n", ups->driver->driver_name);
310 
311 	ups->start_time = time(NULL);
312 
313 	/* Print configuration */
314 	pmsg("sharenet.type = %s\n", ups->sharenet.long_name);
315 	pmsg("cable.type = %s\n", ups->cable.long_name);
316 	pmsg("mode.type = %s\n", ups->mode.long_name);
317 
318 	if (create_lockfile(ups) == LCKERROR) {
319 		Error_abort("Unable to create UPS lock file.\n"
320 				"  If apcctrl or apctest is already running,\n"
321 				"  please stop it and run this program again.\n");
322 	}
323 
324 	pmsg("Setting up the port ...\n");
325 	if (!setup_device(ups))
326 	{
327 		Error_abort("Unable to open UPS device.\n"
328 				"  If apcctrl or apctest is already running,\n"
329 				"  please stop it and run this program again.\n");
330 	}
331 
332 	if (hibernate_ups) {
333 		pmsg("apctest: bad option, I cannot do a killpower\n");
334 		apctest_terminate(0);
335 	}
336 
337 	init_signals(apctest_terminate);
338 
339 	pmsg("Doing prep_device() ...\n");
340 	prep_device(ups);
341 
342 	/*
343 	 * This isn't a documented option but can be used
344 	 * for testing dumb mode on a SmartUPS if you have
345 	 * the proper cable.
346 	 */
347 	if (dumb_mode_test) {
348 #ifdef HAVE_APCSMART_DRIVER
349 		char ans[20];
350 
351 		write(ups->fd, "R", 1);   /* enter dumb mode */
352 		*ans = 0;
353 		getline(ans, sizeof(ans), ups);
354 		pmsg("Going dumb: %s\n", ans);
355 #else
356 		pmsg("apcsmart not compiled: dumb mode test unavailable\n");
357 #endif
358 	}
359 
360 	switch (ups->mode.type)
361 	{
362 	case MODBUS_UPS:
363 		pmsg("\nYou are using a MODBUS cable type, so I'm entering MODBUS test mode\n");
364 		do_modbus_testing();
365 		break;
366 	case USB_UPS:
367 		pmsg("\nYou are using a USB cable type, so I'm entering USB test mode\n");
368 		do_usb_testing();
369 		break;
370 	case APCSMART_UPS:
371 		pmsg("\nYou are using a SMART cable type, so I'm entering SMART test mode\n");
372 		do_smart_testing();
373 		break;
374 	case DUMB_UPS:
375 		pmsg("\nYou are using a DUMB cable type, so I'm entering DUMB test mode\n");
376 		do_dumb_testing();
377 		break;
378 	case BRAZIL_UPS:
379 		pmsg("\nYou are using a APC Brazil type, so I'm entering BRAZIL test mode\n");
380 		do_brazil_testing();
381 		break;
382 
383 	default:
384 		pmsg("Testing not yet implemented for this UPSTYPE.\n");
385 	}
386 	apctest_terminate(0);
387 	return -1;                      /* to keep compiler happy */
388 }
389 
print_bits(int bits)390 static void print_bits(int bits)
391 {
392 	char buf[200];
393 
394 	asnprintf(buf, sizeof(buf), "IOCTL GET: %x ", bits);
395 
396 	if (bits & le_bit)
397 		strlcat(buf, "LE ", sizeof(buf));
398 	if (bits & st_bit)
399 		strlcat(buf, "ST ", sizeof(buf));
400 	if (bits & sr_bit)
401 		strlcat(buf, "SR ", sizeof(buf));
402 	if (bits & dtr_bit)
403 		strlcat(buf, "DTR ", sizeof(buf));
404 	if (bits & rts_bit)
405 		strlcat(buf, "RTS ", sizeof(buf));
406 	if (bits & cts_bit)
407 		strlcat(buf, "CTS ", sizeof(buf));
408 	if (bits & cd_bit)
409 		strlcat(buf, "CD ", sizeof(buf));
410 	if (bits & rng_bit)
411 		strlcat(buf, "RNG ", sizeof(buf));
412 	if (bits & dsr_bit)
413 		strlcat(buf, "DSR ", sizeof(buf));
414 
415 	strlcat(buf, "\n", sizeof(buf));
416 
417 	pmsg(buf);
418 }
419 
do_dumb_testing(void)420 static void do_dumb_testing(void)
421 {
422 	int quit = FALSE;
423 	char *cmd;
424 
425 	pmsg("Hello, this is the apcctrl Cable Test program.\n\n"
426 			"We are beginning testing for dumb UPSes, which\n"
427 			"use signaling rather than commands.\n"
428 			"Most tests enter a loop polling every second for 10 seconds.\n");
429 
430 	while (!quit) {
431 		pmsg("\n"
432 				"1) Test 1 - normal mode\n"
433 				"2) Test 2 - no cable\n"
434 				"3) Test 3 - no power\n"
435 				"4) Test 4 - low battery (requires test 3 first)\n"
436 				"5) Test 5 - battery exhausted\n"
437 				"6) Test 6 - kill UPS power\n"
438 				"7) Test 7 - run tests 1 through 5\n"
439 				"8) Guess which is the appropriate cable\n"
440 				"Q) Quit\n\n");
441 
442 		cmd = get_cmd("Select test number: ");
443 
444 		if (cmd) {
445 			int item = atoi(cmd);
446 
447 			switch (item) {
448 			case 1:
449 				test1();
450 				break;
451 			case 2:
452 				test2();
453 				break;
454 			case 3:
455 				test3();
456 				break;
457 			case 4:
458 				test4();
459 				break;
460 			case 5:
461 				test5();
462 				break;
463 			case 6:
464 				test6();
465 				break;
466 			case 7:
467 				test1();
468 				test2();
469 				test3();
470 				test4();
471 				test5();
472 				break;
473 			case 8:
474 				guess();
475 				break;
476 			default:
477 				if (tolower(*cmd) == 'q')
478 					quit = TRUE;
479 				else
480 					pmsg("Illegal response. Please enter 1-8,Q\n");
481 				break;
482 			}
483 		} else {
484 			pmsg("Illegal response. Please enter 1-8,Q\n");
485 		}
486 	}
487 
488 	ptime();
489 	pmsg("End apctest.\n");
490 }
491 
492 /*
493  * Poll 10 times once per second and report any
494  * change in serial port bits.
495  */
test_bits(int inbits)496 static int test_bits(int inbits)
497 {
498 	int i, nbits;
499 	int bits = inbits;
500 
501 	for (i = 0; i < 10; i++) {
502 		if (ioctl(ups->fd, TIOCMGET, &nbits) < 0) {
503 			pmsg("ioctl error, big problem: %s\n", strerror(errno));
504 			return 0;
505 		}
506 
507 		if (i == 0 || nbits != bits) {
508 			ptime();
509 			print_bits(nbits);
510 			bits = nbits;
511 		}
512 
513 		sleep(1);
514 	}
515 
516 	return bits;
517 }
518 
test1(void)519 static void test1(void)
520 {
521 	pmsg("\nFor the first test, everything should be normal.\n"
522 			"The UPS should be plugged in to the power, and the serial cable\n"
523 			"should be connected to the computer.\n\n"
524 			"Please enter any character when ready to continue: ");
525 	fgetc(stdin);
526 	pmsg("\n");
527 
528 	normal = test_bits(0);
529 
530 	ptime();
531 	pmsg("Test 1: normal condition, completed.\n");
532 	test1_done = TRUE;
533 }
534 
test2(void)535 static void test2(void)
536 {
537 	pmsg("\nFor the second test, the UPS should be plugged in to the power, \n"
538 			"but the serial port should be DISCONNECTED from the computer.\n\n"
539 			"Please enter any character when ready to continue: ");
540 	fgetc(stdin);
541 	pmsg("\n");
542 
543 	no_cable = test_bits(0);
544 
545 	ptime();
546 	pmsg("Test 2: no cable, completed. \n");
547 	test2_done = TRUE;
548 }
549 
test3(void)550 static void test3(void)
551 {
552 	pmsg("\nFor the third test, the serial cable should be plugged\n"
553 			"back into the UPS, but the AC power plug to the UPS should be DISCONNECTED.\n\n"
554 			"Please enter any character when ready to continue: ");
555 	fgetc(stdin);
556 	pmsg("\n");
557 
558 	no_power = test_bits(0);
559 
560 	ptime();
561 	pmsg("Test 3: no power, completed.\n");
562 	test3_done = TRUE;
563 }
564 
test4(void)565 static void test4(void)
566 {
567 	int i, bits;
568 
569 	if (!test3_done) {
570 		pmsg("We need the output of test 3 to run this test.\n"
571 				"Please run test 3 first then this test.\n");
572 		return;
573 	}
574 
575 	pmsg("\nThe fourth test is the same as the third test:\n"
576 			"the serial cable should be plugged in to the UPS, but the AC power\n"
577 			"plug to the UPS should be DISCONNECTED. In addition, you should\n"
578 			"continue this test until the batteries are exhausted.\n"
579 			"If apctest detects a state change, it will stop\n"
580 			"the test. If not, hit control-C to stop the program\n\n"
581 			"PLEASE DO NOT RUN THIS TEST WITH A COMPUTER CONNECTED TO YOUR UPS!!!\n\n"
582 			"Please enter any character when ready to continue: ");
583 	fgetc(stdin);
584 	pmsg("\n");
585 
586 	low_batt = no_power;
587 
588 	ptime();
589 	pmsg("Start test 4: ");
590 	pmsg("\n");
591 
592 	/* Spin until we get a state change */
593 	for (i = 0;; i++) {
594 		if (ioctl(ups->fd, TIOCMGET, &bits) < 0) {
595 			pmsg("ioctl error, big problem: %s\n", strerror(errno));
596 			return;
597 		}
598 
599 		if (bits != low_batt) {
600 			ptime();
601 			print_bits(bits);
602 			low_batt = bits;
603 			break;
604 		} else if (i == 0) {
605 			ptime();
606 			print_bits(bits);
607 		}
608 
609 		sleep(1);
610 	}
611 
612 	ptime();
613 	pmsg("Test 4: low battery, completed.\n");
614 	test4_done = TRUE;
615 }
616 
test5(void)617 static void test5(void)
618 {
619 	int i, bits, last_bits = 0;
620 
621 	pmsg("\nThe fifth test is the same as the third test:\n"
622 			"the serial cable should be plugged in to the UPS, but the AC power\n"
623 			"plug to the UPS should be DISCONNECTED. In addition, you should\n"
624 			"continue this test until the batteries are exhausted.\n"
625 			"If apctest detects a state change, contrary to test 4, it\n"
626 			"will not stop. The idea is to see ANY changed bits just up to\n"
627 			"the very moment that the UPS powers down.\n\n"
628 			"PLEASE DO NOT RUN THIS TEST WITH A COMPUTER CONNECTED TO YOUR UPS!!!\n\n"
629 			"Please enter any character when ready to continue: ");
630 	fgetc(stdin);
631 	pmsg("\n");
632 
633 	pmsg("Start test 5:\n");
634 
635 	/* Spin until we get a state change */
636 	for (i = 0;; i++) {
637 		if (ioctl(ups->fd, TIOCMGET, &bits) < 0) {
638 			pmsg("ioctl error, big problem: %s\n", strerror(errno));
639 			return;
640 		}
641 
642 		if (i == 60)
643 			i = 0;                    /* force print once a minute */
644 
645 		if (i == 0 || bits != last_bits) {
646 			ptime();
647 			print_bits(bits);
648 			last_bits = bits;
649 		}
650 
651 		sleep(1);
652 	}
653 
654 	/* Should never get here */
655 	/* NOTREACHED */
656 	ptime();
657 	pmsg("Test 5: battery exhausted, completed.\n");
658 	test5_done = TRUE;
659 }
660 
test6(void)661 static void test6(void)
662 {
663 	int bits;
664 
665 	pmsg("\nThis test will attempt to power down the UPS.\n"
666 			"The serial cable should be plugged in to the UPS, but the\n"
667 			"AC power plug to the UPS should be DISCONNECTED.\n\n"
668 			"PLEASE DO NOT RUN THIS TEST WITH A COMPUTER CONNECTED TO YOUR UPS!!!\n\n"
669 			"Please enter any character when ready to continue: ");
670 	fgetc(stdin);
671 	pmsg("\n");
672 
673 	if (ioctl(ups->fd, TIOCMGET, &bits) < 0) {
674 		pmsg("ioctl error, big problem: %s\n", strerror(errno));
675 		return;
676 	}
677 
678 	ptime();
679 	print_bits(bits);
680 	make_file(ups, ups->pwrfailpath);
681 	initiate_hibernate(ups);
682 	unlink(ups->pwrfailpath);
683 	ptime();
684 
685 	pmsg("returned from kill_power function.\n");
686 
687 	if (ioctl(ups->fd, TIOCMGET, &bits) < 0) {
688 		pmsg("ioctl error, big problem: %s\n", strerror(errno));
689 		return;
690 	}
691 
692 	ptime();
693 	print_bits(bits);
694 }
695 
696 /*
697  * Make a wild guess at the cable type
698  *
699  * If I had more data on each of the cable types, this could
700  * be much improved.
701  */
guess(void)702 static void guess(void)
703 {
704 	int found = 0;
705 
706 	if (!(test1_done && test3_done)) {
707 		pmsg("Test 1 and test 3 must be performed before I can make a guess.\n");
708 		return;
709 	}
710 	if (!(normal & (cd_bit | cts_bit)) && (no_power & cd_bit) && (low_batt & cts_bit)) {
711 		pmsg("This looks like a CUSTOM_SIMPLE cable\n");
712 		found = 1;
713 	}
714 
715 	if (!(normal & (cd_bit | cts_bit)) && (no_power & cts_bit) && (low_batt & cd_bit)) {
716 		pmsg("This looks like a 940-0020A\n");
717 		found = 1;
718 	}
719 
720 	if (!(normal & (cd_bit | sr_bit)) && (no_power & cd_bit) && (low_batt & sr_bit)) {
721 		pmsg("This looks like a 940-0023A cable\n");
722 		found = 1;
723 	}
724 
725 	if (!(normal & (rng_bit | cd_bit)) && (no_power & rng_bit) && (low_batt & cd_bit)) {
726 		pmsg("This looks like a 940-0095A cable\n");
727 		found = 1;
728 	}
729 
730 	if (!found) {
731 		pmsg("Hmmm. I don't quite know what you have. Sorry.\n");
732 	}
733 }
734 
do_smart_testing(void)735 static void do_smart_testing(void)
736 {
737 #ifdef HAVE_APCSMART_DRIVER
738 	char *cmd;
739 	int quit = FALSE;
740 
741 	pmsg("Hello, this is the apcctrl Cable Test program.\n"
742 			"This part of apctest is for testing Smart UPSes.\n"
743 			"Please select the function you want to perform.\n");
744 
745 	while (!quit) {
746 		pmsg("\n"
747 				"1) Query the UPS for all known values\n"
748 				"2) Perform a Battery Runtime Calibration\n"
749 				"3) Abort Battery Calibration\n"
750 				"4) Monitor Battery Calibration progress\n"
751 				"5) Program EEPROM\n"
752 				"6) Enter TTY mode communicating with UPS\n"
753 				"Q) Quit\n\n");
754 
755 		cmd = get_cmd("Select function number: ");
756 		if (cmd) {
757 			int item = atoi(cmd);
758 
759 			switch (item) {
760 			case 1:
761 				smart_test1();
762 				break;
763 			case 2:
764 				smart_calibration();
765 				break;
766 			case 3:
767 				terminate_calibration(1);
768 				break;
769 			case 4:
770 				monitor_calibration_progress(0);
771 				break;
772 			case 5:
773 				program_smart_eeprom();
774 				break;
775 			case 6:
776 				smart_ttymode();
777 				break;
778 			default:
779 				if (tolower(*cmd) == 'q')
780 					quit = TRUE;
781 				else
782 					pmsg("Illegal response. Please enter 1-6,Q\n");
783 				break;
784 				break;
785 			}
786 		} else {
787 			pmsg("Illegal response. Please enter 1-6,Q\n");
788 		}
789 	}
790 	ptime();
791 	pmsg("End apctest.\n");
792 #else
793 	pmsg("APC Smart Driver not configured.\n");
794 #endif
795 }
796 
797 
798 
799 #ifdef HAVE_APCSMART_DRIVER
smart_ttymode(void)800 static void smart_ttymode(void)
801 {
802 #ifdef HAVE_MINGW
803 	// This is crap. Windows has no sane way (that I can find) to watch two
804 	// fds for activity from a single thread without involving the overly
805 	// complex "overlapped" io junk. So we will resort to polling.
806 
807 	// Save any existing timeouts on the UPS fd
808 	HANDLE hnd = (HANDLE)_get_osfhandle(ups->fd);
809 	COMMTIMEOUTS orig_ups_ct;
810 	GetCommTimeouts(hnd, &orig_ups_ct);
811 
812 	// Reset UPS fd timeout to 50 msec
813 	COMMTIMEOUTS ct;
814 	ct.ReadIntervalTimeout = MAXDWORD;
815 	ct.ReadTotalTimeoutMultiplier = 0;
816 	ct.ReadTotalTimeoutConstant = 50;
817 	ct.WriteTotalTimeoutMultiplier = 0;
818 	ct.WriteTotalTimeoutConstant = 0;
819 	SetCommTimeouts(hnd, &ct);
820 
821 	pmsg("Enter an ESC character (or ctl-[) to exit.\n\n");
822 
823 	char ch;
824 	while (1)
825 	{
826 		// Waits up to 50 msec for a char from the UPS
827 		if (read(ups->fd, &ch, 1) == 1)
828 			putch(ch);
829 
830 		// Check if keyboard key was hit and read it (only Windows would
831 		// have a function dedicated to checking if a key has been pressed!)
832 		if (kbhit())
833 		{
834 			ch = getch();
835 			if (ch == 0x1b)
836 				break;
837 			else
838 				write(ups->fd, &ch, 1);
839 		}
840 	}
841 
842 	// Restore original timeouts on UPS fd
843 	SetCommTimeouts(hnd, &orig_ups_ct);
844 #else
845 	char ch;
846 	struct termios t, old_term_params;
847 	fd_set rfds;
848 	int stat;
849 
850 	if (tcgetattr(0, &old_term_params) != 0) {
851 		pmsg("Cannot tcgetattr()\n");
852 		return;
853 	}
854 
855 	t = old_term_params;
856 	t.c_cc[VMIN] = 1;               /* satisfy read after 1 char */
857 	t.c_cc[VTIME] = 0;
858 	t.c_iflag &= ~(BRKINT | IGNPAR | PARMRK | INPCK |
859 			ISTRIP | ICRNL | IXON | IXOFF | INLCR | IGNCR);
860 	t.c_iflag |= IGNBRK;
861 	t.c_lflag &= ~(ICANON | ISIG | NOFLSH | TOSTOP);
862 	tcflush(0, TCIFLUSH);
863 
864 	if (tcsetattr(0, TCSANOW, &t) == -1) {
865 		pmsg("Cannot tcsetattr()\n");
866 		return;
867 	}
868 
869 	pmsg("Enter an ESC character (or ctl-[) to exit.\n\n");
870 
871 	tcflush(ups->fd, TCIOFLUSH);
872 	for (;;) {
873 		FD_ZERO(&rfds);
874 		FD_SET(0, &rfds);
875 		FD_SET(ups->fd, &rfds);
876 		stat = select((ups->fd) + 1, &rfds, NULL, NULL, NULL);
877 		if (stat == -1) {
878 			pmsg("select() failed.\n");
879 			break;
880 		}
881 		if (FD_ISSET(0, &rfds)) {
882 			if (read(0, &ch, 1) != 1)
883 				break;
884 
885 			if (ch == 0x1B)
886 				break;
887 
888 			write(ups->fd, &ch, 1);
889 		}
890 		if (FD_ISSET(ups->fd, &rfds)) {
891 			if (read(ups->fd, &ch, 1) != 1)
892 				break;
893 
894 			write(1, &ch, 1);
895 		}
896 	}
897 
898 	tcsetattr(0, TCSANOW, &old_term_params);
899 #endif
900 }
901 
902 
903 /* Do runtime battery calibration */
smart_calibration(void)904 static void smart_calibration(void)
905 {
906 	char *ans, cmd;
907 	char answer[2000];
908 	int stat, monitor, elapse;
909 	time_t start_time;
910 
911 	pmsg("First ensure that we have a good link and \n"
912 			"that the UPS is functionning normally.\n");
913 	pmsg("Simulating UPSlinkCheck ...\n");
914 
915 	tcflush(ups->fd, TCIOFLUSH);
916 
917 	/* Start really simply */
918 	cmd = 'Y';
919 	stat = write(ups->fd, &cmd, 1);
920 	if (stat < 0)
921 		pmsg("Bad status from write: %s\n", strerror(errno));
922 
923 	stat = getline(answer, sizeof(answer), ups);
924 	pmsg("Wrote: Y Got: %s\n", answer);
925 	if (stat == FAILURE) {
926 		pmsg("getline failed. Apparently the link is not up.\n"
927 				"Giving up.\n");
928 		return;
929 	}
930 
931 	pmsg("Attempting to use smart_poll() ...\n");
932 	ans = smart_poll('Y', ups);
933 	pmsg("Sent: Y Got: %s  ", ans);
934 	if (strcmp(ans, "SM") == 0) {
935 		pmsg("Good -- smart_poll() works!.\n\n");
936 	} else {
937 		pmsg("Not good.\nGiving up.\n");
938 		return;
939 	}
940 
941 	pmsg("Checking estimated runtime ...\n");
942 	ans = smart_poll('j', ups);
943 	if (*ans >= '0' && *ans <= '9') {
944 		int rt = atoi(ans);
945 
946 		pmsg("Current runtime is %d minutes\n", rt);
947 	} else {
948 		pmsg("Unexpected response from UPS: %s\n", ans);
949 		return;
950 	}
951 
952 	pmsg("Checking for battery level ...\n");
953 	ans = smart_poll('f', ups);
954 	if (strncmp(ans, "100.0", 5) == 0) {
955 		pmsg("Battery level is 100.0 -- OK\n");
956 	} else {
957 		pmsg("Battery level %s insufficient to run test.\n", ans);
958 		return;
959 	}
960 
961 	pmsg("\nThe battery calibration should automatically end\n"
962 			"when the battery level drops below about 25, depending\n"
963 			"on your UPS.\n\n"
964 			"I can also optionally monitor the progress\n"
965 			"and stop the calibration if it goes below 10. However,\n"
966 			"in the case of a new battery this may prematurely end the\n"
967 			"calibration loosing the effect.\n\n");
968 
969 	ans = get_cmd("Do you want me to stop the calibration\n"
970 			"if the battery level goes too low? (y/n): ");
971 
972 	if (*ans == 'Y' || *ans == 'y')
973 		monitor = 1;
974 	else
975 		monitor = 0;
976 
977 	pmsg("\nSending Battery Calibration command. ...\n");
978 
979 	ans = smart_poll('D', ups);
980 	if (*ans == '!' || strcmp(ans, "OK") == 0) {
981 		pmsg("UPS has initiated battery calibration.\n");
982 	} else {
983 		pmsg("Unexpected response from UPS: %s\n", ans);
984 		return;
985 	}
986 
987 	start_time = time(NULL);
988 	monitor_calibration_progress(monitor);
989 	elapse = time(NULL) - start_time;
990 	pmsg("On battery %d sec or %dm%ds.\n", elapse, elapse / 60, elapse % 60);
991 }
992 
terminate_calibration(int ask)993 static void terminate_calibration(int ask)
994 {
995 	char *ans, *cmd;
996 
997 	if (ask) {
998 		pmsg("\nCAUTION! Don't use this command unless the UPS\n"
999 				"is already doing a calibration.\n");
1000 		cmd = get_cmd("Are you sure? (yes/no): ");
1001 		if (strcmp(cmd, "yes") != 0)
1002 			return;
1003 	}
1004 
1005 	pmsg("\nAttempting to abort calibration ...\n");
1006 	ans = smart_poll('D', ups);     /* abort calibration */
1007 	if (*ans == '$' || strcmp(ans, "NO") == 0) {
1008 		pmsg("Battery Runtime Calibration terminated.\n");
1009 		pmsg("Checking estimated runtime ...\n");
1010 		ans = smart_poll('j', ups);
1011 		if (*ans >= '0' && *ans <= '9') {
1012 			int rt = atoi(ans);
1013 
1014 			pmsg("Updated runtime is %d\n", rt);
1015 		} else {
1016 			pmsg("Unexpected response from UPS: %s\n", ans);
1017 		}
1018 	} else {
1019 		pmsg("Response to abort request: %s\n", ans);
1020 	}
1021 }
1022 
1023 
monitor_calibration_progress(int monitor)1024 static void monitor_calibration_progress(int monitor)
1025 {
1026 	char *ans;
1027 	int count = 6;
1028 	int max_count = 6;
1029 
1030 	pmsg("Monitoring the calibration progress ...\n"
1031 			"To stop the calibration, enter a return.\n");
1032 
1033 	for (;;) {
1034 		int percent;
1035 		char cmd;
1036 		bool aborted = false;
1037 		int retval = 0;
1038 
1039 #ifdef HAVE_MINGW
1040 		// select only works on sockets on win32, so we must poll
1041 		for (unsigned int i = 0; !aborted && retval == 0 && i < 10; ++i)
1042 		{
1043 			while (kbhit())
1044 			{
1045 				aborted = true;
1046 				(void)getch();
1047 			}
1048 			if (!aborted)
1049 			{
1050 				COMMTIMEOUTS ct;
1051 				HANDLE h = (HANDLE)_get_osfhandle(ups->fd);
1052 				ct.ReadIntervalTimeout = MAXDWORD;
1053 				ct.ReadTotalTimeoutMultiplier = MAXDWORD;
1054 				ct.ReadTotalTimeoutConstant = 1000;
1055 				ct.WriteTotalTimeoutMultiplier = 0;
1056 				ct.WriteTotalTimeoutConstant = 0;
1057 				SetCommTimeouts(h, &ct);
1058 				retval = read(ups->fd, &cmd, 1);
1059 			}
1060 		}
1061 #else
1062 		fd_set rfds;
1063 		struct timeval tv;
1064 
1065 		FD_ZERO(&rfds);
1066 		FD_SET(ups->fd, &rfds);
1067 		FD_SET(STDIN_FILENO, &rfds);
1068 		tv.tv_sec = 10;
1069 		tv.tv_usec = 0;
1070 		errno = 0;
1071 
1072 		retval = select((ups->fd) + 1, &rfds, NULL, NULL, &tv);
1073 		if (retval == -1 && (errno == EINTR || errno == EAGAIN))
1074 		{
1075 			continue;
1076 		}
1077 		else if (retval != 0)
1078 		{
1079 			if (FD_ISSET(STDIN_FILENO, &rfds))
1080 			{
1081 				/* *ANY* user input aborts the calibration */
1082 				read(STDIN_FILENO, &cmd, 1);
1083 				aborted = true;
1084 			}
1085 			else if(FD_ISSET(ups->fd, &rfds))
1086 			{
1087 				// UPS char
1088 				retval = read(ups->fd, &cmd, 1);
1089 			}
1090 		}
1091 #endif
1092 
1093 		if (retval == -1)
1094 		{
1095 			pmsg("\nselect/read failure.\n");
1096 			terminate_calibration(0);
1097 			return;
1098 		}
1099 
1100 		if (aborted)
1101 		{
1102 			pmsg("\nUser input. Terminating calibration ...\n");
1103 			terminate_calibration(0);
1104 			return;
1105 		}
1106 
1107 		if (retval != 0)
1108 		{
1109 			if (cmd == '$') {
1110 				pmsg("\nBattery Runtime Calibration terminated by UPS.\n");
1111 				pmsg("Checking estimated runtime ...\n");
1112 				ans = smart_poll('j', ups);
1113 				if (*ans >= '0' && *ans <= '9') {
1114 					int rt = atoi(ans);
1115 
1116 					pmsg("Remaining runtime is %d minutes\n", rt);
1117 				} else {
1118 					pmsg("Unexpected response from UPS: %s\n", ans);
1119 				}
1120 				return;
1121 				/* ignore normal characters */
1122 			} else if (cmd == '!' || cmd == '+' || cmd == ' ' ||
1123 					cmd == '\n' || cmd == '\r') {
1124 				continue;
1125 			} else {
1126 				pmsg("\nUPS sent: %c\n", cmd);
1127 			}
1128 		}
1129 		else
1130 		{
1131 			if (++count >= max_count) {
1132 				ans = smart_poll('f', ups); /* Get battery charge */
1133 				percent = (int)strtod(ans, NULL);
1134 				pmsg("\nBattery charge %d\n", percent);
1135 
1136 				if (percent > 0) {
1137 					if (monitor && percent <= 10) {
1138 						pmsg("Battery charge less than 10% terminating calibration ...\n");
1139 						terminate_calibration(0);
1140 						return;
1141 					}
1142 					if (percent < 30)
1143 						max_count = 2;   /* report faster */
1144 				}
1145 
1146 				ans = smart_poll('j', ups); /* Get runtime */
1147 				if (*ans >= '0' && *ans <= '9') {
1148 					int rt = atoi(ans);
1149 
1150 					pmsg("Remaining runtime is %d minutes\n", rt);
1151 					if (monitor && rt < 2) {
1152 						pmsg("Runtime less than 2 minutes terminating calibration ...\n");
1153 						terminate_calibration(0);
1154 						return;
1155 					}
1156 				}
1157 				count = 0;
1158 			} else {
1159 				printf(".");
1160 				fflush(stdout);
1161 			}
1162 		}
1163 	}
1164 }
1165 
program_smart_eeprom(void)1166 static void program_smart_eeprom(void)
1167 {
1168 	char *cmd;
1169 	int quit = FALSE;
1170 
1171 	pmsg("This is the EEPROM programming section of apctest.\n"
1172 			"Please select the function you want to perform.\n");
1173 
1174 	while (!quit) {
1175 		pmsg("\n"
1176 				" 1) Print EEPROM values\n"
1177 				" 2) Change Battery date\n"
1178 				" 3) Change UPS name\n"
1179 				" 4) Change sensitivity\n"
1180 				" 5) Change alarm delay\n"
1181 				" 6) Change low battery warning delay\n"
1182 				" 7) Change wakeup delay\n"
1183 				" 8) Change shutdown delay\n"
1184 				" 9) Change low transfer voltage\n"
1185 				"10) Change high transfer voltage\n"
1186 				"11) Change battery return threshold percent\n"
1187 				"12) Change output voltage when on batteries\n"
1188 				"13) Change the self test interval\n"
1189 				"14) Set EEPROM with conf file values\n"
1190 				" Q) Quit\n\n");
1191 
1192 		cmd = get_cmd("Select function number: ");
1193 		if (cmd) {
1194 			int item = atoi(cmd);
1195 
1196 			switch (item) {
1197 			case 1:
1198 				print_eeprom_values(ups);
1199 				break;
1200 
1201 			case 2:
1202 				cmd = get_cmd("Enter new battery date -- DD/MM/YY: ");
1203 				if (strlen(cmd) != 8 || cmd[2] != '/' || cmd[5] != '/') {
1204 					pmsg("Date must be exactly DD/MM/YY\n");
1205 					break;
1206 				}
1207 				apcsmart_ups_program_eeprom(ups, CI_BATTDAT, cmd);
1208 				break;
1209 
1210 			case 3:
1211 				cmd = get_cmd("Enter new UPS name -- max 8 chars: ");
1212 				if (strlen(cmd) == 0 || strlen(cmd) > 8) {
1213 					pmsg("Name must be between 1 and 8 characters long.\n");
1214 					break;
1215 				}
1216 				apcsmart_ups_program_eeprom(ups, CI_IDEN, cmd);
1217 				break;
1218 
1219 			case 4:
1220 				cmd = get_cmd("Enter new sensitivity: ");
1221 				apcsmart_ups_program_eeprom(ups, CI_SENS, cmd);
1222 				break;
1223 
1224 			case 5:
1225 				cmd = get_cmd("Enter new alarm delay: ");
1226 				apcsmart_ups_program_eeprom(ups, CI_DALARM, cmd);
1227 				break;
1228 
1229 			case 6:
1230 				cmd = get_cmd("Enter new low battery delay: ");
1231 				apcsmart_ups_program_eeprom(ups, CI_DLBATT, cmd);
1232 				break;
1233 
1234 			case 7:
1235 				cmd = get_cmd("Enter new wakeup delay: ");
1236 				apcsmart_ups_program_eeprom(ups, CI_DWAKE, cmd);
1237 				break;
1238 
1239 			case 8:
1240 				cmd = get_cmd("Enter new shutdown delay: ");
1241 				apcsmart_ups_program_eeprom(ups, CI_DSHUTD, cmd);
1242 				break;
1243 
1244 			case 9:
1245 				cmd = get_cmd("Enter new low transfer voltage: ");
1246 				apcsmart_ups_program_eeprom(ups, CI_LTRANS, cmd);
1247 				break;
1248 
1249 			case 10:
1250 				cmd = get_cmd("Enter new high transfer voltage: ");
1251 				apcsmart_ups_program_eeprom(ups, CI_HTRANS, cmd);
1252 				break;
1253 
1254 			case 11:
1255 				cmd = get_cmd("Enter new battery return level: ");
1256 				apcsmart_ups_program_eeprom(ups, CI_RETPCT, cmd);
1257 				break;
1258 
1259 			case 12:
1260 				cmd = get_cmd("Enter new output voltage on batteries: ");
1261 				apcsmart_ups_program_eeprom(ups, CI_NOMOUTV, cmd);
1262 				break;
1263 
1264 			case 13:
1265 				cmd = get_cmd("Enter new self test interval: ");
1266 				apcsmart_ups_program_eeprom(ups, CI_STESTI, cmd);
1267 				break;
1268 
1269 			case 14:
1270 				pmsg("The EEPROM values to be changed will be taken from\n"
1271 						"the configuration directives in your apcctrl.conf file.\n");
1272 				cmd = get_cmd("Do you want to continue? (Y/N): ");
1273 				if (*cmd != 'y' && *cmd != 'Y') {
1274 					pmsg("EEPROM changes NOT made.\n");
1275 					break;
1276 				}
1277 				apcsmart_ups_program_eeprom(ups, -1, NULL);
1278 				break;
1279 
1280 			default:
1281 				if (tolower(*cmd) == 'q')
1282 					quit = TRUE;
1283 				else
1284 					pmsg("Illegal response. Please enter 1-14,Q\n");
1285 				break;
1286 			}
1287 		} else {
1288 			pmsg("Illegal response. Please enter 1-14,Q\n");
1289 		}
1290 	}
1291 	ptime();
1292 	pmsg("End EEPROM programming.\n");
1293 }
1294 
smart_test1(void)1295 static void smart_test1(void)
1296 {
1297 	char *ans, *p, *o, cmd;
1298 	char answer[2000];
1299 	char parts[2000];
1300 	int stat;
1301 
1302 #ifdef working
1303 	char locale, locale1, locale2;
1304 #endif
1305 
1306 	pmsg("I am going to run through the series of queries of the UPS\n"
1307 			"that are used in initializing apcctrl.\n\n");
1308 
1309 	pmsg("Simulating UPSlinkCheck ...\n");
1310 	tcflush(ups->fd, TCIOFLUSH);
1311 
1312 	/* Start really simply */
1313 	cmd = 'Y';
1314 	stat = write(ups->fd, &cmd, 1);
1315 	if (stat < 0)
1316 		pmsg("Bad status from write: %s\n", strerror(errno));
1317 
1318 	stat = getline(answer, sizeof(answer), ups);
1319 	pmsg("Wrote: Y Got: %s\n", answer);
1320 	if (stat == FAILURE) {
1321 		pmsg("getline failed. Apparently the link is not up.\n"
1322 				"Giving up.\n");
1323 		return;
1324 	}
1325 
1326 	pmsg("Attempting to use smart_poll() ...\n");
1327 	ans = smart_poll('Y', ups);
1328 	pmsg("Sent: Y Got: %s  ", ans);
1329 	if (strcmp(ans, "SM") == 0) {
1330 		pmsg("Good -- smart_poll() works!.\n\n");
1331 	} else {
1332 		pmsg("Not good.\nGiving up.\n");
1333 		return;
1334 	}
1335 
1336 	pmsg("Going to ask for valid commands...\n");
1337 	cmd = 'a';
1338 	stat = write(ups->fd, &cmd, 1);
1339 	if (stat != 1)
1340 		pmsg("Bad response from write: %d %s\n", stat, strerror(errno));
1341 
1342 	*answer = 0;
1343 	stat = getline(answer, sizeof(answer), ups);
1344 	pmsg("Wrote: a Got: %s\n", answer);
1345 	if (stat == FAILURE) {
1346 		pmsg("Cannot get the list of valid commands (a very old ups perhaps ?).\n");
1347 	}
1348 
1349 	/* Get protocol version */
1350 	for (p = answer, o = parts; *p && *p != '.'; p++)
1351 		*o++ = *p;
1352 
1353 	*o = 0;
1354 	pmsg("Protocol version is: %s\n", parts);
1355 	if (*p == '.')
1356 		p++;
1357 
1358 	/* Get alert characters */
1359 	for (o = parts; *p && *p != '.'; p++) {
1360 		if (*p < 0x20) {
1361 			*o++ = '^';
1362 			*o++ = *p + 'A' - 1;
1363 		} else {
1364 			*o++ = *p;
1365 		}
1366 	}
1367 	*o = 0;
1368 
1369 	pmsg("Alert characters are: %s\n", parts);
1370 	if (*p == '.')
1371 		p++;
1372 
1373 	/* Get command characters */
1374 	for (o = parts; *p; p++) {
1375 		if (*p < 0x20) {
1376 			*o++ = '^';
1377 			*o++ = *p + 'A' - 1;
1378 		} else {
1379 			*o++ = *p;
1380 		}
1381 	}
1382 	*o = 0;
1383 
1384 	pmsg("Command characters are: %s\n", parts);
1385 	pmsg("\nNow running through apcctrl get_UPS capabilities().\n"
1386 			"NA  indicates that the feature is Not Available\n\n");
1387 
1388 	pmsg("UPS Status: %s\n", smart_poll(ups->UPS_Cmd[CI_STATUS], ups));
1389 
1390 	pmsg("Line quality: %s\n", smart_poll(ups->UPS_Cmd[CI_LQUAL], ups));
1391 
1392 	pmsg("Reason for last transfer to batteries: %s\n",
1393 			smart_poll(ups->UPS_Cmd[CI_WHY_BATT], ups));
1394 
1395 	pmsg("Self-Test Status: %s\n", smart_poll(ups->UPS_Cmd[CI_ST_STAT], ups));
1396 
1397 	pmsg("Line Voltage: %s\n", smart_poll(ups->UPS_Cmd[CI_VLINE], ups));
1398 
1399 	pmsg("Line Voltage Max: %s\n", smart_poll(ups->UPS_Cmd[CI_VMAX], ups));
1400 
1401 	pmsg("Line Voltage Min: %s\n", smart_poll(ups->UPS_Cmd[CI_VMIN], ups));
1402 
1403 	pmsg("Output Voltage: %s\n", smart_poll(ups->UPS_Cmd[CI_VOUT], ups));
1404 
1405 	pmsg("Batt level percent: %s\n", smart_poll(ups->UPS_Cmd[CI_BATTLEV], ups));
1406 
1407 	pmsg("Batt voltage: %s\n", smart_poll(ups->UPS_Cmd[CI_VBATT], ups));
1408 
1409 	pmsg("UPS Load: %s\n", smart_poll(ups->UPS_Cmd[CI_LOAD], ups));
1410 
1411 	pmsg("Line freq: %s\n", smart_poll(ups->UPS_Cmd[CI_FREQ], ups));
1412 
1413 	pmsg("Runtime left: %s\n", smart_poll(ups->UPS_Cmd[CI_RUNTIM], ups));
1414 
1415 	pmsg("UPS Internal temp: %s\n", smart_poll(ups->UPS_Cmd[CI_ITEMP], ups));
1416 
1417 	pmsg("Dip switch settings: %s\n", smart_poll(ups->UPS_Cmd[CI_DIPSW], ups));
1418 
1419 	pmsg("Register 1: %s\n", smart_poll(ups->UPS_Cmd[CI_REG1], ups));
1420 
1421 	pmsg("Register 2: %s\n", smart_poll(ups->UPS_Cmd[CI_REG2], ups));
1422 
1423 	pmsg("Register 3: %s\n", smart_poll('8', ups));
1424 
1425 	pmsg("Sensitivity: %s\n", smart_poll(ups->UPS_Cmd[CI_SENS], ups));
1426 
1427 	pmsg("Wakeup delay: %s\n", smart_poll(ups->UPS_Cmd[CI_DWAKE], ups));
1428 
1429 	pmsg("Sleep delay: %s\n", smart_poll(ups->UPS_Cmd[CI_DSHUTD], ups));
1430 
1431 	pmsg("Low transfer voltage: %s\n", smart_poll(ups->UPS_Cmd[CI_LTRANS], ups));
1432 
1433 	pmsg("High transfer voltage: %s\n", smart_poll(ups->UPS_Cmd[CI_HTRANS], ups));
1434 
1435 	pmsg("Batt charge for return: %s\n", smart_poll(ups->UPS_Cmd[CI_RETPCT], ups));
1436 
1437 	pmsg("Alarm status: %s\n", smart_poll(ups->UPS_Cmd[CI_DALARM], ups));
1438 
1439 	pmsg("Low battery shutdown level: %s\n", smart_poll(ups->UPS_Cmd[CI_DLBATT],
1440 			ups));
1441 
1442 	pmsg("UPS Name: %s\n", smart_poll(ups->UPS_Cmd[CI_IDEN], ups));
1443 
1444 	pmsg("UPS Self test interval: %s\n", smart_poll(ups->UPS_Cmd[CI_STESTI], ups));
1445 
1446 	pmsg("UPS manufacture date: %s\n", smart_poll(ups->UPS_Cmd[CI_MANDAT], ups));
1447 
1448 	pmsg("UPS serial number: %s\n", smart_poll(ups->UPS_Cmd[CI_SERNO], ups));
1449 
1450 	pmsg("Date battery replaced: %s\n", smart_poll(ups->UPS_Cmd[CI_BATTDAT], ups));
1451 
1452 	pmsg("Output voltage when on batteries: %s\n",
1453 			smart_poll(ups->UPS_Cmd[CI_NOMOUTV], ups));
1454 
1455 	pmsg("Nominal battery voltage: %s\n", smart_poll(ups->UPS_Cmd[CI_NOMBATTV], ups));
1456 
1457 	pmsg("Percent humidity: %s\n", smart_poll(ups->UPS_Cmd[CI_HUMID], ups));
1458 
1459 	pmsg("Ambient temperature: %s\n", smart_poll(ups->UPS_Cmd[CI_ATEMP], ups));
1460 
1461 	pmsg("Firmware revision: %s\n", smart_poll(ups->UPS_Cmd[CI_REVNO], ups));
1462 
1463 	pmsg("Number of external batteries installed: %s\n",
1464 			smart_poll(ups->UPS_Cmd[CI_EXTBATTS], ups));
1465 
1466 	pmsg("Number of bad batteries installed: %s\n",
1467 			smart_poll(ups->UPS_Cmd[CI_BADBATTS], ups));
1468 
1469 	pmsg("UPS model as defined by UPS: %s\n", smart_poll(ups->UPS_Cmd[CI_UPSMODEL],
1470 			ups));
1471 
1472 	pmsg("UPS EPROM capabilities string: %s\n", (ans =
1473 			smart_poll(ups->UPS_Cmd[CI_EPROM], ups)));
1474 	pmsg("The EPROM string is %d characters long!\n", strlen(ans));
1475 
1476 	pmsg("Hours since last self test: %s\n", smart_poll(ups->UPS_Cmd[CI_ST_TIME],
1477 			ups));
1478 
1479 	pmsg("\nThat is all for now.\n");
1480 	return;
1481 }
1482 #endif
1483 
do_usb_testing(void)1484 static void do_usb_testing(void)
1485 {
1486 #ifdef HAVE_USB_DRIVER
1487 	char *cmd;
1488 	int quit = FALSE;
1489 
1490 	pmsg("Hello, this is the apcctrl Cable Test program.\n"
1491 			"This part of apctest is for testing USB UPSes.\n");
1492 
1493 	pmsg("\nGetting UPS capabilities...");
1494 	if (!ups->driver->get_capabilities())
1495 		pmsg("FAILED\nSome or all tests may not work!\n");
1496 	else
1497 		pmsg("SUCCESS\n");
1498 
1499 	pmsg("\nPlease select the function you want to perform.\n");
1500 
1501 	while (!quit) {
1502 		pmsg("\n"
1503 				"1)  Test kill UPS power\n"
1504 				"2)  Perform self-test\n"
1505 				"3)  Read last self-test result\n"
1506 				"4)  View/Change battery date\n"
1507 				"5)  View manufacturing date\n"
1508 				"6)  View/Change alarm behavior\n"
1509 				"7)  View/Change sensitivity\n"
1510 				"8)  View/Change low transfer voltage\n"
1511 				"9)  View/Change high transfer voltage\n"
1512 				"10) Perform battery calibration\n"
1513 				"11) Test alarm\n"
1514 				"12) View/Change self-test interval\n"
1515 				" Q) Quit\n\n");
1516 
1517 		cmd = get_cmd("Select function number: ");
1518 		if (cmd) {
1519 			int item = atoi(cmd);
1520 
1521 			switch (item) {
1522 			case 1:
1523 				usb_kill_power_test();
1524 				break;
1525 			case 2:
1526 				usb_run_self_test();
1527 				break;
1528 			case 3:
1529 				usb_get_self_test_result();
1530 				break;
1531 			case 4:
1532 				usb_set_battery_date();
1533 				break;
1534 			case 5:
1535 				usb_get_manf_date();
1536 				break;
1537 			case 6:
1538 				usb_set_alarm();
1539 				break;
1540 			case 7:
1541 				usb_set_sens();
1542 				break;
1543 			case 8:
1544 				usb_set_xferv(0);
1545 				break;
1546 			case 9:
1547 				usb_set_xferv(1);
1548 				break;
1549 			case 10:
1550 				usb_calibration();
1551 				break;
1552 			case 11:
1553 				usb_test_alarm();
1554 				break;
1555 			case 12:
1556 				usb_set_self_test_interval();
1557 				break;
1558 			default:
1559 				if (tolower(*cmd) == 'q')
1560 					quit = TRUE;
1561 				else
1562 					pmsg("Illegal response. Please enter 1-12,Q\n");
1563 				break;
1564 			}
1565 		} else {
1566 			pmsg("Illegal response. Please enter 1-12,Q\n");
1567 		}
1568 	}
1569 	ptime();
1570 	pmsg("End apctest.\n");
1571 #else
1572 	pmsg("USB Driver not configured.\n");
1573 #endif
1574 }
1575 
1576 #ifdef HAVE_USB_DRIVER
1577 
usb_set_xferv(int lowhigh)1578 static void usb_set_xferv(int lowhigh)
1579 {
1580 	int result;
1581 	char* cmd;
1582 	const char *text;
1583 	int ci;
1584 
1585 	if (lowhigh) {
1586 		text = "HIGH";
1587 		ci = CI_HTRANS;
1588 	} else {
1589 		text = "LOW";
1590 		ci = CI_LTRANS;
1591 	}
1592 
1593 	if (!usb_read_int_from_ups(ups, ci, &result)) {
1594 		pmsg("\nI don't know how to control the %s transfer voltage "
1595 				" settings on your UPS.\n", text);
1596 		return;
1597 	}
1598 
1599 	int newval;
1600 	do {
1601 		pmsg("Current %s transfer voltage setting: %d Volts\n", text, result);
1602 
1603 		pmsg("Enter new %s transfer voltage (0 to cancel): ", text);
1604 		cmd = get_cmd("");
1605 		newval = atoi(cmd);
1606 
1607 		// Check for exit
1608 		if (newval == 0)
1609 			return;
1610 
1611 		// Write new value
1612 		usb_write_int_to_ups(ups, ci, newval, text);
1613 
1614 		// Give write a chance to work, then read new value
1615 		sleep(1);
1616 		result = 0;
1617 		usb_read_int_from_ups(ups, ci, &result);
1618 
1619 		if (result != newval) {
1620 			pmsg("FAILED to set new %s transfer voltage.\n\n"
1621 					"This is probably because you entered a value that is out-of-range\n"
1622 					"for your UPS. The acceptable range of values varies based on UPS\n"
1623 					"model. The UPS has probably set the value as close as possible to\n"
1624 					"what you requested.\n\n", text);
1625 		}
1626 	}
1627 	while (result != newval);
1628 
1629 	pmsg("New %s transfer voltage setting: %d Volts\n", text, result);
1630 }
1631 
usb_set_sens(void)1632 static void usb_set_sens(void)
1633 {
1634 	int result;
1635 	char* cmd;
1636 
1637 	if (!usb_read_int_from_ups(ups, CI_SENS, &result)) {
1638 		pmsg("\nI don't know how to control the alarm settings on your UPS.\n");
1639 		return;
1640 	}
1641 
1642 	pmsg("Current sensitivity setting: ");
1643 	switch(result) {
1644 	case 0:
1645 		pmsg("LOW\n");
1646 		break;
1647 	case 1:
1648 		pmsg("MEDIUM\n");
1649 		break;
1650 	case 2:
1651 		pmsg("HIGH\n");
1652 		break;
1653 	default:
1654 		pmsg("UNKNOWN\n");
1655 		break;
1656 	}
1657 
1658 	while(1) {
1659 		pmsg("Press...\n"
1660 				" L for Low sensitivity\n"
1661 				" M for Medium sensitivity\n"
1662 				" H for High sensitivity\n"
1663 				" Q to Quit with no changes\n"
1664 				"Your choice: ");
1665 		cmd = get_cmd("Select function: ");
1666 		if (cmd) {
1667 			switch(tolower(*cmd)) {
1668 			case 'l':
1669 				usb_write_int_to_ups(ups, CI_SENS, 0, "CI_SENS");
1670 				break;
1671 			case 'm':
1672 				usb_write_int_to_ups(ups, CI_SENS, 1, "CI_SENS");
1673 				break;
1674 			case 'h':
1675 				usb_write_int_to_ups(ups, CI_SENS, 2, "CI_SENS");
1676 				break;
1677 			case 'q':
1678 				return;
1679 			default:
1680 				pmsg("Illegal response.\n");
1681 				continue;
1682 			}
1683 		} else {
1684 			pmsg("Illegal response.\n");
1685 			continue;
1686 		}
1687 
1688 		break;
1689 	}
1690 
1691 	/* Delay needed for readback to work */
1692 	sleep(1);
1693 
1694 	usb_read_int_from_ups(ups, CI_SENS, &result);
1695 	pmsg("New sensitivity setting: ");
1696 	switch(result) {
1697 	case 0:
1698 		pmsg("LOW\n");
1699 		break;
1700 	case 1:
1701 		pmsg("MEDIUM\n");
1702 		break;
1703 	case 2:
1704 		pmsg("HIGH\n");
1705 		break;
1706 	default:
1707 		pmsg("UNKNOWN\n");
1708 		break;
1709 	}
1710 }
1711 
usb_set_alarm(void)1712 static void usb_set_alarm(void)
1713 {
1714 	int result;
1715 	char* cmd;
1716 
1717 	if (!usb_read_int_from_ups(ups, CI_DALARM, &result)) {
1718 		pmsg("\nI don't know how to control the alarm settings on your UPS.\n");
1719 		return;
1720 	}
1721 
1722 	pmsg("Current alarm setting: ");
1723 	switch(result) {
1724 	case 1:
1725 		pmsg("DISABLED\n");
1726 		break;
1727 	case 2:
1728 		pmsg("ENABLED\n");
1729 		break;
1730 	default:
1731 		pmsg("UNKNOWN\n");
1732 		break;
1733 	}
1734 
1735 	while(1) {
1736 		pmsg("Press...\n"
1737 				" E to Enable alarms\n"
1738 				" D to Disable alarms\n"
1739 				" Q to Quit with no changes\n"
1740 				"Your choice: ");
1741 		cmd = get_cmd("Select function: ");
1742 		if (cmd) {
1743 			switch(tolower(*cmd)) {
1744 			case 'e':
1745 				usb_write_int_to_ups(ups, CI_DALARM, 2, "CI_DALARM");
1746 				break;
1747 			case 'd':
1748 				usb_write_int_to_ups(ups, CI_DALARM, 1, "CI_DALARM");
1749 				break;
1750 			case 'q':
1751 				return;
1752 			default:
1753 				pmsg("Illegal response.\n");
1754 				continue;
1755 			}
1756 		} else {
1757 			pmsg("Illegal response.\n");
1758 			continue;
1759 		}
1760 
1761 		break;
1762 	}
1763 
1764 	/* Delay needed for readback to work */
1765 	sleep(1);
1766 
1767 	usb_read_int_from_ups(ups, CI_DALARM, &result);
1768 	pmsg("New alarm setting: ");
1769 	switch(result) {
1770 	case 1:
1771 		pmsg("DISABLED\n");
1772 		break;
1773 	case 2:
1774 		pmsg("ENABLED\n");
1775 		break;
1776 	default:
1777 		pmsg("UNKNOWN\n");
1778 		break;
1779 	}
1780 }
1781 
usb_kill_power_test(void)1782 static void usb_kill_power_test(void)
1783 {
1784 	pmsg("\nThis test will attempt to power down the UPS.\n"
1785 			"The USB cable should be plugged in to the UPS, but the\n"
1786 			"AC power plug to the UPS should be DISCONNECTED.\n\n"
1787 			"PLEASE DO NOT RUN THIS TEST WITH A COMPUTER CONNECTED TO YOUR UPS!!!\n\n"
1788 			"Please enter any character when ready to continue: ");
1789 
1790 	fgetc(stdin);
1791 	pmsg("\n");
1792 
1793 	ptime();
1794 	pmsg("calling kill_power function.\n");
1795 
1796 	make_file(ups, ups->pwrfailpath);
1797 	initiate_hibernate(ups);
1798 	unlink(ups->pwrfailpath);
1799 
1800 	ptime();
1801 	pmsg("returned from kill_power function.\n");
1802 }
1803 
usb_get_self_test_result(void)1804 static void usb_get_self_test_result(void)
1805 {
1806 	int result;
1807 
1808 	if (!usb_read_int_from_ups(ups, CI_ST_STAT, &result)) {
1809 		pmsg("\nI don't know how to run a self test on your UPS\n"
1810 				"or your UPS does not support self test.\n");
1811 		return;
1812 	}
1813 
1814 	pmsg("Result of last self test: ");
1815 	switch (result) {
1816 	case 1:
1817 		pmsg("PASSED\n");
1818 		break;
1819 	case 2:
1820 		pmsg("WARNING\n");
1821 		break;
1822 	case 3:
1823 		pmsg("ERROR\n");
1824 		break;
1825 	case 4:
1826 		pmsg("ABORTED\n");
1827 		break;
1828 	case 5:
1829 		pmsg("IN PROGRESS\n");
1830 		break;
1831 	case 6:
1832 		pmsg("NO TEST PERFORMED\n");
1833 		break;
1834 	default:
1835 		pmsg("UNKNOWN (%02x)\n", result);
1836 		break;
1837 	}
1838 }
1839 
usb_clear_test_result()1840 static bool usb_clear_test_result()
1841 {
1842 	int timeout, result;
1843 
1844 	pmsg("Clearing previous self test result...");
1845 
1846 	// abort battery calibration in case it's in progress
1847 	usb_write_int_to_ups(ups, CI_ST_STAT, 3, "SelftestStatus");
1848 
1849 	if (!usb_write_int_to_ups(ups, CI_ST_STAT, 0, "SelftestStatus")) {
1850 		pmsg("FAILED\n");
1851 		return false;
1852 	}
1853 
1854 	for (timeout = 0; timeout < 10; timeout++) {
1855 		if (!usb_read_int_from_ups(ups, CI_ST_STAT, &result)) {
1856 			pmsg("FAILED\n");
1857 			return false;
1858 		}
1859 
1860 		if (result == 6) {
1861 			pmsg("CLEARED\n");
1862 			break;
1863 		}
1864 
1865 		sleep(1);
1866 	}
1867 
1868 	if (timeout == 10) {
1869 		pmsg("FAILED\n");
1870 		return false;
1871 	}
1872 
1873 	return true;
1874 }
1875 
usb_run_self_test(void)1876 static void usb_run_self_test(void)
1877 {
1878 	int result;
1879 	int timeout;
1880 
1881 	pmsg("\nThis test instructs the UPS to perform a self-test\n"
1882 			"operation and reports the result when the test completes.\n\n");
1883 
1884 	if (!usb_read_int_from_ups(ups, CI_ST_STAT, &result)) {
1885 		pmsg("I don't know how to run a self test on your UPS\n"
1886 				"or your UPS does not support self test.\n");
1887 		return;
1888 	}
1889 
1890 	if (!usb_clear_test_result())
1891 		return;
1892 
1893 	pmsg("Initiating self test...");
1894 	if (!usb_write_int_to_ups(ups, CI_ST_STAT, 1, "SelftestStatus")) {
1895 		pmsg("FAILED\n");
1896 		return;
1897 	}
1898 
1899 	pmsg("INITIATED\n");
1900 
1901 	pmsg("Waiting for test to complete...");
1902 
1903 	for (timeout = 0; timeout < 40; timeout++) {
1904 		if (!usb_read_int_from_ups(ups, CI_ST_STAT, &result)) {
1905 			pmsg("ERROR READING STATUS\n");
1906 			usb_write_int_to_ups(ups, CI_ST_STAT, 3, "SelftestStatus");
1907 			return;
1908 		}
1909 
1910 		if (result != 6) {
1911 			pmsg("COMPLETED\n");
1912 			break;
1913 		}
1914 
1915 		sleep(1);
1916 	}
1917 
1918 	if (timeout == 40) {
1919 		pmsg("TEST DID NOT COMPLETE\n");
1920 		usb_write_int_to_ups(ups, CI_ST_STAT, 3, "SelftestStatus");
1921 		return;
1922 	}
1923 
1924 	usb_get_self_test_result();
1925 }
1926 
usb_get_battery_date(void)1927 static int usb_get_battery_date(void)
1928 {
1929 	int result;
1930 
1931 	if (!usb_read_int_from_ups(ups, CI_BATTDAT, &result)) {
1932 		pmsg("\nI don't know how to access the battery date on your UPS\n"
1933 				"or your UPS does not support the battery date feature.\n");
1934 		return 0;
1935 	}
1936 
1937 	/*
1938 	 * Date format is:
1939 	 *   YYYY|YYYM|MMMD|DDDD
1940 	 *   bit  0-4:  Day
1941 	 *        5-8:  Month
1942 	 *        9-15: Year-1980
1943 	 */
1944 	pmsg("Current battery date: %02u/%02u/%04u\n",
1945 			(result & 0x1e0) >> 5, result & 0x1f, 1980 + ((result & 0xfe00) >> 9));
1946 
1947 	return result;
1948 }
1949 
usb_set_battery_date(void)1950 static void usb_set_battery_date(void)
1951 {
1952 	char *cmd;
1953 	int result, day, month, year, temp, max;
1954 
1955 	if (!(result = usb_get_battery_date()))
1956 		return;
1957 
1958 	cmd = get_cmd("Enter new battery date (MM/DD/YYYY), blank to quit: ");
1959 	if (!isdigit(cmd[0]) || !isdigit(cmd[1]) || cmd[2] != '/' ||
1960 			!isdigit(cmd[3]) || !isdigit(cmd[4]) || cmd[5] != '/' ||
1961 			!isdigit(cmd[6]) || !isdigit(cmd[7]) || !isdigit(cmd[8]) ||
1962 			!isdigit(cmd[9]) || cmd[10] != '\0' ||
1963 			((month = strtoul(cmd, NULL, 10)) > 12) || (month < 1) ||
1964 			((day = strtoul(cmd + 3, NULL, 10)) > 31) || (day < 1) ||
1965 			((year = strtoul(cmd + 6, NULL, 10)) < 1980)) {
1966 		pmsg("Invalid format.\n");
1967 		return;
1968 	}
1969 
1970 	result = ((year - 1980) << 9) | (month << 5) | day;
1971 
1972 	pmsg("Writing new date...");
1973 	if (!usb_write_int_to_ups(ups, CI_BATTDAT, result, "ManufactureDate")) {
1974 		pmsg("FAILED\n");
1975 		return;
1976 	}
1977 
1978 	pmsg("SUCCESS\n");
1979 
1980 	pmsg("Waiting for change to take effect...");
1981 	for (max = 0; max < 10; max++) {
1982 		if (!usb_read_int_from_ups(ups, CI_BATTDAT, &temp)) {
1983 			pmsg("ERROR\n");
1984 			return;
1985 		}
1986 
1987 		if (temp == result)
1988 			break;
1989 
1990 		sleep(1);
1991 	}
1992 
1993 	if (max == 10)
1994 		pmsg("TIMEOUT\n");
1995 	else
1996 		pmsg("SUCCESS\n");
1997 
1998 	usb_get_battery_date();
1999 }
2000 
usb_get_manf_date(void)2001 static void usb_get_manf_date(void)
2002 {
2003 	int result;
2004 
2005 	if (!usb_read_int_from_ups(ups, CI_MANDAT, &result)) {
2006 		pmsg("\nI don't know how to access the manufacturing date on your UPS\n"
2007 				"or your UPS does not support the manufacturing date feature.\n");
2008 		return;
2009 	}
2010 
2011 	/*
2012 	 * Date format is:
2013 	 *   YYYY|YYYM|MMMD|DDDD
2014 	 *   bit  0-4:  Day
2015 	 *        5-8:  Month
2016 	 *        9-15: Year-1980
2017 	 */
2018 	pmsg("Manufacturing date: %02u/%02u/%04u\n",
2019 			(result & 0x1e0) >> 5, result & 0x1f, 1980 + ((result & 0xfe00) >> 9));
2020 }
2021 
usb_calibration()2022 static void usb_calibration()
2023 {
2024 	int result;
2025 	int aborted;
2026 	int ilastbl;
2027 
2028 	if (!ups->UPS_Cap[CI_ST_STAT] ||
2029 			!ups->UPS_Cap[CI_BATTLEV] ||
2030 			!ups->UPS_Cap[CI_LOAD]) {
2031 		pmsg("\nI don't know how to run a battery calibration on your UPS\n"
2032 				"or your UPS does not support battery calibration\n");
2033 		return;
2034 	}
2035 
2036 	pmsg("This test instructs the UPS to perform a battery calibration\n"
2037 			"operation and reports the result when the process completes.\n"
2038 			"The battery level must be at 100%% and the load must be at least\n"
2039 			"10%% to begin this test.\n\n");
2040 
2041 	if (!usb_read_int_from_ups(ups, CI_BATTLEV, &result)) {
2042 		pmsg("Failed to read current battery level\n");
2043 		return;
2044 	}
2045 
2046 	if (result == 100) {
2047 		pmsg("Battery level is %d%% -- OK\n", result);
2048 	}
2049 	else {
2050 		pmsg("Battery level %d%% is insufficient to run test.\n", result);
2051 		return;
2052 	}
2053 
2054 	if (!usb_read_int_from_ups(ups, CI_LOAD, &result)) {
2055 		pmsg("Failed to read current load level\n");
2056 		return;
2057 	}
2058 
2059 	if (result >= 10) {
2060 		pmsg("Load level is %d%% -- OK\n", result);
2061 	}
2062 	else {
2063 		pmsg("Load level %d%% is insufficient to run test.\n", result);
2064 		return;
2065 	}
2066 
2067 	if (!usb_clear_test_result())
2068 		return;
2069 
2070 	pmsg("\nThe battery calibration should automatically end\n"
2071 			"when the battery level drops below about 25%%.\n"
2072 			"This process can take minutes or hours, depending on\n"
2073 			"the size of your UPS and the load attached.\n\n");
2074 
2075 	pmsg("Initiating battery calibration...");
2076 	if (!usb_write_int_to_ups(ups, CI_ST_STAT, 2, "SelftestStatus")) {
2077 		pmsg("FAILED\n");
2078 		return;
2079 	}
2080 
2081 	pmsg("INITIATED\n\n");
2082 
2083 	pmsg("Waiting for calibration to complete...\n"
2084 			"To abort the calibration, press ENTER.\n");
2085 
2086 	ilastbl = 0;
2087 	while (1) {
2088 #ifndef HAVE_MINGW
2089 		fd_set rfds;
2090 		struct timeval tv;
2091 		FD_ZERO(&rfds);
2092 		FD_SET(STDIN_FILENO, &rfds);
2093 		tv.tv_sec = 10;
2094 		tv.tv_usec = 0;
2095 
2096 		aborted = select(STDIN_FILENO+1, &rfds, NULL, NULL, &tv) == 1;
2097 		if (aborted) {
2098 			while (fgetc(stdin) != '\n')
2099 				;
2100 		}
2101 #else
2102 		aborted = false;
2103 		for (int i = 0; i < 100 && !aborted; i++) {
2104 			while (kbhit() && !aborted)
2105 				aborted = getch() == '\r';
2106 			if (!aborted)
2107 			{
2108 				struct timespec ts;
2109 				ts.tv_sec = 0;
2110 				ts.tv_nsec = 100000000;
2111 				nanosleep(&ts, NULL);
2112 			}
2113 		}
2114 #endif
2115 
2116 		if (aborted) {
2117 			pmsg("\n\nUser input detected; aborting calibration...");
2118 			if (!usb_write_int_to_ups(ups, CI_ST_STAT, 3, "SelftestStatus")) {
2119 				pmsg("FAILED\n");
2120 			}
2121 			else {
2122 				pmsg("ABORTED\n");
2123 			}
2124 			return;
2125 		}
2126 
2127 		if (!usb_read_int_from_ups(ups, CI_ST_STAT, &result)) {
2128 			pmsg("\n\nError reading status; aborting calibration...");
2129 			if (!usb_write_int_to_ups(ups, CI_ST_STAT, 3, "SelftestStatus")) {
2130 				pmsg("FAILED\n");
2131 			}
2132 			else {
2133 				pmsg("ABORTED\n");
2134 			}
2135 			return;
2136 		}
2137 
2138 		if (result != 5) {
2139 			pmsg("\nCALIBRATION COMPLETED\n");
2140 			break;
2141 		}
2142 
2143 		// Output the battery level
2144 		if (usb_read_int_from_ups(ups, CI_BATTLEV, &result)) {
2145 			if (ilastbl == result)
2146 				pmsg(".");
2147 			else
2148 				pmsg("\nBattery level: %d%%", result);
2149 			ilastbl = result;
2150 		}
2151 		else
2152 			pmsg(".");
2153 	}
2154 
2155 	usb_get_self_test_result();
2156 }
2157 
usb_test_alarm(void)2158 static void usb_test_alarm(void)
2159 {
2160 	int result;
2161 
2162 	if (!usb_read_int_from_ups(ups, CI_TESTALARM, &result))
2163 	{
2164 		pmsg("\nI don't know how to test the alarm on your UPS.\n");
2165 		return;
2166 	}
2167 
2168 	// Write to UPS
2169 	pmsg("Testing alarm...");
2170 	usb_write_int_to_ups(ups, CI_TESTALARM, 1, "CI_TESTALARM");
2171 	sleep(1);
2172 
2173 	pmsg("COMPLETE\n");
2174 }
2175 
usb_get_self_test_interval(void)2176 static int usb_get_self_test_interval(void)
2177 {
2178 	int result;
2179 
2180 	if (!usb_read_int_from_ups(ups, CI_STESTI, &result))
2181 	{
2182 		pmsg("\nI don't know how to access the self-test interval on your UPS\n"
2183 				"or your UPS does not support the self-test interval feature.\n");
2184 		return -1;
2185 	}
2186 
2187 	pmsg("Current Self-test interval: ");
2188 	switch (result)
2189 	{
2190 	case 0:
2191 		pmsg("None\n");
2192 		break;
2193 	case 1:
2194 		pmsg("Power On\n");
2195 		break;
2196 	case 2:
2197 		pmsg("7 days\n");
2198 		break;
2199 	case 3:
2200 		pmsg("14 days\n");
2201 		break;
2202 	default:
2203 		pmsg("UNKNOWN (%02x)\n", result);
2204 		break;
2205 	}
2206 
2207 	return result;
2208 }
2209 
usb_set_self_test_interval(void)2210 static void usb_set_self_test_interval(void)
2211 {
2212 	if (usb_get_self_test_interval() == -1)
2213 		return;
2214 
2215 	while(1)
2216 	{
2217 		pmsg("Press...\n"
2218 				" 0 for None\n"
2219 				" 1 for On Power\n"
2220 				" 2 for 7 Days\n"
2221 				" 3 for 14 Days\n"
2222 				" Q to Quit with no changes\n"
2223 				"Your choice: ");
2224 		char *cmd = get_cmd("Select function: ");
2225 		if (cmd)
2226 		{
2227 			if (*cmd >= '0' && *cmd <= '3')
2228 			{
2229 				usb_write_int_to_ups(ups, CI_STESTI, *cmd-'0', "CI_STESTI");
2230 				break;
2231 			}
2232 			else if (tolower(*cmd) == 'q')
2233 				return;
2234 			else
2235 				pmsg("Illegal response.\n");
2236 		}
2237 		else
2238 		{
2239 			pmsg("Illegal response.\n");
2240 		}
2241 	}
2242 
2243 	/* Delay needed for readback to work */
2244 	sleep(1);
2245 	usb_get_self_test_interval();
2246 }
2247 
2248 #endif
2249 
2250 /* Get next input command from the terminal */
get_cmd(const char * prompt)2251 static char *get_cmd(const char *prompt)
2252 {
2253 	static char cmd[1000];
2254 
2255 	pmsg(prompt);
2256 
2257 	if (fgets(cmd, sizeof(cmd), stdin) == NULL)
2258 		return NULL;
2259 
2260 	write_file(cmd);
2261 
2262 	pmsg("\n");
2263 	strip_trailing_junk(cmd);
2264 
2265 	return cmd;
2266 }
2267 
2268 /* Strip any trailing junk from the command */
strip_trailing_junk(char * cmd)2269 static void strip_trailing_junk(char *cmd)
2270 {
2271 	char *p;
2272 
2273 	p = cmd + strlen(cmd) - 1;
2274 
2275 	/* strip trailing junk from command */
2276 	while ((p >= cmd) && (*p == '\n' || *p == '\r' || *p == ' '))
2277 		*p-- = 0;
2278 }
2279 
2280 #ifdef HAVE_APCSMART_DRIVER
2281 
2282 /*
2283  * EPROM commands and their values as parsed from the
2284  * ^Z eprom string returned by the UPS.
2285  */
2286 static struct {
2287 	char cmd;
2288 	char size;
2289 	char num;
2290 	char cmdvals[50];
2291 } cmd[15];
2292 
2293 /* Total number of EPROM commands */
2294 static int ncmd = 0;
2295 
2296 static UPSINFO eeprom_ups;
2297 
2298 /*
2299  * Table of the UPS command, the apcctrl configuration directive,
2300  * and an explanation of what the command sets in the EPROM.
2301  */
2302 static struct {
2303 	char cmd;
2304 	const char *config_directive;
2305 	const char *descript;
2306 	char type;
2307 	int *data;
2308 } cmd_table[] = {
2309 		{'u', "HITRANSFER",   "Upper transfer voltage",  'i', &eeprom_ups.hitrans},
2310 		{'l', "LOTRANSFER",   "Lower transfer voltage",  'i', &eeprom_ups.lotrans},
2311 		{'e', "RETURNCHARGE", "Return threshold",        'i', &eeprom_ups.rtnpct},
2312 		{'o', "OUTPUTVOLTS",  "Output voltage on batts", 'i', &eeprom_ups.NomOutputVoltage},
2313 		{'s', "SENSITIVITY",  "Sensitivity",             'c', (int *)eeprom_ups.sensitivity},
2314 		{'q', "LOWBATT",      "Low battery warning",     'i', &eeprom_ups.dlowbatt},
2315 		{'p', "SLEEP",        "Shutdown grace delay",    'i', &eeprom_ups.dshutd},
2316 		{'k', "BEEPSTATE",    "Alarm delay",             'c', (int *)eeprom_ups.beepstate},
2317 		{'r', "WAKEUP",       "Wakeup delay",            'i', &eeprom_ups.dwake},
2318 		{'E', "SELFTEST",     "Self test interval",      'c', (int *)eeprom_ups.selftest},
2319 		{0,   NULL,           NULL}     /* Last entry */
2320 };
2321 
2322 
print_valid_eeprom_values(UPSINFO * ups)2323 static void print_valid_eeprom_values(UPSINFO *ups)
2324 {
2325 	int i, j, k, l;
2326 	char *p;
2327 	char val[100];;
2328 
2329 	pmsg("\nValid EEPROM values for the %s\n\n", ups->mode.long_name);
2330 
2331 	memcpy(&eeprom_ups, ups, sizeof(UPSINFO));
2332 
2333 	pmsg("%-24s %-12s  %-6s  %s\n", "           ", "Config", "Current", "Permitted");
2334 	pmsg("%-24s %-12s  %-6s  %s\n", "Description", "Directive", "Value  ", "Values");
2335 	pmsg("===================================================================\n");
2336 
2337 	for (i = 0; i < ncmd; i++) {
2338 		for (j = 0; cmd_table[j].cmd; j++) {
2339 			if (cmd[i].cmd == cmd_table[j].cmd) {
2340 				if (cmd_table[j].type == 'c')
2341 					asnprintf(val, sizeof(val), "%s", (char *)cmd_table[j].data);
2342 				else
2343 					asnprintf(val, sizeof(val), "%d", *cmd_table[j].data);
2344 
2345 				pmsg("%-24s %-12s  %-6s   ", cmd_table[j].descript,
2346 						cmd_table[j].config_directive, val);
2347 
2348 				p = cmd[i].cmdvals;
2349 				for (k = cmd[i].num; k; k--) {
2350 					for (l = cmd[i].size; l; l--)
2351 						pmsg("%c", *p++);
2352 
2353 					pmsg(" ");
2354 				}
2355 
2356 				pmsg("\n");
2357 				break;
2358 			}
2359 		}
2360 	}
2361 
2362 	pmsg("===================================================================\n");
2363 
2364 	tcflush(ups->fd, TCIOFLUSH);
2365 	smart_poll('Y', ups);
2366 	smart_poll('Y', ups);
2367 
2368 	pmsg("Battery date: %s\n", smart_poll(ups->UPS_Cmd[CI_BATTDAT], ups));
2369 	pmsg("UPS Name    : %s\n", smart_poll(ups->UPS_Cmd[CI_IDEN], ups));
2370 	pmsg("\n");
2371 }
2372 
2373 /*
2374  * Parse EPROM command string returned by a ^Z command. We
2375  * pull out only entries that correspond to our UPS (locale).
2376  */
parse_eeprom_cmds(char * eprom,char locale)2377 static void parse_eeprom_cmds(char *eprom, char locale)
2378 {
2379 	char *p = eprom;
2380 	char c, l, n, s;
2381 #ifdef debuggggggg
2382 	int i;
2383 #endif
2384 
2385 	ncmd = 0;
2386 	for (;;) {
2387 		c = *p++;
2388 		if (c == 0)
2389 			break;
2390 
2391 		if (c == '#')
2392 			continue;
2393 
2394 		l = *p++;                    /* get locale */
2395 		n = *p++ - '0';              /* get number of commands */
2396 		s = *p++ - '0';              /* get character size */
2397 
2398 		if (l != '4' && l != locale) {    /* skip this locale */
2399 			p += n * s;
2400 			continue;
2401 		}
2402 
2403 		cmd[ncmd].cmd = c;           /* store command */
2404 		cmd[ncmd].size = s;          /* chare length of each value */
2405 		cmd[ncmd].num = n;           /* number of values */
2406 
2407 		strncpy(cmd[ncmd].cmdvals, p, n * s);     /* save values */
2408 		p += n * s;
2409 		ncmd++;
2410 	}
2411 #ifdef debuggggggg
2412 	printf("\n");
2413 	for (i = 0; i < ncmd; i++) {
2414 		printf("cmd=%c len=%d nvals=%d vals=%s\n", cmd[i].cmd,
2415 				cmd[i].size, cmd[i].num, cmd[i].cmdvals);
2416 	}
2417 #endif
2418 }
2419 
2420 /*********************************************************************/
print_eeprom_values(UPSINFO * ups)2421 static void print_eeprom_values(UPSINFO *ups)
2422 {
2423 	char locale, locale1, locale2;
2424 
2425 	pmsg("Doing prep_device() ...\n");
2426 	prep_device(ups);
2427 	if (!ups->UPS_Cap[CI_EPROM])
2428 		Error_abort("Your model does not support EPROM programming.\n");
2429 
2430 	if (ups->UPS_Cap[CI_REVNO])
2431 		locale1 = *(ups->firmrev + strlen(ups->firmrev) - 1);
2432 	else
2433 		locale1 = 0;
2434 
2435 	if (ups->UPS_Cap[CI_UPSMODEL])
2436 		locale2 = *(ups->upsmodel + strlen(ups->upsmodel) - 1);
2437 	else
2438 		locale2 = 0;
2439 
2440 	if (locale1 == locale2 && locale1 == 0)
2441 		Error_abort("Your model does not support EPROM programming.\n");
2442 
2443 	if (locale1 == locale2)
2444 		locale = locale1;
2445 
2446 	if (locale1 == 0)
2447 		locale = locale2;
2448 	else
2449 		locale = locale1;
2450 
2451 	parse_eeprom_cmds(ups->eprom, locale);
2452 	print_valid_eeprom_values(ups);
2453 }
2454 #endif
2455 
do_modbus_testing(void)2456 static void do_modbus_testing(void)
2457 {
2458 #ifdef HAVE_MODBUS_DRIVER
2459 	char *cmd;
2460 	int quit = FALSE;
2461 
2462 	pmsg("Hello, this is the apcctrl Cable Test program.\n"
2463 			"This part of apctest is for testing MODBUS UPSes.\n");
2464 
2465 	pmsg("\nGetting UPS capabilities...");
2466 	if (!ups->driver->get_capabilities())
2467 		pmsg("FAILED\nSome or all tests may not work!\n");
2468 	else
2469 		pmsg("SUCCESS\n");
2470 
2471 	pmsg("\nPlease select the function you want to perform.\n");
2472 
2473 	while (!quit) {
2474 		pmsg("\n"
2475 				"1)  Test kill UPS power\n"
2476 				"2)  Perform self-test\n"
2477 				"3)  Read last self-test result\n"
2478 				"4)  View/Change battery date\n"
2479 				"5)  View manufacturing date\n"
2480 				//"6)  View/Change alarm behavior\n"
2481 				//"7)  View/Change sensitivity\n"
2482 				//"8)  View/Change low transfer voltage\n"
2483 				//"9)  View/Change high transfer voltage\n"
2484 				"10) Perform battery calibration\n"
2485 				"11) Test alarm\n"
2486 				//"12) View/Change self-test interval\n"
2487 				" Q) Quit\n\n");
2488 
2489 		cmd = get_cmd("Select function number: ");
2490 		if (cmd) {
2491 			int item = atoi(cmd);
2492 
2493 			switch (item) {
2494 			case 1:
2495 				modbus_kill_power_test();
2496 				break;
2497 			case 2:
2498 				modbus_run_self_test();
2499 				break;
2500 			case 3:
2501 				modbus_get_self_test_result();
2502 				break;
2503 			case 4:
2504 				modbus_set_battery_date();
2505 				break;
2506 			case 5:
2507 				modbus_get_manf_date();
2508 				break;
2509 				//case 6:
2510 				//   modbus_set_alarm();
2511 				//   break;
2512 				//case 7:
2513 				//   modbus_set_sens();
2514 				//   break;
2515 				//case 8:
2516 				//   modbus_set_xferv(0);
2517 				//   break;
2518 				//case 9:
2519 				//   modbus_set_xferv(1);
2520 				//   break;
2521 			case 10:
2522 				modbus_calibration();
2523 				break;
2524 			case 11:
2525 				modbus_test_alarm();
2526 				break;
2527 				//case 12:
2528 				//   modbus_set_self_test_interval();
2529 				//   break;
2530 			default:
2531 				if (tolower(*cmd) == 'q')
2532 					quit = TRUE;
2533 				else
2534 					pmsg("Illegal response. Please enter 1-12,Q\n");
2535 				break;
2536 			}
2537 		} else {
2538 			pmsg("Illegal response. Please enter 1-12,Q\n");
2539 		}
2540 	}
2541 	ptime();
2542 	pmsg("End apctest.\n");
2543 #else
2544 	pmsg("MODBUS Driver not configured.\n");
2545 #endif
2546 }
2547 
2548 #ifdef HAVE_MODBUS_DRIVER
modbus_kill_power_test(void)2549 static void modbus_kill_power_test(void)
2550 {
2551 	pmsg("\nThis test will attempt to power down the UPS.\n"
2552 			"The MODBUS cable should be plugged in to the UPS, but the\n"
2553 			"AC power plug to the UPS should be DISCONNECTED.\n\n"
2554 			"PLEASE DO NOT RUN THIS TEST WITH A COMPUTER CONNECTED TO YOUR UPS!!!\n\n"
2555 			"Please enter any character when ready to continue: ");
2556 
2557 	fgetc(stdin);
2558 	pmsg("\n");
2559 
2560 	ptime();
2561 	pmsg("calling kill_power function.\n");
2562 
2563 	make_file(ups, ups->pwrfailpath);
2564 	initiate_hibernate(ups);
2565 	unlink(ups->pwrfailpath);
2566 
2567 	ptime();
2568 	pmsg("returned from kill_power function.\n");
2569 }
2570 
modbus_get_self_test_result(void)2571 static void modbus_get_self_test_result(void)
2572 {
2573 	uint64_t uint;
2574 
2575 	if (!modbus_read_int_from_ups(ups, REG_BATTERY_TEST_STATUS, &uint)) {
2576 		pmsg("\nI don't know how to run a self test on your UPS\n"
2577 				"or your UPS does not support self test.\n");
2578 		return;
2579 	}
2580 
2581 	pmsg("Result of last self test: ");
2582 	if (uint == 0)
2583 	{
2584 		pmsg("NO TEST PERFORMED\n");
2585 	}
2586 	else
2587 	{
2588 		if (uint & BTS_PENDING)
2589 			pmsg("PENDING");
2590 		else if (uint & BTS_IN_PROGRESS)
2591 			pmsg("IN PROGRESS");
2592 		else if (uint & BTS_PASSED)
2593 			pmsg("PASSED");
2594 		else if (uint & BTS_FAILED)
2595 			pmsg("FAILED");
2596 		else if (uint & BTS_REFUSED)
2597 			pmsg("REFUSED");
2598 		else if (uint & BTS_ABORTED)
2599 			pmsg("ABORTED");
2600 		else
2601 			pmsg("0x%llx", uint);
2602 
2603 		if (uint & BTS_INVALID_STATE)
2604 			pmsg(" (INVALID STATE)");
2605 		else if (uint & BTS_INTERNAL_FAULT)
2606 			pmsg(" (INTERNAL FAUILT)");
2607 		else if (uint & BTS_STATE_OF_CHARGE)
2608 			pmsg(" (STATE_OF_CHARGE)");
2609 
2610 		if (uint & BTS_SRC_PROTOCOL)
2611 			pmsg(" (PROTOCOL)");
2612 		else if (uint & BTS_SRC_LOCAL_UI)
2613 			pmsg(" (LOCAL UI)");
2614 		else if (uint & BTS_SRC_INTERNAL)
2615 			pmsg(" (INTERNAL)");
2616 
2617 		pmsg("\n");
2618 	}
2619 }
2620 
modbus_run_self_test(void)2621 static void modbus_run_self_test(void)
2622 {
2623 	uint64_t uint;
2624 	int timeout;
2625 
2626 	pmsg("\nThis test instructs the UPS to perform a self-test\n"
2627 			"operation and reports the result when the test completes.\n\n");
2628 
2629 	if (!modbus_read_int_from_ups(ups, REG_BATTERY_TEST_CMD, &uint)) {
2630 		pmsg("I don't know how to run a self test on your UPS\n"
2631 				"or your UPS does not support self test.\n");
2632 		return;
2633 	}
2634 
2635 	pmsg("Initiating self test...");
2636 	if (!modbus_write_int_to_ups(ups, REG_BATTERY_TEST_CMD, BTC_START_TEST)) {
2637 		pmsg("FAILED\n");
2638 		return;
2639 	}
2640 
2641 	pmsg("INITIATED\n");
2642 
2643 	pmsg("Waiting for test to complete...");
2644 
2645 	for (timeout = 0; timeout < 40; timeout++) {
2646 		if (!modbus_read_int_from_ups(ups, REG_BATTERY_TEST_STATUS, &uint)) {
2647 			pmsg("ERROR READING STATUS\n");
2648 			return;
2649 		}
2650 
2651 		if ((uint & (BTS_PENDING|BTS_IN_PROGRESS)) == 0) {
2652 			pmsg("COMPLETED\n");
2653 			break;
2654 		}
2655 
2656 		sleep(1);
2657 	}
2658 
2659 	if (timeout == 40) {
2660 		pmsg("TEST DID NOT COMPLETE\n");
2661 		return;
2662 	}
2663 
2664 	modbus_get_self_test_result();
2665 }
2666 
modbus_calibration()2667 static void modbus_calibration()
2668 {
2669 	uint64_t result;
2670 	bool aborted;
2671 	uint64_t ilastbl;
2672 
2673 	if (!ups->UPS_Cap[CI_ST_STAT] ||
2674 			!ups->UPS_Cap[CI_BATTLEV] ||
2675 			!ups->UPS_Cap[CI_LOAD]) {
2676 		pmsg("\nI don't know how to run a battery calibration on your UPS\n"
2677 				"or your UPS does not support battery calibration\n");
2678 		return;
2679 	}
2680 
2681 	pmsg("This test instructs the UPS to perform a battery calibration\n"
2682 			"operation and reports the result when the process completes.\n"
2683 			"The battery level must be at 100%% and the load must be above a\n"
2684 			"certain minimum to begin this test. If the conditions are not\n"
2685 			"met, the UPS will refuse the calibration attempt.\n");
2686 
2687 	pmsg("\nThe battery calibration should automatically end\n"
2688 			"when the battery level drops below about 25%%.\n"
2689 			"This process can take minutes or hours, depending on\n"
2690 			"the size of your UPS and the load attached.\n\n");
2691 
2692 	pmsg("Initiating battery calibration...");
2693 	if (!modbus_write_int_to_ups(ups, REG_CALIBRATION_CMD, CC_START_CALIBRATION)) {
2694 		pmsg("FAILED\n");
2695 		return;
2696 	}
2697 
2698 	pmsg("INITIATED\n\n");
2699 
2700 	pmsg("Waiting for calibration to complete...\n"
2701 			"To abort the calibration, press ENTER.\n");
2702 
2703 	ilastbl = 0;
2704 	while (1) {
2705 
2706 		if (!modbus_read_int_from_ups(ups, REG_CALIBRATION_STATUS, &result)) {
2707 			pmsg("\n\nError reading status; aborting calibration...");
2708 			if (!modbus_write_int_to_ups(ups, REG_CALIBRATION_CMD, CC_ABORT_CALIBRATION)) {
2709 				pmsg("FAILED\n");
2710 			}
2711 			else {
2712 				pmsg("ABORTED\n");
2713 			}
2714 			return;
2715 		}
2716 
2717 		if (!(result & (CS_PENDING|CS_IN_PROGRESS))) {
2718 			pmsg("\nCALIBRATION COMPLETED\n");
2719 			break;
2720 		}
2721 
2722 #ifndef HAVE_MINGW
2723 		fd_set rfds;
2724 		struct timeval tv;
2725 		FD_ZERO(&rfds);
2726 		FD_SET(STDIN_FILENO, &rfds);
2727 		tv.tv_sec = 10;
2728 		tv.tv_usec = 0;
2729 
2730 		aborted = select(STDIN_FILENO+1, &rfds, NULL, NULL, &tv) == 1;
2731 		if (aborted) {
2732 			while (fgetc(stdin) != '\n')
2733 				;
2734 		}
2735 #else
2736 		aborted = false;
2737 		for (int i = 0; i < 100 && !aborted; i++) {
2738 			while (kbhit() && !aborted)
2739 				aborted = getch() == '\r';
2740 			if (!aborted)
2741 			{
2742 				struct timespec ts;
2743 				ts.tv_sec = 0;
2744 				ts.tv_nsec = 100000000;
2745 				nanosleep(&ts, NULL);
2746 			}
2747 		}
2748 #endif
2749 
2750 		if (aborted) {
2751 			pmsg("\n\nUser input detected; aborting calibration...");
2752 			if (!modbus_write_int_to_ups(ups, REG_CALIBRATION_CMD, CC_ABORT_CALIBRATION)) {
2753 				pmsg("FAILED\n");
2754 			}
2755 			else {
2756 				pmsg("ABORTED\n");
2757 			}
2758 			return;
2759 		}
2760 
2761 		// Output the battery level
2762 		if (modbus_read_int_from_ups(ups, REG_STATE_OF_CHARGE_PCT, &result)) {
2763 			if (ilastbl == result)
2764 				pmsg(".");
2765 			else
2766 				pmsg("\nBattery level: %d%%", result);
2767 			ilastbl = result;
2768 		}
2769 		else
2770 			pmsg(".");
2771 	}
2772 
2773 	pmsg("Calibration result: ");
2774 	if (result & CS_PASSED)
2775 		pmsg("PASSED");
2776 	else if (result & CS_FAILED)
2777 		pmsg("FAILED");
2778 	else if (result & CS_REFUSED)
2779 		pmsg("REFUSED");
2780 	else if (result & CS_ABORTED)
2781 		pmsg("ABORTED");
2782 	else
2783 		pmsg("0x%llx", result);
2784 
2785 	if (result & CS_SRC_PROTOCOL)
2786 		pmsg(" (PROTOCOL)");
2787 	else if (result & CS_SRC_LOCAL_UI)
2788 		pmsg(" (LOCAL UI)");
2789 	else if (result & CS_SRC_INTERNAL)
2790 		pmsg(" (INTERNAL)");
2791 
2792 	if (result & CS_INVALID_STATE)
2793 		pmsg(" (INVALID STATE)");
2794 	else if (result & CS_INTERNAL_FAULT)
2795 		pmsg(" (INTERNAL FAULT)");
2796 	else if (result & CS_STATE_OF_CHARGE)
2797 		pmsg(" (STATE OF CHARGE)");
2798 	else if (result & CS_LOAD_CHANGE)
2799 		pmsg(" (LOAD CHANGE)");
2800 	else if (result & CS_AC_INPUT_BAD)
2801 		pmsg(" (AC INPUT BAD)");
2802 	else if (result & CS_LOAD_TOO_LOW)
2803 		pmsg(" (LOAD TOO LOW)");
2804 	else if (result & CS_OVER_CHARGE)
2805 		pmsg(" (OVER CHARGE)");
2806 
2807 	pmsg("\n");
2808 }
2809 
modbus_uint_to_date(uint64_t uint)2810 static struct tm modbus_uint_to_date(uint64_t uint)
2811 {
2812 	// uint is in days since 1/1/2000
2813 	time_t date = ModbusRegTotime_t(uint);
2814 	struct tm ret;
2815 	gmtime_r(&date, &ret);
2816 	return ret;
2817 }
2818 
modbus_get_manf_date(void)2819 static void modbus_get_manf_date(void)
2820 {
2821 	uint64_t result;
2822 
2823 	if (!modbus_read_int_from_ups(ups, REG_MANUFACTURE_DATE, &result)) {
2824 		pmsg("\nI don't know how to access the manufacturing date on your UPS\n"
2825 				"or your UPS does not support the manufacturing date feature.\n");
2826 		return;
2827 	}
2828 
2829 	struct tm date = modbus_uint_to_date(result);
2830 	char str[11];
2831 	strftime(str, sizeof(str), "%m/%d/%Y", &date);
2832 	pmsg("Manufacturing date: %s\n", str);
2833 }
2834 
modbus_test_alarm(void)2835 static void modbus_test_alarm(void)
2836 {
2837 	uint64_t result;
2838 
2839 	if (!modbus_read_int_from_ups(ups, REG_USER_INTERFACE_CMD, &result))
2840 	{
2841 		pmsg("\nI don't know how to test the alarm on your UPS.\n");
2842 		return;
2843 	}
2844 
2845 	// Write to UPS
2846 	pmsg("Testing alarm...");
2847 	modbus_write_int_to_ups(ups, REG_USER_INTERFACE_CMD, UIC_SHORT_TEST);
2848 
2849 	// Wait for complete
2850 	while (modbus_read_int_from_ups(ups, REG_USER_INTERFACE_CMD, &result) &&
2851 			(result & UIS_TEST_IN_PROGRESS))
2852 	{
2853 		sleep(1);
2854 	}
2855 
2856 	pmsg("COMPLETE\n");
2857 }
2858 
modbus_get_battery_date(void)2859 static uint64_t modbus_get_battery_date(void)
2860 {
2861 	uint64_t result;
2862 
2863 	if (!modbus_read_int_from_ups(ups, REG_BATTERY_DATE_SETTING, &result)) {
2864 		pmsg("\nI don't know how to access the battery date on your UPS\n"
2865 				"or your UPS does not support the battery date feature.\n");
2866 		return 0;
2867 	}
2868 
2869 	struct tm date = modbus_uint_to_date(result);
2870 	char str[11];
2871 	strftime(str, sizeof(str), "%m/%d/%Y", &date);
2872 	pmsg("Current battery date: %s\n", str);
2873 	return result;
2874 }
2875 
modbus_set_battery_date(void)2876 static void modbus_set_battery_date(void)
2877 {
2878 	char *cmd;
2879 	int day, month, year, max;
2880 	uint64_t result, temp;
2881 
2882 	if (!(result = modbus_get_battery_date()))
2883 		return;
2884 
2885 	cmd = get_cmd("Enter new battery date (MM/DD/YYYY), blank to quit: ");
2886 	if (!isdigit(cmd[0]) || !isdigit(cmd[1]) || cmd[2] != '/' ||
2887 			!isdigit(cmd[3]) || !isdigit(cmd[4]) || cmd[5] != '/' ||
2888 			!isdigit(cmd[6]) || !isdigit(cmd[7]) || !isdigit(cmd[8]) ||
2889 			!isdigit(cmd[9]) || cmd[10] != '\0' ||
2890 			((month = strtoul(cmd, NULL, 10)) > 12) || (month < 1) ||
2891 			((day = strtoul(cmd + 3, NULL, 10)) > 31) || (day < 1) ||
2892 			((year = strtoul(cmd + 6, NULL, 10)) < 1980)) {
2893 		pmsg("Invalid format.\n");
2894 		return;
2895 	}
2896 
2897 	struct tm new_date_tm = {0};
2898 	new_date_tm.tm_mon = month - 1;
2899 	new_date_tm.tm_year = year - 1900;
2900 	new_date_tm.tm_mday = day;
2901 	new_date_tm.tm_isdst = -1;
2902 	time_t new_date = mktime(&new_date_tm);
2903 	if (new_date < MODBUS_BASE_TIMESTAMP)
2904 	{
2905 		pmsg("Invalid date; Must be 1/1/2000 or later\n");
2906 		return;
2907 	}
2908 
2909 	result = time_tToModbusReg(new_date);
2910 
2911 	pmsg("Writing new date...");
2912 	if (!modbus_write_int_to_ups(ups, REG_BATTERY_DATE_SETTING, result)) {
2913 		pmsg("FAILED\n");
2914 		return;
2915 	}
2916 
2917 	pmsg("SUCCESS\n");
2918 
2919 	pmsg("Waiting for change to take effect...");
2920 	for (max = 0; max < 10; max++) {
2921 		if (!modbus_read_int_from_ups(ups, REG_BATTERY_DATE_SETTING, &temp)) {
2922 			pmsg("ERROR\n");
2923 			return;
2924 		}
2925 
2926 		if (temp == result)
2927 			break;
2928 
2929 		sleep(1);
2930 	}
2931 
2932 	if (max == 10)
2933 		pmsg("TIMEOUT\n");
2934 	else
2935 		pmsg("SUCCESS\n");
2936 
2937 	modbus_get_battery_date();
2938 }
2939 
2940 #endif
2941 
2942 #ifdef HAVE_BRAZIL_DRIVER
2943 
2944 /* MODBUS driver functions */
2945 #include "drivers/brazil/brazildriver.h"
2946 
2947 //#define getEvents(events,ups) ((BrazilUpsDriver*)((ups)->driver->getEvents(events)))
2948 
2949 ///* Our own test functions */
2950 static void brazil_print();
2951 static void brazil_last();
2952 static void brazil_turnoff();
2953 static void brazil_turnon();
2954 static void brazil_getSchedule();
2955 static void brazil_setSchedule();
2956 static void brazil_cancelSchedule();
2957 static void brazil_testBatteryHealth();
2958 static void brazil_testTimeLeftRoutines();
2959 
2960 #endif
2961 
brazil_print()2962 static void brazil_print(){
2963 	((BrazilUpsDriver*)(ups)->driver)->refresh();
2964 
2965 	pmsg("MODEL NAME:             %s\n",((BrazilUpsDriver*)(ups)->driver)->model->getModelName());
2966 	pmsg("LINE VOLTAGE:           %03.1f V\n",((BrazilUpsDriver*)(ups)->driver)->model->getLineVoltage());
2967 	pmsg("LINE NOMINAL:           %03d V\n",((BrazilUpsDriver*)(ups)->driver)->model->getLineVoltageNom());
2968 	pmsg("LINE FREQUENCY:         %03.1f Hz\n",((BrazilUpsDriver*)(ups)->driver)->model->getLineFrequency());
2969 	pmsg("LOAD ACT POWER:         %02.1f \%\n",((BrazilUpsDriver*)(ups)->driver)->model->getLoadActivePowerPercent());
2970 	pmsg("LOAD TOTAL POWER:       %02.1f \%\n",((BrazilUpsDriver*)(ups)->driver)->model->getLoadPowerPercent());
2971 	pmsg("OUT VOLTAGE:            %03.1f V\n",((BrazilUpsDriver*)(ups)->driver)->model->getOutputVoltage());
2972 	pmsg("OUT VOLTAGE NOM:        %03d V\n",((BrazilUpsDriver*)(ups)->driver)->model->getOutputVoltageNom());
2973 	pmsg("OUT CURRENT:            %02.1f A\n",((BrazilUpsDriver*)(ups)->driver)->model->getOutputCurrent());
2974 	pmsg("OUT ACT POWER:          %04.1f W\n",((BrazilUpsDriver*)(ups)->driver)->model->getOutputActivePower());
2975 	pmsg("OUT ACT POWER NOM:      %04.1f W\n",((BrazilUpsDriver*)(ups)->driver)->model->getOutputActivePowerNom());
2976 	pmsg("OUT TOTAL POWER:        %04.1f VA\n",((BrazilUpsDriver*)(ups)->driver)->model->getOutputPower());
2977 	pmsg("OUT TOTAL POWER NOM:    %04.1f VA\n",((BrazilUpsDriver*)(ups)->driver)->model->getOutputPowerNom());
2978 	pmsg("BATTERY VOLTAGE:        %02.1f V\n",((BrazilUpsDriver*)(ups)->driver)->model->getBatteryVoltage());
2979 	pmsg("BATTERY VOLTAGE NOM:    %02.1f V\n",((BrazilUpsDriver*)(ups)->driver)->model->bat->getBatteryVoltageNom());
2980 	pmsg("BATTERY CURRENT NOM:    %02.1f A\n",((BrazilUpsDriver*)(ups)->driver)->model->bat->getBatteryCurrentNom());
2981 	pmsg("TEMPERATURE:            %02.1f oC\n",((BrazilUpsDriver*)(ups)->driver)->model->getTemperature());
2982 	pmsg("TIMELEFT ESTIMATE:      %02.1f minutes\n",((BrazilUpsDriver*)(ups)->driver)->model->getBatteryTimeLeft());
2983 	pmsg("FLAG LINE 220V:         %s\n",((BrazilUpsDriver*)(ups)->driver)->model->isLine220V()?"true":"false");
2984 	pmsg("FLAG BATTERY CHARGING:  %s\n",((BrazilUpsDriver*)(ups)->driver)->model->isCharging()?"true":"false");
2985 	pmsg("FLAG BATTERY CRITICAL:  %s\n",((BrazilUpsDriver*)(ups)->driver)->model->isBatteryCritical()?"true":"false");
2986 	pmsg("FLAG LINE ON:           %s\n",((BrazilUpsDriver*)(ups)->driver)->model->isLineMode()?"true":"false");
2987 	pmsg("FLAG OUT ON:            %s\n",((BrazilUpsDriver*)(ups)->driver)->model->isOutputOn()?"true":"false");
2988 	pmsg("FLAG OVERLOAD:          %s\n",((BrazilUpsDriver*)(ups)->driver)->model->isOverload()?"true":"false");
2989 }
brazil_last()2990 static void brazil_last(){
2991 	char *events = 0;
2992 	((BrazilUpsDriver*)(ups)->driver)->getEventsStr(&events);
2993 	pmsg(events);
2994 }
brazil_turnoff()2995 static void brazil_turnoff(){
2996 	((BrazilUpsDriver*)(ups)->driver)->turnLineOn(false);
2997 }
brazil_turnon()2998 static void brazil_turnon(){
2999 	((BrazilUpsDriver*)(ups)->driver)->turnLineOn(true);
3000 }
brazil_getSchedule()3001 static void brazil_getSchedule(){
3002 	((BrazilUpsDriver*)(ups)->driver)->refresh();
3003 
3004 	char str[1000];
3005 	unsigned int strpos = 0;
3006 	char dayofweek[10];
3007 	switch(((BrazilUpsDriver*)(ups)->driver)->model->getDayOfWeek()){
3008 	case 0:
3009 		sprintf(dayofweek, "Sunday");
3010 		break;
3011 	case 1:
3012 		sprintf(dayofweek, "Monday");
3013 		break;
3014 	case 2:
3015 		sprintf(dayofweek, "Tuesday");
3016 		break;
3017 	case 3:
3018 		sprintf(dayofweek, "Wednesday");
3019 		break;
3020 	case 4:
3021 		sprintf(dayofweek, "Thursday");
3022 		break;
3023 	case 5:
3024 		sprintf(dayofweek, "Friday");
3025 		break;
3026 	case 6:
3027 		sprintf(dayofweek, "Saturday");
3028 		break;
3029 	default:
3030 		sprintf(dayofweek, "Unknown");
3031 		break;
3032 	}
3033 
3034 	strpos += sprintf(str+strpos, "UPS time: %s, %02d:%02d:%02d %02d/%02d/%02d UTC\n",\
3035 			dayofweek,\
3036 			((BrazilUpsDriver*)(ups)->driver)->model->getHour(),\
3037 			((BrazilUpsDriver*)(ups)->driver)->model->getMinute(),\
3038 			((BrazilUpsDriver*)(ups)->driver)->model->getSecond(),\
3039 			((BrazilUpsDriver*)(ups)->driver)->model->getMonth(),\
3040 			((BrazilUpsDriver*)(ups)->driver)->model->getDayOfMonth(),\
3041 			((BrazilUpsDriver*)(ups)->driver)->model->getYear()\
3042 	);
3043 
3044 	strpos += sprintf(str+strpos, "Hour to turn off: %02d:%02d:00\n",\
3045 			((BrazilUpsDriver*)(ups)->driver)->model->getTurnOffHour(),\
3046 			((BrazilUpsDriver*)(ups)->driver)->model->getTurnOffMinute());
3047 
3048 	strpos += sprintf(str+strpos, "Hour to turn on: %02d:%02d:00\n",\
3049 			((BrazilUpsDriver*)(ups)->driver)->model->getTurnOnHour(),\
3050 			((BrazilUpsDriver*)(ups)->driver)->model->getTurnOnMinute());
3051 
3052 	strpos += sprintf(str+strpos,"Day of week to execute: \n"
3053 			"		Sunday:    %s\n"
3054 			"		Monday:    %s\n"
3055 			"		Tuesday:   %s\n"
3056 			"		Wednesday: %s\n"
3057 			"		Thursday:  %s\n"
3058 			"		Friday:    %s\n"
3059 			"		Saturday:  %s\n",\
3060 			(((BrazilUpsDriver*)(ups)->driver)->model->isScheduleSetDayOfWeek(Sunday) ? "true" : "false"),\
3061 			(((BrazilUpsDriver*)(ups)->driver)->model->isScheduleSetDayOfWeek(Monday) ? "true" : "false"),\
3062 			(((BrazilUpsDriver*)(ups)->driver)->model->isScheduleSetDayOfWeek(Tuesday) ? "true" : "false"),\
3063 			(((BrazilUpsDriver*)(ups)->driver)->model->isScheduleSetDayOfWeek(Wednesday) ? "true" : "false"),\
3064 			(((BrazilUpsDriver*)(ups)->driver)->model->isScheduleSetDayOfWeek(Thursday) ? "true" : "false"),\
3065 			(((BrazilUpsDriver*)(ups)->driver)->model->isScheduleSetDayOfWeek(Friday) ? "true" : "false"),\
3066 			(((BrazilUpsDriver*)(ups)->driver)->model->isScheduleSetDayOfWeek(Saturday) ? "true" : "false")\
3067 	);
3068 	strpos += sprintf(str+strpos,"\n");
3069 
3070 	pmsg(str);
3071 
3072 }
brazil_setSchedule()3073 static void brazil_setSchedule(){
3074 	char *cmd;
3075 	pmsg("\n"
3076 			"Atenção!!! Esse teste causará o desligamento do nobreak em 1 minuto e será\n"
3077 			"religado após 1 minuto. Você deseja continuar?\n\n"
3078 			"Y) Yes\n"
3079 			"N) No\n\n");
3080 
3081 	cmd = get_cmd("Select the option: ");
3082 	if (tolower(*cmd) == 'y'){
3083 		(((BrazilUpsDriver*)(ups)->driver))->programmation(true,1,true,2);
3084 	}
3085 }
brazil_cancelSchedule()3086 static void brazil_cancelSchedule(){
3087 	(((BrazilUpsDriver*)(ups)->driver))->programmation(false,0,false,0);
3088 }
brazil_testBatteryHealth()3089 static void brazil_testBatteryHealth(){
3090 	BrazilModelAbstract *br = ((BrazilUpsDriver*)(ups)->driver)->model;
3091 
3092 	double batlevel_limit = 40;
3093 	int start_delay_offbat = 5;
3094 	int start_delay_onbat = 10;
3095 
3096 	char *cmd;
3097 	pmsg("\n"
3098 			"Atenção!!! Este teste desconectará seu nobreak da rede elétrica e será\n"
3099 			"executado até que o nível da bateria atinja %2.0f%%. Você quer continuar?\n\n"
3100 			"Y) Yes\n"
3101 			"N) No\n\n",batlevel_limit);
3102 
3103 	cmd = get_cmd("Select the option: ");
3104 	if (tolower(*cmd) != 'y'){
3105 		return;
3106 	}
3107 	if(br->isCharging()){
3108 		pmsg("\n"
3109 				"Atenção!!! A bateria está sendo carregada. Este teste foi projetado para ser\n"
3110 				"usado com a bateria carregada. Você quer continuar?\n\n"
3111 				"C) Continue\n"
3112 				"N) No\n\n");
3113 
3114 		cmd = get_cmd("Select the option: ");
3115 		if (tolower(*cmd) != 'c'){
3116 			return;
3117 		}
3118 	}
3119 	pmsg("\n"
3120 			"Atenção!!! Você não deve variar a carga no nobreak durante o teste. Você quer\n"
3121 			"continuar?\n\n"
3122 			"Y) Yes\n"
3123 			"N) No\n\n");
3124 
3125 	cmd = get_cmd("Select the option: ");
3126 	if (tolower(*cmd) != 'y'){
3127 		return;
3128 	}
3129 
3130 	if(! br->isLineMode()){
3131 		pmsg("\n"
3132 				"Atenção!!! Seu nobreak não está conectado na rede elétrica. Faça a correção e\n"
3133 				"tente novamente.\n\n");
3134 		return;
3135 	}
3136 
3137 	double timeleft0, timeleft1, bat0, bat1, power_peukert, power0, power1, batload_peukert, batload0, batload1, bat_expected, timeleft_peukert, timeleft_diff, timeleft_rate, timeleft_rate_peukert, seconds;
3138 	bool batcritical = false;
3139 
3140 	pmsg("1) Iniciando teste!\n");
3141 	pmsg("2) Aguarde %d segundos para estabilizar o nobreak.",start_delay_offbat);
3142 	for(int i=0 ; i<start_delay_offbat ; i++){ // necessário para estabilizar a tensão da bateria de início.
3143 		((BrazilUpsDriver*)(ups)->driver)->refresh();
3144 		pmsg(".");
3145 	}
3146 	pmsg("\n");
3147 
3148 	bat_expected = br->getBatteryVoltageExpectedInitial();
3149 	timeleft_peukert = br->getBatteryTimeLeft();
3150 	batload_peukert = br->getBatteryLoad();
3151 	power_peukert = br->getOutputActivePower();
3152 
3153 	pmsg("3) Enviando comando para desligar a entrada.\n");
3154 	pmsg("4) Aguarde %d segundos para atualizar a tensão da bateria.",start_delay_onbat);
3155 	((BrazilUpsDriver*)(ups)->driver)->turnLineOn(false);
3156 
3157 	if(br->isLineMode()){
3158 		pmsg("\n5) ERRO! O nobreak não desligou a entrada de energia\n\n");
3159 		return;
3160 	}
3161 
3162 	for(int i=0 ; i<start_delay_onbat ; i++){ // necessário para estabilizar a tensão da bateria de início.
3163 		((BrazilUpsDriver*)(ups)->driver)->refresh();
3164 		pmsg(".");
3165 	}
3166 	pmsg("\n");
3167 
3168 	time_t start,end,now;
3169 	struct tm tm_start,tm_end,tm_now;
3170 	time(&start);
3171 	gmtime_r(&start, &tm_start);
3172 
3173 	char csv_filename[120];
3174 	sprintf(csv_filename,"apctest.battery.%04d-%02d-%02d-%02d-%02d-%02d.csv",(tm_start.tm_year+1900),tm_start.tm_mon+1,tm_start.tm_mday,tm_start.tm_hour,tm_start.tm_min,tm_start.tm_sec);
3175 	FILE *CsvFile=fopen (csv_filename,"w");
3176 	setvbuf (CsvFile , NULL , _IOFBF , 65535 );
3177 
3178 	batload0 = br->getBatteryLoad();
3179 	timeleft0 = br->getBatteryTimeLeft();
3180 	bat0 = timeleft1 = br->getBatteryVoltage();
3181 	power0 = br->getOutputActivePower();
3182 
3183 	fprintf(CsvFile,"\"Potência ativa no início do teste (W):\",%4.1f\n",br->getOutputActivePower());
3184 	fprintf(CsvFile,"\"Potência total no início do teste (VA):\",%4.1f\n",br->getOutputPower());
3185 	fprintf(CsvFile,"\"Fator de descarga da bateria no início do teste (C):\",%1.2f,\"O fator de descarga deve-se permanecer o mais constante possível.\"\n",batload0);
3186 	fprintf(CsvFile,"\"Tesão esperada no início do teste (V):\",%2.2f\n",bat_expected);
3187 	fprintf(CsvFile,"\"Tempo restante calculado pela formula de Peukert (minutos):\",%2.2f\n",timeleft_peukert);
3188 
3189 	pmsg("5) Iniciando a monitoração do nível carga da bateria.\n");
3190 
3191 	int duration_s;
3192 	char datetime[50];
3193 	fprintf(CsvFile,"\n");
3194 	fprintf(CsvFile,"\"Data\",\"tempo em teste(s)\",\"Potência ativa de saida (W)\",\"Fator de descarga da bateria (C)\",\"Tensão da bateria (V)\",\"Expectativa do tempo restante (minutos)\",\"Fator de descarga(C)\"\n");
3195 	do{
3196 		((BrazilUpsDriver*)(ups)->driver)->refresh();
3197 		time(&now);
3198 		gmtime_r(&now, &tm_now);
3199 		duration_s = floor(now - start);
3200 		sprintf(datetime,"%04d-%02d-%02d %02d:%02d:%02d",(tm_now.tm_year+1900),tm_now.tm_mon+1,tm_now.tm_mday,tm_now.tm_hour,tm_now.tm_min,tm_now.tm_sec);
3201 		fprintf(CsvFile,"\"%s\",\"%d\",\"%4.0f\",\"%1.2f\",\"%2.2f\",\"%2.1f\",\"%1.2f\"\n",
3202 				datetime,
3203 				duration_s,
3204 				br->getOutputActivePower(),
3205 				br->getBatteryLoad(),
3206 				br->getBatteryVoltage(),
3207 				br->getBatteryTimeLeft(),
3208 				br->getBatteryLoad());
3209 
3210 		pmsg("  %04d; Carga: %02.1f%%; Tensão: %2.2fV; Out Power: %4.1fW; Autonomia: %2.1f min\n",duration_s,br->getBatteryLevel(),br->getBatteryVoltage(),br->getOutputActivePower(),br->getBatteryTimeLeft());
3211 	}while((! br->isLineMode()) && (br->getBatteryLevel() > batlevel_limit) && (! br->isBatteryCritical()));
3212 	fflush(CsvFile);
3213 	fclose(CsvFile);
3214 
3215 	if(br->isLineMode()){
3216 		pmsg("\n6) ERRO! A entrada do nobreak foi religada inadvertidamente\n\n");
3217 		return;
3218 	}
3219 	else{
3220 		pmsg("6) Fim do teste! Ligando a entrada do nobreak.\n");
3221 	}
3222 
3223 	time(&end);
3224 	gmtime_r(&end, &tm_end);
3225 	seconds = end - start;
3226 
3227 	if(br->isBatteryCritical()){
3228 		batcritical = true;
3229 	}
3230 	timeleft1 = br->getBatteryTimeLeft();
3231 	bat1 = br->getBatteryVoltage();
3232 	power1 = br->getOutputActivePower();
3233 	batload1 = br->getBatteryLoad();
3234 
3235 	timeleft_diff = fabs(timeleft0 - timeleft1);
3236 	if(seconds == 0) seconds = 0.1;
3237 	timeleft_rate = timeleft_diff / (seconds / 60);
3238 
3239 	if(timeleft_peukert == 0) timeleft_peukert = 0.1;
3240 	timeleft_rate_peukert = timeleft0 / timeleft_peukert;
3241 
3242 	((BrazilUpsDriver*)(ups)->driver)->turnLineOn(true);
3243 
3244 	pmsg("7) Resultados:\n");
3245 	pmsg("7.1) Datas de início e fim do teste:\n");
3246 	pmsg("  Datetime no início:     datetime0        = %04d-%02d-%02d %02d:%02d:%02d UTC\n",1900+tm_start.tm_year,tm_start.tm_mon,tm_start.tm_mday,tm_start.tm_hour,tm_start.tm_min,tm_start.tm_sec);
3247 	pmsg("  Datatime no fim:        datetime1        = %04d-%02d-%02d %02d:%02d:%02d UTC\n",1900+tm_end.tm_year,tm_end.tm_mon,tm_end.tm_mday,tm_end.tm_hour,tm_end.tm_min,tm_end.tm_sec);
3248 	pmsg("7.2) Expectativa antes do início (em função apenas da carga):\n");
3249 	pmsg("  Tensão da bateria:      batvolt_peukert  = %03.2f V\n",bat_expected);
3250 	pmsg("  Autonomia teórica:      timeleft_peukert = %02.2f minutos\n",timeleft_peukert);
3251 	pmsg("  Potência na saída:      power_peukert    = %03.2f W\n",power_peukert);
3252 	pmsg("  Fator de descarga:      batload_peukert  = %01.2f C\n",batload_peukert);
3253 	pmsg("7.3) No início do teste:\n");
3254 	pmsg("  Tensão da bateria:      batvolt0         = %03.2f V\n",bat0);
3255 	pmsg("  Autonomia esperada:     timeleft0        = %02.2f minutos\n",timeleft0);
3256 	pmsg("  Potência na saída:      power0           = %03.2f W\n",power0);
3257 	pmsg("  Fator de descarga:      batload0         = %01.2f C\n",batload0);
3258 	pmsg("7.4) No fim do teste:\n");
3259 	pmsg("  Alarme nível crítico:   batcritical      = %s\n",(batcritical ? "SIM!" : "Não!"));
3260 	pmsg("  Tensão da bateria:      batvolt1         = %03.2f V\n",bat1);
3261 	pmsg("  Autonomia esperada:     timeleft1        = %03.2f minutos\n",timeleft1);
3262 	pmsg("  Potência na saída:      power1           = %03.2f W\n",power1);
3263 	pmsg("  Fator de descarga:      batload1         = %01.2f C\n",batload1);
3264 	pmsg("7.5) Análise dos resultados quando conectado na rede elétrica (Peukert):\n");
3265 	pmsg("  Autonomia na bateria:   timeleft0        = %02.2f minutos\n",timeleft0);
3266 	pmsg("  Autonomia teórica:      timeleft_peukert = %02.2f minutos\n",timeleft_peukert);
3267 	pmsg("  Razão entre autonomias: timeleft_rate    = %01.2f = timeleft0 / timeleft_peukert\n",timeleft_rate_peukert);
3268 	if((timeleft_rate_peukert < 0.8) || (timeleft_rate_peukert > 1.25)){
3269 		pmsg("  Conclusão parcial:\n");
3270 		pmsg("    Falhou! A razão entre a autonomia medida inicial e a calculada pela formulá\n");
3271 		pmsg("    de Peukert está com uma variação maior que 25%%. Isso pode ter sido\n");
3272 		pmsg("    ocasionado caso as baterias não estivessem em plena carga já que mesmo o\n");
3273 		pmsg("    nobreak indicandoque as baterias não estão sendo carregadas diretamente, a\n");
3274 		pmsg("    forma de operação delas após a carga inicial é conhecida por flutuação.\n");
3275 		pmsg("    Nessa condição ela está sendo constantemente carregada de forma lenta.\n");
3276 		pmsg("    Para confirmar esse resultado repita esse teste dentro de dois dias. Dessa\n");
3277 		pmsg("    forma você poderá confirmar esse resultado. Esse pode ser um indicativo que\n");
3278 		pmsg("    suas baterias estão entrando no fim da vida útil. Lembrando que são\n");
3279 		pmsg("    cálculos teóricos e a decisão final cabe a sua avaliação.\n");
3280 	}else{
3281 		pmsg("  Conclusão parcial:\n");
3282 		pmsg("    SUCESSO! A razão entre a autonomia medida inicial e a calculada pela forma\n");
3283 		pmsg("    está dentro de uma variação menor que 25%%. Provavelmente suas baterias\n");
3284 		pmsg("    estavam em plena carga já que mesmo quando o nobreak indica que as baterias\n");
3285 		pmsg("    não estão sendo carregadas, a forma de operação delas após a carga inicial é\n");
3286 		pmsg("    conhecida por flutuação. Nessa condição ela está sendo constantemente\n");
3287 		pmsg("    carregada de forma lenta.\n");
3288 	}
3289 	pmsg("7.6) Análise dos resultados quando operando com as baterias:\n");
3290 	if(batcritical){
3291 		pmsg("  Nível crítico:\n");
3292 		pmsg("    ATENÇÃO!!! O nobreak informou que as baterias atingiram um nível crítico! É\n");
3293 		pmsg("    muito provável que seja necessário trocar as baterias. Nessa condição o\n");
3294 		pmsg("    nobreak se auto desligará em breve.\n");
3295 	}else{
3296 		pmsg("  Nível crítico:\n");
3297 		pmsg("    O nobreak não informou que as baterias chegaram em um nível crítico até o\n");
3298 		pmsg("    fim do teste. Caso isso tivesse ocorrido, muito provavelmente, seria\n");
3299 		pmsg("    necessário a troca imediata das baterias.\n");
3300 	}
3301 	pmsg("  Autonomia inicial:      tl0              = %02.1f minutos\n",timeleft0);
3302 	pmsg("  Autonomia final:        tl1              = %02.1f minutos\n",timeleft1);
3303 	pmsg("  Duração estimada:       tl_diff          = %02.1f minutos = tl0 - tl1\n",timeleft_diff);
3304 	pmsg("  Duração medida:         dt_diff          = %02.1f minutos = dt1 - dt0\n",seconds/60);
3305 	pmsg("  Razão entre durações:   rate             = %01.2f = tl_diff / dt_diff\n",timeleft_rate);
3306 
3307 	if(timeleft_rate >= 0.8 && timeleft_rate <= 1.25){
3308 		pmsg("  Conclusão parcial:\n");
3309 		pmsg("    SUCESSO! O erro entre a duração estimada (timeleft_diff) e a duração medida\n");
3310 		pmsg("    (datetime_diff) foi menor que 25%%. As baterias parecem estar em boas\n");
3311 		pmsg("    condições ou pode ser necessário revisar as configurações ou as rotinas de\n");
3312 		pmsg("    cálculo.\n");
3313 	}
3314 	if(timeleft_rate > 1.25){
3315 		pmsg("  Conclusão parcial:\n");
3316 		pmsg("    FALHOU! O erro entre a duração estimada (timeleft_diff) e a duração medida\n");
3317 		pmsg("    (datetime_diff) foi maior que 25%%. A duração do teste foi muito menor que\n");
3318 		pmsg("    a duração esperada. Isso indica que as baterias estão descarregando mais\n");
3319 		pmsg("    rapidamente que o esperado. Possivelmente é necessário trocar as baterias.\n");
3320 		pmsg("    Esse resultado também pode ser decorrente de erros nas rotinas de cáculo de\n");
3321 		pmsg("    autonomia. Recomenda-se repitir o teste após alguns dias para garantir que\n");
3322 		pmsg("    as baterias estejam realmente carregadas.\n");
3323 	}else{
3324 		pmsg("  Conclusão parcial:\n");
3325 		pmsg("    FALHOU! O erro entre a duração estimada (timeleft_diff) e a duração medida\n");
3326 		pmsg("    (datetime_diff) foi maior que 25%%. A duração do teste foi muito maior que\n");
3327 		pmsg("    a duração esperada. Isso indica que as baterias estão descarregando\n");
3328 		pmsg("    lentamente, o que é um bom sinal, mas também pode ser decorrentes de uma\n");
3329 		pmsg("    configuração equivocada do software ou erros nas rotinas de cálculo de\n");
3330 		pmsg("    autonomia. Recomenda-se repitir o teste após alguns dias para garantir que\n");
3331 		pmsg("    as baterias estejam realmente carregadas.\n");
3332 	}
3333 	pmsg("8) Dados extras no arquivo: %s\n",csv_filename);
3334 	pmsg("9) Fim do teste!\n\n");
3335 }
brazil_testTimeLeftRoutines()3336 static void brazil_testTimeLeftRoutines(){
3337 	pmsg("1) Iniciando teste das rotinas de calculo de autonomia!\n");
3338 
3339 	time_t start;
3340 	struct tm tm_start;
3341 
3342 	time(&start);
3343 	gmtime_r(&start, &tm_start);
3344 
3345 	char csv_filename[120];
3346 	sprintf(csv_filename,"apctest.TimeLeftRoutines.%04d-%02d-%02d-%02d-%02d-%02d.csv",(tm_start.tm_year+1900),tm_start.tm_mon+1,tm_start.tm_mday,tm_start.tm_hour,tm_start.tm_min,tm_start.tm_sec);
3347 	FILE *CsvFile=fopen (csv_filename,"w");
3348 	setvbuf (CsvFile , NULL , _IOFBF , 65535 );
3349 
3350 	BrazilModelAbstract *br = ((BrazilUpsDriver*)(ups)->driver)->model;
3351 
3352 	fprintf(CsvFile,"\"### INFORMAÇÕES BÁSICAS ###\"\n");
3353 	fprintf(CsvFile,"\"Modelo:\",\"%s\"\n",br->getModelName());
3354 	fprintf(CsvFile,"\"Tensão de entrada nominal:\",\"%3.0d V\"\n",br->getLineVoltageNom());
3355 	fprintf(CsvFile,"\"Tensão de saída nominal:\",\"%3.0d V\"\n",br->getOutputVoltageNom());
3356 	fprintf(CsvFile,"\"Potência de saída real (ativa) nominal:\",\"%2.0f W\"\n",br->getOutputActivePowerNom());
3357 	fprintf(CsvFile,"\"Potência de saída total nominal:\",\"%4.0f VA\"\n",br->getOutputPowerNom());
3358 	fprintf(CsvFile,"\"Tensão nominal do banco de baterias:\",\"%2.1f V\"\n",br->bat->getBatteryVoltageNom());
3359 	fprintf(CsvFile,"\"Corrente nominal do banco de baterias:\",\"%2.1f A\"\n",br->bat->getBatteryCurrentNom());
3360 	fprintf(CsvFile,"\"Corrente nominal do banco de baterias em C1:\",\"%2.1f A\"\n",br->bat->getBatteryCurrentC1Nom());
3361 	fprintf(CsvFile,"\"Potência real do banco de baterias em C1:\",\"%4.1f W\"\n",br->bat->getBatteryPowerC1Nom());
3362 	fprintf(CsvFile,"\n");
3363 	fprintf(CsvFile,"\"### DADOS INICIAIS TEÓRICOS\"\n");
3364 	fprintf(CsvFile,"\"Os dados à seguir são funções unicamente da corrente requerida da bateria (A).\"\n");
3365 	fprintf(CsvFile,"\n");
3366 	fprintf(CsvFile,"\"A taxa de descarregamento da bateria (C) é a relação entre a corrente (A) e a corrente\"\n");
3367 	fprintf(CsvFile,"\"de C1 (Amper por 1 hora), que é uma constante.\"\n");
3368 	fprintf(CsvFile,"\n");
3369 	fprintf(CsvFile,"\"Quando o nobreak está funcionando com as baterias, a tensão inicial prevista na bateria\"\n");
3370 	fprintf(CsvFile,"\"é uma função da taxa de descarregamento. Essa tensão inicial teórica é fundamental para as\"\n");
3371 	fprintf(CsvFile,"\"funções de timeleft. Ela também fornece informação para estimar se a bateria está em uma\"\n");
3372 	fprintf(CsvFile,"\"boa condição\"\n");
3373 	fprintf(CsvFile,"\n");
3374 	fprintf(CsvFile,"\"A tensão mínima das baterias é uma função da taxa de descarregamento. Quando o nobreak\"\n");
3375 	fprintf(CsvFile,"\"está funcionando com as baterias, as baterias não podem atingir uma tensão muito baixa.\"\n");
3376 	fprintf(CsvFile,"\"Caso isso ocorra elas serão danificadas. A tensão mínima também é utilizada para as funções\"\n");
3377 	fprintf(CsvFile,"\"de calculo da autonomia.\"\n");
3378 	fprintf(CsvFile,"\n");
3379 	fprintf(CsvFile,"\"Quando o nobreak está ligado a rede elétrica a autonomia é calculada com base na formula\"\n");
3380 	fprintf(CsvFile,"\"de Peikert. Essa autonomia teórica é uma função da potência de saída do nobreak. Compare os\"\n");
3381 	fprintf(CsvFile,"\"dados gerados com o manual do fabricante\"\n");
3382 	fprintf(CsvFile,"\n");
3383 	fprintf(CsvFile,"\"TABELA 1: Calculo da autonomia inicial (Peukert Law) em função da potência ativa de saída\"\n");
3384 	fprintf(CsvFile,"\"          do nobreak. Essa tabela é usada para comparar o calculo pela formula de Peukert\"\n");
3385 	fprintf(CsvFile,"\"          (com a rede elétrica) versus formula em função da tensão da bateria.\"\n");
3386 	fprintf(CsvFile,"\"Potência ativa de saída (W)\",\"Taxa de descarga da bateria (C)\",\"Tensão inicial esperada da bateria(V)\",\"Autonomia Peukert (minutos)\",\"Autonomia inicial na bateria (minutos)\"\n");
3387 	double power = 75;
3388 	do{
3389 		power += 25;
3390 		double batload = br->bat->calcBatteryLoadC1(power);
3391 		double volt_inicial = br->bat->calcVoltageMax(batload);
3392 		double timeleft_peukert = br->bat->calcTimeLeftPeukert(batload);
3393 		double timeleft_tensao = br->bat->calcTimeLeft(batload,volt_inicial);
3394 		fprintf(CsvFile,"\"%4.1f\",\"%1.1f\",\"%2.2f\",\"%2.2f\",\"%2.2f\"\n",power,batload,volt_inicial,timeleft_peukert,timeleft_tensao);
3395 	}while(power < br->getOutputActivePowerNom());
3396 
3397 	fprintf(CsvFile,"\n");
3398 	fprintf(CsvFile,"\n");
3399 	fprintf(CsvFile,"\"### AUTONOMIA EM FUNÇÃO DA TENSÃO DA BATERIA\"\n");
3400 	fprintf(CsvFile,"Quando o nobreak está funcionando com as baterias a autonomia é estimada com base na\"\n");
3401 	fprintf(CsvFile,"tensão das baterias (V) e na taxa de descarregamento da bateria (C).\n");
3402 	fprintf(CsvFile,"\n");
3403 	fprintf(CsvFile,"\"TABELA 2: Calculo da autonomia em função da potência ativa de saída e da tensão na bateria.\"\n");
3404 	fprintf(CsvFile,"\"          Esse cálculo é usado quando o nobreak está operando com as baterias.\"\n");
3405 	fprintf(CsvFile,"\"Potência ativa de saída (W)\",\"Taxa de descarga da bateria (C)\",\"Tensão (V)\",\"Autonomia (minutos)\"\n");
3406 	power = 75;
3407 	do{
3408 		power += 25;
3409 		double batload = br->bat->calcBatteryLoadC1(power);
3410 		double volt_max = br->bat->calcVoltageMax(batload);
3411 		double volt_min = br->bat->calcVoltageMin(batload);
3412 		for(double volt=volt_max ; volt>volt_min ; volt-=((volt_max-volt_min)/10)){
3413 			double timeleft = br->bat->calcTimeLeft(batload,volt);
3414 			fprintf(CsvFile,"\"%4.1f\",\"%1.1f\",\"%2.2f\",\"%2.2f\"\n",power,batload,volt,timeleft);
3415 
3416 		}
3417 	}while(power < br->getOutputActivePowerNom());
3418 	fprintf(CsvFile,"\n");
3419 	fprintf(CsvFile,"\n");
3420 	fprintf(CsvFile,"\"### AUTONOMIA EM FUNÇÃO DA TENSÃO DA BATERIA COM C1\"\n");
3421 	fprintf(CsvFile,"\"Quando o nobreak está funcionando com as baterias a autonomia é estimada com base na\"\n");
3422 	fprintf(CsvFile,"\"tensão das baterias (V) e na taxa de descarregamento da bateria (C).\"\n");
3423 	fprintf(CsvFile,"\n");
3424 	fprintf(CsvFile,"\"TABELA 3: Calculo da autonomia em relação da potência ativa de saída e da tensão na bateria.\"\n");
3425 	fprintf(CsvFile,"\"          Essa tabela serve para comparar com datasheet de uma bateria de chumbo-ácido.\"\n");
3426 	fprintf(CsvFile,"\"Potência ativa de saída (W)\",\"Taxa de descarga da bateria (C)\",\"Tensão (V)\",\"Autonomia (minutos)\"\n");
3427 	double batload = 1;
3428 	double voltmax = br->bat->calcVoltageMax(batload);
3429 	double voltmin = br->bat->calcVoltageMin(batload);
3430 	power = br->bat->getBatteryPowerC1Nom();
3431 	for(double volt = voltmax ; volt > voltmin ; volt-=0.1){
3432 		double timeleft = br->bat->calcTimeLeft(batload,volt);
3433 		fprintf(CsvFile,"\"%4.1f\",\"%1.1f\",\"%2.2f\",\"%2.2f\"\n",power,batload,volt,timeleft);
3434 	}
3435 
3436 	fflush(CsvFile);
3437 	fclose(CsvFile);
3438 
3439 	pmsg("2) Dados gravados no arquivo:    %s\n",csv_filename);
3440 	pmsg("3) Fim do teste!\n");
3441 }
3442 
do_brazil_testing(void)3443 static void do_brazil_testing(void)
3444 {
3445 #ifdef HAVE_BRAZIL_DRIVER
3446 	char *cmd;
3447 	int quit = FALSE;
3448 
3449 	// 1) Força o desligamento de qualquer programação no carregamento do apctest.
3450 	// 2) Quando o driver brazil é carregado ele desliga qualquer programaçao desde que as flags
3451 	//    hibernate_ups ou shutdown_ups não estejam setadas.
3452 	// 3) Quando o apctest é carregado ele verifica se a flag hibernate_ups está setada. Em caso
3453 	//    positivo ele encerra o apctest. Ficando a única hipótese com a flag shutdown_ups.
3454 	// 4) Quando o nobreak passa por um desligamento normal (com o uso do apcctrl daemon) pode ocorrer
3455 	//    uma programação de desligamento e religamento, que ficará armazenada no nobreak. Dessa forma,
3456 	//    esse comportamento só ocorrerá em caso de desligamento por falta de rede elétrica. Em sequência,
3457 	//    a observação da programação pela opção 3 no menu do apctest só ocorrerá se a flag shutdown_ups
3458 	//    que fará com que o driver brazil não cancele qualquer programação.
3459 	// 5) Bug detectado por Mauricio Girardi <girardi.mauricio@gmail.com>
3460 	((BrazilUpsDriver*)(ups)->driver)->programmation(false, 0, false, 0);
3461 	((BrazilUpsDriver*)(ups)->driver)->turnLineOn(true);
3462 	((BrazilUpsDriver*)(ups)->driver)->turnOutputOn(true);
3463 	((BrazilUpsDriver*)(ups)->driver)->turnContinueMode();
3464 
3465 	pmsg("Hello, this is the apcctrl Cable Test program.\n"
3466 			"This part of apctest is for testing APC-Microsol Brazil.\n"
3467 			"Please select the function you want to perform.\n");
3468 
3469 	while (!quit) {
3470 		pmsg("\n"
3471 				"1) Query the UPS for all known values\n"
3472 				"2) Query for last events\n"
3473 				"3) Query the internal UPS clock and scheduler\n"
3474 				"4) Schedule the UPS to shutdown in 1 minutes and restart 1 minute later.\n"
3475 				"5) Cancel Schedule. Do not shutdown or start\n"
3476 				"6) Test set input turn off\n"
3477 				"7) Test set input Turn on\n"
3478 				"8) Test battery health\n"
3479 				"9) Generate data of timeleft functions\n"
3480 				"Q) Quit\n\n");
3481 
3482 		cmd = get_cmd("Select function number: ");
3483 		if (cmd) {
3484 			int item = atoi(cmd);
3485 
3486 			switch (item) {
3487 			case 1:
3488 				brazil_print();
3489 				break;
3490 			case 2:
3491 				brazil_last();
3492 				break;
3493 			case 3:
3494 				brazil_getSchedule();
3495 				break;
3496 			case 4:
3497 				brazil_setSchedule();
3498 				break;
3499 			case 5:
3500 				brazil_cancelSchedule();
3501 				break;
3502 			case 6:
3503 				brazil_turnoff();
3504 				break;
3505 			case 7:
3506 				brazil_turnon();
3507 				break;
3508 			case 8:
3509 				brazil_testBatteryHealth();
3510 				break;
3511 			case 9:
3512 				brazil_testTimeLeftRoutines();
3513 				break;
3514 			default:
3515 				if (tolower(*cmd) == 'q')
3516 					quit = TRUE;
3517 				else
3518 					pmsg("Illegal response. Please enter 1-8,Q\n");
3519 				break;
3520 				break;
3521 			}
3522 		} else {
3523 			pmsg("Illegal response. Please enter 1-8,Q\n");
3524 		}
3525 	}
3526 	ptime();
3527 	pmsg("End apctest.\n");
3528 #else
3529 	pmsg("APC-Microsol Brazil Driver not configured.\n");
3530 #endif
3531 }
3532 
3533