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

View File

@@ -0,0 +1,38 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <filesystem>
#include <optional>
#include <string>
namespace iso15118::config {
enum class TlsNegotiationStrategy {
ACCEPT_CLIENT_OFFER,
ENFORCE_TLS,
ENFORCE_NO_TLS,
};
enum class CertificateBackend {
EVEREST_LAYOUT,
JOSEPPA_LAYOUT,
};
struct SSLConfig {
CertificateBackend backend{CertificateBackend::EVEREST_LAYOUT};
// Used by the JOSEPPA_LAYOUT
std::string config_string;
// Used by the EVEREST_LAYOUT
std::string path_certificate_chain;
std::string path_certificate_key;
std::optional<std::string> private_key_password{};
std::string path_certificate_v2g_root;
std::string path_certificate_mo_root;
bool enable_ssl_logging{false};
bool enable_tls_key_logging{false};
bool enforce_tls_1_3{false};
std::filesystem::path tls_key_logging_path{};
};
} // namespace iso15118::config

View File

@@ -0,0 +1,29 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <iso15118/message/common_types.hpp>
namespace iso15118::d20 {
namespace dt = message_20::datatypes;
struct AcTargetPower {
std::optional<dt::RationalNumber> target_active_power;
std::optional<dt::RationalNumber> target_active_power_L2;
std::optional<dt::RationalNumber> target_active_power_L3;
std::optional<dt::RationalNumber> target_reactive_power;
std::optional<dt::RationalNumber> target_reactive_power_L2;
std::optional<dt::RationalNumber> target_reactive_power_L3;
std::optional<dt::RationalNumber> target_frequency;
};
struct AcPresentPower {
std::optional<dt::RationalNumber> present_active_power;
std::optional<dt::RationalNumber> present_active_power_L2;
std::optional<dt::RationalNumber> present_active_power_L3;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,78 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
#include <optional>
#include <vector>
#include <iso15118/d20/limits.hpp>
#include <iso15118/message/common_types.hpp>
namespace iso15118::d20 {
struct ControlMobilityNeedsModes {
message_20::datatypes::ControlMode control_mode;
message_20::datatypes::MobilityNeedsMode mobility_mode;
};
struct AcSetupConfig {
uint32_t voltage;
std::vector<message_20::datatypes::AcConnector> connectors;
};
struct BptSetupConfig {
message_20::datatypes::BptChannel bpt_channel;
message_20::datatypes::GeneratorMode generator_mode;
std::optional<message_20::datatypes::GridCodeIslandingDetectionMethod> grid_code_detection_method;
};
struct EvseSetupConfig {
std::string evse_id;
std::vector<message_20::datatypes::ServiceCategory> supported_energy_services;
std::vector<message_20::datatypes::Authorization> authorization_services;
std::vector<uint16_t> supported_vas_services;
bool enable_certificate_install_service;
d20::DcTransferLimits dc_limits;
d20::AcTransferLimits ac_limits;
std::vector<ControlMobilityNeedsModes> control_mobility_modes;
std::optional<std::string> custom_protocol{std::nullopt};
std::optional<AcSetupConfig> ac_setup_config{std::nullopt};
std::optional<BptSetupConfig> bpt_setup_config{std::nullopt};
d20::DcTransferLimits powersupply_limits;
};
// This should only have EVSE information
struct SessionConfig {
explicit SessionConfig(EvseSetupConfig);
std::string evse_id;
bool cert_install_service;
std::vector<message_20::datatypes::Authorization> authorization_services;
std::vector<message_20::datatypes::ServiceCategory> supported_energy_transfer_services;
std::vector<std::uint16_t> supported_vas_services;
std::vector<message_20::datatypes::AcParameterList> ac_parameter_list;
std::vector<message_20::datatypes::AcBptParameterList> ac_bpt_parameter_list;
std::vector<message_20::datatypes::DcParameterList> dc_parameter_list;
std::vector<message_20::datatypes::DcBptParameterList> dc_bpt_parameter_list;
std::vector<message_20::datatypes::McsParameterList> mcs_parameter_list;
std::vector<message_20::datatypes::McsBptParameterList> mcs_bpt_parameter_list;
std::vector<message_20::datatypes::InternetParameterList> internet_parameter_list;
std::vector<message_20::datatypes::ParkingParameterList> parking_parameter_list;
DcTransferLimits dc_limits;
AcTransferLimits ac_limits;
DcTransferLimits powersupply_limits;
std::vector<ControlMobilityNeedsModes> supported_control_mobility_modes;
std::optional<std::string> custom_protocol{std::nullopt};
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,175 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <any>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <iso15118/d20/timeout.hpp>
#include <iso15118/message/payload_type.hpp>
#include <iso15118/message/variant.hpp>
#include <iso15118/session/feedback.hpp>
#include <iso15118/session/logger.hpp>
#include "config.hpp"
#include "control_event.hpp"
#include "ev_information.hpp"
#include "ev_session_info.hpp"
#include "session.hpp"
namespace iso15118::d20 {
// forward declare
class ControlEventQueue;
class MessageExchange {
public:
MessageExchange(io::StreamOutputView);
void set_request(std::unique_ptr<message_20::Variant> new_request);
std::unique_ptr<message_20::Variant> pull_request();
message_20::Type peek_request_type() const;
template <typename MessageType> void set_response(const MessageType& msg) {
response_size = message_20::serialize(msg, response);
response_available = true;
payload_type = message_20::PayloadTypeTrait<MessageType>::type;
response_type = message_20::TypeTrait<MessageType>::type;
response_message = msg;
}
template <typename Msg> std::optional<Msg> get_response() {
static_assert(message_20::TypeTrait<Msg>::type != message_20::Type::None, "Unhandled type!");
if (message_20::TypeTrait<Msg>::type != response_type) {
return std::nullopt;
}
try {
return std::any_cast<Msg>(response_message);
} catch (const std::bad_any_cast& ex) {
return std::nullopt;
}
}
std::tuple<bool, size_t, io::v2gtp::PayloadType, message_20::Type> check_and_clear_response();
bool has_response() const {
return response_available;
}
private:
// input
std::unique_ptr<message_20::Variant> request{nullptr};
// output
const io::StreamOutputView response;
size_t response_size{0};
bool response_available{false};
io::v2gtp::PayloadType payload_type;
message_20::Type response_type;
std::any response_message;
};
std::unique_ptr<MessageExchange> create_message_exchange(uint8_t* buf, const size_t len);
struct StateBase;
using BasePointerType = std::unique_ptr<StateBase>;
class Context {
public:
// FIXME (aw): bundle arguments
Context(session::feedback::Callbacks, session::SessionLogger&, d20::SessionConfig, std::optional<PauseContext>&,
const std::optional<ControlEvent>&, MessageExchange&, Timeouts&);
template <typename StateType, typename... Args> BasePointerType create_state(Args&&... args) {
return std::make_unique<StateType>(*this, std::forward<Args>(args)...);
}
std::unique_ptr<message_20::Variant> pull_request();
message_20::Type peek_request_type() const;
template <typename MessageType> void respond(const MessageType& msg) {
message_exchange.set_response(msg);
}
template <typename Msg> std::optional<Msg> get_response() {
return message_exchange.get_response<Msg>();
}
const auto& get_control_event() {
return current_control_event;
}
template <typename T> T const* get_control_event() {
if (not current_control_event.has_value()) {
return nullptr;
}
if (not std::holds_alternative<T>(*current_control_event)) {
return nullptr;
}
return &std::get<T>(*current_control_event);
}
void set_new_vehicle_cert_hash(std::optional<io::sha512_hash_t> hash) {
vehicle_cert_hash = hash;
}
auto get_new_vehicle_cert_hash() const {
return vehicle_cert_hash;
}
void start_timeout(d20::TimeoutType type, uint32_t time_ms) {
timeouts.start_timeout(type, time_ms);
}
void stop_timeout(d20::TimeoutType type) {
timeouts.stop_timeout(type);
}
d20::TimeoutType const* get_active_timeout() {
if (not current_timeout.has_value()) {
return nullptr;
}
return &current_timeout.value();
}
void set_active_timeout(TimeoutType timeout) {
current_timeout = timeout;
}
const session::Feedback feedback;
session::SessionLogger& log;
Session session;
SessionConfig session_config;
// Contains the EV received data
EVSessionInfo session_ev_info;
EVInformation ev_info;
std::optional<d20::PauseContext>& pause_ctx;
bool session_stopped{false};
bool session_paused{false};
std::optional<UpdateDynamicModeParameters> cache_dynamic_mode_parameters;
std::optional<AcTargetPower> cache_ac_target_power;
std::optional<AcPresentPower> cache_ac_present_power;
private:
const std::optional<ControlEvent>& current_control_event;
MessageExchange& message_exchange;
std::optional<io::sha512_hash_t> vehicle_cert_hash{std::nullopt};
Timeouts& timeouts;
std::optional<TimeoutType> current_timeout{std::nullopt};
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,94 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
#include <optional>
#include <variant>
#include <vector>
#include <iso15118/d20/ac_powers.hpp>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
#include <iso15118/d20/limits.hpp>
namespace iso15118::d20 {
class CableCheckFinished {
public:
explicit CableCheckFinished(bool success_) : success(success_) {
}
operator bool() const {
return success;
}
private:
bool success;
};
struct PresentVoltageCurrent {
float voltage;
float current;
};
class AuthorizationResponse {
public:
explicit AuthorizationResponse(bool authorized_) : authorized(authorized_) {
}
operator bool() const {
return authorized;
}
private:
bool authorized;
};
class StopCharging {
public:
explicit StopCharging(bool stop_) : stop(stop_) {
}
operator bool() const {
return stop;
}
private:
bool stop;
};
class PauseCharging {
public:
explicit PauseCharging(bool pause_) : pause(pause_) {
}
operator bool() const {
return pause;
}
private:
bool pause;
};
using EnergyServices = std::vector<message_20::datatypes::ServiceCategory>;
class ClosedContactor {
public:
explicit ClosedContactor(bool closed_) : closed(closed_) {
}
operator bool() const {
return closed;
}
private:
bool closed;
};
// TODO(SL): Define this globally for message and states
using SupportedVASs = std::vector<uint16_t>;
using ControlEvent = std::variant<CableCheckFinished, PresentVoltageCurrent, AuthorizationResponse, StopCharging,
PauseCharging, DcTransferLimits, AcTransferLimits, UpdateDynamicModeParameters,
ClosedContactor, AcTargetPower, AcPresentPower, EnergyServices, SupportedVASs>;
} // namespace iso15118::d20

View File

@@ -0,0 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <mutex>
#include <optional>
#include <queue>
#include "control_event.hpp"
namespace iso15118::d20 {
class ControlEventQueue {
public:
std::optional<ControlEvent> pop();
void push(ControlEvent);
private:
std::queue<ControlEvent> queue;
std::mutex mutex;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,17 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
#include <ctime>
#include <optional>
namespace iso15118::d20 {
struct UpdateDynamicModeParameters {
std::optional<std::time_t> departure_time;
std::optional<std::uint8_t> target_soc;
std::optional<std::uint8_t> min_soc;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/message/supported_app_protocol.hpp>
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::d20 {
using EVSupportedAppProtocols = everest::lib::util::fixed_vector<message_20::SupportedAppProtocol, 20>;
// Holds information about the EV
struct EVInformation {
EVSupportedAppProtocols ev_supported_app_protocols;
message_20::SupportedAppProtocol selected_app_protocol;
std::string evcc_id;
std::optional<std::string> ev_tls_leaf_cert;
std::optional<std::string> ev_tls_sub_ca_1_cert;
std::optional<std::string> ev_tls_sub_ca_2_cert;
std::optional<std::string> ev_tls_root_cert;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,17 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/message/common_types.hpp>
#include <iso15118/session/feedback.hpp>
namespace iso15118::d20 {
// Holds information reported by the EV
struct EVSessionInfo {
session::feedback::EvTransferLimits ev_transfer_limits;
session::feedback::EvSEControlMode ev_control_mode;
std::vector<message_20::datatypes::ServiceCategory> ev_energy_services;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,44 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <iso15118/message/common_types.hpp>
namespace iso15118::d20 {
namespace dt = message_20::datatypes;
template <typename T> struct Limit {
T max;
T min;
};
struct Limits {
Limit<message_20::datatypes::RationalNumber> power;
Limit<message_20::datatypes::RationalNumber> current;
};
struct DcTransferLimits {
Limits charge_limits;
std::optional<Limits> discharge_limits;
Limit<message_20::datatypes::RationalNumber> voltage;
std::optional<message_20::datatypes::RationalNumber> power_ramp_limit;
};
struct AcTransferLimits {
Limit<dt::RationalNumber> charge_power;
std::optional<Limit<dt::RationalNumber>> charge_power_L2;
std::optional<Limit<dt::RationalNumber>> charge_power_L3;
dt::RationalNumber nominal_frequency;
std::optional<dt::RationalNumber> max_power_asymmetry;
std::optional<dt::RationalNumber> power_ramp_limitation;
std::optional<Limit<dt::RationalNumber>> discharge_power;
std::optional<Limit<dt::RationalNumber>> discharge_power_L2;
std::optional<Limit<dt::RationalNumber>> discharge_power_L3;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,152 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <array>
#include <cstdint>
#include <map>
#include <optional>
#include <variant>
#include <vector>
#include <iso15118/io/sha_hash.hpp>
#include <iso15118/message/common_types.hpp>
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::d20 {
namespace dt = message_20::datatypes;
// Key: service IDs, value: vector with the parameter set ids
using CustomVasList = std::map<std::uint16_t, std::vector<uint16_t>>;
struct OfferedServices {
everest::lib::util::fixed_vector<dt::Authorization, 2> auth_services;
std::vector<dt::ServiceCategory> energy_services;
std::vector<uint16_t> vas_services;
std::map<uint8_t, dt::AcParameterList> ac_parameter_list;
std::map<uint8_t, dt::AcBptParameterList> ac_bpt_parameter_list;
std::map<uint8_t, dt::DcParameterList> dc_parameter_list;
std::map<uint8_t, dt::DcBptParameterList> dc_bpt_parameter_list;
std::map<uint8_t, dt::McsParameterList> mcs_parameter_list;
std::map<uint8_t, dt::McsBptParameterList> mcs_bpt_parameter_list;
std::map<uint8_t, dt::InternetParameterList> internet_parameter_list;
std::map<uint8_t, dt::ParkingParameterList> parking_parameter_list;
CustomVasList custom_vas_list;
};
struct SelectedServiceParameters {
dt::ServiceCategory selected_energy_service;
std::variant<dt::AcConnector, dt::DcConnector, dt::McsConnector> selected_connector;
dt::ControlMode selected_control_mode;
dt::MobilityNeedsMode selected_mobility_needs_mode;
dt::Pricing selected_pricing;
// BPT
std::optional<dt::BptChannel> selected_bpt_channel;
std::optional<dt::GeneratorMode> selected_generator_mode;
// AC specific
std::optional<float> evse_nominal_voltage;
std::optional<dt::GridCodeIslandingDetectionMethod> selected_grid_code_method;
SelectedServiceParameters() = default;
SelectedServiceParameters(dt::ServiceCategory energy_service_, dt::DcConnector dc_connector_,
dt::ControlMode control_mode_, dt::MobilityNeedsMode mobility_, dt::Pricing pricing_);
SelectedServiceParameters(dt::ServiceCategory energy_service_, dt::DcConnector dc_connector_,
dt::ControlMode control_mode_, dt::MobilityNeedsMode mobility_, dt::Pricing pricing_,
dt::BptChannel channel_, dt::GeneratorMode generator_);
SelectedServiceParameters(dt::ServiceCategory energy_service_, dt::McsConnector mcs_connector_,
dt::ControlMode control_mode_, dt::MobilityNeedsMode mobility_, dt::Pricing pricing_);
SelectedServiceParameters(dt::ServiceCategory energy_service_, dt::McsConnector mcs_connector_,
dt::ControlMode control_mode_, dt::MobilityNeedsMode mobility_, dt::Pricing pricing_,
dt::BptChannel channel_, dt::GeneratorMode generator_);
SelectedServiceParameters(dt::ServiceCategory energy_service_, dt::AcConnector ac_connector_,
dt::ControlMode control_mode_, dt::MobilityNeedsMode mobility_, dt::Pricing pricing_,
float nominal_voltage_);
SelectedServiceParameters(dt::ServiceCategory energy_service_, dt::AcConnector ac_connector_,
dt::ControlMode control_mode_, dt::MobilityNeedsMode mobility_, dt::Pricing pricing_,
dt::BptChannel channel_, dt::GeneratorMode generator_, float nominal_voltage_,
dt::GridCodeIslandingDetectionMethod grid_code_method_);
};
// Todo(sl): missing services
// WPT -> ControlMode, Pricing
// DC_ACDP -> ControlMode, MobilityNeedsMode
// DC_ACDP_BPT -> ControlMode, MobilityNeedsMode, BPTChannel
struct SelectedVasParameter {
std::vector<dt::ServiceCategory> vas_services;
dt::Protocol internet_protocol;
dt::Port internet_port;
dt::IntendedService parking_intended_service;
dt::ParkingStatus parking_status;
};
// TODO(SL): How to handle d2 pause? Move Struct to a seperate header file?
// TODO(SL): Missing handling scheduletuple in schedule mode [V2G20-1058]
struct PauseContext {
io::sha512_hash_t vehicle_cert_session_id_hash{};
std::array<uint8_t, 8> old_session_id{};
SelectedServiceParameters selected_service_parameters{};
};
class Session {
// TODO(sl): move to a common defs file
static constexpr auto ID_LENGTH = 8;
public:
Session();
Session(const PauseContext& pause_ctx);
Session(SelectedServiceParameters);
Session(OfferedServices);
std::array<uint8_t, ID_LENGTH> get_id() const {
return id;
}
bool find_energy_parameter_set_id(const dt::ServiceCategory service, int16_t id);
bool find_vas_parameter_set_id(const uint16_t vas_service, int16_t id);
void selected_service_parameters(const dt::ServiceCategory service, const uint16_t id);
void selected_service_parameters(const uint16_t vas_service, const uint16_t id);
auto get_selected_services() const& {
return selected_services;
}
bool is_ac_charger() const {
return selected_services.selected_energy_service == dt::ServiceCategory::AC or
selected_services.selected_energy_service == dt::ServiceCategory::AC_BPT;
}
bool is_dc_charger() const {
return selected_services.selected_energy_service == dt::ServiceCategory::DC or
selected_services.selected_energy_service == dt::ServiceCategory::DC_BPT or
selected_services.selected_energy_service == dt::ServiceCategory::MCS or
selected_services.selected_energy_service == dt::ServiceCategory::MCS_BPT;
}
~Session();
OfferedServices offered_services;
bool service_renegotiation_supported{false};
private:
// NOTE (aw): could be const
std::array<uint8_t, ID_LENGTH> id{};
SelectedServiceParameters selected_services{};
SelectedVasParameter selected_vas_services{};
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,31 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
#include <iso15118/d20/ac_powers.hpp>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
namespace iso15118::d20::state {
struct AC_ChargeLoop : public StateBase {
AC_ChargeLoop(Context& ctx) : StateBase(ctx, StateID::AC_ChargeLoop) {
}
void enter() final;
Result feed(Event) final;
private:
float target_frequency{0};
bool stop{false};
bool pause{false};
UpdateDynamicModeParameters dynamic_parameters{};
AcTargetPower target_powers{};
AcPresentPower present_powers{};
bool first_entry_in_charge_loop{false};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
#include <iso15118/d20/ac_powers.hpp>
namespace iso15118::d20::state {
struct AC_ChargeParameterDiscovery : public StateBase {
AC_ChargeParameterDiscovery(Context& ctx) : StateBase(ctx, StateID::AC_ChargeParameterDiscovery) {
}
void enter() final;
Result feed(Event) final;
private:
AcPresentPower present_powers;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
#include <iso15118/message/authorization.hpp>
namespace iso15118::d20::state {
struct Authorization : public StateBase {
public:
Authorization(Context& ctx) : StateBase(ctx, StateID::Authorization){};
void enter() final;
Result feed(Event) final;
private:
message_20::datatypes::AuthStatus authorization_status{message_20::datatypes::AuthStatus::Pending};
bool first_req_msg{true};
bool timeout_ongoing_reached{false};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct AuthorizationSetup : public StateBase {
AuthorizationSetup(Context& ctx) : StateBase(ctx, StateID::AuthorizationSetup) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct DC_CableCheck : public StateBase {
DC_CableCheck(Context& ctx) : StateBase(ctx, StateID::DC_CableCheck) {
}
void enter() final;
Result feed(Event) final;
private:
bool cable_check_initiated{false};
bool cable_check_done{false};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
#include <cstdint>
#include <optional>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
namespace iso15118::d20::state {
struct DC_ChargeLoop : public StateBase {
DC_ChargeLoop(Context& ctx) : StateBase(ctx, StateID::DC_ChargeLoop) {
}
void enter() final;
Result feed(Event) final;
private:
float present_voltage{0};
float present_current{0};
bool stop{false};
bool pause{false};
UpdateDynamicModeParameters dynamic_parameters;
bool first_entry_in_charge_loop{true};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct DC_ChargeParameterDiscovery : public StateBase {
DC_ChargeParameterDiscovery(Context& ctx) : StateBase(ctx, StateID::DC_ChargeParameterDiscovery) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct DC_PreCharge : public StateBase {
DC_PreCharge(Context& ctx) : StateBase(ctx, StateID::DC_PreCharge) {
}
void enter() final;
Result feed(Event) final;
private:
bool pre_charge_initiated{false};
float present_voltage{0};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct DC_WeldingDetection : public StateBase {
DC_WeldingDetection(Context& ctx) : StateBase(ctx, StateID::DC_WeldingDetection) {
}
void enter() final;
Result feed(Event) final;
private:
float present_voltage{0};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
#include <iso15118/message/power_delivery.hpp>
#include <optional>
namespace iso15118::d20::state {
struct PowerDelivery : public StateBase {
PowerDelivery(Context& ctx) : StateBase(ctx, StateID::PowerDelivery) {
}
void enter() final;
Result feed(Event) final;
private:
float present_voltage{0};
bool ac_connector_closed{false};
std::optional<message_20::PowerDeliveryRequest> previous_req;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,27 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
#include <cstdint>
#include <optional>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
namespace iso15118::d20::state {
struct ScheduleExchange : public StateBase {
ScheduleExchange(Context& ctx) : StateBase(ctx, StateID::ScheduleExchange) {
}
void enter() final;
Result feed(Event) final;
private:
UpdateDynamicModeParameters dynamic_parameters;
bool first_req_msg{true};
bool timeout_ongoing_reached{false};
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct ServiceDetail : public StateBase {
public:
ServiceDetail(Context& ctx) : StateBase(ctx, StateID::ServiceDetail) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct ServiceDiscovery : public StateBase {
public:
ServiceDiscovery(Context& ctx) : StateBase(ctx, StateID::ServiceDiscovery) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct ServiceSelection : public StateBase {
ServiceSelection(Context& ctx) : StateBase(ctx, StateID::ServiceSelection) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <string>
#include "../states.hpp"
namespace iso15118::d20::state {
struct SessionSetup : public StateBase {
SessionSetup(Context& ctx) : StateBase(ctx, StateID::SessionSetup) {
}
void enter() final;
Result feed(Event) final;
private:
std::string evse_id;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct SessionStop : public StateBase {
SessionStop(Context& ctx) : StateBase(ctx, StateID::SessionStop) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "../states.hpp"
namespace iso15118::d20::state {
struct SupportedAppProtocol : public StateBase {
public:
SupportedAppProtocol(Context& ctx) : StateBase(ctx, StateID::SupportedAppProtocol) {
}
void enter() final;
Result feed(Event) final;
};
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,73 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#pragma once
#include "context.hpp"
namespace iso15118::d20 {
class Context;
enum class Event {
RESET,
V2GTP_MESSAGE,
CONTROL_MESSAGE,
TIMEOUT,
// internal events
FAILED,
};
enum class StateID {
SupportedAppProtocol,
SessionSetup,
AuthorizationSetup,
Authorization,
ServiceDetail,
ServiceDiscovery,
ServiceSelection,
AC_ChargeParameterDiscovery,
AC_ChargeLoop,
DC_ChargeParameterDiscovery,
DC_PreCharge,
DC_ChargeLoop,
DC_WeldingDetection,
DC_CableCheck,
PowerDelivery,
ScheduleExchange,
SessionStop
};
struct Result {
constexpr Result() = default;
Result(BasePointerType result_state) : unhandled(false), new_state(std::move(result_state)) {
}
bool unhandled{true};
BasePointerType new_state{nullptr};
};
struct StateBase {
using ContainerType = BasePointerType;
using EventType = Event;
StateBase(Context& ctx, StateID id) : m_ctx(ctx), m_id(id){};
virtual ~StateBase() = default;
StateID get_id() const {
return m_id;
}
virtual void enter(){};
virtual Result feed(Event) = 0;
virtual void leave(){};
protected:
Context& m_ctx;
private:
StateID m_id;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,47 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 Pionix GmbH and Contributors to EVerest
#pragma once
#include <array>
#include <optional>
#include <vector>
#include <iso15118/io/time.hpp>
namespace iso15118::d20 {
template <typename T> constexpr auto to_underlying_value(T t) {
return static_cast<std::underlying_type_t<T>>(t);
}
enum class TimeoutType : uint8_t {
SEQUENCE = 0,
PERFORMANCE,
ONGOING,
CONTACTOR,
};
constexpr uint8_t TIMEOUT_TYPE_SIZE = 4;
static_assert(TIMEOUT_TYPE_SIZE == to_underlying_value(TimeoutType::CONTACTOR) + 1,
"TIMEOUT_TYPE_SIZE should be in sync with the TimeoutType enum definition");
constexpr auto TIMEOUT_ONGOING = 1000 * 55;
constexpr auto TIMEOUT_SEQUENCE = 1000 * 60;
constexpr auto TIMEOUT_EIM_ONGOING = 1000 * 60 * 3;
class Timeouts {
public:
explicit Timeouts() = default;
~Timeouts() = default;
void start_timeout(TimeoutType type, uint32_t timeout_ms);
void stop_timeout(TimeoutType type);
void reset_timeout(TimeoutType type);
std::optional<std::vector<TimeoutType>> check();
private:
std::array<std::optional<Timeout>, TIMEOUT_TYPE_SIZE> timeouts;
};
} // namespace iso15118::d20

View File

@@ -0,0 +1,99 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <stdexcept>
#include <cbv2g/common/exi_bitstream.h>
#include <iso15118/io/stream_view.hpp>
#define CB2CPP_STRING(property) (std::string(property.characters, property.charactersLen))
#define CPP2CB_STRING(in, out) \
if (in.length() > sizeof(out.characters)) { \
throw std::runtime_error("String too long"); \
} \
in.copy(out.characters, in.length()); \
out.charactersLen = in.length()
#define CPP2CB_BYTES(in, out) \
if (in.size() > sizeof(out.bytes)) { \
throw std::runtime_error("Byte vector too long"); \
} \
std::copy(in.begin(), in.end(), out.bytes); \
out.bytesLen = in.size()
#define CB2CPP_BYTES(in, out) \
if (in.bytesLen > out.size()) { \
throw std::runtime_error("Byte array too long"); \
} \
std::copy(std::begin(in.bytes), std::end(in.bytes), out.begin());
#define CB_SET_USED(property) (property##_isUsed = 1)
#define CB2CPP_ASSIGN_IF_USED(in, out) \
if (in##_isUsed) { \
out = in; \
}
#define CPP2CB_ASSIGN_IF_USED(in, out) \
if (in) { \
out = in.value(); \
} \
\
out##_isUsed = static_cast<bool>(in);
#define CB2CPP_CONVERT_IF_USED(in, out) \
if (in##_isUsed) { \
convert(in, out.emplace()); \
}
#define CPP2CB_CONVERT_IF_USED(in, out) \
if (in) { \
convert(in.value(), out); \
} \
\
out##_isUsed = static_cast<bool>(in);
#define CPP2CB_STRING_IF_USED(in, out) \
if (in) { \
CPP2CB_STRING(in.value(), out); \
} \
\
out##_isUsed = static_cast<bool>(in);
#define CB2CPP_STRING_IF_USED(in, out) \
if (in##_isUsed) { \
out = CB2CPP_STRING(in); \
}
#define CB2CPP_BYTES_IF_USED(in, out) \
if (in##_isUsed) { \
CB2CPP_BYTES(in, out.emplace()); \
}
template <typename T1, typename T2> void cb_convert_enum(const T1& in, T2& out) {
out = static_cast<T2>(in);
}
exi_bitstream_t get_exi_input_stream(const iso15118::io::StreamInputView&);
exi_bitstream_t get_exi_output_stream(const iso15118::io::StreamOutputView&);
namespace iso15118::message_20 {
template <typename MessageType> int serialize_to_exi(const MessageType& in, exi_bitstream_t& out);
template <typename MessageType>
size_t serialize_helper(const MessageType& in, const io::StreamOutputView& stream_view) {
auto out = get_exi_output_stream(stream_view);
const auto error = serialize_to_exi(in, out);
if (error != 0) {
throw std::runtime_error("Could not encode exi: " + std::to_string(error));
}
return exi_bitstream_get_length(&out);
}
} // namespace iso15118::message_20

View File

@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/context.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/common_types.hpp>
namespace iso15118::d20 {
// FIXME (aw): not sure about correct signature here for RVO
template <typename Response, typename ResponseCode> Response& response_with_code(Response& res, ResponseCode code) {
// FIXME (aw): could add an static_assert here that ResponseCode is an enum?
res.response_code = code;
return res;
}
bool validate_and_setup_header(message_20::Header&, const Session&, const decltype(message_20::Header::session_id)&);
void setup_header(message_20::Header&, const Session&);
void send_sequence_error(const message_20::Type, d20::Context&);
} // namespace iso15118::d20

View File

@@ -0,0 +1,20 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 205 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/ac_charge_loop.hpp>
#include <iso15118/d20/ac_powers.hpp>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
#include <iso15118/d20/limits.hpp>
namespace iso15118::d20::state {
message_20::AC_ChargeLoopResponse handle_request(const message_20::AC_ChargeLoopRequest& req,
const d20::Session& session, bool stop, bool pause,
float target_frequency, const AcTargetPower& target_powers,
const AcPresentPower& present_powers,
const UpdateDynamicModeParameters& dynamic_parameters);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/ac_powers.hpp>
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/ac_charge_parameter_discovery.hpp>
namespace iso15118::d20::state {
message_20::AC_ChargeParameterDiscoveryResponse
handle_request(const message_20::AC_ChargeParameterDiscoveryRequest& req, const d20::Session& session,
const d20::AcTransferLimits& limits, const d20::AcPresentPower& powers);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/authorization.hpp>
namespace iso15118::d20::state {
message_20::AuthorizationResponse handle_request(const message_20::AuthorizationRequest& req,
const d20::Session& session,
const message_20::datatypes::AuthStatus& authorization_status,
bool timeout_reached);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/authorization_setup.hpp>
namespace iso15118::d20::state {
message_20::AuthorizationSetupResponse
handle_request(const message_20::AuthorizationSetupRequest& req, d20::Session& session, bool cert_install_service,
const std::vector<message_20::datatypes::Authorization>& authorization_services);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/dc_cable_check.hpp>
namespace iso15118::d20::state {
message_20::DC_CableCheckResponse handle_request(const message_20::DC_CableCheckRequest& req,
const d20::Session& session, bool cable_check_done);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
#include <ctime>
#include <optional>
#include <tuple>
#include <variant>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
#include <iso15118/d20/limits.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/dc_charge_loop.hpp>
#include <iso15118/session/feedback.hpp>
namespace iso15118::d20::state {
message_20::DC_ChargeLoopResponse handle_request(const message_20::DC_ChargeLoopRequest& req,
const d20::Session& session, const float present_voltage,
const float present_current, const bool stop, const bool pause,
const DcTransferLimits& dc_limits,
const UpdateDynamicModeParameters& dynamic_parameters);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/limits.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/dc_charge_parameter_discovery.hpp>
namespace iso15118::d20::state {
message_20::DC_ChargeParameterDiscoveryResponse
handle_request(const message_20::DC_ChargeParameterDiscoveryRequest& req, const d20::Session& session,
const d20::DcTransferLimits& dc_limits);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <tuple>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/dc_pre_charge.hpp>
#include <iso15118/session/feedback.hpp>
namespace iso15118::d20::state {
message_20::DC_PreChargeResponse handle_request(const message_20::DC_PreChargeRequest& req, const d20::Session& session,
const float present_voltage);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/dc_welding_detection.hpp>
namespace iso15118::d20::state {
message_20::DC_WeldingDetectionResponse handle_request(const message_20::DC_WeldingDetectionRequest& req,
const d20::Session& session, const float present_voltage);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/power_delivery.hpp>
namespace iso15118::d20::state {
message_20::PowerDeliveryResponse handle_request(const message_20::PowerDeliveryRequest& req,
const d20::Session& session, bool contactor_error);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/schedule_exchange.hpp>
#include <cstdint>
#include <ctime>
#include <optional>
#include <iso15118/d20/dynamic_mode_parameters.hpp>
namespace iso15118::d20::state {
message_20::ScheduleExchangeResponse handle_request(const message_20::ScheduleExchangeRequest& req,
const d20::Session& session,
const message_20::datatypes::RationalNumber& max_power,
const UpdateDynamicModeParameters& dynamic_parameters,
bool timeout_reached);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/service_detail.hpp>
namespace iso15118::d20::state {
message_20::ServiceDetailResponse handle_request(const message_20::ServiceDetailRequest& req, d20::Session& session,
const d20::SessionConfig& config,
const std::optional<dt::ServiceParameterList>& custom_vas_parameters);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,20 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <vector>
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/common_types.hpp>
#include <iso15118/message/service_discovery.hpp>
namespace iso15118::d20::state {
message_20::ServiceDiscoveryResponse
handle_request(const message_20::ServiceDiscoveryRequest& req, d20::Session& session,
const std::vector<message_20::datatypes::ServiceCategory>& energy_services,
const std::vector<uint16_t>& vas_services,
std::vector<message_20::datatypes::ServiceCategory>& ev_energy_services);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/session.hpp>
#include <iso15118/message/service_selection.hpp>
namespace iso15118::d20::state {
message_20::ServiceSelectionResponse handle_request(const message_20::ServiceSelectionRequest& req,
d20::Session& session);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/session_setup.hpp>
namespace iso15118::d20::state {
message_20::SessionSetupResponse handle_request(const message_20::SessionSetupRequest& req, const d20::Session& session,
const std::string& evse_id, bool new_session);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/d20/session.hpp>
#include <iso15118/message/session_stop.hpp>
namespace iso15118::d20::state {
message_20::SessionStopResponse handle_request(const message_20::SessionStopRequest& req, const d20::Session& session);
} // namespace iso15118::d20::state

View File

@@ -0,0 +1,52 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/io/log_levels.hpp>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <functional>
#include <string>
namespace iso15118 {
void logf(const char* fmt, ...);
void logf(const LogLevel&, const char* fmt, ...);
void logf_error(const char* fmt, ...);
void logf_warning(const char* fmt, ...);
void logf_info(const char* fmt, ...);
void logf_debug(const char* fmt, ...);
void logf_trace(const char* fmt, ...);
void vlogf(const char* fmt, va_list ap);
void vlogf(const LogLevel&, const char* fmt, va_list ap);
void log(const LogLevel&, const std::string&);
void log_and_throw(const char* msg);
std::string adding_err_msg(const std::string& msg);
template <typename CallbackType, typename... Args> bool call_if_available(const CallbackType& callback, Args... args) {
if (not callback) {
return false;
}
std::invoke(callback, std::forward<Args>(args)...);
return true;
}
// Note(sl): Copied from https://en.cppreference.com/w/cpp/utility/intcmp because libiso15118 uses C++17
template <class T, class U> constexpr bool cmp_equal(T t, U u) noexcept {
if constexpr (std::is_signed_v<T> == std::is_signed_v<U>)
return t == u;
else if constexpr (std::is_signed_v<T>)
return t >= 0 && std::make_unsigned_t<T>(t) == u;
else
return u >= 0 && std::make_unsigned_t<U>(u) == t;
}
} // namespace iso15118

View File

@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <string>
namespace iso15118::io {
std::string log_openssl_error(const std::string& error_msg);
void log_and_raise_openssl_error(const std::string& error_msg);
} // namespace iso15118::io

View File

@@ -0,0 +1,17 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <memory>
#include <string>
#include <netinet/in.h>
namespace iso15118::io {
bool check_and_update_interface(std::string& interface_name);
bool get_first_sockaddr_in6_for_interface(const std::string& interface_name, sockaddr_in6& address);
std::unique_ptr<char[]> sockaddr_in6_to_name(const sockaddr_in6&);
} // namespace iso15118::io

View File

@@ -0,0 +1,36 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cassert>
#include <iso15118/message/variant.hpp>
#include "cb_exi.hpp"
namespace iso15118::message_20 {
struct VariantAccess {
// input
exi_bitstream_t input_stream;
// output
void*& data;
iso15118::message_20::Type& type;
iso15118::message_20::Variant::CustomDeleter& custom_deleter;
std::string& error;
template <typename MessageType, typename CbExiMessageType> void insert_type(const CbExiMessageType& in) {
assert(data == nullptr);
data = new MessageType;
type = iso15118::message_20::TypeTrait<MessageType>::type;
custom_deleter = [](void* ptr) { delete static_cast<MessageType*>(ptr); };
convert(in, *static_cast<MessageType*>(data));
};
};
template <typename CbExiMessageType> void insert_type(VariantAccess& va, const CbExiMessageType&);
} // namespace iso15118::message_20

View File

@@ -0,0 +1,43 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstddef>
#include <functional>
#include <optional>
#include "ipv6_endpoint.hpp"
#include <iso15118/io/sha_hash.hpp>
namespace iso15118::io {
enum class ConnectionEvent {
ACCEPTED,
OPEN,
NEW_DATA,
CLOSED,
};
using ConnectionEventCallback = std::function<void(ConnectionEvent)>;
struct ReadResult {
bool would_block{true};
size_t bytes_read{0};
};
struct IConnection {
virtual void set_event_callback(const ConnectionEventCallback&) = 0;
virtual Ipv6EndPoint get_public_endpoint() const = 0;
virtual void write(const uint8_t* buf, size_t len) = 0;
virtual ReadResult read(uint8_t* buf, size_t len) = 0;
virtual void close() = 0;
virtual std::optional<sha512_hash_t> get_vehicle_cert_hash() const = 0;
virtual ~IConnection() = default;
};
} // namespace iso15118::io

View File

@@ -0,0 +1,44 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "connection_abstract.hpp"
#include <iso15118/config.hpp>
#include <iso15118/io/poll_manager.hpp>
namespace iso15118::io {
class ConnectionPlain : public IConnection {
public:
ConnectionPlain(PollManager&, const std::string& interface_name);
void set_event_callback(const ConnectionEventCallback&) final;
Ipv6EndPoint get_public_endpoint() const final;
void write(const uint8_t* buf, size_t len) final;
ReadResult read(uint8_t* buf, size_t len) final;
void close() final;
std::optional<sha512_hash_t> get_vehicle_cert_hash() const final {
return std::nullopt;
}
~ConnectionPlain();
private:
PollManager& poll_manager;
Ipv6EndPoint end_point;
int fd{-1};
bool connection_open{false};
ConnectionEventCallback event_callback{nullptr};
void handle_connect();
void handle_data();
};
} // namespace iso15118::io

View File

@@ -0,0 +1,46 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include "connection_abstract.hpp"
#include <memory>
#include <optional>
#include <iso15118/config.hpp>
#include <iso15118/io/poll_manager.hpp>
#include <iso15118/io/sha_hash.hpp>
namespace iso15118::io {
// forward declaration
struct SSLContext;
class ConnectionSSL : public IConnection {
public:
ConnectionSSL(PollManager&, const std::string& interface_name, const config::SSLConfig&);
void set_event_callback(const ConnectionEventCallback&) final;
Ipv6EndPoint get_public_endpoint() const final;
void write(const uint8_t* buf, size_t len) final;
ReadResult read(uint8_t* buf, size_t len) final;
void close() final;
std::optional<sha512_hash_t> get_vehicle_cert_hash() const final;
~ConnectionSSL();
private:
PollManager& poll_manager;
std::unique_ptr<SSLContext> ssl;
Ipv6EndPoint end_point;
ConnectionEventCallback event_callback{nullptr};
bool handshake_complete{false};
void handle_connect();
void handle_data();
};
} // namespace iso15118::io

View File

@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
namespace iso15118::io {
struct Ipv6EndPoint {
uint16_t port;
uint16_t address[8];
};
} // namespace iso15118::io

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
namespace iso15118 {
enum class LogLevel {
Error,
Warning,
Info,
Debug,
Trace,
};
} // namespace iso15118

View File

@@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <functional>
#include <string>
#include "log_levels.hpp"
namespace iso15118::io {
void set_logging_callback(const std::function<void(LogLevel, std::string)>&);
} // namespace iso15118::io

View File

@@ -0,0 +1,36 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <functional>
#include <map>
#include <vector>
#include <poll.h>
namespace iso15118::io {
using PollCallback = const std::function<void()>;
struct PollSet {
std::vector<struct pollfd> fds;
std::vector<PollCallback*> callbacks;
};
class PollManager {
public:
PollManager();
void register_fd(int fd, PollCallback& poll_callback);
void unregister_fd(int fd);
void poll(int timeout_ms);
void abort();
private:
std::map<int, PollCallback> registered_fds;
PollSet poll_set;
int event_fd{-1};
};
} // namespace iso15118::io

View File

@@ -0,0 +1,27 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstdint>
namespace iso15118::io::v2gtp {
enum class Security : uint8_t {
TLS = 0x00,
NO_TRANSPORT_SECURITY = 0x10,
};
enum class TransportProtocol : uint8_t {
TCP = 0x00,
RESERVED_FOR_UDP = 0x10,
};
static constexpr auto SDP_SERVER_PORT = 15118;
enum class PayloadType : uint16_t {
SAP = 0x8001,
Part20Main = 0x8002,
Part20AC = 0x8003,
Part20DC = 0x8004,
};
} // namespace iso15118::io::v2gtp

View File

@@ -0,0 +1,73 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstddef>
#include <cstdint>
#include "sdp.hpp"
namespace iso15118::io {
// FIXME (aw): these shouldn't be necessary public - but nice to have
static constexpr uint8_t SDP_PROTOCOL_VERSION = 0x01;
static constexpr uint8_t SDP_INVERSE_PROTOCOL_VERSION = 0xFE;
// FIXME (aw): should be called V2GTP or SDP buffer
class SdpPacket {
public:
static constexpr auto V2GTP_HEADER_SIZE = 8;
enum class State {
BUFFER_EMPTY,
HEADER_READ,
COMPLETE,
// failed states
INVALID_HEADER,
PAYLOAD_TOO_LONG,
};
auto get_state() const {
return state;
}
auto is_complete() const {
return state == State::COMPLETE;
}
auto get_payload_length() const {
return (state == State::COMPLETE) ? (length - V2GTP_HEADER_SIZE) : 0;
}
v2gtp::PayloadType get_payload_type() const;
uint8_t const* get_payload_buffer() const {
return buffer + V2GTP_HEADER_SIZE;
}
uint8_t const* get_buffer() const {
return buffer;
}
uint8_t* get_current_buffer_pos() {
return buffer + bytes_read;
}
size_t get_remaining_buffer_capacity() const {
return sizeof(buffer) - bytes_read;
}
size_t get_remaining_bytes_to_read() const;
void update_read_bytes(size_t len);
private:
void parse_header();
State state{State::BUFFER_EMPTY};
uint8_t buffer[2048];
size_t bytes_read{0};
size_t length; // length includes V2GTP_HEADER_SIZE
};
} // namespace iso15118::io

View File

@@ -0,0 +1,71 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <atomic>
#include <cstdint>
#include <string>
#include <netinet/in.h>
#include "ipv6_endpoint.hpp"
#include "sdp.hpp"
namespace iso15118::io {
struct PeerRequestContext {
explicit PeerRequestContext(bool valid_) : valid(valid_){};
v2gtp::Security security;
v2gtp::TransportProtocol transport_protocol;
struct sockaddr_in6 address;
operator bool() const {
return valid;
}
private:
const bool valid;
};
class SdpServer {
public:
explicit SdpServer(const std::string& interface_name);
~SdpServer();
PeerRequestContext get_peer_request();
void send_response(const PeerRequestContext&, const Ipv6EndPoint&);
auto get_fd() const {
return fd;
}
void set_dlink_ready(bool ready);
bool is_dlink_ready() const;
private:
int fd{-1};
uint8_t udp_buffer[2048];
std::atomic<bool> dlink_ready_{false};
};
class TlsKeyLoggingServer {
public:
TlsKeyLoggingServer(const std::string& interface_name, uint16_t port);
~TlsKeyLoggingServer();
ssize_t send(const char* line);
auto get_fd() const {
return fd;
}
auto get_port() const {
return port;
}
private:
int fd{-1};
uint16_t port{0};
sockaddr_in6 destination_address{};
};
} // namespace iso15118::io

View File

@@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <array>
#include <cstdint>
namespace iso15118::io {
constexpr std::size_t sha_512_hash_size = 64;
using sha512_hash_t = std::array<uint8_t, sha_512_hash_size>;
} // namespace iso15118::io

View File

@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstddef>
#include <cstdint>
namespace iso15118::io {
struct StreamInputView {
uint8_t const* payload;
size_t payload_len;
};
struct StreamOutputView {
uint8_t* payload;
size_t payload_len;
operator StreamInputView() const {
return {payload, payload_len};
}
};
} // namespace iso15118::io

View File

@@ -0,0 +1,51 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
namespace iso15118 {
using TimePoint = std::chrono::steady_clock::time_point;
inline TimePoint get_current_time_point() {
return std::chrono::steady_clock::now();
}
inline TimePoint offset_time_point_by_ms(const TimePoint& time_point, int32_t offset) {
return time_point + std::chrono::milliseconds(offset);
}
inline int32_t get_timeout_ms_until(const TimePoint& until, int32_t max_timeout_ms) {
const auto duration =
std::chrono::duration_cast<std::chrono::duration<int32_t, std::milli>>(until - get_current_time_point());
const auto timeout_ms = duration.count();
// if the timeout is already reached, return 0 to trigger an immediate timeout event
if (timeout_ms <= 0) {
return 0;
}
return timeout_ms < max_timeout_ms ? timeout_ms : max_timeout_ms;
}
class Timeout {
public:
explicit Timeout(uint32_t timeout_ms) {
timeout_point = get_current_time_point() + std::chrono::milliseconds(timeout_ms);
};
~Timeout() = default;
bool is_reached() {
return get_current_time_point() >= timeout_point;
}
TimePoint get_timeout_point() const {
return timeout_point;
}
private:
TimePoint timeout_point{};
};
} // namespace iso15118

View File

@@ -0,0 +1,122 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <variant>
#include <vector>
#include "common_types.hpp"
namespace iso15118::message_20 {
namespace datatypes {
struct Scheduled_AC_CLReqControlMode : Scheduled_CLReqControlMode {
std::optional<RationalNumber> max_charge_power;
std::optional<RationalNumber> max_charge_power_L2;
std::optional<RationalNumber> max_charge_power_L3;
std::optional<RationalNumber> min_charge_power;
std::optional<RationalNumber> min_charge_power_L2;
std::optional<RationalNumber> min_charge_power_L3;
RationalNumber present_active_power;
std::optional<RationalNumber> present_active_power_L2;
std::optional<RationalNumber> present_active_power_L3;
std::optional<RationalNumber> present_reactive_power;
std::optional<RationalNumber> present_reactive_power_L2;
std::optional<RationalNumber> present_reactive_power_L3;
};
struct BPT_Scheduled_AC_CLReqControlMode : Scheduled_AC_CLReqControlMode {
std::optional<RationalNumber> max_discharge_power;
std::optional<RationalNumber> max_discharge_power_L2;
std::optional<RationalNumber> max_discharge_power_L3;
std::optional<RationalNumber> min_discharge_power;
std::optional<RationalNumber> min_discharge_power_L2;
std::optional<RationalNumber> min_discharge_power_L3;
};
struct Dynamic_AC_CLReqControlMode : Dynamic_CLReqControlMode {
RationalNumber max_charge_power;
std::optional<RationalNumber> max_charge_power_L2;
std::optional<RationalNumber> max_charge_power_L3;
RationalNumber min_charge_power;
std::optional<RationalNumber> min_charge_power_L2;
std::optional<RationalNumber> min_charge_power_L3;
RationalNumber present_active_power;
std::optional<RationalNumber> present_active_power_L2;
std::optional<RationalNumber> present_active_power_L3;
RationalNumber present_reactive_power;
std::optional<RationalNumber> present_reactive_power_L2;
std::optional<RationalNumber> present_reactive_power_L3;
};
struct BPT_Dynamic_AC_CLReqControlMode : Dynamic_AC_CLReqControlMode {
RationalNumber max_discharge_power;
std::optional<RationalNumber> max_discharge_power_L2;
std::optional<RationalNumber> max_discharge_power_L3;
RationalNumber min_discharge_power;
std::optional<RationalNumber> min_discharge_power_L2;
std::optional<RationalNumber> min_discharge_power_L3;
std::optional<RationalNumber> max_v2x_energy_request;
std::optional<RationalNumber> min_v2x_energy_request;
};
struct Scheduled_AC_CLResControlMode : Scheduled_CLResControlMode {
std::optional<RationalNumber> target_active_power;
std::optional<RationalNumber> target_active_power_L2;
std::optional<RationalNumber> target_active_power_L3;
std::optional<RationalNumber> target_reactive_power;
std::optional<RationalNumber> target_reactive_power_L2;
std::optional<RationalNumber> target_reactive_power_L3;
std::optional<RationalNumber> present_active_power;
std::optional<RationalNumber> present_active_power_L2;
std::optional<RationalNumber> present_active_power_L3;
};
struct BPT_Scheduled_AC_CLResControlMode : Scheduled_AC_CLResControlMode {};
struct Dynamic_AC_CLResControlMode : Dynamic_CLResControlMode {
RationalNumber target_active_power;
std::optional<RationalNumber> target_active_power_L2;
std::optional<RationalNumber> target_active_power_L3;
std::optional<RationalNumber> target_reactive_power;
std::optional<RationalNumber> target_reactive_power_L2;
std::optional<RationalNumber> target_reactive_power_L3;
std::optional<RationalNumber> present_active_power;
std::optional<RationalNumber> present_active_power_L2;
std::optional<RationalNumber> present_active_power_L3;
};
struct BPT_Dynamic_AC_CLResControlMode : Dynamic_AC_CLResControlMode {};
} // namespace datatypes
struct AC_ChargeLoopRequest {
Header header;
// the following 2 are inherited from ChargeLoopReq
std::optional<datatypes::DisplayParameters> display_parameters;
bool meter_info_requested;
std::variant<datatypes::Scheduled_AC_CLReqControlMode, datatypes::BPT_Scheduled_AC_CLReqControlMode,
datatypes::Dynamic_AC_CLReqControlMode, datatypes::BPT_Dynamic_AC_CLReqControlMode>
control_mode;
};
struct AC_ChargeLoopResponse {
Header header;
datatypes::ResponseCode response_code;
// the following 3 are inherited from ChargeLoopRes
std::optional<datatypes::EvseStatus> status;
std::optional<datatypes::MeterInfo> meter_info;
std::optional<datatypes::Receipt> receipt;
std::optional<datatypes::RationalNumber> target_frequency;
std::variant<datatypes::Scheduled_AC_CLResControlMode, datatypes::BPT_Scheduled_AC_CLResControlMode,
datatypes::Dynamic_AC_CLResControlMode, datatypes::BPT_Dynamic_AC_CLResControlMode>
control_mode = datatypes::Scheduled_AC_CLResControlMode();
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,71 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <variant>
#include "common_types.hpp"
namespace iso15118::message_20 {
namespace datatypes {
struct AC_CPDReqEnergyTransferMode {
RationalNumber max_charge_power;
std::optional<RationalNumber> max_charge_power_L2;
std::optional<RationalNumber> max_charge_power_L3;
RationalNumber min_charge_power;
std::optional<RationalNumber> min_charge_power_L2;
std::optional<RationalNumber> min_charge_power_L3;
};
struct BPT_AC_CPDReqEnergyTransferMode : AC_CPDReqEnergyTransferMode {
RationalNumber max_discharge_power;
std::optional<RationalNumber> max_discharge_power_L2;
std::optional<RationalNumber> max_discharge_power_L3;
RationalNumber min_discharge_power;
std::optional<RationalNumber> min_discharge_power_L2;
std::optional<RationalNumber> min_discharge_power_L3;
};
struct AC_CPDResEnergyTransferMode {
RationalNumber max_charge_power;
std::optional<RationalNumber> max_charge_power_L2;
std::optional<RationalNumber> max_charge_power_L3;
RationalNumber min_charge_power;
std::optional<RationalNumber> min_charge_power_L2;
std::optional<RationalNumber> min_charge_power_L3;
RationalNumber nominal_frequency;
std::optional<RationalNumber> max_power_asymmetry;
std::optional<RationalNumber> power_ramp_limitation;
std::optional<RationalNumber> present_active_power;
std::optional<RationalNumber> present_active_power_L2;
std::optional<RationalNumber> present_active_power_L3;
};
struct BPT_AC_CPDResEnergyTransferMode : AC_CPDResEnergyTransferMode {
RationalNumber max_discharge_power;
std::optional<RationalNumber> max_discharge_power_L2;
std::optional<RationalNumber> max_discharge_power_L3;
RationalNumber min_discharge_power;
std::optional<RationalNumber> min_discharge_power_L2;
std::optional<RationalNumber> min_discharge_power_L3;
};
} // namespace datatypes
struct AC_ChargeParameterDiscoveryRequest {
Header header;
std::variant<datatypes::AC_CPDReqEnergyTransferMode, datatypes::BPT_AC_CPDReqEnergyTransferMode> transfer_mode;
};
struct AC_ChargeParameterDiscoveryResponse {
Header header;
datatypes::ResponseCode response_code;
std::variant<datatypes::AC_CPDResEnergyTransferMode, datatypes::BPT_AC_CPDResEnergyTransferMode> transfer_mode =
datatypes::AC_CPDResEnergyTransferMode();
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,43 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <string>
#include <variant>
#include <vector>
#include "common_types.hpp"
namespace iso15118::message_20 {
namespace datatypes {
enum class AuthStatus {
Accepted = 0,
Pending = 1,
Rejected = 2,
};
struct EIM_ASReqAuthorizationMode {};
struct PnC_ASReqAuthorizationMode {
std::string id;
GenChallenge gen_challenge;
ContractCertificateChain contract_certificate_chain;
};
} // namespace datatypes
struct AuthorizationRequest {
Header header;
datatypes::Authorization selected_authorization_service;
std::variant<datatypes::EIM_ASReqAuthorizationMode, datatypes::PnC_ASReqAuthorizationMode> authorization_mode;
};
struct AuthorizationResponse {
Header header;
datatypes::ResponseCode response_code;
datatypes::Processing evse_processing{datatypes::Processing::Finished};
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,42 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <array>
#include <optional>
#include <string>
#include <variant>
#include <vector>
#include "common_types.hpp"
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
namespace datatypes {
using SupportedProvidersList = everest::lib::util::fixed_vector<Name, 128>; // [0 - 128]
struct EIM_ASResAuthorizationMode {};
struct PnC_ASResAuthorizationMode {
GenChallenge gen_challenge;
std::optional<datatypes::SupportedProvidersList> supported_providers;
};
} // namespace datatypes
struct AuthorizationSetupRequest {
Header header;
};
struct AuthorizationSetupResponse {
Header header;
datatypes::ResponseCode response_code;
everest::lib::util::fixed_vector<datatypes::Authorization, 2> authorization_services{datatypes::Authorization::EIM};
bool certificate_installation_service{false};
std::variant<datatypes::EIM_ASResAuthorizationMode, datatypes::PnC_ASResAuthorizationMode> authorization_mode =
datatypes::EIM_ASResAuthorizationMode();
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,378 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <array>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
template <typename InType, typename OutType> void convert(const InType&, OutType&);
template <typename T> constexpr auto to_underlying_value(T t) {
return static_cast<std::underlying_type_t<T>>(t);
}
namespace datatypes {
using PercentValue = uint8_t; // [0 - 100]
using NumericId = uint32_t; // [1 - 4294967295]
using Identifier = std::string; // MaxLength: 255
using Name = std::string; // MaxLength: 80
using Description = std::string; // MaxLength: 160
static constexpr auto SESSION_ID_LENGTH = 8;
using SessionId = std::array<uint8_t, SESSION_ID_LENGTH>;
using MeterId = std::string; // MaxLength: 32
using MeterSignature = std::string; // Base64 encoded, MaxLength: 64
static constexpr auto GEN_CHALLENGE_LENGTH = 16;
using GenChallenge = std::array<uint8_t, GEN_CHALLENGE_LENGTH>; // Base64 encoded, MaxLength: 16
using Certificate = std::string; // Base64 encoded, MaxLength: 1600
using SubCertificate = everest::lib::util::fixed_vector<Certificate, 3>; // Max: 3
enum class ResponseCode {
OK = 0,
OK_CertificateExpiresSoon = 1,
OK_NewSessionEstablished = 2,
OK_OldSessionJoined = 3,
OK_PowerToleranceConfirmed = 4,
WARNING_AuthorizationSelectionInvalid = 5,
WARNING_CertificateExpired = 6,
WARNING_CertificateNotYetValid = 7,
WARNING_CertificateRevoked = 8,
WARNING_CertificateValidationError = 9,
WARNING_ChallengeInvalid = 10,
WARNING_EIMAuthorizationFailure = 11,
WARNING_eMSPUnknown = 12,
WARNING_EVPowerProfileViolation = 13,
WARNING_GeneralPnCAuthorizationError = 14,
WARNING_NoCertificateAvailable = 15,
WARNING_NoContractMatchingPCIDFound = 16,
WARNING_PowerToleranceNotConfirmed = 17,
WARNING_ScheduleRenegotiationFailed = 18,
WARNING_StandbyNotAllowed = 19,
WARNING_WPT = 20,
FAILED = 21,
FAILED_AssociationError = 22,
FAILED_ContactorError = 23,
FAILED_EVPowerProfileInvalid = 24,
FAILED_EVPowerProfileViolation = 25,
FAILED_MeteringSignatureNotValid = 26,
FAILED_NoEnergyTransferServiceSelected = 27,
FAILED_NoServiceRenegotiationSupported = 28,
FAILED_PauseNotAllowed = 29,
FAILED_PowerDeliveryNotApplied = 30,
FAILED_PowerToleranceNotConfirmed = 31,
FAILED_ScheduleRenegotiation = 32,
FAILED_ScheduleSelectionInvalid = 33,
FAILED_SequenceError = 34,
FAILED_ServiceIDInvalid = 35,
FAILED_ServiceSelectionInvalid = 36,
FAILED_SignatureError = 37,
FAILED_UnknownSession = 38,
FAILED_WrongChargeParameter = 39
};
enum class Processing {
Finished = 0,
Ongoing = 1,
Ongoing_WaitingForCustomerInteraction = 2,
};
enum class Authorization {
EIM = 0,
PnC = 1,
};
enum class ServiceCategory : uint16_t {
AC = 1,
DC = 2,
WPT = 3,
DC_ACDP = 4,
AC_BPT = 5,
DC_BPT = 6,
DC_ACDP_BPT = 7,
MCS = 8,
MCS_BPT = 9,
AC_DER = 10,
Internet = 65,
ParkingStatus = 66,
};
enum class EvseNotification {
Pause,
ExitStandby,
Terminate,
ScheduleRenegotiation,
ServiceRenegotiation,
MeteringConfirmation,
};
enum class ChargingSession {
Pause,
Terminate,
ServiceRenegotiation,
};
enum class AcConnector {
SinglePhase = 1,
ThreePhase = 3,
};
enum class DcConnector {
Core = 1,
Extended = 2,
Dual2 = 3,
Dual4 = 4,
};
enum class McsConnector {
Mcs = 1,
Chaoji = 2,
UltraChaoji = 3,
rMcs = 4,
xMcs = 5,
Aviation = 6,
Marine = 7,
};
enum class ControlMode {
Scheduled = 1,
Dynamic = 2,
};
enum class MobilityNeedsMode {
ProvidedByEvcc = 1,
ProvidedBySecc = 2,
};
enum class Pricing {
NoPricing = 0,
AbsolutePricing = 1,
PriceLevels = 2,
};
enum class BptChannel {
Unified = 1,
Separated = 2,
};
enum class GeneratorMode {
GridFollowing = 1,
GridForming = 2,
};
enum class GridCodeIslandingDetectionMethod {
Active = 1,
Passive = 2,
};
enum class Protocol {
Ftp,
Http,
Https,
};
enum class Port {
Port20 = 20,
Port21 = 21,
Port80 = 80,
Port443 = 443,
};
enum class IntendedService {
VehicleCheckIn = 1,
VehicleCheckOut = 2,
};
enum class ParkingStatus {
AutoInternal = 1,
AutoExternal = 2,
ManualInternal = 3,
ManualExternal = 4,
};
struct RationalNumber {
int16_t value{0};
int8_t exponent{0};
};
struct EvseStatus {
uint16_t notification_max_delay;
EvseNotification notification;
};
struct AcParameterList {
AcConnector connector;
ControlMode control_mode;
MobilityNeedsMode mobility_needs_mode;
uint32_t evse_nominal_voltage;
Pricing pricing;
};
struct AcBptParameterList : AcParameterList {
BptChannel bpt_channel;
GeneratorMode generator_mode;
GridCodeIslandingDetectionMethod grid_code_detection_method;
};
struct DcParameterList {
DcConnector connector;
ControlMode control_mode;
MobilityNeedsMode mobility_needs_mode;
Pricing pricing;
};
struct DcBptParameterList : DcParameterList {
BptChannel bpt_channel;
GeneratorMode generator_mode;
};
struct McsParameterList {
McsConnector connector;
ControlMode control_mode;
MobilityNeedsMode mobility_needs_mode;
Pricing pricing;
};
struct McsBptParameterList : McsParameterList {
BptChannel bpt_channel;
GeneratorMode generator_mode;
};
struct InternetParameterList {
Protocol protocol;
Port port;
};
struct ParkingParameterList {
IntendedService intended_service;
ParkingStatus parking_status;
};
struct Scheduled_CLReqControlMode {
std::optional<RationalNumber> target_energy_request;
std::optional<RationalNumber> max_energy_request;
std::optional<RationalNumber> min_energy_request;
};
struct Dynamic_CLReqControlMode {
std::optional<uint32_t> departure_time;
RationalNumber target_energy_request;
RationalNumber max_energy_request;
RationalNumber min_energy_request;
};
struct Scheduled_CLResControlMode {};
struct Dynamic_CLResControlMode {
std::optional<uint32_t> departure_time;
std::optional<uint8_t> minimum_soc;
std::optional<uint8_t> target_soc;
std::optional<uint16_t> ack_max_delay;
};
struct PowerScheduleEntry {
uint32_t duration;
RationalNumber power;
std::optional<RationalNumber> power_l2;
std::optional<RationalNumber> power_l3;
};
struct MeterInfo {
MeterId meter_id;
uint64_t charged_energy_reading_wh;
std::optional<uint64_t> bpt_discharged_energy_reading_wh;
std::optional<uint64_t> capacitive_energy_reading_varh;
std::optional<uint64_t> bpt_inductive_energy_reading_varh;
std::optional<MeterSignature> meter_signature;
std::optional<int16_t> meter_status;
std::optional<uint64_t> meter_timestamp;
};
struct DisplayParameters {
std::optional<PercentValue> present_soc;
std::optional<PercentValue> min_soc;
std::optional<PercentValue> target_soc;
std::optional<PercentValue> max_soc;
std::optional<uint32_t> remaining_time_to_min_soc;
std::optional<uint32_t> remaining_time_to_target_soc;
std::optional<uint32_t> remaining_time_to_max_soc;
std::optional<bool> charging_complete;
std::optional<RationalNumber> battery_energy_capacity;
std::optional<bool> inlet_hot;
};
struct DetailedCost {
RationalNumber amount;
RationalNumber cost_per_unit;
};
struct DetailedTax {
NumericId tax_rule_id;
RationalNumber amount;
};
struct Receipt {
uint64_t time_anchor;
std::optional<DetailedCost> energy_costs;
std::optional<DetailedCost> occupancy_costs;
std::optional<DetailedCost> additional_service_costs;
std::optional<DetailedCost> overstay_costs;
everest::lib::util::fixed_vector<DetailedTax, 10> tax_costs; // 0 to 10 elements! // FIXME(sl): optional?
};
struct X509IssuerSerial {
std::string issuer_name;
int64_t serial_number;
};
struct ListOfRootCertificateIDs {
everest::lib::util::fixed_vector<X509IssuerSerial, 20> root_certificate_id;
};
// TODO(sl): Adding content to following structs
struct SignedInfo {};
struct SignatureValue {};
struct KeyInfo {};
struct Object {};
struct Signature {
SignedInfo signed_info;
SignatureValue signature;
std::optional<KeyInfo> key_info;
std::optional<Object> object;
std::optional<std::string> id;
};
struct ContractCertificateChain {
Certificate certificate;
SubCertificate sub_certificates;
};
float from_RationalNumber(const RationalNumber& in);
RationalNumber from_float(float in);
std::string from_Protocol(const Protocol& in);
std::string from_control_mode(const ControlMode& in);
std::string from_mobility_needs_mode(const MobilityNeedsMode& in);
} // namespace datatypes
struct Header {
datatypes::SessionId session_id{};
uint64_t timestamp;
// std::optional<datatypes::Signature> signature;
};
template <typename cb_HeaderType> void convert(const cb_HeaderType& in, Header& out);
template <typename cb_RationalNumberType> void convert(const cb_RationalNumberType& in, datatypes::RationalNumber& out);
template <typename cb_RationalNumberType> void convert(const datatypes::RationalNumber& in, cb_RationalNumberType& out);
} // namespace iso15118::message_20

View File

@@ -0,0 +1,20 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "common_types.hpp"
namespace iso15118::message_20 {
struct DC_CableCheckRequest {
Header header;
};
struct DC_CableCheckResponse {
Header header;
datatypes::ResponseCode response_code;
datatypes::Processing processing{datatypes::Processing::Ongoing};
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,110 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <variant>
#include <vector>
#include "common_types.hpp"
namespace iso15118::message_20 {
namespace datatypes {
struct Scheduled_DC_CLReqControlMode : Scheduled_CLReqControlMode {
RationalNumber target_current;
RationalNumber target_voltage;
std::optional<RationalNumber> max_charge_power;
std::optional<RationalNumber> min_charge_power;
std::optional<RationalNumber> max_charge_current;
std::optional<RationalNumber> max_voltage;
std::optional<RationalNumber> min_voltage;
};
struct BPT_Scheduled_DC_CLReqControlMode : Scheduled_DC_CLReqControlMode {
std::optional<RationalNumber> max_discharge_power;
std::optional<RationalNumber> min_discharge_power;
std::optional<RationalNumber> max_discharge_current;
};
struct Dynamic_DC_CLReqControlMode : Dynamic_CLReqControlMode {
RationalNumber max_charge_power;
RationalNumber min_charge_power;
RationalNumber max_charge_current;
RationalNumber max_voltage;
RationalNumber min_voltage;
};
struct BPT_Dynamic_DC_CLReqControlMode : Dynamic_DC_CLReqControlMode {
RationalNumber max_discharge_power;
RationalNumber min_discharge_power;
RationalNumber max_discharge_current;
std::optional<RationalNumber> max_v2x_energy_request;
std::optional<RationalNumber> min_v2x_energy_request;
};
struct Scheduled_DC_CLResControlMode : Scheduled_CLResControlMode {
std::optional<RationalNumber> max_charge_power;
std::optional<RationalNumber> min_charge_power;
std::optional<RationalNumber> max_charge_current;
std::optional<RationalNumber> max_voltage;
};
struct BPT_Scheduled_DC_CLResControlMode : Scheduled_DC_CLResControlMode {
std::optional<RationalNumber> max_discharge_power;
std::optional<RationalNumber> min_discharge_power;
std::optional<RationalNumber> max_discharge_current;
std::optional<RationalNumber> min_voltage;
};
struct Dynamic_DC_CLResControlMode : Dynamic_CLResControlMode {
RationalNumber max_charge_power;
RationalNumber min_charge_power;
RationalNumber max_charge_current;
RationalNumber max_voltage;
};
struct BPT_Dynamic_DC_CLResControlMode : Dynamic_DC_CLResControlMode {
RationalNumber max_discharge_power;
RationalNumber min_discharge_power;
RationalNumber max_discharge_current;
RationalNumber min_voltage;
};
} // namespace datatypes
struct DC_ChargeLoopRequest {
Header header;
// the following 2 are inherited from ChargeLoopReq
std::optional<datatypes::DisplayParameters> display_parameters;
bool meter_info_requested;
datatypes::RationalNumber present_voltage;
std::variant<datatypes::Scheduled_DC_CLReqControlMode, datatypes::BPT_Scheduled_DC_CLReqControlMode,
datatypes::Dynamic_DC_CLReqControlMode, datatypes::BPT_Dynamic_DC_CLReqControlMode>
control_mode;
};
struct DC_ChargeLoopResponse {
Header header;
datatypes::ResponseCode response_code;
// the following 3 are inherited from ChargeLoopRes
std::optional<datatypes::EvseStatus> status;
std::optional<datatypes::MeterInfo> meter_info;
std::optional<datatypes::Receipt> receipt;
datatypes::RationalNumber present_current{0, 0};
datatypes::RationalNumber present_voltage{0, 0};
bool power_limit_achieved{false};
bool current_limit_achieved{false};
bool voltage_limit_achieved{false};
std::variant<datatypes::Scheduled_DC_CLResControlMode, datatypes::BPT_Scheduled_DC_CLResControlMode,
datatypes::Dynamic_DC_CLResControlMode, datatypes::BPT_Dynamic_DC_CLResControlMode>
control_mode = datatypes::Scheduled_DC_CLResControlMode();
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,63 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <variant>
#include "common_types.hpp"
namespace iso15118::message_20 {
namespace datatypes {
struct DC_CPDReqEnergyTransferMode {
RationalNumber max_charge_power;
RationalNumber min_charge_power;
RationalNumber max_charge_current;
RationalNumber min_charge_current;
RationalNumber max_voltage;
RationalNumber min_voltage;
std::optional<uint8_t> target_soc;
};
struct BPT_DC_CPDReqEnergyTransferMode : DC_CPDReqEnergyTransferMode {
RationalNumber max_discharge_power;
RationalNumber min_discharge_power;
RationalNumber max_discharge_current;
RationalNumber min_discharge_current;
};
struct DC_CPDResEnergyTransferMode {
RationalNumber max_charge_power;
RationalNumber min_charge_power;
RationalNumber max_charge_current;
RationalNumber min_charge_current;
RationalNumber max_voltage;
RationalNumber min_voltage;
std::optional<RationalNumber> power_ramp_limit;
};
struct BPT_DC_CPDResEnergyTransferMode : DC_CPDResEnergyTransferMode {
RationalNumber max_discharge_power;
RationalNumber min_discharge_power;
RationalNumber max_discharge_current;
RationalNumber min_discharge_current;
};
} // namespace datatypes
struct DC_ChargeParameterDiscoveryRequest {
Header header;
std::variant<datatypes::DC_CPDReqEnergyTransferMode, datatypes::BPT_DC_CPDReqEnergyTransferMode> transfer_mode;
};
struct DC_ChargeParameterDiscoveryResponse {
Header header;
datatypes::ResponseCode response_code;
std::variant<datatypes::DC_CPDResEnergyTransferMode, datatypes::BPT_DC_CPDResEnergyTransferMode> transfer_mode =
datatypes::DC_CPDResEnergyTransferMode();
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "common_types.hpp"
namespace iso15118::message_20 {
struct DC_PreChargeRequest {
Header header;
datatypes::Processing processing;
datatypes::RationalNumber present_voltage;
datatypes::RationalNumber target_voltage;
};
struct DC_PreChargeResponse {
Header header;
datatypes::ResponseCode response_code;
datatypes::RationalNumber present_voltage;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include "common_types.hpp"
namespace iso15118::message_20 {
struct DC_WeldingDetectionRequest {
Header header;
datatypes::Processing processing;
};
struct DC_WeldingDetectionResponse {
Header header;
datatypes::ResponseCode response_code;
datatypes::RationalNumber present_voltage;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,50 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
// FIXME (aw): how to streamline this with type.hpp?
#include <iso15118/io/sdp.hpp>
namespace iso15118::message_20 {
template <typename T> struct PayloadTypeTrait;
//
// definitions of type traits
//
#ifdef CREATE_TYPE_TRAIT
#define CREATE_TYPE_TRAIT_PUSHED CREATE_TYPE_TRAIT
#endif
#define CREATE_TYPE_TRAIT(struct_name, payload_type) \
struct struct_name; \
template <> struct PayloadTypeTrait<struct_name> { \
static const io::v2gtp::PayloadType type = io::v2gtp::PayloadType::payload_type; \
}
CREATE_TYPE_TRAIT(SupportedAppProtocolResponse, SAP);
CREATE_TYPE_TRAIT(SessionSetupResponse, Part20Main);
CREATE_TYPE_TRAIT(AuthorizationSetupResponse, Part20Main);
CREATE_TYPE_TRAIT(AuthorizationResponse, Part20Main);
CREATE_TYPE_TRAIT(ServiceDiscoveryResponse, Part20Main);
CREATE_TYPE_TRAIT(ServiceDetailResponse, Part20Main);
CREATE_TYPE_TRAIT(ServiceSelectionResponse, Part20Main);
CREATE_TYPE_TRAIT(DC_ChargeParameterDiscoveryResponse, Part20DC);
CREATE_TYPE_TRAIT(ScheduleExchangeResponse, Part20Main);
CREATE_TYPE_TRAIT(DC_CableCheckResponse, Part20DC);
CREATE_TYPE_TRAIT(DC_PreChargeResponse, Part20DC);
CREATE_TYPE_TRAIT(PowerDeliveryResponse, Part20Main);
CREATE_TYPE_TRAIT(DC_ChargeLoopResponse, Part20DC);
CREATE_TYPE_TRAIT(DC_WeldingDetectionResponse, Part20DC);
CREATE_TYPE_TRAIT(SessionStopResponse, Part20Main);
CREATE_TYPE_TRAIT(AC_ChargeParameterDiscoveryResponse, Part20AC);
CREATE_TYPE_TRAIT(AC_ChargeLoopResponse, Part20AC);
#ifdef CREATE_TYPE_TRAIT_PUSHED
#define CREATE_TYPE_TRAIT CREATE_TYPE_TRAIT_PUSHED
#else
#undef CREATE_TYPE_TRAIT
#endif
} // namespace iso15118::message_20

View File

@@ -0,0 +1,68 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <variant>
#include <vector>
#include "common_types.hpp"
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
namespace datatypes {
enum class Progress {
Start,
Stop,
Standby,
ScheduleRenegotiation,
};
struct Dynamic_EVPPTControlMode {
// intentionally left blank
};
enum class PowerToleranceAcceptance : uint8_t {
NotConfirmed,
Confirmed,
};
struct Scheduled_EVPPTControlMode {
NumericId selected_schedule;
std::optional<PowerToleranceAcceptance> power_tolerance_acceptance;
};
struct PowerProfile {
uint64_t time_anchor;
std::variant<Dynamic_EVPPTControlMode, Scheduled_EVPPTControlMode> control_mode;
everest::lib::util::fixed_vector<PowerScheduleEntry, 2048> entries; // maximum 2048
};
enum class ChannelSelection : uint8_t {
Charge,
Discharge,
};
}; // namespace datatypes
struct PowerDeliveryRequest {
Header header;
datatypes::Processing processing;
datatypes::Progress charge_progress;
std::optional<datatypes::PowerProfile> power_profile;
std::optional<datatypes::ChannelSelection> channel_selection;
};
struct PowerDeliveryResponse {
Header header;
datatypes::ResponseCode response_code;
std::optional<datatypes::EvseStatus> status;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,206 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <array>
#include <optional>
#include <string>
#include <variant>
#include "common_types.hpp"
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
namespace datatypes {
static constexpr auto TAX_RULE_LENGTH = 10;
static constexpr auto PRICE_RULE_STACK_LENGTH = 1024;
static constexpr auto PRICE_RULE_LENGTH = 8;
static constexpr auto OVERSTAY_RULE_LENGTH = 5;
static constexpr auto ADDITIONAL_SERVICE_LENGTH = 5;
static constexpr auto PRICE_LEVEL_SCHEDULE_LENGTH = 1024;
static constexpr auto SCHEDULED_POWER_DURATION_S = 86400;
using MaxSupportingPointsScheduleTuple = uint16_t; // needs to be [12 - 1024]
using Curreny = std::string; // MaxLength: 3
using Language = std::string; // MaxLength: 3
struct TaxRule {
NumericId tax_rule_id;
std::optional<Name> tax_rule_name;
RationalNumber tax_rate;
std::optional<bool> tax_included_in_price;
bool applies_to_energy_fee;
bool applies_to_parking_fee;
bool applies_to_overstay_fee;
bool applies_to_minimum_maximum_cost;
};
struct PriceRule {
RationalNumber energy_fee;
std::optional<RationalNumber> parking_fee;
std::optional<uint32_t> parking_fee_period;
std::optional<uint16_t> carbon_dioxide_emission;
std::optional<uint8_t> renewable_generation_percentage;
RationalNumber power_range_start;
};
struct PriceRuleStack {
uint32_t duration;
std::array<PriceRule, PRICE_RULE_LENGTH> price_rule;
};
struct AdditionalService {
Name service_name;
RationalNumber service_fee;
};
using TaxRuleList = std::array<TaxRule, TAX_RULE_LENGTH>;
using PriceRuleStackList = std::array<PriceRuleStack, PRICE_RULE_STACK_LENGTH>;
using AdditionalServiceList = std::array<AdditionalService, ADDITIONAL_SERVICE_LENGTH>;
struct Dynamic_SEReqControlMode {
uint32_t departure_time;
std::optional<PercentValue> minimum_soc;
std::optional<PercentValue> target_soc;
RationalNumber target_energy;
RationalNumber max_energy;
RationalNumber min_energy;
std::optional<RationalNumber> max_v2x_energy;
std::optional<RationalNumber> min_v2x_energy;
};
struct EVPowerScheduleEntry {
uint32_t duration;
RationalNumber power;
};
struct EVPowerSchedule {
uint64_t time_anchor;
everest::lib::util::fixed_vector<EVPowerScheduleEntry, 1024> entries; // max 1024
};
struct EVPriceRule {
RationalNumber energy_fee;
RationalNumber power_range_start;
};
struct EVPriceRuleStack {
uint32_t duration;
everest::lib::util::fixed_vector<EVPriceRule, 8> price_rules; // max 8
};
struct EVAbsolutePriceSchedule {
uint64_t time_anchor;
Curreny currency;
Identifier price_algorithm;
everest::lib::util::fixed_vector<EVPriceRuleStack, 1024> price_rule_stacks; // max 1024
};
struct EVEnergyOffer {
EVPowerSchedule power_schedule;
EVAbsolutePriceSchedule absolute_price_schedule;
};
struct Scheduled_SEReqControlMode {
std::optional<uint32_t> departure_time;
std::optional<RationalNumber> target_energy;
std::optional<RationalNumber> max_energy;
std::optional<RationalNumber> min_energy;
std::optional<EVEnergyOffer> energy_offer;
};
struct OverstayRule {
std::optional<Description> overstay_rule_description;
uint32_t start_time;
RationalNumber overstay_fee;
uint32_t overstay_fee_period;
};
struct OverstayRulesList {
std::optional<uint32_t> overstay_time_threshold;
std::optional<RationalNumber> overstay_power_threshold;
everest::lib::util::fixed_vector<OverstayRule, 5> overstay_rule; // 5
};
struct AbsolutePriceSchedule {
std::optional<std::string> id;
uint64_t time_anchor;
NumericId price_schedule_id;
std::optional<Description> price_schedule_description;
Curreny currency;
Language language;
Identifier price_algorithm;
std::optional<RationalNumber> minimum_cost;
std::optional<RationalNumber> maximum_cost;
std::optional<TaxRuleList> tax_rules;
PriceRuleStackList price_rule_stacks;
std::optional<OverstayRulesList> overstay_rules;
std::optional<AdditionalServiceList> additional_selected_services;
};
struct PriceLevelScheduleEntry {
uint32_t duration;
uint8_t price_level;
};
struct PriceLevelSchedule {
std::optional<std::string> id;
uint64_t time_anchor;
NumericId price_schedule_id;
std::optional<Description> price_schedule_description;
uint8_t number_of_price_levels;
everest::lib::util::fixed_vector<PriceLevelScheduleEntry, 1024> price_level_schedule_entries; // 1024
};
struct Dynamic_SEResControlMode {
std::optional<uint32_t> departure_time;
std::optional<PercentValue> minimum_soc;
std::optional<PercentValue> target_soc;
std::variant<std::monostate, AbsolutePriceSchedule, PriceLevelSchedule> price_schedule;
};
struct PowerSchedule {
uint64_t time_anchor;
std::optional<RationalNumber> available_energy;
std::optional<RationalNumber> power_tolerance;
everest::lib::util::fixed_vector<PowerScheduleEntry, 1024> entries; // 1024
};
struct ChargingSchedule {
PowerSchedule power_schedule;
std::variant<std::monostate, AbsolutePriceSchedule, PriceLevelSchedule> price_schedule;
};
struct ScheduleTuple {
NumericId schedule_tuple_id; // 1 - 255
ChargingSchedule charging_schedule;
std::optional<ChargingSchedule> discharging_schedule;
};
struct Scheduled_SEResControlMode {
everest::lib::util::fixed_vector<ScheduleTuple, 3> schedule_tuple; // 3
};
}; // namespace datatypes
struct ScheduleExchangeRequest {
Header header;
datatypes::MaxSupportingPointsScheduleTuple max_supporting_points;
std::variant<datatypes::Dynamic_SEReqControlMode, datatypes::Scheduled_SEReqControlMode> control_mode;
};
struct ScheduleExchangeResponse {
Header header;
datatypes::ResponseCode response_code;
datatypes::Processing processing{datatypes::Processing::Finished};
std::optional<bool> go_to_pause;
std::variant<datatypes::Dynamic_SEResControlMode, datatypes::Scheduled_SEResControlMode> control_mode =
datatypes::Dynamic_SEResControlMode();
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,52 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <variant>
#include "common_types.hpp"
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
namespace datatypes {
struct Parameter {
Name name;
std::variant<bool, int8_t, int16_t, int32_t, Name, RationalNumber> value;
};
struct ParameterSet {
uint16_t id;
everest::lib::util::fixed_vector<Parameter, 32> parameter;
ParameterSet() = default;
ParameterSet(uint16_t _id);
ParameterSet(uint16_t _id, const AcParameterList& list);
ParameterSet(uint16_t _id, const AcBptParameterList& list);
ParameterSet(uint16_t _id, const DcParameterList& list);
ParameterSet(uint16_t _id, const DcBptParameterList& list);
ParameterSet(uint16_t _id, const McsParameterList& list);
ParameterSet(uint16_t _id, const McsBptParameterList& list);
ParameterSet(uint16_t _id, const InternetParameterList& list);
ParameterSet(uint16_t _id, const ParkingParameterList& list);
};
using ServiceParameterList = everest::lib::util::fixed_vector<ParameterSet, 32>; // Max: 32
} // namespace datatypes
struct ServiceDetailRequest {
Header header;
uint16_t service;
};
struct ServiceDetailResponse {
Header header;
datatypes::ResponseCode response_code;
uint16_t service{to_underlying_value(datatypes::ServiceCategory::DC)};
datatypes::ServiceParameterList service_parameter_list{datatypes::ParameterSet(0)};
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,48 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <vector>
#include "common_types.hpp"
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
namespace datatypes {
using ServiceIdList = everest::lib::util::fixed_vector<std::uint16_t, 16>; // 16
struct Service {
ServiceCategory service_id;
bool free_service;
};
using ServiceList = everest::lib::util::fixed_vector<Service, 8>; // max: 8
struct VasService {
uint16_t service_id;
bool free_service;
};
using VasServiceList = everest::lib::util::fixed_vector<VasService, 8>; // max: 8
} // namespace datatypes
struct ServiceDiscoveryRequest {
Header header;
std::optional<datatypes::ServiceIdList> supported_service_ids;
};
struct ServiceDiscoveryResponse {
Header header;
datatypes::ResponseCode response_code;
bool service_renegotiation_supported = false;
datatypes::ServiceList energy_transfer_service_list = {{
datatypes::ServiceCategory::AC, // service_id
false // free_service
}};
std::optional<datatypes::VasServiceList> vas_list;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,40 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include "common_types.hpp"
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
namespace datatypes {
struct SelectedService {
ServiceCategory service_id;
uint16_t parameter_set_id;
};
struct VasSelectedService {
uint16_t service_id;
uint16_t parameter_set_id;
};
using VasSelectedServiceList = everest::lib::util::fixed_vector<VasSelectedService, 16>; // Max: 16
} // namespace datatypes
struct ServiceSelectionRequest {
Header header;
datatypes::SelectedService selected_energy_transfer_service;
std::optional<datatypes::VasSelectedServiceList> selected_vas_list;
};
struct ServiceSelectionResponse {
Header header;
datatypes::ResponseCode response_code;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <string>
#include "common_types.hpp"
namespace iso15118::message_20 {
struct SessionSetupRequest {
Header header;
datatypes::Identifier evccid;
};
struct SessionSetupResponse {
Header header;
datatypes::ResponseCode response_code;
datatypes::Identifier evseid;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <optional>
#include <string>
#include "common_types.hpp"
namespace iso15118::message_20 {
struct SessionStopRequest {
Header header;
datatypes::ChargingSession charging_session;
std::optional<datatypes::Name> ev_termination_code;
std::optional<datatypes::Description> ev_termination_explanation;
};
struct SessionStopResponse {
Header header;
datatypes::ResponseCode response_code;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,47 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstddef>
#include <optional>
#include <string>
#include <vector>
#include <iso15118/io/stream_view.hpp>
#include <everest/util/vector/fixed_vector.hpp>
namespace iso15118::message_20 {
struct SupportedAppProtocol {
std::string protocol_namespace;
uint32_t version_number_major;
uint32_t version_number_minor;
uint8_t schema_id;
uint8_t priority;
bool operator==(const iso15118::message_20::SupportedAppProtocol& other) const {
return this->protocol_namespace == other.protocol_namespace and this->priority == other.priority and
this->schema_id == other.schema_id and this->version_number_major == other.version_number_major and
this->version_number_minor == other.version_number_minor;
}
};
struct SupportedAppProtocolRequest {
everest::lib::util::fixed_vector<SupportedAppProtocol, 20> app_protocol;
};
struct SupportedAppProtocolResponse {
enum class ResponseCode {
OK_SuccessfulNegotiation = 0,
OK_SuccessfulNegotiationWithMinorDeviation = 1,
Failed_NoNegotiation = 2,
};
ResponseCode response_code;
std::optional<uint8_t> schema_id;
};
size_t encode_supported_app_protocol_response(const io::StreamOutputView&, const SupportedAppProtocolResponse&);
} // namespace iso15118::message_20

View File

@@ -0,0 +1,109 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <iso15118/io/stream_view.hpp>
namespace iso15118::message_20 {
enum class Type {
None,
SupportedAppProtocolReq,
SupportedAppProtocolRes,
SessionSetupReq,
SessionSetupRes,
AuthorizationSetupReq,
AuthorizationSetupRes,
AuthorizationReq,
AuthorizationRes,
ServiceDiscoveryReq,
ServiceDiscoveryRes,
ServiceDetailReq,
ServiceDetailRes,
ServiceSelectionReq,
ServiceSelectionRes,
DC_ChargeParameterDiscoveryReq,
DC_ChargeParameterDiscoveryRes,
ScheduleExchangeReq,
ScheduleExchangeRes,
DC_CableCheckReq,
DC_CableCheckRes,
DC_PreChargeReq,
DC_PreChargeRes,
PowerDeliveryReq,
PowerDeliveryRes,
DC_ChargeLoopReq,
DC_ChargeLoopRes,
DC_WeldingDetectionReq,
DC_WeldingDetectionRes,
SessionStopReq,
SessionStopRes,
AC_ChargeParameterDiscoveryReq,
AC_ChargeParameterDiscoveryRes,
AC_ChargeLoopReq,
AC_ChargeLoopRes,
};
template <typename T> struct TypeTrait {
static const Type type = Type::None;
};
template <typename InType, typename OutType> void convert(const InType&, OutType&);
template <typename MessageType> size_t serialize(const MessageType&, const io::StreamOutputView&);
//
// definitions of type traits
//
#ifdef CREATE_TYPE_TRAIT
#define CREATE_TYPE_TRAIT_PUSHED CREATE_TYPE_TRAIT
#endif
#define CREATE_TYPE_TRAIT(struct_name, enum_name) \
struct struct_name; \
template <> struct TypeTrait<struct_name> { \
static const Type type = Type::enum_name; \
}
CREATE_TYPE_TRAIT(SupportedAppProtocolRequest, SupportedAppProtocolReq);
CREATE_TYPE_TRAIT(SupportedAppProtocolResponse, SupportedAppProtocolRes);
CREATE_TYPE_TRAIT(SessionSetupRequest, SessionSetupReq);
CREATE_TYPE_TRAIT(SessionSetupResponse, SessionSetupRes);
CREATE_TYPE_TRAIT(AuthorizationSetupRequest, AuthorizationSetupReq);
CREATE_TYPE_TRAIT(AuthorizationSetupResponse, AuthorizationSetupRes);
CREATE_TYPE_TRAIT(AuthorizationRequest, AuthorizationReq);
CREATE_TYPE_TRAIT(AuthorizationResponse, AuthorizationRes);
CREATE_TYPE_TRAIT(ServiceDiscoveryRequest, ServiceDiscoveryReq);
CREATE_TYPE_TRAIT(ServiceDiscoveryResponse, ServiceDiscoveryRes);
CREATE_TYPE_TRAIT(ServiceDetailRequest, ServiceDetailReq);
CREATE_TYPE_TRAIT(ServiceDetailResponse, ServiceDetailRes);
CREATE_TYPE_TRAIT(ServiceSelectionRequest, ServiceSelectionReq);
CREATE_TYPE_TRAIT(ServiceSelectionResponse, ServiceSelectionRes);
CREATE_TYPE_TRAIT(DC_ChargeParameterDiscoveryRequest, DC_ChargeParameterDiscoveryReq);
CREATE_TYPE_TRAIT(DC_ChargeParameterDiscoveryResponse, DC_ChargeParameterDiscoveryRes);
CREATE_TYPE_TRAIT(ScheduleExchangeRequest, ScheduleExchangeReq);
CREATE_TYPE_TRAIT(ScheduleExchangeResponse, ScheduleExchangeRes);
CREATE_TYPE_TRAIT(DC_CableCheckRequest, DC_CableCheckReq);
CREATE_TYPE_TRAIT(DC_CableCheckResponse, DC_CableCheckRes);
CREATE_TYPE_TRAIT(DC_PreChargeRequest, DC_PreChargeReq);
CREATE_TYPE_TRAIT(DC_PreChargeResponse, DC_PreChargeRes);
CREATE_TYPE_TRAIT(PowerDeliveryRequest, PowerDeliveryReq);
CREATE_TYPE_TRAIT(PowerDeliveryResponse, PowerDeliveryRes);
CREATE_TYPE_TRAIT(DC_ChargeLoopRequest, DC_ChargeLoopReq);
CREATE_TYPE_TRAIT(DC_ChargeLoopResponse, DC_ChargeLoopRes);
CREATE_TYPE_TRAIT(DC_WeldingDetectionRequest, DC_WeldingDetectionReq);
CREATE_TYPE_TRAIT(DC_WeldingDetectionResponse, DC_WeldingDetectionRes);
CREATE_TYPE_TRAIT(SessionStopRequest, SessionStopReq);
CREATE_TYPE_TRAIT(SessionStopResponse, SessionStopRes);
CREATE_TYPE_TRAIT(AC_ChargeParameterDiscoveryRequest, AC_ChargeParameterDiscoveryReq);
CREATE_TYPE_TRAIT(AC_ChargeParameterDiscoveryResponse, AC_ChargeParameterDiscoveryRes);
CREATE_TYPE_TRAIT(AC_ChargeLoopRequest, AC_ChargeLoopReq);
CREATE_TYPE_TRAIT(AC_ChargeLoopResponse, AC_ChargeLoopRes);
#ifdef CREATE_TYPE_TRAIT_PUSHED
#define CREATE_TYPE_TRAIT CREATE_TYPE_TRAIT_PUSHED
#else
#undef CREATE_TYPE_TRAIT
#endif
} // namespace iso15118::message_20

View File

@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cstddef>
#include <cstdint>
#include <memory>
#include <stdexcept>
#include <string>
// FIXME (aw): we only need the payload types from sdp.hpp, this could be shared in a separate header file
#include <iso15118/io/sdp.hpp>
#include <iso15118/io/stream_view.hpp>
#include "type.hpp"
namespace iso15118::message_20 {
class Variant {
public:
using CustomDeleter = void (*)(void*);
Variant(io::v2gtp::PayloadType, const io::StreamInputView&);
template <typename MessageType> Variant(const MessageType& in) {
static_assert(TypeTrait<MessageType>::type != Type::None, "Unhandled type!");
data = new MessageType;
*static_cast<MessageType*>(data) = in;
custom_deleter = [](void* ptr) { delete static_cast<MessageType*>(ptr); };
type = message_20::TypeTrait<MessageType>::type;
}
~Variant();
Type get_type() const;
const std::string& get_error() const;
template <typename T> const T& get() const {
static_assert(TypeTrait<T>::type != Type::None, "Unhandled type!");
if (TypeTrait<T>::type != type) {
throw std::runtime_error("Illegal message type access");
}
return *static_cast<T*>(data);
}
template <typename T> T const* get_if() const {
static_assert(TypeTrait<T>::type != Type::None, "Unhandled type!");
if (TypeTrait<T>::type != type) {
return nullptr;
}
return static_cast<T*>(data);
}
private:
CustomDeleter custom_deleter{nullptr};
void* data{nullptr};
Type type{Type::None};
std::string error;
};
} // namespace iso15118::message_20

View File

@@ -0,0 +1,122 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <cmath>
#include <functional>
#include <optional>
#include <string>
#include <variant>
#include <iso15118/d20/ev_information.hpp>
#include <iso15118/d20/limits.hpp>
#include <iso15118/d20/session.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/dc_charge_parameter_discovery.hpp>
#include <iso15118/message/schedule_exchange.hpp>
#include <iso15118/message/service_detail.hpp>
#include <iso15118/message/service_selection.hpp>
#include <iso15118/message/type.hpp>
namespace iso15118::session {
namespace dt = message_20::datatypes;
namespace feedback {
enum class Signal {
REQUIRE_AUTH_EIM,
START_CABLE_CHECK,
SETUP_FINISHED,
PRE_CHARGE_STARTED,
CHARGE_LOOP_STARTED,
CHARGE_LOOP_FINISHED,
DC_OPEN_CONTACTOR,
AC_CLOSE_CONTACTOR,
AC_OPEN_CONTACTOR,
DLINK_TERMINATE,
DLINK_ERROR,
DLINK_PAUSE,
};
struct DcMaximumLimits {
float voltage{NAN};
float current{NAN};
float power{NAN};
};
using PresentVoltage = dt::RationalNumber;
using MeterInfoRequested = bool;
using DcReqControlMode = std::variant<dt::Scheduled_DC_CLReqControlMode, dt::BPT_Scheduled_DC_CLReqControlMode,
dt::Dynamic_DC_CLReqControlMode, dt::BPT_Dynamic_DC_CLReqControlMode>;
using AcReqControlMode = std::variant<dt::Scheduled_AC_CLReqControlMode, dt::BPT_Scheduled_AC_CLReqControlMode,
dt::Dynamic_AC_CLReqControlMode, dt::BPT_Dynamic_AC_CLReqControlMode>;
using DcChargeLoopReq = std::variant<DcReqControlMode, dt::DisplayParameters, PresentVoltage, MeterInfoRequested>;
using EvseTransferLimits = std::variant<d20::DcTransferLimits, d20::AcTransferLimits>;
using EvTransferLimits = std::variant<dt::DC_CPDReqEnergyTransferMode, dt::BPT_DC_CPDReqEnergyTransferMode,
dt::AC_CPDReqEnergyTransferMode, dt::BPT_AC_CPDReqEnergyTransferMode>;
using EvSEControlMode = std::variant<dt::Dynamic_SEReqControlMode, dt::Scheduled_SEReqControlMode>;
using AcChargeLoopReq = std::variant<AcReqControlMode, dt::DisplayParameters, MeterInfoRequested>;
using AcLimits = std::variant<dt::AC_CPDReqEnergyTransferMode, dt::BPT_AC_CPDReqEnergyTransferMode>;
struct Callbacks {
std::function<void(Signal)> signal;
std::function<void(float)> dc_pre_charge_target_voltage;
std::function<void(const DcChargeLoopReq&)> dc_charge_loop_req;
std::function<void(const DcMaximumLimits&)> dc_max_limits;
std::function<void(const AcChargeLoopReq&)> ac_charge_loop_req;
std::function<void(const message_20::Type&)> v2g_message;
std::function<void(const std::string&)> evccid;
std::function<void(const std::string&)> selected_protocol;
std::function<void(const dt::ServiceCategory&, const std::optional<dt::AcConnector>&, const dt::ControlMode&,
const dt::MobilityNeedsMode&, const EvseTransferLimits&, const EvTransferLimits&,
const EvSEControlMode&, const std::vector<message_20::datatypes::ServiceCategory>&)>
notify_ev_charging_needs;
std::function<void(const d20::SelectedServiceParameters&)> selected_service_parameters;
std::function<void(const d20::EVInformation&)> ev_information;
std::function<std::optional<dt::ServiceParameterList>(uint16_t)> get_vas_parameters;
std::function<void(const dt::VasSelectedServiceList&)> selected_vas_services;
std::function<void(const AcLimits&)> ac_limits;
std::function<void(const std::string&, const std::string&)> ev_termination;
};
} // namespace feedback
class Feedback {
public:
Feedback(feedback::Callbacks);
void signal(feedback::Signal) const;
void dc_pre_charge_target_voltage(float) const;
void dc_charge_loop_req(const feedback::DcChargeLoopReq&) const;
void dc_max_limits(const feedback::DcMaximumLimits&) const;
void ac_charge_loop_req(const feedback::AcChargeLoopReq&) const;
void v2g_message(const message_20::Type&) const;
void evcc_id(const std::string&) const;
void selected_protocol(const std::string&) const;
void notify_ev_charging_needs(const dt::ServiceCategory&, const std::optional<dt::AcConnector>&,
const dt::ControlMode&, const dt::MobilityNeedsMode&,
const feedback::EvseTransferLimits&, const feedback::EvTransferLimits&,
const feedback::EvSEControlMode&,
const std::vector<message_20::datatypes::ServiceCategory>&) const;
void selected_service_parameters(const d20::SelectedServiceParameters&) const;
void ev_information(const d20::EVInformation&) const;
std::optional<dt::ServiceParameterList> get_vas_parameters(uint16_t) const;
void selected_vas_services(const dt::VasSelectedServiceList&) const;
void ac_limits(const feedback::AcLimits&) const;
void ev_termination(const std::string&, const std::string&) const;
private:
feedback::Callbacks callbacks;
};
} // namespace iso15118::session

View File

@@ -0,0 +1,81 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <memory>
#include <optional>
#include <iso15118/config.hpp>
#include <everest/util/fsm/fsm.hpp>
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/context.hpp>
#include <iso15118/d20/control_event_queue.hpp>
#include <iso15118/d20/states.hpp>
#include <iso15118/io/connection_abstract.hpp>
#include <iso15118/io/poll_manager.hpp>
#include <iso15118/io/sdp_packet.hpp>
#include <iso15118/io/time.hpp>
#include <iso15118/session/feedback.hpp>
#include <iso15118/session/logger.hpp>
#include <iso15118/d20/timeout.hpp>
namespace iso15118 {
struct SessionState {
bool connected{false};
bool new_data{false};
bool fsm_needs_call{false};
};
class Session {
public:
Session(std::unique_ptr<io::IConnection>, d20::SessionConfig, const session::feedback::Callbacks&,
std::optional<d20::PauseContext>&);
~Session();
TimePoint const& poll();
void push_control_event(const d20::ControlEvent&);
bool is_finished() const {
return (ctx.session_stopped or ctx.session_paused) and not message_exchange.has_response();
}
void close();
private:
std::unique_ptr<io::IConnection> connection;
session::SessionLogger log;
SessionState state;
// input buffer
io::SdpPacket packet;
// output buffer
uint8_t response_buffer[1028];
d20::MessageExchange message_exchange{{response_buffer + io::SdpPacket::V2GTP_HEADER_SIZE,
sizeof(response_buffer) - io::SdpPacket::V2GTP_HEADER_SIZE}};
// control event buffer
d20::ControlEventQueue control_event_queue;
std::optional<d20::ControlEvent> active_control_event{std::nullopt};
d20::Context ctx;
fsm::v2::FSM<d20::StateBase> fsm;
TimePoint next_session_event;
d20::Timeouts timeouts;
void handle_connection_event(io::ConnectionEvent event);
void send_response();
std::optional<TimePoint> last_response_tx_time; // timestamp of the last response message sent
std::optional<TimePoint> response_send_after; // time point when the next response message can be sent
};
} // namespace iso15118

View File

@@ -0,0 +1,57 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <chrono>
#include <cstdint>
#include <functional>
#include <string>
#include <variant>
namespace iso15118::session {
namespace logging {
using TimePoint = std::chrono::time_point<std::chrono::system_clock>;
struct SimpleEvent {
TimePoint time_point;
std::string info;
};
enum class ExiMessageDirection {
FROM_EV,
TO_EV,
};
struct ExiMessageEvent {
TimePoint time_point;
uint16_t payload_type;
uint8_t const* data;
size_t len;
ExiMessageDirection direction;
};
using Event = std::variant<SimpleEvent, ExiMessageEvent>;
using Callback = std::function<void(std::size_t id, const Event&)>;
void set_session_log_callback(const Callback&);
} // namespace logging
class SessionLogger {
public:
SessionLogger(void*);
void enter_state(const std::string& new_state);
void event(const std::string& info) const;
void exi(uint16_t payload_type, uint8_t const* data, size_t len, logging::ExiMessageDirection direction) const;
void operator()(const std::string&) const;
void operator()(const char* format, ...) const;
private:
std::uintptr_t id;
std::string last_state_name;
};
} // namespace iso15118::session

View File

@@ -0,0 +1,72 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2023 Pionix GmbH and Contributors to EVerest
#pragma once
#include <list>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "config.hpp"
#include <iso15118/d20/config.hpp>
#include <iso15118/d20/control_event.hpp>
#include <iso15118/d20/limits.hpp>
#include <iso15118/io/poll_manager.hpp>
#include <iso15118/io/sdp_server.hpp>
#include <iso15118/io/time.hpp>
#include <iso15118/message/common_types.hpp>
#include <iso15118/session/feedback.hpp>
#include <iso15118/session/iso.hpp>
namespace iso15118 {
struct TbdConfig {
config::SSLConfig ssl{config::CertificateBackend::EVEREST_LAYOUT, {}, {}, {}, {}, {}, {}};
std::string interface_name;
config::TlsNegotiationStrategy tls_negotiation_strategy{config::TlsNegotiationStrategy::ACCEPT_CLIENT_OFFER};
bool enable_sdp_server{true};
};
class TbdController {
public:
TbdController(TbdConfig, session::feedback::Callbacks, d20::EvseSetupConfig);
void loop();
void send_control_event(const d20::ControlEvent&);
void update_authorization_services(const std::vector<message_20::datatypes::Authorization>& services,
bool cert_install_service);
void update_dc_limits(const d20::DcTransferLimits&);
void update_powersupply_limits(const d20::DcTransferLimits&);
void update_energy_modes(const std::vector<message_20::datatypes::ServiceCategory>&);
void update_ac_limits(const d20::AcTransferLimits&);
void update_supported_vas_services(const std::vector<uint16_t>& vas_services);
void set_dlink_ready(bool ready);
private:
io::PollManager poll_manager;
std::unique_ptr<io::SdpServer> sdp_server;
std::unique_ptr<Session> session;
// callbacks for sdp server
void handle_sdp_server_input();
const TbdConfig config;
const session::feedback::Callbacks callbacks;
d20::EvseSetupConfig evse_setup;
std::string interface_name;
std::optional<d20::PauseContext> pause_ctx{std::nullopt};
static constexpr uint32_t V2G_COMMUNICATION_SETUP_TIMEOUT_MS{18000};
std::optional<Timeout> communication_setup_timeout;
};
} // namespace iso15118