huge changes: a lot of code refactoring, coding standards, a improved configuration system, support for absolute counters etc...

This commit is contained in:
Steffen Vogel 2011-11-21 00:38:05 +01:00
parent 4da2811f34
commit bdabc5c518
38 changed files with 1578 additions and 573 deletions

View file

@ -2,8 +2,8 @@ AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(DEPS_VZ_CFLAGS)
AM_CPPFLAGS = -I$(top_srcdir)/include -Iinclude
bin_PROGRAMS = vzlogger
vzlogger_SOURCES = src/vzlogger.c src/channel.c src/api.c src/configuration.c src/threads.c src/buffer.c
vzlogger_LDADD = $(top_srcdir)/src/libmeter.a
vzlogger_SOURCES = src/vzlogger.c src/channel.c src/api.c src/config.c src/threads.c src/buffer.c
vzlogger_LDADD = $(top_srcdir)/src/libmtr.a
vzlogger_LDFLAGS = -lpthread -lm $(DEPS_VZ_LIBS)
# local interface support

View file

@ -57,16 +57,16 @@ CONFIG_CLEAN_VPATH_FILES =
am__installdirs = "$(DESTDIR)$(bindir)"
PROGRAMS = $(bin_PROGRAMS)
am__vzlogger_SOURCES_DIST = src/vzlogger.c src/channel.c src/api.c \
src/configuration.c src/threads.c src/buffer.c src/local.c
src/config.c src/threads.c src/buffer.c src/local.c
@LOCAL_SUPPORT_TRUE@am__objects_1 = local.$(OBJEXT)
am_vzlogger_OBJECTS = vzlogger.$(OBJEXT) channel.$(OBJEXT) \
api.$(OBJEXT) configuration.$(OBJEXT) threads.$(OBJEXT) \
api.$(OBJEXT) config.$(OBJEXT) threads.$(OBJEXT) \
buffer.$(OBJEXT) $(am__objects_1)
vzlogger_OBJECTS = $(am_vzlogger_OBJECTS)
am__DEPENDENCIES_1 =
@LOCAL_SUPPORT_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
@SML_SUPPORT_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1)
vzlogger_DEPENDENCIES = $(top_srcdir)/src/libmeter.a \
vzlogger_DEPENDENCIES = $(top_srcdir)/src/libmtr.a \
$(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3)
vzlogger_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(vzlogger_LDFLAGS) \
$(LDFLAGS) -o $@
@ -182,9 +182,9 @@ top_srcdir = @top_srcdir@
AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(DEPS_VZ_CFLAGS) \
$(am__append_3) $(am__append_5)
AM_CPPFLAGS = -I$(top_srcdir)/include -Iinclude
vzlogger_SOURCES = src/vzlogger.c src/channel.c src/api.c \
src/configuration.c src/threads.c src/buffer.c $(am__append_1)
vzlogger_LDADD = $(top_srcdir)/src/libmeter.a $(am__append_2) \
vzlogger_SOURCES = src/vzlogger.c src/channel.c src/api.c src/config.c \
src/threads.c src/buffer.c $(am__append_1)
vzlogger_LDADD = $(top_srcdir)/src/libmtr.a $(am__append_2) \
$(am__append_4)
vzlogger_LDFLAGS = -lpthread -lm $(DEPS_VZ_LIBS)
all: all-am
@ -271,7 +271,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/api.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channel.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/configuration.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/local.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/threads.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vzlogger.Po@am__quote@
@ -332,19 +332,19 @@ api.obj: src/api.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o api.obj `if test -f 'src/api.c'; then $(CYGPATH_W) 'src/api.c'; else $(CYGPATH_W) '$(srcdir)/src/api.c'; fi`
configuration.o: src/configuration.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT configuration.o -MD -MP -MF $(DEPDIR)/configuration.Tpo -c -o configuration.o `test -f 'src/configuration.c' || echo '$(srcdir)/'`src/configuration.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/configuration.Tpo $(DEPDIR)/configuration.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='src/configuration.c' object='configuration.o' libtool=no @AMDEPBACKSLASH@
config.o: src/config.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT config.o -MD -MP -MF $(DEPDIR)/config.Tpo -c -o config.o `test -f 'src/config.c' || echo '$(srcdir)/'`src/config.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/config.Tpo $(DEPDIR)/config.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='src/config.c' object='config.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o configuration.o `test -f 'src/configuration.c' || echo '$(srcdir)/'`src/configuration.c
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o config.o `test -f 'src/config.c' || echo '$(srcdir)/'`src/config.c
configuration.obj: src/configuration.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT configuration.obj -MD -MP -MF $(DEPDIR)/configuration.Tpo -c -o configuration.obj `if test -f 'src/configuration.c'; then $(CYGPATH_W) 'src/configuration.c'; else $(CYGPATH_W) '$(srcdir)/src/configuration.c'; fi`
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/configuration.Tpo $(DEPDIR)/configuration.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='src/configuration.c' object='configuration.obj' libtool=no @AMDEPBACKSLASH@
config.obj: src/config.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT config.obj -MD -MP -MF $(DEPDIR)/config.Tpo -c -o config.obj `if test -f 'src/config.c'; then $(CYGPATH_W) 'src/config.c'; else $(CYGPATH_W) '$(srcdir)/src/config.c'; fi`
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/config.Tpo $(DEPDIR)/config.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='src/config.c' object='config.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o configuration.obj `if test -f 'src/configuration.c'; then $(CYGPATH_W) 'src/configuration.c'; else $(CYGPATH_W) '$(srcdir)/src/configuration.c'; fi`
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o config.obj `if test -f 'src/config.c'; then $(CYGPATH_W) 'src/config.c'; else $(CYGPATH_W) '$(srcdir)/src/config.c'; fi`
threads.o: src/threads.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT threads.o -MD -MP -MF $(DEPDIR)/threads.Tpo -c -o threads.o `test -f 'src/threads.c' || echo '$(srcdir)/'`src/threads.c

View file

@ -44,7 +44,7 @@ typedef struct {
pthread_mutex_t mutex;
} buffer_t;
/* Prototypes */
/* prototypes */
void buffer_init(buffer_t *buf);
reading_t * buffer_push(buffer_t *buf, reading_t *rd);
void buffer_free(buffer_t *buf);

View file

