Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method to send cleared charging limit request #62

Merged
4 changes: 2 additions & 2 deletions doc/ocpp_201_status.md
Original file line number Diff line number Diff line change
Expand Up @@ -1400,8 +1400,8 @@ This document contains the status of which OCPP 2.0.1 numbered functional requir
| ID | Status | Remark |
|-----------|--------|--------|
| K13.FR.01 | | |
| K13.FR.02 | | |
| K13.FR.03 | | |
| K13.FR.02 | | |
| K13.FR.03 | | |

## SmartCharging - External Charging Limit with Local Controller

Expand Down
12 changes: 12 additions & 0 deletions include/ocpp/v201/charge_point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,15 @@ class ChargePointInterface {
double percentage_delta, ChargingLimitSourceEnum source,
std::optional<int32_t> evse_id) = 0;

/// \brief Notifies the ChargePoint that an external limit has been cleared.
/// This shall send a ClearedChargingLimitRequest when called.
// This may send TransactionEventRequest if the \p percentage_delta is greater than our LimitChangeSignificance.
/// \param percentage_delta the percent changed from the existing limits
/// \param source the source of the external limit
/// \param evse_id if provided checks for transactions on the provided evse
virtual void on_external_limit_cleared(double percentage_delta, ChargingLimitSourceEnum source,
std::optional<int32_t> evse_id) = 0;

/// \brief Data transfer mechanism initiated by charger
/// \param vendorId
/// \param messageId
Expand Down Expand Up @@ -891,6 +900,9 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa
double percentage_delta, ChargingLimitSourceEnum source,
std::optional<int32_t> evse_id) override;

void on_external_limit_cleared(double percentage_delta, ChargingLimitSourceEnum source,
std::optional<int32_t> evse_id) override;

std::optional<DataTransferResponse> data_transfer_req(const CiString<255>& vendorId,
const std::optional<CiString<50>>& messageId,
const std::optional<json>& data) override;
Expand Down
18 changes: 17 additions & 1 deletion include/ocpp/v201/smart_charging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
#define OCPP_V201_SMART_CHARGING_HPP

#include "ocpp/v201/comparators.hpp"
#include "ocpp/v201/messages/ClearedChargingLimit.hpp"
#include "ocpp/v201/messages/TransactionEvent.hpp"
#include <limits>
#include <memory>
#include <optional>
#include <utility>
#include <variant>

#include <ocpp/v201/database_handler.hpp>
Expand Down Expand Up @@ -121,6 +125,10 @@ class SmartChargingHandlerInterface {
handle_external_limits_changed(const std::variant<ConstantChargingLimit, ChargingSchedule>& limit,
double percentage_delta, ChargingLimitSourceEnum source,
std::optional<int32_t> evse_id) const = 0;

virtual std::optional<std::pair<ClearedChargingLimitRequest, std::vector<TransactionEventRequest>>>
handle_external_limit_cleared(double percentage_delta, ChargingLimitSourceEnum source,
std::optional<int32_t> evse_id) const = 0;
};

/// \brief This class handles and maintains incoming ChargingProfiles and contains the logic
Expand Down Expand Up @@ -194,14 +202,18 @@ class SmartChargingHandler : public SmartChargingHandlerInterface {
std::optional<ChargingRateUnitEnum> charging_rate_unit) override;

///
/// \brief Determines whether or not we should notify the CSMS of a change to our limits
/// \brief Determines whether or not we should notify the CSMS of a cleared external limit
/// based on \p percentage_delta and builds the notification.
///
std::optional<NotifyChargingLimitRequest>
handle_external_limits_changed(const std::variant<ConstantChargingLimit, ChargingSchedule>& limit,
double percentage_delta, ChargingLimitSourceEnum source,
std::optional<int32_t> evse_id) const override;

virtual std::optional<std::pair<ClearedChargingLimitRequest, std::vector<TransactionEventRequest>>>
handle_external_limit_cleared(double percentage_delta, ChargingLimitSourceEnum source,
std::optional<int32_t> evse_id) const override;

