Repository navigation
Conversation
Fuse protection read every phase current as a magnitude. Under 20 A of solar export a phase counted as 20 A of import, so on a 25 A fuse a car never started, and on a 16 A fuse 8 A of export was enough to block it. phaseCurrentsA now signs each phase by the same phase's power in the reading (l1_w, l2_w, l3_w, which P1, Pixii, SolarEdge and SDM630 emit). Without that power the current counts as import, the safe side. Rises and starts read the level with direction: an exporting phase counts at its reading, so new load there has more room. An importing phase still counts at the higher of its reading and its heat. The heat filter keeps I², and only an importing phase can be over for the EV cut. The site simulation's meter now emits signed phase power, as P1 does, also when its currents lose their sign. Both export rows pass and lose their known line. 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. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b587de8e6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| signed := math.Abs(*a) | ||
| if w := powers[i]; w != nil && finiteFuseValue(*w) && *w < 0 { | ||
| signed = -signed |
There was a problem hiding this comment.
Normalize phase-current signs at the driver boundary
For meters reporting unsigned phase currents, this introduces sign normalization inside Core control rather than at the driver boundary, contrary to the non-negotiable invariant in AGENTS.md. Consequently, the EV guard interprets a different current convention from other consumers of the same telemetry, such as phaseImbalanceA and perPhaseOverageW, which still use magnitudes. Normalize the phase-current direction in the upstream drivers and update the bundled snapshot, so Core consumers receive one consistent convention rather than independently adapting vendor readings.
AGENTS.md reference: AGENTS.md:L63-L64
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
l1_w/l2_w/l3_w already carry the site sign: the drivers convert them at the boundary (P1 takes each phase's export from its import, Pixii and SolarEdge emit them signed). Core flips no vendor sign here. It combines two driver values, the current's size and the same phase power's direction, as the fuse rule of 21 October says; without phase power the current counts as import. The other Core readers (perPhaseOverageW, phaseImbalanceA) move to this one reader in #1789.
Review of #1782: evBatteryPhaseSpareW reads worstPhaseA, which now takes each phase's direction, so an exporting phase counts as 0 and leaves the battery's charge more room. The comment said magnitudes could not label the worst phase; it now says what the code does. No code change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Step 4 of the fuse rule in Fredrik's review of 21 October 2026: fuse protection counts the site meter's current per phase with direction, import positive, plus our own raises that cannot show yet. This PR handles the direction. The first of three PRs, then
import_limitand one function.Problem
phaseCurrentsA(go/internal/control/fuse_heat.go) tookmath.Absof each phase current. Under export a phase counted as import, and charging the car would lower the current through the fuse. On a 25 A fuse with 20 A of solar export per phase, Charge now never started (fuse_limit). On a 16 A fuse, 8 A of export (about 5.5 kW) was enough to block it. Introduced with #1701.Change
phaseCurrentsAsigns each phase by the same phase's power in the reading (l1_w/l2_w/l3_w). P1 DSMR reports current as a magnitude and power with a sign. Pixii signsl1_aitself, also from phase power, and SolarEdge and SDM630 emitl*_wtoo. Without that phase's power the current counts as import, the safe side.fuseHeat.levelA). An exporting phase counts at its reading, so new load there has more room: room = limit − (I_p + pending_p). An importing phase still counts at the higher of its reading and its heat, as before.overA), because cutting a car on an exporting phase raises the export.Deleted: the
math.Abson phase current inphaseCurrentsA. Net lines are small here; the large deletions come in the third PR.The battery's share beside a car changes too:
evBatteryPhaseSpareW(clampEVBatteryHeadroom) readsworstPhaseA, which now takes each phase's direction, so an exporting phase counts as 0 and the battery's charge can get more room there.Not changed here: the battery side's fuse paths still read magnitudes.
perPhaseOverageW(dispatch.go:3878), the #1536 imbalance proxyphaseImbalanceA(fuse_phase.go:44) and the per-phase relief (dispatch.go:3606,:4468) attribute direction from the total. A probe with L1 exporting 27 A while the site imports made the fuse saver discharge and push L1 to 29.7 A, on master and on this PR alike. The one-function PR (#1789, next beta) replaces them with the same reader.Site simulation, before → after
The surplus row's known cause is the charger's late readback added back to surplus, not the fuse. Its idle time moved because the fuse no longer holds the car back. That row is not in this PR's scope.
New unit test:
TestPhaseCurrentsTakeDirectionFromPhasePower(P1 magnitudes, Pixii signed, no phase power → import).Evidence
go test -p 2 ./internal/control ./internal/sitesim ./internal/loadpoint ./cmd/ftw: passmake verify(withGOFLAGS=-p=2): passReal-hardware evidence is still needed: a P1 or Pixii site under solar export, with the car starting on Charge now.
🤖 Generated with Claude Code