QOL improvements

This commit is contained in:
Agustín Gimenez 2023-06-28 13:32:35 +02:00
parent 3918633682
commit 7f96804d3e
17 changed files with 202 additions and 45 deletions

View File

@ -1,19 +1,9 @@
{ {
"AppName": "Avalonia.Designer.HostApp, Version=0.10.16.0, Culture=neutral, PublicKeyToken=c8d484a7012f9a8b", "AppName": "LogicAnalyzer, Version=3.5.0.1, Culture=neutral, PublicKeyToken=null",
"WindowSettings": { "WindowSettings": {
"LogicAnalyzer.Dialogs.CreateSamplesDialog": {
"Width": 800.0,
"Height": 600.0,
"Position": {
"$type": "Avalonia.PixelPoint, Avalonia.Visuals",
"X": 0,
"Y": 0
},
"WindowState": 0
},
"LogicAnalyzer.MainWindow": { "LogicAnalyzer.MainWindow": {
"Width": 1024.0, "Width": 1837.0,
"Height": 800.0, "Height": 1083.0,
"Position": { "Position": {
"$type": "Avalonia.PixelPoint, Avalonia.Visuals", "$type": "Avalonia.PixelPoint, Avalonia.Visuals",
"X": 0, "X": 0,
@ -22,13 +12,23 @@
"WindowState": 0 "WindowState": 0
}, },
"LogicAnalyzer.Dialogs.SignalComposerDialog": { "LogicAnalyzer.Dialogs.SignalComposerDialog": {
"EditorFontSize": 14.0, "EditorFontSize": 19.0,
"Width": 640.0, "Width": 973.0,
"Height": 480.0, "Height": 415.0,
"Position": { "Position": {
"$type": "Avalonia.PixelPoint, Avalonia.Visuals", "$type": "Avalonia.PixelPoint, Avalonia.Visuals",
"X": 0, "X": 1280,
"Y": 0 "Y": 1016
},
"WindowState": 0
},
"LogicAnalyzer.Dialogs.CreateSamplesDialog": {
"Width": 800.0,
"Height": 600.0,
"Position": {
"$type": "Avalonia.PixelPoint, Avalonia.Visuals",
"X": 1665,
"Y": 1233
}, },
"WindowState": 0 "WindowState": 0
}, },
@ -37,8 +37,8 @@
"Height": 450.0, "Height": 450.0,
"Position": { "Position": {
"$type": "Avalonia.PixelPoint, Avalonia.Visuals", "$type": "Avalonia.PixelPoint, Avalonia.Visuals",
"X": 0, "X": 1629,
"Y": 0 "Y": 793
}, },
"WindowState": 0 "WindowState": 0
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

View File

@ -6,7 +6,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ApplicationIcon>window.ico</ApplicationIcon> <ApplicationIcon>window.ico</ApplicationIcon>
<Version>4.0.0.0</Version> <Version>4.5.1.0</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -16,7 +16,7 @@ namespace CLCapture
public string? AddressPort { get; set; } public string? AddressPort { get; set; }
[Value(1, Required = true, HelpText = "Desired sampling frequency.")] [Value(1, Required = true, HelpText = "Desired sampling frequency.")]
public int SamplingFrequency { get; set; } public int SamplingFrequency { get; set; }
[Value(2, Required = true, HelpText = "List of channels to capture (channels sepparated by comma).")] [Value(2, Required = true, HelpText = "List of channels to capture (channels sepparated by comma, can contain a name adding a semicolon after the channel number, no spaces allowed).")]
public string? Channels { get; set; } public string? Channels { get; set; }
[Value(3, Required = true, HelpText = "Number of samples to capture before the trigger.")] [Value(3, Required = true, HelpText = "Number of samples to capture before the trigger.")]
public int PreSamples { get; set; } public int PreSamples { get; set; }

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CLCapture
{
public class CLChannel
{
public CLChannel(string Definition)
{
if(string.IsNullOrWhiteSpace(Definition))
throw new ArgumentNullException("Missing channel definition.");
var inputParts = Definition.Trim().Split(":");
if (inputParts.Length < 1)
throw new ArgumentException("Invalid channel definition");
if (inputParts.Length == 1)
{
int value;
if (!int.TryParse(inputParts[0], out value))
throw new ArgumentException("Invalid channel definition, channel must be defined in decimal form.");
ChannelNumber = value;
ChannelName = $"Channel {value}";
}
else if (inputParts.Length == 2)
{
int value;
if (!int.TryParse(inputParts[0], out value))
throw new ArgumentException("Invalid channel definition, channel must be defined in decimal form.");
ChannelNumber = value;
ChannelName = inputParts[1];
}
else
{
throw new ArgumentException("Invalid channel definition, too many parts.");
}
}
public int ChannelNumber { get; set; }
public string ChannelName { get; set; }
}
}

View File

@ -39,15 +39,27 @@ async Task<int> Capture(CLCaptureOptions opts)
return -1; return -1;
} }
int[]? channels = opts.Channels?.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(c => int.Parse(c)).ToArray(); CLChannel[]? channels;
if (channels == null || channels.Any(c => c < 1 || c > 24)) try
{
//int[]? channels = opts.Channels?.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(c => int.Parse(c)).ToArray();
channels = opts.Channels?.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(c => new CLChannel(c)).ToArray();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return -1;
}
if (channels == null || channels.Any(c => c.ChannelNumber < 1 || c.ChannelNumber > 24))
{ {
Console.WriteLine("Specified capture channels out of range."); Console.WriteLine("Specified capture channels out of range.");
return -1; return -1;
} }
int maxChannel = channels.Max(); int maxChannel = channels.Max(c => c.ChannelNumber);
int channelMode = maxChannel <= 8 ? 0 : (maxChannel <= 16 ? 1 : 2); int channelMode = maxChannel <= 8 ? 0 : (maxChannel <= 16 ? 1 : 2);
int channelCount = maxChannel <= 8 ? 8 : (maxChannel <= 16 ? 16 : 24); int channelCount = maxChannel <= 8 ? 8 : (maxChannel <= 16 ? 16 : 24);
@ -163,13 +175,13 @@ async Task<int> Capture(CLCaptureOptions opts)
captureCompletedTask = new TaskCompletionSource<CaptureEventArgs>(); captureCompletedTask = new TaskCompletionSource<CaptureEventArgs>();
channels = opts.Channels.Split(",", StringSplitOptions.RemoveEmptyEntries).Select(c => int.Parse(c) - 1).ToArray(); int[] nChannels = channels.Select(c => c.ChannelNumber - 1).ToArray();
if (opts.Trigger.TriggerType == CLTriggerType.Edge) if (opts.Trigger.TriggerType == CLTriggerType.Edge)
{ {
Console.WriteLine("Starting edge triggered capture..."); Console.WriteLine("Starting edge triggered capture...");
var resStart = driver.StartCapture(opts.SamplingFrequency, opts.PreSamples, opts.PostSamples, var resStart = driver.StartCapture(opts.SamplingFrequency, opts.PreSamples, opts.PostSamples,
channels, opts.Trigger.Channel - 1, opts.Trigger.Value == "0", CaptureFinished); nChannels, opts.Trigger.Channel - 1, opts.Trigger.Value == "0", CaptureFinished);
if (resStart != CaptureError.None) if (resStart != CaptureError.None)
{ {
@ -209,7 +221,7 @@ async Task<int> Capture(CLCaptureOptions opts)
} }
var resStart = driver.StartPatternCapture(opts.SamplingFrequency, opts.PreSamples, opts.PostSamples, var resStart = driver.StartPatternCapture(opts.SamplingFrequency, opts.PreSamples, opts.PostSamples,
channels, opts.Trigger.Channel - 1, bitCount, triggerPattern, opts.Trigger.TriggerType == CLTriggerType.Fast, CaptureFinished); nChannels, opts.Trigger.Channel - 1, bitCount, triggerPattern, opts.Trigger.TriggerType == CLTriggerType.Fast, CaptureFinished);
if (resStart != CaptureError.None) if (resStart != CaptureError.None)
{ {
@ -246,7 +258,7 @@ async Task<int> Capture(CLCaptureOptions opts)
var file = File.Create(opts.OutputFile); var file = File.Create(opts.OutputFile);
StreamWriter sw = new StreamWriter(file); StreamWriter sw = new StreamWriter(file);
sw.WriteLine(String.Join(',', channels.Select(c => $"Channel {c + 1}").ToArray())); sw.WriteLine(String.Join(',', channels.Select(c => c.ChannelName).ToArray()));
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@ -254,14 +266,14 @@ async Task<int> Capture(CLCaptureOptions opts)
{ {
sb.Clear(); sb.Clear();
for (int buc = 0; buc < opts.Channels.Length; buc++) for (int buc = 0; buc < nChannels.Length; buc++)
{ {
if ((result.Samples[sample] & ((UInt128)1 << buc)) == 0) if ((result.Samples[sample] & ((UInt128)1 << nChannels[buc])) == 0)
sb.Append("0,"); sb.Append("0,");
else else
sb.Append("1,"); sb.Append("1,");
} }
sb.Remove(sb.Length - 1, 1);
sw.WriteLine(sb.ToString()); sw.WriteLine(sb.ToString());
} }

View File

@ -2,7 +2,7 @@
"profiles": { "profiles": {
"CLCapture": { "CLCapture": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "capture COM3 1000000 1,2,3,4,5,6,7,8 20 20000 TriggerType:Edge,Channel:1,Value:1 ../test.csv" "commandLineArgs": "capture COM9 1000000 1:SDA,2:SDB,3:SDC_A,4:nana,5,6,7,8 20 20000 TriggerType:Edge,Channel:1,Value:0 test.csv"
} }
} }
} }

View File

@ -0,0 +1,16 @@
using LogicAnalyzer.Protocols;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LogicAnalyzer.Classes
{
public class AnalysisSettings
{
public ProtocolAnalyzerBase? Analyzer { get; set; }
public ProtocolAnalyzerSelectedChannel[]? Channels { get; set; }
public ProtocolAnalyzerSettingValue[]? Settings { get; set; }
}
}

View File

@ -142,7 +142,7 @@ namespace LogicAnalyzer.Dialogs
private void LoadSettings(AnalyzerDriverType DriverType) private void LoadSettings(AnalyzerDriverType DriverType)
{ {
settingsFile = $"cpSettings{DriverType}.json"; settingsFile = WindowExtensions.GetFilePath($"cpSettings{DriverType}.json");
if (File.Exists(settingsFile)) if (File.Exists(settingsFile))
{ {
@ -316,7 +316,13 @@ namespace LogicAnalyzer.Dialogs
{ {
trigger = (int)nudTriggerBase.Value - 1; trigger = (int)nudTriggerBase.Value - 1;
char[] patternChars = txtPattern.Text.ToArray(); if (string.IsNullOrWhiteSpace(txtPattern.Text))
{
await this.ShowError("Error", "Trigger pattern must be at least one bit long.");
return;
}
char[] patternChars = txtPattern.Text.Trim().ToArray();
if (patternChars.Length == 0) if (patternChars.Length == 0)
{ {

View File

@ -11,18 +11,32 @@ using System.Threading.Tasks;
using System.IO; using System.IO;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Text.Json; using System.Text.Json;
using static System.Environment;
namespace LogicAnalyzer.Extensions namespace LogicAnalyzer.Extensions
{ {
public static class WindowExtensions public static class WindowExtensions
{ {
static string BasePath { get { return Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) ?? ""; } }
static JsonSerializerSettings jSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Formatting = Formatting.Indented }; static JsonSerializerSettings jSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Formatting = Formatting.Indented };
public static string GetFilePath(string FileName)
{
string appData = Path.Combine(Environment.GetFolderPath(SpecialFolder.ApplicationData, SpecialFolderOption.DoNotVerify), "LogicAnalyzer");
Directory.CreateDirectory(appData);
return Path.Combine(appData, FileName);
}
static AppConfig config; static AppConfig config;
static WindowExtensions() static WindowExtensions()
{ {
if (File.Exists("AppConfig.json"))
config = JsonConvert.DeserializeObject<AppConfig>(File.ReadAllText("AppConfig.json"), jSettings); string path = GetFilePath("AppConfig.json");
if (File.Exists(path))
config = JsonConvert.DeserializeObject<AppConfig>(File.ReadAllText(path), jSettings);
if(config == null) if(config == null)
config = new AppConfig(); config = new AppConfig();
@ -132,8 +146,9 @@ namespace LogicAnalyzer.Extensions
} }
private static void PersistSettings() private static void PersistSettings()
{ {
string path = GetFilePath("AppConfig.json");
File.WriteAllText("AppConfig.json", JsonConvert.SerializeObject(config, Formatting.Indented, jSettings)); File.WriteAllText(path, JsonConvert.SerializeObject(config, Formatting.Indented, jSettings));
} }
private static object GetPropertyValue(object Source, string PropertyName) private static object GetPropertyValue(object Source, string PropertyName)
{ {

View File

@ -8,7 +8,7 @@
<BuiltInComInteropSupport>true</BuiltInComInteropSupport> <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationIcon>Assets\Ico40.ico</ApplicationIcon> <ApplicationIcon>Assets\Ico40.ico</ApplicationIcon>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<Version>4.0.0.0</Version> <Version>4.5.1.0</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove=".gitignore" /> <None Remove=".gitignore" />

View File

@ -1,6 +1,10 @@
<?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\Windows-Arm64.pubxml</_LastSelectedProfileId> <_LastSelectedProfileId>C:\Users\geniw\source\repos\LogicAnalyzer\LogicAnalyzer\Properties\PublishProfiles\Linux.pubxml</_LastSelectedProfileId>
<ActiveDebugProfile>LogicAnalyzer</ActiveDebugProfile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -3,11 +3,11 @@
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:LogicAnalyzer.Controls" xmlns:controls="clr-namespace:LogicAnalyzer.Controls"
mc:Ignorable="d" d:DesignWidth="1024" d:DesignHeight="800" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"
x:Class="LogicAnalyzer.MainWindow" x:Class="LogicAnalyzer.MainWindow"
Title="LogicAnalyzer - Multiplatform version" Title="LogicAnalyzer - Multiplatform version"
Icon="/Assets/Ico40.ico" Icon="/Assets/Ico40.ico"
Background="#383838" MinWidth="1024" MinHeight="800" Width="1024" Height="800" WindowStartupLocation="CenterScreen"> Background="#383838" MinWidth="800" MinHeight="600" Width="800" Height="600" WindowStartupLocation="CenterScreen">
<DockPanel VerticalAlignment="Stretch"> <DockPanel VerticalAlignment="Stretch">
<Menu DockPanel.Dock="Top" Background="#f0202020"> <Menu DockPanel.Dock="Top" Background="#f0202020">
<MenuItem Header="_File"> <MenuItem Header="_File">
@ -47,7 +47,7 @@
<controls:SampleMarker Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Foreground="White" Background="Transparent" Name="sampleMarker"></controls:SampleMarker> <controls:SampleMarker Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Foreground="White" Background="Transparent" Name="sampleMarker"></controls:SampleMarker>
</Grid> </Grid>
<ScrollBar VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Orientation="Horizontal" DockPanel.Dock="Bottom" Name="scrSamplePos"></ScrollBar> <ScrollBar VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Orientation="Horizontal" DockPanel.Dock="Bottom" Name="scrSamplePos" AllowAutoHide="False"></ScrollBar>
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ColumnDefinitions="*,240" DockPanel.Dock="Bottom"> <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ColumnDefinitions="*,240" DockPanel.Dock="Bottom">
<ScrollViewer VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> <ScrollViewer VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid ColumnDefinitions="140,*" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"> <Grid ColumnDefinitions="140,*" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">

View File

@ -35,6 +35,12 @@ namespace LogicAnalyzer
UInt128[]? copiedSamples; UInt128[]? copiedSamples;
MenuItem? mnuRepeatAnalysis;
AnalysisSettings? analysisSettings;
bool preserveSamples = false;
public MainWindow() public MainWindow()
{ {
Instance = this; Instance = this;
@ -558,7 +564,10 @@ namespace LogicAnalyzer
sampleViewer.Samples = e.Samples; sampleViewer.Samples = e.Samples;
sampleViewer.PreSamples = e.PreSamples; sampleViewer.PreSamples = e.PreSamples;
sampleViewer.ChannelCount = e.ChannelCount; sampleViewer.ChannelCount = e.ChannelCount;
if(!preserveSamples)
sampleViewer.SamplesInScreen = Math.Min(100, e.Samples.Length / 10); sampleViewer.SamplesInScreen = Math.Min(100, e.Samples.Length / 10);
sampleViewer.FirstSample = Math.Max(e.PreSamples - 10, 0); sampleViewer.FirstSample = Math.Max(e.PreSamples - 10, 0);
sampleViewer.ClearRegions(); sampleViewer.ClearRegions();
sampleViewer.ClearAnalyzedChannels(); sampleViewer.ClearAnalyzedChannels();
@ -613,14 +622,43 @@ namespace LogicAnalyzer
return itm; return itm;
}).ToArray()); }).ToArray());
mnuRepeatAnalysis = new MenuItem { Header = "Repeat last analysis" };
mnuRepeatAnalysis.IsEnabled = false;
mnuRepeatAnalysis.Click += MnuRepeatAnalysis_Click;
finalItems.Add(mnuRepeatAnalysis);
var clearItem = new MenuItem { Header = "C_lear analysis data" }; var clearItem = new MenuItem { Header = "C_lear analysis data" };
clearItem.Click += ClearItem_Click; clearItem.Click += ClearItem_Click;
finalItems.Add(clearItem); finalItems.Add(clearItem);
mnuProtocols.Items = finalItems; mnuProtocols.Items = finalItems;
} }
} }
private void MnuRepeatAnalysis_Click(object? sender, RoutedEventArgs e)
{
if (analysisSettings == null || analysisSettings.Channels == null || analysisSettings.Analyzer == null || analysisSettings.Settings == null)
return;
var channels = analysisSettings.Channels;
var samples = sampleViewer.Samples;
foreach (var channel in channels)
ExtractSamples(channel, samples);
var analysisResult = analysisSettings.Analyzer.Analyze(settings.Frequency, settings.PreTriggerSamples - 1, analysisSettings.Settings, channels);
if (analysisResult != null)
{
sampleViewer.BeginUpdate();
sampleViewer.AddAnalyzedChannels(analysisResult);
sampleViewer.EndUpdate();
}
}
private void ClearItem_Click(object? sender, RoutedEventArgs e) private void ClearItem_Click(object? sender, RoutedEventArgs e)
{ {
sampleViewer.BeginUpdate(); sampleViewer.BeginUpdate();
@ -662,6 +700,11 @@ namespace LogicAnalyzer
sampleViewer.AddAnalyzedChannels(analysisResult); sampleViewer.AddAnalyzedChannels(analysisResult);
sampleViewer.EndUpdate(); sampleViewer.EndUpdate();
} }
analysisSettings = new AnalysisSettings { Analyzer = analyzer, Channels = channels, Settings = dlg.SelectedSettings };
if(mnuRepeatAnalysis != null)
mnuRepeatAnalysis.IsEnabled = true;
} }
} }
@ -762,7 +805,7 @@ namespace LogicAnalyzer
await this.ShowError("Error", "No capture to repeat"); await this.ShowError("Error", "No capture to repeat");
return; return;
} }
preserveSamples = true;
BeginCapture(); BeginCapture();
} }
@ -775,6 +818,7 @@ namespace LogicAnalyzer
return; return;
settings = dialog.SelectedSettings; settings = dialog.SelectedSettings;
preserveSamples = false;
BeginCapture(); BeginCapture();
} }

View File

@ -0,0 +1,11 @@
{
"profiles": {
"LogicAnalyzer": {
"commandName": "Project"
},
"WSL": {
"commandName": "WSL2",
"distributionName": ""
}
}
}

BIN
Wiki artwork/LogoPage.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

BIN
Wiki artwork/LogoPage.psd Normal file

Binary file not shown.