feat: autotune PSU capacity from the same full-load run as fan ceilings
Extract the fan peak-tracking into observedPeakStore (observe max under load, hold >= minHold to reject spikes, round, persist JSON) and add a second instance for PSU draw (psu-observation.json, keyed by PSU ordinal). Fed from samplePSUPower like fans are from sampleFanSpeeds, so any full-load run refines it — the Fan Ceiling Check (which also samples PSU power at a slow cadence off its loop and writes psu_<i>_peak_w), a burn, thermal cycling, and the 5s web metrics collector. /topo PSU cards now scale the load fill by wattage_w when the BMC reports it, else by the observed peak draw — marked "~N% load". This MSI stand's BMC gives only instantaneous input power, so the observed peak is the only capacity figure available. Fan behaviour is unchanged (tests exercise updateFanObservation / estimateFanDutyCyclePctFromObservation / ResolveFanMaxRPM through the new store). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VHG21rgTUiR1G3qFHTVmN
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
e713504fc9
commit
7ed652c5b1
@@ -262,6 +262,27 @@ func TestFanSpinPeriodSec(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderTopoPSURowUsesObservedMaxWhenNoRating(t *testing.T) {
|
||||
ok := "OK"
|
||||
draw := 340.0
|
||||
psus := []schema.HardwarePowerSupply{
|
||||
{HardwareComponentStatus: schema.HardwareComponentStatus{Status: &ok}, InputPowerW: &draw}, // no WattageW
|
||||
}
|
||||
// Observed peak (keyed by ordinal) stands in for the missing nameplate.
|
||||
html := renderTopoPSURow(psus, map[string]float64{"0": 2400})
|
||||
if !strings.Contains(html, `data-psu-max-src="observed"`) {
|
||||
t.Fatalf("expected observed-scale marker: %s", html)
|
||||
}
|
||||
if !strings.Contains(html, "340 / ~2400 W · ~14% load") {
|
||||
t.Fatalf("expected estimated load line: %s", html)
|
||||
}
|
||||
// Without an observed peak either, just the raw watts, no fill.
|
||||
html = renderTopoPSURow(psus, nil)
|
||||
if strings.Contains(html, "% load") {
|
||||
t.Fatalf("no rating and no observed peak → no load figure: %s", html)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTopoPageRendersStorageDisksGroupedByType(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "audit.json")
|
||||
|
||||
Reference in New Issue
Block a user