Tasmota/lib/lib_div/ams/han_Parser.cpp

275 lines
7.5 KiB
C++

#include "han_Parser.h"
#include "Cosem.h"
extern int SML_print(const char *, ...);
#define han_debug SML_print
Han_Parser::Han_Parser(uint16_t (dp)(uint8_t, uint8_t), uint8_t m, uint8_t *key, uint8_t *auth) {
dispatch = dp;
meter = m;
memmove(encryptionKey, key, 16);
if (auth) {
memmove(authenticationKey, auth, 16);
} else {
memset(authenticationKey, 0, 16);
}
}
Han_Parser::~Han_Parser(void) {
if (hdlcParser) delete hdlcParser;
if (mbusParser) delete mbusParser;
if (gbtParser) delete gbtParser;
if (gcmParser) delete gcmParser;
if (llcParser) delete llcParser;
if (dlmsParser) delete dlmsParser;
if (dsmrParser) delete dsmrParser;
}
int Han_Parser::serial_available(void) {
return dispatch(meter, 0);
}
int Han_Parser::serial_read(void) {
return dispatch(meter, 1);
}
int16_t Han_Parser::serial_readBytes(uint8_t *buf, uint16_t size) {
if (size > serial_available()) {
size = serial_available();
}
for (uint16_t cnt = 0; cnt < size; cnt++) {
buf[cnt] = serial_read();
}
return size;
}
bool Han_Parser::readHanPort(uint8_t **out, uint16_t *size, uint8_t flags) {
if (!serial_available()) return false;
// Before reading, empty serial buffer to increase chance of getting first byte of a data transfer
if (!serialInit) {
serial_readBytes(hanBuffer, BUF_SIZE_HAN);
serialInit = true;
return false;
}
DataParserContext ctx = {0};
ctx.flags = flags;
int pos = DATA_PARSE_INCOMPLETE;
// For each byte received, check if we have a complete frame we can handle
while (serial_available() && pos == DATA_PARSE_INCOMPLETE) {
yield();
// If buffer was overflowed, reset
if (len >= BUF_SIZE_HAN) {
serial_readBytes(hanBuffer, BUF_SIZE_HAN);
len = 0;
han_debug(PSTR("Buffer overflow, resetting"));
return false;
}
hanBuffer[len++] = serial_read();
ctx.length = len;
pos = unwrapData((uint8_t *) hanBuffer, ctx);
if(ctx.type > 0 && pos >= 0) {
if(ctx.type == DATA_TAG_DLMS) {
han_debug(PSTR("Received valid DLMS at %d"), pos);
} else if(ctx.type == DATA_TAG_DSMR) {
han_debug(PSTR("Received valid DSMR at %d"), pos);
} else {
// TODO: Move this so that payload is sent to MQTT
han_debug(PSTR("Unknown tag %02X at pos %d"), ctx.type, pos);
len = 0;
return false;
}
}
}
if (pos == DATA_PARSE_INCOMPLETE) {
return false;
} else if(pos == DATA_PARSE_UNKNOWN_DATA) {
han_debug(PSTR("Unknown data payload:"));
len = len + serial_readBytes(hanBuffer + len, BUF_SIZE_HAN - len);
//debugPrint(hanBuffer, 0, len);
len = 0;
return false;
}
if (pos == DATA_PARSE_INTERMEDIATE_SEGMENT) {
len = 0;
return false;
} else if (pos < 0) {
printHanReadError(pos);
len += serial_readBytes(hanBuffer + len, BUF_SIZE_HAN - len);
while (serial_available()) serial_read(); // Make sure it is all empty, in case we overflowed buffer above
len = 0;
return false;
}
// Data is valid, clear the rest of the buffer to avoid tainted parsing
for (int i = pos + ctx.length; i < BUF_SIZE_HAN; i++) {
hanBuffer[i] = 0x00;
}
//AmsData data;
char* payload = ((char *) (hanBuffer)) + pos;
if (ctx.type == DATA_TAG_DLMS) {
han_debug(PSTR("Using application data:"));
//if (Debug.isActive(RemoteDebug::VERBOSE)) debugPrint((byte*) payload, 0, ctx.length);
// Rudimentary detector for L&G proprietary format
if (payload[0] == CosemTypeStructure && payload[2] == CosemTypeArray && payload[1] == payload[3]) {
//data = LNG(payload, meterState.getMeterType(), &meterConfig, ctx, &Debug);
} else {
// TODO: Split IEC6205675 into DataParserKaifa and DataParserObis. This way we can add other means of parsing, for those other proprietary formats
//data = IEC6205675(payload, meterState.getMeterType(), &meterConfig, ctx);
}
} else if(ctx.type == DATA_TAG_DSMR) {
//data = IEC6205621(payload);
}
*out = hanBuffer + pos;
*size = ctx.length;
len = 0;
return true;
}
int16_t Han_Parser::unwrapData(uint8_t *buf, DataParserContext &context) {
int16_t ret = 0;
bool doRet = false;
uint16_t end = BUF_SIZE_HAN;
uint8_t tag = (*buf);
uint8_t lastTag = DATA_TAG_NONE;
while (tag != DATA_TAG_NONE) {
int16_t curLen = context.length;
int8_t res = 0;
switch(tag) {
case DATA_TAG_HDLC:
if (hdlcParser == NULL) hdlcParser = new HDLCParser();
res = hdlcParser->parse(buf, context);
break;
case DATA_TAG_MBUS:
if (mbusParser == NULL) mbusParser = new MBUSParser();
res = mbusParser->parse(buf, context);
break;
case DATA_TAG_GBT:
if (gbtParser == NULL) gbtParser = new GBTParser();
res = gbtParser->parse(buf, context);
break;
case DATA_TAG_GCM:
if (gcmParser == NULL) gcmParser = new GCMParser(encryptionKey, authenticationKey);
res = gcmParser->parse(buf, context);
break;
case DATA_TAG_LLC:
if (llcParser == NULL) llcParser = new LLCParser();
res = llcParser->parse(buf, context);
break;
case DATA_TAG_DLMS:
if (dlmsParser == NULL) dlmsParser = new DLMSParser();
res = dlmsParser->parse(buf, context);
if (res >= 0) doRet = true;
break;
case DATA_TAG_DSMR:
if (dsmrParser == NULL) dsmrParser = new DSMRParser();
res = dsmrParser->parse(buf, context, lastTag != DATA_TAG_NONE);
if (res >= 0) doRet = true;
break;
default:
han_debug(PSTR("Ended up in default case while unwrapping...(tag %02X)"), tag);
return DATA_PARSE_UNKNOWN_DATA;
}
lastTag = tag;
if (res == DATA_PARSE_INCOMPLETE) {
return res;
}
if (context.length > end) return false;
if (Debug) {
switch(tag) {
case DATA_TAG_HDLC:
han_debug(PSTR("HDLC frame:"));
break;
case DATA_TAG_MBUS:
han_debug(PSTR("MBUS frame:"));
break;
case DATA_TAG_GBT:
han_debug(PSTR("GBT frame:"));
break;
case DATA_TAG_GCM:
han_debug(PSTR("GCM frame:"));
break;
case DATA_TAG_LLC:
han_debug(PSTR("LLC frame:"));
break;
case DATA_TAG_DLMS:
han_debug(PSTR("DLMS frame:"));
break;
case DATA_TAG_DSMR:
han_debug(PSTR("DSMR frame:"));
break;
}
}
if (res == DATA_PARSE_FINAL_SEGMENT) {
if (tag == DATA_TAG_MBUS) {
res = mbusParser->write(buf, context);
}
}
if (res < 0) {
return res;
}
buf += res;
end -= res;
ret += res;
// If we are ready to return, do that
if (doRet) {
context.type = tag;
return ret;
}
// Use start byte of new buffer position as tag for next round in loop
tag = (*buf);
}
han_debug(PSTR("Got to end of unwrap method..."));
return DATA_PARSE_UNKNOWN_DATA;
}
void Han_Parser::printHanReadError(int16_t pos) {
switch(pos) {
case DATA_PARSE_BOUNDRY_FLAG_MISSING:
han_debug(PSTR("Boundry flag missing"));
break;
case DATA_PARSE_HEADER_CHECKSUM_ERROR:
han_debug(PSTR("Header checksum error"));
break;
case DATA_PARSE_FOOTER_CHECKSUM_ERROR:
han_debug(PSTR("Frame checksum error"));
break;
case DATA_PARSE_INCOMPLETE:
han_debug(PSTR("Received frame is incomplete"));
break;
case GCM_AUTH_FAILED:
han_debug(PSTR("Decrypt authentication failed"));
break;
case GCM_ENCRYPTION_KEY_FAILED:
han_debug(PSTR("Setting decryption key failed"));
break;
case GCM_DECRYPT_FAILED:
han_debug(PSTR("Decryption failed"));
break;
case MBUS_FRAME_LENGTH_NOT_EQUAL:
han_debug(PSTR("Frame length mismatch"));
break;
case DATA_PARSE_INTERMEDIATE_SEGMENT:
han_debug(PSTR("Intermediate segment received"));
break;
case DATA_PARSE_UNKNOWN_DATA:
han_debug(PSTR("Unknown data format %02X"), hanBuffer[0]);
break;
default:
han_debug(PSTR("Unspecified error while reading data: %d"), pos);
break;
}
}