public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException("cfr");
            }

            // Must be real aircraft or FFS
            if (!cfr.fIsCertifiedLanding)
            {
                return;
            }

            int cNightTakeoffs = cfr.FlightProps.TotalCountForPredicate(cfp => cfp.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropNightTakeoff);
            int cNightLandings = cfr.cFullStopNightLandings + cfr.FlightProps.TotalCountForPredicate(cfp => cfp.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropNightTouchAndGo);

            if (cNightTakeoffs > 0)
            {
                fcNightTakeoff.AddRecentFlightEvents(cfr.dtFlight, cNightTakeoffs);
            }
            if (cNightLandings > 0)
            {
                fcNightLanding.AddRecentFlightEvents(cfr.dtFlight, cNightLandings);
            }
        }
        /// <summary>
        /// Computes the progress against this milestone
        /// </summary>
        /// <returns>A list of MilestoneItem objects</returns>
        public Collection <MilestoneItem> Refresh()
        {
            if (String.IsNullOrEmpty(Username))
            {
                throw new MyFlightbookException("Cannot compute milestones on an empty user!");
            }

            List <ExaminerFlightRow> lstRows  = new List <ExaminerFlightRow>();
            StringBuilder            sbRoutes = new StringBuilder();

            DBHelper dbh = new DBHelper(CurrencyExaminer.CurrencyQuery(CurrencyExaminer.CurrencyQueryDirection.Descending));

            dbh.ReadRows(
                (comm) =>
            {
                comm.Parameters.AddWithValue("UserName", Username);
                comm.Parameters.AddWithValue("langID", System.Threading.Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName);
            },
                (dr) =>
            {
                ExaminerFlightRow cfr = new ExaminerFlightRow(dr);
                sbRoutes.AppendFormat("{0} ", cfr.Route);
                lstRows.Add(cfr);       // we'll examine it below, after we've populated the routes
            });

            // Set up the airport list once for DB efficiency
            AirportListOfRoutes = new AirportList(sbRoutes.ToString());

            lstRows.ForEach(cfr => { ExamineFlight(cfr); });

            return(Milestones);
        }
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }

            decimal IMCTime = cfr.IMC + cfr.IMCSim;

            if (cfr.fIsCertifiedIFR)
            {
                miMinIFRTime.AddEvent(IMCTime);
            }

            if (!cfr.fIsRealAircraft)
            {
                return;
            }

            decimal part135XC = Part135Milestones.Part135CrossCountry(cfr);

            miMinTimeAsPilot.AddEvent(cfr.Total);
            miMinXCTime.AddEvent(part135XC);
            miMinNightTime.AddEvent(cfr.Night);
            miMinIFRAircraftTime.AddEvent(IMCTime);
        }
        /// <summary>
        /// Returns the timespan since the specified date that overlaps the nettime.  E.g., if you have 2.5 hours of CFI time and need to know how much of that fell in the window since
        /// the start of the period (dtStart), this returns the amount of the 2.5 hours that falls within the window
        /// To figure out how much of the time (e.g., CFI time) could contribute to the 8-hour limit within 24 hours, we need to figure out the most conservative
        /// start to the 24 hour period, as follows
        /// a) Call EffectiveDuty.This sets dtDutyStart, dtDutyEnd to either their specified values OR to the 24 hour midnight-to-midnight range.  This provides a starting point.  (Done in the constructor)
        /// b) If a flight start or an engine start is specified, use the later of those(or the duty time), since it's only flight time that is limited.
        /// Since engine should precede flight, doing a "LaterDate" call should prefer flightstart over enginestart, which is what we want.If dutytime is later still, then this is a no-op.
        /// c) If not flight/engine start is specified and a flight end is specified, subtract off the CFI time from that and use that as the start time, if it's later than what emerged from (b).
        /// Don't do this, though, if we had start times.
        /// d) Otherwise, use an engine time(if specified).  But don't do this if we had flight-end.
        /// Regardless, pull the effective END of duty time in by the end-of-flight time, if specified, or the engine end, if specified.This limits the end time.
        /// We've now got as late a start time as possible and as early an end-time as possible.
        /// Caller will Contribute MIN(netTime, duty-time window) towards the limit.
        /// </summary>
        /// <param name="dtStart">Start of the period</param>
        /// <param name="netTime">Max time that could be within the period</param>
        /// <param name="cfr">The flight row that specifies a potential duty period</param>
        /// <returns></returns>
        public TimeSpan TimeSince(DateTime dtStart, double netTime, ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }

            DateTime dtDutyStart = FlightDutyStart;
            DateTime dtDutyEnd   = FlightDutyEnd;

            if (cfr.dtFlightStart.HasValue() || cfr.dtEngineStart.HasValue())
            {
                dtDutyStart = dtDutyStart.LaterDate(cfr.dtFlightStart).LaterDate(cfr.dtEngineStart);
            }
            else if (cfr.dtFlightEnd.HasValue())
            {
                dtDutyStart = dtDutyStart.LaterDate(cfr.dtFlightEnd.AddHours(-netTime));
            }
            else if (cfr.dtEngineEnd.HasValue())    // don't do engine if we have a flight end
            {
                dtDutyStart = dtDutyStart.LaterDate(cfr.dtEngineEnd.AddHours(-netTime));
            }

            if (cfr.dtFlightEnd.HasValue())
            {
                dtDutyEnd = dtDutyEnd.EarlierDate(cfr.dtFlightEnd);
            }
            else if (cfr.dtEngineEnd.HasValue())
            {
                dtDutyEnd = dtDutyEnd.EarlierDate(cfr.dtEngineEnd);
            }

            return(dtDutyEnd.Subtract(dtDutyStart.LaterDate(dtStart)));
        }
