Skip to content

Commit

Permalink
* Removed handling of operational states of components (ChargingStati…
Browse files Browse the repository at this point in the history
…on, EVSE and Connector) from OCPP201 module. This is now persisted and handled inside libocpp

* Removed kvs requirement from OCPP201 module because it is not required anymore to store operational states
* Calling on_enabled and on_unavailable as part of connector_effective_operative_status_changed_callback to synchronously update the state machine in libocpp. Async updates using Enabled and Disabled events lead to a race condition on boot and could result in StatusNotification.req that are not up to date

Signed-off-by: pietfried <[email protected]>
  • Loading branch information
Pietfried committed Jan 18, 2024
1 parent 9a4de02 commit 7ffd0ab
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 171 deletions.
3 changes: 0 additions & 3 deletions config/config-sil-ocpp201.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,6 @@ active_modules:
security:
- module_id: evse_security
implementation_id: main
kvs:
- module_id: persistent_store
implementation_id: main
persistent_store:
module: PersistentStore
evse_security:
Expand Down
2 changes: 1 addition & 1 deletion dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ libcurl:
# OCPP
libocpp:
git: https://github.com/EVerest/libocpp.git
git_tag: 9afe405
git_tag: 536ec6e
# Josev
Josev:
git: https://github.com/EVerest/ext-switchev-iso15118.git
Expand Down
169 changes: 29 additions & 140 deletions modules/OCPP201/OCPP201.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ namespace module {
const std::string INIT_SQL = "init_core.sql";
const std::string CERTS_DIR = "certs";

const std::string KVS_OCPP201_INOPERATIVE_KEY_PREFIX = "OCPP201_INOPERATIVE_";

namespace fs = std::filesystem;

ocpp::v201::FirmwareStatusEnum get_firmware_status_notification(const types::system::FirmwareUpdateStatusEnum status) {
Expand Down Expand Up @@ -509,52 +507,32 @@ void OCPP201::init_evse_ready_map() {
}
}

void OCPP201::init_evses() {

if (this->r_kvs->call_exists(KVS_OCPP201_INOPERATIVE_KEY_PREFIX + "0")) {
this->cs_operational_status = ocpp::v201::OperationalStatusEnum::Inoperative;
} else {
this->cs_operational_status = ocpp::v201::OperationalStatusEnum::Operative;
}

std::map<int32_t, int32_t> OCPP201::get_connector_structure() {
std::map<int32_t, int32_t> evse_connector_structure;
int evse_id = 1;
for (const auto& evse : this->r_evse_manager) {
int connector_id = 1;
auto _evse = evse->call_get_evse();
int32_t num_connectors = _evse.connectors.size();

if (_evse.id != evse_id) {
throw std::runtime_error("Configured evse_id(s) must start with 1 counting upwards");
}

if (_evse.connectors.size() == 0) {
_evse.connectors.push_back({1});
}

Evse module_evse;
module_evse.evse_id = evse_id;

if (this->r_kvs->call_exists(KVS_OCPP201_INOPERATIVE_KEY_PREFIX + std::to_string(evse_id))) {
module_evse.operational_state = ocpp::v201::OperationalStatusEnum::Inoperative;
} else {
module_evse.operational_state = ocpp::v201::OperationalStatusEnum::Operative;
}

for (const auto& connector : _evse.connectors) {
if (connector.id != connector_id) {
throw std::runtime_error("Configured connector_id(s) must start with 1 counting upwards");
}

auto connector_id_str = std::to_string(evse_id) + "." + std::to_string(connector_id);
if (this->r_kvs->call_exists(KVS_OCPP201_INOPERATIVE_KEY_PREFIX + connector_id_str)) {
module_evse.connectors[connector_id] = ocpp::v201::OperationalStatusEnum::Inoperative;
} else {
module_evse.connectors[connector_id] = ocpp::v201::OperationalStatusEnum::Operative;
if (num_connectors > 0) {
int connector_id = 1;
for (const auto& connector : _evse.connectors) {
if (connector.id != connector_id) {
throw std::runtime_error("Configured connector_id(s) must start with 1 counting upwards");
}
connector_id++;
}
connector_id++;
} else {
num_connectors = 1;
}

this->evses[_evse.id] = module_evse;
evse_connector_structure[evse_id] = num_connectors;
evse_id++;
}
return evse_connector_structure;
}

bool OCPP201::all_evse_ready() {
Expand All @@ -567,30 +545,6 @@ bool OCPP201::all_evse_ready() {
return true;
}

void OCPP201::set_connector_operational_status(const ocpp::v201::OperationalStatusEnum operational_status,
const int32_t evse_id, const int32_t connector_id, const bool persist) {
if (operational_status == ocpp::v201::OperationalStatusEnum::Operative) {
this->evses.at(evse_id).connectors.at(connector_id) = ocpp::v201::OperationalStatusEnum::Operative;
auto connector_id_str = std::to_string(evse_id) + "." + std::to_string(connector_id);
this->r_kvs->call_delete(KVS_OCPP201_INOPERATIVE_KEY_PREFIX + connector_id_str);
} else if (persist) {
this->evses.at(evse_id).connectors.at(connector_id) = ocpp::v201::OperationalStatusEnum::Inoperative;
auto connector_id_str = std::to_string(evse_id) + "." + std::to_string(connector_id);
this->r_kvs->call_store(KVS_OCPP201_INOPERATIVE_KEY_PREFIX + connector_id_str, true);
}
}

void OCPP201::set_evse_operational_status(const ocpp::v201::OperationalStatusEnum operational_status,
const int32_t evse_id, const bool persist) {
if (operational_status == ocpp::v201::OperationalStatusEnum::Operative) {
this->evses.at(evse_id).operational_state = ocpp::v201::OperationalStatusEnum::Operative;
this->r_kvs->call_delete(KVS_OCPP201_INOPERATIVE_KEY_PREFIX + std::to_string(evse_id));
} else if (persist) {
this->evses.at(evse_id).operational_state = ocpp::v201::OperationalStatusEnum::Inoperative;
this->r_kvs->call_store(KVS_OCPP201_INOPERATIVE_KEY_PREFIX + std::to_string(evse_id), true);
}
}

void OCPP201::init() {
invoke_init(*p_main);
invoke_init(*p_auth_provider);
Expand Down Expand Up @@ -636,7 +590,6 @@ void OCPP201::ready() {
invoke_ready(*p_auth_validator);

this->ocpp_share_path = this->info.paths.share;
this->cs_operational_status = ocpp::v201::OperationalStatusEnum::Operative;

const auto device_model_database_path = [&]() {
const auto config_device_model_path = fs::path(this->config.DeviceModelDatabasePath);
Expand Down Expand Up @@ -694,49 +647,18 @@ void OCPP201::ready() {
}
};

callbacks.change_availability_callback = [this](const ocpp::v201::ChangeAvailabilityRequest& request,
const bool persist) {
if (request.evse.has_value()) {
auto evse_id = request.evse.value().id;
auto connector_id = request.evse.value().connectorId;
if (request.operationalStatus == ocpp::v201::OperationalStatusEnum::Operative) {
// change to operative
if (connector_id.has_value()) {
// connector is addressed
this->set_connector_operational_status(ocpp::v201::OperationalStatusEnum::Operative, evse_id,
connector_id.value(), persist);
} else {
// EVSE is addressed
this->set_evse_operational_status(ocpp::v201::OperationalStatusEnum::Operative, evse_id, persist);
callbacks.connector_effective_operative_status_changed_callback =
[this](const int32_t evse_id, const int32_t connector_id, const ocpp::v201::OperationalStatusEnum new_status) {
if (new_status == ocpp::v201::OperationalStatusEnum::Operative) {
if (this->r_evse_manager.at(evse_id - 1)->call_enable(connector_id)) {
this->charge_point->on_enabled(evse_id, connector_id);
}
this->r_evse_manager.at(evse_id - 1)->call_enable(request.evse.value().connectorId.value_or(0));
} else {
// change to inoperative
if (connector_id.has_value()) {
// connector is addressed
this->set_connector_operational_status(ocpp::v201::OperationalStatusEnum::Inoperative, evse_id,
connector_id.value(), persist);
} else {
// EVSE is addressed
this->set_evse_operational_status(ocpp::v201::OperationalStatusEnum::Inoperative, evse_id, persist);
}
this->r_evse_manager.at(evse_id - 1)->call_disable(request.evse.value().connectorId.value_or(0));
}
} else {
// whole charging station is adressed
this->cs_operational_status = request.operationalStatus;
for (size_t evse_id = 1; evse_id <= this->r_evse_manager.size(); evse_id++) {
if (this->cs_operational_status == ocpp::v201::OperationalStatusEnum::Operative and
this->evses.at(evse_id).operational_state == ocpp::v201::OperationalStatusEnum::Operative) {
this->r_evse_manager.at(evse_id - 1)->call_enable(0);
this->r_kvs->call_delete(KVS_OCPP201_INOPERATIVE_KEY_PREFIX + "0");
} else if (request.operationalStatus == ocpp::v201::OperationalStatusEnum::Inoperative) {
this->r_evse_manager.at(evse_id - 1)->call_disable(0);
this->r_kvs->call_store(KVS_OCPP201_INOPERATIVE_KEY_PREFIX + "0", true);
if (this->r_evse_manager.at(evse_id - 1)->call_disable(connector_id)) {
this->charge_point->on_unavailable(evse_id, connector_id);
}
}
}
};
};

callbacks.remote_start_transaction_callback = [this](const ocpp::v201::RequestStartTransactionRequest& request,
const bool authorize_remote_start) {
Expand Down Expand Up @@ -841,13 +763,7 @@ void OCPP201::ready() {

const auto sql_init_path = this->ocpp_share_path / INIT_SQL;

this->init_evses();

std::map<int32_t, int32_t> evse_connector_structure;
for (const auto [evse_id, evse] : this->evses) {
evse_connector_structure[evse_id] = evse.connectors.size();
}

std::map<int32_t, int32_t> evse_connector_structure = this->get_connector_structure();
this->charge_point = std::make_unique<ocpp::v201::ChargePoint>(
evse_connector_structure, device_model_database_path, this->ocpp_share_path.string(),
this->config.CoreDatabasePath, sql_init_path.string(), this->config.MessageLogPath,
Expand Down Expand Up @@ -948,28 +864,12 @@ void OCPP201::ready() {
break;
}
case types::evse_manager::SessionEventEnum::Disabled: {
if (session_event.connector_id.has_value()) {
this->charge_point->on_unavailable(evse_id, connector_id);
} else {
for (size_t index = 1; index <= this->evses.at(evse_id).connectors.size(); index++) {
this->charge_point->on_unavailable(evse_id, index);
}
}
this->charge_point->on_unavailable(evse_id, connector_id);
break;
}
case types::evse_manager::SessionEventEnum::Enabled: {
if (session_event.connector_id.has_value()) {
if (this->evses.at(evse_id).operational_state == ocpp::v201::OperationalStatusEnum::Operative) {
this->charge_point->on_operative(evse_id, connector_id);
}
} else {
for (size_t index = 1; index <= this->evses.at(evse_id).connectors.size(); index++) {
if (this->evses.at(evse_id).connectors.at(index) ==
ocpp::v201::OperationalStatusEnum::Operative) {
this->charge_point->on_operative(evse_id, index);
}
}
}
// A single connector was enabled
this->charge_point->on_enabled(evse_id, connector_id);
break;
}
}
Expand Down Expand Up @@ -1013,19 +913,8 @@ void OCPP201::ready() {
while (!this->all_evse_ready()) {
this->evse_ready_cv.wait(lk);
}

// align state machine based on operational status
for (const auto& [evse_id, evse] : this->evses) {
if (evse.operational_state == ocpp::v201::OperationalStatusEnum::Inoperative or
this->cs_operational_status == ocpp::v201::OperationalStatusEnum::Inoperative) {
this->r_evse_manager.at(evse_id - 1)->call_disable(0);
}
for (const auto [connector_id, operational_state] : evse.connectors) {
if (operational_state == ocpp::v201::OperationalStatusEnum::Inoperative) {
this->r_evse_manager.at(evse_id - 1)->call_disable(connector_id);
}
}
}
// In case (for some reason) EvseManager ready signals are sent after this point, this will prevent a hang
lk.unlock();

const auto boot_reason = get_boot_reason(this->r_system->call_get_boot_reason());
this->charge_point->set_message_queue_resume_delay(std::chrono::seconds(this->config.MessageQueueResumeDelay));
Expand Down
25 changes: 2 additions & 23 deletions modules/OCPP201/OCPP201.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,12 @@
// headers for required interface implementations
#include <generated/interfaces/evse_manager/Interface.hpp>
#include <generated/interfaces/evse_security/Interface.hpp>
#include <generated/interfaces/kvs/Interface.hpp>
#include <generated/interfaces/ocpp_data_transfer/Interface.hpp>
#include <generated/interfaces/system/Interface.hpp>

// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
#include <ocpp/v201/charge_point.hpp>

struct Evse {
uint16_t evse_id;
ocpp::v201::OperationalStatusEnum operational_state;
std::map<uint16_t, ocpp::v201::OperationalStatusEnum> connectors;
ocpp::v201::OperationalStatusEnum get_connector_state(uint16_t connector_id) {
return connectors.at(connector_id);
}
};
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1

namespace module {
Expand All @@ -57,7 +47,7 @@ class OCPP201 : public Everest::ModuleBase {
std::unique_ptr<auth_token_providerImplBase> p_auth_provider,
std::unique_ptr<ocpp_data_transferImplBase> p_data_transfer,
std::vector<std::unique_ptr<evse_managerIntf>> r_evse_manager, std::unique_ptr<systemIntf> r_system,
std::unique_ptr<evse_securityIntf> r_security, std::unique_ptr<kvsIntf> r_kvs,
std::unique_ptr<evse_securityIntf> r_security,
std::vector<std::unique_ptr<ocpp_data_transferIntf>> r_data_transfer, Conf& config) :
ModuleBase(info),
mqtt(mqtt_provider),
Expand All @@ -68,7 +58,6 @@ class OCPP201 : public Everest::ModuleBase {
r_evse_manager(std::move(r_evse_manager)),
r_system(std::move(r_system)),
r_security(std::move(r_security)),
r_kvs(std::move(r_kvs)),
r_data_transfer(std::move(r_data_transfer)),
config(config){};

Expand All @@ -80,7 +69,6 @@ class OCPP201 : public Everest::ModuleBase {
const std::vector<std::unique_ptr<evse_managerIntf>> r_evse_manager;
const std::unique_ptr<systemIntf> r_system;
const std::unique_ptr<evse_securityIntf> r_security;
const std::unique_ptr<kvsIntf> r_kvs;
const std::vector<std::unique_ptr<ocpp_data_transferIntf>> r_data_transfer;
const Conf& config;

Expand All @@ -105,22 +93,13 @@ class OCPP201 : public Everest::ModuleBase {
// trigger reason in TransactionStarted event
std::filesystem::path ocpp_share_path;

// holds operational states of EVSE
ocpp::v201::OperationalStatusEnum cs_operational_status;
std::map<uint16_t, Evse> evses;

// key represents evse_id, value indicates if ready
std::map<int32_t, bool> evse_ready_map;
std::mutex evse_ready_mutex;
std::condition_variable evse_ready_cv;
void init_evse_ready_map();
void init_evses();
bool all_evse_ready();

void set_connector_operational_status(const ocpp::v201::OperationalStatusEnum operational_status,
const int32_t evse_id, const int32_t connector_id, const bool persist);
void set_evse_operational_status(const ocpp::v201::OperationalStatusEnum operational_status, const int32_t evse_id,
const bool persist);
std::map<int32_t, int32_t> get_connector_structure();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};

Expand Down
4 changes: 0 additions & 4 deletions modules/OCPP201/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@ requires:
interface: evse_security
min_connections: 1
max_connections: 1
kvs:
interface: kvs
min_connections: 1
max_connections: 1
data_transfer:
interface: ocpp_data_transfer
min_connections: 0
Expand Down

0 comments on commit 7ffd0ab

Please sign in to comment.