Files
cariflex/tools/EVerest-main/modules/EVSE/EvseV2G/v2g_ctx.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

578 lines
26 KiB
C++

// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2022-2023 chargebyte GmbH
// Copyright (C) 2022-2023 Contributors to EVerest
#include <cstdlib>
#include <cstring>
#include <dirent.h>
#include <errno.h>
#include <math.h>
#include <unistd.h> // sleep
#include "log.hpp"
#include "tools.hpp"
#include "v2g_ctx.hpp"
#include <cbv2g/iso_2/iso2_msgDefDatatypes.h>
void init_physical_value(struct iso2_PhysicalValueType* const physicalValue, iso2_unitSymbolType unit) {
physicalValue->Multiplier = 0;
physicalValue->Unit = unit;
physicalValue->Value = 0;
}
// Only for AC
bool populate_physical_value(struct iso2_PhysicalValueType* pv, long long int value, iso2_unitSymbolType unit) {
struct iso2_PhysicalValueType physic_tmp = {pv->Multiplier, pv->Unit, pv->Value}; // To restore
pv->Unit = unit;
pv->Multiplier = 0; // with integers, we don't need negative multipliers for precision, so start at 0
// if the value is too large to be represented in 16 signed bits, increase the multiplier
while ((value > INT16_MAX) || (value < INT16_MIN)) {
pv->Multiplier++;
value /= 10;
}
if ((pv->Multiplier < PHY_VALUE_MULT_MIN) || (pv->Multiplier > PHY_VALUE_MULT_MAX)) {
memcpy(pv, &physic_tmp, sizeof(struct iso2_PhysicalValueType));
dlog(DLOG_LEVEL_WARNING, "Physical value out of scope. Ignore value");
return false;
}
pv->Value = value;
return true;
}
void populate_physical_value_float(struct iso2_PhysicalValueType* pv, float value, uint8_t decimal_places,
iso2_unitSymbolType unit) {
if (false == populate_physical_value(pv, (long long int)value, unit)) {
return;
}
if (pv->Multiplier == 0) {
for (uint8_t idx = 0; idx < decimal_places; idx++) {
if (((long int)(value * 10) < INT16_MAX) && ((long int)(value * 10) > INT16_MIN)) {
pv->Multiplier--;
value *= 10;
}
}
}
if (pv->Multiplier != -decimal_places) {
dlog(DLOG_LEVEL_WARNING,
"Possible precision loss while converting to physical value type, requested %i, actual %i (value %f)",
decimal_places, -pv->Multiplier, value);
}
pv->Value = value;
}
static void* v2g_ctx_eventloop(void* data) {
struct v2g_context* ctx = static_cast<struct v2g_context*>(data);
while (!ctx->shutdown) {
int rv;
rv = event_base_loop(ctx->event_base, 0);
if (rv == -1)
break;
/* if no events are registered, restart looping */
if (rv == 1)
sleep(1); /* FIXME this is bad since we actually do busy-waiting here */
}
return NULL;
}
static int v2g_ctx_start_events(struct v2g_context* ctx) {
int rv = pthread_create(&ctx->event_thread, NULL, v2g_ctx_eventloop, ctx);
return rv ? -1 : 0;
}
void v2g_ctx_init_charging_session(struct v2g_context* const ctx, bool is_connection_terminated) {
v2g_ctx_init_charging_state(ctx, is_connection_terminated); // Init charging state
v2g_ctx_init_charging_values(ctx); // Loads the internal default config
}
void v2g_ctx_init_charging_state(struct v2g_context* const ctx, bool is_connection_terminated) {
ctx->stop_hlc = false;
ctx->intl_emergency_shutdown = false;
ctx->is_connection_terminated = is_connection_terminated;
ctx->last_v2g_msg = V2G_UNKNOWN_MSG;
ctx->current_v2g_msg = V2G_UNKNOWN_MSG;
ctx->state = 0; // WAIT_FOR_SESSIONSETUP
ctx->selected_protocol = V2G_UNKNOWN_PROTOCOL;
ctx->session.renegotiation_required = false;
ctx->session.is_charging = false;
ctx->evse_v2g_data.evse_notification = (uint8_t)0;
}
void v2g_ctx_init_charging_values(struct v2g_context* const ctx) {
static bool initialize_once = false;
if (ctx->hlc_pause_active != true) {
ctx->evse_v2g_data.session_id =
(uint64_t)0; /* store associated session id, this is zero until SessionSetupRes is sent */
}
ctx->evse_v2g_data.notification_max_delay = (uint32_t)0;
ctx->evse_v2g_data.evse_isolation_status = (uint8_t)iso2_isolationLevelType_Invalid;
ctx->evse_v2g_data.evse_isolation_status_is_used = (unsigned int)1; // Shall be used in DIN
ctx->evse_v2g_data.evse_notification = (uint8_t)0;
ctx->evse_v2g_data.evse_status_code[PHASE_INIT] = iso2_DC_EVSEStatusCodeType_EVSE_NotReady;
ctx->evse_v2g_data.evse_status_code[PHASE_AUTH] = iso2_DC_EVSEStatusCodeType_EVSE_NotReady;
ctx->evse_v2g_data.evse_status_code[PHASE_PARAMETER] = iso2_DC_EVSEStatusCodeType_EVSE_Ready; // [V2G-DC-453]
ctx->evse_v2g_data.evse_status_code[PHASE_ISOLATION] = iso2_DC_EVSEStatusCodeType_EVSE_IsolationMonitoringActive;
ctx->evse_v2g_data.evse_status_code[PHASE_PRECHARGE] = iso2_DC_EVSEStatusCodeType_EVSE_Ready;
ctx->evse_v2g_data.evse_status_code[PHASE_CHARGE] = iso2_DC_EVSEStatusCodeType_EVSE_Ready;
ctx->evse_v2g_data.evse_status_code[PHASE_WELDING] = iso2_DC_EVSEStatusCodeType_EVSE_NotReady;
ctx->evse_v2g_data.evse_status_code[PHASE_STOP] = iso2_DC_EVSEStatusCodeType_EVSE_NotReady;
memset(ctx->evse_v2g_data.evse_processing, iso2_EVSEProcessingType_Ongoing, PHASE_LENGTH);
ctx->evse_v2g_data.evse_processing[PHASE_PARAMETER] = iso2_EVSEProcessingType_Finished; // Skip parameter phase
if (ctx->hlc_pause_active != true) {
ctx->evse_v2g_data.charge_service.ServiceCategory = iso2_serviceCategoryType_EVCharging;
ctx->evse_v2g_data.charge_service.ServiceID = (uint16_t)1;
strncpy_to_v2g(ctx->evse_v2g_data.charge_service.ServiceName.characters,
sizeof(ctx->evse_v2g_data.charge_service.ServiceName.characters),
&ctx->evse_v2g_data.charge_service.ServiceName.charactersLen,
// Conform to table 105 [V2G2-417]
"AC_DC_Charging");
ctx->evse_v2g_data.charge_service.ServiceName_isUsed = 0;
// ctx->evse_v2g_data.chargeService.ServiceScope.characters
// ctx->evse_v2g_data.chargeService.ServiceScope.charactersLen
ctx->evse_v2g_data.charge_service.ServiceScope_isUsed = (unsigned int)0;
}
ctx->meter_info.meter_info_is_used = false;
ctx->evse_v2g_data.service_parameter_list.clear();
if (initialize_once == false) {
ctx->evse_v2g_data.charge_service.FreeService = 0;
std::string evse_id = std::string("DE*CBY*ETE1*234");
strcpy(reinterpret_cast<char*>(ctx->evse_v2g_data.evse_id.bytes), evse_id.data());
ctx->evse_v2g_data.evse_id.bytesLen = evse_id.size();
ctx->evse_v2g_data.charge_service.SupportedEnergyTransferMode.EnergyTransferMode.array[0] =
iso2_EnergyTransferModeType_AC_single_phase_core;
ctx->evse_v2g_data.charge_service.SupportedEnergyTransferMode.EnergyTransferMode.arrayLen = 1;
ctx->evse_v2g_data.date_time_now_is_used = (unsigned int)0;
// evse power values
init_physical_value(&ctx->evse_v2g_data.evse_current_regulation_tolerance, iso2_unitSymbolType_A);
ctx->evse_v2g_data.evse_current_regulation_tolerance_is_used = (unsigned int)0; // optional in din
init_physical_value(&ctx->evse_v2g_data.evse_energy_to_be_delivered, iso2_unitSymbolType_Wh);
ctx->evse_v2g_data.evse_energy_to_be_delivered_is_used = (unsigned int)0; // optional in din
init_physical_value(&ctx->evse_v2g_data.evse_maximum_current_limit, iso2_unitSymbolType_A);
ctx->evse_v2g_data.evse_maximum_current_limit_is_used = (unsigned int)0;
ctx->evse_v2g_data.evse_current_limit_achieved = (int)0;
init_physical_value(&ctx->evse_v2g_data.evse_maximum_power_limit, iso2_unitSymbolType_W);
ctx->evse_v2g_data.evse_maximum_power_limit_is_used = (unsigned int)0;
ctx->evse_v2g_data.evse_power_limit_achieved = (int)0;
init_physical_value(&ctx->evse_v2g_data.evse_maximum_voltage_limit, iso2_unitSymbolType_V);
ctx->evse_v2g_data.evse_maximum_voltage_limit_is_used = (unsigned int)0; // mandatory
ctx->evse_v2g_data.evse_voltage_limit_achieved = (int)0;
init_physical_value(&ctx->evse_v2g_data.evse_minimum_current_limit, iso2_unitSymbolType_A);
init_physical_value(&ctx->evse_v2g_data.evse_minimum_voltage_limit, iso2_unitSymbolType_V);
init_physical_value(&ctx->evse_v2g_data.evse_peak_current_ripple, iso2_unitSymbolType_A);
// AC evse power values
init_physical_value(&ctx->evse_v2g_data.evse_nominal_voltage, iso2_unitSymbolType_V);
ctx->evse_v2g_data.rcd = (int)0; // 0 if RCD has not detected an error
ctx->contactor_is_closed = false;
ctx->evse_v2g_data.payment_option_list.clear();
ctx->evse_v2g_data.payment_option_list.reserve(iso2_paymentOptionType_2_ARRAY_SIZE);
ctx->evse_v2g_data.payment_option_list.push_back(iso2_paymentOptionType_ExternalPayment);
ctx->evse_v2g_data.evse_service_list.clear();
ctx->evse_v2g_data.evse_service_list.reserve(iso2_ServiceType_8_ARRAY_SIZE);
}
init_physical_value(&ctx->evse_v2g_data.evse_present_voltage, iso2_unitSymbolType_V);
init_physical_value(&ctx->evse_v2g_data.evse_present_current, iso2_unitSymbolType_A);
if (ctx->hlc_pause_active != true) {
// SAScheduleTupleID#PMaxScheduleTupleID#Start#Duration#PMax#
init_physical_value(&ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0]
.PMaxSchedule.PMaxScheduleEntry.array[0]
.PMax,
iso2_unitSymbolType_W);
ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0]
.PMaxSchedule.PMaxScheduleEntry.array[0]
.RelativeTimeInterval.duration = (uint32_t)0;
ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0]
.PMaxSchedule.PMaxScheduleEntry.array[0]
.RelativeTimeInterval.duration_isUsed = (unsigned int)1;
ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0]
.PMaxSchedule.PMaxScheduleEntry.array[0]
.RelativeTimeInterval.start = (uint32_t)0;
ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0]
.PMaxSchedule.PMaxScheduleEntry.array[0]
.RelativeTimeInterval_isUsed = (unsigned int)1; // Optional: In DIN/ISO it must be set to 1
ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0]
.PMaxSchedule.PMaxScheduleEntry.array[0]
.TimeInterval_isUsed = (unsigned int)0;
ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.arrayLen = 1;
ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0].SalesTariff_isUsed = (unsigned int)0;
ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0].SAScheduleTupleID =
(uint8_t)1; // [V2G2-773] 1 to 255
ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.arrayLen = (uint16_t)1;
ctx->evse_v2g_data.evse_sa_schedule_list_is_used = false;
// ctx->evse_v2g_data.evseSAScheduleTuple.SalesTariff
ctx->evse_v2g_data.evse_sa_schedule_list.SAScheduleTuple.array[0].SalesTariff_isUsed =
(unsigned int)0; // Not supported in DIN
} else {
ctx->evse_v2g_data.evse_sa_schedule_list_is_used = true;
}
if (ctx->evse_v2g_data.cert_install_res_b64_buffer.empty() == false) {
ctx->evse_v2g_data.cert_install_res_b64_buffer.clear();
}
// AC paramter
ctx->evse_v2g_data.rcd = (int)0; // 0 if RCD has not detected an error
ctx->contactor_is_closed = false;
ctx->evse_v2g_data.receipt_required = (int)0;
// Specific SAE J2847 bidi values
ctx->evse_v2g_data.sae_bidi_data.enabled_sae_v2g = false;
ctx->evse_v2g_data.sae_bidi_data.enabled_sae_v2h = false;
ctx->evse_v2g_data.sae_bidi_data.sae_v2h_minimal_soc = 20;
ctx->evse_v2g_data.sae_bidi_data.discharging = false;
ctx->evse_v2g_data.no_energy_pause = NoEnergyPauseStatus::None;
// Init EV received v2g-data to an invalid state
memset(&ctx->ev_v2g_data, 0xff, sizeof(ctx->ev_v2g_data));
/* Init session values */
if (ctx->hlc_pause_active != true) {
ctx->session.iso_selected_payment_option = iso2_paymentOptionType_ExternalPayment;
} else {
ctx->evse_v2g_data.payment_option_list.clear();
ctx->evse_v2g_data.payment_option_list.push_back(ctx->session.iso_selected_payment_option);
}
memset(ctx->session.gen_challenge, 0, sizeof(ctx->session.gen_challenge));
ctx->session.authorization_rejected = false;
remove_service_from_service_list_if_exists(ctx, V2G_SERVICE_ID_CERTIFICATE);
initialize_once = true;
}
struct v2g_context* v2g_ctx_create(ISO15118_chargerImplBase* p_chargerImplBase,
iso15118_extensionsImplBase* p_extensions, evse_securityIntf* r_security,
std::vector<ISO15118_vasIntf*> r_vas) {
struct v2g_context* ctx;
// TODO There are c++ objects within v2g_context and calloc doesn't call initialisers.
// free() will not call destructors
ctx = static_cast<v2g_context*>(calloc(1, sizeof(*ctx)));
if (!ctx)
return NULL;
ctx->r_security = r_security;
ctx->r_vas = r_vas;
ctx->p_charger = p_chargerImplBase;
ctx->p_extensions = p_extensions;
ctx->tls_security = TLS_SECURITY_PROHIBIT; // default
/* This evse parameter will be initialized once */
ctx->basic_config.evse_ac_current_limit = 0.0f;
ctx->basic_config.evse_ac_nominal_current = 0.0f;
ctx->basic_config.evse_ac_nominal_voltage = 230.0f;
ctx->local_tcp_addr = NULL;
ctx->local_tls_addr = NULL;
ctx->is_dc_charger = true;
v2g_ctx_init_charging_session(ctx, true);
/* interface from config file or options */
ctx->if_name = "eth1";
ctx->network_read_timeout = 1000;
ctx->network_read_timeout_tls = 5000;
ctx->sdp_socket = -1;
ctx->tcp_socket = -1;
ctx->tls_socket.fd = -1;
ctx->tls_key_logging = false;
ctx->debugMode = false;
/* according to man page, both functions never return an error */
evthread_use_pthreads();
pthread_mutex_init(&ctx->mqtt_lock, NULL);
pthread_condattr_init(&ctx->mqtt_attr);
pthread_condattr_setclock(&ctx->mqtt_attr, CLOCK_MONOTONIC);
pthread_cond_init(&ctx->mqtt_cond, &ctx->mqtt_attr);
ctx->event_base = event_base_new();
if (!ctx->event_base) {
dlog(DLOG_LEVEL_ERROR, "event_base_new failed");
goto free_out;
}
ctx->event_thread = 0;
if (v2g_ctx_start_events(ctx) != 0)
goto free_out;
ctx->hlc_pause_active = false;
ctx->connection_initiated = false;
return ctx;
free_out:
ctx->shutdown = true;
if (ctx->event_base) {
event_base_loopbreak(ctx->event_base);
}
if (ctx->event_thread) {
pthread_join(ctx->event_thread, NULL);
}
if (ctx->event_base) {
event_base_free(ctx->event_base);
}
free(ctx->local_tls_addr);
free(ctx->local_tcp_addr);
free(ctx);
return NULL;
}
void v2g_ctx_free(struct v2g_context* ctx) {
if (ctx == nullptr) {
return;
}
ctx->shutdown = true;
if (ctx->event_base) {
event_base_loopbreak(ctx->event_base);
}
if (ctx->event_thread) {
pthread_join(ctx->event_thread, NULL);
}
if (ctx->event_base) {
event_base_free(ctx->event_base);
}
pthread_cond_destroy(&ctx->mqtt_cond);
pthread_mutex_destroy(&ctx->mqtt_lock);
free(ctx->local_tls_addr);
ctx->local_tls_addr = NULL;
free(ctx->local_tcp_addr);
ctx->local_tcp_addr = NULL;
free(ctx);
}
void publish_dc_ev_maximum_limits(struct v2g_context* ctx, const float& v2g_dc_ev_max_current_limit,
const unsigned int& v2g_dc_ev_max_current_limit_is_used,
const float& v2g_dc_ev_max_power_limit,
const unsigned int& v2g_dc_ev_max_power_limit_is_used,
const float& v2g_dc_ev_max_voltage_limit,
const unsigned int& v2g_dc_ev_max_voltage_limit_is_used) {
types::iso15118::DcEvMaximumLimits dc_ev_maximum_limits;
bool publish_message = false;
if (v2g_dc_ev_max_current_limit_is_used == (unsigned int)1) {
dc_ev_maximum_limits.dc_ev_maximum_current_limit = v2g_dc_ev_max_current_limit;
if (ctx->ev_v2g_data.ev_maximum_current_limit != dc_ev_maximum_limits.dc_ev_maximum_current_limit.value()) {
ctx->ev_v2g_data.ev_maximum_current_limit = v2g_dc_ev_max_current_limit;
publish_message = true;
}
}
if (v2g_dc_ev_max_power_limit_is_used == (unsigned int)1) {
dc_ev_maximum_limits.dc_ev_maximum_power_limit = v2g_dc_ev_max_power_limit;
if (ctx->ev_v2g_data.ev_maximum_power_limit != v2g_dc_ev_max_power_limit) {
ctx->ev_v2g_data.ev_maximum_power_limit = v2g_dc_ev_max_power_limit;
publish_message = true;
}
}
if (v2g_dc_ev_max_voltage_limit_is_used == (unsigned int)1) {
dc_ev_maximum_limits.dc_ev_maximum_voltage_limit = v2g_dc_ev_max_voltage_limit;
if (ctx->ev_v2g_data.ev_maximum_voltage_limit != dc_ev_maximum_limits.dc_ev_maximum_voltage_limit.value()) {
ctx->ev_v2g_data.ev_maximum_voltage_limit = v2g_dc_ev_max_voltage_limit;
publish_message = true;
}
}
if (publish_message == true) {
ctx->p_charger->publish_dc_ev_maximum_limits(dc_ev_maximum_limits);
}
}
void publish_dc_ev_target_voltage_current(struct v2g_context* ctx, const float& v2g_dc_ev_target_voltage,
const float& v2g_dc_ev_target_current) {
if ((ctx->ev_v2g_data.v2g_target_voltage != v2g_dc_ev_target_voltage) ||
(ctx->ev_v2g_data.v2g_target_current != v2g_dc_ev_target_current)) {
types::iso15118::DcEvTargetValues dc_ev_target_values;
dc_ev_target_values.dc_ev_target_voltage = v2g_dc_ev_target_voltage;
dc_ev_target_values.dc_ev_target_current = v2g_dc_ev_target_current;
ctx->ev_v2g_data.v2g_target_voltage = v2g_dc_ev_target_voltage;
ctx->ev_v2g_data.v2g_target_current = v2g_dc_ev_target_current;
ctx->p_charger->publish_dc_ev_target_voltage_current(dc_ev_target_values);
}
}
void publish_dc_ev_remaining_time(struct v2g_context* ctx, const float& v2g_dc_ev_remaining_time_to_full_soc,
const unsigned int& v2g_dc_ev_remaining_time_to_full_soc_is_used,
const float& v2g_dc_ev_remaining_time_to_bulk_soc,
const unsigned int& v2g_dc_ev_remaining_time_to_bulk_soc_is_used) {
types::iso15118::DcEvRemainingTime dc_ev_remaining_time;
const char* format = "%Y-%m-%dT%H:%M:%SZ";
char buffer[100];
std::time_t time_now_in_sec = time(NULL);
bool publish_message = false;
if (v2g_dc_ev_remaining_time_to_full_soc_is_used == (unsigned int)1) {
if (ctx->ev_v2g_data.remaining_time_to_full_soc != v2g_dc_ev_remaining_time_to_full_soc) {
std::time_t time_to_full_soc = time_now_in_sec + v2g_dc_ev_remaining_time_to_full_soc;
std::strftime(buffer, sizeof(buffer), format, std::gmtime(&time_to_full_soc));
dc_ev_remaining_time.ev_remaining_time_to_full_soc = std::string(buffer);
ctx->ev_v2g_data.remaining_time_to_full_soc = v2g_dc_ev_remaining_time_to_full_soc;
publish_message = true;
}
}
if (v2g_dc_ev_remaining_time_to_bulk_soc_is_used == (unsigned int)1) {
if (ctx->ev_v2g_data.remaining_time_to_bulk_soc != v2g_dc_ev_remaining_time_to_bulk_soc) {
std::time_t time_to_bulk_soc = time_now_in_sec + v2g_dc_ev_remaining_time_to_bulk_soc;
std::strftime(buffer, sizeof(buffer), format, std::gmtime(&time_to_bulk_soc));
dc_ev_remaining_time.ev_remaining_time_to_full_bulk_soc = std::string(buffer);
ctx->ev_v2g_data.remaining_time_to_bulk_soc = v2g_dc_ev_remaining_time_to_bulk_soc;
publish_message = true;
}
}
if (publish_message == true) {
ctx->p_charger->publish_dc_ev_remaining_time(dc_ev_remaining_time);
}
}
/*!
* \brief log_selected_energy_transfer_type This function prints the selected energy transfer mode.
* \param selected_energy_transfer_mode is the selected energy transfer mode
*/
void log_selected_energy_transfer_type(int selected_energy_transfer_mode) {
if (selected_energy_transfer_mode >= iso2_EnergyTransferModeType_AC_single_phase_core &&
selected_energy_transfer_mode <= iso2_EnergyTransferModeType_DC_unique) {
dlog(DLOG_LEVEL_INFO, "Selected energy transfer mode: %s",
selected_energy_transfer_mode_string[selected_energy_transfer_mode]);
} else {
dlog(DLOG_LEVEL_WARNING, "Selected energy transfer mode %d is invalid", selected_energy_transfer_mode);
}
}
bool add_service_to_service_list(struct v2g_context* v2g_ctx, const struct iso2_ServiceType& evse_service,
const int16_t* parameter_set_id, uint8_t parameter_set_id_len) {
uint8_t write_idx = 0;
bool service_found = false;
for (const auto& service : v2g_ctx->evse_v2g_data.evse_service_list) {
if (service.ServiceID == evse_service.ServiceID) {
service_found = true;
break;
}
write_idx++;
}
if (service_found == false and (v2g_ctx->evse_v2g_data.evse_service_list.size() < iso2_ServiceType_8_ARRAY_SIZE)) {
v2g_ctx->evse_v2g_data.evse_service_list.push_back(evse_service);
} else if (v2g_ctx->evse_v2g_data.evse_service_list.size() == iso2_ServiceType_8_ARRAY_SIZE) {
dlog(DLOG_LEVEL_ERROR, "Maximum service list size reached. Unable to add service ID %u",
evse_service.ServiceID);
return false;
}
// Configure parameter-set-id if requiered
for (uint8_t idx = 0; idx < parameter_set_id_len; idx++) {
iso2_ServiceParameterListType parameter_list{};
init_iso2_ServiceParameterListType(&parameter_list);
v2g_ctx->evse_v2g_data.service_parameter_list.try_emplace(evse_service.ServiceID, parameter_list);
configure_parameter_set(v2g_ctx->evse_v2g_data.service_parameter_list.at(evse_service.ServiceID),
parameter_set_id[idx], evse_service.ServiceID);
}
return true;
}
void remove_service_from_service_list_if_exists(struct v2g_context* v2g_ctx, uint16_t service_id) {
auto& service_list = v2g_ctx->evse_v2g_data.evse_service_list;
service_list.erase(std::remove_if(service_list.begin(), service_list.end(),
[service_id](const auto service) { return service.ServiceID == service_id; }),
service_list.end());
}
void configure_parameter_set(iso2_ServiceParameterListType& parameterSetList, int16_t parameterSetId,
uint16_t serviceId) {
bool parameter_set_id_found = false;
uint8_t write_idx = 0;
for (uint8_t idx = 0; idx < parameterSetList.ParameterSet.arrayLen; idx++) {
if (parameterSetList.ParameterSet.array[idx].ParameterSetID == parameterSetId) {
parameter_set_id_found = true;
write_idx = idx;
break;
}
}
if ((parameter_set_id_found == false) &&
(parameterSetList.ParameterSet.arrayLen < iso2_ParameterSetType_5_ARRAY_SIZE)) {
write_idx = parameterSetList.ParameterSet.arrayLen;
parameterSetList.ParameterSet.arrayLen++;
} else if (parameterSetList.ParameterSet.arrayLen == iso2_ParameterSetType_5_ARRAY_SIZE) {
dlog(DLOG_LEVEL_ERROR, "Maximum parameter-set list size reached. Unable to add parameter-set-ID %d",
parameterSetId);
return;
}
/* Get an free parameter-set-entry */
auto& parameterSet = parameterSetList.ParameterSet.array[write_idx];
parameterSet.ParameterSetID = parameterSetId;
if (serviceId == 2) {
/* Configure parameter-set-ID of the certificate service */
/* Service to install a Contract Certificate (Ref. Table 106 —
* ServiceParameterList for certificate service) */
if (parameterSet.ParameterSetID == 1) {
/* Configure parameter name */
strncpy_to_v2g(parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].Name.characters,
sizeof(parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].Name.characters),
&parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].Name.charactersLen,
"Service");
/* Configure parameter value */
strncpy_to_v2g(parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].stringValue.characters,
sizeof(parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].stringValue.characters),
&parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].stringValue.charactersLen,
"Installation");
parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].stringValue_isUsed = 1;
parameterSet.Parameter.arrayLen = 1;
}
/* Service to update a Contract Certificate */
else if (parameterSet.ParameterSetID == 2) {
/* Configure parameter name */
strncpy_to_v2g(parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].Name.characters,
sizeof(parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].Name.characters),
&parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].Name.charactersLen,
"Service");
/* Configure parameter value */
strncpy_to_v2g(parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].stringValue.characters,
sizeof(parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].stringValue.characters),
&parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].stringValue.charactersLen,
"Update");
parameterSet.Parameter.array[parameterSet.Parameter.arrayLen].stringValue_isUsed = 1;
parameterSet.Parameter.arrayLen = 1;
}
} else {
dlog(DLOG_LEVEL_WARNING, "Parameter-set-ID of service ID %u is not supported", serviceId);
}
}