Skip to content

Commit

Permalink
Smart Charging: Expand add profile capabilities (#682)
Browse files Browse the repository at this point in the history
* Add TxDefaultProfile to either EVSE or entire station.

Signed-off-by: Gianfranco Berardi <[email protected]>

* Return SetChargingProfileResponse when adding profile.

Signed-off-by: Gianfranco Berardi <[email protected]>

* Check boxes for K01.FR.14 and K01.FR.15.

Signed-off-by: Gianfranco Berardi <[email protected]>

* smart_charging: Handle profile updates per K01.FR.05

Signed-off-by: Christopher Davis <[email protected]>

* Test Callbacks validity check.

Signed-off-by: Gianfranco Berardi <[email protected]>

* Ensure each function is required in validity check.

Signed-off-by: Gianfranco Berardi <[email protected]>

* Add security_event_callback to validity check.

Signed-off-by: Gianfranco Berardi <[email protected]>

* Add set_charging_profiles_callback to validity check.

Signed-off-by: Gianfranco Berardi <[email protected]>

* Fix lint issue.

Signed-off-by: Gianfranco Berardi <[email protected]>

* Fix lint issue.

Signed-off-by: Gianfranco Berardi <[email protected]>

* Add tests for optional functions being validated.

Signed-off-by: Gianfranco Berardi <[email protected]>

* Fix lint.

Signed-off-by: Gianfranco Berardi <[email protected]>

* Specify requirement in test names.

Signed-off-by: Gianfranco Berardi <[email protected]>

* Update callback validity check with all optional function objects.

Signed-off-by: Gianfranco Berardi <[email protected]>

* charge_point: Fix incorrect type in tests

Signed-off-by: Christopher Davis <[email protected]>

* added comment for K01 functional requirement

Signed-off-by: Coury Richards <[email protected]>

* commented reason for test case

Signed-off-by: Coury Richards <[email protected]>

* smart_charging: Clarify test names for add profile

Clarify where the existing profiles in the FR05
tests are.

Signed-off-by: Christopher Davis <[email protected]>

* smart_charging: Ensure profile ids are unique across destinations

Signed-off-by: Christopher Davis <[email protected]>

* smart_charging: Adjust get_station_wide_profiles

We don't need a try-catch actually

Signed-off-by: Christopher Davis <[email protected]>

* smart_charging: Clean up redundant imports

Signed-off-by: Christopher Davis <[email protected]>

* corrected test name

Signed-off-by: Coury Richards <[email protected]>

---------

Signed-off-by: Gianfranco Berardi <[email protected]>
Signed-off-by: Christopher Davis <[email protected]>
Signed-off-by: Coury Richards <[email protected]>
Co-authored-by: Gianfranco Berardi <[email protected]>
Co-authored-by: Christopher Davis <[email protected]>
  • Loading branch information
3 people authored Jul 24, 2024
1 parent d6064d7 commit 01f064f
Show file tree
Hide file tree
Showing 8 changed files with 661 additions and 18 deletions.
6 changes: 3 additions & 3 deletions doc/ocpp_201_status.md
Original file line number Diff line number Diff line change
Expand Up @@ -1234,7 +1234,7 @@ This document contains the status of which OCPP 2.0.1 numbered functional requir
| K01.FR.02 | 🌐 | |
| K01.FR.03 | 🌐 💂 | `TxProfile`s without `transactionId`s are rejected. |
| K01.FR.04 || |
| K01.FR.05 | | |
| K01.FR.05 | | |
| K01.FR.06 | 🌐 | |
| K01.FR.07 | ⛽️ | Notified through the `signal_set_charging_profiles` callback. |
| K01.FR.08 | 🌐 | `TxDefaultProfile`s are supported. |
Expand All @@ -1243,8 +1243,8 @@ This document contains the status of which OCPP 2.0.1 numbered functional requir
| K01.FR.11 | | |
| K01.FR.12 | | |
| K01.FR.13 | | |
| K01.FR.14 | | |
| K01.FR.15 | | |
| K01.FR.14 | | |
| K01.FR.15 | | |
| K01.FR.16 | | |
| K01.FR.17 | | |
| K01.FR.19 | | |
Expand Down
5 changes: 4 additions & 1 deletion include/ocpp/v201/charge_point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class UnexpectedMessageTypeFromCSMS : public std::runtime_error {

struct Callbacks {
///\brief Function to check if the callback struct is completely filled. All std::functions should hold a function,
/// all std::optional<std::functions> should either be emtpy or hold a function.
/// all std::optional<std::functions> should either be empty or hold a function.
///
///\retval false if any of the normal callbacks are nullptr or any of the optional ones are filled with a nullptr
/// true otherwise
Expand Down Expand Up @@ -152,6 +152,9 @@ struct Callbacks {
std::function<void(const CiString<50>& event_type, const std::optional<CiString<255>>& tech_info)>
security_event_callback;

/// \brief Callback for indicating when a charging profile is received and was accepted.
std::function<void()> set_charging_profiles_callback;

/// \brief Callback for when a bootnotification response is received
std::optional<std::function<void(const ocpp::v201::BootNotificationResponse& boot_notification_response)>>
boot_notification_callback;
Expand Down
17 changes: 15 additions & 2 deletions include/ocpp/v201/smart_charging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "ocpp/v201/device_model.hpp"
#include "ocpp/v201/enums.hpp"
#include "ocpp/v201/messages/SetChargingProfile.hpp"
#include <limits>

#include <memory>
Expand All @@ -21,6 +22,7 @@ const int DEFAULT_AND_MAX_NUMBER_PHASES = 3;
enum class ProfileValidationResultEnum {
Valid,
EvseDoesNotExist,
ExistingChargingStationExternalConstraints,
InvalidProfileType,
TxProfileMissingTransactionId,
TxProfileEvseIdNotGreaterThanZero,
Expand Down Expand Up @@ -61,7 +63,6 @@ class SmartChargingHandler {
std::shared_ptr<ocpp::v201::DatabaseHandler> database_handler;
// cppcheck-suppress unusedStructMember
std::map<int32_t, std::vector<ChargingProfile>> charging_profiles;
std::vector<ChargingProfile> station_wide_charging_profiles;

public:
SmartChargingHandler(EvseManagerInterface& evse_manager, std::shared_ptr<DeviceModel>& device_model);
Expand All @@ -76,7 +77,12 @@ class SmartChargingHandler {
///
/// \brief Adds a given \p profile and associated \p evse_id to our stored list of profiles
///
void add_profile(int32_t evse_id, ChargingProfile& profile);
SetChargingProfileResponse add_profile(int32_t evse_id, ChargingProfile& profile);

///
/// \brief Retrieves existing profiles on system.
///
std::vector<ChargingProfile> get_profiles() const;

protected:
///
Expand Down Expand Up @@ -113,7 +119,14 @@ class SmartChargingHandler {
///
bool is_overlapping_validity_period(int evse_id, const ChargingProfile& profile) const;

///
/// \brief Checks a given \p profile does not have an id that conflicts with an existing profile
/// of type ChargingStationExternalConstraints
///
ProfileValidationResultEnum verify_no_conflicting_external_constraints_id(const ChargingProfile& profile) const;

private:
std::vector<ChargingProfile> get_station_wide_profiles() const;
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;
Expand Down
19 changes: 17 additions & 2 deletions lib/ocpp/v201/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,30 @@ bool Callbacks::all_callbacks_valid() const {
this->connector_effective_operative_status_changed_callback != nullptr and
this->get_log_request_callback != nullptr and this->unlock_connector_callback != nullptr and
this->remote_start_transaction_callback != nullptr and this->is_reservation_for_token_callback != nullptr and
this->update_firmware_request_callback != nullptr and
this->update_firmware_request_callback != nullptr and this->security_event_callback != nullptr and
this->set_charging_profiles_callback != nullptr and
(!this->variable_changed_callback.has_value() or this->variable_changed_callback.value() != nullptr) and
(!this->validate_network_profile_callback.has_value() or
this->validate_network_profile_callback.value() != nullptr) and
(!this->configure_network_connection_profile_callback.has_value() or
this->configure_network_connection_profile_callback.value() != nullptr) and
(!this->time_sync_callback.has_value() or this->time_sync_callback.value() != nullptr) and
(!this->boot_notification_callback.has_value() or this->boot_notification_callback.value() != nullptr) and
(!this->ocpp_messages_callback.has_value() or this->ocpp_messages_callback.value() != nullptr);
(!this->ocpp_messages_callback.has_value() or this->ocpp_messages_callback.value() != nullptr) and
(!this->cs_effective_operative_status_changed_callback.has_value() or
this->cs_effective_operative_status_changed_callback.value() != nullptr) and
(!this->evse_effective_operative_status_changed_callback.has_value() or
this->evse_effective_operative_status_changed_callback.value() != nullptr) and
(!this->get_customer_information_callback.has_value() or
this->get_customer_information_callback.value() != nullptr) and
(!this->clear_customer_information_callback.has_value() or
this->clear_customer_information_callback.value() != nullptr) and
(!this->all_connectors_unavailable_callback.has_value() or
this->all_connectors_unavailable_callback.value() != nullptr) and
(!this->data_transfer_callback.has_value() or this->data_transfer_callback.value() != nullptr) and
(!this->transaction_event_callback.has_value() or this->transaction_event_callback.value() != nullptr) and
(!this->transaction_event_response_callback.has_value() or
this->transaction_event_response_callback.value() != nullptr);
}

ChargePoint::ChargePoint(const std::map<int32_t, int32_t>& evse_connector_structure,
Expand Down
77 changes: 67 additions & 10 deletions lib/ocpp/v201/smart_charging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
#include "ocpp/v201/device_model.hpp"
#include "ocpp/v201/enums.hpp"
#include "ocpp/v201/evse.hpp"
#include "ocpp/v201/messages/SetChargingProfile.hpp"
#include "ocpp/v201/ocpp_types.hpp"
#include "ocpp/v201/transaction.hpp"
#include <algorithm>
#include <iterator>
#include <memory>
#include <ocpp/v201/smart_charging.hpp>
#include <optional>

Expand All @@ -27,6 +27,8 @@ std::string profile_validation_result_to_string(ProfileValidationResultEnum e) {
return "Valid";
case ProfileValidationResultEnum::EvseDoesNotExist:
return "EvseDoesNotExist";
case ProfileValidationResultEnum::ExistingChargingStationExternalConstraints:
return "ExstingChargingStationExternalConstraints";
case ProfileValidationResultEnum::InvalidProfileType:
return "InvalidProfileType";
case ProfileValidationResultEnum::TxProfileMissingTransactionId:
Expand Down Expand Up @@ -112,6 +114,11 @@ ProfileValidationResultEnum SmartChargingHandler::validate_profile(ChargingProfi
}
}

result = verify_no_conflicting_external_constraints_id(profile);
if (result != ProfileValidationResultEnum::Valid) {
return result;
}

if (evse_id != STATION_WIDE_ID) {
auto& evse = evse_manager.get_evse(evse_id);
result = this->validate_profile_schedules(profile, &evse);
Expand Down Expand Up @@ -315,30 +322,67 @@ SmartChargingHandler::validate_profile_schedules(ChargingProfile& profile,
return ProfileValidationResultEnum::Valid;
}

void SmartChargingHandler::add_profile(int32_t evse_id, ChargingProfile& profile) {
if (STATION_WIDE_ID == evse_id) {
station_wide_charging_profiles.push_back(profile);
SetChargingProfileResponse SmartChargingHandler::add_profile(int32_t evse_id, ChargingProfile& profile) {
SetChargingProfileResponse response;
response.status = ChargingProfileStatusEnum::Accepted;
auto found_profile = false;
for (auto& [existing_evse_id, evse_profiles] : charging_profiles) {
for (auto it = evse_profiles.begin(); it != evse_profiles.end(); it++) {
if (profile.id == it->id) {
evse_profiles.erase(it);
found_profile = true;
break;
}
}

if (found_profile) {
break;
}
}

charging_profiles[evse_id].push_back(profile);

return response;
}

std::vector<ChargingProfile> SmartChargingHandler::get_station_wide_profiles() const {
std::vector<ChargingProfile> station_wide_profiles;
if (charging_profiles.count(STATION_WIDE_ID) > 0) {
station_wide_profiles = charging_profiles.at(STATION_WIDE_ID);
} else {
charging_profiles[evse_id].push_back(profile);
station_wide_profiles = {};
}

return station_wide_profiles;
}

std::vector<ChargingProfile> SmartChargingHandler::get_profiles() const {
std::vector<ChargingProfile> all_profiles;
for (auto evse_profile_pair : charging_profiles) {
all_profiles.insert(all_profiles.end(), evse_profile_pair.second.begin(), evse_profile_pair.second.end());
}
return all_profiles;
}

std::vector<ChargingProfile> SmartChargingHandler::get_evse_specific_tx_default_profiles() const {
std::vector<ChargingProfile> evse_specific_tx_default_profiles;

for (auto evse_profile_pair : charging_profiles) {
for (auto profile : evse_profile_pair.second)
if (profile.chargingProfilePurpose == ChargingProfilePurposeEnum::TxDefaultProfile) {
evse_specific_tx_default_profiles.push_back(profile);
for (auto& [evse_id, profiles] : charging_profiles) {
if (evse_id != STATION_WIDE_ID) {
for (auto profile : profiles) {
if (profile.chargingProfilePurpose == ChargingProfilePurposeEnum::TxDefaultProfile) {
evse_specific_tx_default_profiles.push_back(profile);
}
}
}
}

return evse_specific_tx_default_profiles;
}

std::vector<ChargingProfile> SmartChargingHandler::get_station_wide_tx_default_profiles() const {
std::vector<ChargingProfile> station_wide_tx_default_profiles;
for (auto profile : station_wide_charging_profiles) {
for (auto profile : this->get_station_wide_profiles()) {
if (profile.chargingProfilePurpose == ChargingProfilePurposeEnum::TxDefaultProfile) {
station_wide_tx_default_profiles.push_back(profile);
}
Expand Down Expand Up @@ -382,4 +426,17 @@ void SmartChargingHandler::conform_validity_periods(ChargingProfile& profile) co
profile.validTo = profile.validTo.value_or(ocpp::DateTime(date::utc_clock::time_point::max()));
}

ProfileValidationResultEnum
SmartChargingHandler::verify_no_conflicting_external_constraints_id(const ChargingProfile& profile) const {
auto result = ProfileValidationResultEnum::Valid;
for (auto existing_profile : this->get_profiles()) {
if (existing_profile.id == profile.id &&
existing_profile.chargingProfilePurpose == ChargingProfilePurposeEnum::ChargingStationExternalConstraints) {
result = ProfileValidationResultEnum::ExistingChargingStationExternalConstraints;
}
}

return result;
}

} // namespace ocpp::v201
1 change: 1 addition & 0 deletions tests/lib/ocpp/v201/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ target_include_directories(libocpp_unit_tests PUBLIC
${CMAKE_CURRENT_SOURCE_DIR})

target_sources(libocpp_unit_tests PRIVATE
test_charge_point.cpp
test_database_migration_files.cpp
test_device_model_storage_sqlite.cpp
test_notify_report_requests_splitter.cpp
Expand Down
Loading

0 comments on commit 01f064f

Please sign in to comment.