// SPDX-License-Identifier: Apache-2.0 // Copyright 2023 Pionix GmbH and Contributors to EVerest #include "ErrorHandling.hpp" namespace { std::string generate_description(const Everest::error::Error& error) { std::string result; if (auto n = error.type.find('/'); n != std::string::npos) { // found slash '/' if ((n + 1) == error.type.size()) { // slash is the last character result = error.type.substr(0, n); } else { // remove upto and including the first / result = error.type.substr(++n); // remove slash at the end if (result.back() == '/') { result.pop_back(); } } if (!error.sub_type.empty()) { result += '/' + error.sub_type; } } else { // return the original description result = error.description; } return result; } } // namespace namespace module { using ErrorList = std::list; static const struct IgnoreErrors { // p_evse. We need to ignore Inoperative here as this is the result of this check. ErrorList evse{"evse_manager/Inoperative"}; ErrorList bsp{"evse_board_support/MREC3HighTemperature", "evse_board_support/MREC18CableOverTempDerate", "evse_board_support/VendorWarning"}; ErrorList connector_lock{"connector_lock/VendorWarning"}; ErrorList ac_rcd{"ac_rcd/VendorWarning"}; ErrorList imd{"isolation_monitor/VendorWarning"}; ErrorList powersupply{"power_supply_DC/VendorWarning"}; ErrorList powermeter{"generic/VendorWarning"}; ErrorList over_voltage_monitor{"over_voltage_monitor/VendorWarning"}; } ignore_errors; ErrorHandling::ErrorHandling(const std::unique_ptr& _r_bsp, const std::vector>& _r_hlc, const std::vector>& _r_connector_lock, const std::vector>& _r_ac_rcd, const std::unique_ptr& _p_evse, const std::vector>& _r_imd, const std::vector>& _r_powersupply, const std::vector>& _r_powermeter, const std::vector>& _r_over_voltage_monitor, bool _inoperative_error_use_vendor_id) : r_bsp(_r_bsp), r_hlc(_r_hlc), r_connector_lock(_r_connector_lock), r_ac_rcd(_r_ac_rcd), p_evse(_p_evse), r_imd(_r_imd), r_powersupply(_r_powersupply), r_powermeter(_r_powermeter), r_over_voltage_monitor(_r_over_voltage_monitor), inoperative_error_use_vendor_id(_inoperative_error_use_vendor_id) { // Subscribe to bsp driver to receive Errors from the bsp hardware r_bsp->subscribe_all_errors([this](const Everest::error::Error& error) { process_error(); }, [this](const Everest::error::Error& error) { process_error(); }); // Subscribe to connector lock to receive errors from connector lock hardware if (r_connector_lock.size() > 0) { r_connector_lock[0]->subscribe_all_errors([this](const Everest::error::Error& error) { process_error(); }, [this](const Everest::error::Error& error) { process_error(); }); } // Subscribe to ac_rcd to receive errors from AC RCD hardware if (r_ac_rcd.size() > 0) { r_ac_rcd[0]->subscribe_all_errors([this](const Everest::error::Error& error) { process_error(); }, [this](const Everest::error::Error& error) { process_error(); }); } // Subscribe to ac_rcd to receive errors from IMD hardware if (r_imd.size() > 0) { r_imd[0]->subscribe_all_errors([this](const Everest::error::Error& error) { process_error(); }, [this](const Everest::error::Error& error) { process_error(); }); } // Subscribe to powersupply to receive errors from DC powersupply hardware if (r_powersupply.size() > 0) { r_powersupply[0]->subscribe_all_errors([this](const Everest::error::Error& error) { process_error(); }, [this](const Everest::error::Error& error) { process_error(); }); } // Subscribe to powermeter to receive errors from powermeter hardware if (r_powermeter.size() > 0) { r_powermeter[0]->subscribe_all_errors([this](const Everest::error::Error& error) { process_error(); }, [this](const Everest::error::Error& error) { process_error(); }); } // Subscribe to over_voltage_monitor to receive errors from over voltage monitor hardware if (r_over_voltage_monitor.size() > 0) { r_over_voltage_monitor[0]->subscribe_all_errors( [this](const Everest::error::Error& error) { process_error(); }, [this](const Everest::error::Error& error) { process_error(); }); } } void ErrorHandling::raise_overcurrent_error(const std::string& description) { // raise externally // Emergency shutdown according to IEC61851-23 Table CC.10 --> Severity::High Everest::error::Error error_object = p_evse->error_factory->create_error( "evse_manager/MREC4OverCurrentFailure", "", description, Everest::error::Severity::High); p_evse->raise_error(error_object); process_error(); } void ErrorHandling::clear_overcurrent_error() { // clear externally if (p_evse->error_state_monitor->is_error_active("evse_manager/MREC4OverCurrentFailure", "")) { p_evse->clear_error("evse_manager/MREC4OverCurrentFailure", ""); } process_error(); } void ErrorHandling::raise_over_voltage_error(Everest::error::Severity severity, const std::string& description) { Everest::error::Error error_object = p_evse->error_factory->create_error("evse_manager/MREC5OverVoltage", "", description, severity); p_evse->raise_error(error_object); process_error(); } void ErrorHandling::clear_over_voltage_error() { if (p_evse->error_state_monitor->is_error_active("evse_manager/MREC5OverVoltage", "")) { p_evse->clear_error("evse_manager/MREC5OverVoltage", ""); } process_error(); } // Find out if the current error set is fatal to charging or not void ErrorHandling::process_error() { const auto fatal = errors_prevent_charging(); if (fatal) { // signal to charger a new error has been set that prevents charging raise_inoperative_error(*fatal); } else { // signal an error that does not prevent charging clear_inoperative_error(); } // All errors cleared signal is for OCPP 1.6. It is triggered when there are no errors anymore, // even those that did not block charging. auto number_of_active_errors = [](const auto& impl) { if (impl.size() > 0) { return static_cast(impl[0]->error_state_monitor->get_active_errors().size()); } else { return 0; } }; const int error_count = p_evse->error_state_monitor->get_active_errors().size() + r_bsp->error_state_monitor->get_active_errors().size() + number_of_active_errors(r_connector_lock) + number_of_active_errors(r_ac_rcd) + number_of_active_errors(r_imd) + number_of_active_errors(r_powersupply) + number_of_active_errors(r_powermeter); if (error_count == 0) { signal_all_errors_cleared(); } } // Check all errors from p_evse and all requirements to see if they block charging std::optional ErrorHandling::errors_prevent_charging() { auto is_fatal = [](auto errors, auto ignore_list) -> std::optional { for (const auto& e : errors) { if (std::none_of(ignore_list.begin(), ignore_list.end(), [e](const auto& ign) { return e->type == ign; })) { return *e; } } return std::nullopt; }; auto fatal = is_fatal(p_evse->error_state_monitor->get_active_errors(), ignore_errors.evse); if (fatal) { return fatal; } fatal = is_fatal(r_bsp->error_state_monitor->get_active_errors(), ignore_errors.bsp); if (fatal) { return fatal; } if (r_connector_lock.size() > 0) { fatal = is_fatal(r_connector_lock[0]->error_state_monitor->get_active_errors(), ignore_errors.connector_lock); if (fatal) { return fatal; } } if (r_ac_rcd.size() > 0) { fatal = is_fatal(r_ac_rcd[0]->error_state_monitor->get_active_errors(), ignore_errors.ac_rcd); if (fatal) { return fatal; } } if (r_imd.size() > 0) { fatal = is_fatal(r_imd[0]->error_state_monitor->get_active_errors(), ignore_errors.imd); if (fatal) { return fatal; } } if (r_powersupply.size() > 0) { fatal = is_fatal(r_powersupply[0]->error_state_monitor->get_active_errors(), ignore_errors.powersupply); if (fatal) { return fatal; } } if (r_powermeter.size() > 0) { fatal = is_fatal(r_powermeter[0]->error_state_monitor->get_active_errors(), ignore_errors.powermeter); if (fatal) { return fatal; } } if (r_over_voltage_monitor.size() > 0) { fatal = is_fatal(r_over_voltage_monitor[0]->error_state_monitor->get_active_errors(), ignore_errors.over_voltage_monitor); if (fatal) { return fatal; } } return std::nullopt; } void ErrorHandling::raise_inoperative_error(const Everest::error::Error& caused_by) { if (p_evse->error_state_monitor->is_error_active("evse_manager/Inoperative", "")) { // dont raise if already raised return; } // raise externally Everest::error::Error error_object = p_evse->error_factory->create_error( "evse_manager/Inoperative", "", caused_by.type, Everest::error::Severity::High); error_object.description = generate_description(caused_by); if (inoperative_error_use_vendor_id && !caused_by.vendor_id.empty()) { error_object.vendor_id = caused_by.vendor_id; } else { error_object.vendor_id = "EVerest"; } p_evse->raise_error(error_object); // shutdown based on severity if (caused_by.severity == Everest::error::Severity::High) { signal_error(ErrorHandlingEvents::ForceEmergencyShutdown); } else { signal_error(ErrorHandlingEvents::ForceErrorShutdown); } } void ErrorHandling::clear_inoperative_error() { // clear externally if (p_evse->error_state_monitor->is_error_active("evse_manager/Inoperative", "")) { p_evse->clear_error("evse_manager/Inoperative"); signal_error(ErrorHandlingEvents::AllErrorsPreventingChargingCleared); } } void ErrorHandling::raise_internal_error(const std::string& description) { // raise externally Everest::error::Error error_object = p_evse->error_factory->create_error("evse_manager/Internal", "", description, Everest::error::Severity::High); p_evse->raise_error(error_object); process_error(); } void ErrorHandling::clear_internal_error() { // clear externally if (p_evse->error_state_monitor->is_error_active("evse_manager/Internal", "")) { p_evse->clear_error("evse_manager/Internal"); process_error(); } } void ErrorHandling::raise_authorization_timeout_error(const std::string& description) { // raise externally Everest::error::Error error_object = p_evse->error_factory->create_error( "evse_manager/MREC9AuthorizationTimeout", "", description, Everest::error::Severity::Medium); p_evse->raise_error(error_object); process_error(); } void ErrorHandling::clear_authorization_timeout_error() { // clear externally if (p_evse->error_state_monitor->is_error_active("evse_manager/MREC9AuthorizationTimeout", "")) { p_evse->clear_error("evse_manager/MREC9AuthorizationTimeout"); process_error(); } } void ErrorHandling::raise_powermeter_transaction_start_failed_error(const std::string& description) { // raise externally Everest::error::Error error_object = p_evse->error_factory->create_error( "evse_manager/PowermeterTransactionStartFailed", "", description, Everest::error::Severity::Medium); p_evse->raise_error(error_object); process_error(); } void ErrorHandling::clear_powermeter_transaction_start_failed_error() { // clear externally if (p_evse->error_state_monitor->is_error_active("evse_manager/PowermeterTransactionStartFailed", "")) { p_evse->clear_error("evse_manager/PowermeterTransactionStartFailed"); process_error(); } } void ErrorHandling::raise_isolation_resistance_fault(const std::string& description, const std::string& sub_type) { // Error shutdown according to IEC61851-23 Table CC.10 --> Severity::Medium Everest::error::Error error_object = p_evse->error_factory->create_error( "evse_manager/MREC22ResistanceFault", sub_type, description, Everest::error::Severity::Medium); p_evse->raise_error(error_object); process_error(); } void ErrorHandling::clear_isolation_resistance_fault(const std::string& sub_type) { if (p_evse->error_state_monitor->is_error_active("evse_manager/MREC22ResistanceFault", sub_type)) { p_evse->clear_error("evse_manager/MREC22ResistanceFault", sub_type); process_error(); } } void ErrorHandling::raise_cable_check_fault(const std::string& description) { // Error shutdown according to IEC61851-23 Table CC.10 --> Severity::Medium Everest::error::Error error_object = p_evse->error_factory->create_error( "evse_manager/MREC11CableCheckFault", "Self test failed", description, Everest::error::Severity::Medium); p_evse->raise_error(error_object); process_error(); } void ErrorHandling::clear_cable_check_fault() { if (p_evse->error_state_monitor->is_error_active("evse_manager/MREC11CableCheckFault", "Self test failed")) { p_evse->clear_error("evse_manager/MREC11CableCheckFault", "Self test failed"); process_error(); } } void ErrorHandling::raise_voltage_plausibility_fault(const std::string& description) { // raise externally // High severity as this indicates a serious measurement inconsistency Everest::error::Error error_object = p_evse->error_factory->create_error( "evse_manager/VoltagePlausibilityFault", "", description, Everest::error::Severity::High); p_evse->raise_error(error_object); process_error(); } void ErrorHandling::clear_voltage_plausibility_fault() { // clear externally if (p_evse->error_state_monitor->is_error_active("evse_manager/VoltagePlausibilityFault", "")) { p_evse->clear_error("evse_manager/VoltagePlausibilityFault"); process_error(); } } } // namespace module