![florian](/assets/img/avatar_default.png)
This patch adds a command line utility to interface with the WattsUp power meters (https://www.wattsupmeters.com/) Signed-off-by: Roberto Riggio<roberto.riggio@create-net.org> git-svn-id: svn://svn.openwrt.org/openwrt/packages@26380 3c298f89-4303-0410-b956-a3cf2f4a3e73
1878 lines
32 KiB
C
1878 lines
32 KiB
C
/*
|
|
* wattsup - Program for controlling the Watts Up? Pro Device
|
|
*
|
|
*
|
|
* Copyright (c) 2005 Patrick Mochel
|
|
*
|
|
* This program is released under the GPLv2
|
|
*
|
|
*
|
|
* Compiled with:
|
|
*
|
|
* gcc -O2 -Wall -o wattsup wattsup.c
|
|
*
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include<stdio.h>
|
|
#include<stdlib.h>
|
|
#include<stdarg.h>
|
|
#include<string.h>
|
|
#include<errno.h>
|
|
#include<unistd.h>
|
|
#include<fcntl.h>
|
|
#include<termios.h>
|
|
#include<ctype.h>
|
|
#include<getopt.h>
|
|
#include<signal.h>
|
|
#include<time.h>
|
|
|
|
#include<sys/stat.h>
|
|
|
|
static const char * wu_version = "0.02";
|
|
|
|
|
|
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
|
|
|
|
static const char * prog_name = "wattsup";
|
|
|
|
static const char * sysfs_path_start = "/sys/class/tty";
|
|
|
|
static char * wu_device = "ttyUSB0";
|
|
static int wu_fd = 0;
|
|
static int wu_count = 0;
|
|
static int wu_debug = 0;
|
|
static char *wu_delim = ", ";
|
|
static int wu_final = 0;
|
|
static int wu_interval = 1;
|
|
static int wu_label = 0;
|
|
static int wu_newline = 0;
|
|
static int wu_suppress = 0;
|
|
|
|
static int wu_localtime = 0;
|
|
static int wu_gmtime = 0;
|
|
|
|
static int wu_info_all = 0;
|
|
static int wu_no_data = 0;
|
|
static int wu_set_only = 0;
|
|
|
|
#define wu_strlen 256
|
|
#define wu_num_fields 18
|
|
#define wu_param_len 16
|
|
|
|
struct wu_packet {
|
|
unsigned int cmd;
|
|
unsigned int sub_cmd;
|
|
unsigned int count;
|
|
char buf[wu_strlen];
|
|
int len;
|
|
char * field[wu_num_fields];
|
|
char * label[wu_num_fields];
|
|
};
|
|
|
|
|
|
struct wu_data {
|
|
unsigned int watts;
|
|
unsigned int volts;
|
|
unsigned int amps;
|
|
unsigned int watt_hours;
|
|
|
|
unsigned int cost;
|
|
unsigned int mo_kWh;
|
|
unsigned int mo_cost;
|
|
unsigned int max_watts;
|
|
|
|
unsigned int max_volts;
|
|
unsigned int max_amps;
|
|
unsigned int min_watts;
|
|
unsigned int min_volts;
|
|
|
|
unsigned int min_amps;
|
|
unsigned int power_factor;
|
|
unsigned int duty_cycle;
|
|
unsigned int power_cycle;
|
|
};
|
|
|
|
struct wu_options {
|
|
char * longopt;
|
|
int shortopt;
|
|
int param;
|
|
int flag;
|
|
char * value;
|
|
|
|
char * descr;
|
|
char * option;
|
|
char * format;
|
|
|
|
int (*show)(int dev_fd);
|
|
int (*store)(int dev_fd);
|
|
};
|
|
|
|
enum {
|
|
wu_option_help = 0,
|
|
wu_option_version,
|
|
|
|
wu_option_debug,
|
|
|
|
wu_option_count,
|
|
wu_option_final,
|
|
|
|
wu_option_delim,
|
|
wu_option_newline,
|
|
wu_option_localtime,
|
|
wu_option_gmtime,
|
|
wu_option_label,
|
|
|
|
wu_option_suppress,
|
|
|
|
wu_option_cal,
|
|
wu_option_header,
|
|
|
|
wu_option_interval,
|
|
wu_option_mode,
|
|
wu_option_user,
|
|
|
|
wu_option_info_all,
|
|
wu_option_no_data,
|
|
wu_option_set_only,
|
|
};
|
|
|
|
|
|
static char * wu_option_value(unsigned int index);
|
|
|
|
|
|
enum {
|
|
wu_field_watts = 0,
|
|
wu_field_volts,
|
|
wu_field_amps,
|
|
|
|
wu_field_watt_hours,
|
|
wu_field_cost,
|
|
wu_field_mo_kwh,
|
|
wu_field_mo_cost,
|
|
|
|
wu_field_max_watts,
|
|
wu_field_max_volts,
|
|
wu_field_max_amps,
|
|
|
|
wu_field_min_watts,
|
|
wu_field_min_volts,
|
|
wu_field_min_amps,
|
|
|
|
wu_field_power_factor,
|
|
wu_field_duty_cycle,
|
|
wu_field_power_cycle,
|
|
};
|
|
|
|
struct wu_field {
|
|
unsigned int enable;
|
|
char * name;
|
|
char * descr;
|
|
};
|
|
|
|
static struct wu_field wu_fields[wu_num_fields] = {
|
|
[wu_field_watts] = {
|
|
.name = "watts",
|
|
.descr = "Watt Consumption",
|
|
},
|
|
|
|
[wu_field_min_watts] = {
|
|
.name = "min-watts",
|
|
.descr = "Minimum Watts Consumed",
|
|
},
|
|
|
|
[wu_field_max_watts] = {
|
|
.name = "max-watts",
|
|
.descr = "Maxium Watts Consumed",
|
|
},
|
|
|
|
[wu_field_volts] = {
|
|
.name = "volts",
|
|
.descr = "Volts Consumption",
|
|
},
|
|
|
|
[wu_field_min_volts] = {
|
|
.name = "max-volts",
|
|
.descr = "Minimum Volts Consumed",
|
|
},
|
|
|
|
[wu_field_max_volts] = {
|
|
.name = "min-volts",
|
|
.descr = "Maximum Volts Consumed",
|
|
},
|
|
|
|
[wu_field_amps] = {
|
|
.name = "amps",
|
|
.descr = "Amp Consumption",
|
|
},
|
|
|
|
[wu_field_min_amps] = {
|
|
.name = "min-amps",
|
|
.descr = "Minimum Amps Consumed",
|
|
},
|
|
|
|
[wu_field_max_amps] = {
|
|
.name = "max-amps",
|
|
.descr = "Maximum Amps Consumed",
|
|
},
|
|
|
|
[wu_field_watt_hours] = {
|
|
.name = "kwh",
|
|
.descr = "Average KWH",
|
|
},
|
|
|
|
[wu_field_mo_kwh] = {
|
|
.name = "mo-kwh",
|
|
.descr = "Average monthly KWH",
|
|
},
|
|
|
|
[wu_field_cost] = {
|
|
.name = "cost",
|
|
.descr = "Cost per watt",
|
|
},
|
|
|
|
[wu_field_mo_cost] = {
|
|
.name = "mo-cost",
|
|
.descr = "Monthly Cost",
|
|
},
|
|
|
|
[wu_field_power_factor] = {
|
|
.name = "power-factor",
|
|
.descr = "Ratio of Watts vs. Volt Amps",
|
|
},
|
|
|
|
[wu_field_duty_cycle] = {
|
|
.name = "duty-cycle",
|
|
.descr = "Percent of the Time On vs. Time Off",
|
|
},
|
|
|
|
[wu_field_power_cycle] = {
|
|
.name = "power-cycle",
|
|
.descr = "Indication of power cycle",
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static void msg_start(const char * fmt, ...)
|
|
{
|
|
va_list(ap);
|
|
va_start(ap, fmt);
|
|
vprintf(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
static void msg_end(void)
|
|
{
|
|
printf("\n");
|
|
}
|
|
|
|
static void msg(const char * fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
vprintf(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
static void dbg(const char * fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (wu_debug) {
|
|
va_start(ap, fmt);
|
|
msg_start("%s: [debug] ", prog_name);
|
|
vprintf(fmt, ap);
|
|
msg_end();
|
|
va_end(ap);
|
|
}
|
|
}
|
|
|
|
static void err(const char * fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
fprintf(stderr, "%s: [error] ", prog_name);
|
|
vfprintf(stderr, fmt, ap);
|
|
fprintf(stderr, "\n");
|
|
va_end(ap);
|
|
}
|
|
|
|
static void perr(const char * fmt, ...)
|
|
{
|
|
char buf[1024];
|
|
int n;
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
n = sprintf(buf, "%s: [error] ", prog_name);
|
|
vsnprintf(buf + n, sizeof(buf) - n, fmt, ap);
|
|
perror(buf);
|
|
va_end(ap);
|
|
}
|
|
|
|
static int ret_err(int err)
|
|
{
|
|
errno = err;
|
|
return -1;
|
|
}
|
|
|
|
|
|
static void print_packet(struct wu_packet * p, char * str)
|
|
{
|
|
int i;
|
|
|
|
if (!wu_suppress)
|
|
msg_start("Watts Up? %s\n", str);
|
|
for (i = 0; i< p->count; i++) {
|
|
if (i)
|
|
msg("%s", wu_newline ? "\n" : wu_delim);
|
|
if (wu_label)
|
|
msg("[%s] ", p->label[i]);
|
|
msg(p->field[i]);
|
|
}
|
|
msg_end();
|
|
}
|
|
|
|
|
|
static void print_time(void)
|
|
{
|
|
time_t t;
|
|
struct tm * tm;
|
|
|
|
if (wu_localtime || wu_gmtime) {
|
|
time(&t);
|
|
|
|
if (wu_localtime)
|
|
tm = localtime(&t);
|
|
else
|
|
tm = gmtime(&t);
|
|
|
|
msg("[%02d:%02d:%02d] ",
|
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
|
}
|
|
}
|
|
|
|
static void print_packet_filter(struct wu_packet * p,
|
|
int (*filter_ok)(struct wu_packet * p, int i, char * str))
|
|
{
|
|
char buf[256];
|
|
int printed;
|
|
int i;
|
|
|
|
print_time();
|
|
for (i = 0, printed = 0; i< p->count; i++) {
|
|
if (!filter_ok(p, i, buf))
|
|
continue;
|
|
|
|
if (printed++)
|
|
msg("%s", wu_newline ? "\n" : wu_delim);
|
|
if (wu_label)
|
|
msg("[%s] ", p->label[i]);
|
|
msg(buf);
|
|
}
|
|
msg_end();
|
|
}
|
|
|
|
|
|
/*
|
|
* Device should be something like "ttyS0"
|
|
*/
|
|
|
|
static int open_device(char * device_name, int * dev_fd)
|
|
{
|
|
struct stat s;
|
|
int ret;
|
|
int cur_fd;
|
|
|
|
cur_fd = open(".", O_RDONLY);
|
|
if (cur_fd< 0) {
|
|
perr("Could not open current directory.");
|
|
return cur_fd;
|
|
}
|
|
|
|
ret = chdir(sysfs_path_start);
|
|
if (ret) {
|
|
perr(sysfs_path_start);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* First, check if /sys/class/tty/<name>/ exists.
|
|
*/
|
|
|
|
dbg("Checking sysfs path: %s/%s", sysfs_path_start, device_name);
|
|
|
|
ret = stat(device_name,&s);
|
|
if (ret< 0) {
|
|
perr(device_name);
|
|
goto Done;
|
|
}
|
|
|
|
if (!S_ISDIR(s.st_mode)) {
|
|
errno = -ENOTDIR;
|
|
err("%s is not a TTY device.", device_name);
|
|
goto Done;
|
|
}
|
|
|
|
dbg("%s is a registered TTY device", device_name);
|
|
|
|
fchdir(cur_fd);
|
|
|
|
|
|
/*
|
|
* Check if device node exists and is writable
|
|
*/
|
|
chdir("/dev");
|
|
|
|
ret = stat(device_name,&s);
|
|
if (ret< 0) {
|
|
perr("/dev/%s (device node)", device_name);
|
|
goto Done;
|
|
}
|
|
|
|
if (!S_ISCHR(s.st_mode)) {
|
|
errno = -ENOTTY;
|
|
ret = -1;
|
|
err("%s is not a TTY character device.", device_name);
|
|
goto Done;
|
|
}
|
|
|
|
dbg("%s has a device node", device_name);
|
|
|
|
ret = access(device_name, R_OK | W_OK);
|
|
if (ret) {
|
|
perr("%s: Not writable?", device_name);
|
|
goto Done;
|
|
}
|
|
|
|
ret = open(device_name, O_RDWR | O_NONBLOCK);
|
|
if (ret< 0) {
|
|
perr("Could not open %s");
|
|
goto Done;
|
|
}
|
|
*dev_fd = ret;
|
|
ret = 0;
|
|
Done:
|
|
fchdir(cur_fd);
|
|
close(cur_fd);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int setup_serial_device(int dev_fd)
|
|
{
|
|
struct termios t;
|
|
int ret;
|
|
|
|
ret = tcgetattr(dev_fd,&t);
|
|
if (ret)
|
|
return ret;
|
|
|
|
cfmakeraw(&t);
|
|
cfsetispeed(&t, B115200);
|
|
cfsetospeed(&t, B115200);
|
|
tcflush(dev_fd, TCIFLUSH);
|
|
|
|
t.c_iflag |= IGNPAR;
|
|
t.c_cflag&= ~CSTOPB;
|
|
ret = tcsetattr(dev_fd, TCSANOW,&t);
|
|
|
|
if (ret) {
|
|
perr("setting terminal attributes");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wu_write(int fd, struct wu_packet * p)
|
|
{
|
|
int ret;
|
|
int n;
|
|
int i;
|
|
char * s = p->buf;
|
|
|
|
memset(p->buf, 0, sizeof(p->buf));
|
|
n = sprintf(p->buf, "#%c,%c,%d", p->cmd, p->sub_cmd, p->count);
|
|
p->len = n;
|
|
s = p->buf + n;
|
|
|
|
for (i = 0; i< p->count; i++) {
|
|
if ((p->len + strlen(p->field[i]) + 4)>= sizeof(p->buf)) {
|
|
err("Overflowed command string");
|
|
return ret_err(EOVERFLOW);
|
|
}
|
|
n = sprintf(s, ",%s", p->field[i]);
|
|
s += n;
|
|
p->len += n;
|
|
}
|
|
p->buf[p->len++] = ';';
|
|
|
|
dbg("Writing '%s' (strlen = %d) (len = %d) to device",
|
|
p->buf, strlen(p->buf), p->len);
|
|
ret = write(fd, p->buf, p->len);
|
|
if (ret != p->len)
|
|
perr("Writing to device");
|
|
|
|
return ret>= 0 ? 0 : ret;
|
|
}
|
|
|
|
|
|
static void dump_packet(struct wu_packet * p)
|
|
{
|
|
int i;
|
|
|
|
dbg("Packet - Command '%c' %d parameters", p->cmd, p->count);
|
|
|
|
for (i = 0; i< p->count; i++)
|
|
dbg("[%2d] [%20s] = \"%s\"", i, p->label[i], p->field[i]);
|
|
}
|
|
|
|
|
|
static int parse_packet(struct wu_packet * p)
|
|
{
|
|
char * s, *next;
|
|
int i;
|
|
|
|
p->buf[p->len] = '\0';
|
|
|
|
dbg("Parsing Packet, Raw buffer is (%d bytes) [%s]",
|
|
p->len, p->buf);
|
|
|
|
s = p->buf;
|
|
|
|
/*
|
|
* First character should be '#'
|
|
*/
|
|
if (s) {
|
|
s = strchr(s, '#');
|
|
if (s)
|
|
s++;
|
|
else {
|
|
dbg("Invalid packet");
|
|
return ret_err(EFAULT);
|
|
}
|
|
} else {
|
|
dbg("Invalid packet");
|
|
return ret_err(EFAULT);
|
|
}
|
|
|
|
/*
|
|
* Command character is first
|
|
*/
|
|
next = strchr(s, ',');
|
|
if (next) {
|
|
p->cmd = *s;
|
|
s = ++next;
|
|
} else {
|
|
dbg("Invalid Command field [%s]", s);
|
|
return ret_err(EFAULT);
|
|
}
|
|
|
|
/*
|
|
* Next character is the subcommand, and should be '-'
|
|
* Though, it doesn't matter, because we just
|
|
* discard it anyway.
|
|
*/
|
|
next = strchr(s, ',');
|
|
if (next) {
|
|
p->sub_cmd = *s;
|
|
s = ++next;
|
|
} else {
|
|
dbg("Invalid 2nd field");
|
|
return ret_err(EFAULT);
|
|
}
|
|
|
|
/*
|
|
* Next is the number of parameters,
|
|
* which should always be> 0.
|
|
*/
|
|
next = strchr(s, ',');
|
|
if (next) {
|
|
*next++ = '\0';
|
|
p->count = atoi(s);
|
|
s = next;
|
|
} else {
|
|
dbg("Couldn't determine number of parameters");
|
|
return ret_err(EFAULT);
|
|
}
|
|
|
|
dbg("Have %d parameter%s (cmd = '%c')",
|
|
p->count, p->count> 1 ? "s" : "", p->cmd);
|
|
|
|
/*
|
|
* Now, we loop over the rest of the string,
|
|
* storing a pointer to each in p->field[].
|
|
*
|
|
* The last character was originally a ';', but may have been
|
|
* overwritten with a '\0', so we make sure to catch
|
|
* that when converting the last parameter.
|
|
*/
|
|
for (i = 0; i< p->count; i++) {
|
|
next = strpbrk(s, ",;");
|
|
if (next) {
|
|
*next++ = '\0';
|
|
} else {
|
|
if (i< (p->count - 1)) {
|
|
dbg("Malformed parameter string [%s]", s);
|
|
return ret_err(EFAULT);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Skip leading white space in fields
|
|
*/
|
|
while (isspace(*s))
|
|
s++;
|
|
p->field[i] = s;
|
|
s = next;
|
|
}
|
|
dump_packet(p);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wu_read(int fd, struct wu_packet * p)
|
|
{
|
|
fd_set read_fd;
|
|
struct timeval tv;
|
|
int ret;
|
|
|
|
FD_ZERO(&read_fd);
|
|
FD_SET(fd,&read_fd);
|
|
|
|
tv.tv_sec = 2;
|
|
tv.tv_usec = 0;
|
|
|
|
ret = select(fd + 1,&read_fd, NULL, NULL,&tv);
|
|
if (ret< 0) {
|
|
perr("select on terminal device");
|
|
return ret;
|
|
} else if (ret> 0) {
|
|
|
|
ret = read(fd, p->buf, wu_strlen);
|
|
if (ret< 0) {
|
|
perr("Reading from device");
|
|
return ret;
|
|
}
|
|
p->len = ret;
|
|
} else {
|
|
dbg("Device timed out while reading");
|
|
return ret_err(ETIME);
|
|
}
|
|
return parse_packet(p);
|
|
}
|
|
|
|
|
|
static int wu_show_header(int fd)
|
|
{
|
|
struct wu_packet p = {
|
|
.cmd = 'H',
|
|
.sub_cmd = 'R',
|
|
.count = 0,
|
|
.label = {
|
|
[0] = "watts header",
|
|
[1] = "volts header",
|
|
[2] = "amps header",
|
|
[3] = "kWh header",
|
|
[4] = "cost header",
|
|
[5] = "mo. kWh header",
|
|
[6] = "mo. cost header",
|
|
[7] = "max watts header",
|
|
[8] = "max volts header",
|
|
[9] = "max amps header",
|
|
[10] = "min watts header",
|
|
[11] = "min volts header",
|
|
[12] = "min amps header",
|
|
[13] = "power factor header",
|
|
[14] = "duty cycle header",
|
|
[15] = "power cycle header",
|
|
}
|
|
};
|
|
int ret;
|
|
|
|
ret = wu_write(fd,&p);
|
|
if (ret) {
|
|
perr("Requesting header strings");
|
|
return ret;
|
|
}
|
|
sleep(1);
|
|
|
|
ret = wu_read(fd,&p);
|
|
if (ret) {
|
|
perr("Reading header strings");
|
|
return ret;
|
|
}
|
|
|
|
print_packet(&p, "Header Record");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wu_show_cal(int fd)
|
|
{
|
|
struct wu_packet p = {
|
|
.cmd = 'F',
|
|
.sub_cmd = 'R',
|
|
.count = 0,
|
|
.label = {
|
|
[0] = "flags",
|
|
[1] = "sample count",
|
|
[2] = "volts gain",
|
|
[3] = "volts bias",
|
|
[4] = "amps gain",
|
|
[5] = "amps bias",
|
|
[6] = "amps offset",
|
|
[7] = "low amps gain",
|
|
[8] = "low amps bias",
|
|
[9] = "low amps offset",
|
|
[10] = "watts gain",
|
|
[11] = "watts offset",
|
|
[12] = "low watts gain",
|
|
[13] = "low watts offset",
|
|
},
|
|
};
|
|
int ret;
|
|
|
|
ret = wu_write(fd,&p);
|
|
if (ret) {
|
|
perr("Requesting calibration parameters");
|
|
return ret;
|
|
}
|
|
sleep(1);
|
|
|
|
ret = wu_read(fd,&p);
|
|
if (ret) {
|
|
perr("Reading header strings");
|
|
return ret;
|
|
}
|
|
print_packet(&p, "Calibration Settings");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wu_start_log(void)
|
|
{
|
|
struct wu_packet p = {
|
|
.cmd = 'L',
|
|
.sub_cmd = 'W',
|
|
.count = 3,
|
|
.field = {
|
|
[0] = "E",
|
|
[1] = "1",
|
|
[2] = "1",
|
|
},
|
|
};
|
|
int ret;
|
|
|
|
/*
|
|
* Start up logging
|
|
*/
|
|
ret = wu_write(wu_fd,&p);
|
|
if (!ret)
|
|
sleep(1);
|
|
else {
|
|
perr("Starting External Logging");
|
|
return ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int wu_stop_log(void)
|
|
{
|
|
struct wu_packet p = {
|
|
.cmd = 'L',
|
|
.sub_cmd = 'R',
|
|
.count = 0,
|
|
.label = {
|
|
[0] = "time stamp",
|
|
[1] = "interval",
|
|
},
|
|
};
|
|
int ret;
|
|
|
|
/*
|
|
* Stop logging and read time stamp.
|
|
*/
|
|
ret = wu_write(wu_fd,&p);
|
|
if (ret) {
|
|
perr("Stopping External Logging");
|
|
return ret;
|
|
}
|
|
sleep(1);
|
|
|
|
ret = wu_read(wu_fd,&p);
|
|
if (ret) {
|
|
perr("Reading final time stamp");
|
|
return ret;
|
|
}
|
|
if (wu_final)
|
|
print_packet(&p, "Final Time Stamp and Interval");
|
|
return ret;
|
|
}
|
|
|
|
static int filter_data(struct wu_packet * p, int i, char * buf)
|
|
{
|
|
if (i< wu_num_fields) {
|
|
if (wu_fields[i].enable) {
|
|
double val = strtod(p->field[i], NULL);
|
|
snprintf(buf, 256, "%.1f", val / 10.0);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int wu_clear(int fd)
|
|
{
|
|
struct wu_packet p = {
|
|
.cmd = 'R',
|
|
.sub_cmd = 'W',
|
|
.count = 0,
|
|
};
|
|
int ret;
|
|
|
|
/*
|
|
* Clear the memory
|
|
*/
|
|
ret = wu_write(fd,&p);
|
|
if (ret)
|
|
perr("Clearing memory");
|
|
else
|
|
sleep(2);
|
|
|
|
/*
|
|
* Dummy read
|
|
*/
|
|
wu_read(fd,&p);
|
|
return ret;
|
|
|
|
}
|
|
|
|
static int wu_read_data(int fd)
|
|
{
|
|
struct wu_packet p = {
|
|
.label = {
|
|
[0] = "watts",
|
|
[1] = "volts",
|
|
[2] = "amps",
|
|
[3] = "watt hours",
|
|
[4] = "cost",
|
|
[5] = "mo. kWh",
|
|
[6] = "mo. cost",
|
|
[7] = "max watts",
|
|
[8] = "max volts",
|
|
[9] = "max amps",
|
|
[10] = "min watts",
|
|
[11] = "min volts",
|
|
[12] = "min amps",
|
|
[13] = "power factor",
|
|
[14] = "duty cycle",
|
|
[15] = "power cycle",
|
|
},
|
|
};
|
|
int num_read = 0;
|
|
int retry = 0;
|
|
int ret;
|
|
int i;
|
|
|
|
static const int wu_max_retry = 2;
|
|
|
|
i = 0;
|
|
while (1) {
|
|
|
|
ret = wu_read(fd,&p);
|
|
if (ret) {
|
|
if (++retry< wu_max_retry) {
|
|
dbg("Bad record back, retrying\n");
|
|
sleep(wu_interval);
|
|
continue;
|
|
} else if (retry == wu_max_retry) {
|
|
dbg("Still couldn't get a good record, resetting\n");
|
|
wu_stop_log();
|
|
wu_clear(fd);
|
|
wu_start_log();
|
|
num_read = 0;
|
|
sleep(wu_interval);
|
|
continue;
|
|
}
|
|
perr("Blech. Giving up on read");
|
|
break;
|
|
} else if (retry)
|
|
retry = 0;
|
|
|
|
dbg("[%d] ", num_read);
|
|
num_read++;
|
|
print_packet_filter(&p, filter_data);
|
|
|
|
if (wu_count&& (++i == wu_count))
|
|
break;
|
|
|
|
sleep(wu_interval);
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
static int wu_show_interval(int fd)
|
|
{
|
|
struct wu_packet p = {
|
|
.cmd = 'S',
|
|
.sub_cmd = 'R',
|
|
.count = 0,
|
|
.label = {
|
|
[0] = "reserved",
|
|
[1] = "interval",
|
|
},
|
|
};
|
|
int ret;
|
|
|
|
ret = wu_write(fd,&p);
|
|
if (ret) {
|
|
perr("Requesting interval");
|
|
return ret;
|
|
}
|
|
sleep(1);
|
|
|
|
ret = wu_read(fd,&p);
|
|
if (ret) {
|
|
perr("Reading interval");
|
|
return ret;
|
|
}
|
|
print_packet(&p, "Interval Settings");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wu_write_interval(int fd, unsigned int seconds,
|
|
unsigned int interval)
|
|
{
|
|
char str_seconds[wu_param_len];
|
|
char str_interval[wu_param_len];
|
|
struct wu_packet p = {
|
|
.cmd = 'S',
|
|
.sub_cmd = 'W',
|
|
.count = 2,
|
|
.field = {
|
|
[0] = str_seconds,
|
|
[1] = str_interval,
|
|
},
|
|
};
|
|
int ret;
|
|
|
|
snprintf(str_seconds, wu_param_len, "%ud", seconds);
|
|
snprintf(str_interval, wu_param_len, "%ud", interval);
|
|
|
|
ret = wu_write(fd,&p);
|
|
if (ret) {
|
|
perr("Setting Sampling Interval");
|
|
return ret;
|
|
}
|
|
sleep(1);
|
|
return 0;
|
|
}
|
|
|
|
static int wu_store_interval(int fd)
|
|
{
|
|
char * s = wu_option_value(wu_option_interval);
|
|
char * end;
|
|
|
|
wu_interval = strtol(s,&end, 0);
|
|
if (*end) {
|
|
err("Invalid interval: %s", s);
|
|
return ret_err(EINVAL);
|
|
}
|
|
return wu_write_interval(fd, 1, wu_interval);
|
|
}
|
|
|
|
static int wu_show_mode(int fd)
|
|
{
|
|
struct wu_packet p = {
|
|
.cmd = 'M',
|
|
.sub_cmd = 'R',
|
|
.count = 0,
|
|
.label = {
|
|
[0] = "display mode",
|
|
},
|
|
};
|
|
int ret;
|
|
|
|
ret = wu_write(fd,&p);
|
|
if (ret) {
|
|
perr("Requesting device display mode");
|
|
return ret;
|
|
}
|
|
|
|
ret = wu_read(fd,&p);
|
|
if (ret) {
|
|
perr("Reaing device display mode");
|
|
return ret;
|
|
}
|
|
dump_packet(&p);
|
|
return ret;
|
|
}
|
|
|
|
static int wu_write_mode(int fd, int mode)
|
|
{
|
|
char str_mode[wu_param_len];
|
|
struct wu_packet p = {
|
|
.cmd = 'M',
|
|
.sub_cmd = 'W',
|
|
.count = 1,
|
|
.field = {
|
|
[0] = str_mode,
|
|
},
|
|
};
|
|
int ret;
|
|
|
|
snprintf(str_mode, wu_param_len, "%ud", mode);
|
|
ret = wu_write(fd,&p);
|
|
if (ret)
|
|
perr("Setting device display mode");
|
|
else
|
|
sleep(1);
|
|
return ret;
|
|
}
|
|
|
|
static int wu_store_mode(int fd)
|
|
{
|
|
char * s = wu_option_value(wu_option_mode);
|
|
char * end;
|
|
unsigned int mode;
|
|
|
|
mode = strtol(s,&end, 0);
|
|
if (*end) {
|
|
err("Invalid mode: %s", s);
|
|
return ret_err(EINVAL);
|
|
}
|
|
return wu_write_mode(fd, mode);
|
|
}
|
|
|
|
|
|
|
|
static int wu_show_user(int fd)
|
|
{
|
|
struct wu_packet p = {
|
|
.cmd = 'U',
|
|
.sub_cmd = 'R',
|
|
.count = 0,
|
|
.label = {
|
|
[0] = "cost per kWh",
|
|
[1] = "2nd tier cost",
|
|
[2] = "2nd tier threshold",
|
|
[3] = "duty cycle threshold",
|
|
},
|
|
};
|
|
int ret;
|
|
|
|
ret = wu_write(fd,&p);
|
|
if (ret) {
|
|
perr("Requesting user parameters");
|
|
return ret;
|
|
}
|
|
sleep(1);
|
|
|
|
ret = wu_read(fd,&p);
|
|
if (ret) {
|
|
perr("Reading user parameters");
|
|
return ret;
|
|
}
|
|
print_packet(&p, "User Settings");
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wu_write_user(int fd, unsigned int kwh_cost,
|
|
unsigned int second_tier_cost,
|
|
unsigned int second_tier_threshold,
|
|
unsigned int duty_cycle_threshold)
|
|
{
|
|
char str_kwh_cost[wu_param_len];
|
|
char str_2nd_tier_cost[wu_param_len];
|
|
char str_2nd_tier_threshold[wu_param_len];
|
|
char str_duty_cycle_threshold[wu_param_len];
|
|
|
|
struct wu_packet p = {
|
|
.cmd = 'U',
|
|
.sub_cmd = 'R',
|
|
.count = 0,
|
|
.label = {
|
|
[0] = str_kwh_cost,
|
|
[1] = str_2nd_tier_cost,
|
|
[2] = str_2nd_tier_threshold,
|
|
[3] = str_duty_cycle_threshold,
|
|
},
|
|
};
|
|
int ret;
|
|
|
|
snprintf(str_kwh_cost, wu_param_len, "%ud", kwh_cost);
|
|
snprintf(str_2nd_tier_cost, wu_param_len, "%ud",
|
|
second_tier_cost);
|
|
snprintf(str_2nd_tier_threshold, wu_param_len, "%ud",
|
|
second_tier_threshold);
|
|
snprintf(str_duty_cycle_threshold, wu_param_len, "%ud",
|
|
duty_cycle_threshold);
|
|
|
|
ret = wu_write(fd,&p);
|
|
if (ret)
|
|
perr("Writing user parameters");
|
|
else
|
|
sleep(1);
|
|
return ret;
|
|
}
|
|
|
|
static int wu_store_user(int fd)
|
|
{
|
|
unsigned int kwh_cost;
|
|
unsigned int second_tier_cost;
|
|
unsigned int second_tier_threshold;
|
|
unsigned int duty_cycle_threshold;
|
|
char * buf = wu_option_value(wu_option_user);
|
|
char * s = buf;
|
|
char * next;
|
|
|
|
if (!buf) {
|
|
err("No user parameters?");
|
|
return ret_err(EINVAL);
|
|
}
|
|
|
|
kwh_cost = strtoul(s,&next, 0);
|
|
if (next == s) {
|
|
err("Incomplete user parameters");
|
|
return ret_err(EINVAL);
|
|
}
|
|
|
|
s = next;
|
|
while (s&& !isdigit(*s))
|
|
s++;
|
|
if (!s) {
|
|
err("Incomplete user parameters");
|
|
return ret_err(EINVAL);
|
|
}
|
|
|
|
|
|
second_tier_cost = strtoul(s,&next, 0);
|
|
if (next == s) {
|
|
err("Incomplete user parameters");
|
|
return ret_err(EINVAL);
|
|
}
|
|
|
|
s = next;
|
|
while (s&& !isdigit(*s))
|
|
s++;
|
|
if (!s) {
|
|
err("Incomplete user parameters");
|
|
return ret_err(EINVAL);
|
|
}
|
|
|
|
|
|
second_tier_threshold = strtoul(s,&next, 0);
|
|
if (next == s) {
|
|
err("Incomplete user parameters");
|
|
return ret_err(EINVAL);
|
|
}
|
|
|
|
s = next;
|
|
while (s&& !isdigit(*s))
|
|
s++;
|
|
if (!s) {
|
|
err("Incomplete user parameters");
|
|
return ret_err(EINVAL);
|
|
}
|
|
|
|
|
|
duty_cycle_threshold = strtoul(s,&next, 0);
|
|
if (next == s) {
|
|
err("Incomplete user parameters");
|
|
return ret_err(EINVAL);
|
|
}
|
|
|
|
s = next;
|
|
while (s&& !isdigit(*s))
|
|
s++;
|
|
if (!s) {
|
|
err("Incomplete user parameters");
|
|
return ret_err(EINVAL);
|
|
}
|
|
|
|
return wu_write_user(fd, kwh_cost, second_tier_cost,
|
|
second_tier_threshold, duty_cycle_threshold);
|
|
}
|
|
|
|
|
|
static void enable_field(char * name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i< wu_num_fields; i++) {
|
|
if (!strcasecmp(wu_fields[i].name, name)) {
|
|
wu_fields[i].enable = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void enable_all_fields(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i< wu_num_fields; i++)
|
|
wu_fields[i].enable = 1;
|
|
}
|
|
|
|
|
|
|
|
static int wu_show_help(int);
|
|
static int wu_show_version(int);
|
|
|
|
|
|
|
|
static int wu_store_count(int unused)
|
|
{
|
|
char * s = wu_option_value(wu_option_count);
|
|
char * end;
|
|
|
|
if (s) {
|
|
wu_count = strtol(s,&end, 0);
|
|
if (*end) {
|
|
err("Bad count field");
|
|
return ret_err(EINVAL);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int wu_store_debug(int unused)
|
|
{
|
|
wu_debug = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int wu_store_delim(int unused)
|
|
{
|
|
char * s = wu_option_value(wu_option_delim);
|
|
|
|
if (s)
|
|
wu_delim = s;
|
|
return 0;
|
|
}
|
|
|
|
static int wu_store_final(int unused)
|
|
{
|
|
wu_final = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int wu_store_label(int unused)
|
|
{
|
|
wu_label = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int wu_store_newline(int unused)
|
|
{
|
|
wu_newline = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int wu_store_suppress(int unused)
|
|
{
|
|
wu_suppress = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int wu_store_localtime(int unused)
|
|
{
|
|
wu_localtime = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int wu_store_gmtime(int unused)
|
|
{
|
|
wu_gmtime = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int wu_store_info_all(int unused)
|
|
{
|
|
wu_info_all = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int wu_store_no_data(int unused)
|
|
{
|
|
wu_no_data = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int wu_store_set_only(int unused)
|
|
{
|
|
wu_set_only = 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* wu_options - command line options and their associated flags
|
|
*
|
|
*/
|
|
static struct wu_options wu_options[] = {
|
|
|
|
/*
|
|
* Help!
|
|
*/
|
|
[wu_option_help] = {
|
|
.longopt = "help",
|
|
.shortopt = 'h',
|
|
.param = 0,
|
|
.descr = "Display help text and exit",
|
|
.show = wu_show_help,
|
|
},
|
|
|
|
[wu_option_version] = {
|
|
.longopt = "version",
|
|
.shortopt = 'V',
|
|
.param = 0,
|
|
.descr = "Display version information and exit",
|
|
.show = wu_show_version,
|
|
},
|
|
|
|
/*
|
|
* Modifies the output for all other options
|
|
*/
|
|
[wu_option_debug] = {
|
|
.longopt = "debug",
|
|
.shortopt = 'd',
|
|
.param = 0,
|
|
.descr = "Print out debugging messages",
|
|
.store = wu_store_debug,
|
|
},
|
|
|
|
/*
|
|
* For data reading..
|
|
*/
|
|
[wu_option_count] = {
|
|
.longopt = "count",
|
|
.shortopt = 'c',
|
|
.param = 1,
|
|
.descr = "Specify number of data samples",
|
|
.option = "<n>",
|
|
.store = wu_store_count,
|
|
},
|
|
|
|
[wu_option_final] = {
|
|
.longopt = "final",
|
|
.shortopt = 'z',
|
|
.param = 0,
|
|
.descr = "Print final interval information",
|
|
.store = wu_store_final,
|
|
},
|
|
|
|
/*
|
|
* Modifies output for each option (most relevant for data)
|
|
*/
|
|
[wu_option_delim] = {
|
|
.longopt = "delim",
|
|
.shortopt = 'f',
|
|
.param = 1,
|
|
.descr = "Set field delimiter (default \", \")",
|
|
.option = "<str>",
|
|
.store = wu_store_delim,
|
|
},
|
|
|
|
[wu_option_newline] = {
|
|
.longopt = "newline",
|
|
.shortopt = 'n',
|
|
.param = 0,
|
|
.descr = "Use '\\n' as delimter instead",
|
|
.store = wu_store_newline,
|
|
},
|
|
|
|
[wu_option_localtime] = {
|
|
.longopt = "localtime",
|
|
.shortopt = 't',
|
|
.param = 0,
|
|
.descr = "Print localtime with each data reading",
|
|
.store = wu_store_localtime,
|
|
},
|
|
|
|
[wu_option_gmtime] = {
|
|
.longopt = "gmtime",
|
|
.shortopt = 'g',
|
|
.param = 0,
|
|
.descr = "Print GMT time with each data reading",
|
|
.store = wu_store_gmtime,
|
|
},
|
|
|
|
[wu_option_label] = {
|
|
.longopt = "label",
|
|
.shortopt = 'l',
|
|
.param = 0,
|
|
.descr = "Show labels of each field",
|
|
.store = wu_store_label,
|
|
},
|
|
|
|
/*
|
|
* Relevant for each of the fields below
|
|
*/
|
|
[wu_option_suppress] = {
|
|
.longopt = "suppress",
|
|
.shortopt = 's',
|
|
.param = 0,
|
|
.descr = "Suppress printing of the field description",
|
|
.store = wu_store_suppress,
|
|
},
|
|
|
|
/*
|
|
* These options print values from the device and exit.
|
|
*/
|
|
[wu_option_cal] = {
|
|
.longopt = "calibrate",
|
|
.shortopt = 'b',
|
|
.param = 0,
|
|
.descr = "Print calibration parameters",
|
|
.show = wu_show_cal,
|
|
},
|
|
|
|
[wu_option_header] = {
|
|
.longopt = "header",
|
|
.shortopt = 'r',
|
|
.param = 0,
|
|
.descr = "Print data field names (as read from device)",
|
|
.show = wu_show_header,
|
|
},
|
|
|
|
/*
|
|
* These options have an optional parameter.
|
|
* W/o that parameter, they print values from the device.
|
|
* W/ that parameter, they set that option and read data.
|
|
*
|
|
* Except when the 'set-only' parameter is used, then the
|
|
* parameters are set, then re-read and printed.
|
|
*/
|
|
[wu_option_interval] = {
|
|
.longopt = "interval",
|
|
.shortopt = 'i',
|
|
.param = 2,
|
|
.descr = "Get/Set sampling interval",
|
|
.option = "<n>",
|
|
.show = wu_show_interval,
|
|
.store = wu_store_interval,
|
|
},
|
|
|
|
[wu_option_mode] = {
|
|
.longopt = "mode",
|
|
.shortopt = 'm',
|
|
.param = 2,
|
|
.descr = "Get/Set display mode",
|
|
.option = "<n>",
|
|
.show = wu_show_mode,
|
|
.store = wu_store_mode,
|
|
},
|
|
|
|
[wu_option_user] = {
|
|
.longopt = "user",
|
|
.shortopt = 'u',
|
|
.param = 2,
|
|
.descr = "Get/Set user parameters",
|
|
.option = "<str>",
|
|
.format = "<cost per kwh>,<2nd tier cost>,"
|
|
"<2nd tier threshold>,"
|
|
"<duty cycle threshold>",
|
|
.show = wu_show_user,
|
|
.store = wu_store_user,
|
|
},
|
|
|
|
[wu_option_info_all] = {
|
|
.longopt = "show-all",
|
|
.shortopt = 'a',
|
|
.param = 0,
|
|
.descr = "Show all device parameters",
|
|
.store = wu_store_info_all,
|
|
},
|
|
|
|
[wu_option_no_data] = {
|
|
.longopt = "no-data",
|
|
.shortopt = 'N',
|
|
.param = 0,
|
|
.descr = "Don't read any data (just read device info)",
|
|
.store = wu_store_no_data,
|
|
},
|
|
|
|
[wu_option_set_only] = {
|
|
.longopt = "set-only",
|
|
.shortopt = 'S',
|
|
.param = 0,
|
|
.descr = "Set parameters only (don't read them back)",
|
|
.store = wu_store_set_only,
|
|
},
|
|
};
|
|
|
|
#define wu_num_options ARRAY_SIZE(wu_options)
|
|
|
|
static int wu_show_version(int unused)
|
|
{
|
|
printf("%s Version %s\n", prog_name, wu_version);
|
|
return 0;
|
|
}
|
|
|
|
static int wu_show_help(int unused)
|
|
{
|
|
int i;
|
|
int n;
|
|
|
|
wu_show_version(unused);
|
|
printf(" A program for interfacing with the Watts Up? Power Meter\n");
|
|
printf("\n");
|
|
|
|
printf("Usage: %s [<options> ... ]<device> [<values> ... ]\n",
|
|
prog_name);
|
|
printf("\n");
|
|
|
|
printf("<device> is the serial port the device is connected at.\n");
|
|
printf("\n");
|
|
|
|
printf("<options> are any of the following:\n");
|
|
for (i = 0; i< wu_num_options; i++) {
|
|
n = printf(" -%c", wu_options[i].shortopt);
|
|
|
|
if (wu_options[i].param == 0)
|
|
n = printf(" ");
|
|
else if (wu_options[i].param == 1)
|
|
n = printf(" %s", wu_options[i].option);
|
|
else if (wu_options[i].param == 2)
|
|
n = printf(" [%s]", wu_options[i].option);
|
|
|
|
n += printf("%*c| ", n - 12, ' ');
|
|
n += printf("--%s", wu_options[i].longopt);
|
|
|
|
if (wu_options[i].param == 0)
|
|
n += printf(" ");
|
|
else if (wu_options[i].param == 1)
|
|
n += printf("=%s", wu_options[i].option);
|
|
else if (wu_options[i].param == 2)
|
|
n += printf("[=%s]", wu_options[i].option);
|
|
|
|
printf("%*c%s\n",
|
|
40 - n, ' ', wu_options[i].descr);
|
|
}
|
|
printf("\n");
|
|
printf("<value> specifies which of these to print out (default: ALL)\n");
|
|
for (i = 0; i< wu_num_fields; i++) {
|
|
printf("%12s -- %s\n", wu_fields[i].name, wu_fields[i].descr);
|
|
}
|
|
printf("\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static char * wu_option_value(unsigned int index)
|
|
{
|
|
return (index< wu_num_options) ? wu_options[index].value : NULL;
|
|
}
|
|
|
|
|
|
static int wu_check_option_show(int index)
|
|
{
|
|
/*
|
|
* Return 1 if we need to print something out for
|
|
* a particular option.
|
|
*/
|
|
if (index< wu_num_options) {
|
|
if (wu_options[index].flag) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int wu_check_option_store(int index)
|
|
{
|
|
/*
|
|
* Return a 1 if this option is set.
|
|
*/
|
|
|
|
if (index< wu_num_options) {
|
|
if (wu_options[index].flag) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wu_show(int index, int dev_fd)
|
|
{
|
|
if (wu_options[index].show)
|
|
return wu_options[index].show(dev_fd);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Check if the option is set, and call its method if so.
|
|
* Return whether or not we did anything..
|
|
*/
|
|
|
|
static int wu_check_show(int index, int dev_fd)
|
|
{
|
|
if (wu_check_option_show(index)) {
|
|
wu_show(index, dev_fd);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Check if the option is set and if so, call it
|
|
* Return the value from the ->store() method.
|
|
*/
|
|
|
|
static int wu_check_store(int index, int dev_fd)
|
|
{
|
|
if (wu_check_option_store(index)) {
|
|
if (wu_options[index].store)
|
|
return wu_options[index].store(dev_fd);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void make_longopt(struct option * l)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i< wu_num_options; i++) {
|
|
l[i].name = wu_options[i].longopt;
|
|
l[i].has_arg = wu_options[i].param;
|
|
l[i].flag =&wu_options[i].flag;
|
|
l[i].val = 0;
|
|
}
|
|
}
|
|
|
|
static void make_shortopt(char * str)
|
|
{
|
|
int i;
|
|
char * s = str;
|
|
|
|
for (i = 0; i< wu_num_options; i++) {
|
|
*s++ = wu_options[i].shortopt;
|
|
if (wu_options[i].param)
|
|
*s++ = wu_options[i].param == 1 ? ':' : ';';
|
|
}
|
|
}
|
|
|
|
static void enable_short_option(int c, char * arg)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* Friggin' getopt_long() will return the
|
|
* character if we get a short option (e.g. '-h'),
|
|
* instead of returning 0 like it does when it
|
|
* gets a long option (e.g. "--help"). Ugh.
|
|
*/
|
|
for (i = 0; i< wu_num_options; i++) {
|
|
if (wu_options[i].shortopt == c) {
|
|
wu_options[i].flag = 1;
|
|
if (arg)
|
|
wu_options[i].value = strdup(arg);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int parse_args(int argc, char ** argv)
|
|
{
|
|
struct option longopts[wu_num_options + 1] = { };
|
|
char shortopts[wu_num_options * 2] = "";
|
|
|
|
make_longopt(longopts);
|
|
make_shortopt(shortopts);
|
|
|
|
|
|
while (1) {
|
|
int c;
|
|
int index;
|
|
|
|
c = getopt_long(argc, argv, shortopts,
|
|
longopts,&index);
|
|
if (c == -1)
|
|
break;
|
|
|
|
switch (c) {
|
|
case 0:
|
|
wu_options[index].flag = 1;
|
|
if (optarg)
|
|
wu_options[index].value = strdup(optarg);
|
|
|
|
printf("long option: val = %c, optarg = %s\n",
|
|
wu_options[index].shortopt, optarg);
|
|
break;
|
|
case '?':
|
|
err("Bad parameter");
|
|
return ret_err(EINVAL);
|
|
break;
|
|
default:
|
|
enable_short_option(c, optarg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check for help request now and bail after
|
|
* printing it, if it's set.
|
|
*/
|
|
if (wu_check_show(wu_option_help, 0))
|
|
exit(0);
|
|
|
|
if (wu_check_show(wu_option_version, 0))
|
|
exit(0);
|
|
|
|
/*
|
|
* Fields to print out
|
|
*/
|
|
if (optind< argc) {
|
|
int i;
|
|
|
|
wu_device = argv[optind++];
|
|
|
|
if (optind< argc) {
|
|
for (i = optind; i< argc; i++)
|
|
enable_field(argv[i]);
|
|
} else
|
|
enable_all_fields();
|
|
|
|
} else {
|
|
wu_show(wu_option_help, 0);
|
|
return ret_err(EINVAL);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int main(int argc, char ** argv)
|
|
{
|
|
int ret;
|
|
int fd = 0;
|
|
|
|
ret = parse_args(argc, argv);
|
|
if (ret)
|
|
return 0;
|
|
|
|
/*
|
|
* Try to enable debugging early
|
|
*/
|
|
if ((ret = wu_check_store(wu_option_debug, 0)))
|
|
goto Close;
|
|
|
|
ret = open_device(wu_device,&fd);
|
|
if (ret)
|
|
return ret;
|
|
|
|
dbg("%s: Open for business", wu_device);
|
|
|
|
ret = setup_serial_device(fd);
|
|
if (ret)
|
|
goto Close;
|
|
|
|
wu_clear(fd);
|
|
|
|
wu_fd = fd;
|
|
|
|
/*
|
|
* Set delimeter before we print out any fields.
|
|
*/
|
|
if ((ret = wu_check_store(wu_option_delim, fd)))
|
|
goto Close;
|
|
|
|
/*
|
|
* Ditto for 'label' and 'newline' flags.
|
|
*/
|
|
if ((ret = wu_check_store(wu_option_label, fd)))
|
|
goto Close;
|
|
|
|
if ((ret = wu_check_store(wu_option_newline, fd)))
|
|
goto Close;
|
|
|
|
if ((ret = wu_check_store(wu_option_suppress, fd)))
|
|
goto Close;
|
|
|
|
if ((ret = wu_check_store(wu_option_localtime, fd)))
|
|
goto Close;
|
|
|
|
if ((ret = wu_check_store(wu_option_gmtime, fd)))
|
|
goto Close;
|
|
|
|
if ((ret = wu_check_store(wu_option_set_only, fd)))
|
|
goto Close;
|
|
|
|
if ((ret = wu_check_store(wu_option_no_data, fd)))
|
|
goto Close;
|
|
|
|
if ((ret = wu_check_store(wu_option_info_all, fd)))
|
|
goto Close;
|
|
|
|
|
|
/*
|
|
* Options to set device parameters.
|
|
*/
|
|
if ((ret = wu_check_store(wu_option_interval, fd)))
|
|
goto Close;
|
|
|
|
if ((ret = wu_check_store(wu_option_mode, fd)))
|
|
goto Close;
|
|
|
|
if ((ret = wu_check_store(wu_option_user, fd)))
|
|
goto Close;
|
|
|
|
/*
|
|
* Check for options to print device info
|
|
*/
|
|
if (wu_info_all) {
|
|
wu_show(wu_option_cal, fd);
|
|
wu_show(wu_option_header, fd);
|
|
wu_show(wu_option_interval, fd);
|
|
wu_show(wu_option_mode, fd);
|
|
wu_show(wu_option_user, fd);
|
|
} else {
|
|
wu_check_show(wu_option_cal, fd);
|
|
wu_check_show(wu_option_header, fd);
|
|
|
|
if (!wu_set_only) {
|
|
wu_check_show(wu_option_interval, fd);
|
|
wu_check_show(wu_option_mode, fd);
|
|
wu_check_show(wu_option_user, fd);
|
|
}
|
|
}
|
|
|
|
if (!wu_no_data) {
|
|
|
|
if ((ret = wu_check_store(wu_option_count, fd)))
|
|
goto Close;
|
|
|
|
if ((ret = wu_check_store(wu_option_final, fd)))
|
|
goto Close;
|
|
|
|
if ((ret = wu_start_log()))
|
|
goto Close;
|
|
|
|
wu_read_data(fd);
|
|
|
|
wu_stop_log();
|
|
}
|
|
Close:
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
|