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:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,113 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef CHARGER_ISO15118_CHARGER_IMPL_HPP
#define CHARGER_ISO15118_CHARGER_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/ISO15118_charger/Implementation.hpp>
#include "../Evse15118D20.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
#include <bitset>
#include <mutex>
#include "utils.hpp"
#include <iso15118/d20/config.hpp>
#include <iso15118/session/feedback.hpp>
#include <iso15118/tbd_controller.hpp>
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace charger {
struct Conf {};
class ISO15118_chargerImpl : public ISO15118_chargerImplBase {
public:
ISO15118_chargerImpl() = delete;
ISO15118_chargerImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<Evse15118D20>& mod, Conf& config) :
ISO15118_chargerImplBase(ev, "charger"), 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_setup(types::iso15118::EVSEID& evse_id, types::iso15118::SaeJ2847BidiMode& sae_j2847_mode,
bool& debug_mode) override;
virtual void handle_set_charging_parameters(types::iso15118::SetupPhysicalValues& physical_values) override;
virtual void handle_session_setup(std::vector<types::iso15118::PaymentOption>& payment_options,
bool& supported_certificate_service,
bool& central_contract_validation_allowed) override;
virtual void handle_bpt_setup(types::iso15118::BptSetup& bpt_config) override;
virtual void handle_set_powersupply_capabilities(types::power_supply_DC::Capabilities& capabilities) override;
virtual void handle_authorization_response(types::authorization::AuthorizationStatus& authorization_status,
types::authorization::CertificateStatus& certificate_status) override;
virtual void handle_ac_contactor_closed(bool& status) override;
virtual void handle_dlink_ready(bool& value) override;
virtual void handle_cable_check_finished(bool& status) override;
virtual void handle_receipt_is_required(bool& receipt_required) override;
virtual void handle_stop_charging(bool& stop) override;
virtual void handle_pause_charging(bool& pause) override;
virtual void handle_no_energy_pause_charging(types::iso15118::NoEnergyPauseMode& mode) override;
virtual bool
handle_update_supported_app_protocols(types::iso15118::SupportedAppProtocols& supported_app_protocols) override;
virtual void handle_update_energy_transfer_modes(
std::vector<types::iso15118::EnergyTransferMode>& supported_energy_transfer_modes) override;
virtual void handle_update_ac_max_current(double& max_current) override;
virtual void handle_update_ac_parameters(types::iso15118::AcParameters& ac_parameters) override;
virtual void handle_update_ac_maximum_limits(types::iso15118::AcEvseMaximumPower& maximum_limits) override;
virtual void handle_update_ac_minimum_limits(types::iso15118::AcEvseMinimumPower& minimum_limits) override;
virtual void handle_update_ac_target_values(types::iso15118::AcTargetValues& target_values) override;
virtual void handle_update_ac_present_power(types::units::Power& present_power) override;
virtual void handle_update_dc_maximum_limits(types::iso15118::DcEvseMaximumLimits& maximum_limits) override;
virtual void handle_update_dc_minimum_limits(types::iso15118::DcEvseMinimumLimits& minimum_limits) override;
virtual void handle_update_isolation_status(types::iso15118::IsolationStatus& isolation_status) override;
virtual void
handle_update_dc_present_values(types::iso15118::DcEvsePresentVoltageCurrent& present_voltage_current) override;
virtual void handle_update_meter_info(types::powermeter::Powermeter& powermeter) override;
virtual void handle_send_error(types::iso15118::EvseError& error) override;
virtual void handle_reset_error() override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<Evse15118D20>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
iso15118::session::feedback::Callbacks create_callbacks();
std::unique_ptr<iso15118::TbdController> controller;
iso15118::d20::EvseSetupConfig setup_config;
std::bitset<NUMBER_OF_SETUP_STEPS> setup_steps_done{0};
std::vector<iso15118::d20::SupportedVASs> supported_vas_services_per_provider;
std::mutex vas_mutex;
void update_supported_vas_services();
std::optional<size_t> get_vas_provider_index(uint16_t service_id);
// 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 charger
} // namespace module
#endif // CHARGER_ISO15118_CHARGER_IMPL_HPP

View File

