예제 #1
0
        public void InstrumentRequiresOperatorAction()
        {
            // arrange
            SettingsReadOperation operation = new SettingsReadOperation();

            dsEvent = new SettingsReadEvent(operation);

            dockingStation = Helper.GetDockingStationForTest(DeviceType.MX4);
            instrument     = Helper.GetInstrumentForTest(DeviceType.VPRO, DeviceSubType.VentisPro4);

            Initialize();

            Sensor sensor = new Sensor();

            sensor.Enabled           = true;
            sensor.CalibrationStatus = Status.Failed;
            sensor.Type.Code         = SensorCode.O3;

            InstalledComponent installedComponent = new InstalledComponent();

            installedComponent.Position  = 1;
            installedComponent.Component = sensor;
            instrument.InstalledComponents.Add(installedComponent);

            Configuration.DockingStation = dockingStation;

            CreateMasterForTest();

            // act
            nextAction = scheduler.GetNextAction(dsEvent);

            // assert
            Xunit.Assert.True(nextAction is ManualCalibrationRequiredAction);
        }
예제 #2
0
        public void GetCalibrationActionForSensorAdded()
        {
            // arrange
            InstrumentCalibrationAction action = new InstrumentCalibrationAction();

            instrument = Helper.GetInstrumentForTest(DeviceType.VPRO, DeviceSubType.VentisPro4);
            List <InstalledComponent> installedComponents = Helper.GetSensorsForTest(new List <string>()
            {
                GasCode.CO, GasCode.H2S, GasCode.O2, GasCode.CombustibleLEL
            });

            dockingStation = Helper.GetDockingStationForTest(DeviceType.MX4);

            List <EventJournal> eventJournals = new List <EventJournal>();

            eventJournals.Add(new EventJournal(EventCode.GetCachedCode(EventCode.InstrumentDiagnostics), instrument.SerialNumber, DateTime.Now.AddMonths(-1), DateTime.Now.AddMonths(-1), true, instrument.SoftwareVersion));
            for (int i = 0; i < 2; i++)
            {
                InstalledComponent comp = installedComponents[i];
                eventJournals.Add(new EventJournal(EventCode.Calibration, comp.Component.Uid, instrument.SerialNumber, DateTime.Now.AddMonths(-1), DateTime.Now.AddMonths(-1), true, comp.Position, instrument.SoftwareVersion));
            }
            instrument.InstalledComponents.AddRange(installedComponents);
            _eventJournalDataAccess.Setup(x => x.FindBySerialNumbers(It.IsAny <string[]>(), It.IsAny <IDataAccessTransaction>())).Returns(eventJournals);
            _eventJournalDataAccess.Setup(x => x.FindLastEventByInstrumentSerialNumber(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <IDataAccessTransaction>())).Returns(eventJournals);

            Initialize();

            // act
            CreateMasterForTest();
            schema.Setup(x => x.Activated).Returns(true);
            InstrumentCalibrationOperation operation = new InstrumentCalibrationOperation(action);

            dsEvent    = new InstrumentCalibrationEvent(operation);
            nextAction = scheduler.GetNextAction(dsEvent);

            // assert
            Xunit.Assert.True(nextAction is InstrumentCalibrationAction);
        }
