diff --git a/OpenSky.Agent.SimConnectMSFS/OpenSky.Agent.SimConnectMSFS.csproj b/OpenSky.Agent.SimConnectMSFS/OpenSky.Agent.SimConnectMSFS.csproj index cd9befe..572752a 100644 --- a/OpenSky.Agent.SimConnectMSFS/OpenSky.Agent.SimConnectMSFS.csproj +++ b/OpenSky.Agent.SimConnectMSFS/OpenSky.Agent.SimConnectMSFS.csproj @@ -51,8 +51,8 @@ ..\packages\Microsoft.Maps.MapControl.WPF.1.0.0.3\lib\net40-Client\Microsoft.Maps.MapControl.WPF.dll - - ..\packages\OpenSky.FlightLogXML.0.1.7\lib\net48\OpenSky.FlightLogXML.dll + + ..\packages\OpenSky.FlightLogXML.0.1.8\lib\net48\OpenSky.FlightLogXML.dll ..\packages\MSFT.ParallelExtensionsExtras.1.2.0\lib\ParallelExtensionsExtras.dll diff --git a/OpenSky.Agent.SimConnectMSFS/packages.config b/OpenSky.Agent.SimConnectMSFS/packages.config index 1d7ee89..bebc59d 100644 --- a/OpenSky.Agent.SimConnectMSFS/packages.config +++ b/OpenSky.Agent.SimConnectMSFS/packages.config @@ -5,7 +5,7 @@ - + diff --git a/OpenSky.Agent/Controls/Models/ErrorDetails.cs b/OpenSky.Agent.Simulator/Controls/Models/ErrorDetails.cs similarity index 96% rename from OpenSky.Agent/Controls/Models/ErrorDetails.cs rename to OpenSky.Agent.Simulator/Controls/Models/ErrorDetails.cs index 014340c..cddb77a 100644 --- a/OpenSky.Agent/Controls/Models/ErrorDetails.cs +++ b/OpenSky.Agent.Simulator/Controls/Models/ErrorDetails.cs @@ -4,7 +4,7 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace OpenSky.Agent.Controls.Models +namespace OpenSky.Agent.Simulator.Controls.Models { using System; diff --git a/OpenSky.Agent/Controls/Models/ExtendedMessageBoxImage.cs b/OpenSky.Agent.Simulator/Controls/Models/ExtendedMessageBoxImage.cs similarity index 97% rename from OpenSky.Agent/Controls/Models/ExtendedMessageBoxImage.cs rename to OpenSky.Agent.Simulator/Controls/Models/ExtendedMessageBoxImage.cs index e94b6fe..4a62dbc 100644 --- a/OpenSky.Agent/Controls/Models/ExtendedMessageBoxImage.cs +++ b/OpenSky.Agent.Simulator/Controls/Models/ExtendedMessageBoxImage.cs @@ -4,7 +4,7 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace OpenSky.Agent.Controls.Models +namespace OpenSky.Agent.Simulator.Controls.Models { /// ------------------------------------------------------------------------------------------------- /// diff --git a/OpenSky.Agent/Controls/Models/ExtendedMessageBoxResult.cs b/OpenSky.Agent.Simulator/Controls/Models/ExtendedMessageBoxResult.cs similarity index 94% rename from OpenSky.Agent/Controls/Models/ExtendedMessageBoxResult.cs rename to OpenSky.Agent.Simulator/Controls/Models/ExtendedMessageBoxResult.cs index 76b40bb..88fec59 100644 --- a/OpenSky.Agent/Controls/Models/ExtendedMessageBoxResult.cs +++ b/OpenSky.Agent.Simulator/Controls/Models/ExtendedMessageBoxResult.cs @@ -4,7 +4,7 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace OpenSky.Agent.Controls.Models +namespace OpenSky.Agent.Simulator.Controls.Models { /// ------------------------------------------------------------------------------------------------- /// diff --git a/OpenSky.Agent/Controls/OpenSkyMessageBox.xaml b/OpenSky.Agent.Simulator/Controls/OpenSkyMessageBox.xaml similarity index 97% rename from OpenSky.Agent/Controls/OpenSkyMessageBox.xaml rename to OpenSky.Agent.Simulator/Controls/OpenSkyMessageBox.xaml index 57a6622..d66887b 100644 --- a/OpenSky.Agent/Controls/OpenSkyMessageBox.xaml +++ b/OpenSky.Agent.Simulator/Controls/OpenSkyMessageBox.xaml @@ -9,13 +9,13 @@ ==================================================================================================================== --> - diff --git a/OpenSky.Agent/Controls/OpenSkyMessageBox.xaml.cs b/OpenSky.Agent.Simulator/Controls/OpenSkyMessageBox.xaml.cs similarity index 99% rename from OpenSky.Agent/Controls/OpenSkyMessageBox.xaml.cs rename to OpenSky.Agent.Simulator/Controls/OpenSkyMessageBox.xaml.cs index f8ec37f..1abec36 100644 --- a/OpenSky.Agent/Controls/OpenSkyMessageBox.xaml.cs +++ b/OpenSky.Agent.Simulator/Controls/OpenSkyMessageBox.xaml.cs @@ -4,7 +4,7 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace OpenSky.Agent.Controls +namespace OpenSky.Agent.Simulator.Controls { using System; using System.Media; @@ -13,7 +13,7 @@ namespace OpenSky.Agent.Controls using System.Windows.Input; using System.Windows.Media; - using OpenSky.Agent.Controls.Models; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.Tools; /// ------------------------------------------------------------------------------------------------- diff --git a/OpenSky.Agent/Controls/OpenSkyNotification.xaml b/OpenSky.Agent.Simulator/Controls/OpenSkyNotification.xaml similarity index 96% rename from OpenSky.Agent/Controls/OpenSkyNotification.xaml rename to OpenSky.Agent.Simulator/Controls/OpenSkyNotification.xaml index 2e01f3c..4b1c159 100644 --- a/OpenSky.Agent/Controls/OpenSkyNotification.xaml +++ b/OpenSky.Agent.Simulator/Controls/OpenSkyNotification.xaml @@ -9,13 +9,13 @@ ==================================================================================================================== --> - diff --git a/OpenSky.Agent/Controls/OpenSkyNotification.xaml.cs b/OpenSky.Agent.Simulator/Controls/OpenSkyNotification.xaml.cs similarity index 99% rename from OpenSky.Agent/Controls/OpenSkyNotification.xaml.cs rename to OpenSky.Agent.Simulator/Controls/OpenSkyNotification.xaml.cs index 51e4593..469d17a 100644 --- a/OpenSky.Agent/Controls/OpenSkyNotification.xaml.cs +++ b/OpenSky.Agent.Simulator/Controls/OpenSkyNotification.xaml.cs @@ -4,14 +4,14 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace OpenSky.Agent.Controls +namespace OpenSky.Agent.Simulator.Controls { using System; using System.Threading; using System.Windows; using System.Windows.Media; - using OpenSky.Agent.Controls.Models; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.Tools; /// ------------------------------------------------------------------------------------------------- diff --git a/OpenSky.Agent.Simulator/OpenSky.Agent.Simulator.csproj b/OpenSky.Agent.Simulator/OpenSky.Agent.Simulator.csproj index 9615919..0762a6b 100644 --- a/OpenSky.Agent.Simulator/OpenSky.Agent.Simulator.csproj +++ b/OpenSky.Agent.Simulator/OpenSky.Agent.Simulator.csproj @@ -48,14 +48,17 @@ ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll - - ..\packages\OpenSky.FlightLogXML.0.1.7\lib\net48\OpenSky.FlightLogXML.dll + + ..\packages\OpenSky.FlightLogXML.0.1.8\lib\net48\OpenSky.FlightLogXML.dll ..\packages\MSFT.ParallelExtensionsExtras.1.2.0\lib\ParallelExtensionsExtras.dll + + ..\packages\Syncfusion.SfProgressBar.WPF.23.2.7\lib\net46\Syncfusion.SfProgressBar.WPF.dll + @@ -79,6 +82,15 @@ + + + + + OpenSkyMessageBox.xaml + + + OpenSkyNotification.xaml + @@ -137,6 +149,7 @@ + @@ -149,6 +162,14 @@ + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + Designer MSBuild:Compile @@ -161,5 +182,6 @@ + \ No newline at end of file diff --git a/OpenSky.Agent.Simulator/Properties/AssemblyInfo.cs b/OpenSky.Agent.Simulator/Properties/AssemblyInfo.cs index f315046..ae20885 100644 --- a/OpenSky.Agent.Simulator/Properties/AssemblyInfo.cs +++ b/OpenSky.Agent.Simulator/Properties/AssemblyInfo.cs @@ -5,6 +5,7 @@ // -------------------------------------------------------------------------------------------------------------------- using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; [assembly: AssemblyTitle("OpenSky.Agent.Simulator")] @@ -19,3 +20,4 @@ [assembly: Guid("30c467e8-2eee-41e5-be01-0142a61ba171")] [assembly: AssemblyVersion("0.5.9")] [assembly: AssemblyFileVersion("0.5.9")] +[assembly: InternalsVisibleTo("OpenSky.Agent")] diff --git a/OpenSky.Agent.Simulator/Simulator.Flight.cs b/OpenSky.Agent.Simulator/Simulator.Flight.cs index d6c8d2e..e3d4d73 100644 --- a/OpenSky.Agent.Simulator/Simulator.Flight.cs +++ b/OpenSky.Agent.Simulator/Simulator.Flight.cs @@ -7,6 +7,7 @@ namespace OpenSky.Agent.Simulator { using System; + using System.ComponentModel; using System.Device.Location; using System.Diagnostics; using System.IO; @@ -21,6 +22,8 @@ namespace OpenSky.Agent.Simulator using Microsoft.Maps.MapControl.WPF; + using OpenSky.Agent.Simulator.Controls; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.Enums; using OpenSky.Agent.Simulator.Models; using OpenSky.Agent.Simulator.Tools; @@ -159,6 +162,13 @@ public partial class Simulator /// ------------------------------------------------------------------------------------------------- public event EventHandler TrackingStatusChanged; + /// ------------------------------------------------------------------------------------------------- + /// + /// Occurs when a messageBox was created and should be displayed to the user. + /// + /// ------------------------------------------------------------------------------------------------- + public event EventHandler MessageBoxCreated; + /// ------------------------------------------------------------------------------------------------- /// /// Gets a value indicating whether we can start tracking the current flight or not. @@ -186,6 +196,14 @@ public bool CanStartTracking } } + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets a value indicating whether we can finish tracking the current flight or not - this is + /// not aborting, this is wrapping up and submitting pirep finish. + /// + /// ------------------------------------------------------------------------------------------------- + public bool CanFinishTracking => this.WasAirborne && !this.SecondaryTracking.EngineRunning && this.PrimaryTracking.OnGround && this.PrimaryTracking.GroundSpeed < 1 && this.TrackingStatus == TrackingStatus.Tracking; + /// ------------------------------------------------------------------------------------------------- /// /// Gets or sets the flight the player wants to track. @@ -521,7 +539,7 @@ public void StartTracking() // Check if we just resumed a save that failed to upload if (this.FlightPhase == FlightPhase.PostFlight) { - this.FinishUpFlightTracking(); + this.PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.CanFinishTracking))); } } @@ -578,6 +596,11 @@ public void StopTracking(bool resumeLater) this.lastFlightLogAutoSave = DateTime.MinValue; this.lastPositionReportUpload = DateTime.MinValue; this.lastAutoSaveUpload = DateTime.MinValue; + this.FinalTouchDownIndex = -1; + this.plaQueueIndex = 0; + this.plaQueueCountdown = -1; + this.wasCrash = false; + this.plaQueue = new LandingAnalysis[100]; this.DeleteSaveFile(); if (this.Flight != null) @@ -812,6 +835,7 @@ private void DeleteSaveFile() } } + /// ------------------------------------------------------------------------------------------------- /// /// Finishes up the flight tracking. @@ -820,123 +844,220 @@ private void DeleteSaveFile() /// sushi.at, 22/03/2021. /// /// ------------------------------------------------------------------------------------------------- - private void FinishUpFlightTracking() + public void FinishUpFlightTracking() { Debug.WriteLine("SimConnect finishing up flight tracking started"); - var callStopFlight = false; - if (this.Flight != null) + if (!this.wasCrash) { - try + if (!this.CanFinishTracking) + { + Debug.WriteLine("Can't actually finish tracking right now..."); + return; + } + + // Did the user shut the engines off on the runway or was there some taxi to parking? + if (this.taxiInStarted) { - Debug.WriteLine("Creating final position report..."); - var onlineForDuration = this.OnlineNetworkConnectionDuration; - if (this.OnlineNetworkConnectionStarted.HasValue) + if (!this.taxiInTurned) { - onlineForDuration += (DateTime.UtcNow - this.OnlineNetworkConnectionStarted.Value); + this.AddTrackingEvent(this.PrimaryTracking, this.SecondaryTracking, FlightTrackingEventType.EngineOffRunway, OpenSkyColors.OpenSkyWarningOrange, "Engine turned off on the runway?"); } - var positionReport = new PositionReport - { - Id = this.Flight.Id, - AirspeedTrue = this.PrimaryTracking.AirspeedTrue, - Altitude = this.PrimaryTracking.Altitude, - BankAngle = this.PrimaryTracking.BankAngle, - FlightPhase = this.PrimaryTracking.Crash ? FlightPhase.Crashed : this.FlightPhase, - GroundSpeed = this.PrimaryTracking.GroundSpeed, - Heading = this.PrimaryTracking.Heading, - Latitude = this.PrimaryTracking.Latitude, - Longitude = this.PrimaryTracking.Longitude, - OnGround = this.PrimaryTracking.OnGround, - PitchAngle = this.PrimaryTracking.PitchAngle, - RadioHeight = this.PrimaryTracking.RadioHeight, - VerticalSpeedSeconds = this.PrimaryTracking.VerticalSpeedSeconds, - TimeWarpTimeSavedSeconds = (int)this.timeSavedBecauseOfSimRate.TotalSeconds, - ConnectedToOnlineNetworkSeconds = (int)onlineForDuration.TotalSeconds, - - FuelTankCenterQuantity = this.FuelTanks.FuelTankCenterQuantity, - FuelTankCenter2Quantity = this.FuelTanks.FuelTankCenter2Quantity, - FuelTankCenter3Quantity = this.FuelTanks.FuelTankCenter3Quantity, - FuelTankLeftMainQuantity = this.FuelTanks.FuelTankLeftMainQuantity, - FuelTankLeftAuxQuantity = this.FuelTanks.FuelTankLeftAuxQuantity, - FuelTankLeftTipQuantity = this.FuelTanks.FuelTankLeftTipQuantity, - FuelTankRightMainQuantity = this.FuelTanks.FuelTankRightMainQuantity, - FuelTankRightAuxQuantity = this.FuelTanks.FuelTankRightAuxQuantity, - FuelTankRightTipQuantity = this.FuelTanks.FuelTankRightTipQuantity, - FuelTankExternal1Quantity = this.FuelTanks.FuelTankExternal1Quantity, - FuelTankExternal2Quantity = this.FuelTanks.FuelTankExternal2Quantity - }; + this.taxiInStarted = false; + } + + // Add one last position report + UpdateGUIDelegate addPositionReport = () => + { + this.AircraftTrailLocations.Add(new AircraftTrailLocation(DateTime.UtcNow, this.PrimaryTracking, this.SecondaryTracking, this.WeightAndBalance.FuelTotalQuantity)); + this.TrackingEventMarkerAdded?.Invoke(this, new TrackingEventMarker(this.PrimaryTracking, this.SecondaryTracking, this.WeightAndBalance.FuelTotalQuantity, 8, OpenSkyColors.OpenSkyTeal, "Position report")); + }; + Application.Current.Dispatcher.Invoke(addPositionReport); - this.RefreshModelNow(Requests.FuelTanks); - this.RefreshModelNow(Requests.PayloadStations); - Thread.Sleep(500); + this.AddTrackingEvent(this.PrimaryTracking, this.SecondaryTracking, FlightTrackingEventType.TrackingStopped, OpenSkyColors.OpenSkyTealLight, "Flight tracking stopped"); - var saveFile = this.GenerateSaveFile(); - if (saveFile != null) + // Play flight complete audio + SpeechSoundPacks.Instance.PlaySpeechEvent(SpeechEvent.FlightCompleteSubmitting); + } + + var callStopFlight = false; + if (this.Flight != null) + { + var successfullOrAbortedOuter = false; + while (!successfullOrAbortedOuter) + { + try { - var xmlStream = new MemoryStream(Encoding.UTF8.GetBytes($"{saveFile}")); - xmlStream.Seek(0, SeekOrigin.Begin); - var targetMemoryStream = new MemoryStream(); - byte[] zippedBytes; - using (var gzip = new GZipStream(targetMemoryStream, CompressionMode.Compress)) + Debug.WriteLine("Creating final position report..."); + var onlineForDuration = this.OnlineNetworkConnectionDuration; + if (this.OnlineNetworkConnectionStarted.HasValue) { - xmlStream.CopyTo(gzip); - gzip.Close(); - zippedBytes = targetMemoryStream.ToArray(); + onlineForDuration += (DateTime.UtcNow - this.OnlineNetworkConnectionStarted.Value); } - var base64String = Convert.ToBase64String(zippedBytes); - - Debug.WriteLine("Submitting final report to OpenSky server..."); - var finalReport = new FinalReport + var positionReport = new PositionReport { - FinalPositionReport = positionReport, - FlightLog = base64String + Id = this.Flight.Id, + AirspeedTrue = this.PrimaryTracking.AirspeedTrue, + Altitude = this.PrimaryTracking.Altitude, + BankAngle = this.PrimaryTracking.BankAngle, + FlightPhase = this.PrimaryTracking.Crash ? FlightPhase.Crashed : this.FlightPhase, + GroundSpeed = this.PrimaryTracking.GroundSpeed, + Heading = this.PrimaryTracking.Heading, + Latitude = this.PrimaryTracking.Latitude, + Longitude = this.PrimaryTracking.Longitude, + OnGround = this.PrimaryTracking.OnGround, + PitchAngle = this.PrimaryTracking.PitchAngle, + RadioHeight = this.PrimaryTracking.RadioHeight, + VerticalSpeedSeconds = this.PrimaryTracking.VerticalSpeedSeconds, + TimeWarpTimeSavedSeconds = (int)this.timeSavedBecauseOfSimRate.TotalSeconds, + ConnectedToOnlineNetworkSeconds = (int)onlineForDuration.TotalSeconds, + + FuelTankCenterQuantity = this.FuelTanks.FuelTankCenterQuantity, + FuelTankCenter2Quantity = this.FuelTanks.FuelTankCenter2Quantity, + FuelTankCenter3Quantity = this.FuelTanks.FuelTankCenter3Quantity, + FuelTankLeftMainQuantity = this.FuelTanks.FuelTankLeftMainQuantity, + FuelTankLeftAuxQuantity = this.FuelTanks.FuelTankLeftAuxQuantity, + FuelTankLeftTipQuantity = this.FuelTanks.FuelTankLeftTipQuantity, + FuelTankRightMainQuantity = this.FuelTanks.FuelTankRightMainQuantity, + FuelTankRightAuxQuantity = this.FuelTanks.FuelTankRightAuxQuantity, + FuelTankRightTipQuantity = this.FuelTanks.FuelTankRightTipQuantity, + FuelTankExternal1Quantity = this.FuelTanks.FuelTankExternal1Quantity, + FuelTankExternal2Quantity = this.FuelTanks.FuelTankExternal2Quantity }; - var result = this.openSkyServiceInstance.CompleteFlightAsync(finalReport).Result; - if (result.IsError) + this.RefreshModelNow(Requests.FuelTanks); + this.RefreshModelNow(Requests.PayloadStations); + Thread.Sleep(500); + + var saveFile = this.GenerateSaveFile(); + if (saveFile != null) { - Debug.WriteLine("Error submitting final flight report: " + result.Message + "\r\n" + result.ErrorDetails); + var xmlStream = new MemoryStream(Encoding.UTF8.GetBytes($"{saveFile}")); + xmlStream.Seek(0, SeekOrigin.Begin); + var targetMemoryStream = new MemoryStream(); + byte[] zippedBytes; + using (var gzip = new GZipStream(targetMemoryStream, CompressionMode.Compress)) + { + xmlStream.CopyTo(gzip); + gzip.Close(); + zippedBytes = targetMemoryStream.ToArray(); + } - // todo how to handle this? save to a special file or only offer retry? Or does the user have to resume from the last save? + var base64String = Convert.ToBase64String(zippedBytes); + + Debug.WriteLine("Submitting final report to OpenSky server..."); + var finalReport = new FinalReport + { + FinalPositionReport = positionReport, + FlightLog = base64String + }; + + var successfullOrAbortedInner = false; + while (!successfullOrAbortedInner) + { + var result = this.openSkyServiceInstance.CompleteFlightAsync(finalReport).Result; + if (result.IsError) + { + Debug.WriteLine("Error submitting final flight report: " + result.Message + "\r\n" + result.ErrorDetails); + + ExtendedMessageBoxResult? answer = null; + UpdateGUIDelegate reportError = () => + { + var messageBox = new OpenSkyMessageBox( + "Error submitting completed flight", + $"Received error from OpenSky server, do you want to retry submitting?\r\n\r\nDetails: {result.Message}", + MessageBoxButton.YesNo, + ExtendedMessageBoxImage.Warning); + messageBox.SetErrorColorStyle(); + messageBox.Closed += (_, _) => { answer = messageBox.Result; }; + this.MessageBoxCreated?.Invoke(this, messageBox); + }; + Application.Current.Dispatcher.BeginInvoke(reportError); + while (answer == null && !SleepScheduler.IsShutdownInProgress) + { + Thread.Sleep(500); + } + + if (answer == ExtendedMessageBoxResult.No) + { + successfullOrAbortedInner = true; + } + } + else + { + Debug.WriteLine("Submitting final report to OpenSky server successfully!"); + callStopFlight = true; + successfullOrAbortedInner = true; + successfullOrAbortedOuter = true; + } + } } - else + } + catch (AbandonedMutexException) + { + // Ignore + } + catch (Exception ex) + { + Debug.WriteLine("Error submitting final flight report: " + ex); + + ExtendedMessageBoxResult? answer = null; + UpdateGUIDelegate reportError = () => { - Debug.WriteLine("Submitting final report to OpenSky server successfully!"); - callStopFlight = true; + var messageBox = new OpenSkyMessageBox( + "Error submitting completed flight", + $"Encountered error, do you want to retry submitting?\r\n\r\nDetails: {ex.Message}", + MessageBoxButton.YesNo, + ExtendedMessageBoxImage.Error); + messageBox.SetErrorColorStyle(); + messageBox.ExceptionText.Text = ex.ToString(); + messageBox.ExceptionText.Visibility = Visibility.Visible; + messageBox.Closed += (_, _) => { answer = messageBox.Result; }; + this.MessageBoxCreated?.Invoke(this, messageBox); + }; + Application.Current.Dispatcher.BeginInvoke(reportError); + while (answer == null && !SleepScheduler.IsShutdownInProgress) + { + Thread.Sleep(500); } - } - } - catch (AbandonedMutexException) - { - // Ignore - } - catch (Exception ex) - { - Debug.WriteLine("Error submitting final flight report: " + ex); - // todo how to handle this? save to a special file or only offer retry? Or does the user have to resume from the last save? - } - finally - { - try - { - this.flightSaveMutex.ReleaseMutex(); + if (answer == ExtendedMessageBoxResult.No) + { + successfullOrAbortedOuter = true; + } } - catch + finally { - // Ignore + try + { + this.flightSaveMutex.ReleaseMutex(); + } + catch + { + // Ignore + } + } } } else { - Debug.WriteLine("CRITICAL: Unable to finish up flight tracking and submitting final log because SimConnect.Flight is NULL!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + UpdateGUIDelegate badNews = () => + { + var messageBox = new OpenSkyMessageBox( + "Critical error", + "CRITICAL: Unable to finish up flight tracking and submitting final log because SimConnect.Flight is NULL!\r\n\r\nPlease report this as a bug report with as much detail as possible on the OpenSky discord.\r\n\r\nYou can try to resume the flight from the last auto-save.", + MessageBoxButton.OK, + ExtendedMessageBoxImage.Hand); + this.MessageBoxCreated?.Invoke(this, messageBox); + }; + Application.Current.Dispatcher.BeginInvoke(badNews); + callStopFlight = true; } - // todo only call this if the saving/etc. worked, ask user for retry etc. if (callStopFlight) { this.DeleteSaveFile(); @@ -1196,6 +1317,8 @@ private void TrackFlight(ProcessPrimaryTracking ppt) if (this.PrimaryTracking.Crash) { + this.wasCrash = true; + // Plane crashed this.AddTrackingEvent(ppt.New, this.SecondaryTracking, FlightTrackingEventType.Crashed, OpenSkyColors.OpenSkyRed, "Aircraft crashed"); @@ -1210,7 +1333,7 @@ private void TrackFlight(ProcessPrimaryTracking ppt) this.SaveFlight(); } - // Do a position report? + // Report position? if ((DateTime.UtcNow - this.lastPositionReportUpload).TotalSeconds > 30) { this.UploadPositionReport(); @@ -1224,6 +1347,13 @@ private void TrackFlight(ProcessPrimaryTracking ppt) } } + /// ------------------------------------------------------------------------------------------------- + /// + /// Did a crash end the flight?. + /// + /// ------------------------------------------------------------------------------------------------- + private bool wasCrash; + /// ------------------------------------------------------------------------------------------------- /// /// Upload auto-save to OpenSky. diff --git a/OpenSky.Agent.Simulator/Simulator.Metar.cs b/OpenSky.Agent.Simulator/Simulator.Metar.cs index f678a68..22d1ee0 100644 --- a/OpenSky.Agent.Simulator/Simulator.Metar.cs +++ b/OpenSky.Agent.Simulator/Simulator.Metar.cs @@ -23,21 +23,21 @@ public partial class Simulator /// The alternate metar. /// /// ------------------------------------------------------------------------------------------------- - private string alternateMetar = "???"; + private string alternateMetar = "???\n"; /// ------------------------------------------------------------------------------------------------- /// /// Destination metar. /// /// ------------------------------------------------------------------------------------------------- - private string destinationMetar = "???"; + private string destinationMetar = "???\n"; /// ------------------------------------------------------------------------------------------------- /// /// The origin metar. /// /// ------------------------------------------------------------------------------------------------- - private string originMetar = "???"; + private string originMetar = "???\n"; /// ------------------------------------------------------------------------------------------------- /// @@ -122,10 +122,14 @@ public void RefreshMetar() { var url = $"https://aviationweather.gov/api/data/metar?ids={this.Flight.Origin.Icao}"; this.OriginMetar = client.DownloadString(url); + if (string.IsNullOrEmpty(this.OriginMetar)) + { + this.OriginMetar = "Unavailable\n"; + } } else { - this.OriginMetar = "???"; + this.OriginMetar = "???\n"; } } catch (Exception ex) @@ -140,10 +144,14 @@ public void RefreshMetar() { var url = $"https://aviationweather.gov/api/data/metar?ids={this.Flight.Destination.Icao}"; this.DestinationMetar = client.DownloadString(url); + if (string.IsNullOrEmpty(this.DestinationMetar)) + { + this.DestinationMetar = "Unavailable\n"; + } } else { - this.DestinationMetar = "???"; + this.DestinationMetar = "???\n"; } } catch (Exception ex) @@ -158,10 +166,14 @@ public void RefreshMetar() { var url = $"https://aviationweather.gov/api/data/metar?ids={this.Flight.Alternate.Icao}"; this.AlternateMetar = client.DownloadString(url); + if (string.IsNullOrEmpty(this.AlternateMetar)) + { + this.AlternateMetar = "Unavailable\n"; + } } else { - this.AlternateMetar = "???"; + this.AlternateMetar = "???\n"; } } catch (Exception ex) diff --git a/OpenSky.Agent.Simulator/Simulator.Process.FlightPhases.cs b/OpenSky.Agent.Simulator/Simulator.Process.FlightPhases.cs index 39f1d5a..eadb390 100644 --- a/OpenSky.Agent.Simulator/Simulator.Process.FlightPhases.cs +++ b/OpenSky.Agent.Simulator/Simulator.Process.FlightPhases.cs @@ -349,7 +349,7 @@ private void TransitionFlightPhase() // Landing if (this.WasAirborne && this.PrimaryTracking.RadioHeight <= 500 && this.PrimaryTracking.GroundSpeed >= 40 && this.SecondaryTracking.EngineRunning) { - // Landing is allowed to override descent in case of return to origin airport or I guess landing anywhere but the destination/alternate + // Landing is allowed to override descent in case of return to origin airport, or I guess landing anywhere but the destination/alternate if (unknownFlightPhase || this.FlightPhase == FlightPhase.Descent) { this.FlightPhase = FlightPhase.Landing; @@ -393,7 +393,7 @@ private void TransitionFlightPhase() if (unknownFlightPhase) { this.FlightPhase = FlightPhase.PostFlight; - this.NextFlightStep = "All done, saving report and submitting to OpenSky..."; + this.NextFlightStep = "All done, secure the aircraft and hit \"Complete Flight\" when ready..."; newNextStepFlashing = true; unknownFlightPhase = false; } diff --git a/OpenSky.Agent.Simulator/Simulator.Process.Landing.cs b/OpenSky.Agent.Simulator/Simulator.Process.Landing.cs index 6424b50..77f36b7 100644 --- a/OpenSky.Agent.Simulator/Simulator.Process.Landing.cs +++ b/OpenSky.Agent.Simulator/Simulator.Process.Landing.cs @@ -23,10 +23,33 @@ public partial class Simulator { /// ------------------------------------------------------------------------------------------------- /// - /// Gets the landing reports. + /// (Immutable) The process landing analysis queue (to monitor values like g-force over a period + /// of reports) /// /// ------------------------------------------------------------------------------------------------- - public ObservableCollection LandingReports { get; } + private LandingAnalysis[] plaQueue = new LandingAnalysis[100]; + + /// ------------------------------------------------------------------------------------------------- + /// + /// The last touchdown even recorded (to group bounces etc., before creating new touchdown event). + /// + /// ------------------------------------------------------------------------------------------------- + private DateTime lastTouchdown = DateTime.MinValue; + + /// ------------------------------------------------------------------------------------------------- + /// + /// The pla queue countdown after initial touchdown (to monitor values like g-force for a short + /// period after touchdown) + /// + /// ------------------------------------------------------------------------------------------------- + private int plaQueueCountdown = -1; + + /// ------------------------------------------------------------------------------------------------- + /// + /// The current pla queue index. + /// + /// ------------------------------------------------------------------------------------------------- + private int plaQueueIndex; /// ------------------------------------------------------------------------------------------------- /// @@ -35,6 +58,21 @@ public partial class Simulator /// ------------------------------------------------------------------------------------------------- public event EventHandler LandingReported; + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets the zero-based index of the final touch down landing report (in case there are multiple + /// like a bounce on takeoff). + /// + /// ------------------------------------------------------------------------------------------------- + public int FinalTouchDownIndex { get; private set; } = -1; + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets the landing reports. + /// + /// ------------------------------------------------------------------------------------------------- + public ObservableCollection LandingReports { get; } + /// ------------------------------------------------------------------------------------------------- /// /// Check for and analyse landings. @@ -50,15 +88,26 @@ private void CheckForAndAnalyseLanding(ProcessLandingAnalysis pla) { if (this.TrackingStatus == TrackingStatus.Tracking && !pla.Old.OnGround && pla.New.OnGround) { - // We have touchdown + // We have a touchdown Debug.WriteLine("Adding new landing report"); + + // Check for max g-force before touchdown + var maxGeforce = Math.Max(pla.Old.Gforce, pla.New.Gforce); + for (var i = 0; i < 100; i++) + { + if (this.plaQueue[i] != null) + { + maxGeforce = Math.Max(maxGeforce, this.plaQueue[i].Gforce); + } + } + var landingReport = new TouchDown( DateTime.UtcNow, pla.New.Location.Latitude, pla.New.Location.Longitude, (int)pla.New.Location.Altitude, pla.New.LandingRate * -1.0, - Math.Max(pla.Old.Gforce, pla.New.Gforce), + maxGeforce, pla.New.SpeedLong, pla.New.SpeedLat, pla.New.WindLong, @@ -68,14 +117,47 @@ private void CheckForAndAnalyseLanding(ProcessLandingAnalysis pla) pla.New.AirspeedTrue ); + // Add to the list and start the countdown this.LandingReports.Add(landingReport); - - if (this.LandingReports.Count == 1) + if ((DateTime.UtcNow - this.lastTouchdown).TotalSeconds > 30) { - // First landing for this flight this.AddTrackingEvent(this.PrimaryTracking, this.SecondaryTracking, FlightTrackingEventType.Touchdown, OpenSkyColors.OpenSkyTeal, "Touchdown"); - this.LandingReported?.Invoke(this, LandingReportNotification.AsSoonAsPossible); + this.FinalTouchDownIndex = this.LandingReports.Count - 1; + this.plaQueueCountdown = 100; + } + + this.lastTouchdown = DateTime.UtcNow; + } + + this.plaQueue[this.plaQueueIndex++] = pla.New; + if (this.plaQueueIndex == 100) + { + this.plaQueueIndex = 0; + } + + if (this.plaQueueCountdown > 0) + { + this.plaQueueCountdown--; + } + + if (this.plaQueueCountdown == 0 && this.FinalTouchDownIndex >= 0) + { + this.plaQueueCountdown = -1; + + // Check for max g-force after touchdown + var maxGeforce = this.LandingReports[this.FinalTouchDownIndex].GForce; + for (var i = 0; i < 100; i++) + { + if (this.plaQueue[i] != null) + { + maxGeforce = Math.Max(maxGeforce, this.plaQueue[i].Gforce); + } } + + this.LandingReports[this.FinalTouchDownIndex].GForce = maxGeforce; + + // First landing for this flight + this.LandingReported?.Invoke(this, LandingReportNotification.AsSoonAsPossible); } } } diff --git a/OpenSky.Agent.Simulator/Simulator.Process.Systems.cs b/OpenSky.Agent.Simulator/Simulator.Process.Systems.cs index 29fee25..887184d 100644 --- a/OpenSky.Agent.Simulator/Simulator.Process.Systems.cs +++ b/OpenSky.Agent.Simulator/Simulator.Process.Systems.cs @@ -7,18 +7,15 @@ namespace OpenSky.Agent.Simulator { using System; + using System.ComponentModel; using System.Diagnostics; using System.Media; using System.Reflection; - using System.Windows; using OpenSky.Agent.Simulator.Enums; using OpenSky.Agent.Simulator.Models; - using OpenSky.Agent.Simulator.Tools; using OpenSky.FlightLogXML; - using TrackingEventMarker = OpenSky.Agent.Simulator.Models.TrackingEventMarker; - /// ------------------------------------------------------------------------------------------------- /// /// Simulator interface - systems processing. @@ -198,43 +195,13 @@ private void MonitorSecondarySystems(ProcessSecondaryTracking pst) $"OpenSky Warning: Taxi and/or Landing light on when engine was turned {(pst.New.EngineRunning ? "on" : "off")}"); } - // Was the engine turned off on the ground, not moving, while tracking? -> End tracking session, save reports and report to API - if (!pst.New.EngineRunning && this.PrimaryTracking.OnGround && this.PrimaryTracking.GroundSpeed < 1 && this.TrackingStatus == TrackingStatus.Tracking) + // Was the engine turned off on the ground, not moving, while tracking? -> Report that we can now finish up tracking + if (!pst.New.EngineRunning && this.PrimaryTracking.OnGround && this.PrimaryTracking.GroundSpeed < 1 && this.TrackingStatus == TrackingStatus.Tracking && this.WasAirborne) { - // todo expand this to battery off and proper shutdown/secure flow for extra xp (enable/disable in the settings) + this.PropertyChanged(this, new PropertyChangedEventArgs(nameof(this.CanFinishTracking))); - if (this.WasAirborne) - { - Debug.WriteLine("Engine was turned off, wrapping up flight"); - - // Did the user shut the engines off on the runway or was there some taxi to parking? - if (this.taxiInStarted) - { - if (!this.taxiInTurned) - { - this.AddTrackingEvent(this.PrimaryTracking, pst.New, FlightTrackingEventType.EngineOffRunway, OpenSkyColors.OpenSkyWarningOrange, "Engine turned off on the runway?"); - } - - this.taxiInStarted = false; - } - - // Add one last position report - UpdateGUIDelegate addPositionReport = () => - { - this.AircraftTrailLocations.Add(new AircraftTrailLocation(DateTime.UtcNow, this.PrimaryTracking, pst.New, this.WeightAndBalance.FuelTotalQuantity)); - this.TrackingEventMarkerAdded?.Invoke(this, new TrackingEventMarker(this.PrimaryTracking, pst.New, this.WeightAndBalance.FuelTotalQuantity, 8, OpenSkyColors.OpenSkyTeal, "Position report")); - }; - Application.Current.Dispatcher.Invoke(addPositionReport); - - this.AddTrackingEvent(this.PrimaryTracking, pst.New, FlightTrackingEventType.TrackingStopped, OpenSkyColors.OpenSkyTealLight, "Flight tracking stopped"); - - // Show landing report notification now? - this.LandingReported?.Invoke(this, LandingReportNotification.AfterTurningEnginesOff); - - // Actually finish up the tracking session now - SpeechSoundPacks.Instance.PlaySpeechEvent(SpeechEvent.FlightCompleteSubmitting); - this.FinishUpFlightTracking(); - } + // Show landing report notification now? + this.LandingReported?.Invoke(this, LandingReportNotification.AfterTurningEnginesOff); } } diff --git a/OpenSky.Agent.Simulator/Simulator.Process.cs b/OpenSky.Agent.Simulator/Simulator.Process.cs index 008b6e2..754e9d0 100644 --- a/OpenSky.Agent.Simulator/Simulator.Process.cs +++ b/OpenSky.Agent.Simulator/Simulator.Process.cs @@ -379,7 +379,7 @@ private void ProcessPrimaryTracking() } // Are we close to landing? - this.SampleRates[Requests.LandingAnalysis] = this.WasAirborne && ppt.New.RadioHeight < 500 ? 25 : 500; + this.SampleRates[Requests.LandingAnalysis] = this.WasAirborne && ppt.New.RadioHeight < 200 ? 25 : 500; this.OnPropertyChanged(nameof(this.SampleRates)); // Was the sim paused/un-paused? diff --git a/OpenSky.Agent.Simulator/Simulator.SaveLoadXML.cs b/OpenSky.Agent.Simulator/Simulator.SaveLoadXML.cs index 03ad91d..65cfc24 100644 --- a/OpenSky.Agent.Simulator/Simulator.SaveLoadXML.cs +++ b/OpenSky.Agent.Simulator/Simulator.SaveLoadXML.cs @@ -120,6 +120,7 @@ private XElement GenerateSaveFile() log.PositionReports.AddRange(this.AircraftTrailLocations.Cast().Select(loc => loc.Position)); log.TouchDowns.AddRange(this.LandingReports); + log.FinalTouchDownIndex = this.FinalTouchDownIndex; log.NavLogWaypoints.AddRange(this.simbriefWaypointMarkers.Select(w => w.WayPoint)); return log.GenerateFlightLog(); @@ -274,6 +275,8 @@ private void RestoreSaveFile(string saveFile) this.LandingReports.Add(touchdown); } + this.FinalTouchDownIndex = log.FinalTouchDownIndex; + // Restore simbrief waypoint markers UpdateGUIDelegate restoreSimbrief = () => { diff --git a/OpenSky.Agent/Tools/VisibilityAnimation.cs b/OpenSky.Agent.Simulator/Tools/VisibilityAnimation.cs similarity index 99% rename from OpenSky.Agent/Tools/VisibilityAnimation.cs rename to OpenSky.Agent.Simulator/Tools/VisibilityAnimation.cs index 10ebbd7..4ced522 100644 --- a/OpenSky.Agent/Tools/VisibilityAnimation.cs +++ b/OpenSky.Agent.Simulator/Tools/VisibilityAnimation.cs @@ -4,7 +4,7 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace OpenSky.Agent.Tools +namespace OpenSky.Agent.Simulator.Tools { using System; using System.Collections.Generic; diff --git a/OpenSky.Agent.Simulator/packages.config b/OpenSky.Agent.Simulator/packages.config index 38ee8f9..7376c9b 100644 --- a/OpenSky.Agent.Simulator/packages.config +++ b/OpenSky.Agent.Simulator/packages.config @@ -5,7 +5,10 @@ - + + + + \ No newline at end of file diff --git a/OpenSky.Agent.UdpXPlane11/OpenSky.Agent.UdpXPlane11.csproj b/OpenSky.Agent.UdpXPlane11/OpenSky.Agent.UdpXPlane11.csproj index c52326a..f930172 100644 --- a/OpenSky.Agent.UdpXPlane11/OpenSky.Agent.UdpXPlane11.csproj +++ b/OpenSky.Agent.UdpXPlane11/OpenSky.Agent.UdpXPlane11.csproj @@ -40,8 +40,8 @@ ..\packages\JetBrains.Annotations.2023.3.0\lib\net20\JetBrains.Annotations.dll - - ..\packages\OpenSky.FlightLogXML.0.1.7\lib\net48\OpenSky.FlightLogXML.dll + + ..\packages\OpenSky.FlightLogXML.0.1.8\lib\net48\OpenSky.FlightLogXML.dll ..\packages\MSFT.ParallelExtensionsExtras.1.2.0\lib\ParallelExtensionsExtras.dll diff --git a/OpenSky.Agent.UdpXPlane11/packages.config b/OpenSky.Agent.UdpXPlane11/packages.config index 7810f95..fcbd655 100644 --- a/OpenSky.Agent.UdpXPlane11/packages.config +++ b/OpenSky.Agent.UdpXPlane11/packages.config @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/OpenSky.Agent.sln.DotSettings b/OpenSky.Agent.sln.DotSettings index ad724fa..890113d 100644 --- a/OpenSky.Agent.sln.DotSettings +++ b/OpenSky.Agent.sln.DotSettings @@ -607,6 +607,7 @@ OpenSky project ${CurrentDate.Year} True True True + True True True True diff --git a/OpenSky.Agent/Controls/OpenSkyWindow.cs b/OpenSky.Agent/Controls/OpenSkyWindow.cs index 70d5044..1d1b592 100644 --- a/OpenSky.Agent/Controls/OpenSkyWindow.cs +++ b/OpenSky.Agent/Controls/OpenSkyWindow.cs @@ -19,7 +19,8 @@ namespace OpenSky.Agent.Controls using System.Windows.Interop; using System.Windows.Shapes; - using OpenSky.Agent.Controls.Models; + using OpenSky.Agent.Simulator.Controls; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.Tools; using Button = System.Windows.Controls.Button; diff --git a/OpenSky.Agent/OpenSky.Agent.csproj b/OpenSky.Agent/OpenSky.Agent.csproj index 01f12ce..8e0bc11 100644 --- a/OpenSky.Agent/OpenSky.Agent.csproj +++ b/OpenSky.Agent/OpenSky.Agent.csproj @@ -109,15 +109,6 @@ FuelTankControl.xaml - - - - - OpenSkyMessageBox.xaml - - - OpenSkyNotification.xaml - PayloadStationControl.xaml @@ -164,7 +155,6 @@ - @@ -221,14 +211,6 @@ Designer MSBuild:Compile - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - Designer MSBuild:Compile @@ -407,7 +389,7 @@ 13.0.3 - 0.1.7 + 0.1.8 23.2.7 diff --git a/OpenSky.Agent/Tools/ExceptionExtensions.cs b/OpenSky.Agent/Tools/ExceptionExtensions.cs index 1086afd..7a2ea63 100644 --- a/OpenSky.Agent/Tools/ExceptionExtensions.cs +++ b/OpenSky.Agent/Tools/ExceptionExtensions.cs @@ -16,8 +16,9 @@ namespace OpenSky.Agent.Tools using Newtonsoft.Json; using OpenSky.Agent.Controls; - using OpenSky.Agent.Controls.Models; using OpenSky.Agent.MVVM; + using OpenSky.Agent.Simulator.Controls; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.OpenAPIs; using OpenSky.Agent.Views; diff --git a/OpenSky.Agent/Views/AircraftTypes.xaml b/OpenSky.Agent/Views/AircraftTypes.xaml index 832edc1..064804b 100644 --- a/OpenSky.Agent/Views/AircraftTypes.xaml +++ b/OpenSky.Agent/Views/AircraftTypes.xaml @@ -22,6 +22,7 @@ xmlns:controls="clr-namespace:OpenSky.Agent.Controls" xmlns:models="clr-namespace:OpenSky.Agent.Views.Models" xmlns:converters="clr-namespace:OpenSky.Agent.Converters" + xmlns:simTools="clr-namespace:OpenSky.Agent.Simulator.Tools;assembly=OpenSky.Agent.Simulator" xmlns:tools="clr-namespace:OpenSky.Agent.Tools" mc:Ignorable="d" d:DesignWidth="2000" Loaded="AircraftTypesOnLoaded" Title="Aircraft Types" Height="800" Width="1400" HorizontalScrollBar="False" VerticalScrollBar="False" LoadingText="{Binding LoadingText}"> @@ -83,7 +84,7 @@ - + Aircraft Type Details @@ -383,7 +384,7 @@ - + Edit aircraft type @@ -529,7 +530,7 @@ - + Add aircraft type @@ -672,7 +673,7 @@ - + Upgrade aircraft types diff --git a/OpenSky.Agent/Views/FlightTracking.xaml b/OpenSky.Agent/Views/FlightTracking.xaml index 87d9f50..77c69e2 100644 --- a/OpenSky.Agent/Views/FlightTracking.xaml +++ b/OpenSky.Agent/Views/FlightTracking.xaml @@ -256,6 +256,7 @@ + Tracking duration Fuel remaining @@ -281,9 +282,10 @@ - [*] Time warp time saved and pause times only update when the sim rate is changed or the sim is resumed. - - Note: You can close this window and OpenSky will monitor your flight in the background... + + [*] Time warp time saved and pause times only update when the sim rate is changed or the sim is resumed. + + Note: You can close this window and OpenSky will monitor your flight in the background... diff --git a/OpenSky.Agent/Views/Models/AddAircraftViewModel.cs b/OpenSky.Agent/Views/Models/AddAircraftViewModel.cs index 2afae86..3c22763 100644 --- a/OpenSky.Agent/Views/Models/AddAircraftViewModel.cs +++ b/OpenSky.Agent/Views/Models/AddAircraftViewModel.cs @@ -13,9 +13,9 @@ namespace OpenSky.Agent.Views.Models using System.Threading; using System.Windows; - using OpenSky.Agent.Controls; - using OpenSky.Agent.Controls.Models; using OpenSky.Agent.MVVM; + using OpenSky.Agent.Simulator.Controls; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.Tools; using OpenSky.Agent.Tools; diff --git a/OpenSky.Agent/Views/Models/AircraftTypesViewModel.cs b/OpenSky.Agent/Views/Models/AircraftTypesViewModel.cs index b742fea..f84ab6d 100644 --- a/OpenSky.Agent/Views/Models/AircraftTypesViewModel.cs +++ b/OpenSky.Agent/Views/Models/AircraftTypesViewModel.cs @@ -18,9 +18,9 @@ namespace OpenSky.Agent.Views.Models using Microsoft.Win32; - using OpenSky.Agent.Controls; - using OpenSky.Agent.Controls.Models; using OpenSky.Agent.MVVM; + using OpenSky.Agent.Simulator.Controls; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.Tools; using OpenSky.Agent.Tools; diff --git a/OpenSky.Agent/Views/Models/AutoUpdateViewModel.cs b/OpenSky.Agent/Views/Models/AutoUpdateViewModel.cs index 76a9e29..122d92e 100644 --- a/OpenSky.Agent/Views/Models/AutoUpdateViewModel.cs +++ b/OpenSky.Agent/Views/Models/AutoUpdateViewModel.cs @@ -16,9 +16,9 @@ namespace OpenSky.Agent.Views.Models using Newtonsoft.Json; - using OpenSky.Agent.Controls; - using OpenSky.Agent.Controls.Models; using OpenSky.Agent.MVVM; + using OpenSky.Agent.Simulator.Controls; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.Tools; using OpenSky.Agent.Tools; diff --git a/OpenSky.Agent/Views/Models/FlightTrackingViewModel.FuelTanks.cs b/OpenSky.Agent/Views/Models/FlightTrackingViewModel.FuelTanks.cs index e17fdc5..de6a057 100644 --- a/OpenSky.Agent/Views/Models/FlightTrackingViewModel.FuelTanks.cs +++ b/OpenSky.Agent/Views/Models/FlightTrackingViewModel.FuelTanks.cs @@ -11,9 +11,9 @@ namespace OpenSky.Agent.Views.Models using System.Collections.Specialized; using System.Diagnostics; - using OpenSky.Agent.Controls; - using OpenSky.Agent.Controls.Models; using OpenSky.Agent.MVVM; + using OpenSky.Agent.Simulator.Controls; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.Enums; /// ------------------------------------------------------------------------------------------------- diff --git a/OpenSky.Agent/Views/Models/FlightTrackingViewModel.Map.cs b/OpenSky.Agent/Views/Models/FlightTrackingViewModel.Map.cs index 604aed8..e67ba56 100644 --- a/OpenSky.Agent/Views/Models/FlightTrackingViewModel.Map.cs +++ b/OpenSky.Agent/Views/Models/FlightTrackingViewModel.Map.cs @@ -18,9 +18,9 @@ namespace OpenSky.Agent.Views.Models using Microsoft.Maps.MapControl.WPF; - using OpenSky.Agent.Controls; - using OpenSky.Agent.Controls.Models; using OpenSky.Agent.MVVM; + using OpenSky.Agent.Simulator.Controls; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.Models; using OpenSky.Agent.Simulator.Tools; diff --git a/OpenSky.Agent/Views/Models/FlightTrackingViewModel.PayloadStations.cs b/OpenSky.Agent/Views/Models/FlightTrackingViewModel.PayloadStations.cs index 4f4557e..bf96235 100644 --- a/OpenSky.Agent/Views/Models/FlightTrackingViewModel.PayloadStations.cs +++ b/OpenSky.Agent/Views/Models/FlightTrackingViewModel.PayloadStations.cs @@ -11,9 +11,9 @@ namespace OpenSky.Agent.Views.Models using System.Collections.ObjectModel; using System.Diagnostics; - using OpenSky.Agent.Controls; - using OpenSky.Agent.Controls.Models; using OpenSky.Agent.MVVM; + using OpenSky.Agent.Simulator.Controls; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.Enums; /// ------------------------------------------------------------------------------------------------- diff --git a/OpenSky.Agent/Views/Models/FlightTrackingViewModel.cs b/OpenSky.Agent/Views/Models/FlightTrackingViewModel.cs index ce6d97a..b6cace9 100644 --- a/OpenSky.Agent/Views/Models/FlightTrackingViewModel.cs +++ b/OpenSky.Agent/Views/Models/FlightTrackingViewModel.cs @@ -18,10 +18,10 @@ namespace OpenSky.Agent.Views.Models using JetBrains.Annotations; - using OpenSky.Agent.Controls; using OpenSky.Agent.Controls.CustomModules; - using OpenSky.Agent.Controls.Models; using OpenSky.Agent.MVVM; + using OpenSky.Agent.Simulator.Controls; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.Enums; using OpenSky.Agent.Simulator.Models; using OpenSky.Agent.Simulator.Tools; @@ -185,6 +185,7 @@ public FlightTrackingViewModel() this.SpeedUpGroundHandlingCommand = new Command(this.SpeedUpGroundHandling); this.SkipGroundHandlingCommand = new Command(this.SkipGroundHandling); this.RefreshMetarCommand = new Command(this.RefreshMetar); + this.CompleteFlightCommand = new AsynchronousCommand(this.CompleteFlight); // Are we already preparing/resuming/tracking? this.SimulatorTrackingStatusChanged(this, this.Simulator.TrackingStatus); @@ -209,6 +210,13 @@ public FlightTrackingViewModel() /// ------------------------------------------------------------------------------------------------- public AsynchronousCommand AbortFlightCommand { get; } + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets the complete flight command. + /// + /// ------------------------------------------------------------------------------------------------- + public AsynchronousCommand CompleteFlightCommand { get; } + /// ------------------------------------------------------------------------------------------------- /// /// Gets the ground handling warning visibility. @@ -648,6 +656,22 @@ private void AbortFlight() } } + /// ------------------------------------------------------------------------------------------------- + /// + /// Complete the flight and submit it to the OpenSky server. + /// + /// + /// sushi.at, 13/12/2023. + /// + /// ------------------------------------------------------------------------------------------------- + private void CompleteFlight() + { + if (this.Simulator.CanFinishTracking) + { + this.Simulator.FinishUpFlightTracking(); + } + } + /// ------------------------------------------------------------------------------------------------- /// /// Refresh metar. @@ -978,7 +1002,7 @@ private void StartTracking() // Check fuel if (this.Simulator.Flight != null) { - if (this.Simulator.WeightAndBalance.FuelTotalQuantity < (this.Simulator.Flight.FuelGallons ?? 0)) + if (this.Simulator.WeightAndBalance.FuelTotalQuantity < (this.Simulator.Flight.FuelGallons ?? 0) && ((this.Simulator.Flight.FuelGallons ?? 0) - this.Simulator.WeightAndBalance.FuelTotalQuantity) > 0.2) { Debug.WriteLine("Fuel below flight plan, double checking with user..."); ExtendedMessageBoxResult? answer = null; diff --git a/OpenSky.Agent/Views/Models/LandingReportViewModel.cs b/OpenSky.Agent/Views/Models/LandingReportViewModel.cs index 9858ab6..a7e61c9 100644 --- a/OpenSky.Agent/Views/Models/LandingReportViewModel.cs +++ b/OpenSky.Agent/Views/Models/LandingReportViewModel.cs @@ -66,9 +66,12 @@ public LandingReportViewModel() this.FlightNumberHeader = $"Flight #{this.Simulator.Flight?.FullFlightNumber}\r\nLanding Report"; // Fetch the initial already existing landing report(s) - foreach (var item in this.Simulator.LandingReports) + if (this.Simulator.FinalTouchDownIndex >= 0) { - this.landingReports.Add(item); + for (var i = this.Simulator.FinalTouchDownIndex; i < this.Simulator.LandingReports.Count; i++) + { + this.landingReports.Add(this.Simulator.LandingReports[i]); + } } // Subscribe to changes @@ -105,7 +108,7 @@ public LandingReportViewModel() /// Gets the cross wind (only from first touchdown). /// /// ------------------------------------------------------------------------------------------------- - public string CrossWind => this.landingReports.Count > 0 ? this.landingReports[0].CrossWind.ToString("Left, 0.00;Right, 0.00;None, 0") : "0"; + public string CrossWind => this.landingReports.Count > 0 ? this.landingReports[0].CrossWind.ToString("Left, 0.0;Right, 0.0;None, 0") : "0"; /// ------------------------------------------------------------------------------------------------- /// @@ -144,10 +147,10 @@ private set /// ------------------------------------------------------------------------------------------------- /// - /// Gets the head wind (only from first touchdown). + /// Gets the head-wind (only from first touchdown). /// /// ------------------------------------------------------------------------------------------------- - public string HeadWind => this.landingReports.Count > 0 ? this.landingReports[0].HeadWind.ToString("Tail, 0.00;Head, 0.00;None, 0") : "0"; + public string HeadWind => this.landingReports.Count > 0 ? this.landingReports[0].HeadWind.ToString("Tail, 0.0;Head, 0.0;None, 0") : "0"; /// ------------------------------------------------------------------------------------------------- /// @@ -161,19 +164,31 @@ public string LandingGrade // ---------------------------------------------------- // Landing rate (absolute) // ---------------------------------------------------- - // A+ Butter <=60 (PISTON,TURBO) - // A+ Perfect >60 <=130 (JET) - // A- Too soft <=60 (JET) - // A Good >60 <=180 - // B OK >180 <=240 - // C Hard >240 <=600 - // D Rough >600 <=1000 (Inspection) + // A+ Butter <=80 (Other) + // A+ Perfect <=250 >=80 (WBA) + // A+ Perfect <=160 >=80 (NBA) + // A+ Perfect <=160 >=50 (JET engine) + // A- Too soft <=80 (NBA, WBA) + // A- Too soft <=50 (JET engine) + // B OK >350 (WBA) + // B OK >250 (NBA) + // B OK >220 (JET engine) + // B OK >200 (Other) + // D Hard >600 <=840 (Inspection) + // E Severe Hard >840 <=1000 (Inspection, possible repair) // F Crash >1000 (Repair) // ---------------------------------------------------- // G-Force // ---------------------------------------------------- - // C Hard >2.1 + // A Good >=0.75 <=1.25 + // B OK >1.25 <=1.5 + // B- Low-G <0.75 + // B- Uncomfortable >1.5 <=2.1 + // C Rough >2.1 <=2.6 + // D Hard >2.6 <=2.86 + // E Severed Hard >2.86 (Inspection, possible repair) + // F Crash >3 (Repair) // ---------------------------------------------------- // Bounces @@ -182,95 +197,195 @@ public string LandingGrade // D Porpoising >2 // ---------------------------------------------------- - // Wind todo add wind ratings + // Wind // ---------------------------------------------------- - // E Dangerous Cross >40 (JET) >20 (PISTON,TURBO) - // E Dangerous Tail >15 (JET) >5 (PISTON,TURBO) + // E Dangerous Cross >40 (Airliners) >20 (GA) + // E Dangerous Tail >15 // ---------------------------------------------------- // Sideslip // ---------------------------------------------------- - // E Dangerous <-15 or >15 + // E Dangerous <-16 or >16 // ---------------------------------------------------- // Bank angle // ---------------------------------------------------- // E Dangerous <-5 or >5 + if (this.landingReports.Count == 0) + { + this.LandingGradeDescription = "Unknown"; + return "?"; + } + var landingRateAbs = Math.Abs(this.MaxLandingRate); - var grade = "A+"; - var desc = "Butter landing"; + var crossWindAbs = Math.Abs(this.landingReports[0].CrossWind); + var grade = "?"; + var desc = "Unknown"; - if (landingRateAbs > 1000) + if (landingRateAbs > 1000 || this.MaxGForce > 3) { grade = "F"; desc = "Crash landing"; } - if (grade == "A+" && (this.MaxBankAngle is < -5.0 or > 5.0)) + if (grade == "?" && (this.MaxBankAngle is < -5.0 or > 5.0)) { grade = "E"; desc = "Dangerous bank angle"; } - if (grade == "A+" && (this.MaxSideSlipAngle is < -15.0 or > 15.0)) + if (grade == "?" && (this.MaxSideSlipAngle is < -65.0 or > 16.0)) { grade = "E"; desc = "Dangerous sideslip angle"; } - if (grade == "A+" && landingRateAbs is > 600 and <= 1000) + if (grade == "?" && (landingRateAbs is > 840 and <= 1000 || this.MaxGForce > 2.86)) + { + grade = "E"; + desc = "Severe hard landing"; + } + + if (grade == "?" && crossWindAbs > 40) + { + grade = "E"; + desc = "Dangerous crosswind"; + } + + if (grade == "?" && crossWindAbs > 20 && this.Simulator.Flight?.Aircraft.Type.Category is AircraftTypeCategory.SEP or AircraftTypeCategory.MEP or AircraftTypeCategory.SET or AircraftTypeCategory.MET or AircraftTypeCategory.HEL) + { + grade = "E"; + desc = "Dangerous crosswind"; + } + + if (grade == "?" && this.landingReports[0].HeadWind > 15) + { + grade = "E"; + desc = "Dangerous tailwind"; + } + + if (grade == "A+" && (landingRateAbs is > 600 and <= 840 || this.MaxGForce is > 2.6 and <= 2.86)) { grade = "D"; - desc = "Rough landing"; + desc = "Hard landing"; } - if (grade == "A+" && this.Bounces > 2) + if (grade == "?" && this.Bounces > 2) { grade = "D"; desc = "Porpoising landing"; } - if (grade == "A+" && (this.MaxGForce is < -2.1 or > 2.1)) + if (grade == "?" && this.MaxGForce is > 2.1 and <= 2.6) { grade = "C"; - desc = "Hard landing"; + desc = "Rough landing"; } - if (grade == "A+" && landingRateAbs is > 240 and <= 600) + if (grade == "?" && this.Bounces > 1) { grade = "C"; - desc = "Hard landing"; + desc = "Bouncy landing"; } - if (grade == "A+" && this.Bounces > 1) + if (grade == "?" && this.MaxGForce is > 1.5 and <= 2.1) { - grade = "C"; - desc = "Bouncy landing"; + grade = "B-"; + desc = "Uncomfortable landing"; } - if (grade == "A+" && landingRateAbs is > 180 and <= 240) + if (grade == "?" && this.MaxGForce is > 1.25 and <= 1.5) { grade = "B"; - desc = "OK landing"; + desc = "Good landing"; } - if (grade == "A+" && landingRateAbs is > 60 and <= 180) + if (grade == "?" && this.MinGForce >= 0.75 && this.MaxGForce <= 1.25) { grade = "A"; - desc = "Good landing"; + desc = "Great landing"; - if (landingRateAbs <= 130 && this.Simulator.AircraftIdentity.EngineType == EngineType.Jet) + if (this.Simulator.Flight?.Aircraft.Type.Category is AircraftTypeCategory.WBA) { - grade = "A+"; - desc = "Perfect landing"; + if (landingRateAbs > 350) + { + grade = "B"; + desc = "OK landing"; + } + + if (landingRateAbs is <= 250 and >= 80) + { + grade = "A+"; + desc = "Perfect landing"; + } + + if (landingRateAbs < 80) + { + grade = "A-"; + desc = "Lading too soft"; + } + } + else if (this.Simulator.Flight?.Aircraft.Type.Category is AircraftTypeCategory.NBA) + { + if (landingRateAbs > 250) + { + grade = "B"; + desc = "OK landing"; + } + + if (landingRateAbs is <= 160 and >= 80) + { + grade = "A+"; + desc = "Perfect landing"; + } + + if (landingRateAbs < 80) + { + grade = "A-"; + desc = "Lading too soft"; + } + } + else if (this.Simulator.AircraftIdentity.EngineType == EngineType.Jet) + { + if (landingRateAbs > 220) + { + grade = "B"; + desc = "OK landing"; + } + + if (landingRateAbs is <= 160 and >= 50) + { + grade = "A+"; + desc = "Perfect landing"; + } + + if (landingRateAbs < 50) + { + grade = "A-"; + desc = "Lading too soft"; + } + } + else + { + if (landingRateAbs > 200) + { + grade = "B"; + desc = "OK landing"; + } + + if (landingRateAbs < 80) + { + grade = "A+"; + desc = "Butter landing"; + } } } - if (grade == "A+" && this.Simulator.AircraftIdentity.EngineType == EngineType.Jet) + if (grade == "?" && this.MinGForce < 0.75) { - grade = "A-"; - desc = "Lading too soft"; + grade = "B-"; + desc = "Low-G landing"; } this.LandingGradeDescription = desc; @@ -324,7 +439,7 @@ public double MaxBankAngle /// Gets the maximum bank angle info string. /// /// ------------------------------------------------------------------------------------------------- - public string MaxBankAngleInfo => this.MaxBankAngle.ToString("Left, 0.00;Right, 0.00;None, 0"); + public string MaxBankAngleInfo => this.MaxBankAngle.ToString("Left, 0.0;Right, 0.0;None, 0"); /// ------------------------------------------------------------------------------------------------- /// @@ -342,7 +457,7 @@ public double MaxBankAngle /// ------------------------------------------------------------------------------------------------- /// - /// Gets the maximum side slip angle. + /// Gets the maximum side-slip angle. /// /// ------------------------------------------------------------------------------------------------- public double MaxSideSlipAngle @@ -362,10 +477,17 @@ public double MaxSideSlipAngle /// ------------------------------------------------------------------------------------------------- /// - /// Gets the maximum side slip angle info text. + /// Gets the maximum side-slip angle info text. + /// + /// ------------------------------------------------------------------------------------------------- + public string MaxSideSlipAngleInfo => this.MaxSideSlipAngle.ToString("Left, 0.0;Right, 0.0;None, 0"); + + /// ------------------------------------------------------------------------------------------------- + /// + /// Gets the minimum g-force. /// /// ------------------------------------------------------------------------------------------------- - public string MaxSideSlipAngleInfo => this.MaxSideSlipAngle.ToString("Left, 0.00;Right, 0.00;None, 0"); + public double MinGForce => this.landingReports.Count > 0 ? this.landingReports.Min(lr => lr.GForce) : 0.0; /// ------------------------------------------------------------------------------------------------- /// @@ -439,6 +561,7 @@ private void LandingReportsCollectionChanged(object sender, NotifyCollectionChan this.NotifyPropertyChanged(nameof(this.MaxLandingRate)); this.NotifyPropertyChanged(nameof(this.MaxGForce)); + this.NotifyPropertyChanged(nameof(this.MinGForce)); this.NotifyPropertyChanged(nameof(this.MaxSideSlipAngleInfo)); this.NotifyPropertyChanged(nameof(this.Bounces)); this.NotifyPropertyChanged(nameof(this.MaxBankAngleInfo)); diff --git a/OpenSky.Agent/Views/Models/SettingsViewModel.cs b/OpenSky.Agent/Views/Models/SettingsViewModel.cs index 98859f6..f45336c 100644 --- a/OpenSky.Agent/Views/Models/SettingsViewModel.cs +++ b/OpenSky.Agent/Views/Models/SettingsViewModel.cs @@ -20,12 +20,12 @@ namespace OpenSky.Agent.Views.Models using Microsoft.Win32; - using OpenSky.Agent.Controls; - using OpenSky.Agent.Controls.Models; using OpenSky.Agent.MVVM; using OpenSky.Agent.Properties; using OpenSky.Agent.SimConnectMSFS; using OpenSky.Agent.Simulator; + using OpenSky.Agent.Simulator.Controls; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.Models; using OpenSky.Agent.Simulator.Tools; using OpenSky.Agent.Tools; @@ -839,6 +839,7 @@ private void SaveSettings() if (simulatorWasChanged && Simulator.Instance != null) { + StartupViewModel.Instance.SimulatorChanged(); Simulator.Instance.LandingReported += (_, landingReportNotification) => { if (landingReportNotification.Equals(LandingReportNotification.Parse(Settings.Default.LandingReportNotification))) diff --git a/OpenSky.Agent/Views/Models/SoundPackTesterViewModel.cs b/OpenSky.Agent/Views/Models/SoundPackTesterViewModel.cs index fe15b24..e512d0b 100644 --- a/OpenSky.Agent/Views/Models/SoundPackTesterViewModel.cs +++ b/OpenSky.Agent/Views/Models/SoundPackTesterViewModel.cs @@ -12,10 +12,10 @@ namespace OpenSky.Agent.Views.Models using System.Linq; using System.Speech.Synthesis; - using OpenSky.Agent.Controls; - using OpenSky.Agent.Controls.Models; using OpenSky.Agent.MVVM; using OpenSky.Agent.Simulator; + using OpenSky.Agent.Simulator.Controls; + using OpenSky.Agent.Simulator.Controls.Models; /// ------------------------------------------------------------------------------------------------- /// diff --git a/OpenSky.Agent/Views/Models/StartupViewModel.cs b/OpenSky.Agent/Views/Models/StartupViewModel.cs index 87cd14b..87ed595 100644 --- a/OpenSky.Agent/Views/Models/StartupViewModel.cs +++ b/OpenSky.Agent/Views/Models/StartupViewModel.cs @@ -25,10 +25,10 @@ namespace OpenSky.Agent.Views.Models using JetBrains.Annotations; - using OpenSky.Agent.Controls; - using OpenSky.Agent.Controls.Models; using OpenSky.Agent.MVVM; using OpenSky.Agent.Simulator; + using OpenSky.Agent.Simulator.Controls; + using OpenSky.Agent.Simulator.Controls.Models; using OpenSky.Agent.Simulator.Enums; using OpenSky.Agent.Simulator.Tools; @@ -161,6 +161,7 @@ public StartupViewModel() Instance = this; Simulator.Instance.PropertyChanged += this.SimConnectPropertyChanged; Simulator.Instance.FlightChanged += this.SimConnectFlightChanged; + Simulator.Instance.MessageBoxCreated += this.SimMessageBoxCreated; this.notificationIcon = this.greyIcon; if (!UserSessionService.Instance.IsUserLoggedIn) @@ -334,6 +335,21 @@ public StartupViewModel() { Name = "OpenSky.StartupViewModel.CheckForFlights" }.Start(); } + /// ------------------------------------------------------------------------------------------------- + /// + /// Simulator interface was changed in the settings. + /// + /// + /// sushi.at, 13/12/2023. + /// + /// ------------------------------------------------------------------------------------------------- + public void SimulatorChanged() + { + Simulator.Instance.PropertyChanged += this.SimConnectPropertyChanged; + Simulator.Instance.FlightChanged += this.SimConnectFlightChanged; + Simulator.Instance.MessageBoxCreated += this.SimMessageBoxCreated; + } + /// ------------------------------------------------------------------------------------------------- /// /// Gets or sets the discord RPC client. @@ -905,6 +921,26 @@ private void OpenFlightTracking() FlightTracking.Open(); } + /// ------------------------------------------------------------------------------------------------- + /// + /// The simulator interface created a message box and asked as to show it to the user. + /// + /// + /// sushi.at, 13/12/2023. + /// + /// + /// Source of the event. + /// + /// + /// The OpenSky message box. + /// + /// ------------------------------------------------------------------------------------------------- + private void SimMessageBoxCreated(object sender, OpenSkyMessageBox openSkyMessageBox) + { + FlightTracking.Open(); + FlightTracking.Instance.ShowMessageBox(openSkyMessageBox); + } + /// ------------------------------------------------------------------------------------------------- /// /// Opens the settings view. diff --git a/changelog.txt b/changelog.txt index 2644e42..f4e1283 100644 --- a/changelog.txt +++ b/changelog.txt @@ -8,6 +8,7 @@ Version 0.5.9 (ALPHA5) - Fixed headwind/tailwind for Xplane - Added live METAR to tracking window - Added new simpler "Add Aircraft" window for users +- Landing Analysis 2.0 -------------------------------------------------------------------------------------- Version 0.5.8 (ALPHA5)