From 6bccd24e8147ba65f90d60cab7d71ce24c9212b2 Mon Sep 17 00:00:00 2001 From: icylogic Date: Thu, 1 Oct 2015 12:04:32 +0800 Subject: [PATCH 01/18] Catch All Exception for GetStringAsync --- shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index 49820f63..9ed89a49 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -102,7 +102,7 @@ namespace Shadowsocks.Controller { jsonString = await new HttpClient().GetStringAsync(API); } - catch (HttpRequestException e) + catch (Exception e) { Logging.LogUsefulException(e); return null; From 7599704d1f4e63b49b1125eb2c4608c538c2b28d Mon Sep 17 00:00:00 2001 From: icylogic Date: Mon, 18 Jan 2016 01:31:50 +0800 Subject: [PATCH 02/18] add speed to statistics --- .../Service/AvailabilityStatistics.cs | 103 ++++++++++++++++-- .../Controller/Service/TCPRelay.cs | 17 ++- .../Controller/ShadowsocksController.cs | 10 +- 3 files changed, 117 insertions(+), 13 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index f9c3c3d7..30a339a0 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -23,25 +24,39 @@ namespace Shadowsocks.Controller using Statistics = Dictionary>; + //TODO: change to singleton public class AvailabilityStatistics { - public static readonly string DateTimePattern = "yyyy-MM-dd HH:mm:ss"; + public const string DateTimePattern = "yyyy-MM-dd HH:mm:ss"; private const string StatisticsFilesName = "shadowsocks.availability.csv"; private const string Delimiter = ","; private const int Timeout = 500; - private const int DelayBeforeStart = 1000; + private readonly TimeSpan DelayBeforeStart = TimeSpan.FromSeconds(1); public Statistics RawStatistics { get; private set; } public Statistics FilteredStatistics { get; private set; } public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1); private int Repeat => _config.RepeatTimesNum; - private const int RetryInterval = 2 * 60 * 1000; //retry 2 minutes after failed - private int Interval => (int)TimeSpan.FromMinutes(_config.DataCollectionMinutes).TotalMilliseconds; + private readonly TimeSpan RetryInterval = TimeSpan.FromMinutes(2); //retry 2 minutes after failed + private TimeSpan Interval => TimeSpan.FromMinutes(_config.DataCollectionMinutes); private Timer _timer; + private Timer _speedMonior; private State _state; private List _servers; private StatisticsStrategyConfiguration _config; + private const string Empty = ""; + public static string AvailabilityStatisticsFile; + //speed in KiB/s + private int _inboundSpeed = 0; + private int _outboundSpeed = 0; + private int? _latency = 0; + private Server _currentServer; + private Configuration _globalConfig; + private readonly ShadowsocksController _controller; + private long _lastInboundCounter = 0; + private long _lastOutboundCounter = 0; + private readonly TimeSpan MonitorInterval = TimeSpan.FromSeconds(1); //static constructor to initialize every public static fields before refereced static AvailabilityStatistics() @@ -49,9 +64,11 @@ namespace Shadowsocks.Controller AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName); } - public AvailabilityStatistics(Configuration config, StatisticsStrategyConfiguration statisticsConfig) + public AvailabilityStatistics(ShadowsocksController controller) { - UpdateConfiguration(config, statisticsConfig); + _controller = controller; + _globalConfig = controller.GetCurrentConfiguration(); + UpdateConfiguration(_globalConfig, controller.StatisticsConfiguration); } public bool Set(StatisticsStrategyConfiguration config) @@ -70,6 +87,7 @@ namespace Shadowsocks.Controller else { _timer?.Dispose(); + _speedMonior?.Dispose(); } return true; } @@ -80,6 +98,27 @@ namespace Shadowsocks.Controller } } + private void UpdateSpeed(object state) + { + var bytes = _controller.inboundCounter - _lastInboundCounter; + _lastInboundCounter = _controller.inboundCounter; + var inboundSpeed = GetSpeedInKiBPerSecond(bytes ,MonitorInterval.TotalSeconds); + + bytes = _controller.outboundCounter - _lastOutboundCounter; + _lastOutboundCounter = _controller.outboundCounter; + var outboundSpeed = GetSpeedInKiBPerSecond(bytes, MonitorInterval.TotalSeconds); + + if (inboundSpeed > _inboundSpeed) + { + _inboundSpeed = inboundSpeed; + } + if (outboundSpeed > _outboundSpeed) + { + _outboundSpeed = outboundSpeed; + } + Logging.Debug($"{_currentServer.FriendlyName()}: current/max inbound {inboundSpeed}/{_inboundSpeed} KiB/s, current/max outbound {outboundSpeed}/{_outboundSpeed} KiB/s"); + } + //hardcode //TODO: backup reliable isp&geolocation provider or a local database is required public static async Task GetGeolocationAndIsp() @@ -98,6 +137,7 @@ namespace Shadowsocks.Controller }; } + //TODO: remove geolocation private static async Task GetInfoFromAPI(string API) { string jsonString; @@ -146,6 +186,9 @@ namespace Shadowsocks.Controller { new KeyValuePair("Timestamp", timestamp), new KeyValuePair("Server", server.FriendlyName()), + new KeyValuePair("Latency", GetRecentLatency(server)), + new KeyValuePair("InboundSpeed", GetRecentInboundSpeed(server)), + new KeyValuePair("OutboundSpeed", GetRecentOutboundSpeed(server)), new KeyValuePair("Status", reply?.Status.ToString()), new KeyValuePair("RoundtripTime", reply?.RoundtripTime.ToString()) //new KeyValuePair("data", reply.Buffer.ToString()); // The data of reply @@ -162,11 +205,43 @@ namespace Shadowsocks.Controller return ret; } + + private string GetRecentOutboundSpeed(Server server) + { + if (server != _currentServer) return Empty; + return _outboundSpeed.ToString(); + } + + private string GetRecentInboundSpeed(Server server) + { + if (server != _currentServer) return Empty; + return _inboundSpeed.ToString(); + } + + private string GetRecentLatency(Server server) + { + if (server != _currentServer) return Empty; + return _latency == null ? Empty : _latency.ToString(); + } + + private void ResetSpeed() + { + _currentServer = _globalConfig.GetCurrentServer(); + _latency = null; + _inboundSpeed = 0; + _outboundSpeed = 0; + } + private void Run(object obj) { + if (_speedMonior?.Change(DelayBeforeStart, MonitorInterval) == null) + { + _speedMonior = new Timer(UpdateSpeed, null, DelayBeforeStart, MonitorInterval); + } LoadRawStatistics(); FilterRawStatistics(); evaluate(); + ResetSpeed(); } private async void evaluate() @@ -177,7 +252,6 @@ namespace Shadowsocks.Controller if (dataLists == null) continue; foreach (var dataList in dataLists.Where(dataList => dataList != null)) { - await geolocationAndIsp; Append(dataList, geolocationAndIsp.Result); } } @@ -211,6 +285,7 @@ namespace Shadowsocks.Controller { Set(statisticsConfig); _servers = config.configs; + ResetSpeed(); } private async void FilterRawStatistics() @@ -258,7 +333,7 @@ namespace Shadowsocks.Controller Logging.Debug($"loading statistics from {path}"); if (!File.Exists(path)) { - Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval / 60 / 1000} minutes later"); + Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval.TotalMinutes} minutes later"); _timer.Change(RetryInterval, Interval); return; } @@ -302,6 +377,7 @@ namespace Shadowsocks.Controller public const string Unknown = "Unknown"; } + //TODO: redesign model public class RawStatisticsData { public DateTime Timestamp; @@ -319,5 +395,16 @@ namespace Shadowsocks.Controller public int MinResponse; public int MaxResponse; } + + public void UpdateLatency(int latency) + { + _latency = latency; + } + + private static int GetSpeedInKiBPerSecond(long bytes, double seconds) + { + var result = (int) (bytes / seconds) / 1024; + return result; + } } } diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 2fd3c149..d5d82ed5 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -78,6 +78,11 @@ namespace Shadowsocks.Controller { _controller.UpdateOutboundCounter(n); } + + public void UpdateLatency(Server server, TimeSpan latency) + { + _controller.UpdateLatency(server, latency); + } } class TCPHandler @@ -126,6 +131,9 @@ namespace Shadowsocks.Controller private object decryptionLock = new object(); private DateTime _startConnectTime; + private DateTime _startReceivingTime; + private DateTime _startSendingTime; + private int _bytesToSend; private TCPRelay tcprelay; // TODO: tcprelay ?= relay public TCPHandler(TCPRelay tcprelay) @@ -484,6 +492,7 @@ namespace Shadowsocks.Controller { strategy.UpdateLatency(server, latency); } + tcprelay.UpdateLatency(server, latency); StartPipe(); } @@ -513,6 +522,7 @@ namespace Shadowsocks.Controller } try { + _startReceivingTime = DateTime.Now; remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); } @@ -601,13 +611,12 @@ namespace Shadowsocks.Controller } Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeConnectionReceiveCallback() (upload)"); tcprelay.UpdateOutboundCounter(bytesToSend); + _startSendingTime = DateTime.Now; + _bytesToSend = bytesToSend; remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); IStrategy strategy = controller.GetCurrentStrategy(); - if (strategy != null) - { - strategy.UpdateLastWrite(server); - } + strategy?.UpdateLastWrite(server); } else { diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index f7d50bd3..9b3206c1 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -343,7 +343,7 @@ namespace Shadowsocks.Controller if (availabilityStatistics == null) { - availabilityStatistics = new AvailabilityStatistics(_config, StatisticsConfiguration); + availabilityStatistics = new AvailabilityStatistics(this); } availabilityStatistics.UpdateConfiguration(_config, StatisticsConfiguration); @@ -498,5 +498,13 @@ namespace Shadowsocks.Controller Thread.Sleep(30 * 1000); } } + + public void UpdateLatency(Server server, TimeSpan latency) + { + if (_config.availabilityStatistics) + { + availabilityStatistics.UpdateLatency((int) latency.TotalMilliseconds); + } + } } } From b735d59d8a59329d8e83003de4f8cf8344728722 Mon Sep 17 00:00:00 2001 From: icylogic Date: Mon, 18 Jan 2016 16:28:03 +0800 Subject: [PATCH 03/18] remove ISP/Geolocation detection --- .../Service/AvailabilityStatistics.cs | 82 ++----------------- 1 file changed, 5 insertions(+), 77 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index 30a339a0..ef64197d 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -119,56 +119,6 @@ namespace Shadowsocks.Controller Logging.Debug($"{_currentServer.FriendlyName()}: current/max inbound {inboundSpeed}/{_inboundSpeed} KiB/s, current/max outbound {outboundSpeed}/{_outboundSpeed} KiB/s"); } - //hardcode - //TODO: backup reliable isp&geolocation provider or a local database is required - public static async Task GetGeolocationAndIsp() - { - Logging.Debug("Retrive information of geolocation and isp"); - const string API = "http://ip-api.com/json"; - const string alternativeAPI = "http://www.telize.com/geoip"; //must be comptible with current API - var result = await GetInfoFromAPI(API); - if (result != null) return result; - result = await GetInfoFromAPI(alternativeAPI); - if (result != null) return result; - return new DataList - { - new DataUnit(State.Geolocation, State.Unknown), - new DataUnit(State.ISP, State.Unknown) - }; - } - - //TODO: remove geolocation - private static async Task GetInfoFromAPI(string API) - { - string jsonString; - try - { - jsonString = await new HttpClient().GetStringAsync(API); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - return null; - } - JObject obj; - try - { - obj = JObject.Parse(jsonString); - } - catch (JsonReaderException) - { - return null; - } - string country = (string)obj["country"]; - string city = (string)obj["city"]; - string isp = (string)obj["isp"]; - if (country == null || city == null || isp == null) return null; - return new DataList { - new DataUnit(State.Geolocation, $"\"{country} {city}\""), - new DataUnit(State.ISP, $"\"{isp}\"") - }; - } - private async Task> ICMPTest(Server server) { Logging.Debug("Ping " + server.FriendlyName()); @@ -186,11 +136,11 @@ namespace Shadowsocks.Controller { new KeyValuePair("Timestamp", timestamp), new KeyValuePair("Server", server.FriendlyName()), + new KeyValuePair("Status", reply?.Status.ToString()), + new KeyValuePair("RoundtripTime", reply?.RoundtripTime.ToString()), new KeyValuePair("Latency", GetRecentLatency(server)), new KeyValuePair("InboundSpeed", GetRecentInboundSpeed(server)), - new KeyValuePair("OutboundSpeed", GetRecentOutboundSpeed(server)), - new KeyValuePair("Status", reply?.Status.ToString()), - new KeyValuePair("RoundtripTime", reply?.RoundtripTime.ToString()) + new KeyValuePair("OutboundSpeed", GetRecentOutboundSpeed(server)) //new KeyValuePair("data", reply.Buffer.ToString()); // The data of reply }); Thread.Sleep(Timeout + new Random().Next() % Timeout); @@ -246,13 +196,12 @@ namespace Shadowsocks.Controller private async void evaluate() { - var geolocationAndIsp = GetGeolocationAndIsp(); foreach (var dataLists in await TaskEx.WhenAll(_servers.Select(ICMPTest))) { if (dataLists == null) continue; foreach (var dataList in dataLists.Where(dataList => dataList != null)) { - Append(dataList, geolocationAndIsp.Result); + Append(dataList, Enumerable.Empty()); } } } @@ -298,19 +247,6 @@ namespace Shadowsocks.Controller foreach (IEnumerable rawData in RawStatistics.Values) { var filteredData = rawData; - if (_config.ByIsp) - { - var current = await GetGeolocationAndIsp(); - filteredData = - filteredData.Where( - data => - data.Geolocation == current[0].Value || - data.Geolocation == State.Unknown); - filteredData = - filteredData.Where( - data => data.ISP == current[1].Value || data.ISP == State.Unknown); - if (filteredData.LongCount() == 0) return; - } if (_config.ByHourOfDay) { var currentHour = DateTime.Now.Hour; @@ -344,11 +280,7 @@ namespace Shadowsocks.Controller Timestamp = ParseExactOrUnknown(strings[0]), ServerName = strings[1], ICMPStatus = strings[2], - RoundtripTime = int.Parse(strings[3]), - Geolocation = 5 > strings.Length ? - null - : strings[4], - ISP = 6 > strings.Length ? null : strings[5] + RoundtripTime = int.Parse(strings[3]) } group rawData by rawData.ServerName into server select new @@ -372,8 +304,6 @@ namespace Shadowsocks.Controller public class State { public DataList dataList = new DataList(); - public const string Geolocation = "Geolocation"; - public const string ISP = "ISP"; public const string Unknown = "Unknown"; } @@ -384,8 +314,6 @@ namespace Shadowsocks.Controller public string ServerName; public string ICMPStatus; public int RoundtripTime; - public string Geolocation; - public string ISP; } public class StatisticsData From 6df589facf01b2d3556a6516447fa37ef21ab7c9 Mon Sep 17 00:00:00 2001 From: icylogic Date: Mon, 18 Jan 2016 16:42:33 +0800 Subject: [PATCH 04/18] fix error caused by type migration --- .../Controller/Service/AvailabilityStatistics.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index 3039a9a0..d5a2efec 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -285,8 +285,8 @@ namespace Shadowsocks.Controller { Logging.LogUsefulException(e); } - if (!File.Exists(path)) { - Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval / 60 / 1000} minutes later"); + if (!File.Exists(path)) { + Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval.TotalMinutes} minutes later"); _timer.Change(RetryInterval, Interval); return; } From aef4b86c917a1367b31404b68ac4817f25090c60 Mon Sep 17 00:00:00 2001 From: icylogic Date: Mon, 18 Jan 2016 17:21:06 +0800 Subject: [PATCH 05/18] refactor AvailabilityStatistics to singleton --- .../Service/AvailabilityStatistics.cs | 74 ++++++++----------- .../Controller/ShadowsocksController.cs | 10 +-- 2 files changed, 35 insertions(+), 49 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index d5a2efec..3bf0d13e 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -1,19 +1,13 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; -using System.Net.Http; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; - -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - using Shadowsocks.Model; using Shadowsocks.Util; @@ -24,19 +18,23 @@ namespace Shadowsocks.Controller using Statistics = Dictionary>; - //TODO: change to singleton - public class AvailabilityStatistics + + public sealed class AvailabilityStatistics { + // Static Singleton Initialization + public static AvailabilityStatistics Instance { get; } = new AvailabilityStatistics(); + private AvailabilityStatistics() { } + public const string DateTimePattern = "yyyy-MM-dd HH:mm:ss"; private const string StatisticsFilesName = "shadowsocks.availability.csv"; private const string Delimiter = ","; - private const int Timeout = 500; - private readonly TimeSpan DelayBeforeStart = TimeSpan.FromSeconds(1); + private const int TimeoutMilliseconds = 500; + private readonly TimeSpan _delayBeforeStart = TimeSpan.FromSeconds(1); public Statistics RawStatistics { get; private set; } public Statistics FilteredStatistics { get; private set; } public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1); private int Repeat => _config.RepeatTimesNum; - private readonly TimeSpan RetryInterval = TimeSpan.FromMinutes(2); //retry 2 minutes after failed + private readonly TimeSpan _retryInterval = TimeSpan.FromMinutes(2); //retry 2 minutes after failed private TimeSpan Interval => TimeSpan.FromMinutes(_config.DataCollectionMinutes); private Timer _timer; private Timer _speedMonior; @@ -53,10 +51,10 @@ namespace Shadowsocks.Controller private int? _latency = 0; private Server _currentServer; private Configuration _globalConfig; - private readonly ShadowsocksController _controller; + private ShadowsocksController _controller; private long _lastInboundCounter = 0; private long _lastOutboundCounter = 0; - private readonly TimeSpan MonitorInterval = TimeSpan.FromSeconds(1); + private readonly TimeSpan _monitorInterval = TimeSpan.FromSeconds(1); //static constructor to initialize every public static fields before refereced static AvailabilityStatistics() @@ -64,13 +62,6 @@ namespace Shadowsocks.Controller AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName); } - public AvailabilityStatistics(ShadowsocksController controller) - { - _controller = controller; - _globalConfig = controller.GetCurrentConfiguration(); - UpdateConfiguration(_globalConfig, controller.StatisticsConfiguration); - } - public bool Set(StatisticsStrategyConfiguration config) { _config = config; @@ -78,10 +69,10 @@ namespace Shadowsocks.Controller { if (config.StatisticsEnabled) { - if (_timer?.Change(DelayBeforeStart, Interval) == null) + if (_timer?.Change(_delayBeforeStart, Interval) == null) { _state = new State(); - _timer = new Timer(Run, _state, DelayBeforeStart, Interval); + _timer = new Timer(Run, _state, _delayBeforeStart, Interval); } } else @@ -102,11 +93,11 @@ namespace Shadowsocks.Controller { var bytes = _controller.inboundCounter - _lastInboundCounter; _lastInboundCounter = _controller.inboundCounter; - var inboundSpeed = GetSpeedInKiBPerSecond(bytes ,MonitorInterval.TotalSeconds); + var inboundSpeed = GetSpeedInKiBPerSecond(bytes ,_monitorInterval.TotalSeconds); bytes = _controller.outboundCounter - _lastOutboundCounter; _lastOutboundCounter = _controller.outboundCounter; - var outboundSpeed = GetSpeedInKiBPerSecond(bytes, MonitorInterval.TotalSeconds); + var outboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds); if (inboundSpeed > _inboundSpeed) { @@ -133,7 +124,7 @@ namespace Shadowsocks.Controller //ICMP echo. we can also set options and special bytes try { - var reply = await ping.SendTaskAsync(IP, Timeout); + var reply = await ping.SendTaskAsync(IP, TimeoutMilliseconds); ret.Add(new List> { new KeyValuePair("Timestamp", timestamp), @@ -145,7 +136,7 @@ namespace Shadowsocks.Controller new KeyValuePair("OutboundSpeed", GetRecentOutboundSpeed(server)) //new KeyValuePair("data", reply.Buffer.ToString()); // The data of reply }); - Thread.Sleep(Timeout + new Random().Next() % Timeout); + Thread.Sleep(TimeoutMilliseconds + new Random().Next() % TimeoutMilliseconds); //Do ICMPTest in a random frequency } catch (Exception e) @@ -165,14 +156,12 @@ namespace Shadowsocks.Controller private string GetRecentOutboundSpeed(Server server) { - if (server != _currentServer) return Empty; - return _outboundSpeed.ToString(); + return server != _currentServer ? Empty : _outboundSpeed.ToString(); } private string GetRecentInboundSpeed(Server server) { - if (server != _currentServer) return Empty; - return _inboundSpeed.ToString(); + return server != _currentServer ? Empty : _inboundSpeed.ToString(); } private string GetRecentLatency(Server server) @@ -183,7 +172,7 @@ namespace Shadowsocks.Controller private void ResetSpeed() { - _currentServer = _globalConfig.GetCurrentServer(); + _currentServer = _controller.GetCurrentServer(); _latency = null; _inboundSpeed = 0; _outboundSpeed = 0; @@ -191,17 +180,17 @@ namespace Shadowsocks.Controller private void Run(object obj) { - if (_speedMonior?.Change(DelayBeforeStart, MonitorInterval) == null) + if (_speedMonior?.Change(_delayBeforeStart, _monitorInterval) == null) { - _speedMonior = new Timer(UpdateSpeed, null, DelayBeforeStart, MonitorInterval); + _speedMonior = new Timer(UpdateSpeed, null, _delayBeforeStart, _monitorInterval); } LoadRawStatistics(); FilterRawStatistics(); - evaluate(); + Evaluate(); ResetSpeed(); } - private async void evaluate() + private async void Evaluate() { foreach (var dataLists in await TaskEx.WhenAll(_servers.Select(ICMPTest))) { @@ -237,14 +226,15 @@ namespace Shadowsocks.Controller } } - internal void UpdateConfiguration(Configuration config, StatisticsStrategyConfiguration statisticsConfig) + internal void UpdateConfiguration(ShadowsocksController controller) { - Set(statisticsConfig); - _servers = config.configs; + _controller = controller; ResetSpeed(); + Set(controller.StatisticsConfiguration); + _servers = _controller.GetCurrentConfiguration().configs; } - private async void FilterRawStatistics() + private void FilterRawStatistics() { if (RawStatistics == null) return; if (FilteredStatistics == null) @@ -277,7 +267,7 @@ namespace Shadowsocks.Controller if (!File.Exists(path)) { try { - using (FileStream fs = File.Create(path)) + using (var fs = File.Create(path)) { //do nothing } @@ -286,8 +276,8 @@ namespace Shadowsocks.Controller Logging.LogUsefulException(e); } if (!File.Exists(path)) { - Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval.TotalMinutes} minutes later"); - _timer.Change(RetryInterval, Interval); + Console.WriteLine($"statistics file does not exist, try to reload {_retryInterval.TotalMinutes} minutes later"); + _timer.Change(_retryInterval, Interval); return; } } diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 9b3206c1..551e2401 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -30,7 +30,7 @@ namespace Shadowsocks.Controller private StrategyManager _strategyManager; private PolipoRunner polipoRunner; private GFWListUpdater gfwListUpdater; - public AvailabilityStatistics availabilityStatistics { get; private set; } + public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance; public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; } public long inboundCounter = 0; @@ -268,7 +268,7 @@ namespace Shadowsocks.Controller public void UpdateStatisticsConfiguration(bool enabled) { if (availabilityStatistics == null) return; - availabilityStatistics.UpdateConfiguration(_config, StatisticsConfiguration); + availabilityStatistics.UpdateConfiguration(this); _config.availabilityStatistics = enabled; SaveConfig(_config); } @@ -341,11 +341,7 @@ namespace Shadowsocks.Controller gfwListUpdater.Error += pacServer_PACUpdateError; } - if (availabilityStatistics == null) - { - availabilityStatistics = new AvailabilityStatistics(this); - } - availabilityStatistics.UpdateConfiguration(_config, StatisticsConfiguration); + availabilityStatistics.UpdateConfiguration(this); if (_listener != null) { From ce67551d31083373d89a77fc46d6d45b73126743 Mon Sep 17 00:00:00 2001 From: icylogic Date: Tue, 19 Jan 2016 01:46:21 +0800 Subject: [PATCH 06/18] migrate to record data model --- .../Service/AvailabilityStatistics.cs | 316 +++++++++--------- .../Controller/Strategy/StatisticsStrategy.cs | 39 ++- shadowsocks-csharp/Model/Server.cs | 5 + shadowsocks-csharp/Model/StatisticsRecord.cs | 76 +++++ .../Model/StatisticsStrategyConfiguration.cs | 4 +- .../StatisticsStrategyConfigurationForm.cs | 14 +- shadowsocks-csharp/shadowsocks-csharp.csproj | 1 + 7 files changed, 268 insertions(+), 187 deletions(-) create mode 100644 shadowsocks-csharp/Model/StatisticsRecord.cs diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index 3bf0d13e..6a41210a 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -8,53 +8,40 @@ using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; +using Newtonsoft.Json; using Shadowsocks.Model; using Shadowsocks.Util; namespace Shadowsocks.Controller { - using DataUnit = KeyValuePair; - using DataList = List>; - - using Statistics = Dictionary>; - + using Statistics = Dictionary>; public sealed class AvailabilityStatistics { - // Static Singleton Initialization - public static AvailabilityStatistics Instance { get; } = new AvailabilityStatistics(); - private AvailabilityStatistics() { } - public const string DateTimePattern = "yyyy-MM-dd HH:mm:ss"; - private const string StatisticsFilesName = "shadowsocks.availability.csv"; - private const string Delimiter = ","; + private const string StatisticsFilesName = "shadowsocks.availability.json"; private const int TimeoutMilliseconds = 500; - private readonly TimeSpan _delayBeforeStart = TimeSpan.FromSeconds(1); - public Statistics RawStatistics { get; private set; } - public Statistics FilteredStatistics { get; private set; } - public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1); - private int Repeat => _config.RepeatTimesNum; - private readonly TimeSpan _retryInterval = TimeSpan.FromMinutes(2); //retry 2 minutes after failed - private TimeSpan Interval => TimeSpan.FromMinutes(_config.DataCollectionMinutes); - private Timer _timer; - private Timer _speedMonior; - private State _state; - private List _servers; - private StatisticsStrategyConfiguration _config; - private const string Empty = ""; + public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1); public static string AvailabilityStatisticsFile; - //speed in KiB/s - private int _inboundSpeed = 0; - private int _outboundSpeed = 0; - private int? _latency = 0; - private Server _currentServer; - private Configuration _globalConfig; - private ShadowsocksController _controller; - private long _lastInboundCounter = 0; - private long _lastOutboundCounter = 0; + private readonly TimeSpan _delayBeforeStart = TimeSpan.FromSeconds(1); private readonly TimeSpan _monitorInterval = TimeSpan.FromSeconds(1); + private readonly TimeSpan _retryInterval = TimeSpan.FromMinutes(2); //retry 2 minutes after failed + private readonly TimeSpan _writingInterval = TimeSpan.FromMinutes(1); + private StatisticsStrategyConfiguration _config; + private ShadowsocksController _controller; + private Server _currentServer; + //speed in KiB/s + private List _inboundSpeedRecords; + private long _lastInboundCounter; + private long _lastOutboundCounter; + private List _latencyRecords; + private List _outboundSpeedRecords; + private Timer _recorder; + private List _servers; + private Timer _speedMonior; + private Timer _writer; //static constructor to initialize every public static fields before refereced static AvailabilityStatistics() @@ -62,6 +49,18 @@ namespace Shadowsocks.Controller AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName); } + private AvailabilityStatistics() + { + RawStatistics = new Statistics(); + } + + // Static Singleton Initialization + public static AvailabilityStatistics Instance { get; } = new AvailabilityStatistics(); + public Statistics RawStatistics { get; private set; } + public Statistics FilteredStatistics { get; private set; } + private int Repeat => _config.RepeatTimesNum; + private TimeSpan RecordingInterval => TimeSpan.FromMinutes(_config.DataCollectionMinutes); + public bool Set(StatisticsStrategyConfiguration config) { _config = config; @@ -69,15 +68,23 @@ namespace Shadowsocks.Controller { if (config.StatisticsEnabled) { - if (_timer?.Change(_delayBeforeStart, Interval) == null) + if (_recorder?.Change(_delayBeforeStart, RecordingInterval) == null) + { + _recorder = new Timer(Run, null, _delayBeforeStart, RecordingInterval); + } + LoadRawStatistics(); + if (_speedMonior?.Change(_delayBeforeStart, _monitorInterval) == null) + { + _speedMonior = new Timer(UpdateSpeed, null, _delayBeforeStart, _monitorInterval); + } + if (_writer?.Change(_delayBeforeStart, RecordingInterval) == null) { - _state = new State(); - _timer = new Timer(Run, _state, _delayBeforeStart, Interval); + _writer = new Timer(Save, null, _delayBeforeStart, RecordingInterval); } } else { - _timer?.Dispose(); + _recorder?.Dispose(); _speedMonior?.Dispose(); } return true; @@ -93,51 +100,49 @@ namespace Shadowsocks.Controller { var bytes = _controller.inboundCounter - _lastInboundCounter; _lastInboundCounter = _controller.inboundCounter; - var inboundSpeed = GetSpeedInKiBPerSecond(bytes ,_monitorInterval.TotalSeconds); + var inboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds); + _inboundSpeedRecords.Add(inboundSpeed); bytes = _controller.outboundCounter - _lastOutboundCounter; _lastOutboundCounter = _controller.outboundCounter; var outboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds); + _outboundSpeedRecords.Add(outboundSpeed); - if (inboundSpeed > _inboundSpeed) - { - _inboundSpeed = inboundSpeed; - } - if (outboundSpeed > _outboundSpeed) - { - _outboundSpeed = outboundSpeed; - } - Logging.Debug($"{_currentServer.FriendlyName()}: current/max inbound {inboundSpeed}/{_inboundSpeed} KiB/s, current/max outbound {outboundSpeed}/{_outboundSpeed} KiB/s"); + Logging.Debug( + $"{_currentServer.FriendlyName()}: current/max inbound {inboundSpeed}/{_inboundSpeedRecords.Max()} KiB/s, current/max outbound {outboundSpeed}/{_outboundSpeedRecords.Max()} KiB/s"); } - private async Task> ICMPTest(Server server) + private async Task ICMPTest(Server server) { Logging.Debug("Ping " + server.FriendlyName()); if (server.server == "") return null; - var ret = new List(); - try { - var IP = Dns.GetHostAddresses(server.server).First(ip => (ip.AddressFamily == AddressFamily.InterNetwork || ip.AddressFamily == AddressFamily.InterNetworkV6)); + var result = new ICMPResult(server); + try + { + var IP = + Dns.GetHostAddresses(server.server) + .First( + ip => + ip.AddressFamily == AddressFamily.InterNetwork || + ip.AddressFamily == AddressFamily.InterNetworkV6); var ping = new Ping(); - foreach (var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString(DateTimePattern))) + foreach (var _ in Enumerable.Range(0, Repeat)) { - //ICMP echo. we can also set options and special bytes try { var reply = await ping.SendTaskAsync(IP, TimeoutMilliseconds); - ret.Add(new List> - { - new KeyValuePair("Timestamp", timestamp), - new KeyValuePair("Server", server.FriendlyName()), - new KeyValuePair("Status", reply?.Status.ToString()), - new KeyValuePair("RoundtripTime", reply?.RoundtripTime.ToString()), - new KeyValuePair("Latency", GetRecentLatency(server)), - new KeyValuePair("InboundSpeed", GetRecentInboundSpeed(server)), - new KeyValuePair("OutboundSpeed", GetRecentOutboundSpeed(server)) - //new KeyValuePair("data", reply.Buffer.ToString()); // The data of reply - }); - Thread.Sleep(TimeoutMilliseconds + new Random().Next() % TimeoutMilliseconds); + if (!reply.Status.Equals(IPStatus.Success)) + { + result.RoundtripTime.Add((int?) reply.RoundtripTime); + } + else + { + result.RoundtripTime.Add(null); + } + //Do ICMPTest in a random frequency + Thread.Sleep(TimeoutMilliseconds + new Random().Next()%TimeoutMilliseconds); } catch (Exception e) { @@ -145,63 +150,73 @@ namespace Shadowsocks.Controller Logging.LogUsefulException(e); } } - }catch(Exception e) + } + catch (Exception e) { Logging.Error($"An exception occured while eveluating {server.FriendlyName()}"); Logging.LogUsefulException(e); } - return ret; + return result; } - - private string GetRecentOutboundSpeed(Server server) + private void Reset() { - return server != _currentServer ? Empty : _outboundSpeed.ToString(); + _inboundSpeedRecords = new List(); + _outboundSpeedRecords = new List(); + _latencyRecords = new List(); } - private string GetRecentInboundSpeed(Server server) + private void Run(object _) { - return server != _currentServer ? Empty : _inboundSpeed.ToString(); + AppendRecord(); + Reset(); + FilterRawStatistics(); } - private string GetRecentLatency(Server server) + private async void AppendRecord() { - if (server != _currentServer) return Empty; - return _latency == null ? Empty : _latency.ToString(); - } + //todo: option for icmp test + var icmpResults = TaskEx.WhenAll(_servers.Select(ICMPTest)); - private void ResetSpeed() - { - _currentServer = _controller.GetCurrentServer(); - _latency = null; - _inboundSpeed = 0; - _outboundSpeed = 0; - } + var currentServerRecord = new StatisticsRecord(_currentServer.Identifier(), + _inboundSpeedRecords, _outboundSpeedRecords, _latencyRecords); - private void Run(object obj) - { - if (_speedMonior?.Change(_delayBeforeStart, _monitorInterval) == null) + foreach (var result in (await icmpResults).Where(result => result != null)) { - _speedMonior = new Timer(UpdateSpeed, null, _delayBeforeStart, _monitorInterval); + List records; + if (!RawStatistics.TryGetValue(result.Server.Identifier(), out records)) + { + records = new List(); + } + + if (result.Server.Equals(_currentServer)) + { + currentServerRecord.setResponse(result.RoundtripTime); + records.Add(currentServerRecord); + } + else + { + records.Add(new StatisticsRecord(result.Server.Identifier(), result.RoundtripTime)); + } + RawStatistics[result.Server.Identifier()] = records; } - LoadRawStatistics(); - FilterRawStatistics(); - Evaluate(); - ResetSpeed(); } - private async void Evaluate() + private void Save(object _) { - foreach (var dataLists in await TaskEx.WhenAll(_servers.Select(ICMPTest))) + try { - if (dataLists == null) continue; - foreach (var dataList in dataLists.Where(dataList => dataList != null)) - { - Append(dataList, Enumerable.Empty()); - } + File.WriteAllText(AvailabilityStatisticsFile, + JsonConvert.SerializeObject(RawStatistics, Formatting.None)); + } + catch (IOException e) + { + Logging.LogUsefulException(e); + _writer.Change(_retryInterval, _writingInterval); } } + /* private static void Append(DataList dataList, IEnumerable extra) { var data = dataList.Concat(extra); @@ -225,15 +240,28 @@ namespace Shadowsocks.Controller Logging.LogUsefulException(e); } } + */ internal void UpdateConfiguration(ShadowsocksController controller) { _controller = controller; - ResetSpeed(); + _currentServer = _controller.GetCurrentServer(); + Reset(); Set(controller.StatisticsConfiguration); _servers = _controller.GetCurrentConfiguration().configs; } + private bool IsValidRecord(StatisticsRecord record) + { + if (_config.ByHourOfDay) + { + var currentHour = DateTime.Now.Hour; + if (record.Timestamp == UnknownDateTime) return false; + if (!record.Timestamp.Hour.Equals(DateTime.Now.Hour)) return false; + } + return true; + } + private void FilterRawStatistics() { if (RawStatistics == null) return; @@ -241,20 +269,12 @@ namespace Shadowsocks.Controller { FilteredStatistics = new Statistics(); } - foreach (IEnumerable rawData in RawStatistics.Values) + + foreach (var serverAndRecords in RawStatistics) { - var filteredData = rawData; - if (_config.ByHourOfDay) - { - var currentHour = DateTime.Now.Hour; - filteredData = filteredData.Where(data => - data.Timestamp != UnknownDateTime && data.Timestamp.Hour.Equals(currentHour) - ); - if (filteredData.LongCount() == 0) return; - } - var dataList = filteredData as List ?? filteredData.ToList(); - var serverName = dataList[0].ServerName; - FilteredStatistics[serverName] = dataList; + var server = serverAndRecords.Key; + var filteredRecords = serverAndRecords.Value.FindAll(IsValidRecord); + FilteredStatistics[server] = filteredRecords; } } @@ -266,36 +286,26 @@ namespace Shadowsocks.Controller Logging.Debug($"loading statistics from {path}"); if (!File.Exists(path)) { - try { + try + { using (var fs = File.Create(path)) { //do nothing } - }catch(Exception e) + } + catch (Exception e) { Logging.LogUsefulException(e); } - if (!File.Exists(path)) { - Console.WriteLine($"statistics file does not exist, try to reload {_retryInterval.TotalMinutes} minutes later"); - _timer.Change(_retryInterval, Interval); + if (!File.Exists(path)) + { + Console.WriteLine( + $"statistics file does not exist, try to reload {_retryInterval.TotalMinutes} minutes later"); + _recorder.Change(_retryInterval, RecordingInterval); return; } } - RawStatistics = (from l in File.ReadAllLines(path).Skip(1) - let strings = l.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) - let rawData = new RawStatisticsData - { - Timestamp = ParseExactOrUnknown(strings[0]), - ServerName = strings[1], - ICMPStatus = strings[2], - RoundtripTime = int.Parse(strings[3]) - } - group rawData by rawData.ServerName into server - select new - { - ServerName = server.Key, - data = server.ToList() - }).ToDictionary(server => server.ServerName, server => server.data); + RawStatistics = JsonConvert.DeserializeObject(File.ReadAllText(path)) ?? RawStatistics; } catch (Exception e) { @@ -306,41 +316,31 @@ namespace Shadowsocks.Controller private DateTime ParseExactOrUnknown(string str) { DateTime dateTime; - return !DateTime.TryParseExact(str, DateTimePattern, null, DateTimeStyles.None, out dateTime) ? UnknownDateTime : dateTime; + return !DateTime.TryParseExact(str, DateTimePattern, null, DateTimeStyles.None, out dateTime) + ? UnknownDateTime + : dateTime; } - public class State + public void UpdateLatency(int latency) { - public DataList dataList = new DataList(); - public const string Unknown = "Unknown"; + _latencyRecords.Add(latency); } - //TODO: redesign model - public class RawStatisticsData + private static int GetSpeedInKiBPerSecond(long bytes, double seconds) { - public DateTime Timestamp; - public string ServerName; - public string ICMPStatus; - public int RoundtripTime; + var result = (int) (bytes/seconds)/1024; + return result; } - public class StatisticsData + private class ICMPResult { - public float PackageLoss; - public int AverageResponse; - public int MinResponse; - public int MaxResponse; - } + internal readonly List RoundtripTime = new List(); + internal readonly Server Server; - public void UpdateLatency(int latency) - { - _latency = latency; - } - - private static int GetSpeedInKiBPerSecond(long bytes, double seconds) - { - var result = (int) (bytes / seconds) / 1024; - return result; + internal ICMPResult(Server server) + { + Server = server; + } } } -} +} \ No newline at end of file diff --git a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs index df34c994..3567291e 100644 --- a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs +++ b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs @@ -11,12 +11,14 @@ using Shadowsocks.Model; namespace Shadowsocks.Controller.Strategy { + using Statistics = Dictionary>; class StatisticsStrategy : IStrategy { private readonly ShadowsocksController _controller; private Server _currentServer; private readonly Timer _timer; - private Dictionary> _filteredStatistics; + private Statistics _filteredStatistics; + private AvailabilityStatistics Service => _controller.availabilityStatistics; private int ChoiceKeptMilliseconds => (int) TimeSpan.FromMinutes(_controller.StatisticsConfiguration.ChoiceKeptMinutes).TotalMilliseconds; @@ -39,7 +41,10 @@ namespace Shadowsocks.Controller.Strategy private void LoadStatistics() { - _filteredStatistics = _controller.availabilityStatistics.RawStatistics ?? _filteredStatistics ?? new Dictionary>(); + _filteredStatistics = + Service.FilteredStatistics ?? + Service.RawStatistics ?? + _filteredStatistics; } //return the score by data @@ -47,28 +52,26 @@ namespace Shadowsocks.Controller.Strategy private float GetScore(string serverName) { var config = _controller.StatisticsConfiguration; - List dataList; - if (_filteredStatistics == null || !_filteredStatistics.TryGetValue(serverName, out dataList)) return 0; - var successTimes = (float) dataList.Count(data => data.ICMPStatus.Equals(IPStatus.Success.ToString())); - var timedOutTimes = (float) dataList.Count(data => data.ICMPStatus.Equals(IPStatus.TimedOut.ToString())); - var statisticsData = new AvailabilityStatistics.StatisticsData - { - PackageLoss = timedOutTimes/(successTimes + timedOutTimes)*100, - AverageResponse = Convert.ToInt32(dataList.Average(data => data.RoundtripTime)), - MinResponse = dataList.Min(data => data.RoundtripTime), - MaxResponse = dataList.Max(data => data.RoundtripTime) - }; + List records; + if (_filteredStatistics == null || !_filteredStatistics.TryGetValue(serverName, out records)) return 0; float factor; float score = 0; + + var averageRecord = new StatisticsRecord(serverName, + records.FindAll(record => record.MaxInboundSpeed != null).Select(record => record.MaxInboundSpeed.Value), + records.FindAll(record => record.MaxOutboundSpeed != null).Select(record => record.MaxOutboundSpeed.Value), + records.FindAll(record => record.AverageLatency != null).Select(record => record.AverageLatency.Value)); + averageRecord.setResponse(records.Select(record => record.AverageResponse)); + if (!config.Calculations.TryGetValue("PackageLoss", out factor)) factor = 0; - score += statisticsData.PackageLoss*factor; + score += averageRecord.PackageLoss*factor ?? 0; if (!config.Calculations.TryGetValue("AverageResponse", out factor)) factor = 0; - score += statisticsData.AverageResponse*factor; + score += averageRecord.AverageResponse*factor ?? 0; if (!config.Calculations.TryGetValue("MinResponse", out factor)) factor = 0; - score += statisticsData.MinResponse*factor; + score += averageRecord.MinResponse*factor ?? 0; if (!config.Calculations.TryGetValue("MaxResponse", out factor)) factor = 0; - score += statisticsData.MaxResponse*factor; - Logging.Debug($"{serverName} {JsonConvert.SerializeObject(statisticsData)}"); + score += averageRecord.MaxResponse*factor ?? 0; + Logging.Debug($"{JsonConvert.SerializeObject(averageRecord, Formatting.Indented)}"); return score; } diff --git a/shadowsocks-csharp/Model/Server.cs b/shadowsocks-csharp/Model/Server.cs index 8306d9e3..54c29b6f 100755 --- a/shadowsocks-csharp/Model/Server.cs +++ b/shadowsocks-csharp/Model/Server.cs @@ -95,5 +95,10 @@ namespace Shadowsocks.Model throw new FormatException(); } } + + public string Identifier() + { + return server + ':' + server_port; + } } } diff --git a/shadowsocks-csharp/Model/StatisticsRecord.cs b/shadowsocks-csharp/Model/StatisticsRecord.cs new file mode 100644 index 00000000..181567ce --- /dev/null +++ b/shadowsocks-csharp/Model/StatisticsRecord.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Shadowsocks.Model +{ + // Simple processed records for a short period of time + public class StatisticsRecord + { + public DateTime Timestamp; + + public string ServerName; + + // these fields ping-only records would be null + public int? AverageLatency; + public int? MinLatency; + public int? MaxLatency; + + public int? AverageInboundSpeed; + public int? MinInboundSpeed; + public int? MaxInboundSpeed; + + public int? AverageOutboundSpeed; + public int? MinOutboundSpeed; + public int? MaxOutboundSpeed; + + // if user disabled ping test, response would be null + public int? AverageResponse; + public int? MinResponse; + public int? MaxResponse; + public float? PackageLoss; + + public StatisticsRecord(string identifier, IEnumerable inboundSpeedRecords, IEnumerable outboundSpeedRecords, IEnumerable latencyRecords) + { + Timestamp = DateTime.Now; + ServerName = identifier; + if (inboundSpeedRecords != null && inboundSpeedRecords.Any()) + { + AverageInboundSpeed = (int) inboundSpeedRecords.Average(); + MinInboundSpeed = inboundSpeedRecords.Min(); + MaxInboundSpeed = inboundSpeedRecords.Max(); + } + if (outboundSpeedRecords != null && outboundSpeedRecords.Any()) + { + AverageOutboundSpeed = (int) outboundSpeedRecords.Average(); + MinOutboundSpeed = outboundSpeedRecords.Min(); + MaxOutboundSpeed = outboundSpeedRecords.Max(); + } + if (latencyRecords != null && latencyRecords.Any()) + { + AverageLatency = (int) latencyRecords.Average(); + MinLatency = latencyRecords.Min(); + MaxLatency = latencyRecords.Max(); + } + } + + public StatisticsRecord(string identifier, IEnumerable responseRecords) + { + Timestamp = DateTime.Now; + ServerName = identifier; + setResponse(responseRecords); + } + + public void setResponse(IEnumerable responseRecords) + { + if (responseRecords == null) return; + var records = responseRecords.Where(response => response != null).Select(response => response.Value).ToList(); + if (!records.Any()) return; + AverageResponse = (int?) records.Average(); + MinResponse = records.Min(); + MaxResponse = records.Max(); + PackageLoss = responseRecords.Count(response => response != null)/(float) responseRecords.Count(); + } + } +} diff --git a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs index 70433117..e59fcde5 100644 --- a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs +++ b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs @@ -61,9 +61,7 @@ namespace Shadowsocks.Model public StatisticsStrategyConfiguration() { - var availabilityStatisticsType = typeof(AvailabilityStatistics); - var statisticsData = availabilityStatisticsType.GetNestedType("StatisticsData"); - var properties = statisticsData.GetFields(BindingFlags.Instance | BindingFlags.Public); + var properties = typeof(StatisticsRecord).GetFields(BindingFlags.Instance | BindingFlags.Public); Calculations = properties.ToDictionary(p => p.Name, _ => (float)0); } diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs index aae33546..9db17302 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs @@ -10,6 +10,7 @@ using Shadowsocks.Model; namespace Shadowsocks.View { + using Statistics = Dictionary>; public partial class StatisticsStrategyConfigurationForm : Form { private readonly ShadowsocksController _controller; @@ -86,9 +87,9 @@ namespace Shadowsocks.View //return directly when no data is usable if (_controller.availabilityStatistics?.FilteredStatistics == null) return; - List statistics; + List statistics; if (!_controller.availabilityStatistics.FilteredStatistics.TryGetValue(serverName, out statistics)) return; - IEnumerable> dataGroups; + IEnumerable> dataGroups; if (allMode.Checked) { dataGroups = statistics.GroupBy(data => data.Timestamp.DayOfYear); @@ -105,12 +106,9 @@ namespace Shadowsocks.View orderby dataGroup.Key select new { - Timestamp = dataGroup.First().Timestamp, - Ping = (int)dataGroup.Average(data => data.RoundtripTime), - PackageLoss = (int) - (dataGroup.Count(data => data.ICMPStatus.Equals(IPStatus.TimedOut.ToString())) - / (float)dataGroup.Count() * 100) - }; + dataGroup.First().Timestamp, + Ping = (int)dataGroup.Average(data => data.AverageResponse), + PackageLoss = dataGroup.Average(data => data.PackageLoss)}; foreach (var data in finalData) { _dataTable.Rows.Add(data.Timestamp, data.PackageLoss, data.Ping); diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 69f7b546..389c1122 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -195,6 +195,7 @@ + True From 586caab7c4783a78c86f64c6d3edef08c5b07442 Mon Sep 17 00:00:00 2001 From: icylogic Date: Tue, 19 Jan 2016 02:04:49 +0800 Subject: [PATCH 07/18] follow CA1001: Types that own disposable fields should be disposable https://msdn.microsoft.com/library/ms182172.aspx --- .../Controller/Service/AvailabilityStatistics.cs | 9 ++++++++- .../Controller/Strategy/StatisticsStrategy.cs | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index 6a41210a..0a67962c 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -16,7 +16,7 @@ namespace Shadowsocks.Controller { using Statistics = Dictionary>; - public sealed class AvailabilityStatistics + public sealed class AvailabilityStatistics : IDisposable { public const string DateTimePattern = "yyyy-MM-dd HH:mm:ss"; private const string StatisticsFilesName = "shadowsocks.availability.json"; @@ -342,5 +342,12 @@ namespace Shadowsocks.Controller Server = server; } } + + public void Dispose() + { + _recorder.Dispose(); + _writer.Dispose(); + _speedMonior.Dispose(); + } } } \ No newline at end of file diff --git a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs index 3567291e..d9d786d2 100644 --- a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs +++ b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs @@ -12,7 +12,8 @@ using Shadowsocks.Model; namespace Shadowsocks.Controller.Strategy { using Statistics = Dictionary>; - class StatisticsStrategy : IStrategy + + internal class StatisticsStrategy : IStrategy, IDisposable { private readonly ShadowsocksController _controller; private Server _currentServer; @@ -152,5 +153,10 @@ namespace Shadowsocks.Controller.Strategy { //TODO: combine this part of data with ICMP statics } + + public void Dispose() + { + _timer.Dispose(); + } } } From bec1f808b38ff841379b10e011404bd482249595 Mon Sep 17 00:00:00 2001 From: icylogic Date: Wed, 20 Jan 2016 23:51:51 +0800 Subject: [PATCH 08/18] add an option for Ping tests (disabled by default) and improve the readability of the code --- .../Service/AvailabilityStatistics.cs | 181 +++++++----------- .../Controller/Strategy/StatisticsStrategy.cs | 12 +- .../Model/StatisticsStrategyConfiguration.cs | 50 +---- ...sticsStrategyConfigurationForm.Designer.cs | 108 +++++------ .../StatisticsStrategyConfigurationForm.cs | 7 +- 5 files changed, 137 insertions(+), 221 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index 0a67962c..8d805786 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -19,84 +19,87 @@ namespace Shadowsocks.Controller public sealed class AvailabilityStatistics : IDisposable { public const string DateTimePattern = "yyyy-MM-dd HH:mm:ss"; + public static readonly DateTime UnknownDateTime; private const string StatisticsFilesName = "shadowsocks.availability.json"; - private const int TimeoutMilliseconds = 500; + public static string AvailabilityStatisticsFile; + //static constructor to initialize every public static fields before refereced + static AvailabilityStatistics() + { + AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName); + UnknownDateTime = new DateTime(1970, 1, 1); + } - public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1); + //arguments for ICMP tests + private int Repeat => Config.RepeatTimesNum; + private const int TimeoutMilliseconds = 500; - public static string AvailabilityStatisticsFile; - private readonly TimeSpan _delayBeforeStart = TimeSpan.FromSeconds(1); - private readonly TimeSpan _monitorInterval = TimeSpan.FromSeconds(1); - private readonly TimeSpan _retryInterval = TimeSpan.FromMinutes(2); //retry 2 minutes after failed - private readonly TimeSpan _writingInterval = TimeSpan.FromMinutes(1); - private StatisticsStrategyConfiguration _config; - private ShadowsocksController _controller; - private Server _currentServer; + //records cache for current server in {_monitorInterval} minutes + private List _latencyRecords; //speed in KiB/s - private List _inboundSpeedRecords; private long _lastInboundCounter; + private List _inboundSpeedRecords; private long _lastOutboundCounter; - private List _latencyRecords; private List _outboundSpeedRecords; - private Timer _recorder; - private List _servers; - private Timer _speedMonior; - private Timer _writer; - //static constructor to initialize every public static fields before refereced - static AvailabilityStatistics() - { - AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName); - } + //tasks + private readonly TimeSpan _delayBeforeStart = TimeSpan.FromSeconds(1); + private readonly TimeSpan _retryInterval = TimeSpan.FromMinutes(2); + private Timer _recorder; //analyze and save cached records to RawStatistics and filter records + private TimeSpan RecordingInterval => TimeSpan.FromMinutes(Config.DataCollectionMinutes); + private Timer _speedMonior; + private readonly TimeSpan _monitorInterval = TimeSpan.FromSeconds(1); + private Timer _writer; //write RawStatistics to file + private readonly TimeSpan _writingInterval = TimeSpan.FromMinutes(1); - private AvailabilityStatistics() - { - RawStatistics = new Statistics(); - } + private ShadowsocksController _controller; + private StatisticsStrategyConfiguration Config => _controller.StatisticsConfiguration; + private Server CurrentServer => _controller.GetCurrentServer(); // Static Singleton Initialization public static AvailabilityStatistics Instance { get; } = new AvailabilityStatistics(); public Statistics RawStatistics { get; private set; } public Statistics FilteredStatistics { get; private set; } - private int Repeat => _config.RepeatTimesNum; - private TimeSpan RecordingInterval => TimeSpan.FromMinutes(_config.DataCollectionMinutes); - public bool Set(StatisticsStrategyConfiguration config) + private AvailabilityStatistics() + { + RawStatistics = new Statistics(); + } + + internal void UpdateConfiguration(ShadowsocksController controller) { - _config = config; + _controller = controller; + Reset(); try { - if (config.StatisticsEnabled) + if (Config.StatisticsEnabled) { - if (_recorder?.Change(_delayBeforeStart, RecordingInterval) == null) - { - _recorder = new Timer(Run, null, _delayBeforeStart, RecordingInterval); - } + StartTimerWithoutState(ref _recorder, Run, RecordingInterval); LoadRawStatistics(); - if (_speedMonior?.Change(_delayBeforeStart, _monitorInterval) == null) - { - _speedMonior = new Timer(UpdateSpeed, null, _delayBeforeStart, _monitorInterval); - } - if (_writer?.Change(_delayBeforeStart, RecordingInterval) == null) - { - _writer = new Timer(Save, null, _delayBeforeStart, RecordingInterval); - } + StartTimerWithoutState(ref _speedMonior, UpdateSpeed, _monitorInterval); + StartTimerWithoutState(ref _writer, Save, _writingInterval); } else { _recorder?.Dispose(); _speedMonior?.Dispose(); + _writer?.Dispose(); } - return true; } catch (Exception e) { Logging.LogUsefulException(e); - return false; } } - private void UpdateSpeed(object state) + private void StartTimerWithoutState(ref Timer timer, TimerCallback callback, TimeSpan interval) + { + if (timer?.Change(_delayBeforeStart, interval) == null) + { + timer = new Timer(callback, null, _delayBeforeStart, interval); + } + } + + private void UpdateSpeed(object _) { var bytes = _controller.inboundCounter - _lastInboundCounter; _lastInboundCounter = _controller.inboundCounter; @@ -109,7 +112,7 @@ namespace Shadowsocks.Controller _outboundSpeedRecords.Add(outboundSpeed); Logging.Debug( - $"{_currentServer.FriendlyName()}: current/max inbound {inboundSpeed}/{_inboundSpeedRecords.Max()} KiB/s, current/max outbound {outboundSpeed}/{_outboundSpeedRecords.Max()} KiB/s"); + $"{CurrentServer.FriendlyName()}: current/max inbound {inboundSpeed}/{_inboundSpeedRecords.Max()} KiB/s, current/max outbound {outboundSpeed}/{_outboundSpeedRecords.Max()} KiB/s"); } private async Task ICMPTest(Server server) @@ -168,94 +171,66 @@ namespace Shadowsocks.Controller private void Run(object _) { - AppendRecord(); + UpdateRecords(); Reset(); FilterRawStatistics(); } - private async void AppendRecord() + private async void UpdateRecords() { - //todo: option for icmp test - var icmpResults = TaskEx.WhenAll(_servers.Select(ICMPTest)); + var currentServerRecord = new StatisticsRecord(CurrentServer.Identifier(), _inboundSpeedRecords, _outboundSpeedRecords, _latencyRecords); - var currentServerRecord = new StatisticsRecord(_currentServer.Identifier(), - _inboundSpeedRecords, _outboundSpeedRecords, _latencyRecords); + if (!Config.Ping) + { + AppendRecord(CurrentServer, currentServerRecord); + return; + } + + var icmpResults = TaskEx.WhenAll(_controller.GetCurrentConfiguration().configs.Select(ICMPTest)); foreach (var result in (await icmpResults).Where(result => result != null)) { - List records; - if (!RawStatistics.TryGetValue(result.Server.Identifier(), out records)) - { - records = new List(); - } - - if (result.Server.Equals(_currentServer)) + if (result.Server.Equals(CurrentServer)) { currentServerRecord.setResponse(result.RoundtripTime); - records.Add(currentServerRecord); + AppendRecord(CurrentServer, currentServerRecord); } else { - records.Add(new StatisticsRecord(result.Server.Identifier(), result.RoundtripTime)); + AppendRecord(result.Server, new StatisticsRecord(result.Server.Identifier(), result.RoundtripTime)); } - RawStatistics[result.Server.Identifier()] = records; } } - private void Save(object _) + private void AppendRecord(Server server, StatisticsRecord record) { - try + List records; + if (!RawStatistics.TryGetValue(server.Identifier(), out records)) { - File.WriteAllText(AvailabilityStatisticsFile, - JsonConvert.SerializeObject(RawStatistics, Formatting.None)); - } - catch (IOException e) - { - Logging.LogUsefulException(e); - _writer.Change(_retryInterval, _writingInterval); + records = new List(); } + records.Add(record); + RawStatistics[server.Identifier()] = records; } - /* - private static void Append(DataList dataList, IEnumerable extra) + private void Save(object _) { - var data = dataList.Concat(extra); - var dataLine = string.Join(Delimiter, data.Select(kv => kv.Value).ToArray()); - string[] lines; - if (!File.Exists(AvailabilityStatisticsFile)) - { - var headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray()); - lines = new[] { headerLine, dataLine }; - } - else - { - lines = new[] { dataLine }; - } try { - File.AppendAllLines(AvailabilityStatisticsFile, lines); + File.WriteAllText(AvailabilityStatisticsFile, + JsonConvert.SerializeObject(RawStatistics, Formatting.None)); } catch (IOException e) { Logging.LogUsefulException(e); + _writer.Change(_retryInterval, _writingInterval); } } - */ - - internal void UpdateConfiguration(ShadowsocksController controller) - { - _controller = controller; - _currentServer = _controller.GetCurrentServer(); - Reset(); - Set(controller.StatisticsConfiguration); - _servers = _controller.GetCurrentConfiguration().configs; - } private bool IsValidRecord(StatisticsRecord record) { - if (_config.ByHourOfDay) + if (Config.ByHourOfDay) { - var currentHour = DateTime.Now.Hour; if (record.Timestamp == UnknownDateTime) return false; if (!record.Timestamp.Hour.Equals(DateTime.Now.Hour)) return false; } @@ -288,7 +263,7 @@ namespace Shadowsocks.Controller { try { - using (var fs = File.Create(path)) + using (File.Create(path)) { //do nothing } @@ -313,14 +288,6 @@ namespace Shadowsocks.Controller } } - private DateTime ParseExactOrUnknown(string str) - { - DateTime dateTime; - return !DateTime.TryParseExact(str, DateTimePattern, null, DateTimeStyles.None, out dateTime) - ? UnknownDateTime - : dateTime; - } - public void UpdateLatency(int latency) { _latencyRecords.Add(latency); diff --git a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs index d9d786d2..c2c64898 100644 --- a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs +++ b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Net; -using System.Net.NetworkInformation; using System.Threading; using Newtonsoft.Json; @@ -27,7 +26,7 @@ namespace Shadowsocks.Controller.Strategy { _controller = controller; var servers = controller.GetCurrentConfiguration().configs; - var randomIndex = new Random().Next() % servers.Count(); + var randomIndex = new Random().Next() % servers.Count; _currentServer = servers[randomIndex]; //choose a server randomly at first _timer = new Timer(ReloadStatisticsAndChooseAServer); } @@ -117,14 +116,10 @@ namespace Shadowsocks.Controller.Strategy public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint) { - var oldServer = _currentServer; - if (oldServer == null) + if (_currentServer == null) { ChooseNewServer(_controller.GetCurrentConfiguration().configs); } - if (oldServer != _currentServer) - { - } return _currentServer; //current server cached for CachedInterval } @@ -141,17 +136,14 @@ namespace Shadowsocks.Controller.Strategy public void UpdateLastRead(Server server) { - //TODO: combine this part of data with ICMP statics } public void UpdateLastWrite(Server server) { - //TODO: combine this part of data with ICMP statics } public void UpdateLatency(Server server, TimeSpan latency) { - //TODO: combine this part of data with ICMP statics } public void Dispose() diff --git a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs index e59fcde5..895a7f03 100644 --- a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs +++ b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs @@ -13,13 +13,13 @@ namespace Shadowsocks.Model [Serializable] public class StatisticsStrategyConfiguration { - public static readonly string ID = "com.shadowsocks.strategy.statistics"; - private bool _statisticsEnabled = true; - private bool _byIsp = false; - private bool _byHourOfDay = false; - private int _choiceKeptMinutes = 10; - private int _dataCollectionMinutes = 10; - private int _repeatTimesNum = 4; + public static readonly string ID = "com.shadowsocks.strategy.statistics"; + public bool StatisticsEnabled { get; } = true; + public bool ByHourOfDay { get; } = true; + public bool Ping { get; set; } + public int ChoiceKeptMinutes { get; } = 10; + public int DataCollectionMinutes { get; } = 10; + public int RepeatTimesNum { get; } = 4; private const string ConfigFile = "statistics-config.json"; @@ -64,41 +64,5 @@ namespace Shadowsocks.Model var properties = typeof(StatisticsRecord).GetFields(BindingFlags.Instance | BindingFlags.Public); Calculations = properties.ToDictionary(p => p.Name, _ => (float)0); } - - public bool StatisticsEnabled - { - get { return _statisticsEnabled; } - set { _statisticsEnabled = value; } - } - - public bool ByIsp - { - get { return _byIsp; } - set { _byIsp = value; } - } - - public bool ByHourOfDay - { - get { return _byHourOfDay; } - set { _byHourOfDay = value; } - } - - public int ChoiceKeptMinutes - { - get { return _choiceKeptMinutes; } - set { _choiceKeptMinutes = value; } - } - - public int DataCollectionMinutes - { - get { return _dataCollectionMinutes; } - set { _dataCollectionMinutes = value; } - } - - public int RepeatTimesNum - { - get { return _repeatTimesNum; } - set { _repeatTimesNum = value; } - } } } diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs index a207f309..d3f319a9 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs @@ -34,10 +34,9 @@ System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series(); System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series(); this.StatisticsChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); - this.byISPCheckBox = new System.Windows.Forms.CheckBox(); + this.PingCheckBox = new System.Windows.Forms.CheckBox(); this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); - this.label4 = new System.Windows.Forms.Label(); this.chartModeSelector = new System.Windows.Forms.GroupBox(); this.allMode = new System.Windows.Forms.RadioButton(); this.dayMode = new System.Windows.Forms.RadioButton(); @@ -111,57 +110,48 @@ series2.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime; this.StatisticsChart.Series.Add(series1); this.StatisticsChart.Series.Add(series2); - this.StatisticsChart.Size = new System.Drawing.Size(1077, 303); + this.StatisticsChart.Size = new System.Drawing.Size(1089, 303); this.StatisticsChart.TabIndex = 2; // - // byISPCheckBox + // PingCheckBox // - this.byISPCheckBox.AutoSize = true; - this.byISPCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "ByIsp", true)); - this.byISPCheckBox.Location = new System.Drawing.Point(13, 54); - this.byISPCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.byISPCheckBox.Name = "byISPCheckBox"; - this.byISPCheckBox.Size = new System.Drawing.Size(220, 31); - this.byISPCheckBox.TabIndex = 5; - this.byISPCheckBox.Text = "By ISP/geolocation"; - this.byISPCheckBox.UseVisualStyleBackColor = true; + this.PingCheckBox.AutoSize = true; + this.PingCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "Ping", true)); + this.PingCheckBox.Location = new System.Drawing.Point(13, 54); + this.PingCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); + this.PingCheckBox.Name = "PingCheckBox"; + this.PingCheckBox.Size = new System.Drawing.Size(107, 27); + this.PingCheckBox.TabIndex = 5; + this.PingCheckBox.Text = "Ping Test"; + this.PingCheckBox.UseVisualStyleBackColor = true; + this.PingCheckBox.CheckedChanged += new System.EventHandler(this.PingCheckBox_CheckedChanged); // // label2 // this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(8, 136); + this.label2.Location = new System.Drawing.Point(9, 267); this.label2.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(167, 27); + this.label2.Size = new System.Drawing.Size(144, 23); this.label2.TabIndex = 8; this.label2.Text = "Keep choice for "; // // label3 // this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(285, 136); + this.label3.Location = new System.Drawing.Point(286, 267); this.label3.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(87, 27); + this.label3.Size = new System.Drawing.Size(75, 23); this.label3.TabIndex = 9; this.label3.Text = "minutes"; // - // label4 - // - this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(8, 218); - this.label4.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(54, 27); - this.label4.TabIndex = 10; - this.label4.Text = "Ping"; - // // chartModeSelector // this.chartModeSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.chartModeSelector.Controls.Add(this.allMode); this.chartModeSelector.Controls.Add(this.dayMode); - this.chartModeSelector.Location = new System.Drawing.Point(801, 104); + this.chartModeSelector.Location = new System.Drawing.Point(813, 98); this.chartModeSelector.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.chartModeSelector.Name = "chartModeSelector"; this.chartModeSelector.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10); @@ -178,7 +168,7 @@ this.allMode.Location = new System.Drawing.Point(11, 61); this.allMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.allMode.Name = "allMode"; - this.allMode.Size = new System.Drawing.Size(58, 31); + this.allMode.Size = new System.Drawing.Size(50, 27); this.allMode.TabIndex = 1; this.allMode.TabStop = true; this.allMode.Text = "all"; @@ -191,7 +181,7 @@ this.dayMode.Location = new System.Drawing.Point(11, 29); this.dayMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.dayMode.Name = "dayMode"; - this.dayMode.Size = new System.Drawing.Size(73, 31); + this.dayMode.Size = new System.Drawing.Size(61, 27); this.dayMode.TabIndex = 0; this.dayMode.Text = "24h"; this.dayMode.UseVisualStyleBackColor = true; @@ -217,7 +207,7 @@ this.splitContainer1.Panel2.Controls.Add(this.OKButton); this.splitContainer1.Panel2.Controls.Add(this.chartModeSelector); this.splitContainer1.Panel2.Controls.Add(this.StatisticsChart); - this.splitContainer1.Size = new System.Drawing.Size(1077, 614); + this.splitContainer1.Size = new System.Drawing.Size(1089, 614); this.splitContainer1.SplitterDistance = 301; this.splitContainer1.SplitterWidth = 10; this.splitContainer1.TabIndex = 12; @@ -242,14 +232,13 @@ this.splitContainer2.Panel1.Controls.Add(this.repeatTimesNum); this.splitContainer2.Panel1.Controls.Add(this.label6); this.splitContainer2.Panel1.Controls.Add(this.label2); - this.splitContainer2.Panel1.Controls.Add(this.label4); - this.splitContainer2.Panel1.Controls.Add(this.byISPCheckBox); + this.splitContainer2.Panel1.Controls.Add(this.PingCheckBox); this.splitContainer2.Panel1.Controls.Add(this.label3); // // splitContainer2.Panel2 // this.splitContainer2.Panel2.Controls.Add(this.splitContainer3); - this.splitContainer2.Size = new System.Drawing.Size(1077, 301); + this.splitContainer2.Size = new System.Drawing.Size(1089, 301); this.splitContainer2.SplitterDistance = 384; this.splitContainer2.SplitterWidth = 5; this.splitContainer2.TabIndex = 7; @@ -257,10 +246,10 @@ // label9 // this.label9.AutoSize = true; - this.label9.Location = new System.Drawing.Point(8, 175); + this.label9.Location = new System.Drawing.Point(9, 164); this.label9.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.label9.Name = "label9"; - this.label9.Size = new System.Drawing.Size(162, 27); + this.label9.Size = new System.Drawing.Size(139, 23); this.label9.TabIndex = 20; this.label9.Text = "Collect data per"; // @@ -268,10 +257,10 @@ // this.label8.AutoSize = true; this.label8.Font = new System.Drawing.Font("Microsoft YaHei", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label8.Location = new System.Drawing.Point(285, 176); + this.label8.Location = new System.Drawing.Point(286, 165); this.label8.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.label8.Name = "label8"; - this.label8.Size = new System.Drawing.Size(87, 27); + this.label8.Size = new System.Drawing.Size(75, 23); this.label8.TabIndex = 19; this.label8.Text = "minutes"; // @@ -283,7 +272,7 @@ 0, 0, 0}); - this.dataCollectionMinutesNum.Location = new System.Drawing.Point(176, 173); + this.dataCollectionMinutesNum.Location = new System.Drawing.Point(177, 162); this.dataCollectionMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.dataCollectionMinutesNum.Maximum = new decimal(new int[] { 120, @@ -296,7 +285,7 @@ 0, 0}); this.dataCollectionMinutesNum.Name = "dataCollectionMinutesNum"; - this.dataCollectionMinutesNum.Size = new System.Drawing.Size(100, 34); + this.dataCollectionMinutesNum.Size = new System.Drawing.Size(100, 29); this.dataCollectionMinutesNum.TabIndex = 18; this.dataCollectionMinutesNum.Value = new decimal(new int[] { 10, @@ -311,7 +300,7 @@ this.StatisticsEnabledCheckBox.Location = new System.Drawing.Point(13, 12); this.StatisticsEnabledCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.StatisticsEnabledCheckBox.Name = "StatisticsEnabledCheckBox"; - this.StatisticsEnabledCheckBox.Size = new System.Drawing.Size(189, 31); + this.StatisticsEnabledCheckBox.Size = new System.Drawing.Size(163, 27); this.StatisticsEnabledCheckBox.TabIndex = 17; this.StatisticsEnabledCheckBox.Text = "Enable Statistics"; this.StatisticsEnabledCheckBox.UseVisualStyleBackColor = true; @@ -324,7 +313,7 @@ 0, 0, 0}); - this.choiceKeptMinutesNum.Location = new System.Drawing.Point(176, 134); + this.choiceKeptMinutesNum.Location = new System.Drawing.Point(177, 265); this.choiceKeptMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.choiceKeptMinutesNum.Maximum = new decimal(new int[] { 120, @@ -337,7 +326,7 @@ 0, 0}); this.choiceKeptMinutesNum.Name = "choiceKeptMinutesNum"; - this.choiceKeptMinutesNum.Size = new System.Drawing.Size(100, 34); + this.choiceKeptMinutesNum.Size = new System.Drawing.Size(100, 29); this.choiceKeptMinutesNum.TabIndex = 16; this.choiceKeptMinutesNum.Value = new decimal(new int[] { 10, @@ -349,10 +338,10 @@ // this.byHourOfDayCheckBox.AutoSize = true; this.byHourOfDayCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "ByHourOfDay", true)); - this.byHourOfDayCheckBox.Location = new System.Drawing.Point(13, 95); + this.byHourOfDayCheckBox.Location = new System.Drawing.Point(13, 127); this.byHourOfDayCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.byHourOfDayCheckBox.Name = "byHourOfDayCheckBox"; - this.byHourOfDayCheckBox.Size = new System.Drawing.Size(180, 31); + this.byHourOfDayCheckBox.Size = new System.Drawing.Size(150, 27); this.byHourOfDayCheckBox.TabIndex = 15; this.byHourOfDayCheckBox.Text = "By hour of day"; this.byHourOfDayCheckBox.UseVisualStyleBackColor = true; @@ -360,7 +349,7 @@ // repeatTimesNum // this.repeatTimesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "RepeatTimesNum", true)); - this.repeatTimesNum.Location = new System.Drawing.Point(72, 216); + this.repeatTimesNum.Location = new System.Drawing.Point(34, 84); this.repeatTimesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.repeatTimesNum.Maximum = new decimal(new int[] { 10, @@ -368,7 +357,7 @@ 0, 0}); this.repeatTimesNum.Name = "repeatTimesNum"; - this.repeatTimesNum.Size = new System.Drawing.Size(99, 34); + this.repeatTimesNum.Size = new System.Drawing.Size(99, 29); this.repeatTimesNum.TabIndex = 14; this.repeatTimesNum.Value = new decimal(new int[] { 4, @@ -380,9 +369,9 @@ // this.label6.AutoSize = true; this.label6.Font = new System.Drawing.Font("Microsoft YaHei", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label6.Location = new System.Drawing.Point(178, 218); + this.label6.Location = new System.Drawing.Point(139, 86); this.label6.Name = "label6"; - this.label6.Size = new System.Drawing.Size(201, 27); + this.label6.Size = new System.Drawing.Size(172, 23); this.label6.TabIndex = 13; this.label6.Text = "packages everytime"; // @@ -403,7 +392,7 @@ // splitContainer3.Panel2 // this.splitContainer3.Panel2.Controls.Add(this.calculationContainer); - this.splitContainer3.Size = new System.Drawing.Size(688, 301); + this.splitContainer3.Size = new System.Drawing.Size(700, 301); this.splitContainer3.SplitterDistance = 46; this.splitContainer3.SplitterWidth = 10; this.splitContainer3.TabIndex = 6; @@ -414,7 +403,7 @@ this.label1.Location = new System.Drawing.Point(5, 12); this.label1.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(262, 27); + this.label1.Size = new System.Drawing.Size(225, 23); this.label1.TabIndex = 0; this.label1.Text = "Design evaluation method"; // @@ -425,23 +414,23 @@ this.calculationContainer.Location = new System.Drawing.Point(0, 0); this.calculationContainer.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.calculationContainer.Name = "calculationContainer"; - this.calculationContainer.Size = new System.Drawing.Size(688, 245); + this.calculationContainer.Size = new System.Drawing.Size(700, 245); this.calculationContainer.TabIndex = 1; // // serverSelector // this.serverSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.serverSelector.FormattingEnabled = true; - this.serverSelector.Location = new System.Drawing.Point(801, 67); + this.serverSelector.Location = new System.Drawing.Point(813, 61); this.serverSelector.Name = "serverSelector"; - this.serverSelector.Size = new System.Drawing.Size(260, 35); + this.serverSelector.Size = new System.Drawing.Size(260, 31); this.serverSelector.TabIndex = 6; this.serverSelector.SelectedIndexChanged += new System.EventHandler(this.serverSelector_SelectedIndexChanged); // // CancelButton // this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.CancelButton.Location = new System.Drawing.Point(960, 220); + this.CancelButton.Location = new System.Drawing.Point(972, 214); this.CancelButton.Name = "CancelButton"; this.CancelButton.Size = new System.Drawing.Size(101, 41); this.CancelButton.TabIndex = 5; @@ -452,7 +441,7 @@ // OKButton // this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.OKButton.Location = new System.Drawing.Point(852, 220); + this.OKButton.Location = new System.Drawing.Point(864, 214); this.OKButton.Name = "OKButton"; this.OKButton.Size = new System.Drawing.Size(101, 41); this.OKButton.TabIndex = 4; @@ -466,10 +455,10 @@ // // StatisticsStrategyConfigurationForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 27F); + this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 23F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoSize = true; - this.ClientSize = new System.Drawing.Size(1077, 614); + this.ClientSize = new System.Drawing.Size(1089, 614); this.Controls.Add(this.splitContainer1); this.Font = new System.Drawing.Font("Microsoft YaHei", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); @@ -503,10 +492,9 @@ #endregion private System.Windows.Forms.DataVisualization.Charting.Chart StatisticsChart; - private System.Windows.Forms.CheckBox byISPCheckBox; + private System.Windows.Forms.CheckBox PingCheckBox; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; - private System.Windows.Forms.Label label4; private System.Windows.Forms.GroupBox chartModeSelector; private System.Windows.Forms.RadioButton allMode; private System.Windows.Forms.RadioButton dayMode; diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs index 9db17302..cda9448c 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs @@ -15,7 +15,7 @@ namespace Shadowsocks.View { private readonly ShadowsocksController _controller; private StatisticsStrategyConfiguration _configuration; - private DataTable _dataTable = new DataTable(); + private readonly DataTable _dataTable = new DataTable(); private List _servers; public StatisticsStrategyConfigurationForm(ShadowsocksController controller) @@ -135,5 +135,10 @@ namespace Shadowsocks.View { loadChartData(); } + + private void PingCheckBox_CheckedChanged(object sender, EventArgs e) + { + repeatTimesNum.ReadOnly = !PingCheckBox.Checked; + } } } From 06d60f54bcdd8e0de640b99d3d4ba9dadfaa3618 Mon Sep 17 00:00:00 2001 From: icylogic Date: Fri, 5 Feb 2016 17:25:48 +0800 Subject: [PATCH 09/18] redesign statistics form --- .../Service/AvailabilityStatistics.cs | 1 - ...sticsStrategyConfigurationForm.Designer.cs | 29 +++++++++------ .../StatisticsStrategyConfigurationForm.cs | 37 ++++++++++++------- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index 8d805786..4e6a71bf 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Net; diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs index d3f319a9..0bc91e92 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs @@ -33,8 +33,10 @@ System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend(); System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series(); System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series(); + System.Windows.Forms.DataVisualization.Charting.Series series3 = new System.Windows.Forms.DataVisualization.Charting.Series(); this.StatisticsChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); this.PingCheckBox = new System.Windows.Forms.CheckBox(); + this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components); this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.chartModeSelector = new System.Windows.Forms.GroupBox(); @@ -56,8 +58,8 @@ this.serverSelector = new System.Windows.Forms.ComboBox(); this.CancelButton = new System.Windows.Forms.Button(); this.OKButton = new System.Windows.Forms.Button(); - this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components); ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit(); this.chartModeSelector.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); this.splitContainer1.Panel1.SuspendLayout(); @@ -74,7 +76,6 @@ this.splitContainer3.Panel1.SuspendLayout(); this.splitContainer3.Panel2.SuspendLayout(); this.splitContainer3.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit(); this.SuspendLayout(); // // StatisticsChart @@ -108,10 +109,16 @@ series2.Legend = "ChartLegend"; series2.Name = "Ping"; series2.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime; + series3.ChartArea = "DataArea"; + series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Area; + series3.Legend = "ChartLegend"; + series3.Name = "Speed"; this.StatisticsChart.Series.Add(series1); this.StatisticsChart.Series.Add(series2); + this.StatisticsChart.Series.Add(series3); this.StatisticsChart.Size = new System.Drawing.Size(1089, 303); this.StatisticsChart.TabIndex = 2; + this.StatisticsChart.Click += new System.EventHandler(this.StatisticsChart_Click); // // PingCheckBox // @@ -126,6 +133,10 @@ this.PingCheckBox.UseVisualStyleBackColor = true; this.PingCheckBox.CheckedChanged += new System.EventHandler(this.PingCheckBox_CheckedChanged); // + // bindingConfiguration + // + this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration); + // // label2 // this.label2.AutoSize = true; @@ -151,7 +162,7 @@ this.chartModeSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.chartModeSelector.Controls.Add(this.allMode); this.chartModeSelector.Controls.Add(this.dayMode); - this.chartModeSelector.Location = new System.Drawing.Point(813, 98); + this.chartModeSelector.Location = new System.Drawing.Point(813, 92); this.chartModeSelector.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.chartModeSelector.Name = "chartModeSelector"; this.chartModeSelector.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10); @@ -421,7 +432,7 @@ // this.serverSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.serverSelector.FormattingEnabled = true; - this.serverSelector.Location = new System.Drawing.Point(813, 61); + this.serverSelector.Location = new System.Drawing.Point(813, 55); this.serverSelector.Name = "serverSelector"; this.serverSelector.Size = new System.Drawing.Size(260, 31); this.serverSelector.TabIndex = 6; @@ -430,7 +441,7 @@ // CancelButton // this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.CancelButton.Location = new System.Drawing.Point(972, 214); + this.CancelButton.Location = new System.Drawing.Point(972, 208); this.CancelButton.Name = "CancelButton"; this.CancelButton.Size = new System.Drawing.Size(101, 41); this.CancelButton.TabIndex = 5; @@ -441,7 +452,7 @@ // OKButton // this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.OKButton.Location = new System.Drawing.Point(864, 214); + this.OKButton.Location = new System.Drawing.Point(864, 208); this.OKButton.Name = "OKButton"; this.OKButton.Size = new System.Drawing.Size(101, 41); this.OKButton.TabIndex = 4; @@ -449,10 +460,6 @@ this.OKButton.UseVisualStyleBackColor = true; this.OKButton.Click += new System.EventHandler(this.OKButton_Click); // - // bindingConfiguration - // - this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration); - // // StatisticsStrategyConfigurationForm // this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 23F); @@ -466,6 +473,7 @@ this.Name = "StatisticsStrategyConfigurationForm"; this.Text = "StatisticsStrategyConfigurationForm"; ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit(); this.chartModeSelector.ResumeLayout(false); this.chartModeSelector.PerformLayout(); this.splitContainer1.Panel1.ResumeLayout(false); @@ -485,7 +493,6 @@ this.splitContainer3.Panel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit(); this.splitContainer3.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit(); this.ResumeLayout(false); } diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs index cda9448c..2fb71b00 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Data; using System.Linq; -using System.Net.NetworkInformation; using System.Windows.Forms; using Shadowsocks.Controller; @@ -52,15 +51,22 @@ namespace Shadowsocks.View serverSelector.DataSource = _servers; _dataTable.Columns.Add("Timestamp", typeof(DateTime)); - _dataTable.Columns.Add("Package Loss", typeof(int)); - _dataTable.Columns.Add("Ping", typeof(int)); + _dataTable.Columns.Add("Speed", typeof (int)); + StatisticsChart.Series["Speed"].XValueMember = "Timestamp"; + StatisticsChart.Series["Speed"].YValueMembers = "Speed"; + + if (_configuration.Ping) + { + _dataTable.Columns.Add("Package Loss", typeof (int)); + _dataTable.Columns.Add("Ping", typeof (int)); + StatisticsChart.Series["Package Loss"].XValueMember = "Timestamp"; + StatisticsChart.Series["Package Loss"].YValueMembers = "Package Loss"; + StatisticsChart.Series["Ping"].XValueMember = "Timestamp"; + StatisticsChart.Series["Ping"].YValueMembers = "Ping"; + } - StatisticsChart.Series["Package Loss"].XValueMember = "Timestamp"; - StatisticsChart.Series["Package Loss"].YValueMembers = "Package Loss"; - StatisticsChart.Series["Ping"].XValueMember = "Timestamp"; - StatisticsChart.Series["Ping"].YValueMembers = "Ping"; StatisticsChart.DataSource = _dataTable; - loadChartData(); + LoadChartData(); StatisticsChart.DataBind(); } @@ -80,9 +86,9 @@ namespace Shadowsocks.View Close(); } - private void loadChartData() + private void LoadChartData() { - string serverName = _servers[serverSelector.SelectedIndex]; + var serverName = _servers[serverSelector.SelectedIndex]; _dataTable.Rows.Clear(); //return directly when no data is usable @@ -118,7 +124,7 @@ namespace Shadowsocks.View private void serverSelector_SelectedIndexChanged(object sender, EventArgs e) { - loadChartData(); + LoadChartData(); } private void chartModeSelector_Enter(object sender, EventArgs e) @@ -128,17 +134,22 @@ namespace Shadowsocks.View private void dayMode_CheckedChanged(object sender, EventArgs e) { - loadChartData(); + LoadChartData(); } private void allMode_CheckedChanged(object sender, EventArgs e) { - loadChartData(); + LoadChartData(); } private void PingCheckBox_CheckedChanged(object sender, EventArgs e) { repeatTimesNum.ReadOnly = !PingCheckBox.Checked; } + + private void StatisticsChart_Click(object sender, EventArgs e) + { + + } } } From 3b7cbf8020f95bac267685fd5354b5ed029410fc Mon Sep 17 00:00:00 2001 From: icylogic Date: Mon, 29 Feb 2016 21:36:55 +0800 Subject: [PATCH 10/18] fix weird logic for reading files. --- .../Service/AvailabilityStatistics.cs | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index c0467393..17f66f5c 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -260,23 +260,9 @@ namespace Shadowsocks.Controller Logging.Debug($"loading statistics from {path}"); if (!File.Exists(path)) { - try - { - using (File.Create(path)) - { - //do nothing - } - } - catch (Exception e) - { - Logging.LogUsefulException(e); - } - if (!File.Exists(path)) + using (File.Create(path)) { - Console.WriteLine( - $"statistics file does not exist, try to reload {_retryInterval.TotalMinutes} minutes later"); - _recorder.Change(_retryInterval, RecordingInterval); - return; + //do nothing } } RawStatistics = JsonConvert.DeserializeObject(File.ReadAllText(path)) ?? RawStatistics; @@ -284,6 +270,8 @@ namespace Shadowsocks.Controller catch (Exception e) { Logging.LogUsefulException(e); + Console.WriteLine($"failed to load statistics; try to reload {_retryInterval.TotalMinutes} minutes later"); + _recorder.Change(_retryInterval, RecordingInterval); } } From e0e0f646b0ff9a4daa3ec7bedbd061f68ea15b8e Mon Sep 17 00:00:00 2001 From: icylogic Date: Tue, 1 Mar 2016 00:38:43 +0800 Subject: [PATCH 11/18] fix the chart --- .../Service/AvailabilityStatistics.cs | 7 +- shadowsocks-csharp/Model/StatisticsRecord.cs | 4 + ...sticsStrategyConfigurationForm.Designer.cs | 108 +++++++++--------- .../StatisticsStrategyConfigurationForm.cs | 51 ++++----- 4 files changed, 87 insertions(+), 83 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index 17f66f5c..8b359105 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -30,7 +30,7 @@ namespace Shadowsocks.Controller //arguments for ICMP tests private int Repeat => Config.RepeatTimesNum; - private const int TimeoutMilliseconds = 500; + public const int TimeoutMilliseconds = 500; //records cache for current server in {_monitorInterval} minutes private List _latencyRecords; @@ -134,7 +134,7 @@ namespace Shadowsocks.Controller try { var reply = await ping.SendTaskAsync(IP, TimeoutMilliseconds); - if (!reply.Status.Equals(IPStatus.Success)) + if (reply.Status.Equals(IPStatus.Success)) { result.RoundtripTime.Add((int?) reply.RoundtripTime); } @@ -265,7 +265,8 @@ namespace Shadowsocks.Controller //do nothing } } - RawStatistics = JsonConvert.DeserializeObject(File.ReadAllText(path)) ?? RawStatistics; + var content = File.ReadAllText(path); + RawStatistics = JsonConvert.DeserializeObject(content) ?? RawStatistics; } catch (Exception e) { diff --git a/shadowsocks-csharp/Model/StatisticsRecord.cs b/shadowsocks-csharp/Model/StatisticsRecord.cs index 181567ce..54242455 100644 --- a/shadowsocks-csharp/Model/StatisticsRecord.cs +++ b/shadowsocks-csharp/Model/StatisticsRecord.cs @@ -31,6 +31,10 @@ namespace Shadowsocks.Model public int? MaxResponse; public float? PackageLoss; + public StatisticsRecord() + { + } + public StatisticsRecord(string identifier, IEnumerable inboundSpeedRecords, IEnumerable outboundSpeedRecords, IEnumerable latencyRecords) { Timestamp = DateTime.Now; diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs index 0bc91e92..5bda6b71 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs @@ -36,7 +36,6 @@ System.Windows.Forms.DataVisualization.Charting.Series series3 = new System.Windows.Forms.DataVisualization.Charting.Series(); this.StatisticsChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); this.PingCheckBox = new System.Windows.Forms.CheckBox(); - this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components); this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.chartModeSelector = new System.Windows.Forms.GroupBox(); @@ -58,8 +57,8 @@ this.serverSelector = new System.Windows.Forms.ComboBox(); this.CancelButton = new System.Windows.Forms.Button(); this.OKButton = new System.Windows.Forms.Button(); + this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components); ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit(); this.chartModeSelector.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); this.splitContainer1.Panel1.SuspendLayout(); @@ -76,6 +75,7 @@ this.splitContainer3.Panel1.SuspendLayout(); this.splitContainer3.Panel2.SuspendLayout(); this.splitContainer3.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit(); this.SuspendLayout(); // // StatisticsChart @@ -83,6 +83,7 @@ this.StatisticsChart.BackColor = System.Drawing.Color.Transparent; chartArea1.AxisX.MajorGrid.Enabled = false; chartArea1.AxisY.MajorGrid.Enabled = false; + chartArea1.AxisY2.Enabled = System.Windows.Forms.DataVisualization.Charting.AxisEnabled.False; chartArea1.AxisY2.MajorGrid.Enabled = false; chartArea1.BackColor = System.Drawing.Color.Transparent; chartArea1.Name = "DataArea"; @@ -96,29 +97,33 @@ this.StatisticsChart.Name = "StatisticsChart"; this.StatisticsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.Pastel; series1.ChartArea = "DataArea"; - series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bubble; - series1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(221)))), ((int)(((byte)(88)))), ((int)(((byte)(0))))); + series1.Color = System.Drawing.Color.DarkGray; series1.Legend = "ChartLegend"; - series1.Name = "Package Loss"; - series1.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime; - series1.YValuesPerPoint = 2; - series2.BorderWidth = 4; + series1.Name = "Speed"; + series1.ToolTip = "Max inbound speed\\n#VAL KiB/s"; series2.ChartArea = "DataArea"; - series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; - series2.Color = System.Drawing.Color.FromArgb(((int)(((byte)(155)))), ((int)(((byte)(77)))), ((int)(((byte)(150))))); + series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bubble; + series2.Color = System.Drawing.Color.Crimson; + series2.CustomProperties = "EmptyPointValue=Zero"; series2.Legend = "ChartLegend"; - series2.Name = "Ping"; - series2.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime; + series2.Name = "Package Loss"; + series2.ToolTip = "#VAL%"; + series2.YAxisType = System.Windows.Forms.DataVisualization.Charting.AxisType.Secondary; + series2.YValuesPerPoint = 2; + series3.BorderWidth = 5; series3.ChartArea = "DataArea"; - series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Area; + series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; + series3.Color = System.Drawing.Color.DodgerBlue; series3.Legend = "ChartLegend"; - series3.Name = "Speed"; + series3.MarkerSize = 10; + series3.MarkerStyle = System.Windows.Forms.DataVisualization.Charting.MarkerStyle.Circle; + series3.Name = "Ping"; + series3.ToolTip = "#VAL ms"; this.StatisticsChart.Series.Add(series1); this.StatisticsChart.Series.Add(series2); this.StatisticsChart.Series.Add(series3); - this.StatisticsChart.Size = new System.Drawing.Size(1089, 303); + this.StatisticsChart.Size = new System.Drawing.Size(1089, 352); this.StatisticsChart.TabIndex = 2; - this.StatisticsChart.Click += new System.EventHandler(this.StatisticsChart_Click); // // PingCheckBox // @@ -127,33 +132,29 @@ this.PingCheckBox.Location = new System.Drawing.Point(13, 54); this.PingCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.PingCheckBox.Name = "PingCheckBox"; - this.PingCheckBox.Size = new System.Drawing.Size(107, 27); + this.PingCheckBox.Size = new System.Drawing.Size(124, 31); this.PingCheckBox.TabIndex = 5; this.PingCheckBox.Text = "Ping Test"; this.PingCheckBox.UseVisualStyleBackColor = true; this.PingCheckBox.CheckedChanged += new System.EventHandler(this.PingCheckBox_CheckedChanged); // - // bindingConfiguration - // - this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration); - // // label2 // this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(9, 267); + this.label2.Location = new System.Drawing.Point(9, 206); this.label2.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(144, 23); + this.label2.Size = new System.Drawing.Size(167, 27); this.label2.TabIndex = 8; this.label2.Text = "Keep choice for "; // // label3 // this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(286, 267); + this.label3.Location = new System.Drawing.Point(286, 206); this.label3.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(75, 23); + this.label3.Size = new System.Drawing.Size(87, 27); this.label3.TabIndex = 9; this.label3.Text = "minutes"; // @@ -162,7 +163,7 @@ this.chartModeSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.chartModeSelector.Controls.Add(this.allMode); this.chartModeSelector.Controls.Add(this.dayMode); - this.chartModeSelector.Location = new System.Drawing.Point(813, 92); + this.chartModeSelector.Location = new System.Drawing.Point(813, 123); this.chartModeSelector.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.chartModeSelector.Name = "chartModeSelector"; this.chartModeSelector.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10); @@ -170,7 +171,6 @@ this.chartModeSelector.TabIndex = 3; this.chartModeSelector.TabStop = false; this.chartModeSelector.Text = "Chart Mode"; - this.chartModeSelector.Enter += new System.EventHandler(this.chartModeSelector_Enter); // // allMode // @@ -179,7 +179,7 @@ this.allMode.Location = new System.Drawing.Point(11, 61); this.allMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.allMode.Name = "allMode"; - this.allMode.Size = new System.Drawing.Size(50, 27); + this.allMode.Size = new System.Drawing.Size(58, 31); this.allMode.TabIndex = 1; this.allMode.TabStop = true; this.allMode.Text = "all"; @@ -192,7 +192,7 @@ this.dayMode.Location = new System.Drawing.Point(11, 29); this.dayMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.dayMode.Name = "dayMode"; - this.dayMode.Size = new System.Drawing.Size(61, 27); + this.dayMode.Size = new System.Drawing.Size(73, 31); this.dayMode.TabIndex = 0; this.dayMode.Text = "24h"; this.dayMode.UseVisualStyleBackColor = true; @@ -219,7 +219,7 @@ this.splitContainer1.Panel2.Controls.Add(this.chartModeSelector); this.splitContainer1.Panel2.Controls.Add(this.StatisticsChart); this.splitContainer1.Size = new System.Drawing.Size(1089, 614); - this.splitContainer1.SplitterDistance = 301; + this.splitContainer1.SplitterDistance = 252; this.splitContainer1.SplitterWidth = 10; this.splitContainer1.TabIndex = 12; // @@ -249,7 +249,7 @@ // splitContainer2.Panel2 // this.splitContainer2.Panel2.Controls.Add(this.splitContainer3); - this.splitContainer2.Size = new System.Drawing.Size(1089, 301); + this.splitContainer2.Size = new System.Drawing.Size(1089, 252); this.splitContainer2.SplitterDistance = 384; this.splitContainer2.SplitterWidth = 5; this.splitContainer2.TabIndex = 7; @@ -260,7 +260,7 @@ this.label9.Location = new System.Drawing.Point(9, 164); this.label9.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.label9.Name = "label9"; - this.label9.Size = new System.Drawing.Size(139, 23); + this.label9.Size = new System.Drawing.Size(162, 27); this.label9.TabIndex = 20; this.label9.Text = "Collect data per"; // @@ -271,7 +271,7 @@ this.label8.Location = new System.Drawing.Point(286, 165); this.label8.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.label8.Name = "label8"; - this.label8.Size = new System.Drawing.Size(75, 23); + this.label8.Size = new System.Drawing.Size(87, 27); this.label8.TabIndex = 19; this.label8.Text = "minutes"; // @@ -291,12 +291,12 @@ 0, 0}); this.dataCollectionMinutesNum.Minimum = new decimal(new int[] { - 5, + 1, 0, 0, 0}); this.dataCollectionMinutesNum.Name = "dataCollectionMinutesNum"; - this.dataCollectionMinutesNum.Size = new System.Drawing.Size(100, 29); + this.dataCollectionMinutesNum.Size = new System.Drawing.Size(100, 34); this.dataCollectionMinutesNum.TabIndex = 18; this.dataCollectionMinutesNum.Value = new decimal(new int[] { 10, @@ -311,7 +311,7 @@ this.StatisticsEnabledCheckBox.Location = new System.Drawing.Point(13, 12); this.StatisticsEnabledCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.StatisticsEnabledCheckBox.Name = "StatisticsEnabledCheckBox"; - this.StatisticsEnabledCheckBox.Size = new System.Drawing.Size(163, 27); + this.StatisticsEnabledCheckBox.Size = new System.Drawing.Size(189, 31); this.StatisticsEnabledCheckBox.TabIndex = 17; this.StatisticsEnabledCheckBox.Text = "Enable Statistics"; this.StatisticsEnabledCheckBox.UseVisualStyleBackColor = true; @@ -324,7 +324,7 @@ 0, 0, 0}); - this.choiceKeptMinutesNum.Location = new System.Drawing.Point(177, 265); + this.choiceKeptMinutesNum.Location = new System.Drawing.Point(177, 204); this.choiceKeptMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.choiceKeptMinutesNum.Maximum = new decimal(new int[] { 120, @@ -332,12 +332,12 @@ 0, 0}); this.choiceKeptMinutesNum.Minimum = new decimal(new int[] { - 5, + 1, 0, 0, 0}); this.choiceKeptMinutesNum.Name = "choiceKeptMinutesNum"; - this.choiceKeptMinutesNum.Size = new System.Drawing.Size(100, 29); + this.choiceKeptMinutesNum.Size = new System.Drawing.Size(100, 34); this.choiceKeptMinutesNum.TabIndex = 16; this.choiceKeptMinutesNum.Value = new decimal(new int[] { 10, @@ -352,7 +352,7 @@ this.byHourOfDayCheckBox.Location = new System.Drawing.Point(13, 127); this.byHourOfDayCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.byHourOfDayCheckBox.Name = "byHourOfDayCheckBox"; - this.byHourOfDayCheckBox.Size = new System.Drawing.Size(150, 27); + this.byHourOfDayCheckBox.Size = new System.Drawing.Size(180, 31); this.byHourOfDayCheckBox.TabIndex = 15; this.byHourOfDayCheckBox.Text = "By hour of day"; this.byHourOfDayCheckBox.UseVisualStyleBackColor = true; @@ -368,7 +368,7 @@ 0, 0}); this.repeatTimesNum.Name = "repeatTimesNum"; - this.repeatTimesNum.Size = new System.Drawing.Size(99, 29); + this.repeatTimesNum.Size = new System.Drawing.Size(99, 34); this.repeatTimesNum.TabIndex = 14; this.repeatTimesNum.Value = new decimal(new int[] { 4, @@ -382,7 +382,7 @@ this.label6.Font = new System.Drawing.Font("Microsoft YaHei", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label6.Location = new System.Drawing.Point(139, 86); this.label6.Name = "label6"; - this.label6.Size = new System.Drawing.Size(172, 23); + this.label6.Size = new System.Drawing.Size(201, 27); this.label6.TabIndex = 13; this.label6.Text = "packages everytime"; // @@ -403,7 +403,7 @@ // splitContainer3.Panel2 // this.splitContainer3.Panel2.Controls.Add(this.calculationContainer); - this.splitContainer3.Size = new System.Drawing.Size(700, 301); + this.splitContainer3.Size = new System.Drawing.Size(700, 252); this.splitContainer3.SplitterDistance = 46; this.splitContainer3.SplitterWidth = 10; this.splitContainer3.TabIndex = 6; @@ -414,7 +414,7 @@ this.label1.Location = new System.Drawing.Point(5, 12); this.label1.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(225, 23); + this.label1.Size = new System.Drawing.Size(262, 27); this.label1.TabIndex = 0; this.label1.Text = "Design evaluation method"; // @@ -425,23 +425,23 @@ this.calculationContainer.Location = new System.Drawing.Point(0, 0); this.calculationContainer.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.calculationContainer.Name = "calculationContainer"; - this.calculationContainer.Size = new System.Drawing.Size(700, 245); + this.calculationContainer.Size = new System.Drawing.Size(700, 196); this.calculationContainer.TabIndex = 1; // // serverSelector // this.serverSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.serverSelector.FormattingEnabled = true; - this.serverSelector.Location = new System.Drawing.Point(813, 55); + this.serverSelector.Location = new System.Drawing.Point(813, 86); this.serverSelector.Name = "serverSelector"; - this.serverSelector.Size = new System.Drawing.Size(260, 31); + this.serverSelector.Size = new System.Drawing.Size(260, 35); this.serverSelector.TabIndex = 6; - this.serverSelector.SelectedIndexChanged += new System.EventHandler(this.serverSelector_SelectedIndexChanged); + this.serverSelector.SelectionChangeCommitted += new System.EventHandler(this.serverSelector_SelectionChangeCommitted); // // CancelButton // this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.CancelButton.Location = new System.Drawing.Point(972, 208); + this.CancelButton.Location = new System.Drawing.Point(972, 239); this.CancelButton.Name = "CancelButton"; this.CancelButton.Size = new System.Drawing.Size(101, 41); this.CancelButton.TabIndex = 5; @@ -452,7 +452,7 @@ // OKButton // this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.OKButton.Location = new System.Drawing.Point(864, 208); + this.OKButton.Location = new System.Drawing.Point(864, 239); this.OKButton.Name = "OKButton"; this.OKButton.Size = new System.Drawing.Size(101, 41); this.OKButton.TabIndex = 4; @@ -460,9 +460,13 @@ this.OKButton.UseVisualStyleBackColor = true; this.OKButton.Click += new System.EventHandler(this.OKButton_Click); // + // bindingConfiguration + // + this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration); + // // StatisticsStrategyConfigurationForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 23F); + this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 27F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoSize = true; this.ClientSize = new System.Drawing.Size(1089, 614); @@ -473,7 +477,6 @@ this.Name = "StatisticsStrategyConfigurationForm"; this.Text = "StatisticsStrategyConfigurationForm"; ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit(); this.chartModeSelector.ResumeLayout(false); this.chartModeSelector.PerformLayout(); this.splitContainer1.Panel1.ResumeLayout(false); @@ -493,6 +496,7 @@ this.splitContainer3.Panel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit(); this.splitContainer3.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit(); this.ResumeLayout(false); } diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs index 2fb71b00..a0f67f78 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; using System.Data; +using System.Drawing; using System.Linq; using System.Windows.Forms; - +using System.Windows.Forms.DataVisualization.Charting; using Shadowsocks.Controller; using Shadowsocks.Model; @@ -50,20 +51,22 @@ namespace Shadowsocks.View serverSelector.DataSource = _servers; + var speedSeries = StatisticsChart.Series["Speed"]; + var packageLossSeries = StatisticsChart.Series["Package Loss"]; + var pingSeries = StatisticsChart.Series["Ping"]; + _dataTable.Columns.Add("Timestamp", typeof(DateTime)); _dataTable.Columns.Add("Speed", typeof (int)); - StatisticsChart.Series["Speed"].XValueMember = "Timestamp"; - StatisticsChart.Series["Speed"].YValueMembers = "Speed"; + speedSeries.XValueMember = "Timestamp"; + speedSeries.YValueMembers = "Speed"; - if (_configuration.Ping) - { - _dataTable.Columns.Add("Package Loss", typeof (int)); - _dataTable.Columns.Add("Ping", typeof (int)); - StatisticsChart.Series["Package Loss"].XValueMember = "Timestamp"; - StatisticsChart.Series["Package Loss"].YValueMembers = "Package Loss"; - StatisticsChart.Series["Ping"].XValueMember = "Timestamp"; - StatisticsChart.Series["Ping"].YValueMembers = "Ping"; - } + // might be empty + _dataTable.Columns.Add("Package Loss", typeof (int)); + _dataTable.Columns.Add("Ping", typeof (int)); + packageLossSeries.XValueMember = "Timestamp"; + packageLossSeries.YValueMembers = "Package Loss"; + pingSeries.XValueMember = "Timestamp"; + pingSeries.YValueMembers = "Ping"; StatisticsChart.DataSource = _dataTable; LoadChartData(); @@ -99,8 +102,8 @@ namespace Shadowsocks.View if (allMode.Checked) { dataGroups = statistics.GroupBy(data => data.Timestamp.DayOfYear); - StatisticsChart.ChartAreas["DataArea"].AxisX.LabelStyle.Format = "MM/dd/yyyy"; - StatisticsChart.ChartAreas["DataArea"].AxisX2.LabelStyle.Format = "MM/dd/yyyy"; + StatisticsChart.ChartAreas["DataArea"].AxisX.LabelStyle.Format = "g"; + StatisticsChart.ChartAreas["DataArea"].AxisX2.LabelStyle.Format = "g"; } else { @@ -113,25 +116,22 @@ namespace Shadowsocks.View select new { dataGroup.First().Timestamp, - Ping = (int)dataGroup.Average(data => data.AverageResponse), - PackageLoss = dataGroup.Average(data => data.PackageLoss)}; + Speed = dataGroup.Max(data => data.MaxInboundSpeed) ?? 0, + Ping = (int) (dataGroup.Average(data => data.AverageResponse) ?? 0), + PackageLossPercentage = (dataGroup.Average(data => data.PackageLoss) ?? 0) * 100 + }; foreach (var data in finalData) { - _dataTable.Rows.Add(data.Timestamp, data.PackageLoss, data.Ping); + _dataTable.Rows.Add(data.Timestamp, data.Speed, data.PackageLossPercentage, data.Ping); } StatisticsChart.DataBind(); } - private void serverSelector_SelectedIndexChanged(object sender, EventArgs e) + private void serverSelector_SelectionChangeCommitted(object sender, EventArgs e) { LoadChartData(); } - private void chartModeSelector_Enter(object sender, EventArgs e) - { - - } - private void dayMode_CheckedChanged(object sender, EventArgs e) { LoadChartData(); @@ -146,10 +146,5 @@ namespace Shadowsocks.View { repeatTimesNum.ReadOnly = !PingCheckBox.Checked; } - - private void StatisticsChart_Click(object sender, EventArgs e) - { - - } } } From 7a3f9469139323bb41de2133e4e22fc1690a7ab4 Mon Sep 17 00:00:00 2001 From: icylogic Date: Tue, 1 Mar 2016 11:06:09 +0800 Subject: [PATCH 12/18] fix databinding --- .../Model/StatisticsStrategyConfiguration.cs | 10 +-- ...sticsStrategyConfigurationForm.Designer.cs | 84 ++++++++++--------- .../StatisticsStrategyConfigurationForm.cs | 13 ++- 3 files changed, 58 insertions(+), 49 deletions(-) diff --git a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs index 895a7f03..4a654814 100644 --- a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs +++ b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs @@ -14,12 +14,12 @@ namespace Shadowsocks.Model public class StatisticsStrategyConfiguration { public static readonly string ID = "com.shadowsocks.strategy.statistics"; - public bool StatisticsEnabled { get; } = true; - public bool ByHourOfDay { get; } = true; + public bool StatisticsEnabled { get; set; } = true; + public bool ByHourOfDay { get; set; } = true; public bool Ping { get; set; } - public int ChoiceKeptMinutes { get; } = 10; - public int DataCollectionMinutes { get; } = 10; - public int RepeatTimesNum { get; } = 4; + public int ChoiceKeptMinutes { get; set; } = 10; + public int DataCollectionMinutes { get; set; } = 10; + public int RepeatTimesNum { get; set; } = 4; private const string ConfigFile = "statistics-config.json"; diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs index 5bda6b71..c6cd9764 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs @@ -29,11 +29,11 @@ private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea(); - System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend(); - System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series(); - System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series(); - System.Windows.Forms.DataVisualization.Charting.Series series3 = new System.Windows.Forms.DataVisualization.Charting.Series(); + System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea2 = new System.Windows.Forms.DataVisualization.Charting.ChartArea(); + System.Windows.Forms.DataVisualization.Charting.Legend legend2 = new System.Windows.Forms.DataVisualization.Charting.Legend(); + System.Windows.Forms.DataVisualization.Charting.Series series4 = new System.Windows.Forms.DataVisualization.Charting.Series(); + System.Windows.Forms.DataVisualization.Charting.Series series5 = new System.Windows.Forms.DataVisualization.Charting.Series(); + System.Windows.Forms.DataVisualization.Charting.Series series6 = new System.Windows.Forms.DataVisualization.Charting.Series(); this.StatisticsChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); this.PingCheckBox = new System.Windows.Forms.CheckBox(); this.label2 = new System.Windows.Forms.Label(); @@ -81,47 +81,47 @@ // StatisticsChart // this.StatisticsChart.BackColor = System.Drawing.Color.Transparent; - chartArea1.AxisX.MajorGrid.Enabled = false; - chartArea1.AxisY.MajorGrid.Enabled = false; - chartArea1.AxisY2.Enabled = System.Windows.Forms.DataVisualization.Charting.AxisEnabled.False; - chartArea1.AxisY2.MajorGrid.Enabled = false; - chartArea1.BackColor = System.Drawing.Color.Transparent; - chartArea1.Name = "DataArea"; - this.StatisticsChart.ChartAreas.Add(chartArea1); + chartArea2.AxisX.MajorGrid.Enabled = false; + chartArea2.AxisY.MajorGrid.Enabled = false; + chartArea2.AxisY2.Enabled = System.Windows.Forms.DataVisualization.Charting.AxisEnabled.False; + chartArea2.AxisY2.MajorGrid.Enabled = false; + chartArea2.BackColor = System.Drawing.Color.Transparent; + chartArea2.Name = "DataArea"; + this.StatisticsChart.ChartAreas.Add(chartArea2); this.StatisticsChart.Dock = System.Windows.Forms.DockStyle.Fill; - legend1.BackColor = System.Drawing.Color.Transparent; - legend1.Name = "ChartLegend"; - this.StatisticsChart.Legends.Add(legend1); + legend2.BackColor = System.Drawing.Color.Transparent; + legend2.Name = "ChartLegend"; + this.StatisticsChart.Legends.Add(legend2); this.StatisticsChart.Location = new System.Drawing.Point(0, 0); this.StatisticsChart.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.StatisticsChart.Name = "StatisticsChart"; this.StatisticsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.Pastel; - series1.ChartArea = "DataArea"; - series1.Color = System.Drawing.Color.DarkGray; - series1.Legend = "ChartLegend"; - series1.Name = "Speed"; - series1.ToolTip = "Max inbound speed\\n#VAL KiB/s"; - series2.ChartArea = "DataArea"; - series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bubble; - series2.Color = System.Drawing.Color.Crimson; - series2.CustomProperties = "EmptyPointValue=Zero"; - series2.Legend = "ChartLegend"; - series2.Name = "Package Loss"; - series2.ToolTip = "#VAL%"; - series2.YAxisType = System.Windows.Forms.DataVisualization.Charting.AxisType.Secondary; - series2.YValuesPerPoint = 2; - series3.BorderWidth = 5; - series3.ChartArea = "DataArea"; - series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; - series3.Color = System.Drawing.Color.DodgerBlue; - series3.Legend = "ChartLegend"; - series3.MarkerSize = 10; - series3.MarkerStyle = System.Windows.Forms.DataVisualization.Charting.MarkerStyle.Circle; - series3.Name = "Ping"; - series3.ToolTip = "#VAL ms"; - this.StatisticsChart.Series.Add(series1); - this.StatisticsChart.Series.Add(series2); - this.StatisticsChart.Series.Add(series3); + series4.ChartArea = "DataArea"; + series4.Color = System.Drawing.Color.DarkGray; + series4.Legend = "ChartLegend"; + series4.Name = "Speed"; + series4.ToolTip = "Max inbound speed\\n#VAL KiB/s"; + series5.ChartArea = "DataArea"; + series5.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bubble; + series5.Color = System.Drawing.Color.Crimson; + series5.CustomProperties = "EmptyPointValue=Zero"; + series5.Legend = "ChartLegend"; + series5.Name = "Package Loss"; + series5.ToolTip = "#VAL%"; + series5.YAxisType = System.Windows.Forms.DataVisualization.Charting.AxisType.Secondary; + series5.YValuesPerPoint = 2; + series6.BorderWidth = 5; + series6.ChartArea = "DataArea"; + series6.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; + series6.Color = System.Drawing.Color.DodgerBlue; + series6.Legend = "ChartLegend"; + series6.MarkerSize = 10; + series6.MarkerStyle = System.Windows.Forms.DataVisualization.Charting.MarkerStyle.Circle; + series6.Name = "Ping"; + series6.ToolTip = "#VAL ms"; + this.StatisticsChart.Series.Add(series4); + this.StatisticsChart.Series.Add(series5); + this.StatisticsChart.Series.Add(series6); this.StatisticsChart.Size = new System.Drawing.Size(1089, 352); this.StatisticsChart.TabIndex = 2; // @@ -463,6 +463,8 @@ // bindingConfiguration // this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration); + this.bindingConfiguration.BindingComplete += new System.Windows.Forms.BindingCompleteEventHandler(this.bindingConfiguration_BindingComplete); + this.bindingConfiguration.CurrentItemChanged += new System.EventHandler(this.bindingConfiguration_CurrentItemChanged); // // StatisticsStrategyConfigurationForm // diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs index a0f67f78..91e9d1aa 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs @@ -1,16 +1,13 @@ using System; using System.Collections.Generic; using System.Data; -using System.Drawing; using System.Linq; using System.Windows.Forms; -using System.Windows.Forms.DataVisualization.Charting; using Shadowsocks.Controller; using Shadowsocks.Model; namespace Shadowsocks.View { - using Statistics = Dictionary>; public partial class StatisticsStrategyConfigurationForm : Form { private readonly ShadowsocksController _controller; @@ -146,5 +143,15 @@ namespace Shadowsocks.View { repeatTimesNum.ReadOnly = !PingCheckBox.Checked; } + + private void bindingConfiguration_CurrentItemChanged(object sender, EventArgs e) + { + Logging.Info("?"); + } + + private void bindingConfiguration_BindingComplete(object sender, BindingCompleteEventArgs e) + { + Logging.Info("?"); + } } } From 7914d704d911b6966526e02ec6596ab3c98e5de2 Mon Sep 17 00:00:00 2001 From: icylogic Date: Tue, 1 Mar 2016 14:53:57 +0800 Subject: [PATCH 13/18] finish basic calculation --- .../Service/AvailabilityStatistics.cs | 3 - .../Controller/Strategy/StatisticsStrategy.cs | 10 +- shadowsocks-csharp/Model/StatisticsRecord.cs | 9 +- .../View/CalculationControl.Designer.cs | 14 +- ...sticsStrategyConfigurationForm.Designer.cs | 130 +++++++++--------- .../StatisticsStrategyConfigurationForm.cs | 30 ++-- .../StatisticsStrategyConfigurationForm.resx | 5 +- 7 files changed, 108 insertions(+), 93 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index 8b359105..f547af10 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -18,14 +18,12 @@ namespace Shadowsocks.Controller public sealed class AvailabilityStatistics : IDisposable { public const string DateTimePattern = "yyyy-MM-dd HH:mm:ss"; - public static readonly DateTime UnknownDateTime; private const string StatisticsFilesName = "shadowsocks.availability.json"; public static string AvailabilityStatisticsFile; //static constructor to initialize every public static fields before refereced static AvailabilityStatistics() { AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName); - UnknownDateTime = new DateTime(1970, 1, 1); } //arguments for ICMP tests @@ -230,7 +228,6 @@ namespace Shadowsocks.Controller { if (Config.ByHourOfDay) { - if (record.Timestamp == UnknownDateTime) return false; if (!record.Timestamp.Hour.Equals(DateTime.Now.Hour)) return false; } return true; diff --git a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs index c6643a09..261ef909 100644 --- a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs +++ b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs @@ -64,14 +64,14 @@ namespace Shadowsocks.Controller.Strategy averageRecord.setResponse(records.Select(record => record.AverageResponse)); if (!config.Calculations.TryGetValue("PackageLoss", out factor)) factor = 0; - score += averageRecord.PackageLoss*factor ?? 0; + score += averageRecord.PackageLoss * factor ?? 0; if (!config.Calculations.TryGetValue("AverageResponse", out factor)) factor = 0; - score += averageRecord.AverageResponse*factor ?? 0; + score += averageRecord.AverageResponse * factor ?? 0; if (!config.Calculations.TryGetValue("MinResponse", out factor)) factor = 0; - score += averageRecord.MinResponse*factor ?? 0; + score += averageRecord.MinResponse * factor ?? 0; if (!config.Calculations.TryGetValue("MaxResponse", out factor)) factor = 0; - score += averageRecord.MaxResponse*factor ?? 0; - Logging.Debug($"{JsonConvert.SerializeObject(averageRecord, Formatting.Indented)}"); + score += averageRecord.MaxResponse * factor ?? 0; + Logging.Debug($"Highest score: {score} {JsonConvert.SerializeObject(averageRecord, Formatting.Indented)}"); return score; } diff --git a/shadowsocks-csharp/Model/StatisticsRecord.cs b/shadowsocks-csharp/Model/StatisticsRecord.cs index 54242455..545aa3a9 100644 --- a/shadowsocks-csharp/Model/StatisticsRecord.cs +++ b/shadowsocks-csharp/Model/StatisticsRecord.cs @@ -8,11 +8,10 @@ namespace Shadowsocks.Model // Simple processed records for a short period of time public class StatisticsRecord { - public DateTime Timestamp; + public DateTime Timestamp { get; set; } = DateTime.Now; + public string ServerName { get; set; } - public string ServerName; - - // these fields ping-only records would be null + // in ping-only records, these fields would be null public int? AverageLatency; public int? MinLatency; public int? MaxLatency; @@ -37,7 +36,6 @@ namespace Shadowsocks.Model public StatisticsRecord(string identifier, IEnumerable inboundSpeedRecords, IEnumerable outboundSpeedRecords, IEnumerable latencyRecords) { - Timestamp = DateTime.Now; ServerName = identifier; if (inboundSpeedRecords != null && inboundSpeedRecords.Any()) { @@ -61,7 +59,6 @@ namespace Shadowsocks.Model public StatisticsRecord(string identifier, IEnumerable responseRecords) { - Timestamp = DateTime.Now; ServerName = identifier; setResponse(responseRecords); } diff --git a/shadowsocks-csharp/View/CalculationControl.Designer.cs b/shadowsocks-csharp/View/CalculationControl.Designer.cs index b5a9bfab..9995bb05 100644 --- a/shadowsocks-csharp/View/CalculationControl.Designer.cs +++ b/shadowsocks-csharp/View/CalculationControl.Designer.cs @@ -38,13 +38,14 @@ // factorNum // this.factorNum.DecimalPlaces = 2; + this.factorNum.Dock = System.Windows.Forms.DockStyle.Right; this.factorNum.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.factorNum.Increment = new decimal(new int[] { 1, 0, 0, 131072}); - this.factorNum.Location = new System.Drawing.Point(285, 5); + this.factorNum.Location = new System.Drawing.Point(236, 0); this.factorNum.Minimum = new decimal(new int[] { 1000, 0, @@ -58,7 +59,7 @@ // this.multiply.AutoSize = true; this.multiply.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.multiply.Location = new System.Drawing.Point(251, 7); + this.multiply.Location = new System.Drawing.Point(202, 2); this.multiply.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.multiply.Name = "multiply"; this.multiply.Size = new System.Drawing.Size(26, 28); @@ -69,7 +70,7 @@ // this.plus.AutoSize = true; this.plus.Font = new System.Drawing.Font("Segoe UI", 10F); - this.plus.Location = new System.Drawing.Point(5, 7); + this.plus.Location = new System.Drawing.Point(5, 2); this.plus.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.plus.Name = "plus"; this.plus.Size = new System.Drawing.Size(26, 28); @@ -80,7 +81,7 @@ // this.valueLabel.AutoSize = true; this.valueLabel.Font = new System.Drawing.Font("Microsoft YaHei", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.valueLabel.Location = new System.Drawing.Point(39, 11); + this.valueLabel.Location = new System.Drawing.Point(39, 6); this.valueLabel.Name = "valueLabel"; this.valueLabel.Size = new System.Drawing.Size(118, 24); this.valueLabel.TabIndex = 7; @@ -88,14 +89,15 @@ // // CalculationControl // - this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F); + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.valueLabel); this.Controls.Add(this.factorNum); this.Controls.Add(this.multiply); this.Controls.Add(this.plus); + this.Margin = new System.Windows.Forms.Padding(0); this.Name = "CalculationControl"; - this.Size = new System.Drawing.Size(380, 46); + this.Size = new System.Drawing.Size(322, 34); ((System.ComponentModel.ISupportInitialize)(this.factorNum)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs index c6cd9764..274f93b0 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs @@ -29,11 +29,11 @@ private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea2 = new System.Windows.Forms.DataVisualization.Charting.ChartArea(); - System.Windows.Forms.DataVisualization.Charting.Legend legend2 = new System.Windows.Forms.DataVisualization.Charting.Legend(); - System.Windows.Forms.DataVisualization.Charting.Series series4 = new System.Windows.Forms.DataVisualization.Charting.Series(); - System.Windows.Forms.DataVisualization.Charting.Series series5 = new System.Windows.Forms.DataVisualization.Charting.Series(); - System.Windows.Forms.DataVisualization.Charting.Series series6 = new System.Windows.Forms.DataVisualization.Charting.Series(); + System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea(); + System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend(); + System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series(); + System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series(); + System.Windows.Forms.DataVisualization.Charting.Series series3 = new System.Windows.Forms.DataVisualization.Charting.Series(); this.StatisticsChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); this.PingCheckBox = new System.Windows.Forms.CheckBox(); this.label2 = new System.Windows.Forms.Label(); @@ -57,6 +57,7 @@ this.serverSelector = new System.Windows.Forms.ComboBox(); this.CancelButton = new System.Windows.Forms.Button(); this.OKButton = new System.Windows.Forms.Button(); + this.CalculatinTip = new System.Windows.Forms.ToolTip(this.components); this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components); ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).BeginInit(); this.chartModeSelector.SuspendLayout(); @@ -81,48 +82,51 @@ // StatisticsChart // this.StatisticsChart.BackColor = System.Drawing.Color.Transparent; - chartArea2.AxisX.MajorGrid.Enabled = false; - chartArea2.AxisY.MajorGrid.Enabled = false; - chartArea2.AxisY2.Enabled = System.Windows.Forms.DataVisualization.Charting.AxisEnabled.False; - chartArea2.AxisY2.MajorGrid.Enabled = false; - chartArea2.BackColor = System.Drawing.Color.Transparent; - chartArea2.Name = "DataArea"; - this.StatisticsChart.ChartAreas.Add(chartArea2); + chartArea1.AxisX.MajorGrid.Enabled = false; + chartArea1.AxisY.MajorGrid.Enabled = false; + chartArea1.AxisY2.Enabled = System.Windows.Forms.DataVisualization.Charting.AxisEnabled.False; + chartArea1.AxisY2.MajorGrid.Enabled = false; + chartArea1.BackColor = System.Drawing.Color.Transparent; + chartArea1.Name = "DataArea"; + this.StatisticsChart.ChartAreas.Add(chartArea1); this.StatisticsChart.Dock = System.Windows.Forms.DockStyle.Fill; - legend2.BackColor = System.Drawing.Color.Transparent; - legend2.Name = "ChartLegend"; - this.StatisticsChart.Legends.Add(legend2); + legend1.BackColor = System.Drawing.Color.Transparent; + legend1.Name = "ChartLegend"; + this.StatisticsChart.Legends.Add(legend1); this.StatisticsChart.Location = new System.Drawing.Point(0, 0); this.StatisticsChart.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.StatisticsChart.Name = "StatisticsChart"; this.StatisticsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.Pastel; - series4.ChartArea = "DataArea"; - series4.Color = System.Drawing.Color.DarkGray; - series4.Legend = "ChartLegend"; - series4.Name = "Speed"; - series4.ToolTip = "Max inbound speed\\n#VAL KiB/s"; - series5.ChartArea = "DataArea"; - series5.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bubble; - series5.Color = System.Drawing.Color.Crimson; - series5.CustomProperties = "EmptyPointValue=Zero"; - series5.Legend = "ChartLegend"; - series5.Name = "Package Loss"; - series5.ToolTip = "#VAL%"; - series5.YAxisType = System.Windows.Forms.DataVisualization.Charting.AxisType.Secondary; - series5.YValuesPerPoint = 2; - series6.BorderWidth = 5; - series6.ChartArea = "DataArea"; - series6.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; - series6.Color = System.Drawing.Color.DodgerBlue; - series6.Legend = "ChartLegend"; - series6.MarkerSize = 10; - series6.MarkerStyle = System.Windows.Forms.DataVisualization.Charting.MarkerStyle.Circle; - series6.Name = "Ping"; - series6.ToolTip = "#VAL ms"; - this.StatisticsChart.Series.Add(series4); - this.StatisticsChart.Series.Add(series5); - this.StatisticsChart.Series.Add(series6); - this.StatisticsChart.Size = new System.Drawing.Size(1089, 352); + series1.ChartArea = "DataArea"; + series1.Color = System.Drawing.Color.DarkGray; + series1.Legend = "ChartLegend"; + series1.Name = "Speed"; + series1.ToolTip = "#VALX\\nMax inbound speed\\n#VAL KiB/s"; + series1.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Time; + series2.ChartArea = "DataArea"; + series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bubble; + series2.Color = System.Drawing.Color.Crimson; + series2.CustomProperties = "EmptyPointValue=Zero"; + series2.Legend = "ChartLegend"; + series2.Name = "Package Loss"; + series2.ToolTip = "#VALX\\n#VAL%"; + series2.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Time; + series2.YAxisType = System.Windows.Forms.DataVisualization.Charting.AxisType.Secondary; + series2.YValuesPerPoint = 2; + series3.BorderWidth = 5; + series3.ChartArea = "DataArea"; + series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; + series3.Color = System.Drawing.Color.DodgerBlue; + series3.Legend = "ChartLegend"; + series3.MarkerSize = 10; + series3.MarkerStyle = System.Windows.Forms.DataVisualization.Charting.MarkerStyle.Circle; + series3.Name = "Ping"; + series3.ToolTip = "#VALX\\n#VAL ms"; + series3.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Time; + this.StatisticsChart.Series.Add(series1); + this.StatisticsChart.Series.Add(series2); + this.StatisticsChart.Series.Add(series3); + this.StatisticsChart.Size = new System.Drawing.Size(978, 429); this.StatisticsChart.TabIndex = 2; // // PingCheckBox @@ -163,11 +167,11 @@ this.chartModeSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.chartModeSelector.Controls.Add(this.allMode); this.chartModeSelector.Controls.Add(this.dayMode); - this.chartModeSelector.Location = new System.Drawing.Point(813, 123); + this.chartModeSelector.Location = new System.Drawing.Point(729, 194); this.chartModeSelector.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.chartModeSelector.Name = "chartModeSelector"; this.chartModeSelector.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.chartModeSelector.Size = new System.Drawing.Size(261, 103); + this.chartModeSelector.Size = new System.Drawing.Size(234, 103); this.chartModeSelector.TabIndex = 3; this.chartModeSelector.TabStop = false; this.chartModeSelector.Text = "Chart Mode"; @@ -175,13 +179,11 @@ // allMode // this.allMode.AutoSize = true; - this.allMode.Checked = true; this.allMode.Location = new System.Drawing.Point(11, 61); this.allMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.allMode.Name = "allMode"; this.allMode.Size = new System.Drawing.Size(58, 31); this.allMode.TabIndex = 1; - this.allMode.TabStop = true; this.allMode.Text = "all"; this.allMode.UseVisualStyleBackColor = true; this.allMode.CheckedChanged += new System.EventHandler(this.allMode_CheckedChanged); @@ -189,11 +191,13 @@ // dayMode // this.dayMode.AutoSize = true; + this.dayMode.Checked = true; this.dayMode.Location = new System.Drawing.Point(11, 29); this.dayMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.dayMode.Name = "dayMode"; this.dayMode.Size = new System.Drawing.Size(73, 31); this.dayMode.TabIndex = 0; + this.dayMode.TabStop = true; this.dayMode.Text = "24h"; this.dayMode.UseVisualStyleBackColor = true; this.dayMode.CheckedChanged += new System.EventHandler(this.dayMode_CheckedChanged); @@ -218,8 +222,8 @@ this.splitContainer1.Panel2.Controls.Add(this.OKButton); this.splitContainer1.Panel2.Controls.Add(this.chartModeSelector); this.splitContainer1.Panel2.Controls.Add(this.StatisticsChart); - this.splitContainer1.Size = new System.Drawing.Size(1089, 614); - this.splitContainer1.SplitterDistance = 252; + this.splitContainer1.Size = new System.Drawing.Size(978, 744); + this.splitContainer1.SplitterDistance = 305; this.splitContainer1.SplitterWidth = 10; this.splitContainer1.TabIndex = 12; // @@ -249,7 +253,7 @@ // splitContainer2.Panel2 // this.splitContainer2.Panel2.Controls.Add(this.splitContainer3); - this.splitContainer2.Size = new System.Drawing.Size(1089, 252); + this.splitContainer2.Size = new System.Drawing.Size(978, 305); this.splitContainer2.SplitterDistance = 384; this.splitContainer2.SplitterWidth = 5; this.splitContainer2.TabIndex = 7; @@ -403,20 +407,21 @@ // splitContainer3.Panel2 // this.splitContainer3.Panel2.Controls.Add(this.calculationContainer); - this.splitContainer3.Size = new System.Drawing.Size(700, 252); - this.splitContainer3.SplitterDistance = 46; - this.splitContainer3.SplitterWidth = 10; + this.splitContainer3.Size = new System.Drawing.Size(589, 305); + this.splitContainer3.SplitterDistance = 42; + this.splitContainer3.SplitterWidth = 1; this.splitContainer3.TabIndex = 6; // // label1 // this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(5, 12); + this.label1.Location = new System.Drawing.Point(5, 9); this.label1.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(262, 27); + this.label1.Size = new System.Drawing.Size(137, 27); this.label1.TabIndex = 0; - this.label1.Text = "Design evaluation method"; + this.label1.Text = "Final Score ="; + this.CalculatinTip.SetToolTip(this.label1, "(The server with the highest score would be choosen)"); // // calculationContainer // @@ -425,23 +430,23 @@ this.calculationContainer.Location = new System.Drawing.Point(0, 0); this.calculationContainer.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.calculationContainer.Name = "calculationContainer"; - this.calculationContainer.Size = new System.Drawing.Size(700, 196); + this.calculationContainer.Size = new System.Drawing.Size(589, 262); this.calculationContainer.TabIndex = 1; // // serverSelector // this.serverSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.serverSelector.FormattingEnabled = true; - this.serverSelector.Location = new System.Drawing.Point(813, 86); + this.serverSelector.Location = new System.Drawing.Point(729, 157); this.serverSelector.Name = "serverSelector"; - this.serverSelector.Size = new System.Drawing.Size(260, 35); + this.serverSelector.Size = new System.Drawing.Size(233, 35); this.serverSelector.TabIndex = 6; this.serverSelector.SelectionChangeCommitted += new System.EventHandler(this.serverSelector_SelectionChangeCommitted); // // CancelButton // this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.CancelButton.Location = new System.Drawing.Point(972, 239); + this.CancelButton.Location = new System.Drawing.Point(861, 376); this.CancelButton.Name = "CancelButton"; this.CancelButton.Size = new System.Drawing.Size(101, 41); this.CancelButton.TabIndex = 5; @@ -452,7 +457,7 @@ // OKButton // this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.OKButton.Location = new System.Drawing.Point(864, 239); + this.OKButton.Location = new System.Drawing.Point(754, 376); this.OKButton.Name = "OKButton"; this.OKButton.Size = new System.Drawing.Size(101, 41); this.OKButton.TabIndex = 4; @@ -471,11 +476,11 @@ this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 27F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoSize = true; - this.ClientSize = new System.Drawing.Size(1089, 614); + this.ClientSize = new System.Drawing.Size(978, 744); this.Controls.Add(this.splitContainer1); this.Font = new System.Drawing.Font("Microsoft YaHei", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.MinimumSize = new System.Drawing.Size(1059, 498); + this.MinimumSize = new System.Drawing.Size(1000, 800); this.Name = "StatisticsStrategyConfigurationForm"; this.Text = "StatisticsStrategyConfigurationForm"; ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).EndInit(); @@ -528,5 +533,6 @@ private new System.Windows.Forms.Button CancelButton; private System.Windows.Forms.Button OKButton; private System.Windows.Forms.ComboBox serverSelector; + private System.Windows.Forms.ToolTip CalculatinTip; } } \ No newline at end of file diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs index 91e9d1aa..9fec183d 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; using System.Windows.Forms; +using System.Windows.Forms.DataVisualization.Charting; using Shadowsocks.Controller; using Shadowsocks.Model; @@ -14,15 +15,22 @@ namespace Shadowsocks.View private StatisticsStrategyConfiguration _configuration; private readonly DataTable _dataTable = new DataTable(); private List _servers; + private readonly Series _speedSeries; + private readonly Series _packageLossSeries; + private readonly Series _pingSeries; public StatisticsStrategyConfigurationForm(ShadowsocksController controller) { if (controller == null) return; InitializeComponent(); + _speedSeries = StatisticsChart.Series["Speed"]; + _packageLossSeries = StatisticsChart.Series["Package Loss"]; + _pingSeries = StatisticsChart.Series["Ping"]; _controller = controller; _controller.ConfigChanged += (sender, args) => LoadConfiguration(); LoadConfiguration(); Load += (sender, args) => InitData(); + } private void LoadConfiguration() @@ -48,22 +56,18 @@ namespace Shadowsocks.View serverSelector.DataSource = _servers; - var speedSeries = StatisticsChart.Series["Speed"]; - var packageLossSeries = StatisticsChart.Series["Package Loss"]; - var pingSeries = StatisticsChart.Series["Ping"]; - _dataTable.Columns.Add("Timestamp", typeof(DateTime)); _dataTable.Columns.Add("Speed", typeof (int)); - speedSeries.XValueMember = "Timestamp"; - speedSeries.YValueMembers = "Speed"; + _speedSeries.XValueMember = "Timestamp"; + _speedSeries.YValueMembers = "Speed"; // might be empty _dataTable.Columns.Add("Package Loss", typeof (int)); _dataTable.Columns.Add("Ping", typeof (int)); - packageLossSeries.XValueMember = "Timestamp"; - packageLossSeries.YValueMembers = "Package Loss"; - pingSeries.XValueMember = "Timestamp"; - pingSeries.YValueMembers = "Ping"; + _packageLossSeries.XValueMember = "Timestamp"; + _packageLossSeries.YValueMembers = "Package Loss"; + _pingSeries.XValueMember = "Timestamp"; + _pingSeries.YValueMembers = "Ping"; StatisticsChart.DataSource = _dataTable; LoadChartData(); @@ -98,12 +102,18 @@ namespace Shadowsocks.View IEnumerable> dataGroups; if (allMode.Checked) { + _pingSeries.XValueType = ChartValueType.DateTime; + _packageLossSeries.XValueType = ChartValueType.DateTime; + _speedSeries.XValueType = ChartValueType.DateTime; dataGroups = statistics.GroupBy(data => data.Timestamp.DayOfYear); StatisticsChart.ChartAreas["DataArea"].AxisX.LabelStyle.Format = "g"; StatisticsChart.ChartAreas["DataArea"].AxisX2.LabelStyle.Format = "g"; } else { + _pingSeries.XValueType = ChartValueType.Time; + _packageLossSeries.XValueType = ChartValueType.Time; + _speedSeries.XValueType = ChartValueType.Time; dataGroups = statistics.GroupBy(data => data.Timestamp.Hour); StatisticsChart.ChartAreas["DataArea"].AxisX.LabelStyle.Format = "HH:00"; StatisticsChart.ChartAreas["DataArea"].AxisX2.LabelStyle.Format = "HH:00"; diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx index 5f9d5c44..446dcc01 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx @@ -120,7 +120,10 @@ 1, 30 + + 238, 6 + - 63 + 37 \ No newline at end of file From a99fef37d323f1e13910cdf6a989d5cf567eecf6 Mon Sep 17 00:00:00 2001 From: icylogic Date: Tue, 1 Mar 2016 21:32:43 +0800 Subject: [PATCH 14/18] track multiple servers at the same time Correct AvailabilityStatistics's behavior under special strategies like "LoadBalance", which may switch the server several times in a short period. --- .../Service/AvailabilityStatistics.cs | 158 ++++++++++++------ .../Controller/Service/TCPRelay.cs | 17 +- .../Controller/ShadowsocksController.cs | 29 ++-- .../Controller/Strategy/StatisticsStrategy.cs | 75 +++++---- shadowsocks-csharp/Data/cn.txt | 2 +- shadowsocks-csharp/Model/StatisticsRecord.cs | 31 +++- ...sticsStrategyConfigurationForm.Designer.cs | 24 ++- .../StatisticsStrategyConfigurationForm.cs | 12 +- .../StatisticsStrategyConfigurationForm.resx | 4 +- shadowsocks-csharp/shadowsocks-csharp.csproj | 20 +-- 10 files changed, 224 insertions(+), 148 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index f547af10..790f5d1b 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -31,12 +32,14 @@ namespace Shadowsocks.Controller public const int TimeoutMilliseconds = 500; //records cache for current server in {_monitorInterval} minutes - private List _latencyRecords; + private readonly ConcurrentDictionary> _latencyRecords = new ConcurrentDictionary>(); //speed in KiB/s - private long _lastInboundCounter; - private List _inboundSpeedRecords; - private long _lastOutboundCounter; - private List _outboundSpeedRecords; + private readonly ConcurrentDictionary _inboundCounter = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _lastInboundCounter = new ConcurrentDictionary(); + private readonly ConcurrentDictionary> _inboundSpeedRecords = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary _outboundCounter = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _lastOutboundCounter = new ConcurrentDictionary(); + private readonly ConcurrentDictionary> _outboundSpeedRecords = new ConcurrentDictionary>(); //tasks private readonly TimeSpan _delayBeforeStart = TimeSpan.FromSeconds(1); @@ -45,12 +48,11 @@ namespace Shadowsocks.Controller private TimeSpan RecordingInterval => TimeSpan.FromMinutes(Config.DataCollectionMinutes); private Timer _speedMonior; private readonly TimeSpan _monitorInterval = TimeSpan.FromSeconds(1); - private Timer _writer; //write RawStatistics to file - private readonly TimeSpan _writingInterval = TimeSpan.FromMinutes(1); + //private Timer _writer; //write RawStatistics to file + //private readonly TimeSpan _writingInterval = TimeSpan.FromMinutes(1); private ShadowsocksController _controller; private StatisticsStrategyConfiguration Config => _controller.StatisticsConfiguration; - private Server CurrentServer => _controller.GetCurrentServer(); // Static Singleton Initialization public static AvailabilityStatistics Instance { get; } = new AvailabilityStatistics(); @@ -73,13 +75,11 @@ namespace Shadowsocks.Controller StartTimerWithoutState(ref _recorder, Run, RecordingInterval); LoadRawStatistics(); StartTimerWithoutState(ref _speedMonior, UpdateSpeed, _monitorInterval); - StartTimerWithoutState(ref _writer, Save, _writingInterval); } else { _recorder?.Dispose(); _speedMonior?.Dispose(); - _writer?.Dispose(); } } catch (Exception e) @@ -98,18 +98,27 @@ namespace Shadowsocks.Controller private void UpdateSpeed(object _) { - var bytes = _controller.inboundCounter - _lastInboundCounter; - _lastInboundCounter = _controller.inboundCounter; - var inboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds); - _inboundSpeedRecords.Add(inboundSpeed); - - bytes = _controller.outboundCounter - _lastOutboundCounter; - _lastOutboundCounter = _controller.outboundCounter; - var outboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds); - _outboundSpeedRecords.Add(outboundSpeed); - - Logging.Debug( - $"{CurrentServer.FriendlyName()}: current/max inbound {inboundSpeed}/{_inboundSpeedRecords.Max()} KiB/s, current/max outbound {outboundSpeed}/{_outboundSpeedRecords.Max()} KiB/s"); + foreach (var kv in _lastInboundCounter) + { + var id = kv.Key; + + var lastInbound = kv.Value; + var inbound = _inboundCounter[id]; + var bytes = inbound - lastInbound; + _lastInboundCounter[id] = inbound; + var inboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds); + _inboundSpeedRecords.GetOrAdd(id, new List {inboundSpeed}).Add(inboundSpeed); + + var lastOutbound = _lastOutboundCounter[id]; + var outbound = _outboundCounter[id]; + bytes = outbound - lastOutbound; + _lastOutboundCounter[id] = outbound; + var outboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds); + _outboundSpeedRecords.GetOrAdd(id, new List {outboundSpeed}).Add(outboundSpeed); + + Logging.Debug( + $"{id}: current/max inbound {inboundSpeed}/{_inboundSpeedRecords[id].Max()} KiB/s, current/max outbound {outboundSpeed}/{_outboundSpeedRecords[id].Max()} KiB/s"); + } } private async Task ICMPTest(Server server) @@ -161,66 +170,75 @@ namespace Shadowsocks.Controller private void Reset() { - _inboundSpeedRecords = new List(); - _outboundSpeedRecords = new List(); - _latencyRecords = new List(); + _inboundSpeedRecords.Clear(); + _outboundSpeedRecords.Clear(); + _latencyRecords.Clear(); } private void Run(object _) { UpdateRecords(); + Save(); Reset(); FilterRawStatistics(); } private async void UpdateRecords() { - var currentServerRecord = new StatisticsRecord(CurrentServer.Identifier(), _inboundSpeedRecords, _outboundSpeedRecords, _latencyRecords); + var records = new Dictionary(); - if (!Config.Ping) + foreach (var server in _controller.GetCurrentConfiguration().configs) { - AppendRecord(CurrentServer, currentServerRecord); - return; + var id = server.Identifier(); + List inboundSpeedRecords = null; + List outboundSpeedRecords = null; + List latencyRecords = null; + _inboundSpeedRecords.TryGetValue(id, out inboundSpeedRecords); + _outboundSpeedRecords.TryGetValue(id, out outboundSpeedRecords); + _latencyRecords.TryGetValue(id, out latencyRecords); + records.Add(id, new StatisticsRecord(id, inboundSpeedRecords, outboundSpeedRecords, latencyRecords)); } - var icmpResults = TaskEx.WhenAll(_controller.GetCurrentConfiguration().configs.Select(ICMPTest)); - - foreach (var result in (await icmpResults).Where(result => result != null)) + if (Config.Ping) { - if (result.Server.Equals(CurrentServer)) + var icmpResults = await TaskEx.WhenAll(_controller.GetCurrentConfiguration().configs.Select(ICMPTest)); + foreach (var result in icmpResults.Where(result => result != null)) { - currentServerRecord.setResponse(result.RoundtripTime); - AppendRecord(CurrentServer, currentServerRecord); - } - else - { - AppendRecord(result.Server, new StatisticsRecord(result.Server.Identifier(), result.RoundtripTime)); + records[result.Server.Identifier()].SetResponse(result.RoundtripTime); } } + + foreach (var kv in records.Where(kv => !kv.Value.IsEmptyData())) + { + AppendRecord(kv.Key, kv.Value); + } } - private void AppendRecord(Server server, StatisticsRecord record) + private void AppendRecord(string serverIdentifier, StatisticsRecord record) { List records; - if (!RawStatistics.TryGetValue(server.Identifier(), out records)) + if (!RawStatistics.TryGetValue(serverIdentifier, out records)) { records = new List(); } records.Add(record); - RawStatistics[server.Identifier()] = records; + RawStatistics[serverIdentifier] = records; } - private void Save(object _) + private void Save() { + if (RawStatistics.Count == 0) + { + return; + } try { - File.WriteAllText(AvailabilityStatisticsFile, - JsonConvert.SerializeObject(RawStatistics, Formatting.None)); + var content = JsonConvert.SerializeObject(RawStatistics, Formatting.None); + File.WriteAllText(AvailabilityStatisticsFile, content); } catch (IOException e) { Logging.LogUsefulException(e); - _writer.Change(_retryInterval, _writingInterval); } } @@ -273,11 +291,6 @@ namespace Shadowsocks.Controller } } - public void UpdateLatency(int latency) - { - _latencyRecords.Add(latency); - } - private static int GetSpeedInKiBPerSecond(long bytes, double seconds) { var result = (int) (bytes/seconds)/1024; @@ -298,8 +311,49 @@ namespace Shadowsocks.Controller public void Dispose() { _recorder.Dispose(); - _writer.Dispose(); _speedMonior.Dispose(); } + + public void UpdateLatency(Server server, int latency) + { + List records; + _latencyRecords.TryGetValue(server.Identifier(), out records); + if (records == null) + { + records = new List(); + } + records.Add(latency); + _latencyRecords[server.Identifier()] = records; + } + + public void UpdateInboundCounter(Server server, long n) + { + long count; + if (_inboundCounter.TryGetValue(server.Identifier(), out count)) + { + count += n; + } + else + { + count = n; + _lastInboundCounter[server.Identifier()] = 0; + } + _inboundCounter[server.Identifier()] = count; + } + + public void UpdateOutboundCounter(Server server, long n) + { + long count; + if (_outboundCounter.TryGetValue(server.Identifier(), out count)) + { + count += n; + } + else + { + count = n; + _lastOutboundCounter[server.Identifier()] = 0; + } + _outboundCounter[server.Identifier()] = count; + } } } diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index d5d82ed5..9c162780 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -69,14 +69,14 @@ namespace Shadowsocks.Controller return true; } - public void UpdateInboundCounter(long n) + public void UpdateInboundCounter(Server server, long n) { - _controller.UpdateInboundCounter(n); + _controller.UpdateInboundCounter(server, n); } - public void UpdateOutboundCounter(long n) + public void UpdateOutboundCounter(Server server, long n) { - _controller.UpdateOutboundCounter(n); + _controller.UpdateOutboundCounter(server, n); } public void UpdateLatency(Server server, TimeSpan latency) @@ -488,10 +488,7 @@ namespace Shadowsocks.Controller var latency = DateTime.Now - _startConnectTime; IStrategy strategy = controller.GetCurrentStrategy(); - if (strategy != null) - { - strategy.UpdateLatency(server, latency); - } + strategy?.UpdateLatency(server, latency); tcprelay.UpdateLatency(server, latency); StartPipe(); @@ -543,7 +540,7 @@ namespace Shadowsocks.Controller { int bytesRead = remote.EndReceive(ar); totalRead += bytesRead; - tcprelay.UpdateInboundCounter(bytesRead); + tcprelay.UpdateInboundCounter(server, bytesRead); if (bytesRead > 0) { @@ -610,7 +607,7 @@ namespace Shadowsocks.Controller encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); } Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeConnectionReceiveCallback() (upload)"); - tcprelay.UpdateOutboundCounter(bytesToSend); + tcprelay.UpdateOutboundCounter(server, bytesToSend); _startSendingTime = DateTime.Now; _bytesToSend = bytesToSend; remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index cb49f5f2..13c6650d 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -5,7 +5,7 @@ using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; - +using System.Threading.Tasks; using Newtonsoft.Json; using Shadowsocks.Controller.Strategy; @@ -307,14 +307,30 @@ namespace Shadowsocks.Controller Configuration.Save(_config); } - public void UpdateInboundCounter(long n) + public void UpdateLatency(Server server, TimeSpan latency) + { + if (_config.availabilityStatistics) + { + new Task(() => availabilityStatistics.UpdateLatency(server, (int) latency.TotalMilliseconds)).Start(); + } + } + + public void UpdateInboundCounter(Server server, long n) { Interlocked.Add(ref inboundCounter, n); + if (_config.availabilityStatistics) + { + new Task(() => availabilityStatistics.UpdateInboundCounter(server, n)).Start(); + } } - public void UpdateOutboundCounter(long n) + public void UpdateOutboundCounter(Server server, long n) { Interlocked.Add(ref outboundCounter, n); + if (_config.availabilityStatistics) + { + new Task(() => availabilityStatistics.UpdateOutboundCounter(server, n)).Start(); + } } protected void Reload() @@ -498,12 +514,5 @@ namespace Shadowsocks.Controller } } - public void UpdateLatency(Server server, TimeSpan latency) - { - if (_config.availabilityStatistics) - { - availabilityStatistics.UpdateLatency((int) latency.TotalMilliseconds); - } - } } } diff --git a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs index 261ef909..075e18e4 100644 --- a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs +++ b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs @@ -49,29 +49,32 @@ namespace Shadowsocks.Controller.Strategy //return the score by data //server with highest score will be choosen - private float GetScore(string serverName) + private float? GetScore(string identifier, List records) { var config = _controller.StatisticsConfiguration; - List records; - if (_filteredStatistics == null || !_filteredStatistics.TryGetValue(serverName, out records)) return 0; - float factor; - float score = 0; - - var averageRecord = new StatisticsRecord(serverName, - records.FindAll(record => record.MaxInboundSpeed != null).Select(record => record.MaxInboundSpeed.Value), - records.FindAll(record => record.MaxOutboundSpeed != null).Select(record => record.MaxOutboundSpeed.Value), - records.FindAll(record => record.AverageLatency != null).Select(record => record.AverageLatency.Value)); - averageRecord.setResponse(records.Select(record => record.AverageResponse)); - - if (!config.Calculations.TryGetValue("PackageLoss", out factor)) factor = 0; - score += averageRecord.PackageLoss * factor ?? 0; - if (!config.Calculations.TryGetValue("AverageResponse", out factor)) factor = 0; - score += averageRecord.AverageResponse * factor ?? 0; - if (!config.Calculations.TryGetValue("MinResponse", out factor)) factor = 0; - score += averageRecord.MinResponse * factor ?? 0; - if (!config.Calculations.TryGetValue("MaxResponse", out factor)) factor = 0; - score += averageRecord.MaxResponse * factor ?? 0; - Logging.Debug($"Highest score: {score} {JsonConvert.SerializeObject(averageRecord, Formatting.Indented)}"); + float? score = null; + + var averageRecord = new StatisticsRecord(identifier, + records.Where(record => record.MaxInboundSpeed != null).Select(record => record.MaxInboundSpeed.Value).ToList(), + records.Where(record => record.MaxOutboundSpeed != null).Select(record => record.MaxOutboundSpeed.Value).ToList(), + records.Where(record => record.AverageLatency != null).Select(record => record.AverageLatency.Value).ToList()); + averageRecord.SetResponse(records.Select(record => record.AverageResponse).ToList()); + + foreach (var calculation in config.Calculations) + { + var name = calculation.Key; + var field = typeof (StatisticsRecord).GetField(name); + dynamic value = field.GetValue(averageRecord); + var factor = calculation.Value; + if (value == null || factor.Equals(0)) continue; + score = score ?? 0; + score += value * factor; + } + + if (score != null) + { + Logging.Debug($"Highest score: {score} {JsonConvert.SerializeObject(averageRecord, Formatting.Indented)}"); + } return score; } @@ -83,15 +86,25 @@ namespace Shadowsocks.Controller.Strategy } try { - var bestResult = (from server in servers - let name = server.FriendlyName() - where _filteredStatistics.ContainsKey(name) - select new - { - server, - score = GetScore(name) - } - ).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2); + var serversWithStatistics = (from server in servers + let id = server.Identifier() + where _filteredStatistics.ContainsKey(id) + let score = GetScore(server.Identifier(), _filteredStatistics[server.Identifier()]) + where score != null + select new + { + server, + score + }).ToArray(); + + if (serversWithStatistics.Length < 2) + { + LogWhenEnabled("no enough statistics data for evaluation"); + return; + } + + var bestResult = serversWithStatistics + .Aggregate((server1, server2) => server1.score > server2.score ? server1 : server2); LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by statistics: score {bestResult.score}"); _currentServer = bestResult.server; @@ -112,7 +125,7 @@ namespace Shadowsocks.Controller.Strategy public string ID => "com.shadowsocks.strategy.scbs"; - public string Name => I18N.GetString("Choose By Total Package Loss"); + public string Name => I18N.GetString("Choose by statistics"); public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint) { diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt index a6d77156..68af293b 100644 --- a/shadowsocks-csharp/Data/cn.txt +++ b/shadowsocks-csharp/Data/cn.txt @@ -30,7 +30,7 @@ Quit=退出 Edit Servers=编辑服务器 Load Balance=负载均衡 High Availability=高可用 -Choose By Total Package Loss=累计丢包率 +Choose by statistics=根据统计 # Config Form diff --git a/shadowsocks-csharp/Model/StatisticsRecord.cs b/shadowsocks-csharp/Model/StatisticsRecord.cs index 545aa3a9..47015f75 100644 --- a/shadowsocks-csharp/Model/StatisticsRecord.cs +++ b/shadowsocks-csharp/Model/StatisticsRecord.cs @@ -9,34 +9,49 @@ namespace Shadowsocks.Model public class StatisticsRecord { public DateTime Timestamp { get; set; } = DateTime.Now; - public string ServerName { get; set; } + public string ServerIdentifier { get; set; } // in ping-only records, these fields would be null public int? AverageLatency; public int? MinLatency; public int? MaxLatency; + private bool EmptyLatencyData => (AverageLatency == null) && (MinLatency == null) && (MaxLatency == null); + public int? AverageInboundSpeed; public int? MinInboundSpeed; public int? MaxInboundSpeed; + private bool EmptyInboundSpeedData + => (AverageInboundSpeed == null) && (MinInboundSpeed == null) && (MaxInboundSpeed == null); + public int? AverageOutboundSpeed; public int? MinOutboundSpeed; public int? MaxOutboundSpeed; + private bool EmptyOutboundSpeedData + => (AverageOutboundSpeed == null) && (MinOutboundSpeed == null) && (MaxOutboundSpeed == null); + // if user disabled ping test, response would be null public int? AverageResponse; public int? MinResponse; public int? MaxResponse; public float? PackageLoss; + private bool EmptyResponseData + => (AverageResponse == null) && (MinResponse == null) && (MaxResponse == null) && (PackageLoss == null); + + public bool IsEmptyData() { + return EmptyInboundSpeedData && EmptyOutboundSpeedData && EmptyResponseData && EmptyLatencyData; + } + public StatisticsRecord() { } - public StatisticsRecord(string identifier, IEnumerable inboundSpeedRecords, IEnumerable outboundSpeedRecords, IEnumerable latencyRecords) + public StatisticsRecord(string identifier, ICollection inboundSpeedRecords, ICollection outboundSpeedRecords, ICollection latencyRecords) { - ServerName = identifier; + ServerIdentifier = identifier; if (inboundSpeedRecords != null && inboundSpeedRecords.Any()) { AverageInboundSpeed = (int) inboundSpeedRecords.Average(); @@ -57,13 +72,13 @@ namespace Shadowsocks.Model } } - public StatisticsRecord(string identifier, IEnumerable responseRecords) + public StatisticsRecord(string identifier, ICollection responseRecords) { - ServerName = identifier; - setResponse(responseRecords); + ServerIdentifier = identifier; + SetResponse(responseRecords); } - public void setResponse(IEnumerable responseRecords) + public void SetResponse(ICollection responseRecords) { if (responseRecords == null) return; var records = responseRecords.Where(response => response != null).Select(response => response.Value).ToList(); @@ -71,7 +86,7 @@ namespace Shadowsocks.Model AverageResponse = (int?) records.Average(); MinResponse = records.Min(); MaxResponse = records.Max(); - PackageLoss = responseRecords.Count(response => response != null)/(float) responseRecords.Count(); + PackageLoss = responseRecords.Count(response => response != null)/(float) responseRecords.Count; } } } diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs index 274f93b0..19cc6731 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs @@ -36,6 +36,7 @@ System.Windows.Forms.DataVisualization.Charting.Series series3 = new System.Windows.Forms.DataVisualization.Charting.Series(); this.StatisticsChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); this.PingCheckBox = new System.Windows.Forms.CheckBox(); + this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components); this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.chartModeSelector = new System.Windows.Forms.GroupBox(); @@ -58,8 +59,8 @@ this.CancelButton = new System.Windows.Forms.Button(); this.OKButton = new System.Windows.Forms.Button(); this.CalculatinTip = new System.Windows.Forms.ToolTip(this.components); - this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components); ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit(); this.chartModeSelector.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); this.splitContainer1.Panel1.SuspendLayout(); @@ -76,7 +77,6 @@ this.splitContainer3.Panel1.SuspendLayout(); this.splitContainer3.Panel2.SuspendLayout(); this.splitContainer3.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit(); this.SuspendLayout(); // // StatisticsChart @@ -142,6 +142,10 @@ this.PingCheckBox.UseVisualStyleBackColor = true; this.PingCheckBox.CheckedChanged += new System.EventHandler(this.PingCheckBox_CheckedChanged); // + // bindingConfiguration + // + this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration); + // // label2 // this.label2.AutoSize = true; @@ -167,7 +171,7 @@ this.chartModeSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.chartModeSelector.Controls.Add(this.allMode); this.chartModeSelector.Controls.Add(this.dayMode); - this.chartModeSelector.Location = new System.Drawing.Point(729, 194); + this.chartModeSelector.Location = new System.Drawing.Point(729, 188); this.chartModeSelector.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.chartModeSelector.Name = "chartModeSelector"; this.chartModeSelector.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10); @@ -437,7 +441,7 @@ // this.serverSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.serverSelector.FormattingEnabled = true; - this.serverSelector.Location = new System.Drawing.Point(729, 157); + this.serverSelector.Location = new System.Drawing.Point(729, 151); this.serverSelector.Name = "serverSelector"; this.serverSelector.Size = new System.Drawing.Size(233, 35); this.serverSelector.TabIndex = 6; @@ -446,7 +450,7 @@ // CancelButton // this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.CancelButton.Location = new System.Drawing.Point(861, 376); + this.CancelButton.Location = new System.Drawing.Point(861, 370); this.CancelButton.Name = "CancelButton"; this.CancelButton.Size = new System.Drawing.Size(101, 41); this.CancelButton.TabIndex = 5; @@ -457,7 +461,7 @@ // OKButton // this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.OKButton.Location = new System.Drawing.Point(754, 376); + this.OKButton.Location = new System.Drawing.Point(754, 370); this.OKButton.Name = "OKButton"; this.OKButton.Size = new System.Drawing.Size(101, 41); this.OKButton.TabIndex = 4; @@ -465,12 +469,6 @@ this.OKButton.UseVisualStyleBackColor = true; this.OKButton.Click += new System.EventHandler(this.OKButton_Click); // - // bindingConfiguration - // - this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration); - this.bindingConfiguration.BindingComplete += new System.Windows.Forms.BindingCompleteEventHandler(this.bindingConfiguration_BindingComplete); - this.bindingConfiguration.CurrentItemChanged += new System.EventHandler(this.bindingConfiguration_CurrentItemChanged); - // // StatisticsStrategyConfigurationForm // this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 27F); @@ -484,6 +482,7 @@ this.Name = "StatisticsStrategyConfigurationForm"; this.Text = "StatisticsStrategyConfigurationForm"; ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit(); this.chartModeSelector.ResumeLayout(false); this.chartModeSelector.PerformLayout(); this.splitContainer1.Panel1.ResumeLayout(false); @@ -503,7 +502,6 @@ this.splitContainer3.Panel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit(); this.splitContainer3.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit(); this.ResumeLayout(false); } diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs index 9fec183d..42f8ee87 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs @@ -36,7 +36,7 @@ namespace Shadowsocks.View private void LoadConfiguration() { var configs = _controller.GetCurrentConfiguration().configs; - _servers = configs.Select(server => server.FriendlyName()).ToList(); + _servers = configs.Select(server => server.Identifier()).ToList(); _configuration = _controller.StatisticsConfiguration ?? new StatisticsStrategyConfiguration(); if (_configuration.Calculations == null) @@ -153,15 +153,5 @@ namespace Shadowsocks.View { repeatTimesNum.ReadOnly = !PingCheckBox.Checked; } - - private void bindingConfiguration_CurrentItemChanged(object sender, EventArgs e) - { - Logging.Info("?"); - } - - private void bindingConfiguration_BindingComplete(object sender, BindingCompleteEventArgs e) - { - Logging.Info("?"); - } } } diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx index 446dcc01..8360b4c2 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx @@ -118,12 +118,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - 1, 30 + 4, 5 238, 6 - 37 + 191 \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 101ad9ee..d186471e 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -53,13 +53,14 @@ bin\x86\Release\ TRACE - true + false pdbonly x86 prompt ManagedMinimumRules.ruleset false true + true app.manifest @@ -341,13 +342,12 @@ - - - - - - - + + + + + f.ItemSpec).Where(f => !excludedAssemblie foreach (var item in filesToCleanup) File.Delete(item); -]]> - +]]> + From e72822aac0810cc719b10d94aec8059c28e16d91 Mon Sep 17 00:00:00 2001 From: icylogic Date: Tue, 1 Mar 2016 21:33:56 +0800 Subject: [PATCH 15/18] turn on optimization --- shadowsocks-csharp/shadowsocks-csharp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index d186471e..2f43d366 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -53,7 +53,7 @@ bin\x86\Release\ TRACE - false + true pdbonly x86 prompt From 8f175c481ebf1adbf7e9e06afb13c93c1b4c54a3 Mon Sep 17 00:00:00 2001 From: icylogic Date: Tue, 1 Mar 2016 22:25:32 +0800 Subject: [PATCH 16/18] filter out meanless data. --- shadowsocks-csharp/Model/StatisticsRecord.cs | 27 ++++++++++--------- .../StatisticsStrategyConfigurationForm.cs | 4 +-- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/shadowsocks-csharp/Model/StatisticsRecord.cs b/shadowsocks-csharp/Model/StatisticsRecord.cs index 47015f75..5c1051a4 100644 --- a/shadowsocks-csharp/Model/StatisticsRecord.cs +++ b/shadowsocks-csharp/Model/StatisticsRecord.cs @@ -52,23 +52,26 @@ namespace Shadowsocks.Model public StatisticsRecord(string identifier, ICollection inboundSpeedRecords, ICollection outboundSpeedRecords, ICollection latencyRecords) { ServerIdentifier = identifier; - if (inboundSpeedRecords != null && inboundSpeedRecords.Any()) + var inbound = inboundSpeedRecords?.Where(s => s > 0).ToList(); + if (inbound != null && inbound.Any()) { - AverageInboundSpeed = (int) inboundSpeedRecords.Average(); - MinInboundSpeed = inboundSpeedRecords.Min(); - MaxInboundSpeed = inboundSpeedRecords.Max(); + AverageInboundSpeed = (int) inbound.Average(); + MinInboundSpeed = inbound.Min(); + MaxInboundSpeed = inbound.Max(); } - if (outboundSpeedRecords != null && outboundSpeedRecords.Any()) + var outbound = outboundSpeedRecords?.Where(s => s > 0).ToList(); + if (outbound!= null && outbound.Any()) { - AverageOutboundSpeed = (int) outboundSpeedRecords.Average(); - MinOutboundSpeed = outboundSpeedRecords.Min(); - MaxOutboundSpeed = outboundSpeedRecords.Max(); + AverageOutboundSpeed = (int) outbound.Average(); + MinOutboundSpeed = outbound.Min(); + MaxOutboundSpeed = outbound.Max(); } - if (latencyRecords != null && latencyRecords.Any()) + var latency = latencyRecords?.Where(s => s > 0).ToList(); + if (latency!= null && latency.Any()) { - AverageLatency = (int) latencyRecords.Average(); - MinLatency = latencyRecords.Min(); - MaxLatency = latencyRecords.Max(); + AverageLatency = (int) latency.Average(); + MinLatency = latency.Min(); + MaxLatency = latency.Max(); } } diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs index 42f8ee87..4f1b95ae 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs @@ -125,9 +125,9 @@ namespace Shadowsocks.View dataGroup.First().Timestamp, Speed = dataGroup.Max(data => data.MaxInboundSpeed) ?? 0, Ping = (int) (dataGroup.Average(data => data.AverageResponse) ?? 0), - PackageLossPercentage = (dataGroup.Average(data => data.PackageLoss) ?? 0) * 100 + PackageLossPercentage = (int) (dataGroup.Average(data => data.PackageLoss) ?? 0) * 100 }; - foreach (var data in finalData) + foreach (var data in finalData.Where(data => data.Speed != 0 || data.PackageLossPercentage != 0 || data.Ping != 0)) { _dataTable.Rows.Add(data.Timestamp, data.Speed, data.PackageLossPercentage, data.Ping); } From 11066b75819fab541237a5cde0c82dc98e1aba34 Mon Sep 17 00:00:00 2001 From: icylogic Date: Tue, 1 Mar 2016 22:43:38 +0800 Subject: [PATCH 17/18] in case of broken configs --- shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs index 075e18e4..fabf726d 100644 --- a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs +++ b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs @@ -64,7 +64,7 @@ namespace Shadowsocks.Controller.Strategy { var name = calculation.Key; var field = typeof (StatisticsRecord).GetField(name); - dynamic value = field.GetValue(averageRecord); + dynamic value = field?.GetValue(averageRecord); var factor = calculation.Value; if (value == null || factor.Equals(0)) continue; score = score ?? 0; From 65e12e43c9ee97e534eb5ee3472cd4c6ed079e2c Mon Sep 17 00:00:00 2001 From: icylogic Date: Tue, 1 Mar 2016 23:06:21 +0800 Subject: [PATCH 18/18] make hints more friendly. --- shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs index fabf726d..9679f681 100644 --- a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs +++ b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs @@ -99,7 +99,7 @@ namespace Shadowsocks.Controller.Strategy if (serversWithStatistics.Length < 2) { - LogWhenEnabled("no enough statistics data for evaluation"); + LogWhenEnabled("no enough statistics data or all factors in calculations are 0"); return; }