Merge branch 'master' of github.com:volkszaehler/vzlogger
Conflicts: src/d0.c src/obis.c
This commit is contained in:
commit
0eb37cd777
7 changed files with 100 additions and 344 deletions
|
@ -1,41 +0,0 @@
|
|||
/**
|
||||
* Parsing commandline options and channel list
|
||||
*
|
||||
* @author Steffen Vogel <info@steffenvogel.de>
|
||||
* @copyright Copyright (c) 2011, The volkszaehler.org project
|
||||
* @package vzlogger
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*/
|
||||
/*
|
||||
* 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 _CONFIGURATION_H_
|
||||
#define _CONFIGURATION_H_
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include "channel.h"
|
||||
#include "list.h"
|
||||
#include "vzlogger.h"
|
||||
|
||||
void parse_configuration(char *filename, list_t *assocs, options_t *opts);
|
||||
channel_t * parse_channel(struct json_object *jso);
|
||||
assoc_t * parse_meter(struct json_object *jso);
|
||||
|
||||
int check_type(char *key, struct json_object *jso, enum json_type type);
|
||||
|
||||
#endif /* _CONFIGURATION_H_ */
|
|
@ -1,107 +0,0 @@
|
|||
/**
|
||||
* Generic linked list
|
||||
*
|
||||
* @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/>.
|
||||
*/
|
||||
|
||||
#ifndef _LIST_H_
|
||||
#define _LIST_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 {
|
||||
int size;
|
||||
__list_item_t *head;
|
||||
__list_item_t *tail;
|
||||
} list_t;
|
||||
|
||||
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;
|
||||
|
||||
if (old == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *data = old->data;
|
||||
|
||||
list->tail = old->prev;
|
||||
list->size--;
|
||||
|
||||
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_ */
|
|
@ -239,7 +239,7 @@ channel_t * parse_channel(struct json_object *jso) {
|
|||
else if (enabled == TRUE) {
|
||||
// TODO other identifiers are not supported at the moment
|
||||
reading_id_t id;
|
||||
|
||||
// TODO: at present (2011-11-05) aliases for identifiers don't work because "lookup aliases" is not (re-)implemented yet in src/obis.c; for now, only the obis identifiers (like "1.8.0") are allowed, so
|
||||
if (obis_parse(&id.obis, identifier, strlen(identifier)) != 0) {
|
||||
print(-1, "Invalid identifier: %s", NULL, identifier);
|
||||
exit(EXIT_FAILURE);
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* Wrapper to read Dallas 1-wire Sensors via the 1-wire Filesystem (owfs)
|
||||
*
|
||||
* @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/>.
|
||||
*/
|
||||
|
||||
#ifndef _ONEWIRE_H_
|
||||
#define _ONEWIRE_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
FILE *file;
|
||||
} meter_handle_onewire_t;
|
||||
|
||||
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_ */
|
155
src/d0.c
155
src/d0.c
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* Plaintext protocol according to DIN EN 62056-21
|
||||
*
|
||||
* This protocol uses OBIS to identify the readout data
|
||||
* This protocol uses OBIS codes to identify the readout data
|
||||
*
|
||||
* This is our example protocol. Use this skeleton to add your own
|
||||
* protocols and meters.
|
||||
|
@ -50,7 +50,8 @@
|
|||
int meter_d0_open_socket(const char *node, const char *service) {
|
||||
struct sockaddr_in sin;
|
||||
struct addrinfo *ais;
|
||||
int fd, res;
|
||||
int fd; // file descriptor
|
||||
int res;
|
||||
|
||||
fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
|
@ -116,86 +117,101 @@ int meter_close_d0(meter_t *mtr) {
|
|||
return close(handle->fd);
|
||||
}
|
||||
|
||||
size_t meter_read_d0(meter_t *mtr, reading_t rds[], size_t n) {
|
||||
size_t meter_read_d0(meter_t *mtr, reading_t rds[], size_t max_readings) {
|
||||
meter_handle_d0_t *handle = &mtr->handle.d0;
|
||||
|
||||
enum { START, VENDOR, BAUD, IDENT, START_LINE, OBIS, VALUE, UNIT, END_LINE, END } context;
|
||||
enum { START, VENDOR, BAUDRATE, IDENTIFICATION, START_LINE, OBIS_CODE, VALUE, UNIT, END_LINE, END } context;
|
||||
|
||||
char vendor[3+1]; /* 3 upper case vendor + '\0' termination */
|
||||
char identification[16+1]; /* 16 meter specific + '\0' termination */
|
||||
char id[16+1];
|
||||
char value[32+1];
|
||||
char unit[16+1];
|
||||
char obis_code[16+1]; /* A-B:C.D.E*F
|
||||
fields A, B, E, F are optional
|
||||
fields C & D are mandatory
|
||||
A: energy type; 1: energy
|
||||
B: channel number; 0: no channel specified
|
||||
C: data items; 0-89 in COSEM context: IEC 62056-62, Clause D.1; 96: General service entries
|
||||
1: Totel Active power+
|
||||
21: L1 Active power+
|
||||
31: L1 Current
|
||||
32: L1 Voltage
|
||||
41: L2 Active power+
|
||||
51: L2 Current
|
||||
52: L2 Voltage
|
||||
61: L3 Active power+
|
||||
71: L3 Current
|
||||
72: L3 Voltage
|
||||
96.1.255: Metering point ID 256 (electricity related)
|
||||
96.5.5: Meter started status flag
|
||||
D: types
|
||||
E: further processing or classification of quantities
|
||||
F: storage of data
|
||||
see DIN-EN-62056-61 */
|
||||
char value[32+1]; /* value, i.e. the actual reading */
|
||||
char unit[16+1]; /* the unit of the value, e.g. kWh, V, ... */
|
||||
|
||||
char baudrate; /* 1 byte */
|
||||
char byte;
|
||||
int j, k, m;
|
||||
char baudrate; /* 1 byte for */
|
||||
char byte; /* we parse our input byte wise */
|
||||
int byte_iterator;
|
||||
int number_of_tuples;
|
||||
|
||||
j = k = m = baudrate = 0;
|
||||
byte_iterator = number_of_tuples = baudrate = 0;
|
||||
|
||||
context = START;
|
||||
context = START; /* start with context START */
|
||||
|
||||
while (read(handle->fd, &byte, 1)) {
|
||||
if (byte == '/') context = START;
|
||||
else if (byte == '!') context = END;
|
||||
|
||||
if (byte == '/') context = START; /* reset to START if "/" reoccurs */
|
||||
else if (byte == '!') context = END; /* "!" is the identifier for the END */
|
||||
switch (context) {
|
||||
case START:
|
||||
if (byte == '/') {
|
||||
j = k = m = 0;
|
||||
context = VENDOR;
|
||||
}
|
||||
case START: /* strip the initial "/" */
|
||||
byte_iterator = number_of_tuples = 0; /* start */
|
||||
context = VENDOR; /* set new context: START -> VENDOR */
|
||||
break;
|
||||
|
||||
case VENDOR:
|
||||
if (!isalpha(byte)) goto error;
|
||||
else vendor[j++] = byte;
|
||||
case VENDOR: /* VENDOR has 3 Bytes */
|
||||
if (!isalpha(byte)) goto error; /* Vendor ID needs to be alpha */
|
||||
vendor[byte_iterator++] = byte; /* read next byte */
|
||||
if (byte_iterator >= 3) { /* stop after 3rd byte */
|
||||
vendor[byte_iterator] = '\0'; /* termination */
|
||||
byte_iterator = 0; /* reset byte counter */
|
||||
|
||||
if (j >= 3) {
|
||||
vendor[j] = '\0'; /* termination */
|
||||
j = k = 0;
|
||||
|
||||
context = BAUD;
|
||||
}
|
||||
context = BAUDRATE; /* set new context: VENDOR -> BAUDRATE */
|
||||
}
|
||||
break;
|
||||
|
||||
case BAUD:
|
||||
baudrate = byte;
|
||||
context = IDENT;
|
||||
j = k = 0;
|
||||
case BAUDRATE: /* BAUDRATE consists of 1 char only */
|
||||
baudrate = byte;
|
||||
context = IDENTIFICATION; /* set new context: BAUDRATE -> IDENTIFICATION */
|
||||
byte_iterator = 0;
|
||||
break;
|
||||
|
||||
case IDENT:
|
||||
/* data block starts after twice a '\r\n' sequence */
|
||||
/* b= CR LF CR LF */
|
||||
/* k= 1 2 3 4 */
|
||||
if (byte == '\r' || byte == '\n') {
|
||||
k++;
|
||||
if (k >= 4) {
|
||||
identification[j] = '\0'; /* termination */
|
||||
j = k = 0;
|
||||
|
||||
context = START_LINE;
|
||||
}
|
||||
case IDENTIFICATION: /* IDENTIFICATION has 16 bytes */
|
||||
if (byte == '\r' || byte == '\n') { /* detect line end */
|
||||
identification[byte_iterator] = '\0'; /* termination */
|
||||
context = OBIS_CODE; /* set new context: IDENTIFICATION -> OBIS_CODE */
|
||||
byte_iterator = 0;
|
||||
}
|
||||
else identification[j++] = byte;
|
||||
else identification[byte_iterator++] = byte;
|
||||
break;
|
||||
|
||||
case START_LINE:
|
||||
case OBIS:
|
||||
if (byte == '(') {
|
||||
id[j] = '\0';
|
||||
j = k = 0;
|
||||
break;
|
||||
case OBIS_CODE:
|
||||
if ((byte != '\n') && (byte != '\r'))
|
||||
{
|
||||
if (byte == '(') {
|
||||
obis_code[byte_iterator] = '\0';
|
||||
byte_iterator = 0;
|
||||
|
||||
context = VALUE;
|
||||
context = VALUE;
|
||||
}
|
||||
else obis_code[byte_iterator++] = byte;
|
||||
}
|
||||
else id[j++] = byte;
|
||||
break;
|
||||
|
||||
case VALUE:
|
||||
if (byte == '*' || byte == ')') {
|
||||
value[j] = '\0';
|
||||
j = k = 0;
|
||||
value[byte_iterator] = '\0';
|
||||
byte_iterator = 0;
|
||||
|
||||
if (byte == ')') {
|
||||
unit[0] = '\0';
|
||||
|
@ -205,41 +221,38 @@ size_t meter_read_d0(meter_t *mtr, reading_t rds[], size_t n) {
|
|||
context = UNIT;
|
||||
}
|
||||
}
|
||||
else value[j++] = byte;
|
||||
else value[byte_iterator++] = byte;
|
||||
break;
|
||||
|
||||
case UNIT:
|
||||
if (byte == ')') {
|
||||
unit[j] = '\0';
|
||||
j = k = 0;
|
||||
unit[byte_iterator] = '\0';
|
||||
byte_iterator = 0;
|
||||
|
||||
context = END_LINE;
|
||||
}
|
||||
else unit[j++] = byte;
|
||||
else unit[byte_iterator++] = byte;
|
||||
break;
|
||||
|
||||
case END_LINE:
|
||||
if (byte == '\r' || byte == '\n') {
|
||||
k++;
|
||||
if (k >= 2) {
|
||||
if (m < n) { /* free slots available? */
|
||||
//printf("parsed reading (id=%s, value=%s, unit=%s)\n", id, value, unit);
|
||||
rds[m].value = strtof(value, NULL);
|
||||
obis_parse(id, &rds[m].identifier.obis);
|
||||
gettimeofday(&rds[m].time, NULL);
|
||||
if ((number_of_tuples < max_readings) && (strlen(obis_code) > 0) && (strlen(value) > 0)) { /* free slots available and sain content? */
|
||||
printf("parsed reading (OBIS code=%s, value=%s, unit=%s)\n", obis_code, value, unit);
|
||||
rds[number_of_tuples].value = strtof(value, NULL);
|
||||
obis_parse(obis_code, &rds[number_of_tuples].identifier.obis);
|
||||
gettimeofday(&rds[number_of_tuples].time, NULL);
|
||||
|
||||
j = k = 0;
|
||||
m++;
|
||||
}
|
||||
byte_iterator = 0;
|
||||
number_of_tuples++;
|
||||
|
||||
context = START_LINE;
|
||||
context = OBIS_CODE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case END:
|
||||
print(log_info, "Read package with %i tuples (vendor=%s, baudrate=%c, ident=%s)", mtr, m, vendor, baudrate, identification);
|
||||
return m;
|
||||
print(log_debug, "Read package with %i tuples (vendor=%s, baudrate=%c, identification=%s)", mtr, number_of_tuples, vendor, baudrate, identification);
|
||||
return number_of_tuples;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
28
src/obis.c
28
src/obis.c
|
@ -105,33 +105,35 @@ obis_id_t * obis_init(obis_id_t *id, unsigned char *raw) {
|
|||
int obis_parse(const char *str, obis_id_t *id) {
|
||||
enum { A = 0, B, C, D, E, F };
|
||||
|
||||
char b = 0;
|
||||
int num = 0;
|
||||
int field = -1;
|
||||
size_t n = strlen(str);
|
||||
char byte; /* currently processed byte */
|
||||
int num;
|
||||
int field;
|
||||
int len = strlen(str);
|
||||
|
||||
memset(&id->raw, DC, 6); /* initialize as wildcard */
|
||||
num = byte = 0;
|
||||
field = -1;
|
||||
memset(&id->raw, 0xff, 6); /* initialize as wildcard */
|
||||
|
||||
/* format: "A-B:C.D.E[*&]F" */
|
||||
/* fields A, B, E, F are optional */
|
||||
/* fields C & D are mandatory */
|
||||
for (int i = 0; i < n; i++) {
|
||||
b = str[i];
|
||||
for (int i = 0; i < len; i++) {
|
||||
byte = str[i];
|
||||
|
||||
if (isdigit(b)) {
|
||||
num = (num * 10) + (b - '0'); /* parse digits */
|
||||
if (isdigit(byte)) {
|
||||
num = (num * 10) + (byte - '0'); /* parse digits */
|
||||
}
|
||||
else {
|
||||
if (b == '-' && field < A) { /* end of field A */
|
||||
if (byte == '-' && field < A) { /* end of field A */
|
||||
field = A;
|
||||
}
|
||||
else if (b == ':' && field < B) { /* end of field B */
|
||||
else if (byte == ':' && field < B) { /* end of field B */
|
||||
field = B;
|
||||
}
|
||||
else if (b == '.' && field < D) { /* end of field C & D*/
|
||||
else if (byte == '.' && field < D) { /* end of field C & D*/
|
||||
field = (field < C) ? C : D;
|
||||
}
|
||||
else if ((b == '*' || b == '&') && field == D) { /* end of field E, start of field F */
|
||||
else if ((byte == '*' || byte == '&') && field == D) { /* end of field E, start of field F */
|
||||
field = E;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
/**
|
||||
* Wrapper to read Dallas 1-wire Sensors via the 1-wire Filesystem (owfs)
|
||||
*
|
||||
* @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 <stdlib.h>
|
||||
|
||||
#include "meter.h"
|
||||
#include "onewire.h"
|
||||
|
||||
/**
|
||||
* Initialize sensor
|
||||
*
|
||||
* @param address path to the sensor in the owfs
|
||||
* @return pointer to file descriptor
|
||||
*/
|
||||
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_close_onewire(meter_t *mtr) {
|
||||
meter_handle_onewire_t *handle = &mtr->handle.onewire;
|
||||
|
||||
fclose(handle->file);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
do {
|
||||
rewind(handle->file);
|
||||
bytes = fread(buffer, 1, 16, handle->file);
|
||||
buffer[bytes] = '\0'; /* zero terminated, required? */
|
||||
|
||||
if (bytes) {
|
||||
rds->value = strtof(buffer, NULL);
|
||||
gettimeofday(&rds->time, NULL);
|
||||
}
|
||||
} while (rds->value == 85); /* skip invalid readings */
|
||||
|
||||
return 1;
|
||||
}
|
Loading…
Add table
Reference in a new issue