// SGF  24-Aug-2011  INS-2314 -- introducing the code for O2 tests
        // SGF  Jan-2-2009  DSW-173, DSW-174
        /// <summary>
        /// Compare the current reading with the target for the sensor.
        /// </summary>
        private bool IsBumpCriterionMet(Sensor sensor, SensorGasResponse response, int criterion)
        {
            if (sensor.BumpCriterionType == CriterionType.FullSpanValue)
            {
                return(response.FullSpanReserve >= (double)criterion);
            }

            if (sensor.BumpCriterionType == CriterionType.PPMLimit)
            {
                return(response.Reading >= sensor.BumpCriterionPPMLimit);
            }

            if (sensor.BumpCriterionType == CriterionType.O2)
            {
                // BEGIN INS-7625 SSAM v7.6
                // O2 low bump test: Update O2 pass criteria when 18 or 19 %VOL cylinder used to be between 15% and 19.5%.
                if (response.GasConcentration.Concentration >= 18.0 && response.GasConcentration.Concentration <= 19.0)
                {
                    return(response.Reading > 15.0 && response.Reading < 19.5);
                }
                // O2 low bump test: Update O2 pass criteria when N2 or less than 18 %VOL cylinder used to be between 1% and 19.5%.
                else if (response.GasConcentration.Concentration < 18.0)
                {
                    return(response.Reading > 1.0 && response.Reading < 19.5);
                }
                // END INS-7625
            }

            throw new ApplicationException("Unknown/unsupported BumpCriterionType: " + sensor.BumpCriterionType.ToString());
        }
        /// <summary>
        /// Executes an instrument bump test operation.
        /// </summary>
        /// <returns>The completed event for this bump test.</returns>
        /// <exception cref="FailedBumpTestException">
        /// If anything extraordinary happened during the bump test.
        /// </exception>
        protected void BumpInstrument()
        {
            // SGF  14-Jun-2011  INS-1732
            _cumulativeBumpTestResponseTime = 0;

            try
            {
                List <InstalledComponent> workingSensorList = GetWorkingSensorList();

                // SGF  24-Aug-2011  INS-2314
                // Create sensor gas response objects for each sensor in the instrument
                foreach (InstalledComponent installedComponent in workingSensorList)
                {
                    if (!(installedComponent.Component is Sensor))  // Skip non-sensors.
                    {
                        continue;
                    }

                    if (!installedComponent.Component.Enabled) // Skip disabled sensors.
                    {
                        continue;
                    }

                    Sensor            sensor = (Sensor)installedComponent.Component;
                    SensorGasResponse sgr    = new SensorGasResponse(sensor.Uid, DateTime.UtcNow);
                    sgr.GasConcentration = new GasConcentration(sensor.CalibrationGas, sensor.CalibrationGasConcentration);
                    sgr.GasDetected      = sensor.GasDetected;
                    sgr.Type             = GasResponseType.Bump;

                    if (!sensor.IsBumpEnabled(GasEndPoints)) // For sensors that are not bump-enabled set the status as Skipped
                    {
                        sgr.Status = Status.Skipped;         //Suresh 19-APR-2012 INS-4537 (DEV)
                    }
                    //  SGF (Suresh)  3-Oct-2012  INS-2709
                    // If this is a sensor-type specific bump test, and this sensor should be skipped during the bump
                    // test operation, then set the Status on the Sensor Gas Response for this sensor to Skipped.
                    if (ComponentCodes.Count != 0 && !ComponentCodes.Contains(sensor.Type.Code))
                    {
                        sgr.Status = Status.Skipped;
                    }

                    _returnEvent.GasResponses.Add(sgr);
                }

                BumpTestInstrumentParallel(); // Also known as "quick bump".
            }
            finally
            {
                if (Master.Instance.SwitchService.BadPumpTubingDetectedDuringBump)
                {
                    Master.Instance.ConsoleService.UpdateState(ConsoleState.BumpStoppedCheckTubing);
                }
                else
                {
                    Master.Instance.ConsoleService.UpdateState(ConsoleState.BumpingInstrument);  // Clear the reference to a step in the bump testing process
                }
            }
        } // end-BumpInstrument
        /// <summary>Determines if any enabled sensors are currently in alarm.</summary>
        /// <param name="returnEvent">The event should only be provided for the final PostBump (PurgeAfterBump) check as sensors will be put into bump fault.</param>
        /// <remarks>INS-6723, INS-1735</remarks>
        private bool IsInstrumentInAlarm(Instrument instrument, InstrumentBumpTestEvent returnEvent)
        {
            bool         instrumentInAlarm = false;
            const string funcMsg           = "IsInstrumentInAlarm: ";

            foreach (InstalledComponent ic in instrument.InstalledComponents)
            {
                if (!(ic.Component is Sensor))
                {
                    continue;
                }

                Sensor sensor = (Sensor)ic.Component;

                if (sensor.IsBumpEnabled(GasEndPoints) == false)     // why do we check this? - JMP, 8/2015
                {
                    Log.Debug(String.Format("{0}Sensor in position={1} is not bump enabled", funcMsg, ic.Position));
                    continue;
                }

                if (sensor.Enabled == false)
                {
                    Log.Debug(String.Format("{0}Sensor in position={1} is not enabled", funcMsg, ic.Position));
                    continue;
                }

                // If sensor is in a cal/zero-failure state, then it's not reading gas.
                if (SensorGasResponse.IsFailedCalibrationStatus(sensor.CalibrationStatus))
                {
                    Log.Debug(String.Format("{0}Sensor in position={1} has {2} status", funcMsg, ic.Position, sensor.CalibrationStatus));
                    continue;
                }

                if (IsSensorInAlarm(sensor, ic.Position) == true)
                {
                    // This method is only intended to be called once when returnEvent is not null,
                    // and only for the PostBump (PurgeAfterBump) purge.
                    if (returnEvent != null)
                    {
                        SensorGasResponse sgr = GetSensorGasResponse(returnEvent, sensor);
                        // If instrument is still in alarm after purge is finishes, then we are setting the
                        // sensor's bump fault flag.
                        if (sgr != null && sgr.Status == Status.Passed)
                        {
                            Log.Debug(String.Format("{0}Setting sensor to FAILED", funcMsg));
                            sgr.Status = Status.Failed;
                            _instrumentController.SetSensorBumpFault(ic.Position, true);
                        }
                    }

                    instrumentInAlarm = true;
                }
            }

            Log.Debug(String.Format("{0}returned {1}", funcMsg, instrumentInAlarm ? "IN ALARM" : "NOT in alarm"));
            return(instrumentInAlarm);
        }
 /// <summary>
 /// INS-7282 - Check the sensor span reserve and if the value is less than the configured threshold, set the sensor calibration as Failed.
 /// This check is applicable only for Repair account type.
 /// </summary>
 /// <param name="sensor"></param>
 /// <param name="response"></param>
 /// <returns>bool</returns>
 private bool IsSensorFailedForRepairAccount( Sensor sensor, SensorGasResponse response, double spanReserveThreshold )
 {
     if ( Configuration.IsRepairAccount() && ( spanReserveThreshold > 0 ) )
     {            
         double spanReserve = ( response.Reading / sensor.CalibrationGasConcentration ) * 100;
         Log.Debug( string.Format( "Checking if sensor span reserve is below the threshold. Sensor:{0}, Span Reserve Threshold:{1}, Span Reserve:{2}", sensor.Type.Code, spanReserveThreshold, spanReserve ) );
         if ( spanReserve < spanReserveThreshold )
         {
             Log.Warning( string.Format( "Sensor span reserve is less than the configured span reserve threshold for Sensor:{0}, Span Reserve Threshold:{1}, Span Reserve:{2}", sensor.Type.Code, spanReserveThreshold, spanReserve ) );
             return true;
         }
     }
     return false;
 }
    /// <summary>
    /// INS-7282 - Check the sensor age based on the setup date and the configured sensor age
    /// This is applicable only for Repair account type.
    /// </summary>
    /// <param name="sensor"></param>
    /// <param name="response"></param>
    /// <returns>bool</returns>
    private bool IsSensorExpiredForServiceAccount( Sensor sensor, SensorGasResponse response, List<SensorCalibrationLimits> sensorCalibrationLimits )
    {
        // Ideally the sensorCalibrationLimits will be loaded only for repair Account, but its better to validate that.
        // Possible cases where the calibration limits are configured for the service account and then changed the service type of the account to another.
        if ( Configuration.IsRepairAccount() && ( sensorCalibrationLimits.Count > 0 ) )
        {            
            SensorCalibrationLimits sensorCalLimits = sensorCalibrationLimits.Find(sc => sc.SensorCode == sensor.Type.Code);
            if (sensorCalLimits != null)
            {
                int sensorLifeInMonths = sensorCalLimits.Age;
                DateTime sensorAge;
                try
                {
                    //Some of the LEL sensors are not having the setup date, in that case get the first four digits which determines the sensor setup year and month 
                    if (sensor.SetupDate == DomainModelConstant.NullDateTime)
                    {
                        int year = Convert.ToInt32(sensor.SerialNumber.Substring(0, 2));
                        int month = Convert.ToInt32(sensor.SerialNumber.Substring(2, 2));
                        DateTime date = new DateTime(year, month, 01);
                        sensorAge = date.AddMonths(sensorLifeInMonths);
                    }
                    else
                    {
                        sensorAge = sensor.SetupDate.AddMonths(sensorLifeInMonths);
                    }

                    bool expired = DateTime.Now > sensorAge;
                    Log.Debug( string.Format( "Expired sensor check: {0}, SetupDate:{1}, Expiry Age:{2}, AgeLimit:{3} (Expired={4})", 
                        sensor.Type.Code, Log.DateTimeToString(sensor.SetupDate), Log.DateTimeToString(sensorAge), sensorLifeInMonths, expired ) );
                    return expired;
                }
                catch (Exception ex)
                {
                    Log.Error( string.Format( "Skipping sensor age check for {0} sensor. Invalid SetupDate \"{1}\".",
                        sensor.Type.Code, Log.DateTimeToString(sensor.SetupDate) ), ex );
                    return false;
                }
            }
        }
        return false;
    }
        /// <summary>
        /// This method takes one reading for all enabled O2 sensors without flowing any gas
        /// which saves at least 4 seconds.
        /// <para>True - Run an O2 recovery purge.</para>
        /// <para>False - No O2 recovery purge needed.</para>
        /// </summary>
        /// <returns></returns>
        private bool IsFullO2RecoveryPurgeNeeded()
        {
            string funcMsg = "IsFullO2RecoveryPurgeNeeded: ";

            // Take an initial reading for all O2 sensors and only kick off a purge if one of them is not reading above 20%.
            List <InstalledComponent> o2Components = _returnEvent.DockedInstrument.InstalledComponents.FindAll(c => c.Component.Enabled && c.Component.Type.Code == SensorCode.O2);

            o2Components = o2Components.FindAll(o2 => _returnEvent.GetSensorGasResponseByUid(o2.Component.Uid) != null);

            int numO2SensorsPassed = 0;

            foreach (InstalledComponent ic in o2Components)
            {
                Sensor            sensor = (Sensor)ic.Component;
                SensorGasResponse sgr    = _returnEvent.GetSensorGasResponseByUid(sensor.Uid);
                sgr.Status = Status.O2RecoveryFailed;

                sgr.O2HighReading = _instrumentController.GetSensorReading(ic.Position, sensor.Resolution);
                sgr.Time          = DateTime.UtcNow;
                Log.Debug(string.Format("{0}O2 sensor UID={1} O2HighReading={2}.", funcMsg, sensor.Uid, sgr.O2HighReading.ToString()));

                // SGF  24-Aug-2011  INS-2314 -- getting rid of the test for the high threshold, since we don't use cylinders with higher than normal O2 levels
                // INS-7625 SSAM v7.6 Adding test for high threshold to be max 22% VOL
                // O2 high bump test: Update O2 pass criteria to be between 20% and 22%.
                if (InstrumentPurgeOperation.OXYGEN_FRESH_AIR_TEST_LOW_PASS_PCT <= sgr.O2HighReading && sgr.O2HighReading <= OXYGEN_FRESH_AIR_TEST_HIGH_PASS_PCT)
                {
                    Log.Debug(string.Format("{0}O2 sensor UID={1} reading is within normal range.", funcMsg, sensor.Uid));
                    sgr.Status             = Status.Passed;
                    sgr.IsO2HighBumpPassed = true;
                    numO2SensorsPassed++;
                }
            }

            if (numO2SensorsPassed == o2Components.Count)
            {
                return(false); // All O2 sensors pass the recovery test; time to short-circuit the purge
            }
            return(true);
        }
