[vz-dev] vzlogger MeterD0.cpp Master branch parity

Reinhard Wilzeck reinhard at wilzeck.de
Sun Dec 22 16:56:09 CET 2013


Hallo,
seit einigen Wochen läuft meine Version mit dem Zähler Landis+Gyr E350 
EDL21 mit 300baud nun stabil.

Was ich ergänzt habe:
1) Fehler beim Setzen des parity korrigiert. (RTS/CTS)
2) Der Landis+Gyr E350 braucht einen Acknowledge. Der kann über die 
vzlogger.conf nun konfiguriert werden.
     "ackseq": "063030300d0a",
Aufbau: $06 ist ein Startzeichen, die nachfolgenden '000' sind 300 
baud.' 040' wäre 4800baud. 0d0a sind die üblichen CR LF
3) Schnittstelle so konfiguriert, dass nach 5s ein timeout kommt und 
diesen ausgewertet. (der read kommt dann mit 0 zurück)
        tio.c_cc[VTIME]    = 50;     /* inter-character timer 50*0.1s*/
         tio.c_cc[VMIN]     = 0;     /* VTIME is timeout counter */

4) Baudrate umschalten. Kann nun konfiguriert werden. (Im Prinzip müßte 
es gehen, praktisch bekomme ich von meinem Zähler dann keine Antwort. 
Vielleicht kann es mal jemand testen oder mir sagen warum es nicht geht. )
"baudrate_read":300 setzt die Schnittstelle über
                             cfsetispeed(&tio, baudrate_read);
                             tcsetattr(_fd, TCSANOW, &tio);
auf die neue baudrate (nur die Empfangsrichtung)

Die Einträge in der vzlogger.conf für einen Acknowledge ohne baudraten 
umschalten sehen dann so aus:
         "protocol" : "d0",     /* see 'vzlogger -h' for list of 
available protocols */
         "device" : "/dev/ttyUSB0",
     "enabled": true,
     "parity": "7e1",
     "baudrate":300,
     "baudrate_read":300,
     "pullseq":"2f3f210d0a",
     "ackseq": "063030300d0a",
     "interval": 60,
         "channels": [{
...
         }]
Fehlen die zusätzlichen Einträge, ist die entsprechende Funktion 
inaktiv. (kompatible Erweiterung)

