Browse Source

core functions finished

tags/3.0
icylogic 10 years ago
parent
commit
e45dd8b695
10 changed files with 192 additions and 131 deletions
  1. +39
    -20
      shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
  2. +7
    -9
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  3. +94
    -44
      shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs
  4. +9
    -7
      shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs
  5. +1
    -0
      shadowsocks-csharp/View/CalculationControl.Designer.cs
  6. +5
    -3
      shadowsocks-csharp/View/CalculationControl.cs
  7. +0
    -8
      shadowsocks-csharp/View/MenuViewController.cs
  8. +32
    -39
      shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs
  9. +2
    -1
      shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs
  10. +3
    -0
      shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx

+ 39
- 20
shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs View File

@@ -6,10 +6,11 @@ using System.Net;
using SimpleJson; using SimpleJson;
using System.Net.Http; using System.Net.Http;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using Shadowsocks.Model; using Shadowsocks.Model;
using SimpleJson = SimpleJson.SimpleJson;
using Shadowsocks.Util; using Shadowsocks.Util;
using Timer = System.Threading.Timer; using Timer = System.Threading.Timer;


@@ -20,35 +21,43 @@ namespace Shadowsocks.Controller


internal class AvailabilityStatistics internal class AvailabilityStatistics
{ {
public static readonly string DateTimePattern = "yyyy-MM-dd HH:mm:ss";
private const string StatisticsFilesName = "shadowsocks.availability.csv"; private const string StatisticsFilesName = "shadowsocks.availability.csv";
private const string Delimiter = ","; private const string Delimiter = ",";
private const int Timeout = 500; 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 Timer _timer;
private State _state; private State _state;
private List<Server> _servers; private List<Server> _servers;
private StatisticsStrategyConfiguration _config;


public static string AvailabilityStatisticsFile; public static string AvailabilityStatisticsFile;


//static constructor to initialize every public static fields before refereced //static constructor to initialize every public static fields before refereced
static AvailabilityStatistics() static AvailabilityStatistics()
{ {
string temppath = Utils.GetTempPath();
var temppath = Utils.GetTempPath();
AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName); 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 try
{ {
if (enabled)
if (config.StatisticsEnabled)
{ {
if (_timer?.Change(0, Interval) == null)
if (_timer?.Change(DelayBeforeStart, _interval) == null)
{ {
_state = new State(); _state = new State();
_timer = new Timer(Evaluate, _state, 0, Interval);
_timer = new Timer(Evaluate, _state, DelayBeforeStart, _interval);
} }
} }
else else
@@ -66,7 +75,7 @@ namespace Shadowsocks.Controller


//hardcode //hardcode
//TODO: backup reliable isp&geolocation provider or a local database is required //TODO: backup reliable isp&geolocation provider or a local database is required
private static async Task<DataList> getGeolocationAndISP()
public static async Task<DataList> GetGeolocationAndIsp()
{ {
Logging.Debug("Retrive information of geolocation and isp"); Logging.Debug("Retrive information of geolocation and isp");
const string api = "http://ip-api.com/json"; const string api = "http://ip-api.com/json";
@@ -92,24 +101,25 @@ namespace Shadowsocks.Controller
string isp = obj["isp"]; string isp = obj["isp"];
string regionName = obj["regionName"]; string regionName = obj["regionName"];
if (country == null || city == null || isp == null || regionName == null) return ret; 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); ret[1] = new DataUnit(State.ISP, isp);
return ret; return ret;
} }


private static async Task<List<DataList>> ICMPTest(Server server)
private async Task<List<DataList>> ICMPTest(Server server)
{ {
Logging.Debug("eveluating " + server.FriendlyName());
Logging.Debug("Ping " + server.FriendlyName());
if (server.server == "") return null; if (server.server == "") return null;
var IP = Dns.GetHostAddresses(server.server).First(ip => ip.AddressFamily == AddressFamily.InterNetwork);
var ping = new Ping(); var ping = new Ping();
var ret = new List<DataList>(); var ret = new List<DataList>();
foreach ( 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 //ICMP echo. we can also set options and special bytes
try try
{ {
var reply = await ping.SendTaskAsync(server.server, Timeout);
var reply = ping.Send(IP, Timeout);
ret.Add(new List<KeyValuePair<string, string>> ret.Add(new List<KeyValuePair<string, string>>
{ {
new KeyValuePair<string, string>("Timestamp", timestamp), new KeyValuePair<string, string>("Timestamp", timestamp),
@@ -118,6 +128,8 @@ namespace Shadowsocks.Controller
new KeyValuePair<string, string>("RoundtripTime", reply?.RoundtripTime.ToString()) new KeyValuePair<string, string>("RoundtripTime", reply?.RoundtripTime.ToString())
//new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply //new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply
}); });
Thread.Sleep(new Random().Next() % Timeout);
//Do ICMPTest in a random frequency
} }
catch (Exception e) catch (Exception e)
{ {
@@ -130,7 +142,7 @@ namespace Shadowsocks.Controller


private async void Evaluate(object obj) private async void Evaluate(object obj)
{ {
var geolocationAndIsp = getGeolocationAndISP();
var geolocationAndIsp = GetGeolocationAndIsp();
foreach (var dataLists in await TaskEx.WhenAll(_servers.Select(ICMPTest))) foreach (var dataLists in await TaskEx.WhenAll(_servers.Select(ICMPTest)))
{ {
if (dataLists == null) continue; if (dataLists == null) continue;
@@ -156,16 +168,23 @@ namespace Shadowsocks.Controller
{ {
lines = new[] {dataLine}; 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; _servers = config.configs;
} }


private class State
public class State
{ {
public DataList dataList = new DataList(); public DataList dataList = new DataList();
public const string Geolocation = "Geolocation"; public const string Geolocation = "Geolocation";


+ 7
- 9
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -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) public void SavePACUrl(string pacUrl)
@@ -315,9 +313,9 @@ namespace Shadowsocks.Controller
if (_availabilityStatics == null) if (_availabilityStatics == null)
{ {
_availabilityStatics = new AvailabilityStatistics();
_availabilityStatics = new AvailabilityStatistics(_config, StatisticsConfiguration);
} }
_availabilityStatics.UpdateConfiguration(_config);
_availabilityStatics.UpdateConfiguration(_config, StatisticsConfiguration);
if (_listener != null) if (_listener != null)
{ {


+ 94
- 44
shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@@ -7,19 +8,25 @@ using System.Text;
using Shadowsocks.Model; using Shadowsocks.Model;
using System.IO; using System.IO;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Threading;
using System.Windows.Forms;
using Newtonsoft.Json;
using Shadowsocks.Model; using Shadowsocks.Model;
using Timer = System.Threading.Timer;


namespace Shadowsocks.Controller.Strategy namespace Shadowsocks.Controller.Strategy
{ {
using DataUnit = KeyValuePair<string, string>;
using DataList = List<KeyValuePair<string, string>>;

class StatisticsStrategy : IStrategy class StatisticsStrategy : IStrategy
{ {
private readonly ShadowsocksController _controller; private readonly ShadowsocksController _controller;
private Server _currentServer; private Server _currentServer;
private readonly Timer _timer; private readonly Timer _timer;
private Dictionary<string, StatisticsData> _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<string, List<StatisticsRawData>> _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) public StatisticsStrategy(ShadowsocksController controller)
{ {
@@ -38,13 +45,6 @@ namespace Shadowsocks.Controller.Strategy
ChooseNewServer(servers); ChooseNewServer(servers);
} }


/*
return a dict:
{
'ServerFriendlyName1':StatisticsData,
'ServerFriendlyName2':...
}
*/
private void LoadStatistics() private void LoadStatistics()
{ {
try try
@@ -53,32 +53,30 @@ namespace Shadowsocks.Controller.Strategy
Logging.Debug($"loading statistics from {path}"); Logging.Debug($"loading statistics from {path}");
if (!File.Exists(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; 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) catch (Exception e)
{ {
@@ -88,15 +86,67 @@ namespace Shadowsocks.Controller.Strategy


//return the score by data //return the score by data
//server with highest score will be choosen //server with highest score will be choosen
private static double GetScore(StatisticsData data)
private float GetScore(IEnumerable<StatisticsRawData> 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<StatisticsRawData> ?? 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 class StatisticsData
{ {
public int SuccessTimes;
public int TimedOutTimes;
public float PackageLoss;
public int AverageResponse; public int AverageResponse;
public int MinResponse; public int MinResponse;
public int MaxResponse; public int MaxResponse;
@@ -104,7 +154,7 @@ namespace Shadowsocks.Controller.Strategy


private void ChooseNewServer(List<Server> servers) private void ChooseNewServer(List<Server> servers)
{ {
if (_statistics == null || servers.Count == 0)
if (_rawStatistics == null || servers.Count == 0)
{ {
return; return;
} }
@@ -112,17 +162,17 @@ namespace Shadowsocks.Controller.Strategy
{ {
var bestResult = (from server in servers var bestResult = (from server in servers
let name = server.FriendlyName() let name = server.FriendlyName()
where _statistics.ContainsKey(name)
where _rawStatistics.ContainsKey(name)
select new select new
{ {
server, server,
score = GetScore(_statistics[name])
score = GetScore(_rawStatistics[name])
} }
).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2); ).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2);


if (!_currentServer.Equals(bestResult.server)) //output when enabled 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; _currentServer = bestResult.server;
} }
@@ -160,7 +210,7 @@ namespace Shadowsocks.Controller.Strategy
public void ReloadServers() public void ReloadServers()
{ {
ChooseNewServer(_controller.GetCurrentConfiguration().configs); ChooseNewServer(_controller.GetCurrentConfiguration().configs);
_timer?.Change(0, CachedInterval);
_timer?.Change(0, ChoiceKeptMilliseconds);
} }


public void SetFailure(Server server) public void SetFailure(Server server)


+ 9
- 7
shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs View File

@@ -14,12 +14,12 @@ namespace Shadowsocks.Model
public class StatisticsStrategyConfiguration public class StatisticsStrategyConfiguration
{ {
public static readonly string ID = "com.shadowsocks.strategy.statistics"; 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"; private const string ConfigFile = "statistics-config.json";
@@ -34,7 +34,9 @@ namespace Shadowsocks.Model
} }
catch (FileNotFoundException e) catch (FileNotFoundException e)
{ {
return new StatisticsStrategyConfiguration();
var configuration = new StatisticsStrategyConfiguration();
Save(configuration);
return configuration;
} }
catch (Exception e) catch (Exception e)
{ {


+ 1
- 0
shadowsocks-csharp/View/CalculationControl.Designer.cs View File

@@ -37,6 +37,7 @@
// //
// factorNum // 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.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[] { this.factorNum.Increment = new decimal(new int[] {
1, 1,


+ 5
- 3
shadowsocks-csharp/View/CalculationControl.cs View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.Data; using System.Data;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
@@ -11,13 +12,14 @@ namespace Shadowsocks.View
{ {
public partial class CalculationControl : UserControl public partial class CalculationControl : UserControl
{ {
public CalculationControl(string value)
public CalculationControl(string text, float value)
{ {
InitializeComponent(); InitializeComponent();
valueLabel.Text = value;
valueLabel.Text = text;
factorNum.Value = (decimal) value;
} }


public string Value => valueLabel.Text; public string Value => valueLabel.Text;
public float Factor => float.Parse(factorNum.Text);
public float Factor => (float) factorNum.Value;
} }
} }

+ 0
- 8
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -29,7 +29,6 @@ namespace Shadowsocks.View
private MenuItem enableItem; private MenuItem enableItem;
private MenuItem modeItem; private MenuItem modeItem;
private MenuItem AutoStartupItem; private MenuItem AutoStartupItem;
private MenuItem AvailabilityStatistics;
private MenuItem ShareOverLANItem; private MenuItem ShareOverLANItem;
private MenuItem SeperatorItem; private MenuItem SeperatorItem;
private MenuItem ConfigItem; private MenuItem ConfigItem;
@@ -179,7 +178,6 @@ namespace Shadowsocks.View
}), }),
new MenuItem("-"), new MenuItem("-"),
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), 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)), this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)),
new MenuItem("-"), new MenuItem("-"),
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
@@ -264,7 +262,6 @@ namespace Shadowsocks.View
PACModeItem.Checked = !config.global; PACModeItem.Checked = !config.global;
ShareOverLANItem.Checked = config.shareOverLan; ShareOverLANItem.Checked = config.shareOverLan;
AutoStartupItem.Checked = AutoStartup.Check(); AutoStartupItem.Checked = AutoStartup.Check();
AvailabilityStatistics.Checked = config.availabilityStatistics;
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac;
localPACItem.Checked = !onlinePACItem.Checked; localPACItem.Checked = !onlinePACItem.Checked;
UpdatePACItemsEnabledStatus(); 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) private void LocalPACItem_Click(object sender, EventArgs e)
{ {
if (!localPACItem.Checked) if (!localPACItem.Checked)


+ 32
- 39
shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs View File

@@ -33,9 +33,9 @@
System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend(); 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 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 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.StatisticsChart = new System.Windows.Forms.DataVisualization.Charting.Chart();
this.byISPCheckBox = new System.Windows.Forms.CheckBox(); this.byISPCheckBox = new System.Windows.Forms.CheckBox();
this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components);
this.label2 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label();
@@ -57,8 +57,8 @@
this.calculationContainer = new System.Windows.Forms.FlowLayoutPanel(); this.calculationContainer = new System.Windows.Forms.FlowLayoutPanel();
this.CancelButton = new System.Windows.Forms.Button(); this.CancelButton = new System.Windows.Forms.Button();
this.OKButton = 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.StatisticsChart)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit();
this.groupBox1.SuspendLayout(); this.groupBox1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout(); this.splitContainer1.Panel1.SuspendLayout();
@@ -75,7 +75,6 @@
this.splitContainer3.Panel1.SuspendLayout(); this.splitContainer3.Panel1.SuspendLayout();
this.splitContainer3.Panel2.SuspendLayout(); this.splitContainer3.Panel2.SuspendLayout();
this.splitContainer3.SuspendLayout(); this.splitContainer3.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit();
this.SuspendLayout(); this.SuspendLayout();
// //
// StatisticsChart // StatisticsChart
@@ -96,26 +95,20 @@
this.StatisticsChart.Name = "StatisticsChart"; this.StatisticsChart.Name = "StatisticsChart";
this.StatisticsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.Pastel; this.StatisticsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.Pastel;
series1.ChartArea = "ChartArea"; 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.Legend = "ChartLegend";
series1.Name = "Data Transferred";
series1.Name = "Package Loss";
series1.YValuesPerPoint = 4;
series2.BorderWidth = 4;
series2.ChartArea = "ChartArea"; 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.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(series1);
this.StatisticsChart.Series.Add(series2); 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; this.StatisticsChart.TabIndex = 2;
// //
// byISPCheckBox // byISPCheckBox
@@ -130,6 +123,10 @@
this.byISPCheckBox.Text = "By ISP/geolocation"; this.byISPCheckBox.Text = "By ISP/geolocation";
this.byISPCheckBox.UseVisualStyleBackColor = true; this.byISPCheckBox.UseVisualStyleBackColor = true;
// //
// bindingConfiguration
//
this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration);
//
// label2 // label2
// //
this.label2.AutoSize = true; this.label2.AutoSize = true;
@@ -153,7 +150,7 @@
// label4 // label4
// //
this.label4.AutoSize = true; 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.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
this.label4.Name = "label4"; this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(51, 28); 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.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.radioButton2);
this.groupBox1.Controls.Add(this.radioButton1); 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.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.groupBox1.Name = "groupBox1"; this.groupBox1.Name = "groupBox1";
this.groupBox1.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10); this.groupBox1.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10);
@@ -177,6 +174,7 @@
// radioButton2 // radioButton2
// //
this.radioButton2.AutoSize = true; this.radioButton2.AutoSize = true;
this.radioButton2.Checked = true;
this.radioButton2.Location = new System.Drawing.Point(10, 63); this.radioButton2.Location = new System.Drawing.Point(10, 63);
this.radioButton2.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.radioButton2.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.radioButton2.Name = "radioButton2"; this.radioButton2.Name = "radioButton2";
@@ -194,7 +192,6 @@
this.radioButton1.Name = "radioButton1"; this.radioButton1.Name = "radioButton1";
this.radioButton1.Size = new System.Drawing.Size(70, 32); this.radioButton1.Size = new System.Drawing.Size(70, 32);
this.radioButton1.TabIndex = 0; this.radioButton1.TabIndex = 0;
this.radioButton1.TabStop = true;
this.radioButton1.Text = "24h"; this.radioButton1.Text = "24h";
this.radioButton1.UseVisualStyleBackColor = true; this.radioButton1.UseVisualStyleBackColor = true;
// //
@@ -217,8 +214,8 @@
this.splitContainer1.Panel2.Controls.Add(this.OKButton); this.splitContainer1.Panel2.Controls.Add(this.OKButton);
this.splitContainer1.Panel2.Controls.Add(this.groupBox1); this.splitContainer1.Panel2.Controls.Add(this.groupBox1);
this.splitContainer1.Panel2.Controls.Add(this.StatisticsChart); 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.SplitterWidth = 10;
this.splitContainer1.TabIndex = 12; this.splitContainer1.TabIndex = 12;
// //
@@ -249,7 +246,7 @@
// splitContainer2.Panel2 // splitContainer2.Panel2
// //
this.splitContainer2.Panel2.Controls.Add(this.splitContainer3); 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.SplitterDistance = 365;
this.splitContainer2.SplitterWidth = 5; this.splitContainer2.SplitterWidth = 5;
this.splitContainer2.TabIndex = 7; this.splitContainer2.TabIndex = 7;
@@ -282,7 +279,7 @@
0, 0,
0, 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.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.dataCollectionMinutesNum.Maximum = new decimal(new int[] { this.dataCollectionMinutesNum.Maximum = new decimal(new int[] {
120, 120,
@@ -298,7 +295,7 @@
this.dataCollectionMinutesNum.Size = new System.Drawing.Size(92, 34); this.dataCollectionMinutesNum.Size = new System.Drawing.Size(92, 34);
this.dataCollectionMinutesNum.TabIndex = 18; this.dataCollectionMinutesNum.TabIndex = 18;
this.dataCollectionMinutesNum.Value = new decimal(new int[] { this.dataCollectionMinutesNum.Value = new decimal(new int[] {
5,
10,
0, 0,
0, 0,
0}); 0});
@@ -339,7 +336,7 @@
this.choiceKeptMinutesNum.Size = new System.Drawing.Size(92, 34); this.choiceKeptMinutesNum.Size = new System.Drawing.Size(92, 34);
this.choiceKeptMinutesNum.TabIndex = 16; this.choiceKeptMinutesNum.TabIndex = 16;
this.choiceKeptMinutesNum.Value = new decimal(new int[] { this.choiceKeptMinutesNum.Value = new decimal(new int[] {
5,
10,
0, 0,
0, 0,
0}); 0});
@@ -359,7 +356,7 @@
// repeatTimesNum // repeatTimesNum
// //
this.repeatTimesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "RepeatTimesNum", true)); 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.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.repeatTimesNum.Maximum = new decimal(new int[] { this.repeatTimesNum.Maximum = new decimal(new int[] {
10, 10,
@@ -378,7 +375,7 @@
// label6 // label6
// //
this.label6.AutoSize = true; 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.Name = "label6";
this.label6.Size = new System.Drawing.Size(184, 28); this.label6.Size = new System.Drawing.Size(184, 28);
this.label6.TabIndex = 13; this.label6.TabIndex = 13;
@@ -401,7 +398,7 @@
// splitContainer3.Panel2 // splitContainer3.Panel2
// //
this.splitContainer3.Panel2.Controls.Add(this.calculationContainer); 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.SplitterDistance = 46;
this.splitContainer3.SplitterWidth = 10; this.splitContainer3.SplitterWidth = 10;
this.splitContainer3.TabIndex = 6; this.splitContainer3.TabIndex = 6;
@@ -423,13 +420,13 @@
this.calculationContainer.Location = new System.Drawing.Point(0, 0); this.calculationContainer.Location = new System.Drawing.Point(0, 0);
this.calculationContainer.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.calculationContainer.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
this.calculationContainer.Name = "calculationContainer"; 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; this.calculationContainer.TabIndex = 1;
// //
// CancelButton // CancelButton
// //
this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 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.Name = "CancelButton";
this.CancelButton.Size = new System.Drawing.Size(93, 43); this.CancelButton.Size = new System.Drawing.Size(93, 43);
this.CancelButton.TabIndex = 5; this.CancelButton.TabIndex = 5;
@@ -440,7 +437,7 @@
// OKButton // OKButton
// //
this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 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.Name = "OKButton";
this.OKButton.Size = new System.Drawing.Size(93, 43); this.OKButton.Size = new System.Drawing.Size(93, 43);
this.OKButton.TabIndex = 4; this.OKButton.TabIndex = 4;
@@ -448,16 +445,12 @@
this.OKButton.UseVisualStyleBackColor = true; this.OKButton.UseVisualStyleBackColor = true;
this.OKButton.Click += new System.EventHandler(this.OKButton_Click); this.OKButton.Click += new System.EventHandler(this.OKButton_Click);
// //
// bindingConfiguration
//
this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration);
//
// StatisticsStrategyConfigurationForm // StatisticsStrategyConfigurationForm
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(11F, 28F); this.AutoScaleDimensions = new System.Drawing.SizeF(11F, 28F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true; 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.Controls.Add(this.splitContainer1);
this.Font = new System.Drawing.Font("Segoe UI", 10F); this.Font = new System.Drawing.Font("Segoe UI", 10F);
this.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); this.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10);
@@ -465,6 +458,7 @@
this.Name = "StatisticsStrategyConfigurationForm"; this.Name = "StatisticsStrategyConfigurationForm";
this.Text = "StatisticsStrategyConfigurationForm"; this.Text = "StatisticsStrategyConfigurationForm";
((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit();
this.groupBox1.ResumeLayout(false); this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout(); this.groupBox1.PerformLayout();
this.splitContainer1.Panel1.ResumeLayout(false); this.splitContainer1.Panel1.ResumeLayout(false);
@@ -484,7 +478,6 @@
this.splitContainer3.Panel2.ResumeLayout(false); this.splitContainer3.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit();
this.splitContainer3.ResumeLayout(false); this.splitContainer3.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit();
this.ResumeLayout(false); this.ResumeLayout(false);


} }


+ 2
- 1
shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs View File

@@ -37,7 +37,7 @@ namespace Shadowsocks.View
bindingConfiguration.Add(_configuration); bindingConfiguration.Add(_configuration);
foreach (var kv in _configuration.Calculations) foreach (var kv in _configuration.Calculations)
{ {
var calculation = new CalculationControl(kv.Key);
var calculation = new CalculationControl(kv.Key, kv.Value);
calculationContainer.Controls.Add(calculation); calculationContainer.Controls.Add(calculation);
} }
} }
@@ -54,6 +54,7 @@ namespace Shadowsocks.View
_configuration.Calculations[calculation.Value] = calculation.Factor; _configuration.Calculations[calculation.Value] = calculation.Factor;
} }
_controller?.SaveStrategyConfigurations(_configuration); _controller?.SaveStrategyConfigurations(_configuration);
_controller?.UpdateStatisticsConfiguration(StatisticsEnabledCheckBox.Checked);
Close(); Close();
} }
} }


+ 3
- 0
shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx View File

@@ -120,4 +120,7 @@
<metadata name="bindingConfiguration.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="bindingConfiguration.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1, 30</value> <value>1, 30</value>
</metadata> </metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>63</value>
</metadata>
</root> </root>

Loading…
Cancel
Save