New multiplatform application

This commit is contained in:
Agustín Gimenez 2022-07-31 00:26:07 +02:00
parent 591a904aa9
commit cf6d3b5d13
73 changed files with 1795 additions and 7151 deletions

View File

@ -5,8 +5,13 @@
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ApplicationIcon>window.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Content Include="window.ico" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" /> <PackageReference Include="CommandLineParser" Version="2.9.1" />
</ItemGroup> </ItemGroup>

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@ -3,15 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148 VisualStudioVersion = 17.0.32014.148
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogicAnalyzer", "LogicAnalyzer\LogicAnalyzer.csproj", "{64C09858-40D1-4752-B800-86B4C725A1A3}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedDriver", "SharedDriver\SharedDriver.csproj", "{544DB7FA-C828-48A1-922D-F63E2BD479F9}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SPIProtocolAnalyzer", "SPIProtocolAnalyzer\SPIProtocolAnalyzer.csproj", "{C2166CC8-3BD1-4563-BB9F-0001632B449A}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CLCapture", "CLCapture\CLCapture.csproj", "{AA68B9F9-ABFC-443C-9304-E06F264840F6}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogicAnalyzerMultiplatform", "LogicAnalyzerMultiplatform\LogicAnalyzerMultiplatform.csproj", "{713605B4-A2B7-4254-91C3-FC2A4F5653D8}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LogicAnalyzer", "LogicAnalyzer\LogicAnalyzer.csproj", "{EA087A8F-181D-4600-ACC3-A48270D018EE}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedDriver", "SharedDriver\SharedDriver.csproj", "{544DB7FA-C828-48A1-922D-F63E2BD479F9}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SPIProtocolAnalyzer", "SPIProtocolAnalyzer\SPIProtocolAnalyzer.csproj", "{6F18CFDA-7596-47F7-ABB9-AB09DC003C0B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLCapture", "CLCapture\CLCapture.csproj", "{AA68B9F9-ABFC-443C-9304-E06F264840F6}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -19,18 +17,6 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{64C09858-40D1-4752-B800-86B4C725A1A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{64C09858-40D1-4752-B800-86B4C725A1A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{64C09858-40D1-4752-B800-86B4C725A1A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{64C09858-40D1-4752-B800-86B4C725A1A3}.Release|Any CPU.Build.0 = Release|Any CPU
{C2166CC8-3BD1-4563-BB9F-0001632B449A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C2166CC8-3BD1-4563-BB9F-0001632B449A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2166CC8-3BD1-4563-BB9F-0001632B449A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2166CC8-3BD1-4563-BB9F-0001632B449A}.Release|Any CPU.Build.0 = Release|Any CPU
{713605B4-A2B7-4254-91C3-FC2A4F5653D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{713605B4-A2B7-4254-91C3-FC2A4F5653D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{713605B4-A2B7-4254-91C3-FC2A4F5653D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{713605B4-A2B7-4254-91C3-FC2A4F5653D8}.Release|Any CPU.Build.0 = Release|Any CPU
{544DB7FA-C828-48A1-922D-F63E2BD479F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {544DB7FA-C828-48A1-922D-F63E2BD479F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{544DB7FA-C828-48A1-922D-F63E2BD479F9}.Debug|Any CPU.Build.0 = Debug|Any CPU {544DB7FA-C828-48A1-922D-F63E2BD479F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{544DB7FA-C828-48A1-922D-F63E2BD479F9}.Release|Any CPU.ActiveCfg = Release|Any CPU {544DB7FA-C828-48A1-922D-F63E2BD479F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -39,6 +25,14 @@ Global
{AA68B9F9-ABFC-443C-9304-E06F264840F6}.Debug|Any CPU.Build.0 = Debug|Any CPU {AA68B9F9-ABFC-443C-9304-E06F264840F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA68B9F9-ABFC-443C-9304-E06F264840F6}.Release|Any CPU.ActiveCfg = Release|Any CPU {AA68B9F9-ABFC-443C-9304-E06F264840F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA68B9F9-ABFC-443C-9304-E06F264840F6}.Release|Any CPU.Build.0 = Release|Any CPU {AA68B9F9-ABFC-443C-9304-E06F264840F6}.Release|Any CPU.Build.0 = Release|Any CPU
{EA087A8F-181D-4600-ACC3-A48270D018EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA087A8F-181D-4600-ACC3-A48270D018EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA087A8F-181D-4600-ACC3-A48270D018EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA087A8F-181D-4600-ACC3-A48270D018EE}.Release|Any CPU.Build.0 = Release|Any CPU
{6F18CFDA-7596-47F7-ABB9-AB09DC003C0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6F18CFDA-7596-47F7-ABB9-AB09DC003C0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6F18CFDA-7596-47F7-ABB9-AB09DC003C0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6F18CFDA-7596-47F7-ABB9-AB09DC003C0B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -1,6 +1,6 @@
<Application xmlns="https://github.com/avaloniaui" <Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="LogicAnalyzerMultiplatform.App"> x:Class="LogicAnalyzer.App">
<Application.Styles> <Application.Styles>
<FluentTheme Mode="Dark"/> <FluentTheme Mode="Dark"/>
</Application.Styles> </Application.Styles>

View File

@ -2,7 +2,7 @@ using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
namespace LogicAnalyzerMultiplatform namespace LogicAnalyzer
{ {
public partial class App : Application public partial class App : Application
{ {

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@ -5,14 +5,14 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace LogicAnalyzerMultiplatform.Classes namespace LogicAnalyzer.Classes
{ {
public static class AnalyzerColors public static class AnalyzerColors
{ {
public static Color[] BgChannelColors => new Color[] public static Color[] BgChannelColors => new Color[]
{ {
Color.FromRgb(32,32,32), Color.FromArgb(160, 32,32,32),
Color.FromRgb(64,64, 64), Color.FromArgb(160, 64,64, 64),
}; };
public static Color[] FgChannelColors => new Color[] public static Color[] FgChannelColors => new Color[]

View File

@ -1,77 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LogicAnalyzer
{
public static class ChannelColors
{
public static Pen[] ChannelPens = new Pen[24];
public static Brush[] ChannelBrushes = new Brush[24];
public static Brush ChannlBgBrush = new SolidBrush(Color.FromArgb(64, 64, 64));
static ChannelColors()
{
ChannelPens[0] = new Pen(Color.FromArgb(254, 0, 0));
ChannelPens[1] = new Pen(Color.FromArgb(128, 255, 0));
ChannelPens[2] = new Pen(Color.FromArgb(1, 255, 255));
ChannelPens[3] = new Pen(Color.FromArgb(127, 0, 255));
ChannelPens[4] = new Pen(Color.FromArgb(255, 64, 1));
ChannelPens[5] = new Pen(Color.FromArgb(64, 255, 1));
ChannelPens[6] = new Pen(Color.FromArgb(0, 192, 255));
ChannelPens[7] = new Pen(Color.FromArgb(191, 0, 254));
ChannelPens[8] = new Pen(Color.FromArgb(255, 127, 0));
ChannelPens[9] = new Pen(Color.FromArgb(0, 255, 1));
ChannelPens[10] = new Pen(Color.FromArgb(0, 128, 255));
ChannelPens[11] = new Pen(Color.FromArgb(255, 0, 254));
ChannelPens[12] = new Pen(Color.FromArgb(255, 192, 0));
ChannelPens[13] = new Pen(Color.FromArgb(0, 255, 65));
ChannelPens[14] = new Pen(Color.FromArgb(0, 65, 255));
ChannelPens[15] = new Pen(Color.FromArgb(255, 0, 192));
ChannelPens[16] = new Pen(Color.FromArgb(255, 255, 1));
ChannelPens[17] = new Pen(Color.FromArgb(0, 254, 129));
ChannelPens[18] = new Pen(Color.FromArgb(0, 0, 254));
ChannelPens[19] = new Pen(Color.FromArgb(255, 0, 128));
ChannelPens[20] = new Pen(Color.FromArgb(192, 255, 0));
ChannelPens[21] = new Pen(Color.FromArgb(1, 255, 193));
ChannelPens[22] = new Pen(Color.FromArgb(63, 0, 255));
ChannelPens[23] = new Pen(Color.FromArgb(255, 1, 65));
ChannelBrushes[0] = new SolidBrush(Color.FromArgb(254, 0, 0));
ChannelBrushes[1] = new SolidBrush(Color.FromArgb(128, 255, 0));
ChannelBrushes[2] = new SolidBrush(Color.FromArgb(1, 255, 255));
ChannelBrushes[3] = new SolidBrush(Color.FromArgb(127, 0, 255));
ChannelBrushes[4] = new SolidBrush(Color.FromArgb(255, 64, 1));
ChannelBrushes[5] = new SolidBrush(Color.FromArgb(64, 255, 1));
ChannelBrushes[6] = new SolidBrush(Color.FromArgb(0, 192, 255));
ChannelBrushes[7] = new SolidBrush(Color.FromArgb(191, 0, 254));
ChannelBrushes[8] = new SolidBrush(Color.FromArgb(255, 127, 0));
ChannelBrushes[9] = new SolidBrush(Color.FromArgb(0, 255, 1));
ChannelBrushes[10] = new SolidBrush(Color.FromArgb(0, 128, 255));
ChannelBrushes[11] = new SolidBrush(Color.FromArgb(255, 0, 254));
ChannelBrushes[12] = new SolidBrush(Color.FromArgb(255, 192, 0));
ChannelBrushes[13] = new SolidBrush(Color.FromArgb(0, 255, 65));
ChannelBrushes[14] = new SolidBrush(Color.FromArgb(0, 65, 255));
ChannelBrushes[15] = new SolidBrush(Color.FromArgb(255, 0, 192));
ChannelBrushes[16] = new SolidBrush(Color.FromArgb(255, 255, 1));
ChannelBrushes[17] = new SolidBrush(Color.FromArgb(0, 254, 129));
ChannelBrushes[18] = new SolidBrush(Color.FromArgb(0, 0, 254));
ChannelBrushes[19] = new SolidBrush(Color.FromArgb(255, 0, 128));
ChannelBrushes[20] = new SolidBrush(Color.FromArgb(192, 255, 0));
ChannelBrushes[21] = new SolidBrush(Color.FromArgb(1, 255, 193));
ChannelBrushes[22] = new SolidBrush(Color.FromArgb(63, 0, 255));
ChannelBrushes[23] = new SolidBrush(Color.FromArgb(255, 1, 65));
}
}
}

View File

@ -1,4 +1,5 @@
using Newtonsoft.Json; using LogicAnalyzer.Classes;
using Newtonsoft.Json;
using SharedDriver; using SharedDriver;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@ -0,0 +1,54 @@
using Avalonia.Media;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LogicAnalyzer.Classes
{
public static class GraphicObjectsCache
{
static Dictionary<Color, Brush> _brushes = new Dictionary<Color, Brush>();
static Dictionary<string, Pen> _pens = new Dictionary<string, Pen>();
public static Brush GetBrush(Color BrushColor)
{
if(!_brushes.ContainsKey(BrushColor))
{
SolidColorBrush brush = new SolidColorBrush(BrushColor);
_brushes.Add(BrushColor, brush);
}
return _brushes[BrushColor];
}
public static Pen GetPen(Color PenColor, double PenThickness, IDashStyle Style = null)
{
string key = "COLOR" + PenColor.ToString() + PenThickness.ToString() + (Style?.ToString() ?? "");
if (!_pens.ContainsKey(key))
{
Pen pen = new Pen(PenColor.ToUint32(), PenThickness);
pen.DashStyle = Style;
_pens.Add(key, pen);
}
return _pens[key];
}
public static Pen GetPen(IBrush PenBrush, double PenThickness, IDashStyle Style = null)
{
string key = "BRUSH" + PenBrush.GetHashCode().ToString() + PenThickness.ToString() + (Style?.ToString() ?? "");
if (!_pens.ContainsKey(key))
{
Pen pen = new Pen(PenBrush, PenThickness);
pen.DashStyle = Style;
_pens.Add(key, pen);
}
return _pens[key];
}
}
}

View File

@ -1,29 +1,27 @@
using Newtonsoft.Json; using System;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace LogicAnalyzer namespace LogicAnalyzer.Classes
{ {
public class SelectedSampleRegion : IDisposable using Avalonia.Media;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class SelectedSampleRegion
{ {
public int FirstSample { get; set; } public int FirstSample { get; set; }
public int LastSample { get; set; } public int LastSample { get; set; }
public int SampleCount { get { return LastSample - FirstSample; } } public int SampleCount { get { return LastSample - FirstSample; } }
public string RegionName { get; set; } = ""; public string RegionName { get; set; } = "";
public SolidBrush? RegionColor { get; set; } = new SolidBrush(Color.FromArgb(128, Color.White)); public Color RegionColor { get; set; } = Color.FromArgb(128, 255,255,255);
public void Dispose()
{
if (RegionColor != null)
{
RegionColor.Dispose();
RegionColor = null;
}
}
public class SelectedSampleRegionConverter : JsonConverter public class SelectedSampleRegionConverter : JsonConverter
{ {
@ -40,7 +38,7 @@ namespace LogicAnalyzer
if (jObject == null) if (jObject == null)
return null; return null;
return new SelectedSampleRegion { FirstSample = jObject["FirstSample"].Value<int>(), LastSample = jObject["LastSample"].Value<int>(), RegionName = jObject["RegionName"].Value<string>(), RegionColor = new SolidBrush(Color.FromArgb(jObject["A"].Value<byte>(), jObject["R"].Value<byte>(), jObject["G"].Value<byte>(), jObject["B"].Value<byte>())) }; return new SelectedSampleRegion { FirstSample = jObject["FirstSample"].Value<int>(), LastSample = jObject["LastSample"].Value<int>(), RegionName = jObject["RegionName"].Value<string>(), RegionColor = Color.FromArgb(jObject["A"].Value<byte>(), jObject["R"].Value<byte>(), jObject["G"].Value<byte>(), jObject["B"].Value<byte>()) };
} }
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
@ -59,13 +57,13 @@ namespace LogicAnalyzer
writer.WritePropertyName("RegionName"); writer.WritePropertyName("RegionName");
writer.WriteValue(obj.RegionName); writer.WriteValue(obj.RegionName);
writer.WritePropertyName("R"); writer.WritePropertyName("R");
writer.WriteValue(obj.RegionColor?.Color.R); writer.WriteValue(obj.RegionColor.R);
writer.WritePropertyName("G"); writer.WritePropertyName("G");
writer.WriteValue(obj.RegionColor?.Color.G); writer.WriteValue(obj.RegionColor.G);
writer.WritePropertyName("B"); writer.WritePropertyName("B");
writer.WriteValue(obj.RegionColor?.Color.B); writer.WriteValue(obj.RegionColor.B);
writer.WritePropertyName("A"); writer.WritePropertyName("A");
writer.WriteValue(obj.RegionColor?.Color.A); writer.WriteValue(obj.RegionColor.A);
writer.WriteEndObject(); writer.WriteEndObject();
} }
} }

View File

@ -1,36 +0,0 @@
namespace LogicAnalyzer
{
partial class BorderGroupBox
{
/// <summary>
/// Variable del diseñador necesaria.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Limpiar los recursos que se estén usando.
/// </summary>
/// <param name="disposing">true si los recursos administrados se deben desechar; false en caso contrario.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Código generado por el Diseñador de componentes
/// <summary>
/// Método necesario para admitir el Diseñador. No se puede modificar
/// el contenido de este método con el editor de código.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}

View File

@ -1,135 +0,0 @@
using LogicAnalyzer.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LogicAnalyzer
{
public partial class BorderGroupBox : GroupBox
{
private Color _borderColor = Color.Black;
private int _borderWidth = 2;
private int _borderRadius = 5;
private int _textIndent = 10;
StringFormat textFormatCenter = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center };
public BorderGroupBox() : base()
{
InitializeComponent();
SetStyle(
ControlStyles.UserPaint | ControlStyles.ResizeRedraw |
ControlStyles.SupportsTransparentBackColor |
ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer,
true);
}
public BorderGroupBox(int width, int radius, Color color) : base()
{
this._borderWidth = Math.Max(1, width);
this._borderColor = color;
this._borderRadius = Math.Max(0, radius);
InitializeComponent();
}
public Color BorderColor
{
get => this._borderColor;
set
{
this._borderColor = value;
this.Invalidate();
}
}
public int BorderWidth
{
get => this._borderWidth;
set
{
if (value > 0)
{
this._borderWidth = Math.Min(value, 10);
this.Invalidate();
}
}
}
public int BorderRadius
{
get => this._borderRadius;
set
{ // Setting a radius of 0 produces square corners...
if (value >= 0)
{
this._borderRadius = value;
this.Invalidate();
}
}
}
public int LabelIndent
{
get => this._textIndent;
set
{
this._textIndent = value;
this.Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e)
{
DrawGroupBox(e.Graphics);
}
private void DrawGroupBox(Graphics g)
{
Brush textBrush = new SolidBrush(this.ForeColor);
SizeF strSize = g.MeasureString(this.Text, this.Font);
Brush borderBrush = new SolidBrush(this.BorderColor);
Pen borderPen = new Pen(borderBrush, (float)this._borderWidth);
Rectangle rect = new Rectangle(0,
(int)(strSize.Height / 2),
this.Width - 1,
this.Height - (int)(strSize.Height / 2) - 1);
Brush labelBrush = new SolidBrush(this.BackColor);
// Clear text and border
g.Clear(this.BackColor);
g.DrawRoundedRectangle(borderPen, rect.X, rect.Y, rect.Width - 1, rect.Height - 1, (float)this._borderRadius);
/*
// Drawing Border (added "Fix" from Jim Fell, Oct 6, '18)
int rectX = rect.X + this._borderWidth;
int rectHeight = (0 == this._borderWidth % 2) ? rect.Height - this._borderWidth / 2 : rect.Height - 1 - this._borderWidth / 2;
int rectWidth = rect.Width - this._borderWidth;
// NOTE DIFFERENCE: rectX vs rect.X and rectHeight vs rect.Height
g.DrawRoundedRectangle(borderPen, rectX, rect.Y, rectWidth, rectHeight, (float)this._borderRadius);
*/
// Draw text
if (this.Text.Length > 0)
{
// Do some work to ensure we don't put the label outside
// of the box, regardless of what value is assigned to the Indent:
int width = (int)rect.Width, posX;
posX = (this._textIndent < 0) ? Math.Max(0 - width, this._textIndent) : Math.Min(width, this._textIndent);
posX = (posX < 0) ? rect.Width + posX - (int)strSize.Width : posX;
RectangleF rectTxt = new RectangleF(posX, 0, strSize.Width, strSize.Height);
g.FillRectangle(labelBrush, posX, 0, strSize.Width, strSize.Height);
g.DrawString(this.Text, this.Font, textBrush, rectTxt, textFormatCenter);
}
}
}
}

View File

@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,45 +0,0 @@
namespace LogicAnalyzer
{
partial class ChannelViewer
{
/// <summary>
/// Variable del diseñador necesaria.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Limpiar los recursos que se estén usando.
/// </summary>
/// <param name="disposing">true si los recursos administrados se deben desechar; false en caso contrario.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Código generado por el Diseñador de componentes
/// <summary>
/// Método necesario para admitir el Diseñador. No se puede modificar
/// el contenido de este método con el editor de código.
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// ChannelViewer
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Name = "ChannelViewer";
this.Size = new System.Drawing.Size(150, 545);
this.ResumeLayout(false);
}
#endregion
}
}

View File

@ -3,7 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="140" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="140" d:DesignHeight="450"
x:Class="LogicAnalyzerMultiplatform.Controls.ChannelViewer"> x:Class="LogicAnalyzer.Controls.ChannelViewer">
<Grid Name="ChannelGrid"></Grid> <Grid Name="ChannelGrid"></Grid>
</UserControl> </UserControl>

View File

@ -2,11 +2,12 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media; using Avalonia.Media;
using LogicAnalyzerMultiplatform.Classes; using LogicAnalyzer.Classes;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace LogicAnalyzerMultiplatform.Controls namespace LogicAnalyzer.Controls
{ {
public partial class ChannelViewer : UserControl public partial class ChannelViewer : UserControl
{ {
@ -23,6 +24,21 @@ namespace LogicAnalyzerMultiplatform.Controls
} }
} }
public string[] ChannelsText
{
get { return boxes.Select(b => b.Text).ToArray(); }
set
{
if (value == null || channels == null || value.Length != channels.Length)
return;
else
{
for (int buc = 0; buc < value.Length; buc++)
boxes[buc].Text = value[buc];
}
}
}
private void CreateControls() private void CreateControls()
{ {
ChannelGrid.Children.Clear(); ChannelGrid.Children.Clear();
@ -51,7 +67,7 @@ namespace LogicAnalyzerMultiplatform.Controls
newChannelGrid.RowDefinitions = new RowDefinitions("*,*"); newChannelGrid.RowDefinitions = new RowDefinitions("*,*");
newChannelGrid.Background = new SolidColorBrush(AnalyzerColors.BgChannelColors[buc % 2], 0.8f); newChannelGrid.Background = GraphicObjectsCache.GetBrush(AnalyzerColors.BgChannelColors[buc % 2]);
ChannelGrid.Children.Add(newChannelGrid); ChannelGrid.Children.Add(newChannelGrid);
@ -65,7 +81,7 @@ namespace LogicAnalyzerMultiplatform.Controls
newChannelLabel.Text = $"Channel {channels[buc] + 1}"; newChannelLabel.Text = $"Channel {channels[buc] + 1}";
newChannelLabel.Foreground = new SolidColorBrush(AnalyzerColors.FgChannelColors[buc]); newChannelLabel.Foreground = GraphicObjectsCache.GetBrush(AnalyzerColors.FgChannelColors[buc]);
newChannelGrid.Children.Add(newChannelLabel); newChannelGrid.Children.Add(newChannelLabel);
@ -79,8 +95,8 @@ namespace LogicAnalyzerMultiplatform.Controls
newChannelTextbox.HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch; newChannelTextbox.HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch;
newChannelTextbox.Margin = new Thickness(5, 0, 5, 0); newChannelTextbox.Margin = new Thickness(5, 0, 5, 0);
newChannelTextbox.Background = new SolidColorBrush(AnalyzerColors.BgChannelColors[1 - (buc % 2)], 0.8f); newChannelTextbox.Background = GraphicObjectsCache.GetBrush(AnalyzerColors.BgChannelColors[1 - (buc % 2)]);
newChannelTextbox.Foreground = new SolidColorBrush(AnalyzerColors.TxtColor, 1); newChannelTextbox.Foreground = GraphicObjectsCache.GetBrush(AnalyzerColors.TxtColor);
newChannelTextbox.MinHeight = newChannelTextbox.MaxHeight = newChannelTextbox.Height = 18; newChannelTextbox.MinHeight = newChannelTextbox.MaxHeight = newChannelTextbox.Height = 18;
newChannelTextbox.Padding = new Thickness(2); newChannelTextbox.Padding = new Thickness(2);
@ -100,8 +116,11 @@ namespace LogicAnalyzerMultiplatform.Controls
public ChannelViewer() public ChannelViewer()
{ {
InitializeComponent(); InitializeComponent();
Channels = new int[] { 1, 3, 5 };
} }
} }
public class RegionEventArgs : EventArgs
{
public SelectedSampleRegion? Region { get; set; }
}
} }

View File

@ -1,132 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LogicAnalyzer
{
public partial class ChannelViewer : UserControl
{
int[] channels;
List<TextBox> boxes = new List<TextBox>();
Font boxFont = new Font("Segoe UI", 7, FontStyle.Regular);
public int[] Channels
{
get { return channels; }
set { channels = value; CreateBoxes(); this.Invalidate(); }
}
public string[] ChannelsText
{
get { return boxes.Select(b => b.Text).ToArray(); }
set
{
if (value == null || channels == null || value.Length != channels.Length)
return;
else
{
for (int buc = 0; buc < value.Length; buc++)
boxes[buc].Text = value[buc];
}
}
}
StringFormat textFormat = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center };
public ChannelViewer()
{
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.Opaque, true);
}
void CreateBoxes()
{
foreach (var control in boxes)
{
this.Controls.Remove(control);
control.Dispose();
}
boxes.Clear();
if (channels == null)
return;
float channelHeight = this.Height / (float)channels.Length;
float quarterHeight = channelHeight / 4;
for (int i = 0; i < channels.Length; i++)
{
var box = new TextBox();
box.Visible = true;
box.Width = this.Width - 10;
box.Top = (int)(((i + 1) * channelHeight) - (quarterHeight + 7));
box.Left = 5;
box.Height = 12;
box.Anchor = AnchorStyles.Top;
box.BorderStyle = BorderStyle.None;
box.Font = boxFont;
box.AutoSize = false;
box.TextAlign = HorizontalAlignment.Center;
box.BackColor = Color.DimGray;
box.ForeColor = Color.LightGray;
this.Controls.Add(box);
boxes.Add(box);
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SetClip(new Rectangle(Point.Empty, this.Size));
e.Graphics.Clear(Color.Black);
if (channels == null)
return;
float channelHeight = this.Height / (float)channels.Length;
float halfHeight = channelHeight / 2;
for (int chan = 0; chan < channels.Length; chan++)
{
if ((chan % 2) == 1)
{
e.Graphics.FillRectangle(ChannelColors.ChannlBgBrush, new RectangleF(0, chan * channelHeight, this.Width, channelHeight));
}
var rect = new RectangleF(0, channelHeight * chan, this.Width, halfHeight);
e.Graphics.DrawString($"Channel {channels[chan] + 1}", this.Font, ChannelColors.ChannelBrushes[chan], rect, textFormat);
}
}
protected override void OnResize(EventArgs e)
{
if (channels != null && boxes.Count > 0)
{
float channelHeight = this.Height / (float)channels.Length;
float quarterHeight = channelHeight / 4;
for (int i = 0; i < boxes.Count; i++)
{
var box = boxes[i];
box.Width = this.Width - 10;
box.Top = (int)(((i + 1) * channelHeight) - (quarterHeight + 7));
box.Left = 5;
}
}
base.OnResize(e);
}
}
}

View File

@ -1,60 +0,0 @@
<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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,134 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LogicAnalyzer
{
public class DividerLabel : Label
{
#region Members
/// <summary>Spacing between the end of the text and the start of the line.</summary>
private int m_DividerSpacing;
/// <summary>style of the divider line</summary>
private Border3DStyle m_BorderStyle = Border3DStyle.Etched;
/// <summary>whether to auto-size the height based on text height and padding.</summary>
private bool m_AutoSizeHeight = false;
#endregion
#region Accessors
/// <summary>Border style of the line.</summary>
[System.ComponentModel.Category("Appearance")]
public Border3DStyle LineStyle
{
get { return m_BorderStyle; }
set
{
if (value != m_BorderStyle)
{
m_BorderStyle = value;
Invalidate();
}
}
}
/// <summary>Whether to auto-size the height based on the text height and padding.</summary>
[System.ComponentModel.Category("Appearance")]
public bool AutoSizeHeight
{
get { return m_AutoSizeHeight; }
set
{
if (value != m_AutoSizeHeight)
{
m_AutoSizeHeight = value;
if (value)
ApplyAutoHeight();
}
}
}
/// <summary>Spacing between the end of the text and the start of the divider line.</summary>
public int DividerSpacing
{
get { return m_DividerSpacing; }
set
{
if (value != m_DividerSpacing)
{
m_DividerSpacing = value;
Invalidate();
}
}
}
#endregion
#region Methods
/// <summary>OnPaint override</summary>
/// <param name="e"></param>
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Brush brush = new SolidBrush(ForeColor);
SizeF textSize = g.MeasureString(Text, Font, Width);
g.DrawString(Text, Font, brush, Padding.Left, Padding.Top);
int textLeft = Padding.Left + (int)(textSize.Width + DividerSpacing);
if (textLeft < Width)
{
int textTop = Padding.Top + (int)(textSize.Height / 2);
ControlPaint.DrawBorder3D(
g,
textLeft,
textTop,
Width - textLeft,
5,
m_BorderStyle,
Border3DSide.Top);
}
}
/// <summary></summary>
/// <param name="e"></param>
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
ApplyAutoHeight();
}
/// <summary></summary>
/// <param name="e"></param>
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
ApplyAutoHeight();
}
/// <summary></summary>
/// <param name="e"></param>
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
ApplyAutoHeight();
}
/// <summary></summary>
/// <param name="e"></param>
protected override void OnPaddingChanged(EventArgs e)
{
base.OnPaddingChanged(e);
ApplyAutoHeight();
}
/// <summary>apply auto height, if necessary.</summary>
private void ApplyAutoHeight()
{
if (m_AutoSizeHeight)
Height = GetPreferredSize(Size.Empty).Height;
}
#endregion
#region Constructor
/// <summary>default constructor</summary>
public DividerLabel()
{
SetStyle(ControlStyles.DoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.ResizeRedraw, true);
}
#endregion
}
}

View File

@ -1,44 +0,0 @@
namespace LogicAnalyzer
{
partial class SampleMarker
{
/// <summary>
/// Variable del diseñador necesaria.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Limpiar los recursos que se estén usando.
/// </summary>
/// <param name="disposing">true si los recursos administrados se deben desechar; false en caso contrario.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Código generado por el Diseñador de componentes
/// <summary>
/// Método necesario para admitir el Diseñador. No se puede modificar
/// el contenido de este método con el editor de código.
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// SampleMarker
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Name = "SampleMarker";
this.ResumeLayout(false);
}
#endregion
}
}

View File

@ -0,0 +1,12 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="LogicAnalyzer.Controls.SampleMarker" ToolTip.Tip="">
<Panel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<TextBlock Margin="0,0,0,0" Name="lblLeft" VerticalAlignment="Center" HorizontalAlignment="Left"></TextBlock>
<TextBlock Name="lblCenter" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBlock>
<TextBlock Margin="0,0,0,0" Name="lblRight" VerticalAlignment="Center" HorizontalAlignment="Right"></TextBlock>
</Panel>
</UserControl>

View File

@ -0,0 +1,264 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Threading;
using LogicAnalyzer.Classes;
using LogicAnalyzer.Dialogs;
using System;
using System.Collections.Generic;
using System.Linq;
namespace LogicAnalyzer.Controls
{
public partial class SampleMarker : UserControl
{
public static readonly StyledProperty<int> FirstSampleProperty = AvaloniaProperty.Register<SampleMarker, int>(nameof(FirstSample));
public static readonly StyledProperty<int> VisibleSamplesProperty = AvaloniaProperty.Register<SampleMarker, int>(nameof(VisibleSamples));
public static readonly StyledProperty<IBrush> ForegroundProperty = AvaloniaProperty.Register<SampleMarker, IBrush>(nameof(Foreground));
public static readonly StyledProperty<IBrush> BackgroundProperty = AvaloniaProperty.Register<SampleMarker, IBrush>(nameof(Background));
public int FirstSample
{
get { return GetValue(FirstSampleProperty); }
set { SetValue(FirstSampleProperty, value); UpdateValues(); InvalidateVisual(); }
}
public int VisibleSamples
{
get { return GetValue(VisibleSamplesProperty); }
set { SetValue(VisibleSamplesProperty, value); UpdateValues(); InvalidateVisual(); }
}
public new IBrush Foreground
{
get { return GetValue<IBrush>(ForegroundProperty); }
set { SetValue<IBrush>(ForegroundProperty, value); UpdateTextColors(); InvalidateVisual(); }
}
public new IBrush Background
{
get { return GetValue<IBrush>(BackgroundProperty); }
set { SetValue<IBrush>(BackgroundProperty, value); InvalidateVisual(); }
}
public event EventHandler<RegionEventArgs> RegionCreated;
public event EventHandler<RegionEventArgs> RegionDeleted;
List<SelectedSampleRegion> regions = new List<SelectedSampleRegion>();
SelectedSampleRegion? regionUnderConstruction;
public SelectedSampleRegion[] SelectedRegions { get { return regions.ToArray(); } }
public SampleMarker()
{
InitializeComponent();
}
public void AddRegion(SelectedSampleRegion Region)
{
regions.Add(Region);
this.InvalidateVisual();
}
public void AddRegions(IEnumerable<SelectedSampleRegion> Regions)
{
regions.AddRange(Regions);
}
public bool RemoveRegion(SelectedSampleRegion Region)
{
var res = regions.Remove(Region);
if (res)
this.InvalidateVisual();
return res;
}
public void ClearRegions()
{
regions.Clear();
this.InvalidateVisual();
}
void UpdateValues()
{
lblLeft.Text = FirstSample.ToString();
lblCenter.Text = (FirstSample + VisibleSamples / 2).ToString();
lblRight.Text = (FirstSample + VisibleSamples - 1).ToString();
}
void UpdateTextColors()
{
lblLeft.Foreground = Foreground;
lblCenter.Foreground = Foreground;
lblRight.Foreground = Foreground;
}
public override void Render(DrawingContext context)
{
context.FillRectangle(Background, new Rect(0, 0, Bounds.Width, Bounds.Height));
if (VisibleSamples == 0)
return;
double sampleWidth = this.Bounds.Width / VisibleSamples;
double halfWidth = sampleWidth / 2;
double halfHeight = this.Bounds.Height / 2f;
if (regionUnderConstruction != null)
{
double start = (regionUnderConstruction.FirstSample - FirstSample) * sampleWidth;
double end = sampleWidth * regionUnderConstruction.SampleCount;
context.FillRectangle(GraphicObjectsCache.GetBrush(regionUnderConstruction.RegionColor), new Rect(start, 0, end, this.Bounds.Height));
}
if (regions.Count > 0)
{
foreach (var region in regions)
{
double start = (region.FirstSample - FirstSample) * sampleWidth;
double end = sampleWidth * region.SampleCount;
context.FillRectangle(GraphicObjectsCache.GetBrush(region.RegionColor), new Rect(start, 0, end, this.Bounds.Height));
FormattedText text = new FormattedText(region.RegionName, Typeface.Default, 12, TextAlignment.Left, TextWrapping.NoWrap, Size.Infinity);
context.DrawText(GraphicObjectsCache.GetBrush(Colors.White), new Point(start + (end / 2) - (text.Bounds.Width / 2), 5), text);
}
}
//Draw ticks
for (int buc = 0; buc < VisibleSamples; buc++)
{
double x = buc * sampleWidth + halfWidth;
double y1 = halfHeight * 1.5f;
double y2 = this.Bounds.Height;
context.DrawLine(GraphicObjectsCache.GetPen(Foreground, 1), new Point(x, y1), new Point(x, y2));
}
base.Render(context);
}
protected override void OnPointerMoved(PointerEventArgs e)
{
if (VisibleSamples == 0)
ToolTip.SetIsOpen(this, false);
else
{
double sampleWidth = this.Bounds.Width / (float)VisibleSamples;
var pos = e.GetCurrentPoint(this);
int ovrSample = (int)(pos.Position.X / sampleWidth) + FirstSample;
if (regionUnderConstruction != null)
{
regionUnderConstruction.LastSample = ovrSample + 1;
this.InvalidateVisual();
}
else
{
if (ToolTip.GetTip(this)?.ToString() != ovrSample.ToString())
{
ToolTip.SetTip(this, ovrSample.ToString());
ToolTip.SetIsOpen(this, false);
ToolTip.SetShowDelay(this, 0);
ToolTip.SetIsOpen(this, true);
}
}
}
base.OnPointerMoved(e);
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
if (VisibleSamples != 0)
{
var point = e.GetCurrentPoint(this);
if (point.Properties.IsLeftButtonPressed)
{
ToolTip.SetIsOpen(this, false);
double sampleWidth = this.Bounds.Width / (float)VisibleSamples;
var pos = e.GetCurrentPoint(this);
int ovrSample = (int)(pos.Position.X / sampleWidth) + FirstSample;
regionUnderConstruction = new SelectedSampleRegion { FirstSample = ovrSample, LastSample = ovrSample + 1 };
this.InvalidateVisual();
}
}
}
async void ShowDialog(SelectedSampleRegion rgn)
{
var dlg = new SelectedRegionDialog();
dlg.SelectedRegion = rgn;
if (await dlg.ShowDialog<bool>(MainWindow.Instance))
{
if (this.RegionCreated != null)
this.RegionCreated(this, new RegionEventArgs { Region = rgn });
AddRegion(rgn);
}
InvalidateVisual();
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
if (VisibleSamples != 0)
{
var pos = e.GetCurrentPoint(this);
if (regionUnderConstruction != null)
{
double sampleWidth = this.Bounds.Width / (float)VisibleSamples;
int ovrSample = (int)(pos.Position.X / sampleWidth) + FirstSample;
regionUnderConstruction.LastSample = ovrSample + 1;
var rgn = regionUnderConstruction;
regionUnderConstruction = null;
if (rgn.SampleCount > 0)
{
ShowDialog(rgn);
}
else
this.InvalidateVisual();
}
else if (pos.Properties.PointerUpdateKind == PointerUpdateKind.RightButtonReleased)
{
double sampleWidth = this.Bounds.Width / (float)VisibleSamples;
int ovrSample = (int)(pos.Position.X / sampleWidth) + FirstSample;
var toDelete = regions.Where(r => ovrSample >= r.FirstSample && ovrSample < r.LastSample).ToArray();
foreach (var region in toDelete)
{
if (ovrSample >= region.FirstSample && ovrSample < region.LastSample)
{
if (RegionDeleted != null)
RegionDeleted(this, new RegionEventArgs { Region = region });
RemoveRegion(region);
}
}
}
}
}
}
}

View File

@ -1,257 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LogicAnalyzer
{
public partial class SampleMarker : UserControl
{
int firstSample;
public int FirstSample { get { return firstSample; } set { firstSample = value; Invalidate(); } }
int visibleSamples;
public int VisibleSamples { get { return visibleSamples; } set { visibleSamples = value; Invalidate(); } }
StringFormat textFormatLeft = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Near };
StringFormat textFormatRight = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Far };
StringFormat textFormatCenter = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center };
Pen tickPen;
Brush txtBrush;
ToolTip overTooltip = new ToolTip();
SelectedSampleRegion? regionUnderConstruction;
public event EventHandler<RegionEventArgs> RegionCreated;
public event EventHandler<RegionEventArgs> RegionDeleted;
List<SelectedSampleRegion> regions = new List<SelectedSampleRegion>();
public SelectedSampleRegion[] SelectedRegions { get { return regions.ToArray(); } }
public override Color ForeColor
{
get
{
return base.ForeColor;
}
set
{
base.ForeColor = value;
tickPen.Dispose();
tickPen = new Pen(ForeColor);
txtBrush.Dispose();
txtBrush = new SolidBrush(ForeColor);
}
}
public SampleMarker()
{
InitializeComponent();
tickPen = new Pen(ForeColor);
txtBrush = new SolidBrush(ForeColor);
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.Opaque, true);
}
public void AddRegion(SelectedSampleRegion Region)
{
regions.Add(Region);
this.Invalidate();
}
public void AddRegions(IEnumerable<SelectedSampleRegion> Regions)
{
regions.AddRange(Regions);
}
public bool RemoveRegion(SelectedSampleRegion Region)
{
var res = regions.Remove(Region);
if (res)
this.Invalidate();
return res;
}
public void ClearRegions()
{
regions.Clear();
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.Clear(this.BackColor);
if (VisibleSamples == 0)
return;
float sampleWidth = this.Width / (float)visibleSamples;
float halfWidth = sampleWidth / 2;
float halfHeight = this.Height / 2f;
//Draw ticks
for (int buc = 0; buc < visibleSamples; buc++)
{
float x = buc * sampleWidth + halfWidth;
float y1 = halfHeight * 1.5f;
float y2 = this.Height;
e.Graphics.DrawLine(tickPen, x, y1, x, y2);
}
e.Graphics.DrawString(firstSample.ToString(), this.Font, txtBrush, new RectangleF(0, 0, 64, halfHeight * 1.5f), textFormatLeft);
e.Graphics.DrawString((firstSample + visibleSamples - 1).ToString(), this.Font, txtBrush, new RectangleF(this.Width - 64, 0, 64, halfHeight * 1.5f), textFormatRight);
e.Graphics.DrawString((firstSample + visibleSamples / 2).ToString(), this.Font, txtBrush, new RectangleF(this.Width / 2.0f - 32, 0, 64, halfHeight * 1.5f), textFormatCenter);
e.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
if (regionUnderConstruction != null)
{
float start = (regionUnderConstruction.FirstSample - firstSample) * sampleWidth;
float end = sampleWidth * regionUnderConstruction.SampleCount;
e.Graphics.FillRectangle(regionUnderConstruction.RegionColor, new RectangleF(start, 0, end, this.Height));
}
if (regions.Count > 0)
{
foreach (var region in regions)
{
float start = (region.FirstSample - firstSample) * sampleWidth;
float end = sampleWidth * region.SampleCount;
e.Graphics.FillRectangle(region.RegionColor, new RectangleF(start, 0, end, this.Height));
e.Graphics.DrawString(region.RegionName, this.Font, txtBrush, new RectangleF(start, 0, end, halfHeight * 1.5f), textFormatCenter);
}
}
e.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
}
protected override void OnMouseEnter(EventArgs e)
{
if (visibleSamples != 0)
{
float sampleWidth = this.Width / (float)visibleSamples;
var pos = PointToClient(Cursor.Position);
int ovrSample = (int)(pos.X / sampleWidth) + firstSample;
overTooltip.Show(ovrSample.ToString(), this, pos.X, -16, 5000);
}
base.OnMouseEnter(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (visibleSamples != 0)
{
if (e.Button == MouseButtons.Left)
{
overTooltip.Hide(this);
float sampleWidth = this.Width / (float)visibleSamples;
var pos = PointToClient(Cursor.Position);
int ovrSample = (int)(pos.X / sampleWidth) + firstSample;
regionUnderConstruction = new SelectedSampleRegion { FirstSample = ovrSample, LastSample = ovrSample + 1 };
this.Invalidate();
}
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (visibleSamples != 0)
{
if (regionUnderConstruction != null)
{
float sampleWidth = this.Width / (float)visibleSamples;
var pos = PointToClient(Cursor.Position);
int ovrSample = (int)(pos.X / sampleWidth) + firstSample;
regionUnderConstruction.LastSample = ovrSample + 1;
var rgn = regionUnderConstruction;
regionUnderConstruction = null;
if (rgn.SampleCount > 0)
{
using (var dlg = new SelectedRegionDialog())
{
dlg.SelectedRegion = rgn;
if (dlg.ShowDialog() != DialogResult.OK)
rgn.Dispose();
else
{
if (this.RegionCreated != null)
this.RegionCreated(this, new RegionEventArgs { Region = rgn });
AddRegion(rgn);
}
}
}
this.Invalidate();
}
else if (e.Button == MouseButtons.Right)
{
float sampleWidth = this.Width / (float)visibleSamples;
var pos = PointToClient(Cursor.Position);
int ovrSample = (int)(pos.X / sampleWidth) + firstSample;
var toDelete = regions.Where(r => ovrSample >= r.FirstSample && ovrSample < r.LastSample).ToArray();
foreach (var region in toDelete)
{
if (ovrSample >= region.FirstSample && ovrSample < region.LastSample)
{
if(RegionDeleted != null)
RegionDeleted(this, new RegionEventArgs { Region = region});
RemoveRegion(region);
}
}
}
}
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (visibleSamples != 0)
{
float sampleWidth = this.Width / (float)visibleSamples;
var pos = PointToClient(Cursor.Position);
int ovrSample = (int)(pos.X / sampleWidth) + firstSample;
if (regionUnderConstruction != null)
{
regionUnderConstruction.LastSample = ovrSample + 1;
this.Invalidate();
}
else
overTooltip.Show(ovrSample.ToString(), this, pos.X, -16, 5000);
}
base.OnMouseMove(e);
}
protected override void OnMouseLeave(EventArgs e)
{
overTooltip.Hide(this);
base.OnMouseLeave(e);
}
}
public class RegionEventArgs : EventArgs
{
public SelectedSampleRegion Region { get; set; }
}
}

View File

@ -1,60 +0,0 @@
<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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,47 +0,0 @@
namespace LogicAnalyzer
{
partial class SampleViewer
{
/// <summary>
/// Variable del diseñador necesaria.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Limpiar los recursos que se estén usando.
/// </summary>
/// <param name="disposing">true si los recursos administrados se deben desechar; false en caso contrario.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Código generado por el Diseñador de componentes
/// <summary>
/// Método necesario para admitir el Diseñador. No se puede modificar
/// el contenido de este método con el editor de código.
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// SampleViewer
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.DoubleBuffered = true;
this.MinimumSize = new System.Drawing.Size(16, 16);
this.Name = "SampleViewer";
this.Size = new System.Drawing.Size(644, 296);
this.ResumeLayout(false);
}
#endregion
}
}

View File

@ -0,0 +1,7 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="LogicAnalyzer.Controls.SampleViewer">
</UserControl>

View File

@ -0,0 +1,178 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using LogicAnalyzer.Classes;
using LogicAnalyzer.Protocols;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace LogicAnalyzer.Controls
{
public partial class SampleViewer : UserControl
{
public int PreSamples { get; set; }
public uint[] Samples { get; set; }
public int ChannelCount { get; set; }
public int SamplesInScreen { get; set; }
public int FirstSample { get; set; }
bool updating = false;
List<SelectedSampleRegion> regions = new List<SelectedSampleRegion>();
public SelectedSampleRegion[] SelectedRegions { get { return regions.ToArray(); } }
List<ProtocolAnalyzedChannel> analysisData = new List<ProtocolAnalyzedChannel>();
Color sampleLineColor = Color.FromArgb(80,120,120,120);
Color triggerLineColor = Colors.White;
public SampleViewer()
{
InitializeComponent();
}
public void BeginUpdate()
{
updating = true;
}
public void EndUpdate()
{
updating = false;
this.InvalidateVisual();
}
public void AddRegion(SelectedSampleRegion Region)
{
regions.Add(Region);
}
public void AddRegions(IEnumerable<SelectedSampleRegion> Regions)
{
regions.AddRange(Regions);
}
public bool RemoveRegion(SelectedSampleRegion Region)
{
return regions.Remove(Region);
}
public void ClearRegions()
{
regions.Clear();
}
public void AddAnalyzedChannel(ProtocolAnalyzedChannel Data)
{
analysisData.Add(Data);
}
public void AddAnalyzedChannels(IEnumerable<ProtocolAnalyzedChannel> Data)
{
analysisData.AddRange(Data);
}
public bool RemoveAnalyzedChannel(ProtocolAnalyzedChannel Data)
{
return analysisData.Remove(Data);
}
public void ClearAnalyzedChannels()
{
foreach (var data in analysisData)
data.Dispose();
analysisData.Clear();
}
public override void Render(DrawingContext context)
{
base.Render(context);
Rect thisBounds = new Rect(0, 0, Bounds.Width, Bounds.Height);
using (context.PushClip(thisBounds))
{
//context.FillRectangle(GraphicObjectsCache.GetBrush(AnalyzerColors.BgChannelColors[0]), thisBounds);
if (PreSamples == 0 || Samples == null || ChannelCount == 0 || SamplesInScreen == 0 || updating)
return;
double channelHeight = thisBounds.Height / (double)ChannelCount;
double sampleWidth = thisBounds.Width / (double)SamplesInScreen;
double margin = channelHeight / 5;
int lastSample = Math.Min(SamplesInScreen + FirstSample, Samples.Length);
for (int chan = 0; chan < ChannelCount; chan++)
{
context.FillRectangle(GraphicObjectsCache.GetBrush(AnalyzerColors.BgChannelColors[chan % 2]), new Rect(0, chan * channelHeight, thisBounds.Width, channelHeight));
}
if (regions.Count > 0)
{
foreach (var region in regions)
{
double start = (region.FirstSample - FirstSample) * sampleWidth;
double end = sampleWidth * region.SampleCount;
context.FillRectangle(GraphicObjectsCache.GetBrush(region.RegionColor), new Rect(start, 0, end, this.Bounds.Height));
}
}
for (int buc = FirstSample; buc < lastSample; buc++)
{
uint sample = Samples[buc];
uint prevSample = buc == 0 ? 0 : Samples[buc - 1];
double lineX = (buc - FirstSample) * sampleWidth;
context.DrawLine(GraphicObjectsCache.GetPen(sampleLineColor, 1, DashStyle.Dash), new Point(lineX + sampleWidth / 2, 0), new Point(lineX + sampleWidth / 2, thisBounds.Height));
if (buc == PreSamples)
context.DrawLine(GraphicObjectsCache.GetPen(triggerLineColor, 2), new Point(lineX, 0), new Point(lineX, thisBounds.Height));
for (int chan = 0; chan < ChannelCount; chan++)
{
double lineY;
uint curVal = (uint)(sample & (1 << chan));
uint prevVal = (uint)(prevSample & (1 << chan));
if (curVal != 0)
lineY = chan * channelHeight + margin;
else
lineY = (chan + 1) * channelHeight - margin;
context.DrawLine(GraphicObjectsCache.GetPen(AnalyzerColors.FgChannelColors[chan], 1), new Point(lineX, lineY), new Point(lineX + sampleWidth, lineY));
if (curVal != prevVal)
{
lineY = chan * channelHeight + margin;
context.DrawLine(GraphicObjectsCache.GetPen(AnalyzerColors.FgChannelColors[chan], 1), new Point(lineX, lineY), new Point(lineX, lineY + channelHeight - margin * 2));
}
}
}
if (analysisData != null && analysisData.Count > 0)
{
foreach (var channel in analysisData)
{
var overlappingSegments = channel.Segments.Where(s => s.FirstSample <= s.LastSample && s.LastSample >= FirstSample);
if (overlappingSegments.Any())
{
double yStart = channel.ChannelIndex * channelHeight + channelHeight * 0.25f;
double yEnd = channelHeight * 0.5f;
foreach (var segment in overlappingSegments)
{
double xStart = (segment.FirstSample - FirstSample) * sampleWidth;
double xEnd = sampleWidth * (segment.LastSample - segment.FirstSample + 1);
Rect area = new Rect(xStart, yStart, xEnd, yEnd);
channel.Render(segment, context, area);
}
}
}
}
}
}
}
}

View File

@ -1,185 +0,0 @@
using LogicAnalyzer.Protocols;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LogicAnalyzer
{
public partial class SampleViewer : UserControl
{
public int PreSamples { get; set; }
public uint[] Samples { get; set; }
public int ChannelCount { get; set; }
public int SamplesInScreen { get; set; }
public int FirstSample { get; set; }
bool updating = false;
Pen samplePen;
Pen triggerPen = new Pen(Color.White, 2);
List<SelectedSampleRegion> regions = new List<SelectedSampleRegion>();
public SelectedSampleRegion[] SelectedRegions { get { return regions.ToArray(); } }
List<ProtocolAnalyzedChannel> analysisData = new List<ProtocolAnalyzedChannel>();
public SampleViewer()
{
InitializeComponent();
samplePen = new Pen(Color.DarkGray);
samplePen.DashPattern = new float[] { 1, 4 };
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.Opaque, true);
}
public void BeginUpdate()
{
updating = true;
}
public void EndUpdate()
{
updating = false;
this.Invalidate();
}
public void AddRegion(SelectedSampleRegion Region)
{
regions.Add(Region);
}
public void AddRegions(IEnumerable<SelectedSampleRegion> Regions)
{
regions.AddRange(Regions);
}
public bool RemoveRegion(SelectedSampleRegion Region)
{
return regions.Remove(Region);
}
public void ClearRegions()
{
regions.Clear();
}
public void AddAnalyzedChannel(ProtocolAnalyzedChannel Data)
{
analysisData.Add(Data);
}
public void AddAnalyzedChannels(IEnumerable<ProtocolAnalyzedChannel> Data)
{
analysisData.AddRange(Data);
}
public bool RemoveAnalyzedChannel(ProtocolAnalyzedChannel Data)
{
return analysisData.Remove(Data);
}
public void ClearAnalyzedChannels()
{
foreach (var data in analysisData)
data.Dispose();
analysisData.Clear();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SetClip(new Rectangle(Point.Empty, this.Size));
e.Graphics.Clear(Color.Black);
if (PreSamples == 0 || Samples == null || ChannelCount == 0 || SamplesInScreen == 0 || updating)
return;
float channelHeight = this.Height / (float)ChannelCount;
float sampleWidth = this.Width / (float)SamplesInScreen;
float margin = channelHeight / 5;
int lastSample = Math.Min(SamplesInScreen + FirstSample, Samples.Length);
for (int chan = 1; chan < ChannelCount; chan += 2)
{
e.Graphics.FillRectangle(ChannelColors.ChannlBgBrush, new RectangleF(0, chan * channelHeight, this.Width, channelHeight));
}
for (int buc = FirstSample; buc < lastSample; buc++)
{
uint sample = Samples[buc];
uint prevSample = buc == 0 ? 0 : Samples[buc - 1];
float lineX = (buc - FirstSample) * sampleWidth;
e.Graphics.DrawLine(samplePen, lineX + sampleWidth / 2, 0, lineX + sampleWidth / 2, this.Height);
if (buc == PreSamples)
e.Graphics.DrawLine(triggerPen, lineX, 0, lineX, this.Height);
for (int chan = 0; chan < ChannelCount; chan++)
{
float lineY;
uint curVal = (uint)(sample & (1 << chan));
uint prevVal = (uint)(prevSample & (1 << chan));
if (curVal != 0)
lineY = chan * channelHeight + margin;
else
lineY = (chan + 1) * channelHeight - margin;
e.Graphics.DrawLine(ChannelColors.ChannelPens[chan], lineX, lineY, lineX + sampleWidth, lineY);
if (curVal != prevVal)
{
lineY = chan * channelHeight + margin;
e.Graphics.DrawLine(ChannelColors.ChannelPens[chan], lineX, lineY, lineX, lineY + channelHeight - margin * 2);
}
}
}
e.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
if (regions.Count > 0)
{
foreach (var region in regions)
{
float start = (region.FirstSample - FirstSample) * sampleWidth;
float end = sampleWidth * region.SampleCount;
e.Graphics.FillRectangle(region.RegionColor, new RectangleF(start, 0, end, this.Height));
}
}
if (analysisData != null && analysisData.Count > 0)
{
foreach (var channel in analysisData)
{
var overlappingSegments = channel.Segments.Where(s => s.FirstSample <= s.LastSample && s.LastSample >= FirstSample);
if (overlappingSegments.Any())
{
float yStart = channel.ChannelIndex * channelHeight + channelHeight * 0.25f;
float yEnd = channelHeight * 0.5f;
foreach (var segment in overlappingSegments)
{
float xStart = (segment.FirstSample - FirstSample) * sampleWidth;
float xEnd = sampleWidth * (segment.LastSample - segment.FirstSample + 1);
RectangleF area = new RectangleF(xStart, yStart, xEnd, yEnd);
channel.Render(segment, e.Graphics, area);
}
}
}
}
e.Graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
}
}
}

View File

@ -1,60 +0,0 @@
<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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,264 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="660"
MaxWidth="800" MinWidth="800" Width="800" MaxHeight="660" MinHeight="660" Height="660"
CanResize="False" WindowStartupLocation="CenterOwner"
x:Class="LogicAnalyzer.Dialogs.CaptureDialog"
Title="Start capture..."
TransparencyLevelHint="AcrylicBlur"
Background="Transparent" Icon="/Assets/window.ico">
<Panel>
<ExperimentalAcrylicBorder IsHitTestVisible="False">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
BackgroundSource="Digger"
TintColor="Black"
TintOpacity="1"
MaterialOpacity="0.65" />
</ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder>
<Grid RowDefinitions="2*,14*,1*" Margin="10,15,10,15">
<Grid Grid.Row="0" ColumnDefinitions="*,*,*" Margin="10,0,10,0">
<StackPanel Grid.Column="0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Margin="0,0,5,0">Frequency:</TextBlock>
<NumericUpDown Width="170" Height="35" Minimum="3100" Maximum="100000000" Value="100000000" Name="nudFrequency"></NumericUpDown>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock VerticalAlignment="Center" Margin="0,0,5,0">Pre samples:</TextBlock>
<NumericUpDown Width="130" Height="35" Value="512" Minimum="0" Maximum="32767" Name="nudPreSamples"></NumericUpDown>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock VerticalAlignment="Center" Margin="0,0,5,0">Post samples:</TextBlock>
<NumericUpDown Width="130" Height="35" Value="1024" Minimum="0" Maximum="32767" Name="nudPostSamples"></NumericUpDown>
</StackPanel>
</Grid>
<Grid Grid.Row="1" RowDefinitions="2*,5*">
<StackPanel Orientation="Vertical" VerticalAlignment="Top" Margin="10,10,10,0" Background="#80505050">
<TextBlock Margin="15,15,0,0">Capture channels</TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="12,15,0,0">
<TextBlock Width="28">0
1
</TextBlock>
<TextBlock Width="28">0
2</TextBlock>
<TextBlock Width="28">0
3</TextBlock>
<TextBlock Width="28">0
4</TextBlock>
<TextBlock Width="28">0
5</TextBlock>
<TextBlock Width="28">0
6</TextBlock>
<TextBlock Width="28">0
7</TextBlock>
<TextBlock Width="28">0
8</TextBlock>
<TextBlock Width="28">0
9</TextBlock>
<TextBlock Width="28">1
0</TextBlock>
<TextBlock Width="28">1
1</TextBlock>
<TextBlock Width="28">1
2</TextBlock>
<TextBlock Width="28">1
3</TextBlock>
<TextBlock Width="28">1
4</TextBlock>
<TextBlock Width="28">1
5</TextBlock>
<TextBlock Width="28">1
6</TextBlock>
<TextBlock Width="28">1
7</TextBlock>
<TextBlock Width="28">1
8</TextBlock>
<TextBlock Width="28">1
9</TextBlock>
<TextBlock Width="28">2
0</TextBlock>
<TextBlock Width="28">2
1</TextBlock>
<TextBlock Width="28">2
2</TextBlock>
<TextBlock Width="28">2
3</TextBlock>
<TextBlock Width="28">2
4</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,15">
<CheckBox Name="ckCapture1"></CheckBox>
<CheckBox Name="ckCapture2"></CheckBox>
<CheckBox Name="ckCapture3"></CheckBox>
<CheckBox Name="ckCapture4"></CheckBox>
<CheckBox Name="ckCapture5"></CheckBox>
<CheckBox Name="ckCapture6"></CheckBox>
<CheckBox Name="ckCapture7"></CheckBox>
<CheckBox Name="ckCapture8"></CheckBox>
<CheckBox Name="ckCapture9"></CheckBox>
<CheckBox Name="ckCapture10"></CheckBox>
<CheckBox Name="ckCapture11"></CheckBox>
<CheckBox Name="ckCapture12"></CheckBox>
<CheckBox Name="ckCapture13"></CheckBox>
<CheckBox Name="ckCapture14"></CheckBox>
<CheckBox Name="ckCapture15"></CheckBox>
<CheckBox Name="ckCapture16"></CheckBox>
<CheckBox Name="ckCapture17"></CheckBox>
<CheckBox Name="ckCapture18"></CheckBox>
<CheckBox Name="ckCapture19"></CheckBox>
<CheckBox Name="ckCapture20"></CheckBox>
<CheckBox Name="ckCapture21"></CheckBox>
<CheckBox Name="ckCapture22"></CheckBox>
<CheckBox Name="ckCapture23"></CheckBox>
<CheckBox Name="ckCapture24"></CheckBox>
</StackPanel>
</StackPanel>
<StackPanel Grid.Row="1" Margin="10,10,10,10" Background="#80505050">
<TextBlock Margin="15,15,0,0">Trigger</TextBlock>
<RadioButton Margin="15,15,0,0" IsChecked="true" Name="rbTriggerTypeEdge">Edge trigger</RadioButton>
<StackPanel Orientation="Vertical" VerticalAlignment="Top" Margin="10,0,10,10" Background="#80303030" Name="pnlEdge">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="12,15,0,0">
<TextBlock Width="28">
0
1
</TextBlock>
<TextBlock Width="28">
0
2
</TextBlock>
<TextBlock Width="28">
0
3
</TextBlock>
<TextBlock Width="28">
0
4
</TextBlock>
<TextBlock Width="28">
0
5
</TextBlock>
<TextBlock Width="28">
0
6
</TextBlock>
<TextBlock Width="28">
0
7
</TextBlock>
<TextBlock Width="28">
0
8
</TextBlock>
<TextBlock Width="28">
0
9
</TextBlock>
<TextBlock Width="28">
1
0
</TextBlock>
<TextBlock Width="28">
1
1
</TextBlock>
<TextBlock Width="28">
1
2
</TextBlock>
<TextBlock Width="28">
1
3
</TextBlock>
<TextBlock Width="28">
1
4
</TextBlock>
<TextBlock Width="28">
1
5
</TextBlock>
<TextBlock Width="28">
1
6
</TextBlock>
<TextBlock Width="28">
1
7
</TextBlock>
<TextBlock Width="28">
1
8
</TextBlock>
<TextBlock Width="28">
1
9
</TextBlock>
<TextBlock Width="28">
2
0
</TextBlock>
<TextBlock Width="28">
2
1
</TextBlock>
<TextBlock Width="28">
2
2
</TextBlock>
<TextBlock Width="28">
2
3
</TextBlock>
<TextBlock Width="28">
2
4
</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,15">
<RadioButton Name="rbTrigger1" IsChecked="True"></RadioButton>
<RadioButton Name="rbTrigger2"></RadioButton>
<RadioButton Name="rbTrigger3"></RadioButton>
<RadioButton Name="rbTrigger4"></RadioButton>
<RadioButton Name="rbTrigger5"></RadioButton>
<RadioButton Name="rbTrigger6"></RadioButton>
<RadioButton Name="rbTrigger7"></RadioButton>
<RadioButton Name="rbTrigger8"></RadioButton>
<RadioButton Name="rbTrigger9"></RadioButton>
<RadioButton Name="rbTrigger10"></RadioButton>
<RadioButton Name="rbTrigger11"></RadioButton>
<RadioButton Name="rbTrigger12"></RadioButton>
<RadioButton Name="rbTrigger13"></RadioButton>
<RadioButton Name="rbTrigger14"></RadioButton>
<RadioButton Name="rbTrigger15"></RadioButton>
<RadioButton Name="rbTrigger16"></RadioButton>
<RadioButton Name="rbTrigger17"></RadioButton>
<RadioButton Name="rbTrigger18"></RadioButton>
<RadioButton Name="rbTrigger19"></RadioButton>
<RadioButton Name="rbTrigger20"></RadioButton>
<RadioButton Name="rbTrigger21"></RadioButton>
<RadioButton Name="rbTrigger22"></RadioButton>
<RadioButton Name="rbTrigger23"></RadioButton>
<RadioButton Name="rbTrigger24"></RadioButton>
</StackPanel>
<CheckBox Name="ckNegativeTrigger" Margin="10,0,0,10">Negative edge</CheckBox>
</StackPanel>
<RadioButton Margin="15,0,0,0" Name="rbTriggerTypePattern">Pattern trigger</RadioButton>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="10,0,10,10" Background="#80303030" Name="pnlComplex">
<TextBlock VerticalAlignment="Center" Margin="10,0,0,0">First channel:</TextBlock>
<NumericUpDown Margin="5,15,0,15" Value="1" Minimum="1" Maximum="24" Name="nudTriggerBase"></NumericUpDown>
<TextBlock Margin="15,0,10,0" VerticalAlignment="Center">Pattern:</TextBlock>
<TextBox VerticalAlignment="Center" Width="160" Name="txtPattern"></TextBox>
<CheckBox Margin="15,0,10,0" Name="ckFastTrigger">Fast pattern matching (max. 5 bits)</CheckBox>
</StackPanel>
</StackPanel>
</Grid>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right" Grid.Row="2">
<Button Name="btnAccept">Accept</Button>
<Button Name="btnCancel" Margin="10,0,10,0">Cancel</Button>
</StackPanel>
</Grid>
</Panel>
</Window>

View File

@ -1,18 +1,21 @@
using Newtonsoft.Json; using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using LogicAnalyzer.Extensions;
using MessageBox.Avalonia;
using Newtonsoft.Json;
using SharedDriver; using SharedDriver;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.IO;
using System.Data;
using System.Drawing;
using System.Linq; using System.Linq;
using System.Text; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms;
namespace LogicAnalyzer namespace LogicAnalyzer.Dialogs
{ {
public partial class CaptureDialog : Form public partial class CaptureDialog : Window
{ {
CheckBox[] captureChannels; CheckBox[] captureChannels;
RadioButton[] triggerChannels; RadioButton[] triggerChannels;
@ -22,9 +25,16 @@ namespace LogicAnalyzer
public CaptureDialog() public CaptureDialog()
{ {
InitializeComponent(); InitializeComponent();
btnAccept.Click += btnAccept_Click;
btnCancel.Click += btnCancel_Click;
InitializeControlArrays(); InitializeControlArrays();
LoadSettings(); LoadSettings();
} }
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
this.FixStartupPosition();
}
private void InitializeControlArrays() private void InitializeControlArrays()
{ {
@ -33,8 +43,8 @@ namespace LogicAnalyzer
for (int buc = 1; buc < 25; buc++) for (int buc = 1; buc < 25; buc++)
{ {
channels.Add((CheckBox)Controls.Find($"ckCapture{buc}", true).First()); channels.Add(this.FindControl<CheckBox>($"ckCapture{buc}"));
triggers.Add((RadioButton)Controls.Find($"rbTrigger{buc}", true).First()); triggers.Add(this.FindControl<RadioButton>($"rbTrigger{buc}"));
} }
captureChannels = channels.ToArray(); captureChannels = channels.ToArray();
@ -55,29 +65,29 @@ namespace LogicAnalyzer
nudPreSamples.Value = settings.PreTriggerSamples; nudPreSamples.Value = settings.PreTriggerSamples;
nudPostSamples.Value = settings.PostTriggerSamples; nudPostSamples.Value = settings.PostTriggerSamples;
foreach(var channel in settings.CaptureChannels) foreach (var channel in settings.CaptureChannels)
captureChannels[channel].Checked = true; captureChannels[channel].IsChecked = true;
switch (settings.TriggerType) switch (settings.TriggerType)
{ {
case 0: case 0:
rbTriggerTypePattern.Checked = false; rbTriggerTypePattern.IsChecked = false;
rbTriggerTypeEdge.Checked = true; rbTriggerTypeEdge.IsChecked = true;
triggerChannels[settings.TriggerChannel].Checked = true; triggerChannels[settings.TriggerChannel].IsChecked = true;
ckNegativeTrigger.Checked = settings.TriggerInverted; ckNegativeTrigger.IsChecked = settings.TriggerInverted;
rbTriggerTypePattern.Checked = false; rbTriggerTypePattern.IsChecked = false;
rbTriggerTypeEdge.Checked = true; rbTriggerTypeEdge.IsChecked = true;
ckFastTrigger.Checked = false; ckFastTrigger.IsChecked = false;
break; break;
case 1: case 1:
{ {
rbTriggerTypePattern.Checked = true; rbTriggerTypePattern.IsChecked = true;
rbTriggerTypeEdge.Checked = false; rbTriggerTypeEdge.IsChecked = false;
nudTriggerBase.Value = settings.TriggerChannel + 1; nudTriggerBase.Value = settings.TriggerChannel + 1;
string pattern = ""; string pattern = "";
@ -87,14 +97,14 @@ namespace LogicAnalyzer
txtPattern.Text = pattern; txtPattern.Text = pattern;
ckFastTrigger.Checked = false; ckFastTrigger.IsChecked = false;
} }
break; break;
case 2: case 2:
{ {
rbTriggerTypePattern.Checked = true; rbTriggerTypePattern.IsChecked = true;
rbTriggerTypeEdge.Checked = false; rbTriggerTypeEdge.IsChecked = false;
nudTriggerBase.Value = settings.TriggerChannel + 1; nudTriggerBase.Value = settings.TriggerChannel + 1;
string pattern = ""; string pattern = "";
@ -104,18 +114,18 @@ namespace LogicAnalyzer
txtPattern.Text = pattern; txtPattern.Text = pattern;
ckFastTrigger.Checked = true; ckFastTrigger.IsChecked = true;
} }
break; break;
} }
} }
} }
private void btnAccept_Click(object sender, EventArgs e) private async void btnAccept_Click(object? sender, RoutedEventArgs e)
{ {
if (nudPreSamples.Value + nudPostSamples.Value > 32767) if (nudPreSamples.Value + nudPostSamples.Value > 32767)
{ {
MessageBox.Show("Total samples cannot exceed 32767."); await ShowError("Error", "Total samples cannot exceed 32767.");
return; return;
} }
@ -123,13 +133,13 @@ namespace LogicAnalyzer
for (int buc = 0; buc < captureChannels.Length; buc++) for (int buc = 0; buc < captureChannels.Length; buc++)
{ {
if (captureChannels[buc].Checked) if (captureChannels[buc].IsChecked == true)
channelsToCapture.Add(buc); channelsToCapture.Add(buc);
} }
if (channelsToCapture.Count == 0) if (channelsToCapture.Count == 0)
{ {
MessageBox.Show("Select at least one channel to be captured."); await ShowError("Error", "Select at least one channel to be captured.");
return; return;
} }
@ -138,17 +148,17 @@ namespace LogicAnalyzer
UInt16 triggerPattern = 0; UInt16 triggerPattern = 0;
if (rbTriggerTypeEdge.Checked) if (rbTriggerTypeEdge.IsChecked == true)
{ {
for (int buc = 0; buc < triggerChannels.Length; buc++) for (int buc = 0; buc < triggerChannels.Length; buc++)
{ {
if (triggerChannels[buc].Checked) if (triggerChannels[buc].IsChecked == true)
{ {
if (trigger == -1) if (trigger == -1)
trigger = buc; trigger = buc;
else else
{ {
MessageBox.Show("Only one trigger channel supported. How the heck did you managed to select two? ¬¬"); await ShowError("Error", "Only one trigger channel supported.");
return; return;
} }
} }
@ -162,25 +172,25 @@ namespace LogicAnalyzer
if (patternChars.Length == 0) if (patternChars.Length == 0)
{ {
MessageBox.Show("Trigger pattern must be at least one bit long."); await ShowError("Error", "Trigger pattern must be at least one bit long.");
return; return;
} }
if (patternChars.Any(c => c != '0' && c != '1')) if (patternChars.Any(c => c != '0' && c != '1'))
{ {
MessageBox.Show("Trigger patterns must be composed only by 0's and 1's."); await ShowError("Error", "Trigger patterns must be composed only by 0's and 1's.");
return; return;
} }
if ((trigger - 1) + patternChars.Length > 16) if ((trigger - 1) + patternChars.Length > 16)
{ {
MessageBox.Show("Only first 16 channels can be used in a pattern trigger."); await ShowError("Error", "Only first 16 channels can be used in a pattern trigger.");
return; return;
} }
if (ckFastTrigger.Checked && patternChars.Length > 5) if (ckFastTrigger.IsChecked == true && patternChars.Length > 5)
{ {
MessageBox.Show("Fast pattern matching is restricted to 5 channels."); await ShowError("Error", "Fast pattern matching is restricted to 5 channels.");
return; return;
} }
@ -195,7 +205,7 @@ namespace LogicAnalyzer
if (trigger == -1) if (trigger == -1)
{ {
MessageBox.Show("You must select a trigger channel. How the heck did you managed to deselect all? ¬¬"); await ShowError("Error", "You must select a trigger channel. How the heck did you managed to deselect all? ¬¬");
return; return;
} }
@ -205,46 +215,55 @@ namespace LogicAnalyzer
settings.PreTriggerSamples = (int)nudPreSamples.Value; settings.PreTriggerSamples = (int)nudPreSamples.Value;
settings.PostTriggerSamples = (int)nudPostSamples.Value; settings.PostTriggerSamples = (int)nudPostSamples.Value;
settings.TriggerType = rbTriggerTypePattern.Checked ? (ckFastTrigger.Checked ? 2 : 1) : 0; settings.TriggerType = rbTriggerTypePattern.IsChecked == true ? (ckFastTrigger.IsChecked == true ? 2 : 1) : 0;
settings.TriggerPattern = triggerPattern; settings.TriggerPattern = triggerPattern;
settings.TriggerBitCount = triggerBits; settings.TriggerBitCount = triggerBits;
settings.TriggerChannel = trigger; settings.TriggerChannel = trigger;
settings.TriggerInverted = ckNegativeTrigger.Checked; settings.TriggerInverted = ckNegativeTrigger.IsChecked == true;
settings.CaptureChannels = channelsToCapture.ToArray(); settings.CaptureChannels = channelsToCapture.ToArray();
File.WriteAllText("captureSettings.json", JsonConvert.SerializeObject(settings)); File.WriteAllText("captureSettings.json", JsonConvert.SerializeObject(settings));
SelectedSettings = settings; SelectedSettings = settings;
DialogResult = DialogResult.OK; this.Close(true);
this.Close();
} }
private void btnCancel_Click(object sender, EventArgs e) private void btnCancel_Click(object? sender, EventArgs e)
{ {
DialogResult = DialogResult.Cancel; this.Close(false);
this.Close();
} }
private void rbTriggerTypeEdge_CheckedChanged(object sender, EventArgs e) private void rbTriggerTypeEdge_CheckedChanged(object sender, EventArgs e)
{ {
if (rbTriggerTypeEdge.Checked) if (rbTriggerTypeEdge.IsChecked == true)
{ {
gbEdgeTrigger.Enabled = true; pnlEdge.IsEnabled = true;
gbPatternTrigger.Enabled = false; pnlComplex.IsEnabled = false;
} }
else else
{ {
gbEdgeTrigger.Enabled = false; pnlEdge.IsEnabled = false;
gbPatternTrigger.Enabled = true; pnlComplex.IsEnabled = true;
} }
} }
private void ckFastTrigger_CheckedChanged(object sender, EventArgs e) private void ckFastTrigger_CheckedChanged(object sender, EventArgs e)
{ {
if (ckFastTrigger.Checked) if (ckFastTrigger.IsChecked == true)
txtPattern.MaxLength = 5; txtPattern.MaxLength = 5;
else else
txtPattern.MaxLength = 16; txtPattern.MaxLength = 16;
} }
private async Task ShowError(string Title, string Text)
{
var box = MessageBoxManager.GetMessageBoxStandardWindow(Title, Text, icon: MessageBox.Avalonia.Enums.Icon.Error);
var prop = box.GetType().GetField("_window", BindingFlags.Instance | BindingFlags.NonPublic);
var win = prop.GetValue(box) as Window;
win.Icon = this.Icon;
await box.Show();
}
} }
} }

View File

@ -1,60 +0,0 @@
<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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,111 +0,0 @@
namespace LogicAnalyzer
{
partial class ProtocolAnalyzerSettingsDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#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()
{
this.flSettings = new System.Windows.Forms.FlowLayoutPanel();
this.btnCancel = new System.Windows.Forms.Button();
this.btnAccept = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// flSettings
//
this.flSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.flSettings.AutoScroll = true;
this.flSettings.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flSettings.Location = new System.Drawing.Point(17, 20);
this.flSettings.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.flSettings.Name = "flSettings";
this.flSettings.Size = new System.Drawing.Size(459, 662);
this.flSettings.TabIndex = 0;
this.flSettings.WrapContents = false;
//
// btnCancel
//
this.btnCancel.BackColor = System.Drawing.Color.DimGray;
this.btnCancel.FlatAppearance.BorderSize = 0;
this.btnCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnCancel.Location = new System.Drawing.Point(369, 692);
this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(107, 38);
this.btnCancel.TabIndex = 0;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = false;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// btnAccept
//
this.btnAccept.BackColor = System.Drawing.Color.DimGray;
this.btnAccept.Enabled = false;
this.btnAccept.FlatAppearance.BorderSize = 0;
this.btnAccept.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnAccept.Location = new System.Drawing.Point(253, 692);
this.btnAccept.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.btnAccept.Name = "btnAccept";
this.btnAccept.Size = new System.Drawing.Size(107, 38);
this.btnAccept.TabIndex = 1;
this.btnAccept.Text = "Accept";
this.btnAccept.UseVisualStyleBackColor = false;
this.btnAccept.Click += new System.EventHandler(this.btnAccept_Click);
//
// ProtocolAnalyzerSettingsDialog
//
this.AcceptButton = this.btnAccept;
this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 25F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(493, 750);
this.ControlBox = false;
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.flSettings);
this.Controls.Add(this.btnAccept);
this.ForeColor = System.Drawing.Color.LightGray;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.MaximizeBox = false;
this.MaximumSize = new System.Drawing.Size(515, 806);
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(515, 806);
this.Name = "ProtocolAnalyzerSettingsDialog";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "ProtocolAnalyzerSettingsDialog";
this.ResumeLayout(false);
}
#endregion
private FlowLayoutPanel flSettings;
private Button btnCancel;
private Button btnAccept;
}
}

View File

@ -0,0 +1,32 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="350" d:DesignHeight="450"
MaxWidth="350" MinWidth="350"
MaxHeight="450" MinHeight="450"
x:Class="LogicAnalyzer.Dialogs.ProtocolAnalyzerSettingsDialog"
Title="ProtocolAnalyzerSettingsDialog" Icon="/Assets/window.ico"
TransparencyLevelHint="AcrylicBlur"
Background="Transparent" CanResize="False" WindowStartupLocation="CenterOwner">
<Grid RowDefinitions="8*,2*">
<ExperimentalAcrylicBorder IsHitTestVisible="False" Grid.RowSpan="2">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
BackgroundSource="Digger"
TintColor="Black"
TintOpacity="1"
MaterialOpacity="0.65" />
</ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder>
<ScrollViewer Name="scrViewer" Margin="10" VerticalAlignment="Top" Height="380">
<StackPanel Name="pnlControls">
</StackPanel>
</ScrollViewer>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom" Grid.Row="1" Margin="0,10,0,10">
<Button Name="btnAccept">Accept</Button>
<Button Name="btnCancel" Margin="10,0,10,0">Cancel</Button>
</StackPanel>
</Grid>
</Window>

View File

@ -1,17 +1,17 @@
using LogicAnalyzer.Protocols; using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using LogicAnalyzer.Extensions;
using LogicAnalyzer.Protocols;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LogicAnalyzer namespace LogicAnalyzer.Dialogs
{ {
public partial class ProtocolAnalyzerSettingsDialog : Form public partial class ProtocolAnalyzerSettingsDialog : Window
{ {
public ProtocolAnalyzerSettingValue[]? SelectedSettings { get; private set; } public ProtocolAnalyzerSettingValue[]? SelectedSettings { get; private set; }
public ProtocolAnalyzerSelectedChannel[]? SelectedChannels { get; private set; } public ProtocolAnalyzerSelectedChannel[]? SelectedChannels { get; private set; }
@ -26,15 +26,24 @@ namespace LogicAnalyzer
public ProtocolAnalyzerSettingsDialog() public ProtocolAnalyzerSettingsDialog()
{ {
InitializeComponent(); InitializeComponent();
btnAccept.IsEnabled = false;
btnAccept.Click += btnAccept_Click;
btnCancel.Click += btnCancel_Click;
} }
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
this.FixStartupPosition();
}
void LoadControls() void LoadControls()
{ {
flSettings.Controls.Clear(); pnlControls.Children.Clear();
if (analyzer == null) if (analyzer == null)
return; return;
this.Text = $"{analyzer.ProtocolName} analyzer settings"; this.Title = $"{analyzer.ProtocolName} analyzer settings";
var signals = analyzer?.Signals; var signals = analyzer?.Signals;
@ -51,15 +60,15 @@ namespace LogicAnalyzer
{ {
var signal = signals[buc]; var signal = signals[buc];
flSettings.Controls.Add(new Label { Visible = true, Name = $"Label_Signal{buc}", Text = $"Channel for signal { signal.SignalName }:", AutoSize = true }); pnlControls.Children.Add(new TextBlock{ IsVisible = true, Name = $"Label_Signal{buc}", Text = $"Channel for signal { signal.SignalName }:" });
var list = new ComboBox { Visible = true, Name = $"List_Signal{buc}", DropDownStyle = ComboBoxStyle.DropDownList, DataSource = channelsSource.ToArray(), Width = flSettings.Width - 32 }; var list = new ComboBox { IsVisible = true, Name = $"List_Signal{buc}", Items = channelsSource.ToArray(), HorizontalAlignment=Avalonia.Layout.HorizontalAlignment.Stretch, Margin= new Thickness(0,10,20,0) };
flSettings.Controls.Add(list); pnlControls.Children.Add(list);
list.SelectedIndexChanged += SignalChannel_SelectedIndexChanged; list.SelectionChanged += SignalChannel_SelectedIndexChanged;
flSettings.Controls.Add(new DividerLabel { Visible = true, Name = $"Divider_Signal{buc}", LineStyle = Border3DStyle.Bump, Width = flSettings.Width - 32 }); pnlControls.Children.Add(new Panel { HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch, Background = Brushes.White, Height = 1, Margin = new Thickness(10) });
} }
} }
@ -71,55 +80,56 @@ namespace LogicAnalyzer
{ {
var set = settings[buc]; var set = settings[buc];
flSettings.Controls.Add(new Label { Visible = true, Name = $"Label_Index{buc}", Text = set.Caption + ":", AutoSize = true }); pnlControls.Children.Add(new TextBlock { IsVisible = true, Name = $"Label_Index{buc}", Text = set.Caption + ":"});
switch (set.SettingType) switch (set.SettingType)
{ {
case ProtocolAnalyzerSetting.ProtocolAnalyzerSettingType.Boolean: case ProtocolAnalyzerSetting.ProtocolAnalyzerSettingType.Boolean:
var ck = new CheckBox { Visible = true, Name = $"Check_Index{buc}", Text = set.CheckCaption }; var ck = new CheckBox { IsVisible = true, Name = $"Check_Index{buc}", Content = set.CheckCaption, Margin = new Thickness(0, 10, 20, 0) };
flSettings.Controls.Add(ck); pnlControls.Children.Add(ck);
ck.CheckedChanged += BooleanSetting_CheckedChanged; ck.Checked += BooleanSetting_CheckedChanged;
break; break;
case ProtocolAnalyzerSetting.ProtocolAnalyzerSettingType.Integer: case ProtocolAnalyzerSetting.ProtocolAnalyzerSettingType.Integer:
var nud = new NumericUpDown { Visible = true, Name = $"Numeric_Index{buc}", Minimum = set.IntegerMinimumValue, Maximum = set.IntegerMaximumValue, Width = flSettings.Width - 32 }; var nud = new NumericUpDown { IsVisible = true, Name = $"Numeric_Index{buc}", Minimum = set.IntegerMinimumValue, Maximum = set.IntegerMaximumValue, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch, Margin = new Thickness(0, 10, 20, 0) };
flSettings.Controls.Add(nud); pnlControls.Children.Add(nud);
nud.ValueChanged += IntegerSetting_ValueChanged; nud.ValueChanged += IntegerSetting_ValueChanged;
break; break;
case ProtocolAnalyzerSetting.ProtocolAnalyzerSettingType.List: case ProtocolAnalyzerSetting.ProtocolAnalyzerSettingType.List:
var list = new ComboBox { Visible = true, Name = $"List_Index{buc}", DropDownStyle = ComboBoxStyle.DropDownList, DataSource = set.ListValues, Width = flSettings.Width - 32 }; var list = new ComboBox { IsVisible = true, Name = $"List_Index{buc}", Items = set.ListValues, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch, Margin = new Thickness(0, 10, 20, 0) };
flSettings.Controls.Add(list); pnlControls.Children.Add(list);
list.SelectedIndexChanged += ListSetting_SelectedIndexChanged; list.SelectionChanged += ListSetting_SelectedIndexChanged;
break; break;
} }
flSettings.Controls.Add(new DividerLabel { Visible = true, Name = $"Divider_Index{buc}", LineStyle = Border3DStyle.Bump, Width = flSettings.Width - 32 }); pnlControls.Children.Add(new Panel { HorizontalAlignment=Avalonia.Layout.HorizontalAlignment.Stretch, Background = Brushes.White, Height=1, Margin=new Thickness(10) });
} }
} }
} }
private void ListSetting_SelectedIndexChanged(object? sender, EventArgs e) private void ListSetting_SelectedIndexChanged(object? sender, RoutedEventArgs e)
{ {
ValidateSettings(); ValidateSettings();
} }
private void IntegerSetting_ValueChanged(object? sender, EventArgs e) private void IntegerSetting_ValueChanged(object? sender, RoutedEventArgs e)
{ {
ValidateSettings(); ValidateSettings();
} }
private void BooleanSetting_CheckedChanged(object? sender, EventArgs e) private void BooleanSetting_CheckedChanged(object? sender, RoutedEventArgs e)
{ {
ValidateSettings(); ValidateSettings();
} }
private void SignalChannel_SelectedIndexChanged(object? sender, EventArgs e) private void SignalChannel_SelectedIndexChanged(object? sender, SelectionChangedEventArgs e)
{ {
ValidateSettings(); ValidateSettings();
} }
@ -131,17 +141,17 @@ namespace LogicAnalyzer
if (st == null || ch == null) if (st == null || ch == null)
{ {
btnAccept.Enabled = false; btnAccept.IsEnabled = false;
return; return;
} }
if (analyzer.ValidateSettings(st, ch)) if (analyzer.ValidateSettings(st, ch))
{ {
btnAccept.Enabled = true; btnAccept.IsEnabled = true;
} }
else else
{ {
btnAccept.Enabled = false; btnAccept.IsEnabled = false;
} }
@ -168,18 +178,18 @@ namespace LogicAnalyzer
{ {
case ProtocolAnalyzerSetting.ProtocolAnalyzerSettingType.Boolean: case ProtocolAnalyzerSetting.ProtocolAnalyzerSettingType.Boolean:
var ck = flSettings.Controls.Find($"Check_Index{buc}", false).FirstOrDefault() as CheckBox; var ck = pnlControls.Children.Where(c => c.Name == $"Check_Index{buc}").FirstOrDefault() as CheckBox;
if (ck == null) if (ck == null)
return null; return null;
value = ck.Checked; value = ck.IsChecked;
break; break;
case ProtocolAnalyzerSetting.ProtocolAnalyzerSettingType.Integer: case ProtocolAnalyzerSetting.ProtocolAnalyzerSettingType.Integer:
var nud = flSettings.Controls.Find($"Numeric_Index{buc}", false).FirstOrDefault() as NumericUpDown; var nud = pnlControls.Children.Where(c => c.Name == $"Numeric_Index{buc}").FirstOrDefault() as NumericUpDown;
if (nud == null) if (nud == null)
return null; return null;
@ -190,18 +200,18 @@ namespace LogicAnalyzer
case ProtocolAnalyzerSetting.ProtocolAnalyzerSettingType.List: case ProtocolAnalyzerSetting.ProtocolAnalyzerSettingType.List:
var list = flSettings.Controls.Find($"List_Index{buc}", false).FirstOrDefault() as ComboBox; var list = pnlControls.Children.Where(c => c.Name == $"List_Index{buc}").FirstOrDefault() as ComboBox;
if (list == null) if (list == null)
return null; return null;
value = (string)list.SelectedItem; value = (string)list.SelectedItem?.ToString();
break; break;
} }
settingsValues.Add( new ProtocolAnalyzerSettingValue settingsValues.Add(new ProtocolAnalyzerSettingValue
{ {
SettingIndex = buc, SettingIndex = buc,
Value = value Value = value
@ -223,7 +233,7 @@ namespace LogicAnalyzer
for (int buc = 0; buc < signals.Length; buc++) for (int buc = 0; buc < signals.Length; buc++)
{ {
var signal = signals[buc]; var signal = signals[buc];
var list = flSettings.Controls.Find($"List_Signal{buc}", false).FirstOrDefault() as ComboBox; var list = pnlControls.Children.Where(c => c.Name == $"List_Signal{buc}").FirstOrDefault() as ComboBox;
if (list == null) if (list == null)
return null; return null;
@ -238,18 +248,16 @@ namespace LogicAnalyzer
return selectedChannels.ToArray(); return selectedChannels.ToArray();
} }
private void btnCancel_Click(object sender, EventArgs e) private void btnCancel_Click(object? sender, RoutedEventArgs e)
{ {
this.DialogResult = DialogResult.Cancel; this.Close(false);
this.Close();
} }
private void btnAccept_Click(object sender, EventArgs e) private void btnAccept_Click(object? sender, RoutedEventArgs e)
{ {
SelectedSettings = ComposeSettings(); SelectedSettings = ComposeSettings();
SelectedChannels = ComposeChannels(); SelectedChannels = ComposeChannels();
this.DialogResult = DialogResult.OK; this.Close(true);
this.Close();
} }
} }
} }

View File

@ -1,60 +0,0 @@
<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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,197 +0,0 @@
namespace LogicAnalyzer
{
partial class SelectedRegionDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#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()
{
this.cwRegionColor = new Cyotek.Windows.Forms.ColorWheel();
this.label1 = new System.Windows.Forms.Label();
this.txtName = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.btnCancel = new System.Windows.Forms.Button();
this.btnAccept = new System.Windows.Forms.Button();
this.tkAlpha = new System.Windows.Forms.TrackBar();
this.txtAlpha = new System.Windows.Forms.TextBox();
this.label3 = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.tkAlpha)).BeginInit();
this.SuspendLayout();
//
// cwRegionColor
//
this.cwRegionColor.Alpha = 1D;
this.cwRegionColor.LineColor = System.Drawing.Color.DarkGray;
this.cwRegionColor.Location = new System.Drawing.Point(17, 110);
this.cwRegionColor.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.cwRegionColor.Name = "cwRegionColor";
this.cwRegionColor.ShowAngleArrow = true;
this.cwRegionColor.ShowCenterLines = true;
this.cwRegionColor.ShowSaturationRing = true;
this.cwRegionColor.Size = new System.Drawing.Size(359, 435);
this.cwRegionColor.TabIndex = 0;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(17, 25);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(120, 25);
this.label1.TabIndex = 1;
this.label1.Text = "Region name:";
//
// txtName
//
this.txtName.BackColor = System.Drawing.Color.DimGray;
this.txtName.ForeColor = System.Drawing.Color.LightGray;
this.txtName.Location = new System.Drawing.Point(140, 20);
this.txtName.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.txtName.Name = "txtName";
this.txtName.Size = new System.Drawing.Size(300, 31);
this.txtName.TabIndex = 2;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(17, 80);
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(112, 25);
this.label2.TabIndex = 3;
this.label2.Text = "Region color";
//
// btnCancel
//
this.btnCancel.BackColor = System.Drawing.Color.DimGray;
this.btnCancel.FlatAppearance.BorderSize = 0;
this.btnCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnCancel.Location = new System.Drawing.Point(341, 555);
this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(107, 38);
this.btnCancel.TabIndex = 4;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = false;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// btnAccept
//
this.btnAccept.BackColor = System.Drawing.Color.DimGray;
this.btnAccept.FlatAppearance.BorderSize = 0;
this.btnAccept.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnAccept.Location = new System.Drawing.Point(226, 555);
this.btnAccept.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.btnAccept.Name = "btnAccept";
this.btnAccept.Size = new System.Drawing.Size(107, 38);
this.btnAccept.TabIndex = 5;
this.btnAccept.Text = "Accept";
this.btnAccept.UseVisualStyleBackColor = false;
this.btnAccept.Click += new System.EventHandler(this.btnAccept_Click);
//
// tkAlpha
//
this.tkAlpha.Location = new System.Drawing.Point(384, 115);
this.tkAlpha.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.tkAlpha.Maximum = 255;
this.tkAlpha.Name = "tkAlpha";
this.tkAlpha.Orientation = System.Windows.Forms.Orientation.Vertical;
this.tkAlpha.Size = new System.Drawing.Size(69, 382);
this.tkAlpha.TabIndex = 6;
this.tkAlpha.TickFrequency = 10;
this.tkAlpha.TickStyle = System.Windows.Forms.TickStyle.Both;
this.tkAlpha.Value = 128;
this.tkAlpha.ValueChanged += new System.EventHandler(this.tkAlpha_ValueChanged);
//
// txtAlpha
//
this.txtAlpha.BackColor = System.Drawing.Color.DimGray;
this.txtAlpha.ForeColor = System.Drawing.Color.LightGray;
this.txtAlpha.Location = new System.Drawing.Point(384, 507);
this.txtAlpha.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.txtAlpha.Name = "txtAlpha";
this.txtAlpha.ReadOnly = true;
this.txtAlpha.Size = new System.Drawing.Size(63, 31);
this.txtAlpha.TabIndex = 7;
this.txtAlpha.Text = "128";
this.txtAlpha.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(389, 85);
this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(58, 25);
this.label3.TabIndex = 8;
this.label3.Text = "Alpha";
//
// SelectedRegionDialog
//
this.AcceptButton = this.btnAccept;
this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 25F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(462, 610);
this.ControlBox = false;
this.Controls.Add(this.label3);
this.Controls.Add(this.txtAlpha);
this.Controls.Add(this.tkAlpha);
this.Controls.Add(this.btnAccept);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.label2);
this.Controls.Add(this.txtName);
this.Controls.Add(this.label1);
this.Controls.Add(this.cwRegionColor);
this.ForeColor = System.Drawing.Color.LightGray;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.MaximizeBox = false;
this.MaximumSize = new System.Drawing.Size(484, 666);
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(484, 666);
this.Name = "SelectedRegionDialog";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Create selected region";
((System.ComponentModel.ISupportInitialize)(this.tkAlpha)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private Cyotek.Windows.Forms.ColorWheel cwRegionColor;
private Label label1;
private TextBox txtName;
private Label label2;
private Button btnCancel;
private Button btnAccept;
private TrackBar tkAlpha;
private TextBox txtAlpha;
private Label label3;
}
}

View File

@ -0,0 +1,33 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:colorpicker="clr-namespace:AvaloniaColorPicker;assembly=AvaloniaColorPicker"
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="150"
x:Class="LogicAnalyzer.Dialogs.SelectedRegionDialog"
TransparencyLevelHint="AcrylicBlur"
Background="Transparent" Title="Region properties..."
Classes="tool_window" Icon="/Assets/window.ico"
Width="300" Height="150" CanResize="False" WindowStartupLocation="CenterOwner">
<Panel>
<ExperimentalAcrylicBorder IsHitTestVisible="False">
<ExperimentalAcrylicBorder.Material>
<ExperimentalAcrylicMaterial
BackgroundSource="Digger"
TintColor="Black"
TintOpacity="1"
MaterialOpacity="0.65" />
</ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder>
<Grid ColumnDefinitions="100,*" RowDefinitions="*,*,*" Margin="10,15,10,15">
<TextBlock Grid.Column="0" VerticalAlignment="Center" Margin="5,0,0,0">Region name:</TextBlock>
<TextBox Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Center" Name="txtName"></TextBox>
<TextBlock Grid.Column="0" Grid.Row="1" VerticalAlignment="Center" Margin="5,0,0,0">Region color:</TextBlock>
<colorpicker:ColorButton Name="clrRegion" Color="#2010ff10" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch"></colorpicker:ColorButton >
<StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" Grid.Column="1" Grid.Row="2" HorizontalAlignment="Right">
<Button Name="btnAccept">Accept</Button>
<Button Name="btnCancel">Cancel</Button>
</StackPanel>
</Grid>
</Panel>
</Window>

View File

@ -0,0 +1,62 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using LogicAnalyzer.Classes;
using LogicAnalyzer.Extensions;
using MessageBox.Avalonia;
using System;
using System.Reflection;
using System.Threading.Tasks;
namespace LogicAnalyzer.Dialogs
{
public partial class SelectedRegionDialog : Window
{
public SelectedSampleRegion SelectedRegion { get; set; } = new SelectedSampleRegion();
public SelectedRegionDialog()
{
InitializeComponent();
btnAccept.Click += BtnAccept_Click;
btnCancel.Click += BtnCancel_Click;
#if DEBUG
this.AttachDevTools();
#endif
}
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
this.FixStartupPosition();
}
private void BtnCancel_Click(object? sender, RoutedEventArgs e)
{
this.Close(false);
}
private async void BtnAccept_Click(object? sender, RoutedEventArgs e)
{
if (SelectedRegion == null)
{
await ShowError("Error", "No region selected, internal error.");
return;
}
SelectedRegion.RegionColor = clrRegion.Color;
SelectedRegion.RegionName = txtName.Text;
this.Close(true);
}
private async Task ShowError(string Title, string Text)
{
var box = MessageBoxManager.GetMessageBoxStandardWindow(Title, Text, icon: MessageBox.Avalonia.Enums.Icon.Error);
var prop = box.GetType().GetField("_window", BindingFlags.Instance | BindingFlags.NonPublic);
var win = prop.GetValue(box) as Window;
win.Icon = this.Icon;
await box.Show();
}
}
}

View File

@ -1,47 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LogicAnalyzer
{
public partial class SelectedRegionDialog : Form
{
public SelectedSampleRegion SelectedRegion { get; set; } = new SelectedSampleRegion();
public SelectedRegionDialog()
{
InitializeComponent();
}
private void btnAccept_Click(object sender, EventArgs e)
{
if (SelectedRegion == null)
{
MessageBox.Show("No region selected, internal error.");
return;
}
SelectedRegion.RegionColor?.Dispose();
SelectedRegion.RegionColor = new SolidBrush(Color.FromArgb(byte.Parse(txtAlpha.Text), cwRegionColor.Color));
SelectedRegion.RegionName = txtName.Text;
this.DialogResult = DialogResult.OK;
this.Close();
}
private void btnCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
private void tkAlpha_ValueChanged(object sender, EventArgs e)
{
txtAlpha.Text = tkAlpha.Value.ToString();
}
}
}

View File

@ -1,60 +0,0 @@
<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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,134 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LogicAnalyzer.Extensions
{
static class GraphicsExtension
{
private static GraphicsPath GenerateRoundedRectangle(
this Graphics graphics,
RectangleF rectangle,
float radius)
{
float diameter;
GraphicsPath path = new GraphicsPath();
if (radius <= 0.0F)
{
path.AddRectangle(rectangle);
path.CloseFigure();
return path;
}
else
{
if (radius >= (Math.Min(rectangle.Width, rectangle.Height)) / 2.0)
return graphics.GenerateCapsule(rectangle);
diameter = radius * 2.0F;
SizeF sizeF = new SizeF(diameter, diameter);
RectangleF arc = new RectangleF(rectangle.Location, sizeF);
path.AddArc(arc, 180, 90);
arc.X = rectangle.Right - diameter;
path.AddArc(arc, 270, 90);
arc.Y = rectangle.Bottom - diameter;
path.AddArc(arc, 0, 90);
arc.X = rectangle.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
}
return path;
}
private static GraphicsPath GenerateCapsule(
this Graphics graphics,
RectangleF baseRect)
{
float diameter;
RectangleF arc;
GraphicsPath path = new GraphicsPath();
try
{
if (baseRect.Width > baseRect.Height)
{
diameter = baseRect.Height;
SizeF sizeF = new SizeF(diameter, diameter);
arc = new RectangleF(baseRect.Location, sizeF);
path.AddArc(arc, 90, 180);
arc.X = baseRect.Right - diameter;
path.AddArc(arc, 270, 180);
}
else if (baseRect.Width < baseRect.Height)
{
diameter = baseRect.Width;
SizeF sizeF = new SizeF(diameter, diameter);
arc = new RectangleF(baseRect.Location, sizeF);
path.AddArc(arc, 180, 180);
arc.Y = baseRect.Bottom - diameter;
path.AddArc(arc, 0, 180);
}
else path.AddEllipse(baseRect);
}
catch { path.AddEllipse(baseRect); }
finally { path.CloseFigure(); }
return path;
}
/// <summary>
/// Draws a rounded rectangle specified by a pair of coordinates, a width, a height and the radius
/// for the arcs that make the rounded edges.
/// </summary>
/// <param name="brush">System.Drawing.Pen that determines the color, width and style of the rectangle.</param>
/// <param name="x">The x-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="y">The y-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="width">Width of the rectangle to draw.</param>
/// <param name="height">Height of the rectangle to draw.</param>
/// <param name="radius">The radius of the arc used for the rounded edges.</param>
public static void DrawRoundedRectangle(
this Graphics graphics,
Pen pen,
float x,
float y,
float width,
float height,
float radius)
{
RectangleF rectangle = new RectangleF(x, y, width, height);
GraphicsPath path = graphics.GenerateRoundedRectangle(rectangle, radius);
SmoothingMode old = graphics.SmoothingMode;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.DrawPath(pen, path);
graphics.SmoothingMode = old;
}
/// <summary>
/// Draws a rounded rectangle specified by a pair of coordinates, a width, a height and the radius
/// for the arcs that make the rounded edges.
/// </summary>
/// <param name="brush">System.Drawing.Pen that determines the color, width and style of the rectangle.</param>
/// <param name="x">The x-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="y">The y-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="width">Width of the rectangle to draw.</param>
/// <param name="height">Height of the rectangle to draw.</param>
/// <param name="radius">The radius of the arc used for the rounded edges.</param>
public static void DrawRoundedRectangle(
this Graphics graphics,
Pen pen,
int x,
int y,
int width,
int height,
int radius)
{
graphics.DrawRoundedRectangle(
pen,
Convert.ToSingle(x),
Convert.ToSingle(y),
Convert.ToSingle(width),
Convert.ToSingle(height),
Convert.ToSingle(radius));
}
}
}

View File

@ -0,0 +1,50 @@
using Avalonia;
using Avalonia.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LogicAnalyzer.Extensions
{
public static class WindowExtensions
{
public static void FixStartupPosition(this Window windowToFix)
{
if (OperatingSystem.IsWindows())
{
// Not needed for Windows
return;
}
var scale = windowToFix.PlatformImpl?.DesktopScaling ?? 1.0;
var pOwner = windowToFix.Owner?.PlatformImpl;
if (pOwner != null)
{
scale = pOwner.DesktopScaling;
}
var rect = new PixelRect(PixelPoint.Origin,
PixelSize.FromSize(windowToFix.ClientSize, scale));
if (windowToFix.WindowStartupLocation == WindowStartupLocation.CenterScreen)
{
var screen = windowToFix.Screens.ScreenFromPoint(pOwner?.Position ?? windowToFix.Position);
if (screen == null)
{
return;
}
windowToFix.Position = screen.WorkingArea.CenterRect(rect).Position;
}
else
{
if (pOwner == null ||
windowToFix.WindowStartupLocation != WindowStartupLocation.CenterOwner)
{
return;
}
windowToFix.Position = new PixelRect(pOwner.Position,
PixelSize.FromSize(pOwner.ClientSize, scale)).CenterRect(rect).Position;
}
}
}
}

View File

@ -1,37 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms> <!--Avalonia doesen't support TrimMode=link currently,but we are working on that https://github.com/AvaloniaUI/Avalonia/issues/6892 -->
<ImplicitUsings>enable</ImplicitUsings> <TrimMode>copyused</TrimMode>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationIcon>Assets\window.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Cyotek.Windows.Forms.ColorPicker" Version="2.0.0-beta.7" /> <None Remove=".gitignore" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <None Remove="Assets\window.ico" />
<PackageReference Include="System.IO.Ports" Version="6.0.0" /> </ItemGroup>
<ItemGroup>
<!--This helps with theme dll-s trimming.
If you will publish your application in self-contained mode with p:PublishTrimmed=true and it will use Fluent theme Default theme will be trimmed from the output and vice versa.
https://github.com/AvaloniaUI/Avalonia/issues/5593 -->
<TrimmableAssembly Include="Avalonia.Themes.Fluent" />
<TrimmableAssembly Include="Avalonia.Themes.Default" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.16" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.16" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.16" />
<PackageReference Include="AvaloniaColorPicker" Version="1.3.3" />
<PackageReference Include="MessageBox.Avalonia" Version="2.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta1" />
<PackageReference Include="XamlNameReferenceGenerator" Version="1.3.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\SharedDriver\SharedDriver.csproj" /> <ProjectReference Include="..\SharedDriver\SharedDriver.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Properties\Resources.Designer.cs"> <Compile Update="Dialogs\SelectedRegionDialog.axaml.cs">
<DesignTime>True</DesignTime> <DependentUpon>SelectedRegionDialog.axaml</DependentUpon>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx"> <AvaloniaResource Include="Assets\**" />
<Generator>ResXFileCodeGenerator</Generator> </ItemGroup>
<LastGenOutput>Resources.Designer.cs</LastGenOutput> <ItemGroup>
</EmbeddedResource> <Content Include="Assets\window.ico" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,38 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<_LastSelectedProfileId>C:\Users\geniw\source\repos\LogicAnalyzer\LogicAnalyzer\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId> <_LastSelectedProfileId>C:\Users\geniw\source\repos\LogicAnalyzer\LogicAnalyzer\Properties\PublishProfiles\Windows.pubxml</_LastSelectedProfileId>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Update="Dialogs\CaptureDialog.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="Controls\ChannelViewer.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Update="Controls\DividerLabel.cs">
<SubType>Component</SubType>
</Compile>
<Compile Update="Controls\BorderGroupBox.cs">
<SubType>Component</SubType>
</Compile>
<Compile Update="MainForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="Dialogs\ProtocolAnalyzerSettingsDialog.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="Controls\SampleMarker.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Update="Controls\SampleViewer.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Update="Dialogs\SelectedRegionDialog.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="Splash.cs">
<SubType>Form</SubType>
</Compile>
</ItemGroup>
</Project> </Project>

View File

@ -1,602 +0,0 @@
namespace LogicAnalyzer
{
partial class MainForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#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()
{
this.btnOpenClose = new System.Windows.Forms.Button();
this.btnRefresh = new System.Windows.Forms.Button();
this.sampleViewer = new LogicAnalyzer.SampleViewer();
this.ddSerialPorts = new System.Windows.Forms.ComboBox();
this.label1 = new System.Windows.Forms.Label();
this.lblConnectedDevice = new System.Windows.Forms.Label();
this.btnCapture = new System.Windows.Forms.Button();
this.btnRepeat = new System.Windows.Forms.Button();
this.scrSamplePos = new System.Windows.Forms.HScrollBar();
this.groupBox1 = new LogicAnalyzer.BorderGroupBox();
this.btnJmpTrigger = new System.Windows.Forms.Button();
this.label4 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.tkInScreen = new System.Windows.Forms.TrackBar();
this.label2 = new System.Windows.Forms.Label();
this.channelViewer = new LogicAnalyzer.ChannelViewer();
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.openCaptureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.saveCaptureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.protocolAnalyzersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.availableAnalyzersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.noneToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.groupBox2 = new LogicAnalyzer.BorderGroupBox();
this.lblEdge = new System.Windows.Forms.Label();
this.label18 = new System.Windows.Forms.Label();
this.lblTrigger = new System.Windows.Forms.Label();
this.label16 = new System.Windows.Forms.Label();
this.lblChannels = new System.Windows.Forms.Label();
this.label14 = new System.Windows.Forms.Label();
this.lblPostSamples = new System.Windows.Forms.Label();
this.label12 = new System.Windows.Forms.Label();
this.lblPreSamples = new System.Windows.Forms.Label();
this.label10 = new System.Windows.Forms.Label();
this.lblSamples = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this.lblFreq = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.sampleMarker = new LogicAnalyzer.SampleMarker();
this.label6 = new System.Windows.Forms.Label();
this.groupBox1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.tkInScreen)).BeginInit();
this.menuStrip1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.SuspendLayout();
//
// btnOpenClose
//
this.btnOpenClose.BackColor = System.Drawing.Color.DimGray;
this.btnOpenClose.FlatAppearance.BorderSize = 0;
this.btnOpenClose.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnOpenClose.ForeColor = System.Drawing.Color.LightGray;
this.btnOpenClose.Location = new System.Drawing.Point(189, 27);
this.btnOpenClose.Name = "btnOpenClose";
this.btnOpenClose.Size = new System.Drawing.Size(126, 23);
this.btnOpenClose.TabIndex = 0;
this.btnOpenClose.Text = "Open device";
this.btnOpenClose.UseVisualStyleBackColor = false;
this.btnOpenClose.Click += new System.EventHandler(this.btnOpenClose_Click);
//
// btnRefresh
//
this.btnRefresh.BackColor = System.Drawing.Color.DimGray;
this.btnRefresh.FlatAppearance.BorderSize = 0;
this.btnRefresh.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnRefresh.ForeColor = System.Drawing.Color.LightGray;
this.btnRefresh.Location = new System.Drawing.Point(12, 27);
this.btnRefresh.Name = "btnRefresh";
this.btnRefresh.Size = new System.Drawing.Size(75, 23);
this.btnRefresh.TabIndex = 1;
this.btnRefresh.Text = "Refresh";
this.btnRefresh.UseVisualStyleBackColor = false;
this.btnRefresh.Click += new System.EventHandler(this.button2_Click);
//
// sampleViewer
//
this.sampleViewer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.sampleViewer.ChannelCount = 0;
this.sampleViewer.FirstSample = 0;
this.sampleViewer.Location = new System.Drawing.Point(150, 83);
this.sampleViewer.MinimumSize = new System.Drawing.Size(16, 16);
this.sampleViewer.Name = "sampleViewer";
this.sampleViewer.PreSamples = 0;
this.sampleViewer.Samples = null;
this.sampleViewer.SamplesInScreen = 0;
this.sampleViewer.Size = new System.Drawing.Size(1000, 649);
this.sampleViewer.TabIndex = 2;
//
// ddSerialPorts
//
this.ddSerialPorts.BackColor = System.Drawing.Color.LightGray;
this.ddSerialPorts.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.ddSerialPorts.FormattingEnabled = true;
this.ddSerialPorts.Location = new System.Drawing.Point(93, 27);
this.ddSerialPorts.Name = "ddSerialPorts";
this.ddSerialPorts.Size = new System.Drawing.Size(90, 23);
this.ddSerialPorts.TabIndex = 3;
//
// label1
//
this.label1.AutoSize = true;
this.label1.ForeColor = System.Drawing.Color.LightGray;
this.label1.Location = new System.Drawing.Point(321, 31);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(105, 15);
this.label1.TabIndex = 4;
this.label1.Text = "Connected device:";
//
// lblConnectedDevice
//
this.lblConnectedDevice.AutoSize = true;
this.lblConnectedDevice.ForeColor = System.Drawing.Color.LightGray;
this.lblConnectedDevice.Location = new System.Drawing.Point(432, 31);
this.lblConnectedDevice.Name = "lblConnectedDevice";
this.lblConnectedDevice.Size = new System.Drawing.Size(58, 15);
this.lblConnectedDevice.TabIndex = 5;
this.lblConnectedDevice.Text = "< None >";
//
// btnCapture
//
this.btnCapture.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btnCapture.BackColor = System.Drawing.Color.DimGray;
this.btnCapture.Enabled = false;
this.btnCapture.FlatAppearance.BorderSize = 0;
this.btnCapture.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnCapture.ForeColor = System.Drawing.Color.LightGray;
this.btnCapture.Location = new System.Drawing.Point(1281, 27);
this.btnCapture.Name = "btnCapture";
this.btnCapture.Size = new System.Drawing.Size(75, 23);
this.btnCapture.TabIndex = 6;
this.btnCapture.Text = "Capture";
this.btnCapture.UseVisualStyleBackColor = false;
this.btnCapture.Click += new System.EventHandler(this.btnCapture_Click);
//
// btnRepeat
//
this.btnRepeat.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btnRepeat.BackColor = System.Drawing.Color.DimGray;
this.btnRepeat.Enabled = false;
this.btnRepeat.FlatAppearance.BorderSize = 0;
this.btnRepeat.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnRepeat.ForeColor = System.Drawing.Color.LightGray;
this.btnRepeat.Location = new System.Drawing.Point(1156, 27);
this.btnRepeat.Name = "btnRepeat";
this.btnRepeat.Size = new System.Drawing.Size(119, 23);
this.btnRepeat.TabIndex = 7;
this.btnRepeat.Text = "Repeat last capture";
this.btnRepeat.UseVisualStyleBackColor = false;
this.btnRepeat.Click += new System.EventHandler(this.btnRepeat_Click);
//
// scrSamplePos
//
this.scrSamplePos.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.scrSamplePos.Location = new System.Drawing.Point(12, 735);
this.scrSamplePos.Maximum = 32;
this.scrSamplePos.Name = "scrSamplePos";
this.scrSamplePos.Size = new System.Drawing.Size(1344, 17);
this.scrSamplePos.TabIndex = 8;
this.scrSamplePos.Value = 16;
this.scrSamplePos.ValueChanged += new System.EventHandler(this.scrSamplePos_ValueChanged);
//
// groupBox1
//
this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.groupBox1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.groupBox1.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.groupBox1.BorderRadius = 0;
this.groupBox1.BorderWidth = 1;
this.groupBox1.Controls.Add(this.btnJmpTrigger);
this.groupBox1.Controls.Add(this.label4);
this.groupBox1.Controls.Add(this.label3);
this.groupBox1.Controls.Add(this.tkInScreen);
this.groupBox1.Controls.Add(this.label2);
this.groupBox1.ForeColor = System.Drawing.Color.LightGray;
this.groupBox1.LabelIndent = 10;
this.groupBox1.Location = new System.Drawing.Point(1156, 56);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(200, 135);
this.groupBox1.TabIndex = 9;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Adjustments";
//
// btnJmpTrigger
//
this.btnJmpTrigger.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.btnJmpTrigger.BackColor = System.Drawing.Color.DimGray;
this.btnJmpTrigger.FlatAppearance.BorderSize = 0;
this.btnJmpTrigger.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btnJmpTrigger.Location = new System.Drawing.Point(6, 106);
this.btnJmpTrigger.Name = "btnJmpTrigger";
this.btnJmpTrigger.Size = new System.Drawing.Size(188, 23);
this.btnJmpTrigger.TabIndex = 4;
this.btnJmpTrigger.Text = "Jump to trigger";
this.btnJmpTrigger.UseVisualStyleBackColor = false;
this.btnJmpTrigger.Click += new System.EventHandler(this.btnJmpTrigger_Click);
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(163, 27);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(25, 15);
this.label4.TabIndex = 3;
this.label4.Text = "100";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(6, 27);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(19, 15);
this.label3.TabIndex = 2;
this.label3.Text = "10";
//
// tkInScreen
//
this.tkInScreen.LargeChange = 10;
this.tkInScreen.Location = new System.Drawing.Point(6, 45);
this.tkInScreen.Maximum = 200;
this.tkInScreen.Minimum = 10;
this.tkInScreen.Name = "tkInScreen";
this.tkInScreen.Size = new System.Drawing.Size(182, 45);
this.tkInScreen.TabIndex = 1;
this.tkInScreen.TickFrequency = 10;
this.tkInScreen.Value = 10;
this.tkInScreen.ValueChanged += new System.EventHandler(this.tkInScreen_ValueChanged);
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(44, 27);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(101, 15);
this.label2.TabIndex = 0;
this.label2.Text = "Samples in screen";
//
// channelViewer
//
this.channelViewer.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.channelViewer.Channels = null;
this.channelViewer.ChannelsText = new string[0];
this.channelViewer.Location = new System.Drawing.Point(12, 83);
this.channelViewer.Name = "channelViewer";
this.channelViewer.Size = new System.Drawing.Size(137, 649);
this.channelViewer.TabIndex = 10;
//
// menuStrip1
//
this.menuStrip1.BackColor = System.Drawing.Color.Silver;
this.menuStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Visible;
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileToolStripMenuItem,
this.protocolAnalyzersToolStripMenuItem});
this.menuStrip1.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.Flow;
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.RenderMode = System.Windows.Forms.ToolStripRenderMode.Professional;
this.menuStrip1.Size = new System.Drawing.Size(1365, 23);
this.menuStrip1.TabIndex = 11;
this.menuStrip1.Text = "menuStrip1";
//
// fileToolStripMenuItem
//
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.openCaptureToolStripMenuItem,
this.saveCaptureToolStripMenuItem});
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 19);
this.fileToolStripMenuItem.Text = "File";
//
// openCaptureToolStripMenuItem
//
this.openCaptureToolStripMenuItem.Name = "openCaptureToolStripMenuItem";
this.openCaptureToolStripMenuItem.Size = new System.Drawing.Size(146, 22);
this.openCaptureToolStripMenuItem.Text = "Open capture";
this.openCaptureToolStripMenuItem.Click += new System.EventHandler(this.openCaptureToolStripMenuItem_Click);
//
// saveCaptureToolStripMenuItem
//
this.saveCaptureToolStripMenuItem.Enabled = false;
this.saveCaptureToolStripMenuItem.Name = "saveCaptureToolStripMenuItem";
this.saveCaptureToolStripMenuItem.Size = new System.Drawing.Size(146, 22);
this.saveCaptureToolStripMenuItem.Text = "Save capture";
this.saveCaptureToolStripMenuItem.Click += new System.EventHandler(this.saveCaptureToolStripMenuItem_Click);
//
// protocolAnalyzersToolStripMenuItem
//
this.protocolAnalyzersToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.availableAnalyzersToolStripMenuItem});
this.protocolAnalyzersToolStripMenuItem.Name = "protocolAnalyzersToolStripMenuItem";
this.protocolAnalyzersToolStripMenuItem.Size = new System.Drawing.Size(115, 19);
this.protocolAnalyzersToolStripMenuItem.Text = "Protocol analyzers";
//
// availableAnalyzersToolStripMenuItem
//
this.availableAnalyzersToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.noneToolStripMenuItem});
this.availableAnalyzersToolStripMenuItem.Enabled = false;
this.availableAnalyzersToolStripMenuItem.Name = "availableAnalyzersToolStripMenuItem";
this.availableAnalyzersToolStripMenuItem.Size = new System.Drawing.Size(173, 22);
this.availableAnalyzersToolStripMenuItem.Text = "Available analyzers";
//
// noneToolStripMenuItem
//
this.noneToolStripMenuItem.Name = "noneToolStripMenuItem";
this.noneToolStripMenuItem.Size = new System.Drawing.Size(125, 22);
this.noneToolStripMenuItem.Text = "< None >";
//
// groupBox2
//
this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.groupBox2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.groupBox2.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.groupBox2.BorderRadius = 0;
this.groupBox2.BorderWidth = 1;
this.groupBox2.Controls.Add(this.lblEdge);
this.groupBox2.Controls.Add(this.label18);
this.groupBox2.Controls.Add(this.lblTrigger);
this.groupBox2.Controls.Add(this.label16);
this.groupBox2.Controls.Add(this.lblChannels);
this.groupBox2.Controls.Add(this.label14);
this.groupBox2.Controls.Add(this.lblPostSamples);
this.groupBox2.Controls.Add(this.label12);
this.groupBox2.Controls.Add(this.lblPreSamples);
this.groupBox2.Controls.Add(this.label10);
this.groupBox2.Controls.Add(this.lblSamples);
this.groupBox2.Controls.Add(this.label8);
this.groupBox2.Controls.Add(this.lblFreq);
this.groupBox2.Controls.Add(this.label5);
this.groupBox2.ForeColor = System.Drawing.Color.LightGray;
this.groupBox2.LabelIndent = 10;
this.groupBox2.Location = new System.Drawing.Point(1156, 603);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(200, 129);
this.groupBox2.TabIndex = 12;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Information";
//
// lblEdge
//
this.lblEdge.Location = new System.Drawing.Point(93, 109);
this.lblEdge.Name = "lblEdge";
this.lblEdge.Size = new System.Drawing.Size(101, 15);
this.lblEdge.TabIndex = 13;
this.lblEdge.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// label18
//
this.label18.AutoSize = true;
this.label18.Location = new System.Drawing.Point(6, 109);
this.label18.Name = "label18";
this.label18.Size = new System.Drawing.Size(36, 15);
this.label18.TabIndex = 12;
this.label18.Text = "Edge:";
//
// lblTrigger
//
this.lblTrigger.Location = new System.Drawing.Point(93, 94);
this.lblTrigger.Name = "lblTrigger";
this.lblTrigger.Size = new System.Drawing.Size(101, 15);
this.lblTrigger.TabIndex = 11;
this.lblTrigger.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// label16
//
this.label16.AutoSize = true;
this.label16.Location = new System.Drawing.Point(6, 94);
this.label16.Name = "label16";
this.label16.Size = new System.Drawing.Size(46, 15);
this.label16.TabIndex = 10;
this.label16.Text = "Trigger:";
//
// lblChannels
//
this.lblChannels.Location = new System.Drawing.Point(93, 79);
this.lblChannels.Name = "lblChannels";
this.lblChannels.Size = new System.Drawing.Size(101, 15);
this.lblChannels.TabIndex = 9;
this.lblChannels.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// label14
//
this.label14.AutoSize = true;
this.label14.Location = new System.Drawing.Point(6, 79);
this.label14.Name = "label14";
this.label14.Size = new System.Drawing.Size(59, 15);
this.label14.TabIndex = 8;
this.label14.Text = "Channels:";
//
// lblPostSamples
//
this.lblPostSamples.Location = new System.Drawing.Point(93, 64);
this.lblPostSamples.Name = "lblPostSamples";
this.lblPostSamples.Size = new System.Drawing.Size(101, 15);
this.lblPostSamples.TabIndex = 7;
this.lblPostSamples.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// label12
//
this.label12.AutoSize = true;
this.label12.Location = new System.Drawing.Point(6, 64);
this.label12.Name = "label12";
this.label12.Size = new System.Drawing.Size(79, 15);
this.label12.TabIndex = 6;
this.label12.Text = "Post samples:";
//
// lblPreSamples
//
this.lblPreSamples.Location = new System.Drawing.Point(93, 49);
this.lblPreSamples.Name = "lblPreSamples";
this.lblPreSamples.Size = new System.Drawing.Size(101, 15);
this.lblPreSamples.TabIndex = 5;
this.lblPreSamples.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// label10
//
this.label10.AutoSize = true;
this.label10.Location = new System.Drawing.Point(6, 49);
this.label10.Name = "label10";
this.label10.Size = new System.Drawing.Size(73, 15);
this.label10.TabIndex = 4;
this.label10.Text = "Pre samples:";
//
// lblSamples
//
this.lblSamples.Location = new System.Drawing.Point(93, 34);
this.lblSamples.Name = "lblSamples";
this.lblSamples.Size = new System.Drawing.Size(101, 15);
this.lblSamples.TabIndex = 3;
this.lblSamples.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(6, 34);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(81, 15);
this.label8.TabIndex = 2;
this.label8.Text = "Total samples:";
//
// lblFreq
//
this.lblFreq.Location = new System.Drawing.Point(93, 19);
this.lblFreq.Name = "lblFreq";
this.lblFreq.Size = new System.Drawing.Size(101, 15);
this.lblFreq.TabIndex = 1;
this.lblFreq.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(6, 19);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(65, 15);
this.label5.TabIndex = 0;
this.label5.Text = "Frequency:";
//
// sampleMarker
//
this.sampleMarker.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.sampleMarker.BackColor = System.Drawing.Color.DimGray;
this.sampleMarker.FirstSample = 0;
this.sampleMarker.ForeColor = System.Drawing.Color.LightGray;
this.sampleMarker.Location = new System.Drawing.Point(150, 56);
this.sampleMarker.Name = "sampleMarker";
this.sampleMarker.Size = new System.Drawing.Size(1000, 21);
this.sampleMarker.TabIndex = 13;
this.sampleMarker.VisibleSamples = 0;
this.sampleMarker.RegionCreated += new System.EventHandler<LogicAnalyzer.RegionEventArgs>(this.sampleMarker_RegionCreated);
this.sampleMarker.RegionDeleted += new System.EventHandler<LogicAnalyzer.RegionEventArgs>(this.sampleMarker_RegionDeleted);
//
// label6
//
this.label6.BackColor = System.Drawing.Color.DimGray;
this.label6.ForeColor = System.Drawing.Color.LightGray;
this.label6.Location = new System.Drawing.Point(12, 56);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(137, 21);
this.label6.TabIndex = 14;
this.label6.Text = "Channels";
this.label6.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
this.ClientSize = new System.Drawing.Size(1365, 761);
this.Controls.Add(this.label6);
this.Controls.Add(this.sampleMarker);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.channelViewer);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.scrSamplePos);
this.Controls.Add(this.btnRepeat);
this.Controls.Add(this.btnCapture);
this.Controls.Add(this.lblConnectedDevice);
this.Controls.Add(this.label1);
this.Controls.Add(this.ddSerialPorts);
this.Controls.Add(this.sampleViewer);
this.Controls.Add(this.btnRefresh);
this.Controls.Add(this.btnOpenClose);
this.Controls.Add(this.menuStrip1);
this.DoubleBuffered = true;
this.ForeColor = System.Drawing.Color.LightGray;
this.MinimumSize = new System.Drawing.Size(1024, 800);
this.Name = "MainForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "LogicAnalyzer V1.0 by El Dr. Gusman";
this.Load += new System.EventHandler(this.Form1_Load);
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.tkInScreen)).EndInit();
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private Button btnOpenClose;
private Button btnRefresh;
private SampleViewer sampleViewer;
private ComboBox ddSerialPorts;
private Label label1;
private Label lblConnectedDevice;
private Button btnCapture;
private Button btnRepeat;
private HScrollBar scrSamplePos;
private BorderGroupBox groupBox1;
private Label label4;
private Label label3;
private TrackBar tkInScreen;
private Label label2;
private Button btnJmpTrigger;
private ChannelViewer channelViewer;
private MenuStrip menuStrip1;
private ToolStripMenuItem fileToolStripMenuItem;
private ToolStripMenuItem openCaptureToolStripMenuItem;
private ToolStripMenuItem saveCaptureToolStripMenuItem;
private BorderGroupBox groupBox2;
private Label lblEdge;
private Label label18;
private Label lblTrigger;
private Label label16;
private Label lblChannels;
private Label label14;
private Label lblPostSamples;
private Label label12;
private Label lblPreSamples;
private Label label10;
private Label lblSamples;
private Label label8;
private Label lblFreq;
private Label label5;
private SampleMarker sampleMarker;
private Label label6;
private ToolStripMenuItem protocolAnalyzersToolStripMenuItem;
private ToolStripMenuItem availableAnalyzersToolStripMenuItem;
private ToolStripMenuItem noneToolStripMenuItem;
}
}