protected:
///
/// \brief validates the existence of the given \p evse_id according to the specification
Expand Down Expand Up @@ -261,6 +273,10 @@ class SmartChargingHandler : public SmartChargingHandlerInterface {
std::vector<ChargingProfile> get_valid_profiles_for_evse(int32_t evse_id);
void conform_validity_periods(ChargingProfile& profile) const;
CurrentPhaseType get_current_phase_type(const std::optional<EvseInterface*> evse_opt) const;
TransactionEventRequest create_transaction_event_request(std::unique_ptr<EnhancedTransaction>& tx) const;
bool process_evses_with_active_transactions(const bool limit_change_significance_exceeded,
std::vector<TransactionEventRequest>& transaction_event_requests,
std::optional<int32_t> evse_id) const;
};

} // namespace ocpp::v201
Expand Down
22 changes: 22 additions & 0 deletions lib/ocpp/v201/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,28 @@ void ChargePoint::on_external_limits_changed(const std::variant<ConstantCharging
}
}

void ChargePoint::on_external_limit_cleared(double percentage_delta, ChargingLimitSourceEnum source,
std::optional<int32_t> evse_id) {
auto request = this->smart_charging_handler->handle_external_limit_cleared(percentage_delta, source, evse_id);

if (request.has_value()) {

auto [cleared_charging_limit_request, transaction_event_requests] = request.value();

ocpp::Call<ClearedChargingLimitRequest> call(cleared_charging_limit_request,
this->message_queue->createMessageId());
this->send<ClearedChargingLimitRequest>(call);

if (transaction_event_requests.size() > 0) {
for (auto transaction_event_request : transaction_event_requests) {
ocpp::Call<TransactionEventRequest> call(transaction_event_request,
this->message_queue->createMessageId());
this->send<TransactionEventRequest>(call);
}
}
}
}

bool ChargePoint::send(CallError call_error) {
this->message_queue->push(call_error);
return true;
Expand Down
79 changes: 79 additions & 0 deletions lib/ocpp/v201/smart_charging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,22 @@
#include "ocpp/v201/ctrlr_component_variables.hpp"
#include "ocpp/v201/device_model.hpp"
#include "ocpp/v201/evse.hpp"
#include "ocpp/v201/messages/ClearedChargingLimit.hpp"
#include "ocpp/v201/messages/NotifyChargingLimit.hpp"
#include "ocpp/v201/messages/SetChargingProfile.hpp"
#include "ocpp/v201/messages/TransactionEvent.hpp"
#include "ocpp/v201/ocpp_enums.hpp"
#include "ocpp/v201/ocpp_types.hpp"
#include "ocpp/v201/profile.hpp"
#include "ocpp/v201/transaction.hpp"
#include "ocpp/v201/utils.hpp"
#include <algorithm>
#include <cstring>
#include <iterator>
#include <ocpp/common/constants.hpp>
#include <ocpp/v201/smart_charging.hpp>
#include <optional>
#include <utility>
#include <variant>

using namespace std::chrono;
Expand Down Expand Up @@ -804,4 +808,79 @@ SmartChargingHandler::handle_external_limits_changed(const std::variant<Constant
return request;
}

std::optional<std::pair<ClearedChargingLimitRequest, std::vector<TransactionEventRequest>>>
SmartChargingHandler::handle_external_limit_cleared(double percentage_delta, ChargingLimitSourceEnum source,
std::optional<int32_t> evse_id) const {

std::pair<ClearedChargingLimitRequest, std::vector<TransactionEventRequest>> pair;
std::vector<TransactionEventRequest> transaction_event_requests = {};

const auto& limit_change_cv = ControllerComponentVariables::LimitChangeSignificance;
const float limit_change_significance = this->device_model->get_value<double>(limit_change_cv);

const bool limit_change_significance_exceeded = percentage_delta > limit_change_significance;

bool has_transaction = this->process_evses_with_active_transactions(limit_change_significance_exceeded,
transaction_event_requests, evse_id);

std::optional<std::pair<ClearedChargingLimitRequest, std::vector<TransactionEventRequest>>> request;

if (has_transaction) {
// K13.FR.02
ClearedChargingLimitRequest cleared_charging_limit_request = {};

if (evse_id.has_value()) {
cleared_charging_limit_request.evseId = evse_id.value();
}

// There is not restriction on this source in the spec.
// K12.FR04 requires it not to be CSO. Not enforced here.
cleared_charging_limit_request.chargingLimitSource = source;
pair.first = cleared_charging_limit_request;

pair.second = transaction_event_requests;
request.emplace(pair);
}

return request;
}

TransactionEventRequest
SmartChargingHandler::create_transaction_event_request(std::unique_ptr<EnhancedTransaction>& tx) const {
auto tmp = TransactionEventRequest{};
tmp.eventType = TransactionEventEnum::Updated;
tmp.timestamp = ocpp::DateTime();
tmp.triggerReason = TriggerReasonEnum::ChargingRateChanged;

tmp.seqNo = tx->get_seq_no();
tmp.transactionInfo = tx->get_transaction();

return tmp;
}

bool SmartChargingHandler::process_evses_with_active_transactions(
const bool limit_change_significance_exceeded, std::vector<TransactionEventRequest>& transaction_event_requests,
std::optional<int32_t> evse_id) const {
bool has_transaction = false;
if (evse_id.has_value()) {
auto evse = &this->evse_manager.get_evse(evse_id.value());
if (evse->has_active_transaction()) {
has_transaction = true;
// K13.FR.03
if (limit_change_significance_exceeded) {
auto& tx = evse->get_transaction();
transaction_event_requests.push_back(this->create_transaction_event_request(tx));
}
}
} else {
for (auto& evse : this->evse_manager) {
has_transaction = (process_evses_with_active_transactions(limit_change_significance_exceeded,
transaction_event_requests, evse.get_id()) ||
has_transaction);
}
}

return has_transaction;
}

} // namespace ocpp::v201
5 changes: 5 additions & 0 deletions tests/lib/ocpp/v201/mocks/smart_charging_handler_mock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <variant>
#include <vector>

