Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions lib/everest/ocpp/lib/ocpp/v16/profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,8 @@ IntermediateProfile generate_profile_from_periods(std::vector<period_entry_t>& p
IntermediateProfile combined{};
DateTime current = now;

while (current < end) {
// run calculation at least once especially where current >= end
do {
// find schedule to use for time: current
DateTime earliest = end;
DateTime next_earliest = end;
Expand Down Expand Up @@ -476,7 +477,7 @@ IntermediateProfile generate_profile_from_periods(std::vector<period_entry_t>& p
current = next_earliest;
}
}
}
} while (current < end);

return combined;
}
Expand Down
8 changes: 7 additions & 1 deletion lib/everest/ocpp/lib/ocpp/v16/smart_charging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,14 @@ ChargingSchedule SmartChargingHandler::calculate_composite_schedule(const ocpp::
const std::int32_t evse_id,
ChargingRateUnit charging_rate_unit,
bool is_offline, bool simulate_transaction_active) {
// handle edge case where start_time > end_time
auto start_time_w = start_time;
if (start_time_w > end_time) {
start_time_w = end_time;
}

const auto enhanced_composite_schedule = this->calculate_enhanced_composite_schedule(
start_time, end_time, evse_id, charging_rate_unit, is_offline, simulate_transaction_active);
start_time_w, end_time, evse_id, charging_rate_unit, is_offline, simulate_transaction_active);
ChargingSchedule composite_schedule;
composite_schedule.chargingRateUnit = enhanced_composite_schedule.chargingRateUnit;
composite_schedule.duration = enhanced_composite_schedule.duration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,12 +550,18 @@ std::vector<IntermediateProfile> generate_evse_intermediates(std::vector<Chargin
}
} // namespace

CompositeSchedule SmartCharging::calculate_composite_schedule(const ocpp::DateTime& start_time,
CompositeSchedule SmartCharging::calculate_composite_schedule(const ocpp::DateTime& start_t,
const ocpp::DateTime& end_time,
const std::int32_t evse_id,
ChargingRateUnitEnum charging_rate_unit, bool is_offline,
bool simulate_transaction_active) {

// handle edge case where start_time > end_time
auto start_time = start_t;
if (start_time > end_time) {
start_time = end_time;
}

const CompositeScheduleConfig config{this->context.device_model, is_offline};

std::optional<ocpp::DateTime> session_start{};
Expand Down
5 changes: 3 additions & 2 deletions lib/everest/ocpp/lib/ocpp/v2/profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,8 @@ IntermediateProfile generate_profile_from_periods(std::vector<period_entry_t>& p
IntermediateProfile combined{};
DateTime current = now;

while (current < end) {
// run calculation at least once especially where current >= end
do {
// find schedule to use for time: current
DateTime earliest = end;
DateTime next_earliest = end;
Expand Down Expand Up @@ -477,7 +478,7 @@ IntermediateProfile generate_profile_from_periods(std::vector<period_entry_t>& p
current = next_earliest;
}
}
}
} while (current < end);

return combined;
}
Expand Down
72 changes: 72 additions & 0 deletions lib/everest/ocpp/tests/lib/ocpp/v16/test_composite_schedule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -928,5 +928,77 @@ TEST_F(CompositeScheduleTestFixture, TxDefaultConnector0) {
PeriodEquals(350, 10000.0F)));
}

