From fad473e9bb5744f21394c003926e057f75621718 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Thu, 9 Jun 2011 18:29:30 +0200 Subject: [PATCH] =?UTF-8?q?added=20support=20for=20raw=20S0-Hutschienenz?= =?UTF-8?q?=C3=A4hler=20on=20RS232=20port?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- misc/controller/vzlogger/src/api.c | 48 ++++++------ misc/controller/vzlogger/src/local.c | 25 +------ misc/controller/vzlogger/src/main.c | 29 ++++--- misc/controller/vzlogger/src/protocol.h | 7 ++ .../controller/vzlogger/src/protocols/rawS0.c | 75 +++++++++++++------ .../controller/vzlogger/src/protocols/rawS0.h | 5 +- misc/controller/vzlogger/src/queue.c | 51 ++++--------- misc/controller/vzlogger/src/queue.h | 13 ++-- misc/controller/vzlogger/vzlogger.conf | 3 +- 9 files changed, 130 insertions(+), 126 deletions(-) diff --git a/misc/controller/vzlogger/src/api.c b/misc/controller/vzlogger/src/api.c index 1420b3d..93da881 100644 --- a/misc/controller/vzlogger/src/api.c +++ b/misc/controller/vzlogger/src/api.c @@ -83,26 +83,31 @@ size_t curl_custom_write_callback(void *ptr, size_t size, size_t nmemb, void *da return realsize; } -json_object * api_build_json(channel_t *ch) { +json_object * api_json_tuples(channel_t *ch, bool_t all) { reading_t rd; - json_object *json_obj = json_object_new_object(); json_object *json_tuples = json_object_new_array(); - for (int j = 0; j < ch->queue.size; j++) { - queue_deque(&ch->queue, &rd); + int index = ch->queue.read_p; + do { + pthread_mutex_lock(&ch->mutex); + rd = ch->queue.buf[index]; + pthread_mutex_unlock(&ch->mutex); - if (rd.tv.tv_sec) { /* skip empty readings */ - json_object *json_tuple = json_object_new_array(); - json_object_array_add(json_tuple, json_object_new_int(rd.tv.tv_sec * 1000 + rd.tv.tv_usec / 1000)); - json_object_array_add(json_tuple, json_object_new_double(rd.value)); - json_object_array_add(json_tuples, json_tuple); - } - } + struct json_object *json_tuple = json_object_new_array(); + + 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_tuples, json_tuple); + + index++; + index %= ch->queue.size; + } while (index != (all) ? ch->queue.read_p : ch->queue.write_p); - json_object_object_add(json_obj, "tuples", json_tuples); - - return json_obj; + return json_tuples; } CURL * api_curl_init(channel_t *ch) { @@ -133,8 +138,8 @@ CURL * api_curl_init(channel_t *ch) { } void api_parse_exception(CURLresponse response, char *err) { - struct json_tokener * json_tok; - struct json_object * json_obj; + struct json_tokener *json_tok; + struct json_object *json_obj; json_tok = json_tokener_new(); json_obj = json_tokener_parse_ex(json_tok, response.data, response.size); @@ -188,9 +193,7 @@ void *api_thread(void *arg) { } pthread_mutex_unlock(&ch->mutex); - pthread_mutex_lock(&ch->mutex); - json_str = json_object_to_json_string(api_build_json(ch)); - pthread_mutex_unlock(&ch->mutex); + json_str = json_object_to_json_string(api_json_tuples(ch, FALSE)); print(1, "JSON request body: %s", ch, json_str); @@ -205,7 +208,7 @@ void *api_thread(void *arg) { if (curl_code == CURLE_OK && http_code == 200) { /* everything is ok */ print(1, "Request succeeded with code: %i", ch, http_code); - // TODO clear queue + queue_clear(&ch->queue); } else { /* error */ if (curl_code != CURLE_OK) { @@ -213,7 +216,7 @@ void *api_thread(void *arg) { } else if (http_code != 200) { char err[255]; - api_parse_exception(response, &err); + api_parse_exception(response, err); print(-1, "Invalid middlware response: %s", ch, err); } @@ -224,8 +227,7 @@ void *api_thread(void *arg) { /* householding */ free(json_str); // TODO free json objects - - if (response.data) free(response.data); + free(response.data); pthread_testcancel(); /* test for cancelation request */ } while (opts.daemon); diff --git a/misc/controller/vzlogger/src/local.c b/misc/controller/vzlogger/src/local.c index a7dab34..51d8f3a 100644 --- a/misc/controller/vzlogger/src/local.c +++ b/misc/controller/vzlogger/src/local.c @@ -43,8 +43,8 @@ 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_object(); + struct json_object *json_obj = json_object_new_object(); + struct json_object *json_data = json_object_new_object(); for (int i = 0; i < num_chans; i++) { channel_t *ch = &chans[i]; @@ -56,25 +56,8 @@ int handle_request(void *cls, struct MHD_Connection *connection, const char *url pthread_cond_wait(&ch->condition, &ch->mutex); // TODO use pthread_cond_timedwait() pthread_mutex_unlock(&ch->mutex); - struct json_object * json_tuples = json_object_new_array(); - - for (int j = 0; j < ch->queue.size; j++) { - pthread_mutex_lock(&ch->mutex); - queue_deque(&ch->queue, &rd); - pthread_mutex_unlock(&ch->mutex); - - if (rd.value != 0) { /* skip invalid / empty readings */ - struct json_object * json_tuple = json_object_new_array(); - - 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_tuples, json_tuple); - } - } - + struct json_object *json_tuples = api_json_tuples(ch, TRUE); + 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); diff --git a/misc/controller/vzlogger/src/main.c b/misc/controller/vzlogger/src/main.c index a41a09f..974a32d 100644 --- a/misc/controller/vzlogger/src/main.c +++ b/misc/controller/vzlogger/src/main.c @@ -40,15 +40,17 @@ #include "protocols/obis.h" #include "protocols/1wire.h" +#include "protocols/rawS0.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}, - {"1wire", "Dallas 1-Wire sensors (via OWFS)", onewire_get, onewire_init, onewire_close}, + {"obis", "Plaintext OBIS", obis_get, obis_init, obis_close, MODE_SENSOR}, +// {"fluksousb", "FluksoUSB board", flukso_get, flukso_init, flukso_close, MODE_SENSOR}, + {"rawS0", "S0 on RS232", rawS0_get, rawS0_init, rawS0_close, MODE_METER}, + {"1wire", "Dallas 1-Wire sensors (via OWFS)", onewire_get, onewire_init, onewire_close, MODE_SENSOR}, {NULL} /* stop condition for iterator */ }; @@ -179,7 +181,6 @@ void parse_options(int argc, char * argv[], options_t * opts) { case 'l': opts->local = TRUE; - opts->daemon = TRUE; /* implicates daemon mode */ break; case 'd': @@ -310,16 +311,20 @@ void *read_thread(void *arg) { reading_t rd = ch->prot->read_func(ch->handle); /* aquire reading */ pthread_mutex_lock(&ch->mutex); - queue_enque(&ch->queue, rd); - pthread_cond_broadcast(&ch->condition); + queue_push(&ch->queue, rd); + pthread_cond_broadcast(&ch->condition); /* notify webserver and logging thread */ pthread_mutex_unlock(&ch->mutex); - print(1, "Value read: %.3f (next reading in %i secs)", ch, rd.value, ch->interval); - //if (opts.verbose > 5) queue_print(&ch->queue); /* Debugging */ + print(1, "Value read: %.1f", ch, rd.value); + if (opts.verbose > 5) queue_print(&ch->queue); /* Debugging */ + + if (ch->prot->mode != MODE_METER) { /* for meters, the read_func call is blocking */ + print(5, "Next reading in %i seconds", ch, ch->interval); + sleep(ch->interval); /* else sleep and restart aquisition */ + } pthread_testcancel(); /* test for cancelation request */ - sleep(ch->interval); /* else sleep and restart aquisition */ - } while (opts.daemon); + } while (opts.daemon || opts.local); /* close channel */ ch->prot->close_func(ch->handle); @@ -374,11 +379,11 @@ int main(int argc, char * argv[]) { pthread_join(ch->reading_thread, NULL); pthread_join(ch->logging_thread, NULL); - /*free(ch->middleware); + free(ch->middleware); free(ch->uuid); free(ch->options); - queue_free(&ch->queue);*/ + queue_free(&ch->queue); pthread_cond_destroy(&ch->condition); pthread_mutex_destroy(&ch->mutex); diff --git a/misc/controller/vzlogger/src/protocol.h b/misc/controller/vzlogger/src/protocol.h index a51bfdc..b59fa6f 100644 --- a/misc/controller/vzlogger/src/protocol.h +++ b/misc/controller/vzlogger/src/protocol.h @@ -12,12 +12,19 @@ typedef void *(*ifp_t)(char *options); typedef void (*cfp_t)(void *handle); typedef reading_t (*rfp_t)(void *handle); +typedef enum { + MODE_METER, + MODE_SENSOR +} mode_t; + typedef struct { char * name; /* short identifier for protocol */ char * desc; /* more detailed description */ rfp_t read_func; /* function pointer to read data */ ifp_t init_func; /* function pointer to init a channel */ cfp_t close_func; /* function pointer to close a channel */ + mode_t mode; /* should we wait for next pulse? */ + } protocol_t; #endif /* _PROTOCOL_H_ */ diff --git a/misc/controller/vzlogger/src/protocols/rawS0.c b/misc/controller/vzlogger/src/protocols/rawS0.c index 2553b30..71e6331 100644 --- a/misc/controller/vzlogger/src/protocols/rawS0.c +++ b/misc/controller/vzlogger/src/protocols/rawS0.c @@ -1,7 +1,5 @@ /** - * OBIS protocol parser - * - * + * S0 Hutschienenzähler directly connected to an rs232 port * * @package vzlogger * @copyright Copyright (c) 2011, The volkszaehler.org project @@ -27,7 +25,9 @@ #include #include - +#include +#include +#include #include "../main.h" #include "../protocol.h" @@ -35,42 +35,71 @@ typedef struct { int fd; /* file descriptor of port */ - struct termios old_tio; - struct termios tio; + struct termios oldtio; /* required to reset port */ } rawS0_state_t; +/** + * Setup serial port + */ void * rawS0_init(char * port) { - struct termios tio; - int *fd = malloc(sizeof(int)); + rawS0_state_t *state; + struct termios newtio; - memset(&tio, 0, sizeof(tio)); + /* initialize handle */ + state = malloc(sizeof(rawS0_state_t)); - tio.c_iflag = 0; - tio.c_oflag = 0; - tio.c_cflag = CS7|CREAD|CLOCAL; // 7n1, see termios.h for more information - tio.c_lflag = 0; - tio.c_cc[VMIN] = 1; - tio.c_cc[VTIME] = 5; + state->fd = open(port, O_RDWR | O_NOCTTY); + if (state->fd < 0) { + char err[255]; + strerror_r(errno, err, 255); + print(-1, "%s: %s", NULL, port, err); + exit(EXIT_FAILURE); + } + + tcgetattr(state->fd, &state->oldtio); /* save current port settings */ - *fd = open(port, O_RDWR | O_NONBLOCK); - cfsetospeed(&tio, B9600); // 9600 baud - cfsetispeed(&tio, B9600); // 9600 baud + + /* configure port */ + memset(&newtio, 0, sizeof(struct termios)); - return (void *) fd; + newtio.c_cflag = B300 | CS8 | CLOCAL | CREAD; + newtio.c_iflag = IGNPAR; + newtio.c_oflag = 0; + newtio.c_lflag = 0; /* set input mode (non-canonical, no echo,...) */ + newtio.c_cc[VTIME] = 0; /* inter-character timer unused */ + newtio.c_cc[VMIN] = 1; /* blocking read until data is received */ + + /* apply configuration */ + tcsetattr(state->fd, TCSANOW, &newtio); + + return (void *) state; } -void obis_close(void *handle) { - // TODO close serial port +void rawS0_close(void *handle) { + rawS0_state_t *state = (rawS0_state_t *) handle; + tcsetattr(state->fd, TCSANOW, &state->oldtio); + + close(state->fd); free(handle); } -reading_t obis_get(void *handle) { +reading_t rawS0_get(void *handle) { + char buf[255]; + + rawS0_state_t *state = (rawS0_state_t *) handle; reading_t rd; - read(); + rd.value = 1; + + tcflush(state->fd, TCIOFLUSH); + + read(state->fd, buf, 255); /* blocking until one character/pulse is read */ gettimeofday(&rd.tv, NULL); + /* wait some ms for debouncing */ + usleep(30000); + return rd; } diff --git a/misc/controller/vzlogger/src/protocols/rawS0.h b/misc/controller/vzlogger/src/protocols/rawS0.h index f3fe860..c265d50 100644 --- a/misc/controller/vzlogger/src/protocols/rawS0.h +++ b/misc/controller/vzlogger/src/protocols/rawS0.h @@ -1,8 +1,5 @@ /** - * OBIS protocol parser - * - * This is our example protocol. Use this skeleton to add your own - * protocols and meters. + * S0 Hutschienenzähler directly connected to an rs232 port * * @package vzlogger * @copyright Copyright (c) 2011, The volkszaehler.org project diff --git a/misc/controller/vzlogger/src/queue.c b/misc/controller/vzlogger/src/queue.c index 1a4feab..e7af0ec 100644 --- a/misc/controller/vzlogger/src/queue.c +++ b/misc/controller/vzlogger/src/queue.c @@ -3,62 +3,43 @@ #include "queue.h" -bool_t queue_init(queue_t *q, size_t size) { +queue_t * queue_init(queue_t *q, size_t size) { q->buf = malloc(sizeof(reading_t) * size); /* keep one slot open */ if (q->buf) { q->size = size; q->read_p = q->write_p = 0; /* queue is empty */ - return TRUE; + return q; } - else { - return FALSE; /* cannot allocate memory */ + else { /* cannot allocat memory */ + return NULL; } } -bool_t queue_is_full(queue_t *q) { - return (((q->write_p + 1) % q->size) == q->read_p); -} - bool_t queue_is_empty(queue_t *q) { return (q->read_p == q->write_p); } - -bool_t queue_enque(queue_t *q, reading_t rd) { + +void queue_free(queue_t *q) { + queue_clear(q); + free(q->buf); +} + +void queue_clear(queue_t *q) { + q->read_p = q->write_p; +} + +void queue_push(queue_t *q, reading_t rd) { q->buf[q->write_p] = rd; q->write_p++; q->write_p %= q->size; - - return !queue_is_full(q); } -bool_t queue_deque(queue_t *q, reading_t *rd) { - *rd = q->buf[q->read_p]; - q->read_p++; - q->read_p %= q->size; - - return !queue_is_empty(q); -} - -size_t queue_size(queue_t *q) { - return q->write_p - q->read_p + (q->read_p > q->write_p) * q->size; -} - -bool_t queue_first(queue_t *q, reading_t *rd) { - *rd = q->buf[q->read_p]; - - return !queue_is_empty(q); -} - void queue_print(queue_t *q) { printf("Queue dump: [%.1f", q->buf[0].value); for (int i = 1; i < q->size; i++) { printf("|%.2f", q->buf[i].value); } - printf("]\n"); -} - -void queue_free(queue_t *q) { - free(q->buf); + printf("] write_p = %i\t read_p = %i\n", q->write_p, q->read_p); } diff --git a/misc/controller/vzlogger/src/queue.h b/misc/controller/vzlogger/src/queue.h index 585c19f..e790f66 100644 --- a/misc/controller/vzlogger/src/queue.h +++ b/misc/controller/vzlogger/src/queue.h @@ -43,19 +43,18 @@ typedef struct { int read_p; int write_p; + int fill_count; reading_t *buf; } queue_t; -bool_t queue_init(queue_t *q, size_t size); -bool_t queue_is_full(queue_t *q); +queue_t * queue_init(queue_t *q, size_t size); bool_t queue_is_empty(queue_t *q); -bool_t queue_enque(queue_t *q, reading_t rd); -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_push(queue_t *q, reading_t rd); +void queue_clear(queue_t *q); void queue_free(queue_t *q); +void queue_print(queue_t *q); + #endif /* _QUEUE_H_ */ diff --git a/misc/controller/vzlogger/vzlogger.conf b/misc/controller/vzlogger/vzlogger.conf index 614e829..6090c29 100644 --- a/misc/controller/vzlogger/vzlogger.conf +++ b/misc/controller/vzlogger/vzlogger.conf @@ -5,4 +5,5 @@ ;prot intval uuid middleware options ;1wire 3 52960fe0-8882-11e0-b356-85eba28c1922 http://localhost/workspace/volkszaehler.org/htdocs/middleware /mnt/1wire/10.12E6D3000800/temperature ;obis 10 ef0e9adf-cd9e-4d9a-92c5-b4fb4c89ff98 http://volkszaehler.org/demo/middleware.php /dev/ttyS0 -obis 10 ef0e9adf-cd9e-4d9a-92c5-b4fb4c89ff98 http://volksxzaehler.org/demo/middleware.php /dev/ttyS1 +rawS0 10 27a1b4c0-8f8a-11e0-ad82-db6efbc4ba2e http://volkszaehler.org/demo/middleware.php /dev/ttyUSB2 +