Beispiel #1
0
 def digitaloff(self):
     data = np.array([0, 0, 0], dtype=np.uint8)
     task = Task()
     task.CreateDOChan("/Dev1/port0/line0", "", DAQmx_Val_ChanPerLine)
     task.StartTask()
     task.WriteDigitalLines(1, 1, DAQmx_Val_WaitInfinitely,
                            DAQmx_Val_GroupByChannel, data, None, None)
     task.StopTask()
Beispiel #2
0
def connect_do_port(devport):
    '''
    Initialize task for writing to digital output port
    in    Device/port e.g. Dev1/port0
    out   Task handle
    '''
    task = Task()
    task.CreateDOChan(devport, '', DAQmx_Val_ChanForAllLines)
    task.StartTask()
    return task
Beispiel #3
0
def connect_do_line(devportline):
    '''
    Initialize task for writing to digital output lines
    in      Device/port/lines e.g. Dev1/port0/line0:3
            also single line supported e.g. Dev1/port0/line1
    out     Task handle
    '''
    task = Task()
    task.CreateDOChan(devportline, '', DAQmx_Val_ChanPerLine)
    task.StartTask()
    return task
def port_supports_buffered(device_name, port, clock_terminal=None):
    """Empirically determines if the digital port supports buffered output.

    Args:
        device_name (str): NI-MAX device name
        port (int): Which port to intro-spect
        clock_terminal (str, optional): String that specifies the clock terminal.

    Returns:
        bool: True if `port` supports buffered output.
    """
    all_terminals = DAQmxGetDevTerminals(device_name)
    if clock_terminal is None:
        clock_terminal = all_terminals[0]
    npts = 16
    task = Task()
    clock_terminal_full = '/' + device_name + '/' + clock_terminal
    data = np.zeros(npts, dtype=np.uint32)
    task.CreateDOChan(device_name + "/" + port, "", c.DAQmx_Val_ChanForAllLines)
    task.CfgSampClkTiming(
        clock_terminal_full, 100, c.DAQmx_Val_Rising, c.DAQmx_Val_FiniteSamps, npts
    )
    written = int32()
    try:
        task.WriteDigitalU32(
            npts, False, 10.0, c.DAQmx_Val_GroupByScanNumber, data, byref(written), None
        )
    except (
        PyDAQmx.DAQmxFunctions.BufferedOperationsNotSupportedOnSelectedLinesError,
        PyDAQmx.DAQmxFunctions.PhysicalChanNotSupportedGivenSampTimingType653xError,
    ):
        return False
    except (
        PyDAQmx.DAQmxFunctions.CantUsePort3AloneGivenSampTimingTypeOn653xError,
        PyDAQmx.DAQmxFunctions.CantUsePort1AloneGivenSampTimingTypeOn653xError,
    ):
        # Ports that throw this error on 653x devices do support buffered output, though
        # there are requirements that multiple ports be used together.
        return True
    except PyDAQmx.DAQmxFunctions.RouteNotSupportedByHW_RoutingError:
        # Try again with a different terminal
        current_terminal_index = all_terminals.index(clock_terminal)
        if current_terminal_index == len(all_terminals) - 1:
            # There are no more terminals. No terminals can be used as clocks,
            # therefore we cannot do externally clocked buffered output.
            return False
        next_terminal_to_try = all_terminals[current_terminal_index + 1]
        return port_supports_buffered(device_name, port, next_terminal_to_try)
    else:
        return True
    finally:
        task.ClearTask()
Beispiel #5
0
    def shutter(self):

        task = Task()
        task.CreateDOChan("/Dev1/port1/line1", "",
                          PyDAQmx.DAQmx_Val_ChanForAllLines)
        task.StartTask()
        if self.button_s.isChecked():
            self.label_state.setText('ON')
            task.WriteDigitalScalarU32(1, 10.0, 1, None)
            task.StopTask()
            #print(1)
        else:
            self.label_state.setText('OFF')
            task.WriteDigitalScalarU32(1, 10.0, 0, None)
            task.StopTask()
Beispiel #6
0
class Stim():
	def __init__(self):
		self.pulse = np.zeros(1, dtype=np.uint8)
		self.task = Task()

	def connect(self):
		self.task.CreateDOChan("/Dev1/port0/line3","",PyDAQmx.DAQmx_Val_ChanForAllLines)
		self.task.StartTask()

	def disconnect(self):
		self.task.StopTask()

	def stim_on(self):
	 	self.pulse[0]=1
	 	self.task.WriteDigitalLines(1, 1, 5.0, PyDAQmx.DAQmx_Val_GroupByChannel, self.pulse, None, None)

	def stim_off(self):
	 	self.pulse[0]=0
	 	self.task.WriteDigitalLines(1, 1, 5.0, PyDAQmx.DAQmx_Val_GroupByChannel, self.pulse , None, None)
Beispiel #7
0
class Laser():
    """
	Laser Controller interface.
 	This is a class for low level interface control of the laser which is controlled
	through a NI acquisition card.
	"""
    def __init__(self):
        self.pulse = np.zeros(1, dtype=np.uint8)

    def connect(self, line="/Dev1/port0/line2"):
        """
		Connect the laser to the software through the NI card through
		digital lines interface.
		"""
        self.task = Task()
        self.task.CreateDOChan(line, "", PyDAQmx.DAQmx_Val_ChanForAllLines)
        self.task.StartTask()

    def disconnect(self):
        """
		Disconnect the laser from the software through the NI card
		"""
        self.task.StopTask()

    def turn_on(self):
        """
		Turn the laser ON
		"""
        self.pulse[0] = 1
        self.task.WriteDigitalLines(1, 1, 5.0,
                                    PyDAQmx.DAQmx_Val_GroupByChannel,
                                    self.pulse, None, None)

    def turn_off(self):
        """
		Turn the laser OFF
		"""
        self.pulse[0] = 0
        self.task.WriteDigitalLines(1, 1, 5.0,
                                    PyDAQmx.DAQmx_Val_GroupByChannel,
                                    self.pulse, None, None)
