ophyd icon indicating copy to clipboard operation
ophyd copied to clipboard

SoftPositioner is not identified as device or signal using walk_components()

Open prjemian opened this issue 2 years ago • 4 comments

While exploring a resolution to https://github.com/BCDA-APS/apstools/issues/641, tested the proposed solution there to a diffractometer instance from hklpy, such as:

import hkl

sim4c = hkl.SimulatedE4CV("", name="sim4c")

Using the walk_components() method, it is possible to print a small table showing if a Component is either a device or a signal:

import pyRestTable

table = pyRestTable.Table()
table.labels = "dotted_name is_device  is_signal".split()
for item in sim4c.walk_components():
    table.addRow((item.dotted_name,item.item.is_device, item.item.is_signal))    
print(table.reST(fmt="md"))

This table is printed:

dotted_name is_device is_signal
energy False True
energy_units False True
energy_offset False True
energy_update_calc_flag False True
geometry_name False True
class_name False True
sample_name False True
lattice False True
lattice_reciprocal False True
U False True
UB False True
reflections False True
reflections_details False True
ux False True
uy False True
uz False True
diffractometer_name False True
_hklpy_version False True
_pseudos False True
_reals False True
_constraints False True
_mode False True
orientation_attrs False True
max_forward_iterations False True
h True False
h.readback False True
h.setpoint False True
k True False
k.readback False True
k.setpoint False True
l True False
l.readback False True
l.setpoint False True
omega False False
chi False False
phi False False
tth False False

Note that omega, chi, phi, and tth that both columns are False. It is expected that each of these SoftPositioners would be identified as is_device=True.

prjemian avatar Feb 24 '23 19:02 prjemian

For reference: SoftPositioner, walk_components(), is_device, and is_signal

prjemian avatar Feb 24 '23 19:02 prjemian

Looking at PositionerBase, it is not clear whether this might be used as a Device or as a Signal, so the test: https://github.com/bluesky/ophyd/blob/ac6251131b4638c715ce8b90478a0d714cb857d3/ophyd/device.py#L215-L218

returns False since it is not a subclass of Device. Similarly, this cannot be a Signal since it is not subclass: https://github.com/bluesky/ophyd/blob/ac6251131b4638c715ce8b90478a0d714cb857d3/ophyd/device.py#L220-L223

Perhaps the test for is_device could consider if the object in question as Component attributes. Wouldn't this determine whether the object is or is not a Device? And the test for is_signal would verify that the class does not have Component attributes.

prjemian avatar Feb 24 '23 19:02 prjemian

In [130]: dir(sim4c.omega)
Out[130]: 
['SUB_DONE',
 'SUB_READBACK',
 'SUB_START',
 '_OphydObject__any_instantiated',
 '_OphydObject__instantiation_callbacks',
 '_OphydObject__register_instance',
 '_SUB_REQ_DONE',
 '__annotations__',
 '__class__',
 '__copy__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getnewargs_ex__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_args_cache',
 '_attr_name',
 '_callbacks',
 '_cb_count',
 '_cid_to_event_mapping',
 '_default_sub',
 '_done_moving',
 '_egu',
 '_kind',
 '_limits',
 '_mark_as_instantiated',
 '_moving',
 '_name',
 '_ophyd_labels_',
 '_parent',
 '_position',
 '_repr_info',
 '_reset_sub',
 '_run_subs',
 '_set_position',
 '_settle_time',
 '_setup_move',
 '_started_moving',
 '_timeout',
 '_unwrapped_callbacks',
 '_validate_kind',
 'add_instantiation_callback',
 'attr_name',
 'check_value',
 'clear_sub',
 'connected',
 'control_layer_log',
 'describe',
 'describe_configuration',
 'destroy',
 'dotted_name',
 'egu',
 'event_types',
 'high_limit',
 'hints',
 'kind',
 'limits',
 'log',
 'low_limit',
 'move',
 'moving',
 'name',
 'parent',
 'position',
 'read',
 'read_configuration',
 'report',
 'root',
 'set',
 'settle_time',
 'source',
 'stop',
 'subscribe',
 'subscriptions',
 'timeout',
 'unsubscribe',
 'unsubscribe_all']

Does not look like a Device to me since it has no Component attributes. Also:

In [131]: sim4c.omega.read()
Out[131]: OrderedDict([('sim4c_omega', {'value': 0, 'timestamp': 1677266815.4288268})])

prjemian avatar Feb 24 '23 19:02 prjemian

Perhaps, leave is_device as-is and make is_signal be not _is_device. Such as:

 @property 
 def is_signal(self): 
     "Does this Component contain a Signal?" 
     return isinstance(self.cls, type) and not self.is_device

prjemian avatar Feb 24 '23 19:02 prjemian