vzlogger is proceeding..
This commit is contained in:
parent
672bff6efa
commit
282c2c309a
5 changed files with 305 additions and 90 deletions
|
@ -8,11 +8,11 @@ all: $(TARGET)
|
|||
clean:
|
||||
rm -rf *.o
|
||||
|
||||
vzlogger: main.c ehz.c
|
||||
$(CC) $(LDFLAGS) main.o ehz.o `curl-config --libs` -o $(TARGET)
|
||||
vzlogger: main.c obis.c
|
||||
$(CC) $(LDFLAGS) main.o obis.o `curl-config --libs` -l json -o $(TARGET)
|
||||
|
||||
main.c:
|
||||
$(CC) $(CFLAGS) src/main.c -o main.o `curl-config --cflags`
|
||||
|
||||
ehz.c:
|
||||
$(CC) $(CFLAGS) src/ehz.c -o ehz.o
|
||||
obis.c:
|
||||
$(CC) $(CFLAGS) src/protocols/obis.c -o obis.o
|
||||
|
|
|
@ -34,27 +34,28 @@
|
|||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <curl/types.h>
|
||||
#include <curl/easy.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "ehz.h"
|
||||
|
||||
struct options opt;
|
||||
#include "protocols/obis.h"
|
||||
|
||||
static struct device devices[] = {
|
||||
// {"1wire", "Dallas 1-Wire Sensors", 1wire_get},
|
||||
// {"ccost", "CurrentCost", ccost_get},
|
||||
// {"fusb", "FluksoUSB prototype board", fusb_get}
|
||||
{"ehz", "German \"elektronische Heimzähler\"", ehz_get},
|
||||
// {"fusb", "FluksoUSB prototype board", fusb_get},
|
||||
{"obis", "Plaintext OBIS", obis_get},
|
||||
{NULL} /* stop condition for iterator */
|
||||
};
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"middleware", required_argument, 0, 'm'},
|
||||
{"uuid", required_argument, 0, 'u'},
|
||||
{"value", required_argument, 0, 'v'},
|
||||
{"value", required_argument, 0, 'V'},
|
||||
{"device", required_argument, 0, 'd'},
|
||||
{"port", required_argument, 0, 'p'},
|
||||
// {"config", required_argument, 0, 'c'},
|
||||
|
@ -83,6 +84,12 @@ static char * long_options_descs[] = {
|
|||
NULL /* stop condition for iterator */
|
||||
};
|
||||
|
||||
/* globals */
|
||||
struct options opts;
|
||||
|
||||
/**
|
||||
* Print availble options and some other usefull information
|
||||
*/
|
||||
void usage(char * argv[]) {
|
||||
char ** desc = long_options_descs;
|
||||
struct option * op = long_options;
|
||||
|
@ -106,63 +113,25 @@ void usage(char * argv[]) {
|
|||
}
|
||||
|
||||
printf("\nvzlogger - volkszaehler.org logging utility VERSION\n");
|
||||
printf("by Steffen Vogel <volkszaehler@steffenvogel.de>\n");
|
||||
printf("by Steffen Vogel <stv0g@0l.de>\n");
|
||||
}
|
||||
|
||||
CURLcode backend_log(char * middleware, char * uuid, struct timeval tv, float value) {
|
||||
CURL *curl;
|
||||
CURLcode res;
|
||||
|
||||
char url[255], useragent[255], post[255];
|
||||
|
||||
sprintf(url, "%s/data/%s.json", middleware, uuid); /* build url */
|
||||
sprintf(useragent, "vzlogger/%s (%s)", VZ_VERSION, curl_version());
|
||||
sprintf(post, "?timestamp=%lu%lu&value=%f", tv.tv_sec, tv.tv_usec, value);
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
curl_formadd(&formpost,
|
||||
&lastptr,
|
||||
CURLFORM_COPYNAME, "value",
|
||||
CURLFORM_PTRCONTENTS , value_str,
|
||||
CURLFORM_END);
|
||||
|
||||
curl_formadd(&formpost,
|
||||
&lastptr,
|
||||
CURLFORM_COPYNAME, "timestamp",
|
||||
CURLFORM_PTRCONTENTS , ×tamp,
|
||||
CURLFORM_END);
|
||||
|
||||
curl = curl_easy_init();
|
||||
/**
|
||||
* Parse options from command line
|
||||
*/
|
||||
struct options parse_options(int argc, char * argv[]) {
|
||||
struct options opts;
|
||||
|
||||
if (curl) {
|
||||
/* what URL that receives this POST */
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, useragent);
|
||||
curl_easy_setopt(curl, CURLOPT_VERBOSE, (int) opt.verbose);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
curl_easy_cleanup(curl); /* always cleanup */
|
||||
curl_formfree(formpost); /* then cleanup the formpost chain */
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
/* setting default options */
|
||||
opt.interval = 300; /* 5 minutes */
|
||||
opts.interval = 300;
|
||||
opts.verbose = 0;
|
||||
opts.daemon = 0;
|
||||
|
||||
/* parse cli arguments */
|
||||
while (1) {
|
||||
while (1) {
|
||||
/* getopt_long stores the option index here. */
|
||||
int option_index = 0;
|
||||
|
||||
int c = getopt_long(argc, argv, "i:m:u:v:t:p:c:hdv", long_options, &option_index);
|
||||
int c = getopt_long(argc, argv, "i:m:u:V:t:p:c:hdv", long_options, &option_index);
|
||||
|
||||
/* detect the end of the options. */
|
||||
if (c == -1)
|
||||
|
@ -170,64 +139,199 @@ int main(int argc, char * argv[]) {
|
|||
|
||||
switch (c) {
|
||||
case 'v':
|
||||
opt.verbose = 1;
|
||||
opts.verbose = 1;
|
||||
break;
|
||||
|
||||
|
||||
case 'd':
|
||||
opt.daemon = 1;
|
||||
opts.daemon = 1;
|
||||
break;
|
||||
|
||||
|
||||
case 'i':
|
||||
opt.interval = atoi(optarg);
|
||||
opts.interval = atoi(optarg);
|
||||
break;
|
||||
|
||||
|
||||
case 'u':
|
||||
opt.uuid = (char *) malloc(strlen(optarg)+1);
|
||||
strcpy(opt.uuid, optarg);
|
||||
opts.uuid = (char *) malloc(strlen(optarg)+1);
|
||||
strcpy(opts.uuid, optarg);
|
||||
break;
|
||||
|
||||
|
||||
case 'm':
|
||||
opt.middleware = (char *) malloc(strlen(optarg)+1);
|
||||
strcpy(opt.middleware, optarg);
|
||||
opts.middleware = (char *) malloc(strlen(optarg)+1);
|
||||
strcpy(opts.middleware, optarg);
|
||||
break;
|
||||
|
||||
|
||||
case 'p':
|
||||
opt.port = (char *) malloc(strlen(optarg)+1);
|
||||
strcpy(opt.port, optarg);
|
||||
opts.port = (char *) malloc(strlen(optarg)+1);
|
||||
strcpy(opts.port, optarg);
|
||||
break;
|
||||
|
||||
//case 'c': /* read config file */
|
||||
|
||||
// break;
|
||||
|
||||
//case 'c': /* read config file */
|
||||
// break;
|
||||
|
||||
case 'h':
|
||||
case '?':
|
||||
usage(argv);
|
||||
exit((c == '?') ? -1 : 0);
|
||||
exit((c == '?') ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
/* setup devices */
|
||||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reformat CURLs debugging output
|
||||
*/
|
||||
int curl_custom_debug_callback(CURL *curl, curl_infotype type, char *data, size_t size, void *custom) {
|
||||
switch (type) {
|
||||
case CURLINFO_TEXT:
|
||||
case CURLINFO_END:
|
||||
printf("%.*s", (int) size, data);
|
||||
break;
|
||||
|
||||
case CURLINFO_HEADER_IN:
|
||||
case CURLINFO_HEADER_OUT:
|
||||
//printf("header in: %.*s", size, data);
|
||||
break;
|
||||
|
||||
case CURLINFO_SSL_DATA_IN:
|
||||
case CURLINFO_DATA_IN:
|
||||
printf("Received %lu bytes\n", (unsigned long) size);
|
||||
break;
|
||||
|
||||
case CURLINFO_SSL_DATA_OUT:
|
||||
case CURLINFO_DATA_OUT:
|
||||
printf("Sent %lu bytes.. ", (unsigned long) size);
|
||||
break;
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t curl_custom_write_callback(void *ptr, size_t size, size_t nmemb, void *data) {
|
||||
size_t realsize = size * nmemb;
|
||||
struct curl_response *response = (struct curl_response *) data;
|
||||
|
||||
response->data = realloc(response->data, response->size + realsize + 1);
|
||||
if (response->data == NULL) { /* out of memory! */
|
||||
fprintf(stderr, "Not enough memory (realloc returned NULL)\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memcpy(&(response->data[response->size]), ptr, realsize);
|
||||
response->size += realsize;
|
||||
response->data[response->size] = 0;
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log against the vz.org middleware with simple HTTP requests via CURL
|
||||
*/
|
||||
CURLcode api_log(char * middleware, char * uuid, struct reading read) {
|
||||
CURL *curl;
|
||||
CURLcode rc = -1;
|
||||
|
||||
char url[255], useragent[255], post[255];
|
||||
int curl_code;
|
||||
struct curl_response chunk = {NULL, 0};
|
||||
|
||||
sprintf(url, "%s/data/%s.json", middleware, uuid); /* build url */
|
||||
sprintf(useragent, "vzlogger/%s (%s)", VZ_VERSION, curl_version());
|
||||
sprintf(post, "?timestamp=%lu%lu&value=%f", read.tv.tv_sec, read.tv.tv_usec, read.value);
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
curl = curl_easy_init();
|
||||
|
||||
if (curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, useragent);
|
||||
curl_easy_setopt(curl, CURLOPT_VERBOSE, (int) opts.verbose);
|
||||
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_custom_debug_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_custom_write_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &chunk);
|
||||
|
||||
if (opts.verbose) printf("Sending request: %s%s\n", url, post);
|
||||
|
||||
rc = curl_easy_perform(curl);
|
||||
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &curl_code);
|
||||
|
||||
if (opts.verbose) printf("Request %s with code: %i\n", (curl_code == 200) ? "succeded" : "failed", curl_code);
|
||||
|
||||
if (rc != CURLE_OK) {
|
||||
fprintf(stderr, "CURL error: %s\n", curl_easy_strerror(rc));
|
||||
}
|
||||
else if (chunk.size == 0 || chunk.data == NULL) {
|
||||
fprintf(stderr, "No data received!\n");
|
||||
rc = -1;
|
||||
}
|
||||
else if (curl_code != 200) { /* parse exception */
|
||||
struct json_tokener * json_tok;
|
||||
struct json_object * json_obj;
|
||||
|
||||
json_tok = json_tokener_new();
|
||||
json_obj = json_tokener_parse_ex(json_tok, chunk.data, chunk.size);
|
||||
|
||||
if (json_tok->err == json_tokener_success) {
|
||||
json_obj = json_object_object_get(json_obj, "exception");
|
||||
|
||||
if (json_obj) {
|
||||
fprintf(stderr, "%s [%i]: %s\n",
|
||||
json_object_get_string(json_object_object_get(json_obj, "type")),
|
||||
json_object_get_int(json_object_object_get(json_obj, "code")),
|
||||
json_object_get_string(json_object_object_get(json_obj, "message"))
|
||||
);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Malformed middleware response: missing exception\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Malformed middleware response: %s\n", json_tokener_errors[json_tok->err]);
|
||||
}
|
||||
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl); /* always cleanup */
|
||||
free(chunk.data); /* free response */
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Failed to create CURL handle\n");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main loop
|
||||
*/
|
||||
int main(int argc, char * argv[]) {
|
||||
opts = parse_options(argc, argv); /* parse command line arguments */
|
||||
|
||||
struct reading rd;
|
||||
|
||||
log: /* start logging */
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
rd.value = 33.333;
|
||||
gettimeofday(&rd.tv, NULL);
|
||||
|
||||
CURLcode rc = backend_log(opt.middleware, opt.uuid, tv, 33.333);
|
||||
CURLcode rc = api_log(opts.middleware, opts.uuid, rd);
|
||||
|
||||
if (rc != CURLE_OK) {
|
||||
fprintf(stderr, "curl error: %s\n", curl_easy_strerror(rc));
|
||||
} else if (opt.verbose) {
|
||||
fprintf(stdout, "logging %s against %s with value %f", opt.uuid, opt.middleware, 33.333);
|
||||
if (opts.verbose) printf("Delaying next transmission for 15 minutes due to pervious error\n");
|
||||
sleep(15*60);
|
||||
}
|
||||
|
||||
if (opt.daemon) {
|
||||
sleep(opt.interval);
|
||||
if (opts.daemon) {
|
||||
if (opts.verbose) printf("Sleeping %i seconds for next transmission\n", opts.interval);
|
||||
sleep(opts.interval);
|
||||
goto log;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -26,7 +26,17 @@
|
|||
#ifndef _MAIN_H_
|
||||
#define _MAIN_H_
|
||||
|
||||
typedef float (*rfp)();
|
||||
typedef struct reading (*rfp)();
|
||||
|
||||
struct curl_response {
|
||||
char *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct reading {
|
||||
float value;
|
||||
struct timeval tv;
|
||||
};
|
||||
|
||||
struct device {
|
||||
char * name;
|
||||
|
@ -35,14 +45,20 @@ struct device {
|
|||
};
|
||||
|
||||
struct options {
|
||||
char * uuid; /* universal unique channel identifier */
|
||||
char * middleware; /* url to middleware server */
|
||||
char * port; /* port your sensor is connected to */
|
||||
unsigned interval; /* interval in seconds, the daemon send data */
|
||||
char * middleware; /* url to middleware server */
|
||||
|
||||
/* following options should be replace by a list of connected/configured sensors/meters */
|
||||
char * uuid; /* universal unique channel identifier */
|
||||
char * port; /* port your sensor is connected to */
|
||||
|
||||
unsigned verbose:1; /* boolean bitfield, at the end of struct */
|
||||
unsigned daemon:1; /* boolean bitfield */
|
||||
};
|
||||
|
||||
/* Prototypes */
|
||||
void usage(char ** argv);
|
||||
struct options parse_options(int argc, char * argv[]);
|
||||
CURLcode api_log(char * middleware, char * uuid, struct reading read);
|
||||
|
||||
#endif /* _MAIN_H_ */
|
||||
|
|
58
misc/controller/vzlogger/src/protocols/obis.c
Normal file
58
misc/controller/vzlogger/src/protocols/obis.c
Normal file
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* OBIS protocol parser
|
||||
*
|
||||
* This is our example protocol. Use this skeleton to add your own
|
||||
* protocols and meters.
|
||||
*
|
||||
* @package controller
|
||||
* @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>
|
||||
* @author Mathias Dalheimer <md@gonium.net>
|
||||
* based heavily on libehz (https://github.com/gonium/libehz.git)
|
||||
*/
|
||||
/*
|
||||
* This file is part of volkzaehler.org
|
||||
*
|
||||
* volkzaehler.org is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* volkzaehler.org is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <string.h>
|
||||
|
||||
int obis_init(char * port) {
|
||||
struct termios tio;
|
||||
int fd;
|
||||
|
||||
memset(&tio, 0, sizeof(tio));
|
||||
|
||||
tio.c_iflag = 0;
|
||||
tio.c_oflag = 0;
|
||||
tio.c_cflag = CS7|CREAD|CLOCAL; // 7n1, see termios.h for more information
|
||||
tio.c_lflag = 0;
|
||||
tio.c_cc[VMIN] = 1;
|
||||
tio.c_cc[VTIME] = 5;
|
||||
|
||||
fd = open(port, O_RDWR); // | O_NONBLOCK);
|
||||
cfsetospeed(&tio, B9600); // 9600 baud
|
||||
cfsetispeed(&tio, B9600); // 9600 baud
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
float obis_get() {
|
||||
return 0;
|
||||
}
|
||||
|
37
misc/controller/vzlogger/src/protocols/obis.h
Normal file
37
misc/controller/vzlogger/src/protocols/obis.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* OBIS protocol parser
|
||||
*
|
||||
* This is our example protocol. Use this skeleton to add your own
|
||||
* protocols and meters.
|
||||
*
|
||||
* @package controller
|
||||
* @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/>.
|
||||
*/
|
||||
|
||||
#ifndef _OBIS_H_
|
||||
#define _OBIS_H_
|
||||
|
||||
#include "../main.h"
|
||||
|
||||
int obis_init(char * port);
|
||||
struct reading obis_get();
|
||||
|
||||
#endif /* _OBIS_H_ */
|
Loading…
Add table
Reference in a new issue