diff --git a/bin/logger/src/channel.c b/bin/logger/src/channel.c index d615938..34008c0 100644 --- a/bin/logger/src/channel.c +++ b/bin/logger/src/channel.c @@ -57,24 +57,33 @@ void channel_free(channel_t *ch) { free(ch->middleware); } +int channel_compare_identifier(meter_protocol_t protocol, reading_id_t a, reading_id_t b) { + switch (protocol) { + case meter_protocol_d0: + case meter_protocol_sml: + /* intelligent protocols require matching ids */ + return obis_compare(a.obis, b.obis); + + case meter_protocol_file: + case meter_protocol_exec: + return strcmp(a.string, b.string); + break; + default: + /* no channel identifier, adding all readings to buffer */ + return 0; /* equal */ + } +} + reading_t * channel_add_readings(channel_t *ch, meter_protocol_t protocol, reading_t *rds, size_t n) { reading_t *first = NULL; /* first unsent reading which has been added */ double delta; for (int i = 0; i < n; i++) { - int add = FALSE; - if (protocol == meter_protocol_d0 || protocol == meter_protocol_sml) { /* intelligent protocols require matching ids */ - if (obis_compare(rds[i].identifier.obis, ch->identifier.obis) == 0) { - add = TRUE; - } - } - else { /* no channel identifier, adding all readings to buffer */ - add = TRUE; - } + if (channel_compare_identifier(protocol, rds[i].identifier, ch->identifier) == 0) { - if (add) { - delta = (ch->last == 0) ? 0 : rds[i].value - ch->last; /* ignoring first reading when no preceeding reading is available (no consumption) */ + /* ignoring first reading when no preceeding reading is available (no consumption) */ + delta = (ch->last == 0) ? 0 : rds[i].value - ch->last; ch->last = rds[i].value; print(log_info, "Adding reading to queue (value=%.2f delta=%.2f ts=%.3f)", ch, rds[i].value, delta, tvtod(rds[i].time)); diff --git a/bin/logger/src/threads.c b/bin/logger/src/threads.c index aa841dd..7a32ed5 100644 --- a/bin/logger/src/threads.c +++ b/bin/logger/src/threads.c @@ -64,10 +64,26 @@ void * reading_thread(void *arg) { /* dumping meter output */ if (options.verbosity > log_debug) { print(log_debug, "Got %i new readings from meter:", mtr, n); - char obis_str[OBIS_STR_LEN]; + char identifier[255]; // TODO choose correct length for (int i = 0; i < n; i++) { - obis_unparse(rds[i].identifier.obis, obis_str, OBIS_STR_LEN); - print(log_debug, "Reading: id=%s value=%.2f ts=%.3f", mtr, obis_str, rds[i].value, tvtod(rds[i].time)); + switch (mtr->protocol) { + case meter_protocol_d0: + case meter_protocol_sml: + obis_unparse(rds[i].identifier.obis, identifier, 255); + break; + + case meter_protocol_file: + case meter_protocol_exec: + if (rds[i].identifier.string != NULL) { + strncpy(identifier, rds[i].identifier.string, 255); + break; + } + + default: + identifier[0] = '\0'; + } + + print(log_debug, "Reading: id=%s value=%.2f ts=%.3f", mtr, identifier, rds[i].value, tvtod(rds[i].time)); } } diff --git a/configure b/configure index d708026..f1e1764 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.67 for vzlogger 0.3.1. +# Generated by GNU Autoconf 2.67 for vzlogger 0.3.2. # # Report bugs to . # @@ -552,8 +552,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='vzlogger' PACKAGE_TARNAME='vzlogger' -PACKAGE_VERSION='0.3.1' -PACKAGE_STRING='vzlogger 0.3.1' +PACKAGE_VERSION='0.3.2' +PACKAGE_STRING='vzlogger 0.3.2' PACKAGE_BUGREPORT='http://bugs.volkszaehler.org' PACKAGE_URL='' @@ -1263,7 +1263,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures vzlogger 0.3.1 to adapt to many kinds of systems. +\`configure' configures vzlogger 0.3.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1329,7 +1329,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of vzlogger 0.3.1:";; + short | recursive ) echo "Configuration of vzlogger 0.3.2:";; esac cat <<\_ACEOF @@ -1442,7 +1442,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -vzlogger configure 0.3.1 +vzlogger configure 0.3.2 generated by GNU Autoconf 2.67 Copyright (C) 2010 Free Software Foundation, Inc. @@ -1911,7 +1911,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by vzlogger $as_me 0.3.1, which was +It was created by vzlogger $as_me 0.3.2, which was generated by GNU Autoconf 2.67. Invocation command line was $ $0 $@ @@ -2726,7 +2726,7 @@ fi # Define the identity of the package. PACKAGE=vzlogger - VERSION=0.3.1 + VERSION=0.3.2 cat >>confdefs.h <<_ACEOF @@ -5661,7 +5661,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by vzlogger $as_me 0.3.1, which was +This file was extended by vzlogger $as_me 0.3.2, which was generated by GNU Autoconf 2.67. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -5727,7 +5727,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -vzlogger config.status 0.3.1 +vzlogger config.status 0.3.2 configured by $0, generated by GNU Autoconf 2.67, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 3feab4d..af9e7b3 100644 --- a/configure.ac +++ b/configure.ac @@ -4,8 +4,8 @@ AC_PREREQ([2.67]) # update version here! -AC_INIT([vzlogger], [0.3.1], [http://bugs.volkszaehler.org]) -AM_INIT_AUTOMAKE([vzlogger], [0.3.1]) +AC_INIT([vzlogger], [0.3.2], [http://bugs.volkszaehler.org]) +AM_INIT_AUTOMAKE([vzlogger], [0.3.2]) AC_CONFIG_SRCDIR([src/meter.c]) AC_CONFIG_HEADERS([config.h]) diff --git a/etc/vzlogger.conf b/etc/vzlogger.conf index 0485742..d276507 100644 --- a/etc/vzlogger.conf +++ b/etc/vzlogger.conf @@ -26,7 +26,7 @@ "meters" : [{ "enabled" : false, /* disabled meters will be ignored */ "protocol" : "sml", /* use 'vzlogger -h' for list of available protocols */ - "connection" : "meinzaehler.dyndns.info:7331", + "host" : "meinzaehler.dyndns.info:7331", "channels": [{ "uuid" : "fde8f1d0-c5d0-11e0-856e-f9e4360ced10", "middleware" : "http://localhost/workspace/volkszaehler.org/volkszaehler.org/htdocs/middleware.php", @@ -34,7 +34,8 @@ }, { "uuid" : "a8da012a-9eb4-49ed-b7f3-38c95142a90c", "middleware" : "http://localhost/workspace/volkszaehler.org/volkszaehler.org/htdocs/middleware.php", - "identifier" : "voltage" + "identifier" : "counter", + "counter" : true /* send only delta between to values (simluate impulses) */ }, { "uuid" : "d5c6db0f-533e-498d-a85a-be972c104b48", "middleware" : "http://localhost/workspace/volkszaehler.org/volkszaehler.org/htdocs/middleware.php", @@ -43,17 +44,25 @@ }, { "protocol" : "random", "interval" : 2, - "connection" : "40", + "max" : 40.0, /* has to be double! */ + "min" : -5.0, "channel" : { "uuid" : "bac2e840-f72c-11e0-bedf-3f850c1e5a66", "middleware" : "http://localhost/workspace/volkszaehler.org/volkszaehler.org/htdocs/middleware.php" } }, { "protocol" : "s0", - "connection" : "/dev/ttyUSB0", + "port" : "/dev/ttyUSB0", "channel" : { "uuid" : "d495a390-f747-11e0-b3ca-f7890e45c7b2", "middleware" : "http://localhost/workspace/volkszaehler.org/volkszaehler.org/htdocs/middleware.php" } + }, + { + "protocol" : "file", + "path" : "/proc/loadavg", +// "format" : "$i $v $t", /* $i => identifier, $v => value, $t => timestamp, arbitrary text and whitespaces are allowed */ + "rewind" : true, /* reset file pointer each interval to the beginning of the file */ + "interval" : 2 } ]} diff --git a/include/exec.h b/include/exec.h index e037b22..71494af 100644 --- a/include/exec.h +++ b/include/exec.h @@ -30,7 +30,7 @@ typedef struct { char *command; - char *regex; + char *format; } meter_handle_exec_t; /* forward declarations */ diff --git a/include/file.h b/include/file.h index 64c12cf..232c3d5 100644 --- a/include/file.h +++ b/include/file.h @@ -30,7 +30,8 @@ typedef struct { char *path; - char *regex; + char *format; + int rewind; FILE *fd; } meter_handle_file_t; diff --git a/include/s0.h b/include/s0.h index 43d42e2..27e7b73 100644 --- a/include/s0.h +++ b/include/s0.h @@ -27,13 +27,13 @@ #define _S0_H_ #include -#include typedef struct { char *device; + int resolution; - int fd; /* file descriptor of port */ - struct termios oldtio; /* required to reset port */ + int fd; /* file descriptor of port */ + struct termios old_tio; /* required to reset port */ } meter_handle_s0_t; /* forward declarations */ diff --git a/include/sml.h b/include/sml.h index 90e67b7..9169a5e 100644 --- a/include/sml.h +++ b/include/sml.h @@ -35,29 +35,23 @@ #include #include +#include + #include "obis.h" typedef struct { char *host; char *device; - int baudrate; + speed_t baudrate; - int fd; - //termios old_tio; + int fd; /* file descriptor of port */ + struct termios old_tio; /* required to reset port */ } meter_handle_sml_t; /* forward declarations */ struct meter; struct reading; -/** - * Cast arbitrary sized sml_value to double - * - * @param value the sml_value which should be casted - * @return double value representation of sml_value, NAN if an error occured - */ -double sml_value_todouble(sml_value *value); - /** * Initialize meter structure with a list of options * @@ -107,9 +101,11 @@ void meter_sml_parse(sml_list *list, struct reading *rd); * Open serial port by device * * @param device the device path, usually /dev/ttyS* + * @param old_config pointer to termios structure, will be filled with old port configuration + * @param baudrate the baudrate * @return file descriptor, <0 on error */ -int meter_sml_open_port(const char *device); +int meter_sml_open_device(const char *device, struct termios *old_config, speed_t baudrate); /** * Open socket diff --git a/src/d0.c b/src/d0.c index fecd4a2..1f4ee8a 100644 --- a/src/d0.c +++ b/src/d0.c @@ -11,7 +11,6 @@ * @license http://www.gnu.org/licenses/gpl.txt GNU Public License * @author Steffen Vogel * @author Mathias Dalheimer - * based heavily on libehz (https://github.com/gonium/libehz.git) */ /* * This file is part of volkzaehler.org @@ -34,11 +33,11 @@ #include #include #include +#include #include #include /* socket */ -#include #include #include @@ -47,47 +46,33 @@ #include "obis.h" #include "options.h" -int meter_d0_open_socket(const char *node, const char *service) { - struct sockaddr_in sin; - struct addrinfo *ais; - int fd; // file descriptor - int res; - - fd = socket(PF_INET, SOCK_STREAM, 0); - if (fd < 0) { - print(log_error, "socket(): %s", NULL, strerror(errno)); - return ERR; - } - - getaddrinfo(node, service, NULL, &ais); - memcpy(&sin, ais->ai_addr, ais->ai_addrlen); - freeaddrinfo(ais); - - res = connect(fd, (struct sockaddr *) &sin, sizeof(sin)); - if (res < 0) { - print(log_error, "connect(%s, %s): %s", NULL, node, service, strerror(errno)); - return ERR; - } - - return fd; -} - int meter_init_d0(meter_t *mtr, list_t options) { meter_handle_d0_t *handle = &mtr->handle.d0; /* connection */ - handle->host = NULL; - handle->device = NULL; - if (options_lookup_string(options, "host", &handle->host) != SUCCESS && options_lookup_string(options, "device", &handle->device) != SUCCESS) { - print(log_error, "Missing host or port", mtr); + char *host, *device; + if (options_lookup_string(options, "host", &host) == SUCCESS) { + handle->host = strdup(host); + handle->device = NULL; + } + else if (options_lookup_string(options, "device", &device) == SUCCESS) { + handle->device = strdup(device); + handle->host = NULL; + } + else { + print(log_error, "Missing host and port", mtr); return ERR; } /* baudrate */ - handle->baudrate = 9600; - if (options_lookup_int(options, "baudrate", &handle->baudrate) == ERR_INVALID_TYPE) { - print(log_error, "Invalid type for baudrate", mtr); - return ERR; + switch (options_lookup_int(options, "baudrate", &handle->baudrate)) { + case ERR_NOT_FOUND: /* using default value if not specified */ + handle->baudrate = 9600; + break; + + default: + print(log_error, "Failed to parse the baudrate", mtr); + return ERR; } return SUCCESS; @@ -236,8 +221,9 @@ size_t meter_read_d0(meter_t *mtr, reading_t rds[], size_t max_readings) { case END_LINE: if (byte == '\r' || byte == '\n') { - if ((number_of_tuples < max_readings) && (strlen(obis_code) > 0) && (strlen(value) > 0)) { /* free slots available and sain content? */ - printf("parsed reading (OBIS code=%s, value=%s, unit=%s)\n", obis_code, value, unit); + /* free slots available and sain content? */ + if ((number_of_tuples < max_readings) && (strlen(obis_code) > 0) && (strlen(value) > 0)) { + print(log_debug, "Parsed reading (OBIS code=%s, value=%s, unit=%s)", mtr, obis_code, value, unit); rds[number_of_tuples].value = strtof(value, NULL); obis_parse(obis_code, &rds[number_of_tuples].identifier.obis); gettimeofday(&rds[number_of_tuples].time, NULL); @@ -251,7 +237,8 @@ size_t meter_read_d0(meter_t *mtr, reading_t rds[], size_t max_readings) { break; case END: - print(log_debug, "Read package with %i tuples (vendor=%s, baudrate=%c, identification=%s)", mtr, number_of_tuples, vendor, baudrate, identification); + print(log_debug, "Read package with %i tuples (vendor=%s, baudrate=%c, identification=%s)", + mtr, number_of_tuples, vendor, baudrate, identification); return number_of_tuples; } } @@ -261,3 +248,27 @@ error: return 0; } +int meter_d0_open_socket(const char *node, const char *service) { + struct sockaddr_in sin; + struct addrinfo *ais; + int fd; /* file descriptor */ + int res; + + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) { + print(log_error, "socket(): %s", NULL, strerror(errno)); + return ERR; + } + + getaddrinfo(node, service, NULL, &ais); + memcpy(&sin, ais->ai_addr, ais->ai_addrlen); + freeaddrinfo(ais); + + res = connect(fd, (struct sockaddr *) &sin, sizeof(sin)); + if (res < 0) { + print(log_error, "connect(%s, %s): %s", NULL, node, service, strerror(errno)); + return ERR; + } + + return fd; +} diff --git a/src/exec.c b/src/exec.c index 820b8d7..d4ae8c8 100644 --- a/src/exec.c +++ b/src/exec.c @@ -1,5 +1,5 @@ /** - * Get data by calling programs + * Get data by calling a program * * @package vzlogger * @copyright Copyright (c) 2011, The volkszaehler.org project @@ -33,17 +33,32 @@ int meter_init_exec(meter_t *mtr, list_t options) { meter_handle_exec_t *handle = &mtr->handle.exec; - if (options_lookup_string(options, "command", &handle->command) != SUCCESS) { + char *command; + if (options_lookup_string(options, "command", &command) == SUCCESS) { + handle->command = strdup(command); + } + else { print(log_error, "Missing command or invalid type", mtr); return ERR; } - handle->regex = NULL; - if (options_lookup_string(options, "regex", &handle->regex) == ERR_INVALID_TYPE) { - print(log_error, "Regex has to be a string", mtr); - return ERR; + char *format; + switch (options_lookup_string(options, "format", &format)) { + case SUCCESS: + handle->format = strdup(format); + // TODO parse format (see file.c) + break; + + case ERR_NOT_FOUND: + handle->format = NULL; /* use default format */ + break; + + default: + print(log_error, "Failed to parse format", mtr); + return ERR; } + return SUCCESS; } diff --git a/src/file.c b/src/file.c index ab8a3e6..47fac8a 100644 --- a/src/file.c +++ b/src/file.c @@ -34,6 +34,7 @@ int meter_init_file(meter_t *mtr, list_t options) { meter_handle_file_t *handle = &mtr->handle.file; + /* path to file which should be read */ char *path; if (options_lookup_string(options, "path", &path) == SUCCESS) { handle->path = strdup(path); @@ -43,13 +44,83 @@ int meter_init_file(meter_t *mtr, list_t options) { return ERR; } - char *regex; - if (options_lookup_string(options, "regex", ®ex) == SUCCESS) { - handle->regex = strdup(regex); + /* a optional format string for scanf() */ + char *config_format; + switch (options_lookup_string(options, "format", &config_format)) { + case SUCCESS: { + /** + * Compiling the provided format string in a format string for scanf + * by replacing the following tokens + * + * "$v" => "%1$f" (value) + * "$i" => "%2$ms" (identifier) (memory gets allocated by sscanf()) + * "$t" => "%3$f" (timestamp) + */ + + int config_len = strlen(config_format); + int scanf_len = config_len + 8; /* adding extra space for longer conversion specification in scanf_format */ + + char *scanf_format = malloc(scanf_len); /* the scanf format string */ + + int i = 0; /* index in config_format string */ + int j = 0; /* index in scanf_format string */ + while (i <= config_len && j <= scanf_len) { + switch (config_format[i]) { + case '$': + if (i+1 < config_len) { /* introducing a token */ + switch (config_format[i+1]) { + case 'v': j += sprintf(scanf_format+j, "%%1$f"); break; + case 'i': j += sprintf(scanf_format+j, "%%2$ms"); break; + case 't': j += sprintf(scanf_format+j, "%%3$lf"); break; + } + i++; + } + break; + + case '%': + scanf_format[j++] = '%'; /* add double %% to escape a conversion identifier */ + + default: + scanf_format[j++] = config_format[i]; /* just copying */ + } + + i++; + } + + print(log_debug, "Parsed format string \"%s\" => \"%s\"", mtr, config_format, scanf_format); + handle->format = scanf_format; + + } + break; + + case ERR_NOT_FOUND: + handle->format = NULL; /* disable format matching */ + break; + + default: + print(log_error, "Failed to parse 'format'", mtr); + return ERR; } - else if (options_lookup_string(options, "regex", ®ex) == ERR_INVALID_TYPE) { // TODO improve code - print(log_error, "Regex has to be a string", mtr); - return ERR; + + /* should we start each time at the beginning of the file? */ + /* or do we read from a logfile (append) */ + int rewind; + switch (options_lookup_boolean(options, "rewind", &rewind)) { + case SUCCESS: + handle->rewind = rewind; + break; + + case ERR_INVALID_TYPE: + print(log_error, "Invalid type for 'rewind'", mtr); + return ERR; + + case ERR_NOT_FOUND: + handle->rewind = FALSE; /* do not rewind file by default */ + break; + + default: + print(log_error, "Failed to parse 'rewind'", mtr); + return ERR; } return SUCCESS; @@ -77,19 +148,42 @@ int meter_close_file(meter_t *mtr) { size_t meter_read_file(meter_t *mtr, reading_t rds[], size_t n) { meter_handle_file_t *handle = &mtr->handle.file; - char buffer[16]; - int bytes; + // TODO use inotify to block eading until file changes - rewind(handle->fd); - bytes = fread(buffer, 1, 16, handle->fd); - buffer[bytes] = '\0'; /* zero terminated, required? */ + char line[256], *endptr; - if (bytes) { - rds->value = strtof(buffer, NULL); - gettimeofday(&rds->time, NULL); - - return 1; + /* reset file pointer to beginning of file */ + if (handle->rewind) { + rewind(handle->fd); } - return 0; /* empty file */ + int i = 0; + while (fgets(line, 256, handle->fd) && i < n) { + char *nl; + if ((nl = strrchr(line, '\n'))) *nl = '\0'; /* remove trailing newline */ + if ((nl = strrchr(line, '\r'))) *nl = '\0'; + + if (handle->format) { + double timestamp; + + /* at least the value has to been read */ + int found = sscanf(line, handle->format, &rds[i].value, &rds[i].identifier.string, ×tamp); + if (found >= 1) { // TODO free() space allocated for identifier string + rds[i].time = dtotv(timestamp); /* convert double to timeval */ + i++; /* read successfully */ + } + } + else { /* just reading a value per line */ + rds[i].value = strtod(line, &endptr); + rds[i].identifier.string = NULL; + + gettimeofday(&rds[i].time, NULL); + + if (endptr != line) { + i++; /* read successfully */ + } + } + } + + return i; } diff --git a/src/meter.c b/src/meter.c index f604954..626e457 100644 --- a/src/meter.c +++ b/src/meter.c @@ -36,8 +36,8 @@ static const meter_details_t protocols[] = { /* alias description max_rds periodic ===============================================================================================*/ -METER_DETAIL(file, "Read from file (ex. 1-Wire sensors via OWFS)", 1, TRUE), -METER_DETAIL(exec, "Read from program (ex. 1-Wire sensors via digitemp)", 1, TRUE), +METER_DETAIL(file, "Read from file (ex. 1-Wire sensors via OWFS)", 32, TRUE), +//METER_DETAIL(exec, "Read from program (ex. 1-Wire sensors via digitemp)", 32, TRUE), METER_DETAIL(random, "Random walk", 1, TRUE), METER_DETAIL(s0, "S0 on RS232", 1, TRUE), METER_DETAIL(d0, "Plaintext protocol (DIN EN 62056-21)", 32, FALSE), @@ -117,8 +117,13 @@ double tvtod(struct timeval tv) { } struct timeval dtotv(double ts) { - struct timeval tv; - tv.tv_usec = modf(ts, &tv.tv_sec); + double integral; + double fraction = modf(ts, &integral); + + struct timeval tv = { + .tv_usec = (long int) (fraction * 1e6), + .tv_sec = (long int) integral + }; return tv; } diff --git a/src/s0.c b/src/s0.c index 6f853ca..9a191fb 100644 --- a/src/s0.c +++ b/src/s0.c @@ -37,17 +37,28 @@ int meter_init_s0(meter_t *mtr, list_t options) { meter_handle_s0_t *handle = &mtr->handle.s0; - if (options_lookup_string(options, "device", &handle->device) != SUCCESS) { + char *device; + if (options_lookup_string(options, "device", &device) == SUCCESS) { + handle->device = strdup(device); + } + else { print(log_error, "Missing device or invalid type", mtr); return ERR; } + switch (options_lookup_int(options, "resolution", &handle->resolution)) { + case ERR_NOT_FOUND: + handle->resolution = 1; /* 1 Wh per impulse */ + break; + + case ERR_INVALID_TYPE: + print(log_error, "Failed to parse resolution", mtr); + return ERR; + } + return SUCCESS; } -/** - * Setup serial port - */ int meter_open_s0(meter_t *mtr) { meter_handle_s0_t *handle = &mtr->handle.s0; @@ -60,7 +71,7 @@ int meter_open_s0(meter_t *mtr) { } /* save current port settings */ - tcgetattr(fd, &handle->oldtio); + tcgetattr(fd, &handle->old_tio); /* configure port */ struct termios tio; @@ -85,11 +96,9 @@ int meter_open_s0(meter_t *mtr) { int meter_close_s0(meter_t *mtr) { meter_handle_s0_t *handle = &mtr->handle.s0; - /* reset serial port */ - tcsetattr(handle->fd, TCSANOW, &handle->oldtio); + tcsetattr(handle->fd, TCSANOW, &handle->old_tio); /* reset serial port */ - /* close serial port */ - return close(handle->fd); + return close(handle->fd); /* close serial port */ } size_t meter_read_s0(meter_t *mtr, reading_t rds[], size_t n) { diff --git a/src/sml.c b/src/sml.c index f39096d..f62e5de 100644 --- a/src/sml.c +++ b/src/sml.c @@ -38,7 +38,6 @@ /* serial port */ #include #include -#include /* socket */ #include @@ -57,18 +56,49 @@ int meter_init_sml(meter_t *mtr, list_t options) { meter_handle_sml_t *handle = &mtr->handle.sml; /* connection */ - handle->host = NULL; - handle->device = NULL; - if (options_lookup_string(options, "host", &handle->host) != SUCCESS && options_lookup_string(options, "device", &handle->device) != SUCCESS) { - print(log_error, "Missing host or port", mtr); + char *host, *device; + if (options_lookup_string(options, "host", &host) == SUCCESS) { + handle->host = strdup(host); + handle->device = NULL; + } + else if (options_lookup_string(options, "device", &device) == SUCCESS) { + handle->device = strdup(device); + handle->host = NULL; + } + else { + print(log_error, "Missing host and port", mtr); return ERR; } /* baudrate */ - handle->baudrate = 9600; - if (options_lookup_int(options, "baudrate", &handle->baudrate) == ERR_INVALID_TYPE) { - print(log_error, "Invalid type for baudrate", mtr); - return ERR; + int baudrate; + switch (options_lookup_int(options, "baudrate", &baudrate)) { + case SUCCESS: + /* find constant for termios structure */ + switch (baudrate) { + case 1200: handle->baudrate = B1200; break; + case 1800: handle->baudrate = B1800; break; + case 2400: handle->baudrate = B2400; break; + case 4800: handle->baudrate = B4800; break; + case 9600: handle->baudrate = B9600; break; + case 19200: handle->baudrate = B19200; break; + case 38400: handle->baudrate = B38400; break; + case 57600: handle->baudrate = B57600; break; + case 115200: handle->baudrate = B115200; break; + case 230400: handle->baudrate = B230400; break; + default: + print(log_error, "Invalid baudrate: %i", mtr, baudrate); + return ERR; + } + break; + + case ERR_NOT_FOUND: /* using default value if not specified */ + handle->baudrate = B9600; + break; + + default: + print(log_error, "Failed to parse the baudrate", mtr); + return ERR; } return SUCCESS; @@ -77,16 +107,17 @@ int meter_init_sml(meter_t *mtr, list_t options) { int meter_open_sml(meter_t *mtr) { meter_handle_sml_t *handle = &mtr->handle.sml; - if (handle->device != NULL) { - print(log_error, "TODO: implement serial interface", mtr); - return ERR; + if (handle->device != NULL) { /* local connection */ + handle->fd = meter_sml_open_device(handle->device, &handle->old_tio, handle->baudrate); } - else if (handle->host != NULL) { + else if (handle->host != NULL) { /* remote connection */ char *addr = strdup(handle->host); - char *node = strsep(&addr, ":"); + char *node = strsep(&addr, ":"); /* split port/service from hostname */ char *service = strsep(&addr, ":"); handle->fd = meter_sml_open_socket(node, service); + + free(addr); } return (handle->fd < 0) ? ERR : SUCCESS; @@ -95,7 +126,11 @@ int meter_open_sml(meter_t *mtr) { int meter_close_sml(meter_t *meter) { meter_handle_sml_t *handle = &meter->handle.sml; - // TODO reset serial port + if (handle->device != NULL) { + /* reset serial port */ + tcsetattr(handle->fd, TCSANOW, &handle->old_tio); + } + return close(handle->fd); } @@ -109,7 +144,7 @@ size_t meter_read_sml(meter_t *meter, reading_t rds[], size_t n) { sml_get_list_response *body; sml_list *entry; - /* blocking read from fd */ + /* wait until a we receive a new datagram from the meter (blocking read) */ bytes = sml_transport_read(handle->fd, buffer, SML_BUFFER_LEN); /* parse SML file & stripping escape sequences */ @@ -145,14 +180,13 @@ void meter_sml_parse(sml_list *entry, reading_t *rd) { obis_init(&rd->identifier.obis, entry->obj_name->str); - /* get time */ // TODO handle SML_TIME_SEC_INDEX or time by SML File/Message - if (entry->val_time) { + if (entry->val_time) { /* use time from meter */ rd->time.tv_sec = *entry->val_time->data.timestamp; rd->time.tv_usec = 0; } else { - gettimeofday(&rd->time, NULL); + gettimeofday(&rd->time, NULL); /* use local time */ } } @@ -180,10 +214,10 @@ int meter_sml_open_socket(const char *node, const char *service) { return fd; } -int meter_sml_open_port(const char *device) { +int meter_sml_open_device(const char *device, struct termios *old_tio, speed_t baudrate) { int bits; - struct termios config; - memset(&config, 0, sizeof(config)); + struct termios tio; + memset(&tio, 0, sizeof(struct termios)); int fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); if (fd < 0) { @@ -191,24 +225,30 @@ int meter_sml_open_port(const char *device) { return ERR; } - // set RTS + /* enable RTS as supply for infrared adapters */ ioctl(fd, TIOCMGET, &bits); bits |= TIOCM_RTS; ioctl(fd, TIOCMSET, &bits); - tcgetattr(fd, &config) ; + /* get old configuration */ + tcgetattr(fd, &tio) ; - // set 8-N-1 - config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - config.c_oflag &= ~OPOST; - config.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - config.c_cflag &= ~(CSIZE | PARENB | PARODD | CSTOPB); - config.c_cflag |= CS8; + /* backup old configuration to restore it when closing the meter connection */ + memcpy(old_tio, &tio, sizeof(struct termios)); - // set speed to 9600 baud - cfsetispeed( &config, B9600); - cfsetospeed( &config, B9600); + /* set 8-N-1 */ + tio.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + tio.c_oflag &= ~OPOST; + tio.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + tio.c_cflag &= ~(CSIZE | PARENB | PARODD | CSTOPB); + tio.c_cflag |= CS8; + + /* set baudrate */ + cfsetispeed(&tio, baudrate); + cfsetospeed(&tio, baudrate); + + /* apply new configuration */ + tcsetattr(fd, TCSANOW, &tio); - tcsetattr(fd, TCSANOW, &config); return fd; }