Beispiel #5
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }

            // quick short circuit for anything more than 24 hours old, or not a real aircraft
            if (!cfr.fIsRealAircraft || cfr.dtFlight.CompareTo(dt24HoursAgo.Date) < 0)
            {
                return;
            }

            EffectiveDutyPeriod edp = new EffectiveDutyPeriod(cfr)
            {
                FlightDutyStart = DateTime.UtcNow.AddDays(-1), FlightDutyEnd = DateTime.UtcNow
            };

            TimeSpan ts = edp.TimeSince(dt24HoursAgo, (double)cfr.Total, cfr);

            if (ts.TotalHours > 0)
            {
                TotalFlying += Math.Min((double)cfr.Total, ts.TotalHours);
            }
        }
Beispiel #6
0
        public void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }

            // Only include potential commercial flying - so nothing in a sim.
            if (!cfr.fIsRealAircraft)
            {
                return;
            }

            if (cfr.dtFlight.CompareTo(PeriodStart) >= 0)
            {
                if (cfr.dtFlightStart.HasValue() && cfr.dtFlightEnd.HasValue())
                {
                    DateTime dtEffectivestart = cfr.dtFlightStart.LaterDate(PeriodStart);
                    TimeSpan ts = cfr.dtFlightEnd.Subtract(dtEffectivestart);
                    if (ts.TotalHours > 0)
                    {
                        StatusSoFar += ts.TotalHours;
                    }
                }
                else
                {
                    StatusSoFar += Math.Round(((double)cfr.Total * 60)) / 60.0;
                }
            }
        }
Beispiel #7
0
        public void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }

            if (cfr.dtFlight.CompareTo(DateTime.Now.AddYears(-5)) < 0)
            {
                return;
            }

            fHasPICExperience = fHasPICExperience || (cfr.PIC + cfr.SIC > 0);

            // Alternative to having PIC experience is having a flight review in the prior year
            if (cfr.dtFlight.CompareTo(DateTime.Now.AddYears(-1)) > 0)
            {
                cfr.FlightProps.ForEachEvent((pe) =>
                {
                    if (pe.PropertyType.IsBFR)
                    {
                        fHasFlightReview = true;
                    }
                });
            }
        }
        /// <summary>
        /// Assumes we're going in
        /// </summary>
        /// <param name="le"></param>
        /// <exception cref="ArgumentNullException"></exception>
        public void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }

            queue.Enqueue(cfr); // queue it up
            currentTotal += cfr.Total;

            // We've crossed the threshold - update the start/end dates, if needed, and then dequeue until we are back under the threshold
            if (currentTotal > Threshold)
            {
                do
                {
                    // cfr has the oldest date
                    DateTime          dtStart = cfr.dtFlight;
                    ExaminerFlightRow efr     = queue.Dequeue();
                    currentTotal -= efr.Total;  // take it out of the current total.
                    DateTime dtEnd = efr.dtFlight;
                    if (dtEnd.Subtract(dtStart).CompareTo(ShortestSpan) < 0)
                    {
                        StartDate = dtStart;
                        EndDate   = dtEnd;
                    }
                } while (currentTotal > Threshold);
            }
        }
