Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
471
tools/EVerest-main/modules/EV/EvManager/main/car_simulation.cpp
Normal file
471
tools/EVerest-main/modules/EV/EvManager/main/car_simulation.cpp
Normal file
@@ -0,0 +1,471 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#include "car_simulation.hpp"
|
||||
|
||||
#include "constants.hpp"
|
||||
|
||||
#include <everest/logging.hpp>
|
||||
|
||||
constexpr double MS_FACTOR = (1.0 / 60.0 / 60.0 / 1000.0);
|
||||
|
||||
void CarSimulation::state_machine() {
|
||||
using types::ev_board_support::EvCpState;
|
||||
|
||||
const auto state_has_changed = sim_data.state != sim_data.last_state;
|
||||
sim_data.last_state = sim_data.state;
|
||||
|
||||
switch (sim_data.state) {
|
||||
case SimState::UNPLUGGED:
|
||||
if (state_has_changed) {
|
||||
|
||||
r_ev_board_support->call_set_cp_state(EvCpState::A);
|
||||
r_ev_board_support->call_allow_power_on(false);
|
||||
// Wait for physical plugin (ev BSP sees state A on CP and not Disconnected)
|
||||
|
||||
sim_data.slac_state = types::slac::State::UNMATCHED;
|
||||
if (!r_ev.empty()) {
|
||||
r_ev[0]->call_stop_charging();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SimState::PLUGGED_IN:
|
||||
if (state_has_changed) {
|
||||
r_ev_board_support->call_set_cp_state(EvCpState::B);
|
||||
r_ev_board_support->call_allow_power_on(false);
|
||||
simulate_soc();
|
||||
}
|
||||
break;
|
||||
case SimState::CHARGING_REGULATED:
|
||||
if (state_has_changed || sim_data.pwm_duty_cycle != sim_data.last_pwm_duty_cycle) {
|
||||
sim_data.last_pwm_duty_cycle = sim_data.pwm_duty_cycle;
|
||||
// do not draw power if EVSE paused by stopping PWM
|
||||
if (sim_data.pwm_duty_cycle > 7.0 && sim_data.pwm_duty_cycle < 97.0) {
|
||||
r_ev_board_support->call_set_cp_state(EvCpState::C);
|
||||
r_ev_board_support->call_allow_power_on(true);
|
||||
} else {
|
||||
r_ev_board_support->call_set_cp_state(EvCpState::B);
|
||||
r_ev_board_support->call_allow_power_on(false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SimState::CHARGING_FIXED:
|
||||
// Todo(sl): What to do here
|
||||
if (state_has_changed) {
|
||||
// Also draw power if EVSE stopped PWM - this is a break the rules simulator->mode to test the charging
|
||||
// implementation!
|
||||
r_ev_board_support->call_set_cp_state(EvCpState::C);
|
||||
r_ev_board_support->call_allow_power_on(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case SimState::ERROR_E:
|
||||
if (state_has_changed) {
|
||||
r_ev_board_support->call_set_cp_state(EvCpState::E);
|
||||
r_ev_board_support->call_allow_power_on(false);
|
||||
}
|
||||
break;
|
||||
case SimState::DIODE_FAIL:
|
||||
if (state_has_changed) {
|
||||
r_ev_board_support->call_diode_fail(true);
|
||||
r_ev_board_support->call_allow_power_on(false);
|
||||
}
|
||||
break;
|
||||
case SimState::ISO_POWER_READY:
|
||||
if (state_has_changed) {
|
||||
r_ev_board_support->call_set_cp_state(EvCpState::C);
|
||||
}
|
||||
break;
|
||||
case SimState::ISO_CHARGING_REGULATED:
|
||||
if (state_has_changed) {
|
||||
r_ev_board_support->call_set_cp_state(EvCpState::C);
|
||||
r_ev_board_support->call_allow_power_on(true);
|
||||
}
|
||||
break;
|
||||
case SimState::BCB_TOGGLE:
|
||||
if (sim_data.bcb_toggle_C) {
|
||||
r_ev_board_support->call_set_cp_state(EvCpState::C);
|
||||
sim_data.bcb_toggle_C = false;
|
||||
} else {
|
||||
r_ev_board_support->call_set_cp_state(EvCpState::B);
|
||||
sim_data.bcb_toggle_C = true;
|
||||
++sim_data.bcb_toggles;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sim_data.state = SimState::UNPLUGGED;
|
||||
break;
|
||||
}
|
||||
|
||||
if (not state_has_changed and
|
||||
(sim_data.state == SimState::CHARGING_REGULATED or sim_data.state == SimState::CHARGING_FIXED or
|
||||
sim_data.state == SimState::ISO_CHARGING_REGULATED)) {
|
||||
simulate_soc();
|
||||
}
|
||||
timepoint_last_update = std::chrono::steady_clock::now();
|
||||
};
|
||||
|
||||
void CarSimulation::simulate_soc() {
|
||||
const double ms =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - timepoint_last_update)
|
||||
.count();
|
||||
const double factor = MS_FACTOR * ms;
|
||||
double power = 0.0;
|
||||
types::evse_manager::EVInfo ev_info;
|
||||
switch (charge_mode) {
|
||||
case ChargeMode::None:
|
||||
// nothing to do
|
||||
break;
|
||||
case ChargeMode::AC:
|
||||
power = charge_current_a * config.ac_nominal_voltage;
|
||||
ev_info.target_current = charge_current_a;
|
||||
ev_info.target_voltage = 0;
|
||||
break;
|
||||
case ChargeMode::ACThreePhase:
|
||||
power = charge_current_a * config.ac_nominal_voltage * 3.0;
|
||||
ev_info.target_current = charge_current_a;
|
||||
ev_info.target_voltage = 0;
|
||||
break;
|
||||
case ChargeMode::DC:
|
||||
power = config.dc_target_current * config.dc_target_voltage;
|
||||
ev_info.target_current = config.dc_target_current;
|
||||
ev_info.target_voltage = config.dc_target_voltage;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sim_data.battery_charge_wh > sim_data.battery_capacity_wh) {
|
||||
sim_data.battery_charge_wh = sim_data.battery_capacity_wh;
|
||||
} else {
|
||||
sim_data.battery_charge_wh += power * factor;
|
||||
}
|
||||
|
||||
auto soc = (sim_data.battery_charge_wh / sim_data.battery_capacity_wh) * 100.0;
|
||||
|
||||
if (soc > 100.0) {
|
||||
soc = 100.0;
|
||||
} else if (soc <= 0.0) {
|
||||
soc = 0.0;
|
||||
}
|
||||
|
||||
if (latest_soc != soc) {
|
||||
latest_soc = soc;
|
||||
|
||||
if (!r_ev.empty()) {
|
||||
r_ev[0]->call_update_soc(soc);
|
||||
}
|
||||
}
|
||||
|
||||
ev_info.soc = soc;
|
||||
ev_info.battery_capacity = sim_data.battery_capacity_wh;
|
||||
ev_info.battery_full_soc = 100;
|
||||
|
||||
p_ev_manager->publish_ev_info(ev_info);
|
||||
}
|
||||
|
||||
bool CarSimulation::sleep(const CmdArguments& arguments, size_t loop_interval_ms) {
|
||||
if (not sim_data.sleep_ticks_left.has_value()) {
|
||||
const auto sleep_time = std::stold(arguments[0]);
|
||||
const auto sleep_time_ms = sleep_time * 1000;
|
||||
sim_data.sleep_ticks_left = static_cast<long long>(sleep_time_ms / loop_interval_ms) + 1;
|
||||
}
|
||||
auto& sleep_ticks_left = sim_data.sleep_ticks_left.value();
|
||||
sleep_ticks_left -= 1;
|
||||
if (not(sleep_ticks_left > 0)) {
|
||||
sim_data.sleep_ticks_left.reset();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CarSimulation::iec_wait_pwr_ready(const CmdArguments& arguments) {
|
||||
sim_data.state = SimState::PLUGGED_IN;
|
||||
return (sim_data.pwm_duty_cycle > 7.0f && sim_data.pwm_duty_cycle < 97.0f);
|
||||
}
|
||||
|
||||
bool CarSimulation::iso_wait_pwm_is_running(const CmdArguments& arguments) {
|
||||
sim_data.state = SimState::PLUGGED_IN;
|
||||
return (sim_data.pwm_duty_cycle > 4.0f && sim_data.pwm_duty_cycle < 97.0f);
|
||||
}
|
||||
|
||||
bool CarSimulation::draw_power_regulated(const CmdArguments& arguments) {
|
||||
charge_current_a = std::stod(arguments[0]);
|
||||
r_ev_board_support->call_set_ac_max_current(charge_current_a);
|
||||
if (arguments[1] == constants::THREE_PHASES) {
|
||||
r_ev_board_support->call_set_three_phases(true);
|
||||
charge_mode = ChargeMode::ACThreePhase;
|
||||
} else {
|
||||
r_ev_board_support->call_set_three_phases(false);
|
||||
charge_mode = ChargeMode::AC;
|
||||
}
|
||||
sim_data.state = SimState::CHARGING_REGULATED;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CarSimulation::draw_power_fixed(const CmdArguments& arguments) {
|
||||
charge_current_a = std::stod(arguments[0]);
|
||||
r_ev_board_support->call_set_ac_max_current(charge_current_a);
|
||||
if (arguments[1] == constants::THREE_PHASES) {
|
||||
r_ev_board_support->call_set_three_phases(true);
|
||||
charge_mode = ChargeMode::ACThreePhase;
|
||||
} else {
|
||||
r_ev_board_support->call_set_three_phases(false);
|
||||
charge_mode = ChargeMode::AC;
|
||||
}
|
||||
sim_data.state = SimState::CHARGING_FIXED;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CarSimulation::pause(const CmdArguments& arguments) {
|
||||
sim_data.state = SimState::PLUGGED_IN;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CarSimulation::unplug(const CmdArguments& arguments) {
|
||||
sim_data.state = SimState::UNPLUGGED;
|
||||
charge_mode = ChargeMode::None;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CarSimulation::error_e(const CmdArguments& arguments) {
|
||||
sim_data.state = SimState::ERROR_E;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CarSimulation::diode_fail(const CmdArguments& arguments) {
|
||||
sim_data.state = SimState::DIODE_FAIL;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CarSimulation::rcd_current(const CmdArguments& arguments) {
|
||||
sim_data.rcd_current_ma = std::stof(arguments[0]);
|
||||
r_ev_board_support->call_set_rcd_error(sim_data.rcd_current_ma);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CarSimulation::iso_wait_slac_matched(const CmdArguments& arguments) {
|
||||
sim_data.state = SimState::PLUGGED_IN;
|
||||
|
||||
if (sim_data.slac_state == types::slac::State::UNMATCHED) {
|
||||
EVLOG_debug << "Slac UNMATCHED";
|
||||
if (!r_slac.empty()) {
|
||||
EVLOG_debug << "Slac trigger matching";
|
||||
r_slac[0]->call_reset();
|
||||
r_slac[0]->call_trigger_matching();
|
||||
sim_data.slac_state = types::slac::State::MATCHING;
|
||||
}
|
||||
}
|
||||
if (sim_data.slac_state == types::slac::State::MATCHED) {
|
||||
EVLOG_debug << "Slac Matched";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CarSimulation::iso_wait_pwr_ready(const CmdArguments& arguments) {
|
||||
if (sim_data.iso_pwr_ready) {
|
||||
sim_data.state = SimState::ISO_POWER_READY;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CarSimulation::iso_dc_power_on(const CmdArguments& arguments) {
|
||||
sim_data.state = SimState::ISO_POWER_READY;
|
||||
if (sim_data.dc_power_on) {
|
||||
sim_data.state = SimState::ISO_CHARGING_REGULATED;
|
||||
r_ev_board_support->call_allow_power_on(true);
|
||||
charge_mode = ChargeMode::DC;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sim_data.iso_charger_paused) {
|
||||
|
||||
const auto cmds =
|
||||
std::array<std::string, 2>{"pause;iso_wait_v2g_session_stopped;sleep 2;iso_wait_pwm_is_running;",
|
||||
"iso_wait_pwr_ready;iso_wait_for_stop 36000"};
|
||||
|
||||
EVLOG_info << "Charger wants to pause the session";
|
||||
r_ev_board_support->call_allow_power_on(false);
|
||||
|
||||
// NOTE(sl): Change when the Energymode has more then 2 values
|
||||
const std::string energy_mode = (sim_data.energy_mode == EnergyMode::AC) ? "AC" : "DC";
|
||||
const std::string iso_start_v2g_session = "iso_start_v2g_session " + energy_mode + ";";
|
||||
|
||||
auto& modify_session_cmds = sim_data.modify_charging_session_cmds.emplace();
|
||||
|
||||
modify_session_cmds = cmds[0];
|
||||
modify_session_cmds += iso_start_v2g_session;
|
||||
modify_session_cmds += cmds[1];
|
||||
|
||||
sim_data.iso_pwr_ready = false;
|
||||
sim_data.sleep_ticks_left.reset();
|
||||
sim_data.iso_charger_paused = false;
|
||||
|
||||
// NOTE(sl): return false, otherwise the simulation will end too early before the session cmds can be adjusted
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CarSimulation::iso_start_v2g_session(const CmdArguments& arguments, bool three_phases) {
|
||||
const auto& energy_mode = arguments[0];
|
||||
const auto& payment_option = arguments[1];
|
||||
const auto& departure_time = std::stoi(arguments[2]);
|
||||
const auto& e_amount = std::stoi(arguments[3]);
|
||||
|
||||
const auto selected_payment_option = [payment_option,
|
||||
this](bool auto_payment_option) -> types::iso15118::SelectedPaymentOption {
|
||||
if (auto_payment_option) {
|
||||
return types::iso15118::SelectedPaymentOption{};
|
||||
}
|
||||
auto selected_payment_option =
|
||||
types::iso15118::SelectedPaymentOption{std::nullopt, config.force_payment_option};
|
||||
|
||||
if (payment_option == "externalpayment") {
|
||||
selected_payment_option.payment_option = types::iso15118::PaymentOption::ExternalPayment;
|
||||
} else if (payment_option == "contract") {
|
||||
selected_payment_option.payment_option = types::iso15118::PaymentOption::Contract;
|
||||
} else {
|
||||
EVLOG_warning << "Go back to auto payment because payment_option was not recognized";
|
||||
selected_payment_option.enforce_payment_option.reset();
|
||||
}
|
||||
|
||||
return selected_payment_option;
|
||||
}(payment_option == "auto");
|
||||
|
||||
if (energy_mode == constants::AC) {
|
||||
sim_data.energy_mode = EnergyMode::AC;
|
||||
if (not three_phases) {
|
||||
r_ev[0]->call_start_charging(types::iso15118::EnergyTransferMode::AC_single_phase_core,
|
||||
selected_payment_option, departure_time, e_amount);
|
||||
charge_mode = ChargeMode::AC;
|
||||
} else {
|
||||
r_ev[0]->call_start_charging(types::iso15118::EnergyTransferMode::AC_three_phase_core,
|
||||
selected_payment_option, departure_time, e_amount);
|
||||
charge_mode = ChargeMode::ACThreePhase;
|
||||
}
|
||||
} else if (energy_mode == constants::DC) {
|
||||
r_ev[0]->call_start_charging(types::iso15118::EnergyTransferMode::DC_extended, selected_payment_option,
|
||||
departure_time, e_amount);
|
||||
sim_data.energy_mode = EnergyMode::DC;
|
||||
charge_mode = ChargeMode::DC;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CarSimulation::iso_draw_power_regulated(const CmdArguments& arguments) {
|
||||
charge_current_a = std::stod(arguments[0]);
|
||||
r_ev_board_support->call_set_ac_max_current(charge_current_a);
|
||||
if (arguments[1] == constants::THREE_PHASES) {
|
||||
r_ev_board_support->call_set_three_phases(true);
|
||||
charge_mode = ChargeMode::ACThreePhase;
|
||||
} else {
|
||||
r_ev_board_support->call_set_three_phases(false);
|
||||
charge_mode = ChargeMode::AC;
|
||||
}
|
||||
sim_data.state = SimState::ISO_CHARGING_REGULATED;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CarSimulation::iso_stop_charging(const CmdArguments& arguments) {
|
||||
r_ev[0]->call_stop_charging();
|
||||
r_ev_board_support->call_allow_power_on(false);
|
||||
if (sim_data.state != SimState::UNPLUGGED) {
|
||||
sim_data.state = SimState::PLUGGED_IN;
|
||||
}
|
||||
charge_mode = ChargeMode::None;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CarSimulation::iso_wait_for_stop(const CmdArguments& arguments, size_t loop_interval_ms) {
|
||||
if (not sim_data.sleep_ticks_left.has_value()) {
|
||||
const auto sleep_time_ms = std::stold(arguments[0]) * 1000;
|
||||
sim_data.sleep_ticks_left = static_cast<long long>(sleep_time_ms / loop_interval_ms) + 1;
|
||||
}
|
||||
auto& sleep_ticks_left = sim_data.sleep_ticks_left.value();
|
||||
sleep_ticks_left -= 1;
|
||||
if (not(sleep_ticks_left > 0)) {
|
||||
r_ev[0]->call_stop_charging();
|
||||
r_ev_board_support->call_allow_power_on(false);
|
||||
sim_data.state = SimState::PLUGGED_IN;
|
||||
sim_data.sleep_ticks_left.reset();
|
||||
return true;
|
||||
}
|
||||
if (sim_data.iso_stopped) {
|
||||
EVLOG_info << "POWER OFF iso stopped";
|
||||
r_ev_board_support->call_allow_power_on(false);
|
||||
sim_data.state = SimState::PLUGGED_IN;
|
||||
sim_data.sleep_ticks_left.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sim_data.iso_charger_paused) {
|
||||
|
||||
const auto cmds =
|
||||
std::array<std::string, 2>{"pause;iso_wait_v2g_session_stopped;sleep 2;iso_wait_pwm_is_running;",
|
||||
"iso_wait_pwr_ready;iso_wait_for_stop 36000"};
|
||||
|
||||
EVLOG_info << "Charger wants to pause the session";
|
||||
r_ev_board_support->call_allow_power_on(false);
|
||||
|
||||
// NOTE(sl): Change when the Energymode has more then 2 values
|
||||
const std::string energy_mode = (sim_data.energy_mode == EnergyMode::AC) ? "AC" : "DC";
|
||||
const std::string iso_start_v2g_session = "iso_start_v2g_session " + energy_mode + ";";
|
||||
|
||||
auto& modify_session_cmds = sim_data.modify_charging_session_cmds.emplace();
|
||||
|
||||
modify_session_cmds = cmds[0];
|
||||
modify_session_cmds += iso_start_v2g_session;
|
||||
modify_session_cmds += cmds[1];
|
||||
|
||||
sim_data.iso_pwr_ready = false;
|
||||
sim_data.sleep_ticks_left.reset();
|
||||
sim_data.iso_charger_paused = false;
|
||||
|
||||
// NOTE(sl): return false, otherwise the simulation will end too early before the session cmds can be adjusted
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CarSimulation::iso_wait_v2g_session_stopped(const CmdArguments& arguments) {
|
||||
if (sim_data.v2g_finished) {
|
||||
sim_data.v2g_finished = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CarSimulation::iso_pause_charging(const CmdArguments& arguments) {
|
||||
r_ev[0]->call_pause_charging();
|
||||
sim_data.state = SimState::PLUGGED_IN;
|
||||
sim_data.iso_pwr_ready = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CarSimulation::iso_wait_for_resume(const CmdArguments& arguments) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CarSimulation::iso_start_bcb_toggle(const CmdArguments& arguments) {
|
||||
sim_data.v2g_finished = false;
|
||||
sim_data.state = SimState::BCB_TOGGLE;
|
||||
if (sim_data.bcb_toggles >= std::stoul(arguments[0]) || sim_data.bcb_toggles == 3) {
|
||||
sim_data.bcb_toggles = 0;
|
||||
sim_data.state = SimState::PLUGGED_IN;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CarSimulation::wait_for_real_plugin(const CmdArguments& arguments) {
|
||||
using types::board_support_common::Event;
|
||||
if (sim_data.actual_bsp_event == Event::A) {
|
||||
EVLOG_info << "Real plugin detected";
|
||||
sim_data.state = SimState::PLUGGED_IN;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user