# SuperXAS device v4 migration plan ## Scope and source mapping | Debye source | SuperXAS target | | --- | --- | | `debye_bec/debye_bec/devices/utils/utils.py` | `superxas_bec/superxas_bec/devices/utils/utils.py` | | `debye_bec/debye_bec/devices/mo1_bragg/mo1_bragg.py` | `superxas_bec/superxas_bec/devices/mo1_bragg/mo1_bragg.py` | | `debye_bec/debye_bec/devices/nidaq/nidaq.py` | `superxas_bec/superxas_bec/devices/nidaq/nidaq.py` | The migration should preserve SuperXAS hardware naming, EPICS prefixes, enum modules, and beamline-specific details. Only the scan metadata access pattern should be changed to match the Debye v4 migration pattern. ## Shared helper Add `superxas_bec/superxas_bec/devices/utils/utils.py` with Debye's `fetch_scan_info(scan_info)` helper. The helper should: - read `scan_info.msg.info` - convert list-valued `positions` back to `numpy.ndarray` - validate the message as `bec_server.scan_server.scans.scan_base.ScanInfo` - fall back for legacy scan messages by converting old `"fly"` scan type to `"hardware_triggered"` and other old scan types to `"software_triggered"` This keeps devices compatible during the migration window where both old and v4 scan status messages may still appear. ## `mo1_bragg` migration Current SuperXAS behavior: - reads the scan name from `self.scan_info.msg.scan_name` - populates a local `ScanParameter` pydantic model from `self.scan_info.msg.request_inputs["inputs"]` and `["kwargs"]` - reads old-style parameters such as `scan_parameter.start`, `scan_parameter.stop`, `scan_parameter.scan_time`, and `scan_parameter.scan_duration` Target v4 behavior: - import `ScanInfo as ScanServerScanInfo` - store `self.scan_parameters: ScanServerScanInfo | None` - call `self.scan_parameters = fetch_scan_info(self.scan_info)` at the start of `on_stage` - validate the scan with `_check_if_scan_name_is_valid(self.scan_parameters)` - read start/stop from `self.scan_parameters.positions` - read scan arguments from `self.scan_parameters.additional_scan_parameters` For the scans migrated in step 2: - `xas_simple_scan` - required: `positions`, `scan_time`, `scan_duration` - configure simple XAS settings - disable trigger settings - set scan-control mode to `ScanControlMode.SIMPLE` - `xas_advanced_scan` - required: `positions`, `scan_time`, `scan_duration`, `p_kink`, `e_kink` - configure advanced XAS settings - disable trigger settings - set scan-control mode to `ScanControlMode.ADVANCED` Keep SuperXAS-specific hardware behavior: - keep the SuperXAS prefix and imports - keep `set_trig_settings(...)` matching the current SuperXAS IOC API unless XRD scan classes are reintroduced in the scan migration - keep `convert_angle_energy(...)` available for the v4 scan's gonio centering - consider adding `"convert_angle_energy"` to `USER_ACCESS`, matching Debye, because the scan migration now relies on the method being callable through the device container/device manager path Remove or stop using `_update_scan_parameter()` and the old `ScanParameter` model once all parameter reads come from `self.scan_parameters`. ## `nidaq` migration Current SuperXAS behavior: - checks scan validity through `self.scan_info.msg.scan_name` - reads continuous scan settings from `self.scan_info.msg.scan_parameters["scan_duration"]` and `["compression"]` - `_progress_update` also reads `self.scan_info.msg.scan_parameters` Target v4 behavior: - import `ScanInfo as ScanServerScanInfo` - store `self.scan_parameters: ScanServerScanInfo | None` - call `self.scan_parameters = fetch_scan_info(self.scan_info)` at the start of `on_stage` - change `_check_if_scan_name_is_valid(...)` to accept the normalized `scan_parameters` - read the scan name from `self.scan_parameters.scan_name` - read continuous scan settings from `self.scan_parameters.additional_scan_parameters` - make `_progress_update` use `self.scan_parameters.additional_scan_parameters.get("scan_duration")` The Debye runtime behavior should be mirrored: - invalid scan names return without touching the IOC - non-continuous XAS scans stage NIDAQ as triggered, duration `0`, compression enabled - `nidaq_continuous_scan` stages NIDAQ as continuous and applies `scan_duration` plus `compression` - non-continuous XAS scans kick off during stage - `nidaq_continuous_scan` waits for explicit scan-core kickoff - `on_complete` stops the backend only for non-continuous scans ## Tests to migrate or add Use Debye's NIDAQ device tests as the starting point and adapt imports to SuperXAS: - verify `fetch_scan_info` accepts v4 scan info and legacy fly scan info - verify NIDAQ valid/invalid scan name checks - verify NIDAQ pre-scan and complete behavior for XAS versus `nidaq_continuous_scan` - add a focused `mo1_bragg` staging test that feeds v4 scan info and asserts the correct settings methods are called for `xas_simple_scan` and `xas_advanced_scan` Keep the test blast radius focused on metadata parsing and device decisions. The EPICS signal behavior is already covered by existing device-level patterns.