From e45dd8b69514970cc7dfc1e5aa4c342aa3322c96 Mon Sep 17 00:00:00 2001 From: icylogic Date: Thu, 3 Sep 2015 23:37:43 +0800 Subject: [PATCH] core functions finished --- .../Service/AvailabilityStatistics.cs | 59 +++++--- .../Controller/ShadowsocksController.cs | 16 +- .../Controller/Strategy/StatisticsStrategy.cs | 138 ++++++++++++------ .../Model/StatisticsStrategyConfiguration.cs | 16 +- .../View/CalculationControl.Designer.cs | 1 + shadowsocks-csharp/View/CalculationControl.cs | 8 +- shadowsocks-csharp/View/MenuViewController.cs | 8 - ...sticsStrategyConfigurationForm.Designer.cs | 71 ++++----- .../StatisticsStrategyConfigurationForm.cs | 3 +- .../StatisticsStrategyConfigurationForm.resx | 3 + 10 files changed, 192 insertions(+), 131 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index c5f94424..887a7a72 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -6,10 +6,11 @@ using System.Net; using SimpleJson; using System.Net.Http; using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Shadowsocks.Model; -using SimpleJson = SimpleJson.SimpleJson; using Shadowsocks.Util; using Timer = System.Threading.Timer; @@ -20,35 +21,43 @@ namespace Shadowsocks.Controller internal class AvailabilityStatistics { + public static readonly 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 Repeat = 4; //repeat times every evaluation - private const int Interval = 10*60*1000; //evaluate proxies every 15 minutes - private const int delayBeforeStart = 1*1000; //delay 1 second before start + private const int DelayBeforeStart = 1000; + private int _repeat => _config.RepeatTimesNum; + private int _interval => (int) TimeSpan.FromMinutes(_config.DataCollectionMinutes).TotalMilliseconds; private Timer _timer; private State _state; private List _servers; + private StatisticsStrategyConfiguration _config; public static string AvailabilityStatisticsFile; //static constructor to initialize every public static fields before refereced static AvailabilityStatistics() { - string temppath = Utils.GetTempPath(); + var temppath = Utils.GetTempPath(); AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName); } - public bool Set(bool enabled) + public AvailabilityStatistics(Configuration config, StatisticsStrategyConfiguration statisticsConfig) { + UpdateConfiguration(config, statisticsConfig); + } + + public bool Set(StatisticsStrategyConfiguration config) + { + _config = config; try { - if (enabled) + if (config.StatisticsEnabled) { - if (_timer?.Change(0, Interval) == null) + if (_timer?.Change(DelayBeforeStart, _interval) == null) { _state = new State(); - _timer = new Timer(Evaluate, _state, 0, Interval); + _timer = new Timer(Evaluate, _state, DelayBeforeStart, _interval); } } else @@ -66,7 +75,7 @@ namespace Shadowsocks.Controller //hardcode //TODO: backup reliable isp&geolocation provider or a local database is required - private static async Task getGeolocationAndISP() + public static async Task GetGeolocationAndIsp() { Logging.Debug("Retrive information of geolocation and isp"); const string api = "http://ip-api.com/json"; @@ -92,24 +101,25 @@ namespace Shadowsocks.Controller string isp = obj["isp"]; string regionName = obj["regionName"]; if (country == null || city == null || isp == null || regionName == null) return ret; - ret[0] = new DataUnit(State.Geolocation, $"{country} {regionName} {city}"); + ret[0] = new DataUnit(State.Geolocation, $"{country} | {regionName} | {city}"); ret[1] = new DataUnit(State.ISP, isp); return ret; } - private static async Task> ICMPTest(Server server) + private async Task> ICMPTest(Server server) { - Logging.Debug("eveluating " + server.FriendlyName()); + Logging.Debug("Ping " + server.FriendlyName()); if (server.server == "") return null; + var IP = Dns.GetHostAddresses(server.server).First(ip => ip.AddressFamily == AddressFamily.InterNetwork); var ping = new Ping(); var ret = new List(); foreach ( - var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))) + var timestamp in Enumerable.Range(0, _repeat).Select(_ => DateTime.Now.ToString(DateTimePattern))) { //ICMP echo. we can also set options and special bytes try { - var reply = await ping.SendTaskAsync(server.server, Timeout); + var reply = ping.Send(IP, Timeout); ret.Add(new List> { new KeyValuePair("Timestamp", timestamp), @@ -118,6 +128,8 @@ namespace Shadowsocks.Controller new KeyValuePair("RoundtripTime", reply?.RoundtripTime.ToString()) //new KeyValuePair("data", reply.Buffer.ToString()); // The data of reply }); + Thread.Sleep(new Random().Next() % Timeout); + //Do ICMPTest in a random frequency } catch (Exception e) { @@ -130,7 +142,7 @@ namespace Shadowsocks.Controller private async void Evaluate(object obj) { - var geolocationAndIsp = getGeolocationAndISP(); + var geolocationAndIsp = GetGeolocationAndIsp(); foreach (var dataLists in await TaskEx.WhenAll(_servers.Select(ICMPTest))) { if (dataLists == null) continue; @@ -156,16 +168,23 @@ namespace Shadowsocks.Controller { lines = new[] {dataLine}; } - File.AppendAllLines(AvailabilityStatisticsFile, lines); + try + { + File.AppendAllLines(AvailabilityStatisticsFile, lines); + } + catch (IOException e) + { + Logging.LogUsefulException(e); + } } - internal void UpdateConfiguration(Configuration config) + internal void UpdateConfiguration(Configuration config, StatisticsStrategyConfiguration statisticsConfig) { - Set(config.availabilityStatistics); + Set(statisticsConfig); _servers = config.configs; } - private class State + public class State { public DataList dataList = new DataList(); public const string Geolocation = "Geolocation"; diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 15a5d76a..38a3ed09 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -258,14 +258,12 @@ namespace Shadowsocks.Controller } } - public void ToggleAvailabilityStatistics(bool enabled) + public void UpdateStatisticsConfiguration(bool enabled) { - if (_availabilityStatics != null) - { - _availabilityStatics.Set(enabled); - _config.availabilityStatistics = enabled; - SaveConfig(_config); - } + if (_availabilityStatics == null) return; + _availabilityStatics.UpdateConfiguration(_config, StatisticsConfiguration); + _config.availabilityStatistics = enabled; + SaveConfig(_config); } public void SavePACUrl(string pacUrl) @@ -315,9 +313,9 @@ namespace Shadowsocks.Controller if (_availabilityStatics == null) { - _availabilityStatics = new AvailabilityStatistics(); + _availabilityStatics = new AvailabilityStatistics(_config, StatisticsConfiguration); } - _availabilityStatics.UpdateConfiguration(_config); + _availabilityStatics.UpdateConfiguration(_config, StatisticsConfiguration); if (_listener != null) { diff --git a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs index 8bbe13e4..ff08b7a0 100644 --- a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs +++ b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -7,19 +8,25 @@ using System.Text; using Shadowsocks.Model; using System.IO; using System.Net.NetworkInformation; -using System.Threading; +using System.Windows.Forms; +using Newtonsoft.Json; using Shadowsocks.Model; +using Timer = System.Threading.Timer; namespace Shadowsocks.Controller.Strategy { + using DataUnit = KeyValuePair; + using DataList = List>; + class StatisticsStrategy : IStrategy { private readonly ShadowsocksController _controller; private Server _currentServer; private readonly Timer _timer; - private Dictionary _statistics; - private const int CachedInterval = 30*60*1000; //choose a new server every 30 minutes - private const int RetryInterval = 2*60*1000; //choose a new server every 30 minutes + private Dictionary> _rawStatistics; + private int ChoiceKeptMilliseconds + => (int) TimeSpan.FromMinutes(_controller.StatisticsConfiguration.ChoiceKeptMinutes).TotalMilliseconds; + private const int RetryInterval = 2*60*1000; //retry 2 minutes after failed public StatisticsStrategy(ShadowsocksController controller) { @@ -38,13 +45,6 @@ namespace Shadowsocks.Controller.Strategy ChooseNewServer(servers); } - /* - return a dict: - { - 'ServerFriendlyName1':StatisticsData, - 'ServerFriendlyName2':... - } - */ private void LoadStatistics() { try @@ -53,32 +53,30 @@ namespace Shadowsocks.Controller.Strategy Logging.Debug($"loading statistics from {path}"); if (!File.Exists(path)) { - LogWhenEnabled($"statistics file does not exist, try to reload {RetryInterval} minutes later"); - _timer.Change(RetryInterval, CachedInterval); + LogWhenEnabled($"statistics file does not exist, try to reload {RetryInterval/60/1000} minutes later"); + _timer.Change(RetryInterval, ChoiceKeptMilliseconds); return; } - _statistics = (from l in File.ReadAllLines(path) - .Skip(1) - let strings = l.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) - let rawData = new - { - ServerName = strings[1], - IPStatus = strings[2], - RoundtripTime = int.Parse(strings[3]) - } - group rawData by rawData.ServerName into server - select new - { - ServerName = server.Key, - data = new StatisticsData - { - SuccessTimes = server.Count(data => IPStatus.Success.ToString().Equals(data.IPStatus)), - TimedOutTimes = server.Count(data => IPStatus.TimedOut.ToString().Equals(data.IPStatus)), - AverageResponse = Convert.ToInt32(server.Average(data => data.RoundtripTime)), - MinResponse = server.Min(data => data.RoundtripTime), - MaxResponse = server.Max(data => data.RoundtripTime) - } - }).ToDictionary(server => server.ServerName, server => server.data); + _rawStatistics = (from l in File.ReadAllLines(path) + .Skip(1) + let strings = l.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries) + let rawData = new StatisticsRawData + { + Timestamp = 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] + } + group rawData by rawData.ServerName into server + select new + { + ServerName = server.Key, + data = server.ToList() + }).ToDictionary(server => server.ServerName, server=> server.data); } catch (Exception e) { @@ -88,15 +86,67 @@ namespace Shadowsocks.Controller.Strategy //return the score by data //server with highest score will be choosen - private static double GetScore(StatisticsData data) + private float GetScore(IEnumerable rawDataList) + { + var config = _controller.StatisticsConfiguration; + if (config.ByIsp) + { + var current = AvailabilityStatistics.GetGeolocationAndIsp().Result; + rawDataList = rawDataList.Where(data => data.Geolocation == current[0].Value || data.Geolocation == AvailabilityStatistics.State.Unknown); + rawDataList = rawDataList.Where(data => data.ISP == current[1].Value || data.ISP == AvailabilityStatistics.State.Unknown); + if (rawDataList.LongCount() == 0) return 0; + } + if (config.ByHourOfDay) + { + var currentHour = DateTime.Now.Hour; + rawDataList = rawDataList.Where(data => + { + DateTime dateTime; + DateTime.TryParseExact(data.Timestamp, AvailabilityStatistics.DateTimePattern, null, + DateTimeStyles.None, out dateTime); + var result = dateTime.Hour.Equals(currentHour); + return result; + }); + if (rawDataList.LongCount() == 0) return 0; + } + var dataList = rawDataList as IList ?? rawDataList.ToList(); + var serverName = dataList[0]?.ServerName; + 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 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) + }; + float factor; + float score = 0; + if (!config.Calculations.TryGetValue("PackageLoss", out factor)) factor = 0; + score += statisticsData.PackageLoss*factor; + if (!config.Calculations.TryGetValue("AverageResponse", out factor)) factor = 0; + score += statisticsData.AverageResponse*factor; + if (!config.Calculations.TryGetValue("MinResponse", out factor)) factor = 0; + score += statisticsData.MinResponse*factor; + if (!config.Calculations.TryGetValue("MaxResponse", out factor)) factor = 0; + score += statisticsData.MaxResponse*factor; + Logging.Debug($"{serverName} {JsonConvert.SerializeObject(statisticsData)}"); + return score; + } + + class StatisticsRawData { - return (double)data.SuccessTimes / (data.SuccessTimes + data.TimedOutTimes); //simply choose min package loss + public string Timestamp; + public string ServerName; + public string ICMPStatus; + public int RoundtripTime; + public string Geolocation; + public string ISP ; } public class StatisticsData { - public int SuccessTimes; - public int TimedOutTimes; + public float PackageLoss; public int AverageResponse; public int MinResponse; public int MaxResponse; @@ -104,7 +154,7 @@ namespace Shadowsocks.Controller.Strategy private void ChooseNewServer(List servers) { - if (_statistics == null || servers.Count == 0) + if (_rawStatistics == null || servers.Count == 0) { return; } @@ -112,17 +162,17 @@ namespace Shadowsocks.Controller.Strategy { var bestResult = (from server in servers let name = server.FriendlyName() - where _statistics.ContainsKey(name) + where _rawStatistics.ContainsKey(name) select new { server, - score = GetScore(_statistics[name]) + score = GetScore(_rawStatistics[name]) } ).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2); if (!_currentServer.Equals(bestResult.server)) //output when enabled { - LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by package loss:{1 - bestResult.score}"); + LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by statistics: score {bestResult.score}"); } _currentServer = bestResult.server; } @@ -160,7 +210,7 @@ namespace Shadowsocks.Controller.Strategy public void ReloadServers() { ChooseNewServer(_controller.GetCurrentConfiguration().configs); - _timer?.Change(0, CachedInterval); + _timer?.Change(0, ChoiceKeptMilliseconds); } public void SetFailure(Server server) diff --git a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs index b427355b..800dc816 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"; - private bool _statisticsEnabled; - private bool _byIsp; - private bool _byHourOfDay; - private int _choiceKeptMinutes; - private int _dataCollectionMinutes; - private int _repeatTimesNum; + private bool _statisticsEnabled = true; + private bool _byIsp = false; + private bool _byHourOfDay = false; + private int _choiceKeptMinutes = 10; + private int _dataCollectionMinutes = 10; + private int _repeatTimesNum = 4; private const string ConfigFile = "statistics-config.json"; @@ -34,7 +34,9 @@ namespace Shadowsocks.Model } catch (FileNotFoundException e) { - return new StatisticsStrategyConfiguration(); + var configuration = new StatisticsStrategyConfiguration(); + Save(configuration); + return configuration; } catch (Exception e) { diff --git a/shadowsocks-csharp/View/CalculationControl.Designer.cs b/shadowsocks-csharp/View/CalculationControl.Designer.cs index 62266759..19b9d222 100644 --- a/shadowsocks-csharp/View/CalculationControl.Designer.cs +++ b/shadowsocks-csharp/View/CalculationControl.Designer.cs @@ -37,6 +37,7 @@ // // factorNum // + this.factorNum.DecimalPlaces = 2; 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, diff --git a/shadowsocks-csharp/View/CalculationControl.cs b/shadowsocks-csharp/View/CalculationControl.cs index 89a2228d..c7f8fdf2 100644 --- a/shadowsocks-csharp/View/CalculationControl.cs +++ b/shadowsocks-csharp/View/CalculationControl.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; +using System.Globalization; using System.Linq; using System.Text; using System.Windows.Forms; @@ -11,13 +12,14 @@ namespace Shadowsocks.View { public partial class CalculationControl : UserControl { - public CalculationControl(string value) + public CalculationControl(string text, float value) { InitializeComponent(); - valueLabel.Text = value; + valueLabel.Text = text; + factorNum.Value = (decimal) value; } public string Value => valueLabel.Text; - public float Factor => float.Parse(factorNum.Text); + public float Factor => (float) factorNum.Value; } } diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index 9c73f320..05c0ba2d 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -29,7 +29,6 @@ namespace Shadowsocks.View private MenuItem enableItem; private MenuItem modeItem; private MenuItem AutoStartupItem; - private MenuItem AvailabilityStatistics; private MenuItem ShareOverLANItem; private MenuItem SeperatorItem; private MenuItem ConfigItem; @@ -179,7 +178,6 @@ namespace Shadowsocks.View }), new MenuItem("-"), this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), - this.AvailabilityStatistics = CreateMenuItem("Availability Statistics", new EventHandler(this.AvailabilityStatisticsItem_Click)), this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)), new MenuItem("-"), CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), @@ -264,7 +262,6 @@ namespace Shadowsocks.View PACModeItem.Checked = !config.global; ShareOverLANItem.Checked = config.shareOverLan; AutoStartupItem.Checked = AutoStartup.Check(); - AvailabilityStatistics.Checked = config.availabilityStatistics; onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; localPACItem.Checked = !onlinePACItem.Checked; UpdatePACItemsEnabledStatus(); @@ -535,11 +532,6 @@ namespace Shadowsocks.View } } - private void AvailabilityStatisticsItem_Click(object sender, EventArgs e) { - AvailabilityStatistics.Checked = !AvailabilityStatistics.Checked; - controller.ToggleAvailabilityStatistics(AvailabilityStatistics.Checked); - } - private void LocalPACItem_Click(object sender, EventArgs e) { if (!localPACItem.Checked) diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs index 801d9821..cc31ddc3 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs @@ -33,9 +33,9 @@ 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.byISPCheckBox = 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.label4 = new System.Windows.Forms.Label(); @@ -57,8 +57,8 @@ this.calculationContainer = new System.Windows.Forms.FlowLayoutPanel(); 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.groupBox1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); this.splitContainer1.Panel1.SuspendLayout(); @@ -75,7 +75,6 @@ this.splitContainer3.Panel1.SuspendLayout(); this.splitContainer3.Panel2.SuspendLayout(); this.splitContainer3.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit(); this.SuspendLayout(); // // StatisticsChart @@ -96,26 +95,20 @@ this.StatisticsChart.Name = "StatisticsChart"; this.StatisticsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.Pastel; series1.ChartArea = "ChartArea"; - series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Area; - series1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(204)))), ((int)(((byte)(204)))), ((int)(((byte)(204))))); + 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.Legend = "ChartLegend"; - series1.Name = "Data Transferred"; + series1.Name = "Package Loss"; + series1.YValuesPerPoint = 4; + series2.BorderWidth = 4; series2.ChartArea = "ChartArea"; - series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bubble; - series2.Color = System.Drawing.Color.FromArgb(((int)(((byte)(221)))), ((int)(((byte)(88)))), ((int)(((byte)(0))))); + 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.Legend = "ChartLegend"; - series2.Name = "Package Loss"; - series2.YValuesPerPoint = 4; - series3.BorderWidth = 4; - series3.ChartArea = "ChartArea"; - series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; - series3.Color = System.Drawing.Color.FromArgb(((int)(((byte)(155)))), ((int)(((byte)(77)))), ((int)(((byte)(150))))); - series3.Legend = "ChartLegend"; - series3.Name = "Ping"; + series2.Name = "Ping"; this.StatisticsChart.Series.Add(series1); this.StatisticsChart.Series.Add(series2); - this.StatisticsChart.Series.Add(series3); - this.StatisticsChart.Size = new System.Drawing.Size(951, 222); + this.StatisticsChart.Size = new System.Drawing.Size(1061, 314); this.StatisticsChart.TabIndex = 2; // // byISPCheckBox @@ -130,6 +123,10 @@ this.byISPCheckBox.Text = "By ISP/geolocation"; this.byISPCheckBox.UseVisualStyleBackColor = true; // + // bindingConfiguration + // + this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration); + // // label2 // this.label2.AutoSize = true; @@ -153,7 +150,7 @@ // label4 // this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(12, 226); + this.label4.Location = new System.Drawing.Point(7, 226); this.label4.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(51, 28); @@ -165,7 +162,7 @@ this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.groupBox1.Controls.Add(this.radioButton2); this.groupBox1.Controls.Add(this.radioButton1); - this.groupBox1.Location = new System.Drawing.Point(698, 7); + this.groupBox1.Location = new System.Drawing.Point(808, 81); this.groupBox1.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.groupBox1.Name = "groupBox1"; this.groupBox1.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10); @@ -177,6 +174,7 @@ // radioButton2 // this.radioButton2.AutoSize = true; + this.radioButton2.Checked = true; this.radioButton2.Location = new System.Drawing.Point(10, 63); this.radioButton2.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.radioButton2.Name = "radioButton2"; @@ -194,7 +192,6 @@ this.radioButton1.Name = "radioButton1"; this.radioButton1.Size = new System.Drawing.Size(70, 32); this.radioButton1.TabIndex = 0; - this.radioButton1.TabStop = true; this.radioButton1.Text = "24h"; this.radioButton1.UseVisualStyleBackColor = true; // @@ -217,8 +214,8 @@ this.splitContainer1.Panel2.Controls.Add(this.OKButton); this.splitContainer1.Panel2.Controls.Add(this.groupBox1); this.splitContainer1.Panel2.Controls.Add(this.StatisticsChart); - this.splitContainer1.Size = new System.Drawing.Size(951, 458); - this.splitContainer1.SplitterDistance = 226; + this.splitContainer1.Size = new System.Drawing.Size(1061, 637); + this.splitContainer1.SplitterDistance = 313; this.splitContainer1.SplitterWidth = 10; this.splitContainer1.TabIndex = 12; // @@ -249,7 +246,7 @@ // splitContainer2.Panel2 // this.splitContainer2.Panel2.Controls.Add(this.splitContainer3); - this.splitContainer2.Size = new System.Drawing.Size(951, 226); + this.splitContainer2.Size = new System.Drawing.Size(1061, 313); this.splitContainer2.SplitterDistance = 365; this.splitContainer2.SplitterWidth = 5; this.splitContainer2.TabIndex = 7; @@ -282,7 +279,7 @@ 0, 0, 0}); - this.dataCollectionMinutesNum.Location = new System.Drawing.Point(161, 188); + this.dataCollectionMinutesNum.Location = new System.Drawing.Point(161, 179); this.dataCollectionMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.dataCollectionMinutesNum.Maximum = new decimal(new int[] { 120, @@ -298,7 +295,7 @@ this.dataCollectionMinutesNum.Size = new System.Drawing.Size(92, 34); this.dataCollectionMinutesNum.TabIndex = 18; this.dataCollectionMinutesNum.Value = new decimal(new int[] { - 5, + 10, 0, 0, 0}); @@ -339,7 +336,7 @@ this.choiceKeptMinutesNum.Size = new System.Drawing.Size(92, 34); this.choiceKeptMinutesNum.TabIndex = 16; this.choiceKeptMinutesNum.Value = new decimal(new int[] { - 5, + 10, 0, 0, 0}); @@ -359,7 +356,7 @@ // repeatTimesNum // this.repeatTimesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "RepeatTimesNum", true)); - this.repeatTimesNum.Location = new System.Drawing.Point(72, 223); + this.repeatTimesNum.Location = new System.Drawing.Point(66, 224); this.repeatTimesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.repeatTimesNum.Maximum = new decimal(new int[] { 10, @@ -378,7 +375,7 @@ // label6 // this.label6.AutoSize = true; - this.label6.Location = new System.Drawing.Point(171, 226); + this.label6.Location = new System.Drawing.Point(163, 226); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(184, 28); this.label6.TabIndex = 13; @@ -401,7 +398,7 @@ // splitContainer3.Panel2 // this.splitContainer3.Panel2.Controls.Add(this.calculationContainer); - this.splitContainer3.Size = new System.Drawing.Size(581, 226); + this.splitContainer3.Size = new System.Drawing.Size(691, 313); this.splitContainer3.SplitterDistance = 46; this.splitContainer3.SplitterWidth = 10; this.splitContainer3.TabIndex = 6; @@ -423,13 +420,13 @@ 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(581, 170); + this.calculationContainer.Size = new System.Drawing.Size(691, 257); this.calculationContainer.TabIndex = 1; // // 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(844, 166); + this.CancelButton.Location = new System.Drawing.Point(954, 240); this.CancelButton.Name = "CancelButton"; this.CancelButton.Size = new System.Drawing.Size(93, 43); this.CancelButton.TabIndex = 5; @@ -440,7 +437,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(745, 166); + this.OKButton.Location = new System.Drawing.Point(855, 240); this.OKButton.Name = "OKButton"; this.OKButton.Size = new System.Drawing.Size(93, 43); this.OKButton.TabIndex = 4; @@ -448,16 +445,12 @@ 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(11F, 28F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoSize = true; - this.ClientSize = new System.Drawing.Size(951, 458); + this.ClientSize = new System.Drawing.Size(1061, 637); this.Controls.Add(this.splitContainer1); this.Font = new System.Drawing.Font("Segoe UI", 10F); this.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); @@ -465,6 +458,7 @@ this.Name = "StatisticsStrategyConfigurationForm"; this.Text = "StatisticsStrategyConfigurationForm"; ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit(); this.groupBox1.ResumeLayout(false); this.groupBox1.PerformLayout(); this.splitContainer1.Panel1.ResumeLayout(false); @@ -484,7 +478,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 587153c7..a4cb18a2 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs @@ -37,7 +37,7 @@ namespace Shadowsocks.View bindingConfiguration.Add(_configuration); foreach (var kv in _configuration.Calculations) { - var calculation = new CalculationControl(kv.Key); + var calculation = new CalculationControl(kv.Key, kv.Value); calculationContainer.Controls.Add(calculation); } } @@ -54,6 +54,7 @@ namespace Shadowsocks.View _configuration.Calculations[calculation.Value] = calculation.Factor; } _controller?.SaveStrategyConfigurations(_configuration); + _controller?.UpdateStatisticsConfiguration(StatisticsEnabledCheckBox.Checked); Close(); } } diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx index fa7d769a..5f9d5c44 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx @@ -120,4 +120,7 @@ 1, 30 + + 63 + \ No newline at end of file