Skip to content

Commit

Permalink
Make EVSE mockable and add mock (#534)
Browse files Browse the repository at this point in the history
* EVSE: Create interface for mockability
* tests: Add Evse mock
---------

Signed-off-by: Christopher Davis <[email protected]>
  • Loading branch information
christopher-davis-afs authored Mar 28, 2024
1 parent 45d7e89 commit ce79223
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 80 deletions.
10 changes: 5 additions & 5 deletions include/ocpp/v201/charge_point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class ChargePoint : ocpp::ChargingStationBase {

private:
// reference to evses
std::map<int32_t, std::unique_ptr<Evse>> evses;
std::map<int32_t, std::unique_ptr<EvseInterface>> evses;

// utility
std::unique_ptr<MessageQueue<v201::MessageType>> message_queue;
Expand Down Expand Up @@ -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>& evse, const IdToken& id_token,
bool is_evse_reserved_for_other(const std::unique_ptr<EvseInterface>& evse, const IdToken& id_token,
const std::optional<IdToken>& group_id_token) const;

///
Expand All @@ -359,15 +359,15 @@ 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>& evse) const;
bool is_evse_connector_available(const std::unique_ptr<EvseInterface>& evse) const;

///
/// \brief Set all connectors of a given evse to unavailable.
/// \param evse The evse.
/// \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>& evse, bool persist);
void set_evse_connectors_unavailable(const std::unique_ptr<EvseInterface>& evse, bool persist);

/// \brief Get the value optional offline flag
/// \return true if the charge point is offline. std::nullopt if it is online;
Expand Down Expand Up @@ -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);
Expand Down
163 changes: 103 additions & 60 deletions include/ocpp/v201/evse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<IdToken>& group_id_token,
const std::optional<int32_t> 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<EnhancedTransaction>& 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;
Expand Down Expand Up @@ -63,98 +165,39 @@ class Evse {
const std::optional<int32_t> reservation_id)>& transaction_meter_value_req,
const std::function<void()> 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<IdToken>& group_id_token, const std::optional<int32_t> 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);

/// \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<EnhancedTransaction>& 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);
};

Expand Down
6 changes: 3 additions & 3 deletions include/ocpp/v201/smart_charging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ enum class ProfileValidationResultEnum {
/// to calculate the composite schedules
class SmartChargingHandler {
private:
std::map<int32_t, std::unique_ptr<Evse>>& evses;
std::map<int32_t, std::unique_ptr<EvseInterface>>& evses;

std::shared_ptr<ocpp::v201::DatabaseHandler> database_handler;
// cppcheck-suppress unusedStructMember
std::vector<ChargingProfile> charging_profiles;

public:
explicit SmartChargingHandler(std::map<int32_t, std::unique_ptr<Evse>>& evses);
explicit SmartChargingHandler(std::map<int32_t, std::unique_ptr<EvseInterface>>& evses);

///
/// \brief validates the existence of the given \p evse_id according to the specification
Expand All @@ -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;
Expand Down
18 changes: 9 additions & 9 deletions lib/ocpp/v201/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1683,7 +1683,7 @@ std::optional<int32_t> ChargePoint::get_transaction_evseid(const CiString<36>& t
return std::nullopt;
}

bool ChargePoint::is_evse_reserved_for_other(const std::unique_ptr<Evse>& evse, const IdToken& id_token,
bool ChargePoint::is_evse_reserved_for_other(const std::unique_ptr<EvseInterface>& evse, const IdToken& id_token,
const std::optional<IdToken>& group_id_token) const {
const uint32_t connectors = evse->get_number_of_connectors();
for (uint32_t i = 1; i <= connectors; ++i) {
Expand All @@ -1702,7 +1702,7 @@ bool ChargePoint::is_evse_reserved_for_other(const std::unique_ptr<Evse>& evse,
return false;
}

bool ChargePoint::is_evse_connector_available(const std::unique_ptr<Evse>& evse) const {
bool ChargePoint::is_evse_connector_available(const std::unique_ptr<EvseInterface>& 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.
Expand All @@ -1724,7 +1724,7 @@ bool ChargePoint::is_evse_connector_available(const std::unique_ptr<Evse>& evse)
return false;
}

void ChargePoint::set_evse_connectors_unavailable(const std::unique_ptr<Evse>& evse, bool persist) {
void ChargePoint::set_evse_connectors_unavailable(const std::unique_ptr<EvseInterface>& evse, bool persist) {
uint32_t number_of_connectors = evse->get_number_of_connectors();

for (uint32_t i = 1; i <= number_of_connectors; ++i) {
Expand Down Expand Up @@ -2444,7 +2444,7 @@ void ChargePoint::handle_reset_req(Call<ResetRequest> call) {
}
} else if (msg.type == ResetEnum::OnIdle && !evse_no_transactions.empty()) {
for (const int32_t evse_id : evse_no_transactions) {
std::unique_ptr<Evse>& evse = this->evses.at(evse_id);
std::unique_ptr<EvseInterface>& evse = this->evses.at(evse_id);
if (evse) {
this->set_evse_connectors_unavailable(evse, false);
} else {
Expand Down Expand Up @@ -2559,7 +2559,7 @@ void ChargePoint::handle_unlock_connector(Call<UnlockConnectorRequest> call) {
void ChargePoint::handle_trigger_message(Call<TriggerMessageRequest> call) {
const TriggerMessageRequest& msg = call.msg;
TriggerMessageResponse response;
Evse* evse_ptr = nullptr;
EvseInterface* evse_ptr = nullptr;

response.status = TriggerMessageStatusEnum::Rejected;

Expand Down Expand Up @@ -2654,7 +2654,7 @@ void ChargePoint::handle_trigger_message(Call<TriggerMessageRequest> call) {
return;
}

auto send_evse_message = [&](std::function<void(int32_t evse_id, Evse & evse)> send) {
auto send_evse_message = [&](std::function<void(int32_t evse_id, EvseInterface & evse)> send) {
if (evse_ptr != nullptr) {
send(msg.evse.value().id, *evse_ptr);
} else {
Expand All @@ -2670,7 +2670,7 @@ void ChargePoint::handle_trigger_message(Call<TriggerMessageRequest> 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);
Expand All @@ -2683,7 +2683,7 @@ void ChargePoint::handle_trigger_message(Call<TriggerMessageRequest> 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;
}
Expand Down Expand Up @@ -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";
Expand Down
3 changes: 3 additions & 0 deletions lib/ocpp/v201/evse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<DatabaseHandler> database_handler,
std::shared_ptr<ComponentStateManagerInterface> component_state_manager,
Expand Down
4 changes: 2 additions & 2 deletions lib/ocpp/v201/smart_charging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ using namespace std::chrono;

namespace ocpp::v201 {

SmartChargingHandler::SmartChargingHandler(std::map<int32_t, std::unique_ptr<Evse>>& evses) : evses(evses) {
SmartChargingHandler::SmartChargingHandler(std::map<int32_t, std::unique_ptr<EvseInterface>>& evses) : evses(evses) {
}

ProfileValidationResultEnum SmartChargingHandler::validate_evse_exists(int32_t evse_id) const {
Expand All @@ -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;
}
Expand Down
Loading

0 comments on commit ce79223

Please sign in to comment.