fixed the OBIS/d0 parser
This commit is contained in:
parent
4da2811f34
commit
c0f4a5b034
3 changed files with 95 additions and 83 deletions
|
@ -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);
|
||||
|
|
156
src/d0.c
156
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.
|
||||
|
@ -48,7 +48,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) {
|
||||
|
@ -94,87 +95,101 @@ void meter_close_d0(meter_t *mtr) {
|
|||
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;
|
||||
printf("reset!!!\n");
|
||||
}
|
||||
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';
|
||||
|
@ -184,41 +199,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(&rds[m].identifier.obis, id, strlen(id));
|
||||
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(&rds[number_of_tuples].identifier.obis, obis_code, strlen(obis_code));
|
||||
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:
|
||||
printf("read package with %i tuples (vendor=%s, baudrate=%c, ident=%s)\n", m, vendor, baudrate, identification);
|
||||
return m;
|
||||
printf("read package with %i tuples (vendor=%s, baudrate=%c, identification=%s)\n", number_of_tuples, vendor, baudrate, identification);
|
||||
return number_of_tuples;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
20
src/obis.c
20
src/obis.c
|
@ -103,11 +103,11 @@ obis_id_t * obis_init(obis_id_t *id, const unsigned char *raw) {
|
|||
int obis_parse(obis_id_t *id, const char *str, size_t n) {
|
||||
enum { A = 0, B, C, D, E, F };
|
||||
|
||||
char b;
|
||||
char byte; //currently processed Byte
|
||||
int num;
|
||||
int field;
|
||||
|
||||
num = b = 0;
|
||||
num = byte = 0;
|
||||
field = -1;
|
||||
memset(&id->raw, 0xff, 6); /* initialize as wildcard */
|
||||
|
||||
|
@ -115,22 +115,22 @@ int obis_parse(obis_id_t *id, const char *str, size_t n) {
|
|||
/* fields A, B, E, F are optional */
|
||||
/* fields C & D are mandatory */
|
||||
for (int i = 0; i < n; i++) {
|
||||
b = str[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 goto error; // TODO lookup aliases
|
||||
|
@ -149,7 +149,7 @@ int obis_parse(obis_id_t *id, const char *str, size_t n) {
|
|||
return 0;
|
||||
|
||||
error:
|
||||
printf("something unexpected happened (field=%i, b=%c, num=%i): %s:%i!\n", field, b, num, __FUNCTION__, __LINE__);
|
||||
printf("something unexpected happened (field=%i, byte=%c, num=%i): %s:%i!\n", field, byte, num, __FUNCTION__, __LINE__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue