/// <summary>
        /// This function creates channels in the given task, for the named device.
        /// </summary>
        /// <param name="deviceName">
        /// The name of the device which the channels will will be created for. Eg Dev1
        /// </param>
        /// <param name="usedDigitalChannels"></param>
        /// A dictionary, indexed by logical ID#, of all of the digital hardware channels parsed from parsed from settings data which
        /// reside on this server.
        /// <param name="usedAnalogChannels">
        /// A dictionary, indexed by logical ID#, of all of the analog hardware channels parsed from the settings data, which reside on this server.
        /// </param>
        /// <param name="task">The task for which channels will be created.</param>
        /// <param name="analogIDs">
        /// An out parameter, which will store a list of the analog IDs that were created, in the order created.
        /// </param>
        /// <param name="analogs">
        /// An out parameter, which will store a list of the analog hardware channels corresponding to the channels created, in the order created.
        /// </param>
        /// <param name="port_digital_IDs"></param>
        /// An out parameter, which will store a dictionary mapping a digital port number to an 8-length array of integers specifying the logical ID# of each bit of the
        /// given port #. Entries of -1 in this array indicate bits that have no logical ID# assigned. See **** in this method's source code for further information.
        /// <param name="usedPortNumbers">
        /// An out parameter, which will store a list of integers corresponding to the digital port numbers that were used on this device, in the order created.
        /// </param>
        private static void parseAndCreateChannels(string deviceName, DeviceSettings deviceSettings, Dictionary <int, HardwareChannel> usedDigitalChannels, Dictionary <int, HardwareChannel> usedAnalogChannels, Task task, out List <int> analogIDs, out List <HardwareChannel> analogs, out Dictionary <int, int[]> port_digital_IDs, out List <int> usedPortNumbers)
        {
            // figure out which of the analog and digital channels belong on this device. Add them here and index by
            // logical ID#
            Dictionary <int, HardwareChannel> analogsUnsorted  = getChannelsOnDevice(usedAnalogChannels, deviceName);
            Dictionary <int, HardwareChannel> digitalsUnsorted = getChannelsOnDevice(usedDigitalChannels, deviceName);


            // sort the lists by ID
            analogIDs = new List <int>();
            analogs   = new List <HardwareChannel>();
            sortDicionaryByID(analogIDs, analogs, analogsUnsorted);

            // list of the digital IDs of channels on this device
            List <int> digitalIDs = new List <int>();
            // list of the corresponding hardware channels
            List <HardwareChannel> digitals = new List <HardwareChannel>();

            sortDicionaryByID(digitalIDs, digitals, digitalsUnsorted);

            // ****
            // description of port_digital_IDs:
            // mapping from port number to an array of integers. The nth element of the array gives the digital ID
            // of the channel which is connected to the nth line of that port. If there is no channel on that port,
            // the ID is -1.

            if (deviceSettings.DeviceDescription.Contains("6533"))
            {
                groupDigitalChannels(digitalIDs, digitals, out port_digital_IDs, out usedPortNumbers, true);
            }
            else
            {
                groupDigitalChannels(digitalIDs, digitals, out port_digital_IDs, out usedPortNumbers, false);
            }

            //ok! create the channels.

            // analog first
            for (int i = 0; i < analogs.Count; i++)
            {
                task.AOChannels.CreateVoltageChannel(analogs[i].physicalChannelName(), "", -10, 10, AOVoltageUnits.Volts);
            }

            // now digital
            for (int i = 0; i < usedPortNumbers.Count; i++)
            {
                int portNum = usedPortNumbers[i];
                task.DOChannels.CreateChannel(deviceName + '/' + HardwareChannel.digitalPhysicalChannelName(portNum), "", ChannelLineGrouping.OneChannelForAllLines);
            }
        }
        public FpgaTimebaseTask(DeviceSettings deviceSettings, okCFrontPanel opalKellyDevice, SequenceData sequence, double masterClockPeriod, out int nSegments, bool useRfModulation, bool assymetric)
            : base()
        {
            com.opalkelly.frontpanel.okCFrontPanel.ErrorCode errorCode;

            this.opalKellyDevice = opalKellyDevice;

            this.masterClockPeriod = masterClockPeriod;

            TimestepTimebaseSegmentCollection segments = sequence.generateVariableTimebaseSegments(SequenceData.VariableTimebaseTypes.AnalogGroupControlledVariableFrequencyClock,
                                                                                                   masterClockPeriod);

            this.max_elapsedtime_ms = (UInt32)((sequence.SequenceDuration * 1000.0) + 100);

            byte[] data = FpgaTimebaseTask.createByteArray(segments, sequence, out nSegments, masterClockPeriod, assymetric);

            // Send the device an abort trigger.
            errorCode = opalKellyDevice.ActivateTriggerIn(0x40, 1);
            if (errorCode != okCFrontPanel.ErrorCode.NoError)
            {
                throw new Exception("Unable to set abort trigger to FPGA device. Error code " + errorCode.ToString());
            }

            UInt16 wireInValue = 0;

            if (deviceSettings.StartTriggerType != DeviceSettings.TriggerType.SoftwareTrigger)
            {
                wireInValue += 1;
            }

            if (useRfModulation)
            {
                wireInValue += 2;
            }

            setWireInValue(0x00, wireInValue);

            setWireInValue(0x01, deviceSettings.RetriggerDebounceSamples);

            opalKellyDevice.UpdateWireIns();

            // pipe the byte stream to the device
            int xfered = opalKellyDevice.WriteToPipeIn(0x80, data.Length, data);

            if (xfered != data.Length)
            {
                throw new Exception("Error when piping clock data to FPGA device. Sent " + xfered + " bytes instead of " + data.Length + "bytes.");
            }
        }
        public FpgaTimebaseTask(DeviceSettings deviceSettings, okCFrontPanel opalKellyDevice, SequenceData sequence, double masterClockPeriod, out int nSegments, bool useRfModulation, bool assymetric)
            : base()
        {
            com.opalkelly.frontpanel.okCFrontPanel.ErrorCode errorCode;

            this.opalKellyDevice = opalKellyDevice;

            this.masterClockPeriod = masterClockPeriod;

            TimestepTimebaseSegmentCollection segments = sequence.generateVariableTimebaseSegments(SequenceData.VariableTimebaseTypes.AnalogGroupControlledVariableFrequencyClock,
                                                        masterClockPeriod);

            this.max_elapsedtime_ms = (UInt32)((sequence.SequenceDuration * 1000.0) + 100);

            byte[] data = FpgaTimebaseTask.createByteArray(segments, sequence, out nSegments, masterClockPeriod, assymetric );

            // Send the device an abort trigger.
            errorCode = opalKellyDevice.ActivateTriggerIn(0x40, 1);
            if (errorCode != okCFrontPanel.ErrorCode.NoError)
            {
                throw new Exception("Unable to set abort trigger to FPGA device. Error code " + errorCode.ToString());
            }

            UInt16 wireInValue = 0;
            if (deviceSettings.StartTriggerType != DeviceSettings.TriggerType.SoftwareTrigger)
            {
                wireInValue += 1;
            }

            if (useRfModulation)
            {
                wireInValue += 2;
            }

            setWireInValue(0x00, wireInValue);

            setWireInValue(0x01, deviceSettings.RetriggerDebounceSamples);

            opalKellyDevice.UpdateWireIns();

            // pipe the byte stream to the device
            int xfered = opalKellyDevice.WriteToPipeIn(0x80, data.Length, data);
            if (xfered != data.Length)
            {
                throw new Exception("Error when piping clock data to FPGA device. Sent " + xfered + " bytes instead of " + data.Length + "bytes.");
            }
        }
        /// <summary>
        /// This method creates analog and digital output buffers for daqMx cards. Note that the daqmx library seems to only support
        /// either analog OR digital on a given card at one time. Despite the fact that this method will create both types of buffers,
        /// it will probably throw some daqMX level exceptions if asked to create both analog and digital buffers for the same device.
        /// </summary>
        /// <param name="deviceName"></param>
        /// <param name="deviceSettings"></param>
        /// <param name="sequence"></param>
        /// <param name="settings"></param>
        /// <param name="usedDigitalChannels">digital channels which reside on this server.</param>
        /// <param name="usedAnalogChannels">analog channels which reside on this server</param>
        /// <returns></returns>
        public static Task createDaqMxTask(string deviceName, DeviceSettings deviceSettings, SequenceData sequence, 
            SettingsData settings, Dictionary<int, HardwareChannel> usedDigitalChannels, Dictionary<int, HardwareChannel> usedAnalogChannels,
            ServerSettings serverSettings, out long expectedSamplesGenerated)
        {
            expectedSamplesGenerated = 0;

            Task task = new Task(deviceName + " output task");

            List<int> analogIDs;
            List<HardwareChannel> analogs;
            Dictionary<int, int[]> port_digital_IDs;
            List<int> usedPortNumbers;

            // Parse and create channels.
            parseAndCreateChannels(deviceName,deviceSettings, usedDigitalChannels, usedAnalogChannels, task, out analogIDs, out analogs, out port_digital_IDs, out usedPortNumbers);

            if (analogIDs.Count != 0)
            {
                if (deviceSettings.UseCustomAnalogTransferSettings)
                {
                    task.AOChannels.All.DataTransferMechanism = deviceSettings.AnalogDataTransferMechanism;
                    task.AOChannels.All.DataTransferRequestCondition = deviceSettings.AnalogDataTransferCondition;
                }
            }
            if (usedPortNumbers.Count != 0)
            {
                if (deviceSettings.UseCustomDigitalTransferSettings)
                {
                    task.DOChannels.All.DataTransferMechanism = deviceSettings.DigitalDataTransferMechanism;
                    task.DOChannels.All.DataTransferRequestCondition = deviceSettings.DigitalDataTransferCondition;
                }
            }

            // ok! now create the buffers

            #region NON variable timebase buffer
            if (deviceSettings.UsingVariableTimebase == false)
            {
                // non "variable timebase" buffer creation

                double timeStepSize = 1.0 / (double)deviceSettings.SampleClockRate;
                int nBaseSamples = sequence.nSamples(timeStepSize);

                // for reasons that are utterly stupid and frustrating, the DAQmx libraries seem to prefer sample
                // buffers with lengths that are a multiple of 4. (otherwise they, on occasion, depending on the parity of the
                // number of channels, throw exceptions complaining.
                // thus we add a few filler samples at the end of the sequence which parrot back the last sample.

                int nFillerSamples = 4 - nBaseSamples % 4;
                if (nFillerSamples == 4)
                    nFillerSamples = 0;

                int nSamples = nBaseSamples + nFillerSamples;

                if (deviceSettings.MySampleClockSource == DeviceSettings.SampleClockSource.DerivedFromMaster)
                {
                    task.Timing.ConfigureSampleClock("", deviceSettings.SampleClockRate, deviceSettings.ClockEdge, SampleQuantityMode.FiniteSamples, nSamples);
                }
                else
                {
                    task.Timing.ConfigureSampleClock(deviceSettings.SampleClockExternalSource, deviceSettings.SampleClockRate, deviceSettings.ClockEdge, SampleQuantityMode.FiniteSamples, nSamples);
                }
                if (deviceSettings.MasterTimebaseSource != "" && deviceSettings.MasterTimebaseSource != null)
                {
                    task.Timing.MasterTimebaseSource = deviceSettings.MasterTimebaseSource.ToString();
                }

                // Analog first...

                if (analogIDs.Count != 0)
                {
                    double[,] analogBuffer;
                    double[] singleChannelBuffer;
                    try
                    {
                        analogBuffer = new double[analogs.Count, nSamples];
                        singleChannelBuffer = new double[nSamples];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate analog buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < analogIDs.Count; i++)
                    {
                        int analogID = analogIDs[i];

                        if (settings.logicalChannelManager.Analogs[analogID].TogglingChannel)
                        {
                            DaqMxTaskGenerator.getAnalogTogglingBuffer(singleChannelBuffer);
                        }
                        else if (settings.logicalChannelManager.Analogs[analogID].overridden)
                        {
                            for (int j = 0; j < singleChannelBuffer.Length; j++)
                            {
                                singleChannelBuffer[j] = settings.logicalChannelManager.Analogs[analogID].analogOverrideValue;
                            }
                        }
                        else
                        {
                            sequence.computeAnalogBuffer(analogIDs[i], timeStepSize, singleChannelBuffer);
                        }

                        for (int j = 0; j < nBaseSamples; j++)
                        {
                            analogBuffer[i, j] = singleChannelBuffer[j];
                        }
                        for (int j = nBaseSamples; j < nSamples; j++)
                        {
                            analogBuffer[i, j] = analogBuffer[i, j - 1];
                        }
                    }

                    singleChannelBuffer = null;
                    System.GC.Collect();

                    AnalogMultiChannelWriter writer = new AnalogMultiChannelWriter(task.Stream);

                    writer.WriteMultiSample(false, analogBuffer);
                    // analog cards report the exact number of generated samples. for non-variable timebase this is nSamples
                    expectedSamplesGenerated = nSamples;

                }

                if (usedPortNumbers.Count != 0)
                {
                    byte[,] digitalBuffer;
                    bool[] singleChannelBuffer;

                    try
                    {
                        digitalBuffer = new byte[usedPortNumbers.Count, nSamples];
                        singleChannelBuffer = new bool[nSamples];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate digital buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < usedPortNumbers.Count; i++)
                    {
                        int portNum = usedPortNumbers[i];
                        byte digitalBitMask = 1;
                        for (int lineNum = 0; lineNum < 8; lineNum++)
                        {
                            int digitalID = port_digital_IDs[portNum][lineNum];
                            if (digitalID != -1)
                            {
                                if (settings.logicalChannelManager.Digitals[digitalID].TogglingChannel)
                                {
                                    getDigitalTogglingBuffer(singleChannelBuffer);
                                }
                                else if (settings.logicalChannelManager.Digitals[digitalID].overridden)
                                {
                                    for (int j = 0; j < singleChannelBuffer.Length; j++)
                                    {
                                        singleChannelBuffer[j] = settings.logicalChannelManager.Digitals[digitalID].digitalOverrideValue;
                                    }
                                }
                                else
                                {

                                    sequence.computeDigitalBuffer(digitalID, timeStepSize, singleChannelBuffer);
                                }
                                // byte digitalBitMask = (byte)(((byte) 2)^ ((byte)lineNum));
                                for (int j = 0; j < nBaseSamples; j++)
                                {
                                    // copy the bit value into the digital buffer byte.
                                    if (singleChannelBuffer[j])
                                        digitalBuffer[i, j] |= digitalBitMask;
                                }

                            }
                            digitalBitMask = (byte)(digitalBitMask << 1);
                        }
                        for (int j = nBaseSamples; j < nSamples; j++)
                        {
                            digitalBuffer[i, j] = digitalBuffer[i, j - 1];
                        }
                    }
                    singleChannelBuffer = null;
                    System.GC.Collect();
                    DigitalMultiChannelWriter writer = new DigitalMultiChannelWriter(task.Stream);
                    writer.WriteMultiSamplePort(false, digitalBuffer);
                    /// Digital cards report the number of generated samples as a multiple of 4
                    expectedSamplesGenerated = nSamples;
                }
            }
            #endregion
            #region Variable timebase buffer creation
            else // variable timebase buffer creation...
            {

                double timeStepSize = 1.0 / (double)deviceSettings.SampleClockRate;

                TimestepTimebaseSegmentCollection timebaseSegments =
            sequence.generateVariableTimebaseSegments(serverSettings.VariableTimebaseType,
                                            timeStepSize);

                int nBaseSamples = timebaseSegments.nSegmentSamples();

                nBaseSamples++; // add one sample for the dwell sample at the end of the buffer

                // for reasons that are utterly stupid and frustrating, the DAQmx libraries seem to prefer sample
                // buffers with lengths that are a multiple of 4. (otherwise they, on occasion, depending on the parity of the
                // number of channels, throw exceptions complaining.
                // thus we add a few filler samples at the end of the sequence which parrot back the last sample.

                int nFillerSamples = 4 - nBaseSamples % 4;
                if (nFillerSamples == 4)
                    nFillerSamples = 0;

                int nSamples = nBaseSamples + nFillerSamples;

                if (deviceSettings.MySampleClockSource == DeviceSettings.SampleClockSource.DerivedFromMaster)
                {
                    throw new Exception("Attempt to use a uniform sample clock with a variable timebase enabled device. This will not work. To use a variable timebase for this device, you must specify an external sample clock source.");
                }
                else
                {
                    task.Timing.ConfigureSampleClock(deviceSettings.SampleClockExternalSource, deviceSettings.SampleClockRate, deviceSettings.ClockEdge, SampleQuantityMode.FiniteSamples, nSamples);
                }

                // Analog first...

                if (analogIDs.Count != 0)
                {
                    double[,] analogBuffer;
                    double[] singleChannelBuffer;
                    try
                    {
                        analogBuffer = new double[analogs.Count, nSamples];
                        singleChannelBuffer = new double[nSamples];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate analog buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < analogIDs.Count; i++)
                    {
                        int analogID = analogIDs[i];

                        if (settings.logicalChannelManager.Analogs[analogID].TogglingChannel)
                        {
                            getAnalogTogglingBuffer(singleChannelBuffer);
                        }
                        else if (settings.logicalChannelManager.Analogs[analogID].overridden)
                        {
                            for (int j = 0; j < singleChannelBuffer.Length; j++)
                            {
                                singleChannelBuffer[j] = settings.logicalChannelManager.Analogs[analogID].analogOverrideValue;
                            }
                        }
                        else
                        {
                            sequence.computeAnalogBuffer(analogIDs[i], timeStepSize, singleChannelBuffer, timebaseSegments);
                        }
                        for (int j = 0; j < nBaseSamples; j++)
                        {
                            analogBuffer[i, j] = singleChannelBuffer[j];
                        }
                        for (int j = nBaseSamples; j < nSamples; j++)
                        {
                            analogBuffer[i, j] = analogBuffer[i, j - 1];
                        }
                    }

                    singleChannelBuffer = null;
                    System.GC.Collect();

                    AnalogMultiChannelWriter writer = new AnalogMultiChannelWriter(task.Stream);

                    writer.WriteMultiSample(false, analogBuffer);
                    // Analog cards report the exact number of samples generated. for variable timebase this is nBaseSamples
                    expectedSamplesGenerated = nBaseSamples;

                }

                if (usedPortNumbers.Count != 0)
                {
                    byte[,] digitalBuffer;
                    bool[] singleChannelBuffer;

                    try
                    {
                        digitalBuffer = new byte[usedPortNumbers.Count, nSamples];
                        singleChannelBuffer = new bool[nSamples];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate digital buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < usedPortNumbers.Count; i++)
                    {
                        int portNum = usedPortNumbers[i];
                        byte digitalBitMask = 1;
                        for (int lineNum = 0; lineNum < 8; lineNum++)
                        {
                            int digitalID = port_digital_IDs[portNum][lineNum];
                            if (digitalID != -1)
                            {
                                if (settings.logicalChannelManager.Digitals[digitalID].TogglingChannel)
                                {
                                    getDigitalTogglingBuffer(singleChannelBuffer);
                                }
                                else if (settings.logicalChannelManager.Digitals[digitalID].overridden)
                                {
                                    for (int j = 0; j < singleChannelBuffer.Length; j++)
                                    {
                                        singleChannelBuffer[j] = settings.logicalChannelManager.Digitals[digitalID].digitalOverrideValue;
                                    }
                                }
                                else
                                {

                                    sequence.computeDigitalBuffer(digitalID, timeStepSize, singleChannelBuffer, timebaseSegments);
                                }
                                // byte digitalBitMask = (byte)(((byte) 2)^ ((byte)lineNum));
                                for (int j = 0; j < nBaseSamples; j++)
                                {
                                    // copy the bit value into the digital buffer byte.
                                    if (singleChannelBuffer[j])
                                        digitalBuffer[i, j] |= digitalBitMask;
                                }

                            }
                            digitalBitMask = (byte)(digitalBitMask << 1);
                        }
                        for (int j = nBaseSamples; j < nSamples; j++)
                        {
                            digitalBuffer[i, j] = digitalBuffer[i, j - 1];
                        }
                    }
                    singleChannelBuffer = null;
                    System.GC.Collect();
                    DigitalMultiChannelWriter writer = new DigitalMultiChannelWriter(task.Stream);
                    writer.WriteMultiSamplePort(false, digitalBuffer);
                    // digital cards report number of samples generated up to multiple of 4
                    expectedSamplesGenerated = nSamples;
                }
            }

            #endregion

            if (deviceSettings.StartTriggerType == DeviceSettings.TriggerType.TriggerIn)
            {

                task.Triggers.StartTrigger.ConfigureDigitalEdgeTrigger(
                    deviceSettings.TriggerInPort,
                    DigitalEdgeStartTriggerEdge.Rising);

            }

            task.Control(TaskAction.Verify);
            task.Control(TaskAction.Commit);
            task.Control(TaskAction.Reserve);

            return task;
        }
        /// <summary>
        /// This method is to be called on the server side, and is not open to use via remoting. Its purpose
        /// is to update the servers internal list of hardware channels by querying the National Instruments drivers.
        /// </summary>
        public void refreshHardwareLists()
        {
            System.Console.WriteLine("Running refreshHardwareLists()...");

            try
            {

                // Set all the devices to disconnected. They will be set back to connected if they are detecter later in this method.
                foreach (DeviceSettings ds in serverSettings.myDevicesSettings.Values)
                    ds.deviceConnected = false;

                myHardwareChannels = new List<HardwareChannel>();

                //List of string identifiers for all devices detected in the process.
                detectedDevices = new List<string>();

                Dictionary<string, string> myDeviceDescriptions = new Dictionary<string, string>();

                // Detect National Instruments analog and digital channels.

                #region detect NI analog and digital  (daqMx)

                System.Console.WriteLine("Accessing DaqSystem devices list...");

                DaqSystem daqSystem = DaqSystem.Local;
                string[] devices = daqSystem.Devices;

                System.Console.WriteLine("...done.");

                System.Console.WriteLine("Found " + devices.Length.ToString() + " devices.");

                bool niDaqDevicesExistThatAreNotNamedDevSomething = false;

                for (int i = 0; i < devices.Length; i++)
                {
                    if (!devices[i].ToUpper().Contains("DEV"))
                    {
                        niDaqDevicesExistThatAreNotNamedDevSomething = true;
                    }

                    System.Console.WriteLine("Querying device " + i + "...");

                    detectedDevices.Add(devices[i]);

                    Device device = daqSystem.LoadDevice(devices[i]);

                    myDeviceDescriptions.Add(devices[i], device.ProductType);
                    string[] analogs = device.AOPhysicalChannels;
                    string[] digitalLines = device.DOLines;

                    if (!serverSettings.myDevicesSettings.ContainsKey(devices[i]))
                    {
                        serverSettings.myDevicesSettings.Add(devices[i], new DeviceSettings(devices[i], myDeviceDescriptions[devices[i]]));
                    }

                    // Special case: 6259 cards use 32-bit wide ports instead of 16 bit wide.
                    if (myDeviceDescriptions[devices[i]].Contains("6259"))
                    {
                        serverSettings.myDevicesSettings[devices[i]].use32BitDigitalPorts = true;
                    }

                    // Add all the analog channels, but only if the device settings say this card is enabled

                    if (serverSettings.myDevicesSettings.ContainsKey(devices[i]) && serverSettings.myDevicesSettings[devices[i]].DeviceEnabled)
                    {

                        if (serverSettings.myDevicesSettings[devices[i]].AnalogChannelsEnabled)
                        {
                            for (int j = 0; j < analogs.Length; j++)
                            {
                                string channelName = justTheChannelName(analogs[j], devices[i]);
                                HardwareChannel hc = new HardwareChannel(this.myServerSettings.ServerName, devices[i], channelName, HardwareChannel.HardwareConstants.ChannelTypes.analog);
                                if (!serverSettings.ExcludedChannels.Contains(hc))
                                {
                                    myHardwareChannels.Add(hc);
                                }
                            }
                        }

                        if (serverSettings.myDevicesSettings[devices[i]].DigitalChannelsEnabled)
                        {
                            for (int j = 0; j < digitalLines.Length; j++)
                            {
                                string channelName = justTheChannelName(digitalLines[j], devices[i]);
                                HardwareChannel hc = new HardwareChannel(this.myServerSettings.ServerName, devices[i], channelName, HardwareChannel.HardwareConstants.ChannelTypes.digital);
                                if (!serverSettings.ExcludedChannels.Contains(hc))
                                {
                                    myHardwareChannels.Add(hc);
                                }
                            }
                        }
                    }

                    System.Console.WriteLine("...done.");

                }

                #endregion

                #region "detect" RFSG cards

                foreach (ServerSettings.rfsgDeviceName rfsgDevName in serverSettings.RfsgDeviceNames)
                {

                    System.Console.WriteLine("Querying RFSG devices...");

                    string devName = rfsgDevName.DeviceName;
                    HardwareChannel hc = new HardwareChannel(serverSettings.ServerName, devName, "rf_out", HardwareChannel.HardwareConstants.ChannelTypes.gpib);
                    hc.gpibMasquerade = true;
                    hc.myGpibMasqueradeType = HardwareChannel.GpibMasqueradeType.RFSG;

                    myHardwareChannels.Add(hc);

                    if (!serverSettings.myDevicesSettings.ContainsKey(devName))
                    {
                        DeviceSettings devSettings = new DeviceSettings(devName, "RFSG driver library signal generator");
                        serverSettings.myDevicesSettings.Add(devName, devSettings);
                    }

                    System.Console.WriteLine("...done.");

                }

                #endregion

                #region detect NI GBIB

                // try a maxumimum of 10 GPIB boards... this is a totally arbitrary number.
                for (int i = 0; i < 10; i++)
                {

                    System.Console.WriteLine("Querying or detecting GPIB Board Number " + i + "...");

                    try
                    {
                        NationalInstruments.NI4882.Board board = new NationalInstruments.NI4882.Board(i);
                        board.SendInterfaceClear();
                        //              board.BecomeActiveController(false);
                        NationalInstruments.NI4882.AddressCollection listeners = board.FindListeners();

                        if (listeners.Count != 0)
                        {
                            foreach (NationalInstruments.NI4882.Address address in listeners)
                            {

                                int wait_delay = 100;

                                try
                                {
                                    NationalInstruments.NI4882.Device dev = new NationalInstruments.NI4882.Device(i, address);
                                    dev.Clear();

                                    // ask the device for its identity
                                    Action<string> writeDelegate = new Action<string>(dev.Write);
                                    IAsyncResult result = writeDelegate.BeginInvoke("*IDN?\n", null, null);
                                    result.AsyncWaitHandle.WaitOne(wait_delay, true);

                                    if (!result.IsCompleted)
                                    {
                                        messageLog(this, new MessageEvent("GPIB device took longer than " + wait_delay + " ms to respond to id request. Aborting."));
                                        dev.AbortAsynchronousIO();
                                        continue;
                                    }

                                    string deviceDescription = dev.ReadString();

                                    string deviceName = "GPIB" + i + "/" + gpibAddressToShortString(address);
                                    detectedDevices.Add(deviceName);

                                    myDeviceDescriptions.Add(deviceName, deviceDescription);

                                    HardwareChannel.HardwareConstants.GPIBDeviceType gpibDeviceType = new HardwareChannel.HardwareConstants.GPIBDeviceType();

                                    // VERY IMPORTANT!!!!!!!!!!
                                    // *******************  THIS IS WHERE YOU ADD DEVICE-DETECTION CODE FOR NEW GPIB DEVICES *********************/
                                    // detect the gpib device type
                                    if (deviceDescription.Contains("ESG-4000B"))
                                    {
                                        gpibDeviceType = HardwareChannel.HardwareConstants.GPIBDeviceType.Agilent_ESG_SIG_Generator;
                                    }
                                    // place any other device type detection code here as else ifs.
                                    else if (deviceDescription.Contains("N5181"))
                                    {
                                        gpibDeviceType = HardwareChannel.HardwareConstants.GPIBDeviceType.Agilent_ESG_SIG_Generator;
                                    }
                                    else
                                    {
                                        gpibDeviceType = HardwareChannel.HardwareConstants.GPIBDeviceType.Unknown;
                                    }

                                    HardwareChannel hc = new HardwareChannel(this.myServerSettings.ServerName,
                                        deviceName,
                                        gpibAddressToShortString(address),
                                        deviceDescription,
                                        HardwareChannel.HardwareConstants.ChannelTypes.gpib, dsAddress(address), gpibDeviceType);
                                    if (!serverSettings.ExcludedChannels.Contains(hc))
                                    {
                                        myHardwareChannels.Add(hc);
                                    }
                                }
                                catch (Exception e)
                                {
                                    messageLog(this, new MessageEvent("Exception when attempting to communicate with GPIB device " + "GPIB" + i + "/" + gpibAddressToShortString(address) + ". " + e.Message + "\n" + e.StackTrace));
                                }
                            }
                        }
                    }
                    catch (Exception)
                    {
                        // throw e;
                    }

                    System.Console.WriteLine("...done.");

                }

                /*
                NationalInstruments.NI4882.Board board = new NationalInstruments.NI4882.Board();
                board.FindListeners(
                */

                #endregion

                #region Detect NI RS232 ports

                System.Console.WriteLine("Querying NI VisaNS Serial Resources...");

                string[] resourceNames = null;
                NationalInstruments.VisaNS.ResourceManager VisaRescources = null;

                System.Console.WriteLine("...done.");

                try
                {
                    VisaRescources = NationalInstruments.VisaNS.ResourceManager.GetLocalManager();
                    resourceNames = VisaRescources.FindResources("/?*");
                }
                catch (Exception e)
                {
                    if (messageLogHandler != null)
                    {
                        messageLogHandler(this, new MessageEvent("Caught exception when attempting to detect serial ports: " + e.Message + e.StackTrace));
                    }
                    else
                    {
                        MessageBox.Show("Caught exception when attempting to detect serial ports: " + e.Message + e.StackTrace);
                    }
                }

                if (resourceNames != null)
                {

                    foreach (string s in resourceNames)
                    {

                        try
                        {

                            System.Console.WriteLine("Querying Resource " + s);

                            NationalInstruments.VisaNS.HardwareInterfaceType hType;
                            short chanNum;
                            VisaRescources.ParseResource(s, out hType, out chanNum);
                            if (hType == NationalInstruments.VisaNS.HardwareInterfaceType.Serial)
                            {
                                NationalInstruments.VisaNS.SerialSession ss = (NationalInstruments.VisaNS.SerialSession)NationalInstruments.VisaNS.ResourceManager.GetLocalManager().Open(s);

                                string description = ss.HardwareInterfaceName;

                                HardwareChannel hc = new HardwareChannel(this.serverSettings.ServerName, "Serial", s, description, HardwareChannel.HardwareConstants.ChannelTypes.rs232);
                                if (!serverSettings.ExcludedChannels.Contains(hc))
                                {
                                    MyHardwareChannels.Add(hc);
                                }
                                if (!detectedDevices.Contains("Serial"))
                                {
                                    detectedDevices.Add("Serial");
                                    myDeviceDescriptions.Add("Serial", "All local RS232 devices.");
                                }

                                ss.Dispose();

                            }
                        }
                        catch (Exception e)
                        {
                            System.Console.WriteLine("Caught exception when attempting to query serial resource named " + s + ". Displaying exception and then skipping this device.");
                            ExceptionViewerDialog ev = new ExceptionViewerDialog(e);
                            ev.ShowDialog();

                        }

                        System.Console.WriteLine("...done.");
                    }
                }

                #endregion

                // If necessary, add DeviceSettings entries for devices.
                foreach (string device in detectedDevices)
                {
                    // If this device does not already have a settings object...
                    if (!myServerSettings.myDevicesSettings.ContainsKey(device))
                    {
                        myServerSettings.myDevicesSettings.Add(device,
                            new DeviceSettings(device, myDeviceDescriptions[device]));
                    }
                    else
                    {
                        myServerSettings.myDevicesSettings[device].deviceConnected = true;
                    }
                }

                #region FPGA Detection and configuration

                if (this.serverSettings.UseOpalKellyFPGA)
                {
                    try
                    {
                        System.Console.WriteLine("Scanning for Opal Kelly FPGA devices...");

                        opalKellyDevices = new List<com.opalkelly.frontpanel.okCFrontPanel>();
                        opalKellyDeviceNames = new List<string>();

                        com.opalkelly.frontpanel.okCFrontPanel ok = new com.opalkelly.frontpanel.okCFrontPanel();
                        int fpgaDeviceCount = ok.GetDeviceCount();

                        System.Console.WriteLine("Found " + fpgaDeviceCount + " fpga device(s).");

                        for (int i = 0; i < fpgaDeviceCount; i++)
                        {
                            string name = ok.GetDeviceListSerial(i);
                            com.opalkelly.frontpanel.okCFrontPanel fpgaDevice = new com.opalkelly.frontpanel.okCFrontPanel();

                            com.opalkelly.frontpanel.okCFrontPanel.ErrorCode errorCode;

                            errorCode = fpgaDevice.OpenBySerial(name);

                            if (errorCode != com.opalkelly.frontpanel.okCFrontPanel.ErrorCode.NoError)
                            {
                                System.Console.WriteLine("Unable to open FPGA device " + name + " due to error code " + errorCode.ToString());
                                continue;
                            }

                            if (!this.detectedDevices.Contains(name))
                            {
                                this.detectedDevices.Add(name);
                            }

                            if (!myServerSettings.myDevicesSettings.ContainsKey(name))
                            {
                                DeviceSettings newSettings = new DeviceSettings(name, "Opal Kelly FPGA Device");
                                newSettings.deviceConnected = true;
                                newSettings.isFPGADevice = true;
                                myServerSettings.myDevicesSettings.Add(name, newSettings);
                            }
                            else
                            {
                                myServerSettings.myDevicesSettings[name].deviceConnected = true;
                            }

                            bool deviceProgrammed = false;

                            if (myServerSettings.myDevicesSettings[name].DeviceEnabled)
                            {
                                if (myServerSettings.myDevicesSettings[name].UsingVariableTimebase)
                                {
                                    deviceProgrammed = true;
                                    if (myServerSettings.myDevicesSettings[name].MySampleClockSource == DeviceSettings.SampleClockSource.DerivedFromMaster)
                                    {
                                        errorCode = fpgaDevice.ConfigureFPGA("variable_timebase_fpga_internal.bit");
                                    }
                                    else
                                    {
                                        errorCode = fpgaDevice.ConfigureFPGA("variable_timebase_fpga_external.bit");
                                    }

                                    if (errorCode == com.opalkelly.frontpanel.okCFrontPanel.ErrorCode.NoError)
                                    {

                                        System.Console.WriteLine("Programmed fpga device " + name + ". Used fpga code version based on sample clock source " + myServerSettings.myDevicesSettings[name].MySampleClockSource.ToString());

                                        opalKellyDeviceNames.Add(name);
                                        opalKellyDevices.Add(fpgaDevice);
                                    }
                                    else
                                    {
                                        System.Console.WriteLine("Error when attempting to program fpga device, error code " + errorCode.ToString());
                                    }
                                }
                            }
                            if (!deviceProgrammed)
                                System.Console.WriteLine("Fpga device " + name + " not programmed, as it is not enable or varible timebase on that device is not enabled.");

                        }
                    }
                    catch (Exception e)
                    {
                        if (e.InnerException != null && e.InnerException.InnerException != null)
                        {
                            Exception innerInner = e.InnerException.InnerException;
                            if (innerInner is System.BadImageFormatException)
                            {
                                MessageBox.Show("You are probably running the wrong build of Atticus. As of version 1.56 Atticus now comes in a 64-bit or 32-bit versions (in the bin\\Release-x64 and bin\\Release-x86 subdirectories respectively). These builds make use of different versions of the Opal Kelly Frontpanel libraries. Please ensure you are using the correct Atticus build. The exception which gave rise to this error will now be displayed.", "Wrong build of Atticus?");
                                DataStructures.ExceptionViewerDialog dial = new ExceptionViewerDialog(e);
                                dial.ShowDialog();
                            }
                            else if (innerInner is System.DllNotFoundException)
                            {
                                MessageBox.Show("As of version 1.56, Atticus is now built to use the 4.0.8 version of the Opal Kelly Frontpanel libraries/drivers. Perhaps you have the wrong version of the libraries installed? You can find a driver-only installer for 4.0.8 in the \\Opal Kelly\\Opal Kelly 4.0.8\\ directory of the Cicero distribution. Please install the newer drivers from there, or contact Opal Kelly to receive a CD with the newest full Frontpanel distribution (or go to the download section of github page for the Cicero Word generator, and download and install Opal-Kelly-CD-4.0.8.zip. You may also need to plug an Opal Kelly device into this computer during installation of the drivers, to insure that the WINUSB windows subsystem gets installed. The exception which gave rise to this error will now be displayed.", "Wrong version of FrontPanel drivers installed?");
                                DataStructures.ExceptionViewerDialog dial = new ExceptionViewerDialog(e);
                                dial.ShowDialog();
                            }
                            else
                            {
                                MessageBox.Show("An unrecognized exception was encountered when scanning for Opal Kelly FPGA devices. The exception will now be displayed.", "Unrecognized exception.");
                                DataStructures.ExceptionViewerDialog dial = new ExceptionViewerDialog(e);
                                dial.ShowDialog();
                            }
                        }
                        else
                        {
                            MessageBox.Show("An unrecognized exception was encountered when scanning for Opal Kelly FPGA devices. The exception will now be displayed.", "Unrecognized exception.");
                            DataStructures.ExceptionViewerDialog dial = new ExceptionViewerDialog(e);
                            dial.ShowDialog();
                        }

                        System.Console.WriteLine("Caught exceptions when attempting to scan for Opal Kelly FPGA devices. Aborted FPGA scan.");
                        opalKellyDevices = null;

                    }
                }
                else
                {
                    System.Console.WriteLine("Opal Kelly FPGA not enabled, so not scanning for them.");
                    opalKellyDevices = null;
                }

                #endregion

                System.Console.WriteLine("...done running refreshHardwareLists().");

                if (niDaqDevicesExistThatAreNotNamedDevSomething)
                {
                    System.Console.WriteLine("!! NOTE !! Some NI devices were detected whose name does not follow the Dev1, Dev2, Dev3, naming convention. This will cause problems.");
                    MessageBox.Show("National Instruments cards were detected whose names do not corresponding to the Dev1, Dev2, Dev3, etc. naming convention. This convention is relied upon by Atticus. Not following this convention will cause problems when attempting to run sequences on these devices. Please use MAX (the NI-supplied program) to rename your NI devices to follow the convention.", "Invalid niDaq Device Names");
                }

            }
            catch (Exception e)
            {
                System.Console.WriteLine("Unhandled exception when scanning connected hardware. Displaying exception, and then continuing to attempt to start Atticus.");
                ExceptionViewerDialog ev = new ExceptionViewerDialog(e);
                ev.ShowDialog();
                MessageBox.Show("Atticus encountered an unhandled exception when scanning for connected hardware. Atticus will continue to run. It is unlikely to function correctly for driving outputs, but will allow you to edit your settings to disable whatever is causing the unhandled exception.");
            }
        }
        /// <summary>
        /// This method creates analog and digital output buffers for daqMx cards. Note that the daqmx library seems to only support
        /// either analog OR digital on a given card at one time. Despite the fact that this method will create both types of buffers,
        /// it will probably throw some daqMX level exceptions if asked to create both analog and digital buffers for the same device.
        /// </summary>
        /// <param name="deviceName"></param>
        /// <param name="deviceSettings"></param>
        /// <param name="sequence"></param>
        /// <param name="settings"></param>
        /// <param name="usedDigitalChannels">digital channels which reside on this server.</param>
        /// <param name="usedAnalogChannels">analog channels which reside on this server</param>
        /// <returns></returns>
        public static Task createDaqMxTask(string deviceName, DeviceSettings deviceSettings, SequenceData sequence,
                                           SettingsData settings, Dictionary <int, HardwareChannel> usedDigitalChannels, Dictionary <int, HardwareChannel> usedAnalogChannels,
                                           ServerSettings serverSettings, out long expectedSamplesGenerated)
        {
            expectedSamplesGenerated = 0;

            Task task = new Task(deviceName + " output task");



            List <int>              analogIDs;
            List <HardwareChannel>  analogs;
            Dictionary <int, int[]> port_digital_IDs;
            List <int>              usedPortNumbers;

            // Parse and create channels.
            parseAndCreateChannels(deviceName, deviceSettings, usedDigitalChannels, usedAnalogChannels, task, out analogIDs, out analogs, out port_digital_IDs, out usedPortNumbers);



            if (analogIDs.Count != 0)
            {
                if (deviceSettings.UseCustomAnalogTransferSettings)
                {
                    task.AOChannels.All.DataTransferMechanism        = deviceSettings.AnalogDataTransferMechanism;
                    task.AOChannels.All.DataTransferRequestCondition = deviceSettings.AnalogDataTransferCondition;
                }
            }
            if (usedPortNumbers.Count != 0)
            {
                if (deviceSettings.UseCustomDigitalTransferSettings)
                {
                    task.DOChannels.All.DataTransferMechanism        = deviceSettings.DigitalDataTransferMechanism;
                    task.DOChannels.All.DataTransferRequestCondition = deviceSettings.DigitalDataTransferCondition;
                }
            }

            // ok! now create the buffers

            #region NON variable timebase buffer
            if (deviceSettings.UsingVariableTimebase == false)
            {
                // non "variable timebase" buffer creation


                double timeStepSize = Common.getPeriodFromFrequency(deviceSettings.SampleClockRate);
                int    nBaseSamples = sequence.nSamples(timeStepSize);

                // for reasons that are utterly stupid and frustrating, the DAQmx libraries seem to prefer sample
                // buffers with lengths that are a multiple of 4. (otherwise they, on occasion, depending on the parity of the
                // number of channels, throw exceptions complaining.
                // thus we add a few filler samples at the end of the sequence which parrot back the last sample.

                int nFillerSamples = 4 - nBaseSamples % 4;
                if (nFillerSamples == 4)
                {
                    nFillerSamples = 0;
                }

                int nSamples = nBaseSamples + nFillerSamples;

                if (deviceSettings.MySampleClockSource == DeviceSettings.SampleClockSource.DerivedFromMaster)
                {
                    task.Timing.ConfigureSampleClock("", deviceSettings.SampleClockRate, deviceSettings.ClockEdge, SampleQuantityMode.FiniteSamples, nSamples);
                }
                else
                {
                    task.Timing.ConfigureSampleClock(deviceSettings.SampleClockExternalSource, deviceSettings.SampleClockRate, deviceSettings.ClockEdge, SampleQuantityMode.FiniteSamples, nSamples);
                }
                if (deviceSettings.MasterTimebaseSource != "" && deviceSettings.MasterTimebaseSource != null)
                {
                    task.Timing.MasterTimebaseSource = deviceSettings.MasterTimebaseSource.ToString();
                }



                // Analog first...

                if (analogIDs.Count != 0)
                {
                    double[,] analogBuffer;
                    double[] singleChannelBuffer;
                    try
                    {
                        analogBuffer        = new double[analogs.Count, nSamples];
                        singleChannelBuffer = new double[nSamples];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate analog buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < analogIDs.Count; i++)
                    {
                        int analogID = analogIDs[i];

                        if (settings.logicalChannelManager.Analogs[analogID].TogglingChannel)
                        {
                            DaqMxTaskGenerator.getAnalogTogglingBuffer(singleChannelBuffer);
                        }
                        else if (settings.logicalChannelManager.Analogs[analogID].overridden)
                        {
                            for (int j = 0; j < singleChannelBuffer.Length; j++)
                            {
                                singleChannelBuffer[j] = settings.logicalChannelManager.Analogs[analogID].analogOverrideValue;
                            }
                        }
                        else
                        {
                            sequence.computeAnalogBuffer(analogIDs[i], timeStepSize, singleChannelBuffer);
                        }

                        for (int j = 0; j < nBaseSamples; j++)
                        {
                            analogBuffer[i, j] = singleChannelBuffer[j];
                        }
                        for (int j = nBaseSamples; j < nSamples; j++)
                        {
                            analogBuffer[i, j] = analogBuffer[i, j - 1];
                        }
                    }

                    singleChannelBuffer = null;
                    System.GC.Collect();


                    AnalogMultiChannelWriter writer = new AnalogMultiChannelWriter(task.Stream);

                    writer.WriteMultiSample(false, analogBuffer);
                    // analog cards report the exact number of generated samples. for non-variable timebase this is nSamples
                    expectedSamplesGenerated = nSamples;
                }


                if (usedPortNumbers.Count != 0)
                {
                    byte[,] digitalBuffer;
                    bool[] singleChannelBuffer;

                    try
                    {
                        digitalBuffer       = new byte[usedPortNumbers.Count, nSamples];
                        singleChannelBuffer = new bool[nSamples];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate digital buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < usedPortNumbers.Count; i++)
                    {
                        int  portNum        = usedPortNumbers[i];
                        byte digitalBitMask = 1;
                        for (int lineNum = 0; lineNum < 8; lineNum++)
                        {
                            int digitalID = port_digital_IDs[portNum][lineNum];
                            if (digitalID != -1)
                            {
                                if (settings.logicalChannelManager.Digitals[digitalID].TogglingChannel)
                                {
                                    getDigitalTogglingBuffer(singleChannelBuffer);
                                }
                                else if (settings.logicalChannelManager.Digitals[digitalID].overridden)
                                {
                                    for (int j = 0; j < singleChannelBuffer.Length; j++)
                                    {
                                        singleChannelBuffer[j] = settings.logicalChannelManager.Digitals[digitalID].digitalOverrideValue;
                                    }
                                }
                                else
                                {
                                    sequence.computeDigitalBuffer(digitalID, timeStepSize, singleChannelBuffer);
                                }
                                // byte digitalBitMask = (byte)(((byte) 2)^ ((byte)lineNum));
                                for (int j = 0; j < nBaseSamples; j++)
                                {
                                    // copy the bit value into the digital buffer byte.
                                    if (singleChannelBuffer[j])
                                    {
                                        digitalBuffer[i, j] |= digitalBitMask;
                                    }
                                }
                            }
                            digitalBitMask = (byte)(digitalBitMask << 1);
                        }
                        for (int j = nBaseSamples; j < nSamples; j++)
                        {
                            digitalBuffer[i, j] = digitalBuffer[i, j - 1];
                        }
                    }
                    singleChannelBuffer = null;
                    System.GC.Collect();
                    DigitalMultiChannelWriter writer = new DigitalMultiChannelWriter(task.Stream);
                    writer.WriteMultiSamplePort(false, digitalBuffer);
                    /// Digital cards report the number of generated samples as a multiple of 4
                    expectedSamplesGenerated = nSamples;
                }
            }
            #endregion
            #region Variable timebase buffer creation
            else // variable timebase buffer creation...
            {
                double timeStepSize = Common.getPeriodFromFrequency(deviceSettings.SampleClockRate);

                TimestepTimebaseSegmentCollection timebaseSegments =
                    sequence.generateVariableTimebaseSegments(serverSettings.VariableTimebaseType,
                                                              timeStepSize);

                int nBaseSamples = timebaseSegments.nSegmentSamples();

                nBaseSamples++; // add one sample for the dwell sample at the end of the buffer

                // for reasons that are utterly stupid and frustrating, the DAQmx libraries seem to prefer sample
                // buffers with lengths that are a multiple of 4. (otherwise they, on occasion, depending on the parity of the
                // number of channels, throw exceptions complaining.
                // thus we add a few filler samples at the end of the sequence which parrot back the last sample.

                int nFillerSamples = 4 - nBaseSamples % 4;
                if (nFillerSamples == 4)
                {
                    nFillerSamples = 0;
                }

                int nSamples = nBaseSamples + nFillerSamples;

                if (deviceSettings.MySampleClockSource == DeviceSettings.SampleClockSource.DerivedFromMaster)
                {
                    throw new Exception("Attempt to use a uniform sample clock with a variable timebase enabled device. This will not work. To use a variable timebase for this device, you must specify an external sample clock source.");
                }
                else
                {
                    task.Timing.ConfigureSampleClock(deviceSettings.SampleClockExternalSource, deviceSettings.SampleClockRate, deviceSettings.ClockEdge, SampleQuantityMode.FiniteSamples, nSamples);
                }


                // Analog first...

                if (analogIDs.Count != 0)
                {
                    double[,] analogBuffer;
                    double[] singleChannelBuffer;
                    try
                    {
                        analogBuffer        = new double[analogs.Count, nSamples];
                        singleChannelBuffer = new double[nSamples];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate analog buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < analogIDs.Count; i++)
                    {
                        int analogID = analogIDs[i];

                        if (settings.logicalChannelManager.Analogs[analogID].TogglingChannel)
                        {
                            getAnalogTogglingBuffer(singleChannelBuffer);
                        }
                        else if (settings.logicalChannelManager.Analogs[analogID].overridden)
                        {
                            for (int j = 0; j < singleChannelBuffer.Length; j++)
                            {
                                singleChannelBuffer[j] = settings.logicalChannelManager.Analogs[analogID].analogOverrideValue;
                            }
                        }
                        else
                        {
                            sequence.computeAnalogBuffer(analogIDs[i], timeStepSize, singleChannelBuffer, timebaseSegments);
                        }
                        for (int j = 0; j < nBaseSamples; j++)
                        {
                            analogBuffer[i, j] = singleChannelBuffer[j];
                        }
                        for (int j = nBaseSamples; j < nSamples; j++)
                        {
                            analogBuffer[i, j] = analogBuffer[i, j - 1];
                        }
                    }

                    singleChannelBuffer = null;
                    System.GC.Collect();


                    AnalogMultiChannelWriter writer = new AnalogMultiChannelWriter(task.Stream);

                    writer.WriteMultiSample(false, analogBuffer);
                    // Analog cards report the exact number of samples generated. for variable timebase this is nBaseSamples
                    expectedSamplesGenerated = nBaseSamples;
                }


                if (usedPortNumbers.Count != 0)
                {
                    byte[,] digitalBuffer;
                    bool[] singleChannelBuffer;

                    try
                    {
                        digitalBuffer       = new byte[usedPortNumbers.Count, nSamples];
                        singleChannelBuffer = new bool[nSamples];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate digital buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < usedPortNumbers.Count; i++)
                    {
                        int  portNum        = usedPortNumbers[i];
                        byte digitalBitMask = 1;
                        for (int lineNum = 0; lineNum < 8; lineNum++)
                        {
                            int digitalID = port_digital_IDs[portNum][lineNum];
                            if (digitalID != -1)
                            {
                                if (settings.logicalChannelManager.Digitals[digitalID].TogglingChannel)
                                {
                                    getDigitalTogglingBuffer(singleChannelBuffer);
                                }
                                else if (settings.logicalChannelManager.Digitals[digitalID].overridden)
                                {
                                    for (int j = 0; j < singleChannelBuffer.Length; j++)
                                    {
                                        singleChannelBuffer[j] = settings.logicalChannelManager.Digitals[digitalID].digitalOverrideValue;
                                    }
                                }
                                else
                                {
                                    sequence.computeDigitalBuffer(digitalID, timeStepSize, singleChannelBuffer, timebaseSegments);
                                }
                                // byte digitalBitMask = (byte)(((byte) 2)^ ((byte)lineNum));
                                for (int j = 0; j < nBaseSamples; j++)
                                {
                                    // copy the bit value into the digital buffer byte.
                                    if (singleChannelBuffer[j])
                                    {
                                        digitalBuffer[i, j] |= digitalBitMask;
                                    }
                                }
                            }
                            digitalBitMask = (byte)(digitalBitMask << 1);
                        }
                        for (int j = nBaseSamples; j < nSamples; j++)
                        {
                            digitalBuffer[i, j] = digitalBuffer[i, j - 1];
                        }
                    }
                    singleChannelBuffer = null;
                    System.GC.Collect();
                    DigitalMultiChannelWriter writer = new DigitalMultiChannelWriter(task.Stream);
                    writer.WriteMultiSamplePort(false, digitalBuffer);
                    // digital cards report number of samples generated up to multiple of 4
                    expectedSamplesGenerated = nSamples;
                }
            }

            #endregion

            if (deviceSettings.StartTriggerType == DeviceSettings.TriggerType.TriggerIn)
            {
                task.Triggers.StartTrigger.ConfigureDigitalEdgeTrigger(
                    deviceSettings.TriggerInPort,
                    DigitalEdgeStartTriggerEdge.Rising);
            }

            task.Control(TaskAction.Verify);
            task.Control(TaskAction.Commit);
            task.Control(TaskAction.Reserve);


            return(task);
        }
        /// <summary>
        /// This method is a sort of combination of createDaqMxVariableTimebaseSource and createDaqMxTask. It is intended
        /// for use to create a digital output task that has both a variable timebase source on it, without having to discard
        /// all of the other channels on that port (and possibly on its neighboring port).
        ///
        /// NOTE: No longer true. This function can not create the variable timebase outputs at the same time as
        /// normal outputs. If you attempt to use digital outputs on the same half of the card as your variable timebase output,
        /// this method will complain.
        /// </summary>
        /// <param name="channelName">
        /// Name of the channel that will output the variable timebase clock.
        /// </param>
        /// <param name="portsToUse">
        /// A list of integers specifying the digital ports that this task will use. The task will automatically
        /// make use of the full port that the variable timebase clock belongs to. If portsToUse is null, then
        /// this function will automatically use both this port and its neighboring port (0 with 1, 2 with 3, etc).
        /// The rationale for this is that on some NI devices, these pairs of ports will share a sample clock and
        /// cannot truly be used independently.
        /// </param>
        /// <param name="masterFrequency">
        /// The frequency, in hertz, of the master clock which will drive the variable timebase clock and the rest of the
        /// channels in this task.
        /// </param>
        /// <param name="sequenceData"></param>
        /// <param name="timebaseType"></param>
        /// <param name="deviceName"></param>
        /// <param name="deviceSettings"></param>
        /// <param name="sequence"></param>
        /// <param name="settings"></param>
        /// <param name="usedDigitalChannels"></param>
        /// <param name="usedAnalogChannels"></param>
        /// <param name="serverSettings"></param>
        /// <returns></returns>
        public static Task createDaqMxDigitalOutputAndVariableTimebaseSource(string channelName, List <int> portsToUse, int masterFrequency,
                                                                             SequenceData sequenceData, SequenceData.VariableTimebaseTypes timebaseType,
                                                                             string deviceName, DeviceSettings deviceSettings, SettingsData settings, Dictionary <int, HardwareChannel> usedDigitalChannels, ServerSettings serverSettings)
        {
            // First generate the variable timebase buffer. We will need stuff like its length for configuring the task, which is why we do this first.

            TimestepTimebaseSegmentCollection timebaseSegments = sequenceData.generateVariableTimebaseSegments(timebaseType, Common.getPeriodFromFrequency(masterFrequency));

            bool[] variableTimebaseBuffer = sequenceData.getVariableTimebaseClock(timebaseSegments);


            if (deviceName.ToUpper() != HardwareChannel.parseDeviceNameStringFromPhysicalChannelString(channelName).ToUpper())
            {
                throw new Exception("The variable timebase device " + HardwareChannel.parseDeviceNameStringFromPhysicalChannelString(channelName) + " does not match device " + deviceName + ". These must match for their their task to be created together.");
            }

            int timebasePortNum;
            int timebaseLineNum;

            try
            {
                timebasePortNum = HardwareChannel.parsePortNumberFromChannelString(channelName);
                timebaseLineNum = HardwareChannel.parseLineNumberFromChannelString(channelName);
            }
            catch (Exception)
            {
                throw new Exception("Channel name " + channelName + " is not a valid digital channel name. Cannot create a variable timebase output on this channel.");
            }



            if (portsToUse == null)
            {
                portsToUse = new List <int>();
                portsToUse.Add(timebasePortNum);
                int spousePort; // this port is likely to have a shared sample clock with timebasePortNum,
                // at least in my experience so far.
                if (timebasePortNum % 2 == 0)
                {
                    spousePort = timebasePortNum + 1;
                }
                else
                {
                    spousePort = timebasePortNum - 1;
                }

                portsToUse.Add(spousePort);
            }

            if (!portsToUse.Contains(timebasePortNum))
            {
                portsToUse.Add(timebasePortNum);
            }

            bool otherChannelsUsedOnUsedPort = false;

            foreach (HardwareChannel hc in usedDigitalChannels.Values)
            {
                if (hc.DeviceName.ToUpper() == deviceName.ToUpper())
                {
                    if (portsToUse.Contains(hc.daqMxDigitalPortNumber()))
                    {
                        otherChannelsUsedOnUsedPort = true;
                    }
                }
            }

            if (otherChannelsUsedOnUsedPort)
            {
                throw new Exception("Variable timebase channel is on a port that shares a sample clock with a used output channel (on most devices, port 0 and 1 have a shared clock, and port 2 and 3 have a shared clock). This usage is not recommended, and not currently supported. Aborting buffer generation.");

                #region Deprecated code

                /*
                 * Task task = new Task("Variable timebase output task");
                 *
                 * // Create channels in the task
                 * foreach (int portNum in portsToUse)
                 * {
                 *  task.DOChannels.CreateChannel(deviceName + '/' + HardwareChannel.digitalPhysicalChannelName(portNum), "", ChannelLineGrouping.OneChannelForAllLines);
                 * }
                 *
                 * // Configure the task...
                 *
                 * task.Timing.ConfigureSampleClock("", masterFrequency, SampleClockActiveEdge.Rising, SampleQuantityMode.FiniteSamples, variableTimebaseBuffer.Length);
                 *
                 * if (serverSettings.VariableTimebaseTriggerInput != "")
                 * {
                 *  task.Triggers.StartTrigger.ConfigureDigitalEdgeTrigger(serverSettings.VariableTimebaseTriggerInput, DigitalEdgeStartTriggerEdge.Rising);
                 * }
                 *
                 *
                 * // Figure out which ports we are going to use, and which digital ID each line on each of those ports
                 * // maps to.
                 * Dictionary<int, HardwareChannel> digitalChannelsToUse = getChannelsOnDevice(usedDigitalChannels, deviceName);
                 * List<int> temp = new List<int>(digitalChannelsToUse.Keys);
                 * foreach (int id in temp)
                 * {
                 *  HardwareChannel ch = digitalChannelsToUse[id];
                 *  if (!portsToUse.Contains(HardwareChannel.parsePortNumberFromChannelString(ch.ChannelName)))
                 *  {
                 *      digitalChannelsToUse.Remove(id);
                 *  }
                 * }
                 *
                 * // Remove all of the digital channels this buffer generation is consuming from the
                 * // usedDigitalChannels dictionary. Why? Because there may still be another task to
                 * // generate on this device, and this is a way of keeping track of which channels
                 * // have already had their buffers generated.
                 * // Since digitalChannelsToUse gets passed by reference from AtticusServerRuntime.generateBuffers(...),
                 * // these changes thus get communicated back to AtticusServerRuntime.
                 * foreach (HardwareChannel hc in digitalChannelsToUse.Values)
                 * {
                 *  foreach (int i in usedDigitalChannels.Keys)
                 *  {
                 *      if (usedDigitalChannels[i] == hc)
                 *      {
                 *          usedDigitalChannels.Remove(i);
                 *          break;
                 *      }
                 *  }
                 * }
                 *
                 * List<int> ids = new List<int>(digitalChannelsToUse.Keys);
                 * ids.Sort();
                 * List<HardwareChannel> hcs = new List<HardwareChannel>();
                 * foreach (int id in ids)
                 * {
                 *  hcs.Add(digitalChannelsToUse[id]);
                 * }
                 *
                 * Dictionary<int, int[]> port_digital_ids;
                 * List<int> usedPorts;
                 *
                 * groupDigitalChannels(ids, hcs, out port_digital_ids, out usedPorts);
                 *
                 *
                 *
                 * // now to generate the buffers.
                 *
                 *
                 * if (usedPorts.Count != 0)
                 * {
                 *  byte[,] digitalBuffer;
                 *
                 *
                 *  try
                 *  {
                 *      digitalBuffer = new byte[usedPorts.Count, variableTimebaseBuffer.Length];
                 *  }
                 *  catch (Exception e)
                 *  {
                 *      throw new Exception("Unable to allocate digital buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                 *  }
                 *
                 *  for (int i = 0; i < usedPorts.Count; i++)
                 *  {
                 *      int portNum = usedPorts[i];
                 *      byte digitalBitMask = 1;
                 *      for (int lineNum = 0; lineNum < 8; lineNum++)
                 *      {
                 *          bool[] singleChannelBuffer = null;
                 *
                 *          if (portNum == timebasePortNum && lineNum == timebaseLineNum)
                 *          { // this current line is the variable timebase...
                 *              singleChannelBuffer = variableTimebaseBuffer;
                 *          }
                 *          else
                 *          {
                 *              int digitalID = port_digital_ids[portNum][lineNum];
                 *              if (digitalID != -1)
                 *              {
                 *                  if (settings.logicalChannelManager.Digitals[digitalID].overridden)
                 *                  {
                 *                      singleChannelBuffer = new bool[variableTimebaseBuffer.Length];
                 *                      if (settings.logicalChannelManager.Digitals[digitalID].digitalOverrideValue)
                 *                      {
                 *                          for (int j = 0; j < singleChannelBuffer.Length; j++)
                 *                          {
                 *                              singleChannelBuffer[j] = true;
                 *                          }
                 *                      }
                 *                  }
                 *                  else
                 *                  {
                 *                      singleChannelBuffer = sequenceData.getDigitalBufferClockSharedWithVariableTimebaseClock(timebaseSegments, digitalID, 1.0 / deviceSettings.SampleClockRate);
                 *                  }
                 *              }
                 *          }
                 *
                 *          if (singleChannelBuffer != null)
                 *          {
                 *              for (int j = 0; j < singleChannelBuffer.Length; j++)
                 *              {
                 *                  if (singleChannelBuffer[j])
                 *                  {
                 *                      digitalBuffer[i, j] |= digitalBitMask;
                 *                  }
                 *              }
                 *          }
                 *          digitalBitMask = (byte)(digitalBitMask << 1);
                 *
                 *      }
                 *  }
                 *  System.GC.Collect();
                 *  DigitalMultiChannelWriter writer = new DigitalMultiChannelWriter(task.Stream);
                 *  writer.WriteMultiSamplePort(false, digitalBuffer);
                 *
                 *
                 * }
                 *
                 *
                 * return task; */

                #endregion
            }
            else
            {
                return(createDaqMxVariableTimebaseSource(
                           channelName, masterFrequency, sequenceData, timebaseType, serverSettings, deviceSettings));
            }
        }
