Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 Pionix GmbH and Contributors to EVerest
#pragma once

#include <iso15118/message/d2/msg_data_types.hpp>

#include <optional>
#include <variant>

Check warning on line 8 in lib/everest/iso15118/include/iso15118/message/d2/charge_parameter_discovery.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

lib/everest/iso15118/include/iso15118/message/d2/charge_parameter_discovery.hpp#L8

Include file: <variant> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <vector>

namespace iso15118::d2::msg {

namespace data_types {
enum class EnergyTransferMode {
AcSinglePhaseCore,
AcThreePhaseCore,
DcCore,
DcExtended,
DcComboCore,
DcUnique
};

enum class CostKind {
RelativePricePercentage,
RenewableGenerationPercentage,
CarbonDioxideEmission
};

struct EvChargeParameter {
std::optional<uint32_t> departure_time; // Seconds to departure
};

struct AcEvChargeParameter : EvChargeParameter {
PhysicalValue e_amount; // Wh
PhysicalValue ev_max_voltage;
PhysicalValue ev_max_current;
PhysicalValue ev_min_current;
};

struct DcEvChargeParameter : EvChargeParameter {
DcEvStatus dc_ev_status;
PhysicalValue ev_maximum_current_limit;
std::optional<PhysicalValue> ev_maximum_power_limit;
PhysicalValue ev_maximum_voltage_limit;
std::optional<PhysicalValue> ev_energy_capacity; // Wh
std::optional<PhysicalValue> ev_energy_request; // Wh
std::optional<PercentValue> full_soc;
std::optional<PercentValue> bulk_soc;
};

struct RelativeTimeInterval {
uint32_t start; // Start in s from now
std::optional<uint32_t> duration; // Duration in s
};

struct Entry {
RelativeTimeInterval time_interval;
};

struct Cost {
uint32_t amount; // cost per kWh
std::optional<int8_t> amount_multiplier; // [-3 - 3]
CostKind cost_kind;
};

struct ConsumptionCost {
PhysicalValue start_value; // W
std::vector<Cost> cost; // [1 - 3]
};

struct SalesTariffEntry : Entry {
std::optional<uint8_t> e_price_level;
std::vector<ConsumptionCost> consumption_cost; // [1 - 3]
};
constexpr auto SalesTariffEntryConsumptionCostMaxLength = 3;

struct SalesTariff {
std::optional<std::string> id;
SAScheduleTupleID sales_tariff_id;
std::optional<std::string> sales_tariff_description; // MaxLength: 32
std::optional<uint8_t> num_e_price_levels;
std::vector<SalesTariffEntry> sales_tariff_entry; // [1 - 1024]
};
constexpr auto SalesTariffEntryMaxLength = 1024;

struct PMaxScheduleEntry : Entry {
PhysicalValue p_max; // W
};
using PMaxSchedule = std::vector<PMaxScheduleEntry>; // [1 - 1024]
constexpr auto PMaxScheduleMaxLength = 1024;

struct SaScheduleTuple {
SAScheduleTupleID sa_schedule_tuple_id;
PMaxSchedule pmax_schedule;
std::optional<SalesTariff> sales_tariff;
};
using SaSchedules = std::vector<SaScheduleTuple>; // [1 - 3]

struct AcEvseChargeParameter {
AcEvseStatus ac_evse_status;
PhysicalValue evse_nominal_voltage;
PhysicalValue evse_max_current;
};

struct DcEvseChargeParameter {
DcEvseStatus dc_evse_status;
PhysicalValue evse_maximum_current_limit;
PhysicalValue evse_maximum_power_limit;
PhysicalValue evse_maximum_voltage_limit;
PhysicalValue evse_minimum_current_limit;
PhysicalValue evse_minimum_voltage_limit;
std::optional<PhysicalValue> evse_current_regulation_tolerance;
PhysicalValue evse_peak_current_ripple;
std::optional<PhysicalValue> evse_energy_to_be_delivered;
};

} // namespace data_types

struct ChargeParameterDiscoveryRequest {
Header header;
std::optional<uint16_t> max_entries_sa_schedule_tuple;
data_types::EnergyTransferMode requested_energy_transfer_mode;
std::variant<data_types::AcEvChargeParameter, data_types::DcEvChargeParameter> ev_charge_parameter;
};

struct ChargeParameterDiscoveryResponse {
Header header;
data_types::ResponseCode response_code;
data_types::EvseProcessing evse_processing;
std::optional<data_types::SaSchedules> sa_schedule_list; // The SECC shall only omit the parameter 'SAScheduleList'
// in case EVSEProcessing is set to 'Ongoing'.
std::variant<data_types::AcEvseChargeParameter, data_types::DcEvseChargeParameter> evse_charge_parameter;
};

} // namespace iso15118::d2::msg
4 changes: 4 additions & 0 deletions lib/everest/iso15118/include/iso15118/message/d2/type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ enum class Type {
PaymentServiceSelectionRes,
AuthorizationReq,
AuthorizationRes,
ChargeParameterDiscoveryReq,
ChargeParameterDiscoveryRes,
CableCheckReq,
CableCheckRes,
PreChargeReq,
Expand Down Expand Up @@ -63,6 +65,8 @@ CREATE_TYPE_TRAIT(PaymentServiceSelectionRequest, PaymentServiceSelectionReq);
CREATE_TYPE_TRAIT(PaymentServiceSelectionResponse, PaymentServiceSelectionRes);
CREATE_TYPE_TRAIT(AuthorizationRequest, AuthorizationReq);
CREATE_TYPE_TRAIT(AuthorizationResponse, AuthorizationRes);
CREATE_TYPE_TRAIT(ChargeParameterDiscoveryRequest, ChargeParameterDiscoveryReq);
CREATE_TYPE_TRAIT(ChargeParameterDiscoveryResponse, ChargeParameterDiscoveryRes);
CREATE_TYPE_TRAIT(DC_CableCheckRequest, CableCheckReq);
CREATE_TYPE_TRAIT(DC_CableCheckResponse, CableCheckRes);
CREATE_TYPE_TRAIT(DC_PreChargeRequest, PreChargeReq);
Expand Down
1 change: 1 addition & 0 deletions lib/everest/iso15118/src/iso15118/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ target_sources(iso15118
message/session_stop.cpp

message/d2/authorization.cpp
message/d2/charge_parameter_discovery.cpp
message/d2/dc_cable_check.cpp
message/d2/dc_current_demand.cpp
message/d2/dc_pre_charge.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 Pionix GmbH and Contributors to EVerest
#include <cstddef>
#include <iso15118/message/d2/charge_parameter_discovery.hpp>

#include <iso15118/detail/variant_access.hpp>

#include <cbv2g/iso_2/iso2_msgDefDecoder.h>
#include <cbv2g/iso_2/iso2_msgDefEncoder.h>

#include <iso15118/detail/helper.hpp>
#include <variant>

Check warning on line 12 in lib/everest/iso15118/src/iso15118/message/d2/charge_parameter_discovery.cpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

lib/everest/iso15118/src/iso15118/message/d2/charge_parameter_discovery.cpp#L12

Include file: <variant> not found. Please note: Cppcheck does not need standard library headers to get proper results.

namespace iso15118::d2::msg {

template <> void convert(const struct iso2_AC_EVChargeParameterType& in, data_types::AcEvChargeParameter& out) {
CB2CPP_ASSIGN_IF_USED(in.DepartureTime, out.departure_time);
convert(in.EAmount, out.e_amount);
convert(in.EVMaxVoltage, out.ev_max_voltage);
convert(in.EVMaxCurrent, out.ev_max_current);
convert(in.EVMinCurrent, out.ev_min_current);
}

template <> void convert(const struct iso2_DC_EVChargeParameterType& in, data_types::DcEvChargeParameter& out) {
CB2CPP_ASSIGN_IF_USED(in.DepartureTime, out.departure_time);
convert(in.DC_EVStatus, out.dc_ev_status);
convert(in.EVMaximumCurrentLimit, out.ev_maximum_current_limit);
CB2CPP_CONVERT_IF_USED(in.EVMaximumPowerLimit, out.ev_maximum_power_limit);
convert(in.EVMaximumVoltageLimit, out.ev_maximum_voltage_limit);
CB2CPP_CONVERT_IF_USED(in.EVEnergyCapacity, out.ev_energy_capacity);
CB2CPP_CONVERT_IF_USED(in.EVEnergyRequest, out.ev_energy_request);
CB2CPP_ASSIGN_IF_USED(in.FullSOC, out.full_soc);
CB2CPP_ASSIGN_IF_USED(in.BulkSOC, out.bulk_soc);
}

template <> void convert(const data_types::AcEvseChargeParameter& in, struct iso2_AC_EVSEChargeParameterType& out) {
init_iso2_AC_EVSEChargeParameterType(&out);
convert(in.ac_evse_status, out.AC_EVSEStatus);
convert(in.evse_nominal_voltage, out.EVSENominalVoltage);
convert(in.evse_max_current, out.EVSEMaxCurrent);
}

template <> void convert(const data_types::DcEvseChargeParameter& in, struct iso2_DC_EVSEChargeParameterType& out) {
init_iso2_DC_EVSEChargeParameterType(&out);
convert(in.dc_evse_status, out.DC_EVSEStatus);
convert(in.evse_maximum_current_limit, out.EVSEMaximumCurrentLimit);
convert(in.evse_maximum_power_limit, out.EVSEMaximumPowerLimit);
convert(in.evse_maximum_voltage_limit, out.EVSEMaximumVoltageLimit);
convert(in.evse_minimum_current_limit, out.EVSEMinimumCurrentLimit);
convert(in.evse_minimum_voltage_limit, out.EVSEMinimumVoltageLimit);
CPP2CB_CONVERT_IF_USED(in.evse_current_regulation_tolerance, out.EVSECurrentRegulationTolerance);
convert(in.evse_peak_current_ripple, out.EVSEPeakCurrentRipple);
CPP2CB_CONVERT_IF_USED(in.evse_energy_to_be_delivered, out.EVSEEnergyToBeDelivered);
}

template <> void convert(const data_types::RelativeTimeInterval& in, struct iso2_RelativeTimeIntervalType& out) {
init_iso2_RelativeTimeIntervalType(&out);
out.start = in.start;
CPP2CB_ASSIGN_IF_USED(in.duration, out.duration);
}

template <> void convert(const data_types::ConsumptionCost& in, struct iso2_ConsumptionCostType& out) {
init_iso2_ConsumptionCostType(&out);
convert(in.start_value, out.startValue);

const auto cost_type_max_length = std::min(static_cast<size_t>(iso2_CostType_3_ARRAY_SIZE), in.cost.size());
for (size_t i = 0; i < cost_type_max_length; i++) {
const auto& entry_in = in.cost.at(i);
auto& entry_out = out.Cost.array[i];
init_iso2_CostType(&entry_out);
cb_convert_enum(entry_in.cost_kind, entry_out.costKind);
entry_out.amount = entry_in.amount;
CPP2CB_ASSIGN_IF_USED(entry_in.amount_multiplier, entry_out.amountMultiplier);
}
out.Cost.arrayLen = cost_type_max_length;
}

template <> void convert(const data_types::SalesTariffEntry& in, struct iso2_SalesTariffEntryType& out) {
init_iso2_SalesTariffEntryType(&out);
convert(in.time_interval, out.RelativeTimeInterval);
out.RelativeTimeInterval_isUsed = true;
CPP2CB_ASSIGN_IF_USED(in.e_price_level, out.EPriceLevel);

const auto consumption_cost_max_length =
std::min(static_cast<size_t>(iso2_ConsumptionCostType_3_ARRAY_SIZE), in.consumption_cost.size());
for (size_t i = 0; i < consumption_cost_max_length; i++) {
const auto& cost_in = in.consumption_cost.at(i);
auto& cost_out = out.ConsumptionCost.array[i];
convert(cost_in, cost_out);
}
out.ConsumptionCost.arrayLen = consumption_cost_max_length;
}

template <> void convert(const data_types::SaScheduleTuple& in, struct iso2_SAScheduleTupleType& out) {
init_iso2_SAScheduleTupleType(&out);
out.SAScheduleTupleID = in.sa_schedule_tuple_id;

const auto pmax_schedule_max_length =
std::min(static_cast<size_t>(iso2_PMaxScheduleEntryType_12_ARRAY_SIZE), in.pmax_schedule.size());
for (size_t i = 0; i < pmax_schedule_max_length; i++) {
const auto& entry_in = in.pmax_schedule.at(i);
auto& entry_out = out.PMaxSchedule.PMaxScheduleEntry.array[i];
init_iso2_PMaxScheduleEntryType(&entry_out);
convert(entry_in.time_interval, entry_out.RelativeTimeInterval);
entry_out.RelativeTimeInterval_isUsed = true;
convert(entry_in.p_max, entry_out.PMax);
}
out.PMaxSchedule.PMaxScheduleEntry.arrayLen = pmax_schedule_max_length;

if (in.sales_tariff.has_value()) {
init_iso2_SalesTariffType(&out.SalesTariff);
out.SalesTariff_isUsed = true;
CPP2CB_STRING_IF_USED(in.sales_tariff->id, out.SalesTariff.Id);
out.SalesTariff.SalesTariffID = in.sales_tariff->sales_tariff_id;
CPP2CB_STRING_IF_USED(in.sales_tariff->sales_tariff_description, out.SalesTariff.SalesTariffDescription);
CPP2CB_ASSIGN_IF_USED(in.sales_tariff->num_e_price_levels, out.SalesTariff.NumEPriceLevels);

const auto sales_tariff_entry_max_length = std::min(
static_cast<size_t>(iso2_SalesTariffEntryType_12_ARRAY_SIZE), in.sales_tariff->sales_tariff_entry.size());
for (size_t i = 0; i < sales_tariff_entry_max_length; i++) {
const auto& entry_in = in.sales_tariff->sales_tariff_entry.at(i);
auto& entry_out = out.SalesTariff.SalesTariffEntry.array[i];
convert(entry_in, entry_out);
}
out.SalesTariff.SalesTariffEntry.arrayLen = sales_tariff_entry_max_length;
}
}

template <> void convert(const struct iso2_ChargeParameterDiscoveryReqType& in, ChargeParameterDiscoveryRequest& out) {
CB2CPP_ASSIGN_IF_USED(in.MaxEntriesSAScheduleTuple, out.max_entries_sa_schedule_tuple);
cb_convert_enum(in.RequestedEnergyTransferMode, out.requested_energy_transfer_mode);
if (in.AC_EVChargeParameter_isUsed) {
data_types::AcEvChargeParameter param;
convert(in.AC_EVChargeParameter, param);
out.ev_charge_parameter = param;
} else if (in.DC_EVChargeParameter_isUsed) {
data_types::DcEvChargeParameter param;
convert(in.DC_EVChargeParameter, param);
out.ev_charge_parameter = param;
}
}

template <>
void insert_type(VariantAccess& va, const struct iso2_ChargeParameterDiscoveryReqType& in,
const struct iso2_MessageHeaderType& header) {
va.insert_type<ChargeParameterDiscoveryRequest>(in, header);
}

template <> void convert(const ChargeParameterDiscoveryResponse& in, struct iso2_ChargeParameterDiscoveryResType& out) {
init_iso2_ChargeParameterDiscoveryResType(&out);

cb_convert_enum(in.response_code, out.ResponseCode);
cb_convert_enum(in.evse_processing, out.EVSEProcessing);

if (in.sa_schedule_list.has_value()) {
const auto sa_schedule_list_max_length =
std::min(static_cast<size_t>(iso2_SAScheduleTupleType_3_ARRAY_SIZE), in.sa_schedule_list->size());
for (size_t i = 0; i < sa_schedule_list_max_length; i++) {
const auto& schedule_in = in.sa_schedule_list->at(i);
auto& schedule_out = out.SAScheduleList.SAScheduleTuple.array[i];
convert(schedule_in, schedule_out);
}
out.SAScheduleList.SAScheduleTuple.arrayLen = sa_schedule_list_max_length;
out.SAScheduleList_isUsed = true;
}

if (std::holds_alternative<data_types::AcEvseChargeParameter>(in.evse_charge_parameter)) {
const auto& param = std::get<data_types::AcEvseChargeParameter>(in.evse_charge_parameter);
convert(param, out.AC_EVSEChargeParameter);
CB_SET_USED(out.AC_EVSEChargeParameter);
} else if (std::holds_alternative<data_types::DcEvseChargeParameter>(in.evse_charge_parameter)) {
const auto& param = std::get<data_types::DcEvseChargeParameter>(in.evse_charge_parameter);
convert(param, out.DC_EVSEChargeParameter);
CB_SET_USED(out.DC_EVSEChargeParameter);
}
}

template <> int serialize_to_exi(const ChargeParameterDiscoveryResponse& in, exi_bitstream_t& out) {

iso2_exiDocument doc;
init_iso2_exiDocument(&doc);
init_iso2_BodyType(&doc.V2G_Message.Body);

convert(in.header, doc.V2G_Message.Header);

CB_SET_USED(doc.V2G_Message.Body.ChargeParameterDiscoveryRes);
convert(in, doc.V2G_Message.Body.ChargeParameterDiscoveryRes);

return encode_iso2_exiDocument(&out, &doc);
}

template <> size_t serialize(const ChargeParameterDiscoveryResponse& in, const io::StreamOutputView& out) {
return serialize_helper(in, out);
}

} // namespace iso15118::d2::msg
2 changes: 2 additions & 0 deletions lib/everest/iso15118/src/iso15118/message/d2/variant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ void handle_v2g(VariantAccess& va) {
insert_type(va, doc.V2G_Message.Body.AuthorizationReq, doc.V2G_Message.Header);
} else if (doc.V2G_Message.Body.CableCheckReq_isUsed) {
insert_type(va, doc.V2G_Message.Body.CableCheckReq, doc.V2G_Message.Header);
} else if (doc.V2G_Message.Body.ChargeParameterDiscoveryReq_isUsed) {
insert_type(va, doc.V2G_Message.Body.ChargeParameterDiscoveryReq, doc.V2G_Message.Header);
} else if (doc.V2G_Message.Body.CurrentDemandReq_isUsed) {
insert_type(va, doc.V2G_Message.Body.CurrentDemandReq, doc.V2G_Message.Header);
} else if (doc.V2G_Message.Body.PreChargeReq_isUsed) {
Expand Down
1 change: 1 addition & 0 deletions lib/everest/iso15118/test/exi/cb/iso2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ function(create_exi_test_target NAME)
catch_discover_tests(test_exi_d2_${NAME})
endfunction()

create_exi_test_target(charge_parameter_discovery)
create_exi_test_target(dc_cable_check)
create_exi_test_target(dc_current_demand)
create_exi_test_target(dc_pre_charge)
Expand Down
Loading
Loading