Initial commit
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
/packages/
|
||||||
|
riderModule.iml
|
||||||
|
/_ReSharper.Caches/
|
||||||
|
/.idea/
|
||||||
21
CruiseControllerMX5.csproj
Normal file
21
CruiseControllerMX5.csproj
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net9.0-windows</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<ApplicationVisualStyles>true</ApplicationVisualStyles>
|
||||||
|
<ApplicationUseCompatibleTextRendering>false</ApplicationUseCompatibleTextRendering>
|
||||||
|
<ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
|
<!-- <ApplicationDefaultFont>Microsoft Sans Serif, 8.25pt</ApplicationDefaultFont> -->
|
||||||
|
<!-- <AllowUnsafeBlocks>true</AllowUnsafeBlocks> -->
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.IO.Ports" Version="9.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
4
CruiseControllerMX5.csproj.user
Normal file
4
CruiseControllerMX5.csproj.user
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup />
|
||||||
|
</Project>
|
||||||
25
CruiseControllerMX5.sln
Normal file
25
CruiseControllerMX5.sln
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.5.002.0
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CruiseControllerMX5", "CruiseControllerMX5.csproj", "{1EB855FC-DA5B-4693-9AA9-F83B3EFCD166}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{1EB855FC-DA5B-4693-9AA9-F83B3EFCD166}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1EB855FC-DA5B-4693-9AA9-F83B3EFCD166}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1EB855FC-DA5B-4693-9AA9-F83B3EFCD166}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1EB855FC-DA5B-4693-9AA9-F83B3EFCD166}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {D516363E-E077-41A2-B6C6-E6E893675F31}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
838
src/App.Designer.cs
generated
Normal file
838
src/App.Designer.cs
generated
Normal file
@@ -0,0 +1,838 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace CruiseControllerMX5;
|
||||||
|
|
||||||
|
partial class App
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private IContainer components = null;
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
leftPanel = new System.Windows.Forms.Panel();
|
||||||
|
leftFillPanel = new System.Windows.Forms.Panel();
|
||||||
|
speedGaugeCanvas = new System.Windows.Forms.PictureBox();
|
||||||
|
leftFillTopPanel = new System.Windows.Forms.Panel();
|
||||||
|
leftBottomPanel = new System.Windows.Forms.Panel();
|
||||||
|
kpidPanel = new System.Windows.Forms.TableLayoutPanel();
|
||||||
|
kdCurrentField = new System.Windows.Forms.TextBox();
|
||||||
|
kdField = new System.Windows.Forms.TextBox();
|
||||||
|
kpidNewValueLabel = new System.Windows.Forms.Label();
|
||||||
|
kpidCurrentValueLabel = new System.Windows.Forms.Label();
|
||||||
|
kpLabel = new System.Windows.Forms.Label();
|
||||||
|
kpField = new System.Windows.Forms.TextBox();
|
||||||
|
kpCurrentField = new System.Windows.Forms.TextBox();
|
||||||
|
kiLabel = new System.Windows.Forms.Label();
|
||||||
|
kiField = new System.Windows.Forms.TextBox();
|
||||||
|
kiCurrentField = new System.Windows.Forms.TextBox();
|
||||||
|
kdLabel = new System.Windows.Forms.Label();
|
||||||
|
rightPanel = new System.Windows.Forms.Panel();
|
||||||
|
graphPanel = new System.Windows.Forms.Panel();
|
||||||
|
graphCanvas = new System.Windows.Forms.PictureBox();
|
||||||
|
graphControlsPanel = new System.Windows.Forms.Panel();
|
||||||
|
servoToggle = new System.Windows.Forms.CheckBox();
|
||||||
|
accelerationToggle = new System.Windows.Forms.CheckBox();
|
||||||
|
speedToggle = new System.Windows.Forms.CheckBox();
|
||||||
|
derivativeToggle = new System.Windows.Forms.CheckBox();
|
||||||
|
integralToggle = new System.Windows.Forms.CheckBox();
|
||||||
|
errorToggle = new System.Windows.Forms.CheckBox();
|
||||||
|
valuesPanel = new System.Windows.Forms.Panel();
|
||||||
|
tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||||
|
errorField = new System.Windows.Forms.TextBox();
|
||||||
|
errorLabel = new System.Windows.Forms.Label();
|
||||||
|
accelerationLabel = new System.Windows.Forms.Label();
|
||||||
|
speedLabel = new System.Windows.Forms.Label();
|
||||||
|
speedField = new System.Windows.Forms.TextBox();
|
||||||
|
accelerationField = new System.Windows.Forms.TextBox();
|
||||||
|
derivativeField = new System.Windows.Forms.TextBox();
|
||||||
|
integralField = new System.Windows.Forms.TextBox();
|
||||||
|
proportionalField = new System.Windows.Forms.TextBox();
|
||||||
|
derivativeLabel = new System.Windows.Forms.Label();
|
||||||
|
integralLabel = new System.Windows.Forms.Label();
|
||||||
|
proportionalLabel = new System.Windows.Forms.Label();
|
||||||
|
servoLabel = new System.Windows.Forms.Label();
|
||||||
|
servoMinLabel = new System.Windows.Forms.Label();
|
||||||
|
servoMaxLabel = new System.Windows.Forms.Label();
|
||||||
|
servoNeutralLabel = new System.Windows.Forms.Label();
|
||||||
|
servoField = new System.Windows.Forms.TextBox();
|
||||||
|
servoMinField = new System.Windows.Forms.TextBox();
|
||||||
|
servoMaxField = new System.Windows.Forms.TextBox();
|
||||||
|
servoNeutralField = new System.Windows.Forms.TextBox();
|
||||||
|
statusPanel = new System.Windows.Forms.TableLayoutPanel();
|
||||||
|
stateLabel = new System.Windows.Forms.Label();
|
||||||
|
brakeLabel = new System.Windows.Forms.Label();
|
||||||
|
menuStrip1 = new System.Windows.Forms.MenuStrip();
|
||||||
|
leftPanel.SuspendLayout();
|
||||||
|
leftFillPanel.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)speedGaugeCanvas).BeginInit();
|
||||||
|
leftBottomPanel.SuspendLayout();
|
||||||
|
kpidPanel.SuspendLayout();
|
||||||
|
rightPanel.SuspendLayout();
|
||||||
|
graphPanel.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)graphCanvas).BeginInit();
|
||||||
|
graphControlsPanel.SuspendLayout();
|
||||||
|
valuesPanel.SuspendLayout();
|
||||||
|
tableLayoutPanel1.SuspendLayout();
|
||||||
|
statusPanel.SuspendLayout();
|
||||||
|
SuspendLayout();
|
||||||
|
//
|
||||||
|
// leftPanel
|
||||||
|
//
|
||||||
|
leftPanel.Controls.Add(leftFillPanel);
|
||||||
|
leftPanel.Controls.Add(leftBottomPanel);
|
||||||
|
leftPanel.Dock = System.Windows.Forms.DockStyle.Left;
|
||||||
|
leftPanel.Location = new System.Drawing.Point(0, 24);
|
||||||
|
leftPanel.Name = "leftPanel";
|
||||||
|
leftPanel.Size = new System.Drawing.Size(300, 480);
|
||||||
|
leftPanel.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// leftFillPanel
|
||||||
|
//
|
||||||
|
leftFillPanel.Controls.Add(speedGaugeCanvas);
|
||||||
|
leftFillPanel.Controls.Add(leftFillTopPanel);
|
||||||
|
leftFillPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
leftFillPanel.Location = new System.Drawing.Point(0, 0);
|
||||||
|
leftFillPanel.Name = "leftFillPanel";
|
||||||
|
leftFillPanel.Size = new System.Drawing.Size(300, 300);
|
||||||
|
leftFillPanel.TabIndex = 4;
|
||||||
|
//
|
||||||
|
// speedGaugeCanvas
|
||||||
|
//
|
||||||
|
speedGaugeCanvas.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
speedGaugeCanvas.Location = new System.Drawing.Point(0, 40);
|
||||||
|
speedGaugeCanvas.Name = "speedGaugeCanvas";
|
||||||
|
speedGaugeCanvas.Size = new System.Drawing.Size(300, 260);
|
||||||
|
speedGaugeCanvas.TabIndex = 2;
|
||||||
|
speedGaugeCanvas.TabStop = false;
|
||||||
|
speedGaugeCanvas.Paint += speedGaugePanel_Paint;
|
||||||
|
//
|
||||||
|
// leftFillTopPanel
|
||||||
|
//
|
||||||
|
leftFillTopPanel.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
|
leftFillTopPanel.Location = new System.Drawing.Point(0, 0);
|
||||||
|
leftFillTopPanel.Name = "leftFillTopPanel";
|
||||||
|
leftFillTopPanel.Size = new System.Drawing.Size(300, 40);
|
||||||
|
leftFillTopPanel.TabIndex = 3;
|
||||||
|
//
|
||||||
|
// leftBottomPanel
|
||||||
|
//
|
||||||
|
leftBottomPanel.Controls.Add(kpidPanel);
|
||||||
|
leftBottomPanel.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
|
leftBottomPanel.Location = new System.Drawing.Point(0, 300);
|
||||||
|
leftBottomPanel.Name = "leftBottomPanel";
|
||||||
|
leftBottomPanel.Size = new System.Drawing.Size(300, 180);
|
||||||
|
leftBottomPanel.TabIndex = 3;
|
||||||
|
//
|
||||||
|
// kpidPanel
|
||||||
|
//
|
||||||
|
kpidPanel.AutoSize = true;
|
||||||
|
kpidPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||||
|
kpidPanel.ColumnCount = 3;
|
||||||
|
kpidPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||||
|
kpidPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||||
|
kpidPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||||
|
kpidPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||||
|
kpidPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||||
|
kpidPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||||
|
kpidPanel.Controls.Add(kdCurrentField, 0, 3);
|
||||||
|
kpidPanel.Controls.Add(kdField, 0, 3);
|
||||||
|
kpidPanel.Controls.Add(kpidNewValueLabel, 1, 0);
|
||||||
|
kpidPanel.Controls.Add(kpidCurrentValueLabel, 2, 0);
|
||||||
|
kpidPanel.Controls.Add(kpLabel, 0, 1);
|
||||||
|
kpidPanel.Controls.Add(kpField, 1, 1);
|
||||||
|
kpidPanel.Controls.Add(kpCurrentField, 2, 1);
|
||||||
|
kpidPanel.Controls.Add(kiLabel, 0, 2);
|
||||||
|
kpidPanel.Controls.Add(kiField, 1, 2);
|
||||||
|
kpidPanel.Controls.Add(kiCurrentField, 2, 2);
|
||||||
|
kpidPanel.Controls.Add(kdLabel, 0, 3);
|
||||||
|
kpidPanel.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
|
kpidPanel.Location = new System.Drawing.Point(0, 29);
|
||||||
|
kpidPanel.Name = "kpidPanel";
|
||||||
|
kpidPanel.Padding = new System.Windows.Forms.Padding(16);
|
||||||
|
kpidPanel.RowCount = 4;
|
||||||
|
kpidPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
kpidPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
kpidPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
kpidPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
kpidPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
kpidPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
kpidPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
kpidPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
kpidPanel.Size = new System.Drawing.Size(300, 151);
|
||||||
|
kpidPanel.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// kdCurrentField
|
||||||
|
//
|
||||||
|
kdCurrentField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
kdCurrentField.Location = new System.Drawing.Point(165, 105);
|
||||||
|
kdCurrentField.Name = "kdCurrentField";
|
||||||
|
kdCurrentField.ReadOnly = true;
|
||||||
|
kdCurrentField.Size = new System.Drawing.Size(116, 27);
|
||||||
|
kdCurrentField.TabIndex = 10;
|
||||||
|
kdCurrentField.TabStop = false;
|
||||||
|
//
|
||||||
|
// kdField
|
||||||
|
//
|
||||||
|
kdField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
kdField.Location = new System.Drawing.Point(44, 105);
|
||||||
|
kdField.Name = "kdField";
|
||||||
|
kdField.Size = new System.Drawing.Size(115, 27);
|
||||||
|
kdField.TabIndex = 9;
|
||||||
|
//
|
||||||
|
// kpidNewValueLabel
|
||||||
|
//
|
||||||
|
kpidNewValueLabel.AutoSize = true;
|
||||||
|
kpidNewValueLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
kpidNewValueLabel.Location = new System.Drawing.Point(41, 16);
|
||||||
|
kpidNewValueLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
kpidNewValueLabel.Name = "kpidNewValueLabel";
|
||||||
|
kpidNewValueLabel.Size = new System.Drawing.Size(121, 20);
|
||||||
|
kpidNewValueLabel.TabIndex = 0;
|
||||||
|
kpidNewValueLabel.Text = "Réglage";
|
||||||
|
kpidNewValueLabel.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
|
||||||
|
//
|
||||||
|
// kpidCurrentValueLabel
|
||||||
|
//
|
||||||
|
kpidCurrentValueLabel.AutoSize = true;
|
||||||
|
kpidCurrentValueLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
kpidCurrentValueLabel.Location = new System.Drawing.Point(162, 16);
|
||||||
|
kpidCurrentValueLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
kpidCurrentValueLabel.Name = "kpidCurrentValueLabel";
|
||||||
|
kpidCurrentValueLabel.Size = new System.Drawing.Size(122, 20);
|
||||||
|
kpidCurrentValueLabel.TabIndex = 1;
|
||||||
|
kpidCurrentValueLabel.Text = "Actuel";
|
||||||
|
kpidCurrentValueLabel.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
|
||||||
|
//
|
||||||
|
// kpLabel
|
||||||
|
//
|
||||||
|
kpLabel.AutoSize = true;
|
||||||
|
kpLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
kpLabel.Location = new System.Drawing.Point(16, 36);
|
||||||
|
kpLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
kpLabel.Name = "kpLabel";
|
||||||
|
kpLabel.Size = new System.Drawing.Size(25, 33);
|
||||||
|
kpLabel.TabIndex = 2;
|
||||||
|
kpLabel.Text = "kp";
|
||||||
|
kpLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
//
|
||||||
|
// kpField
|
||||||
|
//
|
||||||
|
kpField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
kpField.Location = new System.Drawing.Point(44, 39);
|
||||||
|
kpField.Name = "kpField";
|
||||||
|
kpField.Size = new System.Drawing.Size(115, 27);
|
||||||
|
kpField.TabIndex = 3;
|
||||||
|
//
|
||||||
|
// kpCurrentField
|
||||||
|
//
|
||||||
|
kpCurrentField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
kpCurrentField.Location = new System.Drawing.Point(165, 39);
|
||||||
|
kpCurrentField.Name = "kpCurrentField";
|
||||||
|
kpCurrentField.ReadOnly = true;
|
||||||
|
kpCurrentField.Size = new System.Drawing.Size(116, 27);
|
||||||
|
kpCurrentField.TabIndex = 4;
|
||||||
|
kpCurrentField.TabStop = false;
|
||||||
|
//
|
||||||
|
// kiLabel
|
||||||
|
//
|
||||||
|
kiLabel.AutoSize = true;
|
||||||
|
kiLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
kiLabel.Location = new System.Drawing.Point(16, 69);
|
||||||
|
kiLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
kiLabel.Name = "kiLabel";
|
||||||
|
kiLabel.Size = new System.Drawing.Size(25, 33);
|
||||||
|
kiLabel.TabIndex = 5;
|
||||||
|
kiLabel.Text = "ki";
|
||||||
|
kiLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
//
|
||||||
|
// kiField
|
||||||
|
//
|
||||||
|
kiField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
kiField.Location = new System.Drawing.Point(44, 72);
|
||||||
|
kiField.Name = "kiField";
|
||||||
|
kiField.Size = new System.Drawing.Size(115, 27);
|
||||||
|
kiField.TabIndex = 6;
|
||||||
|
//
|
||||||
|
// kiCurrentField
|
||||||
|
//
|
||||||
|
kiCurrentField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
kiCurrentField.Location = new System.Drawing.Point(165, 72);
|
||||||
|
kiCurrentField.Name = "kiCurrentField";
|
||||||
|
kiCurrentField.ReadOnly = true;
|
||||||
|
kiCurrentField.Size = new System.Drawing.Size(116, 27);
|
||||||
|
kiCurrentField.TabIndex = 7;
|
||||||
|
kiCurrentField.TabStop = false;
|
||||||
|
//
|
||||||
|
// kdLabel
|
||||||
|
//
|
||||||
|
kdLabel.AutoSize = true;
|
||||||
|
kdLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
kdLabel.Location = new System.Drawing.Point(16, 102);
|
||||||
|
kdLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
kdLabel.Name = "kdLabel";
|
||||||
|
kdLabel.Size = new System.Drawing.Size(25, 33);
|
||||||
|
kdLabel.TabIndex = 8;
|
||||||
|
kdLabel.Text = "kd";
|
||||||
|
kdLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
//
|
||||||
|
// rightPanel
|
||||||
|
//
|
||||||
|
rightPanel.Controls.Add(graphPanel);
|
||||||
|
rightPanel.Controls.Add(valuesPanel);
|
||||||
|
rightPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
rightPanel.Location = new System.Drawing.Point(300, 24);
|
||||||
|
rightPanel.Name = "rightPanel";
|
||||||
|
rightPanel.Size = new System.Drawing.Size(1300, 480);
|
||||||
|
rightPanel.TabIndex = 2;
|
||||||
|
//
|
||||||
|
// graphPanel
|
||||||
|
//
|
||||||
|
graphPanel.Controls.Add(graphCanvas);
|
||||||
|
graphPanel.Controls.Add(graphControlsPanel);
|
||||||
|
graphPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
graphPanel.Location = new System.Drawing.Point(0, 0);
|
||||||
|
graphPanel.Name = "graphPanel";
|
||||||
|
graphPanel.Size = new System.Drawing.Size(1300, 300);
|
||||||
|
graphPanel.TabIndex = 3;
|
||||||
|
//
|
||||||
|
// graphCanvas
|
||||||
|
//
|
||||||
|
graphCanvas.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
graphCanvas.Location = new System.Drawing.Point(0, 40);
|
||||||
|
graphCanvas.Name = "graphCanvas";
|
||||||
|
graphCanvas.Size = new System.Drawing.Size(1300, 260);
|
||||||
|
graphCanvas.TabIndex = 2;
|
||||||
|
graphCanvas.TabStop = false;
|
||||||
|
graphCanvas.Paint += graphCanvas_Paint;
|
||||||
|
//
|
||||||
|
// graphControlsPanel
|
||||||
|
//
|
||||||
|
graphControlsPanel.Controls.Add(servoToggle);
|
||||||
|
graphControlsPanel.Controls.Add(accelerationToggle);
|
||||||
|
graphControlsPanel.Controls.Add(speedToggle);
|
||||||
|
graphControlsPanel.Controls.Add(derivativeToggle);
|
||||||
|
graphControlsPanel.Controls.Add(integralToggle);
|
||||||
|
graphControlsPanel.Controls.Add(errorToggle);
|
||||||
|
graphControlsPanel.Dock = System.Windows.Forms.DockStyle.Top;
|
||||||
|
graphControlsPanel.Location = new System.Drawing.Point(0, 0);
|
||||||
|
graphControlsPanel.Name = "graphControlsPanel";
|
||||||
|
graphControlsPanel.Size = new System.Drawing.Size(1300, 40);
|
||||||
|
graphControlsPanel.TabIndex = 3;
|
||||||
|
//
|
||||||
|
// servoToggle
|
||||||
|
//
|
||||||
|
servoToggle.AutoSize = true;
|
||||||
|
servoToggle.Location = new System.Drawing.Point(452, 10);
|
||||||
|
servoToggle.Name = "servoToggle";
|
||||||
|
servoToggle.Size = new System.Drawing.Size(65, 24);
|
||||||
|
servoToggle.TabIndex = 5;
|
||||||
|
servoToggle.Text = "Servo";
|
||||||
|
servoToggle.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// accelerationToggle
|
||||||
|
//
|
||||||
|
accelerationToggle.AutoSize = true;
|
||||||
|
accelerationToggle.Location = new System.Drawing.Point(335, 10);
|
||||||
|
accelerationToggle.Name = "accelerationToggle";
|
||||||
|
accelerationToggle.Size = new System.Drawing.Size(111, 24);
|
||||||
|
accelerationToggle.TabIndex = 4;
|
||||||
|
accelerationToggle.Text = "Accélération";
|
||||||
|
accelerationToggle.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// speedToggle
|
||||||
|
//
|
||||||
|
speedToggle.AutoSize = true;
|
||||||
|
speedToggle.Location = new System.Drawing.Point(255, 10);
|
||||||
|
speedToggle.Name = "speedToggle";
|
||||||
|
speedToggle.Size = new System.Drawing.Size(74, 24);
|
||||||
|
speedToggle.TabIndex = 3;
|
||||||
|
speedToggle.Text = "Vitesse";
|
||||||
|
speedToggle.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// derivativeToggle
|
||||||
|
//
|
||||||
|
derivativeToggle.AutoSize = true;
|
||||||
|
derivativeToggle.Location = new System.Drawing.Point(164, 10);
|
||||||
|
derivativeToggle.Name = "derivativeToggle";
|
||||||
|
derivativeToggle.Size = new System.Drawing.Size(85, 24);
|
||||||
|
derivativeToggle.TabIndex = 2;
|
||||||
|
derivativeToggle.Text = "Dérivatif";
|
||||||
|
derivativeToggle.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// integralToggle
|
||||||
|
//
|
||||||
|
integralToggle.AutoSize = true;
|
||||||
|
integralToggle.Location = new System.Drawing.Point(79, 10);
|
||||||
|
integralToggle.Name = "integralToggle";
|
||||||
|
integralToggle.Size = new System.Drawing.Size(79, 24);
|
||||||
|
integralToggle.TabIndex = 1;
|
||||||
|
integralToggle.Text = "Intégral";
|
||||||
|
integralToggle.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// errorToggle
|
||||||
|
//
|
||||||
|
errorToggle.AutoSize = true;
|
||||||
|
errorToggle.Location = new System.Drawing.Point(6, 10);
|
||||||
|
errorToggle.Name = "errorToggle";
|
||||||
|
errorToggle.Size = new System.Drawing.Size(67, 24);
|
||||||
|
errorToggle.TabIndex = 0;
|
||||||
|
errorToggle.Text = "Erreur";
|
||||||
|
errorToggle.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
|
// valuesPanel
|
||||||
|
//
|
||||||
|
valuesPanel.Controls.Add(tableLayoutPanel1);
|
||||||
|
valuesPanel.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
|
valuesPanel.Location = new System.Drawing.Point(0, 300);
|
||||||
|
valuesPanel.Name = "valuesPanel";
|
||||||
|
valuesPanel.Size = new System.Drawing.Size(1300, 180);
|
||||||
|
valuesPanel.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// tableLayoutPanel1
|
||||||
|
//
|
||||||
|
tableLayoutPanel1.AutoSize = true;
|
||||||
|
tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||||
|
tableLayoutPanel1.ColumnCount = 8;
|
||||||
|
tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||||
|
tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 200F));
|
||||||
|
tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 16F));
|
||||||
|
tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||||
|
tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100F));
|
||||||
|
tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 16F));
|
||||||
|
tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||||
|
tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 50F));
|
||||||
|
tableLayoutPanel1.Controls.Add(errorField, 1, 0);
|
||||||
|
tableLayoutPanel1.Controls.Add(errorLabel, 0, 0);
|
||||||
|
tableLayoutPanel1.Controls.Add(accelerationLabel, 3, 1);
|
||||||
|
tableLayoutPanel1.Controls.Add(speedLabel, 3, 0);
|
||||||
|
tableLayoutPanel1.Controls.Add(speedField, 4, 0);
|
||||||
|
tableLayoutPanel1.Controls.Add(accelerationField, 4, 1);
|
||||||
|
tableLayoutPanel1.Controls.Add(derivativeField, 1, 3);
|
||||||
|
tableLayoutPanel1.Controls.Add(integralField, 1, 2);
|
||||||
|
tableLayoutPanel1.Controls.Add(proportionalField, 1, 1);
|
||||||
|
tableLayoutPanel1.Controls.Add(derivativeLabel, 0, 3);
|
||||||
|
tableLayoutPanel1.Controls.Add(integralLabel, 0, 2);
|
||||||
|
tableLayoutPanel1.Controls.Add(proportionalLabel, 0, 1);
|
||||||
|
tableLayoutPanel1.Controls.Add(servoLabel, 6, 0);
|
||||||
|
tableLayoutPanel1.Controls.Add(servoMinLabel, 6, 1);
|
||||||
|
tableLayoutPanel1.Controls.Add(servoMaxLabel, 6, 2);
|
||||||
|
tableLayoutPanel1.Controls.Add(servoNeutralLabel, 6, 3);
|
||||||
|
tableLayoutPanel1.Controls.Add(servoField, 7, 0);
|
||||||
|
tableLayoutPanel1.Controls.Add(servoMinField, 7, 1);
|
||||||
|
tableLayoutPanel1.Controls.Add(servoMaxField, 7, 2);
|
||||||
|
tableLayoutPanel1.Controls.Add(servoNeutralField, 7, 3);
|
||||||
|
tableLayoutPanel1.Location = new System.Drawing.Point(16, 32);
|
||||||
|
tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||||
|
tableLayoutPanel1.RowCount = 4;
|
||||||
|
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
tableLayoutPanel1.Size = new System.Drawing.Size(699, 132);
|
||||||
|
tableLayoutPanel1.TabIndex = 11;
|
||||||
|
//
|
||||||
|
// errorField
|
||||||
|
//
|
||||||
|
errorField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
errorField.Location = new System.Drawing.Point(103, 3);
|
||||||
|
errorField.Name = "errorField";
|
||||||
|
errorField.ReadOnly = true;
|
||||||
|
errorField.Size = new System.Drawing.Size(194, 27);
|
||||||
|
errorField.TabIndex = 1;
|
||||||
|
errorField.TabStop = false;
|
||||||
|
//
|
||||||
|
// errorLabel
|
||||||
|
//
|
||||||
|
errorLabel.AutoSize = true;
|
||||||
|
errorLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
errorLabel.Location = new System.Drawing.Point(0, 0);
|
||||||
|
errorLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
errorLabel.Name = "errorLabel";
|
||||||
|
errorLabel.Size = new System.Drawing.Size(100, 33);
|
||||||
|
errorLabel.TabIndex = 0;
|
||||||
|
errorLabel.Text = "Erreur";
|
||||||
|
errorLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
//
|
||||||
|
// accelerationLabel
|
||||||
|
//
|
||||||
|
accelerationLabel.AutoSize = true;
|
||||||
|
accelerationLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
accelerationLabel.Location = new System.Drawing.Point(316, 33);
|
||||||
|
accelerationLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
accelerationLabel.Name = "accelerationLabel";
|
||||||
|
accelerationLabel.Size = new System.Drawing.Size(92, 33);
|
||||||
|
accelerationLabel.TabIndex = 10;
|
||||||
|
accelerationLabel.Text = "Accélération";
|
||||||
|
accelerationLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
//
|
||||||
|
// speedLabel
|
||||||
|
//
|
||||||
|
speedLabel.AutoSize = true;
|
||||||
|
speedLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
speedLabel.Location = new System.Drawing.Point(316, 0);
|
||||||
|
speedLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
speedLabel.Name = "speedLabel";
|
||||||
|
speedLabel.Size = new System.Drawing.Size(92, 33);
|
||||||
|
speedLabel.TabIndex = 8;
|
||||||
|
speedLabel.Text = "Vitesse";
|
||||||
|
speedLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
//
|
||||||
|
// speedField
|
||||||
|
//
|
||||||
|
speedField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
speedField.Location = new System.Drawing.Point(411, 3);
|
||||||
|
speedField.Name = "speedField";
|
||||||
|
speedField.ReadOnly = true;
|
||||||
|
speedField.Size = new System.Drawing.Size(94, 27);
|
||||||
|
speedField.TabIndex = 9;
|
||||||
|
speedField.TabStop = false;
|
||||||
|
//
|
||||||
|
// accelerationField
|
||||||
|
//
|
||||||
|
accelerationField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
accelerationField.Location = new System.Drawing.Point(411, 36);
|
||||||
|
accelerationField.Name = "accelerationField";
|
||||||
|
accelerationField.ReadOnly = true;
|
||||||
|
accelerationField.Size = new System.Drawing.Size(94, 27);
|
||||||
|
accelerationField.TabIndex = 11;
|
||||||
|
accelerationField.TabStop = false;
|
||||||
|
//
|
||||||
|
// derivativeField
|
||||||
|
//
|
||||||
|
derivativeField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
derivativeField.Location = new System.Drawing.Point(103, 102);
|
||||||
|
derivativeField.Name = "derivativeField";
|
||||||
|
derivativeField.ReadOnly = true;
|
||||||
|
derivativeField.Size = new System.Drawing.Size(194, 27);
|
||||||
|
derivativeField.TabIndex = 7;
|
||||||
|
derivativeField.TabStop = false;
|
||||||
|
//
|
||||||
|
// integralField
|
||||||
|
//
|
||||||
|
integralField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
integralField.Location = new System.Drawing.Point(103, 69);
|
||||||
|
integralField.Name = "integralField";
|
||||||
|
integralField.ReadOnly = true;
|
||||||
|
integralField.Size = new System.Drawing.Size(194, 27);
|
||||||
|
integralField.TabIndex = 5;
|
||||||
|
integralField.TabStop = false;
|
||||||
|
//
|
||||||
|
// proportionalField
|
||||||
|
//
|
||||||
|
proportionalField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
proportionalField.Location = new System.Drawing.Point(103, 36);
|
||||||
|
proportionalField.Name = "proportionalField";
|
||||||
|
proportionalField.ReadOnly = true;
|
||||||
|
proportionalField.Size = new System.Drawing.Size(194, 27);
|
||||||
|
proportionalField.TabIndex = 3;
|
||||||
|
proportionalField.TabStop = false;
|
||||||
|
//
|
||||||
|
// derivativeLabel
|
||||||
|
//
|
||||||
|
derivativeLabel.AutoSize = true;
|
||||||
|
derivativeLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
derivativeLabel.Location = new System.Drawing.Point(0, 99);
|
||||||
|
derivativeLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
derivativeLabel.Name = "derivativeLabel";
|
||||||
|
derivativeLabel.Size = new System.Drawing.Size(100, 33);
|
||||||
|
derivativeLabel.TabIndex = 6;
|
||||||
|
derivativeLabel.Text = "Dérivatif";
|
||||||
|
derivativeLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
//
|
||||||
|
// integralLabel
|
||||||
|
//
|
||||||
|
integralLabel.AutoSize = true;
|
||||||
|
integralLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
integralLabel.Location = new System.Drawing.Point(0, 66);
|
||||||
|
integralLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
integralLabel.Name = "integralLabel";
|
||||||
|
integralLabel.Size = new System.Drawing.Size(100, 33);
|
||||||
|
integralLabel.TabIndex = 4;
|
||||||
|
integralLabel.Text = "Intégral";
|
||||||
|
integralLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
//
|
||||||
|
// proportionalLabel
|
||||||
|
//
|
||||||
|
proportionalLabel.AutoSize = true;
|
||||||
|
proportionalLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
proportionalLabel.Location = new System.Drawing.Point(0, 33);
|
||||||
|
proportionalLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
proportionalLabel.Name = "proportionalLabel";
|
||||||
|
proportionalLabel.Size = new System.Drawing.Size(100, 33);
|
||||||
|
proportionalLabel.TabIndex = 2;
|
||||||
|
proportionalLabel.Text = "Proportionnel";
|
||||||
|
proportionalLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
//
|
||||||
|
// servoLabel
|
||||||
|
//
|
||||||
|
servoLabel.AutoSize = true;
|
||||||
|
servoLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
servoLabel.Location = new System.Drawing.Point(524, 0);
|
||||||
|
servoLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
servoLabel.Name = "servoLabel";
|
||||||
|
servoLabel.Size = new System.Drawing.Size(125, 33);
|
||||||
|
servoLabel.TabIndex = 12;
|
||||||
|
servoLabel.Text = "Commande servo";
|
||||||
|
servoLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
//
|
||||||
|
// servoMinLabel
|
||||||
|
//
|
||||||
|
servoMinLabel.AutoSize = true;
|
||||||
|
servoMinLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
servoMinLabel.Location = new System.Drawing.Point(524, 33);
|
||||||
|
servoMinLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
servoMinLabel.Name = "servoMinLabel";
|
||||||
|
servoMinLabel.Size = new System.Drawing.Size(125, 33);
|
||||||
|
servoMinLabel.TabIndex = 14;
|
||||||
|
servoMinLabel.Text = "Servo min";
|
||||||
|
servoMinLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
//
|
||||||
|
// servoMaxLabel
|
||||||
|
//
|
||||||
|
servoMaxLabel.AutoSize = true;
|
||||||
|
servoMaxLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
servoMaxLabel.Location = new System.Drawing.Point(524, 66);
|
||||||
|
servoMaxLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
servoMaxLabel.Name = "servoMaxLabel";
|
||||||
|
servoMaxLabel.Size = new System.Drawing.Size(125, 33);
|
||||||
|
servoMaxLabel.TabIndex = 16;
|
||||||
|
servoMaxLabel.Text = "Servo max";
|
||||||
|
servoMaxLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
//
|
||||||
|
// servoNeutralLabel
|
||||||
|
//
|
||||||
|
servoNeutralLabel.AutoSize = true;
|
||||||
|
servoNeutralLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
servoNeutralLabel.Location = new System.Drawing.Point(524, 99);
|
||||||
|
servoNeutralLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
servoNeutralLabel.Name = "servoNeutralLabel";
|
||||||
|
servoNeutralLabel.Size = new System.Drawing.Size(125, 33);
|
||||||
|
servoNeutralLabel.TabIndex = 18;
|
||||||
|
servoNeutralLabel.Text = "Servo neutre";
|
||||||
|
servoNeutralLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
|
//
|
||||||
|
// servoField
|
||||||
|
//
|
||||||
|
servoField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
servoField.Location = new System.Drawing.Point(652, 3);
|
||||||
|
servoField.Name = "servoField";
|
||||||
|
servoField.ReadOnly = true;
|
||||||
|
servoField.Size = new System.Drawing.Size(44, 27);
|
||||||
|
servoField.TabIndex = 13;
|
||||||
|
servoField.TabStop = false;
|
||||||
|
//
|
||||||
|
// servoMinField
|
||||||
|
//
|
||||||
|
servoMinField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
servoMinField.Location = new System.Drawing.Point(652, 36);
|
||||||
|
servoMinField.Name = "servoMinField";
|
||||||
|
servoMinField.ReadOnly = true;
|
||||||
|
servoMinField.Size = new System.Drawing.Size(44, 27);
|
||||||
|
servoMinField.TabIndex = 15;
|
||||||
|
servoMinField.TabStop = false;
|
||||||
|
//
|
||||||
|
// servoMaxField
|
||||||
|
//
|
||||||
|
servoMaxField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
servoMaxField.Location = new System.Drawing.Point(652, 69);
|
||||||
|
servoMaxField.Name = "servoMaxField";
|
||||||
|
servoMaxField.ReadOnly = true;
|
||||||
|
servoMaxField.Size = new System.Drawing.Size(44, 27);
|
||||||
|
servoMaxField.TabIndex = 17;
|
||||||
|
servoMaxField.TabStop = false;
|
||||||
|
//
|
||||||
|
// servoNeutralField
|
||||||
|
//
|
||||||
|
servoNeutralField.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
servoNeutralField.Location = new System.Drawing.Point(652, 102);
|
||||||
|
servoNeutralField.Name = "servoNeutralField";
|
||||||
|
servoNeutralField.ReadOnly = true;
|
||||||
|
servoNeutralField.Size = new System.Drawing.Size(44, 27);
|
||||||
|
servoNeutralField.TabIndex = 19;
|
||||||
|
servoNeutralField.TabStop = false;
|
||||||
|
//
|
||||||
|
// statusPanel
|
||||||
|
//
|
||||||
|
statusPanel.ColumnCount = 3;
|
||||||
|
statusPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.333332F));
|
||||||
|
statusPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.333332F));
|
||||||
|
statusPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.333332F));
|
||||||
|
statusPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.333332F));
|
||||||
|
statusPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.333332F));
|
||||||
|
statusPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.333332F));
|
||||||
|
statusPanel.Controls.Add(stateLabel, 0, 0);
|
||||||
|
statusPanel.Controls.Add(brakeLabel, 1, 0);
|
||||||
|
statusPanel.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
|
statusPanel.Location = new System.Drawing.Point(0, 504);
|
||||||
|
statusPanel.Name = "statusPanel";
|
||||||
|
statusPanel.RowCount = 1;
|
||||||
|
statusPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
statusPanel.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||||
|
statusPanel.Size = new System.Drawing.Size(1600, 32);
|
||||||
|
statusPanel.TabIndex = 3;
|
||||||
|
//
|
||||||
|
// stateLabel
|
||||||
|
//
|
||||||
|
stateLabel.BackColor = System.Drawing.Color.FromArgb(((int)((byte)70)), ((int)((byte)70)), ((int)((byte)70)));
|
||||||
|
stateLabel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||||
|
stateLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
stateLabel.Font = new System.Drawing.Font("Segoe UI", 14F, System.Drawing.FontStyle.Bold);
|
||||||
|
stateLabel.Location = new System.Drawing.Point(0, 0);
|
||||||
|
stateLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
stateLabel.Name = "stateLabel";
|
||||||
|
stateLabel.Size = new System.Drawing.Size(533, 32);
|
||||||
|
stateLabel.TabIndex = 0;
|
||||||
|
stateLabel.Text = "Déconnecté / OFF";
|
||||||
|
stateLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
|
//
|
||||||
|
// brakeLabel
|
||||||
|
//
|
||||||
|
brakeLabel.BackColor = System.Drawing.Color.FromArgb(((int)((byte)70)), ((int)((byte)70)), ((int)((byte)70)));
|
||||||
|
brakeLabel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||||
|
brakeLabel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
brakeLabel.Font = new System.Drawing.Font("Segoe UI", 14F, System.Drawing.FontStyle.Bold);
|
||||||
|
brakeLabel.Location = new System.Drawing.Point(533, 0);
|
||||||
|
brakeLabel.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
brakeLabel.Name = "brakeLabel";
|
||||||
|
brakeLabel.Size = new System.Drawing.Size(533, 32);
|
||||||
|
brakeLabel.TabIndex = 1;
|
||||||
|
brakeLabel.Text = "Frein";
|
||||||
|
brakeLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||||
|
//
|
||||||
|
// menuStrip1
|
||||||
|
//
|
||||||
|
menuStrip1.Location = new System.Drawing.Point(0, 0);
|
||||||
|
menuStrip1.Name = "menuStrip1";
|
||||||
|
menuStrip1.Size = new System.Drawing.Size(1600, 24);
|
||||||
|
menuStrip1.TabIndex = 4;
|
||||||
|
menuStrip1.Text = "menuStrip1";
|
||||||
|
//
|
||||||
|
// App
|
||||||
|
//
|
||||||
|
AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
||||||
|
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
|
||||||
|
BackColor = System.Drawing.Color.FromArgb(((int)((byte)30)), ((int)((byte)30)), ((int)((byte)30)));
|
||||||
|
ClientSize = new System.Drawing.Size(1600, 536);
|
||||||
|
Controls.Add(rightPanel);
|
||||||
|
Controls.Add(leftPanel);
|
||||||
|
Controls.Add(statusPanel);
|
||||||
|
Controls.Add(menuStrip1);
|
||||||
|
Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)0));
|
||||||
|
ForeColor = System.Drawing.Color.White;
|
||||||
|
Location = new System.Drawing.Point(15, 15);
|
||||||
|
MainMenuStrip = menuStrip1;
|
||||||
|
MinimumSize = new System.Drawing.Size(1616, 551);
|
||||||
|
Text = "Cruise Controller MX5";
|
||||||
|
Load += App_Load;
|
||||||
|
leftPanel.ResumeLayout(false);
|
||||||
|
leftFillPanel.ResumeLayout(false);
|
||||||
|
((System.ComponentModel.ISupportInitialize)speedGaugeCanvas).EndInit();
|
||||||
|
leftBottomPanel.ResumeLayout(false);
|
||||||
|
leftBottomPanel.PerformLayout();
|
||||||
|
kpidPanel.ResumeLayout(false);
|
||||||
|
kpidPanel.PerformLayout();
|
||||||
|
rightPanel.ResumeLayout(false);
|
||||||
|
graphPanel.ResumeLayout(false);
|
||||||
|
((System.ComponentModel.ISupportInitialize)graphCanvas).EndInit();
|
||||||
|
graphControlsPanel.ResumeLayout(false);
|
||||||
|
graphControlsPanel.PerformLayout();
|
||||||
|
valuesPanel.ResumeLayout(false);
|
||||||
|
valuesPanel.PerformLayout();
|
||||||
|
tableLayoutPanel1.ResumeLayout(false);
|
||||||
|
tableLayoutPanel1.PerformLayout();
|
||||||
|
statusPanel.ResumeLayout(false);
|
||||||
|
ResumeLayout(false);
|
||||||
|
PerformLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
private System.Windows.Forms.MenuStrip menuStrip1;
|
||||||
|
|
||||||
|
private System.Windows.Forms.CheckBox speedToggle;
|
||||||
|
private System.Windows.Forms.CheckBox accelerationToggle;
|
||||||
|
private System.Windows.Forms.CheckBox servoToggle;
|
||||||
|
|
||||||
|
private System.Windows.Forms.CheckBox integralToggle;
|
||||||
|
private System.Windows.Forms.CheckBox derivativeToggle;
|
||||||
|
|
||||||
|
private System.Windows.Forms.CheckBox errorToggle;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label servoMinLabel;
|
||||||
|
private System.Windows.Forms.Label servoMaxLabel;
|
||||||
|
private System.Windows.Forms.Label servoNeutralLabel;
|
||||||
|
private System.Windows.Forms.TextBox servoField;
|
||||||
|
private System.Windows.Forms.TextBox servoMinField;
|
||||||
|
private System.Windows.Forms.TextBox servoMaxField;
|
||||||
|
private System.Windows.Forms.TextBox servoNeutralField;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label servoLabel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Panel leftFillTopPanel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Panel leftFillPanel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Panel leftBottomPanel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.TextBox speedField;
|
||||||
|
private System.Windows.Forms.TextBox accelerationField;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label speedLabel;
|
||||||
|
private System.Windows.Forms.Label accelerationLabel;
|
||||||
|
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||||
|
|
||||||
|
private System.Windows.Forms.TextBox errorField;
|
||||||
|
|
||||||
|
private System.Windows.Forms.TextBox integralField;
|
||||||
|
private System.Windows.Forms.TextBox derivativeField;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label derivativeLabel;
|
||||||
|
private System.Windows.Forms.Label errorLabel;
|
||||||
|
private System.Windows.Forms.TextBox proportionalField;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label integralLabel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label proportionalLabel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label brakeLabel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label stateLabel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.PictureBox graphCanvas;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Panel graphControlsPanel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Panel graphPanel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.PictureBox speedGaugeCanvas;
|
||||||
|
|
||||||
|
private System.Windows.Forms.TableLayoutPanel statusPanel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Panel valuesPanel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Label kpidNewValueLabel;
|
||||||
|
private System.Windows.Forms.Label kpidCurrentValueLabel;
|
||||||
|
private System.Windows.Forms.Label kpLabel;
|
||||||
|
private System.Windows.Forms.TextBox kpField;
|
||||||
|
private System.Windows.Forms.TextBox kpCurrentField;
|
||||||
|
private System.Windows.Forms.Label kiLabel;
|
||||||
|
private System.Windows.Forms.TextBox kiField;
|
||||||
|
private System.Windows.Forms.TextBox kiCurrentField;
|
||||||
|
private System.Windows.Forms.Label kdLabel;
|
||||||
|
private System.Windows.Forms.TextBox kdField;
|
||||||
|
private System.Windows.Forms.TextBox kdCurrentField;
|
||||||
|
|
||||||
|
private System.Windows.Forms.TableLayoutPanel kpidPanel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Panel rightPanel;
|
||||||
|
|
||||||
|
private System.Windows.Forms.Panel leftPanel;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
1196
src/App.cs
Normal file
1196
src/App.cs
Normal file
File diff suppressed because it is too large
Load Diff
64
src/App.resx
Normal file
64
src/App.resx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing.Primitives, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>17, 17</value>
|
||||||
|
</metadata>
|
||||||
|
</root>
|
||||||
502
src/ArduinoCom.cs
Normal file
502
src/ArduinoCom.cs
Normal file
@@ -0,0 +1,502 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO.Ports;
|
||||||
|
|
||||||
|
namespace CruiseControllerMX5;
|
||||||
|
|
||||||
|
public interface IArduinoCom : IDisposable, IObservable<Sample>, IObserver<string>
|
||||||
|
{
|
||||||
|
void Open();
|
||||||
|
void Close();
|
||||||
|
IDisposable AddSerialObserver(IObserver<Sample> observer);
|
||||||
|
void SendCommand(string command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ArduinoCom : IArduinoCom
|
||||||
|
{
|
||||||
|
private readonly List<IObserver<Sample>> _sampleObservers = [];
|
||||||
|
private readonly BlockingCollection<string> _commands = [];
|
||||||
|
|
||||||
|
private readonly SerialPort _serialPort;
|
||||||
|
private readonly bool _keepOpen;
|
||||||
|
|
||||||
|
private bool _hasIgnoredFirstPacket, _hasReadSecondPacket;
|
||||||
|
private Sample? _previousSample;
|
||||||
|
|
||||||
|
public ArduinoCom(SerialPort serialPort, bool keepOpen = false)
|
||||||
|
{
|
||||||
|
_serialPort = serialPort;
|
||||||
|
_keepOpen = keepOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArduinoCom(Action<SerialPort> configure)
|
||||||
|
{
|
||||||
|
var serialPort = new SerialPort();
|
||||||
|
configure(serialPort);
|
||||||
|
_serialPort = serialPort;
|
||||||
|
_keepOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long ZeroTimestamp { get; private set; }
|
||||||
|
|
||||||
|
public void Open()
|
||||||
|
{
|
||||||
|
_serialPort.Open();
|
||||||
|
|
||||||
|
var readThread = new Thread(DoRead)
|
||||||
|
{
|
||||||
|
IsBackground = true
|
||||||
|
};
|
||||||
|
readThread.Start();
|
||||||
|
|
||||||
|
var writeThread = new Thread(DoWrite)
|
||||||
|
{
|
||||||
|
IsBackground = true
|
||||||
|
};
|
||||||
|
writeThread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
_serialPort.Close();
|
||||||
|
|
||||||
|
_hasIgnoredFirstPacket = false;
|
||||||
|
_hasReadSecondPacket = false;
|
||||||
|
ZeroTimestamp = 0;
|
||||||
|
_previousSample = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDisposable AddSerialObserver(IObserver<Sample> observer)
|
||||||
|
{
|
||||||
|
_sampleObservers.Add(observer);
|
||||||
|
return new Disposer(() => _sampleObservers.Remove(observer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendCommand(string command)
|
||||||
|
{
|
||||||
|
_commands.Add(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Sample HandlePacket(string packet)
|
||||||
|
{
|
||||||
|
var timestamp = TimeProvider.System.GetTimestamp();
|
||||||
|
|
||||||
|
if (!_hasReadSecondPacket)
|
||||||
|
{
|
||||||
|
ZeroTimestamp = timestamp;
|
||||||
|
_hasReadSecondPacket = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine(packet);
|
||||||
|
var millis = (uint)TimeProvider.System.GetElapsedTime(ZeroTimestamp, timestamp).TotalMilliseconds;
|
||||||
|
var sample = new Sample(millis,
|
||||||
|
_previousSample == null || _previousSample.Timestamp / 1000 != timestamp / 1000);
|
||||||
|
|
||||||
|
var variables = packet.Split(';');
|
||||||
|
|
||||||
|
for (int i = 0, max = variables.Length - 1; i < max; i++)
|
||||||
|
{
|
||||||
|
var variable = variables[i];
|
||||||
|
var split = variable.Split(':', 2);
|
||||||
|
|
||||||
|
var name = split[0];
|
||||||
|
var value = split[1];
|
||||||
|
|
||||||
|
UpdateSample(sample, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateSample(Sample sample, string name, string value)
|
||||||
|
{
|
||||||
|
switch (name)
|
||||||
|
{
|
||||||
|
case "KP":
|
||||||
|
sample.Kp = float.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "KI":
|
||||||
|
sample.Ki = float.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "KD":
|
||||||
|
sample.Kd = float.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "VITESSEMIN":
|
||||||
|
sample.SpeedMin = int.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "VITESSEMAX":
|
||||||
|
sample.SpeedMax = int.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "ETAT":
|
||||||
|
sample.Active = Utils.IntToBool(value);
|
||||||
|
break;
|
||||||
|
case "CONSIGNE":
|
||||||
|
sample.Consigne = float.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "VITESSE":
|
||||||
|
sample.Speed = float.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "ACCEL":
|
||||||
|
sample.Acceleration = float.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "ERREUR":
|
||||||
|
sample.Error = float.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "PROPORTIONNEL":
|
||||||
|
sample.Proportional = float.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "INTEGRAL":
|
||||||
|
sample.Integral = float.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "DERIVATIF":
|
||||||
|
sample.Derivative = float.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "SERVO":
|
||||||
|
sample.Servo = int.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "SERVOMIN":
|
||||||
|
sample.ServoMin = int.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "SERVOMAX":
|
||||||
|
sample.ServoMax = int.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "SERVONEUTRE":
|
||||||
|
sample.ServoNeutre = int.Parse(value, CultureInfo.InvariantCulture);
|
||||||
|
break;
|
||||||
|
case "FREIN":
|
||||||
|
sample.BrakeFlag = Utils.IntToBool(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoRead()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (_serialPort.IsOpen)
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
line = _serialPort.ReadLine();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("Failed to read line from serial connection");
|
||||||
|
Console.Error.WriteLine(ex.Message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(line))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_hasIgnoredFirstPacket)
|
||||||
|
{
|
||||||
|
_hasIgnoredFirstPacket = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sample = HandlePacket(line);
|
||||||
|
_previousSample = sample;
|
||||||
|
|
||||||
|
foreach (var observer in _sampleObservers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
observer.OnNext(sample);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("An exception occurred in a packet handler");
|
||||||
|
Console.Error.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("An exception occurred in the serial read loop");
|
||||||
|
Console.Error.WriteLine(ex.Message);
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoWrite()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (_serialPort.IsOpen)
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
line = _commands.Take();
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(line))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_serialPort.WriteLine(line);
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("Failed to send command");
|
||||||
|
Console.Error.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("An exception occurred in the serial write loop");
|
||||||
|
Console.Error.WriteLine(ex.Message);
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IObservable
|
||||||
|
|
||||||
|
IDisposable IObservable<Sample>.Subscribe(IObserver<Sample> observer)
|
||||||
|
{
|
||||||
|
return AddSerialObserver(observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IObserver
|
||||||
|
|
||||||
|
void IObserver<string>.OnCompleted()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void IObserver<string>.OnError(Exception error)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void IObserver<string>.OnNext(string value)
|
||||||
|
{
|
||||||
|
SendCommand(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
private bool _disposedValue;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposedValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_keepOpen)
|
||||||
|
{
|
||||||
|
_serialPort.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
_commands.Dispose();
|
||||||
|
_sampleObservers.Clear();
|
||||||
|
|
||||||
|
_disposedValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class TestArduinoCom : IArduinoCom
|
||||||
|
{
|
||||||
|
private readonly List<IObserver<Sample>> _sampleObservers = [];
|
||||||
|
private long _zeroTimestamp;
|
||||||
|
|
||||||
|
private volatile Sample? _previousSample;
|
||||||
|
private volatile bool _keepGoing;
|
||||||
|
|
||||||
|
public void Open()
|
||||||
|
{
|
||||||
|
_keepGoing = true;
|
||||||
|
|
||||||
|
var readThread = new Thread(DoRead)
|
||||||
|
{
|
||||||
|
IsBackground = true
|
||||||
|
};
|
||||||
|
readThread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
_keepGoing = false;
|
||||||
|
|
||||||
|
_previousSample = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDisposable AddSerialObserver(IObserver<Sample> observer)
|
||||||
|
{
|
||||||
|
_sampleObservers.Add(observer);
|
||||||
|
return new Disposer(() => _sampleObservers.Remove(observer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendCommand(string command)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DoRead()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
|
||||||
|
while (_keepGoing)
|
||||||
|
{
|
||||||
|
var timestamp = TimeProvider.System.GetTimestamp();
|
||||||
|
|
||||||
|
if (_zeroTimestamp == 0L)
|
||||||
|
{
|
||||||
|
_zeroTimestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentSecs = (int)TimeProvider.System.GetElapsedTime(_zeroTimestamp, timestamp).TotalSeconds;
|
||||||
|
|
||||||
|
Sample sample;
|
||||||
|
|
||||||
|
if (_previousSample == null)
|
||||||
|
{
|
||||||
|
sample = new Sample(timestamp, true)
|
||||||
|
{
|
||||||
|
Speed = 0,
|
||||||
|
SpeedMin = 30,
|
||||||
|
SpeedMax = 150,
|
||||||
|
Active = true,
|
||||||
|
Consigne = 30,
|
||||||
|
ServoMin = 1160,
|
||||||
|
ServoMax = 1600,
|
||||||
|
ServoNeutre = 1200,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var previousSecs = (int)TimeProvider.System
|
||||||
|
.GetElapsedTime(_zeroTimestamp, _previousSample.Timestamp).TotalSeconds;
|
||||||
|
|
||||||
|
sample = _previousSample.Copy(timestamp, previousSecs != currentSecs);
|
||||||
|
|
||||||
|
sample.Speed += 1f;
|
||||||
|
if (sample.Speed > 240)
|
||||||
|
{
|
||||||
|
sample.Speed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sample.Consigne += 0.1f;
|
||||||
|
if (sample.Consigne > sample.SpeedMax)
|
||||||
|
{
|
||||||
|
sample.Consigne = sample.SpeedMin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sample.Active = currentSecs / 3 % 3 == 0;
|
||||||
|
sample.BrakeFlag = currentSecs % 2 == 0;
|
||||||
|
|
||||||
|
_previousSample = sample;
|
||||||
|
|
||||||
|
foreach (var observer in _sampleObservers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
observer.OnNext(sample);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("An exception occurred in a packet handler");
|
||||||
|
Console.Error.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("An exception occurred in the serial read loop");
|
||||||
|
Console.Error.WriteLine(ex.Message);
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IObservable
|
||||||
|
|
||||||
|
IDisposable IObservable<Sample>.Subscribe(IObserver<Sample> observer)
|
||||||
|
{
|
||||||
|
return AddSerialObserver(observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IObserver
|
||||||
|
|
||||||
|
void IObserver<string>.OnCompleted()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void IObserver<string>.OnError(Exception error)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void IObserver<string>.OnNext(string value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
private bool _disposedValue;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposedValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Close();
|
||||||
|
_sampleObservers.Clear();
|
||||||
|
|
||||||
|
_disposedValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
73
src/Buffer.cs
Normal file
73
src/Buffer.cs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace CruiseControllerMX5;
|
||||||
|
|
||||||
|
public static class Buffer
|
||||||
|
{
|
||||||
|
public static IBuffer<T> CreateBounded<T>(int capacity)
|
||||||
|
{
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(capacity);
|
||||||
|
|
||||||
|
return new BoundedBuffer<T>(capacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IBuffer<T> : IEnumerable<T>
|
||||||
|
{
|
||||||
|
int Capacity { get; }
|
||||||
|
|
||||||
|
int Size { get; }
|
||||||
|
|
||||||
|
T? Get();
|
||||||
|
|
||||||
|
T? Get(int index);
|
||||||
|
|
||||||
|
void Add(T value);
|
||||||
|
|
||||||
|
void Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
file class BoundedBuffer<T>(int capacity) : IBuffer<T>
|
||||||
|
{
|
||||||
|
private readonly LinkedList<T> _buffer = [];
|
||||||
|
|
||||||
|
public int Capacity { get; } = capacity;
|
||||||
|
|
||||||
|
public int Size => _buffer.Count;
|
||||||
|
|
||||||
|
public T? Get()
|
||||||
|
{
|
||||||
|
var head = _buffer.First;
|
||||||
|
return head is null ? default : head.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T? Get(int index)
|
||||||
|
{
|
||||||
|
var node = _buffer.First;
|
||||||
|
if (node is null) return default;
|
||||||
|
|
||||||
|
for (var i = 0; i < index; i++)
|
||||||
|
{
|
||||||
|
node = node.Next;
|
||||||
|
if (node is null) return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(T value)
|
||||||
|
{
|
||||||
|
_buffer.AddFirst(value);
|
||||||
|
|
||||||
|
if (Size > Capacity)
|
||||||
|
{
|
||||||
|
_buffer.RemoveLast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear() => _buffer.Clear();
|
||||||
|
|
||||||
|
public IEnumerator<T> GetEnumerator() => _buffer.GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_buffer).GetEnumerator();
|
||||||
|
}
|
||||||
6
src/Disposer.cs
Normal file
6
src/Disposer.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace CruiseControllerMX5;
|
||||||
|
|
||||||
|
public sealed class Disposer(Action callback) : IDisposable
|
||||||
|
{
|
||||||
|
public void Dispose() => callback();
|
||||||
|
}
|
||||||
296
src/DrawContext.cs
Normal file
296
src/DrawContext.cs
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
using System.Drawing.Drawing2D;
|
||||||
|
|
||||||
|
namespace CruiseControllerMX5;
|
||||||
|
|
||||||
|
public sealed class DrawContext(Graphics g) : IDisposable
|
||||||
|
{
|
||||||
|
private readonly Stack<Context> _contexts = new();
|
||||||
|
|
||||||
|
// private Font font = (Font)SystemFonts.DefaultFont.Clone();
|
||||||
|
private EllipseMode _ellipseMode = EllipseMode.Default;
|
||||||
|
private RectMode _rectMode = RectMode.Default;
|
||||||
|
private bool _noStroke, _noFill;
|
||||||
|
|
||||||
|
public Graphics Graphics { get; } = g;
|
||||||
|
|
||||||
|
public Pen DrawPen { get; private set; } = new(Color.White)
|
||||||
|
{
|
||||||
|
StartCap = LineCap.Round,
|
||||||
|
EndCap = LineCap.Round,
|
||||||
|
DashStyle = DashStyle.Solid,
|
||||||
|
DashCap = DashCap.Round
|
||||||
|
};
|
||||||
|
|
||||||
|
public Brush FillBrush { get; private set; } = new SolidBrush(Color.White);
|
||||||
|
|
||||||
|
public StringFormat TextAlignment { get; private set; } = new();
|
||||||
|
|
||||||
|
public void Push()
|
||||||
|
{
|
||||||
|
_contexts.Push(new Context(Graphics.Save(), (Pen)DrawPen.Clone(), (Brush)FillBrush.Clone(),
|
||||||
|
(StringFormat)TextAlignment.Clone(), _ellipseMode, _rectMode, _noStroke, _noFill));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Pop()
|
||||||
|
{
|
||||||
|
if (_contexts.Count > 0)
|
||||||
|
{
|
||||||
|
var ctx = _contexts.Pop();
|
||||||
|
Graphics.Restore(ctx.GraphicsState);
|
||||||
|
DrawPen.Dispose();
|
||||||
|
DrawPen = ctx.DrawPen;
|
||||||
|
FillBrush.Dispose();
|
||||||
|
FillBrush = ctx.FillBrush;
|
||||||
|
TextAlignment.Dispose();
|
||||||
|
TextAlignment = ctx.StringFormat;
|
||||||
|
_noStroke = ctx.NoStroke;
|
||||||
|
_noFill = ctx.NoFill;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stroke(int gray) => Stroke(gray, gray, gray);
|
||||||
|
public void Stroke(int alpha, int gray) => Stroke(alpha, gray, gray, gray);
|
||||||
|
public void Stroke(int red, int green, int blue) => Stroke(Color.FromArgb(red, green, blue));
|
||||||
|
public void Stroke(int alpha, int red, int green, int blue) => Stroke(Color.FromArgb(alpha, red, green, blue));
|
||||||
|
|
||||||
|
public void Stroke(Color color)
|
||||||
|
{
|
||||||
|
DrawPen.Color = color;
|
||||||
|
_noStroke = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StrokeWeight(float strokeWeight)
|
||||||
|
{
|
||||||
|
DrawPen.Width = strokeWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StrokeCap(LineCap cap)
|
||||||
|
{
|
||||||
|
DrawPen.StartCap = cap;
|
||||||
|
DrawPen.EndCap = cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Fill(int gray) => Fill(gray, gray, gray);
|
||||||
|
public void Fill(int alpha, int gray) => Fill(alpha, gray, gray, gray);
|
||||||
|
public void Fill(int red, int green, int blue) => Fill(Color.FromArgb(red, green, blue));
|
||||||
|
public void Fill(int alpha, int red, int green, int blue) => Fill(Color.FromArgb(alpha, red, green, blue));
|
||||||
|
|
||||||
|
public void Fill(Color color)
|
||||||
|
{
|
||||||
|
FillBrush.Dispose();
|
||||||
|
FillBrush = new SolidBrush(color);
|
||||||
|
_noFill = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NoStroke()
|
||||||
|
{
|
||||||
|
_noStroke = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NoFill()
|
||||||
|
{
|
||||||
|
_noFill = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetDashes()
|
||||||
|
{
|
||||||
|
DrawPen.DashStyle = DashStyle.Solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDashes(float filled, float empty)
|
||||||
|
{
|
||||||
|
DrawPen.DashStyle = DashStyle.Custom;
|
||||||
|
DrawPen.DashPattern = [filled, empty];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetTextAlign() => SetTextAlign(TextHorizontalAlignment.Default, TextVerticalAlignment.Default);
|
||||||
|
|
||||||
|
public void SetTextAlign(TextHorizontalAlignment hAlign = TextHorizontalAlignment.Unspecified,
|
||||||
|
TextVerticalAlignment vAlign = TextVerticalAlignment.Unspecified)
|
||||||
|
{
|
||||||
|
if (hAlign is not TextHorizontalAlignment.Unspecified)
|
||||||
|
{
|
||||||
|
TextAlignment.Alignment = hAlign switch
|
||||||
|
{
|
||||||
|
TextHorizontalAlignment.Center => StringAlignment.Center,
|
||||||
|
TextHorizontalAlignment.Left => StringAlignment.Near,
|
||||||
|
TextHorizontalAlignment.Right => StringAlignment.Far,
|
||||||
|
_ => throw new ArgumentException("Invalid horizontal alignment", nameof(hAlign))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vAlign is not TextVerticalAlignment.Unspecified)
|
||||||
|
{
|
||||||
|
TextAlignment.LineAlignment = vAlign switch
|
||||||
|
{
|
||||||
|
TextVerticalAlignment.Center => StringAlignment.Center,
|
||||||
|
TextVerticalAlignment.Top => StringAlignment.Near,
|
||||||
|
TextVerticalAlignment.Bottom => StringAlignment.Far,
|
||||||
|
_ => throw new ArgumentException("Invalid vertical alignment", nameof(vAlign))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetEllipseMode(EllipseMode mode)
|
||||||
|
{
|
||||||
|
_ellipseMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRectMode(RectMode mode)
|
||||||
|
{
|
||||||
|
_rectMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public void Arc(float x, float y, float width, float height, float startAngle, float sweepAngle)
|
||||||
|
// {
|
||||||
|
// g.DrawArc(drawPen, RectangleExtensions.FromCenter((int)speedGaugeX, (int)speedGaugeY, speedGaugeRect.Width - 16, speedGaugeRect.Height - 16), speedGaugeStartDeg, speedGaugeEndDeg);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public void Rect(int x, int y, int width, int height)
|
||||||
|
// {
|
||||||
|
// if (!noFill)
|
||||||
|
// {
|
||||||
|
// g.FillRectangle(fillBrush, x, y, width, height);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!noStroke)
|
||||||
|
// {
|
||||||
|
// g.DrawRectangle(DrawPen, x, y, width, height);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public void Rect(float x, float y, float width, float height)
|
||||||
|
// {
|
||||||
|
// if (!noFill)
|
||||||
|
// {
|
||||||
|
// g.FillRectangle(fillBrush, x, y, width, height);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!noStroke)
|
||||||
|
// {
|
||||||
|
// g.DrawRectangle(DrawPen, x, y, width, height);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public void Circle(int x, int y, int width, int height)
|
||||||
|
// {
|
||||||
|
// if (!noFill)
|
||||||
|
// {
|
||||||
|
// g.FillEllipse(fillBrush, x, y, width, height);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!noStroke)
|
||||||
|
// {
|
||||||
|
// g.DrawEllipse(DrawPen, x, y, width, height);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public void Circle(float x, float y, float width, float height)
|
||||||
|
// {
|
||||||
|
// if (!noFill)
|
||||||
|
// {
|
||||||
|
// g.FillRectangle(fillBrush, x, y, width, height);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!noStroke)
|
||||||
|
// {
|
||||||
|
// g.DrawRectangle(DrawPen, x, y, width, height);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
private bool _disposedValue;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposedValue) return;
|
||||||
|
|
||||||
|
DrawPen.Dispose();
|
||||||
|
FillBrush.Dispose();
|
||||||
|
TextAlignment.Dispose();
|
||||||
|
|
||||||
|
_disposedValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public enum TextHorizontalAlignment
|
||||||
|
{
|
||||||
|
Unspecified,
|
||||||
|
Center,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Default = Left
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TextVerticalAlignment
|
||||||
|
{
|
||||||
|
Unspecified,
|
||||||
|
Center,
|
||||||
|
Top,
|
||||||
|
Bottom,
|
||||||
|
Default = Center
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum EllipseMode
|
||||||
|
{
|
||||||
|
Center,
|
||||||
|
Radius,
|
||||||
|
Corner,
|
||||||
|
Corners,
|
||||||
|
Default = Center
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum RectMode
|
||||||
|
{
|
||||||
|
Center,
|
||||||
|
Radius,
|
||||||
|
Corner,
|
||||||
|
Corners,
|
||||||
|
Default = Corner
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Context(
|
||||||
|
GraphicsState graphicsState,
|
||||||
|
Pen drawPen,
|
||||||
|
Brush fillBrush,
|
||||||
|
StringFormat sf,
|
||||||
|
EllipseMode ellipseMode,
|
||||||
|
RectMode rectMode,
|
||||||
|
bool noStroke,
|
||||||
|
bool noFill) : IDisposable
|
||||||
|
{
|
||||||
|
public GraphicsState GraphicsState { get; } = graphicsState;
|
||||||
|
public Pen DrawPen { get; } = drawPen;
|
||||||
|
public Brush FillBrush { get; } = fillBrush;
|
||||||
|
public StringFormat StringFormat { get; } = sf;
|
||||||
|
public bool NoStroke { get; } = noStroke;
|
||||||
|
public bool NoFill { get; } = noFill;
|
||||||
|
public EllipseMode EllipseMode { get; } = ellipseMode;
|
||||||
|
public RectMode RectMode { get; } = rectMode;
|
||||||
|
|
||||||
|
private bool _disposedValue;
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposedValue)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
DrawPen.Dispose();
|
||||||
|
FillBrush.Dispose();
|
||||||
|
StringFormat.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/Listener.cs
Normal file
15
src/Listener.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
namespace CruiseControllerMX5;
|
||||||
|
|
||||||
|
public static class Listener
|
||||||
|
{
|
||||||
|
public static Listener<T> Create<T>(Action<T> callback) => new(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Listener<T>(Action<T> callback) : IObserver<T>
|
||||||
|
{
|
||||||
|
public void OnCompleted() { }
|
||||||
|
|
||||||
|
public void OnError(Exception error) { }
|
||||||
|
|
||||||
|
public void OnNext(T value) => callback(value);
|
||||||
|
}
|
||||||
16
src/Program.cs
Normal file
16
src/Program.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
namespace CruiseControllerMX5;
|
||||||
|
|
||||||
|
static class Program
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The main entry point for the application.
|
||||||
|
/// </summary>
|
||||||
|
[STAThread]
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
// To customize application configuration such as set high DPI settings or default font,
|
||||||
|
// see https://aka.ms/applicationconfiguration.
|
||||||
|
ApplicationConfiguration.Initialize();
|
||||||
|
Application.Run(new App());
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/RectangleExtensions.cs
Normal file
60
src/RectangleExtensions.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
namespace CruiseControllerMX5;
|
||||||
|
|
||||||
|
public static class RectangleExtensions
|
||||||
|
{
|
||||||
|
public static (Rectangle left, Rectangle right) SplitVerticallyAt(this Rectangle rect, int x) => (
|
||||||
|
rect with { Width = x - rect.Left }, rect with { X = x, Width = rect.Right - x });
|
||||||
|
|
||||||
|
public static (Rectangle left, Rectangle right) SplitHorizontallyAt(this Rectangle rect, int y) => (
|
||||||
|
rect with { Height = y - rect.Bottom }, rect with { Y = y, Height = rect.Bottom - y });
|
||||||
|
|
||||||
|
public static (Rectangle left, Rectangle right) SplitVertically(this Rectangle rect, int w) => (
|
||||||
|
rect with { Width = w }, rect with { X = rect.X + w, Width = rect.Width - w });
|
||||||
|
|
||||||
|
public static (Rectangle left, Rectangle right) SplitHorizontally(this Rectangle rect, int h) => (
|
||||||
|
rect with { Height = h }, rect with { Y = rect.Y + h, Height = rect.Height - h });
|
||||||
|
|
||||||
|
public static Rectangle[] SplitVerticallyInto(this Rectangle rect, int n)
|
||||||
|
{
|
||||||
|
var result = new Rectangle[n];
|
||||||
|
SplitVerticallyInto(rect, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SplitVerticallyInto(this Rectangle rect, Span<Rectangle> rects)
|
||||||
|
{
|
||||||
|
var n = rects.Length;
|
||||||
|
var w = (float)rect.Width / n;
|
||||||
|
|
||||||
|
for (var i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
rects[i] = rect with { X = (int)(i * w), Width = (int)w };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Rectangle[] SplitHorizontallyInto(this Rectangle rect, int n)
|
||||||
|
{
|
||||||
|
var result = new Rectangle[n];
|
||||||
|
SplitHorizontallyInto(rect, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SplitHorizontallyInto(this Rectangle rect, Span<Rectangle> rects)
|
||||||
|
{
|
||||||
|
var n = rects.Length;
|
||||||
|
var h = (float)rect.Height / n;
|
||||||
|
|
||||||
|
for (var i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
rects[i] = rect with { Y = (int)(i * h), Height = (int)h };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PointF GetCenter(this Rectangle rect) => new(rect.X + rect.Width / 2F, rect.Y + rect.Height / 2F);
|
||||||
|
|
||||||
|
public static Rectangle Centered(Point center, int width, int height) =>
|
||||||
|
Centered(center.X, center.Y, width, height);
|
||||||
|
|
||||||
|
public static Rectangle Centered(int centerX, int centerY, int width, int height) =>
|
||||||
|
new(centerX - width / 2, centerY - height / 2, width, height);
|
||||||
|
}
|
||||||
71
src/RectangleFExtensions.cs
Normal file
71
src/RectangleFExtensions.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
namespace CruiseControllerMX5;
|
||||||
|
|
||||||
|
public static class RectangleFExtensions
|
||||||
|
{
|
||||||
|
public static (RectangleF left, RectangleF right) SplitVerticallyAt(this RectangleF rect, int x) => (
|
||||||
|
rect with { Width = x - rect.Left }, rect with { X = x, Width = rect.Right - x });
|
||||||
|
|
||||||
|
public static (RectangleF left, RectangleF right) SplitHorizontallyAt(this RectangleF rect, int y) => (
|
||||||
|
rect with { Height = y - rect.Bottom }, rect with { Y = y, Height = rect.Bottom - y });
|
||||||
|
|
||||||
|
public static (RectangleF left, RectangleF right) SplitVertically(this RectangleF rect, int w) => (
|
||||||
|
rect with { Width = w }, rect with { X = rect.X + w, Width = rect.Width - w });
|
||||||
|
|
||||||
|
public static (RectangleF left, RectangleF right) SplitHorizontally(this RectangleF rect, int h) => (
|
||||||
|
rect with { Height = h }, rect with { Y = rect.Y + h, Height = rect.Height - h });
|
||||||
|
|
||||||
|
public static RectangleF[] SplitVerticallyInto(this RectangleF rect, int n)
|
||||||
|
{
|
||||||
|
var result = new RectangleF[n];
|
||||||
|
SplitVerticallyInto(rect, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SplitVerticallyInto(this RectangleF rect, Span<RectangleF> rects)
|
||||||
|
{
|
||||||
|
var n = rects.Length;
|
||||||
|
var w = rect.Width / n;
|
||||||
|
|
||||||
|
for (var i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
rects[i] = rect with { X = i * w, Width = w };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RectangleF[] SplitHorizontallyInto(this RectangleF rect, int n)
|
||||||
|
{
|
||||||
|
var result = new RectangleF[n];
|
||||||
|
SplitHorizontallyInto(rect, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SplitHorizontallyInto(this RectangleF rect, Span<RectangleF> rects)
|
||||||
|
{
|
||||||
|
var n = rects.Length;
|
||||||
|
var h = rect.Height / n;
|
||||||
|
|
||||||
|
for (var i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
rects[i] = rect with { Y = i * h, Height = h };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float GetCenterX(this RectangleF rect) => rect.X + rect.Width / 2F;
|
||||||
|
public static float GetCenterY(this RectangleF rect) => rect.Y + rect.Height / 2F;
|
||||||
|
public static PointF GetCenter(this RectangleF rect) => new(GetCenterX(rect), GetCenterY(rect));
|
||||||
|
|
||||||
|
public static RectangleF Centered(PointF center, float width, float height) =>
|
||||||
|
Centered(center.X, center.Y, width, height);
|
||||||
|
|
||||||
|
public static RectangleF Centered(float centerX, float centerY, float width, float height) =>
|
||||||
|
new(centerX - width / 2, centerY - height / 2, width, height);
|
||||||
|
|
||||||
|
public static RectangleF Scaled(this RectangleF rect, float scale) => Scaled(rect, scale, scale);
|
||||||
|
public static RectangleF Scaled(this RectangleF rect, SizeF scale) => Scaled(rect, scale.Width, scale.Height);
|
||||||
|
|
||||||
|
public static RectangleF Scaled(this RectangleF rect, float scaleX, float scaleY) => new(rect.X * scaleX,
|
||||||
|
rect.Y * scaleY, rect.Width * scaleX, rect.Height * scaleY);
|
||||||
|
|
||||||
|
public static Rectangle Round(this RectangleF rect) =>
|
||||||
|
Rectangle.FromLTRB((int)rect.Left, (int)rect.Top, (int)rect.Right, (int)rect.Bottom);
|
||||||
|
}
|
||||||
33
src/Sample.cs
Normal file
33
src/Sample.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
namespace CruiseControllerMX5;
|
||||||
|
|
||||||
|
public class Sample(long timestamp, bool drawTimeTick)
|
||||||
|
{
|
||||||
|
public long Timestamp { get; private set; } = timestamp;
|
||||||
|
public bool DrawTimeTick { get; private set; } = drawTimeTick;
|
||||||
|
public float Proportional { get; set; }
|
||||||
|
public float Integral { get; set; }
|
||||||
|
public float Derivative { get; set; }
|
||||||
|
public float Speed { get; set; }
|
||||||
|
public float Acceleration { get; set; }
|
||||||
|
public float Consigne { get; set; }
|
||||||
|
public float Error { get; set; }
|
||||||
|
public int Servo { get; set; }
|
||||||
|
public int ServoMin { get; set; }
|
||||||
|
public int ServoMax { get; set; }
|
||||||
|
public int ServoNeutre { get; set; }
|
||||||
|
public bool Active { get; set; }
|
||||||
|
public float Kp { get; set; }
|
||||||
|
public float Ki { get; set; }
|
||||||
|
public float Kd { get; set; }
|
||||||
|
public int SpeedMin { get; set; }
|
||||||
|
public int SpeedMax { get; set; }
|
||||||
|
public bool BrakeFlag { get; set; }
|
||||||
|
|
||||||
|
public Sample Copy(long timestamp, bool drawTimeTick)
|
||||||
|
{
|
||||||
|
var copy = (Sample)MemberwiseClone();
|
||||||
|
copy.Timestamp = timestamp;
|
||||||
|
copy.DrawTimeTick = drawTimeTick;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/Splitter.cs
Normal file
38
src/Splitter.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace CruiseControllerMX5;
|
||||||
|
|
||||||
|
public class Splitter(string str)
|
||||||
|
{
|
||||||
|
private const char Delimiter = ',';
|
||||||
|
|
||||||
|
public delegate T ValueReader<out T>(ReadOnlySpan<char> s);
|
||||||
|
|
||||||
|
private int _pos = 0, _charCount = 0, _nextPos = 0;
|
||||||
|
|
||||||
|
private bool MoveNext()
|
||||||
|
{
|
||||||
|
_pos = _nextPos;
|
||||||
|
var i = str.IndexOf(Delimiter, _pos);
|
||||||
|
(_charCount, _nextPos) = i < 0 ? (str.Length - _pos, str.Length) : (i - _pos, i + 1);
|
||||||
|
|
||||||
|
return HasNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasNext => _pos < str.Length;
|
||||||
|
|
||||||
|
private T Read<T>(ValueReader<T> reader) => MoveNext() ? reader(str.AsSpan(_pos, _charCount)) : throw new InvalidOperationException();
|
||||||
|
|
||||||
|
public string ReadString() => Read(static s => new string(s));
|
||||||
|
|
||||||
|
public bool ReadBool() => Read(static s => s.Length == 1 ? s[0] switch { '0' => false, '1' => true, _ => throw new InvalidDataException() } : throw new InvalidDataException());
|
||||||
|
|
||||||
|
public int ReadInt() => Read(static s => int.Parse(s, provider: CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
|
public uint ReadUInt() => Read(static s => uint.Parse(s, provider: CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
|
public float ReadFloat() => Read(static s => float.Parse(s, provider: CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
|
public TParsable Read<TParsable>() where TParsable: ISpanParsable<TParsable> => Read(static s => TParsable.Parse(s, provider: CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
19
src/Utils.cs
Normal file
19
src/Utils.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace CruiseControllerMX5;
|
||||||
|
|
||||||
|
public static class Utils
|
||||||
|
{
|
||||||
|
public static TTo Map<TFrom, TTo>(TFrom value, TFrom fromMin, TFrom fromMax, TTo toMin, TTo toMax)
|
||||||
|
where TFrom : INumber<TFrom> where TTo : INumber<TTo> =>
|
||||||
|
Map<TFrom, TTo, double>(value, fromMin, fromMax, toMin, toMax);
|
||||||
|
|
||||||
|
public static TTo Map<TFrom, TTo, TInt>(TFrom value, TFrom fromMin, TFrom fromMax, TTo toMin, TTo toMax)
|
||||||
|
where TFrom : INumber<TFrom> where TTo : INumber<TTo> where TInt : INumber<TInt> => TTo.CreateChecked(
|
||||||
|
TInt.CreateChecked(value - fromMin) / TInt.CreateChecked(fromMax - fromMin) *
|
||||||
|
TInt.CreateChecked(toMax - toMin) + TInt.CreateChecked(toMin));
|
||||||
|
|
||||||
|
public static bool IntToBool(string value) => value.Equals("1", StringComparison.Ordinal);
|
||||||
|
|
||||||
|
public static int BoolToInt(bool value) => value ? 1 : 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user