-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Port UndulatorDCM
to ophyd-async
#461
Conversation
498792a
to
6cce80e
Compare
This work provoked a discussion about the best way to handle composite device logic like
So far, this PR is doing the 3rd option, but is otherwise just a naive conversion to ophyd-async, changing as little logic/attributes as possible. I'm tempted to leave it that way and spin out the additional changes we discussed, as well as the required changes for #389 and #397 out into additional issues/PRs, this one is already getting big. @DominicOram @dperl-dls @olliesilvester would that be okay with you? Additional Changes Discussed
The latter two are not required for Hyperion, however a small Hyperion PR is still needed to make this work |
src/dodal/devices/dcm.py
Outdated
def __init__( | ||
self, | ||
prefix: str, | ||
daq_configuration_path: str, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DominicOram and @rtuck99 how would you feel about moving the configuration file attributes for both the DCM
and Undulator
into UndulatorDCM
? Since it's just a convenient place to store them for Hyperion and means that the devices are as simple as possible for the read-only use case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine by me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, see what you think
src/dodal/devices/undulator_dcm.py
Outdated
raise AccessError( | ||
"Undulator gap access is disabled. Contact Control Room" | ||
) | ||
def __init__(self, prefix: str, undulator: Undulator, dcm: DCM, name: str = ""): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefix
is unused and only there to satisfy device_instantiation
, which assumes every device takes a prefix. I'm not overjoyed with this but unsure if this should be a change to device_instantiation
or just not using to instantiate this device.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given we plan to redo device instantiation, is it worth worrying about? IMO best to just note that we don't want this to be the case anymore when we do that
40d8fde
to
e182834
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DominicOram @dperl-dls @olliesilvester would that be okay with you?
Yep, works for me, thank you!
Some comments in code. Additionally, can you make test_i03_system
(with the i03 panda and pin detect commented out due to #261) and test_i04_system
still work? That's how I caught the Enum issue
src/dodal/devices/dcm.py
Outdated
from dodal.beamlines.beamline_parameters import get_beamline_parameters | ||
|
||
|
||
class DCM(StandardReadable, HasHints): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should: I appreciate this wasn't your doing but while where here... Can we roll -MO-DCM-01:
into the prefix? Either by specifying it in the beamline file like we do in other devices or by just doing a prefix = prefix + "-MO-DCM-01"
as the first thing in the init?
src/dodal/devices/dcm.py
Outdated
def __init__( | ||
self, | ||
prefix: str, | ||
daq_configuration_path: str, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine by me
src/dodal/devices/undulator.py
Outdated
) -> None: | ||
self.gap_motor = Motor(prefix + "BLGAPMTR") | ||
self.current_gap = epics_signal_r(float, prefix + "CURRGAPD") | ||
self.gap_access = epics_signal_r(UndulatorGapAccess, prefix + "IDBLENA") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Must: The Enum
needs to inherit from str
too. +1 to bluesky/ophyd-async#261
src/dodal/devices/undulator_dcm.py
Outdated
async def _get_energy_distance_table(lookup_table_path: str) -> ndarray: | ||
# Slight cheat to make the file IO async, numpy doesn't do any real IO now, just | ||
# decodes the text | ||
async with aiofiles.open(lookup_table_path, "r") as stream: | ||
raw_table = await stream.read() | ||
return loadtxt(StringIO(raw_table), comments=["#", "Units"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should: Can you move this util function into lookup_tables.py
?
# Attributes are set after super call so they are not renamed to | ||
# <name>-undulator, etc. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Nice comment, thanks!
"undulator-gap_access", | ||
], | ||
) | ||
async def test_read_and_describe_includes( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should: Same as above, why does this need to be staged?
# fake_undulator_dcm.undulator.gap_motor.move = MagicMock() | ||
# fake_undulator_dcm.dcm.energy_in_kev.move = MagicMock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should: Can we remove these now?
# fake_undulator_dcm.undulator.gap_motor.move = MagicMock() | ||
# fake_undulator_dcm.dcm.energy_in_kev.move = MagicMock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should: Can we remove these?
# fake_undulator_dcm.undulator.gap_motor.move = MagicMock() | ||
# fake_undulator_dcm.dcm.energy_in_kev.move = MagicMock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should: As above, remove
|
||
|
||
def test_energy_set_only_complete_when_all_statuses_are_finished( | ||
async def test_energy_set_only_complete_when_all_statuses_are_finished( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this is a nice pattern for testing this that I hadn't yet worked out for ophyd_async
!
Fix for black CI issues here: #470 |
In accordance with pep8: https://peps.python.org/pep-0008/#package-and-module-names
…d DCM to UndulatorDCM
d5dd286
to
48091cd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, some more changes in code. This will also break Hyperion, I'll make a PR to fix
src/dodal/devices/dcm.py
Outdated
"dcm_bragg_in_degrees", | ||
"dcm_roll_in_mrad", | ||
"dcm_offset_in_mm", | ||
"dcm_perp_in_mm", | ||
"dcm_energy_in_kev", | ||
"dcm_pitch_in_mrad", | ||
"dcm_wavelength", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should: Can we return self.bragg_in_degrees.name
etc. This adds the potential to catch people when if they rename the signal but miss this string.
I did have some comments about |
src/dodal/devices/dcm.py
Outdated
def __init__( | ||
self, | ||
prefix: str, | ||
name: str = "", | ||
) -> None: | ||
self.bragg_in_degrees = Motor(prefix + "BRAGG") | ||
self.roll_in_mrad = Motor(prefix + "ROLL") | ||
self.offset_in_mm = Motor(prefix + "OFFSET") | ||
self.perp_in_mm = Motor(prefix + "PERP") | ||
self.energy_in_kev = Motor(prefix + "ENERGY") | ||
self.pitch_in_mrad = Motor(prefix + "PITCH") | ||
self.wavelength = Motor(prefix + "WAVELENGTH") | ||
|
||
# temperatures | ||
self.xtal1_temp = epics_signal_r(float, prefix + "TEMP1") | ||
self.xtal2_temp = epics_signal_r(float, prefix + "TEMP2") | ||
self.xtal1_heater_temp = epics_signal_r(float, prefix + "TEMP3") | ||
self.xtal2_heater_temp = epics_signal_r(float, prefix + "TEMP4") | ||
self.backplate_temp = epics_signal_r(float, prefix + "TEMP5") | ||
self.perp_temp = epics_signal_r(float, prefix + "TEMP6") | ||
self.perp_sub_assembly_temp = epics_signal_r(float, prefix + "TEMP7") | ||
|
||
self.set_readable_signals( | ||
read=[ | ||
self.bragg_in_degrees, # type: ignore | ||
self.roll_in_mrad, # type: ignore | ||
self.offset_in_mm, # type: ignore | ||
self.perp_in_mm, # type: ignore | ||
self.energy_in_kev, # type: ignore | ||
self.pitch_in_mrad, # type: ignore | ||
self.wavelength, # type: ignore | ||
self.xtal1_temp, | ||
self.xtal2_temp, | ||
self.xtal1_heater_temp, | ||
self.xtal2_heater_temp, | ||
self.backplate_temp, | ||
self.perp_temp, | ||
self.perp_sub_assembly_temp, | ||
] | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def __init__( | |
self, | |
prefix: str, | |
name: str = "", | |
) -> None: | |
self.bragg_in_degrees = Motor(prefix + "BRAGG") | |
self.roll_in_mrad = Motor(prefix + "ROLL") | |
self.offset_in_mm = Motor(prefix + "OFFSET") | |
self.perp_in_mm = Motor(prefix + "PERP") | |
self.energy_in_kev = Motor(prefix + "ENERGY") | |
self.pitch_in_mrad = Motor(prefix + "PITCH") | |
self.wavelength = Motor(prefix + "WAVELENGTH") | |
# temperatures | |
self.xtal1_temp = epics_signal_r(float, prefix + "TEMP1") | |
self.xtal2_temp = epics_signal_r(float, prefix + "TEMP2") | |
self.xtal1_heater_temp = epics_signal_r(float, prefix + "TEMP3") | |
self.xtal2_heater_temp = epics_signal_r(float, prefix + "TEMP4") | |
self.backplate_temp = epics_signal_r(float, prefix + "TEMP5") | |
self.perp_temp = epics_signal_r(float, prefix + "TEMP6") | |
self.perp_sub_assembly_temp = epics_signal_r(float, prefix + "TEMP7") | |
self.set_readable_signals( | |
read=[ | |
self.bragg_in_degrees, # type: ignore | |
self.roll_in_mrad, # type: ignore | |
self.offset_in_mm, # type: ignore | |
self.perp_in_mm, # type: ignore | |
self.energy_in_kev, # type: ignore | |
self.pitch_in_mrad, # type: ignore | |
self.wavelength, # type: ignore | |
self.xtal1_temp, | |
self.xtal2_temp, | |
self.xtal1_heater_temp, | |
self.xtal2_heater_temp, | |
self.backplate_temp, | |
self.perp_temp, | |
self.perp_sub_assembly_temp, | |
] | |
) | |
def __init__( | |
self, | |
prefix: str, | |
name: str = "", | |
) -> None: | |
with self.add_children_as_readables(): | |
self.bragg_in_degrees = Motor(prefix + "BRAGG") | |
self.roll_in_mrad = Motor(prefix + "ROLL") | |
self.offset_in_mm = Motor(prefix + "OFFSET") | |
self.perp_in_mm = Motor(prefix + "PERP") | |
self.energy_in_kev = Motor(prefix + "ENERGY") | |
self.pitch_in_mrad = Motor(prefix + "PITCH") | |
self.wavelength = Motor(prefix + "WAVELENGTH") | |
# temperatures | |
self.xtal1_temp = epics_signal_r(float, prefix + "TEMP1") | |
self.xtal2_temp = epics_signal_r(float, prefix + "TEMP2") | |
self.xtal1_heater_temp = epics_signal_r(float, prefix + "TEMP3") | |
self.xtal2_heater_temp = epics_signal_r(float, prefix + "TEMP4") | |
self.backplate_temp = epics_signal_r(float, prefix + "TEMP5") | |
self.perp_temp = epics_signal_r(float, prefix + "TEMP6") | |
self.perp_sub_assembly_temp = epics_signal_r(float, prefix + "TEMP7") |
src/dodal/devices/dcm.py
Outdated
@property | ||
def hints(self) -> Hints: | ||
return { | ||
"fields": [ | ||
self.bragg_in_degrees.name, | ||
self.roll_in_mrad.name, | ||
self.offset_in_mm.name, | ||
self.perp_in_mm.name, | ||
self.energy_in_kev.name, | ||
self.pitch_in_mrad.name, | ||
self.wavelength.name, | ||
] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@property | |
def hints(self) -> Hints: | |
return { | |
"fields": [ | |
self.bragg_in_degrees.name, | |
self.roll_in_mrad.name, | |
self.offset_in_mm.name, | |
self.perp_in_mm.name, | |
self.energy_in_kev.name, | |
self.pitch_in_mrad.name, | |
self.wavelength.name, | |
] | |
} |
Not required with above change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was literally half way through doing this when I got called to i03 ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are of one mind, when my hair is long again we can link up Na'vi style
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, thank you. Have updated the Hyperion side to match in DiamondLightSource/hyperion#1349 so happy to merge both at the same time when that is approved.
Works towards #389 and #397
Changes:
DCM.py
todcm.py
to comply with pep8: https://peps.python.org/pep-0008/#package-and-module-namesUndulatorDCM
and its two componentsUndulator
andDCM
to ophyd-async asStandardReadable
s.Instructions to reviewer on how to test:
Checks for reviewer