Browse Source

Merge pull request #201 from tznind/single-multi

Single multi
BDisp 9 months ago
parent
commit
1d58ba4514
1 changed files with 253 additions and 17 deletions
  1. 253 17
      UICatalog/Scenarios/AnsiEscapeSequenceRequests.cs

+ 253 - 17
UICatalog/Scenarios/AnsiEscapeSequenceRequests.cs

@@ -1,4 +1,7 @@
+using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Linq;
+using System.Text;
 using Terminal.Gui;
 using Terminal.Gui;
 
 
 namespace UICatalog.Scenarios;
 namespace UICatalog.Scenarios;
@@ -7,17 +10,66 @@ namespace UICatalog.Scenarios;
 [ScenarioCategory ("Ansi Escape Sequence")]
 [ScenarioCategory ("Ansi Escape Sequence")]
 public sealed class AnsiEscapeSequenceRequests : Scenario
 public sealed class AnsiEscapeSequenceRequests : Scenario
 {
 {
+    private GraphView _graphView;
+
+    private DateTime start = DateTime.Now;
+    private ScatterSeries _sentSeries;
+    private ScatterSeries _answeredSeries;
+
+    private List<DateTime> sends = new ();
+
+    private object lockAnswers = new object ();
+    private Dictionary<DateTime, string> answers = new ();
+    private Label _lblSummary;
+
     public override void Main ()
     public override void Main ()
     {
     {
         // Init
         // Init
         Application.Init ();
         Application.Init ();
 
 
+        TabView tv = new TabView
+        {
+            Width = Dim.Fill (),
+            Height = Dim.Fill ()
+        };
+
+        Tab single = new Tab ();
+        single.DisplayText = "Single";
+        single.View = BuildSingleTab ();
+
+        Tab bulk = new ();
+        bulk.DisplayText = "Multi";
+        bulk.View = BuildBulkTab ();
+
+        tv.AddTab (single, true);
+        tv.AddTab (bulk, false);
+
         // Setup - Create a top-level application window and configure it.
         // Setup - Create a top-level application window and configure it.
         Window appWindow = new ()
         Window appWindow = new ()
         {
         {
             Title = GetQuitKeyAndName (),
             Title = GetQuitKeyAndName (),
         };
         };
-        appWindow.Padding.Thickness = new (1);
+
+        appWindow.Add (tv);
+
+        // Run - Start the application.
+        Application.Run (appWindow);
+        bulk.View.Dispose ();
+        single.View.Dispose ();
+        appWindow.Dispose ();
+
+        // Shutdown - Calling Application.Shutdown is required.
+        Application.Shutdown ();
+    }
+    private View BuildSingleTab ()
+    {
+        View w = new View ()
+        {
+            Width = Dim.Fill(),
+            Height = Dim.Fill ()
+        };
+
+        w.Padding.Thickness = new (1);
 
 
         var scrRequests = new List<string>
         var scrRequests = new List<string>
         {
         {
@@ -28,19 +80,19 @@ public sealed class AnsiEscapeSequenceRequests : Scenario
         };
         };
 
 
         var cbRequests = new ComboBox () { Width = 40, Height = 5, ReadOnly = true, Source = new ListWrapper<string> (new (scrRequests)) };
         var cbRequests = new ComboBox () { Width = 40, Height = 5, ReadOnly = true, Source = new ListWrapper<string> (new (scrRequests)) };
-        appWindow.Add (cbRequests);
+        w.Add (cbRequests);
 
 
         var label = new Label { Y = Pos.Bottom (cbRequests) + 1, Text = "Request:" };
         var label = new Label { Y = Pos.Bottom (cbRequests) + 1, Text = "Request:" };
         var tfRequest = new TextField { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 20 };
         var tfRequest = new TextField { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 20 };
-        appWindow.Add (label, tfRequest);
+        w.Add (label, tfRequest);
 
 
         label = new Label { X = Pos.Right (tfRequest) + 1, Y = Pos.Top (tfRequest) - 1, Text = "Value:" };
         label = new Label { X = Pos.Right (tfRequest) + 1, Y = Pos.Top (tfRequest) - 1, Text = "Value:" };
         var tfValue = new TextField { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 6 };
         var tfValue = new TextField { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 6 };
-        appWindow.Add (label, tfValue);
+        w.Add (label, tfValue);
 
 
         label = new Label { X = Pos.Right (tfValue) + 1, Y = Pos.Top (tfValue) - 1, Text = "Terminator:" };
         label = new Label { X = Pos.Right (tfValue) + 1, Y = Pos.Top (tfValue) - 1, Text = "Terminator:" };
         var tfTerminator = new TextField { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 4 };
         var tfTerminator = new TextField { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 4 };
-        appWindow.Add (label, tfTerminator);
+        w.Add (label, tfTerminator);
 
 
         cbRequests.SelectedItemChanged += (s, e) =>
         cbRequests.SelectedItemChanged += (s, e) =>
                                           {
                                           {
@@ -76,24 +128,24 @@ public sealed class AnsiEscapeSequenceRequests : Scenario
 
 
         label = new Label { Y = Pos.Bottom (tfRequest) + 2, Text = "Response:" };
         label = new Label { Y = Pos.Bottom (tfRequest) + 2, Text = "Response:" };
         var tvResponse = new TextView { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 40, Height = 4, ReadOnly = true };
         var tvResponse = new TextView { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 40, Height = 4, ReadOnly = true };
-        appWindow.Add (label, tvResponse);
+        w.Add (label, tvResponse);
 
 
         label = new Label { X = Pos.Right (tvResponse) + 1, Y = Pos.Top (tvResponse) - 1, Text = "Error:" };
         label = new Label { X = Pos.Right (tvResponse) + 1, Y = Pos.Top (tvResponse) - 1, Text = "Error:" };
         var tvError = new TextView { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 40, Height = 4, ReadOnly = true };
         var tvError = new TextView { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 40, Height = 4, ReadOnly = true };
-        appWindow.Add (label, tvError);
+        w.Add (label, tvError);
 
 
         label = new Label { X = Pos.Right (tvError) + 1, Y = Pos.Top (tvError) - 1, Text = "Value:" };
         label = new Label { X = Pos.Right (tvError) + 1, Y = Pos.Top (tvError) - 1, Text = "Value:" };
         var tvValue = new TextView { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 6, Height = 4, ReadOnly = true };
         var tvValue = new TextView { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 6, Height = 4, ReadOnly = true };
-        appWindow.Add (label, tvValue);
+        w.Add (label, tvValue);
 
 
         label = new Label { X = Pos.Right (tvValue) + 1, Y = Pos.Top (tvValue) - 1, Text = "Terminator:" };
         label = new Label { X = Pos.Right (tvValue) + 1, Y = Pos.Top (tvValue) - 1, Text = "Terminator:" };
         var tvTerminator = new TextView { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 4, Height = 4, ReadOnly = true };
         var tvTerminator = new TextView { X = Pos.Left (label), Y = Pos.Bottom (label), Width = 4, Height = 4, ReadOnly = true };
-        appWindow.Add (label, tvTerminator);
+        w.Add (label, tvTerminator);
 
 
         var btnResponse = new Button { X = Pos.Center (), Y = Pos.Bottom (tvResponse) + 2, Text = "Send Request", IsDefault = true };
         var btnResponse = new Button { X = Pos.Center (), Y = Pos.Bottom (tvResponse) + 2, Text = "Send Request", IsDefault = true };
 
 
         var lblSuccess = new Label { X = Pos.Center (), Y = Pos.Bottom (btnResponse) + 1 };
         var lblSuccess = new Label { X = Pos.Center (), Y = Pos.Bottom (btnResponse) + 1 };
-        appWindow.Add (lblSuccess);
+        w.Add (lblSuccess);
 
 
         btnResponse.Accepting += (s, e) =>
         btnResponse.Accepting += (s, e) =>
                               {
                               {
@@ -125,15 +177,199 @@ public sealed class AnsiEscapeSequenceRequests : Scenario
                                       lblSuccess.Text = "Error";
                                       lblSuccess.Text = "Error";
                                   }
                                   }
                               };
                               };
-        appWindow.Add (btnResponse);
+        w.Add (btnResponse);
 
 
-        appWindow.Add (new Label { Y = Pos.Bottom (lblSuccess) + 2, Text = "You can send other requests by editing the TextFields." });
+        w.Add (new Label { Y = Pos.Bottom (lblSuccess) + 2, Text = "You can send other requests by editing the TextFields." });
 
 
-        // Run - Start the application.
-        Application.Run (appWindow);
-        appWindow.Dispose ();
+        return w;
+    }
 
 
-        // Shutdown - Calling Application.Shutdown is required.
-        Application.Shutdown ();
+    private View BuildBulkTab ()
+    {
+        View w = new View ()
+        {
+            Width = Dim.Fill (),
+            Height = Dim.Fill ()
+        };
+
+        var lbl = new Label ()
+        {
+            Text = "This scenario tests Ansi request/response processing. Use the TextView to ensure regular user interaction continues as normal during sends",
+            Height = 2,
+            Width = Dim.Fill ()
+        };
+
+        Application.AddTimeout (
+                                TimeSpan.FromMilliseconds (1000),
+                                () =>
+                                {
+                                    lock (lockAnswers)
+                                    {
+                                        UpdateGraph ();
+
+                                        UpdateResponses ();
+                                    }
+
+
+
+                                    return true;
+                                });
+
+        var tv = new TextView ()
+        {
+            Y = Pos.Bottom (lbl),
+            Width = Dim.Percent (50),
+            Height = Dim.Fill ()
+        };
+
+
+        var lblDar = new Label ()
+        {
+            Y = Pos.Bottom (lbl),
+            X = Pos.Right (tv) + 1,
+            Text = "DAR per second",
+        };
+        var cbDar = new NumericUpDown ()
+        {
+            X = Pos.Right (lblDar),
+            Y = Pos.Bottom (lbl),
+            Value = 0,
+        };
+
+        cbDar.ValueChanging += (s, e) =>
+        {
+            if (e.NewValue < 0 || e.NewValue > 20)
+            {
+                e.Cancel = true;
+            }
+        };
+        w.Add (cbDar);
+
+        int lastSendTime = Environment.TickCount;
+        object lockObj = new object ();
+        Application.AddTimeout (
+                                TimeSpan.FromMilliseconds (50),
+                                () =>
+                                {
+                                    lock (lockObj)
+                                    {
+                                        if (cbDar.Value > 0)
+                                        {
+                                            int interval = 1000 / cbDar.Value; // Calculate the desired interval in milliseconds
+                                            int currentTime = Environment.TickCount; // Current system time in milliseconds
+
+                                            // Check if the time elapsed since the last send is greater than the interval
+                                            if (currentTime - lastSendTime >= interval)
+                                            {
+                                                SendDar (); // Send the request
+                                                lastSendTime = currentTime; // Update the last send time
+                                            }
+                                        }
+                                    }
+
+                                    return true;
+                                });
+
+
+        _graphView = new GraphView ()
+        {
+            Y = Pos.Bottom (cbDar),
+            X = Pos.Right (tv),
+            Width = Dim.Fill (),
+            Height = Dim.Fill (1)
+        };
+
+        _lblSummary = new Label ()
+        {
+            Y = Pos.Bottom (_graphView),
+            X = Pos.Right (tv),
+            Width = Dim.Fill ()
+        };
+
+        SetupGraph ();
+
+        w.Add (lbl);
+        w.Add (lblDar);
+        w.Add (cbDar);
+        w.Add (tv);
+        w.Add (_graphView);
+        w.Add (_lblSummary);
+
+        return w;
+    }
+    private void UpdateResponses ()
+    {
+        _lblSummary.Text = GetSummary ();
+        _lblSummary.SetNeedsDisplay ();
+    }
+
+    private string GetSummary ()
+    {
+        if (answers.Count == 0)
+        {
+            return "No requests sent yet";
+        }
+
+        var last = answers.Last ().Value;
+
+        var unique = answers.Values.Distinct ().Count ();
+        var total = answers.Count;
+
+        return $"Last:{last} U:{unique} T:{total}";
+    }
+
+    private void SetupGraph ()
+    {
+
+        _graphView.Series.Add (_sentSeries = new ScatterSeries ());
+        _graphView.Series.Add (_answeredSeries = new ScatterSeries ());
+
+        _sentSeries.Fill = new GraphCellToRender (new Rune ('.'), new Attribute (ColorName16.BrightGreen, ColorName16.Black));
+        _answeredSeries.Fill = new GraphCellToRender (new Rune ('.'), new Attribute (ColorName16.BrightRed, ColorName16.Black));
+
+        // Todo:
+        // _graphView.Annotations.Add (_sentSeries new PathAnnotation {});
+
+        _graphView.CellSize = new PointF (1, 1);
+        _graphView.MarginBottom = 2;
+        _graphView.AxisX.Increment = 1;
+        _graphView.AxisX.Text = "Seconds";
+        _graphView.GraphColor = new Attribute (Color.Green, Color.Black);
+    }
+
+    private void UpdateGraph ()
+    {
+        _sentSeries.Points = sends
+                             .GroupBy (ToSeconds)
+                             .Select (g => new PointF (g.Key, g.Count ()))
+                             .ToList ();
+
+        _answeredSeries.Points = answers.Keys
+                                        .GroupBy (ToSeconds)
+                                        .Select (g => new PointF (g.Key, g.Count ()))
+                                        .ToList ();
+        //  _graphView.ScrollOffset  = new PointF(,0);
+        _graphView.SetNeedsDisplay ();
+
+    }
+
+    private int ToSeconds (DateTime t)
+    {
+        return (int)(DateTime.Now - t).TotalSeconds;
+    }
+
+    private void SendDar ()
+    {
+        sends.Add (DateTime.Now);
+        var result = Application.Driver.WriteAnsiRequest (EscSeqUtils.CSI_SendDeviceAttributes);
+        HandleResponse (result);
+    }
+
+    private void HandleResponse (string response)
+    {
+        lock (lockAnswers)
+        {
+            answers.Add (DateTime.Now, response);
+        }
     }
     }
 }
 }