View File

@ -1,360 +0,0 @@
using LogicAnalyzer.Protocols;
using Newtonsoft.Json;
using SharedDriver;
using System.IO.Ports;
namespace LogicAnalyzer
{
public partial class MainForm : Form
{
LogicAnalyzerDriver driver;
CaptureSettings settings;
ProtocolAnalyzerLoader pLoader;
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
public MainForm()
{
var splashForm = new Splash();
splashForm.Show();
InitializeComponent();
menuStrip1.Renderer = new ToolStripProfessionalRenderer(new MenuColors.MyColorTable());
}
private void Driver_CaptureCompleted(object? sender, CaptureEventArgs e)
{
this.BeginInvoke(new Action(() =>
{
sampleViewer.BeginUpdate();
sampleViewer.Samples = e.Samples;
sampleViewer.PreSamples = e.PreSamples;
sampleViewer.ChannelCount = e.ChannelCount;
sampleViewer.SamplesInScreen = Math.Min(100, e.Samples.Length / 10);
sampleViewer.FirstSample = Math.Max(e.PreSamples - 10, 0);
sampleViewer.ClearRegions();
sampleViewer.ClearAnalyzedChannels();
sampleViewer.EndUpdate();
scrSamplePos.Maximum = e.Samples.Length - 1;
scrSamplePos.Value = sampleViewer.FirstSample;
tkInScreen.Value = sampleViewer.SamplesInScreen;
channelViewer.Channels = settings.CaptureChannels;
sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen;
sampleMarker.FirstSample = sampleViewer.FirstSample;
sampleMarker.ClearRegions();
btnCapture.Enabled = true;
btnRepeat.Enabled = true;
btnOpenClose.Enabled = true;
availableAnalyzersToolStripMenuItem.Enabled = true;
saveCaptureToolStripMenuItem.Enabled = true;
LoadInfo();
}));
}
private void Form1_Load(object sender, EventArgs e)
{
LoadAnalyzers();
RefreshPorts();
}
void LoadAnalyzers()
{
pLoader = new ProtocolAnalyzerLoader(Path.Combine(Application.StartupPath, "analyzers"));
var protocols = pLoader.ProtocolNames;
availableAnalyzersToolStripMenuItem.DropDownItems.Clear();
if (protocols.Length == 0)
availableAnalyzersToolStripMenuItem.DropDownItems.Add("< None >");
else
{
foreach (var protocol in pLoader.ProtocolNames)
{
var menu = availableAnalyzersToolStripMenuItem.DropDownItems.Add(protocol, null, ProtocolAnalyzer_Click);
menu.Tag = protocol;
}
}
}
private void ProtocolAnalyzer_Click(object? sender, EventArgs e)
{
var item = (sender as ToolStripMenuItem).Tag.ToString();
var analyzer = pLoader.GetAnalyzer(item);
using (var dlg = new ProtocolAnalyzerSettingsDialog())
{
dlg.Analyzer = analyzer;
dlg.Channels = channelViewer.Channels;
if (dlg.ShowDialog() != DialogResult.OK)
return;
var channels = dlg.SelectedChannels;
var samples = sampleViewer.Samples;
foreach (var channel in channels)
ExtractSamples(channel, samples);
var analysisResult = analyzer.Analyze(settings.Frequency, settings.PreTriggerSamples - 1, dlg.SelectedSettings, channels);
if (analysisResult != null)
{
sampleViewer.BeginUpdate();
sampleViewer.AddAnalyzedChannels(analysisResult);
sampleViewer.EndUpdate();
}
}
}
private void ExtractSamples(ProtocolAnalyzerSelectedChannel channel, uint[]? samples)
{
if (channel == null || samples == null)
return;
int idx = channel.ChannelIndex;
int mask = 1 << idx;
channel.Samples = samples.Select(s => (s & mask) != 0 ? (byte)1 : (byte)0).ToArray();
}
private void btnOpenClose_Click(object sender, EventArgs e)
{
if (driver == null)
{
if (ddSerialPorts.SelectedIndex == -1)
{
MessageBox.Show("Select a serial port to connect.");
return;
}
try
{
driver = new LogicAnalyzerDriver(ddSerialPorts.SelectedItem.ToString(), 115200);
driver.CaptureCompleted += Driver_CaptureCompleted;
}
catch
{
MessageBox.Show("Cannot connect to device.");
return;
}
lblConnectedDevice.Text = driver.DeviceVersion;
ddSerialPorts.Enabled = false;
btnRefresh.Enabled = false;
btnOpenClose.Text = "Close device";
btnCapture.Enabled = true;
btnRepeat.Enabled = true;
}
else
{
driver.Dispose();
driver = null;
lblConnectedDevice.Text = "< None >";
ddSerialPorts.Enabled = true;
btnRefresh.Enabled = true;
btnOpenClose.Text = "Open device";
RefreshPorts();
btnCapture.Enabled = false;
btnRepeat.Enabled = false;
}
}
private void button2_Click(object sender, EventArgs e)
{
RefreshPorts();
}
void RefreshPorts()
{
ddSerialPorts.DataSource = null;
ddSerialPorts.Refresh();
ddSerialPorts.DataSource = SerialPort.GetPortNames();
}
private void btnRepeat_Click(object sender, EventArgs e)
{
if (settings == null)
{
MessageBox.Show("No capture to repeat");
return;
}
BeginCapture();
}
private void btnCapture_Click(object sender, EventArgs e)
{
using (var dialog = new CaptureDialog())
{
if (dialog.ShowDialog() != DialogResult.OK)
return;
settings = dialog.SelectedSettings;
BeginCapture();
}
}
private void BeginCapture()
{
if (settings.TriggerType != 0)
{
if (!driver.StartPatternCapture(settings.Frequency, settings.PreTriggerSamples, settings.PostTriggerSamples, settings.CaptureChannels, settings.TriggerChannel, settings.TriggerBitCount, settings.TriggerPattern, settings.TriggerType == 2 ? true : false))
{
MessageBox.Show("Device reported error starting capture. Restart the device and try again.");
return;
}
}
else
{
if (!driver.StartCapture(settings.Frequency, settings.PreTriggerSamples, settings.PostTriggerSamples, settings.CaptureChannels, settings.TriggerChannel, settings.TriggerInverted))
{
MessageBox.Show("Device reported error starting capture. Restart the device and try again.");
return;
}
}
btnCapture.Enabled = false;
btnRepeat.Enabled = false;
btnOpenClose.Enabled = false;
}
private void scrSamplePos_ValueChanged(object sender, EventArgs e)
{
if (sampleViewer.Samples != null)
{
sampleViewer.BeginUpdate();
sampleViewer.FirstSample = scrSamplePos.Value;
sampleViewer.EndUpdate();
sampleMarker.FirstSample = sampleViewer.FirstSample;
}
}
private void tkInScreen_ValueChanged(object sender, EventArgs e)
{
if (sampleViewer.Samples != null)
{
sampleViewer.BeginUpdate();
sampleViewer.SamplesInScreen = tkInScreen.Value;
sampleViewer.EndUpdate();
sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen;
}
}
private void btnJmpTrigger_Click(object sender, EventArgs e)
{
if (sampleViewer.Samples != null && settings != null)
{
sampleViewer.BeginUpdate();
sampleViewer.FirstSample = Math.Max(settings.PreTriggerSamples - (tkInScreen.Value / 10), 0);
sampleViewer.EndUpdate();
scrSamplePos.Value = sampleViewer.FirstSample;
sampleMarker.FirstSample = sampleViewer.FirstSample;
}
}
private void saveCaptureToolStripMenuItem_Click(object sender, EventArgs e)
{
using (var sf = new SaveFileDialog())
{
sf.Filter = "Logic analyzer captures|*.lac";
if (sf.ShowDialog() != DialogResult.OK)
return;
ExportedCapture ex = new ExportedCapture { Settings = settings, Samples = sampleViewer.Samples, ChannelTexts = channelViewer.ChannelsText, SelectedRegions = sampleViewer.SelectedRegions };
File.WriteAllText(sf.FileName,JsonConvert.SerializeObject(ex, new JsonConverter[] { new SelectedSampleRegion.SelectedSampleRegionConverter() }));
}
}
private void openCaptureToolStripMenuItem_Click(object sender, EventArgs e)
{
using (var sf = new OpenFileDialog())
{
sf.Filter = "Logic analyzer captures|*.lac";
if (sf.ShowDialog() != DialogResult.OK)
return;
ExportedCapture ex = JsonConvert.DeserializeObject<ExportedCapture>(File.ReadAllText(sf.FileName), new JsonConverter[] { new SelectedSampleRegion.SelectedSampleRegionConverter() });
if (ex == null)
return;
settings = ex.Settings;
sampleViewer.BeginUpdate();
sampleViewer.Samples = ex.Samples;
sampleViewer.PreSamples = ex.Settings.PreTriggerSamples;
sampleViewer.ChannelCount = ex.Settings.CaptureChannels.Length;
sampleViewer.SamplesInScreen = Math.Min(100, ex.Samples.Length / 10);
sampleViewer.FirstSample = Math.Max(ex.Settings.PreTriggerSamples - 10, 0);
sampleViewer.ClearRegions();
sampleViewer.ClearAnalyzedChannels();
if (ex.SelectedRegions != null)
sampleViewer.AddRegions(ex.SelectedRegions);
sampleViewer.EndUpdate();
sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen;
sampleMarker.FirstSample = sampleViewer.FirstSample;
sampleMarker.ClearRegions();
if (ex.SelectedRegions != null)
sampleMarker.AddRegions(ex.SelectedRegions);
scrSamplePos.Maximum = ex.Samples.Length - 1;
scrSamplePos.Value = sampleViewer.FirstSample;
tkInScreen.Value = sampleViewer.SamplesInScreen;
channelViewer.Channels = ex.Settings.CaptureChannels;
channelViewer.ChannelsText = ex.ChannelTexts;
saveCaptureToolStripMenuItem.Enabled = true;
availableAnalyzersToolStripMenuItem.Enabled = true;
LoadInfo();
}
}
void LoadInfo()
{
lblFreq.Text = String.Format("{0:n}", settings.Frequency) + " Hz";
lblPreSamples.Text = String.Format("{0:n}", settings.PreTriggerSamples);
lblPostSamples.Text = String.Format("{0:n}", settings.PostTriggerSamples);
lblSamples.Text = String.Format("{0:n}", settings.PostTriggerSamples + settings.PreTriggerSamples);
lblChannels.Text = settings.CaptureChannels.Length.ToString();
lblTrigger.Text = $"Channel {settings.TriggerChannel + 1}";
lblEdge.Text = settings.TriggerInverted ? "Negative" : "Positive";
}
private void sampleMarker_RegionCreated(object sender, RegionEventArgs e)
{
sampleViewer.BeginUpdate();
sampleViewer.AddRegion(e.Region);
sampleViewer.EndUpdate();
}
private void sampleMarker_RegionDeleted(object sender, RegionEventArgs e)
{
sampleViewer.BeginUpdate();
sampleViewer.RemoveRegion(e.Region);
sampleViewer.EndUpdate();
}
}
}

