From 850784b71be5320abc2145635b7dc06613109351 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 27 May 2011 22:26:50 +0200 Subject: [PATCH] added some comments and one wire support to vzlogger --- misc/controller/vzlogger/Makefile | 8 +- misc/controller/vzlogger/src/local.c | 53 ++++---- misc/controller/vzlogger/src/main.c | 116 ++++++++++-------- misc/controller/vzlogger/src/main.h | 8 +- .../controller/vzlogger/src/protocols/1wire.c | 76 ++++++++++++ .../controller/vzlogger/src/protocols/1wire.h | 38 ++++++ misc/controller/vzlogger/src/queue.c | 4 + misc/controller/vzlogger/src/queue.h | 1 + misc/controller/vzlogger/vzlogger.conf | 4 +- 9 files changed, 224 insertions(+), 84 deletions(-) create mode 100644 misc/controller/vzlogger/src/protocols/1wire.c create mode 100644 misc/controller/vzlogger/src/protocols/1wire.h diff --git a/misc/controller/vzlogger/Makefile b/misc/controller/vzlogger/Makefile index c1c5edf..dec5045 100644 --- a/misc/controller/vzlogger/Makefile +++ b/misc/controller/vzlogger/Makefile @@ -8,8 +8,8 @@ all: $(TARGET) clean: rm -rf *.o -vzlogger: main.c api.c local.c queue.c obis.c - $(CC) $(LDFLAGS) main.o api.o local.o queue.o obis.o `curl-config --libs` -ljson -lpthread -o $(TARGET) -lmicrohttpd -lm +vzlogger: main.c api.c local.c queue.c 1wire.c obis.c + $(CC) $(LDFLAGS) main.o api.o local.o queue.o 1wire.o obis.o `curl-config --libs` -ljson -lpthread -o $(TARGET) -lmicrohttpd -lm main.c: $(CC) $(CFLAGS) src/main.c -o main.o @@ -20,9 +20,11 @@ api.c: local.c: $(CC) $(CFLAGS) src/local.c -o local.o - queue.c: $(CC) $(CFLAGS) src/queue.c -o queue.o + +1wire.c: + $(CC) $(CFLAGS) src/protocols/1wire.c -o 1wire.o obis.c: $(CC) $(CFLAGS) src/protocols/obis.c -o obis.o diff --git a/misc/controller/vzlogger/src/local.c b/misc/controller/vzlogger/src/local.c index 9b1b7e3..ce4a449 100644 --- a/misc/controller/vzlogger/src/local.c +++ b/misc/controller/vzlogger/src/local.c @@ -35,6 +35,7 @@ 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 * json_str; + const char * uuid = url + 1; int ret; int num_chans = *(int *) cls; @@ -43,43 +44,45 @@ int handle_request(void *cls, struct MHD_Connection *connection, const char *url struct MHD_Response *response; struct json_object * json_obj = json_object_new_object(); - struct json_object * json_data = json_object_new_array(); + struct json_object * json_data = json_object_new_object(); for (int i = 0; i < num_chans; i++) { channel_t *ch = &chans[i]; reading_t rd; - struct json_object * json_channel = json_object_new_object(); - struct json_object * json_tuples = json_object_new_array(); - - int j = ch->queue.write_p; - do { + if (strcmp(ch->uuid, uuid) == 0) { pthread_mutex_lock(&ch->mutex); - rd = ch->queue.buf[j]; + pthread_cond_wait(&ch->condition, &ch->mutex); /* wait for new data comet-like blocking of HTTP response */ pthread_mutex_unlock(&ch->mutex); - if (rd.value != 0) { - struct json_object * json_tuple = json_object_new_array(); - - int timestamp = rd.tv.tv_sec * 1000 + rd.tv.tv_usec / 1000; + struct json_object * json_tuples = json_object_new_array(); - json_object_array_add(json_tuple, json_object_new_int(timestamp)); - json_object_array_add(json_tuple, json_object_new_double(rd.value)); - json_object_array_add(json_tuple, json_object_new_int(1)); /* just for middleware compability */ + int j = ch->queue.write_p; + do { + pthread_mutex_lock(&ch->mutex); + rd = ch->queue.buf[j]; + pthread_mutex_unlock(&ch->mutex); - json_object_array_add(json_tuples, json_tuple); - } + if (rd.value != 0) { /* skip invalid / empty readings */ + struct json_object * json_tuple = json_object_new_array(); - j++; - j %= ch->queue.size; - } while (j != ch->queue.read_p); + int timestamp = rd.tv.tv_sec * 1000 + rd.tv.tv_usec / 1000; + + json_object_array_add(json_tuple, json_object_new_int(timestamp)); + json_object_array_add(json_tuple, json_object_new_double(rd.value)); + json_object_array_add(json_tuple, json_object_new_int(1)); /* just for middleware compability */ + + json_object_array_add(json_tuples, json_tuple); + } + + j++; + j %= ch->queue.size; + } while (j != ch->queue.read_p); - json_object_object_add(json_channel, "uuid", json_object_new_string(ch->uuid)); - json_object_object_add(json_channel, "interval", json_object_new_int(ch->interval)); - json_object_object_add(json_channel, "tuples", json_tuples); - - - json_object_array_add(json_data, json_channel); + 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)); + json_object_object_add(json_data, "tuples", json_tuples); + } } json_object_object_add(json_obj, "version", json_object_new_string(VZ_VERSION)); diff --git a/misc/controller/vzlogger/src/main.c b/misc/controller/vzlogger/src/main.c index 3aab1e3..59676e2 100644 --- a/misc/controller/vzlogger/src/main.c +++ b/misc/controller/vzlogger/src/main.c @@ -42,14 +42,23 @@ #include "local.h" #include "protocols/obis.h" - +#include "protocols/1wire.h" + +/** + * List of available protocols + * incl. function pointers + */ static protocol_t protocols[] = { - {"obis", "Plaintext OBIS", obis_get, obis_init, obis_close}, -// {"fluksousb", "FluksoUSB board", flukso_get, flukso_init, flukso_close}, -// {"onewire", "Dallas 1-Wire sensors",onewire_get, onewire_init, onewire_close}, + {"obis", "Plaintext OBIS", obis_get, obis_init, obis_close}, +// {"fluksousb", "FluksoUSB board", flukso_get, flukso_init, flukso_close}, + {"1wire", "Dallas 1-Wire sensors (via OWFS)", onewire_get, onewire_init, onewire_close}, {NULL} /* stop condition for iterator */ }; + +/** + * Command line options + */ static struct option long_options[] = { {"config", required_argument, 0, 'c'}, {"daemon", required_argument, 0, 'd'}, @@ -61,6 +70,9 @@ static struct option long_options[] = { {NULL} /* stop condition for iterator */ }; +/** + * Descriptions vor command line options + */ static char * long_options_descs[] = { "config file with channel -> uuid mapping", "run as daemon", @@ -72,9 +84,15 @@ static char * long_options_descs[] = { NULL /* stop condition for iterator */ }; -/* globals */ -options_t opts; -channel_t * chans; /* mem gets allocated in parse_channels(), and freed in main() */ +/* Global variables */ +channel_t chans[MAX_CHANNELS]; // TODO use dynamic allocation +options_t opts = { /* setting default options */ + "vzlogger.conf", /* config file */ + 8080, /* port for local interface */ + 0, /* debug level / verbosity */ + FALSE, /* daemon mode */ + FALSE /* local interface */ +}; /** * Print availble options and some other usefull information @@ -107,6 +125,9 @@ void usage(char * argv[]) { /** * Wrapper to log notices and errors + * + * @param ch could be NULL for general messages + * @todo integrate into syslog */ void print(int level, char * format, channel_t *ch, ... ) { static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; @@ -143,21 +164,12 @@ void print(int level, char * format, channel_t *ch, ... ) { /** * Parse options from command line */ -options_t parse_options(int argc, char * argv[]) { - options_t opts; - - /* setting default options */ - opts.daemon = FALSE; - opts.local = FALSE; - opts.verbose = 0; - opts.port = 8080; - opts.config = "vzlogger.conf"; - +void parse_options(int argc, char * argv[], options_t * opts) { while (TRUE) { /* getopt_long stores the option index here. */ int option_index = 0; - int c = getopt_long(argc, argv, "i:c:p:lhdv:", long_options, &option_index); + int c = getopt_long(argc, argv, "i:c:p:lhdv", long_options, &option_index); /* detect the end of the options. */ if (c == -1) @@ -165,24 +177,25 @@ options_t parse_options(int argc, char * argv[]) { switch (c) { case 'v': - opts.verbose = (optarg == NULL) ? 1 : atoi(optarg); + opts->verbose = (optarg == NULL) ? 1 : atoi(optarg); break; case 'l': - opts.local = TRUE; + opts->local = TRUE; + opts->daemon = TRUE; /* implicates daemon mode */ break; case 'd': - opts.daemon = TRUE; + opts->daemon = TRUE; break; case 'p': /* port for local interface */ - opts.port = atoi(optarg); + opts->port = atoi(optarg); break; case 'c': /* read config file */ - opts.config = (char *) malloc(strlen(optarg)+1); - strcpy(opts.config, optarg); + opts->config = (char *) malloc(strlen(optarg)+1); + strcpy(opts->config, optarg); break; case 'h': @@ -191,11 +204,9 @@ options_t parse_options(int argc, char * argv[]) { exit((c == '?') ? EXIT_FAILURE : EXIT_SUCCESS); } } - - return opts; } -channel_t * parse_channels(char * filename, int * num_chans) { +int parse_channels(char * filename, channel_t * chans) { FILE *file = fopen(filename, "r"); /* open configuration */ if (file == NULL) { @@ -206,9 +217,7 @@ channel_t * parse_channels(char * filename, int * num_chans) { char line[256]; int j = 0; - channel_t *chans = malloc(sizeof(channel_t) * MAX_CHANNELS); - - while (fgets(line, sizeof line, file) != NULL) { /* read a line */ + while (j < MAX_CHANNELS && fgets(line, sizeof line, file) != NULL) { /* read a line */ if (line[0] == '#' || line[0] == '\n') continue; /* skip comments */ channel_t ch; @@ -216,14 +225,16 @@ channel_t * parse_channels(char * filename, int * num_chans) { char *tok = strtok(line, ";"); for (int i = 0; i < 7 && tok != NULL; i++) { + size_t len = strlen(tok); + switch(i) { case 0: /* middleware */ - ch.middleware = (char *) malloc(strlen(tok)+1); + ch.middleware = (char *) malloc(len+1); /* including string termination */ strcpy(ch.middleware, tok); break; case 1: /* uuid */ - ch.uuid = (char *) malloc(strlen(tok)+1); + ch.uuid = (char *) malloc(len+1); /* including string termination */ strcpy(ch.uuid, tok); break; @@ -238,8 +249,9 @@ channel_t * parse_channels(char * filename, int * num_chans) { break; case 4: /* options */ - ch.options = (char *) malloc(strlen(tok)+1); - strcpy(ch.options, tok); + ch.options = (char *) malloc(len); + strncpy(ch.options, tok, len-1); + ch.options[len] = '\0'; /* replace \n by \0 */ break; } @@ -248,14 +260,13 @@ channel_t * parse_channels(char * filename, int * num_chans) { ch.id = j; - print(1, "Parsed (on %s)", &ch, ch.middleware); + print(1, "Parsed %s (on %s)", &ch, ch.uuid, ch.middleware); chans[j++] = ch; } fclose(file); - *num_chans = j; - return chans; + return j; } /** @@ -271,7 +282,7 @@ void *log_thread(void *arg) { print(1, "Started logging thread", ch); - while (TRUE) { + do { pthread_mutex_lock(&ch->mutex); while (queue_is_empty(&ch->queue)) { /* detect spurious wakeups */ pthread_cond_wait(&ch->condition, &ch->mutex); /* wait for new data */ @@ -291,13 +302,12 @@ void *log_thread(void *arg) { pthread_mutex_unlock(&ch->mutex); } else { - print(1, "Delaying next transmission for 15 minutes due to pervious error", ch); - + print(1, "Delaying next transmission for %i seconds due to pervious error", ch, RETRY_PAUSE); sleep(RETRY_PAUSE); } } pthread_testcancel(); /* test for cancelation request */ - } + } while (opts.daemon); return NULL; } @@ -315,7 +325,7 @@ void *read_thread(void *arg) { /* initalize channel */ ch->handle = ch->prot->init_func(ch->options); /* init sensor/meter */ - while (TRUE) { + do { reading_t rd = ch->prot->read_func(ch->handle); /* aquire reading */ pthread_mutex_lock(&ch->mutex); @@ -329,7 +339,7 @@ void *read_thread(void *arg) { pthread_testcancel(); /* test for cancelation request */ sleep(ch->interval); /* else sleep and restart aquisition */ - } + } while (opts.daemon); /* close channel */ ch->prot->close_func(ch->handle); @@ -341,30 +351,32 @@ void *read_thread(void *arg) { * The main loop */ int main(int argc, char * argv[]) { - int num_chans = 0; + int num_chans; struct MHD_Daemon * d; - opts = parse_options(argc, argv); /* parse command line arguments */ - chans = parse_channels(opts.config, &num_chans); /* parse channels from configuration */ + parse_options(argc, argv, &opts); /* parse command line arguments */ + num_chans = parse_channels(opts.config, chans); /* parse channels from configuration */ print(1, "Started %s with verbosity level %i", NULL, argv[0], opts.verbose); curl_global_init(CURL_GLOBAL_ALL); /* global intialization for all threads */ - /* start threads */ for (int i = 0; i < num_chans; i++) { channel_t * ch = &chans[i]; queue_init(&ch->queue, (BUFFER_LENGTH / ch->interval) + 1); /* initialize queue to buffer 10 minutes of data */ + /* initialize thread syncronization helpers */ pthread_mutex_init(&ch->mutex, NULL); pthread_cond_init(&ch->condition, NULL); + /* start threads */ pthread_create(&ch->reading_thread, NULL, read_thread, (void *) ch); pthread_create(&ch->logging_thread, NULL, log_thread, (void *) ch); } - if (opts.local) { /* start webserver */ + /* start webserver for local interface */ + if (opts.local) { d = MHD_start_daemon( MHD_USE_THREAD_PER_CONNECTION, opts.port, @@ -382,6 +394,12 @@ int main(int argc, char * argv[]) { pthread_join(ch->reading_thread, NULL); pthread_join(ch->logging_thread, NULL); + free(ch->middleware); + free(ch->uuid); + free(ch->options); + + queue_free(&ch->queue); + pthread_cond_destroy(&ch->condition); pthread_mutex_destroy(&ch->mutex); } @@ -390,7 +408,5 @@ int main(int argc, char * argv[]) { MHD_stop_daemon(d); } - free(chans); - return 0; } diff --git a/misc/controller/vzlogger/src/main.h b/misc/controller/vzlogger/src/main.h index 136c261..8435e3b 100644 --- a/misc/controller/vzlogger/src/main.h +++ b/misc/controller/vzlogger/src/main.h @@ -35,8 +35,8 @@ #define VZ_VERSION "0.2" #define MAX_CHANNELS 16 -#define RETRY_PAUSE 16 /* seconds to wait after failed request */ -#define BUFFER_LENGTH 25 /* in seconds */ +#define RETRY_PAUSE 600 /* seconds to wait after failed request */ +#define BUFFER_LENGTH 600 /* in seconds */ #ifndef TRUE #define TRUE 1 @@ -81,8 +81,8 @@ typedef struct { } options_t; /* Prototypes */ -options_t parse_options(int argc, char * argv[]); -channel_t * parse_channels(char * filename, int * num_chans); +void parse_options(int argc, char * argv[], options_t *opts); +int parse_channels(char * filename, channel_t *chans); void print(int level, char * format, channel_t *ch, ... ); void usage(char ** argv); diff --git a/misc/controller/vzlogger/src/protocols/1wire.c b/misc/controller/vzlogger/src/protocols/1wire.c new file mode 100644 index 0000000..c2422d6 --- /dev/null +++ b/misc/controller/vzlogger/src/protocols/1wire.c @@ -0,0 +1,76 @@ +/** + * Wrapper to read Dallas 1-wire Sensors via the 1-wire Filesystem (owfs) + * + * This is our example protocol. Use this skeleton to add your own + * protocols and meters. + * + * @package vzlogger + * @copyright Copyright (c) 2011, The volkszaehler.org project + * @license http://www.gnu.org/licenses/gpl.txt GNU Public License + * @author Steffen Vogel + * @author Mathias Dalheimer + * based heavily on libehz (https://github.com/gonium/libehz.git) + */ +/* + * This file is part of volkzaehler.org + * + * 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 "../main.h" +#include "../protocol.h" +#include "1wire.h" + +/** + * Initialize sensor + * + * @param address path to the sensor in the owfs + * @return pointer to file descriptor + */ +void * onewire_init(char * address) { + FILE * fd = fopen(address, "r"); + + if (fd == NULL) { + perror(address); + print(-1, "Failed to open sensor: %s", NULL, address); + exit(EXIT_FAILURE); + } + + return (void *) fd; +} + +void onewire_close(void *handle) { + fclose((FILE *) handle); +} + +reading_t onewire_get(void *handle) { + reading_t rd; + char buffer[16]; + int bytes; + + rewind((FILE *) handle); + bytes = fread(buffer, 1, 16, (FILE *) handle); + + if (bytes) { + print(4, "Read from sensor file: %s", NULL, buffer); + + rd.value = strtof(buffer, NULL); + gettimeofday(&rd.tv, NULL); + } + + return rd; +} diff --git a/misc/controller/vzlogger/src/protocols/1wire.h b/misc/controller/vzlogger/src/protocols/1wire.h new file mode 100644 index 0000000..d43d4e1 --- /dev/null +++ b/misc/controller/vzlogger/src/protocols/1wire.h @@ -0,0 +1,38 @@ +/** + * Wrapper to read Dallas 1-wire Sensors via the 1-wire Filesystem (owfs) + * + * This is our example protocol. Use this skeleton to add your own + * protocols and meters. + * + * @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 . + */ + +#ifndef _1WIRE_H_ +#define _1WIRE_H_ + +#include "../protocol.h" + +void * onewire_init(char * port); +void onewire_close(void *handle); +reading_t onewire_get(void *handle); + +#endif /* _1WIRE_H_ */ diff --git a/misc/controller/vzlogger/src/queue.c b/misc/controller/vzlogger/src/queue.c index 4d9a586..49fda5c 100644 --- a/misc/controller/vzlogger/src/queue.c +++ b/misc/controller/vzlogger/src/queue.c @@ -58,3 +58,7 @@ void queue_print(queue_t *q) { } printf("]\n"); } + +void queue_free(queue_t *q) { + free(q->buf); +} diff --git a/misc/controller/vzlogger/src/queue.h b/misc/controller/vzlogger/src/queue.h index 0dfa5da..585c19f 100644 --- a/misc/controller/vzlogger/src/queue.h +++ b/misc/controller/vzlogger/src/queue.h @@ -55,6 +55,7 @@ bool_t queue_deque(queue_t *q, reading_t *rd); bool_t queue_first(queue_t *q, reading_t *rd); size_t queue_size(queue_t *q); void queue_print(queue_t *q); +void queue_free(queue_t *q); #endif /* _QUEUE_H_ */ diff --git a/misc/controller/vzlogger/vzlogger.conf b/misc/controller/vzlogger/vzlogger.conf index 435feee..78f9d63 100644 --- a/misc/controller/vzlogger/vzlogger.conf +++ b/misc/controller/vzlogger/vzlogger.conf @@ -1,3 +1,3 @@ #middleware;uuid;type;interval;options -http://localhost/workspace/volkszaehler.org/htdocs/middleware;a301d8d0-903b-1234-94bb-d943d061b6a8;obis;5;/dev/ttyUSB0 -http://volkszaehler.org/demo/middleware.php;ef0e9adf-cd9e-4d9a-92c5-b4fb4c89ff98;obis;10;/dev/ttyS0 +http://localhost/workspace/volkszaehler.org/htdocs/middleware;52960fe0-8882-11e0-b356-85eba28c1922;obis;10;/mnt/1wire/10.12E6D3000800/temperature +#http://volkszaehler.org/demo/middleware.php;ef0e9adf-cd9e-4d9a-92c5-b4fb4c89ff98;obis;10;/dev/ttyS0