예제 #3
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()
예제 #4
0
        // INS-8630 RHP v7.5 - Updated method signature to include errorCodes and explanationCodes to display ISC Cylinder Part Number on LCD and to send the same as Alert
        private void FindInstalledCylinderGases(string eventCode, List <GasEndPoint> gasEndPoints,
                                                DockingStation dockingStation, InstalledComponent installedComponent,
                                                StringBuilder explanation,
                                                List <string> explanationCodes,
                                                List <string> errorCodes)
        {
            Sensor sensor = installedComponent.Component as Sensor;

            // INS-8630 RHP v7.5 - Initialize outside the loop since if no gas is found on entire loop, then we still need to pass expired/empty if either are true.
            _expired = _empty = false;

            // SGF  21-May-2012  INS-3078
            _gasNeeded = IsGasOperationSupported(eventCode, sensor);

            foreach (GasEndPoint gasEndPoint in dockingStation.GasEndPoints)
            {
                Cylinder cyl = gasEndPoint.Cylinder;

                // If we are examining the fresh air/zero air port to possibly add a gas to the gas end points list,
                // make sure that the type of fresh air/zero air is allowed based on port 1 descriptions.  For example,
                // if the installed cylinder is fresh air, make sure the port 1 restrictions allow for fresh
                // air to be specified on this port.
                //
                if (gasEndPoint.Position == Controller.FRESH_AIR_GAS_PORT)
                {
                    if (cyl.IsFreshAir && ((dockingStation.Port1Restrictions & PortRestrictions.FreshAir) != PortRestrictions.FreshAir))
                    {
                        Log.Debug(Name + ": FRESH AIR attached to Port 1; defined Port 1 Restrictions prohibit the use of FRESH AIR");
                        continue;
                    }

                    if (cyl.IsZeroAir && ((dockingStation.Port1Restrictions & PortRestrictions.ZeroAir) != PortRestrictions.ZeroAir))
                    {
                        Log.Debug(Name + ": ZERO AIR attached to Port 1; defined Port 1 Restrictions prohibit the use of ZERO AIR");
                        continue;
                    }
                }

                // Reset each time through loop. At end of loop, they'll be
                // set for the last cylinder matching desired gas that was looked at.
                //_expired = _empty = false; - INS-8630 RHP v7.5 Commented since if no gas is found on entire loop, then we still need to pass expired/empty if either are true.
                bool alreadyAdded = false;

                string msg = string.Format("{0} Examining port {1} ({2}), fid={3},pn={4},Fresh={5},ZeroAir={6},Exp={7},Pressure={8}",
                                           Name, gasEndPoint.Position, gasEndPoint.InstallationType.ToString(), cyl.FactoryId, cyl.PartNumber, cyl.IsFreshAir, cyl.IsZeroAir,
                                           cyl.ExpirationDate.ToShortDateString(), cyl.Pressure.ToString());
                Log.Debug(msg);

                msg = "......";
                foreach (GasConcentration gasCon in gasEndPoint.Cylinder.GasConcentrations)
                {
                    msg += "[" + gasCon.Type.Code + " ";
                    msg += (gasCon.Concentration == double.MinValue) ? "fresh" : gasCon.Concentration.ToString();
                    msg += "]";
                }
                Log.Debug(msg);

                string sensorGasCode = sensor.CalibrationGas.Code;

                // If we're doing a bump test, we use Chlorine for the calibration gas.
                if (sensor.Type.Code == SensorCode.ClO2 && eventCode == EventCode.BumpTest)
                {
                    sensorGasCode = GasCode.Cl2;
                    Log.Debug("...Sensor is CLO2. Looking for cylinders containing Chlorine instead of cal gas.");
                }

                bool containsGas = gasEndPoint.Cylinder.ContainsGas(sensorGasCode);

                if (Configuration.DockingStation.UseExpiredCylinders && (eventCode == EventCode.BumpTest))
                {
                    Log.Debug("...UseExpiredCylinders=true; Ignoring expiration date");
                }
                else if (Configuration.ToLocalTime(gasEndPoint.Cylinder.ExpirationDate) <= Configuration.GetLocalTime())
                {
                    //Log.Debug( "...Skipping expired cylinder" );
                    explanation.Append("Cylinder has expired." + '\n');
                    explanation.Append("Cylinder id =" + gasEndPoint.Cylinder.FactoryId + '\n');
                    explanation.Append("Port number: " + gasEndPoint.Position + '\n');

                    // INS-8630 RHP v7.5 - IDS is expected to display expired cylinder details on LCD
                    if (containsGas)
                    {
                        // INS-8630 RHP v7.5 - Multiple cylinder with the same gas code may be expired, in such scenarios IDS message
                        // should display the first cylinder it identifies.
                        if (!(_expired || _empty))
                        {
                            // For ISC cylinder IDS should display the cylinder part NUmber, so add the part number to explanation codes
                            if (!explanationCodes.Contains(gasEndPoint.Cylinder.PartNumber) && !string.IsNullOrEmpty(gasEndPoint.Cylinder.PartNumber))
                            {
                                explanationCodes.Add(gasEndPoint.Cylinder.PartNumber);
                                errorCodes.Add(string.Format("{0} ({1})", gasEndPoint.Cylinder.PartNumber, gasEndPoint.Position)); // see INS-3821
                            }
                            else
                            {
                                // For Non ISC Pass the gas code. But I believe that both ISC and Non-ISC cylinders have their own PArt numbers.
                                // So this condition may not be required ?
                                GasType gs = GasType.Cache[GasCode.FreshAir];
                                if (!explanationCodes.Contains(gasEndPoint.Cylinder.PartNumber) && gs != null)
                                {
                                    explanationCodes.Add(gs.Symbol);
                                    errorCodes.Add(string.Format("{0} ({1})", gs.Symbol, gs.Code));
                                }
                            }
                            _expired = true;
                        }
                        continue;
                    }
                }

                if (gasEndPoint.Cylinder.Pressure == PressureLevel.Empty)
                {
                    //Log.Debug( "...Skipping cylinder; Pressure is " + gasEndPoint.Cylinder.Pressure );
                    explanation.Append("Cylinder is empty." + '\n');
                    explanation.Append("Cylinder id =" + gasEndPoint.Cylinder.FactoryId + '\n');
                    explanation.Append("Port number: " + gasEndPoint.Position + '\n');

                    // INS-8630 RHP v7.5 - IDS is expected to display empty cylinder details on LCD
                    if (containsGas)
                    {
                        // INS-8630 RHP v7.5 - Multiple cylinder with the same gas code may be expired, in such scenarios IDS message
                        // should display the first cylinder it identifies.
                        if (!(_expired || _empty))
                        {
                            // For ISC cylinder IDS should display the cylinder part NUmber, so add the part number to explanation codes
                            if (!explanationCodes.Contains(gasEndPoint.Cylinder.PartNumber) && !string.IsNullOrEmpty(gasEndPoint.Cylinder.PartNumber))
                            {
                                explanationCodes.Add(gasEndPoint.Cylinder.PartNumber);
                                errorCodes.Add(string.Format("{0} ({1})", gasEndPoint.Cylinder.PartNumber, gasEndPoint.Position));  // see INS-3821
                            }
                            else
                            {
                                // For Non ISC Pass the gas code. But I believe that both ISC and Non-ISC cylinders have their own PArt numbers.
                                // So this condition may not be required ?
                                GasType gs = GasType.Cache[GasCode.FreshAir];
                                if (!explanationCodes.Contains(gasEndPoint.Cylinder.PartNumber) && gs != null)
                                {
                                    explanationCodes.Add(gs.Symbol);
                                    errorCodes.Add(string.Format("{0} ({1})", gs.Symbol, gs.Code));
                                }
                            }
                            _empty = true;
                        }
                        continue;
                    }
                }

                //string sensorGasCode = sensor.CalibrationGas.Code;

                if (eventCode == EventCode.BumpTest)
                {
                    // If we're doing a bump test, and this is a combustible sensor either in LEL or PPM mode,
                    // and docking station has a CombustibleBumpTestGas setting, then ignore the sensor's cal
                    // gas code and instead only look for cylinders that match the CombustibleBumpTestGas setting.
                    if ((sensor.Type.Code == SensorCode.CombustibleLEL || sensor.Type.Code == SensorCode.CombustiblePPM) &&
                        (Configuration.DockingStation.CombustibleBumpTestGas.Length > 0))
                    {
                        sensorGasCode = Configuration.DockingStation.CombustibleBumpTestGas;
                        Log.Debug(string.Format("...Sensor is combustible and CombustibleBumpTestGas setting is {0}.", sensorGasCode));
                        Log.Debug(string.Format("...Overriding sensor cal gas. Looking for cylinders containing {0}.", sensorGasCode));
                    }
                    // If we're doing a bump test, we use Chlorine for the calibration gas.
                    else if (sensor.Type.Code == SensorCode.ClO2)
                    {
                        sensorGasCode = GasCode.Cl2;
                        Log.Debug("...Sensor is CLO2. Looking for cylinders containing Chlorine instead of cal gas.");
                    }
                }

                containsGas = gasEndPoint.Cylinder.ContainsGas(sensorGasCode);

                // Ensure bump tests for O2 sensors use concentrations of 19% O2 or less.
                // Zero air cylinders should not be selected for gasFound, but should be selected for zeroFound.
                if (containsGas && (eventCode == EventCode.BumpTest && sensor.CalibrationGas.Code == GasCode.O2))
                {
                    // The following Find should always succeed because containsGas is true.
                    GasConcentration gasConcentration = cyl.GasConcentrations.Find(gc => gc.Type.Code == sensorGasCode);
                    // Parts per million (ppm) of X divided by 10,000 = percentage concentration of X
                    if ((gasConcentration.Concentration / 10000.0) > 19.0)
                    {
                        Log.Debug("...Not allowed to use O2 concentration higher than 19%.");

                        containsGas = false;
                    }
                }

                // Ensure cylinder concentration is not higher than 60% for potentially explosive cal gases
                double lelMultiplier = GasType.Cache[sensorGasCode].LELMultiplier;
                if (containsGas && lelMultiplier > 0.0)
                {
                    double cylinderPPM = -1.0;
                    foreach (GasConcentration gasConcentration in cyl.GasConcentrations)
                    {
                        if (gasConcentration.Type.Code == sensorGasCode)
                        {
                            cylinderPPM = gasConcentration.Concentration;
                            break;
                        }
                    }

                    if (cylinderPPM < 0)   // this should never happen.  Which means we better check anyways.
                    {
                        Log.Debug("...Skipping cylinder. Does not contain " + sensorGasCode);
                        continue;
                    }

                    double cylinderLEL = cylinderPPM * lelMultiplier;
                    if (cylinderLEL > 60.0)                        // cylinder is higher than 60%?  Don't use it.
                    {
                        Log.Debug(string.Format("...Skipping cylinder. Contains {0} with too high LEL concentration ({1}%)",
                                                sensorGasCode, Math.Round(cylinderLEL, 1)));
                        Log.Debug("...Not allowed to use LEL concentration higher than 60%.");
                        continue;
                    }
                }

                // The gas is found if the cylinder contains a gas with a matching code, or
                // if the cylinder is fresh air and the sensor is an O2 sensor.
                // Fresh air is not acceptable as O2 if doing a bump test.
                // Zero air is not acceptable for an O2 bump test.
                if (containsGas || ((sensor.CalibrationGas.Code == GasCode.O2) && !(eventCode == EventCode.BumpTest) && gasEndPoint.Cylinder.IsFreshAir))
                {
                    Log.Debug("...Gas found.");
                    if (HasCylinder(gasEndPoint, gasEndPoints) == false)
                    {
                        gasEndPoints.Add(gasEndPoint);
                    }
                    _gasFound    = true;
                    alreadyAdded = true;
                }

                // Nitrogen is acceptable for doing O2 bump tests.
                if (sensor.CalibrationGas.Code == GasCode.O2 &&
                    (eventCode == EventCode.BumpTest) &&
                    gasEndPoint.Cylinder.ContainsOnlyGas(GasCode.N2) == true)
                {
                    Log.Debug("...Gas found.");
                    if (HasCylinder(gasEndPoint, gasEndPoints) == false)
                    {
                        gasEndPoints.Add(gasEndPoint);
                    }
                    _gasFound = true;
                }

                if (gasEndPoint.Cylinder.IsFreshAir)
                {
                    Log.Debug("...Fresh air found.");
                    if (!alreadyAdded)
                    {
                        if (HasCylinder(gasEndPoint, gasEndPoints) == false)
                        {
                            gasEndPoints.Add(gasEndPoint);
                        }
                    }
                    _freshFound  = true;
                    alreadyAdded = true;
                }

                if (gasEndPoint.Cylinder.IsZeroAir)
                {
                    Log.Debug("...Zero Air found.");
                    if (!alreadyAdded)
                    {
                        if (HasCylinder(gasEndPoint, gasEndPoints) == false)
                        {
                            gasEndPoints.Add(gasEndPoint);
                        }
                    }
                    _zeroFound   = true;
                    alreadyAdded = true;
                }
            }   // end-foreach  Gasendpoints

            // BEGIN INS-8630 RHP v7.5 - Loop through the entire list of installed cylinders.
            // if _gasFound is set to true, then the desired gas is found and hence we can reset _expired and _empty.
            // There may be cases where a docking station may have more than one gas code of the same kind assigned to it as a part of installed cylinders and
            // put of which one of the cylinders may be expired/empty and other may be full.
            if (_empty && _gasFound)
            {
                _empty = false;
            }
            else if (_expired && _gasFound)
            {
                _expired = false;
            }
            // END INS-8630 RHP v7.5
        }
        /// <summary>
        /// Find the zero air end point for this sensor.  If zero air is not
        /// found, then fresh air may be returned instead (See notes on zeroAirOnly
        /// parameter).
        /// </summary>
        /// <param name="installedComponent">The sensor to find the zero air for.</param>
        /// <param name="zeroAirOnly">If true, then this routine will only find
        /// and return zero air cylinders.  If false, then this routine will
        /// attempt to find a zero air cylinder, but will find and return a
        /// fresh air as an alternative if zero air is not found.</param>
        /// <param name="checkIfUsed">If true, check if the cylinder under consideration
        /// has already been used.</param>
        /// <returns>The correct zero air gas end point.</returns>
        /// <exception cref="CorrectBumpTestGasUnavailable">
        /// If there are no zero air gas end points for this sensor.
        /// </exception>
        protected internal GasEndPoint GetSensorZeroAir(InstalledComponent installedComponent, bool zeroAirOnly, bool checkIfUsed)
        {
            Sensor sensor = null;

            if (installedComponent != null)
            {
                sensor = installedComponent.Component as Sensor;
            }

            string msg = "Finding Zero air cylinder";

            if (sensor != null)
            {
                msg += " for sensor " + sensor.Uid;
            }
            msg += "...";
            Log.Debug(msg);

            // Find zero air end points.

            int pointCount = 0;

            foreach (GasEndPoint gasEndPoint in GasEndPoints)
            {
                #region LogDebug
                msg = "GasEndPoint #" + ++pointCount;
                Log.Debug(msg);

                Cylinder cyl = gasEndPoint.Cylinder;

                msg = "...Pos=" + gasEndPoint.Position
                      //+ ", ID: " + cyl.ID
                      + ", FactID=" + cyl.FactoryId
                      + ", Part=" + cyl.PartNumber
                      + ", Fresh=" + cyl.IsFreshAir
                      + ", ZeroAir=" + cyl.IsZeroAir
                      + ", Pressure=" + cyl.Pressure.ToString();
                if (cyl.Volume != DomainModelConstant.NullInt)
                {
                    msg += ", Vol=" + cyl.Volume;
                }

                Log.Debug(msg);
                #endregion

                if (!cyl.IsZeroAir)
                {
                    Log.Debug("...Rejected.  Not zero air.");
                    continue;
                }

                if (cyl.Pressure == PressureLevel.Empty)
                {
                    Log.Debug("...Rejected zero air. Cylinder empty.");
                    continue;
                }

                if (checkIfUsed == true && _triedGasEndPoints.ContainsKey(gasEndPoint))
                {
                    Log.Debug("...Rejected. Already tried cylinder.");
                    continue;
                }

                if (sensor == null)
                {
                    Log.Debug("...SELECTED GasEndPoint.  Zero air found.");
                    return(gasEndPoint);
                }

                Log.Debug("...SELECTED GasEndPoint.  Zero air found for sensor " + sensor.Uid);
                return(gasEndPoint);
            }

            if (zeroAirOnly)
            {
                Log.Debug("No zero air found.");
                throw new CorrectBumpTestGasUnavailable("Zero Air");
            }

            // No calibration gases were found, attempt to use the fresh air.
            Log.Debug("No zero air found.  Looking for alternative fresh air...");

            return(GetSensorFreshAir(installedComponent, checkIfUsed));
        }
