Skip to content

Commit 6cd19b9

Browse files
committed
[smartcharging] GetCompositeSchedule implementation : add user schedule
1 parent 7291c1e commit 6cd19b9

13 files changed

+599
-30
lines changed

examples/common/DefaultChargePointEventsHandler.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,16 @@ void DefaultChargePointEventsHandler::transactionDeAuthorized(unsigned int conne
181181
cout << "Transaction deauthorized on connector : " << connector_id << endl;
182182
}
183183

184+
/** @copydoc bool IChargePointEventsHandler::getLocalLimitationsSchedule(unsigned int, ocpp::types::ChargingSchedule&) */
185+
bool DefaultChargePointEventsHandler::getLocalLimitationsSchedule(unsigned int connector_id,
186+
unsigned int duration,
187+
ocpp::types::ChargingSchedule& schedule)
188+
{
189+
(void)schedule;
190+
cout << "Local limitations schedule requested : " << connector_id << " - " << duration << endl;
191+
return false;
192+
}
193+
184194
/** @copydoc bool IChargePointEventsHandler::resetRequested(ocpp::types::ResetType) */
185195
bool DefaultChargePointEventsHandler::resetRequested(ocpp::types::ResetType reset_type)
186196
{

examples/common/DefaultChargePointEventsHandler.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ class DefaultChargePointEventsHandler : public ocpp::chargepoint::IChargePointEv
9696
/** @copydoc void IChargePointEventsHandler::transactionDeAuthorized(unsigned int) */
9797
void transactionDeAuthorized(unsigned int connector_id) override;
9898

99+
/** @copydoc bool IChargePointEventsHandler::getLocalLimitationsSchedule(unsigned int, unsigned int, ocpp::types::ChargingSchedule&) */
100+
bool getLocalLimitationsSchedule(unsigned int connector_id, unsigned int duration, ocpp::types::ChargingSchedule& schedule) override;
101+
99102
/** @copydoc bool IChargePointEventsHandler::resetRequested(ocpp::types::ResetType) */
100103
bool resetRequested(ocpp::types::ResetType reset_type) override;
101104

src/chargepoint/ChargePoint.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ bool ChargePoint::start()
279279
m_smart_charging_manager = std::make_unique<SmartChargingManager>(m_stack_config,
280280
m_ocpp_config,
281281
m_database,
282+
m_events_handler,
282283
*m_timer_pool,
283284
*m_worker_pool,
284285
m_connectors,

src/chargepoint/interface/IChargePointEventsHandler.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ along with OpenOCPP. If not, see <http://www.gnu.org/licenses/>.
2020
#define OPENOCPP_ICHARGEPOINTEVENTSHANDLER_H
2121

2222
#include "Certificate.h"
23+
#include "ChargingSchedule.h"
2324
#include "DateTime.h"
2425
#include "Enums.h"
2526
#include "MeterValue.h"
@@ -137,6 +138,15 @@ class IChargePointEventsHandler
137138
*/
138139
virtual void transactionDeAuthorized(unsigned int connector_id) = 0;
139140

141+
/**
142+
* @brief Called on reception of a GetCompositeSchedule request
143+
* @param connector_id Id of the concerned connector
144+
* @param duration Duration in seconds of the schedule
145+
* @param schedule Schedule containing the local limitations for the requested duration
146+
* @return true if a schedule has been defined, false if there are no local limitations for the requested duration
147+
*/
148+
virtual bool getLocalLimitationsSchedule(unsigned int connector_id, unsigned int duration, ocpp::types::ChargingSchedule& schedule) = 0;
149+
140150
/**
141151
* @brief Called on a reset request from the Central System
142152
* @param reset_type Type of reset

src/chargepoint/smartcharging/SmartChargingManager.cpp

Lines changed: 193 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ along with OpenOCPP. If not, see <http://www.gnu.org/licenses/>.
2020
#include "Connectors.h"
2121
#include "GenericMessageSender.h"
2222
#include "IChargePointConfig.h"
23+
#include "IChargePointEventsHandler.h"
2324
#include "IOcppConfig.h"
2425
#include "Logger.h"
2526
#include "WorkerThreadPool.h"
@@ -38,6 +39,7 @@ namespace chargepoint
3839
SmartChargingManager::SmartChargingManager(const ocpp::config::IChargePointConfig& stack_config,
3940
ocpp::config::IOcppConfig& ocpp_config,
4041
ocpp::database::Database& database,
42+
IChargePointEventsHandler& events_handler,
4143
ocpp::helpers::ITimerPool& timer_pool,
4244
ocpp::helpers::WorkerThreadPool& worker_pool,
4345
Connectors& connectors,
@@ -48,6 +50,7 @@ SmartChargingManager::SmartChargingManager(const ocpp::config::IChargePointConfi
4850
GenericMessageHandler<GetCompositeScheduleReq, GetCompositeScheduleConf>(GET_COMPOSITE_SCHEDULE_ACTION, messages_converter),
4951
m_stack_config(stack_config),
5052
m_ocpp_config(ocpp_config),
53+
m_events_handler(events_handler),
5154
m_worker_pool(worker_pool),
5255
m_connectors(connectors),
5356
m_profile_db(ocpp_config, database),
@@ -392,6 +395,32 @@ bool SmartChargingManager::handleMessage(const ocpp::messages::GetCompositeSched
392395
break;
393396
}
394397
}
398+
if (periods.empty())
399+
{
400+
LOG_INFO << "No charging profiles for the requested period";
401+
}
402+
403+
// Get local limitations
404+
ChargingProfile local_profile;
405+
local_profile.chargingProfileId = 0;
406+
local_profile.chargingProfileKind = ChargingProfileKindType::Relative;
407+
local_profile.chargingProfilePurpose = ChargingProfilePurposeType::TxDefaultProfile;
408+
local_profile.stackLevel = 0;
409+
if (m_events_handler.getLocalLimitationsSchedule(request.connectorId, request.duration, local_profile.chargingSchedule) &&
410+
!local_profile.chargingSchedule.chargingSchedulePeriod.empty())
411+
{
412+
// Ensure profile is relative with the requested duration
413+
local_profile.chargingSchedule.startSchedule.clear();
414+
local_profile.chargingSchedule.duration = request.duration;
415+
416+
// Merge periods
417+
std::vector<Period> local_periods = getProfilePeriods(connector, local_profile, now, request.duration);
418+
periods = mergeLocalPeriods(periods, local_periods);
419+
}
420+
else
421+
{
422+
LOG_INFO << "No local limitations for the requested period";
423+
}
395424

396425
// Create response
397426
if (!periods.empty())
@@ -767,19 +796,22 @@ std::vector<SmartChargingManager::Period> SmartChargingManager::getProfilePeriod
767796
// Check if the profile is active
768797
bool found = false;
769798
size_t period = 0;
770-
if (isProfileActive(connector, profile, period, time_point))
771-
{
772-
// Profile is active
773-
found = true;
774-
}
775-
else
799+
if (!profile.chargingSchedule.chargingSchedulePeriod.empty())
776800
{
777-
// Check start of schedule
778-
if ((start_of_schedule <= ts_end_of_schedule) && isProfileValid(profile, start_of_schedule))
801+
if (isProfileActive(connector, profile, period, time_point))
802+
{
803+
// Profile is active
804+
found = true;
805+
}
806+
else
779807
{
780-
// Get the first period
781-
period = 0;
782-
found = true;
808+
// Check start of schedule
809+
if ((start_of_schedule <= ts_end_of_schedule) && isProfileValid(profile, start_of_schedule))
810+
{
811+
// Get the first period
812+
period = 0;
813+
found = true;
814+
}
783815
}
784816
}
785817

@@ -833,13 +865,13 @@ std::vector<SmartChargingManager::Period> SmartChargingManager::getProfilePeriod
833865
return periods;
834866
}
835867

836-
/** @brief Merge composite schedule periods */
868+
/** @brief Merge charging profiles periods */
837869
std::vector<SmartChargingManager::Period> SmartChargingManager::mergeProfilePeriods(const std::vector<Period>& ref_periods,
838870
const std::vector<Period>& new_periods)
839871
{
840872
std::vector<Period> merged_periods;
841873

842-
// Check first merge
874+
// Check if a merge is needed
843875
if (ref_periods.empty())
844876
{
845877
merged_periods = new_periods;
@@ -964,13 +996,160 @@ std::vector<SmartChargingManager::Period> SmartChargingManager::mergeProfilePeri
964996
}
965997
if (error || (ref_period_index != ref_periods.size()))
966998
{
967-
LOG_ERROR << "Unable to compute the composite schedule due to non continuous profiles periods";
999+
LOG_WARNING << "Unable to compute the composite schedule due to non continuous profiles periods";
9681000
merged_periods.clear();
9691001
}
9701002
}
9711003

9721004
return merged_periods;
9731005
}
9741006

1007+
/** @brief Merge local limitations periods */
1008+
std::vector<SmartChargingManager::Period> SmartChargingManager::mergeLocalPeriods(const std::vector<Period>& profiles_periods,
1009+
const std::vector<Period>& local_periods)
1010+
{
1011+
std::vector<Period> merged_periods;
1012+
1013+
// Check if a merge is needed
1014+
if (profiles_periods.empty())
1015+
{
1016+
merged_periods = local_periods;
1017+
}
1018+
else
1019+
{
1020+
bool offset = false;
1021+
int local_period_start = 0;
1022+
int local_period_start_offset = 0;
1023+
size_t profiles_period_index = 0;
1024+
for (size_t i = 0; i < local_periods.size(); i++)
1025+
{
1026+
// Compute start of current local period
1027+
if (offset)
1028+
{
1029+
offset = false;
1030+
}
1031+
else
1032+
{
1033+
local_period_start = local_periods[i].start;
1034+
local_period_start_offset = 0;
1035+
}
1036+
1037+
int local_period_end = local_period_start + (local_periods[i].duration - local_period_start_offset);
1038+
// Check if there are profiles periods left
1039+
if (profiles_period_index != profiles_periods.size())
1040+
{
1041+
if (local_period_end <= profiles_periods[profiles_period_index].start)
1042+
{
1043+
// The whole period is before the profile period
1044+
addMergedPeriod(local_periods[i], merged_periods);
1045+
}
1046+
else if (local_period_start >= profiles_periods[profiles_period_index].start)
1047+
{
1048+
int profiles_period_end =
1049+
profiles_periods[profiles_period_index].start + profiles_periods[profiles_period_index].duration;
1050+
if (local_period_end <= profiles_period_end)
1051+
{
1052+
// The whole period is included inside the profile period
1053+
Period p;
1054+
p.start = local_period_start;
1055+
p.duration = local_periods[i].duration - local_period_start_offset;
1056+
mergeSetpoint(profiles_periods[profiles_period_index], local_periods[i], p);
1057+
addMergedPeriod(p, merged_periods);
1058+
}
1059+
else
1060+
{
1061+
// The period is across profile periods
1062+
Period p;
1063+
p.start = local_period_start;
1064+
p.duration = profiles_period_end - local_period_start;
1065+
mergeSetpoint(profiles_periods[profiles_period_index], local_periods[i], p);
1066+
addMergedPeriod(p, merged_periods);
1067+
1068+
// Next profiles period but keep working on the same local period
1069+
local_period_start = p.start + p.duration;
1070+
local_period_start_offset = local_period_start - local_periods[i].start;
1071+
offset = true;
1072+
profiles_period_index++;
1073+
i--;
1074+
}
1075+
}
1076+
else
1077+
{
1078+
// A part of the local period which overlapse the profile period
1079+
Period p;
1080+
p.start = local_periods[i].start;
1081+
p.duration = profiles_periods[profiles_period_index].start - local_periods[i].start;
1082+
p.setpoint = local_periods[i].setpoint;
1083+
p.unit = local_periods[i].unit;
1084+
p.nb_phases = local_periods[i].nb_phases;
1085+
addMergedPeriod(p, merged_periods);
1086+
1087+
// Keep working on the same local period
1088+
local_period_start = p.start + p.duration;
1089+
local_period_start_offset = local_period_start - local_periods[i].start;
1090+
offset = true;
1091+
i--;
1092+
}
1093+
}
1094+
else
1095+
{
1096+
// A local period without merge
1097+
Period p;
1098+
p.start = local_period_start;
1099+
p.duration = local_periods[i].duration - local_period_start_offset;
1100+
p.setpoint = local_periods[i].setpoint;
1101+
p.unit = local_periods[i].unit;
1102+
p.nb_phases = local_periods[i].nb_phases;
1103+
addMergedPeriod(p, merged_periods);
1104+
}
1105+
}
1106+
}
1107+
return merged_periods;
1108+
}
1109+
1110+
/** @brief Merge the setpoint of a local limitation and a profile limitation */
1111+
void SmartChargingManager::mergeSetpoint(const Period& profile_period, const Period& local_period, Period& merged_period)
1112+
{
1113+
float profile_setpoint = profile_period.setpoint;
1114+
if (profile_period.unit != local_period.unit)
1115+
{
1116+
profile_setpoint = convertToUnit(profile_period.setpoint, local_period.unit, profile_period.nb_phases);
1117+
}
1118+
if (local_period.setpoint < profile_setpoint)
1119+
{
1120+
merged_period.setpoint = local_period.setpoint;
1121+
merged_period.unit = local_period.unit;
1122+
merged_period.nb_phases = local_period.nb_phases;
1123+
}
1124+
else
1125+
{
1126+
merged_period.setpoint = profile_period.setpoint;
1127+
merged_period.unit = profile_period.unit;
1128+
merged_period.nb_phases = profile_period.nb_phases;
1129+
}
1130+
}
1131+
1132+
/** @brief Add a local limitation and profiles limitations merged period */
1133+
void SmartChargingManager::addMergedPeriod(const Period& merged_period, std::vector<Period>& periods)
1134+
{
1135+
if (periods.empty())
1136+
{
1137+
periods.push_back(merged_period);
1138+
}
1139+
else
1140+
{
1141+
Period& last_period = periods.back();
1142+
if ((last_period.setpoint == merged_period.setpoint) && (last_period.unit == merged_period.unit) &&
1143+
(last_period.nb_phases == merged_period.nb_phases))
1144+
{
1145+
last_period.duration += merged_period.duration;
1146+
}
1147+
else
1148+
{
1149+
periods.push_back(merged_period);
1150+
}
1151+
}
1152+
}
1153+
9751154
} // namespace chargepoint
9761155
} // namespace ocpp

