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
50 changes: 30 additions & 20 deletions mxcubeweb/core/adapter/adapter_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from mxcubecore.utils.conversion import make_table

from mxcubeweb.core.adapter.adapter_base import AdapterBase
from mxcubeweb.core.adapter.beamline_adapter import BeamlineAdapter


class HardwareObjectAdapterManager:
Expand Down Expand Up @@ -44,7 +43,6 @@ def init(self) -> None:
done when one wishes.
"""
try:
self.beamline = BeamlineAdapter(HWR.beamline, self.app)
self.adapt_hardware_objects()
except Exception: # noqa: BLE001
msg = (
Expand Down Expand Up @@ -124,32 +122,44 @@ def find_best_adapter(self, ho):
logging.getLogger("MX3.HWR").error(msg)
raise RuntimeError(msg)

def adapt_hardware_object(self, ho, _id):
adapter_cls = self.find_best_adapter(ho)

if adapter_cls:
try:
adapter_instance = adapter_cls(ho, _id, self.app)
msg = f"Added adapter for {_id}"
logging.getLogger("MX3.HWR").info(msg)
except Exception:
msg = f"Could not add adapter for {_id}"
logging.getLogger("MX3.HWR").exception(msg)
adapter_cls = AdapterBase
adapter_instance = AdapterBase(ho, _id, self.app)

self._add_adapter(_id, adapter_cls, ho, adapter_instance)
else:
msg = f"No adapter for {_id}"
logging.getLogger("MX3.HWR").info(msg)

def adapt_hardware_objects(self):
_hwr = HWR.get_hardware_repository()

# Beamline is not added to the list of hardware objects
# returned by _hwr.hardware_objects as its considered a
# special object root object for all other hardware objects
# so we add it manually here. We give it the id 'beamline'
# so that it can be retrieved by the adapter manager but
# in reality has no id.
self.adapt_hardware_object(HWR.beamline, "beamline")
Copy link
Member Author

Choose a reason for hiding this comment

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

The BeamlineAdapter used to be a sort of special object. Now its a Adapter object like the others. However the beamline HardwareObject is considered special as its the container for all (most) other HardwareObjects. It does not necessarily have to be in this way, but It was a design decision made at the time the Beamline object was introduced. It also means that the Beamline object does not have any id. So we specifically add the Beamline object to the list adapted hardware objects and give the id "beamline" which is alright as far as adapters are concerned.


for ho_name in _hwr.hardware_objects:
ho = _hwr.get_hardware_object(ho_name)
if not ho:
continue

_id = ho.id
adapter_cls = self.find_best_adapter(ho)

if adapter_cls:
try:
adapter_instance = adapter_cls(ho, _id, self.app)
msg = f"Added adapter for {_id}"
logging.getLogger("MX3.HWR").info(msg)
except Exception:
msg = f"Could not add adapter for {_id}"
logging.getLogger("MX3.HWR").exception(msg)
adapter_cls = AdapterBase
adapter_instance = AdapterBase(ho, _id, self.app)

self._add_adapter(_id, adapter_cls, ho, adapter_instance)
else:
msg = f"No adapter for {_id}"
logging.getLogger("MX3.HWR").info(msg)
_id = ho.id or "beamline"

self.adapt_hardware_object(ho, _id)

self._print_adapter_table()

Expand Down
85 changes: 49 additions & 36 deletions mxcubeweb/core/adapter/beamline_adapter.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,72 @@
import logging
from typing import ClassVar

import pydantic
from mxcubecore import HardwareRepository as HWR
from mxcubecore.HardwareObjects.Beamline import Beamline

BEAMLINE_ADAPTER = None
from mxcubeweb.core.adapter.adapter_base import ActuatorAdapterBase
from mxcubeweb.core.models.configmodels import ResourceHandlerConfigModel

# Singleton like interface is needed to keep the same reference to the
# adapter object and its corresponding hardware objects, so that the signal
# system won't clean up signal handlers. (PyDispatcher removes signal handlers
# when an object is garbage collected)
resource_handler_config = ResourceHandlerConfigModel(
commands=["prepare_beamline_for_sample"],
attributes=["data", "get_value"],
)


def BeamlineAdapter(*args):
global BEAMLINE_ADAPTER

if BEAMLINE_ADAPTER is None:
BEAMLINE_ADAPTER = _BeamlineAdapter(*args)

return BEAMLINE_ADAPTER


class _BeamlineAdapter:
class BeamlineAdapter(ActuatorAdapterBase):
Copy link
Member Author

Choose a reason for hiding this comment

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

So the old some what strange custom _BeamlineAdapter object was made into a real Adapter

"""
Adapter between Beamline route and Beamline hardware object.
"""

def __init__(self, beamline_hwobj, app):
SUPPORTED_TYPES: ClassVar[list[object]] = [Beamline]

def __init__(self, ho, role, app):
super().__init__(ho, role, app, resource_handler_config)
self.app = app
self._bl = beamline_hwobj
self.adapter_dict = {}

workflow = self._bl.workflow
workflow = self._ho.workflow
if workflow:
workflow.connect("parametersNeeded", self.wf_parameters_needed)
workflow.connect("parametersNeeded", self._wf_parameters_needed)

gphl_workflow = self._bl.gphl_workflow
gphl_workflow = self._ho.gphl_workflow
if gphl_workflow:
gphl_workflow.connect(
"GphlJsonParametersNeeded", self.gphl_json_wf_parameters_needed
"GphlJsonParametersNeeded", self._gphl_json_wf_parameters_needed
)
gphl_workflow.connect(
"GphlUpdateUiParameters", self.gphl_json_wf_update_ui_parameters
"GphlUpdateUiParameters", self._gphl_json_wf_update_ui_parameters
)

def wf_parameters_needed(self, params):
from mxcubeweb.routes import signals
Copy link
Member Author

Choose a reason for hiding this comment

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

So this will still need some further cleanup, so that we can get rid of this import


if HWR.beamline.xrf_spectrum:
HWR.beamline.xrf_spectrum.connect(
HWR.beamline.xrf_spectrum,
"xrf_task_progress",
signals.xrf_task_progress,
)

def _wf_parameters_needed(self, params):
self.app.server.emit("workflowParametersDialog", params, namespace="/hwr")

def gphl_json_wf_parameters_needed(self, schema, ui_schema):
def _gphl_json_wf_parameters_needed(self, schema, ui_schema):
params = {}
params["schema"] = schema
params["ui_schema"] = ui_schema
self.app.server.emit("gphlWorkflowParametersDialog", params, namespace="/hwr")

def gphl_json_wf_update_ui_parameters(self, update_dict):
def _gphl_json_wf_update_ui_parameters(self, update_dict):
self.app.server.emit(
"gphlWorkflowUpdateUiParametersDialog", update_dict, namespace="/hwr"
)

def get_object(self, name):
return self._ho.get_hardware_object(name)
def _get_available_elements(self):
escan = self._ho.energy_scan
return escan.get_elements() if escan else []

def dict(self):
def get_value(self) -> dict:
"""
Build dictionary value-representation for each beamline attribute
Returns:
Expand All @@ -68,6 +75,10 @@ def dict(self):
attributes = {}