View File

@ -1,63 +0,0 @@
<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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View File

@ -2,12 +2,12 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:LogicAnalyzerMultiplatform.Controls" xmlns:controls="clr-namespace:LogicAnalyzer.Controls"
mc:Ignorable="d" d:DesignWidth="1024" d:DesignHeight="800" mc:Ignorable="d" d:DesignWidth="1024" d:DesignHeight="800"
x:Class="LogicAnalyzerMultiplatform.MainWindow" x:Class="LogicAnalyzer.MainWindow"
Title="LogicAnalyzer - Multiplatform version" Title="LogicAnalyzer - Multiplatform version"
TransparencyLevelHint="AcrylicBlur" TransparencyLevelHint="AcrylicBlur" Icon="/Assets/window.ico"
Background="Transparent" MinWidth="1024" MinHeight="800" Width="1024" Height="800"> Background="Transparent" MinWidth="1024" MinHeight="800" Width="1024" Height="800" WindowStartupLocation="CenterScreen">
<DockPanel VerticalAlignment="Stretch"> <DockPanel VerticalAlignment="Stretch">
<ExperimentalAcrylicBorder IsHitTestVisible="False"> <ExperimentalAcrylicBorder IsHitTestVisible="False">
@ -20,55 +20,60 @@
</ExperimentalAcrylicBorder.Material> </ExperimentalAcrylicBorder.Material>
</ExperimentalAcrylicBorder> </ExperimentalAcrylicBorder>
<Menu DockPanel.Dock="Top" Background="#f0202020"> <Menu DockPanel.Dock="Top" Background="#f0202020">
<MenuItem Header="_File"> <MenuItem Header="_File">
<MenuItem Header="_Open..." Name="mnuOpen"/> <MenuItem Header="_Open..." Name="mnuOpen"/>
<MenuItem Header="_Save..." Name="mnuSave"/> <MenuItem Header="_Save..." Name="mnuSave" IsEnabled="False"/>
<MenuItem Header="E_xport..." Name="mnuExport"/> <MenuItem Header="E_xport..." Name="mnuExport" IsEnabled="False"/>
<Separator/> <Separator/>
<MenuItem Header="_Exit" Name="mnuExit"/> <MenuItem Header="_Exit" Name="mnuExit"/>
</MenuItem> </MenuItem>
<MenuItem Header="_Protocol analyzers" Name="mnuProtocols"> <MenuItem Header="_Protocol analyzers" Name="mnuProtocols" IsEnabled="False">
<MenuItem Header="None"/> <MenuItem Header="None"/>
</MenuItem> </MenuItem>
</Menu> </Menu>
<Grid DockPanel.Dock="Top" Height="48" ColumnDefinitions="*,*" Background="#80303030"> <Grid DockPanel.Dock="Top" Height="48" ColumnDefinitions="*,*" Background="#80303030">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Column="0" Margin="10,0,0,0"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Column="0" Margin="10,0,0,0">
<Button Name="btnRefresh">Refresh</Button> <Button Name="btnRefresh">Refresh</Button>
<ComboBox VerticalAlignment="Center" Margin="10,0,10,0" Name="cbSerialPorts"> <ComboBox VerticalAlignment="Center" Margin="10,0,10,0" Name="ddSerialPorts">
</ComboBox> </ComboBox>
<Button Name="btnOpen">Open device</Button> <Button Name="btnOpenClose">Open device</Button>
<TextBlock VerticalAlignment="Center" Margin="10,0,10,0">Open device:</TextBlock> <TextBlock VerticalAlignment="Center" Margin="10,0,10,0">Current device:</TextBlock>
<TextBlock VerticalAlignment="Center" Name="lblDevice">&lt; None &gt;</TextBlock> <TextBlock VerticalAlignment="Center" Name="lblConnectedDevice">&lt; None &gt;</TextBlock>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Stretch" Grid.Column="1" Margin="0,0,10,0"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Stretch" Grid.Column="1" Margin="0,0,10,0">
<Button Name="btnRepeate">Repeat last capture</Button> <Button Name="btnRepeat" IsEnabled="False" Margin="0,0,10,0">Repeat last capture</Button>
<Button Name="btnCapture">Capture</Button> <Button Name="btnCapture" IsEnabled="False">Capture</Button>
</StackPanel> </StackPanel>
</Grid> </Grid>
<ScrollBar VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Orientation="Horizontal" DockPanel.Dock="Bottom" Name="scbPosition"></ScrollBar> <Grid DockPanel.Dock="Top" Height="32" ColumnDefinitions="140,10*,240" Background="#50808080">
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ColumnDefinitions="140,10*,240" DockPanel.Dock="Bottom"> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" Foreground="LightGray">
<controls:ChannelViewer Name="chViewer" Grid.Row="0" Grid.Column="0" /> Channels
<TextBlock Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center">
CONTENT
</TextBlock> </TextBlock>
<controls:SampleMarker Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Foreground="White" Background="Transparent" Name="sampleMarker"></controls:SampleMarker>
</Grid>
<ScrollBar VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Orientation="Horizontal" DockPanel.Dock="Bottom" Name="scrSamplePos"></ScrollBar>
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ColumnDefinitions="140,10*,240" DockPanel.Dock="Bottom">
<controls:ChannelViewer Name="channelViewer" Grid.Row="0" Grid.Column="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
<controls:SampleViewer Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Name="sampleViewer"></controls:SampleViewer>
<DockPanel Grid.Column="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="#80303030"> <DockPanel Grid.Column="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="#80303030">
<Grid DockPanel.Dock="Top" RowDefinitions="*,*,2*,*,2*" ColumnDefinitions="*,6*,*" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="5,0,5,5" Background="#80404040"> <Grid DockPanel.Dock="Top" RowDefinitions="*,*,2*,*,2*" ColumnDefinitions="*,6*,*" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="5,5,5,5" Background="#80404040">
<TextBlock Grid.ColumnSpan="2" Margin="10"> <TextBlock Grid.ColumnSpan="2" Margin="10">
Adjustments Adjustments
</TextBlock> </TextBlock>
<TextBlock Grid.Row="1" Margin="10"> <TextBlock Grid.Row="1" Margin="10" Name="lblMinSamples">
10 10
</TextBlock> </TextBlock>
<TextBlock Grid.Row="1" Grid.Column="1" Margin="10" HorizontalAlignment="Center"> <TextBlock Grid.Row="1" Grid.Column="1" Margin="10" HorizontalAlignment="Center">
Samples in screen Samples in screen
</TextBlock> </TextBlock>
<TextBlock Grid.Row="1" Grid.Column="2" Margin="10"> <TextBlock Grid.Row="1" Grid.Column="2" Margin="10" Name="lblMaxSamples">
200 200
</TextBlock> </TextBlock>
<Slider Grid.Row="2" Grid.ColumnSpan="3" Margin="10,0,10,0" TickFrequency="10" TickPlacement="BottomRight" Minimum="10" Maximum="200" Value="100" Name="sldSamples"></Slider> <Slider Grid.Row="2" Grid.ColumnSpan="3" Margin="10,0,10,0" TickFrequency="10" TickPlacement="BottomRight" Minimum="10" Maximum="200" Value="100" Name="tkInScreen"></Slider>
</Grid> </Grid>
<StackPanel DockPanel.Dock="Bottom" VerticalAlignment="Bottom" Margin="5,0,5,5" Background="#80404040" > <StackPanel DockPanel.Dock="Bottom" VerticalAlignment="Bottom" Margin="5,0,5,5" Background="#80404040">
<TextBlock Margin="5"> <TextBlock Margin="5">
Information Information
</TextBlock> </TextBlock>
@ -76,57 +81,43 @@
<TextBlock Margin="2" HorizontalAlignment="Left"> <TextBlock Margin="2" HorizontalAlignment="Left">
- Frequency: - Frequency:
</TextBlock> </TextBlock>
<TextBlock Margin="2" HorizontalAlignment="Right" Name="lblFreq"> <TextBlock Margin="2" HorizontalAlignment="Right" Name="lblFreq"></TextBlock>
100000000
</TextBlock>
</Panel> </Panel>
<Panel> <Panel>
<TextBlock Margin="2" HorizontalAlignment="Left"> <TextBlock Margin="2" HorizontalAlignment="Left">
- Total samples: - Total samples:
</TextBlock> </TextBlock>
<TextBlock Margin="2" HorizontalAlignment="Right" Name="lblSamples"> <TextBlock Margin="2" HorizontalAlignment="Right" Name="lblSamples"></TextBlock>
1536
</TextBlock>
</Panel> </Panel>
<Panel> <Panel>
<TextBlock Margin="2" HorizontalAlignment="Left"> <TextBlock Margin="2" HorizontalAlignment="Left">
- Pre samples: - Pre samples:
</TextBlock> </TextBlock>
<TextBlock Margin="2" HorizontalAlignment="Right" Name="lblPreSamples"> <TextBlock Margin="2" HorizontalAlignment="Right" Name="lblPreSamples"></TextBlock>
512
</TextBlock>
</Panel> </Panel>
<Panel> <Panel>
<TextBlock Margin="2" HorizontalAlignment="Left"> <TextBlock Margin="2" HorizontalAlignment="Left">
- Post samples: - Post samples:
</TextBlock> </TextBlock>
<TextBlock Margin="2" HorizontalAlignment="Right" Name="lblPostSamples"> <TextBlock Margin="2" HorizontalAlignment="Right" Name="lblPostSamples"></TextBlock>
1024
</TextBlock>
</Panel> </Panel>
<Panel> <Panel>
<TextBlock Margin="2" HorizontalAlignment="Left"> <TextBlock Margin="2" HorizontalAlignment="Left">
- Channels: - Channels:
</TextBlock> </TextBlock>
<TextBlock Margin="2" HorizontalAlignment="Right" Name="lblChannels"> <TextBlock Margin="2" HorizontalAlignment="Right" Name="lblChannels"></TextBlock>
4
</TextBlock>
</Panel> </Panel>
<Panel> <Panel>
<TextBlock Margin="2" HorizontalAlignment="Left"> <TextBlock Margin="2" HorizontalAlignment="Left">
- Trigger: - Trigger:
</TextBlock> </TextBlock>
<TextBlock Margin="2" HorizontalAlignment="Right" Name="lblTrigger"> <TextBlock Margin="2" HorizontalAlignment="Right" Name="lblTrigger"></TextBlock>
Simple, Channel 1
</TextBlock>
</Panel> </Panel>
<Panel> <Panel>
<TextBlock Margin="2" HorizontalAlignment="Left"> <TextBlock Margin="2" HorizontalAlignment="Left">
- Value: - Value:
</TextBlock> </TextBlock>
<TextBlock Margin="2" HorizontalAlignment="Right" Name="lblValue"> <TextBlock Margin="2" HorizontalAlignment="Right" Name="lblValue"></TextBlock>
"10010"
</TextBlock>
</Panel> </Panel>
</StackPanel> </StackPanel>
</DockPanel> </DockPanel>

