diff --git a/.gitignore b/.gitignore
index 82c89bb5..4a64d6be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,9 @@ shadowsocks-csharp/shadowsocks-csharp.csproj.user
TestResults
*.suo
+shadowsocks-csharp/3rd/*
+!shadowsocks-csharp/3rd/zxing/
+!shadowsocks-csharp/3rd/SimpleJson.cs
+packages/*
+
+shadowsocks-csharp.sln.DotSettings.user
diff --git a/nuget.config b/nuget.config
new file mode 100644
index 00000000..57b36f04
--- /dev/null
+++ b/nuget.config
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
index 08125e92..49820f63 100644
--- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
+++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
@@ -1,50 +1,72 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
+using System.Net;
+using System.Net.Http;
using System.Net.NetworkInformation;
+using System.Net.Sockets;
using System.Threading;
+using System.Threading.Tasks;
using Shadowsocks.Model;
-using System.Reflection;
using Shadowsocks.Util;
namespace Shadowsocks.Controller
{
- class AvailabilityStatistics
+ using DataUnit = KeyValuePair;
+ using DataList = List>;
+
+ using Statistics = Dictionary>;
+
+ public class AvailabilityStatistics
{
- private static readonly string StatisticsFilesName = "shadowsocks.availability.csv";
- private static readonly string Delimiter = ",";
- private static readonly int Timeout = 500;
- private static readonly int Repeat = 4; //repeat times every evaluation
- private static readonly int Interval = 10 * 60 * 1000; //evaluate proxies every 15 minutes
- private Timer timer = null;
- private State state = null;
- private List servers;
+ 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 DelayBeforeStart = 1000;
+ public Statistics RawStatistics { get; private set; }
+ public Statistics FilteredStatistics { get; private set; }
+ public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1);
+ private int Repeat => _config.RepeatTimesNum;
+ private const int RetryInterval = 2*60*1000; //retry 2 minutes after failed
+ private int Interval => (int) TimeSpan.FromMinutes(_config.DataCollectionMinutes).TotalMilliseconds;
+ private 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);
+ _state = new State();
+ _timer = new Timer(Run, _state, DelayBeforeStart, Interval);
}
}
else
{
- timer?.Dispose();
+ _timer?.Dispose();
}
return true;
}
@@ -55,64 +77,240 @@ namespace Shadowsocks.Controller
}
}
- private void Evaluate(object obj)
+ //hardcode
+ //TODO: backup reliable isp&geolocation provider or a local database is required
+ public static async Task