class DigitalOutput(object):

    ## This function returns the sample rate configured in the DAQmx Task.
    #  @param self The object pointer.
    def getSampleRate(self):
        if self.initialized:
            sampleRate = float64()
            self.status = self.taskRef.GetSampClkRate(ctypes.byref(sampleRate))
            self._sampleRate = sampleRate.value
        return self._sampleRate

    ## This function sets the sample rate in the DAQmx Task.
    #  @param self The object pointer.
    #  @param value The value to set the sample rate.
    def setSampleRate(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampClkRate(float64(value))
        self._sampleRate = value

    ## This function deletes the sample rate variable inside the DigitalOutput
    #  object.
    #  @param self The object pointer.
    def _delSampleRate(self):
        del self._sampleRate

    sampleRate = property(getSampleRate, setSampleRate, _delSampleRate, doc=
                             """The sample rate of the digital output.""")

#-- Samples Per Channel Property

    ## This function returns the samples per channel configured in the DAQmx Task.
    #  @param self The object pointer.
    def getSamplesPerChannel(self):
        if self.initialized:
            samplesPerChannel = uInt64()
            self.status = self.taskRef.GetSampQuantSampPerChan(
                    ctypes.byref(samplesPerChannel))
            self._samplesPerChannel = samplesPerChannel.value
        return self._samplesPerChannel

    ## This function sets the samples per channel in the DAQmx Task.
    #  @param self The object pointer.
    #  @param value The value to set the samples per channel.
    def setSamplesPerChannel(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampQuantSampPerChan(uInt64(value))
        self._samplesPerChannel = value

    ## This function deletes the samplesPerChannel variable from the DigitalOutput
    #  object.
    #  @param self The object pointer.
    def _delSamplesPerChannel(self):
        del self._samplesPerChannel

    samplesPerChannel = property(getSamplesPerChannel, setSamplesPerChannel,
            _delSamplesPerChannel,
            """The samples per channel of the digital output.""")

#-- Clock Source Property

    ## This function returns the sample clock source configured in the DAQmx Task.
    #  @param self The object pointer.
    def getClkSource(self):
        if self.initialized:
            buffSize = uInt32(255)
            buff = ctypes.create_string_buffer(buffSize.value)
            self.status = self.taskRef.GetSampClkSrc(buff, buffSize)
            self._clkSource = buff.value
        return self._clkSource

    ## This function sets the sample clock source in the DAQmx Task.
    #  @param self The object pointer.
    #  @param value The value to set the clock source.
    def setClkSource(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampClkSrc(value)
            value = self.getClkSource()
        self._clkSource = value

    ## This function deletes the clkSource variable within the DigitalOutput
    #  object.
    #  @param self The object pointer.
    def _delClkSource(self):
        del self._clkSource

    clkSource = property(getClkSource, setClkSource, _delClkSource,
    """The clock source for the digital outputsample clock.""")

#-------------------- Functions --------------------

    ## This function is a constructor for the DigitalOutput class.
    #
    #  It creates the internal variables required to perform functions within
    #  the class. This function does not initialize any hardware.
    #  @param self This object pointer
    def __init__(self):
        ## The DAQmx task reference.
        self.taskRef = Task()

        ## This is the status of the DAQmx task.
        #
        #  A value greater than 0 means that an error has occurred. When the
        #  status is greater than 0 an error should be reported by the class.
        self.status = int32()

        ## This is a boolean that is true when the DAQmx task has been
        #  initialized.
        self.initialized = False

        ## @var sampleRate
        #  This is the sample rate of the digital output.
        self._sampleRate = 100e3

        ## @var samplesPerChannel
        #  This is the number of samples per channel that will be generated in
        #  Finite mode.
        self._samplesPerChannel = 100

        ## @var clkSource
        #  This is the sample clock source terminal.  It can be set to an
        #  internal clock or external clock such as a PFI line i.e. "/PXI1Slot3/PFI15."
        self._clkSource = ''

        ## This is the mode of operation for the digital outputs.
        #
        #  There are currently three modes available.  Static mode is where one
        #  static digital sample is set with no need for a sample clock.
        #  Finite mode is where a finite number of digital samples will be set
        #  at a sample clock rate. Continuous mode is where a sequence of
        #  voltages are generated at a sample rate and then repeated until the
        #  stop() method is called.
        self.mode = dutil.Mode.Finite

        ## The number of time to iterate over a Finite number of samples.
        #
        #  This value is only useful in the "Finite" mode.  It is the number of
        #  times that a sequence of digital samples will be looped.  The default
        #  is allways 1.
        self.loops = 1
    
    ## Initialize the digital outputs based on the object's configuration.
    #  @param self The object pointer.
    #  @param physicalChannel A string representing the device and digital
    #  output channels. Example value: "PXI1Slot3/ao0:7"
    def init(self, physicalChannel):
        self.__createTask(physicalChannel)
        self.initialized = True
        
        #Finite Mode
        if self.mode == dutil.Mode.Finite:
            self.status = self.taskRef.SetWriteRegenMode(DAQmx_Val_AllowRegen)
            self.__configTiming(DAQmx_Val_FiniteSamps)
            
        #Continuous Mode
        if self.mode == dutil.Mode.Continuous:
            self.status = self.taskRef.SetWriteRegenMode(DAQmx_Val_AllowRegen)
            self.__configTiming(DAQmx_Val_ContSamps)
            
        #Static Mode
        if self.mode == dutil.Mode.Static:
            pass
        
    ## This function returns a random 1D numpy array of samples for writing the
    #  buffer of digital output channels.
    #  @param self The objet pointer.
    def createTestBuffer(self):
        data = numpy.random.rand(self._samplesPerChannel)
        data = numpy.ubyte(data * 255)
        return data
        
    ## This function returns the number of digital lines configured in the DAQmx
    #  Task.
    #  @param self The object pointer.
    def getNumLines(self):
        numLines = uInt32()
        #bufferSize = 255
        #channel = ctypes.create_string_buffer(bufferSize)
        #self.taskRef.GetTaskChannels(channel, bufferSize)
        #print channel.value
        self.taskRef.GetDONumLines('', ctypes.byref(numLines))
        return numLines.value
    
    ## This function returns the number of digital channels configured in the 
    #  DAQmx Task.
    #  @param self The object pointer.
    def getNumChannels(self):
        numChannels = uInt32()
        self.taskRef.GetTaskNumChans(ctypes.byref(numChannels))
        return numChannels.value
        
    ## This function writes the specified values into the buffer.
    #  @param self The object pointer.
    #  @param data This is a 1D 8-bit unsigned integer array that contians samples for
    #  each digital channel. Channels are non-interleaved (channel1 n-samples
    #  then channel2 n-samples).
    def writeToBuffer(self, data):
        autostart = self.mode == dutil.Mode.Static
        samplesWritten = int32()
        self.status = self.taskRef.WriteDigitalU8(self._samplesPerChannel,
                autostart, 10, DAQmx_Val_GroupByChannel, data,
                ctypes.byref(samplesWritten), None)
        #print 'Samples Written: ' + str(samplesWritten.value)
        return samplesWritten.value
    
    ## This function starts the digital output generation.
    #  @param self The object pointer.
    def start(self):
        self.status = self.taskRef.StartTask()
        
    ## This functions waits for the digital output generation to complete.
    #  @param self The object pointer.
    def waitUntilDone(self):
        sampPerChan = uInt64()
        self.status = self.taskRef.GetSampQuantSampPerChan(
                ctypes.byref(sampPerChan))
        #print 'DO Samples Per Channel: ' + str(sampPerChan.value)
        estAcqTime = (self.loops * sampPerChan.value) / self._sampleRate
        
        #print "Estimated Acquisition Time: " + str(estAcqTime)
        
        if self.mode != dutil.Mode.Static:
            self.status = self.taskRef.WaitUntilTaskDone(float64(estAcqTime + 0.1))
    
    ## This function stops the digital output generation.
    #  @param self The object pointer.
    def stop(self):
        self.status = self.taskRef.StopTask()
    
    ## This is a private method that creates the Task object for use inside the
    #  DigitalOutput class.
    def __createTask(self, physicalChannel):
        self.status = self.taskRef.CreateDOChan(physicalChannel, '',
                DAQmx_Val_ChanForAllLines)
        
    ## This is a private method that configures the timing for the DigitalOutput
    #  class.
    #  @param self The object pointer.
    def __configTiming(self, sampleMode):
        totalSamples = self._samplesPerChannel * self.loops
        self.taskRef.CfgSampClkTiming(self._clkSource, float64(self._sampleRate),
                                          DAQmx_Val_Falling, sampleMode,
                                          uInt64(totalSamples))
        
    ## This function will close connection to the digital ouput device and
    #  channels.
    #  @param self The object pointer.
    def close(self):
        """"""
        self.initialized = False
        self.status = self.taskRef.ClearTask()
        self.taskRef = Task()
        
    ## This is the destructor for the DigitalOutput Class.
    #  @param self The object pointer.
    def __del__(self):
        if self.initialized:
            self.close()
        
        del self.taskRef
        del self.status
        del self.initialized
        del self.sampleRate
        del self.samplesPerChannel
        del self.clkSource
        del self.mode
        del self.loops
Beispiel #9
0
 def setup_triggers(address="/Dev1/port0/line0:7"):
     task = Task()
     task.CreateDOChan("/Dev1/port0/line0:7", "",
                       PyDAQmx.DAQmx_Val_ChanForAllLines)
     task.StartTask()
     return task
Beispiel #10
0
class NI_6713Device():
    """
    This class is the interface to the NI driver for a NI PCI-6713 analog output card
    """
    def __init__(self, MAX_name, message_queue):
        """
        Initialise the driver and tasks using the given MAX name and message queue to communicate with this class

        Parameters
        ----------
        MAX_name : str
            the National Instrument MAX name used to identify the hardware card
        message_queue : JoinableQueue
            a message queue used to send instructions to this class
        """
        print("initialize device")
        self.NUM_AO = 8
        self.NUM_DO = 8
        self.MAX_name = MAX_name
        self.limits = [-10, 10]

        #Create AO Task
        self.ao_task = Task()
        self.ao_read = int32()
        self.ao_data = np.zeros((self.NUM_AO, ), dtype=np.float64)

        #Create DO Task
        self.do_task = Task()
        self.do_read = int32()
        self.do_data = np.zeros((self.NUM_DO, ), dtype=np.uint8)

        self.setup_static_channels()

        #DAQmx Start Code
        self.ao_task.StartTask()
        self.do_task.StartTask()

        self.wait_for_rerun = False

        self.running = True
        self.read_Thread = Thread(target=self.read_fun, args=(message_queue, ))

    def start(self):
        """
        Starts the message queue thread to read incoming instructions
        """
        self.read_Thread.start()

    def read_fun(self, message_queue):
        """
        Main method to read incoming instructions from the message queue
        """
        while self.running:
            try:
                #read an instruction from the message queue
                typ, msg = message_queue.get(timeout=0.5)
            except Queue.Empty:
                continue  #if there is no instruction in the queue, just read again until there is an instruction, or until the the the thread stops (running==False)

            # handle incoming instructions
            if typ == 'manual':
                # the msg argument contains the dict front_panel_values to send to the device
                self.program_manual(msg)
                message_queue.task_done(
                )  #signalise the sender, that the instruction is complete
            elif typ == 'trans to buff':
                #Transition to Buffered
                # msg is a dict containing all relevant arguments
                # If fresh is true, the hardware should be programmed with new commands, which were permitted
                # if fresh is false, use the last programmed harware commands again, so no hardware programming is needed at all
                if msg['fresh']:
                    self.transition_to_buffered(True, msg['clock_terminal'],
                                                msg['ao_channels'],
                                                msg['ao_data'])
                else:
                    self.transition_to_buffered(False, None, None, None)
                message_queue.task_done()  #signalize that the task is done
            elif typ == 'trans to man':
                #Transition to Manual
                self.transition_to_manual(msg['more_reps'], msg['abort'])
                message_queue.task_done()  # signalise that the task is done
            else:
                # an unknown/unimplemented instruction is requestet
                print("unkown message: " + msg)
                message_queue.task_done()
                continue

    def setup_static_channels(self):
        self.wait_for_rerun = False
        #setup AO channels
        for i in range(self.NUM_AO):
            self.ao_task.CreateAOVoltageChan(self.MAX_name + "/ao%d" % i, "",
                                             self.limits[0], self.limits[1],
                                             DAQmx_Val_Volts, None)
        #setup DO port(s)
        self.do_task.CreateDOChan(self.MAX_name + "/port0/line0:7", "",
                                  DAQmx_Val_ChanForAllLines)

    def shutdown(self):
        """
        Shutdown the device (stop & clear all tasks). Also stop the message queue thread
        """
        print("shutdown device")
        self.running = False
        self.ao_task.StopTask()
        self.ao_task.ClearTask()
        self.do_task.StopTask()
        self.do_task.ClearTask()

    def program_manual(self, front_panel_values):
        """
        Update the static output chanels with new values.

        This method transitions the device into manual mode (if it is still in rerun mode) and
        updates the output state of all channels

        Parameters
        ----------
        front_panel_values : dict {connection name : new state, ...}
            Containing the connection name and corresponding new output state
        """
        if self.wait_for_rerun:
            print("dont wait for rerun any more. setup static")
            self.ao_task.StopTask()
            self.ao_task.ClearTask()
            self.do_task.StopTask()
            self.do_task.ClearTask()
            self.ao_task = Task()
            self.do_task = Task()
            self.setup_static_channels()
            self.wait_for_rerun = False

        for i in range(self.NUM_AO):
            self.ao_data[i] = front_panel_values['ao%d' % i]
        self.ao_task.WriteAnalogF64(1, True, 1, DAQmx_Val_GroupByChannel,
                                    self.ao_data, byref(self.ao_read), None)

        for i in range(self.NUM_DO):
            self.do_data[i] = front_panel_values['do_%d' % i]
        self.do_task.WriteDigitalLines(1, True, 1, DAQmx_Val_GroupByChannel,
                                       self.do_data, byref(self.do_read), None)

    def transition_to_buffered(self, fresh, clock_terminal, ao_channels,
                               ao_data):
        """
        Transition the device to buffered mode

        This method does the hardware programming if needed

        Parameters
        ----------
        fresh : bool
            True if the device should be programmed with new instructions
            False if the old instructions should be executed again, so no programming is needed (just rerun last instructions)
        clock_terminal : str
            The device connection on which the clock signal is connected (e.g. 'PFI0')
        ao_channels : list str
            A list of all analog output channels that should be used 
        ao_data : 2d-numpy array, float64
            A 2d-array containing the instructions for each ao_channel for every clock tick
        """
        self.ao_task.StopTask(
        )  #Stop the last task (static mode or last buffered shot)
        if not fresh:
            if not self.wait_for_rerun:
                raise Exception("Cannot rerun Task.")
            self.ao_task.StartTask()  #just run old task again
            return
        elif not clock_terminal or not ao_channels or ao_data is None:
            raise Exception(
                "Cannot progam device. Some arguments are missing.")

        self.ao_task.ClearTask(
        )  #clear the last task and create a new one with new parameters & instructions
        self.ao_task = Task()

        self.ao_task.CreateAOVoltageChan(ao_channels, "", -10.0, 10.0,
                                         DAQmx_Val_Volts, None)
        self.ao_task.CfgSampClkTiming(clock_terminal, 1000000,
                                      DAQmx_Val_Rising, DAQmx_Val_FiniteSamps,
                                      ao_data.shape[0])
        self.ao_task.WriteAnalogF64(ao_data.shape[0], False, 10.0,
                                    DAQmx_Val_GroupByScanNumber, ao_data,
                                    self.ao_read, None)

        self.ao_task.StartTask()  #finally start the task

    def transition_to_manual(self, more_reps, abort):
        """
        Stop buffered mode
        """
        if abort:
            self.wait_for_rerun = False
            self.ao_task.ClearTask()
            self.do_task.StopTask()
            self.do_task.ClearTask()

            self.ao_task = Task()
            self.do_task = Task()

            self.setup_static_channels()
            self.ao_task.StartTask()
            self.do_task.StartTask()
        else:
            self.wait_for_rerun = True
Beispiel #11
0
""" Simple example of digital output

    This example outputs the values of data on line 0 to 7
"""

from PyDAQmx import Task
import numpy as np


data = np.array([0,1,1,0,1,0,1,0], dtype=np.uint8)

task = Task()
task.CreateDOChan("/TestDevice/port0/line0:7","",PyDAQmx.DAQmx_Val_ChanForAllLines)
task.StartTask()
task.WriteDigitalLines(1,1,10.0,PyDAQmx.DAQmx_Val_GroupByChannel,data,None,None)
task.StopTask()

Beispiel #12
0
class TriggerOutputDig(object):
    t = None
    wvfms = None
    Nsamps = None
    sampleRate = None
    pulseLength = None

    def __init__(self, port="Dev12/port0", startTrigChan="PFI0"):
        self.th = Task()  #Task.__init__(self)
        self.th.CreateDOChan(port, "", DAQmx_Val_ChanForAllLines)
        #self.setTiming(sampleRate, pulseLength)
        self.th.CfgDigEdgeStartTrig(startTrigChan, DAQmx_Val_Rising)
        self.th.SetStartTrigRetriggerable(True)
        #DAQmxLoadTask("WvfmOutputTask", byref(self.taskHandle))

    def start(self):
        self.th.StartTask()

    def stop(self):
        try:
            self.th.StopTask()
        except DAQError as e:
            if e.error == 200010:
                self.th.StopTask()
            else:
                raise (e)

    def setTiming(self, sampleRate, waveformLength):
        Nsamps = int(waveformLength * sampleRate)
        #self.th.CfgSampClkTiming("",sampleRate,DAQmx_Val_Rising,DAQmx_Val_ContSamps, 5*Nsamps);
        self.th.CfgSampClkTiming("", sampleRate, DAQmx_Val_Rising,
                                 DAQmx_Val_FiniteSamps, Nsamps)
        self.sampleRate = sampleRate
        self.Nsamps = Nsamps

    def reset(self):
        try:
            self.stop()
            sleep(0.2)
        except DAQError:
            pass
        finally:
            self.start()

    def setWaveforms(self, t, *wvfms):
        Npts = t.size
        sampleRate = 1. / (t[1] - t[0])
        totalLength = Npts / sampleRate
        self.setTiming(sampleRate, totalLength)

        wvfmPort = np.zeros(t.size, dtype='u4')
        for k, wv in enumerate(wvfms):
            wv[:] = np.where(wv, 1, 0)
            wv = wv.astype('u4')
            wvfmPort += (k + 1) * wv

        written = int32()
        self.th.WriteDigitalU32(1 * wvfmPort.size, 1, 10,
                                DAQmx_Val_GroupByChannel,
                                np.hstack(1 * [wvfmPort]).astype('u4'),
                                byref(written), None)
        self.t = t
        self.wvfms = wvfms
        return self.t, wvfmPort
Beispiel #13
0
class Device(metaclass=DeviceMeta):  #pylint: disable=too-many-instance-attributes
    """All Devices have to inherit from this Device parent class

    The Device class is responsible for port communication and acts as wrapper
    for the device metaclass that adds an 'open' and 'close' statement to
    every method and removes queue objects if there are any.

    Args:
        connection: The connection dictionary
        **kwargs: Additional keyword arguments (only termination at the moment)

    """
    def __init__(self, connection=None, **kwargs):  #pylint: disable=too-many-branches, too-many-statements

        # Add logger instance Utility function?
        self.log = logging.getLogger("MC.{0}".format(self.__class__.__name__))
        format_string = '%(asctime)s - %(levelname)s - %(name)s - %(message)s'
        formatter = logging.Formatter(format_string)
        console_handler = logging.StreamHandler()
        console_handler.setFormatter(formatter)
        self.log.addHandler(console_handler)
        self.log.setLevel(logging.DEBUG)

        self.storage = {}
        self.name = kwargs.get('name', None)

        # Store connection and termination
        if not connection:
            self._connection = {}
        else:
            self._connection = connection
        termination = self._connection.get("termination", '')
        self._read_termination = self._connection.get("read_termination",
                                                      termination)
        self._write_termination = self._connection.get("write_termination",
                                                       termination)

        system = platform.system()
        interface = self._connection.get("interface", "")

        # choose backend
        if (self._connection is None or 'port' not in self._connection
                or 'None' in self._connection['port']):
            self._backend = None
        elif 'GPIB' in self._connection['port'] and system == 'Windows':
            self._backend = 'pyVISA'
        elif 'GPIB' in self._connection['port'] and system != 'Windows':
            self._backend = 'linux-gpib'
        elif 'tcpip' in self._connection['port']:
            self._backend = 'pyVISA'
        elif ('COM' in self._connection['port']
              or 'tty' in self._connection['port']):
            self._backend = 'pySerial'
        elif 'line' in self._connection['port']:
            self._backend = 'pyDAQmx'
        elif 'file://' in self._connection['port']:
            self._backend = 'file'
        elif 'vxi11://' in self._connection['port']:
            self._backend = 'vxi11'
        elif 'usbtmcWR://' in self._connection['port']:
            self._backend = 'usbtmcWR'
        elif 'usbtmc://' in self._connection['port']:
            self._backend = 'usbtmcWR'
        elif 'modbus://' in self._connection['port']:
            self._backend = 'modbus'
        else:
            error_string = "No correct backend found for {0}. Check configs!"\
                           .format(type(self).__name__)
            self.log.error(error_string)
            self._backend = None

        # override by `interface` config parameter
        if interface in ('pyVISA', 'linux-gpib', 'pySerial', 'pyDAQmx', 'file',
                         'vxi11', 'usbtmcWR', 'usbtmc', 'modbus'):
            self._backend = interface

        # create port objects, if needed for the chosen backend
        if self._backend == 'pySerial':
            self._port = \
                serial.Serial( \
            port=self._connection['port'],
            baudrate=self._connection.get('baudrate', 9600),
            bytesize=SERIAL_BYTESIZE[self._connection.get('bytesize', 8)],
            parity=SERIAL_PARITY[self._connection.get('parity', 'none')],
            stopbits=SERIAL_STOPBITS[self._connection.get('stopbits', 1)],
            timeout=self._connection.get('timeout', 5),
            xonxoff=self._connection.get('xonxoff', False),
            rtscts=self._connection.get('rtscts', False),
            dsrdtr=self._connection.get('dsrdts', False),
            write_timeout=self._connection.get('write_timeout', None),
            inter_byte_timeout=self._connection.get('inter_byte_timeout', None)
            ) # pylint: disable=C0330
            self._port.close()
        elif self._backend == 'file':
            self._port = self._connection['port'].replace('file://', '')
        elif self._backend == 'usbtmcWR':
            port = self._connection['port'].replace('usbtmcWR://',
                                                    '').split(':')
            self._port = usbtmc.Instrument(int(port[0], 16), int(port[1], 16))
        elif self._backend == 'usbtmc':
            port = self._connection['port'].replace('usbtmc://', '').split(':')
            self._port = usbtmc.Instrument(self._connection['port'][0],
                                           self._connection['port'][1])
        elif self._backend == 'modbus':
            port = self._connection['port'].replace('modbus://', '')
            pport = None
            if ":" in port:
                pport = int(port.split(":")[1])
                port = port.split(":")[0]
            auto_open = self._connection.get('auto_open', True)
            self._port = mtcp_client.ModbusClient(host=port,
                                                  port=pport,
                                                  auto_open=auto_open)

    def _open(self):
        if self._backend == 'pyVISA':
            self._port = visa.ResourceManager() \
                            .open_resource(self._connection['port'],
                                           write_termination=
                                           self._write_termination,
                                           read_termination=
                                           self._read_termination)
            # timeout has to be in ms
            self._port.timeout = float(self._connection.get('timeout',
                                                            5)) * 1000
        elif self._backend == 'linux-gpib':
            con = re.findall(r'\d+', self._connection['port'])
            self._port = gpib.dev(int(con[0]), int(con[1]))
        elif self._backend == 'pySerial':
            if not self._port.is_open:
                self._port.open()
        elif self._backend == 'pyDAQmx':
            pass
        elif self._backend in ['usbtmc', 'usbtmcWR']:
            self._port.open()
        elif self._backend == "file":
            file = Path(self._port)
            if not file.is_file():
                self.log.warning("Path ist not a file! Check your port settings in config file!")  #pylint: disable=line-too-long
        elif self._backend == "vxi11":
            self._port = vxi11.Instrument(self._connection["port"].replace(
                "vxi11://", ""))
        elif self._backend == "modbus":
            self._port.open()

    def _close(self):
        if self._backend == 'pyVISA':
            del self._port
        elif self._backend == 'linux-gpib':
            gpib.close(self._port)
        elif self._backend == 'pySerial':
            self._port.close()
        elif self._backend == 'pyDAQmx':
            pass
        elif self._backend in ['usbtmc', 'usbtmcWR']:
            self._port.close()
        elif self._backend == 'vxi11':
            self._port.close()
        elif self._backend == 'modbus':
            self._port.close()

    def write(self, command, keep_open=False, **kwargs):
        """Format string and send it to the device

        Depending on the connection the command string can be send directly
        to the device or has to formatted first.

        Args:
            command (str): Command that is send to the device
            keep_open (bool, optional): Keep port open at the end

        """
        if self._backend == "usbtmcWR":
            keep_open = True
        self._open()

        if self._backend == 'pySerial':
            if not self._port.is_open:
                self._open()
            self._port.write(
                bytes('{0}{1}'.format(command, self._write_termination),
                      'utf-8'))
        elif self._backend == 'pyVISA':
            self._port.write(command)
        elif self._backend == 'linux-gpib':
            gpib.write(self._port, command)
        elif self._backend == 'pyDAQmx':
            self._port = Task()
            self._port.CreateDOChan(self._connection['port'], "",
                                    DAQmx_Val_ChanForAllLines)
            self._port.StartTask()
            self._port.WriteDigitalLines(1, 1, 10.0, DAQmx_Val_GroupByChannel,
                                         command, None, None)
            self._port.StopTask()
        elif self._backend == 'file':
            try:
                with open(self._port, "w") as writer:
                    writer.write(str(command))
            except OSError as error_object:
                self.log.warning(error_object)
        elif self._backend in ['usbtmc', 'usbtmcWR']:
            self._port.write('{0}{1}'.format(command, self._write_termination))
        elif self._backend == 'vxi11':
            self._port.write(command)
        elif self._backend == 'modbus':
            modbus_write(self._port, command, **kwargs)
        if not keep_open:
            self._close()

    def read(self, command=None, keep_open=False, decode="utf-8", **kwargs):  #pylint: disable=R0912
        """Read the device and return the formatted value

        Depending on the connection the return value has to be formatted and
        and trailing termination characters removed.

        Args:
            command (:obj:`string`, optional): Command that initializes read
        """
        if self._backend == 'usbtmcWR':
            keep_open = True
        if command is not None:
            self.write(command, keep_open=True)
        else:
            self._open()

        response = None
        if self._backend == 'pySerial':
            response = self._read_serialdata(decode)
        elif self._backend == 'pyVISA':
            if decode is None:
                response = self._port.read_binary_values()
            else:
                response = self._port.read()
        elif self._backend == 'linux-gpib':
            response = gpib.read(self._port, kwargs.get("numbytes", 1024))
            if decode is not None:
                response = response.decode(decode)
        elif self._backend == 'file':
            try:
                with open(self._port) as file:
                    response = file.read()
            except OSError as error_object:
                self.log.error(error_object)
                response = -1
        elif self._backend in ['usbtmc', 'usbtmcWR']:
            if decode is None:
                response = self._port.read_raw()
            else:
                response = self._port.read()
        elif self._backend == 'vxi11':
            if decode is None:
                response = self._port.read_raw()
            else:
                response = self._port.read()
        elif self._backend == 'modbus':
            response = modbus_read(self._port, command, **kwargs)
        if not keep_open:
            self._close()

        return response

    def _read_serialdata(self, decode="utf-8"):
        buffer = bytearray()
        while True:
            one_byte = self._port.read(1)
            buffer.extend(one_byte)
            if bytearray(self._read_termination, "utf-8") in buffer:
                if decode is not None:
                    return bytes(buffer).decode(decode).strip(
                        self._read_termination)
                return bytes(buffer)
            if not one_byte:
                self.log.warning("Serial timeout")
                self._close()
                return None

    def identifier(self):  #pylint: disable=R0201
        """ Returns the identfier of the device

        This method is only a placeholder and should be implemented in every
        subclass.
        """
        return True
Beispiel #14
0
class NI_DIODevice():
    """
    This class is the interface to the NI driver for a NI PCI-DIO-32HS digital output card
    """
    def __init__(self, MAX_name, message_queue):
        """
        Initialise the driver and tasks using the given MAX name and message queue to communicate with this class

        Parameters
        ----------
        MAX_name : str
            the National Instrument MAX name used to identify the hardware card
        message_queue : JoinableQueue
            a message queue used to send instructions to this class
        """
        print("initialize device")
        self.NUM_DO = 32
        self.MAX_name = MAX_name

        #Create DO Task
        self.do_task = Task()
        self.do_read = int32()
        self.do_data = np.zeros((self.NUM_DO, ), dtype=np.uint8)

        self.setup_static_channels()

        #DAQmx Start Code
        self.do_task.StartTask()

        self.wait_for_rerun = False

        self.running = True
        self.read_Thread = Thread(target=self.read_fun, args=(message_queue, ))

    def start(self):
        """
        Starts the message queue thread to read incoming instructions
        """
        self.read_Thread.start()

    def read_fun(self, message_queue):
        """
        Main method to read incoming instructions from the message queue
        """
        while self.running:
            try:
                typ, msg = message_queue.get(timeout=0.5)
            except Queue.Empty:
                continue

            if typ == 'manual':
                self.program_manual(msg)
                message_queue.task_done()
            elif typ == 'trans to buff':
                #Transition to Buffered
                if msg['fresh']:
                    self.transition_to_buffered(True, msg['clock_terminal'],
                                                msg['do_channels'],
                                                msg['do_data'])
                else:
                    self.transition_to_buffered(False, None, None, None)
                message_queue.task_done()  #signalize that the task is done
            elif typ == 'trans to man':
                #Transition to Manual
                self.transition_to_manual(msg['more_reps'], msg['abort'])
                message_queue.task_done()
            else:
                print("unkown message: " + msg)
                message_queue.task_done()
                continue

    def setup_static_channels(self):
        #setup DO port(s)
        self.do_task.CreateDOChan(
            self.MAX_name + "/port0/line0:7," + self.MAX_name +
            "/port1/line0:7," + self.MAX_name + "/port2/line0:7," +
            self.MAX_name + "/port3/line0:7", "", DAQmx_Val_ChanForAllLines)

    def shutdown(self):
        """
        Shutdown the device (stop & clear all tasks). Also stop the message queue thread
        """
        print("shutdown device")
        self.running = False
        self.do_task.StopTask()
        self.do_task.ClearTask()

    def program_manual(self, front_panel_values):
        """
        Update the static output chanels with new values.

        This method transitions the device into manual mode (if it is still in rerun mode) and
        updates the output state of all channels

        Parameters
        ----------
        front_panel_values : dict {connection name : new state, ...}
            Containing the connection name and corresponding new output state
        """
        if self.wait_for_rerun:
            print("dont wait for rerun any more. setup static")
            self.do_task.StopTask()
            self.do_task.ClearTask()
            self.do_task = Task()
            self.setup_static_channels()
            self.wait_for_rerun = False

        for port in range(4):
            for line in range(8):
                self.do_data[port * 8 +
                             line] = front_panel_values['port%d/line%d' %
                                                        (port, line)]

        self.do_task.WriteDigitalLines(1, True, 1, DAQmx_Val_GroupByChannel,
                                       self.do_data, byref(self.do_read), None)

    def transition_to_buffered(self, fresh, clock_terminal, do_channels,
                               do_data):
        """
        Transition the device to buffered mode

        This method does the hardware programming if needed

        Parameters
        ----------
        fresh : bool
            True if the device should be programmed with new instructions
            False if the old instructions should be executed again, so no programming is needed (just rerun last instructions)
        clock_terminal : str
            The device connection on which the clock signal is connected (e.g. 'PFI2')
        ao_channels : list str
            A list of all analog output channels that should be used 
        ao_data : 2d-numpy array, uint8
            A 2d-array containing the instructions for each ao_channel for every clock tick
        """
        self.do_task.StopTask()
        if not fresh:
            if not self.wait_for_rerun:
                raise Exception("Cannot rerun Task.")
            self.do_task.StartTask()  #just run old task again
            return
        elif not clock_terminal or not do_channels or do_data is None:
            raise Exception(
                "Cannot progam device. Some arguments are missing.")

        self.do_task.ClearTask()
        self.do_task = Task()

        self.do_task.CreateDOChan(do_channels, "", DAQmx_Val_ChanPerLine)
        self.do_task.CfgSampClkTiming(clock_terminal, 10000000,
                                      DAQmx_Val_Rising, DAQmx_Val_FiniteSamps,
                                      do_data.shape[0])
        self.do_task.WriteDigitalLines(do_data.shape[0], False, 10.0,
                                       DAQmx_Val_GroupByScanNumber, do_data,
                                       self.do_read, None)

        #print("Wrote "+str(self.do_read)+" samples to the buffer")

        self.do_task.StartTask()

    def transition_to_manual(self, more_reps, abort):
        """
        Stop buffered mode
        """
        if abort:
            self.wait_for_rerun = False
            self.do_task.ClearTask()
            self.do_task = Task()

            self.setup_static_channels()
            self.do_task.StartTask()
        else:
            self.wait_for_rerun = True
Beispiel #15
0
class DigitalOutput(object):
    #-- Sample Rate Property
    ## This function returns the sample rate configured in the DAQmx Task.
    #  @param self The object reference.
    def getSampleRate(self):
        if self.initialized:
            sampleRate = float64()
            self.status = self.taskRef.GetSampClkRate(ctypes.byref(sampleRate))
            self._sampleRate = sampleRate.value
        return self._sampleRate

    ## This function sets the sample rate in the DAQmx Task.
    #  @param self The object reference.
    #  @param value The value to set the sample rate.
    def setSampleRate(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampClkRate(float64(value))
        self._sampleRate = value

    ## This function deletes the sample rate variable inside the
    #  DigitalOutput object.
    #  @param self The object reference.
    def _delSampleRate(self):
        del self._sampleRate

    sampleRate = property(getSampleRate,
                          setSampleRate,
                          _delSampleRate,
                          doc="""The sample rate of the digital output.""")

    #-- Samples Per Channel Property

    ## This function returns the samples per channel configured in the
    #  DAQmx Task.
    #  @param self The object reference.
    def getSamplesPerChannel(self):
        if self.initialized:
            samplesPerChannel = uInt64()
            self.status = self.taskRef.GetSampQuantSampPerChan(
                ctypes.byref(samplesPerChannel))
            self._samplesPerChannel = samplesPerChannel.value
        return self._samplesPerChannel

    ## This function sets the samples per channel in the DAQmx Task.
    #  @param self The object reference.
    #  @param value The value to set the samples per channel.
    def setSamplesPerChannel(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampQuantSampPerChan(uInt64(value))
        self._samplesPerChannel = value

    ## This function deletes the samplesPerChannel variable from the
    #  DigitalOutput object.
    #  @param self The object reference.
    def _delSamplesPerChannel(self):
        del self._samplesPerChannel

    samplesPerChannel = property(
        getSamplesPerChannel, setSamplesPerChannel, _delSamplesPerChannel,
        """The samples per channel of the digital output.""")

    #-- Clock Source Property

    ## This function returns the sample clock source configured in the
    #  DAQmx Task.
    #  @param self The object reference.
    def getClkSource(self):
        if self.initialized:
            buffSize = uInt32(255)
            buff = ctypes.create_string_buffer(buffSize.value)
            self.status = self.taskRef.GetSampClkSrc(buff, buffSize)
            self._clkSource = buff.value
        return self._clkSource

    ## This function sets the sample clock source in the DAQmx Task.
    #  @param self The object reference.
    #  @param value The value to set the clock source.
    def setClkSource(self, value):
        if self.initialized:
            self.status = self.taskRef.SetSampClkSrc(value)
            value = self.getClkSource()
        self._clkSource = value

    ## This function deletes the clkSource variable within the
    #  DigitalOutput
    #  object.
    #  @param self The object reference.
    def _delClkSource(self):
        del self._clkSource

    clkSource = property(
        getClkSource, setClkSource, _delClkSource,
        """The clock source for the digital outputsample clock.""")

    #-- Start Trigger Property
    ## This function returns the start trigger source configured in the
    #  DAQmx Task.
    #  @param self The object reference.
    def _getStartTriggerSource(self):
        if self.initialized:
            buffSize = uInt32(255)
            buff = ctypes.create_string_buffer(buffSize.value)
            self.status = self.taskRef.GetDigEdgeStartTrigSrc(buff, buffSize)
            self._startTriggerSource = buff.value
        return self._startTriggerSource

    ## This function sets the start trigger source in the DAQmx Task.
    #  @param self The object reference.
    #  @param value The value to set the start trigger.
    def _setStartTriggerSource(self, value):
        if self.initialized:
            self.status = self.taskRef.SetDigEdgeStartTrigSrc(value)
            value = self.getStartTriggerSource()
        self._startTriggerSource = value

    startTriggerSource = property(_getStartTriggerSource,
                                  _setStartTriggerSource)

    #-- Done Property
    def _getDone(self):
        done = bool32()
        if self.initialized:
            self.status = self.taskRef.GetTaskComplete(ctypes.byref(done))
        else:
            done.value = 1
        return bool(done.value)

    ## @var done
    #  Returns the task done status.
    #
    #  This mode works differently depending on the mode. <br />
    #  <ul>
    #  <li><B>Static and Continuous</B>: done is false after a start
    #  method and true</li>
    #  only after a stop method.
    #  <li><B>Finite</B>: done is false until all samples are
    #  generated.</li></ul>
    done = property(_getDone)

    #-- Pause Trigger Source
    def _getPauseTriggerSource(self):
        if self.initialized:
            buffSize = uInt32(255)
            buff = ctypes.create_string_buffer(buffSize.value)
            self.status = self.taskRef.GetDigLvlPauseTrigSrc(buff, buffSize)
            self._pauseTriggerSource = buff.value
        return self._pauseTriggerSource

    def _setPauseTriggerSource(self, value):
        if self.initialized:
            if value == '':
                self.status = self.taskRef.SetPauseTrigType(DAQmx_Val_None)
                self.status = self.taskRef.ResetDigLvlPauseTrigSrc()
            else:
                self.status = self.taskRef.SetDigLvlPauseTrigWhen(
                    DAQmx_Val_High)
                self.status = self.taskRef.SetPauseTrigType(DAQmx_Val_DigLvl)
                self.status = self.taskRef.SetDigLvlPauseTrigSrc(value)
        self._pauseTriggerSource = value

    pauseTriggerSource = property(_getPauseTriggerSource,
                                  _setPauseTriggerSource)

    #-------------------- Functions --------------------

    ## This function is a constructor for the DigitalOutput class.
    #
    #  It creates the internal variables required to perform functions
    #  within the class. This function does not initialize any hardware.
    #  @param self This object reference
    def __init__(self):
        ## The DAQmx task reference.
        self.taskRef = Task()

        ## This is the status of the DAQmx task.
        #
        #  A value greater than 0 means that an error has occurred. When
        #  the status is greater than 0 an error should be reported by
        #  the class.
        self.status = int32()

        ## This is a boolean that is true when the DAQmx task has been
        #  initialized.
        self.initialized = False

        ## @var sampleRate
        #  This is the sample rate of the digital output.
        self._sampleRate = 100e3

        ## @var samplesPerChannel
        #  This is the number of samples per channel that will be generated
        #  in Finite mode.
        self._samplesPerChannel = 100

        ## @var clkSource
        #  This is the sample clock source terminal.  It can be set to an
        #  internal clock or external clock such as a PFI line i.e.
        #  "/PXI1Slot3/PFI15."
        self._clkSource = ''

        ## @var startTriggerSource
        #  This is the start trigger source terminal.  It can be set to
        #  a PFI line such as "/PXISlot3/PFI0"
        self._startTriggerSource = ''

        ## @var pauseTriggerSource
        #  The source terminal of the pause trigger.  This can be
        #  any PFI or backplane trigger such as 'PFI5' and 'PXI_TRIG5'
        self._pauseTriggerSource = ''

        ## This is the mode of operation for the digital outputs.
        #
        #  There are currently three modes available.  Static mode is
        #  where one static digital sample is set with no need for a
        #  sample clock.  Finite mode is where a finite number of digital
        #  samples will be set at a sample clock rate. Continuous mode is
        #  where a sequence of voltages are generated at a sample rate and
        #  then repeated until the stop() method is called.
        self.mode = dutil.Mode.Finite

        self.triggerType = dutil.TriggerType.Software

        ## The number of time to iterate over a Finite number of samples.
        #
        #  This value is only useful in the "Finite" mode.  It is the
        #  number of times that a sequence of digital samples will be
        #  looped.  The default is allways 1.
        self.loops = 1

        ##  The time in seconds to wait for a trigger or for a digital
        #   state change.
        self.timeout = 1

        self.timeoutPad = 10

        self._pCh = ''

    ## Initialize the digital outputs based on the object's configuration.
    #  @param self The object reference.
    #  @param physicalChannel A string representing the device and digital
    #  output channels. Example value: "PXI1Slot3/ao0:7"
    def init(self, physicalChannel):
        self._pCh = physicalChannel
        self.__createTask(physicalChannel)
        self.initialized = True

        #Finite Mode
        if self.mode == dutil.Mode.Finite:
            self.status = self.taskRef.SetWriteRegenMode(DAQmx_Val_AllowRegen)
            self.__configTiming(DAQmx_Val_FiniteSamps)

        #Continuous Mode
        if self.mode == dutil.Mode.Continuous:
            self.status = self.taskRef.SetWriteRegenMode(DAQmx_Val_AllowRegen)
            self.__configTiming(DAQmx_Val_ContSamps)

        #Static Mode
        if self.mode == dutil.Mode.Static:
            pass

    ## This function returns a random 1D numpy array of samples for
    #  writing the buffer of digital output channels.
    #  @param self The objet reference.
    def createTestBuffer(self):
        import numpy
        data = numpy.random.rand(self._samplesPerChannel)
        data = numpy.ubyte(data * 255)
        return data

    ## This function returns the number of digital lines configured in
    #  the DAQmx Task.
    #  @param self The object reference.
    def getNumLines(self):
        numLines = uInt32()
        #bufferSize = 255
        #channel = ctypes.create_string_buffer(bufferSize)
        #self.taskRef.GetTaskChannels(channel, bufferSize)
        #print channel.value
        self.taskRef.GetDONumLines('', ctypes.byref(numLines))
        return numLines.value

    ## This function returns the number of digital channels configured in
    #  the DAQmx Task.
    #  @param self The object reference.
    def getNumChannels(self):
        numChannels = uInt32()
        self.taskRef.GetTaskNumChans(ctypes.byref(numChannels))
        return numChannels.value

    ## This function writes the specified values into the buffer.
    #  @param self The object reference.
    #  @param data This is a 1D 8-bit unsigned integer array that
    #  contians samples for each digital channel. Channels are
    #  non-interleaved (channel1 n-samples then channel2 n-samples).
    def writeToBuffer(self, data):
        autostart = self.mode == dutil.Mode.Static
        samplesWritten = int32()
        self.buff = data
        self.status = self.taskRef.WriteDigitalU8(self._samplesPerChannel,
                                                  autostart, 10,
                                                  DAQmx_Val_GroupByChannel,
                                                  data,
                                                  ctypes.byref(samplesWritten),
                                                  None)
        #print 'Samples Written: ' + str(samplesWritten.value)
        return samplesWritten.value

    ## This function writes a static value to the digital line(s)
    #  configured in the init() method.
    #  @param self The object reference.
    #  @param data The static value to send to the digital line(s).
    def writeStatic(self, data):
        if isinstance(data, bool) and data == True:
            digLineNum = int(self._pCh[len(self._pCh) - 1])
            data = 2**digLineNum
        autostart = True
        self.status = self.taskRef.WriteDigitalScalarU32(
            autostart, float64(self.timeout), uInt32(data), None)

    ## This function starts the digital output generation.
    #  @param self The object reference.
    def start(self):
        self.status = self.taskRef.StartTask()

    ## This functions waits for the digital output generation to complete.
    #  @param self The object reference.
    def waitUntilDone(self):
        sampPerChan = uInt64()
        self.status = self.taskRef.GetSampQuantSampPerChan(
            ctypes.byref(sampPerChan))
        #print 'DO Samples Per Channel: ' + str(sampPerChan.value)
        estAcqTime = (self.loops * sampPerChan.value) / self._sampleRate

        #print "Estimated Acquisition Time: " + str(estAcqTime)

        if self.mode != dutil.Mode.Static:
            self.status = self.taskRef.WaitUntilTaskDone(
                float64(estAcqTime + self.timeoutPad))

    ## This function stops the digital output generation.
    #  @param self The object reference.
    def stop(self):
        self.status = self.taskRef.StopTask()

    ## This is a private method that creates the Task object for use
    #  inside the DigitalOutput class.
    def __createTask(self, physicalChannel):
        self.status = self.taskRef.CreateDOChan(physicalChannel, '',
                                                DAQmx_Val_ChanForAllLines)

    ## This is a private method that configures the timing for the
    #  DigitalOutput class.
    #  @param self The object reference.
    def __configTiming(self, sampleMode):
        totalSamples = self._samplesPerChannel * self.loops
        if self.triggerType == dutil.TriggerType.Software:
            self.status = self.taskRef.CfgSampClkTiming(
                self._clkSource, float64(self._sampleRate), DAQmx_Val_Falling,
                sampleMode, uInt64(totalSamples))
        elif self.triggerType == dutil.TriggerType.Hardware:
            self.status = self.taskRef.CfgSampClkTiming(
                self._clkSource, float64(self._sampleRate), DAQmx_Val_Falling,
                sampleMode, uInt64(totalSamples))
            self.status = self.taskRef.CfgDigEdgeStartTrig(
                self._startTriggerSource, DAQmx_Val_Falling)

    ## This function will close connection to the digital ouput device and
    #  channels.
    #  @param self The object reference.
    def close(self):
        self.initialized = False
        self.status = self.taskRef.ClearTask()
        self.taskRef = Task()

    ## This is the destructor for the DigitalOutput Class.
    #  @param self The object reference.
    def __del__(self):
        if self.initialized:
            self.close()

        del self.taskRef
        del self.status
        del self.initialized
        del self.sampleRate
        del self.samplesPerChannel
        del self.clkSource
        del self.mode
        del self.loops
        del self.timeout
        del self.timeoutPad
        del self._pCh