View File

@ -0,0 +1,472 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Threading;
using LogicAnalyzer.Classes;
using LogicAnalyzer.Controls;
using LogicAnalyzer.Dialogs;
using LogicAnalyzer.Extensions;
using LogicAnalyzer.Protocols;
using MessageBox.Avalonia;
using Newtonsoft.Json;
using SharedDriver;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace LogicAnalyzer
{
public partial class MainWindow : Window
{
LogicAnalyzerDriver driver;
CaptureSettings settings;
ProtocolAnalyzerLoader pLoader;
public static MainWindow? Instance { get; private set; }
public MainWindow()
{
Instance = this;
InitializeComponent();
btnRefresh.Click += btnRefresh_Click;
btnOpenClose.Click += btnOpenClose_Click;
btnRepeat.Click += btnRepeat_Click;
btnCapture.Click += btnCapture_Click;
sampleMarker.RegionCreated += sampleMarker_RegionCreated;
sampleMarker.RegionDeleted += sampleMarker_RegionDeleted;
tkInScreen.PropertyChanged += tkInScreen_ValueChanged;
scrSamplePos.Scroll += scrSamplePos_ValueChanged;
mnuOpen.Click += mnuOpen_Click;
mnuSave.Click += mnuSave_Click;
mnuExit.Click += MnuExit_Click;
mnuExport.Click += MnuExport_Click;
LoadAnalyzers();
RefreshPorts();
}
protected override void OnOpened(EventArgs e)
{
base.OnOpened(e);
this.FixStartupPosition();
}
private async void MnuExport_Click(object? sender, RoutedEventArgs e)
{
var sf = new SaveFileDialog();
{
sf.Filters.Add(new FileDialogFilter { Name = "Comma-separated values file", Extensions = new System.Collections.Generic.List<string> { "csv" } });
var file = await sf.ShowAsync(this);
if (string.IsNullOrWhiteSpace(file))
return;
StreamWriter sw = new StreamWriter(File.Create(file));
sw.WriteLine(String.Join(',', channelViewer.Channels.Select(c => string.IsNullOrWhiteSpace(channelViewer.ChannelsText[c]) ? $"Channel {c + 1}" : channelViewer.ChannelsText[c]).ToArray()));
StringBuilder sb = new StringBuilder();
for (int sample = 0; sample < sampleViewer.Samples.Length; sample++)
{
sb.Clear();
for (int buc = 0; buc < channelViewer.Channels.Length; buc++)
{
if ((sampleViewer.Samples[sample] & (1 << buc)) == 0)
sb.Append("0,");
else
sb.Append("1,");
}
sb.Remove(sb.Length - 1, 1);
sw.WriteLine(sb.ToString());
}
sw.Close();
sw.Dispose();
}
}
private void MnuExit_Click(object? sender, RoutedEventArgs e)
{
Close();
}
private void Driver_CaptureCompleted(object? sender, CaptureEventArgs e)
{
Dispatcher.UIThread.InvokeAsync(() =>
{
sampleViewer.BeginUpdate();
sampleViewer.Samples = e.Samples;
sampleViewer.PreSamples = e.PreSamples;
sampleViewer.ChannelCount = e.ChannelCount;
sampleViewer.SamplesInScreen = Math.Min(100, e.Samples.Length / 10);
sampleViewer.FirstSample = Math.Max(e.PreSamples - 10, 0);
sampleViewer.ClearRegions();
sampleViewer.ClearAnalyzedChannels();
sampleViewer.EndUpdate();
scrSamplePos.Maximum = e.Samples.Length - 1;
scrSamplePos.Value = sampleViewer.FirstSample;
tkInScreen.Value = sampleViewer.SamplesInScreen;
channelViewer.Channels = settings.CaptureChannels;
sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen;
sampleMarker.FirstSample = sampleViewer.FirstSample;
sampleMarker.ClearRegions();
btnCapture.IsEnabled = true;
btnRepeat.IsEnabled = true;
btnOpenClose.IsEnabled = true;
mnuProtocols.IsEnabled = true;
mnuSave.IsEnabled = true;
mnuExport.IsEnabled = true;
LoadInfo();
});
}
private void Form1_Load(object sender, EventArgs e)
{
LoadAnalyzers();
RefreshPorts();
}
void LoadAnalyzers()
{
pLoader = new ProtocolAnalyzerLoader(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "analyzers"));
var protocols = pLoader.ProtocolNames;
mnuProtocols.Items = null;
if (protocols.Length == 0)
mnuProtocols.Items = new MenuItem[] { new MenuItem { Header = "<- None ->" } };
else
{
List<MenuItem> finalItems = new List<MenuItem>();
finalItems.AddRange(pLoader.ProtocolNames.Select(p =>
{
var itm = new MenuItem { Header = p, Tag = p };
itm.Click += ProtocolAnalyzer_Click;
return itm;
}).ToArray());
var clearItem = new MenuItem { Header = "C_lear analysis data" };
clearItem.Click += ClearItem_Click;
finalItems.Add(clearItem);
mnuProtocols.Items = finalItems;
}
}
private void ClearItem_Click(object? sender, RoutedEventArgs e)
{
sampleViewer.BeginUpdate();
sampleViewer.ClearAnalyzedChannels();
sampleViewer.EndUpdate();
}
private async void ProtocolAnalyzer_Click(object? sender, RoutedEventArgs e)
{
var item = (sender as MenuItem)?.Tag?.ToString();
if (item == null)
return;
var analyzer = pLoader.GetAnalyzer(item);
var dlg = new ProtocolAnalyzerSettingsDialog();
{
dlg.Analyzer = analyzer;
dlg.Channels = channelViewer.Channels;
if (await dlg.ShowDialog<bool>(this) != true)
return;
var channels = dlg.SelectedChannels;
var samples = sampleViewer.Samples;
foreach (var channel in channels)
ExtractSamples(channel, samples);
var analysisResult = analyzer.Analyze(settings.Frequency, settings.PreTriggerSamples - 1, dlg.SelectedSettings, channels);
if (analysisResult != null)
{
sampleViewer.BeginUpdate();
sampleViewer.AddAnalyzedChannels(analysisResult);
sampleViewer.EndUpdate();
}
}
}
private void ExtractSamples(ProtocolAnalyzerSelectedChannel channel, uint[]? samples)
{
if (channel == null || samples == null)
return;
int idx = channel.ChannelIndex;
int mask = 1 << idx;
channel.Samples = samples.Select(s => (s & mask) != 0 ? (byte)1 : (byte)0).ToArray();
}
private async void btnOpenClose_Click(object? sender, EventArgs e)
{
if (driver == null)
{
if (ddSerialPorts.SelectedIndex == -1)
{
await ShowError("Error", "Select a serial port to connect.");
return;
}
try
{
driver = new LogicAnalyzerDriver(ddSerialPorts.SelectedItem?.ToString() ?? "", 115200);
driver.CaptureCompleted += Driver_CaptureCompleted;
}
catch(Exception ex)
{
await ShowError("Error", $"Cannot connect to device ({ex.Message}).");
return;
}
lblConnectedDevice.Text = driver.DeviceVersion;
ddSerialPorts.IsEnabled = false;
btnRefresh.IsEnabled = false;
btnOpenClose.Content = "Close device";
btnCapture.IsEnabled = true;
btnRepeat.IsEnabled = true;
}
else
{
driver.Dispose();
driver = null;
lblConnectedDevice.Text = "< None >";
ddSerialPorts.IsEnabled = true;
btnRefresh.IsEnabled = true;
btnOpenClose.Content = "Open device";
RefreshPorts();
btnCapture.IsEnabled = false;
btnRepeat.IsEnabled = false;
}
}
private void btnRefresh_Click(object? sender, RoutedEventArgs e)
{
RefreshPorts();
}
void RefreshPorts()
{
ddSerialPorts.Items = null;
ddSerialPorts.Items = SerialPort.GetPortNames();
}
private async void btnRepeat_Click(object? sender, RoutedEventArgs e)
{
if (settings == null)
{
await ShowError("Error", "No capture to repeat");
return;
}
BeginCapture();
}
private async void btnCapture_Click(object? sender, RoutedEventArgs e)
{
var dialog = new CaptureDialog();
{
if (!await dialog.ShowDialog<bool>(this))
return;
settings = dialog.SelectedSettings;
BeginCapture();
}
}
private async void BeginCapture()
{
if (settings.TriggerType != 0)
{
if (!driver.StartPatternCapture(settings.Frequency, settings.PreTriggerSamples, settings.PostTriggerSamples, settings.CaptureChannels, settings.TriggerChannel, settings.TriggerBitCount, settings.TriggerPattern, settings.TriggerType == 2 ? true : false))
{
await ShowError("Error", "Device reported error starting capture. Restart the device and try again.");
return;
}
}
else
{
if (!driver.StartCapture(settings.Frequency, settings.PreTriggerSamples, settings.PostTriggerSamples, settings.CaptureChannels, settings.TriggerChannel, settings.TriggerInverted))
{
await ShowError("Error", "Device reported error starting capture. Restart the device and try again.");
return;
}
}
btnCapture.IsEnabled = false;
btnRepeat.IsEnabled = false;
btnOpenClose.IsEnabled = false;
}
private void scrSamplePos_ValueChanged(object? sender, ScrollEventArgs e)
{
if (sampleViewer.Samples != null)
{
sampleViewer.BeginUpdate();
sampleViewer.FirstSample = (int)scrSamplePos.Value;
sampleViewer.EndUpdate();
sampleMarker.FirstSample = sampleViewer.FirstSample;
}
}
private void tkInScreen_ValueChanged(object? sender, Avalonia.AvaloniaPropertyChangedEventArgs e)
{
if (sampleViewer.Samples != null)
{
sampleViewer.BeginUpdate();
sampleViewer.SamplesInScreen = (int)tkInScreen.Value;
sampleViewer.EndUpdate();
sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen;
}
}
private void btnJmpTrigger_Click(object? sender, RoutedEventArgs e)
{
if (sampleViewer.Samples != null && settings != null)
{
sampleViewer.BeginUpdate();
sampleViewer.FirstSample = (int)Math.Max(settings.PreTriggerSamples - (tkInScreen.Value / 10), 0);
sampleViewer.EndUpdate();
scrSamplePos.Value = sampleViewer.FirstSample;
sampleMarker.FirstSample = sampleViewer.FirstSample;
}
}
private async void mnuSave_Click(object? sender, RoutedEventArgs e)
{
var sf = new SaveFileDialog();
{
sf.Filters.Add( new FileDialogFilter { Name = "Logic analyzer captures", Extensions = new System.Collections.Generic.List<string> { "lac" } });
var file = await sf.ShowAsync(this);
if (string.IsNullOrWhiteSpace(file))
return;
ExportedCapture ex = new ExportedCapture { Settings = settings, Samples = sampleViewer.Samples, ChannelTexts = channelViewer.ChannelsText, SelectedRegions = sampleViewer.SelectedRegions };
File.WriteAllText(file, JsonConvert.SerializeObject(ex, new JsonConverter[] { new SelectedSampleRegion.SelectedSampleRegionConverter() }));
}
}
private async void mnuOpen_Click(object? sender, RoutedEventArgs e)
{
var sf = new OpenFileDialog();
{
sf.Filters.Add(new FileDialogFilter { Name = "Logic analyzer captures", Extensions = new System.Collections.Generic.List<string> { "lac" } });
var file = (await sf.ShowAsync(this))?.FirstOrDefault();
if (string.IsNullOrWhiteSpace(file))
return;
ExportedCapture ex = JsonConvert.DeserializeObject<ExportedCapture>(File.ReadAllText(file), new JsonConverter[] { new SelectedSampleRegion.SelectedSampleRegionConverter() });
if (ex == null)
return;
settings = ex.Settings;
sampleViewer.BeginUpdate();
sampleViewer.Samples = ex.Samples;
sampleViewer.PreSamples = ex.Settings.PreTriggerSamples;
sampleViewer.ChannelCount = ex.Settings.CaptureChannels.Length;
sampleViewer.SamplesInScreen = Math.Min(100, ex.Samples.Length / 10);
sampleViewer.FirstSample = Math.Max(ex.Settings.PreTriggerSamples - 10, 0);
sampleViewer.ClearRegions();
sampleViewer.ClearAnalyzedChannels();
if (ex.SelectedRegions != null)
sampleViewer.AddRegions(ex.SelectedRegions);
sampleViewer.EndUpdate();
sampleMarker.VisibleSamples = sampleViewer.SamplesInScreen;
sampleMarker.FirstSample = sampleViewer.FirstSample;
sampleMarker.ClearRegions();
if (ex.SelectedRegions != null)
sampleMarker.AddRegions(ex.SelectedRegions);
scrSamplePos.Maximum = ex.Samples.Length - 1;
scrSamplePos.Value = sampleViewer.FirstSample;
tkInScreen.Value = sampleViewer.SamplesInScreen;
channelViewer.Channels = ex.Settings.CaptureChannels;
channelViewer.ChannelsText = ex.ChannelTexts;
mnuSave.IsEnabled = true;
mnuProtocols.IsEnabled = true;
mnuExport.IsEnabled = true;
LoadInfo();
}
}
void LoadInfo()
{
string triggerType = settings.TriggerType == 0 ? "Edge" : (settings.TriggerType == 1 ? "Complex" : "Fast");
lblFreq.Text = String.Format("{0:n0}", settings.Frequency) + " Hz";
lblPreSamples.Text = String.Format("{0:n0}", settings.PreTriggerSamples);
lblPostSamples.Text = String.Format("{0:n0}", settings.PostTriggerSamples);
lblSamples.Text = String.Format("{0:n0}", settings.PostTriggerSamples + settings.PreTriggerSamples);
lblChannels.Text = settings.CaptureChannels.Length.ToString();
lblTrigger.Text = $"{triggerType}, channel {settings.TriggerChannel + 1}";
lblValue.Text = settings.TriggerType == 0 ? (settings.TriggerInverted ? "Negative" : "Positive") : GenerateStringTrigger(settings.TriggerPattern, settings.TriggerBitCount);
}
private string GenerateStringTrigger(ushort triggerPattern, int bitCount)
{
string value = "";
for(int buc = 0; buc < bitCount; buc++)
value += (triggerPattern & (1 << buc)) == 0 ? "0" : "1";
return value;
}
private void sampleMarker_RegionCreated(object? sender, RegionEventArgs e)
{
sampleViewer.BeginUpdate();
sampleViewer.AddRegion(e.Region);
sampleViewer.EndUpdate();
}
private void sampleMarker_RegionDeleted(object? sender, RegionEventArgs e)
{
sampleViewer.BeginUpdate();
sampleViewer.RemoveRegion(e.Region);
sampleViewer.EndUpdate();
}
private async Task ShowError(string Title, string Text)
{
var box = MessageBoxManager.GetMessageBoxStandardWindow(Title, Text, icon: MessageBox.Avalonia.Enums.Icon.Error);
var prop = box.GetType().GetField("_window", BindingFlags.Instance | BindingFlags.NonPublic);
var win = prop.GetValue(box) as Window;
win.Icon = this.Icon;
await box.Show();
}
}
}

