#include "faults.h" #include "../types.h" #include "../mystrings/mystrings.h" #include "../macros.h" #include 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;