215 lines
6.4 KiB
C
215 lines
6.4 KiB
C
/**
|
|
* OBIS IDs as specified in DIN EN 62056-61
|
|
*
|
|
* @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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "obis.h"
|
|
#include "common.h"
|
|
|
|
#define DC 0xff // wildcard, dont care
|
|
|
|
static const obis_alias_t aliases[] = {
|
|
/* A B C D E F alias description
|
|
====================================================================*/
|
|
|
|
/* general */
|
|
{{{ 1, 0, 1, 7, DC, DC}}, "power", "Wirkleistung (Summe)"},
|
|
{{{ 1, 0, 21, 7, DC, DC}}, "power-l1", "Wirkleistung (Phase 1)"},
|
|
{{{ 1, 0, 41, 7, DC, DC}}, "power-l2", "Wirkleistung (Phase 2)"},
|
|
{{{ 1, 0, 61, 7, DC, DC}}, "power-l3", "Wirkleistung (Phase 3)"},
|
|
|
|
{{{ 1, 0, 12, 7, DC, DC}}, "voltage", "Spannung (Mittelwert)"},
|
|
{{{ 1, 0, 32, 7, DC, DC}}, "voltage-l1", "Spannung (Phase 1)"},
|
|
{{{ 1, 0, 52, 7, DC, DC}}, "voltage-l2", "Spannung (Phase 2)"},
|
|
{{{ 1, 0, 72, 7, DC, DC}}, "voltage-l3", "Spannung (Phase 3)"},
|
|
|
|
{{{ 1, 0, 11, 7, DC, DC}}, "current", "Stromstaerke (Summe)"},
|
|
{{{ 1, 0, 31, 7, DC, DC}}, "current-l1", "Stromstaerke (Phase 1)"},
|
|
{{{ 1, 0, 51, 7, DC, DC}}, "current-l2", "Stromstaerke (Phase 2)"},
|
|
{{{ 1, 0, 71, 7, DC, DC}}, "current-l3", "Stromstaerke (Phase 3)"},
|
|
|
|
{{{ 1, 0, 14, 7, 0, DC}}, "frequency", "Netzfrequenz"},
|
|
{{{ 1, 0, 12, 7, 0, DC}}, "powerfactor", "Leistungsfaktor"},
|
|
|
|
{{{ 0, 0, 96, 1, DC, DC}}, "device", "Zaehler Seriennr."},
|
|
{{{ 1, 0, 96, 5, 5, DC}}, "status", "Zaehler Status"},
|
|
|
|
{{{ 1, 0, 1, 8, DC, DC}}, "counter", "Zaehlerstand Wirkleistung"},
|
|
{{{ 1, 0, 2, 8, DC, DC}}, "counter-out", "Zaehlerstand Lieferg."},
|
|
|
|
/* Easymeter */
|
|
|
|
/* ESYQ3B (Easymeter Q3B) */
|
|
{{{ 1, 0, 1, 8, 1, DC}}, "esy-counter-t1", "Active Power Counter Tariff 1"},
|
|
{{{ 1, 0, 1, 8, 2, DC}}, "esy-counter-t2", "Active Power Counter Tariff 2"},
|
|
//{{{129, 129, 199, 130, 3, DC}}, "", ""}, // ???
|
|
|
|
/* ESYQ3D (Easymeter Q3D) */
|
|
//{{{ 0, 0, 0, 0, 0, DC}}, "", ""}, // ???
|
|
|
|
/* HAG eHZ010C_EHZ1WA02 (Hager eHz) */
|
|
{{{ 1, 0, 0, 0, 0, DC}}, "hag-id", "Eigentumsnr."},
|
|
{{{ 1, 0, 96, 50, 0, 0}}, "hag-status", "Netz Status"}, /* bitcodiert: Drehfeld, Anlaufschwelle, Energierichtung */
|
|
{{{ 1, 0, 96, 50, 0, 1}}, "hag-frequency", "Netz Periode"}, /* hexadezimal (Einheit 1/100 ms) */
|
|
{{{ 1, 0, 96, 50, 0, 2}}, "hag-temp", "aktuelle Chiptemperatur"}, /* hexadezimal, Einheit °C */
|
|
{{{ 1, 0, 96, 50, 0, 3}}, "hag-temp-min", "minimale Chiptemperatur"},
|
|
{{{ 1, 0, 96, 50, 0, 4}}, "hag-temp-avg", "gemittelte Chiptemperatur"},
|
|
{{{ 1, 0, 96, 50, 0, 5}}, "hag-temp-max", "maximale Chiptemperatur"},
|
|
{{{ 1, 0, 96, 50, 0, 6}}, "hag-check", "Kontrollnr."},
|
|
{{{ 1, 0, 96, 50, 0, 7}}, "hag-diag", "Diagnose"},
|
|
|
|
{} /* stop condition for iterator */
|
|
};
|
|
|
|
|
|
const obis_alias_t * obis_get_aliases() {
|
|
return aliases;
|
|
}
|
|
|
|
obis_id_t * obis_init(obis_id_t *id, unsigned char *raw) {
|
|
if (raw == NULL) {
|
|
// TODO why not initialize with DC fields to accept all readings?
|
|
memset(id->raw, 0, 6); /* initialize with zeros */
|
|
}
|
|
else {
|
|
memcpy(id->raw, raw, 6);
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
int obis_parse(const char *str, obis_id_t *id) {
|
|
enum { A = 0, B, C, D, E, F };
|
|
|
|
char byte; /* currently processed byte */
|
|
int num;
|
|
int field;
|
|
int len = strlen(str);
|
|
|
|
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 < len; i++) {
|
|
byte = str[i];
|
|
|
|
if (isdigit(byte)) {
|
|
num = (num * 10) + (byte - '0'); /* parse digits */
|
|
}
|
|
else {
|
|
if (byte == '-' && field < A) { /* end of field A */
|
|
field = A;
|
|
}
|
|
else if (byte == ':' && field < B) { /* end of field B */
|
|
field = B;
|
|
}
|
|
else if (byte == '.' && field < D) { /* end of field C & D*/
|
|
field = (field < C) ? C : D;
|
|
}
|
|
else if ((byte == '*' || byte == '&') && field == D) { /* end of field E, start of field F */
|
|
field = E;
|
|
}
|
|
else {
|
|
return ERR;
|
|
}
|
|
|
|
id->raw[field] = num;
|
|
num = 0;
|
|
}
|
|
}
|
|
|
|
/* set last field */
|
|
id->raw[++field] = num;
|
|
|
|
/* fields C & D are mandatory */
|
|
return (field < D) ? ERR : SUCCESS;
|
|
}
|
|
|
|
int obis_lookup_alias(const char *alias, obis_id_t *id) {
|
|
for (const obis_alias_t *it = aliases; it != NULL; it++) {
|
|
if (strcmp(it->name, alias) == 0) {
|
|
*id = it->id;
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
return ERR_NOT_FOUND;
|
|
}
|
|
|
|
int obis_unparse(obis_id_t id, char *buffer, size_t n) {
|
|
return snprintf(buffer, n, "%i-%i:%i.%i.%i*%i",
|
|
id.groups.media,
|
|
id.groups.channel,
|
|
id.groups.indicator,
|
|
id.groups.mode,
|
|
id.groups.quantities,
|
|
id.groups.storage
|
|
);
|
|
}
|
|
|
|
int obis_compare(obis_id_t a, obis_id_t b) {
|
|
for (int i = 0; i < 6; i++) {
|
|
if (a.raw[i] == b.raw[i] || a.raw[i] == 0xff || b.raw[i] == 0xff ) {
|
|
continue; /* skip on wildcard or equal */
|
|
}
|
|
else if (a.raw[i] < b.raw[i]) {
|
|
return -1;
|
|
}
|
|
else if (a.raw[i] > b.raw[i]) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0; /* equal */
|
|
}
|
|
|
|
int obis_is_null(obis_id_t id) {
|
|
return !(
|
|
id.raw[0] ||
|
|
id.raw[1] ||
|
|
id.raw[2] ||
|
|
id.raw[3] ||
|
|
id.raw[4] ||
|
|
id.raw[5]
|
|
);
|
|
}
|
|
|
|
int obis_is_manufacturer_specific(obis_id_t id) {
|
|
return (
|
|
(id.groups.channel >= 128 && id.groups.channel <= 199) ||
|
|
(id.groups.indicator >= 128 && id.groups.indicator <= 199) ||
|
|
(id.groups.indicator == 240) ||
|
|
(id.groups.mode >= 128 && id.groups.mode <= 254) ||
|
|
(id.groups.quantities >= 128 && id.groups.quantities <= 254) ||
|
|
(id.groups.storage >= 128 && id.groups.storage <= 254)
|
|
);
|
|
}
|
|
|