Skip to content

Commit

Permalink
Add handling of max energy on invalid id
Browse files Browse the repository at this point in the history
Signed-off-by: Marc Emmers <[email protected]>
  • Loading branch information
marcemmers committed Oct 26, 2023
1 parent a133941 commit ce842b5
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 11 deletions.
23 changes: 20 additions & 3 deletions include/ocpp/v201/evse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <everest/timer.hpp>

#include <ocpp/v201/connector.hpp>
#include <ocpp/v201/device_model.hpp>
#include <ocpp/v201/ocpp_types.hpp>
#include <ocpp/v201/transaction.hpp>

Expand All @@ -20,26 +21,38 @@ class Evse {

private:
int32_t evse_id;
DeviceModel& device_model;
std::map<int32_t, std::unique_ptr<Connector>> id_connector_map;
std::function<void(const int32_t connector_id, const ConnectorStatusEnum& status)> status_notification_callback;
std::function<void(const MeterValue& meter_value, const Transaction& transaction, const int32_t seq_no,
const std::optional<int32_t> reservation_id)>
transaction_meter_value_req;
std::function<void()> pause_charging_callback;
std::unique_ptr<EnhancedTransaction> transaction; // pointer to active transaction (can be nullptr)
MeterValue meter_value; // represents current meter value
std::mutex meter_value_mutex;
std::recursive_mutex meter_value_mutex;
Everest::SteadyTimer sampled_meter_values_timer;

/// \brief gets the active import energy meter value from meter_value, normalized to Wh.
std::optional<float> get_active_import_register_meter_value();

/// \brief function to check if the max energy has been exceeded, calls pause_charging_callback if so.
void check_max_energy_on_invalid_id();

public:
/// \brief Construct a new Evse object
/// \param evse_id id of the evse
/// \param number_of_connectors of the evse
/// \param device_model reference to the device model
/// \param status_notification_callback that is called when the status of a connector changes
Evse(const int32_t evse_id, const int32_t number_of_connectors,
/// \param pause_charging_callback that is called when the charging should be paused due to max energy on invalid id
/// being exceeded
Evse(const int32_t evse_id, const int32_t number_of_connectors, DeviceModel& device_model,
const std::function<void(const int32_t connector_id, const ConnectorStatusEnum& status)>&
status_notification_callback,
const std::function<void(const MeterValue& meter_value, const Transaction& transaction, const int32_t seq_no,
const std::optional<int32_t> reservation_id)>& transaction_meter_value_req);
const std::optional<int32_t> reservation_id)>& transaction_meter_value_req,
const std::function<void()> pause_charging_callback);

/// \brief Returns an OCPP2.0.1 EVSE type
/// \return
Expand Down Expand Up @@ -74,6 +87,10 @@ class Evse {
/// \param reason
void close_transaction(const DateTime& timestamp, const MeterValue& meter_stop, const ReasonEnum& reason);

/// \brief Start checking if the max energy on invalid id has exceeded.
/// Will call pause_charging_callback when that happens.
void start_checking_max_energy_on_invalid_id();

/// \brief Indicates if a transaction is active at this evse
/// \return
bool has_active_transaction();
Expand Down
2 changes: 2 additions & 0 deletions include/ocpp/v201/transaction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ struct EnhancedTransaction : public Transaction {
std::optional<IdToken> group_id_token;
std::optional<int32_t> reservation_id;
int32_t seq_no = 0;
std::optional<float> active_energy_import_start_value;
bool check_max_active_import_energy;
int32_t get_seq_no();
Transaction get_transaction();
};
Expand Down
11 changes: 7 additions & 4 deletions lib/ocpp/v201/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,12 @@ ChargePoint::ChargePoint(const std::map<int32_t, int32_t>& evse_connector_struct
}
};

auto pause_charging_callback = [this, evse_id_]() { this->callbacks.pause_charging_callback(evse_id_); };

