public void OnLegacyPlaneInfoReceived(object sender, NetworkDataReceivedEventArgs e)
        {
            NetworkAircraft aircraft = mNetworkAircraft.FirstOrDefault(a => a.Callsign == e.From);

            if (aircraft != null)
            {
                if (aircraft.UpdateCount >= 3)
                {
                    if (string.IsNullOrEmpty(aircraft.TypeCode))
                    {
                        Console.WriteLine($"Received legacy aircraft info for {e.From} after {aircraft.UpdateCount} updates with no modern plane info packet received, selecting model based on legacy info.");
                        aircraft.TypeCode = e.Data.IndexOf("-") > -1 ? e.Data.Substring(0, e.Data.IndexOf("-")) : e.Data;
                        AddAircraftToSim(aircraft);
                    }
                    else
                    {
                        Console.WriteLine($"Received legacy aircraft info for {e.From} after having already selected a model, ignorning");
                    }
                }
                else
                {
                    Console.WriteLine($"Received legacy aircraft info for {e.From} after fewer than 3 updates, ignoring.");
                }
            }
            else
            {
                Console.WriteLine($"Received legacy aircraft info for unknown callsign {e.From}, ignoring.");
            }
        }
        private void UpdateNetworkAircraftInSim(NetworkAircraft aircraft, NetworkAircraftState State)
        {
            dynamic data = new ExpandoObject();

            data.Callsign         = aircraft.Callsign;
            data.Latitude         = State.Location.Lat;
            data.Longitude        = State.Location.Lon;
            data.Altitude         = State.Altitude;
            data.Bank             = State.Bank;
            data.Pitch            = State.Pitch;
            data.Heading          = State.Heading;
            data.GroundSpeed      = State.GroundSpeed;
            data.TransponderCode  = State.Transponder.TransponderCode;
            data.TransponderModeC = State.Transponder.TransponderModeC;
            data.TransponderIdent = State.Transponder.TransponderIdent;
            data.Origin           = aircraft.OriginAirport;
            data.Destination      = aircraft.DestinationAirport;

            XplaneConnect xp = new XplaneConnect
            {
                Type      = XplaneConnect.MessageType.PositionUpdate,
                Timestamp = DateTime.Now,
                Data      = data
            };

            XPlaneEventPosted?.Invoke(this, new ClientEventArgs <string>(xp.ToJSON()));
        }
        private void UpdateLegacyAircraftConfig(NetworkAircraft aircraft)
        {
            LegacyClientConfigFlags flags = new LegacyClientConfigFlags
            {
                EnginesRunning = true,
                BeaconLightsOn = true,
                NavLightsOn    = true
            };

            if (aircraft.GroundSpeed >= 50.0)
            {
                flags.StrobeLightsOn  = true;
                flags.TaxiLightsOn    = false;
                flags.LandingLightsOn = aircraft.CurrentPosition.Altitude <= 10000 ? true : false;
            }
            else if (aircraft.GroundSpeed > 0.0)
            {
                flags.TaxiLightsOn    = true;
                flags.StrobeLightsOn  = false;
                flags.LandingLightsOn = false;
            }
            else
            {
                flags.StrobeLightsOn  = false;
                flags.LandingLightsOn = false;
                flags.TaxiLightsOn    = false;
            }
            flags.OnGround = aircraft.CurrentPosition.Altitude == aircraft.PreviousPosition.Altitude;
            flags.GearDown = DeriveGearStatus(aircraft);

            UpdateLegacyAircraftConfig(aircraft, flags);
        }
        public void OnDeletePilotReceived(object sender, NetworkDataReceivedEventArgs e)
        {
            NetworkAircraft aircraft = mNetworkAircraft.FirstOrDefault(o => o.Callsign == e.From);

            if (aircraft != null)
            {
                DeleteNetworkAircraft(aircraft);
            }
        }
        private void ProcessRemoteFlightPlan(FlightPlan fp)
        {
            NetworkAircraft aircraft = mNetworkAircraft.FirstOrDefault(o => o.Callsign == fp.Callsign);

            if (aircraft != null)
            {
                aircraft.OriginAirport      = fp.DepartureAirport;
                aircraft.DestinationAirport = fp.DestinationAirport;
            }
        }
        private void NetworkAircraftUpdateReceived(string from, NetworkAircraftState state)
        {
            NetworkAircraft aircraft = mNetworkAircraft.FirstOrDefault(a => a.Callsign == from);

            if (aircraft == null)
            {
                ProcessNewAircraft(from, state);
            }
            else
            {
                UpdateExistingNetworkAircraft(aircraft, state);
            }
        }
        public void OnCapabilitiesResponseReceived(object sender, NetworkDataReceivedEventArgs e)
        {
            Console.WriteLine($"Received capabilities list from: {e.From} - {e.Data}");
            if (e.Data.Contains("ACCONFIG=1"))
            {
                NetworkAircraft aircraft = mNetworkAircraft.FirstOrDefault(o => o.Callsign == e.From);
                if (aircraft != null)
                {
                    Console.WriteLine($"Remote aircraft supports ACCONFIG: {e.From}");
                    aircraft.SupportsConfigurationProtocol = true;

                    Console.WriteLine($"Requesting full ACCONFIG packet from {e.From}");
                    mFsdManager.RequestFullAircraftConfiguration(e.From);
                }
            }
        }
        private void DeleteNetworkAircraft(NetworkAircraft aircraft)
        {
            dynamic data = new ExpandoObject();

            data.Callsign = aircraft.Callsign;

            XplaneConnect xp = new XplaneConnect
            {
                Type      = XplaneConnect.MessageType.RemovePlane,
                Timestamp = DateTime.Now,
                Data      = data
            };

            XPlaneEventPosted?.Invoke(this, new ClientEventArgs <string>(xp.ToJSON()));
            mNetworkAircraft.Remove(aircraft);
        }
        private void UpdateExistingNetworkAircraft(NetworkAircraft aircraft, NetworkAircraftState state)
        {
            if (aircraft != null && aircraft.UpdateCount >= 2 && string.IsNullOrEmpty(aircraft.TypeCode))
            {
                Console.WriteLine($"Still no type code received for {aircraft.Callsign} after position update #{aircraft.UpdateCount} - requesting aircraft info");
                mFsdManager.RequestPlaneInformation(aircraft.Callsign);
            }

            double diff = (DateTime.Now - aircraft.LastUpdated).TotalSeconds;

            aircraft.VerticalSpeed = (int)((aircraft.CurrentPosition.Altitude - aircraft.PreviousPosition.Altitude) / (diff / 60.0));
            aircraft.LastUpdated   = DateTime.Now;
            aircraft.StateHistory.Add(state);
            aircraft.Transponder = state.Transponder;
            aircraft.UpdateCount++;

            while (aircraft.StateHistory.Count > 2)
            {
                aircraft.StateHistory.RemoveAt(0);
            }

            UpdateNetworkAircraftInSim(aircraft, state);

            if (aircraft.Status == AircraftStatus.Active)
            {
                if (!aircraft.SupportsConfigurationProtocol)
                {
                    UpdateLegacyAircraftConfig(aircraft);
                }
                else
                {
                    while (aircraft.PendingAircraftConfiguration.Count > 0)
                    {
                        var cfg = aircraft.PendingAircraftConfiguration.Pop();
                        ProcessAircraftConfig(aircraft, cfg);
                    }
                }

                if ((DateTime.Now - aircraft.LastFlightPlanFetch).TotalMinutes >= 5)
                {
                    mFsdManager.RequestRemoteFlightPlan(aircraft.Callsign);
                    aircraft.LastFlightPlanFetch = DateTime.Now;
                }
            }
        }
        private void ProcessNewAircraft(string from, NetworkAircraftState state)
        {
            NetworkAircraft aircraft = new NetworkAircraft
            {
                Callsign    = from,
                LastUpdated = DateTime.Now,
                UpdateCount = 1,
                Status      = AircraftStatus.New
            };

            aircraft.StateHistory.Add(state);
            mNetworkAircraft.Add(aircraft);

            Console.WriteLine($"Aircraft discovered: {aircraft.Callsign} - requesting CAPS, sending CAPS, requesting aircraft info");
            mFsdManager.RequestClientCapabilities(aircraft.Callsign);
            mFsdManager.SendClientCaps(aircraft.Callsign);
            mFsdManager.RequestPlaneInformation(aircraft.Callsign);
        }
        public void OnNetworkAircraftInfoReceived(object sender, NetworkDataReceivedEventArgs e)
        {
            NetworkAircraft aircraft = mNetworkAircraft.FirstOrDefault(o => o.Callsign == e.From);

            if (aircraft == null)
            {
                Console.WriteLine($"Received aircraft info for an unknown aircraft: {e.From}");
            }
            else
            {
                if (!string.IsNullOrEmpty(aircraft.TypeCode))
                {
                    if (aircraft.TypeCode != e.Data)
                    {
                        Console.WriteLine($"Received new aircraft info for {e.From} - changing model");

                        aircraft.TypeCode = e.Data.IndexOf("-") > -1 ? e.Data.Substring(0, e.Data.IndexOf("-")) : e.Data;

                        dynamic data = new ExpandoObject();
                        data.Callsign = aircraft.Callsign;
                        data.TypeCode = aircraft.TypeCode.Substring(0, Math.Min(4, aircraft.TypeCode.Length));
                        data.Airline  = aircraft.AirlineIcao;

                        XplaneConnect xp = new XplaneConnect
                        {
                            Type      = XplaneConnect.MessageType.ChangeModel,
                            Timestamp = DateTime.Now,
                            Data      = data
                        };
                        XPlaneEventPosted?.Invoke(this, new ClientEventArgs <string>(xp.ToJSON()));
                    }
                    else
                    {
                        Console.WriteLine($"Received unchanged aircraft info for {e.From} - already have a model selected");
                    }
                }
                else
                {
                    aircraft.TypeCode = e.Data.IndexOf("-") > -1 ? e.Data.Substring(0, e.Data.IndexOf("-")) : e.Data;
                    AddAircraftToSim(aircraft);
                }
            }
        }
 private bool DeriveGearStatus(NetworkAircraft aircraft)
 {
     if (aircraft.IsClimbing && aircraft.CurrentPosition.Altitude > 500.0)
     {
         return(false);
     }
     if (aircraft.IsDescending && aircraft.CurrentPosition.Altitude <= 3000.0)
     {
         return(true);
     }
     if (aircraft.LastAppliedConfigFlags.OnGround)
     {
         return(true);
     }
     if (aircraft.CurrentPosition.Altitude < 300.0)
     {
         return(true);
     }
     return(false);
 }
        private void AddAircraftToSim(NetworkAircraft aircraft)
        {
            aircraft.Status = AircraftStatus.Active;

            dynamic data = new ExpandoObject();

            data.Callsign = aircraft.Callsign;
            data.TypeCode = aircraft.TypeCode.Substring(0, Math.Min(4, aircraft.TypeCode.Length));
            if (aircraft.Callsign.Length >= 3)
            {
                data.Airline = aircraft.Callsign.Substring(0, 3);
            }

            XplaneConnect xp = new XplaneConnect
            {
                Type      = XplaneConnect.MessageType.AddPlane,
                Timestamp = DateTime.Now,
                Data      = data
            };

            XPlaneEventPosted?.Invoke(this, new ClientEventArgs <string>(xp.ToJSON()));
        }
        public void OnAircraftConfigurationInfoReceived(object sender, NetworkDataReceivedEventArgs e)
        {
            AircraftConfigurationInfo aircraftConfigurationInfo;

            try
            {
                aircraftConfigurationInfo = AircraftConfigurationInfo.FromJson(e.Data);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Unable to deserialize aircraft configuration JSON from {e.From}: {e.Data} - Error: {ex.Message}");
                return;
            }
            NetworkAircraft aircraft = mNetworkAircraft.FirstOrDefault(o => o.Callsign == e.From);

            if (aircraftConfigurationInfo.HasConfig && aircraft != null)
            {
                if (!aircraft.SupportsConfigurationProtocol)
                {
                    aircraft.SupportsConfigurationProtocol = true;
                    mFsdManager.RequestFullAircraftConfiguration(aircraft.Callsign);
                    Console.WriteLine($"Received aircraft configuration from {e.From}. Requesting full ACCONFIG again.");
                }

                if (aircraft.Status == AircraftStatus.Active)
                {
                    Console.WriteLine($"Received aircraft configuration info from: {e.From}: {e.Data}");
                    ProcessAircraftConfig(aircraft, aircraftConfigurationInfo.Config);
                }
                else
                {
                    Console.WriteLine($"Queing ACCONFIG for {e.From} because the aircraft isn't active yet.");
                    aircraft.PendingAircraftConfiguration.Push(aircraftConfigurationInfo.Config);
                }
            }
        }
        private void ProcessAircraftConfig(NetworkAircraft aircraft, AircraftConfiguration config)
        {
            NetworkAircraftConfig cfgInterop = new NetworkAircraftConfig
            {
                Callsign  = aircraft.Callsign,
                Timestamp = DateTime.Now
            };

            if (config.IsFullData.HasValue && config.IsFullData.Value)
            {
                config.EnsurePopulated();
                aircraft.SupportsConfigurationProtocol = true;
                aircraft.Configuration = config;
            }
            else
            {
                if (!aircraft.SupportsConfigurationProtocol || aircraft.Configuration == null)
                {
                    return;
                }
                aircraft.Configuration.ApplyIncremental(config);
            }

            if (!aircraft.InitialConfigurationSet)
            {
                if (aircraft.Configuration.Lights.StrobesOn.HasValue)
                {
                    cfgInterop.Lights.StrobesOn = aircraft.Configuration.Lights.StrobesOn.Value;
                }
                if (aircraft.Configuration.Lights.LandingOn.HasValue)
                {
                    cfgInterop.Lights.LandingOn = aircraft.Configuration.Lights.LandingOn.Value;
                }
                if (aircraft.Configuration.Lights.TaxiOn.HasValue)
                {
                    cfgInterop.Lights.TaxiOn = aircraft.Configuration.Lights.TaxiOn.Value;
                }
                if (aircraft.Configuration.Lights.BeaconOn.HasValue)
                {
                    cfgInterop.Lights.BeaconOn = aircraft.Configuration.Lights.BeaconOn.Value;
                }
                if (aircraft.Configuration.Lights.NavOn.HasValue)
                {
                    cfgInterop.Lights.NavOn = aircraft.Configuration.Lights.NavOn.Value;
                }
                if (aircraft.Configuration.OnGround.HasValue)
                {
                    cfgInterop.OnGround = aircraft.Configuration.OnGround.Value;
                }
                if (aircraft.Configuration.FlapsPercent.HasValue)
                {
                    cfgInterop.Flaps = aircraft.Configuration.FlapsPercent.Value / 100.0f;
                }
                if (aircraft.Configuration.GearDown.HasValue)
                {
                    cfgInterop.GearDown = aircraft.Configuration.GearDown.Value;
                }
                if (aircraft.Configuration.Engines.IsAnyEngineRunning)
                {
                    cfgInterop.EnginesOn = aircraft.Configuration.Engines.IsAnyEngineRunning;
                }
                if (aircraft.Configuration.SpoilersDeployed.HasValue)
                {
                    cfgInterop.SpoilersDeployed = aircraft.Configuration.SpoilersDeployed.Value;
                }
                aircraft.InitialConfigurationSet = true;
            }
            else
            {
                if (aircraft.LastAppliedConfiguration == null)
                {
                    aircraft.InitialConfigurationSet = false;
                    Console.WriteLine($"LastAppliedConfiguration null; Requesting full ACCONFIG for: {aircraft.Callsign}");
                    mFsdManager.RequestFullAircraftConfiguration(aircraft.Callsign);
                    return;
                }

                if (aircraft.Configuration.Lights.StrobesOn.HasValue && aircraft.Configuration.Lights.StrobesOn.Value != aircraft.LastAppliedConfiguration.Lights.StrobesOn.Value)
                {
                    cfgInterop.Lights.StrobesOn = aircraft.Configuration.Lights.StrobesOn.Value;
                }
                if (aircraft.Configuration.Lights.NavOn.HasValue && aircraft.Configuration.Lights.NavOn.Value != aircraft.LastAppliedConfiguration.Lights.NavOn.Value)
                {
                    cfgInterop.Lights.NavOn = aircraft.Configuration.Lights.NavOn.Value;
                }
                if (aircraft.Configuration.Lights.BeaconOn.HasValue && aircraft.Configuration.Lights.BeaconOn.Value != aircraft.LastAppliedConfiguration.Lights.BeaconOn.Value)
                {
                    cfgInterop.Lights.BeaconOn = aircraft.Configuration.Lights.BeaconOn.Value;
                }
                if (aircraft.Configuration.Lights.LandingOn.HasValue && aircraft.Configuration.Lights.LandingOn.Value != aircraft.LastAppliedConfiguration.Lights.LandingOn.Value)
                {
                    cfgInterop.Lights.LandingOn = aircraft.Configuration.Lights.LandingOn.Value;
                }
                if (aircraft.Configuration.Lights.TaxiOn.HasValue && aircraft.Configuration.Lights.TaxiOn.Value != aircraft.LastAppliedConfiguration.Lights.TaxiOn.Value)
                {
                    cfgInterop.Lights.TaxiOn = aircraft.Configuration.Lights.TaxiOn.Value;
                }
                if (aircraft.Configuration.GearDown.HasValue && aircraft.Configuration.GearDown.Value != aircraft.LastAppliedConfiguration.GearDown.Value)
                {
                    cfgInterop.GearDown = aircraft.Configuration.GearDown.Value;
                }
                if (aircraft.Configuration.OnGround.HasValue && aircraft.Configuration.OnGround.Value != aircraft.LastAppliedConfiguration.OnGround.Value)
                {
                    cfgInterop.OnGround = aircraft.Configuration.OnGround.Value;
                }
                if (aircraft.Configuration.FlapsPercent.HasValue && aircraft.Configuration.FlapsPercent.Value != aircraft.LastAppliedConfiguration.FlapsPercent.Value)
                {
                    cfgInterop.Flaps = aircraft.Configuration.FlapsPercent.Value / 100.0f;
                }
                if (aircraft.Configuration.SpoilersDeployed.HasValue && aircraft.Configuration.SpoilersDeployed.Value != aircraft.LastAppliedConfiguration.SpoilersDeployed.Value)
                {
                    cfgInterop.SpoilersDeployed = aircraft.Configuration.SpoilersDeployed.Value;
                }
                if (aircraft.Configuration.Engines.IsAnyEngineRunning != aircraft.LastAppliedConfiguration.Engines.IsAnyEngineRunning)
                {
                    cfgInterop.EnginesOn = aircraft.Configuration.Engines.IsAnyEngineRunning;
                }
            }
            aircraft.LastAppliedConfiguration = aircraft.Configuration.Clone();

            if (cfgInterop.HasConfig)
            {
                XplaneConnect xp = new XplaneConnect
                {
                    Type      = XplaneConnect.MessageType.SurfaceUpdate,
                    Timestamp = DateTime.Now,
                    Data      = cfgInterop
                };
                XPlaneEventPosted?.Invoke(this, new ClientEventArgs <string>(xp.ToJSON()));
                Console.WriteLine($"Sending ACCONFIG to the sim for: {aircraft.Callsign} - {xp.ToJSON()}");
            }
        }
        private void UpdateLegacyAircraftConfig(NetworkAircraft aircraft, LegacyClientConfigFlags flags)
        {
            NetworkAircraftConfig temp = new NetworkAircraftConfig
            {
                Callsign  = aircraft.Callsign,
                Timestamp = DateTime.Now,
            };

            if (!aircraft.InitialConfigurationSet)
            {
                temp.GearDown                    = flags.GearDown;
                temp.OnGround                    = flags.OnGround;
                temp.Lights.TaxiOn               = flags.TaxiLightsOn;
                temp.Lights.LandingOn            = flags.LandingLightsOn;
                temp.Lights.BeaconOn             = flags.BeaconLightsOn;
                temp.Lights.NavOn                = flags.NavLightsOn;
                temp.Lights.StrobesOn            = flags.StrobeLightsOn;
                temp.EnginesOn                   = flags.EnginesRunning;
                aircraft.InitialConfigurationSet = true;
            }
            else
            {
                if (flags.GearDown != aircraft.LastAppliedConfigFlags.GearDown)
                {
                    temp.GearDown = flags.GearDown;
                }
                if (flags.OnGround != aircraft.LastAppliedConfigFlags.OnGround)
                {
                    temp.OnGround = flags.OnGround;
                }
                if (flags.TaxiLightsOn != aircraft.LastAppliedConfigFlags.TaxiLightsOn)
                {
                    temp.Lights.TaxiOn = flags.TaxiLightsOn;
                }
                if (flags.LandingLightsOn != aircraft.LastAppliedConfigFlags.LandingLightsOn)
                {
                    temp.Lights.LandingOn = flags.LandingLightsOn;
                }
                if (flags.BeaconLightsOn != aircraft.LastAppliedConfigFlags.BeaconLightsOn)
                {
                    temp.Lights.BeaconOn = flags.BeaconLightsOn;
                }
                if (flags.NavLightsOn != aircraft.LastAppliedConfigFlags.NavLightsOn)
                {
                    temp.Lights.NavOn = flags.NavLightsOn;
                }
                if (flags.EnginesRunning != aircraft.LastAppliedConfigFlags.EnginesRunning)
                {
                    temp.EnginesOn = flags.EnginesRunning;
                }
            }
            aircraft.LastAppliedConfigFlags = flags;

            if (temp.HasConfig)
            {
                XplaneConnect msg = new XplaneConnect
                {
                    Type      = XplaneConnect.MessageType.SurfaceUpdate,
                    Timestamp = DateTime.Now,
                    Data      = temp
                };
                XPlaneEventPosted?.Invoke(this, new ClientEventArgs <string>(msg.ToJSON()));
                Console.WriteLine($"Sending Legacy Config to the sim for: {aircraft.Callsign} - {msg.ToJSON()}");
            }
        }