Ejemplo n.º 1
0
        /// <summary>
        /// Create gas endpoints for testing, port 1 contains fresh air and port 2 contains default 4-gas cylinder
        /// Change gas endpoints as needed for your testing if default configuration is not what you wanted.
        /// </summary>
        /// <param name="components"></param>
        /// <returns></returns>
        internal static List <GasEndPoint> GetGasEndPointsForTest(List <InstalledComponent> components)
        {
            List <GasEndPoint> gasEndPoints = new List <GasEndPoint>();

            //Port 1 is fresh air
            gasEndPoints.Add(GasEndPoint.CreateFreshAir(1));

            //Port 2, add 4-gas cylinder
            Cylinder cyl = new Cylinder("1810-9155", "ISC")
            {
                ExpirationDate = DateTime.Today.AddDays(30), Pressure = PressureLevel.Full
            };

            cyl.GasConcentrations.AddRange(new List <GasConcentration>()
            {
                new GasConcentration(GasType.Cache[GasCode.CO.ToString()], 100.00),
                new GasConcentration(GasType.Cache[GasCode.H2S.ToString()], 25.00),
                new GasConcentration(GasType.Cache[GasCode.Pentane.ToString()], 3521.10),
                new GasConcentration(GasType.Cache[GasCode.O2.ToString()], 180000.00)
            });

            gasEndPoints.Add(new GasEndPoint(cyl, 2, GasEndPoint.Type.Manual));

            return(gasEndPoints);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Determines if passed-in GasEndPoints list already contains the passed-in cylinder.
        /// <para>
        /// (Matches on cylinder position and part number.)
        /// </para>
        /// </summary>
        /// <param name="candidateCylinder"></param>
        /// <param name="gasEndPoints"></param>
        /// <returns></returns>
        private bool HasCylinder(GasEndPoint candidateCylinder, List <GasEndPoint> gasEndPoints)
        {
            GasEndPoint foundEndPoint = gasEndPoints.Find(g => g.Position == candidateCylinder.Position &&
                                                          g.Cylinder.PartNumber == candidateCylinder.Cylinder.PartNumber);

            return(foundEndPoint != null);
        }
        /// <summary>
        /// Logs the gas end point.  This is a helper method for GetGasEndPoint.
        /// </summary>
        /// <param name="gasEndPoint"></param>
        /// <param name="pointCount"></param>
        private void LogGasEndPoint(GasEndPoint gasEndPoint, int pointCount)
        {
            string 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()
                  + ", ExpDate=" + cyl.ExpirationDate;
            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);
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="gep"></param>
        /// <param name="trx"></param>
        /// <returns></returns>
        public bool Save(GasEndPoint gep, DataAccessTransaction trx)
        {
            string sql = string.Empty;

            try
            {
                Delete(gep, trx);   // delete any old cylinder at this position.

                sql = "INSERT INTO GASENDPOINT ( POSITION, RECUPDATETIMEUTC, FACTORYID, PARTNUMBER, PRESSURE, REFILLDATE, EXPIRATIONDATE, INSTALLATIONTYPE ) VALUES ( @POSITION, @RECUPDATETIMEUTC, @FACTORYID, @PARTNUMBER, @PRESSURE, @REFILLDATE, @EXPIRATIONDATE, @INSTALLATIONTYPE )";
                using (IDbCommand cmd = GetCommand(sql, trx))
                {
                    cmd.Parameters.Add(GetDataParameter("@POSITION", gep.Position));
                    cmd.Parameters.Add(GetDataParameter("@RECUPDATETIMEUTC", trx.TimestampUtc));
                    cmd.Parameters.Add(GetDataParameter("@INSTALLATIONTYPE", gep.InstallationType.ToString()));
                    cmd.Parameters.Add(GetDataParameter("@FACTORYID", gep.Cylinder.FactoryId));
                    cmd.Parameters.Add(GetDataParameter("@PARTNUMBER", gep.Cylinder.PartNumber));
                    cmd.Parameters.Add(GetDataParameter("@PRESSURE", gep.Cylinder.Pressure.ToString()));
                    cmd.Parameters.Add(GetDataParameter("@REFILLDATE", gep.Cylinder.RefillDate));
                    cmd.Parameters.Add(GetDataParameter("@EXPIRATIONDATE", gep.Cylinder.ExpirationDate));

                    return(cmd.ExecuteNonQuery() > 0);
                }
            }
            catch (DataAccessException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new DataAccessException(sql, ex);
            }
        }
        /// <summary>
        /// Throws a FlowFailedException if cylinder is detected as going empty
        /// during the purge.
        /// </summary>
        /// <param name="airEndPoint"></param>
        private void CheckAir(GasEndPoint airEndPoint)
        {
            if (Pump.GetOpenValvePosition() > 0)
            {
                return;
            }

            // For now, we don't bother checking fresh air to be 'empty'.
            // If we did check fresh air, then the port would get marked as 'empty'
            // in the database, and there's currently no way to reset it back to
            // Full except to reboot the docking station.
            //
            // If/when we have mechanism to allow an 'Empty' fresh air port to
            // be 'reset' back to Full, then we should remove this check for IsFreshAir.
            //
            // SEE ALSO: The IsFreshAir check in Pump.CheckFlow's loop.
            //
            // - JMP, 6/30/2011
            if (airEndPoint.Cylinder.IsFreshAir)
            {
                return;
            }

            throw new FlowFailedException(airEndPoint);   // empty
        }
Ejemplo n.º 6
0
        private UnsupportedCylinderAction CreateUnsupportedCylinderAction(GasEndPoint gasEndPoint)
        {
            UnsupportedCylinderAction action = new UnsupportedCylinderAction(gasEndPoint);

            action.Messages.Add(gasEndPoint.Cylinder.PartNumber);
            return(action);
        }
        public DockingStationEvent Execute()
        {
            string funcMsg = Name + ".Execute";

            Log.Debug(funcMsg);

            // We copy the PostUpdate and SettingsRefId from the action to the event so they can
            // be passed on to the followup SettingsRead.  See the EventProcessor.GetFollowupAction method.
            CylinderPressureResetEvent dsEvent = new CylinderPressureResetEvent(this);

            dsEvent.PostUpdate    = this.PostUpdate;
            dsEvent.SettingsRefId = this.SettingsRefId;

            List <GasEndPoint> emptyManGasEndPoints = new List <GasEndPoint>();
            List <GasEndPoint> manGasEndPoints
                = new GasEndPointDataAccess().FindAll().FindAll(m => m.InstallationType == GasEndPoint.Type.Manifold ||
                                                                m.InstallationType == GasEndPoint.Type.Manual);

            // We want to reset low/empty non-iGas cylinders to full.
            for (int position = 1; position <= Configuration.DockingStation.NumGasPorts; position++)
            {
                // We don't want to process (manual) fresh air cylinders on port 1.
                GasEndPoint man = manGasEndPoints.Find(m => m.Position == position && !(m.Position == 1 && m.Cylinder.IsFreshAir));
                if (man != null)
                {
                    Log.Debug(string.Format("{0}Position {1} {2} found (\"{3}\", \"{4}\") with {5} pressure.", LOG_LABEL, position,
                                            man.InstallationType == GasEndPoint.Type.Manifold ? "Manifold" : "Manual Cylinder",
                                            man.Cylinder.FactoryId, man.Cylinder.PartNumber, man.Cylinder.Pressure));

                    if (man.Cylinder.Pressure != PressureLevel.Full)
                    {
                        man.GasChangeType     = GasEndPoint.ChangeType.PressureChanged;
                        man.Cylinder.Pressure = PressureLevel.Full;
                        emptyManGasEndPoints.Add(man);
                    }
                }
            }

            if (emptyManGasEndPoints.Count > 0)
            {
                // Save the modified cylinders in the local database.  The followup SettingsRead with
                // ChangedSmartCards set to null will take care of updating the cylinders in memory.
                using (DataAccessTransaction trx = new DataAccessTransaction())
                {
                    new GasEndPointDataAccess().SaveChangedCylinders(emptyManGasEndPoints, trx);
                    trx.Commit();

                    Log.Debug(string.Format("{0}{1} non-iGas cylinders were reset to full.", LOG_LABEL, emptyManGasEndPoints.Count));
                }
            }
            else
            {
                Log.Debug(string.Format("{0}No manifold or manual cylinders were found that needed reset to full.", LOG_LABEL));
            }

            return(dsEvent);
        }
Ejemplo n.º 8
0
        public DockingStationAction ReportFlowFailedError(GasEndPoint gasEndPoint)
        {
            Log.Debug(string.Format("Empty Cylinder was reported on position {0} during gas operation.", gasEndPoint.Position));

            using (InetUploader inetUploader = new InetUploader())
            {
                DockingStationAction dsAction = ProcessEmptyGasEndPoint(gasEndPoint, inetUploader);
                return(dsAction);
            }
        }
        /// <summary>
        /// Get the bump test gas end point for a sensor.
        /// </summary>
        /// <param name="sensor">The sensor to get the gas for.</param>
        /// <returns>The correct gas end point.</returns>
        /// <exception cref="CorrectBumpTestGasUnavailable">
        /// Thrown when no cylinder is provided for the sensor.
        /// </exception>
        protected GasEndPoint GetSensorGasEndPoint(Sensor sensor)
        {
            #region LogDebug
            if (sensor != null)
            {
                Log.Debug("BumpTest.GetSensorGasEndPoint");
                Log.Debug("Finding appropriate Bump gas for Sensor: " + sensor.Type
                          + ", S/N: " + sensor.Uid
                          + ", CalGas Code: " + sensor.CalibrationGas.Code
                          + ", Conc: " + sensor.CalibrationGasConcentration
                          + ", Measurement: " + ((SensorType)sensor.Type).MeasurementType);
            }
            #endregion

            GasEndPoint endPoint = null;

            Log.Debug("UseExpiredCylinders=" + Configuration.DockingStation.UseExpiredCylinders);

            // If UseExpiredCylinders is true, then we should try and use expired cylinders
            // if there are any. i.e., expired cylinders are "preferred" over non-expired cylinders.
            if (Configuration.DockingStation.UseExpiredCylinders)
            {
                // Get sub-list of available end points that are only the expired cylinders.
                DateTime           localTime    = Configuration.GetLocalTime();
                List <GasEndPoint> gasEndPoints = GasEndPoints.FindAll(gep => gep.Cylinder.ExpirationDate <= localTime);
                Log.Debug(string.Format("Looking for an expired cylinder to use ({0} expired candidates)....", gasEndPoints.Count));
                // See if we can find an appropriate gas to use that's in this expired end points list.
                endPoint = GetSensorGasEndPoint(sensor, gasEndPoints);
                // If we didn't find an expired cylinder to use, we need to see if there's an un-expired cylinder to use.
                if (endPoint == null)
                {
                    gasEndPoints = GasEndPoints.FindAll(gep => gep.Cylinder.ExpirationDate > localTime);
                    Log.Debug(string.Format("No expired cylinder found.  Looking for an unexpired cylinder ({0} unexpired candidates)....", gasEndPoints.Count));
                    endPoint = GetSensorGasEndPoint(sensor, gasEndPoints);
                }
            }
            else
            {
                endPoint = GetSensorGasEndPoint(sensor, GasEndPoints);
            }

            if (endPoint == null)
            {
                Log.Debug("NO APPROPRIATE BUMP GAS FOUND!");
            }

            return(endPoint);
        }
        private void ConstructorInit(PurgeType purgeType, InstrumentController instrumentController, List <GasEndPoint> gasEndPoints, InstrumentGasResponseEvent gasResponseEvent)
        {
            Log.Assert(instrumentController != null, "instrumentController cannot be null");

            _instrumentController = instrumentController;
            _purgeType            = purgeType;

            _returnGasResponseEvent = gasResponseEvent;

            // clone the supplied GasEndPoints
            foreach (GasEndPoint currentGasEndPoint in gasEndPoints)
            {
                GasEndPoint purgeGasEndPoint = (GasEndPoint)currentGasEndPoint.Clone();
                GasEndPoints.Add(purgeGasEndPoint);
            }
        }
        private GasEndPoint MakeInstalledCylinder(IDataReader reader, DataAccessOrdinals ordinals, DataAccessTransaction trx)
        {
            short  position   = SqlSafeGetShort(reader, ordinals["POSITION"]);
            string partNumber = SqlSafeGetString(reader, ordinals["PARTNUMBER"]);

            // Try and get the Factory Cylinder information for the part number.
            // Note that there may not be any factory cylinder info available if the
            // part number is for a new cylinder type that's unknown to to iNet.
            FactoryCylinder factoryCylinder = null;

            if (partNumber != string.Empty)
            {
                factoryCylinder = new FactoryCylinderDataAccess().FindByPartNumber(partNumber, trx);
            }

            Cylinder cylinder;

            if (factoryCylinder != null)
            {
                cylinder = new Cylinder(factoryCylinder);
            }
            else
            {
                cylinder            = new Cylinder();
                cylinder.PartNumber = partNumber;
            }

            string installationTypeString = SqlSafeGetString(reader, ordinals["INSTALLATIONTYPE"]);

            GasEndPoint.Type installationType = (GasEndPoint.Type)Enum.Parse(typeof(GasEndPoint.Type), installationTypeString, true);

            GasEndPoint gep = new GasEndPoint(cylinder, position, installationType);

            gep.Cylinder.FactoryId      = SqlSafeGetString(reader, ordinals["FACTORYID"]);
            gep.Cylinder.ExpirationDate = SqlSafeGetDate(reader, ordinals["EXPIRATIONDATE"]);
            gep.Cylinder.RefillDate     = SqlSafeGetDate(reader, ordinals["REFILLDATE"]);
            string pressure = SqlSafeGetString(reader, ordinals["PRESSURE"]);

            gep.Cylinder.Pressure = (PressureLevel)Enum.Parse(typeof(PressureLevel), pressure, true);

            return(gep);
        }
        /// <summary>
        /// Returns all of the currently known installed cylinders.
        /// </summary>
        /// <param name="trx"></param>
        /// <returns>The list of InstalledCylinders will be sorted by Position</returns>
        public List <GasEndPoint> FindAll(DataAccessTransaction trx)
        {
            List <GasEndPoint> list = new List <GasEndPoint>();

            string sql = "SELECT * FROM GASENDPOINT ORDER BY POSITION";

            using (IDbCommand cmd = GetCommand(sql, trx))
            {
                using (IDataReader reader = cmd.ExecuteReader())
                {
                    DataAccessOrdinals ordinals = new DataAccessOrdinals(reader);
                    while (reader.Read())
                    {
                        GasEndPoint gep = MakeInstalledCylinder(reader, ordinals, trx);
                        list.Add(gep);
                    }
                }
            }
            return(list);
        }
        /// <summary>
        /// Deletes the InstalledCylinder from its specified Position.
        /// </summary>
        /// <param name="gep"></param>
        /// <param name="trx"></param>
        /// <returns></returns>
        public bool Delete(GasEndPoint gep, DataAccessTransaction trx)
        {
            string sql = "DELETE FROM GASENDPOINT WHERE POSITION = @POSITION";

            try
            {
                using (IDbCommand cmd = GetCommand(sql, trx))
                {
                    cmd.Parameters.Add(GetDataParameter("@POSITION", gep.Position));
                    return(cmd.ExecuteNonQuery() > 0);
                }
            }
            catch (DataAccessException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new DataAccessException(sql, ex);
            }
        }
        public bool UpdatePressureLevel(GasEndPoint gep, DataAccessTransaction trx)
        {
            string sql = "UPDATE GASENDPOINT SET PRESSURE = @PRESSURE, RECUPDATETIMEUTC = @RECUPDATETIMEUTC WHERE POSITION = @POSITION";

            try
            {
                using (IDbCommand cmd = GetCommand(sql, trx))
                {
                    cmd.Parameters.Add(GetDataParameter("@POSITION", gep.Position));
                    cmd.Parameters.Add(GetDataParameter("@RECUPDATETIMEUTC", trx.TimestampUtc));
                    cmd.Parameters.Add(GetDataParameter("@PRESSURE", gep.Cylinder.Pressure.ToString()));

                    return(cmd.ExecuteNonQuery() > 0);
                }
            }
            catch (DataAccessException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new DataAccessException(sql, ex);
            }
        }
Ejemplo n.º 15
0
        /// <summary>
        /// </summary>
        /// <remarks>
        /// 1) Updates the cylinder's pressure in the database (change it from Full to Low, or Low to Empty).
        /// 2) Update cached cylinder (in Configuration.DockingStation.InstalledCylinders) with the pressure
        /// change.
        /// 3) Also forces an upload of a SettingsReadEvent in order to notify iNet of the pressure change.
        /// </remarks>
        /// <param name="emptyCylinderError"></param>
        /// <param name="inetUploader"></param>
        /// <returns></returns>
        private DockingStationAction ProcessEmptyGasEndPoint(GasEndPoint emptyEndPoint, InetUploader inetUploader)
        {
            Log.Debug(string.Format("Empty Cylinder was reported on position {0}", emptyEndPoint.Position));

            GasEndPoint gasEndPoint = null;

            using (DataAccessTransaction trx = new DataAccessTransaction())
            {
                GasEndPointDataAccess gepDataAccess = new GasEndPointDataAccess();

                gasEndPoint = gepDataAccess.FindByPosition(emptyEndPoint.Position, trx);

                if (gasEndPoint == null)
                {
                    return(null); // should we display an error?
                }
                if (gasEndPoint.Cylinder.Pressure == PressureLevel.Full)
                {
                    gasEndPoint.Cylinder.Pressure = PressureLevel.Low;
                    Log.Warning("Low pressure warning, position " + emptyEndPoint.Position);
                }
                else
                {
                    gasEndPoint.Cylinder.Pressure = PressureLevel.Empty;
                    Log.Warning("Empty pressure warning, position " + emptyEndPoint.Position);
                }

                Log.Debug(string.Format("Changing pressure to \"{0}\" for cylinder on position {1}", gasEndPoint.Cylinder.Pressure.ToString(), gasEndPoint.Position));
                Log.Debug(string.Format("...PartNumber={0}, FactoryId={1}", gasEndPoint.Cylinder.PartNumber, gasEndPoint.Cylinder.FactoryId));

                gepDataAccess.UpdatePressureLevel(gasEndPoint, trx);

                trx.Commit();
            }

            // After updating the database, we need to update the cached copy of the
            // installed cylinder, too, which is kept in the global DockingStation.
            GasEndPoint cachedGasEndPoint = Configuration.DockingStation.GasEndPoints.Find(g => g.Position == gasEndPoint.Position);

            if (cachedGasEndPoint != null)
            {
                cachedGasEndPoint.Cylinder.Pressure = gasEndPoint.Cylinder.Pressure;
            }

            // We need to inform iNet whenever a cylinder's pressure changes. We can only do this
            // by uploading a full docking station with the cylinder information.  Which means we
            // need to immediately do a SettingsRead to get the full docking station info, and then
            // upload that info along with the currently installed cylinder info.

            SettingsReadOperation settingsReadOperation = new SettingsReadOperation();

            // Explicitly set the ChangedSmartCards to all falses so that no smart cards are read.
            settingsReadOperation.ChangedSmartCards = new bool[Configuration.DockingStation.NumGasPorts];

            SettingsReadEvent settingsReadEvent = (SettingsReadEvent)settingsReadOperation.Execute();

            // Copy currently installed cylinder info to the docking station object we're going to upload.
            settingsReadEvent.DockingStation.GasEndPoints.Clear();
            settingsReadEvent.DockingStation.ChangedGasEndPoints.Clear();

            foreach (GasEndPoint gep in Configuration.DockingStation.GasEndPoints)
            {
                settingsReadEvent.DockingStation.GasEndPoints.Add((GasEndPoint)gep.Clone());
            }

            inetUploader.UploadEvent(settingsReadEvent, Configuration.DockingStation.TimeZoneInfo);

            // BEGIN INS-8630 RHP v7.5
            List <string> consoleMessages = new List <string>();

            consoleMessages.Add(gasEndPoint.Cylinder.Pressure.ToString());

            if (!string.IsNullOrEmpty(gasEndPoint.Cylinder.PartNumber)) // For ISC Pass the Cylinder Part Number
            {
                consoleMessages.Add(gasEndPoint.Cylinder.PartNumber);
            }
            // For Non ISC Pass the gas code. But I believe that both ISC and Non-ISC cylinders have their own PArt numbers.
            // So below condition may not be required ?
            else if (gasEndPoint.Cylinder.GasConcentrations.Count > 0)
            {
                consoleMessages.Add(gasEndPoint.Cylinder.GasConcentrations[0].Type.Symbol);
            }

            Log.Info("Sending IDS ReplaceCylinderAction");
            DockingStationAction dsAction = new ResourceUnavailableAction(new List <string>()
            {
                gasEndPoint.Cylinder.Pressure.ToString()
            }, consoleMessages);

            // END INS-8630

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

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

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

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

            bool instHasAccessoryPump  = false;
            bool isPumpAdapterAttached = false;

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

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

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

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

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

            int PUMP_PURGE_20SECONDS = (instHasAccessoryPump) ? 20 : 0;

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

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

                break;

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

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

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

                break;

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

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

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


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

                break;

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

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

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

                break;

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

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

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

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

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

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

            return(true);
        } // end-Prepare
        /// <summary>
        ///
        /// </summary>
        /// <param name="gasEndPoints"></param>
        /// <param name="changesOnly">
        /// If changesOnly is set, then the passed-in list only contains
        /// changes.  e.g.., if there's been no iGas card insertion/removal on, say,
        /// position 2, then there will be no cylinder in the list at that position.
        /// </param>
        public void SaveInstalledCylinders(List <GasEndPoint> gasEndPoints, DataAccessTransaction trx)
        {
            List <GasEndPoint> persistedListed = FindAll(trx);

            List <GasEndPoint> saveList = new List <GasEndPoint>();

            for (int position = 1; position <= Configuration.DockingStation.NumGasPorts; position++)
            {
                GasEndPoint installed = gasEndPoints.Find(g => g.Position == position);
                GasEndPoint persisted = persistedListed.Find(g => g.Position == position);

                // Is no cylinder on the port, but database thinks there is one?
                // Then assume user has uninstalled the cylinder. We need to remove it from the database
                if (installed == null && persisted != null)    // Not installed?  make sure it's marked as uninstalled in the DB, too.
                {
                    Log.Debug(string.Format("Port {0} (fid=\"{1}\",pn=\"{2}\",{3}) has been uninstalled.",
                                            position, persisted.Cylinder.FactoryId, persisted.Cylinder.PartNumber, persisted.InstallationType));

                    persisted.GasChangeType = GasEndPoint.ChangeType.Uninstalled; // mark it for deletion.
                    saveList.Add(persisted);
                }
                // Was this cylinder not known to be installed but is now installed?
                else if (persisted == null && installed != null)
                {
                    installed.GasChangeType = GasEndPoint.ChangeType.Installed; // mark it for saving.
                    saveList.Add(installed);
                }
                // Was this cylinder already known to be installed?  Then update
                // any data that's changed.
                else if (installed != null && persisted != null)
                {
                    // If anything has changed, then we need to update the cylinder.
                    if (installed.Cylinder.FactoryId != persisted.Cylinder.FactoryId ||
                        installed.Cylinder.ExpirationDate != persisted.Cylinder.ExpirationDate ||
                        installed.Cylinder.Pressure != persisted.Cylinder.Pressure ||
                        installed.Cylinder.PartNumber != persisted.Cylinder.PartNumber)
                    {
                        Log.Debug(string.Format("Port {0} has changed...", position));
                        Log.Debug(string.Format("......fid=\"{0}\", pn=\"{1}\", {2}, {3}, {4}",
                                                installed.Cylinder.FactoryId, installed.Cylinder.PartNumber, installed.InstallationType,
                                                Log.DateTimeToString(installed.Cylinder.ExpirationDate), installed.Cylinder.Pressure));

                        installed.GasChangeType = GasEndPoint.ChangeType.Installed; // mark it for saving.
                        saveList.Add(installed);
                    }
                    else
                    {
                        Log.Debug(string.Format("Port {0} has not changed. (fid=\"{1}\",pn=\"{2}\",{3})",
                                                position, persisted.Cylinder.FactoryId, persisted.Cylinder.PartNumber, persisted.InstallationType));
                    }
                }
                else if (installed == null && persisted == null)
                {
                    Log.Debug(string.Format("Port {0}: Nothing installed.", position));
                }
            }

            SaveGasEndPoints(saveList, trx);

            return;
        }
        /// <summary>
        /// Return the information from all smart cards that are present.
        /// </summary>
        /// <returns>All of the installed cylinders.</returns>
        static private void ReadChangedCylinders(bool[] changedSmartCards, IList <GasEndPoint> changedGasEndPoints, PortRestrictions port1Restrictions)
        {
            const string funcName = "ReadChangedCylinders: ";

            Log.Assert(changedSmartCards != null, funcName + "changedSmartCards should not be null.");

            DockingStation dockingStation = Controller.GetDockingStation(); // SGF  8-Nov-2012  INS-2686

            changedGasEndPoints.Clear();

            for (int i = 0; i < changedSmartCards.Length; i++)
            {
                int position = i + 1;

                Log.Debug(funcName + "POSITION " + position);

                if (changedSmartCards[position - 1] == false)     // No detection of a card insertion, nor a removal?
                {
                    Log.Debug(string.Format("{0}Position {1} Smart card SKIPPED; No insertion change detected.", funcName, position));

                    if (position == Controller.FRESH_AIR_GAS_PORT)
                    {
                        GasEndPoint persistedPort1GasEndPoint = new GasEndPointDataAccess().FindByPosition(Controller.FRESH_AIR_GAS_PORT);

                        // If there's nothing known to be installed on port 1, then create a fresh air
                        // cylinder for the port.
                        // This could happen if, while nothing was installed on the port, a SettingsUpdate
                        // just occurred previously where the port setting was changed to allow fresh air.
                        // In that situation, we have to then make the fresh air cylinder.
                        if (persistedPort1GasEndPoint == null)
                        {
                            Log.Debug(string.Format("{0}Position {1}, No persisted cylinder; assuming Fresh Air.", funcName, position));
                            GasEndPoint freshAirCylinder = GasEndPoint.CreateFreshAir(position);
                            freshAirCylinder.GasChangeType = GasEndPoint.ChangeType.Installed;
                            changedGasEndPoints.Add(freshAirCylinder);
                        }
                    }

                    // SGF  8-Nov-2012  INS-2686 -- begin
                    // Soon, we will check to see if a pressure switch is present, and if so, read the pressure level.
                    // Before we do that, we must check for two scenarios in which it is not necessary or appropriate
                    // to read a pressure switch.
                    //     1. If there is no cylinder attached to the port, there is no reason to check for a pressure switch.
                    //     2. If this is the first port, and we know that the port is drawing fresh air, there is no reason
                    //        to check for a pressure switch.  This case cannot be handled by case #1, as we define a "logical"
                    //        cylinder to represent fresh air.
                    // If we find either scenario for the current port, we skip further processing on this port, and proceed to
                    // the next one.
                    GasEndPoint currentInstalledCylinder = dockingStation.GasEndPoints.Find(ic => ic.Position == position);
                    if (currentInstalledCylinder == null)
                    {
                        Log.Debug(string.Format("{0}Position {1} No cylinder present; do not check for pressure switch.", funcName, position));
                        continue;
                    }
                    else
                    {
                        bool isFreshAir = currentInstalledCylinder.Cylinder.IsFreshAir;
                        if (isFreshAir)
                        {
                            Log.Debug(string.Format("{0}Position {1} Fresh air; do not check for pressure switch.", funcName, position));
                            continue;
                        }
                    }
                    // SGF  8-Nov-2012  INS-2686 -- end

                    // SMARTCARD NOT CHANGED (NO INSERT OR REMOVAL DETECTED).
                    // WE NEED TO AT LEAST ALWAYS READ THE PRESSURE SWITCH THEN.
                    if (SmartCardManager.IsPressureSwitchPresent(position))
                    {
                        GasEndPoint pressureCylinder = new GasEndPoint();
                        pressureCylinder.Position          = position;
                        pressureCylinder.Cylinder.Pressure = ReadPressureLevel(position);
                        pressureCylinder.GasChangeType     = GasEndPoint.ChangeType.PressureChanged;

                        Log.Debug(string.Format("{0}Position {1} Pressure Switch reports {2}.", funcName, position, pressureCylinder.Cylinder.Pressure));

                        changedGasEndPoints.Add(pressureCylinder);
                    }
                    else
                    {
                        Log.Debug(string.Format("{0}Position {1} Pressure Switch not present.", funcName, position));
                    }

                    continue;
                }

                // IF WE MAKE IT TO HERE, THE CARD HAS BEEN EITHER INSERTED OR REMOVED.

                if (!SmartCardManager.IsCardPresent(position))      // CARD REMOVED?
                {
                    Log.Debug(string.Format("{0}Position {1} SmartCard not present, Returning CardRemoved", funcName, position));

                    // Server needs to know specifically that cylinder is missing.
                    // Indicate this with an InstalledCylinder containing an empty Cylinder object.
                    GasEndPoint missingCylinder = new GasEndPoint();
                    missingCylinder.Position      = position;
                    missingCylinder.GasChangeType = GasEndPoint.ChangeType.Uninstalled;
                    changedGasEndPoints.Add(missingCylinder);

                    // If a cylinder is not installed on the fresh air port, then assume fresh air
                    // for the port
                    if (position == Controller.FRESH_AIR_GAS_PORT)
                    {
                        Log.Debug(string.Format("{0}Position {1} is assumed to be Fresh Air.", funcName, position));
                        GasEndPoint freshAirCylinder = GasEndPoint.CreateFreshAir(position);
                        freshAirCylinder.GasChangeType = GasEndPoint.ChangeType.Installed;
                        changedGasEndPoints.Add(freshAirCylinder);
                    }

                    continue;
                }

                // IF WE MAKE IT TO HERE, THE CARD HAS BEEN INSERTED.

                Cylinder cylinder = SmartCardManager.ReadCard(position);
                if (cylinder == null)    // Couldn't read valid cylinder?  Driver error or corrupt card.
                {
                    Log.Debug(string.Format("{0}Position {1}, ReadCard returned null. SKIPPING cylinder.", funcName, position));
                    continue;
                }

                // Dates read from card will be in 'local' time, but everything we deal with is in UTC.
                cylinder.ExpirationDate = Configuration.ToUniversalTime(cylinder.ExpirationDate);
                cylinder.RefillDate     = Configuration.ToUniversalTime(cylinder.RefillDate);

                Thread.Sleep(1000);

                cylinder.Pressure = ReadPressureLevel(position);

                GasEndPoint gasEndPoint = new GasEndPoint(cylinder, position, GasEndPoint.Type.iGas);
                gasEndPoint.GasChangeType = GasEndPoint.ChangeType.Installed;
                Log.Debug(string.Format("{0}Position {1}, Returning CardInserted. Pressure={2}", funcName, position, cylinder.Pressure));

                changedGasEndPoints.Add((GasEndPoint)gasEndPoint.Clone());
            }  // end-for

            return;
        }
Ejemplo n.º 19
0
        internal List <GasEndPoint> GetGasEndPoints(string eventCode, DockingStationAction dsAction, StringBuilder explanation, List <string> explanationCodes, List <string> errorCodes)
        {
            Log.Debug(string.Format("{0}.GetGasEndPoints, {1}", Name, eventCode));
            //explanationCodes.Clear();
            //errorCodes.Clear(); // SGF  20-Feb-2013  INS-3821

            ISC.iNet.DS.DomainModel.Instrument dockedInstrument = Master.Instance.SwitchService.Instrument;
            DockingStation dockingStation = Configuration.DockingStation;

            List <GasEndPoint> gasEndPoints = new List <GasEndPoint>();

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

                Sensor sensor = (Sensor)installedComponent.Component;
                if (!sensor.Enabled)
                {
                    Log.Info(string.Format("{0}: Ignoring disabled sensor {1}", Name, sensor.Uid));
                    continue;
                }

                // SGF  21-May-2012  INS-3078 -- Comment out the following if statement
                //if (!GasOperationsSupported(sensor))
                //{
                //    Log.Debug( string.Format( "{0}.GasOperationsSupported returned False for sensor {1}. Ignoring sensor.", Name, sensor.SerialNumber ) );
                //    continue;
                //}

                if (sensor.CalibrationGas.Code.Length == 0)
                {
                    throw new ApplicationException("Sensor " + sensor.Uid + " has null calibration gas code.");
                }

                // SGF  03-Nov-2010  Single Sensor Cal and Bump
                if (dsAction is InstrumentGasAction)
                {
                    InstrumentGasAction gasAction = (InstrumentGasAction)dsAction;
                    if (gasAction.ComponentCodes.Count != 0 && !gasAction.ComponentCodes.Contains(sensor.Type.Code))
                    {
                        Log.Debug(string.Format("{0}: Component type {1} is not included in the defined list of components to test. Ignoring sensor.", Name, sensor.Type.Code));
                        continue;
                    }
                }

                Log.Debug(string.Format("{0}: Looking for sensor {1}'s cal gas ({2})", Name, sensor.Uid, sensor.CalibrationGas.Code));

                _empty     = _expired = _gasFound = _freshFound = _zeroFound = false;
                _gasNeeded = true;  // SGF  21-May-2012  INS-3078

                // INS-8630 RHP v7.5 clear the messages for every installed component to avoid confusion
                explanationCodes.Clear();
                errorCodes.Clear(); // SGF  20-Feb-2013  INS-3821

                // Loop thru the cylinders for the docking station and if the cylinder contains the gas that
                // the sensor needs, add that cylinder as a gas end point in the docking station action.
                FindInstalledCylinderGases(eventCode, gasEndPoints, dockingStation, installedComponent, explanation, explanationCodes, errorCodes);

                if (!_freshFound && !_zeroFound)
                {
                    // Present which type of air should be, but is not, available.  If the port1 restrictions
                    // only allow for zero air, present 'ZERO AIR'; otherwise, present 'FRESH AIR'.
                    if (Configuration.DockingStation.Port1Restrictions == PortRestrictions.ZeroAir)
                    {
                        explanationCodes.Add("ZEROAIR");
                        errorCodes.Add(string.Format("{0} ({1})", "ZEROAIR", GasCode.O2));     // SGF  20-Feb-2013  INS-3821
                    }
                    else if (Configuration.DockingStation.Port1Restrictions == PortRestrictions.FreshAir)
                    {
                        GasType gasType = GasType.Cache[GasCode.FreshAir];
                        if (gasType != null)
                        {
                            explanationCodes.Add(gasType.Symbol);
                            errorCodes.Add(string.Format("{0} ({1})", gasType.Symbol, gasType.Code));     // SGF  20-Feb-2013  INS-3821
                        }
                    }

                    else // SGF  19-Jan-2012  INS-1913 & INS-1914
                    {
                        // either fresh air or zero air is allowed; present which type is connected, if something is connected.
                        GasEndPoint gasEndPoint = dockingStation.GasEndPoints[0];
                        Cylinder    cyl         = gasEndPoint.Cylinder;
                        if (cyl.IsZeroAir)
                        {
                            explanationCodes.Add("ZEROAIR");
                            errorCodes.Add(string.Format("{0} ({1})", "ZEROAIR", GasCode.O2));     // SGF  20-Feb-2013  INS-3821
                        }
                        else //suresh 14-Mar-2012 INS-4427 (DEV)
                        {
                            // If port 1 cylinder is not Zero Air then we report that 'Fresh air' is unavailable
                            GasType gasType = GasType.Cache[GasCode.FreshAir];
                            if (gasType != null)
                            {
                                explanationCodes.Add(gasType.Symbol);
                                errorCodes.Add(string.Format("{0} ({1})", gasType.Symbol, gasType.Code));     // SGF  20-Feb-2013  INS-3821
                            }
                        }
                    }

                    if (_expired)
                    {
                        explanationCodes.Add("Expired");
                        errorCodes.Add("Expired");  // INS-8630 RHP v7.5 - Notify iNet on the expired state
                    }
                    else if (_empty)
                    {
                        explanationCodes.Add(PressureLevel.Empty.ToString());
                        errorCodes.Add(PressureLevel.Empty.ToString());     // INS-8630 RHP v7.5 - Notify iNet on the empty state
                    }
                    explanation.Append("Fresh air not found for sensor " + sensor.Uid + '\n');

                    Log.Debug(string.Format("{0}: Returning nothing: gasFound={1}, freshFound={2}, expired={3}, empty={4}", Name, _gasFound, _freshFound, _expired, _empty));
                    return(new List <GasEndPoint>());
                }

                if (_gasNeeded && !_gasFound)    // SGF  21-May-2012  INS-3078 -- add the '_gasNeeded' clause to the if-condition
                {
                    // If gas not found, IDS needs the symbol for that gas for display on its LCD.
                    // Look it up in our cache.  For Fresh Air, we just return the gas code;
                    // The IDS knows to look for that as a special case.
                    if (sensor.CalibrationGas.Code == GasCode.FreshAir)
                    {
                        GasType gasType = GasType.Cache[GasCode.FreshAir];
                        if (gasType != null)
                        {
                            explanationCodes.Add(gasType.Symbol);
                            errorCodes.Add(string.Format("{0} ({1})", gasType.Symbol, gasType.Code));     // SGF  20-Feb-2013  INS-3821
                        }
                    }
                    // DSW-1758 RHP v9.6.1 - Added (! (_expired || _empty) ) since gas symbol has already been added to explanationCodes for Empty/Expired states.
                    else if (!(_expired || _empty))
                    {
                        // 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 make sure we report that the
                        // gas type not found is the CombustibleBumpTestGas and not the sensor cal gas.
                        string sensorGasCode = sensor.CalibrationGas.Code;
                        if ((eventCode == EventCode.BumpTest) &&
                            (sensor.Type.Code == SensorCode.CombustibleLEL || sensor.Type.Code == SensorCode.CombustiblePPM) &&
                            (Configuration.DockingStation.CombustibleBumpTestGas.Length > 0))
                        {
                            sensorGasCode = Configuration.DockingStation.CombustibleBumpTestGas;
                        }
                        GasType gasType = GasType.Cache[sensorGasCode];
                        if (gasType != null)
                        {
                            explanationCodes.Add(gasType.Symbol);
                            errorCodes.Add(string.Format("{0} ({1})", gasType.Symbol, gasType.Code));     // SGF  20-Feb-2013  INS-3821
                        }
                    }

                    if (_expired)
                    {
                        explanationCodes.Add("Expired");
                        errorCodes.Add("Expired");  // INS-8630 RHP v7.5 - Notify iNet on the expired state
                    }
                    else if (_empty)
                    {
                        explanationCodes.Add(PressureLevel.Empty.ToString());
                        errorCodes.Add(PressureLevel.Empty.ToString());     // INS-8630 RHP v7.5 - Notify iNet on the empty state
                    }

                    explanation.Append("Could not find cylinder needed for sensor " + sensor.Uid + ", CalGasCode=\"" + sensor.CalibrationGas.Code + "\" (");
                    for (int i = 0; i < explanationCodes.Count; i++)
                    {
                        if (i > 0)
                        {
                            explanation.Append(" ");
                        }
                        explanation.Append(explanationCodes[i]);
                    }
                    explanation.Append(")\n");

                    Log.Debug(string.Format("{0}: Returning nothing: gasFound={1}, freshFound={2}, expired={3}, empty={4}", Name, _gasFound, _freshFound, _expired, _empty));
                    return(new List <GasEndPoint>());
                }

                // Zero air is required for CO2 sensors; Only zero air is used to zero CO2, never fresh air.
                if ((sensor.CalibrationGas.Code == GasCode.CO2) && !_zeroFound)
                {
                    GasType gasType = GasType.Cache[GasCode.O2];
                    if (gasType != null)
                    {
                        explanationCodes.Add("ZEROAIR");                                   // SGF  5-Feb-2013  INS-3837
                        errorCodes.Add(string.Format("{0} ({1})", "ZEROAIR", GasCode.O2)); // SGF  20-Feb-2013  INS-3821
                    }
                    if (_expired)
                    {
                        explanationCodes.Add("Expired");
                    }
                    else if (_empty)
                    {
                        explanationCodes.Add(PressureLevel.Empty.ToString());
                    }
                    explanation.Append("Zero air not found for CO2 sensor " + sensor.Uid + '\n');
                    Log.Debug(string.Format("{0}: Returning nothing: gasFound={1}, freshFound={2}, expired={3}, empty={4}", Name, _gasFound, _freshFound, _expired, _empty));
                    return(new List <GasEndPoint>());;
                }
            }

            Log.Debug(string.Format("{0}.GetGasEndPoints returned {1} gas end points", Name, gasEndPoints.Count));
            return(gasEndPoints);
        }
