-
Notifications
You must be signed in to change notification settings - Fork 3
Add Shutter devices to sophys-common devices
#88
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
Open
joao-biondo
wants to merge
17
commits into
main
Choose a base branch
from
shutters
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
7a4946f
feat(shutters): add `shutters.py`
joao-biondo 4e5c8a3
feat(shutters): add firs idea for shutters devices.
joao-biondo 867f197
feat(shutters): add docstrings for the shutter devices.
joao-biondo 9cf44ce
fix(shutters): set correct `ophyd.Kind` for components.
joao-biondo 7de535a
chore(shutters): set parameter `type` for the `__init__` method of sh…
joao-biondo b0135b4
feat(shutters): overwrite `read_configuration` method for dealing wit…
joao-biondo e49b4a5
fix(shutters): swap names from the two shutter classes.
joao-biondo 88df15c
chore(shutters): make docstring example of usage simpler.
joao-biondo f9aa1c1
fix(shutters): fix typos in shutters class.
joao-biondo b338fe3
refactor(shutters): change `ophyd.Kind` of permission signal to "omit…
joao-biondo 28726b1
refactor(shutters): fix the actuation PVs shuffixes `OPENCLOSE`, `OPE…
joao-biondo 5c75b25
fix(shutters): fix typo in docstring.
joao-biondo e52a7d8
refactor(shutters): instantiate `permission` as attribute if `permis…
joao-biondo 3ac7d59
refactor(shutters): add `permission_flag` inside `set` method and `co…
joao-biondo f769d15
feat(shutters): add `_is_closed` method to check the status of photon…
joao-biondo b227be8
chore: fix typo in docstring.
joao-biondo 9f659d8
refactor(shutters): make the permission check for the `set` method si…
joao-biondo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| from ophyd import EpicsSignal, EpicsSignalRO, FormattedComponent, Device | ||
| from ophyd.pv_positioner import PVPositionerComparator | ||
| from ..utils.status import PremadeStatus | ||
| from ophyd.status import AndStatus, SubscriptionStatus | ||
|
|
||
|
|
||
| class ShutterToggle(PVPositionerComparator): | ||
| """ | ||
|
joao-biondo marked this conversation as resolved.
|
||
| Abstraction layer for shutters with one actuation PV (OPENCLOSE) and one readback PV (PG_STATUS). There's an optional parameter for a permission PV. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| prefix: str | ||
| Prefix for the shutter's PVs. | ||
|
|
||
| setpoint_suffix: str | ||
| Suffix for the actuation PV. NOTE: This should be place/location of the shutter, e.g. OEA/FOE. | ||
| The PV will be formatted as "{prefix}{setpoint_suffix}OPENCLOSE". | ||
|
|
||
| readback_suffix: str | ||
| Suffix for the readback PV, e.g. PG_STATUS | ||
|
|
||
| permission_suffix: str, optional | ||
| Permission PV string, if it exists. | ||
|
|
||
| NOTE | ||
| ---- | ||
| This implemantation considers that the value of the `readback` signal is 0 for an open shutter and 1 for a closed shutter. | ||
| This is not so intuitive, so the `set` method considers that 1 is for opening and 0 for closing the shutter. | ||
|
|
||
| Usage Example | ||
| ------------- | ||
| >>> shutter = ShutterOpenClose(prefix="prefix", setpoint_suffix="setpoint_suffix", readback="readback_suffix", name="shutter") | ||
| >>> shutter.set(0).wait() # for closing | ||
| >>> shutter.set(1).wait() # for opening | ||
| """ | ||
|
|
||
| real_setpoint = None | ||
| setpoint = FormattedComponent( | ||
| EpicsSignal, "{prefix}{setpoint_suffix}OPENCLOSE", kind="config" | ||
| ) | ||
| readback = FormattedComponent( | ||
| EpicsSignalRO, "{prefix}{readback_suffix}", kind="hinted" | ||
| ) | ||
|
|
||
| def __init__( | ||
| self, | ||
| *args, | ||
| setpoint_suffix: str, | ||
| readback_suffix: str, | ||
| permission_pv: str = None, | ||
| **kwargs, | ||
| ): | ||
| self.setpoint_suffix = setpoint_suffix | ||
| self.readback_suffix = readback_suffix | ||
| self._permission_pv_name = permission_pv | ||
| super().__init__(*args, **kwargs) | ||
| if self._permission_pv_name is not None: | ||
| self.permission_signal = EpicsSignalRO( | ||
| f"{self._permission_pv_name}", name="permission" | ||
| ) | ||
|
|
||
| def set(self, value, *args, **kwargs): | ||
| if hasattr(self, "permission_signal"): | ||
| permission_status = self.permission_signal.get( | ||
| connection_timeout=2, **kwargs | ||
| ) | ||
| if not permission_status: | ||
| raise PremadeStatus( | ||
| success=False, | ||
| exception=PermissionError( | ||
| f"Shutter open permission is denied: {self.permission_signal.pvname} {permission_status}." | ||
| ), | ||
| ) | ||
|
|
||
| if ( | ||
| value == self.readback.get() | ||
| ): # Since we're swapping the readback values (0 for closing and 1 for opening), we actuate when value == readback | ||
| self.real_setpoint = 1 if value == 0 else 0 | ||
| return super().set(1, *args, **kwargs) | ||
| else: | ||
| return PremadeStatus(success=True) | ||
|
|
||
| def done_comparator(self, readback, setpoint): | ||
| return self.real_setpoint == readback | ||
|
|
||
|
joao-biondo marked this conversation as resolved.
|
||
|
|
||
| class ShutterOpenClose(Device): | ||
| """ | ||
| Abstraction layer for shutters with two actuation PV (OPEN and CLOSE) and two readback PV (PS_STATUS and GS_STATUS). There's an optional parameter for a permission PV. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| prefix: str | ||
| Prefix for the shutter's PVs. | ||
|
|
||
| shutter_suffix: str | ||
| Suffix for the OPEN and CLOSE PVs. NOTE: This should be place/location of the shutter, e.g. OEA/FOE. | ||
| The PVs will be formatted as "{prefix}{shutter_suffix}OPEN" and "{prefix}{shutter_suffix}CLOSE". | ||
|
|
||
| ps_suffix: str | ||
| Suffix for one readback PVs, e.g. PS_STATUS | ||
|
|
||
| gs_suffix: str | ||
| Suffix for the second readback PV, e.g. GS_STATUS | ||
|
|
||
| permission_pv: str, optional | ||
| Permission PV string, if it exists. | ||
|
|
||
| NOTES | ||
| ----- | ||
| This implemantation considers that the value of the `readback` signal is 0 for an open shutter and 1 for a closed shutter. | ||
| This is not so intuitive, so the `set` method considers that 1 is for opening and 0 for closing the shutter. | ||
|
|
||
| The `return` of the `set` method is an `AndStatus` with both `readback` signals. | ||
|
|
||
| There's a `done_comparator` method that returns the state of the shutter, based in the two `readback` PVs. This method is | ||
| used as the `callback` for both `readback` signals. | ||
|
|
||
| Usage Example | ||
| ------------- | ||
| >>> shutter = ShutterToggle(prefix="prefix", open_suffix="open_suffix", close_suffix="close_suffix", ps_suffix="ps_suffix", gs_suffix="gs_suffix", name="shutter") | ||
| >>> shutter.set(0).wait() # for closing | ||
| >>> shutter.set(1).wait() # for opening | ||
| """ | ||
|
|
||
| setpoint = None | ||
| photon_status = FormattedComponent( | ||
| EpicsSignalRO, "{prefix}{ps_suffix}", kind="hinted" | ||
| ) | ||
| gamma_status = FormattedComponent( | ||
| EpicsSignalRO, "{prefix}{gs_suffix}", kind="hinted" | ||
| ) | ||
| open = FormattedComponent( | ||
| EpicsSignal, "{prefix}{shutter_suffix}OPEN", kind="config" | ||
| ) | ||
| close = FormattedComponent( | ||
| EpicsSignal, "{prefix}{shutter_suffix}CLOSE", kind="config" | ||
| ) | ||
|
|
||
| def __init__( | ||
| self, | ||
| *args, | ||
| shutter_suffix: str, | ||
| ps_suffix: str, | ||
| gs_suffix: str, | ||
| permission_pv: str = None, | ||
| **kwargs, | ||
| ): | ||
| self.shutter_suffix = shutter_suffix | ||
| self.ps_suffix = ps_suffix | ||
| self.gs_suffix = gs_suffix | ||
| self._permission_pv_name = permission_pv | ||
| super().__init__(*args, **kwargs) | ||
| if self._permission_pv_name is not None: | ||
| self.permission_signal = EpicsSignalRO( | ||
| f"{self._permission_pv_name}", name="permission" | ||
| ) | ||
|
|
||
| def set(self, value, *args, **kwargs): | ||
| if hasattr(self, "permission_signal"): | ||
| permission_status = self.permission_signal.get( | ||
| connection_timeout=2, **kwargs | ||
| ) | ||
| if not permission_status: | ||
| raise PremadeStatus( | ||
| success=False, | ||
| exception=PermissionError( | ||
| f"Shutter open permission is denied: {self.permission_signal.pvname} {permission_status}" | ||
| ), | ||
| ) | ||
|
|
||
| if value == 0 and not self._is_closed(): | ||
| self.close.set(1, *args, **kwargs).wait() | ||
|
|
||
| elif value == 1 and self._is_closed(): | ||
| self.open.set(1, *args, **kwargs).wait() | ||
|
|
||
| else: | ||
| return PremadeStatus(success=True) | ||
|
|
||
| self.setpoint = value | ||
|
|
||
| return AndStatus( | ||
| SubscriptionStatus(self.photon_status, self.done_comparator, settle_time=3), | ||
| SubscriptionStatus(self.gamma_status, self.done_comparator, settle_time=3), | ||
| timeout=15, | ||
| ) | ||
|
|
||
| def _is_closed(self): | ||
| """Check wheter the shutter is open or closed given the photon and gamma status PVs.""" | ||
| return (self.photon_status.get() == 1) and ( | ||
| self.gamma_status.get() == 1 | ||
| ) # NOTE: if one of the status is equal to zero, the shutter can be partially open | ||
|
|
||
| def done_comparator(self, value, **kwargs): | ||
| is_closed = self._is_closed() | ||
| return is_closed if self.setpoint == 0 else not is_closed | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.