src/chargepoint/smartcharging/SmartChargingManager.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ namespace chargepoint
5454

5555
class Connectors;
5656
struct Connector;
57+
class IChargePointEventsHandler;
5758

5859
/** @brief Handle smart charging for the charge point */
5960
class SmartChargingManager
@@ -68,6 +69,7 @@ class SmartChargingManager
6869
SmartChargingManager(const ocpp::config::IChargePointConfig& stack_config,
6970
ocpp::config::IOcppConfig& ocpp_config,
7071
ocpp::database::Database& database,
72+
IChargePointEventsHandler& events_handler,
7173
ocpp::helpers::ITimerPool& timer_pool,
7274
ocpp::helpers::WorkerThreadPool& worker_pool,
7375
Connectors& connectors,
@@ -134,6 +136,8 @@ class SmartChargingManager
134136
const ocpp::config::IChargePointConfig& m_stack_config;
135137
/** @brief Standard OCPP configuration */
136138
ocpp::config::IOcppConfig& m_ocpp_config;
139+
/** @brief User defined events handler */
140+
IChargePointEventsHandler& m_events_handler;
137141
/** @brief Worker thread pool */
138142
ocpp::helpers::WorkerThreadPool& m_worker_pool;
139143
/** @brief Connectors */
@@ -204,8 +208,17 @@ class SmartChargingManager
204208
const ocpp::types::DateTime& time_point,
205209
unsigned int duration);
206210

