Fix/automation jfjoch aarescan failures #197
+1
-1
@@ -26,7 +26,7 @@ dependencies = [
|
||||
"psi-pshell==2.1.0",
|
||||
"bec_lib>=3.130.3",
|
||||
"bec-ipython-client>=3.130.3",
|
||||
"aarescan-client==1.0.0rc5",
|
||||
"aarescan-client>=1.0.0rc14",
|
||||
"aarelcinfer-client==0.1.1a8",
|
||||
"matplotlib>=3.10.3",
|
||||
"httpx>=0.28.1",
|
||||
|
||||
+34
-69
@@ -956,7 +956,7 @@ class AareDAQ:
|
||||
sample_id = getattr(sample, "db_id", None)
|
||||
|
||||
logger.exception(
|
||||
"Operation failed",
|
||||
f"Operation {operation.value} failed with error {error}",
|
||||
extra={
|
||||
"operation": operation.value,
|
||||
"event_type": str(event_type),
|
||||
@@ -965,6 +965,8 @@ class AareDAQ:
|
||||
"additional_comment": additional_comment,
|
||||
},
|
||||
)
|
||||
logger.warning("cleaning up detector state after failed operation...")
|
||||
self._jfjoch.cancel(wait_for_done=True)
|
||||
|
||||
if sample is None or sample_id is None:
|
||||
logger.error(f"Error in {operation.value}: {error}")
|
||||
@@ -1236,6 +1238,15 @@ class AareDAQ:
|
||||
success=False, payload=payload, error=e, comment=additional_comment
|
||||
)
|
||||
|
||||
def _validate_grid_params(self, request: RasterGridRequest):
|
||||
self._devs.aerotech.validate_grid_scan(
|
||||
grid_elem_size_y_um=request.grid_size_mm.y * 1000,
|
||||
grid_elem_size_x_um=request.grid_size_mm.x * 1000,
|
||||
grid_elem_count_x=request.n_x,
|
||||
grid_elem_count_y=request.n_y,
|
||||
time_sec=request.exp_time_s,
|
||||
)
|
||||
|
||||
def _execute_raster_sequence(
|
||||
self, grid_request: RasterGridRequest, auto_center: bool = False
|
||||
) -> CompletedRasterGrid | None:
|
||||
@@ -1377,6 +1388,27 @@ class AareDAQ:
|
||||
)
|
||||
return None
|
||||
|
||||
def _validate_rotation_params(self, request: RotationScanRequest):
|
||||
total_time = request.exp_time_s * request.steps
|
||||
logger.debug("Validating rotation parameters with AareScan")
|
||||
if request.screening:
|
||||
if request.wedge_omega_deg is None:
|
||||
raise ValueError("Must supply a wedge angle for screening requests")
|
||||
self._devs.aerotech.validate_screening_scan(
|
||||
rotation_deg=request.incr_omega_deg,
|
||||
wedge_deg=request.wedge_omega_deg,
|
||||
time_sec=total_time,
|
||||
steps=request.steps,
|
||||
run_async=True,
|
||||
)
|
||||
else:
|
||||
self._devs.aerotech.rotation_scan(
|
||||
rotation_deg=request.steps * request.incr_omega_deg,
|
||||
time_sec=total_time,
|
||||
start_pos_deg=request.start_omega_deg,
|
||||
run_async=True,
|
||||
)
|
||||
|
||||
def _execute_rotation_sequence(
|
||||
self, rotation_request: RotationScanRequest
|
||||
) -> CompletedRotationScan | None:
|
||||
@@ -1394,6 +1426,7 @@ class AareDAQ:
|
||||
logger.warning(f"Exposure shorter than default of {min_exp_time} s! Adjusting.")
|
||||
rotation_request.exp_time_s = min_exp_time
|
||||
try:
|
||||
self._validate_rotation_params(rotation_request)
|
||||
return self._create_rotation_service().run(rotation_request)
|
||||
except JFJochCommunicationError as e:
|
||||
logger.error(f"Rotation sequence failed due to JFJoch Communication error: {e}")
|
||||
@@ -1990,74 +2023,6 @@ class AareDAQ:
|
||||
self._set_state(BeamlineStateEnum.SampleAlignment)
|
||||
raise
|
||||
|
||||
def _rotation(self, request: RotationScanRequest) -> CompletedRotationScan:
|
||||
omega_start = self.omega
|
||||
status = self.status
|
||||
|
||||
if request.exp_time_s < 0.004:
|
||||
logger.warning("Exposure time too short for PXII rotation scan")
|
||||
request.exp_time_s = 0.004
|
||||
total_time = request.exp_time_s * request.steps
|
||||
if self.sample is not None and self.sample.db_id is not None:
|
||||
self._aare.create_rotation_run(self.sample, request, status)
|
||||
|
||||
if not self._cfg.simulated_detector:
|
||||
try:
|
||||
self._jfjoch.wait_till_running(timeout=60.0)
|
||||
except Exception as e:
|
||||
self._raise_if_critical_jfjoch_detector_error(e, command="wait_till_running")
|
||||
raise
|
||||
try:
|
||||
if request.screening:
|
||||
self._devs.aerotech.screening_scan(
|
||||
rotation_deg=request.steps * request.incr_omega_deg,
|
||||
wedge_deg=request.wedge_omega_deg,
|
||||
time_sec=total_time,
|
||||
steps=request.steps,
|
||||
run_async=True,
|
||||
)
|
||||
else:
|
||||
self._devs.aerotech.rotation_scan(
|
||||
rotation_deg=request.steps * request.incr_omega_deg,
|
||||
time_sec=total_time,
|
||||
start_pos_deg=request.start_omega_deg,
|
||||
run_async=True,
|
||||
)
|
||||
|
||||
# Is this for helical scans...? do we do smargon scans?
|
||||
if request.start is not None and request.end is not None:
|
||||
smargon_time_step = request.exp_time_s / float(request.steps)
|
||||
pos_step = (request.end.sh_mm - request.start.sh_mm) * (
|
||||
1.0 / float(request.steps)
|
||||
)
|
||||
|
||||
for i in range(request.steps):
|
||||
self._devs.smargon.target = SmargonCoordinate(
|
||||
sh_mm=request.start.sh_mm + pos_step * i
|
||||
)
|
||||
time.sleep(smargon_time_step)
|
||||
|
||||
self._devs.aerotech.wait_till_done(timeout=int(round(total_time + 60, 0)))
|
||||
self._devs.aerotech_omega = omega_start
|
||||
|
||||
if self._cfg.simulated_detector:
|
||||
logger.warning("Detector in simulation mode, returning fake zero rotation result.")
|
||||
return self._build_fake_rotation_result(request)
|
||||
else:
|
||||
try:
|
||||
scan_result = self._jfjoch.wait_till_done(60)
|
||||
except Exception as e:
|
||||
self._raise_if_critical_jfjoch_detector_error(e, command="wait_till_done")
|
||||
raise
|
||||
return CompletedRotationScan(request=copy.deepcopy(request), result=scan_result)
|
||||
|
||||
except JFJochCommunicationError as e:
|
||||
logger.error(f"Exception during rotation scan related to JFJoch: {e}")
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Exception during rotation scan: {e}")
|
||||
raise
|
||||
|
||||
def measure_rotation(self, request: RotationScanRequest) -> CompletedRotationScan:
|
||||
"""
|
||||
Execute a rotation scan.
|
||||
|
||||
@@ -421,19 +421,12 @@ class RasterService:
|
||||
if self.ctx.sample is not None and self.ctx.sample.db_id is not None:
|
||||
self.ctx.deps.aare.create_gridscan_run(self.ctx.sample, request, status)
|
||||
|
||||
if not self.ctx.deps.cfg.simulated_detector:
|
||||
self.ctx.deps.jfjoch.wait_till_running(timeout=60.0)
|
||||
else:
|
||||
self.logger.info("Simulated detector mode enabled; faking jfjoch intilalisation.")
|
||||
self.ctx.deps.jfjoch.wait_till_running(timeout=60.0)
|
||||
|
||||
self.logger.debug(
|
||||
f"Starting grid scan with {request.n_x}x{request.n_y} points, exp time {request.exp_time_s}s"
|
||||
)
|
||||
if not self.ctx.deps.cfg.simulated_detector:
|
||||
# The detector move (sa2dc) is non-blocking; ensure it has reached
|
||||
# the requested distance before triggering the scan, so data is
|
||||
# collected at the dtz already reported to JFJoch/DB.
|
||||
self.ctx.deps.devs.set_dtz(request.dtz, wait=True)
|
||||
self.ctx.deps.devs.set_dtz(request.dtz, wait=True)
|
||||
self.ctx.deps.devs.aerotech.grid_scan(
|
||||
grid_elem_size_y_um=request.grid_size_mm.y * 1000,
|
||||
grid_elem_size_x_um=request.grid_size_mm.x * 1000,
|
||||
@@ -747,12 +740,9 @@ class RasterService:
|
||||
grid.omega_deg = geom.omega_deg
|
||||
|
||||
status = self.ctx.status
|
||||
if not self.ctx.deps.cfg.simulated_detector:
|
||||
self.logger.info("initialise detector")
|
||||
self.ctx.deps.jfjoch.measure_raster(grid, status)
|
||||
self.logger.info("detector initialised")
|
||||
else:
|
||||
self.logger.info("Simulated detector mode enabled; using fake raster result.")
|
||||
self.logger.info("initialise detector")
|
||||
self.ctx.deps.jfjoch.measure_raster(grid, status)
|
||||
self.logger.info("detector initialised")
|
||||
|
||||
if self.ctx.services.state is None:
|
||||
raise RuntimeError("RasterService requires services.state")
|
||||
|
||||
@@ -190,10 +190,7 @@ class AerotechController:
|
||||
start_pos_deg=start_pos_deg,
|
||||
run_async=run_async,
|
||||
)
|
||||
logger.debug(f"Payload sent to AareScan for rotation: {payload}")
|
||||
if self._simulated:
|
||||
return payload
|
||||
|
||||
logger.debug(f"Payload sent to AareScan for rotation execution: {payload}")
|
||||
try:
|
||||
return self._api.rotation_scan_post(payload)
|
||||
except Exception as e:
|
||||
@@ -204,6 +201,26 @@ class AerotechController:
|
||||
operation="POST",
|
||||
) from e
|
||||
|
||||
def validate_rotation_scan(
|
||||
self, rotation_deg: float, time_sec: float, start_pos_deg: float, run_async: bool = False
|
||||
):
|
||||
payload = RotationRequest(
|
||||
rotation_deg=rotation_deg,
|
||||
time_sec=time_sec,
|
||||
start_pos_deg=start_pos_deg,
|
||||
run_async=run_async,
|
||||
)
|
||||
logger.debug(f"Payload sent to AareScan for validating rotation: {payload}")
|
||||
try:
|
||||
return self._api.validate_rotation_scan_post(payload)
|
||||
except Exception as e:
|
||||
raise AerotechCommunicationError(
|
||||
f"Aerotech rotation scan failed validation: {e}",
|
||||
endpoint="rotation_scan_post",
|
||||
base_url=self._base,
|
||||
operation="POST",
|
||||
) from e
|
||||
|
||||
def grid_scan(
|
||||
self,
|
||||
grid_elem_count_y: int,
|
||||
@@ -221,14 +238,44 @@ class AerotechController:
|
||||
time_sec=time_sec,
|
||||
run_async=run_async,
|
||||
)
|
||||
logger.debug(f"Payload sent to AareScan for grid_scan: {payload}")
|
||||
logger.debug(f"Payload sent to AareScan for grid_scan execution: {payload}")
|
||||
if self._simulated:
|
||||
return payload
|
||||
try:
|
||||
return self._api.grid_scan_post(payload)
|
||||
except Exception as e:
|
||||
raise AerotechCommunicationError(
|
||||
"Aerotech grid scan failed",
|
||||
f"Aerotech grid scan failed: {e}",
|
||||
endpoint="grid_scan_post",
|
||||
base_url=self._base,
|
||||
operation="POST",
|
||||
) from e
|
||||
|
||||
def validate_grid_scan(
|
||||
self,
|
||||
grid_elem_count_y: int,
|
||||
grid_elem_size_y_um: float,
|
||||
time_sec: float,
|
||||
grid_elem_size_x_um: float,
|
||||
grid_elem_count_x: int,
|
||||
run_async: bool | None = False,
|
||||
):
|
||||
payload = GridRequest(
|
||||
grid_elem_count_x=grid_elem_count_x,
|
||||
grid_elem_count_y=grid_elem_count_y,
|
||||
grid_elem_size_x_um=grid_elem_size_x_um,
|
||||
grid_elem_size_y_um=grid_elem_size_y_um,
|
||||
time_sec=time_sec,
|
||||
run_async=run_async,
|
||||
)
|
||||
logger.debug(f"Payload sent to AareScan for grid_scan validation: {payload}")
|
||||
if self._simulated:
|
||||
return payload
|
||||
try:
|
||||
return self._api.validate_grid_scan_post(payload)
|
||||
except Exception as e:
|
||||
raise AerotechCommunicationError(
|
||||
f"Aerotech grid scan validation failed: {e}",
|
||||
endpoint="grid_scan_post",
|
||||
base_url=self._base,
|
||||
operation="POST",
|
||||
@@ -252,7 +299,7 @@ class AerotechController:
|
||||
if self._simulated:
|
||||
return payload
|
||||
try:
|
||||
logger.info("sending screening scan request to aerotech")
|
||||
logger.info(f"sending screening scan request to aerotech with payload {payload}")
|
||||
return self._api.screening_post(payload)
|
||||
except Exception as e:
|
||||
raise AerotechCommunicationError(
|
||||
@@ -262,6 +309,34 @@ class AerotechController:
|
||||
operation="POST",
|
||||
) from e
|
||||
|
||||
def validate_screening_scan(
|
||||
self,
|
||||
rotation_deg: float,
|
||||
wedge_deg: float,
|
||||
time_sec: float,
|
||||
steps: int,
|
||||
run_async: bool = False,
|
||||
):
|
||||
payload = ScreenRequest(
|
||||
rotation_deg=rotation_deg,
|
||||
wedge_deg=wedge_deg,
|
||||
time_sec=time_sec,
|
||||
steps=steps,
|
||||
run_async=run_async,
|
||||
)
|
||||
try:
|
||||
logger.info(
|
||||
f"sending screening scan request to aarescan for validation with payload {payload}"
|
||||
)
|
||||
return self._api.screening_post(payload)
|
||||
except Exception as e:
|
||||
raise AerotechCommunicationError(
|
||||
f"Aerotech screening validation failed: {e}",
|
||||
endpoint="screening_post",
|
||||
base_url=self._base,
|
||||
operation="POST",
|
||||
) from e
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
beamline = mx_beamline()
|
||||
|
||||
@@ -114,7 +114,7 @@ class JFJochWrapper:
|
||||
) from e
|
||||
|
||||
@needs_init
|
||||
def cancel(self, on_init: bool = False):
|
||||
def cancel(self, on_init: bool = False, wait_for_done: bool = False):
|
||||
try:
|
||||
self._api.cancel_post()
|
||||
except Exception as e:
|
||||
@@ -124,6 +124,8 @@ class JFJochWrapper:
|
||||
operation="POST",
|
||||
endpoint="cancel_post",
|
||||
) from e
|
||||
if wait_for_done:
|
||||
self._api.wait_till_done_post(1)
|
||||
|
||||
@needs_init
|
||||
def is_idle(self) -> bool:
|
||||
|
||||
@@ -425,7 +425,6 @@ class TellClient:
|
||||
|
||||
def get_mounted_sample(self) -> SampleDewarAddress | None:
|
||||
ret = self.get_setting("mounted_sample_position").strip()
|
||||
logger.debug(f"Get TELL setting 'mounted_sample_position' returned: {ret}")
|
||||
if not ret:
|
||||
return None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user