Hallo,
ich bin es nochmal. Ich hab ein sleep in der Schleife vergessen. Der
vzlogger funktioniert im Prinzip, aber ohne das sleep hat er 100%
CPU-Auslastung.
Anbei mal eine berichtigte Version.
Gruß
Sebastian
Am 2013-12-12 07:41, schrieb Michael Wulz:
Guten Morgen,
besten Dank für das Diff.
Werde es gleich bei mir probieren - hatte auch immer die gleichen
Probleme. Damit wird mein Logger auch stabiler rennen ;-)
merci
Am 12.12.13 07:35, schrieb Sebastian Michel:
Hallo Zusammen,
ich habe bei mir einen opt. USB-Lesekopf von Udo in Verbindung mit
einem Siemens TD-3511 am Laufen. Das ganze hängt an einem raspberry pi
mit image von volkszähler.
Leider lief es nicht so problemlos wie erhofft. Der vzlogger hat zwar
die pullseq geschickt, aber dann keine Daten empfangen. Ich hab mir
dann die Quellen vom vzlogger angeschaut und folgende Änderungen in
MeterD0.cpp gemacht, dass es läuft:
1) Das Device wird im blocking mode geöffnet. Das führte bei mir dazu,
dass die read() Funktion mit 0 zurückkehrt. Das heißt normalerweise
ein eof oder derartiges. Sowas sollte bei einem seriellen Device
niemals passieren. Ich weiß auch nicht warum das der Fall ist.
-> Ich hab den Code dahingehend geändert, dass das Device im
non-blocking mode geöffnet wird. Damit funktioniert's nun.
2) Die Statemachine sollte niemals unendlich warten und irgendwo
hängen bleiben.
-> Ich hab ein Timeout von 10s eingebaut. Also wenn innerhalb 10s
keine Daten empfangen werden bzw. beim Start kein Sync-byte kommt,
wird dieser Lesevorgang abgebrochen.
3) Die Statemaching wird immer beim Empfang eines '/' zurückgesetzt.
Da mein Zähler ein '/' im Identification String sendet, hängt sich die
Statemachine auf.
-> Das habe ich rausgenommen.
4) Mein Zähler sendet ca. 330 obis-codes. Da ist dann sowas dabei wie
2.8.1*16, 2.8.1*15 usw. Ich interessiere mich eigentlich nur für die
Einträge von 2.8.0. Das ist insofern ein Problem, dass der D0-Meter
nur maximal 32 Einträge zulässt. Das heißt bis die interessanten codes
ankommen, sind die 32 Einträge schon längst belegt.
-> Das ist vielleicht nicht ganz sauber, aber ich nehme nur obis-codes
an die eine Länge von genau 5 bytes haben. Damit kommen bei meinem
Zähler genau 32 zusammen.
5) Der Zähler sendet ungültige Obis-Codes. Zumindest ist der string
für die Klasse Obis.cpp nicht auswertbar. Das führt dazu, dass
vzlogger abstürzt. Vermutlich sind es die obis-codes die unter anderem
Buchstaben statt Zahlen haben.
-> Ich hab die Stellen mit einem try-except Block abgefangen und
überspringe die betroffenen obis-codes.
Alles in allem freue ich mich, dass es nun funktioniert. Die
Änderungen habe ich als Diff mal angehangen.
Viele Grüße
Sebastian
diff --git a/src/Obis.cpp b/src/Obis.cpp
index f918797..eebf5f8 100644
--- a/src/Obis.cpp
+++ b/src/Obis.cpp
@@ -192,7 +192,7 @@ int Obis::parse(const char *str) {
}
int Obis::lookup_alias(const char *alias) {
- for (const obis_alias_t *it = aliases; it != NULL; it++) {
+ for (const obis_alias_t *it = aliases; !it->id.isNull(); it++) {
if (strcmp(it->name, alias) == 0) {
*this = it->id;
return SUCCESS;
diff --git a/src/protocols/MeterD0.cpp b/src/protocols/MeterD0.cpp
index f48f864..e3e34b3 100644
--- a/src/protocols/MeterD0.cpp
+++ b/src/protocols/MeterD0.cpp
@@ -204,6 +204,13 @@ ssize_t MeterD0::read(std::vector<Reading>&rds, size_t max_readings) {
char byte; /* we parse our input byte wise */
int byte_iterator;
size_t number_of_tuples;
+ int bytes_read;
+
+ time_t start_time;
+ time_t end_time;
+
+ // get current time
+ time(&start_time);
if(_pull.size()) {
int wlen=write(_fd,_pull.c_str(),_pull.size());
@@ -215,12 +222,41 @@ ssize_t MeterD0::read(std::vector<Reading>&rds, size_t max_readings) {
context = START; /* start with context START */
- while (::read(_fd, &byte, 1)) {
- if (byte == '/') context = START; /* reset to START if "/" reoccurs */
- else if (byte == '!') context = END; /* "!" is the identifier for the END */
+ while (1) {
+
+ // check if timed out
+ time(&end_time);
+ if (difftime(end_time, start_time) > 10)
+ {
+ print(log_error, "nothing received for more than 10 seconds", name().c_str());
+ break;
+ }
+
+ // now read a single byte
+ bytes_read = ::read(_fd, &byte, 1);
+ if (bytes_read == 0 || bytes_read == -1 && errno == EAGAIN)
+ {
+ // wait and read again
+ usleep(5000); // 5 ms
+ continue;
+ }
+ else if (bytes_read == -1)
+ {
+ // an error occured reading a byte
+ print(log_error, "error reading a byte (%d)", name().c_str(), errno);
+ break;
+ }
+
+ // reset timeout
+ if (context != START)
+ {
+ time(&start_time);
+ }
+
+ 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 */
+ if (byte == '/') {
byte_iterator = number_of_tuples = 0; /* start */
context = VENDOR; /* set new context: START -> VENDOR */
}
@@ -239,6 +275,7 @@ ssize_t MeterD0::read(std::vector<Reading>&rds, size_t max_readings) {
case BAUDRATE: /* BAUDRATE consists of 1 char only */
baudrate = byte;
+ print(log_debug, "Meter supports baudrate of %d", name().c_str(), 300 * (1 << (baudrate-0x30)));
context = IDENTIFICATION; /* set new context: BAUDRATE -> IDENTIFICATION */
byte_iterator = 0;
break;
@@ -304,20 +341,25 @@ ssize_t MeterD0::read(std::vector<Reading>&rds, size_t max_readings) {
case END_LINE:
if (byte == '\r' || byte == '\n') {
/* free slots available and sain content? */
- if ((number_of_tuples < max_readings) && (strlen(obis_code) > 0) &&
+ if ((number_of_tuples < max_readings) && (strlen(obis_code) == 5) &&
(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;
+ try
+ {
+ Obis obis(obis_code);
+ rds[number_of_tuples].value(strtof(value, NULL));
+ ReadingIdentifier *rid(new ObisIdentifier(obis));
+ rds[number_of_tuples].identifier(rid);
+ rds[number_of_tuples].time();
+ number_of_tuples++;
+ } catch(std::exception &e)
+ {
+ print(log_debug, "--> skipped because of parse errors", name().c_str());
+ }
}
+
+ byte_iterator = 0;
+ context = OBIS_CODE;
}
break;
@@ -366,7 +408,7 @@ 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);
+ int fd = ::open(device(), O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd < 0) {
print(log_error, "open(%s): %s", name().c_str(), device(), strerror(errno));
return ERR;