diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs
index 089fbaee..983130a5 100644
--- a/shadowsocks-csharp/Controller/ShadowsocksController.cs
+++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs
@@ -89,7 +89,7 @@ namespace Shadowsocks.Controller
StatisticsConfiguration = StatisticsStrategyConfiguration.Load();
_strategyManager = new StrategyManager(this);
StartReleasingMemory();
- StartTrafficStatistics(60);
+ StartTrafficStatistics(61);
}
public void Start()
diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs
index 872147b6..cf4406b0 100755
--- a/shadowsocks-csharp/Util/Util.cs
+++ b/shadowsocks-csharp/Util/Util.cs
@@ -117,6 +117,44 @@ namespace Shadowsocks.Util
return $"{result.Item1:0.##}{result.Item2}";
}
+ public static string FormatBytes(long bytes)
+ {
+ const long K = 1024L;
+ const long M = K * 1024L;
+ const long G = M * 1024L;
+ const long T = G * 1024L;
+ const long P = T * 1024L;
+ const long E = P * 1024L;
+
+ if (bytes >= P * 990)
+ return (bytes / (double)E).ToString("F5") + "EB";
+ if (bytes >= T * 990)
+ return (bytes / (double)P).ToString("F5") + "PB";
+ if (bytes >= G * 990)
+ return (bytes / (double)T).ToString("F5") + "TB";
+ if (bytes >= M * 990)
+ {
+ return (bytes / (double)G).ToString("F4") + "GB";
+ }
+ if (bytes >= M * 100)
+ {
+ return (bytes / (double)M).ToString("F1") + "MB";
+ }
+ if (bytes >= M * 10)
+ {
+ return (bytes / (double)M).ToString("F2") + "MB";
+ }
+ if (bytes >= K * 990)
+ {
+ return (bytes / (double)M).ToString("F3") + "MB";
+ }
+ if (bytes > K * 2)
+ {
+ return (bytes / (double)K).ToString("F1") + "KB";
+ }
+ return bytes.ToString();
+ }
+
///
/// Return scaled bandwidth
///
diff --git a/shadowsocks-csharp/View/LogForm.Designer.cs b/shadowsocks-csharp/View/LogForm.Designer.cs
index 9f0cf52b..7621c17d 100644
--- a/shadowsocks-csharp/View/LogForm.Designer.cs
+++ b/shadowsocks-csharp/View/LogForm.Designer.cs
@@ -250,6 +250,8 @@
chartArea1.AxisX.MajorGrid.Interval = 5D;
chartArea1.AxisX.MajorGrid.LineColor = System.Drawing.Color.LightGray;
chartArea1.AxisX.MajorTickMark.Enabled = false;
+ chartArea1.AxisX.Maximum = 61D;
+ chartArea1.AxisX.Minimum = 1D;
chartArea1.AxisY.IntervalAutoMode = System.Windows.Forms.DataVisualization.Charting.IntervalAutoMode.VariableCount;
chartArea1.AxisY.LabelAutoFitMaxFontSize = 8;
chartArea1.AxisY.LabelStyle.Interval = 0D;
@@ -266,12 +268,14 @@
this.trafficChart.Location = new System.Drawing.Point(0, 0);
this.trafficChart.Name = "trafficChart";
this.trafficChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.None;
+ series1.BorderWidth = 2;
series1.ChartArea = "ChartArea1";
series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
series1.Color = System.Drawing.Color.FromArgb(255, 128, 0);
series1.IsXValueIndexed = true;
series1.Legend = "Legend1";
series1.Name = "Inbound";
+ series2.BorderWidth = 2;
series2.ChartArea = "ChartArea1";
series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
series2.Color = System.Drawing.Color.FromArgb(128, 128, 255);
diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs
index d45b47b2..a77a961e 100644
--- a/shadowsocks-csharp/View/LogForm.cs
+++ b/shadowsocks-csharp/View/LogForm.cs
@@ -22,11 +22,8 @@ namespace Shadowsocks.View
ShadowsocksController controller;
#region chart
- List inboundPoints = new List();
- List outboundPoints = new List();
- Tuple bandwidthScale = new Tuple(0, "B", 1);
- TextAnnotation inboundAnnotation = new TextAnnotation();
- TextAnnotation outboundAnnotation = new TextAnnotation();
+ long lastMaxSpeed;
+ public ShadowsocksController.QueueLast> traffic = new ShadowsocksController.QueueLast>();
#endregion
public LogForm(ShadowsocksController controller, string filename)
@@ -56,16 +53,40 @@ namespace Shadowsocks.View
UpdateTexts();
}
- private void controller_TrafficChanged(object sender, EventArgs e)
+ private void update_TrafficChart()
{
+ List inboundPoints = new List();
+ List outboundPoints = new List();
+ TextAnnotation inboundAnnotation = new TextAnnotation();
+ TextAnnotation outboundAnnotation = new TextAnnotation();
+ Tuple bandwidthScale;
+ const long minScale = 50;
long maxSpeed = 0;
- inboundPoints.Clear();
- outboundPoints.Clear();
- foreach (var trafficPerSecond in controller.traffic)
+ long lastInbound, lastOutbound;
+
+ lock (this)
{
- inboundPoints.Add(trafficPerSecond.inboundIncreasement);
- outboundPoints.Add(trafficPerSecond.outboundIncreasement);
- maxSpeed = Math.Max(maxSpeed, Math.Max(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement));
+ if (traffic.Count == 0)
+ return;
+ foreach (var trafficPerSecond in traffic)
+ {
+ inboundPoints.Add(trafficPerSecond.Item1);
+ outboundPoints.Add(trafficPerSecond.Item2);
+ maxSpeed = Math.Max(maxSpeed, Math.Max(trafficPerSecond.Item1, trafficPerSecond.Item2));
+ }
+ lastInbound = traffic.Last().Item1;
+ lastOutbound = traffic.Last().Item2;
+ }
+
+ if (maxSpeed > 0)
+ {
+ lastMaxSpeed -= lastMaxSpeed / 32;
+ maxSpeed = Math.Max(minScale, Math.Max(maxSpeed, lastMaxSpeed));
+ lastMaxSpeed = maxSpeed;
+ }
+ else
+ {
+ maxSpeed = lastMaxSpeed = minScale;
}
bandwidthScale = Utils.GetBandwidthScale(maxSpeed);
@@ -74,29 +95,31 @@ namespace Shadowsocks.View
inboundPoints = inboundPoints.Select(p => p / bandwidthScale.Item3).ToList();
outboundPoints = outboundPoints.Select(p => p / bandwidthScale.Item3).ToList();
- try
+ if (trafficChart.IsHandleCreated)
{
- if (trafficChart.InvokeRequired && trafficChart.IsHandleCreated)
- {
- trafficChart.Invoke(new Action(() =>
- {
- trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints);
- trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints);
- trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.Item2;
- inboundAnnotation.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last();
- inboundAnnotation.Text = Utils.FormatBandwidth(controller.traffic.Last.inboundIncreasement);
- outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last();
- outboundAnnotation.Text = Utils.FormatBandwidth(controller.traffic.Last.outboundIncreasement);
- trafficChart.Annotations.Clear();
- trafficChart.Annotations.Add(inboundAnnotation);
- trafficChart.Annotations.Add(outboundAnnotation);
- }));
- }
+ trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints);
+ trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints);
+ trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.Item2;
+ trafficChart.ChartAreas[0].AxisY.Maximum = bandwidthScale.Item1;
+ inboundAnnotation.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last();
+ inboundAnnotation.Text = Utils.FormatBandwidth(lastInbound);
+ outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last();
+ outboundAnnotation.Text = Utils.FormatBandwidth(lastOutbound);
+ trafficChart.Annotations.Clear();
+ trafficChart.Annotations.Add(inboundAnnotation);
+ trafficChart.Annotations.Add(outboundAnnotation);
}
- catch (ObjectDisposedException)
+ }
+
+ private void controller_TrafficChanged(object sender, EventArgs e)
+ {
+ lock (this)
{
- // suppress the thread race error:
- // when closing the form but the Invoked Action is still working and cause 'Chart is Disposed' exception
+ traffic = new ShadowsocksController.QueueLast>();
+ foreach (var trafficPerSecond in controller.traffic)
+ {
+ traffic.Enqueue(new Tuple(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement));
+ }
}
}
@@ -124,6 +147,7 @@ namespace Shadowsocks.View
private void Timer_Tick(object sender, EventArgs e)
{
UpdateContent();
+ update_TrafficChart();
}
private void InitContent()
@@ -149,29 +173,35 @@ namespace Shadowsocks.View
private void UpdateContent()
{
- try {
- using(StreamReader reader = new StreamReader(new FileStream(filename,
- FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) {
+ try
+ {
+ using (StreamReader reader = new StreamReader(new FileStream(filename,
+ FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
+ {
reader.BaseStream.Seek(lastOffset, SeekOrigin.Begin);
string line = "";
bool changed = false;
- while((line = reader.ReadLine()) != null) {
+ while ((line = reader.ReadLine()) != null)
+ {
changed = true;
LogMessageTextBox.AppendText(line + Environment.NewLine);
}
- if(changed) {
+ if (changed)
+ {
LogMessageTextBox.ScrollToCaret();
}
lastOffset = reader.BaseStream.Position;
}
- } catch(FileNotFoundException) {
+ }
+ catch (FileNotFoundException)
+ {
}
this.Text = I18N.GetString("Log Viewer") +
- $" [in: {Utils.FormatBandwidth(controller.InboundCounter)}, out: {Utils.FormatBandwidth(controller.OutboundCounter)}]";
+ $" [in: {Utils.FormatBytes(controller.InboundCounter)}, out: {Utils.FormatBytes(controller.OutboundCounter)}]";
}
private void LogForm_Load(object sender, EventArgs e)
@@ -179,7 +209,7 @@ namespace Shadowsocks.View
InitContent();
timer = new Timer();
- timer.Interval = 300;
+ timer.Interval = 100;
timer.Tick += Timer_Tick;
timer.Start();
@@ -190,7 +220,8 @@ namespace Shadowsocks.View
Width = config.width;
Top = config.GetBestTop();
Left = config.GetBestLeft();
- if(config.maximized) {
+ if (config.maximized)
+ {
WindowState = FormWindowState.Maximized;
}
@@ -218,7 +249,8 @@ namespace Shadowsocks.View
config.SetFont(LogMessageTextBox.Font);
config.SetBackgroundColor(LogMessageTextBox.BackColor);
config.SetTextColor(LogMessageTextBox.ForeColor);
- if (WindowState != FormWindowState.Minimized && !(config.maximized = WindowState == FormWindowState.Maximized)) {
+ if (WindowState != FormWindowState.Minimized && !(config.maximized = WindowState == FormWindowState.Maximized))
+ {
config.top = Top;
config.left = Left;
config.height = Height;