changed a lot.. (configuration parsing still not finished: bin/logger/src/options.c)
This commit is contained in:
parent
21344c9afa
commit
df091b1d6b
33 changed files with 1171 additions and 966 deletions
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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_ */
|
|
@ -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_ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
187
bin/logger/src/threads.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
11
include/d0.h
11
include/d0.h
|
@ -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_ */
|
||||
|
|
112
include/meter.h
112
include/meter.h
|
@ -28,16 +28,19 @@
|
|||
|
||||
/**
|
||||
* We have 2 diffrent protocol types:
|
||||
* - SENSOR: a readout is triggered in equidistant intervals by calling
|
||||
* - sensors: a readout is triggered in equidistant intervals by calling
|
||||
* the read function with an POSIX timer.
|
||||
* The interval is set in the configuration.
|
||||
* - METER: the meter itselfs triggers a readout.
|
||||
* - meters: the meter itselfs triggers a readout.
|
||||
* The pointer to the read function shoul be NULL.
|
||||
* The 'interval' column in the configuration as no meaning.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
#include "reading.h"
|
||||
#include <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_ */
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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_ */
|
||||
|
|
11
include/s0.h
11
include/s0.h
|
@ -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_ */
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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
|
||||
|
|
191
src/Makefile.in
191
src/Makefile.in
|
@ -32,8 +32,6 @@ POST_INSTALL = :
|
|||
NORMAL_UNINSTALL = :
|
||||
PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
|
||||
# SML support
|
||||
####################################################################
|
||||
|
@ -71,44 +69,33 @@ am__base_list = \
|
|||
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
|
||||
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
|
||||
am__installdirs = "$(DESTDIR)$(libdir)"
|
||||
LTLIBRARIES = $(lib_LTLIBRARIES)
|
||||
libmeter_la_LIBADD =
|
||||
am__libmeter_la_SOURCES_DIST = meter.c d0.c s0.c random.c onewire.c \
|
||||
ltqnorm.c sml.c
|
||||
@SML_SUPPORT_TRUE@am__objects_1 = sml.lo
|
||||
am_libmeter_la_OBJECTS = meter.lo d0.lo s0.lo random.lo onewire.lo \
|
||||
ltqnorm.lo $(am__objects_1)
|
||||
libmeter_la_OBJECTS = $(am_libmeter_la_OBJECTS)
|
||||
libmeter_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
|
||||
$(libmeter_la_LDFLAGS) $(LDFLAGS) -o $@
|
||||
libobis_la_LIBADD =
|
||||
am_libobis_la_OBJECTS = obis.lo
|
||||
libobis_la_OBJECTS = $(am_libobis_la_OBJECTS)
|
||||
libobis_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
|
||||
$(libobis_la_LDFLAGS) $(LDFLAGS) -o $@
|
||||
LIBRARIES = $(lib_LIBRARIES)
|
||||
AR = ar
|
||||
ARFLAGS = cru
|
||||
libmeter_a_AR = $(AR) $(ARFLAGS)
|
||||
libmeter_a_LIBADD =
|
||||
am__libmeter_a_SOURCES_DIST = meter.c d0.c s0.c random.c onewire.c \
|
||||
ltqnorm.c obis.c sml.c
|
||||
@SML_SUPPORT_TRUE@am__objects_1 = sml.$(OBJEXT)
|
||||
am_libmeter_a_OBJECTS = meter.$(OBJEXT) d0.$(OBJEXT) s0.$(OBJEXT) \
|
||||
random.$(OBJEXT) onewire.$(OBJEXT) ltqnorm.$(OBJEXT) \
|
||||
obis.$(OBJEXT) $(am__objects_1)
|
||||
libmeter_a_OBJECTS = $(am_libmeter_a_OBJECTS)
|
||||
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
|
||||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
am__mv = mv -f
|
||||
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
|
||||
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
|
||||
LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
||||
--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
|
||||
CCLD = $(CC)
|
||||
LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
||||
--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
|
||||
$(LDFLAGS) -o $@
|
||||
SOURCES = $(libmeter_la_SOURCES) $(libobis_la_SOURCES)
|
||||
DIST_SOURCES = $(am__libmeter_la_SOURCES_DIST) $(libobis_la_SOURCES)
|
||||
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
SOURCES = $(libmeter_a_SOURCES)
|
||||
DIST_SOURCES = $(am__libmeter_a_SOURCES_DIST)
|
||||
ETAGS = etags
|
||||
CTAGS = ctags
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
ACLOCAL = @ACLOCAL@
|
||||
AMTAR = @AMTAR@
|
||||
AR = @AR@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
|
@ -127,38 +114,24 @@ DEPS_SML_CFLAGS = @DEPS_SML_CFLAGS@
|
|||
DEPS_SML_LIBS = @DEPS_SML_LIBS@
|
||||
DEPS_VZ_CFLAGS = @DEPS_VZ_CFLAGS@
|
||||
DEPS_VZ_LIBS = @DEPS_VZ_LIBS@
|
||||
DLLTOOL = @DLLTOOL@
|
||||
DSYMUTIL = @DSYMUTIL@
|
||||
DUMPBIN = @DUMPBIN@
|
||||
ECHO_C = @ECHO_C@
|
||||
ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
FGREP = @FGREP@
|
||||
GREP = @GREP@
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
LD = @LD@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
LIBS = @LIBS@
|
||||
LIBTOOL = @LIBTOOL@
|
||||
LIPO = @LIPO@
|
||||
LN_S = @LN_S@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
MANIFEST_TOOL = @MANIFEST_TOOL@
|
||||
MKDIR_P = @MKDIR_P@
|
||||
NM = @NM@
|
||||
NMEDIT = @NMEDIT@
|
||||
OBJDUMP = @OBJDUMP@
|
||||
OBJEXT = @OBJEXT@
|
||||
OTOOL = @OTOOL@
|
||||
OTOOL64 = @OTOOL64@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
PACKAGE_NAME = @PACKAGE_NAME@
|
||||
|
@ -171,7 +144,6 @@ PKG_CONFIG = @PKG_CONFIG@
|
|||
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
|
||||
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
|
||||
RANLIB = @RANLIB@
|
||||
SED = @SED@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
STRIP = @STRIP@
|
||||
|
@ -180,31 +152,21 @@ abs_builddir = @abs_builddir@
|
|||
abs_srcdir = @abs_srcdir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
ac_ct_AR = @ac_ct_AR@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
am__tar = @am__tar@
|
||||
am__untar = @am__untar@
|
||||
bindir = @bindir@
|
||||
build = @build@
|
||||
build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
builddir = @builddir@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
dvidir = @dvidir@
|
||||
exec_prefix = @exec_prefix@
|
||||
host = @host@
|
||||
host_alias = @host_alias@
|
||||
host_cpu = @host_cpu@
|
||||
host_os = @host_os@
|
||||
host_vendor = @host_vendor@
|
||||
htmldir = @htmldir@
|
||||
includedir = @includedir@
|
||||
infodir = @infodir@
|
||||
|
@ -231,16 +193,14 @@ top_srcdir = @top_srcdir@
|
|||
AM_CFLAGS = -Wall -D_REENTRANT -std=gnu99 $(am__append_2)
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
AM_LDFLAGS =
|
||||
lib_LTLIBRARIES = libmeter.la libobis.la
|
||||
libmeter_la_SOURCES = meter.c d0.c s0.c random.c onewire.c ltqnorm.c \
|
||||
$(am__append_1)
|
||||
libmeter_la_LDFLAGS = -version-info $(VERSION)
|
||||
libobis_la_SOURCES = obis.c
|
||||
libobis_la_LDFLAGS = -version-info $(VERSION)
|
||||
lib_LIBRARIES = libmeter.a
|
||||
libmeter_a_SOURCES = meter.c d0.c s0.c random.c onewire.c ltqnorm.c \
|
||||
obis.c $(am__append_1)
|
||||
libmeter_a_LDFLAGS = -version-info $(VERSION)
|
||||
all: all-am
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .lo .o .obj
|
||||
.SUFFIXES: .c .o .obj
|
||||
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
|
||||
@for dep in $?; do \
|
||||
case '$(am__configure_deps)' in \
|
||||
|
@ -271,41 +231,42 @@ $(top_srcdir)/configure: $(am__configure_deps)
|
|||
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
|
||||
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
|
||||
$(am__aclocal_m4_deps):
|
||||
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
|
||||
install-libLIBRARIES: $(lib_LIBRARIES)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
|
||||
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
|
||||
@list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \
|
||||
list2=; for p in $$list; do \
|
||||
if test -f $$p; then \
|
||||
list2="$$list2 $$p"; \
|
||||
else :; fi; \
|
||||
done; \
|
||||
test -z "$$list2" || { \
|
||||
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
|
||||
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
|
||||
}
|
||||
|
||||
uninstall-libLTLIBRARIES:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
|
||||
echo " $(INSTALL_DATA) $$list2 '$(DESTDIR)$(libdir)'"; \
|
||||
$(INSTALL_DATA) $$list2 "$(DESTDIR)$(libdir)" || exit $$?; }
|
||||
@$(POST_INSTALL)
|
||||
@list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \
|
||||
for p in $$list; do \
|
||||
$(am__strip_dir) \
|
||||
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
|
||||
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
|
||||
if test -f $$p; then \
|
||||
$(am__strip_dir) \
|
||||
echo " ( cd '$(DESTDIR)$(libdir)' && $(RANLIB) $$f )"; \
|
||||
( cd "$(DESTDIR)$(libdir)" && $(RANLIB) $$f ) || exit $$?; \
|
||||
else :; fi; \
|
||||
done
|
||||
|
||||
clean-libLTLIBRARIES:
|
||||
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
|
||||
@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
|
||||
dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
|
||||
test "$$dir" != "$$p" || dir=.; \
|
||||
echo "rm -f \"$${dir}/so_locations\""; \
|
||||
rm -f "$${dir}/so_locations"; \
|
||||
done
|
||||
libmeter.la: $(libmeter_la_OBJECTS) $(libmeter_la_DEPENDENCIES)
|
||||
$(libmeter_la_LINK) -rpath $(libdir) $(libmeter_la_OBJECTS) $(libmeter_la_LIBADD) $(LIBS)
|
||||
libobis.la: $(libobis_la_OBJECTS) $(libobis_la_DEPENDENCIES)
|
||||
$(libobis_la_LINK) -rpath $(libdir) $(libobis_la_OBJECTS) $(libobis_la_LIBADD) $(LIBS)
|
||||
uninstall-libLIBRARIES:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \
|
||||
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
|
||||
test -n "$$files" || exit 0; \
|
||||
echo " ( cd '$(DESTDIR)$(libdir)' && rm -f "$$files" )"; \
|
||||
cd "$(DESTDIR)$(libdir)" && rm -f $$files
|
||||
|
||||
clean-libLIBRARIES:
|
||||
-test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES)
|
||||
libmeter.a: $(libmeter_a_OBJECTS) $(libmeter_a_DEPENDENCIES)
|
||||
-rm -f libmeter.a
|
||||
$(libmeter_a_AR) libmeter.a $(libmeter_a_OBJECTS) $(libmeter_a_LIBADD)
|
||||
$(RANLIB) libmeter.a
|
||||
|
||||
mostlyclean-compile:
|
||||
-rm -f *.$(OBJEXT)
|
||||
|
@ -313,14 +274,14 @@ mostlyclean-compile:
|
|||
distclean-compile:
|
||||
-rm -f *.tab.c
|
||||
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d0.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ltqnorm.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/meter.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/obis.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onewire.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/random.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/s0.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sml.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/d0.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ltqnorm.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/meter.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/obis.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onewire.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/random.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/s0.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sml.Po@am__quote@
|
||||
|
||||
.c.o:
|
||||
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
||||
|
@ -336,19 +297,6 @@ distclean-compile:
|
|||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
|
||||
|
||||
.c.lo:
|
||||
@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
||||
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
|
||||
|
||||
mostlyclean-libtool:
|
||||
-rm -f *.lo
|
||||
|
||||
clean-libtool:
|
||||
-rm -rf .libs _libs
|
||||
|
||||
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
unique=`for i in $$list; do \
|
||||
|
@ -433,7 +381,7 @@ distdir: $(DISTFILES)
|
|||
done
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile $(LTLIBRARIES)
|
||||
all-am: Makefile $(LIBRARIES)
|
||||
installdirs:
|
||||
for dir in "$(DESTDIR)$(libdir)"; do \
|
||||
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
|
||||
|
@ -465,8 +413,7 @@ maintainer-clean-generic:
|
|||
@echo "it deletes files that may require special tools to rebuild."
|
||||
clean: clean-am
|
||||
|
||||
clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
|
||||
mostlyclean-am
|
||||
clean-am: clean-generic clean-libLIBRARIES mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
-rm -rf ./$(DEPDIR)
|
||||
|
@ -492,7 +439,7 @@ install-dvi: install-dvi-am
|
|||
|
||||
install-dvi-am:
|
||||
|
||||
install-exec-am: install-libLTLIBRARIES
|
||||
install-exec-am: install-libLIBRARIES
|
||||
|
||||
install-html: install-html-am
|
||||
|
||||
|
@ -521,8 +468,7 @@ maintainer-clean-am: distclean-am maintainer-clean-generic
|
|||
|
||||
mostlyclean: mostlyclean-am
|
||||
|
||||
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
|
||||
mostlyclean-libtool
|
||||
mostlyclean-am: mostlyclean-compile mostlyclean-generic
|
||||
|
||||
pdf: pdf-am
|
||||
|
||||
|
@ -532,23 +478,22 @@ ps: ps-am
|
|||
|
||||
ps-am:
|
||||
|
||||
uninstall-am: uninstall-libLTLIBRARIES
|
||||
uninstall-am: uninstall-libLIBRARIES
|
||||
|
||||
.MAKE: install-am install-strip
|
||||
|
||||
.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
|
||||
clean-libLTLIBRARIES clean-libtool ctags distclean \
|
||||
distclean-compile distclean-generic distclean-libtool \
|
||||
distclean-tags distdir dvi dvi-am html html-am info info-am \
|
||||
install install-am install-data install-data-am install-dvi \
|
||||
install-dvi-am install-exec install-exec-am install-html \
|
||||
install-html-am install-info install-info-am \
|
||||
install-libLTLIBRARIES install-man install-pdf install-pdf-am \
|
||||
install-ps install-ps-am install-strip installcheck \
|
||||
installcheck-am installdirs maintainer-clean \
|
||||
clean-libLIBRARIES ctags distclean distclean-compile \
|
||||
distclean-generic distclean-tags distdir dvi dvi-am html \
|
||||
html-am info info-am install install-am install-data \
|
||||
install-data-am install-dvi install-dvi-am install-exec \
|
||||
install-exec-am install-html install-html-am install-info \
|
||||
install-info-am install-libLIBRARIES install-man install-pdf \
|
||||
install-pdf-am install-ps install-ps-am install-strip \
|
||||
installcheck installcheck-am installdirs maintainer-clean \
|
||||
maintainer-clean-generic mostlyclean mostlyclean-compile \
|
||||
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
|
||||
tags uninstall uninstall-am uninstall-libLTLIBRARIES
|
||||
mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
|
||||
uninstall-am uninstall-libLIBRARIES
|
||||
|
||||
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
|
|
24
src/d0.c
24
src/d0.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
131
src/meter.c
131
src/meter.c
|
@ -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;
|
||||
}
|
||||
|
|
136
src/obis.c
136
src/obis.c
|
@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
25
src/random.c
25
src/random.c
|
@ -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;
|
||||
}
|
||||
|
|
32
src/s0.c
32
src/s0.c
|
@ -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
170
src/sml.c
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue