Browse Source

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.
tags/3.2
icylogic 9 years ago
parent
commit
a99fef37d3
10 changed files with 224 additions and 148 deletions
  1. +106
    -52
      shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
  2. +7
    -10
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  3. +19
    -10
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  4. +44
    -31
      shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs
  5. +1
    -1
      shadowsocks-csharp/Data/cn.txt
  6. +23
    -8
      shadowsocks-csharp/Model/StatisticsRecord.cs
  7. +11
    -13
      shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs
  8. +1
    -11
      shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs
  9. +2
    -2
      shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx
  10. +10
    -10
      shadowsocks-csharp/shadowsocks-csharp.csproj

+ 106
- 52
shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs View File

@@ -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<int> _latencyRecords;
private readonly ConcurrentDictionary<string, List<int>> _latencyRecords = new ConcurrentDictionary<string, List<int>>();
//speed in KiB/s
private long _lastInboundCounter;
private List<int> _inboundSpeedRecords;
private long _lastOutboundCounter;
private List<int> _outboundSpeedRecords;
private readonly ConcurrentDictionary<string, long> _inboundCounter = new ConcurrentDictionary<string, long>();
private readonly ConcurrentDictionary<string, long> _lastInboundCounter = new ConcurrentDictionary<string, long>();
private readonly ConcurrentDictionary<string, List<int>> _inboundSpeedRecords = new ConcurrentDictionary<string, List<int>>();
private readonly ConcurrentDictionary<string, long> _outboundCounter = new ConcurrentDictionary<string, long>();
private readonly ConcurrentDictionary<string, long> _lastOutboundCounter = new ConcurrentDictionary<string, long>();
private readonly ConcurrentDictionary<string, List<int>> _outboundSpeedRecords = new ConcurrentDictionary<string, List<int>>();

//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<int> {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<int> {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<ICMPResult> ICMPTest(Server server)
@@ -161,66 +170,75 @@ namespace Shadowsocks.Controller

private void Reset()
{
_inboundSpeedRecords = new List<int>();
_outboundSpeedRecords = new List<int>();
_latencyRecords = new List<int>();
_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<string, StatisticsRecord>();

if (!Config.Ping)
foreach (var server in _controller.GetCurrentConfiguration().configs)
{
AppendRecord(CurrentServer, currentServerRecord);
return;
var id = server.Identifier();
List<int> inboundSpeedRecords = null;
List<int> outboundSpeedRecords = null;
List<int> 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<StatisticsRecord> records;
if (!RawStatistics.TryGetValue(server.Identifier(), out records))
if (!RawStatistics.TryGetValue(serverIdentifier, out records))
{
records = new List<StatisticsRecord>();
}
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<int> records;
_latencyRecords.TryGetValue(server.Identifier(), out records);
if (records == null)
{
records = new List<int>();
}
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;
}
}
}

+ 7
- 10
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -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);


+ 19
- 10
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -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);
}
}
}
}

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

@@ -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<StatisticsRecord> records)
{
var config = _controller.StatisticsConfiguration;
List<StatisticsRecord> 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)
{


+ 1
- 1
shadowsocks-csharp/Data/cn.txt View File

@@ -30,7 +30,7 @@ Quit=退出
Edit Servers=编辑服务器
Load Balance=负载均衡
High Availability=高可用
Choose By Total Package Loss=累计丢包率
Choose by statistics=根据统计

# Config Form



+ 23
- 8
shadowsocks-csharp/Model/StatisticsRecord.cs View File

@@ -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<int> inboundSpeedRecords, IEnumerable<int> outboundSpeedRecords, IEnumerable<int> latencyRecords)
public StatisticsRecord(string identifier, ICollection<int> inboundSpeedRecords, ICollection<int> outboundSpeedRecords, ICollection<int> 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<int?> responseRecords)
public StatisticsRecord(string identifier, ICollection<int?> responseRecords)
{
ServerName = identifier;
setResponse(responseRecords);
ServerIdentifier = identifier;
SetResponse(responseRecords);
}

public void setResponse(IEnumerable<int?> responseRecords)
public void SetResponse(ICollection<int?> 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;
}
}
}

+ 11
- 13
shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs View File

@@ -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);

}


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

@@ -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("?");
}
}
}

+ 2
- 2
shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx View File

@@ -118,12 +118,12 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="bindingConfiguration.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>1, 30</value>
<value>4, 5</value>
</metadata>
<metadata name="CalculatinTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>238, 6</value>
</metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>37</value>
<value>191</value>
</metadata>
</root>

+ 10
- 10
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -53,13 +53,14 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<Optimize>false</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
<DebugSymbols>true</DebugSymbols>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
@@ -341,13 +342,12 @@
<Files Output="false" Required="true" ParameterType="Microsoft.Build.Framework.ITaskItem[]" />
</ParameterGroup>
<Task Evaluate="true">
<Reference xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Include="System.Xml" />
<Reference xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Include="System.Xml.Linq" />
<Using xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Namespace="System" />
<Using xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Namespace="System.IO" />
<Using xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Namespace="System.Xml.Linq" />
<Code xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Type="Fragment" Language="cs">
<![CDATA[
<Reference xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Include="System.Xml" />
<Reference xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Include="System.Xml.Linq" />
<Using xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Namespace="System" />
<Using xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Namespace="System.IO" />
<Using xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Namespace="System.Xml.Linq" />
<Code xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Type="Fragment" Language="cs"><![CDATA[
var config = XElement.Load(Config.ItemSpec).Elements("Costura").FirstOrDefault();

if (config == null) return true;
@@ -366,8 +366,8 @@ var filesToCleanup = Files.Select(f => f.ItemSpec).Where(f => !excludedAssemblie

foreach (var item in filesToCleanup)
File.Delete(item);
]]>
</Code></Task>
]]></Code>
</Task>
</UsingTask>
<Target Name="CleanReferenceCopyLocalPaths" AfterTargets="AfterBuild;NonWinFodyTarget">
<CosturaCleanup Config="FodyWeavers.xml" Files="@(ReferenceCopyLocalPaths->'$(OutDir)%(DestinationSubDirectory)%(Filename)%(Extension)')" />


Loading…
Cancel
Save