diff --git a/bin/logger/include/api.h b/bin/logger/include/api.h index fdc6b4d..4890092 100644 --- a/bin/logger/include/api.h +++ b/bin/logger/include/api.h @@ -32,18 +32,35 @@ #include #include "buffer.h" +#include "channel.h" typedef struct { char *data; size_t size; } CURLresponse; -/* curl callbacks */ +CURL * api_curl_init(channel_t *ch); + +/** + * Reformat CURLs debugging output + */ int curl_custom_debug_callback(CURL *curl, curl_infotype type, char *data, size_t size, void *custom); + size_t curl_custom_write_callback(void *ptr, size_t size, size_t nmemb, void *data); -json_object * api_json_tuples(buffer_t *buf, meter_reading_t *first, meter_reading_t *last); -void * logging_thread(void *arg); -double api_tvtof(struct timeval tv); +/** + * Create JSON object of tuples + * + * @param buf the buffer our readings are stored in (required for mutex) + * @param first the first tuple of our linked list which should be encoded + * @param last the last tuple of our linked list which should be encoded + * @return the json_object (has to be free'd) + */ +json_object * api_json_tuples(buffer_t *buf, reading_t *first, reading_t *last); + +/** + * Parses JSON encoded exception and stores describtion in err + */ +void api_parse_exception(CURLresponse response, char *err, size_t n); #endif /* _API_H_ */ diff --git a/bin/logger/include/buffer.h b/bin/logger/include/buffer.h index 863c744..4950286 100644 --- a/bin/logger/include/buffer.h +++ b/bin/logger/include/buffer.h @@ -31,12 +31,12 @@ #include #include -#include +#include typedef struct { - meter_reading_t *last; /* the oldest reading NOT sent */ - meter_reading_t *start; - meter_reading_t *sent; + reading_t *tail; + reading_t *head; + reading_t *sent; int size; /* number of readings currently in the buffer */ int keep; /* number of readings to cache for local interface */ @@ -45,8 +45,8 @@ typedef struct { } buffer_t; /* Prototypes */ -void buffer_init(buffer_t *buf, int keep); -meter_reading_t * buffer_push(buffer_t *buf, meter_reading_t rd); +void buffer_init(buffer_t *buf); +reading_t * buffer_push(buffer_t *buf, reading_t *rd); void buffer_free(buffer_t *buf); void buffer_clean(buffer_t *buf); void buffer_clear(buffer_t *buf); diff --git a/bin/logger/include/channel.h b/bin/logger/include/channel.h index ba73bba..dcd6c1d 100644 --- a/bin/logger/include/channel.h +++ b/bin/logger/include/channel.h @@ -27,34 +27,27 @@ #define _CHANNEL_H_ #include -#include - #include +#include "vzlogger.h" #include "buffer.h" -typedef struct channel { - unsigned int id; /* only for internal usage & debugging */ +typedef struct { + char id[5]; /* only for internal usage & debugging */ char *middleware; /* url to middleware */ - char *options; /* protocols specific configuration */ char *uuid; /* unique identifier for middleware */ - - unsigned long interval; /* polling interval (for sensors only) */ - - meter_t meter; /* handle to store connection status */ + unsigned long interval; /* polling interval (== 0 for meters) */ + + reading_id_t identifier; /* channel identifier (OBIS, string) */ buffer_t buffer; /* circular queue to buffer readings */ - pthread_t logging_thread; /* pthread for asynchronus logging */ - pthread_t reading_thread; /* pthread for asynchronus reading */ pthread_cond_t condition; /* pthread syncronization to notify logging thread and local webserver */ - - struct channel *next; /* pointer for linked list */ + pthread_t thread; /* pthread for asynchronus logging */ + pthread_status_t status; } channel_t; /* Prototypes */ -void channel_init(channel_t *ch, char *uuid, char *middleware, unsigned long interval, char *options, meter_type_t *type); +void channel_init(channel_t *ch, char *uuid, char *middleware, unsigned long interval, reading_id_t identifier); void channel_free(channel_t *ch); -void * reading_thread(void *arg); - #endif /* _CHANNEL_H_ */ diff --git a/bin/logger/include/list.h b/bin/logger/include/list.h index 7ba93ab..9ee72a9 100644 --- a/bin/logger/include/list.h +++ b/bin/logger/include/list.h @@ -1,5 +1,5 @@ /** - * Linked list to manage channels + * Generic linked list * * @package vzlogger * @copyright Copyright (c) 2011, The volkszaehler.org project @@ -26,16 +26,81 @@ #ifndef _LIST_H_ #define _LIST_H_ -#include "channel.h" +#include + +#define foreach(list, it) \ + for( \ + __list_item_t *(it) = (list).head; \ + (it) != NULL; \ + (it) = (it)->next \ + ) \ + +typedef struct __list_item { + void *data; + struct __list_item *prev; + struct __list_item *next; +} __list_item_t; typedef struct { - channel_t *start; int size; + __list_item_t *head; + __list_item_t *tail; } list_t; -/* Prototypes */ -void list_init(list_t *ls); -int list_push(list_t *ls, channel_t ch); -void list_free(list_t *ls); +inline void list_init(list_t *list) { + list->size = 0; + list->head = list->tail = NULL; +} + +inline int 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; + void *data = old->data; + + list->tail = old->prev; + list->size--; + + if (list->head == old) { + list->head = NULL; + } + + 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->size = 0; + list->tail = NULL; +} #endif /* _LIST_H_ */ diff --git a/bin/logger/include/options.h b/bin/logger/include/options.h index ccb332d..b4ad524 100644 --- a/bin/logger/include/options.h +++ b/bin/logger/include/options.h @@ -27,6 +27,7 @@ #define _OPTIONS_H_ #include "list.h" +#include "channel.h" /** * Options from command line @@ -36,13 +37,14 @@ typedef struct { unsigned int port; /* tcp port for local interface */ int verbose; /* verbosity level */ - /* boolean bitfields, at the end of struct */ - int daemon:1; + /* boolean bitfields, padding at the end of struct */ + int daemon:1; /* run in background */ int local:1; /* enable local interface */ + int logging:1; /* start logging threads */ } options_t; /* Prototypes */ -void parse_options(int argc, char *argv[], options_t *opts); -void parse_channels(char *filename, list_t *chans); +void parse_options(int argc, char *argv[], options_t *options); +void parse_channels(const char *filename, list_t *meters); #endif /* _OPTIONS_H_ */ diff --git a/bin/logger/src/list.c b/bin/logger/include/threads.h similarity index 57% rename from bin/logger/src/list.c rename to bin/logger/include/threads.h index 443f94c..93277ee 100644 --- a/bin/logger/src/list.c +++ b/bin/logger/include/threads.h @@ -1,5 +1,5 @@ /** - * Linked list to manage channels + * Thread headers * * @package vzlogger * @copyright Copyright (c) 2011, The volkszaehler.org project @@ -23,46 +23,12 @@ * along with volkszaehler.org. If not, see . */ -#include -#include +#ifndef _THREADS_H_ +#define _THREADS_H_ -#include "list.h" +void logging_thread_cleanup(void *arg); -void list_init(list_t *ls) { - ls->start = NULL; - ls->size = 0; -} +void * logging_thread(void *arg); +void * reading_thread(void *arg); -int list_push(list_t *ls, channel_t ch) { - channel_t *new = malloc(sizeof(channel_t)); - - if (!new) { - return 0; /* cannot allocate memory */ - } - - memcpy(new, &ch, sizeof(channel_t)); - - if (ls->start == NULL) { /* empty list */ - new->next = NULL; - } - else { - new->next = ls->start; - } - - ls->start = new; - ls->size++; - - return ls->size; -} - -void list_free(list_t *ls) { - channel_t *ch = ls->start; - do { - channel_t *tmp = ch; - ch = ch->next; - channel_free(tmp); - } while (ch); - - ls->start = NULL; - ls->size = 0; -} +#endif /* _THREADS_H_ */ diff --git a/bin/logger/include/vzlogger.h b/bin/logger/include/vzlogger.h index 8148fd5..ee3ca6d 100644 --- a/bin/logger/include/vzlogger.h +++ b/bin/logger/include/vzlogger.h @@ -26,20 +26,37 @@ #ifndef _VZLOGGER_H_ #define _VZLOGGER_H_ -#include "../../config.h" - +#include #include -#include "channel.h" +#include "config.h" + +#include "list.h" /* some hard coded configuration */ -#define RETRY_PAUSE 10 //600 /* seconds to wait after failed request */ -#define BUFFER_DURATION 60 /* in seconds */ -#define BUFFER_LENGTH 10 // 256 /* in readings */ -#define COMET_TIMEOUT 6 //30 /* in seconds */ +#define RETRY_PAUSE 30 /* seconds to wait after failed request */ +#define BUFFER_KEEP 600 /* for the local interface; in seconds */ +#define COMET_TIMEOUT 30 /* in seconds */ + +typedef enum { + UNKNOWN, + RUNNING, + TERMINATED, + CANCELED +} pthread_status_t; + +/** + * Type for associating channels to meters + */ +typedef struct { + meter_t meter; + list_t channels; + pthread_t thread; + pthread_status_t status; +} assoc_t; /* Prototypes */ -void print(int level, char *format, channel_t *ch, ... ); +void print(int level, const char *format, void *id, ... ); void usage(char ** argv); #endif /* _VZLOGGER_H_ */ diff --git a/bin/logger/src/api.c b/bin/logger/src/api.c index 8e7f26c..1b075ac 100644 --- a/bin/logger/src/api.c +++ b/bin/logger/src/api.c @@ -28,20 +28,15 @@ #include #include #include - -#include +#include #include "api.h" -#include "vzlogger.h" #include "options.h" -extern options_t opts; +extern options_t options; -/** - * Reformat CURLs debugging output - */ int curl_custom_debug_callback(CURL *curl, curl_infotype type, char *data, size_t size, void *arg) { - channel_t *ch = (channel_t *) ch; + channel_t *ch = (channel_t *) arg; char *end = strchr(data, '\n'); if (data == end) return 0; /* skip empty line */ @@ -50,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(7, "CURL: %.*s", ch, (int) size, data); + print(11, "CURL: %.*s", ch, (int) size, data); break; case CURLINFO_SSL_DATA_IN: case CURLINFO_DATA_IN: - print(9, "CURL: Received %lu bytes", ch, (unsigned long) size); + print(14, "CURL: Received %lu bytes", ch, (unsigned long) size); break; case CURLINFO_SSL_DATA_OUT: case CURLINFO_DATA_OUT: - print(9, "CURL: Sent %lu bytes.. ", ch, (unsigned long) size); + print(14, "CURL: Sent %lu bytes.. ", ch, (unsigned long) size); break; case CURLINFO_HEADER_IN: @@ -88,26 +83,18 @@ size_t curl_custom_write_callback(void *ptr, size_t size, size_t nmemb, void *da return realsize; } -double api_tvtof(struct timeval tv) { - return round(tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0); -} - -/** - * Create JSON object of tuples - * - * @param buf the buffer our readings are stored in (required for mutex) - * @param first the first tuple of our linked list which should be encoded - * @param last the last tuple of our linked list which should be encoded - */ -json_object * api_json_tuples(buffer_t *buf, meter_reading_t *first, meter_reading_t *last) { +json_object * api_json_tuples(buffer_t *buf, reading_t *first, reading_t *last) { json_object *json_tuples = json_object_new_array(); - meter_reading_t *it; + reading_t *it; for (it = first; it != last->next; it = it->next) { struct json_object *json_tuple = json_object_new_array(); pthread_mutex_lock(&buf->mutex); - double timestamp = api_tvtof(it->tv); // TODO use long int of new json-c version + + // TODO use long int of new json-c version + // API requires milliseconds => * 1000 + double timestamp = tvtod(it->time) * 1000; double value = it->value; pthread_mutex_unlock(&buf->mutex); @@ -141,14 +128,14 @@ CURL * api_curl_init(channel_t *ch) { curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header); - curl_easy_setopt(curl, CURLOPT_VERBOSE, (int) opts.verbose); + curl_easy_setopt(curl, CURLOPT_VERBOSE, (int) options.verbose); curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_custom_debug_callback); curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void *) ch); return curl; } -void api_parse_exception(CURLresponse response, char *err) { +void api_parse_exception(CURLresponse response, char *err, size_t n) { struct json_tokener *json_tok; struct json_object *json_obj; @@ -158,103 +145,20 @@ void api_parse_exception(CURLresponse response, char *err) { json_obj = json_object_object_get(json_obj, "exception"); if (json_obj) { - sprintf(err, "%s: %s", + snprintf(err, n, "%s: %s", json_object_get_string(json_object_object_get(json_obj, "type")), json_object_get_string(json_object_object_get(json_obj, "message")) ); } else { - strcpy(err, "missing exception"); + strncpy(err, "missing exception", n); } } else { - strcpy(err, json_tokener_errors[json_tok->err]); + strncpy(err, json_tokener_errors[json_tok->err], n); } json_object_put(json_obj); json_tokener_free(json_tok); } - -/** - * Logging thread - * - * Logs buffered readings against middleware - */ -void logging_thread_cleanup(void *arg) { - curl_easy_cleanup((CURL *) arg); /* always cleanup */ -} - -void * logging_thread(void *arg) { - CURL *curl; - channel_t *ch = (channel_t *) arg; /* casting argument */ - - /* we don't to log in local only mode */ - if (opts.local && !opts.daemon) { - return NULL; - } - - curl = api_curl_init(ch); - pthread_cleanup_push(&logging_thread_cleanup, curl); - - do { /* start thread mainloop */ - CURLresponse response; - json_object *json_obj; - meter_reading_t *last; - - const char *json_str; - long int http_code, curl_code; - - /* initialize response */ - response.data = NULL; - response.size = 0; - - pthread_mutex_lock(&ch->buffer.mutex); - while (ch->buffer.sent == NULL) { /* detect spurious wakeups */ - pthread_cond_wait(&ch->condition, &ch->buffer.mutex); /* sleep until new data has been read */ - } - pthread_mutex_unlock(&ch->buffer.mutex); - - last = ch->buffer.last; - json_obj = api_json_tuples(&ch->buffer, ch->buffer.sent, last); - json_str = json_object_to_json_string(json_obj); - - print(10, "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); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &response); - - curl_code = curl_easy_perform(curl); - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - - /* check response */ - if (curl_code == CURLE_OK && http_code == 200) { /* everything is ok */ - print(4, "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)); - } - else if (http_code != 200) { - char err[255]; - api_parse_exception(response, err); - print(-1, "Error from middleware: %s", ch, err); - } - } - - /* householding */ - free(response.data); - json_object_put(json_obj); - - if (opts.daemon && (curl_code != CURLE_OK || http_code != 200)) { - print(2, "Sleeping %i seconds due to previous failure", ch, RETRY_PAUSE); - sleep(RETRY_PAUSE); - } - } while (opts.daemon); - - pthread_cleanup_pop(1); - - return NULL; -} diff --git a/bin/logger/src/buffer.c b/bin/logger/src/buffer.c index b032d74..b99aeb0 100644 --- a/bin/logger/src/buffer.c +++ b/bin/logger/src/buffer.c @@ -31,31 +31,32 @@ #include "buffer.h" -void buffer_init(buffer_t *buf, int keep) { +void buffer_init(buffer_t *buf) { pthread_mutex_init(&buf->mutex, NULL); pthread_mutex_lock(&buf->mutex); - buf->last = NULL; - buf->start = NULL; + buf->head = NULL; + buf->tail = NULL; buf->sent = NULL; buf->size = 0; - buf->keep = keep; + buf->keep = 0; pthread_mutex_unlock(&buf->mutex); } -meter_reading_t * buffer_push(buffer_t *buf, meter_reading_t rd) { - meter_reading_t *new; +reading_t * buffer_push(buffer_t *buf, reading_t *rd) { + reading_t *new; /* allocate memory for new reading */ - new = malloc(sizeof(meter_reading_t)); + new = malloc(sizeof(reading_t)); /* cannot allocate memory */ if (new == NULL) { /* => delete old readings (ring buffer) */ if (buf->size > 0) { - pthread_mutex_lock(&buf->mutex); - new = buf->start; - buf->start = new->next; + new = buf->head; + + pthread_mutex_lock(&buf->mutex); + buf->head = new->next; buf->size--; pthread_mutex_unlock(&buf->mutex); } @@ -64,21 +65,20 @@ meter_reading_t * buffer_push(buffer_t *buf, meter_reading_t rd) { } } - memcpy(new, &rd, sizeof(meter_reading_t)); + memcpy(new, rd, sizeof(reading_t)); pthread_mutex_lock(&buf->mutex); if (buf->size == 0) { /* empty buffer */ - buf->start = new; + buf->head = new; } else { - buf->last->next = new; + buf->tail->next = new; } new->next = NULL; - buf->last = new; + buf->tail = new; buf->size++; - pthread_mutex_unlock(&buf->mutex); return new; @@ -86,10 +86,10 @@ meter_reading_t * buffer_push(buffer_t *buf, meter_reading_t rd) { void buffer_clean(buffer_t *buf) { pthread_mutex_lock(&buf->mutex); - while(buf->size > buf->keep && buf->start != buf->sent) { - meter_reading_t *pop = buf->start; + while(buf->size > buf->keep && buf->head != buf->sent) { + reading_t *pop = buf->head; - buf->start = buf->start->next; + buf->head = buf->head->next; buf->size--; free(pop); @@ -100,7 +100,7 @@ void buffer_clean(buffer_t *buf) { char * buffer_dump(buffer_t *buf, char *dump, int len) { strcpy(dump, "|"); - for (meter_reading_t *rd = buf->start; rd != NULL; rd = rd->next) { + for (reading_t *rd = buf->head; rd != NULL; rd = rd->next) { char tmp[16]; sprintf(tmp, "%.2f|", rd->value); @@ -122,12 +122,16 @@ char * buffer_dump(buffer_t *buf, char *dump, int len) { void buffer_free(buffer_t *buf) { pthread_mutex_destroy(&buf->mutex); - meter_reading_t *rd = buf->start; - do { - meter_reading_t *tmp = rd; + reading_t *rd = buf->head; + if (rd) do { + reading_t *tmp = rd; rd = rd->next; free(tmp); } while (rd); - memset(buf, 0, sizeof(buffer_t)); + buf->head = NULL; + buf->tail = NULL; + buf->sent = NULL; + buf->size = 0; + buf->keep = 0; } diff --git a/bin/logger/src/channel.c b/bin/logger/src/channel.c index 6ecd4e8..7dab98e 100644 --- a/bin/logger/src/channel.c +++ b/bin/logger/src/channel.c @@ -26,82 +26,22 @@ #include #include #include +#include -#include "vzlogger.h" -#include "api.h" #include "channel.h" -#include "options.h" -extern options_t opts; - -void * reading_thread(void *arg) { - channel_t *ch = (channel_t *) arg; - - do { /* start thread main loop */ - meter_reading_t rd, *added; - - rd = meter_read(&ch->meter); - print(1, "Value read: %.2f at %.0f", ch, rd.value, api_tvtof(rd.tv)); - - /* add reading to buffer */ - added = buffer_push(&ch->buffer, rd); - - if ( /* queue reading into sending queue if... */ - added /* we got it in the buffer */ - && ch->buffer.sent == NULL /* there are no other readings pending in queue */ - && (opts.daemon || !opts.local) /* we aren't in local only mode */ - ) { - ch->buffer.sent = added; - } - - /* shrink buffer */ - buffer_clean(&ch->buffer); - - pthread_mutex_lock(&ch->buffer.mutex); - pthread_cond_broadcast(&ch->condition); /* notify webserver and logging thread */ - pthread_mutex_unlock(&ch->buffer.mutex); - - /* Debugging */ - if (opts.verbose >= 10) { - char dump[1024]; - buffer_dump(&ch->buffer, dump, 1024); - print(10, "Buffer dump: %s (size=%i, memory=%i)", ch, dump, ch->buffer.size, ch->buffer.keep); - } - - if ((opts.daemon || opts.local) && ch->meter.type->periodical) { - print(8, "Next reading in %i seconds", ch, ch->interval); - sleep(ch->interval); - } - } while (opts.daemon || opts.local); - - return NULL; -} - -void channel_init(channel_t *ch, char *uuid, char *middleware, unsigned long interval, char *options, meter_type_t *type) { +void channel_init(channel_t *ch, char *uuid, char *middleware, unsigned long interval, reading_id_t identifier) { static int instances; /* static to generate channel ids */ + snprintf(ch->id, 5, "ch%i", instances++); - int keep; /* number of readings to cache for local interface */ - if (opts.local) { - if (type->periodical) { - keep = (BUFFER_DURATION / interval) + 1; /* determine cache length by interval */ - } - else { - keep = BUFFER_LENGTH; /* fixed cache length for meters */ - } - } - else { - keep = 0; /* don't cache if we have no HTTPd started */ - } - - ch->id = instances++; ch->interval = interval; + ch->identifier = identifier; + ch->status = UNKNOWN; + ch->uuid = strdup(uuid); ch->middleware = strdup(middleware); - ch->options = strdup(options); - - meter_init(&ch->meter, type, options); - buffer_init(&ch->buffer, keep); /* initialize buffer */ + buffer_init(&ch->buffer); /* initialize buffer */ pthread_cond_init(&ch->condition, NULL); /* initialize thread syncronization helpers */ } @@ -110,10 +50,8 @@ void channel_init(channel_t *ch, char *uuid, char *middleware, unsigned long int */ void channel_free(channel_t *ch) { buffer_free(&ch->buffer); - meter_free(&ch->meter); pthread_cond_destroy(&ch->condition); free(ch->uuid); - free(ch->options); free(ch->middleware); } diff --git a/bin/logger/src/local.c b/bin/logger/src/local.c index 58df3bb..5f04ec9 100644 --- a/bin/logger/src/local.c +++ b/bin/logger/src/local.c @@ -29,57 +29,65 @@ #include #include "vzlogger.h" +#include "channel.h" #include "local.h" -#include "options.h" #include "api.h" -extern list_t chans; -extern options_t opts; - 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) { - const char *mode; - int ret, http_status = MHD_HTTP_NOT_FOUND; + int status; + int response_code = MHD_HTTP_NOT_FOUND; + + list_t *assocs = (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: %s %s %s", NULL, version, method, url); + print(2, "Local request received: method=%s url=%s mode=%s", NULL, method, url, mode); - mode = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "mode"); - if (strcmp(method, "GET") == 0) { struct timespec ts; struct timeval tp; struct json_object *json_obj = json_object_new_object(); - struct json_object *json_data = json_object_new_object(); + struct json_object *json_data = json_object_new_array(); const char *uuid = url+1; const char *json_str; - for (channel_t *ch = chans.start; ch != NULL; ch = ch->next) { - if (strcmp(url, "/") == 0 || strcmp(ch->uuid, uuid) == 0) { - http_status = MHD_HTTP_OK; - - /* convert from timeval to timespec */ - gettimeofday(&tp, NULL); - ts.tv_sec = tp.tv_sec; - ts.tv_nsec = tp.tv_usec * 1000; - - ts.tv_sec += (ch->meter.type->periodical) ? ch->interval : COMET_TIMEOUT; + foreach(*assocs, it) { + assoc_t *assoc = (assoc_t *) it->data; + + foreach(assoc->channels, it) { + channel_t *ch = (channel_t *) it->data; + + if (strcmp(url, "/") == 0 || strcmp(ch->uuid, uuid) == 0) { + response_code = MHD_HTTP_OK; - if (strcmp(url, "/") != 0 && mode && strcmp(mode, "comet") == 0) { - /* blocking until new data arrives (comet-like blocking of HTTP response) */ - pthread_mutex_lock(&ch->buffer.mutex); - pthread_cond_timedwait(&ch->condition, &ch->buffer.mutex, &ts); - pthread_mutex_unlock(&ch->buffer.mutex); + /* blocking until new data arrives (comet-like blocking of HTTP response) */ + if (strcmp(url, "/") != 0 && mode && strcmp(mode, "comet") == 0) { + /* convert from timeval to timespec */ + gettimeofday(&tp, NULL); + ts.tv_sec = tp.tv_sec + COMET_TIMEOUT; + ts.tv_nsec = tp.tv_usec * 1000; + + pthread_mutex_lock(&ch->buffer.mutex); + pthread_cond_timedwait(&ch->condition, &ch->buffer.mutex, &ts); + pthread_mutex_unlock(&ch->buffer.mutex); + } + + struct json_object *json_ch = json_object_new_object(); + + 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(ch->interval)); + + 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); + + json_object_array_add(json_data, json_ch); } - - json_object_object_add(json_data, "uuid", json_object_new_string(ch->uuid)); - json_object_object_add(json_data, "interval", json_object_new_int(ch->interval)); - - struct json_object *json_tuples = api_json_tuples(&ch->buffer, ch->buffer.start, ch->buffer.last); - json_object_object_add(json_data, "tuples", json_tuples); } } @@ -95,16 +103,17 @@ int handle_request(void *cls, struct MHD_Connection *connection, const char *url } else { char *response_str = strdup("not implemented\n"); - response = MHD_create_response_from_data(strlen(response_str), (void *) response_str, TRUE, FALSE); - http_status = MHD_HTTP_METHOD_NOT_ALLOWED; + response = MHD_create_response_from_data(strlen(response_str), (void *) response_str, TRUE, FALSE); + response_code = MHD_HTTP_METHOD_NOT_ALLOWED; + MHD_add_response_header(response, "Content-type", "text/text"); } - ret = MHD_queue_response(connection, http_status, response); + status = MHD_queue_response(connection, response_code, response); MHD_destroy_response(response); - return ret; + return status; } diff --git a/bin/logger/src/options.c b/bin/logger/src/options.c index cb790e9..a6fb17e 100644 --- a/bin/logger/src/options.c +++ b/bin/logger/src/options.c @@ -31,16 +31,15 @@ #include #include -#include "../../config.h" +#include -#include "list.h" #include "options.h" #include "channel.h" #include "vzlogger.h" extern meter_type_t meter_types[]; -options_t opts = { /* setting default options */ +options_t options = { /* setting default options */ "/etc/vzlogger.conf", /* config file */ 8080, /* port for local interface */ 0, /* verbosity level */ @@ -64,6 +63,15 @@ const struct option long_options[] = { {} /* stop condition for iterator */ }; +/** + * Options for config file + */ +struct { + char *key; + int level; +} + + /** * Descriptions vor command line options */ @@ -85,14 +93,10 @@ char *long_options_descs[] = { */ void parse_options(int argc, char * argv[], options_t * opts) { while (1) { - /* getopt_long stores the option index here. */ - int option_index = 0; - - int c = getopt_long(argc, argv, "i:c:p:lhVdv::", long_options, &option_index); + int c = getopt_long(argc, argv, "i:c:p:lhVdv::", long_options, NULL); /* detect the end of the options. */ - if (c == -1) - break; + if (c == -1) break; switch (c) { case 'v': @@ -123,15 +127,18 @@ void parse_options(int argc, char * argv[], options_t * opts) { exit(EXIT_SUCCESS); break; - case 'h': case '?': + case 'h': + default: usage(argv); exit((c == '?') ? EXIT_FAILURE : EXIT_SUCCESS); } } + + opts->logging = (!opts->local || opts->daemon); } -void parse_channels(char *filename, list_t *chans) { +void parse_channels(const char *filename, list_t *assocs) { FILE *file = fopen(filename, "r"); /* open configuration */ if (!filename) { /* nothing found */ @@ -146,84 +153,66 @@ void parse_channels(char *filename, list_t *chans) { else { print(2, "Start parsing configuration from %s", NULL, filename); } - - int lineno = 1; - char *buffer = malloc(256); - char *tokens[5]; - char *line; /* compile regular expressions */ - regex_t re_uuid, re_middleware; - regcomp(&re_uuid, "^[a-f0-9]{8}-([a-f0-9]{4}-){3,3}[a-f0-9]{12}$", REG_EXTENDED | REG_ICASE | REG_NOSUB); - regcomp(&re_middleware, "^https?://[a-z0-9.-]+\\.[a-z]{2,6}(/\\S*)?$", REG_EXTENDED | REG_ICASE | REG_NOSUB); + regex_t re_uuid, re_middleware, re_section, re_value; + regcomp(&re_uuid, "^[:xdigit:]{8}-([:xdigit:]{4}-){3}[:xdigit:]{12}$", REG_EXTENDED | REG_NOSUB); + regcomp(&re_middleware, "^https?://[[:alnum:].-]+(\\.[:alpha:]{2,6})?(:[:digit:]{1,5})?(/\\S*)?$", REG_EXTENDED | REG_NOSUB); - /*regerror(err, &re_uuid, buffer, 256); - printf("Error analyzing regular expression: %s.\n", buffer);*/ - - while ((line = fgets(buffer, 256, file)) != NULL) { /* read a line */ - line[strcspn(line, "\n\r;")] = '\0'; /* strip newline and comments */ - if (strlen(line) == 0) continue; /* skip empty lines */ - - /* channel properties */ - char *middleware, *options, *uuid; - unsigned long interval; - channel_t ch; - - meter_type_t *type; - - /* parse tokens (required) */ - memset(tokens, 0, 5); - for (int i = 0; i < 5; i++) { - do { - tokens[i] = strsep(&line, " \t"); - } while (tokens[i] == NULL && line != NULL); + regcomp(&re_section, "^[:blank:]*<(/?)([:alpha]+)>[:blank:]*$", REG_EXTENDED); + regcomp(&re_value, "^[:blank:]*([:alpha]+)[:blank:]+(.+)[:blank:]*$", REG_EXTENDED); + + struct options { + char *key; + enum context; + //char regex; + } + + /* read a line */ + while ((line = fgets(buffer, 256, file)) != NULL) { + if (regex(&re_section) == 0) { + } - - /* protocol (required) */ - for (type = meter_types; type->name != NULL; type++) { /* linear search */ - if (strcmp(type->name, tokens[0]) == 0) break; - } - + else if (regex(&re_value) == 0) { + switch (context) { + case GLOBAL: + /* no values allowed here */ + break; - if (type->name == NULL) { /* reached end */ - print(-1, "Invalid protocol: %s in %s:%i", NULL, tokens[0], filename, lineno); - exit(EXIT_FAILURE); - } - - /* middleware (required) */ - middleware = tokens[1]; - if (regexec(&re_middleware, middleware, 0, NULL, 0) == 0) { - print(-1, "Invalid interval: %s in %s:%i", NULL, tokens[1], filename, lineno); - exit(EXIT_FAILURE); - } - - /* uuid (required) */ - uuid = tokens[2]; - if (regexec(&re_uuid, uuid, 0, NULL, 0) != 0) { - print(-1, "Invalid uuid: %s in %s:%i", NULL, tokens[2], filename, lineno); - exit(EXIT_FAILURE); - } - - /* interval (only if protocol is sensor) */ - if (type->periodical) { - interval = strtol(tokens[3], (char **) NULL, 10); - if (errno == EINVAL || errno == ERANGE) { - print(-1, "Invalid interval: %s in %s:%i", NULL, tokens[3], filename, lineno); - exit(EXIT_FAILURE); + case METER: + if (strcasecmp(key, "protocol") == 0) { + + } + else if (strcasecmp(key, "interval") == 0) { + + } + else if (strcasecmp(key, "host") == 0) { + + } + else if (strcasecmp(key, "port") == 0) { + + } + break; + + case CHANNEL: + if (strcasecmp(key, "uuid") == 0) { + + } + else if (strcasecmp(key, "middlware") == 0) { + + } + else if (strcasecmp(key, "identifier") == 0) { + + } + + break; + + char *key, *value; + + } } - else { - interval = 0; - } - - /* options (optional) */ - options = tokens[type->periodical ? 4 : 3]; - - channel_init(&ch, uuid, middleware, interval, options, type); - print(1, "Parsed (protocol=%s interval=%i uuid=%s middleware=%s options=%s)", &ch, type->name, interval, uuid, middleware, options); - - list_push(chans, ch); - lineno++; + } fclose(file); @@ -231,3 +220,5 @@ void parse_channels(char *filename, list_t *chans) { regfree(&re_middleware); regfree(&re_uuid); } + + diff --git a/bin/logger/src/threads.c b/bin/logger/src/threads.c new file mode 100644 index 0000000..de323e9 --- /dev/null +++ b/bin/logger/src/threads.c @@ -0,0 +1,187 @@ +/** + * Thread implementations + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + */ +/* + * 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 . + */ + +#include +#include + +#include "threads.h" +#include "api.h" +#include "options.h" + +extern 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); + time_t last, delta; + size_t n = 0; + + pthread_cleanup_push(&reading_thread_cleanup, rds); + + do { /* start thread main loop */ + /* fetch readings from meter */ + + last = time(NULL); + n = meter_read(mtr, rds, mtr->type->max_readings); + delta = time(NULL) - last; + + foreach(assoc->channels, it) { + channel_t *ch = (channel_t *) it->data; + buffer_t *buf = &ch->buffer; + reading_t *added; + + 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(5, "New reading (value=%.2f ts=%f)", ch, ch->id, rds[i].value, tvtod(rds[i].time)); + added = buffer_push(buf, &rds[i]); + } + } + + /* update buffer length with current interval */ + if (!mtr->type->periodic && options.local && delta) { + print(15, "Updating interval to %i", ch, delta); + ch->interval = delta; + ch->buffer.keep = ceil(BUFFER_KEEP / (double) delta); + } + + /* queue reading into sending buffer logging thread if + logging is enabled & sent queue is empty */ + if (options.logging && !buf->sent) { + buf->sent = added; + } + + /* shrink buffer */ + buffer_clean(buf); + + /* notify webserver and logging thread */ + pthread_mutex_lock(&buf->mutex); + pthread_cond_broadcast(&ch->condition); + pthread_mutex_unlock(&buf->mutex); + + /* debugging */ + if (options.verbose >= 10) { + char dump[1024]; + buffer_dump(buf, dump, 1024); + print(10, "Buffer dump: %s (size=%i, memory=%i)", ch, dump, buf->size, buf->keep); + } + } + + if ((options.daemon || options.local) && mtr->type->periodic) { + print(8, "Next reading in %i seconds", NULL, 5); + sleep(5); // TODO handle parsing + } + } while (options.daemon || options.local); + + pthread_cleanup_pop(1); + + return NULL; +} + +void logging_thread_cleanup(void *arg) { + curl_easy_cleanup((CURL *) arg); /* always cleanup */ +} + +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; + + /* initialize response */ + response.data = NULL; + response.size = 0; + + pthread_mutex_lock(&ch->buffer.mutex); + while (ch->buffer.sent == NULL) { /* detect spurious wakeups */ + pthread_cond_wait(&ch->condition, &ch->buffer.mutex); /* sleep until new data has been read */ + } + pthread_mutex_unlock(&ch->buffer.mutex); + + last = ch->buffer.tail; + json_obj = api_json_tuples(&ch->buffer, ch->buffer.sent, last); + json_str = json_object_to_json_string(json_obj); + + print(10, "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); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &response); + + curl_code = curl_easy_perform(curl); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + + /* check response */ + if (curl_code == CURLE_OK && http_code == 200) { /* everything is ok */ + print(4, "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)); + } + else if (http_code != 200) { + char err[255]; + api_parse_exception(response, err, 255); + print(-1, "Error from middleware: %s", ch, err); + } + } + + /* householding */ + free(response.data); + json_object_put(json_obj); + + if (options.daemon && (curl_code != CURLE_OK || http_code != 200)) { + print(2, "Sleeping %i seconds due to previous failure", ch, RETRY_PAUSE); + sleep(RETRY_PAUSE); + } + } while (options.daemon); + + pthread_cleanup_pop(1); + + return NULL; +} diff --git a/bin/logger/src/vzlogger.c b/bin/logger/src/vzlogger.c index 858e206..c64cdcf 100644 --- a/bin/logger/src/vzlogger.c +++ b/bin/logger/src/vzlogger.c @@ -26,27 +26,27 @@ #include /* for print() */ #include - +#include +#include #include #include #include +#include #include "vzlogger.h" - #include "list.h" -#include "buffer.h" #include "channel.h" -#include "api.h" #include "options.h" +#include "threads.h" #ifdef LOCAL_SUPPORT #include #include "local.h" #endif /* LOCAL_SUPPORT */ -/* global variables */ -list_t chans; -extern options_t opts; +list_t assocs; /* mapping between meters and channels */ + +extern options_t options; extern const char *long_options_descs[]; extern const struct option long_options[]; extern const meter_type_t meter_types[]; @@ -86,25 +86,28 @@ void usage(char *argv[]) { * @param ch could be NULL for general messages * @todo integrate into syslog */ -void print(int level, char * format, channel_t *ch, ... ) { +void print(int level, const char *format, void *id, ... ) { va_list args; struct timeval now; struct tm * timeinfo; - char buffer[1024] = "[", *pos = buffer+1; + char buffer[1024], *pos = buffer; - if (level <= opts.verbose) { + if (level <= options.verbose) { gettimeofday(&now, NULL); timeinfo = localtime(&now.tv_sec); + + pos += sprintf(pos, "["); pos += strftime(pos, 16, "%b %d %H:%M:%S", timeinfo); - pos += sprintf(pos, ".%03lu] ", now.tv_usec / 1000); - - if (ch != NULL) { - pos += sprintf(pos, "[ch#%i] ", ch->id); + if (id != NULL) { + pos += sprintf(pos, "][%s]\t", (char *) id); + } + else { + pos += sprintf(pos, "]\t"); } - va_start(args, ch); + va_start(args, id); pos += vsprintf(pos, format, args); va_end(args); @@ -119,9 +122,17 @@ void print(int level, char * format, channel_t *ch, ... ) { */ void quit(int sig) { print(2, "Closing connections to terminate", NULL); - for (channel_t *ch = chans.start; ch != NULL; ch = ch->next) { - pthread_cancel(ch->logging_thread); - pthread_cancel(ch->reading_thread); + + foreach(assocs, it) { + assoc_t *assoc = (assoc_t *) it->data; + + pthread_cancel(assoc->thread); + + foreach(assoc->channels, it) { + channel_t *ch = (channel_t *) it->data; + + pthread_cancel(ch->thread); + } } } @@ -129,6 +140,7 @@ void quit(int sig) { * The main loop */ int main(int argc, char *argv[]) { + /* bind signal handler */ struct sigaction action; sigemptyset(&action.sa_mask); @@ -136,58 +148,89 @@ int main(int argc, char *argv[]) { action.sa_handler = quit; sigaction(SIGINT, &action, NULL); - list_init(&chans); - parse_options(argc, argv, &opts); /* parse command line arguments */ - parse_channels(opts.config, &chans); /* parse channels from configuration */ + /* initialize adts and apis */ + curl_global_init(CURL_GLOBAL_ALL); + list_init(&assocs); + + parse_options(argc, argv, &options); /* parse command line arguments */ + parse_channels(options.config, &assocs);/* parse channels from configuration */ - curl_global_init(CURL_GLOBAL_ALL); /* global intialization for all threads */ - - for (channel_t *ch = chans.start; ch != NULL; ch = ch->next) { - print(5, "Opening connection to meter", ch); - meter_open(&ch->meter); + /* open connection meters & start threads */ + foreach(assocs, it) { + assoc_t *assoc = (assoc_t *) it->data; + meter_t *mtr = &assoc->meter; + + int res = meter_open(mtr); + if (res < 0) { + exit(EXIT_FAILURE); + } + print(5, "Meter connected", mtr); + pthread_create(&assoc->thread, NULL, &reading_thread, (void *) assoc); + print(5, "Meter thread started", mtr); - print(5, "Starting threads", ch); - pthread_create(&ch->logging_thread, NULL, &logging_thread, (void *) ch); - pthread_create(&ch->reading_thread, NULL, &reading_thread, (void *) ch); + foreach(assoc->channels, it) { + channel_t *ch = (channel_t *) it->data; + + /* set buffer length for perriodic meters */ + if (mtr->type->periodic && options.local) { + ch->buffer.keep = ceil(BUFFER_KEEP / (double) ch->interval); + } + + if (ch->status != RUNNING && options.logging) { + pthread_create(&ch->thread, NULL, &logging_thread, (void *) ch); + print(5, "Logging thread started", ch); + } + } } #ifdef LOCAL_SUPPORT /* start webserver for local interface */ struct MHD_Daemon *httpd_handle = NULL; - if (opts.local) { - print(5, "Starting local interface HTTPd on port %i", NULL, opts.port); + if (options.local) { + print(5, "Starting local interface HTTPd on port %i", NULL, options.port); httpd_handle = MHD_start_daemon( MHD_USE_THREAD_PER_CONNECTION, - opts.port, + options.port, NULL, NULL, - handle_request, - NULL, + &handle_request, &assocs, MHD_OPTION_END ); } #endif /* LOCAL_SUPPORT */ /* wait for all threads to terminate */ - for (channel_t *ch = chans.start; ch != NULL; ch = ch->next) { - pthread_join(ch->logging_thread, NULL); - pthread_join(ch->reading_thread, NULL); + foreach(assocs, it) { + assoc_t *assoc = (assoc_t *) it->data; + meter_t *mtr = &assoc->meter; + + pthread_join(assoc->thread, NULL); + + foreach(assoc->channels, it) { + channel_t *ch = (channel_t *) it->data; + + pthread_join(ch->thread, NULL); + + channel_free(ch); + } + + list_free(&assoc->channels); - meter_close(&ch->meter); /* closing connection */ + meter_close(mtr); /* closing connection */ + meter_free(mtr); } - + #ifdef LOCAL_SUPPORT /* stop webserver */ if (httpd_handle) { - print(8, "Stopping local interface HTTPd on port %i", NULL, opts.port); + print(8, "Stopping local interface HTTPd on port %i", NULL, options.port); MHD_stop_daemon(httpd_handle); } #endif /* LOCAL_SUPPORT */ /* householding */ - list_free(&chans); + list_free(&assocs); curl_global_cleanup(); print(10, "Bye bye!", NULL); - return EXIT_SUCCESS; } diff --git a/bin/reader/Makefile.am b/bin/reader/Makefile.am index 41c59ea..30838ec 100644 --- a/bin/reader/Makefile.am +++ b/bin/reader/Makefile.am @@ -3,12 +3,12 @@ AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(DEPS_VZ_CFLAGS) AM_CPPFLAGS = -I$(top_srcdir)/include -Iinclude AM_LDFLAGS = -# SML +# SML support #################################################################### if SML_SUPPORT bin_PROGRAMS = smlreader smlreader_SOURCES = src/smlreader.c -smlreader_LDADD = -lm $(DEPS_SML_LIBS) ../../src/libobis.la +smlreader_LDADD = -lm $(DEPS_SML_LIBS) $(top_srcdir)/src/libmeter.a AM_CFLAGS += $(DEPS_SML_CFLAGS) endif diff --git a/bin/reader/Makefile.in b/bin/reader/Makefile.in index d91328f..688d784 100644 --- a/bin/reader/Makefile.in +++ b/bin/reader/Makefile.in @@ -32,8 +32,6 @@ POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ @SML_SUPPORT_TRUE@bin_PROGRAMS = smlreader$(EXEEXT) @SML_SUPPORT_TRUE@am__append_1 = $(DEPS_SML_CFLAGS) subdir = bin/reader @@ -53,20 +51,15 @@ am__smlreader_SOURCES_DIST = src/smlreader.c smlreader_OBJECTS = $(am_smlreader_OBJECTS) am__DEPENDENCIES_1 = @SML_SUPPORT_TRUE@smlreader_DEPENDENCIES = $(am__DEPENDENCIES_1) \ -@SML_SUPPORT_TRUE@ ../../src/libobis.la +@SML_SUPPORT_TRUE@ $(top_srcdir)/src/libmeter.a DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) -LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ - $(LDFLAGS) -o $@ +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ SOURCES = $(smlreader_SOURCES) DIST_SOURCES = $(am__smlreader_SOURCES_DIST) ETAGS = etags @@ -74,7 +67,6 @@ CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ -AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -93,38 +85,24 @@ DEPS_SML_CFLAGS = @DEPS_SML_CFLAGS@ DEPS_SML_LIBS = @DEPS_SML_LIBS@ DEPS_VZ_CFLAGS = @DEPS_VZ_CFLAGS@ DEPS_VZ_LIBS = @DEPS_VZ_LIBS@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ -FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ @@ -137,7 +115,6 @@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ -SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ @@ -146,31 +123,21 @@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ -build = @build@ build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ -host = @host@ host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ @@ -201,11 +168,11 @@ AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(DEPS_VZ_CFLAGS) \ AM_CPPFLAGS = -I$(top_srcdir)/include -Iinclude AM_LDFLAGS = @SML_SUPPORT_TRUE@smlreader_SOURCES = src/smlreader.c -@SML_SUPPORT_TRUE@smlreader_LDADD = -lm $(DEPS_SML_LIBS) ../../src/libobis.la +@SML_SUPPORT_TRUE@smlreader_LDADD = -lm $(DEPS_SML_LIBS) $(top_srcdir)/src/libmeter.a all: all-am .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -242,7 +209,7 @@ install-binPROGRAMS: $(bin_PROGRAMS) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ - while read p p1; do if test -f $$p || test -f $$p1; \ + while read p p1; do if test -f $$p; \ then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ @@ -256,8 +223,8 @@ install-binPROGRAMS: $(bin_PROGRAMS) while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ - echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ - $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done @@ -272,13 +239,7 @@ uninstall-binPROGRAMS: cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: - @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ - echo " rm -f" $$list; \ - rm -f $$list || exit $$?; \ - test -n "$(EXEEXT)" || exit 0; \ - list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ - echo " rm -f" $$list; \ - rm -f $$list + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) smlreader$(EXEEXT): $(smlreader_OBJECTS) $(smlreader_DEPENDENCIES) @rm -f smlreader$(EXEEXT) $(LINK) $(smlreader_OBJECTS) $(smlreader_LDADD) $(LIBS) @@ -305,13 +266,6 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` -.c.lo: -@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< - smlreader.o: src/smlreader.c @am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT smlreader.o -MD -MP -MF $(DEPDIR)/smlreader.Tpo -c -o smlreader.o `test -f 'src/smlreader.c' || echo '$(srcdir)/'`src/smlreader.c @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/smlreader.Tpo $(DEPDIR)/smlreader.Po @@ -326,12 +280,6 @@ smlreader.obj: src/smlreader.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 smlreader.obj `if test -f 'src/smlreader.c'; then $(CYGPATH_W) 'src/smlreader.c'; else $(CYGPATH_W) '$(srcdir)/src/smlreader.c'; fi` -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs - ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ @@ -448,7 +396,7 @@ maintainer-clean-generic: @echo "it deletes files that may require special tools to rebuild." clean: clean-am -clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) @@ -503,8 +451,7 @@ maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am -mostlyclean-am: mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool +mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am @@ -519,18 +466,17 @@ uninstall-am: uninstall-binPROGRAMS .MAKE: install-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ - clean-generic clean-libtool ctags distclean distclean-compile \ - distclean-generic distclean-libtool distclean-tags distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-binPROGRAMS install-data install-data-am install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am install-man \ - install-pdf install-pdf-am install-ps install-ps-am \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ - pdf pdf-am ps ps-am tags uninstall uninstall-am \ - uninstall-binPROGRAMS + clean-generic ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-binPROGRAMS # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/bin/reader/src/smlreader.c b/bin/reader/src/smlreader.c index 872933f..591e449 100644 --- a/bin/reader/src/smlreader.c +++ b/bin/reader/src/smlreader.c @@ -69,7 +69,15 @@ void transport_receiver(unsigned char *buffer, size_t buffer_len) { time.tv_sec = *body->act_sensor_time->data.timestamp; time.tv_usec = 0; time_mode = 1; + printf("sensor time: %lu.%lu, %i\n", time.tv_sec, time.tv_usec, *body->act_sensor_time->tag); } + + if (body->act_gateway_time) { + time.tv_sec = *body->act_sensor_time->data.timestamp; + time.tv_usec = 0; + time_mode = -1; + printf("sensor time: %lu.%lu, %i\n", time.tv_sec, time.tv_usec, *body->act_sensor_time->tag); + } for (entry = body->val_list; entry != NULL; entry = entry->next) { /* linked list */ obis_id_t id = obis_init(entry->obj_name->str); diff --git a/include/d0.h b/include/d0.h index 669d0bf..7577247 100644 --- a/include/d0.h +++ b/include/d0.h @@ -31,15 +31,16 @@ #include -#include "reading.h" - typedef struct { int fd; /* file descriptor of port */ struct termios oldtio; /* required to reset port */ } meter_handle_d0_t; -int meter_d0_open(meter_handle_d0_t *handle, char *options); -void meter_d0_close(meter_handle_d0_t *handle); -meter_reading_t meter_d0_read(meter_handle_d0_t *handle); +struct meter; /* forward declaration */ +struct reading; /* forward declaration */ + +int meter_open_d0(struct meter *mtr); +void meter_close_d0(struct meter *mtr); +size_t meter_read_d0(struct meter *mtr, struct reading *rds, size_t n); #endif /* _D0_H_ */ diff --git a/include/meter.h b/include/meter.h index e815a1b..ac3fd79 100644 --- a/include/meter.h +++ b/include/meter.h @@ -28,16 +28,19 @@ /** * We have 2 diffrent protocol types: - * - SENSOR: a readout is triggered in equidistant intervals by calling + * - sensors: a readout is triggered in equidistant intervals by calling * the read function with an POSIX timer. * The interval is set in the configuration. - * - METER: the meter itselfs triggers a readout. + * - meters: the meter itselfs triggers a readout. * The pointer to the read function shoul be NULL. * The 'interval' column in the configuration as no meaning. */ -#include "../config.h" -#include "reading.h" +#include +#include + +#include "config.h" +#include "obis.h" /* meter types */ #include "random.h" @@ -48,26 +51,37 @@ #include "sml.h" #endif /* SML_SUPPORT */ -typedef enum { - S0, - D0, - ONEWIRE, - RANDOM, -#ifdef SML_SUPPORT - SML -#endif /* SML_SUPPORT */ -} meter_tag_t; +typedef union reading_id { + obis_id_t obis; +} reading_id_t; + +typedef struct reading { + float value; + struct timeval time; + union reading_id identifier; + + struct reading *next; /* pointer for linked list */ +} reading_t; typedef struct { - meter_tag_t tag; - char *name; /* short identifier for protocol */ - char *desc; /* more detailed description */ - int periodical:1; /* does this meter has be triggered periodically? */ + 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 struct { +typedef struct meter { + char id[5]; /* only for internal usage & debugging */ + char *connection; /* args for connection, further configuration etc */ meter_type_t *type; - char *options; + union { meter_handle_s0_t s0; meter_handle_d0_t d0; @@ -79,12 +93,60 @@ typedef struct { } handle; } meter_t; -/* prototypes */ -void meter_init(meter_t *meter, meter_type_t *type, char *options); -void meter_free(meter_t *meter); -meter_reading_t meter_read(meter_t *meter); +/* Prototypes */ -int meter_open(meter_t *meter); -void meter_close(meter_t *meter); +/** + * Converts timeval structure to double + * + * @param tv the timeval structure + * @return the double value + */ +double tvtod(struct timeval tv); + +/** + * 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..) + */ +void meter_init(meter_t *mtr, meter_type_t *type, const char *connection); + +/** + * Freeing all memory which has been allocated during the initialization + * + * @param mtr the meter structure + */ +void meter_free(meter_t *mtr); + +/** + * Dispatcher for blocking read from meters of diffrent types + * + * rds has to point to an array with space for at least n readings! + * + * @param mtr the meter structure + * @param rds the array to store the readings to + * @param n the size of the array + * @return number of readings + */ +size_t meter_read(meter_t *mtr, reading_t rds[], size_t n); + +/** + * Dispatcher for opening meters of diffrent types, + * + * Establish connection, initialize meter etc. + * + * @param mtr the meter structure + */ +int meter_open(meter_t *mtr); + +/** + * Dispatcher for closing meters of diffrent types + * + * Reset ports, shutdown meter etc. + * + * @param mtr the meter structure + */ +void meter_close(meter_t *mtr); #endif /* _METER_H_ */ diff --git a/include/obis.h b/include/obis.h index a044bd8..df3d8ad 100644 --- a/include/obis.h +++ b/include/obis.h @@ -104,15 +104,13 @@ typedef struct { char *desc; } obis_alias_t; -/* prototypes */ -obis_id_t obis_init(unsigned char *raw); -obis_id_t obis_parse(char *str); +/* Prototypes */ +obis_id_t obis_init(const unsigned char *raw); +obis_id_t obis_parse(const char *str); +obis_id_t obis_lookup_alias(const char *alias); int obis_unparse(obis_id_t id, char *buffer); -char obis_is_manufacturer_specific(obis_id_t id); - -/* inline functions */ -inline int obis_compare(obis_id_t a, obis_id_t b) { - return memcmp(&a, &b, sizeof(obis_id_t)); -} +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); #endif /* _OBIS_H_ */ diff --git a/include/onewire.h b/include/onewire.h index 7b03585..1d0006d 100644 --- a/include/onewire.h +++ b/include/onewire.h @@ -28,14 +28,15 @@ #include -#include "reading.h" - typedef struct { FILE *file; } meter_handle_onewire_t; -int meter_onewire_open(meter_handle_onewire_t *handle, char *options); -void meter_onewire_close(meter_handle_onewire_t *handle); -meter_reading_t meter_onewire_read(meter_handle_onewire_t *handle); +struct meter; /* forward declaration */ +struct reading; /* forward declaration */ + +int meter_open_onewire(struct meter *mtr); +void meter_close_onewire(struct meter *mtr); +size_t meter_read_onewire(struct meter *mtr, struct reading *rds, size_t n); #endif /* _ONEWIRE_H_ */ diff --git a/include/random.h b/include/random.h index dab1aa9..9eaf7b6 100644 --- a/include/random.h +++ b/include/random.h @@ -26,16 +26,16 @@ #ifndef _RANDOM_H_ #define _RANDOM_H_ -#include "reading.h" - typedef struct { double min, max, last; } meter_handle_random_t; -double ltqnorm(double p); /* forward declaration */ +struct meter; /* forward declaration */ +struct reading; /* forward declaration */ +double ltqnorm(double p); /* forward declaration */ -int meter_random_open(meter_handle_random_t *handle, char *options); -void meter_random_close(meter_handle_random_t *handle); -meter_reading_t meter_random_read(meter_handle_random_t *handle); +int meter_open_random(struct meter *mtr); +void meter_close_random(struct meter *mtr); +size_t meter_read_random(struct meter *mtr, struct reading *rds, size_t n); #endif /* _RANDOM_H_ */ diff --git a/include/s0.h b/include/s0.h index 85eaa06..7eb533f 100644 --- a/include/s0.h +++ b/include/s0.h @@ -28,15 +28,16 @@ #include -#include "reading.h" - typedef struct { int fd; /* file descriptor of port */ struct termios oldtio; /* required to reset port */ } meter_handle_s0_t; -int meter_s0_open(meter_handle_s0_t *handle, char *options); -void meter_s0_close(meter_handle_s0_t *handle); -meter_reading_t meter_s0_read(meter_handle_s0_t *handle); +struct meter; /* forward declaration */ +struct reading; /* forward declaration */ + +int meter_open_s0(struct meter *mtr); +void meter_close_s0(struct meter *mtr); +size_t meter_read_s0(struct meter *mtr, struct reading *rds, size_t n); #endif /* _S0_H_ */ diff --git a/include/sml.h b/include/sml.h index d29494a..6a3fa76 100644 --- a/include/sml.h +++ b/include/sml.h @@ -33,24 +33,78 @@ #define SML_BUFFER_LEN 8096 #include +#include -#include "reading.h" #include "obis.h" typedef struct { int fd; - obis_id_t id; /* which OBIS we want to log */ - float counter; /* Zählerstand */ - // termios etc.. + //float counter; /* Zählerstand */ + //termios old_tio; } meter_handle_sml_t; -int meter_sml_open(meter_handle_sml_t *handle, char *options); -void meter_sml_close(meter_handle_sml_t *handle); -meter_reading_t meter_sml_read(meter_handle_sml_t *handle); +struct meter; /* forward declaration */ +struct reading; /* forward declaration */ -meter_reading_t meter_sml_parse(sml_file *file, obis_id_t which); +/** + * Cast arbitrary sized sml_value to double + * + * @param value the sml_value which should be casted + * @return double value representation of sml_value, NAN if an error occured + */ +double sml_value_todouble(sml_value *value); -int meter_sml_open_port(char *device); -int meter_sml_open_socket(char *node, char *service); +/** + * Open connection via serial port or socket to meter + * + * @param mtr the meter structure + * @return 0 on success, -1 on error + */ +int meter_open_sml(struct meter *mtr); + +/** + * Reset port/socket and freeing handle + * + * @param mtr the meter structure + */ +void meter_close_sml(struct meter *mtr); + +/** + * Blocking read on meter + * + * Most EDL conform meters periodically send data every + * 3-4 seconds. + * + * @param mtr the meter structure + * @param rds pointer to array of readings with size n + * @param n size of the rds array + * @return number of readings stored to rds + */ +size_t meter_read_sml(struct meter *mtr, struct reading *rds, size_t n); + +/** + * Parses SML list entry and stores it in reading pointed by rd + * + * @param list the list entry + * @param rd the reading to store to + */ +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 + */ +int meter_sml_open_port(const char *device); + +/** + * Open socket + * + * @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 + */ +int meter_sml_open_socket(const char *node, const char *service); #endif /* _SML_H_ */ diff --git a/src/Makefile.am b/src/Makefile.am index a04a55f..f409d52 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,17 +4,14 @@ AM_LDFLAGS = VERSION = 1:0:0 -lib_LTLIBRARIES = libmeter.la libobis.la +lib_LIBRARIES = libmeter.a -libmeter_la_SOURCES = meter.c d0.c s0.c random.c onewire.c ltqnorm.c -libmeter_la_LDFLAGS = -version-info $(VERSION) - -libobis_la_SOURCES = obis.c -libobis_la_LDFLAGS = -version-info $(VERSION) +libmeter_a_SOURCES = meter.c d0.c s0.c random.c onewire.c ltqnorm.c obis.c +libmeter_a_LDFLAGS = -version-info $(VERSION) # SML support #################################################################### if SML_SUPPORT -libmeter_la_SOURCES += sml.c +libmeter_a_SOURCES += sml.c AM_CFLAGS += $(DEPS_SML_CFLAGS) endif diff --git a/src/Makefile.in b/src/Makefile.in index 4485e55..9b10ddb 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -32,8 +32,6 @@ POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : -build_triplet = @build@ -host_triplet = @host@ # SML support #################################################################### @@ -71,44 +69,33 @@ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__installdirs = "$(DESTDIR)$(libdir)" -LTLIBRARIES = $(lib_LTLIBRARIES) -libmeter_la_LIBADD = -am__libmeter_la_SOURCES_DIST = meter.c d0.c s0.c random.c onewire.c \ - ltqnorm.c sml.c -@SML_SUPPORT_TRUE@am__objects_1 = sml.lo -am_libmeter_la_OBJECTS = meter.lo d0.lo s0.lo random.lo onewire.lo \ - ltqnorm.lo $(am__objects_1) -libmeter_la_OBJECTS = $(am_libmeter_la_OBJECTS) -libmeter_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(libmeter_la_LDFLAGS) $(LDFLAGS) -o $@ -libobis_la_LIBADD = -am_libobis_la_OBJECTS = obis.lo -libobis_la_OBJECTS = $(am_libobis_la_OBJECTS) -libobis_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(libobis_la_LDFLAGS) $(LDFLAGS) -o $@ +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 +@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) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CCLD = $(CC) -LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ - $(LDFLAGS) -o $@ -SOURCES = $(libmeter_la_SOURCES) $(libobis_la_SOURCES) -DIST_SOURCES = $(am__libmeter_la_SOURCES_DIST) $(libobis_la_SOURCES) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(libmeter_a_SOURCES) +DIST_SOURCES = $(am__libmeter_a_SOURCES_DIST) ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ -AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -127,38 +114,24 @@ DEPS_SML_CFLAGS = @DEPS_SML_CFLAGS@ DEPS_SML_LIBS = @DEPS_SML_LIBS@ DEPS_VZ_CFLAGS = @DEPS_VZ_CFLAGS@ DEPS_VZ_LIBS = @DEPS_VZ_LIBS@ -DLLTOOL = @DLLTOOL@ -DSYMUTIL = @DSYMUTIL@ -DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ -FGREP = @FGREP@ GREP = @GREP@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ -LIBTOOL = @LIBTOOL@ -LIPO = @LIPO@ -LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ -MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ -NM = @NM@ -NMEDIT = @NMEDIT@ -OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ -OTOOL = @OTOOL@ -OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ @@ -171,7 +144,6 @@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ -SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ @@ -180,31 +152,21 @@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ -ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ -ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ -build = @build@ build_alias = @build_alias@ -build_cpu = @build_cpu@ -build_os = @build_os@ -build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ -host = @host@ host_alias = @host_alias@ -host_cpu = @host_cpu@ -host_os = @host_os@ -host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ @@ -231,16 +193,14 @@ top_srcdir = @top_srcdir@ AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(am__append_2) AM_CPPFLAGS = -I$(top_srcdir)/include AM_LDFLAGS = -lib_LTLIBRARIES = libmeter.la libobis.la -libmeter_la_SOURCES = meter.c d0.c s0.c random.c onewire.c ltqnorm.c \ - $(am__append_1) -libmeter_la_LDFLAGS = -version-info $(VERSION) -libobis_la_SOURCES = obis.c -libobis_la_LDFLAGS = -version-info $(VERSION) +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) all: all-am .SUFFIXES: -.SUFFIXES: .c .lo .o .obj +.SUFFIXES: .c .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ @@ -271,41 +231,42 @@ $(top_srcdir)/configure: $(am__configure_deps) $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): -install-libLTLIBRARIES: $(lib_LTLIBRARIES) +install-libLIBRARIES: $(lib_LIBRARIES) @$(NORMAL_INSTALL) test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ - } - -uninstall-libLTLIBRARIES: - @$(NORMAL_UNINSTALL) - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + echo " $(INSTALL_DATA) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(INSTALL_DATA) $$list2 "$(DESTDIR)$(libdir)" || exit $$?; } + @$(POST_INSTALL) + @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \ for p in $$list; do \ - $(am__strip_dir) \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + if test -f $$p; then \ + $(am__strip_dir) \ + echo " ( cd '$(DESTDIR)$(libdir)' && $(RANLIB) $$f )"; \ + ( cd "$(DESTDIR)$(libdir)" && $(RANLIB) $$f ) || exit $$?; \ + else :; fi; \ done -clean-libLTLIBRARIES: - -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) - @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ - dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ - test "$$dir" != "$$p" || dir=.; \ - echo "rm -f \"$${dir}/so_locations\""; \ - rm -f "$${dir}/so_locations"; \ - done -libmeter.la: $(libmeter_la_OBJECTS) $(libmeter_la_DEPENDENCIES) - $(libmeter_la_LINK) -rpath $(libdir) $(libmeter_la_OBJECTS) $(libmeter_la_LIBADD) $(LIBS) -libobis.la: $(libobis_la_OBJECTS) $(libobis_la_DEPENDENCIES) - $(libobis_la_LINK) -rpath $(libdir) $(libobis_la_OBJECTS) $(libobis_la_LIBADD) $(LIBS) +uninstall-libLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(libdir)' && rm -f "$$files" )"; \ + cd "$(DESTDIR)$(libdir)" && rm -f $$files + +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 mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -313,14 +274,14 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d0.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ltqnorm.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/meter.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/obis.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onewire.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/random.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/s0.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sml.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d0.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)/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@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -336,19 +297,6 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` -.c.lo: -@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< - -mostlyclean-libtool: - -rm -f *.lo - -clean-libtool: - -rm -rf .libs _libs - ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ @@ -433,7 +381,7 @@ distdir: $(DISTFILES) done check-am: all-am check: check-am -all-am: Makefile $(LTLIBRARIES) +all-am: Makefile $(LIBRARIES) installdirs: for dir in "$(DESTDIR)$(libdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ @@ -465,8 +413,7 @@ maintainer-clean-generic: @echo "it deletes files that may require special tools to rebuild." clean: clean-am -clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ - mostlyclean-am +clean-am: clean-generic clean-libLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) @@ -492,7 +439,7 @@ install-dvi: install-dvi-am install-dvi-am: -install-exec-am: install-libLTLIBRARIES +install-exec-am: install-libLIBRARIES install-html: install-html-am @@ -521,8 +468,7 @@ maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am -mostlyclean-am: mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool +mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am @@ -532,23 +478,22 @@ ps: ps-am ps-am: -uninstall-am: uninstall-libLTLIBRARIES +uninstall-am: uninstall-libLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ - clean-libLTLIBRARIES clean-libtool ctags distclean \ - distclean-compile distclean-generic distclean-libtool \ - distclean-tags distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am \ - install-libLTLIBRARIES install-man install-pdf install-pdf-am \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ + clean-libLIBRARIES ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLIBRARIES install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags uninstall uninstall-am uninstall-libLTLIBRARIES + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-libLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/src/d0.c b/src/d0.c index 1690122..0110106 100644 --- a/src/d0.c +++ b/src/d0.c @@ -35,14 +35,17 @@ #include #include -#include "../include/d0.h" +#include "meter.h" +#include "d0.h" -int meter_d0_open(meter_handle_d0_t *handle, char *options) { +int meter_open_d0(meter_t *mtr) { + meter_handle_d0_t *handle = &mtr->handle.d0; struct termios tio; + memset(&tio, 0, sizeof(tio)); /* open serial port */ - handle->fd = open(options, O_RDWR); // | O_NONBLOCK); + handle->fd = open(mtr->connection, O_RDWR); // | O_NONBLOCK); if (handle->fd < 0) { return -1; @@ -63,16 +66,17 @@ int meter_d0_open(meter_handle_d0_t *handle, char *options) { return 0; } -void meter_d0_close(meter_handle_d0_t *handle) { +void meter_close_d0(meter_t *mtr) { + meter_handle_d0_t *handle = &mtr->handle.d0; + close(handle->fd); } -meter_reading_t meter_d0_read(meter_handle_d0_t *handle) { - meter_reading_t rd; +size_t meter_read_d0(meter_t *mtr, reading_t rds[], size_t n) { + // TODO implement + rds->value = 123.456; + gettimeofday(&rds->time, NULL); - rd.value = 33.3334; - gettimeofday(&rd.tv, NULL); - - return rd; + return 1; } diff --git a/src/meter.c b/src/meter.c index 96e65a0..bbe1913 100644 --- a/src/meter.c +++ b/src/meter.c @@ -25,102 +25,79 @@ #include #include +#include -#include "../include/meter.h" +#include "../bin/logger/include/list.h" + +#include "meter.h" /* List of available meter types */ const meter_type_t meter_types[] = { - {ONEWIRE, "onewire", "Dallas 1-Wire sensors (via OWFS)", 1}, - {RANDOM, "random", "Random walk", 1}, - {S0, "S0", "S0 on RS232", 0}, -// {D0, "D0", "On-site plaintext protocol (DIN EN 62056-21)", 0}, + {ONEWIRE, "onewire", "Dallas 1-Wire sensors (via OWFS)", 1, 1}, + {RANDOM, "random", "Random walk", 1, 1}, + {S0, "s0", "S0 on RS232", 1, 0}, +// {D0, "d0", "On-site plaintext protocol (DIN EN 62056-21)", 16, 0}, #ifdef SML_SUPPORT - {SML, "sml", "Smart Meter Language", 0}, + {SML, "sml", "Smart Meter Language", 16, 0}, #endif /* SML_SUPPORT */ {} /* stop condition for iterator */ }; + +double tvtod(struct timeval tv) { + return tv.tv_sec + tv.tv_usec / 1e6; +} -void meter_init(meter_t *meter, meter_type_t *type, char *options) { - meter->type = type; - meter->options = strdup(options); +void meter_init(meter_t *mtr, 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 *meter) { - free(meter->options); +void meter_free(meter_t *mtr) { + free(mtr->connection); } -int meter_open(meter_t *meter) { - switch (meter->type->tag) { - case RANDOM: - return meter_random_open(&meter->handle.random, meter->options); - - case S0: - return meter_s0_open(&meter->handle.s0, meter->options); - - case D0: - return meter_d0_open(&meter->handle.d0, meter->options); - +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_sml_open(&meter->handle.sml, meter->options); + case SML: return meter_open_sml(mtr); #endif /* SML_SUPPORT */ - - case ONEWIRE: - return meter_onewire_open(&meter->handle.onewire, meter->options); - - default: - return -1; + default: fprintf(stderr, "error: unknown meter type: %i\n", mtr->type->id); } -} - -void meter_close(meter_t *meter) { - switch (meter->type->tag) { - case RANDOM: - meter_random_close(&meter->handle.random); - break; - - case S0: - meter_s0_close(&meter->handle.s0); - break; - - case D0: - meter_d0_close(&meter->handle.d0); - break; - -#ifdef SML_SUPPORT - case SML: - meter_sml_close(&meter->handle.sml); - break; -#endif /* SML_SUPPORT */ - - case ONEWIRE: - meter_onewire_close(&meter->handle.onewire); - break; - } -} - -meter_reading_t meter_read(meter_t *meter) { - meter_reading_t rd; - switch (meter->type->tag) { - case RANDOM: - return meter_random_read(&meter->handle.random); - - case S0: - return meter_s0_read(&meter->handle.s0); - - case D0: - return meter_d0_read(&meter->handle.d0); + 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: - return meter_sml_read(&meter->handle.sml); + case SML: meter_close_sml(mtr); break; #endif /* SML_SUPPORT */ - - case ONEWIRE: - return meter_onewire_read(&meter->handle.onewire); - - default: - return rd; + 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; +} diff --git a/src/obis.c b/src/obis.c index 8b7b2c8..72adfa8 100644 --- a/src/obis.c +++ b/src/obis.c @@ -28,46 +28,68 @@ #include #include -#include "../include/obis.h" +#include "obis.h" obis_alias_t obis_aliases[] = { -// A B C D E F abbreviation description -//===================================================================================== -{{"\x81\x81\xC7\x82\x03\xFF"}, "voltage", ""}, -{{"\x81\x81\xC7\x82\x03\xFF"}, "current", ""}, -{{"\x81\x81\xC7\x82\x03\xFF"}, "frequency", ""}, -{{"\x81\x81\xC7\x82\x03\xFF"}, "powerfactor", ""}, +/** + * 255 is considered as wildcard! + * + * A B C D E F alias description + * ===================================================================================*/ + +/* General */ +{{{ 1, 0, 12, 7, 0, 255}}, "voltage", ""}, +{{{ 1, 0, 11, 7, 0, 255}}, "current", ""}, +{{{ 1, 0, 14, 7, 0, 255}}, "frequency", ""}, +{{{ 1, 0, 12, 7, 0, 255}}, "powerfactor",""}, + +{{{ 1, 0, 1, 7, 255, 255}}, "power", "Active Power Instantaneous value Total"}, +{{{ 1, 0, 21, 7, 255, 255}}, "power-l1", "L1 Active Power Instantaneous value Total"}, +{{{ 1, 0, 41, 7, 255, 255}}, "power-l2", "L1 Active Power Instantaneous value Total"}, +{{{ 1, 0, 61, 7, 255, 255}}, "power-l3", "L3 Active Power Instantaneous value Total"}, + +{{{ 1, 0, 1, 8, 0, 255}}, "counter", "Active Power Counter Total"}, + +/* Easymeter */ +{{{ 1, 0, 96, 5, 5, 255}}, "status", "Meter status flag"}, /* ESYQ3B (Easymeter Q3B) */ -{{"\x81\x81\xC7\x82\x03\xFF"}, "vendor", "vendor specific"}, -{{"\x01\x00\x01\x08\x00\xFF"}, "counter", "Active Power Counter Total"}, -{{"\x01\x00\x01\x08\x01\xFF"}, "counter-tarif1","Active Power Counter Tariff 1"}, -{{"\x01\x00\x01\x08\x02\xFF"}, "counter-tarif2","Active Power Counter Tariff 2"}, -{{"\x01\x00\x01\x07\x00\xFF"}, "power", "Active Power Instantaneous value Total"}, -{{"\x01\x00\x15\x07\x00\xFF"}, "power-l1", "L1 Active Power Instantaneous value Total"}, -{{"\x01\x00\x29\x07\x00\xFF"}, "power-l2", "L1 Active Power Instantaneous value Total"}, -{{"\x01\x00\x3D\x07\x00\xFF"}, "power-l2", "L3 Active Power Instantaneous value Total"}, -{{"\x01\x00\x60\x05\x05\xFF"}, "status", "Meter status flag"}, +{{{129, 129, 199, 130, 3, 255}}, "", ""}, // ??? +{{{ 1, 0, 1, 8, 1, 255}}, "counter-t1", "Active Power Counter Tariff 1"}, +{{{ 1, 0, 1, 8, 2, 255}}, "counter-t2", "Active Power Counter Tariff 2"}, + +/* ESYQ3D (Easymeter Q3D) */ +{{{ 0, 0, 96, 1, 255, 255}}, "device", "Complete device ID"}, +{{{ 0, 0, 0, 0, 0, 255}}, "", ""}, // ??? + {} /* stop condition for iterator */ }; -obis_id_t obis_init(unsigned char *raw) { +obis_id_t obis_init(const unsigned char *raw) { obis_id_t id; - memcpy(id.raw, raw, 6); + + if (raw == NULL) { + memset(id.raw, 0, 6); /* initialize with zeros */ + } + else { + memcpy(id.raw, raw, 6); + } + return id; } -obis_id_t obis_parse(char *str) { +obis_id_t obis_parse(const char *str) { obis_id_t id; - regex_t expr; + regex_t re; regmatch_t matches[7]; - regcomp(&expr, "^([0-9])-([a-f0-9]{,2}):([a-f0-9]{,2})\\.([a-f0-9]{,2})\\.([a-f0-9]{,2})(\\*[a-f0-9]{,2})?$", REG_EXTENDED | REG_ICASE); + regcomp(&re, "^([0-9])-([0-9]{,2}):([0-9]{,2})\\.([0-9]{,2})\\.([0-9]{,2})(\\[*&][0-9]{,2})?$", REG_EXTENDED | REG_ICASE); + // TODO make values A B C optional to allow notations like "1.8.0" - if (regexec(&expr, str, 7, matches, 0) == 0) { /* found string in OBIS notation */ + if (regexec(&re, str, 7, matches, 0) == 0) { /* found string in OBIS notation */ for (int i=0; i<6; i++) { if (matches[i+1].rm_so != -1) { - id.raw[i] = strtoul(str+matches[i+1].rm_so, NULL, 16); + id.raw[i] = strtoul(str+matches[i+1].rm_so, NULL, 10); } else { id.raw[i] = 0xff; /* default value */ @@ -75,26 +97,25 @@ obis_id_t obis_parse(char *str) { } } else { /* looking for alias */ - obis_alias_t *it = obis_aliases; - do { /* linear search */ - if (strcmp(it->name, str) == 0) { - return it->id; - } - } while ((++it)->name); + id = obis_lookup_alias(str); } + + regfree(&re); /* householding */ return id; } -char obis_is_manufacturer_specific(obis_id_t id) { - return ( - (id.groups.channel >= 128 && id.groups.channel <= 199) || - (id.groups.indicator >= 128 && id.groups.indicator <= 199) || - (id.groups.indicator == 240) || - (id.groups.mode >= 128 && id.groups.mode <= 254) || - (id.groups.quantities >= 128 && id.groups.quantities <= 254) || - (id.groups.storage >= 128 && id.groups.storage <= 254) - ); +obis_id_t obis_lookup_alias(const char *alias) { + obis_id_t nf = obis_init(NULL); /* not found */ + obis_alias_t *it = obis_aliases; + + do { /* linear search */ + if (strcmp(it->name, alias) == 0) { + return it->id; + } + } while ((++it)->name); + + return nf; } int obis_unparse(obis_id_t id, char *buffer) { @@ -108,3 +129,42 @@ int obis_unparse(obis_id_t id, char *buffer) { ); } +int obis_compare(obis_id_t a, obis_id_t b) { + for (int i = 0; i < 6; i++) { + if (a.raw[i] == b.raw[i] || a.raw[i] == 255 || b.raw[i] == 255 ) { + continue; /* skip on wildcard or equal */ + } + else if (a.raw[i] < b.raw[i]) { + return -1; + } + else if (a.raw[i] > b.raw[i]) { + return 1; + } + } + + return 0; /* equal */ +} + +int obis_is_null(obis_id_t id) { + return !( + id.raw[0] || + id.raw[1] || + id.raw[2] || + id.raw[3] || + id.raw[4] || + id.raw[5] + ); +} + +int obis_is_manufacturer_specific(obis_id_t id) { + return ( + (id.groups.channel >= 128 && id.groups.channel <= 199) || + (id.groups.indicator >= 128 && id.groups.indicator <= 199) || + (id.groups.indicator == 240) || + (id.groups.mode >= 128 && id.groups.mode <= 254) || + (id.groups.quantities >= 128 && id.groups.quantities <= 254) || + (id.groups.storage >= 128 && id.groups.storage <= 254) + ); +} + + diff --git a/src/onewire.c b/src/onewire.c index 576ce74..346e8d3 100644 --- a/src/onewire.c +++ b/src/onewire.c @@ -25,7 +25,8 @@ #include -#include "../include/onewire.h" +#include "meter.h" +#include "onewire.h" /** * Initialize sensor @@ -33,18 +34,23 @@ * @param address path to the sensor in the owfs * @return pointer to file descriptor */ -int meter_onewire_open(meter_handle_onewire_t *handle, char *options) { - handle->file = fopen(options, "r"); +int meter_open_onewire(meter_t *mtr) { + meter_handle_onewire_t *handle = &mtr->handle.onewire; + + handle->file = fopen(mtr->connection, "r"); return (handle->file == NULL) ? -1 : 0; } -void meter_onewire_close(meter_handle_onewire_t *handle) { +void meter_close_onewire(meter_t *mtr) { + meter_handle_onewire_t *handle = &mtr->handle.onewire; + fclose(handle->file); } -meter_reading_t meter_onewire_read(meter_handle_onewire_t *handle) { - meter_reading_t rd; +size_t meter_read_onewire(meter_t *mtr, reading_t rds[], size_t n) { + meter_handle_onewire_t *handle = &mtr->handle.onewire; + char buffer[16]; int bytes; @@ -54,10 +60,10 @@ meter_reading_t meter_onewire_read(meter_handle_onewire_t *handle) { buffer[bytes] = '\0'; /* zero terminated, required? */ if (bytes) { - rd.value = strtof(buffer, NULL); - gettimeofday(&rd.tv, NULL); + rds->value = strtof(buffer, NULL); + gettimeofday(&rds->time, NULL); } - } while (rd.value == 85); /* skip invalid readings */ + } while (rds->value == 85); /* skip invalid readings */ - return rd; + return 1; } diff --git a/src/random.c b/src/random.c index 72ba760..1306418 100644 --- a/src/random.c +++ b/src/random.c @@ -26,25 +26,30 @@ #include #include #include +#include -#include "../include/random.h" +#include "meter.h" +#include "random.h" -int meter_random_open(meter_handle_random_t *handle, char *options) { +int meter_open_random(meter_t *mtr) { + 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(options, NULL); + handle->max = strtof(mtr->connection, NULL); handle->last = handle->max * ((float) rand() / RAND_MAX); /* start value */ return 0; /* always succeeds */ } -void meter_random_close(meter_handle_random_t *handle) { - /* nothing todo */ +void meter_close_random(meter_t *mtr) { + //meter_handle_random_t *handle = &mtr->handle.random; } -meter_reading_t meter_random_read(meter_handle_random_t *handle) { - meter_reading_t rd; +size_t meter_read_random(meter_t *mtr, reading_t rds[], size_t n) { + meter_handle_random_t *handle = &mtr->handle.random; handle->last += ltqnorm((float) rand() / RAND_MAX); @@ -56,8 +61,8 @@ meter_reading_t meter_random_read(meter_handle_random_t *handle) { handle->last = handle->min; } - rd.value = handle->last; - gettimeofday(&rd.tv, NULL); + rds->value = handle->last; + gettimeofday(&rds->time, NULL); - return rd; + return 1; } diff --git a/src/s0.c b/src/s0.c index 0b4ed74..34c8174 100644 --- a/src/s0.c +++ b/src/s0.c @@ -29,14 +29,17 @@ #include #include -#include "../include/s0.h" +#include "meter.h" +#include "s0.h" /** * Setup serial port */ -int meter_s0_open(meter_handle_s0_t *handle, char *options) { +int meter_open_s0(meter_t *mtr) { + meter_handle_s0_t *handle = &mtr->handle.s0; + /* open port */ - handle->fd = open(options, O_RDWR | O_NOCTTY); + handle->fd = open(mtr->connection, O_RDWR | O_NOCTTY); if (handle->fd < 0) { return -1; @@ -45,7 +48,6 @@ int meter_s0_open(meter_handle_s0_t *handle, char *options) { /* save current port settings */ tcgetattr(handle->fd, &handle->oldtio); - /* configure port */ struct termios tio; memset(&tio, 0, sizeof(struct termios)); @@ -63,7 +65,9 @@ int meter_s0_open(meter_handle_s0_t *handle, char *options) { return 0; } -void meter_s0_close(meter_handle_s0_t *handle) { +void meter_close_s0(meter_t *mtr) { + meter_handle_s0_t *handle = &mtr->handle.s0; + /* reset serial port */ tcsetattr(handle->fd, TCSANOW, &handle->oldtio); @@ -71,21 +75,25 @@ void meter_s0_close(meter_handle_s0_t *handle) { close(handle->fd); } -meter_reading_t meter_s0_read(meter_handle_s0_t *handle) { +size_t meter_read_s0(meter_t *mtr, reading_t rds[], size_t n) { + meter_handle_s0_t *handle = &mtr->handle.s0; + char buf[8]; - meter_reading_t rd; - - rd.value = 1; + rds->value = 1; + /* clear input buffer */ tcflush(handle->fd, TCIOFLUSH); - read(handle->fd, buf, 8); /* blocking until one character/pulse is read */ - gettimeofday(&rd.tv, NULL); + /* blocking until one character/pulse is read */ + read(handle->fd, buf, 8); + + /* store current timestamp */ + gettimeofday(&rds->time, NULL); /* wait some ms for debouncing */ usleep(30000); - return rd; + return 1; } diff --git a/src/sml.c b/src/sml.c index 63fc80c..a78b6de 100644 --- a/src/sml.c +++ b/src/sml.c @@ -47,69 +47,120 @@ #include #include -#include "../include/sml.h" -#include "../include/obis.h" +#include "meter.h" +#include "sml.h" +#include "obis.h" + +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, ":"); -int meter_sml_open(meter_handle_sml_t *handle, char *options) { - char *node = strsep(&options, ":"); - char *service = strsep(&options, ":"); - - handle->id = obis_parse(options); handle->fd = meter_sml_open_socket(node, service); - //handle->fd = meter_sml_open_port(options); - + //handle->fd = meter_sml_open_port(args); + + free(addr); + return (handle->fd < 0) ? -1 : 0; } -void meter_sml_close(meter_handle_sml_t *handle) { +void meter_close_sml(meter_t *meter) { + meter_handle_sml_t *handle = &meter->handle.sml; + // TODO reset serial port close(handle->fd); } -meter_reading_t meter_sml_read(meter_handle_sml_t *handle) { +size_t meter_read_sml(meter_t *meter, reading_t rds[], size_t n) { + meter_handle_sml_t *handle = &meter->handle.sml; + unsigned char buffer[SML_BUFFER_LEN]; - size_t bytes; - sml_file *sml_file; - meter_reading_t rd; - + size_t bytes, m = 0; + + sml_file *file; + sml_get_list_response *body; + sml_list *entry; + /* blocking read from fd */ bytes = sml_transport_read(handle->fd, buffer, SML_BUFFER_LEN); - - /* sml parsing & stripping escape sequences */ - sml_file = sml_file_parse(buffer + 8, bytes - 16); - - /* extraction of readings */ - rd = meter_sml_parse(sml_file, handle->id); + + /* parse SML file & stripping escape sequences */ + file = sml_file_parse(buffer + 8, bytes - 16); + + /* obtain SML messagebody of type getResponseList */ + for (short i = 0; i < file->messages_len; i++) { + sml_message *message = file->messages[i]; + + if (*message->message_body->tag == SML_MESSAGE_GET_LIST_RESPONSE) { + body = (sml_get_list_response *) message->message_body->data; + entry = body->val_list; + + /* iterating through linked list */ + for (m = 0; m < n && entry != NULL; m++) { + meter_sml_parse(entry, &rds[m]); + entry = entry->next; + } + } + } /* free the malloc'd memory */ - sml_file_free(sml_file); - - return rd; + sml_file_free(file); + + return m+1; } -int meter_sml_open_socket(char *node, char *service) { +void meter_sml_parse(sml_list *entry, reading_t *rd) { + //int unit = (entry->unit) ? *entry->unit : 0; + int scaler = (entry->scaler) ? *entry->scaler : 1; + + rd->value = sml_value_to_double(entry->value) * pow(10, scaler); + rd->identifier.obis = obis_init(entry->obj_name->str); + + /* get time */ + // TODO handle SML_TIME_SEC_INDEX or time by SML File/Message + if (entry->val_time) { + rd->time.tv_sec = *entry->val_time->data.timestamp; + rd->time.tv_usec = 0; + } + else { + gettimeofday(&rd->time, NULL); + } +} + +int meter_sml_open_socket(const char *node, const char *service) { struct sockaddr_in sin; struct addrinfo *ais; - int fd, res; - + int fd, res, flags; + char byte; + getaddrinfo(node, service, NULL, &ais); memcpy(&sin, ais->ai_addr, ais->ai_addrlen); - + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) { + fprintf(stderr, "error: socket(): %s\n", strerror(errno)); + return -1; + } 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; + } - return (res < 0) ? -1 : fd; + return fd; } -int meter_sml_open_port(char *device) { +int meter_sml_open_port(const char *device) { int bits; struct termios config; memset(&config, 0, sizeof(config)); int fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); if (fd < 0) { - printf("error: open(%s): %s\n", device, strerror(errno)); + fprintf(stderr, "error: open(%s): %s\n", device, strerror(errno)); return -1; } @@ -118,7 +169,7 @@ int meter_sml_open_port(char *device) { bits |= TIOCM_RTS; ioctl(fd, TIOCMSET, &bits); - tcgetattr( fd, &config ) ; + tcgetattr(fd, &config) ; // set 8-N-1 config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); @@ -134,58 +185,3 @@ int meter_sml_open_port(char *device) { tcsetattr(fd, TCSANOW, &config); return fd; } - -meter_reading_t meter_sml_parse(sml_file *file, obis_id_t which) { - meter_reading_t rd; - - for (int i = 0; i < file->messages_len; i++) { - sml_message *message = file->messages[i]; - - if (*message->message_body->tag == SML_MESSAGE_GET_LIST_RESPONSE) { - sml_list *entry; - sml_get_list_response *body; - - body = (sml_get_list_response *) message->message_body->data; - - for (entry = body->val_list; entry != NULL; entry = entry->next) { /* linked list */ - obis_id_t id = obis_init(entry->obj_name->str); - - if (obis_compare(which, id) == 0) { - //int unit = (entry->unit) ? *entry->unit : 0; - int scaler = (entry->scaler) ? *entry->scaler : 1; - - switch (entry->value->type) { - case 0x51: rd.value = *entry->value->data.int8; break; - case 0x52: rd.value = *entry->value->data.int16; break; - case 0x54: rd.value = *entry->value->data.int32; break; - case 0x58: rd.value = *entry->value->data.int64; break; - case 0x61: rd.value = *entry->value->data.uint8; break; - case 0x62: rd.value = *entry->value->data.uint16; break; - case 0x64: rd.value = *entry->value->data.uint32; break; - case 0x68: rd.value = *entry->value->data.uint64; break; - - default: - fprintf(stderr, "Unknown value type: %x", entry->value->type); - } - - /* apply scaler */ - rd.value *= pow(10, scaler); - - - /* get time */ - if (entry->val_time) { // TODO handle SML_TIME_SEC_INDEX - rd.tv.tv_sec = *entry->val_time->data.timestamp; - rd.tv.tv_usec = 0; - } - else { - gettimeofday(&rd.tv, NULL); - } - - return rd; /* skipping rest */ - } - } - } - } - - return rd; -}