[package] add wattsup
[openwrt/svn-archive/archive.git] / utils / wattsup / src / wattsup.c
1 /*
2 * wattsup - Program for controlling the Watts Up? Pro Device
3 *
4 *
5 * Copyright (c) 2005 Patrick Mochel
6 *
7 * This program is released under the GPLv2
8 *
9 *
10 * Compiled with:
11 *
12 * gcc -O2 -Wall -o wattsup wattsup.c
13 *
14 */
15
16 #define _GNU_SOURCE
17 #include<stdio.h>
18 #include<stdlib.h>
19 #include<stdarg.h>
20 #include<string.h>
21 #include<errno.h>
22 #include<unistd.h>
23 #include<fcntl.h>
24 #include<termios.h>
25 #include<ctype.h>
26 #include<getopt.h>
27 #include<signal.h>
28 #include<time.h>
29
30 #include<sys/stat.h>
31
32 static const char * wu_version = "0.02";
33
34
35 #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
36
37 static const char * prog_name = "wattsup";
38
39 static const char * sysfs_path_start = "/sys/class/tty";
40
41 static char * wu_device = "ttyUSB0";
42 static int wu_fd = 0;
43 static int wu_count = 0;
44 static int wu_debug = 0;
45 static char *wu_delim = ", ";
46 static int wu_final = 0;
47 static int wu_interval = 1;
48 static int wu_label = 0;
49 static int wu_newline = 0;
50 static int wu_suppress = 0;
51
52 static int wu_localtime = 0;
53 static int wu_gmtime = 0;
54
55 static int wu_info_all = 0;
56 static int wu_no_data = 0;
57 static int wu_set_only = 0;
58
59 #define wu_strlen 256
60 #define wu_num_fields 18
61 #define wu_param_len 16
62
63 struct wu_packet {
64 unsigned int cmd;
65 unsigned int sub_cmd;
66 unsigned int count;
67 char buf[wu_strlen];
68 int len;
69 char * field[wu_num_fields];
70 char * label[wu_num_fields];
71 };
72
73
74 struct wu_data {
75 unsigned int watts;
76 unsigned int volts;
77 unsigned int amps;
78 unsigned int watt_hours;
79
80 unsigned int cost;
81 unsigned int mo_kWh;
82 unsigned int mo_cost;
83 unsigned int max_watts;
84
85 unsigned int max_volts;
86 unsigned int max_amps;
87 unsigned int min_watts;
88 unsigned int min_volts;
89
90 unsigned int min_amps;
91 unsigned int power_factor;
92 unsigned int duty_cycle;
93 unsigned int power_cycle;
94 };
95
96 struct wu_options {
97 char * longopt;
98 int shortopt;
99 int param;
100 int flag;
101 char * value;
102
103 char * descr;
104 char * option;
105 char * format;
106
107 int (*show)(int dev_fd);
108 int (*store)(int dev_fd);
109 };
110
111 enum {
112 wu_option_help = 0,
113 wu_option_version,
114
115 wu_option_debug,
116
117 wu_option_count,
118 wu_option_final,
119
120 wu_option_delim,
121 wu_option_newline,
122 wu_option_localtime,
123 wu_option_gmtime,
124 wu_option_label,
125
126 wu_option_suppress,
127
128 wu_option_cal,
129 wu_option_header,
130
131 wu_option_interval,
132 wu_option_mode,
133 wu_option_user,
134
135 wu_option_info_all,
136 wu_option_no_data,
137 wu_option_set_only,
138 };
139
140
141 static char * wu_option_value(unsigned int index);
142
143
144 enum {
145 wu_field_watts = 0,
146 wu_field_volts,
147 wu_field_amps,
148
149 wu_field_watt_hours,
150 wu_field_cost,
151 wu_field_mo_kwh,
152 wu_field_mo_cost,
153
154 wu_field_max_watts,
155 wu_field_max_volts,
156 wu_field_max_amps,
157
158 wu_field_min_watts,
159 wu_field_min_volts,
160 wu_field_min_amps,
161
162 wu_field_power_factor,
163 wu_field_duty_cycle,
164 wu_field_power_cycle,
165 };
166
167 struct wu_field {
168 unsigned int enable;
169 char * name;
170 char * descr;
171 };
172
173 static struct wu_field wu_fields[wu_num_fields] = {
174 [wu_field_watts] = {
175 .name = "watts",
176 .descr = "Watt Consumption",
177 },
178
179 [wu_field_min_watts] = {
180 .name = "min-watts",
181 .descr = "Minimum Watts Consumed",
182 },
183
184 [wu_field_max_watts] = {
185 .name = "max-watts",
186 .descr = "Maxium Watts Consumed",
187 },
188
189 [wu_field_volts] = {
190 .name = "volts",
191 .descr = "Volts Consumption",
192 },
193
194 [wu_field_min_volts] = {
195 .name = "max-volts",
196 .descr = "Minimum Volts Consumed",
197 },
198
199 [wu_field_max_volts] = {
200 .name = "min-volts",
201 .descr = "Maximum Volts Consumed",
202 },
203
204 [wu_field_amps] = {
205 .name = "amps",
206 .descr = "Amp Consumption",
207 },
208
209 [wu_field_min_amps] = {
210 .name = "min-amps",
211 .descr = "Minimum Amps Consumed",
212 },
213
214 [wu_field_max_amps] = {
215 .name = "max-amps",
216 .descr = "Maximum Amps Consumed",
217 },
218
219 [wu_field_watt_hours] = {
220 .name = "kwh",
221 .descr = "Average KWH",
222 },
223
224 [wu_field_mo_kwh] = {
225 .name = "mo-kwh",
226 .descr = "Average monthly KWH",
227 },
228
229 [wu_field_cost] = {
230 .name = "cost",
231 .descr = "Cost per watt",
232 },
233
234 [wu_field_mo_cost] = {
235 .name = "mo-cost",
236 .descr = "Monthly Cost",
237 },
238
239 [wu_field_power_factor] = {
240 .name = "power-factor",
241 .descr = "Ratio of Watts vs. Volt Amps",
242 },
243
244 [wu_field_duty_cycle] = {
245 .name = "duty-cycle",
246 .descr = "Percent of the Time On vs. Time Off",
247 },
248
249 [wu_field_power_cycle] = {
250 .name = "power-cycle",
251 .descr = "Indication of power cycle",
252 },
253
254 };
255
256
257
258 static void msg_start(const char * fmt, ...)
259 {
260 va_list(ap);
261 va_start(ap, fmt);
262 vprintf(fmt, ap);
263 va_end(ap);
264 }
265
266 static void msg_end(void)
267 {
268 printf("\n");
269 }
270
271 static void msg(const char * fmt, ...)
272 {
273 va_list ap;
274
275 va_start(ap, fmt);
276 vprintf(fmt, ap);
277 va_end(ap);
278 }
279
280 static void dbg(const char * fmt, ...)
281 {
282 va_list ap;
283
284 if (wu_debug) {
285 va_start(ap, fmt);
286 msg_start("%s: [debug] ", prog_name);
287 vprintf(fmt, ap);
288 msg_end();
289 va_end(ap);
290 }
291 }
292
293 static void err(const char * fmt, ...)
294 {
295 va_list ap;
296
297 va_start(ap, fmt);
298 fprintf(stderr, "%s: [error] ", prog_name);
299 vfprintf(stderr, fmt, ap);
300 fprintf(stderr, "\n");
301 va_end(ap);
302 }
303
304 static void perr(const char * fmt, ...)
305 {
306 char buf[1024];
307 int n;
308 va_list ap;
309
310 va_start(ap, fmt);
311 n = sprintf(buf, "%s: [error] ", prog_name);
312 vsnprintf(buf + n, sizeof(buf) - n, fmt, ap);
313 perror(buf);
314 va_end(ap);
315 }
316
317 static int ret_err(int err)
318 {
319 errno = err;
320 return -1;
321 }
322
323
324 static void print_packet(struct wu_packet * p, char * str)
325 {
326 int i;
327
328 if (!wu_suppress)
329 msg_start("Watts Up? %s\n", str);
330 for (i = 0; i< p->count; i++) {
331 if (i)
332 msg("%s", wu_newline ? "\n" : wu_delim);
333 if (wu_label)
334 msg("[%s] ", p->label[i]);
335 msg(p->field[i]);
336 }
337 msg_end();
338 }
339
340
341 static void print_time(void)
342 {
343 time_t t;
344 struct tm * tm;
345
346 if (wu_localtime || wu_gmtime) {
347 time(&t);
348
349 if (wu_localtime)
350 tm = localtime(&t);
351 else
352 tm = gmtime(&t);
353
354 msg("[%02d:%02d:%02d] ",
355 tm->tm_hour, tm->tm_min, tm->tm_sec);
356 }
357 }
358
359 static void print_packet_filter(struct wu_packet * p,
360 int (*filter_ok)(struct wu_packet * p, int i, char * str))
361 {
362 char buf[256];
363 int printed;
364 int i;
365
366 print_time();
367 for (i = 0, printed = 0; i< p->count; i++) {
368 if (!filter_ok(p, i, buf))
369 continue;
370
371 if (printed++)
372 msg("%s", wu_newline ? "\n" : wu_delim);
373 if (wu_label)
374 msg("[%s] ", p->label[i]);
375 msg(buf);
376 }
377 msg_end();
378 }
379
380
381 /*
382 * Device should be something like "ttyS0"
383 */
384
385 static int open_device(char * device_name, int * dev_fd)
386 {
387 struct stat s;
388 int ret;
389 int cur_fd;
390
391 cur_fd = open(".", O_RDONLY);
392 if (cur_fd< 0) {
393 perr("Could not open current directory.");
394 return cur_fd;
395 }
396
397 ret = chdir(sysfs_path_start);
398 if (ret) {
399 perr(sysfs_path_start);
400 return ret;
401 }
402
403 /*
404 * First, check if /sys/class/tty/<name>/ exists.
405 */
406
407 dbg("Checking sysfs path: %s/%s", sysfs_path_start, device_name);
408
409 ret = stat(device_name,&s);
410 if (ret< 0) {
411 perr(device_name);
412 goto Done;
413 }
414
415 if (!S_ISDIR(s.st_mode)) {
416 errno = -ENOTDIR;
417 err("%s is not a TTY device.", device_name);
418 goto Done;
419 }
420
421 dbg("%s is a registered TTY device", device_name);
422
423 fchdir(cur_fd);
424
425
426 /*
427 * Check if device node exists and is writable
428 */
429 chdir("/dev");
430
431 ret = stat(device_name,&s);
432 if (ret< 0) {
433 perr("/dev/%s (device node)", device_name);
434 goto Done;
435 }
436
437 if (!S_ISCHR(s.st_mode)) {
438 errno = -ENOTTY;
439 ret = -1;
440 err("%s is not a TTY character device.", device_name);
441 goto Done;
442 }
443
444 dbg("%s has a device node", device_name);
445
446 ret = access(device_name, R_OK | W_OK);
447 if (ret) {
448 perr("%s: Not writable?", device_name);
449 goto Done;
450 }
451
452 ret = open(device_name, O_RDWR | O_NONBLOCK);
453 if (ret< 0) {
454 perr("Could not open %s");
455 goto Done;
456 }
457 *dev_fd = ret;
458 ret = 0;
459 Done:
460 fchdir(cur_fd);
461 close(cur_fd);
462 return ret;
463 }
464
465
466 static int setup_serial_device(int dev_fd)
467 {
468 struct termios t;
469 int ret;
470
471 ret = tcgetattr(dev_fd,&t);
472 if (ret)
473 return ret;
474
475 cfmakeraw(&t);
476 cfsetispeed(&t, B115200);
477 cfsetospeed(&t, B115200);
478 tcflush(dev_fd, TCIFLUSH);
479
480 t.c_iflag |= IGNPAR;
481 t.c_cflag&= ~CSTOPB;
482 ret = tcsetattr(dev_fd, TCSANOW,&t);
483
484 if (ret) {
485 perr("setting terminal attributes");
486 return ret;
487 }
488
489 return 0;
490 }
491
492
493 static int wu_write(int fd, struct wu_packet * p)
494 {
495 int ret;
496 int n;
497 int i;
498 char * s = p->buf;
499
500 memset(p->buf, 0, sizeof(p->buf));
501 n = sprintf(p->buf, "#%c,%c,%d", p->cmd, p->sub_cmd, p->count);
502 p->len = n;
503 s = p->buf + n;
504
505 for (i = 0; i< p->count; i++) {
506 if ((p->len + strlen(p->field[i]) + 4)>= sizeof(p->buf)) {
507 err("Overflowed command string");
508 return ret_err(EOVERFLOW);
509 }
510 n = sprintf(s, ",%s", p->field[i]);
511 s += n;
512 p->len += n;
513 }
514 p->buf[p->len++] = ';';
515
516 dbg("Writing '%s' (strlen = %d) (len = %d) to device",
517 p->buf, strlen(p->buf), p->len);
518 ret = write(fd, p->buf, p->len);
519 if (ret != p->len)
520 perr("Writing to device");
521
522 return ret>= 0 ? 0 : ret;
523 }
524
525
526 static void dump_packet(struct wu_packet * p)
527 {
528 int i;
529
530 dbg("Packet - Command '%c' %d parameters", p->cmd, p->count);
531
532 for (i = 0; i< p->count; i++)
533 dbg("[%2d] [%20s] = \"%s\"", i, p->label[i], p->field[i]);
534 }
535
536
537 static int parse_packet(struct wu_packet * p)
538 {
539 char * s, *next;
540 int i;
541
542 p->buf[p->len] = '\0';
543
544 dbg("Parsing Packet, Raw buffer is (%d bytes) [%s]",
545 p->len, p->buf);
546
547 s = p->buf;
548
549 /*
550 * First character should be '#'
551 */
552 if (s) {
553 s = strchr(s, '#');
554 if (s)
555 s++;
556 else {
557 dbg("Invalid packet");
558 return ret_err(EFAULT);
559 }
560 } else {
561 dbg("Invalid packet");
562 return ret_err(EFAULT);
563 }
564
565 /*
566 * Command character is first
567 */
568 next = strchr(s, ',');
569 if (next) {
570 p->cmd = *s;
571 s = ++next;
572 } else {
573 dbg("Invalid Command field [%s]", s);
574 return ret_err(EFAULT);
575 }
576
577 /*
578 * Next character is the subcommand, and should be '-'
579 * Though, it doesn't matter, because we just
580 * discard it anyway.
581 */
582 next = strchr(s, ',');
583 if (next) {
584 p->sub_cmd = *s;
585 s = ++next;
586 } else {
587 dbg("Invalid 2nd field");
588 return ret_err(EFAULT);
589 }
590
591 /*
592 * Next is the number of parameters,
593 * which should always be> 0.
594 */
595 next = strchr(s, ',');
596 if (next) {
597 *next++ = '\0';
598 p->count = atoi(s);
599 s = next;
600 } else {
601 dbg("Couldn't determine number of parameters");
602 return ret_err(EFAULT);
603 }
604
605 dbg("Have %d parameter%s (cmd = '%c')",
606 p->count, p->count> 1 ? "s" : "", p->cmd);
607
608 /*
609 * Now, we loop over the rest of the string,
610 * storing a pointer to each in p->field[].
611 *
612 * The last character was originally a ';', but may have been
613 * overwritten with a '\0', so we make sure to catch
614 * that when converting the last parameter.
615 */
616 for (i = 0; i< p->count; i++) {
617 next = strpbrk(s, ",;");
618 if (next) {
619 *next++ = '\0';
620 } else {
621 if (i< (p->count - 1)) {
622 dbg("Malformed parameter string [%s]", s);
623 return ret_err(EFAULT);
624 }
625 }
626
627 /*
628 * Skip leading white space in fields
629 */
630 while (isspace(*s))
631 s++;
632 p->field[i] = s;
633 s = next;
634 }
635 dump_packet(p);
636 return 0;
637 }
638
639
640 static int wu_read(int fd, struct wu_packet * p)
641 {
642 fd_set read_fd;
643 struct timeval tv;
644 int ret;
645
646 FD_ZERO(&read_fd);
647 FD_SET(fd,&read_fd);
648
649 tv.tv_sec = 2;
650 tv.tv_usec = 0;
651
652 ret = select(fd + 1,&read_fd, NULL, NULL,&tv);
653 if (ret< 0) {
654 perr("select on terminal device");
655 return ret;
656 } else if (ret> 0) {
657
658 ret = read(fd, p->buf, wu_strlen);
659 if (ret< 0) {
660 perr("Reading from device");
661 return ret;
662 }
663 p->len = ret;
664 } else {
665 dbg("Device timed out while reading");
666 return ret_err(ETIME);
667 }
668 return parse_packet(p);
669 }
670
671
672 static int wu_show_header(int fd)
673 {
674 struct wu_packet p = {
675 .cmd = 'H',
676 .sub_cmd = 'R',
677 .count = 0,
678 .label = {
679 [0] = "watts header",
680 [1] = "volts header",
681 [2] = "amps header",
682 [3] = "kWh header",
683 [4] = "cost header",
684 [5] = "mo. kWh header",
685 [6] = "mo. cost header",
686 [7] = "max watts header",
687 [8] = "max volts header",
688 [9] = "max amps header",
689 [10] = "min watts header",
690 [11] = "min volts header",
691 [12] = "min amps header",
692 [13] = "power factor header",
693 [14] = "duty cycle header",
694 [15] = "power cycle header",
695 }
696 };
697 int ret;
698
699 ret = wu_write(fd,&p);
700 if (ret) {
701 perr("Requesting header strings");
702 return ret;
703 }
704 sleep(1);
705
706 ret = wu_read(fd,&p);
707 if (ret) {
708 perr("Reading header strings");
709 return ret;
710 }
711
712 print_packet(&p, "Header Record");
713
714 return 0;
715 }
716
717
718 static int wu_show_cal(int fd)
719 {
720 struct wu_packet p = {
721 .cmd = 'F',
722 .sub_cmd = 'R',
723 .count = 0,
724 .label = {
725 [0] = "flags",
726 [1] = "sample count",
727 [2] = "volts gain",
728 [3] = "volts bias",
729 [4] = "amps gain",
730 [5] = "amps bias",
731 [6] = "amps offset",
732 [7] = "low amps gain",
733 [8] = "low amps bias",
734 [9] = "low amps offset",
735 [10] = "watts gain",
736 [11] = "watts offset",
737 [12] = "low watts gain",
738 [13] = "low watts offset",
739 },
740 };
741 int ret;
742
743 ret = wu_write(fd,&p);
744 if (ret) {
745 perr("Requesting calibration parameters");
746 return ret;
747 }
748 sleep(1);
749
750 ret = wu_read(fd,&p);
751 if (ret) {
752 perr("Reading header strings");
753 return ret;
754 }
755 print_packet(&p, "Calibration Settings");
756
757 return 0;
758 }
759
760 static int wu_start_log(void)
761 {
762 struct wu_packet p = {
763 .cmd = 'L',
764 .sub_cmd = 'W',
765 .count = 3,
766 .field = {
767 [0] = "E",
768 [1] = "1",
769 [2] = "1",
770 },
771 };
772 int ret;
773
774 /*
775 * Start up logging
776 */
777 ret = wu_write(wu_fd,&p);
778 if (!ret)
779 sleep(1);
780 else {
781 perr("Starting External Logging");
782 return ret;
783 }
784 return ret;
785 }
786
787 static int wu_stop_log(void)
788 {
789 struct wu_packet p = {
790 .cmd = 'L',
791 .sub_cmd = 'R',
792 .count = 0,
793 .label = {
794 [0] = "time stamp",
795 [1] = "interval",
796 },
797 };
798 int ret;
799
800 /*
801 * Stop logging and read time stamp.
802 */
803 ret = wu_write(wu_fd,&p);
804 if (ret) {
805 perr("Stopping External Logging");
806 return ret;
807 }
808 sleep(1);
809
810 ret = wu_read(wu_fd,&p);
811 if (ret) {
812 perr("Reading final time stamp");
813 return ret;
814 }
815 if (wu_final)
816 print_packet(&p, "Final Time Stamp and Interval");
817 return ret;
818 }
819
820 static int filter_data(struct wu_packet * p, int i, char * buf)
821 {
822 if (i< wu_num_fields) {
823 if (wu_fields[i].enable) {
824 double val = strtod(p->field[i], NULL);
825 snprintf(buf, 256, "%.1f", val / 10.0);
826 return 1;
827 }
828 }
829 return 0;
830 }
831
832 static int wu_clear(int fd)
833 {
834 struct wu_packet p = {
835 .cmd = 'R',
836 .sub_cmd = 'W',
837 .count = 0,
838 };
839 int ret;
840
841 /*
842 * Clear the memory
843 */
844 ret = wu_write(fd,&p);
845 if (ret)
846 perr("Clearing memory");
847 else
848 sleep(2);
849
850 /*
851 * Dummy read
852 */
853 wu_read(fd,&p);
854 return ret;
855
856 }
857
858 static int wu_read_data(int fd)
859 {
860 struct wu_packet p = {
861 .label = {
862 [0] = "watts",
863 [1] = "volts",
864 [2] = "amps",
865 [3] = "watt hours",
866 [4] = "cost",
867 [5] = "mo. kWh",
868 [6] = "mo. cost",
869 [7] = "max watts",
870 [8] = "max volts",
871 [9] = "max amps",
872 [10] = "min watts",
873 [11] = "min volts",
874 [12] = "min amps",
875 [13] = "power factor",
876 [14] = "duty cycle",
877 [15] = "power cycle",
878 },
879 };
880 int num_read = 0;
881 int retry = 0;
882 int ret;
883 int i;
884
885 static const int wu_max_retry = 2;
886
887 i = 0;
888 while (1) {
889
890 ret = wu_read(fd,&p);
891 if (ret) {
892 if (++retry< wu_max_retry) {
893 dbg("Bad record back, retrying\n");
894 sleep(wu_interval);
895 continue;
896 } else if (retry == wu_max_retry) {
897 dbg("Still couldn't get a good record, resetting\n");
898 wu_stop_log();
899 wu_clear(fd);
900 wu_start_log();
901 num_read = 0;
902 sleep(wu_interval);
903 continue;
904 }
905 perr("Blech. Giving up on read");
906 break;
907 } else if (retry)
908 retry = 0;
909
910 dbg("[%d] ", num_read);
911 num_read++;
912 print_packet_filter(&p, filter_data);
913
914 if (wu_count&& (++i == wu_count))
915 break;
916
917 sleep(wu_interval);
918 }
919 return 0;
920
921 }
922
923
924 static int wu_show_interval(int fd)
925 {
926 struct wu_packet p = {
927 .cmd = 'S',
928 .sub_cmd = 'R',
929 .count = 0,
930 .label = {
931 [0] = "reserved",
932 [1] = "interval",
933 },
934 };
935 int ret;
936
937 ret = wu_write(fd,&p);
938 if (ret) {
939 perr("Requesting interval");
940 return ret;
941 }
942 sleep(1);
943
944 ret = wu_read(fd,&p);
945 if (ret) {
946 perr("Reading interval");
947 return ret;
948 }
949 print_packet(&p, "Interval Settings");
950
951 return 0;
952 }
953
954 static int wu_write_interval(int fd, unsigned int seconds,
955 unsigned int interval)
956 {
957 char str_seconds[wu_param_len];
958 char str_interval[wu_param_len];
959 struct wu_packet p = {
960 .cmd = 'S',
961 .sub_cmd = 'W',
962 .count = 2,
963 .field = {
964 [0] = str_seconds,
965 [1] = str_interval,
966 },
967 };
968 int ret;
969
970 snprintf(str_seconds, wu_param_len, "%ud", seconds);
971 snprintf(str_interval, wu_param_len, "%ud", interval);
972
973 ret = wu_write(fd,&p);
974 if (ret) {
975 perr("Setting Sampling Interval");
976 return ret;
977 }
978 sleep(1);
979 return 0;
980 }
981
982 static int wu_store_interval(int fd)
983 {
984 char * s = wu_option_value(wu_option_interval);
985 char * end;
986
987 wu_interval = strtol(s,&end, 0);
988 if (*end) {
989 err("Invalid interval: %s", s);
990 return ret_err(EINVAL);
991 }
992 return wu_write_interval(fd, 1, wu_interval);
993 }
994
995 static int wu_show_mode(int fd)
996 {
997 struct wu_packet p = {
998 .cmd = 'M',
999 .sub_cmd = 'R',
1000 .count = 0,
1001 .label = {
1002 [0] = "display mode",
1003 },
1004 };
1005 int ret;
1006
1007 ret = wu_write(fd,&p);
1008 if (ret) {
1009 perr("Requesting device display mode");
1010 return ret;
1011 }
1012
1013 ret = wu_read(fd,&p);
1014 if (ret) {
1015 perr("Reaing device display mode");
1016 return ret;
1017 }
1018 dump_packet(&p);
1019 return ret;
1020 }
1021
1022 static int wu_write_mode(int fd, int mode)
1023 {
1024 char str_mode[wu_param_len];
1025 struct wu_packet p = {
1026 .cmd = 'M',
1027 .sub_cmd = 'W',
1028 .count = 1,
1029 .field = {
1030 [0] = str_mode,
1031 },
1032 };
1033 int ret;
1034
1035 snprintf(str_mode, wu_param_len, "%ud", mode);
1036 ret = wu_write(fd,&p);
1037 if (ret)
1038 perr("Setting device display mode");
1039 else
1040 sleep(1);
1041 return ret;
1042 }
1043
1044 static int wu_store_mode(int fd)
1045 {
1046 char * s = wu_option_value(wu_option_mode);
1047 char * end;
1048 unsigned int mode;
1049
1050 mode = strtol(s,&end, 0);
1051 if (*end) {
1052 err("Invalid mode: %s", s);
1053 return ret_err(EINVAL);
1054 }
1055 return wu_write_mode(fd, mode);
1056 }
1057
1058
1059
1060 static int wu_show_user(int fd)
1061 {
1062 struct wu_packet p = {
1063 .cmd = 'U',
1064 .sub_cmd = 'R',
1065 .count = 0,
1066 .label = {
1067 [0] = "cost per kWh",
1068 [1] = "2nd tier cost",
1069 [2] = "2nd tier threshold",
1070 [3] = "duty cycle threshold",
1071 },
1072 };
1073 int ret;
1074
1075 ret = wu_write(fd,&p);
1076 if (ret) {
1077 perr("Requesting user parameters");
1078 return ret;
1079 }
1080 sleep(1);
1081
1082 ret = wu_read(fd,&p);
1083 if (ret) {
1084 perr("Reading user parameters");
1085 return ret;
1086 }
1087 print_packet(&p, "User Settings");
1088 return 0;
1089 }
1090
1091
1092 static int wu_write_user(int fd, unsigned int kwh_cost,
1093 unsigned int second_tier_cost,
1094 unsigned int second_tier_threshold,
1095 unsigned int duty_cycle_threshold)
1096 {
1097 char str_kwh_cost[wu_param_len];
1098 char str_2nd_tier_cost[wu_param_len];
1099 char str_2nd_tier_threshold[wu_param_len];
1100 char str_duty_cycle_threshold[wu_param_len];
1101
1102 struct wu_packet p = {
1103 .cmd = 'U',
1104 .sub_cmd = 'R',
1105 .count = 0,
1106 .label = {
1107 [0] = str_kwh_cost,
1108 [1] = str_2nd_tier_cost,
1109 [2] = str_2nd_tier_threshold,
1110 [3] = str_duty_cycle_threshold,
1111 },
1112 };
1113 int ret;
1114
1115 snprintf(str_kwh_cost, wu_param_len, "%ud", kwh_cost);
1116 snprintf(str_2nd_tier_cost, wu_param_len, "%ud",
1117 second_tier_cost);
1118 snprintf(str_2nd_tier_threshold, wu_param_len, "%ud",
1119 second_tier_threshold);
1120 snprintf(str_duty_cycle_threshold, wu_param_len, "%ud",
1121 duty_cycle_threshold);
1122
1123 ret = wu_write(fd,&p);
1124 if (ret)
1125 perr("Writing user parameters");
1126 else
1127 sleep(1);
1128 return ret;
1129 }
1130
1131 static int wu_store_user(int fd)
1132 {
1133 unsigned int kwh_cost;
1134 unsigned int second_tier_cost;
1135 unsigned int second_tier_threshold;
1136 unsigned int duty_cycle_threshold;
1137 char * buf = wu_option_value(wu_option_user);
1138 char * s = buf;
1139 char * next;
1140
1141 if (!buf) {
1142 err("No user parameters?");
1143 return ret_err(EINVAL);
1144 }
1145
1146 kwh_cost = strtoul(s,&next, 0);
1147 if (next == s) {
1148 err("Incomplete user parameters");
1149 return ret_err(EINVAL);
1150 }
1151
1152 s = next;
1153 while (s&& !isdigit(*s))
1154 s++;
1155 if (!s) {
1156 err("Incomplete user parameters");
1157 return ret_err(EINVAL);
1158 }
1159
1160
1161 second_tier_cost = strtoul(s,&next, 0);
1162 if (next == s) {
1163 err("Incomplete user parameters");
1164 return ret_err(EINVAL);
1165 }
1166
1167 s = next;
1168 while (s&& !isdigit(*s))
1169 s++;
1170 if (!s) {
1171 err("Incomplete user parameters");
1172 return ret_err(EINVAL);
1173 }
1174
1175
1176 second_tier_threshold = strtoul(s,&next, 0);
1177 if (next == s) {
1178 err("Incomplete user parameters");
1179 return ret_err(EINVAL);
1180 }
1181
1182 s = next;
1183 while (s&& !isdigit(*s))
1184 s++;
1185 if (!s) {
1186 err("Incomplete user parameters");
1187 return ret_err(EINVAL);
1188 }
1189
1190
1191 duty_cycle_threshold = strtoul(s,&next, 0);
1192 if (next == s) {
1193 err("Incomplete user parameters");
1194 return ret_err(EINVAL);
1195 }
1196
1197 s = next;
1198 while (s&& !isdigit(*s))
1199 s++;
1200 if (!s) {
1201 err("Incomplete user parameters");
1202 return ret_err(EINVAL);
1203 }
1204
1205 return wu_write_user(fd, kwh_cost, second_tier_cost,
1206 second_tier_threshold, duty_cycle_threshold);
1207 }
1208
1209
1210 static void enable_field(char * name)
1211 {
1212 int i;
1213
1214 for (i = 0; i< wu_num_fields; i++) {
1215 if (!strcasecmp(wu_fields[i].name, name)) {
1216 wu_fields[i].enable = 1;
1217 break;
1218 }
1219 }
1220 }
1221
1222 static void enable_all_fields(void)
1223 {
1224 int i;
1225
1226 for (i = 0; i< wu_num_fields; i++)
1227 wu_fields[i].enable = 1;
1228 }
1229
1230
1231
1232 static int wu_show_help(int);
1233 static int wu_show_version(int);
1234
1235
1236
1237 static int wu_store_count(int unused)
1238 {
1239 char * s = wu_option_value(wu_option_count);
1240 char * end;
1241
1242 if (s) {
1243 wu_count = strtol(s,&end, 0);
1244 if (*end) {
1245 err("Bad count field");
1246 return ret_err(EINVAL);
1247 }
1248 }
1249 return 0;
1250 }
1251
1252 static int wu_store_debug(int unused)
1253 {
1254 wu_debug = 1;
1255 return 0;
1256 }
1257
1258 static int wu_store_delim(int unused)
1259 {
1260 char * s = wu_option_value(wu_option_delim);
1261
1262 if (s)
1263 wu_delim = s;
1264 return 0;
1265 }
1266
1267 static int wu_store_final(int unused)
1268 {
1269 wu_final = 1;
1270 return 0;
1271 }
1272
1273 static int wu_store_label(int unused)
1274 {
1275 wu_label = 1;
1276 return 0;
1277 }
1278
1279 static int wu_store_newline(int unused)
1280 {
1281 wu_newline = 1;
1282 return 0;
1283 }
1284
1285 static int wu_store_suppress(int unused)
1286 {
1287 wu_suppress = 1;
1288 return 0;
1289 }
1290
1291 static int wu_store_localtime(int unused)
1292 {
1293 wu_localtime = 1;
1294 return 0;
1295 }
1296
1297 static int wu_store_gmtime(int unused)
1298 {
1299 wu_gmtime = 1;
1300 return 0;
1301 }
1302
1303 static int wu_store_info_all(int unused)
1304 {
1305 wu_info_all = 1;
1306 return 0;
1307 }
1308
1309 static int wu_store_no_data(int unused)
1310 {
1311 wu_no_data = 1;
1312 return 0;
1313 }
1314
1315 static int wu_store_set_only(int unused)
1316 {
1317 wu_set_only = 1;
1318 return 0;
1319 }
1320
1321
1322 /**
1323 * wu_options - command line options and their associated flags
1324 *
1325 */
1326 static struct wu_options wu_options[] = {
1327
1328 /*
1329 * Help!
1330 */
1331 [wu_option_help] = {
1332 .longopt = "help",
1333 .shortopt = 'h',
1334 .param = 0,
1335 .descr = "Display help text and exit",
1336 .show = wu_show_help,
1337 },
1338
1339 [wu_option_version] = {
1340 .longopt = "version",
1341 .shortopt = 'V',
1342 .param = 0,
1343 .descr = "Display version information and exit",
1344 .show = wu_show_version,
1345 },
1346
1347 /*
1348 * Modifies the output for all other options
1349 */
1350 [wu_option_debug] = {
1351 .longopt = "debug",
1352 .shortopt = 'd',
1353 .param = 0,
1354 .descr = "Print out debugging messages",
1355 .store = wu_store_debug,
1356 },
1357
1358 /*
1359 * For data reading..
1360 */
1361 [wu_option_count] = {
1362 .longopt = "count",
1363 .shortopt = 'c',
1364 .param = 1,
1365 .descr = "Specify number of data samples",
1366 .option = "<n>",
1367 .store = wu_store_count,
1368 },
1369
1370 [wu_option_final] = {
1371 .longopt = "final",
1372 .shortopt = 'z',
1373 .param = 0,
1374 .descr = "Print final interval information",
1375 .store = wu_store_final,
1376 },
1377
1378 /*
1379 * Modifies output for each option (most relevant for data)
1380 */
1381 [wu_option_delim] = {
1382 .longopt = "delim",
1383 .shortopt = 'f',
1384 .param = 1,
1385 .descr = "Set field delimiter (default \", \")",
1386 .option = "<str>",
1387 .store = wu_store_delim,
1388 },
1389
1390 [wu_option_newline] = {
1391 .longopt = "newline",
1392 .shortopt = 'n',
1393 .param = 0,
1394 .descr = "Use '\\n' as delimter instead",
1395 .store = wu_store_newline,
1396 },
1397
1398 [wu_option_localtime] = {
1399 .longopt = "localtime",
1400 .shortopt = 't',
1401 .param = 0,
1402 .descr = "Print localtime with each data reading",
1403 .store = wu_store_localtime,
1404 },
1405
1406 [wu_option_gmtime] = {
1407 .longopt = "gmtime",
1408 .shortopt = 'g',
1409 .param = 0,
1410 .descr = "Print GMT time with each data reading",
1411 .store = wu_store_gmtime,
1412 },
1413
1414 [wu_option_label] = {
1415 .longopt = "label",
1416 .shortopt = 'l',
1417 .param = 0,
1418 .descr = "Show labels of each field",
1419 .store = wu_store_label,
1420 },
1421
1422 /*
1423 * Relevant for each of the fields below
1424 */
1425 [wu_option_suppress] = {
1426 .longopt = "suppress",
1427 .shortopt = 's',
1428 .param = 0,
1429 .descr = "Suppress printing of the field description",
1430 .store = wu_store_suppress,
1431 },
1432
1433 /*
1434 * These options print values from the device and exit.
1435 */
1436 [wu_option_cal] = {
1437 .longopt = "calibrate",
1438 .shortopt = 'b',
1439 .param = 0,
1440 .descr = "Print calibration parameters",
1441 .show = wu_show_cal,
1442 },
1443
1444 [wu_option_header] = {
1445 .longopt = "header",
1446 .shortopt = 'r',
1447 .param = 0,
1448 .descr = "Print data field names (as read from device)",
1449 .show = wu_show_header,
1450 },
1451
1452 /*
1453 * These options have an optional parameter.
1454 * W/o that parameter, they print values from the device.
1455 * W/ that parameter, they set that option and read data.
1456 *
1457 * Except when the 'set-only' parameter is used, then the
1458 * parameters are set, then re-read and printed.
1459 */
1460 [wu_option_interval] = {
1461 .longopt = "interval",
1462 .shortopt = 'i',
1463 .param = 2,
1464 .descr = "Get/Set sampling interval",
1465 .option = "<n>",
1466 .show = wu_show_interval,
1467 .store = wu_store_interval,
1468 },
1469
1470 [wu_option_mode] = {
1471 .longopt = "mode",
1472 .shortopt = 'm',
1473 .param = 2,
1474 .descr = "Get/Set display mode",
1475 .option = "<n>",
1476 .show = wu_show_mode,
1477 .store = wu_store_mode,
1478 },
1479
1480 [wu_option_user] = {
1481 .longopt = "user",
1482 .shortopt = 'u',
1483 .param = 2,
1484 .descr = "Get/Set user parameters",
1485 .option = "<str>",
1486 .format = "<cost per kwh>,<2nd tier cost>,"
1487 "<2nd tier threshold>,"
1488 "<duty cycle threshold>",
1489 .show = wu_show_user,
1490 .store = wu_store_user,
1491 },
1492
1493 [wu_option_info_all] = {
1494 .longopt = "show-all",
1495 .shortopt = 'a',
1496 .param = 0,
1497 .descr = "Show all device parameters",
1498 .store = wu_store_info_all,
1499 },
1500
1501 [wu_option_no_data] = {
1502 .longopt = "no-data",
1503 .shortopt = 'N',
1504 .param = 0,
1505 .descr = "Don't read any data (just read device info)",
1506 .store = wu_store_no_data,
1507 },
1508
1509 [wu_option_set_only] = {
1510 .longopt = "set-only",
1511 .shortopt = 'S',
1512 .param = 0,
1513 .descr = "Set parameters only (don't read them back)",
1514 .store = wu_store_set_only,
1515 },
1516 };
1517
1518 #define wu_num_options ARRAY_SIZE(wu_options)
1519
1520 static int wu_show_version(int unused)
1521 {
1522 printf("%s Version %s\n", prog_name, wu_version);
1523 return 0;
1524 }
1525
1526 static int wu_show_help(int unused)
1527 {
1528 int i;
1529 int n;
1530
1531 wu_show_version(unused);
1532 printf(" A program for interfacing with the Watts Up? Power Meter\n");
1533 printf("\n");
1534
1535 printf("Usage: %s [<options> ... ]<device> [<values> ... ]\n",
1536 prog_name);
1537 printf("\n");
1538
1539 printf("<device> is the serial port the device is connected at.\n");
1540 printf("\n");
1541
1542 printf("<options> are any of the following:\n");
1543 for (i = 0; i< wu_num_options; i++) {
1544 n = printf(" -%c", wu_options[i].shortopt);
1545
1546 if (wu_options[i].param == 0)
1547 n = printf(" ");
1548 else if (wu_options[i].param == 1)
1549 n = printf(" %s", wu_options[i].option);
1550 else if (wu_options[i].param == 2)
1551 n = printf(" [%s]", wu_options[i].option);
1552
1553 n += printf("%*c| ", n - 12, ' ');
1554 n += printf("--%s", wu_options[i].longopt);
1555
1556 if (wu_options[i].param == 0)
1557 n += printf(" ");
1558 else if (wu_options[i].param == 1)
1559 n += printf("=%s", wu_options[i].option);
1560 else if (wu_options[i].param == 2)
1561 n += printf("[=%s]", wu_options[i].option);
1562
1563 printf("%*c%s\n",
1564 40 - n, ' ', wu_options[i].descr);
1565 }
1566 printf("\n");
1567 printf("<value> specifies which of these to print out (default: ALL)\n");
1568 for (i = 0; i< wu_num_fields; i++) {
1569 printf("%12s -- %s\n", wu_fields[i].name, wu_fields[i].descr);
1570 }
1571 printf("\n");
1572
1573 return 0;
1574 }
1575
1576
1577 static char * wu_option_value(unsigned int index)
1578 {
1579 return (index< wu_num_options) ? wu_options[index].value : NULL;
1580 }
1581
1582
1583 static int wu_check_option_show(int index)
1584 {
1585 /*
1586 * Return 1 if we need to print something out for
1587 * a particular option.
1588 */
1589 if (index< wu_num_options) {
1590 if (wu_options[index].flag) {
1591 return 1;
1592 }
1593 }
1594 return 0;
1595 }
1596
1597 static int wu_check_option_store(int index)
1598 {
1599 /*
1600 * Return a 1 if this option is set.
1601 */
1602
1603 if (index< wu_num_options) {
1604 if (wu_options[index].flag) {
1605 return 1;
1606 }
1607 }
1608 return 0;
1609 }
1610
1611
1612 static int wu_show(int index, int dev_fd)
1613 {
1614 if (wu_options[index].show)
1615 return wu_options[index].show(dev_fd);
1616 return 0;
1617 }
1618
1619 /*
1620 * Check if the option is set, and call its method if so.
1621 * Return whether or not we did anything..
1622 */
1623
1624 static int wu_check_show(int index, int dev_fd)
1625 {
1626 if (wu_check_option_show(index)) {
1627 wu_show(index, dev_fd);
1628 return 1;
1629 }
1630 return 0;
1631 }
1632
1633
1634 /*
1635 * Check if the option is set and if so, call it
1636 * Return the value from the ->store() method.
1637 */
1638
1639 static int wu_check_store(int index, int dev_fd)
1640 {
1641 if (wu_check_option_store(index)) {
1642 if (wu_options[index].store)
1643 return wu_options[index].store(dev_fd);
1644 }
1645 return 0;
1646 }
1647
1648
1649 static void make_longopt(struct option * l)
1650 {
1651 int i;
1652
1653 for (i = 0; i< wu_num_options; i++) {
1654 l[i].name = wu_options[i].longopt;
1655 l[i].has_arg = wu_options[i].param;
1656 l[i].flag =&wu_options[i].flag;
1657 l[i].val = 0;
1658 }
1659 }
1660
1661 static void make_shortopt(char * str)
1662 {
1663 int i;
1664 char * s = str;
1665
1666 for (i = 0; i< wu_num_options; i++) {
1667 *s++ = wu_options[i].shortopt;
1668 if (wu_options[i].param)
1669 *s++ = wu_options[i].param == 1 ? ':' : ';';
1670 }
1671 }
1672
1673 static void enable_short_option(int c, char * arg)
1674 {
1675 int i;
1676
1677 /*
1678 * Friggin' getopt_long() will return the
1679 * character if we get a short option (e.g. '-h'),
1680 * instead of returning 0 like it does when it
1681 * gets a long option (e.g. "--help"). Ugh.
1682 */
1683 for (i = 0; i< wu_num_options; i++) {
1684 if (wu_options[i].shortopt == c) {
1685 wu_options[i].flag = 1;
1686 if (arg)
1687 wu_options[i].value = strdup(arg);
1688 break;
1689 }
1690 }
1691 }
1692
1693 static int parse_args(int argc, char ** argv)
1694 {
1695 struct option longopts[wu_num_options + 1] = { };
1696 char shortopts[wu_num_options * 2] = "";
1697
1698 make_longopt(longopts);
1699 make_shortopt(shortopts);
1700
1701
1702 while (1) {
1703 int c;
1704 int index;
1705
1706 c = getopt_long(argc, argv, shortopts,
1707 longopts,&index);
1708 if (c == -1)
1709 break;
1710
1711 switch (c) {
1712 case 0:
1713 wu_options[index].flag = 1;
1714 if (optarg)
1715 wu_options[index].value = strdup(optarg);
1716
1717 printf("long option: val = %c, optarg = %s\n",
1718 wu_options[index].shortopt, optarg);
1719 break;
1720 case '?':
1721 err("Bad parameter");
1722 return ret_err(EINVAL);
1723 break;
1724 default:
1725 enable_short_option(c, optarg);
1726 break;
1727 }
1728 }
1729
1730 /*
1731 * Check for help request now and bail after
1732 * printing it, if it's set.
1733 */
1734 if (wu_check_show(wu_option_help, 0))
1735 exit(0);
1736
1737 if (wu_check_show(wu_option_version, 0))
1738 exit(0);
1739
1740 /*
1741 * Fields to print out
1742 */
1743 if (optind< argc) {
1744 int i;
1745
1746 wu_device = argv[optind++];
1747
1748 if (optind< argc) {
1749 for (i = optind; i< argc; i++)
1750 enable_field(argv[i]);
1751 } else
1752 enable_all_fields();
1753
1754 } else {
1755 wu_show(wu_option_help, 0);
1756 return ret_err(EINVAL);
1757 }
1758 return 0;
1759 }
1760
1761
1762 int main(int argc, char ** argv)
1763 {
1764 int ret;
1765 int fd = 0;
1766
1767 ret = parse_args(argc, argv);
1768 if (ret)
1769 return 0;
1770
1771 /*
1772 * Try to enable debugging early
1773 */
1774 if ((ret = wu_check_store(wu_option_debug, 0)))
1775 goto Close;
1776
1777 ret = open_device(wu_device,&fd);
1778 if (ret)
1779 return ret;
1780
1781 dbg("%s: Open for business", wu_device);
1782
1783 ret = setup_serial_device(fd);
1784 if (ret)
1785 goto Close;
1786
1787 wu_clear(fd);
1788
1789 wu_fd = fd;
1790
1791 /*
1792 * Set delimeter before we print out any fields.
1793 */
1794 if ((ret = wu_check_store(wu_option_delim, fd)))
1795 goto Close;
1796
1797 /*
1798 * Ditto for 'label' and 'newline' flags.
1799 */
1800 if ((ret = wu_check_store(wu_option_label, fd)))
1801 goto Close;
1802
1803 if ((ret = wu_check_store(wu_option_newline, fd)))
1804 goto Close;
1805
1806 if ((ret = wu_check_store(wu_option_suppress, fd)))
1807 goto Close;
1808
1809 if ((ret = wu_check_store(wu_option_localtime, fd)))
1810 goto Close;
1811
1812 if ((ret = wu_check_store(wu_option_gmtime, fd)))
1813 goto Close;
1814
1815 if ((ret = wu_check_store(wu_option_set_only, fd)))
1816 goto Close;
1817
1818 if ((ret = wu_check_store(wu_option_no_data, fd)))
1819 goto Close;
1820
1821 if ((ret = wu_check_store(wu_option_info_all, fd)))
1822 goto Close;
1823
1824
1825 /*
1826 * Options to set device parameters.
1827 */
1828 if ((ret = wu_check_store(wu_option_interval, fd)))
1829 goto Close;
1830
1831 if ((ret = wu_check_store(wu_option_mode, fd)))
1832 goto Close;
1833
1834 if ((ret = wu_check_store(wu_option_user, fd)))
1835 goto Close;
1836
1837 /*
1838 * Check for options to print device info
1839 */
1840 if (wu_info_all) {
1841 wu_show(wu_option_cal, fd);
1842 wu_show(wu_option_header, fd);
1843 wu_show(wu_option_interval, fd);
1844 wu_show(wu_option_mode, fd);
1845 wu_show(wu_option_user, fd);
1846 } else {
1847 wu_check_show(wu_option_cal, fd);
1848 wu_check_show(wu_option_header, fd);
1849
1850 if (!wu_set_only) {
1851 wu_check_show(wu_option_interval, fd);
1852 wu_check_show(wu_option_mode, fd);
1853 wu_check_show(wu_option_user, fd);
1854 }
1855 }
1856
1857 if (!wu_no_data) {
1858
1859 if ((ret = wu_check_store(wu_option_count, fd)))
1860 goto Close;
1861
1862 if ((ret = wu_check_store(wu_option_final, fd)))
1863 goto Close;
1864
1865 if ((ret = wu_start_log()))
1866 goto Close;
1867
1868 wu_read_data(fd);
1869
1870 wu_stop_log();
1871 }
1872 Close:
1873 close(fd);
1874 return ret;
1875 }
1876
1877