예제 #6
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
예제 #7
0
        /// <summary>
        /// Create needed sensors based on list of gas codes tester requested.
        /// </summary>
        internal static List <InstalledComponent> GetSensorsForTest(List <string> gasCodes)
        {
            List <InstalledComponent> sensors = new List <InstalledComponent>();

            int position = 0;

            foreach (string gasCode in gasCodes)
            {
                position++;

                InstalledComponent component = new InstalledComponent
                {
                    Component = new Sensor(String.Format("TESTSEN-{0}", position.ToString("D3"))),
                    Position  = position
                };

                //Set typical properties and let caller change as needed
                Sensor sensor = component.Component as Sensor;
                sensor.Type               = new SensorType(gasCode.Replace("G", "S"));
                sensor.Enabled            = true;
                sensor.BumpTestStatus     = true;
                sensor.CalibrationStatus  = Status.Passed;
                sensor.CalibrationTimeout = 10;

                // POPULATED CAL ORDER AND BUMP ODER FROM FS
                switch (sensor.Type.Code)
                {
                case SensorCode.CO:
                    sensor.CalibrationGas = new GasType(GasCode.CO, 14, 13, 0.0, "CO", true, "Carbon Monoxide");
                    sensor.CalibrationGasConcentration = 100.0;
                    sensor.Alarm.Low  = 35;
                    sensor.Alarm.High = 70;
                    sensor.Alarm.TWA  = 35;
                    sensor.Alarm.STEL = 200;
                    ((SensorType)component.Component.Type).MeasurementType = MeasurementType.PPM;
                    break;

                case SensorCode.H2S:
                    sensor.CalibrationGas = new GasType(GasCode.H2S, 13, 12, 0.0, "H2S", true, "HYDROGEN SULPHIDE");
                    sensor.CalibrationGasConcentration = 25.0;
                    sensor.Alarm.Low  = 10;
                    sensor.Alarm.High = 20;
                    sensor.Alarm.TWA  = 10;
                    sensor.Alarm.STEL = 15;
                    ((SensorType)component.Component.Type).MeasurementType = MeasurementType.PPM;
                    break;

                case SensorCode.O2:
                    sensor.CalibrationGas = new GasType(GasCode.O2, 1, 23, 0.0, "O2", true, "OXYGEN");
                    sensor.CalibrationGasConcentration = 18;
                    sensor.Alarm.Low  = 19.5;
                    sensor.Alarm.High = 23.5;
                    ((SensorType)component.Component.Type).MeasurementType = MeasurementType.VOL;
                    break;

                case SensorCode.CombustibleLEL:
                    sensor.CalibrationGas = new GasType(GasCode.Pentane, 18, 17, 0.0071, "LEL", true, "COMBUSTIBLE LEL");
                    sensor.CalibrationGasConcentration = 25;
                    sensor.Alarm.Low  = 10;
                    sensor.Alarm.High = 20;
                    ((SensorType)component.Component.Type).MeasurementType = MeasurementType.LEL;
                    break;

                case SensorCode.ClO2:
                    sensor.CalibrationGas = new GasType(GasCode.ClO2, 2, 1, 0.0, "CLO2", true, "CHLORINE DIOXIDE");
                    sensor.CalibrationGasConcentration = 1;
                    sensor.Alarm.Low  = 0.1;
                    sensor.Alarm.High = 0.2;
                    sensor.Alarm.TWA  = 0.1;
                    sensor.Alarm.STEL = 0.3;
                    ((SensorType)component.Component.Type).MeasurementType = MeasurementType.PPM;
                    break;

                case SensorCode.CO2:
                    sensor.CalibrationGas = new GasType(GasCode.CO2, 60, 1, 0.0, "CO2", true, "CARBON DIOXIDE");
                    sensor.CalibrationGasConcentration = 1;
                    sensor.Alarm.Low  = 0.5;
                    sensor.Alarm.High = 1;
                    sensor.Alarm.TWA  = 0.5;
                    sensor.Alarm.STEL = 3;
                    ((SensorType)component.Component.Type).MeasurementType = MeasurementType.PPM;
                    break;

                case SensorCode.HCl:
                    sensor.CalibrationGas = new GasType(GasCode.HCl, 3, 1, 0.0, "HCL", true, "HYDROGEN CHLORIDE");
                    sensor.CalibrationGasConcentration = 10;
                    sensor.Alarm.Low  = 2.5;
                    sensor.Alarm.High = 5;
                    sensor.Alarm.TWA  = 2.5;
                    sensor.Alarm.STEL = 2.5;
                    ((SensorType)component.Component.Type).MeasurementType = MeasurementType.PPM;
                    break;

                case SensorCode.NH3:
                    sensor.CalibrationGas = new GasType(GasCode.NH3, 8, 1, 0.0, "NH3", true, "AMMONIA");
                    sensor.CalibrationGasConcentration = 50;
                    sensor.Alarm.Low  = 25;
                    sensor.Alarm.High = 50;
                    sensor.Alarm.TWA  = 25;
                    sensor.Alarm.STEL = 35;
                    ((SensorType)component.Component.Type).MeasurementType = MeasurementType.PPM;
                    break;

                default:
                    sensor.CalibrationGas = GasType.Cache[gasCode.ToString()];
                    break;
                }

                sensors.Add(component);
            }

            return(sensors);
        }
        /// <summary>
        /// Get the calibration gas concentration.
        /// </summary>
        /// <param name="sensor">The sensor to get the concentration for.</param>
        /// <param name="endPoint">The gas end point that contains the gas.</param>
        protected double GetCalibrationGasConcentration(InstalledComponent installedComponent, GasEndPoint endPoint)
        {
            const string func = "GetCalibrationGasConcentration: ";

            Sensor sensor = (Sensor)installedComponent.Component;

            double availableConcentration = DomainModelConstant.NullDouble;

            string          gasCode               = sensor.CalibrationGas.Code;
            double          lelMultiplier         = GasType.Cache[gasCode].LELMultiplier;
            MeasurementType sensorMeasurementType = ((SensorType)sensor.Type).MeasurementType;

            Cylinder cylinder = endPoint.Cylinder; // Get the cylinder.

            // For nitrogen cylinder's, being used for O2 bumps, we assume 0% O2.
            if ((gasCode == GasCode.O2) && cylinder.ContainsOnlyGas(GasCode.N2))
            {
                availableConcentration = 0.0d;
            }
            else
            {
                // Determine the gas concentration of the gas to use.
                foreach (GasConcentration gasCon in cylinder.GasConcentrations)
                {
                    if (gasCon.Type.Code == gasCode)
                    {
                        availableConcentration = gasCon.Concentration;
                        break;
                    }
                    else if ((gasCode == GasCode.O2) && (gasCon.Type.Code == GasCode.FreshAir))
                    {
                        availableConcentration = 209000d;
                        break;
                    }
                }
            }

            // If we didn't find anything with the gas.
            if (availableConcentration == DomainModelConstant.NullDouble)
            {
                throw new CorrectBumpTestGasUnavailable(gasCode);
            }

            Log.Debug("Sensor cal gas concentration: "
                      + sensor.CalibrationGasConcentration + " res: " + sensor.Resolution);
            // Check the measurement type for how to multiply the concentration.
            if (sensorMeasurementType == MeasurementType.LEL)
            {
                availableConcentration *= lelMultiplier;
                availableConcentration  = Master.Instance.ControllerWrapper.Round(availableConcentration, 0);
            }
            else if (sensorMeasurementType != MeasurementType.PPM)
            {
                availableConcentration /= 10000;
            }

            if (availableConcentration == sensor.CalibrationGasConcentration)
            {
                return(sensor.CalibrationGasConcentration); // Its the correct concentration.
            }
            availableConcentration = Master.Instance.ControllerWrapper.Round(availableConcentration, 2);

            Log.Debug("gas: " + gasCode + " new conc: " + availableConcentration);

            // INS- RHP v7.6 - For MX6v4.4 and above, Set the sensor's calibration gas concentration to
            // match the concentration of gas end point that contains the gas.
            //if (_returnEvent.DockedInstrument.Type == DeviceType.MX6 && new Version(_returnEvent.DockedInstrument.SoftwareVersion) >= _MX6_v44
            //    && availableConcentration > 0.0d && sensor.BumpCriterionType != CriterionType.PPMLimit)
            //{
            //    // If sensor is %vol, and it has a zero resolution, then we want to round the concentration
            //    // up to the next integer value.  e.g., if cylinder contains 2.1% gas, then we want to round
            //    // it to 3.
            //    if (sensorMeasurementType == MeasurementType.VOL && sensor.Resolution == 1.0)
            //    {
            //        Log.Debug(string.Format("{0}Sensor is %VOL and has resolution of zero decimals. Rounding {1} up to next integer",
            //            func, availableConcentration));
            //        availableConcentration = Math.Ceiling(availableConcentration);
            //    }

            //    Log.Debug(string.Format("{0}SETTING SENSOR FROM CONCENTRATION {1} TO {2}, (res={3})", func, sensor.CalibrationGasConcentration, availableConcentration, sensor.Resolution));

            //    // Set the sensor's calibration gas concentration.
            //    _instrumentController.SetSensorCalGasConcentration(installedComponent.Position, availableConcentration, sensor.Resolution);

            //    Log.Debug(string.Format("{0}NEW CONCENTRATION: {1}", func, _instrumentController.GetSensorCalGasConcentration(installedComponent.Position, sensor.Resolution)));
            //}

            return(availableConcentration);
        }
