[vz-dev] [vz-users] vzlogger MeterD0 mit L&G
Karlheinz
karlheinz.es at gmx.de
Mon Apr 28 22:07:19 CEST 2014
Hallo Reinhard,
die print Anweisung hab ich aus vorherigen Code-Stellen kopiert - es
sollte funktionieren. Probier doch bitte mal aus, ob die MeterD0.cpp
(Anhang) bei dir geht.
Gruß
Karlheinz
------- Original Nachricht --------
Betreff: Re: [vz-dev] [vz-users] vzlogger MeterD0 mit L&G
Von: Reinhard Wilzeck <reinhard at wilzeck.de>
An: volkszaehler.org <volkszaehler-dev at demo.volkszaehler.org>
Datum: Freitag, 25. April 2014 19:16:34
> Noch eine blöde Frage( auf die Gefahr mich ohne meinen PC zu blamieren):
> Ist die printanweisung richtig?
> Vielleich noch ein %s ? 2 Argumente nach dem " aber nur ein Platzhalter.
>
>
> Mfg. Reinhard Wilzeck
>
>
>
> -------- Ursprüngliche Nachricht --------
> Von: Karlheinz <karlheinz.es at gmx.de>
> Datum: 25.04.2014 12:15 (GMT+01:00)
> An: volkszaehler-dev at demo.volkszaehler.org
> Betreff: Re: [vz-dev] [vz-users] vzlogger MeterD0 mit L&G
>
>
> Hallo Reinhard,
>
> Danke für den Hinweis.
> Bei mir bricht vzlogger schon früher ab. Ich habe eine print-Anweisung
> nach "case OBIS_CODE:" drin, die nach dem CRLF nicht mehr durchlaufen
> wird.
> case OBIS_CODE:
> print(log_debug, "DEBUG OBIS_CODE byte hex= %X ",name().c_str(), byte);
>
> =-O ssl ratlos! Hast du einen Tipp für Anfänger wie ich das debuggen
> kann?
>
> Dein Problem mit '*' war schon im MeterD0 drin.
>
> Gruß
> Karlheinz
>
> ------- Original Nachricht --------
> Betreff: Re: [vz-dev] [vz-users] vzlogger MeterD0 mit L&G
> Von: Reinhard Wilzeck <reinhard at wilzeck.de>
> An: volkszaehler.org <volkszaehler-dev at demo.volkszaehler.org>
> Datum: Donnerstag, 24. April 2014 22:28:45
>
>> Hallo,
>> ich hatte damals das gleiche Problem und habe es auf die brutale Art
>> gelöst.
>> "Illegale" Zeichen werden unterdrückt, bevor der OBIS Parser dadurch
>> abstürzen kann.
>> Wir müssen den STX (0x02) ausblenden. (ordentlicher wäre auf den STX
>> zu warten, aber davon haben wir nix. Und wenn der STX richtig
>> ausgewertet wird, dann müßten wir konsequenterweise das Telgramm bis
>> zum Ende auswerten, damit wir Fehler erkennen)
>> Ich hatte auch ein Problem mit "*" vor der Einheit. Da die Einheit
>> auch nicht wirklich verwendet wird kann man auch bei dem "*" schon
>> Schluss machen)
>>
>> Nachstehende Modifikation sollte die Probleme auch für Deinen Fall lösen.
>> case OBIS_CODE:
>> * if ((byte != '\n') && (byte != '\r')&& (byte != 0x02)***&& (byte
>> != 0x1F)*)// STX und US ausklammern *
>> {
>> if (byte == '(') {
>> obis_code[byte_iterator] = '\0';
>> byte_iterator = 0;
>>
>> context = VALUE;
>> }
>> else obis_code[byte_iterator++] = byte;
>> }
>> break;
>>
>> case VALUE:
>> ***if (byte == '*' **|| byte == ')') {*
>> value[byte_iterator] = '\0';
>> byte_iterator = 0;
>>
>> if (byte == ')') {
>> unit[0] = '\0';
>> context = END_LINE;
>> }
>> else {
>> context = UNIT;
>> }
>> }
>> else value[byte_iterator++] = byte;
>> break;
>>
>> Gruß
>> Reinhard
>>
>> Am 23.04.2014 23:48, schrieb Karlheinz:
>>> Hallo Stefan,
>>>
>>> es gibt einige Beiträge mit diesem D0-Problem. Shell-Scripts gibts
>>> es auch schon im Wiki. Besser wäre es wenn vzlogger das von Haus aus
>>> unterstützt.
>>> Aber ohne Debug-Unterstützung komme ich leider nicht weiter.
>>> Wie erzeuge ich eine debug-fähige vzlogger version, die ich mit ddd
>>> debuggen kann?
>>> Vielleicht verirrt sich mal ein Programmierer in den Chat
>>> <http://webchat.freenode.net/?channels=volkszaehler.org>und leistet
>>> etwas Entwicklungshilfe :-)
>>>
>>> Gruß
>>> Karlheinz
>>>
>>> ------- Original Nachricht --------
>>> Betreff: Re: [vz-users] vzlogger MeterD0 mit L&G
>>> Von: Stefan Klammer <klammerstefan85 at gmail.com>
>>> An: volkszaehler.org - users <volkszaehler-users at demo.volkszaehler.org>
>>> Datum: Mittwoch, 23. April 2014 08:26:47
>>>
>>>> Hallo Karlheinz,
>>>>
>>>> ich habe ein ähnliches Problem mit meinem (Landis & Gyr ZMD120AR),
>>>> dieser sendet am Schluss der Übertragung ein Zeichen das dem
>>>> vzlogger bzw. d0 Parser nicht passt und der logger somit aussteigt.
>>>> Ich wollte denn D0 Parser auch schon anpassen das er wirklich nur
>>>> die geforderten OBIS Codes ausliest (z.B.: 1.8.1) und alles andere
>>>> was er nicht kennt einfach ignoriert. Leider bin ich bis jetzt
>>>> nicht dazugekommen. Vielleicht kannst du das mal ausprobieren wäre
>>>> super wenn da endlich mal was Zustande kommt.
>>>>
>>>> Vielleicht schaust du dir das hier mal an:
>>>> http://www.mail-archive.com/volkszaehler-dev%40lists.volkszaehler.org/msg02150.html
>>>> Hört sich nach einem ähnlichen Problem an.
>>>>
>>>> Oder hier:
>>>> http://www.mail-archive.com/volkszaehler-dev%40lists.volkszaehler.org/msg01732.html
>>>> Da kannst du meine Ausgaben vom Zähler sehen.
>>>>
>>>> Hoffe du kriegst noch was raus. Wäre schön zu hören wenns klappt.
>>>>
>>>> Gruß Stefan
>>>>
>>>> Am 18. April 2014 17:17 schrieb Karlheinz <karlheinz.es at gmx.de
>>>> <mailto:karlheinz.es at gmx.de>>:
>>>>
>>>> Hallo Leute,
>>>>
>>>> seit Tagen versuche ich den vzlogger (MeterD0.cpp) auch für
>>>> meinen Stromzähler (Landys & Gir ZMD120 ...) anzupassen. Über
>>>> die ersten Stolpersteine bin ich mittlerweile drüber, aber nun
>>>> steigt das Programm bei der while Schleife "while (::read(_fd,
>>>> &byte, 1)) { " beim Zeichen 0x1F immer aus. Wenn ich das
>>>> richtig sehe wird das Zeichen 0x1F und dann 0x02 gesendet,
>>>> bevor es bei F.F usw. weiter geht.
>>>>
>>>> /LGZ52ZMD120APt.G03
>>>> ..F.F(00000000)
>>>> 0.0.0( 26700)
>>>> 1.8.1(012334.7*kWh)
>>>> ...
>>>>
>>>> Hat jemand eine Idee?
>>>>
>>>> Gruß
>>>> Karlheinz
>>>>
>>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://demo.volkszaehler.org/pipermail/volkszaehler-dev/attachments/20140428/9cb94345/attachment-0001.html>
-------------- next part --------------
/**
* Plaintext protocol according to DIN EN 62056-21
*
* 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.
*
* @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 at steffenvogel.de>
* @author Mathias Dalheimer <md at gonium.net>
*/
/*
* 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 <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <sys/time.h>
/* socket */
#include <netdb.h>
#include <sys/socket.h>
#include "protocols/MeterD0.hpp"
#include <VZException.hpp>
#include "Obis.hpp"
MeterD0::MeterD0(std::list<Option> options)
: Protocol("d0")
, _host("")
, _device("")
{
OptionList optlist;
/* connection */
try {
_host = optlist.lookup_string(options, "host");
} catch ( vz::OptionNotFoundException &e ) {
try {
_device = optlist.lookup_string(options, "device");
} catch ( vz::VZException &e ){
print(log_error, "Missing device or host", name().c_str());
throw ;
}
} catch( vz::VZException &e ) {
print(log_error, "Missing device or host", name().c_str());
throw;
}
try {
std::string hex;
hex = optlist.lookup_string(options, "pullseq");
int n=hex.size();
int i;
for(i=0;i<n;i=i+2) {
char hs[3];
strncpy(hs,hex.c_str()+i,2);
char hx[2];
hx[0]=strtol(hs,NULL,16);
_pull.append(hx,1);
}
print(log_debug,"pullseq len:%d found",name().c_str(),_pull.size());
} catch( vz::OptionNotFoundException &e ) {
/* using default value if not specified */
_pull = "";
}
/* baudrate */
int baudrate = 9600; /* default to avoid compiler warning */
try {
baudrate = optlist.lookup_int(options, "baudrate");
/* find constant for termios structure */
switch (baudrate) {
case 50: _baudrate = B50; break;
case 75: _baudrate = B75; break;
case 110: _baudrate = B110; break;
case 134: _baudrate = B134; break;
case 150: _baudrate = B150; break;
case 200: _baudrate = B200; break;
case 300: _baudrate = B300; break;
case 600: _baudrate = B600; break;
case 1200: _baudrate = B1200; break;
case 1800: _baudrate = B1800; break;
case 2400: _baudrate = B2400; break;
case 4800: _baudrate = B4800; break;
case 9600: _baudrate = B9600; break;
case 19200: _baudrate = B19200; break;
case 38400: _baudrate = B38400; break;
case 57600: _baudrate = B57600; break;
case 115200: _baudrate = B115200; break;
case 230400: _baudrate = B230400; break;
default:
print(log_error, "Invalid baudrate: %i", name().c_str(), baudrate);
throw vz::VZException("Invalid baudrate");
}
} catch( vz::OptionNotFoundException &e ) {
/* using default value if not specified */
_baudrate = B9600;
} catch( vz::VZException &e ) {
print(log_error, "Failed to parse the baudrate", name().c_str());
throw;
}
_parity=parity_7e1;
try {
const char *parity = optlist.lookup_string(options, "parity");
/* find constant for termios structure */
if(strcasecmp(parity,"8n1")==0) {
_parity=parity_8n1;
} else if(strcasecmp(parity,"7n1")==0) {
_parity=parity_7n1;
} else if(strcasecmp(parity,"7e1")==0) {
_parity=parity_7e1;
} else if(strcasecmp(parity,"7o1")==0) {
_parity=parity_7o1;
} else {
throw vz::VZException("Invalid parity");
}
} catch( vz::OptionNotFoundException &e ) {
/* using default value if not specified */
_parity = parity_7e1;
} catch( vz::VZException &e ) {
print(log_error, "Failed to parse the parity", name().c_str());
throw;
}
}
MeterD0::~MeterD0() {
}
int MeterD0::open() {
if (_device != "") {
_fd = _openDevice(&_oldtio, _baudrate);
}
else if (_host != "") {
char *addr = strdup(host());
const char *node = strsep(&addr, ":");
const char *service = strsep(&addr, ":");
_fd = _openSocket(node, service);
}
return (_fd < 0) ? ERR : SUCCESS;
}
int MeterD0::close() {
return ::close(_fd);
}
ssize_t MeterD0::read(std::vector<Reading>&rds, size_t max_readings) {
enum { START, VENDOR, BAUDRATE, IDENTIFICATION, START_LINE, OBIS_CODE, VALUE, UNIT, END_LINE, ERROR_MESSAGE, END } context;
bool error_flag = false;
char vendor[3+1]; /* 3 upper case vendor + '\0' termination */
char identification[16+1]; /* 16 meter specific + '\0' termination */
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 for */
char byte; /* we parse our input byte wise */
int byte_iterator;
char endseq[2+1]; /* Endsequence ! not ?! */
size_t number_of_tuples;
if(_pull.size()) {
int wlen=write(_fd,_pull.c_str(),_pull.size());
print(log_debug,"sending pullsequenz send (len:%d is:%d).",name().c_str(),_pull.size(),wlen);
}
byte_iterator = number_of_tuples = baudrate = 0;
context = START; /* start with context START */
while (::read(_fd, &byte, 1)) {
/* reset to START if "/" reoccurs */
/* if (byte == '/') context = START; */
/* else if (byte == '!') context = END; */
/* "!" is the identifier for the END */
/* print(log_debug, "DEBUG begin switch byte hex= %X ", name().c_str(), byte);*/
if (byte == '/') context = VENDOR;
else if (byte == '?' or byte == '!') context = END; /* "!" is the identifier for the END */
switch (context) {
case START: /* strip the initial "/" */
print(log_error, "DEBUG START byte= %c %x ", name().c_str(), byte, byte);
if (byte != '\r' && byte != '\n') { /*allow extra new line at the start */
byte_iterator = number_of_tuples = 0; /* start */
context = VENDOR; /* set new context: START -> VENDOR */
}
break;
case VENDOR: /* VENDOR has 3 Bytes */
/*print(log_debug, "DEBUG Vendor1 byte= %c hex= %x byteIterator= %i ",name().c_str(), byte, byte, byte_iterator);*/
if (byte == '\r' or byte == '\n' or byte == '/' ) {
byte_iterator = number_of_tuples = 0;
break;
}
print(log_debug, "DEBUG Vendor2 byte= %c hex= %x byteIterator= %i ",name().c_str(), byte, byte, byte_iterator);
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 */
context = BAUDRATE; /* set new context: VENDOR -> BAUDRATE */
}
break;
case BAUDRATE: /* BAUDRATE consists of 1 char only */
print(log_debug, "DEBUG BAUDRATE byte= %c %x ",name().c_str(), byte, byte);
baudrate = byte;
context = IDENTIFICATION; /* set new context: BAUDRATE -> IDENTIFICATION */
byte_iterator = 0;
break;
case IDENTIFICATION: /* IDENTIFICATION has 16 bytes */
print(log_debug, "DEBUG IDENTIFICATION byte= %c hex= %x ",name().c_str(), byte, byte);
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 {
if(!isprint(byte)) {
print(log_error, "====> binary character '%x'", name().c_str(), byte);
//error_flag=true;
}
else {
identification[byte_iterator++] = byte;
}
}
break;
case START_LINE:
break;
case OBIS_CODE:
/*print(log_debug, "DEBUG OBIS_CODE byte= %c hex= %x ",name().c_str(), byte, byte);*/
print(log_debug, "DEBUG OBIS_CODE byte hex= %X ",name().c_str(), byte);
if ((byte != '\n') && (byte != '\r') && (byte != 0x1F) && (byte != 0x02) ) {
print(log_debug, "DEBUG OBIS_CODE 2 byte hex= %X ",name().c_str(), byte);
if (byte == 'F') {
/* Fehlerausgabe mit F beginnend */
context = ERROR_MESSAGE;
break;
}
print(log_debug, "DEBUG OBIS_CODE start byte= %c hex= %x ",name().c_str(), byte, byte);
if (byte == '(') {
obis_code[byte_iterator] = '\0';
byte_iterator = 0;
context = VALUE;
}
else obis_code[byte_iterator++] = byte;
}
/*print(log_debug, "DEBUG OBIS_CODE end byte= %c hex= %x ",name().c_str(), byte, byte);*/
print(log_debug, "DEBUG OBIS_CODE end byte hex= %x ",name().c_str(), byte);
break;
case VALUE:
print(log_debug, "DEBUG VALUE byte= %c hex= %x ",name().c_str(), byte, byte);
if (byte == '*' || byte == ')') {
value[byte_iterator] = '\0';
byte_iterator = 0;
if (byte == ')') {
unit[0] = '\0';
context = END_LINE;
}
else {
context = UNIT;
}
}
else value[byte_iterator++] = byte;
break;
case UNIT:
if (byte == ')') {
unit[byte_iterator] = '\0';
byte_iterator = 0;
context = END_LINE;
}
else unit[byte_iterator++] = byte;
break;
case END_LINE:
if (byte == '\r' || byte == '\n') {
/* free slots available and sain content? */
if ((number_of_tuples < max_readings) && (strlen(obis_code) > 0) &&
(strlen(value) > 0)) {
print(log_debug, "Parsed reading (OBIS code=%s, value=%s, unit=%s)", name().c_str(), obis_code, value, unit);
rds[number_of_tuples].value(strtof(value, NULL));
Obis obis(obis_code);
ReadingIdentifier *rid(new ObisIdentifier(obis));
rds[number_of_tuples].identifier(rid);
rds[number_of_tuples].time();
byte_iterator = 0;
number_of_tuples++;
context = OBIS_CODE;
}
}
break;
case ERROR_MESSAGE:
print(log_debug, "DEBUG ERROR_MESSAGE byte= %c hex= %x ",name().c_str(), byte, byte);
/* waiting for CR or NL */
if ((byte == '\n') or (byte == '\r')) {
context = OBIS_CODE;
}
break;
case END:
print(log_debug, "DEBUG END1 %c %i ", name().c_str(), byte, byte_iterator);
endseq[byte_iterator++] = byte;
print(log_debug, "DEBUG END2 byte: %c iterator: %i ", name().c_str(), byte, byte_iterator);
if(endseq[0] == '?' ) {
/* endseq[byte_iterator++] = byte;*/
/* context = END; */
print(log_debug, "DEBUG END3 byte: %x endseq: %x ", name().c_str(), byte, endseq);
if(endseq[1] == '!') {
context = VENDOR;
endseq[byte_iterator] = '\0';
print(log_debug, "DEBUG END4 goto VENDOR", name().c_str());
byte_iterator = 0;
endseq[3] = { 0 };
}
break;
}
if(error_flag) {
print(log_error, "reading binary values.", name().c_str());
goto error;
}
print(log_debug, "Read package with %i tuples (vendor=%s, baudrate=%c, identification=%s)",
name().c_str(), number_of_tuples, vendor, baudrate, identification);
return number_of_tuples;
}
}
error:
print(log_error, "Something unexpected happened: %s:%i!", name().c_str(), __FUNCTION__, __LINE__);
print(log_debug, "Read (vendor=%s, baudrate=%c, identification=%s)",
name().c_str(), number_of_tuples, vendor, baudrate, identification);
return 0;
}
int MeterD0::_openSocket(const char *node, const char *service) {
struct sockaddr_in sin;
struct addrinfo *ais;
int fd; /* file descriptor */
int res;
fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0) {
print(log_error, "socket(): %s", name().c_str(), strerror(errno));
return ERR;
}
getaddrinfo(node, service, NULL, &ais);
memcpy(&sin, ais->ai_addr, ais->ai_addrlen);
freeaddrinfo(ais);
res = connect(fd, (struct sockaddr *) &sin, sizeof(sin));
if (res < 0) {
print(log_error, "connect(%s, %s): %s", name().c_str(), node, service, strerror(errno));
return ERR;
}
return fd;
}
int MeterD0::_openDevice(struct termios *old_tio, speed_t baudrate) {
struct termios tio;
memset(&tio, 0, sizeof(struct termios));
int fd = ::open(device(), O_RDWR);
if (fd < 0) {
print(log_error, "open(%s): %s", name().c_str(), device(), strerror(errno));
return ERR;
}
/* get old configuration */
tcgetattr(fd, &tio) ;
/* backup old configuration to restore it when closing the meter connection */
memcpy(old_tio, &tio, sizeof(struct termios));
tio.c_iflag &= ~(BRKINT | INLCR | IMAXBEL);
tio.c_oflag &= ~(OPOST | ONLCR);
tio.c_lflag &= ~(ISIG | ICANON | IEXTEN | ECHO);
switch(_parity) {
case parity_8n1:
tio.c_cflag &= ~ PARENB;
tio.c_cflag &= ~ CSTOPB;
tio.c_cflag &= ~ CSIZE;
tio.c_cflag |= CS8;
break;
case parity_7n1:
tio.c_cflag &= ~ PARENB;
tio.c_cflag &= ~ CSTOPB;
tio.c_cflag &= ~ CSIZE;
tio.c_cflag |= CS7;
break;
case parity_7e1:
tio.c_cflag |= ~ PARENB;
tio.c_cflag &= ~ PARODD;
tio.c_cflag &= ~ CSTOPB;
tio.c_cflag &= ~ CSIZE;
tio.c_cflag |= CS7;
break;
case parity_7o1:
tio.c_cflag |= ~ PARENB;
tio.c_cflag |= ~ PARODD;
tio.c_cflag &= ~ CSTOPB;
tio.c_cflag &= ~ CSIZE;
tio.c_cflag |= CS7;
break;
}
/* set baudrate */
cfsetispeed(&tio, baudrate);
cfsetospeed(&tio, baudrate);
/* apply new configuration */
tcsetattr(fd, TCSANOW, &tio);
return fd;
}
More information about the volkszaehler-dev
mailing list