this->evses.insert(
std::make_pair(evse_id, std::make_unique<Evse>(evse_id, number_of_connectors, status_notification_callback,
transaction_meter_value_callback)));
std::make_pair(evse_id, std::make_unique<Evse>(evse_id, number_of_connectors, *this->device_model,
status_notification_callback,
transaction_meter_value_callback, pause_charging_callback)));
for (int32_t connector_id = 1; connector_id <= number_of_connectors; connector_id++) {
// operational status for this connector
this->database_handler->insert_availability(evse_id, connector_id, OperationalStatusEnum::Operative, false);
Expand Down Expand Up @@ -1790,11 +1793,11 @@ void ChargePoint::handle_start_transaction_event_response(const EnhancedMessage<
if (this->device_model->get_value<bool>(ControllerComponentVariables::StopTxOnInvalidId)) {
this->callbacks.stop_transaction_callback(evse_id, ReasonEnum::DeAuthorized);
} else {
if (this->device_model->get_optional_value<bool>(ControllerComponentVariables::MaxEnergyOnInvalidId)
if (this->device_model->get_optional_value<int32_t>(ControllerComponentVariables::MaxEnergyOnInvalidId)
.has_value()) {
// TODO(piet): E05.FR.03
// Energy delivery to the EV SHALL be allowed until the amount of energy specified in
// MaxEnergyOnInvalidId has been reached.
this->evses.at(evse_id)->start_checking_max_energy_on_invalid_id();
} else {
this->callbacks.pause_charging_callback(evse_id);
}
Expand Down
85 changes: 81 additions & 4 deletions lib/ocpp/v201/evse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,50 @@
#include <utility>

#include <everest/logging.hpp>
#include <ocpp/v201/ctrlr_component_variables.hpp>
#include <ocpp/v201/evse.hpp>
#include <ocpp/v201/utils.hpp>

namespace ocpp {
namespace v201 {

Evse::Evse(const int32_t evse_id, const int32_t number_of_connectors,
// Convert an energy value into Wh
static float get_normalized_energy_value(SampledValue sampled_value) {
float value = sampled_value.value;
// If no unit of measure is present the unit is in Wh so nothing to do
if (sampled_value.unitOfMeasure.has_value()) {
const auto& unit_of_measure = sampled_value.unitOfMeasure.value();
if (unit_of_measure.unit.has_value()) {
if (unit_of_measure.unit.value() == "kWh") {
value *= 1000.0f;
} else if (unit_of_measure.unit.value() == "Wh") {
// do nothing
} else {
EVLOG_AND_THROW(
std::runtime_error("Attempt to convert an energy value which does not have a correct unit"));
}
}

if (unit_of_measure.multiplier.has_value()) {
if (unit_of_measure.multiplier.value() != 0) {
value *= powf(10, unit_of_measure.multiplier.value());
}
}
}
return value;
}

Evse::Evse(const int32_t evse_id, const int32_t number_of_connectors, DeviceModel& device_model,
const std::function<void(const int32_t connector_id, const ConnectorStatusEnum& status)>&
status_notification_callback,
const std::function<void(const MeterValue& meter_value, const Transaction& transaction, const int32_t seq_no,
const std::optional<int32_t> reservation_id)>& transaction_meter_value_req) :
const std::optional<int32_t> reservation_id)>& transaction_meter_value_req,
const std::function<void()> pause_charging_callback) :
evse_id(evse_id),
device_model(device_model),
status_notification_callback(status_notification_callback),
transaction_meter_value_req(transaction_meter_value_req),
pause_charging_callback(pause_charging_callback),
transaction(nullptr) {
for (int connector_id = 1; connector_id <= number_of_connectors; connector_id++) {
this->id_connector_map.insert(std::make_pair(
Expand Down Expand Up @@ -54,6 +84,11 @@ void Evse::open_transaction(const std::string& transaction_id, const int32_t con
this->transaction->id_token = id_token;
this->transaction->group_id_token = group_id_token;

auto start_value = this->get_active_import_register_meter_value();
if (start_value.has_value()) {
this->transaction->active_energy_import_start_value = start_value.value();
}

transaction->meter_values.push_back(meter_start);

if (sampled_data_tx_updated_interval > 0) {
Expand All @@ -79,6 +114,15 @@ void Evse::close_transaction(const DateTime& timestamp, const MeterValue& meter_
this->sampled_meter_values_timer.stop();
}

void Evse::start_checking_max_energy_on_invalid_id() {
if (this->transaction != nullptr) {
this->transaction->check_max_active_import_energy = true;
this->check_max_energy_on_invalid_id();
} else {
EVLOG_error << "Trying to start \"MaxEnergyOnInvalidId\" checking without an active transaction";
}
}

bool Evse::has_active_transaction() {
return this->transaction != nullptr;
}
Expand Down Expand Up @@ -110,14 +154,47 @@ void Evse::trigger_status_notification_callback(const int32_t connector_id) {
}

void Evse::on_meter_value(const MeterValue& meter_value) {
std::lock_guard<std::mutex> lk(this->meter_value_mutex);
std::lock_guard<std::recursive_mutex> lk(this->meter_value_mutex);
this->meter_value = meter_value;
this->check_max_energy_on_invalid_id();
}

MeterValue Evse::get_meter_value() {
std::lock_guard<std::mutex> lk(this->meter_value_mutex);
std::lock_guard<std::recursive_mutex> lk(this->meter_value_mutex);
return this->meter_value;
}

std::optional<float> Evse::get_active_import_register_meter_value() {
std::lock_guard<std::recursive_mutex> lk(this->meter_value_mutex);
auto it = std::find_if(
this->meter_value.sampledValue.begin(), this->meter_value.sampledValue.end(), [](const SampledValue& value) {
return value.measurand == MeasurandEnum::Energy_Active_Import_Register and !value.phase.has_value();
});
if (it != this->meter_value.sampledValue.end()) {
return get_normalized_energy_value(*it);
}
return std::nullopt;
}

void Evse::check_max_energy_on_invalid_id() {
// Handle E05.02
auto max_energy_on_invalid_id =
this->device_model.get_optional_value<int32_t>(ControllerComponentVariables::MaxEnergyOnInvalidId);
auto& transaction = this->transaction;
if (transaction != nullptr and max_energy_on_invalid_id.has_value() and
transaction->active_energy_import_start_value.has_value() and transaction->check_max_active_import_energy) {
const auto opt_energy_value = this->get_active_import_register_meter_value();

if (opt_energy_value.has_value()) {
auto charged_energy = opt_energy_value.value() - transaction->active_energy_import_start_value.value();

if (charged_energy > static_cast<float>(max_energy_on_invalid_id.value())) {
this->pause_charging_callback();
transaction->check_max_active_import_energy = false; // No need to check anymore
}
}
}
}

} // namespace v201
} // namespace ocpp

0 comments on commit ce842b5

Please sign in to comment.