DreherTankController/src/faults/faults.cpp

684 lines
19 KiB
C++

#include "faults.h"
#include "../types.h"
#include "../mystrings/mystrings.h"
#include "../macros.h"
#include <EEPROM.h>
extern u32 currMillis;
extern u16 zones;
extern u16 modbusAlarms[];
const u16 EEPROM_FAULTS[] = {
_EEPROM_FAULT_1, _EEPROM_FAULT_2, _EEPROM_FAULT_3,_EEPROM_FAULT_4, _EEPROM_FAULT_5,
_EEPROM_FAULT_6, _EEPROM_FAULT_7, _EEPROM_FAULT_8, _EEPROM_FAULT_9, _EEPROM_FAULT_10,
_EEPROM_FAULT_11, _EEPROM_FAULT_12, _EEPROM_FAULT_13, _EEPROM_FAULT_14, _EEPROM_FAULT_15,
_EEPROM_FAULT_16, _EEPROM_FAULT_17, _EEPROM_FAULT_18, _EEPROM_FAULT_19, _EEPROM_FAULT_20,
_EEPROM_FAULT_21, _EEPROM_FAULT_22, _EEPROM_FAULT_23, _EEPROM_FAULT_24, _EEPROM_FAULT_25,
_EEPROM_FAULT_26, _EEPROM_FAULT_27, _EEPROM_FAULT_28, _EEPROM_FAULT_29, _EEPROM_FAULT_30
};
Fault::Fault()
: code(invalidCode)
, active(false)
{
}
Fault::Fault(FaultCode fcode, const u16 id_)
: code(fcode)
, id(id_)
, counterActivated(0)
, counterDeactivated(0)
{
timeActivated = currMillis;
}
Fault::Fault(const Fault &other)
{
id = other.id;
code = other.code;
timeActivated = other.timeActivated;
timeDeactivated = other.timeDeactivated;
timeActivatedOverflow = other.timeActivatedOverflow;
timeDeactivatedOverflow = other.timeDeactivatedOverflow;
active = other.active;
counterActivated = other.counterActivated;
counterDeactivated = other.counterDeactivated;
_of1 = other._of1;
_of2 = other._of2;
}
Fault::Fault(const SavedFault &other)
{
id = other.id;
code = other.code;
active = other.active;
counterActivated = other.counterActivated + 1;
if (other.deactivated) {
timeDeactivated = 0;
counterDeactivated = other.counterDeactivated + 1;
} else {
counterDeactivated = 0;
}
}
static char __buff_1[30];
const char* Fault::title()
{
switch(code) {
case t1TooHi:
(zones > 1) ? getMyStr200(STR_T1_TOO_HI) : getMyStr200(STR_T_TOO_HI);
break;
case t1TooLo:
(zones > 1) ? getMyStr200(STR_T1_TOO_LO) : getMyStr200(STR_T_TOO_LO);
break;
case t2TooHi:
getMyStr200(STR_T2_TOO_HI);
break;
case t2TooLo:
getMyStr200(STR_T2_TOO_LO);
break;
case pTooHi:
getMyStr200(STR_P_TOO_HI);
break;
case pTooLo:
getMyStr200(STR_P_TOO_LO);
break;
case t1Invalid:
case t2Invalid:
getMyStr200(STR_T_FAIL);
break;
case pInvalid:
getMyStr200(STR_P_FAIL);
break;
default:
getMyStr200(STR_UNKNOWN_ERROR);
}
if (active) {
strcpy(__buff_1, "*");
strcat(__buff_1, charBuffer200);
return __buff_1;
}
return charBuffer200;
}
const char* Fault::activatedText()
{
const u32 diff = currMillis - timeActivated;
u16 result;
if (counterActivated) {
sprintf(__buff_1, getMyStr200(STR_UNKNOWN_ACTIVATED), counterActivated);
} else if (timeActivatedOverflow) {
return getMyStr200(STR_GT_7_WEEKS_1);
} else if (diff < 100000) { // < 100 Sekunden
result = diff / 1000;
sprintf(__buff_1, getMyStr200(STR_F_SECONDS_1), result);
} else if (diff < 6000000) { // < 100 Minuten
result = diff / 60000;
sprintf(__buff_1, getMyStr200(STR_F_MINUTES_1), result);
} else if (diff < 172800000) { // < 48 Stunden
result = diff / 3600000;
sprintf(__buff_1, getMyStr200(STR_F_HOURS_1), result);
} else { // Tage
result = diff / 86400000;
sprintf(__buff_1, getMyStr200(STR_F_DAYS_1), result);
}
return __buff_1;
}
const char* Fault::deactivatedText()
{
const u32 diff = currMillis - timeDeactivated;
u16 result;
if (timeDeactivatedOverflow) {
return getMyStr200(STR_GT_7_WEEKS_2);
} else if (counterDeactivated) {
sprintf(__buff_1, getMyStr200(STR_UNKNOWN_DEACTIVAED), counterDeactivated);
} else if (timeDeactivated == 0xFFFFFFFF) { // wurde nie deaktiviert
sprintf(__buff_1, (active) ? getMyStr200(STR_NEVER_DEACTIVATED_ACTIVE) : getMyStr200(STR_NEVER_DEACTIVATED_INACTIVE));
} else if (diff < 100000) { // < 100 Sekunden
result = diff / 1000;
sprintf(__buff_1, getMyStr200(STR_F_SECONDS_2), result);
} else if (diff < 6000000) { // < 100 Minuten
result = diff / 60000;
sprintf(__buff_1, getMyStr200(STR_F_MINUTES_2), result);
} else if (diff < 172800000) { // < 48 Stunden
result = diff / 3600000;
sprintf(__buff_1, getMyStr200(STR_F_HOURS_2), result);
} else { // Tage
result = diff / 86400000;
sprintf(__buff_1, getMyStr200(STR_F_DAYS_2), result);
}
return __buff_1;
}
void Fault::process()
{
if (!counterActivated) {
if (!_of1 && currMillis < timeActivated)
_of1 = true;
else if (!timeActivatedOverflow && _of1 && currMillis > timeActivated)
timeActivatedOverflow = true;
if (!active && !_of2 && currMillis < timeDeactivated)
_of2 = true;
else if (!timeDeactivatedOverflow && _of2 && currMillis > timeDeactivated)
timeDeactivatedOverflow = true;
}
}
FaultSettings faultSettings;
FaultList::FaultList()
{
EEPROM.get(_EEPROM_FAULT_LAST_ID, _lastID);
if (_lastID == 0xFFFF)
_lastID = 0;
uint8_t count = EEPROM.read(_EEPROM_FAULT_COUNT);
_size = (count > _MAX_FAULTS) ? 0 : count;
for (u8 i=0; i<_size; i++) {
SavedFault savedFault;
u16 id, reg_1, reg_2;
EEPROM.get(EEPROM_FAULTS[i], id);
EEPROM.get(EEPROM_FAULTS[i] + 2, reg_1);
EEPROM.get(EEPROM_FAULTS[i] + 4, reg_2);
u8 rawCode = reg_1 >> 12;
if (reg_2 >> 14 || rawCode > 9 || id == 0 || id == 0xFFFF) {
// unter einer dieser Bedingungen wären die Daten nicht gültig
savedFault.id = 0;
savedFault.code = invalidCode;
savedFault.active = false;
savedFault.deactivated = true;
savedFault.counterActivated = 0;
savedFault.counterDeactivated = 0;
} else {
savedFault.id = id;
savedFault.counterActivated = reg_1 & 0xFFF;
savedFault.counterDeactivated = reg_2 & 0xFFF;
savedFault.active = bitRead(reg_2, 12);
savedFault.deactivated = bitRead(reg_2, 13);
switch (rawCode) {
case t1TooHi:
savedFault.code = t1TooHi;
if (savedFault.active) {
_lastActiveT1Fault = savedFault.code;
_t1Active = true;
}
break;
case t1TooLo:
savedFault.code = t1TooLo;
if (savedFault.active) {
_lastActiveT1Fault = savedFault.code;
_t1Active = true;
}
break;
case t1Invalid:
savedFault.code = t1Invalid;
if (savedFault.active) {
_lastActiveT1Fault = savedFault.code;
_t1Active = true;
}
break;
case t2TooHi:
savedFault.code = t2TooHi;
if (savedFault.active) {
_lastActiveT2Fault = savedFault.code;
_t2Active = true;
}
break;
case t2TooLo:
savedFault.code = t2TooLo;
if (savedFault.active) {
_lastActiveT2Fault = savedFault.code;
_t2Active = true;
}
break;
case t2Invalid:
savedFault.code = t2Invalid;
if (savedFault.active) {
_lastActiveT2Fault = savedFault.code;
_t2Active = true;
}
break;
case pTooHi:
savedFault.code = pTooHi;
if (savedFault.active) {
_lastActivePFault = savedFault.code;
_pActive = true;
}
break;
case pTooLo:
savedFault.code = pTooLo;
if (savedFault.active) {
_lastActivePFault = savedFault.code;
_pActive = true;
}
break;
case pInvalid:
savedFault.code = pInvalid;
if (savedFault.active) {
_lastActivePFault = savedFault.code;
_pActive = true;
}
break;
default:
savedFault.code = invalidCode;
}
}
_list[i] = savedFault;
_save(i);
#if _MODBUS == 1
_setModbusAlarm(_list[i], i, true);
#endif
}
}
void FaultList::process()
{
FaultState s = noFault;
for (u8 i=0; i<_size; i++) {
Fault &f = _list[i];
f.process();
bool wasActive = f.active;
switch (f.code) {
case t1TooHi:
case t1TooLo:
case t1Invalid:
_test(f, _t1Active, _lastActiveT1Fault);
break;
case t2TooHi:
case t2TooLo:
case t2Invalid:
_test(f, _t2Active, _lastActiveT2Fault);
break;
case pTooHi:
case pTooLo:
case pInvalid:
_test(f, _pActive, _lastActivePFault);
}
switch (s) {
case inactiveFault:
if (f.active)
s = activeFault;
break;
case activeFault:
break;
default: // noFault
s = (f.active) ? activeFault : inactiveFault;
}
if (f.active != wasActive) {
// _printf("save in process(): ");
_save(i);
}
#if _MODBUS == 1
_setModbusAlarm(f, i, false);
#endif
}
_state = s;
}
bool FaultList::check(FaultCode code, int16_t val=0)
{
if (_size >= _MAX_FAULTS)
return false;
switch (code) {
case t1TooHi:
if (code == _lastActiveT1Fault || currMillis - _lastT1Idle < _CHECK_T_FAULT_INTERVAL
|| faultSettings.tMinFRate < _compT1 - vals.t1)
return false;
_t1Active = true;
_lastActiveT1Fault = code;
break;
case t1TooLo:
if (code == _lastActiveT1Fault || currMillis - _lastT1Idle < _CHECK_T_FAULT_INTERVAL
|| faultSettings.tMinFRate < vals.t1 - _compT1)
return false;
_t1Active = true;
_lastActiveT1Fault = code;
break;
case t1Invalid:
if (code == _lastActiveT1Fault || currMillis - _lastT1Idle < _CHECK_T_FAULT_INTERVAL)
return false;
_t1Active = true;
_lastActiveT1Fault = code;
break;
case t2TooHi:
if (code == _lastActiveT2Fault || currMillis - _lastT2Idle < _CHECK_T_FAULT_INTERVAL
|| faultSettings.tMinFRate < _compT2 - vals.t2)
return false;
_t2Active = true;
_lastActiveT2Fault = code;
break;
case t2TooLo:
if (code == _lastActiveT2Fault || currMillis - _lastT2Idle < _CHECK_T_FAULT_INTERVAL
|| faultSettings.tMinFRate < vals.t2 - _compT2)
return false;
_t2Active = true;
_lastActiveT2Fault = code;
break;
case t2Invalid:
if (code == _lastActiveT2Fault || currMillis - _lastT2Idle < _CHECK_T_FAULT_INTERVAL)
return false;
_t2Active = true;
_lastActiveT2Fault = code;
break;
case pTooHi:
if (code == _lastActivePFault || currMillis - _lastPIdle < _CHECK_P_FAULT_INTERVAL
|| faultSettings.pMinRate < _compP - val)
return false;
_pActive = true;
_lastActivePFault = code;
break;
case pTooLo:
if (code == _lastActivePFault || currMillis - _lastPIdle < _CHECK_P_FAULT_INTERVAL
|| faultSettings.pMinRate < val - _compP)
return false;
_pActive = true;
_lastActivePFault = code;
break;
case pInvalid:
if (code == _lastActivePFault || currMillis - _lastPIdle < _CHECK_P_FAULT_INTERVAL)
return false;
_pActive = true;
_lastActivePFault = code;
break;
}
_lastID++;
if (_lastID == 0xFFFF || _lastID == 0)
_lastID = 1;
Fault f(code, _lastID);
// _printf("Nächste Alarm-ID: "); _println(_lastID);
_list[_size] = f;
// _printf("save in check(): ");
_save(_size);
_saveLastID();
_size++;
_saveSize();
#if _MODBUS == 1
_setModbusAlarm(f, _size - 1, true);
#endif
return true;
}
u8 FaultList::count()
{
return _size;
}
Fault* FaultList::first()
{
if (_size)
return &_list[0];
return nullptr;
}
Fault* FaultList::last()
{
if (_size)
return &_list[_size-1];
return nullptr;
}
Fault* FaultList::at(const u8 position)
{
if (position < _size)
return &_list[position];
return nullptr;
}
bool FaultList::removeID(const u16 id)
{
for (u8 i=0; i<_size; i++) {
if (_list[i].id == id) {
return removeOne(i);
}
}
return false;
}
bool FaultList::removeOne(const u8 position)
{
if (position < _size) {
if (_list[position].active)
return false; // aktiver Fehler soll nicht gelöscht werden!
u8 i;
for (i=position; i<_size; i++) {
if (i < _MAX_FAULTS - 1)
_list[i] = _list[i + 1];
}
_size--;
#if _MODBUS == 1
if (!_size)
// normalerweise wird der Alarm-Counter in _setModbusAlarm() gesetzt,
// aber wenn _size == 0, muss der Counter extra zurückgesetzt werden
modbusAlarms[0] = 0;
#endif
if (!_removeAllActive) {
for (i=0; i<_size; i++)
_save(i);
_saveSize();
}
return true;
}
return false;
}
bool FaultList::removeAll()
{
bool atLeastOneRemoved = false;
if (!_size) {
return false;
} else if (_state != activeFault) {
_size = 0;
_saveSize();
atLeastOneRemoved = true;
#if _MODBUS == 1
// normalerweise wird der Alarm-Counter in _setModbusAlarm() gesetzt,
// aber wenn _size == 0, muss der Counter extra zurückgesetzt werden
modbusAlarms[0] = 0;
#endif
} else {
_removeAllActive = true;
bool finished = false;
u8 i;
while (!finished) {
for (i=0; i<_size; i++) {
if (!_list[i].active) { // aktiver Fehler soll nicht gelöscht werden!
removeOne(i);
atLeastOneRemoved = true;
break;
}
if (i >= _size - 1)
finished = true;
}
}
for (i=0; i<_size; i++)
_save(i);
_saveSize();
_removeAllActive = false;
}
return atLeastOneRemoved;
}
FaultState FaultList::state()
{
return _state;
}
void FaultList::t1Idle() {
static bool firstRun = true;
if (!_t1Active) {
return;
}
if (firstRun) {
// Um beim ersten Idle einen ev. gespeicherten Code nicht zurückzusetzen
firstRun = false;
return;
}
_t1Active = false;
_lastActiveT1Fault = invalidCode;
}
void FaultList::t1Idle(const int16_t val)
{
_compT1 = val;
_lastT1Idle = currMillis;
t1Idle();
}
void FaultList::t2Idle() {
static bool firstRun = true;
if (!_t2Active) {
return;
}
if (firstRun) {
// Um beim ersten Idle einen ev. gespeicherten Code nicht zurückzusetzen
firstRun = false;
return;
}
_t2Active = false;
_lastActiveT2Fault = invalidCode;
}
void FaultList::t2Idle(const int16_t val)
{
_compT2 = val;
_lastT2Idle = currMillis;
t2Idle();
}
void FaultList::pIdle() {
static bool firstRun = true;
if (!_pActive) {
return;
}
if (firstRun) {
// Um beim ersten Idle einen ev. gespeicherten Code nicht zurückzusetzen
firstRun = false;
return;
}
_pActive = false;
_lastActivePFault = invalidCode;
}
void FaultList::pIdle(const int16_t val)
{
_compP = val;
_lastPIdle = currMillis;
pIdle();
}
const char* FaultList::menu()
{
if (_size) {
_str = "";
for (u8 i=0; i<_size; i++) {
_str += _list[i].title();
if (i < _size - 1)
_str += '\n';
}
return _str.c_str();
} else {
return getMyStr503(STR_NO_ERROR);
}
}
bool FaultList::tActive()
{
return _t1Active || _t2Active;
}
bool FaultList::pActive()
{
return _pActive;
}
void FaultList::_save(const u8 index)
{
// _printf("Speichere Index "); _print(index);
if (index <= _size) {
u16 reg_1 = _list[index].counterActivated;
u16 reg_2 = _list[index].counterDeactivated;
reg_1 |= _list[index].code << 12; // setze Bit 12...15
if (_list[index].active)
reg_2 |= 0x1000; // Setze Bit 12
if (_list[index].timeDeactivated != 0xFFFFFFFF)
reg_2 |= 0x2000; // Setze Bit 13
EEPROM.put(EEPROM_FAULTS[index], _list[index].id);
EEPROM.put(EEPROM_FAULTS[index] + 2, reg_1);
EEPROM.put(EEPROM_FAULTS[index] + 4, reg_2);
// _printf(", code = "); _print(_list[index].code);
// _printf(", active = "); _print(_list[index].active);
}
// _println();
}
void FaultList::_saveSize()
{
EEPROM.update(_EEPROM_FAULT_COUNT, _size);
}
void FaultList::_saveLastID()
{
EEPROM.put(_EEPROM_FAULT_LAST_ID, _lastID);
}
void FaultList::_test(Fault &f, const bool &groupActive, const FaultCode &lastActiveFault)
{
if (f.active && !groupActive) {
f.timeDeactivated =
(currMillis == 0xFFFFFFFF) ? 0xFFFFFFFE :
(currMillis == 0) ? 1 : currMillis;
f.active = false;
} else if (f.code != lastActiveFault)
f.active = false;
}
#if _MODBUS == 1
void FaultList::_setModbusAlarm(const Fault &fault, const u8 index, const bool new_)
{
u16 reg_1 = 0;
u16 reg_2 = 0;
u16 timestamp = modbusData[5] & 0xFFF;
if (fault.counterActivated) {
reg_1 = fault.counterActivated;
reg_2 = 0x1000; // Bit 12 setzen
} else if (fault.timeActivatedOverflow) {
reg_1 = 0xFFF;
reg_2 = 0x2000; // Bit 13 setzen
} else {
reg_1 = timestamp;
}
reg_1 |= fault.code << 12;
if (fault.counterDeactivated) {
reg_2 |= fault.counterDeactivated;
reg_2 |= 0x4000; // Bit 14 setzen
} else if (fault.timeDeactivatedOverflow) {
reg_2 |= 0xFFF;
reg_2 |= 0x8000; // Bit 15 setzen
} else if (fault.timeDeactivated == 0xFFFFFFFF) {
// wurde nie deaktiviert
if (!fault.active) {
// Der Alarm wurde nicht deaktiviert, sondern von einem neuen Alarm abgelöst
reg_2 |= 1;
} // else: nichts tun, die 12 MSBs von reg_2 bleiben 0
} else {
// Die Werte 0 und 1 sind reserviert, also muss der Wert hier min. 2 sein
reg_2 |= (timestamp > 1) ? timestamp : 2;
}
modbusAlarms[0] = _size;
modbusAlarms[index * 3 + 1] = fault.id;
modbusAlarms[index * 3 + 2] = reg_1;
modbusAlarms[index * 3 + 3] = reg_2;
if (new_) {
// Bit 12 gibt an, ob ein neuer Fehler aufgetreten ist - setzen
bitSet(modbusData[1], 12);
}
// _printf("alarm reg 1 (index "); _print(index*3+1); _printf("): "); _println(fault.id);
// _printf("alarm reg 2 (index "); _print(index*3+2); _printf("): "); _println(reg_1);
// _printf("alarm reg 3 (index "); _print(index*3+3); _printf("): "); _println(reg_2);
}
#endif
FaultList faults;