Skip to content

Commit

Permalink
Merge branch 'main' into feat/fail_on_powermeter_errors
Browse files Browse the repository at this point in the history
  • Loading branch information
florinmihut authored Dec 16, 2024
2 parents 7b58046 + a9d1ab0 commit aafeb79
Show file tree
Hide file tree
Showing 23 changed files with 4,207 additions and 40 deletions.
4 changes: 2 additions & 2 deletions dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ libevse-security:
# OCPP
libocpp:
git: https://github.com/EVerest/libocpp.git
git_tag: e7a37da3610e4cbf66dfbc58b9aa98fca2aa6cec
git_tag: e52ac969095804144af4896af1984cedaf45a3f8
cmake_condition: "EVEREST_DEPENDENCY_ENABLED_LIBOCPP"
# Josev
Josev:
Expand All @@ -86,7 +86,7 @@ ext-mbedtls:
# everest-testing and ev-dev-tools
everest-utils:
git: https://github.com/EVerest/everest-utils.git
git_tag: v0.4.3
git_tag: v0.4.4

# unit testing
gtest:
Expand Down
5 changes: 3 additions & 2 deletions interfaces/evse_manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ cmds:
arguments:
reservation_id:
description: >-
The reservation id (should be added to the TransactionStarted
event)
The reservation id (should be added to the TransactionStarted event). Set this to a negative value if there is
no specific reservation id for this evse but the evse should still move to a Reserved state because of total
global reservations.
type: integer
result:
description: Returns true if the EVSE accepted the reservation, else false.
Expand Down
2 changes: 1 addition & 1 deletion modules/Auth/Auth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ void Auth::ready() {
[this](const std::optional<int32_t> evse_id, const int32_t reservation_id, const ReservationEndReason reason,
const bool send_reservation_update) {
// Only call the evse manager to cancel the reservation if it was for a specific evse
if (evse_id.has_value()) {
if (evse_id.has_value() && evse_id.value() > 0) {
EVLOG_debug << "Call evse manager to cancel the reservation with evse id " << evse_id.value();
this->r_evse_manager.at(evse_id.value() - 1)->call_cancel_reservation();
}
Expand Down
6 changes: 6 additions & 0 deletions modules/Auth/include/AuthHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,12 @@ class AuthHandler {
const AuthorizationType& type);
void submit_event_for_connector(const int32_t evse_id, const int32_t connector_id,
const ConnectorEvent connector_event);
/**
* @brief Check reservations: if there are as many reservations as evse's, all should be set to reserved.
*
* This will check the reservation status of the evse's and send the statusses to the evse manager.
*/
void check_evse_reserved_and_send_updates();
};

} // namespace module
Expand Down
38 changes: 38 additions & 0 deletions modules/Auth/include/ReservationHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <map>
#include <mutex>
#include <optional>
#include <set>
#include <vector>

#include <Connector.hpp>
Expand All @@ -15,6 +16,11 @@ class kvsIntf;

namespace module {

struct ReservationEvseStatus {
std::set<int32_t> reserved;
std::set<int32_t> available;
};

class ReservationHandler {
private: // Members
/// \brief Map of EVSE's, with EVSE id as key and the EVSE struct as value.
Expand Down Expand Up @@ -45,6 +51,8 @@ class ReservationHandler {
const types::reservation::ReservationEndReason reason, const bool send_reservation_update)>
reservation_cancelled_callback;

std::set<int32_t> last_reserved_status;

/// \brief worker for the timers.
boost::shared_ptr<boost::asio::io_service::work> work;
/// \brief io_service for the worker for the timers.
Expand Down Expand Up @@ -125,6 +133,14 @@ class ReservationHandler {
std::pair<bool, std::optional<uint32_t>> cancel_reservation(const int reservation_id, const bool execute_callback,
const types::reservation::ReservationEndReason reason);

///
/// \brief Cancel a reservation.
/// \param evse_id The evse id to cancel the reservation for.
/// \param execute_callback True if the `reservation_cancelled_callback` must be called.
/// \return True if the reservation could be cancelled.
///
bool cancel_reservation(const uint32_t evse_id, const bool execute_callback);

///
/// \brief Register reservation cancelled callback.
/// \param callback The callback that should be called when a reservation is cancelled.
Expand Down Expand Up @@ -163,6 +179,15 @@ class ReservationHandler {
///
bool has_reservation_parent_id(const std::optional<uint32_t> evse_id);

///
/// \brief Check if the number of global reservations match the number of available evse's.
/// \return The new reservation status of the evse's.
///
/// \note The return value has the new reserved and new available statusses (so the ones that were already reserved
/// are not added to those lists).
///
ReservationEvseStatus check_number_global_reservations_match_number_available_evses();

private: // Functions
///
/// \brief Check if there is a specific connector type in the vector.
Expand Down Expand Up @@ -308,6 +333,19 @@ class ReservationHandler {
///
void store_reservations();

///
/// \brief Get new reserved / available status for evse's and store it.
/// \param currently_available_evses Current available evse's.
/// \param reserved_evses Current reserved evse's.
/// \return A struct with changed reservation statuses compared with the last time this function was called.
///
/// When an evse is reserved and it was available before, it will be added to the set in the struct (return value).
/// But when an evse is reserved and last time it was already reserved, it is not added.
///
ReservationEvseStatus
get_evse_global_reserved_status_and_set_new_status(const std::set<int32_t>& currently_available_evses,
const std::set<int32_t>& reserved_evses);

///
/// \brief Helper function to print information about reservations and evses, to find out why a reservation has
/// failed.
Expand Down
48 changes: 45 additions & 3 deletions modules/Auth/lib/AuthHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ void AuthHandler::init_evse(const int evse_id, const int evse_index, const std::

void AuthHandler::initialize() {
this->reservation_handler.load_reservations();
check_evse_reserved_and_send_updates();
}

TokenHandlingResult AuthHandler::on_token(const ProvidedIdToken& provided_token) {
Expand Down Expand Up @@ -611,15 +612,20 @@ ReservationCheckStatus AuthHandler::handle_reservation_exists(std::string& id_to
}

bool AuthHandler::call_reserved(const int reservation_id, const std::optional<int>& evse_id) {
return this->reserved_callback(evse_id, reservation_id);
const bool reserved = this->reserved_callback(evse_id, reservation_id);
if (reserved) {
this->check_evse_reserved_and_send_updates();
}

return reserved;
}

void AuthHandler::call_reservation_cancelled(const int32_t reservation_id,
const types::reservation::ReservationEndReason reason,
const std::optional<int>& evse_id, const bool send_reservation_update) {
std::optional<int32_t> evse_index;
if (evse_id.has_value() && evse_id.value() > 0) {
EVLOG_info << "Cancel reservation for evse id" << evse_id.value();
EVLOG_info << "Cancel reservation for evse id " << evse_id.value();
}

this->reservation_cancelled_callback(evse_id, reservation_id, reason, send_reservation_update);
Expand Down Expand Up @@ -659,6 +665,7 @@ void AuthHandler::handle_session_event(const int evse_id, const SessionEvent& ev
std::lock_guard<std::mutex> lk(this->timer_mutex);
this->evses.at(evse_id)->event_mutex.lock();
const auto event_type = event.event;
bool check_reservations = false;

switch (event_type) {
case SessionEventEnum::SessionStarted: {
Expand Down Expand Up @@ -689,6 +696,7 @@ void AuthHandler::handle_session_event(const int evse_id, const SessionEvent& ev
this->evses.at(evse_id)->transaction_active = true;
this->submit_event_for_connector(evse_id, connector_id, ConnectorEvent::TRANSACTION_STARTED);
this->evses.at(evse_id)->timeout_timer.stop();
check_reservations = true;
break;
}
case SessionEventEnum::TransactionFinished:
Expand All @@ -704,18 +712,26 @@ void AuthHandler::handle_session_event(const int evse_id, const SessionEvent& ev
std::lock_guard<std::mutex> lk(this->plug_in_queue_mutex);
this->plug_in_queue.remove_if([evse_id](int value) { return value == evse_id; });
}

check_reservations = true;
break;
}
case SessionEventEnum::Disabled:
this->submit_event_for_connector(evse_id, connector_id, ConnectorEvent::DISABLE);
check_reservations = true;
break;
case SessionEventEnum::Enabled:
this->submit_event_for_connector(evse_id, connector_id, ConnectorEvent::ENABLE);
check_reservations = true;
break;
case SessionEventEnum::ReservationStart:
break;
case SessionEventEnum::ReservationEnd:
case SessionEventEnum::ReservationEnd: {
if (reservation_handler.is_evse_reserved(evse_id)) {
reservation_handler.cancel_reservation(evse_id, true);
}
break;
}
/// explicitly fall through all the SessionEventEnum values we are not handling
case SessionEventEnum::Authorized:
[[fallthrough]];
Expand Down Expand Up @@ -747,6 +763,12 @@ void AuthHandler::handle_session_event(const int evse_id, const SessionEvent& ev
break;
}
this->evses.at(evse_id)->event_mutex.unlock();

// When reservation is started or ended, check if the number of reservations match the number of evses and
// send 'reserved' notifications to the evse manager accordingly if needed.
if (check_reservations) {
check_evse_reserved_and_send_updates();
}
}

void AuthHandler::set_connection_timeout(const int connection_timeout) {
Expand Down Expand Up @@ -821,4 +843,24 @@ void AuthHandler::submit_event_for_connector(const int32_t evse_id, const int32_
}
}

void AuthHandler::check_evse_reserved_and_send_updates() {
ReservationEvseStatus reservation_status =
this->reservation_handler.check_number_global_reservations_match_number_available_evses();
for (const auto& available_evse : reservation_status.available) {
EVLOG_debug << "Evse " << available_evse << " is now available";
this->reservation_cancelled_callback(
available_evse, -1, types::reservation::ReservationEndReason::GlobalReservationRequirementDropped, false);
}

for (const auto& reserved_evse : reservation_status.reserved) {
EVLOG_debug << "Evse " << reserved_evse << " is now reserved";
if (this->reserved_callback != nullptr) {
const bool reserved = this->reserved_callback(reserved_evse, -1);
if (!reserved) {
EVLOG_warning << "Could not reserve " << reserved_evse << " for non evse specific reservations";
}
}
}
}

} // namespace module
90 changes: 88 additions & 2 deletions modules/Auth/lib/ReservationHandler.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include <ReservationHandler.hpp>

#include <algorithm>
#include <iostream>

#include <Connector.hpp>

Expand Down Expand Up @@ -313,6 +312,19 @@ ReservationHandler::cancel_reservation(const int reservation_id, const bool exec
return result;
}

bool ReservationHandler::cancel_reservation(const uint32_t evse_id, const bool execute_callback) {
auto it = this->evse_reservations.find(evse_id);
if (it != this->evse_reservations.end()) {
int reservation_id = it->second.reservation_id;
return this
->cancel_reservation(reservation_id, execute_callback, types::reservation::ReservationEndReason::Cancelled)
.first;
} else {
EVLOG_warning << "Could not cancel reservation with evse id " << evse_id;
return false;
}
}

void ReservationHandler::register_reservation_cancelled_callback(
const std::function<void(const std::optional<uint32_t>& evse_id, const int32_t reservation_id,
const types::reservation::ReservationEndReason reason,
Expand All @@ -322,7 +334,7 @@ void ReservationHandler::register_reservation_cancelled_callback(

void ReservationHandler::on_reservation_used(const int32_t reservation_id) {
const std::pair<bool, std::optional<uint32_t>> cancelled =
this->cancel_reservation(reservation_id, true, types::reservation::ReservationEndReason::UsedToStartCharging);
this->cancel_reservation(reservation_id, false, types::reservation::ReservationEndReason::UsedToStartCharging);
if (cancelled.first) {
if (cancelled.second.has_value()) {
EVLOG_info << "Reservation (" << reservation_id << ") for evse#" << cancelled.second.value()
Expand Down Expand Up @@ -400,6 +412,51 @@ bool ReservationHandler::has_reservation_parent_id(const std::optional<uint32_t>
return false;
}

ReservationEvseStatus ReservationHandler::check_number_global_reservations_match_number_available_evses() {
std::set<int32_t> available_evses;
std::unique_lock<std::recursive_mutex> lock(this->evse_mutex);
// Get all evse's that are not reserved or used.
for (const auto& evse : this->evses) {
if (get_evse_connector_state_reservation_result(static_cast<uint32_t>(evse.first), this->evse_reservations) ==
types::reservation::ReservationResult::Accepted &&
get_connector_availability_reservation_result(static_cast<uint32_t>(evse.first),
types::evse_manager::ConnectorTypeEnum::Unknown) ==
types::reservation::ReservationResult::Accepted) {
available_evses.insert(evse.first);
}
}

std::unique_lock<std::recursive_mutex> reservation_lock(this->reservation_mutex);
if (available_evses.size() == this->global_reservations.size()) {
// There are as many evses available as 'global' reservations, so all evse's are reserved. Set all available
// evse's to reserved.
return get_evse_global_reserved_status_and_set_new_status(available_evses, available_evses);
}

// There are not as many global reservations as available evse's, but we have to check for specific connector types
// as well.
std::set<int32_t> reserved_evses_with_specific_connector_type;
for (const auto& global_reservation : this->global_reservations) {
if (!is_reservation_possible(global_reservation.connector_type, this->global_reservations,
this->evse_reservations)) {
// A new reservation with this type is not possible (so also arrival of an extra car is not), so all evse's
// with this connector type should be set to reserved.
for (const auto& evse : this->evses) {
if (available_evses.find(evse.first) != available_evses.end() &&
this->has_evse_connector_type(
evse.second->connectors,
global_reservation.connector_type.value_or(types::evse_manager::ConnectorTypeEnum::Unknown))) {
// This evse is available and has a specific connector type. So it should be set to unavailable.
reserved_evses_with_specific_connector_type.insert(evse.first);
}
}
}
}

return get_evse_global_reserved_status_and_set_new_status(available_evses,
reserved_evses_with_specific_connector_type);
}

bool ReservationHandler::has_evse_connector_type(const std::vector<Connector>& evse_connectors,
const types::evse_manager::ConnectorTypeEnum connector_type) const {
if (connector_type == types::evse_manager::ConnectorTypeEnum::Unknown) {
Expand Down Expand Up @@ -744,6 +801,35 @@ void ReservationHandler::store_reservations() {
}
}

ReservationEvseStatus ReservationHandler::get_evse_global_reserved_status_and_set_new_status(
const std::set<int32_t>& currently_available_evses, const std::set<int32_t>& reserved_evses) {
ReservationEvseStatus evse_status_to_send;
std::set<int32_t> new_reserved_evses;

for (const auto evse_id : reserved_evses) {
if (this->last_reserved_status.find(evse_id) != this->last_reserved_status.end()) {
// Evse was already reserved, don't add it to the new status.
} else {
evse_status_to_send.reserved.insert(evse_id);
}
}

for (const auto evse_id : currently_available_evses) {
const bool is_reserved = reserved_evses.find(evse_id) != reserved_evses.end();
const bool was_reserved = this->last_reserved_status.find(evse_id) != this->last_reserved_status.end();
if (not is_reserved) {
if (was_reserved) {
evse_status_to_send.available.insert(evse_id);
}
}
}

new_reserved_evses = reserved_evses;
this->last_reserved_status = new_reserved_evses;

return evse_status_to_send;
}

void ReservationHandler::print_reservations_debug_info(const types::reservation::Reservation& reservation,
const std::optional<uint32_t> evse_id,
const bool reservation_failed) {
Expand Down
Loading

0 comments on commit aafeb79

Please sign in to comment.