Skip to content

Commit

Permalink
Merge pull request NSLS-II-ARI#16 from awalter-bnl/updates_from_Tom
Browse files Browse the repository at this point in the history
Updating the PlanCollector and PlanCollectorSub to use types.SimpleNamespace
  • Loading branch information
Jiemin-Li authored Aug 6, 2024
2 parents 434e982 + ea7453a commit 2bb658d
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 140 deletions.
Binary file modified src/ari_sxn_common/__pycache__/ari_ophyd.cpython-312.pyc
Binary file not shown.
Binary file modified src/ari_sxn_common/__pycache__/common_bluesky.cpython-312.pyc
Binary file not shown.
Binary file modified src/ari_sxn_common/__pycache__/common_ophyd.cpython-312.pyc
Binary file not shown.
11 changes: 6 additions & 5 deletions src/ari_sxn_common/ari_ophyd.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from common_ophyd import (BaffleSlit, Diagnostic, DeviceWithLocations,
ID29EpicsMotor, ID29EpicsSignalRO)
ID29EpicsMotor, ID29EpicsSignalRO,
ID29TwoButtonShutter)
from ophyd import Component
from nslsii.devices import TwoButtonShutter



class M1(DeviceWithLocations):
Expand Down Expand Up @@ -84,14 +85,14 @@ def __init__(self, *args, **kwargs):
labels=('detector',))
ip = Component(ID29EpicsSignalRO, "ip", name='ip', kind='config',
labels=('detector',))
gv = Component(TwoButtonShutter, "gv:", name='gv', kind='config',
gv = Component(ID29TwoButtonShutter, "gv:", name='gv', kind='config',
labels=('position',))


# baffle slit sub-device
slits = Component(BaffleSlit, "baffle:", name='slits', kind='normal',
labels=('device',),
locations_data={'in': {'top': (-12.7, 0.1),
locations_data={'all_in': {'top': (-12.7, 0.1),
'bottom': (12.7, 0.1),
'inboard': (12.7, 0.1),
'outboard': (-12.7, 0.1)},
Expand All @@ -103,7 +104,7 @@ def __init__(self, *args, **kwargs):
'bottom': (-12.7, 0.1),
'inboard': (-12.7, 0.1),
'outboard': (12.7, 0.1)},
'out': {'top': (28, 0.1),
'all_out': {'top': (28, 0.1),
'bottom': (-28, 0.1),
'inboard': (-28, 0.1),
'outboard': (28, 0.1)}})
Expand Down
15 changes: 5 additions & 10 deletions src/ari_sxn_common/bluesky_test_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import ari_ophyd
from bluesky import RunEngine
from bluesky.callbacks.best_effort import BestEffortCallback
from bluesky import plans as plans
from bluesky import plan_stubs as plan_stubs
# from bluesky import plans as plans
# from bluesky import plan_stubs as plan_stubs
from bluesky.utils import ProgressBarManager
import common_bluesky
from databroker import Broker
Expand All @@ -22,14 +22,10 @@

EpicsSignalBase.set_defaults(timeout=10, connection_timeout=10)

# create the plan_stub objects
for plan_stub, aliases in common_bluesky._plan_stubs_to_import.items():
for alias in aliases:
globals()[alias] = getattr(plan_stubs, plan_stub)
# create the plan objects
for plan, aliases in common_bluesky._plans_to_import.items():
for aliases, plan in common_bluesky._plans_to_alias.items():
for alias in aliases:
globals()[alias] = getattr(plans, plan)
globals()[alias] = plan

# Setup the m1 mirror ophyd object
m1_locations_data = {'measure': {'diag.locations': ('Out', None),
Expand All @@ -41,6 +37,5 @@
M1 = m1 # Create a reference object so that m1 or M1 are equivalent.

plans = common_bluesky.PlanCollector(
plans_to_import=common_bluesky._plans_to_import,
plan_stubs_to_import=common_bluesky._plan_stubs_to_import,
plans_for_methods=common_bluesky._plans_to_alias,
name='plans')
179 changes: 60 additions & 119 deletions src/ari_sxn_common/common_bluesky.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
from bluesky import plans
from bluesky import plan_stubs

# Note that the dict has the structure {'plan_stub':[alias1, alias2, ...], ...}
_plan_stubs_to_import = {'mv': ['move', 'mv'], 'mvr': ['relative_move', 'mv']}

# Note that the dict has the structure {'plan':[alias1, alias2, ...], ...}
_plans_to_import = {'count': ['count'],
'scan': ['scan'],
'rel_scan': ['relative_scan', 'rel_scan'],
'grid_scan': ['grid_scan'],
'rel_grid_scan': ['relative_grid_scan', 'rel_grid_scan'],
'list_scan': ['list_scan'],
'rel_list_scan': ['relative_list_scan', 'rel_list_scan'],
'list_grid_scan': ['list_grid_scan'],
'rel_list_grid_scan': ['relative_list_grid_scan',
'rel_list_grid_scan'],
'log_scan': ['log_scan'],
'rel_log_scan': ['relative_log_scan', 'rel_log_scan'],
'spiral': ['spiral'],
'rel_spiral': ['relative_spiral', 'rel_spiral'],
'spiral_fermat': ['spiral_fermat'],
'rel_spiral_fermat': ['relative_spiral_fermat',
'rel_spiral_fermat'],
'spiral_square': ['spiral_square'],
'rel_spiral_square': ['relative_spiral_square',
'rel_spiral_square']}


class PlanCollectorSub:
from types import SimpleNamespace

# Note that the dict has the structure {[alias1, alias2, ...]:'plan', ...}
# noinspection PyRedundantParentheses
_plans_to_alias = {('move', 'mv'): plan_stubs.mv,
('relative_move', 'mvr'): plan_stubs.mvr,
('count',): plans.count,
('scan',): plans.scan,
('relative_scan', 'rel_scan'): plans.rel_scan,
('grid_scan',): plans.grid_scan,
('relative_grid_scan',
'rel_grid_scan'): plans.rel_grid_scan,
('list_scan',): plans.list_scan,
('relative_list_scan',
'rel_list_scan'): plans.relative_list_scan,
('list_grid_scan',): plans.list_grid_scan,
('relative_list_grid_scan',
'rel_list_grid_scan'): plans.rel_list_grid_scan,
('log_scan',): plans.log_scan,
('relative_log_scan',
'rel_log_scan'): plans.relative_log_scan,
('spiral',): plans.spiral,
('relative_spiral', 'rel_spiral'): plans.relative_spiral,
('spiral_fermat',): plans.spiral_fermat,
('relative_spiral_fermat',
'rel_spiral_fermat'): plans.rel_spiral_fermat,
('spiral_square',): plans.spiral_square,
('relative_spiral_square',
'rel_spiral_square'): plans.rel_spiral_square}


class PlanCollectorSub(SimpleNamespace):
"""
A class used to initialize child attributes with methods for PlanCollector's
Expand All @@ -36,8 +40,9 @@ class PlanCollectorSub:
Parameters
----------
methods_to_import : {str: func}
A dictionary mapping method names to methods for the sub class.
plans for methods : {str: func}
A dictionary mapping method tuples of method names to methods for the
sub class.
name : str
The name of the sub-class, usually matches this instances attribute name
parent : PlanCollector
Expand All @@ -52,8 +57,6 @@ class PlanCollectorSub:
Methods
_______
methods : many
All of the methods specified in methods_to_import
__str__() :
Returns a user friendly formatted string showing the structure of the
instance including all of the methods from methods_to_import but not
Expand All @@ -62,9 +65,8 @@ class PlanCollectorSub:
Returns a list of attribute name strings to be used to define what
options are available when doing tab-to-complete.
"""
def __init__(self, methods_to_import, name, parent):
for plan_name, function in methods_to_import.items():
setattr(self, plan_name, function)
def __init__(self, plans_for_methods, name, parent):
super().__init__(**plans_for_methods)

self.name = f'{parent.name}_{name}'
self.parent = parent
Expand Down Expand Up @@ -111,10 +113,8 @@ def __dir__(self):
return attribute_list



class PlanCollector:
"""
A class used to collect together the plans to be used at ARI and SXN,
class PlanCollector(SimpleNamespace):
"""A class used to collect together the plans to be used at ARI and SXN,
This is a 'collector' class that is designed to hold together the plans that
are used at both ARI and SXN. It will include all of the builtin
Expand All @@ -128,39 +128,14 @@ class PlanCollector:
Parameters
----------
plans_to_import : {str:[str,str,...]}
The `bluesky.plans` plans to add as methods, has the structure:
{'plan_name': ['alias 1', 'alias 2', ...]}
for example:
{'rel_scan': ['relative_scan', 'rel_scan']}
Note: only the first alias above will be added to avoid to many options,
all aliases are in this dict as the same dict is used to populate the
global namespace versions of these.
plan_stubs_to_import : {str:[str,str,...]}
The `bluesky.plan_stubs` plan_stubs to add as methods, has the
structure:
{'plan_stub_name': ['alias 1', 'alias 2', ...]}
for example:
{'mv': ['move', 'mv']}
Note: only the first alias above will be added to avoid to many options,
all aliases are in this dict as the same dict is used to populate the
global namespace versions of these.
name : str
The name of the sub-class, usually matches this instances attribute name
Attributes
----------
built-ins : many
All of the built-in plans from `bluesky.plans` (but not aliases) as
given in plans_to_import and plan_stubs_to_import.
name : str
The name of the sub-class, usually matches this instances attribute name
plans for methods : {str: func}
A dictionary mapping method tuples of method names to methods for the
sub class.
name : str
The name of the object, usually matches this instances attribute name
Methods
_______
methods : many
All of the built-in plans from `bluesky.plans` (but not aliases) as
given in plans_to_import and plan_stubs_to_import.
__str__() :
Returns a user friendly formatted string showing the structure of the
instance including all of the methods from plans_to_import,
Expand All @@ -169,55 +144,21 @@ class PlanCollector:
__dir__() :
Returns a list of attribute name strings to be used to define what
options are available when doing tab-to-complete.
"""
def __init__(self, plans_to_import, plan_stubs_to_import, name):
"""
Initializes the methods from plans_to_import and sub_plans_to_import
Parameters
----------
plans_to_import : {str:[str,str,...]}
The `bluesky.plans` plans to add as methods, has the structure:
{'plan_name': ['alias 1', 'alias 2', ...]}
for example:
{'rel_scan': ['relative_scan', 'rel_scan']}
Note: only the first alias above will be added to avoid to many
options, all aliases are in this dict as the same dict is used to
populate the global namespace versions of these.
plan_stubs_to_import : {str:[str,str,...]}
The `bluesky.plan_stubs` plan_stubs to add as methods, has the
structure:
{'plan_stub_name': ['alias 1', 'alias 2', ...]}
for example:
{'mv': ['move', 'mv']}
Note: only the first alias above will be added to avoid to many
options, all aliases are in this dict as the same dict is used to
populate the global namespace versions of these.
"""
self.name = name

relative_plans_to_import = {} # used to separate out relative plans

# create the plan_stub methods
for plan_stub, aliases in plan_stubs_to_import.items():
if plan_stub.startswith('rel_') or plan_stub == 'mvr':
plan_stub_name = aliases[0].split('_', 1)[1]
relative_plans_to_import[plan_stub_name] = getattr(plan_stubs,
plan_stub)
else:
setattr(self, aliases[0], getattr(plan_stubs, plan_stub))

# create the plan methods
for plan, aliases in plans_to_import.items():
if plan.startswith('rel_'):
plan_name = aliases[0].split('_', 1)[1]
relative_plans_to_import[plan_name] = getattr(plans, plan)
else:
setattr(self, aliases[0], getattr(plans, plan))

# add the relative scan subclass
self.relative = PlanCollectorSub(relative_plans_to_import,
name='relative', parent=self)
def __init__(self, plans_for_methods={}, name=None):

if name:
self.name = name
else:
self.name = 'PlanCollector'

super().__init__(**{k[0]: v for k, v in plans_for_methods.items()
if not k[0].startswith('rel')})
self.relative = PlanCollectorSub(plans_for_methods={
k[0]: v for k, v in plans_for_methods.items()
if k[0].startswith('rel')},
name='relative', parent=self)

def __str__(self):
"""
Expand All @@ -234,7 +175,7 @@ def __str__(self):
output = f'\n{self.name}:'
for name, plan in self.__dict__.items():
if name not in ['name', 'parent']:
if plan.__dict__: #if plan has attributes
if plan.__dict__: # if plan has attributes
output += f'\n {plan.__str__().replace(
'\n', '\n ').replace(
f'{self.name}_', '')}'
Expand All @@ -253,7 +194,7 @@ def __dir__(self):
This method is used to give the list of options when using pythons tab
to complete process. It gives all of the method attributes and any
PlanCollectorSun attributes but not the 'name' attribute.
PlanCollectorSub attributes but not the 'name' attribute.
Returns
-------
Expand Down
25 changes: 19 additions & 6 deletions src/ari_sxn_common/common_ophyd.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections import defaultdict
from collections import defaultdict, namedtuple
from nslsii.devices import TwoButtonShutter
from ophyd import (Component, Device, EpicsMotor)
from ophyd.areadetector.base import ADComponent
from ophyd.areadetector.cam import ProsilicaDetectorCam
Expand Down Expand Up @@ -257,6 +258,13 @@ def __init__(self, *args, **kwargs):
device.kind = 'omitted' # set signal to 'omitted'.


class ID29TwoButtonShutter(PrettyStrForSignal, TwoButtonShutter):
"""
An nslsii.devices.TwoButtonShutter class that adds the `__str__` and
`__dir__` methods from PrettyStrForSignal
"""


class Prosilica(PrettyStrForSignal, SingleTrigger, ProsilicaDetector):
"""
Adds the `cam1.array_data` attribute required when not image saving.
Expand Down Expand Up @@ -466,7 +474,7 @@ def get(self, **kwargs):
Returns the result of super().get(**kwargs)
"""
# Determine the locations we are currently 'in'.
locations = []
locations = {}
# Note the next line gives an 'accessing a protected member,
# _locations_data' warning in my editor. I accept the risk !-).
for location, location_data in self.parent._locations_data.items():
Expand Down Expand Up @@ -516,9 +524,11 @@ def get(self, **kwargs):
f'DeviceWithLocations '
f'LocationSignal signals')
if all(value_check):
locations.append(location)

self.put(locations) # Set the value at read time.
locations[location] = True
else:
locations[location] = False
# Set the value at read time.
self.put(self.parent._LocationsTuple(**locations))

return super().get(**kwargs) # run the parent get function.

Expand Down Expand Up @@ -588,7 +598,10 @@ class specific attributes. See the class doc-string for parameter
locations_data = {}
self._locations_data = locations_data

locations = Component(LocationSignal, value=[], name='locations',
self._LocationsTuple = namedtuple('Locations',
self._locations_data.keys())

locations = Component(LocationSignal, name='locations',
kind='config', labels=('position',))


Expand Down

0 comments on commit 2bb658d

Please sign in to comment.