// SPDX-License-Identifier: Apache-2.0 // Copyright Pionix GmbH and Contributors to EVerest #ifndef EVSE_MANAGER_HPP #define EVSE_MANAGER_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 // headers for required interface implementations #include #include #include #include #include #include #include #include #include #include // ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1 // insert your custom include headers here #include #include #include #include #include #include #include #include #include #include "CarManufacturer.hpp" #include "Charger.hpp" #include "ErrorHandling.hpp" #include "PersistentStore.hpp" #include "SessionLog.hpp" #include "VarContainer.hpp" #include "over_voltage/OverVoltageMonitor.hpp" #include "scoped_lock_timeout.hpp" #include "voltage_plausibility/VoltagePlausibilityMonitor.hpp" #include // ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1 namespace module { struct Conf { int connector_id; std::string connector_type; std::string evse_id; std::string evse_id_din; bool payment_enable_eim; bool payment_enable_contract; double ac_nominal_voltage; bool ev_receipt_required; bool session_logging; std::string session_logging_path; bool session_logging_xml; bool has_ventilation; std::string charge_mode; bool supported_iso_ac_bpt; bool ac_hlc_enabled; bool ac_hlc_use_5percent; bool ac_enforce_hlc; bool ac_with_soc; int internal_over_voltage_duration_ms; bool dbg_hlc_auth_after_tstep; int dc_isolation_voltage_V; int cable_check_wait_number_of_imd_measurements; bool cable_check_enable_imd_self_test; bool cable_check_enable_imd_self_test_relays_open; int cable_check_relays_open_voltage_V; int cable_check_relays_closed_timeout_s; bool cable_check_wait_below_60V_before_finish; bool hack_skoda_enyaq; int hack_present_current_offset; bool hack_pause_imd_during_precharge; bool hack_allow_bpt_with_iso2; bool hack_simplified_mode_limit_10A; bool autocharge_use_slac_instead_of_hlc; bool enable_autocharge; std::string logfile_suffix; double soft_over_current_tolerance_percent; double soft_over_current_measurement_noise_A; bool hack_fix_hlc_integer_current_requests; bool disable_authentication; bool sae_j2847_2_bpt_enabled; std::string sae_j2847_2_bpt_mode; bool request_zero_power_in_idle; bool external_ready_to_start_charging; bool uk_smartcharging_random_delay_enable; int uk_smartcharging_random_delay_max_duration; bool uk_smartcharging_random_delay_at_any_change; int initial_meter_value_timeout_ms; int switch_3ph1ph_delay_s; std::string switch_3ph1ph_cp_state; int soft_over_current_timeout_ms; bool lock_connector_in_state_b; int state_F_after_fault_ms; bool fail_on_powermeter_errors; bool raise_mrec9; int sleep_before_enabling_pwm_hlc_mode_ms; bool central_contract_validation_allowed; bool contract_certificate_installation_enabled; bool inoperative_error_use_vendor_id; double voltage_plausibility_max_spread_threshold_V; int voltage_plausibility_fault_duration_ms; std::string session_id_type; bool zero_power_ignore_pause; bool zero_power_allow_ev_to_ignore_pause; std::string bpt_channel; std::string bpt_generator_mode; std::string bpt_grid_code_island_method; int hlc_charge_loop_without_energy_timeout_s; int dc_ramp_ampere_per_second; }; class EvseManager : public Everest::ModuleBase { public: EvseManager() = delete; EvseManager(const ModuleInfo& info, Everest::MqttProvider& mqtt_provider, Everest::TelemetryProvider& telemetry, std::unique_ptr p_evse, std::unique_ptr p_energy_grid, std::unique_ptr p_token_provider, std::unique_ptr p_random_delay, std::unique_ptr p_dc_external_derate, std::unique_ptr r_bsp, std::vector> r_ac_rcd, std::vector> r_connector_lock, std::vector> r_powermeter_grid_side, std::vector> r_powermeter_car_side, std::vector> r_slac, std::vector> r_hlc, std::vector> r_imd, std::vector> r_over_voltage_monitor, std::vector> r_powersupply_DC, std::vector> r_store, Conf& config) : ModuleBase(info), mqtt(mqtt_provider), telemetry(telemetry), p_evse(std::move(p_evse)), p_energy_grid(std::move(p_energy_grid)), p_token_provider(std::move(p_token_provider)), p_random_delay(std::move(p_random_delay)), p_dc_external_derate(std::move(p_dc_external_derate)), r_bsp(std::move(r_bsp)), r_ac_rcd(std::move(r_ac_rcd)), r_connector_lock(std::move(r_connector_lock)), r_powermeter_grid_side(std::move(r_powermeter_grid_side)), r_powermeter_car_side(std::move(r_powermeter_car_side)), r_slac(std::move(r_slac)), r_hlc(std::move(r_hlc)), r_imd(std::move(r_imd)), r_over_voltage_monitor(std::move(r_over_voltage_monitor)), r_powersupply_DC(std::move(r_powersupply_DC)), r_store(std::move(r_store)), config(config){}; Everest::MqttProvider& mqtt; Everest::TelemetryProvider& telemetry; const std::unique_ptr p_evse; const std::unique_ptr p_energy_grid; const std::unique_ptr p_token_provider; const std::unique_ptr p_random_delay; const std::unique_ptr p_dc_external_derate; const std::unique_ptr r_bsp; const std::vector> r_ac_rcd; const std::vector> r_connector_lock; const std::vector> r_powermeter_grid_side; const std::vector> r_powermeter_car_side; const std::vector> r_slac; const std::vector> r_hlc; const std::vector> r_imd; const std::vector> r_over_voltage_monitor; const std::vector> r_powersupply_DC; const std::vector> r_store; const Conf& config; // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1 // insert your public definitions here std::unique_ptr charger; sigslot::signal signalNrOfPhasesAvailable; types::powermeter::Powermeter get_latest_powermeter_data_billing(); types::evse_board_support::HardwareCapabilities get_hw_capabilities(); std::atomic ready_for_capabilities{false}; std::mutex external_local_limits_mutex; bool update_max_current_limit(types::energy::ExternalLimits& limits, float max_current_import, float max_current_export); bool update_max_watt_limit(types::energy::ExternalLimits& limits, float max_watt_export, std::optional max_watt_import); void update_to_zero_discharge_limit(types::energy::ExternalLimits& limits); bool update_local_energy_limit(types::energy::ExternalLimits l); types::energy::ExternalLimits get_local_energy_limits(); void cancel_reservation(bool signal_event); bool is_reserved(); std::optional connector_type; /// /// \brief Reserve this evse. /// \param id The reservation id. /// \param signal_reservation_event True when other modules must be signalled about a new reservation (session /// event). /// \return True on success. /// bool reserve(int32_t id, const bool signal_reservation_event = true); int32_t get_reservation_id(); bool get_hlc_waiting_for_auth_pnc(); void set_pnc_enabled(const bool pnc_enabled); void set_central_contract_validation_allowed(const bool central_contract_validation_allowed); void set_contract_certificate_installation_enabled(const bool contract_certificate_installation_enabled); sigslot::signal signalReservationEvent; void charger_was_authorized(); const std::vector>& r_powermeter_billing(); // FIXME: this will be removed with proper integration of BPT on ISO-20 // on DIN SPEC and -2 we claim a positive charging current on ISO protocol, // but the power supply switches to discharge if this flag is set. std::atomic_bool is_actually_exporting_to_grid{false}; types::evse_manager::EVInfo get_ev_info(); void apply_new_target_voltage_current(); void process_dc_ev_target_voltage_current(const types::iso15118::DcEvseMaximumLimits& hlc_limits); std::string selected_protocol = "Unknown"; std::atomic_bool sae_bidi_active{false}; void ready_to_start_charging(); std::unique_ptr bsp; std::unique_ptr error_handling; std::unique_ptr store; std::atomic_bool random_delay_enabled{false}; std::atomic_bool random_delay_running{false}; std::chrono::time_point random_delay_end_time; std::chrono::time_point random_delay_start_time; std::atomic random_delay_max_duration; std::atomic> timepoint_ready_for_charging; bool session_is_iso_d20_ac_bpt(); bool session_is_iso_d20_dc_bpt(); types::power_supply_DC::Capabilities get_powersupply_capabilities(); void set_external_derating(types::dc_external_derate::ExternalDerating d); void update_powersupply_capabilities(types::power_supply_DC::Capabilities caps) { std::scoped_lock lock(powersupply_capabilities_mutex); if (caps != powersupply_capabilities) { r_hlc[0]->call_set_powersupply_capabilities(caps); } powersupply_capabilities = caps; // Inform HLC layer about update of physical values types::iso15118::SetupPhysicalValues setup_physical_values; setup_physical_values.dc_current_regulation_tolerance = powersupply_capabilities.current_regulation_tolerance_A; setup_physical_values.dc_peak_current_ripple = powersupply_capabilities.peak_current_ripple_A; setup_physical_values.dc_energy_to_be_delivered = 10000; r_hlc[0]->call_set_charging_parameters(setup_physical_values); types::iso15118::DcEvseMinimumLimits evse_min_limits; evse_min_limits.evse_minimum_current_limit = powersupply_capabilities.min_export_current_A; evse_min_limits.evse_minimum_voltage_limit = powersupply_capabilities.min_export_voltage_V; evse_min_limits.evse_minimum_power_limit = evse_min_limits.evse_minimum_current_limit * evse_min_limits.evse_minimum_voltage_limit; r_hlc[0]->call_update_dc_minimum_limits(evse_min_limits); // HLC layer will also get new maximum current/voltage/watt limits etc, but those will need to run through // energy management first. Those limits will be applied in energy_grid implementation when requesting // energy, so it is enough to set the powersupply_capabilities here. // FIXME: this is not implemented yet: enforce_limits uses the enforced limits to tell HLC, but capabilities // limits are not yet included in request. } std::atomic_int ac_nr_phases_active{0}; // 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 std::mutex powersupply_capabilities_mutex; types::power_supply_DC::Capabilities powersupply_capabilities; std::mutex dc_external_derate_mutex; types::dc_external_derate::ExternalDerating dc_external_derate; Everest::timed_mutex_traceable power_mutex; types::powermeter::Powermeter latest_powermeter_data_billing; Everest::Thread energyThreadHandle; everest::lib::util::monitor hw_capabilities; types::energy::ExternalLimits external_local_energy_limits; const float EVSE_ABSOLUTE_MAX_CURRENT = 80.0; bool slac_enabled; std::atomic_bool contactor_open{true}; Everest::timed_mutex_traceable charger_ready_mutex; bool charger_ready{false}; std::atomic_bool hlc_enabled; std::atomic_bool hlc_waiting_for_auth_eim; std::atomic_bool hlc_waiting_for_auth_pnc; std::atomic_bool pnc_enabled{false}; std::atomic_bool central_contract_validation_allowed{false}; std::atomic_bool contract_certificate_installation_enabled{false}; VarContainer isolation_measurement; VarContainer powersupply_measurement; VarContainer selftest_result; std::unique_ptr internal_over_voltage_monitor; // Track voltage to earth failures for debouncing int voltage_to_earth_failure_count{0}; std::chrono::steady_clock::time_point first_voltage_to_earth_failure_time{}; static constexpr std::chrono::seconds MIN_TIME_BETWEEN_FIRST_AND_LAST_FAILURE{2}; static constexpr int REQUIRED_CONSECUTIVE_FAILURES{2}; std::atomic latest_target_current_low_pass{0.}; std::atomic latest_target_current_low_pass_last_update{}; std::atomic latest_target_voltage{0.}; std::atomic latest_target_current{0.}; std::atomic last_power_supply_voltage{0.}; std::atomic last_power_supply_current{0.}; // Raw EV target values as received from ISO15118 stack std::atomic raw_ev_target_voltage{0.}; std::atomic raw_ev_target_current{0.}; types::authorization::ProvidedIdToken autocharge_token; void log_v2g_message(types::iso15118::V2gMessages const& v2g_messages); // Reservations bool reserved; int32_t reservation_id; Everest::timed_mutex_traceable reservation_mutex; // Voltage plausibility monitor std::unique_ptr voltage_plausibility_monitor; void setup_AC_mode(); void setup_fake_DC_mode(); // special funtion to switch mode while session is active void switch_AC_mode(); void switch_DC_mode(); // DC handlers void cable_check(); void powersupply_DC_on(); std::atomic_bool powersupply_dc_is_on{false}; bool powersupply_DC_set(double voltage, double current); void powersupply_DC_off(); bool wait_powersupply_DC_voltage_reached(double target_voltage); bool wait_powersupply_DC_below_voltage(double target_voltage); bool cable_check_should_exit(); double get_emergency_over_voltage_threshold(); double get_error_over_voltage_threshold(); // EV information Everest::timed_mutex_traceable ev_info_mutex; types::evse_manager::EVInfo ev_info; types::evse_manager::CarManufacturer car_manufacturer{types::evse_manager::CarManufacturer::Unknown}; void imd_stop(); void imd_start(); Everest::Thread telemetryThreadHandle; void fail_cable_check(const std::string& reason); // setup sae j2847/2 v2h mode void setup_v2h_mode(); bool check_isolation_resistance_in_range(double resistance); bool check_voltage_to_protective_earth_in_range(types::isolation_monitor::IsolationMeasurement m); static constexpr double CABLECHECK_CURRENT_LIMIT{2}; static constexpr double CABLECHECK_INSULATION_FAULT_RESISTANCE_OHM{100000.}; static constexpr double CABLECHECK_SAFE_VOLTAGE{60.}; static constexpr int CABLECHECK_SELFTEST_TIMEOUT{30}; std::atomic_bool current_demand_active{false}; std::atomic_bool slac_unmatched{false}; std::mutex powermeter_mutex; std::condition_variable powermeter_cv; bool initial_powermeter_value_received{false}; std::optional selected_d20_energy_service{std::nullopt}; std::atomic power_supply_DC_charging_phase{ types::power_supply_DC::ChargingPhase::Other}; types::power_supply_DC::ChargingPhase last_power_supply_DC_charging_phase{ types::power_supply_DC::ChargingPhase::Other}; everest::lib::util::monitor> supported_energy_transfers; void publish_and_update_supported_energy_transfers(); bool update_supported_energy_transfers(const std::vector& energy_transfers); bool update_supported_energy_transfers(const types::iso15118::EnergyTransferMode& energy_transfer); std::mutex hlc_ac_parameters_mutex; void update_hlc_ac_parameters(); // 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 // EVSE_MANAGER_HPP