Skip to content

Commit

Permalink
Better handling of the stats command, both ran one time or to refresh…
Browse files Browse the repository at this point in the history
… an existing stats object
  • Loading branch information
sidewinder94 committed Jul 11, 2019
1 parent 04776fe commit 12762e4
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 40 deletions.
66 changes: 55 additions & 11 deletions SpeedifyCliWrapper/ReturnTypes/SpeedifyStats.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,80 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using JetBrains.Annotations;

namespace SpeedifyCliWrapper.ReturnTypes
{
public class SpeedifyStats : ICustomJson
public class SpeedifyStats : ICustomJson, INotifyPropertyChanged
{
public SpeedifyState State { get; private set; }
public SpeedifyState State
{
get { return this._state; }
private set
{
if (Equals(value, this._state)) return;
this._state = value;
this.OnPropertyChanged();
}
}

public List<SpeedifyAdapter> Adapters { get; private set; } = new List<SpeedifyAdapter>();
public List<SpeedifyAdapter> Adapters
{
get { return this._adapters; }
private set
{
if (Equals(value, this._adapters)) return;
this._adapters = value;
this.OnPropertyChanged();
}
}

public SpeedifyConnectionStats ConnectionStats { get; private set; }
public SpeedifyConnectionStats ConnectionStats
{
get { return this._connectionStats; }
private set
{
if (Equals(value, this._connectionStats)) return;
this._connectionStats = value;
this.OnPropertyChanged();
}
}

public SpeedifySessionStats SessionStats { get; private set; }
public SpeedifySessionStats SessionStats
{
get { return this._sessionStats; }
private set
{
if (Equals(value, this._sessionStats)) return;
this._sessionStats = value;
this.OnPropertyChanged();
}
}

private readonly IReadOnlyDictionary<string, MethodInfo> _accessorDictionary;
private SpeedifyState _state;
private List<SpeedifyAdapter> _adapters = new List<SpeedifyAdapter>();
private SpeedifyConnectionStats _connectionStats;
private SpeedifySessionStats _sessionStats;

public SpeedifyStats()
{
this._accessorDictionary = this.GetType().GetProperties().ToDictionary(kp => kp.Name.ToLower(), vp => vp.SetMethod);
}


public MethodInfo this[string part]
public MethodInfo this[string part] => this._accessorDictionary[part.Replace("_", "")];

public event PropertyChangedEventHandler PropertyChanged;

[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
get
{
return this._accessorDictionary[part.Replace("_", "")];
}
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

}
}
89 changes: 78 additions & 11 deletions SpeedifyCliWrapper/Speedify.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ private string RunSpeedifyCommand(int timeout = 60, params string[] args)
throw new ArgumentException("Timeout cannot be negative ", nameof(timeout));
}

var timeoutSpan = new TimeSpan(0, 0, 60);