5) Filter auf die empfangenen OBIS Codes, da sonst Absturz.

                 case OBIS_CODE:
                     if ((byte != '\n') && (byte != '\r')&& (byte != 
0x02))// STX ausklammern
                 case END_LINE:
                             ...
                             if ((obis_code[0]=='1')||(obis_code[0]=='2')) {
                                 Obis obis(obis_code);
Letzteres ist eigentlich eine Notlösung, da ich obis.cpp nicht wirklich 
durchschaut habe, aber feststellen musste, das es sehr empfindlich auf 
nicht Obis codes reagiert.

Anbei die geänderten sourcen.
Ich hoffe, sie sind verständlich und es hilft bei der Entwicklung der 
nächsten Version.

Reinhard


Am 21.11.2013 00:57, schrieb Thorben Thuermer:
> On Wed, 20 Nov 2013 23:36:13 +0100
> Reinhard Wilzeck <reinhard at wilzeck.de> wrote:
>> erfreulicherweise kann man nun für das Protokoll D0 parity und baud rate
>> über die vzlogger.conf konfigurieren.
>> (Nötig z.B. für Landis+Gyr E350 EDL21 .)
>>        "protocol" : "d0",
>>       "parity": "7e1",
>>       "baudrate":300,
>> Leider hat sich ein Fehler beim Setzen der einzelnen bits eingeschlichen.
>> (Ich weiss nicht, ob es schon jemand gesehen hat)
>> Dadurch wird unbeabsichtigt u.a. CRTSCTS gesetzt.
>> Damit sendet Udos USB IR Schreiblesekopf nicht.  (macht er richtig, denn
>> ohne den vzlogger arbeitet er einwandfrei.)
>>
>> falsch ist z.B.
>>       case parity_7e1:
>>           tio.c_cflag |= ~ PARENB;
>> Damit werden alle bits gesetzt (außer PARENB)
>> Richtig ist:
>>       switch(_parity) {
>>       case parity_8n1:
>>           tio.c_cflag &= ~ PARENB;
> das koennte die probleme erklaeren die andere user schon mit porteinstellungen
> bei vzlogger hatten...
> (finde den thread gerade nicht)
>
>> An dem Acknowledge, das der Zähler braucht arbeite ich noch. Außerdem
>> kann man die Schnittstelle auch mit einem Timeout versehen, so das der
>> read nicht für immer hängen bleibt.
>> /* Set return rules for read to prevent endless waiting*/
>>        tio.c_cc[VTIME]    = 50;     /* inter-character timer 50*0.1s*/
>>        tio.c_cc[VMIN]     = 0;     /* VTIME is timeout counter */
>>
>> Der praktische Beweis der Funktion steht aber noch aus.
> ich kann das leider mangels zaehlern wenig testen.
> schickst du uns einen patch, sobald du eine version hast die laeuft?
> danke!
>
>> Gruß
>>       Reinhard
> - Thorben
>

-------------- 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/>.
 */
 // Add Acknowlegde RW
// Corrected parity
// Correct Obidis( filter 1 und 2)
// Filter STX
// Baudrate change

#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 = "";
	}
	try {
		std::string hex;
		hex = optlist.lookup_string(options, "ackseq");
		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);
			_ack.append(hx,1);
		}
		print(log_debug,"ackseq len:%d found %s, %x",name().c_str(),_ack.size(),_ack.c_str(),_ack.c_str()[0]);
	} catch( vz::OptionNotFoundException &e ) {
		/* using default value if not specified */
		_ack = "";
	}

	/* 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, "RW: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;
	}
	try {
		baudrate = optlist.lookup_int(options, "baudrate_read");
		/* find constant for termios structure */
		switch (baudrate) {
				case 50:   _baudrate_read = B50; break;
				case 75:   _baudrate_read = B75; break;
				case 110:  _baudrate_read = B110; break;
				case 134:  _baudrate_read = B134; break;
				case 150:  _baudrate_read = B150; break;
				case 200:  _baudrate_read = B200; break;
				case 300:  _baudrate_read = B300; break;
				case 600:  _baudrate_read = B600; break;
				case 1200: _baudrate_read = B1200; break;
				case 1800: _baudrate_read = B1800; break;
				case 2400: _baudrate_read = B2400; break;
				case 4800: _baudrate_read = B4800; break;
				case 9600: _baudrate_read = B9600; break;
				case 19200: _baudrate_read = B19200; break;
				case 38400: _baudrate_read = B38400; break;
				case 57600: _baudrate_read = B57600; break;
				case 115200: _baudrate_read = B115200; break;
				case 230400: _baudrate_read = B230400; break;
				default:
					print(log_error, "RW:Invalid baudrate_read: %i", name().c_str(), baudrate);
					throw vz::VZException("Invalid baudrate");
		}
	} catch( vz::OptionNotFoundException &e ) {
		/* using default value if not specified */
		_baudrate_read = _baudrate;
	} catch( vz::VZException &e ) {
		print(log_error, "Failed to parse the baudrate_read", 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,ACK, START_LINE, OBIS_CODE, VALUE, UNIT, END_LINE, 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,lastbyte;			/* we parse our input byte wise */
	int byte_iterator; 
	size_t number_of_tuples;
	struct termios tio;
	int baudrate_connect,baudrate_read;// Baudrates for switching
	
	
	baudrate_connect=_baudrate;
	baudrate_read=_baudrate_read;
	tcgetattr(_fd, &tio) ;

	if(_pull.size()) {
		tcflush(_fd, TCIOFLUSH);
		cfsetispeed(&tio, baudrate_connect);
		cfsetospeed(&tio, baudrate_connect);
		/* apply new configuration */
		tcsetattr(_fd, TCSANOW, &tio);

		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;
	byte=lastbyte=0;	
	context = START;				/* start with context START */

	while (::read(_fd, &byte, 1)) {
		lastbyte=byte;
		if (byte == '/') context = START; 	/* reset to START if "/" reoccurs */
		else if (byte == '!') context = END;	/* "!" is the identifier for the END */		
		switch (context) {
				case START:			/* strip the initial "/" */
                    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 */
					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 */
					baudrate = byte;	
					context = IDENTIFICATION;	/* set new context: BAUDRATE -> IDENTIFICATION */
					byte_iterator = 0;
					break;

				case IDENTIFICATION:		/* IDENTIFICATION has 16 bytes */
					if (byte == '\r' || byte == '\n') { /* detect line end */
						identification[byte_iterator] = '\0'; /* termination */
					print(log_debug, "Pull answer (vendor=%s, baudrate=%c, identification=%s)",
								name().c_str(),  vendor, baudrate, identification);
						//context = OBIS_CODE;	/* set new context: IDENTIFICATION -> OBIS_CODE */
						context = ACK;
						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;
					}
					break; //--um dies Ack Sequenz send zu können zwar den context wechseln, aber ohne ein Zeichen zu erwarten
				case ACK:
					if(_ack.size()) {
						//tcflush(_fd, TCIOFLUSH);
						//usleep (500000);
						if (baudrate_read!=baudrate_connect) {
							cfsetispeed(&tio, baudrate_read);
							tcsetattr(_fd, TCSANOW, &tio);
						}
						int wlen=write(_fd,_ack.c_str(),_ack.size());
						tcdrain(_fd);// Warte bis Ausgang gesendet
						print(log_debug,"sending ack sequenz send (len:%d is:%d,%s).",name().c_str(),_ack.size(),wlen,_ack.c_str());
					}
					context = OBIS_CODE;

					break;
				case START_LINE:
					break;
				case OBIS_CODE:
					if ((byte != '\n') && (byte != '\r')&& (byte != 0x02))// STX 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;

				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));
							if ((obis_code[0]=='1')||(obis_code[0]=='2')) {
								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 END:
					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;
		}/* end switch*/
	}/* end while*/
	// Read terminated
	print(log_error, "read timed out!, context: %i, bytes read: %i, last byte 0x%x", name().c_str(),context,byte_iterator,lastbyte);
	return 0;
	error:
	print(log_error, "Something unexpected happened: %s:%i!", name().c_str(), __FUNCTION__, __LINE__);
	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));
      /* 
          initialize all control characters 
          default values can be found in /usr/include/termios.h, and are given
          in the comments, but we don't need them here
        */
         tio.c_cc[VINTR]    = 0;     /* Ctrl-c */ 
         tio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
         tio.c_cc[VERASE]   = 0;     /* del */
         tio.c_cc[VKILL]    = 0;     /* @ */
         tio.c_cc[VEOF]     = 4;     /* Ctrl-d */
         tio.c_cc[VTIME]    = 0;     /* inter-character timer unused */
         tio.c_cc[VMIN]     = 1;     /* blocking read until 1 character arrives */
         tio.c_cc[VSWTC]    = 0;     /* '\0' */
         tio.c_cc[VSTART]   = 0;     /* Ctrl-q */ 
         tio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
         tio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
         tio.c_cc[VEOL]     = 0;     /* '\0' */
         tio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
         tio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
         tio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
         tio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
         tio.c_cc[VEOL2]    = 0;     /* '\0' */
       
	tio.c_iflag &= ~(BRKINT | INLCR | IMAXBEL | IXOFF| IXON);
	tio.c_oflag &= ~(OPOST | ONLCR);
	tio.c_lflag &= ~(ISIG | ICANON | IEXTEN | ECHO);

	switch(_parity) {
	case parity_8n1:
		tio.c_cflag &= ~ PARENB;
		tio.c_cflag &= ~ PARODD;
		tio.c_cflag &= ~ CSTOPB;
		tio.c_cflag &= ~ CSIZE;
		tio.c_cflag |= CS8;
		break;
	case parity_7n1:
		tio.c_cflag &= ~ PARENB;
		tio.c_cflag &= ~ PARODD;
		tio.c_cflag &= ~ CSTOPB;
		tio.c_cflag &= ~ CSIZE;
		tio.c_cflag |= CS7;
		break;
	case parity_7e1:
		tio.c_cflag &= ~CRTSCTS;
		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 return rules for read to prevent endless waiting*/
 	tio.c_cc[VTIME]    = 50;     /* inter-character timer  50*0.1s*/
        tio.c_cc[VMIN]     = 0;     /* VTIME is timeout counter */
       /* 
          now clean the modem line and activate the settings for the port
        */
        tcflush(fd, TCIOFLUSH);
	/* set baudrate */
	cfsetispeed(&tio, baudrate);
	cfsetospeed(&tio, baudrate);

	/* apply new configuration */
	tcsetattr(fd, TCSANOW, &tio);

	return fd;
}
-------------- next part --------------
/**
 * Plaintext protocol according to DIN EN 62056-21
 *
 * This protocol uses OBIS to identify the readout data
 * And is also sometimes called "D0"
 *
 * @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>
 */
/*
 * 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/>.
 */
// RW: added ack 
#ifndef _D0_H_
#define _D0_H_

#define D0_BUFFER_LENGTH 1024

#include <termios.h>

#include <protocols/Protocol.hpp>

class MeterD0 : public vz::protocol::Protocol {
public:
	MeterD0(std::list<Option> options);
	virtual ~MeterD0();

	int open();
	int close();
	ssize_t read(std::vector<Reading> &rds, size_t n);

	const char *host() const { return _host.c_str(); }
	const char *device() const { return _device.c_str(); }

private:
	std::string _host;
	std::string _device;
	int _baudrate;
	int _baudrate_read;

	parity_type_t _parity;
	std::string _pull;
	std::string _ack;


	int _fd; /* file descriptor of port */
	struct termios _oldtio; /* required to reset port */

	/**
	 * Open socket
	 *
	 * @param node the hostname or ASCII encoded IP address
	 * @param the ASCII encoded portnum or service as in /etc/services
	 * @return file descriptor, <0 on error
	 */
	int _openSocket(const char *node, const char *service);
	int _openDevice(struct termios *old_tio, speed_t baudrate);
};

#endif /* _D0_H_ */


More information about the volkszaehler-dev mailing list