diff --git a/include/ocpp/v201/charge_point.hpp b/include/ocpp/v201/charge_point.hpp index 4f67b6c64..7d2749776 100644 --- a/include/ocpp/v201/charge_point.hpp +++ b/include/ocpp/v201/charge_point.hpp @@ -182,7 +182,7 @@ class ChargePoint : ocpp::ChargingStationBase { private: // reference to evses - std::map> evses; + std::map> evses; // utility std::unique_ptr> message_queue; @@ -350,7 +350,7 @@ class ChargePoint : ocpp::ChargingStationBase { /// If id_token is equal to reserved id_token or group_id_token is equal, return false. /// If there is no reservation, return false. /// - bool is_evse_reserved_for_other(const std::unique_ptr& evse, const IdToken& id_token, + bool is_evse_reserved_for_other(const std::unique_ptr& evse, const IdToken& id_token, const std::optional& group_id_token) const; /// @@ -359,7 +359,7 @@ class ChargePoint : ocpp::ChargingStationBase { /// \param evse Evse to check. /// \return True if at least one connector is not faulted or unavailable. /// - bool is_evse_connector_available(const std::unique_ptr& evse) const; + bool is_evse_connector_available(const std::unique_ptr& evse) const; /// /// \brief Set all connectors of a given evse to unavailable. @@ -367,7 +367,7 @@ class ChargePoint : ocpp::ChargingStationBase { /// \param persist True if unavailability should persist. If it is set to false, there will be a check per /// connector if it was already set to true and if that is the case, it will be persisted anyway. /// - void set_evse_connectors_unavailable(const std::unique_ptr& evse, bool persist); + void set_evse_connectors_unavailable(const std::unique_ptr& evse, bool persist); /// \brief Get the value optional offline flag /// \return true if the charge point is offline. std::nullopt if it is online; @@ -522,7 +522,7 @@ class ChargePoint : ocpp::ChargingStationBase { bool are_all_connectors_effectively_inoperative(); /// \brief Returns a pointer to the EVSE with ID \param evse_id - Evse* get_evse(int32_t evse_id); + EvseInterface* get_evse(int32_t evse_id); /// \brief Returns a pointer to the connector with ID \param connector_id in the EVSE with ID \param evse_id Connector* get_connector(int32_t evse_id, int32_t connector_id); diff --git a/include/ocpp/v201/evse.hpp b/include/ocpp/v201/evse.hpp index 259e71ebb..8c51279cf 100644 --- a/include/ocpp/v201/evse.hpp +++ b/include/ocpp/v201/evse.hpp @@ -18,9 +18,111 @@ namespace ocpp { namespace v201 { +class EvseInterface { +public: + virtual ~EvseInterface(); + + /// \brief Returns an OCPP2.0.1 EVSE type + /// \return + virtual EVSE get_evse_info() = 0; + + /// \brief Returns the number of connectors of this EVSE + /// \return + virtual uint32_t get_number_of_connectors() = 0; + + /// \brief Opens a new transaction + /// \param transaction_id id of the transaction + /// \param connector_id id of the connector + /// \param timestamp timestamp of the start of the transaction + /// \param meter_start start meter value of the transaction + /// \param id_token id_token with which the transaction was authorized / started + /// \param group_id_token optional group id_token + /// \param reservation optional reservation_id if evse was reserved + /// \param sampled_data_tx_updated_interval Interval between sampling of metering (or other) data, intended to + /// be transmitted via TransactionEventRequest (eventType = Updated) messages + virtual void open_transaction(const std::string& transaction_id, const int32_t connector_id, + const DateTime& timestamp, const MeterValue& meter_start, const IdToken& id_token, + const std::optional& group_id_token, + const std::optional reservation_id, + const std::chrono::seconds sampled_data_tx_updated_interval, + const std::chrono::seconds sampled_data_tx_ended_interval, + const std::chrono::seconds aligned_data_tx_updated_interval, + const std::chrono::seconds aligned_data_tx_ended_interval) = 0; + + /// \brief Closes the transaction on this evse by adding the given \p timestamp \p meter_stop and \p reason . + /// \param timestamp + /// \param meter_stop + /// \param reason + virtual void close_transaction(const DateTime& timestamp, const MeterValue& meter_stop, + const ReasonEnum& reason) = 0; + + /// \brief Start checking if the max energy on invalid id has exceeded. + /// Will call pause_charging_callback when that happens. + virtual void start_checking_max_energy_on_invalid_id() = 0; + + /// \brief Indicates if a transaction is active at this evse + /// \return + virtual bool has_active_transaction() = 0; + + /// \brief Indicates if a transaction is active at this evse at the given \p connector_id + /// \param connector_id id of the connector of the evse + /// \return + virtual bool has_active_transaction(const int32_t connector_id) = 0; + + /// \brief Releases the reference of the transaction on this evse + virtual void release_transaction() = 0; + + /// \brief Returns a pointer to the EnhancedTransaction of this evse + /// \return pointer to transaction (nullptr if no transaction is active) + virtual std::unique_ptr& get_transaction() = 0; + + /// \brief Submits the given \p event to the state machine controller of the connector with the given + /// \p connector_id + /// \param connector_id id of the connector of the evse + /// \param event + virtual void submit_event(const int32_t connector_id, ConnectorEvent event) = 0; + + /// \brief Event handler that should be called when a new meter_value for this evse is present + /// \param meter_value + virtual void on_meter_value(const MeterValue& meter_value) = 0; + + /// \brief Returns the last present meter value for this evse + /// \return + virtual MeterValue get_meter_value() = 0; + + /// @brief Return the idle meter values for this evse + /// \return MeterValue type + virtual MeterValue get_idle_meter_value() = 0; + + /// @brief Clear the idle meter values for this evse + virtual void clear_idle_meter_values() = 0; + + /// \brief Returns a pointer to the connector with ID \param connector_id in this EVSE. + virtual Connector* get_connector(int32_t connector_id) = 0; + + /// \brief Gets the effective Operative/Inoperative status of this EVSE + virtual OperationalStatusEnum get_effective_operational_status() = 0; + + /// \brief Switches the operative status of the EVSE + /// \param new_status The operative status to switch to + /// \param persist True the updated operative state should be persisted + virtual void set_evse_operative_status(OperationalStatusEnum new_status, bool persist) = 0; + + /// \brief Switches the operative status of a connector within this EVSE + /// \param connector_id The ID of the connector + /// \param new_status The operative status to switch to + /// \param persist True the updated operative state should be persisted + virtual void set_connector_operative_status(int32_t connector_id, OperationalStatusEnum new_status, + bool persist) = 0; + + /// \brief Restores the operative status of a connector within this EVSE to the persisted status and recomputes its + /// effective status \param connector_id The ID of the connector + virtual void restore_connector_operative_status(int32_t connector_id) = 0; +}; + /// \brief Represents an EVSE. An EVSE can contain multiple Connector objects, but can only supply energy to one of /// them. -class Evse { +class Evse : public EvseInterface { private: int32_t evse_id; @@ -63,24 +165,10 @@ class Evse { const std::optional reservation_id)>& transaction_meter_value_req, const std::function pause_charging_callback); - /// \brief Returns an OCPP2.0.1 EVSE type - /// \return EVSE get_evse_info(); - /// \brief Returns the number of connectors of this EVSE - /// \return uint32_t get_number_of_connectors(); - /// \brief Opens a new transaction - /// \param transaction_id id of the transaction - /// \param connector_id id of the connector - /// \param timestamp timestamp of the start of the transaction - /// \param meter_start start meter value of the transaction - /// \param id_token id_token with which the transaction was authorized / started - /// \param group_id_token optional group id_token - /// \param reservation optional reservation_id if evse was reserved - /// \param sampled_data_tx_updated_interval Interval between sampling of metering (or other) data, intended to - /// be transmitted via TransactionEventRequest (eventType = Updated) messages void open_transaction(const std::string& transaction_id, const int32_t connector_id, const DateTime& timestamp, const MeterValue& meter_start, const IdToken& id_token, const std::optional& group_id_token, const std::optional reservation_id, @@ -88,73 +176,28 @@ class Evse { const std::chrono::seconds sampled_data_tx_ended_interval, const std::chrono::seconds aligned_data_tx_updated_interval, const std::chrono::seconds aligned_data_tx_ended_interval); - - /// \brief Closes the transaction on this evse by adding the given \p timestamp \p meter_stop and \p reason . - /// \param timestamp - /// \param meter_stop - /// \param reason void close_transaction(const DateTime& timestamp, const MeterValue& meter_stop, const ReasonEnum& reason); - /// \brief Start checking if the max energy on invalid id has exceeded. - /// Will call pause_charging_callback when that happens. void start_checking_max_energy_on_invalid_id(); - /// \brief Indicates if a transaction is active at this evse - /// \return bool has_active_transaction(); - - /// \brief Indicates if a transaction is active at this evse at the given \p connector_id - /// \param connector_id id of the connector of the evse - /// \return bool has_active_transaction(const int32_t connector_id); - - /// \brief Releases the reference of the transaction on this evse void release_transaction(); - - /// \brief Returns a pointer to the EnhancedTransaction of this evse - /// \return pointer to transaction (nullptr if no transaction is active) std::unique_ptr& get_transaction(); - /// \brief Submits the given \p event to the state machine controller of the connector with the given - /// \p connector_id - /// \param connector_id id of the connector of the evse - /// \param event void submit_event(const int32_t connector_id, ConnectorEvent event); - /// \brief Event handler that should be called when a new meter_value for this evse is present - /// \param meter_value void on_meter_value(const MeterValue& meter_value); - - /// \brief Returns the last present meter value for this evse - /// \return MeterValue get_meter_value(); - /// @brief Return the idle meter values for this evse - /// \return MeterValue type MeterValue get_idle_meter_value(); - - /// @brief Clear the idle meter values for this evse void clear_idle_meter_values(); - /// \brief Returns a pointer to the connector with ID \param connector_id in this EVSE. Connector* get_connector(int32_t connector_id); - /// \brief Gets the effective Operative/Inoperative status of this EVSE OperationalStatusEnum get_effective_operational_status(); - - /// \brief Switches the operative status of the EVSE - /// \param new_status The operative status to switch to - /// \param persist True the updated operative state should be persisted void set_evse_operative_status(OperationalStatusEnum new_status, bool persist); - - /// \brief Switches the operative status of a connector within this EVSE - /// \param connector_id The ID of the connector - /// \param new_status The operative status to switch to - /// \param persist True the updated operative state should be persisted void set_connector_operative_status(int32_t connector_id, OperationalStatusEnum new_status, bool persist); - - /// \brief Restores the operative status of a connector within this EVSE to the persisted status and recomputes its - /// effective status \param connector_id The ID of the connector void restore_connector_operative_status(int32_t connector_id); }; diff --git a/include/ocpp/v201/smart_charging.hpp b/include/ocpp/v201/smart_charging.hpp index 605826d74..92ece402d 100644 --- a/include/ocpp/v201/smart_charging.hpp +++ b/include/ocpp/v201/smart_charging.hpp @@ -34,14 +34,14 @@ enum class ProfileValidationResultEnum { /// to calculate the composite schedules class SmartChargingHandler { private: - std::map>& evses; + std::map>& evses; std::shared_ptr database_handler; // cppcheck-suppress unusedStructMember std::vector charging_profiles; public: - explicit SmartChargingHandler(std::map>& evses); + explicit SmartChargingHandler(std::map>& evses); /// /// \brief validates the existence of the given \p evse_id according to the specification @@ -51,7 +51,7 @@ class SmartChargingHandler { /// /// \brief validates the given \p profile according to the specification /// - ProfileValidationResultEnum validate_tx_profile(const ChargingProfile& profile, Evse& evse) const; + ProfileValidationResultEnum validate_tx_profile(const ChargingProfile& profile, EvseInterface& evse) const; /// \brief validates that the given \p profile has valid charging schedules ProfileValidationResultEnum validate_profile_schedules(const ChargingProfile& profile) const; diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 277d5943a..c116d35e3 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -1683,7 +1683,7 @@ std::optional ChargePoint::get_transaction_evseid(const CiString<36>& t return std::nullopt; } -bool ChargePoint::is_evse_reserved_for_other(const std::unique_ptr& evse, const IdToken& id_token, +bool ChargePoint::is_evse_reserved_for_other(const std::unique_ptr& evse, const IdToken& id_token, const std::optional& group_id_token) const { const uint32_t connectors = evse->get_number_of_connectors(); for (uint32_t i = 1; i <= connectors; ++i) { @@ -1702,7 +1702,7 @@ bool ChargePoint::is_evse_reserved_for_other(const std::unique_ptr& evse, return false; } -bool ChargePoint::is_evse_connector_available(const std::unique_ptr& evse) const { +bool ChargePoint::is_evse_connector_available(const std::unique_ptr& evse) const { if (evse->has_active_transaction()) { // If an EV is connected and has no authorization yet then the status is 'Occupied' and the // RemoteStartRequest should still be accepted. So this is the 'occupied' check instead. @@ -1724,7 +1724,7 @@ bool ChargePoint::is_evse_connector_available(const std::unique_ptr& evse) return false; } -void ChargePoint::set_evse_connectors_unavailable(const std::unique_ptr& evse, bool persist) { +void ChargePoint::set_evse_connectors_unavailable(const std::unique_ptr& evse, bool persist) { uint32_t number_of_connectors = evse->get_number_of_connectors(); for (uint32_t i = 1; i <= number_of_connectors; ++i) { @@ -2444,7 +2444,7 @@ void ChargePoint::handle_reset_req(Call call) { } } else if (msg.type == ResetEnum::OnIdle && !evse_no_transactions.empty()) { for (const int32_t evse_id : evse_no_transactions) { - std::unique_ptr& evse = this->evses.at(evse_id); + std::unique_ptr& evse = this->evses.at(evse_id); if (evse) { this->set_evse_connectors_unavailable(evse, false); } else { @@ -2559,7 +2559,7 @@ void ChargePoint::handle_unlock_connector(Call call) { void ChargePoint::handle_trigger_message(Call call) { const TriggerMessageRequest& msg = call.msg; TriggerMessageResponse response; - Evse* evse_ptr = nullptr; + EvseInterface* evse_ptr = nullptr; response.status = TriggerMessageStatusEnum::Rejected; @@ -2654,7 +2654,7 @@ void ChargePoint::handle_trigger_message(Call call) { return; } - auto send_evse_message = [&](std::function send) { + auto send_evse_message = [&](std::function send) { if (evse_ptr != nullptr) { send(msg.evse.value().id, *evse_ptr); } else { @@ -2670,7 +2670,7 @@ void ChargePoint::handle_trigger_message(Call call) { break; case MessageTriggerEnum::MeterValues: { - auto send_meter_value = [&](int32_t evse_id, Evse& evse) { + auto send_meter_value = [&](int32_t evse_id, EvseInterface& evse) { const auto meter_value = get_latest_meter_value_filtered(evse.get_meter_value(), ReadingContextEnum::Trigger, ControllerComponentVariables::AlignedDataMeasurands); @@ -2683,7 +2683,7 @@ void ChargePoint::handle_trigger_message(Call call) { } break; case MessageTriggerEnum::TransactionEvent: { - auto send_transaction = [&](int32_t evse_id, Evse& evse) { + auto send_transaction = [&](int32_t evse_id, EvseInterface& evse) { if (!evse.has_active_transaction()) { return; } @@ -3222,7 +3222,7 @@ bool ChargePoint::are_all_connectors_effectively_inoperative() { return true; } -Evse* ChargePoint::get_evse(int32_t evse_id) { +EvseInterface* ChargePoint::get_evse(int32_t evse_id) { if (evse_id <= 0 || evse_id > this->evses.size()) { std::stringstream err_msg; err_msg << "EVSE ID " << evse_id << " out of bounds"; diff --git a/lib/ocpp/v201/evse.cpp b/lib/ocpp/v201/evse.cpp index 5eedfb969..119ddbba6 100644 --- a/lib/ocpp/v201/evse.cpp +++ b/lib/ocpp/v201/evse.cpp @@ -38,6 +38,9 @@ static float get_normalized_energy_value(SampledValue sampled_value) { return value; } +EvseInterface::~EvseInterface() { +} + Evse::Evse(const int32_t evse_id, const int32_t number_of_connectors, DeviceModel& device_model, std::shared_ptr database_handler, std::shared_ptr component_state_manager, diff --git a/lib/ocpp/v201/smart_charging.cpp b/lib/ocpp/v201/smart_charging.cpp index 236888e7b..5bc1f7b6f 100644 --- a/lib/ocpp/v201/smart_charging.cpp +++ b/lib/ocpp/v201/smart_charging.cpp @@ -13,7 +13,7 @@ using namespace std::chrono; namespace ocpp::v201 { -SmartChargingHandler::SmartChargingHandler(std::map>& evses) : evses(evses) { +SmartChargingHandler::SmartChargingHandler(std::map>& evses) : evses(evses) { } ProfileValidationResultEnum SmartChargingHandler::validate_evse_exists(int32_t evse_id) const { @@ -22,7 +22,7 @@ ProfileValidationResultEnum SmartChargingHandler::validate_evse_exists(int32_t e } ProfileValidationResultEnum SmartChargingHandler::validate_tx_profile(const ChargingProfile& profile, - Evse& evse) const { + EvseInterface& evse) const { if (!profile.transactionId.has_value()) { return ProfileValidationResultEnum::TxProfileMissingTransactionId; } diff --git a/tests/lib/ocpp/v201/mocks/evse_mock.hpp b/tests/lib/ocpp/v201/mocks/evse_mock.hpp new file mode 100644 index 000000000..2ed636283 --- /dev/null +++ b/tests/lib/ocpp/v201/mocks/evse_mock.hpp @@ -0,0 +1,37 @@ +#include "gmock/gmock.h" + +#include "ocpp/v201/evse.hpp" + +namespace ocpp::v201 { +class EvseMock : public EvseInterface { +public: + MOCK_METHOD(EVSE, get_evse_info, ()); + MOCK_METHOD(uint32_t, get_number_of_connectors, ()); + MOCK_METHOD(void, open_transaction, + (const std::string& transaction_id, const int32_t connector_id, const DateTime& timestamp, + const MeterValue& meter_start, const IdToken& id_token, const std::optional& group_id_token, + const std::optional reservation_id, + const std::chrono::seconds sampled_data_tx_updated_interval, + const std::chrono::seconds sampled_data_tx_ended_interval, + const std::chrono::seconds aligned_data_tx_updated_interval, + const std::chrono::seconds aligned_data_tx_ended_interval)); + MOCK_METHOD(void, close_transaction, + (const DateTime& timestamp, const MeterValue& meter_stop, const ReasonEnum& reason)); + MOCK_METHOD(void, start_checking_max_energy_on_invalid_id, ()); + MOCK_METHOD(bool, has_active_transaction, ()); + MOCK_METHOD(bool, has_active_transaction, (int32_t connector_id)); + MOCK_METHOD(void, release_transaction, ()); + MOCK_METHOD(std::unique_ptr&, get_transaction, ()); + MOCK_METHOD(void, submit_event, (const int32_t connector_id, ConnectorEvent event)); + MOCK_METHOD(void, on_meter_value, (const MeterValue& meter_value)); + MOCK_METHOD(MeterValue, get_meter_value, ()); + MOCK_METHOD(MeterValue, get_idle_meter_value, ()); + MOCK_METHOD(void, clear_idle_meter_values, ()); + MOCK_METHOD(Connector*, get_connector, (int32_t connector_id)); + MOCK_METHOD(OperationalStatusEnum, get_effective_operational_status, ()); + MOCK_METHOD(void, set_evse_operative_status, (OperationalStatusEnum new_status, bool persist)); + MOCK_METHOD(void, set_connector_operative_status, + (int32_t connector_id, OperationalStatusEnum new_status, bool persist)); + MOCK_METHOD(void, restore_connector_operative_status, (int32_t connector_id)); +}; +} // namespace ocpp::v201 diff --git a/tests/lib/ocpp/v201/test_smart_charging_handler.cpp b/tests/lib/ocpp/v201/test_smart_charging_handler.cpp index ee4c50812..be5c449d2 100644 --- a/tests/lib/ocpp/v201/test_smart_charging_handler.cpp +++ b/tests/lib/ocpp/v201/test_smart_charging_handler.cpp @@ -179,7 +179,7 @@ class ChargepointTestFixtureV201 : public testing::Test { } // Default values used within the tests - std::map> evses; + std::map> evses; std::shared_ptr database_handler; const int evse_id = 1;