View File

@ -1,159 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LogicAnalyzer.MenuColors
{
public class SubmenuColorTable : ProfessionalColorTable
{
public override Color MenuItemSelected
{
get { return ColorTranslator.FromHtml("#302E2D"); }
}
public override Color MenuItemBorder
{
get { return Color.Silver; }
}
public override Color ToolStripDropDownBackground
{
get { return ColorTranslator.FromHtml("#21201F"); }
}
public override Color ToolStripContentPanelGradientBegin
{
get { return ColorTranslator.FromHtml("#21201F"); }
}
}
public class LeftMenuColorTable : ProfessionalColorTable
{
public override Color MenuItemBorder
{
get { return ColorTranslator.FromHtml("#BAB9B9"); }
}
public override Color MenuBorder //added for changing the menu border
{
get { return Color.Silver; }
}
public override Color MenuItemPressedGradientBegin
{
get { return ColorTranslator.FromHtml("#4C4A48"); }
}
public override Color MenuItemPressedGradientEnd
{
get { return ColorTranslator.FromHtml("#5F5D5B"); }
}
public override Color ToolStripBorder
{
get { return ColorTranslator.FromHtml("#4C4A48"); }
}
public override Color MenuItemSelectedGradientBegin
{
get { return ColorTranslator.FromHtml("#4C4A48"); }
}
public override Color MenuItemSelectedGradientEnd
{
get { return ColorTranslator.FromHtml("#5F5D5B"); }
}
public override Color ToolStripDropDownBackground
{
get { return ColorTranslator.FromHtml("#404040"); }
}
public override Color ToolStripGradientBegin
{
get { return ColorTranslator.FromHtml("#404040"); }
}
public override Color ToolStripGradientEnd
{
get { return ColorTranslator.FromHtml("#404040"); }
}
public override Color ToolStripGradientMiddle
{
get { return ColorTranslator.FromHtml("#404040"); }
}
}
public class MyColorTable : ProfessionalColorTable
{
/// <summary>
/// Gets the starting color of the gradient used when
/// a top-level System.Windows.Forms.ToolStripMenuItem is pressed.
/// </summary>
public override Color MenuItemPressedGradientBegin => Color.DarkGray;
/// <summary>
/// Gets the end color of the gradient used when a top-level
/// System.Windows.Forms.ToolStripMenuItem is pressed.
/// </summary>
public override Color MenuItemPressedGradientEnd => Color.DarkGray;
/// <summary>
/// Gets the border color to use with a
/// System.Windows.Forms.ToolStripMenuItem.
/// </summary>
public override Color MenuItemBorder => Color.DarkGray;
/// <summary>
/// Gets the starting color of the gradient used when the
/// System.Windows.Forms.ToolStripMenuItem is selected.
/// </summary>
public override Color MenuItemSelectedGradientBegin => Color.Silver;
/// <summary>
/// Gets the end color of the gradient used when the
/// System.Windows.Forms.ToolStripMenuItem is selected.
/// </summary>
public override Color MenuItemSelectedGradientEnd => Color.DarkGray;
/// <summary>
/// Gets the solid background color of the
/// System.Windows.Forms.ToolStripDropDown.
/// </summary>
public override Color ToolStripDropDownBackground => Color.DarkGray;
/// <summary>
/// Gets the starting color of the gradient used in the image
/// margin of a System.Windows.Forms.ToolStripDropDownMenu.
/// </summary>
public override Color ImageMarginGradientBegin => Color.DarkGray;
/// <summary>
/// Gets the middle color of the gradient used in the image
/// margin of a System.Windows.Forms.ToolStripDropDownMenu.
/// </summary>
public override Color ImageMarginGradientMiddle => Color.DarkGray;
/// <summary>
/// Gets the end color of the gradient used in the image
/// margin of a System.Windows.Forms.ToolStripDropDownMenu.
/// </summary>
public override Color ImageMarginGradientEnd => Color.DarkGray;
/// <summary>
/// Gets the color to use to for shadow effects on
/// the System.Windows.Forms.ToolStripSeparator.
/// </summary>
public override Color SeparatorDark => Color.Black;
/// <summary>
/// Gets the color that is the border color to use
/// on a System.Windows.Forms.MenuStrip.
/// </summary>
public override Color MenuBorder => Color.DarkGray;
}
}

