diff --git a/include/ocpp/v16/charge_point.hpp b/include/ocpp/v16/charge_point.hpp index 2b1404e4e..c9ed2a1e2 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..bc367ff53 100644 --- a/include/ocpp/v16/charge_point_impl.hpp +++ b/include/ocpp/v16/charge_point_impl.hpp @@ -232,6 +232,12 @@ 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); bool validate_against_cache_entries(CiString<20> id_tag); @@ -239,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 @@ -382,8 +385,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..e614f6ce5 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..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::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,16 +395,24 @@ 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; + } } } } @@ -855,7 +863,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 +873,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 +912,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;