Ejemplo n.º 7
0
        } // end-CalibrateInstrumentSequential

        /// <summary>
        /// Calibrate the sensor.  Operates on "_component" member variable (an InstalledComponent)
        /// Note that this routine assumes that the parent class has already zeroed sensor.
        /// </summary>
        /// <param name="instComp">Component to be calibrated</param>
        /// <param name="gasEndPoints">Gas End Points to be used</param>
        /// <param name="isO2HighBumpFailed">Whether the CalibrateSensor is called as a part of O2 High Bump Failure</param>
        /// <returns></returns>
        internal SensorGasResponse CalibrateSensor(InstalledComponent instComp, List <GasEndPoint> gasEndPoints, bool isO2HighBumpFailed)
        {
            _component   = instComp;
            GasEndPoints = gasEndPoints;

            Log.Assert(this._component != null);

            bool        alreadyFailed = false;
            GasEndPoint gasEndPoint;
            Sensor      sensor = (Sensor)_component.Component;
            double      currentConcentration = sensor.CalibrationGasConcentration; // SGF  11-Oct-2010  INS-1189
            DateTime    durationStart        = DateTime.UtcNow;

            List <SensorCalibrationLimits> sensorCalibrationLimits = _testOnlySensorCalibrationLimits == null ? new SensorCalibrationLimitsDataAccess().FindAll() : _testOnlySensorCalibrationLimits;

            // SGF  14-Jun-2011  INS-1732
            SensorGasResponse response = _returnEvent.GetSensorGasResponseByUid(sensor.Uid);

            // If CalibrateSensor was called as a part of O2 High Bump Failure, the reponse
            // object will be null and has to be initialized for calibration operation.
            if (isO2HighBumpFailed)
            {
                response = new SensorGasResponse(sensor.Uid, DateTime.UtcNow);
                response.GasConcentration = new GasConcentration(sensor.CalibrationGas, currentConcentration);
                response.GasDetected      = sensor.GasDetected;
                response.Type             = GasResponseType.Calibrate;
            }
            if (response == null)
            {
                Log.Debug("CALIBRATION: Skipping sensor " + _component.Position + " due to missing sensor gas response object!");
                return(null);
            }
            response.Position = _component.Position;

            // Add in whatever cylinder was used for zeroing.  zeroGasEndPoint
            // will have been set by zerosensor(). Should never be null, but check just in case.
            if (_zeroingUsedGasEndPoint != null)
            {
                response.UsedGasEndPoints.Add(_zeroingUsedGasEndPoint);
            }

            try
            {
                // See if Zeroing was successful.  If not, set up response to indicate
                // the failure and then we can leave. (The finally block below will handle
                // the response as we leave this method)
                if (!_instrumentController.GetSensorZeroingStatus(_component.Position))
                {
                    Log.Debug(string.Format("CALIBRATION: SKIPPING SENSOR {0} ({1}) DUE TO FAILED ZEROING!", _component.Position, _component.Component.Uid));

                    // Setup status with our failed zeroing and last cal status.
                    response.Status  = Status.ZeroFailed;
                    response.Time    = DateTime.UtcNow;
                    response.Reading = 0.0;
                    return(response);
                }

                //Suresh 16-JAN-2012 INS-2480 - Begin
                if (!_instrumentController.IsSensorCalibrationEnabled(_component))
                {
                    Log.Debug("CALIBRATION: Calibration is disabled for sensor " + _component.Position + " (" + sensor.Type.Code + ")");

                    //In the above if condition we have already checked for zeroing status, therefore we verywell know
                    //when execution comes here then zeroing is passed for current calibration disabled sensor.

                    response.Status  = Status.ZeroPassed;
                    response.Time    = DateTime.UtcNow;
                    response.Reading = 0.0;

                    Log.Debug("CALIBRATION: SKIPPING SENSOR " + _component.Position + " (" + sensor.Type.Code + ")");

                    return(response);
                }
                //Suresh 16-JAN-2012 INS-2480 - End

                // SGF  03-Nov-2010  Single Sensor Cal and Bump
                if (ComponentCodes.Count != 0 && !ComponentCodes.Contains(sensor.Type.Code))
                {
                    Log.Debug(string.Format("CALIBRATION: Skipping sensor {0} ({1}) not included in schedule's specified component list.", _component.Position, sensor.Type.Code));

                    // This sensor will not be calibrated.  Indicate that zeroing passed.
                    response.Status  = Status.ZeroPassed;
                    response.Time    = DateTime.UtcNow;
                    response.Reading = 0.0;
                    return(response);
                }
                //INS-7282 - To determine if the sensor is expired based on the configured sensor age and setup date. Applicable only to Service Account
                if (IsSensorExpiredForServiceAccount(sensor, response, sensorCalibrationLimits))
                {
                    Log.Debug(string.Format("CALIBRATION: IsSensorExpiredForServiceAccount returned TRUE for {0} at position {1}.", sensor.Type.Code, _component.Position));
                    Log.Debug(string.Format("CALIBRATION: Marking {0} sensor at position {1} as {2}.", sensor.Type.Code, _component.Position, Status.Failed));
                    response.Status  = Status.Failed;
                    response.Time    = DateTime.UtcNow;
                    response.Reading = 0.0;
                    return(response);
                }

                if (_instrumentController.TestForInstrumentReset(response, "calibrating sensor, checked zeroing status") == true)
                {
                    Log.Warning("CALIBRATION: ABORTED DUE TO INSTRUMENT RESET");
                    return(response);
                }

                Log.Debug("CALIBRATION: Zeroing of sensor " + _component.Position + " determined as successful.");

                // Continue to calibrate, until there is known success, failure, or timeout
                while (true)
                {
                    // We'll fail to get a gas end point when we've tried and failed on
                    // every available bottle of appropriate gas.
                    try
                    {
                        gasEndPoint = GetSensorGasEndPoint(_component);   // Get gas end point for calibrating this sensor.
                    }
                    catch
                    {
                        if (alreadyFailed)
                        {
                            return(response);
                        }
                        throw;
                    }

                    try
                    {
                        if (gasEndPoint == null)   // There is no gas available.?
                        {
                            throw new CorrectCalibrationGasUnavailable(_returnEvent.DockedInstrument.SerialNumber);
                        }

                        // Purge between each passes when switching between attached cylinders during CAL to clear gases in line.
                        // INETQA-4189 RHP v7.6. Also make sure that this CYLINDER SWITCH PURGE does not happen for O2 Calibration initiated as part of Bump Test(02 sensor)
                        if (_usedGasEndPoint == null)
                        {
                            _usedGasEndPoint = (sensor.Type.Code == SensorCode.O2) ? null : gasEndPoint;
                        }
                        else if (_usedGasEndPoint != gasEndPoint && !isO2HighBumpFailed)
                        {
                            Log.Debug("CYLINDER SWITCH DETECTED : CLEAR GASES IN LINES BEFORE CALIBRATING NEXT SENSOR");
                            Stopwatch cylinderSwitchPurgeStopwatch = Log.TimingBegin("CALIBRATING - PURGE(CYLINDER-SWITCH)");
                            new InstrumentPurgeOperation(PurgeType.CylinderSwitch, _instrumentController, GasEndPoints, _returnEvent, new List <SensorGasResponse> {
                                response
                            }).Execute();
                            Log.TimingEnd("CALIBRATING - PURGE(CYLINDER-SWITCH)", cylinderSwitchPurgeStopwatch);
                            _usedGasEndPoint = gasEndPoint;
                        }

                        //Suresh 18-OCT-2011 INS-2293
                        // Indicate on the console which sensor is being calibrated
                        Master.Instance.ConsoleService.UpdateState(ConsoleState.CalibratingInstrument, Master.Instance.ConsoleService.GetSensorLabel(sensor.Type.Code));

                        // Guarantee that the correct calibration gas concentration is available.
                        double availableConcentration = _instrumentController.SetCalibrationGasConcentration(_component, gasEndPoint);

                        // If we didn't find anything with the gas.
                        if (availableConcentration == 0.0)
                        {
                            throw new CorrectCalibrationGasUnavailable(sensor.CalibrationGas.Code);
                        }

                        // Set the gas concentration.
                        response.GasConcentration.Concentration = sensor.CalibrationGasConcentration;
                        Log.Debug("Calibrating gas: " + sensor.CalibrationGas.Code + " conc: " + sensor.CalibrationGasConcentration);

                        // Determine the length of time to calibrate before timing out.  We add a an extra
                        // timeout cushion so we don't want the DS to timeout before the instrument.
                        TimeSpan calTimeOut = _instrumentController.GetSensorCalibrationTimeout(_component.Position) + _timeOutCushion;

                        if (_instrumentController.TestForInstrumentReset(response, "calibrating sensor, getting calibration timeout") == true)
                        {
                            Log.Warning("CALIBRATION: ABORTED DUE TO INSTRUMENT RESET");
                            return(response);
                        }

                        // Do any preconditioning necessary.
                        Stopwatch stopwatch = Log.TimingBegin("CAL - PRECONDITION SENSOR");
                        TimeSpan  preTime   = _instrumentController.PreconditionSensor(_component, gasEndPoint, response);
                        Log.TimingEnd("CAL - PRECONDITION SENSOR", stopwatch);

                        if (preTime.TotalSeconds > 0)   // will return zero if no precondition performed/needed.
                        {
                            response.UsedGasEndPoints.Add(new UsedGasEndPoint(gasEndPoint, CylinderUsage.Precondition, preTime));
                        }

                        if (!Master.Instance.ControllerWrapper.IsDocked())   // Did user undock instrument during preconditioning?
                        {
                            throw new InstrumentNotDockedException();
                        }

                        // SGF  14-Jun-2011  INS-1732
                        response.ReadingAfterPreconditioning = _instrumentController.GetSensorReading(_component.Position, sensor.Resolution);
                        response.TimeAfterPreconditioning    = DateTime.UtcNow;

                        if (_instrumentController.TestForInstrumentReset(response, "calibrating sensor, sensor preconditioned") == true)
                        {
                            Log.Warning("CALIBRATION: ABORTED DUE TO INSTRUMENT RESET");
                            return(response);
                        }

                        // SGF  Jan-13-2009  DSW-173
                        stopwatch = Log.TimingBegin("CAL - PAUSE GAS FLOW");
                        _instrumentController.PauseGasFlow(gasEndPoint, sensor, response);
                        Log.TimingEnd("CAL - PAUSE GAS FLOW", stopwatch);

                        if (_instrumentController.TestForInstrumentReset(response, "calibrating sensor, gas flow paused") == true)
                        {
                            Log.Warning("CALIBRATION: ABORTED DUE TO INSTRUMENT RESET");
                            return(response);
                        }

                        // Get the sensor's maximum reading.
                        double maximumReading = _instrumentController.GetSensorMaximumReading(_component.Position, sensor.Resolution);

                        response.BaseLine      = _instrumentController.GetSensorBaseline(_component.Position);
                        response.ZeroOffset    = _instrumentController.GetSensorZeroOffset(_component.Position, sensor.Resolution);
                        response.AccessoryPump = _instrumentController.AccessoryPump;

                        int calFlowRate = _instrumentController.GetSensorCalibrationFlowRate(_component);

                        _instrumentController.OpenGasEndPoint(gasEndPoint, calFlowRate);

                        DateTime startTime     = DateTime.UtcNow;
                        int      totalReadings = 0;

                        Log.Debug("CALIBRATION: BEGINNING CALIBRATION ON POSITION " + _component.Position + ", UID=" + _component.Component.Uid);

                        // Send the command to begin calibrating.
                        _instrumentController.BeginSensorCalibration(new int[] { _component.Position });

                        Log.Debug("CALIBRATION: Taking readings every " + _WAIT_INTERVAL + " msecs");

                        #region CALIBRATION LOOP

                        stopwatch = Log.TimingBegin("CAL - CALIBRATE SENSOR");

                        Status calibrationStatus = Status.InProgress;

                        bool instResetting = false;

                        bool?isCalibrating = false;
                        bool hasAborted    = false;
                        Pump.IsBadPumpTubing = false;
                        do
                        {
                            TimeSpan calTime = DateTime.UtcNow - startTime;
                            if (calTime > calTimeOut)
                            {
                                Log.Debug("CALIBRATION: DS timing out calibration.  Setting status to + " + Status.Failed);
                                calibrationStatus = Status.Failed;
                                break;
                            }

#if !TEST
                            Thread.Sleep(_WAIT_INTERVAL);
#endif

                            if (!Master.Instance.ControllerWrapper.IsDocked())   // watch out for user undocking during the long sleep interval.
                            {
                                break;
                            }

                            //If bad pump tubing is detected, throw flow failed exception
                            //which will further down be handled to report to iNet the situation
                            //and display appropriate error on LCD.
                            if (Master.Instance.PumpWrapper.IsBadPumpTubing())
                            {
                                string msg = "CALIBRATION: Bad pump tubing is detected.  Stopping calibration.";
                                Log.Debug(msg);
                                throw new FlowFailedException(gasEndPoint);
                            }

                            // Get current reading.
                            response.Reading = _instrumentController.GetSensorCalibrationReading(_component.Position, sensor.Resolution);
                            response.Time    = DateTime.UtcNow;

                            instResetting = _instrumentController.TestForInstrumentReset(response, "calibrating sensor, getting reading");

                            totalReadings++;

                            Log.Debug("CALIBRATION: (" + _component.Position + "), Reading #" + totalReadings + ": " + response.Reading + ", Span: " + response.FullSpanReserve);

                            // isCalibrating will be null if the instrument reset (InstrumentAborted)
                            isCalibrating = _instrumentController.IsSensorCalibrating(_component.Position);
                            hasAborted    = isCalibrating == null ? true : false;
                        }while (isCalibrating == true &&
                                Master.Instance.PumpWrapper.GetOpenValvePosition() > 0 &&
                                instResetting == false);

                        Log.TimingEnd("CAL - CALIBRATE SENSOR", stopwatch);

                        #endregion CALIBRATION LOOP

                        // If we detect we're undocked, then assume that's why we broke out of above loop
                        // debug: Do we really need this check?
                        if (!Master.Instance.ControllerWrapper.IsDocked())
                        {
                            string msg = "CALIBRATION: Aborting on sensor " + _component.Position + " - Undocked instrument.";
                            Log.Debug(msg);
                            throw new InstrumentNotDockedException();
                        }

                        if (instResetting == false)
                        {
                            instResetting = _instrumentController.TestForInstrumentReset(response, "calibrating sensor, calibration finished");
                        }

                        bool flowFailed = Master.Instance.PumpWrapper.GetOpenValvePosition() <= 0;

                        TimeSpan elapsedTime = DateTime.UtcNow - startTime;

                        // Put info for the cylinder used during the bump into the Response object.
                        // (iNet needs to know)
                        response.UsedGasEndPoints.Add(new UsedGasEndPoint(gasEndPoint, CylinderUsage.Calibration, elapsedTime));

                        // If we detect flow failure, then assume that's why we broke out of above loop.
                        if (flowFailed)
                        {
                            // TODO - Abort calibration on instrument?
                            string msg = "CALIBRATION: Aborting on sensor " + _component.Position + " - Flow failed.";
                            Log.Debug(msg);
                            throw new FlowFailedException(gasEndPoint);
                        }

                        if (calibrationStatus == Status.Failed)    // Timed out in above loop by IDS?
                        {
                            // TODO - Tell instrument to abort calibration?
                            Log.Debug("CALIBRATION: TIMED OUT by DS after " + elapsedTime.TotalSeconds + " seconds!");
                        }
                        else if (instResetting == true)
                        {
                            Log.Warning("CALIBRATION: ABORTED DUE TO INSTRUMENT RESET");
                            return(response);
                        }
                        else                          // Find out if instrument decided to pass or fail the calibration
                        {
                            calibrationStatus = _instrumentController.GetSensorCalibrationStatus(_component.Position) ? Status.Passed : Status.SpanFailed;
                            response.Time     = DateTime.UtcNow;

                            if (_instrumentController.TestForInstrumentReset(response, "calibrating sensor, retrieving calibration status") == true)
                            {
                                Log.Warning("CALIBRATION: ABORTED DUE TO INSTRUMENT RESET");
                                return(response);
                            }

                            Log.Debug(string.Format("CALIBRATION: Instrument {0} sensor {1} after {2} seconds!",
                                                    calibrationStatus, _component.Position, elapsedTime.TotalSeconds));

                            // Get instrument's final span reading
                            response.Reading  = _instrumentController.GetSensorSpanReserve(_component.Position);
                            response.SpanCoef = _instrumentController.GetSensorSpanCoeff(_component.Position);

                            response.Status = calibrationStatus;

                            // Checking for obviously screwed up sensor.
                            if (hasAborted)
                            {
                                // we already know the instrument reset
                                response.Status = Status.InstrumentAborted;
                            }
                            else if (response.Status == Status.Passed)
                            {
                                // last calibration time is only changed on the sensor if calibration passed
                                response.PostCal_LastCalibrationTime = _instrumentController.GetSensorLastCalibrationTime(response.Position);

                                // status should never be passed if span reserve is 0 or below
                                if (response.FullSpanReserve <= 0)
                                {
                                    Log.Warning(string.Format("CALIBRATION: FullSpanReserve is {0} but status is {1}. DS overriding with a Failed status.", response.FullSpanReserve, response.Status));
                                    response.Status = Status.Failed;
                                }
                                // last calibration time (pre-cal vs post-cal) should have changed
                                else if (response.WasCalibrationInstrumentAborted())
                                {
                                    Log.Warning(string.Format("CALIBRATION: Status is {0}, but LastCalibrationTime did not change.  DS overriding with an InstrumentAborted status.", response.Status));
                                    response.Status = Status.InstrumentAborted;                                     // A new response object will be created so most values are cleared before uploading to iNet.
                                }

                                //INS-7282 - Check the sensor span reserve and if the value is less than the configured threshold, set the sensor calibration as failed. Applicable only to repair Accounts.
                                if (IsSensorFailedForRepairAccount(sensor, response, Configuration.DockingStation.SpanReserveThreshold))
                                {
                                    response.Status = Status.Failed;
                                }
                            }
                        }

                        Log.Debug("CALIBRATION: " + response.Status
                                  + " - FullSpanReserve=" + response.FullSpanReserve
                                  + " SpanReading=" + response.Reading
                                  + " max=" + maximumReading);

                        if (response.Status == Status.Passed)
                        {
                            return(response);
                        }

                        alreadyFailed = true;
                    }
                    finally
                    {
                        _instrumentController.CloseGasEndPoint(gasEndPoint);
                    }
                }  // end-while(true)
            }
            finally
            {
                // How long did this sensor take to calibrate?
                TimeSpan duration = DateTime.UtcNow - durationStart;
                response.Duration = Convert.ToInt32(duration.TotalSeconds);

                // SGF  14-Jun-2011  INS-1732
                _cumulativeCalTestResponseTime  = _cumulativeCalTestResponseTime + response.Duration;
                response.CumulativeResponseTime = _cumulativeCalTestResponseTime;

                ResetTriedGasEndPoints();

                string msg = string.Empty;
                try
                {
                    if (Master.Instance.ControllerWrapper.IsDocked())
                    {
                        msg = "SetCalibrationGasConcentration";

                        //// Make certain oxygen sensors are set to ambient air concentrations.
                        ////
                        //// TODO - Prior to the calibration, the O2 sensor may have had a cal gas
                        //// concentration setting other than 20.9, so we probably shouldn't be blindly setting it back to 20.9.
                        //// This likely needs to be corrected as part of INS-1189 (which used to be DSW-156, which used to be DSZ-1305).
                        //// - JMP, 9/28/2009
                        ////
                        //if ( sensor.CalibrationGas.Code == GasCode.O2 )
                        //    // Guarantee that the correct calibration gas concentration is available.
                        //    SetCalibrationGasConcentration( GetSensorZeroAir( _component, false ) );

                        // SGF  11-Oct-2010  INS-1189
                        // Restore the concentration that was present upon entry into this method
                        _instrumentController.SetCalibrationGasConcentration(_component, currentConcentration, true);

                        msg = "GetSensorBumpStatus";

                        //Suresh 22-Feb-2012 INS-2705
                        //After calibration is completed , we need to update sensor bump test status because in scheduler we have logic
                        //to force calibration based on sensor BumpTestStatus
                        sensor.BumpTestStatus = _instrumentController.GetSensorBumpStatus(_component.Position);
                        // INETQA-4178 RHP v7.6 Update the return event BumpTestStatus as this is used the eventprocessor to update switch service instrument
                        // this is required for the scheduler logic discussed above
                        Sensor installedSensor = (Sensor)_returnEvent.DockedInstrument.InstalledComponents.Find(ic => ic.Component.Uid == sensor.Uid).Component;
                        if (installedSensor != null)
                        {
                            installedSensor.BumpTestStatus = sensor.BumpTestStatus;
                        }
                    }
                }
                catch (Exception e)
                {
                    Log.Error(msg, e);
                }
            }
        }  // end-CalibrateSensor()
