/// <summary>
 /// Try open junction by the EM, by calling EMOpenJunction.
 /// if min voltage exceeded without the junction being opened, do a few steps by the stepper motor, then retry EM (recursion).
 /// </summary>
 /// <param name="settings"></param>
 /// <returns></returns>
 private bool EMTryOpenJunction(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
 {
     //
     // if the EM reached voltage 0 without opening the junction, 
     // return to higher voltage on EM, do some steps by the stepper motor and retry opening by EM.
     //
     if (!EMOpenJunction(settings, worker, e))
     {
         m_electroMagnet.ReachEMVoltageGradually(m_electroMagnet.MinDelay, c_initialEMVoltage);                
         MoveStepsByStepperMotor(StepperDirection.UP, 20);
         return EMTryOpenJunction(settings, worker, e);
     }
     return true;
 }
 /// <summary>
 /// Close the junction asynchronously by the ElectroMagnet
 /// </summary>
 /// <param name="settings"></param>       
 private void EMBeginCloseJunction(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
 {
     EMCloseJunctionMethodDelegate emCloseJunctionDelegate = new EMCloseJunctionMethodDelegate(EMTryCloseJunction);
     AsyncCallback callback = new AsyncCallback(EMEndCloseJunction);
     IAsyncResult asyncResult = emCloseJunctionDelegate.BeginInvoke(settings, worker, e, callback, emCloseJunctionDelegate);
 }
        /// <summary>
        /// Close the junction by the ElectroMagnet.
        /// If max voltage exceeded without the junction being closed, return false. 
        /// </summary>
        /// <param name="settings">The settings to be used to open the junction</param>
        private bool EMCloseJunction(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
        {
            //
            // Set the direction of the movement
            // And configure the first setpper delay (shorter) - faster movement
            //
            m_electroMagnet.Direction = StepperDirection.DOWN;
            m_electroMagnet.Delay = settings.ElectromagnetSettings.EMFastDelayTime;


            //
            // Set false to stop operation flag
            // Keep polling the value until it is abit different than 0.
            // This is a problem of instability with Flaxer's card.
            //
            double initialVoltage = AnalogIn(0);
            while (initialVoltage == 0.0)
            {
                initialVoltage = AnalogIn(0);
            }
            m_quitJunctionClosingOperation = false;
            bool isDelayedChanged = false;    

            //
            // Until we've not been signaled from outer thread to stop we'll continue moving up.
            //
            while (!m_quitJunctionClosingOperation)
            {
                //
                // Cancel the operatin if user asked for
                //
                if (worker.CancellationPending == true)
                {
                    e.Cancel = true;
                    break;
                }

                if (!m_electroMagnet.MoveSingleStep())
                {
                    //
                    // if min Votlage on EM was exceeded return false.
                    //
                    return false;
                }

                double currentVoltage = AnalogIn(0);
                
                //
                // If voltgae had been changed in 0.0001% then switch to slow mode
                // Note that voltage can be negative so we must take the absoulute value
                // The two numbers must be of the same sign in order for us to compare them
                //
                if (!isDelayedChanged && (currentVoltage * initialVoltage > 0) && (Math.Abs(currentVoltage) > Math.Abs(initialVoltage) * 10))
                {
                    m_electroMagnet.Delay = settings.ElectromagnetSettings.EMSlowDelayTime;
                    isDelayedChanged = true;
                }

                //
                // If hold-on trigger was exceeded, wait 10 ms and check if still true.
                //
                if (settings.ElectromagnetSettings.IsEMHoldOnEnable && Math.Abs(currentVoltage) > Math.Abs(settings.ElectromagnetSettings.EMHoldOnMaxVoltage * 1.1))
                {
                    Thread.Sleep(10);
                    if (Math.Abs(currentVoltage) > Math.Abs(settings.ElectromagnetSettings.EMHoldOnMaxVoltage * 1.1))
                    {
                        //
                        // trigger was truely exceeded. try to hold on to a certain junction voltage.
                        //
                        if (!StabilizeJunction(currentVoltage, settings.ElectromagnetSettings.EMHoldOnMaxVoltage, settings.ElectromagnetSettings.EMHoldOnMinVoltage))
                        {
                            return false;
                        }
                        else
                        {
                            return true;
                        }
                    }
                }
                Thread.Sleep(m_electroMagnet.Delay);
            }
            return true;
        }
        /// <summary>
        /// Open the junction by the ElectroMagnet.
        /// If min voltage exceeded without the junction being opened, return false. 
        /// </summary>
        /// <param name="settings">The settings to be used to open the junction</param>
        private bool EMOpenJunction(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
        {
            //
            // Set the direction of the movement
            // And configure the first setpper delay (shorter) - faster movement
            //
            m_electroMagnet.Direction = StepperDirection.UP;
            m_electroMagnet.Delay = settings.ElectromagnetSettings.EMFastDelayTime;

            //
            // Read the initial voltgae before we've done anything
            //

            double initialVoltage = AnalogIn(0);
            bool isDelayedChanged = false;            
            m_quitJunctionOpenningOperation = false;

            //
            // Until we've not been signaled from outer thread to stop we'll continue moving up.
            //
            while (!m_quitJunctionOpenningOperation)
            {
                //
                // Cancel the operatin if user asked for
                //
                if (worker.CancellationPending == true)
                {
                    e.Cancel = true;
                    break;
                }

                if (!m_electroMagnet.MoveSingleStep())
                {
                    //
                    // if min Votlage on EM was exceeded return false.
                    //
                    return false;
                }
                double currentVoltage = AnalogIn(0);

                //
                // If voltgae had been changed in 0.0001% then switch to slow mode
                // Note that voltage can be negative so we must take the absoulute value
                //
                if (!isDelayedChanged && (Math.Abs(currentVoltage) < Math.Abs(initialVoltage) * 0.9999))
                {
                    m_electroMagnet.Delay = settings.ElectromagnetSettings.EMSlowDelayTime;
                    isDelayedChanged = true;
                }

                //
                // If hold-on trigger was exceeded, wait 10 ms and check if still true.
                //
                if (settings.ElectromagnetSettings.IsEMHoldOnEnable && Math.Abs(currentVoltage) < Math.Abs(settings.ElectromagnetSettings.EMHoldOnMaxVoltage * 1.1))
                {
                    Thread.Sleep(10);
                    if (Math.Abs(currentVoltage) < Math.Abs(settings.ElectromagnetSettings.EMHoldOnMaxVoltage * 1.1))
                    {
                        //
                        // trigger was truely exceeded. try to hold on to a certain junction voltage.
                        //
                        if (!StabilizeJunction(currentVoltage, settings.ElectromagnetSettings.EMHoldOnMaxVoltage, settings.ElectromagnetSettings.EMHoldOnMinVoltage))
                        {
                            return false;
                        }
                        else
                        {
                            return true;
                        }
                    }
                }
                Thread.Sleep(m_electroMagnet.Delay);
            }
            return true;
        }
        /// <summary>
        /// Aquire data from an open position while closing the junction
        /// </summary>
        /// <param name="settings"></param>
        /// <param name="worker"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        private bool PerformMakeJunctionCycles(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
        {
            double[,] dataAquired;
            bool isCancelled = false;
            int finalFileNumber = settings.GeneralSettings.CurrentFileNumber;
            List<IDataChannel> physicalChannels = new List<IDataChannel>();

            //
            // Since we set the gain to E5 when configuring the task
            // it is time to set it back to the desired one.
            //
            int gainPower;
            Int32.TryParse(settings.GeneralSettings.Gain, out gainPower);
            m_amplifier.ChangeGain(gainPower);

            //
            // Change the gain power to 5 before reaching contact
            // to ensure full contact current
            //
            if (settings.GeneralSettings.UseDefaultGain)
            {
                m_amplifier.ChangeGain(5);
            }

            //
            // Configure the gain to the desired one if we changed it to E5 for reaching contact            
            //
            if (settings.GeneralSettings.UseDefaultGain)
            {
                m_amplifier.ChangeGain(gainPower);
            }

            //
            // Before stat measuring, bring to contact.
            // Use stepper motor only since we could be far away from reaching contact
            // It is an important step since we can't be sure in what position we are currently standing.
            //
            TryObtainShortCircuit(settings.GeneralSettings.ShortCircuitVoltage, settings.GeneralSettings.UseShortCircuitDelayTime,settings.GeneralSettings.ShortCircuitDelayTime, worker, e);

            //
            // Now we can begin our measurements cycles
            //
            for (int i = 0; i < settings.GeneralSettings.TotalNumberOfCycles; i++)
            {
                //
                // Cancel the operatin if user asked for
                //
                if (worker.CancellationPending == true)
                {
                    e.Cancel = true;
                    break;
                }

                //
                // Turn off the laser before we open the junction
                //
                if (settings.LaserSettings.IsLaserOn)
                {
                    m_LaserController.TurnOff();
                    Thread.Sleep(5000);
                }

                //
                // Before we start the measurement cycles we reach to contact and then open
                // It is important to reach to contact before trying to open since
                // there could be a situation where the junction was not closed entirely in the previous 
                // cycle and so it still seems open where actually it is not.
                // No need to do it in the first cycle since this is done outside the for loop.
                //
                if (settings.ElectromagnetSettings.IsEMEnable && i > 0)
                {
                    EMTryObtainShortCircuit(settings.ElectromagnetSettings.EMShortCircuitDelayTime, settings.GeneralSettings.ShortCircuitVoltage, worker, e);
                }
                else
                {
                    TryObtainShortCircuit(settings.GeneralSettings.ShortCircuitVoltage, settings.GeneralSettings.UseShortCircuitDelayTime,settings.GeneralSettings.ShortCircuitDelayTime, worker, e);
                }

                //
                // Reach to open circuit for initial position
                // If we intend to use the EM, for the first cycle only, use the stepper motor since
                // we initially reached to contact with the stepper motor and the EM doesn't have enough force
                // to withdraw the cantiliver from this intially squeezed position.
                //
                isCancelled = (settings.ElectromagnetSettings.IsEMEnable && i > 0) ?
                              EMTryObtainOpenCircuit(settings.ElectromagnetSettings.EMOpenCircuitDelayTime, settings.GeneralSettings.OpenCircuitVoltage, worker, e) :
                              TryObtainOpenCircuit(settings.GeneralSettings.OpenCircuitVoltage, worker, e);

                if (isCancelled)
                {
                    break;
                }

                //
                // Turn back on the laser
                //
                if (settings.LaserSettings.IsLaserOn)
                {
                    m_LaserController.TurnOn();
                }

                //
                // Start making the junction.
                //
                if (settings.ElectromagnetSettings.IsEMEnable)
                {
                    EMBeginCloseJunction(settings, worker, e);
                }
                else
                {
                    BeginCloseJunction(settings, worker, e);
                }

                //
                // Start the task and wait for the data
                //
                try
                {
                    m_activeTriggeredTask.Start();
                }
                catch (DaqException ex)
                {
                    throw new SBJException("Error occured when tryin to start DAQ task", ex);
                }

                AnalogMultiChannelReader reader = new AnalogMultiChannelReader(m_activeTriggeredTask.Stream);

                try
                {
                    dataAquired = reader.ReadMultiSample(-1);

                    if (dataAquired.GetLength(1) < settings.GeneralSettings.TotalSamples)
                    {
                        //
                        // If from some reason we weren't able to 
                        // receive all data points, ignore and continue;
                        //
                        m_activeTriggeredTask.Stop();
                        continue;
                    }

                    if (settings.ChannelsSettings.ActiveChannels.Count != dataAquired.GetLength(0))
                    {
                        throw new SBJException("Number of data channels doesn't fit the recieved data.");
                    }
                }
                catch (DaqException)
                {
                    //
                    // Probably timeout.
                    // Ignore this cycle and rerun.
                    //
                    m_activeTriggeredTask.Stop();
                    continue;
                }

                //
                // At this point the reader has returned with all the data
                // so we can stop the openning of the junction.
                //
                m_quitJunctionClosingOperation = true;
                m_activeTriggeredTask.Stop();

                //
                // Assign the aquired data for each channel.
                // First clear all data from previous interation.
                //                
                ClearRawData(settings.ChannelsSettings.ActiveChannels);
                AssignRawDataToChannels(settings.ChannelsSettings.ActiveChannels, dataAquired);

                //
                // physical channel will include both simple and complex channels. 
                // 
                physicalChannels = GetChannelsForDisplay(settings.ChannelsSettings.ActiveChannels);

                //
                // calculate the physical data for each channel
                //
                GetPhysicalData(physicalChannels);

                // 
                // Increase file number by one
                // Save data if needed
                //
                finalFileNumber++;
                if (settings.GeneralSettings.IsFileSavingRequired)
                {
                    finalFileNumber = SaveData(settings.GeneralSettings.Path, settings.ChannelsSettings.ActiveChannels, physicalChannels, finalFileNumber);
                }

                //
                // Signal UI we have the data
                //
                if (DataAquired != null)
                {
                    DataAquired(this, new DataAquiredEventArgs(physicalChannels, finalFileNumber));
                }
            }
            return e.Cancel || isCancelled;
        }
        /// <summary>
        /// Get the task for the data aquisition
        /// </summary>
        /// <param name="settings"></param>
        /// <param name="worker"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        private Task GetMultipleChannelsTriggeredTask(SBJControllerSettings settings, string taskName, RunDirection runDirection, AnalogEdgeReferenceTriggerSlope triggerSlope, double triggerVoltage, BackgroundWorker worker, DoWorkEventArgs e)
        {
            AnalogEdgeReferenceTriggerSlope localTriggerSlope;
            double localTriggerVoltage;

            //
            // Determine the trigger slope direction and voltage according to sign of the measured signal.
            //
            if (triggerSlope > 0) 
            {
                localTriggerSlope = triggerSlope;
                localTriggerVoltage = triggerVoltage;
            }
            else
            {
                //
                // Reach to contact in order to retrieve the signal.
                // 
                if (settings.ElectromagnetSettings.IsEMEnable)
                {
                    EMTryObtainShortCircuit(settings.ElectromagnetSettings.EMShortCircuitDelayTime, settings.GeneralSettings.ShortCircuitVoltage, worker, e);
                }
                else
                {
                    TryObtainShortCircuit(settings.GeneralSettings.ShortCircuitVoltage, settings.GeneralSettings.UseShortCircuitDelayTime, settings.GeneralSettings.ShortCircuitDelayTime, worker, e);
                }

                //
                // Determines the direction of the current - 
                // Either positive (then voltage is negative) or negative (then voltage is positive number)
                //
                bool isPositiveVoltage = AnalogIn(0) > 0;

                //
                // Trigger's slope depends both on voltage sign and also on the direction of measurement - break or make junction.
                //
                if (isPositiveVoltage)
                {
                    localTriggerSlope = (runDirection == RunDirection.Break) ? AnalogEdgeReferenceTriggerSlope.Falling : AnalogEdgeReferenceTriggerSlope.Rising;
                }
                else
                {
                    localTriggerSlope = (runDirection == RunDirection.Break) ? AnalogEdgeReferenceTriggerSlope.Rising : AnalogEdgeReferenceTriggerSlope.Falling;
                }

                localTriggerVoltage = isPositiveVoltage ? settings.GeneralSettings.TriggerVoltage * (-1) : settings.GeneralSettings.TriggerVoltage;

                //
                // Before re-openning the junction set auto range off
                // so we can stay at high range suitable to E5 gain.
                //
                if (settings.GeneralSettings.UseKeithley)
                {
                    m_sourceMeter.SetAutoRange(false);
                }

                //
                // Open the junction once again as if we didn't do anything.
                //
                if (settings.ElectromagnetSettings.IsEMEnable)
                {
                    EMTryObtainOpenCircuit(settings.ElectromagnetSettings.EMShortCircuitDelayTime, settings.GeneralSettings.OpenCircuitVoltage, worker, e);
                }
                else
                {
                    TryObtainOpenCircuit(settings.GeneralSettings.OpenCircuitVoltage, worker, e);
                }
            }            

            //
            // Create the task with its propertites
            //
            TriggeredTaskProperties taskProperties = new TriggeredTaskProperties(taskName,
                                                                                 settings.ChannelsSettings.ActiveChannels,                                                                                 
                                                                                 settings.GeneralSettings.SampleRate,
                                                                                 settings.GeneralSettings.TotalSamples,
                                                                                 localTriggerVoltage,
                                                                                 settings.GeneralSettings.PretriggerSamples,
                                                                                 localTriggerSlope);


            return m_daqController.CreateMultipleChannelsTriggeredTask(taskProperties);
        }
        /// <summary>
        /// Configure laser parameters.
        /// </summary>
        /// <param name="settings">The settings by which the laser is to be configured</param>
        /// <exception cref="SBJException"></exception>
        private void ConfigureLaserIfNeeded(SBJControllerSettings settings)
        {            
            //
            // Return if no need to turn the laser on
            //
            if (!settings.LaserSettings.IsLaserOn)
            {
                return;
            }

            //ILaserController laserController = null;

            if (settings.LaserSettings.LaserMode.Equals("IODrive"))
            {
                (m_LaserController as IODriveLaserController).SetAmplitude(settings.LaserSettings.LaserAmplitudeVolts);                
            }
            else
            {
                if (settings.LaserSettings.LaserMode.Equals("DC"))
                {
                    (m_LaserController as TaborLaserController).SetDCMode(settings.LaserSettings.LaserAmplitudeVolts);
                }
                else
                {
                    if (settings.LaserSettings.LaserMode.Equals("Square"))
                    {
                        (m_LaserController as TaborLaserController).SetSquareMode(settings.LaserSettings.LaserFrequency, settings.LaserSettings.LaserAmplitudeVolts);                        
                    }
                    else
                    {
                        throw new SBJException("Invalid laser mode. Expected modes: DC or Sqaure");
                    }
                }
            }

            if (settings.LaserSettings.IsFirstEOMOn)
            {
                m_taborFirstEOMController.SetSinusoidMode(settings.LaserSettings.FirstEOMFrequency);
                m_taborFirstEOMController.TurnOn();
            }

            if (settings.LaserSettings.IsSecondEOMOn)
            {
                m_taborSecondEOMController.SetSinusoidMode(settings.LaserSettings.SecondEOMFrequency);
                m_taborSecondEOMController.TurnOn();
        }
        }
 /// <summary>
 /// Open the junction asynchronously
 /// </summary>
 /// <param name="settings"></param>       
 private void BeginOpenJunction(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
 {
     OpenJunctionMethodDelegate openJunctionDelegate = new OpenJunctionMethodDelegate(OpenJunction);
     AsyncCallback callback = new AsyncCallback(EndOpenJunction);
     IAsyncResult asyncResult = openJunctionDelegate.BeginInvoke(settings, worker, e, callback, openJunctionDelegate);
 }
        /// <summary>
        /// Move cantiliver to position until trigger is reached; Then stop.
        /// </summary>
        /// <param name="sBJControllerSettings"></param>
        /// <param name="worker"></param>
        /// <param name="e"></param>
        public bool ReachPosition(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
        {
            bool isCancelled = false;
            //
            // Apply voltage with desired tool: Task or Keithley
            //
            ApplyVoltageIfNeeded(settings.GeneralSettings.UseKeithley,
                                 settings.GeneralSettings.Bias,
                                 settings.GeneralSettings.BiasError, settings.GeneralSettings.Range,
                                 settings.GeneralSettings.AutoRange);

            //
            // Use Lambda Zup to apply constant voltage on external electromagnet if needed
            //
            UseLambdaZupIfNeeded(settings.LambdaZupSettings.IsLambdaZupEnable,
                                 settings.LambdaZupSettings.OutputVoltage);

            //
            // Configure the laser if needed for this run
            //
            ConfigureLaserIfNeeded(settings);

            //
            // Save this run settings if desired
            //
            SaveSettingsIfNeeded(settings, settings.GeneralSettings.IsFileSavingRequired, settings.GeneralSettings.Path);

            //
            //apply initial voltage on the EM
            //
            ApplyVoltageOnElectroMagnetIfNeeded(settings.ElectromagnetSettings.IsEMEnable);

           
            switch (settings.GeneralSettings.RunDirection)
            {
                case RunDirection.Both:
                    //TODO: Add implementation for both direction measurement
                    break;
                case RunDirection.Break:                    
                    isCancelled = ReachToPositionByMovingUp(settings, worker, e);
                    break;
                case RunDirection.Make:                       
                    isCancelled = ReachToPositionByMovingDown(settings, worker, e);
                    break;
            }

            //
            // Finish the measurement properly
            //
            if (settings.LaserSettings.IsLaserOn)
            {
                m_LaserController.TurnOff();
            }
            if (settings.ElectromagnetSettings.IsEMEnable)
            {
                m_electroMagnet.Shutdown();
            }
            if (settings.LaserSettings.IsFirstEOMOn)
            {
                m_taborFirstEOMController.TurnOff();
            }
            if (settings.LaserSettings.IsSecondEOMOn)
            {
                m_taborSecondEOMController.TurnOff();
            }
            if (settings.LambdaZupSettings.IsLambdaZupEnable)
            {
                m_lambdaZup.TurnOffOutput();
            }
            m_activeTriggeredTask.Dispose();
            m_stepperMotor.Shutdown();

            return isCancelled;            
        }   
 /// <summary>
 /// Close the junction asynchronously
 /// </summary>
 /// <param name="settings"></param>       
 private void BeginCloseJunction(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
 {
     CloseJunctionMethodDelegate closeJunctionDelegate = new CloseJunctionMethodDelegate(CloseJunction);
     AsyncCallback callback = new AsyncCallback(EndCloseJunction);
     IAsyncResult asyncResult = closeJunctionDelegate.BeginInvoke(settings, worker, e, callback, closeJunctionDelegate);
 }
        /// <summary>
        /// Manually aquire data
        /// This method continuously poll the buffer for data until it is stopped by the user.
        /// </summary>
        /// <param name="settings">The settings for running the aquisition</param>
        /// <returns>True whether the operation was cacled by the user. False otherwise.</returns>
        public bool AquireDataContinuously(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
        {
            //
            // Apply voltage with desired tool: Task or Keithley
            //
            ApplyVoltageIfNeeded(settings.GeneralSettings.UseKeithley,
                                 settings.GeneralSettings.Bias,
                                 settings.GeneralSettings.BiasError,
                                 settings.GeneralSettings.Range,
                                 settings.GeneralSettings.AutoRange);
            bool isCancelled = false;
            int finalFileNumber = settings.GeneralSettings.CurrentFileNumber;

            //
            // The array is intialized with size for 1 minute sampling.
            //
            double[,] dataAquired = new double[1000, 1000];

            List<IDataChannel> physicalChannels = new List<IDataChannel>();

            //
            // Configure the laser if needed for this run
            //
            ConfigureLaserIfNeeded(settings);

            //
            // Save this run settings if desired
            //
            SaveSettingsIfNeeded(settings, settings.GeneralSettings.IsFileSavingRequired, settings.GeneralSettings.Path);

            //
            //apply initial voltage on the EM
            //
            ApplyVoltageOnElectroMagnetIfNeeded(settings.ElectromagnetSettings.IsEMEnable);

            //
            // Create the task
            //
            m_activeTriggeredTask = GetContinuousAITask(settings.GeneralSettings.SampleRate, settings.ChannelsSettings.ActiveChannels, null);

            //
            // If EM is enabled, and we are asked to skip the first cycle (that is done by the stepper motor), 
            // then return.
            //
            if (settings.ElectromagnetSettings.IsEMEnable && settings.ElectromagnetSettings.IsEMSkipFirstCycleEnable)
            {
                m_stepperMotor.Shutdown();
                return false;
            } 

            if (settings.LaserSettings.IsLaserOn)
            {
                m_LaserController.TurnOn();
            }
           

            //
            // Start the task and wait for the data
            //
            try
            {
                m_activeTriggeredTask.Start();
            }
            catch (DaqException ex)
            {
                throw new SBJException("Error occured when tryin to start DAQ task", ex);
            }

            AnalogMultiChannelReader reader = new AnalogMultiChannelReader(m_activeTriggeredTask.Stream);
            List<List<double>> fullData = new List<List<double>>(settings.ChannelsSettings.ActiveChannels.Count);

            for (int i = 0; i < fullData.Capacity; i++)
            {
                fullData.Add(new List<double>());
            }

            try
            {
                //
                // Before getting all the data clear the lists.
                //
                ClearRawData(settings.ChannelsSettings.ActiveChannels);

                //
                // As long as the user didn't ask to stop the acquisition 
                // (which is signaled by the stop of the stepper motion)
                // we coninue sampling.
                //
                while (!worker.CancellationPending)
                {
                    //
                    // Read all available data points in the buffer that
                    // were not read so far.
                    //
                    dataAquired = reader.ReadMultiSample(-1);
                    fullData = AccumulateData(fullData, dataAquired);                    
                    dataAquired = null;                  
                }
            }
            catch (DaqException)
            {
                //
                // In case of an error just return
                //
                m_activeTriggeredTask.Stop();
                m_activeTriggeredTask.Dispose();
                if (m_LaserController != null)
                {
                    m_LaserController.TurnOff();
                }
                if (m_taborFirstEOMController != null)
                {
                    m_taborFirstEOMController.TurnOff();
                }
                if (m_taborSecondEOMController != null)
                {
                    m_taborSecondEOMController.TurnOff();
                }
                return false;
            }

            //
            // At this point the user had requested to stop the data aquisition.
            // By signaling "stop". We can stop the task.
            //            
            m_activeTriggeredTask.Stop();

            //
            // Assign the aquired data for each channel after an average process
            //                    
            AssignRawDataToChannels(settings.ChannelsSettings.ActiveChannels, ConvertDataToMatrix(fullData));

            //
            // physical channel will include both simple and complex channels. 
            // 
            physicalChannels = GetChannelsForDisplay(settings.ChannelsSettings.ActiveChannels);

            //
            // calculate the physical data for each channel
            //
            GetPhysicalData(physicalChannels);

            // 
            // Increase file number by one
            // Save data if needed
            //
            finalFileNumber++;
            if (settings.GeneralSettings.IsFileSavingRequired)
            {
                finalFileNumber = SaveData(settings.GeneralSettings.Path, settings.ChannelsSettings.ActiveChannels, physicalChannels, finalFileNumber);
            }

            //
            // Signal UI we have the data
            //
            if (DataAquired != null)
            {
                DataAquired(this, new DataAquiredEventArgs(physicalChannels, finalFileNumber));
            }


            //
            // Finish the measurement properly
            //
            if (settings.LaserSettings.IsLaserOn)
            {
                m_LaserController.TurnOff();
            }
            if (settings.ElectromagnetSettings.IsEMEnable)
            {
                m_electroMagnet.Shutdown();
            }
            if (settings.LaserSettings.IsFirstEOMOn)
            {
                m_taborFirstEOMController.TurnOff();
            }
            if (settings.LaserSettings.IsSecondEOMOn)
            {
                m_taborSecondEOMController.TurnOff();
            }

            m_activeTriggeredTask.Dispose();
            m_stepperMotor.Shutdown();

            return (isCancelled || e.Cancel);
        }
        /// <summary>
        /// Manually aquire data
        /// This method continuously poll the buffer for data until it is stopped by the user.
        /// </summary>
        /// <param name="settings">The settings for running the aquisition</param>
        /// <returns>True whether the operation was cacled by the user. False otherwise.</returns>
        public bool AquireDataManually(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
        {
            //
            // Apply voltage with desired tool: Task or Keithley
            //
            ApplyVoltageIfNeeded(settings.GeneralSettings.UseKeithley,
                                 settings.GeneralSettings.Bias,
                                 settings.GeneralSettings.BiasError,
                                 settings.GeneralSettings.Range,
                                 settings.GeneralSettings.AutoRange);

            bool isCancelled = false;
            int finalFileNumber = settings.GeneralSettings.CurrentFileNumber;
       
            //
            // The array is intialized with size for 1 minute sampling.
            //
            double[,] dataAquired = new double[1000, 1000];

            List<IDataChannel> physicalChannels = new List<IDataChannel>();

            //
            // Configure the laser if needed for this run
            //
            ConfigureLaserIfNeeded(settings);

            //
            // Save this run settings if desired
            //
            SaveSettingsIfNeeded(settings, settings.GeneralSettings.IsFileSavingRequired, settings.GeneralSettings.Path);

            //
            //apply initial voltage on the EM
            //
            ApplyVoltageOnElectroMagnetIfNeeded(settings.ElectromagnetSettings.IsEMEnable);

            //
            // Create the task
            //
            m_activeTriggeredTask = GetContinuousAITask(settings.GeneralSettings.SampleRate, settings.ChannelsSettings.ActiveChannels, null);
            
            //
            // If EM is enabled, and we are asked to skip the first cycle (that is done by the stepper motor), 
            // then return.
            //
            if (settings.ElectromagnetSettings.IsEMEnable && settings.ElectromagnetSettings.IsEMSkipFirstCycleEnable)
            {
                m_stepperMotor.Shutdown();
                return false;
            }

            //
            // Turn off the laser before we reach contact
            //
            if (settings.LaserSettings.IsLaserOn)
            {
                m_LaserController.TurnOff();
                Thread.Sleep(5000);
            }

            //
            // Change the gain power to 5 before reaching contact
            // to ensure full contact current
            //
            if (settings.GeneralSettings.UseDefaultGain)
            {
                m_amplifier.ChangeGain(5);
            }

            //
            // Reach to contact before we start openning the junction
            // If EM is enabled and we're after the first cycle, use the EM.
            // If user asked to stop than exit
            //
            isCancelled = (settings.ElectromagnetSettings.IsEMEnable) ?
                           EMTryObtainShortCircuit(settings.ElectromagnetSettings.EMShortCircuitDelayTime, 
                                                   settings.GeneralSettings.ShortCircuitVoltage, worker, e) :
                           TryObtainShortCircuit(settings.GeneralSettings.ShortCircuitVoltage, settings.GeneralSettings.UseShortCircuitDelayTime,settings.GeneralSettings.ShortCircuitDelayTime, worker, e);
            if (isCancelled)
            {
                return false;
            }

            //
            // Configure the gain to the desired one before strating the measurement.
            // And also this is the time to switch the laser on.
            //
            if (settings.GeneralSettings.UseDefaultGain)
            {
                int gainPower;
                Int32.TryParse(settings.GeneralSettings.Gain, out gainPower);
                m_amplifier.ChangeGain(gainPower);
            }


            if (settings.LaserSettings.IsLaserOn)
            {
                m_LaserController.TurnOn();
            }

            //
            // Start openning the junction.
            // If EM is enabled then use it.
            //
            if (settings.ElectromagnetSettings.IsEMEnable)
            {
                m_stepperMotor.Shutdown();
                EMBeginOpenJunction(settings, worker, e);                
            }
            else
            {
                BeginOpenJunction(settings, worker, e);
            }

            //
            // Start the task and wait for the data
            //
            try
            {
                m_activeTriggeredTask.Start();
            }
            catch (DaqException ex)
            {
                throw new SBJException("Error occured when tryin to start DAQ task", ex);
            }

            AnalogMultiChannelReader reader = new AnalogMultiChannelReader(m_activeTriggeredTask.Stream);
            List<List<double>> averagedData = new List<List<double>>(settings.ChannelsSettings.ActiveChannels.Count);

            for (int i = 0; i < averagedData.Capacity; i++)
            {
                averagedData.Add(new List<double>());
            }

            try
            {
                //
                // Before getting all the data clear the lists.
                //
                ClearRawData(settings.ChannelsSettings.ActiveChannels);              

                //
                // As long as the user didn't ask to stop the acquisition 
                // (which is signaled by the stop of the stepper motion)
                // we coninue sampling.
                //
                while (!m_quitJunctionOpenningOperation)
                {
                    //
                    // Read all available data points in the buffer that
                    // were not read so far.
                    //
                    dataAquired = reader.ReadMultiSample(-1);

                    //
                    // Get average for the acquired the data and assign to variable
                    //
                    List<double> averageDataValues = GetAverageDataValue(dataAquired);
                    for (int i = 0; i < averageDataValues.Count; i++)
                    {                        
                        averagedData[i].Add(averageDataValues[i]);
                    }
                      
                    dataAquired = null;
 
                    //
                    // Cancel the operatin if user asked for
                    // We do it at the end of the loop to make sure we 
                    // saved all the data we have available.
                    //
                    if (worker.CancellationPending == true)
                    {
                        e.Cancel = true;
                        break;
                    }
                }                
            }
            catch (DaqException)
            {
                //
                // In case of an error just return
                //
                m_activeTriggeredTask.Stop();
                m_activeTriggeredTask.Dispose();
                if (m_LaserController != null)
                {
                m_LaserController.TurnOff();
                }
                if (m_taborFirstEOMController != null)
                {
                    m_taborFirstEOMController.TurnOff();
                }
                if (m_taborSecondEOMController != null)
                {
                    m_taborSecondEOMController.TurnOff();
                }
                return false;
            }

            //
            // At this point the user had requested to stop the data aquisition.
            // By signaling "stop". We can stop the task.
            //            
            m_activeTriggeredTask.Stop();

            //
            // Assign the aquired data for each channel after an average process
            //                    
            AssignRawDataToChannels(settings.ChannelsSettings.ActiveChannels, ConvertDataToMatrix(GetAveragedData(averagedData, 5000)));

            //
            // physical channel will include both simple and complex channels. 
            // 
            physicalChannels = GetChannelsForDisplay(settings.ChannelsSettings.ActiveChannels);

            //
            // calculate the physical data for each channel
            //
            GetPhysicalData(physicalChannels);

            // 
            // Increase file number by one
            // Save data if needed
            //
            finalFileNumber++;
            if (settings.GeneralSettings.IsFileSavingRequired)
            {
                finalFileNumber = SaveData(settings.GeneralSettings.Path, settings.ChannelsSettings.ActiveChannels, physicalChannels, finalFileNumber);
            }

            //
            // Signal UI we have the data
            //
            if (DataAquired != null)
            {
                DataAquired(this, new DataAquiredEventArgs(physicalChannels, finalFileNumber));
            }
            

            //
            // Finish the measurement properly
            //
            if (settings.LaserSettings.IsLaserOn)
            {
                m_LaserController.TurnOff();
            }
            if (settings.ElectromagnetSettings.IsEMEnable)
            {
                m_electroMagnet.Shutdown();
            }
            if (settings.LaserSettings.IsFirstEOMOn)
            {
                m_taborFirstEOMController.TurnOff();
            }
            if (settings.LaserSettings.IsSecondEOMOn)
            {
                m_taborSecondEOMController.TurnOff();
            }

            m_activeTriggeredTask.Dispose();
            m_stepperMotor.Shutdown();

            return (isCancelled || e.Cancel);
        }
        /// <summary>
        /// Data aquisition
        /// </summary>
        /// <param name="settings">The settings for running the aquisition</param>
        /// <returns>True whether the operation was cacled by the user. False otherwise.</returns>
        public bool AquireData(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
        {
            bool isCancelled = false;
            //
            // Apply voltage with desired tool: Task or Keithley
            //
            ApplyVoltageIfNeeded(settings.GeneralSettings.UseKeithley, 
                                 settings.GeneralSettings.Bias, 
                                 settings.GeneralSettings.BiasError,
                                 settings.GeneralSettings.Range,
                                 settings.GeneralSettings.AutoRange);

            //
            // Use Lambda Zup to apply constant voltage on external electromagnet if needed
            //
            UseLambdaZupIfNeeded(settings.LambdaZupSettings.IsLambdaZupEnable,
                                 settings.LambdaZupSettings.OutputVoltage);

            //
            // Configure the laser if needed for this run
            //
            ConfigureLaserIfNeeded(settings);

            //
            // Save this run settings if desired
            //
            SaveSettingsIfNeeded(settings, settings.GeneralSettings.IsFileSavingRequired, settings.GeneralSettings.Path);

            //
            //apply initial voltage on the EM
            //
            ApplyVoltageOnElectroMagnetIfNeeded(settings.ElectromagnetSettings.IsEMEnable);

            //
            // Change to E5 gain to determine current sign (+/-) in order to configure the task correctly
            // This is also used to disable auto range once reached to contact.
            //
            m_amplifier.ChangeGain(5);
            //
            // Create the task
            //
            switch (settings.GeneralSettings.RunDirection)
            {
                case RunDirection.Both:
                    //TODO: Add implementation for both direction measurement
                    break;
                case RunDirection.Break:
                    m_firstTriggeredTask = GetMultipleChannelsTriggeredTask(settings, "firstTriggeredTask" ,RunDirection.Break, 0, 0, worker, e);
                    m_firstTriggeredTask.Control(TaskAction.Verify);
                    double level = m_firstTriggeredTask.Triggers.ReferenceTrigger.AnalogEdge.Level * -1;
                    AnalogEdgeReferenceTriggerSlope slope = m_firstTriggeredTask.Triggers.ReferenceTrigger.AnalogEdge.Slope == AnalogEdgeReferenceTriggerSlope.Falling ? AnalogEdgeReferenceTriggerSlope.Rising : AnalogEdgeReferenceTriggerSlope.Falling;
                    m_secondaryTriggeredTask = GetMultipleChannelsTriggeredTask(settings, "secondaryTriggeredTask",  RunDirection.Break, slope, level , worker, e);
                    isCancelled = PerformBreakJunctionCycles(settings, worker, e);
                    break;
                case RunDirection.Make:
                    m_activeTriggeredTask = GetMultipleChannelsTriggeredTask(settings, null,RunDirection.Make, m_triggerSlope, m_triggerVoltage, worker, e);
                    isCancelled = PerformMakeJunctionCycles(settings, worker, e);                    
                    break;
            }     

            //
            // Finish the measurement properly
            //
            if (settings.LaserSettings.IsLaserOn)
            {
                m_LaserController.TurnOff();
            }
            if (settings.ElectromagnetSettings.IsEMEnable)
            {
                m_electroMagnet.Shutdown();
            }
            if (settings.LaserSettings.IsFirstEOMOn)
            {
                m_taborFirstEOMController.TurnOff();
            }
            if (settings.LaserSettings.IsSecondEOMOn)
            {
                m_taborSecondEOMController.TurnOff();
            }
            if (settings.LambdaZupSettings.IsLambdaZupEnable)
            {
                m_lambdaZup.TurnOffOutput();
            }
            m_activeTriggeredTask.Dispose();
            m_firstTriggeredTask.Dispose();
            m_secondaryTriggeredTask.Dispose();
            m_stepperMotor.Shutdown();

            return isCancelled;
        }      
        /// <summary>
        /// Open the junction
        /// </summary>
        /// <param name="settings">The settings to be used to open the junction</param>
        private void OpenJunction(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
        {
            //
            // Set the direction of the movement and stepping mode
            // And configure the first setpper delay (shorter) - faster movement
            //
            m_stepperMotor.Direction = StepperDirection.UP;
            m_stepperMotor.SteppingMode = StepperSteppingMode.HALF;
            m_stepperMotor.Delay = settings.GeneralSettings.StepperWaitTime1;

            //
            // Read the initial voltgae before we've done anything
            //
            double initialVoltage = AnalogIn(0);
            bool isDelayedChanged = false;
            m_quitJunctionOpenningOperation = false;

            //
            // Until we've not been signaled from outer thread to stop we'll continue moving up.
            //
            while (!m_quitJunctionOpenningOperation)
            {
                //
                // Cancel the operatin if user asked for
                //
                if (worker.CancellationPending == true)
                {
                    e.Cancel = true;
                    break;
                }

                m_stepperMotor.MoveSingleStep();
                double currentVoltage = AnalogIn(0);

                //
                // If voltgae had been changed in 0.0001% then switch to slow mode
                // Note that voltage can be negative so we must take the absoulute value
                //
                if (!isDelayedChanged && (Math.Abs(currentVoltage) < Math.Abs(initialVoltage) * 0.95))
                {
                    m_stepperMotor.Delay = settings.GeneralSettings.StepperWaitTime2;
                    isDelayedChanged = true;
                }
                Thread.Sleep(m_stepperMotor.Delay);
            }
        }
        /// <summary>
        /// Reach to position specified by conductance value and then stop and aqcuire data until asked to stop.
        /// </summary>
        /// <param name="settings"></param>
        /// <param name="worker"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        private bool ReachToPositionByMovingUp(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
        {
            double[,] dataAcquired = new double[1000, 1000];
            int finalFileNumber = settings.GeneralSettings.CurrentFileNumber;
            List<IDataChannel> physicalChannels = new List<IDataChannel>();


            for (int i = 0; i < settings.GeneralSettings.TotalNumberOfCycles; i++)
            {
                //
                // Cancel the operatin if user asked for
                //
                if (worker.CancellationPending == true)
                {
                    e.Cancel = true;
                    break;
                }

                //
                // This flag is used to signal us when the user asked to stop the real time data acquisition
                //
                m_quitRealTimeOperation = false;
                m_activeTriggeredTask = GetMultipleChannelsTriggeredTask(settings, null, RunDirection.Break, m_triggerSlope, m_triggerVoltage, worker, e);
                m_activeTriggeredTask.EveryNSamplesReadEventInterval = settings.GeneralSettings.TotalSamples;
                m_activeTriggeredTask.Done += new TaskDoneEventHandler(OnTaskDoneOpenning);
                m_activeTriggeredTask.Control(TaskAction.Verify);
                m_triggerSlope = m_activeTriggeredTask.Triggers.ReferenceTrigger.AnalogEdge.Slope;
                m_triggerVoltage = m_activeTriggeredTask.Triggers.ReferenceTrigger.AnalogEdge.Level;

                //
                // physical channel will include both simple and complex channels. 
                // 
                physicalChannels = GetChannelsForDisplay(settings.ChannelsSettings.ActiveChannels);

                //
                // Assign the aquired data for each channel.
                // First clear all data from previous interation.
                //                
                ClearRawData(settings.ChannelsSettings.ActiveChannels);

                //
                // Create the tasks: One for triggering us to stop and the other for start monitoring the data
                //
                m_realTimeTask = GetContinuousAITask(settings.GeneralSettings.SampleRate, settings.ChannelsSettings.ActiveChannels, null);
                AnalogMultiChannelReader realTimeReader = new AnalogMultiChannelReader(m_realTimeTask.Stream);

                //
                // Start closing the junction.
                // If EM is enabled use the EM.
                //
                if (settings.ElectromagnetSettings.IsEMEnable)
                {
                    EMTryObtainShortCircuit(settings.ElectromagnetSettings.EMShortCircuitDelayTime, settings.GeneralSettings.ShortCircuitVoltage, worker, e);
                }
                else
                {
                    TryObtainShortCircuit(settings.GeneralSettings.ShortCircuitVoltage, settings.GeneralSettings.UseShortCircuitDelayTime,settings.GeneralSettings.ShortCircuitDelayTime, worker, e);
                }

                //
                // Start openning the junction. ASync operation.
                // If EM is enabled use the EM.
                //
                if (settings.ElectromagnetSettings.IsEMEnable)
                {
                    EMBeginOpenJunction(settings, worker, e);
                }
                else
                {
                    BeginOpenJunction(settings, worker, e);
                }

                //
                // Cancel the operatin if user asked for
                //
                if (worker.CancellationPending == true)
                {
                    e.Cancel = true;
                    break;
                }

                //
                // Start the triggered task. 
                //
                m_activeTriggeredTask.Start();

                //
                // If the user asked to stop the operation on the external thread then
                // WaitUntilDone will throw an expection. We can ignore that and return.
                //
                try
                {
                    m_activeTriggeredTask.WaitUntilDone();
                }
                catch (DaqException)
                {
                    //
                    // We got here if the user asked to stop the operation
                    //
                    break;
                }

                //
                // We reach this point only after we reached the desired conductance value.
                // As long as the user didn't ask to stop the operation continue recording the data.
                //
                while (!m_quitRealTimeOperation)
                {
                    //
                    // Read operation implicity start the task without the need to call Start() method.
                    //
                    try
                    {
                        dataAcquired = realTimeReader.ReadMultiSample(-1);
                    }
                    catch (DaqException)
                    {
                        continue;
                    }


                    if (dataAcquired.Length == 0)
                    {
                        continue;
                    }
                    //
                    // Assign the aquired data for each channel.
                    //                            
                    AssignRawDataToChannels(settings.ChannelsSettings.ActiveChannels, dataAcquired);

                    //
                    // calculate the physical data for each channel
                    //
                    GetPhysicalData(physicalChannels);

                    //
                    // Signal UI we have the data
                    //
                    if (DataAquired != null)
                    {
                        DataAquired(this, new DataAquiredEventArgs(physicalChannels, finalFileNumber));
                    }
                }

                if (DoneReadingData != null)
                {
                    DoneReadingData(this, null);
                }
                m_realTimeTask.Stop();
                m_realTimeTask.Dispose();

                // 
                // Increase file number by one
                // Save data if needed
                //
                finalFileNumber++;
                if (settings.GeneralSettings.IsFileSavingRequired)
                {
                    finalFileNumber = SaveData(settings.GeneralSettings.Path, settings.ChannelsSettings.ActiveChannels, physicalChannels, finalFileNumber);
                }
            }

            m_activeTriggeredTask.Dispose();
            m_realTimeTask.Dispose();
            m_triggerSlope = 0;
            m_triggerVoltage = 0;
            return e.Cancel;
        }
        /// <summary>
        /// Open the junction
        /// </summary>
        /// <param name="settings">The settings to be used to open the junction</param>
        private void CloseJunction(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
        {
            //
            // Set the direction of the movement and stepping mode
            // And configure the first setpper delay.
            //
            m_stepperMotor.Direction = StepperDirection.DOWN;
            m_stepperMotor.SteppingMode = StepperSteppingMode.HALF;
            m_stepperMotor.Delay = settings.GeneralSettings.StepperWaitTime1;

            //
            // Read the initial voltgae before we've done anything
            // Keep polling the value until it is abit different than 0.
            // This is a problem of instability with Flaxer's card.
            //
            double initialVoltage = AnalogIn(0);
            while (initialVoltage == 0.0)
            {
                initialVoltage = AnalogIn(0);
            }
            bool isDelayedChanged = false;            
            m_quitJunctionClosingOperation = false;

            //
            // Until we've not been signaled from outer thread to stop we'll continue moving up.
            //
            while (!m_quitJunctionClosingOperation)
            {
                //
                // Cancel the operatin if user asked for
                //
                if (worker.CancellationPending == true)
                {
                    e.Cancel = true;
                    break;
                }

                m_stepperMotor.MoveSingleStep();

                double currentVoltage = AnalogIn(0);

                //
                // If voltgae had been changed in 0.0001% then switch to slow mode
                // Note that voltage can be negative so we must take the absoulute value
                // The two numbers must be of the same sign in order for us to compare them
                //
                if (!isDelayedChanged && (currentVoltage * initialVoltage > 0 ) && (Math.Abs(currentVoltage) > Math.Abs(initialVoltage) * 50))
                {
                    m_stepperMotor.Delay = settings.GeneralSettings.StepperWaitTime2;
                    isDelayedChanged = true;
                }

                Thread.Sleep(m_stepperMotor.Delay);
            }
        }
        /// <summary>
        /// Try close junction by the EM, by calling EMCloseJunction.
        /// if max voltage exceeded without the junction being closed, do a few steps by the stepper motor, then retry EM (recursion).
        /// </summary>
        /// <param name="settings"></param>
        /// <returns></returns>
        private bool EMTryCloseJunction(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
        {
            double initialValue = Math.Abs(AnalogIn(0));
            while (initialValue == 0.0)
            {
                initialValue = Math.Abs(AnalogIn(0));
            }

            //
            // if the EM reached max voltage  without closing the junction, 
            // return to lower voltage on EM, do some steps by the stepper motor and retry opening by EM.
            //
            if (!EMCloseJunction(settings, worker, e))
            {
                double currentValue = Math.Abs(AnalogIn(0));
                m_electroMagnet.ReachEMVoltageGradually(m_electroMagnet.MinDelay, c_initialEMVoltage);
                if (currentValue > initialValue*2)
                {
                    MoveStepsByStepperMotor(StepperDirection.DOWN, 50);
                }
                else
                {
                    MoveStepsByStepperMotor(StepperDirection.DOWN, 200);
                }
                return EMTryCloseJunction(settings, worker, e);
            }
            return true;
        }
        /// <summary>
        /// Aquire data from a close position while openning the junction
        /// </summary>
        /// <param name="settings"></param>
        /// <param name="worker"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        private bool PerformBreakJunctionCycles(SBJControllerSettings settings, BackgroundWorker worker, DoWorkEventArgs e)
        {
            double[,] dataAquired;
            bool isCancelled = false;
            int finalFileNumber = settings.GeneralSettings.CurrentFileNumber;
            List<IDataChannel> physicalChannels = new List<IDataChannel>();
            
            //
            // Since we set the gain to E5 when configuring the task
            // it is time to set it back to the desired one.
            //
            int gainPower;
            Int32.TryParse(settings.GeneralSettings.Gain, out gainPower);
            m_amplifier.ChangeGain(gainPower);


            for (int i = 0; i < settings.GeneralSettings.TotalNumberOfCycles; i++)
            {
                //
                // Cancel the operation if user asked for
                //
                if (worker.CancellationPending == true)
                {
                    e.Cancel = true;
                    break;
                }

                //
                // if EM is enabled, and we are asked to skip the first cycle (that is done by the stepper motor), 
                // move on to the next cycle.
                //
                if (settings.ElectromagnetSettings.IsEMEnable && settings.ElectromagnetSettings.IsEMSkipFirstCycleEnable && i == 0)
                {
                    m_stepperMotor.Shutdown();
                    continue;
                }

                //
                // Turn off the laser before we reach contact
                //
                //if (settings.LaserSettings.IsLaserOn)
                //{
                //    m_LaserController.TurnOff();
                //    Thread.Sleep(5000);
                //}

                //
                // Change the gain power to 5 before reaching contact
                // to ensure full contact current
                //
                if (settings.GeneralSettings.UseDefaultGain || settings.GeneralSettings.ChangeGain)
                {
                    m_amplifier.ChangeGain(5);
                }

                //
                // Reach to contact before we start openning the junction
                // If EM is enabled and we're after the first cycle, use the EM.
                // If user asked to stop then exit
                //
                isCancelled = (settings.ElectromagnetSettings.IsEMEnable && i > 0) ?
                               EMTryObtainShortCircuit(settings.ElectromagnetSettings.EMShortCircuitDelayTime, settings.GeneralSettings.ShortCircuitVoltage, worker, e) :
                               TryObtainShortCircuit(settings.GeneralSettings.ShortCircuitVoltage, settings.GeneralSettings.UseShortCircuitDelayTime, settings.GeneralSettings.ShortCircuitDelayTime, worker, e);
                if (isCancelled)
                {
                    break;
                }

                //
                // Configure the gain to the desired one if we changed it to E5
                // before strating the measurement.
                // And also this is the time to switch the laser on.
                //
                if (settings.GeneralSettings.UseDefaultGain)
                {
                    m_amplifier.ChangeGain(gainPower);
                }


                if (settings.LaserSettings.IsLaserOn)
                {
                    m_LaserController.TurnOn();
                }

                //
                // Set the bias if AC mode
                //
                m_activeTriggeredTask = m_firstTriggeredTask;
                if (settings.GeneralSettings.ACBias)
                {
                    double bias = Math.Pow(-1, finalFileNumber) * settings.GeneralSettings.Bias;
                    m_sourceMeter.SetBias(bias, settings.GeneralSettings.Range,settings.GeneralSettings.AutoRange);
                    m_activeTriggeredTask =  (bias == settings.GeneralSettings.Bias) ? m_firstTriggeredTask : m_secondaryTriggeredTask;
                }

                //
                // Start openning the junction.
                // If EM is enabled and we're after the first cycle, use the EM.
                //
                if (settings.ElectromagnetSettings.IsEMEnable)
                {
                    if (i == 0)
                    {
                        //
                        // open the junction by stepper motor. this function does it indipendently; 
                        // it doesn't wait for the trigger task to finish. and we stay on the current thread. 
                        //
                        ObtainOpenJunctionByStepperMotor(settings.GeneralSettings.TriggerVoltage, worker, e);

                        //
                        // from now on we will use the EM, so we don't want the stepper motor to stay on. 
                        //
                        m_stepperMotor.Shutdown();
                        continue;
                    }
                    else
                    {
                        EMBeginOpenJunction(settings, worker, e);
                    }
                }
                else
                {
                    BeginOpenJunction(settings, worker, e);
                }



                //
                // Start the task and wait for the data
                //
                try
                {
                    m_activeTriggeredTask.Start();
                }
                catch (DaqException ex)
                {
                    throw new SBJException("Error occured when tryin to start DAQ task", ex);
                }

                if (settings.GeneralSettings.ChangeGain && (settings.GeneralSettings.StepperWaitTime2 > settings.GeneralSettings.StepperWaitTime1))
                {
                    while (!m_quitJunctionOpenningOperation && (m_stepperMotor.Delay != settings.GeneralSettings.StepperWaitTime2)) { }
                    if (!m_quitJunctionOpenningOperation)
                    {
                        m_amplifier.ChangeGain(gainPower);
                    }
                }
                AnalogMultiChannelReader reader = new AnalogMultiChannelReader(m_activeTriggeredTask.Stream);

                try
                {
                     
                    dataAquired = reader.ReadMultiSample(-1);

                    if (dataAquired.GetLength(1) < settings.GeneralSettings.TotalSamples)
                    {
                        //
                        // If from some reason we weren't able to 
                        // receive all data points, ignore and continue;
                        //
                        m_activeTriggeredTask.Stop();
                        continue;
                    }

                    if (settings.ChannelsSettings.ActiveChannels.Count != dataAquired.GetLength(0))
                    {
                        throw new SBJException("Number of data channels doesn't fit the recieved data.");
                    }
                }
                catch (DaqException ex  )
                {
                    if (ex.Error == -88709 || ex.Error == -88710) 
                    {
                        //
                        // User asked to stop so the task was aborted from the UI thread
                        //
                        break;
                    }
                    else
                    {
                        //
                        // Probably timeout.
                        // Ignore this cycle and rerun.
                        //
                        m_activeTriggeredTask.Stop();
                        continue;
                    }
                }

                //
                // At this point the reader has returned with all the data
                // so we can stop the openning of the junction.
                //
                m_quitJunctionOpenningOperation = true;
                m_activeTriggeredTask.Stop();

                //
                // Assign the aquired data for each channel.
                // First clear all data from previous interation.
                //                
                ClearRawData(settings.ChannelsSettings.ActiveChannels);
                AssignRawDataToChannels(settings.ChannelsSettings.ActiveChannels, dataAquired);

                //
                // physical channel will include both simple and complex channels. 
                // 
                physicalChannels = GetChannelsForDisplay(settings.ChannelsSettings.ActiveChannels);

                //
                // calculate the physical data for each channel
                //
                GetPhysicalData(physicalChannels);

                // 
                // Increase file number by one
                // Save data if needed
                //
                finalFileNumber++;
                if (settings.GeneralSettings.IsFileSavingRequired)
                {
                    finalFileNumber = SaveData(settings.GeneralSettings.Path, settings.ChannelsSettings.ActiveChannels, physicalChannels, finalFileNumber);                    
                }

                //
                // Signal UI we have the data
                //
                if (DataAquired != null)
                {
                    DataAquired(this, new DataAquiredEventArgs(physicalChannels, finalFileNumber));
                }
            }

            return e.Cancel || isCancelled;
        }