huge changes: a lot of code refactoring, coding standards, a improved configuration system, support for absolute counters etc...
This commit is contained in:
parent
4da2811f34
commit
bdabc5c518
38 changed files with 1578 additions and 573 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_ */
|
||||
|
|
94
bin/logger/include/config.h
Normal file
94
bin/logger/include/config.h
Normal 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_ */
|
|
@ -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(
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
288
bin/logger/src/config.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
35
include/common.h
Normal 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_ */
|
12
include/d0.h
12
include/d0.h
|
@ -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
45
include/exec.h
Normal 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
47
include/file.h
Normal 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
116
include/list.h
Normal 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_ */
|
|
@ -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_ */
|
||||
|
|
|
@ -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
44
include/options.h
Normal 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_ */
|
|
@ -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_ */
|
||||
|
|
10
include/s0.h
10
include/s0.h
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
0
src/common.c
Normal file
71
src/d0.c
71
src/d0.c
|
@ -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
69
src/exec.c
Normal 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
95
src/file.c
Normal 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", ®ex) == SUCCESS) {
|
||||
handle->regex = strdup(regex);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
}
|
|
@ -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 /
|
||||
|
|
144
src/meter.c
144
src/meter.c
|
@ -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;
|
||||
}
|
||||
|
|
107
src/obis.c
107
src/obis.c
|
@ -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
39
src/options.c
Normal 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); }
|
48
src/random.c
48
src/random.c
|
@ -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);
|
||||
|
|
25
src/s0.c
25
src/s0.c
|
@ -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) {
|
||||
|
|
58
src/sml.c
58
src/sml.c
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue