Skip to content

Commit

Permalink
Merge pull request #203 from spc-group/filters
Browse files Browse the repository at this point in the history
Support for XIA PFCU filters
  • Loading branch information
canismarko authored Apr 28, 2024
2 parents 3b72fc5 + 26ed28e commit 547eb49
Show file tree
Hide file tree
Showing 22 changed files with 6,500 additions and 39 deletions.
52 changes: 48 additions & 4 deletions src/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# from pydm.data_plugins import plugin_modules, add_plugin
import pytest
from apstools.devices.srs570_preamplifier import GainSignal
from ophyd import DynamicDeviceComponent as DDC
from ophyd import DynamicDeviceComponent as DCpt
from ophyd import Kind
from ophyd.sim import (
FakeEpicsSignal,
Expand Down Expand Up @@ -36,6 +36,7 @@
from haven.instrument.robot import Robot
from haven.instrument.shutter import Shutter
from haven.instrument.slits import ApertureSlits, BladeSlits
from haven.instrument.xia_pfcu import PFCUFilter, PFCUFilterBank, PFCUShutter
from haven.instrument.xspress import Xspress3Detector
from haven.instrument.xspress import add_mcas as add_xspress_mcas

Expand Down Expand Up @@ -161,7 +162,7 @@ class SimpleBeamlineManager(BeamlineManager):
"""

iocs = DDC(
iocs = DCpt(
{
"ioc255idb": (IOCManager, "ioc255idb:", {}),
"ioc255idc": (IOCManager, "ioc255idc:", {}),
Expand Down Expand Up @@ -211,7 +212,7 @@ def sim_camera(sim_registry):


class DxpVortex(DxpDetector):
mcas = DDC(
mcas = DCpt(
add_dxp_mcas(range_=[0, 1, 2, 3]),
kind=Kind.normal | Kind.hinted,
default_read_attrs=[f"mca{i}" for i in [0, 1, 2, 3]],
Expand All @@ -229,7 +230,7 @@ def dxp(sim_registry):


class Xspress3Vortex(Xspress3Detector):
mcas = DDC(
mcas = DCpt(
add_xspress_mcas(range_=[0, 1, 2, 3]),
kind=Kind.normal | Kind.hinted,
default_read_attrs=[f"mca{i}" for i in [0, 1, 2, 3]],
Expand Down Expand Up @@ -289,6 +290,33 @@ def aps(sim_registry):
yield aps


@pytest.fixture()
def xia_shutter_bank(sim_registry):
class ShutterBank(PFCUFilterBank):
shutters = DCpt(
{
"shutter_0": (
PFCUShutter,
"",
{"top_filter": 4, "bottom_filter": 3, "labels": {"shutters"}},
)
}
)

def __new__(cls, *args, **kwargs):
return object.__new__(cls)

FakeBank = make_fake_device(ShutterBank)
bank = FakeBank(prefix="255id:pfcu4:", name="xia_filter_bank", shutters=[[3, 4]])
sim_registry.register(bank)
yield bank


@pytest.fixture()
def xia_shutter(xia_shutter_bank):
yield xia_shutter_bank.shutters.shutter_0


@pytest.fixture()
def shutters(sim_registry):
FakeShutter = make_fake_device(Shutter)
Expand All @@ -309,6 +337,22 @@ def shutters(sim_registry):
yield shutters


@pytest.fixture()
def filters(sim_registry):
FakeFilter = make_fake_device(PFCUFilter)
kw = {
"labels": {"filters"},
}
filters = [
FakeFilter(name="Filter A", prefix="filter1", **kw),
FakeFilter(name="Filter B", prefix="filter2", **kw),
]
# Register the simulated filters
for fltr in filters:
sim_registry.register(fltr)
return filters


# holds a global QApplication instance created in the qapp fixture; keeping
# this reference alive avoids it being garbage collected too early
_ffapp_instance = None
Expand Down
13 changes: 13 additions & 0 deletions src/firefly/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,12 @@ def setup_window_actions(self):
ui_file="xrf_detector.py",
device_key="DEV",
)
self._setup_window_action(
action_name="show_filters_window_action",
text="Filters",
slot=self.show_filters_window,
)
self.show_filters_window_action.setIcon(qta.icon("mdi.air-filter"))
# Action for showing the beamline status window
self._setup_window_action(
action_name="show_status_window_action",
Expand Down Expand Up @@ -254,6 +260,7 @@ def setup_window_actions(self):
text="Energy",
slot=self.show_energy_window,
)
self.show_energy_window_action.setIcon(qta.icon("mdi.sine-wave"))
# Launch camera overview
self._setup_window_action(
action_name="show_cameras_window_action",
Expand Down Expand Up @@ -643,6 +650,12 @@ def show_bss_window(self):
def show_iocs_window(self):
return self.show_window(FireflyMainWindow, ui_dir / "iocs.py", name="iocs")

@QtCore.Slot()
def show_filters_window(self):
return self.show_window(
FireflyMainWindow, ui_dir / "filters.py", name="filters"
)

@QtCore.Slot(bool)
def set_open_environment_action_state(self, is_open: bool):
"""Update the readback value for opening the queueserver environment."""
Expand Down
5,524 changes: 5,504 additions & 20 deletions src/firefly/beamline_components_rc.py

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions src/firefly/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import json
from typing import Sequence

from pydm.widgets import PyDMEmbeddedDisplay

from firefly import display
from haven import registry


class FiltersDisplay(display.FireflyDisplay):
filters: Sequence

def ui_filename(self):
return "filters.ui"

def customize_device(self):
filters = registry.findall(label="filters", allow_none=True)
self.filters = sorted(
filters, key=lambda dev: (dev.material.get(), dev.thickness.get())
)

def customize_ui(self):
# Delete existing filter widgets
for idx in reversed(range(self.filters_layout.count())):
self.filters_layout.takeAt(idx).widget().deleteLater()
# Add embedded displays for all the ion chambers
self._filter_displays = []
for idx, device in enumerate(self.filters):
# Create the display object
disp = PyDMEmbeddedDisplay(parent=self)
disp.macros = json.dumps({"DEV": device.name})
disp.filename = "filters_row.ui"
# Add the Embedded Display to the Results Layout
self.filters_layout.addWidget(disp)
self._filter_displays.append(disp)
89 changes: 89 additions & 0 deletions src/firefly/filters.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>598</width>
<height>376</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="filters_layout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="PyDMEmbeddedDisplay" name="PyDMEmbeddedDisplay_2">
<property name="toolTip">
<string/>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="filename" stdset="0">
<string>filters_row.ui</string>
</property>
</widget>
</item>
<item>
<widget class="PyDMEmbeddedDisplay" name="PyDMEmbeddedDisplay">
<property name="toolTip">
<string/>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="filename" stdset="0">
<string>filters_row.ui</string>
</property>
</widget>
</item>
<item>
<widget class="PyDMEmbeddedDisplay" name="PyDMEmbeddedDisplay_3">
<property name="toolTip">
<string/>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="filename" stdset="0">
<string>filters_row.ui</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>PyDMEmbeddedDisplay</class>
<extends>QFrame</extends>
<header>pydm.widgets.embedded_display</header>
</customwidget>
</customwidgets>
<resources>
<include location="resources/beamline_components.qrc"/>
</resources>
<connections/>
</ui>
Loading

0 comments on commit 547eb49

Please sign in to comment.