// 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); }
} // 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()
/// <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 ); } }