Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions mxcubeweb/core/adapter/diffractometer_adapter.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from typing import ClassVar

from mxcubecore.HardwareObjects import (
GenericDiffractometer,
MiniDiff,
)
from mxcubecore.HardwareObjects import GenericDiffractometer, MiniDiff

from mxcubeweb.core.adapter.adapter_base import AdapterBase
from mxcubeweb.core.models.configmodels import ResourceHandlerConfigModel

resource_handler_config = ResourceHandlerConfigModel(
commands=["set_chip_layout", "set_phase"],
attributes=["data", "get_value", "head_configuration"],
)


class DiffractometerAdapter(AdapterBase):
Expand All @@ -17,12 +20,12 @@ class DiffractometerAdapter(AdapterBase):
GenericDiffractometer.GenericDiffractometer,
]

def __init__(self, ho, *args):
def __init__(self, ho, role, app):
"""
Args:
(object): Hardware object.
"""
super().__init__(ho, *args)
super().__init__(ho, role, app, resource_handler_config)
ho.connect("stateChanged", self._state_change)
ho.connect("valueChanged", self._state_change)
ho.connect("phaseChanged", self._diffractometer_phase_changed)
Expand All @@ -37,6 +40,12 @@ def _diffractometer_phase_changed(self, phase):
def _state_change(self, *args, **kwargs):
self.state_change(*args, **kwargs)

def get_value(self) -> dict:
return {
"currentPhase": self._ho.get_current_phase(),
"phaseList": self._ho.get_phase_list(),
}

def stop(self):
pass

Expand All @@ -53,3 +62,7 @@ def set_chip_layout(
) -> bool:
self._ho.set_chip_layout(layout_name)
return True

def set_phase(self, phase: str) -> bool:
self._ho.set_phase(phase)
return True
30 changes: 0 additions & 30 deletions mxcubeweb/core/components/beamline.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,42 +95,12 @@ def beamline_get_all_attributes(self):
{"energyScanElements": ho.get_available_elements().get("elements", [])}
)

data.update(self.diffractometer_get_info())

return data

def prepare_beamline_for_sample(self):
if hasattr(HWR.beamline.collect, "prepare_for_new_sample"):
HWR.beamline.collect.prepare_for_new_sample()

def diffractometer_set_phase(self, phase):
try:
HWR.beamline.diffractometer.wait_ready(30)
except Exception:
logging.getLogger("MX3.HWR").warning("Diffractometer not ready")

HWR.beamline.diffractometer.set_phase(phase)

def diffractometer_get_info(self):
ret = {}

try:
ret["useSC"] = HWR.beamline.diffractometer.use_sc
except AttributeError:
ret["useSC"] = False

try:
ret["currentPhase"] = HWR.beamline.diffractometer.get_current_phase()
except AttributeError:
ret["currentPhase"] = "None"

try:
ret["phaseList"] = HWR.beamline.diffractometer.get_phase_list()
except AttributeError:
ret["phaseList"] = []

return ret

def get_detector_info(self):
try:
filetype = HWR.beamline.detector.get_property("file_suffix")
Expand Down
39 changes: 0 additions & 39 deletions mxcubeweb/routes/diffractometer.py

This file was deleted.

9 changes: 0 additions & 9 deletions mxcubeweb/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,6 @@ def register_routes(mxcube):
from mxcubeweb.routes.beamline import init_route as init_beamline_route
from mxcubeweb.routes.csp_report import init_route as init_csp_route
from mxcubeweb.routes.detector import init_route as init_detector_route
from mxcubeweb.routes.diffractometer import (
init_route as init_diffractometer_route,
)
from mxcubeweb.routes.harvester import init_route as init_harvester_route
from mxcubeweb.routes.lims import init_route as init_lims_route
from mxcubeweb.routes.login import init_route as init_login_route
Expand All @@ -146,12 +143,6 @@ def register_routes(mxcube):
init_detector_route, mxcube, f"{url_root_prefix}/detector"
)

Server._register_route(
init_diffractometer_route,
mxcube,
f"{url_root_prefix}/diffractometer",
)

Server._register_route(init_lims_route, mxcube, f"{url_root_prefix}/lims")

