-
Notifications
You must be signed in to change notification settings - Fork 66
Description
OCPP Version
OCPP1.6, OCPP2.0.1, OCPP2.1
Describe the bug
Summary
When calling GetCompositeSchedule with duration: 0, the response returns hardware default limits instead of the currently active ChargePointMaxProfile (OCPP 1.6) / ChargingStationMaxProfile (OCPP 2.0.1) limit. Using any non-zero duration correctly returns the profile limit.
Affected Versions
- OCPP 1.6 (
lib/ocpp/v16/profile.cpp) - OCPP 2.0.1 (
lib/ocpp/v2/profile.cpp)
To Reproduce
Steps to Reproduce
- Set a
ChargePointMaxProfileon connector 0:
{
"connectorId": 0,
"csChargingProfiles": {
"chargingProfileId": 0,
"chargingProfileKind": "Absolute",
"chargingProfilePurpose": "ChargePointMaxProfile",
"chargingSchedule": {
"chargingRateUnit": "W",
"chargingSchedulePeriod": [{"limit": 50000, "startPeriod": 0}]
},
"stackLevel": 0
}
}- Call
GetCompositeSchedulewithduration: 0:
{
"connectorId": 0,
"duration": 0,
"chargingRateUnit": "W"
}-
Observe the response returns hardware default limit (e.g., 66240W) instead of the profile limit (50000W).
-
Call
GetCompositeSchedulewithduration: 3600:
{
"connectorId": 0,
"duration": 3600,
"chargingRateUnit": "W"
}- Observe the response correctly returns the profile limit (50000W).
Expected Behavior
GetCompositeSchedule with duration: 0 should return the currently active charging profile limit at that instant (50000W).
Actual Behavior
GetCompositeSchedule with duration: 0 returns the hardware default limit (66240W), ignoring the active charging profile.
Anything else?
Root Cause Analysis
The bug is in the generate_profile_from_periods() function in both v16/profile.cpp (line 428) and v2/profile.cpp (line 411).
The Problem
DateTime current = now;
while (current < end) { // <-- BUG: When duration=0, now == end, so this is immediately false
// ... find and process schedules ...
}When duration: 0:
start_time == end_time(after flooring to seconds)now == end- The condition
current < endevaluates tofalse - The while loop never executes
- Returns an empty
combinedprofile - Empty profile →
NO_LIMIT_SPECIFIED→ falls back to hardware default limit
Secondary Issue
Even if the loop executed, the schedule matching condition would fail:
if (schedule.end > current) // Excludes schedules where end == currentProposed Fix
Change the while loop to a do-while loop to ensure at least one iteration, and fix the schedule matching to include exact time matches:
// Use do-while to ensure at least one iteration for duration=0 case
do {
// find schedule to use for time: current
DateTime earliest = end;
DateTime next_earliest = end;
const period_entry_t* chosen{nullptr};
for (const auto& schedule : periods) {
if (schedule.start <= earliest) {
// Use >= instead of > to include schedules active at exactly 'current' time
if (schedule.end >= current && schedule.start <= current) {
next_earliest = earliest;
earliest = schedule.start;
chosen = &schedule;
break;
} else if (schedule.end > current) {
next_earliest = earliest;
earliest = schedule.start;
chosen = &schedule;
if (earliest <= current) {
break;
}
}
}
}
// ... rest of loop body unchanged ...
} while (current < end);Affected Files
| File | Line | Issue |
|---|---|---|
lib/ocpp/v16/profile.cpp |
428 | while (current < end) never executes when now == end |
lib/ocpp/v2/profile.cpp |
411 | Same issue |
Additional Context
The OCPP specification allows duration: 0 in GetCompositeSchedule requests. While semantically ambiguous (a schedule of 0 seconds), it's reasonable to interpret this as "what is the current limit at this instant?" and return the active profile's limit rather than falling back to defaults.