@@ -0,0 +1,141 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "session_logger.hpp"
#include <chrono>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <stdexcept>
#include <date/date.h>
#include <iso15118/session/logger.hpp>
#include <everest/logging.hpp>
using LogEvent = iso15118::session::logging::Event;
std::string get_filename_for_current_time() {
const auto now = std::chrono::system_clock::now();
const auto now_t = std::chrono::system_clock::to_time_t(now);
std::tm now_tm;
gmtime_r(&now_t, &now_tm);
char buffer[64];
strftime(buffer, sizeof(buffer), "%y%m%d_%H-%M-%S.yaml", &now_tm);
return buffer;
}
// static auto timepoint_to_string(const iso15118::session::logging::TimePoint& timepoint) {
// using namespace date;
// return static_cast<std::string>(timepoint);
// }
std::ostream& operator<<(std::ostream& os, const iso15118::session::logging::ExiMessageDirection& direction) {
using Direction = iso15118::session::logging::ExiMessageDirection;
switch (direction) {
case Direction::FROM_EV:
return os << "FROM_EV";
case Direction::TO_EV:
return os << "TO_EV";
}
return os;
}
class SessionLog {
public:
SessionLog(const std::string& file_name) : file(file_name.c_str(), std::ios::out) {
if (not file.good()) {
throw std::runtime_error("Failed to open file " + file_name + " for writing iso15118 session log");
}
EVLOG_info << "Created logfile at: " << file_name;
}
void operator()(const iso15118::session::logging::SimpleEvent& event) {
file << "- type: INFO\n";
add_timestamp(event.time_point);
file << " info: \"" << event.info << "\"\n";
}
void operator()(const iso15118::session::logging::ExiMessageEvent& event) {
file << "- type: EXI\n";
add_timestamp(event.time_point);
file << " direction: " << event.direction << "\n";
file << " sdp_payload_type: " << event.payload_type << "\n";
add_hex_encoded_data(event.data, event.len);
}
void flush() {
file.flush();
}
private:
std::fstream file;
void add_timestamp(const iso15118::session::logging::TimePoint& timestamp) {
if (not timestamp_initialized) {
last_timestamp = timestamp;
timestamp_initialized = true;
}
const auto offset_ms = std::chrono::duration_cast<std::chrono::milliseconds>(timestamp - last_timestamp);
file << " timestamp_offset: " << offset_ms.count() << "\n";
const auto dp = date::floor<date::days>(timestamp);
const auto time = date::make_time(timestamp - dp);
const auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(time.subseconds());
file << " timestamp: \"";
file << std::setfill('0') << std::setw(2) << time.hours().count() << ":";
file << std::setfill('0') << std::setw(2) << time.minutes().count() << ":";
file << std::setfill('0') << std::setw(2) << time.seconds().count() << ".";
file << std::setfill('0') << std::setw(4) << milliseconds.count();
file << "\"\n";
last_timestamp = timestamp;
}
void add_hex_encoded_data(const uint8_t* data, size_t len) {
file << " data: \"";
const auto flags = file.flags();
file << std::hex;
for (int i = 0; i < len; ++i) {
file << std::setfill('0') << std::setw(2) << static_cast<int>(data[i]);
}
file.flags(flags);
file << "\"\n";
}
iso15118::session::logging::TimePoint last_timestamp;
bool timestamp_initialized{false};
};
SessionLogger::SessionLogger(std::filesystem::path output_dir_) : output_dir(std::filesystem::absolute(output_dir_)) {
// FIXME (aw): this is quite brute force ...
if (not std::filesystem::exists(output_dir)) {
std::filesystem::create_directory(output_dir);
}
iso15118::session::logging::set_session_log_callback([this](std::uintptr_t id, const LogEvent& event) {
auto log_it = logs.find(id);
if (log_it == logs.end()) {
const auto log_file_name = output_dir / get_filename_for_current_time();
const auto emplaced = logs.emplace(id, std::make_unique<SessionLog>(log_file_name.string()));
log_it = emplaced.first;
}
auto& log = *log_it->second;
std::visit(log, event);
log.flush();
});
}
SessionLogger::~SessionLogger() = default;

View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
#include <filesystem>
#include <map>
#include <memory>
// forward declare
class SessionLog;
class SessionLogger {
public:
SessionLogger(std::filesystem::path output_dir);
~SessionLogger();
private:
std::filesystem::path output_dir;
std::map<std::uintptr_t, std::unique_ptr<SessionLog>> logs;
};

View File

