Skip to content

Commit

Permalink
Auto-save and allow restoring previous recording if the app crashes #54
Browse files Browse the repository at this point in the history
  • Loading branch information
nguyenquyhy committed May 31, 2022
1 parent 37343c9 commit eb7a539
Show file tree
Hide file tree
Showing 11 changed files with 488 additions and 336 deletions.
24 changes: 18 additions & 6 deletions Design/States.drawio
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<mxfile>
<mxfile host="65bd71144e">
<diagram id="vKat9y-B0GSofk75mpwx" name="Page-1">
<mxGraphModel dx="932" dy="633" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<mxGraphModel dx="1168" dy="425" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
Expand Down Expand Up @@ -477,15 +477,27 @@
<mxCell id="4" value="Recording" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=0" parent="1" vertex="1">
<mxGeometry x="230" y="220" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="167" value="LoadAI" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="74" target="3">
<mxCell id="167" value="LoadAI" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" parent="1" source="74" target="3" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="74" value="Idle (Empty)" style="rounded=0;whiteSpace=wrap;html=1;fontStyle=0" parent="1" vertex="1">
<mxGeometry x="320" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="165" value="LoadAI" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="78" target="36">
<mxCell id="165" value="LoadAI" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" parent="1" source="78" target="36" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="168" style="edgeStyle=none;html=1;entryX=0.25;entryY=0;entryDx=0;entryDy=0;exitX=0;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="78" target="59">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="90" y="190"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="169" value="Restore Crash Data" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="168">
<mxGeometry x="-0.0589" y="-2" relative="1" as="geometry">
<mxPoint x="-31" y="14" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="78" value="Disconnected (Empty)" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="120" width="120" height="40" as="geometry"/>
</mxCell>
Expand Down Expand Up @@ -737,13 +749,13 @@
<mxPoint x="-10.600000000000001" y="-3.56" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="164" value="" style="endArrow=classic;html=1;" edge="1" parent="1">
<mxCell id="164" value="" style="endArrow=classic;html=1;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="450" y="350" as="sourcePoint"/>
<mxPoint x="500" y="300" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="166" value="" style="endArrow=classic;html=1;" edge="1" parent="1">
<mxCell id="166" value="" style="endArrow=classic;html=1;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="450" y="350" as="sourcePoint"/>
<mxPoint x="500" y="300" as="targetPoint"/>
Expand Down
11 changes: 11 additions & 0 deletions FlightRecorder.Client.Logics/IDialogLogic.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Threading.Tasks;

namespace FlightRecorder.Client.Logics;

public interface IDialogLogic
{
bool Confirm(string message);
void Error(string error);
Task<string?> SaveAsync(SavedData data);
Task<(string? fileName, SavedData? data)> LoadAsync();
}
132 changes: 67 additions & 65 deletions FlightRecorder.Client.Logics/RecorderLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,97 +4,99 @@
using System.Collections.Generic;
using System.Diagnostics;

namespace FlightRecorder.Client.Logics
namespace FlightRecorder.Client.Logics;

