Files
TeslaRel410/restoration/source410/BT/POWERSUB.CPP
T
CydandClaude Fable 5 4cf09917ce BT410 5.3.57: the crash is deterministic, and the real blocker is the load gate
The previous commit's attribution was wrong and is corrected in the roadmap.

WRONG: 'the crash is the skeleton walk's' rested on 0 crashes in 2 runs with
the walk disabled -- a 25% coin flip presented as evidence.  The oldest
preserved dump has no [skl] line and ends on the old 'couldn't figure out how
to MakeEntityRenderables' fallback, so it predates the walk and faults
identically.

WRONG: 'it lands at different points each time'.  Every dump carries the same
numbers to the byte (cr2 7000FA64, EIP 66D9).  What varies is how far the log
gets, not where the fault is.

Resolving the address through btl4opt.map names it exactly:
EulerAngles::operator=(const LinearMatrix&)+0x19, a read through the matrix
reference.  A binary scan finds all three call sites of that operator pass a
stack local, so the 1.79GB pointer still has no static explanation -- that is
now its own open item rather than a guess.

Two theories killed cleanly by the new BT_STACK_LOG probe and a binary scan:
ESP drift (drift=0 over 2002 frames; exactly one callee-cleans function exists
in the whole binary) and an undersized stack (our PE and the shipped one have
identical 1MB/8K geometry).

What the probe found matters more: the application is parked in state=2, which
is LoadingMission, not WaitingForLaunch.  Priority 0 is where the interest
manager queues renderer events during load, the gate needs that priority empty,
and it never empties -- so the mission never launches and the renderer holds a
blank screen by design.  The fault arrives ~30s into that wait.  Runs that DO
launch never print a single [launch] line.  So 'crashes half the time' and
'hangs during load' are one event seen twice.

A 15x disagreement between two clocks looked like a reconstruction slip --
BTL4.CPP passes GetTicksPerSecond() where ApplicationManager wants a frame
rate.  Checked against the shipped binary before touching it: same instruction
sequence, same kind of static float pushed.  Authentic.  Documented so nobody
'fixes' it.

Also swept every subsystem DefaultData against its real C++ base.  Fifteen
chain past their immediate parent, but fourteen skip only classes that add no
handlers and no attributes, and no class's attribute-ID base disagrees with its
index chain -- so there are no gap slots there.  The one real defect: Generator
is a HeatSink but chained to Subsystem::MessageHandlers, so it ignored every
ToggleCooling message.  Fixed (compiles next build).

Tooling: podrun.sh stages the build over BTL4REC.EXE, exports the host-side VPX
board env -- without which the run dies at the iserver handshake rather than
merely rendering nothing -- and archives every run's log, marking it -CRASH
when it faulted.  Before this the only preserved dump was an accident, on a rig
where each run costs four minutes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 21:41:38 -05:00

840 lines
24 KiB
C++