Ejemplo n.º 8
0
        public RfsgTask(SequenceData sequence, SettingsData settings, int channelID, string rfsgDeviceName, DeviceSettings deviceSettings)
        {
            if (!settings.logicalChannelManager.GPIBs.ContainsKey(channelID))
            {
                throw new InvalidDataException("Attempted to create an rfsg task with channel id " + channelID + ", which does not exist in the settings as a gpib channel.");
            }

            this.channelID = channelID;

            int currentStepIndex = -1;

            //measured in ticks. 1 tick = 100 ns.
            long currentTime = 0;

            commandBuffer = new List<RFSGCommand>();

            if (deviceSettings.AutoInitate)
            {
                RFSGCommand com = new RFSGCommand();
                com.commandTime = 0;
                com.commandType = RFSGCommand.CommandType.Initiate;
                commandBuffer.Add(com);
            }

            if (deviceSettings.AutoEnable)
            {
                RFSGCommand com = new RFSGCommand();
                com.commandTime = 0;
                com.commandType = RFSGCommand.CommandType.EnableOutput;
                commandBuffer.Add(com);
            }

            long postRetriggerTime = 100; // corresponds to 10us.
            // A workaround to issue when using software timed groups in
            // fpga-retriggered words

            // the workaround: delay the software timed group by an immesurable amount
            // if it is started in a retriggered word

            // This functionality is sort of somewhat duplicated in sequencedata.generatebuffers. It would be good
            // to come up with a more coherent framework to do these sorts of operations.
            while (true)
            {
                currentStepIndex++;

                if (currentStepIndex >= sequence.TimeSteps.Count)
                    break;

                TimeStep currentStep = sequence.TimeSteps[currentStepIndex];

                if (!currentStep.StepEnabled)
                    continue;

                if (currentStep.GpibGroup == null || !currentStep.GpibGroup.channelEnabled(channelID))
                {
                    currentTime += seconds_to_ticks(currentStep.StepDuration.getBaseValue());
                    continue;
                }

                long postTime = 0;
                if (currentStep.RetriggerOptions.WaitForRetrigger)
                    postTime = postRetriggerTime;

                // determine the index of the next step in which this channel has an action
                int nextEnabledStepIndex = sequence.findNextGpibChannelEnabledTimestep(currentStepIndex, channelID);

                long groupDuration = seconds_to_ticks(sequence.timeBetweenSteps(currentStepIndex, nextEnabledStepIndex));

                // now take action:

                GPIBGroupChannelData channelData = currentStep.GpibGroup.getChannelData(channelID);

                if (channelData.DataType == GPIBGroupChannelData.GpibChannelDataType.raw_string)
                {

                    throw new Exception("Not yet implemented.");
                    /*

                    // Raw string commands just get added
                    string stringWithCorrectNewlines = AddNewlineCharacters(channelData.RawString);

                    commandBuffer.Add(new GpibCommand(stringWithCorrectNewlines, currentTime));*/
                }
                else if (channelData.DataType == GPIBGroupChannelData.GpibChannelDataType.voltage_frequency_waveform)
                {

                    double[] amplitudeArray;
                    double[] frequencyArray;

                    // get amplitude and frequency value arrays
                    int nSamples = (int)(ticks_to_seconds(groupDuration) * (double)deviceSettings.SampleClockRate);
                    double secondsPerSample = ticks_to_seconds(groupDuration) / (double)nSamples;

                    amplitudeArray = channelData.volts.getInterpolation(nSamples, 0, ticks_to_seconds(groupDuration), sequence.Variables, sequence.CommonWaveforms);
                    frequencyArray = channelData.frequency.getInterpolation(nSamples, 0, ticks_to_seconds(groupDuration), sequence.Variables, sequence.CommonWaveforms);

                    double lastFreq = Double.MinValue;
                    double lastAmp = Double.MinValue;

                    for (int i = 0; i < nSamples; i++)
                    {
                        double currentFreq = frequencyArray[i];
                        double currentAmp = amplitudeArray[i];
                        if (currentFreq != lastFreq || currentAmp != lastAmp)
                        {
                            RFSGCommand command = new RFSGCommand();
                            command.commandType = RFSGCommand.CommandType.AmplitudeFrequency;
                            command.frequency = currentFreq;
                            command.amplitude = Vpp_to_dBm(currentAmp);
                            command.commandTime = (long)(currentTime + i * secondsPerSample * 10000000) + postTime;

                            commandBuffer.Add(command);
                        }
                        lastAmp = currentAmp;
                        lastFreq = currentFreq;

                    }
                }

                else if (channelData.DataType == GPIBGroupChannelData.GpibChannelDataType.string_param_string)
                {
                    foreach (StringParameterString sps in channelData.StringParameterStrings)
                    {
                        string clean = sps.Prefix.Trim().ToUpper();
                        if (clean == "ENABLE")
                        {
                            RFSGCommand com = new RFSGCommand();
                            com.commandType = RFSGCommand.CommandType.EnableOutput;
                            com.commandTime = currentTime + postTime;
                            commandBuffer.Add(com);
                        }

                        if (clean == "DISABLE")
                        {
                            RFSGCommand com = new RFSGCommand();
                            com.commandType = RFSGCommand.CommandType.DisableOutput;
                            com.commandTime = currentTime + postTime;
                            commandBuffer.Add(com);
                        }

                        if (clean == "ABORT")
                        {
                            RFSGCommand com = new RFSGCommand();
                            com.commandType = RFSGCommand.CommandType.Abort;
                            com.commandTime = currentTime + postTime;
                            commandBuffer.Add(com);
                        }

                        if (clean == "INITIATE")
                        {
                            RFSGCommand com = new RFSGCommand();
                            com.commandType = RFSGCommand.CommandType.Initiate;
                            com.commandTime = currentTime + postTime;
                            commandBuffer.Add(com);
                        }
                    }
                }

                currentTime += seconds_to_ticks(currentStep.StepDuration.getBaseValue());
            }

            if (rfsgDevices == null)
            {
                rfsgDevices = new Dictionary<string, niRFSG>();
                rfsgDeviceInitiated = new Dictionary<niRFSG, bool>();
            }

            if (rfsgDevices.ContainsKey(rfsgDeviceName))
            {
                rfsgDevice = rfsgDevices[rfsgDeviceName];
            }
            else
            {

                try
                {
                    rfsgDevice = new niRFSG(rfsgDeviceName, true, false);
                }
                catch (Exception e)
                {
                    throw new InvalidDataException("Caught exception when attempting to instantiate an rfsg device named " + rfsgDeviceName + ". Maybe a device by this name does not exist? Exception message: " + e.Message);
                }
                rfsgDevices.Add(rfsgDeviceName, rfsgDevice);
                rfsgDeviceInitiated.Add(rfsgDevice, false);
            }

            if (deviceSettings.MasterTimebaseSource != null && deviceSettings.MasterTimebaseSource != "")
            {
                rfsgDevice.ConfigureRefClock(deviceSettings.MasterTimebaseSource, 10000000);
            }
        }
        /// <summary>
        /// Creates a task for a variable timebase output. Consumes the entire port (8 bits) that the timebase is on. (ie outputs the
        /// signal on all 8 bits
        /// </summary>
        /// <param name="channelName"></param>
        /// <param name="masterFrequency"></param>
        /// <param name="sequenceData"></param>
        /// <param name="timebaseType"></param>
        /// <returns></returns>
        public static Task createDaqMxVariableTimebaseSource(string channelName, int masterFrequency, SequenceData sequenceData, 
            SequenceData.VariableTimebaseTypes timebaseType, ServerSettings serverSettings, DeviceSettings deviceSettings)
        {
            Task task = new Task("Variable timebase output task");

            TimestepTimebaseSegmentCollection timebaseSegments = sequenceData.generateVariableTimebaseSegments(timebaseType,
                1.0 / (double)masterFrequency);

            bool [] buffer = sequenceData.getVariableTimebaseClock(timebaseSegments);

            string timebaseDeviceName = HardwareChannel.parseDeviceNameStringFromPhysicalChannelString(channelName);

            string timebasePort = HardwareChannel.parsePortStringFromChannelString(channelName);

            task.DOChannels.CreateChannel(timebasePort, "", ChannelLineGrouping.OneChannelForAllLines);

            task.Timing.ConfigureSampleClock("", (double)masterFrequency, deviceSettings.ClockEdge, SampleQuantityMode.FiniteSamples, buffer.Length);

            if (serverSettings.VariableTimebaseTriggerInput != "")
            {
                task.Triggers.StartTrigger.ConfigureDigitalEdgeTrigger(serverSettings.VariableTimebaseTriggerInput, DigitalEdgeStartTriggerEdge.Rising);
            }

            DigitalSingleChannelWriter writer = new DigitalSingleChannelWriter(task.Stream);

            byte[] byteBuffer = new byte[buffer.Length];
            for (int j = 0; j < buffer.Length; j++)
            {
                if (buffer[j])
                {
                    byteBuffer[j] = 255;
                }
            }

            writer.WriteMultiSamplePort(false, byteBuffer);

            return task;
        }
