293 lines
8.2 KiB
C++
293 lines
8.2 KiB
C++
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2022 Thomas Basler and others
|
|
*/
|
|
#include "AlarmLogParser.h"
|
|
#include <cstring>
|
|
|
|
void AlarmLogParser::clearBuffer()
|
|
{
|
|
memset(_payloadAlarmLog, 0, ALARM_LOG_PAYLOAD_SIZE);
|
|
_alarmLogLength = 0;
|
|
}
|
|
|
|
void AlarmLogParser::appendFragment(uint8_t offset, uint8_t* payload, uint8_t len)
|
|
{
|
|
if (offset + len > ALARM_LOG_PAYLOAD_SIZE) {
|
|
Serial.printf("FATAL: (%s, %d) stats packet too large for buffer (%d > %d)\n", __FILE__, __LINE__, offset + len, ALARM_LOG_PAYLOAD_SIZE);
|
|
return;
|
|
}
|
|
memcpy(&_payloadAlarmLog[offset], payload, len);
|
|
_alarmLogLength += len;
|
|
}
|
|
|
|
uint8_t AlarmLogParser::getEntryCount()
|
|
{
|
|
return (_alarmLogLength - 2) / ALARM_LOG_ENTRY_SIZE;
|
|
}
|
|
|
|
void AlarmLogParser::setLastAlarmRequestSuccess(LastCommandSuccess status)
|
|
{
|
|
_lastAlarmRequestSuccess = status;
|
|
}
|
|
|
|
LastCommandSuccess AlarmLogParser::getLastAlarmRequestSuccess()
|
|
{
|
|
return _lastAlarmRequestSuccess;
|
|
}
|
|
|
|
void AlarmLogParser::getLogEntry(uint8_t entryId, AlarmLogEntry_t* entry)
|
|
{
|
|
uint8_t entryStartOffset = 2 + entryId * ALARM_LOG_ENTRY_SIZE;
|
|
|
|
int timezoneOffset = getTimezoneOffset();
|
|
|
|
uint32_t wcode = (uint16_t)_payloadAlarmLog[entryStartOffset] << 8 | _payloadAlarmLog[entryStartOffset + 1];
|
|
uint32_t startTimeOffset = 0;
|
|
if (((wcode >> 13) & 0x01) == 1) {
|
|
startTimeOffset = 12 * 60 * 60;
|
|
}
|
|
|
|
uint32_t endTimeOffset = 0;
|
|
if (((wcode >> 12) & 0x01) == 1) {
|
|
endTimeOffset = 12 * 60 * 60;
|
|
}
|
|
|
|
entry->MessageId = _payloadAlarmLog[entryStartOffset + 1];
|
|
entry->StartTime = (((uint16_t)_payloadAlarmLog[entryStartOffset + 4] << 8) | ((uint16_t)_payloadAlarmLog[entryStartOffset + 5])) + startTimeOffset + timezoneOffset;
|
|
entry->EndTime = ((uint16_t)_payloadAlarmLog[entryStartOffset + 6] << 8) | ((uint16_t)_payloadAlarmLog[entryStartOffset + 7]);
|
|
|
|
if (entry->EndTime > 0) {
|
|
entry->EndTime += (endTimeOffset + timezoneOffset);
|
|
}
|
|
|
|
switch (entry->MessageId) {
|
|
case 1:
|
|
entry->Message = F("Inverter start");
|
|
break;
|
|
case 2:
|
|
entry->Message = F("DTU command failed");
|
|
break;
|
|
case 121:
|
|
entry->Message = F("Over temperature protection");
|
|
break;
|
|
case 124:
|
|
entry->Message = F("Shut down by remote control");
|
|
break;
|
|
case 125:
|
|
entry->Message = F("Grid configuration parameter error");
|
|
break;
|
|
case 126:
|
|
entry->Message = F("Software error code 126");
|
|
break;
|
|
case 127:
|
|
entry->Message = F("Firmware error");
|
|
break;
|
|
case 128:
|
|
entry->Message = F("Software error code 128");
|
|
break;
|
|
case 129:
|
|
entry->Message = F("Abnormal bias");
|
|
break;
|
|
case 130:
|
|
entry->Message = F("Offline");
|
|
break;
|
|
case 141:
|
|
entry->Message = F("Grid: Grid overvoltage");
|
|
break;
|
|
case 142:
|
|
entry->Message = F("Grid: 10 min value grid overvoltage");
|
|
break;
|
|
case 143:
|
|
entry->Message = F("Grid: Grid undervoltage");
|
|
break;
|
|
case 144:
|
|
entry->Message = F("Grid: Grid overfrequency");
|
|
break;
|
|
case 145:
|
|
entry->Message = F("Grid: Grid underfrequency");
|
|
break;
|
|
case 146:
|
|
entry->Message = F("Grid: Rapid grid frequency change rate");
|
|
break;
|
|
case 147:
|
|
entry->Message = F("Grid: Power grid outage");
|
|
break;
|
|
case 148:
|
|
entry->Message = F("Grid: Grid disconnection");
|
|
break;
|
|
case 149:
|
|
entry->Message = F("Grid: Island detected");
|
|
break;
|
|
case 205:
|
|
entry->Message = F("MPPT-A: Input overvoltage");
|
|
break;
|
|
case 206:
|
|
entry->Message = F("MPPT-B: Input overvoltage");
|
|
break;
|
|
case 207:
|
|
entry->Message = F("MPPT-A: Input undervoltage");
|
|
break;
|
|
case 208:
|
|
entry->Message = F("MPPT-B: Input undervoltage");
|
|
break;
|
|
case 209:
|
|
entry->Message = F("PV-1: No input");
|
|
break;
|
|
case 210:
|
|
entry->Message = F("PV-2: No input");
|
|
break;
|
|
case 211:
|
|
entry->Message = F("PV-3: No input");
|
|
break;
|
|
case 212:
|
|
entry->Message = F("PV-4: No input");
|
|
break;
|
|
case 213:
|
|
entry->Message = F("MPPT-A: PV-1 & PV-2 abnormal wiring");
|
|
break;
|
|
case 214:
|
|
entry->Message = F("MPPT-B: PV-3 & PV-4 abnormal wiring");
|
|
break;
|
|
case 215:
|
|
entry->Message = F("PV-1: Input overvoltage");
|
|
break;
|
|
case 216:
|
|
entry->Message = F("PV-1: Input undervoltage");
|
|
break;
|
|
case 217:
|
|
entry->Message = F("PV-2: Input overvoltage");
|
|
break;
|
|
case 218:
|
|
entry->Message = F("PV-2: Input undervoltage");
|
|
break;
|
|
case 219:
|
|
entry->Message = F("PV-3: Input overvoltage");
|
|
break;
|
|
case 220:
|
|
entry->Message = F("PV-3: Input undervoltage");
|
|
break;
|
|
case 221:
|
|
entry->Message = F("PV-4: Input overvoltage");
|
|
break;
|
|
case 222:
|
|
entry->Message = F("PV-4: Input undervoltage");
|
|
break;
|
|
case 301:
|
|
entry->Message = F("Hardware error code 301");
|
|
break;
|
|
case 302:
|
|
entry->Message = F("Hardware error code 302");
|
|
break;
|
|
case 303:
|
|
entry->Message = F("Hardware error code 303");
|
|
break;
|
|
case 304:
|
|
entry->Message = F("Hardware error code 304");
|
|
break;
|
|
case 305:
|
|
entry->Message = F("Hardware error code 305");
|
|
break;
|
|
case 306:
|
|
entry->Message = F("Hardware error code 306");
|
|
break;
|
|
case 307:
|
|
entry->Message = F("Hardware error code 307");
|
|
break;
|
|
case 308:
|
|
entry->Message = F("Hardware error code 308");
|
|
break;
|
|
case 309:
|
|
entry->Message = F("Hardware error code 309");
|
|
break;
|
|
case 310:
|
|
entry->Message = F("Hardware error code 310");
|
|
break;
|
|
case 311:
|
|
entry->Message = F("Hardware error code 311");
|
|
break;
|
|
case 312:
|
|
entry->Message = F("Hardware error code 312");
|
|
break;
|
|
case 313:
|
|
entry->Message = F("Hardware error code 313");
|
|
break;
|
|
case 314:
|
|
entry->Message = F("Hardware error code 314");
|
|
break;
|
|
case 5041:
|
|
entry->Message = F("Error code-04 Port 1");
|
|
break;
|
|
case 5042:
|
|
entry->Message = F("Error code-04 Port 2");
|
|
break;
|
|
case 5043:
|
|
entry->Message = F("Error code-04 Port 3");
|
|
break;
|
|
case 5044:
|
|
entry->Message = F("Error code-04 Port 4");
|
|
break;
|
|
case 5051:
|
|
entry->Message = F("PV Input 1 Overvoltage/Undervoltage");
|
|
break;
|
|
case 5052:
|
|
entry->Message = F("PV Input 2 Overvoltage/Undervoltage");
|
|
break;
|
|
case 5053:
|
|
entry->Message = F("PV Input 3 Overvoltage/Undervoltage");
|
|
break;
|
|
case 5054:
|
|
entry->Message = F("PV Input 4 Overvoltage/Undervoltage");
|
|
break;
|
|
case 5060:
|
|
entry->Message = F("Abnormal bias");
|
|
break;
|
|
case 5070:
|
|
entry->Message = F("Over temperature protection");
|
|
break;
|
|
case 5080:
|
|
entry->Message = F("Grid Overvoltage/Undervoltage");
|
|
break;
|
|
case 5090:
|
|
entry->Message = F("Grid Overfrequency/Underfrequency");
|
|
break;
|
|
case 5100:
|
|
entry->Message = F("Island detected");
|
|
break;
|
|
case 5120:
|
|
entry->Message = F("EEPROM reading and writing error");
|
|
break;
|
|
case 5150:
|
|
entry->Message = F("10 min value grid overvoltage");
|
|
break;
|
|
case 5200:
|
|
entry->Message = F("Firmware error");
|
|
break;
|
|
case 8310:
|
|
entry->Message = F("Shut down");
|
|
break;
|
|
case 9000:
|
|
entry->Message = F("Microinverter is suspected of being stolen");
|
|
break;
|
|
default:
|
|
entry->Message = F("Unknown");
|
|
break;
|
|
}
|
|
}
|
|
|
|
int AlarmLogParser::getTimezoneOffset()
|
|
{
|
|
// see: https://stackoverflow.com/questions/13804095/get-the-time-zone-gmt-offset-in-c/44063597#44063597
|
|
|
|
time_t gmt, rawtime = time(NULL);
|
|
struct tm* ptm;
|
|
|
|
struct tm gbuf;
|
|
ptm = gmtime_r(&rawtime, &gbuf);
|
|
|
|
// Request that mktime() looksup dst in timezone database
|
|
ptm->tm_isdst = -1;
|
|
gmt = mktime(ptm);
|
|
|
|
return static_cast<int>(difftime(rawtime, gmt));
|
|
} |