/// <summary>
        /// Change stim rate to specific value.
        /// </summary>
        /// <param name="localSummit">Summit system</param>
        /// <param name="senseFriendly">True for sense friendly values or false for not</param>
        /// <param name="rateValueToChangeTo">Value to set stim rate to.</param>
        /// <param name="currentRateValue">Value of the current stim rate.</param>
        /// <returns>bool for success/failure and double for new stim rate. string give error message</returns>
        public async Task <Tuple <bool, double?, string> > ChangeStimRateToValue(SummitSystem localSummit, bool senseFriendly, double rateValueToChangeTo, double currentRateValue)
        {
            double?updatedStimRate = 0;

            if (localSummit == null || localSummit.IsDisposed)
            {
                return(Tuple.Create(false, updatedStimRate, "Summit Disposed"));
            }
            APIReturnInfo bufferReturnInfo;

            try
            {
                double rateToChangeTo = 0;
                rateToChangeTo = Math.Round(rateValueToChangeTo + (-1 * currentRateValue), 1);
                if (rateToChangeTo != 0)
                {
                    int counter = 5;
                    do
                    {
                        bufferReturnInfo = await Task.Run(() => localSummit.StimChangeStepFrequency(rateToChangeTo, senseFriendly, out updatedStimRate));

                        if (counter < 5)
                        {
                            Thread.Sleep(400);
                        }
                        counter--;
                    } while ((bufferReturnInfo.RejectCode != 0) && (bufferReturnInfo.RejectCode != 5) && counter > 0);
                    //Sense friendly rate not found, just return back original value
                    if (bufferReturnInfo.RejectCode == 5)
                    {
                        return(Tuple.Create(true, (double?)currentRateValue, "Success"));
                    }
                    if ((bufferReturnInfo.RejectCode != 0) && counter == 0)
                    {
                        _log.Warn(":: Error: Medtronic API return error changing stim rate to value: " + bufferReturnInfo.Descriptor + ". Reject Code: " + bufferReturnInfo.RejectCode);
                        return(Tuple.Create(false, updatedStimRate, "Error: Medtronic API return error changing stim rate to value: " + bufferReturnInfo.Descriptor + ".Reject Code: " + bufferReturnInfo.RejectCode));
                    }
                }
                else
                {
                    return(Tuple.Create(false, updatedStimRate, "Could not change stim rate to value"));
                }
            }
            catch (Exception e)
            {
                _log.Error(e);
                return(Tuple.Create(false, updatedStimRate, "Error changing stim rate to value"));
            }
            return(Tuple.Create(true, updatedStimRate, "Success"));
        }
        /// <summary>
        /// Increment or decrement the stim rate by step value
        /// </summary>
        /// <param name="localSummit">Summit system</param>
        /// <param name="senseFriendly">True for sense friendly values or false for not</param>
        /// <param name="rateStepValue">Value to adjust. Plus for increment or minus for decrement</param>
        /// <returns>bool for success/failure and double for new stim rate. string give error message</returns>
        public async Task <Tuple <bool, double?, string> > ChangeStimRateStep(SummitSystem localSummit, bool senseFriendly, double rateStepValue)
        {
            double?updatedStimRate = 0;

            if (localSummit == null || localSummit.IsDisposed)
            {
                return(Tuple.Create(false, updatedStimRate, "Summit Disposed"));
            }
            APIReturnInfo bufferReturnInfo;

            try
            {
                int counter = 5;
                do
                {
                    bufferReturnInfo = await Task.Run(() => localSummit.StimChangeStepFrequency(rateStepValue, senseFriendly, out updatedStimRate));

                    if (counter < 5)
                    {
                        Thread.Sleep(400);
                    }
                    counter--;
                } while ((bufferReturnInfo.RejectCode != 0) && counter > 0);
                if ((bufferReturnInfo.RejectCode != 0) && counter == 0)
                {
                    _log.Warn(":: Error: Medtronic API return error changing stim step rate: " + bufferReturnInfo.Descriptor + ". Reject Code: " + bufferReturnInfo.RejectCode);
                    return(Tuple.Create(false, updatedStimRate, "Error: Medtronic API return error changing stim step rate: " + bufferReturnInfo.Descriptor + ".Reject Code: " + bufferReturnInfo.RejectCode));
                }
            }
            catch (Exception e)
            {
                _log.Error(e);
                return(Tuple.Create(false, updatedStimRate, "Error changing stim step rate"));
            }
            return(Tuple.Create(true, updatedStimRate, "Success"));
        }
        /// <summary>
        /// Sweep through all the indicated stimulation amplitudes, pulse widths, and frequencies. If stimulation engine failure flag is raised, or if user
        /// pushes quit button, the sweep is aborted.
        /// </summary>
        public bool Sweep()
        {
            bool sweepAborted = false;

            //go through each parameter, put thier number of values, stim stop, and pause durations in the right order
            List <int> numValues = new List <int>()
            {
                0, 0, 0
            };
            List <int> pauseDurations = new List <int>()
            {
                0, 0, 0
            };

            List <int> maxIters = new List <int>(2);

            for (int iLevel = 0; iLevel < m_sweepParameters.paramOrder.Count; iLevel++)
            {
                numValues[m_sweepParameters.paramOrder[iLevel]]      = m_sweepParameters.paramNumValues[iLevel];
                pauseDurations[m_sweepParameters.paramOrder[iLevel]] = m_sweepParameters.paramPauseDurationMilliSeconds[iLevel];
            }

            //initialize current values
            if (!SummitUtils.CheckCurrentStimParameters(m_summit, m_groupNum, 0, out m_currentAmp, out m_currentPulseWidth, out m_currentFreq))
            {
                sweepAborted = true;
                return(sweepAborted);
            }

            //bring pulse width and freq to starting values:
            if (m_currentAmp != 0)
            {
                //amp should be 0 so there will be no stimulation when setting pw and freq. Set to 0 if it's not.
                Console.WriteLine(String.Format("Sweep initialzation amplitude should be 0, but it's {0}! Cannot do stim sweep", m_currentAmp));
                return(sweepAborted = true);
            }

            m_summitInfo = m_summit.StimChangeTherapyOn();
            // Reset POR if set
            if (m_summitInfo.RejectCodeType == typeof(MasterRejectCode) &&
                (MasterRejectCode)m_summitInfo.RejectCode == MasterRejectCode.ChangeTherapyPor)
            {
                // Inform user
                Console.WriteLine("POR set, resetting...");
                // Reset POR
                m_summitInfo = SummitUtils.resetPOR(m_summit);
            }
            Thread.Sleep(1000);
            try
            {
                ResetSweep();
            }
            catch
            {
                sweepAborted = true;
                return(sweepAborted);
            }

            //initialize a counter to 0's for the number of values we've already sweeped across for each parameter
            List <int> iterCounter = new List <int> {
                0, 0, 0
            };
            int iParameter, iAmp = 0, iPW = 0, iFreq = 0;

            //make a listener thread to check for terminations
            Thread stopListenerThread = new Thread(StopSweepListener);

            //start stimulation by bringing amp to first value
            m_sweepFinished = false;
            m_summitInfo    = m_summit.StimChangeStepAmp(0, m_sweepParameters.ampValues[0], out m_currentAmp);
            if (m_summitInfo.RejectCode != 0)
            {
                Console.WriteLine("Error when increasing stimulation amplitude to " + m_sweepParameters.ampValues[iAmp] + "mA. Error descriptor:" + m_summitInfo.Descriptor);
                if (!SummitUtils.CheckCurrentStimParameters(m_summit, m_groupNum, 0, out m_currentAmp, out m_currentPulseWidth, out m_currentFreq))
                {
                    sweepAborted = true;
                    return(sweepAborted);
                }
            }
            Console.WriteLine("Sweep started at: " + m_currentAmp
                              + "mA , " + m_currentPulseWidth
                              + "us pulse width, and " + m_currentFreq + " Hz");
            stopListenerThread.Start();

            Thread.Sleep(m_sweepParameters.permutationDuration);

            //now do iteration by increasing the counter for each parameter (until all the parameter's value's have all been iterated across)
            while (iterCounter[0] < (numValues[0] - 1) || iterCounter[1] < (numValues[1] - 1) || iterCounter[2] < (numValues[2] - 1))
            {
                //First do check here to see if the sweep has been stopped (either quit key has been pressed or stim engine out of range flag has been set)
                if (m_stopSweep)
                {
                    Console.WriteLine("Stimulation Sweep Aborted");
                    sweepAborted = true;
                    break;
                }

                if (iterCounter[0] < (numValues[0] - 1))
                {
                    try
                    {
                        //we're at innermost parameter
                        IntraSweepPause(pauseDurations[0]);
                        iterCounter[0]++;
                        iParameter = 0;
                    }
                    catch
                    {
                        sweepAborted = true;
                        return(sweepAborted);
                    }
                }
                else if (iterCounter[1] < (numValues[1] - 1))
                {
                    //we're at the middle parameter
                    try
                    {
                        IntraSweepPause(pauseDurations[1]);
                        iterCounter[1]++;
                        iParameter = 1;

                        //reset innermost parameter
                        ResetParameter(0, ref iAmp, ref iPW, ref iFreq);
                        iterCounter[0] = 0;
                    }
                    catch
                    {
                        sweepAborted = true;
                        return(sweepAborted);
                    }
                    Console.WriteLine();
                }
                else
                {
                    try
                    {
                        //we're at the outermost parameter
                        IntraSweepPause(pauseDurations[2]);
                        iterCounter[2]++;
                        iParameter = 2;

                        //reset innermost and middle parameter
                        ResetParameter(0, ref iAmp, ref iPW, ref iFreq);
                        ResetParameter(1, ref iAmp, ref iPW, ref iFreq);
                        iterCounter[0] = 0;
                        iterCounter[1] = 0;
                    }
                    catch
                    {
                        sweepAborted = true;
                        return(sweepAborted);
                    }
                    Console.WriteLine();
                }


                // increment the parameter we're at in the sweep
                if (iParameter == m_sweepParameters.ampOrder)
                {
                    //increase stim amp
                    iAmp++;

                    m_summitInfo = m_summit.StimChangeStepAmp(0, Math.Round(m_sweepParameters.ampValues[iAmp] - m_currentAmp.Value, 1), out m_currentAmp);
                    if (m_summitInfo.RejectCode != 0)
                    {
                        Console.WriteLine("Error when increasing stimulation amplitude to " + m_sweepParameters.ampValues[iAmp] + "mA. Error descriptor:" + m_summitInfo.Descriptor);
                        if (!SummitUtils.CheckCurrentStimParameters(m_summit, m_groupNum, 0, out m_currentAmp, out m_currentPulseWidth, out m_currentFreq))
                        {
                            sweepAborted = true;
                            return(sweepAborted);
                        }
                    }

                    Console.WriteLine("Increased amplitude to " + m_currentAmp + "mA");

                    m_stimPaused = false;
                    Thread.Sleep(m_sweepParameters.permutationDuration);
                }
                else if (iParameter == m_sweepParameters.pulseWidthOrder)
                {
                    //increase pulse duration
                    iPW++;
                    m_summitInfo = m_summit.StimChangeStepPW(0, m_sweepParameters.pulseWidthValues[iPW] - m_currentPulseWidth.Value, out m_currentPulseWidth);

                    if (m_summitInfo.RejectCode != 0)
                    {
                        Console.WriteLine("Error when increasing stimulation pulse width to " + m_sweepParameters.pulseWidthValues[iPW] + "us. Error descriptor:" + m_summitInfo.Descriptor);
                        if (!SummitUtils.CheckCurrentStimParameters(m_summit, m_groupNum, 0, out m_currentAmp, out m_currentPulseWidth, out m_currentFreq))
                        {
                            sweepAborted = true;
                            return(sweepAborted);
                        }
                    }

                    Console.WriteLine("Increased pulse width to " + m_currentPulseWidth + "us");

                    //bring stimulation back, if it was turned off for pause
                    if (m_stimPaused)
                    {
                        m_summitInfo = m_summit.StimChangeStepAmp(0, m_prePauseAmp, out m_currentAmp);

                        if (m_summitInfo.RejectCode != 0)
                        {
                            Console.WriteLine("Error when increasing stimulation amplitude back after pulse width increase. Error descriptor:" + m_summitInfo.Descriptor);
                            if (!SummitUtils.CheckCurrentStimParameters(m_summit, m_groupNum, 0, out m_currentAmp, out m_currentPulseWidth, out m_currentFreq))
                            {
                                sweepAborted = true;
                                return(sweepAborted);
                            }
                        }

                        m_stimPaused = false;
                    }
                    Thread.Sleep(m_sweepParameters.permutationDuration);
                }
                else if (iParameter == m_sweepParameters.freqOrder)
                {
                    //increase frequency
                    iFreq++;

                    m_summitInfo = m_summit.StimChangeStepFrequency(Math.Round(m_sweepParameters.freqValues[iFreq] - m_currentFreq.Value, 1), false, out m_currentFreq);

                    if (m_summitInfo.RejectCode != 0)
                    {
                        Console.WriteLine("Error when increasing stimulation frequency to " + m_sweepParameters.freqValues[iFreq] + "Hz. Error descriptor:" + m_summitInfo.Descriptor);
                        if (!SummitUtils.CheckCurrentStimParameters(m_summit, m_groupNum, 0, out m_currentAmp, out m_currentPulseWidth, out m_currentFreq))
                        {
                            sweepAborted = true;
                            return(sweepAborted);
                        }
                    }

                    Console.WriteLine("Increased frequency to " + m_currentFreq + "Hz");

                    //bring stimulation back, if it was turned off for pause
                    if (m_stimPaused)
                    {
                        m_summitInfo = m_summit.StimChangeStepAmp(0, m_prePauseAmp, out m_currentAmp);

                        if (m_summitInfo.RejectCode != 0)
                        {
                            Console.WriteLine("Error when increasing stimulation amplitude back after frequency increase. Error descriptor:" + m_summitInfo.Descriptor);
                            if (!SummitUtils.CheckCurrentStimParameters(m_summit, m_groupNum, 0, out m_currentAmp, out m_currentPulseWidth, out m_currentFreq))
                            {
                                sweepAborted = true;
                                return(sweepAborted);
                            }
                        }

                        m_stimPaused = false;
                    }
                    Thread.Sleep(m_sweepParameters.permutationDuration);
                }

                //just call some function to interragate INS to allow any OOR info to arrive (add 100 ms sleep to all time for setting the abort flags)
                TherapyGroup groupInfo = new TherapyGroup();
                m_summit.ReadStimGroup(m_groupNum, out groupInfo);
                Thread.Sleep(100);
            }

            //ok sweep finished, reset all parametrs and flags, and join listener thread
            m_stimOutOfRangeFlag = false;
            try
            {
                ResetSweep();
            }
            catch
            {
                sweepAborted = true;
                return(sweepAborted);
            }

            //turn therapy off
            m_summit.StimChangeTherapyOff(false);
            m_sweepFinished = true;
            stopListenerThread.Join();
            m_stopSweep = false;
            return(sweepAborted);
        }