Server._register_route(init_login_route, mxcube, f"{url_root_prefix}/login")
Expand Down
14 changes: 3 additions & 11 deletions test/test_diffractometer_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_set_phase(client):
current phase after the move is OP
"""
# Get current phase
resp = client.get("/mxcube/api/v0.1/diffractometer/info")
resp = client.get("/mxcube/api/v0.1/hwobj/diffractometer/diffractometer/get_value")
info = json.loads(resp.data)

phase_list = info["phaseList"]
Expand All @@ -23,13 +23,13 @@ def test_set_phase(client):

# Set a phase (any in the phase list)
resp = client.put(
"/mxcube/api/v0.1/diffractometer/phase",
"/mxcube/api/v0.1/hwobj/diffractometer/diffractometer/set_phase",
data=json.dumps({"phase": new_phase}),
content_type="application/json",
)

# Retrieve current phase
resp = client.get("/mxcube/api/v0.1/diffractometer/info")
resp = client.get("/mxcube/api/v0.1/hwobj/diffractometer/diffractometer/get_value")
actual_phase = json.loads(resp.data)["currentPhase"]

assert new_phase == actual_phase
Expand Down Expand Up @@ -88,11 +88,3 @@ def test_set_aperture(client):

assert ap == actual_aperture
assert actual_original_aperture == original_aperture


def test_get_diffractometer_info(client):
"""
Simply checks if the route runs and does not throws any exceptions
"""
resp = client.get("/mxcube/api/v0.1/diffractometer/info")
assert resp.status_code == 200
5 changes: 3 additions & 2 deletions ui/src/actions/login.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable promise/prefer-await-to-then */
import { fetchBeamlineSetup } from '../api/beamline';
import { fetchDetectorInfo } from '../api/detector';
import { fetchDiffractometerInfo } from '../api/diffractometer';
import { fetchValue } from '../api/hardware-object';
import { fetchHarvesterInitialState } from '../api/harvester';
import { sendSelectProposal } from '../api/lims';
Expand Down Expand Up @@ -110,7 +109,9 @@ export function getInitialState() {
fetchImageData()
.then((camera) => ({ camera }))
.catch(notify),
fetchDiffractometerInfo().catch(notify),
fetchValue('diffractometer', 'diffractometer')
.then((diffractometer) => ({ diffractometer }))
.catch(notify),
fetchDetectorInfo()
.then((detector) => ({ detector }))
.catch(notify),
Expand Down
7 changes: 4 additions & 3 deletions ui/src/actions/sampleview.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { sendUpdateCurrentPhase } from '../api/diffractometer';
import { sendSetValue } from '../api/hardware-object';
import { sendExecuteCommand, sendSetValue } from '../api/hardware-object';
import {
sendAbortCentring,
sendAcceptCentring,
Expand Down Expand Up @@ -355,7 +354,9 @@ export function changeAperture(size) {

export function changeCurrentPhase(phase) {
return async (dispatch) => {
await sendUpdateCurrentPhase(phase);
await sendExecuteCommand('diffractometer', 'diffractometer', 'set_phase', {
phase,
});
dispatch(setCurrentPhase(phase));
Comment on lines +357 to 360
Copy link
Collaborator

@axelboc axelboc Sep 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh just to double check: does sendExecuteCommand resolve to a value — i.e. does the server respond with the updated phase value? If so, it would be best to use that value to update the Redux store.

Or maybe the updated value is sent soon after via a signal and the dispatch is not needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good point. I overlooked the dispatch. sendExecuteCommand can return a value, in the case of set_phase its done via an event (because its a action that can take some time). We should perhaps when you mention it distinguish between the two types, i.e have a sendAsync variant or something ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment, I think I will keep things as it is. But Ill think about a pattern or how to better handle/specify values updated by events

Copy link
Collaborator

@axelboc axelboc Sep 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should perhaps when you mention it distinguish between the two types, i.e have a sendAsync variant or something ?

I see, thanks for the clarification! Yeah in that case it might be good, eventually, to have two distinct endpoints for predictability. 💯

};
}
11 changes: 0 additions & 11 deletions ui/src/api/diffractometer.js

This file was deleted.

4 changes: 2 additions & 2 deletions ui/src/reducers/sampleview.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ function sampleViewReducer(state = INITIAL_STATE, action = {}) {
x: action.data.beamInfo.size_x,
y: action.data.beamInfo.size_y,
},
phaseList: action.data.phaseList,
currentPhase: action.data.currentPhase,
phaseList: action.data.diffractometer.phaseList,
currentPhase: action.data.diffractometer.currentPhase,
pixelsPerMm: action.data.camera.pixelsPerMm,
sourceScale: action.data.camera.scale,
};
Expand Down