Repository navigation
refactor(io): split FDA TxDevice/HVController from RUO subclasses - #47
Merged
peterhollender merged 2 commits intoOct 9, 2026
Conversation
DeviceInterface (the FDA entry point) handed out TxDevice and HVController objects that still carried every RUO command (register writes, set_voltage, set_trigger, DFU, config writes, async mode). Restricting the interface alone left them one attribute away. - TxDevice / HVController are now the FDA components. They expose only identity, telemetry, trigger read/start/stop and stored-preset methods, and carry a SUPPORTED_COMMANDS whitelist (TX_FDA_COMMANDS / HV_FDA_COMMANDS) so any other opcode raises ValueError before it reaches the UART. - LIFUTxDevice / LIFUHVController subclass them and add the RUO methods; shared RUO globals (start/stop, send_async, echo, LED, reset, DFU, write_config) move to LIFUComponentMixin. - Only LIFUInterface creates the LIFU classes (_create_devices hook). - Async mode is RUO-only: DeviceInterface drops run_async, start_monitoring/stop_monitoring and the async_mode calls in start/stop_sonication; LIFUInterface keeps them. - The simulator mirrors the split (SimulatedLIFUTxDevice, SimulatedLIFUHVController; monitoring and async_mode on SimulatedLIFUInterface). - RUO unit tests now use the LIFU classes; new tests cover the class surfaces, the opcode whitelist and which classes each interface builds. test_tx_device.py imports HW_ID_DATA_LENGTH from LIFUConfig so it collects again (three stale set_trigger tests remain, #44). Refs SR-002 Closes #43 Refs #44 Signed-off-by: Peter Hollender <peterhollender@gmail.com>
…tract test_23b/23c/23d asserted the 2.0.14 behaviour (auto-filling a zero train interval and bumping it past the pulse period for firmware <= 2.0.3). Since 8b3eaf2 set_trigger sends 0 for back-to-back trains, and firmware <= 2.0.3 no longer needs support. Replace them with tests of the current contract: zero or zero-gap intervals are sent as 0; longer intervals pass through; a short interval is sent as 0 for a single train and raises ValueError (with nothing sent) for multiple trains. Refs SR-002 Closes #44 Signed-off-by: Peter Hollender <peterhollender@gmail.com>
peterhollender
approved these changes
Oct 9, 2026
peterhollender
merged commit Oct 9, 2026
7051a09
into
feature/SR-002-device-interface
2 checks passed
peterhollender
added a commit
that referenced
this pull request
Oct 9, 2026
* refactor(io): split FDA TxDevice/HVController from RUO subclasses DeviceInterface (the FDA entry point) handed out TxDevice and HVController objects that still carried every RUO command (register writes, set_voltage, set_trigger, DFU, config writes, async mode). Restricting the interface alone left them one attribute away. - TxDevice / HVController are now the FDA components. They expose only identity, telemetry, trigger read/start/stop and stored-preset methods, and carry a SUPPORTED_COMMANDS whitelist (TX_FDA_COMMANDS / HV_FDA_COMMANDS) so any other opcode raises ValueError before it reaches the UART. - LIFUTxDevice / LIFUHVController subclass them and add the RUO methods; shared RUO globals (start/stop, send_async, echo, LED, reset, DFU, write_config) move to LIFUComponentMixin. - Only LIFUInterface creates the LIFU classes (_create_devices hook). - Async mode is RUO-only: DeviceInterface drops run_async, start_monitoring/stop_monitoring and the async_mode calls in start/stop_sonication; LIFUInterface keeps them. - The simulator mirrors the split (SimulatedLIFUTxDevice, SimulatedLIFUHVController; monitoring and async_mode on SimulatedLIFUInterface). - RUO unit tests now use the LIFU classes; new tests cover the class surfaces, the opcode whitelist and which classes each interface builds. test_tx_device.py imports HW_ID_DATA_LENGTH from LIFUConfig so it collects again (three stale set_trigger tests remain, #44). Refs SR-002 Closes #43 Refs #44 Signed-off-by: Peter Hollender <peterhollender@gmail.com> * test(tx): update set_trigger train-interval tests to back-to-back contract test_23b/23c/23d asserted the 2.0.14 behaviour (auto-filling a zero train interval and bumping it past the pulse period for firmware <= 2.0.3). Since 8b3eaf2 set_trigger sends 0 for back-to-back trains, and firmware <= 2.0.3 no longer needs support. Replace them with tests of the current contract: zero or zero-gap intervals are sent as 0; longer intervals pass through; a short interval is sent as 0 for a single train and raises ValueError (with nothing sent) for multiple trains. Refs SR-002 Closes #44 Signed-off-by: Peter Hollender <peterhollender@gmail.com> --------- Signed-off-by: Peter Hollender <peterhollender@gmail.com> Co-authored-by: Peter Hollender <peterhollender@gmail.com> Signed-off-by: Peter (Agent) <phollender@openwater.cc>
peterhollender
added a commit
that referenced
this pull request
Oct 12, 2026
…erge fda_version (#45) * feat(preset): add simulated preset-integrity handshake endpoints (#38) * feat(preset): add simulated preset-integrity handshake endpoints Adds the SDK side of the SR-002 / SR-003 host<->firmware preset- integrity handshake described in the operator-interface repo's ``preset_pseudo_code.md``. **Simulated side (implemented):** * ``SimulatedTxDevice`` now takes an optional ``preset_flash: list[tuple[str, int]]`` in its constructor and exposes ``get_preset(preset_index) -> (id, settings_crc)``, ``set_preset(preset_index, sequence_duration_index, expected_crc)``, ``set_preset_flash(flash)``, and ``get_loaded_preset() -> (preset_index | None, duration_index | None)`` for test round-tripping. ``set_preset`` verifies the caller's ``expected_crc`` against the flash-baked ``settings_crc``, matching what real firmware will do once PROJ-04 lands. Mismatch raises ``ValueError`` -- this is the CRC-mismatch fault path the operator UI must annunciate per SR-003. * ``SimulatedLIFUInterface.__init__`` gains a ``preset_flash`` keyword that is forwarded to the underlying ``SimulatedTxDevice``. **Real side (stubbed):** * ``TxDevice.get_preset`` / ``TxDevice.set_preset`` are added as documented ``NotImplementedError`` stubs pointing at PROJ-04 for firmware work. Their docstrings direct testers to ``SimulatedLIFUInterface`` in the meantime. **Tests (new, ``unit-test/test_simulated_preset_flash.py``):** * Happy-path: ``get_preset`` returns flash entries; ``set_preset`` records selection on matching CRC; ``get_loaded_preset`` round-trips. * Rejection: ``set_preset`` on CRC mismatch / out-of-range index / negative duration index; ``get_preset`` out-of-range. * Flash reseed clears loaded selection. * ``SimulatedLIFUInterface.__init__`` forwards ``preset_flash`` to its txdevice. * ``TxDevice.get_preset`` / ``set_preset`` raise ``NotImplementedError`` mentioning PROJ-04. All 14 new tests pass. Full ``unit-test/`` run (excluding the pre-existing ``test_tx_device.py`` collection failure that is unrelated to this change): 92 passed. No firmware changes in this PR. The corresponding operator- interface work that consumes this API lands in OpenwaterHealth/openlifu-operator-interface as a follow-up (SR-002 / SR-003 UI wiring; part of PROJ-03 close-out). Signed-off-by: Peter Hollender <peterhollender@gmail.com> * feat(sim): add version mutators for firmware-compat simulation SimulatedTxDevice.set_version(version, module=0) and SimulatedHVController.set_version(version) let a debug UI or test flip the reported firmware version so the operator connector's check_firmware_compat can be driven down the incompatible-firmware path without shipping a separate `firmware_compatible` toggle. Single string per device today; can extend to per-module later. 3 new tests, 17 total in test_simulated_preset_flash.py. Signed-off-by: Peter Hollender <peterhollender@gmail.com> * refactor(sim): preset_flash holds machine_config dicts; implement update_firmware Two related SDK-side changes that support the SR-003 preset-integrity handshake per the operator-interface repo's ``preset_pseudo_code.md``: **1. preset_flash schema change (breaking)** The simulated flash-baked preset table now holds full ``machine_config`` dicts, not just ``(id, settings_crc)`` tuples. Each entry is ``(machine_config_dict, machine_config_crc)`` where: * ``machine_config_dict`` mirrors what the real firmware image would carry after PROJ-04 codegen -- specifically, includes an ``id`` and a ``settings_crc`` field the ``get_preset`` method reads. * ``machine_config_crc`` is what the host sends to ``set_preset`` at runtime -- the CRC of the full machine_config, not the CRC of the underlying preset dict. New method: ``SimulatedTxDevice.get_machine_config(preset_index)`` returns ``(machine_config_dict, machine_config_crc)`` for debugging + tests to inspect the flash-side state. ``set_preset``'s CRC-mismatch error message now names ``machine_config_crc`` explicitly so callers understand which CRC was checked (the machine_config's, not the preset's). **2. Simulator's update_firmware is implemented** ``SimulatedTxDevice.update_firmware`` and ``SimulatedHVController.update_firmware`` now flip the reported firmware version to a ``target_version`` kwarg (default: ``"sim-1.0.7"``). Real DFU is out of scope for the simulator; the purpose here is to let an app-side firmware-update flow (e.g. the operator-interface's "Firmware update required" modal -> ``connector.update_firmware()`` -> re-probe) run end-to-end in sim mode. The signatures mirror the shape of the real ``TxDevice.update_firmware`` / HV equivalent so app-side code can call the same method against either backend. Tests: 22 total (was 17). New coverage: * ``machine_config_dict`` -> ``(id, settings_crc)`` extraction * ``get_machine_config`` round-trip * ``set_preset`` CRC-mismatch error message names ``machine_config_crc`` * ``update_firmware`` on both TX + HV flips ``get_version`` output No firmware changes in this PR. Signed-off-by: Peter Hollender <peterhollender@gmail.com> --------- Signed-off-by: Peter Hollender <peterhollender@gmail.com> Co-authored-by: Peter Hollender <peterhollender@gmail.com> Signed-off-by: Peter (Agent) <phollender@openwater.cc> * feat(trigger): add TrainCount + trigger_status for polled progress (#39) Lets the operator interface poll sonication progress against ``TxDevice.get_trigger()`` instead of subscribing to the async ``signal_data_received`` STATUS-frame OWSignal. Companion to openlifu-operator-interface#78 (PROJ-08 Task 1: Running-state ProgressBar + firmware-driven Running->Finished transition). The walking-skeleton OI deliberately dropped async telemetry / status-frame subscriptions in openlifu-operator-interface#92; reintroducing one for sonication progress would undo that work. A polled model fits the sync-probe idiom the OI state machine already uses for connect state and firmware compat. ## Changes ### Simulated TxDevice * ``SimulatedTxDevice.__init__`` adds ``self._train_curr = 0`` so a polled host can read progress without subscribing to the OWSignal. Written by the engine on each tick. * ``SimulatedTxDevice.get_trigger_json()`` adds ``"TrainCount": self._train_curr`` to its returned dict. * ``SimulatedTxDevice.get_trigger()`` is new -- mirrors the shape of the real ``LIFUTXDevice.TxDevice.get_trigger()`` so OI code calling ``interface.txdevice.get_trigger()`` gets the same keys against sim and real hardware. Adds two walking-skeleton polling fields: ``train_count`` (current running counter) and ``trigger_status`` (``"RUNNING"`` / ``"STOPPED"``). ### Sim engine * ``_SimulatedRunEngine.start()`` resets ``self._tx._train_curr = 0`` so a polled host sees progress starting from 0 on every fresh sonication (previously the counter carried over from a prior run if the host polled before the first tick). * ``_SimulatedRunEngine._on_train_tick`` mirrors ``self._train_curr`` back to ``self._tx._train_curr`` after each increment -- the field the polled host reads. * ``_SimulatedRunEngine._on_train_tick`` natural-completion branch now calls ``self._tx.stop_trigger()`` (previously only an explicit ``stop_sonication`` flipped ``_trigger_running`` to False, so a polled host saw ``TriggerStatus=RUNNING`` after the sim finished naturally). The emitted STATUS frame already carried ``status=STOPPED`` so callers that subscribed to the OWSignal weren't affected by the bug; polled callers were. ### Real TxDevice * ``LIFUTXDevice.get_trigger()`` extends its return dict with the same two fields: * ``"train_count": trigger_json.get("TrainCount", 0)`` * ``"trigger_status": trigger_json.get("TriggerStatus", "UNKNOWN")`` Both use ``.get(default)`` so pre-PROJ-04 firmware (which doesn't yet populate ``TrainCount``) returns 0 cleanly. The firmware-side ``OW_CTRL_GET_SWTRIG`` response-payload extension to actually populate the field is tracked separately as a PROJ-04 companion on openlifu-transmitter-fw -- not blocking the OI walking skeleton since OI runs against the sim pre-PROJ-04. ## Tests New: ``unit-test/test_simulated_trigger_progress.py`` -- 7 tests exercising the sim-side changes end-to-end: * ``get_trigger_json`` / ``get_trigger`` initial state (both ``TrainCount`` / ``train_count`` default to 0, ``TriggerStatus`` / ``trigger_status`` default to ``STOPPED``). * ``get_trigger`` snake-case shape check (every expected key present). * ``train_count`` advances monotonically during a short synthetic sequence (5 pulse trains * 20 ms), reaches ``pulse_train_count`` on natural completion. * Natural completion flips ``trigger_status`` to ``STOPPED`` -- regression test for the pre-2026-10-01 bug where ``_trigger_running`` wasn't flipped on natural end. * Operator-initiated ``stop_sonication`` midway: ``trigger_status`` flips to ``STOPPED``, ``train_count`` retained at partial progress (not reset to 0, not advanced to total). * Restart resets ``train_count`` to 0 at engine ``start()``. Full suite (excluding pre-existing ``test_tx_device.py`` collection failure on an unrelated ``HW_ID_DATA_LENGTH`` import): 107 passed. Refs OpenwaterHealth/openlifu-operator-interface#78 Signed-off-by: Peter Hollender <peterhollender@gmail.com> Co-authored-by: Peter Hollender <peterhollender@gmail.com> Signed-off-by: Peter (Agent) <phollender@openwater.cc> * feat(preset): apply trigger config in SimulatedTxDevice.set_preset (#40) Companion to the OI #78 smoke-test regression. The old ``SimulatedTxDevice.set_preset`` verified the CRC and recorded the loaded selection but did not update ``_sequence`` / ``_pulse`` with the loaded preset's parameters, so an immediate ``SimulatedLIFUInterface.start_sonication`` ran the default 1-train sequence instead of the operator's selected duration. That path reaches the UI as "Progress bar never advances; sim reports STOPPED after one tick." Real firmware does not have this problem: ``set_preset`` on the device loads the machine_config and applies its registers / profiles, and no separate ``set_trigger`` call is needed before ``start_trigger``. The sim now mirrors that model. ## Change ``SimulatedTxDevice.set_preset`` now reads the following fields from the flash-baked machine_config and applies them to the TX device state before returning: * ``pulse_interval_ms`` (ms -> seconds -> ``_sequence["pulse_interval"]``) * ``pulse_count`` -> ``_sequence["pulse_count"]`` * ``pulse_train_interval_s`` -> ``_sequence["pulse_train_interval"]`` * ``pulse_train_count_selections[sequence_duration_index]`` -> ``_sequence["pulse_train_count"]`` * ``pulse_length_us`` (us -> seconds -> ``_pulse["duration"]``) Also adds a bounds check that raises ``ValueError`` when ``sequence_duration_index`` is beyond the preset's own ``pulse_train_count_selections`` length (previously only the negative-index case raised; an out-of-range positive index silently stored a nonsense selection). The CRC-mismatch error path is unchanged -- no partial state leaks into ``_sequence`` / ``_pulse`` on a failed CRC check. ## Tests New: ``unit-test/test_simulated_set_preset_applies_trigger.py`` (7 tests): * ``set_preset`` writes pulse_interval / pulse_count / pulse_train_interval / pulse_train_count into ``_sequence``. * ``set_preset`` writes pulse_length_us (converted to seconds) into ``_pulse["duration"]``. * The applied values are visible through ``get_trigger()`` -- wrapper round-trip test. * Out-of-range ``sequence_duration_index`` raises ``ValueError`` mentioning "out of range". * Negative ``sequence_duration_index`` still raises (regression). * CRC mismatch leaves ``_sequence`` and ``_pulse`` unchanged (no partial state on fault path). * **Integration**: load preset -> start_sonication -> engine ticks run the selected ``pulse_train_count``, reach ``trigger_status == STOPPED`` at ``train_count == pulse_train_count``. Updated ``_mc()`` helper in ``unit-test/test_simulated_preset_flash.py`` to include the required trigger-config fields plus a default ``pulse_train_count_selections=[1, 2, 3, 4, 5]`` so pre-existing tests that call ``set_preset(duration_index=0..4)`` don't need per-site overrides. Full suite (excluding pre-existing ``test_tx_device.py`` collection failure on unrelated ``HW_ID_DATA_LENGTH`` import): 114 passed (was 107 pre-change). Refs OpenwaterHealth/openlifu-operator-interface#78 Signed-off-by: Peter Hollender <peterhollender@gmail.com> Co-authored-by: Peter Hollender <peterhollender@gmail.com> Signed-off-by: Peter (Agent) <phollender@openwater.cc> * refactor(io): split DeviceInterface (FDA) from LIFUInterface (RUO) DeviceInterface is now the FDA-facing base class: connection, monitoring, PID lock, load_preset (TX load + HV select with frequency-specific sensitivity), start/stop sonication, status, close. LIFUInterface extends it with the research-only endpoints (set_solution, check_solution, set_module_invert, voltage tables, sequence duty/duration) and the RUO constructor options (ext_power_supply, module_invert, voltage_table_selection) via a _create_devices hook. Existing LIFUInterface callers are unchanged. Sensitivity lookup is a single sensitivity_at_frequency() in LIFUUserConfig (np.interp per module, clamped, mean across modules); TxDevice and SimulatedTxDevice both delegate to it. HVController.select_preset sends <I (CRC only) or <Iff (CRC, sensitivity, frequency_hz); a half-specified call raises ValueError. Removed the unused HVController.load_preset alias. Simulator mirrors the split: SimulatedDeviceInterface exposes no RUO methods; SimulatedLIFUInterface adds them. Simulated preset paths raise LIFUDeviceError (OW_BAD_CRC / OW_HV_PRESET_CRC) and LIFUSolutionError like the hardware path. Refs SR-002 Refs OpenwaterHealth/openlifu-diathermy-application#16 Signed-off-by: Peter Hollender <peterhollender@gmail.com> * refactor(io): split FDA TxDevice/HVController from RUO subclasses (#47) * refactor(io): split FDA TxDevice/HVController from RUO subclasses DeviceInterface (the FDA entry point) handed out TxDevice and HVController objects that still carried every RUO command (register writes, set_voltage, set_trigger, DFU, config writes, async mode). Restricting the interface alone left them one attribute away. - TxDevice / HVController are now the FDA components. They expose only identity, telemetry, trigger read/start/stop and stored-preset methods, and carry a SUPPORTED_COMMANDS whitelist (TX_FDA_COMMANDS / HV_FDA_COMMANDS) so any other opcode raises ValueError before it reaches the UART. - LIFUTxDevice / LIFUHVController subclass them and add the RUO methods; shared RUO globals (start/stop, send_async, echo, LED, reset, DFU, write_config) move to LIFUComponentMixin. - Only LIFUInterface creates the LIFU classes (_create_devices hook). - Async mode is RUO-only: DeviceInterface drops run_async, start_monitoring/stop_monitoring and the async_mode calls in start/stop_sonication; LIFUInterface keeps them. - The simulator mirrors the split (SimulatedLIFUTxDevice, SimulatedLIFUHVController; monitoring and async_mode on SimulatedLIFUInterface). - RUO unit tests now use the LIFU classes; new tests cover the class surfaces, the opcode whitelist and which classes each interface builds. test_tx_device.py imports HW_ID_DATA_LENGTH from LIFUConfig so it collects again (three stale set_trigger tests remain, #44). Refs SR-002 Closes #43 Refs #44 Signed-off-by: Peter Hollender <peterhollender@gmail.com> * test(tx): update set_trigger train-interval tests to back-to-back contract test_23b/23c/23d asserted the 2.0.14 behaviour (auto-filling a zero train interval and bumping it past the pulse period for firmware <= 2.0.3). Since 8b3eaf2 set_trigger sends 0 for back-to-back trains, and firmware <= 2.0.3 no longer needs support. Replace them with tests of the current contract: zero or zero-gap intervals are sent as 0; longer intervals pass through; a short interval is sent as 0 for a single train and raises ValueError (with nothing sent) for multiple trains. Refs SR-002 Closes #44 Signed-off-by: Peter Hollender <peterhollender@gmail.com> --------- Signed-off-by: Peter Hollender <peterhollender@gmail.com> Co-authored-by: Peter Hollender <peterhollender@gmail.com> Signed-off-by: Peter (Agent) <phollender@openwater.cc> --------- Signed-off-by: Peter Hollender <peterhollender@gmail.com> Signed-off-by: Peter (Agent) <phollender@openwater.cc> Co-authored-by: Peter Hollender <peterhollender@gmail.com>
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #43
Closes #44
Refs OpenwaterHealth/openlifu-diathermy-application#16 (SR-002)
Stacked on #45 (base
feature/SR-002-device-interface). Merge #45 first.What
DeviceInterface(the FDA entry point) handed outTxDeviceandHVControllerobjects that still had every RUO command on them.Restricting the interface alone left those commands one attribute away.
This PR moves the split down to the component level.
DeviceInterface)LIFUInterface)TxDevice: identity, temperatures,get_trigger/start_trigger/stop_trigger,get_preset/load_preset, metadataLIFUTxDevice(LIFUComponentMixin, TxDevice):set_trigger*, profiles/set_solution, registers,async_mode, module invert, firmware updateHVController: identity, HV on/off/status,get_preset/select_preset, telemetry readsLIFUHVController(LIFUComponentMixin, HVController):set_voltage, DACs, 12 V, fan, RGB,hv_enableOWComponent:connect/close,send_checked, ping/version/HWID/read_configLIFUComponentMixin:uart,start/stop,send_async, echo, LED, reset, DFU,write_config*SUPPORTED_COMMANDS(
TX_FDA_COMMANDS/HV_FDA_COMMANDSinLIFUConfig).send/send_checkedraiseValueErrorfor any other opcodebefore the UART is touched, so a raw
send_checked(OW_TX7332_WREG)on an FDA object is refused.
DeviceInterfaceno longer hasrun_async,start_monitoring/stop_monitoring, orasync_modecalls instart_sonication/stop_sonication.LIFUInterfacekeeps allof these.
_create_devices(LIFUInterfacebuilds the LIFUclasses) and
_connect_devices(LIFUInterfaceskips the syncconnect in async mode).
SimulatedLIFUTxDevice,SimulatedLIFUHVController; monitoring andasync_modeare onSimulatedLIFUInterface).Behaviour changes to note
LIFUInterface.start_sonicationnow setsasync_modebeforeturning HV on (previously after). Both happen before the trigger
starts, so the sonication sequence is unchanged.
DeviceInterfacehas no monitor thread, so there is noUSB hot-plug auto-reconnect on the FDA path. Ports are opened once
in
__init__.TxDevice.__init__no longer takesmodule_invert(it is now onLIFUTxDevice).Residual gaps (not fixed here)
OW_CMD_USR_CFGcovers both read and write (reserved=1). Thewhitelist checks only the opcode, so it cannot block a hand-built
config write.
write_configitself is no longer on the FDA classes._uartis private by convention only.values are unique across packet types.
path on
DeviceInterface. The sim keeps anupdate_firmwareplaceholder. Tracked in Add a service interface for firmware update / config writes outside the FDA DeviceInterface #46 (service interface).
Test plan (executed)
pytest unit-testgives 223 passed, 113 subtests, with no deselections. The baseline before this change was 147 passed withtest_tx_device.pyignored entirely.test_fda_presets.py:TestComponentSurface: the FDA classes lack every RUO member, theLIFU classes have them, and the simulator classes match.
TestCommandWhitelist: FDA TX rejectsOW_TX7332_WREGandOW_CMD_ECHO, FDA HV rejectsOW_POWER_SET_HV, and none ofthese calls reaches
send_packet. The LIFU classes allow them.TestInterfaceSurface:DeviceInterface._create_devicesbuildsexactly
TxDevice/HVController, andLIFUInterface._create_devicesbuilds the LIFU classes. Monitoringis now in
RUO_ENDPOINTS.feature/SR-002-device-interface+ this SDK):all 12 test files pass.
test_sonication_progress.pyneeded atest-only change because it used
set_triggeron the FDA sim; thatchange is on OI PR #124 (commit
c4762c3).#44
test_tx_device.pyexercises the class being split, so this PR fixesits
HW_ID_DATA_LENGTHimport (now fromLIFUConfig) and points itat
LIFUTxDevice. Once the file collected, three tests(
test_23b/23c/23d) failed. They asserted the 2.0.14train-interval auto-fill and the bump for firmware 2.0.3 and older.
set_triggerhas sent0for back-to-back trains since8b3eaf2,and firmware 2.0.3 and older no longer needs support. A separate commit
replaces those three tests with tests of the current contract (
23b-23f):0.0for a single train, and raisesValueErrorwith nothing sent for multiple trains.Self-review
TX_FDA_COMMANDS/HV_FDA_COMMANDS. That set is derived fromevery method the operator interface calls on
txdevice/hvcontroller(verified by its test suite).
modules. I kept them next to their FDA bases to keep the diff
reviewable and preserve import paths. The FDA classes still do not
define any RUO method.
interface factories. Not covered: real-hardware async/monitor path
(unchanged code, moved), and the FDA no-hot-plug behaviour.
DeviceInterface's components, or passedrun_async/async_modetoDeviceInterface, must useLIFUInterface. Theonly in-tree caller of that kind was an OI test (fixed).
LIFUInterfaceusers are unaffected.