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