View File

@ -1,17 +1,23 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using System;
namespace LogicAnalyzer namespace LogicAnalyzer
{ {
internal static class Program internal class Program
{ {
/// <summary> // Initialization code. Don't use any Avalonia, third-party APIs or any
/// The main entry point for the application. // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
/// </summary> // yet and stuff might break.
[STAThread] [STAThread]
static void Main() public static void Main(string[] args) => BuildAvaloniaApp()
{ .StartWithClassicDesktopLifetime(args);
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration. // Avalonia configuration, don't remove; also used by visual designer.
ApplicationConfiguration.Initialize(); public static AppBuilder BuildAvaloniaApp()
Application.Run(new MainForm()); => AppBuilder.Configure<App>()
} .UsePlatformDetect()
.LogToTrace();
} }
} }

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net6.0-windows\publish\win-x86\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net6.0-windows</TargetFramework>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>False</PublishSingleFile>
<PublishReadyToRun>False</PublishReadyToRun>
</PropertyGroup>
</Project>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<History>False|2022-07-13T14:45:35.3720563Z;True|2022-07-13T16:45:17.3874110+02:00;True|2022-07-08T18:00:09.3470232+02:00;True|2022-07-08T16:55:29.2331928+02:00;True|2022-07-03T16:20:52.1046389+02:00;</History>
</PropertyGroup>
</Project>

