Esempio n. 1
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException("cfr");
            }

            // No training devices for sport pilots
            if (!cfr.fIsRealAircraft)
            {
                return;
            }

            // Minimum time can be in anything
            miMinTime.AddEvent(cfr.Total);

            // Everything else must be in matching category/class
            // allow perfect match or ASEL->ASES match
            if (CatClassID != cfr.idCatClassOverride && !(CatClassID == CategoryClass.CatClassID.ASEL && cfr.idCatClassOverride == CategoryClass.CatClassID.ASES))
            {
                return;
            }

            miMinInstruction.AddEvent(cfr.Dual);
            decimal soloTime = 0.0M;

            cfr.ForEachEvent(pf => { if (pf.PropertyType.IsSolo)
                                     {
                                         soloTime += pf.DecValue;
                                     }
                             });
            miMinSolo.AddEvent(soloTime);

            int cFSLandings = cfr.cFullStopLandings + cfr.cFullStopNightLandings;

            miMinCrossCountry.AddEvent(Math.Min(cfr.XC, cfr.Dual));
            miMinLandings.AddEvent(cFSLandings);
            if (soloTime > 0 && cFSLandings > 1)
            {
                AirportList al = AirportListOfRoutes.CloneSubset(cfr.Route, true);

                if (al.DistanceForRoute() > MinXCDistance && al.MaxSegmentForRoute() > 25)
                {
                    miSoloXCFlight.AddEvent(1);
                    miSoloXCFlight.MatchingEventID   = cfr.flightID;
                    miSoloXCFlight.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.MilestoneProgress.MatchingXCFlightTemplate, cfr.dtFlight.ToShortDateString(), cfr.Route);
                }
            }

            if (DateTime.Now.AddCalendarMonths(-2).CompareTo(cfr.dtFlight) <= 0)
            {
                miTestPrep.AddEvent(cfr.Dual);
            }
        }
Esempio n. 2
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }

            decimal ifrTraining = Math.Min(cfr.Dual, cfr.IMC + cfr.IMCSim);

            // Aeronautical experience - can be in any aircraft or certified sim
            if (cfr.fIsCertifiedIFR)    // includes real aircraft
            {
                miAeronauticalExperience.AddEvent(Math.Max(cfr.GroundSim, cfr.Total));
            }

            // total pilot time and IFR time can both be in any real aircraft
            if (miPilotTime != null && cfr.fIsRealAircraft)
            {
                miPilotTime.AddEvent(cfr.Total);
            }
            miDualInstrumentTime.AddEvent((cfr.fIsRealAircraft || cfr.fIsCertifiedIFR) ? ifrTraining : 0);

            // everything else must be in a matching category AND must be in a real aircraft
            if (IsMatchingCategory(cfr.idCatClassOverride) && cfr.fIsRealAircraft)
            {
                decimal soloTime = 0.0M;
                cfr.FlightProps.ForEachEvent(pf =>
                {
                    if (pf.PropertyType.IsSolo)
                    {
                        soloTime += pf.DecValue;
                    }
                });

                miTimeInCategory.AddEvent(cfr.Total);
                miSoloTimeInCategory.AddEvent(soloTime);
                miSoloXCTimeInCategory.AddEvent(Math.Min(soloTime, cfr.XC));
                miDualInstrumentTimeInCategory.AddEvent(ifrTraining);

                bool fAllowLongXC = (soloTime > 0 || (!fLongCrossCountryMustBeSolo && cfr.PIC > 0));    // solo is always OK for cross country, otherwise need PIC.
                if (fAllowLongXC && !miSoloLongCrossCountry.IsSatisfied)
                {
                    AirportList al = AirportListOfRoutes.CloneSubset(cfr.Route, true);

                    int cRequiredLandings = fXCLandingsMustBeFullStop ? cfr.cFullStopLandings + cfr.cFullStopNightLandings : cfr.cLandingsThisFlight;

                    if (al.DistanceForRoute() >= reqXCDistance && al.GetAirportList().Length >= 3 && cRequiredLandings >= 2)
                    {
                        miSoloLongCrossCountry.MatchFlightEvent(cfr);
                    }
                }
            }
        }
