changed a lot.. (configuration parsing still not finished: bin/logger/src/options.c)

This commit is contained in:
Steffen Vogel 2011-10-07 22:26:03 +02:00
parent 21344c9afa
commit df091b1d6b
33 changed files with 1171 additions and 966 deletions

View file

@ -32,18 +32,35 @@
#include <sys/time.h>
#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_ */

View file

@ -31,12 +31,12 @@
#include <pthread.h>
#include <sys/time.h>
#include <reading.h>
#include <meter.h>
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);

View file

@ -27,34 +27,27 @@
#define _CHANNEL_H_
#include <pthread.h>
#include <signal.h>
#include <meter.h>
#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_ */

View file

@ -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 <stdlib.h>
#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_ */

View file

@ -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_ */

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#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_ */

View file

@ -26,20 +26,37 @@
#ifndef _VZLOGGER_H_
#define _VZLOGGER_H_
#include "../../config.h"
#include <pthread.h>
#include <meter.h>
#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_ */

View file

@ -28,20 +28,15 @@
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <reading.h>
#include <meter.h>
#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;
}

View file

@ -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;
}

View file

@ -26,82 +26,22 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#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);
}

View file

@ -29,57 +29,65 @@
#include <time.h>
#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;
}

View file

@ -31,16 +31,15 @@
#include <getopt.h>
#include <regex.h>
#include "../../config.h"
#include <meter.h>
#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);
}

187
bin/logger/src/threads.c Normal file
View file

@ -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 <info@steffenvogel.de>
*/
/*
* This file is part of volkzaehler.org
*
* volkzaehler.org is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* volkzaehler.org is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
*/
#include <math.h>
#include <unistd.h>
#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;
}

View file

@ -26,27 +26,27 @@
#include <stdio.h> /* for print() */
#include <stdarg.h>
#include <math.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <getopt.h>
#include <curl/curl.h>
#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 <microhttpd.h>
#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;
}

View file

@ -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

View file

@ -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.

View file

@ -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);

View file

@ -31,15 +31,16 @@
#include <termios.h>
#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_ */

View file

@ -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 <sys/socket.h>
#include <sys/time.h>
#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_ */

View file

@ -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_ */

View file

@ -28,14 +28,15 @@
#include <stdio.h>
#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_ */

View file

@ -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_ */

View file

@ -28,15 +28,16 @@
#include <termios.h>
#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_ */

View file

@ -33,24 +33,78 @@
#define SML_BUFFER_LEN 8096
#include <sml/sml_file.h>
#include <sml/sml_value.h>
#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_ */

View file

@ -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

View file

@ -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.

View file

@ -35,14 +35,17 @@
#include <fcntl.h>
#include <unistd.h>
#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;
}

View file

@ -25,102 +25,79 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#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;
}

View file

@ -28,46 +28,68 @@
#include <string.h>
#include <regex.h>
#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)
);
}

View file

@ -25,7 +25,8 @@
#include <stdlib.h>
#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;
}

View file

@ -26,25 +26,30 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#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;
}

View file

@ -29,14 +29,17 @@
#include <string.h>
#include <errno.h>
#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;
}

170
src/sml.c
View file

@ -47,69 +47,120 @@
#include <sml/sml_file.h>
#include <sml/sml_transport.h>
#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;
}