Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// run calculation at least once especially where current < end
// run calculation at least once, especially when 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// run calculation at least once especially where current < end
// run calculation at least once, especially when 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