Ejemplo n.º 10
0
        public bool generateBuffer(SequenceData sequence, DeviceSettings deviceSettings, HardwareChannel hc, int logicalChannelID)
        {
            this.logicalChannelID = logicalChannelID;

            commandBuffer = new List<RS232Command>();

            if (deviceSettings.StartTriggerType != DeviceSettings.TriggerType.SoftwareTrigger)
            {
                throw new Exception("RS232 devices must have a software start trigger.");
            }

              //     List<TimeStep> enabledSteps = sequence.enabledTimeSteps();

            int currentStepIndex = -1;

            //measured in ticks. 1 tick = 100 ns.
            long currentTime = 0;

               // This functionality is sort of somewhat duplicated in sequencedata.generatebuffers. It would be good
               // to come up with a more coherent framework to do these sorts of operations.
               while (true)
               {
               currentStepIndex++;

               if (currentStepIndex >= sequence.TimeSteps.Count)
                   break;

               TimeStep currentStep = sequence.TimeSteps[currentStepIndex];

               if (!currentStep.StepEnabled)
                   continue;

               if (currentStep.rs232Group == null || !currentStep.rs232Group.channelEnabled(logicalChannelID))
               {
                   currentTime += seconds_to_ticks(currentStep.StepDuration.getBaseValue());
                   continue;
               }

               // determine the index of the next step in which this channel has an action
               int nextEnabledStepIndex = sequence.findNextRS232ChannelEnabledTimestep(currentStepIndex, logicalChannelID);

               long groupDuration = seconds_to_ticks(sequence.timeBetweenSteps(currentStepIndex, nextEnabledStepIndex));

               // now take action:

               RS232GroupChannelData channelData = currentStep.rs232Group.getChannelData(logicalChannelID);

               if (channelData.DataType == RS232GroupChannelData.RS232DataType.Raw) {
                       // Raw string commands just get added
                       string stringWithCorrectNewlines = AddNewlineCharacters(channelData.RawString);

                       commandBuffer.Add(new RS232Command(  stringWithCorrectNewlines, currentTime));
               }
               else if (channelData.DataType == RS232GroupChannelData.RS232DataType.Parameter)
               {
                   if (channelData.StringParameterStrings != null)
                   {
                       foreach (StringParameterString srs in channelData.StringParameterStrings)
                       {
                           commandBuffer.Add(new RS232Command(
                               AddNewlineCharacters(srs.ToString()), currentTime));
                       }
                   }
               }

               currentTime += seconds_to_ticks(currentStep.StepDuration.getBaseValue());
               }

               return true;
        }
