Three design improvements deferred from #90. None changes behaviour a user can see; each makes the code simpler to hold in your head. Sequenced deliberately — consolidate state first, then extract — because splitting the device class before its state is consolidated would just redistribute the complexity.
1. One owner for typed settings (retire _displaySettings)
FanatecDeviceSettings holds the typed snapshot, but display values are mirrored field-by-field into a mutable _displaySettings object that the display and ITM drivers read every frame — and the Screen panel edits that mirror directly, outside the owner's lock. The mirror is the source of both races listed under "Known gaps" in docs/device-settings-lifecycle.md.
The change: bind the Screen panel to the owner (the Tuning panel already is), have drivers read one immutable snapshot per frame, then delete _displaySettings, the Changed event and the mirroring. Removes the documented races rather than narrowing them.
2. Split runtime output out of FanatecWheelDeviceInstance
The instance currently owns SimHub lifecycle adaptation, generation tracking, connection transitions, display/ITM/LED driver lifetimes, persistence integration, panel construction and the Enabled presentation layer — roughly 750 lines. Extract the runtime half (bound generation, connection/output transitions, driver lifetimes, the output gate) into a WheelOutputSession, leaving the instance as the SimHub-facing adapter.
3. Narrow IFanatecLedModuleHost per consumer
After (2), persistence needs only apply/capture/reset and the output session only display/status/stop. Split the interface into those two views, both implemented by the same concrete host — no new objects.
Three design improvements deferred from #90. None changes behaviour a user can see; each makes the code simpler to hold in your head. Sequenced deliberately — consolidate state first, then extract — because splitting the device class before its state is consolidated would just redistribute the complexity.
1. One owner for typed settings (retire
_displaySettings)FanatecDeviceSettingsholds the typed snapshot, but display values are mirrored field-by-field into a mutable_displaySettingsobject that the display and ITM drivers read every frame — and the Screen panel edits that mirror directly, outside the owner's lock. The mirror is the source of both races listed under "Known gaps" indocs/device-settings-lifecycle.md.The change: bind the Screen panel to the owner (the Tuning panel already is), have drivers read one immutable snapshot per frame, then delete
_displaySettings, theChangedevent and the mirroring. Removes the documented races rather than narrowing them.2. Split runtime output out of
FanatecWheelDeviceInstanceThe instance currently owns SimHub lifecycle adaptation, generation tracking, connection transitions, display/ITM/LED driver lifetimes, persistence integration, panel construction and the
Enabledpresentation layer — roughly 750 lines. Extract the runtime half (bound generation, connection/output transitions, driver lifetimes, the output gate) into aWheelOutputSession, leaving the instance as the SimHub-facing adapter.3. Narrow
IFanatecLedModuleHostper consumerAfter (2), persistence needs only apply/capture/reset and the output session only display/status/stop. Split the interface into those two views, both implemented by the same concrete host — no new objects.