- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
373 lines
15 KiB
C++
373 lines
15 KiB
C++
// 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<Everest::error::ErrorType>;
|
|
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<evse_board_supportIntf>& _r_bsp,
|
|
const std::vector<std::unique_ptr<ISO15118_chargerIntf>>& _r_hlc,
|
|
const std::vector<std::unique_ptr<connector_lockIntf>>& _r_connector_lock,
|
|
const std::vector<std::unique_ptr<ac_rcdIntf>>& _r_ac_rcd,
|
|
const std::unique_ptr<evse_managerImplBase>& _p_evse,
|
|
const std::vector<std::unique_ptr<isolation_monitorIntf>>& _r_imd,
|
|
const std::vector<std::unique_ptr<power_supply_DCIntf>>& _r_powersupply,
|
|
const std::vector<std::unique_ptr<powermeterIntf>>& _r_powermeter,
|
|
const std::vector<std::unique_ptr<over_voltage_monitorIntf>>& _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<int>(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<Everest::error::Error> ErrorHandling::errors_prevent_charging() {
|
|
|
|
auto is_fatal = [](auto errors, auto ignore_list) -> std::optional<Everest::error::Error> {
|
|
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
|