Source code for labscript_devices.NI_DAQmx.blacs_tabs

#####################################################################
#                                                                   #
# /NI_DAQmx/blacs_tab.py                                            #
#                                                                   #
# Copyright 2018, Monash University, JQI, Christopher Billington    #
#                                                                   #
# This file is part of the module labscript_devices, in the         #
# labscript suite (see http://labscriptsuite.org), and is           #
# licensed under the Simplified BSD License. See the license.txt    #
# file in the root of the project for the full license.             #
#                                                                   #
#####################################################################
import labscript_utils.h5_lock
import h5py
from labscript_utils import dedent

from blacs.device_base_class import DeviceTab
from .utils import split_conn_AO, split_conn_DO
from . import models
import warnings


[docs] class NI_DAQmxTab(DeviceTab):
[docs] def initialise_GUI(self): # Get capabilities from connection table properties: connection_table = self.settings['connection_table'] properties = connection_table.find_by_name(self.device_name).properties # Raise an error on old connection tables, since we are not backward compatible # with them. In terms of old shot files being added to the queue once BLACS has # started, BLACS will reject them as being incomatible since the connection # table properties won't match. So we do not need to add a check for that. version = properties.get('__version__', None) if version is None: msg = """Connection table was compiled with the old version of the NI_DAQmx device class. The new BLACS tab is not backward compatible with old shot files (including connection tables). Either downgrade labscript_devices to 2.4.0 or less, or recompile the connection table with labscript_devices 2.5.0 or greater. """ raise RuntimeError(dedent(msg)) num_AO = properties['num_AO'] num_AI = properties['num_AI'] try: AI_chans = properties['AI_chans'] except KeyError: # new code being run on older model specification file # assume legacy behavior, warn user to update AI_chans = [f'ai{i:d}' for i in range(num_AI)] msg = """Connection table was compiled with old model specifications for {0}. Please recompile the connection table. """ warnings.warn(dedent(msg.format(properties['MAX_name'])), FutureWarning) ports = properties['ports'] num_CI = properties['num_CI'] AO_base_units = 'V' if num_AO > 0: AO_base_min, AO_base_max = properties['AO_range'] else: AO_base_min, AO_base_max = None, None AO_base_step = 0.1 AO_base_decimals = 3 clock_terminal = properties['clock_terminal'] clock_mirror_terminal = properties['clock_mirror_terminal'] # get to avoid error on older connection tables connected_terminals = properties.get('connected_terminals', None) static_AO = properties['static_AO'] static_DO = properties['static_DO'] clock_limit = properties['clock_limit'] min_semiperiod_measurement = properties['min_semiperiod_measurement'] # And the Measurement and Automation Explorer (MAX) name we will need to # communicate with the device: self.MAX_name = properties['MAX_name'] # Create output objects: AO_prop = {} for i in range(num_AO): AO_prop['ao%d' % i] = { 'base_unit': AO_base_units, 'min': AO_base_min, 'max': AO_base_max, 'step': AO_base_step, 'decimals': AO_base_decimals, } DO_proplist = [] DO_hardware_names = [] for port_num in range(len(ports)): port_str ='port%d' % port_num port_props = {} for line in range(ports[port_str]['num_lines']): hardware_name = 'port%d/line%d' % (port_num, line) port_props[hardware_name] = {} DO_hardware_names.append(hardware_name) DO_proplist.append((port_str, port_props)) # Create the output objects self.create_analog_outputs(AO_prop) # Create widgets for outputs defined so far (i.e. analog outputs only) _, AO_widgets, _ = self.auto_create_widgets() # now create the digital output objects one port at a time for _, DO_prop in DO_proplist: self.create_digital_outputs(DO_prop) # Manually create the digital output widgets so they are grouped separately DO_widgets_by_port = {} for port_str, DO_prop in DO_proplist: DO_widgets_by_port[port_str] = self.create_digital_widgets(DO_prop) # Auto place the widgets in the UI, specifying sort keys for ordering them: widget_list = [("Analog outputs", AO_widgets, split_conn_AO)] for port_num in range(len(ports)): port_str ='port%d' % port_num DO_widgets = DO_widgets_by_port[port_str] name = "Digital outputs: %s" % port_str if ports[port_str]['supports_buffered']: name += ' (buffered)' else: name += ' (static)' widget_list.append((name, DO_widgets, split_conn_DO)) self.auto_place_widgets(*widget_list) # We only need a wait monitor worker if we are if fact the device with # the wait monitor input. with h5py.File(connection_table.filepath, 'r') as f: waits = f['waits'] wait_acq_device = waits.attrs['wait_monitor_acquisition_device'] wait_acq_connection = waits.attrs['wait_monitor_acquisition_connection'] wait_timeout_device = waits.attrs['wait_monitor_timeout_device'] wait_timeout_connection = waits.attrs['wait_monitor_timeout_connection'] try: timeout_trigger_type = waits.attrs['wait_monitor_timeout_trigger_type'] except KeyError: timeout_trigger_type = 'rising' # Create and set the primary worker self.create_worker( "main_worker", 'labscript_devices.NI_DAQmx.blacs_workers.NI_DAQmxOutputWorker', { 'MAX_name': self.MAX_name, 'Vmin': AO_base_min, 'Vmax': AO_base_max, 'num_AO': num_AO, 'ports': ports, 'clock_limit': clock_limit, 'clock_terminal': clock_terminal, 'clock_mirror_terminal': clock_mirror_terminal, 'static_AO': static_AO, 'static_DO': static_DO, 'DO_hardware_names': DO_hardware_names, 'wait_timeout_device': wait_timeout_device, 'wait_timeout_connection': wait_timeout_connection, 'wait_timeout_rearm_value': int(timeout_trigger_type == 'falling') }, ) self.primary_worker = "main_worker" if wait_acq_device == self.device_name: if wait_timeout_device: wait_timeout_device = connection_table.find_by_name(wait_timeout_device) wait_timeout_MAX_name = wait_timeout_device.properties['MAX_name'] else: wait_timeout_MAX_name = None if num_CI == 0: msg = """Device cannot be the wait monitor acquisiiton device as it has no counter inputs""" raise RuntimeError(dedent(msg)) self.create_worker( "wait_monitor_worker", 'labscript_devices.NI_DAQmx.blacs_workers.NI_DAQmxWaitMonitorWorker', { 'MAX_name': self.MAX_name, 'wait_acq_connection': wait_acq_connection, 'wait_timeout_MAX_name': wait_timeout_MAX_name, 'wait_timeout_connection': wait_timeout_connection, 'timeout_trigger_type': timeout_trigger_type, 'min_semiperiod_measurement': min_semiperiod_measurement, }, ) self.add_secondary_worker("wait_monitor_worker") # Only need an acquisition worker if we have analog inputs. It is important that # the acquisition worker is created after the wait monitor worker if there is # one, because the creation order determines the order that transition_to_manual # runs, and the acquisition processing requires processing that is done in the # wait monitor during transition_to_manual. if num_AI > 0: self.create_worker( "acquisition_worker", 'labscript_devices.NI_DAQmx.blacs_workers.NI_DAQmxAcquisitionWorker', { 'MAX_name': self.MAX_name, 'num_AI': num_AI, 'AI_chans': AI_chans, 'AI_term': properties['AI_term'], 'AI_range': properties['AI_range'], 'AI_start_delay': properties['AI_start_delay'], 'AI_start_delay_ticks': properties['AI_start_delay_ticks'], 'AI_timebase_terminal': properties.get('AI_timebase_terminal',None), 'AI_timebase_rate': properties.get('AI_timebase_rate',None), 'clock_terminal': clock_terminal, }, ) self.add_secondary_worker("acquisition_worker") # Set the capabilities of this device self.supports_remote_value_check(False) self.supports_smart_programming(False)