Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Next release 🥳 #16

Merged
merged 63 commits into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
9d41a0f
First proof of concept passing tests using stateless!
corstian Aug 2, 2020
306aa41
Add the spatial map, and resolve old project references
corstian Aug 3, 2020
934d959
Fix a test, and replace the nearby aircraft retrieval mechanism
corstian Aug 4, 2020
b996d88
Remove context on expiration
corstian Aug 4, 2020
cee5065
Add a LaunchMethod property
corstian Aug 4, 2020
0205a23
Some scaffolding to start analysing the launch methods
corstian Aug 4, 2020
3742d4c
Add a class to calculate a ZScore for realtime peak detection
corstian Aug 4, 2020
42f80aa
Remove async enqueue methods
corstian Aug 4, 2020
b8d9862
Add proof of concept for winch launch detection
corstian Aug 4, 2020
8533c8c
Remove extreme outliers by discarding potentially duplicate data we'v…
corstian Aug 4, 2020
4250659
Remove extreme outliers by enforcing a minimum time constraint
corstian Aug 4, 2020
eeab1ac
Remove last seen field for it was not used by the library
corstian Aug 4, 2020
a344d0b
Correct the dupe removal logic so that the end times are correctly re…
corstian Aug 4, 2020
072a2c3
Early proof of concept for launch method recognition
corstian Aug 5, 2020
1ac5376
Add FlightContextOptions to configure more advanced use-cases
corstian Aug 5, 2020
3158b86
Remove states which are no longer used
corstian Aug 5, 2020
cfabe18
Group geo methods together again
corstian Aug 5, 2020
805f4ed
Move NormalizeData method together with the geo helpers
corstian Aug 5, 2020
8f13609
Fix build errors
corstian Aug 5, 2020
4466602
Refactor states into individual files
corstian Aug 5, 2020
a2490f6
Add a distance check for "winch" launches
corstian Aug 5, 2020
804853f
Change the way a context is instantiated
corstian Aug 5, 2020
cd7eeec
Wire the launch completed event handlers in the factory
corstian Aug 5, 2020
6b8d054
Fix build error
corstian Aug 5, 2020
85a1854
Fix failing tests due to a null reference exception
corstian Aug 5, 2020
66cfc94
Add interpolation logic
corstian Aug 5, 2020
d1e7da8
Change return type of the AircraftAccessor
corstian Aug 5, 2020
5998e2f
Determine whether an aircraft is being towed or is towing
corstian Aug 5, 2020
91a54e3
Reduce the complexity of the state machine by using internal triggers
corstian Aug 5, 2020
361e62c
Proof of concept for dealing with different launch methods
corstian Aug 5, 2020
29b260a
Add LaunchFinished property
corstian Aug 6, 2020
15e0d7a
Playing around with the structure with which to track launch status
corstian Aug 6, 2020
42825d6
Fix build errors
corstian Aug 6, 2020
87e2181
Add the first tests which validate the model output
corstian Aug 6, 2020
66af3f9
Fix a test situation
corstian Aug 6, 2020
1e292a8
Actually throw errors in case an event handler fails
corstian Aug 6, 2020
24631f0
Fix tests once again (:
corstian Aug 6, 2020
e9ebfae
Resolve a failing test
corstian Sep 26, 2020
316f296
Add processing tool
corstian Sep 26, 2020
f0059f2
performane improvements
corstian Sep 26, 2020
a3c256e
Add proper test case
corstian Sep 27, 2020
d20a2e9
Enqueue a collection point for point to maintain geospatial relevance…
corstian Sep 29, 2020
5fc2a34
Introduce experimental new state machine structure
corstian Sep 29, 2020
48c5fcb
Switcheroo
corstian Sep 29, 2020
a756237
Proof of concept for the new state machine is somewhat working.
corstian Sep 29, 2020
56e53f7
Working proof of concept for the renewed flight analysis tool
corstian Sep 29, 2020
c28e5c3
Properly deal with the NearbyAircraftAccessor.
corstian Sep 30, 2020
8da5fb1
Refactor NearbyAircraftAccessor as Flight Context only option
corstian Sep 30, 2020
e454a41
First proof of concept for tow recognition (A little over 50% accurate)
corstian Sep 30, 2020
7bf725f
Refactor the spatial map into its own project.
corstian Oct 2, 2020
8acc016
Get 95% reliability for aerotows when visible
corstian Oct 2, 2020
1891b4e
We're not going through with the randomness plan anymore.
corstian Oct 2, 2020
c7c26c4
Way better flight recognition, as well as support for aborted departures
corstian Oct 3, 2020
bbf66b9
Add a time based limit for departure data
corstian Oct 6, 2020
adfdcdd
Ensure any hypothesis is cleared before switching back to flight trac…
corstian Oct 6, 2020
bb173ea
Update FlightContext tests to use the new version
corstian Oct 6, 2020
263106e
A preliminary departure heading is added. Tests pass again.
corstian Oct 6, 2020
b062b40
Made some more tests pass with the experimental version
corstian Oct 7, 2020
d5383a4
Definitely move the experimental version to the base
corstian Oct 7, 2020
5422c78
Remove the arrival theory cancellationtokensource
corstian Oct 8, 2020
b9efbcd
Remove package dependency and simplify runway object
corstian Oct 9, 2020
964d9d7
Add runway queries
corstian Oct 9, 2020
f31afc6
Bump version number
corstian Oct 9, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Introduce experimental new state machine structure
  • Loading branch information
corstian committed Sep 29, 2020
commit 5fc2a342d095c416794a7636001f8e010e31a3fe
2 changes: 1 addition & 1 deletion ConsoleApp1/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Skyhop.FlightAnalysis;
using Skyhop.FlightAnalysis.Experimental;
using System;

namespace ConsoleApp1
Expand Down
14 changes: 7 additions & 7 deletions Skyhop.FlightAnalysis.Tests/FlightContextTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,32 @@ public class FlightContextTests
[TestMethod]
public async Task Flight_D1908_20170408()
{
FlightContext fc = new FlightContext("6770");
var fc = new Experimental.FlightContext("6770");

var countdownEvent = new CountdownEvent(3);

fc.OnTakeoff += (sender, args) =>
{
countdownEvent.Signal();

Assert.AreEqual(636272591685778931, ((FlightContext)sender).Flight.StartTime?.Ticks);
Assert.AreEqual(244, ((FlightContext)sender).Flight.DepartureHeading);
Assert.AreEqual(636272591685778931, ((Experimental.FlightContext)sender).Flight.StartTime?.Ticks);
//Assert.AreEqual(244, ((Experimental.FlightContext)sender).Flight.DepartureHeading);
};

fc.OnLaunchCompleted += (sender, args) =>
{
countdownEvent.Signal();

Assert.AreEqual(LaunchMethods.Winch, ((FlightContext)sender).Flight.LaunchMethod);
Assert.AreEqual(636272591994430449, ((FlightContext)sender).Flight.LaunchFinished?.Ticks);
Assert.AreEqual(LaunchMethods.Winch, ((Experimental.FlightContext)sender).Flight.LaunchMethod);
Assert.AreEqual(636272591994430449, ((Experimental.FlightContext)sender).Flight.LaunchFinished?.Ticks);
};

fc.OnLanding += (sender, args) =>
{
countdownEvent.Signal();

Assert.AreEqual(636272628474023926, ((FlightContext)sender).Flight.EndTime?.Ticks);
Assert.AreEqual(250, ((FlightContext)sender).Flight.ArrivalHeading);
Assert.AreEqual(636272628474023926, ((Experimental.FlightContext)sender).Flight.EndTime?.Ticks);
Assert.AreEqual(250, ((Experimental.FlightContext)sender).Flight.ArrivalHeading);
};

// These events should NOT be fired
Expand Down
327 changes: 327 additions & 0 deletions Skyhop.FlightAnalysis/Experimental/FlightContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Reactive.Linq;
using Skyhop.FlightAnalysis.Models;
using Stateless;
using Stateless.Graph;
using Skyhop.FlightAnalysis.Experimental.States;

namespace Skyhop.FlightAnalysis.Experimental
{
/// <summary>
/// The FlightContext is an aircraft specific instance which analyses the flight's position updates.
///
/// To process data points from multiple aircraft use the FlightContextFactory.
/// </summary>
public class FlightContext
{
public enum State
{
None,
Stationary,
Departing,
Airborne,
Arriving
}

public enum Trigger
{
Next,
TrackMovements,
Depart,
LaunchCompleted,
Landing,
Arrived
}

public readonly StateMachine<State, Trigger> StateMachine;

public Flight Flight { get; internal set; }

internal readonly FlightContextOptions Options = new FlightContextOptions();
internal PositionUpdate CurrentPosition;
internal DateTime LatestTimeStamp;

public DateTime LastActive { get; private set; }

public FlightContext(FlightContextOptions options)
{
Options = options;
}

/// <summary>
/// Initializes a new instance of the <see cref="FlightContext"/> class.
/// </summary>
/// <param name="flightMetadata">When provided the flightMetadata parameter will set the flight information assuming previous
/// processing has been done.</param>
public FlightContext(FlightMetadata flightMetadata, Action<FlightContextOptions> options)
{
StateMachine = new StateMachine<State, Trigger>(State.None, FiringMode.Queued);

StateMachine.Configure(State.None)
.Permit(Trigger.Next, State.Stationary);

StateMachine.Configure(State.Stationary)
.OnEntry(this.Stationary)
.PermitReentry(Trigger.Next)
.Permit(Trigger.Depart, State.Departing)
.OnExit(InvokeOnTakeoffEvent);

StateMachine.Configure(State.Departing)
.OnEntry(this.Departing)
.PermitReentry(Trigger.Next)
.Permit(Trigger.LaunchCompleted, State.Airborne)
.Permit(Trigger.Landing, State.Arriving)
.OnExit(InvokeOnLaunchCompletedEvent);

StateMachine.Configure(State.Airborne)
.OnEntry(this.Airborne)
.PermitReentry(Trigger.Next)
.Permit(Trigger.Landing, State.Arriving);

StateMachine.Configure(State.Arriving)
.OnEntry(this.Arriving)
.PermitReentry(Trigger.Next)
.Permit(Trigger.Arrived, State.Stationary)
.OnExit(InvokeOnLandingEvent);


//StateMachine.Configure(State.ProcessPoint)
// .OnEntry(this.ProcessNextPoint)
// .PermitReentry(Trigger.Next)
// .InternalTransition(Trigger.Initialize, this.Initialize)
// .Permit(Trigger.Standby, State.WaitingForData)
// .Permit(Trigger.ResolveState, State.DetermineFlightState);

//StateMachine.Configure(State.DetermineFlightState)
// .OnEntry(this.DetermineFlightState)
// .PermitReentry(Trigger.ResolveState)
// .InternalTransition(Trigger.ResolveDeparture, this.FindDepartureHeading)
// .InternalTransition(Trigger.ResolveArrival, this.FindArrivalHeading)
// .Permit(Trigger.Next, State.ProcessPoint)
// .Permit(Trigger.ResolveLaunchMethod, State.DetermineLaunchMethod);

//StateMachine.Configure(State.DetermineLaunchMethod)
// .OnEntry(this.DetermineLaunchMethod)
// .PermitReentry(Trigger.ResolveLaunchMethod)
// .Permit(Trigger.Next, State.ProcessPoint);

//StateMachine.Configure(State.TrackLaunchMethod)
// .OnEntry(this.TrackLaunch)
// .PermitReentry(Trigger.TrackLaunch);

//StateMachine.Configure(State.WaitingForData)
// .PermitReentry(Trigger.Standby)
// .Permit(Trigger.Next, State.ProcessPoint);

options?.Invoke(Options);

Options.AircraftId = flightMetadata.Aircraft; // This line prevents the factory from crashing when the attach method is used.
Flight = flightMetadata.Flight;

LastActive = DateTime.UtcNow;
}

/// <summary>
/// FlightContext Constructor
/// </summary>
/// <param name="aircraftId">Optional string used to identify this context.</param>
public FlightContext(string aircraftId, Action<FlightContextOptions> options = default) : this(
new FlightMetadata
{
Aircraft = aircraftId
},
options)
{ }

public FlightContext(FlightMetadata flightMetadata) : this(flightMetadata, default) { }

/// <summary>
/// Queue a positionupdate for this specific context to process.
/// </summary>
/// <param name="positionUpdate">The positionupdate to queue</param>
/// <param name="startOrContinueProcessing">Whether or not to start/continue processing</param>
public void Enqueue(PositionUpdate positionUpdate)
{
if (positionUpdate == null) return;

CurrentPosition = positionUpdate;

StateMachine.Fire(Trigger.Next);

this.Flight.PositionUpdates.Add(positionUpdate);

LastActive = DateTime.UtcNow;
}

/// <summary>
/// Queue a collection of position updates on this context for processing. Processing will either start
/// directly or continue in case it is still running.
/// </summary>
/// <param name="positionUpdates">The position updates to queue</param>
public void Enqueue(IEnumerable<PositionUpdate> positionUpdates)
{
foreach (var update in positionUpdates
.OrderBy(q => q.TimeStamp)
.ToList())
{
Enqueue(update);
}

LastActive = DateTime.UtcNow;
}

/// <summary>
/// This method casually removes some position updates.
/// </summary>
internal void CleanupDataPoints()
{
if (Options.MinifyMemoryPressure && Flight.PositionUpdates.Count > Options.MinimumRequiredPositionUpdateCount)
Flight.PositionUpdates.RemoveRange(0, Flight.PositionUpdates.Count - Options.MinimumRequiredPositionUpdateCount);
}

#warning REMOVE BEFORE PUBLISH
public string ToDotGraph()
{
return UmlDotGraph.Format(StateMachine.GetInfo());
}

/*
* By wrapping the event invocation in try catch blocks we can prevent that the context abruptly ends because
* of an exception through the event handler.
*/
internal void InvokeOnTakeoffEvent()
{
try
{
OnTakeoff?.Invoke(this, new OnTakeoffEventArgs(Flight));
}
catch (Exception ex)
{
Trace.Write(ex);
throw;
}
}

internal void InvokeOnLaunchCompletedEvent()
{
try
{
OnLaunchCompleted?.Invoke(this, new OnLaunchCompletedEventArgs(Flight));
}
catch (Exception ex)
{
Trace.Write(ex);
throw;
}
}

internal void InvokeOnLandingEvent()
{
try
{
OnLanding?.Invoke(this, new OnLandingEventArgs(Flight));
}
catch (Exception ex)
{
Trace.Write(ex);
throw;
}
}

internal void InvokeOnRadarContactEvent()
{
try
{
OnRadarContact?.Invoke(this, new OnRadarContactEventArgs(Flight));
}
catch (Exception ex)
{
Trace.Write(ex);
throw;
}
}

internal void InvokeOnCompletedWithErrorsEvent()
{
try
{
OnCompletedWithErrors?.Invoke(this, new OnCompletedWithErrorsEventArgs(Flight));
}
catch (Exception ex)
{
Trace.Write(ex);
throw;
}
}

/// <summary>
/// The OnTakeoff event will fire once the data indicates a takeoff.
/// </summary>
public event EventHandler<OnTakeoffEventArgs> OnTakeoff;

/// <summary>
/// Observable source for live updates on departures.
/// </summary>
public IObservable<OnTakeoffEventArgs> Departure => Observable
.FromEventPattern<OnTakeoffEventArgs>(
(args) => OnTakeoff += args,
(args) => OnTakeoff -= args)
.Select(q => q.EventArgs);

public event EventHandler<OnLaunchCompletedEventArgs> OnLaunchCompleted;

public IObservable<OnLaunchCompletedEventArgs> LaunchCompleted => Observable
.FromEventPattern<OnLaunchCompletedEventArgs>(
(args) => OnLaunchCompleted += args,
(args) => OnLaunchCompleted -= args)
.Select(q => q.EventArgs);

/// <summary>
/// The OnLanding event will fire once the data indicates a landing.
/// </summary>
public event EventHandler<OnLandingEventArgs> OnLanding;

/// <summary>
/// Observable source for live updates on arrivals.
/// </summary>
public IObservable<OnLandingEventArgs> Arrival => Observable
.FromEventPattern<OnLandingEventArgs>(
(args) => OnLanding += args,
(args) => OnLanding -= args)
.Select(q => q.EventArgs);

/// <summary>
/// The OnRadarContact event will fire when a takeoff has not been recorded but an aircraft is mid flight
/// </summary>
public event EventHandler<OnRadarContactEventArgs> OnRadarContact;

/// <summary>
/// Observable source to get notified when a new aircraft is being tracked.
/// </summary>
public IObservable<OnRadarContactEventArgs> RadarContact => Observable
.FromEventPattern<OnRadarContactEventArgs>(
(args) => OnRadarContact += args,
(args) => OnRadarContact -= args)
.Select(q => q.EventArgs);

/// <summary>
/// The OnCompletedWithErrors event will fire when flight processing has been completed but some errors have
/// been detected. (For example destination airfield could not be found)
/// </summary>
public event EventHandler<OnCompletedWithErrorsEventArgs> OnCompletedWithErrors;

/// <summary>
/// Observable source to get notified when a certain tracked aircraft hasn't been heard of in a while,
/// while it hasn't been observed to have landed.
/// </summary>
public IObservable<OnCompletedWithErrorsEventArgs> Vanished => Observable
.FromEventPattern<OnCompletedWithErrorsEventArgs>(
(args) => OnCompletedWithErrors += args,
(args) => OnCompletedWithErrors -= args)
.Select(q => q.EventArgs);
}
}
14 changes: 14 additions & 0 deletions Skyhop.FlightAnalysis/Experimental/States/Airborne.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Skyhop.FlightAnalysis.Experimental.States
{
internal static partial class MachineStates
{
internal static void Airborne(this FlightContext context)
{

}
}
}
Loading