Beamline state panel: exposed new beamline state changes to GUI

This commit is contained in:
2026-06-24 15:29:59 +02:00
committed by appleb_m
parent 6797e26e5a
commit f444f6c2c8
5 changed files with 203 additions and 37 deletions
+4
View File
@@ -370,6 +370,8 @@ class BeamlineStateEnum(Enum):
Moving = 8
RobotSampleExchange = 9
XtalSnapshot = 10
BeamstopAlignment = 11
FluxMeasurement = 12
def display_name(self) -> str:
return {
@@ -383,6 +385,8 @@ class BeamlineStateEnum(Enum):
BeamlineStateEnum.Moving: "Moving",
BeamlineStateEnum.RobotSampleExchange: "Robot sample exchange",
BeamlineStateEnum.XtalSnapshot: "Xtal snapshot",
BeamlineStateEnum.BeamstopAlignment: "Beamstop alignment",
BeamlineStateEnum.FluxMeasurement: "Flux measurement",
}.get(self, "-")
+124 -22
View File
@@ -2827,6 +2827,41 @@ class AareDAQ:
elif target == BeamlineStateEnum.SampleExchange:
workflows.bl2sa(self.__devs, self.__cfg)
workflows.sa2se(self.__devs, self.__cfg)
elif target == BeamlineStateEnum.BeamstopAlignment:
workflows.bl2ba(self.__devs, self.__cfg)
elif target == BeamlineStateEnum.FluxMeasurement:
workflows.bl2flux_measurement(self.__devs, self.__cfg)
else:
raise TransformationInvalidException(
f"Cannot go from {curr_state} to {target}, not implemented"
)
case BeamlineStateEnum.BeamstopAlignment:
if target == BeamlineStateEnum.SampleAlignment:
workflows.ba2sa(self.__devs, self.__cfg)
elif target == BeamlineStateEnum.SampleExchange:
workflows.ba2sa(self.__devs, self.__cfg)
workflows.sa2se(self.__devs, self.__cfg)
elif target == BeamlineStateEnum.FluxMeasurement:
workflows.ba2flux_measurement(self.__devs, self.__cfg)
elif target == BeamlineStateEnum.BeamLocation:
workflows.ba2bl(self.__devs, self.__cfg)
elif target == BeamlineStateEnum.SampleExchange:
workflows.bl2sa(self.__devs, self.__cfg)
workflows.sa2se(self.__devs, self.__cfg)
else:
raise TransformationInvalidException(
f"Cannot go from {curr_state} to {target}, not implemented"
)
case BeamlineStateEnum.FluxMeasurement:
if target == BeamlineStateEnum.SampleAlignment:
workflows.flux_measurement2sa(self.__devs, self.__cfg)
elif target == BeamlineStateEnum.SampleExchange:
workflows.flux_measurement2sa(self.__devs, self.__cfg)
workflows.sa2se(self.__devs, self.__cfg)
elif target == BeamlineStateEnum.BeamLocation:
workflows.flux_measurement2bl(self.__devs, self.__cfg)
elif target == BeamlineStateEnum.BeamstopAlignment:
workflows.flux_measurement2ba(self.__devs, self.__cfg)
else:
raise TransformationInvalidException(
f"Cannot go from {curr_state} to {target}, not implemented"
@@ -2844,6 +2879,10 @@ class AareDAQ:
workflows.sa2xrf(self.__devs, self.__cfg)
elif target == BeamlineStateEnum.BeamLocation:
workflows.sa2bl(self.__devs, self.__cfg)
elif target == BeamlineStateEnum.BeamstopAlignment:
workflows.sa2ba(self.__devs, self.__cfg)
elif target == BeamlineStateEnum.FluxMeasurement:
workflows.sa2flux_measurement(self.__devs, self.__cfg)
elif target == BeamlineStateEnum.XtalSnapshot:
workflows.sa2xtal_snapshot(self.__devs, self.__cfg)
else:
@@ -2923,7 +2962,9 @@ class AareDAQ:
@property
def diffraction_geometry(self) -> DiffractionGeometry:
try:
#start = time.perf_counter()
det_cfg = self.__jfjoch.detector()
#logger.debug(f"Safe diffraction geometry call took {time.perf_counter() - start:.3f}s")
return DiffractionGeometry(
energy_keV=self.__devs.energy_kev,
dtz_mm=self.__devs.dtz,
@@ -2954,19 +2995,62 @@ class AareDAQ:
@property
def beamline_status(self) -> BeamlineStatus:
try:
# start = time.perf_counter()
ring_current = self.__devs.ring_current
# logger.debug(f"Safe ring current call took {time.perf_counter() - start:.3f}s")
# start = time.perf_counter()
front_light = self.front_light
# logger.debug(f"Safe front light call took {time.perf_counter() - start:.3f}s")
# start = time.perf_counter()
back_light = self.back_light
# logger.debug(f"Safe back light call took {time.perf_counter() - start:.3f}s")
# start = time.perf_counter()
cryojet_temp = self.__devs.cryojet_temp
# logger.debug(f"Safe cryojet temp call took {time.perf_counter() - start:.3f}s")
# start = time.perf_counter()
shutter_open = self.__devs.shutter
# logger.debug(f"Safe shutter call took {time.perf_counter() - start:.3f}s")
# start = time.perf_counter()
exp_shutter_open = self.__devs.exp_shutter.state()
# logger.debug(f"Safe exp shutter call took {time.perf_counter() - start:.3f}s")
# start = time.perf_counter()
flux = self.__devs.full_flux
# logger.debug(f"Safe flux call took {time.perf_counter() - start:.3f}s")
# start = time.perf_counter()
samcam_settings = self.__devs.samcam_settings
# logger.debug(f"Safe samcam call took {time.perf_counter() - start:.3f}s")
# start = time.perf_counter()
bl = self.__bl
# logger.debug(f"Safe bl call took {time.perf_counter() - start:.3f}s")
# start = time.perf_counter()
transmission = self.__devs.transmission
# logger.debug(f"Safe transmission call took {time.perf_counter() - start:.3f}s")
# start = time.perf_counter()
zoom = self.__devs.zoom
# logger.debug(f"Safe zoom call took {time.perf_counter() - start:.3f}s")
# start = time.perf_counter()
commisioning_mode = self.__cfg.commissioning_mode
# logger.debug(f"Safe commissioning_mode call took {time.perf_counter() - start:.3f}s")
start = time.perf_counter()
#TODO work this one out, do it on startup!
dtz_min = self.__devs.dtz_low
logger.debug(f"Safe dtz_min call took {time.perf_counter() - start:.3f}s")
start = time.perf_counter()
dtz_max = self.__devs.dtz_high
logger.debug(f"Safe dtz_max call took {time.perf_counter() - start:.3f}s")
return BeamlineStatus(
ring_current_mA=self.__devs.ring_current,
front_light=self.front_light,
back_light=self.back_light,
cryojet_K=self.__devs.cryojet_temp,
shutter_open=self.__devs.shutter,
exp_shutter_open=self.__devs.exp_shutter.state(),
flux_ph_s=self.__devs.full_flux,
sample_camera=self.__devs.samcam_settings,
name=self.__bl,
transmission=self.__devs.transmission,
zoom=self.__devs.zoom,
commissioning_mode=self.__cfg.commissioning_mode,
ring_current_mA=ring_current,
front_light=front_light,
back_light=back_light,
cryojet_K=cryojet_temp,
shutter_open=shutter_open,
exp_shutter_open=exp_shutter_open,
flux_ph_s=flux,
sample_camera=samcam_settings,
name=bl,
transmission=transmission,
zoom=zoom,
commissioning_mode=commisioning_mode,
dtz_min=20,
dtz_max=1000,
)
@@ -3089,22 +3173,37 @@ class AareDAQ:
@property
def status(self) -> DAQStatusModel:
try:
#og_start = time.perf_counter()
safe_sample, tell_ok, tell_err = self._safe_sample()
#logger.debug(f"Safe sample info call took {time.perf_counter() - og_start:.3f}s")
#start = time.perf_counter()
safe_geom, smargon_ok, smargon_err, aerotech_ok, aerotech_err = self._safe_geom()
#logger.debug(f"safe geom call took {time.perf_counter() - start:.3f}s")
#start = time.perf_counter()
safe_tell_state = self._safe_tell_state()
return DAQStatusModel(
state=self.state,
busy=self.busy,
geom=safe_geom,
bl=self._safe_beamline_status(),
sample=safe_sample,
session=SessionStatus(
#logger.debug(f"Safe tell call call took {time.perf_counter() - start:.3f}s")
#start = time.perf_counter()
safe_beamline_status = self._safe_beamline_status()
#logger.debug(f"Safe beamline status call took {time.perf_counter() - start:.3f}s")
#start = time.perf_counter()
safe_diffraction_geom = self._safe_diffraction_geometry()
#logger.debug(f"Safe diffraction geometry call took {time.perf_counter() - start:.3f}s")
#start = time.perf_counter()
session_status = SessionStatus(
current_pgroup=self.__cfg.pgroup,
session=self.__cfg.session_state(0), # 0 is dummy session
staff=False
),
diffraction=self._safe_diffraction_geometry(),
)
#logger.debug(f"Safe session status call took {time.perf_counter() - start:.3f}s")
#start = time.perf_counter()
status = DAQStatusModel(
state=self.state,
busy=self.busy,
geom=safe_geom,
bl=safe_beamline_status,
sample=safe_sample,
session=session_status,
diffraction=safe_diffraction_geom,
box=self.__saved_box,
last_best_res=self.__cfg.last_best_res,
last_best_b_factor=self.__cfg.last_best_b_factor,
@@ -3117,6 +3216,9 @@ class AareDAQ:
aerotech_connected=aerotech_ok,
aerotech_error=aerotech_err,
)
#logger.debug(f"Creating DAQStatusModel took {time.perf_counter() - start:.3f}s")
#logger.debug(f"returning status call took {time.perf_counter() - og_start:.3f}s")
return status
except Exception as e:
logger.error(f"Failed to retrieve DAQ status: {e}")
+30
View File
@@ -1259,6 +1259,36 @@ async def beam_location(token: str = Depends(oauth2_scheme)):
auth.check_jwt_staff(cfg, auth.parse_token(token))
daq.state = BeamlineStateEnum.BeamLocation
@app.post("/state/beamstop_alignment")
async def beamstop_alignment(token: str = Depends(oauth2_scheme)):
"""
Transition beamline state to BeamstopAlignment.
Args:
token: OAuth2 access token.
Returns:
"OK" on success.
"""
auth.check_jwt_staff(cfg, auth.parse_token(token))
daq.state = BeamlineStateEnum.BeamstopAlignment
return "OK"
@app.post("/state/flux_measurement")
async def flux_measurement(token: str = Depends(oauth2_scheme)):
"""
Transition beamline state to FluxMeasurement.
Args:
token: OAuth2 access token.
Returns:
"OK" on success.
"""
auth.check_jwt_staff(cfg, auth.parse_token(token))
daq.state = BeamlineStateEnum.FluxMeasurement
return "OK"
@app.post("/state/data_collection")
async def data_collection(token: str = Depends(oauth2_scheme)):
"""
+37 -15
View File
@@ -53,9 +53,11 @@ class BeamlineStatePanel(QFrame):
xtal_snapshot = Signal()
xray_fluorescence = Signal()
robot_sample_exchange = Signal()
beamstop_alignment = Signal()
flux_measurement = Signal()
set_width = 400
map_height = 470
map_height = 542
title_height = 50
collapsed_height = 50
station_radius = 8
@@ -85,14 +87,16 @@ class BeamlineStatePanel(QFrame):
self._label_hover_bg = "rgba(244, 196, 48, 0.22)"
self._group_colors: dict[BeamlineStateEnum, QColor] = {
BeamlineStateEnum.DewarTransfer: QColor(128, 90, 213), # Purple
BeamlineStateEnum.SampleExchange: QColor(237, 137, 54), # Orange
BeamlineStateEnum.RobotSampleExchange: QColor(237, 137, 54), # Orange
BeamlineStateEnum.SampleAlignment: QColor(72, 187, 120), # Green
BeamlineStateEnum.BeamLocation: QColor(72, 187, 120), # Green
BeamlineStateEnum.DataCollection: QColor(236, 72, 153), # Pink
BeamlineStateEnum.XtalSnapshot: QColor(236, 72, 153), # Pink
BeamlineStateEnum.XrayFluorescence: QColor(236, 72, 153), # Pink
BeamlineStateEnum.DewarTransfer: QColor(128, 90, 213), # Purple
BeamlineStateEnum.SampleExchange: QColor(237, 137, 54), # Orange
BeamlineStateEnum.RobotSampleExchange: QColor(237, 137, 54), # Orange
BeamlineStateEnum.SampleAlignment: QColor(72, 187, 120), # Green
BeamlineStateEnum.BeamLocation: QColor(72, 187, 120), # Green
BeamlineStateEnum.BeamstopAlignment: QColor(72, 187, 120), # Green
BeamlineStateEnum.FluxMeasurement: QColor(72, 187, 120), # Green
BeamlineStateEnum.DataCollection: QColor(236, 72, 153), # Pink
BeamlineStateEnum.XtalSnapshot: QColor(236, 72, 153), # Pink
BeamlineStateEnum.XrayFluorescence: QColor(236, 72, 153), # Pink
}
self._group_label_colors: dict[BeamlineStateEnum, str] = {
@@ -105,6 +109,8 @@ class BeamlineStatePanel(QFrame):
BeamlineStateEnum.RobotSampleExchange: "rgba(237, 137, 54, 0.16)",
BeamlineStateEnum.SampleAlignment: "rgba(72, 187, 120, 0.16)",
BeamlineStateEnum.BeamLocation: "rgba(72, 187, 120, 0.16)",
BeamlineStateEnum.BeamstopAlignment: "rgba(72, 187, 120, 0.16)",
BeamlineStateEnum.FluxMeasurement: "rgba(72, 187, 120, 0.16)",
BeamlineStateEnum.DataCollection: "rgba(236, 72, 153, 0.14)",
BeamlineStateEnum.XtalSnapshot: "rgba(236, 72, 153, 0.14)",
BeamlineStateEnum.XrayFluorescence: "rgba(236, 72, 153, 0.14)",
@@ -112,13 +118,20 @@ class BeamlineStatePanel(QFrame):
self._stations = [
StationSpec(BeamlineStateEnum.DewarTransfer, "Dewar transfer", 54, 140, True, "Dewar transfer mode"),
StationSpec(BeamlineStateEnum.SampleExchange, "Manual sample exchange", 54, 176, True, "Manual sample exchange mode"),
StationSpec(BeamlineStateEnum.RobotSampleExchange, "Robot sample exchange", 54, 212, True, "Robot-assisted sample exchange"),
StationSpec(BeamlineStateEnum.SampleAlignment, "Sample alignment", 54, 248, True, "Sample centring and alignment mode"),
StationSpec(BeamlineStateEnum.SampleExchange, "Manual sample exchange", 54, 176, True,
"Manual sample exchange mode"),
StationSpec(BeamlineStateEnum.RobotSampleExchange, "Robot sample exchange", 54, 212, True,
"Robot-assisted sample exchange"),
StationSpec(BeamlineStateEnum.SampleAlignment, "Sample alignment", 54, 248, True,
"Sample centring and alignment mode"),
StationSpec(BeamlineStateEnum.BeamLocation, "Beam location", 54, 284, True, "Beam location mode"),
StationSpec(BeamlineStateEnum.DataCollection, "Data collection", 54, 320, True, "Measurement / collection mode"),
StationSpec(BeamlineStateEnum.XtalSnapshot, "Crystal snapshot", 54, 356, True, "Crystal snapshot mode"),
StationSpec(BeamlineStateEnum.XrayFluorescence, "XRF", 54, 392, True, "X-ray fluorescence mode"),
StationSpec(BeamlineStateEnum.BeamstopAlignment, "Beamstop alignment", 54, 320, True,
"Beamstop alignment mode"),
StationSpec(BeamlineStateEnum.FluxMeasurement, "Flux measurement", 54, 356, True, "Flux measurement mode"),
StationSpec(BeamlineStateEnum.DataCollection, "Data collection", 54, 392, True,
"Measurement / collection mode"),
StationSpec(BeamlineStateEnum.XtalSnapshot, "Crystal snapshot", 54, 428, True, "Crystal snapshot mode"),
StationSpec(BeamlineStateEnum.XrayFluorescence, "XRF", 54, 464, True, "X-ray fluorescence mode"),
]
self._segments = [
@@ -126,7 +139,12 @@ class BeamlineStatePanel(QFrame):
(BeamlineStateEnum.SampleExchange, BeamlineStateEnum.RobotSampleExchange),
(BeamlineStateEnum.RobotSampleExchange, BeamlineStateEnum.SampleAlignment),
(BeamlineStateEnum.SampleAlignment, BeamlineStateEnum.BeamLocation),
(BeamlineStateEnum.SampleAlignment, BeamlineStateEnum.BeamstopAlignment),
(BeamlineStateEnum.SampleAlignment, BeamlineStateEnum.FluxMeasurement),
(BeamlineStateEnum.SampleAlignment, BeamlineStateEnum.DataCollection),
(BeamlineStateEnum.BeamLocation, BeamlineStateEnum.BeamstopAlignment),
(BeamlineStateEnum.BeamLocation, BeamlineStateEnum.FluxMeasurement),
(BeamlineStateEnum.BeamstopAlignment, BeamlineStateEnum.FluxMeasurement),
(BeamlineStateEnum.DataCollection, BeamlineStateEnum.XtalSnapshot),
(BeamlineStateEnum.DataCollection, BeamlineStateEnum.XrayFluorescence),
]
@@ -339,6 +357,10 @@ class BeamlineStatePanel(QFrame):
self.robot_sample_exchange.emit()
elif state == BeamlineStateEnum.BeamLocation:
self.beam_location.emit()
elif state == BeamlineStateEnum.BeamstopAlignment:
self.beamstop_alignment.emit()
elif state == BeamlineStateEnum.FluxMeasurement:
self.flux_measurement.emit()
elif state == BeamlineStateEnum.DataCollection:
self.data_collection.emit()
elif state == BeamlineStateEnum.XtalSnapshot:
+8
View File
@@ -771,6 +771,14 @@ class DAQWorker(QObject):
def beam_location(self):
self.generic_post("state/beam_location")
@Slot()
def beamstop_alignment(self):
self.generic_post("state/beamstop_alignment")
@Slot()
def flux_measurement(self):
self.generic_post("state/flux_measurement")
@Slot()
def data_collection(self):
self.generic_post("state/data_collection")