You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit dc5e972
Browse filesBrowse the repository at this point in the historyBrowse files
fix(export): make the encoding's producers and its readers agree
Review of the split found two places where a limit was produced in a shape the rest of the
encoding does not accept, and both only bite at the top of the target range - exactly where
the packed encoding used to run out of room, which is why neither side's tests saw them.
calculate_yesterday's history reconstruction still appended a bare SoC percentage for a real
export, while its freeze sibling had been converted. A bare number decodes through the legacy
compatibility path, so it works by accident below 99 and every mode assertion passes either
way - but a slot that ended at 99% reads back as a freeze and one at 100% as an idle window,
dropping it from the History view.
The stored-form validator rejected any target at or above the freeze sentinel, a bound the
tuple form has no reason to keep. clip_export_slots genuinely produces the top of the range:
it narrows a target towards the SoC the simulation says is reachable, so a near-full battery
on a derated discharge rate clips to 99 or 100. export_limit_to_stored wrote those out
faithfully and export_limit_from_stored read them back as idle, so a restored plan or a
replayed debug dump silently lost the export. The bound is now the representable range.
Also from review, none of it behavioural:
- create_debug_yaml converted only the two Predbat-level limit lists. Each inverter keeps its
own copy, which rode into the dump as a bare sequence and came back from a replay as a list,
where export_mode_of compares it against a float and raises. Written and read the same way
as the other two now.
- execute.py asked for a target as `export_target_of(...) or 0` in five places. That reads as
a guard against a falsy target, which is not what is being guarded - the callers are all
inside a target-mode branch, so it is a type normalisation with an unreachable fallback.
Spelled out as export_target_percent_or_zero().
- prediction_kernel checks at import that struct's "@iid" layout really does match
PkExportLimit, rather than reasoning about it in a comment. from_buffer_copy already catches
a size mismatch, but not two layouts of the same size with the double at a different offset,
and armv7l/i686 are cross-built so nobody runs them here. A mismatch drops to per-field
assignment rather than raising, matching how this module treats a kernel it cannot trust.
- export_limit_sort_key documents that it is deliberately lossy at the top of the range: a 99%
target and a freeze both key as 99.0 because the display paths print this number. Ordering
and display only - every mode test goes through export_mode_of, which reads the field.
- inverter.py uses FULL_EXPORT_POWER rather than a bare 1.0.
Tests: the encoding round trips now sweep the full 0-100 target range; test_clip_export_slots
pairs the clip pass with the validator so the two cannot disagree again; calculate_yesterday
pins that a rebuilt limit is an instruction rather than a bare number; and the marshaller's
fast and fallback paths are compared field for field. Each was confirmed to fail with its fix
reverted. Full quick suite green with PREDBAT_KERNEL_REQUIRED=1, pre-commit clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
self.log("Export Hold (Demand mode) as export is now at/below target or freeze only is set - current SoC {}kWh and target {}kWh".format(self.soc_kw, discharge_soc))
print("ERROR: {} of {} rebuilt export limits are bare numbers rather than (mode, target, power) instructions: {!r}".format(len(bare), len(captured["export_limits_best"]), bare))
print("ERROR: clipping a {}kWh SoC at {}kW gave mode {}, expected a target".format(soc_kwh, rate_kw, export_mode_of(limit)))
276
+
failed=True
277
+
continue
278
+
ifexport_target_of(limit) <99:
279
+
print("ERROR: clipping a {}kWh SoC of 10.0kWh at {}kW gave target {}, expected the top of the range - the fixture no longer exercises the case".format(soc_kwh, rate_kw, export_target_of(limit)))
0 commit comments