/// <summary>
 /// Generic digital data server for a given 32 bit port. The main data buffer that this class updates
 /// 'dataBuffer', itself a DigitalEventBuffer object.This method accepts a time range (in seconds referenced to the start of the recording)
 /// as input and will copy the portion of the current data buffer that is within that range to the user as a 
 /// DigitalEventBuffer object. The EstimateAvailableTimeRange method can be used to get an estimate of a valide range
 /// to enter for a Read operation. If there is no data in the time range provided, the method returns a null object.
 /// </summary>
 /// <param name="sampleFrequencyHz"> Sampling frequency of the DAQ that is feeding this server</param>
 /// <param name="bufferSizeSec">The requested history of the buffer in seconds</param>
 /// <param name="numSamplesPerWrite"> How many samples will the DAQ provide when a Write is called?</param>
 public DigitalEventSrv(double sampleFrequencyHz, double bufferSizeSec, int numSamplesPerWrite)
 {
     this.sampleFrequencyHz = sampleFrequencyHz;
     this.dataBuffer = new DigitalEventBuffer(sampleFrequencyHz);
     this.numSamplesPerWrite = numSamplesPerWrite;
     this.bufferSizeInSamples = (int)Math.Ceiling(bufferSizeSec * sampleFrequencyHz);
 }
        /// <summary>
        /// Write data to the Digital Event Server
        /// </summary>
        /// <param name="newData"> A digital event buffer containing the digital events to be added to the buffer.</param>
        internal void WriteToBuffer(DigitalEventBuffer newData)
        {
            lock (lockObj)
            {
                // First we must remove the expired samples (we cannot assume these are
                // in temporal order since for 64 channels, we have to write 2x, once for
                // each 32 channel recording task)
                int i = 0;
                while (i < dataBuffer.SampleBuffer.Count)
                {
                    // Remove expired data
                    if (dataBuffer.SampleBuffer.ElementAt(i) + (ulong)bufferSizeInSamples < currentSample)
                    {
                        dataBuffer.SampleBuffer.RemoveAt(i);
                        dataBuffer.PortStateBuffer.RemoveAt(i);
                    }
                }

                // Add new data
                dataBuffer.SampleBuffer.AddRange(newData.SampleBuffer);
                dataBuffer.PortStateBuffer.AddRange(newData.PortStateBuffer);

                // Update the most current sample read
                currentSample += (ulong)numSamplesPerWrite;
            }
        }
        /// <summary>
        /// Read data from buffer. This method will attempt to retrieve samples within the range
        /// specified by the input arguements. The object that is returned
        /// will contain information on the true sample bounds. You can use the EstimateAvailableTimeRange
        /// method to get a (time-sensitive) estimate for good-arguments for this method.
        /// </summary>
        /// <param name="desiredStartIndex">earliest sample, referenced to 0, that should be returned</param>
        /// <param name="desiredStopIndex">latest sample, referenced to 0, that should be returned</param>
        /// <returns>DigitalEventBuffer</returns>
        internal DigitalEventBuffer ReadFromBuffer(ulong desiredStartIndex, ulong desiredStopIndex)
        {
            lock (lockObj)
            {
                DigitalEventBuffer returnBuffer = new DigitalEventBuffer(dataBuffer.SampleFrequencyHz);

                // Collect all the data within the desired sample range and add to the returnBuffer
                // object
                int i = 0;
                while (i < dataBuffer.SampleBuffer.Count)
                {
                    if (dataBuffer.SampleBuffer.ElementAt(i) > desiredStartIndex &&
                        dataBuffer.SampleBuffer.ElementAt(i) <= desiredStopIndex)
                    {
                        returnBuffer.SampleBuffer.Add(dataBuffer.SampleBuffer.ElementAt(i));
                        returnBuffer.PortStateBuffer.Add(dataBuffer.PortStateBuffer.ElementAt(i));
                    }
                }

                // Return the data
                return returnBuffer;
            }
        }