TEST_F(CompositeScheduleTestFixture, ZeroDuration) {
// test relating to libocpp issue 1169
// incorrect composite schedule when duration is zero

// using absolute schedule with limit 2000.0, starting 2024-01-17T18:04:00.000Z
// default limit is 33120.0

auto handler = create_smart_charging_handler(1, false);
ChargingProfile profile_tx_default_1 = get_charging_profile_from_file("TxProfile_03_Absolute.json");

handler->add_tx_default_profile(profile_tx_default_1, 0);

// start 1 minute into the profile
const DateTime start_time = ocpp::DateTime("2024-01-17T18:05:00.000Z");
const DateTime end_time = ocpp::DateTime("2024-01-17T18:05:01.000Z");
const auto expected_limit = 2000.0F;

// check with end time and a corresponding duration of 1 second
auto result_con0 =
handler->calculate_composite_schedule(start_time, end_time, STATION_WIDE_ID, ChargingRateUnit::W, false, true);

EXPECT_EQ(result_con0.startSchedule, start_time);
EXPECT_EQ(result_con0.duration, 1);
EXPECT_EQ(result_con0.chargingRateUnit, ChargingRateUnit::W);

EXPECT_THAT(result_con0.chargingSchedulePeriod, testing::ElementsAre(PeriodEquals(0, expected_limit)));

auto result_con1 = handler->calculate_composite_schedule(start_time, end_time, 1, ChargingRateUnit::W, false, true);

EXPECT_EQ(result_con1.startSchedule, start_time);
EXPECT_EQ(result_con1.duration, 1);
EXPECT_EQ(result_con1.chargingRateUnit, ChargingRateUnit::W);

EXPECT_THAT(result_con1.chargingSchedulePeriod, testing::ElementsAre(PeriodEquals(0, expected_limit)));

// Now with duration of 0 seconds
result_con0 = handler->calculate_composite_schedule(start_time, start_time, STATION_WIDE_ID, ChargingRateUnit::W,
false, true);

EXPECT_EQ(result_con0.startSchedule, start_time);
EXPECT_EQ(result_con0.duration, 0);
EXPECT_EQ(result_con0.chargingRateUnit, ChargingRateUnit::W);

EXPECT_THAT(result_con0.chargingSchedulePeriod, testing::ElementsAre(PeriodEquals(0, expected_limit)));

result_con1 = handler->calculate_composite_schedule(start_time, start_time, 1, ChargingRateUnit::W, false, true);

EXPECT_EQ(result_con1.startSchedule, start_time);
EXPECT_EQ(result_con1.duration, 0);
EXPECT_EQ(result_con1.chargingRateUnit, ChargingRateUnit::W);

EXPECT_THAT(result_con1.chargingSchedulePeriod, testing::ElementsAre(PeriodEquals(0, expected_limit)));

// Now with duration of -1 second (treated as an instantaneous 0 duration)
result_con0 =
handler->calculate_composite_schedule(end_time, start_time, STATION_WIDE_ID, ChargingRateUnit::W, false, true);

EXPECT_EQ(result_con0.startSchedule, start_time);
EXPECT_EQ(result_con0.duration, 0);
EXPECT_EQ(result_con0.chargingRateUnit, ChargingRateUnit::W);

EXPECT_THAT(result_con0.chargingSchedulePeriod, testing::ElementsAre(PeriodEquals(0, expected_limit)));

result_con1 = handler->calculate_composite_schedule(end_time, start_time, 1, ChargingRateUnit::W, false, true);

EXPECT_EQ(result_con1.startSchedule, start_time);
EXPECT_EQ(result_con1.duration, 0);
EXPECT_EQ(result_con1.chargingRateUnit, ChargingRateUnit::W);

EXPECT_THAT(result_con1.chargingSchedulePeriod, testing::ElementsAre(PeriodEquals(0, expected_limit)));
}

} // namespace v16
} // namespace ocpp
47 changes: 47 additions & 0 deletions lib/everest/ocpp/tests/lib/ocpp/v2/test_composite_schedule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1201,4 +1201,51 @@ TEST_F(CompositeScheduleTestFixtureV2, TxDefaultConnector0) {
PeriodEquals(350, 10000.0F)));
}

