Repository navigation
fix(loadpoint): the deadline reserve holds to the plan's limits, not the charger's raw table - #1860
Merged
Merged
Conversation
…the charger's raw table With no plan, withDeadlineReserve took its on-power from DeadlineNeed, which held it to LimitPower(st, st.AllowedStepsW): the charger's whole table. On a table with one-phase and three-phase levels, a car whose own maximum is below the lowest three-phase level (a 3,680 W plug-in hybrid) got a one-phase level, 3,680 W. Planned charging runs on three phases, so the controller snapped with the three-phase minimum, 4,140 W, and sent 0 W. The car is marked one-phase only once it draws, so it never was. The plan path gets PlanningLimits and orders 4,140 W. DeadlineNeed now takes the limits. Controller.PlanningLimits is PlanningLimits on the loadpoint's phase mode and the site fuse, read under the controller's and the manager's own locks. The controller's reserve check uses it, and main.go wires it into the new mpc.Service.PlanningLimits; nil keeps the charger's table. A marked one-phase car is reserved the third of a three-phase level it takes, so a 2.3 kW car on a three-phase table is ordered 6,900 W, not the charger's 11,040 W. A charger without a table still takes any power in its range. Tests fail on master 286ac14: DeadlineNeed on mixed and three-phase tables, the reserve through SlotDirectiveAt, and the controller's order on mixed tables (unset, auto, 3p). Of twelve new noplan field-matrix rows, the seven on mixed tables go idle on master (order 0 W); the five on three-phase tables pass on both. The known list stays empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The independent review of #1807 (25 Oct 2026) found this case. With no plan, Core runs a saved goal on its deadline reserve. Take a charger whose step table has both one-phase levels (6–16 A on one phase, 1,380–3,680 W) and three-phase levels (4,140–11,040 W), with
phase_modeunset,autoor3p. A car whose own maximum is below the lowest three-phase level, such as a 3,680 W plug-in hybrid, got an order of 0 W and stood still until a plan came back. The field-matrix rows16A|25A / empty|auto / mixed / car1p / noplanshow it: 0 W, 0 kWh, idle. #1807 did not change this.Cause
withDeadlineReserve(mpc/deadline_directive.go) took its on-power fromDeadlineNeed, which held it toLimitPower(st, st.AllowedStepsW), the charger's whole table. With the car's 3,680 W maximum, that leaves the one-phase levels, so the reserve asked for 3,680 W. Planned charging runs on three phases on such a charger (resolvePhaseMode), so the controller snaps with the three-phase minimum, 4,140 W.SnapChargeW(3680, 4140, 3680, …)returns 0.The car counts as one-phase (
CarPhases == 1, #1801) only after the meter sees it draw. With 0 W it never draws, so it is never marked. The plan path does not have this bug:PlanningLimitsgives it the three-phase levels, keeps the lowest one, 4,140 W, and the plan orders that.Fix
The reserve now uses the limits the plan gets:
DeadlineNeed(st, minW, maxW, steps)takes the limits instead of computing them from the raw table.Controller.PlanningLimits(st)isPlanningLimitson the loadpoint's phase mode and the site fuse. It reads them under the controller's and the manager's own locks, so a config reload cannot race it. The controller's own check of the reserve uses it.mpc.Service.PlanningLimitsis a new hook.main.gowires it to the controller's method. With no hook set, the reserve uses the charger's own table, as before.What changes on the charger:
The reviewer also suspected a three-phase-only table with a car below its lowest three-phase step. I checked that case:
Evidence
The new tests fail on master 286ac14 and pass here:
loadpoint:TestDeadlineNeedRunsOnThePlansPhasesfails on master: mixed table, 3.7 kW car, on-power 3,680 W where 4,140 W is wanted; three-phase table, 2.3 kW marked car, 4,140 W where 2,300 W is wanted.TestControllerPlanningLimitsReadPhaseModeAndSiteis new. It covers the new method: the phase mode, a one-phase site, and a nil controller.mpc:TestGoalReserveHoldsToThePlansLimitsgoes throughSlotDirectiveAt. On master the reserve is 3,680 W, and 1,380 W near the goal; 4,140 W is wanted.control:TestJoinedNoPlanGoalSmallCarGetsChargerMinimumgains mixed tables (unset,auto,3p) and a charger whose minimum (2,000 W) is above the car's maximum (1,500 W). It runs the real service and controller to the driver order. On master the orders are[0]; here they are 4,140 W. The last case also pins the controller's check: with that check reverted to the raw table, it orders[0].noplanrows: 16 and 25 A, with mixed tables (unset,auto,3p) and three-phase tables (unset,auto), plus a 2.3 kW one-phase car on each table.idle: order 0 W, 0.0 kWh, idle for 55–59 min. The 5 rows on three-phase tables pass on both.homeboxnoplan rows, and 3.6 kWh on a 25 A fuse, peak 25 A. The 2.3 kW car takes 2.3 kWh, peak 19 A.go vet ./internal/... ./cmd/ftw/...andgo test ./internal/... ./cmd/ftw/...are green on 63 packages, aftermake driversin the fresh worktree. Race runs of the reserve tests are clean.1ppins, stepless chargers with an unmarked car, and manual holds are unchanged.cfgwithoutcfgMuon the control goroutine.Limits
3p: the order is still 0 W, as on master. That configuration has no valid three-phase order.mpc.Serviceand a different line inmain.go. fix(charging): carry two-phase cars through plans, current limits and Easee #1792 touches other parts ofcontroller.goandplanning_steps.go. The hunks do not overlap.🤖 Generated with Claude Code