Ejemplo n.º 20
0
 public virtual void OpenGasEndPoint(GasEndPoint endPoint)
 {
     Pump.OpenGasEndPoint(endPoint);
 }
Ejemplo n.º 21
0
        /// <summary>
        /// Check if pump works properly.
        /// </summary>
        /// <remarks>
        /// Engineering Notes: This step is to check if pump works properly.
        /// Run pump at two different control voltages, check the flow rate difference between these two cases.
        /// </remarks>
        /// <param name="details">The string to hold details.</param>
        private void TestPump(DetailsBuilder details)
        {
            // Find a port without a gas cylinder connected. 
            // If all ports are connected, skip this test.
            DockingStation ds = Controller.GetDockingStation();

            // First, see if port one is unconnected or is providing FRESH AIR
            // Note: we are trying ports in the order of 3-2-1 so as to avoid pulling air through the filter, if possible.
            int testPort = -1;
            GasEndPoint gasEndPoint = null;
            for ( int solenoid = Configuration.DockingStation.NumGasPorts; solenoid >= 1 && testPort <= 0; solenoid-- )
            {
                gasEndPoint = ds.GasEndPoints.Find(m => m.Position == solenoid);
                if (gasEndPoint == null || gasEndPoint.Cylinder.IsFreshAir)
                    testPort = solenoid;
            }

            if (testPort <= 0)
            {
                Log.Debug("TestPump:  could not find open solenoid; this test will be skipped.");
                return;
            }

            // Open solenoid valve determined to be unconnected
            Pump.CloseAllValves( true );
            Thread.Sleep(500); // Give the valves a chance to finish closing
            Pump.OpenValve(testPort, false);

            // Start pump with pump voltage of 80
            Pump.Start(80);

            // Wait 3 seconds
            Thread.Sleep(3000);

            // Read flow 1
            ushort flowCount1 = Pump.GetRawFlow();
            ushort flowVolts1 = Pump.ConvertRawFlowToVolts( flowCount1 );
            ushort vacuumCounts1 = Pump.GetRawVacuum();

            int flowRate1 = Pump.CalculateFlowRate( flowVolts1, vacuumCounts1 );
            string flowString1 = BuildFlowString(flowCount1, flowRate1, flowVolts1);

            // Increase pump voltage to 240
            Pump.SetNewPumpVoltage(240);

            // Wait 3 seconds
            Thread.Sleep(3000);

            // Check Pump Error status
            int pumpErrorState = Pump.GetPumpErrorState();

            // Fail if state is 1
            _gdpList.Add(new GeneralDiagnosticProperty("DIAGNOSTIC_PUMP_ERROR_STATUS", pumpErrorState.ToString()));
            ReportDiagnostic(details, DiagnosticResources.PUMP_ERROR_STATUS, pumpErrorState.ToString(), (pumpErrorState == 1));

            // Read flow 2
            ushort flowCount2 = Pump.GetRawFlow();
            ushort flowVolts2 = Pump.ConvertRawFlowToVolts( flowCount2 );
            ushort vacuumCounts2 = Pump.GetRawVacuum();
            int flowRate2 = Pump.CalculateFlowRate( flowVolts2, vacuumCounts2 );
            string flowString2 = BuildFlowString(flowCount2, flowRate2, flowVolts2);

            // Fail if f2 - f1 < 100 OR f2 - f1 > 450
            _gdpList.Add(new GeneralDiagnosticProperty("DIAGNOSTIC_PUMP_F1", flowCount1.ToString()));
            _gdpList.Add(new GeneralDiagnosticProperty("DIAGNOSTIC_PUMP_F2", flowCount2.ToString()));
            ReportDiagnostic(details, DiagnosticResources.PUMP, flowString1, flowString2, ( flowCount2 - flowCount1 < 100 ) || ( flowCount2 - flowCount1 > 450 ) );

            // Stop the pump and close the port used for this test
            Pump.CloseValve(testPort);
            Thread.Sleep(500);
        }
