From ea9f8cdf01eadd17c20940d7636d2fc8f0e0b5b2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 10:34:48 +0000 Subject: [PATCH 01/19] docs(flomni): FZP how-tos as Markdown, updated for the PREEMPT_RT loop, move-ahead renamed to move-beyond Markdown versions of local_contact_only-howto_calibrate_the_fzp_scanner.pdf and local_contact_only-howto_limit_the_fzp_scanner_range.pdf (original text kept, update sections for the present flOMNI loop). New section "Move-beyond" (FAST_FZP_PID_LOOP(0) < 0 of the sample PIDs): mechanism, sizing (about half the FZP stroke), Abbe correction on the FZP side, hold (5) now reads the FZP drive incl. feedforward (flomni repo, branch fzp-move-beyond, 6a15f68). The PDFs are kept. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019aWcwG89wi7oVyrefukXX6 --- ...ct_only-howto_calibrate_the_fzp_scanner.md | 183 ++++++++++++++++++ ..._only-howto_limit_the_fzp_scanner_range.md | 41 ++++ 2 files changed, 224 insertions(+) create mode 100644 csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.md create mode 100644 csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_limit_the_fzp_scanner_range.md diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.md b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.md new file mode 100644 index 00000000..3d29d0b5 --- /dev/null +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.md @@ -0,0 +1,183 @@ +# How to calibrate the FZP scanner of flOMNI + +Source of `local_contact_only-howto_calibrate_the_fzp_scanner.pdf`. The original text is kept unchanged; the +section "Update 2026-10-20" at the end lists what the present flOMNI loop (PREEMPT_RT, 100 µs, branch `rt_preemt`) +needs in addition (corrected 2026-10-23: "move-ahead" → **move-beyond**, see the section at the end). + +```text +This is a description of how to calibrate the FZP scanner of flOMNI + +Rmemeber to do all modifications for both directions x and y! + +1. Align flOMNI without the FZP scanner. This implies disabling the +FZP scanner completely. To do so, in OrchestraHMI edit the parameters +of the FZP scanner as follows. +(a) Module PID_i_FZP: Set Param FAST_FZP_PID_LOOP(0) to 2. Then the FZP +will just remain +statically centered to the capacitive sensor position in closed loop. +(b) Module PID_i: Set Param FAST_FZP_PID_LOOP(0) to 0. If this is +negative, the FZP scanner will move ahead in a point-by-point scan by +that amount. +(c) Module PID_i: Set Param FAST_FZP_PID_LOOP(5) to 0. If this is +larger than zero the sample stage motion is stopped if the position is +reached by the FZP scanner (which is the case when the scanner is not +active). Zero means thus full compensation by the sample stage. + +In this state the setup can be aligned, and the affine matrix for +ptychography can be found. This refers then purely to the alignment of +the interferometer and X-ray detector. + +2. In the next step we perform a scan (with a small beam and small +range) such that only the FZP scanner is used and the sample stage +will not move. + +Before we start ensure that the Correction Matrix for the capacitive +sensors is cleared. + +To do this change the parameters of the flOMNI control +loop as follows: + +(a) Module AnalogInput_6259: Set parameter MatrixCorr to 1 0 0 1 +(b) Module PID_j_fzp: Set Param FAST_FZP_PID_LOOP(0) to 1 to re-activate +the FZP scanner. +(c) Module PID_i: Set Param FAST_FZP_PID_LOOP(5) to 10, so up to 10 +volts output of the fast PID is accepted before starting a movement of +the sample stage. +(d) Module PID_i: Param FAST_FZP_PID_LOOP(4) 0 +(e) Module PID_i: Param FAST_FZP_PID_LOOP(6) 1 + with d and e it will now stabilize the sample stage position at zero +(f) Module PID_i: Param FAST_FZP_PID_LOOP(0) remains 0 (no move ahead +in a scan) + +With these settings perform a scan. Use a field of view matching the +range of the FZP scanner. Watch the sample stage position in Orchestra +to ensure it does not change! +A good range should be 2.8 microns in x and 1.8 microns in y. +Reconstruct this scan with Michal's position refinement code and ask +for an update of the FZP matrix. This can then be put into the +AnalogInput_6259 module. +I guess one has to have the matrix found for the interferometer in the +reconsturction code, such that the new matrix of the FZP scanner will +adjust the coordinate system of the FZP scanner to the system of the +interferometer. Later these positions are used together and should +match in direction. + +3. Back to default operating values: +(a) Module PID_i: Set Param FAST_FZP_PID_LOOP(0) to -0.5, for up to +0.5 microns move ahead in a scan. +All other parameters are already as supposed to be. +Default values for the sample stage pid, fast FZP parameter are "- +0.5,0,0,0,0.1,0.5,0" +Default values for the fzp pid, fast FZP parameter are "1,0.0001,0.1,- +4,0,0,0" +``` + +Naming: "move ahead" in the text above (sample PIDs `FAST_FZP_PID_LOOP(0)` < 0) is called **move-beyond** from +2026-10-23 on: the sample stage moves *beyond* the scan point towards the next one, the FZP compensates and so sits +beyond its centre during the exposure, on the side that gives it more stroke for the next step. It is not a +"pre-move" of the FZP to the next point (that is `TargetSelect` `use_final` 1, see below). + +## Update 2026-10-20 (flOMNI loop on PREEMPT_RT, mpc2844) + +The loop has new modules since the text above was written (flOMNI `FeedbackLoop/tuning_guide.md`, +`feedback_report.md` 5.6, 6n, 6o, 6q). Parameters are changed in the web HMI (`http://mpc2844:8080`, tab +Parameters) instead of OrchestraHMI. `i` = x or y, as above. Module names now: `PID_x`, `PID_y` (sample stage), +`PID_x_fzp`, `PID_y_fzp` (FZP scanner), `AnalogInput_6289` (was `AnalogInput_6259`). + +**FAST_FZP_PID_LOOP has 7 elements** (`PID.cpp`). Sample stage PIDs: (0) move-beyond (negative, µm, point-by-point +scans only; section "Move-beyond" below), (4) FZP error threshold (µm) and (5) FZP drive limit (V) for holding the sample stage while the FZP +takes the step (5 = 0: never held), (6) 1 = hold the sample stage at 0. FZP PIDs: (0) mode (1 operation, 2 FZP +centred on its cap sensor = disabled, 3 not commissioned, 4 debug), (1)–(3) min / max / gain of the old velocity +correction (−4 before 2026-10-19, now 0: replaced by the feedforward). + +**In addition to step 1 (FZP disabled):** + +- `Feedforward`: `enable` 0,0 — the static FZP feedforward adds its drive also in FZP mode 2. +- `AbbeCorrection`: `enable` 0 (default until the signs are known). +- `TargetSelect_x`, `TargetSelect_y`: `use_final` 0; `OnTargetTrigger`: `WaitForSlew` 1 (defaults). +- `FreqGen`: `enable` 0 (excitation for measurements; default). + +**In addition to step 2 (FZP-only calibration scan):** + +- FZP range: `PID_x_fzp` / `PID_y_fzp` `Ub` / `Lb` are ±1.5 V in operation (the cap sensors are calibrated over + about ±2 V only). For the field of view above (2.8 × 1.8 µm) widen them, e.g. to ±10 V (FZP ≈ 0.13 µm/V in x, + ≈ 0.05 µm/V in y). The feedforward stays off (`enable` 0,0) during this scan. +- The detector trigger (`OnTargetTrigger`) checks the relative error sample vs FZP (tolerance / hold time from the + ConstEmitter, BEC `interferometer_trigger_*`, default 10 nm / 3 ms) and waits `MinDelay_ms` (15 ms). + +**Step 3, default operating values (replace the values in the text above):** + +| Module | Parameter | Value | +|---|---|---| +| `PID_x`, `PID_y` | `FAST_FZP_PID_LOOP` | `0,0,0,0,0.1,0,0` (no move-beyond: −0.5 µm is larger than the ±1.5 V FZP stroke, x ≈ ±0.19 µm, y ≈ ±0.08 µm) | +| `PID_x_fzp` | `FAST_FZP_PID_LOOP`, `Ub` / `Lb` | `1,0.0001,0.1,0,0,0,0`, ±1.5 V | +| `PID_y_fzp` | `FAST_FZP_PID_LOOP`, `Ub` / `Lb` | `1,0.0005,0.02,0,0,0,0`, ±1.5 V | +| `Feedforward` | `enable`; `ff_limit`, `out_min` / `out_max` | 1,1; ±1.5 V (as the FZP `Ub` / `Lb`) | +| `OnTargetTrigger` | `WaitForSlew`, `MinDelay_ms` | 1, 15 | +| `TargetSelect_x`, `TargetSelect_y` | `use_final` | 0 | +| `AbbeCorrection` | `enable` | 0 (until the signs are known) | +| `AnalogInput_6289` | `MatrixCorr` | the new matrix from the calibration | + +Check afterwards: web HMI tab **Loop check**, level 1 (it expects these values and lists live values that differ +from the `.xmp` files); save the new values as default (Parameters → Unsaved changes) or in the `.xmp`. + +**After the calibration (per experiment): faster scans, exposure during the motion.** With the FZP calibrated +over a wider range the FZP can take the small steps alone (flOMNI `feedback_report.md` 6q, tested at ±7 V): + +- `PID_x_fzp` / `PID_y_fzp` `Ub` / `Lb` and the `Feedforward` limits to the calibrated range (e.g. ±7 V); +- `TargetSelect_x` / `_y` `use_final` 1 (the FZP aims at the new point at once, the sample stage follows its + S-curve) and `OnTargetTrigger` `WaitForSlew` 0 (trigger on the relative error only); optionally `MinDelay_ms` 0; +- optionally move-beyond, sized to the FZP stroke (≈ half of it, see below): at ±7 V (x ≈ ±0.9 µm, y ≈ ±0.36 µm) + `PID_x` (0) e.g. −0.5, `PID_y` (0) e.g. −0.2 (the values tested in `feedback_report.md` 6q). + +Measured with 101 ms exposures: 0.2–1 µm steps trigger after 4.5–5.8 ms instead of 17–32 ms; the error during the +exposure grows with the step (1 µm: ≈ 6 nm max instead of 2.5 nm), so the setting depends on the experiment. + +## Move-beyond (`PID_x` / `PID_y` `FAST_FZP_PID_LOOP(0)` < 0), 2026-10-23 + +**What it does** (`PID.cpp`, unchanged since the original loop). Only in point-by-point scans (scan state 2). The +TrajectoryGenerator gives the next scan point (outputs 5 / 6, PID input 9). The sample PID then aims not at the +target T but at T + m · sign(next − T), m = min(|FAST_FZP_PID_LOOP(0)|, |next − T|): the sample stage moves *beyond* +the point towards the next one, by at most the step. The FZP loop keeps its own target T (it does not see m), so the +FZP compensates the offset: during the exposure it holds the relative position at T while sitting at +m off its +centre. At the next point the FZP then has stroke R + m in the step direction instead of R (R = stroke from the centre), +so larger steps are taken by the FZP alone. Steps smaller than |FAST_FZP_PID_LOOP(0)|: the sample stage is one point +ahead, the FZP moves between 0 and +step only. The offset is not shaped (InputSlew acts on T only); the sample stage +moves to it with its own (slow) loop, i.e. partly during the exposure, and the FZP compensates that motion. + +**Is it still the same in the new loop?** Yes. The relative position is computed as before: +FZP error = Cap − (Chan − target) (mode 1), `Position sample vs FZP` = Chan − Cap (PID output 3, RT_sampler). The +sample PIDs still get the shaped target (InputSlew) + move-beyond and the raw interferometer. The FZP side now has +two additions that act on the FZP side only: `TargetSelect` (`use_final` 0 = shaped target as before, 1 = final +target) and `AbbeCorrection` (Chan corrected for the stage tilt, see below). The `Feedforward` uses the same FZP +target and Chan, so it supplies the +m offset of the FZP directly (static term m / K). + +**Sizing.** During the exposure the FZP has to hold m + Abbe correction + noise / settling inside its stroke: +|m| ≤ R − reserve, as a rule ≈ R / 2. R per axis = `Ub` × K (K ≈ 0.13 µm/V x, ≈ 0.05 µm/V y): + +| FZP `Ub`/`Lb` | R x / y | move-beyond x / y (≈ R/2) | +|---|---|---| +| ±1.5 V (production) | ±0.19 / ±0.08 µm | ≈ −0.1 / ≈ −0.04 µm (production: 0) | +| ±7 V (after calibration) | ±0.9 / ±0.36 µm | −0.5 / −0.2 µm (tested, 6q) | + +The original default −0.5 µm in both axes dates from the ±9 V limits of the old loop (x ≈ ±1.2 µm, about R/2; in y +±9 V ≈ ±0.45 µm, i.e. about the full stroke). When the FZP saturates while holding the offset, the relative error grows and the trigger +waits. + +**Abbe correction.** `AbbeCorrection` adds a new term to the measured sample x / y on the FZP side only: +x = Chan2_x + θz · offset_y_for_x, y = Chan1_y + θz · offset_x_for_y (θz = Chan3 / 13 mm). So yes: it is an error of +the sample x / y position (stage tilt about z, not seen by the x / y interferometer beams) that is corrected by the +FZP x / y, not by the sample stage (`PID_x` / `PID_y` keep the raw Chan, the rotZ piezo is too slow). The positions +recorded for the reconstruction (`Position sample vs FZP`) contain it as well. It uses FZP stroke (limited to +`correction_limit_um` 0.1 µm per axis; y at rest ≈ 2 nm, ≈ 30–37 nm during 20 µm x steps) — keep this in the reserve +of the move-beyond, in y it is a large part of the ±0.08 µm stroke. Keep it off (`enable` 0) during the FZP +calibration scan (step 2). + +**Notes.** +- Hold of the sample stage, (4) / (5): input 8 of `PID_x` / `PID_y` was the FZP PID output *before* the + `Feedforward` (with the feedforward on only the remainder, not the real FZP drive). Since 2026-10-23 it is the + `Feedforward` output (FZP drive incl. feedforward), so (5) again compares the real FZP drive. No change with the + feedforward off or with (5) = 0 (production). +- On the last point of a scan the next point output is 0, so the move-beyond points towards 0 (as in the old loop). +- `TargetSelect` `use_final` 1 is a different thing: the FZP aims at the new point at once while the sample stage + follows the S-curve. Both can be combined (6q, E0). diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_limit_the_fzp_scanner_range.md b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_limit_the_fzp_scanner_range.md new file mode 100644 index 00000000..89755f1e --- /dev/null +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_limit_the_fzp_scanner_range.md @@ -0,0 +1,41 @@ +# How to limit the FZP scanner range of flOMNI + +Source of `local_contact_only-howto_limit_the_fzp_scanner_range.pdf`. The original text is kept unchanged; the +section "Update 2026-10-20" lists what the present flOMNI loop (PREEMPT_RT, 100 µs, branch `rt_preemt`) needs in +addition. "ahead movement" (element 0) is called **move-beyond** from 2026-10-23 on: the sample stage moves beyond the +scan point towards the next one, the FZP compensates and sits off-centre, which gives it stroke for the next step. + +```text +to limit the fzp scanner ranges +in PID_ FAST FZP PID LOOP +0: reduce to small ahead movement range, such as -0.05 +4: reduce error threshold, eg 0.01 +5: inform PID that FZP range is small, such as 0.1 + +in PID_ _FZP +limit the analog output LB and UB +``` + +## Update 2026-10-20 (flOMNI loop on PREEMPT_RT, mpc2844) + +The limited range is the present operating state: `PID_x_fzp` / `PID_y_fzp` `Ub` / `Lb` = ±1.5 V because the cap +sensors are calibrated over about ±2 V only (flOMNI `feedback_report.md` 6n). FZP ≈ 0.13 µm/V in x, ≈ 0.05 µm/V in y, +so ±1.5 V ≈ ±0.19 µm (x) / ±0.08 µm (y). In the sample stage PIDs (`PID_x`, `PID_y`) `FAST_FZP_PID_LOOP` is +`0,0,0,0,0.1,0,0`: no move-beyond, (5) = 0 (the sample stage always compensates fully). + +Changing the FZP range now involves, in addition to the text above: + +- **`Feedforward`** (static FZP feedforward): `ff_limit`, `out_min` / `out_max` to the same values as the FZP `Ub` / + `Lb` (they limit drive + feedforward). +- **`FreqGen`** `out_min` / `out_max` (channels 0 / 1) limit the FZP drive only while an excitation is running + (measurements). +- **`OnTargetTrigger`** `WaitForSlew` and **`TargetSelect_x` / `_y`** `use_final`: keep the defaults (1 / 0) with a + small range. `use_final` 1 + `WaitForSlew` 0 ("exposure during the motion") only makes sense when the FZP range + covers the scan step; steps larger than the range then still wait for the sample stage. +- Move-beyond (0) must fit into the stroke: the FZP holds it during the exposure, together with the Abbe correction + (up to `correction_limit_um` 0.1 µm when enabled) and noise. Rule ≈ half the stroke: at ±1.5 V ≈ −0.1 µm (x), + ≈ −0.04 µm (y); at ±7 V −0.5 / −0.2 µm (tested). Production: 0. Details: calibration how-to, section "Move-beyond". +- (5) compares the real FZP drive incl. the feedforward (input 8 of `PID_x` / `PID_y` = `Feedforward` output since + 2026-10-23; before it was the FZP PID output without the feedforward). +- After a change: web HMI tab **Loop check** level 1 (expected settings) and, with the beamline ready, the scan test + (level 4: positioning time, stability during the exposure per step size). -- 2.54.0 From 79f5cced428dbd9612058f59e05a0e5578d4468f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 12:55:31 +0000 Subject: [PATCH 02/19] feat(flomni): flomni.realtime_system - loop settings through the web HMI; ConstEmitter emitter_* removed The ConstEmitter is no longer in the flOMNI loop (flOMNI 773df74, branch fzp-move-beyond): its values are module parameters, set with their rules by the web HMI on mpc2844 (RT-system webhmi/loopsettings.py, REST /api/settings). - flomni_realtime_system.py (new): FlomniRealtimeSystem, flomni.realtime_system: show / get / check / set (refused with the reason, adjust=True for the dependent settings) / profiles / apply_profile / unsaved / save_as_default, shortcuts trigger_* and tracking_targets; stdlib HTTP, API token from ~/.flomni_webhmi_token (FLOMNI_WEBHMI_TOKEN_FILE), URL http://:8080 (FLOMNI_WEBHMI_URL). No logic of its own: the same rules as the web page. - flomni.py: flomni.realtime_system; laser_parameters_* and laser_parameters_tweak_tracking() through it; interferometer_trigger_* are aliases of realtime_system.trigger_* (renamed, Mirko: "interferometer" was confusing next to flomni.laser_*) - rt_flomni_ophyd.py: emitter_* methods and their USER_ACCESS entries removed (the e-commands now answer with an error line); move_xyz stays - tests: test_flomni_realtime_system.py against a small local HTTP server standing in for the web HMI Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019aWcwG89wi7oVyrefukXX6 --- .../plugins/flomni/flomni.py | 123 +++------ .../plugins/flomni/flomni_realtime_system.py | 256 ++++++++++++++++++ csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py | 145 +--------- .../test_flomni_realtime_system.py | 140 ++++++++++ 4 files changed, 439 insertions(+), 225 deletions(-) create mode 100644 csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py create mode 100644 tests/tests_bec_ipython_client/test_flomni_realtime_system.py diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index 36c5ec29..bf19f6af 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -17,6 +17,7 @@ from typeguard import typechecked from csaxs_bec.bec_ipython_client.plugins.cSAXS import cSAXSBeamlineChecks from csaxs_bec.bec_ipython_client.plugins.flomni.flomni_optics_mixin import FlomniOpticsMixin +from csaxs_bec.bec_ipython_client.plugins.flomni.flomni_realtime_system import FlomniRealtimeSystem from csaxs_bec.bec_ipython_client.plugins.flomni.gui_tools import flomniGuiTools from csaxs_bec.bec_ipython_client.plugins.flomni.x_ray_eye_align import XrayEyeAlign from csaxs_bec.bec_ipython_client.plugins.OMNY_shared.angle_file import ( @@ -503,25 +504,27 @@ class FlomniSampleTransferMixin: def laser_tracker_off(self): dev.rtx.controller.laser_tracker_off() + # laser tracker targets and thresholds: settings of the real-time loop (flomni.realtime_system, group + # "tracking"; until 2026-10-23 ConstEmitter lines set through the CommunicationServer e-commands) def laser_parameters_show_all(self): - dev.rtx.controller.emitter_show_all() + self.realtime_system.show(group="tracking") def laser_parameters_set_targety(self, val: float): - dev.rtx.controller.emitter_set_tracking_target_y(val) + self.realtime_system.set(tracking_target_y=val) def laser_parameters_set_targetz(self, val: float): - dev.rtx.controller.emitter_set_tracking_target_z(val) + self.realtime_system.set(tracking_target_z=val) def laser_parameters_set_intensity_threshold_laser(self, val: float): - dev.rtx.controller.emitter_set_intensity_threshold_laser(val) + self.realtime_system.set(interferometer_threshold=val) def laser_parameters_set_psd_intensity_threshold_tracking_low(self, val: float): - dev.rtx.controller.emitter_set_psd_intensity_threshold_tracking_low(val) + self.realtime_system.set(psd_threshold_low=val) def laser_parameters_tweak_tracking(self): """Interactive tweak of the two laser tracking target positions. - Arrow keys nudge the ConstEmitter tracking targets by a fixed step of + Arrow keys nudge the tracking targets (realtime_system tracking_target_y / _z) by a fixed step of 0.01 (absolute set of current +/- step): left / right : target y -/+ up / down : target z +/- @@ -542,6 +545,7 @@ class FlomniSampleTransferMixin: import tty controller = dev.rtx.controller + rts = self.realtime_system if not controller.laser_tracker_check_enabled(): print( @@ -554,11 +558,7 @@ class FlomniSampleTransferMixin: step = 0.02 def read_targets(): - values = controller.emitter_get() - return ( - values["targetpos_tracking_y"], - values["targetpos_tracking_z"], - ) + return rts.tracking_targets() def signal_fzpx(): # second channel (index 1) of show_signal_strength_interferometer, @@ -597,8 +597,7 @@ class FlomniSampleTransferMixin: print("\n\rExiting tweak mode\r") break elif key == "s": - controller.emitter_set_tracking_target_y(start_y) - controller.emitter_set_tracking_target_z(start_z) + rts.set(tracking_target_y=start_y, tracking_target_z=start_z, quiet=True) print("\rReturned to start positions\r") print_status() elif key == "\x1b": # escape sequences for arrow keys @@ -606,16 +605,16 @@ class FlomniSampleTransferMixin: if next1 == "[": target_y, target_z = read_targets() if next2 == "A": # up: target z + - controller.emitter_set_tracking_target_z(target_z + step) + rts.set(tracking_target_z=target_z + step, quiet=True) print_status() elif next2 == "B": # down: target z - - controller.emitter_set_tracking_target_z(target_z - step) + rts.set(tracking_target_z=target_z - step, quiet=True) print_status() elif next2 == "C": # right: target y + - controller.emitter_set_tracking_target_y(target_y + step) + rts.set(tracking_target_y=target_y + step, quiet=True) print_status() elif next2 == "D": # left: target y - - controller.emitter_set_tracking_target_y(target_y - step) + rts.set(tracking_target_y=target_y - step, quiet=True) print_status() except IOError: pass @@ -710,81 +709,36 @@ class FlomniSampleTransferMixin: else: print("The rt feedback is \x1b[91mNOT\x1b[0m running.") - def interferometer_trigger_show(self): - """Show the detector-trigger stability criterion of the interferometer feedback. + # detector trigger: renamed to flomni.realtime_system.trigger_* (2026-10-23, Mirko: "interferometer" was + # confusing next to flomni.laser_*); the values are settings of the real-time loop (OnTargetTrigger parameters, + # until 2026-10-23 ConstEmitter lines). The old names stay as aliases. + def _renamed(self, old, new): + print(f"flomni.{old} is now flomni.realtime_system.{new}") - A step-scan point is triggered when the relative position sample vs FZP (interferometer - minus FZP cap sensor) is within the tolerance in x and in y, each averaged over 0.5 ms, - for the hold time without interruption. - """ - values = dev.rtx.controller.emitter_get_ontarget() - tol_nm = values["ontarget_tolerance_um"] * 1000 - if tol_nm > 0: - print(f"Detector trigger: |position error| x and y <= {tol_nm:.1f} nm " - f"for {values['ontarget_hold_ms']:.1f} ms.") - else: - print("Detector trigger: former criterion (on-target flags of the FZP PID loops).") - if values.get("trigger_on_move") is not None: - mode = "on" if values["trigger_on_move"] else "off" - print(f"Trigger after every position change (not only at scan points): {mode}.") + def interferometer_trigger_show(self): + """Alias of flomni.realtime_system.trigger_show().""" + self._renamed("interferometer_trigger_show()", "trigger_show()") + self.realtime_system.trigger_show() def interferometer_trigger_set_tolerance_nm(self, tolerance_nm: float): - """Set the detector-trigger tolerance in nm (per axis, x and y). - - Typical: 30 nm for a fast low-resolution scan, 10 nm standard, 2 nm for the highest - resolution (needs a few ms more per point). 0 selects the former criterion (on-target flags - of the FZP PID loops). - - Args: - tolerance_nm (float): tolerance in nm, 0 or 1 ... 1000. - """ - if tolerance_nm != 0 and not 1 <= tolerance_nm <= 1000: - raise FlomniError( - "The trigger tolerance must be 0 (former criterion) or 1 ... 1000 nm." - ) - dev.rtx.controller.emitter_set_ontarget_tolerance(tolerance_nm / 1000) - self.interferometer_trigger_show() + """Alias of flomni.realtime_system.trigger_set_tolerance_nm().""" + self._renamed("interferometer_trigger_set_tolerance_nm()", "trigger_set_tolerance_nm()") + self.realtime_system.trigger_set_tolerance_nm(tolerance_nm) def interferometer_trigger_set_hold_ms(self, hold_ms: float): - """Set how long (ms) the position must stay within the tolerance before the trigger. - - Typical 3 ms. Longer = safer against a position that is only passing through the tolerance. - - Args: - hold_ms (float): hold time in ms, 0 ... 100. - """ - if not 0 <= hold_ms <= 100: - raise FlomniError("The trigger hold time must be 0 ... 100 ms.") - dev.rtx.controller.emitter_set_ontarget_hold_ms(hold_ms) - self.interferometer_trigger_show() + """Alias of flomni.realtime_system.trigger_set_hold_ms().""" + self._renamed("interferometer_trigger_set_hold_ms()", "trigger_set_hold_ms()") + self.realtime_system.trigger_set_hold_ms(hold_ms) def interferometer_trigger_set_after_move(self, enable: bool): - """Send one detector trigger after every position change, not only at scan points. - - With True every direct move of the sample stage (e.g. umv of rtx / rty / rtz, or - dev.rtx.controller.move_xyz) gives exactly one trigger, as soon as the position is stable - (same tolerance and hold time as for scan points). False: triggers at scan points only. - """ - dev.rtx.controller.emitter_set_trigger_on_move(1 if enable else 0) - self.interferometer_trigger_show() + """Alias of flomni.realtime_system.trigger_set_after_move().""" + self._renamed("interferometer_trigger_set_after_move()", "trigger_set_after_move()") + self.realtime_system.trigger_set_after_move(enable) def interferometer_trigger_help(self): - """Print the detector-trigger commands with a short explanation.""" - lines = [ - "Detector trigger (step scans): a point is triggered when the relative position", - "sample vs FZP is within the tolerance in x and y (0.5 ms average) for the hold time.", - " flomni.interferometer_trigger_show() tolerance and hold time", - " flomni.interferometer_trigger_set_tolerance_nm(val) 30 coarse, 10 standard,", - " 2 finest; 0 = former", - " criterion (FZP PID flags)", - " flomni.interferometer_trigger_set_hold_ms(val) e.g. 3", - " flomni.interferometer_trigger_set_after_move(True) also one trigger after every", - " position change (False: off)", - " dev.rtx.controller.move_xyz(x, y, z) move all three axes at once", - "The values are live; after a restart of the control loop the start values", - "(10 nm, 3 ms) from the loop parameter file apply again.", - ] - print("\n".join(lines)) + """Alias of flomni.realtime_system.help().""" + self._renamed("interferometer_trigger_help()", "help()") + self.realtime_system.help() def lights_off(self): self.device_manager.devices.fsamx.controller.lights_off() @@ -1819,6 +1773,9 @@ class Flomni( self._webpage_gen.start() self.OMNYTools = OMNYTools(self.client) + # expert functions of the real-time loop (settings with rules, via the web HMI on the loop machine; + # flomni_realtime_system.py): flomni.realtime_system.show() / .set(...) / .help() + self.realtime_system = FlomniRealtimeSystem(device_manager=self.device_manager) self.reconstructor = PtychoReconstructor(self.ptycho_reconstruct_foldername) self.tomo_id_manager = TomoIDManager() self.align = XrayEyeAlign(self.client, self) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py new file mode 100644 index 00000000..79eaaec0 --- /dev/null +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py @@ -0,0 +1,256 @@ +"""flomni.realtime_system: the operating settings of the flOMNI real-time loop (expert functions). + +The settings, the parameters they map onto and the rules between them live in the flOMNI instrument repository +(FeedbackLoop/loopsettings/flomni.json) and are applied by the web HMI on the loop machine (RT-system +webhmi/loopsettings.py). This client only talks to its REST API (/api/settings); it holds no logic of its own, so the +web page (tab "Loop settings") and BEC always behave the same way. + + flomni.realtime_system.show() all settings, rules, scan state + flomni.realtime_system.set(trigger_tolerance_nm=5) change (refused if a rule objects) + flomni.realtime_system.set(fzp_enable=0, adjust=True) ... and adjust the dependent settings + flomni.realtime_system.check(fzp_range_V_x=1.0) dry run + flomni.realtime_system.apply_profile("fzp_off") a named set of settings + flomni.realtime_system.save_as_default() write the live values into the .xmp files + +Changes are live only: the next start of the control loop uses the .xmp files again (save_as_default keeps them). + +Connection: the web HMI URL (default http://:8080, env FLOMNI_WEBHMI_URL) and an API token in a file +(default ~/.flomni_webhmi_token, env FLOMNI_WEBHMI_TOKEN_FILE; the same token as in the web HMI's api_token_file on +the loop machine, see flOMNI FeedbackLoop/loopsettings/README.md). +""" + +import json +import os +import urllib.error +import urllib.request + +DEFAULT_TOKEN_FILE = "~/.flomni_webhmi_token" +DEFAULT_PORT = 8080 + + +class RealtimeSystemError(Exception): + """A request was refused (rules, scan running) or the web HMI could not be reached.""" + + +def _fmt(v): + if v is None: + return "-" + if isinstance(v, float): + return f"{v:.6g}" + return str(v) + + +class FlomniRealtimeSystem: + def __init__(self, url=None, token_file=None, device_manager=None, timeout=5.0): + self._url = url + self._token_file = token_file + self._device_manager = device_manager + self.timeout = timeout + + # ── connection ──────────────────────────────────────────────────────────── + + @property + def url(self): + if self._url: + return self._url.rstrip("/") + env = os.environ.get("FLOMNI_WEBHMI_URL") + if env: + return env.rstrip("/") + host = "mpc2844.psi.ch" + try: # the host of the rt controller (device config of rtx) + host = self._device_manager.devices.rtx._config["deviceConfig"]["host"] or host + except Exception: # noqa: BLE001 + pass + return f"http://{host}:{DEFAULT_PORT}" + + def _token(self): + path = os.path.expanduser(self._token_file or os.environ.get("FLOMNI_WEBHMI_TOKEN_FILE") or DEFAULT_TOKEN_FILE) + try: + with open(path) as f: + tok = f.readline().strip() + except OSError: + raise RealtimeSystemError( + f"No API token for the web HMI: {path} not readable. Copy the token from the loop machine " + "(~/loop-data/webhmi/api_token of the user control) into this file (mode 600), or set " + "FLOMNI_WEBHMI_TOKEN_FILE.") from None + if not tok: + raise RealtimeSystemError(f"API token file {path} is empty") + return tok + + def _request(self, path, body=None): + data = None if body is None else json.dumps(body).encode() + req = urllib.request.Request(self.url + path, data=data, method="GET" if body is None else "POST", + headers={"Authorization": "Bearer " + self._token(), "X-Client": "BEC", + "Content-Type": "application/json"}) + try: + with urllib.request.urlopen(req, timeout=self.timeout) as r: + return json.load(r) + except urllib.error.HTTPError as exc: + try: + detail = json.load(exc).get("detail", exc.reason) + except Exception: # noqa: BLE001 + detail = exc.reason + if exc.code == 401: + detail = f"{detail} (API token not accepted by the web HMI: compare with its api_token_file)" + raise RealtimeSystemError(f"web HMI {self.url}{path}: {exc.code} {detail}") from None + except (urllib.error.URLError, OSError) as exc: + raise RealtimeSystemError(f"web HMI {self.url} not reachable: {exc}") from None + + # ── reading ─────────────────────────────────────────────────────────────── + + def read(self) -> dict: + """The full state as the web HMI reports it (settings, rules, scan state, profiles).""" + return self._request("/api/settings") + + def get(self, name: str = None): + """Live value of one setting, or a dict of all values.""" + values = {s["name"]: s["value"] for s in self.read()["settings"]} + if name is None: + return values + if name not in values: + raise RealtimeSystemError(f"unknown setting '{name}'; known: {', '.join(values)}") + return values[name] + + def show(self, group: str = None, expert: bool = False): + """Print the settings (live value, .xmp value, markers), the violated rules and the scan state.""" + snap = self.read() + groups = {g["name"]: g for g in snap["groups"]} + sc = snap.get("scan") or {} + print(f"Loop settings {snap.get('instrument', '')} ({self.url}) - " + + (f"scan running (mode {_fmt(sc.get('mode'))})" if sc.get("active") else "no scan")) + for gname, g in groups.items(): + if group and gname != group: + continue + if g.get("expert") and not expert and not group: + continue + rows = [s for s in snap["settings"] if s["group"] == gname] + if not rows: + continue + print(f"\n{g.get('title', gname)}" + (" (changed only between scans)" if g.get("interlock") else "")) + for s in rows: + info = f" ({s['info']['label']} {s['info']['value']:.4g} {s['info']['unit']})" if s.get("info") else "" + mark = (" NOT SAVED (.xmp " + _fmt(s["saved"]) + ")" if s["unsaved"] else "") + \ + (" INCONSISTENT: " + "; ".join(s["issues"]) if not s["consistent"] else "") + print(f" {s['name']:30s} {_fmt(s['value']):>10s} {s.get('unit', ''):3s}{info}{mark}") + bad = [r for r in snap["rules"] if not r["ok"]] + if bad: + print() + for r in bad: + print(f"{r['severity'].upper()}: {r['message']}") + if not expert and not group and any(g.get("expert") for g in snap["groups"]): + print("\n(expert groups hidden: show(expert=True))") + print("\n" + (snap.get("note") or "")) + + def profiles(self): + """Print the named profiles and their values.""" + for p in self.read()["profiles"]: + print(f"{p['name']:18s} {p.get('title', '')}") + if p.get("help"): + print(f"{'':18s} {p['help']}") + print(f"{'':18s} " + ", ".join(f"{k}={_fmt(v)}" for k, v in p["values"].items())) + + def unsaved(self): + """Print the parameters whose live value differs from the .xmp (settings only).""" + un = self._request("/api/settings/unsaved") + if not un: + print("All settings are saved: the .xmp files hold the live values.") + for u in un: + print(f" {u['module']}.{u['param']}: .xmp {u['saved']} -> live {u['live']}") + return un + + # ── changing ────────────────────────────────────────────────────────────── + + @staticmethod + def _print_result(r, dry=False): + head = ("Check: " if dry else "") + ("applied" if r.get("applied") else ("ok" if r["ok"] else "REFUSED")) + if r["ok"] and not r["params"]: + head = "no change" + print(head + (f" (profile {r['profile']})" if r.get("profile") else "")) + for c in r["changes"]: + print(f" {c['name']}: {_fmt(c['old'])} -> {_fmt(c['new'])}" + + (f" (adjusted by rule {c['adjusted']})" if c.get("adjusted") else "")) + for e in r["errors"]: + print(f" ERROR: {e}") + for w in r["warnings"]: + print(f" warning: {w}") + if r.get("applied"): + print(" " + r.get("note", "Live only: save_as_default() to keep it.")) + + def check(self, adjust: bool = False, **changes) -> dict: + """Dry run: what would change, which rules object. Nothing is sent to the loop.""" + r = self._request("/api/settings/plan", {"changes": changes, "profile": None, "adjust": adjust}) + self._print_result(r, dry=True) + return r + + def set(self, adjust: bool = False, quiet: bool = False, **changes) -> dict: + """Change settings, e.g. set(trigger_tolerance_nm=5). All parameters change in one loop step. + + A rule that objects refuses the request (RealtimeSystemError); with adjust=True the settings the rule names + are changed with it (never the ones given here). Live only until save_as_default(). quiet: print nothing + (interactive tools); a refusal still raises. + """ + if not changes: + raise RealtimeSystemError("nothing to set: e.g. set(trigger_tolerance_nm=5); show() lists the names") + r = self._request("/api/settings/apply", {"changes": changes, "profile": None, "adjust": adjust}) + if not quiet: + self._print_result(r) + if not r["ok"]: + raise RealtimeSystemError("; ".join(r["errors"])) + return r + + def apply_profile(self, name: str, adjust: bool = False, **changes) -> dict: + """Apply a named profile (profiles() lists them); extra keyword values override the profile's.""" + r = self._request("/api/settings/apply", {"changes": changes, "profile": name, "adjust": adjust}) + self._print_result(r) + if not r["ok"]: + raise RealtimeSystemError("; ".join(r["errors"])) + return r + + def save_as_default(self, *names: str, confirm: bool = True) -> dict: + """Write the live values of the settings' parameters (all, or the named settings) into the .xmp files, so the + next loop start uses them (the web HMI keeps a backup of each file).""" + un = self._request("/api/settings/unsaved") + if not un: + print("Nothing to save: the .xmp files hold the live values.") + return {"saved": []} + if not names: + print("Save as default (written into the .xmp files of the loop):") + for u in un: + print(f" {u['module']}.{u['param']}: {u['saved']} -> {u['live']}") + if confirm and input("Save? [y/N] ").strip().lower() not in ("y", "yes"): + print("not saved") + return {"saved": []} + r = self._request("/api/settings/save", {"names": list(names) or None}) + print(r.get("message", ""), "; ".join(r.get("saved", []))) + return r + + # ── shortcuts (detector trigger, laser tracker) ──────────────────────────── + + def trigger_show(self): + """Detector trigger (step scans): tolerance, hold time, minimum delay, trigger after every move.""" + self.show(group="trigger") + + def trigger_set_tolerance_nm(self, tolerance_nm: float): + """|relative position sample vs FZP| x and y within this (nm) for the hold time. 30 coarse, 10 standard, 2 finest; + 0 = the former criterion (OnTarget_legacy flags of the FZP PIDs).""" + return self.set(trigger_tolerance_nm=tolerance_nm) + + def trigger_set_hold_ms(self, hold_ms: float): + """How long the position must stay within the tolerance before the trigger (ms), typical 3.""" + return self.set(trigger_hold_ms=hold_ms) + + def trigger_set_after_move(self, enable: bool): + """True: one detector trigger after every direct position change (umv rtx / rty / rtz, move_xyz) once on target; + False: triggers at step-scan points only.""" + return self.set(trigger_on_move=1 if enable else 0) + + def tracking_targets(self): + """-> (target y, target z) of the laser tracker on the PSD.""" + v = self.get() + return v["tracking_target_y"], v["tracking_target_z"] + + def help(self): + """Print the functions with a short explanation.""" + print(__doc__.split("\n\nConnection:")[0]) + print("\nShortcuts: trigger_show(), trigger_set_tolerance_nm(v), trigger_set_hold_ms(v)," + " trigger_set_after_move(True/False), tracking_targets(); profiles(), unsaved(), get(name)") diff --git a/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py b/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py index afa49424..3ef464b1 100644 --- a/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py +++ b/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py @@ -45,17 +45,6 @@ class RtFlomniController(Controller): "laser_tracker_check_signalstrength", "laser_tracker_check_enabled", "is_axis_moving", - "emitter_show_all", - "emitter_get", - "emitter_set_tracking_target_y", - "emitter_set_tracking_target_z", - "emitter_set_intensity_threshold_laser", - "emitter_set_psd_intensity_threshold_tracking_low", - "emitter_get_ontarget", - "emitter_set_ontarget_tolerance", - "emitter_set_ontarget_hold_ms", - "emitter_help", - "emitter_set_trigger_on_move", "move_xyz", ] @@ -404,84 +393,9 @@ class RtFlomniController(Controller): return True return False - def emitter_get(self) -> dict: - """Read the runtime constants served by the ConstEmitter Orchestra module. - - These are values that used to live in individual module parameter files - (tracking targets and intensity thresholds) and are now emitted centrally - so they can be tweaked live. The rotation-loop laser threshold - (intensity_threshold_rot_laser) is a pure startup constant and is - intentionally not reported here. - - Returns: - dict: current emitter values. - """ - ret = self.socket_put_and_receive("es") - # remove trailing \n - ret = ret.split("\n")[0] - values = [float(val) for val in ret.split(",")] - if len(values) != 4: - raise RtCommunicationError( - f"Expected to receive 4 emitter values. Instead received {values}" - ) - return { - "targetpos_tracking_y": values[0], - "targetpos_tracking_z": values[1], - "intensity_threshold_laser": values[2], - "PSD_intensity_threshold_tracking_low": values[3], - } - - def emitter_show_all(self): - """Print all ConstEmitter runtime constants as a table.""" - t = PrettyTable() - t.title = "ConstEmitter Runtime Constants" - t.field_names = ["Name", "Value"] - for key, val in self.emitter_get().items(): - t.add_row([key, val]) - for key, val in self.emitter_get_ontarget().items(): - t.add_row([key, val]) - print(t) - - def emitter_get_ontarget(self) -> dict: - """Read the detector-trigger stability criterion (OnTargetTrigger in the control loop). - - A step-scan point is triggered when the relative position sample vs FZP is within - ``ontarget_tolerance_um`` in x and in y (each averaged over 0.5 ms) for ``ontarget_hold_ms`` - without interruption. - - Returns: - dict: ontarget_tolerance_um, ontarget_hold_ms, trigger_on_move (None from a server - that does not report it yet). - """ - ret = self.socket_put_and_receive("eg") - ret = ret.split("\n")[0] - values = [float(val) for val in ret.split(",")] - if len(values) not in (2, 3): - raise RtCommunicationError( - f"Expected to receive 2 or 3 on-target values. Instead received {values}" - ) - return { - "ontarget_tolerance_um": values[0], - "ontarget_hold_ms": values[1], - "trigger_on_move": values[2] if len(values) == 3 else None, - } - - def emitter_set_ontarget_tolerance(self, val_um: float) -> None: - """Set the detector-trigger tolerance in um, per axis. - - E.g. 0.030 for a low-resolution scan, 0.002 for the highest resolution. A value <= 0 selects - the former criterion (on-target flags of the FZP PID loops, set in their parameter files). - """ - self.socket_put(f"eo{val_um:.6f}") - - def emitter_set_ontarget_hold_ms(self, val_ms: float) -> None: - """Set the time in ms the position must stay within the tolerance before the trigger.""" - self.socket_put(f"eh{val_ms:.3f}") - - def emitter_set_trigger_on_move(self, val: int) -> None: - """1: send one detector trigger after every direct position change (pa / p), once on target - (same criterion as a step-scan point); 0: triggers at step-scan points only.""" - self.socket_put(f"em{1 if val else 0}") + # emitter_* (ConstEmitter get / set through the CommunicationServer e-commands) removed 2026-10-23: the + # ConstEmitter is no longer in the loop; its values are loop settings, set through the web HMI + # (flomni.realtime_system, csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py). def move_xyz(self, x: float, y: float, z: float) -> None: """Move rtx, rty and rtz with one command (CommunicationServer p,,). @@ -502,59 +416,6 @@ class RtFlomniController(Controller): targets.append(target_val) self.socket_put(f"p{targets[0]:.4f},{targets[1]:.4f},{targets[2]:.4f}") - def emitter_help(self) -> None: - """Print the ConstEmitter commands (loop runtime constants) with a short explanation.""" - t = PrettyTable() - t.title = "ConstEmitter commands (rt controller)" - t.field_names = ["Method", "Wire", "Explanation"] - t.align = "l" - rows = [ - ("emitter_show_all()", "es, eg", "print all runtime constants"), - ("emitter_get()", "es", "dict: tracking targets y/z, laser and PSD thresholds"), - ("emitter_get_ontarget()", "eg", "dict: detector-trigger tolerance (um), hold (ms)"), - ("emitter_set_tracking_target_y(val)", "ey", "target of the tracking loop y (PSD)"), - ("emitter_set_tracking_target_z(val)", "ez", "target of the tracking loop z (PSD)"), - ("emitter_set_intensity_threshold_laser(val)", "et", - "interferometer signal below this: slew limiters and x/y/z, FZP PIDs stop"), - ("emitter_set_psd_intensity_threshold_tracking_low(val)", "el", - "PSD intensity below this: tracking PIDs y/z stop"), - ("emitter_set_ontarget_tolerance(val_um)", "eo", - "detector trigger: |relative error| x and y <= val um (e.g. 0.030 coarse, 0.002 fine);" - " <= 0: FZP PID flags"), - ("emitter_set_ontarget_hold_ms(val_ms)", "eh", - "detector trigger: ... held for val ms without interruption"), - ("emitter_set_trigger_on_move(val)", "em", - "1: one detector trigger after every direct move (pa / p) once on target; 0: off"), - ] - for row in rows: - t.add_row(row) - print(t) - print( - "Values are live in the control loop; the start values after a loop restart come from" - " ConstEmitter.xmp." - ) - - def emitter_set_tracking_target_y(self, val: float) -> None: - """Set the y tracking target position (PID_tracking_y).""" - self.socket_put(f"ey{val:.4f}") - - def emitter_set_tracking_target_z(self, val: float) -> None: - """Set the z tracking target position (PID_tracking_z).""" - self.socket_put(f"ez{val:.4f}") - - def emitter_set_intensity_threshold_laser(self, val: float) -> None: - """Set the shared laser intensity threshold. - - Wired into the slew-rate limiters (x/y/z) and the PID loops - (x/y/z and the fast FZP x/y loops). Does not affect the - rotation loops, which use a separate fixed threshold. - """ - self.socket_put(f"et{val:.4f}") - - def emitter_set_psd_intensity_threshold_tracking_low(self, val: float) -> None: - """Set the low PSD intensity threshold used by the tracking PIDs (y and z).""" - self.socket_put(f"el{val:.4f}") - def pid_y(self) -> float: ret = float(self.socket_put_and_receive("G").strip()) return ret diff --git a/tests/tests_bec_ipython_client/test_flomni_realtime_system.py b/tests/tests_bec_ipython_client/test_flomni_realtime_system.py new file mode 100644 index 00000000..16265a7a --- /dev/null +++ b/tests/tests_bec_ipython_client/test_flomni_realtime_system.py @@ -0,0 +1,140 @@ +"""Tests for flomni.realtime_system (flomni_realtime_system.py): the client of the web HMI loop-settings API on the +flOMNI loop machine. A small local HTTP server stands in for the web HMI (RT-system webhmi, /api/settings*); it checks +the token header and answers like the real server. No BEC service, no loop.""" + +import json +import threading +from http.server import BaseHTTPRequestHandler, HTTPServer + +import pytest + +from csaxs_bec.bec_ipython_client.plugins.flomni.flomni_realtime_system import ( + FlomniRealtimeSystem, + RealtimeSystemError, +) + +TOKEN = "test-token-0123456789abcdef" + +SNAP = { + "instrument": "flOMNI", "note": "Live only.", "scan": {"mode": 0, "active": False, "text": ""}, + "groups": [{"name": "trigger", "title": "Detector trigger"}, {"name": "tracking", "title": "Tracker"}], + "settings": [ + {"name": "trigger_tolerance_nm", "group": "trigger", "unit": "nm", "value": 10.0, "saved": 10.0, + "unsaved": False, "consistent": True, "issues": [], "info": None}, + {"name": "tracking_target_y", "group": "tracking", "unit": "", "value": 0.13, "saved": 0.13, + "unsaved": False, "consistent": True, "issues": [], "info": None}, + {"name": "tracking_target_z", "group": "tracking", "unit": "", "value": 0.2, "saved": 0.2, + "unsaved": False, "consistent": True, "issues": [], "info": None}, + ], + "rules": [], "profiles": [{"name": "production", "title": "Production", "help": "", "values": {}}], +} + + +class _Handler(BaseHTTPRequestHandler): + requests = [] + + def log_message(self, *args): + pass + + def _reply(self, code, body): + data = json.dumps(body).encode() + self.send_response(code) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + def _auth(self): + if self.headers.get("Authorization") != "Bearer " + TOKEN: + self._reply(401, {"detail": "login required"}) + return False + return True + + def do_GET(self): + if not self._auth(): + return + if self.path == "/api/settings": + self._reply(200, SNAP) + elif self.path == "/api/settings/unsaved": + self._reply(200, []) + else: + self._reply(404, {"detail": "not found"}) + + def do_POST(self): + if not self._auth(): + return + body = json.loads(self.rfile.read(int(self.headers["Content-Length"]))) + _Handler.requests.append((self.path, body, self.headers.get("X-Client"))) + changes = body.get("changes", {}) + if "move_beyond_um_x" in changes: + self._reply(200, {"ok": False, "errors": ["Move-beyond x does not fit"], "warnings": [], "changes": [], + "params": [], "profile": None, "applied": False}) + return + ch = [{"name": k, "old": 0, "new": v, "adjusted": None} for k, v in changes.items()] + self._reply(200, {"ok": True, "errors": [], "warnings": [], "changes": ch, + "params": [{"module": "M", "param": "p", "old": [0], "new": [1]}] if ch or body.get("profile") else [], + "profile": body.get("profile"), "applied": self.path.endswith("apply"), + "note": "Live only: save as default to keep them."}) + + +@pytest.fixture +def server(tmp_path): + _Handler.requests = [] + srv = HTTPServer(("127.0.0.1", 0), _Handler) + threading.Thread(target=srv.serve_forever, daemon=True).start() + tok = tmp_path / "token" + tok.write_text(TOKEN + "\n") + yield f"http://127.0.0.1:{srv.server_address[1]}", str(tok), tmp_path + srv.shutdown() + + +def test_read_and_shortcuts(server, capsys): + url, tok, _ = server + rts = FlomniRealtimeSystem(url=url, token_file=tok) + assert rts.get("trigger_tolerance_nm") == 10.0 + assert rts.tracking_targets() == (0.13, 0.2) + rts.show() + out = capsys.readouterr().out + assert "trigger_tolerance_nm" in out and "Live only." in out + with pytest.raises(RealtimeSystemError): + rts.get("nosuch") + + +def test_set_sends_changes_and_token(server, capsys): + url, tok, _ = server + rts = FlomniRealtimeSystem(url=url, token_file=tok) + r = rts.trigger_set_tolerance_nm(5) + assert r["applied"] + path, body, client = _Handler.requests[-1] + assert path == "/api/settings/apply" and body == {"changes": {"trigger_tolerance_nm": 5}, "profile": None, + "adjust": False} and client == "BEC" + assert "Live only" in capsys.readouterr().out + rts.set(tracking_target_y=0.2, quiet=True) + assert capsys.readouterr().out == "" + rts.apply_profile("production", adjust=True) + assert _Handler.requests[-1][1] == {"changes": {}, "profile": "production", "adjust": True} + rts.check(fzp_enable=0, adjust=True) + assert _Handler.requests[-1][0] == "/api/settings/plan" + + +def test_refused_raises(server): + url, tok, _ = server + with pytest.raises(RealtimeSystemError, match="does not fit"): + FlomniRealtimeSystem(url=url, token_file=tok).set(move_beyond_um_x=0.5) + with pytest.raises(RealtimeSystemError, match="nothing to set"): + FlomniRealtimeSystem(url=url, token_file=tok).set() + + +def test_token_errors(server): + url, _, tmp = server + with pytest.raises(RealtimeSystemError, match="No API token"): + FlomniRealtimeSystem(url=url, token_file=str(tmp / "missing")).get() + bad = tmp / "bad" + bad.write_text("wrong-token-xxxxxxxxxxxx") + with pytest.raises(RealtimeSystemError, match="401"): + FlomniRealtimeSystem(url=url, token_file=str(bad)).get() + + +def test_unreachable(): + with pytest.raises(RealtimeSystemError, match="not reachable"): + FlomniRealtimeSystem(url="http://127.0.0.1:9", token_file=__file__, timeout=1).get() -- 2.54.0 From 6dbf8784fff22a322384d669c90b74965f238088 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 12:55:32 +0000 Subject: [PATCH 03/19] docs(flomni): realtime_system in the user guide; FZP how-tos with the loop-settings profiles - user guide: detector-trigger commands under flomni.realtime_system.trigger_* (old names still work), new section "Real-time loop settings (expert)" - calibration how-to: steps 1-3 as profiles fzp_off / fzp_calibration / production (+ fast_scan); trigger values are OnTargetTrigger parameters; move-beyond sizing with the loop-settings rule (0.6 x stroke - Abbe limit) - range how-to: FZP range through the loop settings (Ub / Lb and Feedforward limits together) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019aWcwG89wi7oVyrefukXX6 --- ...ct_only-howto_calibrate_the_fzp_scanner.md | 27 ++++++++++++-- ..._only-howto_limit_the_fzp_scanner_range.md | 7 +++- docs/user/ptychography/flomni.md | 36 +++++++++++++++---- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.md b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.md index 3d29d0b5..e0214cbd 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.md +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.md @@ -102,8 +102,9 @@ correction (−4 before 2026-10-19, now 0: replaced by the feedforward). - FZP range: `PID_x_fzp` / `PID_y_fzp` `Ub` / `Lb` are ±1.5 V in operation (the cap sensors are calibrated over about ±2 V only). For the field of view above (2.8 × 1.8 µm) widen them, e.g. to ±10 V (FZP ≈ 0.13 µm/V in x, ≈ 0.05 µm/V in y). The feedforward stays off (`enable` 0,0) during this scan. -- The detector trigger (`OnTargetTrigger`) checks the relative error sample vs FZP (tolerance / hold time from the - ConstEmitter, BEC `interferometer_trigger_*`, default 10 nm / 3 ms) and waits `MinDelay_ms` (15 ms). +- The detector trigger (`OnTargetTrigger`) checks the relative error sample vs FZP (parameters + `ontarget_tolerance_um` / `ontarget_hold_ms`, BEC `flomni.realtime_system.trigger_*`, default 10 nm / 3 ms; until + 2026-10-23 ConstEmitter lines) and waits `MinDelay_ms` (15 ms). **Step 3, default operating values (replace the values in the text above):** @@ -133,6 +134,24 @@ over a wider range the FZP can take the small steps alone (flOMNI `feedback_repo Measured with 101 ms exposures: 0.2–1 µm steps trigger after 4.5–5.8 ms instead of 17–32 ms; the error during the exposure grows with the step (1 µm: ≈ 6 nm max instead of 2.5 nm), so the setting depends on the experiment. +## With the loop settings (since 2026-10-23) + +The steps above are profiles of the loop settings (web HMI tab *Loop settings*, BEC `flomni.realtime_system`; +flOMNI `FeedbackLoop/loopsettings/`). The rules keep the modules consistent, so the individual parameters above do +not have to be set by hand: + +| Step | Profile | BEC | +|---|---|---| +| 1, FZP disabled (alignment, affine matrix) | `fzp_off` | `flomni.realtime_system.apply_profile("fzp_off")` | +| 2, FZP-only calibration scan (clear `MatrixCorr` first: not part of the settings) | `fzp_calibration` | `apply_profile("fzp_calibration")` | +| 3, back to operation | `production` | `apply_profile("production")` | +| afterwards, faster scans (calibrated over ±7 V) | `fast_scan` | `apply_profile("fast_scan")` | + +Single values: `flomni.realtime_system.set(fzp_range_V_x=7, fzp_range_V_y=7)`, +`set(move_beyond_um_x=0.3)`, `set(exposure_during_motion=1)`; refused with the reason if a rule objects +(`adjust=True` changes the dependent settings too). FZP settings only between scans. Live only until +`save_as_default()`. + ## Move-beyond (`PID_x` / `PID_y` `FAST_FZP_PID_LOOP(0)` < 0), 2026-10-23 **What it does** (`PID.cpp`, unchanged since the original loop). Only in point-by-point scans (scan state 2). The @@ -153,7 +172,9 @@ target) and `AbbeCorrection` (Chan corrected for the stage tilt, see below). The target and Chan, so it supplies the +m offset of the FZP directly (static term m / K). **Sizing.** During the exposure the FZP has to hold m + Abbe correction + noise / settling inside its stroke: -|m| ≤ R − reserve, as a rule ≈ R / 2. R per axis = `Ub` × K (K ≈ 0.13 µm/V x, ≈ 0.05 µm/V y): +|m| ≤ R − reserve, as a rule ≈ R / 2. R per axis = `Ub` × K (K ≈ 0.13 µm/V x, ≈ 0.05 µm/V y). The loop settings +refuse more than 0.6 × R minus the Abbe limit (rule `move_beyond_*_fits`, constant `move_beyond_fraction`; the +tested ±7 V values are 0.56 × R): | FZP `Ub`/`Lb` | R x / y | move-beyond x / y (≈ R/2) | |---|---|---| diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_limit_the_fzp_scanner_range.md b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_limit_the_fzp_scanner_range.md index 89755f1e..488462ee 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_limit_the_fzp_scanner_range.md +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_limit_the_fzp_scanner_range.md @@ -33,9 +33,14 @@ Changing the FZP range now involves, in addition to the text above: small range. `use_final` 1 + `WaitForSlew` 0 ("exposure during the motion") only makes sense when the FZP range covers the scan step; steps larger than the range then still wait for the sample stage. - Move-beyond (0) must fit into the stroke: the FZP holds it during the exposure, together with the Abbe correction - (up to `correction_limit_um` 0.1 µm when enabled) and noise. Rule ≈ half the stroke: at ±1.5 V ≈ −0.1 µm (x), + (up to `correction_limit_um` 0.1 µm when enabled) and noise. Rule ≈ half the stroke (the loop settings refuse more than 0.6 × stroke minus the Abbe limit): at ±1.5 V ≈ −0.1 µm (x), ≈ −0.04 µm (y); at ±7 V −0.5 / −0.2 µm (tested). Production: 0. Details: calibration how-to, section "Move-beyond". - (5) compares the real FZP drive incl. the feedforward (input 8 of `PID_x` / `PID_y` = `Feedforward` output since 2026-10-23; before it was the FZP PID output without the feedforward). - After a change: web HMI tab **Loop check** level 1 (expected settings) and, with the beamline ready, the scan test (level 4: positioning time, stability during the exposure per step size). + +**With the loop settings (since 2026-10-23):** `flomni.realtime_system.set(fzp_range_V_x=..., fzp_range_V_y=...)` +(web HMI tab *Loop settings*) sets `PID_*_fzp` `Ub` / `Lb` and the `Feedforward` limits together; a move-beyond that +no longer fits is refused, or clamped with `adjust=True` (rule `move_beyond_*_fits`). `FreqGen` limits stay +manual (measurements only). diff --git a/docs/user/ptychography/flomni.md b/docs/user/ptychography/flomni.md index 8031e5d2..2a28802f 100644 --- a/docs/user/ptychography/flomni.md +++ b/docs/user/ptychography/flomni.md @@ -224,18 +224,19 @@ In step scans the detector is triggered when the sample is stable at the scan po sample vs FZP (interferometer minus FZP cap sensor) must stay within a tolerance in x and in y (each averaged over 0.5 ms) for a hold time. Both values are live and can be changed between scans: -- `flomni.interferometer_trigger_show()` — current tolerance and hold time -- `flomni.interferometer_trigger_set_tolerance_nm(10)` — tolerance per axis in nm: about 30 for a fast +- `flomni.realtime_system.trigger_show()` — current tolerance, hold time, minimum delay, trigger after a move +- `flomni.realtime_system.trigger_set_tolerance_nm(10)` — tolerance per axis in nm: about 30 for a fast low-resolution scan, 10 standard, 2 for the highest resolution (a few ms more per point); 0 selects the former criterion (on-target flags of the FZP loops) -- `flomni.interferometer_trigger_set_hold_ms(3)` — hold time in ms -- `flomni.interferometer_trigger_set_after_move(True)` — additionally one trigger after every position change +- `flomni.realtime_system.trigger_set_hold_ms(3)` — hold time in ms +- `flomni.realtime_system.trigger_set_after_move(True)` — additionally one trigger after every position change of the sample stage (e.g. `umv(dev.rtx, ...)`), as soon as the position is stable with the same tolerance and hold time; `False` (default): triggers at scan points only - `dev.rtx.controller.move_xyz(x, y, z)` — moves rtx, rty and rtz with one command (one trigger for the move) -- `flomni.interferometer_trigger_help()` — this list -After a restart of the control loop the start values (10 nm, 3 ms) apply again. Measured with 3 µm steps +The former names `flomni.interferometer_trigger_*` still work (they print the new name). The values are live; after +a restart of the control loop the values of the loop's parameter files (10 nm, 3 ms) apply again, unless they were +saved with `flomni.realtime_system.save_as_default()`. Measured with 3 µm steps (2026-10-20): the trigger comes about 36 ms (x) / 48 ms (y) after the step at 10 nm, 40 / 55 ms at 2 nm; after the trigger the position stays within about 4 nm even at a 30 nm tolerance. @@ -781,10 +782,31 @@ removed later. [See short version](flomni.md#sample-storage-and-transfer) +### Real-time loop settings (expert): `flomni.realtime_system` + +The operating settings of the real-time loop — FZP scanner on/off, FZP range, feedforward, move-beyond, exposure +during the motion, Abbe correction, detector trigger, laser tracker targets and thresholds — with the rules between +them. The rules and the mapping onto the loop parameters are kept on the loop machine (web HMI tab *Loop settings*, +`http://mpc2844:8080/?tab=settings`; definition in the flOMNI control repository, +`FeedbackLoop/loopsettings/flomni.json` and `README.md`); BEC and the web page use the same rules. + +- `flomni.realtime_system.show()` — all settings with live and saved value, violated rules, scan state + (`show(expert=True)` also the FZP calibration group) +- `flomni.realtime_system.set(name=value, ...)` — change; refused (with the reason) if a rule objects, e.g. the + feedforward must be off while the FZP loop is off; `set(..., adjust=True)` changes the dependent settings too +- `flomni.realtime_system.check(name=value)` — dry run +- `flomni.realtime_system.profiles()`, `apply_profile("production" | "fzp_off" | "fzp_calibration" | "fast_scan")` +- `flomni.realtime_system.unsaved()`, `save_as_default()` — the changes are live only until the next start of the + control loop; `save_as_default()` writes them into the loop's parameter files +- `flomni.realtime_system.help()` + +FZP settings can only be changed between scans. BEC needs the API token of the web HMI in `~/.flomni_webhmi_token` +(local contact; flOMNI `FeedbackLoop/loopsettings/README.md`). + ### Detector trigger and interferometer tuning The trigger tolerance and hold time: [Detector trigger: position stability](#detector-trigger-position-stability) -(`flomni.interferometer_trigger_*`). The control loops of the sample stage, the FZP scanner and the laser +(`flomni.realtime_system.trigger_*`). The control loops of the sample stage, the FZP scanner and the laser tracker were characterized and retuned in October 2026; results, the present parameters and how to retune them are documented in the flOMNI control repository (`FeedbackLoop/feedback_report.md` with PDF, `FeedbackLoop/tuning_guide.md`). -- 2.54.0 From 1806b721b1d10606ba45964bba86158e569c69f5 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 14:02:37 +0000 Subject: [PATCH 04/19] flomni: interferometer_trigger_* removed (use realtime_system.trigger_*); realtime_system falls back to port 80 - the former flomni.interferometer_trigger_* aliases are removed (Mirko 2026-10-23); user guide updated - flomni.realtime_system: when the default URL http://:8080 is not reachable, the request goes to port 80 of the same host (nftables redirect to the web HMI on the loop machine) and that URL is kept; an explicit URL (argument or FLOMNI_WEBHMI_URL) gets no fallback; test added Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019aWcwG89wi7oVyrefukXX6 --- .../plugins/flomni/flomni.py | 32 ++----------------- .../plugins/flomni/flomni_realtime_system.py | 31 +++++++++++++++--- docs/user/ptychography/flomni.md | 2 +- .../test_flomni_realtime_system.py | 27 ++++++++++++++++ 4 files changed, 57 insertions(+), 35 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index bf19f6af..fc2768a1 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -709,36 +709,8 @@ class FlomniSampleTransferMixin: else: print("The rt feedback is \x1b[91mNOT\x1b[0m running.") - # detector trigger: renamed to flomni.realtime_system.trigger_* (2026-10-23, Mirko: "interferometer" was - # confusing next to flomni.laser_*); the values are settings of the real-time loop (OnTargetTrigger parameters, - # until 2026-10-23 ConstEmitter lines). The old names stay as aliases. - def _renamed(self, old, new): - print(f"flomni.{old} is now flomni.realtime_system.{new}") - - def interferometer_trigger_show(self): - """Alias of flomni.realtime_system.trigger_show().""" - self._renamed("interferometer_trigger_show()", "trigger_show()") - self.realtime_system.trigger_show() - - def interferometer_trigger_set_tolerance_nm(self, tolerance_nm: float): - """Alias of flomni.realtime_system.trigger_set_tolerance_nm().""" - self._renamed("interferometer_trigger_set_tolerance_nm()", "trigger_set_tolerance_nm()") - self.realtime_system.trigger_set_tolerance_nm(tolerance_nm) - - def interferometer_trigger_set_hold_ms(self, hold_ms: float): - """Alias of flomni.realtime_system.trigger_set_hold_ms().""" - self._renamed("interferometer_trigger_set_hold_ms()", "trigger_set_hold_ms()") - self.realtime_system.trigger_set_hold_ms(hold_ms) - - def interferometer_trigger_set_after_move(self, enable: bool): - """Alias of flomni.realtime_system.trigger_set_after_move().""" - self._renamed("interferometer_trigger_set_after_move()", "trigger_set_after_move()") - self.realtime_system.trigger_set_after_move(enable) - - def interferometer_trigger_help(self): - """Alias of flomni.realtime_system.help().""" - self._renamed("interferometer_trigger_help()", "help()") - self.realtime_system.help() + # detector trigger: flomni.realtime_system.trigger_* (the former flomni.interferometer_trigger_* removed + # 2026-10-23, Mirko: "interferometer" was confusing next to flomni.laser_*) def lights_off(self): self.device_manager.devices.fsamx.controller.lights_off() diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py index 79eaaec0..d023a0e9 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py @@ -14,7 +14,8 @@ web page (tab "Loop settings") and BEC always behave the same way. Changes are live only: the next start of the control loop uses the .xmp files again (save_as_default keeps them). -Connection: the web HMI URL (default http://:8080, env FLOMNI_WEBHMI_URL) and an API token in a file +Connection: the web HMI URL (default http://:8080, and http:// (port 80, nftables redirect to +8080 on the loop machine) when 8080 cannot be reached; env FLOMNI_WEBHMI_URL) and an API token in a file (default ~/.flomni_webhmi_token, env FLOMNI_WEBHMI_TOKEN_FILE; the same token as in the web HMI's api_token_file on the loop machine, see flOMNI FeedbackLoop/loopsettings/README.md). """ @@ -43,6 +44,7 @@ def _fmt(v): class FlomniRealtimeSystem: def __init__(self, url=None, token_file=None, device_manager=None, timeout=5.0): self._url = url + self._fallback = None # port-80 URL that answered when 8080 did not (kept for the session) self._token_file = token_file self._device_manager = device_manager self.timeout = timeout @@ -51,6 +53,8 @@ class FlomniRealtimeSystem: @property def url(self): + if self._fallback: + return self._fallback if self._url: return self._url.rstrip("/") env = os.environ.get("FLOMNI_WEBHMI_URL") @@ -63,6 +67,9 @@ class FlomniRealtimeSystem: pass return f"http://{host}:{DEFAULT_PORT}" + def _explicit_url(self): + return bool(self._url or os.environ.get("FLOMNI_WEBHMI_URL")) + def _token(self): path = os.path.expanduser(self._token_file or os.environ.get("FLOMNI_WEBHMI_TOKEN_FILE") or DEFAULT_TOKEN_FILE) try: @@ -78,8 +85,21 @@ class FlomniRealtimeSystem: return tok def _request(self, path, body=None): + try: + return self._request_once(self.url, path, body) + except RealtimeSystemError as exc: + # default URL on port 8080 not reachable: the loop machine also serves the web HMI on port 80 + # (nftables redirect 80 -> 8080, the port the PSI gateway passes); not for an explicit URL + if getattr(exc, "unreachable", False) and not self._explicit_url() and not self._fallback: + alt = self.url.replace(f":{DEFAULT_PORT}", "") + r = self._request_once(alt, path, body) + self._fallback = alt + return r + raise + + def _request_once(self, url, path, body=None): data = None if body is None else json.dumps(body).encode() - req = urllib.request.Request(self.url + path, data=data, method="GET" if body is None else "POST", + req = urllib.request.Request(url + path, data=data, method="GET" if body is None else "POST", headers={"Authorization": "Bearer " + self._token(), "X-Client": "BEC", "Content-Type": "application/json"}) try: @@ -92,9 +112,11 @@ class FlomniRealtimeSystem: detail = exc.reason if exc.code == 401: detail = f"{detail} (API token not accepted by the web HMI: compare with its api_token_file)" - raise RealtimeSystemError(f"web HMI {self.url}{path}: {exc.code} {detail}") from None + raise RealtimeSystemError(f"web HMI {url}{path}: {exc.code} {detail}") from None except (urllib.error.URLError, OSError) as exc: - raise RealtimeSystemError(f"web HMI {self.url} not reachable: {exc}") from None + err = RealtimeSystemError(f"web HMI {url} not reachable: {exc}") + err.unreachable = True + raise err from None # ── reading ─────────────────────────────────────────────────────────────── @@ -226,6 +248,7 @@ class FlomniRealtimeSystem: # ── shortcuts (detector trigger, laser tracker) ──────────────────────────── + # the former flomni.interferometer_trigger_* (removed 2026-10-23) def trigger_show(self): """Detector trigger (step scans): tolerance, hold time, minimum delay, trigger after every move.""" self.show(group="trigger") diff --git a/docs/user/ptychography/flomni.md b/docs/user/ptychography/flomni.md index 2a28802f..0ef07084 100644 --- a/docs/user/ptychography/flomni.md +++ b/docs/user/ptychography/flomni.md @@ -234,7 +234,7 @@ over 0.5 ms) for a hold time. Both values are live and can be changed between sc hold time; `False` (default): triggers at scan points only - `dev.rtx.controller.move_xyz(x, y, z)` — moves rtx, rty and rtz with one command (one trigger for the move) -The former names `flomni.interferometer_trigger_*` still work (they print the new name). The values are live; after +The former names `flomni.interferometer_trigger_*` were removed (2026-10-23). The values are live; after a restart of the control loop the values of the loop's parameter files (10 nm, 3 ms) apply again, unless they were saved with `flomni.realtime_system.save_as_default()`. Measured with 3 µm steps (2026-10-20): the trigger comes about 36 ms (x) / 48 ms (y) after the step at 10 nm, 40 / 55 ms at 2 nm; diff --git a/tests/tests_bec_ipython_client/test_flomni_realtime_system.py b/tests/tests_bec_ipython_client/test_flomni_realtime_system.py index 16265a7a..728f5527 100644 --- a/tests/tests_bec_ipython_client/test_flomni_realtime_system.py +++ b/tests/tests_bec_ipython_client/test_flomni_realtime_system.py @@ -138,3 +138,30 @@ def test_token_errors(server): def test_unreachable(): with pytest.raises(RealtimeSystemError, match="not reachable"): FlomniRealtimeSystem(url="http://127.0.0.1:9", token_file=__file__, timeout=1).get() + + +def test_port_80_fallback(monkeypatch): + """Default URL (rtx host, port 8080) not reachable: the same request goes to port 80 (nftables redirect on the + loop machine) and that URL is kept; an explicit URL gets no fallback.""" + monkeypatch.delenv("FLOMNI_WEBHMI_URL", raising=False) + dm = type("DM", (), {})() + dm.devices = type("D", (), {})() + dm.devices.rtx = type("R", (), {"_config": {"deviceConfig": {"host": "loophost"}}})() + rts = FlomniRealtimeSystem(device_manager=dm, token_file=__file__) + calls = [] + + def once(url, path, body=None): + calls.append(url) + if url.endswith(":8080"): + err = RealtimeSystemError("not reachable") + err.unreachable = True + raise err + return {"ok": True} + + monkeypatch.setattr(rts, "_request_once", once) + assert rts._request("/api/settings") == {"ok": True} + assert calls == ["http://loophost:8080", "http://loophost"] and rts.url == "http://loophost" + explicit = FlomniRealtimeSystem(url="http://other:8080", token_file=__file__) + monkeypatch.setattr(explicit, "_request_once", once) + with pytest.raises(RealtimeSystemError): + explicit._request("/api/settings") -- 2.54.0 From e418f96a1e5dc5e0899e30a0086c1e151679b3d2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 16:36:46 +0000 Subject: [PATCH 05/19] feat(flomni): baseline device flomni_loop_settings - the loop settings stored with every scan Decisions Mirko 2026-10-23: settings only (not all loop parameters), snapshot at the scan start, web HMI down -> marked unavailable and the scan continues, flOMNI only for now, token part of the device configuration. - devices/omny/flomni_loop_settings.py (new): FlomniLoopSettings, readoutPriority baseline (the Fermat scan reads the baseline devices in prepare_scan): one signal per setting (21, NaN when not available), available, message, unsaved / inconsistent counts, violated error rules, definition path + sha1, compact snapshot_json; never raises; after a failure no new attempt for 60 s (a tomogram does not wait for the timeout at every projection); show() / snapshot() for the user - devices/omny/webhmi_client.py (new): the HTTP client (URL with port-80 fallback, token value or file, errors) shared by the device and flomni.realtime_system (which now uses it; behaviour unchanged) - ptycho_flomni.yaml: flomni_loop_settings with token_file (the token value only in a config that is not committed), host mpc2844.psi.ch, timeout 2 s - tests: tests_devices/test_flomni_loop_settings.py (stub web HMI: values, counts, unavailable / back-off, unreachable, token file); checked end-to-end against the web HMI with the flOMNI definition and fakecore (all 21 settings filled) - user guide: the settings are stored with every scan Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019aWcwG89wi7oVyrefukXX6 --- .../plugins/flomni/flomni_realtime_system.py | 93 +----------- csaxs_bec/device_configs/ptycho_flomni.yaml | 20 +++ .../devices/omny/flomni_loop_settings.py | 142 ++++++++++++++++++ csaxs_bec/devices/omny/webhmi_client.py | 116 ++++++++++++++ docs/user/ptychography/flomni.md | 4 + .../test_flomni_loop_settings.py | 97 ++++++++++++ 6 files changed, 386 insertions(+), 86 deletions(-) create mode 100644 csaxs_bec/devices/omny/flomni_loop_settings.py create mode 100644 csaxs_bec/devices/omny/webhmi_client.py create mode 100644 tests/tests_devices/test_flomni_loop_settings.py diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py index d023a0e9..19dd75e0 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py @@ -20,17 +20,8 @@ Connection: the web HMI URL (default http://:8080, and http:// 8080, the port the PSI gateway passes); not for an explicit URL - if getattr(exc, "unreachable", False) and not self._explicit_url() and not self._fallback: - alt = self.url.replace(f":{DEFAULT_PORT}", "") - r = self._request_once(alt, path, body) - self._fallback = alt - return r - raise - - def _request_once(self, url, path, body=None): - data = None if body is None else json.dumps(body).encode() - req = urllib.request.Request(url + path, data=data, method="GET" if body is None else "POST", - headers={"Authorization": "Bearer " + self._token(), "X-Client": "BEC", - "Content-Type": "application/json"}) - try: - with urllib.request.urlopen(req, timeout=self.timeout) as r: - return json.load(r) - except urllib.error.HTTPError as exc: - try: - detail = json.load(exc).get("detail", exc.reason) - except Exception: # noqa: BLE001 - detail = exc.reason - if exc.code == 401: - detail = f"{detail} (API token not accepted by the web HMI: compare with its api_token_file)" - raise RealtimeSystemError(f"web HMI {url}{path}: {exc.code} {detail}") from None - except (urllib.error.URLError, OSError) as exc: - err = RealtimeSystemError(f"web HMI {url} not reachable: {exc}") - err.unreachable = True - raise err from None + def __init__(self, url=None, token_file=None, device_manager=None, timeout=5.0, token=None): + super().__init__(url=url, token=token, token_file=token_file, device_manager=device_manager, timeout=timeout) # ── reading ─────────────────────────────────────────────────────────────── diff --git a/csaxs_bec/device_configs/ptycho_flomni.yaml b/csaxs_bec/device_configs/ptycho_flomni.yaml index 9b3c278d..3aacbcaa 100644 --- a/csaxs_bec/device_configs/ptycho_flomni.yaml +++ b/csaxs_bec/device_configs/ptycho_flomni.yaml @@ -616,6 +616,26 @@ flomni_temphum: deviceTags: - ptycho_flomni # ############################################################ +# ############ flOMNI real-time loop settings ################# +# ############################################################ +# The loop settings (FZP, trigger, tracker; flOMNI FeedbackLoop/loopsettings) stored with every scan: read from the +# web HMI on the loop machine at the start of each scan (baseline). Web HMI down: available = 0, the scan continues. +# token_file: a file with the web HMI API token on the machine of the device server (mode 600). The token itself can +# be given as "token: " instead, but only in a config that is not committed - never put it in this file. +flomni_loop_settings: + description: flOMNI real-time loop settings (web HMI loop settings, snapshot at scan start) + deviceClass: csaxs_bec.devices.omny.flomni_loop_settings.FlomniLoopSettings + deviceConfig: + host: mpc2844.psi.ch + token_file: ~/.flomni_webhmi_token + timeout: 2.0 + enabled: true + onFailure: buffer + readOnly: true + readoutPriority: baseline + deviceTags: + - ptycho_flomni +# ############################################################ # ########## OMNY / flOMNI / LamNI fast shutter ############## # REPLACED BY SIMULATED BEAMLINE DEVICE !!!!!!!!!!!!!!!! # ############################################################ diff --git a/csaxs_bec/devices/omny/flomni_loop_settings.py b/csaxs_bec/devices/omny/flomni_loop_settings.py new file mode 100644 index 00000000..16feee75 --- /dev/null +++ b/csaxs_bec/devices/omny/flomni_loop_settings.py @@ -0,0 +1,142 @@ +"""flomni_loop_settings: the operating settings of the flOMNI real-time loop as a baseline device. + +Read at the start of every scan (readoutPriority: baseline; the flOMNI Fermat scan calls read_baseline_devices in +prepare_scan), so each scan file holds the loop settings it ran with: FZP on/off, FZP range, feedforward, +move-beyond, exposure during the motion, Abbe correction, detector trigger, tracker targets and thresholds +(definition: flOMNI FeedbackLoop/loopsettings/flomni.json; served by the web HMI on the loop machine, GET +/api/settings). Settings only, not all loop parameters (decision Mirko 2026-10-23). + +Signals: one per setting (float, NaN when not available; names = the setting names), and + available 1 = read from the web HMI, 0 = not available (the scan continues; message says why) + message "" or the reason it is not available + unsaved number of settings whose live value differs from the .xmp (not saved as default) + inconsistent number of settings whose parameters disagree (e.g. FZP x on, y off) + rules_violated number of violated error rules + definition path and sha1 of the definition file on the loop machine + snapshot_json JSON: every setting (value, saved, unsaved, consistent, unit), the violated rules, the + scan state, the definition path and sha1 + +deviceConfig: token (the API token itself; only in a config that is not committed) or token_file (path to a file +with it, default ~/.flomni_webhmi_token), optional url / host / timeout (default 2 s). Connection details: +devices/omny/webhmi_client.py. +""" + +import json +import math +import time + +from ophyd import Component as Cpt +from ophyd import Device, Kind, Signal + +from csaxs_bec.devices.omny.webhmi_client import WebHmiClient, WebHmiError + +# the settings of flomni.json (2026-10-23); a setting added to the definition later is in snapshot_json only until +# it is added here +SETTINGS = ( + "fzp_enable", "fzp_range_V_x", "fzp_range_V_y", "feedforward_enable", "move_beyond_um_x", "move_beyond_um_y", + "exposure_during_motion", "abbe_enable", "abbe_limit_um", + "trigger_tolerance_nm", "trigger_hold_ms", "trigger_min_delay_ms", "trigger_on_move", + "tracking_target_y", "tracking_target_z", "psd_threshold_low", "interferometer_threshold", + "interferometer_threshold_rot", + "sample_hold_fzp_V", "sample_hold_err_um", "sample_hold_zero", +) + +RETRY_AFTER_FAILURE_S = 60.0 # web HMI not reachable: no new attempt for this long (a tomogram does not wait each time) + + +class FlomniLoopSettings(Device): + USER_ACCESS = ["show", "snapshot"] + + available = Cpt(Signal, value=0, kind=Kind.normal) + message = Cpt(Signal, value="", kind=Kind.normal) + unsaved = Cpt(Signal, value=-1, kind=Kind.normal) + inconsistent = Cpt(Signal, value=-1, kind=Kind.normal) + rules_violated = Cpt(Signal, value=-1, kind=Kind.normal) + definition = Cpt(Signal, value="", kind=Kind.normal) + snapshot_json = Cpt(Signal, value="", kind=Kind.normal) + + # one signal per setting + locals().update({_n: Cpt(Signal, value=math.nan, kind=Kind.normal) for _n in SETTINGS}) + + def __init__(self, prefix="", *, name, token=None, token_file=None, url=None, host=None, timeout=2.0, + device_manager=None, **kwargs): + super().__init__(prefix, name=name, **kwargs) + self._client = WebHmiClient(url=url, token=token, token_file=token_file, host=host, + device_manager=device_manager, timeout=timeout, client_name="BEC baseline") + self._failed_at = None + self._last = None + + def _unavailable(self, reason): + self.available.put(0) + self.message.put(str(reason)) + for n in ("unsaved", "inconsistent", "rules_violated"): + getattr(self, n).put(-1) + self.definition.put("") + self.snapshot_json.put("") + for n in SETTINGS: + getattr(self, n).put(math.nan) + + def update(self): + """Read the settings from the web HMI into the signals. Never raises: not available -> available 0.""" + now = time.time() + if self._failed_at is not None and now - self._failed_at < RETRY_AFTER_FAILURE_S: + return # keep the 'not available' values of the last failure + try: + snap = self._client.settings() + except WebHmiError as exc: + self._failed_at = now + self._unavailable(f"{exc} (next attempt after {RETRY_AFTER_FAILURE_S:.0f} s)") + return + except Exception as exc: # noqa: BLE001 (a broken answer must not stop a scan) + self._failed_at = now + self._unavailable(f"unexpected answer from the web HMI: {exc}") + return + self._failed_at = None + self._last = snap + values = {s["name"]: s for s in snap.get("settings", [])} + for n in SETTINGS: + v = values.get(n, {}).get("value") + getattr(self, n).put(float(v) if isinstance(v, (int, float)) else math.nan) # bool is an int too + self.unsaved.put(sum(1 for s in values.values() if s.get("unsaved"))) + self.inconsistent.put(sum(1 for s in values.values() if not s.get("consistent", True))) + self.rules_violated.put(sum(1 for r in snap.get("rules", []) if not r.get("ok") and r.get("severity") == "error")) + self.definition.put(f"{snap.get('definition', '')} sha1 {snap.get('definition_sha1', '?')}") + missing = [n for n in values if n not in SETTINGS] + self.message.put(f"settings not stored as own signals (in snapshot_json): {', '.join(missing)}" if missing else "") + # compact: values and flags only (help texts and parameter mappings are in the definition, see its sha1) + compact = { + "instrument": snap.get("instrument"), "definition": snap.get("definition"), + "definition_sha1": snap.get("definition_sha1"), "scan": snap.get("scan"), + "settings": {n: {"value": s.get("value"), "saved": s.get("saved"), "unsaved": s.get("unsaved"), + "consistent": s.get("consistent"), "unit": s.get("unit", "")} + for n, s in values.items()}, + "rules_violated": [{"name": r.get("name"), "severity": r.get("severity"), "message": r.get("message")} + for r in snap.get("rules", []) if not r.get("ok")], + } + self.snapshot_json.put(json.dumps(compact, separators=(",", ":"))) + self.available.put(1) + + def read(self): + self.update() + return super().read() + + def snapshot(self) -> dict: + """Read now and return {setting: value} (empty when not available).""" + self._failed_at = None # an explicit request always tries + self.update() + if not self.available.get(): + return {} + return {n: getattr(self, n).get() for n in SETTINGS} + + def show(self): + """Print the settings as they would be stored with a scan now.""" + snap = self.snapshot() + if not snap: + print(f"loop settings not available: {self.message.get()}") + return + for n, v in snap.items(): + print(f" {n:30s} {v:g}") + print(f"unsaved {self.unsaved.get()}, inconsistent {self.inconsistent.get()}, " + f"rules violated {self.rules_violated.get()}; {self.definition.get()}") + if self.message.get(): + print(self.message.get()) diff --git a/csaxs_bec/devices/omny/webhmi_client.py b/csaxs_bec/devices/omny/webhmi_client.py new file mode 100644 index 00000000..92554527 --- /dev/null +++ b/csaxs_bec/devices/omny/webhmi_client.py @@ -0,0 +1,116 @@ +"""Client of the web HMI on an OMNY-family loop machine (RT-system webhmi): REST requests with the API token. + +Shared by the BEC client plugin (flomni.realtime_system, bec_ipython_client/plugins/flomni/flomni_realtime_system.py) +and the baseline device FlomniLoopSettings (devices/omny/flomni_loop_settings.py), so both talk to the web HMI the +same way. Standard library only (runs in the IPython client and in the device server). + +URL: explicit `url`, else env FLOMNI_WEBHMI_URL, else http://:8080 with = `host`, else the host of the +rtx device config, else mpc2844.psi.ch. With the default URL a request that cannot reach port 8080 is repeated on +port 80 of the same host (nftables redirect 80 -> 8080 on the loop machine, the port the PSI gateway passes); that URL +is then kept. An explicit URL gets no fallback. + +Token: `token` (the value, e.g. from a device config that is not committed), else `token_file`, else env +FLOMNI_WEBHMI_TOKEN_FILE, else ~/.flomni_webhmi_token. The same line as the web HMI's api_token_file on the loop +machine (flOMNI FeedbackLoop/loopsettings/README.md). Never commit a token. +""" + +import json +import os +import urllib.error +import urllib.request + +DEFAULT_TOKEN_FILE = "~/.flomni_webhmi_token" +DEFAULT_PORT = 8080 +DEFAULT_HOST = "mpc2844.psi.ch" + + +class WebHmiError(Exception): + """A request was refused (rules, scan running) or the web HMI could not be reached (.unreachable = True).""" + + unreachable = False + + +class WebHmiClient: + def __init__(self, url=None, token=None, token_file=None, host=None, device_manager=None, timeout=5.0, + client_name="BEC"): + self._url = url + self._fallback = None # port-80 URL that answered when 8080 did not (kept for the session) + self._token_value = token + self._token_file = token_file + self._host = host + self._device_manager = device_manager + self.timeout = timeout + self.client_name = client_name + + @property + def url(self): + if self._fallback: + return self._fallback + if self._url: + return self._url.rstrip("/") + env = os.environ.get("FLOMNI_WEBHMI_URL") + if env: + return env.rstrip("/") + host = self._host or DEFAULT_HOST + if not self._host: + try: # the host of the rt controller (device config of rtx) + host = self._device_manager.devices.rtx._config["deviceConfig"]["host"] or host + except Exception: # noqa: BLE001 + pass + return f"http://{host}:{DEFAULT_PORT}" + + def _explicit_url(self): + return bool(self._url or os.environ.get("FLOMNI_WEBHMI_URL")) + + def _token(self): + if self._token_value: + return str(self._token_value).strip() + path = os.path.expanduser(self._token_file or os.environ.get("FLOMNI_WEBHMI_TOKEN_FILE") or DEFAULT_TOKEN_FILE) + try: + with open(path) as f: + tok = f.readline().strip() + except OSError: + raise WebHmiError( + f"No API token for the web HMI: {path} not readable. Copy the token from the loop machine " + "(~/loop-data/webhmi/api_token of the user control) into this file (mode 600), or set " + "FLOMNI_WEBHMI_TOKEN_FILE.") from None + if not tok: + raise WebHmiError(f"API token file {path} is empty") + return tok + + def _request(self, path, body=None): + try: + return self._request_once(self.url, path, body) + except WebHmiError as exc: + # default URL on port 8080 not reachable: the loop machine also serves the web HMI on port 80 + if exc.unreachable and not self._explicit_url() and not self._fallback: + alt = self.url.replace(f":{DEFAULT_PORT}", "") + r = self._request_once(alt, path, body) + self._fallback = alt + return r + raise + + def _request_once(self, url, path, body=None): + data = None if body is None else json.dumps(body).encode() + req = urllib.request.Request(url + path, data=data, method="GET" if body is None else "POST", + headers={"Authorization": "Bearer " + self._token(), + "X-Client": self.client_name, "Content-Type": "application/json"}) + try: + with urllib.request.urlopen(req, timeout=self.timeout) as r: + return json.load(r) + except urllib.error.HTTPError as exc: + try: + detail = json.load(exc).get("detail", exc.reason) + except Exception: # noqa: BLE001 + detail = exc.reason + if exc.code == 401: + detail = f"{detail} (API token not accepted by the web HMI: compare with its api_token_file)" + raise WebHmiError(f"web HMI {url}{path}: {exc.code} {detail}") from None + except (urllib.error.URLError, OSError) as exc: + err = WebHmiError(f"web HMI {url} not reachable: {exc}") + err.unreachable = True + raise err from None + + def settings(self) -> dict: + """GET /api/settings: settings, rules, scan state, profiles.""" + return self._request("/api/settings") diff --git a/docs/user/ptychography/flomni.md b/docs/user/ptychography/flomni.md index 0ef07084..4b7e6cbf 100644 --- a/docs/user/ptychography/flomni.md +++ b/docs/user/ptychography/flomni.md @@ -800,6 +800,10 @@ them. The rules and the mapping onto the loop parameters are kept on the loop ma control loop; `save_as_default()` writes them into the loop's parameter files - `flomni.realtime_system.help()` +Every scan stores the loop settings it ran with: the baseline device `flomni_loop_settings` (one value per +setting, `available`, unsaved / inconsistent counts, the definition's sha1, a JSON of all settings); +`dev.flomni_loop_settings.show()` prints what a scan would store now. + FZP settings can only be changed between scans. BEC needs the API token of the web HMI in `~/.flomni_webhmi_token` (local contact; flOMNI `FeedbackLoop/loopsettings/README.md`). diff --git a/tests/tests_devices/test_flomni_loop_settings.py b/tests/tests_devices/test_flomni_loop_settings.py new file mode 100644 index 00000000..8c594316 --- /dev/null +++ b/tests/tests_devices/test_flomni_loop_settings.py @@ -0,0 +1,97 @@ +"""Tests for the baseline device flomni_loop_settings (devices/omny/flomni_loop_settings.py): the loop settings of +the flOMNI web HMI stored with every scan. A small local HTTP server stands in for the web HMI (/api/settings).""" + +import json +import math +import threading +from http.server import BaseHTTPRequestHandler, HTTPServer + +import pytest + +from csaxs_bec.devices.omny import flomni_loop_settings as fls +from csaxs_bec.devices.omny.flomni_loop_settings import SETTINGS, FlomniLoopSettings + +TOKEN = "device-token-0123456789abcdef" + +SNAP = { + "instrument": "flOMNI", "definition": "/home/control/OMNY/flOMNI/FeedbackLoop/loopsettings/flomni.json", + "definition_sha1": "a" * 40, "scan": {"mode": 0, "active": False, "text": ""}, + "settings": [ + {"name": "fzp_enable", "value": 1, "unsaved": False, "consistent": True}, + {"name": "fzp_range_V_x", "value": 1.5, "unsaved": True, "consistent": True}, + {"name": "trigger_tolerance_nm", "value": 10.0, "unsaved": False, "consistent": False}, + {"name": "new_setting_later", "value": 3.0, "unsaved": False, "consistent": True}, + ], + "rules": [{"name": "r1", "severity": "error", "ok": False}, {"name": "r2", "severity": "warning", "ok": False}], +} + + +class _Handler(BaseHTTPRequestHandler): + hits = 0 + + def log_message(self, *args): + pass + + def do_GET(self): + _Handler.hits += 1 + code, body = (200, SNAP) if self.headers.get("Authorization") == "Bearer " + TOKEN else (401, {"detail": "no"}) + data = json.dumps(body).encode() + self.send_response(code) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + +@pytest.fixture +def url(): + _Handler.hits = 0 + srv = HTTPServer(("127.0.0.1", 0), _Handler) + threading.Thread(target=srv.serve_forever, daemon=True).start() + yield f"http://127.0.0.1:{srv.server_address[1]}" + srv.shutdown() + + +def test_read_stores_settings(url): + dev = FlomniLoopSettings(name="flomni_loop_settings", url=url, token=TOKEN) + r = dev.read() + assert r["flomni_loop_settings_available"]["value"] == 1 + assert r["flomni_loop_settings_fzp_enable"]["value"] == 1.0 + assert r["flomni_loop_settings_fzp_range_V_x"]["value"] == 1.5 + assert math.isnan(r["flomni_loop_settings_move_beyond_um_x"]["value"]) # not in the answer + assert r["flomni_loop_settings_unsaved"]["value"] == 1 + assert r["flomni_loop_settings_inconsistent"]["value"] == 1 + assert r["flomni_loop_settings_rules_violated"]["value"] == 1 # error rules only + assert r["flomni_loop_settings_definition"]["value"].endswith("sha1 " + "a" * 40) + assert "new_setting_later" in r["flomni_loop_settings_message"]["value"] + snap = json.loads(r["flomni_loop_settings_snapshot_json"]["value"]) + assert snap["settings"]["fzp_enable"]["value"] == 1 and "profiles" not in snap + assert snap["settings"]["new_setting_later"]["value"] == 3.0 # later settings: in the JSON + assert [r["name"] for r in snap["rules_violated"]] == ["r1", "r2"] + assert set(SETTINGS) <= {k.replace("flomni_loop_settings_", "") for k in r} + + +def test_unavailable_does_not_raise_and_backs_off(url, monkeypatch): + dev = FlomniLoopSettings(name="ls", url=url, token="wrong-token-xxxxxxxxxxxx") + r = dev.read() + assert r["ls_available"]["value"] == 0 and "401" in r["ls_message"]["value"] + assert math.isnan(r["ls_fzp_enable"]["value"]) and r["ls_unsaved"]["value"] == -1 + hits = _Handler.hits + dev.read() # within the back-off: no new request + assert _Handler.hits == hits + monkeypatch.setattr(fls, "RETRY_AFTER_FAILURE_S", 0.0) + dev.read() + assert _Handler.hits == hits + 1 + + +def test_unreachable(): + dev = FlomniLoopSettings(name="ls", url="http://127.0.0.1:9", token=TOKEN, timeout=0.5) + r = dev.read() + assert r["ls_available"]["value"] == 0 and "not reachable" in r["ls_message"]["value"] + + +def test_token_file_and_snapshot(url, tmp_path): + tok = tmp_path / "token" + tok.write_text(TOKEN + "\n") + dev = FlomniLoopSettings(name="ls", url=url, token_file=str(tok)) + assert dev.snapshot()["fzp_range_V_x"] == 1.5 -- 2.54.0 From 533fca8c2437159ea192188931d523ef7221e8af Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 16:39:40 +0000 Subject: [PATCH 06/19] feat(flomni): realtime_system goes through the device flomni_loop_settings (RPC); token in its device config Mirko 2026-10-23: the token goes into the device config (behind the beamline firewall like the other devices); the user commands call the BEC device in the background. - FlomniLoopSettings: api_get / api_post (RPC for flomni.realtime_system; only /api/settings* and /api/status; 10 s timeout, no back-off). Not named set / put: the BEC device server treats those as ophyd writes (refused on a readOnly device; checked in bec_server rpc_handler.py) - FlomniRealtimeSystem: requests through dev.flomni_loop_settings when it exists and is enabled (errors from the device -> RealtimeSystemError), else direct HTTP as before; show() names the route - ptycho_flomni.yaml: token field (empty: token_file is used) - tests: RPC methods and path check; routing through a device and back to direct HTTP when it is disabled (checked against bec_server: deviceConfig keys are matched to the constructor parameters, device_manager is passed, the constructor does no network access) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019aWcwG89wi7oVyrefukXX6 --- .../plugins/flomni/flomni_realtime_system.py | 37 +++++++++++++++---- csaxs_bec/device_configs/ptycho_flomni.yaml | 6 ++- .../devices/omny/flomni_loop_settings.py | 32 ++++++++++++++-- docs/user/ptychography/flomni.md | 4 +- .../test_flomni_realtime_system.py | 32 ++++++++++++++++ .../test_flomni_loop_settings.py | 11 ++++++ 6 files changed, 108 insertions(+), 14 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py index 19dd75e0..374c5366 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py @@ -14,10 +14,9 @@ web page (tab "Loop settings") and BEC always behave the same way. Changes are live only: the next start of the control loop uses the .xmp files again (save_as_default keeps them). -Connection: the web HMI URL (default http://:8080, and http:// (port 80, nftables redirect to -8080 on the loop machine) when 8080 cannot be reached; env FLOMNI_WEBHMI_URL) and an API token in a file -(default ~/.flomni_webhmi_token, env FLOMNI_WEBHMI_TOKEN_FILE; the same token as in the web HMI's api_token_file on -the loop machine, see flOMNI FeedbackLoop/loopsettings/README.md). +Connection: through the BEC device flomni_loop_settings (RPC; the API token is in its device config, +ptycho_flomni.yaml) when it exists and is enabled, else directly to the web HMI (devices/omny/webhmi_client.py: URL +http://:8080 with port-80 fallback, token from ~/.flomni_webhmi_token or FLOMNI_WEBHMI_TOKEN_FILE). """ from csaxs_bec.devices.omny.webhmi_client import WebHmiClient @@ -33,12 +32,36 @@ def _fmt(v): class FlomniRealtimeSystem(WebHmiClient): - """Connection (URL with port-80 fallback, token, errors): WebHmiClient (devices/omny/webhmi_client.py), shared - with the baseline device flomni_loop_settings.""" + """Requests go through the BEC device flomni_loop_settings (RPC api_get / api_post; the token is in its device + config) when it exists and is enabled; otherwise directly to the web HMI (WebHmiClient, + devices/omny/webhmi_client.py; token from token_file / ~/.flomni_webhmi_token).""" + + DEVICE = "flomni_loop_settings" def __init__(self, url=None, token_file=None, device_manager=None, timeout=5.0, token=None): super().__init__(url=url, token=token, token_file=token_file, device_manager=device_manager, timeout=timeout) + def _device(self): + try: + dev = getattr(self._device_manager.devices, self.DEVICE) + except Exception: # noqa: BLE001 (no device manager / no such device) + return None + return dev if getattr(dev, "enabled", True) else None + + def _request(self, path, body=None): + dev = self._device() + if dev is None: + return super()._request(path, body) # direct HTTP + try: + return dev.api_get(path) if body is None else dev.api_post(path, body) + except Exception as exc: # noqa: BLE001 (RPC error with the device-side message) + raise RealtimeSystemError(f"via device {self.DEVICE}: {exc}") from None + + @property + def route(self): + """'device flomni_loop_settings' or the direct URL.""" + return f"device {self.DEVICE}" if self._device() is not None else self.url + # ── reading ─────────────────────────────────────────────────────────────── def read(self) -> dict: @@ -59,7 +82,7 @@ class FlomniRealtimeSystem(WebHmiClient): snap = self.read() groups = {g["name"]: g for g in snap["groups"]} sc = snap.get("scan") or {} - print(f"Loop settings {snap.get('instrument', '')} ({self.url}) - " + print(f"Loop settings {snap.get('instrument', '')} ({self.route}) - " + (f"scan running (mode {_fmt(sc.get('mode'))})" if sc.get("active") else "no scan")) for gname, g in groups.items(): if group and gname != group: diff --git a/csaxs_bec/device_configs/ptycho_flomni.yaml b/csaxs_bec/device_configs/ptycho_flomni.yaml index 3aacbcaa..677355a1 100644 --- a/csaxs_bec/device_configs/ptycho_flomni.yaml +++ b/csaxs_bec/device_configs/ptycho_flomni.yaml @@ -620,13 +620,15 @@ flomni_temphum: # ############################################################ # The loop settings (FZP, trigger, tracker; flOMNI FeedbackLoop/loopsettings) stored with every scan: read from the # web HMI on the loop machine at the start of each scan (baseline). Web HMI down: available = 0, the scan continues. -# token_file: a file with the web HMI API token on the machine of the device server (mode 600). The token itself can -# be given as "token: " instead, but only in a config that is not committed - never put it in this file. +# The user commands flomni.realtime_system go through this device (RPC), so the token is needed only here. +# token: the web HMI API token (~/loop-data/webhmi/api_token on mpc2844; behind the beamline firewall, Mirko +# 2026-10-23). When empty, token_file (on the machine of the device server) is used. flomni_loop_settings: description: flOMNI real-time loop settings (web HMI loop settings, snapshot at scan start) deviceClass: csaxs_bec.devices.omny.flomni_loop_settings.FlomniLoopSettings deviceConfig: host: mpc2844.psi.ch + token: "" token_file: ~/.flomni_webhmi_token timeout: 2.0 enabled: true diff --git a/csaxs_bec/devices/omny/flomni_loop_settings.py b/csaxs_bec/devices/omny/flomni_loop_settings.py index 16feee75..0a59324c 100644 --- a/csaxs_bec/devices/omny/flomni_loop_settings.py +++ b/csaxs_bec/devices/omny/flomni_loop_settings.py @@ -16,9 +16,12 @@ Signals: one per setting (float, NaN when not available; names = the setting nam snapshot_json JSON: every setting (value, saved, unsaved, consistent, unit), the violated rules, the scan state, the definition path and sha1 -deviceConfig: token (the API token itself; only in a config that is not committed) or token_file (path to a file -with it, default ~/.flomni_webhmi_token), optional url / host / timeout (default 2 s). Connection details: +deviceConfig: token (the API token itself), else token_file (path to a file with it, default +~/.flomni_webhmi_token), optional url / host / timeout (default 2 s for the scan snapshot). Connection details: devices/omny/webhmi_client.py. + +flomni.realtime_system (the user commands) calls api_get / api_post of this device by RPC, so the token is needed +only here. """ import json @@ -45,7 +48,7 @@ RETRY_AFTER_FAILURE_S = 60.0 # web HMI not reachable: no new attempt for this class FlomniLoopSettings(Device): - USER_ACCESS = ["show", "snapshot"] + USER_ACCESS = ["show", "snapshot", "api_get", "api_post"] available = Cpt(Signal, value=0, kind=Kind.normal) message = Cpt(Signal, value="", kind=Kind.normal) @@ -63,6 +66,9 @@ class FlomniLoopSettings(Device): super().__init__(prefix, name=name, **kwargs) self._client = WebHmiClient(url=url, token=token, token_file=token_file, host=host, device_manager=device_manager, timeout=timeout, client_name="BEC baseline") + # requests of flomni.realtime_system (RPC from the client, below): longer timeout, no back-off + self._api = WebHmiClient(url=url, token=token, token_file=token_file, host=host, + device_manager=device_manager, timeout=max(timeout, 10.0), client_name="BEC") self._failed_at = None self._last = None @@ -120,6 +126,26 @@ class FlomniLoopSettings(Device): self.update() return super().read() + # ── RPC for flomni.realtime_system: the user commands go through this device, so the API token is needed only + # in its device config (device server), not on the client. Not called set / put: the device server treats + # those names as ophyd writes (refused on a readOnly device). + + _API_PATHS = ("/api/settings", "/api/status") + + def _check_path(self, path): + if not any(path == p or path.startswith(p + "/") for p in self._API_PATHS): + raise WebHmiError(f"path {path!r} not allowed (only /api/settings*, /api/status)") + + def api_get(self, path: str = "/api/settings") -> dict: + """GET a web HMI loop-settings endpoint; returns its JSON. Errors raise (passed to the client).""" + self._check_path(path) + return self._api._request(path) + + def api_post(self, path: str, body: dict = None) -> dict: + """POST to a web HMI loop-settings endpoint (plan / apply / save); returns its JSON.""" + self._check_path(path) + return self._api._request(path, body or {}) + def snapshot(self) -> dict: """Read now and return {setting: value} (empty when not available).""" self._failed_at = None # an explicit request always tries diff --git a/docs/user/ptychography/flomni.md b/docs/user/ptychography/flomni.md index 4b7e6cbf..61baa612 100644 --- a/docs/user/ptychography/flomni.md +++ b/docs/user/ptychography/flomni.md @@ -804,8 +804,8 @@ Every scan stores the loop settings it ran with: the baseline device `flomni_loo setting, `available`, unsaved / inconsistent counts, the definition's sha1, a JSON of all settings); `dev.flomni_loop_settings.show()` prints what a scan would store now. -FZP settings can only be changed between scans. BEC needs the API token of the web HMI in `~/.flomni_webhmi_token` -(local contact; flOMNI `FeedbackLoop/loopsettings/README.md`). +FZP settings can only be changed between scans. The commands go through the device `flomni_loop_settings`, whose +config holds the web HMI API token (local contact; flOMNI `FeedbackLoop/loopsettings/README.md`). ### Detector trigger and interferometer tuning diff --git a/tests/tests_bec_ipython_client/test_flomni_realtime_system.py b/tests/tests_bec_ipython_client/test_flomni_realtime_system.py index 728f5527..18274088 100644 --- a/tests/tests_bec_ipython_client/test_flomni_realtime_system.py +++ b/tests/tests_bec_ipython_client/test_flomni_realtime_system.py @@ -165,3 +165,35 @@ def test_port_80_fallback(monkeypatch): monkeypatch.setattr(explicit, "_request_once", once) with pytest.raises(RealtimeSystemError): explicit._request("/api/settings") + + +def test_routes_through_the_device(): + """With the BEC device flomni_loop_settings present and enabled, requests go to it by RPC (api_get / api_post), + no token on the client; errors of the device come back as RealtimeSystemError.""" + calls = [] + + class Dev: + enabled = True + + def api_get(self, path): + calls.append(("get", path)) + return {"settings": [{"name": "trigger_tolerance_nm", "value": 10.0}]} + + def api_post(self, path, body): + calls.append(("post", path, body)) + if "move_beyond_um_x" in body.get("changes", {}): + raise RuntimeError("web HMI not reachable") + return {"ok": True, "errors": [], "warnings": [], "changes": [], "params": [], "applied": False} + + dm = type("DM", (), {})() + dm.devices = type("D", (), {})() + dm.devices.flomni_loop_settings = Dev() + rts = FlomniRealtimeSystem(device_manager=dm, token_file="/nonexistent") # token never read + assert rts.get("trigger_tolerance_nm") == 10.0 and rts.route == "device flomni_loop_settings" + rts.check(fzp_enable=0) + assert calls[-1] == ("post", "/api/settings/plan", {"changes": {"fzp_enable": 0}, "profile": None, "adjust": False}) + with pytest.raises(RealtimeSystemError, match="via device flomni_loop_settings: web HMI not reachable"): + rts.set(move_beyond_um_x=0.5) + dm.devices.flomni_loop_settings.enabled = False # disabled: direct HTTP + with pytest.raises(RealtimeSystemError, match="No API token"): + rts.get() diff --git a/tests/tests_devices/test_flomni_loop_settings.py b/tests/tests_devices/test_flomni_loop_settings.py index 8c594316..08066006 100644 --- a/tests/tests_devices/test_flomni_loop_settings.py +++ b/tests/tests_devices/test_flomni_loop_settings.py @@ -95,3 +95,14 @@ def test_token_file_and_snapshot(url, tmp_path): tok.write_text(TOKEN + "\n") dev = FlomniLoopSettings(name="ls", url=url, token_file=str(tok)) assert dev.snapshot()["fzp_range_V_x"] == 1.5 + + +def test_api_methods_for_realtime_system(url): + """api_get / api_post: the RPC used by flomni.realtime_system (token only in the device config).""" + dev = FlomniLoopSettings(name="ls", url=url, token=TOKEN) + assert dev.api_get("/api/settings")["instrument"] == "flOMNI" + with pytest.raises(Exception, match="not allowed"): + dev.api_get("/api/params") # only the loop-settings endpoints + with pytest.raises(Exception, match="not allowed"): + dev.api_post("/api/loop/stop", {}) + assert "api_get" in FlomniLoopSettings.USER_ACCESS and "set" not in FlomniLoopSettings.USER_ACCESS -- 2.54.0 From 76aa2b9e56b52044cb1855a91642f6c9b199efa8 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 18:56:12 +0000 Subject: [PATCH 07/19] feat(flomni): realtime_system.help(name) / help("all"); marked beamline scientists / local contact only - help(""): what it does (help text of the flOMNI definition, as the web HMI tooltips), range, live and saved value, the loop parameters it sets, its rules; help("all"): every setting by group - realtime_system is for beamline scientists / local contact only (Mirko 2026-10-23): module docstring, a line at show() / help(), user guide section heading and note - tests: help output Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019aWcwG89wi7oVyrefukXX6 --- .../plugins/flomni/flomni_realtime_system.py | 61 +++++++++++++++++-- docs/user/ptychography/flomni.md | 10 ++- .../test_flomni_realtime_system.py | 15 +++++ 3 files changed, 78 insertions(+), 8 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py index 374c5366..be6d666f 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py @@ -1,4 +1,8 @@ -"""flomni.realtime_system: the operating settings of the flOMNI real-time loop (expert functions). +"""flomni.realtime_system: the operating settings of the flOMNI real-time loop. + +BEAMLINE SCIENTISTS / LOCAL CONTACT ONLY - not for users. These settings change how the FZP scanner, the detector +trigger and the laser tracker work; a wrong combination can spoil a beamtime's data. The rules refuse combinations +that do not work, but not ones that are only unsuitable for an experiment. The settings, the parameters they map onto and the rules between them live in the flOMNI instrument repository (FeedbackLoop/loopsettings/flomni.json) and are applied by the web HMI on the loop machine (RT-system @@ -23,6 +27,9 @@ from csaxs_bec.devices.omny.webhmi_client import WebHmiClient from csaxs_bec.devices.omny.webhmi_client import WebHmiError as RealtimeSystemError # name kept for BEC users +STAFF_ONLY = "flomni.realtime_system: beamline scientists / local contact only - not for users." + + def _fmt(v): if v is None: return "-" @@ -82,6 +89,7 @@ class FlomniRealtimeSystem(WebHmiClient): snap = self.read() groups = {g["name"]: g for g in snap["groups"]} sc = snap.get("scan") or {} + print(STAFF_ONLY) print(f"Loop settings {snap.get('instrument', '')} ({self.route}) - " + (f"scan running (mode {_fmt(sc.get('mode'))})" if sc.get("active") else "no scan")) for gname, g in groups.items(): @@ -216,8 +224,49 @@ class FlomniRealtimeSystem(WebHmiClient): v = self.get() return v["tracking_target_y"], v["tracking_target_z"] - def help(self): - """Print the functions with a short explanation.""" - print(__doc__.split("\n\nConnection:")[0]) - print("\nShortcuts: trigger_show(), trigger_set_tolerance_nm(v), trigger_set_hold_ms(v)," - " trigger_set_after_move(True/False), tracking_targets(); profiles(), unsaved(), get(name)") + def help(self, name: str = None): + """help(): the functions; help("all"): every setting with its explanation; help(name): one setting (what it + does, unit, range, the loop parameters it sets, live and saved value), from the definition on the loop + machine (the same text as the tooltips of the web HMI tab).""" + if name is None: + print(__doc__.split("\n\nConnection:")[0]) + print("\nShortcuts: trigger_show(), trigger_set_tolerance_nm(v), trigger_set_hold_ms(v)," + " trigger_set_after_move(True/False), tracking_targets(); profiles(), unsaved(), get(name)") + print('Explanations: help("all") or help(""), e.g. help("move_beyond_um_x")') + return + snap = self.read() + settings = {s["name"]: s for s in snap["settings"]} + groups = {g["name"]: g for g in snap["groups"]} + if name == "all": + print(STAFF_ONLY) + for gname, g in groups.items(): + rows = [s for s in snap["settings"] if s["group"] == gname] + if not rows: + continue + print(f"\n{g.get('title', gname)}" + (" (only between scans)" if g.get("interlock") else "")) + for s in rows: + unit = f" [{s['unit']}]" if s.get("unit") else "" + print(f" {s['name']}{unit}: {s.get('title', '')}") + if s.get("help"): + print(f" {s['help']}") + return + s = settings.get(name) + if s is None: + raise RealtimeSystemError(f"unknown setting '{name}'; help('all') lists them") + g = groups.get(s["group"], {}) + print(STAFF_ONLY) + print(f"{s['name']} - {s.get('title', '')} (group {g.get('title', s['group'])}" + + (", only between scans)" if g.get("interlock") else ")")) + if s.get("help"): + print(f" {s['help']}") + rng = "0 / 1" if s.get("type") == "bool" else f"{_fmt(s.get('min'))} ... {_fmt(s.get('max'))} {s.get('unit', '')}" + print(f" range: {rng}") + print(f" live: {_fmt(s.get('value'))} saved (.xmp): {_fmt(s.get('saved'))}" + + (" NOT SAVED" if s.get("unsaved") else "") + ("" if s.get("consistent", True) else " INCONSISTENT")) + if s.get("info"): + print(f" {s['info'].get('label', '')} {s['info']['value']:.4g} {s['info'].get('unit', '')}") + print(" sets: " + ", ".join(f"{t['module']}.{t['param']}[{t['index']}] = {t.get('write', 'v')}" + for t in s.get("targets", []))) + rules = [r for r in snap.get("rules", []) if name in r.get("settings", [])] + if rules: + print(" rules: " + ", ".join(r["name"] + ("" if r.get("ok") else " (VIOLATED)") for r in rules)) diff --git a/docs/user/ptychography/flomni.md b/docs/user/ptychography/flomni.md index 61baa612..b8b99e4a 100644 --- a/docs/user/ptychography/flomni.md +++ b/docs/user/ptychography/flomni.md @@ -224,6 +224,8 @@ In step scans the detector is triggered when the sample is stable at the scan po sample vs FZP (interferometer minus FZP cap sensor) must stay within a tolerance in x and in y (each averaged over 0.5 ms) for a hold time. Both values are live and can be changed between scans: +(Beamline scientists / local contact only, see below.) + - `flomni.realtime_system.trigger_show()` — current tolerance, hold time, minimum delay, trigger after a move - `flomni.realtime_system.trigger_set_tolerance_nm(10)` — tolerance per axis in nm: about 30 for a fast low-resolution scan, 10 standard, 2 for the highest resolution (a few ms more per point); @@ -782,7 +784,10 @@ removed later. [See short version](flomni.md#sample-storage-and-transfer) -### Real-time loop settings (expert): `flomni.realtime_system` +### Real-time loop settings: `flomni.realtime_system` (beamline scientists / local contact only) + +**Not for users.** These settings change how the FZP scanner, the detector trigger and the laser tracker work; a +wrong combination can spoil the data of a beamtime. Users ask the local contact. The operating settings of the real-time loop — FZP scanner on/off, FZP range, feedforward, move-beyond, exposure during the motion, Abbe correction, detector trigger, laser tracker targets and thresholds — with the rules between @@ -798,7 +803,8 @@ them. The rules and the mapping onto the loop parameters are kept on the loop ma - `flomni.realtime_system.profiles()`, `apply_profile("production" | "fzp_off" | "fzp_calibration" | "fast_scan")` - `flomni.realtime_system.unsaved()`, `save_as_default()` — the changes are live only until the next start of the control loop; `save_as_default()` writes them into the loop's parameter files -- `flomni.realtime_system.help()` +- `flomni.realtime_system.help()`; `help("all")` explains every setting, `help("move_beyond_um_x")` one setting + (what it does, range, the loop parameters it sets, live and saved value, its rules) Every scan stores the loop settings it ran with: the baseline device `flomni_loop_settings` (one value per setting, `available`, unsaved / inconsistent counts, the definition's sha1, a JSON of all settings); diff --git a/tests/tests_bec_ipython_client/test_flomni_realtime_system.py b/tests/tests_bec_ipython_client/test_flomni_realtime_system.py index 18274088..ec664b87 100644 --- a/tests/tests_bec_ipython_client/test_flomni_realtime_system.py +++ b/tests/tests_bec_ipython_client/test_flomni_realtime_system.py @@ -197,3 +197,18 @@ def test_routes_through_the_device(): dm.devices.flomni_loop_settings.enabled = False # disabled: direct HTTP with pytest.raises(RealtimeSystemError, match="No API token"): rts.get() + + +def test_help(server, capsys): + """help(name) / help("all"): explanations from the definition, marked as beamline-scientist only.""" + url, tok, _ = server + rts = FlomniRealtimeSystem(url=url, token_file=tok) + rts.help("trigger_tolerance_nm") + out = capsys.readouterr().out + assert "local contact only" in out and "trigger_tolerance_nm" in out and "live: 10" in out + rts.help("all") + assert "Detector trigger" in capsys.readouterr().out + rts.help() + assert "BEAMLINE SCIENTISTS" in capsys.readouterr().out + with pytest.raises(RealtimeSystemError, match="unknown setting"): + rts.help("nosuch") -- 2.54.0 From 7c0a15115d95731979465b93f395d350aa32aa24 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 18:57:47 +0000 Subject: [PATCH 08/19] docs(flomni): loop settings out of the user guide; beamline-scientist document local_contact_only-realtime_system.md Mirko 2026-10-23: a user could change the loop settings and even save them as the new default. The user guide now only says that the real-time loop settings are set by the local contact, that every scan stores them, and where the details are; the commands (realtime_system, trigger, tracker), the profiles, save_as_default, the baseline device, the connection and the web HMI are in plugins/flomni/docs/local_contact_only-realtime_system.md. The detector trigger section of the user guide keeps the explanation and the measured times, without commands. show() / help() name the document. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019aWcwG89wi7oVyrefukXX6 --- .../local_contact_only-realtime_system.md | 81 +++++++++++++++++++ .../plugins/flomni/flomni_realtime_system.py | 3 +- docs/user/ptychography/flomni.md | 63 +++------------ 3 files changed, 93 insertions(+), 54 deletions(-) create mode 100644 csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.md diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.md b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.md new file mode 100644 index 00000000..0fbce70c --- /dev/null +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.md @@ -0,0 +1,81 @@ +# flOMNI real-time loop settings (beamline scientists / local contact only) + +**Not for users.** `flomni.realtime_system` changes how the FZP scanner, the detector trigger and the laser tracker +work, and `save_as_default()` makes a change the default of every following loop start. A wrong combination can spoil +the data of a beamtime. The user guide only says that these settings exist, are set by the local contact and are +stored with every scan; the details are here. + +Background and reference (flOMNI control repository, `OMNY/flOMNI` on gitea): + +- `FeedbackLoop/loopsettings/README.md` — every setting with the loop parameters it sets and its production value, + the rules, the profiles; `FeedbackLoop/loopsettings/flomni.json` — the definition (help texts, rules, profiles) +- `FeedbackLoop/tuning_guide.md`, `FeedbackLoop/feedback_report.md` — characterization, measurements, why the values +- RT-system `docs/loop-settings.md` — design, the web HMI side, installation +- the FZP how-tos next to this file (`local_contact_only-howto_calibrate_the_fzp_scanner.md`, + `local_contact_only-howto_limit_the_fzp_scanner_range.md`) — move-beyond, FZP range, calibration steps as profiles + +## What can be set + +| Group | Settings | +|---|---| +| FZP scanner (only between scans) | `fzp_enable` (both axes or none), `fzp_range_V_x` / `_y`, `feedforward_enable`, `move_beyond_um_x` / `_y`, `exposure_during_motion`, `abbe_enable`, `abbe_limit_um` | +| Detector trigger | `trigger_tolerance_nm`, `trigger_hold_ms`, `trigger_min_delay_ms`, `trigger_on_move` | +| Laser tracker and thresholds | `tracking_target_y` / `_z`, `psd_threshold_low`, `interferometer_threshold`, `interferometer_threshold_rot` | +| FZP calibration (expert, only between scans) | `sample_hold_fzp_V`, `sample_hold_err_um`, `sample_hold_zero` | + +`flomni.realtime_system.help("all")` explains every setting, `help("")` one setting (what it does, range, +live and saved value, the loop parameters it sets, its rules). + +## Commands + +- `flomni.realtime_system.show()` — all settings with live and saved value, violated rules, scan state + (`show(expert=True)` also the FZP calibration group) +- `flomni.realtime_system.set(name=value, ...)` — change; refused with the reason if a rule objects (e.g. the + feedforward must be off while the FZP loop is off); `set(..., adjust=True)` changes the dependent settings the + rule names (never the ones given) +- `flomni.realtime_system.check(name=value)` — dry run +- `flomni.realtime_system.profiles()`, `apply_profile("production" | "fzp_off" | "fzp_calibration" | "fast_scan")` +- `flomni.realtime_system.unsaved()` — what differs from the loop's parameter files +- `flomni.realtime_system.save_as_default()` — writes the live values into the loop's parameter files: the next + loop start uses them. Only for a change that should stay (a backup of each file is kept on the loop machine). +- `flomni.realtime_system.help()`, `help("all")`, `help("")` + +Changes are live only until the next start of the control loop, unless saved. FZP settings can only be changed +between scans (refused while a scan runs); trigger and tracker settings any time. + +### Detector trigger + +In step scans the detector is triggered when the relative position sample vs FZP (interferometer minus FZP cap +sensor) stays within the tolerance in x and in y (each averaged over 0.5 ms) for the hold time. + +- `flomni.realtime_system.trigger_show()` — tolerance, hold time, minimum delay, trigger after a move +- `flomni.realtime_system.trigger_set_tolerance_nm(10)` — about 30 for a fast low-resolution scan, 10 standard, + 2 for the highest resolution (a few ms more per point); 0 = the former criterion (on-target flags of the FZP loops) +- `flomni.realtime_system.trigger_set_hold_ms(3)` — hold time in ms +- `flomni.realtime_system.trigger_set_after_move(True)` — also one trigger after every position change of the + sample stage (e.g. `umv(dev.rtx, ...)`, `dev.rtx.controller.move_xyz(x, y, z)`) once on target; `False` (default): + scan points only + +The former `flomni.interferometer_trigger_*` were removed 2026-10-23. + +### Laser tracker + +`flomni.laser_parameters_show_all()`, `laser_parameters_set_targety(v)` / `_targetz(v)`, +`laser_parameters_set_intensity_threshold_laser(v)`, `laser_parameters_set_psd_intensity_threshold_tracking_low(v)` +and the arrow-key tweak `laser_parameters_tweak_tracking()` use the same settings (`tracking_target_y` / `_z`, +`interferometer_threshold`, `psd_threshold_low`). + +## Stored with every scan + +The baseline device `flomni_loop_settings` is read at the start of every scan: one value per setting, +`available` (0 when the loop machine could not be reached — the scan continues), the number of settings not saved +as default or inconsistent, violated error rules, the definition with its sha1, and a JSON of all settings. +`dev.flomni_loop_settings.show()` prints what a scan would store now. + +## Connection + +`flomni.realtime_system` sends its requests through the device `flomni_loop_settings` (device server). The device +talks to the web HMI on the loop machine (`http://mpc2844:8080`, REST `/api/settings`; port 80 as fallback) with the +API token in its device config (`ptycho_flomni.yaml`, `token:`; the line of `~/loop-data/webhmi/api_token` on +mpc2844). Without that device (e.g. a simulated config) the client talks to the web HMI directly with +`~/.flomni_webhmi_token`. The web HMI tab *Loop settings* shows and changes the same settings with the same rules. diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py index be6d666f..0c08651f 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_realtime_system.py @@ -27,7 +27,8 @@ from csaxs_bec.devices.omny.webhmi_client import WebHmiClient from csaxs_bec.devices.omny.webhmi_client import WebHmiError as RealtimeSystemError # name kept for BEC users -STAFF_ONLY = "flomni.realtime_system: beamline scientists / local contact only - not for users." +STAFF_ONLY = ("flomni.realtime_system: beamline scientists / local contact only - not for users " + "(plugins/flomni/docs/local_contact_only-realtime_system.md).") def _fmt(v): diff --git a/docs/user/ptychography/flomni.md b/docs/user/ptychography/flomni.md index b8b99e4a..9b7f1270 100644 --- a/docs/user/ptychography/flomni.md +++ b/docs/user/ptychography/flomni.md @@ -221,26 +221,12 @@ _There is also an enable without reset, which is used during tomography scans, w #### Detector trigger: position stability In step scans the detector is triggered when the sample is stable at the scan point: the relative position -sample vs FZP (interferometer minus FZP cap sensor) must stay within a tolerance in x and in y (each averaged -over 0.5 ms) for a hold time. Both values are live and can be changed between scans: +sample vs FZP must stay within a tolerance in x and in y for a short hold time (standard 10 nm, 3 ms). The local +contact can choose a coarser tolerance for fast low-resolution scans or a finer one for the highest resolution. +Measured with 3 µm steps (2026-10-20): the trigger comes about 36 ms (x) / 48 ms (y) after the step at 10 nm, +40 / 55 ms at 2 nm; after the trigger the position stays within about 4 nm even at a 30 nm tolerance. -(Beamline scientists / local contact only, see below.) - -- `flomni.realtime_system.trigger_show()` — current tolerance, hold time, minimum delay, trigger after a move -- `flomni.realtime_system.trigger_set_tolerance_nm(10)` — tolerance per axis in nm: about 30 for a fast - low-resolution scan, 10 standard, 2 for the highest resolution (a few ms more per point); - 0 selects the former criterion (on-target flags of the FZP loops) -- `flomni.realtime_system.trigger_set_hold_ms(3)` — hold time in ms -- `flomni.realtime_system.trigger_set_after_move(True)` — additionally one trigger after every position change - of the sample stage (e.g. `umv(dev.rtx, ...)`), as soon as the position is stable with the same tolerance and - hold time; `False` (default): triggers at scan points only -- `dev.rtx.controller.move_xyz(x, y, z)` — moves rtx, rty and rtz with one command (one trigger for the move) - -The former names `flomni.interferometer_trigger_*` were removed (2026-10-23). The values are live; after -a restart of the control loop the values of the loop's parameter files (10 nm, 3 ms) apply again, unless they were -saved with `flomni.realtime_system.save_as_default()`. Measured with 3 µm steps -(2026-10-20): the trigger comes about 36 ms (x) / 48 ms (y) after the step at 10 nm, 40 / 55 ms at 2 nm; -after the trigger the position stays within about 4 nm even at a 30 nm tolerance. +- `dev.rtx.controller.move_xyz(x, y, z)` — moves rtx, rty and rtz with one command ### Scanning in 2D and sample alignment @@ -784,42 +770,13 @@ removed later. [See short version](flomni.md#sample-storage-and-transfer) -### Real-time loop settings: `flomni.realtime_system` (beamline scientists / local contact only) +### Real-time loop settings -**Not for users.** These settings change how the FZP scanner, the detector trigger and the laser tracker work; a -wrong combination can spoil the data of a beamtime. Users ask the local contact. +The operating settings of the real-time control loop (FZP scanner, detector trigger, laser tracker) are set by the +local contact; please do not change them. Every scan stores the settings it ran with (baseline device +`flomni_loop_settings`). -The operating settings of the real-time loop — FZP scanner on/off, FZP range, feedforward, move-beyond, exposure -during the motion, Abbe correction, detector trigger, laser tracker targets and thresholds — with the rules between -them. The rules and the mapping onto the loop parameters are kept on the loop machine (web HMI tab *Loop settings*, -`http://mpc2844:8080/?tab=settings`; definition in the flOMNI control repository, -`FeedbackLoop/loopsettings/flomni.json` and `README.md`); BEC and the web page use the same rules. - -- `flomni.realtime_system.show()` — all settings with live and saved value, violated rules, scan state - (`show(expert=True)` also the FZP calibration group) -- `flomni.realtime_system.set(name=value, ...)` — change; refused (with the reason) if a rule objects, e.g. the - feedforward must be off while the FZP loop is off; `set(..., adjust=True)` changes the dependent settings too -- `flomni.realtime_system.check(name=value)` — dry run -- `flomni.realtime_system.profiles()`, `apply_profile("production" | "fzp_off" | "fzp_calibration" | "fast_scan")` -- `flomni.realtime_system.unsaved()`, `save_as_default()` — the changes are live only until the next start of the - control loop; `save_as_default()` writes them into the loop's parameter files -- `flomni.realtime_system.help()`; `help("all")` explains every setting, `help("move_beyond_um_x")` one setting - (what it does, range, the loop parameters it sets, live and saved value, its rules) - -Every scan stores the loop settings it ran with: the baseline device `flomni_loop_settings` (one value per -setting, `available`, unsaved / inconsistent counts, the definition's sha1, a JSON of all settings); -`dev.flomni_loop_settings.show()` prints what a scan would store now. - -FZP settings can only be changed between scans. The commands go through the device `flomni_loop_settings`, whose -config holds the web HMI API token (local contact; flOMNI `FeedbackLoop/loopsettings/README.md`). - -### Detector trigger and interferometer tuning - -The trigger tolerance and hold time: [Detector trigger: position stability](#detector-trigger-position-stability) -(`flomni.realtime_system.trigger_*`). The control loops of the sample stage, the FZP scanner and the laser -tracker were characterized and retuned in October 2026; results, the present parameters and how to retune them -are documented in the flOMNI control repository (`FeedbackLoop/feedback_report.md` with PDF, -`FeedbackLoop/tuning_guide.md`). +Details for beamline scientists: `csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.md`. ### Heater -- 2.54.0 From 1a5b53c03120e51aeeadbd2ad20d51bc57c50a55 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 24 Oct 2026 06:38:54 +0000 Subject: [PATCH 09/19] fix(flomni): review fixes: absolute isclose tolerances, alignment retry, special angles, resume after completion - np.isclose(a, b, x) took x as rtol: fsamx out (+-1.6 mm), gripper sensor voltage (+-0.55 V / +-1.1 V at -2.2 V), fsamx in checks now use atol (0.2 / 0.5 mm, 0.25 / 0.5 V, fsamx out 0.5 mm = Galil tolerance) - tomo_alignment_scan: an angle with a TimeoutError is repeated (successful was set unconditionally, the angle was skipped); ptychotomoalign_scannum.txt is rewritten with the accepted scans if numbers shifted - special angles: special_angle_tolerance is used (was hard-coded 0.5; init value 20 -> 0.5), any remaining special angle matches (not only the first in the list), kept on tomo_scan_resume in the same session - tomo_scan_resume after a completed scan does nothing (progress tomo_completed; re-acquired the last projection and repeated the end-of-scan steps before); tomo_queue_reacquire clears the flag - laser_parameters_tweak_tracking docstring: step 0.02 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019aWcwG89wi7oVyrefukXX6 --- .../plugins/OMNY_shared/tomo_queue_mixin.py | 2 + .../plugins/flomni/flomni.py | 65 ++++++-- .../test_flomni_review_fixes.py | 141 ++++++++++++++++++ .../test_flomni_special_angles.py | 32 ++++ 4 files changed, 224 insertions(+), 16 deletions(-) create mode 100644 tests/tests_bec_ipython_client/test_flomni_review_fixes.py diff --git a/csaxs_bec/bec_ipython_client/plugins/OMNY_shared/tomo_queue_mixin.py b/csaxs_bec/bec_ipython_client/plugins/OMNY_shared/tomo_queue_mixin.py index efa0a2c9..f46cfe3e 100644 --- a/csaxs_bec/bec_ipython_client/plugins/OMNY_shared/tomo_queue_mixin.py +++ b/csaxs_bec/bec_ipython_client/plugins/OMNY_shared/tomo_queue_mixin.py @@ -1157,6 +1157,8 @@ class TomoQueueMixin: self.progress["estimated_finish_time"] = None self.progress["accumulated_idle_time"] = 0.0 self.progress["heartbeat"] = None + # flOMNI: a completed scan is not resumed (tomo_scan_resume); reopened here on purpose + self.progress["tomo_completed"] = False job_id = job["id"] self._tomo_queue_proxy.update_by_id(job_id, status="incomplete") diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index fc2768a1..3f175ba5 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -82,7 +82,8 @@ class FlomniInitStagesMixin: sensor_voltage_target = dev.ftransy.user_parameter.get("sensor_voltage") sensor_voltage = float(dev.ftransy.controller.socket_put_and_receive("MG@AN[1]").strip()) - if not np.isclose(sensor_voltage, sensor_voltage_target, 0.25): + # absolute tolerances (V, mm): the third positional argument of np.isclose is rtol + if not np.isclose(sensor_voltage, sensor_voltage_target, rtol=0, atol=0.25): print( f"Sensor voltage of the gripper is {sensor_voltage}, while target from config is {sensor_voltage_target}" ) @@ -410,7 +411,7 @@ class FlomniSampleTransferMixin: self.ensure_osa_back() fsamx_in = dev.fsamx.user_parameter.get("in") - if not np.isclose(dev.fsamx.readback.get(), fsamx_in, 0.2): + if not np.isclose(dev.fsamx.readback.get(), fsamx_in, rtol=0, atol=0.2): raise FlomniError("fsamx not in position. Aborting.") fheater_in = dev.fheater.user_parameter.get("in") @@ -525,7 +526,7 @@ class FlomniSampleTransferMixin: """Interactive tweak of the two laser tracking target positions. Arrow keys nudge the tracking targets (realtime_system tracking_target_y / _z) by a fixed step of - 0.01 (absolute set of current +/- step): + 0.02 (absolute set of current +/- step): left / right : target y -/+ up / down : target z +/- Keys: @@ -724,7 +725,7 @@ class FlomniSampleTransferMixin: self.flomnigui_raise() target_pos = -162 - if np.isclose(dev.fsamx.readback.get(), target_pos, 0.01): + if np.isclose(dev.fsamx.readback.get(), target_pos, rtol=0, atol=0.5): return umv(dev.fsamroy, 0) @@ -764,7 +765,7 @@ class FlomniSampleTransferMixin: sensor_voltage_target = dev.ftransy.user_parameter.get("sensor_voltage") sensor_voltage = float(dev.ftransy.controller.socket_put_and_receive("MG@AN[1]").strip()) - if not np.isclose(sensor_voltage, sensor_voltage_target, 0.5): + if not np.isclose(sensor_voltage, sensor_voltage_target, rtol=0, atol=0.5): raise FlomniError(f"Sensor voltage is {sensor_voltage}, indicates an error. Aborting.") def ftransfer_get_sample(self, position: int): @@ -1643,6 +1644,8 @@ class _ProgressProxy: "estimated_finish_time": None, "heartbeat": None, "accumulated_idle_time": 0.0, + # set when tomo_scan() reached its end: tomo_scan_resume() then has nothing to resume + "tomo_completed": False, } def __init__(self, client): @@ -1718,7 +1721,7 @@ class Flomni( self.device_manager = client.device_manager self.special_angles = [] self.special_angle_repeats = 20 - self.special_angle_tolerance = 20 + self.special_angle_tolerance = 0.5 self._current_special_angles = [] self._beam_is_okay = True self._progress_proxy = _ProgressProxy(self.client) @@ -1850,7 +1853,7 @@ class Flomni( self.align.align(keep_shutter_open) except KeyboardInterrupt as exc: fsamx_in = self._get_user_param_safe(dev.fsamx, "in") - if np.isclose(fsamx_in, dev.fsamx.readback.get(), 0.5): + if np.isclose(fsamx_in, dev.fsamx.readback.get(), rtol=0, atol=0.5): print("Stopping alignment. Returning to fsamx in position.") self.feedback_disable() umv(dev.fsamx, fsamx_in) @@ -1911,10 +1914,14 @@ class Flomni( def set_special_angles(self, angles: list, repeats: int = 20, tolerance: float = 0.5): """Set the special angles for a tomo + The first projection of the tomogram within `tolerance` of a special angle is acquired + `repeats` times; each special angle is used once per tomogram (in any order), also across + tomo_scan_resume() in the same session. + Args: angles (list): List of special angles repeats (int, optional): Number of repeats at a special angle. Defaults to 20. - tolerance (float, optional): Number of repeats at a special angle. Defaults to 0.5. + tolerance (float, optional): Angle tolerance in degrees. Defaults to 0.5. """ self.special_angles = angles @@ -2228,6 +2235,8 @@ class Flomni( self.write_alignment_scan_numbers(bec.queue.next_scan_number) start_angle = 0 alignment_scan_numbers = [] + # first scan number of the accepted attempt per angle (a retry shifts the scan numbers) + alignment_first_scans = [] angle_end = start_angle + 180 for angle in np.linspace(start_angle, angle_end, num=int(180 / 45) + 1, endpoint=True): @@ -2245,16 +2254,24 @@ class Flomni( bec.queue.request_queue_reset() time.sleep(2) error_caught = True + print(f"Timeout at angle {angle}, repeating it (Ctrl-C to stop).") else: raise exc # todo here was if blchk success, then setting to success true - successful = True + # repeat the angle after a timeout (it was skipped silently before, 2026-10-24) + successful = not error_caught end_scan_number = bec.queue.next_scan_number + alignment_first_scans.append(start_scan_number) for scan_nr in range(start_scan_number, end_scan_number): # self._write_tomo_scan_number(scan_nr, angle, 0) alignment_scan_numbers.append(scan_nr) + consecutive = [alignment_first_scans[0] + k for k in range(len(alignment_first_scans))] + if alignment_first_scans != consecutive: + # a retry (or stitching) shifted the scan numbers: rewrite the file with the accepted scans + self.write_alignment_scan_numbers(alignment_first_scans[0], scans=alignment_first_scans) + umv(dev.fsamroy, 0) self.OMNYTools.printgreenbold( "\n\nAlignment scan finished. Please run BEC_ptycho_align and load the new fit by flomni.read_alignment_offset() ." @@ -2274,13 +2291,14 @@ class Flomni( print(scilog_content) self._scilog_write(scilog_content, "alignmentscan") - def write_alignment_scan_numbers(self, first_scan): + def write_alignment_scan_numbers(self, first_scan, scans=None): import os file = os.path.expanduser("~/data/raw/logs/ptychotomoalign_scannum.txt") os.makedirs(os.path.dirname(file), exist_ok=True) - scans = [first_scan + k for k in range(5)] + if scans is None: + scans = [first_scan + k for k in range(5)] angles = [0, 45, 90, 135, 180] x_vals = [] @@ -2572,10 +2590,16 @@ class Flomni( # succeeded, so a retried attempt still sees the same special # angle and repeat count. num_repeats = 1 - is_special_angle = bool( - self._current_special_angles - and np.isclose(angle, self._current_special_angles[0], atol=0.5) + # any remaining special angle within the tolerance (not only the next one in the list) + special_index = next( + ( + k + for k, special in enumerate(self._current_special_angles) + if np.isclose(angle, special, rtol=0, atol=self.special_angle_tolerance) + ), + None, ) + is_special_angle = special_index is not None if is_special_angle: num_repeats = self.special_angle_repeats start_scan_number = bec.queue.next_scan_number @@ -2585,7 +2609,7 @@ class Flomni( self._at_each_angle(angle, **overrides) projection_duration = time.perf_counter() - projection_start if is_special_angle: - self._current_special_angles.pop(0) + self._current_special_angles.pop(special_index) end_scan_number = bec.queue.next_scan_number for scan_nr in range(start_scan_number, end_scan_number): @@ -2733,7 +2757,9 @@ class Flomni( bec.builtin_actors.scan_interlock.trigger_setting = "restart_scan" bec.builtin_actors.scan_interlock.enabled = True - self._current_special_angles = self.special_angles.copy() + if is_new_scan or not self.special_angles: + # on a resume keep the special angles not yet acquired (they were taken again before) + self._current_special_angles = list(self.special_angles) if is_new_scan: # pylint: disable=undefined-variable # Always attempt registration (even for an empty/test account) @@ -2762,6 +2788,7 @@ class Flomni( # self.progress.reset() above, before the gates -- only the actual # start time is set here, once the scan is genuinely committed to. self.progress["tomo_start_time"] = datetime.datetime.now().isoformat() + self.progress["tomo_completed"] = False if file_angles is not None: self._init_angle_file_progress(file_angles, projection_number) self.collect_empty_frames() @@ -2938,6 +2965,8 @@ class Flomni( # (120s / 90s) after a tomogram that's actually already done. self.progress["heartbeat"] = None + # all projections acquired: nothing left for tomo_scan_resume() (also if a step below fails) + self.progress["tomo_completed"] = True self.progress["projection"] = self.progress["total_projections"] self.progress["subtomo_projection"] = self.progress["subtomo_total_projections"] self._print_progress() @@ -2991,6 +3020,10 @@ class Flomni( if self.progress.get("tomo_start_time") is None: print("No tomo scan in progress to resume -- nothing to do.") return + if self.progress.get("tomo_completed"): + # it re-acquired the last projection (and repeated the end-of-scan steps) before, 2026-10-24 + print("The last tomo scan was completed -- nothing to resume.") + return if not from_queue and not self._confirm_resume_outside_queue(): print("Aborted.") diff --git a/tests/tests_bec_ipython_client/test_flomni_review_fixes.py b/tests/tests_bec_ipython_client/test_flomni_review_fixes.py new file mode 100644 index 00000000..cb3bd58c --- /dev/null +++ b/tests/tests_bec_ipython_client/test_flomni_review_fixes.py @@ -0,0 +1,141 @@ +"""Fixes from the flomni.py review 2026-10-24: tomo_scan_resume() after a completed scan, special angles kept +on resume, the alignment scan repeats an angle after a timeout, absolute np.isclose tolerances.""" + +import builtins +import types + +import pytest + +import csaxs_bec.bec_ipython_client.plugins.flomni.flomni as flomni_module +from csaxs_bec.bec_ipython_client.plugins.flomni.flomni import Flomni, FlomniError, _ProgressProxy + + +class _FakeClient: + def __init__(self): + self._vars = {} + + def get_global_var(self, key): + return self._vars.get(key) + + def set_global_var(self, key, value): + self._vars[key] = value + + +def _make_flomni(): + obj = object.__new__(Flomni) + obj.client = _FakeClient() + obj._progress_proxy = _ProgressProxy(obj.client) + return obj + + +def test_resume_after_completed_scan_does_nothing(monkeypatch): + flomni = _make_flomni() + flomni.progress["tomo_start_time"] = "2026-10-24T10:00:00" + flomni.progress["tomo_completed"] = True + monkeypatch.setattr(Flomni, "tomo_type", 1, raising=False) + flomni.tomo_scan = lambda **kw: pytest.fail("tomo_scan must not be called") + flomni._confirm_resume_outside_queue = lambda: True + + flomni.tomo_scan_resume() + + +def test_resume_of_interrupted_scan_still_resumes(monkeypatch): + flomni = _make_flomni() + flomni.progress["tomo_start_time"] = "2026-10-24T10:00:00" + flomni.progress["subtomo"] = 3 + flomni.progress["angle"] = 45.0 + monkeypatch.setattr(Flomni, "tomo_type", 1, raising=False) + calls = [] + flomni.tomo_scan = lambda **kw: calls.append(kw) + flomni._confirm_resume_outside_queue = lambda: True + + flomni.tomo_scan_resume() + + assert calls == [{"subtomo_start": 3, "start_angle": 45.0}] + + +def test_progress_reset_clears_completed(): + flomni = _make_flomni() + flomni.progress["tomo_completed"] = True + flomni.progress.reset() + assert flomni.progress.get("tomo_completed") is False + + +class _Timeout(flomni_module.AlarmBase): + def __init__(self): + Exception.__init__(self, "timeout") + self.alarm_type = "TimeoutError" + + +class _Queue: + def __init__(self): + self.next_scan_number = 100 + self.resets = 0 + + def request_queue_reset(self): + self.resets += 1 + + +def test_alignment_scan_repeats_an_angle_after_timeout(monkeypatch, tmp_path): + flomni = _make_flomni() + queue = _Queue() + fake_dev = types.SimpleNamespace( + rtx=types.SimpleNamespace( + controller=types.SimpleNamespace(feedback_is_running=lambda: True) + ), + fsamroy=object(), + ) + monkeypatch.setitem(builtins.__dict__, "bec", types.SimpleNamespace(queue=queue)) + monkeypatch.setitem(builtins.__dict__, "dev", fake_dev) + monkeypatch.setattr(flomni_module, "umv", lambda *a: None, raising=False) + monkeypatch.setattr(flomni_module, "filters_out_of_beam", lambda dev: (True, [])) + monkeypatch.setattr(flomni_module.time, "sleep", lambda s: None) + monkeypatch.setattr(Flomni, "sample_name", "test", raising=False) + monkeypatch.setenv("HOME", str(tmp_path)) + flomni.get_alignment_offset = lambda angle: (1.0, 0.0, 0.0) + flomni._check_eye_out_and_optics_in = lambda: True + flomni.OMNYTools = types.SimpleNamespace(printgreenbold=lambda msg: None) + flomni._scilog_write = lambda *a: True + angles = [] + + def projection(angle, _internal=False): + angles.append(angle) + queue.next_scan_number += 1 # one scan per attempt (the timed-out one included) + if angle == 90 and angles.count(90) == 1: + raise _Timeout() + + flomni.tomo_scan_projection = projection + + flomni.tomo_alignment_scan() + + assert angles == [0, 45, 90, 90, 135, 180] + assert queue.resets == 1 + lines = (tmp_path / "data/raw/logs/ptychotomoalign_scannum.txt").read_text().splitlines() + assert lines[0] == "100 101 103 104 105" # the accepted scan per angle, not 100..104 + + +@pytest.mark.parametrize( + "readback, ok", [(-1.14, True), (-1.30, True), (-1.40, False), (-0.80, False)] +) +def test_heater_down_needs_fsamx_in_within_0_2_mm(monkeypatch, readback, ok): + flomni = _make_flomni() + fsamx = types.SimpleNamespace( + user_parameter={"in": -1.14}, readback=types.SimpleNamespace(get=lambda: readback) + ) + fheater = types.SimpleNamespace( + _config={"deviceConfig": {"axis_Id": "H"}}, user_parameter={"in": 5} + ) + monkeypatch.setitem( + builtins.__dict__, "dev", types.SimpleNamespace(fsamx=fsamx, fheater=fheater) + ) + monkeypatch.setattr(flomni_module, "dev", builtins.__dict__["dev"], raising=False) + moved = [] + monkeypatch.setattr(flomni_module, "umv", lambda *a: moved.append(a), raising=False) + flomni.ensure_osa_back = lambda: None + if ok: + flomni.move_fheater_down() + assert moved + else: + with pytest.raises(FlomniError): + flomni.move_fheater_down() + assert not moved diff --git a/tests/tests_bec_ipython_client/test_flomni_special_angles.py b/tests/tests_bec_ipython_client/test_flomni_special_angles.py index 7cfab251..4a5781f5 100644 --- a/tests/tests_bec_ipython_client/test_flomni_special_angles.py +++ b/tests/tests_bec_ipython_client/test_flomni_special_angles.py @@ -32,6 +32,7 @@ def _make_flomni(monkeypatch): obj.tomo_angle_range = 180 obj.special_angles = [] obj.special_angle_repeats = 3 + obj.special_angle_tolerance = 0.5 obj._current_special_angles = [] obj._print_progress = lambda: None obj._format_duration = lambda seconds: "0s" @@ -86,3 +87,34 @@ def test_special_angle_repeats_survive_a_retry(monkeypatch): assert len(calls) == 1 + 3, "the retry must still repeat the special angle" assert flomni._current_special_angles == [] + + +def test_special_angle_tolerance_is_used(monkeypatch): + """special_angle_tolerance was ignored (hard-coded 0.5) before 2026-10-24.""" + flomni = _make_flomni(monkeypatch) + flomni.special_angles = [90.0] + flomni._current_special_angles = [90.0] + flomni.special_angle_tolerance = 2.0 + calls = [] + flomni._at_each_angle = lambda angle: calls.append(angle) + + flomni._tomo_scan_at_angle(91.5, 1) + + assert calls == [91.5] * 3 + assert flomni._current_special_angles == [] + + +def test_special_angles_match_in_any_order(monkeypatch): + """An angle not reached first (e.g. a reverse sub-tomogram) no longer blocks the later ones.""" + flomni = _make_flomni(monkeypatch) + flomni.special_angles = [30.0, 120.0] + flomni._current_special_angles = [30.0, 120.0] + calls = [] + flomni._at_each_angle = lambda angle: calls.append(angle) + + flomni._tomo_scan_at_angle(120.0, 1) + flomni._tomo_scan_at_angle(120.0, 1) # used once only + flomni._tomo_scan_at_angle(30.0, 1) + + assert calls == [120.0] * 3 + [120.0] + [30.0] * 3 + assert flomni._current_special_angles == [] -- 2.54.0 From b939793416d7d26e0bd357becd9ddb4d165b2bea Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 24 Oct 2026 06:43:17 +0000 Subject: [PATCH 10/19] fix(flomni): x-ray eye Ctrl-C handler looked up fsamx by device object; ffzp_info collision position - start_x_ray_eye_alignment: _get_user_param_safe() needs the device name (dev[name]); with dev.fsamx the KeyboardInterrupt handler raised KeyError and fsamx was never returned to its in position - ffzp_info: the OSA collides at fosaz = 33 - foptz (compute_osa_remaining_space), printed 33 - fosaz Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019aWcwG89wi7oVyrefukXX6 --- .../plugins/flomni/flomni.py | 2 +- .../plugins/flomni/flomni_optics_mixin.py | 3 +- .../test_flomni_review_fixes.py | 38 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index 3f175ba5..c19e1af3 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -1852,7 +1852,7 @@ class Flomni( try: self.align.align(keep_shutter_open) except KeyboardInterrupt as exc: - fsamx_in = self._get_user_param_safe(dev.fsamx, "in") + fsamx_in = self._get_user_param_safe("fsamx", "in") # name: dev[...] lookup (a device raised) if np.isclose(fsamx_in, dev.fsamx.readback.get(), rtol=0, atol=0.5): print("Stopping alignment. Returning to fsamx in position.") self.feedback_disable() diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py index f31dd04f..162eab37 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py @@ -420,7 +420,8 @@ class FlomniOpticsMixin: print("\nOSA Information:") print(f" Current fosaz {fosaz_val:.1f}") - print(f" The OSA will collide with a normal OMNY pin at fosaz \033[1m{(33-fosaz_val):.1f}\033[0m") + # collision where compute_osa_remaining_space() = 33 - foptz - fosaz reaches 0 (was 33 - fosaz) + print(f" The OSA will collide with a normal OMNY pin at fosaz \033[1m{(33-foptz_val):.1f}\033[0m") print(f" Remaining space (right now): \033[1m{remaining_current:.1f}\033[0m") diff = fosaz_val - fosaz_in # >0: OSA is currently more "in" than nominal -> worse diff --git a/tests/tests_bec_ipython_client/test_flomni_review_fixes.py b/tests/tests_bec_ipython_client/test_flomni_review_fixes.py index cb3bd58c..c842b0b5 100644 --- a/tests/tests_bec_ipython_client/test_flomni_review_fixes.py +++ b/tests/tests_bec_ipython_client/test_flomni_review_fixes.py @@ -139,3 +139,41 @@ def test_heater_down_needs_fsamx_in_within_0_2_mm(monkeypatch, readback, ok): with pytest.raises(FlomniError): flomni.move_fheater_down() assert not moved + + +class _DevContainer(dict): + """dict keyed by device name, like bec_lib's DeviceContainer (attribute access too).""" + + def __getattr__(self, name): + return self[name] + + +def test_xray_eye_ctrl_c_returns_fsamx_in(monkeypatch): + """The KeyboardInterrupt handler passed the device instead of its name: dev[device] raised.""" + import csaxs_bec.bec_ipython_client.plugins.flomni.flomni_optics_mixin as optics_module + + flomni = _make_flomni() + fsamx = types.SimpleNamespace( + user_parameter={"in": -1.14}, readback=types.SimpleNamespace(get=lambda: -1.3) + ) + fake_dev = _DevContainer(fsamx=fsamx) + monkeypatch.setitem(builtins.__dict__, "dev", fake_dev) + monkeypatch.setattr(flomni_module, "dev", fake_dev, raising=False) + monkeypatch.setattr(optics_module, "dev", fake_dev, raising=False) + moved = [] + monkeypatch.setattr(flomni_module, "umv", lambda *a: moved.append(a), raising=False) + flomni.OMNYTools = types.SimpleNamespace(yesno=lambda *a, **k: True) + flomni.feedback_disable = lambda: None + + class _Align: + def __init__(self, *a): + pass + + def align(self, keep_shutter_open): + raise KeyboardInterrupt + + monkeypatch.setattr(flomni_module, "XrayEyeAlign", _Align) + + with pytest.raises(KeyboardInterrupt): + flomni.start_x_ray_eye_alignment() + assert moved == [(fsamx, -1.14)] -- 2.54.0 From 04aa7713fbc6bd6332cf24f5d61f728633751270 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 24 Oct 2026 06:55:43 +0000 Subject: [PATCH 11/19] fix(flomni): two 0-deg shots at the start of type 1, stop on declined gates, arrow-key tweaks, CLI angle range - tomo_scan type 1 with zero_deg_reference_at_each_subtomo: no extra reference before sub-tomogram 1 of a new scan (fixed 0-deg shot + sub-tomogram 1's first projection = 2 in a row, was 3) - tomo_scan: a declined eye/optics or filter gate raises FlomniError instead of returning (the tomo queue marked the job "done" without data) - feye_out / feye_in: "no" (direct beam still visible, fttrx1 missing) raises FlomniOpticsError instead of returning (tomo_alignment_scan measured on with the eye in) - tweak_cursor and laser_parameters_tweak_tracking: read_escape_suffix() waits briefly for a split arrow-key sequence and ignores ESC alone (read(2) unpacking raised); tweak_cursor message names the device - tomo_parameters_cli: angle range re-asked until 180 or 360; _get_val re-asks on a non-number - check_position_is_valid message 0-20; collect_empty_frames docstring: the sample is moved out - omny/AI_docs/TODO_flomni_review_2026-10_findings.md: the same bugs in OMNY (x-ray eye Ctrl-C, special angles) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019aWcwG89wi7oVyrefukXX6 --- .../plugins/OMNY_shared/omny_general_tools.py | 27 ++++++++-- .../plugins/flomni/flomni.py | 38 +++++++++----- .../plugins/flomni/flomni_optics_mixin.py | 12 ++--- .../TODO_flomni_review_2026-10_findings.md | 48 ++++++++++++++++++ .../test_flomni_review_fixes.py | 50 +++++++++++++++++++ .../test_flomni_tomo_angles.py | 27 +++++++++- 6 files changed, 180 insertions(+), 22 deletions(-) create mode 100644 csaxs_bec/bec_ipython_client/plugins/omny/AI_docs/TODO_flomni_review_2026-10_findings.md diff --git a/csaxs_bec/bec_ipython_client/plugins/OMNY_shared/omny_general_tools.py b/csaxs_bec/bec_ipython_client/plugins/OMNY_shared/omny_general_tools.py index 0a75cb8f..3df5b45b 100644 --- a/csaxs_bec/bec_ipython_client/plugins/OMNY_shared/omny_general_tools.py +++ b/csaxs_bec/bec_ipython_client/plugins/OMNY_shared/omny_general_tools.py @@ -35,6 +35,26 @@ def umvr(*args): return scans.umv(*args, relative=True) +def read_escape_suffix(stream, timeout: float = 0.05) -> str | None: + """The two characters after an ESC on a non-blocking stdin (arrow keys: "[A".."[D"), + or None if they do not arrive within `timeout` (ESC alone, or a slow terminal). + + A plain stream.read(2) can return fewer characters, or None, before the rest of the + sequence has arrived; unpacking that raised and ended the tweak with a traceback.""" + chars = "" + deadline = time.monotonic() + timeout + while len(chars) < 2 and time.monotonic() < deadline: + try: + got = stream.read(2 - len(chars)) + except (IOError, TypeError): + got = None + if got: + chars += got + else: + time.sleep(0.005) + return chars if len(chars) == 2 else None + + class OMNYTools: HEADER = "\033[95m" @@ -164,10 +184,10 @@ class OMNYTools: self, dev1, step1: float, dev2="none", step2: float = "0", special_command="none" ): if dev1 not in dev.enabled_devices: - print(f"Device 1 {dev} is not in enabled devices.") + print(f"Device 1 {dev1} is not in enabled devices.") return if dev2 not in dev.enabled_devices and dev2 != "none": - print(f"Device 2 {dev} is not in enabled devices.") + print(f"Device 2 {dev2} is not in enabled devices.") return # Save the current terminal settings fd = sys.stdin.fileno() @@ -187,7 +207,8 @@ class OMNYTools: print("\n\rExiting tweak mode\r") break elif key == "\x1b": # Escape sequences for arrow keys - next1, next2 = sys.stdin.read(2) + suffix = read_escape_suffix(sys.stdin) + next1, next2 = suffix if suffix else ("", "") if next1 == "[": if next2 == "A": if dev2 != "none": diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index c19e1af3..7e7e594b 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -40,6 +40,7 @@ from csaxs_bec.bec_ipython_client.plugins.OMNY_shared.omny_general_tools import OMNYTools, PtychoReconstructor, TomoIDManager, + read_escape_suffix, ) from csaxs_bec.bec_ipython_client.plugins.OMNY_shared.ptouch_printer import PTouchLabelPrinter from csaxs_bec.devices.omny.galil.galil_ophyd import GalilError @@ -602,7 +603,8 @@ class FlomniSampleTransferMixin: print("\rReturned to start positions\r") print_status() elif key == "\x1b": # escape sequences for arrow keys - next1, next2 = sys.stdin.read(2) + suffix = read_escape_suffix(sys.stdin) + next1, next2 = suffix if suffix else ("", "") if next1 == "[": target_y, target_z = read_targets() if next2 == "A": # up: target z + @@ -1065,7 +1067,7 @@ class FlomniSampleTransferMixin: if 0 <= position < 21: return raise FlomniError( - f"The given position number [{position}] is not in the valid range of 0-21. " + f"The given position number [{position}] is not in the valid range of 0-20. " ) def ftransfer_controller_enable_mount_mode(self): @@ -2626,8 +2628,8 @@ class Flomni( ) def collect_empty_frames(self): - """Acquire 10 empty-frame (flat-field) images at angle 0, with fsamx - shifted out of the beam by half the field of view (fovx/2), in the + """Acquire 10 empty-frame (flat-field) images at angle 0, with the + sample moved out of the beam by half the field of view (fovx/2), in the direction opposite the normal alignment x-offset at angle 0. Called once at the start of a new tomo_scan() (not on resume). @@ -2739,15 +2741,15 @@ class Flomni( if self.OMNYTools.yesno("Shall I continue?", "n"): print("OK") else: - print("Stopping.") - return + # raise, not return: a return let tomo_queue_execute() mark the job "done" without data + raise FlomniError("Tomo scan stopped: the setup is not in measurement condition.") all_out, offending = filters_out_of_beam(dev) if not all_out: warning = f"Not all filters are out of the beam: {', '.join(offending)}." if not warn_and_confirm(self, warning, interactive=interactive, force=False): - print("Aborting tomo scan.") - return + # raise, not return (see above) + raise FlomniError(f"Tomo scan aborted: {warning}") self.flomnigui_show_progress() @@ -2813,6 +2815,9 @@ class Flomni( start_angle is None and self._subtomo_starts_near_zero(ii) and self.zero_deg_reference_at_each_subtomo + # new scan: the fixed 0-deg shot above and sub-tomogram 1's first + # projection (0 deg) are already the two at the start (Mirko 2026-10-24) + and not (is_new_scan and ii == 1) ): # Dedicated reference shot at exactly 0 degrees, taken # every time the rotation passes back through 0, for @@ -4024,7 +4029,7 @@ class Flomni( if self.tomo_type == 1: self.tomo_angle_range = self._get_val( - "Angular range (180 or 360)", self.tomo_angle_range, int + "Angular range (180 or 360)", self.tomo_angle_range, int, choices=(180, 360) ) # Default shown here must be the actual achievable total # (int(range/stepsize)*8), not the raw algebraic inverse of @@ -4141,14 +4146,23 @@ class Flomni( self._prompt_angle_file() @staticmethod - def _get_val(msg: str, default_value, data_type, min_=None, max_=None): + def _get_val(msg: str, default_value, data_type, min_=None, max_=None, choices=None): """Prompt for a value, casting to data_type. When min_/max_ are given, re-prompts (rather than silently clamping) until the typed value is in range, mirroring the Tomo Parameters GUI's spinbox bounds (tomo_params.py) so tomo_parameters() enforces the same - limits instead of accepting anything of the right type.""" + limits instead of accepting anything of the right type. Also + re-prompts on a value that cannot be cast, or that is not one of + `choices` (when given).""" while True: - val = data_type(input(f"{msg} ({default_value}): ") or default_value) + try: + val = data_type(input(f"{msg} ({default_value}): ") or default_value) + except ValueError: + print(f"Not a valid {data_type.__name__}. Please try again.") + continue + if choices is not None and val not in choices: + print(f"Value must be one of {', '.join(str(c) for c in choices)}. Please try again.") + continue if min_ is not None and val < min_: print(f"Value must be >= {min_}. Please try again.") continue diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py index 162eab37..0703a04a 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni_optics_mixin.py @@ -88,8 +88,10 @@ class FlomniOpticsMixin: if self.OMNYTools.yesno("Did the direct beam on the xray eye disappear?"): print("excellent.") else: - print("Aborting. With visible parts of the direct beam on the xray eye, it cannot be removed.") - return + # raise, not return: callers (tomo_alignment_scan) went on measuring with the eye in + raise FlomniOpticsError( + "Aborting. With visible parts of the direct beam on the xray eye, it cannot be removed." + ) feyex_out = self._get_user_param_safe("feyex", "out") umv(dev.feyex, feyex_out) @@ -104,8 +106,7 @@ class FlomniOpticsMixin: "this could cause collisions when operating on the real " "beamline." ): - print("Aborting feye_out(): fttrx1 not moved.") - return + raise FlomniOpticsError("Aborting feye_out(): fttrx1 not moved.") else: fttrx_in = self._get_user_param_safe("feyex", "fttrx_in") umv(dev.fttrx1, fttrx_in) @@ -119,8 +120,7 @@ class FlomniOpticsMixin: "this could cause collisions when operating on the real " "beamline." ): - print("Aborting feye_in(): fttrx1 not moved.") - return + raise FlomniOpticsError("Aborting feye_in(): fttrx1 not moved.") else: fttrx_out = self._get_user_param_safe("feyex", "fttrx_out") umv(dev.fttrx1, fttrx_out) diff --git a/csaxs_bec/bec_ipython_client/plugins/omny/AI_docs/TODO_flomni_review_2026-10_findings.md b/csaxs_bec/bec_ipython_client/plugins/omny/AI_docs/TODO_flomni_review_2026-10_findings.md new file mode 100644 index 00000000..0b476053 --- /dev/null +++ b/csaxs_bec/bec_ipython_client/plugins/omny/AI_docs/TODO_flomni_review_2026-10_findings.md @@ -0,0 +1,48 @@ +# TODO: OMNY has bugs that were fixed in flomni (review 2026-10-24) + +Found while reviewing `flomni.py` and its mixins (branch `fzp-move-beyond`, csaxs_bec commits +`1a5b53c`, `b939793` and the follow-up). OMNY was not changed; port these on the OMNY branch. + +## 1. X-ray eye Ctrl-C handler cannot return osamx (`omny.py`, `start_x_ray_eye_alignment`) + +```python +osamx_in = self._get_user_param_safe(dev.osamx, "in") +if np.isclose(osamx_in, dev.osamx.readback.get(), 0.5): +``` + +- `_get_user_param_safe()` looks the device up with `dev[device]` and needs the **name** + (`"osamx"`). With the device object the lookup raises (`KeyError`) inside the + `except KeyboardInterrupt` handler, so osamx is never returned to its in position and the + user sees a KeyError instead of the interrupt. flomni fix: `self._get_user_param_safe("fsamx", "in")`. +- The third positional argument of `np.isclose` is **rtol**, not atol: `0.5` means 50 % of the + readback. Use `np.isclose(a, b, rtol=0, atol=0.5)` (mm). + +## 2. Special angles (`omny.py`, the per-angle loop of the tomo scan) + +```python +if self._current_special_angles: + next_special_angle = self._current_special_angles[0] + if np.isclose(angle, next_special_angle, atol=0.5): + self._current_special_angles.pop(0) + num_repeats = self.special_angle_repeats +else: + num_repeats = 1 +``` + +- `num_repeats` is unbound when special angles are set but the angle is not the next one -> + `UnboundLocalError` (flomni had the same bug, fixed earlier with `num_repeats = 1` up front). +- The angle is popped before the projection ran: a repeated attempt (timeout / beam check) + no longer sees it as special. +- `special_angle_tolerance` is never used (hard-coded 0.5); `__init__` sets it to 20 while + `set_special_angles()` defaults to 0.5; the `tolerance` docstring says "Number of repeats". +- Only the first list entry can match: a special angle that is never reached blocks all later ones. + +flomni now: match any remaining special angle within `special_angle_tolerance`, pop it only after +the projection succeeded, keep the remaining list across a resume in the same session +(`Flomni._tomo_scan_at_angle`, tests `tests/tests_bec_ipython_client/test_flomni_special_angles.py`). + +## Already shared (nothing to do for OMNY) + +- `OMNYTools.tweak_cursor()` (OMNY_shared) no longer ends with a traceback when an arrow-key + escape sequence arrives in two parts or ESC is pressed alone (`read_escape_suffix()`), and its + "not in enabled devices" message names the device. diff --git a/tests/tests_bec_ipython_client/test_flomni_review_fixes.py b/tests/tests_bec_ipython_client/test_flomni_review_fixes.py index c842b0b5..8e745039 100644 --- a/tests/tests_bec_ipython_client/test_flomni_review_fixes.py +++ b/tests/tests_bec_ipython_client/test_flomni_review_fixes.py @@ -177,3 +177,53 @@ def test_xray_eye_ctrl_c_returns_fsamx_in(monkeypatch): with pytest.raises(KeyboardInterrupt): flomni.start_x_ray_eye_alignment() assert moved == [(fsamx, -1.14)] + + +def test_tray_position_message_says_0_to_20(): + flomni = _make_flomni() + flomni.check_position_is_valid(20) + with pytest.raises(FlomniError, match="0-20"): + flomni.check_position_is_valid(21) + + +def test_get_val_reasks_angle_range_and_bad_numbers(monkeypatch, capsys): + answers = iter(["270", "abc", "360"]) + monkeypatch.setattr(builtins, "input", lambda prompt="": next(answers)) + assert Flomni._get_val("Angular range", 180, int, choices=(180, 360)) == 360 + out = capsys.readouterr().out + assert "one of 180, 360" in out and "Not a valid int" in out + + +def test_feye_out_raises_when_direct_beam_still_visible(monkeypatch): + import csaxs_bec.bec_ipython_client.plugins.flomni.flomni_optics_mixin as optics_module + + flomni = _make_flomni() + flomni.csaxs = None + flomni.foptics_in = lambda: None + flomni.flomnigui_show_xeyealign = lambda: None + flomni.xrayeye_update_frame = lambda: None + flomni.OMNYTools = types.SimpleNamespace(yesno=lambda *a, **k: False) + fake_dev = types.SimpleNamespace(fsh=types.SimpleNamespace(fshclose=lambda: None)) + monkeypatch.setattr(optics_module, "dev", fake_dev, raising=False) + monkeypatch.setitem(builtins.__dict__, "dev", fake_dev) + with pytest.raises(optics_module.FlomniOpticsError, match="direct beam"): + flomni.feye_out() + + +class _SlowStream: + """Non-blocking stdin stand-in: returns the queued chunks one per read, None in between.""" + + def __init__(self, chunks): + self.chunks = list(chunks) + + def read(self, n): + return self.chunks.pop(0) if self.chunks else None + + +def test_read_escape_suffix_waits_for_split_sequence(): + from csaxs_bec.bec_ipython_client.plugins.OMNY_shared.omny_general_tools import ( + read_escape_suffix, + ) + + assert read_escape_suffix(_SlowStream(["[", None, "A"])) == "[A" + assert read_escape_suffix(_SlowStream([])) is None # ESC alone diff --git a/tests/tests_bec_ipython_client/test_flomni_tomo_angles.py b/tests/tests_bec_ipython_client/test_flomni_tomo_angles.py index 71e6932c..414ff838 100644 --- a/tests/tests_bec_ipython_client/test_flomni_tomo_angles.py +++ b/tests/tests_bec_ipython_client/test_flomni_tomo_angles.py @@ -681,7 +681,11 @@ def test_new_scan_resets_progress_before_eye_optics_gate_declines(monkeypatch, t "must not acquire -- the gate declined" ) - flomni.tomo_scan() # returns early, no exception + from csaxs_bec.bec_ipython_client.plugins.flomni.flomni import FlomniError + + # raises since 2026-10-24 (a plain return let the tomo queue mark the job "done") + with pytest.raises(FlomniError, match="not in measurement condition"): + flomni.tomo_scan() assert flomni.progress["projection"] == 0 assert flomni.progress["total_projections"] == 1 @@ -765,3 +769,24 @@ def test_flomni_gui_progress_survives_zero_total(monkeypatch): assert ring_values == [0.0, 50.0] assert "Angles from file" in labels[0] + + +@pytest.mark.parametrize("zero_deg_reference", [False, True]) +def test_type1_new_scan_takes_two_zero_deg_projections_at_the_start(monkeypatch, zero_deg_reference): + """The fixed 0-deg shot + sub-tomogram 1's first projection (0 deg); the damage-check reference + before sub-tomogram 1 would have been a third one in a row (removed 2026-10-24).""" + flomni = make_flomni_for_golden_resume(monkeypatch, tomo_type=1) + flomni.tomo_angle_range = 180 + flomni.tomo_angle_stepsize = 45.0 + flomni.tomo_type1_always_forward = False + flomni.zero_deg_reference_at_each_subtomo = zero_deg_reference + calls = [] + flomni._tomo_scan_at_angle = lambda angle, subtomo, **kw: calls.append((float(angle), subtomo)) + + flomni.tomo_scan() + + assert calls[:2] == [(0.0, 1), (0.0, 1)] + assert calls[2][0] != 0.0 + # sub-tomogram 3 (forward, starts near 0) still gets its reference shot when enabled + sub3 = [c for c in calls if c[1] == 3] + assert (sub3[0][0] == 0.0) == zero_deg_reference -- 2.54.0 From 5937d930407967ecd9551639e6dacac3c2a36b99 Mon Sep 17 00:00:00 2001 From: x01dc Date: Mon, 26 Oct 2026 09:08:33 +0100 Subject: [PATCH 12/19] mod config files --- csaxs_bec/device_configs/ptycho_flomni.yaml | 68 +++++++++---------- .../simulated_bl_endstation_flomni.yaml | 4 +- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/csaxs_bec/device_configs/ptycho_flomni.yaml b/csaxs_bec/device_configs/ptycho_flomni.yaml index 677355a1..15ccd18e 100644 --- a/csaxs_bec/device_configs/ptycho_flomni.yaml +++ b/csaxs_bec/device_configs/ptycho_flomni.yaml @@ -728,37 +728,37 @@ calculated_signal: # deviceTags: # - ptycho_flomni -omny_panda_continuous: - readoutPriority: async - deviceClass: csaxs_bec.devices.panda_box.panda_box_omny.PandaBoxOMNY - deviceConfig: - host: omny-panda.psi.ch - raw_stream_mode: true - # Only actually Arm() PandA for scans that consume its data -- generic BEC staging - # (stage_all_devices/pre_scan_all_devices) otherwise arms this device for every scan type - # (grid_scan, line_scan, alignment moves, ...), not just the ptycho scan that reads it. See - # docs/developer/panda_box_free_running_setup.md and - # AI_docs/PANDA_POSITION_VALIDATION.md sec 7 (stale frame_counter exposure). - arm_scan_allowlist: - - flomni_fermat_scan - signal_alias: - FMC_IN.VAL1.Value: cap_voltage_fzp_y - FMC_IN.VAL2.Value: cap_voltage_fzp_x - INENC1.VAL.Value: interf_st_fzp_y - INENC2.VAL.Value: interf_st_fzp_x - INENC3.VAL.Value: interf_st_rotz - INENC4.VAL.Value: interf_st_rotx - PCAP.GATE_DURATION.Value: pcap_gate_duration_value - # TTLIN1 (detector trigger) feeds both blocks below - confirmed 2026-09-15 against the - # omny-panda hardware, see docs/developer/panda_box_free_running_setup.md. PCAP.BITS0 - # is a shared 32-bit word; PandaBoxOMNY masks it to bit 0 (TTLIN1.VAL) before - # publishing gate_detector_active. COUNTER1 counts TTLIN1's falling edge, i.e. one - # increment per completed exposure; it also doubles as the on_complete signal. Confirmed - # working end-to-end 2026-09-15; now under speed testing beyond the initial 100 Hz. - PCAP.BITS0.Value: gate_detector_active - COUNTER1.OUT.Value: frame_counter - enabled: true - readOnly: false - softwareTrigger: false - deviceTags: - - ptycho_flomni +# omny_panda_continuous: +# readoutPriority: async +# deviceClass: csaxs_bec.devices.panda_box.panda_box_omny.PandaBoxOMNY +# deviceConfig: +# host: omny-panda.psi.ch +# raw_stream_mode: true +# # Only actually Arm() PandA for scans that consume its data -- generic BEC staging +# # (stage_all_devices/pre_scan_all_devices) otherwise arms this device for every scan type +# # (grid_scan, line_scan, alignment moves, ...), not just the ptycho scan that reads it. See +# # docs/developer/panda_box_free_running_setup.md and +# # AI_docs/PANDA_POSITION_VALIDATION.md sec 7 (stale frame_counter exposure). +# arm_scan_allowlist: +# - flomni_fermat_scan +# signal_alias: +# FMC_IN.VAL1.Value: cap_voltage_fzp_y +# FMC_IN.VAL2.Value: cap_voltage_fzp_x +# INENC1.VAL.Value: interf_st_fzp_y +# INENC2.VAL.Value: interf_st_fzp_x +# INENC3.VAL.Value: interf_st_rotz +# INENC4.VAL.Value: interf_st_rotx +# PCAP.GATE_DURATION.Value: pcap_gate_duration_value +# # TTLIN1 (detector trigger) feeds both blocks below - confirmed 2026-09-15 against the +# # omny-panda hardware, see docs/developer/panda_box_free_running_setup.md. PCAP.BITS0 +# # is a shared 32-bit word; PandaBoxOMNY masks it to bit 0 (TTLIN1.VAL) before +# # publishing gate_detector_active. COUNTER1 counts TTLIN1's falling edge, i.e. one +# # increment per completed exposure; it also doubles as the on_complete signal. Confirmed +# # working end-to-end 2026-09-15; now under speed testing beyond the initial 100 Hz. +# PCAP.BITS0.Value: gate_detector_active +# COUNTER1.OUT.Value: frame_counter +# enabled: true +# readOnly: false +# softwareTrigger: false +# deviceTags: +# - ptycho_flomni diff --git a/csaxs_bec/device_configs/simulated_omny/simulated_bl_endstation_flomni.yaml b/csaxs_bec/device_configs/simulated_omny/simulated_bl_endstation_flomni.yaml index 6632fd0e..2019acf7 100644 --- a/csaxs_bec/device_configs/simulated_omny/simulated_bl_endstation_flomni.yaml +++ b/csaxs_bec/device_configs/simulated_omny/simulated_bl_endstation_flomni.yaml @@ -24,5 +24,5 @@ fsh: flomni: - !include ../ptycho_flomni.yaml -machine: - - !include ../machine.yml +# machine: +# - !include ../machine.yml -- 2.54.0 From 692ee478369b743c561e90af2f6eaa0ba92c75ee Mon Sep 17 00:00:00 2001 From: x01dc Date: Mon, 26 Oct 2026 09:27:16 +0100 Subject: [PATCH 13/19] token added to config file --- csaxs_bec/device_configs/ptycho_flomni.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csaxs_bec/device_configs/ptycho_flomni.yaml b/csaxs_bec/device_configs/ptycho_flomni.yaml index 15ccd18e..450b4222 100644 --- a/csaxs_bec/device_configs/ptycho_flomni.yaml +++ b/csaxs_bec/device_configs/ptycho_flomni.yaml @@ -628,7 +628,7 @@ flomni_loop_settings: deviceClass: csaxs_bec.devices.omny.flomni_loop_settings.FlomniLoopSettings deviceConfig: host: mpc2844.psi.ch - token: "" + token: "3CcGHMdz16G7zuC6iPHEoZPWvKDY1HkCZnTlP1msZbg" token_file: ~/.flomni_webhmi_token timeout: 2.0 enabled: true -- 2.54.0 From 81d9e9eec04951bfddcde4d666a69abcd8b418e2 Mon Sep 17 00:00:00 2001 From: Mirko Holler Date: Mon, 26 Oct 2026 10:43:30 +0100 Subject: [PATCH 14/19] docs(flomni): PDFs of the realtime_system guide and the FZP how-tos (for the web HMI tab Docs) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QnSJvqxh9oDbshx8qXgeiX --- ...t_only-howto_calibrate_the_fzp_scanner.pdf | Bin 31150 -> 88918 bytes ...only-howto_limit_the_fzp_scanner_range.pdf | Bin 20066 -> 57503 bytes .../local_contact_only-realtime_system.pdf | Bin 0 -> 57342 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.pdf diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.pdf b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.pdf index 81ce9c8a5479ba327965c1f1f11cb88a10381c23..b23a54db08bcd0a9636031d5bb23ea2c0b8428a5 100644 GIT binary patch literal 88918 zcmaHS1B@_Hv*mc^8{7WIwr$(CZQHhO+qP}nwz1#8dCA+|Y+llRtGe##RCT8--8ttr zv7C?yH9ZXzB=O?q`935)J{`WTfdwQN7p6uyn&HQh)xvh6F*J7xTPY+2eAdOEeW9wk8Z}s0Gw*Ll+m|HphD~(pf>R$~) z#)h^=#*nm9#x|x-X825uZ1g-l_>NBhF3K9xE$do)G6riDW&4rRA{||w$0_KnE*=8ow?|%*D^khp$201hKD%Cjb^oWd zG}q_Xe(@YV5%cF2kGJem*XO6s{W(w(?`Lg`*G8xNal+&I?Q-PG)KW=hreZWUb5� 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 PbFwf%laUF_ia`G#oS6$x literal 31150 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 P$-@FiK_Q_a3HQGMWG+-C diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_limit_the_fzp_scanner_range.pdf b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_limit_the_fzp_scanner_range.pdf index 66e18ac1f531e6555db57952dcbde5a96f8c3661..2c8a51c7e5a7a8d5b867c7922f15993f67922efc 100644 GIT binary patch literal 57503 zcmaHy1CS(L)2PR`ZQC|?&@;1R+qSi1&+gc^ZF9%AZQFO=@4gY=f8&q16_rt0dGgfB z%IZ3uRsA%%f|vw7GXon8`P|vb4h%C96Oo;vB@7=Qqo|{afwP?>5tXR9qn(X~iw!jl zqk^NIv5S$(w^7N;!hrF+FOdy1JI8m+f4nX1Y{d+mO^B$(cvzTNIGDeMiJO(3nS+*z ziSm2>_5;`%|NmVC1Yk^TjsJ;(^}n$g84xkUFp2<(80G97Z49jc>%`3TUoQy@Yv=FS z7$vN~gAg+@vNJY;VU#toHFGv6;$mfG5)dGAa{fLm8yNSD3muK>!yyNs?(Trt@Lvj@ zDvbU_`j>Q`w}J7%@78~l;(Aa>R)BBs0uL7#$fjx{3A?X0v7>ip)(!OPl1~fUPUlNE zm)YK{l`>C1E6u*5ChwVD1>fKA&*C5Fy+kwRET7lRlRf2UoK@53Da(DH*0!=f-$O5x zi}kXttKs1Z@%%KqKl-QSJFkYmzTTcTZtv^_`F-v>FMMBzmCoscCn4uf-Zu!dJr8`2 zpZ)G4(?h#I?(g4kPSO#pSgYQi_u>iK3)`19=6v2?PP7Wg>lKX?@;Ifb%h7^wwqDGh zgsGoYsTJq8a^GXTR7VGIwkW7gwx~@mlrIwMgEOnc%^j+Q7^HHkN+D} zbPNg0j5S#ez50-o(zXI{S%3JMe>Nd zc@2;p#Jq3-evsW?aPTdmK&}gdxGYNL7hcuhA~fJh=+F93XWQWWy~TKR_Qy(Xg*mUv zVjn~ff+dBmD)=R>m+&+~6ALAQ>IA^(1x>6o;7hppFOz(YhF&nedm|w)?vAU9_GeNZ z{A`STAjx-2!TW%`2bxA71&TqW#efxp&+BNZA>I#rr*iKa=DL$xU~d>VCfN_BkUSMn zUY$6~YCfxl4)Fo3zp3``)au-qFD>3a*%<&Dj{E-3Y)zAlg%S1={<-tbpF<+$hM4_q z9<^x@;!q20TpoX>9q{dka+w`~J94+=_b%fc7uVTp8MHIa)3y=v42CCyozQIIe-P9V zMilv)muEbRH@Kclo`U{as8F%L6@lJ8ajF4@+RwG9;w#wpIcaSw0qXjr@GFfbmPTW5 ztj3D63eQ)1JytqkvEkguyBuNYLY_+($c#53#~U|O*|74pAVGdWl~S)M=^h^SY=~bp zcO$dGq9m{yZ}9c!Mv2dIHEB`OPH<&e6U1h0ek~7^az%UT#ho+i*WOz3@|xJ{iDEi? zeS(f+Z_UMejEu;nlGPt+a5d@#p~4oi{)gT(quKFvaz;SsCUHTy8izqAihP z(<LVp87$!%u6lZ#xT3g^~c#oTaIw~Yy?Fk4cH@d*T!x%U>#M`8bZaAX#V;j zB1L_L*XT!Nkqm=gNmWz6_!4h6qm?+zLhyrxR**P&34(Q`5cqV_Y}uLFotI|xFV8X8 zIJA!&?S53S({e+=$H0P=_t!vn0H#t&QXj~28Kn8CqY;?$&du+zE8*LjK~@KzFxl)p z_B2g8f_i>CpQ~X`YjtQ}aH)d0Hd3Z*Y0mr3S)k{(K~O{&my%<4m(E2BkEPZC`lb9E zcJU6A5j)anrFXQbGL!Hg2T7a)?{4Kr-Q8(uAv7XmXY9|D7LA8eH4g4W#dL6I#c%ecEaUlz5~aPM_wj6ac3%kyH1vDVxR2+Z%o1( zg(EHl0nz+O_OLTF0&C!4mf;XN0d+LDN$S17n&`2`BhYVWq;xs$O|k=*XW(qcZmh<)VNJqB7bN6cyOQk_@ZC$W55 z=fuk8LI)MdAQEWy(LH_&QClwr`zx;BQ3zSYTR}g8=5-D-*~86*=$jvRiDWUYBOddj zaxM`ku-R4#HX`MpIA1S$G4f_&G-6M>&wFw8VW49vha$I27#u**g9i>+&Ad16HX_hA zo8$M@dJY`#`sQTLlXz?guwK4SQf;ibf6Sor<{xd-MtfHOsgXOUo+d<$<$s1)Hys=v z3^rrnL%*J(K|~M%f94Bv`5VZ!UA&(L6r?Ex1hp!^Z;$XE!liqvD`|zkF@sj#3p)>a zU0T+vf)omwO`;4Llj&hw+VP4O)ggXJfbG6W&HSgG$(e$nCb+fp^`4xH-N%KSrhjRVwo5)#!*}0RQ5^m1w#V_qv7;+sT z4J^6}F>hWbiZVo{ra9*%b6{i8SAwvNSb-<(a=MOzTN3FUi_h`OIKB$H#nn0W!z5AL z?3_Qy1e5OIYPpJjt7Z@ve-UnQ6nhaVhXnQtu?FF$cwbe3RP|LJN_t&8(o=vvc_(4d zlYYiqt05Jdx}G*T9Gn^rf(+mhoBb*GqMPYWK+MB!SOAbK<;*k8SG)_P(JZRs>~FNYkfju*=HAbeJU9|{R*EU{pEpKm?#&LsFM zF-4%nJ&Ha>Za~GdftexW+7=@C;e;X9z4z3~6ec%rdJVN19txE4XILvH?hh-)-&$Tu zpd&{x&9M&0QFbBpgXeim3J!4mEr=Iap4Jdfq~&6fi}p1`*-H3Kc(-ttMpq~y=7FbG z$dU~(_SUtON>{W@BVDos&u$!0yjjB(ZMQsTu>cpIm;m@>*u|6iZh!TeVqKAnKM=Z; z(F!Ic5f(^)y0WNe$T!Tg#3)G2CkmSc;$c4aikY!ps+mcQikO~fsoh)63CMB1q#ve@ z%wPHla(#$4>@UG!H+zsRsBSeRuOSZ2Wa3S$TyoYsjXGuV&I28q6}p3zbs)=r_LKKg zYk_`VmSJQa^Ul^Y7%#^SL;U4_tbzmn4LVUBANro=My@qNTIGis2c3hEMU=lC_=8Rk z0h00tAlkR)+(6E&!=o7d=y;mZgr8KAFohDLr;?117N|F<;kZJ8%!ro~zc?<-tF8J= zd{s?b6+2H)nrkJFH9}n5fq~X;8G5X>!aSK=C{IzzEdHdSlVJC#fzbKXaaZUI{}2#NE3}rZ6&Nz~GVjdaOraQ6k|7#4L$V$?fiNhsNpB@^J_rd> zSEQ?!LLO0-4xi9@b#73YMY~*y^R<2S_HlSOF>Q#6hHD&Wk?5|NY3LB3@cs(|QnBZd z=f~TFFr*vejIb26@@u`n6ZZrAgZLfxR-SME8uUjqed!@7zGa$=-+$Sp2ZmCqx$WiEKrMIcwwh(3mN44F;j#$Iyy)OBf{3$BhpJ@ZKE@gqB1*|)gjqtLX z@HEsn(}8Ah_v(I3o10F#%h92Xq1(3;HO1LvGIE=lcZ-;~_%}J+RQ&SX(6eifxU=d3 zZ^%G!>RjX>pi}N#_&G0kJ>at}fVn)GSReV$iBfeesb$gG))7>wJo!$l$(PTp0<{Rq zbR`1a{SAQ#dMES#!7*wVaKkv-cn1EM0q5Y>L^uQKbvj-0x_aBTD&TtG?8nswdp zi|Zl!Jqd>MqWeM)AO<5F+Z~pnBsCD0;e0d zNt0PlgnY#$u(PwpF+pao>D*bm-iW*LGC0wAS<2KjSG;@AI%j)dc2b!)5%ru`u`5H( zQKV@$zItCyqNbzeAfME!7)x3N=V zW4LCn?Igx6@+_*Y@1!u2*n!LB*jtNGwoe-Cw;tq3INCBZVSe&3U%UZ;cwEitZ1+|im^Zf z+>NfB67-zCf8 zW+3DusBxz)y^-$$yCbHn00p7i2eYWRUrmO4^*u=VS8>EN{mJFE&3#V}4eXYZ?Xvd! zgQ;hE2%1U=BSw@|Ft{T``Pd0D&iUpGo-@a-&yMt$<)T`B%CVByJ!OscW=W`<_a+7pbU%7N+NN_E1J zKo#27Y5gR4fmvX>SJi6dBPtd%SoJ5tA~q<6HO%3P%V}cNXhu5BP(Sf10DscOsBHmx zXiu_vXw4KqBl?GF?=S{)v5Ppu74dMaTci?Z^@a15aYgU;25(h2X(@PgMVSv1o6z$0 zES#7TZ#U6J4Y5NF?dbMY=;FPNBjM}+_E?LvCoGvN+=|-aHwVUTtqPA^tq6y6xGLnW zAI-G2Iw#!53M_o@^%j}8;wP9}L6Wc52BO$pBL3xh05(+ zsL^h$B?v=S$Q(6L`t8~Kk)W$$0hO!Z3fkod7G>z_Tt&ZjHm+Oaw#8=zc(H@DEv)Oq z!JqXtK|z%*-ZsQI;z2{C2muguw+1=Qxyx?NSz!Ax4{B!AJlCE@dVW^l&CZ7eCn+Vd z>Db{q4X83lKovR}BoLW~qJjFx)ccucZip>9rI-@zR$T6$bl^h3EX`oGrLZqCk*3_3 zV6*%giH(3H5~}@>vK>oW^S{v_;kG}x;Q!NuLojHw7`bnGChmy)En45=&mH!IAj06krw`hr}v3P}588?MhooAEc zo(gk_(#o2Q{4llpmW0AmdhGr^7WDDOxLzy$IV%g;^bpg?MJSj@Kt7-sp2h&e zqyn7Q!D&pCYf8-6tl44HY5w6*2^Aux?r87(Q4Pg`K>FqU7g{LgxIv@Cm@ts0a;Kxf zP-pmY=4{9u!30G5v1rr@2U7sRHbb@8=iFX(Wgl`MC|iXS)s@S}lXthob~Q4SAbC#b z_{K;+*Or8%c9gOqUEV?!5Wg{BC0n~5EQCri& zh`dlYpoYHA)-Fj~zYgX>r$fARGjUe?v^dk3J@607qv%6#gJjKNMjB&L!GM;vNsibh z!K+#IpTh9vizX!(JPxiLf{shxxVYMv-`_IviHCwmcGQNpgJe$WoMnnK74e zE{SN`6@a# zKwmr@^fGd*P$WS|Eef~s65lT6?KMZZb@uxs6R~b=M79a;T_+#!!{B%+i;U8@+ToQM zis`;suFbu9u^7Ryp9)(FdCPN&IwDWp?TZ+4&YX-b!yxaURw* z7ffZik$EAA*?v!?hhnO`j75}DJjjtO$UsdpW~epSmN(Pmv5~100Hjff9l}{E59!pJj>|G{KFnX|PxSX;?C1{xiRkYBQq=kqpg|wo(7AvIxccE;VsuLThnR|uf&Zp*7{%2G&P)PfB{JY; z1!E>5yXqf>hl5sdjVSH5pFMBnXenfsMr`i6WgM(qm`#PfX61mhR-6o$ z3FIATW*{`Nvee}LBPOGdYyi%jXVbDDg#y8e_A@56=|HyHoA)R*(u zksm!Y-%|A4qX*u8Tu=Nv;4~;>lB{vz@aF^G=KFwgJCpoEp-Glp8*A8cEIb33swUuyJUziQ|;YPmsMT)24mT@IR33)|hI;tZiu)9iaTa%n9k`WJ9` zug4%doaZ9KZ*J1UBLIefqT+*3iXftA4s4oB%V(pkRgIjq6N%;AfTYhYG{TKR_ivne!_|w-VQmqt=$I-fyC$2w4F7y2nM^ z<4GHC3+t9+`xiEAEa9Cq4JNAf$x9A<=an%9sP0>9LT&HwAp}C+0izeluDV&HWDF0e z5e7yDPz$z>{#P)T?B#)X~zvwxyln29)Z2|d%OyBYWdOlOKrkV+x2*%tW*W@e)aY&4+jCon zQA1}n2U2EYqqp6x!ibq;iyZ?#IVNG#`*FLY1JoxDXDX>F>H*GzpnLS`nFY-$s1+D> zR}cS}%RSNSTzeR|I%FKO>9lBd1n5t86|2y`nN@;=Z<8?gQRugFZJCf?rZBX(#{xSK zVZygiGYZtZ+DDPIYV2R)Ied7}f?$2gxHpIqzDX=+OmfYe2NRE%Za;(#D(K-y?2NI= zy`Lq@t$rZ(W7yy`-Su;I30sueShML+nTXnsj*hhx*Uno_j^jBRGVahl3qTWI8Y?YA1W~>gNMhjt zb-L(G11SH}Lf@D^bjy%&<@$nXJgGI&5hZJsyWFQj(=E*dz_!n=x11T0QIYBHNyxV! z?d*u=oXrZy4%e!|5P?~v^mCCc{IXY$UBPL%P3bouj1h|gaKfMqmGW{g(CmQ9+Y+J6 zYn1d~wm+ymft#0uEi&bftz#kl*$kKnkVzr8nCgY%RDPK|Im*RAFNi6^JlNLWzCiIe z{Q+&FSBc|-K!*IYLEXFZJ$GD)xEYigK_ic>!0KdXMCu(USVDF4Id;hnT_#u>xk+n% z3+}h@j^q3+g|Ci)^kz2@qz#4Jnc)@N;+UErGnS<7~4>Vmcmy4PfVYbhv!hJL$ z(fQ&Vjf#d*4o(l#LbnbQfw$fO6OvTlC5Zh&pqy7Q&%rEq;i~=63H7$-EupzXHbmoL z7MCAL?OpJ|mv|8A>>%IsKI1@0MC#JwKtAv}KzqaE9<+7p5(4qWvm`RRYGXJCe+9C2 zG^^Uh+nlf?lNP=rSpH2gCZu@Wl2{4>BhuZSD?OE*m1e7IT zsmv}GhsbA|Gq6?sDDK;bm+yq;z>-VQ{|W3(S_{%eEMMh*I$}xKH&otz2T`q!TmM#? z(+6Qya~A>$K9^LRsAR>Y!XX!ZU0>&t(TlKhj_lAhCKpuRE4r8lU9GYj#)?D|!WQ)` z3}=y&1#i&D=D7#K6itPhqIJKDz z0wyxhk8p$zR@f^=KiQ?*NW;9{2}O(5`d7+t>8tbe#SkL@T0`gcXGj(c$(qL5`c_Hdxhx@wG7OPh-e@>n*t?95{M{p$DBnrAn5 zi=-F@m2<{lR)4}w+2KzjE6>g>J~KmCmMXgJnWcCYUfkk=8CluuFsli3i7L05i-)(# zT#m_5hk{F_jf8lmp9u7uD-QCp$^}^>6#}ny_dUt&z$Om7w(Gz1S_N!}O*-%~HuShR0^-wwx`5M=b>C*XpDvvzQcO770Wo78(TRb;B1Ga}@p|@=hsy@cewJ#Is zE`p}3jrQYFJM>0$eQoW4aLjm8LZq*-LrVeZTWfN!4`cGBH$07>uV7cjj)-)* zmGfa6cm-?wMhRC7`YZa1o(pc0;kvH)Bq*XS2Wrf%zo4#emXOnT;w2Gu6XP-T36Qug zGCMjc>$t}#^WRxQ*bJk7AH0>vdbxKA@_(G1EERXW0w@AT|CAO0H9*?te6a z@8uinFfq0;5V3P7()x$SVCQCM;AZCL{&r?#Vc_EWjzIK#AQM|>C!+u08sFn68yGqf zF)Do%B?gY)4MfcUfG7XN^luXWv;QxM^4}~t{c|Ljf81dhWh{)Hh_wDmmZHx8==Z;} zCu-nqU~Omi&q!w9$@^ae{SV6I|DqzyL~JZ999-YI`2XP~->3Kg;3VGA?#e1nmzrLg zKHq3atD=gONd<|rA(p5Jh{11^RN=5NHYFk<6cCg!Ffp9p)4FD@aS}{$97d#u15nz= zP;8bNNSYlKt6^N)ISy17445+rBa;_SG(?^kZf`zY?OR@3Zl5Qkfg`@MQHtfG*Z~j2`pirsOkzfY~R26jadBXKA86zn>WaaX|w)%E&|( zuj)xdf#}Nvnd@nCeEE9)5`@-2C-eU!X*QK9@GzhIBoE~L0!-wixibGY_OUgR3A_yn z1T~(jsft(DdI1cS2JfHeZ9DkBIL8DGwAb)w)RO{HfaV z{s(G#TC<$P{R@I3+Q($-y3oPH-qT&08@F7F8IUD^yKZws+5#}-_KG(>5QSvyJ#qpQ zCr~|=SlvKM0Y7e6>BM{7FL5OQ9(D?^AjqZE(Q9jBeJiVS*C+J&adJbbOpr-U3>-~m z+%6~W?Tee<$xL5Y9Q!no#@~8YT4>8#t7(FA(%XAh9)|S637XUiuY+6dFXBgX{ddFQ zk9+i>kPRxlHTFW^hASMkF_G03|C?U@BRNpWkoFxlCg7qTS*p;N z8YB!Iqt|0ldsydyDjRvkAYvvEiT;$P;^IDjCdA7~fep_`!u%FYKZd(642qPTXbBcj zeTs2ATGD5IM=g!H=~^3;0HFP*#KQDZyj`N!N;**%AUJ=+6mTU2Iy(P$eVXxKFvoBo z1`OjNBv`P6Oo%k!-GjN2{OOKCR;KqT?Z9n2Fk5amANCjp-FU}?9o50^zCHXEQ>yRW zd5n+gjYTMoMS+TXNjis8nFwk01;zsnQ(#Qm&_et0QV?{AfSP-)Q{aKIeDbScJ}R(i z?z^@i2Nl3{ggziOCpu?Nu~J~3gt5nGhxtXXhLx#4wybZ8^Rp2OU!Tp61;hM7KI>SLC|$t!*wrAcN!Lk9AL5`nWgpaJ@!{{n1*c?y zuN4Y99Sgb)?XGg=C#W6T`O^NbV;&?i9*R@)D_C~0`I^Ob(4hraX@^F85bZpIn+}q3 z7koU#(mi{~0e!Jo={Ur)BoNh}V6xw&xR)1q?ZwqdLdU_A3=*qL_=(4bJ0>{DGNkS=ueo#%-4k_RuV)$ zsM9GdPA`c!(H|T9z*zt+9xZU zr%{mi@OWghAuw2W{CZk})((B)5zO+Z+_$w?iQ7Sm?h&yF9b$&MQA1`j$DwF9fJb2E zhvEe)fuw^#1nLOv(Thd_p>ag1_%nOrh9o?cq&s@A(Xd*kvUEWh%3-Hart0hO%wElPwoy06pP;+;m2`IRp!_}!VKp)bB?rbw^8 z_vi2F2n;Ml?QT)?VCmn}0W<}`OIJvmL~wDw!P6DprN>^V>JOcdAsuh24DU@?2@ehq zK8z>we*{R1^n`s7cNPohJ!zqZCzg0_Oof+te#Yl?e=?Yzs2{E2lWH%| z9>PapndtX+0bNM$%Xxy7V)Pma3_A!Cd^EFz$R7R zYoeSk@=OVzjdX__2t9kiFRaoo1ft=1}Ghv*QbEb%fa4SI;x7^#? z2=(LAg7!;?grC;D|5emGFz4Y6=J{2$p-WNoNja>VHpj)lqB7^ytnJ!Tc*;4KT}7aa zf_q)MJiF<30oZd@y1dNWd%^zICNcI)RbfGzvif#Lnvx$q&kv(PWI8?|3+cWkXBdqC z^+%W>&G8>3e!62~;jaX7#H!paC{2cvS@|(#PoZf=VHq%b+dMta84_XGtF=i?m^M3J z`jqS*OtzVKurl*J51A8P;R7e$o7PNoIn+eK53)HS{7;2O0b>Yr#LiXn2h(cXo(cCG)QdFI^J{?CsnS%#A;? zq#<6

k?|hr9j!Y}O^2h)y#Kyzmxzy@7n*HOUYj=K84cEgO@NT*t1w58dCOUAwggiw@7IN}e z55+JPd}h8G`S3U7J{84)=-rQgk9Wn}{K&UZrHmYP%$E-0ODht_wiZsYERqO^KU%+G zb=JxMYKS4U$MYKgbx^M39nqy;nI5zO7*CKyTJB*GYS54`NLZ?~z$${Jf{kb&)Dcyo zAj&AB{zPVWcA$Xj9hwo>CtZ&fN386L`SJt$v~%0`Lz2>zRV7*cLGF!lK?SWAiCQ3sq)z8FzUE)p#z|8DtOFJ8Ddid*tBd;RZfO8cH@{g6R%RkjX#?_ znVbz2XkO}6C*kLo@hybeDeMU#4VC1y~QJ6XCH}o0TEA8~JWWO88{MyldUq z$=KwmI3*b9_Uu!G)nOcYy(zs0A8c}D{;rXOG~OUnkr7Ua2H4(YHl|+0ot|&F!}qU{ z{VM9e?i>=S=3c#l{bf3km0QTq3w2P|2GvsZlQqKQ_n>#VG&A5I{&-em>X2%vIX^(P zwNw#SZIk1XHtmXK1R7O|D-?2u<|M& zB~-cPPJ8IQ zHF-Vw%)fpAHk%{cY0@^M*7eLuYssec^=O!oii^c#^QfK~ zJup^@>SJLcTNf&riL`mR;)I;lFGZcn)iD$<3K8tn{=WCfzn9FXuoxL@dHc~GD3GI0 z`1fB7kh&CChXQodhB72Z+PDQHHxADG(pcjY67HpG%D4kt)UbOSWgJsD#p%Qg=>@cb zL54`^7XB#q=;+;(Bn?HEF{OrbkA`PyZ4pWYP>3uB zRbH!An;#Vj8WT6ZFoOecMoO!p4cHEo9<{nhX+sT>gqCzxlwSN@RFTsQ;OrdVmqTOw zK|7m5L%_AG<$Eb;!MaMXBkevcssCyVKbm3OL=y~S-Br={2Q}*$>T<7vKMx9XzI+aC zv)vd-R7xH&d&Zg^@Oah+f=X)2I)f={ z#qESam%{4STNjl%hS2>o^v8skl?PAvK#x5L^bvUPI`7S!)#+Od3w>AeHeF0|=mdcdenuoR6^j0fPR>dCq zT%=tXW&@KYoeQh($(#Y$JRv%qtJ@`OUnl4R<+qZGFeteeMelOY0XuiD|pA)N@0KM*c|<5Psj+0iy6u5ql+XGeo2 z*Jrh8(^+|FSydg-CoRqYp||uXupz6czU1cV1gU1JL?2n<>sVwqb+2?9A8>#KB3gIq zunl5$$c_*75TNY*2=vQ-7t9!*U6!n5#D7s zx)-eePs^BB!{<#~w@_L$z<=R|kS^A-}p-YCcsr;cGyyc3d&8*q~`e`iwi4s z#K@r|!!Gq#p=0g=(PjLFFNR%PqDgv4-A_8}wsZhfVofTA%&#Ke5-I7cM~0*T6GP`Q zOwU;1fT9=_wlgam@=CLVxF#$TT0GJ#$9{%02sf9E*7|%%mq*uk4LIZ`e?g9+ldIFK zqa$_yi-u`BUYW>}V^6*vN07w%u1C#_vFgr8mL$VQD)a@z%9LMLg-Utcx6$SjC+Seq zShNuXmbDUSI}jPcPumdsJYNKJq!C!h7>^omsdVl7!Q4ZpB7-X$byNaG^3E`=x3|uh zCkBJI*LfV)6Z=xV$~MO~NmViQ{bc79o2X{N>LFveV}2iJj+@K351jP$fLUbb!io}e z*9H@8cT%d~P_aSvD7zYYf+Lt`kunj{55eyO>=3T02Re)BwPlPHF&W{$*ktM9-nDY| zBWAe;B_m-iw}yAC2^wb_S-e*iE{B@y-+ynoY!L&*E1~S7yx-hFL*InAZfb7{T?uWe zU#aW)I@UHXtEHS%^>BP$mx~PJeYf#3|_g`orw0?!XXFH6a#ae$nZLZWD^)FT&cvvDlTA!0`yW zS^u@B%lNfsyDdkHEyw-wg)|v0s$1xUU>bQMCQPzAMMl~~JBQ+GrG?*_#wU**7^ZQk zkq$<%R8Q<``9w`$1WT)(x$+{q&8*oa>~O-cbyWE3Vu_z4_?mJlLNcT0`Hgss zb_WGQgy=5&=5oh-&rmd^xyGI2O&eE8>oO+ND+pCN8*Grtj)7yG6!Q&PIBkT~L}Lj` z@_1{8Op>;Q>;!wcSQI&OeE@nqPz7-!YaDxjfjWJeM!2sN2WR>hVksen~UnTsC3$bLlY1ds~)wJWHb~prV>~I zsnSWU?P*pgW`xp#1;pMt9x39m29!0>&dtu8g!Hi-(W(?FS50W@M}6Agm6t-PvL%hG z^u$w%6CG=%QzfiLu4DI$z}Uy4B;fnr)zCA5w8(DA?ykVAMd-FAn!uWO^SRMAhEo6K z4|fy>F!Xilg(8I#HYcRNRn9)yW@%v9eX`AJMd!-8wcQ<>H>$FvEf`YF zn6R$D2N0(hyQS50EBcYgB_2eiRhk369!07#9-ho-o4SiF-FN-!>h(Qw1G|L60D}o| z0&xmJ1S5b-Di*|cXL+{XUfGWPg&7zM8XCA+u|J-$7T-_pMt)Q*E6*V7LDkA)qH;T$ zKtGbM=t{X%#0ATCw~m4QIQW}3#b|im)k0G}rfDcUi`H41=Ofr>Xq2*8OJJzzee@w5;WIGoZ77fQPQN#%Qhxf68wC zna?Uil}HzvK2N+uk;x|Ve8#wJgho;m&i5EOCUkF^XeD+HXMTdg!s(n@&84HX8=mhQ z3;BL6LbH#rOm{;?TOMIEFpXy3j0A$EX^%5M5Gd}*9;kztYtY2sF>cKuxIXC69aI-n2 zOtgGsUY90-wx+||+M3ncODv*K8_jNaWZ30! z*A{>1eDP{B??`VpM_#CP@Qb!i6@U&UA>Nq|ICNvXw9rznZl$r%b3^$qERj0=MD834 zf7vzcN;=?f3|B0KL+ci~+c5dB;B}%td@gJygqUZvrzE#FTPb#vRmN}TWchCnGyq+f z96B_<{I=L9dSPSB+-T%uZB zD(PLtO!ib9hTY{g?Iwg9=vtI1s{?x~t&K=wJvfwBoo8IOt~BgUAp0=My#K6N>wJ3- ztvgC50F`JGxuc*F!+IH&Y11AgvQ5SiCRqpR@3#m?0J(C|3-rt9wWxn6cuBF5KQ(F-6S>F%-KF* z;=qA4u)Ln(vi4)M$#i4LrhLg7GzbUiGKn7vmV1XQ9jqkFHT#~x$+nTSzE?ka;?Umm zmw_|IQ@A#F@vvd47wkvkXNl)wOIvZFq*=_Ql#R$+i8Z*<6ZAf%zklKEiqQK@jN{5{| zIFzoxH*!`CB-g|>%v5%_i9biqZiu$}a}JE21?Pn}8TQztT0j0X4!z;m97^=I_Rnr# z1`bA6?PJRB{UhJjW(z8Kc0dk@M}|d&1;fPl*s;KgfJg1gPtPBoL4pRWKgwTlJUUN+ z2H#R*pA;c#dc9LvlF+GG)rn~jVKunhV;O%E7{uOECT!K2J$nw851&?*pe}et|o+eA&L5_x^% z%$QEd0nO=JFbCrco0&svS;|K3@$fd=3miJC$4^m)3>i(AIwt1gejnxdGrI6YFNN8U(}t10-Mz_# z4%bj7O3!^NCYqkGo*44V<8viRNpYu%I{3g|$E2q=?aF2GZR_~_wd{-dym9kpk(^?Z~X* z#?(Cm<;SsE%u4x<(rP@~))vZQ=LA4^#p7~)N(XQZLA(0yFb=>sv5ltWY~8m(kHl2_ zo@g5iO{yzw+ee6%5GN$hvLuqx#R~?v0|*SV$83`TLX!0IYq6o*HSrJtRc2NesfgqMcZtUjE{=Yd|saDClq%+xd#W>@lzK( zRnsWwL!6(<@R~GIH{#MW;>Jzl4G-he(&OUr6zNB(-9dLDKho+g;9_UBP z$jN)b+{mMvykuW4E?dOwH{ky`rPbPM1xGXV`nt{Fzs-r8dTC7-t2boW)vZ{oPGCeX z7_#!ZTXwK-V%WtxIV-lMw3q16=~U=gWB=kSY;J8{U7H2#t;1FxG&D7Zww7!5i8?AG zFWqTmusK@%sMC2Gdk}m*c>S9wkft#sE3GYfjG6yI6^17lN}GM4)YbeOUC}uwt@P>H zGoN;ufnL&Si6JTaDWcdmvq3Mz+}-2J_LKKUlQwMnFG1zCBWRXHj}|a7xc^(B$nTv% zdmaltOm!5ND*Uh!A}fzvJVUT-hg2&o6_k|EqzLOEG=1>@L)=>c#kFnyzKsNTC%C&b z?(PJ43GVJ1+zIaPkYK?fxO)f|+$Bf|PLN=CvG>{g?C+lM-gn=vSM{K(YchM<7;~)I zP51vd)~~v0RD=M_RD^!{Mnf=fX7Vd-YQ%=Rs!)ykR6EQ{`{LrCwEUxr^^@*x1I%Dy z*l;dYDwY8w@KM>_a0YPHa&(-G_gMs>>cb`Ios%hU=6;-ELOs$|;d+`{8&?a~e3Pjs7C z-vY(wHM_e8!!Kz~O(D;iQ5X50HX;+oyaH-ORUZb_zAgP=)379|`QG$H#UrJ=CQn`1 zGXJvsW1Y|*S^Vyv;*zxsy+ecaFP)iM!J`rOT@*QKS6}&&<+(g4-o@Nsd!4%mWyEqy zw!-&DI0|vF4B}Ng)>RyunO4N6_>Q?IEXH9o90s(wSqQ1*O;h!Osq)wz`pn#^43~2* z;}o|aAC$&S_ac~-wDaqQtE*Z*H9A^O&UzerKly(5zVsMZJQ5NhJyk=|-Wc%Vqwb{O zu2;KCyaEf?yfc4l_-=b7@m3rD<70ops}OM~)ivXv+MakcQEDc@_Z)RHfM2M~B~2z4 zxX^8J0yl9(KI_p#!|sJUdQt|3$m3MT{#;(D%V%hSBFtJOBx@hbA^a9QHVo~XgVH`& zWtC;0*5m!w$w9#>kL1+{1|PRvDS4IWPiH2VZ=#u-?1kz5Grl*NaODerp*>9D>k${?FS!;4< ze9ao5?f>oi<0F1^cunHM^8B)AZEmBQ&-zoxj$v)wwS3c_?l06dDBwewmZkP)&*M`b zA+vp*^;me5;e&`e|AS$SpD}?{wfI&+FTg% zQFxMl(d0VKOG=+`8uoEJIM3Fqj$rcEyBPxQx8!SNd3RmZc4KZrafZDuMGI5UTK+}X zpS%}I={`^76Gfbj;p@fEQ!*|xm8Ec#TbH*2{y~Nbf-YS-j$>2S>&QNfSGC*9)O6ii z%-OKnm0vb0JHH+NaQlV)YlG2KH?KLTzD#ende$CAue|!3NAFYGBh#=W659xA$p87n zK|stpOoF1w=r7@Sqw9Bu&&F?-A(|SYn%8)=3S*@D^&i*ZKsCYE9SF>4^oxC!n_z>| z$5ZLY-}TKqs*jy^kEgIt;+u$tiYtxv9RxqL)f%fdF2!>X>AzVoe8*Jon`{(JM;E+! zRWLSQWd0rM`1xydrziPxO*YxvZY{{2ThWh+aR=5Z}-QGP7in5kvM0LSlwMM=cV#>3182za;x>DgN5UZ0!-E1(i)JVd6UwL0=ak0fkZt@ zE@tK~)()1Ww10I2D%ISbob1f)Uz`eL(?MX-dQp7oaj~+#7|->h7h@%5=ip%mb8v97 zzkLx~f!RnoIJp5gf!R1o!JMRe?A*M7CX9^-kXilhKA;m9;5Bv*b{?P;FDV;0DHk9j z<6&jx<{@R{1v>HaGV`(m4c?M+umT-Pxp>%_!K|Dd+@u`rq9C zZeX@Qmcqr(&CK@lCn+y59I!RO*#B z0Mq{62?*_fE%)Dx0Q|-aIF0MYO)nBXHZTVdp!)-s@s}-JoM2`i&KIZhvath8=lLUQ z|E_&Y$_Y$Ps>cmX$OdNR<#-E(xP%r^`w|O(1@?;%{}tH(I{_WAQUBD>y-gZ-2w_E& zym>-k$(9VYCq;!PqbiYM{H<&)6xc&4Ne_c>nb=*EL4Z+?81t2ty~{I$lmR8oz7t94 ztzr7^_tvJ^;=1m(tlcQ)YV75avg*g&+p2aOG*L~$v6X{zhM%ptvgGDxlIb2-C)9=> z3IRo7a6PRI?1T8TKH1R{5$*3V+O@(5*EOsK-@nTW#t1Wk z5~a7CLFh%Icg{ATh?Jjzf%a61+4TRkL+bPT3JIJFe4g`50G11*SCiwzo0?RqGi*9q z!((IKyomm<;5sk<$;gn(d(fuWZ|B)y!RDeaKI!>OoD1qV{8PF>hP#9@;8>ohhq298AFd;+waz-jOz z-UP&;jv5ZuFWI_C|F{DGDFb~Gp8hME{#Ivla)E)9?SB_sU@#Xq*FVGS*bm+fkY`>C z&F0#4q*i=!H79)&1rGr!8coY?q8Gl1d0trJ7nd6hjYuoK%~5er{X zB!Ny*y_RsY7Zk4<%p>vUrdW?(J@?^gRSxgx$<33&(c`mTjh0{bSf@tIk5#|xB}=7! zbqF|6RV_3e*1ETn&F^CIZZK4V0w{s|v98>8*sL@bvPTcJd3ANCG}u@5krA{9A=2S> z(jPAfHhQuHeM15HlH}>K;hH5thzM{CKOFRJ$D(&OK)~=os1AS00XNa^B|8d1iJ*dd zdNSE9-!$39N4^pXfgm;BGgT6N9N6+Vgm@DGGTN>o&Sm$rIYap63%Ya&++B)S@bhu7 z%DI9DJ-~tL;+6dDr1X1h9YG_-po6PbUMeN`+%P&2t-5~x{ccRF_;+E*z@HFC>=2+3 z$j_CCaHN3+P-YlcL77G{NFH7&SfC$$y#=6GkT7ljEX()&BA^RX0fe5hfLr_hxAX7L z$711p`n_HWFY^41i;Ik~TM7ur8~l8+)U!w>I85SylxsAhH3f9t)9I_Q&p52IEz#?Ws+l#PF zJRmX*{ft%sq0Z^1saY<@lD%MH(_ZLHAo&66^7G(ZScCQ>$_#|dEpUeY)sb;Xd^zi7 ze#g#4XFdvh?31uv2RlyLYr*I+o$%egK7kCF_&&xAlsZ7Hxe3m&(VtbO<`6?{>}gkq z<+Q@(5Cek*AefZj6Bxbv5+cPDt~*7)imGdb>()c}F$BSi%pGfrXCx2@#M95JFPgjw z`c}ZWE1^>_5u{iGd$87+Xi!9BjK)+*V@<{+ioZ*hHtbO+kS4i4jKTu}o{Ib`4o^sK zn2h8Sx~^F5l$S&4?v(gV#h6OB0K-Fsjw2XzB8W{t`ZQdQ=c{6$<|2Y?!7Ifg`vWYu z9NDoX9z!GE%+PU=3=DI)niHW%kL<_LYGtvYCURt+6DWS@8xZTn zErI0kI?WKi0wg(OjGqawsXqLa`hXO5EH{z3g`=2W;k(bpTAY0f5~fl=ko6E8PaRqkD;%B^D>9aHR3$K zQcPMQdlh|XPK&+=R{`<*97AbALy<3cwLuN8V&Vw}hU2^I}U6vbV z(rG?XZ`b~=3+LUZtVYiyNB&XnoOgnu?^4B?oiEJ!S{FS~+qD&z^N4zvPsnh)WXy5- zMsdq`T@{~tM(^A%P8Bb7IC+OhbztvW26w+0U-&Be-~WWWUKuR;b!9c*mXSlOp!i4~ zoV&ftjlR2(^KNXECg&kKIeBh(C(Y^_Ov5z2TkPf%oQ$e@@H0f_!k^W8Zx@f|)cJy$ zMw-_PM$uruBrkb^XUR~)pef1E7jvnLyuRgY?=^K2AMDcQPq^j}$v^jYu@ya> z>t{thoGvcpHLqRxyhr5>7|EL^Qj=ba#EbP4}ULh5B{{wVKjJ?M@L?otY5{E$hd7rMx&%R3dx>Xljxlz zFefX=H=3Ng8>PsWp3Ivg@O8AZMK)QA}@2lLZt{a(7K`_7Y^7T~O;Ae8ooB6kQfR zlX~FNv{Jj!&|sd{HbmvD8q;9%;0rTH#_pS~YV#A|N(@QqENb)Po01jt;%g{KX(q4m zwj$q{wzwzd^qUQMZEu+E0L69Wg^R!WJM02^O-lQC6UL>(isIqPr`&x)-5GW z5;u_+G3uMEHYE*uE~P+3=pip-O!5>t1_t4f7!SOZ2M(c>ehv>W{PsZMJ}WE@-^uV5 zp^4!eJIR5us6oOnj5!&JxtSqRQJ52o>WWya@pL&Oisabn(LZBcww=*e)BW1 zN%u`SEZGdj`EjEQEu|NZc#rF;cay-xx1;`KxQk~PeR)W9L;)fz+X>~-p z3P#8BkUw4}mSbk_*vdpyc?!D;x?svp`^f*s3PhxLsuV<%cm%kWbA#8VZ*zNQU zM!_}CqwBYla(2Fc{}aK%Am|mX2n2$1AS4#>*5E&jS1j_5PtV1hjiASxGWg!R-pk+x zSVvSL!vEU(z%X4bSj-~yCu^6t%9l!O@CpR98Qe|Ap+>?d@~~86gz||GD~=HNhu5>m z)}lT+d3Ya&vDMDf65viH4Vd%-9%hqO5sbX@zirj%R8C994G&FC(lKh#CI=cQdG6rnyj5p%Xc z1rt3edBr(*Amj^0C2g5;e_fXv{S+q_LzE z9t+$*klZFXvJ~C0ycIiz2xbTUWGps|e=o*M}@#tlPLaG|rf=XGr6Zhyp)5zIZF za8MNY$&rtwU}jgOxpx-cC|~PR`%F^YrRKg}f^q`|z0yW_fahd{wg*nOuhl2V3KRWk zcGH~yv^cp_fQ0J|>z=b*cjeF(Pa;Vyq8$2lhpmGU?f|wBMh*I#|B0#T;%xA?zOy7x zpC>xY23Z%ZI}Yd=mU;c2nsKkURZwrxARUwv z)QO#Wa)6iE`Nxq)Vf2JF>igKe9p+1t%dJPs4ht74 zdRE86BpgpUZs>e}Dkgr4=QNH_m#xhCjioIS>A3GTjvGYefp}T{Tw2EL;PObM;*|T2wupE=Cib zU57=YLR=A1lN<(SS5l-lY;zPDmtlFAy=qz@PgqV8BZO|)#xF%$QCs%1^>ev9@eg?h zZax=UBfJ++tp25+J~=?>3h&lUnBuDMuU?-s9U6At^n7hqdC(J4o^=mOt3^3@58Mu}@^7-7&C<%g2TW%>*U<^)m_5OwoC-**TkO6L7ZwZY9& zJ_cs|T9U!ryUU8VhUKfk(I%wC zQhGL9G&;I(c8i>|CFj3zicKx1>t>fGjt|@L7Mf?5jt}D)pL=F7>vMC_#Ftl~JB^Wc z`x&)Clfi^BzeO;9h{F^`9VlMrrU|IW>=jCyy{g9&35wp93D4VdA!w`cMZa+(~Jy;6?AybeDwYL7-3Wi{QdtoPKpr7Ku~bzFwVRt!BPX$VcH~+`4ctbUpD? zs7&_j!{|ur#OBYD9PKTwk8-Z$vqdr6sna;BqHaqXse?L0E*JGe> z7BV!b<78oxzIcmDKl$$0T;>?+)VB|5Ouj$L&BsCgGoKI}gWW|w7J?mk zsgY8}wX2(x=ld!)Gj#8NZ*oStn3?G-a|{dvVxBPT_L=pa zyGbTqLs!wW*X0XZAsX-!4qRb4r%tp&6iDaJ3P7 zSl9O{g+dkEG&+ zXpuErGg#ov9P*WQb#ySKqp`jC>#Y=bZDu&<+@|0p+!VU9RNYyo%t9m#AB-4>j-@&V zO9kIFmNOY_q&`&@lNz=TTNk*RZ31{jAS9=j^H@2&CSI}OnE(>TwdWmDe7)QuFqweE z_HwZs3Ae9TsOvS$Ky@`n{>Y|UE1~{6rq78uE5>yKwK((EnuA1GJg)?C_d_Z|BL)lD zv3D}j=JhuBFgXUf^FVC;**H&Py1{@>x6Yxjl{rJh@4k_zuRy~WjLI|^jQ zJAiNLuv)9pI6RPCRH_u4KeFxn&Wc#Vd%Wf0=4jqh+hmCh#zCw*m zxLa@Fi}5AtJqyzj$8ca`tZ>7{Q{0*1?)gsw=1zXnWBp6#Ftx!HWd zYa|kku_!t&hUkX}X-?{jLJy>#C-V?9m7Oxe*jmX?P>dEhFIzQ%K(PI6Q_EQ;Q?#g7@65g2$ z99TQFnMa^@b}WzHrq`}epVs0GSI?Ay7@u*mK6S7LQr7jvE($ zUw;Y$_@8mUtb6_>?f#LiqI`*TuwbonS`vVg4%Cjs%gbo-g+6CF4{fbAhKc6x8 z7Vi08n&fU<*aS%atp9CE`fPoTYuRdG`*9<5q1GxcT6vUNfEDk&0==6eo+{Q`o0-8Z z)s8<|;^+jE#BV^C|K6>IVz5?|wVyu^LLwa-{|Js@Tcoz4TxNlTQ#u)}0@qQNkx_vb zlR+&xqt_%;fYaWx#$W=ThDYxV)z8-+0`wJ~D*gJE8FJndZz!;BnSZ8)G*aeJ4v)#| zR&j}Z{bsRc=~UjKtQVXNxM>kzZIBX{BeEXoT;NKv&jeh!qk!kXXh0{ZoX%{VmNpkQEQjBPDpXhLza{pT7Fj0Se(RGAY^+8hcV=#`xai?>e8g z4T8<#%aahpC=5l6YbN#Ru1VTaG?VCvc)jcEeRjSbW`3D-jKPR z5Q=pi5xx%2+he$ceeg2~!l{SvNHp9vypZC62`h%B(cf=HeITo3fkl4H5YNb&KCG9r(|~kZiAJq{PsKC;>_M(oFBmqD|j4 zcse@T9C>&^V&AIl0Ply`rq6UMU8CFwjaoYA&aN`8MvLgE1TrD=FUv|Ou0uEV`L+%> z%`Jy0-N(%Y)^p#x^_v5n?B(>r)Z0++&XJq3LJ?4gN=nLBGsPZ=hvBSDo)pJYqkT*S z_{w&7B0Re4j24Pm{D*~i(k%kIOBx?Xwc^$7m>#z&urBs>o`*a-gsnD{?q)3qLVxkP zo_!&iQ57cQwfR0t96(LoxG~*H(ntkS@JSIwZ==Q*bE!?$uS256fl}1hfzTxBC!zs^*eY=pM1Wx{_xOXB3;AAkMcilU)^s1qI<-rmuXBx z(8gSgHIKmOCD4m7uh3IbWvj0JLz%p(mli{c=To*}B9gn(=#_(GMmWk4v<8elDtRB| zJ(Qkcv9AkP<4@u1^(P$vyns=bi_5Ax{+gX{GJlZJX^I56T%O_!|bNG?iD1m1r&d2b|? z6^}a;W|!9}u>_f2ngU51DUMG+CgPQI8lrZ9^t)x_HV(Kb$*ztRRbIUV1rFljc<0je zok>Q^__(CdYs*z+h=INgqaIZa&z4Gd9?LEBmF-e}V?pQo261FoF4MZ5?lV6xMi@GlmRMy+ z>+bEGamo5UtL>VPyW*N=8?mw$e|(W*7b@JMKfEuRkqvI$$l*1|ZuEG`*UIkwdwK@R z9D`-|pzGq&0#S0pbIORp3^8?vpYVxURX%p!UkXR4J&U`j2aw1pzZKD!;T7>nQ!jNM zljJ)nDc#&Ov>r#_$hAL0QMAm2onHv#;iTf^;w0l_%f)b(EoqzKFqu-h^RWbD&24lfsEi+{vBQWQ329GTvJ}H9S2$Zf+lk zFRP2K&hka+1B_8`F>rlMr38?WXVx;wCuGNfw}BKVL(6ik{`U`ErLK9lHg<&mX5YJT zHZ(#Fm-M}yW@1`CJV?WSrDe|5blDRbgWi~F+b%!tx|&y!Ui$I$c$AdfDW)0G#ymxo zg!u*yK19&CWYkVATkNf^T`YQbf6o>kkxg34nM(LurEZe}0ekZOhfSB|-o68>>85FD z?1DUf1`0n?HdT+cxV3ZHQStyXT*IZ>E$s8NN0X7GQW9~kh2hc~RBM~Qa;9|Vrbhj? z&{O8DvG_BRyZDEZZ$)cKm}mmi?m_xRrD!nyg6PU|nyR*~=B4u`vtWKpDqVl^p46O_-|IQ_!SH@34nkB< z7>? ztI1}|7U1uQAqynK-@$tIMIsDDmmpNGTdh}-RrN?1<4fcBmVu8OB+F$zL(NJ#vD#u{ zW~=XY=GT0}Rh*H^Bx5H9)S7C_>Gos2PyGWuZ;5+WnjL+8BWlE4em76h{x;Cg@r0NX z8@2w%^xLayX64GACm3IqHl|He()u#GcHZAU>^;79wu-uU3&8Fs;uut?<`~{w6-&7U z!$=FzMV~F#pWH01HN_By8H>gBSZJ6;$|0y4rHtk~{dh_#X4(x;DgGd2Tte(oEOd@G zn1vCO?2K?!yt`%cP^%h@TWjuxeIHhjuGKIxHojQMrqM@TG}Un^JV zu6Bc)+Ox947g+Qg)2ntlqZ^&J%>+|zFc>)z1!jEKGMZp8MMTZ`{+`P9*Wo1YHgs3OyC+&Xs zRMjUiN^xDs&dN{D6gyUQ2MK%5a@6>4nB}!x71Cx3sv`K`(WGWF?zEP(l>!)7r-D`s zjzvud9-i)hJB1}UgH7+}J7&Id>`p}&JSFZ1iaUC*EQlc9;3`=np?x`Q6N#Hul?j($ z(^(N*lU)&9NamuIuKd`r+C;A|fgKgQ(9qmYfmK<>vmGj^5|p*p6TR#GKj zu5c&=&lDI;>fW3{1sU6my2=uk19I(4g0~TKKEoD3^;{Ax~5PDv)I*4olZmER* z!J1stC`JCwFk`n=Q>h_4q^rJk7&_dH;|O{?lx?FSUXT_6fupbAdOJyx(iB<8IJf)# zb&KcbOl8x3^asy`wW6DTkQF`?H2f!@r1nmvzBhz$X(D2Lfhi&I9TXTcF?g?Zs@!ZB z=p#&2l&SAmaOo&79!!w(V&$FjnTO@id^(^Qwx#G*Vr^u3;6KPFgsRFQ#A>jS zVIq@>TV=5!NEOzZF%k_9$q-YaUMLHVerS~gV-@R|;rL}tq#&evpJCBcmAVn@QC0_H zTVd-+YSp=vzpj|J0IrWhy+sRaZePg?tvLGqROauRiM9AqWxk8xD^i~`ynupa93PrQ zKP_7bQR2W9S;XF$u3s_cH*#<0)O~8ol6*On!%rm_&1NVQ{n>KsPgFnRe-U%qBaAgB zNB0^o$xz6uxD7~{qYN2j=|BI~rd{nBDJ;4jKguFSrp3{<{?qUbJ1p3C=8>nQy*A_N z#`eQPoM0u!$n2mKD~iRrZW$L&ym>K4yoYdC{#xaCoA{k!ow{bU^sTU}7>&*SGn{5w z{^0mp!>yfNOq{`5IZ7LoEECo8sLZ!KG0?M4IE4})6_dvqD|8)l-fUZuzfa%(E0!4(Hz29{oDkK>3|AUvjsIICYEN&1?$rLx7gC0jrY?wbBa*R!&cfW~-ESP5PAjx{t8xcojS<$wK!A8$ER}C?)Qx4{CEx_ZukGOvN*HJS2p!w*q3w%2z@tb;f#Y~Pm%x+3p_8cbR%Mq^p)8XA zEbgNKo~3r;7eNG*BjNyY4GD;1%dc-)EI5t?`Of3jw;lI4abi`Oa@rax?eRJ#+tb%+ zl;)$G?yS&rS%gcgOUE1c4?Nm*)(fgn`ImPhRIMsNZ!RfCZ;0&%uO8kb$OtBf>Yv(eZCCn{EYET$)L&|oVi*{v&1ULQaB!t8ydXqrx|o3G=F7lJ<#^W5^4Wu7qip6PVSbIJF~_oz8EaP8vUNUOvx zHW5(EHqSL@_$HtFp;~E0v$x>~!Ysli{W$#*{fetiYWY~lT-#i8e`9~gLfe97O+Zop zLi-i|E9IW5k?tT19<~Z^;6_3UlXmai`buJWguXdCSo(jqI?A|^bu02rKJQ;=#ShFW z4J+tF%ySy-P6j?_CNxJSB=gM>LsQ;m`5DCo-a88GJn0M=BD7X?e2S3lA~Z%%KC=?g zw4ffeK%hdGBcYn-NHC+brY5US3~zK$JE6E66E71}k9;pn#$+~|!ZwRBYg-nXJ@!s1 zVW|uLHqa9+w3#i6=;=G@u&RI&7T?`9J_++ESol*d^IOjcdbR~^rXdI6hV2Lm8W+fV&)vja2L)8t-OhJ{oT{G(^n@i4YO5Iz_g6v z(DD+Tn7g&I*onR5Re>$red-Yv8Pr3OJ>*~W^*HVkY3vVy@1`f=2f4Q2hk(z+&hrP7 z^Cj@OQ}U9M9s_0~v7t{5$!*yF?;}nF35VBPj-i1o7z11pSd9FA3S~Af()>uR*mbHx2pFx+R$||_Fq)BX6HV((jplyDtM!Qb#HW zPW#@kc0VnGz2^+&dKRI6Qnp^u7fYsFO~t~`v#ZE14W%tBEzFHocAcN>-Z<0b2T2u; z^F);dKnZVA&MLBwIYINf;Tq_1h56cg5>;TwKpVsAMpm!x-=|3XTKYl3)o=>6AkYSm zw9d+W>Wyx@rs**I{1Y0p#R{uNP_zAHb~!E9JUBDCt6{YlwRm5Je_8zG^b>qN-zYph z>kwv&Sqf;vxiHO2l6!b57ZW4akA?iG_B~UNiFZ$NC5~S_RD_nufd$==^MINB#1orf zuBD-3i=m>DQhPbHS0|DcC3XF0(hF+M1vSjwdKH{iLt#N)_{U#mzm1e)&sq&SEfjAS z5Yz@gsgJ@l!-cd9`V7#*sRY|AR?t#vCsT(S?x(-c9Vd}Y<1p3qpv z$v2areScw8C?v?8=Ir`AIr;SV)kz@6%zZPk)LX8bz4-Lnn0 z{&wcx@}?cQr$k*?j2Ob)muQm@C`X}2z4`fqy}5%H%f&MYi&d49D50RDj%imN!FizpTVH0ARi1UlN&)9*i9q@2OP*s9XQ58d(QK;i<`L0;D$vK*?40MHfHbL%Q( z#K6&2(fN~jc+OBS*)r@N8aI*&%A!(y534oj?lc9fdd@5oI@80=j-weqVAHh z8z}5i=@Lx@1Ofej_we9)dw7HOq;XcWTmyo<-2fpd2V6qcU=KfAcR>O=VbK5TA%NJ9 zw}cH@b@24yk>nQV9OFM_=-`(W!~cQC{2Pk-6O#E83KILDp&;PN{~I2N10Xd3@`oMB z`uqQy2V!Go<^(tuFu((G0qKqb9EhD1%nhIx0QUmmF92G?!3O>#3-XKZ{a-lH9|@KJ zK~20QQ~nQ|{}TuLSN7$9OYr|22mGzq|Nox^0&_F-a08?WU>pZ0K=1%}0|yA6KdB&q zn_>pDa{)vR0675!576`v*RlWIiJgn}Pil+wA9a6`K7VpTJfvJe1sgjnz^nkA5IX=8 z{ndb!o$a5)zK}XEKo18q>kE_fQsxB!9AIkzZsu>~hl?E`VE%x+I0207g#vm3iMRnS z=B3CE%)!A7pf)@&`|$G11ME2W3+zS84Ny-2^#eS^4X`(W&FsM1{=o@x0X=zu>DYi3 zb1-vraRN~S+#vm5266-Q^1e`I|7C@LuJ~VY(7)&+fJ6Fo!ha9L$pH{me-KCiuHypO zqd#~mfNBHCrI!Hx=fv#n%-k>T21qNw@9cj%gcBgM00@fnpS8RI%>;gdeSqG7_2B~8 zBp!ex0w}D%O?iQ`z`%aKa9y0h6~X(m*QD$K_r&^gVf~{w=fBuHAUpWqJN@svf0Wn& zSO1B8fnT_>e@DoluK#=Efk*&gEnsGLfbIG#iZ}r(3ShB#cz~dL!JN3*{-VzQF8z1( z65Gqz|3~x^$3O9sw@C_)A*@K@r+3)A%@NV>RCM9Vu<Wj$0i$TQu0V#{st2J zK0%T$>Q}TXMJAE1p@7`E<=v7=9Iu3^g{Rr$E7_Ssmx~Unq9UnT=_H0Ef~2ETt$K%# z<_bGgl=0T|7MW0^E#FEC>%pTf>VnkXZ;IuD^?&GF2oRFhkA-Rbl-D zR1N>?$A(K6*7dSG5SS3f&NH0d?Y#ZZ7moi0Qu+6J_#gbze{*>LpQ1~DB)|VJ=+Ymd z^AB{1?d9b7pU@>RaH0TQ>i-U1;^AcbFQK*OdX$qoiog_(jX87L~KE1v`$)W&Im>CK`=aori-@Z7oSi`E*NJhYGdr8hzU72 zt<`>)9@y8@NAL%IB`!)g&;x=#lO#9s_jjx1Z?xP!-G9~ipPo8GLW2mEQQ!t=Ey~I^ zp7({zKnX-3R$J%8K8?@ubD*hQkdxVYaXh8|r_^K6%nFFZ{;=X+?zHqKAC~kj=tTKO?OQyA8@Gu5V?7n3e34M=11BgG7Nj$t#-#I+V+I~%#sgZd ztGY@Y&Tpr&U;#BiB{-v2>35(#J+>hPs(}hx-L`frDTjVT^>~63&NlmIs1gM;17J6A zKnc>C)%uSzr@WtW10^zUv;;fMG`{N5n1R0YgLq$qXlX@!eIa^RZ{FENn0+RpEGQ5s zq6NL<0tZ4tK_AWDTV@~{O+0NyfJREmaRS|euj`KXCZ6pTgWSWuI?~7S*LnuA-_Q>* z%*S?HP^E1AbhJAm>(Cj6V5MtsjxJHObP`$EaS%rzjZe zDnt6q1kzBpI3>Szp=;Gf+I@HGeR*PAKY*_a8G>;o{*AkCH{toDFSKQ!vJB~W#4Pk% zMIPu`)UYZAs~^X?1|vmxIpP9)Xung$zwCFOsd2NEAY}=3vKB^pAq-tJ=_w0vnNRIz zhDCgw6d>TI0fWtzd$o`9?&if;JtOa_aY*E}I~a{CyXLYRGz^oHO-2zI=|10=e20Pg zRhB0LS^NB&rYDybFIrck|xgZ*gY9kZa)wL4KS4 z32aO3LM;l9_lh^wQK0%f#EK5A*+v)lJcREPze3Az1TRe8LZq=OQwGBDg+jz1g;)Ue z!_kjZ56Ndt(I^HFdJ|+mPo z#1hj%FxTnm4qS_&*{kb?m)7rBi$q*!X9-4XoR@onw4k-2ZOI@mfV3Krm0RdQiykrB zFwq1M=9Z{7QV1D@R6o=ih_WX}&$hbxYg&)Z`!Q)__>>-_si&Zd!P+lHM?$t1kc^G< zN`c?!<1~9;YeS>6AV&UlCa99O<9Ic4t1yRcjXBlC>MZMCr4c4@RwVALb zKQ=@3CR#QYhAtrD>f@H^Zy8}-gqr{wpZ4i;nhz`vYd`g<@ggieB!?RV%+zIt-f5aJKxsky&olfhdWBQX4t~m(%gyv!wD#&<`I{Zdpl4?!mItOJ!_RR6?*}e9Z%-kg>CcK?jUOn2_WREIUq6iJ_um>qKY3@z zKJy|8Ye$^!8|`xuoPOzTfZVuG9@@aXr9tM0ep1=YtcDTB40*af^wayE-<^paUC=i| zA1*r+Z*$_hXPn}D*4o8x9fI1$Y902C3}~G8jjF1NCv0tN_m)gMq&6G)qi-9A&z4-8 zcdSV^R6_Y5?Am(_5#H@tlLYMH2_5(UkRz!fj#aX6IRCKvc_LgRRmeaiI8_hX;IO|M z#sU4a&L(w`k|y4RY!EigQ$tTIWPtJRrQw$WWXN%n;a|dr!wZNHcYOM|5yF_-dp$JJ zLeys}LAJzomz;|Fvl2beWWED&@i|77Z$B?j664$^^k_h83GpP?RQ1Bj!=AxfnjC`O ziEKU|eiqxjaMSl7I^_?Sf5nWhP40AF#M7m{N&Y{Gd#Cuy)@@%qs<@I=Y}>YNRBT%n zS8Ut1ZQHhO+fKgBxz1i|uCw-Dd+(d`-Q;HAH~Kr;JJS2VKkaE#&pE6bq*W?gK;E~9 zD^G4<$njfkjn^B^%pR2(hbzA#y7*ZpkXa?ZdYMFSJjDlHK_(Dc1-8~tf3nVP0mtMR=pTgX zrF#a&+3lf|n!8k1{V*Y!WTEX0_R%)XeTU&*3C>L&g6JH1b9TIN%IXY`dsR#@R(8(7 zx=jh%)b0_6z!dxWG<2Fo@v9GD>_?L2-t8GYCO2USY-U(5h*5r%b3)hHFe@Wf2yA*7 znyDP8fKE&FTt(oAWa$GzT+fv||IygHuV?`cO+lZPEZZN0%--eS@uGVn=BQ9THYy8Ms> zG6SpxnS&5vNT>){!}=h?z7x3wbmBUBUEtmsS8f+*f!n%doSZnlYwWNRe2o#=S+}w* zEn#jK@Fr1+R(rV@_O|Q1Ttgg%y%Xuu_Mpy(;L}aL5fQkT{Ac8_)3kZPPm#2(A&&Bm z0d%RnQr{0#c$qMfunfK7#kOO3FNE{hFe?d&$kGmQ0n=j;rUcmep)`&gQUZTZ0aUejl=m(IbX0X5+TO<>+FlwA^|)44$Ic$Lwk zO_EmkuOD)g1vGnqN%aqeBi+|?IL?VzNa*#E;Dn~hR75RwrjuA|@(yuce}Cv~uCS<} zKu87pF=l9YWDKl%yjl4*h#b;RemQl21XFyN(|r6TBpicCIdO=~lW)(_UHIeIHT<)0 zaKYoO7-h2OcTmZD*ckzfuCtOcQu!<8wW?2Xf(Z0Py+zr0XhKoEnPCevgP^^c$pgoH z&$!k2i(zIB?A!JAgLvj!pV8QKVg8^qhBna4O#T2Hg5IlYgX&PqapXXyP=RHG;-z@0 zf=c4q<>!P2p>sV1)zyxo`mb4Tsuaxiz<8_u-(J3pG0Ayv26P6v!_&h<#gO?sSy?eb zw`cFEZS7U`6W)g{@h+s&lj0p#$60}ZQpjN(2U^7fb{63)u*@fc!+a*aD=Pev^ChIVlORT@;?{ zn_vHdj6y}kZy)m|3i*KHnwocLkm#fbxszDee2K4Bo_V`00A;=UPxYS zR+3;g>ic!u@|HGo|C*CbWxdcDC|DpGh7~0tT4@3g-)UHfgZaBRHv3I3 z8XQ&_kMezY?T{!{-`u?Gc(t-9WyeuI=lGovqw(n(DG0bY$EciRU;K&X1e^@>TA|Y) z8Y8q$A)9|IFI*kZ)&$B_EGelrpBIme6n3<@+?Q6iv>wOn&KAOAa63S*6MT?L2hZ+A z5{4T@>k^i0aZ)n}X$K7s8{OE|WLSXe;af0AIQf9Szgd+T)+Jace06@c{lxT1T)L~( zv;>s!I$kemeu4j5;X6zqHh5Brl>w~xoW#LUd^)qUQFd9ae9eT+xY$oI zd?QpeU9DboLZg{mJ=|y9>)XZ+_W9c2LC}saCt$S6P_DeaCR6YEb`({}OFtFVV;MbL z3x#qEg%g=(a{&`73z*_qj2&7SP8&f9ATDlc*t%~kFZQE9E6kPcrw>M?p`@TC7=@$5 z?7{7PS&Fqk$5>;cGve9mGFil8CZ{ek8upfbxd~dDp3e123MX9Mw?jKQ9(LAS2d*bT zufr}7mANnBwMhuuQn~l0I$nCYai+2ysy-65!yW>s+^^_YNo?Rpd8;wKh7T{Yic#GU z;K8dtas&)Q8HkOc#OTyRUtg4}YF&aCe0#)xc{kjtYt83z(@|DO5pA$qdlbJLrfdQr zx&fRSN-(3J-9>`p01lwt+YrC9#=503%m=3I2!uZ|&UJ|Z%8i1>nGfTwmDwsr6P3ah z-6)I~c+MHNl+LY5cvqzQ$alm!eB@)kwOZ!6v5_2Ch4~>q=Q94{XHozb(94BBmy9Rc z7pw6n7m^jEjW&RzAyS{WZ z&dm{>?u9Un1*}7G6mg#w{=f(r!uhCFfN2uz=_FL!YOP8@62H>(ZG9koRU2`!Z?m7( zrR1>EaMh#PpBgDwsob^5;1kEAai7iUDf#7rfp`1`A$3~h5Wq7@q0}lstq??7alrl+ zj!jy|?%2D+3vJp*(7`GH8NLR(T{~wHTna!S(7O*;B(x36 z!r&$xBl23IS|Bb|JIGinNqmbQfMPE_s|V{tcDT-JY0RVa7ro#BD{6_Vn+a5hM5nEU zDfFeG#xg@B_3`q$mD#OdK!z@_j_IWkbGqmE>ZnthurQxrVI|=ZmHko5=YjWX^`NIa zIRv|BQAjD&h6Bm2rVJ=7?v750)Dv2C8q30tDU*C{3s&&6yg!pYoN1Qt}C^gEojk9 z-FR&%JX~q?&agfmO1migkk#N7DthXt?wNJ)4|Qiq9uQjd72+)FeJ%rwf1K8QrA zy?3s?7$sJa#R1B|qFM2#rEJ5b_~<5A?QacRmQ41nQ6{5m{d{+xuY9mTkROUZ{rc6% z$jm$_<}!9vP_Wqayk67(%suoPE*$y#Zvg*^{?g0yDiW|%+ebucl2q_r|N)?OvMKxWz?Dp*pkpzjVZ!AeZ`;_A0v@+EBAZ*nwA`oN9oXzYmf z+lYzE4egh+5T10n&2I^r?Vk6oqbKMKoVQ&#o49y~#!R>Fmk~qdzfG>`t^1;Q-Yagc z$;fadFxf@yw|ij(U-% zCI4o-YDGQ{3lZbcFHU3NF@iZE#*UetBF@Nzy{p=0bYMH2k}M3fcreqvc2Iqv>y#6@ zJ`6@WTr#;ZnIUFJ8@qq?Lp*|gF2S%9>;liu6mP<)_NjfQ{RY~wLCu7sX0wp8wz6Mj zS?yjfwGlidEbOamBYRP}jieM~!?pQk?|JvUF!FW+L!XZ9>W6tpMp)#x1^~L#$}_Z4 zUz8_(M0sQrTYT+e|8bR3q}rBD$e$@j`*Z{4T{aRx#D}mHka$8yTU3xz&U+&WDOqE5{@z0|x{^+_(+5Gck;XIi&{uWy{+{wIBo^m`itQ zG~GJpi;G`zEauQzY4qqHVy@9#4xr!|$4N!bOo&QIN>G5k&&yaazi)8gzR!3GdKr2l z6_Lv{O%2I31%;6~W(YJ2$4yRH9!)m7Fj@h%#D1pf^w{xEuZ+qS5ouHVNOiJ$J8aw= zKEIV&YrRv^-5-vAhc_7zC`~AHxxDw5cVRxGQN23o(5I@FsTfCJnd|%vLpxp_Pw!ZE zjYbl;s^Wvrd(Oo_BUfUZFR5ZTw!6C!{nb1mvP48?rct)B9-o0NnM74-Wnf(RgTfg4 zr-WpUPq16(hprX@usO3bXS2g;EK;f~HOxx;-JNmbs>BH>3)A%67c9Hz{E!QKyvqRD zPj(&u#GjVNWtXZNL_D?P8vU$-CdgmHGopoN%O5y0gs`nxq+C3%f`W8X%k-d(#N(#aCV_7Phv&$vbY%8uF+;^?`TVpCcV}t6|l*6 zG(OGd`MR47MIfB)Mt0ghq!EL0hpWx{<-EtdAkUwemUPSw^cEb%pk4)ur77t09716j ztS3YXv)}H+Crr}YT`MT)b|5-Wgw1raI@ucLb8T9to^a_j3)76Pn+0J%HvStN& z?|SaBM})&P?c~j;7ai6%4!B4hC56oGdNv}GIu+cm>06gLrjel+l{uyKj2h)3P?ffN z^H}3H6AOS;P7jeF!`xc7&F~~^#ZHa)!_uAh#Z)FmE``bChM&2Pfi1_6a*l-UQgs<) z&}prvy#<9OER~yc_WQGBug26RIJaj9d>j(1+@Edd-YIj$DeecpuoG#|qk%h>fIotQ ztuQ%FHt`JC-<$~phXnE1Y7f*thW9k<`dj%vz~{XWtO*o_Yuy`#i$G%e6^5B7ELXA( z1DZ9J${6d%%|XQKyW;n;Rqm#dU@eO*%+PIh?ya?Q!ntBi^v~rh-wTXm_b!|}eqVo! zH4g+_QW^eE?$`Cz5j+F_z--AZyQO2~)JZV!(}k03_h5CCR^KF942Kr->&4c)A2}CdegKpQVAc zoH~_X@x&ko*k?bdp@BQTUJJR7%&Nq9J0V8QNZ2I-BE>Y@gh*N2ozIQi$x1#JgQucH zZvJH>!(j$0QOe2@u&Ot~nB#a-jJpw^A``FLze0=rgfXOARcQtgp5qpA9xXba&LGxk zKU)I0N6yK~#R*+hKqBLE_P`AR9u9%`ez5v{-(9|;HH4RrI~29sd$nOxr>06=+}U+2 zuTzR3^U0Zl_BAN#lQ8IAotLEkkP0K&@lmPESG5Hj5e-aQWr#SihI8f}`vlWXL4?Jd zn#3sqG7Eo*T^f?p!(A>YGV-UplBO+CYoZKq>J>?9SfrUXV9@luOoOHFyyCb`BdiRazgMVAuYwlQzN%UwW!;8CHV;MPH>@TdAZ zK?E7?RZWs8vE_?jALCU>w4-e45f)OJ_;4L_n6)#6Id^jP4yP`4c{whzl;LSVziC|L z?sPjx&-lE0pRhY*-~9X)`4syEYs1$j@-rxy5;V18mC~r%RV5h-jNy&0aYv^%H=<5Y zqHMb+yfJ6n63xfsc&1$TsI`8yV{HqJtyf)412{ny*lCm0!P+M63ll*LzufhC&HJyt(EhFC@R z?*&GzUB!^4l9M-B6c;TC=KefMNNagpK8|VW7KxlOi1nGU!+EesXA6WT0EHl7t2I{o zd`E|?0rIjmFx2T{^eiG=g@Z10U@WDoAr{MO`ieA=nLFp!9Uidcr)S@|vFvj&AfkBT z!x%*g&|Ffs!FbSeU&D`S(^^h^y(3V9nn=pvqldx=Ki>xv6=Wse5`#b!Ek*X`Kw0f`iC? z6`xbWTL7#1y4b1IY$fA}oPKtD=>)w2>df~mGHlv9m)SosPpt&1e*VZ1Z|t2_Fw2pz zg$cWqF3tRkpEk^68!S#=X2%AQ9@}ju5oEL!As;?&dS~q(YtIOL!8W<V>XMgnC;-X88hZE1giTY%_5dcyGQ2J!TCBatxs@TtWKOH~)gdFQoa@{BRI zwAihdW%AAhDZ;*ZFESkctdSYZ>^vCrXZUVZ(w&JwHP3a=qh9Ck$w5^?ocnS!Yr_1A zQ`R9(K;<~QLm~%3SqiJ9|8jLE#cN)(rDV2LS%3VY*tz6#cNCR~?eYx3rjyccdS`zg z?hb~Wph0V>3e+X8N+(*?#{^eeOGcnO$-}h#KlK;0l}{(V>QcSREBT)A~T?d584%B=Ia#oY)*2d)PlUW zUw|_>Uk3vqMbLnD56%XX7VTG z{Bi@^L%uy5wc+S9q(ktz+zW`O#&hyZ6;*$+&`$(%k(=LCS`r1rZira@cJ&Y|n3mr( zClXB!?YSxpPnQwij8|N!a)Q?QJNg7W!prYFR~A=DD{x%yuke&Ge}^4BGVUf|+}oQ@ zQp=af1E_qX_^7nFC;9}rh&Bjzjv6)^M9T)@ii1xD12@4|Brr0bH4swb3c)`#rzeo~ zMwWgRw$H&&b`A-SeI;Kw&cKO(i!|}ExMq+hifN0ZOvE8FHxwhTp)}Q1gTW{nH>_pM z4?--SD}}E#Gs3m#P!UUE7m8(F5liVCaT?;C$wYc|TbLExKyHC z0|lUtTfhg>tAI%p$lj3tAE=B}eAluJr51k37L2E8VUi%lybN4sFEaBC0jc-Uo+`*N z%83WNwzB>Y+VD#w{=c!0Gco*WDE|Wm`Crn8Okav}CN>5Z zCR{3pFEWeiPa`$U7f1CsLo^-jm;C$>nv3oWP5B3H_$P|;S2X1h{O!M|4gXx8f0B>? z7mM(B!sh=yame;X(b4`N!0qdpziiZhaZ%I%#c2HRm>UMhuNUEd>CX*yEg^rWX~s;3 ze7UFtFP%d$R|Uj!8diP4HHEIKJXIQDb;ge%&Et}=-Cq0OETCLvp9YezQzZq}4E&EV zQ8FEiBy8J`!x|XBv#29<{#->X0IFzRDc{?4&<>Z(6H=Op&F!Ea@7hVSyB4x!VdJ(e94m;9af<)Nmff7yH_D5;QomsiciBIZn>j~eDDzeUX8 z`UW58t;H$ygY;WEFB|DSV$LYGW)1*I=KW-0>!AGWe4*GJsE9Vjv@#jUv=*PLP?h?) zZeav?MT{uKLzO&u1{^2BZ!da6fw~A=5ZK4rD_}oVFs{cd&LRi$v1-gGTRQC3GV6sH zn~#U5@6@fQ&|K6@tYH%KH*bJ*OjT|Fy7m5I3IE5=`**JC|DyE%hrAa39~J-4s_8$A zPq1L|KbunlA5oljU@E5IrqXr@*s;;;9WJOC015Uz_g? z76I<$^0?CRZc;MaF?|K0S3`ugLKg&P__Fa_d}2xwMW})$UPl0sn6KzBe*p|w6iDLSHHhF zI%Z>^y$3&%fSFL3Bs3HM_~k8dt4Xc^Cz%wvT7~J93urza!fc-f#dVaP1n>*Ns<(#K zS}Ls(8)M2>3IPED<%9YC`&@I_bz@@)<=`IRNrINi>%IzfZ6RGztsBKr=6PLA-Bk9Jnll* z%LnoID!~3?)_9~5Ji{<@6Q1)zmR&|+{Wc$`TPu$M&oLh4e47{8bmhhIz#q@AT2F^f zPqfn4x)yI>`er;%n}dP7&Ci{hyW*~k2CjmsmBahJtOJ45tj}$qDQrx!=eLVj=1;Ux zk`El*1f7r!Dritu`WCR#e93uo4XE`?{D-Pu`!xU(x`0N!RE<>t8-nM1MQRp1WOtBI zRrF`V1W>#+ylJbyh2R^regLoW6PWbDa7tOv0NYFu^6=0u=*{kM8)p_P#6Jf`1FJU~mNLK7#3Ss=#S!uHRD;zItO^H9wh6)dCz7_GOw^SeH6ms{WHkvN*LR=OEF;+`8yS zG7ry8?tnD5#INjo07-#HYrddmUBZ+Czzew;jXA)l)871JLSQRXen=+5C@s_Ihfi?) zKSpHllb1V6Z{3xNo+`wfWq-VL~JtUa#&5an+PL>=)9yUnZpI2)-!X@ z1O_y@;i&C4IO!3fPX{QKdBaK_@p7Q=aL#7caIM)1&m7O*-I%(VhIOzB7iy)^NT(N| z9L^5Iy&nRkgHN-h96bTF|DqIYtBQyqn!Xjga}X2BtPRQ4ze|gc;Cu~Y#XlY@eOvA= z?W>KPs$G5FHn#2CRiw!&DmDoXetWCs4Y+9N3VWfCxoiFm()R&9BavMbc z&~)@89@}ad5jOA$59)lwLG3J|&fGYC$sxtj@{pqw6_MlA#5l>5PLu}bO#FWtxj^($OkH*myE?tlq0-6K%*(^9UVGj{1N*c`%1YyH!$OuxX>J3pc4Nea z>Gu#Zy|TED`D%OLK@~Qxwf&>V$vh<^_~1&>U9~muQ~D*T*Qe z77+`ULZ<7*2P(N7NwfB+$0+$7!HA6}CimOMCnTF5pJP|sds%7cgsgJa3Ffa7Qgzge zQDSGQrf|8tkifij)j%prYJ09OdJx=AN%?n7Z-g2kA^4iB&TK;BWfp?l7Jzc zk+K6su|Z7#rX?z5}>(sNKr_PQl2n8nn4hTi4apiuZL_7 zZrPH;-<@K43$Y>IfN9B}DVEZxwaOr7S(=#DpU?USC! zM8B&)p&d=o`kE-Ph6GLsOJrw+XM`sZvb{xQZEi>Y>T4mRdVpffYIMJOrPjnol?HUn zX9Xkh(M7pbm@+l_*>FngMcIA!Vjqhh8lZoG`p9ly&F*Su@I*J>XG zJIX-M+##1XyFtV(t+DkOBr()gQ-vd$ho(Z3?|`1>9s%Q2XxW}kA)CtQFY zIuM{IkS|N46`>VTa&uPHt9o8U4Z%?xM|q;Ns7-1*TVyP*H z)KN)xbh%woMvZK~z_>2?8$`UDEItN)^monZp{Mt;p&%QhIGoWW%tV}9Se_Wqs6?KZ zAl0OMg5oMmi^Mg`*S&XJhShHY9l=m;{!M-w_5sGOYvMH&D9y~RC%+)g;iE7lZTEdr z>0jyD-_nZxle3|#qH(j@xv7Lck$ne6Ur zI=q&N`LXpLVyo2RcATQf!f0#~{S){~YDWX#!$F+H+Q%zBiH{2dHh5=rT2>5O0wfJS z_HqP^Q{ZCDnn6hXd|cM(O4k`1#sIdmedez>JRqAch#`>x)5J)&X{3mIf4Hn>eGcTgiv2wPbz0zI_j zM`!?lgu(a0ozPg`lH7Tmf@NcKk z5=H9$zKxical1E#l!0>NwWB>Ul;@kBou3gjljReW#*1_)cC{y_-)?N@!{kLG(#kR_ zwzU~-yeFrcv+%Lvx1sY^{6c>cFe~>>8Lp1IkS6!$`e9<*X$f-<@@M}<6xlUphWNz^ zy~9cdGp~w#Q`N)`(L|ao70pBSA%0tGy!osp)yX;K__%($xasbM;jXUwV0!mUfPlhS zD`7(XB=3D{Yb#s8*H@X|gJKg1Wu}T0v#P1QB5?bnksbet=rnhORr1&hmQsjWCPgGM zv0@-+dTEMKMeaT^R}Hfdxa%!ppXyY-p>fSRKe(mzDc*nU*XwSxypRp%y_?PGvg>}} z4OYVQbs?qc+Ry{fG-urfLVjzlb8WNP3Zv4{Oj7K4!sW>8RQkH3=vu&Sdg8 zr2Tk?B#x+ZdFrb8n;=da8oK+T{zwnH#w5=|8^Q&VRbk}Mt5O1@mDOC0Ve12R-2nwk za2YA7Y6Da|+hNpUQJ1CV)8HjC_@(sL#Sax>Eon&3gedvL>;3)W>Lb~n*psRL(BQ6? z9ciiLrbBqSeuZ^yvoT7?@Wfb(H^ifo()FtBhKkT{pSYgNt~FN6x22b@ET7W}$~;e7 zlJC6SP~>)CTAa^M9kDbchkH04i3t;BLf_jkZ1=a&stLQ-%~nY0Xsw%!3fHd zkR8<}13b>=$=(aBq>T=K&>mZ&|3-IF0}vKS%sq(|k9mXXT-DxodaD0^#bt5^Zuo@dt`qO(M%qw_wef5d_|CVU~M6ROjUZ$ZxPd@$oz=#g%jCiMuB+;ZwTNoS+poLE8-bPE8HUNWy!U-PBjegV^BaQa zXt(h38S+4l?ym+mX%QjT-c-Fh**>eVCa2Zb8ElE=)b08s&SJdZX^RRl_j>pn@cz3g z?AOghwol6q6!AXirDv7T8yv>kmLKBPN|JFi!|Ze)FPaWze)$YE6xJ53H^-S>b=E9y z>TOng{+i4vXi~rAdSI1u!HgPR97)l|F;gdC*x+3VYc>ZeYx|RRX-A5lV&Cpc-%+PYjBPmr(IE9e)ug{CQ;l!~oInOMbQu;FNu9=UGbe+{-D5=3 z(RQP&1q;=I1Ot^$n-aEWKp4B6N84|~e0&GmzSW~MMUkhXqgw?c#g7MkT(xLXl_VuB zu!B&p8o9#~!&GjhmBD7e|Ayz0rTjhC^E#1vS~K1qU(cY78q+aZ1-T~45mS<>AbDn7 zHGwlBca&*QwL~@FD%~dB8G-qf1>J>;qgvuqsMl)ri^e>kJ)ea|&8RY1$exf(7^uT} z9v|~`7P+)$Id6Mwc^j`tzhPXkfPGWp6zbY=1+}WI_3r|ReMEgx)gddnG6)(ol6|a^3-iK&Fg=W|j?}%LNjgZ=ABd&#TOyA(`0bjCo{Ial6 z=rpdB5!VjuF{k<{)yny+-p%SW-)hYp)hC6q`*cs7=O5@PQe4=i&V^P=ok=q?GJnO-e@sK9V)$xu zre*xI9s2JL&411({G+Rw;h!_=^_Ii-Cx7SuOW#cet!%}d=8;(MUiq7GKI?)i`>$~4x7cv5<3PN zK-F{E-h?ga^HvpP-EUV+6i+g_2%pZVfn^y*=ESO#gGqQkCY-_+wiHd1&ClN?bK9R& z?Gd!IUhMCkOVTAa6eD}}lcU)vzq6OOgEj8iNCzq$7nOmX&VVq_T=?h$8JG=HKvJZI zBi7=0U6A5ctcs4J`5Yf<~UM`@|X#{AWE{3-Se_j+Hjs^%xHL2N&#|UdW#ypCMB`q1u zIt5I;`kPEO7czswfyy48IIY2lj}Y`Q8*x_|raLA9NyZ9WnZrLE z5MTa+zu%Slj{us9k@=7Dv!)jIhPGdKrz~{s4FwJVn3M=f^S7sb4Nj%|r<)h!%I@}Y z^U0sw6T}Cx}ZSbaKL;a3z5uO zjCvtN^>I0hVbi^!s_~Y(^?|kv1hS_qmiuthiOi$Ajny-V8_Vt_fZZ*JWuCt$He3%p zHeWd(AKx+#JAgrX`C-Ak`$umeu{~~wY`o6D$trFlhk!dlSOZ%i@(Q>Q3co^ufzAS6 zpL~XuTZS+;zUz*AW`OuX0?050t`NuyeCJiIwANZ(zFXzdJOQ~=_iFOAG=*l$>-ZdW z^qL1EH8VAZKGy!J3g`$6;3Tm+K-J8*t?A_mOxeV&fs;}*`T&pnhTJ8qZX)5gp=1i6 z0j#5qVrD+}l=$iaRAdI&jSA6d9p>SM1W-$M8~r^z+V}xn8rO;n_XH3;$QLf$Cu^-W z1zDF}Eh`X!;4S2U5aK$>7aS-j6L5Ny^w3>M&#Of$1GMk*f|?IavMmAlXC5}`N(jrHHqhRvc5 z@N65lk+cN@BIcq>T*J8X4|SVj_Ut|-LH$N}O-@uzLg{BMttn!EruINBlnd3R)Ny`7 zodl|m4XD;Uf+wHydwfE>%f;DVdQxobciiE8*siMr@a&c@5u|aMj&%I-#yQK6T(Fyk z5Ag~(2bd(x_U1;Z-S23h4G*T+OAf_>G5Z4d`VFJs5PFMpe#T`^mj|X+Ge?4xlA8C@ zuLDuAvMTmIv|Hr@$Zvd!26TCp97>w3g`6&xLgQ70QI}z&%5|CA1*rY#C*#(kjc~i{ zSn8Dcx)j3?P`H5~(0G9_eigy3MJZL}%gDIdRy9(hgJr`FztvNFK_3m&zp;2ZoAjak$@)vF1Vq87M)~#^^{UnB zaWQBAP&DZSt;a=B#QS-<*`zwY$V;WFtCPT(VL!Ap@c?Gs)_fN(z) zA~q_*4XG=F#jZQ3QKv_+QvCyCDhO^R>N^K^ANw2uisM+fTA4qVgmwBn_9;^imt!Oh zV-8^R)CW!;@_LL~FdX|-023W_ioT2A)D08ShV!dSUEjqfrn;#JsUv~wnST|5k5uf? zd~XD(-*kYig)|4{-BWJmDC1G~BIJ=MPCNND#W}oZzLy8_>?pR2SWYkYizyBkdSPbR zEMKz6SuTp41CeFZ_Q&WX!ES5Z?$gadLaxM(r?iWR7@NEg4%nhpC*qqrh8Df~##_ zgeug&tzURt3f`JJi#Qk^qbS=A#`-ANCHaKkMTx+K6LIh+It4N)m7m73rxep|AD_O`RiEn#X!6fEA8W5}i$o zB_+shpW&0qZB4K7v9v9+h1khy?yqjx6qHW34e8^l8LxlBQZY{Z(o_BJ>O&d2^9sI5 zH0_YQHE6N6V(FAAXkKR&A^v2I51T!+Nl|1>oQyf%cpO${Ongm%nAtu&f*AQPST~q8JwZ6mIEbKquA2hn=;R z0c^f!zPKPpd9+EClx`2_h>Z0OTZ`s@V8m*~7~;m{+P*}YwT~;wR_J6M7nN_0lsV6h z<4%b=+m=TsT{b!x#;!>oPTAXDV2j(!7EGq3Ckl&QjvE`NPaYbQ8a9X<#*OWB*Eq@j z-5x&Z6suKu=z?*4mlsbK@%mc>r+8>ck=}+b&<-cqU{4PwIvS*4-zPjNAxXPWL9e$= zchPD2&YF1}gZuNez!YwZ@G)5G4Hw)@^7^!YG211*|JvCns4ezT_%h&n?WlSB4J5XA z&n5{|3csRJe&k-lHdC$TK^sFsRcGW8H2n5<>*_@wWTHFyX88<3Y0E=y3m=$oi;R4C zX{b_Ct8&?u;VzPdzrC7j1`K%YA1b*{c3`KvkE+z(hR1Z+@;af}T1?(Ew zTIo8G6*lU6ZY8f)WH}sCAj_U7-ABgWs?$i|kL6&6YFrl89lo@!2Fo!6I5rHk3Q`rdC_Z5np z1@wXw@_Kx7^y>RGChn3M#`;4#T^H3rBu=q^SUTis;Y|#8r zcRI|8WZ!JgLU}+>FDT!1z2~)dX}B(S7h;GoGWz%!Gp8)n*Xr_SLl0>LY&oVf2%BMR7r0?8%A8J z%%wXPobYpFqu2)K+bvSjCg#xPFbYr@2y5CgT7Pj_RaTMTIz(K35^+_U;0$GkOQXt1 z{@dbI_O}V=*AlJPE#`#JlHGl6d(OA6)$vnU<7IPKcI-Dtt>qi6^vZWRXst|Y?YK{O zwG)hGr}kH2Lx*#Jc^)0rb3+MrUy;3m%d`9OqL#OzSl8u7TvF`{{)`d$Td$x`YHtM5 zZJOdHH3d8B5VK6#NHv>O@qFawE>Z++x$_A5DY@D%F`mdEJ~{BP@U$xzDTK2Q53u&m ztmi20UX`xO2os+a$nW1&vRCq{2~k!#(-_j|-iE72Y@hEoLE}8*pa8CIs-cEicW5Hz zOPhwo=rXfV88ak6#YzzvU96(u$9($xzQ;skK*z|)5YtK}8L$|bfQ>!6!A4i5`YY1^QIF8zoRvi?Af#U7U-6I)K_ zr=&%c)CEMg;}ov&Q}5ud@Z-Oix(H2m#9l3)u25}%T#TsLjtu15?_6$s97NyV3Rm0q zZ~GP;Un<>hzpF~eimJv?HQ0_A`%#&tHDXA5aGnDE za{e;^0^g2`n*PgnH{OjI(PrRnj%{o0g3=}guwtt0sDKC3EVvrA*Jy(<4bJIgW+&#Q z`orn7t|-q+@?x!HcPSHRec>jDr}EcICeNBn?n!k91&QPEBDPCeVEV=*X6xljMtk;) z|0Mvwy+#FmxZ>PY%s zyCgB~1aZS)b@@cr#@5Dsj#(~rRt+N-!qnAWah41UtT}}Q7&8jx1elXJ{z}AaaXd1A zKC&=z-4YE|cj$_vU{^EYiD#x&%Th}sW?w0VGRub)lAbCX147W)Iw#V*6f?Ni$V$8(onss+hWlVFww(szs&kp)WPfo2c zQl2XUoJQnt|L4{Ic z(@ZNSzC>bS)wCowfwX2_7ixCsg>Mz9Int=eo-#?wl6lVgoYyG6mSrg#0fZT~LK zJ9YNnv54v$j#3AC0_N~onYI>>JmQ#t^wd5%r-c+{T2Wo!xKeG`7hqt;wAwUp_K++V+tsrmXfbPlN+};>xscZfQma)sUnHsn ziV7_sMq*8n%{bh5qEAcOgeIdxg8ejItx6&SbwRj*VqandvkV_6S|ovN0Wsb%kvCz0 ztz!n(&uUL2-%7m3*sZ9MBrB@GqPS73noq9%p|rBqjZ>_U`Y7Y-@o?4Hr*jz&Xl&KC zxwv^-RKJ7=;<{|{dNk@<_0`rjasbo5`)9?KV~$nZbNo2cmM{`O(``K^EBs#EaDR9Q|2c8`)7kZp>+L_)e*ZlM`R6kIQ{wb* z7US<3!vA{`Qk|LhKi1-pFZS=sL`~c;H=;Td)0dQxo|TPG6ZgxwsQwQ<;FkySFA~4M z>i{*s?0jEO`){0owEvmw@Beu9{=#8?nHc|GY5y+E_>u?yQ`{9jY1NAhJ#fkCA9F zc@{lA8`EElU|!MHO`#v@L_G9WGokWo4(RpQ*<>P{~2Nelw(Oq{4gXoki7kf#iv%+NeSlgb8*RT7 zH99&Rp0%q>yRq23kf0(Vd~?>%F~8?|MF5l5)T6+-YY_uqS!OLTGCJk#9f817on|XE zZjK5*6WxQNvR$&!-Yb2~dZApB(jI-l2yS?_r$fzH|vzM%V z@*<_0!tt&hK;1fz)UzhrBd%;rNZ+V{Lp{oGD;Oj!DatZ{`QkJnaB7`0h^HoCdf8W-cC!@I+~tUH1+F#15WbiszaYr8O_C~MyFX4f#m z#>f(1%d+tdZ2N(gPSS)QLwM@{YwpU!pBWoO@1Q1U zn@dSu$xMW!D>0vKkbRUwp+bQ64wsNG;WNF~=iJDgl+SEc#7p4ic6wa?kRLHnd^ByowuwTzEk-P-$T>e-9PNlT5J<%5;9v=BTOtBKR}|b z19$P^J43Rcsw_kd$6TE8*pbXI!q}u!?s@FE+9Ooz_eib6IVS~;S2^3A9E`jst>>9D z#j?d~_7}>B5C(j`a;91cQ_X?5ysGXClPVpOi1=99DMRUJySo)vjG-B5IoW)fur6Rw0C-$Uh0J^L*@dg@Vac=Q>?tMiF%f{)}*)g!xRRgNYFXaq0B`FrYNibP9L zV7-2RJqcGUfdJ6HZ9t|InmlUEV5_|b+n8UI!&<6C5;`y;SVphC1#3So zuRtQI3i9VYBioE4Sd8?7efaDq2}8LD^AapC`Z)xrDoOswu)bHZ6=Cm{|5NFL%AqfmVF07AHpUIL=Ruu0CYXNxIvuR(RT^Y6YicXsHFK zjDv6y#QC9*VoNiE`ss~pbQ|?`mw8o|3iwg#P-?LcJzo<`ceK?UXxEQ`*D*D!TH7vCje69py7jbyr!{8iw%&1mmDYc+h=kmDb zMOpI*2FgYsGWi4D!1l$joUsFMkJf$GzrT^!>4Wboj@WZSIaNO|>aekjvwK0lN) ztZh&E7-Ugxur8+MJ%v>pCG=jCMdF`3*^wTyJ*;IG9oFJBLK)lHRCkJyRc94Q zaAm&eV_B7dyzeSHx}ELeiN&-b*rd8(j&pFFE%FL9_($LTN!f8h31Gx+5{G}&!CCY zUEnIW%0WqAg?1kgP0_oyRfd7_Y&Md3y}EVuN&?-f)aiNSzF0>GbqCdDusV2;+H%RA z{q5yr&XpWl){k_b;;K8)_FgAqwzfSnyGfj-PEVfz6Qxw{nhpB&?CK1m5*tW3n?Q<; zmSf$FU2Po@sHv-E3xz_UATVfh(5HdaX>OGoG?Sb6$eI39jOv@ZMBfuY2@CxDh~^}? z_c}*|mEe1zEg?4-u4mh6#$NCjqSmNfZuJ++y@@pre1D)cS7!1?d@!!9Enb2>eppxK z@qA-iyA{bgIJe!lJsFVZ&22wI@|8inxab{|_twy|Ym@^L!Hsl*nw8o>BFPr)!4IN1 zbjoXKd{l?jd z1dio|SwA@%mh4GqJk2PybfBq;IapFdeJ&Rn1zE&qeUegM_9s}z*`x{4=NmQ02#``p zz-t8-q(j3?vclSBwJ-YkKk512WQw`5)nM^t2}6QjMBog(+~54!KyZCPXw}qwP1lxo z=zC;s(^aO8HQx8hnh7si`;(+0|2vNMzfOw(Wx=?40lDc$&c>MkV1gSXR{#PJfJg#i zt&ODk-({1dAa7Oi=JlbRnIUN?PPk14Q<2uF~j+R~D$mo@fX z+I_17?xe$4x=PUfu1&+3OYyi0ECo5?QNmHqmpI9B;(`#%gsb2LnD*z>C? z-_5$C#0PenzyMZ>iCyGxg0b^wk}>AaKI|m%j6#Qlc*rEnANG5!|=7$)iK5 zKEA>LH^&IWz7_>E3WY!^_$qwGfNb|)epR6SiD6MVz{$B~0|WLzc0NM|{ zc7Rd<4+aO+40!to#{$HV*ACDJ;GGvZ4h`r@Y;6PBWdh_6cri5hek-?pol}H1iW^_F z6$4^$c*l!`W4>t@3C95&D_iRVY7a2pz96+RXx=$MVvsn%QMa^<#9&b0JP(7zeq#qD zjOVoj5Uz;ImgfO!?ZxyyHb75P)(8PhAuSP|4uMaDajZ4~Ev}?h9}WcTQfGElz|Jddf-_r^}g)g5-5Y2;1`|^RH&}b}5SV_s) H#zgpEmCtty literal 20066 zcmagE1CV9S(l**Qr+eDA&1u`VZQHhO+wPvWZQHi(JM*4%?sxw9{u_6%z4xlhtjf%) zd{)GhD~ecFP?(yYh7pQ*;PCG7yyQN2W?%@45swbfM&BHYlM|0t*v!(=$o|jWQqR#y z(8$2X&nGV_IcPBO2s7u2qyO^V~=U!NOhXa?0XeX-5`H`jEP!5X=IUeN56b)Qd%kS;l0 z!!7TvR@b)QH?^IP$q+DoQXm981ezehzRW|ds(iXh@R~2l@DC_WYuHGj`=K<3*#gDl zSF(!TN_k_&tOrnUsfDp`k0euWCdLD)p#*!cbo11&)!+u1EcQ0}SOG> z+&U!D`m2wx<$xtbJC#C>iSCmh*zM6;J505)owLA7%Su)ha+F!huUAlKy25h5Ck(a0 zQ=s;m%jbLKy+9KHuPV<7kyKVj_E6MXYtHh|`4Yy8DTS8J4BOyW5{boEINTZ9tf#+o zvL%}`PG)Y177+4(@>i8(Bjp*ro^H4@m<-joHl)5;o8C?GPsH(9nqa7L$Q(CO8EIq7 zVrLv11iZrHymq5VA+VPLx z0q-x`{tS>ZGBnfUw{gYOp!*|Wp=ZEjWoOWW`h%~3`}sTXKd6+ow=qyKa>Ud4<4jN( zk5a97z8Oi#M5?RSEiVCYJA=90# zoijfO@Q5EU+#&JiI7EMf$c2gd$^r!kk~j;33L_(uD;p~I0E&bV7ziOl2l(qRL*60C z4)w|j3LS1nT0mY`G2d^ce-5i{uO2rqubNzYEw=&4e)xm*IjREsGUcfvU!C`A{y-WZ zcm_g40EANo?AbLo1`&A?1X}p$DlIS2;{6Voq7TuwDRJQ*>5yKPW-)u*RS?3daNb(NfcXoQKT zL-nn(NfrVmRCjUfss=R`}!^aer63CApZ+=8Mc$+qS%@r0qlj zcp_5Iz;5VDQUYkS0^>d0t(3XO1GobPXxz%Y#|gI7jvv^AA%%{(xUh5p%(0-MJYjv$ z@wXZ44rYH&8^3y2<|szx@go%b$!H{WSX@#pAviGs1D7&w`1y?Rz2C!bHaXf)m6_9n zdK+}2BzKYs85m~T9>nRV8cSmc73j0L>x0HZ3XblbWr1w!qyyj;#4g(+20miL1t35O zOk~aD4&-&Nfj|#6($!&F6<6aB!{GjP{Dupy8E(BR$w#1Y^aK-QXiuC^gaLdmO!v6;0XFgwfEJ`7K8vhakraFY10G})y zGVq{4SS&(3@9PXnDH=sUd7gkQ*8`y=MjM1yz*{c(3?38MC!kj#Vk*$Q9_$LhZ{O8g z4lD?fzVRJF*Pof-s6Feu;w>oLaLs+FyO=isR6#m@2@t#^5Wus-st}mBg8Z?nM1Y9+ z7I6fHKxIPdv2cZ$tx=+Jb_c*U^w%95x@EXD|o zey{#T`iphNs-%@X&X6pK8Bx>&%DOSS8nr2Pz$vv~B+?1UV!=eH@>lFm~+h-DI&Q(RJXXmUt(@O{L;ia%O_3o1RA~w=EBs;`9#5imilLzag(|uu-M=y#@h-{CHxo0|R zo!*~bXQ?sM{_Y@8dZn=vROo4%qq7kmsy}QCo$JEUp0SUq^XFS zE}3@8p3c%?`abvwIje08b8mSwf9yZwI!nMv$LPX9$56sZVH#paPq$7VO+QROW*TlZ zRflc@Q9o2aZqlYpO1qj+G$w9{V9%?nzAT}h=xM;gL;FyK#gWUYq#5iWgkalNE4~!YMZy9w4SuD ze*QKgwH$TqJYY4jxx%&j+44>aF99DX5;2@ul5mVM4XquTQlk>5Qqvs&tbc8OUBdl~ zJB&Mu8>!2=tHO)+Meh^n)A?=le);D3ZTVdWgbkz;@&u|7L>NRFoEw?}@&~9BNPjP2 zZ@WJ(?VJvg<^l>3|A&Bc5GN!vjMmRL2Q(Tgrb?0vw-zN=Z&w!(YJDL=fq{0>n31^= zk%%Z12^1IMQISERMG+|>51|wxi4;{TwT1)XNcBj(VlEV1%%2`a&h}-`bDI(&`xkpq z`&TrKn#m0pYxYftW#iM;u{ynKb`&_6xY*kW-(g67Wrvd04?3+bhak%d?IG{+$3v9I zQMXzzMjvq?DS`EYvw|rDT44ra)nY%z_JL&hns%J(b?*EQ$`WG}3&&ysi!pHEC(%rT3&7%H&mIg zrx0k*KI)I}h40A_Z^WN(t9hxsZcaP}UUn8e6}`LcI77H>Y@W8=tETCzX|K*y?`TyW zPTb|+HMgr=bSdj;*y+NX#TdpkGL+VLir1KnES8)Vp7~+Kq#t#jc{E>Utqrd)cW+zy z%mYRN4}x>!AY*m-j6BO)RPk92>TT>+_D`SZpS5+H_~`fuUW(qvO^XDH6b<*qI4yE4 zEVGufCbINJ#Shi(+YQ@%?mP;3AXS{oG8VMHfUx1S-|h{+;9h6LaIU(AdzGJ(e&#&= zo>321&s*A93j1BiV&);~v3=@0pM^bzP0f?>?fDS-l$PEwW+E{iH}x|!gFVN^`;LDo z=r$q@HRP9yBX?Wl!CY2#ZS~f;UrLSkZL59RdguFxQc8tMHM9(| zvd)FK*=f_YRp+T~!)Nk-Rdg4q*YTUyJMGp}o7Z9VcNQja1lZdZO_#^F_6FCf*VgAO zcp1Dl&xAYMr|GA`#eP01lnh07IZxH+;>)seS?9w=?y~N5L#RniaEk!#chYN(Oh4#! z*)*kaqwtwXmB@7DYviVT-NWF`cx1}_eo#00*YD?~*@_%*m-kK+{n3FNi`nxu9`UWx zF7~&jC#z$_lY@B|v+LrL@U4n2o+rQ0ymy0#;keTanHQO)oG2a_PsoouR}w4ZqwVLw z`}hA%u>UgHzr^IEQmUbO4@ z)8C}>|0&|2G^qdXJT(j3pJn`M(9{ay_xEg?fuUf|N) z2&jrs@V!>(w;R=;Lb&x;0TUlOkCH&URqtEdTuv%3D$6cQ&q~wr6)98v?h8R^M3b6y zUWoDVUuvRT*>00R@RgR%5#_5o?Po7Q%jzEi1Cj4drn|4k6j!}K^639WuVp5J0TsVFmZ(n<3PQF4K_# zw5mfiI&*=bSQ$U%vITk}Z29q8I15Z#m#)Gxx91fnp9-bA`MGs*5CRac%=Vigf2tlE zyUYFn(!0`M!b80Ba{HB)EWIp{4*Q5Xclyg%%EhQhvBZx-n1??zkauSj=L%;_zENT- zQDrnu>XSLcJ(omwm=V6e6en1Y3#PywP56;;0PU&^@{E=5lcaQ;XxWBewJQltYMUa< zt!4c{beXqUdc2W7K^Ac%pE*m~n8XPc^l8b74rzC3{lJxx5%BK(&@~;adE^$V)hQ@b zhwK=2>{XpzCu3^f;{MQ+0&c_&DJsow!WB8FB*sX1>=dmSF(_{X$S6J9Sa{@=%(x{} z8|sm!)W#Si4smu@DPkl=g$B*lk_%BAW)}>Tu#_E2nPDbE|a1~Koct~}k0lFWVO}0yP%ro+^HVUwH1B$)x zV0cp@ve0>;eM}JVcpboegNl4qbVa2A{vdiVfp8E|Ei!PLDZu+S!pn9BT?nN<<}tMr8}<5=HJVab;KLBvA9nLNR>0x+8LE_Q={z z-N?RJfD5C79{5+bB;K`o#_ejXsZu%=TW zcD|6#NKs{|N2u`3i%I<*&Ah(C^>D@x*c0o&!tv=meAj2>Gp_1z=ByBqCurCTY>oTU zKur*&5v2jA05ZLOx4_tz``t)0&v!JTk1>3Mtu!ocA|)xcRNL|$y-p2^*2SNb%b#LT zZJlG`#3HcZbR$t3*0hEZQKW>xKDO;Ul%i{r4aB0e;yucD2Ne7L%7Vd5Fs`!9O|#6= zxE5!a7Grr9XCh2Qr&4$4Q}qNU=*W%9BGAPRg7;GKF&G!p>@c$2)(0Ilovmw9T z2CbLru>MQtmz71qa-+4$ZJU$Th@Dhb#ge=M&fLNcZ9ZIUeF47nmu=SckiH{P*NfNW zzQ+Fa%i5k0Q^G?Nb3bPcYVq$Yf}e$zb$Y1eC1M)qG*+l(rdT59O%!Qc23Zy<2m9WVNFtOR^tr-UGjla-fSHmTIA)z6m2-^5R}jp56|l8u+8I z^M=$Ae(hOQr|pNkOd&a`>8GF9Yx6f)J*Gj--wnY=t)R0ZHPmw(%>H4B7KV&yTfP!Y37{nqV#wOdzr^f@pT9CGI$gIeTL}VSL z$u7+YH+Md-*E`aC0%1I3JhU7kM$}L)jp^eqT{WRXD*1%+QGeIBB$mwE?iSJv^NG46 zmwF6KF7=7P1N|E^qo4#yY}kbI0pFr2R61ZoE8@@t)34BIJ!YC-fx1AO;pun_)nXeS zQCE~zJuq6ob}c|J*4|9+u-|!~C)f`(8~3EC3SjpDA7QQ(U5MMk*8|Txq_+((>>t>2 zu`y`Dt!Z9apEPz%Wm4RUo*3EZeA-05)fvEaEyz1=5MI#NDNzo#)ZQ@zqHqCfUOEJF znONDHah}w#_)vM0CmOKsR)iQqB>kl|0?qkcCr&L-2=16(L0^Ck1G?0|do&@~0*7~R zUCAT$DZdb(5y5+E{3l2^4JHi`pfF}|r zMIpiweISyEF92vplWlWV1Ui=ODGn=zz4C&dINCo!_mza#otAq5cGlye=^?x!+6d>k z!RkPu?aO*`_XM6en|B4@3W(X0xrGlT7S|7P3y(`XK&g+rzPhWLRdz zOv9ewm|&$EKB@)1Ap0HA8Ld?fkR*93+wC;vX3e)9*%k8kl{q58L{u$aDb^9smi|Ie zCUGU@9hO)MFtieE+>d4x_}q=X8HNM=;R-hN!v2Qg1y1*ke&pYaaS@CNZrHNe-y+gG zLK-mUNSx_#!!He{#Q52||%7S{>-(8fuAecSoPu z9qYq@IP(^fs;{c#*t1?~c;)-tn)4d@8c$rAMQu3TK)J$-9JvQI=oKy%{uvG&Zj+RG z$IvsgkhX_wKhYtit!I(?YRXs%hjyfKhf~(a8sG3jzSm#VGw(TpW(-qCnv{!q2}q^m zYgsNOE=T?Yb59Zbf#iTMeq7R+FcOCv&|Sa~sx`|omuC-kR?Qw#ca3=hzdlXUfThl* zf%!#1Ri~qmK)=UC7Q4Kk8KC`Hj+B=yWp$`a>bg{x@faC86u&k8Q=KpM*%w~fpzi}z zrkC^g;u6yrJY z)QX`Wx+D8Ngd{*l;gTS=#X#f`mHWQgzkHV_4^TaU(_)VHz*D>TNyV_u`5ICO3n8~t zUV^F&!u5unfmzE`;N%E}lKO^|0S3B-nSG-gdifbbxQbhXj?$jf=0|@QrluCtk#Gx) z>p9Z^Ui}6$J^k?j>AW+3tI8slzG`UyV~ziJT@G=UYK+i>^$#+tIwY0d$US9?-1@NR^X6}F!y%snl8lY;Exe}{8};AKyNx>OF-}~ z@SljC6504My%^7McNnd~6GAz;GO4U+kTU!tD@qqoOgKwD26aEPHQ{8TdYmj0uKVZn zM}RlFK}wP7cwV*cOz&j8LUz4<0_(P`u!6f3mI|mA{HJTJD`U?{M4Olwm68NP6cbaD zkx_r592_4W4b&H?rN!9_laWCIc2A$%-P4rxTQ{grWR9B7oHt@LiI?f3`^!lil$HsE ziu~HMByt=jZf(R$hx<8CTY3@-ExX9nBBPnREEQ7B$3=YNb|}PEq$p#SOL`PYp_r6G z$P^s-k{2916HyXr74*>-ESrJALw+nW`U-2m1>hzZ2puTAoC$^>%@H`{I?wE&%<9Z^ zaI|yz%gU?NJDG(Q+-ms@uOdKm16{olo5P|u6Mt$bZyiz|bLZ#&m8^h4CVX#^EhfnZ z+h_PoYF$+nERLMz_+;4rTUs8vonTW>>IUfIcuW}O`98%fecuLK2dp}w<@9Q^yA^GS zvE#slN?>&=cN6oJWQYgT&?LR9wZKN}N8q_9qBY#x(HOJ_*Tk9dvZkN2cBgw0!Zzpc zM}G@0f=&Sq%njgb;@#rtMX8&UCV(>IHu>;`^)>bV7Zz8dlL@`oD*5Y_xs!hE`@j|w zCMqVA#|}7vC=VyBsCMZK=k_A4(fyH4vdZikj34_=*|O^oVAsx=>ReZCINCp6dZ}C4 z*V!G+^_wxJP9wK$^^8#@-OHt>MmNo>-8OnA^nVS_gtma7F)R4SH3siwQJ6AS099qH z0Rs3I!1gQMQUVY-?5Dp)FK)=H!`FoFhtr|{Q2zD@0Olv{#?@rx(kg&)=*C+Uy)^__ z+;esJ;l_y=Y-k`4FFgY+rW?X&)lAQDkfKe)R3Cth?eT|*@@vi7VqwB9)(5yG zvqV1{S<}VgRl1Kv26>&?3VC4VF=kcMa0l#^!abHMvSPC%pY~E#w{UG@(qmeQZedST zDz2G@+Wn+}v4G$bok=DcfMkJmQc1N=b&k?1$kf%dMJfC|GpFIR)6UUqbbLSPsx43` zQNzc4VO;_8&?gd?n%jYlrQf|36TJd}Hjo&zB}@z0_TDE(LkKz zM~2)0g0zsX0q8*oX5h_q;qtd2_6b7J@~r^(!s&4bw&Ts*&U}U7Z@}&{KqMhZ1IhRH zLF$9xU=pu^m-*62EYGaEM}1Ky)^McP}&8^A4* zJk;V*t~kEKd-#CKjb|hILpvHbcc|aYCJ|x*qGg5p*Wv(d!$TE7j2u$6g8ETOVjtoW zniOQrD!c%o3w0Uc7;+mRg*%LWmai}JJ(^3^IEP8kb(VII)6LOMvkfEDVecJfY#!xM ze>ZBIdCg{D$7Tv2KUZg22Y9rb)HN@9U|36*e)Uc%`&NLL$<8|$m7|`!_=+mlGWFwC z$pfYxqX)MzQ>Z_43U{;}x~o_+xwM%p>MWXGNiLFA!^7+;c_}d=qabGpIjAy4G^jaB z>po>AAfHhESn<*oiy)4Ub+N;74Gkw=t~lXx#LDt7TDhnCqP>H1p9UG?&38xS@oh8( ze8N|W=7#kUg45v33H+xW!7@V19>ap~Oe0~$=QxSS~ zwTKBa6=+Z3AtZ5Y<50n_n&;)Fbi*(r`S!y?xES@}yk<6!G$&lm`uJ%#n$zwzr0Fi? zd}l(AtmGO6XfRXHd-<#`i!-H&=8s>XYBgG>tcna3(2nsK-<(Z5QOa@$`dx=f83!$y zREFdjO}e9(lonSh z$`p^Sm-MSsxn{QFk)o4-RM7tj1`<-aInZFN7Q``yb~jcmu{I=xEPpyq7RN6XL#oFg zNQ4EeE<;Y}t>+IKL6<02C%q|kEbcS1nv5~nKfEQZYFXWsr3k62R zcE);0_XR_;zhKkk87Nm7IrrLbo_c!;dm%oKeOhhM|6Q$@bBC z(HK@S4CxwHJ6el{z$LQWA^)FH{yNGfN?J*S2J5gaWQ=K_K=P&w?%69(u^E0c?4#>D zmZ?@O`>Ub-8xn?yXtZCzk}?cnfU{YZdvDVAC4gb&<}%p>#t{w>dvE2`0(V-e?}rax zd_jFc3(|lQlltMpg5rfngZXN4#mn{f^?=ac&FkB&IYX+VAG-6Yu2tzKK3p7&W=$=c zC>06Rsf7oVBmwHcjgH91djd}u2f$k|)rJ7+D4{xvFF@Mh+Y#OFTgM)&di5Iab|HrJ z7&^b9^Vu@!SoqNhzY+)jI%DyK*-rnmyx zE0>;|fLFqj#8R9yzegAx>sX1}@J8K}?kmJDJCl(L0?ksT8XP?G&YsH=lhLeO#p;8Z zf9Ce~(NO5Qgku`D+XI;7iBCw4ERix{nC=uhroNT*z%n4vZJ0>V_$O&gngT=dRr}aEGNCpC@f@QfV%}N?zLY0IVr}}n=ra)> zwkCtsYhejQRFmIZ+nsjXxb4=RN_47NhD3uBPget#ygN1=@E__JG^p4pTlH5v3NF@G z2bys4=l-i;9^q89spxpLoTajJscqQ5oLJ@6pLD@*yihot&m1|gxoCNLx%(vi5%Tmw zo&z*V5YKbxK>3A|sq6s7)5#w1V2mbX--?n*Q8O4Ig_i7RE8r~6M>u5D;vqU%57c8W<2l&t#HeXlTuEIe zu$@>i7#Ld9f166Dt1&)Yj@o2i`#-Rhi#Y<#WrE2T)_XCy6gWyC)@dp0_}Z&b80KrG z9fwzSAeyRh^*ni-Ur&Hfx$q5stH^b!gKqc*C;sX$?A?}mVk^=_j`&oa+wP&m4{q(c zE|~hxRZVxz8gg>Itzn?`g5psmRq5ti?LUgz2H_s2L<)IZ1-h@AH+RbRYU++uiBI#2 zk&S|XdaAYZ70{kxjkrmglUT2B9?)LyDt?!(P|fN5^%Po5{-Ju&T6^~v`Ez%$uBi3A z>DXN`Z&MnR;PKj9_jW~M5Gwaa+WDm!Q#%X zm(sm2+fv4RBoSLdXSg%NbUtL;uJ`TG(hu zZ^!gU^lQs&qMDg=ilH%}MTnNv0dlQKm+OHOc_x4`oFr$_;O}v&EORMeScZal^n{7@ z(i_Z)J|mXH>5;mX#$OghC{w==kM?R=5+fX_?Cj+vcQ?xAdh!% znio0MI*ul2UJvn$kK?}x9B%RMrYjo^ImaJe>WEfo2SYJZONLnA$;KpVdez==U9Gikds;CBvAoml@152DL!5{CmL=c^# zh*)t=cH|{^oT$ZT>vuGj#V@m73kK68fcZim*T=TL2AlL1o5Jg-?*DvQm+1~Kb5 zrx9Av*qI8AkFKNBdTJ)3Z(9clAvTod+=&6!@q8$bj~U&`J@Ron6cA`3 zp0fPjI4BT#0f_$YC4Ix{u!y~EVnnJ8#1T#JiB=H&KZwh@$PR zq!7o+J}+Fot;@>0#Z5nX5W{ZX{U#)`{@iPth_1et7puOS>O3d86v!KUjwA+{)gD^s zo@~Awh7=Odj4Q@sXE#Xki;FKGfEU@}QG;7}tTr74OK8jtbVWlUafZXw)XzUWHRUgjx5zfIQ0C!E^{ zFC0J#I*9w)A;La_D!82b^{!pxXCR;18@0BnDiq<4 z{nQA@7SKAg{4sy*peL~scU=yG8EQtmY<2qEd3`^+vwE|`z2cPpN^BgrGG;x;*3yBi zzk*a__}7|NjNP+3>n|Kr5eRCs^hrwed><%>pUHR^bTs zl7(_E@t9xcV~xgZO5z+bYE6rVx^ZLZ#>_{x$@IZxsmMKujp!44SA)bxJgmnNJ} zRfI4G0y!5+Hj9^pf{@MK39p(--l2*IUXZuFc~M!frbWqe(cFBz-1OoapHEHQ8-8Es zsEvD1rLJg6U~y)ra*niCr>+WpY8CSeL>#xM1F*5LU!>&_KmvgIFbmKHI1?0zs;ABk4awF(kxliR-r@kR_L?_JMYiWD_}oLuz;Q;eh9wBR^7+vKstj(Q zR9HZcSH=qhtGtH>{LGjUFD3rX@2q&u%8bWS-Qki&1IR$GqHN*m>Qt8X_|=RLzP;o& zpKFq|Gs{Qe=T1PkWr9P7I;ND@Wz8Z4BL47-gAq>Rzz@9$@&c9sL&Fx|Cc%CZ+NZXA z2fsmE&#kokblL8cq9>ZM>Ge}?tkFb#Ko|zyEa12)MF}h7`f`|yukV2Xvh}i3-Ymry zpuu8@d!6};aHus~^=rUgjPBHa;79L<$N62MNrPZ%@eic)bzB?oqvQeb7gzmJ-KmGA z^19|g%8ez+{hF#Y$Z|O0cHd$v-g(#)1@iW`7kL>8JC9q2;mhcxE2Zov0UWGiY<#B0 zxON~P2u`i%5v2R;gk_8dUAEGQ|B~fmca2SAy9mhngI}J$cE?` zMWv+F{>L_E%dPZ@_*&KJS^zdVgUC{jvHtlF=+A*`>-wOgK2b7)Me4}v(CQfOKW8|V z1Yd_locDX&U`-do5_-0KE7!QoYhud(92CScdNo$1Rgi#zEdyno1V)$Vz$^v{o#&U= zMjWRQmo;mCg;X+}8$VD~Fe@iqC`K7q%n_@}NsCH^?eqLqw|Bc;<3?~j7fso`AZ7i2 zHl4F2Zo^LqV`@(2y0clPu)U38V$qRUf%Dj|l?~ZklcUX4&8GYG#-O6pwL7T< z+IX4)z$DfAJ^R{k{C;VDQ@{1}o!zX{f-?C_) zRaKSbOI*d+RZTxB(|*RyUx_l#hsm1364s3o(L4pB@QU)IaxR*cHDgIF(l_ zM|kp7sAIiGx!JVX48>g8bjbo4qs#V0={QBM9?4gqy7VR&RGyyi5qUw6*pI61)Hdg0v;9dx=l#Rk3G z{D~x8N~xCFtN(XS`t4BDfst=2pG5BxtL0=x3imICYOyj_G-q)<62RC!J6;TX>;UR{ z=J`wPXrVRfO@fg6l-FQS1N{XMfAStSDK(;;GQ8TuY_(o0My>uT0v9@xPI)kY<9Pl8 zISS9WL_}iD+FwQjyOM$Q4N%>)yi~pms#fuV@(Bk4JsuL0h`&>)QusZgFra%4_MTD+ z-+lq^qpZj@0p;{j9c=={Bqqag2KocBy@~BC16QLn)mx*fYOa14I!82=QqGMN9fK|{hWsxqFU>n zjaQyY9cYCgW%Rn1H;VL}VGaApo_exBB3|S%3^&zwwoU~LF~czhe)JQeMFjdBuN81Y zU#fe3C5NBEGfL`wzSu`jxAgEmeutaKoC9ER2A)|WDu(tnHF6DRdO}}B@-w+h4W+;; zHB3=WX|1_+T(x%CwrXGQ6lz+13Vd(<$SOyTTDRiZX#cRmkU?E*s#PP!WQ0$1q1#8BFDgHE=4efYB7jnm;RF4BDFz@gS436 z-q+g2t2p-*G&Y>*ku;oG8BaUC<&b?~YpJ-cf;^?WEM5iA%M9!z^ELY6JhG^qRh+yC zs0KK+7_kIg%05*B*o9i=dz(lklP3@1dFL1-lvM(D766Q#Cw)>LP|ea4#We!2RXOU_ z2r4Muu1ZleYpAs0=C|8a*dh6pzw*vbF2D{~n#C}g%veR+;qc8k%!B1FBB8PzdFdbQ zU>?PYi*v8~Ob`xUqx9uH0No%$+wR_*owm&Nu+w)qozcR9QG=E5q&A_R!0{fJ<|RVb zJ?KN{K%KNtlf|f}-ERfB!U}xRFk6Q1P5!0=Pu?RkwV?tlC=v*&pi-&D6c1pF(dL63=6X+d? zXlJS}4AFdf1FN@K6t5oeqP((Qt%4ZB$LATb=`KMqJac(z5&1@6Bv-+fT{)Y2(wn_{)zzAm=~Fk zY;Hi_C4h$^AHf}QLE;V@aYL^-gq|+^aV_lCxH}%Jqu1@A8YDma;|`X&@SVVTctL?F z_(X?X&#Zk9OB%LwO^Ucf9wHlvf(BfCydT?_wVto`rmqslBe>h1yJEQ8uD4=VYNly& zSKb%=&ZBADpH}XGUy$7L+;#6wa;&^BYJ7rPFa295!6lFM*q@@ARL)&zq{Bz(7~r0U zyQH<7zkPI!;=UGsIzf2OZeL+q^4zYCUx|D62w3F2~S9Eb||d^xBiK9wsQM#;KF67gt$-8=ejc21_E5 zc^Ys4t4FdhUy{E}-(mW5^!9K*rCZB{*`B6)CnbYN9IL+=*v8<}@GBq+Gp6O1Fs55h z!E$fXxZ0HA3Ck0Z66xFNKK(db3mgrEgAb7dl&%4!lRSv{6HO>(Px*5)DC2SS4ZBXo zKtx}iG!^dwoQ-fOI5YdKo5wVnY~X!xg|=4d_yRwH#d8J0roy5>yQ$-0`RV zt{_Ftu2;^e9G|BtBg%+Nnv6B~@62uL{t8oKgA+{YxOfb~5t{HA$|J4|Sq>@`(*4Zs z7XKyL@@{%NBo8EAV-kuX@3Pw2-y_1@NZar}wAm0jKV?kFYQkwoveI}OTw8a8O-*js zOjT|PXWPS@1zAzApD@GH?E}ym0%pjN&81@Td@5&%ZKVNq%^qy1kU^~^1u+RJoQUQ6 zjvlCTd_k*{=Pj28m?Omm7H6!|`*Q=^B<=^QXM$&rWH|?}V6ow_;j^IX@VkgS-tY{Q z28o);0qJz5oa&!IjLOIZ)^wxs8N;A!UExdjOX4tp+Zt9x^n}k|suXCG9hO3y{#Znv zAJY`!PNkR6%+Jx0T@96(uYj0QR1%Kg;*!58+f`?B zO0>-UFgvK193F%Joz2z$7?5OBt?``FU8(U*!hRDwLGLVumh9k$V#-b9c^Z1r=Fm>J z)rwRgc+X{nz2-WdC%_AhjxpmRFWTIK>w{O%+ahoMe38iWD(Y(0U)PjFz=7+y-V!$Kl;q#7o zCIv0MzoxjrTu@0db745a8nI%!j;gx4B3fn7LSBz>Lp{L8&rl^h^(Nt+m{z)xuwmj& zSt6()4`{f6rgl`HWky3+j6}r((24*_IY%uRs+?@n$?nk`T5WllIUgD_fQe>Gu}_e< zCR{e;UR1c`!u`!1HXsez49VEc^#WP)ejHHT9_u7lq4)))O{``Z2zaBr2WdXs+E;93 za9+aVH@PtpSmCjVjOLyX>a#m z(msBCJ$H+B!jwrB?LoUkZF-M;ToF`KbK^lacm4FW;dA{?gx z@rZz3cimq3c^tJ^3zkT|)MTRjlWhdiHhUV-ZAGAHDZ({w!v~%s9 zJSifLa)zV~ibw%T0V$RXC2MWHQW zr2?0Hak*TYn&h(U6<+|fG4EdgVb!fVtme%6+L1mrB4`6M26ZYD zgBct!a8NQsvJp_R+EH8>u#a9_^c6T$BYG9d@Qk~N22BdH%%nNF2B%dCq_%Mwk! z8=gfx@hKUH_Y`#$?V9C+Qe&2Jb`cn`1wh9}Y?jLyk^b}epyJS2T5x-8sJE{{6A;uQ zZ3IO2)nJFvP0WsF0p?FjSi#d5ue+-n3M~ohV*qi(@{Q6+zFw+ zyS*Tv>qSpf5${h(-9JLN@sYjTb1j&~vAhE7W~m^dXD#j2srd<7CHkmqEUCG%vaq1)cAD6gm3TMuITtvj=5;;( z+I~j*p082}b({p=bNA|dm;g9Vc_Uu-0elEgIQ1IVg5L4zbHoK*tWWes3g9+N+hA@NVJ|OPm`1D*u>+upcas);exv|%{zO2T6yunCK_GFpT)cMQj|c^ z35I08{3z?&)5T^f31AO}KjX&Bq4+9)<1OhBXI_>xj#InPeAK+;?DxEX+Vgi2uT`FDoKPg zqW#Jr2Bj+XDM6?#*2_^R-Lw>zx>>lSptUN64O|<3>iOE7A4%ry3}`^Nn%W$t?ZLYf zis`j^8V8}4`8C1z{}giO(NOMx9H%laW4Rf1EtP4R?96Vb5<)YRZEQuFMJD@BxRwaX zQcR>GBHJ`*RM&R7BpSxjj3(+PvX+!VcE9KLJNI{gw|jo)_dU;F@8|tI-*Z0S^ZfIi z^LnWWaZ`$d___qfRr6}Q=`=ECDngG*nP{5rqI=)vnnhYDV7{QwPmH$y2KMy4pg*vIet}+X`yd0&wXg`)$!ZB z1#0whAC<+0tn&zk8rW?(F{z4MUKiwx#T}<+b$X4g=rr}JH`?Dv~pF%7f-ib@V zZQLkM@yy<4eZp7IapT!%6&aWUa?I)FoOx^hkeHI88`A~s$}33JMtfw)m0gW$mR{;u ziBHeY9&nbDkg*JDU1y==uX?SNS~WbDS>N3eWpn>(YWTs*V{y-`kq2&aoo2N-N6lOP z=IfSAS9Y&DDY*x)00IN6yTM_R4UIoHSP_@Ea+*z&g|yWq+&I)}aOie!knjwm>1d1W zOk&rszP%EXEvJ)oF;6?~&Ch5FC3nB&e%2C_d;uo40R)!v{jNv-jnED!F zA~mCNt0loA-7tJ>C$%Ht2JTGY^33g&_h2zti85`0tX>{?BlqIVcVXDqiyV)UM%P#Y z-hDpQO6ua_qWL^K&&)5_{8k}#`otqg6<%+#hZ0iZIe3xxZKMja%WOY?yY6Ec030!PnyA3 zA4@=Vi}Y}{1`KR*+bFKa)ZeTW8hp3-WJs2a4pdt#V)RSLM1Dl&;CacM&*S+^qp(_+ z`dD9RI3!9uDmE|R{hgGDuZewyrk@5iQ)J}PyQ`T^Dj(j+?ZxjS$sEB+E-OzvbrD*b!uQX{u*hksK6F)2-{Hs-9NPxCAse#w4-(LY;^ACr3$h3{;)h|bN9 zepC>4*7o{Bthn+;cQF3OBw3@aaH=dsB=d!Xoe|ej zB(V34v%^jmFx2(7GqIs8N$&c=LX*hO`D&$A12rF*%iNVNW)^D2h$eK<{VHRRlP{sq z#w_4KNNg%k%o^(qsT7K-65dre0J0Go=VSzV{UNNIg%Lw`-_uoc;;TPQHi5qICKa)2$!9 zBBr_i0!UEoT4#q9N(mR&uIdoWWouRY$jIwaU8QpZ*_pPU54f+{AMg_Y$EN0E;4Z`DQj zW+OR~)vaSonaV7KIwtMy%R7B)4-mimHojc882eCPtY2b>3yU`q9WG$fKho$9G zE@%bn zmo-<)wG%?MDn8|Tgsi)mo=fiWes`Kh-%NwamNCL;i!;`>Ydp13J!v)f)yq#3i!Ky4Yd&m%V478%wl?x2qP0D;>{iXC>Fc!E`CQrLEhW8pXL zpNRO?7+#?4U=%{8Kqkd!Wsb0!?YroA2i3d01*eB!_hF|N(s&0%;BOFHx5%QNhdeuV z;7R@SNh?tAT!NTQxGVp&F-G%*Y4WGM%TJSS^&EajNtcR#ta(x4a-(aP|IU|FtV?yy zJv&IP=N`4*Yy&B6i4pUAXo&;8s2z=p zy{5#fEQ!x=2yv9V6qLqSXSQ{?;>bz4E6^Thtj}Dvmq49gp0`RDm8&^tSg$e6VSdQ{cDX3GHulAG-Hbq1PxlYx#D{{lhj*Ajg4lNiDH4 z!w3TELDArYl(e{bThQd^Y}m~S)kE)w@e`u4_aGZ*1{BI=(s<^Y5vma&`4-P&GmUN* zKBu?LUIW6_Cy!v91URC)r?`Gvu2b?^_>cr>86tG9U21zjso3%ee!2N*Z13;>2CiNM zgS4M$@{Ig2mNu7UH0N@Y9#|V?ZHb*(J@G=C=W44MnADb&+yO!(A50AuyM$$-*H`sdMGE!aQ&T<_XdjX&eU&Bnz;U} zSkY5Q5aQ>= zU5?gx-I&YBf^8VW>LPgkB&VvFcLSA^ochRxDhjD1<7Vup66%?p`#)iH#DD9a01Xp4 zgaqipRD3*01Uo2^5*Xy==L+yva}`=9rG|X;r9Ah(};>BtQ+Be^Wn& z2de^{FJKM}2A(($NS4G^j_V`gNGu!y*F_*u2$Zf19IgypKo93f{-Xl@-<7{O^zc9m z6=1UwNOAao78HxYpfFG>^v?_ffXUzPLVf?tP#7e@y#KNO%HVJSUj9DA>H;dNzxPmR zJwRjiU53T}V-L8FE-=6EdwL=13;bwzj@p2 zydo*U3;^+#pC5o&e`BDvEq$qe&~GPb3&7aN0V3WIgT$ab&=?XKK{@9^CLt+gj1GxR n#*)qyhZq*#K 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 literal 0 HcmV?d00001 -- 2.54.0 From 68ea8235e6028afd889c5f615288d7a49b3b9bb1 Mon Sep 17 00:00:00 2001 From: Mirko Holler Date: Mon, 26 Oct 2026 10:59:15 +0100 Subject: [PATCH 15/19] feat(flomni): rt_positions prints the average of the per-point standard deviations (sample vs FZP during the exposures) to the BEC CLI at the end of each scan; no ZeroDivisionError when no point was read Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QnSJvqxh9oDbshx8qXgeiX --- csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py | 25 ++++++++++++++++---- tests/tests_devices/test_rt_flomni.py | 17 +++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py b/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py index 3ef464b1..eca5bff9 100644 --- a/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py +++ b/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py @@ -877,11 +877,28 @@ class RtFlomniFlyer(Device): status.set_finished() self.progress.put(value=read_counter, max_value=number_of_positions_planned, done=True) - logger.info( - "Flomni statistics: Average of all standard deviations: x" - f" {self.average_stdeviations_x_st_fzp/read_counter*1000:.1f}, y" - f" {self.average_stdeviations_y_st_fzp/read_counter*1000:.1f}" + self._report_statistics(read_counter) + + def _report_statistics(self, read_counter: int): + """At the end of a scan: mean of the per-point standard deviations (sample vs FZP during each exposure, + RT_sampler) to the device-server log and to the BEC command line (Mirko 2026-10-26).""" + if read_counter == 0: + logger.info("Flomni statistics: no scan points read, no average of the standard deviations") + return + msg = ( + "flOMNI scan statistics: average of the standard deviations during the exposures" + f" ({read_counter} points, sample vs FZP): x" + f" {self.average_stdeviations_x_st_fzp/read_counter*1000:.1f} nm, y" + f" {self.average_stdeviations_y_st_fzp/read_counter*1000:.1f} nm" ) + logger.info(msg) + try: + self.controller.device_manager.connector.send_client_info( + msg, scope="flomni_scan_statistics", show_asap=True + ) + except Exception as exc: # pylint: disable=broad-except + # the statistics are information only: never fail the readout for them + logger.warning(f"Flomni statistics not sent to the client: {exc}") def _get_signals_from_table(self, return_table) -> dict: self.average_stdeviations_x_st_fzp += float(return_table[4]) diff --git a/tests/tests_devices/test_rt_flomni.py b/tests/tests_devices/test_rt_flomni.py index 811d6571..9d860184 100644 --- a/tests/tests_devices/test_rt_flomni.py +++ b/tests/tests_devices/test_rt_flomni.py @@ -4,6 +4,7 @@ import pytest from ophyd_devices.tests.utils import SocketMock from csaxs_bec.devices.omny.rt import RtFlomniController, RtFlomniMotor +from csaxs_bec.devices.omny.rt.rt_flomni_ophyd import RtFlomniFlyer from csaxs_bec.devices.omny.rt.rt_ophyd import RtError @@ -98,3 +99,19 @@ def test_feedback_enable_without_reset_raises(rt_flomni): rt_flomni.feedback_enable_without_reset() laser_tracker_on.assert_called_once() assert mock.call(b"l3\n") in rt_flomni.sock.put.mock_calls + + +def test_flyer_reports_average_stdev_to_the_client(rt_flomni): + flyer = RtFlomniFlyer(name="rt_positions", socket_cls=SocketMock, device_manager=rt_flomni.device_manager) + flyer.controller = rt_flomni + for row in (["0", "0", "1", "1", "0.002", "2", "2", "0.001", "0", "0"], + ["1", "0", "1", "1", "0.004", "2", "2", "0.003", "0", "0"]): + flyer._get_signals_from_table(row) + flyer._report_statistics(2) + send = rt_flomni.device_manager.connector.send_client_info + msg = send.call_args.args[0] + assert "x 3.0 nm, y 2.0 nm" in msg and "2 points" in msg + assert send.call_args.kwargs["show_asap"] is True + send.reset_mock() + flyer._report_statistics(0) # no points: nothing sent, no ZeroDivisionError + send.assert_not_called() -- 2.54.0 From 9edeb661f917fe6343369f9ff01184f1940c2667 Mon Sep 17 00:00:00 2001 From: Mirko Holler Date: Mon, 26 Oct 2026 11:01:34 +0100 Subject: [PATCH 16/19] feat(lamni): rt_positions prints the average of the per-point standard deviations and the average angle to the BEC CLI at the end of each scan (as flOMNI); the log line printed the sums Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QnSJvqxh9oDbshx8qXgeiX --- csaxs_bec/devices/omny/rt/rt_lamni_ophyd.py | 25 +++++++++++++++++-- .../tests_devices/test_rt_lamni_statistics.py | 23 +++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/tests_devices/test_rt_lamni_statistics.py diff --git a/csaxs_bec/devices/omny/rt/rt_lamni_ophyd.py b/csaxs_bec/devices/omny/rt/rt_lamni_ophyd.py index 22ea1cf4..5ac3a718 100644 --- a/csaxs_bec/devices/omny/rt/rt_lamni_ophyd.py +++ b/csaxs_bec/devices/omny/rt/rt_lamni_ophyd.py @@ -855,9 +855,30 @@ class RtLamniFlyer(Device): status.set_finished() self.progress.put(value=read_counter, max_value=number_of_positions_planned, done=True) - logger.info( - f"LamNI statistics: Average of all standard deviations: x {self.average_stdeviations_x_st_fzp}, y {self.average_stdeviations_y_st_fzp}, angle {self.average_lamni_angle}." + self._report_statistics(read_counter) + + def _report_statistics(self, read_counter: int): + """At the end of a scan: mean of the per-point standard deviations (sample vs FZP during each exposure, + RT_sampler) and the mean angle to the device-server log and to the BEC command line (Mirko 2026-10-26, + as flOMNI). Before, the log line printed the sums, not the averages.""" + if read_counter == 0: + logger.info("LamNI statistics: no scan points read, no average of the standard deviations") + return + msg = ( + "LamNI scan statistics: average of the standard deviations during the exposures" + f" ({read_counter} points, sample vs FZP): x" + f" {self.average_stdeviations_x_st_fzp/read_counter*1000:.1f} nm, y" + f" {self.average_stdeviations_y_st_fzp/read_counter*1000:.1f} nm;" + f" average angle {self.average_lamni_angle/read_counter:.4f}" ) + logger.info(msg) + try: + self.controller.device_manager.connector.send_client_info( + msg, scope="lamni_scan_statistics", show_asap=True + ) + except Exception as exc: # pylint: disable=broad-except + # the statistics are information only: never fail the readout for them + logger.warning(f"LamNI statistics not sent to the client: {exc}") def _get_signals_from_table(self, return_table) -> dict: self.average_stdeviations_x_st_fzp += float(return_table[5]) diff --git a/tests/tests_devices/test_rt_lamni_statistics.py b/tests/tests_devices/test_rt_lamni_statistics.py new file mode 100644 index 00000000..8c21f750 --- /dev/null +++ b/tests/tests_devices/test_rt_lamni_statistics.py @@ -0,0 +1,23 @@ +from unittest import mock + +from ophyd_devices.tests.utils import SocketMock + +from csaxs_bec.devices.omny.rt.rt_lamni_ophyd import RtLamniController, RtLamniFlyer + + +def test_lamni_flyer_reports_average_stdev_to_the_client(): + RtLamniController._reset_controller() + flyer = RtLamniFlyer(name="rt_positions", socket_cls=SocketMock, device_manager=mock.MagicMock()) + flyer.controller = mock.MagicMock() + for i, (sx, sy) in enumerate(((0.002, 0.001), (0.004, 0.003))): + row = ["0"] * 21 + row[0], row[5], row[8], row[19] = str(i), str(sx), str(sy), "0.5" + flyer._get_signals_from_table(row) + flyer._report_statistics(2) + send = flyer.controller.device_manager.connector.send_client_info + msg = send.call_args.args[0] + assert "x 3.0 nm, y 2.0 nm" in msg and "2 points" in msg and "average angle 0.5000" in msg + send.reset_mock() + flyer._report_statistics(0) # no points: nothing sent, no ZeroDivisionError + send.assert_not_called() + RtLamniController._reset_controller() -- 2.54.0 From 43e92f1e44f5ead3b9d5ea191cc9bdc6b5a3fe27 Mon Sep 17 00:00:00 2001 From: Mirko Holler Date: Mon, 26 Oct 2026 11:18:10 +0100 Subject: [PATCH 17/19] feat(flomni, lamni): end-of-scan statistics with the time split: scan time (kickoff -> end in the RT loop), exposure (points x requested exp_time x frames, passed with the kickoff), overhead per point Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QnSJvqxh9oDbshx8qXgeiX --- csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py | 20 ++++++++++++++++++ csaxs_bec/devices/omny/rt/rt_lamni_ophyd.py | 21 ++++++++++++++++++- csaxs_bec/devices/omny/rt/rt_ophyd.py | 20 ++++++++++++++++++ csaxs_bec/scans/flomni_fermat_scan.py | 6 +++++- csaxs_bec/scans/lamni_fermat_scan.py | 6 +++++- tests/tests_devices/test_rt_flomni.py | 12 +++++++++++ .../tests_devices/test_rt_lamni_statistics.py | 3 +++ 7 files changed, 85 insertions(+), 3 deletions(-) diff --git a/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py b/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py index eca5bff9..b2616c80 100644 --- a/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py +++ b/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py @@ -13,6 +13,7 @@ from ophyd_devices.utils.socket import SocketIO, raise_if_disconnected from prettytable import PrettyTable from csaxs_bec.devices.omny.rt.rt_ophyd import ( + format_scan_timing, RtCommunicationError, RtError, RtReadbackSignal, @@ -816,6 +817,11 @@ class RtFlomniFlyer(Device): ) self.average_stdeviations_x_st_fzp = 0 self.average_stdeviations_y_st_fzp = 0 + # end-of-scan statistics: kickoff parameters from the scan, start / end of the scan + self._exp_time = None + self._frames_per_trigger = 1 + self._t_kickoff = None + self._t_scan_end = None self.readout_thread = None self.scan_done_event = threading.Event() self.scan_done_event.set() @@ -856,6 +862,8 @@ class RtFlomniFlyer(Device): signals = self._get_signals_from_table(return_table) self.data.set(signals) + self._t_scan_end = time.monotonic() # scan finished in the RT loop (statistics) + if self.shutdown_event.wait(0.05): logger.info("Shutdown event set, stopping readout.") # if we are here, the shutdown_event is set. We can exit the readout loop. @@ -890,6 +898,8 @@ class RtFlomniFlyer(Device): f" ({read_counter} points, sample vs FZP): x" f" {self.average_stdeviations_x_st_fzp/read_counter*1000:.1f} nm, y" f" {self.average_stdeviations_y_st_fzp/read_counter*1000:.1f} nm" + + format_scan_timing(read_counter, self._t_kickoff, self._t_scan_end, self._exp_time, + self._frames_per_trigger) ) logger.info(msg) try: @@ -932,12 +942,22 @@ class RtFlomniFlyer(Device): ) self.readout_thread.start() + def configure(self, d: dict) -> tuple: + """The device server calls this with the kickoff parameters of the scan before kickoff(): + exp_time / frames_per_trigger for the end-of-scan statistics (not passed: no exposure part).""" + d = dict(d or {}) + self._exp_time = d.pop("exp_time", None) + self._frames_per_trigger = d.pop("frames_per_trigger", 1) or 1 + return super().configure(d) if d else ({}, {}) + def kickoff(self) -> DeviceStatus: self.shutdown_event.clear() self.scan_done_event.clear() while not self.controller._min_scan_buffer_reached and not self.shutdown_event.wait(0.001): ... self.controller.start_scan() + self._t_kickoff = time.monotonic() + self._t_scan_end = None self.shutdown_event.wait(0.1) status = DeviceStatus(self) status.set_finished() diff --git a/csaxs_bec/devices/omny/rt/rt_lamni_ophyd.py b/csaxs_bec/devices/omny/rt/rt_lamni_ophyd.py index 5ac3a718..19aa1660 100644 --- a/csaxs_bec/devices/omny/rt/rt_lamni_ophyd.py +++ b/csaxs_bec/devices/omny/rt/rt_lamni_ophyd.py @@ -13,7 +13,7 @@ from ophyd_devices.utils.controller import Controller, retry_once, threadlocked from ophyd_devices.utils.socket import SocketIO, SocketSignal, raise_if_disconnected from prettytable import PrettyTable -from csaxs_bec.devices.omny.rt.rt_ophyd import RtCommunicationError, RtError +from csaxs_bec.devices.omny.rt.rt_ophyd import RtCommunicationError, RtError, format_scan_timing logger = bec_logger.logger @@ -752,6 +752,11 @@ class RtLamniFlyer(Device): self.average_stdeviations_x_st_fzp = 0 self.average_stdeviations_y_st_fzp = 0 self.average_lamni_angle = 0 + # end-of-scan statistics: kickoff parameters from the scan, start / end of the scan + self._exp_time = None + self._frames_per_trigger = 1 + self._t_kickoff = None + self._t_scan_end = None self.readout_thread = None self.scan_done_event = threading.Event() self.scan_done_event.set() @@ -767,12 +772,22 @@ class RtLamniFlyer(Device): ) self.readout_thread.start() + def configure(self, d: dict) -> tuple: + """The device server calls this with the kickoff parameters of the scan before kickoff(): + exp_time / frames_per_trigger for the end-of-scan statistics (not passed: no exposure part).""" + d = dict(d or {}) + self._exp_time = d.pop("exp_time", None) + self._frames_per_trigger = d.pop("frames_per_trigger", 1) or 1 + return super().configure(d) if d else ({}, {}) + def kickoff(self) -> DeviceStatus: self.shutdown_event.clear() self.scan_done_event.clear() while not self.controller._min_scan_buffer_reached and not self.shutdown_event.wait(0.001): ... self.controller.start_scan() + self._t_kickoff = time.monotonic() + self._t_scan_end = None self.shutdown_event.wait(0.1) status = DeviceStatus(self) status.set_finished() @@ -834,6 +849,8 @@ class RtLamniFlyer(Device): signals = self._get_signals_from_table(return_table) self.data.set(signals) + self._t_scan_end = time.monotonic() # scan finished in the RT loop (statistics) + if self.shutdown_event.wait(0.05): logger.info("Shutdown event set, stopping readout.") # if we are here, the shutdown_event is set. We can exit the readout loop. @@ -870,6 +887,8 @@ class RtLamniFlyer(Device): f" {self.average_stdeviations_x_st_fzp/read_counter*1000:.1f} nm, y" f" {self.average_stdeviations_y_st_fzp/read_counter*1000:.1f} nm;" f" average angle {self.average_lamni_angle/read_counter:.4f}" + + format_scan_timing(read_counter, self._t_kickoff, self._t_scan_end, self._exp_time, + self._frames_per_trigger) ) logger.info(msg) try: diff --git a/csaxs_bec/devices/omny/rt/rt_ophyd.py b/csaxs_bec/devices/omny/rt/rt_ophyd.py index f456729c..839e7a50 100644 --- a/csaxs_bec/devices/omny/rt/rt_ophyd.py +++ b/csaxs_bec/devices/omny/rt/rt_ophyd.py @@ -14,6 +14,26 @@ from ophyd_devices.utils.socket import SocketSignal logger = bec_logger.logger +def format_scan_timing(n_points: int, t_start, t_end, exp_time=None, frames_per_trigger=1) -> str: + """Time split of a flyer scan for the end-of-scan statistics (Mirko 2026-10-26, BEC side only): scan time from + the kickoff to the end of the scan in the RT loop (wall clock of the device server), exposure = points x + requested exp_time x frames_per_trigger (not measured: the detector may expose longer), overhead = the rest + (positioning, trigger, detector readout). '' when the times are missing.""" + if not n_points or t_start is None or t_end is None or t_end <= t_start: + return "" + total = t_end - t_start + txt = f"; scan time {total:.1f} s ({total / n_points * 1000:.0f} ms/point)" + if exp_time: + expo = n_points * float(exp_time) * int(frames_per_trigger or 1) + over = total - expo + txt += ( + f": exposure {expo:.1f} s ({expo / total * 100:.0f} %, requested {float(exp_time) * 1000:g} ms" + f" x {int(frames_per_trigger or 1)}), overhead {over:.1f} s ({over / n_points * 1000:.0f} ms/point:" + " positioning, trigger, detector)" + ) + return txt + + class RtCommunicationError(ControllerCommunicationError): pass diff --git a/csaxs_bec/scans/flomni_fermat_scan.py b/csaxs_bec/scans/flomni_fermat_scan.py index f07cbca8..fc1b820c 100644 --- a/csaxs_bec/scans/flomni_fermat_scan.py +++ b/csaxs_bec/scans/flomni_fermat_scan.py @@ -238,7 +238,11 @@ class FlomniFermatScan(ScanBase): """ # send off the flyer - self.actions.kickoff(device=self.dev.rt_positions) + self.actions.kickoff( + device=self.dev.rt_positions, + # for the end-of-scan statistics of the flyer (exposure vs overhead) + parameters={"exp_time": self.exp_time, "frames_per_trigger": self.frames_per_trigger}, + ) # start the readout loop of the flyer status = self.actions.complete(device=self.dev.rt_positions, wait=False) diff --git a/csaxs_bec/scans/lamni_fermat_scan.py b/csaxs_bec/scans/lamni_fermat_scan.py index 743d8f39..817347eb 100644 --- a/csaxs_bec/scans/lamni_fermat_scan.py +++ b/csaxs_bec/scans/lamni_fermat_scan.py @@ -218,7 +218,11 @@ class LamniFermatScan(ScanBase): This is where the main scan logic should be implemented. """ - self.actions.kickoff(device="rt_positions") + self.actions.kickoff( + device="rt_positions", + # for the end-of-scan statistics of the flyer (exposure vs overhead) + parameters={"exp_time": self.exp_time, "frames_per_trigger": self.frames_per_trigger}, + ) status = self.actions.complete(device="rt_positions", wait=False) diff --git a/tests/tests_devices/test_rt_flomni.py b/tests/tests_devices/test_rt_flomni.py index 9d860184..b3ebe5e5 100644 --- a/tests/tests_devices/test_rt_flomni.py +++ b/tests/tests_devices/test_rt_flomni.py @@ -107,11 +107,23 @@ def test_flyer_reports_average_stdev_to_the_client(rt_flomni): for row in (["0", "0", "1", "1", "0.002", "2", "2", "0.001", "0", "0"], ["1", "0", "1", "1", "0.004", "2", "2", "0.003", "0", "0"]): flyer._get_signals_from_table(row) + flyer.configure({"exp_time": 0.1, "frames_per_trigger": 2}) # kickoff parameters of the scan + flyer._t_kickoff, flyer._t_scan_end = 100.0, 101.0 flyer._report_statistics(2) send = rt_flomni.device_manager.connector.send_client_info msg = send.call_args.args[0] assert "x 3.0 nm, y 2.0 nm" in msg and "2 points" in msg + assert "scan time 1.0 s (500 ms/point)" in msg and "exposure 0.4 s (40 %" in msg and "overhead 0.6 s (300 ms/point" in msg assert send.call_args.kwargs["show_asap"] is True send.reset_mock() flyer._report_statistics(0) # no points: nothing sent, no ZeroDivisionError send.assert_not_called() + + +def test_format_scan_timing(): + from csaxs_bec.devices.omny.rt.rt_ophyd import format_scan_timing + + assert format_scan_timing(0, 0, 1) == "" and format_scan_timing(5, None, 1) == "" + assert format_scan_timing(10, 0, 2.0) == "; scan time 2.0 s (200 ms/point)" # no exp_time: no split + txt = format_scan_timing(10, 0, 2.0, 0.1, 1) + assert "exposure 1.0 s (50 %" in txt and "overhead 1.0 s (100 ms/point" in txt diff --git a/tests/tests_devices/test_rt_lamni_statistics.py b/tests/tests_devices/test_rt_lamni_statistics.py index 8c21f750..3db16603 100644 --- a/tests/tests_devices/test_rt_lamni_statistics.py +++ b/tests/tests_devices/test_rt_lamni_statistics.py @@ -13,10 +13,13 @@ def test_lamni_flyer_reports_average_stdev_to_the_client(): row = ["0"] * 21 row[0], row[5], row[8], row[19] = str(i), str(sx), str(sy), "0.5" flyer._get_signals_from_table(row) + flyer.configure({"exp_time": 0.05}) # frames_per_trigger default 1 + flyer._t_kickoff, flyer._t_scan_end = 10.0, 10.5 flyer._report_statistics(2) send = flyer.controller.device_manager.connector.send_client_info msg = send.call_args.args[0] assert "x 3.0 nm, y 2.0 nm" in msg and "2 points" in msg and "average angle 0.5000" in msg + assert "scan time 0.5 s (250 ms/point)" in msg and "exposure 0.1 s (20 %" in msg send.reset_mock() flyer._report_statistics(0) # no points: nothing sent, no ZeroDivisionError send.assert_not_called() -- 2.54.0 From 76723df5e30b3fba5c0af1cc4f861375304caa50 Mon Sep 17 00:00:00 2001 From: Mirko Holler Date: Mon, 26 Oct 2026 11:59:03 +0100 Subject: [PATCH 18/19] docs(flomni): scan-mode profiles (high_res, high_res_1v5, normal, low_res) instead of fast_scan Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QnSJvqxh9oDbshx8qXgeiX --- ...ct_only-howto_calibrate_the_fzp_scanner.md | 2 +- ...t_only-howto_calibrate_the_fzp_scanner.pdf | Bin 88918 -> 89158 bytes .../local_contact_only-realtime_system.md | 2 +- .../local_contact_only-realtime_system.pdf | Bin 57342 -> 57759 bytes 4 files changed, 2 insertions(+), 2 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.md b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.md index e0214cbd..aafb81e9 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.md +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.md @@ -145,7 +145,7 @@ not have to be set by hand: | 1, FZP disabled (alignment, affine matrix) | `fzp_off` | `flomni.realtime_system.apply_profile("fzp_off")` | | 2, FZP-only calibration scan (clear `MatrixCorr` first: not part of the settings) | `fzp_calibration` | `apply_profile("fzp_calibration")` | | 3, back to operation | `production` | `apply_profile("production")` | -| afterwards, faster scans (calibrated over ±7 V) | `fast_scan` | `apply_profile("fast_scan")` | +| afterwards, fast scans with the FZP at ±9 V (scan modes, flOMNI `FeedbackLoop/scan_modes.md`) | `high_res`, `normal`, `low_res` | `apply_profile("high_res")` | Single values: `flomni.realtime_system.set(fzp_range_V_x=7, fzp_range_V_y=7)`, `set(move_beyond_um_x=0.3)`, `set(exposure_during_motion=1)`; refused with the reason if a rule objects diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.pdf b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-howto_calibrate_the_fzp_scanner.pdf index b23a54db08bcd0a9636031d5bb23ea2c0b8428a5..29bdcb4c6e1fb62e8c8c29df8b314df9723f94ef 100644 GIT binary patch delta 31432 zcmY&9-Gqs8xj-CRQ?V8xeMy} zJUT({)DYl&+K=1*I=Eiusjbq8>%E>bS`jxrs`C15^R900Ugg%T9?V(Uuq0U6KY5{o9utf8 z6wA8dMWNH{qs>~nPVDJtQ$H`t!0)?GMku6M7ir@4x#C+|xBF{G!c;ctnTH69Cr{bL z#gRH&p|AJCO(M*&v70ksGIY^TpVec@f8_QN$3eY6KU7o!8wsg1=?DxH$X0{Afkrgv zq67{AWwL3oVB( zgoC@FYDfa}Id>_puLo5?V<)bc=O2PoI0i=7i#ql{Wz$D|G)$rd-i_RTxX$wZo2)yS z06^{MtyqNIUCrrl&HrpSJ>g{@iH*awebr;E{i{49RmI)2ejYLo1yAqxzEEV;qz^yr zd5H2B_Mv6d-Jr?cZDqLP^-6RVf&s;rCzrmfNRc)F!T(~eKqn07xRK?-ukEfWVW|u= z^JoUiz`!fy<10=M;z&pVfL8c5?4jT8_!rc#Fdu3gG1t|RJ-2n+!^>d}vpcpQmYX)g z_v-;>JuYu_sx}keITkBQDQM($SCoKU$7x01TjFcO-C+#`5@ZWaztM%gcV2FuJ2k%6 zzV_L<#~MDzmc9_k^gcj7vSao{0!}daw22Z;u&d8m+5d$q^#>QN(ZEddxlQ)qn&Sf@HaF1{AMJO)B(_k5D_@P0L2$*~@a<*u{Kxt!!|Wnnc;{6kha& z*qC~J@A}<;6sR6;02!33UK{g6`ax0MDZOVMRtaFU+`SvRW0WRYi8*Yb`A)tnq=5PD zvNa4|?KY>+>JW7S=E#Cdjy4V7LHujjjo~o6%{XS@Cb0k9`k)%xciFbix|Q}KcXh+o z!BRGyB<#^8$Qj*vg~9;lV!=YghoNWn%HAT7rT!6t8OTZ;wAycQUKy3!@j7!f=g}bX zv30Vg{afM1SG6BkFB4;sf5vx%Z6`MqMCDF#4XSloV@#&~wu(P8}nDJo65nkjlm%h!zRpNEQ}Q{?%1ah{Ejr_lw%9k2cH#uw>KJ6VibK3{L@hO7#CZ)QxSq`D6&AS+VZ*1rRs4Vn|dp5+gaF9sBatk>iSa8gF!SVF?OrY8Z3MPM)#xit8 zw9#(d4U0!vphJ7Xd0{Hj3!}=E0faD+BLyx4NZ#xc`htnmLaw2fROY@8x`DhqJcWG5 zoO--atjPHvqeWY%ymdmL)8GjK?!$J5&E}GHmA0@~p3?UfI3?@ZVdkZm`&H^77ajmN z;YbGU(WbR_+s3tKt$!D(2k|R%fYj1{7#NS+Hnw0a4>Dv0Y`@j{2|Br*;dsnbiuK1K>dpSO3 z4l&|^W(jHpafHTLcMN>q3FiUo4K3nP_mnZ;L{e~OXmi{ zafr^~2rf!~cTZnf5Ji)pkX zY;I!!8~!;HQs^orcrqey4e61jv_{qjjluG=fN={ooJHagLMc-i>ORWiP35LczW?R*#1y_5gJp%*PhAG*rF_5TqB|N)<2@e~(H=V< zG+?#i?^;_}_jzTAyR&_}WdwA;-5Rh?>9-*RZ(LP-5MMP`ZFY+5!;C1#exC+c$7>1n zTm7qSB+eLSxb+lQVgmGSMm;I(Q5?G7#(~bew^9IzrKRGBM?HSmI*Ts$7v28+RzBvAFNvo}uiC`DL2yeBG2XK}Vs(k8D6RRVL%xp;0C`4Y zl?DAGh`U1$Sf7uFRf zyYm3~wEBdwQNN-5B{@Gs9U)XOEQ2EVEiW9^)Ls=0MFhVB(~L%1X*XQBphgv2Yvl0I z!3XOOqdIO>LNP~mF9`l=VXfx+AchFW{J$5t$h$1B#I{Hpz2BHCqQh&X2E_71e^a+< zB~R#8maG`V@a*CLs_B#_`FS##$@7%3(V|QxIX%qPMh+`mtH&0(lBj#0rsBZB+=rst zAK!LPV%L_S!mTIaJgAI`z)Xy`1+Rrmum=d0aqgc5W(lkF@()4}6nxFB3ho=#d*PtO z4m@OyNkCe>&GZTmvaG+gLZ>AWAyNZzo#zuFBfE-f0@OUm8A|S*aDL6*xnVI#c{<^% zVm65lsubB}N{{t=tJfoJj!-Atf5^)3Pj%UUZ?n9IfN7Ci5dJ0g7QR~d7r~zfu_dj% zk5n;*Wfofc=1SHZRu)NaBE>fe;l^F`c|F}Q=91y{!vCt*jIG9I+Vic3Df`C+jSW%#UAV&7W zvmin)y_`X|qAu2oVg46^CJqoKa_yuXwFRstP1w_W_c>~jK=K0n83u7SxT=iYlv+MW z?r=R7vq&37b-yw|Hj=+sH8l`xB0BLCKTEbIRy+}B4PL3?-ZBC{!F7G&dv}T1Tg&g! zjE#=^sK==C%ro^%Vg-u*{&WjTS5ljGyC00iihKaD5-QmtNA7CGZV4=iBwi=X>kH78 z<<}2zX1^Gw@CFbPTU zz$I}YMDzzngLF2an4NHWG76v^(cW@}`|#2E5RzdC8%JYnqLO#!N9&Q&5r4Fi9@p_* zkmfD3GLk~qX6yay9|Hmgsc=A-cc8?><$~4v!V#%ZkonQ6G!IgFiSj{cr17ZIPk|Jj z$*+?KthHWRpINV)L%S5;`q{5RwGDUw?9rsCup8%sjK%UKg?$R?huAIBf)s+=Ei<|@ z+0Z@1Z$e7$iq2~O`yNWi|bF8xgyOcZGo4arxcqiWEcOZb32fO4q0H9X4CM3;e68`rv=NB zeO_P42BwIyi?mc`?w}QX-}+Zs_`|L{aMyRE`F;A`1`j_OeuTY1EnC31%KVYjZzxy}Ep#PbTlxC^bq0 z>--}di!=liupL<&TNwL6{S6DyUXXq!H-`y}4n;}pKy@5h4*ajU|bKWY#A zKZHG)D;4<*9Ri>3N%~Z`!WOp!5RcRm`kaqoeiKg6z|lqDs1ZT({ybxJ)J3=i2&CBZ zC@6tCK~~y_V11lxmudXxm1mQ#kG?LA33l-)nd=m{!ER-Xuk;a%FAt*qGh=(k9u4

%&;Kpsjfu!e9p1?#`%Y*)h)7#UpsWNBF8`LG?k6 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 F{|hAlFKGY( delta 31160 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 diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.md b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.md index 0fbce70c..bb5551ec 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.md +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.md @@ -34,7 +34,7 @@ live and saved value, the loop parameters it sets, its rules). feedforward must be off while the FZP loop is off); `set(..., adjust=True)` changes the dependent settings the rule names (never the ones given) - `flomni.realtime_system.check(name=value)` — dry run -- `flomni.realtime_system.profiles()`, `apply_profile("production" | "fzp_off" | "fzp_calibration" | "fast_scan")` +- `flomni.realtime_system.profiles()`, `apply_profile("production" | "fzp_off" | "fzp_calibration" | "high_res" | "high_res_1v5" | "normal" | "low_res")` (scan modes 2026-10-26: flOMNI `FeedbackLoop/scan_modes.md`) - `flomni.realtime_system.unsaved()` — what differs from the loop's parameter files - `flomni.realtime_system.save_as_default()` — writes the live values into the loop's parameter files: the next loop start uses them. Only for a change that should stay (a backup of each file is kept on the loop machine). diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.pdf b/csaxs_bec/bec_ipython_client/plugins/flomni/docs/local_contact_only-realtime_system.pdf index 2d1f3c692428df5d75c4503f442b2f83249c1c16..d170eb4499d65db812ce848093938626fe4a5ec5 100644 GIT binary patch delta 19001 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 azR(CHfI$<>C$t4n5C{|`E311&5A=U5lZ(#) delta 18603 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 Date: Mon, 26 Oct 2026 12:12:02 +0100 Subject: [PATCH 19/19] feat(flomni): flomni_loop_settings stores target_shaper_scale and jump_max_um_x/_y as own signals (scan-mode profiles) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QnSJvqxh9oDbshx8qXgeiX --- csaxs_bec/devices/omny/flomni_loop_settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/csaxs_bec/devices/omny/flomni_loop_settings.py b/csaxs_bec/devices/omny/flomni_loop_settings.py index 0a59324c..552d6667 100644 --- a/csaxs_bec/devices/omny/flomni_loop_settings.py +++ b/csaxs_bec/devices/omny/flomni_loop_settings.py @@ -42,6 +42,7 @@ SETTINGS = ( "tracking_target_y", "tracking_target_z", "psd_threshold_low", "interferometer_threshold", "interferometer_threshold_rot", "sample_hold_fzp_V", "sample_hold_err_um", "sample_hold_zero", + "target_shaper_scale", "jump_max_um_x", "jump_max_um_y", # group motion, scan-mode profiles (2026-10-26) ) RETRY_AFTER_FAILURE_S = 60.0 # web HMI not reachable: no new attempt for this long (a tomogram does not wait each time) -- 2.54.0