예제 #9
0
        /// <summary>
        /// Set the docked instrument's values.
        /// </summary>
        /// <param name="settings">The values to place on the docked instrument.</param>
        private Instrument UpdateInstrument(Instrument settings, string expectedSerialNumber)
        {
            Log.Debug(string.Format("{0}.UpdateInstrument", Name));

            if (!Controller.IsDocked())   // Determine if there is an instrument to read.
            {
                return(null);
            }

            string tmpString = _instCtrlr.GetSerialNumber();

            if (tmpString == string.Empty)
            {
                Log.Warning("GetSerialNumber returned empty string!");
            }

            Log.Debug(string.Format("Updating with {0} settings (RefId={1})",
                                    (settings.SerialNumber == string.Empty) ? "default instrument" : "instrument-specific", settings.RefId));

            // Check to see if we are modifying the correct instrument.
            if (expectedSerialNumber != tmpString)
            {
                string msg = string.Format("Instrument S/N mismatch. expected=\"{0}\" actual=\"{1}\"", expectedSerialNumber, tmpString);
                Log.Error(msg);
                throw new ApplicationException(msg);
            }

            // Check to see if we are modifying the correct instrument type.
            DeviceType tmpType = Master.Instance.SwitchService.Instrument.Type;

            if (settings.Type != tmpType)
            {
                string msg = string.Format("Instrument Type mismatch. expected=\"{0}\" actual=\"{1}\"", settings.Type.ToString(), tmpType.ToString());
                Log.Error(msg);
                throw new ApplicationException(msg);
            }

            // We already verified that the type of the settings match the type of the docked instrument.
            // Now we verify that the type is supported by this type of docking station.
            if (settings.Type != Configuration.DockingStation.Type)
            {
                // VPRO instruments are supported by MX4 docking stations.
                if (!(settings.Type == DeviceType.VPRO && Configuration.DockingStation.Type == DeviceType.MX4))
                {
                    string msg = string.Format("Instrument {0} is of the wrong type (\"{1}\")", tmpString, settings.Type.ToString());
                    Log.Error(msg);
                    throw new ApplicationException(msg);
                }
            }

            // Temp variables
            int    tmpInt;
            short  tmpShort;
            double tmpDouble;

            // AccessCode will be an empty string if it should be set to the default.
            if (settings.AccessCode == string.Empty)
            {
                settings.AccessCode = _instCtrlr.Driver.Definition.DefaultSecurityCode;
            }

            // Instrument security or access code setting.
            tmpString = Master.Instance.SwitchService.Instrument.AccessCode;
            if (settings.AccessCode != tmpString)
            {
#if DEBUG // don't show access codes in release builds.
                LogUpdate("AccessCode", settings.AccessCode, tmpString);
#else
                LogUpdate("AccessCode", string.Empty.PadRight(settings.AccessCode.Length, '*'), string.Empty.PadRight(tmpString.Length, '*'));
#endif
                _instCtrlr.SetAccessCode(settings.AccessCode);
                Master.Instance.SwitchService.Instrument.AccessCode = settings.AccessCode;
            }

            // Recording interval setting (in seconds) for datalog.
            // Note that RecordingIntervalIncrement will return zero if instrument
            // doesn't allow its recording interval to be changed.
            if (_instCtrlr.HasDataLoggingFeature && _instCtrlr.RecordingIntervalIncrement > 0)
            {
                tmpInt = Master.Instance.SwitchService.Instrument.RecordingInterval;
                if (settings.RecordingInterval != tmpInt)
                {
                    LogUpdate("RecordingInterval", settings.RecordingInterval, tmpInt);
                    _instCtrlr.SetRecordingInterval(settings.RecordingInterval);
                    Master.Instance.SwitchService.Instrument.RecordingInterval = settings.RecordingInterval;
                }
            }

            // TWA Time Base setting.
            if (_instCtrlr.HasTwaFeature)
            {
                tmpInt = Master.Instance.SwitchService.Instrument.TWATimeBase;
                if (settings.TWATimeBase != tmpInt)
                {
                    LogUpdate("TWATimeBase", settings.TWATimeBase, tmpInt);
                    _instCtrlr.SetTwaTimeBase(settings.TWATimeBase);
                    Master.Instance.SwitchService.Instrument.TWATimeBase = settings.TWATimeBase;
                }
            }

            // Out-of-Motion (Man Down) Warning Interval setting
            if (_instCtrlr.HasOomWarningIntervalFeature)
            {
                tmpInt = Master.Instance.SwitchService.Instrument.OomWarningInterval;
                if (settings.OomWarningInterval != tmpInt)
                {
                    LogUpdate("OomWarningInterval", settings.OomWarningInterval, tmpInt);
                    _instCtrlr.SetOomWarningInterval(settings.OomWarningInterval);
                    Master.Instance.SwitchService.Instrument.OomWarningInterval = settings.OomWarningInterval;
                }
            }

            // Dock Overdue Interval setting
            if (_instCtrlr.HasDockIntervalFeature)
            {
                tmpInt = Master.Instance.SwitchService.Instrument.DockInterval;
                if (settings.DockInterval != tmpInt)
                {
                    LogUpdate("DockInterval", settings.DockInterval, tmpInt);
                    _instCtrlr.SetDockInterval(settings.DockInterval);
                    Master.Instance.SwitchService.Instrument.DockInterval = settings.DockInterval;
                }
            }

            // Maintenance Interval setting
            if (_instCtrlr.HasMaintenanceIntervalFeature)
            {
                tmpInt = Master.Instance.SwitchService.Instrument.MaintenanceInterval;
                if (settings.MaintenanceInterval != tmpInt)
                {
                    LogUpdate("MaintenanceInterval", settings.MaintenanceInterval, tmpInt);
                    _instCtrlr.SetMaintenanceInterval(settings.MaintenanceInterval);
                    Master.Instance.SwitchService.Instrument.MaintenanceInterval = settings.MaintenanceInterval;
                }
            }

            // Calibration Interval setting
            if (_instCtrlr.HasCalibrationIntervalFeature)
            {
                tmpShort = Master.Instance.SwitchService.Instrument.CalibrationInterval;
                if (settings.CalibrationInterval != tmpShort)
                {
                    LogUpdate("CalibrationInterval", settings.CalibrationInterval, tmpShort);
                    _instCtrlr.SetCalibrationInterval(settings.CalibrationInterval);
                    Master.Instance.SwitchService.Instrument.CalibrationInterval = settings.CalibrationInterval;
                }
            }

            // Bump Interval setting
            if (_instCtrlr.HasBumpIntervalFeature)
            {
                tmpDouble = Master.Instance.SwitchService.Instrument.BumpInterval;
                if (settings.BumpInterval != tmpDouble)
                {
                    LogUpdate("BumpInterval", settings.BumpInterval, tmpDouble);
                    _instCtrlr.SetBumpInterval(settings.BumpInterval);
                    Master.Instance.SwitchService.Instrument.BumpInterval = settings.BumpInterval;
                }
            }

            // instrument controller will return -1 if instrument does not support bump thresholding.
            if (_instCtrlr.HasBumpThresholdFeature)
            {
                tmpInt = Master.Instance.SwitchService.Instrument.BumpThreshold;
                if (settings.BumpThreshold != tmpInt)
                {
                    LogUpdate("BumpThreshold", settings.BumpThreshold, tmpInt);
                    _instCtrlr.SetBumpThreshold(settings.BumpThreshold);
                    Master.Instance.SwitchService.Instrument.BumpThreshold = settings.BumpThreshold;
                }
            }

            if (_instCtrlr.HasBumpTimeoutFeature)
            {
                tmpInt = Master.Instance.SwitchService.Instrument.BumpTimeout;
                if (settings.BumpTimeout != tmpInt)
                {
                    LogUpdate("BumpTimeout", settings.BumpTimeout, tmpInt);
                    _instCtrlr.SetBumpTimeout(settings.BumpTimeout);
                    Master.Instance.SwitchService.Instrument.BumpTimeout = settings.BumpTimeout;
                }
            }

            UpdateLanguage(settings, Master.Instance.SwitchService.Instrument);

            if (_instCtrlr.HasMagneticFieldDurationFeature)
            {
                tmpInt = Master.Instance.SwitchService.Instrument.MagneticFieldDuration;
                if (settings.MagneticFieldDuration != tmpInt)
                {
                    LogUpdate("MagneticFieldDuration", settings.MagneticFieldDuration, tmpInt);
                    _instCtrlr.SetMagneticFieldDuration(settings.MagneticFieldDuration);
                    Master.Instance.SwitchService.Instrument.MagneticFieldDuration = settings.MagneticFieldDuration;
                }
            }

            if (_instCtrlr.HasCompanyNameFeature)
            {
                tmpString = Master.Instance.SwitchService.Instrument.CompanyName;
                if (settings.CompanyName != tmpString)
                {
                    LogUpdate("CompanyName", settings.CompanyName, tmpString);
                    _instCtrlr.SetCompanyName(settings.CompanyName);
                    Master.Instance.SwitchService.Instrument.CompanyName = settings.CompanyName;
                }
            }

            if (_instCtrlr.HasCompanyMessageFeature)
            {
                tmpString = Master.Instance.SwitchService.Instrument.CompanyMessage;
                if (settings.CompanyMessage != tmpString)
                {
                    LogUpdate("CompanyMessage", settings.CompanyMessage, tmpString);
                    _instCtrlr.SetCompanyMessage(settings.CompanyMessage);
                    Master.Instance.SwitchService.Instrument.CompanyMessage = settings.CompanyMessage;
                }
            }

            UpdateAlarmActionMessages(settings);

            // The GBPRO has no backlight settings
            // 11/13/07 - GBPlus also has no backlight settings.
            // 4/30/10 - Currently, None of the modbus instrument have a backlight setting.
            if (settings.Backlight != BacklightSetting.Unknown)
            {
                // Retrieve instrument's current backlight setting.
                BacklightSetting tmpBacklightSettings = Master.Instance.SwitchService.Instrument.Backlight;
                if (settings.Backlight != tmpBacklightSettings)
                {
                    LogUpdate("Backlight", settings.Backlight.ToString(), tmpBacklightSettings.ToString());
                    _instCtrlr.SetBacklightSetting(settings.Backlight);
                    Master.Instance.SwitchService.Instrument.Backlight = settings.Backlight;
                }
            }

            //Suresh 30-SEPTEMBER-2011 INS-2277
            if (_instCtrlr.HasBacklightTimeoutConfigFeature)
            {
                tmpInt = Master.Instance.SwitchService.Instrument.BacklightTimeout;
                if (settings.BacklightTimeout != tmpInt)
                {
                    LogUpdate("BackLightTimeout", settings.BacklightTimeout, tmpInt);
                    _instCtrlr.SetBacklightTimeout(settings.BacklightTimeout);
                    Master.Instance.SwitchService.Instrument.BacklightTimeout = settings.BacklightTimeout;
                }
            }

            // Synch instrument's clock with docking station's clock.
            if (settings.Type != DeviceType.GBPLS)   // GBPlus has no clock.
            {
                DateTime instTime = _instCtrlr.GetTime();
                DateTime localNow = Configuration.GetLocalTime();

                // Round times to nearest second.
                localNow = new DateTime(localNow.Year, localNow.Month, localNow.Day, localNow.Hour, localNow.Minute, localNow.Second);
                instTime = new DateTime(instTime.Year, instTime.Month, instTime.Day, instTime.Hour, instTime.Minute, instTime.Second);

                if (localNow != instTime)
                {
                    const string dateTimeFormat = "HH:mm:ss MM/dd/yyyy";
                    LogUpdate("Time", localNow.ToString(dateTimeFormat), instTime.ToString(dateTimeFormat));
                    _instCtrlr.SetTime(localNow);
                }
            }

            // Custom response factors NEED to be set here BEFORE we update the sensors.

            _instCtrlr.SetCustomPidFactors(settings.CustomPidFactors);
            Master.Instance.SwitchService.Instrument.CustomPidFactors = settings.CustomPidFactors;

            _instCtrlr.SetFavoritePidFactors(settings.FavoritePidFactors);
            Master.Instance.SwitchService.Instrument.FavoritePidFactors = settings.FavoritePidFactors;

            for (int pos = 1; pos <= _instCtrlr.Driver.Definition.MaxSensorCapacity; pos++)
            {
                InstalledComponent component = Master.Instance.SwitchService.Instrument.InstalledComponents.Find(ic => ic.Position == pos);

                if (component == null || !(component.Component is Sensor))
                {
                    Log.Warning("No sensor installed at position " + pos);
                }
                else
                {
                    UpdateSensor(pos, (Sensor)component.Component, settings.SensorSettings);
                }
            }

            UpdateUsersAndSites(settings);

            // Set the instrument's enabled options.  Although we pass into the method only those
            // options that should be enabled, it returns the state (enabled or disabled) of ALL options.
            // We update the switch service's cached instrument with the list of All options.
            List <DeviceOption> deviceOptions = _instCtrlr.SetInstrumentOptions(settings.Options);
            Master.Instance.SwitchService.Instrument.Options = deviceOptions;

            // instrument level wireless settings
            if (_instCtrlr.Driver.Definition.HasWirelessFeature && (settings.Type == DeviceType.SC || settings.Type == DeviceType.VPRO))
            {
                tmpInt = Master.Instance.SwitchService.Instrument.WirelessPeerLostThreshold;
                if (settings.WirelessPeerLostThreshold != tmpInt)
                {
                    LogUpdate("WirelessPeerLostThreshold", settings.WirelessPeerLostThreshold, tmpInt);
                    _instCtrlr.Driver.setWirelessPeerLostThreshold(settings.WirelessPeerLostThreshold);
                    Master.Instance.SwitchService.Instrument.WirelessPeerLostThreshold = settings.WirelessPeerLostThreshold;
                }

                tmpInt = Master.Instance.SwitchService.Instrument.WirelessNetworkLostThreshold;
                if (settings.WirelessNetworkLostThreshold != tmpInt)
                {
                    LogUpdate("WirelessNetworkLostThreshold", settings.WirelessNetworkLostThreshold, tmpInt);
                    _instCtrlr.Driver.setWirelessNetworkLostThreshold(settings.WirelessNetworkLostThreshold);
                    Master.Instance.SwitchService.Instrument.WirelessNetworkLostThreshold = settings.WirelessNetworkLostThreshold;
                }

                if (_instCtrlr.Driver.Definition.HasWirelessNetworkDisconnectDelayConfigFeature)
                {
                    tmpInt = Master.Instance.SwitchService.Instrument.WirelessNetworkDisconnectDelay;
                    if (settings.WirelessNetworkDisconnectDelay != tmpInt)
                    {
                        LogUpdate("WirelessNetworkDisconnectDelay", settings.WirelessNetworkDisconnectDelay, tmpInt);
                        _instCtrlr.Driver.setWirelessNetworkDisconnectDelay(settings.WirelessNetworkDisconnectDelay);
                        Master.Instance.SwitchService.Instrument.WirelessNetworkDisconnectDelay = settings.WirelessNetworkDisconnectDelay;
                    }
                }

                tmpInt = Master.Instance.SwitchService.Instrument.WirelessReadingsDeadband;
                if (settings.WirelessReadingsDeadband != tmpInt)
                {
                    LogUpdate("WirelessReadingsDeadband", settings.WirelessReadingsDeadband, tmpInt);
                    _instCtrlr.Driver.setWirelessReadingsDeadband(settings.WirelessReadingsDeadband);
                    Master.Instance.SwitchService.Instrument.WirelessReadingsDeadband = settings.WirelessReadingsDeadband;
                }

                if (_instCtrlr.Driver.Definition.HasBluetoothFeature)
                {
                    tmpInt = Master.Instance.SwitchService.Instrument.LoneWorkerOkMessageInterval;
                    if (settings.LoneWorkerOkMessageInterval != tmpInt)
                    {
                        LogUpdate("BluetoothLoneWorkerOkMessageInterval", settings.LoneWorkerOkMessageInterval, tmpInt);
                        _instCtrlr.Driver.setBluetoothLoneWorkerOkMessageInterval(settings.LoneWorkerOkMessageInterval);
                        Master.Instance.SwitchService.Instrument.LoneWorkerOkMessageInterval = settings.LoneWorkerOkMessageInterval;
                    }

                    //INS-7908 -- To handle for the Ventis Pro Instrument, which has already upgraded to latest firmware, but the bluetooth feature has not been enabled in it.
                    //As of now, only VPRO instrument has bluetooth feature from v2.0 and above versions (HasBluetoothFeature determines that).
                    //The below check for VPRO is done specifically, because in future if any instrument has bluetooth feature, we may need to handle that in a better way.
                    if (settings.Type == DeviceType.VPRO && !_instCtrlr.Driver.isBluetoothFeatureEnabled())
                    {
                        //Note: Do we need to check the BluetoothFeatureActivated in settings and allow to override here? as of now, ignoring the settings value for the bluetooth, since by default its disabled now but we need to make enable by default.
                        //Why we are not enabling the feature at the factory/account/settings/instrument level enable by default and set the value based on that??
                        LogUpdate("BluetoothFeatureEnabled", true, Master.Instance.SwitchService.Instrument.BluetoothFeatureActivated);
                        _instCtrlr.Driver.enableBluetoothFeature(true);
                        Master.Instance.SwitchService.Instrument.BluetoothFeatureActivated = true;
                    }
                }
            }

            UpdateWirelessModule(settings, Master.Instance.SwitchService.Instrument.WirelessModule);

            if (_instCtrlr.Driver.Definition.HasGpsFeature)
            {
                tmpInt = Master.Instance.SwitchService.Instrument.GpsReadingInterval;
                if (settings.GpsReadingInterval != tmpInt)
                {
                    LogUpdate("GpsReadingInterval", settings.GpsReadingInterval, tmpInt);
                    _instCtrlr.Driver.setGpsReadingInterval(settings.GpsReadingInterval);
                    Master.Instance.SwitchService.Instrument.GpsReadingInterval = settings.GpsReadingInterval;
                }
            }

            // After updating all the instrument settings we need to send a command to the instrument
            // to have it persist the settings so they will be retained after power is lost.
            // Only SafeCore needs this command.
            _instCtrlr.Driver.saveInstrumentSettings();

            // Read all settings fresh from the docked instrument to ensure accurate instrument
            // settings are cached as well as uploaded to iNet.  Reading all settings during the
            // instrument settings update operation does not incur an extra time penalty for
            // re-establishing communication with the docked instrument like it would in a
            // follow-up instrument settings read operation.
            Instrument returnInstrument = _instCtrlr.DiscoverDockedInstrument(false);

            if (returnInstrument == null || returnInstrument.SerialNumber.Length == 0)
            {
                throw new InstrumentNotDockedException();
            }

            Master.Instance.SwitchService.Instrument = (Instrument)returnInstrument.Clone();

            return(returnInstrument);
        }