Esempio n. 3
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException("cfr");
            }
            // we are not counting training device time.
            if (!cfr.fIsRealAircraft)
            {
                return;
            }

            bool    IsInMatchingCategory = CatClassMatchesRatingSought(cfr.idCatClassOverride);
            decimal XCPICTime            = Math.Min(cfr.PIC, cfr.XC);
            decimal IMCTime   = cfr.IMC + cfr.IMCSim;
            decimal IMCXCTime = Math.Min(IMCTime, cfr.XC);

            // 61.65(def)(1) - Look for cross-country time as PIC
            miMinXCTime.AddEvent(XCPICTime);
            if (IsInMatchingCategory)
            {
                miMinTimeInCategory.AddEvent(XCPICTime);
            }

            // 61.65(def)(2) - IMC time (total)
            miMinIMCTime.AddEvent(IMCTime);

            // 61.65(def)(2)(i) - recent test prep
            if (IsInMatchingCategory)
            {
                if (DateTime.Now.AddCalendarMonths(-2).CompareTo(cfr.dtFlight) <= 0)
                {
                    miMinIMCTestPrep.AddEvent(Math.Min(cfr.Dual, IMCTime));
                }

                if (cfr.cApproaches >= 3 && IMCXCTime > 0.0M)
                {
                    AirportList al = AirportListOfRoutes.CloneSubset(cfr.Route);
                    if (al.DistanceForRoute() >= MinXCDistance)
                    {
                        miIMCXC.AddEvent(1.0M);
                        miIMCXC.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.MilestoneProgress.MatchingXCFlightTemplate, cfr.dtFlight.ToShortDateString(), cfr.Route);
                        miIMCXC.MatchingEventID   = cfr.flightID;
                    }
                }
            }
        }
Esempio n. 4
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException("cfr");
            }
            if (!cfr.fIsRealAircraft)
            {
                return;
            }

            // 61.99
            miMinTime.AddEvent(cfr.Total);

            // 61.99(a)
            miMinInstruction.AddEvent(cfr.Dual);

            // 61.99(a)(1)
            if (cfr.cLandingsThisFlight >= 4 && cfr.Dual > 0)
            {
                AirportList al = AirportListOfRoutes.CloneSubset(cfr.Route, true);
                if (al.MaxDistanceForRoute() >= 25.0)
                {
                    miXCFlight.AddEvent(cfr.Dual);
                }
            }

            if (cfr.idCatClassOverride == CatClassID)
            {
                // 61.99(a)(2)
                if (DateTime.Now.AddCalendarMonths(-2).CompareTo(cfr.dtFlight) <= 0)
                {
                    miTestPrep.AddEvent(cfr.Dual);
                }

                // 61.99(b)
                decimal soloTime = 0.0M;
                cfr.FlightProps.ForEachEvent(pf => { if (pf.PropertyType.IsSolo)
                                                     {
                                                         soloTime += pf.DecValue;
                                                     }
                                             });
                miMinSolo.AddEvent(soloTime);
            }
        }
Esempio n. 5
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }
            bool fIsMatch = CatClassMatchesRatingSought(cfr.idCatClassOverride);

            if (!fIsMatch || !cfr.fIsCertifiedIFR)
            {
                return;
            }

            // Night - optional
            if (cfr.Night > 0)
            {
                miNightTime.AddEvent(cfr.Night);
                decimal nightDual = Math.Min(cfr.Night, cfr.Dual);
                miNightDual.AddEvent(nightDual);
                miNightXC.AddEvent(Math.Min(nightDual, cfr.XC));

                decimal soloTime      = cfr.FlightProps.TotalTimeForPredicate(cfp => cfp.PropertyType.IsSolo);
                decimal nightTakeoffs = cfr.FlightProps.TotalCountForPredicate(cfp => cfp.PropertyType.IsNightTakeOff);

                if (soloTime > 0)
                {
                    miNightSoloTakeoffs.AddEvent(nightTakeoffs);
                    miNightSoloLandings.AddEvent(cfr.cFullStopNightLandings);
                }

                if (nightDual > 0)
                {
                    AirportList al = AirportListOfRoutes.CloneSubset(cfr.Route, true);

                    if (al == null)
                    {
                        al = AirportListOfRoutes.CloneSubset(cfr.Route, true);
                    }
                    if (al.DistanceForRoute() > (double)JAALongNightXCDistanceAirplane)
                    {
                        miNightLongXC.MatchFlightEvent(cfr);
                    }
                }
            }
        }