Beispiel #9
0
        public void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }
            if (cfr.FlightProps == null)
            {
                return;
            }

            cfr.FlightProps.ForEachEvent((pe) =>
            {
                if (pe.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropNVGoggleOperations)
                {
                    // Since being current for a helicopter/powered lift requires doing hovering tasks (61.57(f)(1)(ii)),
                    // we add the events IF (a) they were performed in a heli/pl OR (b) this NVCurrency is not a heli/pl
                    if (IsHelicopterOrPoweredLift(cfr.idCatClassOverride) || !IsHelicopterOrPoweredLift(m_ccid))
                    {
                        fcNVPassengers.AddRecentFlightEvents(cfr.dtFlight, pe.IntValue);
                        fcNVNoPassengers.AddRecentFlightEvents(cfr.dtFlight, pe.IntValue);
                    }
                }
                else if (pe.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropNVGoggleProficiencyCheck)
                {
                    fcNVNoPassengers.AddRecentFlightEvents(cfr.dtFlight, fcNVNoPassengers.RequiredEvents);
                    fcNVPassengers.AddRecentFlightEvents(cfr.dtFlight, fcNVPassengers.RequiredEvents);
                    fcNVIPC.AddRecentFlightEvents(cfr.dtFlight, 1);
                }
            });
        }
Beispiel #10
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            bool fIsAirplane = CategoryClass.IsAirplane(cfr.idCatClassOverride);
            bool fIsIFRSim   = (fIsAirplane && cfr.fIsFTD);

            if (fIsAirplane || cfr.fIsRealAircraft)
            {
                miMinInstrumentTime.AddTrainingEvent(cfr.IMC + cfr.IMCSim, ATPMaxIFRSimulator, fIsIFRSim);
            }

            if (fIsAirplane && cfr.idCatClassOverride == requiredCatClassID)
            {
                bool fIsFullSim = !cfr.fIsRealAircraft && cfr.fIsCertifiedLanding;
                if (fIsAirplane && (cfr.fIsRealAircraft || fIsFullSim))
                {
                    miMinTimeInClass.AddTrainingEvent(cfr.Total, ATPMinTimeInClassFullSimulator, fIsFullSim);
                }
            }

            // Above are the only things to which we will alow training devices.
            if (!cfr.fIsRealAircraft)
            {
                return;
            }

            miTotal.AddEvent(Math.Max(CanCreditSICAndFlightEngineer ? cfr.SIC : 0, cfr.Total));
            miMinXCTime.AddEvent(cfr.XC);
            miMinNightTime.AddEvent(cfr.Night);

            if (CanCreditSICAndFlightEngineer)
            {
                decimal flightEngineerTime = 0.0M;
                cfr.ForEachEvent((pe) => { if (pe.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropFlightEngineerTime)
                                           {
                                               flightEngineerTime += pe.DecValue;
                                           }
                                 });

                if (flightEngineerTime > 0)
                {
                    miTotal.AddTrainingEvent(flightEngineerTime / 3.0M, ATPMaxFlightEngineer, true);
                }
            }

            // Remainder must be done in a real aircraft and it must be in an airplane.
            // Not clear if the night takeoffs/landings need to be in a real aircraft / airplane, but I'll require it to be safe.
            if (fIsAirplane)
            {
                miMinPIC.AddEvent(cfr.PIC);
                miMinPICXC.AddEvent(Math.Min(cfr.PIC, cfr.XC));
                miMinPICNight.AddEvent(Math.Min(cfr.PIC, cfr.Night));
                cfr.ForEachEvent((pe) => { if (pe.PropertyType.IsNightTakeOff)
                                           {
                                               miNightTO.AddEvent(pe.IntValue);
                                           }
                                 });
                miNightLanding.AddEvent(cfr.cFullStopNightLandings);
            }
        }
Beispiel #11
0
 protected override bool IsQualifyingFlight(ExaminerFlightRow cfr)
 {
     if (cfr == null)
     {
         throw new ArgumentNullException(nameof(cfr));
     }
     return(cfr.fIsRealAircraft && cfr.idCatClassOverride == CategoryClass.CatClassID.Helicopter);
 }
