Last active
December 1, 2015 16:10
-
-
Save klauer/8a096f0b818a8b756da0 to your computer and use it in GitHub Desktop.
areadetector plugin status
This file contains 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
2015-10-20 10:24:55,702 [ophyd_session:WARNING] Instantiating SessionManager outside of IPython | |
Plugin: ColorConvPlugin(name='CC1', alias='None') Port: 'CC1' Source: 'TIM' | |
Plugin: ColorConvPlugin(name='CC2', alias='None') Port: 'CC2' Source: 'TIM' | |
Plugin: HDF5Plugin(name='FileHDF1', alias='None') Port: 'FileHDF1' Source: 'TIM' | |
Plugin: JPEGPlugin(name='FileJPEG1', alias='None') Port: 'FileJPEG1' Source: 'TIM' | |
Plugin: MagickPlugin(name='FileMagick1', alias='None') Port: 'FileMagick1' Source: 'TIM' | |
Plugin: NexusPlugin(name='FileNexus1', alias='None') Port: 'FileNexus1' Source: 'TIM' | |
Plugin: OverlayPlugin(name='OVER1', alias='None') Port: 'OVER1' Source: 'TIM' | |
Plugin: ProcessPlugin(name='PROC1', alias='None') Port: 'PROC1' Source: 'TIM' | |
Plugin: ROIPlugin(name='ROI1', alias='None') Port: 'ROI1' Source: 'TIM' | |
Plugin: ROIPlugin(name='ROI2', alias='None') Port: 'ROI2' Source: 'TIM' | |
Plugin: ROIPlugin(name='ROI3', alias='None') Port: 'ROI3' Source: 'TIM' | |
Plugin: ROIPlugin(name='ROI4', alias='None') Port: 'ROI4' Source: 'TIM' | |
Plugin: StatsPlugin(name='STATS1', alias='None') Port: 'STATS1' Source: 'ROI1' | |
Plugin: StatsPlugin(name='STATS2', alias='None') Port: 'STATS2' Source: 'ROI2' | |
Plugin: StatsPlugin(name='STATS3', alias='None') Port: 'STATS3' Source: 'ROI3' | |
Plugin: StatsPlugin(name='STATS4', alias='None') Port: 'STATS4' Source: 'ROI4' | |
Plugin: StatsPlugin(name='STATS5', alias='None') Port: 'STATS5' Source: 'TIM' | |
Plugin: TIFFPlugin(name='FileTIFF1', alias='None') Port: 'FileTIFF1' Source: 'TRANS1' | |
Plugin: TransformPlugin(name='TRANS1', alias='None') Port: 'TRANS1' Source: 'PROC1' | |
Plugin: ImagePlugin(name='Image1', alias='None') Port: 'Image1' Source: 'PROC1' | |
Plugin: NetCDFPlugin(name='FileNetCDF1', alias='None') Port: 'FileNetCDF1' Source: 'TIM' | |
- Port chain #0 | |
-> TIM -> CC1 | |
- Port chain #1 | |
-> TIM -> CC2 | |
- Port chain #2 | |
-> TIM -> FileHDF1 | |
- Port chain #3 | |
-> TIM -> FileJPEG1 | |
- Port chain #4 | |
-> TIM -> FileMagick1 | |
- Port chain #5 | |
-> TIM -> FileNexus1 | |
- Port chain #6 | |
-> TIM -> OVER1 | |
- Port chain #7 | |
-> TIM [blocking] -> PROC1 [blocking] -> TRANS1 [blocking] -> FileTIFF1 | |
* Plugin ProcessPlugin(name='PROC1', alias='None'): | |
XF:03IDC-ES{Tpx:1}Proc1:BlockingCallbacks_RBV = 1: Blocking | |
XF:03IDC-ES{Tpx:1}Proc1:EnableFilter_RBV = 1: Filter is enabled | |
XF:03IDC-ES{Tpx:1}Proc1:NumFilter_RBV = 10: Only processing 1 callback per 10 images | |
* Plugin TransformPlugin(name='TRANS1', alias='None'): | |
XF:03IDC-ES{Tpx:1}Trans1:BlockingCallbacks_RBV = 1: Blocking | |
* Plugin TIFFPlugin(name='FileTIFF1', alias='None'): | |
XF:03IDC-ES{Tpx:1}TIFF1:BlockingCallbacks_RBV = 1: Blocking | |
- Port chain #8 | |
-> TIM [blocking] -> PROC1 -> Image1 | |
* Plugin ProcessPlugin(name='PROC1', alias='None'): | |
XF:03IDC-ES{Tpx:1}Proc1:BlockingCallbacks_RBV = 1: Blocking | |
XF:03IDC-ES{Tpx:1}Proc1:EnableFilter_RBV = 1: Filter is enabled | |
XF:03IDC-ES{Tpx:1}Proc1:NumFilter_RBV = 10: Only processing 1 callback per 10 images | |
- Port chain #9 | |
-> TIM -> ROI1 -> STATS1 | |
- Port chain #10 | |
-> TIM -> ROI2 -> STATS2 | |
- Port chain #11 | |
-> TIM -> ROI3 -> STATS3 | |
- Port chain #12 | |
-> TIM -> ROI4 -> STATS4 | |
- Port chain #13 | |
-> TIM -> STATS5 | |
- Port chain #14 | |
-> TIM -> FileNetCDF1 |
This file contains 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
import sys | |
import logging | |
import subprocess | |
from ophyd.controls.areadetector.detectors import AreaDetector | |
from ophyd.controls.areadetector.plugins import (PluginBase, | |
get_areadetector_plugin, | |
ProcessPlugin) | |
class DetectorNode: | |
def __init__(self, det, port_name): | |
self.plugin = det | |
self.port_name = port_name | |
self.children = [] | |
def __repr__(self): | |
# hack because I don't have the proper prefix yet | |
return '{}(port_name={!r})'.format(self.plugin.__class__.__name__, | |
self.port_name) | |
class PluginNode: | |
def __init__(self, plugin, parent=None): | |
self.plugin = plugin | |
self.parent = parent | |
self.children = [] | |
@property | |
def port_name(self): | |
return self.plugin.port_name.get() | |
def __repr__(self): | |
return '{}(port_name={!r})'.format(self.plugin.__class__.__name__, | |
self.port_name) | |
class PluginTree: | |
def __init__(self): | |
self.roots = [] | |
self.source_ports = {} | |
self.port_to_plugin = {} | |
self.waiting = {} # waiting for parent :( | |
def done(self): | |
# assume all waiting nodes are top-level detectors | |
for port_name, children in self.waiting.items(): | |
parent = DetectorNode(AreaDetector('todo', name=port_name), port_name) | |
for child_plugin in children: | |
child_plugin.parent = parent | |
parent.children.append(child_plugin) | |
self.roots.append(parent) | |
self.waiting = {} | |
def get_paths(self): | |
def iter_children(plugin, parent_nodes): | |
if not plugin.children: | |
yield parent_nodes | |
else: | |
for child in plugin.children: | |
yield from iter_children(child, parent_nodes + [child]) | |
for plugin in self.top_level: | |
for path in iter_children(plugin, [plugin]): | |
yield [node.plugin for node in path] | |
@property | |
def top_level(self): | |
for port_name, plugin in self.waiting.items(): | |
yield from plugin | |
for plugin in self.roots: | |
yield plugin | |
def __iter__(self): | |
def iter_children(plugin): | |
yield plugin | |
for plugin in plugin.children: | |
yield from iter_children(plugin) | |
for plugin in self.top_level: | |
yield from iter_children(plugin) | |
def add(self, plugin): | |
port_name = plugin.port_name.get() | |
if port_name is None: | |
raise ValueError('Plugin not connected') | |
node = PluginNode(plugin) | |
self.port_to_plugin[port_name] = node | |
if port_name in self.waiting: | |
for child_plugin in self.waiting[port_name]: | |
child_plugin.parent = node | |
node.children.append(child_plugin) | |
del self.waiting[port_name] | |
source_port = plugin.nd_array_port.get() | |
self.source_ports[plugin] = source_port | |
try: | |
parent = self.port_to_plugin[source_port] | |
except KeyError: | |
if source_port not in self.waiting: | |
self.waiting[source_port] = [] | |
self.waiting[source_port].append(node) | |
else: | |
parent.children.append(node) | |
node.parent = parent | |
def grep_pvs(prefix, path='/cf-update/*.dbl', suffix='EnableCallbacks', | |
grep_tool='/bin/grep', ignore_exceptions=True): | |
expr = '^{}.*{}$'.format(prefix, suffix) | |
command = '{} -he "{}" {}'.format(grep_tool, expr, path) | |
try: | |
stdout = subprocess.check_output(command, stderr=subprocess.DEVNULL, shell=True) | |
except subprocess.CalledProcessError as ex: | |
# some permissions errors in /cf-update | |
if not ignore_exceptions: | |
raise | |
stdout = ex.output | |
stdout = stdout.decode('ascii') | |
for match in stdout.split('\n'): | |
match = match.strip() | |
if match: | |
yield match[:-len(suffix)] | |
def get_plugins_by_prefix(det_prefix, verbose=True): | |
for prefix in grep_pvs(det_prefix): | |
try: | |
# Try to guess the plugin-type by regular expression | |
plugin = get_areadetector_plugin(prefix) | |
except ValueError: | |
# Or give up and just give a generic plugin | |
plugin = PluginBase(prefix) | |
except AttributeError: | |
# PV not found, oops - should fix this in ophyd | |
if verbose: | |
print('PV not found', prefix) | |
else: | |
plugin._name = plugin.port_name.get() | |
yield plugin | |
def make_port_tree(plugins): | |
tree = PluginTree() | |
for plugin in plugins: | |
tree.add(plugin) | |
return tree | |
def check_plugin(plugin): | |
blocking = plugin.blocking_callbacks.get() | |
if blocking: | |
yield ('Blocking', plugin.blocking_callbacks) | |
min_time = plugin.min_callback_time.get() | |
if blocking and min_time > 0: | |
yield ('Minimum time set', plugin.min_time) | |
if (isinstance(plugin, ProcessPlugin) and plugin.enable_filter.get()): | |
yield ('Filter is enabled', plugin.enable_filter) | |
n_filter = plugin.num_filter.get() | |
cb_filter = plugin.filter_callbacks.get(as_string=True) | |
if n_filter > 0 and cb_filter == 'Array N only': | |
yield ('Only processing 1 callback per {} images' | |
''.format(n_filter), plugin.num_filter) | |
def sup_ad(prefix): | |
plugins = list(get_plugins_by_prefix(prefix)) | |
for plugin in plugins: | |
port = plugin.port_name.get() | |
source_port = plugin.nd_array_port.get() | |
print('Plugin: {} Port: {!r} Source: {!r}' | |
''.format(plugin, port, source_port)) | |
plugin_map = make_port_tree(plugins) | |
plugin_map.done() | |
for i, path in enumerate(plugin_map.get_paths()): | |
print('- Port chain #{}'.format(i)) | |
for plugin in path: | |
if not isinstance(plugin, AreaDetector): | |
if plugin.blocking_callbacks.get(): | |
print(' [blocking] ', end='') | |
print(' ->', plugin.name, end='') | |
print() | |
for plugin in path: | |
if isinstance(plugin, AreaDetector): | |
continue | |
notes = list(check_plugin(plugin)) | |
if notes: | |
print('* Plugin {}:'.format(plugin)) | |
for note, signal in notes: | |
print('\t{} = {}: {}'.format(signal.pvname, | |
signal.get(), note)) | |
# for plugin in path: | |
# print(plugin, end=' -> ') | |
# print() | |
if __name__ == '__main__': | |
logging.basicConfig(level=logging.INFO) | |
logging.getLogger('ophyd_session').setLevel(logging.INFO) | |
try: | |
prefix = sys.argv[1] | |
except IndexError: | |
prefix = 'XF:03IDC-ES{Tpx:1}' | |
sup_ad(prefix) |
Yep, exactly. :) Maybe per-blocking plugin we could also dump out some specific info to help debug issues more quickly?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice! It would be interesting to run that against the PE1/2 detectors at xpd.
But, that may be what prompted you thinking about this in the first place...