Esempio n. 6
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            base.ExamineFlight(cfr);

            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }

            if (!cfr.fIsRealAircraft)
            {
                return;
            }

            switch (cfr.idCatClassOverride)
            {
            case CategoryClass.CatClassID.Helicopter:
            case CategoryClass.CatClassID.ASEL:
            case CategoryClass.CatClassID.ASES:
            case CategoryClass.CatClassID.AMEL:
            case CategoryClass.CatClassID.AMES:
                miXCPIC.AddEvent(Math.Min(cfr.PIC, cfr.XC));
                break;

            default:
                break;
            }

            if (cfr.IMC + cfr.IMCSim > 0 && cfr.Dual > 0 && CatClassMatchesRatingSought(cfr.idCatClassOverride))
            {
                miIFRTrainingInCategory.AddEvent(Math.Min(cfr.Dual, cfr.IMC + cfr.IMCSim));
            }

            if (cfr.cApproaches >= 2 && cfr.Dual > 0 && cfr.IMC + cfr.IMCSim > 0)
            {
                AirportList al = AirportListOfRoutes.CloneSubset(cfr.Route);
                if (al.DistanceForRoute() >= MinXCDistance)
                {
                    miXCDualXC.AddEvent(1.0M);
                    miXCDualXC.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.MilestoneProgress.MatchingXCFlightTemplate, cfr.dtFlight.ToShortDateString(), cfr.Route);
                    miXCDualXC.MatchingEventID   = cfr.flightID;
                }
            }
        }
Esempio n. 7
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }

            decimal IMCTime = cfr.IMC + cfr.IMCSim;

            decimal imcTimeInSim = Math.Min(IMCTime, SimTimeRemaining);

            if (cfr.fIsFTD)
            {
                decimal FTDTimeToApply = Math.Min(imcTimeInSim, FTDTimeRemaining);
                miMinIMCTime.AddEvent(FTDTimeToApply);
                FTDTimeRemaining -= FTDTimeToApply;
            }
            else if (cfr.fIsATD)
            {
                miMinIMCTime.AddTrainingEvent(imcTimeInSim, MaxATDTime, true);
            }

            // Everything past this point only applies to real aircraft
            if (!cfr.fIsRealAircraft)
            {
                SimTimeRemaining = Math.Max(0, SimTimeRemaining - imcTimeInSim);
                return;
            }

            bool    IsInMatchingCategory = CatClassMatchesRatingSought(cfr.idCatClassOverride);
            decimal XCPICTime            = Math.Min(cfr.PIC, cfr.XC);
            decimal IMCXCTime            = Math.Min(IMCTime, cfr.XC);

            // 61.65(def)(1) - Look for cross-country time as PIC
            miMinXCTime.AddEvent(XCPICTime);
            if (IsInMatchingCategory)
            {
                miMinTimeInCategory.AddEvent(XCPICTime);
            }

            // 61.65(def)(2) - IMC time (total)
            miMinIMCTime.AddEvent(IMCTime);

            decimal instTrainingTime = Math.Min(cfr.Dual, IMCTime);

            miInstrumentTraining.AddEvent(instTrainingTime);

            // 61.65(def)(2)(i) - recent test prep
            if (IsInMatchingCategory)
            {
                miMinIMCTestPrep.AddDecayableEvent(cfr.dtFlight, instTrainingTime, DateTime.Now.AddCalendarMonths(-2).CompareTo(cfr.dtFlight) <= 0);

                if (cfr.cApproaches >= 3 && IMCXCTime > 0.0M && instTrainingTime > 0)
                {
                    AirportList al = AirportListOfRoutes.CloneSubset(cfr.Route);
                    if (al.DistanceForRoute() >= MinXCDistance)
                    {
                        miIMCXC.AddEvent(1.0M);
                        miIMCXC.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.MilestoneProgress.MatchingXCFlightTemplate, cfr.dtFlight.ToShortDateString(), cfr.Route);
                        miIMCXC.MatchingEventID   = cfr.flightID;
                    }
                }
            }
        }
