public static void saveServerSettings(string fileName, ServerSettings serverSettings)
        {
            // Save server settings

            BinaryFormatter bf = new BinaryFormatter();
            FileStream fst = new FileStream(fileName, FileMode.Create);
            bf.Serialize(fst, serverSettings);
            fst.Close();
        }
        /// <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;
        }
        /// <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>
        /// 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;
        }
        public AtticusServerCommunicator(ServerSettings settings)
        {
            System.Console.WriteLine("Running AtticusServerRuntime constructor...");

            myServerSettings = settings;

            refreshHardwareLists();

            communicatorStatus = ServerStructures.ServerCommunicatorStatus.Disconnected;

            System.Console.WriteLine("Initializing Network clock broadcast and receiver threads.");

            DataStructures.Timing.NetworkClockBroadcaster.registerMessageLogHandler(this.messageLog);
            DataStructures.Timing.NetworkClockBroadcaster.startBroadcasterThreads();
            resetNetworkClocks();

            DataStructures.Timing.NetworkClockProvider.registerStaticMessageLogHandler(this.messageLog);
            bool created = DataStructures.Timing.NetworkClockProvider.startListener(DataStructures.Timing.NetworkClockEndpointInfo.HostTypes.Atticus_Server);
            if (!created)
            {
                messageLog(this, new MessageEvent("Unable to start network clock listener. Is it possible that a separate Atticus instance is running on this computer?",
                    0, MessageEvent.MessageTypes.Error, MessageEvent.MessageCategories.Networking));

            }

            System.Console.WriteLine("... done running AtticusServerRuntime constructor.");
        }
예제 #6
0
        static void Main()
        {
            /*
             * // demand high security permissions for Atticus. This may help improve performance?
             *
             * SecurityPermission perm = new SecurityPermission(SecurityPermissionFlag.AllFlags);
             * perm.Assert();
             *
             * */

            try
            {
                AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

                try
                {
                    NationalInstruments.NI4882.Address      testObject  = new NationalInstruments.NI4882.Address();
                    NationalInstruments.DAQmx.CouplingTypes testObject2 = new NationalInstruments.DAQmx.CouplingTypes();
                }
                catch (Exception)
                {
                    MessageBox.Show("Unable to load National Instruments DAQmx libraries. You may have an older version of these libraries installed. Please install the latest version of DAQmx. Press OK to see the detailed exception.", "Unable to load DAQmx libraries.");
                    throw;
                }



                // Maximum server uptime: 1000 days. Lets see if this solves some of those "disconnection" issues we had
                System.Runtime.Remoting.Lifetime.LifetimeServices.LeaseTime = new TimeSpan(1000, 0, 0, 0, 0);


                ServerSettings serverSettings;
                String         serverSettingsFilesName = FileNameStrings.DefaultServerSettingsDataFile;

                try
                {
                    serverSettings = loadServerSettings(serverSettingsFilesName);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception when attempting to load server settings.");
                    Console.WriteLine("Proceeding with blank settings.");
                    serverSettings = new ServerSettings();
                }


                Console.WriteLine("Creating AtticusServerRuntime object...");

                server = new AtticusServerCommunicator(serverSettings);

                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);

                MainServerForm form = new MainServerForm();

                server.updateGUI += form.updateGUI;
                server.registerMessageEventHandler(form.addMessageLogText);



                Application.Run(form);



                saveServerSettings(AppDomain.CurrentDomain.BaseDirectory + FileNameStrings.DefaultServerSettingsDataFile, serverSettings);
            }
            catch (Exception e)
            {
                display_unhandled_exception(e);
            }
        }
예제 #7
0
        /// <summary>
        /// Attempts to load server settings object. If one of the fixable System.ArgumentException
        /// arises, due to a naming incompatability between GPIB Address objects, attempts to fix that
        /// and continues loading. If any other exception occurs, the exception is rethrown.
        ///
        /// </summary>
        /// <param name="filename"></param>
        /// <returns>loaded server settings object</returns>
        private static ServerSettings loadServerSettings(string filename)
        {
            ServerSettings serverSettings = Common.loadBinaryObjectFromFile(filename) as ServerSettings;

            return(serverSettings);
        }
        /// <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));
            }
        }
        /// <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);
        }
        public AtticusServerCommunicator(ServerSettings settings)
        {
            System.Console.WriteLine("Running AtticusServerRuntime constructor...");

            myServerSettings = settings;

            refreshHardwareLists();

            communicatorStatus = ServerStructures.ServerCommunicatorStatus.Disconnected;

            // marshal the serverCommunicator if the start up settings say to do so.
            if (settings.ConnectOnStartup)
                reachMarshalStatus(ServerStructures.ServerCommunicatorStatus.Connected);

            System.Console.WriteLine("... done running AtticusServerRuntime constructor.");
        }
예제 #12
0
        static void Main()
        {
            /*
             * // demand high security permissions for Atticus. This may help improve performance?

            SecurityPermission perm = new SecurityPermission(SecurityPermissionFlag.AllFlags);
            perm.Assert();

             * */

            try
            {

                AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

                try
                {
                    NationalInstruments.NI4882.Address testObject = new NationalInstruments.NI4882.Address();
                    NationalInstruments.DAQmx.CouplingTypes testObject2 = new NationalInstruments.DAQmx.CouplingTypes();
                }
                catch (Exception)
                {
                    MessageBox.Show("Unable to load National Instruments DAQmx libraries. You may have an older version of these libraries installed. Please install the latest version of DAQmx. Press OK to see the detailed exception.", "Unable to load DAQmx libraries.");
                    throw;
                }

                // Maximum server uptime: 1000 days. Lets see if this solves some of those "disconnection" issues we had
                System.Runtime.Remoting.Lifetime.LifetimeServices.LeaseTime = new TimeSpan(1000, 0, 0, 0, 0);

                ServerSettings serverSettings;
                String serverSettingsFilesName = FileNameStrings.DefaultServerSettingsDataFile;

                try
                {
                    serverSettings = loadServerSettings(serverSettingsFilesName);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception when attempting to load server settings.");
                    Console.WriteLine("Proceeding with blank settings.");
                    serverSettings = new ServerSettings();
                }

                Console.WriteLine("Creating AtticusServerRuntime object...");

                server = new AtticusServerCommunicator(serverSettings);

                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);

                MainServerForm form = new MainServerForm();

                server.updateGUI += form.updateGUI;
                server.messageLog += form.addMessageLogText;

                Application.Run(form);

                saveServerSettings(AppDomain.CurrentDomain.BaseDirectory +  FileNameStrings.DefaultServerSettingsDataFile, serverSettings);
            }
            catch (Exception e)
            {
                display_unhandled_exception(e);
            }
        }