From ff00a3380619c4675c14263fa041de9dd8fdedea Mon Sep 17 00:00:00 2001 From: David Perl Date: Tue, 13 Oct 2026 14:45:16 +0200 Subject: [PATCH] docs: add inventory of config values for moving to managed config --- docs/configurable_values_inventory.md | 326 ++++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 docs/configurable_values_inventory.md diff --git a/docs/configurable_values_inventory.md b/docs/configurable_values_inventory.md new file mode 100644 index 00000000..2c5e71f2 --- /dev/null +++ b/docs/configurable_values_inventory.md @@ -0,0 +1,326 @@ +# Inventory of user-configurable values (aaredaq + aarecommon) + +Everything that behaves like a setting but is fixed in code, plus a map of the +configuration surfaces that already exist and how well the code actually lines +up with them. Line numbers are from the working tree on 2026-10-13. + +Scope: `aaredaq/src`, `aarecommon/src`. Pydantic request/response model defaults +are excluded per the brief; the handful of *settings* models whose defaults are +the live policy are listed in §6. + +--- + +## 1. Configuration surfaces that already exist + +| Surface | Mechanism | Where | +|---|---|---| +| Per-beamline YAML | `cfg_get("dotted.key", default)` → `BeamlineYAMLConfig` | `aarecommon/config/beamline.py:61`, files in `aarecommon/config/beamline_configs/{x10sa,x06da,x06sa,simulated}.yaml` | +| Local-contact Redis config | `LocalContactConfigModel` stored at `:local_contact_config` | `aaredaq/daq/config_model.py`, read `config.py:1203` | +| Operator-set Redis state | `:auto_params` (`SimpleScanParameters`), `:settings`, `:simple_input_params`, `:runtime:simulate:` | `config.py:963`, `:630`, `:948`, `:1050` | +| Environment | `BEAMLINE`, `JWT_AAREDAQ_KEY`, `AAREDB_SHARED_PASSWORD`, `AAREDB_USERNAME`/`AAREDB_PASSWORD`, `AARE_RECOVERY_CODE`, `AARE_BEAMLINE`, `USER` | `aarecommon/config/beamline.py:30`; `beamline_dispatch/default/beamline_dispatch.py:20`; `aaredb.py:65,132,152,331,335,407,411`; `server.py:179`; `gui/threads/daq_worker.py:1249`; `aarecommon/models/auth.py:67` | +| Beam-centre calibration data | `beam_centres.json` | `aarecommon/config/beamline_configs/beam_centres.json` | + +### 1.1 `LocalContactConfigModel` — the only Redis-backed, UI-editable policy today + +| Field | Default | Bounds | +|---|---|---| +| `mount_to_center_sleep_s` | 0.0 | 0–120 | +| `line_scan_loop_face_y_padding_fraction_each_side` | 0.5 | 0–5 | +| `line_scan_loop_all_y_padding_fraction_each_side` | 0.5 | 0–5 | + +Three fields. Almost everything in §3–§5 is a candidate to join them. + +### 1.2 Keys the YAMLs define + +`x10sa.yaml` / `x06da.yaml` share a nested schema (`gui.*`, `shared.*`, +`daq.hardware.*`, `daq.data_collection_settings.*`, `daq.auto_raster.*`). +`simulated.yaml` is a **flat, unrelated schema** (`redis_host`, `smargon_base`, +`tell_url`, …) — see §2.3. + +--- + +## 2. Config-integrity findings (code vs YAML) + +Produced by diffing every `cfg_get("…")` key in code against the flattened YAMLs. + +### 2.1 Keys read in code that no YAML defines — the code default *always* wins + +| Key read | Where | Falls back to | Likely intended key | +|---|---|---|---| +| `daq.data_collection_settings.default_rotation_settings.incr_omega_deg` | `daq.py:2334` | 0.2 | YAML has `increment_omega_deg` | +| `daq.data_collection_settings.default_raster_settings.{dtz,exp_time_s,transmission}` | `gui/scan_logic/raster_grid_manager.py:143-149` | 200.0 / 0.01 / 1.0 | YAML has `default_raster_scan_settings` | +| `daq.data_collection_settings.default_screening_settings.exp_time_s` | `gui/panels/rotation_data_collection.py:116` | 0.1 | no screening block exists in any YAML | +| `daq.auto_raster.exclude_pin_x_overlap` | `operations/common/ml_bounding_box.py:475` | True | — | +| `daq.auto_raster.exclude_pin_max_overlap_fraction` | `operations/common/ml_bounding_box.py:480` | 0.75 | — | +| `daq.hardware.jfjoch_url` | `aarecommon/config/beamline.py:45-49` | per-beamline host | YAML has `shared.jfjoch.jfjoch_url`, which `beamline.py:77` reads — two readers, two keys | +| `endpoints.smargon_base`, `epics.pv_prefix` | `aarecommon/config/beamline.py:64-65` | None | legacy | + +### 2.2 Keys defined in a YAML that nothing reads — dead config + +| File | Key | Note | +|---|---|---| +| x06da | `daq.detector_limit_modifier = 2.0` | Code reads `daq.detector_distance_limit_modifier` (`daq/devices.py:51`). x10sa spells it right. **x06da silently runs with modifier 1.0.** | +| x06da | `daq.auto_raster.line_scan_y_padding_fraction = 0.15` | Superseded by the two `*_each_side` fields in `LocalContactConfigModel` | +| x06da | `daq.hardware.zoom_min = 1`, `zoom_max = 1000` | never read | +| x06da | `daq.hardware.sam_cam = 'white_balance_ratio = 1.233'` | never read | +| both | `daq.db.aaredb_url` | `aaredb.py:44` hard-codes the host as a default arg instead | +| both | `daq.maximum_flux` | never read | +| both | `beamline_id` | never read | + +### 2.3 Per-beamline gaps + +- **x10sa has no `daq.auto_raster.*` block**, so grid padding (0.15/0.15/=top) and `include_crystal=False` come from code (`ml_bounding_box.py:277-279, 455`). May be intentional; it is invisible. +- `gui.cameras.secondary_beamline_camera_url` exists only in x06da and its only reader is commented out (`gui/main_window.py:520`). +- **Under `BEAMLINE=SIMULATED` every `cfg_get` misses** because `simulated.yaml` uses a different schema, so the simulated beamline runs entirely on the in-code fallbacks in §3. + +### 2.4 Same concept, different fallbacks + +| Concept | Values | Where | +|---|---|---| +| Default raster dtz | 200 · 250 · 200.0 | `daq.py:2093`, `daq.py:2318`, `raster_grid_manager.py:149` | +| Default rotation dtz | 250 (daq) · 110 (dispatch) · 150 (`SimpleScanParameters`) | `daq.py:2330`, `beamline_dispatch/default:60`, `aarecommon/models/models.py:700` | +| Face-detection sweep | model default `steps=14, step_size=15` vs inline `steps=7, step_size=30` | `operations/face_detection/models.py:14-16` vs `daq.py:2501` | + +--- + +## 3. Hard-coded values — automation & beamline policy + +These are the strongest candidates for `LocalContactConfigModel` or YAML. + +### 3.1 Automation sequence (`daq.py`) + +| Value | Location | Note | +|---|---|---| +| `MIN_SPOTS_LOW_RES_THRESHOLD = 10.0` | `daq.py:280` | crystal-present threshold | +| `AUTOMATION_BUSY_TIMEOUT_S = 600` | `daq.py:281` | | +| `MANUAL_RASTER_BUSY_TIMEOUT_S = 600` | `daq.py:282` | | +| `AUTO_RASTER_MAX_IMAGES = 4500` | `daq.py:283` | grid is scaled down to fit (`ml_bounding_box.py:132`) | +| `AUTO_RASTER_MIN_CELL_SIZE_MM = 0.005` | `daq.py:284` | | +| `AUTO_RASTER_SKIP_IF_EXCEED_MAX_IMAGE_THRESHOLD = True` | `daq.py:285` | behaviour flag | +| `_sample_sync_min_interval_s = 2.0` | `daq.py:305` | TELL sync rate limit | +| smargon trace path `/sls/mx/applications/logs/smargon_trace.csv` | `daq.py:301` (also `gui/panels/smargon_trace_panel.py:520`) | | +| rotation `min_exp_time = 0.0011` | `daq.py:1446` | silently clamps the request | +| resolution fudge `new_res = 1/((1/res) + 0.01)` | `daq.py:1666` | spreadsheet target-resolution → dtz | +| omega sanity bound `-2000 < val < 2000` | `daq.py:1702` | | +| face detection `steps=7, step_size=30, face_min_ratio=0.3` | `daq.py:2501` | overrides the model defaults | +| run token `secrets.token_hex(3)` | `daq.py:2518` | 6 hex chars shared by raster+rotation | +| auto-raster seed grid = `beam_size × 0.5` | `daq.py:2534` | | +| autofocus `settle_time_s = 1.0` | `daq.py:1901` | | +| screenshot `settle_time_s = 0.2` | `daq.py:2262, :2272`; `operations/screenshot/service.py:61, :67` | | +| mount-to-centre, unmount waits `sleep(2)`, `sleep(0.2)` | `daq.py:1980, :2574`, `:1740, :1749` | | +| `smargon_wait(timeout=180)` | `daq.py:2016`; `raster/service.py:534` | | +| default dtz min/max fallback `100` / `1000` | `daq.py:3028-3029` | YAML overrides exist | +| raster/rotation default fallbacks | `daq.py:2312-2337` | see §2.4 | + +### 3.2 Mounting (`operations/mounting/service.py`) + +| Value | Location | +|---|---| +| `DRY_AFTER_FAIL_COUNT = 3` | `:14` | +| `STOP_AFTER_FAIL_COUNT = 5` | `:15` | +| mount `timeout=360.0` | `:83` | +| unmount `timeout=60.0` | `:65, :154, :173, :186` | +| magnet-sensor check `timeout=10.0` (default 1.0) | `:146` (def `:33`) | +| TELL post-mount `sleep(0.1)` | `:41` | + +### 3.3 Raster (`operations/raster/service.py`, `models.py`) + +| Value | Location | Note | +|---|---|---| +| Y re-target threshold `beam_size.y × 2.0` | `:302`; and `max(beam.y×2.0, cell.y×4.0)` `:736` | | +| line-scan cell height `beam_size.y × 0.25` | `:730` | | +| per-row overhead `n_y × 0.3 s` | `:441` | time estimate | +| total-time cap `> 1200 s` | `:446` | refuses long rasters | +| detector timeout `expected × 1.1 + 60` | `:428` | | +| `wait_till_running(timeout=60.0)` | `:506`; `rotation/service.py:35` | | +| aerotech `wait_till_done(timeout=60)` | `:438` | | +| `wait_for_screenshot_s = 0.3` | `raster/models.py:26` | | +| `sleep(0.2)` settle | `:644` | | + +### 3.4 Auto-raster bounding box (`operations/common/ml_bounding_box.py`) + +| Value | Location | Note | +|---|---|---| +| grid padding x / y / y_bottom `0.15 / 0.15 / =y` | `:277-279` | YAML-overridable (x06da only) | +| `include_crystal = False` | `:455` | YAML-overridable (x06da only) | +| `exclude_pin_x_overlap = True` | `:475` | no YAML key anywhere | +| `exclude_pin_max_overlap_fraction = 0.75` | `:480` | no YAML key anywhere | +| line-scan cell = `beam_size × 0.8` | `:258` | | +| line-scan `exp_time_s=0.01, transmission=1.0` | `:378-379` | fixed regardless of beamline defaults | +| image-count scale `(count/max)**0.5` | `:132` | | +| `sleep(0.2)` | `:163, :414` | | + +### 3.5 Loop centering & face detection + +| Value | Location | Note | +|---|---|---| +| `overlap_with_pin = 0.5` | `operations/loop_centering/models.py:36` | settings-model default | +| `zoom_value = 200.0` | `:38` | | +| `max_attempts = 2` | `:39` | | +| `target_tolerance_um = 500.0` | `:40` | | +| `sleep(0.2)` | `loop_centering/service.py:100` | | +| face detection `steps=14, step_size=15, face_min_ratio=0.3` | `face_detection/models.py:14-16` | overridden at `daq.py:2501` | +| angle wrap `< 720` | `face_detection/service.py:91` | | +| centre-correction `tolerance=0.2` | `:129` (def `:51`) | | +| MAD outlier `k=3.5` | `face_detection/utils.py:39, :69` | | +| `enlargement_factor = 2` | `beamcenterfit.py:44` | comment says 50% | +| ML inference `conf=0.4` | `aarecommon/lc_infer_wrapper.py:60` | detection confidence floor | +| `RETRY_COUNT = 3`, `RETRY_SLEEP_S = 0.1` | `mlbox.py:46-47` | | + +### 3.6 Per-beamline dispatch defaults (`aaredaq/beamline_dispatch/`) + +| Value | Location | +|---|---| +| rotation `{exp_time_s: 0.04, dtz: 110, incr_omega_deg: 0.2}` | `default/beamline_dispatch.py:60` | +| auto-raster `{exp_time_s: 0.04, transmission: 1.0, dtz: 200}` | `default/beamline_dispatch.py:65` | +| x10sa rotation `transmission: 0.1`; raster `{0.02, 0.1, 200}` | `x10sa/beamline_dispatch.py:129, :134` | +| x06da rotation `transmission: 1.0`; raster `{0.04, 1.0, 150}` | `x06da/beamline_dispatch.py:126, :130` | +| x06da detector tilt `poni_rot1 = -0.001396263`, `poni_rot2 = -0.003839724` | `x06da/beamline_dispatch.py:109-110` (also `aarecommon/math/diffraction_geometry.py:83-84`) | +| x06da flux-vs-energy tables | `x06da/beamline_dispatch.py:10, :25` | + +These duplicate the YAML `default_*_settings` blocks with different numbers (§2.4). + +--- + +## 4. Hard-coded values — hardware, calibration, network + +### 4.1 Lamp brightness — three disagreeing encodings + +| Encoding | Front light | Back light | Location | +|---|---|---|---| +| EPICS PV predefs | off 1.49 · half 2.0 · max 3.0 | off 0 · half 0.98 · max 1.2 | `daq/devices.py:57-70` | +| % ↔ raw conversion | 0 % at ≤1.0, 100 % at ≥2.5, linear `/1.5` | 100 % at 3.0, linear `/3.0` | `daq.py:1767-1792` | +| BEC device YAML `userParameter` | on 3.0 · off 0.0 | on 1.3 · off 0.0 | `pxii_bec/…/pxii-devices.yaml` (out of scope, for comparison) | + +Back light "max" is 1.2 in one place, 3.0 in another, 1.3 in the third. + +### 4.2 Other hardware constants + +| Value | Location | Note | +|---|---|---| +| `CHI_ZERO_TOLERANCE_DEG = 1e-3` | `daq/devices.py:31` | | +| `dtz_mod` default 1.0 | `daq/devices.py:51` | x06da YAML key misspelled (§2.2) | +| PV `timeout=10.0` ×4 | `daq/devices.py:60, :67, :77, :90` | | +| `det_z(..., timeout=60)` | `daq/devices.py:305` | | +| `smargon_wait(timeout=10.0)` | `daq/devices.py:364, :371` | | +| `DEFAULT_LENS_MAGNIFICATION = 10.0` | `config.py:53` | YAML-overridable | +| `ABR_POS_MOUNT`, `ABR_OMEGA_MOUNT = 0.0` | `config.py:47-52` | | +| `SMARGON_HOME` (z=18), `AERO_HOME` | `devices/smargon.py:19-20`; `devices/aerotech.py:10`; `gui/panels/smargon_trace_panel.py:33-35` | three copies | +| `SAFE_POSITION = 600` | `daq/workflows.py:13` | detector safe dtz | +| `dtz_safe_position` fallback 300.0 | `config.py:1246` | x06da YAML has `None` | +| area detector `gain=15, expo=0.025` | `devices/area_detector.py:113` | | +| `MIN_DTZ = 108.0 # beamline dependent` | `gui/panels/scan_settings_panel.py:122` | its own comment says so | +| `MAX_OMEGA_SPEED_DEG_S = 500.0` | `gui/panels/rotation_data_collection.py:19` | | +| `MIN_EXP_TIME_S = {X06DA: 1/900, X10SA: 1/120}` | `rotation_data_collection.py:21` | detector frame-rate limits | +| `STRATEGY_MIN_EXP_TIME_S = {X06DA: 0.002}`, `DEFAULT_MIN_EXP_TIME_S = 0.0005`, `FIXED_FRAME_RATE_HZ = 100.0` | `gui/panels/smart_rotation_panel.py:41-48` | | + +### 4.3 Hosts, URLs, paths (code fallbacks) + +| Value | Location | Note | +|---|---|---| +| Redis `port=6379, db=0` | `config.py:105` | host is YAML-overridable, port/db are not | +| `{bl}-redis.psi.ch`, `{bl}-bec-001.psi.ch` | `config.py:104`; `devices/bec_worker.py:97` | | +| smargon `http://{x06da,x10sa}-smargopolo.psi.ch:3000` | `devices/smargon.py:25, :28` | | +| aerotech `http://mx-{bl}-queue-01.psi.ch:5234` | `devices/aerotech.py:20-26`; `config.py:1178` | | +| TELL `http://{bl}-tell.psi.ch:22222` / `http://PC17488:22222` | `devices/tell_backend.py:90, :92` | x10sa points at a PC hostname | +| JFJoch `sls-gpu-001/002:8080`, frontends | `aarecommon/config/beamline.py:45-49`; `config.py:1184-1186` | | +| aaredb `https://mx-aaredb-dmz-01.psi.ch/dispatcher` | `aaredb.py:44`; `spreadsheetupdater.py:144, :168`; `tellupdater.py:382` | YAML key exists but is unread (§2.2) | +| data root `/sls/mx/data` | `server.py:2366`; `gui/panels/file_path_panel.py:121`; `gui/threads/jfjoch_viewer.py:69`; `operations/screenshot/service.py:41` | four copies | +| logs `/sls/mx/applications/logs` | `operations/screenshot/service.py:40`; `daq.py:301` | | +| BEC log path `/tmp/logs` | `devices/bec_worker.py:99` | | +| BEC device-config path template | `devices/bec_worker.py:346` | | +| uvicorn `host="127.0.0.1", port=5210, workers=4` | `server.py:2625-2629` | `workers` matters for the queue-loop design | +| GUI per-beamline fallbacks (DAQ URL, cert path, camera hosts, gonio id) | `gui/gui.py:51-97` | all YAML-overridable | +| axis camera MJPEG params `1280x720&fps=20` | `gui/threads/axis_video_thread.py:34` | | + +### 4.4 JFJoch dataset settings (`devices/jfjoch.py`) + +| Value | Location | Note | +|---|---|---| +| pgroup fallback `"p16371"`, sample `"unknown_sample"` | `:142-143` | **writes into a real pgroup when sample is unknown** | +| `data_folder` routing `raw/raster`, `raw/screening`, `raw` | `:152-169` | see `docs/rotation_filename_path.md` §6.1 | +| `max_spot_count = 1000` | `:198` | | +| `detect_ice_rings = True` | `:199` | | +| `polarization_factor = 1.0` | `:204` | | +| grid `snake = True`, `vertical = False` | `:211, :214` | | +| omega axis `vector = [-1, 0, 0]` | `:226` | | +| `wait_till_running(timeout=60)` | `:293` | | + +--- + +## 5. Timeouts, polling, sessions, auth + +| Value | Location | Note | +|---|---|---| +| `ACCESS_TOKEN_EXPIRE_MINUTES = 24*60*7` | `daq/auth.py:30` | JWT lifetime 1 week | +| `SESSION_EXPIRE_SECONDS = 600` | `auth.py:31`; `config.py:96` (`GUI_SESSION_EXPIRE_SECONDS`) | duplicated | +| `BATON_REQUEST_TIMEOUT_SECONDS = 30`; refusal re-arm `timeout_sec=10` | `auth.py:32`, `:423`; `config.py:431` default 30, `+5` grace `:439` | | +| `grace_seconds = 60` | `config.py:208` | | +| `STAFF_GROUP = "unx-sls_mx_bs"`, `SUPER_USERS = [...]` | `auth.py:34-35` | identity policy in code | +| `ALGORITHM = "HS256"` | `auth.py:29` | | +| Redis lock `timeout=10` | `config.py:349, :361, :383, :393, :479, :640, :645` | hardware lock has **no** timeout (`config.py:106`) | +| `_ALL_PGROUPS_TTL_S = 60.0` | `server.py:76` | | +| SSE poll `0.15 s`; spreadsheet/ref-tools SSE `10 s`; baton `0.5 s`; `0.2 s` | `server.py:283, :303`; `:1295, :1313`; `:2262`; `:2534, :2537` | | +| `SSE_RECONNECT_DELAY_S = 5` | `daq/tellupdater.py:64` | | +| spreadsheet updater `sleep(5)` | `spreadsheetupdater.py:187` | | +| aaredb HTTP `timeout=30` | `aaredb.py:338, :414` | | +| TELL command `timeout=360.0`; ready `30.0`; probe `1.0`; poll `0.01`; lazy retry `2.0` | `devices/tell_client.py:82, :87, :202, :367`; `:386`; `tell_backend.py:65`; `:172`; `:336` | | +| smargon REST `timeout=2.0`; `initialize(360.0)` / `(30.0)`; `wait(60, tol 0.01, poll 0.01)` | `devices/smargon.py:43, :77`; `:111`, `:235`; `:214, :222` | | +| aerotech `wait_till_done(60)` | `devices/aerotech.py:114` | | +| fluorimeter `timeout_s=30.0` | `devices/fluorimeter.py:65, :91, :98, :114` | | +| ZMQ `timeout_ms=2000` | `devices/zmq_client.py:27` | | +| motor `timeout=5.0` / `300.0`, `poll_rate=0.01` | `devices/my_motor.py:9, :63, :84, :94` | | +| filter transmission `timeout=60.0, fail_on_timeout=True` | `devices/filter_transmission.py:8` | | +| `wait_position(timeout=60.0)` | `devices/workflow_tools.py:6` | | +| workflow waits `60`, `sleep(10.0)` | `daq/workflows.py:163, :175, :289` | | +| BEC `sleep(1)` | `devices/bec_worker.py:566` | | + +--- + +## 6. Analysis thresholds (aarecommon) + +| Value | Location | Note | +|---|---|---| +| autofocus threshold `median × 3.0` | `math/autofocus.py:20` | | +| ice veto `spots_ice > spots × 0.8` | `math/find_xtal.py:147` | comment says 50 % | +| `min_efficiency = 1.0` | `find_xtal.py:178` | | +| `min_low_res_spots = 10.0` | `find_xtal.py:344` | same number as `MIN_SPOTS_LOW_RES_THRESHOLD` | +| mask `n_sigma = 0.5`; `n_clusters = 3`; `percentile = 60.0` | `find_xtal.py:507, :568, :587-590` | | +| coordinate equality `tol = 0.1` / `0.01` | `math/coordinate.py:91, :115` | | +| exception `DEFAULT_CRITICALITY` flags | `errors/exception_handler.py:14, 77, 82, 90, 133, 146, 159, 168, 177, 182, 249, 275, 299, 349, 370, 375` | decide 503 vs 422 per exception class | +| logger levels (all `DEBUG`) | `config/logger.py:63-88` | no env/YAML override | + +--- + +## 7. GUI behaviour and UX policy + +| Value | Location | Note | +|---|---|---| +| `LOW_CURRENT_THRESHOLD = 360.0` mA | `gui/widgets/message_box.py:11`; used `panels/sample_queue_panel.py:226` | gates automation start/pause | +| `SNOOZE_SECONDS = 3600.0` | `message_box.py:12` | | +| TELL-warning recovery pause `600 × 1000` ms | `sample_queue_panel.py:59` | | +| `DEFAULT_SAMPLE_ESTIMATE_S = 150.0` | `panels/automation_panel.py:43` | ETA before data exists | +| dose model `KDOSE_1A_PH_UM2_GY = 2000.0`, `GLASS_TRANSITION_K = 200.0` | `panels/smart_rotation_panel.py:29, :36` | | +| resolution UI `VISIBLE_RES_{MIN,MAX,DEFAULT}_A = 0.8 / 10.0 / 2.0`, `DAQ_RES_EPS_A = 0.001`, `DTZ_STEP_MM = 10.0`, `EXP_TIME_STEP_S = 0.001` | `smart_rotation_panel.py:51-59` | | +| `DEFAULT_ABR_STEP_UM = 5` | `panels/abr_tweak_panel.py:12` | | +| `READBACK_GRACE_S = 1.5` | `panels/illumination_panel.py:11` | | +| `ENERGY_AT_TARGET_TOL_KEV = 0.001` | `panels/monochromator_panel.py:19` | | +| filename templates `"{date}/{puck}/{pos}"`, `"{sample}"`; run range `1-999` | `panels/file_path_panel.py:64, :77, :92` | `{pos}` bug, see `rotation_filename_path.md` §6.6 | +| status poll `500 ms`; `SPREADHSEET_FREQUENCY = 25` ticks (≈5 s); baton tick `1000 ms`; stream restarts `1000 ms` | `gui/threads/daq_worker.py:158, :58, :217, :1897, :2077, :2351` | | +| idle timer `60_000 ms`; remote-close `1000 ms` | `gui/main_window.py:880, :885` | | +| status message clear `5000 ms` | `gui/widgets/status_bar.py:155` | | +| automation refresh `1000 ms` | `panels/automation_panel.py:53` | | +| `MAX_FRAMES_IN_FLIGHT = 2` | `gui/threads/prediction_subscriber.py:28` | | +| `TABLE_MAX_ROWS = 20` | `panels/smargon_trace_panel.py:36` | | + +--- + +## 8. Settings-model defaults (pydantic, listed for completeness) + +Excluded from the inventory proper, but these *are* the live policy when nothing overrides them: + +- `SimpleScanParameters` (`aarecommon/models/models.py:698-716`) — schema of `:auto_params`; `dtz=150, exp_time_s=0.02, incr_omega_deg=0.2, steps=1800, transmission=1.0`. +- `LoopCenteringSettings` (`operations/loop_centering/models.py:36-40`) — §3.5. +- `FaceDetectionSettings` (`operations/face_detection/models.py:14-16`) — §3.5, overridden inline. +- `RasterSettings.wait_for_screenshot_s = 0.3` (`operations/raster/models.py:26`). +- `DiffractionGeometry` field bounds (`aarecommon/math/diffraction_geometry.py:12-14`) — validation limits on energy/dtz/pixel size.