var pi = new ProcessStartInfo(this.CliPath, string.Join(" ", args))
{
RedirectStandardOutput = true,
Expand All @@ -60,8 +62,18 @@ private string RunSpeedifyCommand(int timeout = 60, params string[] args)
p.Start();
p.BeginOutputReadLine();

p.WaitForExit((int)timeout * 1000);
while (!p.HasExited && (DateTime.Now - p.StartTime < timeoutSpan))
{
p.WaitForExit(200);
}

if (!p.HasExited)
{
p.WaitForExit(1);
p.Kill();
}

p.WaitForExit(1);
p.Close();

return outputBuffer.ToString();
Expand All @@ -74,6 +86,8 @@ private void LongRunningSpeedifyCommand(Action<string> callBack, CancellationTok
throw new ArgumentException("Timeout cannot be negative ", nameof(timeout));
}

var timeoutSpan = new TimeSpan(0, 0, 60);

if (!cancellationToken.CanBeCanceled)
{
throw new ArgumentException("Cancellation Token must be cancellable ", nameof(cancellationToken));
Expand All @@ -90,37 +104,62 @@ private void LongRunningSpeedifyCommand(Action<string> callBack, CancellationTok
var p = new Process()
{
StartInfo = pi

};

var sent = false;

p.OutputDataReceived += (sender, eventArgs) =>
{
outputBuffer.AppendLine(eventArgs.Data);
if (eventArgs.Data == string.Empty)
{
callBack(outputBuffer.ToString());
outputBuffer.Clear();
if (!sent)
{
callBack(outputBuffer.ToString());
outputBuffer.Clear();
sent = true;
}
}
else
{
sent = false;
}
};

p.Start();
p.BeginOutputReadLine();


if (timeout > 0)
{
p.WaitForExit((int)timeout * 1000);
while (!p.HasExited && (DateTime.Now - p.StartTime < timeoutSpan))
{
p.WaitForExit(200);
}

if (!p.HasExited)
{
p.WaitForExit(1);
p.Kill();
}

p.WaitForExit(1);
p.Close();
return;
}

cancellationToken.Register(() =>
{
p.WaitForExit(1);
p.Kill();
p.Close();
});

p.WaitForExit();

while (!cancellationToken.IsCancellationRequested)
{
p.WaitForExit(200);
}

}


Expand Down Expand Up @@ -174,16 +213,44 @@ public SpeedifyVersion Version()
return this.RunSpeedifyCommand<SpeedifyVersion>(args: "version");
}

public SpeedifyStats Stats(int duration = 0)
public SpeedifyStats Stats(int duration = 3)
{
var result = new SpeedifyStats();

var cancel = new CancellationTokenSource();
//Time at 3 is the minimum for which we'll get something back
var fusedJson = this.RunSpeedifyCommand(args: new[] { "stats", duration.ToString() });

this.LongRunningSpeedifyCommand((s) => this.HandleCustomJson(s, result), cancel.Token, 0, "stats", "10");
var splittedJson = fusedJson.Split(new[] { Environment.NewLine + Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);

foreach (var json in splittedJson.Where(sj => !string.IsNullOrWhiteSpace(sj)))
{
this.HandleCustomJson(json, result);
}

return result;
}

public void RefreshStats(SpeedifyStats toRefresh, int duration = 1, int timeout = 0)
{

var cancel = new CancellationTokenSource();

if (duration > 0)
{
cancel.CancelAfter(new TimeSpan(0, 0, duration));
}

this.AsynRefreshStats(toRefresh, cancel.Token, timeout).Wait();
}

public Task AsynRefreshStats(SpeedifyStats toRefresh, CancellationToken cancellationToken, int timeout = 0)
{
return Task.Run(() =>
{
this.LongRunningSpeedifyCommand(s => this.HandleCustomJson(s, toRefresh), cancellationToken,
timeout, "stats");
});

}
}
}
2 changes: 2 additions & 0 deletions SpeedifyCliWrapper/SpeedifyCliWrapper.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
</ItemGroup>

Expand Down
25 changes: 7 additions & 18 deletions SppedifyCliWrapperTests/SpeedifyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,17 @@ public void StatsTest()
{
var wrapper = new Speedify();

var r = wrapper.Stats(10);
var updateCount = 0;

Assert.IsNotNull(r);
Assert.IsNotNull(r.State);
}
var pop = wrapper.Stats();

pop.PropertyChanged += (sender, args) => updateCount++;

[TestMethod]
public void DeserializationTest()
{
var str = @"[""state"",
{
""state"": ""CONNECTED""
}
]
";
wrapper.RefreshStats(pop, 60);

var obj = JsonConvert.DeserializeObject(str);
var obj2 = ((JArray)obj).Children();
var obj3 = obj2.Skip(1).First().ToObject<SpeedifyState>();

Assert.IsNotNull(obj3);
Assert.IsNotNull(pop);
Assert.IsNotNull(pop.State);
Assert.IsTrue(updateCount > 3);
}
}
}

0 comments on commit 12762e4

Please sign in to comment.