예제 #10
0
        internal static Mock <InstrumentController> GetInstrumentControllerMockForCal(Instrument instrument = null)
        {
            Mock <InstrumentController> instrumentController = new Mock <InstrumentController>();

            SensorPosition[] sensorPositions = null;

            if (instrument != null)
            {
                sensorPositions = instrument.InstalledComponents
                                  .Where(installedComponent => installedComponent.Component is Sensor)
                                  .Select(sensor => new SensorPosition(sensor.Position, SensorMode.Installed, false))
                                  .ToArray();

                instrumentController.Setup(x => x.AccessoryPump).Returns(instrument.AccessoryPump);
            }

            instrumentController.Setup(x => x.Initialize(It.IsAny <InstrumentController.Mode>()));
            instrumentController.Setup(x => x.TestForInstrumentReset(It.IsAny <SensorGasResponse>(), It.IsAny <string>())).Returns(false);
            instrumentController.Setup(x => x.GetSensorBiasStatus()).Returns(true);
            instrumentController.Setup(x => x.EnablePump(It.IsAny <bool>()));
            instrumentController.Setup(x => x.GetSensorPositions()).Returns(sensorPositions);
            instrumentController.Setup(x => x.OpenGasEndPoint(It.IsAny <GasEndPoint>(), It.IsAny <int>()));
            instrumentController.Setup(x => x.CloseGasEndPoint(It.IsAny <GasEndPoint>()));
            instrumentController.Setup(x => x.GetSensorReading(It.IsAny <int>(), It.IsAny <double>())).Returns(0);
            instrumentController.Setup(x => x.GetSensorBumpStatus(It.IsAny <int>())).Returns(true);
            instrumentController.Setup(x => x.PauseGasFlow(It.IsAny <GasEndPoint>(), It.IsAny <long>()));
            instrumentController.Setup(x => x.GetSensorMaximumReading(It.IsAny <int>(), It.IsAny <double>())).Returns(30);

            instrumentController.Setup(x => x.IsSensorEnabled(It.IsAny <int>())).Returns <int>(pos =>
            {
                InstalledComponent installedComponent = instrument.InstalledComponents.Find(component => component.Position == pos);
                if (installedComponent != null)
                {
                    return(installedComponent.Component.Enabled);
                }
                return(false);
            });
            instrumentController.Setup(x => x.GetSensorLowAlarm(It.IsAny <int>(), It.IsAny <double>())).Returns <int, double>((pos, resolution) =>
            {
                InstalledComponent installedComponent = instrument.InstalledComponents.Find(component => component.Position == pos);
                if (installedComponent != null)
                {
                    Sensor sensor = (Sensor)installedComponent.Component;
                    return(sensor.Alarm.Low);
                }
                return(0);
            });

            #region [ Calibration Methods ]

            instrumentController.Setup(x => x.PreconditionSensor(It.IsAny <InstalledComponent>(), It.IsAny <GasEndPoint>(), It.IsAny <SensorGasResponse>())).Returns(new TimeSpan(0));
            instrumentController.Setup(x => x.GetSensorZeroingStatus(It.IsAny <int>())).Returns(true);
            instrumentController.Setup(x => x.IsSensorCalibrationEnabled(It.IsAny <InstalledComponent>())).Returns(true);
            instrumentController.Setup(x => x.GetSensorLastCalibrationTime(It.IsAny <int>())).Returns(DateTime.Now);
            instrumentController.Setup(x => x.ZeroSensors(It.IsAny <GasEndPoint>())).Returns(true);
            instrumentController.Setup(x => x.SetSensorCalGasConcentration(It.IsAny <int>(), It.IsAny <double>(), It.IsAny <double>()));
            instrumentController.Setup(x => x.SetCalibrationGasConcentration(It.IsAny <InstalledComponent>(), It.IsAny <double>(), It.IsAny <bool>()));
            instrumentController.Setup(x => x.GetSensorCalibrationTimeout(It.IsAny <int>())).Returns(new TimeSpan(0, 0, 10));
            instrumentController.Setup(x => x.GetSensorBaseline(It.IsAny <int>())).Returns(0);
            instrumentController.Setup(x => x.GetSensorZeroOffset(It.IsAny <int>(), It.IsAny <double>())).Returns(0);
            instrumentController.Setup(x => x.GetSensorSpanCoeff(It.IsAny <int>())).Returns(49485);
            instrumentController.Setup(x => x.BeginSensorCalibration(It.IsAny <IEnumerable <int> >()));
            instrumentController.Setup(x => x.GetSensorCalibrationFlowRate(It.IsAny <InstalledComponent>())).Returns(500);
            instrumentController.Setup(x => x.GetSensorPreconditionFlowRate(It.IsAny <InstalledComponent>())).Returns(500);
            instrumentController.Setup(x => x.GetSensorPreconditionTimeout(It.IsAny <InstalledComponent>())).Returns(new TimeSpan(0, 0, 10));

            instrumentController.Setup(x => x.SetCalibrationGasConcentration(It.IsAny <InstalledComponent>(), It.IsAny <GasEndPoint>())).Returns <InstalledComponent, GasEndPoint>((installedComponent, gasEndPoint) =>
            {
                Sensor sensor = (Sensor)installedComponent.Component;
                GasConcentration gasConcentration = null;

                gasConcentration = gasEndPoint.Cylinder.GasConcentrations.Find(gas => gas.Type.Code == sensor.CalibrationGas.Code);
                if (gasConcentration != null)
                {
                    sensor.CalibrationGasConcentration = gasConcentration.Concentration;
                    return(gasConcentration.Concentration);
                }

                gasConcentration = gasEndPoint.Cylinder.GasConcentrations.Find(gas => gas.Type.Code == GasCode.FreshAir && sensor.CalibrationGas.Code == GasCode.O2);
                if (gasConcentration != null)
                {
                    sensor.CalibrationGasConcentration = 209000;
                    return(209000);
                }

                return(0);
            });
            if (instrument != null && instrument.InstalledComponents.Where(installedComponent => installedComponent.Component is Sensor).Count() > 0)
            {
                int noOfReadingsNeeded = 3;
                Func <string, int, string>  sensorID = (sn, position) => string.Join("_", sn, position);
                Func <Sensor, double>       sensorCalibrationGasConcentration = s => ((SensorType)s.Type).MeasurementType != MeasurementType.VOL ? s.CalibrationGasConcentration : s.CalibrationGasConcentration * 10000;
                Dictionary <string, double> sensorReadings      = createSensorReadingDictionary(instrument.InstalledComponents.Where(installedComponent => installedComponent.Component is Sensor));
                Dictionary <int, double>    sensorReadingCounts = createSensorReadingCount(instrument.InstalledComponents.Where(installedComponent => installedComponent.Component is Sensor));
                Func <int, double>          sensorSpanReserve   = position =>
                {
                    double             spanReserve        = 0;
                    InstalledComponent installedComponent = instrument.InstalledComponents.Find(installComp => installComp.Position == position);
                    if (installedComponent != null)
                    {
                        Sensor sensor        = (Sensor)installedComponent.Component;
                        double calGasConc    = sensorCalibrationGasConcentration(sensor);
                        double sensorReading = sensorReadings[sensorID(sensor.SerialNumber, position)];
                        spanReserve = (sensorReading / calGasConc) * 100;
                    }
                    return(spanReserve);
                };

                instrumentController.Setup(x => x.GetSensorCalibrationReading(It.IsAny <int>(), It.IsAny <double>()))
                .Returns <int, double>((position, resolution) =>
                {
                    double reading = 0;
                    InstalledComponent installedComponent = instrument.InstalledComponents.Find(installComp => installComp.Position == position);
                    if (installedComponent != null)
                    {
                        reading = sensorReadings[sensorID(installedComponent.Component.SerialNumber, position)];
                    }
                    return(reading);
                })
                .Callback <int, double>((position, resolution) =>
                {
                    InstalledComponent installedComponent = instrument.InstalledComponents.Find(installComp => installComp.Position == position);
                    if (installedComponent != null)
                    {
                        Sensor sensor        = (Sensor)installedComponent.Component;
                        double calGasConc    = sensorCalibrationGasConcentration(sensor);
                        double increment     = calGasConc / noOfReadingsNeeded;
                        double sensorReading = sensorReadings[sensorID(sensor.SerialNumber, position)];
                        if (sensorReading < (calGasConc + increment))
                        {
                            sensorReadings[sensorID(sensor.SerialNumber, position)] += increment;
                        }
                    }
                });

                instrumentController.Setup(x => x.GetSensorCalibrationStatus(It.IsAny <int>()))
                .Returns <int>(position => sensorSpanReserve(position) > 80);

                instrumentController.Setup(x => x.IsSensorCalibrating(It.IsAny <int>()))
                .Returns <int>(position => sensorReadingCounts[position] <= noOfReadingsNeeded)
                .Callback <int>(position => sensorReadingCounts[position] += 1);

                instrumentController.Setup(x => x.GetSensorSpanReserve(It.IsAny <int>())).Returns <int>(pos => sensorSpanReserve(pos));
                instrumentController.Setup(x => x.GetSensorSpanCoeff(It.IsAny <int>())).Returns(1);

                instrumentController.Setup(x => x.GetSensorLastCalibrationTime(It.IsAny <int>()))
                .Returns <int>(position => sensorReadingCounts[position] > 0 ? DateTime.Now : DateTime.Now.AddMinutes(-5))
                .Callback <int>(position =>
                {
                    if (sensorReadingCounts[position] > 1)
                    {
                        sensorReadingCounts[position] = 0;
                    }
                });
            }

            #endregion

            return(instrumentController);
        }
    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 );
        }
    }
    /// <summary>
    /// Get the calibration test gas end point for a sensor.
    /// </summary>
    /// <param name="installedComponent">The sensor to get the gas for.</param>
    /// <returns>The correct gas end point.</returns>
    /// <exception cref="CorrectCalibrationGasUnavailable">
    /// Thrown when no cylinder is provided for the sensor.
    /// </exception>
	protected GasEndPoint GetSensorGasEndPoint( InstalledComponent installedComponent )
    {
        Sensor sensor = null;
        MeasurementType sensorMeasurementType = MeasurementType.Unknown;

        if (installedComponent != null)
        {
            sensor = installedComponent.Component as Sensor;
            sensorMeasurementType = ((SensorType)sensor.Type).MeasurementType;
        }

        #region LogDebug
        if (sensor != null)
        {
            Log.Debug( "Calibration.GetSensorGasEndPoint" );
            Log.Debug( "Finding appropriate cal gas for Sensor: " + sensor.Type
                + ", UID: " + sensor.Uid
                + ", CalGas Code: " + sensor.CalibrationGas.Code
                + ", Conc: " + sensor.CalibrationGasConcentration
                + ", Pos: " + installedComponent.Position );
        }
        #endregion

        // If this is a sensor that uses 20.9 O2 for its calibration gas,
        // then attempt to find a cylinder that offers Zero Air; if 
        // Zero Air is not available, find Fresh Air.  If neither are
        // available, then fall out of this code and proceed to the main
        // processing loop below.
        if ( sensor.CalibrationGas.Code == GasCode.O2 && sensor.CalibrationGasConcentration == 20.9 )
        {
			GasEndPoint air = GetSensorZeroAir( installedComponent, false, true ); 
            if (air != null)
            {
                _triedGasEndPoints[air] = air;
                Cylinder cyl = air.Cylinder;
                if (cyl.IsZeroAir)
                    Log.Debug("...SELECTED GasEndPoint.  Has ZeroAir for O2 at 20.9.");
                else if (cyl.IsFreshAir)
                    Log.Debug("...SELECTED GasEndPoint.  Has FreshAir for O2 at 20.9.");
                else
                    // We do not expect to reach this line of code, but it has been placed here to 
                    // log that GetSensorZeroAir returned a cylinder that was Fresh Air or Zero Air.
                    Log.Debug(string.Format("...SELECTED GasEndPoint: {0}", cyl.ToString()));
                return air;
            }
        }

        double sensorConcentration = sensor.CalibrationGasConcentration;

        double lelMultiplier = GasType.Cache[ sensor.CalibrationGas.Code ].LELMultiplier;

        int pointCount = 0;

        Log.Debug( "SCAN 1 (Find appropriate gas with desired concentration)..." );
		foreach ( GasEndPoint gasEndPoint in GasEndPoints )
        {
            Cylinder cyl = gasEndPoint.Cylinder;

            #region LogDebug
            string msg = "GasEndPoint #" + ++pointCount;
            Log.Debug(msg);

            msg = "...Pos=" + gasEndPoint.Position
                //+ ", ID=" + cyl.ID
                + ", FactID=" + cyl.FactoryId
                + ", Part=" + cyl.PartNumber
                + ", Fresh=" + cyl.IsFreshAir
                + ", ZeroAir=" + cyl.IsZeroAir
                + ", Pressure=" + cyl.Pressure.ToString();
            if ( cyl.Volume != DomainModelConstant.NullInt ) msg += ", Vol=" + cyl.Volume;

            Log.Debug( msg );
            
            msg = "......";
            foreach ( GasConcentration gasCon in cyl.GasConcentrations )
            {
                msg += "[" + gasCon.Type.Code;
                msg += " ";
                msg += ( gasCon.Concentration == DomainModelConstant.NullDouble ) ? "fresh" : gasCon.Concentration.ToString();
                msg += "]";
            }

            Log.Debug( msg );
            #endregion

            // Ignore already tried cylinders.
            if ( _triedGasEndPoints.ContainsKey( gasEndPoint ) )
            {
                Log.Debug( "...Rejected. Already tried cylinder." );
                continue;
            }

            // Ignore empty cylinders.
            if ( cyl.Pressure == PressureLevel.Empty )
            {
                _triedGasEndPoints[ gasEndPoint ] = gasEndPoint;
                Log.Debug( "...Rejected. Cylinder empty." );
                continue;
            }

            // Check the measurement type for how to multiply the concentration.

            // Make sure we convert sensor's LEL 
            // concentration to PPM for matching against cylinders concentrations
            if (sensorMeasurementType == MeasurementType.LEL)
            {
                // Convert sensor's cal gas concentration from %LEL to PPM
                sensorConcentration = sensor.CalibrationGasConcentration / lelMultiplier;
                // We want to round the concentration to the nearest hundred.
                // e.g., if concentration is 3928, we want to round it to 3900.
                //
                // DO NOT DO THE FOLLOWING COMMENTED OUT LINE!  IT NEEDS TO BE KEPT
                // SEPARATED AS THREE SEPARATE LINES, OTHERWISE IT CRASHES WINCE.
                // SOME BUG IN COMPACT FRAMEWORK?
                // sensorConcentration = Math.Round( sensorConcentration / 100.0, 0 ) * 100.0;
                sensorConcentration /= 100.0;
                sensorConcentration = Math.Round( sensorConcentration, 0 );
                sensorConcentration *= 100.0;
                Log.Debug( string.Format( "...Sensor concentration of {0}%% LEL equals {1} PPM ({2} multiplier)",
                    sensor.CalibrationGasConcentration, sensorConcentration, lelMultiplier ) );
            }

            else if (sensorMeasurementType == MeasurementType.VOL)
            {
                sensorConcentration = sensor.CalibrationGasConcentration * 10000;
                Log.Debug( string.Format( "...Sensor concentration of {0}%% VOL equals {1} PPM",
                    sensor.CalibrationGasConcentration, sensorConcentration ) );
            }

            // Ignore cylinders lacking the right gases.
            if (!cyl.ContainsGas(sensor.CalibrationGas.Code, sensorConcentration, sensorMeasurementType))
            {
                Log.Debug( "...Rejected. Does not contain " + sensor.CalibrationGas.Code + " with desired concentration " + sensorConcentration );
                continue;
            }

            // SGF  Feb-10-2009  DSW-72
            // Ensure cylinder concentration is not higher than 60% for potentially explosive cal gases

            if ( lelMultiplier > 0.0 )
            {
                // We now know this cylinder contains the desired gas concentration. But we don't allow
                // calibration of combustible sensor using any cylinder that is higher than 60% LEL.
                // So, find the gas in the cylinder and check it's LEL level.

                double cylinderPPM = -1.0;
                foreach ( GasConcentration gasConcentration in cyl.GasConcentrations )
                {
                    if ( gasConcentration.Type.Code == sensor.CalibrationGas.Code )
                    {
                        cylinderPPM = gasConcentration.Concentration;
                        break;
                    }
                }

                if ( cylinderPPM < 0 ) // this should never happen.  Which means we better check.
                {
                    Log.Debug( "...Rejected. Does not contain " + sensor.CalibrationGas.Code );
                    continue;
                }

                double cylinderLEL = cylinderPPM * lelMultiplier;
                if ( cylinderLEL > 60.0 )  // cylinder is higher than 60%?  Don't use it.
                {
                    Log.Debug( string.Format( "...Rejected. Contains {0} with too high LEL concentration ({1}%%)",
                        sensor.CalibrationGas.Code,  Math.Round( cylinderLEL, 1 ) ) );
                    Log.Debug( "...Will not use concentration higher than 60%% LEL." );
                    continue;
                }
            }

            // Found a cylinder with the correct gas and concentration.
            _triedGasEndPoints[ gasEndPoint ] = gasEndPoint;

            Log.Debug( "...SELECTED. Contains " + sensor.CalibrationGas.Code + " with desired concentration " + sensorConcentration );

            return gasEndPoint;

        }  // end-foreach 1

        Log.Debug( "SCAN 2 (find appropriate gas with any concentration)..." );

        pointCount = 0;
		foreach ( GasEndPoint gasEndPoint in GasEndPoints )
        {
            Cylinder cyl = gasEndPoint.Cylinder;

            #region LogDebug

            string msg = "GasEndPoint #" + ++pointCount;
            Log.Debug(msg);

            msg = "...Pos=" + gasEndPoint.Position
                //+ ", ID=" + cyl.ID
                + ", FactID=" + cyl.FactoryId
                + ", Part=" + cyl.PartNumber
                + ", Fresh=" + cyl.IsFreshAir
                + ", ZeroAir=" + cyl.IsZeroAir
                + ", Pressure=" + cyl.Pressure.ToString();
            if ( cyl.Volume != DomainModelConstant.NullInt ) msg += ", Vol=" + cyl.Volume;

            Log.Debug( msg );
            
            msg = "......";
            foreach ( GasConcentration gasCon in cyl.GasConcentrations )
            {
                msg += "[" + gasCon.Type.Code;
                msg += " ";
                msg += ( gasCon.Concentration == DomainModelConstant.NullDouble ) ? "fresh" : gasCon.Concentration.ToString();
                msg += "]";
            }

            Log.Debug( msg );
            #endregion

            // Ignore already tried cylinders.
            if ( _triedGasEndPoints.ContainsKey( gasEndPoint ) )
            {
                Log.Debug( "...Rejected. Already tried cylinder." );
                continue;
            }

            // Ignore cylinders lack the right gases.
            if ( ! cyl.ContainsGas( sensor.CalibrationGas.Code ) )
            {
                Log.Debug( "...Rejected. Does not contain " + sensor.CalibrationGas.Code  );
                continue;
            }

            // SGF  Feb-10-2009  DSW-72
            // Ensure cylinder concentration is not higher than 60% for potentially explosive cal gases
            if ( lelMultiplier > 0.0 )
            {
                // We now know this cylinder contains the right gas.  But we don't allow 
                // calibration of combustible sensor using any cylinder that is higher than 60% LEL.
                // So, find the gas in the cylinder and check it's LEL level.

                double cylinderPPM = -1.0;
                foreach ( GasConcentration gasConcentration in cyl.GasConcentrations )
                {
                    if ( gasConcentration.Type.Code == sensor.CalibrationGas.Code )
                    {
                        cylinderPPM = gasConcentration.Concentration;
                        break;
                    }
                }

                if ( cylinderPPM < 0 ) // this should never happen.  Which means we better check.
                {
                    Log.Debug( "...Rejected. Does not contain " + sensor.CalibrationGas.Code );
                    continue;
                }

                double cylinderLEL = cylinderPPM * lelMultiplier;
                if ( cylinderLEL > 60.0 )  // cylinder is higher than 60%?  Don't use it.
                {
                    Log.Debug( string.Format( "...Rejected. Contains {0} with too high LEL concentration ({1}%%)",
                        sensor.CalibrationGas.Code,  Math.Round( cylinderLEL, 1 ) ) );
                    Log.Debug( "...Will not use concentration higher than 60%% LEL." );
                    continue;
                }

            } // end-if MeasurementTypes.LEL

            // Found a cylinder with the correct gas.
            _triedGasEndPoints[ gasEndPoint ] = gasEndPoint;
            
            Log.Debug( "...SELECTED. Contains " + sensor.CalibrationGas.Code );

            return gasEndPoint;

        } // end-foreach 2

		// If this is a sensor that uses O2 for its calibration gas, and no cylinder has been found 
		// yet, then attempt to find a cylinder that offers Fresh Air.  We don't try to find a Zero 
		// Air cylinder because one of the scans above would have already selected it if one was
		// available.  Also, O2 sensors with a cal gas concentration of 20.9 should have already
		// selected a Zero Air or Fresh Air cylinder so we don't need to check again.
		if ( sensor.CalibrationGas.Code == GasCode.O2 && sensor.CalibrationGasConcentration != 20.9 )
		{
			GasEndPoint air = GetSensorFreshAir( installedComponent, true );
			if ( air != null )
			{
				_triedGasEndPoints[air] = air;
				Cylinder cyl = air.Cylinder;
				if ( cyl.IsFreshAir )
					Log.Debug( "...SELECTED GasEndPoint.  Has FreshAir for O2." );
				else
					// We do not expect to reach this line of code, but it has been placed here to 
					// log that GetSensorFreshAir returned a cylinder.
					Log.Debug( string.Format( "...SELECTED GasEndPoint: {0}", cyl.ToString() ) );
				return air;
			}
		}

        Log.Debug( "NO APPROPRIATE CALIBRATION GAS FOUND!" );

        throw new CorrectCalibrationGasUnavailable( string.Format( "Sensor {0}, CalGas={1}({2})", 
            sensor.Uid, sensor.CalibrationGas.Code, sensor.CalibrationGasConcentration ) ); // No gas end point was found.
    }
    /// <summary>
    /// Find the fresh air end point for this sensor.
    /// </summary>
    /// <param name="installedComponent">The sensor to find the fresh air for.</param>
    /// <param name="checkIfUsed">If true, check if the cylinder under consideration
    /// has already been used.</param>
    /// <returns>The correct fresh air gas end point.</returns>
    /// <exception cref="CorrectCalibrationGasUnavailable">
    /// If there are no fresh air gas end points for this sensor.
    /// </exception>
	protected GasEndPoint GetSensorFreshAir( InstalledComponent installedComponent, bool checkIfUsed ) 
    {
        Sensor sensor = null;

        if ( installedComponent != null )
            sensor = installedComponent.Component as Sensor;

        string msg = "Finding Fresh air";
        if ( sensor != null )
            msg += " for sensor " + sensor.Uid;
        msg += "...";
        Log.Debug( msg );

        // Find fresh air and zero gas end points.
        int pointCount = 0;
		foreach ( GasEndPoint gasEndPoint in GasEndPoints )
        {
            #region LogDebug
            msg = "GasEndPoint #" + ++pointCount;
            Log.Debug(msg);

            Cylinder cyl = gasEndPoint.Cylinder; 

            msg = "...Pos=" + gasEndPoint.Position
                + ", FactID=" + cyl.FactoryId
                + ", Part=" + cyl.PartNumber
                + ", Fresh=" + cyl.IsFreshAir
                + ", ZeroAir=" + cyl.IsZeroAir
                + ", Pressure=" + cyl.Pressure.ToString();
            if ( cyl.Volume != DomainModelConstant.NullInt ) msg += ", Vol=" + cyl.Volume;

            Log.Debug( msg );
            #endregion

            // Ignore non-fresh air cylinders
            if ( !cyl.IsFreshAir )
            {
                Log.Debug( "...Rejected.  Not fresh air." );
                continue;
            }

            if ( cyl.Pressure == PressureLevel.Empty )
            {
                Log.Debug( "...Rejected fresh air. Cylinder empty." );
                continue;
            }

            if (checkIfUsed == true && _triedGasEndPoints.ContainsKey(gasEndPoint))
            {
                Log.Debug("...Rejected. Already tried cylinder.");
                continue;
            }

            if ( sensor == null )
            {
                Log.Debug( "...SELECTED GasEndPoint. Fresh air found." );
                return gasEndPoint;
            }

            Log.Debug( "...SELECTED GasEndPoint.  Fresh air found for sensor " + sensor.Uid );
            return gasEndPoint;
        }
        
        Log.Debug( "No fresh air found.");

        throw new CorrectCalibrationGasUnavailable( GasCode.FreshAir ); // No calibration gases were found.
    }