public class RecorderLogic : IRecorderLogic, IDisposable
{
public class RecorderLogic : IRecorderLogic, IDisposable
{
public event EventHandler<RecordsUpdatedEventArgs>? RecordsUpdated;
public event EventHandler<RecordsUpdatedEventArgs>? RecordsUpdated;

private readonly ILogger<RecorderLogic> logger;
private readonly IConnector connector;
private readonly Stopwatch stopwatch = new();
private readonly ILogger<RecorderLogic> logger;
private readonly IConnector connector;
private readonly Stopwatch stopwatch = new();

private long? startMilliseconds;
private long? endMilliseconds;
private SimStateStruct startState;
private List<(long milliseconds, AircraftPositionStruct position)> records = new();
private long? startMilliseconds;
private long? endMilliseconds;
private SimStateStruct startState;
private List<(long milliseconds, AircraftPositionStruct position)> records = new();


private SimStateStruct simState;
private SimStateStruct simState;

private bool IsStarted => startMilliseconds.HasValue && records != null;
private bool IsEnded => startMilliseconds.HasValue && endMilliseconds.HasValue;
private bool IsStarted => startMilliseconds.HasValue && records != null;
private bool IsEnded => startMilliseconds.HasValue && endMilliseconds.HasValue;

public RecorderLogic(ILogger<RecorderLogic> logger, IConnector connector)
{
logger.LogDebug("Creating instance of {class}", nameof(RecorderLogic));
this.logger = logger;
this.connector = connector;
public RecorderLogic(ILogger<RecorderLogic> logger, IConnector connector)
{
logger.LogDebug("Creating instance of {class}", nameof(RecorderLogic));
this.logger = logger;
this.connector = connector;

connector.SimStateUpdated += Connector_SimStateUpdated;
}
connector.SimStateUpdated += Connector_SimStateUpdated;
}

public void Dispose()
{
logger.LogDebug("Disposing {class}", nameof(RecorderLogic));
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose()
{
logger.LogDebug("Disposing {class}", nameof(RecorderLogic));
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (disposing)
{
connector.SimStateUpdated -= Connector_SimStateUpdated;
}
connector.SimStateUpdated -= Connector_SimStateUpdated;
}
}

private void Connector_SimStateUpdated(object? sender, SimStateUpdatedEventArgs e)
{
simState = e.State;
}
private void Connector_SimStateUpdated(object? sender, SimStateUpdatedEventArgs e)
{
simState = e.State;
}

#region Public Functions
#region Public Functions

public void Initialize()
{
logger.LogDebug("Initializing recorder...");
public void Initialize()
{
logger.LogDebug("Initializing recorder...");

stopwatch.Start();
}
stopwatch.Start();
}

public void Record()
{
logger.LogInformation("Start recording...");
public void Record()
{
logger.LogInformation("Start recording...");

startMilliseconds = stopwatch.ElapsedMilliseconds;
endMilliseconds = null;
startState = simState;
records = new List<(long milliseconds, AircraftPositionStruct position)>();
}
startMilliseconds = stopwatch.ElapsedMilliseconds;
endMilliseconds = null;
startState = simState;
records = new List<(long milliseconds, AircraftPositionStruct position)>();
}

public void StopRecording()
public void StopRecording()
{
if (endMilliseconds == null)
{
endMilliseconds = stopwatch.ElapsedMilliseconds;
logger.LogDebug("Recording stopped. {totalFrames} frames recorded.", records.Count);
}
}

public void NotifyPosition(AircraftPositionStruct? value)
{
if (IsStarted && !IsEnded && value.HasValue)
{
records.Add((stopwatch.ElapsedMilliseconds, value.Value));
RecordsUpdated?.Invoke(this, new(null, startState.AircraftTitle, records.Count));
}
}

public SavedData ToData(string clientVersion)
public void NotifyPosition(AircraftPositionStruct? value)
{
if (IsStarted && !IsEnded && value.HasValue)
{
if (startMilliseconds == null) throw new InvalidOperationException("Cannot get data before started recording!");
if (endMilliseconds == null) throw new InvalidOperationException("Cannot get data before finished recording!");
return new(clientVersion, startMilliseconds.Value, endMilliseconds.Value, startState, records);
records.Add((stopwatch.ElapsedMilliseconds, value.Value));
RecordsUpdated?.Invoke(this, new(null, startState.AircraftTitle, records.Count));
}
}

#endregion
public SavedData ToData(string clientVersion)
{
if (startMilliseconds == null) throw new InvalidOperationException("Cannot get data before started recording!");
if (endMilliseconds == null) throw new InvalidOperationException("Cannot get data before finished recording!");
return new(clientVersion, startMilliseconds.Value, endMilliseconds.Value, startState, records);
}

#endregion
}
13 changes: 13 additions & 0 deletions FlightRecorder.Client.ViewModels/ICrashLogic.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using FlightRecorder.Client.Logics;
using System.Threading.Tasks;

namespace FlightRecorder.Client;

/// <summary>
/// Handle emergency save on crash
/// </summary>
public interface ICrashLogic
{
Task LoadDataAsync(StateMachine stateMachine, IReplayLogic replayLogic);
void SaveData();
}
13 changes: 0 additions & 13 deletions FlightRecorder.Client.ViewModels/IDialogLogic.cs

This file was deleted.

3 changes: 3 additions & 0 deletions FlightRecorder.Client.ViewModels/StateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public enum Event
Save,
Load,
LoadAI,
RestoreCrashData,
Exit
}

Expand Down Expand Up @@ -71,6 +72,8 @@ private void InitializeStateMachine()
{
Register(Transition.From(State.Start).To(State.DisconnectedEmpty).By(Event.StartUp).ThenUpdate(viewModel));

Register(Transition.From(State.DisconnectedEmpty).To(State.DisconnectedUnsaved).By(Event.RestoreCrashData).ThenUpdate(viewModel));

Register(Transition.From(State.DisconnectedEmpty).To(State.IdleEmpty).By(Event.Connect).Then(Connect).ThenUpdate(viewModel));
Register(Transition.From(State.DisconnectedEmpty).To(State.LoadingDisconnected).By(Event.RequestLoading).ThenUpdate(viewModel));
Register(Transition.From(State.DisconnectedEmpty).To(State.End).By(Event.Exit)); // NO-OP
Expand Down

0 comments on commit eb7a539

Please sign in to comment.