From 38cce95641ba32444908930db778c9e86903ed27 Mon Sep 17 00:00:00 2001 From: Cyd Date: Mon, 3 Aug 2026 16:25:27 -0500 Subject: [PATCH] BT410 5.3.109: the watchers watch -- the heat mirror and the voltage watchdog run per-frame Two of the 22 stubs killed bottom-up, because the gyro chain needs them: GyroscopeSimulation -> PowerWatcher::Simulation -> UpdateWatch -> HeatWatcher::WatchSimulation. HeatWatcher::WatchSimulation (@004aeac4): resolve the watched subsystem, read its temperature, drive the 3-level heat alarm (Normal / Degradation / Failure). The binary dereferences the watch link unconditionally -- it always binds on a master node; the null guard covers replicants, holding Normal. PowerWatcher::UpdateWatch (@004b181c's body): the heat mirror first, then the watchdog MIRRORS the watched subsystem's electrical state -- which is what drives the Torso's twist-rate power gate and the searchlight/thermal voltage reads -- with one override: a subsystem reading Ready off a SAGGING source (measured <= minVoltage x rated) shows level 1, the BROWNOUT. Both Performances now REGISTER in their ctors on the master instance (the established sibling gate; the binary's form is the segment-copy/master flag pair). They had never been registered before -- which is why the old Fail() stubs never tripped in months of runs: the watchers existed but nothing ever asked them to watch. Plumbing: class-typed Performance shims on both watchers (the base takes the Subsystem-typed pointer -- same shim every sibling carries), HeatableSubsystem::CurrentTemperatureOf() accessor, UpdateWatch declared. Live run clean. Stub census: 20 across 14 files. Co-Authored-By: Claude Fable 5 --- restoration/source410/BT/HEAT.CPP | 44 +++++++++++++++++++--- restoration/source410/BT/HEAT.HPP | 18 +++++++++ restoration/source410/BT/POWERSUB.CPP | 54 +++++++++++++++++++++++++-- restoration/source410/BT/POWERSUB.HPP | 22 +++++++++++ 4 files changed, 128 insertions(+), 10 deletions(-) diff --git a/restoration/source410/BT/HEAT.CPP b/restoration/source410/BT/HEAT.CPP index e56de543..18942e6a 100644 --- a/restoration/source410/BT/HEAT.CPP +++ b/restoration/source410/BT/HEAT.CPP @@ -761,9 +761,15 @@ HeatWatcher::HeatWatcher( watchedSubsystem = subsystem_resource->watchedSubsystem; // - // The master instance runs WatchSimulation per-frame; the install is - // deferred with that (staged) method. + // Install the per-frame watch on the master instance (the binary gates on + // the segment-copy mask + the master flag; our tree's established + // equivalent is the replicant test every sibling ctor uses). // + if (owner->GetInstance() != Entity::ReplicantInstance) + { + SetPerformance(&HeatWatcher::WatchSimulation); + } + Check_Fpu(); } @@ -804,13 +810,39 @@ void } // -// Per-frame: resolve the watched subsystem, read its temperature, drive the -// 3-level alarm. Not yet reconstructed. +// @004aeac4 -- the registered Performance: resolve the watched subsystem, +// read its temperature, drive the 3-level alarm. The binary dereferences +// the watched link unconditionally (it always binds on a master node); the +// null guard covers replicants and bring-up, holding NormalHeat. // void - HeatWatcher::WatchSimulation(Scalar) + HeatWatcher::WatchSimulation(Scalar /* time_slice */) { - Fail("HeatWatcher::WatchSimulation -- heat.cpp not yet reconstructed"); + Check(this); + + HeatableSubsystem + *watched = (HeatableSubsystem *)watchedLink.Resolve(); + if (watched == NULL) + { + heatAlarm.SetLevel(0); + return; + } + + Scalar + temperature = watched->CurrentTemperatureOf(); + + if (temperature > failureTemperature) + { + heatAlarm.SetLevel(2); // FailureHeat + } + else if (temperature > degradationTemperature) + { + heatAlarm.SetLevel(1); // DegradationHeat + } + else + { + heatAlarm.SetLevel(0); // NormalHeat + } } //########################################################################### diff --git a/restoration/source410/BT/HEAT.HPP b/restoration/source410/BT/HEAT.HPP index defd1a6b..d5f1aa56 100644 --- a/restoration/source410/BT/HEAT.HPP +++ b/restoration/source410/BT/HEAT.HPP @@ -117,6 +117,11 @@ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Local Data // + public: + Scalar + CurrentTemperatureOf() + { Check(this); return currentTemperature; } + protected: Scalar currentTemperature; Scalar heatLoad; @@ -204,6 +209,19 @@ // virtual void DeathReset(Logical full_reset); + + // + // The class-typed Performance shim, the same shape every sibling + // subsystem carries (the base takes the Subsystem-typed pointer). + // + typedef void + (HeatWatcher::*Performance)(Scalar time_slice); + void + SetPerformance(Performance performance) + { + Check(this); + activePerformance = (Simulation::Performance)performance; + } void WatchSimulation(Scalar time_slice); diff --git a/restoration/source410/BT/POWERSUB.CPP b/restoration/source410/BT/POWERSUB.CPP index 2ebf53e2..dc27c290 100644 --- a/restoration/source410/BT/POWERSUB.CPP +++ b/restoration/source410/BT/POWERSUB.CPP @@ -794,7 +794,15 @@ PowerWatcher::PowerWatcher( // constant is a tuning value (stored 1:1 here until located). // minVoltage = subsystem_resource->minVoltagePercent; - Check_Fpu(); + // + // Install the per-frame watchdog on the master instance. + // + if (owner->GetInstance() != Entity::ReplicantInstance) + { + SetPerformance(&PowerWatcher::Simulation); + } + +Check_Fpu(); } PowerWatcher::~PowerWatcher() @@ -830,10 +838,48 @@ void } // -// Per-frame supply-voltage watchdog. Not yet reconstructed. +// @004b181c -- the registered Performance wrapper; the body is UpdateWatch. +// +// The watchdog MIRRORS the watched subsystem's electrical state -- this is +// what drives ElectricalStateLevel()==Ready on the Torso (the twist-rate +// power gate) and the searchlight/thermal-sight voltage reads -- with one +// override: a subsystem reading Ready off a SAGGING source (measured +// voltage at or under minVoltage of the source's rating) shows level 1, +// the brownout. // void - PowerWatcher::Simulation(Scalar) + PowerWatcher::UpdateWatch() { - Fail("PowerWatcher::Simulation -- powersub.cpp not yet reconstructed"); + Check(this); + + WatchSimulation(0.0f); // the heat-alarm mirror first + + PoweredSubsystem + *watched = (PoweredSubsystem *)watchedLink.Resolve(); + if (watched == NULL) + { + watchdogAlarm.SetLevel(0); + return; + } + + unsigned + level = watched->GetVoltageState(); + watchdogAlarm.SetLevel(level); + + Generator + *source = (Generator *)watched->ResolveVoltageSource(); + if ( + level == PoweredSubsystem::Ready && + source != NULL && + source->MeasuredVoltage() <= minVoltage * source->RatedVoltageOf() + ) + { + watchdogAlarm.SetLevel(1); // the brownout + } +} + +void + PowerWatcher::Simulation(Scalar /* time_slice */) +{ + UpdateWatch(); } diff --git a/restoration/source410/BT/POWERSUB.HPP b/restoration/source410/BT/POWERSUB.HPP index 72917e2e..99583c58 100644 --- a/restoration/source410/BT/POWERSUB.HPP +++ b/restoration/source410/BT/POWERSUB.HPP @@ -311,6 +311,28 @@ ); ~PowerWatcher(); + public: + // + // The per-frame watch body (@004b181c's Performance calls it): mirror + // the watched subsystem's electrical state, with the brownout + // override. + // + + // + // The class-typed Performance shim, the same shape every sibling + // subsystem carries (the base takes the Subsystem-typed pointer). + // + typedef void + (PowerWatcher::*Performance)(Scalar time_slice); + void + SetPerformance(Performance performance) + { + Check(this); + activePerformance = (Simulation::Performance)performance; + } + void + UpdateWatch(); + protected: Scalar minVoltage; AlarmIndicator watchdogAlarm;