Ejemplo n.º 11
0
        long postRetriggerTime = 100; // corresponds to 10us.
        // A workaround to issue when using software timed groups in
        // fpga-retriggered words

        // the workaround: delay the software timed group by an immesurable amount
        // if it is started in a retriggered word


        // This functionality is sort of somewhat duplicated in sequencedata.generatebuffers. It would be good
        // to come up with a more coherent framework to do these sorts of operations.

        public bool generateBuffer(SequenceData sequence, DeviceSettings deviceSettings, HardwareChannel hc, int logicalChannelID, List <GpibRampCommandConverter> commandConverters)
        {
            this.logicalChannelID = logicalChannelID;
            this.deviceType       = hc.GpibDeviceType;
            commandBuffer         = new List <GpibCommand>();


            if (deviceSettings.StartTriggerType != DeviceSettings.TriggerType.SoftwareTrigger)
            {
                throw new Exception("GPIB devices must have a software start trigger.");
            }

            // start by adding the initialization string to the command buffer
            // this does nothing for unknown device types
            // Modified by REO 11/25/08: Null strings produce errors if written to device, so check
            if (HardwareChannel.HardwareConstants.gpibInitializationCommands[(int)deviceType] != null)
            {
                commandBuffer.Add(new GpibCommand(HardwareChannel.HardwareConstants.gpibInitializationCommands[(int)deviceType], 0));
            }


            if (deviceType == HardwareChannel.HardwareConstants.GPIBDeviceType.Unknown)
            {
                foreach (GpibRampCommandConverter conv in commandConverters)
                {
                    //Modified by REO 11/25/08: Null strings produce errors if written to device, so check
                    if (deviceSettings.DeviceDescription.Contains(conv.DeviceIdentifierSubstring) && conv.InitializationCommand != null)
                    {
                        commandBuffer.Add(new GpibCommand(AddNewlineCharacters(conv.InitializationCommand), 0));
                    }
                }
            }

            int currentStepIndex = -1;

            //measured in ticks. 1 tick = 100 ns.
            long currentTime = 0;


            // This functionality is sort of somewhat duplicated in sequencedata.generatebuffers. It would be good
            // to come up with a more coherent framework to do these sorts of operations.
            while (true)
            {
                currentStepIndex++;

                if (currentStepIndex >= sequence.TimeSteps.Count)
                {
                    break;
                }

                TimeStep currentStep = sequence.TimeSteps[currentStepIndex];

                if (!currentStep.StepEnabled)
                {
                    continue;
                }

                if (currentStep.GpibGroup == null || !currentStep.GpibGroup.channelEnabled(logicalChannelID))
                {
                    currentTime += Shared.SecondsToTicks(currentStep.StepDuration.getBaseValue());
                    continue;
                }

                long postTime = 0;
                if (currentStep.RetriggerOptions.WaitForRetrigger)
                {
                    postTime = postRetriggerTime;
                }

                // determine the index of the next step in which this channel has an action
                int nextEnabledStepIndex = sequence.findNextGpibChannelEnabledTimestep(currentStepIndex, logicalChannelID);

                long groupDuration = Shared.SecondsToTicks(sequence.timeBetweenSteps(currentStepIndex, nextEnabledStepIndex));

                // now take action:

                GPIBGroupChannelData channelData = currentStep.GpibGroup.getChannelData(logicalChannelID);

                if (channelData.DataType == GPIBGroupChannelData.GpibChannelDataType.raw_string)
                {
                    // Raw string commands just get added
                    string stringWithCorrectNewlines = AddNewlineCharacters(channelData.RawString);

                    commandBuffer.Add(new GpibCommand(stringWithCorrectNewlines, currentTime + postTime));
                }
                else if (channelData.DataType == GPIBGroupChannelData.GpibChannelDataType.voltage_frequency_waveform)
                {
                    GpibRampCommandConverter rampConverter = null;
                    switch (hc.GpibDeviceType)
                    {
                    case HardwareChannel.HardwareConstants.GPIBDeviceType.Unknown:
                    {
                        foreach (GpibRampCommandConverter conv in commandConverters)
                        {
                            if (deviceSettings.DeviceDescription.Contains(conv.DeviceIdentifierSubstring))
                            {
                                rampConverter = conv;
                            }
                        }
                        if (rampConverter == null)
                        {
                            throw new Exception("Voltage/frequency ramp not supported for unknown gpib device " + hc.ToString() + ".");
                        }
                    }
                    break;

                    case HardwareChannel.HardwareConstants.GPIBDeviceType.Agilent_ESG_SIG_Generator:
                    {
                        rampConverter = ESG_SeriesRampConverter;
                    }
                    break;
                    }
                    double[] amplitudeArray;
                    double[] frequencyArray;

                    // get amplitude and frequency value arrays
                    int    nSamples         = (int)(Shared.TicksToSeconds(groupDuration) * (double)deviceSettings.SampleClockRate);
                    double secondsPerSample = Shared.TicksToSeconds(groupDuration) / (double)nSamples;

                    amplitudeArray = channelData.volts.getInterpolation(nSamples, 0, Shared.TicksToSeconds(groupDuration), sequence.Variables, sequence.CommonWaveforms);
                    frequencyArray = channelData.frequency.getInterpolation(nSamples, 0, Shared.TicksToSeconds(groupDuration), sequence.Variables, sequence.CommonWaveforms);

                    List <DoubleIntPair> amplitudeIndexPairs = ConvertArrayToIndexValuePairs(amplitudeArray);
                    List <DoubleIntPair> frequencyIndexPairs = ConvertArrayToIndexValuePairs(frequencyArray);
                    int amplitudeIndex = 0;
                    int frequencyIndex = 0;
                    for (int i = 0; i < nSamples; i++)
                    {
                        bool amplitudeMatch = false;
                        bool frequencyMatch = false;

                        // determine if there is either a amplitude or frequency data point for this sample number
                        if (amplitudeIndex < amplitudeIndexPairs.Count)
                        {
                            amplitudeMatch = amplitudeIndexPairs[amplitudeIndex].myInt == i;
                        }
                        if (frequencyIndex < frequencyIndexPairs.Count)
                        {
                            frequencyMatch = frequencyIndexPairs[frequencyIndex].myInt == i;
                        }

                        if (amplitudeIndex >= amplitudeIndexPairs.Count && frequencyIndex >= frequencyIndexPairs.Count)
                        {
                            break;
                        }

                        if (amplitudeMatch || frequencyMatch)
                        {
                            string command = "";
                            if (amplitudeMatch)
                            {
                                command += rampConverter.amplitudeCommand(amplitudeIndexPairs[amplitudeIndex].myDouble);
                                amplitudeIndex++;
                            }
                            if (frequencyMatch)
                            {
                                command += rampConverter.frequencyCommand(frequencyIndexPairs[frequencyIndex].myDouble);
                                frequencyIndex++;
                            }

                            long commandTime = currentTime + Shared.SecondsToTicks((double)i * secondsPerSample) + postTime;


                            commandBuffer.Add(new GpibCommand(command, commandTime));
                        }
                    }
                }
                else if (channelData.DataType == GPIBGroupChannelData.GpibChannelDataType.string_param_string)
                {
                    if (channelData.StringParameterStrings != null)
                    {
                        foreach (StringParameterString sps in channelData.StringParameterStrings)
                        {
                            string commandWithCorrectNewlines = AddNewlineCharacters(sps.ToString());
                            commandBuffer.Add(new GpibCommand(commandWithCorrectNewlines, currentTime + postTime));
                        }
                    }
                }

                currentTime += Shared.SecondsToTicks(currentStep.StepDuration.getBaseValue());
            }

            return(true);
        }
