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:
1
tools/EVerest-main/modules/EVSE/Auth/.gitignore
vendored
Normal file
1
tools/EVerest-main/modules/EVSE/Auth/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
tests.txt
|
||||
235
tools/EVerest-main/modules/EVSE/Auth/Auth.cpp
Normal file
235
tools/EVerest-main/modules/EVSE/Auth/Auth.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#include <everest/helpers/helpers.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "Auth.hpp"
|
||||
#include <everest/logging.hpp>
|
||||
|
||||
namespace module {
|
||||
|
||||
void Auth::init() {
|
||||
invoke_init(*p_main);
|
||||
invoke_init(*p_reservation);
|
||||
|
||||
this->auth_handler = std::make_unique<AuthHandler>(
|
||||
string_to_selection_algorithm(this->config.selection_algorithm), this->config.connection_timeout,
|
||||
this->config.plug_in_timeout_enabled, this->config.prioritize_authorization_over_stopping_transaction,
|
||||
this->config.ignore_connector_faults, this->info.id,
|
||||
(!this->r_kvs.empty() ? this->r_kvs.at(0).get() : nullptr));
|
||||
|
||||
for (const auto& token_provider : this->r_token_provider) {
|
||||
token_provider->subscribe_provided_token([this](ProvidedIdToken provided_token) {
|
||||
{
|
||||
auto state = this->event_state.handle();
|
||||
if (!state->started) {
|
||||
EVLOG_warning << "Auth not fully initialized. Discarding provided token";
|
||||
return;
|
||||
}
|
||||
}
|
||||
std::thread t([this, provided_token]() { this->auth_handler->on_token(provided_token); });
|
||||
t.detach();
|
||||
});
|
||||
}
|
||||
for (const auto& token_validator : this->r_token_validator) {
|
||||
token_validator->subscribe_validate_result_update([this](ValidationResultUpdate validation_result_update) {
|
||||
{
|
||||
auto state = this->event_state.handle();
|
||||
if (!state->started) {
|
||||
EVLOG_warning << "Auth not fully initialized. Discarding validation result update";
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->auth_handler->handle_token_validation_result_update(validation_result_update);
|
||||
});
|
||||
}
|
||||
|
||||
// Subscribe to session events and errors in init() so we don't miss any events
|
||||
// Events received before ready() are queued.
|
||||
int32_t evse_index = 0;
|
||||
for (const auto& evse_manager : this->r_evse_manager) {
|
||||
const int32_t evse_idx = evse_index;
|
||||
|
||||
evse_manager->subscribe_session_event([this, evse_idx](SessionEvent session_event) {
|
||||
{
|
||||
auto state = this->event_state.handle();
|
||||
if (!state->started) {
|
||||
EVLOG_debug << "Auth not fully initialized, but received a session event on evse_index: "
|
||||
<< evse_idx << " that will be queued up: " << session_event.event;
|
||||
state->event_queue.emplace(evse_idx, session_event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->auth_handler->handle_session_event(this->auth_handler->get_evse_id_by_index(evse_idx), session_event);
|
||||
});
|
||||
|
||||
evse_manager->subscribe_error(
|
||||
"evse_manager/Inoperative",
|
||||
[this, evse_idx](const Everest::error::Error& error) {
|
||||
{
|
||||
auto state = this->event_state.handle();
|
||||
if (!state->started) {
|
||||
EVLOG_debug << "Auth not fully initialized, queuing permanent fault raised for evse_index: "
|
||||
<< evse_idx;
|
||||
state->event_queue.emplace(evse_idx, PermanentFaultRaised{1});
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->auth_handler->handle_permanent_fault_raised(this->auth_handler->get_evse_id_by_index(evse_idx),
|
||||
1);
|
||||
},
|
||||
[this, evse_idx](const Everest::error::Error& error) {
|
||||
{
|
||||
auto state = this->event_state.handle();
|
||||
if (!state->started) {
|
||||
EVLOG_debug << "Auth not fully initialized, queuing permanent fault cleared for evse_index: "
|
||||
<< evse_idx;
|
||||
state->event_queue.emplace(evse_idx, PermanentFaultCleared{1});
|
||||
return;
|
||||
}
|
||||
}
|
||||
this->auth_handler->handle_permanent_fault_cleared(this->auth_handler->get_evse_id_by_index(evse_idx),
|
||||
1);
|
||||
});
|
||||
|
||||
evse_index++;
|
||||
}
|
||||
}
|
||||
|
||||
void Auth::ready() {
|
||||
invoke_ready(*p_main);
|
||||
invoke_ready(*p_reservation);
|
||||
|
||||
int32_t evse_index = 0;
|
||||
for (const auto& evse_manager : this->r_evse_manager) {
|
||||
const int32_t evse_id = evse_manager->call_get_evse().id;
|
||||
std::vector<Connector> connectors;
|
||||
for (const auto& connector : evse_manager->call_get_evse().connectors) {
|
||||
connectors.push_back(
|
||||
Connector(connector.id, connector.type.value_or(types::evse_manager::ConnectorTypeEnum::Unknown)));
|
||||
}
|
||||
|
||||
this->auth_handler->init_evse(evse_id, evse_index, connectors);
|
||||
|
||||
evse_index++;
|
||||
}
|
||||
|
||||
this->auth_handler->register_publish_token_validation_status_callback(
|
||||
[this](const ProvidedIdToken& token, TokenValidationStatus status,
|
||||
const std::vector<MessageContent>& tariff_messages) {
|
||||
this->p_main->publish_token_validation_status({token, status, tariff_messages});
|
||||
});
|
||||
|
||||
this->auth_handler->register_notify_evse_callback(
|
||||
[this](const int evse_index, const ProvidedIdToken& provided_token, const ValidationResult& validation_result) {
|
||||
this->r_evse_manager.at(evse_index)->call_authorize_response(provided_token, validation_result);
|
||||
});
|
||||
this->auth_handler->register_withdraw_authorization_callback(
|
||||
[this](const int32_t evse_index) { this->r_evse_manager.at(evse_index)->call_withdraw_authorization(); });
|
||||
this->auth_handler->register_validate_token_callback([this](const ProvidedIdToken& provided_token) {
|
||||
std::vector<ValidationResult> validation_results;
|
||||
for (const auto& token_validator : this->r_token_validator) {
|
||||
try {
|
||||
const auto result = token_validator->call_validate_token(provided_token);
|
||||
validation_results.push_back(result);
|
||||
// TODO: This is very broad catch, make it more narrow when the everest-framework error handling will be
|
||||
// established
|
||||
} catch (const std::exception& e) {
|
||||
EVLOG_warning << "Exception during validating token: " << e.what();
|
||||
ValidationResult validation_result;
|
||||
validation_result.authorization_status = AuthorizationStatus::Unknown;
|
||||
validation_results.push_back(validation_result);
|
||||
}
|
||||
}
|
||||
return validation_results;
|
||||
});
|
||||
this->auth_handler->register_stop_transaction_callback(
|
||||
[this](const int32_t evse_index, const StopTransactionRequest& request) {
|
||||
this->r_evse_manager.at(evse_index)->call_stop_transaction(request);
|
||||
});
|
||||
this->auth_handler->register_reserved_callback(
|
||||
[this](const std::optional<int32_t> evse_id, const int32_t& reservation_id) {
|
||||
// Only call the evse manager to store the reservation if it is done for a specific evse.
|
||||
if (evse_id.has_value()) {
|
||||
EVLOG_info << "Call reserved callback for evse id " << evse_id.value();
|
||||
|
||||
if (!this->r_evse_manager.at(evse_id.value() - 1)->call_reserve(reservation_id)) {
|
||||
EVLOG_warning << "EVSE manager does not allow placing a reservation for evse id " << evse_id.value()
|
||||
<< ": cancelling reservation.";
|
||||
this->auth_handler->handle_cancel_reservation(reservation_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ReservationUpdateStatus status;
|
||||
status.reservation_id = reservation_id;
|
||||
status.reservation_status = Reservation_status::Placed;
|
||||
this->p_reservation->publish_reservation_update(status);
|
||||
return true;
|
||||
});
|
||||
this->auth_handler->register_reservation_cancelled_callback(
|
||||
[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() && 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();
|
||||
}
|
||||
|
||||
if (send_reservation_update) {
|
||||
ReservationUpdateStatus status;
|
||||
status.reservation_id = reservation_id;
|
||||
if (reason == ReservationEndReason::Expired) {
|
||||
status.reservation_status = Reservation_status::Expired;
|
||||
} else if (reason == ReservationEndReason::Cancelled) {
|
||||
status.reservation_status = Reservation_status::Removed;
|
||||
} else {
|
||||
// On reservation used: do not publish a reservation update!!
|
||||
return;
|
||||
}
|
||||
this->p_reservation->publish_reservation_update(status);
|
||||
}
|
||||
});
|
||||
|
||||
// Process any events that were queued during init before we were ready
|
||||
{
|
||||
auto state = this->event_state.handle();
|
||||
while (!state->event_queue.empty()) {
|
||||
auto queued_event = state->event_queue.front();
|
||||
state->event_queue.pop();
|
||||
const int32_t evse_id = this->auth_handler->get_evse_id_by_index(queued_event.evse_index);
|
||||
if (std::holds_alternative<SessionEvent>(queued_event.data)) {
|
||||
const auto& session_event = std::get<SessionEvent>(queued_event.data);
|
||||
EVLOG_debug << "Processing queued session event for evse_id: " << evse_id
|
||||
<< ", event: " << session_event.event;
|
||||
this->auth_handler->handle_session_event(evse_id, session_event);
|
||||
} else if (std::holds_alternative<PermanentFaultRaised>(queued_event.data)) {
|
||||
const auto& fault = std::get<PermanentFaultRaised>(queued_event.data);
|
||||
EVLOG_debug << "Processing queued permanent fault raised for evse_id: " << evse_id;
|
||||
this->auth_handler->handle_permanent_fault_raised(evse_id, fault.connector_id);
|
||||
} else if (std::holds_alternative<PermanentFaultCleared>(queued_event.data)) {
|
||||
const auto& fault = std::get<PermanentFaultCleared>(queued_event.data);
|
||||
EVLOG_debug << "Processing queued permanent fault cleared for evse_id: " << evse_id;
|
||||
this->auth_handler->handle_permanent_fault_cleared(evse_id, fault.connector_id);
|
||||
}
|
||||
}
|
||||
state->started = true;
|
||||
}
|
||||
|
||||
this->auth_handler->initialize();
|
||||
}
|
||||
|
||||
void Auth::set_connection_timeout(int& connection_timeout) {
|
||||
this->auth_handler->set_connection_timeout(connection_timeout);
|
||||
}
|
||||
|
||||
void Auth::set_master_pass_group_id(const std::string& master_pass_group_id) {
|
||||
this->auth_handler->set_master_pass_group_id(master_pass_group_id);
|
||||
}
|
||||
|
||||
WithdrawAuthorizationResult Auth::handle_withdraw_authorization(const WithdrawAuthorizationRequest& request) {
|
||||
return this->auth_handler->handle_withdraw_authorization(request);
|
||||
}
|
||||
|
||||
} // namespace module
|
||||
138
tools/EVerest-main/modules/EVSE/Auth/Auth.hpp
Normal file
138
tools/EVerest-main/modules/EVSE/Auth/Auth.hpp
Normal file
@@ -0,0 +1,138 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef AUTH_HPP
|
||||
#define AUTH_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 2
|
||||
//
|
||||
|
||||
#include "ld-ev.hpp"
|
||||
|
||||
// headers for provided interface implementations
|
||||
#include <generated/interfaces/auth/Implementation.hpp>
|
||||
#include <generated/interfaces/reservation/Implementation.hpp>
|
||||
|
||||
// headers for required interface implementations
|
||||
#include <generated/interfaces/auth_token_provider/Interface.hpp>
|
||||
#include <generated/interfaces/auth_token_validator/Interface.hpp>
|
||||
#include <generated/interfaces/evse_manager/Interface.hpp>
|
||||
#include <generated/interfaces/kvs/Interface.hpp>
|
||||
|
||||
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
|
||||
// insert your custom include headers here
|
||||
#include <AuthHandler.hpp>
|
||||
#include <everest/util/async/monitor.hpp>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <variant>
|
||||
|
||||
using namespace types::evse_manager;
|
||||
using namespace types::authorization;
|
||||
|
||||
struct PermanentFaultRaised {
|
||||
int32_t connector_id;
|
||||
};
|
||||
|
||||
struct PermanentFaultCleared {
|
||||
int32_t connector_id;
|
||||
};
|
||||
|
||||
using AuthEvent = std::variant<SessionEvent, PermanentFaultRaised, PermanentFaultCleared>;
|
||||
|
||||
struct EvseEvent {
|
||||
int32_t evse_index;
|
||||
AuthEvent data;
|
||||
|
||||
EvseEvent(int32_t evse_index_, AuthEvent data_) : evse_index(evse_index_), data(std::move(data_)) {
|
||||
}
|
||||
};
|
||||
|
||||
struct EventQueueState {
|
||||
bool started{false};
|
||||
std::queue<EvseEvent> event_queue;
|
||||
};
|
||||
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
|
||||
|
||||
namespace module {
|
||||
|
||||
struct Conf {
|
||||
std::string selection_algorithm;
|
||||
int connection_timeout;
|
||||
std::string master_pass_group_id;
|
||||
bool prioritize_authorization_over_stopping_transaction;
|
||||
bool ignore_connector_faults;
|
||||
bool plug_in_timeout_enabled;
|
||||
};
|
||||
|
||||
class Auth : public Everest::ModuleBase {
|
||||
public:
|
||||
Auth() = delete;
|
||||
Auth(const ModuleInfo& info, std::unique_ptr<authImplBase> p_main,
|
||||
std::unique_ptr<reservationImplBase> p_reservation,
|
||||
std::vector<std::unique_ptr<auth_token_providerIntf>> r_token_provider,
|
||||
std::vector<std::unique_ptr<auth_token_validatorIntf>> r_token_validator,
|
||||
std::vector<std::unique_ptr<evse_managerIntf>> r_evse_manager, std::vector<std::unique_ptr<kvsIntf>> r_kvs,
|
||||
Conf& config) :
|
||||
ModuleBase(info),
|
||||
p_main(std::move(p_main)),
|
||||
p_reservation(std::move(p_reservation)),
|
||||
r_token_provider(std::move(r_token_provider)),
|
||||
r_token_validator(std::move(r_token_validator)),
|
||||
r_evse_manager(std::move(r_evse_manager)),
|
||||
r_kvs(std::move(r_kvs)),
|
||||
config(config){};
|
||||
|
||||
const std::unique_ptr<authImplBase> p_main;
|
||||
const std::unique_ptr<reservationImplBase> p_reservation;
|
||||
const std::vector<std::unique_ptr<auth_token_providerIntf>> r_token_provider;
|
||||
const std::vector<std::unique_ptr<auth_token_validatorIntf>> r_token_validator;
|
||||
const std::vector<std::unique_ptr<evse_managerIntf>> r_evse_manager;
|
||||
const std::vector<std::unique_ptr<kvsIntf>> r_kvs;
|
||||
const Conf& config;
|
||||
|
||||
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
|
||||
// insert your public definitions here
|
||||
std::unique_ptr<AuthHandler> auth_handler;
|
||||
|
||||
/**
|
||||
* @brief Set the connection timeout for the auth handler
|
||||
*
|
||||
* @param connection_timeout timeout in seconds
|
||||
*/
|
||||
void set_connection_timeout(int& connection_timeout);
|
||||
|
||||
/**
|
||||
* @brief Set the master pass group id for the auth handler
|
||||
*
|
||||
* @param master_pass_group_id master pass group id
|
||||
*/
|
||||
void set_master_pass_group_id(const std::string& master_pass_group_id);
|
||||
|
||||
WithdrawAuthorizationResult handle_withdraw_authorization(const WithdrawAuthorizationRequest& request);
|
||||
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
|
||||
|
||||
protected:
|
||||
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
|
||||
// insert your protected definitions here
|
||||
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
|
||||
|
||||
private:
|
||||
friend class LdEverest;
|
||||
void init();
|
||||
void ready();
|
||||
|
||||
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
|
||||
// insert your private definitions here
|
||||
everest::lib::util::monitor<EventQueueState> event_state;
|
||||
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
|
||||
};
|
||||
|
||||
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
|
||||
// insert other definitions here
|
||||
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
|
||||
|
||||
} // namespace module
|
||||
|
||||
#endif // AUTH_HPP
|
||||
42
tools/EVerest-main/modules/EVSE/Auth/BUILD.bazel
Normal file
42
tools/EVerest-main/modules/EVSE/Auth/BUILD.bazel
Normal file
@@ -0,0 +1,42 @@
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||
load("//modules:module.bzl", "cc_everest_module")
|
||||
|
||||
cc_library(
|
||||
name = "auth_handler",
|
||||
srcs = glob(["lib/*.cpp"]),
|
||||
hdrs = glob(["include/*.hpp"]),
|
||||
strip_include_prefix = "include",
|
||||
deps = [
|
||||
"//lib/everest/timer:libtimer",
|
||||
"@boost.asio",
|
||||
"//lib/everest/framework:framework",
|
||||
"@com_github_HowardHinnant_date//:date",
|
||||
"//types:types_lib",
|
||||
"//interfaces:interfaces_lib",
|
||||
"//lib/everest/helpers",
|
||||
"//lib/everest/util",
|
||||
],
|
||||
# See https://github.com/HowardHinnant/date/issues/324
|
||||
local_defines = [
|
||||
"BUILD_TZ_LIB=ON",
|
||||
"USE_SYSTEM_TZ_DB=ON",
|
||||
"USE_OS_TZDB=1",
|
||||
"USE_AUTOLOAD=0",
|
||||
"HAS_REMOTE_API=0",
|
||||
],
|
||||
copts = ["-std=c++17"],
|
||||
)
|
||||
|
||||
IMPLS = [
|
||||
"main",
|
||||
"reservation",
|
||||
]
|
||||
|
||||
cc_everest_module(
|
||||
name = "Auth",
|
||||
deps = [
|
||||
"//lib/everest/timer:libtimer",
|
||||
":auth_handler",
|
||||
],
|
||||
impls = IMPLS,
|
||||
)
|
||||
40
tools/EVerest-main/modules/EVSE/Auth/CMakeLists.txt
Normal file
40
tools/EVerest-main/modules/EVSE/Auth/CMakeLists.txt
Normal file
@@ -0,0 +1,40 @@
|
||||
#
|
||||
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
# template version 3
|
||||
#
|
||||
|
||||
# module setup:
|
||||
# - ${MODULE_NAME}: module name
|
||||
ev_setup_cpp_module()
|
||||
|
||||
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
|
||||
# insert your custom targets and additional config variables here
|
||||
option(BUILD_TESTING "Run unit tests" OFF)
|
||||
|
||||
add_subdirectory(lib)
|
||||
|
||||
target_include_directories(${MODULE_NAME} PRIVATE "include")
|
||||
|
||||
target_link_libraries(${MODULE_NAME}
|
||||
PRIVATE
|
||||
auth_handler
|
||||
date::date
|
||||
date::date-tz
|
||||
everest::timer
|
||||
everest::helpers
|
||||
everest::util
|
||||
)
|
||||
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
|
||||
|
||||
target_sources(${MODULE_NAME}
|
||||
PRIVATE
|
||||
"main/authImpl.cpp"
|
||||
"reservation/reservationImpl.cpp"
|
||||
)
|
||||
|
||||
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
|
||||
# insert other things like install cmds etc here
|
||||
if(EVEREST_CORE_BUILD_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
|
||||
@@ -0,0 +1,249 @@
|
||||
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="701px" height="376px" viewBox="-0.5 -0.5 701 376" content="<mxfile><diagram id="4SK5cN6X-azEmNZbr7fQ" name="Page-1">7Vrbdto4FP0a1pp5KMu2sIFHHJJMQtPQZpJp5iVL2DJ2KiwqywT60G+fI1sCX4CQadKQCw8J2jq6nq3tc2Qa6GAyP+Z4Gp4xn9CGZfjzBuo3LMvptOGvBBY5gOxuDox55OeQuQIuoh9EgYZC08gnSclQMEZFNC2DHotj4okShjlnd2WzgNHyqFM8JjXgwsO0jv4T+SLM0Y5trPC/SDQO9cimoWomWBsrIAmxz+4KEDpsoAPOmMi/TeYHhMq90/uStzvaULucGCexWNPgMiH8fHQr98QyKB6BWzKjv9k3EgM05GwGW8vzbmjqRb4XYi7yNidqlPSmN7B634UxP+/nlnp8a7mE5dQSsdDbFoqJHM9sIJfNCA9otvIRZd43gLL/F1PsRfFYWd2FkSASkh3cAZUAC1gsFCNMp9lWyAGjjGejIIRbZgsDjmk0jgHzYC9gSchNlp13oQQzEBH4tKfMJpHvy5m6ieCwHet6zGvOZTdCUlf6FcaPKK1inKWxT3y1DjxKGE0F6XFPT12iy1JX9VIYM7ACFNjLMTXNjGYHsLqXlePlmsi8AClfHBM2IYIvwETVIkVAdQKtLsrLdys+W7adY2GByy2FYXWExsueVyyDL8r9uljg3VYebued3+wNqBlPrTBIy7xr7cq7WyLEQvugswPF0DpCOMj2g2AjIda5rEKIFRFg3i7xQV7UfBkXIRuzGNPDFQpdwm70pHSBScxiiZHY14g+QwAdRVQvlswj8TWbga1K17KmaRgdVR4SHoH75OnoG1l7cGWhiSzKNh8M3Ugi1VYb6ZiwlHukIA2g05iPibJyckiufitlC5xcKmmRk0uQE4pFNCur9DqmZmPA5uFFwWDKolgkNSIvJ7Ijt2uirE7MDNNUz6wJZaW5VpmrcHyhH7dAWU6S6AceZQZyt7Pj4mLv2zjjlKalTwKcUrFO9zYqXUZ0Nba5RkmzT/VANNFal7d2VqCdvbRBTzaJRy8FllgGRBsprGirkghQksVk8dFtX/bKSuK8P8Fe5hOsZXf24QmmI6kr8J2PBbsnlDoDIh4Y9s+z4MQrE7H9TsSXSUTHegGhlAG8+3z24Vy0To7LvOv83lCKdExUqNn/UAqS1YcHU6pRKZxq/1I45dTDqfbbCKe6tXDKauZlKbjkRkgF/iPy8y9/vp4Iq/PsEdb9mjI/obfuZYTKmmIajX0SFR8n4VI/gmjeh7IqyaohFuBV6UZYRna690+F2oZTUKFVerZdhlQrLUPmL6tQ+80mdaYOE1YyhKQMfSGJ1IhXIzr6yvUZVedwlhCZ1+EYj+XVpGHeE1Njd8B+dK5QaOKKDr3fT76UoLplV9I7w96HqLrGxXueiV+Ai7OfJ6f/fpm6FS6idy6+UC7a1l5wcTvzmpJ5X8mQ9x1eYd5vvi1/YSmejIp0+bpRvD3fGFll8ZdZDK0K9+ePld6Z6I1EVvX7cpyClzlQ4DWmdebz35xvV5LrE3fw6ah3OhjOFxUl2fm2/E29d9t4wvf36E7mQ4kVnnk63tI/0TArz7JcrVSrLR3ZbWN7R7nG1Tp6JDFp18TkCvPkFamHs+fq0QT1aN+E8XfWGVbU4/2q+c2oRzUS/t/qUe3oadWjftd8MPFfk3o8/53yBaGARUy+NPXJFM4C7K+RFYtVlI0jT1YEctj8Tf9khzf96NQduP1P6Gh25ZTlx9r5UvpxEnBjh/SbkkCUkm+UyVCVC4JN18hYPb9+nOw6+zxddo2Myk0PqmfXprVGzlrGg0n4gOxaZxoN1MuIwaVDZF6GMk+KxZRsqFK/OmUQZmQGWOlnRt1Mp5amU070eys/Nx4xRgsG28nNzeHg6ONw1j9j3Qq5zacndxJiuQd9n3npJPO/O8rJ9nGkey8cgE4TPc0J2P326eFn4Yk4b3Yrz0enW+e8seZGyXYeg/MArX5inD8qV7/TRof/AQ==</diagram></mxfile>">
|
||||
<defs/>
|
||||
<g>
|
||||
<rect x="25" y="0" width="255" height="45" rx="4.5" ry="4.5" fill="#f2f3f5" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 239px; height: 1px; padding-top: 23px; margin-left: 33px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 237px; max-height: 27px;">
|
||||
<div style="display: inline-block; font-size: 16.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Token Provider
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="153" y="28" fill="#3a414a" font-family="Helvetica" font-size="17px" text-anchor="middle">
|
||||
Token Provider
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 152.5 45.36 L 152.5 156.95" fill="none" stroke="#635dff" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 152.5 163.75 L 149.1 156.95 L 155.9 156.95 Z" fill="#635dff" stroke="#635dff" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 106px; margin-left: 153px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
1. Token
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="153" y="110" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
1. Token
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="25" y="165" width="255" height="45" rx="4.5" ry="4.5" fill="#f2f3f5" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 239px; height: 1px; padding-top: 188px; margin-left: 33px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 237px; max-height: 27px;">
|
||||
<div style="display: inline-block; font-size: 16.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Auth Module
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="153" y="193" fill="#3a414a" font-family="Helvetica" font-size="17px" text-anchor="middle">
|
||||
Auth Module
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="25" y="330" width="255" height="45" rx="4.5" ry="4.5" fill="#f2f3f5" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 239px; height: 1px; padding-top: 353px; margin-left: 33px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 237px; max-height: 27px;">
|
||||
<div style="display: inline-block; font-size: 16.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Token Validator
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="153" y="358" fill="#3a414a" font-family="Helvetica" font-size="17px" text-anchor="middle">
|
||||
Token Validator
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 84.92 210.36 L 84.92 321.99" fill="none" stroke="#e81313" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 84.92 328.79 L 81.52 321.99 L 88.32 321.99 Z" fill="#e81313" stroke="#e81313" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 271px; margin-left: 85px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
2. validate_token(id_token)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="85" y="275" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
2. validate_token(id_token)
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 205.03 329.64 L 205.03 218.01" fill="none" stroke="#e81313" stroke-miterlimit="10" stroke-dasharray="3 8" pointer-events="stroke"/>
|
||||
<path d="M 205.03 211.21 L 208.43 218.01 L 201.63 218.01 Z" fill="#e81313" stroke="#e81313" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 270px; margin-left: 206px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
3. Result
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="206" y="274" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
3. Result
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="445" y="112" width="255" height="45" rx="4.5" ry="4.5" fill="#f2f3f5" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 239px; height: 1px; padding-top: 135px; margin-left: 453px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 237px; max-height: 27px;">
|
||||
<div style="display: inline-block; font-size: 16.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Evse Manager 1
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="573" y="140" fill="#3a414a" font-family="Helvetica" font-size="17px" text-anchor="middle">
|
||||
Evse Manager 1
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="445" y="232" width="255" height="45" rx="4.5" ry="4.5" fill="#f2f3f5" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 239px; height: 1px; padding-top: 255px; margin-left: 453px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 237px; max-height: 27px;">
|
||||
<div style="display: inline-block; font-size: 16.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Evse Manager 2
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="573" y="260" fill="#3a414a" font-family="Helvetica" font-size="17px" text-anchor="middle">
|
||||
Evse Manager 2
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 280.25 187.5 L 356.5 187.5 Q 362.5 187.5 362.5 193.5 L 362.5 248.5 Q 362.5 254.5 368.5 254.5 L 437.05 254.5" fill="none" stroke="#e81313" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 443.85 254.5 L 437.05 257.9 L 437.05 251.1 Z" fill="#e81313" stroke="#e81313" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 222px; margin-left: 363px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
authorize(id_token)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="363" y="226" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
authorize(id_token)
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 400 22 L 557.31 22" fill="none" stroke="#635dff" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 564.11 22 L 557.31 25.4 L 557.31 18.6 Z" fill="#635dff" stroke="#635dff" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 23px; margin-left: 484px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Vars
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="484" y="26" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
Vars
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 400 52 L 557.31 52" fill="none" stroke="#e81313" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 564.11 52 L 557.31 55.4 L 557.31 48.6 Z" fill="#e81313" stroke="#e81313" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 53px; margin-left: 484px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Cmds
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="484" y="56" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
Cmds
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="295" y="142" width="120" height="40" rx="4.5" ry="4.5" fill-opacity="0" fill="#ffffff" stroke="rgb(0, 0, 0)" stroke-opacity="0" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 116px; height: 1px; padding-top: 145px; margin-left: 298px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: left; width: 114px;">
|
||||
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Selection depends on Selection logic of auth module
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="298" y="155" fill="#3a414a" font-family="Helvetica" font-size="10px">
|
||||
Selection depends on Se...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 190 76 L 295 76 L 295 123.6 Q 268.75 108.48 242.5 123.6 Q 216.25 138.72 190 123.6 L 190 84.4 Z" fill="rgb(255, 255, 255)" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 101px; height: 1px; padding-top: 79px; margin-left: 193px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: left; width: 99px;">
|
||||
<div style="display: inline-block; font-size: 8.3px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
id_token: string
|
||||
<br/>
|
||||
type: string
|
||||
<br/>
|
||||
connectors: array of int
|
||||
<br/>
|
||||
prevalidated: bool
|
||||
<div>
|
||||
<br/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="193" y="87" fill="#3a414a" font-family="Helvetica" font-size="8px">
|
||||
id_token: string...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
</g>
|
||||
<switch>
|
||||
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
|
||||
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
|
||||
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
|
||||
Viewer does not support full SVG 1.1
|
||||
</text>
|
||||
</a>
|
||||
</switch>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 22 KiB |
87
tools/EVerest-main/modules/EVSE/Auth/docs/index.rst
Normal file
87
tools/EVerest-main/modules/EVSE/Auth/docs/index.rst
Normal file
@@ -0,0 +1,87 @@
|
||||
.. _everest_modules_handwritten_Auth:
|
||||
|
||||
.. ===========
|
||||
.. Auth Module
|
||||
.. ===========
|
||||
|
||||
This module handles incoming authorization and reservation requests.
|
||||
|
||||
The task of the module is to receive tokens from token providers, validate them and assign them to EvseManagers.
|
||||
It is responsible for providing authorization to EvseManagers and for stopping transactions at the EvseManagers if a token
|
||||
or parent id token is presented to stop a transaction. In addition, the module is responsible for managing all
|
||||
reservations and matching them with incoming tokens.
|
||||
|
||||
The module contains the logic to select a connector for incoming tokens (e.g. by waiting for a car plug in, user
|
||||
interface, random selection, etc.). Currently two selection algorithms are implemented and described in
|
||||
`Selection Algorithm`_.
|
||||
|
||||
The following flow diagram describes how an incoming token is handled by the module.
|
||||
|
||||
.. image:: token_handling.drawio.svg
|
||||
:alt: TokenHandling
|
||||
|
||||
.. note::
|
||||
|
||||
The processing of each authorization request and the respective validation runs in an individual thread. This
|
||||
allows the parallel processing of authorization requests.
|
||||
|
||||
Integration in EVerest
|
||||
======================
|
||||
|
||||
This module provides implementations for the `reservation` and the `auth` interfaces.
|
||||
|
||||
It requires connections to module(s) implementing the `token_provider`, `token_validator` and `evse_manager` interfaces.
|
||||
|
||||
The following diagram shows how it integrates with other EVerest modules.
|
||||
|
||||
.. image:: everest_integration.drawio.svg
|
||||
:alt: Integration
|
||||
|
||||
The module connections of the evse_manager requirement must be connected in the correct order in the EVerest config
|
||||
file, i.e. the module representing the EVSE with evse id 1 must listed first, EVSE with evse id 2 second and so on.
|
||||
|
||||
Selection Algorithm
|
||||
===================
|
||||
|
||||
The selection algorithm contains the logic to select one connector for an incoming token. The algorithm can be
|
||||
specified within the module config using the key `selection_algorithm`. In case the charging station has only
|
||||
one connector, the selection of a connector is pretty straight-forward because there is only one that is
|
||||
available. The selection algorithm becomes relevant in case the Auth module manages authorization requests
|
||||
for multiple connectors.
|
||||
|
||||
Three options for the selection are available:
|
||||
|
||||
* PlugEvents
|
||||
* FindFirst
|
||||
* UserInput
|
||||
|
||||
PlugEvents
|
||||
----------
|
||||
|
||||
The following flow chart describes how a connector is selected using the `PlugEvents` algorithm.
|
||||
|
||||
.. image:: plug_events_selection_algorithm.drawio.svg
|
||||
:alt: SelectionAlgorithm
|
||||
|
||||
.. note::
|
||||
|
||||
In case a user authorizes first and no EV is connected to the charger to initiate a SessionStarted event the
|
||||
processing thread waits for a SessionStarted event to select the respective connector. A Plug-In timeout may
|
||||
occur, which will require a subsequent initiation of authorization to start a charging session.
|
||||
|
||||
FindFirst
|
||||
---------
|
||||
|
||||
The `FindFirst` selection method simply selects the first available connector that has no active transaction.
|
||||
This method attempts to select a connector immediately.
|
||||
|
||||
UserInput
|
||||
---------
|
||||
|
||||
Not yet implemented.
|
||||
|
||||
Plug&Charge Authorization
|
||||
=========================
|
||||
|
||||
Please see the :doc:`Plug&Charge configuration guide </how-to-guides/configure-pnc>`
|
||||
for further information about the Plug&Charge integration in EVerest.
|
||||
@@ -0,0 +1,248 @@
|
||||
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="729px" height="417px" viewBox="-0.5 -0.5 729 417" content="<mxfile><diagram id="-amA90pGGpgJD1hGHcMB" name="Page-1">7VtZc9o6FP41PJbxgo15DFsWbsq9Q5I2fekILGMlxqK22PrQ394jWTK2MYRLS2hJMpMEHWs5kj59Z5GpmK3J8jJCU/+WujioGJq7rJjtimHYTh3+csEqEZhWIxGMI+ImIn0tGJDvWAo1KZ0RF8e5iozSgJFpXjiiYYhHLCdDUUQX+WoeDfKjTtEYbwgGIxRsSj8Rl/mJ1LG0tfwKk7GvRtY1+WSCVGUpiH3k0kVGZHYqZiuilCWfJssWDvjaqXVJ2nW3PE0Vi3DIShrcxzjqD5/4mhhagIawLaLStQvlO/qMQ/i/IKAk6DrjCwpTTteRRnz94piMwwnvX4wVzEbEHfkoYknH11IV60u/d60vv6FQf0pqKiXNdJ6p/jFbqbX12YQrpVfMJp3jyAvE8gwDOnoGkfg/mKIRCcey1sInDHMR72ABeANZRGehi11ZA0UjiSHYIbPp0ZDJsq5X61LSogHMj6tgmqim1xDI43Qgs+rwMotgjUpriid9Xp9xUPMdV1IFEU30sblDctNgsgwvMyK5RJeYTjCLVlBFPoXFTZqo49NwkvJiDUbdlHX8DBBrEnVI4n+cdr2GCHyQ26KKGdDsBFE/DLiSNOSQoV6KFexmIRRz+RwRaMi3vLsbRU2t37u4vifd9qfbPIpqr4Ain06Gs7gAGe2IgCngdhjTYMbwRYrfPJobR4SYaeYgVnNKIKZILAuxxlEhthssXQDL5eKmPbnBj3mwWPuC5QkztlKL7WzBRRYN5m+ihW2MpYPeTeyC+ZH60oj5dExDFHTWUugSVuOCmzaoEvIjCK1CV0kU8EHUJYGaLF4S9lloYMnSI39S1bSGLP+LIwLbh/m8NNEetjLThBcfRVFLFOWCYqOtcIzpLBrhjFEAM46iMVZslYj45HdCNgtJrQSSqTDCAWJknjfiZUAVY8DaoVWmwpSSkMUbOE4V+UVoD3CQyJhP4rzN3YX5mt7vPYwd2xm2f+Qxbx+fIA+xpI2/mRZrivIkLVrKI33R8tqno8XI6Pda3fqURO12HiL149Fi1SxBg/hRNeVA+lkQaMOpZSh0TahbCfQDUKZm5jk0pdSDGLS2yaD2WTLoRtQjXZM5CmZSs0ccywVawxmOOHTQzKA6wjH5nrihYpnFiWqi0fNYgEnh0cUegliIoyoAdxYkI9gHvjlNzhwE4sIL+WBCXFfAqYjwnSchd2Y297q+N0vtvT1bKGcbv3RCdzfFtIFinlaLwZ11h/MU03gFN/2sgz3byHvilm7uaXJO6InfAh4e3YepMbLtPB50bV9AvElXXFdW4ZSWxN60JI2ztCSHgfszgNuho49jtKQFcOvHA/cbc6is3CFoOM6rR6Ql/pR+niHpZhrZ2PCoPtLzcaj0/Y3wsTyqi0JWXYT8JBThP0+fohmcqQgWkxHKpd9meIaToTlKhUx8Tqp35jG+RSEaY95ZDIdKpFnhd4DjGLoY8NMoErF4LlL3GuI+nRbSksF80fMK82pDLO4EprC1fI/cKhRKlY/wBFw43qcX0cmLE1n4ol/CVPtY5D9EB8NV2jyRilGSobz0kdQg6ZbB+aYz3hleQqCr3P9tFH5h9nvuRHuuGn23QOGvcDuxbzZZnY4Ae6zEQS2eFUanJQQvBgCGzUv+f0aF95FR0hM/x3N7zXyixTZLEi1OKbPWjun2trKwD7fi+6Vrjbt6v9cKwvvetFm41tDf7zVe817DNv6Ie40Dk786wCj6cuU+Ln5MCzDa+8bjPft7cPbXtv6C7K8DGJl2boh+w4wCRva+IXiPVv789K9eFq9YbyReUSnSM80A6/bJA5bdJFMFkml/vR0+uD+K/rRzPJI5A+o4IN9XSHSk1/WHM4e1yRxvK9/3CYk41BNufchZoiRw7sxffOcNOf3eB6trenf3k8IxeIV7kDfnjxXfg6vb+74Hd0J/zAeMPFRnhvGP5eUxYhzxauSN+WO/mj0W7pnz+/2x86TVDX8sPcrnmT9WYeAJ3bEDEwZLIJ/mcNH+uuoOC+RjvBuooycM/goD1QGM3PznXTNjXEgq7f/m/rsvXzlVGqCxaXbUjp+Z2TkM3lcAb7AOXuv+Q+El8f2/UvAO7/3grVXrTuW3xqpqd88/VgXR+mtgSQ/r79KZnZ8=</diagram></mxfile>">
|
||||
<defs/>
|
||||
<g>
|
||||
<rect x="195" y="0" width="135" height="40" rx="20" ry="20" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 131px; height: 1px; padding-top: 20px; margin-left: 197px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 129px; max-height: 34px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Id Token with multiple connector assignment
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="263" y="24" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Id Token with multiple...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 233 112.5 L 259.4 92.7 Q 263 90 266.6 92.7 L 319.4 132.3 Q 323 135 319.4 137.7 L 266.6 177.3 Q 263 180 259.4 177.3 L 206.6 137.7 Q 203 135 206.6 132.3 Z" fill="rgb(255, 255, 255)" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 116px; height: 1px; padding-top: 135px; margin-left: 205px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 114px; max-height: 84px;">
|
||||
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Only one of assigned connectors available?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="263" y="138" fill="#3a414a" font-family="Helvetica" font-size="10px" text-anchor="middle">
|
||||
Only one of assigned co...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 262.5 40.36 L 262.5 59 Q 262.5 65 262.62 71 L 262.85 83.39" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 262.98 90.19 L 259.45 83.45 L 266.25 83.32 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="390" y="112" width="135" height="46" rx="4.5" ry="4.5" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 119px; height: 1px; padding-top: 135px; margin-left: 398px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 117px; max-height: 28px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Select this connector
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="458" y="139" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Select this connector
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 321.08 135 L 381.9 135" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 388.7 135 L 381.9 138.4 L 381.9 131.6 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 136px; margin-left: 356px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Yes
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="356" y="139" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
Yes
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="593" y="115" width="135" height="40" rx="20" ry="20" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 131px; height: 1px; padding-top: 135px; margin-left: 595px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 129px; max-height: 34px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
End
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="661" y="139" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
End
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 525.4 135 L 584.9 135" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 591.7 135 L 584.9 138.4 L 584.9 131.6 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 263 178.92 L 263 223.39" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 263 230.19 L 259.6 223.39 L 266.4 223.39 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 205px; margin-left: 264px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
No
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="264" y="209" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
No
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="0" y="232" width="180" height="104" rx="4.5" ry="4.5" fill-opacity="0" fill="#ffffff" stroke="rgb(0, 0, 0)" stroke-opacity="0" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 176px; height: 1px; padding-top: 235px; margin-left: 3px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: left; width: 174px;">
|
||||
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
A connector is in the authorization queue as soon as the EvseManager sends a SessionStarted event and no authorization has yet been presented. A connector is removed from the authorization queue when it is selected by the selector or if the connection timeout expires
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="3" y="245" fill="#3a414a" font-family="Helvetica" font-size="10px">
|
||||
A connector is in the authorization...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 233 252.5 L 259.4 232.7 Q 263 230 266.6 232.7 L 319.4 272.3 Q 323 275 319.4 277.7 L 266.6 317.3 Q 263 320 259.4 317.3 L 206.6 277.7 Q 203 275 206.6 272.3 Z" fill="rgb(255, 255, 255)" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 116px; height: 1px; padding-top: 275px; margin-left: 205px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 114px; max-height: 84px;">
|
||||
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Connector in authorization queue?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="263" y="278" fill="#3a414a" font-family="Helvetica" font-size="10px" text-anchor="middle">
|
||||
Connector in authorizat...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="390" y="252" width="135" height="46" rx="4.5" ry="4.5" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 119px; height: 1px; padding-top: 275px; margin-left: 398px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 117px; max-height: 28px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Select this connector
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="458" y="279" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Select this connector
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 321.08 275 L 381.9 275" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 388.7 275 L 381.9 278.4 L 381.9 271.6 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 276px; margin-left: 356px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Yes
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="356" y="279" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
Yes
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 525.4 275 L 654.5 275 Q 660.5 275 660.5 269 L 660.5 163.05" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 660.5 156.25 L 663.9 163.05 L 657.1 163.05 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="195" y="370" width="135" height="46" rx="4.5" ry="4.5" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 119px; height: 1px; padding-top: 393px; margin-left: 203px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 117px; max-height: 28px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Wait for next SessionStarted Event
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="263" y="397" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Wait for next Sessio...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 263 318.92 L 263 339 Q 263 345 262.88 351 L 262.66 361.94" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 262.52 368.74 L 259.26 361.87 L 266.06 362.01 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 346px; margin-left: 264px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
No
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="264" y="349" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
No
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="390" y="370" width="135" height="46" rx="4.5" ry="4.5" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 119px; height: 1px; padding-top: 393px; margin-left: 398px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 117px; max-height: 28px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Select this connector
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="458" y="397" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Select this connector
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 330.4 393 L 381.9 393" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 388.7 393 L 381.9 396.4 L 381.9 389.6 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 525.4 393 L 692.3 393 Q 698.3 393 698.3 387 L 698.3 163.05" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 698.3 156.25 L 701.7 163.05 L 694.9 163.05 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
</g>
|
||||
<switch>
|
||||
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
|
||||
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
|
||||
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
|
||||
Viewer does not support full SVG 1.1
|
||||
</text>
|
||||
</a>
|
||||
</switch>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 23 KiB |
191
tools/EVerest-main/modules/EVSE/Auth/docs/state_chart.drawio
Normal file
191
tools/EVerest-main/modules/EVSE/Auth/docs/state_chart.drawio
Normal file
@@ -0,0 +1,191 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="enTwcsW3kJKdFbbsoOIB" name="Page-1">
|
||||
<mxGraphModel dx="910" dy="576" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="50" style="edgeStyle=none;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" target="49" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="1120" y="524" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="75" style="edgeStyle=none;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" edge="1" parent="1" source="7" target="73">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="180" y="250"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="76" value="TransactionStarted" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="75">
|
||||
<mxGeometry x="-0.3167" y="-4" relative="1" as="geometry">
|
||||
<mxPoint x="29" y="-45" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="77" style="edgeStyle=none;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" edge="1" parent="1" source="7" target="71">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="78" value="Faulted" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="77">
|
||||
<mxGeometry x="-0.0286" relative="1" as="geometry">
|
||||
<mxPoint x="1" y="-10" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="79" style="edgeStyle=none;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" edge="1" parent="1" source="7" target="11">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="210" y="490"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="80" value="Disable" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="79">
|
||||
<mxGeometry x="0.3063" y="2" relative="1" as="geometry">
|
||||
<mxPoint x="11" y="-8" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="Available" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" parent="1" vertex="1">
|
||||
<mxGeometry x="120" y="360" width="120" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="9" value="" style="ellipse;html=1;shape=startState;fillColor=#000000;strokeColor=#ff0000;" parent="1" vertex="1">
|
||||
<mxGeometry x="70" y="365" width="30" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="10" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=0.867;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="9" target="7" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="210" y="330" as="targetPoint"/>
|
||||
<mxPoint x="110" y="390" as="sourcePoint"/>
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="89" style="edgeStyle=none;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="11" target="7">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="180" y="510"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="90" value="Enable" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="89">
|
||||
<mxGeometry x="-0.2428" y="1" relative="1" as="geometry">
|
||||
<mxPoint x="35" y="9" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="91" style="edgeStyle=none;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="11" target="34">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="610" y="490"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="92" value="Faulted" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="91">
|
||||
<mxGeometry x="-0.1389" y="-1" relative="1" as="geometry">
|
||||
<mxPoint x="-23" y="-11" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="11" value="Unavailable" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" parent="1" vertex="1">
|
||||
<mxGeometry x="320" y="480" width="120" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="93" style="edgeStyle=none;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" edge="1" parent="1" source="34" target="71">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="94" value="Enable" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="93">
|
||||
<mxGeometry x="0.1894" y="1" relative="1" as="geometry">
|
||||
<mxPoint x="13" y="9" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="95" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" edge="1" parent="1" source="34" target="11">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="640" y="510"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="96" value="ErrorCleared" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="95">
|
||||
<mxGeometry x="0.1272" y="3" relative="1" as="geometry">
|
||||
<mxPoint x="-35" y="7" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="34" value="UnavailableFaulted" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" parent="1" vertex="1">
|
||||
<mxGeometry x="580" y="360" width="120" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="49" value="Matched" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" parent="1" vertex="1">
|
||||
<mxGeometry x="1320" y="400" width="140" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="85" style="edgeStyle=none;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" edge="1" parent="1" source="71" target="7">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="86" value="ErrorCleared" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="85">
|
||||
<mxGeometry x="-0.1888" y="-2" relative="1" as="geometry">
|
||||
<mxPoint x="-7" y="12" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="87" style="edgeStyle=none;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" edge="1" parent="1" source="71" target="34">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="580" y="370" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="88" value="Disable" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="87">
|
||||
<mxGeometry x="-0.1806" y="4" relative="1" as="geometry">
|
||||
<mxPoint x="11" y="-6" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="71" value="Faulted" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
|
||||
<mxGeometry x="320" y="360" width="120" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="81" style="edgeStyle=none;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="73" target="7">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="210" y="270"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="82" value="SessionFinished" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="81">
|
||||
<mxGeometry x="0.1526" relative="1" as="geometry">
|
||||
<mxPoint x="30" y="-8" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="83" style="edgeStyle=none;html=1;entryX=0;entryY=0.25;entryDx=0;entryDy=0;exitX=1;exitY=0.25;exitDx=0;exitDy=0;" edge="1" parent="1" source="73" target="74">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="410" y="310" as="sourcePoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="84" value="Faulted" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="83">
|
||||
<mxGeometry x="-0.3833" y="4" relative="1" as="geometry">
|
||||
<mxPoint x="23" y="-5" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="73" value="Occupied" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="240" width="120" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="97" style="edgeStyle=none;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="74" target="71">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="558.2799999999997" y="279.9999999999999" as="sourcePoint"/>
|
||||
<mxPoint x="410" y="357.32000000000016" as="targetPoint"/>
|
||||
<Array as="points">
|
||||
<mxPoint x="550" y="320"/>
|
||||
<mxPoint x="410" y="320"/>
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="98" value="SessionFinished" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="97">
|
||||
<mxGeometry x="0.4476" y="1" relative="1" as="geometry">
|
||||
<mxPoint x="49" y="-1" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="99" style="edgeStyle=none;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" edge="1" parent="1" source="74" target="73">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="100" value="ErrorCleared" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="99">
|
||||
<mxGeometry x="0.3106" y="-4" relative="1" as="geometry">
|
||||
<mxPoint x="16" y="14" as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="74" value="FaultedOccupied" style="rounded=1;whiteSpace=wrap;html=1;arcSize=40;fontColor=#000000;fillColor=#ffffc0;strokeColor=#ff0000;" vertex="1" parent="1">
|
||||
<mxGeometry x="520" y="240" width="120" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,430 @@
|
||||
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="541px" height="946px" viewBox="-0.5 -0.5 541 946" content="<mxfile><diagram id="o8SZXadFU9JfIY1HxVTB" name="Page-1">7Vxbe6I6FP01Pk4/IKjwKF56odqZ0vvLfBGi0iJpAa8P/e0ngaACShkrrYfSh7ZsAoRk7bWzV0IqoDmenzrwddTFBrIqAmfMK6BVEYSaVCe/qWERGEBVDgxDxzQCE782aOYSMSPHrBPTQG6koIex5ZmvUaOObRvpXsQGHQfPosUG2Io+9RUOUcKg6dBKWu9NwxsFVqnKre1nyByOwifzHDszhmFhZnBH0MCzDRNoV0DTwdgL/hvPm8iibRe2S3BdZ8fZVcUcZHtbLrh1kXPVf6ZtInAW7JNu8QudG+T4Br8gO7iBNdFNQx9BxwtKn4dPHyrqTK29CZdns6Bk+GRhVflVpVxvETbYyBvTJ/EVoOApcgaW/859C+svxOT/1V6hbtpDVmo2Mj1ETfQGMwIiYnPwxDaQwUpAR2fAIM0OlAG2PXbM8yd1ZmliCzt+FQCAIi9CYndXDwInEj32HPLiW0v6Z65oeY8ilXZjaA37nfPvkWx21hPkZT003zCxJjpFeIw8Z0GKsLOSUA0uYT5RFYPD2RpgPGBFRhvgEhmSIMP0cHXndbeTf1ivhIcbQEgFRnPi0HeyaEUnLml8oUZaiOf8dyaO41DXc6DtQt0zsV0BnXQAwZGiSnBoDqv6SRRA4AsANMLj/sT9UrTEQNt3sTXxUGMF3iiU5RzxBUAEX7y4BWAhLW0CTM4VYOlw6RO4dN8Hj9Xn+0kULmJWuDwjz1uEjS3tQMYmHsCBOGEXXfGk3goySEBh9cWON8JDbEOrvbaSW5LWaNBgRYrY2KY2ZBuhJYQ+MXVMK3xZNDe9B78GVXb0SM+ccJzMjn8jxyTdh+h7cf71pCs3LqGHj/4hF1SUGuIX7YSjiyeOjjYiAmEH6AyRt+HjAkdfPhWym5DktkByZXSQBT1zGg3L24DqP4O0HVxsFHjFpu25CRyvKvJZ7oQMlvj17yZHkgrQkmjqIn9IYJPhgpPuBwKlzepTyxnPHqJ+UM2fNvchS/n/TJU8J4hRrqxVMwbj2vdx5Z2pqLBvNHo3vZcoRmr5ceUJ2AIH/ycsyR7EF4JVZUnc4NU1y+5k1V+ERzkQJdYVz+5FqyBJq9VC0moiuWFJ4xRaE1azR+SyBlrDmfg4uYGygWoHueYS9v0CtJl9j1Kg/jL0wRTi0UADOLHotdAyh2Qo29JJP9DOUSh1mCT9a7ATY9MwfDjFEZ7qCRGfSfZ1LTNNZe6eHZSzi1+u0ZhEidiIPpVznGdF/XXfOPPm3UWUc6QyLh0+LgmAi8Wl2vHHpQuCkduG0r4CXj2KEbkcw6dEGz6MHJ+LNqIkfyreVJPxRipkvNmF7oYPTCYlYoc8iMO6Pnk1KS4+kDv6L4p6ecfdvd3Mfkexz3NZwV/qHQfRO0SunuTKr9c72raRDpmRpah2tVG7bvbkGGT4L4BMoTXWRAAFnJwxgOYKipbp6tAhwOhcI3Y2qyJ/T+CyvF9OlT/wMgaXL5Dkf94QLC4NiEJWBH3jEKw/VtTzB+se/BXmMZBklt3LMdj+Y7DPZfz8FiWV53/UGKyDnZlPkBFe5Dwc6AGmAYORWZoTTIgTjFBj+f68MGNOkHkyoWTKveczs4faXIlSQ1Zga4YD+gqd2aQNLnDByUCjt/DQ1NMR5b4qKu5yz7Z70o0hqpTl80dUTeaPAVGbQLpGpNA0Q2Jovynq03tbmtafbmLQyazWl4nhQRLDurSFmL4+MQwCG6Q0NIaePvoQQu8EQud/LtqWdi7EIFQvIZRzFlCvHSOG2IwznJCBsENbYY+p5jNHUTnpqTdt19oxWJWafv5BTQZHEdTSMaJ5inpa5c5u+rP3GEZy1PTLueaPVvBwZPBwyMSTQSySeDIVtWCJZ2KyOSTvos4283JmpsprujmdY54Ixwxq7nAhLm6jHCNk1sFLjvmHVYKUcaSPOSayStAXu6TDk0wxZxiTJCMkSKaHi8Mx/zAa+h6OmU4U9f7SaS5uBtUYx+Soi/9ojmGritM5RgIRAZ2q8PXPkUx9C8kIP4RkxGKTDDhykukSknkThhezt0EsWcq+eLskmUMPZPiDz9Jto5ijHcewKvymhg1diwdRXUtmX6Gt/SG45SHpKVz3WtBEK9R3jpaf7gg/9Re9W2SosW9LhMw68o9cHJD8yOpf06fVh1n7s460hXWOdm3AdtaR5OiEjMzm9Tq7WEqQpbQLDsBS+3kSmJN0YjC6sG4uWzFPyiydl560lyf5QsTnXOl/5Um5rLJJR7fXaWq/+KveS9fTY+guRf8CCXJbPjDjxUL6QWIsCpKif6FS5WPX/E9Pm5rRfZ6pb9exte+g1PwL9A1ruI/PT5TjQFLzL1S+C75f9L+jK4IpvNgyYTiFphW03AdrqpTzlnbTuV12eliLEVC5P03lS5flVcWjWFKVjpdrgpelgTTtYhn7sAKUG9Tkm/F9foOacFQbiUJcIaPQfui+vGhptdMzgx/UY0sBQTlzcSwZ3wH8YMtoDBRzq6bkaCw5+1CkjA8c++TDH0IxsHvxoNR7pzGKyXHyoaSYLxeVtqlKP2TfIiAVPOMLN2Y6VpK5JSQjT2pXnBr/Tg+UyvU3LldPzIXmwDE/RFUSi61cg2NXrltqS9M8zVjU3mL7Gos5KteFII4jEAK27HEVLksvGHEQ03pz+eAO6x36Qfs/</diagram></mxfile>">
|
||||
<defs/>
|
||||
<g>
|
||||
<rect x="0" y="0" width="135" height="40" rx="20" ry="20" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 131px; height: 1px; padding-top: 20px; margin-left: 2px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 129px; max-height: 34px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Id Token
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="68" y="24" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Id Token
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 38 112.5 L 64.4 92.7 Q 68 90 71.6 92.7 L 124.4 132.3 Q 128 135 124.4 137.7 L 71.6 177.3 Q 68 180 64.4 177.3 L 11.6 137.7 Q 8 135 11.6 132.3 Z" fill="rgb(255, 255, 255)" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 116px; height: 1px; padding-top: 135px; margin-left: 10px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 114px; max-height: 84px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Currently used
|
||||
<br/>
|
||||
for transaction?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="68" y="139" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Currently used...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 67.5 40.36 L 67.5 59 Q 67.5 65 67.62 71 L 67.85 83.39" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 67.98 90.19 L 64.45 83.45 L 71.25 83.32 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="199" y="111" width="135" height="46" rx="4.5" ry="4.5" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 119px; height: 1px; padding-top: 134px; margin-left: 207px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 117px; max-height: 28px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Call stop_transaction at evse manager
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="267" y="138" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Call stop_transactio...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 126.08 135 L 157.5 135 Q 163.5 135 163.5 134.5 L 163.5 134.25 Q 163.5 134 169.5 134 L 190.9 134" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 197.7 134 L 190.9 137.4 L 190.9 130.6 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 136px; margin-left: 164px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Yes
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="164" y="140" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
Yes
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="405" y="112" width="135" height="46" rx="4.5" ry="4.5" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 119px; height: 1px; padding-top: 135px; margin-left: 413px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 117px; max-height: 28px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Remove transaction
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="473" y="139" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Remove transaction
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 334.4 134 L 363.5 134 Q 369.5 134 375.5 134.08 L 396.9 134.39" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 403.7 134.48 L 396.85 137.79 L 396.95 130.99 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 38 375.5 L 64.4 355.7 Q 68 353 71.6 355.7 L 124.4 395.3 Q 128 398 124.4 400.7 L 71.6 440.3 Q 68 443 64.4 440.3 L 11.6 400.7 Q 8 398 11.6 395.3 Z" fill="rgb(255, 255, 255)" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 116px; height: 1px; padding-top: 398px; margin-left: 10px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 114px; max-height: 84px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
All connectors occupied?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="68" y="402" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
All connectors occup...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="405" y="255" width="135" height="40" rx="20" ry="20" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 131px; height: 1px; padding-top: 275px; margin-left: 407px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 129px; max-height: 34px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
End
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="473" y="279" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
End
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="199" y="375" width="135" height="46" rx="4.5" ry="4.5" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 119px; height: 1px; padding-top: 398px; margin-left: 207px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 117px; max-height: 28px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Discard/Reject Id Token
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="267" y="402" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Discard/Reject Id To...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 334.4 398 L 363.5 398 Q 369.5 398 369.5 392 L 369.5 281 Q 369.5 275 375.5 275 L 396.9 275" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 403.7 275 L 396.9 278.4 L 396.9 271.6 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="0" y="255" width="135" height="46" rx="4.5" ry="4.5" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 119px; height: 1px; padding-top: 278px; margin-left: 8px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 117px; max-height: 28px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Forward Id Token to validators
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="68" y="282" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Forward Id Token to...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="0" y="637" width="135" height="46" rx="4.5" ry="4.5" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 119px; height: 1px; padding-top: 660px; margin-left: 8px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 117px; max-height: 28px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Select Connector using Selection logic
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="68" y="664" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Select Connector usi...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 38 757.5 L 64.4 737.7 Q 68 735 71.6 737.7 L 124.4 777.3 Q 128 780 124.4 782.7 L 71.6 822.3 Q 68 825 64.4 822.3 L 11.6 782.7 Q 8 780 11.6 777.3 Z" fill="rgb(255, 255, 255)" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 116px; height: 1px; padding-top: 780px; margin-left: 10px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 114px; max-height: 84px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Connector Reserved?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="68" y="784" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Connector Reserved?
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 281 757.5 L 307.4 737.7 Q 311 735 314.6 737.7 L 367.4 777.3 Q 371 780 367.4 782.7 L 314.6 822.3 Q 311 825 307.4 822.3 L 254.6 782.7 Q 251 780 254.6 777.3 Z" fill="rgb(255, 255, 255)" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 116px; height: 1px; padding-top: 780px; margin-left: 253px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 114px; max-height: 84px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Id Tag match?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="311" y="784" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Id Tag match?
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="0" y="877" width="135" height="46" rx="4.5" ry="4.5" fill="rgb(255, 255, 255)" stroke="#3a414a" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 119px; height: 1px; padding-top: 900px; margin-left: 8px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 117px; max-height: 28px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Call authorize at evse manager
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="68" y="904" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Call authorize at ev...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 126.08 780 L 245.23 780" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 252.03 780 L 245.23 783.4 L 245.23 776.6 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 781px; margin-left: 190px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Yes
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="190" y="784" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
Yes
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 68 823.92 L 68 845 Q 68 851 67.88 857 L 67.65 868.94" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 67.52 875.74 L 64.25 868.87 L 71.05 869.01 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 852px; margin-left: 69px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
No
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="69" y="855" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
No
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 311 736.08 L 311 584 Q 311 578 311.02 572 L 311.43 429.02" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 311.45 422.22 L 314.83 429.03 L 308.03 429.01 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 579px; margin-left: 311px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
No
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="311" y="582" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
No
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 311 823.92 L 311 894 Q 311 900 305 900 L 143.1 900" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 136.3 900 L 143.1 896.6 L 143.1 903.4 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 901px; margin-left: 262px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Yes
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="262" y="904" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
Yes
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 67.5 923.37 L 67.5 931 Q 67.5 937 73.5 937 L 466.5 937 Q 472.5 937 472.5 931 L 472.5 303.05" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 472.5 296.25 L 475.9 303.05 L 469.1 303.05 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 472.5 158.37 L 472.5 246.95" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 472.5 253.75 L 469.1 246.95 L 475.9 246.95 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 68 178.92 L 68 211.5 Q 68 217.5 67.92 223.5 L 67.6 246.94" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 67.51 253.74 L 64.2 246.89 L 71 246.98 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 217px; margin-left: 69px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
No
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="69" y="221" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
No
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 126.08 398 L 190.9 398" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 197.7 398 L 190.9 401.4 L 190.9 394.6 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 399px; margin-left: 163px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Yes
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="163" y="402" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
Yes
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 38 517.5 L 64.4 497.7 Q 68 495 71.6 497.7 L 124.4 537.3 Q 128 540 124.4 542.7 L 71.6 582.3 Q 68 585 64.4 582.3 L 11.6 542.7 Q 8 540 11.6 537.3 Z" fill="rgb(255, 255, 255)" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 116px; height: 1px; padding-top: 540px; margin-left: 10px;">
|
||||
<div data-drawio-colors="color: #3a414a; " style="box-sizing: border-box; font-size: 0px; text-align: center; width: 114px; max-height: 84px;">
|
||||
<div style="display: inline-block; font-size: 11.7px; font-family: Helvetica; color: rgb(58, 65, 74); line-height: 1.2; pointer-events: all; width: 100%; white-space: normal; overflow-wrap: normal;">
|
||||
Valid token available?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="68" y="544" fill="#3a414a" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Valid token availabl...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 67.5 301.37 L 67.5 321 Q 67.5 327 67.61 333 L 67.86 346.39" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 67.98 353.19 L 64.46 346.45 L 71.26 346.32 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 68 441.92 L 68 488.39" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 68 495.19 L 64.6 488.39 L 71.4 488.39 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 469px; margin-left: 69px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
No
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="69" y="473" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
No
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 68 583.92 L 68 605 Q 68 611 67.88 617 L 67.65 628.94" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 67.52 635.74 L 64.25 628.87 L 71.05 629.01 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 612px; margin-left: 69px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Yes
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="69" y="615" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
Yes
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 126.08 540 L 260.5 540 Q 266.5 540 266.5 534 L 266.5 429.06" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 266.5 422.26 L 269.9 429.06 L 263.1 429.06 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 541px; margin-left: 257px;">
|
||||
<div data-drawio-colors="color: #333333; background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 13.3px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; font-weight: bold; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
No
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="257" y="544" fill="#333333" font-family="Helvetica" font-size="13px" text-anchor="middle" font-weight="bold">
|
||||
No
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 67.5 683.37 L 67.5 703 Q 67.5 709 67.61 715 L 67.86 728.39" fill="none" stroke="#3a414a" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 67.98 735.19 L 64.46 728.45 L 71.26 728.32 Z" fill="#3a414a" stroke="#3a414a" stroke-miterlimit="10" pointer-events="all"/>
|
||||
</g>
|
||||
<switch>
|
||||
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
|
||||
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
|
||||
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
|
||||
Viewer does not support full SVG 1.1
|
||||
</text>
|
||||
</a>
|
||||
</switch>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 39 KiB |
349
tools/EVerest-main/modules/EVSE/Auth/include/AuthHandler.hpp
Normal file
349
tools/EVerest-main/modules/EVSE/Auth/include/AuthHandler.hpp
Normal file
@@ -0,0 +1,349 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef _AUTH_HANDLER_HPP_
|
||||
#define _AUTH_HANDLER_HPP_
|
||||
|
||||
#include <condition_variable>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
|
||||
#include <utils/types.hpp>
|
||||
|
||||
#include <generated/types/authorization.hpp>
|
||||
#include <generated/types/evse_manager.hpp>
|
||||
#include <generated/types/reservation.hpp>
|
||||
#include <generated/types/text_message.hpp>
|
||||
|
||||
#include <Connector.hpp>
|
||||
#include <ReservationHandler.hpp>
|
||||
|
||||
using namespace types::evse_manager;
|
||||
using namespace types::authorization;
|
||||
using namespace types::reservation;
|
||||
using namespace types::text_message;
|
||||
|
||||
namespace types {
|
||||
namespace authorization {
|
||||
|
||||
inline bool operator<(const IdToken& lhs, const IdToken& rhs) {
|
||||
return lhs.value < rhs.value;
|
||||
}
|
||||
|
||||
inline bool operator<(const ProvidedIdToken& lhs, const ProvidedIdToken& rhs) {
|
||||
return lhs.id_token < rhs.id_token;
|
||||
}
|
||||
|
||||
} // namespace authorization
|
||||
} // namespace types
|
||||
|
||||
namespace module {
|
||||
|
||||
enum class TokenHandlingResult {
|
||||
ALREADY_IN_PROCESS,
|
||||
REJECTED,
|
||||
USED_TO_START_TRANSACTION,
|
||||
USED_TO_STOP_TRANSACTION,
|
||||
TIMEOUT,
|
||||
NO_CONNECTOR_AVAILABLE,
|
||||
WITHDRAWN
|
||||
};
|
||||
|
||||
namespace conversions {
|
||||
std::string token_handling_result_to_string(const TokenHandlingResult& result);
|
||||
} // namespace conversions
|
||||
|
||||
/**
|
||||
* @brief This class handles authorization and reservation requests. It keeps track of the state of each connector and
|
||||
* validates incoming token and reservation requests accordingly.
|
||||
*
|
||||
*/
|
||||
class AuthHandler {
|
||||
|
||||
public:
|
||||
AuthHandler(const SelectionAlgorithm& selection_algorithm, const int connection_timeout,
|
||||
bool plug_in_timeout_enabled, bool prioritize_authorization_over_stopping_transaction,
|
||||
bool ignore_connector_faults, const std::string& id, kvsIntf* store);
|
||||
virtual ~AuthHandler();
|
||||
|
||||
/**
|
||||
* @brief Initializes the evse with the given \p connectors and the given \p evse_id . It instantiates new
|
||||
* connector objects and fills data sturctures of the class.
|
||||
*
|
||||
* @param evse_id
|
||||
* @param evse_index
|
||||
* @param connectors The connectors.
|
||||
*/
|
||||
void init_evse(const int evse_id, const int evse_index, const std::vector<Connector>& connectors);
|
||||
|
||||
/**
|
||||
* @brief Returns the evse_id for the given \p evse_index .
|
||||
*
|
||||
* @param evse_index
|
||||
* @return int32_t evse_id
|
||||
*/
|
||||
int32_t get_evse_id_by_index(const int evse_index);
|
||||
|
||||
/**
|
||||
* @brief Call when everything is initialized. This will call 'init' of the reservation handler.
|
||||
*/
|
||||
void initialize();
|
||||
|
||||
/**
|
||||
* @brief Handler for a new incoming \p provided_token
|
||||
*
|
||||
* @param provided_token
|
||||
*/
|
||||
TokenHandlingResult on_token(const ProvidedIdToken& provided_token);
|
||||
|
||||
/**
|
||||
* @brief Handler for an update to a token validation result. This is mainly used to update if we have a parent id.
|
||||
*
|
||||
* @param validation_result_update
|
||||
*/
|
||||
void handle_token_validation_result_update(const ValidationResultUpdate& validation_result_update);
|
||||
|
||||
/**
|
||||
* @brief Handler for new incoming \p reservation for the given \p connector . Places the reservation if possible.
|
||||
*
|
||||
* @param reservation
|
||||
* @return types::reservation::ReservationResult
|
||||
*/
|
||||
types::reservation::ReservationResult handle_reservation(const Reservation& reservation);
|
||||
|
||||
/**
|
||||
* @brief Handler for incoming cancel reservation request for the given \p reservation_id .
|
||||
*
|
||||
* @param reservation_id
|
||||
* @return return value first returns false if the reservation could not been cancelled. Return value second is the
|
||||
* evse id or nullopt if the reservation was a 'global' reservation without evse id.
|
||||
*/
|
||||
std::pair<bool, std::optional<int32_t>> handle_cancel_reservation(const int32_t reservation_id);
|
||||
|
||||
/**
|
||||
* @brief Callback to check if there is a reservation for the given token (on the given evse id).
|
||||
* @param id_token The token to check.
|
||||
* @param evse_id The evse to check the reservation for.
|
||||
* @param group_id_token The group id token to check.
|
||||
* @return The reservation check status
|
||||
*/
|
||||
ReservationCheckStatus handle_reservation_exists(std::string& id_token, const std::optional<int>& evse_id,
|
||||
std::optional<std::string>& group_id_token);
|
||||
|
||||
/**
|
||||
* @brief Callback to signal EvseManager that the given \p connector_id has been reserved with the given \p
|
||||
* reservation_id .
|
||||
*
|
||||
* @param evse_id
|
||||
* @param reservation_id
|
||||
*
|
||||
* @return true of EvseManager accepted the reservation.
|
||||
*/
|
||||
bool call_reserved(const int reservation_id, const std::optional<int>& evse_id);
|
||||
|
||||
/**
|
||||
* @brief Callback to signal EvseManager that the reservation for the given \p evse_id has been cancelled.
|
||||
*
|
||||
* @param reservation_id The id of the cancelled reservation.
|
||||
* @param reason The reason the reservation was cancelled.
|
||||
* @param evse_id Evse id if reservation was for a specific evse.
|
||||
* @param send_reservation_update True to send a reservation update. This should not be sent if OCPP cancels
|
||||
* the reservation.
|
||||
*/
|
||||
void call_reservation_cancelled(const int32_t reservation_id, const ReservationEndReason reason,
|
||||
const std::optional<int>& evse_id, const bool send_reservation_update);
|
||||
|
||||
/**
|
||||
* @brief Handler for the given \p events at the given \p connector . Submits events to the state machine of the
|
||||
* handler.
|
||||
*
|
||||
* @param evse_id
|
||||
* @param events
|
||||
*/
|
||||
void handle_session_event(const int evse_id, const SessionEvent& events);
|
||||
|
||||
/**
|
||||
* @brief Handler for permanent faults from evsemanager that prevents charging
|
||||
*/
|
||||
void handle_permanent_fault_cleared(const int evse_id, const int32_t connector_id);
|
||||
void handle_permanent_fault_raised(const int evse_id, const int32_t connector_id);
|
||||
|
||||
/**
|
||||
* @brief Set the connection timeout of the handler.
|
||||
*
|
||||
* @param connection_timeout
|
||||
*/
|
||||
void set_connection_timeout(const int connection_timeout);
|
||||
|
||||
/**
|
||||
* @brief Set the plug in timeout enabled flag of the handler.
|
||||
*
|
||||
* @param plug_in_timeout_enabled
|
||||
*/
|
||||
void set_plug_in_timeout_enabled(bool plug_in_timeout_enabled);
|
||||
|
||||
/**
|
||||
* @brief Set the master pass group id of the handler.
|
||||
*
|
||||
* @param master_pass_group_id
|
||||
*/
|
||||
void set_master_pass_group_id(const std::string& master_pass_group_id);
|
||||
|
||||
/**
|
||||
* @brief Set the prioritize authorization over stopping transaction flag of the handler.
|
||||
*
|
||||
* @param b
|
||||
*/
|
||||
void set_prioritize_authorization_over_stopping_transaction(bool b);
|
||||
|
||||
/**
|
||||
* @brief Registers the given \p callback to notify the evse about the processed authorization request.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
void
|
||||
register_notify_evse_callback(const std::function<void(const int evse_index, const ProvidedIdToken& provided_token,
|
||||
const ValidationResult& validation_result)>& callback);
|
||||
/**
|
||||
* @brief Registers the given \p callback to withdraw authorization.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
void register_withdraw_authorization_callback(const std::function<void(const int evse_index)>& callback);
|
||||
|
||||
/**
|
||||
* @brief Registers the given \p callback to validate a token.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
void register_validate_token_callback(
|
||||
const std::function<std::vector<ValidationResult>(const ProvidedIdToken& provided_token)>& callback);
|
||||
|
||||
/**
|
||||
* @brief Registers the given \p callback to stop a transaction at an EvseManager.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
void register_stop_transaction_callback(
|
||||
const std::function<void(const int evse_index, const StopTransactionRequest& request)>& callback);
|
||||
|
||||
/**
|
||||
* @brief Registers the given \p callback to signal a reservation to an EvseManager.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
void register_reserved_callback(
|
||||
const std::function<bool(const std::optional<int>& evse_id, const int& reservation_id)>& callback);
|
||||
|
||||
/**
|
||||
* @brief Registers the given \p callback to signal a reservation has been cancelled to the EvseManager.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
void register_reservation_cancelled_callback(
|
||||
const std::function<void(const std::optional<int32_t>& evse_id, const int32_t reservation_id,
|
||||
const ReservationEndReason reason, const bool send_reservation_update)>& callback);
|
||||
|
||||
/**
|
||||
* @brief Registers the given \p callback to publish the intermediate token validation status.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
void register_publish_token_validation_status_callback(
|
||||
const std::function<void(const ProvidedIdToken&, TokenValidationStatus, const std::vector<MessageContent>&)>&
|
||||
callback);
|
||||
|
||||
WithdrawAuthorizationResult handle_withdraw_authorization(const WithdrawAuthorizationRequest& request);
|
||||
|
||||
private:
|
||||
enum class SelectEvseReturnStatus {
|
||||
EvseSelected,
|
||||
Interrupted,
|
||||
TimeOut
|
||||
};
|
||||
|
||||
struct SelectEvseResult {
|
||||
std::optional<int> evse_id;
|
||||
SelectEvseReturnStatus status;
|
||||
};
|
||||
|
||||
SelectionAlgorithm selection_algorithm;
|
||||
int connection_timeout;
|
||||
bool plug_in_timeout_enabled;
|
||||
std::optional<std::string> master_pass_group_id;
|
||||
bool prioritize_authorization_over_stopping_transaction;
|
||||
bool ignore_faults;
|
||||
ReservationHandler reservation_handler;
|
||||
|
||||
std::map<int, std::unique_ptr<EVSEContext>> evses;
|
||||
|
||||
std::list<int> plug_in_queue;
|
||||
std::set<ProvidedIdToken> tokens_in_process;
|
||||
std::condition_variable cv;
|
||||
std::condition_variable processing_finished_cv;
|
||||
std::mutex event_mutex;
|
||||
std::mutex withdraw_mutex;
|
||||
std::unique_ptr<WithdrawAuthorizationRequest> withdraw_request;
|
||||
|
||||
// callbacks
|
||||
std::function<void(const int evse_index, const ProvidedIdToken& provided_token,
|
||||
const ValidationResult& validation_result)>
|
||||
notify_evse_callback;
|
||||
std::function<void(const int evse_index)> withdraw_authorization_callback;
|
||||
std::function<std::vector<ValidationResult>(const ProvidedIdToken& provided_token)> validate_token_callback;
|
||||
std::function<void(const int evse_index, const StopTransactionRequest& request)> stop_transaction_callback;
|
||||
std::function<void(const Array& reservations)> reservation_update_callback;
|
||||
std::function<bool(const std::optional<int>& evse_index, const int& reservation_id)> reserved_callback;
|
||||
std::function<void(const std::optional<int>& evse_index, const int32_t reservation_id,
|
||||
const types::reservation::ReservationEndReason reason, const bool send_reservation_update)>
|
||||
reservation_cancelled_callback;
|
||||
std::function<void(const ProvidedIdToken& token, TokenValidationStatus status,
|
||||
const std::vector<MessageContent>& tariff_messages)>
|
||||
publish_token_validation_status_callback;
|
||||
|
||||
void publish_token_validation_status(const ProvidedIdToken& token, TokenValidationStatus status,
|
||||
const std::vector<MessageContent>& tariff_messages = {});
|
||||
|
||||
std::vector<int> get_referenced_evses(const ProvidedIdToken& provided_token);
|
||||
int used_for_transaction(const std::vector<int>& evse_ids, const std::string& id_token);
|
||||
bool is_token_already_in_process(const ProvidedIdToken& provided_id_token,
|
||||
const std::vector<int>& referenced_evses);
|
||||
bool any_evse_available(const std::vector<int>& evse_ids);
|
||||
bool any_parent_id_present(const std::vector<int>& evse_ids);
|
||||
bool equals_master_pass_group_id(const std::optional<types::authorization::IdToken> parent_id_token);
|
||||
|
||||
TokenHandlingResult handle_token(ProvidedIdToken& provided_token, std::unique_lock<std::mutex>& lk);
|
||||
|
||||
/**
|
||||
* @brief Method selects an evse based on the configured selection algorithm. It might block until an event
|
||||
* occurs that can be used to determine an evse.
|
||||
*
|
||||
* @param selected_evses
|
||||
* @param id_token The id token of the request.
|
||||
* @return The status and optional evse id if an evse was selected.
|
||||
*/
|
||||
SelectEvseResult select_evse(const std::vector<int>& selected_evses, const IdToken& id_token,
|
||||
std::unique_lock<std::mutex>& lk);
|
||||
bool is_authorization_withdrawn(const std::vector<int>& selected_evses, const IdToken& id_token);
|
||||
|
||||
int get_latest_plugin(const std::vector<int>& evse_ids);
|
||||
void notify_evse(int evse_id, const ProvidedIdToken& provided_token, const ValidationResult& validation_result,
|
||||
std::unique_lock<std::mutex>& lk);
|
||||
Identifier get_identifier(const ValidationResult& validation_result, const std::string& id_token,
|
||||
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
|
||||
|
||||
#endif //_AUTH_HANDLER_HPP_
|
||||
100
tools/EVerest-main/modules/EVSE/Auth/include/Connector.hpp
Normal file
100
tools/EVerest-main/modules/EVSE/Auth/include/Connector.hpp
Normal file
@@ -0,0 +1,100 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef _CONNECTOR_HPP_
|
||||
#define _CONNECTOR_HPP_
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <everest/timer.hpp>
|
||||
|
||||
#include <utils/types.hpp>
|
||||
|
||||
#include <ConnectorStateMachine.hpp>
|
||||
#include <generated/types/authorization.hpp>
|
||||
#include <generated/types/evse_manager.hpp>
|
||||
|
||||
namespace module {
|
||||
|
||||
/// \brief Validated Identifier struct. Used to keep track of active Identifiers
|
||||
struct Identifier {
|
||||
types::authorization::IdToken id_token; ///< IdToken of the identifier
|
||||
types::authorization::AuthorizationType type; ///< Type of the provider of the identifier
|
||||
std::optional<types::authorization::AuthorizationStatus> authorization_status;
|
||||
std::optional<std::string> expiry_time; ///< Absolute UTC time point when reservation expires in RFC3339 format
|
||||
std::optional<types::authorization::IdToken> parent_id_token; ///< Parent id token of the identifier
|
||||
};
|
||||
|
||||
struct Connector {
|
||||
explicit Connector(
|
||||
int id, const types::evse_manager::ConnectorTypeEnum type = types::evse_manager::ConnectorTypeEnum::Unknown) :
|
||||
id(id), transaction_active(false), state_machine(ConnectorState::AVAILABLE), type(type) {
|
||||
}
|
||||
|
||||
int id;
|
||||
|
||||
bool transaction_active;
|
||||
ConnectorStateMachine state_machine;
|
||||
types::evse_manager::ConnectorTypeEnum type;
|
||||
|
||||
/**
|
||||
* @brief Submits the given \p event to the state machine
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
void submit_event(ConnectorEvent event);
|
||||
|
||||
/**
|
||||
* @brief Returns true if connector is in state UNAVAILABLE or UNAVAILABLE_FAULTED
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool is_unavailable() const;
|
||||
|
||||
ConnectorState get_state() const;
|
||||
};
|
||||
|
||||
struct EVSEContext {
|
||||
|
||||
EVSEContext(
|
||||
int evse_id, int evse_index, int connector_id,
|
||||
const types::evse_manager::ConnectorTypeEnum connector_type = types::evse_manager::ConnectorTypeEnum::Unknown) :
|
||||
evse_id(evse_id), evse_index(evse_index), transaction_active(false), plugged_in(false) {
|
||||
Connector c(connector_id, connector_type);
|
||||
connectors.push_back(c);
|
||||
}
|
||||
|
||||
EVSEContext(int evse_id, int evse_index, const std::vector<Connector>& connectors) :
|
||||
evse_id(evse_id),
|
||||
evse_index(evse_index),
|
||||
transaction_active(false),
|
||||
connectors(connectors),
|
||||
plugged_in(false),
|
||||
plug_in_timeout(false) {
|
||||
}
|
||||
|
||||
int32_t evse_id;
|
||||
int32_t evse_index;
|
||||
bool transaction_active;
|
||||
|
||||
// identifier is set when transaction is running and none if not
|
||||
std::optional<Identifier> identifier = std::nullopt;
|
||||
std::vector<Connector> connectors;
|
||||
Everest::SteadyTimer timeout_timer;
|
||||
std::atomic<bool> timeout_in_progress{false};
|
||||
bool plugged_in;
|
||||
bool plug_in_timeout; // indicates no authorization received within connection_timeout. Replug is required for this
|
||||
// EVSE to get authorization and start a transaction
|
||||
|
||||
bool is_available();
|
||||
bool is_unavailable();
|
||||
};
|
||||
|
||||
namespace conversions {
|
||||
std::string connector_state_to_string(const ConnectorState& state);
|
||||
} // namespace conversions
|
||||
|
||||
} // namespace module
|
||||
|
||||
#endif //_CONNECTOR_HPP_
|
||||
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef _CONNECTOR_STATE_MACHINE_HPP_
|
||||
#define _CONNECTOR_STATE_MACHINE_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace module {
|
||||
|
||||
enum class ConnectorEvent {
|
||||
ENABLE,
|
||||
DISABLE,
|
||||
ERROR_CLEARED,
|
||||
FAULTED,
|
||||
TRANSACTION_STARTED,
|
||||
SESSION_FINISHED
|
||||
};
|
||||
|
||||
/// @warning Do not change the order of ConnectorState, or if you do it, fix the code in ReservationHandler.
|
||||
enum class ConnectorState {
|
||||
AVAILABLE,
|
||||
UNAVAILABLE,
|
||||
FAULTED,
|
||||
OCCUPIED,
|
||||
UNAVAILABLE_FAULTED,
|
||||
FAULTED_OCCUPIED
|
||||
};
|
||||
|
||||
class ConnectorStateMachine {
|
||||
public:
|
||||
explicit ConnectorStateMachine(ConnectorState initial_state);
|
||||
|
||||
bool handle_event(ConnectorEvent event);
|
||||
|
||||
ConnectorState get_state() const;
|
||||
|
||||
private:
|
||||
ConnectorState state;
|
||||
};
|
||||
|
||||
} // namespace module
|
||||
|
||||
#endif //_CONNECTOR_STATE_MACHINE_HPP_
|
||||
@@ -0,0 +1,357 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <Connector.hpp>
|
||||
#include <everest/timer.hpp>
|
||||
#include <generated/types/evse_manager.hpp>
|
||||
#include <generated/types/reservation.hpp>
|
||||
|
||||
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.
|
||||
std::map<int, std::unique_ptr<module::EVSEContext>>& evses;
|
||||
/// \brief Key value store id.
|
||||
const std::string kvs_store_key_id;
|
||||
/// \brief Key value store for storing reservations.
|
||||
kvsIntf* store;
|
||||
/// \brief Map of EVSE specific reservations, with EVSE id as key and the Reservation type as value.
|
||||
std::map<uint32_t, types::reservation::Reservation> evse_reservations;
|
||||
/// \brief All reservations not bound to a specific EVSE.
|
||||
std::vector<types::reservation::Reservation> global_reservations;
|
||||
/// \brief event mutex, for all timer bound locks (for `reservation_id_to_reservation_timeout_timer_map`)
|
||||
mutable std::recursive_mutex event_mutex;
|
||||
/// \brief Map with reservations and their timer.
|
||||
///
|
||||
/// Every reservation has a specific end time, which is stored in this map. Key is the reservation id. When the
|
||||
/// timer expires, it is removed from the map and the reservation is removed from the `evse_reservations` or
|
||||
/// `global_reservations`.
|
||||
std::map<int, std::unique_ptr<Everest::SteadyTimer>> reservation_id_to_reservation_timeout_timer_map;
|
||||
|
||||
/// \brief The callback that is called when a reservation is cancelled.
|
||||
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)>
|
||||
reservation_cancelled_callback;
|
||||
|
||||
std::set<int32_t> last_reserved_status;
|
||||
|
||||
/// \brief worker for the timers.
|
||||
boost::shared_ptr<boost::asio::executor_work_guard<boost::asio::io_context::executor_type>> work;
|
||||
/// \brief io_context for the worker for the timers.
|
||||
boost::asio::io_context io_context;
|
||||
/// \brief io context thread for the timers.
|
||||
std::thread io_context_thread;
|
||||
|
||||
public:
|
||||
///
|
||||
/// \brief Constructor.
|
||||
///
|
||||
ReservationHandler(std::map<int, std::unique_ptr<module::EVSEContext>>& evses, const std::string& id,
|
||||
kvsIntf* store);
|
||||
|
||||
///
|
||||
/// \brief Destructor.
|
||||
///
|
||||
~ReservationHandler();
|
||||
|
||||
///
|
||||
/// \brief Load reservations from key value store.
|
||||
///
|
||||
void load_reservations();
|
||||
|
||||
///
|
||||
/// \brief Try to make a reservation.
|
||||
/// \param evse_id Optional, the evse id. If omitted, a 'global' reservation will be made.
|
||||
/// \param reservation The reservation to make.
|
||||
/// \return The result of the reservation (`Accepted` if the reservation could be made).
|
||||
///
|
||||
types::reservation::ReservationResult make_reservation(const std::optional<uint32_t> evse_id,
|
||||
const types::reservation::Reservation& reservation);
|
||||
|
||||
///
|
||||
/// \brief Change a specific connector state.
|
||||
///
|
||||
/// This is important for the reservation handler, to know which connector is in which state, to know if a
|
||||
/// reservation can be made or not.
|
||||
///
|
||||
/// \param connector_state The state of the connector.
|
||||
/// \param evse_id The EVSE id the connector belongs to.
|
||||
/// \param connector_id The connector id.
|
||||
///
|
||||
void on_connector_state_changed(const ConnectorState connector_state, const uint32_t evse_id,
|
||||
const uint32_t connector_id);
|
||||
|
||||
///
|
||||
/// \brief Check if charging is possible on a given EVSE.
|
||||
///
|
||||
/// If there are multiple global reservations, while a charging station might look available, it is possible that
|
||||
/// charging is not possible because then cars that made reservations can not charge anymore.
|
||||
///
|
||||
/// Only use this function to check if a car can charge without having a reservation id.
|
||||
///
|
||||
/// \param evse_id The evse on which a car wants to charge.
|
||||
/// \return True if charging is possible.
|
||||
///
|
||||
bool is_charging_possible(const uint32_t evse_id);
|
||||
|
||||
///
|
||||
/// \brief Check is an EVSE is reserved.
|
||||
///
|
||||
/// This only looks at EVSE specific reservations.
|
||||
///
|
||||
/// \param evse_id The evse id to check.
|
||||
/// \return True if EVSE is reserved.
|
||||
///
|
||||
bool is_evse_reserved(const uint32_t evse_id);
|
||||
|
||||
///
|
||||
/// \brief Cancel a reservation.
|
||||
/// \param reservation_id The id of the reservation to cancel.
|
||||
/// \param execute_callback True if the `reservation_cancelled_callback` must be called.
|
||||
/// \param reason The cancel reason.
|
||||
/// \return First: true if reservation could be cancelled.
|
||||
/// Second: The evse id if the reservation to cancel was made for a specific EVSE.
|
||||
///
|
||||
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.
|
||||
///
|
||||
void 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);
|
||||
|
||||
///
|
||||
/// \brief Called when a reservation is used, will remove it from the reservation list.
|
||||
/// \param reservation_id The if of the reservation that is used.
|
||||
///
|
||||
/// \note This will not set the EVSE or Connector to 'available'. That must be done separately (because we don't
|
||||
/// know here when the connector is not connected anymore).
|
||||
///
|
||||
void on_reservation_used(const int32_t reservation_id);
|
||||
|
||||
///
|
||||
/// @brief Function checks if the given \p id_token or \p parent_id_token matches the reserved token of the given \p
|
||||
/// evse_id
|
||||
///
|
||||
/// @param id_token Id token
|
||||
/// @param evse_id Evse id
|
||||
/// @param parent_id_token Parent id token
|
||||
/// @return The reservation id when there is a matching identifier, otherwise std::nullopt.
|
||||
///
|
||||
std::optional<int32_t> matches_reserved_identifier(const std::string& id_token,
|
||||
const std::optional<uint32_t> evse_id,
|
||||
std::optional<std::string> parent_id_token);
|
||||
|
||||
///
|
||||
/// @brief Functions check if reservation at the given \p evse_id contains a parent_id
|
||||
/// @param evse_id Evse id
|
||||
/// @return true if reservation for \p evse_id exists and reservation contains a parent_id
|
||||
///
|
||||
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.
|
||||
/// \param evse_connectors The vector to check for the type.
|
||||
/// \param connector_type The connector type to find.
|
||||
/// \return True if the connector type is in the vector.
|
||||
///
|
||||
bool has_evse_connector_type(const std::vector<Connector>& evse_connectors,
|
||||
const types::evse_manager::ConnectorTypeEnum connector_type) const;
|
||||
|
||||
///
|
||||
/// \brief Check if there is at least one EVSE with the given connector type.
|
||||
/// \param connector_type The connector type to check.
|
||||
/// \return True if at least one EVSE has this connector type.
|
||||
///
|
||||
bool does_evse_connector_type_exist(const types::evse_manager::ConnectorTypeEnum connector_type) const;
|
||||
|
||||
///
|
||||
/// \brief Helper function to get a reservation result from the current EVSE state and connector state, and if
|
||||
/// there is a specific reservation for this EVSE.
|
||||
/// \param evse_id The evse id to get the state from.
|
||||
/// \param evse_specific_reservations The evse specific reservations list to look in.
|
||||
/// \return The `ReservationResult` to return for this specific EVSE.
|
||||
///
|
||||
types::reservation::ReservationResult get_evse_connector_state_reservation_result(
|
||||
const uint32_t evse_id, const std::map<uint32_t, types::reservation::Reservation>& evse_specific_reservations);
|
||||
|
||||
///
|
||||
/// \brief Helper function to check if the connector of a specific EVSE is available.
|
||||
/// \param evse_id The evse id the connector belongs to.
|
||||
/// \param connector_type The connector type to check.
|
||||
/// \return The `ReservationResult` to return for his specific connector.
|
||||
///
|
||||
types::reservation::ReservationResult
|
||||
get_connector_availability_reservation_result(const uint32_t evse_id,
|
||||
const types::evse_manager::ConnectorTypeEnum connector_type);
|
||||
|
||||
///
|
||||
/// \brief Get all possible orders of connector types given a vector of connector types.
|
||||
///
|
||||
/// For the reservations, there must be checked if all combinations of arriving cars with specific connector types
|
||||
/// are possible. For that, we want to have a list of all different combinations of arriving.
|
||||
///
|
||||
/// So for example for connector type A, B and C, the different combinations are:
|
||||
/// - A, B, C
|
||||
/// - A, C, B
|
||||
/// - B, A, C
|
||||
/// - B, C, A
|
||||
/// - C, A, B
|
||||
/// - C, B, A
|
||||
///
|
||||
/// And for connector types A, A and B, the combinations are:
|
||||
/// - A, A, B
|
||||
/// - A, B, A
|
||||
/// - B, A, A
|
||||
///
|
||||
/// \param connectors The connector types to get all orders from.
|
||||
/// \return A vector of all orders of the connector types.
|
||||
///
|
||||
std::vector<std::vector<types::evse_manager::ConnectorTypeEnum>>
|
||||
get_all_possible_orders(const std::vector<types::evse_manager::ConnectorTypeEnum>& connectors) const;
|
||||
|
||||
///
|
||||
/// \brief Helper function: For a specific order of arrival of cars, check if there is still an EVSE available for
|
||||
/// each car.
|
||||
///
|
||||
/// This function is called recursively, until no 'virtual cars' are left.
|
||||
///
|
||||
/// \param used_evse_ids The evse id's we have used in previous checks. This will be empty when the
|
||||
/// function is first called and will be filled every time an evse id is
|
||||
/// 'used'.
|
||||
/// \param next_car_arrival_order The order in which the cars arrive. This is for example 'A, B, C' and as
|
||||
/// soon as the first is handled, it is removed from the list before
|
||||
/// recursively calling the function again.
|
||||
/// \param evse_specific_reservations EVSE specific reservations, to see if an EVSE is already reserved.
|
||||
/// \return True if this combination of car arrival orders is possible.
|
||||
///
|
||||
bool 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);
|
||||
|
||||
///
|
||||
/// \brief Check if it is possible to make a new reservation.
|
||||
/// \param global_reservation_type If it is a global reservation: the reservation type.
|
||||
/// \param reservations_no_evse The list of global reservations.
|
||||
/// \param evse_specific_reservations The list of evse specific reservations.
|
||||
/// \return True if a reservation is possible, otherwise false.
|
||||
///
|
||||
bool 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);
|
||||
|
||||
///
|
||||
/// \brief If a reservation is made, add the reservation to the `reservation_id_to_reservation_timeout_timer_map`.
|
||||
/// \param reservation The reservation.
|
||||
/// \param evse_id The evse id.
|
||||
///
|
||||
void set_reservation_timer(const types::reservation::Reservation& reservation,
|
||||
const std::optional<uint32_t> evse_id);
|
||||
|
||||
///
|
||||
/// \brief Get all evses that have a specific connector type.
|
||||
/// \param connector_type The connector type.
|
||||
/// \return Vector with evse's.
|
||||
///
|
||||
std::vector<EVSEContext*>
|
||||
get_all_evses_with_connector_type(const types::evse_manager::ConnectorTypeEnum connector_type) const;
|
||||
|
||||
///
|
||||
/// \brief For H01.FR.11, H01.FR.12 and H01.FR.13, the correct state must be returned.
|
||||
///
|
||||
/// Also see @see module::ReservationHandler::get_reservation_evse_connector_state. This is a helper function to
|
||||
/// return the 'more important' state (Occupied is 'more important' than Unavailable).
|
||||
///
|
||||
/// \param currrent_state The current connector state.
|
||||
/// \param new_state The new state.
|
||||
/// \return The connector state.
|
||||
///
|
||||
ConnectorState get_new_connector_state(ConnectorState currrent_state, const ConnectorState new_state) const;
|
||||
|
||||
///
|
||||
/// \brief For H01.FR.11, H01.FR.12 and H01.FR.13, the correct state must be returned: if (all) evses are Occupied
|
||||
/// or reserved, occupied must be returned, if (all) evses are Faulted, faulted must be returned, if (all)
|
||||
/// evses are unavailable, unavailable must be returned. This function helps returning the correct state.
|
||||
///
|
||||
/// If at least one of the EVSE's is Occupied, it will return occupied, then it will look to faulted and then to
|
||||
/// unavailable. So if one is occupied and one faulted, it will still return occupied.
|
||||
///
|
||||
/// \param connector_type The connector type to check.
|
||||
/// \return The reservation result that can be returned on the reserve now request.
|
||||
///
|
||||
types::reservation::ReservationResult
|
||||
get_reservation_evse_connector_state(const types::evse_manager::ConnectorTypeEnum connector_type) const;
|
||||
|
||||
///
|
||||
/// \brief After a connector or evse is set to unavailable, faulted or occupied, this function can be called to
|
||||
/// check the reservations and cancel reservations that are not possible now anymore.
|
||||
///
|
||||
void check_reservations_and_cancel_if_not_possible();
|
||||
|
||||
///
|
||||
/// \brief Store reservations to key value store.
|
||||
///
|
||||
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.
|
||||
/// \param reservation The reservation.
|
||||
/// \param evse_id The evse id.
|
||||
///
|
||||
void print_reservations_debug_info(const types::reservation::Reservation& reservation,
|
||||
const std::optional<uint32_t> evse_id, const bool reservation_failed);
|
||||
};
|
||||
|
||||
} // namespace module
|
||||
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
|
||||
32
tools/EVerest-main/modules/EVSE/Auth/main/authImpl.cpp
Normal file
32
tools/EVerest-main/modules/EVSE/Auth/main/authImpl.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "authImpl.hpp"
|
||||
|
||||
#include <everest/logging.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace module {
|
||||
namespace main {
|
||||
|
||||
void authImpl::init() {
|
||||
}
|
||||
|
||||
void authImpl::ready() {
|
||||
}
|
||||
|
||||
void authImpl::handle_set_connection_timeout(int& connection_timeout) {
|
||||
this->mod->set_connection_timeout(connection_timeout);
|
||||
}
|
||||
|
||||
void authImpl::handle_set_master_pass_group_id(std::string& master_pass_group_id) {
|
||||
this->mod->set_master_pass_group_id(master_pass_group_id);
|
||||
}
|
||||
|
||||
types::authorization::WithdrawAuthorizationResult
|
||||
authImpl::handle_withdraw_authorization(WithdrawAuthorizationRequest& request) {
|
||||
return this->mod->handle_withdraw_authorization(request);
|
||||
}
|
||||
|
||||
} // namespace main
|
||||
} // namespace module
|
||||
64
tools/EVerest-main/modules/EVSE/Auth/main/authImpl.hpp
Normal file
64
tools/EVerest-main/modules/EVSE/Auth/main/authImpl.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef MAIN_AUTH_IMPL_HPP
|
||||
#define MAIN_AUTH_IMPL_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 3
|
||||
//
|
||||
|
||||
#include <generated/interfaces/auth/Implementation.hpp>
|
||||
|
||||
#include "../Auth.hpp"
|
||||
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
// insert your custom include headers here
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
|
||||
namespace module {
|
||||
namespace main {
|
||||
|
||||
struct Conf {};
|
||||
|
||||
class authImpl : public authImplBase {
|
||||
public:
|
||||
authImpl() = delete;
|
||||
authImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<Auth>& mod, Conf& config) :
|
||||
authImplBase(ev, "main"), mod(mod), config(config){};
|
||||
|
||||
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
|
||||
// insert your public definitions here
|
||||
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
|
||||
|
||||
protected:
|
||||
// command handler functions (virtual)
|
||||
virtual void handle_set_connection_timeout(int& connection_timeout) override;
|
||||
virtual void handle_set_master_pass_group_id(std::string& master_pass_group_id) override;
|
||||
virtual types::authorization::WithdrawAuthorizationResult
|
||||
handle_withdraw_authorization(types::authorization::WithdrawAuthorizationRequest& request) override;
|
||||
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
// insert your protected definitions here
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
|
||||
private:
|
||||
const Everest::PtrContainer<Auth>& mod;
|
||||
const Conf& config;
|
||||
|
||||
virtual void init() override;
|
||||
virtual void ready() override;
|
||||
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
// insert your private definitions here
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
};
|
||||
|
||||
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
|
||||
// insert other definitions here
|
||||
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
|
||||
|
||||
} // namespace main
|
||||
} // namespace module
|
||||
|
||||
#endif // MAIN_AUTH_IMPL_HPP
|
||||
99
tools/EVerest-main/modules/EVSE/Auth/manifest.yaml
Normal file
99
tools/EVerest-main/modules/EVSE/Auth/manifest.yaml
Normal file
@@ -0,0 +1,99 @@
|
||||
description: >-
|
||||
This module implements the authentication handling for the EVerest. It is responsible for providing authorization to
|
||||
the connected evse managers. In addition to that, it handles the reservation management.
|
||||
config:
|
||||
selection_algorithm:
|
||||
description: >-
|
||||
The selection algorithm contains the logic to select one connector for an incoming token. In case the charging station
|
||||
has only one connector, the selection of a connector is pretty straight-forward because there is only one that is
|
||||
available. The selection algorithm becomes relevant in case the Auth module manages authorization requests
|
||||
for multiple connectors. The following values can be set:
|
||||
PlugEvents: Selection of connector is based on EV Plug In events
|
||||
FindFirst: Simply selects the first available connector that has no active transaction
|
||||
UserInput: Placeholder, not yet implemented
|
||||
type: string
|
||||
default: FindFirst
|
||||
connection_timeout:
|
||||
description: >-
|
||||
Defines how many seconds an authorization is valid before it is discarded.
|
||||
Defines how many seconds a user can provide authorization after the plug in
|
||||
of a car
|
||||
type: integer
|
||||
master_pass_group_id:
|
||||
description: >-
|
||||
IdTokens that have this id as groupId belong to the Master Pass Group. Meaning they can stop any ongoing
|
||||
transaction, but cannot start transactions. This can, for example, be used by law enforcement personal to stop any
|
||||
ongoing transaction when an EV has to be towed away. If left empty, master_pass_group_id is not used.
|
||||
type: string
|
||||
default: ""
|
||||
prioritize_authorization_over_stopping_transaction:
|
||||
description: >-
|
||||
Boolean value to describe the handling of parent id tokens.
|
||||
|
||||
If true, a new token will be preferably used for authorization of a new connector if a connector is available. It
|
||||
will not be used to finish a transaction using its parent_id_token. parent_id_token will only be used to finish
|
||||
transaction if no connector is available for authorization anymore.
|
||||
|
||||
If false, a new token will be used to finish a transaction if the parent_id_token of a present transaction matches
|
||||
the parent_id_token of the provided_token. Authorization to available connectors will only be provided if no
|
||||
transaction can be stopped using the given parent_id_token
|
||||
type: boolean
|
||||
default: true
|
||||
ignore_connector_faults:
|
||||
description: >-
|
||||
Boolean value to describe the handling of faults on connectors.
|
||||
|
||||
If true, faults reported on connectors are ignored, i.e. they can still be authorized. This should be disabled in
|
||||
most use cases, but e.g. in free charging applications it may be useful to allow a charging session in the following case:
|
||||
A connector e.g. has an overtemperature fault that at some point will clear once it is cooled down. A car is plugged in before
|
||||
the error is cleared. The user would expect that the charging starts once it is cooled down. When this option is set to false,
|
||||
it will not be authorized on plug in as the connector is in fault state and it will never recover until the car is replugged.
|
||||
If it is set to true, the authorization happens on the faulty connector and charging will start once the fault is cleared.
|
||||
|
||||
If false, faulty connectors are treated as not available and will not be authorized. This is a good setting for e.g. public chargers.
|
||||
type: boolean
|
||||
default: false
|
||||
plug_in_timeout_enabled:
|
||||
description: >-
|
||||
This parameter is only used if the charging station has multiple EVSEs managed by this Auth module.
|
||||
|
||||
Controls the authorization-timeout behavior after a plug-in event. When enabled, an internal timer is started immediately after an EV
|
||||
is detected as plugged in. During the time defined by connection_timeout, the user must present a valid authorization token.
|
||||
|
||||
If no valid token is received within the specified connection_timeout, the authorization attempt is considered as timed out and a
|
||||
re-plug by the user is required to start a new authorization process.
|
||||
|
||||
This setting is particularly useful for charging stations with multiple EVSEs and a shared, non-EVSE-specific authorization interface
|
||||
(e.g., a single RFID reader). It prevents authorization tokens from being incorrectly associated with an EVSE where an EV is
|
||||
plugged in but has not been authorized, by ensuring that expired or ambiguous plug-ins are not considered for EVSE assignments
|
||||
for future authorization attempts.
|
||||
type: boolean
|
||||
default: false
|
||||
provides:
|
||||
main:
|
||||
description: This implements the auth interface for EVerest
|
||||
interface: auth
|
||||
reservation:
|
||||
description: This implements the reservation interface for EVerest.
|
||||
interface: reservation
|
||||
requires:
|
||||
token_provider:
|
||||
interface: auth_token_provider
|
||||
min_connections: 1
|
||||
max_connections: 128
|
||||
token_validator:
|
||||
interface: auth_token_validator
|
||||
min_connections: 1
|
||||
max_connections: 128
|
||||
evse_manager:
|
||||
interface: evse_manager
|
||||
min_connections: 1
|
||||
max_connections: 128
|
||||
kvs:
|
||||
interface: kvs
|
||||
min_connections: 0
|
||||
max_connections: 1
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- 'Piet Gömpel'
|
||||
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "reservationImpl.hpp"
|
||||
|
||||
namespace module {
|
||||
namespace reservation {
|
||||
|
||||
void reservationImpl::init() {
|
||||
}
|
||||
|
||||
void reservationImpl::ready() {
|
||||
}
|
||||
|
||||
types::reservation::ReservationResult reservationImpl::handle_reserve_now(types::reservation::Reservation& request) {
|
||||
// your code for cmd reserve_now goes here
|
||||
|
||||
EVLOG_debug << "Handle reservation for evse id " << (request.evse_id.has_value() ? request.evse_id.value() : -1);
|
||||
|
||||
const auto reservation_result = this->mod->auth_handler->handle_reservation(request);
|
||||
if (reservation_result == ReservationResult::Accepted) {
|
||||
if (!this->mod->auth_handler->call_reserved(request.reservation_id, request.evse_id)) {
|
||||
return ReservationResult::Rejected;
|
||||
}
|
||||
}
|
||||
return reservation_result;
|
||||
};
|
||||
|
||||
bool reservationImpl::handle_cancel_reservation(int& reservation_id) {
|
||||
const auto reservation_cancelled = this->mod->auth_handler->handle_cancel_reservation(reservation_id);
|
||||
if (reservation_cancelled.first) {
|
||||
// Call reservation cancelled. This comes from outside, so we don't send the status update (otherwise this is
|
||||
// sent to OCPP and that is not according to specification).
|
||||
this->mod->auth_handler->call_reservation_cancelled(reservation_id, ReservationEndReason::Cancelled,
|
||||
reservation_cancelled.second, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
types::reservation::ReservationCheckStatus
|
||||
reservationImpl::handle_exists_reservation(types::reservation::ReservationCheck& request) {
|
||||
return this->mod->auth_handler->handle_reservation_exists(request.id_token, request.evse_id,
|
||||
request.group_id_token);
|
||||
};
|
||||
|
||||
} // namespace reservation
|
||||
} // namespace module
|
||||
@@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef RESERVATION_RESERVATION_IMPL_HPP
|
||||
#define RESERVATION_RESERVATION_IMPL_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 3
|
||||
//
|
||||
|
||||
#include <generated/interfaces/reservation/Implementation.hpp>
|
||||
|
||||
#include "../Auth.hpp"
|
||||
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
// insert your custom include headers here
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
|
||||
namespace module {
|
||||
namespace reservation {
|
||||
|
||||
struct Conf {};
|
||||
|
||||
class reservationImpl : public reservationImplBase {
|
||||
public:
|
||||
reservationImpl() = delete;
|
||||
reservationImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<Auth>& mod, Conf& config) :
|
||||
reservationImplBase(ev, "reservation"), mod(mod), config(config){};
|
||||
|
||||
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
|
||||
// insert your public definitions here
|
||||
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
|
||||
|
||||
protected:
|
||||
// command handler functions (virtual)
|
||||
virtual types::reservation::ReservationResult handle_reserve_now(types::reservation::Reservation& request) override;
|
||||
virtual bool handle_cancel_reservation(int& reservation_id) override;
|
||||
virtual types::reservation::ReservationCheckStatus
|
||||
handle_exists_reservation(types::reservation::ReservationCheck& request) override;
|
||||
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
// insert your protected definitions here
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
|
||||
private:
|
||||
const Everest::PtrContainer<Auth>& mod;
|
||||
const Conf& config;
|
||||
|
||||
virtual void init() override;
|
||||
virtual void ready() override;
|
||||
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
// insert your private definitions here
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
};
|
||||
|
||||
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
|
||||
// insert other definitions here
|
||||
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
|
||||
|
||||
} // namespace reservation
|
||||
} // namespace module
|
||||
|
||||
#endif // RESERVATION_RESERVATION_IMPL_HPP
|
||||
43
tools/EVerest-main/modules/EVSE/Auth/tests/CMakeLists.txt
Normal file
43
tools/EVerest-main/modules/EVSE/Auth/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
set(TEST_TARGET_NAME ${PROJECT_NAME}_auth_tests)
|
||||
|
||||
set(MODULE_DIR
|
||||
"${PROJECT_SOURCE_DIR}/modules/EVSE/Auth")
|
||||
|
||||
set(TEST_SOURCES ${MODULE_DIR}/lib/ReservationHandler.cpp
|
||||
${MODULE_DIR}/lib/AuthHandler.cpp
|
||||
${MODULE_DIR}/lib/Connector.cpp
|
||||
${MODULE_DIR}/lib/ConnectorStateMachine.cpp)
|
||||
|
||||
add_executable(${TEST_TARGET_NAME} auth_tests.cpp reservation_tests.cpp ${TEST_SOURCES})
|
||||
|
||||
message("Current source dir: ${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
set(INCLUDE_DIR
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/stubs"
|
||||
"${MODULE_DIR}/include"
|
||||
"${MODULE_DIR}/tests"
|
||||
)
|
||||
|
||||
get_target_property(GENERATED_INCLUDE_DIR generate_cpp_files EVEREST_GENERATED_INCLUDE_DIR)
|
||||
|
||||
target_include_directories(${TEST_TARGET_NAME} PUBLIC
|
||||
${INCLUDE_DIR}
|
||||
${GENERATED_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(${TEST_TARGET_NAME} PRIVATE
|
||||
GTest::gmock
|
||||
GTest::gtest_main
|
||||
everest::timer
|
||||
${CMAKE_DL_LIBS}
|
||||
everest::log
|
||||
everest::framework
|
||||
everest::helpers
|
||||
pthread
|
||||
nlohmann_json::nlohmann_json
|
||||
date::date
|
||||
date::date-tz
|
||||
)
|
||||
|
||||
add_test(${TEST_TARGET_NAME} ${TEST_TARGET_NAME})
|
||||
ev_register_test_target(${TEST_TARGET_NAME})
|
||||
@@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* @brief This implementation is only used for testing purposes. It is used to check which EVSE have received
|
||||
* authorization.
|
||||
*
|
||||
*/
|
||||
class FakeAuthReceiver {
|
||||
|
||||
private:
|
||||
std::map<int32_t, bool> evse_index_to_authorization_map;
|
||||
|
||||
public:
|
||||
FakeAuthReceiver(){};
|
||||
explicit FakeAuthReceiver(const std::vector<int32_t>& evse_indices) {
|
||||
for (const auto evse_index : evse_indices) {
|
||||
evse_index_to_authorization_map[evse_index] = false;
|
||||
}
|
||||
};
|
||||
void add_evse_index(const int32_t evse_index) {
|
||||
evse_index_to_authorization_map[evse_index] = false;
|
||||
};
|
||||
void authorize(const int32_t evse_index) {
|
||||
evse_index_to_authorization_map[evse_index] = true;
|
||||
};
|
||||
void deauthorize(const int32_t evse_index) {
|
||||
evse_index_to_authorization_map[evse_index] = false;
|
||||
};
|
||||
bool get_authorization(const int32_t evse_index) {
|
||||
return evse_index_to_authorization_map[evse_index];
|
||||
};
|
||||
void reset() {
|
||||
for (auto& e : evse_index_to_authorization_map) {
|
||||
e.second = false;
|
||||
}
|
||||
};
|
||||
};
|
||||
2251
tools/EVerest-main/modules/EVSE/Auth/tests/auth_tests.cpp
Normal file
2251
tools/EVerest-main/modules/EVSE/Auth/tests/auth_tests.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1455
tools/EVerest-main/modules/EVSE/Auth/tests/reservation_tests.cpp
Normal file
1455
tools/EVerest-main/modules/EVSE/Auth/tests/reservation_tests.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
#ifndef KVS_INTERFACE_HPP
|
||||
#define KVS_INTERFACE_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
// #include <utils/types.hpp>
|
||||
|
||||
using nlohmann::json;
|
||||
|
||||
using Array = nlohmann::json::array_t;
|
||||
using Object = nlohmann::json::object_t;
|
||||
|
||||
class kvsIntf {
|
||||
private:
|
||||
std::variant<std::nullptr_t, Array, Object, bool, double, int, std::string> value;
|
||||
|
||||
public:
|
||||
kvsIntf() {
|
||||
}
|
||||
void call_store(std::string key,
|
||||
std::variant<std::nullptr_t, Array, Object, bool, double, int, std::string> value) {
|
||||
std::cout << "Store called!" << std::endl;
|
||||
this->value = value;
|
||||
}
|
||||
std::variant<std::nullptr_t, Array, Object, bool, double, int, std::string> call_load(std::string key) {
|
||||
std::cout << "Load called!" << std::endl;
|
||||
|
||||
return this->value;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // KVS_INTERFACE_HPP
|
||||
Reference in New Issue
Block a user