Beispiel #12
0
 protected override bool IsQualifyingFlight(ExaminerFlightRow cfr)
 {
     if (cfr == null)
     {
         throw new ArgumentNullException(nameof(cfr));
     }
     return(cfr.fIsRealAircraft && (cfr.fMotorGlider || CategoryClass.IsAirplane(cfr.idCatClassOverride)));
 }
Beispiel #13
0
        public override bool MatchesClassDual(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }

            return(cfr.idCatClassOverride == CategoryClass.CatClassID.ASEL);
        }
Beispiel #14
0
        public override bool MatchesSoloOrTotalDual(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException("cfr");
            }

            return(cfr.idCatClassOverride == CategoryClass.CatClassID.Helicopter);
        }
        /// <summary>
        /// Returns the timespan since the specified date that overlaps the nettime.  E.g., if you have 2.5 hours of CFI time and need to know how much of that fell in the window since
        /// the start of the period (dtStart), this returns the amount of the 2.5 hours that falls within the window
        /// To figure out how much of the time (e.g., CFI time) could contribute to the 8-hour limit within 24 hours, we need to figure out the most conservative
        /// start to the 24 hour period, as follows
        /// a) Call EffectiveDuty.This sets dtDutyStart, dtDutyEnd to either their specified values OR to the 24 hour midnight-to-midnight range.  This provides a starting point.  (Done in the constructor)
        /// b) The flight time range goes from EARLIEST(Flight start, Engine start, or Block Out) to LATEST(Flight End, Engine End, Block End).  Call this TR.start to TR.end
        /// c) If we can compute one of TR.start or TR.end, we can figure out the other end by adding (subtracting) net time.  i.e., if we know TR.start, then TR.end is TR.start + net time
        /// d) The contributed "time since" is TR.end minus LATEST(FlightDutyStart, TR.start).  i.e., if TR starts after FlightDutyStart, then all of the net time counts.
        ///    Otherwise, only the amount that is after FlightDutyStart counts.
        /// </summary>
        /// <param name="dtStart">Start of the period</param>
        /// <param name="netTime">Max time that could be within the period</param>
        /// <param name="cfr">The flight row that specifies a potential duty period</param>
        /// <returns></returns>
        public TimeSpan TimeSince(DateTime dtStart, double netTime, ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }

            DateTime dtStartFlight = DateTime.MaxValue;
            DateTime dtEndFlight   = DateTime.MinValue;

            DateTime blockOut = cfr.FlightProps.PropertyExistsWithID(CustomPropertyType.KnownProperties.IDBlockOut) ? cfr.FlightProps[CustomPropertyType.KnownProperties.IDBlockOut].DateValue : DateTime.MaxValue;
            DateTime blockIn  = cfr.FlightProps.PropertyExistsWithID(CustomPropertyType.KnownProperties.IDBlockIn) ? cfr.FlightProps[CustomPropertyType.KnownProperties.IDBlockIn].DateValue : DateTime.MinValue;

            if (cfr.dtFlightStart.HasValue())
            {
                dtStartFlight = dtStartFlight.EarlierDate(cfr.dtFlightStart);
            }
            if (cfr.dtEngineStart.HasValue())
            {
                dtStartFlight = dtStartFlight.EarlierDate(cfr.dtEngineStart);
            }
            dtStartFlight = dtStartFlight.EarlierDate(blockOut);

            if (dtStartFlight.CompareTo(DateTime.MaxValue) == 0) // none were found
            {
                dtStartFlight = DateTime.MinValue;
            }

            if (cfr.dtFlightEnd.HasValue())
            {
                dtEndFlight = dtEndFlight.LaterDate(cfr.dtFlightEnd);
            }
            if (cfr.dtEngineEnd.HasValue())
            {
                dtEndFlight = dtEndFlight.LaterDate(cfr.dtEngineEnd);
            }
            dtEndFlight = dtEndFlight.LaterDate(blockIn);

            if (!dtEndFlight.HasValue() && !dtStartFlight.HasValue())
            {
                // Neither end found - conservatively set from (FlightDutyEnd - netTime) to FlightDutyEnd
                dtEndFlight   = FlightDutyEnd;
                dtStartFlight = FlightDutyEnd.AddHours(-netTime);
            }
            if (dtEndFlight.HasValue())
            {
                // found an ending time but not a starting time - compute starting based on end
                dtStartFlight = dtEndFlight.AddHours(-netTime);
            }
            else if (dtStartFlight.HasValue())
            {
                // found a start time, but no ending time - compute an end based on the start
                dtEndFlight = dtStartFlight.AddHours(netTime);
            }

            return(dtEndFlight.Subtract(dtStartFlight.LaterDate(dtStart)));
        }