Esempio n. 8
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException("cfr");
            }

            // no provision for training devices.
            if (!cfr.fIsRealAircraft)
            {
                return;
            }

            // Must be a glider; motorgliders ("powered sailplanes") are subsets of gliders
            if (cfr.idCatClassOverride != CategoryClass.CatClassID.Glider)
            {
                return;
            }

            // Treat TMG time as if it were a training device.
            miTotal.AddTrainingEvent(cfr.Total, maxTMGTime, cfr.fMotorGlider);

            miDual.AddEvent(cfr.Dual);

            decimal soloTime           = 0.0M;
            bool    fInstructorOnBoard = false;
            decimal dutiesOfPICTime    = 0.0M;

            cfr.ForEachEvent(pf =>
            {
                if (pf.PropertyType.IsSolo)
                {
                    soloTime += pf.DecValue;
                }

                if (pf.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropInstructorOnBoard && !pf.IsDefaultValue)
                {
                    fInstructorOnBoard = true;    // instructor-on-board time only counts if you are acting as PIC
                }
                if (pf.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropDutiesOfPIC && !pf.IsDefaultValue)
                {
                    dutiesOfPICTime += pf.DecValue;
                }
            });

            if (fInstructorOnBoard)
            {
                soloTime += Math.Max(Math.Min(dutiesOfPICTime, cfr.Total - cfr.Dual), 0);    // dual received does NOT count as duties of PIC time here
            }
            miSolo.AddEvent(soloTime);
            miLandings.AddEvent(cfr.cLandingsThisFlight);

            if (!miCrossCountry.IsSatisfied)
            {
                AirportList al       = AirportListOfRoutes.CloneSubset(cfr.Route);
                double      distance = al.GetAirportList().Length > 1 ? al.DistanceForRoute() : 0.0;
                if ((soloTime > 0.0M && distance > minSoloXC) || (cfr.Dual > 0 && distance > minDualXC))
                {
                    miCrossCountry.MatchFlightEvent(cfr);
                }
            }
        }
Esempio n. 9
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException("cfr");
            }

            // no provision for training devices.
            if (!cfr.fIsRealAircraft)
            {
                return;
            }

            if (MatchesClassDual(cfr))
            {
                miMinDualInClass.AddEvent(cfr.Dual);
            }

            if (MatchesSoloOrTotalDual(cfr))
            {
                miMinTime.AddEvent(cfr.Total);

                decimal soloTime           = 0.0M;
                bool    fInstructorOnBoard = false;
                decimal dutiesOfPICTime    = 0.0M;
                cfr.ForEachEvent(pf => {
                    if (pf.PropertyType.IsSolo)
                    {
                        soloTime += pf.DecValue;
                    }

                    if (pf.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropInstructorOnBoard && !pf.IsDefaultValue)
                    {
                        fInstructorOnBoard = true;    // instructor-on-board time only counts if you are acting as PIC
                    }
                    if (pf.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropDutiesOfPIC && !pf.IsDefaultValue)
                    {
                        dutiesOfPICTime += pf.DecValue;
                    }
                });

                if (fInstructorOnBoard)
                {
                    soloTime += Math.Max(Math.Min(dutiesOfPICTime, cfr.Total - cfr.Dual), 0);    // dual received does NOT count as duties of PIC time here
                }
                if (soloTime > 0.0M)
                {
                    miMinSolo.AddEvent(soloTime);
                    miMinSoloXC.AddEvent(Math.Min(soloTime, cfr.XC));

                    if (cfr.cFullStopLandings >= 1 && !miMinSoloXCMinDist.IsSatisfied)
                    {
                        AirportList al = AirportListOfRoutes.CloneSubset(cfr.Route);
                        if (al.GetAirportList().Length > 1 && al.DistanceForRoute() >= MinSoloDistance)
                        {
                            miMinSoloXCMinDist.MatchFlightEvent(cfr);
                        }
                    }
                }
            }
        }
