diff --git a/config/config-sil-dc-sae-v2g.yaml b/config/config-sil-dc-sae-v2g.yaml index 8e0bb5d1a..1c75e8556 100644 --- a/config/config-sil-dc-sae-v2g.yaml +++ b/config/config-sil-dc-sae-v2g.yaml @@ -147,4 +147,12 @@ active_modules: evse_manager: - module_id: evse_manager implementation_id: evse + error_history: + - module_id: error_history + implementation_id: error_history + error_history: + module: ErrorHistory + config_implementation: + error_history: + database_path: /tmp/error_history.db x-module-layout: {} diff --git a/config/config-sil-dc-sae-v2h.yaml b/config/config-sil-dc-sae-v2h.yaml index ad0e29f5c..bc81ea96b 100644 --- a/config/config-sil-dc-sae-v2h.yaml +++ b/config/config-sil-dc-sae-v2h.yaml @@ -147,4 +147,12 @@ active_modules: evse_manager: - module_id: evse_manager implementation_id: evse + error_history: + - module_id: error_history + implementation_id: error_history + error_history: + module: ErrorHistory + config_implementation: + error_history: + database_path: /tmp/error_history.db x-module-layout: {} diff --git a/config/config-sil-dc-tls.yaml b/config/config-sil-dc-tls.yaml index 98539e495..d5cdafa79 100644 --- a/config/config-sil-dc-tls.yaml +++ b/config/config-sil-dc-tls.yaml @@ -143,4 +143,12 @@ active_modules: evse_manager: - module_id: evse_manager implementation_id: evse + error_history: + - module_id: error_history + implementation_id: error_history + error_history: + module: ErrorHistory + config_implementation: + error_history: + database_path: /tmp/error_history.db x-module-layout: {} diff --git a/config/config-sil-dc.yaml b/config/config-sil-dc.yaml index ebe78beb7..00877a484 100644 --- a/config/config-sil-dc.yaml +++ b/config/config-sil-dc.yaml @@ -140,4 +140,12 @@ active_modules: evse_manager: - module_id: evse_manager implementation_id: evse + error_history: + - module_id: error_history + implementation_id: error_history + error_history: + module: ErrorHistory + config_implementation: + error_history: + database_path: /tmp/error_history.db x-module-layout: {} diff --git a/config/config-sil-energy-management.yaml b/config/config-sil-energy-management.yaml index db7f20d37..01c698048 100644 --- a/config/config-sil-energy-management.yaml +++ b/config/config-sil-energy-management.yaml @@ -175,4 +175,12 @@ active_modules: random_delay: - module_id: evse_manager_1 implementation_id: random_delay + error_history: + - module_id: error_history + implementation_id: error_history + error_history: + module: ErrorHistory + config_implementation: + error_history: + database_path: /tmp/error_history.db x-module-layout: {} diff --git a/config/config-sil-ocpp-custom-extension.yaml b/config/config-sil-ocpp-custom-extension.yaml index 0ab856a65..ada1fb338 100644 --- a/config/config-sil-ocpp-custom-extension.yaml +++ b/config/config-sil-ocpp-custom-extension.yaml @@ -15,6 +15,7 @@ active_modules: supported_ISO15118_2: true evse_manager_1: module: EvseManager + evse: 1 config_module: connector_id: 1 evse_id: "1" @@ -39,6 +40,7 @@ active_modules: implementation_id: charger evse_manager_2: module: EvseManager + evse: 2 config_module: connector_id: 2 evse_id: "2" @@ -63,10 +65,12 @@ active_modules: implementation_id: charger yeti_driver_1: module: JsYetiSimulator + evse: 1 config_module: connector_id: 1 yeti_driver_2: module: JsYetiSimulator + evse: 2 config_module: connector_id: 2 slac: @@ -192,6 +196,14 @@ active_modules: evse_manager: - module_id: evse_manager_1 implementation_id: evse + error_history: + - module_id: error_history + implementation_id: error_history + error_history: + module: ErrorHistory + config_implementation: + error_history: + database_path: /tmp/error_history.db system: module: System diff --git a/config/config-sil-ocpp-pnc.yaml b/config/config-sil-ocpp-pnc.yaml index 181c74f33..dc5fed3ce 100644 --- a/config/config-sil-ocpp-pnc.yaml +++ b/config/config-sil-ocpp-pnc.yaml @@ -18,6 +18,7 @@ active_modules: is_cert_install_needed: true evse_manager_1: module: EvseManager + evse: 1 config_module: connector_id: 1 evse_id: "DE*PNX*00001" @@ -42,6 +43,7 @@ active_modules: implementation_id: charger evse_manager_2: module: EvseManager + evse: 2 config_module: connector_id: 2 evse_id: "2" @@ -66,10 +68,12 @@ active_modules: implementation_id: charger yeti_driver_1: module: JsYetiSimulator + evse: 1 config_module: connector_id: 1 yeti_driver_2: module: JsYetiSimulator + evse: 2 config_module: connector_id: 2 slac: @@ -186,6 +190,14 @@ active_modules: evse_manager: - module_id: evse_manager_1 implementation_id: evse + error_history: + - module_id: error_history + implementation_id: error_history + error_history: + module: ErrorHistory + config_implementation: + error_history: + database_path: /tmp/error_history.db system: module: System diff --git a/config/config-sil-ocpp.yaml b/config/config-sil-ocpp.yaml index 2d9345614..5e068e9ce 100644 --- a/config/config-sil-ocpp.yaml +++ b/config/config-sil-ocpp.yaml @@ -14,6 +14,7 @@ active_modules: device: auto supported_ISO15118_2: true evse_manager_1: + evse: 1 module: EvseManager config_module: connector_id: 1 @@ -40,6 +41,7 @@ active_modules: implementation_id: charger evse_manager_2: module: EvseManager + evse: 2 config_module: connector_id: 2 evse_id: "2" @@ -64,10 +66,12 @@ active_modules: - module_id: iso15118_charger implementation_id: charger yeti_driver_1: + evse: 1 module: JsYetiSimulator config_module: connector_id: 1 yeti_driver_2: + evse: 2 module: JsYetiSimulator config_module: connector_id: 2 @@ -183,6 +187,14 @@ active_modules: ocpp: - module_id: ocpp implementation_id: ocpp_generic + error_history: + - module_id: error_history + implementation_id: error_history + error_history: + module: ErrorHistory + config_implementation: + error_history: + database_path: /tmp/error_history.db system: module: System diff --git a/config/config-sil-ocpp201-pnc.yaml b/config/config-sil-ocpp201-pnc.yaml index a46acf594..dd9643822 100644 --- a/config/config-sil-ocpp201-pnc.yaml +++ b/config/config-sil-ocpp201-pnc.yaml @@ -18,6 +18,7 @@ active_modules: is_cert_install_needed: true evse_manager_1: module: EvseManager + evse: 1 config_module: connector_id: 1 evse_id: "DE*PNX*00001" @@ -42,6 +43,7 @@ active_modules: implementation_id: charger evse_manager_2: module: EvseManager + evse: 2 config_module: connector_id: 2 evse_id: "2" @@ -66,10 +68,12 @@ active_modules: implementation_id: charger yeti_driver_1: module: JsYetiSimulator + evse: 1 config_module: connector_id: 1 yeti_driver_2: module: JsYetiSimulator + evse: 2 config_module: connector_id: 2 slac: @@ -180,6 +184,14 @@ active_modules: evse_manager: - module_id: evse_manager_1 implementation_id: evse + error_history: + - module_id: error_history + implementation_id: error_history + error_history: + module: ErrorHistory + config_implementation: + error_history: + database_path: /tmp/error_history.db system: module: System diff --git a/config/config-sil-ocpp201.yaml b/config/config-sil-ocpp201.yaml index 96ec38a0c..4a975edac 100644 --- a/config/config-sil-ocpp201.yaml +++ b/config/config-sil-ocpp201.yaml @@ -15,6 +15,7 @@ active_modules: supported_ISO15118_2: true evse_manager_1: module: EvseManager + evse: 1 config_module: connector_id: 1 evse_id: "1" @@ -39,6 +40,7 @@ active_modules: implementation_id: charger evse_manager_2: module: EvseManager + evse: 2 config_module: connector_id: 2 evse_id: "2" @@ -63,10 +65,12 @@ active_modules: implementation_id: charger yeti_driver_1: module: JsYetiSimulator + evse: 1 config_module: connector_id: 1 yeti_driver_2: module: JsYetiSimulator + evse: 2 config_module: connector_id: 2 slac: @@ -175,6 +179,14 @@ active_modules: evse_manager: - module_id: evse_manager_1 implementation_id: evse + error_history: + - module_id: error_history + implementation_id: error_history + error_history: + module: ErrorHistory + config_implementation: + error_history: + database_path: /tmp/error_history.db system: module: System diff --git a/config/config-sil-two-evse-dc.yaml b/config/config-sil-two-evse-dc.yaml index 70bb7c9a3..d2246f493 100644 --- a/config/config-sil-two-evse-dc.yaml +++ b/config/config-sil-two-evse-dc.yaml @@ -168,4 +168,12 @@ active_modules: evse_manager: - module_id: evse_manager_1 implementation_id: evse + error_history: + - module_id: error_history + implementation_id: error_history + error_history: + module: ErrorHistory + config_implementation: + error_history: + database_path: /tmp/error_history.db x-module-layout: {} diff --git a/config/config-sil-two-evse.yaml b/config/config-sil-two-evse.yaml index 6df040892..3d00dbc6a 100644 --- a/config/config-sil-two-evse.yaml +++ b/config/config-sil-two-evse.yaml @@ -149,4 +149,12 @@ active_modules: evse_manager: - module_id: evse_manager_1 implementation_id: evse + error_history: + - module_id: error_history + implementation_id: error_history + error_history: + module: ErrorHistory + config_implementation: + error_history: + database_path: /tmp/error_history.db x-module-layout: {} diff --git a/config/config-sil.yaml b/config/config-sil.yaml index b9e66946e..9e093e9a5 100644 --- a/config/config-sil.yaml +++ b/config/config-sil.yaml @@ -6,7 +6,15 @@ active_modules: evse_manager: - implementation_id: evse module_id: connector_1 + error_history: + - module_id: error_history + implementation_id: error_history module: API + error_history: + module: ErrorHistory + config_implementation: + error_history: + database_path: /tmp/error_history.db auth: config_module: connection_timeout: 10 diff --git a/config/nodered/config-sil-flow.json b/config/nodered/config-sil-flow.json index 8f383fa89..cb150b40f 100644 --- a/config/nodered/config-sil-flow.json +++ b/config/nodered/config-sil-flow.json @@ -2287,7 +2287,7 @@ "type": "ui_switch", "z": "cb7609df6138407d", "name": "", - "label": "PermanentFault", + "label": "Inoperative", "tooltip": "", "group": "18b792e585c4a82f", "order": 1, @@ -2298,11 +2298,11 @@ "topic": "everest_external/nodered/#/carsim/error", "topicType": "str", "style": "", - "onvalue": "{\"error_type\":\"PermanentFault\",\"raise\":\"true\"}", + "onvalue": "{\"error_type\":\"Inoperative\",\"raise\":\"true\"}", "onvalueType": "json", "onicon": "", "oncolor": "", - "offvalue": "{\"error_type\":\"PermanentFault\",\"raise\":\"false\"}", + "offvalue": "{\"error_type\":\"Inoperative\",\"raise\":\"false\"}", "offvalueType": "json", "officon": "", "offcolor": "", @@ -3328,4 +3328,4 @@ ] ] } -] \ No newline at end of file +] diff --git a/dependencies.yaml b/dependencies.yaml index e8910debd..0758493fa 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -4,7 +4,7 @@ --- everest-framework: git: https://github.com/EVerest/everest-framework.git - git_tag: v0.15.2 + git_tag: v0.16.0 options: [ "BUILD_TESTING OFF", "everest-framework_USE_PYTHON_VENV ${PROJECT_NAME}_USE_PYTHON_VENV", @@ -54,13 +54,13 @@ libcurl: # and would otherwise be overwritten by the version used there libevse-security: git: https://github.com/EVerest/libevse-security.git - git_tag: b140c17b0a5eaf09b60035605ed8aeb84627eb78 + git_tag: 8a300f39b94fb6364759dfbb57e8501facb94b2f cmake_condition: "EVEREST_DEPENDENCY_ENABLED_LIBEVSE_SECURITY" # OCPP libocpp: git: https://github.com/EVerest/libocpp.git - git_tag: 01f064f45f6d0ff44605db07373e4b9ea02b63b8 + git_tag: 1067cf3ddf27be3a43f8abc519b69da11c4ef921 cmake_condition: "EVEREST_DEPENDENCY_ENABLED_LIBOCPP" # Josev Josev: diff --git a/errors/evse_manager.yaml b/errors/evse_manager.yaml index 86f614a6f..4f9fb74ca 100644 --- a/errors/evse_manager.yaml +++ b/errors/evse_manager.yaml @@ -10,4 +10,6 @@ errors: - name: MREC4OverCurrentFailure description: Over current event - name: PowermeterTransactionStartFailed - description: Transaction could not be started at the powermeter \ No newline at end of file + description: Transaction could not be started at the powermeter + - name: Inoperative + description: Charging is not possible. Usually caused by another error from one of the requirements. diff --git a/modules/API/API.cpp b/modules/API/API.cpp index d665dfe68..60884be42 100644 --- a/modules/API/API.cpp +++ b/modules/API/API.cpp @@ -34,14 +34,13 @@ bool SessionInfo::is_state_charging(const SessionInfo::State current_state) { void SessionInfo::reset() { std::lock_guard lock(this->session_info_mutex); this->state = State::Unknown; - this->active_permanent_faults.clear(); - this->active_errors.clear(); this->start_energy_import_wh = 0; this->end_energy_import_wh = 0; this->start_energy_export_wh = 0; this->end_energy_export_wh = 0; this->start_time_point = date::utc_clock::now(); this->latest_total_w = 0; + this->permanent_fault = false; } types::energy::ExternalLimits get_external_limits(const std::string& data, bool is_watts) { @@ -76,7 +75,7 @@ static void remove_error_from_list(std::vector& list list.end()); } -void SessionInfo::update_state(const types::evse_manager::SessionEventEnum event, const SessionInfo::Error& error) { +void SessionInfo::update_state(const types::evse_manager::SessionEventEnum event) { std::lock_guard lock(this->session_info_mutex); using Event = types::evse_manager::SessionEventEnum; @@ -123,20 +122,6 @@ void SessionInfo::update_state(const types::evse_manager::SessionEventEnum event case Event::SessionFinished: this->state = State::Unplugged; break; - case Event::Error: - this->active_errors.push_back(error); - break; - case Event::AllErrorsCleared: - this->active_permanent_faults.clear(); - this->active_errors.clear(); - break; - case Event::PermanentFault: - this->active_permanent_faults.push_back(error); - break; - case Event::ErrorCleared: - case Event::PermanentFaultCleared: - remove_error_from_list(this->active_permanent_faults, error.type); - break; case Event::ReplugStarted: case Event::ReplugFinished: default: @@ -252,8 +237,7 @@ SessionInfo::operator std::string() { json session_info = json::object({ {"state", state_to_string(this->state)}, - {"active_permanent_faults", this->active_permanent_faults}, - {"active_errors", this->active_errors}, + {"permanent_fault", this->permanent_fault}, {"charged_energy_wh", charged_energy_wh}, {"discharged_energy_wh", discharged_energy_wh}, {"latest_total_w", this->latest_total_w}, @@ -335,6 +319,11 @@ void API::init() { this->selected_protocol = selected_protocol; }); + evse->subscribe_error( + "evse_manager/Inoperative", + [this, &session_info](const Everest::error::Error& error) { session_info->set_permanent_fault(true); }, + [this, &session_info](const Everest::error::Error& error) { session_info->set_permanent_fault(false); }); + std::string var_datetime = var_base + "datetime"; std::string var_session_info = var_base + "session_info"; std::string var_logging_path = var_base + "logging_path"; @@ -355,15 +344,7 @@ void API::init() { evse->subscribe_session_event( [this, var_session_info, var_logging_path, &session_info](types::evse_manager::SessionEvent session_event) { - SessionInfo::Error error; - if (session_event.error.has_value()) { - error.type = types::evse_manager::error_enum_to_string(session_event.error.value().error_code); - error.description = session_event.error.value().error_description; - error.severity = - types::evse_manager::error_severity_to_string(session_event.error.value().error_severity); - } - - session_info->update_state(session_event.event, error); + session_info->update_state(session_event.event); if (session_event.source.has_value()) { const auto source = session_event.source.value(); @@ -605,6 +586,25 @@ void API::init() { } } + std::string var_active_errors = api_base + "errors/var/active_errors"; + this->api_threads.push_back(std::thread([this, var_active_errors]() { + auto next_tick = std::chrono::steady_clock::now(); + while (this->running) { + std::string datetime_str = Everest::Date::to_rfc3339(date::utc_clock::now()); + // request active errors + types::error_history::FilterArguments filter; + filter.state_filter = types::error_history::State::Active; + auto active_errors = r_error_history->call_get_errors(filter); + json errors_json = json(active_errors); + + // publish + this->mqtt.publish(var_active_errors, errors_json.dump()); + + next_tick += NOTIFICATION_PERIOD; + std::this_thread::sleep_until(next_tick); + } + })); + this->api_threads.push_back( std::thread([this, var_connectors, connectors, var_info, var_ocpp_connection_status, var_ocpp_schedule]() { auto next_tick = std::chrono::steady_clock::now(); diff --git a/modules/API/API.hpp b/modules/API/API.hpp index b861013cf..c297a58c9 100644 --- a/modules/API/API.hpp +++ b/modules/API/API.hpp @@ -14,6 +14,7 @@ #include // headers for required interface implementations +#include #include #include #include @@ -50,7 +51,7 @@ class SessionInfo { false}; ///< Indicate if end export energy value (optional) has been received or not void reset(); - void update_state(const types::evse_manager::SessionEventEnum event, const SessionInfo::Error& error); + void update_state(const types::evse_manager::SessionEventEnum event); void set_start_energy_import_wh(int32_t start_energy_import_wh); void set_end_energy_import_wh(int32_t end_energy_import_wh); void set_latest_energy_import_wh(int32_t latest_energy_wh); @@ -61,15 +62,15 @@ class SessionInfo { void set_uk_random_delay_remaining(const types::uk_random_delay::CountDown& c); void set_enable_disable_source(const std::string& active_source, const std::string& active_state, const int active_priority); + void set_permanent_fault(bool f) { + permanent_fault = f; + } /// \brief Converts this struct into a serialized json object operator std::string(); private: std::mutex session_info_mutex; - - std::vector active_permanent_faults; ///< Array of currently active permanent faults that prevent charging - std::vector active_errors; ///< Array of currently active errors that do not prevent charging int32_t start_energy_import_wh; ///< Energy reading (import) at the beginning of this charging session in Wh int32_t end_energy_import_wh; ///< Energy reading (import) at the end of this charging session in Wh int32_t start_energy_export_wh; ///< Energy reading (export) at the beginning of this charging session in Wh @@ -101,6 +102,7 @@ class SessionInfo { std::string active_enable_disable_source{"Unspecified"}; std::string active_enable_disable_state{"Enabled"}; int active_enable_disable_priority{0}; + bool permanent_fault{false}; }; } // namespace module // ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1 @@ -152,13 +154,15 @@ class API : public Everest::ModuleBase { API() = delete; API(const ModuleInfo& info, Everest::MqttProvider& mqtt_provider, std::unique_ptr p_main, std::vector> r_evse_manager, std::vector> r_ocpp, - std::vector> r_random_delay, Conf& config) : + std::vector> r_random_delay, + std::unique_ptr r_error_history, Conf& config) : ModuleBase(info), mqtt(mqtt_provider), p_main(std::move(p_main)), r_evse_manager(std::move(r_evse_manager)), r_ocpp(std::move(r_ocpp)), r_random_delay(std::move(r_random_delay)), + r_error_history(std::move(r_error_history)), config(config){}; Everest::MqttProvider& mqtt; @@ -166,6 +170,7 @@ class API : public Everest::ModuleBase { const std::vector> r_evse_manager; const std::vector> r_ocpp; const std::vector> r_random_delay; + const std::unique_ptr r_error_history; const Conf& config; // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1 diff --git a/modules/API/README.md b/modules/API/README.md index 4a44ccb01..1ec7439be 100644 --- a/modules/API/README.md +++ b/modules/API/README.md @@ -43,9 +43,8 @@ This variable is published every second and contains a json object with informat "datetime": "2022-10-11T16:48:35.747Z", "discharged_energy_wh": 0, "latest_total_w": 0.0, + "permanent_fault": false, "state": "Unplugged", - "active_permanent_faults": [], - "active_errors": [], "active_enable_disable_source": { "source": "Unspecified", "state": "Enable", @@ -61,44 +60,6 @@ This variable is published every second and contains a json object with informat } ``` -Example with permanent faults being active: - -```json -{ - "active_errors": [], - "active_permanent_faults": [ - { - "description": "The control pilot voltage is out of range.", - "severity": "High", - "type": "MREC14PilotFault" - }, - { - "description": "The vehicle is in an invalid mode for charging (Reported by IEC stack)", - "severity": "High", - "type": "MREC10InvalidVehicleMode" - } - ], - "charged_energy_wh": 0, - "charging_duration_s": 0, - "datetime": "2024-01-15T14:58:15.172Z", - "discharged_energy_wh": 0, - "latest_total_w": 0, - "state": "Preparing", - "active_enable_disable_source": { - "source": "Unspecified", - "state": "Enable", - "priority": 5000 - }, - "uk_random_delay": { - "remaining_s": 34, - "current_limit_after_delay_A": 16.0, - "current_limit_during_delay_A": 0.0, - "start_time": "2024-02-28T14:11:11.129Z" - }, - "last_enable_disable_source": "Unspecified" -} -``` - - **charged_energy_wh** contains the charged energy in Wh - **charging_duration_s** contains the duration of the current charging session in seconds - **datetime** contains a string representation of the current UTC datetime in RFC3339 format @@ -117,46 +78,6 @@ Example with permanent faults being active: - ChargingPausedEVSE - Finished -- **active_permanent_faults** array of all active errors that are permanent faults (i.e. that block charging). If anything is set here it should be shown as an error to the user instead of showing the current state: - - RCD_Selftest - - RCD_DC - - RCD_AC - - VendorError - - VendorWarning - - ConnectorLockCapNotCharged - - ConnectorLockUnexpectedOpen - - ConnectorLockUnexpectedClose - - ConnectorLockFailedLock - - ConnectorLockFailedUnlock - - MREC1ConnectorLockFailure - - MREC2GroundFailure - - MREC3HighTemperature - - MREC4OverCurrentFailure - - MREC5OverVoltage - - MREC6UnderVoltage - - MREC8EmergencyStop - - MREC10InvalidVehicleMode - - MREC14PilotFault - - MREC15PowerLoss - - MREC17EVSEContactorFault - - MREC18CableOverTempDerate - - MREC19CableOverTempStop - - MREC20PartialInsertion - - MREC23ProximityFault - - MREC24ConnectorVoltageHigh - - MREC25BrokenLatch - - MREC26CutCable - - DiodeFault - - VentilationNotAvailable - - BrownOut - - EnergyManagement - - PermanentFault - - PowermeterTransactionStartFailed - -- **active_errors** array of all active errors that do not block charging. -This could be shown to the user but the current state should still be shown -as it does not interfere with charging. The enum is the same as for active_permanent_faults. - ### everest_api/evse_manager/var/limits This variable is published every second and contains a json object with information relating to the current limits of this EVSE. @@ -334,3 +255,6 @@ Command to control the UK Smart Charging random delay feature. The payload can b ### everest_api/evse_manager/cmd/uk_random_delay_set_max_duration_s Command to set the UK Smart Charging random delay maximum duration. Payload is an integer in seconds. + +### everest_api/errors/var/active_errors +Publishes an array of all active errors of the charging station diff --git a/modules/API/manifest.yaml b/modules/API/manifest.yaml index ffde6af47..f72e6bde1 100644 --- a/modules/API/manifest.yaml +++ b/modules/API/manifest.yaml @@ -185,6 +185,8 @@ requires: interface: uk_random_delay min_connections: 0 max_connections: 128 + error_history: + interface: error_history enable_external_mqtt: true metadata: license: https://opensource.org/licenses/Apache-2.0 diff --git a/modules/Auth/Auth.cpp b/modules/Auth/Auth.cpp index a300e8c6f..e08072038 100644 --- a/modules/Auth/Auth.cpp +++ b/modules/Auth/Auth.cpp @@ -37,6 +37,15 @@ void Auth::ready() { this->auth_handler->handle_session_event(connector_id, session_event); }); + evse_manager->subscribe_error( + "evse_manager/Inoperative", + [this, connector_id](const Everest::error::Error& error) { + this->auth_handler->handle_permanent_fault_raised(connector_id); + }, + [this, connector_id](const Everest::error::Error& error) { + this->auth_handler->handle_permanent_fault_cleared(connector_id); + }); + evse_index++; } diff --git a/modules/Auth/include/AuthHandler.hpp b/modules/Auth/include/AuthHandler.hpp index 429f2e71e..1c493754e 100644 --- a/modules/Auth/include/AuthHandler.hpp +++ b/modules/Auth/include/AuthHandler.hpp @@ -109,6 +109,12 @@ class AuthHandler { */ void handle_session_event(const int connector_id, const SessionEvent& events); + /** + * @brief Handler for permanent faults from evsemanager that prevents charging + */ + void handle_permanent_fault_cleared(const int connector_id); + void handle_permanent_fault_raised(const int connector_id); + /** * @brief Set the connection timeout of the handler. * diff --git a/modules/Auth/lib/AuthHandler.cpp b/modules/Auth/lib/AuthHandler.cpp index 9f2ad67d1..94a765479 100644 --- a/modules/Auth/lib/AuthHandler.cpp +++ b/modules/Auth/lib/AuthHandler.cpp @@ -523,6 +523,18 @@ void AuthHandler::call_reservation_cancelled(const int& connector_id) { this->reservation_cancelled_callback(this->connectors.at(connector_id)->evse_index); } +void AuthHandler::handle_permanent_fault_raised(const int connector_id) { + if (not ignore_faults) { + this->connectors.at(connector_id)->connector.submit_event(ConnectorEvent::FAULTED); + } +} + +void AuthHandler::handle_permanent_fault_cleared(const int connector_id) { + if (not ignore_faults) { + this->connectors.at(connector_id)->connector.submit_event(ConnectorEvent::ERROR_CLEARED); + } +} + void AuthHandler::handle_session_event(const int connector_id, const SessionEvent& event) { std::lock_guard lk(this->timer_mutex); @@ -573,21 +585,6 @@ void AuthHandler::handle_session_event(const int connector_id, const SessionEven this->plug_in_queue.remove_if([connector_id](int value) { return value == connector_id; }); } break; - case SessionEventEnum::AllErrorsCleared: - if (not ignore_faults) { - this->connectors.at(connector_id)->connector.submit_event(ConnectorEvent::ERROR_CLEARED); - } - break; - case SessionEventEnum::PermanentFault: - if (not ignore_faults) { - this->connectors.at(connector_id)->connector.submit_event(ConnectorEvent::FAULTED); - } - break; - case SessionEventEnum::Error: - if (not ignore_faults) { - this->connectors.at(connector_id)->connector.submit_event(ConnectorEvent::FAULTED); - } - break; case SessionEventEnum::Disabled: this->connectors.at(connector_id)->connector.submit_event(ConnectorEvent::DISABLE); @@ -627,10 +624,6 @@ void AuthHandler::handle_session_event(const int connector_id, const SessionEven [[fallthrough]]; case SessionEventEnum::ChargingFinished: [[fallthrough]]; - case SessionEventEnum::ErrorCleared: - [[fallthrough]]; - case SessionEventEnum::PermanentFaultCleared: - [[fallthrough]]; case SessionEventEnum::ReplugStarted: [[fallthrough]]; case SessionEventEnum::ReplugFinished: diff --git a/modules/Auth/tests/auth_tests.cpp b/modules/Auth/tests/auth_tests.cpp index 4a0ddfb6e..46d5d02cc 100644 --- a/modules/Auth/tests/auth_tests.cpp +++ b/modules/Auth/tests/auth_tests.cpp @@ -476,10 +476,8 @@ TEST_F(AuthTest, test_faulted_state) { TokenHandlingResult result1; TokenHandlingResult result2; - SessionEvent session_event; - session_event.event = SessionEventEnum::PermanentFault; - std::thread t1([this, session_event]() { this->auth_handler->handle_session_event(1, session_event); }); - std::thread t2([this, session_event]() { this->auth_handler->handle_session_event(2, session_event); }); + std::thread t1([this]() { this->auth_handler->handle_permanent_fault_raised(1); }); + std::thread t2([this]() { this->auth_handler->handle_permanent_fault_raised(2); }); std::vector connectors{1, 2}; ProvidedIdToken provided_token_1 = get_provided_token(VALID_TOKEN_1, connectors); @@ -684,10 +682,7 @@ TEST_F(AuthTest, test_parent_id_finish_because_no_available_connector) { SessionEvent session_event_1 = get_session_started_event(types::evse_manager::StartSessionReason::EVConnected); std::thread t1([this, session_event_1]() { this->auth_handler->handle_session_event(1, session_event_1); }); - - SessionEvent session_event_2; - session_event_2.event = SessionEventEnum::PermanentFault; - std::thread t2([this, session_event_2]() { this->auth_handler->handle_session_event(2, session_event_2); }); + std::thread t2([this]() { this->auth_handler->handle_permanent_fault_raised(2); }); std::vector connectors{1, 2}; ProvidedIdToken provided_token_1 = get_provided_token(VALID_TOKEN_1, connectors); diff --git a/modules/EvseManager/Charger.cpp b/modules/EvseManager/Charger.cpp index d22ff1496..d82339999 100644 --- a/modules/EvseManager/Charger.cpp +++ b/modules/EvseManager/Charger.cpp @@ -73,13 +73,8 @@ Charger::Charger(const std::unique_ptr& bsp, const std::unique_ case ErrorHandlingEvents::prevent_charging: shared_context.error_prevent_charging_flag = true; break; - case ErrorHandlingEvents::prevent_charging_welded: - shared_context.error_prevent_charging_flag = true; - shared_context.contactor_welded = true; - break; case ErrorHandlingEvents::all_errors_cleared: shared_context.error_prevent_charging_flag = false; - shared_context.contactor_welded = false; break; default: EVLOG_error << "ErrorHandlingEvents invalid value: " @@ -93,19 +88,15 @@ Charger::Charger(const std::unique_ptr& bsp, const std::unique_ error_thread.detach(); // Register callbacks for errors/error clearings - error_handling->signal_error.connect([this](const types::evse_manager::Error e, const bool prevent_charging) { + error_handling->signal_error.connect([this](const bool prevent_charging) { if (prevent_charging) { - if (e.error_code == types::evse_manager::ErrorEnum::MREC17EVSEContactorFault) { - error_handling_event_queue.push(ErrorHandlingEvents::prevent_charging_welded); - } else { - error_handling_event_queue.push(ErrorHandlingEvents::prevent_charging); - } + // raise external error to signal we cannot charge anymore + error_handling_event_queue.push(ErrorHandlingEvents::prevent_charging); } }); error_handling->signal_all_errors_cleared.connect([this]() { EVLOG_info << "All errors cleared"; - signal_simple_event(types::evse_manager::SessionEventEnum::AllErrorsCleared); error_handling_event_queue.push(ErrorHandlingEvents::all_errors_cleared); }); } @@ -1818,9 +1809,7 @@ void Charger::graceful_stop_charging() { } // open contactors - if (contactors_closed and not shared_context.contactor_welded) { - bsp->allow_power_on(false, types::evse_board_support::Reason::PowerOff); - } + bsp->allow_power_on(false, types::evse_board_support::Reason::PowerOff); } void Charger::clear_errors_on_unplug() { diff --git a/modules/EvseManager/Charger.hpp b/modules/EvseManager/Charger.hpp index 753324ea5..060357d66 100644 --- a/modules/EvseManager/Charger.hpp +++ b/modules/EvseManager/Charger.hpp @@ -368,7 +368,6 @@ class Charger { // ErrorHandling events enum class ErrorHandlingEvents : std::uint8_t { prevent_charging, - prevent_charging_welded, all_errors_cleared }; diff --git a/modules/EvseManager/ErrorHandling.cpp b/modules/EvseManager/ErrorHandling.cpp index 50e4e89e2..4bb218111 100644 --- a/modules/EvseManager/ErrorHandling.cpp +++ b/modules/EvseManager/ErrorHandling.cpp @@ -5,18 +5,6 @@ namespace module { -static types::evse_manager::Error_severity to_evse_manager_severity(Everest::error::Severity s) { - switch (s) { - case Everest::error::Severity::High: - return types::evse_manager::Error_severity::High; - case Everest::error::Severity::Medium: - return types::evse_manager::Error_severity::Medium; - case Everest::error::Severity::Low: - return types::evse_manager::Error_severity::Low; - } - return types::evse_manager::Error_severity::Low; -} - ErrorHandling::ErrorHandling(const std::unique_ptr& _r_bsp, const std::vector>& _r_hlc, const std::vector>& _r_connector_lock, @@ -37,39 +25,26 @@ ErrorHandling::ErrorHandling(const std::unique_ptr& _r_b // Subscribe to bsp driver to receive Errors from the bsp hardware r_bsp->subscribe_all_errors( [this](const Everest::error::Error& error) { - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - types::evse_manager::Error output_error; - output_error.error_description = error.description; - output_error.error_severity = to_evse_manager_severity(error.severity); - - if (modify_error_bsp(error, true, evse_error)) { + if (modify_error_bsp(error, true)) { // signal to charger a new error has been set that prevents charging - output_error.error_code = evse_error; - signal_error(output_error, true); + raise_inoperative_error(error); } else { // signal an error that does not prevent charging - output_error.error_code = evse_error; - signal_error(output_error, false); + signal_error(false); } }, [this](const Everest::error::Error& error) { - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - types::evse_manager::Error output_error; - output_error.error_description = error.description; - output_error.error_severity = to_evse_manager_severity(error.severity); - - if (modify_error_bsp(error, false, evse_error)) { + if (modify_error_bsp(error, false)) { // signal to charger an error has been cleared that prevents charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, true); + signal_error_cleared(true); } else { // signal an error cleared that does not prevent charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, false); + signal_error_cleared(false); } - if (active_errors.all_cleared()) { + if (active_errors.all_cleared_except_inoperative()) { // signal to charger that all errors are cleared now + clear_inoperative_error(); signal_all_errors_cleared(); // clear errors with HLC stack if (hlc) { @@ -82,39 +57,26 @@ ErrorHandling::ErrorHandling(const std::unique_ptr& _r_b if (r_connector_lock.size() > 0) { r_connector_lock[0]->subscribe_all_errors( [this](const Everest::error::Error& error) { - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - types::evse_manager::Error output_error; - output_error.error_description = error.description; - output_error.error_severity = to_evse_manager_severity(error.severity); - - if (modify_error_connector_lock(error, true, evse_error)) { + if (modify_error_connector_lock(error, true)) { // signal to charger a new error has been set that prevents charging - output_error.error_code = evse_error; - signal_error(output_error, true); + raise_inoperative_error(error); } else { // signal an error that does not prevent charging - output_error.error_code = evse_error; - signal_error(output_error, false); + signal_error(false); } }, [this](const Everest::error::Error& error) { - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - types::evse_manager::Error output_error; - output_error.error_description = error.description; - output_error.error_severity = to_evse_manager_severity(error.severity); - - if (modify_error_connector_lock(error, false, evse_error)) { + if (modify_error_connector_lock(error, false)) { // signal to charger an error has been cleared that prevents charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, true); + signal_error_cleared(true); } else { // signal an error cleared that does not prevent charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, false); + signal_error_cleared(false); } - if (active_errors.all_cleared()) { + if (active_errors.all_cleared_except_inoperative()) { // signal to charger that all errors are cleared now + clear_inoperative_error(); signal_all_errors_cleared(); // clear errors with HLC stack if (hlc) { @@ -128,39 +90,26 @@ ErrorHandling::ErrorHandling(const std::unique_ptr& _r_b if (r_ac_rcd.size() > 0) { r_ac_rcd[0]->subscribe_all_errors( [this](const Everest::error::Error& error) { - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - types::evse_manager::Error output_error; - output_error.error_description = error.description; - output_error.error_severity = to_evse_manager_severity(error.severity); - - if (modify_error_ac_rcd(error, true, evse_error)) { + if (modify_error_ac_rcd(error, true)) { // signal to charger a new error has been set that prevents charging - output_error.error_code = evse_error; - signal_error(output_error, true); + raise_inoperative_error(error); } else { // signal an error that does not prevent charging - output_error.error_code = evse_error; - signal_error(output_error, false); + signal_error(false); } }, [this](const Everest::error::Error& error) { - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - types::evse_manager::Error output_error; - output_error.error_description = error.description; - output_error.error_severity = to_evse_manager_severity(error.severity); - - if (modify_error_ac_rcd(error, false, evse_error)) { + if (modify_error_ac_rcd(error, false)) { // signal to charger an error has been cleared that prevents charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, true); + signal_error_cleared(true); } else { // signal an error cleared that does not prevent charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, false); + signal_error_cleared(false); } - if (active_errors.all_cleared()) { + if (active_errors.all_cleared_except_inoperative()) { // signal to charger that all errors are cleared now + clear_inoperative_error(); signal_all_errors_cleared(); // clear errors with HLC stack if (hlc) { @@ -174,39 +123,25 @@ ErrorHandling::ErrorHandling(const std::unique_ptr& _r_b if (r_imd.size() > 0) { r_imd[0]->subscribe_all_errors( [this](const Everest::error::Error& error) { - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - types::evse_manager::Error output_error; - output_error.error_description = error.description; - output_error.error_severity = to_evse_manager_severity(error.severity); - - if (modify_error_imd(error, true, evse_error)) { + if (modify_error_imd(error, true)) { // signal to charger a new error has been set that prevents charging - output_error.error_code = evse_error; - signal_error(output_error, true); + raise_inoperative_error(error); } else { // signal an error that does not prevent charging - output_error.error_code = evse_error; - signal_error(output_error, false); + signal_error(false); } }, [this](const Everest::error::Error& error) { - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - types::evse_manager::Error output_error; - output_error.error_description = error.description; - output_error.error_severity = to_evse_manager_severity(error.severity); - - if (modify_error_imd(error, false, evse_error)) { + if (modify_error_imd(error, false)) { // signal to charger an error has been cleared that prevents charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, true); + signal_error_cleared(true); } else { - // signal an error cleared that does not prevent charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, false); + signal_error_cleared(false); } - if (active_errors.all_cleared()) { + if (active_errors.all_cleared_except_inoperative()) { // signal to charger that all errors are cleared now + clear_inoperative_error(); signal_all_errors_cleared(); // clear errors with HLC stack if (hlc) { @@ -222,15 +157,10 @@ void ErrorHandling::raise_overcurrent_error(const std::string& description) { 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); - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - if (modify_error_evse_manager("evse_manager/MREC4OverCurrentFailure", true, evse_error)) { + if (modify_error_evse_manager("evse_manager/MREC4OverCurrentFailure", true)) { // signal to charger a new error has been set - types::evse_manager::Error output_error; - output_error.error_description = description; - output_error.error_severity = types::evse_manager::Error_severity::High; - output_error.error_code = types::evse_manager::ErrorEnum::MREC4OverCurrentFailure; - signal_error(output_error, true); + signal_error(true); }; } @@ -239,20 +169,53 @@ void ErrorHandling::clear_overcurrent_error() { if (active_errors.bsp.is_set(BspErrors::MREC4OverCurrentFailure)) { p_evse->clear_error("evse_manager/MREC4OverCurrentFailure"); - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - types::evse_manager::Error output_error; - output_error.error_description = ""; - output_error.error_severity = types::evse_manager::Error_severity::High; - output_error.error_code = types::evse_manager::ErrorEnum::MREC4OverCurrentFailure; + if (modify_error_evse_manager("evse_manager/MREC4OverCurrentFailure", false)) { + // signal to charger an error has been cleared that prevents charging + signal_error_cleared(true); + } else { + // signal an error cleared that does not prevent charging + signal_error_cleared(false); + } - if (modify_error_evse_manager("evse_manager/MREC4OverCurrentFailure", false, evse_error)) { + if (active_errors.all_cleared_except_inoperative()) { + // signal to charger that all errors are cleared now + signal_all_errors_cleared(); + // clear errors with HLC stack + if (hlc) { + r_hlc[0]->call_reset_error(); + } + } + } +} + +void ErrorHandling::raise_inoperative_error(const Everest::error::Error& error) { + if (this->active_errors.evse_manager.is_set(EvseManagerErrors::Inoperative)) { + // dont raise if already raised + return; + } + + // raise externally + Everest::error::Error error_object = + p_evse->error_factory->create_error("evse_manager/Inoperative", "", error.type, Everest::error::Severity::High); + p_evse->raise_error(error_object); + + if (modify_error_evse_manager("evse_manager/Inoperative", true)) { + // signal to charger a new error has been set + signal_error(true); + }; +} + +void ErrorHandling::clear_inoperative_error() { + // clear externally + if (active_errors.evse_manager.is_set(EvseManagerErrors::Inoperative)) { + p_evse->clear_error("evse_manager/Inoperative"); + + if (modify_error_evse_manager("evse_manager/Inoperative", false)) { // signal to charger an error has been cleared that prevents charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, true); + signal_error_cleared(true); } else { // signal an error cleared that does not prevent charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, false); + signal_error_cleared(false); } if (active_errors.all_cleared()) { @@ -271,15 +234,10 @@ void ErrorHandling::raise_internal_error(const std::string& description) { 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); - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - if (modify_error_evse_manager("evse_manager/Internal", true, evse_error)) { + if (modify_error_evse_manager("evse_manager/Internal", true)) { // signal to charger a new error has been set - types::evse_manager::Error output_error; - output_error.error_description = description; - output_error.error_severity = types::evse_manager::Error_severity::High; - output_error.error_code = types::evse_manager::ErrorEnum::VendorError; - signal_error(output_error, true); + signal_error(true); }; } @@ -288,20 +246,12 @@ void ErrorHandling::clear_internal_error() { if (active_errors.evse_manager.is_set(EvseManagerErrors::Internal)) { p_evse->clear_error("evse_manager/Internal"); - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - types::evse_manager::Error output_error; - output_error.error_description = ""; - output_error.error_severity = types::evse_manager::Error_severity::High; - output_error.error_code = types::evse_manager::ErrorEnum::VendorError; - - if (modify_error_evse_manager("evse_manager/Internal", false, evse_error)) { + if (modify_error_evse_manager("evse_manager/Internal", false)) { // signal to charger an error has been cleared that prevents charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, true); + signal_error_cleared(true); } else { // signal an error cleared that does not prevent charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, false); + signal_error_cleared(false); } if (active_errors.all_cleared()) { @@ -320,15 +270,10 @@ void ErrorHandling::raise_powermeter_transaction_start_failed_error(const std::s Everest::error::Error error_object = p_evse->error_factory->create_error( "evse_manager/PowermeterTransactionStartFailed", "", description, Everest::error::Severity::High); p_evse->raise_error(error_object); - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - if (modify_error_evse_manager("evse_manager/PowermeterTransactionStartFailed", true, evse_error)) { + if (modify_error_evse_manager("evse_manager/PowermeterTransactionStartFailed", true)) { // signal to charger a new error has been set - types::evse_manager::Error output_error; - output_error.error_description = description; - output_error.error_severity = types::evse_manager::Error_severity::High; - output_error.error_code = types::evse_manager::ErrorEnum::PowermeterTransactionStartFailed; - signal_error(output_error, true); + signal_error(true); }; } @@ -337,20 +282,12 @@ void ErrorHandling::clear_powermeter_transaction_start_failed_error() { if (active_errors.evse_manager.is_set(EvseManagerErrors::PowermeterTransactionStartFailed)) { p_evse->clear_error("evse_manager/PowermeterTransactionStartFailed"); - types::evse_manager::ErrorEnum evse_error{types::evse_manager::ErrorEnum::VendorWarning}; - types::evse_manager::Error output_error; - output_error.error_description = ""; - output_error.error_severity = types::evse_manager::Error_severity::High; - output_error.error_code = types::evse_manager::ErrorEnum::VendorError; - - if (modify_error_evse_manager("evse_manager/PowermeterTransactionStartFailed", false, evse_error)) { + if (modify_error_evse_manager("evse_manager/PowermeterTransactionStartFailed", false)) { // signal to charger an error has been cleared that prevents charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, true); + signal_error_cleared(true); } else { // signal an error cleared that does not prevent charging - output_error.error_code = evse_error; - signal_error_cleared(output_error, false); + signal_error_cleared(false); } if (active_errors.all_cleared()) { @@ -364,8 +301,7 @@ void ErrorHandling::clear_powermeter_transaction_start_failed_error() { } } -bool ErrorHandling::modify_error_bsp(const Everest::error::Error& error, bool active, - types::evse_manager::ErrorEnum& evse_error) { +bool ErrorHandling::modify_error_bsp(const Everest::error::Error& error, bool active) { const std::string& error_type = error.type; if (active) { @@ -376,147 +312,116 @@ bool ErrorHandling::modify_error_bsp(const Everest::error::Error& error, bool ac if (error_type == "evse_board_support/DiodeFault") { active_errors.bsp.set(BspErrors::DiodeFault, active); - evse_error = types::evse_manager::ErrorEnum::DiodeFault; } else if (error_type == "evse_board_support/VentilationNotAvailable") { active_errors.bsp.set(BspErrors::VentilationNotAvailable, active); - evse_error = types::evse_manager::ErrorEnum::VentilationNotAvailable; } else if (error_type == "evse_board_support/BrownOut") { active_errors.bsp.set(BspErrors::BrownOut, active); - evse_error = types::evse_manager::ErrorEnum::BrownOut; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/EnergyManagement") { active_errors.bsp.set(BspErrors::EnergyManagement, active); - evse_error = types::evse_manager::ErrorEnum::EnergyManagement; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/PermanentFault") { active_errors.bsp.set(BspErrors::PermanentFault, active); - evse_error = types::evse_manager::ErrorEnum::PermanentFault; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/MREC2GroundFailure") { active_errors.bsp.set(BspErrors::MREC2GroundFailure, active); - evse_error = types::evse_manager::ErrorEnum::MREC2GroundFailure; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/MREC4OverCurrentFailure") { active_errors.bsp.set(BspErrors::MREC4OverCurrentFailure, active); - evse_error = types::evse_manager::ErrorEnum::MREC4OverCurrentFailure; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/MREC5OverVoltage") { active_errors.bsp.set(BspErrors::MREC5OverVoltage, active); - evse_error = types::evse_manager::ErrorEnum::MREC5OverVoltage; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/MREC6UnderVoltage") { active_errors.bsp.set(BspErrors::MREC6UnderVoltage, active); - evse_error = types::evse_manager::ErrorEnum::MREC6UnderVoltage; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/MREC8EmergencyStop") { active_errors.bsp.set(BspErrors::MREC8EmergencyStop, active); - evse_error = types::evse_manager::ErrorEnum::MREC8EmergencyStop; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_EmergencyShutdown); } } else if (error_type == "evse_board_support/MREC10InvalidVehicleMode") { active_errors.bsp.set(BspErrors::MREC10InvalidVehicleMode, active); - evse_error = types::evse_manager::ErrorEnum::MREC10InvalidVehicleMode; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/MREC14PilotFault") { active_errors.bsp.set(BspErrors::MREC14PilotFault, active); - evse_error = types::evse_manager::ErrorEnum::MREC14PilotFault; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/MREC15PowerLoss") { active_errors.bsp.set(BspErrors::MREC15PowerLoss, active); - evse_error = types::evse_manager::ErrorEnum::MREC15PowerLoss; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/MREC17EVSEContactorFault") { active_errors.bsp.set(BspErrors::MREC17EVSEContactorFault, active); - evse_error = types::evse_manager::ErrorEnum::MREC17EVSEContactorFault; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Contactor); } } else if (error_type == "evse_board_support/MREC19CableOverTempStop") { active_errors.bsp.set(BspErrors::MREC19CableOverTempStop, active); - evse_error = types::evse_manager::ErrorEnum::MREC19CableOverTempStop; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/MREC20PartialInsertion") { active_errors.bsp.set(BspErrors::MREC20PartialInsertion, active); - evse_error = types::evse_manager::ErrorEnum::MREC20PartialInsertion; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/MREC23ProximityFault") { active_errors.bsp.set(BspErrors::MREC23ProximityFault, active); - evse_error = types::evse_manager::ErrorEnum::MREC23ProximityFault; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/MREC24ConnectorVoltageHigh") { active_errors.bsp.set(BspErrors::MREC24ConnectorVoltageHigh, active); - evse_error = types::evse_manager::ErrorEnum::MREC24ConnectorVoltageHigh; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/MREC25BrokenLatch") { active_errors.bsp.set(BspErrors::MREC25BrokenLatch, active); - evse_error = types::evse_manager::ErrorEnum::MREC25BrokenLatch; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/MREC26CutCable") { active_errors.bsp.set(BspErrors::MREC26CutCable, active); - evse_error = types::evse_manager::ErrorEnum::MREC26CutCable; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/VendorError") { active_errors.bsp.set(BspErrors::VendorError, active); - evse_error = types::evse_manager::ErrorEnum::VendorError; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_board_support/CommunicationFault") { active_errors.bsp.set(BspErrors::CommunicationFault, active); - evse_error = types::evse_manager::ErrorEnum::VendorError; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else { - // Errors that do not stop charging - if (error_type == "evse_board_support/MREC3HighTemperature") { - evse_error = types::evse_manager::ErrorEnum::MREC3HighTemperature; - } else if (error_type == "evse_board_support/MREC18CableOverTempDerate") { - evse_error = types::evse_manager::ErrorEnum::MREC18CableOverTempDerate; - } else if (error_type == "evse_board_support/VendorWarning") { - evse_error = types::evse_manager::ErrorEnum::VendorWarning; - } return false; } // Error stops charging return true; }; -bool ErrorHandling::modify_error_connector_lock(const Everest::error::Error& error, bool active, - types::evse_manager::ErrorEnum& evse_error) { +bool ErrorHandling::modify_error_connector_lock(const Everest::error::Error& error, bool active) { const std::string& error_type = error.type; if (active) { @@ -527,41 +432,30 @@ bool ErrorHandling::modify_error_connector_lock(const Everest::error::Error& err if (error_type == "connector_lock/ConnectorLockCapNotCharged") { active_errors.connector_lock.set(ConnectorLockErrors::ConnectorLockCapNotCharged, active); - evse_error = types::evse_manager::ErrorEnum::ConnectorLockCapNotCharged; } else if (error_type == "connector_lock/ConnectorLockUnexpectedClose") { active_errors.connector_lock.set(ConnectorLockErrors::ConnectorLockUnexpectedClose, active); - evse_error = types::evse_manager::ErrorEnum::ConnectorLockUnexpectedClose; } else if (error_type == "connector_lock/ConnectorLockUnexpectedOpen") { active_errors.connector_lock.set(ConnectorLockErrors::ConnectorLockUnexpectedOpen, active); - evse_error = types::evse_manager::ErrorEnum::ConnectorLockUnexpectedOpen; } else if (error_type == "connector_lock/ConnectorLockFailedLock") { active_errors.connector_lock.set(ConnectorLockErrors::ConnectorLockFailedLock, active); - evse_error = types::evse_manager::ErrorEnum::ConnectorLockFailedLock; } else if (error_type == "connector_lock/ConnectorLockFailedUnlock") { active_errors.connector_lock.set(ConnectorLockErrors::ConnectorLockFailedUnlock, active); - evse_error = types::evse_manager::ErrorEnum::ConnectorLockFailedUnlock; } else if (error_type == "connector_lock/MREC1ConnectorLockFailure") { active_errors.connector_lock.set(ConnectorLockErrors::MREC1ConnectorLockFailure, active); - evse_error = types::evse_manager::ErrorEnum::MREC1ConnectorLockFailure; } else if (error_type == "connector_lock/VendorError") { active_errors.connector_lock.set(ConnectorLockErrors::VendorError, active); - evse_error = types::evse_manager::ErrorEnum::VendorError; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else { // Errors that do not stop charging - if (error_type == "connector_lock/VendorWarning") { - evse_error = types::evse_manager::ErrorEnum::VendorWarning; - } return false; } // Error stops charging return true; }; -bool ErrorHandling::modify_error_ac_rcd(const Everest::error::Error& error, bool active, - types::evse_manager::ErrorEnum& evse_error) { +bool ErrorHandling::modify_error_ac_rcd(const Everest::error::Error& error, bool active) { const std::string& error_type = error.type; if (active) { @@ -572,61 +466,54 @@ bool ErrorHandling::modify_error_ac_rcd(const Everest::error::Error& error, bool if (error_type == "ac_rcd/MREC2GroundFailure") { active_errors.ac_rcd.set(AcRcdErrors::MREC2GroundFailure, active); - evse_error = types::evse_manager::ErrorEnum::MREC2GroundFailure; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_RCD); } } else if (error_type == "ac_rcd/VendorError") { active_errors.ac_rcd.set(AcRcdErrors::VendorError, active); - evse_error = types::evse_manager::ErrorEnum::VendorError; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "ac_rcd/Selftest") { active_errors.ac_rcd.set(AcRcdErrors::Selftest, active); - evse_error = types::evse_manager::ErrorEnum::RCD_Selftest; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "ac_rcd/AC") { active_errors.ac_rcd.set(AcRcdErrors::AC, active); - evse_error = types::evse_manager::ErrorEnum::RCD_AC; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_RCD); } } else if (error_type == "ac_rcd/DC") { active_errors.ac_rcd.set(AcRcdErrors::DC, active); - evse_error = types::evse_manager::ErrorEnum::RCD_DC; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_RCD); } } else { // Errors that do not stop charging - if (error_type == "ac_rcd/VendorWarning") { - evse_error = types::evse_manager::ErrorEnum::VendorWarning; - } return false; } // Error stops charging return true; }; -bool ErrorHandling::modify_error_evse_manager(const std::string& error_type, bool active, - types::evse_manager::ErrorEnum& evse_error) { +bool ErrorHandling::modify_error_evse_manager(const std::string& error_type, bool active) { if (error_type == "evse_manager/MREC4OverCurrentFailure") { active_errors.bsp.set(BspErrors::MREC4OverCurrentFailure, active); - evse_error = types::evse_manager::ErrorEnum::MREC4OverCurrentFailure; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "evse_manager/PowermeterTransactionStartFailed") { active_errors.evse_manager.set(EvseManagerErrors::PowermeterTransactionStartFailed, active); - evse_error = types::evse_manager::ErrorEnum::PowermeterTransactionStartFailed; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } - + } else if (error_type == "evse_manager/Inoperative") { + active_errors.evse_manager.set(EvseManagerErrors::Inoperative, active); + if (hlc && active) { + r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); + } } else { // Error does not stop charging, ignored here return false; @@ -635,8 +522,7 @@ bool ErrorHandling::modify_error_evse_manager(const std::string& error_type, boo return true; }; -bool ErrorHandling::modify_error_imd(const Everest::error::Error& error, bool active, - types::evse_manager::ErrorEnum& evse_error) { +bool ErrorHandling::modify_error_imd(const Everest::error::Error& error, bool active) { const std::string& error_type = error.type; if (active) { @@ -647,27 +533,21 @@ bool ErrorHandling::modify_error_imd(const Everest::error::Error& error, bool ac if (error_type == "isolation_monitor/DeviceFault") { active_errors.imd.set(IMDErrors::DeviceFault, active); - evse_error = types::evse_manager::ErrorEnum::IMDFault; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "isolation_monitor/CommunicationFault") { active_errors.imd.set(IMDErrors::CommunicationFault, active); - evse_error = types::evse_manager::ErrorEnum::IMDFault; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else if (error_type == "isolation_monitor/VendorError") { active_errors.connector_lock.set(ConnectorLockErrors::VendorError, active); - evse_error = types::evse_manager::ErrorEnum::VendorError; if (hlc && active) { r_hlc[0]->call_send_error(types::iso15118_charger::EvseError::Error_Malfunction); } } else { // Errors that do not stop charging - if (error_type == "isolation_monitor/VendorWarning") { - evse_error = types::evse_manager::ErrorEnum::VendorWarning; - } return false; } // Error stops charging diff --git a/modules/EvseManager/ErrorHandling.hpp b/modules/EvseManager/ErrorHandling.hpp index db3b5f01d..d048b9054 100644 --- a/modules/EvseManager/ErrorHandling.hpp +++ b/modules/EvseManager/ErrorHandling.hpp @@ -72,7 +72,8 @@ enum class EvseManagerErrors : std::uint8_t { MREC4OverCurrentFailure, Internal, PowermeterTransactionStartFailed, - last = PowermeterTransactionStartFailed + Inoperative, + last = Inoperative }; enum class AcRcdErrors : std::uint8_t { @@ -114,6 +115,12 @@ struct ActiveErrors { return bsp.all_reset() && evse_manager.all_reset() && ac_rcd.all_reset() && connector_lock.all_reset() && imd.all_reset(); }; + + inline bool all_cleared_except_inoperative() { + return bsp.all_reset() && ac_rcd.all_reset() && connector_lock.all_reset() && imd.all_reset() && + (evse_manager.all_reset() || (evse_manager.is_set(EvseManagerErrors::Inoperative) && + evse_manager._value == evse_manager.bit(EvseManagerErrors::Inoperative))); + }; }; class ErrorHandling { @@ -127,9 +134,9 @@ class ErrorHandling { const std::vector>& _r_imd); // Signal that one error has been raised. Bool argument is true if it preventing charging. - sigslot::signal signal_error; + sigslot::signal signal_error; // Signal that one error has been cleared. Bool argument is true if it was preventing charging. - sigslot::signal signal_error_cleared; + sigslot::signal signal_error_cleared; // Signal that all errors are cleared (both those preventing charging and not) sigslot::signal<> signal_all_errors_cleared; @@ -143,6 +150,9 @@ class ErrorHandling { void clear_powermeter_transaction_start_failed_error(); private: + void raise_inoperative_error(const Everest::error::Error& error); + void clear_inoperative_error(); + const std::unique_ptr& r_bsp; const std::vector>& r_hlc; const std::vector>& r_connector_lock; @@ -150,15 +160,12 @@ class ErrorHandling { const std::unique_ptr& p_evse; const std::vector>& r_imd; - bool modify_error_bsp(const Everest::error::Error& error, bool active, types::evse_manager::ErrorEnum& evse_error); - bool modify_error_connector_lock(const Everest::error::Error& error, bool active, - types::evse_manager::ErrorEnum& evse_error); - bool modify_error_ac_rcd(const Everest::error::Error& error, bool active, - types::evse_manager::ErrorEnum& evse_error); + bool modify_error_bsp(const Everest::error::Error& error, bool active); + bool modify_error_connector_lock(const Everest::error::Error& error, bool active); + bool modify_error_ac_rcd(const Everest::error::Error& error, bool active); - bool modify_error_evse_manager(const std::string& error_type, bool active, - types::evse_manager::ErrorEnum& evse_error); - bool modify_error_imd(const Everest::error::Error& error, bool active, types::evse_manager::ErrorEnum& evse_error); + bool modify_error_evse_manager(const std::string& error_type, bool active); + bool modify_error_imd(const Everest::error::Error& error, bool active); bool hlc{false}; ActiveErrors active_errors; diff --git a/modules/EvseManager/EvseManager.cpp b/modules/EvseManager/EvseManager.cpp index ebea50a2a..798889a75 100644 --- a/modules/EvseManager/EvseManager.cpp +++ b/modules/EvseManager/EvseManager.cpp @@ -754,10 +754,12 @@ void EvseManager::ready() { } }); + // Cancel reservations if charger is faulted + error_handling->signal_error.connect([this](bool prevents_charging) { cancel_reservation(true); }); + charger->signal_simple_event.connect([this](types::evse_manager::SessionEventEnum s) { - // Cancel reservations if charger is disabled or faulted - if (s == types::evse_manager::SessionEventEnum::Disabled or - s == types::evse_manager::SessionEventEnum::PermanentFault) { + // Cancel reservations if charger is disabled + if (s == types::evse_manager::SessionEventEnum::Disabled) { cancel_reservation(true); } if (s == types::evse_manager::SessionEventEnum::SessionFinished) { diff --git a/modules/EvseManager/evse/evse_managerImpl.cpp b/modules/EvseManager/evse/evse_managerImpl.cpp index bc3866e06..4dac48b94 100644 --- a/modules/EvseManager/evse/evse_managerImpl.cpp +++ b/modules/EvseManager/evse/evse_managerImpl.cpp @@ -114,36 +114,6 @@ void evse_managerImpl::init() { void evse_managerImpl::ready() { - // Register callbacks for errors/permanent faults - mod->error_handling->signal_error.connect([this](const types::evse_manager::Error e, const bool prevent_charging) { - types::evse_manager::SessionEvent se; - - se.error = e; - se.uuid = mod->charger->get_session_id(); - - if (prevent_charging) { - se.event = types::evse_manager::SessionEventEnum::PermanentFault; - } else { - se.event = types::evse_manager::SessionEventEnum::Error; - } - publish_session_event(se); - }); - - mod->error_handling->signal_error_cleared.connect( - [this](const types::evse_manager::Error e, const bool prevent_charging) { - types::evse_manager::SessionEvent se; - - se.error = e; - se.uuid = mod->charger->get_session_id(); - - if (prevent_charging) { - se.event = types::evse_manager::SessionEventEnum::PermanentFaultCleared; - } else { - se.event = types::evse_manager::SessionEventEnum::ErrorCleared; - } - publish_session_event(se); - }); - // publish evse id at least once publish_evse_id(mod->config.evse_id); diff --git a/modules/EvseManager/tests/ErrorHandlingTest.cpp b/modules/EvseManager/tests/ErrorHandlingTest.cpp index 5219cc121..493643600 100644 --- a/modules/EvseManager/tests/ErrorHandlingTest.cpp +++ b/modules/EvseManager/tests/ErrorHandlingTest.cpp @@ -172,11 +172,11 @@ struct ErrorHandlingSignals { } void register_callbacks(module::ErrorHandling& eh) { - eh.signal_error.connect([this](types::evse_manager::Error err, bool stop_charging) { + eh.signal_error.connect([this](bool stop_charging) { this->called_signal_error = true; this->signal_error = true; }); - eh.signal_error_cleared.connect([this](types::evse_manager::Error err, bool stop_charging) { + eh.signal_error_cleared.connect([this](bool stop_charging) { this->called_signal_error_cleared = true; this->signal_error_cleared = true; }); @@ -220,20 +220,16 @@ TEST(ErrorHandlingTest, modify_error_bsp) { "Vendor specific error code. Will stop charging session.", id); bool bResult; - auto error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_bsp(error, true, error_type); + bResult = error_handling.modify_error_bsp(error, true); EXPECT_TRUE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::VendorError); EXPECT_FALSE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); EXPECT_FALSE(ehs.called_signal_all_errors_cleared); ehs.reset(); - error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_bsp(error, false, error_type); + bResult = error_handling.modify_error_bsp(error, false); EXPECT_TRUE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::VendorError); EXPECT_TRUE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); @@ -243,20 +239,16 @@ TEST(ErrorHandlingTest, modify_error_bsp) { ehs.reset(); Everest::error::Error warning("evse_board_support/VendorWarning", "", "K2Faults::FAULT_CT_CLAMP", "Vendor specific error code. Will not stop charging session.", id); - error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_bsp(warning, true, error_type); + bResult = error_handling.modify_error_bsp(warning, true); EXPECT_FALSE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::VendorWarning); EXPECT_TRUE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); EXPECT_FALSE(ehs.called_signal_all_errors_cleared); ehs.reset(); - error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_bsp(warning, false, error_type); + bResult = error_handling.modify_error_bsp(warning, false); EXPECT_FALSE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::VendorWarning); EXPECT_TRUE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); @@ -314,20 +306,16 @@ TEST(ErrorHandlingTest, modify_error_connector_lock) { "no description", id); bool bResult; - auto error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_connector_lock(error, true, error_type); + bResult = error_handling.modify_error_connector_lock(error, true); EXPECT_TRUE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::ConnectorLockUnexpectedOpen); EXPECT_FALSE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); EXPECT_FALSE(ehs.called_signal_all_errors_cleared); ehs.reset(); - error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_connector_lock(error, false, error_type); + bResult = error_handling.modify_error_connector_lock(error, false); EXPECT_TRUE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::ConnectorLockUnexpectedOpen); EXPECT_TRUE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); @@ -337,20 +325,16 @@ TEST(ErrorHandlingTest, modify_error_connector_lock) { ehs.reset(); Everest::error::Error warning("connector_lock/VendorWarning", "", "Will not stop charging session.", "no description", id); - error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_connector_lock(warning, true, error_type); + bResult = error_handling.modify_error_connector_lock(warning, true); EXPECT_FALSE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::VendorWarning); EXPECT_TRUE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); EXPECT_FALSE(ehs.called_signal_all_errors_cleared); ehs.reset(); - error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_connector_lock(warning, false, error_type); + bResult = error_handling.modify_error_connector_lock(warning, false); EXPECT_FALSE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::VendorWarning); EXPECT_TRUE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); @@ -380,20 +364,16 @@ TEST(ErrorHandlingTest, modify_error_ac_rcd) { Everest::error::Error error("ac_rcd/AC", "", "Will stop charging session.", "no description", id); bool bResult; - auto error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_ac_rcd(error, true, error_type); + bResult = error_handling.modify_error_ac_rcd(error, true); EXPECT_TRUE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::RCD_AC); EXPECT_FALSE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); EXPECT_FALSE(ehs.called_signal_all_errors_cleared); ehs.reset(); - error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_ac_rcd(error, false, error_type); + bResult = error_handling.modify_error_ac_rcd(error, false); EXPECT_TRUE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::RCD_AC); EXPECT_TRUE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); @@ -402,20 +382,16 @@ TEST(ErrorHandlingTest, modify_error_ac_rcd) { // VendorWarning not treated as an active error ehs.reset(); Everest::error::Error warning("ac_rcd/VendorWarning", "", "Will not stop charging session.", "no description", id); - error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_ac_rcd(warning, true, error_type); + bResult = error_handling.modify_error_ac_rcd(warning, true); EXPECT_FALSE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::VendorWarning); EXPECT_TRUE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); EXPECT_FALSE(ehs.called_signal_all_errors_cleared); ehs.reset(); - error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_ac_rcd(warning, false, error_type); + bResult = error_handling.modify_error_ac_rcd(warning, false); EXPECT_FALSE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::VendorWarning); EXPECT_TRUE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); @@ -446,20 +422,16 @@ TEST(ErrorHandlingTest, modify_error_imd) { "no description", id); bool bResult; - auto error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_imd(error, true, error_type); + bResult = error_handling.modify_error_imd(error, true); EXPECT_TRUE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::IMDFault); EXPECT_FALSE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); EXPECT_FALSE(ehs.called_signal_all_errors_cleared); ehs.reset(); - error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_imd(error, false, error_type); + bResult = error_handling.modify_error_imd(error, false); EXPECT_TRUE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::IMDFault); EXPECT_TRUE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); @@ -469,20 +441,16 @@ TEST(ErrorHandlingTest, modify_error_imd) { ehs.reset(); Everest::error::Error warning("isolation_monitor/VendorWarning", "", "Will not stop charging session.", "no description", id); - error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_imd(warning, true, error_type); + bResult = error_handling.modify_error_imd(warning, true); EXPECT_FALSE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::VendorWarning); EXPECT_TRUE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); EXPECT_FALSE(ehs.called_signal_all_errors_cleared); ehs.reset(); - error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_imd(warning, false, error_type); + bResult = error_handling.modify_error_imd(warning, false); EXPECT_FALSE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::VendorWarning); EXPECT_TRUE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); @@ -512,20 +480,16 @@ TEST(ErrorHandlingTest, modify_error_evse_manager) { std::string error{"evse_manager/PowermeterTransactionStartFailed"}; bool bResult; - auto error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_evse_manager(error, true, error_type); + bResult = error_handling.modify_error_evse_manager(error, true); EXPECT_TRUE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::PowermeterTransactionStartFailed); EXPECT_FALSE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); EXPECT_FALSE(ehs.called_signal_all_errors_cleared); ehs.reset(); - error_type = types::evse_manager::ErrorEnum::PermanentFault; - bResult = error_handling.modify_error_evse_manager(error, false, error_type); + bResult = error_handling.modify_error_evse_manager(error, false); EXPECT_TRUE(bResult); - EXPECT_EQ(error_type, types::evse_manager::ErrorEnum::PowermeterTransactionStartFailed); EXPECT_TRUE(error_handling.active_errors.all_cleared()); EXPECT_FALSE(ehs.called_signal_error); EXPECT_FALSE(ehs.called_signal_error_cleared); diff --git a/modules/OCPP/OCPP.cpp b/modules/OCPP/OCPP.cpp index 7498fb18b..f98d60ffd 100644 --- a/modules/OCPP/OCPP.cpp +++ b/modules/OCPP/OCPP.cpp @@ -7,8 +7,8 @@ #include #include -#include #include +#include #include #include @@ -16,76 +16,48 @@ namespace module { const std::string CERTS_SUB_DIR = "certs"; const std::string SQL_CORE_MIGRTATIONS = "core_migrations"; -const std::string CHARGE_X_MREC_VENDOR_ID = "https://chargex.inl.gov"; +const std::string INOPERATIVE_ERROR_TYPE = "evse_manager/Inoperative"; namespace fs = std::filesystem; -static ErrorInfo get_error_info(const std::optional error) { +/// \brief Converts the given \p error into the ErrorInfo that contains all necessary data for a +/// StatusNotification.req +static ocpp::v16::ErrorInfo get_error_info(const Everest::error::Error& error) { - if (!error.has_value()) { - return {ocpp::v16::ChargePointErrorCode::InternalError}; + const auto error_type = error.type; + const auto uuid = error.uuid.uuid; + + auto it = std::find_if(MREC_ERROR_MAP.begin(), MREC_ERROR_MAP.end(), + [&](const auto& entry) { return error_type.find(entry.first) != std::string::npos; }); + + // is MREC error + if (it != MREC_ERROR_MAP.end()) { + // lambda to create MREC error info + auto make_mrec_error_info = [&](ocpp::v16::ChargePointErrorCode code, const std::string& vendor_error_code) { + return ocpp::v16::ErrorInfo{uuid, code, false, std::nullopt, CHARGE_X_MREC_VENDOR_ID, vendor_error_code}; + }; + return make_mrec_error_info(it->second.first, it->second.second); + } + + if (error_type == INOPERATIVE_ERROR_TYPE) { + return ocpp::v16::ErrorInfo{uuid, ocpp::v16::ChargePointErrorCode::OtherError, + true, "EVSE is inoperative", + "EVerest", "caused_by:" + error.message}; } - const auto error_code = error.value().error_code; - - switch (error_code) { - case types::evse_manager::ErrorEnum::MREC1ConnectorLockFailure: - return {ocpp::v16::ChargePointErrorCode::ConnectorLockFailure, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX001"}; - case types::evse_manager::ErrorEnum::MREC2GroundFailure: - return {ocpp::v16::ChargePointErrorCode::GroundFailure, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX002"}; - case types::evse_manager::ErrorEnum::MREC3HighTemperature: - return {ocpp::v16::ChargePointErrorCode::HighTemperature, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX003"}; - case types::evse_manager::ErrorEnum::MREC4OverCurrentFailure: - return {ocpp::v16::ChargePointErrorCode::OverCurrentFailure, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX004"}; - case types::evse_manager::ErrorEnum::MREC5OverVoltage: - return {ocpp::v16::ChargePointErrorCode::OverVoltage, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX005"}; - case types::evse_manager::ErrorEnum::MREC6UnderVoltage: - return {ocpp::v16::ChargePointErrorCode::UnderVoltage, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX006"}; - case types::evse_manager::ErrorEnum::MREC8EmergencyStop: - return {ocpp::v16::ChargePointErrorCode::OtherError, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX008"}; - case types::evse_manager::ErrorEnum::MREC10InvalidVehicleMode: - return {ocpp::v16::ChargePointErrorCode::OtherError, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX010"}; - case types::evse_manager::ErrorEnum::MREC14PilotFault: - return {ocpp::v16::ChargePointErrorCode::OtherError, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX014"}; - case types::evse_manager::ErrorEnum::MREC15PowerLoss: - return {ocpp::v16::ChargePointErrorCode::OtherError, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX015"}; - case types::evse_manager::ErrorEnum::MREC17EVSEContactorFault: - return {ocpp::v16::ChargePointErrorCode::OtherError, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX017"}; - case types::evse_manager::ErrorEnum::MREC18CableOverTempDerate: - return {ocpp::v16::ChargePointErrorCode::OtherError, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX018"}; - case types::evse_manager::ErrorEnum::MREC19CableOverTempStop: - return {ocpp::v16::ChargePointErrorCode::OtherError, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX019"}; - case types::evse_manager::ErrorEnum::MREC20PartialInsertion: - return {ocpp::v16::ChargePointErrorCode::OtherError, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX020"}; - case types::evse_manager::ErrorEnum::MREC23ProximityFault: - return {ocpp::v16::ChargePointErrorCode::OtherError, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX023"}; - case types::evse_manager::ErrorEnum::MREC24ConnectorVoltageHigh: - return {ocpp::v16::ChargePointErrorCode::OtherError, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX024"}; - case types::evse_manager::ErrorEnum::MREC25BrokenLatch: - return {ocpp::v16::ChargePointErrorCode::OtherError, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX025"}; - case types::evse_manager::ErrorEnum::MREC26CutCable: - return {ocpp::v16::ChargePointErrorCode::OtherError, std::nullopt, CHARGE_X_MREC_VENDOR_ID, "CX026"}; - case types::evse_manager::ErrorEnum::RCD_Selftest: - case types::evse_manager::ErrorEnum::RCD_DC: - case types::evse_manager::ErrorEnum::RCD_AC: - case types::evse_manager::ErrorEnum::VendorError: - case types::evse_manager::ErrorEnum::VendorWarning: - case types::evse_manager::ErrorEnum::ConnectorLockCapNotCharged: - case types::evse_manager::ErrorEnum::ConnectorLockUnexpectedOpen: - case types::evse_manager::ErrorEnum::ConnectorLockUnexpectedClose: - case types::evse_manager::ErrorEnum::ConnectorLockFailedLock: - case types::evse_manager::ErrorEnum::ConnectorLockFailedUnlock: - case types::evse_manager::ErrorEnum::DiodeFault: - case types::evse_manager::ErrorEnum::VentilationNotAvailable: - case types::evse_manager::ErrorEnum::BrownOut: - case types::evse_manager::ErrorEnum::EnergyManagement: - case types::evse_manager::ErrorEnum::PermanentFault: - case types::evse_manager::ErrorEnum::PowermeterTransactionStartFailed: - default: - return {ocpp::v16::ChargePointErrorCode::InternalError, types::evse_manager::error_enum_to_string(error_code)}; + // check if is VendorError + if (error_type.find("VendorError") != std::string::npos) { + return ocpp::v16::ErrorInfo{uuid, + ocpp::v16::ChargePointErrorCode::OtherError, + false, + error.description, + error.origin.to_string(), + error.sub_type}; } - return {ocpp::v16::ChargePointErrorCode::InternalError}; + // Default case + return ocpp::v16::ErrorInfo{ + uuid, ocpp::v16::ChargePointErrorCode::InternalError, false, error.description, std::nullopt, error_type}; } void create_empty_user_config(const fs::path& user_config_path) { @@ -242,18 +214,6 @@ void OCPP::process_session_event(int32_t evse_id, const types::evse_manager::Ses // ev side disconnect this->evse_soc_map[evse_id].reset(); this->charge_point->on_session_stopped(ocpp_connector_id, session_event.uuid); - } else if (session_event.event == types::evse_manager::SessionEventEnum::Error) { - EVLOG_debug << "Connector#" << ocpp_connector_id << ": " - << "Received Error"; - const auto error_info = get_error_info(session_event.error); - this->charge_point->on_error(ocpp_connector_id, error_info.ocpp_error_code, error_info.info, - error_info.vendor_id, error_info.vendor_error_code); - } else if (session_event.event == types::evse_manager::SessionEventEnum::AllErrorsCleared) { - this->charge_point->on_fault(ocpp_connector_id, ocpp::v16::ChargePointErrorCode::NoError); - } else if (session_event.event == types::evse_manager::SessionEventEnum::PermanentFault) { - const auto error_info = get_error_info(session_event.error); - this->charge_point->on_fault(ocpp_connector_id, error_info.ocpp_error_code, error_info.info, - error_info.vendor_id, error_info.vendor_error_code); } else if (session_event.event == types::evse_manager::SessionEventEnum::ReservationStart) { this->charge_point->on_reservation_start(ocpp_connector_id); } else if (session_event.event == types::evse_manager::SessionEventEnum::ReservationEnd) { @@ -297,7 +257,7 @@ void OCPP::init_evse_subscriptions() { EVLOG_info << "OCPP not fully initialized, but received a session event on evse_id: " << evse_id << " that will be queued up: " << session_event.event; std::scoped_lock lock(this->session_event_mutex); - this->session_event_queue[evse_id].push(session_event); + this->event_queue[evse_id].push(session_event); return; } @@ -369,6 +329,28 @@ void OCPP::init() { invoke_init(*p_auth_provider); invoke_init(*p_data_transfer); + const auto error_handler = [this](const Everest::error::Error& error) { + const auto evse_id = error.origin.mapping.has_value() ? error.origin.mapping.value().evse : 0; + const auto error_info = get_error_info(error); + if (this->started) { + this->charge_point->on_error(evse_id, error_info); + } else { + this->event_queue[evse_id].push(error_info); + } + }; + + const auto error_cleared_handler = [this](const Everest::error::Error& error) { + const auto evse_id = error.origin.mapping.has_value() ? error.origin.mapping.value().evse : 0; + + if (this->started) { + this->charge_point->on_error_cleared(evse_id, error.uuid.uuid); + } else { + this->event_queue[evse_id].push(error.uuid.uuid); + } + }; + + subscribe_global_all_errors(error_handler, error_cleared_handler); + this->init_evse_maps(); for (size_t evse_id = 1; evse_id <= this->r_evse_manager.size(); evse_id++) { @@ -795,13 +777,27 @@ void OCPP::ready() { // process session event queue std::scoped_lock lock(this->session_event_mutex); - for (auto& [evse_id, evse_session_event_queue] : this->session_event_queue) { - while (!evse_session_event_queue.empty()) { - auto queued_session_event = evse_session_event_queue.front(); - EVLOG_info << "Processing queued session event for evse_id: " << evse_id - << ", event: " << queued_session_event.event; - this->process_session_event(evse_id, queued_session_event); - evse_session_event_queue.pop(); + for (auto& [evse_id, evse_event_queue] : this->event_queue) { + while (!evse_event_queue.empty()) { + auto queued_event = evse_event_queue.front(); + if (std::holds_alternative(queued_event)) { + const auto session_event = std::get(queued_event); + EVLOG_info << "Processing queued event for evse_id: " << evse_id + << ", event: " << session_event.event; + this->process_session_event(evse_id, session_event); + } else if (std::holds_alternative(queued_event)) { + const auto error = std::get(queued_event); + EVLOG_info << "Processing queued error event for evse_id: " << evse_id + << ", error id: " << error.uuid; + this->charge_point->on_error(evse_id, error); + } else { + // holds string -> is event to clear error + const auto cleared_uuid = std::get(queued_event); + EVLOG_info << "Processing queued cleared error event for evse_id: " << evse_id + << ", error id: " << cleared_uuid; + this->charge_point->on_error_cleared(evse_id, cleared_uuid); + } + evse_event_queue.pop(); } } } diff --git a/modules/OCPP/OCPP.hpp b/modules/OCPP/OCPP.hpp index 3eab24467..5557f3a49 100644 --- a/modules/OCPP/OCPP.hpp +++ b/modules/OCPP/OCPP.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,10 @@ #include using EvseConnectorMap = std::map>; +using ClearedErrorId = std::string; +using EventQueue = + std::map>>; // ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1 namespace module { @@ -142,20 +147,12 @@ class OCPP : public Everest::ModuleBase { std::atomic_bool started{false}; std::mutex session_event_mutex; - std::map> session_event_queue; + EventQueue event_queue; void process_session_event(int32_t evse_id, const types::evse_manager::SessionEvent& session_event); // ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1 }; // ev@087e516b-124c-48df-94fb-109508c7cda9:v1 -// insert other definitions here -/// \brief Contains information about an error -struct ErrorInfo { - ocpp::v16::ChargePointErrorCode ocpp_error_code; - std::optional info; - std::optional vendor_id; - std::optional vendor_error_code; -}; // ev@087e516b-124c-48df-94fb-109508c7cda9:v1 } // namespace module diff --git a/modules/OCPP/doc.rst b/modules/OCPP/doc.rst index 171dad9df..4cbcfdffa 100644 --- a/modules/OCPP/doc.rst +++ b/modules/OCPP/doc.rst @@ -1,3 +1,9 @@ +Global Errors +============= + +The `enable_global_errors` flag for this module is true. This module is therefore able to retrieve and process all reported errors +from other modules loaded in the same EVerest configuration. + Interaction with EVSE Manager ============================= diff --git a/modules/OCPP/error_mapping.hpp b/modules/OCPP/error_mapping.hpp new file mode 100644 index 000000000..da49f3ee3 --- /dev/null +++ b/modules/OCPP/error_mapping.hpp @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest +#ifndef OCPP_ERROR_MAPPING_HPP +#define OCPP_ERROR_MAPPING_HPP + +#include + +#include + +namespace module { + +const std::string CHARGE_X_MREC_VENDOR_ID = "https://chargex.inl.gov"; + +// Error type mappings +const std::unordered_map> MREC_ERROR_MAP = { + {"connector_lock/MREC1ConnectorLockFailure", {ocpp::v16::ChargePointErrorCode::ConnectorLockFailure, "CX001"}}, + {"evse_board_support/MREC2GroundFailure", {ocpp::v16::ChargePointErrorCode::GroundFailure, "CX002"}}, + {"evse_board_support/MREC3HighTemperature", {ocpp::v16::ChargePointErrorCode::HighTemperature, "CX003"}}, + {"evse_board_support/MREC4OverCurrentFailure", {ocpp::v16::ChargePointErrorCode::OverCurrentFailure, "CX004"}}, + {"evse_board_support/MREC5OverVoltage", {ocpp::v16::ChargePointErrorCode::OverVoltage, "CX005"}}, + {"evse_board_support/MREC6UnderVoltage", {ocpp::v16::ChargePointErrorCode::UnderVoltage, "CX006"}}, + {"evse_board_support/MREC8EmergencyStop", {ocpp::v16::ChargePointErrorCode::OtherError, "CX008"}}, + {"evse_board_support/MREC10InvalidVehicleMode", {ocpp::v16::ChargePointErrorCode::OtherError, "CX010"}}, + {"evse_board_support/MREC14PilotFault", {ocpp::v16::ChargePointErrorCode::OtherError, "CX014"}}, + {"evse_board_support/MREC15PowerLoss", {ocpp::v16::ChargePointErrorCode::OtherError, "CX015"}}, + {"evse_board_support/MREC17EVSEContactorFault", {ocpp::v16::ChargePointErrorCode::OtherError, "CX017"}}, + {"evse_board_support/MREC18CableOverTempDerate", {ocpp::v16::ChargePointErrorCode::OtherError, "CX018"}}, + {"evse_board_support/MREC19CableOverTempStop", {ocpp::v16::ChargePointErrorCode::OtherError, "CX019"}}, + {"evse_board_support/MREC20PartialInsertion", {ocpp::v16::ChargePointErrorCode::OtherError, "CX020"}}, + {"evse_board_support/MREC23ProximityFault", {ocpp::v16::ChargePointErrorCode::OtherError, "CX023"}}, + {"evse_board_support/MREC24ConnectorVoltageHigh", {ocpp::v16::ChargePointErrorCode::OtherError, "CX024"}}, + {"evse_board_support/MREC25BrokenLatch", {ocpp::v16::ChargePointErrorCode::OtherError, "CX025"}}, + {"evse_board_support/MREC26CutCable", {ocpp::v16::ChargePointErrorCode::OtherError, "CX026"}}, + {"evse_manager/MREC4OverCurrentFailure", {ocpp::v16::ChargePointErrorCode::OverCurrentFailure, "CX004"}}, + {"ac_rcd/MREC2GroundFailure", {ocpp::v16::ChargePointErrorCode::GroundFailure, "CX002"}}, +}; + +} // namespace module + +#endif diff --git a/modules/OCPP/manifest.yaml b/modules/OCPP/manifest.yaml index b3f40962a..6aa945776 100644 --- a/modules/OCPP/manifest.yaml +++ b/modules/OCPP/manifest.yaml @@ -81,6 +81,7 @@ requires: min_connections: 0 max_connections: 1 enable_external_mqtt: true +enable_global_errors: true metadata: license: https://opensource.org/licenses/Apache-2.0 authors: diff --git a/modules/OCPP201/OCPP201.cpp b/modules/OCPP201/OCPP201.cpp index 2e708e9ec..fe92ae853 100644 --- a/modules/OCPP201/OCPP201.cpp +++ b/modules/OCPP201/OCPP201.cpp @@ -636,6 +636,17 @@ void OCPP201::ready() { this->r_evse_manager.at(evse_id - 1)->call_set_get_certificate_response(everest_response); }); + auto fault_handler = [this, evse_id](const Everest::error::Error& error) { + this->charge_point->on_faulted(evse_id, 1); + }; + + auto fault_cleared_handler = [this, evse_id](const Everest::error::Error& error) { + this->charge_point->on_fault_cleared(evse_id, 1); + }; + + // A permanent fault from the evse requirement indicates that the evse should move to faulted state + evse->subscribe_error("evse_manager/Inoperative", fault_handler, fault_cleared_handler); + evse_id++; } r_system->subscribe_firmware_update_status([this](const types::system::FirmwareUpdateStatus status) { diff --git a/modules/OCPP201/doc.rst b/modules/OCPP201/doc.rst new file mode 100644 index 000000000..e975e1760 --- /dev/null +++ b/modules/OCPP201/doc.rst @@ -0,0 +1,5 @@ +Global Errors +============= + +The `enable_global_errors` flag for this module is true. This module is therefore able to retrieve and process all reported errors +from other modules loaded in the same EVerest configuration. diff --git a/modules/OCPP201/manifest.yaml b/modules/OCPP201/manifest.yaml index 24283384b..e4dba6efd 100644 --- a/modules/OCPP201/manifest.yaml +++ b/modules/OCPP201/manifest.yaml @@ -70,6 +70,7 @@ requires: min_connections: 1 max_connections: 1 enable_external_mqtt: true +enable_global_errors: true metadata: license: https://opensource.org/licenses/Apache-2.0 authors: diff --git a/types/evse_manager.yaml b/types/evse_manager.yaml index 79957779a..ca17fe7ab 100644 --- a/types/evse_manager.yaml +++ b/types/evse_manager.yaml @@ -104,9 +104,6 @@ types: ChargingFinished: Charging is finished. Essentially the same as TransactionFinished, but emitted for clarity TransactionFinished: Signaled when the transaction finished. Transaction finishes at the point where one of the preconditions for charging irrevocably becomes false: When a user swipes to stop the transaction and the stop is authorized. SessionFinished: Session finishes at the point that the EVSE is available again (no cable plugged) - Error: Signaled when an error occured. An error doesnt prevent further charging operations. - AllErrorsCleared: Signalled when all errors are cleared - PermanentFault: Signaled when there is a permanent fault at the EVSE. A permanent fault prevents further charging operations ReservationStart: Signaled when a reservation starts ReservationEnd: Signaled when a reservation ends ReplugStarted: Signaled when the EVSE Manager virtually replugs without interrupting the session or transaction @@ -131,11 +128,6 @@ types: - ChargingFinished - TransactionFinished - SessionFinished - - Error - - ErrorCleared - - AllErrorsCleared - - PermanentFault - - PermanentFaultCleared - ReservationStart - ReservationEnd - ReplugStarted @@ -259,73 +251,6 @@ types: description: Exported meter value type: object $ref: /powermeter#/Powermeter - ErrorEnum: - description: >- - Note this is only kept for compatibility with the legacy error handling and will be removed soon. - type: string - enum: - - RCD_Selftest - - RCD_DC - - RCD_AC - - VendorError - - VendorWarning - - ConnectorLockCapNotCharged - - ConnectorLockUnexpectedOpen - - ConnectorLockUnexpectedClose - - ConnectorLockFailedLock - - ConnectorLockFailedUnlock - - MREC1ConnectorLockFailure - - MREC2GroundFailure - - MREC3HighTemperature - - MREC4OverCurrentFailure - - MREC5OverVoltage - - MREC6UnderVoltage - - MREC8EmergencyStop - - MREC10InvalidVehicleMode - - MREC14PilotFault - - MREC15PowerLoss - - MREC17EVSEContactorFault - - MREC18CableOverTempDerate - - MREC19CableOverTempStop - - MREC20PartialInsertion - - MREC23ProximityFault - - MREC24ConnectorVoltageHigh - - MREC25BrokenLatch - - MREC26CutCable - - DiodeFault - - VentilationNotAvailable - - BrownOut - - EnergyManagement - - PermanentFault - - PowermeterTransactionStartFailed - - IMDFault - Error: - description: >- - Error object that contains information about the error and optional vendor error information - type: object - additionalProperties: false - required: - - error_code - - error_description - - error_severity - properties: - error_code: - description: The error enum - type: string - $ref: /evse_manager#/ErrorEnum - error_description: - description: Description of the error (human readable) - type: string - error_severity: - description: Severity of the error - type: string - enum: - - High - - Medium - - Low - vendor_error: - description: The error code of the vendor - type: string EnableDisableSource: description: >- Source of a Enable or Disable command/event @@ -418,10 +343,6 @@ types: Authorized, Deauthorized type: object $ref: /evse_manager#/AuthorizationEvent - error: - description: Details on error type - type: object - $ref: /evse_manager#/Error source: description: >- Additional data for Enabled/Disabled events. Specifies the source of the command that changed the state.