Beispiel #16
0
 public override void ExamineFlight(ExaminerFlightRow cfr)
 {
     if (cfr == null)
     {
         throw new ArgumentNullException(nameof(cfr));
     }
     base.ExamineFlight(cfr);    // Should handle touch-and-go landings regardless.
     fcCanada.ExamineFlight(cfr);
 }
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException("cfr");
            }

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

            // Keep track of heavier-than-air time, reduce minimums if achieved
            if (CategoryClass.IsHeavierThanAir(cfr.idCatClassOverride))
            {
                TotalHeavierThanAir += cfr.Total;
                if (!QualifiesByHeavierThanAir && TotalHeavierThanAir >= 20)
                {
                    QualifiesByHeavierThanAir      = true;
                    miMinTime.Threshold            = 3;
                    miMinTrainingFlights.Threshold = 5;
                    miMinSolo.Threshold            = 1;
                    miMinSolo.Threshold            = 3;
                }
            }

            // now reject anything not in a glider
            if (CatClassID != cfr.idCatClassOverride)
            {
                return;
            }

            miMinTime.AddEvent(cfr.Total);
            if (cfr.Dual > 0)
            {
                miMinTrainingFlights.AddEvent(Math.Max(1, cfr.cLandingsThisFlight));
                if (DateTime.Now.AddCalendarMonths(-2).CompareTo(cfr.dtFlight) <= 0)
                {
                    miTestPrep.AddEvent(cfr.Dual);
                }
            }

            decimal soloTime = 0.0M;

            cfr.ForEachEvent(pf => { if (pf.PropertyType.IsSolo)
                                     {
                                         soloTime += pf.DecValue;
                                     }
                             });
            if (soloTime > 0)
            {
                miMinSolo.AddEvent(soloTime);
                miMinSoloFlights.AddEvent(cfr.cLandingsThisFlight); // assuming no touch-and-go in a glider!
            }
        }
Beispiel #18
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }

            if (!cfr.fIsRealAircraft && cfr.fIsCertifiedIFR)
            {
                miInstrument.AddTrainingEvent(cfr.IMCSim, MaxSimulator, !cfr.fIsRealAircraft);
            }

            if (!cfr.fIsRealAircraft)
            {
                return;
            }

            miTotal.AddEvent(cfr.Total);

            decimal CoPilot = cfr.SIC + cfr.FlightProps.TimeForProperty(CustomPropertyType.KnownProperties.IDPropMilitaryCoPilottime) + cfr.FlightProps.TimeForProperty(CustomPropertyType.KnownProperties.IDPropCoPilotTime);

            miNight.AddEvent(Math.Min(CoPilot + cfr.PIC, cfr.Night));

            // Can add PIC or PICUS, but only up to MinPICUSInCategory for the latter.
            // So if a flight has, say, an hour of PIC and an hour of PICUS, it's possible that they're double-logged.  Looking at the database, that seems common.
            // E.g., a 1.5 hour flight has 1.5 hours of PIC and 1.5 hours of PICUS.
            // To be conservative, we'll subtract off any PICUS from PIC; the remainder is PIC time we can use unlimited.
            // Thus we can compute total PIC time for a flight as PIC+PICUS - which will possibly undercount but never overcount.
            decimal PICUS  = cfr.FlightProps.TimeForProperty(CustomPropertyType.KnownProperties.IDPropPICUS);
            decimal netPIC = Math.Max(cfr.PIC - PICUS, 0.0M);

            miXCPICAdditional.AddEvent(Math.Min(cfr.PIC, cfr.XC));
            miXCSICPICAdditional.AddEvent(Math.Min(cfr.Total, Math.Min(cfr.PIC + cfr.SIC + CoPilot, cfr.XC)));

            if (cfr.szCategory.CompareCurrentCultureIgnoreCase(Category) == 0)
            {
                miCategory.AddEvent(cfr.Total);
                miXC.AddEvent(cfr.XC);

                miPIC.AddEvent(netPIC);
                miPICXC.AddEvent(Math.Min(netPIC, cfr.XC));
                miPICXCNight.AddEvent(Math.Min(netPIC, Math.Min(cfr.XC, cfr.Night)));

                miPIC.AddTrainingEvent(PICUS, MaxPICUSInCategory, true);
                miPICXC.AddTrainingEvent(Math.Min(PICUS, cfr.XC), MaxPICUSInCategory, true);
                miPICXCNight.AddTrainingEvent(Math.Min(PICUS, Math.Min(cfr.XC, cfr.Night)), MaxPICUSInCategory, true);

                miNightInCategory.AddEvent(Math.Min(CoPilot + cfr.PIC, cfr.Night));

                miInstrument.AddEvent(cfr.IMC + cfr.IMCSim);
            }
            else if (cfr.szCategory.CompareCurrentCultureIgnoreCase(AltCategory) == 0)
            {
                miInstrumentAltCategory.AddTrainingEvent(cfr.IMC + cfr.IMCSim, MaxInstrumentAlternativeCategory, true);
            }
        }