@ -32,21 +32,27 @@
#include "vzlogger.h"
#include "buffer.h"
typedef struct {
typedef struct channel {
char id[5]; /* only for internal usage & debugging */
char *middleware; /* url to middleware */
char *uuid; /* unique identifier for middleware */
reading_id_t identifier; /* channel identifier (OBIS, string) */
buffer_t buffer; /* circular queue to buffer readings */
pthread_cond_t condition; /* pthread syncronization to notify logging thread and local webserver */
pthread_t thread; /* pthread for asynchronus logging */
pthread_status_t status;
pthread_status_t status; /* status of thread */
char *middleware; /* url to middleware */
char *uuid; /* unique identifier for middleware */
double last; /* last counter value */
int counter:1; /* TRUE if we want to send the diffrence between to values */
} channel_t;
/* Prototypes */
void channel_init(channel_t *ch, const char *uuid, const char *middleware, reading_id_t identifier);
/* prototypes */
void channel_init(channel_t *ch, const char *uuid, const char *middleware, reading_id_t identifier, int counter);
void channel_free(channel_t *ch);
reading_t * channel_add_readings(channel_t *ch, meter_protocol_t protocol, reading_t *rds, size_t n);
#endif /* _CHANNEL_H_ */

View file

@ -0,0 +1,94 @@
/**
* Parsing commandline options and channel list
*
* @author Steffen Vogel <info@steffenvogel.de>
* @copyright Copyright (c) 2011, The volkszaehler.org project
* @package vzlogger
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*/
/*
* This file is part of volkzaehler.org
*
* volkzaehler.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* volkzaehler.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _CONFIGURATION_H_
#define _CONFIGURATION_H_
#include <json/json.h>
#include <list.h>
#include <options.h>
/**
* General options from CLI
*/
typedef struct {
char *config; /* filename of configuration */
char *log; /* filename for logging */
FILE *logfd;
int port; /* TCP port for local interface */
int verbosity; /* verbosity level */
int comet_timeout; /* in seconds; */
int buffer_length; /* in seconds; how long to buffer readings for local interfalce */
int retry_pause; /* in seconds; how long to pause after an unsuccessful HTTP request */
/* boolean bitfields, padding at the end of struct */
int channel_index:1; /* give a index of all available channels via local interface */
int daemon:1; /* run in background */
int foreground:1; /* dont fork in background */
int local:1; /* enable local interface */
int logging:1; /* start logging threads, depends on local & daemon */
} config_options_t;
/* forward declarartions */
struct map;
struct channel;
/**
* Reads key, value and type from JSON object
*
* @param jso the JSON object
* @param key the key of the option
* @param option a pointer to a uninitialized option_t structure
* @return 0 on succes, <0 on error
*/
int option_init(struct json_object *jso, char *key, option_t *option);
/**
* Validate UUID
* Match against something like: '550e8400-e29b-11d4-a716-446655440000'
*
* @param const char *uuid a string containing to uuid
* @return int non-zero on success
*/
int config_validate_uuid(const char *uuid);
/**
* Parse JSON formatted configuration file
*
* @param const char *filename the path of the configuration file
* @param list_t *mappings a pointer to a list, where new channel<->meter mappings should be stored
* @param config_options_t *options a pointer to a structure of global configuration options
* @return int non-zero on success
*/
int config_parse(const char *filename, list_t *mappings, config_options_t *options);
struct channel * config_parse_channel(struct json_object *jso);
struct map * config_parse_meter(struct json_object *jso);
#endif /* _CONFIGURATION_H_ */

View file

@ -26,7 +26,9 @@
#ifndef _LOCAL_H_
#define _LOCAL_H_
#include <stdarg.h> /* required for MHD */
#include <stdarg.h> /* required for libMHD */
#include <sys/socket.h> /* required for libMHD */
#include <microhttpd.h>
int handle_request(

View file

@ -27,64 +27,40 @@
#define _VZLOGGER_H_
#include <pthread.h>
#include <meter.h>
#include <common.h>
#include <config.h>
#include "config.h"
#include "list.h"
enum {
LOG_ERROR = -1,
LOG_STD = 0,
LOG_INFO = 5,
LOG_DEBUG = 10,
LOG_FINEST = 15
};
/* enumerations */
typedef enum {
UNKNOWN,
RUNNING,
TERMINATED,
CANCELED
status_unknown,
status_running,
status_terminated,
status__cancelled
} pthread_status_t;
/**
* Type for associating channels to meters
* Type for mapping channels to meters
*/
typedef struct {
typedef struct map {
meter_t meter;
list_t channels;
int interval;
pthread_t thread;
pthread_status_t status;
} assoc_t;
} map_t;
/**
* Options from command line
*/
typedef struct {
char *config; /* filename of configuration */
char *log; /* filename for logging */
FILE *logfd;
int port; /* TCP port for local interface */
int verbosity; /* verbosity level */
int comet_timeout; /* in seconds; */
int buffer_length; /* in seconds; how long to buffer readings for local interfalce */
int retry_pause; /* in seconds; how long to pause after an unsuccessful HTTP request */
/* boolean bitfields, padding at the end of struct */
int channel_index:1; /* give a index of all available channels via local interface */
int daemon:1; /* run in background */
int foreground:1; /* dont fork in background */
int local:1; /* enable local interface */
int logging:1; /* start logging threads, depends on local & daemon */
} options_t;
/* Prototypes */
void print(int level, const char *format, void *id, ... );
void usage(char ** argv);
/* prototypes */
void quit(int sig);
void parse_options(int argc, char *argv[], options_t *options);
void daemonize();
void show_usage(char ** argv);
void show_aliases();
int options_parse(int argc, char *argv[], config_options_t *options);
#endif /* _VZLOGGER_H_ */

View file

@ -33,7 +33,7 @@
#include "api.h"
#include "vzlogger.h"
extern options_t options;
extern config_options_t options;
int curl_custom_debug_callback(CURL *curl, curl_infotype type, char *data, size_t size, void *arg) {
channel_t *ch = (channel_t *) arg;
@ -45,17 +45,17 @@ int curl_custom_debug_callback(CURL *curl, curl_infotype type, char *data, size_
case CURLINFO_TEXT:
case CURLINFO_END:
if (end) *end = '\0'; /* terminate without \n */
print(11, "CURL: %.*s", ch, (int) size, data);
print(log_debug+5, "CURL: %.*s", ch, (int) size, data);
break;
case CURLINFO_SSL_DATA_IN:
case CURLINFO_DATA_IN:
print(14, "CURL: Received %lu bytes", ch, (unsigned long) size);
print(log_debug+5, "CURL: Received %lu bytes", ch, (unsigned long) size);
break;
case CURLINFO_SSL_DATA_OUT:
case CURLINFO_DATA_OUT:
print(14, "CURL: Sent %lu bytes.. ", ch, (unsigned long) size);
print(log_debug+5, "CURL: Sent %lu bytes.. ", ch, (unsigned long) size);
break;
case CURLINFO_HEADER_IN:
@ -72,7 +72,7 @@ size_t curl_custom_write_callback(void *ptr, size_t size, size_t nmemb, void *da
response->data = realloc(response->data, response->size + realsize + 1);
if (response->data == NULL) { /* out of memory! */
print(-1, "Not enough memory", NULL);
print(log_error, "Cannot allocate memory", NULL);
exit(EXIT_FAILURE);
}
@ -122,7 +122,7 @@ CURL * api_curl_init(channel_t *ch) {
curl = curl_easy_init();
if (!curl) {
print(-1, "CURL: cannot create handle", ch);
print(log_error, "CURL: cannot create handle", ch);
exit(EXIT_FAILURE);
}

View file

@ -76,7 +76,7 @@ reading_t * buffer_push(buffer_t *buf, reading_t *rd) {
}
new->next = NULL;
buf->tail = new;
buf->size++;
pthread_mutex_unlock(&buf->mutex);

View file

@ -30,12 +30,13 @@
#include "channel.h"
void channel_init(channel_t *ch, const char *uuid, const char *middleware, reading_id_t identifier) {
void channel_init(channel_t *ch, const char *uuid, const char *middleware, reading_id_t identifier, int counter) {
static int instances; /* static to generate channel ids */
snprintf(ch->id, 5, "ch%i", instances++);
ch->identifier = identifier;
ch->status = UNKNOWN;
ch->status = status_unknown;
ch->counter = counter;
ch->uuid = strdup(uuid);
ch->middleware = strdup(middleware);
@ -50,7 +51,47 @@ void channel_init(channel_t *ch, const char *uuid, const char *middleware, readi
void channel_free(channel_t *ch) {
buffer_free(&ch->buffer);
pthread_cond_destroy(&ch->condition);
free(ch->uuid);
free(ch->middleware);
}
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 last;
double value;
for (int i = 0; i < n; i++) {
int add = FALSE;
if (protocol == meter_protocol_d0 || protocol == meter_protocol_sml) {
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 (add) {
value = rds[i].value;
print(log_info, "Adding reading to queue (value=%.2f delta=%.2f ts=%.3f)", ch, value, value - last, tvtod(rds[i].time));
if (ch->counter) {
rds[i].value -= last;
}
reading_t *added = buffer_push(&ch->buffer, &rds[i]); /* remember last value to calculate relative consumption */
if (first == NULL) {
first = added;
}
}
}
ch->last = last;
return first;
}

288
bin/logger/src/config.c Normal file
View file

@ -0,0 +1,288 @@
/**
* Parsing Apache HTTPd-like configuration
*
* @author Steffen Vogel <info@steffenvogel.de>
* @copyright Copyright (c) 2011, The volkszaehler.org project
* @package vzlogger
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*/
/*
* This file is part of volkzaehler.org
*
* volkzaehler.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* volkzaehler.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include "config.h"
#include "channel.h"
static const char *option_type_str[] = { "null", "boolean", "double", "int", "object", "array", "string" };
int config_parse(const char *filename, list_t *mappings, config_options_t *options) {
struct json_object *json_cfg = NULL;
struct json_tokener *json_tok = json_tokener_new();
char buf[JSON_FILE_BUF_SIZE];
int line = 0;
/* open configuration file */
FILE *file = fopen(filename, "r");
if (file == NULL) {
print(log_error, "Cannot open configfile %s: %s", NULL, filename, strerror(errno)); /* why didn't the file open? */
exit(EXIT_FAILURE);
}
else {
print(log_info, "Start parsing configuration from %s", NULL, filename);
}
/* parse JSON */
while(fgets(buf, JSON_FILE_BUF_SIZE, file)) {
line++;
json_cfg = json_tokener_parse_ex(json_tok, buf, strlen(buf));
if (json_tok->err > 1) {
print(log_error, "Error in %s:%d %s at offset %d", NULL, filename, line, json_tokener_errors[json_tok->err], json_tok->char_offset);
exit(EXIT_FAILURE);
}
}
/* householding */
fclose(file);
json_tokener_free(json_tok);
/* parse options */
json_object_object_foreach(json_cfg, key, value) {
enum json_type type = json_object_get_type(value);
if (strcmp(key, "daemon") == 0 && type == json_type_boolean) {
options->daemon = json_object_get_boolean(value);
}
else if (strcmp(key, "foreground") == 0 && type == json_type_boolean) {
options->foreground = json_object_get_boolean(value);
}
else if (strcmp(key, "log") == 0 && type == json_type_string) {
options->log = strdup(json_object_get_string(value));
}
else if (strcmp(key, "retry") == 0 && type == json_type_int) {
options->retry_pause = json_object_get_int(value);
}
else if (strcmp(key, "verbosity") == 0 && type == json_type_int) {
options->verbosity = json_object_get_int(value);
}
else if (strcmp(key, "local") == 0) {
json_object_object_foreach(value, key, local_value) {
enum json_type local_type = json_object_get_type(local_value);
if (strcmp(key, "enabled") == 0 && local_type == json_type_boolean) {
options->local = json_object_get_boolean(local_value);
}
else if (strcmp(key, "port") == 0 && local_type == json_type_int) {
options->port = json_object_get_int(local_value);
}
else if (strcmp(key, "timeout") == 0 && local_type == json_type_int) {
options->comet_timeout = json_object_get_int(local_value);
}
else if (strcmp(key, "buffer") == 0 && local_type == json_type_int) {
options->buffer_length = json_object_get_int(local_value);
}
else if (strcmp(key, "index") == 0 && local_type == json_type_boolean) {
options->channel_index = json_object_get_boolean(local_value);
}
else {
print(log_error, "Ignoring invalid field or type (%s=%s (%s))",
NULL, key, json_object_get_string(local_value), option_type_str[local_type]);
}
}
}
else if ((strcmp(key, "sensors") == 0 || strcmp(key, "meters") == 0) && type == json_type_array) {
int len = json_object_array_length(value);
for (int i = 0; i < len; i++) {
map_t *mapping = config_parse_meter(json_object_array_get_idx(value, i));
if (mapping == NULL) {
return ERR;
}
list_push(mappings, mapping);
}
}
else {
print(log_error, "Ignoring invalid field or type (%s=%s (%s))",
NULL, key, json_object_get_string(value), option_type_str[type]);
}
}
json_object_put(json_cfg); /* free allocated memory */
return SUCCESS;
}
map_t * config_parse_meter(struct json_object *jso) {
list_t json_channels;
list_t options;
list_init(&json_channels);
list_init(&options);
json_object_object_foreach(jso, key, value) {
enum json_type type = json_object_get_type(value);
if (strcmp(key, "channels") == 0 && type == json_type_array) {
int len = json_object_array_length(value);
for (int i = 0; i < len; i++) {
list_push(&json_channels, json_object_array_get_idx(value, i));
}
}
else if (strcmp(key, "channel") == 0 && type == json_type_object) {
list_push(&json_channels, value);
}
else { /* all other options will be passed to meter_init() */
option_t *option = malloc(sizeof(option_t));
if (option_init(value, key, option) != SUCCESS) {
print(log_error, "Ignoring invalid type (%s=%s (%s))",
NULL, key, json_object_get_string(value), option_type_str[type]);
}
list_push(&options, option);
}
}
/* init meter */
map_t *mapping = malloc(sizeof(map_t));
list_init(&mapping->channels);
if (meter_init(&mapping->meter, options) != SUCCESS) {
print(log_error, "Failed to initialize meter. Arborting.", mapping);
return NULL;
}
print(log_info, "New meter initialized (protocol=%s)", mapping, meter_get_details(mapping->meter.protocol)->name);
/* init channels */
struct json_object *json_channel;
while ((json_channel = list_pop(&json_channels)) != NULL) {
channel_t *ch = config_parse_channel(json_channel);
if (ch) list_push(&mapping->channels, ch);
}
/* householding */
list_free(&options);
return mapping;
}
channel_t * config_parse_channel(struct json_object *jso) {
const char *uuid = NULL;
const char *middleware = NULL;
const char *identifier = NULL;
int counter = FALSE;
json_object_object_foreach(jso, key, value) {
enum json_type type = json_object_get_type(value);
if (strcmp(key, "uuid") == 0 && type == json_type_string) {
uuid = json_object_get_string(value);
}
else if (strcmp(key, "middleware") == 0 && type == json_type_string) {
middleware = json_object_get_string(value);
}
else if (strcmp(key, "identifier") == 0 && type == json_type_string) {
identifier = json_object_get_string(value);
}
else if (strcmp(key, "counter") == 0 && type == json_type_boolean) {
counter = json_object_get_boolean(value);
}
else {
print(log_error, "Ignoring invalid field or type (%s=%s (%s))",
NULL, key, json_object_get_string(value), option_type_str[type]);
}
}
if (uuid == NULL) {
print(log_error, "Missing UUID", NULL);
exit(EXIT_FAILURE);
}
else if (!config_validate_uuid(uuid)) {
print(log_error, "Invalid UUID: %s", NULL, uuid);
exit(EXIT_FAILURE);
}
else if (middleware == NULL) {
print(log_error, "Missing middleware", NULL);
exit(EXIT_FAILURE);
}
// TODO other identifiers are not supported at the moment
reading_id_t id;
if (identifier) {
if (obis_parse(identifier, &id.obis) != SUCCESS) {
if (obis_lookup_alias(identifier, &id.obis) != SUCCESS) {
print(log_error, "Invalid id: %s", NULL, identifier);
return NULL;
}
}
}
else {
obis_init(&id.obis, NULL);
}
char obis_str[OBIS_STR_LEN];
obis_unparse(id.obis, obis_str, 6*3+5+1);
channel_t *ch = malloc(sizeof(channel_t));
channel_init(ch, uuid, middleware, id, counter);
print(log_info, "New channel initialized (uuid=...%s middleware=%s id=%s counter=%s)", ch, uuid+30, middleware, obis_str, (counter) ? "yes" : "no");
return ch;
}
int config_validate_uuid(const char *uuid) {
for (const char *p = uuid; *p; p++) {
switch (p - uuid) {
case 8:
case 13:
case 18:
case 23:
if (*p != '-') return FALSE;
else break;
default:
if (!isxdigit(*p)) return FALSE;
else break;
}
}
return TRUE;
}
int option_init(struct json_object *jso, char *key, option_t *option) {
option_value_t val;
switch (json_object_get_type(jso)) {
case json_type_string: val.string = json_object_get_string(jso); break;
case json_type_int: val.integer = json_object_get_int(jso); break;
case json_type_boolean: val.boolean = json_object_get_boolean(jso); break;
case json_type_double: val.floating = json_object_get_double(jso); break;
default: return ERR;
}
option->key = key;
option->type = json_object_get_type(jso);
option->value = val;
return SUCCESS;
}

View file

@ -33,7 +33,7 @@
#include "local.h"
#include "api.h"
extern options_t options;
extern config_options_t options;
int handle_request(void *cls, struct MHD_Connection *connection, const char *url, const char *method,
const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) {
@ -41,12 +41,12 @@ int handle_request(void *cls, struct MHD_Connection *connection, const char *url
int status;
int response_code = MHD_HTTP_NOT_FOUND;
list_t *assocs = (list_t *) cls;
list_t *mappings = (list_t *) cls;
struct MHD_Response *response;
const char *mode = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "mode");
print(2, "Local request received: method=%s url=%s mode=%s", "http", method, url, mode);
print(log_info+1, "Local request received: method=%s url=%s mode=%s", "http", method, url, mode);
if (strcmp(method, "GET") == 0) {
struct timespec ts;
@ -67,17 +67,13 @@ int handle_request(void *cls, struct MHD_Connection *connection, const char *url
else {
json_exception = json_object_new_object();
json_object_object_add(json_exception, "message", json_object_new_string("channel indexing is disabled"));
json_object_object_add(json_exception, "message", json_object_new_string("channel index is disabled"));
json_object_object_add(json_exception, "code", json_object_new_int(0));
}
}
foreach(*assocs, it) {
assoc_t *assoc = (assoc_t *) it->data;
foreach(assoc->channels, it) {
channel_t *ch = (channel_t *) it->data;
foreach(*mappings, mapping, map_t) {
foreach(mapping->channels, ch, channel_t) {
if (strcmp(ch->uuid, uuid) == 0 || show_all) {
response_code = MHD_HTTP_OK;
@ -97,8 +93,9 @@ int handle_request(void *cls, struct MHD_Connection *connection, const char *url
json_object_object_add(json_ch, "uuid", json_object_new_string(ch->uuid));
json_object_object_add(json_ch, "middleware", json_object_new_string(ch->middleware));
json_object_object_add(json_ch, "interval", json_object_new_int(assoc->interval));
json_object_object_add(json_ch, "protocol", json_object_new_string(assoc->meter.type->name));
json_object_object_add(json_ch, "last", json_object_new_double(ch->last));
json_object_object_add(json_ch, "interval", json_object_new_int(mapping->meter.interval));
json_object_object_add(json_ch, "protocol", json_object_new_string(meter_get_details(mapping->meter.protocol)->name));
struct json_object *json_tuples = api_json_tuples(&ch->buffer, ch->buffer.head, ch->buffer.tail);
json_object_object_add(json_ch, "tuples", json_tuples);

View file

@ -30,58 +30,60 @@
#include "api.h"
#include "vzlogger.h"
extern options_t options;
extern config_options_t options;
void reading_thread_cleanup(void *rds) {
free(rds);
}
void * reading_thread(void *arg) {
assoc_t *assoc = (assoc_t *) arg;
meter_t *mtr = &assoc->meter;
reading_t *rds = malloc(sizeof(reading_t) * mtr->type->max_readings);
reading_t *rds;
map_t *mapping;
meter_t *mtr;
time_t last, delta;
const meter_details_t *details;
size_t n = 0;
mapping = (map_t *) arg;
mtr = &mapping->meter;
details = meter_get_details(mtr->protocol);
/* allocate memory for readings */
size_t bytes = sizeof(reading_t) * details->max_readings;
rds = malloc(bytes);
memset(rds, 0, bytes);
pthread_cleanup_push(&reading_thread_cleanup, rds);
do { /* start thread main loop */
/* fetch readings from meter and measure interval */
last = time(NULL);
n = meter_read(mtr, rds, mtr->type->max_readings);
n = meter_read(mtr, rds, details->max_readings);
delta = time(NULL) - last;
/* update buffer length with current interval */
if (!mtr->type->periodic && delta != assoc->interval) {
print(15, "Updating interval to %i", mtr, delta);
assoc->interval = delta;
/* 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];
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));
}
}
foreach(assoc->channels, it) {
channel_t *ch = (channel_t *) it->data;
/* update buffer length with current interval */
if (!details->periodic && mtr->interval != delta) {
print(log_debug, "Updating interval to %i", mtr, delta);
mtr->interval = delta;
}
foreach(mapping->channels, ch, channel_t) {
buffer_t *buf = &ch->buffer;
reading_t *added = NULL;
for (int i = 0; i < n; i++) {
switch (mtr->type->id) {
case SML:
case D0:
if (obis_compare(rds[i].identifier.obis, ch->identifier.obis) == 0) {
print(5, "New reading (value=%.2f ts=%f)", ch, ch->id, rds[i].value, tvtod(rds[i].time));
added = buffer_push(buf, &rds[i]);
}
break;
default:
/* no channel identifier, adding all readings to buffer */
print(LOG_INFO, "New reading (value=%.2f ts=%f)", ch, ch->id, rds[i].value, tvtod(rds[i].time));
added = buffer_push(buf, &rds[i]);
}
}
reading_t *added = channel_add_readings(ch, mtr->protocol, rds, n);
/* update buffer length to interval */
if (options.local && assoc->interval != 0) {
ch->buffer.keep = ceil(options.buffer_length / assoc->interval);
if (options.local) {
buf->keep = ceil(options.buffer_length / mtr->interval);
}
/* queue reading into sending buffer logging thread if
@ -99,16 +101,16 @@ void * reading_thread(void *arg) {
pthread_mutex_unlock(&buf->mutex);
/* debugging */
if (options.verbosity >= 10) {
if (options.verbosity >= log_debug) {
char dump[1024];
buffer_dump(buf, dump, 1024);
print(LOG_DEBUG, "Buffer dump: %s (size=%i, keep=%i)", ch, dump, buf->size, buf->keep);
print(log_debug, "Buffer dump: %s (size=%i, keep=%i)", ch, dump, buf->size, buf->keep);
}
}
if ((options.daemon || options.local) && mtr->type->periodic) {
print(LOG_INFO, "Next reading in %i seconds", mtr, assoc->interval);
sleep(assoc->interval); // TODO handle parsing
if ((options.daemon || options.local) && details->periodic) {
print(log_info, "Next reading in %i seconds", mtr, mtr->interval);
sleep(mtr->interval);
}
} while (options.daemon || options.local);
@ -124,13 +126,12 @@ void logging_thread_cleanup(void *arg) {
void * logging_thread(void *arg) {
channel_t *ch = (channel_t *) arg; /* casting argument */
CURL *curl = api_curl_init(ch);
pthread_cleanup_push(&logging_thread_cleanup, curl);
do { /* start thread mainloop */
CURLresponse response;
json_object *json_obj;
reading_t *last;
const char *json_str;
long int http_code, curl_code;
@ -145,11 +146,12 @@ void * logging_thread(void *arg) {
}
pthread_mutex_unlock(&ch->buffer.mutex);
last = ch->buffer.tail;
json_obj = api_json_tuples(&ch->buffer, ch->buffer.sent, last);
reading_t *first = ch->buffer.sent;
reading_t *last = ch->buffer.tail;
json_obj = api_json_tuples(&ch->buffer, first, last);
json_str = json_object_to_json_string(json_obj);
print(10, "JSON request body: %s", ch, json_str);
print(log_debug, "JSON request body: %s", ch, json_str);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_str);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_custom_write_callback);
@ -160,17 +162,17 @@ void * logging_thread(void *arg) {
/* check response */
if (curl_code == CURLE_OK && http_code == 200) { /* everything is ok */
print(4, "Request succeeded with code: %i", ch, http_code);
print(log_debug, "Request succeeded with code: %i", ch, http_code);
ch->buffer.sent = last->next;
}
else { /* error */
if (curl_code != CURLE_OK) {
print(-1, "CURL: %s", ch, curl_easy_strerror(curl_code));
print(log_error, "CURL: %s", ch, curl_easy_strerror(curl_code));
}
else if (http_code != 200) {
char err[255];
api_parse_exception(response, err, 255);
print(-1, "Error from middleware: %s", ch, err);
print(log_error, "Error from middleware: %s", ch, err);
}
}
@ -179,7 +181,7 @@ void * logging_thread(void *arg) {
json_object_put(json_obj);
if (options.daemon && (curl_code != CURLE_OK || http_code != 200)) {
print(1, "Waiting %i secs for next request due to previous failure", ch, options.retry_pause);
print(log_info, "Waiting %i secs for next request due to previous failure", ch, options.retry_pause);
sleep(options.retry_pause);
}
} while (options.daemon);

View file

@ -23,10 +23,9 @@
* along with volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h> /* for print() */
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
@ -36,12 +35,15 @@
#include <curl/curl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <list.h>
#include <meter.h>
#include <obis.h>
#include "vzlogger.h"
#include "list.h"
#include "channel.h"
#include "configuration.h"
#include "threads.h"
#ifdef LOCAL_SUPPORT
@ -49,10 +51,8 @@
#include "local.h"
#endif /* LOCAL_SUPPORT */
extern const meter_type_t meter_types[];
list_t assocs; /* mapping between meters and channels */
options_t options; /* global application options */
list_t mappings; /* mapping between meters and channels */
config_options_t options; /* global application options */
/**
* Command line options
@ -91,79 +91,94 @@ const char *long_options_descs[] = {
};
/**
* Print available options and some other usefull information
* Print error/debug/info messages to stdout and/or logfile
*
* @param id could be NULL for general messages
* @todo integrate into syslog
*/
void usage(char *argv[]) {
void print(int level, const char *format, void *id, ... ) {
va_list args;
if (level > options.verbosity) {
return; /* skip message if its under the verbosity level */
}
struct timeval now;
struct tm * timeinfo;
char buffer[1024];
char *pos = buffer;
gettimeofday(&now, NULL);
timeinfo = localtime(&now.tv_sec);
/* print timestamp to buffer */
pos += sprintf(pos, "[");
pos += strftime(pos, 16, "%b %d %H:%M:%S", timeinfo);
/* print logging 'section' */
pos += (id != NULL) ? sprintf(pos, "][%s]", (char *) id) : sprintf(pos, "]");
/* fill with whitespaces */
while(pos - buffer < 24) {
pos += sprintf(pos, " ");
}
/* print formatstring */
va_start(args, id);
pos += vsprintf(pos, format, args);
va_end(args);
/* print to stdout/stderr */
fprintf((level > 0) ? stdout : stderr, "%s\n", buffer);
/* append to logfile */
if (options.logfd) {
fprintf(options.logfd, "%s\n", buffer);
fflush(options.logfd);
}
}
/**
* Print available options, protocols and OBIS aliases
*/
void show_usage(char *argv[]) {
const char **desc = long_options_descs;
const struct option *op = long_options;
const meter_type_t *type = meter_types;
printf("Usage: %s [options]\n\n", argv[0]);
printf(" following options are available:\n");
/* command line options */
printf(" following options are available:\n");
while (op->name && desc) {
printf("\t-%c, --%-12s\t%s\n", op->val, op->name, *desc);
op++; desc++;
}
printf("\n");
printf(" following protocol types are supported:\n");
while (type->name) {
printf("\t%-12s\t%s\n", type->name, type->desc);
type++;
/* protocols */
printf("\n following protocol types are supported:\n");
for (const meter_details_t *it = meter_get_protocols(); it->name != NULL; it++) {
printf("\t%-12s\t%s\n", it->name, it->desc);
}
/* obis aliases */
printf("\n following OBIS aliases are available:\n");
char obis_str[OBIS_STR_LEN];
for (const obis_alias_t *it = obis_get_aliases(); it->name != NULL; it++) {
obis_unparse(it->id, obis_str, OBIS_STR_LEN);
printf("\t%-17s%-31s%-22s\n", it->name, it->desc, obis_str);
}
/* footer */
printf("\n%s - volkszaehler.org logging utility\n", PACKAGE_STRING);
printf("by Steffen Vogel <stv0g@0l.de>\n");
printf("send bugreports to %s\n", PACKAGE_BUGREPORT);
}
/**
* Wrapper to log notices and errors
* Fork process to background
*
* @param ch could be NULL for general messages
* @todo integrate into syslog
* @link http://www.enderunix.org/docs/eng/daemon.php
*/
void print(int level, const char *format, void *id, ... ) {
va_list args;
struct timeval now;
struct tm * timeinfo;
char buffer[1024], *pos = buffer;
if (level <= options.verbosity) {
gettimeofday(&now, NULL);
timeinfo = localtime(&now.tv_sec);
pos += sprintf(pos, "[");
pos += strftime(pos, 16, "%b %d %H:%M:%S", timeinfo);
if (id != NULL) {
pos += sprintf(pos, "][%s]", (char *) id);
}
else {
pos += sprintf(pos, "]");
}
while(pos - buffer < 24) {
pos += sprintf(pos, " ");
}
va_start(args, id);
pos += vsprintf(pos, format, args);
va_end(args);
fprintf((level > 0) ? stdout : stderr, "%s\n", buffer);
if (options.logfd) {
fprintf(options.logfd, "%s\n", buffer);
fflush(options.logfd);
}
}
}
/* http://www.enderunix.org/docs/eng/daemon.php */
void daemonize() {
if(getppid() == 1) {
return; /* already a daemon */
@ -181,7 +196,7 @@ void daemonize() {
setsid(); /* obtain a new process group */
for (i=getdtablesize();i>=0;--i) {
for (i = getdtablesize(); i >= 0; --i) {
close(i); /* close all descriptors */
}
@ -211,15 +226,12 @@ void daemonize() {
* Threads gets joined in main()
*/
void quit(int sig) {
print(2, "Closing connections to terminate", NULL);
print(log_info, "Closing connections to terminate", NULL);
foreach(assocs, it) {
assoc_t *assoc = (assoc_t *) it->data;
foreach(mappings, mapping, map_t) {
pthread_cancel(mapping->thread);
pthread_cancel(assoc->thread);
foreach(assoc->channels, it) {
channel_t *ch = (channel_t *) it->data;
foreach(mapping->channels, ch, channel_t) {
pthread_cancel(ch->thread);
}
}
@ -227,8 +239,11 @@ void quit(int sig) {
/**
* Parse options from command line
*
* @param options pointer to structure for options
* @return int 0 on succes, <0 on error
*/
void parse_options(int argc, char * argv[], options_t * options) {
int config_parse_cli(int argc, char * argv[], config_options_t * options) {
while (1) {
int c = getopt_long(argc, argv, "c:o:p:lhVdfv:", long_options, NULL);
@ -274,10 +289,17 @@ void parse_options(int argc, char * argv[], options_t * options) {
case '?':
case 'h':
default:
usage(argv);
exit((c == '?') ? EXIT_FAILURE : EXIT_SUCCESS);
show_usage(argv);
if (c == '?') {
exit(EXIT_FAILURE);
}
else {
exit(EXIT_SUCCESS);
}
}
}
return SUCCESS;
}
/**
@ -307,19 +329,24 @@ int main(int argc, char *argv[]) {
sigaction(SIGHUP, &action, NULL); /* catch hangup signal */
sigaction(SIGTERM, &action, NULL); /* catch kill signal */
/* initialize adts and apis */
/* initialize ADTs and APIs */
curl_global_init(CURL_GLOBAL_ALL);
list_init(&assocs);
list_init(&mappings);
/* parse command line and file options */
// TODO command line should have a higher priority as file
parse_options(argc, argv, &options);
parse_configuration(options.config, &assocs, &options);
if (config_parse_cli(argc, argv, &options) != SUCCESS) {
return EXIT_FAILURE;
}
if (config_parse(options.config, &mappings, &options) != SUCCESS) {
return EXIT_FAILURE;
}
options.logging = (!options.local || options.daemon);
if (!options.foreground && (options.daemon || options.local)) {
print(1, "Daemonize process...", NULL);
print(log_info, "Daemonize process...", NULL);
daemonize();
}
@ -327,46 +354,44 @@ int main(int argc, char *argv[]) {
if (options.log) {
FILE *logfd = fopen(options.log, "a");
if (logfd) {
options.logfd = logfd;
print(LOG_DEBUG, "Opened logfile %s", NULL, options.log);
}
else {
print(LOG_ERROR, "Cannot open logfile %s: %s", NULL, options.log, strerror(errno));
exit(EXIT_FAILURE);
if (logfd == NULL) {
print(log_error, "Cannot open logfile %s: %s", NULL, options.log, strerror(errno));
return EXIT_FAILURE;
}
options.logfd = logfd;
print(log_debug, "Opened logfile %s", NULL, options.log);
}
if (assocs.size == 0) {
print(6, "No meters found!", NULL);
exit(EXIT_FAILURE);
if (mappings.size <= 0) {
print(log_error, "No meters found!", NULL);
return EXIT_FAILURE;
}
/* open connection meters & start threads */
foreach(assocs, it) {
assoc_t *assoc = (assoc_t *) it->data;
meter_t *mtr = &assoc->meter;
foreach(mappings, mapping, map_t) {
meter_t *mtr = &mapping->meter;
int res = meter_open(mtr);
if (res < 0) {
print(LOG_ERROR, "Failed to open meter", mtr);
exit(EXIT_FAILURE);
if (meter_open(mtr) != SUCCESS) {
print(log_error, "Failed to open meter. Aborting.", mtr);
return EXIT_FAILURE;
}
else {
print(log_info, "Meter connection established", mtr);
}
print(5, "Meter connected", mtr);
pthread_create(&assoc->thread, NULL, &reading_thread, (void *) assoc);
print(5, "Meter thread started", mtr);
foreach(assoc->channels, it) {
channel_t *ch = (channel_t *) it->data;
pthread_create(&mapping->thread, NULL, &reading_thread, (void *) mapping);
print(log_debug, "Meter thread started", mtr);
foreach(mapping->channels, ch, channel_t) {
/* set buffer length for perriodic meters */
if (mtr->type->periodic && options.local) {
ch->buffer.keep = ceil(options.buffer_length / (double) assoc->interval);
if (meter_get_details(mtr->protocol)->periodic && options.local) {
ch->buffer.keep = ceil(options.buffer_length / (double) mapping->meter.interval);
}
if (ch->status != RUNNING && options.logging) {
if (ch->status != status_running && options.logging) {
pthread_create(&ch->thread, NULL, &logging_thread, (void *) ch);
print(5, "Logging thread started", ch);
print(log_debug, "Logging thread started", ch);
}
}
}
@ -375,49 +400,46 @@ int main(int argc, char *argv[]) {
/* start webserver for local interface */
struct MHD_Daemon *httpd_handle = NULL;
if (options.local) {
print(5, "Starting local interface HTTPd on port %i", "http", options.port);
print(log_info, "Starting local interface HTTPd on port %i", "http", options.port);
httpd_handle = MHD_start_daemon(
MHD_USE_THREAD_PER_CONNECTION,
options.port,
NULL, NULL,
&handle_request, &assocs,
&handle_request, &mappings,
MHD_OPTION_END
);
}
#endif /* LOCAL_SUPPORT */
/* wait for all threads to terminate */
foreach(assocs, it) {
assoc_t *assoc = (assoc_t *) it->data;
meter_t *mtr = &assoc->meter;
foreach(mappings, mapping, map_t) {
meter_t *mtr = &mapping->meter;
pthread_join(assoc->thread, NULL);
foreach(assoc->channels, it) {
channel_t *ch = (channel_t *) it->data;
pthread_join(mapping->thread, NULL);
foreach(mapping->channels, ch, channel_t) {
pthread_join(ch->thread, NULL);
channel_free(ch);
}
list_free(&assoc->channels);
list_free(&mapping->channels);
meter_close(mtr); /* closing connection */
meter_free(mtr);
}
#ifdef LOCAL_SUPPORT
/* stop webserver */
if (httpd_handle) {
print(8, "Stopping local interface HTTPd", "http");
MHD_stop_daemon(httpd_handle);
}
#endif /* LOCAL_SUPPORT */
/* householding */
list_free(&assocs);
list_free(&mappings);
curl_global_cleanup();
/* close logfile */
if (options.logfd) {
fclose(options.logfd);
}

35
include/common.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef _COMMON_H_
#define _COMMON_H_
#include <stdarg.h>
/* enumerations */
typedef enum {
log_error = -1,
log_warning = 0,
log_info = 5,
log_debug = 10,
log_finest = 15
} log_level_t;
/* types */
typedef unsigned char bool;
/* constants */
#ifndef TRUE
# define TRUE 1
#endif
#ifndef FALSE
# define FALSE 0
#endif
#define SUCCESS 0
#define ERR -1
#define ERR_NOT_FOUND -2
#define ERR_INVALID_TYPE -3
/* prototypes */
void print(log_level_t lvl, const char *format, void *id, ... );
#endif /* _COMMON_H_ */

View file

@ -34,15 +34,21 @@
#include <termios.h>
typedef struct {
char *host;
char *device;
int baudrate;
int fd; /* file descriptor of port */
struct termios oldtio; /* required to reset port */
} meter_handle_d0_t;
struct meter; /* forward declaration */
struct reading; /* forward declaration */
/* forward declarations */
struct meter;
struct reading;
int meter_init_d0(struct meter *mtr, list_t options);
int meter_open_d0(struct meter *mtr);
void meter_close_d0(struct meter *mtr);
int meter_close_d0(struct meter *mtr);
size_t meter_read_d0(struct meter *mtr, struct reading *rds, size_t n);
int meter_d0_parse_line(struct reading *rd, char *line, size_t n);

45
include/exec.h Normal file
View file

@ -0,0 +1,45 @@
/**
* Get data by calling programs
*
* @package vzlogger
* @copyright Copyright (c) 2011, The volkszaehler.org project
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <info@steffenvogel.de>
*/
/*
* This file is part of volkzaehler.org
*
* volkzaehler.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* volkzaehler.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _EXEC_H_
#define _EXEC_H_
#include <stdio.h>
typedef struct {
char *command;
char *regex;
} meter_handle_exec_t;
/* forward declarations */
struct meter;
struct reading;
int meter_init_exec(struct meter *mtr, list_t options);
int meter_open_exec(struct meter *mtr);
int meter_close_exec(struct meter *mtr);
size_t meter_read_exec(struct meter *mtr, struct reading *rds, size_t n);
#endif /* _EXEC_H_ */

47
include/file.h Normal file
View file

@ -0,0 +1,47 @@
/**
* Read data from files & fifos
*
* @package vzlogger
* @copyright Copyright (c) 2011, The volkszaehler.org project
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <info@steffenvogel.de>
*/
/*
* This file is part of volkzaehler.org
*
* volkzaehler.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* volkzaehler.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _FILE_H_
#define _FILE_H_
#include <stdio.h>
typedef struct {
char *path;
char *regex;
FILE *fd;
} meter_handle_file_t;
/* forward declarations */
struct meter;
struct reading;
int meter_init_file(struct meter *mtr, list_t options);
int meter_open_file(struct meter *mtr);
int meter_close_file(struct meter *mtr);
size_t meter_read_file(struct meter *mtr, struct reading *rds, size_t n);
#endif /* _FILE_H_ */

116
include/list.h Normal file
View file

@ -0,0 +1,116 @@
/**
* Generic linked list
*
* @package vzlogger
* @copyright Copyright (c) 2011, The volkszaehler.org project
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <info@steffenvogel.de>
*/
/*
* This file is part of volkzaehler.org
*
* volkzaehler.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* volkzaehler.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _LIST_H_
#define _LIST_H_
#include <stdlib.h>
#define CONCAT2(a, b) a ## b
#define CONCAT(a, b) CONCAT2(a, b)
#define UNIQUE(prefix) CONCAT(__ ## prefix ## _, __LINE__ )
#define foreach(list, value, type) \
__list_item_t *UNIQUE(it) = (list).head; \
for( \
type * (value) = UNIQUE(it)->data; \
({ \
if (UNIQUE(it)) { \
(value) = UNIQUE(it)->data; \
} \
; UNIQUE(it) != NULL; \
}); \
UNIQUE(it) = UNIQUE(it)->next \
) \
typedef struct __list_item {
void *data;
struct __list_item *prev;
struct __list_item *next;
} __list_item_t;
typedef struct {
size_t size;
__list_item_t *head;
__list_item_t *tail;
} list_t;
inline void list_init(list_t *list) {
list->size = 0;
list->head = list->tail = NULL;
}
inline size_t list_push(list_t *list, void *data) {
__list_item_t *new = malloc(sizeof(__list_item_t));
if (new == NULL) return -1; /* cannot allocate memory */
new->data = data;
new->prev = list->tail;
new->next = NULL;
if (list->tail == NULL) {
list->head = new;
}
else {
list->tail->next = new;
}
list->tail = new;
list->size = list->size + 1;
return list->size;
}
inline void * list_pop(list_t *list) {
__list_item_t *old = list->tail;
if (old == NULL) {
return NULL;
}
void *data = old->data;
list->tail = old->prev;
list->size--;
free(old);
return data;
}
inline void list_free(list_t *list) {
while (list->head != NULL) {
__list_item_t *old = list->head;
list->head = old->next;
free(old->data);
free(old);
}
list_init(list);
}
#endif /* _LIST_H_ */

View file

@ -36,64 +36,77 @@
* The 'interval' column in the configuration as no meaning.
*/
#include <sys/socket.h>
#include <sys/time.h>
#include <config.h>
#include "config.h"
#include "common.h"
#include "list.h"
#include "obis.h"
/* meter types */
#include "file.h"
#include "exec.h"
#include "random.h"
#include "s0.h"
#include "d0.h"
#include "onewire.h"
#ifdef SML_SUPPORT
#include "sml.h"
#endif /* SML_SUPPORT */
typedef union reading_id {
obis_id_t obis;
/* char *string; */
/* char *uuid; */
} reading_id_t;
typedef struct reading {
float value;
struct timeval time;
union reading_id identifier;
reading_id_t identifier;
struct reading *next; /* pointer for linked list */
} reading_t;
typedef struct {
enum {
RANDOM,
S0,
D0,
ONEWIRE,
SML
} id;
const char *name; /* short identifier for protocol */
const char *desc; /* more detailed description */
size_t max_readings; /* how many readings can be read with 1 call */
int periodic:1; /* does this meter has be triggered periodically? */
} meter_type_t;
typedef enum {
meter_protocol_file = 1,
meter_protocol_exec,
meter_protocol_random,
meter_protocol_s0,
meter_protocol_d0,
meter_protocol_sml
} meter_protocol_t;
typedef struct meter {
char id[5]; /* only for internal usage & debugging */
char *connection; /* args for connection, further configuration etc */
const meter_type_t *type;
char id[5];
meter_protocol_t protocol;
int interval;
union {
meter_handle_file_t file;
meter_handle_exec_t exec;
meter_handle_random_t random;
meter_handle_s0_t s0;
meter_handle_d0_t d0;
meter_handle_onewire_t onewire;
meter_handle_random_t random;
#ifdef SML_SUPPORT
meter_handle_sml_t sml;
#endif /* SML_SUPPORT */
} handle;
} meter_t;
/* Prototypes */
typedef struct {
meter_protocol_t id;
char *name; /* short identifier for protocol */
char *desc; /* more detailed description */
size_t max_readings; /* how many readings can be read with 1 call */
int periodic:1; /* does this meter has be triggered periodically? */
/* function pointers */
int (*init_func)(meter_t *mtr, list_t options);
int (*open_func)(meter_t *mtr);
int (*close_func)(meter_t *mtr);
size_t (*read_func)(meter_t *mtr, reading_t *rds, size_t n);
} meter_details_t;
/* prototypes */
/**
* Converts timeval structure to double
@ -103,14 +116,23 @@ typedef struct meter {
*/
double tvtod(struct timeval tv);
/**
* Get list of available meter types
*/
const meter_details_t * meter_get_protocols();
const meter_details_t * meter_get_details(meter_protocol_t protocol);
int meter_lookup_protocol(const char *name, meter_protocol_t *protocol);
/**
* Initialize meter
*
* @param mtr the meter structure to initialze
* @param type the type it should be initialized with
* @param connection type specific initialization arguments (connection settings, port, etc..)
* @param list of key, value pairs of options
* @return 0 on success, -1 on error
*/
void meter_init(meter_t *mtr, const meter_type_t *type, const char *connection);
int meter_init(meter_t *mtr, list_t options);
/**
* Freeing all memory which has been allocated during the initialization
@ -137,6 +159,7 @@ size_t meter_read(meter_t *mtr, reading_t rds[], size_t n);
* Establish connection, initialize meter etc.
*
* @param mtr the meter structure
* @return 0 on success, -1 on error
*/
int meter_open(meter_t *mtr);
@ -146,7 +169,8 @@ int meter_open(meter_t *mtr);
* Reset ports, shutdown meter etc.
*
* @param mtr the meter structure
* @return 0 on success, -1 on error
*/
void meter_close(meter_t *mtr);
int meter_close(meter_t *mtr);
#endif /* _METER_H_ */

View file

@ -28,64 +28,9 @@
#include <string.h>
typedef enum {
ABSTRACT = 0,
ELECTRIC = 1,
HEAT_COST = 4,
COOLING = 5,
HEATING = 6,
GAS = 7,
WATER_COLD = 8,
WATER_HOT = 9
} obis_media_t;
#define OBIS_STR_LEN (6*3+5+1)
typedef union {
enum {
GENERAL_PURPOSE = 0,
ACTIVE_POWER_IN = 1,
ACTIVE_POWER_OUT = 2,
REACTIVE_POWER_IN = 3,
REACTIVE_POWER_OUT = 4,
REACTIVE_POWER_Q1 = 5,
REACTIVE_POWER_Q2 = 6,
REACTIVE_POWER_Q3 = 7,
REACTIVE_POWER_Q4 = 8,
APPARENT_POWER_IN = 9,
APPARENT_POWER_OUT = 10,
CURRENT_ANY = 11,
VOLTAGE_ANY = 12,
POWER_FACTOR_OUT = 13,
SUPPLY_FREQUENCY = 14,
ACTIVE_POWER_Q1 = 17,
ACTIVE_POWER_Q2 = 18,
ACTIVE_POWER_Q3 = 19,
ACTIVE_POWER_Q4 = 20,
L1_ACTIVE_POWER_INT = 21,
L1_ACTIVE_POWER_OUT = 22,
ANGLES = 81,
UNITLESS = 82,
LOSS = 83,
L1_POWER_FACTOR_OUT = 85,
L2_POWER_FACTOR_OUT = 86,
L3_POWER_FACTOR_OUT = 87,
AMPERE_SQUARE_HOURS = 88,
VOLT_SQUARE_HOURS = 89,
SERVICE = 96,
ERROR = 97,
LIST = 98,
DATA_PROFILE = 99
} electric;
int gas; /* as defined in DIN EN 13757-1 */
} obis_indicator_t;
/* regex: A-BB:CC.DD.EE(*FF)? */
/* regex: A-BB:CC.DD.EE([*&]FF)? */
typedef union {
unsigned char raw[6];
struct {
@ -100,12 +45,15 @@ typedef struct {
char *desc;
} obis_alias_t;
/* Prototypes */
obis_id_t * obis_init(obis_id_t *id, const unsigned char *raw);
obis_id_t * obis_lookup_alias(const char *alias);
int obis_parse(obis_id_t *id, const char *str, size_t n);
/* prototypes */
obis_id_t * obis_init(obis_id_t *id, unsigned char *raw);
const obis_alias_t * obis_get_aliases();
int obis_parse(const char *str, obis_id_t *id);
int obis_lookup_alias(const char *alias, obis_id_t *id);
int obis_unparse(obis_id_t id, char *buffer, size_t n);
int obis_compare(obis_id_t a, obis_id_t b);
int obis_is_manufacturer_specific(obis_id_t id);
int obis_is_null(obis_id_t id);

44
include/options.h Normal file
View file

@ -0,0 +1,44 @@
#ifndef _OPTIONS_H_
#define _OPTIONS_H_
#include "list.h"
typedef union {
const char *string;
int integer;
double floating;
int boolean:1;
} option_value_t;
/* subset of json_type's */
typedef enum {
option_type_boolean = 1,
option_type_double,
option_type_int,
option_type_string = 6
} option_type_t;
typedef struct {
char *key;
option_type_t type;
option_value_t value;
} option_t;
/**
* Lookup option by key in a list of options
*
* @param list_t the list of options
* @param char *key the key you are looking for
* @return int success or error (CFG_* constants)
*/
int options_lookup(list_t options, char *key, void *value, option_type_t type);
/**
* Type specific wrapper functions for config_lookup_type()
*/
int options_lookup_string(list_t options, char *key, char **value);
int options_lookup_int(list_t options, char *key, int *value);
int options_lookup_double(list_t options, char *key, double *value);
int options_lookup_boolean(list_t options, char *key, int *value);
#endif /* _OPTIONS_H_ */

View file

@ -27,15 +27,19 @@
#define _RANDOM_H_
typedef struct {
double min, max, last;
double min, max;
double last;
} meter_handle_random_t;
struct meter; /* forward declaration */
struct reading; /* forward declaration */
double ltqnorm(double p); /* forward declaration */
/* forward declarations */
struct meter;
struct reading;
double ltqnorm(double p);
int meter_init_random(struct meter *mtr, list_t options);
int meter_open_random(struct meter *mtr);
void meter_close_random(struct meter *mtr);
int meter_close_random(struct meter *mtr);
size_t meter_read_random(struct meter *mtr, struct reading *rds, size_t n);
#endif /* _RANDOM_H_ */

View file

@ -30,15 +30,19 @@
#include <signal.h>
typedef struct {
char *device;
int fd; /* file descriptor of port */
struct termios oldtio; /* required to reset port */
} meter_handle_s0_t;
struct meter; /* forward declaration */
struct reading; /* forward declaration */
/* forward declarations */
struct meter;
struct reading;
int meter_init_s0(struct meter *mtr, list_t options);
int meter_open_s0(struct meter *mtr);
void meter_close_s0(struct meter *mtr);
int meter_close_s0(struct meter *mtr);
size_t meter_read_s0(struct meter *mtr, struct reading *rds, size_t n);
#endif /* _S0_H_ */

View file

@ -38,13 +38,17 @@
#include "obis.h"
typedef struct {
char *host;
char *device;
int baudrate;
int fd;
//float counter; /* Zählerstand */
//termios old_tio;
} meter_handle_sml_t;
struct meter; /* forward declaration */
struct reading; /* forward declaration */
/* forward declarations */
struct meter;
struct reading;
/**
* Cast arbitrary sized sml_value to double
@ -54,11 +58,20 @@ struct reading; /* forward declaration */
*/
double sml_value_todouble(sml_value *value);
/**
* Initialize meter structure with a list of options
*
* @param mtr the meter structure
* @param options a list of options
* @return 0 on success, <0 on error
*/
int meter_init_sml(struct meter *mtr, list_t options);
/**
* Open connection via serial port or socket to meter
*
* @param mtr the meter structure
* @return 0 on success, -1 on error
* @return 0 on success, <0 on error
*/
int meter_open_sml(struct meter *mtr);
@ -67,7 +80,7 @@ int meter_open_sml(struct meter *mtr);
*
* @param mtr the meter structure
*/
void meter_close_sml(struct meter *mtr);
int meter_close_sml(struct meter *mtr);
/**
* Blocking read on meter
@ -94,7 +107,7 @@ void meter_sml_parse(sml_list *list, struct reading *rd);
* Open serial port by device
*
* @param device the device path, usually /dev/ttyS*
* @return file descriptor, -1 on error
* @return file descriptor, <0 on error
*/
int meter_sml_open_port(const char *device);
@ -103,7 +116,7 @@ int meter_sml_open_port(const char *device);
*
* @param node the hostname or ASCII encoded IP address
* @param the ASCII encoded portnum or service as in /etc/services
* @return file descriptor, -1 on error
* @return file descriptor, <0 on error
*/
int meter_sml_open_socket(const char *node, const char *service);

View file

@ -4,14 +4,14 @@ AM_LDFLAGS =
VERSION = 1:0:0
lib_LIBRARIES = libmeter.a
lib_LIBRARIES = libmtr.a
libmeter_a_SOURCES = meter.c d0.c s0.c random.c onewire.c ltqnorm.c obis.c
libmeter_a_LDFLAGS = -version-info $(VERSION)
libmtr_a_SOURCES = meter.c d0.c s0.c random.c file.c exec.c ltqnorm.c obis.c options.c
libmtr_a_LDFLAGS = -version-info $(VERSION)
# SML support
####################################################################
if SML_SUPPORT
libmeter_a_SOURCES += sml.c
libmtr_a_SOURCES += sml.c
AM_CFLAGS += $(DEPS_SML_CFLAGS)
endif

View file

@ -72,15 +72,16 @@ am__installdirs = "$(DESTDIR)$(libdir)"
LIBRARIES = $(lib_LIBRARIES)
AR = ar
ARFLAGS = cru
libmeter_a_AR = $(AR) $(ARFLAGS)
libmeter_a_LIBADD =
am__libmeter_a_SOURCES_DIST = meter.c d0.c s0.c random.c onewire.c \
ltqnorm.c obis.c sml.c
libmtr_a_AR = $(AR) $(ARFLAGS)
libmtr_a_LIBADD =
am__libmtr_a_SOURCES_DIST = meter.c d0.c s0.c random.c file.c exec.c \
ltqnorm.c obis.c options.c sml.c
@SML_SUPPORT_TRUE@am__objects_1 = sml.$(OBJEXT)
am_libmeter_a_OBJECTS = meter.$(OBJEXT) d0.$(OBJEXT) s0.$(OBJEXT) \
random.$(OBJEXT) onewire.$(OBJEXT) ltqnorm.$(OBJEXT) \
obis.$(OBJEXT) $(am__objects_1)
libmeter_a_OBJECTS = $(am_libmeter_a_OBJECTS)
am_libmtr_a_OBJECTS = meter.$(OBJEXT) d0.$(OBJEXT) s0.$(OBJEXT) \
random.$(OBJEXT) file.$(OBJEXT) exec.$(OBJEXT) \
ltqnorm.$(OBJEXT) obis.$(OBJEXT) options.$(OBJEXT) \
$(am__objects_1)
libmtr_a_OBJECTS = $(am_libmtr_a_OBJECTS)
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
@ -89,8 +90,8 @@ COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
SOURCES = $(libmeter_a_SOURCES)
DIST_SOURCES = $(am__libmeter_a_SOURCES_DIST)
SOURCES = $(libmtr_a_SOURCES)
DIST_SOURCES = $(am__libmtr_a_SOURCES_DIST)
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@ -193,10 +194,10 @@ top_srcdir = @top_srcdir@
AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(am__append_2)
AM_CPPFLAGS = -I$(top_srcdir)/include
AM_LDFLAGS =
lib_LIBRARIES = libmeter.a
libmeter_a_SOURCES = meter.c d0.c s0.c random.c onewire.c ltqnorm.c \
obis.c $(am__append_1)
libmeter_a_LDFLAGS = -version-info $(VERSION)
lib_LIBRARIES = libmtr.a
libmtr_a_SOURCES = meter.c d0.c s0.c random.c file.c exec.c ltqnorm.c \
obis.c options.c $(am__append_1)
libmtr_a_LDFLAGS = -version-info $(VERSION)
all: all-am
.SUFFIXES:
@ -263,10 +264,10 @@ uninstall-libLIBRARIES:
clean-libLIBRARIES:
-test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES)
libmeter.a: $(libmeter_a_OBJECTS) $(libmeter_a_DEPENDENCIES)
-rm -f libmeter.a
$(libmeter_a_AR) libmeter.a $(libmeter_a_OBJECTS) $(libmeter_a_LIBADD)
$(RANLIB) libmeter.a
libmtr.a: $(libmtr_a_OBJECTS) $(libmtr_a_DEPENDENCIES)
-rm -f libmtr.a
$(libmtr_a_AR) libmtr.a $(libmtr_a_OBJECTS) $(libmtr_a_LIBADD)
$(RANLIB) libmtr.a
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@ -275,10 +276,12 @@ distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d0.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exec.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ltqnorm.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/meter.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/obis.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onewire.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/random.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/s0.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sml.Po@am__quote@

0
src/common.c Normal file
View file

View file

@ -35,6 +35,7 @@
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/time.h>
/* socket */
#include <errno.h>
@ -42,8 +43,9 @@
#include <sys/socket.h>
#include "meter.h"
#include "obis.h"
#include "d0.h"
#include "obis.h"
#include "options.h"
int meter_d0_open_socket(const char *node, const char *service) {
struct sockaddr_in sin;
@ -52,8 +54,8 @@ int meter_d0_open_socket(const char *node, const char *service) {
fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0) {
fprintf(stderr, "error: socket(): %s\n", strerror(errno));
return -1;
print(log_error, "socket(): %s", NULL, strerror(errno));
return ERR;
}
getaddrinfo(node, service, NULL, &ais);
@ -62,36 +64,56 @@ int meter_d0_open_socket(const char *node, const char *service) {
res = connect(fd, (struct sockaddr *) &sin, sizeof(sin));
if (res < 0) {
fprintf(stderr, "error: connect(%s, %s): %s\n", node, service, strerror(errno));
return -1;
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);
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;
}
return SUCCESS;
}
int meter_open_d0(meter_t *mtr) {
meter_handle_d0_t *handle = &mtr->handle.d0;
char *addr = strdup(mtr->connection);
char *node = strsep(&addr, ":");
char *service = strsep(&addr, ":");
if (handle->device != NULL) {
print(log_error, "TODO: implement serial interface", mtr);
return ERR;
}
else if (handle->host != NULL) {
char *addr = strdup(handle->host);
char *node = strsep(&addr, ":");
char *service = strsep(&addr, ":");
printf("socket: %s %s\n", node, service);
handle->fd = meter_d0_open_socket(node, service);
}
handle->fd = meter_d0_open_socket(node, service);
free(addr);
printf("socket opened: %s %s\n", node, service);
return (handle->fd < 0) ? -1 : 0;
return (handle->fd < 0) ? ERR : SUCCESS;
}
void meter_close_d0(meter_t *mtr) {
int meter_close_d0(meter_t *mtr) {
meter_handle_d0_t *handle = &mtr->handle.d0;
close(handle->fd);
return close(handle->fd);
}
size_t meter_read_d0(meter_t *mtr, reading_t rds[], size_t n) {
@ -108,7 +130,7 @@ size_t meter_read_d0(meter_t *mtr, reading_t rds[], size_t n) {
char baudrate; /* 1 byte */
char byte;
int j, k, m;
j = k = m = baudrate = 0;
context = START;
@ -122,7 +144,6 @@ size_t meter_read_d0(meter_t *mtr, reading_t rds[], size_t n) {
if (byte == '/') {
j = k = m = 0;
context = VENDOR;
printf("reset!!!\n");
}
break;
@ -145,7 +166,7 @@ size_t meter_read_d0(meter_t *mtr, reading_t rds[], size_t n) {
break;
case IDENT:
/* Data block starts after twice a '\r\n' sequence */
/* data block starts after twice a '\r\n' sequence */
/* b= CR LF CR LF */
/* k= 1 2 3 4 */
if (byte == '\r' || byte == '\n') {
@ -202,9 +223,9 @@ size_t meter_read_d0(meter_t *mtr, reading_t rds[], size_t n) {
k++;
if (k >= 2) {
if (m < n) { /* free slots available? */
printf("parsed reading (id=%s, value=%s, unit=%s)\n", id, value, unit);
//printf("parsed reading (id=%s, value=%s, unit=%s)\n", id, value, unit);
rds[m].value = strtof(value, NULL);
obis_parse(&rds[m].identifier.obis, id, strlen(id));
obis_parse(id, &rds[m].identifier.obis);
gettimeofday(&rds[m].time, NULL);
j = k = 0;
@ -217,13 +238,13 @@ size_t meter_read_d0(meter_t *mtr, reading_t rds[], size_t n) {
break;
case END:
printf("read package with %i tuples (vendor=%s, baudrate=%c, ident=%s)\n", m, vendor, baudrate, identification);
print(log_info, "Read package with %i tuples (vendor=%s, baudrate=%c, ident=%s)", mtr, m, vendor, baudrate, identification);
return m;
}
}
error:
printf("something unexpected happened: %s:%i!\n", __FUNCTION__, __LINE__);
print(log_error, "Something unexpected happened: %s:%i!", mtr, __FUNCTION__, __LINE__);
return 0;
}

69
src/exec.c Normal file
View file

@ -0,0 +1,69 @@
/**
* Get data by calling programs
*
* @package vzlogger
* @copyright Copyright (c) 2011, The volkszaehler.org project
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <info@steffenvogel.de>
*/
/*
* This file is part of volkzaehler.org
*
* volkzaehler.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* volkzaehler.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <sys/time.h>
#include "meter.h"
#include "exec.h"
#include "options.h"
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) {
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;
}
return SUCCESS;
}
int meter_open_exec(meter_t *mtr) {
//meter_handle_exec_t *handle = &mtr->handle.exec;
// TODO implement
return ERR;
}
int meter_close_exec(meter_t *mtr) {
//meter_handle_exec_t *handle = &mtr->handle.exec;
// TODO implement
return ERR;
}
size_t meter_read_exec(meter_t *mtr, reading_t rds[], size_t n) {
//meter_handle_exec_t *handle = &mtr->handle.exec;
// TODO implement
return 0;
}

95
src/file.c Normal file
View file

@ -0,0 +1,95 @@
/**
* Read data from files & fifos
*
* @package vzlogger
* @copyright Copyright (c) 2011, The volkszaehler.org project
* @license http://www.gnu.org/licenses/gpl.txt GNU Public License
* @author Steffen Vogel <info@steffenvogel.de>
*/
/*
* This file is part of volkzaehler.org
*
* volkzaehler.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* volkzaehler.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <sys/time.h>
#include <errno.h>
#include "meter.h"
#include "file.h"
#include "options.h"
int meter_init_file(meter_t *mtr, list_t options) {
meter_handle_file_t *handle = &mtr->handle.file;
char *path;
if (options_lookup_string(options, "path", &path) == SUCCESS) {
handle->path = strdup(path);
}
else {
print(log_error, "Missing path or invalid type", mtr);
return ERR;
}
char *regex;
if (options_lookup_string(options, "regex", &regex) == SUCCESS) {
handle->regex = strdup(regex);
}
else if (options_lookup_string(options, "regex", &regex) == ERR_INVALID_TYPE) { // TODO improve code
print(log_error, "Regex has to be a string", mtr);
return ERR;
}
return SUCCESS;
}
int meter_open_file(meter_t *mtr) {
meter_handle_file_t *handle = &mtr->handle.file;
handle->fd = fopen(handle->path, "r");
if (handle->fd == NULL) {
print(log_error, "fopen(%s): %s", mtr, handle->path, strerror(errno));
return ERR;
}
return SUCCESS;
}
int meter_close_file(meter_t *mtr) {
meter_handle_file_t *handle = &mtr->handle.file;
return fclose(handle->fd);
}
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;
rewind(handle->fd);
bytes = fread(buffer, 1, 16, handle->fd);
buffer[bytes] = '\0'; /* zero terminated, required? */
if (bytes) {
rds->value = strtof(buffer, NULL);
gettimeofday(&rds->time, NULL);
return 1;
}
return 0; /* empty file */
}

View file

@ -19,7 +19,7 @@
#include <math.h>
#include <errno.h>
/* Coefficients in rational approximations. */
/* coefficients in rational approximations. */
static const double a[] = {
-3.969683028665376e+01,
2.209460984245205e+02,
@ -74,17 +74,17 @@ double ltqnorm(double p) {
errno = ERANGE;
return HUGE_VAL /* "infinity" */;
}
else if (p < LOW) { /* Rational approximation for lower region */
else if (p < LOW) { /* rational approximation for lower region */
q = sqrt(-2*log(p));
return (((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) /
((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1);
}
else if (p > HIGH) { /* Rational approximation for upper region */
else if (p > HIGH) { /* rational approximation for upper region */
q = sqrt(-2*log(1-p));
return -(((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) /
((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1);
}
else { /* Rational approximation for central region */
else { /* rational approximation for central region */
q = p - 0.5;
r = q*q;
return (((((a[0]*r+a[1])*r+a[2])*r+a[3])*r+a[4])*r+a[5])*q /

View file

@ -25,79 +25,93 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "../bin/logger/include/list.h"
#include <stdio.h>
#include "meter.h"
#include "options.h"
/* List of available meter types */
const meter_type_t meter_types[] = {
{ONEWIRE, "onewire", "Dallas 1-Wire sensors (via OWFS)", 1, 1},
{RANDOM, "random", "Random walk", 1, 1},
{S0, "s0", "S0 on RS232", 1, 1},
{D0, "d0", "On-site plaintext protocol (DIN EN 62056-21)", 16, 50},
#define METER_DETAIL(NAME, DESC, MAX_RDS, PERIODIC) { meter_protocol_##NAME, #NAME, DESC, MAX_RDS, PERIODIC, meter_init_##NAME, meter_open_##NAME, meter_close_##NAME, meter_read_##NAME }
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(random, "Random walk", 1, TRUE),
METER_DETAIL(s0, "S0 on RS232", 1, TRUE),
METER_DETAIL(d0, "Plaintext protocol (DIN EN 62056-21)", 32, FALSE),
#ifdef SML_SUPPORT
{SML, "sml", "Smart Meter Language", 16, 0},
METER_DETAIL(sml, "Smart Meter Language", 32, FALSE),
#endif /* SML_SUPPORT */
{} /* stop condition for iterator */
{} /* stop condition for iterator */
};
int meter_init(meter_t *mtr, list_t options) {
static int instances; /* static to generate unique channel ids */
snprintf(mtr->id, 5, "mtr%i", instances++); /* set/increment id */
/* protocol */
char *protocol_str;
if (options_lookup_string(options, "protocol", &protocol_str) != SUCCESS) {
print(log_error, "Missing protocol or invalid type", mtr);
return ERR;
}
if (meter_lookup_protocol(protocol_str, &mtr->protocol) != SUCCESS) {
print(log_error, "Invalid protocol: %s", mtr, protocol_str);
return ERR; /* skipping this meter */
}
/* interval */
mtr->interval = 10;
if (options_lookup_int(options, "interval", &mtr->interval) == ERR_INVALID_TYPE) {
print(log_error, "Invalid type for interval", mtr);
return ERR;
}
const meter_details_t *details = meter_get_details(mtr->protocol);
return details->init_func(mtr, options);
}
int meter_open(meter_t *mtr) {
const meter_details_t *details = meter_get_details(mtr->protocol);
return details->open_func(mtr);
}
int meter_close(meter_t *mtr) {
const meter_details_t *details = meter_get_details(mtr->protocol);
return details->close_func(mtr);
}
size_t meter_read(meter_t *mtr, reading_t rds[], size_t n) {
const meter_details_t *details = meter_get_details(mtr->protocol);
return details->read_func(mtr, rds, n);
}
const meter_details_t * meter_get_protocols() {
return protocols;
}
int meter_lookup_protocol(const char *name, meter_protocol_t *protocol) {
for (const meter_details_t *it = protocols; it != NULL; it++) {
if (strcmp(it->name, name) == 0) {
*protocol = it->id;
return SUCCESS;
}
}
return ERR_NOT_FOUND;
}
const meter_details_t * meter_get_details(meter_protocol_t protocol) {
for (const meter_details_t *it = protocols; it != NULL; it++) {
if (it->id == protocol) {
return it;
}
}
return NULL;
}
double tvtod(struct timeval tv) {
return tv.tv_sec + tv.tv_usec / 1e6;
}
void meter_init(meter_t *mtr, const meter_type_t *type, const char *connection) {
static int instances; /* static to generate channel ids */
snprintf(mtr->id, 5, "mtr%i", instances++);
mtr->type = type;
mtr->connection = strdup(connection);
}
void meter_free(meter_t *mtr) {
free(mtr->connection);
}
int meter_open(meter_t *mtr) {
switch (mtr->type->id) {
case RANDOM: return meter_open_random(mtr);
case S0: return meter_open_s0(mtr);
case D0: return meter_open_d0(mtr);
case ONEWIRE: return meter_open_onewire(mtr);
#ifdef SML_SUPPORT
case SML: return meter_open_sml(mtr);
#endif /* SML_SUPPORT */
default: fprintf(stderr, "error: unknown meter type: %i\n", mtr->type->id);
}
return -1;
}
void meter_close(meter_t *mtr) {
switch (mtr->type->id) {
case RANDOM: meter_close_random(mtr); break;
case S0: meter_close_s0(mtr); break;
case D0: meter_close_d0(mtr); break;
case ONEWIRE: meter_close_onewire(mtr); break;
#ifdef SML_SUPPORT
case SML: meter_close_sml(mtr); break;
#endif /* SML_SUPPORT */
default: fprintf(stderr, "error: unknown meter type: %i\n", mtr->type->id);
}
}
size_t meter_read(meter_t *mtr, reading_t rds[], size_t n) {
switch (mtr->type->id) {
case RANDOM: return meter_read_random(mtr, rds, n);
case S0: return meter_read_s0(mtr, rds, n);
case D0: return meter_read_d0(mtr, rds, n);
case ONEWIRE: return meter_read_onewire(mtr, rds, n);
#ifdef SML_SUPPORT
case SML: return meter_read_sml(mtr, rds, n);
#endif /* SML_SUPPORT */
default: fprintf(stderr, "error: unknown meter type: %i\n", mtr->type->id);
}
return 0;
}

View file

@ -29,67 +29,69 @@
#include <ctype.h>
#include "obis.h"
#include "common.h"
#define DC 0xff // wildcard, dont care
obis_alias_t obis_aliases[] = {
/**
* 255 is considered as wildcard!
*
* A B C D E F alias description
* ===================================================================================*/
static const obis_alias_t aliases[] = {
/* A B C D E F alias description
====================================================================*/
/* General */
{{{ 1, 0, 12, 7, DC, DC}}, "voltage", ""},
{{{ 1, 0, 32, 7, DC, DC}}, "voltage-l1", ""},
{{{ 1, 0, 52, 7, DC, DC}}, "voltage-l2", ""},
{{{ 1, 0, 72, 7, DC, DC}}, "voltage-l3", ""},
/* general */
{{{ 1, 0, 1, 7, DC, DC}}, "power", "Wirkleistung (Summe)"},
{{{ 1, 0, 21, 7, DC, DC}}, "power-l1", "Wirkleistung (Phase 1)"},
{{{ 1, 0, 41, 7, DC, DC}}, "power-l2", "Wirkleistung (Phase 2)"},
{{{ 1, 0, 61, 7, DC, DC}}, "power-l3", "Wirkleistung (Phase 3)"},
{{{ 1, 0, 11, 7, DC, DC}}, "current", ""},
{{{ 1, 0, 31, 7, DC, DC}}, "current-l1", ""},
{{{ 1, 0, 51, 7, DC, DC}}, "current-l2", ""},
{{{ 1, 0, 71, 7, DC, DC}}, "current-l3", ""},
{{{ 1, 0, 12, 7, DC, DC}}, "voltage", "Spannung (Mittelwert)"},
{{{ 1, 0, 32, 7, DC, DC}}, "voltage-l1", "Spannung (Phase 1)"},
{{{ 1, 0, 52, 7, DC, DC}}, "voltage-l2", "Spannung (Phase 2)"},
{{{ 1, 0, 72, 7, DC, DC}}, "voltage-l3", "Spannung (Phase 3)"},
{{{ 1, 0, 14, 7, 0, DC}}, "frequency", ""},
{{{ 1, 0, 12, 7, 0, DC}}, "powerfactor", ""},
{{{ 1, 0, 11, 7, DC, DC}}, "current", "Stromstaerke (Summe)"},
{{{ 1, 0, 31, 7, DC, DC}}, "current-l1", "Stromstaerke (Phase 1)"},
{{{ 1, 0, 51, 7, DC, DC}}, "current-l2", "Stromstaerke (Phase 2)"},
{{{ 1, 0, 71, 7, DC, DC}}, "current-l3", "Stromstaerke (Phase 3)"},
{{{ 1, 0, 1, 7, DC, DC}}, "power", "Active Power Instantaneous value Total"},
{{{ 1, 0, 21, 7, DC, DC}}, "power-l1", "L1 Active Power Instantaneous value Total"},
{{{ 1, 0, 41, 7, DC, DC}}, "power-l2", "L1 Active Power Instantaneous value Total"},
{{{ 1, 0, 61, 7, DC, DC}}, "power-l3", "L3 Active Power Instantaneous value Total"},
{{{ 1, 0, 14, 7, 0, DC}}, "frequency", "Netzfrequenz"},
{{{ 1, 0, 12, 7, 0, DC}}, "powerfactor", "Leistungsfaktor"},
{{{ 0, 0, 96, 1, DC, DC}}, "device", "Complete device ID"},
{{{ 1, 0, 96, 5, 5, DC}}, "status", "Meter status flag"},
{{{ 0, 0, 96, 1, DC, DC}}, "device", "Zaehler Seriennr."},
{{{ 1, 0, 96, 5, 5, DC}}, "status", "Zaehler Status"},
{{{ 1, 0, 1, 8, DC, DC}}, "counter", "Active Power Counter Total"},
{{{ 1, 0, 2, 8, DC, DC}}, "counter-out", "Zählerstand Lieferg."},
{{{ 1, 0, 1, 8, DC, DC}}, "counter", "Zaehlerstand Wirkleistung"},
{{{ 1, 0, 2, 8, DC, DC}}, "counter-out", "Zaehlerstand Lieferg."},
/* Easymeter */
/* ESYQ3B (Easymeter Q3B) */
{{{129, 129, 199, 130, 3, DC}}, "esy-?", ""}, // ???
{{{ 1, 0, 1, 8, 1, DC}}, "esy-counter-t1", "Active Power Counter Tariff 1"},
{{{ 1, 0, 1, 8, 2, DC}}, "esy-counter-t2", "Active Power Counter Tariff 2"},
//{{{129, 129, 199, 130, 3, DC}}, "", ""}, // ???
/* ESYQ3D (Easymeter Q3D) */
{{{ 0, 0, 0, 0, 0, DC}}, "esy-?", ""}, // ???
//{{{ 0, 0, 0, 0, 0, DC}}, "", ""}, // ???
/* HAG eHZ010C_EHZ1WA02 (Hager eHz) */
{{{ 1, 0, 0, 0, 0, DC}}, "hag-id", "Eigentumsnr."},
{{{ 1, 0, 96, 50, 0, 0}}, "hag-status", "Netzstatus bitcodiert: Drehfeld, Anlaufschwelle, Energierichtung"},
{{{ 1, 0, 96, 50, 0, 1}}, "hag-frequency", "Netz-Periode, hexadezimal (Einheit 1/100 ms)"},
{{{ 1, 0, 96, 50, 0, 2}}, "hag-temp", "aktuelle Chiptemperatur, hexadezimal, Einheit °C"},
{{{ 1, 0, 96, 50, 0, 0}}, "hag-status", "Netz Status"}, /* bitcodiert: Drehfeld, Anlaufschwelle, Energierichtung */
{{{ 1, 0, 96, 50, 0, 1}}, "hag-frequency", "Netz Periode"}, /* hexadezimal (Einheit 1/100 ms) */
{{{ 1, 0, 96, 50, 0, 2}}, "hag-temp", "aktuelle Chiptemperatur"}, /* hexadezimal, Einheit °C */
{{{ 1, 0, 96, 50, 0, 3}}, "hag-temp-min", "minimale Chiptemperatur"},
{{{ 1, 0, 96, 50, 0, 4}}, "hag-temp-avg", "gemittelte Chiptemperatur"},
{{{ 1, 0, 96, 50, 0, 5}}, "hag-temp-max", "maximale Chiptemperatur"},
{{{ 1, 0, 96, 50, 0, 6}}, "hag-check", "Kontrollnummer"},
{{{ 1, 0, 96, 50, 0, 6}}, "hag-check", "Kontrollnr."},
{{{ 1, 0, 96, 50, 0, 7}}, "hag-diag", "Diagnose"},
{} /* stop condition for iterator */
};
obis_id_t * obis_init(obis_id_t *id, const unsigned char *raw) {
const obis_alias_t * obis_get_aliases() {
return aliases;
}
obis_id_t * obis_init(obis_id_t *id, unsigned char *raw) {
if (raw == NULL) {
memset(id->raw, 0, 6); /* initialize with zeros */
}
@ -100,16 +102,15 @@ obis_id_t * obis_init(obis_id_t *id, const unsigned char *raw) {
return id;
}
int obis_parse(obis_id_t *id, const char *str, size_t n) {
int obis_parse(const char *str, obis_id_t *id) {
enum { A = 0, B, C, D, E, F };
char b;
int num;
int field;
char b = 0;
int num = 0;
int field = -1;
size_t n = strlen(str);
num = b = 0;
field = -1;
memset(&id->raw, 0xff, 6); /* initialize as wildcard */
memset(&id->raw, DC, 6); /* initialize as wildcard */
/* format: "A-B:C.D.E[*&]F" */
/* fields A, B, E, F are optional */
@ -133,7 +134,9 @@ int obis_parse(obis_id_t *id, const char *str, size_t n) {
else if ((b == '*' || b == '&') && field == D) { /* end of field E, start of field F */
field = E;
}
else goto error; // TODO lookup aliases
else {
return ERR;
}
id->raw[field] = num;
num = 0;
@ -144,25 +147,17 @@ int obis_parse(obis_id_t *id, const char *str, size_t n) {
id->raw[++field] = num;
/* fields C & D are mandatory */
if (field < D) goto error;
return 0;
error:
printf("something unexpected happened (field=%i, b=%c, num=%i): %s:%i!\n", field, b, num, __FUNCTION__, __LINE__);
return -1;
return (field < D) ? ERR : SUCCESS;
}
obis_id_t * obis_lookup_alias(const char *alias) {
obis_alias_t *it = obis_aliases;
do { /* linear search */
int obis_lookup_alias(const char *alias, obis_id_t *id) {
for (const obis_alias_t *it = aliases; it != NULL; it++) {
if (strcmp(it->name, alias) == 0) {
return &it->id;
*id = it->id;
return SUCCESS;
}
} while ((++it)->name);
return NULL;
}
return ERR_NOT_FOUND;
}
int obis_unparse(obis_id_t id, char *buffer, size_t n) {

39
src/options.c Normal file
View file

@ -0,0 +1,39 @@
#include <string.h>
#include "options.h"
#include "common.h"
int options_lookup(list_t options, char *key, void *value, option_type_t type) {
option_t * option = NULL;
/* linear search */
foreach(options, val, option_t) {
if (strcmp(val->key, key) == 0) {
option = val;
}
}
/* checks */
if (option == NULL) {
return ERR_NOT_FOUND;
}
else if (option->type == type) {
size_t n = 0;
switch (option->type) {
case option_type_boolean: n = sizeof(option->value.integer); break; /* boolean is a bitfield: int boolean:1; */
case option_type_double: n = sizeof(option->value.floating); break;
case option_type_int: n = sizeof(option->value.integer); break;
case option_type_string: n = sizeof(option->value.string); break;
}
memcpy(value, &option->value, n);
return SUCCESS;
}
else {
return ERR_INVALID_TYPE;
}
}
int options_lookup_boolean(list_t o, char *k, int *v) { return options_lookup(o, k, v, option_type_boolean); }
int options_lookup_int(list_t o, char *k, int *v) { return options_lookup(o, k, v, option_type_int); }
int options_lookup_string(list_t o, char *k, char **v) { return options_lookup(o, k, v, option_type_string); }
int options_lookup_double(list_t o, char *k, double *v) { return options_lookup(o, k, v, option_type_double); }

View file

@ -29,39 +29,55 @@
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include "meter.h"
#include "random.h"
#include "options.h"
int meter_init_random(meter_t *mtr, list_t options) {
meter_handle_random_t *handle = &mtr->handle.random;
handle->min = 0;
handle->max = 40;
handle->last = (handle->max + handle->min) / 2; /* start in the middle */
if (options_lookup_double(options, "min", &handle->min) == ERR_INVALID_TYPE) {
print(log_error, "Min value has to be a floating point number (e.g. '40.0')", mtr);
return ERR;
}
if (options_lookup_double(options, "max", &handle->max) == ERR_INVALID_TYPE) {
print(log_error, "Max value has to be a floating point number (e.g. '40.0')", mtr);
return ERR;
}
return SUCCESS;
}
int meter_open_random(meter_t *mtr) {
meter_handle_random_t *handle = &mtr->handle.random;
//meter_handle_random_t *handle = &mtr->handle.random;
// TODO rewrite to use /dev/u?random
srand(time(NULL)); /* initialize PNRG */
handle->min = 0; // TODO parse from options
handle->max = strtof(mtr->connection, NULL);
handle->last = handle->max * ((float) rand() / RAND_MAX); /* start value */
return 0; /* always succeeds */
return SUCCESS; /* can't fail */
}
void meter_close_random(meter_t *mtr) {
int meter_close_random(meter_t *mtr) {
//meter_handle_random_t *handle = &mtr->handle.random;
return SUCCESS;
}
size_t meter_read_random(meter_t *mtr, reading_t rds[], size_t n) {
meter_handle_random_t *handle = &mtr->handle.random;
meter_handle_random_t *handle = &mtr->handle.random;
handle->last += ltqnorm((float) rand() / RAND_MAX);
double step = ltqnorm((float) rand() / RAND_MAX);
double new = handle->last + step;
/* check bounaries */
if (handle->last > handle->max) {
handle->last = handle->max;
}
else if (handle->last < handle->min) {
handle->last = handle->min;
}
/* check boundaries */
handle->last += (new > handle->max || new < handle->min) ? -step : step;
rds->value = handle->last;
gettimeofday(&rds->time, NULL);

View file

@ -27,10 +27,23 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <errno.h>
#include "meter.h"
#include "s0.h"
#include "options.h"
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) {
print(log_error, "Missing device or invalid type", mtr);
return ERR;
}
return SUCCESS;
}
/**
* Setup serial port
@ -39,11 +52,11 @@ int meter_open_s0(meter_t *mtr) {
meter_handle_s0_t *handle = &mtr->handle.s0;
/* open port */
int fd = open(mtr->connection, O_RDWR | O_NOCTTY);
int fd = open(handle->device, O_RDWR | O_NOCTTY);
if (fd < 0) {
perror(mtr->connection);
return -1;
print(log_error, "open(%s): %s", mtr, handle->device, strerror(errno));
return ERR;
}
/* save current port settings */
@ -66,17 +79,17 @@ int meter_open_s0(meter_t *mtr) {
tcsetattr(fd, TCSANOW, &tio);
handle->fd = fd;
return 0;
return SUCCESS;
}
void meter_close_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);
/* close serial port */
close(handle->fd);
return close(handle->fd);
}
size_t meter_read_s0(meter_t *mtr, reading_t rds[], size_t n) {

View file

@ -33,6 +33,7 @@
#include <errno.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>
/* serial port */
#include <fcntl.h>
@ -50,27 +51,52 @@
#include "meter.h"
#include "sml.h"
#include "obis.h"
#include "options.h"
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);
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;
}
return SUCCESS;
}
int meter_open_sml(meter_t *mtr) {
meter_handle_sml_t *handle = &mtr->handle.sml;
char *addr = strdup(mtr->connection);
char *node = strsep(&addr, ":");
char *service = strsep(&addr, ":");
if (handle->device != NULL) {
print(log_error, "TODO: implement serial interface", mtr);
return ERR;
}
else if (handle->host != NULL) {
char *addr = strdup(handle->host);
char *node = strsep(&addr, ":");
char *service = strsep(&addr, ":");
handle->fd = meter_sml_open_socket(node, service);
//handle->fd = meter_sml_open_port(args);
handle->fd = meter_sml_open_socket(node, service);
}
free(addr);
return (handle->fd < 0) ? -1 : 0;
return (handle->fd < 0) ? ERR : SUCCESS;
}
void meter_close_sml(meter_t *meter) {
int meter_close_sml(meter_t *meter) {
meter_handle_sml_t *handle = &meter->handle.sml;
// TODO reset serial port
close(handle->fd);
return close(handle->fd);
}
size_t meter_read_sml(meter_t *meter, reading_t rds[], size_t n) {
@ -137,8 +163,8 @@ int meter_sml_open_socket(const char *node, const char *service) {
fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0) {
fprintf(stderr, "error: socket(): %s\n", strerror(errno));
return -1;
print(log_error, "socket(): %s", NULL, strerror(errno));
return ERR;
}
getaddrinfo(node, service, NULL, &ais);
@ -147,8 +173,8 @@ int meter_sml_open_socket(const char *node, const char *service) {
res = connect(fd, (struct sockaddr *) &sin, sizeof(sin));
if (res < 0) {
fprintf(stderr, "error: connect(%s, %s): %s\n", node, service, strerror(errno));
return -1;
print(log_error, "connect(%s, %s): %s", NULL, node, service, strerror(errno));
return ERR;
}
return fd;
@ -161,8 +187,8 @@ int meter_sml_open_port(const char *device) {
int fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0) {
fprintf(stderr, "error: open(%s): %s\n", device, strerror(errno));
return -1;
print(log_error, "open(%s): %s", NULL, device, strerror(errno));
return ERR;
}
// set RTS