for attr_name in self.app.mxcubecore.adapter_dict:
# We skip the beamline attribute to avoid endless recursion
if attr_name == "beamline":
continue

try:
_d = self.app.mxcubecore.get_adapter(attr_name).data().dict()
except pydantic.ValidationError:
Expand All @@ -76,13 +87,15 @@ def dict(self):

attributes.update({attr_name: _d})

return {"hardwareObjects": attributes}

def get_available_elements(self):
escan = self._bl.energy_scan
elements = []
return {
"energyScanElements": self._get_available_elements(),
"path": HWR.beamline.session.get_base_image_directory(),
"actionsList": [],
"hardwareObjects": attributes,
}

if escan:
elements = escan.get_elements()
def prepare_beamline_for_sample(self) -> dict:
Copy link
Member Author

Choose a reason for hiding this comment

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

prepare_beamline_for_sample is now under the BeamlineAdapter, you guys (@fabcor-maxiv, @elmjag) have then to decide what to do with it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Related to #1845 ?

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, in a way. If you decide to do something with prepare_beamline_for_sample, keep in mind that it changed location, thats what I wanted to say.

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

return {"elements": elements}
return {}
46 changes: 1 addition & 45 deletions mxcubeweb/core/components/beamline.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from mxcubecore import HardwareRepository as HWR

from mxcubeweb.core.adapter.beamline_adapter import BeamlineAdapter
from mxcubeweb.core.components.component_base import ComponentBase


