style: british->american spelling
CI / lint (push) Skipped
CI / test (3.11) (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped
CI / test (3.11) (pull_request) Failing after 44s
CI / test (3.12) (pull_request) Failing after 41s
CI / test (3.13) (pull_request) Failing after 42s
CI / test-with-beamline-plugins (pxii_bec) (pull_request) Failing after 47s
CI / test-with-beamline-plugins (pxi_bec) (pull_request) Failing after 49s
CI / test-with-beamline-plugins (pxiii_bec) (pull_request) Failing after 1m0s
CI / test-with-coverage (pull_request) Failing after 1m1s
CI / coverage-analysis (pull_request) Skipped
CI / lint (pull_request) Failing after 2m2s

This commit is contained in:
2026-08-24 13:33:14 +02:00
parent b2623f2284
commit 83a602da73
50 changed files with 252 additions and 249 deletions
+1 -1
View File
@@ -155,7 +155,7 @@ jobs:
echo "percent=${percent}" >> "$GITHUB_OUTPUT"
echo "Diff coverage: ${percent}%"
# Two artefacts on purpose: the full report is too big to post. The F5 WAF
# Two artifacts on purpose: the full report is too big to post. The F5 WAF
# in front of gitea.psi.ch rejects large POST bodies with an HTML 403, so
# the comment body is capped and the detail lives in the artifact.
- name: Build coverage summary
+3
View File
@@ -97,6 +97,9 @@ profile = "black"
line_length = 100
multi_line_output = 3
[tool.ruff.lint.isort]
split-on-trailing-comma = false
[tool.ruff]
line-length = 100
@@ -4,7 +4,7 @@ from importlib.resources import files
from aarecommon.config.beamline import MXBeamline
from aarecommon.math.beam_center import BeamCenterFromDetectorStage
from aarecommon.models.beam_centre import BeamCentre
from aarecommon.models.beam_center import BeamCenter
from aare.beamline_dispatch.protocols import AuthDispatch, BeamlineDispatch, Geometry
@@ -21,13 +21,13 @@ class DefaultAuthDispatch(AuthDispatch):
class DefaultGeometry(Geometry):
def __init__(self, beamline: MXBeamline) -> None:
super().__init__()
with open(files("aarecommon.config") / "beamline_configs" / "beam_centres.json") as f:
with open(files("aarecommon.config") / "beamline_configs" / "beam_centers.json") as f:
measured = json.loads(f.read())[beamline.value.lower()]
self._model = BeamCentre.model_validate(measured)
self._model = BeamCenter.model_validate(measured)
self._beamline = beamline
@property
def beam_centre_model(self) -> BeamCenterFromDetectorStage:
def beam_center_model(self) -> BeamCenterFromDetectorStage:
return self._model.model
+1 -1
View File
@@ -33,7 +33,7 @@ class BecMacros(ABC):
class Geometry(ABC):
@property
@abstractmethod
def beam_centre_model(self) -> BeamCenterFromDetectorStage: ...
def beam_center_model(self) -> BeamCenterFromDetectorStage: ...
class BeamlineDispatch(ABC):
+1 -1
View File
@@ -386,7 +386,7 @@ class AareWrapper:
result=raster_result,
sample_id=sample.db_id,
attach_image=True,
centre_of_mass=com,
center_of_mass=com,
raster_score=score, # TODO: Check whether AareDB needs to change for taking this input
start_pxl=start_pxl,
center_pxl=center_pxl,
+1 -1
View File
@@ -456,4 +456,4 @@ def cancel_baton_request(cfg: BeamlineConfig, data: TokenData) -> dict:
return {"error": True, "message": "You can only cancel your own request"}
cfg.clear_pending_baton_request()
return {"cancelled": True, "message": "Request cancelled"}
return {"canceled": True, "message": "Request canceled"}
+1 -1
View File
@@ -612,7 +612,7 @@ class BeamlineConfig:
def zoom_for_pixel_to_mm(self, target_pixel_in_mm: float) -> float:
"""Inverse of :meth:`pixel_to_mm`: the zoom at which one pixel covers
``target_pixel_in_mm`` millimetres.
``target_pixel_in_mm`` millimeters.
pixel_to_mm(z) = lens_factor / (b * exp(a*z)) =>
z = ln(lens_factor / (b * target)) / a
+15 -15
View File
@@ -693,7 +693,7 @@ class AareDAQ:
current_step=None,
steps=[
StepState(step=WorkflowStateKind.MOUNT, status=StepStatus.PENDING),
StepState(step=WorkflowStateKind.LOOP_CENTRE, status=StepStatus.PENDING),
StepState(step=WorkflowStateKind.LOOP_CENTER, status=StepStatus.PENDING),
StepState(step=WorkflowStateKind.RASTER, status=StepStatus.PENDING),
StepState(step=WorkflowStateKind.DATA_COLLECTION, status=StepStatus.PENDING),
StepState(step=WorkflowStateKind.FINAL, status=StepStatus.PENDING),
@@ -713,7 +713,7 @@ class AareDAQ:
def _step_display_name(step: WorkflowStateKind) -> str:
labels = {
WorkflowStateKind.MOUNT: "Mount",
WorkflowStateKind.LOOP_CENTRE: "Center",
WorkflowStateKind.LOOP_CENTER: "Center",
WorkflowStateKind.RASTER: "Raster",
WorkflowStateKind.DATA_COLLECTION: "Collect",
WorkflowStateKind.FINAL: "Paused/Finished",
@@ -1294,10 +1294,10 @@ class AareDAQ:
self._setup_datacollection(request=grid_request)
logger.debug(f"Is detector simulated? {self._cfg.simulated_detector}")
if not self._cfg.simulated_detector:
logger.info("initialise detector for raster")
logger.info("initialize detector for raster")
status = self.status
self._jfjoch.measure_raster(grid_request, status)
logger.info("detector initialised")
logger.info("detector initialized")
else:
logger.info("Simulated detector mode enabled; using fake raster result.")
@@ -1850,24 +1850,24 @@ class AareDAQ:
except Exception:
logger.exception("Failed to turn off blower")
def initialise_smargon(self):
def initialize_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}")
logger.error(f"Failed to initialize Smargon: {e}")
raise
def initialise_detector(self):
def initialize_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}")
logger.error(f"Failed to initialize detector: {e}")
raise
def recovery_unmount_sample(self) -> None:
@@ -2627,7 +2627,7 @@ class AareDAQ:
logger.info(f"mounting done at {time.perf_counter() - start}")
self._mark_progress_running(progress, WorkflowStateKind.LOOP_CENTRE, "Centering sample")
self._mark_progress_running(progress, WorkflowStateKind.LOOP_CENTER, "Centering sample")
local_contact_config = self.get_local_contact_config()
mount_to_center_sleep_s = float(local_contact_config.mount_to_center_sleep_s)
@@ -2652,7 +2652,7 @@ class AareDAQ:
except LoopCenteringFailed as e:
self._record_best_effort_step_failure(
progress=progress,
step=WorkflowStateKind.LOOP_CENTRE,
step=WorkflowStateKind.LOOP_CENTER,
error=e,
sample=sample,
code="LOOP_CENTERING_FAILED",
@@ -2661,7 +2661,7 @@ class AareDAQ:
if not centered:
self._record_best_effort_step_failure(
progress=progress,
step=WorkflowStateKind.LOOP_CENTRE,
step=WorkflowStateKind.LOOP_CENTER,
error=LoopCenteringFailed("Centering returned no result"),
sample=sample,
code="LOOP_CENTERING_FAILED",
@@ -2684,7 +2684,7 @@ class AareDAQ:
logger.info(f"Face Detection done at {time.perf_counter() - start}")
self._mark_progress_success(
progress, WorkflowStateKind.LOOP_CENTRE, "Centering complete"
progress, WorkflowStateKind.LOOP_CENTER, "Centering complete"
)
self._mark_progress_running(progress, WorkflowStateKind.RASTER, "Running raster")
@@ -3158,7 +3158,7 @@ class AareDAQ:
dtz_mm=dtz,
detector_size_pxl=(width, height),
pixel_size_mm=pixel_size_mm,
beam_center_pxl=self._devs.detector_beam_centre_px,
beam_center_pxl=self._devs.detector_beam_center_px,
detector_description=detector_description,
detector_serial_number=detector_serial_number,
poni_rot1_rad=-0.001396263,
@@ -3438,11 +3438,11 @@ class AareDAQ:
return []
return [str(item) for item in devices]
def bec_reinitialise_planner_and_position_devices(self, method: str = "auto") -> list[str]:
def bec_reinitialize_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)
return self._devs.bec_worker.reinitialize_planner_and_position_devices(method=method)
finally:
self._cfg.state_busy = False
+4 -4
View File
@@ -34,9 +34,9 @@ class BeamlineDevices:
BEAMLINE = beamline.value.upper()
self.tell = make_tell_client(beamline)
self.aerotech = aerotech.AerotechController(beamline)
logger.debug("initialising BEC worker")
logger.debug("initializing BEC worker")
self.bec_worker = BECClientWorker(beamline)
logger.debug("initialising BEC worker done")
logger.debug("initializing BEC worker done")
self._smargon = smargon.Smargon(beamline)
self.exp_shutter = ExperimentalHutchShutter(beamline=self._beamline)
# Personnel Safety System: gates whether the robot is allowed to move.
@@ -160,8 +160,8 @@ class BeamlineDevices:
return self._front_light.value
@property
def detector_beam_centre_px(self) -> tuple[float, float]:
beam_center_x, beam_center_y = self._dispatch.geo.beam_centre_model.predict(
def detector_beam_center_px(self) -> tuple[float, float]:
beam_center_x, beam_center_y = self._dispatch.geo.beam_center_model.predict(
self.dtz, self.dty
)
return float(beam_center_x[0]), float(beam_center_y[0])
@@ -45,5 +45,5 @@ def build_fake_raster_result(request: RasterGridRequest) -> CompletedRasterGridE
file_prefix=request.file_prefix, image_count=request.n_x * request.n_y, rotation=False
)
return CompletedRasterGridElem(
request=copy.deepcopy(request), result=result, centre_of_mass=None
request=copy.deepcopy(request), result=result, center_of_mass=None
)
@@ -48,7 +48,7 @@ class FaceDetectionService:
self._progress_emitter().emit_progress(payload)
return payload
def _centre_correction(self, model: MLBoxModel, tolerance: float = 0.2) -> None:
def _center_correction(self, model: MLBoxModel, tolerance: float = 0.2) -> None:
geom = self.ctx.runtime.sample_geometry
beam_y = geom.beam_location_pxl.y
beam_x = geom.beam_location_pxl.x
@@ -56,10 +56,10 @@ class FaceDetectionService:
y1 = model.box.top_y
y2 = model.box.bottom_y
centre_y = y1 + (y2 - y1) / 2
center_y = y1 + (y2 - y1) / 2
if beam_y != 0 and abs(centre_y - beam_y) / abs(beam_y) > tolerance:
coord = geom.picture_to_smargon(Coordinate(x=beam_x, y=centre_y))
if beam_y != 0 and abs(center_y - beam_y) / abs(beam_y) > tolerance:
coord = geom.picture_to_smargon(Coordinate(x=beam_x, y=center_y))
self.ctx.deps.devs.smargon_pos = SmargonCoordinate(sh_mm=coord)
self.ctx.deps.devs.smargon_wait(60)
@@ -126,7 +126,7 @@ class FaceDetectionService:
model.box.bottom_y,
)
self._centre_correction(model, tolerance=0.2)
self._center_correction(model, tolerance=0.2)
if cls_id == 3:
boxes_face[angle] = (x1, y1, x2, y2)
@@ -81,7 +81,7 @@ class LoopCenteringAnalyzer:
sh_mm=geom.picture_to_smargon(Coordinate(x=target_point[0], y=target_point[1]))
)
def _interpret_ml_loop_centre_box(
def _interpret_ml_loop_center_box(
self, boxes
) -> tuple[SmargonCoordinate | None, int | None, list[int]]:
if boxes is None:
@@ -89,7 +89,7 @@ class LoopCenteringAnalyzer:
classes: list[int] = []
pin = None
centre_x, centre_y = None, None
center_x, center_y = None, None
for box in boxes.boxes.values():
if box and box.cls is not None:
@@ -113,33 +113,33 @@ class LoopCenteringAnalyzer:
if cls == MLBoxType.LOOP_ALL.value:
if y1 + y2 <= x1 + x2:
centre_y = y1 + (y2 - y1) / 2
centre_x = x1
center_y = y1 + (y2 - y1) / 2
center_x = x1
elif pin:
position_dict = self.ctx.deps.mlbox.check_box_relation(pin, best_box)
if position_dict["overlap_y"] and position_dict["overlap_x"]:
centre_y = y1 + (y2 - y1) / 2
centre_x = x1
center_y = y1 + (y2 - y1) / 2
center_x = x1
cls = MLBoxType.PIN.value
else:
centre_y = y2 if position_dict["top"] else y1
centre_x = x1 + (x2 - x1) / 2
center_y = y2 if position_dict["top"] else y1
center_x = x1 + (x2 - x1) / 2
else:
centre_y = y1
centre_x = x1 + (x2 - x1) / 2
center_y = y1
center_x = x1 + (x2 - x1) / 2
elif cls == MLBoxType.PIN.value:
centre_y = y1 + (y2 - y1) / 2
centre_x = x1
center_y = y1 + (y2 - y1) / 2
center_x = x1
elif cls in (MLBoxType.CRYSTAL.value, MLBoxType.LOOP_FACE.value):
centre_y = y1 + (y2 - y1) / 2
centre_x = x1 + (x2 - x1) / 2
center_y = y1 + (y2 - y1) / 2
center_x = x1 + (x2 - x1) / 2
else:
return None, cls, classes
coord = geom.picture_to_smargon(Coordinate(x=centre_x, y=centre_y))
coord = geom.picture_to_smargon(Coordinate(x=center_x, y=center_y))
return SmargonCoordinate(sh_mm=coord), cls, classes
def analyze_angle(
@@ -159,7 +159,7 @@ class LoopCenteringAnalyzer:
predicted_target = self._extract_prediction_target(
target_point=prediction_result.target_point
)
calculated_target, selected_class, classes = self._interpret_ml_loop_centre_box(
calculated_target, selected_class, classes = self._interpret_ml_loop_center_box(
prediction_result.predictions
)
@@ -193,7 +193,7 @@ class LoopCenteringService:
else:
alc_comment = (
"Failed to centre but detected objects - "
"Failed to center but detected objects - "
f"Crystal: {found_classes_count.get(2, 0)}, "
f"Loop_face: {found_classes_count.get(3, 0)}, "
f"Loop_all: {found_classes_count.get(0, 0)}, "
+31 -31
View File
@@ -52,7 +52,7 @@ class RasterService:
return len(scan_result.images)
@staticmethod
def _grid_image_id_from_centre_offset(
def _grid_image_id_from_center_offset(
*, x_mm: float, y_mm: float, request: RasterGridRequest
) -> int:
if request.grid_size_mm.x <= 0 or request.grid_size_mm.y <= 0:
@@ -235,7 +235,7 @@ class RasterService:
beam_x_pxl = geom.beam_location_pxl.x
beam_y_pxl = geom.beam_location_pxl.y
line_scan_centre = geom.picture_to_smargon(Coordinate(x=beam_x_pxl, y=beam_y_pxl))
line_scan_center = geom.picture_to_smargon(Coordinate(x=beam_x_pxl, y=beam_y_pxl))
n_y = default_n_y
prediction_result: MLBoxPredictionResult = self.ctx.deps.mlbox.predict(
@@ -275,7 +275,7 @@ class RasterService:
y_retarget_threshold_mm = geom.beam_size_mm.y * 2.0
if y_delta_mm > y_retarget_threshold_mm:
line_scan_centre = geom.picture_to_smargon(Coordinate(x=beam_x_pxl, y=target_y_pxl))
line_scan_center = geom.picture_to_smargon(Coordinate(x=beam_x_pxl, y=target_y_pxl))
self.logger.info(
"Using ML target y for second auto-center raster",
extra=merge_log_context(
@@ -287,15 +287,15 @@ class RasterService:
"target_y_pxl": target_y_pxl,
"y_delta_mm": y_delta_mm,
"threshold_mm": y_retarget_threshold_mm,
"target_sh_x_mm": line_scan_centre.x,
"target_sh_y_mm": line_scan_centre.y,
"target_sh_z_mm": line_scan_centre.z,
"target_sh_x_mm": line_scan_center.x,
"target_sh_y_mm": line_scan_center.y,
"target_sh_z_mm": line_scan_center.z,
},
),
)
else:
self.logger.info(
"Keeping beam-centred y line scan because ML target y shift is small",
"Keeping beam-centered y line scan because ML target y shift is small",
extra=merge_log_context(
sample_log_context(self.ctx.sample),
{
@@ -310,7 +310,7 @@ class RasterService:
)
else:
self.logger.info(
"No ML target point for second auto-center raster; using beam-centred line scan",
"No ML target point for second auto-center raster; using beam-centered line scan",
extra=merge_log_context(
sample_log_context(self.ctx.sample),
{
@@ -362,7 +362,7 @@ class RasterService:
offset = Coordinate(x=-grid_size_mm.x / 2.0, y=-(n_y - 1) * grid_size_mm.y / 2.0)
top_left = SmargonCoordinate(
sh_mm=line_scan_centre + geom.smargon_nudge(offset),
sh_mm=line_scan_center + geom.smargon_nudge(offset),
phi_deg=geom.smargon.phi_deg,
chi_deg=geom.smargon.chi_deg,
)
@@ -486,7 +486,7 @@ class RasterService:
com = raster_highest_score(scan_result.images)
if com is None:
self.logger.info("Calcualted COM is None using centre image")
self.logger.info("Calcualted COM is None using center image")
if request.n_x == 1:
x = request.grid_size_mm.x / 2.0
else:
@@ -498,14 +498,14 @@ class RasterService:
)
self.logger.info(
"Calculated raster centre offset",
"Calculated raster center offset",
extra=merge_log_context(
sample_log_context(self.ctx.sample),
raster_request_log_context(request),
{
"centre_offset_x_mm": target_coor_offset.x,
"centre_offset_y_mm": target_coor_offset.y,
"centre_offset_z_mm": target_coor_offset.z,
"center_offset_x_mm": target_coor_offset.x,
"center_offset_y_mm": target_coor_offset.y,
"center_offset_z_mm": target_coor_offset.z,
"grid_half_width_x_mm": x,
"grid_half_height_y_mm": y,
"top_left_x_mm": getattr(request.smargon_top_left.sh_mm, "x", None),
@@ -519,14 +519,14 @@ class RasterService:
target_coor = com.get_com_mm(request)
target_coor_offset = self.ctx.sample_geometry.smargon_nudge(target_coor)
self.logger.info(
"Calculated raster centre offset",
"Calculated raster center offset",
extra=merge_log_context(
sample_log_context(self.ctx.sample),
raster_request_log_context(request),
{
"centre_offset_x_mm": target_coor_offset.x,
"centre_offset_y_mm": target_coor_offset.y,
"centre_offset_z_mm": target_coor_offset.z,
"center_offset_x_mm": target_coor_offset.x,
"center_offset_y_mm": target_coor_offset.y,
"center_offset_z_mm": target_coor_offset.z,
"grid_half_width_x_mm": x,
"grid_half_height_y_mm": y,
"top_left_x_mm": getattr(request.smargon_top_left.sh_mm, "x", None),
@@ -536,7 +536,7 @@ class RasterService:
),
)
self.logger.info(f"moving Smargon to grid centre offset {target_coor_offset}")
self.logger.info(f"moving Smargon to grid center offset {target_coor_offset}")
target_smargon = SmargonCoordinate(
sh_mm=request.smargon_top_left.sh_mm + target_coor_offset,
phi_deg=request.smargon_top_left.phi_deg,
@@ -547,16 +547,16 @@ class RasterService:
self.ctx.deps.devs.smargon_wait(timeout=180)
self.logger.info(
"Moved Smargon to raster centre",
"Moved Smargon to raster center",
extra=merge_log_context(
sample_log_context(self.ctx.sample),
raster_request_log_context(request),
{
"centre_sh_x_mm": target_smargon.sh_mm.x,
"centre_sh_y_mm": target_smargon.sh_mm.y,
"centre_sh_z_mm": target_smargon.sh_mm.z,
"centre_phi_deg": target_smargon.phi_deg,
"centre_chi_deg": target_smargon.chi_deg,
"center_sh_x_mm": target_smargon.sh_mm.x,
"center_sh_y_mm": target_smargon.sh_mm.y,
"center_sh_z_mm": target_smargon.sh_mm.z,
"center_phi_deg": target_smargon.phi_deg,
"center_chi_deg": target_smargon.chi_deg,
},
),
)
@@ -605,7 +605,7 @@ class RasterService:
diffraction_image_filename = (
f"{self.ctx.sample.db_id}_diffraction_image_near_grid_scan"
)
diffraction_image_id = self._grid_image_id_from_centre_offset(
diffraction_image_id = self._grid_image_id_from_center_offset(
x_mm=x, y_mm=y, request=request
)
@@ -646,7 +646,7 @@ class RasterService:
self.ctx.deps.cfg.last_best_res = get_best_res(result_list=scan_result.images)
self.ctx.deps.cfg.last_best_b_factor = get_best_b_factor(result_list=scan_result.images)
return CompletedRasterGridElem(
request=copy.deepcopy(request), result=scan_result, centre_of_mass=com
request=copy.deepcopy(request), result=scan_result, center_of_mass=com
)
except Exception:
@@ -763,9 +763,9 @@ class RasterService:
status = self.ctx.status
if not self.ctx.deps.cfg.simulated_detector:
self.logger.info("initialise detector")
self.logger.info("initialize detector")
self.ctx.deps.jfjoch.measure_raster(grid, status)
self.logger.info("detector initialised")
self.logger.info("detector initialized")
else:
self.logger.info("Simulated detector mode enabled; using fake raster result.")
@@ -829,9 +829,9 @@ class RasterService:
status = self.ctx.status
if not self.ctx.deps.cfg.simulated_detector:
self.logger.info(f"initialise detector for raster at {grid.omega_deg}")
self.logger.info(f"initialize detector for raster at {grid.omega_deg}")
self.ctx.deps.jfjoch.measure_raster(grid, status)
self.logger.info("detector initialised")
self.logger.info("detector initialized")
else:
self.logger.info("Simulated detector mode enabled; using fake raster result.")
+18 -18
View File
@@ -95,7 +95,7 @@ class AareUvicornWorker(UvicornWorker):
async def lifespan(application: FastAPI):
"""
Runs once per worker process, AFTER fork() and BEFORE serving requests.
All stateful / connection-opening initialisation belongs here so that
All stateful / connection-opening initialization belongs here so that
each worker gets its own fresh Redis, BEC, EPICS, and TELL connections.
"""
global cfg, daq, bl_dispatch
@@ -103,7 +103,7 @@ async def lifespan(application: FastAPI):
logger.info(f"Worker {os.getpid()} setting up JWT authentication...")
bl_dispatch = get_beamline_dispatch()
auth.init_jwt_key(bl_dispatch.auth)
logger.info(f"Worker {os.getpid()} starting initialisation...")
logger.info(f"Worker {os.getpid()} starting initialization...")
# ── Core objects (Redis, EPICS PVs, BEC, TELL, JFJoch, etc.) ──
bl = mx_beamline()
@@ -128,7 +128,7 @@ async def lifespan(application: FastAPI):
daq.set_face_detection_progress_callback(_push_face_detection_progress)
daq.set_automation_progress_callback(_push_automation_progress)
logger.info(f"Worker {os.getpid()} initialised successfully.")
logger.info(f"Worker {os.getpid()} initialized successfully.")
yield # ── application serves requests here ──
@@ -486,7 +486,7 @@ async def mono_pitch_scan(plot: bool = False, token: str = Depends(oauth2_scheme
@app.get("/beamline/steer_beam_available")
async def steer_beam_available(token: str = Depends(oauth2_scheme)):
"""
Check if the beam centring routine is available.
Check if the beam centering routine is available.
Args:
token: OAuth2 access token.
@@ -624,9 +624,9 @@ async def anneal(time_s: float, token: str = Depends(oauth2_scheme)):
@app.post("/smargon/initialize")
async def initialise_smargon(token: str = Depends(oauth2_scheme)) -> dict:
async def initialize_smargon(token: str = Depends(oauth2_scheme)) -> dict:
"""
Initialise Smargon. Staff only.
Initialize Smargon. Staff only.
Args:
token: OAuth2 access token.
@@ -636,8 +636,8 @@ async def initialise_smargon(token: str = Depends(oauth2_scheme)) -> dict:
"""
data = auth.parse_token(token)
auth.check_jwt_staff_only(data)
daq.initialise_smargon()
return {"ok": True, "message": "Smargon initialised."}
daq.initialize_smargon()
return {"ok": True, "message": "Smargon initialized."}
@app.post("/bec/load_user_macros")
@@ -671,12 +671,12 @@ async def bec_list_all_devices(token: str = Depends(oauth2_scheme)) -> list:
return daq.bec_list_all_devices()
@app.post("/bec/reinitialise_planner_and_position_devices")
async def bec_reinitialise_planner_and_position_devices(
@app.post("/bec/reinitialize_planner_and_position_devices")
async def bec_reinitialize_planner_and_position_devices(
method: str = "auto", token: str = Depends(oauth2_scheme)
) -> dict:
"""
Reinitialise BEC planner and position devices. Staff only.
Reinitialize BEC planner and position devices. Staff only.
Args:
method:
@@ -687,12 +687,12 @@ async def bec_reinitialise_planner_and_position_devices(
"""
data = auth.parse_token(token)
auth.check_jwt_staff_only(data)
position_devices = daq.bec_reinitialise_planner_and_position_devices(method=method)
position_devices = daq.bec_reinitialize_planner_and_position_devices(method=method)
return {
"ok": True,
"method": method,
"position_devices": position_devices,
"message": "BEC planner and position devices reinitialised.",
"message": "BEC planner and position devices reinitialized.",
}
@@ -729,14 +729,14 @@ async def bec_save_current_aerotech_position(token: str = Depends(oauth2_scheme)
return {"ok": True, "message": "Saved current BEC aerotech work position and reloaded devices."}
def initialise_aerotech(self):
def initialize_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}")
logger.error(f"Failed to initialize Aerotech: {e}")
raise
@@ -751,14 +751,14 @@ def detector_take_pedestal(self):
raise
def initialise_detector(self):
def initialize_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}")
logger.error(f"Failed to initialize detector: {e}")
raise
@@ -2457,7 +2457,7 @@ async def fluorimeter_stream() -> AsyncGenerator[str, None]:
break
await asyncio.sleep(0.2)
except asyncio.CancelledError as e:
logger.error(f">>> Fluorimeter stream cancelled: {e}")
logger.error(f">>> Fluorimeter stream canceled: {e}")
return
+4 -4
View File
@@ -128,7 +128,7 @@ def xtal_snapshot2xrf(devs: BeamlineDevices, cfg: BeamlineConfig):
def xtal_snapshot2bl(devs: BeamlineDevices, cfg: BeamlineConfig):
devs.bec_worker.move_to(BeamlineState.BEAM_VISUALISATION)
devs.bec_worker.move_to(BeamlineState.BEAM_VISUALIZATION)
def xtal_snapshot2dh(devs: BeamlineDevices, cfg: BeamlineConfig):
@@ -207,7 +207,7 @@ def flux_measurement2ba(devs: BeamlineDevices, cfg: BeamlineConfig):
def flux_measurement2bl(devs: BeamlineDevices, cfg: BeamlineConfig):
devs.bec_worker.move_to(BeamlineState.BEAM_VISUALISATION)
devs.bec_worker.move_to(BeamlineState.BEAM_VISUALIZATION)
def ba2flux_measurement(devs: BeamlineDevices, cfg: BeamlineConfig):
@@ -230,7 +230,7 @@ def ba2sa(devs: BeamlineDevices, cfg: BeamlineConfig):
def sa2bl(devs: BeamlineDevices, cfg: BeamlineConfig):
devs.bec_worker.move_to(BeamlineState.BEAM_VISUALISATION)
devs.bec_worker.move_to(BeamlineState.BEAM_VISUALIZATION)
devs.samcam_settings.exposure = 0.001
# TODO SAMCAM SETTINGS SHOULD BE DOEN via zoom settings
@@ -246,7 +246,7 @@ def bl2ba(devs: BeamlineDevices, cfg: BeamlineConfig):
def ba2bl(devs: BeamlineDevices, cfg: BeamlineConfig):
devs.bec_worker.move_to(BeamlineState.BEAM_VISUALISATION)
devs.bec_worker.move_to(BeamlineState.BEAM_VISUALIZATION)
devs.samcam_settings.exposure = 0.001
+12 -12
View File
@@ -43,7 +43,7 @@ class BeamlineState(str, Enum):
DATA_COLLECTION = "data_collection"
DC_XRF = "DC_XRF"
MANUAL_SAMPLE_EXCHANGE = "manual_sample_exchange"
BEAM_VISUALISATION = "beam_visualisation"
BEAM_VISUALIZATION = "beam_visualization"
FLUX_MEASUREMENT = "flux_measurement"
BEAMSTOP_ALIGNMENT = "beamstop_alignment"
MAINTENANCE = "maintenance"
@@ -57,7 +57,7 @@ def _bec_state_to_aare_state(bec_state: BeamlineState) -> BeamlineStateEnum:
BeamlineState.DATA_COLLECTION: BeamlineStateEnum.DataCollection,
BeamlineState.DC_XRF: BeamlineStateEnum.XrayFluorescence,
BeamlineState.MANUAL_SAMPLE_EXCHANGE: BeamlineStateEnum.SampleExchange,
BeamlineState.BEAM_VISUALISATION: BeamlineStateEnum.BeamLocation,
BeamlineState.BEAM_VISUALIZATION: BeamlineStateEnum.BeamLocation,
BeamlineState.FLUX_MEASUREMENT: BeamlineStateEnum.FluxMeasurement,
BeamlineState.BEAMSTOP_ALIGNMENT: BeamlineStateEnum.BeamstopAlignment,
BeamlineState.MAINTENANCE: BeamlineStateEnum.Maintenance,
@@ -108,7 +108,7 @@ class BECClientWorker:
try:
self._init_beamline_environment()
except Exception:
logger.exception("Error initialising BEC devices")
logger.exception("Error initializing BEC devices")
sys.exit(1)
logger.debug(f"simulated is {self.simulated}")
@@ -120,7 +120,7 @@ class BECClientWorker:
self._zoom = self.dev.scam_zoom
self._ring_current = self.dev.sls_current
except Exception as e:
logger.error(f"Error initialising planner and position devices: {e}")
logger.error(f"Error initializing planner and position devices: {e}")
self.position_devices = None
self.planner = None
self._backlight_brightness = None
@@ -129,10 +129,10 @@ class BECClientWorker:
self._zoom = self.dev.scam_zoom
self._ring_current = self.dev.sls_current
except Exception as e:
logger.exception("Error initialising zoom and ring_current")
logger.exception("Error initializing zoom and ring_current")
self._zoom = None
self.ring_current = None
raise RuntimeError(f"Error initialising BEC devices: {e}") from e
raise RuntimeError(f"Error initializing BEC devices: {e}") from e
def read_current_state(self) -> BeamlineStateEnum:
if self.planner is None:
@@ -341,9 +341,9 @@ class BECClientWorker:
logger.warning("BEC load_all_user_macros returned None")
return result
def reinitialise_planner_and_position_devices(self, method: str = "auto"):
def reinitialize_planner_and_position_devices(self, method: str = "auto"):
"""
Reinitialise BEC position devices and planner.
Reinitialize BEC position devices and planner.
Args:
method:
@@ -352,21 +352,21 @@ class BECClientWorker:
"sample" - force init_se_devices() + planner creation.
Returns:
List of position device names after reinitialisation.
List of position device names after reinitialization.
"""
if self.simulated:
logger.debug(f"Simulating reinitialise_planner_and_position_devices(method={method})")
logger.debug(f"Simulating reinitialize_planner_and_position_devices(method={method})")
return []
try:
self.client.config.update_session_with_file(
f"/sls/{self.beamline}/config/bec/production/{self._beamline_name}_bec/{self._beamline_name}_bec/device_configs/{self._beamline_name}-devices.yaml"
)
self._init_beamline_environment()
logger.info(f"Reinitialised BEC planner and position devices using method={method}")
logger.info(f"Reinitialized BEC planner and position devices using method={method}")
return self.list_position_devices()
except Exception as e:
raise self._bec_error(
e, operation=f"reinitialise_planner_and_position_devices:{method}"
e, operation=f"reinitialize_planner_and_position_devices:{method}"
) from e
def shutdown_client(self):
+1 -1
View File
@@ -144,7 +144,7 @@ class JFJochWrapper:
pgroup = s.sample.user
sample = s.sample.sample_name
# raster grid, rotation or screening specific parameter initialisation
# raster grid, rotation or screening specific parameter initialization
wedge = None
if isinstance(r, RasterGridRequest):
data_folder = f"{pgroup}/raw/raster"
+1 -1
View File
@@ -162,7 +162,7 @@ def main():
if not token or token.count(".") != 2:
raise RuntimeError(
"Authentication did not return a valid token. "
"Please check the server is running (it may still be initialising)."
"Please check the server is running (it may still be initializing)."
)
logger.info("Authentication successful")
splash.set_progress(80, "Authentication successful...")
+12 -12
View File
@@ -275,7 +275,7 @@ class MainWindow(QMainWindow):
None,
"Authentication Error",
"Could not start the GUI because authentication data was invalid.\n\n"
"Most commonly the server is not running yet (or is still initialising).\n"
"Most commonly the server is not running yet (or is still initializing).\n"
"Please start/restart the server and try again.",
)
raise
@@ -365,7 +365,7 @@ class MainWindow(QMainWindow):
# documentMode: no pane frame, so the fixed-width panels aren't inset.
self.left_column_tabs = QTabWidget(self.left_column)
self.left_column_tabs.setDocumentMode(True)
# documentMode draws a grey base line across the bar's full width.
# documentMode draws a gray base line across the bar's full width.
self.left_column_tabs.tabBar().setDrawBase(False)
beamline_page = QWidget()
@@ -618,7 +618,7 @@ class MainWindow(QMainWindow):
self.job_list_panel.hide()
# Queue controls act on the queue, whose order only the Queued chip
# view shows — so they are live there and greyed out elsewhere. The
# view shows — so they are live there and grayed out elsewhere. The
# pop-out clones register themselves in _clone_automation_row. "run"
# kind stays enabled while automation runs: pausing is always allowed.
self._queue_action_buttons: list[tuple[QPushButton, str]] = [
@@ -638,7 +638,7 @@ class MainWindow(QMainWindow):
self.sample_lists_tabs.addTab(dewar_tab, "Dewar samples")
self.sample_lists_tabs.addTab(self.ref_tools_panel, "Auxiliary puck")
# Non-staff never get reference-tools data (the DAQ connect below is
# staff-gated). Grey the tab out instead of hiding it: every role sees
# staff-gated). Gray the tab out instead of hiding it: every role sees
# the same view, and clicking the locked tab says why it is locked.
if not self._decoded_token.staff:
self.sample_lists_tabs.setTabEnabled(1, False)
@@ -2509,7 +2509,7 @@ class MainWindow(QMainWindow):
current_step=None,
steps=[
StepState(step=WorkflowStateKind.MOUNT, status=StepStatus.PENDING),
StepState(step=WorkflowStateKind.LOOP_CENTRE, status=StepStatus.PENDING),
StepState(step=WorkflowStateKind.LOOP_CENTER, status=StepStatus.PENDING),
StepState(step=WorkflowStateKind.RASTER, status=StepStatus.PENDING),
StepState(
step=WorkflowStateKind.DATA_COLLECTION, status=StepStatus.PENDING
@@ -2620,7 +2620,7 @@ class MainWindow(QMainWindow):
def _apply_session_gate(self, session_state) -> None:
# No baton -> watching only: camera views stay live, every operating
# surface is greyed. The SESSION VACANT badge (and the status bar
# surface is grayed. The SESSION VACANT badge (and the status bar
# session menu) remain the way back in.
owned = session_state in (SessionsStateEnum.OwnedByYou, SessionsStateEnum.PendingElseToYou)
if owned == getattr(self, "_session_operations_enabled", None):
@@ -2655,7 +2655,7 @@ class MainWindow(QMainWindow):
banner.set_collapsed(True, persist=False)
# Watch-only shows ONLY the camera stream: every other operating
# surface is hidden outright (not just greyed), and regaining the
# surface is hidden outright (not just grayed), and regaining the
# baton restores exactly the visibility each one had before. The
# status bar and its SESSION VACANT badge stay — they are the way
# back in. To later hide the camera streams as well, add
@@ -2860,7 +2860,7 @@ class MainWindow(QMainWindow):
self.alert_banner.show_message("Baton acquired!", False, auto_clear_ms=4000)
else:
self.alert_banner.show_message(
"Request declined or cancelled", False, auto_clear_ms=4000
"Request declined or canceled", False, auto_clear_ms=4000
)
# Manage incoming request dialog (when someone requests from us)
@@ -2896,7 +2896,7 @@ class MainWindow(QMainWindow):
self._baton_pending_dialog = BatonPendingDialog(
target_user=target_user, timeout_seconds=timeout, parent=self
)
self._baton_pending_dialog.cancelled_signal.connect(self.daq.cancel_baton_request)
self._baton_pending_dialog.canceled_signal.connect(self.daq.cancel_baton_request)
self._baton_pending_dialog.show()
else:
self._baton_pending_dialog.update_remaining(timeout)
@@ -2913,7 +2913,7 @@ class MainWindow(QMainWindow):
self._baton_pending_dialog = BatonPendingDialog(
target_user="Current Holder", timeout_seconds=0, parent=self
)
self._baton_pending_dialog.cancelled_signal.connect(self.daq.cancel_baton_request)
self._baton_pending_dialog.canceled_signal.connect(self.daq.cancel_baton_request)
self._baton_pending_dialog.show()
self._baton_pending_dialog.set_queued_state()
@@ -2976,7 +2976,7 @@ class MainWindow(QMainWindow):
self._baton_pending_dialog = BatonPendingDialog(
target_user="Current Holder", timeout_seconds=0, parent=self
)
self._baton_pending_dialog.cancelled_signal.connect(self.daq.cancel_baton_request)
self._baton_pending_dialog.canceled_signal.connect(self.daq.cancel_baton_request)
self._baton_pending_dialog.show()
self._baton_pending_dialog.set_queued_state()
@@ -3303,7 +3303,7 @@ class MainWindow(QMainWindow):
# widgets it inspects — a raise here spams every event and breaks
# widget cleanup, so missing attributes must mean "not my click".
if event.type() == QEvent.Type.MouseButtonPress:
# Non-staff click on the greyed-out Auxiliary-puck tab: only
# Non-staff click on the grayed-out Auxiliary-puck tab: only
# installed for non-staff, and tabAt() is geometric so it still
# sees the disabled tab — explain the lock instead of silently
# eating the click.
+2 -2
View File
@@ -122,7 +122,7 @@ class UserSampleSpreadsheet(QAbstractTableModel):
return get_entry(self._sorted_samples[index.row()], index.column())
elif role == Qt.ItemDataRole.BackgroundRole:
# Status lives in the "#" column, as a full cell fill under the
# row number — rows themselves alternate grey/white (view-level)
# row number — rows themselves alternate gray/white (view-level)
# and selection stays the pale blue tint.
if index.column() == COL_STATUS:
color = self._status_color(self._sorted_samples[index.row()])
@@ -168,7 +168,7 @@ class UserSampleSpreadsheet(QAbstractTableModel):
@staticmethod
def _measured(sample: SampleShortInfo) -> bool:
# Automatic status, never relabelled by hand: any rotation data
# Automatic status, never relabeled by hand: any rotation data
# counts as measured (>= 1); unmeasured is exactly rotation count 0.
return isinstance(sample.rotation_count, (int, float)) and sample.rotation_count >= 1
+3 -3
View File
@@ -86,7 +86,7 @@ class AutomationProgressWidget(QWidget):
steps_row.setSpacing(6)
for step in (
WorkflowStateKind.MOUNT,
WorkflowStateKind.LOOP_CENTRE,
WorkflowStateKind.LOOP_CENTER,
WorkflowStateKind.RASTER,
WorkflowStateKind.DATA_COLLECTION,
):
@@ -105,7 +105,7 @@ class AutomationProgressWidget(QWidget):
current_step=None,
steps=[
self._make_step(WorkflowStateKind.MOUNT, StepStatus.PENDING),
self._make_step(WorkflowStateKind.LOOP_CENTRE, StepStatus.PENDING),
self._make_step(WorkflowStateKind.LOOP_CENTER, StepStatus.PENDING),
self._make_step(WorkflowStateKind.RASTER, StepStatus.PENDING),
self._make_step(WorkflowStateKind.DATA_COLLECTION, StepStatus.PENDING),
self._make_step(WorkflowStateKind.FINAL, StepStatus.PENDING),
@@ -128,7 +128,7 @@ class AutomationProgressWidget(QWidget):
def _label_for_step(step: WorkflowStateKind) -> str:
return {
WorkflowStateKind.MOUNT: "Mount",
WorkflowStateKind.LOOP_CENTRE: "Center",
WorkflowStateKind.LOOP_CENTER: "Center",
WorkflowStateKind.RASTER: "Raster",
WorkflowStateKind.DATA_COLLECTION: "Collect",
WorkflowStateKind.FINAL: "Status",
+7 -7
View File
@@ -91,7 +91,7 @@ class BeamlineStatePanel(QFrame):
Replaces the former vertical station map: two-line entries in beamline
order, colored by availability (blue/red = active, orange = reachable in
one transition, grey = not reachable).
one transition, gray = not reachable).
"""
sample_exchange = Signal()
@@ -121,7 +121,7 @@ class BeamlineStatePanel(QFrame):
(BeamlineStateEnum.XrayFluorescence, "X-ray fluorescence"),
)
# Beam-optics diagnostic states are admin (staff) only: greyed for
# Beam-optics diagnostic states are admin (staff) only: grayed for
# everyone else, with a red warning tip instead of the routes hint.
_STAFF_ONLY_STATES: ClassVar[frozenset[BeamlineStateEnum]] = frozenset(
{
@@ -135,7 +135,7 @@ class BeamlineStatePanel(QFrame):
BeamlineStateEnum.DewarTransfer: "Dewar transfer mode",
BeamlineStateEnum.SampleExchange: "Manual sample exchange mode",
BeamlineStateEnum.RobotSampleExchange: "Robot-assisted sample exchange",
BeamlineStateEnum.SampleAlignment: "Sample centring and alignment mode",
BeamlineStateEnum.SampleAlignment: "Sample centering and alignment mode",
BeamlineStateEnum.BeamLocation: "Beam location mode",
BeamlineStateEnum.BeamstopAlignment: "Beamstop alignment mode",
BeamlineStateEnum.FluxMeasurement: "Flux measurement mode",
@@ -262,7 +262,7 @@ class BeamlineStatePanel(QFrame):
def _available_targets(self) -> frozenset[BeamlineStateEnum]:
current = self._current_state
# Busy greys the whole strip like Moving does: a transition posted
# Busy grays the whole strip like Moving does: a transition posted
# mid-operation would clobber it (same guard as the status-bar menu).
if self._busy or current is None or current == BeamlineStateEnum.Moving:
return frozenset()
@@ -316,7 +316,7 @@ class BeamlineStatePanel(QFrame):
)
def _emit_for_state(self, state: BeamlineStateEnum) -> None:
# Unavailable transitions are not clickable (grey + forbidden cursor).
# Unavailable transitions are not clickable (gray + forbidden cursor).
if state not in self._available_targets():
return
self._pending_target_state = state
@@ -394,7 +394,7 @@ class BeamlineStatePanel(QFrame):
pending_button = button
# Availability drives the look: active = bold (red for
# Maintenance, blue otherwise), reachable = orange, rest = grey.
# Maintenance, blue otherwise), reachable = orange, rest = gray.
# No backgrounds, no rounded corners.
if is_current or is_pending:
color = (
@@ -422,7 +422,7 @@ class BeamlineStatePanel(QFrame):
# cursor under a resting mouse until it moves again.
# Hover underline (the app-wide tab/chip affordance) only on
# entries that mean something: the current state and reachable
# targets — not the grey dead ends.
# targets — not the gray dead ends.
hover_underline = (
" text-decoration: underline;" if (is_current or is_pending or is_available) else ""
)
+11 -11
View File
@@ -373,9 +373,9 @@ class LocalContactPanel(QFrame):
)
tools_layout.addWidget(
self._make_button(
"Reinitialise BEC planner/devices",
lambda: self._daq.bec_reinitialise_planner_and_position_devices("auto"),
"Reinitialising BEC planner and position devices.",
"Reinitialize BEC planner/devices",
lambda: self._daq.bec_reinitialize_planner_and_position_devices("auto"),
"Reinitializing BEC planner and position devices.",
)
)
@@ -468,20 +468,20 @@ class LocalContactPanel(QFrame):
layout.addWidget(
self._build_section(
"Initialise",
"Initialize",
[
self._make_button(
"Initialise detector",
self._daq.initialise_detector,
"Initialising detector.",
"Initialize detector",
self._daq.initialize_detector,
"Initializing detector.",
),
self._make_button(
"Initialise Smargon", self._daq.initialise_smargon, "Initialising Smargon."
"Initialize Smargon", self._daq.initialize_smargon, "Initializing Smargon."
),
self._make_button(
"Initialise Aerotech",
self._daq.initialise_aerotech,
"Initialising Aerotech.",
"Initialize Aerotech",
self._daq.initialize_aerotech,
"Initializing Aerotech.",
),
],
)
+13 -13
View File
@@ -53,10 +53,10 @@ class RuntimeNotificationWidget(QFrame):
self._message.setObjectName("runtimeNotificationMessage")
self._message.setWordWrap(True)
self._minimise_button = QToolButton(self)
self._minimise_button.setText("")
self._minimise_button.setToolTip("Minimise notification")
self._minimise_button.clicked.connect(self._toggle_minimised)
self._minimize_button = QToolButton(self)
self._minimize_button.setText("")
self._minimize_button.setToolTip("Minimize notification")
self._minimize_button.clicked.connect(self._toggle_minimized)
self._clear_button = QPushButton("Clear", self)
self._clear_button.clicked.connect(self.clear_notification)
@@ -67,7 +67,7 @@ class RuntimeNotificationWidget(QFrame):
header_layout = QHBoxLayout()
header_layout.setContentsMargins(0, 0, 0, 0)
header_layout.addWidget(self._title, 1)
header_layout.addWidget(self._minimise_button)
header_layout.addWidget(self._minimize_button)
button_layout = QHBoxLayout()
button_layout.setContentsMargins(0, 0, 0, 0)
@@ -91,7 +91,7 @@ class RuntimeNotificationWidget(QFrame):
self._full_title = ""
self._full_message = ""
self._minimised = False
self._minimized = False
self._sticky = True
self.setStyleSheet(
@@ -142,11 +142,11 @@ class RuntimeNotificationWidget(QFrame):
self.update()
@Slot()
def _toggle_minimised(self) -> None:
self._minimised = not self._minimised
self._body.setVisible(not self._minimised)
self._minimise_button.setText("+" if self._minimised else "")
if self._minimised:
def _toggle_minimized(self) -> None:
self._minimized = not self._minimized
self._body.setVisible(not self._minimized)
self._minimize_button.setText("+" if self._minimized else "")
if self._minimized:
self._title.setText(self._full_title or "Notification")
else:
self._title.setText(self._full_title)
@@ -170,8 +170,8 @@ class RuntimeNotificationWidget(QFrame):
self._message.setText(self._full_message)
self._clear_button.setVisible(not sticky)
self._body.setVisible(True)
self._minimised = False
self._minimise_button.setText("")
self._minimized = False
self._minimize_button.setText("")
self.setVisible(True)
if not sticky:
+3 -3
View File
@@ -52,12 +52,12 @@ logger = setup_logger(LOGGER_NAME)
# LED step indicator
# ---------------------------------------------------------------------------
class LEDStages(QWidget):
STEPS: ClassVar[list[str]] = ["Mount", "Centre", "Raster", "Collect"]
STEPS: ClassVar[list[str]] = ["Mount", "Center", "Raster", "Collect"]
# WorkflowStateKind → LED index
_KIND_TO_INDEX: ClassVar[dict[WorkflowStateKind, int]] = {
WorkflowStateKind.MOUNT: 0,
WorkflowStateKind.LOOP_CENTRE: 1,
WorkflowStateKind.LOOP_CENTER: 1,
WorkflowStateKind.RASTER: 2,
WorkflowStateKind.DATA_COLLECTION: 3,
}
@@ -360,7 +360,7 @@ class PortraitModePanel(QWidget):
self._leds = LEDStages(active_step=0)
layout.addWidget(self._leds)
# Play/Pause button (single, centred)
# Play/Pause button (single, centered)
ctrl_frame = QFrame()
ctrl_frame.setStyleSheet(f"QFrame {{ background: {CARD_BG}; border-radius: 18px; }}")
+1 -1
View File
@@ -130,7 +130,7 @@ class SamcamPanel(QWidget):
# Target color
target_color_layout = QHBoxLayout()
target_color_label = QLabel("Target colour:")
target_color_label = QLabel("Target color:")
self.target_color_combo = QComboBox()
self.target_color_combo.addItems(["Cyan", "Dark Blue", "Dark Red"])
self.target_color_combo.setCurrentText("Cyan")
+1 -1
View File
@@ -200,7 +200,7 @@ class TellSamplePanel(QFrame):
lambda chip: self.table_model.set_status_filter(chip.property("status_key"))
)
# Staggered grey/white rows tell rows apart — no grid lines, no row
# Staggered gray/white rows tell rows apart — no grid lines, no row
# tints; status fills the frozen "#" column and selection stays blue.
self.table_view = FrozenColumnTableView()
self.table_view.setShowGrid(False)
@@ -51,7 +51,7 @@ _VIRIDIS_COLORS = [
(253, 231, 37), # Yellow
]
# Float (N, 3) lookup table for vectorised colour mapping.
# Float (N, 3) lookup table for vectorised color mapping.
_VIRIDIS_LUT = np.array(_VIRIDIS_COLORS, dtype=np.float64)
@@ -551,7 +551,7 @@ class RasterGridManager(QObject):
self, cache_key: tuple, grid: RasterGridRequest, values: list[float] | list[int]
) -> QImage | None:
"""Return a cached n_x*n_y heatmap bitmap for this grid, building it once
on a cache miss. One pixel per cell; colours baked at full opacity with
on a cache miss. One pixel per cell; colors baked at full opacity with
the alpha channel encoding validity (overlay transparency is applied at
blit time via painter opacity, so it never invalidates the cache)."""
cached = self._heatmap_cache.get(cache_key)
@@ -651,7 +651,7 @@ class RasterGridManager(QObject):
painter.drawRect(bounds)
painter.restore()
# TODO make sure draw_grid is visualising the grid correctly, correct orientation, correct x/y labelling!!!!
# TODO make sure draw_grid is visualizing the grid correctly, correct orientation, correct x/y labelling!!!!
def _draw_grid(
self,
painter: QPainter,
@@ -920,7 +920,7 @@ class RasterGridManager(QObject):
self._active_grid.n_y = 0
self._invalidate_heatmap_cache()
if last_raster is not None and last_raster.result.file_prefix is not None:
com = last_raster.centre_of_mass
com = last_raster.center_of_mass
logger.info(f"COM: {com}")
cell = com.max_image if com else 0
self.image_selected.emit(self._detector_url, cell)
+7 -7
View File
@@ -23,7 +23,7 @@ THEME_BLUEBIRD = "bluebird"
# -- Light theme ------------------------------------------------------------
BACKGROUND = "#e2e7ee"
# App-wide sunrise-sky gradient (sampled from the reference photo taken at dawn
# near Dawn's house at Windisch: slate blue fading through pale grey-lavender
# near Dawn's house at Windisch: slate blue fading through pale gray-lavender
# into warm cream. Painted once per top-level window (QMainWindow/QDialog)
# while plain child widgets stay transparent, so the window reads as ONE
# continuous sky instead of every widget restarting the gradient.
@@ -117,7 +117,7 @@ BANNER_TAB_GAP = 6
# one for SEPARATOR_HINT_DELAY_MS (or a drag starts) — then only the exact
# separator under the cursor fills with SEPARATOR_HINT. The rest/drag gate
# lives in MainWindow.event(); the QSS :hover part picks the one separator.
SEPARATOR_HINT = "rgba(168, 178, 192, 20%)" # scrollbar-track grey @50%
SEPARATOR_HINT = "rgba(168, 178, 192, 20%)" # scrollbar-track gray @50%
SEPARATOR_HINT_DELAY_MS = 888 # int, used in code, not QSS
# Theme-switch screenshot cross-fade duration (int ms, used in code).
@@ -444,7 +444,7 @@ TABLE_SHADE_BG = "#e0e0e0"
# -- Sample status row tints (dewar/queue view) -----------------------------
# Status colors now fill the rounded dot in the table's status column; rows
# themselves alternate WHITE / SAMPLE_ROW_ALT_BG. Tune the palette here.
SAMPLE_ROW_ALT_BG = "#eef1f5" # staggered row grey (alternates with white)
SAMPLE_ROW_ALT_BG = "#eef1f5" # staggered row gray (alternates with white)
SAMPLE_STATUS_QUEUED_BG = "#ffe4c4" # pale orange — waiting in the automation queue
SAMPLE_STATUS_FLAGGED_BG = "#ffd9d9" # pale red — automation failed on this sample
SAMPLE_STATUS_MEASURED_BG = "#dcf2e0" # pale green — already has collected data
@@ -466,7 +466,7 @@ PATH_END = "#d20f39" # raster path gradient end + end circle (red)
LEGEND_BG = "#eff1f5" # base
LEGEND_TEXT = "#4c4f69" # text
TOOLTIP_TEXT = "#4c4f69" # camera coords tooltip pen — NOT the QToolTip popup
SCALE_BAR_GREY = "#8c8fa1" # hover HUD scale bar (Latte overlay1 grey)
SCALE_BAR_GRAY = "#8c8fa1" # hover HUD scale bar (Latte overlay1 gray)
MARK_TOOLTIP_GOLD = "#df8e1d" # yellow
MARK_TOOLTIP_ORANGE = "#fe640b" # peach
MARK_TOOLTIP_RED = "#d20f39" # red
@@ -587,7 +587,7 @@ def admin_tip_qss(theme: str) -> str:
SLIDER_FILL = "#8ba3c7"
# -- Scrollbars (rounded, no arrows) ----------------------------------------
# Flipped on request: the track is now the darker grey and the draggable
# Flipped on request: the track is now the darker gray and the draggable
# handle the light one; hover therefore lightens further instead of darkening.
SCROLLBAR_TRACK = "#E4E4E4"
SCROLLBAR_HANDLE = "#D4D4D4"
@@ -998,7 +998,7 @@ def _sunrise_stylesheet(overrides: dict[str, str] | None = None) -> str:
}
/* Disabled = baton-gated ("watch only"): the explicit colors above mask
Qt's native grey, so spell the greyed state out. */
Qt's native gray, so spell the grayed state out. */
QPushButton:disabled, QCheckBox:disabled, QRadioButton:disabled,
QLabel:disabled, QComboBox:disabled, QLineEdit:disabled,
QAbstractSpinBox:disabled, QTabBar::tab:disabled {
@@ -1179,7 +1179,7 @@ def _sunrise_stylesheet(overrides: dict[str, str] | None = None) -> str:
}
/* Pale-blue selection with readable dark text in every sample table;
staggered grey/white rows where alternation is enabled. */
staggered gray/white rows where alternation is enabled. */
QTableView {
background: $white;
alternate-background-color: $sample_row_alt_bg;
+7 -7
View File
@@ -1129,7 +1129,7 @@ class DAQWorker(QObject):
raster_elem = CompletedRasterGridElem(
request=new_copy,
result=ScanResult(file_prefix=r.file_prefix, images=images),
centre_of_mass=None,
center_of_mass=None,
)
reply = CompletedRasterGrid(r=[raster_elem])
self.raster_scan_completed.emit(reply)
@@ -1171,7 +1171,7 @@ class DAQWorker(QObject):
raster_elem = CompletedRasterGridElem(
request=new_copy,
result=ScanResult(file_prefix=r.file_prefix, images=images),
centre_of_mass=None,
center_of_mass=None,
)
reply = CompletedRasterGrid(r=[raster_elem])
self.raster_scan_completed.emit(reply)
@@ -1727,8 +1727,8 @@ class DAQWorker(QObject):
self.http_error.emit(str(e))
@Slot(str)
def bec_reinitialise_planner_and_position_devices(self, method: str = "auto"):
self.generic_post(f"bec/reinitialise_planner_and_position_devices?method={method}")
def bec_reinitialize_planner_and_position_devices(self, method: str = "auto"):
self.generic_post(f"bec/reinitialize_planner_and_position_devices?method={method}")
@Slot()
def bec_save_current_bs_pos(self):
@@ -1743,7 +1743,7 @@ class DAQWorker(QObject):
self.generic_post("bec/save_current_aerotech_position")
@Slot()
def initialise_smargon(self):
def initialize_smargon(self):
self.generic_post("smargon/initialize")
@Slot()
@@ -1751,7 +1751,7 @@ class DAQWorker(QObject):
self.generic_post("beamline/save_beam_location_camera_setting")
@Slot()
def initialise_aerotech(self):
def initialize_aerotech(self):
logger.info("initisalisation does not initisalise aareSCAN but runs homing script")
self.generic_post("aerotech/initialize")
@@ -1760,7 +1760,7 @@ class DAQWorker(QObject):
self.generic_post("detector/take_pedestal")
@Slot()
def initialise_detector(self):
def initialize_detector(self):
self.generic_post("detector/initialize")
@Slot()
@@ -28,7 +28,7 @@ class ControlsHelpDialog(QDialog):
<h3>Mouse Click / Drag</h3>
<ul>
<li><b>Left click</b>: move sample to clicked position</li>
<li><b>Shift + Left click</b>: move using the special Z-alignment click behaviour</li>
<li><b>Shift + Left click</b>: move using the special Z-alignment click behavior</li>
<li><b>Ctrl + Left click on active raster grid</b>: move to the clicked position under the grid</li>
<li><b>Left drag on active raster grid</b>: move active raster grid</li>
<li><b>Right click</b>: open sample camera context menu</li>
@@ -87,7 +87,7 @@ class ControlsHelpDialog(QDialog):
<ul>
<li><b>Ctrl + Mouse wheel</b>: zoom in/out</li>
<li><b>Mouse wheel</b>: normal scrolling when Ctrl is not pressed</li>
<li><b>Mouse drag</b>: rubber-band drag/selection behaviour is enabled</li>
<li><b>Mouse drag</b>: rubber-band drag/selection behavior is enabled</li>
</ul>
<h3>Keyboard</h3>
+4 -4
View File
@@ -85,7 +85,7 @@ class TutorialOverlay(QWidget):
next_requested = Signal()
back_requested = Signal()
skip_requested = Signal()
cancelled = Signal()
canceled = Signal()
highlight_clicked = Signal()
def __init__(self, parent: QWidget | None = None):
@@ -146,7 +146,7 @@ class TutorialOverlay(QWidget):
self.end_button = QPushButton("End tutorial", self)
self.end_button.setStyleSheet(button_style)
self.end_button.setMinimumHeight(44)
self.end_button.clicked.connect(self.cancelled.emit)
self.end_button.clicked.connect(self.canceled.emit)
self.end_button.hide()
self.hide()
@@ -178,7 +178,7 @@ class TutorialOverlay(QWidget):
def keyPressEvent(self, event) -> None:
if event.key() == Qt.Key_Escape:
self.cancelled.emit()
self.canceled.emit()
event.accept()
return
super().keyPressEvent(event)
@@ -386,7 +386,7 @@ class TutorialManager(QObject):
self.overlay.next_requested.connect(self._on_next_requested)
self.overlay.back_requested.connect(self._on_back_requested)
self.overlay.skip_requested.connect(self._on_skip_requested)
self.overlay.cancelled.connect(self.stop)
self.overlay.canceled.connect(self.stop)
self.overlay.highlight_clicked.connect(self._on_highlight_clicked)
self.event_bus.event_emitted.connect(self._on_tutorial_event)
+2 -2
View File
@@ -56,7 +56,7 @@ class CompactAutomationProgressStrip(QFrame):
for step in (
WorkflowStateKind.MOUNT,
WorkflowStateKind.LOOP_CENTRE,
WorkflowStateKind.LOOP_CENTER,
WorkflowStateKind.RASTER,
WorkflowStateKind.DATA_COLLECTION,
WorkflowStateKind.FINAL,
@@ -79,7 +79,7 @@ class CompactAutomationProgressStrip(QFrame):
def _step_title(step: WorkflowStateKind) -> str:
return {
WorkflowStateKind.MOUNT: "Mount",
WorkflowStateKind.LOOP_CENTRE: "Center",
WorkflowStateKind.LOOP_CENTER: "Center",
WorkflowStateKind.RASTER: "Raster",
WorkflowStateKind.DATA_COLLECTION: "Collect",
WorkflowStateKind.FINAL: "Finish",
+2 -2
View File
@@ -238,7 +238,7 @@ class BatonPendingDialog(QDialog):
or for the beamline queue to clear.
"""
cancelled_signal = Signal()
canceled_signal = Signal()
def __init__(self, target_user: str, timeout_seconds: int = 30, parent=None):
super().__init__(parent)
@@ -361,5 +361,5 @@ class BatonPendingDialog(QDialog):
# Keep cancel button so they can abort the wait if they change their mind
def _on_cancel(self):
self.cancelled_signal.emit()
self.canceled_signal.emit()
self.reject()
+5 -5
View File
@@ -56,7 +56,7 @@ from aare.gui.styles import (
MARKER_GREEN,
PATH_END,
PATH_START,
SCALE_BAR_GREY,
SCALE_BAR_GRAY,
SHADOW,
TARGET_COLORS,
THEME_SUNSET,
@@ -1297,7 +1297,7 @@ class SampleCameraImageLabel(QGraphicsView):
return bar_um, label
def _draw_hover_hud(self, painter: QPainter):
# Bottom-right HUD: grey scale bar over the hovered pixel coordinates.
# Bottom-right HUD: gray scale bar over the hovered pixel coordinates.
if self._hover_pos is None or self.pixmap_item is None:
return
if not self.pixmap_item.sceneBoundingRect().contains(self._hover_pos):
@@ -1317,7 +1317,7 @@ class SampleCameraImageLabel(QGraphicsView):
right = self.viewport().width() - margin
coords_text = f"{self._hover_pos.x():.0f}, {self._hover_pos.y():.0f} pxl"
coords_baseline = self.viewport().height() - margin - fm.descent()
grey = qcolor(SCALE_BAR_GREY)
gray = qcolor(SCALE_BAR_GRAY)
def shadowed_text(x: float, baseline: float, text: str, color: QColor):
painter.setPen(QPen(qcolor(SHADOW, 200)))
@@ -1336,12 +1336,12 @@ class SampleCameraImageLabel(QGraphicsView):
bar_um, label = self._scale_bar(um_per_view_px)
bar_px = bar_um / um_per_view_px
bar_y = coords_baseline - fm.ascent() - 12
painter.setPen(QPen(grey, 3))
painter.setPen(QPen(gray, 3))
painter.drawLine(QPointF(right - bar_px, bar_y), QPointF(right, bar_y))
painter.drawLine(QPointF(right - bar_px, bar_y - 4), QPointF(right - bar_px, bar_y + 4))
painter.drawLine(QPointF(right, bar_y - 4), QPointF(right, bar_y + 4))
shadowed_text(
right - (bar_px + fm.horizontalAdvance(label)) / 2, bar_y - 8, label, grey
right - (bar_px + fm.horizontalAdvance(label)) / 2, bar_y - 8, label, gray
)
painter.restore()
@@ -73,7 +73,7 @@ class LocalContactStatusWidget(QFrame):
"dtz": "Detector distance",
"energy": "Energy",
"wavelength": "Wavelength",
"beam_center": "Beam centre",
"beam_center": "Beam center",
}
def __init__(
+4 -4
View File
@@ -51,7 +51,7 @@ class NumberLineEdit(QLineEdit):
@Slot(str)
def on_text_changed(self, text: str):
# when text changes check validation and change the colour of the line edit
# when text changes check validation and change the color of the line edit
self._is_valid = self.validate(text)
self._set_invalid(not self._is_valid)
@@ -156,13 +156,13 @@ class CheckedLineEdit(QWidget):
def set_busy(self, busy: bool):
# if an external wants to change the busy state.
# update self._busy and change editor read only state. After enable/disable check_box and set colour
# update self._busy and change editor read only state. After enable/disable check_box and set color
self.blockSignals(True)
self._busy = busy
self.setReadOnly()
self.check_box.blockSignals(True)
# No inline checkbox fills: the theme's :disabled rules grey it.
# No inline checkbox fills: the theme's :disabled rules gray it.
if self._busy:
self.check_box.setEnabled(False)
if self._checked:
@@ -232,7 +232,7 @@ class DbOverrideLineEdit(QWidget):
"""A numeric field backed by two sources: a database value and the user's
own value, with a panel-level toggle choosing which is shown.
Behaviour:
Behavior:
* Until the user edits the field, "mine" mirrors the database value, so a
fresh field just follows the database.
* Editing records the user's value, marks the field as user-edited, flips
+5 -5
View File
@@ -542,7 +542,7 @@ class StatusBar(QStatusBar):
if self._is_staff:
action_4.triggered.connect(self.beam_location)
else:
# Same gate as the state strip: greyed, not hidden, so
# Same gate as the state strip: grayed, not hidden, so
# non-staff learn the state exists but needs admin mode.
action_4.setText("Beam location (admin mode only)")
action_4.setEnabled(False)
@@ -590,7 +590,7 @@ class StatusBar(QStatusBar):
}
if not merged:
merged = set(pgroups)
self._generate_pgroup_dialogue(curr=curr, pgroups=sorted(merged))
self._generate_pgroup_dialog(curr=curr, pgroups=sorted(merged))
finally:
try:
self.staff_pgroups_loaded.disconnect(_on_loaded)
@@ -601,7 +601,7 @@ class StatusBar(QStatusBar):
self._list_staff_pgroups()
return
self._generate_pgroup_dialogue(curr=curr, pgroups=pgroups)
self._generate_pgroup_dialog(curr=curr, pgroups=pgroups)
def _on_grab_clicked(self):
"""Handle grab/request click - baton first, p-group after grant."""
@@ -644,7 +644,7 @@ class StatusBar(QStatusBar):
def _list_staff_pgroups(self):
self.get_all_pgroups.emit()
def _generate_pgroup_dialogue(self, curr: str | None = None, pgroups: list | None = None):
def _generate_pgroup_dialog(self, curr: str | None = None, pgroups: list | None = None):
# logger.info(pgroups) # too much information for normal operation
dialog = PGroupDialog(curr_pgroup=curr, pgroups=pgroups, parent=self.window())
@@ -658,6 +658,6 @@ class StatusBar(QStatusBar):
f"P-group '{entered_text}' is not in your allowed list.\n"
f"Please select from: {', '.join(pgroups)}",
)
self._generate_pgroup_dialogue(curr=curr, pgroups=pgroups)
self._generate_pgroup_dialog(curr=curr, pgroups=pgroups)
return
self.set_pgroup.emit(entered_text)
+2 -2
View File
@@ -71,7 +71,7 @@ class TitleLabel(QLabel):
f"QPushButton {{ background: transparent; border: none;"
f" color: {BANNER_TEXT}; font-size: {FONT_VALUE}; font-weight: 700; }}"
)
self.toggle_button.setToolTip("Minimise panel")
self.toggle_button.setToolTip("Minimize panel")
self.toggle_button.setFixedSize(21, 21)
self.toggle_button.setCursor(Qt.CursorShape.PointingHandCursor)
self.toggle_button.clicked.connect(self.toggle_collapsed)
@@ -162,7 +162,7 @@ class TitleLabel(QLabel):
return
self._set_visible(parent_layout, not self._collapsed)
self.toggle_button.setText("+" if self._collapsed else "")
self.toggle_button.setToolTip("Restore panel" if self._collapsed else "Minimise panel")
self.toggle_button.setToolTip("Restore panel" if self._collapsed else "Minimize panel")
def _set_visible(self, layout: QLayout, visible: bool) -> None:
# Recursive: panels like SamcamPanel nest sub-layouts via addLayout.
@@ -201,14 +201,14 @@ def test_service_falls_back_to_loop_all_when_face_ratio_is_low(monkeypatch, cont
assert context._progress_events[-1]["running"] is False
def test_service_applies_centre_correction_when_target_is_far_from_beam(
def test_service_applies_center_correction_when_target_is_far_from_beam(
monkeypatch, context, mock_logger
):
service = FaceDetectionService(context=context, logger=mock_logger)
model = _box(MLBoxType.LOOP_FACE, 40, 160, 80, 200)
service._centre_correction(model, tolerance=0.2)
service._center_correction(model, tolerance=0.2)
assert isinstance(context.deps.devs.smargon_pos, SmargonCoordinate)
assert context.deps.devs.smargon_pos.sh_mm.x == pytest.approx(1.0)
@@ -96,7 +96,7 @@ def test_record_best_effort_step_failure_marks_progress_and_logs_warning(mock_lo
current_step="Center",
steps=[
StepState(
step=WorkflowStateKind.LOOP_CENTRE, status=StepStatus.RUNNING, message="Centering"
step=WorkflowStateKind.LOOP_CENTER, status=StepStatus.RUNNING, message="Centering"
)
],
finished=False,
@@ -116,7 +116,7 @@ def test_record_best_effort_step_failure_marks_progress_and_logs_warning(mock_lo
with patch("aare.daq.daq.logger", mock_logger):
daq._record_best_effort_step_failure(
progress=progress,
step=WorkflowStateKind.LOOP_CENTRE,
step=WorkflowStateKind.LOOP_CENTER,
error=LoopCenteringFailed("Loop centering failed"),
sample=sample,
code="LOOP_CENTERING_FAILED",
+1 -1
View File
@@ -300,7 +300,7 @@ def test_cancel_baton_request(mock_cfg, token_data):
status=BatonRequestStatus.PENDING,
)
res = cancel_baton_request(mock_cfg, token_data)
assert res["cancelled"] is True
assert res["canceled"] is True
mock_cfg.clear_pending_baton_request.assert_called_once()
@@ -46,7 +46,7 @@ def test_automation_progress_state_round_trip_dataclass():
step=WorkflowStateKind.MOUNT, status=StepStatus.SUCCESS, message="Mount complete"
),
StepState(
step=WorkflowStateKind.LOOP_CENTRE,
step=WorkflowStateKind.LOOP_CENTER,
status=StepStatus.RUNNING,
message="Centering sample",
),
+12 -12
View File
@@ -66,67 +66,67 @@ def make_service() -> RasterService:
return RasterService(context=context, logger=MagicMock())
def test_grid_image_id_from_centre_offset_single_cell():
def test_grid_image_id_from_center_offset_single_cell():
request = make_request(1, 1)
image_id = RasterService._grid_image_id_from_centre_offset(
image_id = RasterService._grid_image_id_from_center_offset(
x_mm=request.grid_size_mm.x / 2.0, y_mm=request.grid_size_mm.y / 2.0, request=request
)
assert image_id == 0
def test_grid_image_id_from_centre_offset_odd_grid():
def test_grid_image_id_from_center_offset_odd_grid():
request = make_request(5, 3, cell_x=0.01, cell_y=0.02)
x_mm = ((request.n_x - 1) * request.grid_size_mm.x) / 2.0
y_mm = ((request.n_y - 1) * request.grid_size_mm.y) / 2.0
image_id = RasterService._grid_image_id_from_centre_offset(
image_id = RasterService._grid_image_id_from_center_offset(
x_mm=x_mm, y_mm=y_mm, request=request
)
assert image_id == 7
def test_grid_image_id_from_centre_offset_even_grid():
def test_grid_image_id_from_center_offset_even_grid():
request = make_request(4, 4, cell_x=0.01, cell_y=0.01)
x_mm = ((request.n_x - 1) * request.grid_size_mm.x) / 2.0
y_mm = ((request.n_y - 1) * request.grid_size_mm.y) / 2.0
image_id = RasterService._grid_image_id_from_centre_offset(
image_id = RasterService._grid_image_id_from_center_offset(
x_mm=x_mm, y_mm=y_mm, request=request
)
assert image_id == 6
def test_grid_image_id_from_centre_offset_vertical_scan():
def test_grid_image_id_from_center_offset_vertical_scan():
request = make_request(1, 50, cell_x=0.01, cell_y=0.005)
x_mm = request.grid_size_mm.x / 2.0
y_mm = ((request.n_y - 1) * request.grid_size_mm.y) / 2.0
image_id = RasterService._grid_image_id_from_centre_offset(
image_id = RasterService._grid_image_id_from_center_offset(
x_mm=x_mm, y_mm=y_mm, request=request
)
assert image_id == 24
def test_grid_image_id_from_centre_offset_rejects_non_positive_cell_size():
def test_grid_image_id_from_center_offset_rejects_non_positive_cell_size():
request = make_request(3, 3)
request.grid_size_mm = Coordinate(x=0.0, y=0.01)
with pytest.raises(ValueError, match="grid_size_mm must be positive"):
RasterService._grid_image_id_from_centre_offset(x_mm=0.0, y_mm=0.0, request=request)
RasterService._grid_image_id_from_center_offset(x_mm=0.0, y_mm=0.0, request=request)
def test_grid_image_id_from_centre_offset_rejects_zero_dimensions():
def test_grid_image_id_from_center_offset_rejects_zero_dimensions():
request = make_request(1, 1)
request.n_x = 0
with pytest.raises(ValueError, match="Raster grid dimensions must be >= 1"):
RasterService._grid_image_id_from_centre_offset(x_mm=0.0, y_mm=0.0, request=request)
RasterService._grid_image_id_from_center_offset(x_mm=0.0, y_mm=0.0, request=request)
def test_upload_raster_diffraction_preview_skips_out_of_range_image_id():
@@ -28,7 +28,7 @@ def test_parse_automation_progress_from_sse_payload():
"message": "Mount complete",
},
{
"step": WorkflowStateKind.LOOP_CENTRE.value,
"step": WorkflowStateKind.LOOP_CENTER.value,
"status": StepStatus.RUNNING.value,
"message": "Centering sample",
},
@@ -64,7 +64,7 @@ def test_parse_automation_progress_from_sse_payload():
assert progress.steps[0].status == StepStatus.SUCCESS
assert progress.steps[0].message == "Mount complete"
assert progress.steps[1].step == WorkflowStateKind.LOOP_CENTRE
assert progress.steps[1].step == WorkflowStateKind.LOOP_CENTER
assert progress.steps[1].status == StepStatus.RUNNING
assert progress.steps[1].message == "Centering sample"
@@ -110,7 +110,7 @@ def test_parse_automation_progress_parses_events():
"exception_class": "LoopCenteringFailed",
"message": "Loop centering failed",
"sample_id": 42,
"context": {"step": "loop_centre"},
"context": {"step": "loop_center"},
}
],
"finished": False,
@@ -132,7 +132,7 @@ def test_handle_automation_progress_event_dedups_events_by_timestamp(caplog):
'{"seq":1,"progress":{"current_step":"Center","steps":[],"events":['
'{"ts":"2026-06-03T10:00:00+00:00","level":"WARNING","code":"LOOP_CENTERING_FAILED",'
'"exception_class":"LoopCenteringFailed","message":"Loop centering failed","sample_id":7,'
'"context":{"step":"loop_centre"}}],"finished":false,"success":null}}'
'"context":{"step":"loop_center"}}],"finished":false,"success":null}}'
)
caplog.clear()
@@ -158,7 +158,7 @@ def test_handle_automation_progress_event_trips_recurrence_watcher():
'{"seq":1,"progress":{"current_step":"Center","steps":[],"events":['
'{"ts":"2026-06-03T10:00:00+00:00","level":"WARNING","code":"LOOP_CENTERING_FAILED",'
'"exception_class":"LoopCenteringFailed","message":"Loop centering failed","sample_id":7,'
'"context":{"step":"loop_centre"}}],"finished":false,"success":null}}'
'"context":{"step":"loop_center"}}],"finished":false,"success":null}}'
)
worker._handle_automation_progress_event(payload)
@@ -202,7 +202,7 @@ def test_process_automation_progress_buffer_handles_split_sse_event():
first_chunk = 'data: {"seq":1,"progress":{"current_step":"Center","steps":[{"step":"mount",'
second_chunk = (
'"status":"success","message":"Mount complete"},{"step":"loop_centre",'
'"status":"success","message":"Mount complete"},{"step":"loop_center",'
'"status":"running","message":"Centering sample"}],'
'"finished":false,"success":null}}\n\n'
)
@@ -224,7 +224,7 @@ def test_process_automation_progress_buffer_handles_split_sse_event():
assert len(progress.steps) == 2
assert progress.steps[0].step == WorkflowStateKind.MOUNT
assert progress.steps[0].status == StepStatus.SUCCESS
assert progress.steps[1].step == WorkflowStateKind.LOOP_CENTRE
assert progress.steps[1].step == WorkflowStateKind.LOOP_CENTER
assert progress.steps[1].status == StepStatus.RUNNING
assert worker._automation_progress_buffer == ""
+5 -5
View File
@@ -34,7 +34,7 @@ def test_non_staff_never_reach_admin_only_states(qtbot):
assert BeamlineStateEnum.BeamstopAlignment not in targets
assert BeamlineStateEnum.FluxMeasurement not in targets
assert BeamlineStateEnum.DataCollection in targets # non-admin route stays
# Greyed like unreachable states, and the warning tip carries the red
# Grayed like unreachable states, and the warning tip carries the red
# QToolTip wash while a normal-hint button does not.
gated = panel._buttons[BeamlineStateEnum.BeamLocation]
assert gated.cursor().shape() == Qt.CursorShape.ForbiddenCursor
@@ -74,13 +74,13 @@ def test_availability_palette_and_cursors(qtbot):
panel = _panel(qtbot)
panel.set_current_state(BeamlineStateEnum.Maintenance)
available = panel._buttons[BeamlineStateEnum.SampleExchange]
grey = panel._buttons[BeamlineStateEnum.FluxMeasurement]
gray = panel._buttons[BeamlineStateEnum.FluxMeasurement]
assert STATE_AVAILABLE in available.styleSheet()
assert available.cursor().shape() == Qt.CursorShape.PointingHandCursor
assert not available.font().bold()
assert STATE_UNAVAILABLE in grey.styleSheet()
assert grey.cursor().shape() == Qt.CursorShape.ForbiddenCursor
assert grey.toolTip() == ""
assert STATE_UNAVAILABLE in gray.styleSheet()
assert gray.cursor().shape() == Qt.CursorShape.ForbiddenCursor
assert gray.toolTip() == ""
assert available.toolTip() != ""
+1 -1
View File
@@ -253,7 +253,7 @@ def test_alt_wheel_axis_swap_still_changes_exposure(camera):
def test_hover_hud_coords_and_scale_bar(camera):
# Hover inside the image: bottom-right HUD paints coords + grey scale bar.
# Hover inside the image: bottom-right HUD paints coords + gray scale bar.
_mouse_move(camera, QPoint(400, 300))
assert camera._hover_pos is not None
camera.grab()
+1 -1
View File
@@ -600,7 +600,7 @@ def test_nonstaff_beamline_gate_popups(qtbot, mock_ui_state):
qtbot.mousePress(bar, Qt.MouseButton.LeftButton, pos=bar.tabRect(0).center())
assert tip.showText.called, "gated tab click must explain the gate"
# The greyed-out Auxiliary-puck tab explains itself the same way.
# The grayed-out Auxiliary-puck tab explains itself the same way.
aux_bar = win.sample_lists_tabs.tabBar()
with patch("aare.gui.main_window.QToolTip") as tip:
qtbot.mousePress(aux_bar, Qt.MouseButton.LeftButton, pos=aux_bar.tabRect(1).center())
+1 -1
View File
@@ -38,7 +38,7 @@ def test_stage_apply_settle(chi, qtbot):
assert box.property("movestate") == "moving"
assert not group.button.isEnabled()
group.update_actual("chi", 10.0) # still travelling
group.update_actual("chi", 10.0) # still traveling
assert box.property("movestate") == "moving"
assert box.value == 25.0 # box keeps showing the target