Add Daq.use_running_status_server as a master on/off switch
Previously the only way to stop using an already-configured status server was setting the private _status_server = None directly - which also throws away the configured URL, so re-enabling meant remembering and restoring it by hand. use_running_status_server (default True, matching current behavior) is checked first thing in the status_client property - the single choke point every status-server-backed callback in this class already goes through (append_start_status_to_scan, start_scan_monitoring, copy_aliases_to_scan, use_status_server(), ...), so gating it there covers all of them for free. _status_server itself is left untouched, so toggling this back to True brings back the same client, same base_url, with nothing to re-supply. Live-toggleable on an already-running session, no restart needed: bernina.daq.use_running_status_server = False Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -149,6 +149,8 @@ class Daq(Assembly):
|
||||
status_server_stale_timeout=20.0,
|
||||
status_server_async=True,
|
||||
scan_monitoring=True,
|
||||
use_running_status_server=True,
|
||||
speak_run_number=False,
|
||||
):
|
||||
super().__init__(name=name)
|
||||
self.channels = {}
|
||||
@@ -259,6 +261,21 @@ class Daq(Assembly):
|
||||
# None (default) keeps the existing, unchanged local behaviour.
|
||||
self._status_server = status_server
|
||||
self._status_server_client = None
|
||||
# Master on/off switch, independent of _status_server itself: unlike
|
||||
# setting _status_server = None, toggling this off and back on again
|
||||
# does not lose the configured URL/client - status_client (and so
|
||||
# every status-server-backed callback, all of which go through it)
|
||||
# just returns None while it is False, exactly as if none were
|
||||
# configured. Live-toggleable (bernina.daq.use_running_status_server
|
||||
# = False), not just a constructor default, for the same "switch it
|
||||
# off mid-session without restarting the namespace" reason as
|
||||
# scan_monitoring above.
|
||||
self.use_running_status_server = use_running_status_server
|
||||
# Off by default: this is a per-instrument audible preference
|
||||
# (originated as bernina.py's _message_end_scan, which spoke the run
|
||||
# number via pyttsx3 on every scan end), not something every Daq
|
||||
# user wants. Live-toggleable like use_running_status_server above.
|
||||
self.speak_run_number = speak_run_number
|
||||
# Short timeout for /health and the admin routes (so an unreachable
|
||||
# server fails fast), long one for a snapshot - a full get_status()
|
||||
# fan-out over ~14k bernina channels takes 10-20 s.
|
||||
@@ -340,6 +357,7 @@ class Daq(Assembly):
|
||||
self.end_scan_monitoring,
|
||||
self.copy_scan_info_to_raw,
|
||||
self.end_scan_monitors,
|
||||
self.announce_run_number,
|
||||
]
|
||||
self.elog = elog
|
||||
|
||||
@@ -1331,6 +1349,29 @@ class Daq(Assembly):
|
||||
# scan.daq_run_number = runno
|
||||
scan._append(DetectorMemory, runno, name="daq_run_number")
|
||||
|
||||
def announce_run_number(self, scan=None, **kwargs):
|
||||
"""
|
||||
Speaks the finished run number out loud via pyttsx3. Opt-in
|
||||
(self.speak_run_number, off by default) and best-effort: any failure
|
||||
here (no audio device, engine busy, pyttsx3 not installed, ...) must
|
||||
never fail a scan, exactly like the timetool_data_monitor alarm in
|
||||
eco.bernina.bernina. Formerly bernina.py's module-level
|
||||
_message_end_scan/callbacks_end_scan, from before Daq owned its own
|
||||
scan callbacks.
|
||||
"""
|
||||
if not self.speak_run_number:
|
||||
return
|
||||
try:
|
||||
import pyttsx3
|
||||
|
||||
runno = scan.daq_run_number.get_current_value()
|
||||
e = pyttsx3.init()
|
||||
e.say(f"Finished run {runno}.")
|
||||
e.runAndWait()
|
||||
e.stop()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# get/set_dap_settings and get/set_detector_settings are NOT dead here by
|
||||
# accident -- they're implemented and used, just per-detector rather than
|
||||
# per-Daq: see eco.detector.jungfrau.Jungfrau.get_dap_settings/
|
||||
@@ -1349,7 +1390,15 @@ class Daq(Assembly):
|
||||
|
||||
@property
|
||||
def status_client(self):
|
||||
"""The configured StatusServerClient, or None if not using one."""
|
||||
"""The configured StatusServerClient, or None if not using one --
|
||||
either because none is configured (_status_server is None) or
|
||||
because use_running_status_server has been switched off. Every
|
||||
status-server-backed callback in this class goes through this
|
||||
property rather than _status_server/_status_server_client
|
||||
directly, so gating it here is enough to make the switch above
|
||||
cover all of them."""
|
||||
if not self.use_running_status_server:
|
||||
return None
|
||||
if self._status_server is None:
|
||||
return None
|
||||
if self._status_server_client is None:
|
||||
|
||||
@@ -78,6 +78,7 @@ def _daq(status_server=None, namespace=None, strict=False):
|
||||
daq.namespace = namespace
|
||||
daq._status_server = status_server
|
||||
daq._status_server_client = status_server
|
||||
daq.use_running_status_server = True
|
||||
daq.status_server_timeout = 5.0
|
||||
daq.status_server_snapshot_timeout = 60.0
|
||||
daq.status_server_strict = strict
|
||||
@@ -121,6 +122,7 @@ def test_status_client_is_built_from_a_url_string():
|
||||
daq = Daq.__new__(Daq)
|
||||
daq._status_server = "http://host:8091/"
|
||||
daq._status_server_client = None
|
||||
daq.use_running_status_server = True
|
||||
daq.status_server_timeout = 3.0
|
||||
daq.status_server_snapshot_timeout = 60.0
|
||||
client = daq.status_client
|
||||
@@ -132,9 +134,28 @@ def test_no_status_server_means_no_client():
|
||||
daq = Daq.__new__(Daq)
|
||||
daq._status_server = None
|
||||
daq._status_server_client = None
|
||||
daq.use_running_status_server = True
|
||||
assert daq.status_client is None
|
||||
|
||||
|
||||
def test_use_running_status_server_false_overrides_a_configured_server():
|
||||
"""The master switch (bernina.daq.use_running_status_server = False) --
|
||||
unlike setting _status_server = None, this must not lose the
|
||||
configured URL: flip it back to True and the same client (same
|
||||
base_url) comes back, not a freshly-built one asked all over again."""
|
||||
daq = Daq.__new__(Daq)
|
||||
daq._status_server = "http://host:8091/"
|
||||
daq._status_server_client = None
|
||||
daq.use_running_status_server = False
|
||||
daq.status_server_timeout = 3.0
|
||||
daq.status_server_snapshot_timeout = 60.0
|
||||
assert daq.status_client is None
|
||||
|
||||
daq.use_running_status_server = True
|
||||
client = daq.status_client
|
||||
assert client.base_url == "http://host:8091"
|
||||
|
||||
|
||||
def test_init_namespace_uses_server_and_skips_local_init(capsys):
|
||||
ns = FakeNamespace()
|
||||
daq = _daq(status_server=FakeStatusClient(), namespace=ns)
|
||||
@@ -253,6 +274,7 @@ def test_snapshot_and_health_get_separate_timeouts():
|
||||
daq = Daq.__new__(Daq)
|
||||
daq._status_server = "http://host:8091"
|
||||
daq._status_server_client = None
|
||||
daq.use_running_status_server = True
|
||||
daq.status_server_timeout = 10.0
|
||||
daq.status_server_snapshot_timeout = 180.0
|
||||
client = daq.status_client
|
||||
|
||||
Reference in New Issue
Block a user