Ejemplo n.º 12
0
        public bool generateBuffer(SequenceData sequence, DeviceSettings deviceSettings, HardwareChannel hc, int logicalChannelID)
        {
            lock (commandBuffer)
            {
                commandBuffer.Clear();
                this.logicalChannelID = logicalChannelID;


                if (deviceSettings.StartTriggerType != DeviceSettings.TriggerType.SoftwareTrigger)
                {
                    throw new Exception("RS232 devices must have a software start trigger.");
                }


                //     List<TimeStep> enabledSteps = sequence.enabledTimeSteps();

                int currentStepIndex = -1;

                //measured in ticks. 1 tick = 100 ns.
                long currentTime = 0;

                long postRetriggerTime = 100; // corresponds to 10us.
                // A workaround to issue when using software timed groups in
                // fpga-retriggered words

                // the workaround: delay the software timed group by an immesurable amount
                // if it is started in a retriggered word



                // This functionality is sort of somewhat duplicated in sequencedata.generatebuffers. It would be good
                // to come up with a more coherent framework to do these sorts of operations.
                while (true)
                {
                    currentStepIndex++;

                    if (currentStepIndex >= sequence.TimeSteps.Count)
                    {
                        break;
                    }

                    TimeStep currentStep = sequence.TimeSteps[currentStepIndex];

                    if (!currentStep.StepEnabled)
                    {
                        continue;
                    }

                    long postTime = 0;
                    if (currentStep.RetriggerOptions.WaitForRetrigger)
                    {
                        postTime = postRetriggerTime;
                    }

                    if (currentStep.rs232Group == null || !currentStep.rs232Group.channelEnabled(logicalChannelID))
                    {
                        currentTime += seconds_to_ticks(currentStep.StepDuration.getBaseValue());
                        continue;
                    }

                    // determine the index of the next step in which this channel has an action
                    int nextEnabledStepIndex = sequence.findNextRS232ChannelEnabledTimestep(currentStepIndex, logicalChannelID);

                    long groupDuration = seconds_to_ticks(sequence.timeBetweenSteps(currentStepIndex, nextEnabledStepIndex));

                    // now take action:

                    RS232GroupChannelData channelData = currentStep.rs232Group.getChannelData(logicalChannelID);

                    if (channelData.DataType == RS232GroupChannelData.RS232DataType.Raw)
                    {
                        // Raw string commands just get added
                        string stringWithCorrectNewlines = AddNewlineCharacters(channelData.RawString);

                        commandBuffer.Add(new RS232Command(stringWithCorrectNewlines, currentTime + postTime));
                    }
                    else if (channelData.DataType == RS232GroupChannelData.RS232DataType.Parameter)
                    {
                        if (channelData.StringParameterStrings != null)
                        {
                            foreach (StringParameterString srs in channelData.StringParameterStrings)
                            {
                                commandBuffer.Add(new RS232Command(
                                                      AddNewlineCharacters(srs.ToString()), currentTime + postTime));
                            }
                        }
                    }



                    currentTime += seconds_to_ticks(currentStep.StepDuration.getBaseValue());
                }
            }

            return(true);
        }
