Skip to content

Commit

Permalink
Added K01.FR06 duplication check for dates
Browse files Browse the repository at this point in the history
Signed-off-by: Coury Richards <[email protected]>
  • Loading branch information
couryrr-afs authored and christopher-davis-afs committed Apr 25, 2024
1 parent 8f8a3ae commit 0949570
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 73 deletions.
2 changes: 1 addition & 1 deletion doc/ocpp_201_status.md
Original file line number Diff line number Diff line change
Expand Up @@ -1144,7 +1144,7 @@ This document contains the status of which OCPP 2.0.1 numbered requirements have
| K01.FR.03 | | |
| K01.FR.04 | :white_check_mark: | |
| K01.FR.05 | | |
| K01.FR.06 | | |
| K01.FR.06 | :white_check_mark: | |
| K01.FR.07 | | |
| K01.FR.08 | | |
| K01.FR.09 | | |
Expand Down
12 changes: 10 additions & 2 deletions include/ocpp/v201/smart_charging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ enum class ProfileValidationResultEnum {
ChargingSchedulePeriodInvalidPhaseToUse,
ChargingSchedulePeriodUnsupportedNumberPhases,
ChargingSchedulePeriodExtraneousPhaseValues,
DuplicateTxDefaultProfileFound
DuplicateTxDefaultProfileFound,
DuplicateProfileValidityPeriod
};

namespace conversions {
Expand Down Expand Up @@ -65,7 +66,7 @@ class SmartChargingHandler {
///
/// \brief validates the given \p profile and associated \p evse_id according to the specification
///
ProfileValidationResultEnum validate_tx_default_profile(const ChargingProfile& profile, int32_t evse_id) const;
ProfileValidationResultEnum validate_tx_default_profile(ChargingProfile& profile, int32_t evse_id) const;

///
/// \brief validates the given \p profile according to the specification
Expand All @@ -83,9 +84,16 @@ class SmartChargingHandler {
///
void add_profile(int32_t evse_id, ChargingProfile& profile);

///
/// \brief Checks a given \p profile and associated \p evse_id validFrom and validTo range
/// This method assumes that the existing profile will have dates set for validFrom and validTo
///
bool is_overlapping_validity_period(int evse_id, ChargingProfile& profile) const;

private:
std::vector<ChargingProfile> get_evse_specific_tx_default_profiles() const;
std::vector<ChargingProfile> get_station_wide_tx_default_profiles() const;
void conform_validity_periods(ChargingProfile& profile) const;
};

} // namespace ocpp::v201
Expand Down
53 changes: 51 additions & 2 deletions lib/ocpp/v201/smart_charging.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest

#include "date/tz.h"
#include "everest/logging.hpp"
#include "ocpp/common/types.hpp"
#include "ocpp/v201/enums.hpp"
#include "ocpp/v201/evse.hpp"
#include "ocpp/v201/ocpp_types.hpp"
#include "ocpp/v201/transaction.hpp"
#include <algorithm>
#include <iterator>
#include <memory>
#include <ocpp/v201/smart_charging.hpp>
Expand Down Expand Up @@ -71,9 +73,14 @@ ProfileValidationResultEnum SmartChargingHandler::validate_evse_exists(int32_t e
: ProfileValidationResultEnum::Valid;
}

ProfileValidationResultEnum SmartChargingHandler::validate_tx_default_profile(const ChargingProfile& profile,
ProfileValidationResultEnum SmartChargingHandler::validate_tx_default_profile(ChargingProfile& profile,
int32_t evse_id) const {
auto profiles = evse_id == 0 ? get_evse_specific_tx_default_profiles() : get_station_wide_tx_default_profiles();

if (is_overlapping_validity_period(evse_id, profile)) {
return ProfileValidationResultEnum::DuplicateProfileValidityPeriod;
}

for (auto candidate : profiles) {
if (candidate.stackLevel == profile.stackLevel) {
if (candidate.id != profile.id) {
Expand Down Expand Up @@ -112,6 +119,7 @@ ProfileValidationResultEnum SmartChargingHandler::validate_tx_profile(const Char
candidateProfile.stackLevel == profile.stackLevel;
});
};

if (std::any_of(charging_profiles.begin(), charging_profiles.end(), conflicts_with)) {
return ProfileValidationResultEnum::TxProfileConflictingStackLevel;
}
Expand All @@ -125,10 +133,12 @@ ProfileValidationResultEnum SmartChargingHandler::validate_tx_profile(const Char
* - K01.FR.43
* - K01.FR.48
*/

ProfileValidationResultEnum
SmartChargingHandler::validate_profile_schedules(ChargingProfile& profile,
std::optional<EvseInterface*> evse_opt) const {
for (ChargingSchedule& schedule : profile.chargingSchedule) {
for (auto& schedule : profile.chargingSchedule) {

// A schedule must have at least one chargingSchedulePeriod
if (schedule.chargingSchedulePeriod.empty()) {
return ProfileValidationResultEnum::ChargingProfileNoChargingSchedulePeriods;
Expand Down Expand Up @@ -224,4 +234,43 @@ std::vector<ChargingProfile> SmartChargingHandler::get_station_wide_tx_default_p
return station_wide_tx_default_profiles;
}

bool SmartChargingHandler::is_overlapping_validity_period(int candidate_evse_id,
ChargingProfile& candidate_profile) const {

conform_validity_periods(candidate_profile);

if (candidate_profile.chargingProfilePurpose == ChargingProfilePurposeEnum::TxProfile) {
// This only applies to non TxProfile types.
return false;
}

auto conflicts_with = [candidate_evse_id, &candidate_profile](
const std::pair<int32_t, std::vector<ChargingProfile>>& existing_profiles) {
auto existing_evse_id = existing_profiles.first;
if (existing_evse_id == candidate_evse_id) {
return std::any_of(existing_profiles.second.begin(), existing_profiles.second.end(),
[&candidate_profile](const ChargingProfile& existing_profile) {
if (existing_profile.stackLevel == candidate_profile.stackLevel &&
existing_profile.chargingProfileKind == candidate_profile.chargingProfileKind &&
existing_profile.id != candidate_profile.id) {

return candidate_profile.validFrom <= existing_profile.validTo &&
candidate_profile.validTo >= existing_profile.validFrom; // reject
}
return false;
});
}
return false;
};

return std::any_of(charging_profiles.begin(), charging_profiles.end(), conflicts_with);
}

void SmartChargingHandler::conform_validity_periods(ChargingProfile& profile) const {
profile.validFrom =
profile.validFrom.has_value() ? profile.validFrom.value() : ocpp::DateTime(date::utc_clock::now());
profile.validTo =
profile.validTo.has_value() ? profile.validTo.value() : ocpp::DateTime(date::utc_clock::time_point::max());
}

} // namespace ocpp::v201
Loading

0 comments on commit 0949570

Please sign in to comment.