Files
cariflex/tools/EVerest-main/modules/EVSE/EvseV2G/charger/ISO15118_chargerImpl.cpp
Eric F d398a6ced2 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
2026-06-08 00:38:27 -04:00

653 lines
30 KiB
C++

// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2022-2023 chargebyte GmbH
// Copyright (C) 2022-2023 Contributors to EVerest
#include "ISO15118_chargerImpl.hpp"
#include "log.hpp"
#include "sdp.hpp"
#include "tools.hpp"
#include "v2g_ctx.hpp"
#include <algorithm>
#include <string.h>
#include <string_view>
const std::string CERTS_SUB_DIR = "certs"; // relativ path of the certs
using namespace std::chrono_literals;
using BidiMode = types::iso15118::SaeJ2847BidiMode;
namespace module {
namespace charger {
void ISO15118_chargerImpl::init() {
if (!v2g_ctx) {
dlog(DLOG_LEVEL_ERROR, "v2g_ctx not created");
return;
}
/* Configure if_name and auth_mode */
v2g_ctx->if_name = mod->config.device.data();
dlog(DLOG_LEVEL_DEBUG, "if_name %s", v2g_ctx->if_name);
/* Configure hlc_protocols */
if (mod->config.supported_DIN70121 == true) {
v2g_ctx->supported_protocols |= (1 << V2G_PROTO_DIN70121);
supp_app_protocols_secc.app_protocols.push_back(types::iso15118::SupportedAppProtocol::DIN70121);
}
if (mod->config.supported_ISO15118_2 == true) {
v2g_ctx->supported_protocols |= (1 << V2G_PROTO_ISO15118_2013);
supp_app_protocols_secc.app_protocols.push_back(types::iso15118::SupportedAppProtocol::ISO15118D2);
}
/* Configure tls_security */
if (mod->config.tls_security == "force") {
v2g_ctx->tls_security = TLS_SECURITY_FORCE;
dlog(DLOG_LEVEL_DEBUG, "tls_security force");
} else if (mod->config.tls_security == "allow") {
v2g_ctx->tls_security = TLS_SECURITY_ALLOW;
dlog(DLOG_LEVEL_DEBUG, "tls_security allow");
} else {
v2g_ctx->tls_security = TLS_SECURITY_PROHIBIT;
dlog(DLOG_LEVEL_DEBUG, "tls_security prohibit");
}
v2g_ctx->terminate_connection_on_failed_response = mod->config.terminate_connection_on_failed_response;
v2g_ctx->tls_key_logging = mod->config.tls_key_logging;
v2g_ctx->tls_key_logging_path = mod->config.tls_key_logging_path;
if (mod->config.tls_key_logging == true) {
dlog(DLOG_LEVEL_DEBUG, "tls-key-logging enabled (path: %s)", mod->config.tls_key_logging_path.c_str());
}
v2g_ctx->network_read_timeout_tls = mod->config.tls_timeout;
/* Configure if the contract certificate chain should be verified locally */
v2g_ctx->session.verify_contract_cert_chain = mod->config.verify_contract_cert_chain;
v2g_ctx->session.auth_timeout_eim = mod->config.auth_timeout_eim;
v2g_ctx->session.auth_timeout_pnc = mod->config.auth_timeout_pnc;
v2g_ctx->supported_vas_services_per_provider.reserve(mod->r_iso15118_vas.size());
for (size_t i = 0; i < mod->r_iso15118_vas.size(); i++) {
auto& supported_vas_services = v2g_ctx->supported_vas_services_per_provider.emplace_back();
this->mod->r_iso15118_vas.at(i)->subscribe_offered_vas(
[&supported_vas_services](const types::iso15118_vas::OfferedServices& offered_services) {
for (auto service : offered_services.services) {
const auto id = static_cast<uint16_t>(service.service_id);
if (id == V2G_SERVICE_ID_CHARGING) {
continue;
}
supported_vas_services.push_back(id);
iso2_ServiceType vas_service{};
init_iso2_ServiceType(&vas_service);
vas_service.ServiceID = id;
if (service.service_name.has_value()) {
strncpy_to_v2g(vas_service.ServiceName.characters, sizeof(vas_service.ServiceName.characters),
&vas_service.ServiceName.charactersLen, service.service_name.value());
vas_service.ServiceName_isUsed = 1;
}
if (service.service_scope.has_value()) {
strncpy_to_v2g(vas_service.ServiceScope.characters, sizeof(vas_service.ServiceScope.characters),
&vas_service.ServiceScope.charactersLen, service.service_scope.value());
vas_service.ServiceScope_isUsed = 1;
}
vas_service.FreeService = service.free_service.value_or(true);
if (id == V2G_SERVICE_ID_CERTIFICATE) {
vas_service.ServiceCategory = iso2_serviceCategoryType_ContractCertificate;
} else if (id == V2G_SERVICE_ID_INTERNET) {
vas_service.ServiceCategory = iso2_serviceCategoryType_Internet;
strncpy_to_v2g(vas_service.ServiceName.characters, sizeof(vas_service.ServiceName.characters),
&vas_service.ServiceName.charactersLen, "InternetAccess");
vas_service.ServiceName_isUsed = true;
} else {
vas_service.ServiceCategory = iso2_serviceCategoryType_OtherCustom;
}
if (not add_service_to_service_list(v2g_ctx, vas_service)) {
break;
}
}
});
}
}
void ISO15118_chargerImpl::ready() {
publish_supported_app_protocols_secc(supp_app_protocols_secc);
}
void ISO15118_chargerImpl::handle_setup(types::iso15118::EVSEID& evse_id,
types::iso15118::SaeJ2847BidiMode& sae_j2847_mode, bool& debug_mode) {
uint8_t len = evse_id.evse_id.length();
if (len < iso2_EVSEID_CHARACTER_SIZE) {
memcpy(v2g_ctx->evse_v2g_data.evse_id.bytes, reinterpret_cast<uint8_t*>(evse_id.evse_id.data()), len);
v2g_ctx->evse_v2g_data.evse_id.bytesLen = len;
} else {
dlog(DLOG_LEVEL_WARNING, "EVSEID_CHARACTER_SIZE exceeded (received: %u, max: %u)", len,
iso2_EVSEID_CHARACTER_SIZE);
}
v2g_ctx->debugMode = debug_mode;
if (sae_j2847_mode == BidiMode::V2H || sae_j2847_mode == BidiMode::V2G) {
struct iso2_ServiceType sae_service;
init_iso2_ServiceType(&sae_service);
sae_service.FreeService = 1;
sae_service.ServiceCategory = iso2_serviceCategoryType_OtherCustom;
if (sae_j2847_mode == BidiMode::V2H) {
sae_service.ServiceID = 28472;
strncpy_to_v2g(sae_service.ServiceName.characters, sizeof(sae_service.ServiceName.characters),
&sae_service.ServiceName.charactersLen, "SAE J2847/2 V2H");
sae_service.ServiceName_isUsed = true;
} else {
sae_service.ServiceID = 28473;
strncpy_to_v2g(sae_service.ServiceName.characters, sizeof(sae_service.ServiceName.characters),
&sae_service.ServiceName.charactersLen, "SAE J2847/2 V2G");
sae_service.ServiceName_isUsed = true;
}
add_service_to_service_list(v2g_ctx, sae_service);
}
}
void ISO15118_chargerImpl::handle_set_charging_parameters(types::iso15118::SetupPhysicalValues& physical_values) {
if (physical_values.ac_nominal_voltage.has_value()) {
populate_physical_value_float(&v2g_ctx->evse_v2g_data.evse_nominal_voltage,
physical_values.ac_nominal_voltage.value(), 1, iso2_unitSymbolType_V);
v2g_ctx->basic_config.evse_ac_nominal_voltage = physical_values.ac_nominal_voltage.value();
}
if (physical_values.dc_current_regulation_tolerance.has_value()) {
populate_physical_value_float(&v2g_ctx->evse_v2g_data.evse_current_regulation_tolerance,
physical_values.dc_current_regulation_tolerance.value(), 1,
iso2_unitSymbolType_A);
v2g_ctx->evse_v2g_data.evse_current_regulation_tolerance_is_used = 1;
}
if (physical_values.dc_peak_current_ripple.has_value()) {
populate_physical_value_float(&v2g_ctx->evse_v2g_data.evse_peak_current_ripple,
physical_values.dc_peak_current_ripple.value(), 1, iso2_unitSymbolType_A);
}
if (physical_values.dc_energy_to_be_delivered.has_value()) {
populate_physical_value(&v2g_ctx->evse_v2g_data.evse_energy_to_be_delivered,
physical_values.dc_energy_to_be_delivered.value(), iso2_unitSymbolType_Wh);
v2g_ctx->evse_v2g_data.evse_energy_to_be_delivered_is_used = 1;
}
}
void ISO15118_chargerImpl::handle_session_setup(std::vector<types::iso15118::PaymentOption>& payment_options,
bool& supported_certificate_service,
bool& central_contract_validation_allowed) {
if (not v2g_ctx->hlc_pause_active) {
v2g_ctx->evse_v2g_data.payment_option_list.clear();
if (not payment_options.empty()) {
const auto max_payment_options =
std::min(static_cast<size_t>(iso2_paymentOptionType_2_ARRAY_SIZE), payment_options.size());
for (size_t i = 0; i < max_payment_options; i++) {
const auto& payment_option = payment_options.at(i);
if (payment_option == types::iso15118::PaymentOption::ExternalPayment) {
v2g_ctx->evse_v2g_data.payment_option_list.push_back(iso2_paymentOptionType_ExternalPayment);
} else if (payment_option == types::iso15118::PaymentOption::Contract) {
v2g_ctx->evse_v2g_data.payment_option_list.push_back(iso2_paymentOptionType_Contract);
} else {
dlog(DLOG_LEVEL_WARNING, "Unable to configure PaymentOption %s",
types::iso15118::payment_option_to_string(payment_option).c_str());
}
}
}
if (payment_options.empty() or v2g_ctx->evse_v2g_data.payment_option_list.empty()) {
dlog(DLOG_LEVEL_ERROR, "No valid PaymentOptions configured, falling back to ExternalPayment");
v2g_ctx->evse_v2g_data.payment_option_list.clear();
v2g_ctx->evse_v2g_data.payment_option_list.push_back(iso2_paymentOptionType_ExternalPayment);
}
}
const auto pnc_enabled =
std::find(v2g_ctx->evse_v2g_data.payment_option_list.begin(), v2g_ctx->evse_v2g_data.payment_option_list.end(),
iso2_paymentOptionType_Contract) != v2g_ctx->evse_v2g_data.payment_option_list.end();
using state_t = tls::Server::state_t;
const auto tls_server_state = v2g_ctx->tls_server->state();
const auto tls_server_available =
(tls_server_state == state_t::init_complete or tls_server_state == state_t::running);
if (pnc_enabled and supported_certificate_service and tls_server_available) {
// For setting "Certificate" in ServiceList in ServiceDiscoveryRes
struct iso2_ServiceType cert_service;
init_iso2_ServiceType(&cert_service);
const int16_t cert_parameter_set_id[] = {1}; // parameter-set-ID 1: "Installation" service. TODO: Support of the
// "Update" service (parameter-set-ID 2)
cert_service.FreeService = 1; // true
cert_service.ServiceID = 2; // as defined in ISO 15118-2
cert_service.ServiceCategory = iso2_serviceCategoryType_ContractCertificate;
strncpy_to_v2g(cert_service.ServiceName.characters, sizeof(cert_service.ServiceName.characters),
&cert_service.ServiceName.charactersLen, "Certificate");
cert_service.ServiceName_isUsed = true;
add_service_to_service_list(v2g_ctx, cert_service, cert_parameter_set_id,
sizeof(cert_parameter_set_id) / sizeof(cert_parameter_set_id[0]));
} else {
// Make sure Certificate service is not in ServiceList when pnc is not possible
remove_service_from_service_list_if_exists(v2g_ctx, V2G_SERVICE_ID_CERTIFICATE);
}
// Reset authorization that may have been provided after the TCP was closed, potentially causing
// authorization to be reused in the next session. Resetting it here prevents that.
v2g_ctx->evse_v2g_data.evse_processing[PHASE_AUTH] = (uint8_t)iso2_EVSEProcessingType_Ongoing;
v2g_ctx->evse_v2g_data.central_contract_validation_allowed = central_contract_validation_allowed;
}
void ISO15118_chargerImpl::handle_bpt_setup(types::iso15118::BptSetup& bpt_config) {
EVLOG_warning << "Ignoring handle_bpt_setup call";
}
void ISO15118_chargerImpl::handle_set_powersupply_capabilities(types::power_supply_DC::Capabilities& capabilities) {
const auto max_export_current =
capabilities.nominal_max_export_current_A.value_or(capabilities.max_export_current_A);
const auto min_export_current =
capabilities.nominal_min_export_current_A.value_or(capabilities.min_export_current_A);
const auto max_export_power = capabilities.nominal_max_export_power_W.value_or(capabilities.max_export_power_W);
const auto max_export_voltage =
capabilities.nominal_max_export_voltage_V.value_or(capabilities.max_export_voltage_V);
const auto min_export_voltage =
capabilities.nominal_min_export_voltage_V.value_or(capabilities.min_export_voltage_V);
populate_physical_value_float(&v2g_ctx->evse_v2g_data.power_capabilities.max_current, max_export_current, 1,
iso2_unitSymbolType_A);
populate_physical_value_float(&v2g_ctx->evse_v2g_data.power_capabilities.min_current, min_export_current, 1,
iso2_unitSymbolType_A);
populate_physical_value(&v2g_ctx->evse_v2g_data.power_capabilities.max_power,
static_cast<uint32_t>(max_export_power), iso2_unitSymbolType_W);
populate_physical_value_float(&v2g_ctx->evse_v2g_data.power_capabilities.max_voltage, max_export_voltage, 1,
iso2_unitSymbolType_V);
populate_physical_value_float(&v2g_ctx->evse_v2g_data.power_capabilities.min_voltage, min_export_voltage, 1,
iso2_unitSymbolType_V);
}
void ISO15118_chargerImpl::handle_authorization_response(
types::authorization::AuthorizationStatus& authorization_status,
types::authorization::CertificateStatus& certificate_status) {
v2g_ctx->evse_v2g_data.evse_processing[PHASE_AUTH] = static_cast<uint8_t>(iso2_EVSEProcessingType_Finished);
if (authorization_status != types::authorization::AuthorizationStatus::Accepted) {
v2g_ctx->session.authorization_rejected = true;
}
if (v2g_ctx->session.iso_selected_payment_option == iso2_paymentOptionType_Contract) {
v2g_ctx->session.certificate_status = certificate_status;
}
}
void ISO15118_chargerImpl::handle_ac_contactor_closed(bool& status) {
/* signal changes to possible waiters, according to man page, it never returns an error code */
pthread_mutex_lock(&v2g_ctx->mqtt_lock);
v2g_ctx->contactor_is_closed = status;
pthread_cond_signal(&v2g_ctx->mqtt_cond);
/* unlock */
pthread_mutex_unlock(&v2g_ctx->mqtt_lock);
}
void ISO15118_chargerImpl::handle_dlink_ready(bool& value) {
sdp_set_dlink_ready(v2g_ctx, value);
// If dlink becomes not ready (false), stop TCP connection in the read thread
if (!value) {
v2g_ctx->is_connection_terminated = true;
}
}
void ISO15118_chargerImpl::handle_cable_check_finished(bool& status) {
if (status == true) {
v2g_ctx->evse_v2g_data.evse_processing[PHASE_ISOLATION] = (uint8_t)iso2_EVSEProcessingType_Finished;
} else {
v2g_ctx->evse_v2g_data.evse_processing[PHASE_ISOLATION] = (uint8_t)iso2_EVSEProcessingType_Ongoing;
}
}
void ISO15118_chargerImpl::handle_receipt_is_required(bool& receipt_required) {
v2g_ctx->evse_v2g_data.receipt_required = (int)receipt_required;
}
void ISO15118_chargerImpl::handle_stop_charging(bool& stop) {
// FIXME we need to use locks on v2g-ctx in all commands as they are running in different threads
if (stop) {
// spawn new thread to not block command handler
std::thread([stop] {
// try to gracefully shutdown charging session
v2g_ctx->evse_v2g_data.evse_notification = iso2_EVSENotificationType_StopCharging;
memset(v2g_ctx->evse_v2g_data.evse_status_code, iso2_DC_EVSEStatusCodeType_EVSE_Shutdown,
sizeof(v2g_ctx->evse_v2g_data.evse_status_code));
int i;
bool timeout_reached = true;
// allow 10 seconds for graceful shutdown
for (i = 0; i < 10; i++) {
if (v2g_ctx->is_connection_terminated) {
timeout_reached = false;
break;
}
std::this_thread::sleep_for(1s);
}
// If it did not stop within timeout, stop session using FAILED reply
if (timeout_reached) {
v2g_ctx->stop_hlc = stop;
}
}).detach();
} else {
v2g_ctx->stop_hlc = false;
}
}
void ISO15118_chargerImpl::handle_pause_charging(bool& pause) {
EVLOG_warning << "Pause initialized by the charger is not supported in DIN70121 and ISO15118-2";
}
void ISO15118_chargerImpl::handle_no_energy_pause_charging(types::iso15118::NoEnergyPauseMode& mode) {
switch (mode) {
case types::iso15118::NoEnergyPauseMode::PauseAfterPrecharge:
v2g_ctx->evse_v2g_data.no_energy_pause = NoEnergyPauseStatus::AfterCableCheckPreCharge;
break;
case types::iso15118::NoEnergyPauseMode::PauseBeforeCableCheck:
v2g_ctx->evse_v2g_data.no_energy_pause = NoEnergyPauseStatus::BeforeCableCheck;
break;
case types::iso15118::NoEnergyPauseMode::AllowEvToIgnorePause:
v2g_ctx->evse_v2g_data.no_energy_pause = NoEnergyPauseStatus::AllowEvToIgnorePause;
break;
}
}
bool ISO15118_chargerImpl::handle_update_supported_app_protocols(
types::iso15118::SupportedAppProtocols& supported_app_protocols) {
bool rv{true};
v2g_ctx->supported_protocols = 0;
if (supported_app_protocols.app_protocols.empty()) {
dlog(DLOG_LEVEL_WARNING, "No supported app protocols configured");
return true;
}
std::string configured_protocols;
for (const auto& protocol : supported_app_protocols.app_protocols) {
if (!configured_protocols.empty()) {
configured_protocols += ", ";
}
configured_protocols += types::iso15118::supported_app_protocol_to_string(protocol);
}
dlog(DLOG_LEVEL_INFO, "Configured charging protocols: [%s]", configured_protocols.c_str());
for (const auto& protocol : supported_app_protocols.app_protocols) {
// Check if the supported app protocol is in the SECC list
const bool allowed = std::find(this->supp_app_protocols_secc.app_protocols.begin(),
this->supp_app_protocols_secc.app_protocols.end(),
protocol) != this->supp_app_protocols_secc.app_protocols.end();
if (!allowed) {
dlog(DLOG_LEVEL_WARNING, "Skip unsupported app protocol: %s",
types::iso15118::supported_app_protocol_to_string(protocol).c_str());
rv = false;
continue;
}
/* Configure supported app bitmask. This bitmark is used in the supportedAppHandshake handle
to select the protocol */
switch (protocol) {
case types::iso15118::SupportedAppProtocol::DIN70121:
v2g_ctx->supported_protocols |= (1 << V2G_PROTO_DIN70121);
break;
case types::iso15118::SupportedAppProtocol::ISO15118D2:
v2g_ctx->supported_protocols |= (1 << V2G_PROTO_ISO15118_2013);
break;
case types::iso15118::SupportedAppProtocol::ISO15118D20:
default:
dlog(DLOG_LEVEL_WARNING, "Unsupported app protocol: %s",
types::iso15118::supported_app_protocol_to_string(protocol).c_str());
rv = false;
}
}
if (v2g_ctx->supported_protocols == 0) {
dlog(DLOG_LEVEL_WARNING, "No supported app protocols provided");
rv = false;
}
return rv;
}
void ISO15118_chargerImpl::handle_update_energy_transfer_modes(
std::vector<types::iso15118::EnergyTransferMode>& supported_energy_transfer_modes) {
if (v2g_ctx->hlc_pause_active) {
return;
}
uint16_t& energyArrayLen =
(v2g_ctx->evse_v2g_data.charge_service.SupportedEnergyTransferMode.EnergyTransferMode.arrayLen);
iso2_EnergyTransferModeType* energyArray =
v2g_ctx->evse_v2g_data.charge_service.SupportedEnergyTransferMode.EnergyTransferMode.array;
energyArrayLen = 0;
v2g_ctx->is_dc_charger = true;
const auto max_supported_energy_modes =
std::min(static_cast<size_t>(iso2_EnergyTransferModeType_6_ARRAY_SIZE), supported_energy_transfer_modes.size());
for (size_t i = 0; i < max_supported_energy_modes; i++) {
const auto& mode = supported_energy_transfer_modes.at(i);
switch (mode) {
case types::iso15118::EnergyTransferMode::AC_single_phase_core:
energyArray[(energyArrayLen)++] = iso2_EnergyTransferModeType_AC_single_phase_core;
v2g_ctx->is_dc_charger = false;
break;
case types::iso15118::EnergyTransferMode::AC_three_phase_core:
energyArray[(energyArrayLen)++] = iso2_EnergyTransferModeType_AC_three_phase_core;
v2g_ctx->is_dc_charger = false;
break;
case types::iso15118::EnergyTransferMode::DC_core:
energyArray[(energyArrayLen)++] = iso2_EnergyTransferModeType_DC_core;
break;
case types::iso15118::EnergyTransferMode::DC_extended:
energyArray[(energyArrayLen)++] = iso2_EnergyTransferModeType_DC_extended;
break;
case types::iso15118::EnergyTransferMode::DC_combo_core:
energyArray[(energyArrayLen)++] = iso2_EnergyTransferModeType_DC_combo_core;
break;
case types::iso15118::EnergyTransferMode::DC_unique:
energyArray[(energyArrayLen)++] = iso2_EnergyTransferModeType_DC_unique;
break;
case types::iso15118::EnergyTransferMode::DC_ACDP_BPT:
case types::iso15118::EnergyTransferMode::AC_BPT:
case types::iso15118::EnergyTransferMode::AC_BPT_DER:
case types::iso15118::EnergyTransferMode::DC_BPT:
dlog(DLOG_LEVEL_INFO, "Ignoring bidirectional SupportedEnergyTransferMode");
default:
if (energyArrayLen == 0) {
dlog(DLOG_LEVEL_WARNING, "Unable to configure SupportedEnergyTransferMode %s",
types::iso15118::energy_transfer_mode_to_string(mode).c_str());
}
break;
}
}
if (mod->config.supported_DIN70121 and not v2g_ctx->is_dc_charger) {
v2g_ctx->supported_protocols &= ~(1 << V2G_PROTO_DIN70121);
dlog(DLOG_LEVEL_WARNING, "Removed DIN70121 from the list of supported protocols as AC is enabled");
}
}
void ISO15118_chargerImpl::handle_update_ac_max_current(double& max_current) {
v2g_ctx->basic_config.evse_ac_current_limit = max_current;
}
void ISO15118_chargerImpl::handle_update_ac_parameters(types::iso15118::AcParameters& ac_parameters) {
static bool warning_shown = false;
if (not warning_shown) {
EVLOG_warning << "Ignoring handle_update_ac_parameters call";
warning_shown = true;
}
}
void ISO15118_chargerImpl::handle_update_ac_maximum_limits(types::iso15118::AcEvseMaximumPower& maximum_limits) {
if (v2g_ctx->basic_config.evse_ac_nominal_voltage > 0.0f) {
v2g_ctx->basic_config.evse_ac_nominal_current =
maximum_limits.charge_power.total / v2g_ctx->basic_config.evse_ac_nominal_voltage;
} else {
EVLOG_error << "Cannot compute nominal current: nominal voltage is 0";
}
}
void ISO15118_chargerImpl::handle_update_ac_minimum_limits(types::iso15118::AcEvseMinimumPower& minimum_limits) {
static bool warning_shown = false;
if (not warning_shown) {
EVLOG_warning << "Ignoring handle_update_ac_minimum_limits call";
warning_shown = true;
}
}
void ISO15118_chargerImpl::handle_update_ac_target_values(types::iso15118::AcTargetValues& target_values) {
static bool warning_shown = false;
if (not warning_shown) {
EVLOG_warning << "Ignoring handle_update_ac_target_values call";
warning_shown = true;
}
}
void ISO15118_chargerImpl::handle_update_ac_present_power(types::units::Power& present_power) {
static bool warning_shown = false;
if (not warning_shown) {
EVLOG_warning << "Ignoring handle_update_ac_present_power call";
warning_shown = true;
}
}
void ISO15118_chargerImpl::handle_update_dc_maximum_limits(types::iso15118::DcEvseMaximumLimits& maximum_limits) {
if (maximum_limits.evse_maximum_current_limit > 300.) {
populate_physical_value_float(&v2g_ctx->evse_v2g_data.evse_maximum_current_limit,
maximum_limits.evse_maximum_current_limit, 1, iso2_unitSymbolType_A);
} else {
populate_physical_value_float(&v2g_ctx->evse_v2g_data.evse_maximum_current_limit,
maximum_limits.evse_maximum_current_limit, 2, iso2_unitSymbolType_A);
}
v2g_ctx->evse_v2g_data.evse_maximum_current_limit_is_used = 1;
populate_physical_value(&v2g_ctx->evse_v2g_data.evse_maximum_power_limit, maximum_limits.evse_maximum_power_limit,
iso2_unitSymbolType_W);
v2g_ctx->evse_v2g_data.evse_maximum_power_limit_is_used = 1;
populate_physical_value_float(&v2g_ctx->evse_v2g_data.evse_maximum_voltage_limit,
maximum_limits.evse_maximum_voltage_limit, 1, iso2_unitSymbolType_V);
v2g_ctx->evse_v2g_data.evse_maximum_voltage_limit_is_used = 1;
}
void ISO15118_chargerImpl::handle_update_dc_minimum_limits(types::iso15118::DcEvseMinimumLimits& minimum_limits) {
populate_physical_value_float(&v2g_ctx->evse_v2g_data.evse_minimum_current_limit,
static_cast<long long int>(minimum_limits.evse_minimum_current_limit), 1,
iso2_unitSymbolType_A);
populate_physical_value_float(&v2g_ctx->evse_v2g_data.evse_minimum_voltage_limit,
static_cast<long long int>(minimum_limits.evse_minimum_voltage_limit), 1,
iso2_unitSymbolType_V);
}
void ISO15118_chargerImpl::handle_update_isolation_status(types::iso15118::IsolationStatus& isolation_status) {
v2g_ctx->evse_v2g_data.evse_isolation_status = (uint8_t)isolation_status;
v2g_ctx->evse_v2g_data.evse_isolation_status_is_used = 1;
}
void ISO15118_chargerImpl::handle_update_dc_present_values(
types::iso15118::DcEvsePresentVoltageCurrent& present_voltage_current) {
populate_physical_value_float(&v2g_ctx->evse_v2g_data.evse_present_voltage,
present_voltage_current.evse_present_voltage, 1, iso2_unitSymbolType_V);
if (present_voltage_current.evse_present_current.has_value()) {
populate_physical_value_float(&v2g_ctx->evse_v2g_data.evse_present_current,
static_cast<float>(present_voltage_current.evse_present_current.value()), 1,
iso2_unitSymbolType_A);
}
}
void ISO15118_chargerImpl::handle_update_meter_info(types::powermeter::Powermeter& powermeter) {
// Signal for ChargingStatus and CurrentDemand that MeterInfo is used
v2g_ctx->meter_info.meter_info_is_used = 1;
v2g_ctx->meter_info.meter_reading = powermeter.energy_Wh_import.total;
if (powermeter.meter_id) {
uint8_t len = powermeter.meter_id->length();
if (len < iso2_MeterID_CHARACTER_SIZE) {
memcpy(v2g_ctx->meter_info.meter_id.bytes, powermeter.meter_id->c_str(), len);
v2g_ctx->meter_info.meter_id.bytesLen = len;
} else {
dlog(DLOG_LEVEL_WARNING, "MeterID_CHARACTER_SIZEexceeded (received: %u, max: %u)", len,
iso2_MeterID_CHARACTER_SIZE);
}
}
}
void ISO15118_chargerImpl::handle_send_error(types::iso15118::EvseError& error) {
switch (error) {
case types::iso15118::EvseError::Error_Contactor:
break;
case types::iso15118::EvseError::Error_RCD:
v2g_ctx->evse_v2g_data.rcd = 1;
break;
case types::iso15118::EvseError::Error_UtilityInterruptEvent:
memset(v2g_ctx->evse_v2g_data.evse_status_code, (int)iso2_DC_EVSEStatusCodeType_EVSE_UtilityInterruptEvent,
sizeof(v2g_ctx->evse_v2g_data.evse_status_code));
break;
case types::iso15118::EvseError::Error_Malfunction:
memset(v2g_ctx->evse_v2g_data.evse_status_code, (int)iso2_DC_EVSEStatusCodeType_EVSE_Malfunction,
sizeof(v2g_ctx->evse_v2g_data.evse_status_code));
break;
case types::iso15118::EvseError::Error_EmergencyShutdown:
/* signal changes to possible waiters, according to man page, it never returns an error code */
pthread_mutex_lock(&v2g_ctx->mqtt_lock);
v2g_ctx->intl_emergency_shutdown = true;
pthread_cond_signal(&v2g_ctx->mqtt_cond);
/* unlock */
pthread_mutex_unlock(&v2g_ctx->mqtt_lock);
break;
default:
break;
}
}
void ISO15118_chargerImpl::handle_reset_error() {
v2g_ctx->evse_v2g_data.rcd = 0;
v2g_ctx->evse_v2g_data.evse_status_code[PHASE_INIT] = iso2_DC_EVSEStatusCodeType_EVSE_NotReady;
v2g_ctx->evse_v2g_data.evse_status_code[PHASE_AUTH] = iso2_DC_EVSEStatusCodeType_EVSE_NotReady;
v2g_ctx->evse_v2g_data.evse_status_code[PHASE_PARAMETER] = iso2_DC_EVSEStatusCodeType_EVSE_Ready; // [V2G-DC-453]
v2g_ctx->evse_v2g_data.evse_status_code[PHASE_ISOLATION] =
iso2_DC_EVSEStatusCodeType_EVSE_IsolationMonitoringActive;
v2g_ctx->evse_v2g_data.evse_status_code[PHASE_PRECHARGE] = iso2_DC_EVSEStatusCodeType_EVSE_Ready;
v2g_ctx->evse_v2g_data.evse_status_code[PHASE_CHARGE] = iso2_DC_EVSEStatusCodeType_EVSE_Ready;
v2g_ctx->evse_v2g_data.evse_status_code[PHASE_WELDING] = iso2_DC_EVSEStatusCodeType_EVSE_NotReady;
v2g_ctx->evse_v2g_data.evse_status_code[PHASE_STOP] = iso2_DC_EVSEStatusCodeType_EVSE_NotReady;
// Todo(sl): check if emergency should be cleared here?
}
} // namespace charger
} // namespace module