import logging log = logging.getLogger(__name__) from threading import RLock from time import time from spacq.tool.box import Enum, Synchronized """ Hardware device abstraction interface. """ # PyVISA, Linux GPIB, PyVISA USB. drivers = Enum(['pyvisa', 'lgpib', 'pyvisa_usb']) # Try to import all available drivers. available_drivers = [] try: import Gpib import gpib except ImportError: pass else: available_drivers.append(drivers.lgpib) try: import visa except ImportError: pass else: available_drivers.append(drivers.pyvisa)
class Environment(object): """ An AST-traversal environment. """ # Traversal stages: # declarations: collect variable names and types # values: collect variable values # commands: verify commands for outputs # waveforms: generate waveforms stages = Enum(['declarations', 'values', 'commands', 'waveforms']) # The stages that must happen (in this order) before waveform generation. # These only require the data which is contained in the pulse program itself. prep_stages = [stages.declarations, stages.values, stages.commands] def __init__(self): self.stage = None self.stack = [] # Declared variable names (with types as values). self.variables = {} # Variable and attribute values. # Keys are tuples of strings: # ('x',): x is a variable # ('x', 'y'): y is an attribute self.values = {} # Whether acquisition has already been requested. self.acquisition = False # Any errors; each error is of the form # (error string, (row, col, code line)) self.errors = [] # Whether to actually generate waveforms. self.dry_run = False # Waveform generators for the output channels. # Keys are output names. self.generators = {} # Generated waveforms. self.waveforms = {} # Where to look for shapes. self.cwd = None # Shapes that could not be found. self.missing_shapes = set() # Default frequency. self.frequency = Quantity(1, 'Hz') @property def missing_values(self): existing_values = set(self.values.keys()) return self.all_values - existing_values def add_error(self, msg, loc=None): """ Add an error. """ self.errors.append((msg, loc)) def pre_stage(self): """ Set up for a stage. """ if self.stage == self.stages.waveforms: if self.missing_values: values = ', '.join('.'.join(x) for x in sorted(self.missing_values)) raise ValueError( 'Cannot generate waveforms while values are missing: {0}'. format(values)) # Set up output waveform generators. for output in self.waveforms: self.generators[output] = Generator(frequency=self.frequency, dry_run=self.dry_run) def post_stage(self): """ Clean up after a stage has completed. """ if self.stage == self.stages.declarations: # Prepare for output waveform generators. for output in [ var for var, type in list(self.variables.items()) if type == 'output' ]: self.generators[output] = None self.waveforms[output] = None # Generate labels for all necessary values. self.all_values = set() for name, type in list(self.variables.items()): if type == 'pulse': for attr in ['amplitude', 'length', 'shape']: self.all_values.add((name, attr)) elif type == 'acq_marker': for attr in ['marker_num', 'output']: self.all_values.add((name, attr)) elif type != 'output': self.all_values.add((name, )) elif self.stage == self.stages.waveforms: # Finalize waveform creation. for output in self.generators: self.waveforms[output] = self.generators[output].waveform def set_value(self, target, value): """ Set a value if the types work out. TypeError otherwise. """ # Type checking. var_type = self.variables[target[0]] if var_type == 'acq_marker': if len(target) == 1: raise TypeError('Must assign dictionary to acq_marker') else: if target[1] == 'marker_num': if not isinstance(value, int): raise TypeError('Must assign int to acq_marker num') elif target[1] == 'output': if not isinstance(value, str): raise TypeError('Must assign string to acq_marker num') elif var_type == 'delay': try: value.assert_dimensions('s') except (AttributeError, IncompatibleDimensions): raise TypeError('Must assign time quantity to delay') elif var_type == 'int': if not isinstance(value, int): raise TypeError('Must assign integer to int') elif var_type == 'pulse': if len(target) == 1: raise TypeError('Must assign dictionary to pulse') else: if target[1] == 'amplitude': try: value.assert_dimensions('V') except (AttributeError, IncompatibleDimensions): raise TypeError( 'Must assign voltage quantity to pulse amplitude') elif target[1] == 'length': try: value.assert_dimensions('s') except (AttributeError, IncompatibleDimensions): raise TypeError( 'Must assign time quantity to pulse length') elif target[1] == 'shape': if not isinstance(value, str): raise TypeError('Must assign string to pulse shape') else: raise TypeError( 'Cannot assign to variable of type "{0}"'.format(var_type)) if target in self.values and self.values[target] is not None: raise TypeError('Re-assignment of {0}'.format(target)) else: self.values[target] = value def traverse_tree(self, root): """ Modify the Environment, given a pulse program AST. """ self.pre_stage() root.visit(self) self.post_stage() def format_errors(self): result = [] # Sort by line number, then column number, and ignore duplicates. for error in sorted(set(self.errors), key=(lambda x: (x[1][0], x[1][1]) if x[1] is not None else (-1, -1))): if error[1] is not None: result.append(format_error(error[0], *error[1])) else: result.append(format_error(error[0])) return result
import logging log = logging.getLogger(__name__) from spacq.tool.box import Enum """ Figure out which plot formats can be used on this system. """ # 2D curve, 3D colormapped, 3D surface, 3D waveforms formats = Enum(['two_dimensional', 'colormapped', 'surface', 'waveforms']) # Try to import all available formats. available_formats = {} try: from .colormapped import ColormappedPlotSetupDialog except ImportError as e: log.debug('Could not import from .colormapped: {0}'.format(str(e))) else: available_formats[formats.colormapped] = ColormappedPlotSetupDialog try: from .surface import SurfacePlotSetupDialog, WaveformsPlotSetupDialog except ImportError as e: log.debug('Could not import from .surface: {0}'.format(str(e))) else: available_formats[formats.surface] = SurfacePlotSetupDialog available_formats[formats.waveforms] = WaveformsPlotSetupDialog
class DeviceConfig(object): """ Description for a device. """ address_modes = Enum([ 'ethernet', 'gpib', 'usb', ]) def __init__(self, name): self.name = name # Connection configuration. self.address_mode = None self.ip_address = None self.gpib_board = 0 self.gpib_pad = 0 self.gpib_sad = 0 self.usb_resource = None # Information about module that implements this device. self.manufacturer = None self.model = None self.mock = False # Device-specific GUI setup. self.gui_setup = None # Resource path to label mappings. self.resource_labels = {} # The connected device object. self._device = None # Label to resource object mappings. self.resources = {} def __getstate__(self): """ Return a modified dictionary for pickling. """ result = self.__dict__.copy() # Do not pickle references to mutable objects. del result['_device'] del result['resources'] return result def __setstate__(self, dict): """ Revert the changes done by __getstate__. """ self.__dict__ = dict # Set missing values to defaults. self._device = None self.resources = {} @property def device(self): """ The connected device object. """ return self._device @device.setter def device(self, value): self._device = value self.gui_setup = None try: self.gui_setup = self._device._gui_setup except AttributeError: # No device or no GUI setup available. pass def diff_resources(self, new): """ Compare the resources belonging to 2 DeviceConfig objects. The result is a tuple of: resources which appear resources which change resources which disappear where all "resources" are resource labels. """ old_labels, new_labels = set(self.resources), set(new.resources) appeared = new_labels - old_labels disappeared = old_labels - new_labels changed = set() possibly_changed = old_labels.intersection(new_labels) for p in possibly_changed: if self.resources[p] is not new.resources[p]: changed.add(p) return (appeared, changed, disappeared) def connect(self): """ Create an instance of the implementation and connect to it. """ if self.address_mode not in self.address_modes: raise ConnectionError('Invalid address mode specified.') if self.manufacturer is None or self.model is None: raise ConnectionError('No implementation specified.') address = {} if not self.mock: if self.address_mode == self.address_modes.ethernet: if self.ip_address is None: raise ConnectionError('No IP address specified.') address['ip_address'] = self.ip_address elif self.address_mode == self.address_modes.gpib: address['gpib_board'] = self.gpib_board address['gpib_pad'] = self.gpib_pad address['gpib_sad'] = self.gpib_sad elif self.address_mode == self.address_modes.usb: if self.usb_resource is None: raise ConnectionError('No USB resource specified.') address['usb_resource'] = self.usb_resource tree = device_tree() try: subtree = tree[self.manufacturer] except KeyError: raise ConnectionError('Unknown manufacturer: {0}'.format( self.manufacturer)) try: subtree = subtree[self.model] except KeyError: raise ConnectionError('Unknown model: {0}'.format(self.model)) kind = 'mock' if self.mock else 'real' try: implementation = subtree[kind] except KeyError: raise ConnectionError('Unknown kind: {0}'.format(kind)) try: device = implementation(autoconnect=False, **address) except (ValueError, NotImplementedError) as e: raise ConnectionError('Unable to create device.', e) try: device.connect() except DeviceNotFoundError as e: raise ConnectionError('Unable to make connection to device.', e) self.device = device
def __init__(self, parent, headings, axis_names, max_mesh = [-1, -1], interp_mode = '_remove', *args, **kwargs): Dialog.__init__(self, parent, *args, **kwargs) self.max_mesh = max_mesh #derivative classes can choose to call this constructor with or wihtout the max_mesh argument; #defaults to [-1, -1] meaning 'skip' self.interp_mode = interp_mode #derivative classes can call this constructor with or without the interp_mode argument; #defualt is -1 meaning 'skip' -- show no radio buttons and do no interpolation (for 2D plots) self.axes = [None for _ in axis_names] # Dialog. dialog_box = wx.BoxSizer(wx.VERTICAL) ## Axis setup. axis_panel = AxisSelectionPanel(self, axis_names, headings, self.OnAxisSelection) dialog_box.Add(axis_panel) ## Input: Interpolation Mode: Radio Buttons InterpolationModes = Enum(['_none','_x','_y','_2d_no_mask', '_2d_full_mask']) self.InterpolationModes = InterpolationModes if (not interp_mode == '_remove'): try: if not any( interp_mode == x for x in InterpolationModes ): raise ValueError(interp_mode) except ValueError as e: MessageDialog(self, 'Bad interpolation mode '+str(e)+'. No interplolation assumed.', 'ValueError').Show() self.interp_mode = '_remove' if (not self.interp_mode == '_remove'): radio_box = wx.BoxSizer(wx.HORIZONTAL) radio_static_box = wx.StaticBox(self, label='Interpolation Mode:') radio_settings_box = wx.StaticBoxSizer(radio_static_box, wx.HORIZONTAL) radio_box.Add(radio_settings_box, proportion=1, flag=wx.EXPAND|wx.ALL, border=5) dialog_box.Add(radio_box, flag=wx.CENTER) self.rb1 = wx.RadioButton(self, -1, 'None\n(Not Implemented Yet)', (10, 10), style=wx.RB_GROUP) self.rb2 = wx.RadioButton(self, -1, 'x-axis only\n(Not Implemented Yet)', (10, 10)) self.rb3 = wx.RadioButton(self, -1, 'y-axis only\n(SLOW!)', (10, 10)) self.rb4 = wx.RadioButton(self, -1, '2D (no mask)', (10, 10)) self.rb5 = wx.RadioButton(self, -1, '2D with mask\n(warning: very high memory usage)', (10, 10)) radio_settings_box.Add(self.rb1, flag=wx.RIGHT, border=10) radio_settings_box.Add(self.rb2, flag=wx.RIGHT, border=10) radio_settings_box.Add(self.rb3, flag=wx.RIGHT, border=10) radio_settings_box.Add(self.rb4, flag=wx.RIGHT, border=10) radio_settings_box.Add(self.rb5, flag=wx.RIGHT, border=10) #Boy I miss C-style swith-case if self.interp_mode == InterpolationModes._none: self.rb1.SetValue(True) elif self.interp_mode == InterpolationModes._x: self.rb2.SetValue(True) elif self.interp_mode == InterpolationModes._y: self.rb3.SetValue(True) elif self.interp_mode == InterpolationModes._2d_no_mask: self.rb4.SetValue(True) else: self.rb5.SetValue(True) ## Input: Max Grid Points if (all (item > 0 for item in max_mesh)): self.has_max_mesh_value = True grid_box = wx.BoxSizer(wx.HORIZONTAL) button_static_box = wx.StaticBox(self, label='Max Grid Points') settings_box = wx.StaticBoxSizer(button_static_box, wx.HORIZONTAL) grid_box.Add(settings_box, proportion=1, flag=wx.EXPAND|wx.ALL, border=5) dialog_box.Add(grid_box, flag=wx.CENTER) ### Max X mesh size. settings_box.Add(wx.StaticText(self, label='x: '), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT) self.mesh_x_value = wx.TextCtrl(self, value=str(max_mesh[0]), size=(100, -1), style=wx.TE_PROCESS_ENTER) self.Bind(wx.EVT_TEXT_ENTER, self.OnXValue, self.mesh_x_value) settings_box.Add(self.mesh_x_value, flag=wx.RIGHT, border=20) ### Max Y mesh size. settings_box.Add(wx.StaticText(self, label='y: '), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT) self.mesh_y_value = wx.TextCtrl(self, value=str(max_mesh[1]), size=(100, -1), style=wx.TE_PROCESS_ENTER) self.Bind(wx.EVT_TEXT_ENTER, self.OnYValue, self.mesh_y_value) settings_box.Add(self.mesh_y_value, flag=wx.RIGHT, border=20) else: self.has_max_mesh_value = False ## End buttons. button_box = wx.BoxSizer(wx.HORIZONTAL) dialog_box.Add(button_box, flag=wx.CENTER) self.ok_button = wx.Button(self, wx.ID_OK) self.ok_button.Disable() self.Bind(wx.EVT_BUTTON, self.OnOk, self.ok_button) button_box.Add(self.ok_button) cancel_button = wx.Button(self, wx.ID_CANCEL) button_box.Add(cancel_button) self.SetSizerAndFit(dialog_box)