207-
/** @brief Merge composite schedule periods */
211+
/** @brief Merge charging profiles periods */
208212
std::vector<Period> mergeProfilePeriods(const std::vector<Period>& ref_periods, const std::vector<Period>& new_periods);
213+
214+
/** @brief Merge local limitations periods */
215+
std::vector<Period> mergeLocalPeriods(const std::vector<Period>& profiles_periods, const std::vector<Period>& local_periods);
216+
217+
/** @brief Merge the setpoint of a local limitation and a profile limitation */
218+
void mergeSetpoint(const Period& profile_period, const Period& local_period, Period& merged_period);
219+
220+
/** @brief Add a local limitation and profiles limitations merged period */
221+
void addMergedPeriod(const Period& merged_period, std::vector<Period>& periods);
209222
};
210223

211224
} // namespace chargepoint

tests/chargepoint/smartcharging/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ add_test(
1616
NAME test_composite_schedule2
1717
COMMAND test_composite_schedule2
1818
)
19+
add_executable(test_composite_schedule3 test_composite_schedule3.cpp)
20+
target_link_libraries(test_composite_schedule3 unit_tests_stubs doctest sqlite3 pthread dl )
21+
add_test(
22+
NAME test_composite_schedule3
23+
COMMAND test_composite_schedule3
24+
)
1925

2026
# Unit tests for ProfileDatabase class
2127
add_executable(test_profile_database test_profile_database.cpp)

0 commit comments

Comments
 (0)