From 0e811455856e89f2718a0922d486de05ef1b7ce7 Mon Sep 17 00:00:00 2001 From: pietfried Date: Fri, 2 Feb 2024 19:00:28 +0100 Subject: [PATCH] Added stackLevel to EnhancedChargingSchedulePeriod and implemented respective requirements for introduction of the new types Signed-off-by: pietfried --- include/ocpp/v16/charge_point.hpp | 3 +- include/ocpp/v16/charge_point_impl.hpp | 2 +- include/ocpp/v16/smart_charging.hpp | 33 +++++++ lib/ocpp/v16/charge_point.cpp | 3 +- lib/ocpp/v16/charge_point_impl.cpp | 7 +- lib/ocpp/v16/smart_charging.cpp | 120 +++++++++++++++++++++---- 6 files changed, 147 insertions(+), 21 deletions(-) diff --git a/include/ocpp/v16/charge_point.hpp b/include/ocpp/v16/charge_point.hpp index 2e435f105d..2e5b5f9742 100644 --- a/include/ocpp/v16/charge_point.hpp +++ b/include/ocpp/v16/charge_point.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -151,7 +152,7 @@ class ChargePoint { /// duration_s /// \param duration_s /// \return ChargingSchedules of all connectors - std::map get_all_composite_charging_schedules(const int32_t duration_s); + std::map get_all_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. diff --git a/include/ocpp/v16/charge_point_impl.hpp b/include/ocpp/v16/charge_point_impl.hpp index f2dd6ca0da..05942f0ee6 100644 --- a/include/ocpp/v16/charge_point_impl.hpp +++ b/include/ocpp/v16/charge_point_impl.hpp @@ -451,7 +451,7 @@ class ChargePointImpl : ocpp::ChargingStationBase { /// duration_s /// \param duration_s /// \return ChargingSchedules of all connectors - std::map get_all_composite_charging_schedules(const int32_t duration_s); + std::map get_all_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. diff --git a/include/ocpp/v16/smart_charging.hpp b/include/ocpp/v16/smart_charging.hpp index 5a0113301b..94ea9f0e8b 100644 --- a/include/ocpp/v16/smart_charging.hpp +++ b/include/ocpp/v16/smart_charging.hpp @@ -33,6 +33,33 @@ struct PeriodDateTimePair { ocpp::DateTime end_time; }; +struct EnhancedChargingSchedulePeriod { + int32_t startPeriod; + float limit; + std::optional numberPhases; + int32_t stackLevel; +}; + +/// \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); + +struct EnhancedChargingSchedule { + ChargingRateUnit chargingRateUnit; + std::vector chargingSchedulePeriod; + std::optional duration; + std::optional startSchedule; + std::optional 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 { @@ -127,6 +154,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 valid_profiles, + const ocpp::DateTime& start_time, + const ocpp::DateTime& end_time, + const int connector_id, + std::optional charging_rate_unit); + ChargingSchedule calculate_composite_schedule(std::vector valid_profiles, const ocpp::DateTime& start_time, const ocpp::DateTime& end_time, const int connector_id, diff --git a/lib/ocpp/v16/charge_point.cpp b/lib/ocpp/v16/charge_point.cpp index 363fd99aeb..c057d6dcdd 100644 --- a/lib/ocpp/v16/charge_point.cpp +++ b/lib/ocpp/v16/charge_point.cpp @@ -70,7 +70,8 @@ DataTransferResponse ChargePoint::data_transfer(const CiString<255>& vendorId, return this->charge_point->data_transfer(vendorId, messageId, data); } -std::map ChargePoint::get_all_composite_charging_schedules(const int32_t duration_s) { +std::map +ChargePoint::get_all_composite_charging_schedules(const int32_t duration_s) { return this->charge_point->get_all_composite_charging_schedules(duration_s); } diff --git a/lib/ocpp/v16/charge_point_impl.cpp b/lib/ocpp/v16/charge_point_impl.cpp index e44d2b7758..f99db0ae6d 100644 --- a/lib/ocpp/v16/charge_point_impl.cpp +++ b/lib/ocpp/v16/charge_point_impl.cpp @@ -2648,9 +2648,10 @@ IdTagInfo ChargePointImpl::authorize_id_token(CiString<20> idTag) { return id_tag_info; } -std::map ChargePointImpl::get_all_composite_charging_schedules(const int32_t duration_s) { +std::map +ChargePointImpl::get_all_composite_charging_schedules(const int32_t duration_s) { - std::map charging_schedules; + std::map charging_schedules; for (int connector_id = 0; connector_id <= this->configuration->getNumberOfConnectors(); connector_id++) { const auto start_time = ocpp::DateTime(); @@ -2659,7 +2660,7 @@ std::map ChargePointImpl::get_all_composite_charging_ 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_composite_schedule( + 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; } diff --git a/lib/ocpp/v16/smart_charging.cpp b/lib/ocpp/v16/smart_charging.cpp index 083d1dafae..236c3ba078 100644 --- a/lib/ocpp/v16/smart_charging.cpp +++ b/lib/ocpp/v16/smart_charging.cpp @@ -8,6 +8,72 @@ 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()); + } + 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& charging_schedule_allowed_charging_rate_units) { @@ -170,6 +236,26 @@ int SmartChargingHandler::get_number_installed_profiles() { } ChargingSchedule SmartChargingHandler::calculate_composite_schedule( + std::vector valid_profiles, const ocpp::DateTime& start_time, const ocpp::DateTime& end_time, + const int connector_id, std::optional 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 valid_profiles, const ocpp::DateTime& start_time, const ocpp::DateTime& end_time, const int connector_id, std::optional charging_rate_unit) { // return in amps if not given @@ -177,17 +263,17 @@ ChargingSchedule SmartChargingHandler::calculate_composite_schedule( 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(end_time.to_time_point() - start_time.to_time_point()).count()); - std::vector periods; + std::vector periods; ocpp::DateTime temp_time(start_time); ocpp::DateTime last_period_end_time(end_time); auto current_period_limit = std::numeric_limits::max(); - auto lowest_limit_for_this_period = std::numeric_limits::max(); + LimitStackLevelPair significant_limit_stack_level_pair = {std::numeric_limits::max(), -1}; // calculate every ChargingSchedulePeriod of result within this while loop while (duration_cast(end_time.to_time_point() - temp_time.to_time_point()).count() > 0) { @@ -225,34 +311,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::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::max()) { + if (significant_limit_stack_level_pair.limit != current_period_limit and + significant_limit_stack_level_pair.limit != std::numeric_limits::max()) { - ChargingSchedulePeriod new_period; + EnhancedChargingSchedulePeriod new_period; const auto start_period = duration_cast(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); }