Ejemplo n.º 8
0
        /// <summary>
        /// Legacy / standard calibration.  i.e., Non-quick-cal.
        /// </summary>
        protected internal void CalibrateInstrumentSequential()
        {
            bool      isCalibrationStarted = false;
            Exception thrownException      = null;

            _cumulativeCalTestResponseTime = 0;             // SGF  14-Jun-2011  INS-1732

            try
            {
                // Put instrument into calibration mode.
                // Note that we take the instrument back out of calibration
                // mode below in the 'finally' block.
                _instrumentController.BeginInstrumentCalibration();
                isCalibrationStarted = true;  // need to know to call EndInstrumentCalibration

                // Calibration each of the installed components sequentially.  That is, each sensor will be
                // calibrated by itself; upon completion of one sensor's calibration, the next sensor will
                // begin to be calibrated.
                foreach (InstalledComponent ic in _returnEvent.DockedInstrument.InstalledComponents)
                {
                    if (!(ic.Component is Sensor))      // Skip non-sensors.
                    {
                        continue;
                    }

                    Sensor sensor = (Sensor)ic.Component;

                    if (!_instrumentController.IsSensorEnabled(ic.Position))
                    {
                        Log.Debug("CALIBRATION (S): Skipping Disabled sensor " + ic.Position + " (" + sensor.Type.Code + ")");
                        continue;
                    }

                    // Get the gas code for the gas being used to calibrate this sensor.
                    string calGasCode = sensor.GetGasToCal();

                    // Set the sensor's calibration gas.
                    sensor.CalibrationGas = GasType.Cache[calGasCode];

                    Log.Debug("STARTING CALIBRATION (S) ON POSITION " + ic.Position + ", UID=" + ic.Component.Uid + ", " + ic.Component.Type.Code);
                    Log.Debug("CALIBRATION (S): Desired gas: " + sensor.CalibrationGas.Code + " conc: " + sensor.CalibrationGasConcentration);

                    // Attempt to calibrate the sensor.
                    _component = ic;  // derived classes will look at the member component.

                    CalibrateSensor(_component, GasEndPoints, false);
                }

                // Now, wrap-up steps will be taken.
                // Step 1:  Determine whether the instrument has had all sensors that must be calibrated pass.
                bool passed = _returnEvent.GasResponses.Count > 0;

                ///////////////////////////////////////////////////////////////////////////////
                // *** IMPORTANT NOTE *** The following 'for' loop must remain a 'for' loop!
                // Do NOT change it to a 'foreach' loop!  It used to be  foreach loop prior
                // to v6.0.  But as of v6.0 (which uses Compact Framework 2.0 unlike earlier
                // versions which use CF 1.0), if the GasResponses List is empty, then the
                // finally block sometimes terminates for some unknown reason as soon as the
                // foreach-loop is finished. Meaning all the code after the foreach block never
                // gets executed!
                // This was observed to be happening when a FlowFailedException was being
                // thrown by deeper code due to an empty cylinder.
                // This code used to work in 5.0 and earlier which used CF 1.0).
                // Seems to be broken in CF 2.0.  We ran into some some similar issues with
                // Compact Framework exception handling in CF 1.0; I was hoping it would be
                // fixed in CF 2.0, but I guess that is not the case.  - jpearsall 12/6/2007
                ///////////////////////////////////////////////////////////////////////////////
                for (int i = 0; i < _returnEvent.GasResponses.Count; i++)
                {
                    SensorGasResponse response = (SensorGasResponse)_returnEvent.GasResponses[i];
                    if (response.Status != Status.Passed && response.Status != Status.ZeroPassed)
                    {
                        passed = false;
                        break;
                    }
                }

                // Step 2: Tell the instrument that the calibration has ended.
                // (isCalibrationStarted is set when BeginInstrumentCalibration is called)
                if (isCalibrationStarted && Master.Instance.ControllerWrapper.IsDocked())
                {
                    try
                    {
                        _instrumentController.EndInstrumentCalibration();
                    }
                    catch (Exception e)
                    {
                        Log.Error("EndInstrumentCalibration", e);
                    }
                }

                // Step 3:  Purge the calibration gases from the gas lines in the docking station at the end of
                // the calibration operation.
                try
                {
                    // Purge gases at the end of the calibration operation.
                    Stopwatch calFinalStopwatch = Log.TimingBegin("CAL - PURGE(FINAL)");
                    new InstrumentPurgeOperation(PurgeType.PostCalibration, _instrumentController, GasEndPoints, _returnEvent).Execute();
                    Log.TimingEnd("CAL - PURGE(FINAL)", calFinalStopwatch);
                }
                catch (Exception ex)
                {
                    // We deliberately do NOT rethrow the exception here.
                    // Particularly not FlowFailedExceptions.
                    // If FlowFailedException was thrown earlier, during calibration, zeroing, etc.,
                    // then throwing another one here would cause the earlier thrown one to be lost.
                    // It's more important that we throw the original one since it's likely for
                    // calibration gas.
                    // Also, even if we didn't already throw one earlier, if we were to throw here,
                    // the remaining code in this 'finally' block would not get executed (such as
                    // turning off the pump).
                    // It's unfortunate that we do not throw here, since it causes us
                    // to not be able to notify iNet of empty zero air cylinder detected during the purge.
                    // This whole finally block would need restructured to fix this.  - JMP, 6/28/2011.
                    Log.Warning("Ignoring exception from Post-calibration purge operation", ex);
                }

                // Step 4:  Take the "after-purge" gas readings, and set the time-after-purge property for
                // each sensor gas response.
                //
                // SGF  14-Jun-2011  INS-1732
                // Get the after-purge gas readings
                foreach (InstalledComponent ic in _returnEvent.DockedInstrument.InstalledComponents)
                {
                    if (!(ic.Component is Sensor))      // Skip non-sensors.
                    {
                        continue;
                    }

                    Sensor sensor = (Sensor)ic.Component;
                    if (!sensor.Enabled)   // Skip disabled sensors
                    {
                        continue;
                    }

                    SensorGasResponse sgr = _returnEvent.GetSensorGasResponseByUid(sensor.Uid);
                    if (sgr != null)
                    {
                        sgr.ReadingAfterPurging = _instrumentController.GetSensorReading(ic.Position, sensor.Resolution);
                        sgr.TimeAfterPurging    = DateTime.UtcNow;
                    }
                }
            }
            catch (FlowFailedException ffe)
            {
                thrownException = ffe;
                throw;
            }
            catch (SensorErrorModeException seme)
            {
                thrownException = seme;
                throw;
            }
            catch (Exception e)
            {
                thrownException = e;
                throw;
            }
            finally
            {
                // Upon completion of the calibration activities, we must still take a few actions to
                // return the system to normal, if an exception was thrown during the calibration
                // process.  If no exception was thrown, these steps would already have been taken,
                // so there is no need to run these again.
                if (thrownException != null)
                {
                    //If any pump tubing issue is detected, mark that accordingly, so appropriate error
                    //will be displayed on LCD and uploaded to iNet as well.
                    Master.Instance.SwitchService.BadPumpTubingDetectedDuringCal = Master.Instance.PumpWrapper.IsBadPumpTubing();

                    // Step 5: Tell the instrument that the calibration has ended.
                    // (isCalibrationStarted is set when BeginInstrumentCalibration is called)
                    if (isCalibrationStarted && Master.Instance.ControllerWrapper.IsDocked())
                    {
                        try
                        {
                            _instrumentController.EndInstrumentCalibration();
                        }
                        catch (Exception e)
                        {
                            Log.Error("EndInstrumentCalibration", e);
                        }
                    }

                    // Step 6:  Purge the calibration gases from the gas lines in the docking station at the end of
                    // the calibration operation.
                    try
                    {
                        // Purge gases at the end of the calibration operation.
                        Stopwatch calPurgeStopwatch = Log.TimingBegin("CAL - PURGE(FINAL, ON ERROR)");
                        new InstrumentPurgeOperation(PurgeType.PostCalibration, _instrumentController, GasEndPoints, _returnEvent).Execute();
                        Log.TimingEnd("CAL - PURGE(FINAL, ON ERROR)", calPurgeStopwatch);
                    }
                    catch (Exception ex)
                    {
                        // We deliberately do NOT rethrow the exception here.
                        // Particularly not FlowFailedExceptions.
                        // If FlowFailedException was thrown earlier, during calibration, zeroing, etc.,
                        // then throwing another one here would cause the earlier thrown one to be lost.
                        // It's more important that we throw the original one since it's likely for
                        // calibration gas.
                        // Also, even if we didn't already throw one earlier, if we were to throw here,
                        // the remaining code in this 'finally' block would not get executed (such as
                        // turning off the pump).
                        // It's unfortunate that we do not throw here, since it causes us
                        // to not be able to notify iNet of empty zero air cylinder detected during the purge.
                        // This whole finally block would need restructured to fix this.  - JMP, 6/28/2011.
                        Log.Warning("Ignoring exception from Post-calibration purge operation", ex);
                    }
                }
            }
        } // end-CalibrateInstrumentSequential
        } // end-Prepare

        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        private bool Purge()
        {
            const string funcMsg = "Purge: ";

            bool     purgeDone      = false;
            int      purgeStepTime  = 2;       // 2 seconds is a guess; configure as necessary
            TimeSpan maxPurgeLength = new TimeSpan(0, 0, _purge1Seconds);
            TimeSpan elapsedTime    = new TimeSpan(0, 0, 0);
            DateTime purgeStartTime; // initialized immediately prior to each loop which this is used
            DateTime cylinderStartTime = DateTime.UtcNow;

            try
            {
                // Indicate that the purging process is now in progress
                ConsoleState consoleState;
                if (_purgeType == PurgeType.PreCalibration || _purgeType == PurgeType.PostCalibration)
                {
                    consoleState = ConsoleState.CalibratingInstrument;
                }
                else if (_purgeType == PurgeType.CylinderSwitch)
                {
                    consoleState = (_returnGasResponseEvent is InstrumentCalibrationEvent) ? ConsoleState.CalibratingInstrument : ConsoleState.BumpingInstrument;
                }
                else
                {
                    consoleState = ConsoleState.BumpingInstrument;
                }

                Master.Instance.ConsoleService.UpdateState(consoleState, ConsoleServiceResources.PURGING);

                _instrumentController.OpenGasEndPoint(_airEndPoint, _desiredFlow);

                switch (_purgeType)
                {
                // Constant purge only
                case PurgeType.PreCalibration:     // prior to zeroing, we do a fixed-time purge.
                    Log.Debug(string.Format("{0}Purging for {1} seconds.", funcMsg, _purge1Seconds));

                    purgeStartTime = DateTime.UtcNow;
                    while (elapsedTime < maxPurgeLength)
                    {
                        if (!Master.Instance.ControllerWrapper.IsDocked())
                        {
                            throw new InstrumentNotDockedException();
                        }

                        Thread.Sleep(1000);       // Wait for the purge.

                        // See if ResourceService determined that cylinder was empty while we slept.
                        CheckAir(_airEndPoint);       // throws if empty cylinder is detected

                        elapsedTime = DateTime.UtcNow - purgeStartTime;
                    }
                    purgeDone = true;
                    break;

                // Constant purge followed by a "smart purge"
                case PurgeType.PostCalibration:
                case PurgeType.PostBump:
                    // constant purge
                    if (_purge1Seconds > 0)
                    {
                        Log.Debug(string.Format("{0}Purging for {1} seconds.", funcMsg, _purge1Seconds));

                        purgeStartTime = DateTime.UtcNow;
                        while (elapsedTime < maxPurgeLength)
                        {
                            if (!Master.Instance.ControllerWrapper.IsDocked())
                            {
                                throw new InstrumentNotDockedException();
                            }

                            Thread.Sleep(1000);                                       // Wait for the purge.

                            // See if ResourceService determined that cylinder was empty while we slept.
                            CheckAir(_airEndPoint);                                       // throws if empty cylinder is detected

                            elapsedTime = DateTime.UtcNow - purgeStartTime;
                        }
                    }

                    // smart purge
                    if (_purge2Seconds > 0)
                    {
                        Log.Debug(string.Format("{0}Purging for a maximum of {1} seconds.", funcMsg, _purge2Seconds));

                        // reset as constant purge above may have used these
                        elapsedTime    = new TimeSpan(0, 0, 0);
                        maxPurgeLength = new TimeSpan(0, 0, _purge2Seconds);

                        purgeStartTime = DateTime.UtcNow;
                        while (elapsedTime < maxPurgeLength && purgeDone == false)
                        {
                            // See if the instrument is in alarm.  If it is not, the purge can end early.
                            if (IsInstrumentInAlarm(_returnGasResponseEvent.DockedInstrument, null) == false)
                            {
                                purgeDone = true;
                            }
                            else
                            {
                                CheckAir(_airEndPoint);                                           // throws if empty cylinder is detected
                                Log.Debug(string.Format("{0}Purging for {1} seconds.", funcMsg, purgeStepTime));
                                Thread.Sleep(purgeStepTime * 1000);
                            }
                            elapsedTime = DateTime.UtcNow - purgeStartTime;
                        }

                        if (purgeDone)
                        {
                            Log.Debug(string.Format("{0}Instrument is NOT in alarm after {1} seconds.", funcMsg, elapsedTime.TotalSeconds));
                        }
                        else
                        {
                            Log.Debug(string.Format("{0}Instrument is IN ALARM after {1} seconds.  MAXIMUM PURGE TIME EXCEEDED.", funcMsg, elapsedTime.TotalSeconds));
                        }

                        if (!purgeDone && _purgeType == PurgeType.PostBump)
                        {
                            // If we got here, that must mean that the PurgeAfterBump setting is enabled.
                            Log.Debug(string.Format("{0}PUTTING SENSORS INTO BUMP FAULT THAT ARE STILL IN ALARM.", funcMsg));
                            // See if the instrument is still in alarm.  Report a purgeDone of 'false' if the instrument is still in alarm.
                            // Put sensors into bump fault that are still in alarm.
                            purgeDone = !IsInstrumentInAlarm(_returnGasResponseEvent.DockedInstrument, _returnGasResponseEvent as InstrumentBumpTestEvent);

                            if (purgeDone)
                            {
                                Log.Debug(string.Format("{0}Instrument was NOT in alarm for the final check.", funcMsg));
                            }
                        }
                    }
                    else
                    {
                        // If PurgeAfterBump setting is disabled than we can report that the purge completed successfully.
                        purgeDone = true;
                    }

                    break;

                // Smart purge only
                case PurgeType.PreBump:
                    Log.Debug(string.Format("{0}Purging for a maximum of {1} seconds.", funcMsg, _purge1Seconds));

                    purgeStartTime = DateTime.UtcNow;
                    while (elapsedTime < maxPurgeLength && purgeDone == false)
                    {
                        // See if the instrument is in alarm.  If it is not, the purge can end early.
                        if (IsInstrumentInAlarm(_returnGasResponseEvent.DockedInstrument, null) == false)
                        {
                            purgeDone = true;
                        }
                        else
                        {
                            CheckAir(_airEndPoint);                                       // throws if empty cylinder is detected
                            Log.Debug(string.Format("{0}Purging for {1} seconds.", funcMsg, purgeStepTime));
                            Thread.Sleep(purgeStepTime * 1000);
                        }
                        elapsedTime = DateTime.UtcNow - purgeStartTime;
                    }

                    if (purgeDone)
                    {
                        Log.Debug(string.Format("{0}Instrument is NOT in alarm after {1} seconds.", funcMsg, elapsedTime.TotalSeconds));
                    }
                    else
                    {
                        Log.Debug(string.Format("{0}Instrument is IN ALARM after {1} seconds.  MAXIMUM PURGE TIME EXCEEDED.", funcMsg, elapsedTime.TotalSeconds));
                    }

                    break;

                case PurgeType.O2Recovery:
                    Log.Debug(string.Format("{0}Purging O2 sensors for recovery from depravation for a maximum of {1} seconds.", funcMsg, _purge1Seconds));

                    // From all the InstalledComponents, get a sub-list of just the O2 sensors.
                    // Then, from the list of O2 sensors, whittle it down to just O2 sensors that we have SGRs for.
                    // SGRs might be missing, for example, if we just did a bump test, but one or more sensors were
                    // already in a cal-fault state. (Which could happen with dualsense sensors where one in the pair
                    // is not working.) Those failed sensors would not have been bump tested, so there will be no SGR,
                    // and since the sensoris not "working", we don't have to worry about getting its reading.
                    List <InstalledComponent> o2Components = _returnGasResponseEvent.DockedInstrument.InstalledComponents.FindAll(c => c.Component.Enabled && c.Component.Type.Code == SensorCode.O2);
                    o2Components = o2Components.FindAll(o2 => _returnGasResponseEvent.GetSensorGasResponseByUid(o2.Component.Uid) != null);

                    purgeStartTime = DateTime.UtcNow;
                    while (elapsedTime < maxPurgeLength && purgeDone == false)
                    {
                        Thread.Sleep(1000);

                        CheckAir(_airEndPoint);       // throws if empty cylinder is detected

                        // SGF  24-Aug-2011  INS-2314 -- check O2 sensors for their current readings
                        int numO2SensorsPassed = 0;
                        foreach (InstalledComponent ic in o2Components)
                        {
                            Sensor            sensor = (Sensor)ic.Component;
                            SensorGasResponse sgr    = _returnGasResponseEvent.GetSensorGasResponseByUid(sensor.Uid);
                            sgr.Status = Status.O2RecoveryFailed;

                            sgr.O2HighReading = _instrumentController.GetSensorReading(ic.Position, sensor.Resolution);
                            sgr.Time          = DateTime.UtcNow;
                            Log.Debug(string.Format("{0}O2 sensor UID={1} O2HighReading={2}.", funcMsg, sensor.Uid, sgr.O2HighReading.ToString()));

                            // SGF  24-Aug-2011  INS-2314 -- getting rid of the test for the high threshold, since we don't use cylinders with higher than normal O2 levels
                            if (OXYGEN_FRESH_AIR_TEST_LOW_PASS_PCT <= sgr.O2HighReading)
                            {
                                Log.Debug(string.Format("{0}O2 sensor UID={1} reading is within normal range.", funcMsg, sensor.Uid));
                                // INETQA-4149 INS-7625 SSAM v7.6 IsO2HighBumpPassed flag is set to true if O2 sensor passes the recovery purge.
                                // Else if recovery fails, calibration is initiated to recover the O2 sensor.
                                sgr.IsO2HighBumpPassed = true;
                                sgr.Status             = Status.Passed;
                                numO2SensorsPassed++;
                            }
                        }

                        if (numO2SensorsPassed == o2Components.Count)
                        {
                            purgeDone = true;     // All O2 sensors pass the recovery test; time to short-circuit the purge
                        }
                        elapsedTime = DateTime.UtcNow - purgeStartTime;
                    }

                    // For any O2 sensors that failed to recover above, mark the SGR status as O2RecoveryFailed
                    foreach (InstalledComponent ic in o2Components)
                    {
                        SensorGasResponse sgr = _returnGasResponseEvent.GetSensorGasResponseByUid(ic.Component.Uid);
                        sgr.SpanCoef = _instrumentController.GetSensorSpanCoeff(ic.Position);
                        sgr.UsedGasEndPoints.Add(new UsedGasEndPoint(_airEndPoint, CylinderUsage.BumpHigh, elapsedTime));
                        if (sgr.Status == Status.O2RecoveryFailed)
                        {
                            Log.Warning(string.Format("{0} O2 SENSOR (UID={1}) FAILED TO RECOVER FROM DEPRAVATION.", funcMsg, ic.Component.Uid));
                        }
                    }

                    GasType gasType = GasType.Cache[GasCode.O2];
                    Master.Instance.ConsoleService.UpdateState(ConsoleState.BumpingInstrument, gasType.Symbol);

                    break;     // end-PurgeType.O2Recovery

                // Purge between use of different gas endpoints
                case PurgeType.CylinderSwitch:
                    Log.Debug(string.Format("{0}Purging for {1} seconds.", funcMsg, _purge1Seconds));

                    purgeStartTime = DateTime.UtcNow;
                    while (elapsedTime < maxPurgeLength && purgeDone == false)
                    {
                        // See if the sensor readings have met the purge complete criterion. If it has, the purge can end early.
                        // During a calibration, this is a Constant purge only
                        if (_returnGasResponseEvent is InstrumentBumpTestEvent)
                        {
                            purgeDone = IsInstrumentPurgeCriterionMet();
                        }

                        Thread.Sleep(1000);     // Wait for the purge.

                        // See if ResourceService determined that cylinder was empty while we slept.
                        CheckAir(_airEndPoint);     // throws if empty cylinder is detected
                        elapsedTime = DateTime.UtcNow - purgeStartTime;
                    }

                    if (_returnGasResponseEvent is InstrumentCalibrationEvent)
                    {
                        purgeDone = true;       // For Calibration, its fixed time purge
                    }
                    Log.Debug(string.Concat("CYLINDER-SWITCH purge ", purgeDone ? "PASSED" : "FAILED"));

                    break;
                } // end-switch
            }
            catch (CommunicationAbortedException cae)   // undocked?
            {
                throw new InstrumentNotDockedException(cae);
            }
            catch (InstrumentNotDockedException)
            {
                throw;
            }
            catch (FlowFailedException ffe)   // ran out of gas during the purge?
            {
                Log.Warning(Name + " throwing FlowFailedException for position " + ffe.GasEndPoint.Position);
                throw;
            }
            catch (Exception ex)
            {
                throw new UnableToPurgeException(ex);
            }
            finally
            {
                _instrumentController.CloseGasEndPoint(_airEndPoint);

                //Purge is alway run at max voltage which satisfies condition of bad kink tubing even
                //there is no issue with tubing.  So, explicitly set "Pump.IsBadPumpTubing" to false
                //once purge operation is complete.
                Pump.IsBadPumpTubing = false;

                if (_returnGasResponseEvent != null)
                {
                    // SGF  06-Jun-2011  INS-1735
                    // Add a new UsedGasEndPoint object to the return event if the duration is greater than 0.
                    TimeSpan durationInUse = DateTime.UtcNow - cylinderStartTime;
                    if (durationInUse.CompareTo(TimeSpan.MinValue) > 0)
                    {
                        _returnGasResponseEvent.UsedGasEndPoints.Add(new UsedGasEndPoint(_airEndPoint, CylinderUsage.Purge, durationInUse));
                    }
                }

                Log.Debug(string.Format("{0}Finished", funcMsg));
            }

            return(purgeDone);
        }
        private bool Prepare()
        {
            const string funcMsg = "PreparePurge: ";

            Log.Debug(string.Format("{0}Starting ({1})", funcMsg, _purgeType));

            // SGF  06-Jun-2011  INS-1735
            // Do not perform a purge if the type is Unknown or Invalid
            if (_purgeType == PurgeType.Unknown)
            {
                return(false);
            }

            if (Master.Instance.ControllerWrapper.IsDocked() == false)
            {
                Log.Debug(string.Format("{0}No instrument is docked -- NO PURGE NECESSARY", funcMsg));
                return(false);
            }

            bool instHasAccessoryPump  = false;
            bool isPumpAdapterAttached = false;

            if (_instrumentController != null)
            {
                instHasAccessoryPump  = (_instrumentController.AccessoryPump == AccessoryPumpSetting.Installed);
                isPumpAdapterAttached = Master.Instance.ControllerWrapper.IsPumpAdapterAttached();

                Log.Debug(string.Format("{0}PurgeType={1}", funcMsg, _purgeType));
                Log.Debug(string.Format("{0}AccessoryPump={1}, PumpAdapter={2}", funcMsg, instHasAccessoryPump, isPumpAdapterAttached));

                // Normal flow rate for purging is the docking station's maximum flow rate.
                // Except if instrument has a pump and there is no pump adapter, then we purge
                // using a lower flow rate to avoid damaging the instrument pump.
                if (instHasAccessoryPump && !isPumpAdapterAttached)
                {
                    _desiredFlow = Pump.StandardFlowRate;
                }
            }
            else
            {
                _desiredFlow = Pump.MaxFlowRate;
            }

            bool purgeRequired = true;             // helper variable for multiple case blocks
            bool isDSX         = !Configuration.DockingStation.Reservoir;

            Log.Debug(string.Format("{0}Reservoir={1}", funcMsg, Configuration.DockingStation.Reservoir));

            int PUMP_PURGE_20SECONDS = (instHasAccessoryPump) ? 20 : 0;

            // Determine if we really have to purge or not (based on instrument's sensor
            // configuration) and, if so, how long we should purge.
            switch (_purgeType)
            {
            case PurgeType.PreCalibration:
                // INS-6723: DSX - 10 seconds (30 if aspirated)
                //       iNet DS - 40 seconds (60 if aspirated)
#if TEST
                _purge1Seconds = 0;     // TODO: not a fix. Will have to be revisited
#else
                _purge1Seconds = isDSX ? 10 + PUMP_PURGE_20SECONDS : 40 + PUMP_PURGE_20SECONDS;
#endif
                break;

            case PurgeType.PostCalibration:
                // INS-6723: DSX / iNet DS - 30 seconds for enabled exotic sensor (50 if aspirated), followed by
                //           DSX / iNet DS - 60 seconds smart purge regardless
                foreach (InstalledComponent ic in _returnGasResponseEvent.DockedInstrument.InstalledComponents)
                {
                    if ((ic.Component is Sensor) && ((Sensor)ic.Component).RequiresExtendedPostCalibrationPurge)
                    {
                        Log.Debug(string.Format("{0}Purge required for {1} sensor.", funcMsg, ic.Component.Type.Code));
                        _purge1Seconds = 30 + PUMP_PURGE_20SECONDS;
                        break;
                    }
                }
#if !TEST
                _purge2Seconds = 60;     // follow-up smart purge
#else
                _purge2Seconds = 0;
#endif

                break;

            case PurgeType.PreBump:
                // INS-6723: DSX / iNet DS - 30 seconds smart purge if aspirated or for enabled exotic sensor
                purgeRequired = instHasAccessoryPump;

                if (purgeRequired)
                {
                    Log.Debug(string.Format("{0}Purge required for aspirated instrument.", funcMsg));
                }
                else
                {
                    // if instrument does not have a pump, see if there are enabled exotic sensors
                    foreach (InstalledComponent ic in _returnGasResponseEvent.DockedInstrument.InstalledComponents)
                    {
                        if ((ic.Component is Sensor) && ((Sensor)ic.Component).RequiresBumpTestPurge(GasEndPoints) == true)
                        {
                            Log.Debug(string.Format("{0}Purge required for {1} sensor.", funcMsg, ic.Component.Type.Code));
                            purgeRequired = true;
                            break;
                        }
                    }
                }

                if (purgeRequired)
                {
#if TEST
                    _purge1Seconds = 0;     // TODO: not a fix. Will have to be revisited
#else
                    _purge1Seconds = 30;
#endif
                }
                else
                {
                    Log.Debug(string.Format("{0}PurgeType={1} -- NO PURGE NECESSARY.", funcMsg, _purgeType));
                    return(false);
                }

                break;

            case PurgeType.PostBump:
                // INS-6723: DSX / iNet DS - 30 seconds for enabled exotic sensor (50 if aspirated), followed by
                //           DSX / iNet DS - 60 seconds smart purge when "Purge After Bump" setting enabled
                purgeRequired = false;

                foreach (InstalledComponent ic in _returnGasResponseEvent.DockedInstrument.InstalledComponents.FindAll(c => c.Component is Sensor))
                {
                    if (((Sensor)ic.Component).RequiresBumpTestPurge(GasEndPoints) == true)
                    {
                        Log.Debug(string.Format("{0}Purge required for {1} sensor.", funcMsg, ic.Component.Type.Code));
                        purgeRequired = true;
#if TEST
                        _purge1Seconds = 0;     // TODO: not a fix. Will have to be revisited
#else
                        _purge1Seconds = 30 + PUMP_PURGE_20SECONDS;
#endif
                        break;
                    }
                }

                if (Configuration.DockingStation.PurgeAfterBump)
                {
                    purgeRequired  = true;
                    _purge2Seconds = 60;
                    Log.Debug(string.Format("{0}Purge required as PurgeAfterBump={1}.", funcMsg, Configuration.DockingStation.PurgeAfterBump));
                }


                if (!purgeRequired)
                {
                    Log.Debug(string.Format("{0}PurgeType={1} -- NO PURGE NECESSARY", funcMsg, _purgeType));
                    return(false);
                }

                break;

            case PurgeType.O2Recovery:
                // INS-6684: DSX / iNet DS - until 20% vol seen with 120 seconds maximum
                purgeRequired = false;
                bool isSecondHighBump = false;

                foreach (InstalledComponent ic in _returnGasResponseEvent.DockedInstrument.InstalledComponents)
                {
                    if (!(ic.Component is Sensor))
                    {
                        continue;
                    }
                    if (!((Sensor)ic.Component).Enabled)
                    {
                        continue;
                    }
                    if (ic.Component.Type.Code == SensorCode.O2)
                    {
                        Log.Debug(string.Format("{0}Purge required for {1} sensor.", funcMsg, ic.Component.Type.Code));
                        purgeRequired = true;
                        // INS-7625 SSAM v7.6
                        // Second high bump test should flow gas and have a much shorter timeout of 30 seconds.
                        // If the purge is done as a part of second high bump test, set the purge timeout to 30 seconds.
                        // Else, set it to 120 seconds.
                        SensorGasResponse sgr = _returnGasResponseEvent.GetSensorGasResponseByUid(ic.Component.Uid);
                        if (sgr != null && sgr.IsSecondO2HighBump)
                        {
                            isSecondHighBump = true;
                        }
#if !TEST
                        _purge1Seconds = isSecondHighBump ? 30 : 120;     // See INS-6684 (and INS-2314)
#else
                        _purge1Seconds = 0;
#endif
                    }
                }

                if (!purgeRequired)
                {
                    Log.Debug(string.Format("{0}PurgeType={1} -- NO PURGE NECESSARY", funcMsg, _purgeType));
                    return(false);
                }

                break;

            case PurgeType.CylinderSwitch:
#if !TEST
                // 40 seconds (60 if aspirated)
                _purge1Seconds = 40 + PUMP_PURGE_20SECONDS;
                // For Calibration, 30 seconds (45 if aspirated)
                if (_returnGasResponseEvent is InstrumentCalibrationEvent)
                {
                    _purge1Seconds = instHasAccessoryPump ? 45 : 30;
                }
#else
                _purge1Seconds = 0;
#endif
                break;
            }

            Log.Debug(string.Format("{0}Purge seconds={1}, Desired flow={2}", funcMsg, _purge1Seconds, _desiredFlow));

            if (_purge2Seconds > 0)
            {
                Log.Debug(string.Format("{0}Secondary purge seconds={1}", funcMsg, _purge2Seconds));
            }

            // First try to find fresh air.  If found, then use it.
            try
            {
                _airEndPoint = GetFreshAir(); // Find the fresh air valve
                Log.Debug(string.Format("{0}Found fresh air for purging.", funcMsg));
            }
            catch (CorrectCalibrationGasUnavailable)
            {
                Log.Debug(string.Format("{0}No fresh air found for purging.  Looking for zero air instead.", funcMsg));
            }

            // If no fresh air found, then look for zero air.  Note that we pass a true
            // to GetSensorZeroAir causing it to NOT look for a fresh air alternative.
            // We just looked for fresh air above.
            if (_airEndPoint == null)
            {
                _airEndPoint = GetZeroAir(true);
                Log.Debug(string.Format("{0}Found zero air for purging.", funcMsg));
            }

            if (_airEndPoint == null)
            {
                Log.Debug(string.Format("{0}FOUND NO AIR FOR PURGING.", funcMsg));
                return(false);
            }

            return(true);
        } // end-Prepare
    /// Zeroes all sensors contained on the instrument.
    /// 
    /// NOTE: THIS ROUTINE LOOKS LIKE IT CAN ZERO A SPECIFIC SENSOR 
    /// BUT IN ACTUALITY, IT ALWAYS ZEROS ALL SENSORS.
    /// </summary>
    /// <param name="installedComponents">Contains InstalledComponents for the instrument being zeroed.</param>
    private void ZeroSensors( IEnumerable<InstalledComponent> installedComponents )
    {
        Log.Debug( "ZEROING: Preparing to zero" );

        // Indicate that the zeroing process is now in progress
        Master.Instance.ConsoleService.UpdateState( ConsoleState.CalibratingInstrument, ConsoleServiceResources.ZEROING );

        // See if we have a CO2 sensor.  If so, then we can only zero using zero air.
        // fresh air is NOT allowed.  If no CO2 sensor is installed, then fresh
        // air may be used as an alternative to zero air, if zero air is not found.
        bool useZeroAirOnly = false;
        foreach ( InstalledComponent installedComponent in installedComponents )
        {
            if ( installedComponent.Component.Type.Code == SensorCode.CO2 )
            {
                Sensor sensor = (Sensor)installedComponent.Component;

                if ( !sensor.Enabled ) // if it's disabled, we won't be zeroing it.
                    continue;

                Log.Debug( "ZEROING: Found CO2 sensor.  Will not use fresh air to zero." );
                useZeroAirOnly = true;
                break;
            }
        }

		GasEndPoint zeroEndPoint = null;

        DateTime startTime = DateTime.UtcNow;

        try
        {
			_zeroingUsedGasEndPoint = null;  // Reset any previous setting (if any).

            zeroEndPoint = GetSensorZeroAir( null, useZeroAirOnly, false );  // Get the zeroing gas end point for this gas.

            _instrumentController.OpenGasEndPoint( zeroEndPoint, Pump.StandardFlowRate );

            // ZeroSensor will return false if IDS times out before instrument finishes zeroing.
            // Return value does NOT indicate if zeroing was successful or not!
            if ( !_instrumentController.ZeroSensors( zeroEndPoint ) )
                throw new UnableToZeroInstrumentSensorsException();

            // SGF  14-Jun-2011  INS-1732 -- get sensor readings after zeroing has taken place
            foreach ( InstalledComponent ic in _returnEvent.DockedInstrument.InstalledComponents )
            {
                if ( !( ic.Component is Sensor ) )  // Skip non-sensors.
                    continue;

                Sensor sensor = (Sensor)ic.Component;
                if ( !sensor.Enabled ) // Skip disabled sensors
                    continue;

                SensorGasResponse sgr = _returnEvent.GetSensorGasResponseByUid( sensor.Uid );
                if ( sgr != null )
                {
                    sgr.ReadingAfterZeroing = _instrumentController.GetSensorReading( ic.Position, sensor.Resolution );
                    sgr.TimeAfterZeroing = DateTime.UtcNow;
				}
            }
        }
        catch ( Exception e )
        {
            Log.Error( "Zeroing Sensor", e );
            throw;
        }
        finally
        {
            Log.Debug( "ZEROING: Finished" );

            _instrumentController.CloseGasEndPoint( zeroEndPoint );

            if ( zeroEndPoint != null )  // how could this ever be null?
                _zeroingUsedGasEndPoint = new UsedGasEndPoint( zeroEndPoint, CylinderUsage.Zero, DateTime.UtcNow - startTime, 0 );
        }
    }
    protected void CalibrateInstrument()
    {
        try
		{
			// SGF  14-Jun-2011  INS-1732
			// Create sensor gas response objects for each sensor in the instrument
			foreach ( InstalledComponent installedComponent in _returnEvent.DockedInstrument.InstalledComponents )
			{
				if ( !( installedComponent.Component is Sensor ) )  // Skip non-sensors.
					continue;
				if ( !installedComponent.Component.Enabled )
					continue;

				Sensor sensor = (Sensor)installedComponent.Component;
				SensorGasResponse sgr = new SensorGasResponse( sensor.Uid, DateTime.UtcNow );
				sgr.GasConcentration = new GasConcentration( sensor.CalibrationGas, sensor.CalibrationGasConcentration );
                sgr.GasDetected = sensor.GasDetected;
				sgr.Type = GasResponseType.Calibrate;
				// JFC 07-Mar-2014 INS-4839
				// Recording the last calibration time before calibration to ensure it will be changed by the instrument once
				// calibration of the sensor completes.  If it does not, a status of InstrumentAborted should be assumed.
				sgr.PreCal_LastCalibrationTime = _instrumentController.GetSensorLastCalibrationTime( installedComponent.Position );

				if ( _zeroingUsedGasEndPoint != null )
					sgr.UsedGasEndPoints.Add( _zeroingUsedGasEndPoint );

				_returnEvent.GasResponses.Add( sgr );
			}

			// SGF  14-Jun-2011  INS-1732
			SensorGasResponse response = null;
			if ( _returnEvent.GasResponses.Count > 0 )
				response = _returnEvent.GasResponses[ 0 ];

			if ( _instrumentController.TestForInstrumentReset( response, "calibrating instrument, start" ) == true )
			{
				Log.Warning( "CALIBRATION: ABORTED DUE TO INSTRUMENT RESET" );
				//_returnEvent.GasResponses.Add( response ); // SGF  14-Jun-2011  INS-1732 -- responses already added to return event
				return;
			}

			// Prior to zeroing, preconditioning, and calibrating, we need to tell the
			// instrument to turn on its sensors.
			Stopwatch stopwatch = Log.TimingBegin( "CAL - TURN ON SENSORS" );
			_instrumentController.TurnOnSensors( true,true );
            Log.TimingEnd( "CAL - TURN ON SENSORS", stopwatch );

            // BEIGN INS-7657 RHP v7.5.2
            DateTime biasStateLoopStartTime = DateTime.UtcNow;
            TimeSpan biasStateElapsedTime = TimeSpan.Zero;

            while (!_instrumentController.GetSensorBiasStatus())
            {
                // Calculate the time that has elapsed since the start of the calibration loop
                biasStateElapsedTime = DateTime.UtcNow - biasStateLoopStartTime;
                Log.Debug(string.Format("{0} Time elapsed in Bias State pass = {1}", "CALIBRATION", (int)biasStateElapsedTime.TotalSeconds));

                Master.Instance.ConsoleService.UpdateState(ConsoleState.CalibratingInstrument, new string[] { string.Format(ConsoleServiceResources.ELAPSEDBIASSTATE, Math.Round(biasStateElapsedTime.TotalSeconds).ToString()) });

                if (biasStateElapsedTime.TotalSeconds > _biasStateTimeout) // Have we timed out?
                {
                    Log.Debug("CALIBRATION: ABORTED DUE TO TIMING OUT BIAS STATE CHECK");
                    throw new InstrumentNotReadyException();
                }
#if !TEST
                    // Allow a ten second interval so that we give some interval before we check the sensor Bias state again.
                    Thread.Sleep(10000);
#endif
            }
            //END DSW-7657

			if ( _instrumentController.TestForInstrumentReset( response, "calibrating instrument, after turning on sensors" ) == true )
			{
				Log.Warning( "CALIBRATION: ABORTED DUE TO INSTRUMENT RESET" );
				//_returnEvent.GasResponses.Add( response ); // SGF  14-Jun-2011  INS-1732 -- responses already added to return event
				return;
			}

			#region Check for Sensors in Error Mode

            stopwatch = Log.TimingBegin( "CAL - CHECK SENSORS ERROR MODE" );

			// SGF  Sep-15-2008  DSZ-1501 ("DS sometimes attempts to zero/calibrate MX6 sensors in 'ERR'")
			// Prior to calibrating the sensors in the instrument, we must ensure that sensors have powered 
			// up, and then we must ensure that none of the sensors go into error. Sensors were already turned on above.

			Master.Instance.SwitchService.Instrument.SensorsInErrorMode.Clear(); //Suresh 05-JAN-2012 INS-2564
			SensorPosition[] sensorPositions = _instrumentController.GetSensorPositions();

			foreach ( SensorPosition sensorPosition in sensorPositions )
			{
				if ( sensorPosition.Mode == SensorMode.Error )
				{
					string sensorPosString = sensorPosition.Position.ToString();
					Log.Warning( "CALIBRATION: SENSOR IN ERROR MODE AT POSITION " + sensorPosString );
					InstalledComponent ic = new InstalledComponent();
					ic.Component = new Sensor();
					ic.Position = sensorPosition.Position;
					Master.Instance.SwitchService.Instrument.SensorsInErrorMode.Add( ic );
				}
			}

            Log.TimingEnd( "CAL - CHECK SENSORS ERROR MODE", stopwatch );

			if ( Master.Instance.SwitchService.Instrument.SensorsInErrorMode.Count > 0 )
				throw new SensorErrorModeException( "The calibration failed due to sensor being in error.  Instrument: " + _returnEvent.DockedInstrument.SerialNumber + " ." );

			#endregion
             
			// Clear the gases in the lines.
            stopwatch = Log.TimingBegin( "CAL - PURGE(INITIAL)" );
			new InstrumentPurgeOperation( PurgeType.PreCalibration, _instrumentController, GasEndPoints, _returnEvent ).Execute();
            Log.TimingEnd( "CAL - PURGE(INITIAL)", stopwatch );

			if ( _instrumentController.TestForInstrumentReset( response, "calibrating instrument, after clearing gases" ) == true )
			{
				Log.Warning( "CALIBRATION: ABORTED DUE TO INSTRUMENT RESET" );
				//_returnEvent.GasResponses.Add(response); // SGF  14-Jun-2011  INS-1732 -- responses already added to return event
				return;
			}

			// Zero the sensor before proceeding.
            stopwatch = Log.TimingBegin( "CAL - ZEROING" );
			ZeroSensors( _returnEvent.DockedInstrument.InstalledComponents );
            Log.TimingEnd( "CAL - ZEROING", stopwatch );

			if ( _instrumentController.TestForInstrumentReset( response, "calibrating instrument, after zeroing" ) == true )
			{
				Log.Warning( "CALIBRATION: ABORTED DUE TO INSTRUMENT RESET" );
				//_returnEvent.GasResponses.Add(response); // SGF  14-Jun-2011  INS-1732 -- responses already added to return event
				return;
			}

            //Clear if any flags before initiating calibration once again since this would be set to true during Pre-Calibration Purge
            Pump.IsBadPumpTubing = false;

            if ( _returnEvent.DockedInstrument.Type == DeviceType.TX1
            ||   _returnEvent.DockedInstrument.Type == DeviceType.VPRO
            ||   _returnEvent.DockedInstrument.Type == DeviceType.SC  )
			{
				CalibrateInstrumentParallel(); // Also known as "quick cal".
			}
			else
			{
				CalibrateInstrumentSequential();
			}            

			// Wipe out most data that will be uploaded to iNet when sensor calibration was aborted by the instrument.
			for ( int i = 0; i < _returnEvent.GasResponses.Count; i++ )
			{
				if ( _returnEvent.GasResponses[ i ].Status == Status.InstrumentAborted )
				{
					_returnEvent.GasResponses[ i ] = SensorGasResponse.CreateInstrumentAbortedSensorGasResponse( _returnEvent.GasResponses[ i ] );
				}
			}
		}
		catch ( CorrectCalibrationGasUnavailable ccgu )
		{
			Log.Warning( Name + ": Calibration gas unavailable", ccgu );
			throw;
		}
		catch ( FlowFailedException ffe )
		{
			Log.Warning( Name + ": Flow failed", ffe );
			throw;
		}
		catch ( SensorErrorModeException )
		{
			Log.Warning( Name + ": SensorErrorModeException thrown." );
			throw;
		}
		catch ( ISC.Instrument.Driver.CommunicationAbortedException cae )
		{
			throw new InstrumentNotDockedException( cae );
		}
		catch ( InstrumentNotDockedException )
		{
			throw;
		}
		catch ( ISC.Instrument.Driver.SystemAlarmException sae ) // some instruments may throw this during sensor warmup.
		{
			throw new InstrumentSystemAlarmException( Master.Instance.SwitchService.Instrument.SerialNumber, sae.ErrorCode );
		}
        // INS-7657 RHP v7.5.2 Display Instrument Not Ready Message to be specific that the error is due to Sesnor not biased within 2 hours
        catch (InstrumentNotReadyException inr)
        {
            throw new InstrumentNotReadyException(inr);
        }
		catch ( Exception e )
		{
			throw new FailedCalibrationException( e );
		}
        finally
        {            
            // Make sure we turn off instrument pump before leave.  It may be still on
            // if the reason we're in this finally block is due to a thrown exception.
            _instrumentController.EnablePump( false ); // This call will first check if instrument is docked.

            if(Master.Instance.SwitchService.BadPumpTubingDetectedDuringCal)
                Master.Instance.ConsoleService.UpdateState(ConsoleState.CalibrationStoppedCheckTubing);
            else
                // Clear the reference to a step in the calibration process
                Master.Instance.ConsoleService.UpdateState( ConsoleState.CalibratingInstrument );
        }
    }