feat: remove all busy-state handling
This commit is contained in:
@@ -20,7 +20,6 @@ dependencies = [
|
||||
"requests",
|
||||
"pyepics~=3.5",
|
||||
"redis",
|
||||
"python-redis-lock",
|
||||
"fastapi",
|
||||
"uvicorn",
|
||||
"aaredb>=0.83.1",
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
from aaredaq.config import BeamlineConfig
|
||||
from aaredaqlib.beamline import MXBeamline
|
||||
|
||||
c = BeamlineConfig(MXBeamline.X06DA)
|
||||
c.state_busy = False
|
||||
+12
-52
@@ -2,16 +2,13 @@ import base64
|
||||
import io
|
||||
import json
|
||||
import time
|
||||
import traceback
|
||||
from dataclasses import asdict, is_dataclass
|
||||
from datetime import datetime
|
||||
|
||||
import numpy as np
|
||||
import redis
|
||||
import redis_lock
|
||||
from aarecommon.config.beamline import cfg_get
|
||||
from aarecommon.config.logger import setup_logger
|
||||
from aarecommon.errors.exception_handler import BeamlineBusyException
|
||||
from aarecommon.math.coordinate import AerotechCoordinate, Coordinate
|
||||
from aarecommon.models.auth import (
|
||||
BatonHolderInfo,
|
||||
@@ -40,6 +37,7 @@ from aarecommon.models.models import (
|
||||
ZoomModel,
|
||||
zoom_manager,
|
||||
)
|
||||
from redis.lock import Lock as RedisLock
|
||||
|
||||
from aare.daq.config_model import LocalContactConfigModel
|
||||
|
||||
@@ -344,7 +342,7 @@ class BeamlineConfig:
|
||||
return SessionsStateEnum.OwnedByElse
|
||||
|
||||
def try_set_active_session(self, session: int, expiry_sec: int) -> None:
|
||||
with redis_lock.Lock(self._client, f"{self._bl}:active_session_lock", expire=10):
|
||||
with RedisLock(self.redis, f"{self._bl}:active_session_lock", expire=10):
|
||||
active = self.active_session
|
||||
if active is None:
|
||||
self._client.set(f"{self._bl}:active_session", session)
|
||||
@@ -356,7 +354,7 @@ class BeamlineConfig:
|
||||
|
||||
# TODO finish setting this up!
|
||||
def try_extend_active_session(self, session: int, expiry_sec: int) -> None:
|
||||
with redis_lock.Lock(self._client, f"{self._bl}:active_session_lock", expire=10):
|
||||
with RedisLock.Lock(self.redis, f"{self._bl}:active_session_lock", expire=10):
|
||||
active = self.active_session
|
||||
if active is None:
|
||||
raise RuntimeError("There is no active session with given id. Try again later.")
|
||||
@@ -378,7 +376,7 @@ class BeamlineConfig:
|
||||
)
|
||||
|
||||
def end_active_session(self, session: int) -> None:
|
||||
with redis_lock.Lock(self._client, f"{self._bl}:active_session_lock", expire=10):
|
||||
with RedisLock.Lock(self.redis, f"{self._bl}:active_session_lock", expire=10):
|
||||
active = self.active_session
|
||||
if active is None:
|
||||
return
|
||||
@@ -388,9 +386,9 @@ class BeamlineConfig:
|
||||
|
||||
def force_set_active_session(self, session: int, expiry_sec: int) -> None:
|
||||
# Ensure that there is no active try-set for active session
|
||||
with redis_lock.Lock(self._client, f"{self._bl}:active_session_lock", expire=10):
|
||||
self._client.set(f"{self._bl}:active_session", session)
|
||||
self._client.expire(f"{self._bl}:active_session", expiry_sec)
|
||||
with RedisLock.Lock(self.redis, f"{self._bl}:active_session_lock", expire=10):
|
||||
self.redis.set(f"{self._bl}:active_session", session)
|
||||
self.redis.expire(f"{self._bl}:active_session", expiry_sec)
|
||||
|
||||
# ========== BATON SYSTEM ==========
|
||||
|
||||
@@ -463,7 +461,7 @@ class BeamlineConfig:
|
||||
# Can't transfer while beamline is busy
|
||||
# Add automation queue check here when you implement it
|
||||
# return not (self.state_busy or self.automation_queue_running)
|
||||
return not self.state_busy
|
||||
raise NotImplementedError("Checking for busy state is not implemented")
|
||||
|
||||
def execute_baton_transfer(
|
||||
self,
|
||||
@@ -477,7 +475,7 @@ class BeamlineConfig:
|
||||
Atomically transfer the baton to a new holder.
|
||||
Use existing active_session_lock for consistency.
|
||||
"""
|
||||
with redis_lock.Lock(self._client, f"{self._bl}:active_session_lock", expire=10):
|
||||
with RedisLock(self._client, f"{self._bl}:active_session_lock", expire=10):
|
||||
self._client.set(f"{self._bl}:active_session", to_session)
|
||||
self._client.expire(f"{self._bl}:active_session", expiry_sec)
|
||||
self.baton_holder = BatonHolderInfo(
|
||||
@@ -532,31 +530,11 @@ class BeamlineConfig:
|
||||
else:
|
||||
self._client.delete(f"{self._bl}:commissioning_mode")
|
||||
|
||||
# Beamline state management
|
||||
# Atomic check if beamline is busy and if not set state to busy
|
||||
def try_set_busy(self, timeout: int | None = None):
|
||||
with redis_lock.Lock(self._client, f"{self._bl}:move_state_lock", expire=10):
|
||||
if self.state_busy:
|
||||
raise BeamlineBusyException("Beamline is busy")
|
||||
self.state_busy = True
|
||||
if timeout is not None:
|
||||
self._client.expire(f"{self._bl}:busy", timeout)
|
||||
|
||||
def set_busy(self, target: BeamlineStateEnum, timeout: int | None = None):
|
||||
self.try_set_busy(timeout=timeout)
|
||||
curr_state = self.state
|
||||
if curr_state != target:
|
||||
self.state_busy = False
|
||||
raise RuntimeError("Beamline is not in a proper state")
|
||||
|
||||
def start_moving(
|
||||
self, target: BeamlineStateEnum, timeout: int | None = None
|
||||
) -> BeamlineStateEnum:
|
||||
self.try_set_busy(timeout=timeout)
|
||||
curr_state = self.state
|
||||
if target == curr_state:
|
||||
self.state_busy = False
|
||||
else:
|
||||
if target != curr_state:
|
||||
self.state = BeamlineStateEnum.Moving
|
||||
return curr_state
|
||||
|
||||
@@ -578,23 +556,6 @@ class BeamlineConfig:
|
||||
def state(self, state: BeamlineStateEnum):
|
||||
self._client.set(f"{self._bl}:state", state.value)
|
||||
|
||||
@property
|
||||
def state_busy(self) -> bool:
|
||||
return self._client.get(f"{self._bl}:busy") is not None
|
||||
|
||||
@state_busy.setter
|
||||
def state_busy(self, i: bool):
|
||||
|
||||
logger.debug(
|
||||
f"Busy flag switched to: {i}, at:\n{''.join(traceback.format_stack(limit=5)[:-2])}"
|
||||
)
|
||||
if i:
|
||||
self._client.set(f"{self._bl}:busy", "1")
|
||||
else:
|
||||
self._client.delete(f"{self._bl}:busy")
|
||||
|
||||
# Other beamline settings
|
||||
|
||||
@property
|
||||
def tell_mount_count(self) -> int:
|
||||
return int(self._client.incr(f"{self._bl}:tell_mount_count"))
|
||||
@@ -667,12 +628,12 @@ class BeamlineConfig:
|
||||
|
||||
@property
|
||||
def settings(self) -> BeamlineSettingsModel:
|
||||
with redis_lock.Lock(self._client, f"{self._bl}:settings_lock", expire=10):
|
||||
with RedisLock.Lock(self._client, f"{self._bl}:settings_lock", expire=10):
|
||||
return self._get_settings()
|
||||
|
||||
@settings.setter
|
||||
def settings(self, data: BeamlineSettingsModel):
|
||||
with redis_lock.Lock(self._client, f"{self._bl}:settings_lock", expire=10):
|
||||
with RedisLock.Lock(self._client, f"{self._bl}:settings_lock", expire=10):
|
||||
current = self._get_settings()
|
||||
updated_data = current.model_copy(update=data.model_dump(exclude_unset=True))
|
||||
self._client.set(f"{self._bl}:settings", updated_data.model_dump_json())
|
||||
@@ -1270,6 +1231,5 @@ if __name__ == "__main__":
|
||||
|
||||
cfg = BeamlineConfig(bl=mx_beamline())
|
||||
# cfg.allow_non_staff_request_from_staff = True
|
||||
# cfg.state_busy = False
|
||||
# fg.abr_meas_pos = AerotechCoordinate(at_mm=Coordinate(x=0.0,y=0.0,z=0.0))
|
||||
cfg.dtz_safe_position = 300.0
|
||||
|
||||
+67
-243
@@ -368,50 +368,26 @@ class AareDAQ:
|
||||
return self._devs.read_current_state_from_bec()
|
||||
|
||||
def restart_bec_worker(self) -> dict[str, object]:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._devs.restart_bec_worker(simulated=self._cfg.simulate_bec)
|
||||
return {"ok": True, "device": "bec", "simulated": self._cfg.simulate_bec}
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
self._devs.restart_bec_worker(simulated=self._cfg.simulate_bec)
|
||||
return {"ok": True, "device": "bec", "simulated": self._cfg.simulate_bec}
|
||||
|
||||
def restart_detector(self) -> dict[str, object]:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
beamline = MXBeamline.SIMULATED if self._cfg.simulated_detector else self._beamline
|
||||
logger.info(f"Restarting JFJoch wrapper with simulated={self._cfg.simulated_detector}")
|
||||
self._jfjoch = JFJochWrapper(beamline)
|
||||
return {
|
||||
"ok": True,
|
||||
"device": "detector",
|
||||
"simulated": bool(self._cfg.simulated_detector),
|
||||
}
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
beamline = MXBeamline.SIMULATED if self._cfg.simulated_detector else self._beamline
|
||||
logger.info(f"Restarting JFJoch wrapper with simulated={self._cfg.simulated_detector}")
|
||||
self._jfjoch = JFJochWrapper(beamline)
|
||||
return {"ok": True, "device": "detector", "simulated": bool(self._cfg.simulated_detector)}
|
||||
|
||||
def restart_tell(self) -> dict[str, object]:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._devs.restart_tell(simulated=self._cfg.simulate_tell)
|
||||
return {"ok": True, "device": "tell", "simulated": self._cfg.simulate_tell}
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
self._devs.restart_tell(simulated=self._cfg.simulate_tell)
|
||||
return {"ok": True, "device": "tell", "simulated": self._cfg.simulate_tell}
|
||||
|
||||
def restart_aerotech(self) -> dict[str, object]:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._devs.restart_aerotech(simulated=self._cfg.simulate_aerotech)
|
||||
return {"ok": True, "device": "aerotech", "simulated": self._cfg.simulate_aerotech}
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
self._devs.restart_aerotech(simulated=self._cfg.simulate_aerotech)
|
||||
return {"ok": True, "device": "aerotech", "simulated": self._cfg.simulate_aerotech}
|
||||
|
||||
def restart_smargon(self) -> dict[str, object]:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._devs.restart_smargon(simulated=self._cfg.simulate_smargon)
|
||||
return {"ok": True, "device": "smargon", "simulated": self._cfg.simulate_smargon}
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
self._devs.restart_smargon(simulated=self._cfg.simulate_smargon)
|
||||
return {"ok": True, "device": "smargon", "simulated": self._cfg.simulate_smargon}
|
||||
|
||||
def set_runtime_simulation(self, device: str, enabled: bool) -> dict[str, object]:
|
||||
device = str(device).strip().lower()
|
||||
@@ -1561,10 +1537,6 @@ class AareDAQ:
|
||||
def state(self) -> BeamlineStateEnum:
|
||||
return self._cfg.state
|
||||
|
||||
@property
|
||||
def busy(self) -> bool:
|
||||
return self._cfg.state_busy
|
||||
|
||||
@state.setter
|
||||
def state(self, target: BeamlineStateEnum):
|
||||
if target == BeamlineStateEnum.Moving:
|
||||
@@ -1575,11 +1547,7 @@ class AareDAQ:
|
||||
raise RuntimeError("Cannot explicitly move to busy state")
|
||||
|
||||
start = time.perf_counter()
|
||||
self._cfg.try_set_busy(timeout=300)
|
||||
try:
|
||||
self._set_state(target)
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
self._set_state(target)
|
||||
|
||||
end = time.perf_counter()
|
||||
|
||||
@@ -1675,22 +1643,17 @@ class AareDAQ:
|
||||
if -2000 < val < 2000:
|
||||
try:
|
||||
self._devs.aerotech_omega = val
|
||||
self._cfg.state_busy = False
|
||||
except Exception:
|
||||
logger.exception("Omega move timed out")
|
||||
self._cfg.state_busy = False
|
||||
else:
|
||||
self._cfg.state_busy = False
|
||||
logger.error("Omega has to be between -2000 and 2000 degrees")
|
||||
raise ValueError("Omega has to be between -2000 and 2000 degrees (for now)")
|
||||
|
||||
@omega.setter
|
||||
def omega(self, val: float):
|
||||
self._cfg.set_busy(BeamlineStateEnum.SampleAlignment)
|
||||
self._omega(val)
|
||||
|
||||
def omega_rel(self, val: float):
|
||||
self._cfg.set_busy(BeamlineStateEnum.SampleAlignment)
|
||||
curr_omega = self._devs.aerotech_omega
|
||||
self._omega(curr_omega + val)
|
||||
|
||||
@@ -1783,52 +1746,26 @@ class AareDAQ:
|
||||
self._devs.samcam_settings = s
|
||||
|
||||
def tweak_abr_meas_pos(self, c: AerotechCoordinate):
|
||||
self._cfg.set_busy(BeamlineStateEnum.SampleAlignment)
|
||||
try:
|
||||
new_meas_pos = AerotechCoordinate(at_mm=self._cfg.abr_meas_pos.at_mm + c.at_mm)
|
||||
self._cfg.abr_meas_pos = new_meas_pos
|
||||
self._devs.aerotech_pos = new_meas_pos
|
||||
self._saved_box = None
|
||||
self._cfg.state_busy = False
|
||||
except Exception:
|
||||
self._cfg.state_busy = False
|
||||
raise
|
||||
new_meas_pos = AerotechCoordinate(at_mm=self._cfg.abr_meas_pos.at_mm + c.at_mm)
|
||||
self._cfg.abr_meas_pos = new_meas_pos
|
||||
self._devs.aerotech_pos = new_meas_pos
|
||||
self._saved_box = None
|
||||
|
||||
def save_abr_meas_pos(self):
|
||||
self._cfg.set_busy(BeamlineStateEnum.SampleAlignment)
|
||||
try:
|
||||
self._cfg.abr_meas_pos = AerotechCoordinate(at_mm=self._devs.aerotech_pos.at_mm)
|
||||
self._devs.bec_worker.save_current_aerotech_position()
|
||||
self._cfg.state_busy = False
|
||||
except Exception:
|
||||
self._cfg.state_busy = False
|
||||
raise
|
||||
self._cfg.abr_meas_pos = AerotechCoordinate(at_mm=self._devs.aerotech_pos.at_mm)
|
||||
self._devs.bec_worker.save_current_aerotech_position()
|
||||
|
||||
def goto_abr_meas_pos(self):
|
||||
self._cfg.set_busy(BeamlineStateEnum.SampleAlignment)
|
||||
try:
|
||||
self._devs.aerotech_pos = self._cfg.abr_meas_pos
|
||||
self._cfg.state_busy = False
|
||||
except Exception:
|
||||
self._cfg.state_busy = False
|
||||
raise
|
||||
self._devs.aerotech_pos = self._cfg.abr_meas_pos
|
||||
|
||||
def create_sample(self, target: SampleShortInfo):
|
||||
curr_sample = self._cfg.current_sample
|
||||
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
if curr_sample is not None and curr_sample.location is not None:
|
||||
raise RuntimeError("Sample from TELL is loaded")
|
||||
|
||||
try:
|
||||
curr_sample = self._cfg.current_sample
|
||||
|
||||
if curr_sample is not None and curr_sample.location is not None:
|
||||
raise RuntimeError("Sample from TELL is loaded")
|
||||
|
||||
self._aare.create_manual_sample(target)
|
||||
self._cfg.current_sample = target
|
||||
self._cfg.state_busy = False
|
||||
except Exception:
|
||||
self._cfg.state_busy = False
|
||||
raise
|
||||
self._aare.create_manual_sample(target)
|
||||
self._cfg.current_sample = target
|
||||
|
||||
def check_tell_mount_start_conditions(self) -> None:
|
||||
self._devs.tell.validate_mount_start_conditions()
|
||||
@@ -1838,12 +1775,9 @@ class AareDAQ:
|
||||
|
||||
def park_and_dry(self, park=True):
|
||||
"""External API for dry and park only"""
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._execute_dry(park=park, unmount=False)
|
||||
self._cfg.state_busy = False
|
||||
except Exception as e:
|
||||
self._cfg.state_busy = False
|
||||
logger.error(f"Failed to park and dry: {e}")
|
||||
raise
|
||||
|
||||
@@ -1854,27 +1788,20 @@ class AareDAQ:
|
||||
logger.exception("Failed to turn off blower")
|
||||
|
||||
def initialise_smargon(self):
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._devs.smargon_initialize()
|
||||
self._cfg.state_busy = False
|
||||
except Exception as e:
|
||||
self._cfg.state_busy = False
|
||||
logger.error(f"Failed to initialise Smargon: {e}")
|
||||
raise
|
||||
|
||||
def initialise_detector(self):
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._jfjoch.initialize()
|
||||
self._cfg.state_busy = False
|
||||
except Exception as e:
|
||||
self._cfg.state_busy = False
|
||||
logger.error(f"Failed to initialise detector: {e}")
|
||||
raise
|
||||
|
||||
def recovery_unmount_sample(self) -> None:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._set_state(BeamlineStateEnum.RobotSampleExchange)
|
||||
self._devs.tell.check_enable_motion()
|
||||
@@ -1883,14 +1810,11 @@ class AareDAQ:
|
||||
self._devs.tell.unmount(wait=True, timeout=360.0)
|
||||
self._cfg.current_sample = None
|
||||
self._set_state(BeamlineStateEnum.SampleAlignment)
|
||||
self._cfg.state_busy = False
|
||||
except Exception:
|
||||
self._cfg.state_busy = False
|
||||
raise
|
||||
|
||||
@sample.setter
|
||||
def sample(self, target: SampleShortInfo | None):
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
logger.debug(f"Mount target {target}")
|
||||
|
||||
@@ -1909,10 +1833,8 @@ class AareDAQ:
|
||||
raise MountingFailed(f"Failed to {operation_name.lower()} {sample_name}")
|
||||
|
||||
logger.info(f"Sample operation completed: {target}")
|
||||
self._cfg.state_busy = False
|
||||
|
||||
except Exception as e:
|
||||
self._cfg.state_busy = False
|
||||
logger.debug(f"Failed to change mounted sample: {e}")
|
||||
raise
|
||||
|
||||
@@ -1946,11 +1868,9 @@ class AareDAQ:
|
||||
self._cfg.set_busy(BeamlineStateEnum.SampleAlignment)
|
||||
try:
|
||||
best_z = self._auto_focus(settings)
|
||||
self._cfg.state_busy = False
|
||||
return best_z
|
||||
except Exception as e:
|
||||
logger.error(f"Autofocus failed: {e}")
|
||||
self._cfg.state_busy = False
|
||||
raise
|
||||
|
||||
def auto_exposure(self):
|
||||
@@ -2057,18 +1977,15 @@ class AareDAQ:
|
||||
sample_log_context(self.sample), raster_request_log_context(request)
|
||||
),
|
||||
)
|
||||
self._cfg.try_set_busy(timeout=ceil(360))
|
||||
try:
|
||||
result = self._execute_raster_sequence(request, auto_center=auto_center)
|
||||
|
||||
if result is None:
|
||||
raise RasterScanException("Raster scan failed")
|
||||
self._set_state(BeamlineStateEnum.SampleAlignment)
|
||||
self._cfg.state_busy = False
|
||||
return result
|
||||
except Exception:
|
||||
self._set_state(BeamlineStateEnum.SampleAlignment)
|
||||
self._cfg.state_busy = False
|
||||
raise
|
||||
|
||||
def _rotation(self, request: RotationScanRequest) -> CompletedRotationScan:
|
||||
@@ -2157,7 +2074,6 @@ class AareDAQ:
|
||||
rotation_request_log_context(request, total_time_s=total_time),
|
||||
),
|
||||
)
|
||||
self._cfg.try_set_busy(timeout=ceil(total_time + 360))
|
||||
|
||||
try:
|
||||
result = self._execute_rotation_sequence(request)
|
||||
@@ -2166,16 +2082,13 @@ class AareDAQ:
|
||||
logger.error("Rotation scan failed, no result returned")
|
||||
raise DataCollectionException("Rotation scan failed, no result returned")
|
||||
self._set_state(BeamlineStateEnum.SampleAlignment)
|
||||
self._cfg.state_busy = False
|
||||
return result
|
||||
finally:
|
||||
try:
|
||||
if self._cfg.state_busy and self._cfg.state != BeamlineStateEnum.Maintenance:
|
||||
self._set_state(BeamlineStateEnum.SampleAlignment)
|
||||
self._set_state(BeamlineStateEnum.SampleAlignment)
|
||||
except Exception:
|
||||
logger.exception("Failed to restore SampleAlignment after rotation")
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
raise
|
||||
|
||||
@property
|
||||
def dtz(self) -> float:
|
||||
@@ -2187,7 +2100,6 @@ class AareDAQ:
|
||||
|
||||
@dtz.setter
|
||||
def dtz(self, val: float):
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
state = self._cfg.state
|
||||
|
||||
dtz_low = self._cfg.cached_dtz_low
|
||||
@@ -2198,27 +2110,22 @@ class AareDAQ:
|
||||
try:
|
||||
self.refresh_detector_metadata_cache()
|
||||
except Exception as e:
|
||||
self._cfg.state_busy = False
|
||||
raise RuntimeError(f"DTZ limits unavailable and refresh failed: {e}") from e
|
||||
dtz_low = self._cfg.cached_dtz_low
|
||||
dtz_high = self._cfg.cached_dtz_high
|
||||
|
||||
if dtz_low is None or dtz_high is None:
|
||||
self._cfg.state_busy = False
|
||||
raise RuntimeError("DTZ limits are unavailable")
|
||||
|
||||
if val < dtz_low or val > dtz_high:
|
||||
self._cfg.state_busy = False
|
||||
raise RuntimeError(f"dtz={val} outside limits {dtz_low} to {dtz_high}")
|
||||
|
||||
if state == BeamlineStateEnum.DataCollection:
|
||||
self._cfg.state_busy = False
|
||||
raise RuntimeError("Cannot set dtz during data collection")
|
||||
elif state == BeamlineStateEnum.SampleAlignment:
|
||||
self._devs.set_dtz(val, wait=False)
|
||||
|
||||
self._cfg.dtz = val
|
||||
self._cfg.state_busy = False
|
||||
|
||||
@property
|
||||
def smargon(self) -> SmargonCoordinate:
|
||||
@@ -2226,29 +2133,15 @@ class AareDAQ:
|
||||
|
||||
@smargon.setter
|
||||
def smargon(self, sc: SmargonCoordinate):
|
||||
self._cfg.set_busy(BeamlineStateEnum.SampleAlignment)
|
||||
try:
|
||||
self._saved_box = None
|
||||
self._devs.smargon_pos = sc
|
||||
self._devs.smargon_wait()
|
||||
self._cfg.state_busy = False
|
||||
except Exception:
|
||||
self._cfg.state_busy = False
|
||||
raise
|
||||
self._saved_box = None
|
||||
self._devs.smargon_pos = sc
|
||||
self._devs.smargon_wait()
|
||||
|
||||
def mark_beam(self, x_pxl: float, y_pxl: float):
|
||||
self._cfg.set_busy(BeamlineStateEnum.BeamLocation)
|
||||
try:
|
||||
self._cfg.mark_beam(x_pxl, y_pxl, self._devs.zoom)
|
||||
self._cfg.state_busy = False
|
||||
except Exception:
|
||||
self._cfg.state_busy = False
|
||||
raise
|
||||
self._cfg.mark_beam(x_pxl, y_pxl, self._devs.zoom)
|
||||
|
||||
def clear_mark_beam(self):
|
||||
self._cfg.set_busy(BeamlineStateEnum.BeamLocation)
|
||||
self._cfg.clear_mark_beam()
|
||||
self._cfg.state_busy = False
|
||||
|
||||
@property
|
||||
def sample_geometry(self) -> SampleGeometryModel:
|
||||
@@ -2290,24 +2183,17 @@ class AareDAQ:
|
||||
Returns:
|
||||
RasterGridRequest object representing the found bounding box, or None if failed.
|
||||
"""
|
||||
try:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
r = get_ml_bounding_box(
|
||||
mlbox=self._mlbox,
|
||||
sample=self.sample,
|
||||
sample_geometry=self.sample_geometry,
|
||||
filename=filename,
|
||||
upload_image=self._aare.upload_image,
|
||||
logger=logger,
|
||||
max_images=self.AUTO_RASTER_MAX_IMAGES,
|
||||
min_cell_size_mm=self.AUTO_RASTER_MAX_IMAGES,
|
||||
skip_if_exceed_max_image_threshold=self.AUTO_RASTER_SKIP_IF_EXCEED_MAX_IMAGE_THRESHOLD,
|
||||
)
|
||||
self._cfg.state_busy = False
|
||||
return r
|
||||
except Exception:
|
||||
self._cfg.state_busy = False
|
||||
raise
|
||||
return get_ml_bounding_box(
|
||||
mlbox=self._mlbox,
|
||||
sample=self.sample,
|
||||
sample_geometry=self.sample_geometry,
|
||||
filename=filename,
|
||||
upload_image=self._aare.upload_image,
|
||||
logger=logger,
|
||||
max_images=self.AUTO_RASTER_MAX_IMAGES,
|
||||
min_cell_size_mm=self.AUTO_RASTER_MAX_IMAGES,
|
||||
skip_if_exceed_max_image_threshold=self.AUTO_RASTER_SKIP_IF_EXCEED_MAX_IMAGE_THRESHOLD,
|
||||
)
|
||||
|
||||
def face_detection(
|
||||
self, steps: int = 14, step_size: int = 15, face_min_ratio: float = 0.3
|
||||
@@ -2323,14 +2209,9 @@ class AareDAQ:
|
||||
Returns:
|
||||
Dictionary containing face detection results, including found samples and fits.
|
||||
"""
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
result = self._execute_face_detection(
|
||||
steps=steps, step_size=step_size, face_min_ratio=face_min_ratio, report_error=True
|
||||
)
|
||||
return result.payload
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
return self._execute_face_detection(
|
||||
steps=steps, step_size=step_size, face_min_ratio=face_min_ratio, report_error=True
|
||||
).payload
|
||||
|
||||
@log_timing(logger, "Auto loop center")
|
||||
def auto_loop_center(self, sample: SampleShortInfo | None = None) -> float:
|
||||
@@ -2349,7 +2230,6 @@ class AareDAQ:
|
||||
"""
|
||||
start = time.perf_counter()
|
||||
try:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
if sample is None:
|
||||
if self.sample is None:
|
||||
raise LoopCenteringFailed(
|
||||
@@ -2361,12 +2241,8 @@ class AareDAQ:
|
||||
|
||||
if not self._execute_loop_centering(sample):
|
||||
raise LoopCenteringFailed
|
||||
|
||||
self._cfg.state_busy = False
|
||||
|
||||
except Exception:
|
||||
self._cfg.zoom_mode = ZoomModeEnum.User
|
||||
self._cfg.state_busy = False
|
||||
raise
|
||||
|
||||
finally:
|
||||
@@ -2564,23 +2440,16 @@ class AareDAQ:
|
||||
msg += "with an error"
|
||||
logger.error(f"{msg}, time taken {time.perf_counter() - start} seconds.")
|
||||
try:
|
||||
if self._cfg.state_busy:
|
||||
self._set_state(BeamlineStateEnum.RobotSampleExchange)
|
||||
else:
|
||||
logger.warning(
|
||||
"Skipping recovery transition to RobotSampleExchange: "
|
||||
"beamline is no longer busy. The busy key may have expired"
|
||||
"befor recovery could run"
|
||||
)
|
||||
self._set_state(BeamlineStateEnum.RobotSampleExchange)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"Failed to transition to RobotSampleExchange during error recovery"
|
||||
)
|
||||
raise
|
||||
|
||||
else:
|
||||
msg += " successfully"
|
||||
logger.info(f"{msg}, time taken {time.perf_counter() - start} seconds.")
|
||||
self._cfg.state_busy = False
|
||||
end = time.perf_counter()
|
||||
return end - start
|
||||
|
||||
@@ -2610,7 +2479,6 @@ class AareDAQ:
|
||||
try:
|
||||
logger.info("Automation-measure starting sequence")
|
||||
self._validate_automation_state(context="automation start")
|
||||
self._cfg.try_set_busy(timeout=self.AUTOMATION_BUSY_TIMEOUT_S)
|
||||
|
||||
self._validate_automation_state(context="after acquiring automation busy state")
|
||||
logger.info("Cancelling any pending jfjoch operations")
|
||||
@@ -2676,7 +2544,6 @@ class AareDAQ:
|
||||
logger.info(
|
||||
f"Automation-measure - loop Centering done at {time.perf_counter() - start}"
|
||||
)
|
||||
logger.debug(f"Automation-measure - current busy-state: {self._cfg.state_busy}")
|
||||
|
||||
face_detection_result = self._execute_face_detection(
|
||||
steps=7, step_size=30, face_min_ratio=0.3, report_error=True
|
||||
@@ -2937,8 +2804,7 @@ class AareDAQ:
|
||||
extra={"from_state": curr_state, "to_state": target},
|
||||
)
|
||||
|
||||
if not self._cfg.state_busy:
|
||||
raise RuntimeError("Beamline should be busy")
|
||||
# TODO: CHECK BUSY LOCK
|
||||
|
||||
if target == BeamlineStateEnum.Maintenance:
|
||||
self._cfg.state = BeamlineStateEnum.Maintenance
|
||||
@@ -3137,7 +3003,6 @@ class AareDAQ:
|
||||
extra={"from_state": curr_state, "to_state": target},
|
||||
)
|
||||
self._cfg.state = BeamlineStateEnum.Maintenance
|
||||
self._cfg.state_busy = False
|
||||
raise StateTransitionFailed(
|
||||
f"State transition failed: {curr_state} -> {target}. "
|
||||
f"Beamline moved to Maintenance. Original error: {e}"
|
||||
@@ -3408,32 +3273,16 @@ class AareDAQ:
|
||||
self._jfjoch.cancel()
|
||||
|
||||
def anneal(self, time_s: float):
|
||||
self._cfg.set_busy(BeamlineStateEnum.SampleAlignment)
|
||||
try:
|
||||
self._devs.anneal(time_s)
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
self._devs.anneal(time_s)
|
||||
|
||||
def mono_pitch_scan(self, plot: bool = False) -> None:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._devs.bec_worker.mono_pitch_scan_runner(plot=plot)
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
self._devs.bec_worker.mono_pitch_scan_runner(plot=plot)
|
||||
|
||||
def change_energy(self, value: float, plot: bool = False) -> None:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._devs.bec_worker.change_energy(value=value, plot=plot)
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
self._devs.bec_worker.change_energy(value=value, plot=plot)
|
||||
|
||||
def bec_load_user_macros(self) -> None:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._devs.bec_worker.load_user_macros()
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
self._devs.bec_worker.load_user_macros()
|
||||
|
||||
def bec_list_all_user_macros(self) -> list[str]:
|
||||
macros = self._devs.bec_worker.list_all_user_macros()
|
||||
@@ -3448,33 +3297,17 @@ class AareDAQ:
|
||||
return [str(item) for item in devices]
|
||||
|
||||
def bec_reinitialise_planner_and_position_devices(self, method: str = "auto") -> list[str]:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._devs.bec_worker.load_user_macros()
|
||||
return self._devs.bec_worker.reinitialise_planner_and_position_devices(method=method)
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
self._devs.bec_worker.load_user_macros()
|
||||
return self._devs.bec_worker.reinitialise_planner_and_position_devices(method=method)
|
||||
|
||||
def bec_save_current_bs_pos(self) -> None:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._devs.bec_worker.save_current_bs_pos()
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
self._devs.bec_worker.save_current_bs_pos()
|
||||
|
||||
def bec_save_current_collimator_pos(self) -> None:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._devs.bec_worker.save_current_collimator_pos()
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
self._devs.bec_worker.save_current_collimator_pos()
|
||||
|
||||
def bec_save_current_aerotech_position(self) -> None:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._devs.bec_worker.save_current_aerotech_position()
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
self._devs.bec_worker.save_current_aerotech_position()
|
||||
|
||||
def steer_beam_available(self) -> bool:
|
||||
return "beam_steering" in self._devs.bec_worker.dev
|
||||
@@ -3483,27 +3316,20 @@ class AareDAQ:
|
||||
"""Run the routine to move the beam to the sample location. Update the location if provided."""
|
||||
if "beam_steering" not in self._devs.bec_worker.dev:
|
||||
raise BECCommunicationError("Beam steering device does not exist in the BEC config.")
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
|
||||
try:
|
||||
if not self.shutter:
|
||||
raise AareException(
|
||||
"Shutter not open! Please open the shutter before running beam steering."
|
||||
)
|
||||
self._dispatch.bec_macros.auto_exposure()
|
||||
if x is not None:
|
||||
self._devs.bec_worker.dev.beam_steering.sample_loc_x_px.set(x).wait()
|
||||
if y is not None:
|
||||
self._devs.bec_worker.dev.beam_steering.sample_loc_y_px.set(y).wait()
|
||||
self._devs.bec_worker.dev.beam_steering.trigger().wait()
|
||||
finally:
|
||||
self._cfg.state_busy = False
|
||||
if not self.shutter:
|
||||
raise AareException(
|
||||
"Shutter not open! Please open the shutter before running beam steering."
|
||||
)
|
||||
self._dispatch.bec_macros.auto_exposure()
|
||||
if x is not None:
|
||||
self._devs.bec_worker.dev.beam_steering.sample_loc_x_px.set(x).wait()
|
||||
if y is not None:
|
||||
self._devs.bec_worker.dev.beam_steering.sample_loc_y_px.set(y).wait()
|
||||
self._devs.bec_worker.dev.beam_steering.trigger().wait()
|
||||
|
||||
def fluorimeter_take_spectrum(
|
||||
self, fm: FluorescenceSpectrumParameterModel
|
||||
) -> FluorescenceSpectrumOutputModel:
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
|
||||
try:
|
||||
self._set_state(BeamlineStateEnum.XrayFluorescence)
|
||||
|
||||
@@ -3513,11 +3339,9 @@ class AareDAQ:
|
||||
# TODO: Fill
|
||||
|
||||
self._set_state(BeamlineStateEnum.SampleAlignment)
|
||||
self._cfg.state_busy = False
|
||||
return None
|
||||
except Exception:
|
||||
self._set_state(BeamlineStateEnum.SampleAlignment)
|
||||
self._cfg.state_busy = False
|
||||
raise
|
||||
|
||||
def get_local_contact_config(self) -> LocalContactConfigModel:
|
||||
|
||||
@@ -731,34 +731,25 @@ async def bec_save_current_aerotech_position(token: str = Depends(oauth2_scheme)
|
||||
|
||||
|
||||
def initialise_aerotech(self):
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._devs.aerotech.home_aerotech()
|
||||
self._cfg.state_busy = False
|
||||
except Exception as e:
|
||||
self._cfg.state_busy = False
|
||||
logger.error(f"Failed to initialise Aerotech: {e}")
|
||||
raise
|
||||
|
||||
|
||||
def detector_take_pedestal(self):
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._jfjoch.take_pedestal()
|
||||
self._cfg.state_busy = False
|
||||
except Exception as e:
|
||||
self._cfg.state_busy = False
|
||||
logger.error(f"Failed to take detector pedestal: {e}")
|
||||
raise
|
||||
|
||||
|
||||
def initialise_detector(self):
|
||||
self._cfg.try_set_busy(timeout=360)
|
||||
try:
|
||||
self._jfjoch.initialize()
|
||||
self._cfg.state_busy = False
|
||||
except Exception as e:
|
||||
self._cfg.state_busy = False
|
||||
logger.error(f"Failed to initialise detector: {e}")
|
||||
raise
|
||||
|
||||
@@ -1500,7 +1491,6 @@ async def force_clear_busy(
|
||||
data = auth.parse_token(token)
|
||||
auth.check_jwt_staff_only(data)
|
||||
_validate_recovery_code(payload.confirmation_code)
|
||||
cfg.state_busy = False
|
||||
logger.warning(
|
||||
"Beamline busy flag cleared via protected endpoint.",
|
||||
extra={"session": getattr(data, "session", None)},
|
||||
@@ -1528,10 +1518,8 @@ async def force_maintenance_state(
|
||||
|
||||
sample_mounted = _sample_is_mounted()
|
||||
prev_state = cfg.state
|
||||
prev_busy = cfg.state_busy
|
||||
|
||||
auth.force_current_sesion(cfg, data)
|
||||
cfg.state_busy = False
|
||||
cfg.state = BeamlineStateEnum.Maintenance
|
||||
|
||||
logger.warning(
|
||||
@@ -1539,7 +1527,6 @@ async def force_maintenance_state(
|
||||
extra={
|
||||
"session": getattr(data, "session", None),
|
||||
"previous_state": getattr(prev_state, "name", str(prev_state)),
|
||||
"previous_busy": prev_busy,
|
||||
"sample_mounted": sample_mounted,
|
||||
},
|
||||
)
|
||||
@@ -1548,7 +1535,6 @@ async def force_maintenance_state(
|
||||
"ok": True,
|
||||
"sample_mounted": sample_mounted,
|
||||
"previous_state": getattr(prev_state, "name", str(prev_state)),
|
||||
"previous_busy": prev_busy,
|
||||
"new_state": BeamlineStateEnum.Maintenance.name,
|
||||
}
|
||||
|
||||
@@ -1573,12 +1559,6 @@ async def recovery_unmount_sample(
|
||||
|
||||
auth.force_current_sesion(cfg, data)
|
||||
|
||||
if cfg.state_busy:
|
||||
raise HTTPException(
|
||||
status_code=api_status.HTTP_409_CONFLICT,
|
||||
detail="Beamline is busy. Clear or recover the beamline before attempting recovery unmount.",
|
||||
)
|
||||
|
||||
status = daq.status
|
||||
if not getattr(status, "tell_connected", False):
|
||||
raise HTTPException(
|
||||
|
||||
@@ -76,8 +76,6 @@ def test_public_face_detection_uses_execute_face_detection(monkeypatch):
|
||||
from aare.daq.daq import AareDAQ
|
||||
|
||||
daq = object.__new__(AareDAQ)
|
||||
cfg = types.SimpleNamespace(try_set_busy=lambda timeout=360: None, state_busy=False)
|
||||
daq._cfg = cfg
|
||||
|
||||
daq._execute_face_detection = lambda **kwargs: FaceDetectionResult(
|
||||
success=True,
|
||||
@@ -88,4 +86,3 @@ def test_public_face_detection_uses_execute_face_detection(monkeypatch):
|
||||
|
||||
assert result["running"] is False
|
||||
assert result["samples"] == [{"angle": 45}]
|
||||
assert cfg.state_busy is False
|
||||
|
||||
Reference in New Issue
Block a user