Beispiel #19
0
 public override void ExamineFlight(ExaminerFlightRow cfr)
 {
     cfr.ForEachEvent((pfe) =>
     {
         if (pfe.PropertyType.IsIPC)
         {
             AddRecentFlightEvents(cfr.dtFlight, 1);
         }
     });
 }
Beispiel #20
0
 public override void ExamineFlight(ExaminerFlightRow cfr)
 {
     cfr.ForEachEvent((pfe) =>
     {
         if (pfe.PropTypeID == (int)CustomPropertyType.KnownProperties.IDProp135293Competency || pfe.PropertyType.IsIPC)
         {
             AddRecentFlightEvents(cfr.dtFlight, 1);
         }
     });
 }
        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);
            }
        }
Beispiel #22
0
 public override void ExamineFlight(ExaminerFlightRow cfr)
 {
     if (cfr == null)
     {
         throw new ArgumentNullException(nameof(cfr));
     }
     if (cfr.idCatClassOverride == requiredCatClassID && cfr.fIsRealAircraft)
     {
         miPIC.AddEvent(cfr.PIC);
     }
 }
 public override void ExamineFlight(ExaminerFlightRow cfr)
 {
     if (cfr == null)
     {
         throw new ArgumentNullException(nameof(cfr));
     }
     base.ExamineFlight(cfr);
     // Add in night touch-and-go landings too, since they also count.
     AddRecentFlightEvents(cfr.dtFlight, cfr.FlightProps.TotalCountForPredicate(cfp => cfp.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropNightTouchAndGo));
     fcCanada.ExamineFlight(cfr);
 }
Beispiel #24
0
 public override void ExamineFlight(ExaminerFlightRow cfr)
 {
     if (cfr == null)
     {
         throw new ArgumentNullException("cfr");
     }
     if (cfr.PropertyExistsWithID(CustomPropertyType.KnownProperties.IDProp135293Knowledge))
     {
         AddRecentFlightEvents(cfr.dtFlight, 1);
     }
 }
Beispiel #25
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);
                    }
                }
            }
        }
Beispiel #26
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException("cfr");
            }

            if (cfr.fIsRealAircraft && cfr.FlightProps.PropertyExistsWithID(CustomPropertyType.KnownProperties.IDProp135299FlightCheck))
            {
                AddRecentFlightEvents(cfr.dtFlight, 1);
            }
        }
