Skip to content
Draft
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
Expand Up @@ -19,6 +19,9 @@ namespace data_types {
constexpr auto SESSION_ID_LENGTH = 8;
using SESSION_ID = std::array<uint8_t, SESSION_ID_LENGTH>; // hexBinary, max length 8

using ServiceID = uint16_t;
using ParameterSetID = int16_t;

constexpr auto GEN_CHALLENGE_LENGTH = 16;
using GenChallenge = std::array<uint8_t, GEN_CHALLENGE_LENGTH>; // base64 binary
using PercentValue = uint8_t; // [0 - 100]
Expand Down Expand Up @@ -65,6 +68,11 @@ enum class FaultCode {
UnknownError,
};

enum class PaymentOption {
Contract,
ExternalPayment
};

enum class EvseProcessing {
Finished,
Ongoing,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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 <vector>

namespace iso15118::d2::msg {

namespace data_types {
struct SelectedService {
ServiceID service_id;
std::optional<ParameterSetID> parameter_set_id{std::nullopt};
};
using SelectedServiceList = std::vector<SelectedService>; // [1 - 16]
} // namespace data_types

struct PaymentServiceSelectionRequest {
Header header;
data_types::PaymentOption selected_payment_option;
data_types::SelectedServiceList selected_service_list;
};

struct PaymentServiceSelectionResponse {
Header header;
data_types::ResponseCode response_code;
};

} // namespace iso15118::d2::msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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 <string>
#include <variant>
#include <vector>

namespace iso15118::d2::msg {

namespace data_types {
struct Parameter {
std::string name;
std::variant<bool, int8_t, int16_t, int32_t, PhysicalValue, std::string> value;
};

struct ParameterSet {
ParameterSetID parameter_set_id;
std::vector<Parameter> parameter; // [1 - 16]
};
using ServiceParameterList = std::vector<ParameterSet>; // [1 - 255]
} // namespace data_types

struct ServiceDetailRequest {
Header header;
data_types::ServiceID service_id;
};

struct ServiceDetailResponse {
Header header;
data_types::ResponseCode response_code;
data_types::ServiceID service_id;
std::optional<data_types::ServiceParameterList> service_parameter_list{std::nullopt};
};

} // namespace iso15118::d2::msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Pionix GmbH and Contributors to EVerest
#pragma once

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

#include <array>
#include <ctype.h>
#include <optional>
#include <string>
#include <vector>

namespace iso15118::d2::msg {

namespace data_types {
using ServiceName = std::string; // MaxLength: 32
using ServiceScope = std::string; // MaxLength: 64
using ServiceScope = std::string; // MaxLength: 32
using PaymentOptionList = std::vector<PaymentOption>; // [1 - 2]

enum class ServiceCategory {
EvCharging,
Internet,
ContractCertificate,
OtherCustom
};

enum class EnergyTransferMode {
AC_single_phase_core,
AC_three_phase_core,
DC_core,
DC_extended,
DC_combo_core,
DC_unique
};
using SupportedEnergyTransferMode = std::vector<EnergyTransferMode>; // MaxLength: 6

struct Service {
ServiceID service_id;
std::optional<ServiceName> service_name{std::nullopt};
ServiceCategory service_category;
std::optional<ServiceScope> service_scope{std::nullopt};
bool FreeService;
};
using ServiceList = std::vector<Service>; // [1 - 8]

struct ChargeService : Service {
SupportedEnergyTransferMode supported_energy_transfer_mode;
};
} // namespace data_types

struct ServiceDiscoveryRequest {
Header header;
std::optional<data_types::ServiceScope> service_scope{std::nullopt};
std::optional<data_types::ServiceCategory> service_category{std::nullopt};
};

struct ServiceDiscoveryResponse {
Header header;
data_types::ResponseCode response_code;
data_types::PaymentOptionList payment_option_list;
data_types::ChargeService charge_service;
std::optional<data_types::ServiceList> service_list{std::nullopt};
};

} // namespace iso15118::d2::msg
12 changes: 12 additions & 0 deletions lib/everest/iso15118/include/iso15118/message/d2/type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ enum class Type {
SupportedAppProtocolRes,
SessionSetupReq,
SessionSetupRes,
ServiceDiscoveryReq,
ServiceDiscoveryRes,
ServiceDetailReq,
ServiceDetailRes,
PaymentServiceSelectionReq,
PaymentServiceSelectionRes,
AuthorizationReq,
AuthorizationRes,
CableCheckReq,
Expand Down Expand Up @@ -49,6 +55,12 @@ CREATE_TYPE_TRAIT(SupportedAppProtocolRequest, SupportedAppProtocolReq);
CREATE_TYPE_TRAIT(SupportedAppProtocolResponse, SupportedAppProtocolRes);
CREATE_TYPE_TRAIT(SessionSetupRequest, SessionSetupReq);
CREATE_TYPE_TRAIT(SessionSetupResponse, SessionSetupRes);
CREATE_TYPE_TRAIT(ServiceDiscoveryRequest, ServiceDiscoveryReq);
CREATE_TYPE_TRAIT(ServiceDiscoveryResponse, ServiceDiscoveryRes);
CREATE_TYPE_TRAIT(ServiceDetailRequest, ServiceDetailReq);
CREATE_TYPE_TRAIT(ServiceDetailResponse, ServiceDetailRes);
CREATE_TYPE_TRAIT(PaymentServiceSelectionRequest, PaymentServiceSelectionReq);
CREATE_TYPE_TRAIT(PaymentServiceSelectionResponse, PaymentServiceSelectionRes);
CREATE_TYPE_TRAIT(AuthorizationRequest, AuthorizationReq);
CREATE_TYPE_TRAIT(AuthorizationResponse, AuthorizationRes);
CREATE_TYPE_TRAIT(DC_CableCheckRequest, CableCheckReq);
Expand Down
3 changes: 3 additions & 0 deletions lib/everest/iso15118/src/iso15118/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ target_sources(iso15118
message/d2/dc_welding_detection.cpp
message/d2/variant.cpp
message/d2/msg_data_types.cpp
message/d2/payment_service_selection.cpp
message/d2/service_detail.cpp
message/d2/service_discovery.cpp
message/d2/session_setup.cpp

tbd_controller.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 Pionix GmbH and Contributors to EVerest
#include <iso15118/message/d2/payment_service_selection.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>

namespace iso15118::d2::msg {

template <> void convert(const struct iso2_PaymentServiceSelectionReqType& in, PaymentServiceSelectionRequest& out) {
cb_convert_enum(in.SelectedPaymentOption, out.selected_payment_option);

for (int i = 0; i < in.SelectedServiceList.SelectedService.arrayLen; i++) {
const auto& in_service = in.SelectedServiceList.SelectedService.array[i];
data_types::SelectedService service;
service.service_id = in_service.ServiceID;
if (in_service.ParameterSetID_isUsed) {
service.parameter_set_id = in_service.ParameterSetID;
}
out.selected_service_list.push_back(service);
}
}

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

template <> void convert(const PaymentServiceSelectionResponse& in, struct iso2_PaymentServiceSelectionResType& out) {
init_iso2_PaymentServiceSelectionResType(&out);

cb_convert_enum(in.response_code, out.ResponseCode);
}

template <> int serialize_to_exi(const PaymentServiceSelectionResponse& 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.PaymentServiceSelectionRes);
convert(in, doc.V2G_Message.Body.PaymentServiceSelectionRes);

return encode_iso2_exiDocument(&out, &doc);
}

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

} // namespace iso15118::d2::msg
100 changes: 100 additions & 0 deletions lib/everest/iso15118/src/iso15118/message/d2/service_detail.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 Pionix GmbH and Contributors to EVerest
#include <cstdint>
#include <iso15118/message/d2/service_detail.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>

namespace iso15118::d2::msg {

template <> void convert(const data_types::ServiceParameterList& in, struct iso2_ServiceParameterListType& out) {
int parameter_set_index = 0;
for (auto const& parameter_set : in) {
if (parameter_set_index >= iso2_ParameterSetType_5_ARRAY_SIZE) {
// TODO(kd): Should we raise exception here? I've seen that in -20 there is often no bounds checking at all.
// Example: src/iso15118/message/service_detail.cpp:274
break;
}

auto& out_parameter_set = out.ParameterSet.array[parameter_set_index++];
out_parameter_set.ParameterSetID = parameter_set.parameter_set_id;

int parameter_idx = 0;
for (auto const& parameter : parameter_set.parameter) {
if (parameter_idx >= iso2_ParameterType_16_ARRAY_SIZE) {
// TODO(kd): Should we raise exception here? I've seen that in -20 there is often no bounds checking at
// all. Example: src/iso15118/message/service_detail.cpp:274
break;
}
auto& out_param = out_parameter_set.Parameter.array[parameter_idx++];
CPP2CB_STRING(parameter.name, out_param.Name);

if (std::holds_alternative<bool>(parameter.value)) {
out_param.boolValue = std::get<bool>(parameter.value);
out_param.boolValue_isUsed = true;
} else if (std::holds_alternative<int8_t>(parameter.value)) {
out_param.byteValue = std::get<int8_t>(parameter.value);
out_param.byteValue_isUsed = true;
} else if (std::holds_alternative<int16_t>(parameter.value)) {
out_param.shortValue = std::get<int16_t>(parameter.value);
out_param.shortValue_isUsed = true;
} else if (std::holds_alternative<int32_t>(parameter.value)) {
out_param.intValue = std::get<int32_t>(parameter.value);
out_param.intValue_isUsed = true;
} else if (std::holds_alternative<data_types::PhysicalValue>(parameter.value)) {
convert(std::get<data_types::PhysicalValue>(parameter.value), out_param.physicalValue);
out_param.physicalValue_isUsed = true;
} else if (std::holds_alternative<std::string>(parameter.value)) {
CPP2CB_STRING(std::get<std::string>(parameter.value), out_param.stringValue);
out_param.stringValue_isUsed = true;
}
}
out_parameter_set.Parameter.arrayLen = parameter_set.parameter.size();
}
out.ParameterSet.arrayLen = in.size();
}

template <> void convert(const struct iso2_ServiceDetailReqType& in, ServiceDetailRequest& out) {
out.service_id = in.ServiceID;
}

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

template <> void convert(const ServiceDetailResponse& in, struct iso2_ServiceDetailResType& out) {
init_iso2_ServiceDetailResType(&out);

cb_convert_enum(in.response_code, out.ResponseCode);
out.ServiceID = in.service_id;

CPP2CB_CONVERT_IF_USED(in.service_parameter_list, out.ServiceParameterList);
}

template <> int serialize_to_exi(const ServiceDetailResponse& 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.ServiceDetailRes);
convert(in, doc.V2G_Message.Body.ServiceDetailRes);

return encode_iso2_exiDocument(&out, &doc);
}

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

} // namespace iso15118::d2::msg
Loading
Loading