Ejemplo n.º 13
0
        public RfsgTask(SequenceData sequence, SettingsData settings, int channelID, string rfsgDeviceName, DeviceSettings deviceSettings)
        {
            if (!settings.logicalChannelManager.GPIBs.ContainsKey(channelID))
            {
                throw new InvalidDataException("Attempted to create an rfsg task with channel id " + channelID + ", which does not exist in the settings as a gpib channel.");
            }

            this.channelID = channelID;



            int currentStepIndex = -1;

            //measured in ticks. 1 tick = 100 ns.
            long currentTime = 0;

            commandBuffer = new List <RFSGCommand>();

            if (deviceSettings.AutoInitate)
            {
                RFSGCommand com = new RFSGCommand();
                com.commandTime = 0;
                com.commandType = RFSGCommand.CommandType.Initiate;
                commandBuffer.Add(com);
            }

            if (deviceSettings.AutoEnable)
            {
                RFSGCommand com = new RFSGCommand();
                com.commandTime = 0;
                com.commandType = RFSGCommand.CommandType.EnableOutput;
                commandBuffer.Add(com);
            }

            long postRetriggerTime = 100; // corresponds to 10us.

            // A workaround to issue when using software timed groups in
            // fpga-retriggered words

            // the workaround: delay the software timed group by an immesurable amount
            // if it is started in a retriggered word


            // This functionality is sort of somewhat duplicated in sequencedata.generatebuffers. It would be good
            // to come up with a more coherent framework to do these sorts of operations.
            while (true)
            {
                currentStepIndex++;

                if (currentStepIndex >= sequence.TimeSteps.Count)
                {
                    break;
                }

                TimeStep currentStep = sequence.TimeSteps[currentStepIndex];

                if (!currentStep.StepEnabled)
                {
                    continue;
                }

                if (currentStep.GpibGroup == null || !currentStep.GpibGroup.channelEnabled(channelID))
                {
                    currentTime += seconds_to_ticks(currentStep.StepDuration.getBaseValue());
                    continue;
                }

                long postTime = 0;
                if (currentStep.RetriggerOptions.WaitForRetrigger)
                {
                    postTime = postRetriggerTime;
                }

                // determine the index of the next step in which this channel has an action
                int nextEnabledStepIndex = sequence.findNextGpibChannelEnabledTimestep(currentStepIndex, channelID);

                long groupDuration = seconds_to_ticks(sequence.timeBetweenSteps(currentStepIndex, nextEnabledStepIndex));

                // now take action:

                GPIBGroupChannelData channelData = currentStep.GpibGroup.getChannelData(channelID);

                if (channelData.DataType == GPIBGroupChannelData.GpibChannelDataType.raw_string)
                {
                    throw new Exception("Not yet implemented.");

                    /*
                     *
                     * // Raw string commands just get added
                     * string stringWithCorrectNewlines = AddNewlineCharacters(channelData.RawString);
                     *
                     * commandBuffer.Add(new GpibCommand(stringWithCorrectNewlines, currentTime));*/
                }
                else if (channelData.DataType == GPIBGroupChannelData.GpibChannelDataType.voltage_frequency_waveform)
                {
                    double[] amplitudeArray;
                    double[] frequencyArray;

                    // get amplitude and frequency value arrays
                    int    nSamples         = (int)(ticks_to_seconds(groupDuration) * (double)deviceSettings.SampleClockRate);
                    double secondsPerSample = ticks_to_seconds(groupDuration) / (double)nSamples;

                    amplitudeArray = channelData.volts.getInterpolation(nSamples, 0, ticks_to_seconds(groupDuration), sequence.Variables, sequence.CommonWaveforms);
                    frequencyArray = channelData.frequency.getInterpolation(nSamples, 0, ticks_to_seconds(groupDuration), sequence.Variables, sequence.CommonWaveforms);

                    double lastFreq = Double.MinValue;
                    double lastAmp  = Double.MinValue;

                    for (int i = 0; i < nSamples; i++)
                    {
                        double currentFreq = frequencyArray[i];
                        double currentAmp  = amplitudeArray[i];
                        if (currentFreq != lastFreq || currentAmp != lastAmp)
                        {
                            RFSGCommand command = new RFSGCommand();
                            command.commandType = RFSGCommand.CommandType.AmplitudeFrequency;
                            command.frequency   = currentFreq;
                            command.amplitude   = Vpp_to_dBm(currentAmp);
                            command.commandTime = (long)(currentTime + i * secondsPerSample * 10000000) + postTime;

                            commandBuffer.Add(command);
                        }
                        lastAmp  = currentAmp;
                        lastFreq = currentFreq;
                    }
                }

                else if (channelData.DataType == GPIBGroupChannelData.GpibChannelDataType.string_param_string)
                {
                    foreach (StringParameterString sps in channelData.StringParameterStrings)
                    {
                        string clean = sps.Prefix.Trim().ToUpper();
                        if (clean == "ENABLE")
                        {
                            RFSGCommand com = new RFSGCommand();
                            com.commandType = RFSGCommand.CommandType.EnableOutput;
                            com.commandTime = currentTime + postTime;
                            commandBuffer.Add(com);
                        }

                        if (clean == "DISABLE")
                        {
                            RFSGCommand com = new RFSGCommand();
                            com.commandType = RFSGCommand.CommandType.DisableOutput;
                            com.commandTime = currentTime + postTime;
                            commandBuffer.Add(com);
                        }

                        if (clean == "ABORT")
                        {
                            RFSGCommand com = new RFSGCommand();
                            com.commandType = RFSGCommand.CommandType.Abort;
                            com.commandTime = currentTime + postTime;
                            commandBuffer.Add(com);
                        }

                        if (clean == "INITIATE")
                        {
                            RFSGCommand com = new RFSGCommand();
                            com.commandType = RFSGCommand.CommandType.Initiate;
                            com.commandTime = currentTime + postTime;
                            commandBuffer.Add(com);
                        }
                    }
                }

                currentTime += seconds_to_ticks(currentStep.StepDuration.getBaseValue());
            }


            if (rfsgDevices == null)
            {
                rfsgDevices         = new Dictionary <string, niRFSG>();
                rfsgDeviceInitiated = new Dictionary <niRFSG, bool>();
            }

            if (rfsgDevices.ContainsKey(rfsgDeviceName))
            {
                rfsgDevice = rfsgDevices[rfsgDeviceName];
            }
            else
            {
                try
                {
                    rfsgDevice = new niRFSG(rfsgDeviceName, true, false);
                }
                catch (Exception e)
                {
                    throw new InvalidDataException("Caught exception when attempting to instantiate an rfsg device named " + rfsgDeviceName + ". Maybe a device by this name does not exist? Exception message: " + e.Message);
                }
                rfsgDevices.Add(rfsgDeviceName, rfsgDevice);
                rfsgDeviceInitiated.Add(rfsgDevice, false);
            }


            if (deviceSettings.MasterTimebaseSource != null && deviceSettings.MasterTimebaseSource != "")
            {
                rfsgDevice.ConfigureRefClock(deviceSettings.MasterTimebaseSource, 10000000);
            }
        }
        /// <summary>
        /// This method is a sort of combination of createDaqMxVariableTimebaseSource and createDaqMxTask. It is intended 
        /// for use to create a digital output task that has both a variable timebase source on it, without having to discard
        /// all of the other channels on that port (and possibly on its neighboring port).
        /// 
        /// NOTE: No longer true. This function can not create the variable timebase outputs at the same time as
        /// normal outputs. If you attempt to use digital outputs on the same half of the card as your variable timebase output,
        /// this method will complain.
        /// </summary>
        /// <param name="channelName">
        /// Name of the channel that will output the variable timebase clock.
        /// </param>
        /// <param name="portsToUse">
        /// A list of integers specifying the digital ports that this task will use. The task will automatically
        /// make use of the full port that the variable timebase clock belongs to. If portsToUse is null, then 
        /// this function will automatically use both this port and its neighboring port (0 with 1, 2 with 3, etc).
        /// The rationale for this is that on some NI devices, these pairs of ports will share a sample clock and 
        /// cannot truly be used independently.
        /// </param>
        /// <param name="masterFrequency">
        /// The frequency, in hertz, of the master clock which will drive the variable timebase clock and the rest of the
        /// channels in this task.
        /// </param>
        /// <param name="sequenceData"></param>
        /// <param name="timebaseType"></param>
        /// <param name="deviceName"></param>
        /// <param name="deviceSettings"></param>
        /// <param name="sequence"></param>
        /// <param name="settings"></param>
        /// <param name="usedDigitalChannels"></param>
        /// <param name="usedAnalogChannels"></param>
        /// <param name="serverSettings"></param>
        /// <returns></returns>
        public static Task createDaqMxDigitalOutputAndVariableTimebaseSource(string channelName, List<int> portsToUse, int masterFrequency, 
            SequenceData sequenceData, SequenceData.VariableTimebaseTypes timebaseType,
            string deviceName, DeviceSettings deviceSettings, SettingsData settings, Dictionary<int, HardwareChannel> usedDigitalChannels,  ServerSettings serverSettings)
        {
            // First generate the variable timebase buffer. We will need stuff like its length for configuring the task, which is why we do this first.

            TimestepTimebaseSegmentCollection timebaseSegments = sequenceData.generateVariableTimebaseSegments(timebaseType, 1.0 / deviceSettings.SampleClockRate);
            bool[] variableTimebaseBuffer = sequenceData.getVariableTimebaseClock(timebaseSegments);

            if (deviceName.ToUpper() != HardwareChannel.parseDeviceNameStringFromPhysicalChannelString(channelName).ToUpper())
            {
                throw new Exception("The variable timebase device " + HardwareChannel.parseDeviceNameStringFromPhysicalChannelString(channelName) + " does not match device " + deviceName + ". These must match for their their task to be created together.");
            }

            int timebasePortNum;
            int timebaseLineNum;

            try
            {
                timebasePortNum = HardwareChannel.parsePortNumberFromChannelString(channelName);
                timebaseLineNum = HardwareChannel.parseLineNumberFromChannelString(channelName);
            }
            catch (Exception)
            {
                throw new Exception("Channel name " + channelName + " is not a valid digital channel name. Cannot create a variable timebase output on this channel.");
            }

            if (portsToUse == null)
            {
                portsToUse = new List<int>();
                portsToUse.Add(timebasePortNum);
                int spousePort; // this port is likely to have a shared sample clock with timebasePortNum,
                // at least in my experience so far.
                if (timebasePortNum % 2 == 0)
                    spousePort = timebasePortNum + 1;
                else
                    spousePort = timebasePortNum - 1;

                portsToUse.Add(spousePort);
            }

            if (!portsToUse.Contains(timebasePortNum))
            {
                portsToUse.Add(timebasePortNum);
            }

            bool otherChannelsUsedOnUsedPort = false;
            foreach (HardwareChannel hc in usedDigitalChannels.Values)
            {
                if (hc.DeviceName.ToUpper() == deviceName.ToUpper())
                {
                    if (portsToUse.Contains(hc.daqMxDigitalPortNumber()))
                        otherChannelsUsedOnUsedPort = true;
                }
            }

            if (otherChannelsUsedOnUsedPort)
            {
                throw new Exception("Variable timebase channel is on a port that shares a sample clock with a used output channel (on most devices, port 0 and 1 have a shared clock, and port 2 and 3 have a shared clock). This usage is not recommended, and not currently supported. Aborting buffer generation.");

                #region Deprecated code

                /*
                Task task = new Task("Variable timebase output task");

                // Create channels in the task
                foreach (int portNum in portsToUse)
                {
                    task.DOChannels.CreateChannel(deviceName + '/' + HardwareChannel.digitalPhysicalChannelName(portNum), "", ChannelLineGrouping.OneChannelForAllLines);
                }

                // Configure the task...

                task.Timing.ConfigureSampleClock("", masterFrequency, SampleClockActiveEdge.Rising, SampleQuantityMode.FiniteSamples, variableTimebaseBuffer.Length);

                if (serverSettings.VariableTimebaseTriggerInput != "")
                {
                    task.Triggers.StartTrigger.ConfigureDigitalEdgeTrigger(serverSettings.VariableTimebaseTriggerInput, DigitalEdgeStartTriggerEdge.Rising);
                }

                // Figure out which ports we are going to use, and which digital ID each line on each of those ports
                // maps to.
                Dictionary<int, HardwareChannel> digitalChannelsToUse = getChannelsOnDevice(usedDigitalChannels, deviceName);
                List<int> temp = new List<int>(digitalChannelsToUse.Keys);
                foreach (int id in temp)
                {
                    HardwareChannel ch = digitalChannelsToUse[id];
                    if (!portsToUse.Contains(HardwareChannel.parsePortNumberFromChannelString(ch.ChannelName)))
                    {
                        digitalChannelsToUse.Remove(id);
                    }
                }

                // Remove all of the digital channels this buffer generation is consuming from the
                // usedDigitalChannels dictionary. Why? Because there may still be another task to
                // generate on this device, and this is a way of keeping track of which channels
                // have already had their buffers generated.
                // Since digitalChannelsToUse gets passed by reference from AtticusServerRuntime.generateBuffers(...),
                // these changes thus get communicated back to AtticusServerRuntime.
                foreach (HardwareChannel hc in digitalChannelsToUse.Values)
                {
                    foreach (int i in usedDigitalChannels.Keys)
                    {
                        if (usedDigitalChannels[i] == hc)
                        {
                            usedDigitalChannels.Remove(i);
                            break;
                        }
                    }
                }

                List<int> ids = new List<int>(digitalChannelsToUse.Keys);
                ids.Sort();
                List<HardwareChannel> hcs = new List<HardwareChannel>();
                foreach (int id in ids)
                {
                    hcs.Add(digitalChannelsToUse[id]);
                }

                Dictionary<int, int[]> port_digital_ids;
                List<int> usedPorts;

                groupDigitalChannels(ids, hcs, out port_digital_ids, out usedPorts);

                // now to generate the buffers.

                if (usedPorts.Count != 0)
                {
                    byte[,] digitalBuffer;

                    try
                    {
                        digitalBuffer = new byte[usedPorts.Count, variableTimebaseBuffer.Length];
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Unable to allocate digital buffer for device " + deviceName + ". Reason: " + e.Message + "\n" + e.StackTrace);
                    }

                    for (int i = 0; i < usedPorts.Count; i++)
                    {
                        int portNum = usedPorts[i];
                        byte digitalBitMask = 1;
                        for (int lineNum = 0; lineNum < 8; lineNum++)
                        {
                            bool[] singleChannelBuffer = null;

                            if (portNum == timebasePortNum && lineNum == timebaseLineNum)
                            { // this current line is the variable timebase...
                                singleChannelBuffer = variableTimebaseBuffer;
                            }
                            else
                            {
                                int digitalID = port_digital_ids[portNum][lineNum];
                                if (digitalID != -1)
                                {
                                    if (settings.logicalChannelManager.Digitals[digitalID].overridden)
                                    {
                                        singleChannelBuffer = new bool[variableTimebaseBuffer.Length];
                                        if (settings.logicalChannelManager.Digitals[digitalID].digitalOverrideValue)
                                        {
                                            for (int j = 0; j < singleChannelBuffer.Length; j++)
                                            {
                                                singleChannelBuffer[j] = true;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        singleChannelBuffer = sequenceData.getDigitalBufferClockSharedWithVariableTimebaseClock(timebaseSegments, digitalID, 1.0 / deviceSettings.SampleClockRate);
                                    }
                                }
                            }

                            if (singleChannelBuffer != null)
                            {
                                for (int j = 0; j < singleChannelBuffer.Length; j++)
                                {
                                    if (singleChannelBuffer[j])
                                    {
                                        digitalBuffer[i, j] |= digitalBitMask;
                                    }
                                }
                            }
                            digitalBitMask = (byte)(digitalBitMask << 1);

                        }
                    }
                    System.GC.Collect();
                    DigitalMultiChannelWriter writer = new DigitalMultiChannelWriter(task.Stream);
                    writer.WriteMultiSamplePort(false, digitalBuffer);

                }

                return task; */

                #endregion
            }
            else
            {
                return createDaqMxVariableTimebaseSource(
                    channelName, masterFrequency, sequenceData, timebaseType, serverSettings, deviceSettings);
            }
        }
        /// <summary>
        /// Creates a task for a variable timebase output. Consumes the entire port (8 bits) that the timebase is on. (ie outputs the
        /// signal on all 8 bits
        /// </summary>
        /// <param name="channelName"></param>
        /// <param name="masterFrequency"></param>
        /// <param name="sequenceData"></param>
        /// <param name="timebaseType"></param>
        /// <returns></returns>
        public static Task createDaqMxVariableTimebaseSource(string channelName, int masterFrequency, SequenceData sequenceData,
                                                             SequenceData.VariableTimebaseTypes timebaseType, ServerSettings serverSettings, DeviceSettings deviceSettings)
        {
            Task task = new Task("Variable timebase output task");

            TimestepTimebaseSegmentCollection timebaseSegments = sequenceData.generateVariableTimebaseSegments(timebaseType,
                                                                                                               Common.getPeriodFromFrequency(masterFrequency));

            bool [] buffer = sequenceData.getVariableTimebaseClock(timebaseSegments);

            string timebaseDeviceName = HardwareChannel.parseDeviceNameStringFromPhysicalChannelString(channelName);

            string timebasePort = HardwareChannel.parsePortStringFromChannelString(channelName);

            task.DOChannels.CreateChannel(timebasePort, "", ChannelLineGrouping.OneChannelForAllLines);

            task.Timing.ConfigureSampleClock("", (double)masterFrequency, deviceSettings.ClockEdge, SampleQuantityMode.FiniteSamples, buffer.Length);

            if (serverSettings.VariableTimebaseTriggerInput != "")
            {
                task.Triggers.StartTrigger.ConfigureDigitalEdgeTrigger(serverSettings.VariableTimebaseTriggerInput, DigitalEdgeStartTriggerEdge.Rising);
            }

            DigitalSingleChannelWriter writer = new DigitalSingleChannelWriter(task.Stream);

            byte[] byteBuffer = new byte[buffer.Length];
            for (int j = 0; j < buffer.Length; j++)
            {
                if (buffer[j])
                {
                    byteBuffer[j] = 255;
                }
            }

            writer.WriteMultiSamplePort(false, byteBuffer);


            return(task);
        }
        /*
        /// <summary>
        /// This class contains both the analog and the digital tasks returned from a specific device.
        /// The reason for its existence is that if a device is to have both its analog and its digital outputs used,
        /// then they must exist in separate tasks.
        /// </summary>
        ///
        public class TaskCollection
        {
            public Task analogTask;
            public Task digitalTask;
        }
        */
        /*
        public static Task createVariableTimebaseTask(string digitalTimebaseOutLine, string analogTimebaseOutLine)
        {

        }*/
        ///
        /// This method creates a daqMX task for an "output now" command, and starts the output.
        /// </summary>
        /// <param name="deviceName"></param>
        /// <param name="settings"></param>
        /// <param name="output"></param>
        /// <returns></returns>
        public static Task createDaqMxTaskAndOutputNow(string deviceName, DeviceSettings deviceSettings, SingleOutputFrame output, 
            SettingsData settings, Dictionary<int, HardwareChannel> usedDigitalChannels, Dictionary<int, HardwareChannel> usedAnalogChannels)
        {
            Task task = new Task(deviceName + " output task");

            List<int> analogIDs;
            List<HardwareChannel> analogs;
            Dictionary<int, int[]> port_digital_IDs;
            List<int> usedPortNumbers;

            // Parse and create channels.
            parseAndCreateChannels(deviceName,deviceSettings, usedDigitalChannels, usedAnalogChannels, task, out analogIDs, out analogs, out port_digital_IDs, out usedPortNumbers);

            // now create buffer.

            task.Timing.SampleTimingType = SampleTimingType.OnDemand;

            // analog output
            if (analogIDs.Count != 0)
            {

                // extract a list of analog values corresponding to the list analodIDs. This is
                // sorted in the same way as the channels were created in parseAndCreateChannels
                List<double> outputValues = new List<double>();
                foreach (int analogID in analogIDs)
                {
                    double val;
                    if (output.analogValues.ContainsKey(analogID))
                        val = output.analogValues[analogID];
                    else
                        val = 0;
                    outputValues.Add(val);
                }

                AnalogMultiChannelWriter writer = new AnalogMultiChannelWriter(task.Stream);
                writer.WriteSingleSample(true, outputValues.ToArray());
            }

            // digital output
            if (usedPortNumbers.Count != 0)
            {
                List<byte> outputValues = new List<byte>();

                foreach (int portNumber in usedPortNumbers)
                {
                    byte digitalMask = 1;
                    byte value=0;
                    for (int lineNum = 0; lineNum < 8; lineNum++)
                    {
                        int digitalID = port_digital_IDs[portNumber][lineNum];
                        if (digitalID != -1)
                        {
                            bool val = false;
                            if (output.digitalValues.ContainsKey(digitalID))
                                val = output.digitalValues[digitalID];

                            if (val)
                                value |= digitalMask;
                        }
                        digitalMask = (byte) (digitalMask << 1);
                    }

                    outputValues.Add(value);
                }

                DigitalMultiChannelWriter writer = new DigitalMultiChannelWriter(task.Stream);
                writer.WriteSingleSamplePort(true, outputValues.ToArray());
            }

            return task;
        }
        /*
         * /// <summary>
         * /// This class contains both the analog and the digital tasks returned from a specific device.
         * /// The reason for its existence is that if a device is to have both its analog and its digital outputs used,
         * /// then they must exist in separate tasks.
         * /// </summary>
         * ///
         * public class TaskCollection
         * {
         *  public Task analogTask;
         *  public Task digitalTask;
         * }
         */

        /*
         * public static Task createVariableTimebaseTask(string digitalTimebaseOutLine, string analogTimebaseOutLine)
         * {
         *
         * }*/

        ///
        /// This method creates a daqMX task for an "output now" command, and starts the output.
        /// </summary>
        /// <param name="deviceName"></param>
        /// <param name="settings"></param>
        /// <param name="output"></param>
        /// <returns></returns>


        public static Task createDaqMxTaskAndOutputNow(string deviceName, DeviceSettings deviceSettings, SingleOutputFrame output,
                                                       SettingsData settings, Dictionary <int, HardwareChannel> usedDigitalChannels, Dictionary <int, HardwareChannel> usedAnalogChannels)
        {
            Task task = new Task(deviceName + " output task");

            List <int>              analogIDs;
            List <HardwareChannel>  analogs;
            Dictionary <int, int[]> port_digital_IDs;
            List <int>              usedPortNumbers;

            // Parse and create channels.
            parseAndCreateChannels(deviceName, deviceSettings, usedDigitalChannels, usedAnalogChannels, task, out analogIDs, out analogs, out port_digital_IDs, out usedPortNumbers);


            // now create buffer.

            task.Timing.SampleTimingType = SampleTimingType.OnDemand;

            // analog output
            if (analogIDs.Count != 0)
            {
                // extract a list of analog values corresponding to the list analodIDs. This is
                // sorted in the same way as the channels were created in parseAndCreateChannels
                List <double> outputValues = new List <double>();
                foreach (int analogID in analogIDs)
                {
                    double val;
                    if (output.analogValues.ContainsKey(analogID))
                    {
                        val = output.analogValues[analogID];
                    }
                    else
                    {
                        val = 0;
                    }
                    outputValues.Add(val);
                }

                AnalogMultiChannelWriter writer = new AnalogMultiChannelWriter(task.Stream);
                writer.WriteSingleSample(true, outputValues.ToArray());
            }

            // digital output
            if (usedPortNumbers.Count != 0)
            {
                List <byte> outputValues = new List <byte>();

                foreach (int portNumber in usedPortNumbers)
                {
                    byte digitalMask = 1;
                    byte value       = 0;
                    for (int lineNum = 0; lineNum < 8; lineNum++)
                    {
                        int digitalID = port_digital_IDs[portNumber][lineNum];
                        if (digitalID != -1)
                        {
                            bool val = false;
                            if (output.digitalValues.ContainsKey(digitalID))
                            {
                                val = output.digitalValues[digitalID];
                            }

                            if (val)
                            {
                                value |= digitalMask;
                            }
                        }
                        digitalMask = (byte)(digitalMask << 1);
                    }

                    outputValues.Add(value);
                }

                DigitalMultiChannelWriter writer = new DigitalMultiChannelWriter(task.Stream);
                writer.WriteSingleSamplePort(true, outputValues.ToArray());
            }

            return(task);
        }
        /// <summary>
        /// This function creates channels in the given task, for the named device.
        /// </summary>
        /// <param name="deviceName">
        /// The name of the device which the channels will will be created for. Eg Dev1
        /// </param>
        /// <param name="usedDigitalChannels"></param>
        /// A dictionary, indexed by logical ID#, of all of the digital hardware channels parsed from parsed from settings data which
        /// reside on this server.
        /// <param name="usedAnalogChannels">
        /// A dictionary, indexed by logical ID#, of all of the analog hardware channels parsed from the settings data, which reside on this server.
        /// </param>
        /// <param name="task">The task for which channels will be created.</param>
        /// <param name="analogIDs">
        /// An out parameter, which will store a list of the analog IDs that were created, in the order created.
        /// </param>
        /// <param name="analogs">
        /// An out parameter, which will store a list of the analog hardware channels corresponding to the channels created, in the order created.
        /// </param>
        /// <param name="port_digital_IDs"></param>
        /// An out parameter, which will store a dictionary mapping a digital port number to an 8-length array of integers specifying the logical ID# of each bit of the 
        /// given port #. Entries of -1 in this array indicate bits that have no logical ID# assigned. See **** in this method's source code for further information.
        /// <param name="usedPortNumbers">
        /// An out parameter, which will store a list of integers corresponding to the digital port numbers that were used on this device, in the order created.
        /// </param>
        private static void parseAndCreateChannels(string deviceName,DeviceSettings deviceSettings, Dictionary<int, HardwareChannel> usedDigitalChannels, Dictionary<int, HardwareChannel> usedAnalogChannels, Task task, out List<int> analogIDs, out List<HardwareChannel> analogs, out Dictionary<int, int[]> port_digital_IDs, out List<int> usedPortNumbers)
        {
            // figure out which of the analog and digital channels belong on this device. Add them here and index by
            // logical ID#
            Dictionary<int, HardwareChannel> analogsUnsorted = getChannelsOnDevice(usedAnalogChannels, deviceName);
            Dictionary<int, HardwareChannel> digitalsUnsorted = getChannelsOnDevice(usedDigitalChannels, deviceName);

            // sort the lists by ID
            analogIDs = new List<int>();
            analogs = new List<HardwareChannel>();
            sortDicionaryByID(analogIDs, analogs, analogsUnsorted);

            // list of the digital IDs of channels on this device
            List<int> digitalIDs = new List<int>();
            // list of the corresponding hardware channels
            List<HardwareChannel> digitals = new List<HardwareChannel>();
            sortDicionaryByID(digitalIDs, digitals, digitalsUnsorted);

            // ****
            // description of port_digital_IDs:
            // mapping from port number to an array of integers. The nth element of the array gives the digital ID
            // of the channel which is connected to the nth line of that port. If there is no channel on that port,
            // the ID is -1.

            if (deviceSettings.DeviceDescription.Contains("6533"))
            groupDigitalChannels(digitalIDs, digitals, out port_digital_IDs, out usedPortNumbers,true);
            else
            groupDigitalChannels(digitalIDs, digitals, out port_digital_IDs, out usedPortNumbers, false);

            //ok! create the channels.

            // analog first
            for (int i = 0; i < analogs.Count; i++)
            {
                task.AOChannels.CreateVoltageChannel(analogs[i].physicalChannelName(), "", -10, 10, AOVoltageUnits.Volts);
            }

            // now digital
            for (int i = 0; i < usedPortNumbers.Count; i++)
            {
                int portNum = usedPortNumbers[i];
                task.DOChannels.CreateChannel(deviceName + '/' + HardwareChannel.digitalPhysicalChannelName(portNum), "", ChannelLineGrouping.OneChannelForAllLines);
            }
        }
