From ade74598cdbd6a7de98929e03538833ec445accb Mon Sep 17 00:00:00 2001 From: pietfried Date: Mon, 22 Jul 2024 18:42:06 +0200 Subject: [PATCH 1/3] Allow resuming of transactions for OCPP1.6: * charge_point.start() function can now receives a set of active transactions * If an internal transaction in libocpp can be identified from the internal database and the session_id is also listed in the set of active transactions, libocpp will resume the transaction * If an internal transaction in libocpp can be identified from the internal database and the session_id is not listed in the set of active transactions, libocpp will initiate a StopTransaction.req Signed-off-by: pietfried --- include/ocpp/v16/charge_point.hpp | 10 +++++-- include/ocpp/v16/charge_point_impl.hpp | 11 ++++++-- lib/ocpp/v16/charge_point.cpp | 5 ++-- lib/ocpp/v16/charge_point_impl.cpp | 37 +++++++++++++++++--------- 4 files changed, 45 insertions(+), 18 deletions(-) diff --git a/include/ocpp/v16/charge_point.hpp b/include/ocpp/v16/charge_point.hpp index 2b1404e4e..1a9d1f455 100644 --- a/include/ocpp/v16/charge_point.hpp +++ b/include/ocpp/v16/charge_point.hpp @@ -76,9 +76,15 @@ class ChargePoint { /// (Available, Unavailable, Faulted). connector_status_map is empty, last availability states from the persistant /// storage will be used /// \param bootreason reason for calling the start function - /// \return + /// \param resuming_session_ids can optionally contain active session ids from previous executions. If empty and + /// libocpp has transactions in its internal database that have not been stopped yet, calling this function will + /// initiate a StopTransaction.req for those transactions. If this vector contains session_ids this function will + /// not stop transactions with this session_id even in case it has an internal database entry for this session and + /// it hasnt been stopped yet. Its ignored if this vector contains session_ids that are unknown to libocpp. + /// \return bool start(const std::map& connector_status_map = {}, - BootReasonEnum bootreason = BootReasonEnum::PowerUp); + BootReasonEnum bootreason = BootReasonEnum::PowerUp, + const std::set resuming_session_ids = {}); /// \brief Restarts the ChargePoint if it has been stopped before. The ChargePoint is reinitialized, connects to the /// websocket and starts to communicate OCPP messages again diff --git a/include/ocpp/v16/charge_point_impl.hpp b/include/ocpp/v16/charge_point_impl.hpp index ea602b453..6b2631d0c 100644 --- a/include/ocpp/v16/charge_point_impl.hpp +++ b/include/ocpp/v16/charge_point_impl.hpp @@ -232,6 +232,7 @@ class ChargePointImpl : ocpp::ChargingStationBase { /// update can proceed void change_all_connectors_to_unavailable_for_firmware_update(); + void try_resume_transactions(const std::set resuming_session_ids); void stop_all_transactions(); void stop_all_transactions(Reason reason); bool validate_against_cache_entries(CiString<20> id_tag); @@ -382,8 +383,14 @@ class ChargePointImpl : ocpp::ChargingStationBase { /// \param connector_status_map initial state of connectors including connector 0 with reduced set of states /// (Available, Unavailable, Faulted) /// \param bootreason reason for calling the start function - /// \return - bool start(const std::map& connector_status_map, BootReasonEnum bootreason); + /// \param resuming_session_ids can optionally contain active session ids from previous executions. If empty and + /// libocpp has transactions in its internal database that have not been stopped yet, calling this function will + /// initiate a StopTransaction.req for those transactions. If this vector contains session_ids this function will + /// not stop transactions with this session_id even in case it has an internal database entry for this session and + /// it hasnt been stopped yet. Its ignored if this vector contains session_ids that are unknown to libocpp. + /// \return + bool start(const std::map& connector_status_map, BootReasonEnum bootreason, + const std::set resuming_session_ids); /// \brief Restarts the ChargePoint if it has been stopped before. The ChargePoint is reinitialized, connects to the /// websocket and starts to communicate OCPP messages again diff --git a/lib/ocpp/v16/charge_point.cpp b/lib/ocpp/v16/charge_point.cpp index c96e3756c..307b0d357 100644 --- a/lib/ocpp/v16/charge_point.cpp +++ b/lib/ocpp/v16/charge_point.cpp @@ -21,8 +21,9 @@ ChargePoint::ChargePoint(const std::string& config, const fs::path& share_path, ChargePoint::~ChargePoint() = default; -bool ChargePoint::start(const std::map& connector_status_map, BootReasonEnum bootreason) { - return this->charge_point->start(connector_status_map, bootreason); +bool ChargePoint::start(const std::map& connector_status_map, BootReasonEnum bootreason, + const std::set resuming_session_ids) { + return this->charge_point->start(connector_status_map, bootreason, resuming_session_ids); } bool ChargePoint::restart(const std::map& connector_status_map, BootReasonEnum bootreason) { diff --git a/lib/ocpp/v16/charge_point_impl.cpp b/lib/ocpp/v16/charge_point_impl.cpp index 2414eabf2..c517cbbaf 100644 --- a/lib/ocpp/v16/charge_point_impl.cpp +++ b/lib/ocpp/v16/charge_point_impl.cpp @@ -360,7 +360,7 @@ void ChargePointImpl::update_clock_aligned_meter_values_interval() { } } -void ChargePointImpl::stop_pending_transactions() { +void ChargePointImpl::try_resume_transactions(const std::set resuming_session_ids) { std::vector transactions; try { transactions = this->database_handler->get_transactions(true); @@ -395,20 +395,32 @@ void ChargePointImpl::stop_pending_transactions() { this->transaction_handler->add_transaction(transaction); - // StopTransaction.req is not yet queued for the transaction in the database, so we add the transaction to - // the transaction_handler and initiate a StopTransaction.req - if (!this->message_queue->contains_stop_transaction_message(transaction_entry.transaction_id)) { - EVLOG_info << "Queuing StopTransaction.req for transaction with id: " << transaction_entry.transaction_id - << " because it hasn't been acknowledged by CSMS."; - this->stop_transaction(transaction_entry.connector, Reason::PowerLoss, std::nullopt); - } else { - // mark the transaction as stopped + EVLOG_info << "Trying to resume transaction with session_id: " << transaction_entry.session_id + << " and transaction_id: " << transaction_entry.transaction_id; + + if (this->message_queue->contains_stop_transaction_message(transaction_entry.transaction_id)) { + // StopTransaction.req is already queued for the transaction in the database, so we mark the transaction as + // stopped and wait for the StopTransaction.conf transaction->set_finished(); this->transaction_handler->add_stopped_transaction(transaction->get_connector()); + } else { + if (std::find(resuming_session_ids.begin(), resuming_session_ids.end(), transaction_entry.session_id) == + resuming_session_ids.end()) { + EVLOG_info << "Queuing StopTransaction.req for transaction with id: " + << transaction_entry.transaction_id + << " because it hasn't been acknowledged by CSMS and shall not be resumed."; + this->stop_transaction(transaction_entry.connector, Reason::PowerLoss, std::nullopt); + } else { + EVLOG_info << "Resuming transaction with transaction id: " << transaction_entry.transaction_id; + } } } } +void ChargePointImpl::stop_pending_transactions() { + // Initiate a StopTransaction.req +} + void ChargePointImpl::load_charging_profiles() { try { auto profiles = this->database_handler->get_charging_profiles(); @@ -855,7 +867,8 @@ void ChargePointImpl::send_meter_value(int32_t connector, MeterValue meter_value this->send(call, initiated_by_trigger_message); } -bool ChargePointImpl::start(const std::map& connector_status_map, BootReasonEnum bootreason) { +bool ChargePointImpl::start(const std::map& connector_status_map, BootReasonEnum bootreason, + const std::set resuming_session_ids) { this->message_queue->start(); this->bootreason = bootreason; this->init_state_machine(connector_status_map); @@ -864,7 +877,7 @@ bool ChargePointImpl::start(const std::map& connector_st // push transaction messages including SecurityEventNotification.req onto the message queue this->message_queue->get_persisted_messages_from_db(this->configuration->getDisableSecurityEventNotifications()); this->boot_notification(); - this->stop_pending_transactions(); + this->try_resume_transactions(resuming_session_ids); this->load_charging_profiles(); this->call_set_connection_timeout(); @@ -903,7 +916,7 @@ bool ChargePointImpl::restart(const std::map& connector_ this->database_handler->open_connection(); // instantiating new message queue on restart this->message_queue = this->create_message_queue(); - return this->start(connector_status_map, bootreason); + return this->start(connector_status_map, bootreason, {}); } else { EVLOG_warning << "Attempting to restart Chargepoint while it has not been stopped before"; return false; From ac9a9c56eb2264f5a621ecbadfc26200391118b5 Mon Sep 17 00:00:00 2001 From: pietfried Date: Tue, 23 Jul 2024 10:33:59 +0200 Subject: [PATCH 2/3] added method docs to try_resume_transactions and removed stop_pending_transactions Signed-off-by: pietfried --- include/ocpp/v16/charge_point_impl.hpp | 8 +++++--- lib/ocpp/v16/charge_point_impl.cpp | 4 ---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/include/ocpp/v16/charge_point_impl.hpp b/include/ocpp/v16/charge_point_impl.hpp index 6b2631d0c..32b698bf2 100644 --- a/include/ocpp/v16/charge_point_impl.hpp +++ b/include/ocpp/v16/charge_point_impl.hpp @@ -232,6 +232,11 @@ class ChargePointImpl : ocpp::ChargingStationBase { /// update can proceed void change_all_connectors_to_unavailable_for_firmware_update(); + /// \brief Tries to resume the transactions given by \p resuming_session_ids . This function retrieves open + /// transactions from the internal database (e.g. because of power loss). In case the \p + /// resuming_session_ids contain the internal session_id, this function attempts to resume the transaction by + /// initializing it and adding it to the \ref transaction_handler. If the session_id is not part of \p + /// resuming_session_ids a StopTransaction.req is initiated to properly close the transaction. void try_resume_transactions(const std::set resuming_session_ids); void stop_all_transactions(); void stop_all_transactions(Reason reason); @@ -240,9 +245,6 @@ class ChargePointImpl : ocpp::ChargingStationBase { // new transaction handling: void start_transaction(std::shared_ptr transaction); - /// \brief Sends StopTransaction.req for all transactions for which meter_stop or time_end is not set in the - /// database's Transaction table - void stop_pending_transactions(); void stop_transaction(int32_t connector, Reason reason, std::optional> id_tag_end); /// \brief Converts the given \p measurands_csv to a vector of Measurands diff --git a/lib/ocpp/v16/charge_point_impl.cpp b/lib/ocpp/v16/charge_point_impl.cpp index c517cbbaf..4e2b979de 100644 --- a/lib/ocpp/v16/charge_point_impl.cpp +++ b/lib/ocpp/v16/charge_point_impl.cpp @@ -417,10 +417,6 @@ void ChargePointImpl::try_resume_transactions(const std::set resumi } } -void ChargePointImpl::stop_pending_transactions() { - // Initiate a StopTransaction.req -} - void ChargePointImpl::load_charging_profiles() { try { auto profiles = this->database_handler->get_charging_profiles(); From c70334abbe79fe5ffcbde3702c4e578eed2aa191 Mon Sep 17 00:00:00 2001 From: pietfried Date: Tue, 23 Jul 2024 11:54:02 +0200 Subject: [PATCH 3/3] chanegd arg to const ref Signed-off-by: pietfried --- include/ocpp/v16/charge_point.hpp | 2 +- include/ocpp/v16/charge_point_impl.hpp | 4 ++-- lib/ocpp/v16/charge_point.cpp | 2 +- lib/ocpp/v16/charge_point_impl.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/ocpp/v16/charge_point.hpp b/include/ocpp/v16/charge_point.hpp index 1a9d1f455..c9ed2a1e2 100644 --- a/include/ocpp/v16/charge_point.hpp +++ b/include/ocpp/v16/charge_point.hpp @@ -84,7 +84,7 @@ class ChargePoint { /// \return bool start(const std::map& connector_status_map = {}, BootReasonEnum bootreason = BootReasonEnum::PowerUp, - const std::set resuming_session_ids = {}); + const std::set& resuming_session_ids = {}); /// \brief Restarts the ChargePoint if it has been stopped before. The ChargePoint is reinitialized, connects to the /// websocket and starts to communicate OCPP messages again diff --git a/include/ocpp/v16/charge_point_impl.hpp b/include/ocpp/v16/charge_point_impl.hpp index 32b698bf2..bc367ff53 100644 --- a/include/ocpp/v16/charge_point_impl.hpp +++ b/include/ocpp/v16/charge_point_impl.hpp @@ -237,7 +237,7 @@ class ChargePointImpl : ocpp::ChargingStationBase { /// resuming_session_ids contain the internal session_id, this function attempts to resume the transaction by /// initializing it and adding it to the \ref transaction_handler. If the session_id is not part of \p /// resuming_session_ids a StopTransaction.req is initiated to properly close the transaction. - void try_resume_transactions(const std::set resuming_session_ids); + void try_resume_transactions(const std::set& resuming_session_ids); void stop_all_transactions(); void stop_all_transactions(Reason reason); bool validate_against_cache_entries(CiString<20> id_tag); @@ -392,7 +392,7 @@ class ChargePointImpl : ocpp::ChargingStationBase { /// it hasnt been stopped yet. Its ignored if this vector contains session_ids that are unknown to libocpp. /// \return bool start(const std::map& connector_status_map, BootReasonEnum bootreason, - const std::set resuming_session_ids); + const std::set& resuming_session_ids); /// \brief Restarts the ChargePoint if it has been stopped before. The ChargePoint is reinitialized, connects to the /// websocket and starts to communicate OCPP messages again diff --git a/lib/ocpp/v16/charge_point.cpp b/lib/ocpp/v16/charge_point.cpp index 307b0d357..e614f6ce5 100644 --- a/lib/ocpp/v16/charge_point.cpp +++ b/lib/ocpp/v16/charge_point.cpp @@ -22,7 +22,7 @@ ChargePoint::ChargePoint(const std::string& config, const fs::path& share_path, ChargePoint::~ChargePoint() = default; bool ChargePoint::start(const std::map& connector_status_map, BootReasonEnum bootreason, - const std::set resuming_session_ids) { + const std::set& resuming_session_ids) { return this->charge_point->start(connector_status_map, bootreason, resuming_session_ids); } diff --git a/lib/ocpp/v16/charge_point_impl.cpp b/lib/ocpp/v16/charge_point_impl.cpp index 4e2b979de..6148415ba 100644 --- a/lib/ocpp/v16/charge_point_impl.cpp +++ b/lib/ocpp/v16/charge_point_impl.cpp @@ -360,7 +360,7 @@ void ChargePointImpl::update_clock_aligned_meter_values_interval() { } } -void ChargePointImpl::try_resume_transactions(const std::set resuming_session_ids) { +void ChargePointImpl::try_resume_transactions(const std::set& resuming_session_ids) { std::vector transactions; try { transactions = this->database_handler->get_transactions(true); @@ -864,7 +864,7 @@ void ChargePointImpl::send_meter_value(int32_t connector, MeterValue meter_value } bool ChargePointImpl::start(const std::map& connector_status_map, BootReasonEnum bootreason, - const std::set resuming_session_ids) { + const std::set& resuming_session_ids) { this->message_queue->start(); this->bootreason = bootreason; this->init_state_machine(connector_status_map);