@@ -0,0 +1,508 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "utils.hpp"
#include <everest/logging.hpp>
#include <iso15118/d20/limits.hpp>
#include <iso15118/message/common_types.hpp>
#include <iso15118/message/dc_charge_parameter_discovery.hpp>
#include <iso15118/message/schedule_exchange.hpp>
#include <iso15118/message/service_detail.hpp>
namespace module::charger {
namespace dt = iso15118::message_20::datatypes;
namespace {
dt::Parameter convert_parameter(const types::iso15118_vas::Parameter& parameter) {
dt::Parameter out;
out.name = parameter.name;
if (parameter.value.bool_value.has_value()) {
out.value = parameter.value.bool_value.value();
} else if (parameter.value.int_value.has_value()) {
out.value = static_cast<int32_t>(parameter.value.int_value.value());
} else if (parameter.value.short_value.has_value()) {
out.value = static_cast<int16_t>(parameter.value.short_value.value());
} else if (parameter.value.byte_value.has_value()) {
out.value = static_cast<int8_t>(parameter.value.byte_value.value());
} else if (parameter.value.rational_number.has_value()) {
out.value = dt::from_float(parameter.value.rational_number.value());
} else if (parameter.value.finite_string.has_value()) {
out.value = parameter.value.finite_string.value();
} else {
throw std::invalid_argument("Invalid ParameterValue in convert_parameter: " + parameter.name +
" has no value set");
}
return out;
}
dt::ParameterSet convert_parameter_set(const types::iso15118_vas::ParameterSet& parameter_set) {
dt::ParameterSet out;
out.id = parameter_set.set_id;
for (const auto& parameter : parameter_set.parameters) {
const auto* ptr = out.parameter.try_emplace_back(convert_parameter(parameter));
if (ptr == nullptr) {
EVLOG_warning << "VAS parameter set is bigger then 32";
break;
}
}
return out;
}
} // namespace
std::optional<float> convert_from_optional(const std::optional<dt::RationalNumber>& in) {
return (in.has_value()) ? std::make_optional(dt::from_RationalNumber(*in)) : std::nullopt;
}
std::optional<dt::RationalNumber> convert_from_optional(const std::optional<float>& in) {
return (in.has_value()) ? std::make_optional(dt::from_float(*in)) : std::nullopt;
}
std::optional<float> convert_from_optional(const std::optional<uint32_t>& in) {
return (in.has_value()) ? std::make_optional(static_cast<float>(*in)) : std::nullopt;
}
types::iso15118::AppProtocol convert_app_protocol(const iso15118::message_20::SupportedAppProtocol& app_protocol) {
types::iso15118::AppProtocol result;
result.protocol_namespace = app_protocol.protocol_namespace;
result.priority = app_protocol.priority;
result.schema_id = app_protocol.schema_id;
result.version_number_major = static_cast<int32_t>(app_protocol.version_number_major);
result.version_number_minor = static_cast<int32_t>(app_protocol.version_number_minor);
return result;
}
types::iso15118::EvInformation convert_ev_info(const iso15118::d20::EVInformation& ev_info) {
types::iso15118::EvInformation result;
result.evcc_id = ev_info.evcc_id;
result.selected_protocol = convert_app_protocol(ev_info.selected_app_protocol);
result.supported_protocols.Protocols.reserve(ev_info.ev_supported_app_protocols.size());
for (const auto& supported_app : ev_info.ev_supported_app_protocols) {
result.supported_protocols.Protocols.push_back(convert_app_protocol(supported_app));
}
result.tls_leaf_certificate = ev_info.ev_tls_leaf_cert;
result.tls_sub_ca_1_certificate = ev_info.ev_tls_sub_ca_1_cert;
result.tls_sub_ca_2_certificate = ev_info.ev_tls_sub_ca_2_cert;
result.tls_root_certificate = ev_info.ev_tls_root_cert;
return result;
}
types::iso15118::DcChargeDynamicModeValues convert_dynamic_values(const dt::Dynamic_DC_CLReqControlMode& in) {
return {dt::from_RationalNumber(in.target_energy_request),
dt::from_RationalNumber(in.max_energy_request),
dt::from_RationalNumber(in.min_energy_request),
dt::from_RationalNumber(in.max_charge_power),
dt::from_RationalNumber(in.min_charge_power),
dt::from_RationalNumber(in.max_charge_current),
dt::from_RationalNumber(in.max_voltage),
dt::from_RationalNumber(in.min_voltage),
convert_from_optional(in.departure_time),
std::nullopt,
std::nullopt,
std::nullopt,
std::nullopt,
std::nullopt};
}
types::iso15118::DcChargeDynamicModeValues convert_dynamic_values(const dt::BPT_Dynamic_DC_CLReqControlMode& in) {
return {dt::from_RationalNumber(in.target_energy_request), dt::from_RationalNumber(in.max_energy_request),
dt::from_RationalNumber(in.min_energy_request), dt::from_RationalNumber(in.max_charge_power),
dt::from_RationalNumber(in.min_charge_power), dt::from_RationalNumber(in.max_charge_current),
dt::from_RationalNumber(in.max_voltage), dt::from_RationalNumber(in.min_voltage),
convert_from_optional(in.departure_time), dt::from_RationalNumber(in.max_discharge_power),
dt::from_RationalNumber(in.min_discharge_power), dt::from_RationalNumber(in.max_discharge_current),
convert_from_optional(in.max_v2x_energy_request), convert_from_optional(in.min_v2x_energy_request)};
}
template <>
void fill_v2x_charging_parameters(types::iso15118::V2XChargingParameters& out_params,
const iso15118::d20::DcTransferLimits& evse_limits,
const dt::DC_CPDReqEnergyTransferMode& ev_limits) {
// As per the OCPP 2.1 spec (2.109) we should use the MIN/MAX function between EV and EVSE
out_params.min_charge_power = std::max(dt::from_RationalNumber(evse_limits.charge_limits.power.min),
dt::from_RationalNumber(ev_limits.min_charge_power));
out_params.max_charge_power = std::min(dt::from_RationalNumber(evse_limits.charge_limits.power.max),
dt::from_RationalNumber(ev_limits.max_charge_power));
out_params.min_charge_current = std::max(dt::from_RationalNumber(evse_limits.charge_limits.current.min),
dt::from_RationalNumber(ev_limits.min_charge_current));
out_params.max_charge_current = std::min(dt::from_RationalNumber(evse_limits.charge_limits.current.max),
dt::from_RationalNumber(ev_limits.max_charge_current));
out_params.min_voltage =
std::max(dt::from_RationalNumber(evse_limits.voltage.min), dt::from_RationalNumber(ev_limits.min_voltage));
out_params.max_voltage =
std::min(dt::from_RationalNumber(evse_limits.voltage.max), dt::from_RationalNumber(ev_limits.max_voltage));
out_params.target_soc = ev_limits.target_soc;
}
template <>
void fill_v2x_charging_parameters(types::iso15118::V2XChargingParameters& out_params,
const iso15118::d20::DcTransferLimits& evse_limits,
const dt::BPT_DC_CPDReqEnergyTransferMode& ev_limits) {
// Fill in the common data
fill_v2x_charging_parameters<iso15118::d20::DcTransferLimits, dt::DC_CPDReqEnergyTransferMode>(
out_params, evse_limits, static_cast<const dt::DC_CPDReqEnergyTransferMode&>(ev_limits));
// Fill in the bidi data
if (evse_limits.discharge_limits.has_value()) {
const auto& evse_discharge_limits = evse_limits.discharge_limits.value();
out_params.min_discharge_power = std::max(dt::from_RationalNumber(evse_discharge_limits.power.min),
dt::from_RationalNumber(ev_limits.min_discharge_power));
out_params.max_discharge_power = std::min(dt::from_RationalNumber(evse_discharge_limits.power.max),
dt::from_RationalNumber(ev_limits.max_discharge_power));
out_params.min_discharge_current = std::max(dt::from_RationalNumber(evse_discharge_limits.current.min),
dt::from_RationalNumber(ev_limits.min_discharge_current));
out_params.max_discharge_current = std::min(dt::from_RationalNumber(evse_discharge_limits.current.max),
dt::from_RationalNumber(ev_limits.max_discharge_current));
}
}
template <>
void fill_v2x_charging_parameters(types::iso15118::V2XChargingParameters& out_params,
const iso15118::d20::AcTransferLimits& evse_limits,
const dt::AC_CPDReqEnergyTransferMode& ev_limits) {
// As per the OCPP 2.1 spec (2.109) we should use the MIN/MAX function between EV and EVSE
out_params.min_charge_power = std::max(dt::from_RationalNumber(evse_limits.charge_power.min),
dt::from_RationalNumber(ev_limits.min_charge_power));
out_params.max_charge_power = std::min(dt::from_RationalNumber(evse_limits.charge_power.max),
dt::from_RationalNumber(ev_limits.max_charge_power));
if (evse_limits.charge_power_L2.has_value() and ev_limits.max_charge_power_L2.has_value() and
ev_limits.min_charge_power_L2.has_value()) {
out_params.min_charge_power_l2 = std::max(dt::from_RationalNumber(evse_limits.charge_power_L2.value().min),
dt::from_RationalNumber(ev_limits.min_charge_power_L2.value()));
out_params.max_charge_power_l2 = std::min(dt::from_RationalNumber(evse_limits.charge_power_L2.value().max),
dt::from_RationalNumber(ev_limits.max_charge_power_L2.value()));
}
if (evse_limits.charge_power_L3.has_value() and ev_limits.max_charge_power_L3.has_value() and
ev_limits.min_charge_power_L3.has_value()) {
out_params.min_charge_power_l3 = std::max(dt::from_RationalNumber(evse_limits.charge_power_L3.value().min),
dt::from_RationalNumber(ev_limits.min_charge_power_L3.value()));
out_params.max_charge_power_l3 = std::min(dt::from_RationalNumber(evse_limits.charge_power_L3.value().max),
dt::from_RationalNumber(ev_limits.max_charge_power_L3.value()));
}
}
template <>
void fill_v2x_charging_parameters(types::iso15118::V2XChargingParameters& out_params,
const iso15118::d20::AcTransferLimits& evse_limits,
const dt::BPT_AC_CPDReqEnergyTransferMode& ev_limits) {
// Fill in the common data
fill_v2x_charging_parameters<iso15118::d20::AcTransferLimits, dt::AC_CPDReqEnergyTransferMode>(
out_params, evse_limits, static_cast<const dt::AC_CPDReqEnergyTransferMode&>(ev_limits));
if (evse_limits.discharge_power.has_value()) {
const auto& evse_discharge_limits = evse_limits.discharge_power.value();
out_params.min_discharge_power = std::max(dt::from_RationalNumber(evse_discharge_limits.min),
dt::from_RationalNumber(ev_limits.min_discharge_power));
out_params.max_discharge_power = std::min(dt::from_RationalNumber(evse_discharge_limits.max),
dt::from_RationalNumber(ev_limits.max_discharge_power));
}
if (evse_limits.discharge_power_L2.has_value() && ev_limits.min_discharge_power_L2.has_value() &&
ev_limits.max_discharge_power_L2.has_value()) {
const auto& evse_discharge_limits = evse_limits.discharge_power_L2.value();
out_params.min_discharge_power_l2 = std::max(dt::from_RationalNumber(evse_discharge_limits.min),
dt::from_RationalNumber(ev_limits.min_discharge_power_L2.value()));
out_params.max_discharge_power_l2 = std::min(dt::from_RationalNumber(evse_discharge_limits.max),
dt::from_RationalNumber(ev_limits.max_discharge_power_L2.value()));
}
if (evse_limits.discharge_power_L3.has_value() && ev_limits.min_discharge_power_L3.has_value() &&
ev_limits.max_discharge_power_L3.has_value()) {
const auto& evse_discharge_limits = evse_limits.discharge_power_L3.value();
out_params.min_discharge_power_l3 = std::max(dt::from_RationalNumber(evse_discharge_limits.min),
dt::from_RationalNumber(ev_limits.min_discharge_power_L3.value()));
out_params.max_discharge_power_l3 = std::min(dt::from_RationalNumber(evse_discharge_limits.max),
dt::from_RationalNumber(ev_limits.max_discharge_power_L3.value()));
}
}
template <>
void fill_v2x_charging_parameters(types::iso15118::V2XChargingParameters& out_params,
const dt::Scheduled_SEReqControlMode& ev_control_mode) {
out_params.ev_target_energy_request = convert_from_optional(ev_control_mode.target_energy);
out_params.ev_min_energy_request = convert_from_optional(ev_control_mode.min_energy);
out_params.ev_max_energy_request = convert_from_optional(ev_control_mode.max_energy);
}
template <>
void fill_v2x_charging_parameters(types::iso15118::V2XChargingParameters& out_params,
const dt::Dynamic_SEReqControlMode& ev_control_mode) {
out_params.ev_target_energy_request = dt::from_RationalNumber(ev_control_mode.target_energy);
out_params.ev_min_energy_request = dt::from_RationalNumber(ev_control_mode.min_energy);
out_params.ev_max_energy_request = dt::from_RationalNumber(ev_control_mode.max_energy);
out_params.ev_min_v2xenergy_request = convert_from_optional(ev_control_mode.min_v2x_energy);
out_params.ev_max_v2xenergy_request = convert_from_optional(ev_control_mode.max_v2x_energy);
}
everest::lib::util::fixed_vector<dt::ParameterSet, 32>
convert_parameter_set_list(const std::vector<types::iso15118_vas::ParameterSet>& parameter_set_list) {
everest::lib::util::fixed_vector<dt::ParameterSet, 32> out;
for (const auto& parameter_set : parameter_set_list) {
const auto* ptr = out.try_emplace_back(convert_parameter_set(parameter_set));
if (ptr == nullptr) {
EVLOG_warning << "VAS parameter set list is bigger then 32";
break;
}
}
return out;
}
types::iso15118::AcEvPowerLimits fill_ac_ev_power_limits(const dt::AC_CPDReqEnergyTransferMode& mode) {
const auto max_charge_L1 = dt::from_RationalNumber(mode.max_charge_power);
const auto max_charge_L2 = convert_from_optional(mode.max_charge_power_L2);
const auto max_charge_L3 = convert_from_optional(mode.max_charge_power_L3);
const auto max_charge_total = max_charge_L1 + max_charge_L2.value_or(0.0) + max_charge_L3.value_or(0.0);
const auto min_charge_L1 = dt::from_RationalNumber(mode.min_charge_power);
const auto min_charge_L2 = convert_from_optional(mode.min_charge_power_L2);
const auto min_charge_L3 = convert_from_optional(mode.min_charge_power_L3);
const auto min_charge_total = min_charge_L1 + min_charge_L2.value_or(0.0) + min_charge_L3.value_or(0.0);
types::iso15118::AcEvPowerLimits ac_ev_power_limits;
ac_ev_power_limits.max_charge_power = {max_charge_total, std::make_optional<float>(max_charge_L1), max_charge_L2,
max_charge_L3};
ac_ev_power_limits.min_charge_power = {min_charge_total, std::make_optional<float>(min_charge_L1), min_charge_L2,
min_charge_L3};
return ac_ev_power_limits;
}
types::iso15118::AcEvPowerLimits fill_ac_ev_power_limits(const dt::BPT_AC_CPDReqEnergyTransferMode& mode) {
const auto max_charge_L1 = dt::from_RationalNumber(mode.max_charge_power);
const auto max_charge_L2 = convert_from_optional(mode.max_charge_power_L2);
const auto max_charge_L3 = convert_from_optional(mode.max_charge_power_L3);
const auto max_charge_total = max_charge_L1 + max_charge_L2.value_or(0.0) + max_charge_L3.value_or(0.0);
const auto min_charge_L1 = dt::from_RationalNumber(mode.min_charge_power);
const auto min_charge_L2 = convert_from_optional(mode.min_charge_power_L2);
const auto min_charge_L3 = convert_from_optional(mode.min_charge_power_L3);
const auto min_charge_total = min_charge_L1 + min_charge_L2.value_or(0.0) + min_charge_L3.value_or(0.0);
const auto max_discharge_L1 = dt::from_RationalNumber(mode.max_discharge_power);
const auto max_discharge_L2 = convert_from_optional(mode.max_discharge_power_L2);
const auto max_discharge_L3 = convert_from_optional(mode.max_discharge_power_L3);
const auto max_discharge_total = max_discharge_L1 + max_discharge_L2.value_or(0.0) + max_discharge_L3.value_or(0.0);
const auto min_discharge_L1 = dt::from_RationalNumber(mode.min_discharge_power);
const auto min_discharge_L2 = convert_from_optional(mode.min_discharge_power_L2);
const auto min_discharge_L3 = convert_from_optional(mode.min_discharge_power_L3);
const auto min_discharge_total = min_discharge_L1 + min_discharge_L2.value_or(0.0) + min_discharge_L3.value_or(0.0);
types::iso15118::AcEvPowerLimits ac_ev_power_limits;
ac_ev_power_limits.max_charge_power = {max_charge_total, std::make_optional<float>(max_charge_L1), max_charge_L2,
max_charge_L3};
ac_ev_power_limits.min_charge_power = {min_charge_total, std::make_optional<float>(min_charge_L1), min_charge_L2,
min_charge_L3};
ac_ev_power_limits.max_discharge_power = {max_discharge_total, std::make_optional<float>(max_discharge_L1),
max_discharge_L2, max_discharge_L3};
ac_ev_power_limits.min_discharge_power = {min_discharge_total, std::make_optional<float>(min_discharge_L1),
min_discharge_L2, min_discharge_L3};
return ac_ev_power_limits;
}
types::iso15118::AcEvPowerLimits fill_ac_ev_power_limits(const dt::Dynamic_AC_CLReqControlMode& mode) {
const auto max_charge_L1 = dt::from_RationalNumber(mode.max_charge_power);
const auto max_charge_L2 = convert_from_optional(mode.max_charge_power_L2);
const auto max_charge_L3 = convert_from_optional(mode.max_charge_power_L3);
const auto max_charge_total = max_charge_L1 + max_charge_L2.value_or(0.0) + max_charge_L3.value_or(0.0);
const auto min_charge_L1 = dt::from_RationalNumber(mode.min_charge_power);
const auto min_charge_L2 = convert_from_optional(mode.min_charge_power_L2);
const auto min_charge_L3 = convert_from_optional(mode.min_charge_power_L3);
const auto min_charge_total = min_charge_L1 + min_charge_L2.value_or(0.0) + min_charge_L3.value_or(0.0);
types::iso15118::AcEvPowerLimits ac_ev_power_limits;
ac_ev_power_limits.max_charge_power = {max_charge_total, std::make_optional<float>(max_charge_L1), max_charge_L2,
max_charge_L3};
ac_ev_power_limits.min_charge_power = {min_charge_total, std::make_optional<float>(min_charge_L1), min_charge_L2,
min_charge_L3};
return ac_ev_power_limits;
}
types::iso15118::AcEvPowerLimits fill_ac_ev_power_limits(const dt::BPT_Dynamic_AC_CLReqControlMode& mode) {
const auto max_charge_L1 = dt::from_RationalNumber(mode.max_charge_power);
const auto max_charge_L2 = convert_from_optional(mode.max_charge_power_L2);
const auto max_charge_L3 = convert_from_optional(mode.max_charge_power_L3);
const auto max_charge_total = max_charge_L1 + max_charge_L2.value_or(0.0) + max_charge_L3.value_or(0.0);
const auto min_charge_L1 = dt::from_RationalNumber(mode.min_charge_power);
const auto min_charge_L2 = convert_from_optional(mode.min_charge_power_L2);
const auto min_charge_L3 = convert_from_optional(mode.min_charge_power_L3);
const auto min_charge_total = min_charge_L1 + min_charge_L2.value_or(0.0) + min_charge_L3.value_or(0.0);
const auto max_discharge_L1 = dt::from_RationalNumber(mode.max_discharge_power);
const auto max_discharge_L2 = convert_from_optional(mode.max_discharge_power_L2);
const auto max_discharge_L3 = convert_from_optional(mode.max_discharge_power_L3);
const auto max_discharge_total = max_discharge_L1 + max_discharge_L2.value_or(0.0) + max_discharge_L3.value_or(0.0);
const auto min_discharge_L1 = dt::from_RationalNumber(mode.min_discharge_power);
const auto min_discharge_L2 = convert_from_optional(mode.min_discharge_power_L2);
const auto min_discharge_L3 = convert_from_optional(mode.min_discharge_power_L3);
const auto min_discharge_total = min_discharge_L1 + min_discharge_L2.value_or(0.0) + min_discharge_L3.value_or(0.0);
types::iso15118::AcEvPowerLimits ac_ev_power_limits;
ac_ev_power_limits.max_charge_power = {max_charge_total, std::make_optional<float>(max_charge_L1), max_charge_L2,
max_charge_L3};
ac_ev_power_limits.min_charge_power = {min_charge_total, std::make_optional<float>(min_charge_L1), min_charge_L2,
min_charge_L3};
ac_ev_power_limits.max_discharge_power = {max_discharge_total, std::make_optional<float>(max_discharge_L1),
max_discharge_L2, max_discharge_L3};
ac_ev_power_limits.min_discharge_power = {min_discharge_total, std::make_optional<float>(min_discharge_L1),
min_discharge_L2, min_discharge_L3};
return ac_ev_power_limits;
}
types::iso15118::AcEvPowerLimits fill_ac_ev_power_limits(const dt::Scheduled_AC_CLReqControlMode& mode) {
const auto max_charge_L1 = convert_from_optional(mode.max_charge_power);
const auto max_charge_L2 = convert_from_optional(mode.max_charge_power_L2);
const auto max_charge_L3 = convert_from_optional(mode.max_charge_power_L3);
const auto max_charge_total =
max_charge_L1.value_or(0.0) + max_charge_L2.value_or(0.0) + max_charge_L3.value_or(0.0);
const auto min_charge_L1 = convert_from_optional(mode.min_charge_power);
const auto min_charge_L2 = convert_from_optional(mode.min_charge_power_L2);
const auto min_charge_L3 = convert_from_optional(mode.min_charge_power_L3);
const auto min_charge_total =
min_charge_L1.value_or(0.0) + min_charge_L2.value_or(0.0) + min_charge_L3.value_or(0.0);
types::iso15118::AcEvPowerLimits ac_ev_power_limits;
ac_ev_power_limits.max_charge_power =
(max_charge_L1.has_value() or max_charge_L2.has_value() or max_charge_L3.has_value())
? std::make_optional<types::units::Power>({max_charge_total, max_charge_L1, max_charge_L2, max_charge_L3})
: std::nullopt;
ac_ev_power_limits.min_charge_power =
(max_charge_L1.has_value() or max_charge_L2.has_value() or max_charge_L3.has_value())
? std::make_optional<types::units::Power>({min_charge_total, min_charge_L1, min_charge_L2, min_charge_L3})
: std::nullopt;
return ac_ev_power_limits;
}
types::iso15118::AcEvPowerLimits fill_ac_ev_power_limits(const dt::BPT_Scheduled_AC_CLReqControlMode& mode) {
const auto max_charge_L1 = convert_from_optional(mode.max_charge_power);
const auto max_charge_L2 = convert_from_optional(mode.max_charge_power_L2);
const auto max_charge_L3 = convert_from_optional(mode.max_charge_power_L3);
const auto max_charge_total =
max_charge_L1.value_or(0.0) + max_charge_L2.value_or(0.0) + max_charge_L3.value_or(0.0);
const auto min_charge_L1 = convert_from_optional(mode.min_charge_power);
const auto min_charge_L2 = convert_from_optional(mode.min_charge_power_L2);
const auto min_charge_L3 = convert_from_optional(mode.min_charge_power_L3);
const auto min_charge_total =
min_charge_L1.value_or(0.0) + min_charge_L2.value_or(0.0) + min_charge_L3.value_or(0.0);
const auto max_discharge_L1 = convert_from_optional(mode.max_discharge_power);
const auto max_discharge_L2 = convert_from_optional(mode.max_discharge_power_L2);
const auto max_discharge_L3 = convert_from_optional(mode.max_discharge_power_L3);
const auto max_discharge_total =
max_discharge_L1.value_or(0.0) + max_discharge_L2.value_or(0.0) + max_discharge_L3.value_or(0.0);
const auto min_discharge_L1 = convert_from_optional(mode.min_discharge_power);
const auto min_discharge_L2 = convert_from_optional(mode.min_discharge_power_L2);
const auto min_discharge_L3 = convert_from_optional(mode.min_discharge_power_L3);
const auto min_discharge_total =
min_discharge_L1.value_or(0.0) + min_discharge_L2.value_or(0.0) + min_discharge_L3.value_or(0.0);
types::iso15118::AcEvPowerLimits ac_ev_power_limits;
ac_ev_power_limits.max_charge_power =
(max_charge_L1.has_value() or max_charge_L2.has_value() or max_charge_L3.has_value())
? std::make_optional<types::units::Power>({max_charge_total, max_charge_L1, max_charge_L2, max_charge_L3})
: std::nullopt;
ac_ev_power_limits.min_charge_power =
(min_charge_L1.has_value() or min_charge_L2.has_value() or min_charge_L3.has_value())
? std::make_optional<types::units::Power>({min_charge_total, min_charge_L1, min_charge_L2, min_charge_L3})
: std::nullopt;
ac_ev_power_limits.max_discharge_power =
(max_discharge_L1.has_value() or max_discharge_L2.has_value() or max_discharge_L3.has_value())
? std::make_optional<types::units::Power>(
{max_discharge_total, max_discharge_L1, max_discharge_L2, max_discharge_L3})
: std::nullopt;
ac_ev_power_limits.min_discharge_power =
(min_discharge_L1.has_value() or min_discharge_L2.has_value() or min_discharge_L3.has_value())
? std::make_optional<types::units::Power>(
{min_discharge_total, min_discharge_L1, min_discharge_L2, min_discharge_L3})
: std::nullopt;
return ac_ev_power_limits;
}
types::iso15118::AcEvPresentPowerValues fill_ac_ev_present_power_values(const dt::Dynamic_AC_CLReqControlMode& mode) {
types::iso15118::AcEvPresentPowerValues present_values{};
const auto present_active_power_L1 = dt::from_RationalNumber(mode.present_active_power);
const auto present_active_power_L2 = convert_from_optional(mode.present_active_power_L2);
const auto present_active_power_L3 = convert_from_optional(mode.present_active_power_L3);
const auto present_active_power_total =
present_active_power_L1 + present_active_power_L2.value_or(0.0) + present_active_power_L3.value_or(0.0);
const auto present_reactive_power_L1 = dt::from_RationalNumber(mode.present_reactive_power);
const auto present_reactive_power_L2 = convert_from_optional(mode.present_reactive_power_L2);
const auto present_reactive_power_L3 = convert_from_optional(mode.present_reactive_power_L3);
const auto present_reactive_power_total =
present_reactive_power_L1 + present_reactive_power_L2.value_or(0.0) + present_reactive_power_L3.value_or(0.0);
present_values.present_active_power = {present_active_power_total, present_active_power_L1, present_active_power_L2,
present_active_power_L3};
present_values.present_reactive_power = {present_reactive_power_total, present_reactive_power_L1,
present_reactive_power_L2, present_reactive_power_L3};
return present_values;
}
types::iso15118::AcEvPresentPowerValues fill_ac_ev_present_power_values(const dt::Scheduled_AC_CLReqControlMode& mode) {
types::iso15118::AcEvPresentPowerValues present_values{};
const auto present_active_power_L1 = dt::from_RationalNumber(mode.present_active_power);
const auto present_active_power_L2 = convert_from_optional(mode.present_active_power_L2);
const auto present_active_power_L3 = convert_from_optional(mode.present_active_power_L3);
const auto present_active_power_total =
present_active_power_L1 + present_active_power_L2.value_or(0.0) + present_active_power_L3.value_or(0.0);
const auto present_reactive_power_L1 = convert_from_optional(mode.present_reactive_power);
const auto present_reactive_power_L2 = convert_from_optional(mode.present_reactive_power_L2);
const auto present_reactive_power_L3 = convert_from_optional(mode.present_reactive_power_L3);
const auto present_reactive_power_total = present_reactive_power_L1.value_or(0.0) +
present_reactive_power_L2.value_or(0.0) +
present_reactive_power_L3.value_or(0.0);
present_values.present_active_power = {present_active_power_total, present_active_power_L1, present_active_power_L2,
present_active_power_L3};
present_values.present_reactive_power =
(present_reactive_power_L1.has_value() or present_reactive_power_L2.has_value() or
present_reactive_power_L3.has_value())
? std::make_optional<types::units::Power>({present_reactive_power_total, present_reactive_power_L1,
present_reactive_power_L2, present_reactive_power_L3})
: std::nullopt;
return present_values;
}
} // namespace module::charger