Esempio n. 10
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            bool fIsMatch = CatClassMatchesRatingSought(cfr.idCatClassOverride);
            bool fIsSim   = cfr.fIsCertifiedIFR && !cfr.fIsRealAircraft;

            if (!fIsMatch || !cfr.fIsCertifiedIFR)
            {
                return;
            }

            miTotal.AddTrainingEvent(Math.Min(cfr.Dual, cfr.fIsRealAircraft ? cfr.Total : cfr.GroundSim), JAASimSub, fIsSim);

            // Everything below here must be done in a real aircraft
            if (!cfr.fIsRealAircraft)
            {
                return;
            }

            miDual.AddEvent(cfr.Dual);
            miInstrumentDual.AddEvent(Math.Min(cfr.Dual, cfr.IMC + cfr.IMCSim));

            // Get solo time
            decimal soloTime = 0.0M;
            decimal instructorOnBoardTime = 0.0M;
            bool    fInstructorOnBoard    = false;
            decimal dutiesOfPICTime       = 0.0M;
            int     nightTakeoffs         = 0;

            cfr.ForEachEvent(pf =>
            {
                if (pf.PropertyType.IsSolo)
                {
                    soloTime += pf.DecValue;
                }
                if (pf.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropInstructorOnBoard && !pf.IsDefaultValue)
                {
                    fInstructorOnBoard = true;    // instructor-on-board time only counts if you are acting as PIC
                }
                if (pf.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropDutiesOfPIC && !pf.IsDefaultValue)
                {
                    dutiesOfPICTime += pf.DecValue;
                }
                if (pf.PropertyType.IsNightTakeOff)
                {
                    nightTakeoffs += pf.IntValue;
                }
            });

            if (fInstructorOnBoard)
            {
                instructorOnBoardTime = Math.Max(Math.Min(dutiesOfPICTime, cfr.Total - cfr.Dual), 0);    // dual received does NOT count as duties of PIC time here
            }
            decimal supervisedSolo = soloTime + Math.Min(instructorOnBoardTime, dutiesOfPICTime);

            miSolo.AddEvent(supervisedSolo);
            miSoloXC.AddEvent(Math.Min(supervisedSolo, cfr.XC));

            AirportList al = null;

            if (!miSoloLongXC.IsSatisfied && supervisedSolo > 0)
            {
                al = AirportListOfRoutes.CloneSubset(cfr.Route);

                if (al.DistanceForRoute() >= (double)miSoloLongXC.Threshold && al.GetAirportList().Length >= 3 && (cfr.cFullStopLandings + cfr.cFullStopNightLandings) >= 2)
                {
                    miSoloLongXC.MatchFlightEvent(cfr);
                }
            }

            // Night - optional
            if (cfr.Night > 0)
            {
                miNightTime.AddEvent(cfr.Night);
                decimal nightDual = Math.Min(cfr.Night, cfr.Dual);
                miNightDual.AddEvent(nightDual);
                miNightXC.AddEvent(Math.Min(nightDual, cfr.XC));
                if (soloTime > 0)
                {
                    miNightSoloTakeoffs.AddEvent(nightTakeoffs);
                    miNightSoloLandings.AddEvent(cfr.cFullStopNightLandings);
                }

                if (nightDual > 0)
                {
                    if (al == null)
                    {
                        al = AirportListOfRoutes.CloneSubset(cfr.Route);
                    }
                    if (al.DistanceForRoute() > (double)JAALongNightXCDistanceAirplane)
                    {
                        miNightLongXC.MatchFlightEvent(cfr);
                    }
                }
            }
        }
