Controls: the .CTL positional-id off-by-one -- CONFIRMED live and FIXED (step 2a)

The streamed L4 mapping resource carries the binary's positional attribute ids
(stick=3, throttle=4) but MechControlsMapper chained from Subsystem::NextAttributeID
== 2 -- every streamed record resolved ONE MEMBER LATE (attr 4 -> pedalsPosition,
verified via the new permanent BT_CTRLMAP_LOG diagnostic in CreateStreamedMappings).
A latent real-pod bug: the serial RIO throttle would drive the pedals member; the
dev keyboard bridge masked it. Fix per the mechweap/mech attrPad idiom: ids pinned
to the binary numbering, id-2 gap padded, static_assert-locked. Torso + weapon
chains verified already aligned. Regression: BT_FORCE_THROTTLE headless walk clean
(speedDemand=61.501 through authentic InterpretControls, gait cycles, no faults).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-17 21:59:51 -05:00
co-authored by Claude Fable 5
parent 3f49915d76
commit b26e8205e3
7 changed files with 113 additions and 24 deletions
+19
View File
@@ -249,9 +249,28 @@ MechControlsMapper::MessageHandlerSet&
// Attribute table @0050efd0. (Recorded offsets carry the engine scalar tag,
// e.g. 0x115 -> stickPosition @0x114.)
//
// Step-2a fix (2026-07-17): ids are pinned to the binary numbering (stick=3)
// and the id-2 chain gap is padded -- see the enum note in mechmppr.hpp.
// Locked so a future engine-base attribute publish shows up here instead of
// silently double-assigning ids:
static_assert((int)Subsystem::NextAttributeID
== (int)MechControlsMapper::MechControlsMapperPadFirstAttributeID,
"MechControlsMapper pad base != Subsystem::NextAttributeID -- re-count the pad");
static_assert((int)MechControlsMapper::StickPositionAttributeID == 3
&& (int)MechControlsMapper::PilotArrayAttributeID == 0x16,
"MechControlsMapper ids drifted off the binary .CTL numbering");
const MechControlsMapper::IndexEntry
MechControlsMapper::AttributePointers[]=
{
//
// The PAD fills the chain-vs-binary id gap (id 2) with a valid, named,
// never-bound entry (unique name, harmless target) so Find()'s strcmp
// walk never reads an uninitialized slot.
//
{ (int)MechControlsMapper::MechControlsMapperPadFirstAttributeID,
"MechControlsMapperPad02",
(Simulation::AttributePointer)&MechControlsMapper::throttlePosition },
ATTRIBUTE_ENTRY(MechControlsMapper, StickPosition, stickPosition), // 0x114
ATTRIBUTE_ENTRY(MechControlsMapper, ThrottlePosition, throttlePosition), // 0x11C
ATTRIBUTE_ENTRY(MechControlsMapper, PedalsPosition, pedalsPosition), // 0x120