Skip to content

Commit

Permalink
* OCPP modules subscribe to EVInfo to retrieve the SoC from the evse_…
Browse files Browse the repository at this point in the history
…manager (#729)

* SoC value is stored in memory per EVSE and included in Measurement (OCPP) and MeterValue (OCPP201)
* SoC value is reset on SessionFinished

Signed-off-by: pietfried <pietgoempel@gmail.com>
  • Loading branch information
Pietfried authored Jun 18, 2024
1 parent 74f7f29 commit b11e1e3
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 12 deletions.
22 changes: 18 additions & 4 deletions modules/OCPP/OCPP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest
#include "OCPP.hpp"
#include "generated/types/ocpp.hpp"
#include "ocpp/common/types.hpp"
#include "ocpp/v16/types.hpp"
#include <fmt/core.h>
#include <fstream>
Expand Down Expand Up @@ -239,6 +240,7 @@ void OCPP::process_session_event(int32_t evse_id, const types::evse_manager::Ses
EVLOG_debug << "Connector#" << ocpp_connector_id << ": "
<< "Received SessionFinished";
// ev side disconnect
this->evse_soc_map[evse_id].reset();
this->charge_point->on_session_stopped(ocpp_connector_id, session_event.uuid);
} else if (session_event.event == types::evse_manager::SessionEventEnum::Error) {
EVLOG_debug << "Connector#" << ocpp_connector_id << ": "
Expand All @@ -265,8 +267,19 @@ void OCPP::init_evse_subscriptions() {
int32_t evse_id = 1;
for (auto& evse : this->r_evse_manager) {
evse->subscribe_powermeter([this, evse_id](types::powermeter::Powermeter powermeter) {
json powermeter_json = powermeter;
this->charge_point->on_meter_values(evse_id, powermeter_json); //
ocpp::Measurement measurement;
measurement.power_meter = conversions::to_ocpp_power_meter(powermeter);
if (this->evse_soc_map[evse_id].has_value()) {
// soc is present, so add this to the measurement
measurement.soc_Percent = ocpp::StateOfCharge{this->evse_soc_map[evse_id].value()};
}
this->charge_point->on_meter_values(evse_id, measurement);
});

evse->subscribe_ev_info([this, evse_id](const types::evse_manager::EVInfo& ev_info) {
if (ev_info.soc.has_value()) {
this->evse_soc_map[evse_id] = ev_info.soc.value();
}
});

evse->subscribe_limits([this, evse_id](types::evse_manager::Limits limits) {
Expand Down Expand Up @@ -331,10 +344,11 @@ void OCPP::init_evse_connector_map() {
}
}

void OCPP::init_evse_ready_map() {
void OCPP::init_evse_maps() {
std::lock_guard<std::mutex> lk(this->evse_ready_mutex);
for (size_t evse_id = 1; evse_id <= this->r_evse_manager.size(); evse_id++) {
this->evse_ready_map[evse_id] = false;
this->evse_soc_map[evse_id] = std::nullopt;
}
}

Expand All @@ -355,7 +369,7 @@ void OCPP::init() {
invoke_init(*p_auth_provider);
invoke_init(*p_data_transfer);

this->init_evse_ready_map();
this->init_evse_maps();

for (size_t evse_id = 1; evse_id <= this->r_evse_manager.size(); evse_id++) {
this->r_evse_manager.at(evse_id - 1)->subscribe_waiting_for_external_ready([this, evse_id](bool ready) {
Expand Down
3 changes: 2 additions & 1 deletion modules/OCPP/OCPP.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,12 @@ class OCPP : public Everest::ModuleBase {

void init_evse_subscriptions(); // initialize subscriptions to all EVSEs provided by r_evse_manager
void init_evse_connector_map();
void init_evse_ready_map();
void init_evse_maps();
EvseConnectorMap evse_connector_map; // provides access to OCPP connector id by using EVerests evse and connector id
std::map<int32_t, int32_t>
connector_evse_index_map; // provides access to r_evse_manager index by using OCPP connector id
std::map<int32_t, bool> evse_ready_map;
std::map<int32_t, std::optional<float>> evse_soc_map;
std::mutex evse_ready_mutex;
std::condition_variable evse_ready_cv;
bool all_evse_ready();
Expand Down
43 changes: 43 additions & 0 deletions modules/OCPP/conversions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,49 @@ ocpp::v16::BootReasonEnum to_ocpp_boot_reason_enum(const types::system::BootReas
}
}

ocpp::Powermeter to_ocpp_power_meter(const types::powermeter::Powermeter& powermeter) {
ocpp::Powermeter ocpp_powermeter;
ocpp_powermeter.timestamp = powermeter.timestamp;
ocpp_powermeter.energy_Wh_import = {powermeter.energy_Wh_import.total, powermeter.energy_Wh_import.L1,
powermeter.energy_Wh_import.L2, powermeter.energy_Wh_import.L3};

ocpp_powermeter.meter_id = powermeter.meter_id;
ocpp_powermeter.phase_seq_error = powermeter.phase_seq_error;

if (powermeter.energy_Wh_export.has_value()) {
const auto energy_wh_export = powermeter.energy_Wh_export.value();
ocpp_powermeter.energy_Wh_export =
ocpp::Energy{energy_wh_export.total, energy_wh_export.L1, energy_wh_export.L2, energy_wh_export.L3};
}

if (powermeter.power_W.has_value()) {
const auto power_w = powermeter.power_W.value();
ocpp_powermeter.power_W = ocpp::Power{power_w.total, power_w.L1, power_w.L2, power_w.L3};
}

if (powermeter.voltage_V.has_value()) {
const auto voltage_v = powermeter.voltage_V.value();
ocpp_powermeter.voltage_V = ocpp::Voltage{voltage_v.DC, voltage_v.L1, voltage_v.L2, voltage_v.L3};
}

if (powermeter.VAR.has_value()) {
const auto var = powermeter.VAR.value();
ocpp_powermeter.VAR = ocpp::ReactivePower{var.total, var.L1, var.L2, var.L3};
}

if (powermeter.current_A.has_value()) {
const auto current_a = powermeter.current_A.value();
ocpp_powermeter.current_A = ocpp::Current{current_a.DC, current_a.L1, current_a.L2, current_a.L3, current_a.N};
}

if (powermeter.frequency_Hz.has_value()) {
const auto frequency_hz = powermeter.frequency_Hz.value();
ocpp_powermeter.frequency_Hz = ocpp::Frequency{frequency_hz.L1, frequency_hz.L2, frequency_hz.L3};
}

return ocpp_powermeter;
}

ocpp::v201::HashAlgorithmEnum to_ocpp_hash_algorithm_enum(const types::iso15118_charger::HashAlgorithm hash_algorithm) {
switch (hash_algorithm) {
case types::iso15118_charger::HashAlgorithm::SHA256:
Expand Down
3 changes: 3 additions & 0 deletions modules/OCPP/conversions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ to_ocpp_hash_algorithm_enum_type(const types::iso15118_charger::HashAlgorithm ha
/// \brief Converts a given types::iso15118_charger::Status \p status to a ocpp::v201::Iso15118EVCertificateStatusEnum.
ocpp::v16::BootReasonEnum to_ocpp_boot_reason_enum(const types::system::BootReason reason);

/// \brief Converts a given types::powermeter::Powermeter \p powermeter to a ocpp::Powermeter
ocpp::Powermeter to_ocpp_power_meter(const types::powermeter::Powermeter& powermeter);

/// \brief Converts a given types::iso15118_charger::HashAlgorithm \p hash_algorithm to a ocpp::v201::HashAlgorithmEnum.
ocpp::v201::HashAlgorithmEnum to_ocpp_hash_algorithm_enum(const types::iso15118_charger::HashAlgorithm hash_algorithm);

Expand Down
21 changes: 18 additions & 3 deletions modules/OCPP201/OCPP201.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@ std::set<TxStartStopPoint> get_tx_start_stop_points(const std::string& tx_start_
return tx_start_stop_points;
}

void OCPP201::init_evse_ready_map() {
void OCPP201::init_evse_maps() {
std::lock_guard<std::mutex> lk(this->evse_ready_mutex);
for (size_t evse_id = 1; evse_id <= this->r_evse_manager.size(); evse_id++) {
this->evse_ready_map[evse_id] = false;
this->evse_soc_map[evse_id] = std::nullopt;
}
}

Expand Down Expand Up @@ -194,7 +195,7 @@ void OCPP201::init() {
invoke_init(*p_auth_provider);
invoke_init(*p_auth_validator);

this->init_evse_ready_map();
this->init_evse_maps();

for (size_t evse_id = 1; evse_id <= this->r_evse_manager.size(); evse_id++) {
this->r_evse_manager.at(evse_id - 1)->subscribe_ready([this, evse_id](bool ready) {
Expand Down Expand Up @@ -507,11 +508,24 @@ void OCPP201::ready() {
});

evse->subscribe_powermeter([this, evse_id](const types::powermeter::Powermeter& power_meter) {
const auto meter_value = conversions::to_ocpp_meter_value(
auto meter_value = conversions::to_ocpp_meter_value(
power_meter, ocpp::v201::ReadingContextEnum::Sample_Periodic, power_meter.signed_meter_value);
if (this->evse_soc_map[evse_id].has_value()) {
auto sampled_soc_value = conversions::to_ocpp_sampled_value(
ocpp::v201::ReadingContextEnum::Sample_Periodic, ocpp::v201::MeasurandEnum::SoC, "Percent",
std::nullopt, ocpp::v201::LocationEnum::EV);
sampled_soc_value.value = this->evse_soc_map[evse_id].value();
meter_value.sampledValue.push_back(sampled_soc_value);
}
this->charge_point->on_meter_value(evse_id, meter_value);
});

evse->subscribe_ev_info([this, evse_id](const types::evse_manager::EVInfo& ev_info) {
if (ev_info.soc.has_value()) {
this->evse_soc_map[evse_id] = ev_info.soc.value();
}
});

evse->subscribe_iso15118_certificate_request(
[this, evse_id](const types::iso15118_charger::Request_Exi_Stream_Schema& certificate_request) {
auto ocpp_response = this->charge_point->on_get_15118_ev_certificate_request(
Expand Down Expand Up @@ -695,6 +709,7 @@ void OCPP201::process_session_started(const int32_t evse_id, const int32_t conne

void OCPP201::process_session_finished(const int32_t evse_id, const int32_t connector_id,
const types::evse_manager::SessionEvent& session_event) {
this->evse_soc_map[evse_id].reset();
auto transaction_data = this->transaction_handler->get_transaction_data(evse_id);
if (transaction_data != nullptr) {
transaction_data->charging_state = ocpp::v201::ChargingStateEnum::Idle;
Expand Down
3 changes: 2 additions & 1 deletion modules/OCPP201/OCPP201.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,10 @@ class OCPP201 : public Everest::ModuleBase {

// key represents evse_id, value indicates if ready
std::map<int32_t, bool> evse_ready_map;
std::map<int32_t, std::optional<float>> evse_soc_map;
std::mutex evse_ready_mutex;
std::condition_variable evse_ready_cv;
void init_evse_ready_map();
void init_evse_maps();
bool all_evse_ready();
std::map<int32_t, int32_t> get_connector_structure();
void process_session_event(const int32_t evse_id, const types::evse_manager::SessionEvent& session_event);
Expand Down
5 changes: 3 additions & 2 deletions modules/OCPP201/conversions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,12 @@ ocpp::v201::DataTransferResponse to_ocpp_data_transfer_response(types::ocpp::Dat

ocpp::v201::SampledValue to_ocpp_sampled_value(const ocpp::v201::ReadingContextEnum& reading_context,
const ocpp::v201::MeasurandEnum& measurand, const std::string& unit,
const std::optional<ocpp::v201::PhaseEnum> phase) {
const std::optional<ocpp::v201::PhaseEnum> phase,
ocpp::v201::LocationEnum location) {
ocpp::v201::SampledValue sampled_value;
ocpp::v201::UnitOfMeasure unit_of_measure;
sampled_value.context = reading_context;
sampled_value.location = ocpp::v201::LocationEnum::Outlet;
sampled_value.location = location;
sampled_value.measurand = measurand;
unit_of_measure.unit = unit;
sampled_value.unitOfMeasure = unit_of_measure;
Expand Down
3 changes: 2 additions & 1 deletion modules/OCPP201/conversions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ ocpp::v201::DataTransferResponse to_ocpp_data_transfer_response(types::ocpp::Dat
/// \brief Converts the provided parameters to an ocpp::v201::SampledValue.
ocpp::v201::SampledValue to_ocpp_sampled_value(const ocpp::v201::ReadingContextEnum& reading_context,
const ocpp::v201::MeasurandEnum& measurand, const std::string& unit,
const std::optional<ocpp::v201::PhaseEnum> phase);
const std::optional<ocpp::v201::PhaseEnum> phase,
ocpp::v201::LocationEnum location = ocpp::v201::LocationEnum::Outlet);

/// \brief Converts the given types::units_signed::SignedMeterValue \p signed_meter_value to an
/// ocpp::v201::SignedMeterValue.
Expand Down

0 comments on commit b11e1e3

Please sign in to comment.