View File

@@ -0,0 +1,152 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
#include <optional>
#include <vector>
#include <generated/types/iso15118.hpp>
#include <generated/types/iso15118_vas.hpp>
#include <iso15118/d20/ev_information.hpp>
#include <iso15118/message/ac_charge_loop.hpp>
#include <iso15118/message/ac_charge_parameter_discovery.hpp>
#include <iso15118/message/dc_charge_loop.hpp>
#include <iso15118/message/service_detail.hpp>
#include <iso15118/message/type.hpp>
#include <everest/util/vector/fixed_vector.hpp>
static constexpr auto NUMBER_OF_SETUP_STEPS = 5;
namespace module::charger {
namespace dt = iso15118::message_20::datatypes;
enum class SetupStep : std::uint8_t {
SETUP,
ENERGY_SERVICE,
AUTH_SETUP,
MAX_LIMITS,
MIN_LIMITS,
};
template <typename T> constexpr auto to_underlying_value(T t) {
return static_cast<std::underlying_type_t<T>>(t);
}
static_assert(NUMBER_OF_SETUP_STEPS == to_underlying_value(SetupStep::MIN_LIMITS) + 1,
"NUMBER_OF_SETUP_STEPS should be in sync with the SetupStep enum definition");
constexpr types::iso15118::V2gMessageId convert_v2g_message_type(iso15118::message_20::Type type) {
using Type = iso15118::message_20::Type;
using Id = types::iso15118::V2gMessageId;
switch (type) {
case Type::None:
return Id::UnknownMessage;
case Type::SupportedAppProtocolReq:
return Id::SupportedAppProtocolReq;
case Type::SupportedAppProtocolRes:
return Id::SupportedAppProtocolRes;
case Type::SessionSetupReq:
return Id::SessionSetupReq;
case Type::SessionSetupRes:
return Id::SessionSetupRes;
case Type::AuthorizationSetupReq:
return Id::AuthorizationSetupReq;
case Type::AuthorizationSetupRes:
return Id::AuthorizationSetupRes;
case Type::AuthorizationReq:
return Id::AuthorizationReq;
case Type::AuthorizationRes:
return Id::AuthorizationRes;
case Type::ServiceDiscoveryReq:
return Id::ServiceDiscoveryReq;
case Type::ServiceDiscoveryRes:
return Id::ServiceDiscoveryRes;
case Type::ServiceDetailReq:
return Id::ServiceDetailReq;
case Type::ServiceDetailRes:
return Id::ServiceDetailRes;
case Type::ServiceSelectionReq:
return Id::ServiceSelectionReq;
case Type::ServiceSelectionRes:
return Id::ServiceSelectionRes;
case Type::DC_ChargeParameterDiscoveryReq:
return Id::DcChargeParameterDiscoveryReq;
case Type::DC_ChargeParameterDiscoveryRes:
return Id::DcChargeParameterDiscoveryRes;
case Type::ScheduleExchangeReq:
return Id::ScheduleExchangeReq;
case Type::ScheduleExchangeRes:
return Id::ScheduleExchangeRes;
case Type::DC_CableCheckReq:
return Id::DcCableCheckReq;
case Type::DC_CableCheckRes:
return Id::DcCableCheckRes;
case Type::DC_PreChargeReq:
return Id::DcPreChargeReq;
case Type::DC_PreChargeRes:
return Id::DcPreChargeRes;
case Type::PowerDeliveryReq:
return Id::PowerDeliveryReq;
case Type::PowerDeliveryRes:
return Id::PowerDeliveryRes;
case Type::DC_ChargeLoopReq:
return Id::DcChargeLoopReq;
case Type::DC_ChargeLoopRes:
return Id::DcChargeLoopRes;
case Type::DC_WeldingDetectionReq:
return Id::DcWeldingDetectionReq;
case Type::DC_WeldingDetectionRes:
return Id::DcWeldingDetectionRes;
case Type::SessionStopReq:
return Id::SessionStopReq;
case Type::SessionStopRes:
return Id::SessionStopRes;
case Type::AC_ChargeParameterDiscoveryReq:
return Id::AcChargeParameterDiscoveryReq;
case Type::AC_ChargeParameterDiscoveryRes:
return Id::AcChargeParameterDiscoveryRes;
case Type::AC_ChargeLoopReq:
return Id::AcChargeLoopReq;
case Type::AC_ChargeLoopRes:
return Id::AcChargeLoopRes;
}
return Id::UnknownMessage;
}
std::optional<float> convert_from_optional(const std::optional<dt::RationalNumber>& in);
std::optional<dt::RationalNumber> convert_from_optional(const std::optional<float>& in);
std::optional<float> convert_from_optional(const std::optional<uint32_t>& in);
types::iso15118::AppProtocol convert_app_protocol(const iso15118::message_20::SupportedAppProtocol& app_protocol);
types::iso15118::EvInformation convert_ev_info(const iso15118::d20::EVInformation& ev_info);
types::iso15118::DcChargeDynamicModeValues convert_dynamic_values(const dt::Dynamic_DC_CLReqControlMode& in);
types::iso15118::DcChargeDynamicModeValues convert_dynamic_values(const dt::BPT_Dynamic_DC_CLReqControlMode& in);
template <typename EVSE, typename EV>
void fill_v2x_charging_parameters(types::iso15118::V2XChargingParameters& out_params, const EVSE& evse_limits,
const EV& ev_limits);
template <typename In>
void fill_v2x_charging_parameters(types::iso15118::V2XChargingParameters& out_params, const In& data);
everest::lib::util::fixed_vector<dt::ParameterSet, 32>
convert_parameter_set_list(const std::vector<types::iso15118_vas::ParameterSet>& parameter_set_list);
types::iso15118::AcEvPowerLimits fill_ac_ev_power_limits(const dt::AC_CPDReqEnergyTransferMode& mode);
types::iso15118::AcEvPowerLimits fill_ac_ev_power_limits(const dt::BPT_AC_CPDReqEnergyTransferMode& mode);
types::iso15118::AcEvPowerLimits fill_ac_ev_power_limits(const dt::Dynamic_AC_CLReqControlMode& mode);
types::iso15118::AcEvPowerLimits fill_ac_ev_power_limits(const dt::BPT_Dynamic_AC_CLReqControlMode& mode);
types::iso15118::AcEvPowerLimits fill_ac_ev_power_limits(const dt::Scheduled_AC_CLReqControlMode& mode);
types::iso15118::AcEvPowerLimits fill_ac_ev_power_limits(const dt::BPT_Scheduled_AC_CLReqControlMode& mode);
types::iso15118::AcEvPresentPowerValues fill_ac_ev_present_power_values(const dt::Dynamic_AC_CLReqControlMode& mode);
types::iso15118::AcEvPresentPowerValues fill_ac_ev_present_power_values(const dt::Scheduled_AC_CLReqControlMode& mode);
} // namespace module::charger