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