//===========================================================================//
// File: powersub.cpp //
// Project: BattleTech Brick: Mech subsystems //
// Contents: PoweredSubsystem -- a HeatSink drawing electrical power //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include <bt.hpp>
#pragma hdrstop
#if !defined(POWERSUB_HPP)
# include <powersub.hpp>
#endif
#if !defined(MECH_HPP)
# include <mech.hpp>
#endif
//
//#############################################################################
// Shared data support
//#############################################################################
//
Derivation
PoweredSubsystem::ClassDerivations(
HeatSink::ClassDerivations,
"PoweredSubsystem"
);
//
//#############################################################################
// The cockpit generator buttons (binary handler table @0x50F4EC, ids 4-8),
// chained onto the HeatSink set (id 3 ToggleCooling) so every powered
// subsystem's dispatch reaches the Eng-page Coolant button too. Static-init
// order is safe under the authentic .MAK lib order (heat precedes powersub).
//#############################################################################
//
const PoweredSubsystem::HandlerEntry
PoweredSubsystem::MessageHandlerEntries[] =
{
MESSAGE_ENTRY(PoweredSubsystem, SelectGeneratorA), // id 4 @004b099c
MESSAGE_ENTRY(PoweredSubsystem, SelectGeneratorB), // id 5 @004b09e4
MESSAGE_ENTRY(PoweredSubsystem, SelectGeneratorC), // id 6 @004b0a2c
MESSAGE_ENTRY(PoweredSubsystem, SelectGeneratorD), // id 7 @004b0a74
MESSAGE_ENTRY(PoweredSubsystem, ToggleGeneratorMode) // id 8 @004b0abc
};
PoweredSubsystem::MessageHandlerSet
PoweredSubsystem::MessageHandlers(
ELEMENTS(PoweredSubsystem::MessageHandlerEntries),
PoweredSubsystem::MessageHandlerEntries,
HeatSink::MessageHandlers
);
//
//#############################################################################
// Attribute tables (cockpit binding by name).
//#############################################################################
//
const PoweredSubsystem::IndexEntry
PoweredSubsystem::AttributePointers[]=
{
ATTRIBUTE_ENTRY(PoweredSubsystem, InputVoltage, inputVoltage),
ATTRIBUTE_ENTRY(PoweredSubsystem, OutputVoltage, outputVoltage),
ATTRIBUTE_ENTRY(PoweredSubsystem, RatedVoltage, ratedVoltage),
ATTRIBUTE_ENTRY(PoweredSubsystem, VoltageState, electricalStateAlarm),
ATTRIBUTE_ENTRY(PoweredSubsystem, ConnectMode, modeAlarm)
};
PoweredSubsystem::AttributeIndexSet
PoweredSubsystem::AttributeIndex(
ELEMENTS(PoweredSubsystem::AttributePointers),
PoweredSubsystem::AttributePointers,
HeatSink::AttributeIndex
);
PoweredSubsystem::SharedData
PoweredSubsystem::DefaultData(
PoweredSubsystem::ClassDerivations,
PoweredSubsystem::MessageHandlers,
PoweredSubsystem::AttributeIndex,
Subsystem::StateCount
);
//
//#############################################################################
// A HeatSink that draws electrical power from a generator (binary ctor
// @004b0f74). Resolves the "VoltageSource" roster index to the powering
// generator, attaches the tap, and primes the electrical state machine. The
// voltageSourceIndex indexes the owner mech's SUBSYSTEM ROSTER (the same
// index space the AmmoBin link uses) -- the roster slots ahead of this
// subsystem are already constructed by the segment walk, and the shipped
// stream orders the generators first.
//#############################################################################
//
PoweredSubsystem::PoweredSubsystem(
Mech *owner,
int subsystem_ID,
SubsystemResource *subsystem_resource,
SharedData &shared_data
):
HeatSink(owner, subsystem_ID, subsystem_resource, shared_data),
voltageSource(),
electricalStateAlarm(5),
modeAlarm(3)
{
Check(owner);
Check_Pointer(subsystem_resource);
inputVoltage = 0.0f;
outputVoltage = 0.0f;
ratedVoltage = 0.0f;
thermalResistivityCoefficient = subsystem_resource->thermalResistivityCoefficient;
startTime = subsystem_resource->startTime;
//
// Cache the authored aux-screen block NOW -- the resource memory dies
// with the Mech ctor's stream buffer.
//
auxScreenNumber = subsystem_resource->auxScreenNumber;
auxScreenPlacement = subsystem_resource->auxScreenPlacement;
Str_Copy(auxScreenLabel, subsystem_resource->auxScreenLabel,
sizeof(auxScreenLabel));
Str_Copy(engScreenLabel, subsystem_resource->engScreenLabel,
sizeof(engScreenLabel));
startTimer = startTime;
voltageScale = 1.0f;
//
// Resolve the voltage source from the roster and attach the tap.
//
Subsystem *source = NULL;
if (subsystem_resource->voltageSourceIndex >= 0
&& subsystem_resource->voltageSourceIndex < owner->GetSubsystemCount())
{
source = owner->GetSubsystem(subsystem_resource->voltageSourceIndex);
}
if (source != NULL)
{
AttachToVoltageSource(source);
}
if (getenv("BT_POWER_LOG"))
{
DEBUG_STREAM << "[power] '" << GetName()
<< "' srcIdx=" << subsystem_resource->voltageSourceIndex << " -> ";
if (source != NULL)
{
DEBUG_STREAM << source->GetName();
}
else
{
DEBUG_STREAM << "<none>";
}
DEBUG_STREAM << " startTime=" << startTime << endl << flush;
}
electricalStateAlarm.SetLevel(Ready);
modeAlarm.SetLevel(Connected);
//
// A master (non-replicant) instance runs the per-frame electrical
// simulation. Derived subsystems (the weapons, Sensor, ...) override with
// their own Performance in their ctors, each of which chains this step.
//
if (owner->GetInstance() != Entity::ReplicantInstance)
{
SetPerformance(&PoweredSubsystem::PoweredSubsystemSimulation);
}
Check_Fpu();
}
//
//#############################################################################
//#############################################################################
//
PoweredSubsystem::~PoweredSubsystem()
{
}
//
//#############################################################################
//#############################################################################
//
void
PoweredSubsystem::ResetToInitialState(Logical powered)
{
Check(this);
HeatSink::ResetToInitialState(powered);
inputVoltage = 0.0f;
outputVoltage = 0.0f;
electricalStateAlarm.SetLevel(0);
modeAlarm.SetLevel(0);
}
//
//#############################################################################
//#############################################################################
//
Logical
PoweredSubsystem::TestClass(Mech &)
{
return True;
}
Logical
PoweredSubsystem::TestInstance() const
{
return IsDerivedFrom(ClassDerivations);
}
//
//#############################################################################
// AttachToVoltageSource Link this subsystem to its powering generator
// (binary @004b0dd8): take a tap on the generator (-1 when every tap is
// taken) and hold the live connection.
//#############################################################################
//
int
PoweredSubsystem::AttachToVoltageSource(Subsystem *source)
{
Check(this);
Check(source);
Generator *generator = (Generator *)source;
if (generator->TapVoltageSource() != 0)
{
return -1;
}
voltageSource.Add(source);
inputVoltage = generator->MeasuredVoltage();
return 0;
}
//
//#############################################################################
// DetachFromVoltageSource (binary @004b0e30): release the tap on the current
// source and clear the connection.
//#############################################################################
//
void
PoweredSubsystem::DetachFromVoltageSource()
{
Check(this);
Generator *source = (Generator *)voltageSource.Resolve();
if (source != NULL)
{
source->UntapVoltageSource();
voltageSource.Clear();
}
}
//
//#############################################################################
// FindGeneratorByNumber (binary @004b0b18): walk the owner's roster for the
// Generator whose authored generatorNumber matches (1=A .. 4=D).
//#############################################################################
//
Subsystem*
PoweredSubsystem::FindGeneratorByNumber(int generator_number)
{
Check(this);
for (int slot = 2; slot < owner->GetSubsystemCount(); ++slot)
{
Subsystem *sub = owner->GetSubsystem(slot);
if (sub != NULL
&& sub->IsDerivedFrom(Generator::ClassDerivations)
&& ((Generator *)sub)->GetGeneratorNumber() == generator_number)
{
return sub;
}
}
return NULL;
}
//
//#############################################################################
// SelectGenerator -- the manual re-tap: release the current source, tap
// generator N, drop the connect mode back to Connected (a manual selection
// ends any auto-hunt). The binary dereferences the find unguarded (authored
// mechs always carry A-D); we skip loud instead.
//#############################################################################
//
void
PoweredSubsystem::SelectGenerator(int generator_number)
{
Check(this);
Subsystem *generator = FindGeneratorByNumber(generator_number);
if (generator == NULL)
{
if (getenv("BT_MECH_LOG") || getenv("BT_POWER_LOG"))
{
DEBUG_STREAM << "[gensel] '" << GetName()
<< "' -> generator " << generator_number
<< " NOT FOUND" << endl << flush;
}
return;
}
DetachFromVoltageSource();
int tap = AttachToVoltageSource(generator);
modeAlarm.SetLevel(Connected);
if (getenv("BT_MECH_LOG") || getenv("BT_POWER_LOG"))
{
DEBUG_STREAM << "[gensel] '" << GetName()
<< "' -> '" << generator->GetName() << "'"
<< ((tap >= 0) ? " (tapped)" : " (REFUSED: no spare tap)")
<< endl << flush;
}
}
//
//#############################################################################
// The cockpit button handlers (ids 4-8). Press-only (dataContents > 0),
// novice-locked -- a novice cockpit's generator panel is inert.
//#############################################################################
//
void
PoweredSubsystem::SelectGeneratorAMessageHandler(
ReceiverDataMessageOf<int> *message)
{
Check(this);
if (!NoviceLockout() && message->dataContents > 0)
{
SelectGenerator(1);
}
}
void
PoweredSubsystem::SelectGeneratorBMessageHandler(
ReceiverDataMessageOf<int> *message)
{
Check(this);
if (!NoviceLockout() && message->dataContents > 0)
{
SelectGenerator(2);
}
}
void
PoweredSubsystem::SelectGeneratorCMessageHandler(
ReceiverDataMessageOf<int> *message)
{
Check(this);
if (!NoviceLockout() && message->dataContents > 0)
{
SelectGenerator(3);
}
}
void
PoweredSubsystem::SelectGeneratorDMessageHandler(
ReceiverDataMessageOf<int> *message)
{
Check(this);
if (!NoviceLockout() && message->dataContents > 0)
{
SelectGenerator(4);
}
}
//
//#############################################################################
// ToggleGeneratorMode (id 8, binary @004b0abc): cycle Manual -> AutoConnect
// -> (detach +) Manual. The auto-hunt itself (a shorted/dead source makes
// the subsystem walk for a live generator) lives in
// PoweredSubsystemSimulation -- a later brick.
//#############################################################################
//
void
PoweredSubsystem::ToggleGeneratorModeMessageHandler(
ReceiverDataMessageOf<int> *message)
{
Check(this);
if (NoviceLockout() || message->dataContents <= 0)
{
return;
}
if ((unsigned)modeAlarm.GetLevel() < (unsigned)AutoConnect)
{
modeAlarm.SetLevel(AutoConnect);
}
else if (modeAlarm.GetLevel() == AutoConnect)
{
DetachFromVoltageSource();
modeAlarm.SetLevel(ManualConnect);
}
if (getenv("BT_MECH_LOG") || getenv("BT_POWER_LOG"))
{
DEBUG_STREAM << "[gensel] '" << GetName()
<< "' mode -> " << modeAlarm.GetLevel() << endl << flush;
}
}
//
//#############################################################################
// ChargeTimeScale -- the heat/firepower feedback (binary @004b0d50):
// rise = max(0, sourceTemperature - sourceStartingTemperature)
// scale = max(voltageScale,
// (thermalResistivityCoefficient * rise + 1) * voltageScale)
// A hot generator stretches the exponential charge constant, so recharging
// slows exactly when the electrical plant is cooking.
//#############################################################################
//
Scalar
PoweredSubsystem::ChargeTimeScale()
{
Check(this);
Generator *source = (Generator *)voltageSource.Resolve();
if (source == NULL)
{
return voltageScale;
}
Scalar rise = source->CurrentTemperatureOf()
- source->StartingTemperatureOf();
if (rise < 0.0f)
{
rise = 0.0f;
}
Scalar stretched =
(thermalResistivityCoefficient * rise + 1.0f) * voltageScale;
return (stretched > voltageScale) ? stretched : voltageScale;
}
//
//#############################################################################
// DeathReset -- respawn restore for the powered chain and the generators.
//#############################################################################
//
void
PoweredSubsystem::DeathReset(Logical full_reset)
{
Check(this);
MechSubsystem::DeathReset(full_reset);
ResetToInitialState(True);
//
// ResetToInitialState drops the connect-mode indicator to Manual; the
// respawned subsystem keeps its live tap, so the mode is Connected
// (the ctor's spawn state).
//
modeAlarm.SetLevel(Connected);
}
void
Generator::DeathReset(Logical full_reset)
{
Check(this);
MechSubsystem::DeathReset(full_reset);
//
// PRESERVE the tap accounting across the reset: consumers keep their
// voltageSource links through respawn (nothing detaches), so zeroing
// currentTapCount here would desync the maxTapCount invariant and let
// post-respawn SelectGenerator presses oversubscribe the generator
// (caught by the 5.3.24 adversarial review).
//
int live_taps = currentTapCount;
ResetToInitialState();
currentTapCount = live_taps;
}
//
//#############################################################################
// ForceShortRecovery (binary @004b11bc): on a short event, drive the powering
// generator to Shorted and clear its output; GeneratorSimulation then runs
// the short-recovery timer back to Ready. Both @4ac9c8 calls in the binary
// are the not-novice experience predicate (this part and its source share
// the same mech, hence the same player) -- novice cockpits never see
// electrical shorts.
//#############################################################################
//
void
PoweredSubsystem::ForceShortRecovery()
{
Check(this);
if (NoviceLockout())
{
return;
}
Generator *source = (Generator *)voltageSource.Resolve();
if (source != NULL)
{
source->ForceShort();
if (getenv("BT_POWER_LOG") || getenv("BT_MECH_LOG"))
{
DEBUG_STREAM << "[short] '" << source->GetName()
<< "' SHORTED by special damage" << endl << flush;
}
}
}
//
//#############################################################################
// PoweredSubsystemSimulation -- the per-frame electrical step (binary
// @004b0bd0). Runs the HeatSink thermal step, then advances the electrical
// state machine from the state of the powering generator.
//
// PARTIAL: the AutoConnect replacement-generator hunt (modeAlarm AutoConnect +
// the status-flag gate) joins with the damage wave.
//#############################################################################
//
void
PoweredSubsystem::PoweredSubsystemSimulation(Scalar time_slice)
{
Check(this);
HeatSink::HeatSinkSimulation(time_slice);
Generator *source = (Generator *)voltageSource.Resolve();
if (source == NULL)
{
electricalStateAlarm.SetLevel(NoVoltage);
}
else
{
if (source->GeneratorStateOf() == Generator::GeneratorShorted)
{
electricalStateAlarm.SetLevel(Shorted);
}
if (source->GeneratorStateOf() == Generator::GeneratorStarting
|| source->GeneratorStateOf() == Generator::GeneratorFailed)
{
electricalStateAlarm.SetLevel(GeneratorOff);
}
}
switch (electricalStateAlarm.GetLevel())
{
case Starting:
startTimer += time_slice;
if (startTime <= startTimer)
{
electricalStateAlarm.SetLevel(Ready);
}
break;
case NoVoltage:
if (source != NULL)
{
electricalStateAlarm.SetLevel(Starting);
startTimer = 0.0f;
}
break;
case Shorted:
case GeneratorOff:
if (source != NULL
&& source->GeneratorStateOf() == Generator::GeneratorReady)
{
electricalStateAlarm.SetLevel(Starting);
startTimer = 0.0f;
}
break;
}
if (source != NULL)
{
inputVoltage = source->MeasuredVoltage();
}
Check_Fpu();
}
//###########################################################################
//############################## Generator #############################
//###########################################################################
//
//#############################################################################
// Shared data support
//#############################################################################
//
Derivation
Generator::ClassDerivations(
HeatSink::ClassDerivations,
"Generator"
);
const Generator::IndexEntry
Generator::AttributePointers[]=
{
ATTRIBUTE_ENTRY(Generator, OutputVoltage, outputVoltage),
ATTRIBUTE_ENTRY(Generator, RatedVoltage, ratedVoltage),
ATTRIBUTE_ENTRY(Generator, GeneratorNumber, generatorNumber),
ATTRIBUTE_ENTRY(Generator, GeneratorState, stateAlarm),
{ (int)Generator::GeneratorOnAttributeID2, "GeneratorOn",
(Simulation::AttributePointer)&Generator::generatorOn }
};
Generator::AttributeIndexSet
Generator::AttributeIndex(
ELEMENTS(Generator::AttributePointers),
Generator::AttributePointers,
HeatSink::AttributeIndex
);
//
// A Generator IS a HeatSink, and HeatSink is one of only two classes in the
// subsystem tree that defines a message handler of its own -- ToggleCooling.
// Chaining to Subsystem::MessageHandlers here skipped it, so a generator
// silently ignored every cooling toggle it was sent. Found by sweeping every
// DefaultData against its real C++ base: fifteen classes chain past their
// immediate parent, but the other fourteen skip only classes that add no
// handlers and no attributes, which makes them equivalent (and probably
// authentic). This one loses a handler, so it is a real defect.
//
Generator::SharedData
Generator::DefaultData(
Generator::ClassDerivations,
HeatSink::MessageHandlers,
Generator::AttributeIndex,
Subsystem::StateCount
);
//
//#############################################################################
// The generator -- the voltage source loads tap.
//#############################################################################
//
Generator::Generator(
Mech *owner,
int subsystem_ID,
SubsystemResource *subsystem_resource,
SharedData &shared_data
):
HeatSink(owner, subsystem_ID, subsystem_resource, shared_data),
stateAlarm(5)
{
Check(owner);
Check_Pointer(subsystem_resource);
ratedVoltage = subsystem_resource->ratedVoltage;
outputVoltage = ratedVoltage;
maxTapCount = subsystem_resource->maxTapCount;
currentTapCount = 0;
percentVoltageAvailable = 1.0f;
startTime = subsystem_resource->startTime;
startTimer = startTime;
stateAlarm.SetLevel(GeneratorReady);
generatorOn = 1;
shortRecoveryTime = subsystem_resource->shortRecoveryTime;
shortTimer = shortRecoveryTime;
//
// Generator number from the last character of the segment name
// ('A' -> 1, 'B' -> 2, ...).
//
const char *name = GetName();
generatorNumber = name[strlen(name) - 1] - 0x40;
//
// Install the generator's per-frame electrical Performance.
//
if (owner->GetInstance() != Entity::ReplicantInstance)
{
SetPerformance(&Generator::GeneratorSimulation);
}
Check_Fpu();
}
//
//#############################################################################
//#############################################################################
//
Generator::~Generator()
{
}
//
//#############################################################################
//#############################################################################
//
Logical
Generator::TestClass(Mech &)
{
return True;
}
Logical
Generator::TestInstance() const
{
return IsDerivedFrom(ClassDerivations);
}
//
//#############################################################################
//#############################################################################
//
void
Generator::ResetToInitialState()
{
Check(this);
HeatSink::ResetToInitialState(True);
outputVoltage = ratedVoltage;
currentTapCount = 0;
percentVoltageAvailable = 1.0f;
startTimer = startTime;
shortTimer = shortRecoveryTime;
generatorOn = 1;
stateAlarm.SetLevel(GeneratorReady);
}
//
//#############################################################################
// GeneratorSimulation -- the generator's per-frame step (PARTIAL). Runs the
// HeatSink thermal step and the start/short-recovery timers. The authentic
// load model (output voltage sag under tap load / I^2R self-heat feeding the
// charge integration) joins with the electrical-charge wave
// (TrackSeekVoltage). A healthy generator holds GeneratorReady at its rated
// voltage.
//#############################################################################
//
void
Generator::GeneratorSimulation(Scalar time_slice)
{
Check(this);
HeatSink::HeatSinkSimulation(time_slice);
switch (stateAlarm.GetLevel())
{
case GeneratorStarting:
startTimer += time_slice;
if (startTime <= startTimer)
{
stateAlarm.SetLevel(GeneratorReady);
outputVoltage = ratedVoltage;
}
break;
case GeneratorShorted:
shortTimer -= time_slice;
if (shortTimer <= 0.0f)
{
shortTimer = shortRecoveryTime;
stateAlarm.SetLevel(GeneratorStarting);
startTimer = 0.0f;
}
break;
default:
break;
}
Check_Fpu();
}
//###########################################################################
//############################ PowerWatcher ############################
//###########################################################################
Derivation
PowerWatcher::ClassDerivations(
HeatWatcher::ClassDerivations,
"PowerWatcher"
);
PowerWatcher::SharedData
PowerWatcher::DefaultData(
PowerWatcher::ClassDerivations,
Subsystem::MessageHandlers,
Subsystem::AttributeIndex,
Subsystem::StateCount
);
PowerWatcher::PowerWatcher(
Mech *owner,
int subsystem_ID,
SubsystemResource *subsystem_resource,
SharedData &shared_data
):
HeatWatcher(owner, subsystem_ID, subsystem_resource, shared_data),
watchdogAlarm(5)
{
Check(owner);
Check_Pointer(subsystem_resource);
//
// minVoltage is a scaled fraction of the watched supply; the exact scale
// constant is a tuning value (stored 1:1 here until located).
//
minVoltage = subsystem_resource->minVoltagePercent;
Check_Fpu();
}
PowerWatcher::~PowerWatcher()
{
}
Logical
PowerWatcher::TestClass(Mech &)
{
return True;
}
Logical
PowerWatcher::TestInstance() const
{
return IsDerivedFrom(ClassDerivations);
}
void
PowerWatcher::ResetToInitialState(Logical powered)
{
Check(this);
HeatWatcher::ResetToInitialState(powered);
watchdogAlarm.SetLevel(0);
}
void
PowerWatcher::DeathReset(Logical full_reset)
{
Check(this);
MechSubsystem::DeathReset(full_reset);
ResetToInitialState(True);
}
//
// Per-frame supply-voltage watchdog. Not yet reconstructed.
//
void
PowerWatcher::Simulation(Scalar)
{
Fail("PowerWatcher::Simulation -- powersub.cpp not yet reconstructed");
}