View File

@ -1,63 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// Este código fue generado por una herramienta.
// Versión de runtime:4.0.30319.42000
//
// Los cambios en este archivo podrían causar un comportamiento incorrecto y se perderán si
// se vuelve a generar el código.
// </auto-generated>
//------------------------------------------------------------------------------
namespace LogicAnalyzer.Properties {
using System;
/// <summary>
/// Clase de recurso fuertemente tipado, para buscar cadenas traducidas, etc.
/// </summary>
// StronglyTypedResourceBuilder generó automáticamente esta clase
// a través de una herramienta como ResGen o Visual Studio.
// Para agregar o quitar un miembro, edite el archivo .ResX y, a continuación, vuelva a ejecutar ResGen
// con la opción /str o recompile su proyecto de VS.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Devuelve la instancia de ResourceManager almacenada en caché utilizada por esta clase.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LogicAnalyzer.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Reemplaza la propiedad CurrentUICulture del subproceso actual para todas las
/// búsquedas de recursos mediante esta clase de recurso fuertemente tipado.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,4 +1,6 @@
using System; using Avalonia;
using Avalonia.Media;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -82,77 +84,13 @@ namespace LogicAnalyzer.Protocols
public Color ForeColor { get; set; } public Color ForeColor { get; set; }
public Color BackColor { get; set; } public Color BackColor { get; set; }
private SolidBrush? backBrush; public void Render(ProtocolAnalyzerDataSegment Segment, DrawingContext Context, Rect RenderArea)
public SolidBrush BackBrush
{ {
get SegmentRenderer.RenderSegment(this, Segment, Context, RenderArea);
{
if (backBrush == null)
backBrush = new SolidBrush(BackColor);
else
{
if (backBrush.Color != BackColor)
{
backBrush.Dispose();
backBrush = new SolidBrush(BackColor);
}
}
return backBrush;
}
}
private SolidBrush? foreBrush;
public SolidBrush ForeBrush
{
get
{
if (foreBrush == null)
foreBrush = new SolidBrush(ForeColor);
else
{
if (foreBrush.Color != ForeColor)
{
foreBrush.Dispose();
foreBrush = new SolidBrush(ForeColor);
}
}
return foreBrush;
}
}
private Pen? forePen;
public Pen ForePen
{
get
{
if (forePen == null)
forePen = new Pen(ForeColor);
else
{
if (forePen.Color != ForeColor)
{
forePen.Dispose();
forePen = new Pen(ForeColor);
}
}
return forePen;
}
}
public void Render(ProtocolAnalyzerDataSegment Segment, Graphics G, RectangleF RenderArea)
{
SegmentRenderer.RenderSegment(this, Segment, G, RenderArea);
} }
public void Dispose() public void Dispose()
{ {
if (backBrush != null)
{
backBrush.Dispose();
backBrush = null;
}
if (forePen != null)
{
forePen.Dispose();
forePen = null;
}
} }
} }
public class ProtocolAnalyzerDataSegment public class ProtocolAnalyzerDataSegment

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;