Expand All @@ -9,28 +8,7 @@ def __init__(self, app, config):
super().__init__(app, config)

def init_signals(self):
from mxcubeweb.routes import signals

if HWR.beamline.xrf_spectrum:
HWR.beamline.xrf_spectrum.connect(
HWR.beamline.xrf_spectrum,
"xrf_task_progress",
signals.xrf_task_progress,
)

def get_aperture(self):
"""
Returns list of apertures and the one currently used.

:return: Tuple, (list of apertures, current aperture)
:rtype: tuple
"""
beam = HWR.beamline.beam

aperture_list = beam.get_available_size()["values"]
current_aperture = beam.get_value()[-1]

return aperture_list, current_aperture
pass

def get_viewport_info(self):
"""
Expand Down Expand Up @@ -76,25 +54,3 @@ def get_viewport_info(self):
"videoHash": HWR.beamline.sample_view.camera.stream_hash,
"videoURL": self.app.CONFIG.app.VIDEO_STREAM_URL,
}

def beamline_get_all_attributes(self):
ho = BeamlineAdapter(HWR.beamline)
data = ho.dict()
actions = []

data.update(
{
"path": HWR.beamline.session.get_base_image_directory(),
"actionsList": actions,
}
)

data.update(
{"energyScanElements": ho.get_available_elements().get("elements", [])}
)

return data

def prepare_beamline_for_sample(self):
if hasattr(HWR.beamline.collect, "prepare_for_new_sample"):
HWR.beamline.collect.prepare_for_new_sample()
33 changes: 0 additions & 33 deletions mxcubeweb/routes/beamline.py

This file was deleted.

4 changes: 0 additions & 4 deletions mxcubeweb/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ def register_routes(mxcube):
Server.flask, Server.user_datastore, register_blueprint=False
)

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.harvester import init_route as init_harvester_route
from mxcubeweb.routes.lims import init_route as init_lims_route
Expand All @@ -133,9 +132,6 @@ def register_routes(mxcube):

url_root_prefix = "/mxcube/api/v0.1"

Server._register_route(
init_beamline_route, mxcube, f"{url_root_prefix}/beamline"
)
Server._register_route(init_csp_route, mxcube, f"{url_root_prefix}/csp")

Server._register_route(init_lims_route, mxcube, f"{url_root_prefix}/lims")
Expand Down
4 changes: 3 additions & 1 deletion test/test_beamline_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ def test_beamline_get_all_attribute(client):
Checks that the data returned has the right structure and if "all"
beamline attributes are at least present
"""
resp = client.get("/mxcube/api/v0.1/beamline/")
resp = client.get(
"/mxcube/api/v0.1/hwobj/beamline/beamline/get_value",
)
data = json.loads(resp.data)

actual = list(data.get("hardwareObjects").keys())
Expand Down
9 changes: 7 additions & 2 deletions ui/src/actions/beamline.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { sendPrepareBeamlineForNewSample } from '../api/beamline';
import { sendExecuteCommand, sendSetValue } from '../api/hardware-object';
import { sendLogFrontEndTraceBack } from '../api/log';

Expand Down Expand Up @@ -51,7 +50,13 @@ export function executeCommand(object_type, object_id, name, args) {
}

export function prepareBeamlineForNewSample() {
return () => sendPrepareBeamlineForNewSample();
return () =>
sendExecuteCommand(
'beamline',
'beamline',
'prepare_beamline_for_sample',
{},
);
}

export function logFrontEndTraceBack(stack) {
Expand Down
3 changes: 1 addition & 2 deletions ui/src/actions/login.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* eslint-disable promise/prefer-await-to-then */
import { fetchBeamlineSetup } from '../api/beamline';
import { fetchValue } from '../api/hardware-object';
import { fetchHarvesterInitialState } from '../api/harvester';
import { sendSelectProposal } from '../api/lims';
Expand Down Expand Up @@ -96,7 +95,7 @@ export function getInitialState() {
fetchValue('beam', 'beam')
.then((beamInfo) => ({ beamInfo: beamInfo.value }))
.catch(notify),
fetchBeamlineSetup()
fetchValue('beamline', 'beamline')
.then((beamlineSetup) => ({
beamlineSetup,
datapath: beamlineSetup.path,
Expand Down
11 changes: 0 additions & 11 deletions ui/src/api/beamline.js

This file was deleted.