Ejemplo n.º 19
0
        public bool generateBuffer(SequenceData sequence, DeviceSettings deviceSettings, HardwareChannel hc, int logicalChannelID, List<GpibRampCommandConverter> commandConverters)
        {
            this.logicalChannelID = logicalChannelID;
            this.deviceType = hc.GpibDeviceType;
            commandBuffer = new List<GpibCommand>();

            if (deviceSettings.StartTriggerType != DeviceSettings.TriggerType.SoftwareTrigger)
            {
                throw new Exception("GPIB devices must have a software start trigger.");
            }

            // start by adding the initialization string to the command buffer
            // this does nothing for unknown device types
            // Modified by REO 11/25/08: Null strings produce errors if written to device, so check
            if(HardwareChannel.HardwareConstants.gpibInitializationCommands[(int)deviceType]!= null)
                commandBuffer.Add(new GpibCommand(HardwareChannel.HardwareConstants.gpibInitializationCommands[(int)deviceType], 0));

               if (deviceType == HardwareChannel.HardwareConstants.GPIBDeviceType.Unknown)
               {
               foreach (GpibRampCommandConverter conv in commandConverters)
               {
                   //Modified by REO 11/25/08: Null strings produce errors if written to device, so check
                   if (deviceSettings.DeviceDescription.Contains(conv.DeviceIdentifierSubstring) && conv.InitializationCommand != null)
                       commandBuffer.Add(new GpibCommand(AddNewlineCharacters(conv.InitializationCommand), 0));
               }
               }

               int currentStepIndex = - 1;

               //measured in ticks. 1 tick = 100 ns.
               long currentTime = 0;

               // This functionality is sort of somewhat duplicated in sequencedata.generatebuffers. It would be good
               // to come up with a more coherent framework to do these sorts of operations.
               while (true)
               {
               currentStepIndex++;

               if (currentStepIndex >= sequence.TimeSteps.Count)
                   break;

               TimeStep currentStep = sequence.TimeSteps[currentStepIndex];

               if (!currentStep.StepEnabled)
                   continue;

               if (currentStep.GpibGroup == null || !currentStep.GpibGroup.channelEnabled(logicalChannelID))
               {
                   currentTime += seconds_to_ticks(currentStep.StepDuration.getBaseValue());
                   continue;
               }

               // determine the index of the next step in which this channel has an action
               int nextEnabledStepIndex = sequence.findNextGpibChannelEnabledTimestep(currentStepIndex, logicalChannelID);

               long groupDuration = seconds_to_ticks(sequence.timeBetweenSteps(currentStepIndex, nextEnabledStepIndex));

               // now take action:

               GPIBGroupChannelData channelData = currentStep.GpibGroup.getChannelData(logicalChannelID);

               if (channelData.DataType == GPIBGroupChannelData.GpibChannelDataType.raw_string) {
                       // Raw string commands just get added
                       string stringWithCorrectNewlines = AddNewlineCharacters(channelData.RawString);

                       commandBuffer.Add(new GpibCommand(  stringWithCorrectNewlines, currentTime));
               }
               else if (channelData.DataType == GPIBGroupChannelData.GpibChannelDataType.voltage_frequency_waveform)
               {
                   GpibRampCommandConverter rampConverter = null;
                   switch (hc.GpibDeviceType)
                   {
                       case HardwareChannel.HardwareConstants.GPIBDeviceType.Unknown:
                           {
                               foreach (GpibRampCommandConverter conv in commandConverters)
                               {
                                   if (deviceSettings.DeviceDescription.Contains(conv.DeviceIdentifierSubstring))
                                       rampConverter = conv;
                               }
                               if (rampConverter == null)
                               {
                                   throw new Exception("Voltage/frequency ramp not supported for unknown gpib device " + hc.ToString() + ".");
                               }

                           }
                           break;
                       case HardwareChannel.HardwareConstants.GPIBDeviceType.Agilent_ESG_SIG_Generator:
                           {
                               rampConverter = ESG_SeriesRampConverter;
                           }
                           break;
                   }
                   double[] amplitudeArray;
                   double[] frequencyArray;

                   // get amplitude and frequency value arrays
                   int nSamples = (int)(ticks_to_seconds(groupDuration) * (double)deviceSettings.SampleClockRate);
                   double secondsPerSample = ticks_to_seconds(groupDuration) / (double) nSamples;

                   amplitudeArray = channelData.volts.getInterpolation(nSamples, 0, ticks_to_seconds(groupDuration), sequence.Variables, sequence.CommonWaveforms);
                   frequencyArray = channelData.frequency.getInterpolation(nSamples, 0, ticks_to_seconds(groupDuration), sequence.Variables, sequence.CommonWaveforms);

                   List<DoubleIntPair> amplitudeIndexPairs = ConvertArrayToIndexValuePairs(amplitudeArray);
                   List<DoubleIntPair> frequencyIndexPairs = ConvertArrayToIndexValuePairs(frequencyArray);
                   int amplitudeIndex = 0;
                   int frequencyIndex = 0;
                   for (int i = 0; i < nSamples; i++)
                   {

                       bool amplitudeMatch = false;
                       bool frequencyMatch = false;

                       // determine if there is either a amplitude or frequency data point for this sample number
                       if (amplitudeIndex < amplitudeIndexPairs.Count)
                           amplitudeMatch = amplitudeIndexPairs[amplitudeIndex].myInt == i;
                       if (frequencyIndex < frequencyIndexPairs.Count)
                           frequencyMatch = frequencyIndexPairs[frequencyIndex].myInt == i;

                       if (amplitudeIndex >= amplitudeIndexPairs.Count && frequencyIndex >= frequencyIndexPairs.Count)
                           break;

                       if (amplitudeMatch || frequencyMatch)
                       {
                           string command = "";
                           if (amplitudeMatch)
                           {
                               command += rampConverter.amplitudeCommand(amplitudeIndexPairs[amplitudeIndex].myDouble);
                               amplitudeIndex++;
                           }
                           if (frequencyMatch)
                           {
                               command += rampConverter.frequencyCommand(frequencyIndexPairs[frequencyIndex].myDouble);
                               frequencyIndex++;
                           }

                           long commandTime = currentTime + seconds_to_ticks((double)i * secondsPerSample);

                           commandBuffer.Add(new GpibCommand(command, commandTime));
                       }

                   }

               }
               else if (channelData.DataType == GPIBGroupChannelData.GpibChannelDataType.string_param_string)
               {
                   if (channelData.StringParameterStrings != null)
                   {
                       foreach (StringParameterString sps in channelData.StringParameterStrings)
                       {
                           string commandWithCorrectNewlines = AddNewlineCharacters(sps.ToString());
                           commandBuffer.Add(new GpibCommand(commandWithCorrectNewlines, currentTime));
                       }
                   }
               }

               currentTime += seconds_to_ticks(currentStep.StepDuration.getBaseValue());
               }

               return true;
        }