View File

@ -1,4 +1,6 @@
using System; using Avalonia;
using Avalonia.Media;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -8,6 +10,6 @@ namespace LogicAnalyzer.Protocols
{ {
public abstract class ProtocolAnalyzerSegmentRendererBase public abstract class ProtocolAnalyzerSegmentRendererBase
{ {
public abstract void RenderSegment(ProtocolAnalyzedChannel Channel, ProtocolAnalyzerDataSegment Segment, Graphics G, RectangleF RenderArea); public abstract void RenderSegment(ProtocolAnalyzedChannel Channel, ProtocolAnalyzerDataSegment Segment, DrawingContext Context, Rect RenderArea);
} }
} }

View File

@ -1,4 +1,7 @@
using System; using Avalonia;
using Avalonia.Media;
using LogicAnalyzer.Classes;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -8,15 +11,14 @@ namespace LogicAnalyzer.Protocols
{ {
public class SimpleSegmentRenderer : ProtocolAnalyzerSegmentRendererBase public class SimpleSegmentRenderer : ProtocolAnalyzerSegmentRendererBase
{ {
static StringFormat textFormatCenter = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center }; static Typeface segmentFont = new Typeface("Segoe UI");
static Font segmentFont = new Font("Segoe UI", 9, FontStyle.Regular); public override void RenderSegment(ProtocolAnalyzedChannel Channel, ProtocolAnalyzerDataSegment Segment, DrawingContext G, Rect RenderArea)
public override void RenderSegment(ProtocolAnalyzedChannel Channel, ProtocolAnalyzerDataSegment Segment, Graphics G, RectangleF RenderArea)
{ {
G.FillRectangle(Channel.BackBrush, RenderArea); G.FillRectangle(GraphicObjectsCache.GetBrush(Channel.BackColor), RenderArea);
G.DrawRectangle(Channel.ForePen, Rectangle.Round(RenderArea)); G.DrawRectangle(GraphicObjectsCache.GetPen(Channel.ForeColor, 1), RenderArea);
G.DrawString(Segment.Value, segmentFont, Channel.ForeBrush, RenderArea, textFormatCenter); FormattedText text = new FormattedText(Segment.Value, segmentFont, 12, TextAlignment.Center, TextWrapping.NoWrap, Size.Infinity);
G.DrawText(GraphicObjectsCache.GetBrush(Channel.ForeColor), new Point(RenderArea.X + (RenderArea.Width / 2 - text.Bounds.Width / 2), RenderArea.Y + (RenderArea.Height / 2 - text.Bounds.Height / 2)), text);
} }
} }
} }

View File

@ -1,64 +0,0 @@
namespace LogicAnalyzer
{
partial class Splash
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#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()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Splash));
this.tmrControl = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout();
//
// tmrControl
//
this.tmrControl.Enabled = true;
this.tmrControl.Interval = 10;
this.tmrControl.Tick += new System.EventHandler(this.tmrControl_Tick);
//
// Splash
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
this.ClientSize = new System.Drawing.Size(754, 432);
this.ControlBox = false;
this.DoubleBuffered = true;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Splash";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Splash";
this.TopMost = true;
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Timer tmrControl;
}
}

View File

@ -1,46 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace LogicAnalyzer
{
public partial class Splash : Form
{
[DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
private static extern IntPtr CreateRoundRectRgn
(
int nLeftRect, // x-coordinate of upper-left corner
int nTopRect, // y-coordinate of upper-left corner
int nRightRect, // x-coordinate of lower-right corner
int nBottomRect, // y-coordinate of lower-right corner
int nWidthEllipse, // width of ellipse
int nHeightEllipse // height of ellipse
);
int cnt = 0;
public Splash()
{
InitializeComponent();
Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, Width, Height, 30, 30));
}
private void tmrControl_Tick(object sender, EventArgs e)
{
this.BringToFront();
cnt++;
if (cnt >= 200)
{
tmrControl.Enabled = false;
this.Close();
}
}
}
}

View File

@ -1,940 +0,0 @@
<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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="tmrControl.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
u+MAAAAASUVORK5CYII=
</value>
</data>
</root>

View File

@ -0,0 +1,9 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="Window.tool_window Panel#PART_MinimiseButton">
<Setter Property="IsVisible" Value="False"></Setter>
</Style>
<Style Selector="Window.tool_window Panel#PART_RestoreButton">
<Setter Property="IsVisible" Value="False"></Setter>
</Style>
</Styles>

View File

@ -1,30 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<!--Avalonia doesen't support TrimMode=link currently,but we are working on that https://github.com/AvaloniaUI/Avalonia/issues/6892 -->
<TrimMode>copyused</TrimMode>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
</PropertyGroup>
<ItemGroup>
<None Remove=".gitignore" />
</ItemGroup>
<ItemGroup>
<!--This helps with theme dll-s trimming.
If you will publish your application in self-contained mode with p:PublishTrimmed=true and it will use Fluent theme Default theme will be trimmed from the output and vice versa.
https://github.com/AvaloniaUI/Avalonia/issues/5593 -->
<TrimmableAssembly Include="Avalonia.Themes.Fluent" />
<TrimmableAssembly Include="Avalonia.Themes.Default" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.14" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.14" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.14" />
<PackageReference Include="XamlNameReferenceGenerator" Version="1.3.4" />
</ItemGroup>
<ItemGroup>
<Folder Include="Styles\" />
</ItemGroup>
</Project>

View File

@ -1,12 +0,0 @@
using Avalonia.Controls;
namespace LogicAnalyzerMultiplatform
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}

View File

@ -1,23 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using System;
namespace LogicAnalyzerMultiplatform
{
internal class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.LogToTrace();
}
}

View File

@ -1,5 +1,5 @@
using LogicAnalyzer.Protocols; using Avalonia.Media;
using System.Drawing; using LogicAnalyzer.Protocols;
using System.Text; using System.Text;
namespace SPIProtocolAnalyzer namespace SPIProtocolAnalyzer
@ -119,7 +119,7 @@ namespace SPIProtocolAnalyzer
} }
} }
var result = new ProtocolAnalyzedChannel(ckChannel.SignalName, ckChannel.ChannelIndex, renderer, segments.ToArray(), Color.White, Color.FromArgb(90, Color.Blue)); var result = new ProtocolAnalyzedChannel(ckChannel.SignalName, ckChannel.ChannelIndex, renderer, segments.ToArray(), Colors.White, Color.FromArgb(90, Colors.Blue.R, Colors.Blue.G, Colors.Blue.B));
return result; return result;
} }
@ -129,10 +129,10 @@ namespace SPIProtocolAnalyzer
List<ProtocolAnalyzedChannel> results = new List<ProtocolAnalyzedChannel>(); List<ProtocolAnalyzedChannel> results = new List<ProtocolAnalyzedChannel>();
if (misoChannel != null) if (misoChannel != null)
results.Add(AnalyzeChannel(ranges, ckChannel, misoChannel, shiftOrder, cpol, cpha, Color.FromArgb(100, Color.Red))); results.Add(AnalyzeChannel(ranges, ckChannel, misoChannel, shiftOrder, cpol, cpha, Color.FromArgb(100, Colors.Red.R, Colors.Red.G, Colors.Red.B)));
if (mosiChannel != null) if (mosiChannel != null)
results.Add(AnalyzeChannel(ranges, ckChannel, mosiChannel, shiftOrder, cpol, cpha, Color.FromArgb(100, Color.Green))); results.Add(AnalyzeChannel(ranges, ckChannel, mosiChannel, shiftOrder, cpol, cpha, Color.FromArgb(100, Colors.Green.R, Colors.Green.G, Colors.Green.B)));
return results; return results;
} }
@ -180,7 +180,7 @@ namespace SPIProtocolAnalyzer
} }
} }
var result = new ProtocolAnalyzedChannel(dataChannel.SignalName, dataChannel.ChannelIndex, renderer, segments.ToArray(), Color.White, channelColor); var result = new ProtocolAnalyzedChannel(dataChannel.SignalName, dataChannel.ChannelIndex, renderer, segments.ToArray(), Colors.White, channelColor);
return result; return result;
} }
@ -306,7 +306,7 @@ namespace SPIProtocolAnalyzer
private int FindSample(int Start, byte[] Samples, int Value) private int FindSample(int Start, byte[] Samples, int Value)
{ {
for (int i = Start; i < Samples.Length; i++) for (int i = Start; i < Samples.Length; i++)
if(Samples[i] == Value) if (Samples[i] == Value)
return i; return i;
return -1; return -1;
@ -314,7 +314,7 @@ namespace SPIProtocolAnalyzer
private IEnumerable<ActiveRange> FindActiveRanges(ProtocolAnalyzerSelectedChannel? csChannel, int length) private IEnumerable<ActiveRange> FindActiveRanges(ProtocolAnalyzerSelectedChannel? csChannel, int length)
{ {
if(csChannel == null) if (csChannel == null)
return new ActiveRange[] { new ActiveRange { FirstSample = 0, LastSample = length - 1 } }; return new ActiveRange[] { new ActiveRange { FirstSample = 0, LastSample = length - 1 } };
List<ActiveRange> ranges = new List<ActiveRange>(); List<ActiveRange> ranges = new List<ActiveRange>();

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>

View File

@ -26,6 +26,8 @@ namespace SharedDriver
sp.RtsEnable = true; sp.RtsEnable = true;
sp.DtrEnable = true; sp.DtrEnable = true;
sp.NewLine = "\n"; sp.NewLine = "\n";
sp.ReadBufferSize = 1024 * 1024;
sp.WriteBufferSize = 1024 * 1024;
sp.Open(); sp.Open();
baseStream = sp.BaseStream; baseStream = sp.BaseStream;
@ -86,7 +88,7 @@ namespace SharedDriver
if (result == "CAPTURE_STARTED") if (result == "CAPTURE_STARTED")
{ {
capturing = true; capturing = true;
Task.Run(ReadCapture); Task.Run(() => ReadCapture(PreSamples + PostSamples));
return true; return true;
} }
return false; return false;
@ -135,7 +137,7 @@ namespace SharedDriver
if (result == "CAPTURE_STARTED") if (result == "CAPTURE_STARTED")
{ {
capturing = true; capturing = true;
Task.Run(ReadCapture); Task.Run(() => ReadCapture(PreSamples + PostSamples));
return true; return true;
} }
return false; return false;
@ -178,21 +180,47 @@ namespace SharedDriver
DeviceVersion = null; DeviceVersion = null;
CaptureCompleted = null; CaptureCompleted = null;
} }
void ReadCapture() void ReadCapture(int Samples)
{ {
uint length = readData.ReadUInt32();
uint[] samples = new uint[length]; try
{
for (int buc = 0; buc < length; buc++) byte[] readBuffer = new byte[Samples * 4 + 4];
samples[buc] = readData.ReadUInt32();
if (currentCaptureHandler != null) int left = readBuffer.Length;
currentCaptureHandler(new CaptureEventArgs { Samples = samples, ChannelCount = channelCount, TriggerChannel = triggerChannel, PreSamples = preSamples }); int pos = 0;
else if (CaptureCompleted != null)
CaptureCompleted(this, new CaptureEventArgs { Samples = samples, ChannelCount = channelCount, TriggerChannel = triggerChannel, PreSamples = preSamples });
capturing = false; while (left > 0)
{
pos += sp.Read(readBuffer, pos, left);
left = readBuffer.Length - pos;
}
uint[] samples;
using (MemoryStream ms = new MemoryStream(readBuffer))
{
using (BinaryReader br = new BinaryReader(ms))
{
uint length = br.ReadUInt32();
samples = new uint[length];
for (int buc = 0; buc < length; buc++)
samples[buc] = br.ReadUInt32();
}
}
if (currentCaptureHandler != null)
currentCaptureHandler(new CaptureEventArgs { Samples = samples, ChannelCount = channelCount, TriggerChannel = triggerChannel, PreSamples = preSamples });
else if (CaptureCompleted != null)
CaptureCompleted(this, new CaptureEventArgs { Samples = samples, ChannelCount = channelCount, TriggerChannel = triggerChannel, PreSamples = preSamples });
capturing = false;
}
catch(Exception ex)
{
Console.WriteLine(ex.Message + " - " + ex.StackTrace);
}
} }
class OutputPacket class OutputPacket
{ {