Skip to content

Commit

Permalink
* Added stackLevel to EnhancedChargingSchedulePeriod and implemented …
Browse files Browse the repository at this point in the history
…respective requirements for introduction of the new types

* Introduced charge_point API with function call: get_all_enhanced_composite_schedules in addition to get_all_composite_schedules
* Consumer of libocpp has the option to retrieve EnhancedChargingSchedule and/or \"normal\" ChargingSchedules

Signed-off-by: pietfried <[email protected]>
  • Loading branch information
Pietfried committed Feb 14, 2024
1 parent a1fe09b commit 5d02d92
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 16 deletions.
8 changes: 8 additions & 0 deletions include/ocpp/v16/charge_point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <ocpp/common/evse_security_impl.hpp>
#include <ocpp/common/support_older_cpp_versions.hpp>
#include <ocpp/v16/ocpp_types.hpp>
#include <ocpp/v16/smart_charging.hpp>
#include <ocpp/v16/types.hpp>

#include <ocpp/v16/messages/DataTransfer.hpp>
Expand Down Expand Up @@ -153,6 +154,13 @@ class ChargePoint {
/// \return ChargingSchedules of all connectors
std::map<int32_t, ChargingSchedule> get_all_composite_charging_schedules(const int32_t duration_s);

/// \brief Calculates EnhancedChargingSchedule(s) configured by the CSMS of all connectors from now until now +
/// given \p duration_s . EnhancedChargingSchedules contain EnhancedChargingSchedulePeriod(s) that are enhanced by
/// the stackLevel that was provided for the ChargingProfile
/// \param duration_s
/// \return ChargingSchedules of all connectors
std::map<int32_t, EnhancedChargingSchedule> get_all_enhanced_composite_charging_schedules(const int32_t duration_s);

/// \brief Stores the given \p powermeter values for the given \p connector . This function can be called when a new
/// meter value is present.
/// \param connector
Expand Down
7 changes: 7 additions & 0 deletions include/ocpp/v16/charge_point_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,13 @@ class ChargePointImpl : ocpp::ChargingStationBase {
/// \return ChargingSchedules of all connectors
std::map<int32_t, ChargingSchedule> get_all_composite_charging_schedules(const int32_t duration_s);

/// \brief Calculates EnhancedChargingSchedule(s) configured by the CSMS of all connectors from now until now +
/// given \p duration_s . EnhancedChargingSchedules contain EnhancedChargingSchedulePeriod(s) that are enhanced by
/// the stackLevel that was provided for the ChargingProfile
/// \param duration_s
/// \return ChargingSchedules of all connectors
std::map<int32_t, EnhancedChargingSchedule> get_all_enhanced_composite_charging_schedules(const int32_t duration_s);

/// \brief Stores the given \p powermeter values for the given \p connector . This function can be called when a new
/// meter value is present.
/// \param connector
Expand Down
36 changes: 36 additions & 0 deletions include/ocpp/v16/smart_charging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,36 @@ struct PeriodDateTimePair {
ocpp::DateTime end_time;
};

/// \brief Enhances ChargingSchedulePeriod with stackLevel
struct EnhancedChargingSchedulePeriod {
int32_t startPeriod;

Check notice on line 38 in include/ocpp/v16/smart_charging.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/ocpp/v16/smart_charging.hpp#L38

struct member 'EnhancedChargingSchedulePeriod::startPeriod' is never used.
float limit;

Check notice on line 39 in include/ocpp/v16/smart_charging.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/ocpp/v16/smart_charging.hpp#L39

struct member 'EnhancedChargingSchedulePeriod::limit' is never used.
std::optional<int32_t> numberPhases;
int32_t stackLevel;

Check notice on line 41 in include/ocpp/v16/smart_charging.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/ocpp/v16/smart_charging.hpp#L41

struct member 'EnhancedChargingSchedulePeriod::stackLevel' is never used.
};

/// \brief Conversion from a given EnhancedChargingSchedulePeriod \p k to a given json object \p j
void to_json(json& j, const EnhancedChargingSchedulePeriod& k);

/// \brief Conversion from a given json object \p j to a given EnhancedChargingSchedulePeriod \p k
void from_json(const json& j, EnhancedChargingSchedulePeriod& k);

/// \brief Enhances ChargingSchedule by containing std::vector<EnhancedChargingSchedulePeriods> instead of
/// std::vector<ChargingSchedulePeriod>
struct EnhancedChargingSchedule {
ChargingRateUnit chargingRateUnit;
std::vector<EnhancedChargingSchedulePeriod> chargingSchedulePeriod;

Check notice on line 54 in include/ocpp/v16/smart_charging.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/ocpp/v16/smart_charging.hpp#L54

struct member 'EnhancedChargingSchedule::chargingSchedulePeriod' is never used.
std::optional<int32_t> duration;
std::optional<ocpp::DateTime> startSchedule;
std::optional<float> minChargingRate;
};

/// \brief Conversion from a given EnhancedChargingSchedule \p k to a given json object \p j
void to_json(json& j, const EnhancedChargingSchedule& k);

/// \brief Conversion from a given json object \p j to a given EnhancedChargingSchedule \p k
void from_json(const json& j, EnhancedChargingSchedule& k);

/// \brief This class handles and maintains incoming ChargingProfiles and contains the logic
/// to calculate the composite schedules
class SmartChargingHandler {
Expand Down Expand Up @@ -127,6 +157,12 @@ class SmartChargingHandler {
///
/// \brief Calculates the composite schedule for the given \p valid_profiles and the given \p connector_id .
///
EnhancedChargingSchedule calculate_enhanced_composite_schedule(std::vector<ChargingProfile> valid_profiles,
const ocpp::DateTime& start_time,
const ocpp::DateTime& end_time,
const int connector_id,
std::optional<ChargingRateUnit> charging_rate_unit);

ChargingSchedule calculate_composite_schedule(std::vector<ChargingProfile> valid_profiles,
const ocpp::DateTime& start_time, const ocpp::DateTime& end_time,
const int connector_id,
Expand Down
6 changes: 5 additions & 1 deletion lib/ocpp/v16/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,14 @@ DataTransferResponse ChargePoint::data_transfer(const CiString<255>& vendorId,
}

std::map<int32_t, ChargingSchedule> ChargePoint::get_all_composite_charging_schedules(const int32_t duration_s) {

return this->charge_point->get_all_composite_charging_schedules(duration_s);
}

std::map<int32_t, EnhancedChargingSchedule>
ChargePoint::get_all_enhanced_composite_charging_schedules(const int32_t duration_s) {
return this->charge_point->get_all_enhanced_composite_charging_schedules(duration_s);
}

void ChargePoint::on_meter_values(int32_t connector, const Measurement& measurement) {
this->charge_point->on_meter_values(connector, measurement);
}
Expand Down
20 changes: 20 additions & 0 deletions lib/ocpp/v16/charge_point_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2668,6 +2668,26 @@ std::map<int32_t, ChargingSchedule> ChargePointImpl::get_all_composite_charging_
return charging_schedules;
}

std::map<int32_t, EnhancedChargingSchedule>
ChargePointImpl::get_all_enhanced_composite_charging_schedules(const int32_t duration_s) {

std::map<int32_t, EnhancedChargingSchedule> charging_schedules;

for (int connector_id = 0; connector_id <= this->configuration->getNumberOfConnectors(); connector_id++) {
const auto start_time = ocpp::DateTime();
const auto duration = std::chrono::seconds(duration_s);
const auto end_time = ocpp::DateTime(start_time.to_time_point() + duration);

const auto valid_profiles =
this->smart_charging_handler->get_valid_profiles(start_time, end_time, connector_id);
const auto composite_schedule = this->smart_charging_handler->calculate_enhanced_composite_schedule(
valid_profiles, start_time, end_time, connector_id, ChargingRateUnit::A);
charging_schedules[connector_id] = composite_schedule;
}

return charging_schedules;
}

bool ChargePointImpl::is_pnc_enabled() {
return this->configuration->getSupportedFeatureProfilesSet().count(SupportedFeatureProfiles::PnC) and
this->configuration->getISO15118PnCEnabled();
Expand Down
116 changes: 101 additions & 15 deletions lib/ocpp/v16/smart_charging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,68 @@ using namespace std::chrono;
namespace ocpp {
namespace v16 {

/// \brief Conversion from a given ChargingSchedulePeriod \p k to a given json object \p j
void to_json(json& j, const EnhancedChargingSchedulePeriod& k) {
// the required parts of the message
j = json{{"startPeriod", k.startPeriod}, {"limit", k.limit}, {"stackLevel", k.stackLevel}};
// the optional parts of the message
if (k.numberPhases) {
j["numberPhases"] = k.numberPhases.value();
}
}

/// \brief Conversion from a given json object \p j to a given ChargingSchedulePeriod \p k
void from_json(const json& j, EnhancedChargingSchedulePeriod& k) {
// the required parts of the message
k.startPeriod = j.at("startPeriod");
k.limit = j.at("limit");
k.stackLevel = j.at("stackLevel");

// the optional parts of the message
if (j.contains("numberPhases")) {
k.numberPhases.emplace(j.at("numberPhases"));
}
}

/// \brief Conversion from a given ChargingSchedule \p k to a given json object \p j
void to_json(json& j, const EnhancedChargingSchedule& k) {
// the required parts of the message
j = json{
{"chargingRateUnit", conversions::charging_rate_unit_to_string(k.chargingRateUnit)},
{"chargingSchedulePeriod", k.chargingSchedulePeriod},
};
// the optional parts of the message
if (k.duration) {
j["duration"] = k.duration.value();
}
if (k.startSchedule) {
j["startSchedule"] = k.startSchedule.value().to_rfc3339();
}
if (k.minChargingRate) {
j["minChargingRate"] = k.minChargingRate.value();
}
}

/// \brief Conversion from a given json object \p j to a given ChargingSchedule \p k
void from_json(const json& j, EnhancedChargingSchedule& k) {
// the required parts of the message
k.chargingRateUnit = conversions::string_to_charging_rate_unit(j.at("chargingRateUnit"));
for (auto val : j.at("chargingSchedulePeriod")) {
k.chargingSchedulePeriod.push_back(val);
}

// the optional parts of the message
if (j.contains("duration")) {
k.duration.emplace(j.at("duration"));
}
if (j.contains("startSchedule")) {
k.startSchedule.emplace(j.at("startSchedule").get<std::string>());
}
if (j.contains("minChargingRate")) {
k.minChargingRate.emplace(j.at("minChargingRate"));
}
}

bool validate_schedule(const ChargingSchedule& schedule, const int charging_schedule_max_periods,
const std::vector<ChargingRateUnit>& charging_schedule_allowed_charging_rate_units) {

Expand Down Expand Up @@ -170,24 +232,44 @@ int SmartChargingHandler::get_number_installed_profiles() {
}

ChargingSchedule SmartChargingHandler::calculate_composite_schedule(
std::vector<ChargingProfile> valid_profiles, const ocpp::DateTime& start_time, const ocpp::DateTime& end_time,
const int connector_id, std::optional<ChargingRateUnit> charging_rate_unit) {
const auto enhanced_composite_schedule = this->calculate_enhanced_composite_schedule(
valid_profiles, start_time, end_time, connector_id, charging_rate_unit);
ChargingSchedule composite_schedule;
composite_schedule.chargingRateUnit = enhanced_composite_schedule.chargingRateUnit;
composite_schedule.duration = enhanced_composite_schedule.duration;
composite_schedule.startSchedule = enhanced_composite_schedule.startSchedule;
composite_schedule.minChargingRate = enhanced_composite_schedule.minChargingRate;
for (const auto enhanced_period : enhanced_composite_schedule.chargingSchedulePeriod) {
ChargingSchedulePeriod period;
period.startPeriod = enhanced_period.startPeriod;
period.limit = enhanced_period.limit;
period.numberPhases = enhanced_period.numberPhases;
composite_schedule.chargingSchedulePeriod.push_back(period);
}
return composite_schedule;
}

EnhancedChargingSchedule SmartChargingHandler::calculate_enhanced_composite_schedule(
std::vector<ChargingProfile> valid_profiles, const ocpp::DateTime& start_time, const ocpp::DateTime& end_time,
const int connector_id, std::optional<ChargingRateUnit> charging_rate_unit) {
// return in amps if not given
if (!charging_rate_unit) {
charging_rate_unit.emplace(ChargingRateUnit::A);
}

ChargingSchedule composite_schedule; // the schedule that will be returned
EnhancedChargingSchedule composite_schedule; // the schedule that will be returned
composite_schedule.chargingRateUnit = charging_rate_unit.value();
composite_schedule.duration.emplace(
duration_cast<seconds>(end_time.to_time_point() - start_time.to_time_point()).count());

std::vector<ChargingSchedulePeriod> periods;
std::vector<EnhancedChargingSchedulePeriod> periods;

ocpp::DateTime temp_time(start_time);
ocpp::DateTime last_period_end_time(end_time);
auto current_period_limit = std::numeric_limits<int>::max();
auto lowest_limit_for_this_period = std::numeric_limits<int>::max();
LimitStackLevelPair significant_limit_stack_level_pair = {std::numeric_limits<int>::max(), -1};

// calculate every ChargingSchedulePeriod of result within this while loop
while (duration_cast<seconds>(end_time.to_time_point() - temp_time.to_time_point()).count() > 0) {
Expand Down Expand Up @@ -225,34 +307,38 @@ ChargingSchedule SmartChargingHandler::calculate_composite_schedule(
}
}

int tx_limit;
// if there is a limit with purpose TxProfile it overrules the limit of purpose TxDefaultProfile
if (current_purpose_and_stack_limits.at(ChargingProfilePurposeType::TxProfile).limit !=
std::numeric_limits<int>::max()) {
tx_limit = current_purpose_and_stack_limits.at(ChargingProfilePurposeType::TxProfile).limit;
significant_limit_stack_level_pair =
current_purpose_and_stack_limits.at(ChargingProfilePurposeType::TxProfile);
} else {
tx_limit = current_purpose_and_stack_limits.at(ChargingProfilePurposeType::TxDefaultProfile).limit;
significant_limit_stack_level_pair =
current_purpose_and_stack_limits.at(ChargingProfilePurposeType::TxDefaultProfile);
}

// lowest limit for this period is minimum of ChargePointMaxProfile limit or tx_limit
lowest_limit_for_this_period = std::min(
current_purpose_and_stack_limits.at(ChargingProfilePurposeType::ChargePointMaxProfile).limit, tx_limit);
if (current_purpose_and_stack_limits.at(ChargingProfilePurposeType::ChargePointMaxProfile).limit <
significant_limit_stack_level_pair.limit) {
significant_limit_stack_level_pair =
current_purpose_and_stack_limits.at(ChargingProfilePurposeType::ChargePointMaxProfile);
}

// insert new period to result only if limit changed or period was found
if (lowest_limit_for_this_period != current_period_limit and
lowest_limit_for_this_period != std::numeric_limits<int>::max()) {
if (significant_limit_stack_level_pair.limit != current_period_limit and
significant_limit_stack_level_pair.limit != std::numeric_limits<int>::max()) {

ChargingSchedulePeriod new_period;
EnhancedChargingSchedulePeriod new_period;
const auto start_period =
duration_cast<seconds>(temp_time.to_time_point() - start_time.to_time_point()).count();
new_period.startPeriod = start_period;
new_period.limit =
get_requested_limit(lowest_limit_for_this_period, temp_number_phases, charging_rate_unit.value());
new_period.limit = get_requested_limit(significant_limit_stack_level_pair.limit, temp_number_phases,
charging_rate_unit.value());
new_period.numberPhases = temp_number_phases;
new_period.stackLevel = significant_limit_stack_level_pair.stack_level;
periods.push_back(new_period);

last_period_end_time = temp_period_end_time;
current_period_limit = lowest_limit_for_this_period;
current_period_limit = significant_limit_stack_level_pair.limit;
}
temp_time = this->get_next_temp_time(temp_time, valid_profiles, connector_id);
}
Expand Down

0 comments on commit 5d02d92

Please sign in to comment.