#include "ocpp/v201/messages/ClearedChargingLimit.hpp"
#include "ocpp/v201/messages/SetChargingProfile.hpp"
#include "ocpp/v201/smart_charging.hpp"

Expand All @@ -32,5 +33,9 @@ class SmartChargingHandlerMock : public SmartChargingHandlerInterface {
(const ChargingLimitVariant& limit, double percentage_delta, ChargingLimitSourceEnum source,
std::optional<int32_t> evse_id),
(const, override));
MOCK_METHOD((std::optional<std::pair<ClearedChargingLimitRequest, std::vector<TransactionEventRequest>>>),
handle_external_limit_cleared,
(double percentage_delta, ChargingLimitSourceEnum source, std::optional<int32_t> evse_id),
(const, override));
};
} // namespace ocpp::v201
20 changes: 20 additions & 0 deletions tests/lib/ocpp/v201/test_charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <memory>
#include <optional>
#include <variant>

static const int DEFAULT_EVSE_ID = 1;
Expand Down Expand Up @@ -970,4 +971,23 @@ TEST_F(ChargepointTestFixtureV201, K12_OnExternalLimitsChanged_CallsHandler) {
charge_point->on_external_limits_changed(new_limit, deltaChanged, source, DEFAULT_EVSE_ID);
}

TEST_F(ChargepointTestFixtureV201, K13_OnExternalLimitsCleared_CallsHandler) {
const auto& limit_change_cv = ControllerComponentVariables::LimitChangeSignificance;
device_model->set_value(limit_change_cv.component, limit_change_cv.variable.value(), AttributeEnum::Actual, "0.1",
"test");

ConstantChargingLimit limit = {
.limit = 100.0,
.charging_rate_unit = ChargingRateUnitEnum::A,
};

std::optional<int32_t> evse_id;
double deltaChanged = 0.2;
auto source = ChargingLimitSourceEnum::Other;

EXPECT_CALL(*smart_charging_handler, handle_external_limit_cleared(deltaChanged, source, evse_id));

charge_point->on_external_limit_cleared(deltaChanged, source, std::nullopt);
}

} // namespace ocpp::v201
Loading
Loading