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
This commit is contained in:
1099
tools/EVerest-main/modules/EVSE/Auth/lib/AuthHandler.cpp
Normal file
1099
tools/EVerest-main/modules/EVSE/Auth/lib/AuthHandler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
39
tools/EVerest-main/modules/EVSE/Auth/lib/CMakeLists.txt
Normal file
39
tools/EVerest-main/modules/EVSE/Auth/lib/CMakeLists.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
set(MODULE_DIR "${PROJECT_SOURCE_DIR}/modules/EVSE/Auth")
|
||||
set(INCLUDE_DIR "${MODULE_DIR}/include")
|
||||
|
||||
add_library(auth_handler STATIC)
|
||||
ev_register_library_target(auth_handler)
|
||||
|
||||
target_sources(auth_handler PRIVATE
|
||||
AuthHandler.cpp
|
||||
Connector.cpp
|
||||
ReservationHandler.cpp
|
||||
ConnectorStateMachine.cpp
|
||||
)
|
||||
|
||||
get_target_property(GENERATED_INCLUDE_DIR generate_cpp_files EVEREST_GENERATED_INCLUDE_DIR)
|
||||
|
||||
target_include_directories(auth_handler PRIVATE
|
||||
${INCLUDE_DIR}
|
||||
${GENERATED_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
add_dependencies(auth_handler generate_cpp_files)
|
||||
|
||||
target_link_libraries(auth_handler
|
||||
PRIVATE
|
||||
everest::timer
|
||||
date::date
|
||||
date::date-tz
|
||||
everest::framework
|
||||
everest::helpers
|
||||
)
|
||||
|
||||
# needs c++ 14
|
||||
target_compile_features(auth_handler PRIVATE cxx_std_14)
|
||||
|
||||
if(EVEREST_ENABLE_COMPILE_WARNINGS)
|
||||
target_compile_options(auth_handler
|
||||
PRIVATE ${EVEREST_COMPILE_OPTIONS}
|
||||
)
|
||||
endif()
|
||||
85
tools/EVerest-main/modules/EVSE/Auth/lib/Connector.cpp
Normal file
85
tools/EVerest-main/modules/EVSE/Auth/lib/Connector.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include <Connector.hpp>
|
||||
|
||||
namespace module {
|
||||
|
||||
void Connector::submit_event(ConnectorEvent event) {
|
||||
state_machine.handle_event(event);
|
||||
}
|
||||
|
||||
ConnectorState Connector::get_state() const {
|
||||
return this->state_machine.get_state();
|
||||
}
|
||||
|
||||
bool Connector::is_unavailable() const {
|
||||
return this->get_state() == ConnectorState::UNAVAILABLE || this->get_state() == ConnectorState::UNAVAILABLE_FAULTED;
|
||||
}
|
||||
|
||||
namespace conversions {
|
||||
std::string connector_state_to_string(const ConnectorState& state) {
|
||||
switch (state) {
|
||||
case ConnectorState::AVAILABLE:
|
||||
return "AVAILABLE";
|
||||
case ConnectorState::OCCUPIED:
|
||||
return "OCCUPIED";
|
||||
case ConnectorState::UNAVAILABLE:
|
||||
return "UNAVAILABLE";
|
||||
case ConnectorState::FAULTED:
|
||||
return "FAULTED";
|
||||
case ConnectorState::FAULTED_OCCUPIED:
|
||||
return "FAULTED_OCCUPIED";
|
||||
case ConnectorState::UNAVAILABLE_FAULTED:
|
||||
return "UNAVAILABLE_FAULTED";
|
||||
default:
|
||||
throw std::runtime_error("No known conversion for the given connector state");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace conversions
|
||||
|
||||
bool EVSEContext::is_available() {
|
||||
if (this->plug_in_timeout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if an identifier is present, an EVSE is not considered available
|
||||
if (this->identifier.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool occupied = false;
|
||||
bool available = false;
|
||||
for (const auto& connector : this->connectors) {
|
||||
if (connector.get_state() == ConnectorState::OCCUPIED ||
|
||||
connector.get_state() == ConnectorState::FAULTED_OCCUPIED) {
|
||||
occupied = true;
|
||||
}
|
||||
if (connector.get_state() != ConnectorState::UNAVAILABLE && connector.get_state() != ConnectorState::FAULTED) {
|
||||
available = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (occupied) {
|
||||
// When at least one connector is occupied, they are both not available.
|
||||
return false;
|
||||
}
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
bool EVSEContext::is_unavailable() {
|
||||
for (const auto& connector : this->connectors) {
|
||||
if (!connector.is_unavailable()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// namespace conversions
|
||||
|
||||
// namespace conversions
|
||||
} // namespace module
|
||||
@@ -0,0 +1,98 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include <ConnectorStateMachine.hpp>
|
||||
|
||||
namespace module {
|
||||
|
||||
ConnectorStateMachine::ConnectorStateMachine(ConnectorState initial_state) : state(initial_state) {
|
||||
}
|
||||
|
||||
bool ConnectorStateMachine::handle_event(ConnectorEvent event) {
|
||||
switch (state) {
|
||||
case ConnectorState::AVAILABLE:
|
||||
switch (event) {
|
||||
case ConnectorEvent::TRANSACTION_STARTED:
|
||||
state = ConnectorState::OCCUPIED;
|
||||
return true;
|
||||
case ConnectorEvent::DISABLE:
|
||||
state = ConnectorState::UNAVAILABLE;
|
||||
return true;
|
||||
case ConnectorEvent::FAULTED:
|
||||
state = ConnectorState::FAULTED;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
case ConnectorState::UNAVAILABLE:
|
||||
switch (event) {
|
||||
case ConnectorEvent::ENABLE:
|
||||
state = ConnectorState::AVAILABLE;
|
||||
return true;
|
||||
case ConnectorEvent::FAULTED:
|
||||
state = ConnectorState::UNAVAILABLE_FAULTED;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
case ConnectorState::FAULTED:
|
||||
switch (event) {
|
||||
case ConnectorEvent::ERROR_CLEARED:
|
||||
state = ConnectorState::AVAILABLE;
|
||||
return true;
|
||||
case ConnectorEvent::DISABLE:
|
||||
state = ConnectorState::UNAVAILABLE_FAULTED;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
case ConnectorState::OCCUPIED:
|
||||
switch (event) {
|
||||
case ConnectorEvent::SESSION_FINISHED:
|
||||
state = ConnectorState::AVAILABLE;
|
||||
return true;
|
||||
case ConnectorEvent::FAULTED:
|
||||
state = ConnectorState::FAULTED_OCCUPIED;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
case ConnectorState::FAULTED_OCCUPIED:
|
||||
switch (event) {
|
||||
case ConnectorEvent::ERROR_CLEARED:
|
||||
state = ConnectorState::OCCUPIED;
|
||||
return true;
|
||||
case ConnectorEvent::SESSION_FINISHED:
|
||||
state = ConnectorState::FAULTED;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
case ConnectorState::UNAVAILABLE_FAULTED:
|
||||
switch (event) {
|
||||
case ConnectorEvent::ENABLE:
|
||||
state = ConnectorState::FAULTED;
|
||||
return true;
|
||||
case ConnectorEvent::ERROR_CLEARED:
|
||||
state = ConnectorState::UNAVAILABLE;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
// could/should not happen!
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ConnectorState ConnectorStateMachine::get_state() const {
|
||||
return this->state;
|
||||
}
|
||||
|
||||
} // namespace module
|
||||
878
tools/EVerest-main/modules/EVSE/Auth/lib/ReservationHandler.cpp
Normal file
878
tools/EVerest-main/modules/EVSE/Auth/lib/ReservationHandler.cpp
Normal file
@@ -0,0 +1,878 @@
|
||||
#include <ReservationHandler.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <Connector.hpp>
|
||||
|
||||
#include <everest/helpers/helpers.hpp>
|
||||
#include <everest/logging.hpp>
|
||||
#include <generated/interfaces/kvs/Interface.hpp>
|
||||
#include <utils/date.hpp>
|
||||
|
||||
using everest::helpers::is_equal_case_insensitive;
|
||||
|
||||
namespace module {
|
||||
|
||||
static types::reservation::ReservationResult
|
||||
connector_state_to_reservation_result(const ConnectorState connector_state);
|
||||
|
||||
ReservationHandler::ReservationHandler(std::map<int, std::unique_ptr<module::EVSEContext>>& evses,
|
||||
const std::string& id, kvsIntf* store) :
|
||||
evses(evses), kvs_store_key_id("reservation_" + id), store(store) {
|
||||
// Create this worker thread and io service etc here for the timer.
|
||||
this->work = boost::make_shared<boost::asio::executor_work_guard<boost::asio::io_context::executor_type>>(
|
||||
boost::asio::make_work_guard(this->io_context));
|
||||
this->io_context_thread = std::thread([this]() { this->io_context.run(); });
|
||||
}
|
||||
|
||||
ReservationHandler::~ReservationHandler() {
|
||||
this->reservation_id_to_reservation_timeout_timer_map.clear();
|
||||
work->get_executor().context().stop();
|
||||
(*work).reset(); // explicitly call underlying reset method, not the smart pointer reset
|
||||
io_context.stop();
|
||||
io_context_thread.join();
|
||||
}
|
||||
|
||||
void ReservationHandler::load_reservations() {
|
||||
std::lock_guard<std::recursive_mutex> lk(this->event_mutex);
|
||||
if (this->store == nullptr) {
|
||||
EVLOG_warning << "Can not load reservations because no storage was configured.";
|
||||
return;
|
||||
}
|
||||
|
||||
const auto stored_reservations = store->call_load(this->kvs_store_key_id);
|
||||
const Array* reservations_json = std::get_if<Array>(&stored_reservations);
|
||||
if (reservations_json == nullptr) {
|
||||
EVLOG_warning << "Can not load reservations: reservations is not a json array.";
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& reservation : *reservations_json) {
|
||||
types::reservation::Reservation r;
|
||||
try {
|
||||
r = reservation.at("reservation");
|
||||
} catch (const json::exception& e) {
|
||||
EVLOG_error << "Could not get reservation from store: " << e.what();
|
||||
continue;
|
||||
}
|
||||
|
||||
std::optional<uint32_t> evse_id;
|
||||
if (reservation.contains("evse_id")) {
|
||||
evse_id = reservation.at("evse_id");
|
||||
}
|
||||
|
||||
types::reservation::ReservationResult reservation_result = this->make_reservation(evse_id, r);
|
||||
if (reservation_result != types::reservation::ReservationResult::Accepted) {
|
||||
EVLOG_warning << "Load reservations: Could not make reservation with id " << r.reservation_id
|
||||
<< ": reservation cancelled.";
|
||||
this->reservation_cancelled_callback(evse_id, r.reservation_id,
|
||||
types::reservation::ReservationEndReason::Cancelled, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
types::reservation::ReservationResult
|
||||
ReservationHandler::make_reservation(const std::optional<uint32_t> evse_id,
|
||||
const types::reservation::Reservation& reservation) {
|
||||
std::lock_guard<std::recursive_mutex> lk(this->event_mutex);
|
||||
if (date::utc_clock::now() > Everest::Date::from_rfc3339(reservation.expiry_time)) {
|
||||
EVLOG_info << "Rejecting reservation because expire time is in the past.";
|
||||
return types::reservation::ReservationResult::Rejected;
|
||||
}
|
||||
|
||||
// If a reservation was made with an existing reservation id, the existing reservation must be replaced (H01.FR.01).
|
||||
// We cancel the reservation here because of that. That also means that if the reservation can not be made, the old
|
||||
// reservation is cancelled anyway.
|
||||
std::pair<bool, std::optional<uint32_t>> reservation_cancelled = this->cancel_reservation(
|
||||
reservation.reservation_id, false, types::reservation::ReservationEndReason::Cancelled);
|
||||
if (reservation_cancelled.first && reservation_cancelled.second.has_value()) {
|
||||
EVLOG_debug << "Cancelled reservation with id " << reservation.reservation_id << " for evse id "
|
||||
<< reservation_cancelled.second.value() << " because a reservation with the same id was made";
|
||||
}
|
||||
|
||||
if (evse_id.has_value()) {
|
||||
if (this->evse_reservations.count(evse_id.value()) > 0) {
|
||||
// There already is a reservation for this evse.
|
||||
EVLOG_debug << "Rejected reservation because there already is a reservation for this evse.";
|
||||
return types::reservation::ReservationResult::Occupied;
|
||||
}
|
||||
|
||||
if (this->evses.count(evse_id.value()) == 0) {
|
||||
// There is no evse with this evse id.
|
||||
EVLOG_warning << "Rejected reservation because there is no evse with this evse id: " << evse_id.value();
|
||||
return types::reservation::ReservationResult::Rejected;
|
||||
}
|
||||
const types::evse_manager::ConnectorTypeEnum connector_type =
|
||||
reservation.connector_type.value_or(types::evse_manager::ConnectorTypeEnum::Unknown);
|
||||
|
||||
// We have to return a valid state here.
|
||||
// So if one or all connectors are occupied or reserved, return occupied. (H01.FR.11)
|
||||
// If one or all are faulted, return faulted. (H01.FR.12)
|
||||
// If one or all are unavailable, return unavailable. (H01.FR.13)
|
||||
// It is not clear what to return if one is faulted, one occupied and one available so in that case the first
|
||||
// in row is returned, which is occupied.
|
||||
const types::reservation::ReservationResult evse_state =
|
||||
this->get_evse_connector_state_reservation_result(evse_id.value(), this->evse_reservations);
|
||||
const types::reservation::ReservationResult connector_state =
|
||||
this->get_connector_availability_reservation_result(evse_id.value(), connector_type);
|
||||
|
||||
if (!has_evse_connector_type(this->evses[evse_id.value()]->connectors, connector_type)) {
|
||||
EVLOG_debug << "Rejected reservation because this evse (id: " << evse_id.value()
|
||||
<< ") does not have the requested connector type ("
|
||||
<< types::evse_manager::connector_type_enum_to_string(connector_type) << ")";
|
||||
return types::reservation::ReservationResult::Rejected;
|
||||
} else if (evse_state != types::reservation::ReservationResult::Accepted) {
|
||||
print_reservations_debug_info(reservation, evse_id, true);
|
||||
EVLOG_debug << "Rejecting reservation because connector is not available";
|
||||
return evse_state;
|
||||
} else if (connector_state != types::reservation::ReservationResult::Accepted) {
|
||||
print_reservations_debug_info(reservation, evse_id, true);
|
||||
return connector_state;
|
||||
} else {
|
||||
// Everything fine, continue.
|
||||
if (global_reservations.empty()) {
|
||||
set_reservation_timer(reservation, evse_id);
|
||||
this->evse_reservations[evse_id.value()] = reservation;
|
||||
EVLOG_info << "Created reservation for evse id " << evse_id.value() << ", connector type "
|
||||
<< types::evse_manager::connector_type_enum_to_string(
|
||||
reservation.connector_type.value_or(types::evse_manager::ConnectorTypeEnum::Unknown));
|
||||
return types::reservation::ReservationResult::Accepted;
|
||||
}
|
||||
|
||||
// Make a copy of the evse specific reservations map so we can add this reservation to test if the
|
||||
// reservation is possible. Only if it is, we add it to the 'member' map.
|
||||
std::map<uint32_t, types::reservation::Reservation> evse_specific_reservations = this->evse_reservations;
|
||||
evse_specific_reservations[evse_id.value()] = reservation;
|
||||
|
||||
// Check if the reservations are possible with the added evse specific reservation.
|
||||
if (!is_reservation_possible(std::nullopt, this->global_reservations, evse_specific_reservations)) {
|
||||
print_reservations_debug_info(reservation, evse_id, true);
|
||||
return get_reservation_evse_connector_state(connector_type);
|
||||
}
|
||||
|
||||
// Reservation is possible, add to evse specific reservations.
|
||||
this->evse_reservations[evse_id.value()] = reservation;
|
||||
EVLOG_info << "Created reservation for evse id " << evse_id.value() << ", connector type "
|
||||
<< types::evse_manager::connector_type_enum_to_string(
|
||||
reservation.connector_type.value_or(types::evse_manager::ConnectorTypeEnum::Unknown));
|
||||
}
|
||||
} else {
|
||||
if (reservation.connector_type.has_value() &&
|
||||
!does_evse_connector_type_exist(reservation.connector_type.value())) {
|
||||
EVLOG_info << "Can not make reservation because the connector type does not exist.";
|
||||
return types::reservation::ReservationResult::Rejected;
|
||||
}
|
||||
|
||||
const types::evse_manager::ConnectorTypeEnum connector_type =
|
||||
reservation.connector_type.value_or(types::evse_manager::ConnectorTypeEnum::Unknown);
|
||||
if (!is_reservation_possible(connector_type, this->global_reservations, this->evse_reservations)) {
|
||||
print_reservations_debug_info(reservation, evse_id, true);
|
||||
return get_reservation_evse_connector_state(connector_type);
|
||||
}
|
||||
|
||||
EVLOG_info << "Created reservation for connector type "
|
||||
<< types::evse_manager::connector_type_enum_to_string(
|
||||
reservation.connector_type.value_or(types::evse_manager::ConnectorTypeEnum::Unknown));
|
||||
global_reservations.push_back(reservation);
|
||||
store_reservations();
|
||||
}
|
||||
|
||||
set_reservation_timer(reservation, evse_id);
|
||||
|
||||
return types::reservation::ReservationResult::Accepted;
|
||||
}
|
||||
|
||||
void ReservationHandler::on_connector_state_changed(const ConnectorState connector_state, const uint32_t evse_id,
|
||||
const uint32_t connector_id) {
|
||||
std::lock_guard<std::recursive_mutex> lk(this->event_mutex);
|
||||
if (connector_state == ConnectorState::AVAILABLE) {
|
||||
// Nothing to cancel.
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->evses.count(static_cast<int32_t>(evse_id)) == 0) {
|
||||
EVLOG_warning << "on_connector_state_changed: evse " << evse_id << " does not exist. This should not happen.";
|
||||
return;
|
||||
}
|
||||
|
||||
auto& connectors = this->evses[evse_id]->connectors;
|
||||
auto connector_it = std::find_if(connectors.begin(), connectors.end(),
|
||||
[connector_id](const auto& connector) { return connector_id == connector.id; });
|
||||
|
||||
if (connector_it == connectors.end()) {
|
||||
// Connector with specific connector id not found
|
||||
EVLOG_warning << "Could not change connector state for connector id " << connector_id << " of evse " << evse_id
|
||||
<< ": connector id does not exist. This should not happen.";
|
||||
return;
|
||||
}
|
||||
|
||||
const bool reservation_exists = evse_reservations.count(evse_id) != 0;
|
||||
|
||||
if (reservation_exists && evse_reservations[evse_id].connector_type.has_value() &&
|
||||
(connector_it->type == evse_reservations[evse_id].connector_type.value() ||
|
||||
connector_it->type == types::evse_manager::ConnectorTypeEnum::Unknown ||
|
||||
evse_reservations[evse_id].connector_type.value() == types::evse_manager::ConnectorTypeEnum::Unknown)) {
|
||||
cancel_reservation(evse_reservations[evse_id].reservation_id, true,
|
||||
types::reservation::ReservationEndReason::Cancelled);
|
||||
return;
|
||||
}
|
||||
|
||||
// Now we might have one connector less, let's check if all reservations are still possible now and if not, cancel
|
||||
// the one(s) that can not be done anymore.
|
||||
check_reservations_and_cancel_if_not_possible();
|
||||
}
|
||||
|
||||
bool ReservationHandler::is_charging_possible(const uint32_t evse_id) {
|
||||
std::lock_guard<std::recursive_mutex> lk(this->event_mutex);
|
||||
if (this->evse_reservations.count(evse_id) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->evses.count(evse_id) == 0) {
|
||||
// Not existing evse id
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<uint32_t, types::reservation::Reservation> reservations = this->evse_reservations;
|
||||
// We want to test if charging is possible on this evse id with the current reservations. For that, we do like it
|
||||
// is a new reservation and check if that reservation is possible. If it is, we can charge on that evse.
|
||||
types::reservation::Reservation r;
|
||||
// It is a dummy reservation so the details are not important.
|
||||
reservations[evse_id] = r;
|
||||
return is_reservation_possible(std::nullopt, this->global_reservations, reservations);
|
||||
}
|
||||
|
||||
bool ReservationHandler::is_evse_reserved(const uint32_t evse_id) {
|
||||
std::lock_guard<std::recursive_mutex> lk(this->event_mutex);
|
||||
if (this->evse_reservations.count(evse_id) > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::pair<bool, std::optional<uint32_t>>
|
||||
ReservationHandler::cancel_reservation(const int reservation_id, const bool execute_callback,
|
||||
const types::reservation::ReservationEndReason reason) {
|
||||
std::lock_guard<std::recursive_mutex> lk(this->event_mutex);
|
||||
std::pair<bool, std::optional<uint32_t>> result;
|
||||
|
||||
bool reservation_cancelled = false;
|
||||
|
||||
auto reservation_id_timer_it = this->reservation_id_to_reservation_timeout_timer_map.find(reservation_id);
|
||||
if (reservation_id_timer_it != this->reservation_id_to_reservation_timeout_timer_map.end()) {
|
||||
reservation_id_timer_it->second->stop();
|
||||
this->reservation_id_to_reservation_timeout_timer_map.erase(reservation_id_timer_it);
|
||||
reservation_cancelled = true;
|
||||
result.first = true;
|
||||
}
|
||||
|
||||
if (!reservation_cancelled) {
|
||||
result.first = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
EVLOG_info << "Cancel reservation with reservation id " << reservation_id;
|
||||
|
||||
std::optional<uint32_t> evse_id;
|
||||
for (const auto& reservation : this->evse_reservations) {
|
||||
if (reservation.second.reservation_id == reservation_id) {
|
||||
evse_id = reservation.first;
|
||||
}
|
||||
}
|
||||
|
||||
if (evse_id.has_value()) {
|
||||
auto it = this->evse_reservations.find(evse_id.value());
|
||||
if (it != this->evse_reservations.end()) {
|
||||
this->evse_reservations.erase(it);
|
||||
} else {
|
||||
EVLOG_warning << "Could not remove reservation with evse id " << evse_id.value()
|
||||
<< ": this should not happen";
|
||||
}
|
||||
|
||||
} else {
|
||||
// No evse, search in global reservations
|
||||
const auto& it = std::find_if(this->global_reservations.begin(), this->global_reservations.end(),
|
||||
[reservation_id](const types::reservation::Reservation& reservation) {
|
||||
return reservation.reservation_id == reservation_id;
|
||||
});
|
||||
|
||||
if (it != this->global_reservations.end()) {
|
||||
this->global_reservations.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
this->store_reservations();
|
||||
|
||||
if (execute_callback && this->reservation_cancelled_callback != nullptr) {
|
||||
this->reservation_cancelled_callback(evse_id, reservation_id, reason, execute_callback);
|
||||
}
|
||||
|
||||
result.second = evse_id;
|
||||
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,
|
||||
const bool send_reservation_update)>& callback) {
|
||||
this->reservation_cancelled_callback = callback;
|
||||
}
|
||||
|
||||
void ReservationHandler::on_reservation_used(const int32_t reservation_id) {
|
||||
std::lock_guard<std::recursive_mutex> lk(this->event_mutex);
|
||||
const std::pair<bool, std::optional<uint32_t>> cancelled =
|
||||
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()
|
||||
<< " used and cancelled";
|
||||
} else {
|
||||
EVLOG_info << "Reservation (" << reservation_id << ") without evse id used and cancelled";
|
||||
}
|
||||
} else {
|
||||
EVLOG_info << "Could not cancel reservation with reservation id " << reservation_id;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<int32_t> ReservationHandler::matches_reserved_identifier(const std::string& id_token,
|
||||
const std::optional<uint32_t> evse_id,
|
||||
std::optional<std::string> parent_id_token) {
|
||||
std::lock_guard<std::recursive_mutex> lk(this->event_mutex);
|
||||
EVLOG_debug << "Matches reserved identifier for evse id " << (evse_id.has_value() ? evse_id.value() : 9999)
|
||||
<< " and id token " << everest::helpers::redact(id_token) << " and parent id token "
|
||||
<< (parent_id_token.has_value() ? everest::helpers::redact(parent_id_token.value()) : "-");
|
||||
|
||||
// Return true if id tokens match or parent id tokens exists and match.
|
||||
if (evse_id.has_value()) {
|
||||
if (this->evse_reservations.count(evse_id.value())) {
|
||||
const types::reservation::Reservation& reservation = this->evse_reservations[evse_id.value()];
|
||||
if (is_equal_case_insensitive(reservation.id_token, id_token) ||
|
||||
(parent_id_token.has_value() && reservation.parent_id_token.has_value() &&
|
||||
is_equal_case_insensitive(parent_id_token.value(), reservation.parent_id_token.value()))) {
|
||||
EVLOG_debug << "There is a reservation (" << reservation.reservation_id << ") for evse "
|
||||
<< evse_id.value() << " and the token matches";
|
||||
return reservation.reservation_id;
|
||||
} else {
|
||||
EVLOG_debug << "There is a reservation for evse id " << evse_id.value() << ", but token does not match";
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If evse_id == 0 or there is no reservation found with the given evse id, search globally for reservation with
|
||||
// this token.
|
||||
for (const auto& reservation : global_reservations) {
|
||||
if (is_equal_case_insensitive(reservation.id_token, id_token) ||
|
||||
(parent_id_token.has_value() && reservation.parent_id_token.has_value() &&
|
||||
is_equal_case_insensitive(parent_id_token.value(), reservation.parent_id_token.value()))) {
|
||||
EVLOG_debug << "There is a reservation for the token, reservation id: " << reservation.reservation_id;
|
||||
return reservation.reservation_id;
|
||||
}
|
||||
}
|
||||
|
||||
EVLOG_debug << "No reservation found which matches the reserved identifier";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool ReservationHandler::has_reservation_parent_id(const std::optional<uint32_t> evse_id) {
|
||||
std::lock_guard<std::recursive_mutex> lk(this->event_mutex);
|
||||
if (evse_id.has_value()) {
|
||||
if (this->evses.count(evse_id.value()) == 0) {
|
||||
// EVSE id does not exist.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->evse_reservations.count(evse_id.value())) {
|
||||
return this->evse_reservations[evse_id.value()].parent_id_token.has_value();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if one of the global reservations has a parent id.
|
||||
for (const auto& reservation : this->global_reservations) {
|
||||
if (reservation.parent_id_token.has_value()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ReservationEvseStatus ReservationHandler::check_number_global_reservations_match_number_available_evses() {
|
||||
std::unique_lock<std::recursive_mutex> reservation_lock(this->event_mutex);
|
||||
std::set<int32_t> available_evses;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& connector : evse_connectors) {
|
||||
if (connector.type == types::evse_manager::ConnectorTypeEnum::Unknown || connector.type == connector_type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReservationHandler::does_evse_connector_type_exist(
|
||||
const types::evse_manager::ConnectorTypeEnum connector_type) const {
|
||||
for (const auto& [evse_id, evse] : evses) {
|
||||
if (has_evse_connector_type(evse->connectors, connector_type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
types::reservation::ReservationResult ReservationHandler::get_evse_connector_state_reservation_result(
|
||||
const uint32_t evse_id, const std::map<uint32_t, types::reservation::Reservation>& evse_specific_reservations) {
|
||||
if (evses.count(evse_id) == 0) {
|
||||
EVLOG_warning << "Get evse state for evse " << evse_id
|
||||
<< " not possible: evse id does not exists. This should not happen.";
|
||||
return types::reservation::ReservationResult::Rejected;
|
||||
}
|
||||
|
||||
// Check if evse is available.
|
||||
if (evses[evse_id]->plugged_in) {
|
||||
return connector_state_to_reservation_result(ConnectorState::OCCUPIED);
|
||||
}
|
||||
|
||||
// If one connector is occupied, then the other connector can also not be used (one connector of an evse can be
|
||||
// used at the same time).
|
||||
for (const auto& connector : evses[evse_id]->connectors) {
|
||||
if (connector.get_state() == ConnectorState::OCCUPIED ||
|
||||
connector.get_state() == ConnectorState::FAULTED_OCCUPIED) {
|
||||
return connector_state_to_reservation_result(connector.get_state());
|
||||
}
|
||||
}
|
||||
|
||||
// If evse is reserved, it is not available.
|
||||
if (evse_specific_reservations.count(evse_id) > 0) {
|
||||
return types::reservation::ReservationResult::Occupied;
|
||||
}
|
||||
|
||||
return types::reservation::ReservationResult::Accepted;
|
||||
}
|
||||
|
||||
types::reservation::ReservationResult ReservationHandler::get_connector_availability_reservation_result(
|
||||
const uint32_t evse_id, const types::evse_manager::ConnectorTypeEnum connector_type) {
|
||||
if (evses.count(evse_id) == 0) {
|
||||
EVLOG_warning << "Request if connector is available for evse id " << evse_id
|
||||
<< ", but evse id does not exist. This should not happen.";
|
||||
return types::reservation::ReservationResult::Rejected;
|
||||
}
|
||||
|
||||
ConnectorState connector_state = ConnectorState::UNAVAILABLE;
|
||||
|
||||
for (const auto& connector : evses[evse_id]->connectors) {
|
||||
if ((connector.type == connector_type || connector.type == types::evse_manager::ConnectorTypeEnum::Unknown ||
|
||||
connector_type == types::evse_manager::ConnectorTypeEnum::Unknown)) {
|
||||
if (connector.get_state() == ConnectorState::AVAILABLE) {
|
||||
return types::reservation::ReservationResult::Accepted;
|
||||
} else {
|
||||
connector_state = get_new_connector_state(connector_state, connector.get_state());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return connector_state_to_reservation_result(connector_state);
|
||||
}
|
||||
|
||||
std::vector<std::vector<types::evse_manager::ConnectorTypeEnum>> ReservationHandler::get_all_possible_orders(
|
||||
const std::vector<types::evse_manager::ConnectorTypeEnum>& connectors) const {
|
||||
|
||||
std::vector<types::evse_manager::ConnectorTypeEnum> input_next = connectors;
|
||||
std::vector<types::evse_manager::ConnectorTypeEnum> input_prev = connectors;
|
||||
std::vector<std::vector<types::evse_manager::ConnectorTypeEnum>> output;
|
||||
|
||||
if (connectors.empty()) {
|
||||
return output;
|
||||
}
|
||||
|
||||
// For next_permutation, the input must be ordered or it will stop halfway. So if it stops halafway,
|
||||
// prev_permutation will find the others.
|
||||
do {
|
||||
output.push_back(input_next);
|
||||
} while (std::next_permutation(input_next.begin(), input_next.end()));
|
||||
|
||||
while (std::prev_permutation(input_prev.begin(), input_prev.end())) {
|
||||
output.push_back(input_prev);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
bool ReservationHandler::can_virtual_car_arrive(
|
||||
const std::vector<uint32_t>& used_evse_ids,
|
||||
const std::vector<types::evse_manager::ConnectorTypeEnum>& next_car_arrival_order,
|
||||
const std::map<uint32_t, types::reservation::Reservation>& evse_specific_reservations) {
|
||||
|
||||
bool is_possible = false;
|
||||
|
||||
for (const auto& [evse_id, evse] : evses) {
|
||||
// Check if there is a car already at this evse id.
|
||||
if (std::find(used_evse_ids.begin(), used_evse_ids.end(), evse_id) != used_evse_ids.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (get_evse_connector_state_reservation_result(evse_id, evse_specific_reservations) ==
|
||||
types::reservation::ReservationResult::Accepted &&
|
||||
has_evse_connector_type(evse->connectors, next_car_arrival_order.at(0)) &&
|
||||
get_connector_availability_reservation_result(evse_id, next_car_arrival_order.at(0)) ==
|
||||
types::reservation::ReservationResult::Accepted) {
|
||||
is_possible = true;
|
||||
|
||||
std::vector<uint32_t> next_used_evse_ids = used_evse_ids;
|
||||
// Add evse id to list when we call the function recursively.
|
||||
next_used_evse_ids.push_back(evse_id);
|
||||
|
||||
// Check if this is the last.
|
||||
if (next_car_arrival_order.size() == 1) {
|
||||
// If this is the last and a car can arrive, then this combination is possible.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Call next level recursively.
|
||||
// Remove connector type ('car') from list when we call the function recursively.
|
||||
const std::vector<types::evse_manager::ConnectorTypeEnum> next_arrival_order(
|
||||
next_car_arrival_order.begin() + 1, next_car_arrival_order.end());
|
||||
|
||||
if (!this->can_virtual_car_arrive(next_used_evse_ids, next_arrival_order, evse_specific_reservations)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return is_possible;
|
||||
}
|
||||
|
||||
bool ReservationHandler::is_reservation_possible(
|
||||
const std::optional<types::evse_manager::ConnectorTypeEnum> global_reservation_type,
|
||||
const std::vector<types::reservation::Reservation>& reservations_no_evse,
|
||||
const std::map<uint32_t, types::reservation::Reservation>& evse_specific_reservations) {
|
||||
|
||||
std::vector<types::evse_manager::ConnectorTypeEnum> types;
|
||||
for (const auto& global_reservation : reservations_no_evse) {
|
||||
types.push_back(global_reservation.connector_type.value_or(types::evse_manager::ConnectorTypeEnum::Unknown));
|
||||
}
|
||||
|
||||
if (global_reservation_type.has_value()) {
|
||||
types.push_back(global_reservation_type.value());
|
||||
}
|
||||
|
||||
// Check if the total amount of reservations is not more than the total amount of evse's.
|
||||
if (types.size() + evse_specific_reservations.size() > this->evses.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector<std::vector<types::evse_manager::ConnectorTypeEnum>> orders = get_all_possible_orders(types);
|
||||
|
||||
for (const auto& o : orders) {
|
||||
if (!this->can_virtual_car_arrive({}, o, evse_specific_reservations)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReservationHandler::set_reservation_timer(const types::reservation::Reservation& reservation,
|
||||
const std::optional<uint32_t> evse_id) {
|
||||
std::lock_guard<std::recursive_mutex> lk(this->event_mutex);
|
||||
this->reservation_id_to_reservation_timeout_timer_map[reservation.reservation_id] =
|
||||
std::make_unique<Everest::SteadyTimer>(&this->io_context);
|
||||
|
||||
this->reservation_id_to_reservation_timeout_timer_map[reservation.reservation_id]->at(
|
||||
[this, reservation, evse_id]() {
|
||||
if (evse_id.has_value()) {
|
||||
EVLOG_info << "Reservation expired for evse #" << evse_id.value()
|
||||
<< " (reservation id: " << reservation.reservation_id << ")";
|
||||
} else {
|
||||
EVLOG_info << "Reservation expired for reservation id " << reservation.reservation_id;
|
||||
}
|
||||
|
||||
this->cancel_reservation(reservation.reservation_id, true,
|
||||
types::reservation::ReservationEndReason::Expired);
|
||||
},
|
||||
Everest::Date::from_rfc3339(reservation.expiry_time));
|
||||
}
|
||||
|
||||
std::vector<EVSEContext*> ReservationHandler::get_all_evses_with_connector_type(
|
||||
const types::evse_manager::ConnectorTypeEnum connector_type) const {
|
||||
std::vector<EVSEContext*> result;
|
||||
for (const auto& evse : this->evses) {
|
||||
if (this->has_evse_connector_type(evse.second->connectors, connector_type)) {
|
||||
result.push_back(evse.second.get());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ConnectorState ReservationHandler::get_new_connector_state(ConnectorState current_state,
|
||||
const ConnectorState new_state) const {
|
||||
if (new_state == ConnectorState::OCCUPIED) {
|
||||
return ConnectorState::OCCUPIED;
|
||||
}
|
||||
|
||||
if (new_state > current_state) {
|
||||
if (new_state > ConnectorState::OCCUPIED) {
|
||||
if (new_state == ConnectorState::FAULTED_OCCUPIED) {
|
||||
current_state = ConnectorState::OCCUPIED;
|
||||
} else if (new_state == ConnectorState::UNAVAILABLE_FAULTED) {
|
||||
if (current_state != ConnectorState::OCCUPIED) {
|
||||
current_state = ConnectorState::FAULTED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current_state = new_state;
|
||||
}
|
||||
}
|
||||
|
||||
return current_state;
|
||||
}
|
||||
|
||||
types::reservation::ReservationResult ReservationHandler::get_reservation_evse_connector_state(
|
||||
const types::evse_manager::ConnectorTypeEnum connector_type) const {
|
||||
// If at least one connector is occupied, return occupied.
|
||||
if (!global_reservations.empty() || !(evse_reservations.empty())) {
|
||||
return types::reservation::ReservationResult::Occupied;
|
||||
}
|
||||
|
||||
bool found_state = false;
|
||||
|
||||
ConnectorState state = ConnectorState::UNAVAILABLE;
|
||||
|
||||
for (const auto& [evse_id, evse] : evses) {
|
||||
if (evse->plugged_in) {
|
||||
// Overwrite state if we found a connector that was not available (if needed).
|
||||
state = get_new_connector_state(state, ConnectorState::OCCUPIED);
|
||||
found_state = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_state) {
|
||||
const std::vector<EVSEContext*> evses_with_connector_type =
|
||||
this->get_all_evses_with_connector_type(connector_type);
|
||||
if (evses_with_connector_type.empty()) {
|
||||
// This should not happen because then it should have been rejected before already somewhere in the
|
||||
// code...
|
||||
return types::reservation::ReservationResult::Rejected;
|
||||
}
|
||||
|
||||
// Get all evse's with this specific connector type and check the connectors availability states.
|
||||
for (const auto& evse : evses_with_connector_type) {
|
||||
for (const auto& connector : evse->connectors) {
|
||||
if (connector.type != connector_type &&
|
||||
connector.type != types::evse_manager::ConnectorTypeEnum::Unknown &&
|
||||
connector_type != types::evse_manager::ConnectorTypeEnum::Unknown) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connector.get_state() != ConnectorState::AVAILABLE) {
|
||||
state = get_new_connector_state(state, connector.get_state());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return connector_state_to_reservation_result(state);
|
||||
}
|
||||
|
||||
void ReservationHandler::check_reservations_and_cancel_if_not_possible() {
|
||||
|
||||
std::vector<int32_t> reservations_to_cancel;
|
||||
std::map<uint32_t, types::reservation::Reservation> evse_specific_reservations;
|
||||
std::vector<types::reservation::Reservation> reservations_no_evse;
|
||||
|
||||
for (const auto& [evse_id, reservation] : this->evse_reservations) {
|
||||
evse_specific_reservations[evse_id] = reservation;
|
||||
if (!is_reservation_possible(std::nullopt, reservations_no_evse, evse_specific_reservations)) {
|
||||
reservations_to_cancel.push_back(reservation.reservation_id);
|
||||
evse_specific_reservations.erase(evse_id);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& reservation : this->global_reservations) {
|
||||
if (is_reservation_possible(reservation.connector_type, reservations_no_evse, evse_specific_reservations)) {
|
||||
reservations_no_evse.push_back(reservation);
|
||||
} else {
|
||||
reservations_to_cancel.push_back(reservation.reservation_id);
|
||||
}
|
||||
}
|
||||
|
||||
for (const int32_t reservation_id : reservations_to_cancel) {
|
||||
this->cancel_reservation(reservation_id, true, types::reservation::ReservationEndReason::Cancelled);
|
||||
}
|
||||
}
|
||||
|
||||
void ReservationHandler::store_reservations() {
|
||||
if (this->store == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Array reservations = json::array();
|
||||
for (const auto& reservation : this->evse_reservations) {
|
||||
|
||||
json r = json::object({{"evse_id", reservation.first}, {"reservation", reservation.second}});
|
||||
reservations.push_back(r);
|
||||
}
|
||||
|
||||
for (const auto& reservation : this->global_reservations) {
|
||||
json r = json::object({{"reservation", reservation}});
|
||||
reservations.push_back(r);
|
||||
}
|
||||
|
||||
if (!reservations.empty()) {
|
||||
this->store->call_store(this->kvs_store_key_id, 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) {
|
||||
std::string reservation_information;
|
||||
if (reservation_failed) {
|
||||
reservation_information = "Reservation not possible";
|
||||
} else {
|
||||
reservation_information = "New reservation";
|
||||
}
|
||||
EVLOG_debug << reservation_information
|
||||
<< ". Evse id: " << (evse_id.has_value() ? std::to_string(evse_id.value()) : "no evse id")
|
||||
<< ", connector type: "
|
||||
<< (reservation.connector_type.has_value()
|
||||
? types::evse_manager::connector_type_enum_to_string(reservation.connector_type.value())
|
||||
: "no connector type given");
|
||||
std::string evse_info;
|
||||
for (const auto& evse : this->evses) {
|
||||
evse_info += "- " + std::to_string(evse.first) + ":\n";
|
||||
for (const auto& connector : evse.second->connectors) {
|
||||
evse_info += "--- " + std::to_string(connector.id) + " " +
|
||||
types::evse_manager::connector_type_enum_to_string(connector.type) +
|
||||
", available: " + (connector.get_state() == ConnectorState::AVAILABLE ? "yes" : "no") + "\n";
|
||||
}
|
||||
}
|
||||
std::string reservation_info;
|
||||
for (const auto& evse_reservation : this->evse_reservations) {
|
||||
reservation_info +=
|
||||
"- evse " + std::to_string(evse_reservation.first) + ": " +
|
||||
types::evse_manager::connector_type_enum_to_string(
|
||||
evse_reservation.second.connector_type.value_or(types::evse_manager::ConnectorTypeEnum::Unknown)) +
|
||||
"\n";
|
||||
}
|
||||
|
||||
for (const auto& reservation : this->global_reservations) {
|
||||
reservation_info += "- global : " +
|
||||
types::evse_manager::connector_type_enum_to_string(
|
||||
reservation.connector_type.value_or(types::evse_manager::ConnectorTypeEnum::Unknown)) +
|
||||
"\n";
|
||||
}
|
||||
|
||||
EVLOG_debug << "Current evse's and states: \n" << evse_info;
|
||||
EVLOG_debug << "Current reservations: \n" << reservation_info;
|
||||
}
|
||||
|
||||
static types::reservation::ReservationResult
|
||||
connector_state_to_reservation_result(const ConnectorState connector_state) {
|
||||
switch (connector_state) {
|
||||
case ConnectorState::AVAILABLE:
|
||||
return types::reservation::ReservationResult::Accepted;
|
||||
case ConnectorState::UNAVAILABLE:
|
||||
return types::reservation::ReservationResult::Unavailable;
|
||||
case ConnectorState::FAULTED:
|
||||
case ConnectorState::UNAVAILABLE_FAULTED:
|
||||
case ConnectorState::FAULTED_OCCUPIED:
|
||||
return types::reservation::ReservationResult::Faulted;
|
||||
case ConnectorState::OCCUPIED:
|
||||
return types::reservation::ReservationResult::Occupied;
|
||||
}
|
||||
|
||||
return types::reservation::ReservationResult::Rejected;
|
||||
}
|
||||
|
||||
} // namespace module
|
||||
Reference in New Issue
Block a user