Beispiel #27
0
        public override bool MatchesSoloOrTotalDual(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException("cfr");
            }

            bool fAirplane = cfr.idCatClassOverride == CategoryClass.CatClassID.ASEL;
            bool fTMG      = cfr.idCatClassOverride == CategoryClass.CatClassID.Glider && cfr.fMotorGlider;

            return(fAirplane || fTMG);
        }
 public void MatchFlightEvent(ExaminerFlightRow cfr)
 {
     if (Type == MilestoneType.AchieveOnce && !IsSatisfied)
     {
         if (cfr == null)
         {
             throw new ArgumentNullException("cfr");
         }
         AddEvent(1);
         MatchingEventText = String.Format(CultureInfo.CurrentCulture, Resources.MilestoneProgress.MatchingXCFlightTemplate, cfr.dtFlight.ToShortDateString(), cfr.Route);
         MatchingEventID   = cfr.flightID;
     }
 }
        /// <summary>
        /// Computes the progress against this milestone
        /// </summary>
        /// <returns>A list of MilestoneItem objects</returns>
        public Collection <MilestoneItem> Refresh()
        {
            if (String.IsNullOrEmpty(Username))
            {
                throw new MyFlightbookException("Cannot compute milestones on an empty user!");
            }

            // get all custom flight properties that could contribute to currency of one sort or another
            // and stick them into a dictionary for retrieval down below by flightID.
            Dictionary <int, List <CustomFlightProperty> > dctFlightEvents = new Dictionary <int, List <CustomFlightProperty> >();     // flight events (IPC, Instrument checkrides, etc.), keyed by flight ID
            IEnumerable <CustomFlightProperty>             rgPfe           = CustomFlightProperty.GetFlaggedEvents(Username);

            foreach (CustomFlightProperty pfe in rgPfe)
            {
                List <CustomFlightProperty> lstpf = (dctFlightEvents.ContainsKey(pfe.FlightID) ? dctFlightEvents[pfe.FlightID] : null);
                if (lstpf == null)
                {
                    dctFlightEvents.Add(pfe.FlightID, lstpf = new List <CustomFlightProperty>());
                }
                lstpf.Add(pfe);
            }

            List <ExaminerFlightRow> lstRows  = new List <ExaminerFlightRow>();
            StringBuilder            sbRoutes = new StringBuilder();

            DBHelper dbh = new DBHelper(CurrencyExaminer.CurrencyQuery(CurrencyExaminer.CurrencyQueryDirection.Descending));

            dbh.ReadRows(
                (comm) =>
            {
                comm.Parameters.AddWithValue("UserName", Username);
                comm.Parameters.AddWithValue("langID", System.Threading.Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName);
            },
                (dr) =>
            {
                ExaminerFlightRow cfr = new ExaminerFlightRow(dr);
                if (dctFlightEvents.ContainsKey(cfr.flightID))
                {
                    cfr.AddEvents(dctFlightEvents[cfr.flightID]);
                }
                sbRoutes.AppendFormat("{0} ", cfr.Route);
                lstRows.Add(cfr);       // we'll examine it below, after we've populated the routes
            });

            // Set up the airport list once for DB efficiency
            AirportListOfRoutes = new AirportList(sbRoutes.ToString());

            lstRows.ForEach(cfr => { ExamineFlight(cfr); });

            return(Milestones);
        }
Beispiel #30
0
        public override void ExamineFlight(ExaminerFlightRow cfr)
        {
            if (cfr == null)
            {
                throw new ArgumentNullException(nameof(cfr));
            }
            // Normal currency
            fcPIC.AddRecentFlightEvents(cfr.dtFlight, cfr.PIC);
            if (cfr.PIC > 0)
            {
                fcLandings.AddRecentFlightEvents(cfr.dtFlight, cfr.cLandingsThisFlight);
            }

            fcDual.AddRecentFlightEvents(cfr.dtFlight, cfr.Dual);

            // If we aren't current above, we still want to count any landings or time spent as dual, solo, or DPIC with an Instructor on board.
            bool    fHasProficiencyCheck = false;
            decimal dPIC = 0.0M;
            bool    fInstructorOnBoard = false;

            cfr.FlightProps.ForEachEvent((cfp) =>
            {
                if (cfp.PropertyType.IsPICProficiencyCheck6158)
                {
                    fHasProficiencyCheck = true;
                }
                if (cfp.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropInstructorOnBoard && !cfp.IsDefaultValue)
                {
                    fInstructorOnBoard = true;    // instructor-on-board time only counts if you are acting as PIC
                }
                if (cfp.PropTypeID == (int)CustomPropertyType.KnownProperties.IDPropDutiesOfPIC && !cfp.IsDefaultValue)
                {
                    dPIC += cfp.DecValue;
                }
            });

            if (fHasProficiencyCheck)
            {
                fcProficiencyCheck.AddRecentFlightEvents(cfr.dtFlight, 1);
            }

            if (!fInstructorOnBoard)
            {
                dPIC = 0.0M;        // only counts if instructor on board.
            }
            if (cfr.Dual + dPIC > 0)
            {
                fcAlternateLandings.AddRecentFlightEvents(cfr.dtFlight, cfr.cLandingsThisFlight);
                fcAlternatePIC.AddRecentFlightEvents(cfr.dtFlight, Math.Min(cfr.Total, cfr.Dual + dPIC));
            }
        }