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