From 12febe2e3a11eb21047ddf9cc6ed39e0f0e40d33 Mon Sep 17 00:00:00 2001 From: x12sa Date: Mon, 6 Jul 2026 11:23:30 +0200 Subject: [PATCH] rt_flomni: add ConstEmitter client access to RtFlomniController Expose the flOMNI ConstEmitter runtime constants (served over the control-loop TCP server, command letter 'e') to BEC users. These were previously baked into Orchestra module parameter files; they can now be read and set live. New RtFlomniController methods (all added to USER_ACCESS): emitter_get() -> dict of the four values emitter_show_all() -> PrettyTable printout emitter_set_tracking_target_y(val) -> "ey" emitter_set_tracking_target_z(val) -> "ez" emitter_set_intensity_threshold_laser(val) -> "et" emitter_set_psd_intensity_threshold_tracking_low(val) -> "el" emitter_get issues "es" and parses the CSV reply (targetpos_tracking_y, targetpos_tracking_z, intensity_threshold_laser, PSD_intensity_threshold_tracking_low), same pattern as the Ts/sr readouts. Setters use socket_put with a fixed %.4f value, same pattern as the pa setpoint command; they are fire-and-forget (the server does not ack sets). The rotation-loop laser threshold (intensity_threshold_rot_laser) is a startup constant on the server side and is intentionally neither read back nor settable here. Named methods were chosen over a single opaque command to match the existing laser_tracker_* / feedback_* convention and to self-document each value. Requires the corresponding flOMNI control-loop change (ConstEmitter module + 'e' command in CommunicationServer). Additive change; no existing behaviour modified. --- csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py | 65 +++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py b/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py index 4f8f183..c482ceb 100644 --- a/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py +++ b/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py @@ -45,6 +45,12 @@ 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", ] def __init__( @@ -391,6 +397,63 @@ 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]) + print(t) + + 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 @@ -929,4 +992,4 @@ if __name__ == "__main__": socket_cls=SocketIO, socket_host="mpc2844.psi.ch", socket_port=2222, device_manager=None ) rtcontroller.on() - rtcontroller.laser_tracker_on() + rtcontroller.laser_tracker_on() \ No newline at end of file