Ejemplo n.º 22
0
		/// <summary>
		/// Test the specified solenoid
		/// </summary>
		/// <param name="details">The string to hold details.</param>
		private void TestFlow( DetailsBuilder details, int solenoid )
        {
            // Validate the solenoid number
            if ( solenoid < 1 || solenoid > Configuration.DockingStation.NumGasPorts )
            {
                Log.Debug( "TestFlow:  Invalid solenoid value = " + solenoid.ToString() );
                return;
            }

            Log.Debug( "TestFlow: port=" + solenoid.ToString() );

            // Determine whether a cylinder is attached to this port.  
            // If there is one attached, skip this test.
            DockingStation ds = Controller.GetDockingStation();
            GasEndPoint gasEndPoint = ds.GasEndPoints.Find(m => m.Position == solenoid);
            if (gasEndPoint != null && gasEndPoint.Cylinder.IsFreshAir == false)
            {
                Log.Debug( "TestFlow: Cylinder attached to port " + solenoid.ToString() + "; SKIPPING THIS TEST." );
                return;
            }

            Pump.DoCheckFlow = true;

            // Ensure that only the specified solenoid is open.
            Pump.CloseAllValves( true );
            Thread.Sleep(500); // Give the valves a chance to finish closing
            Pump.OpenValve(solenoid, false); // Open the specified solenoid
            Thread.Sleep(500); // Pause at least 500ms.

            Pump.SetDesiredFlow( Pump.StandardFlowRate);
            Pump.Start( Pump.StandardStartVoltage );  // Turn on the pump.

            Thread.Sleep( 3000 ); // Wait for it to stabilize the flow before letting CheckFlow take its first reading.

            // CheckFlow could enter an infinite loop if it's unable to 
            // establish the desired flow rate and we don't tell it to time out.
            // We therefore give it a time out of a minute which should be more than sufficient.


            ushort rawFlowCounts;
            ushort rawVacuumCounts;
            Pump.FlowStatus flowStatus = Pump.CheckFlow( new TimeSpan( 0, 1, 0 ), out rawFlowCounts, out rawVacuumCounts );

            byte pumpVoltage = Pump.GetPumpVoltage(); // obtain and hold onto final voltage of the pump, to report it to inet.

            // Get the flow rate.
            ushort flowVolts = Pump.ConvertRawFlowToVolts( rawFlowCounts );

            int flowRate = Pump.CalculateFlowRate( flowVolts, rawVacuumCounts );  // Convert that value to mL/min

            // Report the results.
            string flowString = BuildFlowString(flowRate, flowVolts);
            // We create a property for every value used to compute the flow rate, and also the flow rate itself.
            _gdpList.Add( new GeneralDiagnosticProperty( "DIAGNOSTIC_CHECK_FLOW_" + solenoid + "_VACUUM", rawVacuumCounts.ToString() ) );
            _gdpList.Add( new GeneralDiagnosticProperty( "DIAGNOSTIC_CHECK_FLOW_" + solenoid + "_VACUUM_INCHES", Pump.ConvertRawVacuumToInches( rawVacuumCounts ).ToString() ) );
            _gdpList.Add( new GeneralDiagnosticProperty( "DIAGNOSTIC_CHECK_FLOW_" + solenoid, rawFlowCounts.ToString() ) );
            _gdpList.Add( new GeneralDiagnosticProperty( "DIAGNOSTIC_CHECK_FLOW_" + solenoid + "_VOLTS", flowVolts.ToString() ) );
            _gdpList.Add( new GeneralDiagnosticProperty( "DIAGNOSTIC_CHECK_FLOW_" + solenoid + "_RATE", flowRate.ToString() ) );
            _gdpList.Add( new GeneralDiagnosticProperty( "DIAGNOSTIC_CHECK_FLOW_" + solenoid + "_PUMP_VOLTS", pumpVoltage.ToString() ) );
            // The flow is considered a failure if it's not equal to the StandardFlowRate plus/minus the standardtolerance
            bool flowFailed = flowRate < ( Pump.StandardFlowRate - Pump.FLOWRATE_TOLERANCE ) || flowRate > ( Pump.StandardFlowRate + Pump.FLOWRATE_TOLERANCE );
            // TODO - we should rename the translation string so that it's prefixed with "CHECK_FLOW" instead of "SOLENOID_FLOW"
            ReportDiagnostic( details, details.GetText( "SOLENOID_FLOW_" + solenoid ), flowString, flowFailed );

            // Check Pump Error Status -- FAIL IF STATE IS 1
            int pumpErrorState = Pump.GetPumpErrorState();

            // Report the results.
            _gdpList.Add( new GeneralDiagnosticProperty( "DIAGNOSTIC_CHECK_FLOW_" + solenoid + "_PUMP_ERROR", pumpErrorState.ToString() ) );
            // TODO - we should rename the translation string so that it's prefixed with "CHECK_FLOW" instead of "SOLENOID_FLOW"
            ReportDiagnostic(details, details.GetText("SOLENOID_PUMP_ERROR_" + solenoid), pumpErrorState.ToString(), (pumpErrorState == 1));

            // Stop the pump and close the solenoid
            Pump.CloseValve(solenoid);

            Pump.DoCheckFlow = false;
        }