TEST_F(CompositeScheduleTestFixtureV2, ZeroDuration) {
// test relating to libocpp issue 1169
// incorrect composite schedule when duration is zero

// using absolute schedule with limit 32.0, starting 2024-01-01T12:02:00Z
// default limit is 57.0 (DEFAULT_LIMIT_AMPERE)

load_charging_profiles_for_evse("singles/Absolute_301.json", STATION_WIDE_ID);

const DateTime start_time = ocpp::DateTime("2024-01-01T12:02:00Z");
const DateTime end_time = ocpp::DateTime("2024-01-01T12:02:01Z");
const auto expected_limit = 32.0F;

// check with end time and a corresponding duration of 1 second
auto result = handler->calculate_composite_schedule(start_time, end_time, STATION_WIDE_ID, ChargingRateUnitEnum::A,
false, true);

EXPECT_EQ(result.evseId, STATION_WIDE_ID);
EXPECT_EQ(result.scheduleStart, start_time);
EXPECT_EQ(result.duration, 1);
EXPECT_EQ(result.chargingRateUnit, ChargingRateUnitEnum::A);

EXPECT_THAT(result.chargingSchedulePeriod, testing::ElementsAre(PeriodEquals(0, expected_limit)));

// Now with duration of 0 seconds
result = handler->calculate_composite_schedule(start_time, start_time, STATION_WIDE_ID, ChargingRateUnitEnum::A,
false, true);

EXPECT_EQ(result.evseId, STATION_WIDE_ID);
EXPECT_EQ(result.scheduleStart, start_time);
EXPECT_EQ(result.duration, 0);
EXPECT_EQ(result.chargingRateUnit, ChargingRateUnitEnum::A);

EXPECT_THAT(result.chargingSchedulePeriod, testing::ElementsAre(PeriodEquals(0, expected_limit)));

// Now with duration of -1 second
result = handler->calculate_composite_schedule(end_time, start_time, STATION_WIDE_ID, ChargingRateUnitEnum::A,
false, true);

EXPECT_EQ(result.evseId, STATION_WIDE_ID);
EXPECT_EQ(result.scheduleStart, start_time);
EXPECT_EQ(result.duration, 0);
EXPECT_EQ(result.chargingRateUnit, ChargingRateUnitEnum::A);

EXPECT_THAT(result.chargingSchedulePeriod, testing::ElementsAre(PeriodEquals(0, expected_limit)));
}

} // namespace ocpp::v2
42 changes: 42 additions & 0 deletions lib/everest/ocpp/tests/lib/ocpp/v21/test_composite_schedule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,3 +420,45 @@ TEST_F(CompositeScheduleTestFixtureV21, V21DifferentNumberPhases_StationWide) {
ChargingRateUnitEnum::W, false, true);
EXPECT_EQ(actual, expected);
}

TEST_F(CompositeScheduleTestFixtureV21, ZeroDuration) {
load_charging_profiles_for_evse(BASE_JSON_PATH_V21 + "/station_wide_three_phases/", STATION_WIDE_ID);
const DateTime start_time("2024-01-17T18:00:00.000Z");
const DateTime end_time("2024-01-17T18:00:01.000Z");

ChargingSchedulePeriod period1;
period1.startPeriod = 0;
period1.limit = 12420.0F;
period1.limit_L2 = 12430.0F;
period1.limit_L3 = 12440.0F;
period1.numberPhases = 3;

CompositeSchedule expected_0;
expected_0.chargingSchedulePeriod = {period1};
expected_0.evseId = DEFAULT_EVSE_ID;
expected_0.duration = 0;
expected_0.scheduleStart = start_time;
expected_0.chargingRateUnit = ChargingRateUnitEnum::W;

CompositeSchedule expected_1;
expected_1.chargingSchedulePeriod = {period1};
expected_1.evseId = DEFAULT_EVSE_ID;
expected_1.duration = 1;
expected_1.scheduleStart = start_time;
expected_1.chargingRateUnit = ChargingRateUnitEnum::W;

// check with end time and a corresponding duration of 1 second
auto actual = handler->calculate_composite_schedule(start_time, end_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W,
false, true);
EXPECT_EQ(actual, expected_1);

// Now with duration of 0 seconds
actual = handler->calculate_composite_schedule(start_time, start_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W,
false, true);
EXPECT_EQ(actual, expected_0);

// Now with duration of -1 second
actual = handler->calculate_composite_schedule(end_time, start_time, DEFAULT_EVSE_ID, ChargingRateUnitEnum::W,
false, true);
EXPECT_EQ(actual, expected_0);
}
Loading