Esempio n. 11
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException("cfr");
            }
            DateTime dtFlight = cfr.dtFlight.Date;

            if (AutoDateRange)
            {
                StartDate = StartDate.EarlierDate(dtFlight);
                EndDate   = EndDate.LaterDate(dtFlight);
            }

            // ignore anything not in a real aircraft or outside of our date range
            if (!cfr.fIsRealAircraft || dtFlight.CompareTo(StartDate) < 0 || dtFlight.CompareTo(EndDate) > 0)
            {
                return;
            }

            miFlightCount.AddEvent(1);

            string szDateKey = dtFlight.YMDString();

            // Initialize the current streak, if needed
            if (FirstDayOfCurrentStreak == null || LastDayOfCurrentStreak == null)
            {
                FirstDayOfCurrentStreak = LastDayOfCurrentStreak = dtFlight;
            }

            // Extend the current streak if this flight is either on the first date or on a day before the first date; if it isn't one of those, then end the current streak
            if (dtFlight.CompareTo(FirstDayOfCurrentStreak.Value.Date) == 0 || dtFlight.CompareTo(FirstDayOfCurrentStreak.Value.AddDays(-1).Date) == 0)
            {
                FirstDayOfCurrentStreak = dtFlight;
            }
            else
            {
                FirstDayOfCurrentStreak = LastDayOfCurrentStreak = dtFlight;
            }

            int cDaysCurrentStreak = CurrentFlyingDayStreak;

            if (cDaysCurrentStreak > 1 && cDaysCurrentStreak > FlyingDayStreak)
            {
                FirstFlyingDayOfStreak = FirstDayOfCurrentStreak;
                LastFlyingDayOfStreak  = LastDayOfCurrentStreak;
            }

            // Distinct flights on dates
            if (FlightDates.ContainsKey(szDateKey))
            {
                FlightDates[szDateKey] = FlightDates[szDateKey] + 1;
            }
            else
            {
                FlightDates[szDateKey] = 1;
            }

            if (FlightDates[szDateKey] > MaxFlightsPerDay)
            {
                int cFlights = FlightDates[szDateKey];
                MaxFlightsPerDay                     = cFlights;
                miMostFlightsInDay.Progress          = cFlights;
                miMostFlightsInDay.MatchingEventText = String.Format(CultureInfo.InvariantCulture, Resources.Achievements.RecentAchievementMostFlightsInDay, cFlights, cfr.dtFlight);
                miMostFlightsInDay.Query             = new FlightQuery(Username)
                {
                    DateRange = FlightQuery.DateRanges.Custom, DateMin = cfr.dtFlight, DateMax = cfr.dtFlight
                };
            }

            // Longest flight
            if (cfr.Total > LongestFlightLength)
            {
                LongestFlightLength               = cfr.Total;
                miLongestFlight.Progress          = 1;
                miLongestFlight.MatchingEventID   = cfr.flightID;
                miLongestFlight.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.Achievements.RecentAchievementsLongestFlight, cfr.Total, dtFlight);
            }

            // Distinct aircraft/models
            DistinctAircraft.Add(cfr.idAircraft);
            DistinctModels.Add(cfr.idModel);
            DistinctICAO.Add(cfr.szFamily);

            // Furthest Flight & airport computations.
            AirportList al = AirportListOfRoutes.CloneSubset(cfr.Route, true);

            double distance = al.DistanceForRoute();

            if (distance > FurthestFlightDistance)
            {
                FurthestFlightDistance             = distance;
                miFurthestFlight.Progress          = 1;
                miFurthestFlight.MatchingEventID   = cfr.flightID;
                miFurthestFlight.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.Achievements.RecentAchievementsFurthestFlight, distance, dtFlight);
            }

            int cUniqueAirports           = 0;
            HashSet <string> hsThisFlight = new HashSet <string>();

            foreach (airport ap in al.UniqueAirports)
            {
                // Dedupe as we go based on latitude/longitude, ignoring non-ports.
                // We don't actually need the facility name here - so we can just round off the latitude/longitude and distinguish by type code.
                // Note: this can differ slightly from Visited Airports counts because for achievements, we're ignoring flights in training devices; visited airports doesn't ignore them.
                if (ap.IsPort)
                {
                    string szHash = ap.GeoHashKey;
                    if (!Airports.Contains(szHash))
                    {
                        Airports.Add(ap.GeoHashKey);
                        cUniqueAirports++;
                    }
                    hsThisFlight.Add(szHash);
                }
            }

            int cAirportsThisFlight = hsThisFlight.Count;

            if (cAirportsThisFlight > MostAirportsFlightCount)
            {
                MostAirportsFlightCount = cAirportsThisFlight;
                miMostAirportsFlight.MatchingEventID   = cfr.flightID;
                miMostAirportsFlight.Progress          = 1;
                miMostAirportsFlight.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.Achievements.RecentAchievementsAirportsOnFlight, cAirportsThisFlight, dtFlight.ToShortDateString());
                miMostAirportsFlight.Query             = new FlightQuery(Username)
                {
                    DateRange = FlightQuery.DateRanges.Custom, DateMax = dtFlight, DateMin = dtFlight
                };
            }
        }
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException("cfr");
            }
            DateTime dtFlight = cfr.dtFlight.Date;

            // ignore anything not in a real aircraft or outside of our date range
            if (!cfr.fIsRealAircraft || dtFlight.CompareTo(StartDate) < 0 || dtFlight.CompareTo(EndDate) > 0)
            {
                return;
            }

            string szDateKey = dtFlight.YMDString();

            // Initialize the current streak, if needed
            if (FirstDayOfCurrentStreak == null || LastDayOfCurrentStreak == null)
            {
                FirstDayOfCurrentStreak = LastDayOfCurrentStreak = dtFlight;
            }

            // Extend the current streak if this flight is either on the first date or on a day before the first date; if it isn't one of those, then end the current streak
            if (dtFlight.CompareTo(FirstDayOfCurrentStreak.Value.Date) == 0 || dtFlight.CompareTo(FirstDayOfCurrentStreak.Value.AddDays(-1).Date) == 0)
            {
                FirstDayOfCurrentStreak = dtFlight;
            }
            else
            {
                FirstDayOfCurrentStreak = LastDayOfCurrentStreak = dtFlight;
            }

            int cDaysCurrentStreak = CurrentFlyingDayStreak;

            if (cDaysCurrentStreak > 1 && cDaysCurrentStreak > FlyingDayStreak)
            {
                FirstFlyingDayOfStreak = FirstDayOfCurrentStreak;
                LastFlyingDayOfStreak  = LastDayOfCurrentStreak;
            }

            // Distinct flights on dates
            if (FlightDates.ContainsKey(szDateKey))
            {
                FlightDates[szDateKey] = FlightDates[szDateKey] + 1;
            }
            else
            {
                FlightDates[szDateKey] = 1;
            }

            if (FlightDates[szDateKey] > MaxFlightsPerDay)
            {
                int cFlights = FlightDates[szDateKey];
                MaxFlightsPerDay                     = cFlights;
                miMostFlightsInDay.Progress          = cFlights;
                miMostFlightsInDay.MatchingEventText = String.Format(CultureInfo.InvariantCulture, Resources.Achievements.RecentAchievementMostFlightsInDay, cFlights, cfr.dtFlight);
                miMostFlightsInDay.Query             = new FlightQuery(Username)
                {
                    DateRange = FlightQuery.DateRanges.Custom, DateMin = cfr.dtFlight, DateMax = cfr.dtFlight
                };
            }

            // Longest flight
            if (cfr.Total > LongestFlightLength)
            {
                LongestFlightLength               = cfr.Total;
                miLongestFlight.Progress          = 1;
                miLongestFlight.MatchingEventID   = cfr.flightID;
                miLongestFlight.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.Achievements.RecentAchievementsLongestFlight, cfr.Total, dtFlight);
            }

            // Distinct aircraft/models
            DistinctAircraft.Add(cfr.idAircraft);
            DistinctModels.Add(cfr.idModel);
            DistinctICAO.Add(cfr.szFamily);

            // Furthest Flight & airport computations.
            AirportList al = AirportListOfRoutes.CloneSubset(cfr.Route);

            double distance = al.DistanceForRoute();

            if (distance > FurthestFlightDistance)
            {
                FurthestFlightDistance             = distance;
                miFurthestFlight.Progress          = 1;
                miFurthestFlight.MatchingEventID   = cfr.flightID;
                miFurthestFlight.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.Achievements.RecentAchievementsFurthestFlight, distance, dtFlight);
            }

            int cUniqueAirports = al.UniqueAirports.Count();

            foreach (airport ap in al.UniqueAirports)
            {
                if (ap.IsPort)
                {
                    // Dedupe as we go - do the K-hack check; if the K version is in the AirportListOfRoutes, use that.  I.e., just pre-pend a "K" to ANY three-letter airport code.
                    if (ap.Code.Length == 3)
                    {
                        Airports.Add("K" + ap.Code);
                    }
                    else
                    {
                        Airports.Add(ap.Code);
                    }
                }
            }

            if (cUniqueAirports > MostAirportsFlightCount)
            {
                MostAirportsFlightCount                = cUniqueAirports;
                miMostAirportsFlight.Progress          = 1;
                miMostAirportsFlight.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.Achievements.RecentAchievementsAirportsOnFlight, cUniqueAirports, dtFlight.ToShortDateString());
                miMostAirportsFlight.Query             = new FlightQuery(Username)
                {
                    DateRange = FlightQuery.DateRanges.Custom, DateMax = dtFlight, DateMin = dtFlight
                };
            }
        }
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }
            DateTime dtFlight = cfr.dtFlight.Date;

            if (AutoDateRange)
            {
                StartDate = StartDate.EarlierDate(dtFlight);
                EndDate   = EndDate.LaterDate(dtFlight);
            }

            // ignore anything not in a real aircraft or outside of our date range
            if (!cfr.fIsRealAircraft || dtFlight.CompareTo(StartDate) < 0 || dtFlight.CompareTo(EndDate) > 0)
            {
                return;
            }

            miFlightCount.AddEvent(1);

            string szDateKey = dtFlight.YMDString();

            // Initialize the current streak, if needed
            if (FirstDayOfCurrentStreak == null || LastDayOfCurrentStreak == null)
            {
                FirstDayOfCurrentStreak = LastDayOfCurrentStreak = dtFlight;
            }

            // Extend the current streak if this flight is either on the first date or on a day before the first date; if it isn't one of those, then end the current streak
            FirstDayOfCurrentStreak = dtFlight.CompareTo(FirstDayOfCurrentStreak.Value.Date) == 0 || dtFlight.CompareTo(FirstDayOfCurrentStreak.Value.AddDays(-1).Date) == 0
                ? (DateTime?)dtFlight
                : (LastDayOfCurrentStreak = dtFlight);

            int cDaysCurrentStreak = CurrentFlyingDayStreak;

            if (cDaysCurrentStreak > 1 && cDaysCurrentStreak > FlyingDayStreak)
            {
                FirstFlyingDayOfStreak = FirstDayOfCurrentStreak;
                LastFlyingDayOfStreak  = LastDayOfCurrentStreak;
            }

            // Distinct flights on dates
            FlightDates[szDateKey] = FlightDates.ContainsKey(szDateKey) ? FlightDates[szDateKey] + 1 : 1;

            if (FlightDates[szDateKey] > MaxFlightsPerDay)
            {
                int cFlights = FlightDates[szDateKey];
                MaxFlightsPerDay                     = cFlights;
                miMostFlightsInDay.Progress          = cFlights;
                miMostFlightsInDay.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.Achievements.RecentAchievementMostFlightsInDay, cFlights, cfr.dtFlight);
                miMostFlightsInDay.Query             = new FlightQuery(Username)
                {
                    DateRange = FlightQuery.DateRanges.Custom, DateMin = cfr.dtFlight, DateMax = cfr.dtFlight
                };
            }

            FlightLandings[szDateKey] = FlightLandings.ContainsKey(szDateKey) ? FlightLandings[szDateKey] + cfr.cLandingsThisFlight : cfr.cLandingsThisFlight;

            if (FlightLandings[szDateKey] > MaxLandingsPerDay)
            {
                int cLandings = FlightLandings[szDateKey];
                MaxLandingsPerDay                     = cLandings;
                miMostLandingsInDay.Progress          = cLandings;
                miMostLandingsInDay.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.Achievements.RecentAchievementMostLandingsInDay, cLandings, cfr.dtFlight);
                miMostLandingsInDay.Query             = new FlightQuery(Username)
                {
                    DateRange = FlightQuery.DateRanges.Custom, DateMin = cfr.dtFlight, DateMax = cfr.dtFlight
                };
            }

            // Longest flight
            if (cfr.Total > LongestFlightLength)
            {
                LongestFlightLength               = cfr.Total;
                miLongestFlight.Progress          = 1;
                miLongestFlight.MatchingEventID   = cfr.flightID;
                miLongestFlight.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.Achievements.RecentAchievementsLongestFlight, cfr.Total, dtFlight);
            }

            // Distinct aircraft/models
            DistinctAircraft.Add(cfr.idAircraft);
            DistinctModels.Add(cfr.idModel);
            DistinctICAO.Add(cfr.szFamily);

            // Furthest Flight & airport computations.
            AirportList al = AirportListOfRoutes.CloneSubset(cfr.Route, true);

            double distance = al.DistanceForRoute();

            if (distance > FurthestFlightDistance)
            {
                FurthestFlightDistance             = distance;
                miFurthestFlight.Progress          = 1;
                miFurthestFlight.MatchingEventID   = cfr.flightID;
                miFurthestFlight.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.Achievements.RecentAchievementsFurthestFlight, distance, dtFlight);
            }

            int cAirportsThisFlight = MatchAirports(al.UniqueAirports);

            if (cAirportsThisFlight > MostAirportsFlightCount)
            {
                MostAirportsFlightCount = cAirportsThisFlight;
                miMostAirportsFlight.MatchingEventID   = cfr.flightID;
                miMostAirportsFlight.Progress          = 1;
                miMostAirportsFlight.MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.Achievements.RecentAchievementsAirportsOnFlight, cAirportsThisFlight, dtFlight.ToShortDateString());
                miMostAirportsFlight.Query             = new FlightQuery(Username)
                {
                    DateRange = FlightQuery.DateRanges.Custom, DateMax = dtFlight, DateMin = dtFlight
                };
            }

            fs100.ExamineFlight(cfr);
            fs1000.ExamineFlight(cfr);
        }