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)
예제 #2
0
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
예제 #3
0
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
예제 #4
0
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)