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;