diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 5353b233..955a5811 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -42,6 +42,63 @@ jobs: index_username: ${{ secrets.GITEA_USER }} index_password: ${{ secrets.GITEA_TOKEN }} + - name: Run Pytest + env: + QT_QPA_PLATFORM: offscreen + BEAMLINE: SIMULATED + run: | + source .venv/bin/activate + pytest ./tests/unit + + test-with-beamline-plugins: + runs-on: ubuntu-latest + needs: ["lint", "test"] + strategy: + matrix: + plugin_repo: ["pxi_bec", "pxii_bec", "pxiii_bec"] + + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Setup + uses: ./.gitea/actions/install + with: + python_version: "3.12" + index_username: ${{ secrets.GITEA_USER }} + index_password: ${{ secrets.GITEA_TOKEN }} + + - name: Install beamline plugin + run: | + source .venv/bin/activate + git clone https://gitea.psi.ch/bec/mx_bec.git + git clone https://gitea.psi.ch/bec/${{ matrix.plugin_repo }}.git + uv pip install -e ./mx_bec + uv pip install -e ./${{ matrix.plugin_repo }} + + - name: Run Pytest + env: + QT_QPA_PLATFORM: offscreen + BEAMLINE: SIMULATED + run: | + source .venv/bin/activate + pytest ./tests/unit + + test-with-coverage: + runs-on: ubuntu-latest + needs: ["lint", "test", "test-with-beamline-plugins"] + + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Setup + uses: ./.gitea/actions/install + with: + python_version: "3.12" + index_username: ${{ secrets.GITEA_USER }} + index_password: ${{ secrets.GITEA_TOKEN }} + - name: Run Pytest with Coverage env: QT_QPA_PLATFORM: offscreen diff --git a/src/aare/beamline_dispatch/default/beamline_dispatch.py b/src/aare/beamline_dispatch/default/beamline_dispatch.py index 24aac23f..ea31ff95 100644 --- a/src/aare/beamline_dispatch/default/beamline_dispatch.py +++ b/src/aare/beamline_dispatch/default/beamline_dispatch.py @@ -1,6 +1,6 @@ import os -from aare.beamline_dispatch.protocols import AuthDispatch, BeamlineDispatch +from aare.beamline_dispatch.protocols import AuthDispatch, BeamlineDispatch, BecMacros class DefaultAuthDispatch(AuthDispatch): @@ -17,4 +17,8 @@ class DefaultDispatch(BeamlineDispatch): Should be safe and fail rather than assuming anything.""" def __init__(self) -> None: - self.auth = DefaultAuthDispatch() + self._auth = DefaultAuthDispatch() + + @property + def auth(self): + return self._auth diff --git a/src/aare/beamline_dispatch/protocols.py b/src/aare/beamline_dispatch/protocols.py index 4aaddb41..9074e2ef 100644 --- a/src/aare/beamline_dispatch/protocols.py +++ b/src/aare/beamline_dispatch/protocols.py @@ -1,9 +1,37 @@ -from typing import Protocol +from abc import ABC, abstractmethod +from typing import Any -class AuthDispatch(Protocol): +class AuthDispatch(ABC): + @abstractmethod def get_jwt_key(self) -> str: ... -class BeamlineDispatch(Protocol): - auth: AuthDispatch +class BecMacros(ABC): + @staticmethod + @abstractmethod + def save_and_reload(): ... + @staticmethod + @abstractmethod + def save_current_position(device, position, axis=None, force=False, max_delta=0.5) -> None: ... + @staticmethod + @abstractmethod + def init_beamline_environment() -> tuple[Any, Any]: ... + @staticmethod + @abstractmethod + def bl_energy(energy_ev, move_gap=True, mono_scan=True, plot=True): ... + @staticmethod + @abstractmethod + def get_current_energy(): ... + @staticmethod + @abstractmethod + def mono_pitch_scan(plot=True): ... + + +class BeamlineDispatch(ABC): + @property + @abstractmethod + def auth(self) -> AuthDispatch: ... + @property + @abstractmethod + def bec_macros(self) -> BecMacros: ... diff --git a/src/aare/beamline_dispatch/simulated/beamline_dispatch.py b/src/aare/beamline_dispatch/simulated/beamline_dispatch.py index 0d285dfb..88429d77 100644 --- a/src/aare/beamline_dispatch/simulated/beamline_dispatch.py +++ b/src/aare/beamline_dispatch/simulated/beamline_dispatch.py @@ -1,5 +1,7 @@ +from typing import Any + from aare.beamline_dispatch.default.beamline_dispatch import DefaultDispatch -from aare.beamline_dispatch.protocols import AuthDispatch +from aare.beamline_dispatch.protocols import AuthDispatch, BecMacros class SimulatedAuthDispatch(AuthDispatch): @@ -7,6 +9,26 @@ class SimulatedAuthDispatch(AuthDispatch): return "Ns93ijN8VHv4ybvXaGNEDKUb3Sif4m4MYpfcEBIcs1h" +class SimulatedBecMacros(BecMacros): + @staticmethod + def save_and_reload(): ... + @staticmethod + def save_current_position(device, position, axis=None, force=False, max_delta=0.5) -> None: ... + @staticmethod + def init_beamline_environment() -> tuple[Any, Any]: ... + @staticmethod + def bl_energy(energy_ev, move_gap=True, mono_scan=True, plot=True): ... + @staticmethod + def get_current_energy(): ... + @staticmethod + def mono_pitch_scan(plot=True): ... + + class SimulatedDispatch(DefaultDispatch): def __init__(self) -> None: - self.auth = SimulatedAuthDispatch() + self._auth = SimulatedAuthDispatch() + self._bec_macros = SimulatedBecMacros() + + @property + def bec_macros(self): + return self._bec_macros diff --git a/src/aare/beamline_dispatch/x10sa/beamline_dispatch.py b/src/aare/beamline_dispatch/x10sa/beamline_dispatch.py index 421dbbf3..a6647caf 100644 --- a/src/aare/beamline_dispatch/x10sa/beamline_dispatch.py +++ b/src/aare/beamline_dispatch/x10sa/beamline_dispatch.py @@ -1,4 +1,41 @@ +from typing import Any + from aare.beamline_dispatch.default.beamline_dispatch import DefaultDispatch +from aare.beamline_dispatch.protocols import BecMacros -class X10saDispatch(DefaultDispatch): ... +class X10SaBecMacros(BecMacros): + def __init__(self) -> None: + from pxii_bec.macros.build_devices import save_and_reload, save_current_position + from pxii_bec.macros.init_beamline import init_beamline_environment + from pxii_bec.macros.pxii_energy import bl_energy, get_current_energy, mono_pitch_scan + + self.save_and_reload = save_and_reload + self.save_current_position = save_current_position + self.init_beamline_environment = init_beamline_environment + self.bl_energy = bl_energy + self.get_current_energy = get_current_energy + self.mono_pitch_scan = mono_pitch_scan + + @staticmethod + def save_and_reload(): ... + @staticmethod + def save_current_position(device, position, axis=None, force=False, max_delta=0.5) -> None: ... + @staticmethod + def init_beamline_environment() -> tuple[Any, Any]: ... + @staticmethod + def bl_energy(energy_ev, move_gap=True, mono_scan=True, plot=True): ... + @staticmethod + def get_current_energy(): ... + @staticmethod + def mono_pitch_scan(plot=True): ... + + +class X10saDispatch(DefaultDispatch): + def __init__(self) -> None: + super().__init__() + self._bec_macros = X10SaBecMacros() + + @property + def bec_macros(self) -> BecMacros: + return self._bec_macros diff --git a/src/aare/devices/bec_worker.py b/src/aare/devices/bec_worker.py index dff3010e..8e1fe4e1 100644 --- a/src/aare/devices/bec_worker.py +++ b/src/aare/devices/bec_worker.py @@ -12,23 +12,11 @@ from bec_ipython_client.signals import OperationMode from bec_lib.device import RPCError, ScanRequestError from bec_lib.procedures.helper import FrontendProcedureHelper from bec_lib.service_config import ServiceConfig -from pxii_bec.macros.build_devices import save_and_reload, save_current_position -from pxii_bec.macros.init_beamline import init_beamline_environment -from pxii_bec.macros.pxii_energy import bl_energy, get_current_energy, mono_pitch_scan + +from aare.beamline_dispatch.beamline_dispatch import get_beamline_dispatch logger = setup_logger("aareDAQ") -# specify up to 10 queue to runs in parallel, request more if needed! -# st = client.proc.request_new("sleep", ((), {"time_s":5}), queue="test") - -# to see all deevices -# devs.show_all - -# helper fucntions -# helper.get.active_and_pending_queue_names() -# helper.get.running_procedures() -# helper.request.abort_queue() - class DetectorCoverEnum(str, Enum): """Enum for the detector cover position @@ -76,6 +64,8 @@ class BECClientWorker: else: raise ValueError(f"Unknown beamline: {beamline}") + self.dispatch = get_beamline_dispatch() + if self.beamline is MXBeamline.SIMULATED: self.simulated = True @@ -93,7 +83,7 @@ class BECClientWorker: self.dev = self.client.device_manager.devices print(self.dev.keys()) self.scans = self.client.scans - self.macros = self.client.macros + self.macros = self.dispatch.bec_macros self._load_user_macros() print(self._list_all_macros()) self.helper = FrontendProcedureHelper(self.client.connector) @@ -107,7 +97,7 @@ class BECClientWorker: def _init_beamline_environment(self): try: - self.position_devices, self.planner = init_beamline_environment() + self.position_devices, self.planner = self.macros.init_beamline_environment() self._backlight_brightness = self.position_devices["bl_bright"] self._frontlight_brightness = self.position_devices["fl_bright"] self._zoom = self.dev.scam_zoom