Ejemplo n.º 23
0
        private DockingStationAction ExamineGasEndPoints()
        {
            // First, for each attached cylinder, make sure it's a valid part number.
            // We try to not hit the database over and over to do this check
            // The GasEndPoints.Supported property helps us do that. If it's null,
            // then we've not checked the cylinder, so we query the database to do so.
            // We then set it to true or false appropiately.  Once all GasEndPoints' Supported
            // properties are non-null, we don't have to hit the database anymore.
            // Note that there's no need to check the 'part number' for fresh air.

            List <GasEndPoint> unverifiedGasEndPoints
                = Configuration.DockingStation.GasEndPoints.FindAll(g => g.Supported == null && g.Cylinder.PartNumber != FactoryCylinder.FRESH_AIR_PART_NUMBER);

            if (unverifiedGasEndPoints.Count > 0)
            {
                // Next, for every currently installed cylinder, verify that we recognize
                // the part number.  We check because the user may attach a cylinder
                // that has a new part number not yet known to iNet.
                FactoryCylinderDataAccess fcDataAccess = new FactoryCylinderDataAccess();

                using (DataAccessTransaction trx = new DataAccessTransaction(true))
                {
                    foreach (GasEndPoint g in unverifiedGasEndPoints)
                    {
                        g.Supported = fcDataAccess.FindByPartNumber(g.Cylinder.PartNumber, trx) != null;

                        Log.Debug(string.Format("Verified GasEndPoint {0} on port {1}. Supported={2}", g.Cylinder.PartNumber, g.Position, g.Supported));

                        if (g.Supported == false)   // no match found? Must be an unknown part number.
                        {
                            return(CreateUnsupportedCylinderAction(g));
                        }
                    }
                }
            }

            // At this point, we expect that cylinders have been verified.
            // We now check to see that each cylinder is supported by checking the
            // InstalledCylinder.Supported property.
            List <GasEndPoint> unsupportedGasEndPoints = Configuration.DockingStation.GasEndPoints.FindAll(g => g.Supported != null && g.Supported == false);

            if (unsupportedGasEndPoints.Count > 0)
            {
                GasEndPoint gep = unsupportedGasEndPoints[0];
                Log.Debug(string.Format("GasEndPoint {0} on port {1}. Supported={2}", gep.Cylinder.PartNumber, gep.Position, gep.Supported));
                return(CreateUnsupportedCylinderAction(gep));
            }

            // Next, verify that the cylinder on port 1 is legal for whatever the
            // current Fresh/Zero air restriction for that port.

            GasEndPoint gasEndPoint = Configuration.DockingStation.GasEndPoints.Find(g => g.Position == 1);

            // Either zero air OR fresh air is allowed?
            if (Configuration.DockingStation.Port1Restrictions == (PortRestrictions.FreshAir | PortRestrictions.ZeroAir))
            {
                if (gasEndPoint != null && !gasEndPoint.Cylinder.IsFreshAir && !gasEndPoint.Cylinder.IsZeroAir)
                {
                    Log.Debug(string.Format("Port1Restriction={0}, but cylinder is {1}", Configuration.DockingStation.Port1Restrictions.ToString(), gasEndPoint.Cylinder.PartNumber));
                    return(CreateUnsupportedCylinderAction(gasEndPoint));
                }
            }

            // Only fresh air is allowed?  It's illegal to have a non-freshAir cylinder is installed.
            if (Configuration.DockingStation.Port1Restrictions == PortRestrictions.FreshAir)
            {
                if (gasEndPoint != null && !gasEndPoint.Cylinder.IsFreshAir)
                {
                    Log.Debug(string.Format("Port1Restriction is FreshAir, but cylinder is {0}", gasEndPoint.Cylinder.PartNumber));
                    return(CreateUnsupportedCylinderAction(gasEndPoint));
                }
            }

            // Only zero air is allowed? It's illegal to have either fresh air or a non-zeroAir
            // cylinder installed.
            else if (Configuration.DockingStation.Port1Restrictions == PortRestrictions.ZeroAir)
            {
                // It's required that we have a zero-air cylinder on port1.  So return
                // Unsupported gas if there is no cylinder, or if the cylinder is not zero-air.
                if (gasEndPoint == null)
                {
                    Log.Debug("Port1Restriction is ZeroAir, but no cylinder is installed");
                    return(CreateUnsupportedCylinderAction(GasEndPoint.CreateFreshAir(Controller.FRESH_AIR_GAS_PORT)));
                }

                if (!gasEndPoint.Cylinder.IsZeroAir)
                {
                    Log.Debug(string.Format("Port1Restriction is ZeroAir, but cylinder is {0}", gasEndPoint.Cylinder.PartNumber));
                    return(CreateUnsupportedCylinderAction(gasEndPoint));
                }
            }

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

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

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

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

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

		GasEndPoint zeroEndPoint = null;

        DateTime startTime = DateTime.UtcNow;

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

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

            _instrumentController.OpenGasEndPoint( zeroEndPoint, Pump.StandardFlowRate );

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

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

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

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

            _instrumentController.CloseGasEndPoint( zeroEndPoint );

            if ( zeroEndPoint != null )  // how could this ever be null?
                _zeroingUsedGasEndPoint = new UsedGasEndPoint( zeroEndPoint, CylinderUsage.Zero, DateTime.UtcNow - startTime, 0 );
        }
    }
