SAFETY-93 review: a charge that ends by a stale link or a refusal hands back smoothly too
A battery still in CHARGE but dropped by the allocator (stale link, or a SAFETY-73 refusal contradicting its charge) left its -2000 W in the loop's start point, and B went 0 -> -1600 W in one cycle. The end is now noted after the unit claims are filed: in CHARGE, live and not contradicted. Tests: part 6 (stale link, refusal) and a spy on the guard suspension. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -362,6 +362,59 @@ check("it fires once, at the charge end: not at the HOLD end or on any "
|
||||
"other cycle", [x for x in _seen if x] == [[A]]
|
||||
and len(_seen) > 300)
|
||||
|
||||
# ============================================================================ #
|
||||
print("\n6. A still in CHARGE but no longer placed: stale link, refusal")
|
||||
|
||||
|
||||
def link_drop(at):
|
||||
"""A's writes rejected from `at` s, so it is dropped as a stale link."""
|
||||
def before(c, t):
|
||||
a = c.inverters[0]
|
||||
if t >= at and a.rejected_since is None:
|
||||
a.rejected_since = rig.BASE + timedelta(seconds=at)
|
||||
return before
|
||||
|
||||
|
||||
def refuses(at):
|
||||
"""A stops charging from `at` s (reads 0 W); SAFETY-73 drops it."""
|
||||
def before(c, t):
|
||||
if not getattr(c.hass, "_s93r", False):
|
||||
real = c.hass.power
|
||||
|
||||
def power(u):
|
||||
p = real(u)
|
||||
return 0.0 if u == "a" and c.hass.t >= at and p < 0 else p
|
||||
c.hass.power, c.hass._s93r = power, True
|
||||
return before
|
||||
|
||||
|
||||
for k, hook in (("stale link", link_drop), ("refusal", refuses)):
|
||||
def fn(hook=hook):
|
||||
return run(2, 1200.0, 9999.0, dur=1500.0, before=hook(1300.0))
|
||||
rows, _c6 = fn()
|
||||
old = without(fn)[0]
|
||||
drop = first((r[0] for r in rows if r[0] >= 1300
|
||||
and "dropped inverter-a" in str(r[7])), "A dropped")
|
||||
print(f" {k}: A dropped at {drop:.0f} s; B "
|
||||
f"{[cmd(r)['b'] for r in rows if drop - 2 <= r[0] <= drop + 4]}, "
|
||||
f"without the fix {[cmd(r)['b'] for r in old if drop - 2 <= r[0] <= drop + 4]}")
|
||||
check(f"⭐ {k}: A is dropped while still in CHARGE, and B never charges "
|
||||
f"from the grid and moves at most one slew step a cycle",
|
||||
phase_at(rows, drop) == "charge"
|
||||
and min(cmd(r)["b"] for r in rows if r[0] >= 1300) >= 0.0
|
||||
and moves(rows, "b", 1296, 1500) <= SLEW)
|
||||
check(f"non-vacuous ({k}): without the fix B jumps to -1600 W",
|
||||
min(cmd(r)["b"] for r in old if r[0] >= 1300) <= -1600.0)
|
||||
|
||||
_seen.clear()
|
||||
Controller._reseed_charge_end = _spy
|
||||
try:
|
||||
run(2, 6000.0, 1530.0, step=1500.0)
|
||||
finally:
|
||||
Controller._reseed_charge_end = no_fix
|
||||
check("the guard suspending the charge is no end: it fires once, at the HOLD",
|
||||
[x for x in _seen if x] == [[A]])
|
||||
|
||||
print()
|
||||
if fails:
|
||||
print(f"{len(fails)} FAILED: " + ", ".join(fails))
|
||||
|
||||
Reference in New Issue
Block a user