diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6c1af3f60..633c28832 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -35,6 +35,7 @@ /modules/simulation/ @SebaLukas @pietfried @hikinggrass /modules/SlacSimulator/ @SebaLukas @pietfried @corneliusclaussen @MarzellT /modules/rust_examples/ @SirVer @golovasteek @dorezyuk +/modules/YetiSimulator/ @SebaLukas @pietfried @corneliusclaussen @MarzellT **/Cargo.toml @SirVer @golovasteek @dorezyuk **/Cargo.lock @SirVer @golovasteek @dorezyuk diff --git a/config/config-sil-dc-sae-v2g.yaml b/config/config-sil-dc-sae-v2g.yaml index b7591ed3c..fc1dfa8d8 100644 --- a/config/config-sil-dc-sae-v2g.yaml +++ b/config/config-sil-dc-sae-v2g.yaml @@ -49,7 +49,7 @@ active_modules: powersupply_dc: module: DCSupplySimulator yeti_driver: - module: JsYetiSimulator + module: YetiSimulator config_module: connector_id: 1 slac: diff --git a/config/config-sil-dc-sae-v2h.yaml b/config/config-sil-dc-sae-v2h.yaml index fe2f9e11a..8ff638f1e 100644 --- a/config/config-sil-dc-sae-v2h.yaml +++ b/config/config-sil-dc-sae-v2h.yaml @@ -49,7 +49,7 @@ active_modules: powersupply_dc: module: DCSupplySimulator yeti_driver: - module: JsYetiSimulator + module: YetiSimulator config_module: connector_id: 1 slac: diff --git a/config/config-sil-dc-tls.yaml b/config/config-sil-dc-tls.yaml index 7645821b2..731414f13 100644 --- a/config/config-sil-dc-tls.yaml +++ b/config/config-sil-dc-tls.yaml @@ -50,7 +50,7 @@ active_modules: powersupply_dc: module: DCSupplySimulator yeti_driver: - module: JsYetiSimulator + module: YetiSimulator config_module: connector_id: 1 slac: diff --git a/config/config-sil-dc.yaml b/config/config-sil-dc.yaml index 9770bdd19..f9645def0 100644 --- a/config/config-sil-dc.yaml +++ b/config/config-sil-dc.yaml @@ -47,7 +47,7 @@ active_modules: powersupply_dc: module: DCSupplySimulator yeti_driver: - module: JsYetiSimulator + module: YetiSimulator config_module: connector_id: 1 slac: diff --git a/config/config-sil-energy-management.yaml b/config/config-sil-energy-management.yaml index 199f8b5e8..a4312bab6 100644 --- a/config/config-sil-energy-management.yaml +++ b/config/config-sil-energy-management.yaml @@ -60,11 +60,11 @@ active_modules: - module_id: yeti_driver_2 implementation_id: powermeter yeti_driver_1: - module: JsYetiSimulator + module: YetiSimulator config_module: connector_id: 1 yeti_driver_2: - module: JsYetiSimulator + module: YetiSimulator config_module: connector_id: 2 slac: diff --git a/config/config-sil-ocpp-custom-extension.yaml b/config/config-sil-ocpp-custom-extension.yaml index 0a13a162e..be52bac69 100644 --- a/config/config-sil-ocpp-custom-extension.yaml +++ b/config/config-sil-ocpp-custom-extension.yaml @@ -64,12 +64,12 @@ active_modules: - module_id: iso15118_charger implementation_id: charger yeti_driver_1: - module: JsYetiSimulator + module: YetiSimulator evse: 1 config_module: connector_id: 1 yeti_driver_2: - module: JsYetiSimulator + module: YetiSimulator evse: 2 config_module: connector_id: 2 diff --git a/config/config-sil-ocpp-pnc.yaml b/config/config-sil-ocpp-pnc.yaml index 9771903a6..ae6ab7da1 100644 --- a/config/config-sil-ocpp-pnc.yaml +++ b/config/config-sil-ocpp-pnc.yaml @@ -67,12 +67,12 @@ active_modules: - module_id: iso15118_charger implementation_id: charger yeti_driver_1: - module: JsYetiSimulator + module: YetiSimulator evse: 1 config_module: connector_id: 1 yeti_driver_2: - module: JsYetiSimulator + module: YetiSimulator evse: 2 config_module: connector_id: 2 diff --git a/config/config-sil-ocpp.yaml b/config/config-sil-ocpp.yaml index 7ad065089..6be30f29d 100644 --- a/config/config-sil-ocpp.yaml +++ b/config/config-sil-ocpp.yaml @@ -74,12 +74,12 @@ active_modules: implementation_id: charger yeti_driver_1: evse: 1 - module: JsYetiSimulator + module: YetiSimulator config_module: connector_id: 1 yeti_driver_2: evse: 2 - module: JsYetiSimulator + module: YetiSimulator config_module: connector_id: 2 slac: diff --git a/config/config-sil-ocpp201-pnc.yaml b/config/config-sil-ocpp201-pnc.yaml index 834e353bf..f76db500d 100644 --- a/config/config-sil-ocpp201-pnc.yaml +++ b/config/config-sil-ocpp201-pnc.yaml @@ -67,12 +67,12 @@ active_modules: - module_id: iso15118_charger implementation_id: charger yeti_driver_1: - module: JsYetiSimulator + module: YetiSimulator evse: 1 config_module: connector_id: 1 yeti_driver_2: - module: JsYetiSimulator + module: YetiSimulator evse: 2 config_module: connector_id: 2 diff --git a/config/config-sil-ocpp201.yaml b/config/config-sil-ocpp201.yaml index 93ba79bb1..9af912ca0 100644 --- a/config/config-sil-ocpp201.yaml +++ b/config/config-sil-ocpp201.yaml @@ -66,12 +66,12 @@ active_modules: - module_id: iso15118_charger implementation_id: charger yeti_driver_1: - module: JsYetiSimulator + module: YetiSimulator evse: 1 config_module: connector_id: 1 yeti_driver_2: - module: JsYetiSimulator + module: YetiSimulator evse: 2 config_module: connector_id: 2 diff --git a/config/config-sil-two-evse-dc.yaml b/config/config-sil-two-evse-dc.yaml index 01be2f551..4c21cd454 100644 --- a/config/config-sil-two-evse-dc.yaml +++ b/config/config-sil-two-evse-dc.yaml @@ -62,11 +62,11 @@ active_modules: - module_id: yeti_driver_2 implementation_id: powermeter yeti_driver_1: - module: JsYetiSimulator + module: YetiSimulator config_module: connector_id: 1 yeti_driver_2: - module: JsYetiSimulator + module: YetiSimulator config_module: connector_id: 2 slac_1: diff --git a/config/config-sil-two-evse.yaml b/config/config-sil-two-evse.yaml index a0c511c64..4a5565dbc 100644 --- a/config/config-sil-two-evse.yaml +++ b/config/config-sil-two-evse.yaml @@ -56,11 +56,11 @@ active_modules: - module_id: yeti_driver_2 implementation_id: powermeter yeti_driver_1: - module: JsYetiSimulator + module: YetiSimulator config_module: connector_id: 1 yeti_driver_2: - module: JsYetiSimulator + module: YetiSimulator config_module: connector_id: 2 slac: diff --git a/config/config-sil.yaml b/config/config-sil.yaml index eb6b994b7..72b1f7a6d 100644 --- a/config/config-sil.yaml +++ b/config/config-sil.yaml @@ -177,7 +177,7 @@ active_modules: config_module: connector_id: 1 connections: {} - module: JsYetiSimulator + module: YetiSimulator telemetry: id: 1 'x-module-layout': diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index fea8c4923..e31f5a162 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -34,6 +34,7 @@ ev_add_module(DummyTokenValidator) ev_add_module(DummyTokenProvider) ev_add_module(DummyTokenProviderManual) ev_add_module(PhyVersoBSP) +ev_add_module(YetiSimulator) add_subdirectory(examples) add_subdirectory(simulation) diff --git a/modules/YetiSimulator/CMakeLists.txt b/modules/YetiSimulator/CMakeLists.txt new file mode 100644 index 000000000..215307ded --- /dev/null +++ b/modules/YetiSimulator/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# AUTO GENERATED - MARKED REGIONS WILL BE KEPT +# template version 3 +# + +# module setup: +# - ${MODULE_NAME}: module name +ev_setup_cpp_module() + +# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1 +# insert your custom targets and additional config variables here +# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1 + +target_sources(${MODULE_NAME} + PRIVATE + "powermeter/powermeterImpl.cpp" + "board_support/evse_board_supportImpl.cpp" + "ev_board_support/ev_board_supportImpl.cpp" + "rcd/ac_rcdImpl.cpp" + "connector_lock/connector_lockImpl.cpp" +) + +# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1 +# insert other things like install cmds etc here +target_sources(${MODULE_NAME} + PRIVATE + "util/state.cpp" + "util/mqtt_handler.cpp" + "util/error_handler.cpp" + "util/util.cpp" +) +# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1 diff --git a/modules/YetiSimulator/YetiSimulator.cpp b/modules/YetiSimulator/YetiSimulator.cpp new file mode 100644 index 000000000..e54b784dd --- /dev/null +++ b/modules/YetiSimulator/YetiSimulator.cpp @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest +#include "YetiSimulator.hpp" +#include "board_support/evse_board_supportImpl.hpp" +#include "util/util.hpp" + +namespace module { + +void YetiSimulator::init() { + invoke_init(*p_powermeter); + invoke_init(*p_board_support); + invoke_init(*p_ev_board_support); + invoke_init(*p_rcd); + invoke_init(*p_connector_lock); + + clear_data(); + + mqtt_handler = std::make_unique(p_board_support.get(), p_rcd.get(), p_connector_lock.get()); + mqtt.subscribe("everest_external/nodered/" + std::to_string(config.connector_id) + "/carsim/error", + [this](const std::string& payload) { mqtt_handler->handle_mqtt_payload(payload); }); +} + +void YetiSimulator::ready() { + invoke_ready(*p_powermeter); + invoke_ready(*p_board_support); + invoke_ready(*p_ev_board_support); + invoke_ready(*p_rcd); + invoke_ready(*p_connector_lock); + + module_state->pubCnt = 0; + + Simulator(250, this); + if (info.telemetry_enabled) { + std::thread(&YetiSimulator::run_telemetry_slow, this).detach(); + std::thread(&YetiSimulator::run_telemetry_fast, this).detach(); + } +} + +state::ModuleState& YetiSimulator::get_module_state() { + return *module_state; +} + +evse_board_supportImplBase& YetiSimulator::get_board_support() { + return *p_board_support; +}; + +ev_board_supportImplBase& YetiSimulator::get_ev_board_support() { + return *p_ev_board_support; +} + +ac_rcdImplBase& YetiSimulator::get_ac_rcd() { + return *p_rcd; +} + +connector_lockImplBase& YetiSimulator::get_connector_lock() { + return *p_connector_lock; +} + +Everest::MqttProvider& YetiSimulator::get_mqtt() { + return mqtt; +} +const ModuleInfo& YetiSimulator::get_info() const { + return info; +} +powermeterImplBase& YetiSimulator::get_powermeter() { + return *p_powermeter; +} + +void YetiSimulator::clear_data() { + module_state = std::make_unique(); +} + +void YetiSimulator::run_telemetry_slow() { + const auto current_iso_time_string = util::get_current_iso_time_string(); + + auto& p_p_c_v = module_state->telemetry_data.power_path_controller_version; + p_p_c_v.timestamp = current_iso_time_string; + + telemetry.publish("livedata", "power_path_controller_version", + {{"timestamp", p_p_c_v.timestamp}, + {"type", p_p_c_v.type}, + {"hardware_version", p_p_c_v.hardware_version}, + {"software_version", p_p_c_v.software_version}, + {"date_manufactured", p_p_c_v.date_manufactured}, + {"operating_time_h", p_p_c_v.operating_time_h}, + {"operating_time_warning", p_p_c_v.operating_time_h_warning}, + {"operating_time_error", p_p_c_v.operating_time_h_error}, + {"error", p_p_c_v.error}}); +} + +void YetiSimulator::run_telemetry_fast() { + const auto current_iso_time_string = util::get_current_iso_time_string(); + auto& p_p_c = module_state->telemetry_data.power_path_controller; + p_p_c.timestamp = current_iso_time_string; + p_p_c.cp_voltage_high = module_state->pwm_voltage_hi; // TODO: check if this is the correct value + p_p_c.cp_voltage_low = module_state->pwm_voltage_lo; // TODO: same here + p_p_c.cp_pwm_duty_cycle = module_state->pwm_duty_cycle * 100.0; + p_p_c.cp_state = state_to_string(*module_state); + + p_p_c.temperature_controller = module_state->powermeter_data.tempL1; + p_p_c.temperature_car_connector = module_state->powermeter_data.tempL1 * 2.0; + p_p_c.watchdog_reset_count = 0; + p_p_c.error = false; + + auto& p_s = module_state->telemetry_data.power_switch; + p_s.timestamp = current_iso_time_string; + p_s.is_on = module_state->relais_on; + p_s.time_to_switch_on_ms = 110; + p_s.time_to_switch_off_ms = 100; + p_s.temperature_C = 20; + p_s.error = false; + p_s.error_over_current = false; + + auto& rcd = module_state->telemetry_data.rcd; + rcd.timestamp = current_iso_time_string; + rcd.current_mA = module_state->simulation_data.rcd_current; + + telemetry.publish("livedata", "power_path_controller", + {{"timestamp", p_p_c.timestamp}, + {"type", p_p_c.type}, + {"cp_voltage_high", p_p_c.cp_voltage_high}, + {"cp_voltage_low", p_p_c.cp_voltage_low}, + {"cp_pwm_duty_cycle", p_p_c.cp_pwm_duty_cycle}, + {"cp_state", p_p_c.cp_state}, + {"pp_ohm", p_p_c.pp_ohm}, + {"supply_voltage_12V", p_p_c.supply_voltage_12V}, + {"supply_voltage_minus_12V", p_p_c.supply_voltage_minus_12V}, + {"temperature_controller", p_p_c.temperature_controller}, + {"temperature_car_connector", p_p_c.temperature_car_connector}, + {"watchdog_reset_count", p_p_c.watchdog_reset_count}, + {"error", p_p_c.error}}); + telemetry.publish("livedata", "power_switch", + {{"timestamp", p_s.timestamp}, + {"type", p_s.type}, + {"switching_count", p_s.switching_count}, + {"switching_count_warning", p_s.switching_count_warning}, + {"switching_count_error", p_s.switching_count_error}, + {"is_on", p_s.is_on}, + {"time_to_switch_on_ms", p_s.time_to_switch_on_ms}, + {"time_to_switch_off_ms", p_s.time_to_switch_off_ms}, + {"temperature_C", p_s.temperature_C}, + {"error", p_s.error}, + {"error_over_current", p_s.error_over_current}}); + telemetry.publish("livedata", "rcd", + {{"timestamp", rcd.timestamp}, + {"type", rcd.type}, + {"enabled", rcd.enabled}, + {"current_mA", rcd.current_mA}, + {"triggered", rcd.triggered}, + {"error", rcd.error}}); +} + +} // namespace module diff --git a/modules/YetiSimulator/YetiSimulator.hpp b/modules/YetiSimulator/YetiSimulator.hpp new file mode 100644 index 000000000..0d264923b --- /dev/null +++ b/modules/YetiSimulator/YetiSimulator.hpp @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest +#ifndef YETI_SIMULATOR_HPP +#define YETI_SIMULATOR_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 2 +// + +#include "ld-ev.hpp" + +// headers for provided interface implementations +#include +#include +#include +#include +#include + +// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1 +// insert your custom include headers here +#include "util/mqtt_handler.hpp" +#include "util/simulator.hpp" +#include "util/state.hpp" +// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1 + +namespace module { + +struct Conf { + int connector_id; +}; + +class YetiSimulator : public Everest::ModuleBase { +public: + YetiSimulator() = delete; + YetiSimulator(const ModuleInfo& info, Everest::MqttProvider& mqtt_provider, Everest::TelemetryProvider& telemetry, + std::unique_ptr p_powermeter, + std::unique_ptr p_board_support, + std::unique_ptr p_ev_board_support, std::unique_ptr p_rcd, + std::unique_ptr p_connector_lock, Conf& config) : + ModuleBase(info), + mqtt(mqtt_provider), + telemetry(telemetry), + p_powermeter(std::move(p_powermeter)), + p_board_support(std::move(p_board_support)), + p_ev_board_support(std::move(p_ev_board_support)), + p_rcd(std::move(p_rcd)), + p_connector_lock(std::move(p_connector_lock)), + config(config){}; + + Everest::MqttProvider& mqtt; + Everest::TelemetryProvider& telemetry; + const std::unique_ptr p_powermeter; + const std::unique_ptr p_board_support; + const std::unique_ptr p_ev_board_support; + const std::unique_ptr p_rcd; + const std::unique_ptr p_connector_lock; + const Conf& config; + + // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1 + // insert your public definitions here + void clear_data(); + Everest::MqttProvider& get_mqtt(); + state::ModuleState& get_module_state(); + const ModuleInfo& get_info() const; + evse_board_supportImplBase& get_board_support(); + ev_board_supportImplBase& get_ev_board_support(); + powermeterImplBase& get_powermeter(); + ac_rcdImplBase& get_ac_rcd(); + connector_lockImplBase& get_connector_lock(); + // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1 + +protected: + // ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1 + // insert your protected definitions here + // ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1 + +private: + friend class LdEverest; + void init(); + void ready(); + + // ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1 + // insert your private definitions here + void run_telemetry_slow(); + void run_telemetry_fast(); + + std::unique_ptr module_state; + std::unique_ptr mqtt_handler; + + // ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1 +}; + +// ev@087e516b-124c-48df-94fb-109508c7cda9:v1 +// insert other definitions here +// ev@087e516b-124c-48df-94fb-109508c7cda9:v1 + +} // namespace module + +#endif // YETI_SIMULATOR_HPP diff --git a/modules/YetiSimulator/board_support/evse_board_supportImpl.cpp b/modules/YetiSimulator/board_support/evse_board_supportImpl.cpp new file mode 100644 index 000000000..2d0753097 --- /dev/null +++ b/modules/YetiSimulator/board_support/evse_board_supportImpl.cpp @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "evse_board_supportImpl.hpp" + +namespace module { +namespace board_support { + +void evse_board_supportImpl::init() { +} + +void evse_board_supportImpl::ready() { + const auto capabilities = types::evse_board_support::HardwareCapabilities{ + .max_current_A_import = 32.0, + .min_current_A_import = 6.0, + .max_phase_count_import = 3, + .min_phase_count_import = 1, + .max_current_A_export = 16.0, + .min_current_A_export = 0.0, + .max_phase_count_export = 3, + .min_phase_count_export = 1, + .supports_changing_phases_during_charging = true, + .connector_type = types::evse_board_support::Connector_type::IEC62196Type2Cable}; + publish_capabilities(capabilities); +} + +void evse_board_supportImpl::handle_enable(bool& value) { + auto& current_state = mod->get_module_state().current_state; + if (value) { + if (current_state == state::State::STATE_DISABLED) { + current_state = state::State::STATE_A; + } + } else { + current_state = state::State::STATE_DISABLED; + } +} + +void evse_board_supportImpl::handle_pwm_on(double& value) { + const auto dutycycle = value / 100.0; + pwm_on(dutycycle); +} + +void evse_board_supportImpl::handle_pwm_off() { + pwm_off(); +} + +void evse_board_supportImpl::handle_pwm_F() { + pwm_f(); +} + +void evse_board_supportImpl::handle_allow_power_on(types::evse_board_support::PowerOnOff& value) { + auto& module_state = mod->get_module_state(); + + module_state.power_on_allowed = value.allow_power_on; +} + +void evse_board_supportImpl::handle_ac_switch_three_phases_while_charging(bool& value) { + auto& module_state = mod->get_module_state(); + + module_state.use_three_phases = value; + module_state.use_three_phases_confirmed = value; +} + +void evse_board_supportImpl::handle_evse_replug(int& value) { + EVLOG_error << "Replugging not supported"; +} + +types::board_support_common::ProximityPilot evse_board_supportImpl::handle_ac_read_pp_ampacity() { + using types::board_support_common::Ampacity; + const auto pp_resistor = mod->get_module_state().simulation_data.pp_resistor; + + if (pp_resistor < 80 || pp_resistor > 2460) { + EVLOG_error << "PP resistor value " << pp_resistor << " Ohm seems to be outside the allowed range."; + return {.ampacity = Ampacity::None}; + } else if (pp_resistor > 936 && pp_resistor <= 2460) { + return {.ampacity = Ampacity::A_13}; + } else if (pp_resistor > 308 && pp_resistor <= 936) { + return {.ampacity = Ampacity::A_20}; + } else if (pp_resistor > 140 && pp_resistor <= 308) { + return {.ampacity = Ampacity::A_32}; + } else if (pp_resistor > 80 && pp_resistor <= 140) { + return {.ampacity = Ampacity::A_63_3ph_70_1ph}; + } else { + return {.ampacity = Ampacity::None}; + } +} + +void evse_board_supportImpl::handle_ac_set_overcurrent_limit_A(double& value) { + // TODO: intentional? +} + +void evse_board_supportImpl::pwm_on(double dutycycle) { + auto& module_state = mod->get_module_state(); + + if (dutycycle > 0.0) { + module_state.pwm_duty_cycle = dutycycle; + module_state.pwm_running = true; + module_state.pwm_error_f = false; + } else { + pwm_off(); + } +} + +void evse_board_supportImpl::pwm_off() { + auto& module_state = mod->get_module_state(); + + module_state.pwm_duty_cycle = 1.0; + module_state.pwm_running = false; + module_state.pwm_error_f = false; +} + +void evse_board_supportImpl::pwm_f() { + auto& module_state = mod->get_module_state(); + + module_state.pwm_duty_cycle = 1.0; + module_state.pwm_running = false; + module_state.pwm_error_f = true; +} + +} // namespace board_support +} // namespace module diff --git a/modules/YetiSimulator/board_support/evse_board_supportImpl.hpp b/modules/YetiSimulator/board_support/evse_board_supportImpl.hpp new file mode 100644 index 000000000..da94b9404 --- /dev/null +++ b/modules/YetiSimulator/board_support/evse_board_supportImpl.hpp @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest +#ifndef BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP +#define BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 3 +// + +#include + +#include "../YetiSimulator.hpp" + +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 +// insert your custom include headers here +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 + +namespace module { +namespace board_support { + +struct Conf {}; + +class evse_board_supportImpl : public evse_board_supportImplBase { +public: + evse_board_supportImpl() = delete; + evse_board_supportImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer& mod, Conf& config) : + evse_board_supportImplBase(ev, "board_support"), mod(mod), config(config){}; + + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + // insert your public definitions here + void pwm_off(); + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + +protected: + // command handler functions (virtual) + virtual void handle_enable(bool& value) override; + virtual void handle_pwm_on(double& value) override; + virtual void handle_pwm_off() override; + virtual void handle_pwm_F() override; + virtual void handle_allow_power_on(types::evse_board_support::PowerOnOff& value) override; + virtual void handle_ac_switch_three_phases_while_charging(bool& value) override; + virtual void handle_evse_replug(int& value) override; + virtual types::board_support_common::ProximityPilot handle_ac_read_pp_ampacity() override; + virtual void handle_ac_set_overcurrent_limit_A(double& value) override; + + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + // insert your protected definitions here + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + +private: + const Everest::PtrContainer& mod; + const Conf& config; + + virtual void init() override; + virtual void ready() override; + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 + // insert your private definitions here + void pwm_on(double dutycycle); + void pwm_f(); + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 +}; + +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 +// insert other definitions here +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 + +} // namespace board_support +} // namespace module + +#endif // BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP diff --git a/modules/YetiSimulator/connector_lock/connector_lockImpl.cpp b/modules/YetiSimulator/connector_lock/connector_lockImpl.cpp new file mode 100644 index 000000000..9508128af --- /dev/null +++ b/modules/YetiSimulator/connector_lock/connector_lockImpl.cpp @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "connector_lockImpl.hpp" + +namespace module { +namespace connector_lock { + +void connector_lockImpl::init() { +} + +void connector_lockImpl::ready() { +} + +void connector_lockImpl::handle_lock() { + EVLOG_info << "Lock connector"; +} + +void connector_lockImpl::handle_unlock() { + EVLOG_info << "Unlock connector"; +} + +} // namespace connector_lock +} // namespace module diff --git a/modules/YetiSimulator/connector_lock/connector_lockImpl.hpp b/modules/YetiSimulator/connector_lock/connector_lockImpl.hpp new file mode 100644 index 000000000..5d5769bab --- /dev/null +++ b/modules/YetiSimulator/connector_lock/connector_lockImpl.hpp @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest +#ifndef CONNECTOR_LOCK_CONNECTOR_LOCK_IMPL_HPP +#define CONNECTOR_LOCK_CONNECTOR_LOCK_IMPL_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 3 +// + +#include + +#include "../YetiSimulator.hpp" + +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 +// insert your custom include headers here +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 + +namespace module { +namespace connector_lock { + +struct Conf {}; + +class connector_lockImpl : public connector_lockImplBase { +public: + connector_lockImpl() = delete; + connector_lockImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer& mod, Conf& config) : + connector_lockImplBase(ev, "connector_lock"), mod(mod), config(config){}; + + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + // insert your public definitions here + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + +protected: + // command handler functions (virtual) + virtual void handle_lock() override; + virtual void handle_unlock() override; + + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + // insert your protected definitions here + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + +private: + const Everest::PtrContainer& mod; + const Conf& config; + + virtual void init() override; + virtual void ready() override; + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 + // insert your private definitions here + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 +}; + +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 +// insert other definitions here +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 + +} // namespace connector_lock +} // namespace module + +#endif // CONNECTOR_LOCK_CONNECTOR_LOCK_IMPL_HPP diff --git a/modules/YetiSimulator/doc.rst b/modules/YetiSimulator/doc.rst new file mode 100644 index 000000000..22c53d43f --- /dev/null +++ b/modules/YetiSimulator/doc.rst @@ -0,0 +1,22 @@ +.. _everest_modules_handwritten_YetiSimulator: + +.. This file is a placeholder for an optional single file +handwritten documentation for the YetiSimulator module. +Please decide whether you want to use this single file, +or a set of files in the doc/ directory. +In the latter case, you can delete this file. +In the former case, you can delete the doc/ directory. + +.. This handwritten documentation is optional. In case +you do not want to write it, you can delete this file +and the doc/ directory. + +.. The documentation can be written in reStructuredText, +and will be converted to HTML and PDF by Sphinx. + +******************************************* +YetiSimulator +******************************************* + +:ref:`Link ` to the module's reference. +SIL simulator for YETI hardware v1.0 diff --git a/modules/YetiSimulator/docs/index.rst b/modules/YetiSimulator/docs/index.rst new file mode 100644 index 000000000..7add145e1 --- /dev/null +++ b/modules/YetiSimulator/docs/index.rst @@ -0,0 +1,23 @@ +.. _everest_modules_handwritten_YetiSimulator: + +.. This file is a placeholder for optional multiple files +handwritten documentation for the YetiSimulator module. +Please decide whether you want to use the doc.rst file +or a set of files in the doc/ directory. +In the latter case, you can delete the doc.rst file. +In the former case, you can delete the doc/ directory. + +.. This handwritten documentation is optional. In case +you do not want to write it, you can delete this file +and the doc/ directory. + +.. The documentation can be written in reStructuredText, +and will be converted to HTML and PDF by Sphinx. +This index.rst file is the entry point for the module documentation. + +******************************************* +YetiSimulator +******************************************* + +:ref:`Link ` to the module's reference. +SIL simulator for YETI hardware v1.0 diff --git a/modules/YetiSimulator/ev_board_support/ev_board_supportImpl.cpp b/modules/YetiSimulator/ev_board_support/ev_board_supportImpl.cpp new file mode 100644 index 000000000..d64563b39 --- /dev/null +++ b/modules/YetiSimulator/ev_board_support/ev_board_supportImpl.cpp @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "ev_board_supportImpl.hpp" + +namespace module { +namespace ev_board_support { + +void ev_board_supportImpl::init() { +} + +void ev_board_supportImpl::ready() { +} + +void ev_board_supportImpl::handle_enable(bool& value) { + auto& module_state = mod->get_module_state(); + if (module_state.simulation_enabled && !value) { + publish_bsp_event({types::board_support_common::Event::A}); + mod->clear_data(); + } + module_state.simulation_enabled = value; +} + +void ev_board_supportImpl::handle_set_cp_state(types::ev_board_support::EvCpState& cp_state) { + using types::ev_board_support::EvCpState; + auto& simdata_setting = mod->get_module_state().simdata_setting; + + switch (cp_state) { + case EvCpState::A: + simdata_setting.cp_voltage = 12.0; + break; + case EvCpState::B: + simdata_setting.cp_voltage = 9.0; + break; + case EvCpState::C: + simdata_setting.cp_voltage = 6.0; + break; + case EvCpState::D: + simdata_setting.cp_voltage = 3.0; + break; + case EvCpState::E: + simdata_setting.error_e = true; + break; + default: + break; + }; +} + +void ev_board_supportImpl::handle_allow_power_on(bool& value) { + EVLOG_debug << "EV Power On: " << value; +} + +void ev_board_supportImpl::handle_diode_fail(bool& value) { + auto& simdata_setting = mod->get_module_state().simdata_setting; + + simdata_setting.diode_fail = value; +} + +void ev_board_supportImpl::handle_set_ac_max_current(double& current) { + auto& module_state = mod->get_module_state(); + + module_state.ev_max_current = current; +} + +void ev_board_supportImpl::handle_set_three_phases(bool& three_phases) { + auto& module_state = mod->get_module_state(); + + if (three_phases) { + module_state.ev_three_phases = 3; + } else { + module_state.ev_three_phases = 1; + } +} + +void ev_board_supportImpl::handle_set_rcd_error(double& rcd_current_mA) { + auto& simdata_setting = mod->get_module_state().simdata_setting; + + simdata_setting.rcd_current = rcd_current_mA; +} + +} // namespace ev_board_support +} // namespace module diff --git a/modules/YetiSimulator/ev_board_support/ev_board_supportImpl.hpp b/modules/YetiSimulator/ev_board_support/ev_board_supportImpl.hpp new file mode 100644 index 000000000..04e48b849 --- /dev/null +++ b/modules/YetiSimulator/ev_board_support/ev_board_supportImpl.hpp @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest +#ifndef EV_BOARD_SUPPORT_EV_BOARD_SUPPORT_IMPL_HPP +#define EV_BOARD_SUPPORT_EV_BOARD_SUPPORT_IMPL_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 3 +// + +#include + +#include "../YetiSimulator.hpp" + +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 +// insert your custom include headers here +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 + +namespace module { +namespace ev_board_support { + +struct Conf {}; + +class ev_board_supportImpl : public ev_board_supportImplBase { +public: + ev_board_supportImpl() = delete; + ev_board_supportImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer& mod, Conf& config) : + ev_board_supportImplBase(ev, "ev_board_support"), mod(mod), config(config){}; + + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + // insert your public definitions here + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + +protected: + // command handler functions (virtual) + virtual void handle_enable(bool& value) override; + virtual void handle_set_cp_state(types::ev_board_support::EvCpState& cp_state) override; + virtual void handle_allow_power_on(bool& value) override; + virtual void handle_diode_fail(bool& value) override; + virtual void handle_set_ac_max_current(double& current) override; + virtual void handle_set_three_phases(bool& three_phases) override; + virtual void handle_set_rcd_error(double& rcd_current_mA) override; + + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + // insert your protected definitions here + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + +private: + const Everest::PtrContainer& mod; + const Conf& config; + + virtual void init() override; + virtual void ready() override; + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 + // insert your private definitions here + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 +}; + +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 +// insert other definitions here +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 + +} // namespace ev_board_support +} // namespace module + +#endif // EV_BOARD_SUPPORT_EV_BOARD_SUPPORT_IMPL_HPP diff --git a/modules/simulation/JsYetiSimulator/manifest.yaml b/modules/YetiSimulator/manifest.yaml similarity index 96% rename from modules/simulation/JsYetiSimulator/manifest.yaml rename to modules/YetiSimulator/manifest.yaml index 006c41387..d22f8e4ee 100644 --- a/modules/simulation/JsYetiSimulator/manifest.yaml +++ b/modules/YetiSimulator/manifest.yaml @@ -25,3 +25,4 @@ metadata: license: https://opensource.org/licenses/Apache-2.0 authors: - Cornelius Claussen + - Tobias Marzell (Pionix GmbH) diff --git a/modules/YetiSimulator/powermeter/powermeterImpl.cpp b/modules/YetiSimulator/powermeter/powermeterImpl.cpp new file mode 100644 index 000000000..249b00de2 --- /dev/null +++ b/modules/YetiSimulator/powermeter/powermeterImpl.cpp @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "powermeterImpl.hpp" + +namespace module { +namespace powermeter { + +void powermeterImpl::init() { +} + +void powermeterImpl::ready() { +} + +types::powermeter::TransactionStartResponse +powermeterImpl::handle_start_transaction(types::powermeter::TransactionReq& value) { + return {.status = types::powermeter::TransactionRequestStatus::OK}; +} + +types::powermeter::TransactionStopResponse powermeterImpl::handle_stop_transaction(std::string& transaction_id) { + return {.status = types::powermeter::TransactionRequestStatus::NOT_SUPPORTED, + .error = "YetiDriver does not support stop transaction request."}; +} + +} // namespace powermeter +} // namespace module diff --git a/modules/YetiSimulator/powermeter/powermeterImpl.hpp b/modules/YetiSimulator/powermeter/powermeterImpl.hpp new file mode 100644 index 000000000..07f91a3d4 --- /dev/null +++ b/modules/YetiSimulator/powermeter/powermeterImpl.hpp @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest +#ifndef POWERMETER_POWERMETER_IMPL_HPP +#define POWERMETER_POWERMETER_IMPL_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 3 +// + +#include + +#include "../YetiSimulator.hpp" + +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 +// insert your custom include headers here +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 + +namespace module { +namespace powermeter { + +struct Conf {}; + +class powermeterImpl : public powermeterImplBase { +public: + powermeterImpl() = delete; + powermeterImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer& mod, Conf& config) : + powermeterImplBase(ev, "powermeter"), mod(mod), config(config){}; + + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + // insert your public definitions here + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + +protected: + // command handler functions (virtual) + virtual types::powermeter::TransactionStartResponse + handle_start_transaction(types::powermeter::TransactionReq& value) override; + virtual types::powermeter::TransactionStopResponse handle_stop_transaction(std::string& transaction_id) override; + + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + // insert your protected definitions here + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + +private: + const Everest::PtrContainer& mod; + const Conf& config; + + virtual void init() override; + virtual void ready() override; + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 + // insert your private definitions here + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 +}; + +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 +// insert other definitions here +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 + +} // namespace powermeter +} // namespace module + +#endif // POWERMETER_POWERMETER_IMPL_HPP diff --git a/modules/YetiSimulator/rcd/ac_rcdImpl.cpp b/modules/YetiSimulator/rcd/ac_rcdImpl.cpp new file mode 100644 index 000000000..a1c868f27 --- /dev/null +++ b/modules/YetiSimulator/rcd/ac_rcdImpl.cpp @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "ac_rcdImpl.hpp" + +namespace module { +namespace rcd { + +void ac_rcdImpl::init() { +} + +void ac_rcdImpl::ready() { +} + +void ac_rcdImpl::handle_self_test() { + // your code for cmd self_test goes here +} + +bool ac_rcdImpl::handle_reset() { + // your code for cmd reset goes here + return true; +} + +} // namespace rcd +} // namespace module diff --git a/modules/YetiSimulator/rcd/ac_rcdImpl.hpp b/modules/YetiSimulator/rcd/ac_rcdImpl.hpp new file mode 100644 index 000000000..b9d5d674a --- /dev/null +++ b/modules/YetiSimulator/rcd/ac_rcdImpl.hpp @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest +#ifndef RCD_AC_RCD_IMPL_HPP +#define RCD_AC_RCD_IMPL_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 3 +// + +#include + +#include "../YetiSimulator.hpp" + +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 +// insert your custom include headers here +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 + +namespace module { +namespace rcd { + +struct Conf {}; + +class ac_rcdImpl : public ac_rcdImplBase { +public: + ac_rcdImpl() = delete; + ac_rcdImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer& mod, Conf& config) : + ac_rcdImplBase(ev, "rcd"), mod(mod), config(config){}; + + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + // insert your public definitions here + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + +protected: + // command handler functions (virtual) + virtual void handle_self_test() override; + virtual bool handle_reset() override; + + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + // insert your protected definitions here + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + +private: + const Everest::PtrContainer& mod; + const Conf& config; + + virtual void init() override; + virtual void ready() override; + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 + // insert your private definitions here + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 +}; + +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 +// insert other definitions here +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 + +} // namespace rcd +} // namespace module + +#endif // RCD_AC_RCD_IMPL_HPP diff --git a/modules/YetiSimulator/util/error_handler.cpp b/modules/YetiSimulator/util/error_handler.cpp new file mode 100644 index 000000000..d63be421c --- /dev/null +++ b/modules/YetiSimulator/util/error_handler.cpp @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "error_handler.hpp" + +namespace module { + +using Everest::error::Severity; + +ErrorHandler::ErrorHandler(evse_board_supportImplBase* p_board_support, ac_rcdImplBase* p_rcd, + connector_lockImplBase* p_connector_lock) : + p_board_support(p_board_support), p_rcd(p_rcd), p_connector_lock(p_connector_lock) { +} + +void ErrorHandler::error_DiodeFault(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/DiodeFault", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/DiodeFault"); + } +} + +void ErrorHandler::error_BrownOut(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/BrownOut", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/BrownOut"); + } +} + +void ErrorHandler::error_EnergyManagement(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/EnergyManagement", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/EnergyManagement"); + } +} + +void ErrorHandler::error_PermanentFault(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/PermanentFault", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/PermanentFault"); + } +} + +void ErrorHandler::error_MREC2GroundFailure(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC2GroundFailure", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC2GroundFailure"); + } +} + +void ErrorHandler::error_MREC3HighTemperature(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC3HighTemperature", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC3HighTemperature"); + } +} + +void ErrorHandler::error_MREC4OverCurrentFailure(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC4OverCurrentFailure", + "", "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC4OverCurrentFailure"); + } +} + +void ErrorHandler::error_MREC5OverVoltage(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC5OverVoltage", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC5OverVoltage"); + } +} + +void ErrorHandler::error_MREC6UnderVoltage(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC6UnderVoltage", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC6UnderVoltage"); + } +} + +void ErrorHandler::error_MREC8EmergencyStop(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC8EmergencyStop", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC8EmergencyStop"); + } +} + +void ErrorHandler::error_MREC10InvalidVehicleMode(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC10InvalidVehicleMode", + "", "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC10InvalidVehicleMode"); + } +} + +void ErrorHandler::error_MREC14PilotFault(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC14PilotFault", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC14PilotFault"); + } +} + +void ErrorHandler::error_MREC15PowerLoss(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC15PowerLoss", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC15PowerLoss"); + } +} + +void ErrorHandler::error_MREC17EVSEContactorFault(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC17EVSEContactorFault", + "", "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC17EVSEContactorFault"); + } +} + +void ErrorHandler::error_MREC18CableOverTempDerate(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC18CableOverTempDerate", + "", "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC18CableOverTempDerate"); + } +} + +void ErrorHandler::error_MREC19CableOverTempStop(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC19CableOverTempStop", + "", "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC19CableOverTempStop"); + } +} + +void ErrorHandler::error_MREC20PartialInsertion(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC20PartialInsertion", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC20PartialInsertion"); + } +} + +void ErrorHandler::error_MREC23ProximityFault(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC23ProximityFault", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC23ProximityFault"); + } +} + +void ErrorHandler::error_MREC24ConnectorVoltageHigh(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC24ConnectorVoltageHigh", + "", "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC24ConnectorVoltageHigh"); + } +} + +void ErrorHandler::error_MREC25BrokenLatch(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC25BrokenLatch", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC25BrokenLatch"); + } +} + +void ErrorHandler::error_MREC26CutCable(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("evse_board_support/MREC26CutCable", "", + "Simulated fault event", Severity::High); + p_board_support->raise_error(error); + } else { + p_board_support->clear_error("evse_board_support/MREC26CutCable"); + } +} + +void ErrorHandler::error_ac_rcd_MREC2GroundFailure(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("ac_rcd/MREC2GroundFailure", "", + "Simulated fault event", Severity::High); + p_rcd->raise_error(error); + } else { + p_rcd->clear_error("ac_rcd/MREC2GroundFailure"); + } +} + +void ErrorHandler::error_ac_rcd_VendorError(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("ac_rcd/VendorError", "", + "Simulated fault event", Severity::High); + p_rcd->raise_error(error); + } else { + p_rcd->clear_error("ac_rcd/VendorError"); + } +} + +void ErrorHandler::error_ac_rcd_Selftest(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("ac_rcd/Selftest", "", "Simulated fault event", + Severity::High); + p_rcd->raise_error(error); + } else { + p_rcd->clear_error("ac_rcd/Selftest"); + } +} + +void ErrorHandler::error_ac_rcd_AC(bool raise) { + if (raise) { + const auto error = + p_board_support->error_factory->create_error("ac_rcd/AC", "", "Simulated fault event", Severity::High); + p_rcd->raise_error(error); + } else { + p_rcd->clear_error("ac_rcd/AC"); + } +} + +void ErrorHandler::error_ac_rcd_DC(bool raise) { + if (raise) { + const auto error = + p_board_support->error_factory->create_error("ac_rcd/DC", "", "Simulated fault event", Severity::High); + p_rcd->raise_error(error); + } else { + p_rcd->clear_error("ac_rcd/DC"); + } +} + +void ErrorHandler::error_lock_ConnectorLockCapNotCharged(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("connector_lock/ConnectorLockCapNotCharged", "", + "Simulated fault event", Severity::High); + p_connector_lock->raise_error(error); + } else { + p_connector_lock->clear_error("connector_lock/ConnectorLockCapNotCharged"); + } +} + +void ErrorHandler::error_lock_ConnectorLockUnexpectedOpen(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("connector_lock/ConnectorLockUnexpectedOpen", + "", "Simulated fault event", Severity::High); + p_connector_lock->raise_error(error); + } else { + p_connector_lock->clear_error("connector_lock/ConnectorLockUnexpectedOpen"); + } +} + +void ErrorHandler::error_lock_ConnectorLockUnexpectedClose(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("connector_lock/ConnectorLockUnexpectedClose", + "", "Simulated fault event", Severity::High); + p_connector_lock->raise_error(error); + } else { + p_connector_lock->clear_error("connector_lock/ConnectorLockUnexpectedClose"); + } +} + +void ErrorHandler::error_lock_ConnectorLockFailedLock(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("connector_lock/ConnectorLockFailedLock", "", + "Simulated fault event", Severity::High); + p_connector_lock->raise_error(error); + } else { + p_connector_lock->clear_error("connector_lock/ConnectorLockFailedLock"); + } +} + +void ErrorHandler::error_lock_ConnectorLockFailedUnlock(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("connector_lock/ConnectorLockFailedUnlock", "", + "Simulated fault event", Severity::High); + p_connector_lock->raise_error(error); + } else { + p_connector_lock->clear_error("connector_lock/ConnectorLockFailedUnlock"); + } +} + +void ErrorHandler::error_lock_MREC1ConnectorLockFailure(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("connector_lock/MREC1ConnectorLockFailure", "", + "Simulated fault event", Severity::High); + p_connector_lock->raise_error(error); + } else { + p_connector_lock->clear_error("connector_lock/MREC1ConnectorLockFailure"); + } +} + +void ErrorHandler::error_lock_VendorError(bool raise) { + if (raise) { + const auto error = p_board_support->error_factory->create_error("connector_lock/VendorError", "", + "Simulated fault event", Severity::High); + p_connector_lock->raise_error(error); + } else { + p_connector_lock->clear_error("connector_lock/VendorError"); + } +} + +} // namespace module \ No newline at end of file diff --git a/modules/YetiSimulator/util/error_handler.hpp b/modules/YetiSimulator/util/error_handler.hpp new file mode 100644 index 000000000..a4a8de47e --- /dev/null +++ b/modules/YetiSimulator/util/error_handler.hpp @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#pragma once + +#include "generated/interfaces/ac_rcd/Implementation.hpp" +#include "generated/interfaces/connector_lock/Implementation.hpp" +#include "generated/interfaces/evse_board_support/Implementation.hpp" + +namespace module { + +class ErrorHandler { +public: + ErrorHandler(evse_board_supportImplBase* p_board_support, ac_rcdImplBase* p_rcd, + connector_lockImplBase* p_connector_lock); + + void error_DiodeFault(bool raise); + void error_BrownOut(bool raise); + void error_EnergyManagement(bool raise); + void error_PermanentFault(bool raise); + void error_MREC2GroundFailure(bool raise); + void error_MREC3HighTemperature(bool raise); + void error_MREC4OverCurrentFailure(bool raise); + void error_MREC5OverVoltage(bool raise); + void error_MREC6UnderVoltage(bool raise); + void error_MREC8EmergencyStop(bool raise); + void error_MREC10InvalidVehicleMode(bool raise); + void error_MREC14PilotFault(bool raise); + void error_MREC15PowerLoss(bool raise); + void error_MREC17EVSEContactorFault(bool raise); + void error_MREC18CableOverTempDerate(bool raise); + void error_MREC19CableOverTempStop(bool raise); + void error_MREC20PartialInsertion(bool raise); + void error_MREC23ProximityFault(bool raise); + void error_MREC24ConnectorVoltageHigh(bool raise); + void error_MREC25BrokenLatch(bool raise); + void error_MREC26CutCable(bool raise); + void error_ac_rcd_MREC2GroundFailure(bool raise); + void error_ac_rcd_VendorError(bool raise); + void error_ac_rcd_Selftest(bool raise); + void error_ac_rcd_AC(bool raise); + void error_ac_rcd_DC(bool raise); + void error_lock_ConnectorLockCapNotCharged(bool raise); + void error_lock_ConnectorLockUnexpectedOpen(bool raise); + void error_lock_ConnectorLockUnexpectedClose(bool raise); + void error_lock_ConnectorLockFailedLock(bool raise); + void error_lock_ConnectorLockFailedUnlock(bool raise); + void error_lock_MREC1ConnectorLockFailure(bool raise); + void error_lock_VendorError(bool raise); + +private: + evse_board_supportImplBase* p_board_support; + ac_rcdImplBase* p_rcd; + connector_lockImplBase* p_connector_lock; +}; + +} // namespace module diff --git a/modules/YetiSimulator/util/mqtt_handler.cpp b/modules/YetiSimulator/util/mqtt_handler.cpp new file mode 100644 index 000000000..4537d7844 --- /dev/null +++ b/modules/YetiSimulator/util/mqtt_handler.cpp @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "mqtt_handler.hpp" +#include "everest/logging.hpp" +#include "nlohmann/json.hpp" + +namespace module { + +MqttHandler::MqttHandler(evse_board_supportImplBase* p_board_support, ac_rcdImplBase* p_rcd, + connector_lockImplBase* p_connector_lock) : + errorHandler{p_board_support, p_rcd, p_connector_lock} { +} + +void MqttHandler::handle_mqtt_payload(const std::string& payload) { + const auto e = nlohmann::json::parse(payload); + + const auto raise = e["raise"] == "true"; + + const auto error_type = std::string{e["error_type"]}; + + if (error_type == "DiodeFault") { + errorHandler.error_DiodeFault(raise); + } else if (error_type == "BrownOut") { + errorHandler.error_BrownOut(raise); + } else if (error_type == "EnergyManagement") { + errorHandler.error_EnergyManagement(raise); + } else if (error_type == "PermanentFault") { + errorHandler.error_PermanentFault(raise); + } else if (error_type == "MREC2GroundFailure") { + errorHandler.error_MREC2GroundFailure(raise); + } else if (error_type == "MREC3HighTemperature") { + errorHandler.error_MREC3HighTemperature(raise); + } else if (error_type == "MREC4OverCurrentFailure") { + errorHandler.error_MREC4OverCurrentFailure(raise); + } else if (error_type == "MREC5OverVoltage") { + errorHandler.error_MREC5OverVoltage(raise); + } else if (error_type == "MREC6UnderVoltage") { + errorHandler.error_MREC6UnderVoltage(raise); + } else if (error_type == "MREC8EmergencyStop") { + errorHandler.error_MREC8EmergencyStop(raise); + } else if (error_type == "MREC10InvalidVehicleMode") { + errorHandler.error_MREC10InvalidVehicleMode(raise); + } else if (error_type == "MREC14PilotFault") { + errorHandler.error_MREC14PilotFault(raise); + } else if (error_type == "MREC15PowerLoss") { + errorHandler.error_MREC15PowerLoss(raise); + } else if (error_type == "MREC17EVSEContactorFault") { + errorHandler.error_MREC17EVSEContactorFault(raise); + } else if (error_type == "MREC18CableOverTempDerate") { + errorHandler.error_MREC18CableOverTempDerate(raise); + } else if (error_type == "MREC19CableOverTempStop") { + errorHandler.error_MREC19CableOverTempStop(raise); + } else if (error_type == "MREC20PartialInsertion") { + errorHandler.error_MREC20PartialInsertion(raise); + } else if (error_type == "MREC23ProximityFault") { + errorHandler.error_MREC23ProximityFault(raise); + } else if (error_type == "MREC24ConnectorVoltageHigh") { + errorHandler.error_MREC24ConnectorVoltageHigh(raise); + } else if (error_type == "MREC25BrokenLatch") { + errorHandler.error_MREC25BrokenLatch(raise); + } else if (error_type == "MREC26CutCable") { + errorHandler.error_MREC26CutCable(raise); + } else if (error_type == "ac_rcd_MREC2GroundFailure") { + errorHandler.error_ac_rcd_MREC2GroundFailure(raise); + } else if (error_type == "ac_rcd_VendorError") { + errorHandler.error_ac_rcd_VendorError(raise); + } else if (error_type == "ac_rcd_Selftest") { + errorHandler.error_ac_rcd_Selftest(raise); + } else if (error_type == "ac_rcd_AC") { + errorHandler.error_ac_rcd_AC(raise); + } else if (error_type == "ac_rcd_DC") { + errorHandler.error_ac_rcd_DC(raise); + } else if (error_type == "lock_ConnectorLockCapNotCharged") { + errorHandler.error_lock_ConnectorLockCapNotCharged(raise); + } else if (error_type == "lock_ConnectorLockUnexpectedOpen") { + errorHandler.error_lock_ConnectorLockUnexpectedOpen(raise); + } else if (error_type == "lock_ConnectorLockUnexpectedClose") { + errorHandler.error_lock_ConnectorLockUnexpectedClose(raise); + } else if (error_type == "lock_ConnectorLockFailedLock") { + errorHandler.error_lock_ConnectorLockFailedLock(raise); + } else if (error_type == "lock_ConnectorLockFailedUnlock") { + errorHandler.error_lock_ConnectorLockFailedUnlock(raise); + } else if (error_type == "lock_MREC1ConnectorLockFailure") { + errorHandler.error_lock_MREC1ConnectorLockFailure(raise); + } else if (error_type == "lock_VendorError") { + errorHandler.error_lock_VendorError(raise); + } else { + EVLOG_error << "Unknown error raised via MQTT"; + } +} + +} // namespace module \ No newline at end of file diff --git a/modules/YetiSimulator/util/mqtt_handler.hpp b/modules/YetiSimulator/util/mqtt_handler.hpp new file mode 100644 index 000000000..2d9b5d107 --- /dev/null +++ b/modules/YetiSimulator/util/mqtt_handler.hpp @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#pragma once + +#include "error_handler.hpp" +#include "generated/interfaces/ac_rcd/Implementation.hpp" +#include "generated/interfaces/connector_lock/Implementation.hpp" +#include "generated/interfaces/evse_board_support/Implementation.hpp" +#include +namespace module { + +class MqttHandler { +public: + MqttHandler(evse_board_supportImplBase* p_board_support, ac_rcdImplBase* p_rcd, + connector_lockImplBase* p_connector_lock); + ; + + void handle_mqtt_payload(const std::string& payload); + +private: + ErrorHandler errorHandler; +}; + +} // namespace module diff --git a/modules/YetiSimulator/util/simulator.hpp b/modules/YetiSimulator/util/simulator.hpp new file mode 100644 index 000000000..7dc6a285f --- /dev/null +++ b/modules/YetiSimulator/util/simulator.hpp @@ -0,0 +1,612 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#pragma once + +#include "error_handler.hpp" +#include "state.hpp" +#include "util.hpp" +#include + +namespace module { + +template class Simulator { +public: + Simulator(int new_sleep_time_ms, YetiSimulatorT* new_yeti_simulator) { + start(new_sleep_time_ms, new_yeti_simulator); + } + +private: + void start(int sleep_time_ms, YetiSimulatorT* yeti_simulator) { + std::thread(&Simulator::run_simulation, this, sleep_time_ms, yeti_simulator).detach(); + } + + [[noreturn]] void run_simulation(int sleep_time_ms, YetiSimulatorT* yeti_simulator) { + auto& module_state = yeti_simulator->get_module_state(); + while (true) { + if (module_state.simulation_enabled) { + simulation_step(yeti_simulator); + } + + module_state.pubCnt++; + switch (module_state.pubCnt) { + case 1: + publish_powermeter(yeti_simulator); + publish_telemetry(yeti_simulator); + break; + case 3: + publish_keepalive(yeti_simulator); + break; + default: + module_state.pubCnt = 0; + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds{sleep_time_ms}); + } + } + + void simulation_step(YetiSimulatorT* yeti_simulator) { + check_error_rcd(yeti_simulator); + read_from_car(yeti_simulator); + simulation_statemachine(yeti_simulator); + add_noise(yeti_simulator); + simulate_powermeter(yeti_simulator); + publish_ev_board_support(yeti_simulator); + } + + void check_error_rcd(YetiSimulatorT* yeti_simulator) { + using Everest::error::Severity; + auto& module_state = yeti_simulator->get_module_state(); + auto& board_support = yeti_simulator->get_board_support(); + auto& rcd = yeti_simulator->get_ac_rcd(); + + if (module_state.simulation_data.rcd_current > 5.0) { + if (!module_state.rcd_error_reported) { + const auto error = + board_support.error_factory->create_error("ac_rcd/DC", "", "Simulated fault event", Severity::High); + board_support.raise_error(error); + module_state.rcd_error_reported = true; + } + } else { + if (module_state.rcd_error_reported) { + board_support.clear_error("ac_rcd/DC"); + } + module_state.rcd_error_reported = false; + } + rcd.publish_rcd_current_mA(module_state.simulation_data.rcd_current); + } + + void read_from_car(YetiSimulatorT* yeti_simulator) { + auto error_handler = ErrorHandler{&yeti_simulator->get_board_support(), &yeti_simulator->get_ac_rcd(), + &yeti_simulator->get_connector_lock()}; + + auto& module_state = yeti_simulator->get_module_state(); + + auto amps1 = double{}; + auto amps2 = double{}; + auto amps3 = double{}; + + auto hlc_active = false; + if (module_state.pwm_duty_cycle >= 0.03 && module_state.pwm_duty_cycle <= 0.07) + hlc_active = true; + + auto amps = duty_cycle_to_amps(module_state.pwm_duty_cycle); + if (amps > module_state.ev_max_current || hlc_active == true) + amps = module_state.ev_max_current; + + if (module_state.relais_on == true && module_state.ev_three_phases > 0) + amps1 = amps; + else + amps1 = 0; + if (module_state.relais_on == true && module_state.ev_three_phases > 1 && + module_state.use_three_phases_confirmed) + amps2 = amps; + else + amps2 = 0; + if (module_state.relais_on == true && module_state.ev_three_phases > 2 && + module_state.use_three_phases_confirmed) + amps3 = amps; + else + amps3 = 0; + + if (module_state.pwm_running) { + module_state.pwm_voltage_hi = module_state.simulation_data.cp_voltage; + module_state.pwm_voltage_lo = -12.0; + } else { + module_state.pwm_voltage_hi = module_state.simulation_data.cp_voltage; + module_state.pwm_voltage_lo = module_state.pwm_voltage_hi; + } + + if (module_state.pwm_error_f) { + module_state.pwm_voltage_hi = -12.0; + module_state.pwm_voltage_lo = -12.0; + } + if (module_state.simulation_data.error_e) { + module_state.pwm_voltage_hi = 0.0; + module_state.pwm_voltage_lo = 0.0; + } + if (module_state.simulation_data.diode_fail) { + module_state.pwm_voltage_lo = -module_state.pwm_voltage_hi; + } + + const auto cpLo = module_state.pwm_voltage_lo; + const auto cpHi = module_state.pwm_voltage_hi; + + // sth is wrong with negative signal + if (module_state.pwm_running && !is_voltage_in_range(cpLo, -12.0)) { + // CP-PE short or signal somehow gone + if (is_voltage_in_range(cpLo, 0.0) && is_voltage_in_range(cpHi, 0.0)) { + module_state.current_state = state::State::STATE_E; + drawPower(module_state, 0, 0, 0, 0); + } else if (is_voltage_in_range(cpHi + cpLo, 0.0)) { // Diode fault + error_handler.error_DiodeFault(true); + drawPower(module_state, 0, 0, 0, 0); + } + } else if (is_voltage_in_range(cpHi, 12.0)) { + // +12V State A IDLE (open circuit) + // clear all errors that clear on disconnection + clear_disconnect_errors(error_handler, yeti_simulator->get_board_support()); + module_state.current_state = state::State::STATE_A; + drawPower(module_state, 0, 0, 0, 0); + } else if (is_voltage_in_range(cpHi, 9.0)) { + module_state.current_state = state::State::STATE_B; + drawPower(module_state, 0, 0, 0, 0); + } else if (is_voltage_in_range(cpHi, 6.0)) { + module_state.current_state = state::State::STATE_C; + drawPower(module_state, amps1, amps2, amps3, 0.2); + } else if (is_voltage_in_range(cpHi, 3.0)) { + module_state.current_state = state::State::STATE_D; + drawPower(module_state, amps1, amps2, amps3, 0.2); + } else if (is_voltage_in_range(cpHi, -12.0)) { + module_state.current_state = state::State::STATE_F; + drawPower(module_state, 0, 0, 0, 0); + } + } + + void simulation_statemachine(YetiSimulatorT* yeti_simulator) { + auto& module_state = yeti_simulator->get_module_state(); + auto& board_support = yeti_simulator->get_board_support(); + + if (module_state.last_state != module_state.current_state) { + publish_event(board_support, module_state.current_state); + } + + switch (module_state.current_state) { + case state::State::STATE_DISABLED: + powerOff(module_state, board_support); + module_state.power_on_allowed = false; + break; + + case state::State::STATE_A: { + module_state.use_three_phases_confirmed = module_state.use_three_phases; + auto* _board_support = dynamic_cast(&board_support); + _board_support->pwm_off(); + reset_powermeter(module_state); + module_state.simplified_mode = false; + + if (module_state.last_state != state::State::STATE_A && + module_state.last_state != state::State::STATE_DISABLED && + module_state.last_state != state::State::STATE_F) { + powerOff(module_state, board_support); + + // If car was unplugged, reset RCD flag. + module_state.simdata_setting.rcd_current = 0.1; + module_state.rcd_error = false; + } + break; + } + case state::State::STATE_B: + // Table A.6: Sequence 7 EV stops charging + // Table A.6: Sequence 8.2 EV supply equipment + // responds to EV opens S2 (w/o PWM) + + if (module_state.last_state != state::State::STATE_A && module_state.last_state != state::State::STATE_B) { + // Need to switch off according to Table A.6 Sequence 8.1 within + powerOff(module_state, board_support); + } + + // Table A.6: Sequence 1.1 Plug-in + if (module_state.last_state == state::State::STATE_A) { + module_state.simplified_mode = false; + } + + break; + case state::State::STATE_C: + // Table A.6: Sequence 1.2 Plug-in + if (module_state.last_state == state::State::STATE_A) { + module_state.simplified_mode = true; + } + + if (!module_state.pwm_running) { // C1 + // Table A.6 Sequence 10.2: EV does not stop drawing power even + // if PWM stops. Stop within 6 seconds (E.g. Kona1!) + // This is implemented in EvseManager + if (!module_state.power_on_allowed) + powerOff(module_state, board_support); + } else if (module_state.power_on_allowed) { // C2 + // Table A.6: Sequence 4 EV ready to charge. + // Must enable power within 3 seconds. + powerOn(module_state, board_support); + } + break; + case state::State::STATE_D: + // Table A.6: Sequence 1.2 Plug-in (w/ventilation) + if (module_state.last_state == state::State::STATE_A) { + module_state.simplified_mode = true; + } + + if (!module_state.pwm_running) { + // Table A.6 Sequence 10.2: EV does not stop drawing power + // even if PWM stops. Stop within 6 seconds (E.g. Kona1!) + /* if (mod.last_pwm_running) // FIMXE implement 6 second timer + startTimer(6000); + if (timerElapsed()) { */ + // force power off under load + powerOff(module_state, board_support); + // } + } else if (module_state.power_on_allowed && !module_state.relais_on && module_state.has_ventilation) { + // Table A.6: Sequence 4 EV ready to charge. + // Must enable power within 3 seconds. + powerOn(module_state, board_support); + } + break; + case state::State::STATE_E: { + powerOff(module_state, board_support); + auto* _board_support = dynamic_cast(&board_support); + _board_support->pwm_off(); + break; + } + case state::State::STATE_F: + powerOff(module_state, board_support); + break; + default: + break; + } + module_state.last_state = module_state.current_state; + module_state.last_pwm_running = module_state.pwm_running; + } + + void add_noise(YetiSimulatorT* yeti_simulator) { + auto& module_state = yeti_simulator->get_module_state(); + + const auto random_number_between_0_and_1 = []() { + return static_cast(rand()) / static_cast(RAND_MAX); + }; + const auto noise = (1 + (random_number_between_0_and_1() - 0.5) * 0.02); + const auto lonoise = (1 + (random_number_between_0_and_1() - 0.5) * 0.005); + const auto impedance = module_state.simdata_setting.impedance / 1000.0; + + module_state.simulation_data.currents.L1 = module_state.simdata_setting.currents.L1 * noise; + module_state.simulation_data.currents.L2 = module_state.simdata_setting.currents.L2 * noise; + module_state.simulation_data.currents.L3 = module_state.simdata_setting.currents.L3 * noise; + module_state.simulation_data.currents.N = module_state.simdata_setting.currents.N * noise; + + module_state.simulation_data.voltages.L1 = + module_state.simdata_setting.voltages.L1 * noise - impedance * module_state.simulation_data.currents.L1; + module_state.simulation_data.voltages.L2 = + module_state.simdata_setting.voltages.L2 * noise - impedance * module_state.simulation_data.currents.L2; + module_state.simulation_data.voltages.L3 = + module_state.simdata_setting.voltages.L3 * noise - impedance * module_state.simulation_data.currents.L3; + + module_state.simulation_data.frequencies.L1 = module_state.simdata_setting.frequencies.L1 * lonoise; + module_state.simulation_data.frequencies.L2 = module_state.simdata_setting.frequencies.L2 * lonoise; + module_state.simulation_data.frequencies.L3 = module_state.simdata_setting.frequencies.L3 * lonoise; + + module_state.simulation_data.cp_voltage = module_state.simdata_setting.cp_voltage * noise; + module_state.simulation_data.rcd_current = module_state.simdata_setting.rcd_current * noise; + module_state.simulation_data.pp_resistor = module_state.simdata_setting.pp_resistor * noise; + + module_state.simulation_data.diode_fail = module_state.simdata_setting.diode_fail; + module_state.simulation_data.error_e = module_state.simdata_setting.error_e; + } + + void simulate_powermeter(YetiSimulatorT* yeti_simulator) { + using namespace std::chrono; + + auto& module_state = yeti_simulator->get_module_state(); + + const auto time_stamp = time_point_cast(system_clock::now()).time_since_epoch().count(); + if (module_state.powermeter_sim_last_time_stamp == 0) + module_state.powermeter_sim_last_time_stamp = time_stamp; + const auto deltat = time_stamp - module_state.powermeter_sim_last_time_stamp; + module_state.powermeter_sim_last_time_stamp = time_stamp; + + const auto wattL1 = module_state.simulation_data.voltages.L1 * module_state.simulation_data.currents.L1 * + (module_state.relais_on ? 1 : 0); + const auto wattL2 = module_state.simulation_data.voltages.L2 * module_state.simulation_data.currents.L2 * + (module_state.relais_on && module_state.use_three_phases_confirmed ? 1 : 0); + const auto wattL3 = module_state.simulation_data.voltages.L3 * module_state.simulation_data.currents.L3 * + (module_state.relais_on && module_state.use_three_phases_confirmed ? 1 : 0); + + module_state.watt_hr.L1 += (wattL1 * deltat) / 1000.0 / 3600.0; + module_state.watt_hr.L2 += (wattL2 * deltat) / 1000.0 / 3600.0; + module_state.watt_hr.L3 += (wattL3 * deltat) / 1000.0 / 3600.0; + + module_state.powermeter_data.time_stamp = round(time_stamp / 1000); + module_state.powermeter_data.totalWattHr = + round(module_state.watt_hr.L1 + module_state.watt_hr.L2 + module_state.watt_hr.L3); + + module_state.powermeter_data.wattL1 = round(wattL1); + module_state.powermeter_data.vrmsL1 = module_state.simulation_data.voltages.L1; + module_state.powermeter_data.irmsL1 = module_state.simulation_data.currents.L1; + module_state.powermeter_data.wattHrL1 = round(module_state.watt_hr.L1); + module_state.powermeter_data.tempL1 = 25.0 + (wattL1 + wattL2 + wattL3) * 0.003; + module_state.powermeter_data.freqL1 = module_state.simulation_data.frequencies.L1; + + module_state.powermeter_data.wattL2 = round(wattL2); + module_state.powermeter_data.vrmsL2 = module_state.simulation_data.voltages.L2; + module_state.powermeter_data.irmsL2 = module_state.simulation_data.currents.L1; + module_state.powermeter_data.wattHrL2 = round(module_state.watt_hr.L2); + module_state.powermeter_data.tempL2 = 25.0 + (wattL1 + wattL2 + wattL3) * 0.003; + module_state.powermeter_data.freqL2 = module_state.simulation_data.frequencies.L2; + + module_state.powermeter_data.wattL3 = round(wattL3); + module_state.powermeter_data.vrmsL3 = module_state.simulation_data.voltages.L3; + module_state.powermeter_data.irmsL3 = module_state.simulation_data.currents.L3; + module_state.powermeter_data.wattHrL3 = round(module_state.watt_hr.L3); + module_state.powermeter_data.tempL3 = 25.0 + (wattL1 + wattL2 + wattL3) * 0.003; + module_state.powermeter_data.freqL3 = module_state.simulation_data.frequencies.L3; + + module_state.powermeter_data.irmsN = module_state.simulation_data.currents.N; + }; + + void publish_ev_board_support(YetiSimulatorT* yeti_simulator) { + auto& module_state = yeti_simulator->get_module_state(); + auto& ev_board_support = yeti_simulator->get_ev_board_support(); + + const auto pp = read_pp_ampacity(module_state); + + ev_board_support.publish_bsp_measurement( + {.proximity_pilot = pp, + .cp_pwm_duty_cycle = static_cast(module_state.pwm_duty_cycle * 100), + .rcd_current_mA = module_state.simulation_data.rcd_current}); + } + + void publish_powermeter(YetiSimulatorT* yeti_simulator) { + auto& module_state = yeti_simulator->get_module_state(); + auto& powermeter = yeti_simulator->get_powermeter(); + auto& mqtt = yeti_simulator->get_mqtt(); + auto& info = yeti_simulator->get_info(); + + powermeter.publish_powermeter(power_meter_external(module_state.powermeter_data)); + + // Deprecated external stuff + mqtt.publish("/external/powermeter/vrmsL1", module_state.powermeter_data.vrmsL1); + mqtt.publish("/external/powermeter/phaseSeqError", false); + mqtt.publish("/external/powermeter/time_stamp", std::to_string(module_state.powermeter_data.time_stamp)); + mqtt.publish("/external/powermeter/tempL1", module_state.powermeter_data.tempL1); + mqtt.publish("/external/powermeter/totalKw", + (module_state.powermeter_data.wattL1 + module_state.powermeter_data.wattL2 + + module_state.powermeter_data.wattL3) / + 1000.0); + mqtt.publish("/external/powermeter/totalKWattHr", + (module_state.powermeter_data.wattHrL1 + module_state.powermeter_data.wattHrL2 + + module_state.powermeter_data.wattHrL3) / + 1000.0); + mqtt.publish("/external/powermeter_json", json{module_state.powermeter_data}.dump()); + + mqtt.publish("/external/" + info.id + "/powermeter/tempL1", module_state.powermeter_data.tempL1); + mqtt.publish("/external/" + info.id + "/powermeter/totalKw", + (module_state.powermeter_data.wattL1 + module_state.powermeter_data.wattL2 + + module_state.powermeter_data.wattL3) / + 1000.0); + mqtt.publish("/external/" + info.id + "/powermeter/totalKWattHr", + (module_state.powermeter_data.wattHrL1 + module_state.powermeter_data.wattHrL2 + + module_state.powermeter_data.wattHrL3) / + 1000.0); + } + + void publish_telemetry(YetiSimulatorT* yeti_simulator) { + auto& board_support = yeti_simulator->get_board_support(); + const auto& module_state = yeti_simulator->get_module_state(); + + board_support.publish_telemetry({.evse_temperature_C = static_cast(module_state.powermeter_data.tempL1), + .fan_rpm = 1500., + .supply_voltage_12V = 12.01, + .supply_voltage_minus_12V = -11.8, + .relais_on = module_state.relais_on}); + } + + void publish_keepalive(YetiSimulatorT* yeti_simulator) { + using namespace std::chrono; + auto& mqtt = yeti_simulator->get_mqtt(); + + mqtt.publish( + "/external/keepalive_json", + json{ + {"hw_revision", 0}, + {"hw_type", 0}, + {"protocol_version_major", 0}, + {"protocol_version_minor", 1}, + {"sw_version_string", "simulation"}, + {"time_stamp", {time_point_cast(system_clock::now()).time_since_epoch().count() / 1000}}, + } + .dump()); + } + + types::powermeter::Powermeter power_meter_external(state::PowermeterData& powermeter_data) { + const auto current_iso_time_string = util::get_current_iso_time_string(); + const auto& p = powermeter_data; + return { + .timestamp = current_iso_time_string, + .energy_Wh_import = + { + .total = static_cast(p.totalWattHr), + .L1 = static_cast(p.wattHrL1), + .L2 = static_cast(p.wattHrL2), + .L3 = static_cast(p.wattHrL3), + }, + .meter_id = "YETI_POWERMETER", + .phase_seq_error = false, + .power_W = + types::units::Power{ + .total = static_cast(p.wattL1 + p.wattL2 + p.wattL3), + .L1 = static_cast(p.wattL1), + .L2 = static_cast(p.wattL2), + .L3 = static_cast(p.wattL3), + }, + .voltage_V = + types::units::Voltage{ + .L1 = p.vrmsL1, + .L2 = p.vrmsL2, + .L3 = p.vrmsL3, + }, + .current_A = + types::units::Current{ + .L1 = p.irmsL1, + .L2 = p.irmsL2, + .L3 = p.irmsL3, + .N = p.irmsN, + }, + .frequency_Hz = + types::units::Frequency{ + .L1 = static_cast(p.freqL1), + .L2 = static_cast(p.freqL2), + .L3 = static_cast(p.freqL3), + }, + + }; + } + + double duty_cycle_to_amps(double dc) { + if (dc < 8.0 / 100.0) + return 0; + if (dc < 85.0 / 100.0) + return dc * 100.0 * 0.6; + if (dc < 96.0 / 100.0) + return (dc * 100.0 - 64) * 2.5; + if (dc < 97.0 / 100.0) + return 80; + return 0; + } + + bool is_voltage_in_range(const double voltage, double center) { + const auto interval = 1.1; + return ((voltage > center - interval) && (voltage < center + interval)); + } + + void drawPower(state::ModuleState& module_state, int l1, int l2, int l3, int n) { + module_state.simdata_setting.currents.L1 = l1; + module_state.simdata_setting.currents.L2 = l2; + module_state.simdata_setting.currents.L3 = l3; + module_state.simdata_setting.currents.N = n; + } + + void clear_disconnect_errors(ErrorHandler& error_handler, const evse_board_supportImplBase& board_support) { + if (board_support.error_state_monitor->is_error_active("evse_board_support/DiodeFault", "")) { + error_handler.error_DiodeFault(false); + } + } + + void powerOn(state::ModuleState& module_state, evse_board_supportImplBase& board_support) { + if (!module_state.relais_on) { + publish_event(board_support, state::State::Event_PowerOn); + module_state.relais_on = true; + module_state.telemetry_data.power_switch.switching_count += 1; + } + } + + void powerOff(state::ModuleState& module_state, evse_board_supportImplBase& board_support) { + if (module_state.relais_on) { + publish_event(board_support, state::State::Event_PowerOff); + module_state.telemetry_data.power_switch.switching_count++; + module_state.relais_on = false; + } + } + + // NOLINTNEXTLINE + void publish_event(evse_board_supportImplBase& board_support, state::State event) { + board_support.publish_event(event_to_enum(event)); + } + + // NOLINTNEXTLINE + static types::board_support_common::BspEvent event_to_enum(state::State event) { + using state::State; + using types::board_support_common::Event; + + switch (event) { + case State::STATE_A: + return {.event = Event::A}; + case State::STATE_B: + return {.event = Event::B}; + case State::STATE_C: + return {.event = Event::C}; + case State::STATE_D: + return {.event = Event::D}; + case State::STATE_E: + return {.event = Event::E}; + case State::STATE_F: + return {.event = Event::F}; + case State::STATE_DISABLED: + return {.event = Event::F}; + case State::Event_PowerOn: + return {.event = Event::PowerOn}; + case State::Event_PowerOff: + return {.event = Event::PowerOff}; + default: + EVLOG_error << "Invalid event : " << event_to_string(event); + return {.event = Event::Disconnected}; // TODO: correct default value + } + } + + // NOLINTNEXTLINE + static std::string event_to_string(state::State state) { + using state::State; + + switch (state) { + case State::STATE_A: + return "A"; + case State::STATE_B: + return "B"; + case State::STATE_C: + return "C"; + case State::STATE_D: + return "D"; + case State::STATE_E: + return "E"; + case State::STATE_F: + return "F"; + case State::STATE_DISABLED: + return "F"; + case State::Event_PowerOn: + return "PowerOn"; + case State::Event_PowerOff: + return "PowerOff"; + default: + return "invalid"; + } + } + + void reset_powermeter(state::ModuleState& module_state) { + module_state.watt_hr.L1 = 0; + module_state.watt_hr.L2 = 0; + module_state.watt_hr.L3 = 0; + module_state.powermeter_sim_last_time_stamp = 0; + } + + types::board_support_common::ProximityPilot read_pp_ampacity(state::ModuleState& module_state) { + const auto pp_resistor = module_state.simdata_setting.pp_resistor; + + if (pp_resistor < 80.0 || pp_resistor > 2460) { + EVLOG_error << "PP resistor value " << pp_resistor << " Ohm seems to be outside the allowed range."; + return {.ampacity = types::board_support_common::Ampacity::None}; + } + + // PP resistor value in spec, use a conservative interpretation of the resistance ranges + if (pp_resistor > 936.0 && pp_resistor <= 2460.0) { + return {.ampacity = types::board_support_common::Ampacity::A_13}; + } + if (pp_resistor > 308.0 && pp_resistor <= 936.0) { + return {.ampacity = types::board_support_common::Ampacity::A_20}; + } + if (pp_resistor > 140.0 && pp_resistor <= 308.0) { + return {.ampacity = types::board_support_common::Ampacity::A_32}; + } + if (pp_resistor > 80.0 && pp_resistor <= 140.0) { + return {.ampacity = types::board_support_common::Ampacity::A_63_3ph_70_1ph}; + } + return {.ampacity = types::board_support_common::Ampacity::None}; + }; +}; +} // namespace module diff --git a/modules/YetiSimulator/util/state.cpp b/modules/YetiSimulator/util/state.cpp new file mode 100644 index 000000000..c79c3fb3c --- /dev/null +++ b/modules/YetiSimulator/util/state.cpp @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "state.hpp" +#include +#include +#include + +namespace module::state { +using std::chrono::milliseconds; +using std::chrono::system_clock; +using std::chrono::time_point_cast; + +PowermeterData::PowermeterData() : + time_stamp{time_point_cast(system_clock::now()).time_since_epoch().count() / 1000} { +} +ModuleState::ModuleState() : + time_stamp{time_point_cast(system_clock::now()).time_since_epoch().count() / 1000} { +} + +std::string state_to_string(state::ModuleState& module_state) { + using state::State; + + const auto pwm = (module_state.pwm_running ? '2' : '1'); + + switch (module_state.current_state) { + case State::STATE_DISABLED: + return "Disabled"; + case State::STATE_A: + return "A" + std::to_string(pwm); + case State::STATE_B: + return "B" + std::to_string(pwm); + case State::STATE_C: + return "C" + std::to_string(pwm); + case State::STATE_D: + return "D" + std::to_string(pwm); + case State::STATE_E: + return "E" + std::to_string(pwm); + case State::STATE_F: + return "F" + std::to_string(pwm); + default: + return ""; + } +} + +void to_json(nlohmann::json& json, const PowermeterData& powermeter_data) { + json = nlohmann::json{{"time_stamp", powermeter_data.time_stamp}, + {"totalWattHr", powermeter_data.totalWattHr}, + {"wattL1", powermeter_data.wattL1}, + {"vrmsL1", powermeter_data.vrmsL1}, + {"irmsL1", powermeter_data.irmsL1}, + {"wattHrL1", powermeter_data.wattHrL1}, + {"tempL1", powermeter_data.tempL1}, + {"freqL1", powermeter_data.freqL1}, + + {"wattL2", powermeter_data.wattL2}, + {"vrmsL2", powermeter_data.vrmsL2}, + {"irmsL2", powermeter_data.irmsL2}, + {"wattHrL2", powermeter_data.wattHrL2}, + {"tempL2", powermeter_data.tempL2}, + {"freqL2", powermeter_data.freqL2}, + + {"wattL3", powermeter_data.wattL3}, + {"vrmsL3", powermeter_data.vrmsL3}, + {"irmsL3", powermeter_data.irmsL3}, + {"wattHrL3", powermeter_data.wattHrL3}, + {"tempL3", powermeter_data.tempL3}, + {"freqL3", powermeter_data.freqL3}, + + {"irmsN", powermeter_data.irmsN}}; +} + +} // namespace module::state \ No newline at end of file diff --git a/modules/YetiSimulator/util/state.hpp b/modules/YetiSimulator/util/state.hpp new file mode 100644 index 000000000..9f81ae63b --- /dev/null +++ b/modules/YetiSimulator/util/state.hpp @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#ifndef EVEREST_CORE_MODULES_YETISIMULATOR_UTIL_STATE_HPP +#define EVEREST_CORE_MODULES_YETISIMULATOR_UTIL_STATE_HPP + +// NOLINTBEGIN: ignore things like public access or magic values +#include "nlohmann/json.hpp" +#include + +namespace module::state { + +struct PowermeterData { + PowermeterData(); + int64_t time_stamp; + + double totalWattHr = 0.0; + + double wattL1 = 0.0; + double vrmsL1 = 230.0; + double irmsL1 = 0.0; + double wattHrL1 = 0.0; + double tempL1 = 25.0; + double freqL1 = 50.0; + + double wattL2 = 0.0; + double vrmsL2 = 230.0; + double irmsL2 = 0.0; + double wattHrL2 = 0.0; + double tempL2 = 25.0; + double freqL2 = 50.0; + + double wattL3 = 0.0; + double vrmsL3 = 230.0; + double irmsL3 = 0.0; + double wattHrL3 = 0.0; + double tempL3 = 25.0; + double freqL3 = 50.0; + + double irmsN = 0.0; +}; + +struct SimulationData { + double cp_voltage = 12.0; + bool diode_fail = false; + bool error_e = false; + double pp_resistor = 220.1; + double rcd_current = 0.1; + + struct Currents { + double L1 = 0.0; + double L2 = 0.0; + double L3 = 0.0; + double N = 0.0; + }; + + Currents currents; + + struct Voltages { + double L1 = 230.0; + double L2 = 230.0; + double L3 = 230.0; + }; + + Voltages voltages; + + struct Frequencies { + double L1 = 50.0; + double L2 = 50.0; + double L3 = 50.0; + }; + + Frequencies frequencies; +}; + +struct SimdataSetting { + double cp_voltage = 12.0; + double pp_resistor = 220.1; + double impedance = 500.0; + double rcd_current = 0.1; + bool diode_fail = false; + bool error_e = false; + + struct Voltages { + double L1 = 230.0; + double L2 = 230.0; + double L3 = 230.0; + }; + + Voltages voltages; + + struct Currents { + double L1 = 0.0; + double L2 = 0.0; + double L3 = 0.0; + double N = 0.0; + }; + + Currents currents; + + struct Frequencies { + double L1 = 50.0; + double L2 = 50.0; + double L3 = 50.0; + }; + + Frequencies frequencies; +}; + +struct WattHr { + double L1 = 0.0; + double L2 = 0.0; + double L3 = 0.0; +}; + +struct TelemetryData { + + struct PowerPathControllerVersion { + std::string timestamp; + std::string type = "power_path_controller_version"; + int hardware_version = 3; + std::string software_version = "1.01"; + std::string date_manufactured = "20220304"; + int64_t operating_time_h = 2330; + int64_t operating_time_h_warning = 5000; + int64_t operating_time_h_error = 6000; + bool error = false; + }; + + PowerPathControllerVersion power_path_controller_version; + + struct PowerPathController { + std::string timestamp; + std::string type = "power_path_controller"; + double cp_voltage_high = 0.0; + double cp_voltage_low = 0.0; + double cp_pwm_duty_cycle = 0.0; + std::string cp_state = "A1"; + double pp_ohm = 220.1; + double supply_voltage_12V = 12.1; + double supply_voltage_minus_12V = -11.9; + double temperature_controller = 33; + double temperature_car_connector = 65; + int64_t watchdog_reset_count = 1; + bool error = false; + }; + + PowerPathController power_path_controller; + + struct PowerSwitch { + std::string timestamp; + std::string type = "power_switch"; + int64_t switching_count = 0; + int64_t switching_count_warning = 30000; + int64_t switching_count_error = 50000; + bool is_on = false; + int64_t time_to_switch_on_ms = 110; + int64_t time_to_switch_off_ms = 100; + double temperature_C = 20; + bool error = false; + bool error_over_current = false; + }; + + PowerSwitch power_switch; + + struct Rcd { + std::string timestamp; + std::string type = "rcd"; + bool enabled = true; + double current_mA = 2.5; + bool triggered = false; + bool error = false; + }; + + Rcd rcd; +}; + +enum class State { + STATE_DISABLED = 0, + STATE_A = 1, + STATE_B = 2, + STATE_C = 3, + STATE_D = 4, + STATE_E = 5, + STATE_F = 6, + Event_PowerOn = 8, + Event_PowerOff = 9, +}; + +struct ModuleState { + ModuleState(); + + PowermeterData powermeter_data; + SimulationData simulation_data; + SimdataSetting simdata_setting; + TelemetryData telemetry_data; + + int64_t pubCnt = 0; + + bool power_on_allowed = false; + + bool relais_on = false; + State current_state = State::STATE_DISABLED; + State last_state = State::STATE_DISABLED; + int64_t time_stamp; + bool use_three_phases = true; + bool simplified_mode = false; + + bool has_ventilation = false; + + bool rcd_error = false; + bool rcd_error_reported = false; + + bool simulation_enabled = false; + double pwm_duty_cycle = 0; + bool pwm_running = false; + bool pwm_error_f = false; + bool last_pwm_running = false; + bool use_three_phases_confirmed = true; + double pwm_voltage_hi = 12.1; + double pwm_voltage_lo = 12.1; + + std::string country_code = "DE"; + int64_t last_pwm_update = 0; + + WattHr watt_hr; + + int64_t powermeter_sim_last_time_stamp = 0L; + + double ev_max_current = 0.0; + int ev_three_phases = 3; +}; + +std::string state_to_string(state::ModuleState& module_state); + +void to_json(nlohmann::json& json, const PowermeterData& powermeter_data); + +} // namespace module::state + +#endif +// NOLINTEND diff --git a/modules/YetiSimulator/util/util.cpp b/modules/YetiSimulator/util/util.cpp new file mode 100644 index 000000000..cc95d15b7 --- /dev/null +++ b/modules/YetiSimulator/util/util.cpp @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "util.hpp" +#include +#include +#include +#include +#include + +namespace module::util { +std::string get_current_iso_time_string() { + using std::chrono::system_clock; + const auto date = system_clock::to_time_t(system_clock::now()); + + auto string_stream = std::stringstream{}; + // NOLINTNEXTLINE(concurrency-mt-unsafe) + string_stream << std::put_time(gmtime(&date), "%F:%T%Z"); + const auto iso_time_string = string_stream.str(); + return iso_time_string; +} + +} // namespace module::util \ No newline at end of file diff --git a/modules/YetiSimulator/util/util.hpp b/modules/YetiSimulator/util/util.hpp new file mode 100644 index 000000000..4afba2be8 --- /dev/null +++ b/modules/YetiSimulator/util/util.hpp @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#ifndef EVEREST_CORE_MODULES_YETISIMULATOR_UTIL_UTIL_HPP +#define EVEREST_CORE_MODULES_YETISIMULATOR_UTIL_UTIL_HPP + +#include +namespace module::util { + +std::string get_current_iso_time_string(); + +} // namespace module::util + +#endif diff --git a/modules/simulation/JsSlacSimulator/CMakeLists.txt b/modules/simulation/JsSlacSimulator/CMakeLists.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/modules/simulation/JsSlacSimulator/index.js b/modules/simulation/JsSlacSimulator/index.js deleted file mode 100644 index 0c9cdea47..000000000 --- a/modules/simulation/JsSlacSimulator/index.js +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest -const { boot_module } = require('everestjs'); -const { setInterval } = require('timers'); - -let state_evse; -let state_ev; -let cntmatching; -const STATE_UNMATCHED = 0; -const STATE_MATCHING = 1; -const STATE_MATCHED = 2; - -function state_to_string(s) { - switch (s) { - case STATE_UNMATCHED: - return 'UNMATCHED'; - case STATE_MATCHING: - return 'MATCHING'; - case STATE_MATCHED: - return 'MATCHED'; - default: - return ''; - } -} - -function set_unmatched_evse(mod) { - if (state_evse !== STATE_UNMATCHED) { - state_evse = STATE_UNMATCHED; - mod.provides.evse.publish.state(state_to_string(state_evse)); - mod.provides.evse.publish.dlink_ready(false); - } -} -function set_unmatched_ev(mod) { - if (state_ev !== STATE_UNMATCHED) { - state_ev = STATE_UNMATCHED; - mod.provides.ev.publish.state(state_to_string(state_ev)); - mod.provides.ev.publish.dlink_ready(false); - } -} - -function set_matching_evse(mod) { - state_evse = STATE_MATCHING; - cntmatching = 0; - mod.provides.evse.publish.state(state_to_string(state_evse)); -} -function set_matching_ev(mod) { - state_ev = STATE_MATCHING; - cntmatching = 0; - mod.provides.ev.publish.state(state_to_string(state_ev)); -} - -function set_matched_evse(mod) { - state_evse = STATE_MATCHED; - mod.provides.evse.publish.state(state_to_string(state_evse)); - mod.provides.evse.publish.dlink_ready(true); -} -function set_matched_ev(mod) { - state_ev = STATE_MATCHED; - mod.provides.ev.publish.state(state_to_string(state_ev)); - mod.provides.ev.publish.dlink_ready(true); -} - -function simulation_loop(mod) { - // if both are in matching for 2 seconds SLAC matches - cntmatching += 1; - if (state_ev === STATE_MATCHING && state_evse === STATE_MATCHING && cntmatching > 2 * 4) { - set_matched_ev(mod); - set_matched_evse(mod); - } -} - -boot_module(async ({ - setup, -}) => { - state_evse = STATE_UNMATCHED; - state_ev = STATE_UNMATCHED; - cntmatching = 0; - - // register commands for EVSE side - setup.provides.evse.register.reset((mod) => { - set_unmatched_evse(mod); - }); - - setup.provides.evse.register.enter_bcd((mod) => { - set_matching_evse(mod); - return true; - }); - - setup.provides.evse.register.leave_bcd((mod) => { - set_unmatched_evse(mod); - return true; - }); - - setup.provides.evse.register.dlink_terminate((mod) => { - set_unmatched_evse(mod); - return true; - }); - - setup.provides.evse.register.dlink_error((mod) => { - set_unmatched_evse(mod); - return true; - }); - - setup.provides.evse.register.dlink_pause(() => true); - - // register commands for EV side - setup.provides.ev.register.reset((mod) => { - set_unmatched_ev(mod); - }); - - setup.provides.ev.register.trigger_matching((mod) => { - set_matching_ev(mod); - return true; - }); -}).then((mod) => { - mod.provides.ev.publish.state(state_to_string(state_ev)); - mod.provides.evse.publish.state(state_to_string(state_evse)); - setInterval(simulation_loop, 250, mod); -}); diff --git a/modules/simulation/JsSlacSimulator/manifest.yaml b/modules/simulation/JsSlacSimulator/manifest.yaml deleted file mode 100644 index 5cfb55ee1..000000000 --- a/modules/simulation/JsSlacSimulator/manifest.yaml +++ /dev/null @@ -1,31 +0,0 @@ -description: SIL Implementation of SLAC data link negotiation according to ISO15118-3. -provides: - evse: - interface: slac - description: SLAC interface implementation for EVSE side - config: - evse_id: - description: EVSE id - 17 octets. - type: string - default: PIONIX_SAYS_HELLO - nid: - description: NID (Network Identification Key) - 7 octets. - type: string - default: pionix! - number_of_sounds: - description: SLAC number of sounds. - type: integer - default: 10 - ev: - interface: ev_slac - description: SLAC interface implementation for EV side - config: - ev_id: - description: EV id - 17 octets. - type: string - default: PIONIX_SAYS_HELLO -enable_external_mqtt: true -metadata: - license: https://opensource.org/licenses/Apache-2.0 - authors: - - Cornelius Claussen (Pionix GmbH) diff --git a/modules/simulation/JsYetiSimulator/CMakeLists.txt b/modules/simulation/JsYetiSimulator/CMakeLists.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/modules/simulation/JsYetiSimulator/index.js b/modules/simulation/JsYetiSimulator/index.js deleted file mode 100644 index 5bf3dcdc2..000000000 --- a/modules/simulation/JsYetiSimulator/index.js +++ /dev/null @@ -1,1454 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest -const { evlog, boot_module } = require('everestjs'); -const { setInterval } = require('timers'); - -const STATE_DISABLED = 0; -const STATE_A = 1; -const STATE_B = 2; -const STATE_C = 3; -const STATE_D = 4; -const STATE_E = 5; -const STATE_F = 6; - -const Event_PowerOn = 8; -const Event_PowerOff = 9; - -let global_info; - -function publish_ac_nr_of_phases_available(mod, n) { - mod.provides.board_support.publish.ac_nr_of_phases_available(n); -} - -function read_pp_ampacity(mod) { - const { pp_resistor } = mod.simulation_data; - if (pp_resistor < 80.0 || pp_resistor > 2460) { - evlog.error(`PP resistor value '${pp_resistor}' Ohm seems to be outside the allowed range.`); - return 'None'; - } - - // PP resistor value in spec, use a conservative interpretation of the resistance ranges - if (pp_resistor > 936.0 && pp_resistor <= 2460.0) { - return 'A_13'; - } - if (pp_resistor > 308.0 && pp_resistor <= 936.0) { - return 'A_20'; - } - if (pp_resistor > 140.0 && pp_resistor <= 308.0) { - return 'A_32'; - } - if (pp_resistor > 80.0 && pp_resistor <= 140.0) { - return 'A_63'; - } - return 'None'; -} - -function stateToString(mod) { - const pwm = (mod.pwm_running ? '2' : '1'); - switch (mod.state) { - case STATE_DISABLED: - return 'Disabled'; - case STATE_A: - return `A${pwm}`; - case STATE_B: - return `B${pwm}`; - case STATE_C: - return `C${pwm}`; - case STATE_D: - return `D${pwm}`; - case STATE_E: - return 'E'; - case STATE_F: - return 'F'; - default: - return ''; - } -} - -function telemetry_slow(mod) { - const date = new Date(); - mod.telemetry_data.power_path_controller_version.timestamp = date.toISOString(); - mod.telemetry.publish('livedata', 'power_path_controller_version', mod.telemetry_data.power_path_controller_version); -} - -function telemetry_fast(mod) { - const date = new Date(); - mod.telemetry_data.power_path_controller.timestamp = date.toISOString(); - mod.telemetry_data.power_path_controller.cp_voltage_high = mod.cpHi; - mod.telemetry_data.power_path_controller.cp_voltage_low = mod.cpLo; - mod.telemetry_data.power_path_controller.cp_pwm_duty_cycle = mod.pwm_duty_cycle * 100.0; - mod.telemetry_data.power_path_controller.cp_state = stateToString(mod); - - mod.telemetry_data.power_path_controller.temperature_controller = mod.powermeter.tempL1; - mod.telemetry_data.power_path_controller.temperature_car_connector = mod.powermeter.tempL1 * 2.0; - mod.telemetry_data.power_path_controller.watchdog_reset_count = 0; - mod.telemetry_data.power_path_controller.error = false; - - mod.telemetry_data.power_switch.timestamp = date.toISOString(); - mod.telemetry_data.power_switch.is_on = mod.relais_on; - mod.telemetry_data.power_switch.time_to_switch_on_ms = 110; - mod.telemetry_data.power_switch.time_to_switch_off_ms = 100; - mod.telemetry_data.power_switch.temperature_C = 20; - mod.telemetry_data.power_switch.error = false; - mod.telemetry_data.power_switch.error_over_current = false; - - mod.telemetry_data.rcd.timestamp = date.toISOString(); - mod.telemetry_data.rcd.current_mA = mod.simulation_data.rcd_current; - - mod.telemetry.publish('livedata', 'power_path_controller', mod.telemetry_data.power_path_controller); - mod.telemetry.publish('livedata', 'power_switch', mod.telemetry_data.power_switch); - mod.telemetry.publish('livedata', 'rcd', mod.telemetry_data.rcd); -} - -function event_to_enum(event) { - switch (event) { - case STATE_A: - return 'A'; - case STATE_B: - return 'B'; - case STATE_C: - return 'C'; - case STATE_D: - return 'D'; - case STATE_E: - return 'E'; - case STATE_F: - return 'F'; - case STATE_DISABLED: - return 'F'; - case Event_PowerOn: - return 'PowerOn'; - case Event_PowerOff: - return 'PowerOff'; - default: - evlog.error(`Invalid event: ${event}`); - return 'invalid'; - } -} - -function publish_event(mod, event) { - mod.provides.board_support.publish.event({ event: event_to_enum(event) }); -} - -function check_error_rcd(mod) { - if (mod.simulation_data.rcd_current > 5.0) { - if (!mod.rcd_error_reported) { - let error = mod.provides.board_support.error_factory.create_error( - 'ac_rcd/DC', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - mod.rcd_error_reported = true; - } - } else { - if (mod.rcd_error_reported) { - mod.provides.board_support.clear_error('ac_rcd/DC'); - } - mod.rcd_error_reported = false; - } - mod.provides.rcd.publish.rcd_current_mA = mod.simulation_data.rcd_current; -} - -function pwmOff(mod) { - mod.pwm_duty_cycle = 1.0; - mod.pwm_running = false; - mod.pwm_error_f = false; -} - -function pwmOn(mod, dutycycle) { - if (dutycycle > 0.0) { - mod.pwm_duty_cycle = dutycycle; - mod.pwm_running = true; - mod.pwm_error_f = false; - } else { - pwmOff(mod); - } -} - -function pwmF(mod) { - mod.pwm_duty_cycle = 1.0; - mod.pwm_running = false; - mod.pwm_error_f = true; -} - -function powerOn(mod) { - if (!mod.relais_on) { - publish_event(mod, Event_PowerOn); - mod.relais_on = true; - mod.telemetry_data.power_switch.switching_count += 1; - } -} - -function powerOff(mod) { - if (mod.relais_on) { - publish_event(mod, Event_PowerOff); - mod.telemetry_data.power_switch.switching_count += 1; - mod.relais_on = false; - } -} - -function drawPower(mod, l1, l2, l3, n) { - mod.simdata_setting.currents.L1 = l1; - mod.simdata_setting.currents.L2 = l2; - mod.simdata_setting.currents.L3 = l3; - mod.simdata_setting.currents.N = n; -} - -function addNoise(mod) { - const noise = (1 + (Math.random() - 0.5) * 0.02); - const lonoise = (1 + (Math.random() - 0.5) * 0.005); - const impedance = mod.simdata_setting.impedance / 1000.0; - - mod.simulation_data.currents.L1 = mod.simdata_setting.currents.L1 * noise; - mod.simulation_data.currents.L2 = mod.simdata_setting.currents.L2 * noise; - mod.simulation_data.currents.L3 = mod.simdata_setting.currents.L3 * noise; - mod.simulation_data.currents.N = mod.simdata_setting.currents.N * noise; - - mod.simulation_data.voltages.L1 = mod.simdata_setting.voltages.L1 * noise - impedance * mod.simulation_data.currents.L1; - mod.simulation_data.voltages.L2 = mod.simdata_setting.voltages.L2 * noise - impedance * mod.simulation_data.currents.L2; - mod.simulation_data.voltages.L3 = mod.simdata_setting.voltages.L3 * noise - impedance * mod.simulation_data.currents.L3; - - mod.simulation_data.frequencies.L1 = mod.simdata_setting.frequencies.L1 * lonoise; - mod.simulation_data.frequencies.L2 = mod.simdata_setting.frequencies.L2 * lonoise; - mod.simulation_data.frequencies.L3 = mod.simdata_setting.frequencies.L3 * lonoise; - - mod.simulation_data.cp_voltage = mod.simdata_setting.cp_voltage * noise; - mod.simulation_data.rcd_current = mod.simdata_setting.rcd_current * noise; - mod.simulation_data.pp_resistor = mod.simdata_setting.pp_resistor * noise; - - mod.simulation_data.diode_fail = mod.simdata_setting.diode_fail; - mod.simulation_data.error_e = mod.simdata_setting.error_e; -} - -// checks if voltage is within center+-interval -function is_voltage_in_range(voltage, center) { - const interval = 1.1; - return ((voltage > center - interval) && (voltage < center + interval)); -} - -// IEC61851 Table A.8 -function dutyCycleToAmps(dc) { - if (dc < 8.0 / 100.0) return 0; - if (dc < 85.0 / 100.0) return dc * 100.0 * 0.6; - if (dc < 96.0 / 100.0) return (dc * 100.0 - 64) * 2.5; - if (dc < 97.0 / 100.0) return 80; - return 0; -} - -function error_BrownOut(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/BrownOut', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/BrownOut'); - } -} - -function error_EnergyManagement(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/EnergyManagement', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/EnergyManagement'); - } -} - -function error_PermanentFault(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/PermanentFault', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/PermanentFault'); - } -} - -function error_MREC2GroundFailure(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC2GroundFailure', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC2GroundFailure'); - } -} - -function error_MREC3HighTemperature(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC3HighTemperature', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC3HighTemperature'); - } -} - -function error_MREC4OverCurrentFailure(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC4OverCurrentFailure', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC4OverCurrentFailure'); - } -} - -function error_MREC5OverVoltage(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC5OverVoltage', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC5OverVoltage'); - } -} - -function error_MREC6UnderVoltage(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC6UnderVoltage', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC6UnderVoltage'); - } -} - -function error_MREC8EmergencyStop(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC8EmergencyStop', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC8EmergencyStop'); - } -} - -function error_MREC10InvalidVehicleMode(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC10InvalidVehicleMode', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC10InvalidVehicleMode'); - } -} - -function error_MREC14PilotFault(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC14PilotFault', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC14PilotFault'); - } -} - -function error_MREC15PowerLoss(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC15PowerLoss', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC15PowerLoss'); - } -} - -function error_MREC17EVSEContactorFault(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC17EVSEContactorFault', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC17EVSEContactorFault'); - } -} - -function error_MREC18CableOverTempDerate(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC18CableOverTempDerate', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC18CableOverTempDerate'); - } -} - -function error_MREC19CableOverTempStop(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC19CableOverTempStop', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC19CableOverTempStop'); - } -} - -function error_MREC20PartialInsertion(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC20PartialInsertion', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC20PartialInsertion'); - } -} - -function error_MREC23ProximityFault(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC23ProximityFault', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC23ProximityFault'); - } -} - -function error_MREC24ConnectorVoltageHigh(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC24ConnectorVoltageHigh', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC24ConnectorVoltageHigh'); - } -} - -function error_MREC25BrokenLatch(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC25BrokenLatch', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC25BrokenLatch'); - } -} - -function error_MREC26CutCable(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/MREC26CutCable', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/MREC26CutCable'); - } -} - -function error_DiodeFault(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'evse_board_support/DiodeFault', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.board_support.raise_error(error); - } else { - mod.provides.board_support.clear_error('evse_board_support/DiodeFault'); - } -} - -function error_ac_rcd_MREC2GroundFailure(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'ac_rcd/MREC2GroundFailure', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.rcd.raise_error(error); - } else { - mod.provides.rcd.clear_error('ac_rcd/MREC2GroundFailure'); - } -} - -function error_ac_rcd_VendorError(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'ac_rcd/VendorError', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.rcd.raise_error(error); - } else { - mod.provides.rcd.clear_error('ac_rcd/VendorError'); - } -} - -function error_ac_rcd_Selftest(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'ac_rcd/Selftest', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.rcd.raise_error(error); - } else { - mod.provides.rcd.clear_error('ac_rcd/Selftest'); - } -} - -function error_ac_rcd_AC(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'ac_rcd/AC', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.rcd.raise_error(error); - } else { - mod.provides.rcd.clear_error('ac_rcd/AC'); - } -} - -function error_ac_rcd_DC(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'ac_rcd/DC', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.rcd.raise_error(error); - } else { - mod.provides.rcd.clear_error('ac_rcd/DC'); - } -} - -function error_lock_ConnectorLockCapNotCharged(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'connector_lock/ConnectorLockCapNotCharged', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.connector_lock.raise_error(error); - } else { - mod.provides.connector_lock.clear_error('connector_lock/ConnectorLockCapNotCharged'); - } -} - -function error_lock_ConnectorLockUnexpectedOpen(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'connector_lock/ConnectorLockUnexpectedOpen', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.connector_lock.raise_error(error); - } else { - mod.provides.connector_lock.clear_error('connector_lock/ConnectorLockUnexpectedOpen'); - } -} - -function error_lock_ConnectorLockUnexpectedClose(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'connector_lock/ConnectorLockUnexpectedClose', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.connector_lock.raise_error(error); - } else { - mod.provides.connector_lock.clear_error('connector_lock/ConnectorLockUnexpectedClose'); - } -} - -function error_lock_ConnectorLockFailedLock(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'connector_lock/ConnectorLockFailedLock', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.connector_lock.raise_error(error); - } else { - mod.provides.connector_lock.clear_error('connector_lock/ConnectorLockFailedLock'); - } -} - -function error_lock_ConnectorLockFailedUnlock(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'connector_lock/ConnectorLockFailedUnlock', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.connector_lock.raise_error(error); - } else { - mod.provides.connector_lock.clear_error('connector_lock/ConnectorLockFailedUnlock'); - } -} - -function error_lock_MREC1ConnectorLockFailure(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'connector_lock/MREC1ConnectorLockFailure', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.connector_lock.raise_error(error); - } else { - mod.provides.connector_lock.clear_error('connector_lock/MREC1ConnectorLockFailure'); - } -} - -function error_lock_VendorError(mod, raise) { - if (raise) { - let error = mod.provides.board_support.error_factory.create_error( - 'connector_lock/VendorError', - '', - 'Simulated fault event', - 'High' - ); - mod.provides.connector_lock.raise_error(error); - } else { - mod.provides.connector_lock.clear_error('connector_lock/VendorError'); - } -} - -// Example of automatically reset errors up on disconnection of the vehicle. -// All other errors need to be cleared explicitly. -// Note that in real life the clearing of errors may differ between BSPs depending on the -// hardware implementation. -function clear_disconnect_errors(mod) { - if (mod.provides.board_support.error_state_monitor.is_error_active('evse_board_support/DiodeFault', '')) { - error_DiodeFault(mod, false); - } -} - -function clearData(mod) { - // Power meter data - mod.powermeter = { - time_stamp: Math.round(new Date().getTime() / 1000), - totalWattHr: 0.0, - - wattL1: 0.0, - vrmsL1: 230.0, - irmsL1: 0.0, - wattHrL1: 0.0, - tempL1: 25.0, - freqL1: 50.0, - - wattL2: 0.0, - vrmsL2: 230.0, - irmsL2: 0.0, - wattHrL2: 0.0, - tempL2: 25.0, - freqL2: 50.0, - - wattL3: 0.0, - vrmsL3: 230.0, - irmsL3: 0.0, - wattHrL3: 0.0, - tempL3: 25.0, - freqL3: 50.0, - - irmsN: 0.0, - }; - - mod.power_on_allowed = false; - - mod.relais_on = false; - mod.current_state = STATE_DISABLED; - mod.last_state = STATE_DISABLED; - mod.time_stamp = Math.round(new Date().getTime() / 1000); - mod.use_three_phases = true; - mod.simplified_mode = false; - - mod.has_ventilation = false; - - mod.rcd_error = false; - mod.rcd_error_reported = false; - - mod.simulation_enabled = false; - mod.pwm_duty_cycle = 0; - mod.pwm_running = false; - mod.pwm_error_f = false; - mod.last_pwm_running = false; - mod.use_three_phases_confirmed = true; - mod.pwm_voltage_hi = 12.1; - mod.pwm_voltage_lo = 12.1; - - mod.simulation_data = { - cp_voltage: 12, - diode_fail: false, - error_e: false, - pp_resistor: 220.1, - rcd_current: 0.1, - - currents: { - L1: 0.0, - L2: 0.0, - L3: 0.0, - N: 0.0, - }, - - voltages: { - L1: 230.0, - L2: 230.0, - L3: 230.0, - }, - - frequencies: { - L1: 50.0, - L2: 50.0, - L3: 50.0, - }, - - }; - - mod.simdata_setting = { - cp_voltage: 12.0, - pp_resistor: 220.1, - impedance: 500.0, - rcd_current: 0.1, - voltages: { L1: 230.0, L2: 230.0, L3: 230.0 }, - currents: { - L1: 0.0, L2: 0.0, L3: 0.0, N: 0.0, - }, - frequencies: { L1: 50.0, L2: 50.0, L3: 50.0 }, - }; - - mod.country_code = 'DE'; - mod.lastPwmUpdate = 0; - - mod.wattHr = { - L1: 0.0, - L2: 0.0, - L3: 0.0, - }; - mod.powermeter_sim_last_time_stamp = 0; - - mod.telemetry_data = { - - power_path_controller_version: { - timestamp: '', - type: 'power_path_controller_version', - hardware_version: 3, - software_version: '1.01', - date_manufactured: '20220304', - operating_time_h: 2330, - operating_time_h_warning: 5000, - operating_time_h_error: 6000, - error: false, - }, - - power_path_controller: { - timestamp: '', - type: 'power_path_controller', - cp_voltage_high: 0.0, - cp_voltage_low: 0.0, - cp_pwm_duty_cycle: 0.0, - cp_state: 'A1', - pp_ohm: 220.1, - supply_voltage_12V: 12.1, - supply_voltage_minus_12V: -11.9, - temperature_controller: 33, - temperature_car_connector: 65, - watchdog_reset_count: 1, - error: false, - }, - - power_switch: { - timestamp: '', - type: 'power_switch', - switching_count: 0, - switching_count_warning: 30000, - switching_count_error: 50000, - is_on: false, - time_to_switch_on_ms: 110, - time_to_switch_off_ms: 100, - temperature_C: 20, - error: false, - error_over_current: false, - }, - - rcd: { - timestamp: '', - type: 'rcd', - enabled: true, - current_mA: 2.5, - triggered: false, - error: false, - }, - }; - mod.ev_max_current = 0.0; - mod.ev_three_phases = 3; -} - -function reset_powermeter(mod) { - mod.wattHr = { - L1: 0.0, - L2: 0.0, - L3: 0.0, - }; - mod.powermeter_sim_last_time_stamp = 0; -} - -function power_meter_external(p) { - const date = new Date(); - return ({ - timestamp: date.toISOString(), - meter_id: 'YETI_POWERMETER', - phase_seq_error: false, - energy_Wh_import: { - total: p.totalWattHr, - L1: p.wattHrL1, - L2: p.wattHrL2, - L3: p.wattHrL3, - }, - power_W: { - total: p.wattL1 + p.wattL2 + p.wattL3, - L1: p.wattL1, - L2: p.wattL2, - L3: p.wattL3, - }, - voltage_V: { - L1: p.vrmsL1, - L2: p.vrmsL2, - L3: p.vrmsL3, - }, - current_A: { - L1: p.irmsL1, - L2: p.irmsL2, - L3: p.irmsL3, - N: p.irmsN, - }, - frequency_Hz: { - L1: p.freqL1, - L2: p.freqL2, - L3: p.freqL3, - }, - }); -} - -function publish_powermeter(mod) { - mod.provides.powermeter.publish.powermeter(power_meter_external(mod.powermeter)); - - // Deprecated external stuff - mod.mqtt.publish('/external/powermeter/vrmsL1', mod.powermeter.vrmsL1); - mod.mqtt.publish('/external/powermeter/phaseSeqError', false); - mod.mqtt.publish('/external/powermeter/time_stamp', mod.powermeter.time_stamp); - mod.mqtt.publish('/external/powermeter/tempL1', mod.powermeter.tempL1); - mod.mqtt.publish( - '/external/powermeter/totalKw', - (mod.powermeter.wattL1 + mod.powermeter.wattL2 + mod.powermeter.wattL3) / 1000.0 - ); - mod.mqtt.publish( - '/external/powermeter/totalKWattHr', - (mod.powermeter.wattHrL1 + mod.powermeter.wattHrL2 + mod.powermeter.wattHrL3) / 1000.0 - ); - mod.mqtt.publish('/external/powermeter_json', JSON.stringify(mod.powermeter)); - - mod.mqtt.publish(`/external/${mod.info.id}/powermeter/tempL1`, mod.powermeter.tempL1); - mod.mqtt.publish( - `/external/${mod.info.id}/powermeter/totalKw`, - (mod.powermeter.wattL1 + mod.powermeter.wattL2 + mod.powermeter.wattL3) / 1000.0 - ); - mod.mqtt.publish( - `/external/${mod.info.id}/powermeter/totalKWattHr`, - (mod.powermeter.wattHrL1 + mod.powermeter.wattHrL2 + mod.powermeter.wattHrL3) / 1000.0 - ); -} - -function publish_keepalive(mod) { - mod.mqtt.publish('/external/keepalive_json', JSON.stringify( - { - hw_revision: 0, - hw_type: 0, - protocol_version_major: 0, - protocol_version_minor: 1, - sw_version_string: 'simulation', - time_stamp: Math.round(new Date().getTime() / 1000), - } - )); -} - -function publish_telemetry(mod) { - mod.provides.board_support.publish.telemetry({ - evse_temperature_C: mod.powermeter.tempL1, - fan_rpm: 1500.0, - supply_voltage_12V: 12.01, - supply_voltage_minus_12V: -11.8, - relais_on: mod.relais_on, - }); -} - -function publish_ev_board_support(mod) { - const pp = { ampacity: read_pp_ampacity(mod) }; - - mod.provides.ev_board_support.publish.bsp_measurement({ - cp_pwm_duty_cycle: mod.pwm_duty_cycle * 100.0, - rcd_current_mA: mod.simulation_data.rcd_current, - proximity_pilot: pp, - }); -} - -function simulate_powermeter(mod) { - const time_stamp = new Date().getTime(); - if (mod.powermeter_sim_last_time_stamp === 0) mod.powermeter_sim_last_time_stamp = time_stamp; - const deltat = time_stamp - mod.powermeter_sim_last_time_stamp; - mod.powermeter_sim_last_time_stamp = time_stamp; - - const wattL1 = mod.simulation_data.voltages.L1 * mod.simulation_data.currents.L1 * (mod.relais_on ? 1 : 0); - const wattL2 = mod.simulation_data.voltages.L2 * mod.simulation_data.currents.L2 - * (mod.relais_on && mod.use_three_phases_confirmed ? 1 : 0); - const wattL3 = mod.simulation_data.voltages.L3 * mod.simulation_data.currents.L3 - * (mod.relais_on && mod.use_three_phases_confirmed ? 1 : 0); - - mod.wattHr.L1 += (wattL1 * deltat) / 1000.0 / 3600.0; - mod.wattHr.L2 += (wattL2 * deltat) / 1000.0 / 3600.0; - mod.wattHr.L3 += (wattL3 * deltat) / 1000.0 / 3600.0; - - mod.powermeter = { - time_stamp: Math.round(time_stamp / 1000), - totalWattHr: Math.round(mod.wattHr.L1 + mod.wattHr.L2 + mod.wattHr.L3), - - wattL1: Math.round(wattL1), - vrmsL1: mod.simulation_data.voltages.L1, - irmsL1: mod.simulation_data.currents.L1, - wattHrL1: Math.round(mod.wattHr.L1), - tempL1: 25.0 + (wattL1 + wattL2 + wattL3) * 0.003, - freqL1: mod.simulation_data.frequencies.L1, - - wattL2: Math.round(wattL2), - vrmsL2: mod.simulation_data.voltages.L2, - irmsL2: mod.simulation_data.currents.L1, - wattHrL2: Math.round(mod.wattHr.L2), - tempL2: 25.0 + (wattL1 + wattL2 + wattL3) * 0.003, - freqL2: mod.simulation_data.frequencies.L2, - - wattL3: Math.round(wattL3), - vrmsL3: mod.simulation_data.voltages.L3, - irmsL3: mod.simulation_data.currents.L3, - wattHrL3: Math.round(mod.wattHr.L3), - tempL3: 25.0 + (wattL1 + wattL2 + wattL3) * 0.003, - freqL3: mod.simulation_data.frequencies.L3, - - irmsN: mod.simulation_data.currents.N, - }; -} - -// Translate ADC readings for lo and hi part of PWM to IEC61851 states. -function read_from_car(mod) { - let amps1 = 0.0; - let amps2 = 0.0; - let amps3 = 0.0; - - let hlc_active = false; - if (mod.pwm_duty_cycle >= 0.03 && mod.pwm_duty_cycle <= 0.07) hlc_active = true; - - let amps = dutyCycleToAmps(mod.pwm_duty_cycle); - if (amps > mod.ev_max_current || hlc_active === true) amps = mod.ev_max_current; - - if (mod.relais_on === true && mod.ev_three_phases > 0) amps1 = amps; - else amps1 = 0; - if (mod.relais_on === true && mod.ev_three_phases > 1 && mod.use_three_phases_confirmed) amps2 = amps; - else amps2 = 0; - if (mod.relais_on === true && mod.ev_three_phases > 2 && mod.use_three_phases_confirmed) amps3 = amps; - else amps3 = 0; - - if (mod.pwm_running) { - mod.pwm_voltage_hi = mod.simulation_data.cp_voltage; - mod.pwm_voltage_lo = -12.0; - } else { - mod.pwm_voltage_hi = mod.simulation_data.cp_voltage; - mod.pwm_voltage_lo = mod.pwm_voltage_hi; - } - - if (mod.pwm_error_f) { - mod.pwm_voltage_hi = -12.0; - mod.pwm_voltage_lo = -12.0; - } - if (mod.simulation_data.error_e) { - mod.pwm_voltage_hi = 0.0; - mod.pwm_voltage_lo = 0.0; - } - if (mod.simulation_data.diode_fail) { - mod.pwm_voltage_lo = -mod.pwm_voltage_hi; - } - - const cpLo = mod.pwm_voltage_lo; - const cpHi = mod.pwm_voltage_hi; - - // sth is wrong with negative signal - if (mod.pwm_running && !is_voltage_in_range(cpLo, -12.0)) { - // CP-PE short or signal somehow gone - if (is_voltage_in_range(cpLo, 0.0) && is_voltage_in_range(cpHi, 0.0)) { - mod.current_state = STATE_E; - drawPower(mod, 0, 0, 0, 0); - } else if (is_voltage_in_range(cpHi + cpLo, 0.0)) { // Diode fault - error_DiodeFault(mod, true); - drawPower(mod, 0, 0, 0, 0); - } - } else if (is_voltage_in_range(cpHi, 12.0)) { - // +12V State A IDLE (open circuit) - // clear all errors that clear on disconnection - clear_disconnect_errors(mod); - mod.current_state = STATE_A; - drawPower(mod, 0, 0, 0, 0); - } else if (is_voltage_in_range(cpHi, 9.0)) { - mod.current_state = STATE_B; - drawPower(mod, 0, 0, 0, 0); - } else if (is_voltage_in_range(cpHi, 6.0)) { - mod.current_state = STATE_C; - drawPower(mod, amps1, amps2, amps3, 0.2); - } else if (is_voltage_in_range(cpHi, 3.0)) { - mod.current_state = STATE_D; - drawPower(mod, amps1, amps2, amps3, 0.2); - } else if (is_voltage_in_range(cpHi, -12.0)) { - mod.current_state = STATE_F; - drawPower(mod, 0, 0, 0, 0); - } -} - -function enable_simulation(mod, args) { - if (mod.simulation_enabled && !args.value) { - publish_event(mod, STATE_A); - clearData(mod); - } - mod.simulation_enabled = args.value; -} - -// state machine for the evse -function simulation_statemachine(mod) { - if (mod.last_state !== mod.current_state) { - publish_event(mod, mod.current_state); - } - - switch (mod.current_state) { - case STATE_DISABLED: - powerOff(); - mod.power_on_allowed = false; - break; - - case STATE_A: - mod.use_three_phases_confirmed = mod.use_three_phases; - pwmOff(mod); - reset_powermeter(mod); - mod.simplified_mode = false; - - if (mod.last_state !== STATE_A && mod.last_state !== STATE_DISABLED - && mod.last_state !== STATE_F) { - powerOff(mod); - - // If car was unplugged, reset RCD flag. - mod.simdata_setting.rcd_current = 0.1; - mod.rcd_error = false; - } - break; - case STATE_B: - // Table A.6: Sequence 7 EV stops charging - // Table A.6: Sequence 8.2 EV supply equipment - // responds to EV opens S2 (w/o PWM) - - if (mod.last_state !== STATE_A && mod.last_state !== STATE_B) { - // Need to switch off according to Table A.6 Sequence 8.1 within - powerOff(mod); - } - - // Table A.6: Sequence 1.1 Plug-in - if (mod.last_state === STATE_A) { - mod.simplified_mode = false; - } - - break; - case STATE_C: - // Table A.6: Sequence 1.2 Plug-in - if (mod.last_state === STATE_A) { - mod.simplified_mode = true; - } - - if (!mod.pwm_running) { // C1 - // Table A.6 Sequence 10.2: EV does not stop drawing power even - // if PWM stops. Stop within 6 seconds (E.g. Kona1!) - // This is implemented in EvseManager - if (!mod.power_on_allowed) powerOff(mod); - } else if (mod.power_on_allowed) { // C2 - // Table A.6: Sequence 4 EV ready to charge. - // Must enable power within 3 seconds. - powerOn(mod); - } - break; - case STATE_D: - // Table A.6: Sequence 1.2 Plug-in (w/ventilation) - if (mod.last_state === STATE_A) { - mod.simplified_mode = true; - } - - if (!mod.pwm_running) { - // Table A.6 Sequence 10.2: EV does not stop drawing power - // even if PWM stops. Stop within 6 seconds (E.g. Kona1!) - /* if (mod.last_pwm_running) // FIMXE implement 6 second timer - startTimer(6000); - if (timerElapsed()) { */ - // force power off under load - powerOff(mod); - // } - } else if (mod.power_on_allowed && !mod.relais_on && mod.has_ventilation) { - // Table A.6: Sequence 4 EV ready to charge. - // Must enable power within 3 seconds. - powerOn(mod); - } - break; - case STATE_E: - powerOff(mod); - pwmOff(mod); - break; - case STATE_F: - powerOff(mod); - break; - default: - break; - } - mod.last_state = mod.current_state; - mod.last_pwm_running = mod.pwm_running; -} - -function simulation_loop(mod) { - if (mod.simulation_enabled) { - check_error_rcd(mod); - read_from_car(mod); - simulation_statemachine(mod); - addNoise(mod); - simulate_powermeter(mod); - publish_ev_board_support(mod); - } - - // console.error(mod); - mod.pubCnt += 1; - switch (mod.pubCnt) { - case 1: - publish_powermeter(mod); - publish_telemetry(mod); - break; - case 2: - break; - case 3: - publish_keepalive(mod); - break; - case 4: - default: - mod.pubCnt = 0; - break; - } -} - -boot_module(async ({ - setup, info, config, mqtt, -}) => { - global_info = info; - // register commands - setup.provides.powermeter.register.stop_transaction(() => ({ - status: 'NOT_SUPPORTED', - error: 'YetiDriver does not support stop transaction request.', - })); - setup.provides.powermeter.register.start_transaction(() => ({ status: 'OK' })); - - setup.provides.board_support.register.ac_set_overcurrent_limit_A(() => { - }); - - setup.provides.board_support.register.enable((mod, args) => { - if (args.value) { - if (mod.current_state === STATE_DISABLED) mod.state = STATE_A; - } else mod.current_state = STATE_DISABLED; - }); - - setup.provides.board_support.register.pwm_on((mod, args) => { pwmOn(mod, args.value / 100.0); }); - setup.provides.board_support.register.pwm_off((mod) => { pwmOff(mod); }); - setup.provides.board_support.register.pwm_F((mod) => { pwmF(mod); }); - setup.provides.board_support.register.evse_replug(() => { - evlog.error('Replugging not supported'); - }); - - setup.provides.board_support.register.allow_power_on((mod, args) => { - mod.power_on_allowed = args.value.allow_power_on; - }); - - setup.provides.board_support.register.ac_switch_three_phases_while_charging((mod, args) => { - mod.use_three_phases = args.value; - mod.use_three_phases_confirmed = args.value; - }); - setup.provides.board_support.register.ac_read_pp_ampacity((mod) => { - const amp = { ampacity: read_pp_ampacity(mod) }; - return amp; - }); - setup.provides.connector_lock.register.lock(() => { - evlog.info('Lock connector'); - }); - setup.provides.connector_lock.register.unlock(() => { - evlog.info('Unlock connector'); - }); - - // bsp ev - setup.provides.ev_board_support.register.enable(enable_simulation); - - setup.provides.ev_board_support.register.set_cp_state((mod, args) => { - switch (args.cp_state) { - case 'A': - mod.simdata_setting.cp_voltage = 12.0; - break; - case 'B': - mod.simdata_setting.cp_voltage = 9.0; - break; - case 'C': - mod.simdata_setting.cp_voltage = 6.0; - break; - case 'D': - mod.simdata_setting.cp_voltage = 3.0; - break; - case 'E': - mod.simdata_setting.error_e = true; - break; - default: - break; - } - }); - - setup.provides.ev_board_support.register.diode_fail((mod, args) => { - mod.simdata_setting.diode_fail = args.value; - }); - - // Right now the YetiSimulator have no option to control the dc powermeter. - setup.provides.ev_board_support.register.allow_power_on((mod, args) => { - evlog.debug(`EV Power On: ${args.value}`); - }); - - setup.provides.ev_board_support.register.set_ac_max_current((mod, args) => { - mod.ev_max_current = args.current; - }); - - setup.provides.ev_board_support.register.set_three_phases((mod, args) => { - if (args.three_phases) mod.ev_three_phases = 3.0; - else mod.ev_three_phases = 1.0; - }); - - setup.provides.ev_board_support.register.set_rcd_error((mod, args) => { - mod.simdata_setting.rcd_current = args.rcd_current_mA; - }); - - // Subscribe to nodered error injection - mqtt.subscribe(`everest_external/nodered/${config.module.connector_id}/carsim/error`, (mod, en) => { - const e = JSON.parse(en); - const raise = e.raise === 'true'; - - switch (e.error_type) { - case 'DiodeFault': - error_DiodeFault(mod, raise); - break; - case 'BrownOut': - error_BrownOut(mod, raise); - break; - case 'EnergyManagement': - error_EnergyManagement(mod, raise); - break; - case 'PermanentFault': - error_PermanentFault(mod, raise); - break; - case 'MREC2GroundFailure': - error_MREC2GroundFailure(mod, raise); - break; - case 'MREC3HighTemperature': - error_MREC3HighTemperature(mod, raise); - break; - case 'MREC4OverCurrentFailure': - error_MREC4OverCurrentFailure(mod, raise); - break; - case 'MREC5OverVoltage': - error_MREC5OverVoltage(mod, raise); - break; - case 'MREC6UnderVoltage': - error_MREC6UnderVoltage(mod, raise); - break; - case 'MREC8EmergencyStop': - error_MREC8EmergencyStop(mod, raise); - break; - case 'MREC10InvalidVehicleMode': - error_MREC10InvalidVehicleMode(mod, raise); - break; - case 'MREC14PilotFault': - error_MREC14PilotFault(mod, raise); - break; - case 'MREC15PowerLoss': - error_MREC15PowerLoss(mod, raise); - break; - case 'MREC17EVSEContactorFault': - error_MREC17EVSEContactorFault(mod, raise); - break; - case 'MREC18CableOverTempDerate': - error_MREC18CableOverTempDerate(mod, raise); - break; - case 'MREC19CableOverTempStop': - error_MREC19CableOverTempStop(mod, raise); - break; - case 'MREC20PartialInsertion': - error_MREC20PartialInsertion(mod, raise); - break; - case 'MREC23ProximityFault': - error_MREC23ProximityFault(mod, raise); - break; - case 'MREC24ConnectorVoltageHigh': - error_MREC24ConnectorVoltageHigh(mod, raise); - break; - case 'MREC25BrokenLatch': - error_MREC25BrokenLatch(mod, raise); - break; - case 'MREC26CutCable': - error_MREC26CutCable(mod, raise); - break; - case 'ac_rcd_MREC2GroundFailure': - error_ac_rcd_MREC2GroundFailure(mod, raise); - break; - case 'ac_rcd_VendorError': - error_ac_rcd_VendorError(mod, raise); - break; - case 'ac_rcd_Selftest': - error_ac_rcd_Selftest(mod, raise); - break; - case 'ac_rcd_AC': - error_ac_rcd_AC(mod, raise); - break; - case 'ac_rcd_DC': - error_ac_rcd_DC(mod, raise); - break; - case 'lock_ConnectorLockCapNotCharged': - error_lock_ConnectorLockCapNotCharged(mod, raise); - break; - case 'lock_ConnectorLockUnexpectedOpen': - error_lock_ConnectorLockUnexpectedOpen(mod, raise); - break; - case 'lock_ConnectorLockUnexpectedClose': - error_lock_ConnectorLockUnexpectedClose(mod, raise); - break; - case 'lock_ConnectorLockFailedLock': - error_lock_ConnectorLockFailedLock(mod, raise); - break; - case 'lock_ConnectorLockFailedUnlock': - error_lock_ConnectorLockFailedUnlock(mod, raise); - break; - case 'lock_MREC1ConnectorLockFailure': - error_lock_MREC1ConnectorLockFailure(mod, raise); - break; - case 'lock_VendorError': - error_lock_VendorError(mod, raise); - break; - default: - evlog.error('Unknown error raised via MQTT'); - } - }); -}).then((mod) => { - mod.pubCnt = 0; - clearData(mod); - - mod.provides.board_support.publish.capabilities({ - max_current_A_import: 32.0, - min_current_A_import: 6.0, - max_phase_count_import: 3, - min_phase_count_import: 1, - max_current_A_export: 16.0, - min_current_A_export: 0.0, - max_phase_count_export: 3, - min_phase_count_export: 1, - supports_changing_phases_during_charging: true, - connector_type: 'IEC62196Type2Cable', - }); - - setInterval(simulation_loop, 250, mod); - if (global_info.telemetry_enabled) { - setInterval(telemetry_slow, 15000, mod); - setInterval(telemetry_fast, 1000, mod); - } -});