Ejemplo n.º 26
0
 public virtual void CloseGasEndPoint(GasEndPoint endPoint)
 {
     Pump.CloseGasEndPoint(endPoint);
 }
        /// <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);
        }
    /// <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>
        /// Return the information from all smart cards, manifolds and manual cylinders that are present at start-up.
        /// </summary>
        /// <param name="installedCylinders">Information about the cylinders is placed into this passed-in list.</param>
        static private void ReadInstalledCylinders(List <GasEndPoint> gasEndPoints, PortRestrictions port1Restrictions)
        {
            const string funcName = "ReadInstalledCylinders: ";

            // Get all currently attached manifolds and manually-assigned cylinders.
            List <GasEndPoint> manGasEndPoints
                = new GasEndPointDataAccess().FindAll().FindAll(m => m.InstallationType == GasEndPoint.Type.Manifold ||
                                                                m.InstallationType == GasEndPoint.Type.Manual);

            for (int position = 1; position <= Configuration.DockingStation.NumGasPorts; position++)
            {
                Log.Debug(funcName + "POSITION " + position);

                // iGas cylinders take precendence
                if (!SmartCardManager.IsCardPresent(position))
                {
                    Log.Debug(string.Format("{0}Position {1}, No iGas card detected.", funcName, position));

                    // Does the port have a manifold or manual cylinder attached?  Then make sure we include
                    // that cylinder in the returned list. If no cylinder exists on port 1, then create a
                    // virtual fresh air cylinder.
                    GasEndPoint man = manGasEndPoints.Find(m => m.Position == position);
                    if (man != null)
                    {
                        Log.Debug(string.Format("{0}Position {1} {2} found (\"{3}\", \"{4}\", Pressure {5}).", funcName, position,
                                                man.InstallationType == GasEndPoint.Type.Manifold ? "Manifold" : "Manual Cylinder",
                                                man.Cylinder.FactoryId, man.Cylinder.PartNumber, man.Cylinder.Pressure));
                        gasEndPoints.Add(man);
                    }
                    else if (position == Controller.FRESH_AIR_GAS_PORT)
                    {
                        Log.Debug(string.Format("{0}Position {1} is assumed to be Fresh Air.", funcName, position));
                        GasEndPoint freshAirEndPoint = GasEndPoint.CreateFreshAir(position);
                        freshAirEndPoint.GasChangeType = GasEndPoint.ChangeType.Installed;
                        gasEndPoints.Add(freshAirEndPoint);
                    }
                    continue;
                }

                // IF WE MAKE IT TO HERE, THEN WE KNOW WE HAVE AN INSERTED SMART CARD WHICH MEANS iGas IS ATTACHED.

                Cylinder cylinder = SmartCardManager.ReadCard(position);
                if (cylinder == null)   // Check for a valid cylinder.
                {
                    Log.Debug(string.Format("{0}Position {1}, ReadCard returned null. SKIPPING cylinder.", funcName, position));
                    continue;
                }

                // Dates read from card will be in 'local' time, but everything we deal with is in UTC.
                cylinder.ExpirationDate = Configuration.ToUniversalTime(cylinder.ExpirationDate);
                cylinder.RefillDate     = Configuration.ToUniversalTime(cylinder.RefillDate);

                Thread.Sleep(1000);

                if (SmartCardManager.IsPressureSwitchPresent(position))
                {
                    if (SmartCardManager.CheckPressureSwitch(position))
                    {
                        cylinder.Pressure = PressureLevel.Full;
                    }
                    else
                    {
                        cylinder.Pressure = PressureLevel.Low;
                    }

                    Log.Debug(string.Format("{0}Position {1} Pressure Switch reports {2}.", funcName, position, cylinder.Pressure));
                }
                else
                {
                    Log.Debug(string.Format("{0}Position {1} Pressure Switch not detected.", funcName, position));
                }

                GasEndPoint gasEndPoint = new GasEndPoint(cylinder, position, GasEndPoint.Type.iGas);

                // Add the installed cylinder to the DockingStation (IDS).
                gasEndPoints.Add(gasEndPoint);
            }

            return;
        }