diff --git a/.gitignore b/.gitignore index d6920f3b9..5ccb5a940 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ -/Avalonia_Apps/AA21_Buttons/AA21_Buttons/*.user .vs +bin +obj +*.bak +*.user diff --git a/CSharpBible/CharGrid/CharGrid.csproj b/CSharpBible/CharGrid/CharGrid.csproj index e4ab464f1..7e8c47fb8 100644 --- a/CSharpBible/CharGrid/CharGrid.csproj +++ b/CSharpBible/CharGrid/CharGrid.csproj @@ -1,92 +1,22 @@ - - - - Debug - AnyCPU - {C9207149-1178-4D9E-818E-36C4DA31047C} - WinExe - CSharpBible.CharGrid - CharGrid - ..\..\bin\$(MSBuildProjectName)\ - ..\..\obj\$(MSBuildProjectName)\ - ..\..\obj\$(MSBuildProjectName)\ - v4.7.2 - 512 - true - true - - - - AnyCPU - true - full - false - ..\..\bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - ..\..\bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - Form - - - FrmCharGridMain.cs - - - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - True - - - FrmCharGridMain.cs - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - + + + + WinExe + AnyCPU + {C9207149-1178-4D9E-818E-36C4DA31047C} + CSharpBible.CharGrid + net8.0-windows + true + true + + + + + + + + + + + \ No newline at end of file diff --git a/CSharpBible/CharGrid/Program.cs b/CSharpBible/CharGrid/Program.cs index 78722a718..ec2f5c970 100644 --- a/CSharpBible/CharGrid/Program.cs +++ b/CSharpBible/CharGrid/Program.cs @@ -1,36 +1,37 @@ // *********************************************************************** -// Assembly : CharGrid -// Author : Mir -// Created : 12-19-2021 +// Assembly : CharGrid +// Author : Mir +// Created :12-19-2021 // -// Last Modified By : Mir -// Last Modified On : 02-29-2020 -// *********************************************************************** -// -// Copyright © JC-Soft 2020 -// -// +// Last Modified By : GitHub Copilot +// Last Modified On :2025-11-05 // *********************************************************************** using System; using System.Windows.Forms; -using CSharpBible.CharGrid.Visual; +using CSharpBible.CharGrid.Views; +using CSharpBible.CharGrid.Services; +using CSharpBible.CharGrid.ViewModels.Interfaces; +using CSharpBible.CharGrid.ViewModels; +using Microsoft.Extensions.DependencyInjection; namespace CSharpBible.CharGrid { - /// - /// Class Program. - /// static class Program { - /// - /// Der Haupteinstiegspunkt für die Anwendung. - /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new FrmCharGridMain()); + + var services = new ServiceCollection() + .AddSingleton() + .AddSingleton() + .AddTransient() + .AddTransient() + .BuildServiceProvider(); + + Application.Run(services.GetRequiredService()); } } } diff --git a/CSharpBible/CharGrid/Properties/AssemblyInfo.cs b/CSharpBible/CharGrid/Properties/AssemblyInfo.cs index 2febf5445..0308a676b 100644 --- a/CSharpBible/CharGrid/Properties/AssemblyInfo.cs +++ b/CSharpBible/CharGrid/Properties/AssemblyInfo.cs @@ -17,12 +17,7 @@ // Allgemeine Informationen über eine Assembly werden über die folgenden // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, // die einer Assembly zugeordnet sind. -[assembly: AssemblyTitle("CharGrid")] [assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("JC-Soft")] -[assembly: AssemblyProduct("CharGrid")] -[assembly: AssemblyCopyright("Copyright © JC-Soft 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -44,5 +39,3 @@ // Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, // indem Sie "*" wie unten gezeigt eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CSharpBible/CharGrid/Properties/Resources.Designer.cs b/CSharpBible/CharGrid/Properties/Resources.Designer.cs index e150e90bb..8a45b64cf 100644 --- a/CSharpBible/CharGrid/Properties/Resources.Designer.cs +++ b/CSharpBible/CharGrid/Properties/Resources.Designer.cs @@ -20,7 +20,7 @@ namespace CSharpBible.CharGrid.Properties { /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. /// // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert - // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // -Klasse über ein Tool wie ResGen oder Views Studio automatisch generiert. // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] diff --git a/CSharpBible/CharGrid/Services/ICharGridProvider.cs b/CSharpBible/CharGrid/Services/ICharGridProvider.cs new file mode 100644 index 000000000..32d5789cd --- /dev/null +++ b/CSharpBible/CharGrid/Services/ICharGridProvider.cs @@ -0,0 +1,9 @@ +namespace CSharpBible.CharGrid.Services; + +public interface ICharGridProvider +{ + int Rows { get; } + int Columns { get; } + char GetChar(int row, int column); + void SetChar(int row, int column, char value); +} diff --git a/CSharpBible/CharGrid/Services/IRandomCharService.cs b/CSharpBible/CharGrid/Services/IRandomCharService.cs new file mode 100644 index 000000000..d65564e38 --- /dev/null +++ b/CSharpBible/CharGrid/Services/IRandomCharService.cs @@ -0,0 +1,9 @@ +using System; + +namespace CSharpBible.CharGrid.Services +{ + public interface IRandomCharService + { + char NextChar(); + } +} diff --git a/CSharpBible/CharGrid/Services/InMemoryCharGridProvider.cs b/CSharpBible/CharGrid/Services/InMemoryCharGridProvider.cs new file mode 100644 index 000000000..56c2b4f46 --- /dev/null +++ b/CSharpBible/CharGrid/Services/InMemoryCharGridProvider.cs @@ -0,0 +1,25 @@ +using System; + +namespace CSharpBible.CharGrid.Services +{ + public class InMemoryCharGridProvider : ICharGridProvider + { + private readonly char[,] _grid; + public int Rows { get; } + public int Columns { get; } + + public InMemoryCharGridProvider(int rows = 16, int columns = 32, IRandomCharService random = null) + { + Rows = rows; + Columns = columns; + _grid = new char[rows, columns]; + var rnd = random ?? new RandomCharService(); + for (int r = 0; r < rows; r++) + for (int c = 0; c < columns; c++) + _grid[r, c] = rnd.NextChar(); + } + + public char GetChar(int row, int column) => _grid[row, column]; + public void SetChar(int row, int column, char value) => _grid[row, column] = value; + } +} diff --git a/CSharpBible/CharGrid/Services/RandomCharService.cs b/CSharpBible/CharGrid/Services/RandomCharService.cs new file mode 100644 index 000000000..f253335ae --- /dev/null +++ b/CSharpBible/CharGrid/Services/RandomCharService.cs @@ -0,0 +1,14 @@ +using System; + +namespace CSharpBible.CharGrid.Services +{ + public class RandomCharService : IRandomCharService + { + private readonly Random _rnd = new Random(); + public char NextChar() + { + // ASCII uppercase letters + return (char)('A' + _rnd.Next(0,26)); + } + } +} diff --git a/CSharpBible/CharGrid/ViewModels/CharGridViewModel.cs b/CSharpBible/CharGrid/ViewModels/CharGridViewModel.cs new file mode 100644 index 000000000..dcd111ae7 --- /dev/null +++ b/CSharpBible/CharGrid/ViewModels/CharGridViewModel.cs @@ -0,0 +1,52 @@ +using System.Collections.ObjectModel; +using CSharpBible.CharGrid.Services; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CSharpBible.CharGrid.ViewModels.Interfaces; +using System.Windows.Forms; + +namespace CSharpBible.CharGrid.ViewModels; + +public partial class CharGridViewModel : ViewModelBase, ICharGridViewModel +{ + private readonly ICharGridProvider _provider; + + [ObservableProperty] + private ObservableCollection> rows = new(); + + public int RowCount => _provider.Rows; + public int ColumnCount => _provider.Columns; + + public CharGridViewModel(ICharGridProvider provider) + { + _provider = provider; + for (int r = 0; r < _provider.Rows; r++) + { + var row = new ObservableCollection(); + for (int c = 0; c < _provider.Columns; c++) + row.Add(_provider.GetChar(r, c)); + rows.Add(row); + } + } + + [RelayCommand] + private void UpdateCell((int r, int c, char value) args) + { + var (r, c, value) = args; + _provider.SetChar(r, c, value); + rows[r][c] = value; + // ObservableProperty will raise PropertyChanged for Rows when set; direct item change triggers collection notifications + } + + [RelayCommand] + private void Exit() + { + Application.Exit(); + } + + [RelayCommand] + private void About() + { + + } +} diff --git a/CSharpBible/CharGrid/ViewModels/Interfaces/ICharGridViewModel.cs b/CSharpBible/CharGrid/ViewModels/Interfaces/ICharGridViewModel.cs new file mode 100644 index 000000000..654bfe1c6 --- /dev/null +++ b/CSharpBible/CharGrid/ViewModels/Interfaces/ICharGridViewModel.cs @@ -0,0 +1,15 @@ +using CommunityToolkit.Mvvm.Input; +using System.Collections.ObjectModel; + +namespace CSharpBible.CharGrid.ViewModels.Interfaces; + +public interface ICharGridViewModel +{ + ObservableCollection> Rows { get; } + + IRelayCommand ExitCommand { get; } + IRelayCommand AboutCommand { get; } + IRelayCommand<(int r, int c, char value)> UpdateCellCommand { get; } + int ColumnCount { get; } + int RowCount { get; } +} \ No newline at end of file diff --git a/CSharpBible/CharGrid/ViewModels/ViewModelBase.cs b/CSharpBible/CharGrid/ViewModels/ViewModelBase.cs new file mode 100644 index 000000000..165973b62 --- /dev/null +++ b/CSharpBible/CharGrid/ViewModels/ViewModelBase.cs @@ -0,0 +1,9 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace CSharpBible.CharGrid.ViewModels +{ + // Replaced manual INotifyPropertyChanged with ObservableObject from CommunityToolkit + public abstract partial class ViewModelBase : ObservableObject + { + } +} diff --git a/CSharpBible/CharGrid/Visual/FrmCharGridMain.Designer.cs b/CSharpBible/CharGrid/Views/FrmCharGridMain.Designer.cs similarity index 97% rename from CSharpBible/CharGrid/Visual/FrmCharGridMain.Designer.cs rename to CSharpBible/CharGrid/Views/FrmCharGridMain.Designer.cs index a52cb383c..bffb0f28a 100644 --- a/CSharpBible/CharGrid/Visual/FrmCharGridMain.Designer.cs +++ b/CSharpBible/CharGrid/Views/FrmCharGridMain.Designer.cs @@ -1,393 +1,399 @@ -// *********************************************************************** -// Assembly : CharGrid -// Author : Mir -// Created : 12-19-2021 -// -// Last Modified By : Mir -// Last Modified On : 12-24-2021 -// *********************************************************************** -// -// Copyright © JC-Soft 2020 -// -// -// *********************************************************************** -namespace CSharpBible.CharGrid.Visual -{ - /// - /// Class FrmCharGridMain. - /// Implements the - /// - /// - partial class FrmCharGridMain - { - /// - /// Erforderliche Designervariable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Verwendete Ressourcen bereinigen. - /// - /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Vom Windows Form-Designer generierter Code - - /// - /// Erforderliche Methode für die Designerunterstützung. - /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. - /// - private void InitializeComponent() - { - this.menuStrip1 = new System.Windows.Forms.MenuStrip(); - this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.pnlTop = new System.Windows.Forms.Panel(); - this.textBox1 = new System.Windows.Forms.TextBox(); - this.label1 = new System.Windows.Forms.Label(); - this.checkBox1 = new System.Windows.Forms.CheckBox(); - this.lblFont = new System.Windows.Forms.Label(); - this.comboBox1 = new System.Windows.Forms.ComboBox(); - this.pnlBottom = new System.Windows.Forms.Panel(); - this.button4 = new System.Windows.Forms.Button(); - this.button3 = new System.Windows.Forms.Button(); - this.button2 = new System.Windows.Forms.Button(); - this.textBox2 = new System.Windows.Forms.TextBox(); - this.button1 = new System.Windows.Forms.Button(); - this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); - this.textBox3 = new System.Windows.Forms.TextBox(); - this.menuStrip1.SuspendLayout(); - this.pnlTop.SuspendLayout(); - this.pnlBottom.SuspendLayout(); - this.tableLayoutPanel1.SuspendLayout(); - this.SuspendLayout(); - // - // menuStrip1 - // - this.menuStrip1.GripMargin = new System.Windows.Forms.Padding(2, 2, 0, 2); - this.menuStrip1.ImageScalingSize = new System.Drawing.Size(24, 24); - this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.fileToolStripMenuItem, - this.helpToolStripMenuItem}); - this.menuStrip1.Location = new System.Drawing.Point(0, 0); - this.menuStrip1.Name = "menuStrip1"; - this.menuStrip1.Size = new System.Drawing.Size(1183, 36); - this.menuStrip1.TabIndex = 0; - this.menuStrip1.Text = "menuStrip1"; - // - // fileToolStripMenuItem - // - this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.exitToolStripMenuItem}); - this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; - this.fileToolStripMenuItem.Size = new System.Drawing.Size(54, 29); - this.fileToolStripMenuItem.Text = "&File"; - // - // exitToolStripMenuItem - // - this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; - this.exitToolStripMenuItem.Size = new System.Drawing.Size(141, 34); - this.exitToolStripMenuItem.Text = "E&xit"; - // - // helpToolStripMenuItem - // - this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.aboutToolStripMenuItem}); - this.helpToolStripMenuItem.Name = "helpToolStripMenuItem"; - this.helpToolStripMenuItem.Size = new System.Drawing.Size(65, 29); - this.helpToolStripMenuItem.Text = "&Help"; - // - // aboutToolStripMenuItem - // - this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; - this.aboutToolStripMenuItem.Size = new System.Drawing.Size(181, 34); - this.aboutToolStripMenuItem.Text = "&About ..."; - // - // pnlTop - // - this.pnlTop.Controls.Add(this.textBox1); - this.pnlTop.Controls.Add(this.label1); - this.pnlTop.Controls.Add(this.checkBox1); - this.pnlTop.Controls.Add(this.lblFont); - this.pnlTop.Controls.Add(this.comboBox1); - this.pnlTop.Dock = System.Windows.Forms.DockStyle.Top; - this.pnlTop.Location = new System.Drawing.Point(0, 36); - this.pnlTop.Name = "pnlTop"; - this.pnlTop.Size = new System.Drawing.Size(1183, 45); - this.pnlTop.TabIndex = 1; - // - // textBox1 - // - this.textBox1.Location = new System.Drawing.Point(480, 8); - this.textBox1.Name = "textBox1"; - this.textBox1.Size = new System.Drawing.Size(285, 26); - this.textBox1.TabIndex = 4; - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(410, 10); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(51, 20); - this.label1.TabIndex = 3; - this.label1.Text = "label1"; - // - // checkBox1 - // - this.checkBox1.AutoSize = true; - this.checkBox1.Location = new System.Drawing.Point(265, 10); - this.checkBox1.Name = "checkBox1"; - this.checkBox1.Size = new System.Drawing.Size(113, 24); - this.checkBox1.TabIndex = 2; - this.checkBox1.Text = "checkBox1"; - this.checkBox1.UseVisualStyleBackColor = true; - // - // lblFont - // - this.lblFont.AutoSize = true; - this.lblFont.Location = new System.Drawing.Point(10, 10); - this.lblFont.Name = "lblFont"; - this.lblFont.Size = new System.Drawing.Size(46, 20); - this.lblFont.TabIndex = 1; - this.lblFont.Text = "Font:"; - // - // comboBox1 - // - this.comboBox1.FormattingEnabled = true; - this.comboBox1.Location = new System.Drawing.Point(65, 8); - this.comboBox1.Name = "comboBox1"; - this.comboBox1.Size = new System.Drawing.Size(183, 28); - this.comboBox1.TabIndex = 0; - // - // pnlBottom - // - this.pnlBottom.Controls.Add(this.button4); - this.pnlBottom.Controls.Add(this.button3); - this.pnlBottom.Controls.Add(this.button2); - this.pnlBottom.Controls.Add(this.textBox2); - this.pnlBottom.Controls.Add(this.button1); - this.pnlBottom.Dock = System.Windows.Forms.DockStyle.Bottom; - this.pnlBottom.Location = new System.Drawing.Point(0, 553); - this.pnlBottom.Name = "pnlBottom"; - this.pnlBottom.Size = new System.Drawing.Size(1183, 79); - this.pnlBottom.TabIndex = 2; - // - // button4 - // - this.button4.Location = new System.Drawing.Point(589, 10); - this.button4.Name = "button4"; - this.button4.Size = new System.Drawing.Size(110, 60); - this.button4.TabIndex = 4; - this.button4.Text = "button4"; - this.button4.UseVisualStyleBackColor = true; - // - // button3 - // - this.button3.Location = new System.Drawing.Point(398, 10); - this.button3.Name = "button3"; - this.button3.Size = new System.Drawing.Size(110, 60); - this.button3.TabIndex = 3; - this.button3.Text = "button3"; - this.button3.UseVisualStyleBackColor = true; - // - // button2 - // - this.button2.Location = new System.Drawing.Point(263, 10); - this.button2.Name = "button2"; - this.button2.Size = new System.Drawing.Size(110, 60); - this.button2.TabIndex = 2; - this.button2.Text = "button2"; - this.button2.UseVisualStyleBackColor = true; - // - // textBox2 - // - this.textBox2.Font = new System.Drawing.Font("Microsoft Sans Serif", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.textBox2.Location = new System.Drawing.Point(10, 10); - this.textBox2.Name = "textBox2"; - this.textBox2.Size = new System.Drawing.Size(80, 53); - this.textBox2.TabIndex = 1; - // - // button1 - // - this.button1.Location = new System.Drawing.Point(110, 10); - this.button1.Name = "button1"; - this.button1.Size = new System.Drawing.Size(110, 60); - this.button1.TabIndex = 0; - this.button1.Text = "button1"; - this.button1.UseVisualStyleBackColor = true; - // - // tableLayoutPanel1 - // - this.tableLayoutPanel1.ColumnCount = 32; - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 35F)); - this.tableLayoutPanel1.Controls.Add(this.textBox3, 0, 0); - this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; - this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 81); - this.tableLayoutPanel1.Name = "tableLayoutPanel1"; - this.tableLayoutPanel1.RowCount = 8; - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); - this.tableLayoutPanel1.Size = new System.Drawing.Size(1183, 472); - this.tableLayoutPanel1.TabIndex = 3; - // - // textBox3 - // - this.textBox3.Cursor = System.Windows.Forms.Cursors.Hand; - this.textBox3.Location = new System.Drawing.Point(3, 3); - this.textBox3.Name = "textBox3"; - this.textBox3.ReadOnly = true; - this.textBox3.Size = new System.Drawing.Size(47, 26); - this.textBox3.TabIndex = 0; - // - // FrmCharGridMain - // - this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(1183, 632); - this.Controls.Add(this.tableLayoutPanel1); - this.Controls.Add(this.pnlBottom); - this.Controls.Add(this.pnlTop); - this.Controls.Add(this.menuStrip1); - this.MainMenuStrip = this.menuStrip1; - this.Name = "FrmCharGridMain"; - this.Text = "CharGrid"; - this.menuStrip1.ResumeLayout(false); - this.menuStrip1.PerformLayout(); - this.pnlTop.ResumeLayout(false); - this.pnlTop.PerformLayout(); - this.pnlBottom.ResumeLayout(false); - this.pnlBottom.PerformLayout(); - this.tableLayoutPanel1.ResumeLayout(false); - this.tableLayoutPanel1.PerformLayout(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - /// - /// The menu strip1 - /// - private System.Windows.Forms.MenuStrip menuStrip1; - /// - /// The file tool strip menu item - /// - private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; - /// - /// The exit tool strip menu item - /// - private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; - /// - /// The help tool strip menu item - /// - private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem; - /// - /// The about tool strip menu item - /// - private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem; - /// - /// The PNL top - /// - private System.Windows.Forms.Panel pnlTop; - /// - /// The text box1 - /// - private System.Windows.Forms.TextBox textBox1; - /// - /// The label1 - /// - private System.Windows.Forms.Label label1; - /// - /// The check box1 - /// - private System.Windows.Forms.CheckBox checkBox1; - /// - /// The label font - /// - private System.Windows.Forms.Label lblFont; - /// - /// The combo box1 - /// - private System.Windows.Forms.ComboBox comboBox1; - /// - /// The PNL bottom - /// - private System.Windows.Forms.Panel pnlBottom; - /// - /// The button4 - /// - private System.Windows.Forms.Button button4; - /// - /// The button3 - /// - private System.Windows.Forms.Button button3; - /// - /// The button2 - /// - private System.Windows.Forms.Button button2; - /// - /// The text box2 - /// - private System.Windows.Forms.TextBox textBox2; - /// - /// The button1 - /// - private System.Windows.Forms.Button button1; - /// - /// The table layout panel1 - /// - private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; - /// - /// The text box3 - /// - private System.Windows.Forms.TextBox textBox3; - } -} - +// *********************************************************************** +// Assembly : CharGrid +// Author : Mir +// Created : 12-19-2021 +// +// Last Modified By : Mir +// Last Modified On : 12-24-2021 +// *********************************************************************** +// +// Copyright © JC-Soft 2020 +// +// +// *********************************************************************** +using CSharpBible.CharGrid.ViewModels.Interfaces; +using Views; + +namespace CSharpBible.CharGrid.Views +{ + /// + /// Class FrmCharGridMain. + /// Implements the + /// + /// + partial class FrmCharGridMain + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.menuStrip1 = new System.Windows.Forms.MenuStrip(); + this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.pnlTop = new System.Windows.Forms.Panel(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.lblFont = new System.Windows.Forms.Label(); + this.comboBox1 = new System.Windows.Forms.ComboBox(); + this.pnlBottom = new System.Windows.Forms.Panel(); + this.button4 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.textBox3 = new System.Windows.Forms.TextBox(); + this.menuStrip1.SuspendLayout(); + this.pnlTop.SuspendLayout(); + this.pnlBottom.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // menuStrip1 + // + this.menuStrip1.GripMargin = new System.Windows.Forms.Padding(2, 2, 0, 2); + this.menuStrip1.ImageScalingSize = new System.Drawing.Size(24, 24); + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.fileToolStripMenuItem, + this.helpToolStripMenuItem}); + this.menuStrip1.Location = new System.Drawing.Point(0, 0); + this.menuStrip1.Name = "menuStrip1"; + this.menuStrip1.Size = new System.Drawing.Size(1183, 36); + this.menuStrip1.TabIndex = 0; + this.menuStrip1.Text = "menuStrip1"; + // + // fileToolStripMenuItem + // + this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.exitToolStripMenuItem}); + this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; + this.fileToolStripMenuItem.Size = new System.Drawing.Size(54, 29); + this.fileToolStripMenuItem.Text = "&File"; + // + // exitToolStripMenuItem + // + this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; + this.exitToolStripMenuItem.Size = new System.Drawing.Size(141, 34); + this.exitToolStripMenuItem.Text = "E&xit"; + // + // helpToolStripMenuItem + // + this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.aboutToolStripMenuItem}); + this.helpToolStripMenuItem.Name = "helpToolStripMenuItem"; + this.helpToolStripMenuItem.Size = new System.Drawing.Size(65, 29); + this.helpToolStripMenuItem.Text = "&Help"; + // + // aboutToolStripMenuItem + // + this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; + this.aboutToolStripMenuItem.Size = new System.Drawing.Size(181, 34); + this.aboutToolStripMenuItem.Text = "&About ..."; + // + // pnlTop + // + this.pnlTop.Controls.Add(this.textBox1); + this.pnlTop.Controls.Add(this.label1); + this.pnlTop.Controls.Add(this.checkBox1); + this.pnlTop.Controls.Add(this.lblFont); + this.pnlTop.Controls.Add(this.comboBox1); + this.pnlTop.Dock = System.Windows.Forms.DockStyle.Top; + this.pnlTop.Location = new System.Drawing.Point(0, 36); + this.pnlTop.Name = "pnlTop"; + this.pnlTop.Size = new System.Drawing.Size(1183, 45); + this.pnlTop.TabIndex = 1; + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(480, 8); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(285, 26); + this.textBox1.TabIndex = 4; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(410, 10); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(51, 20); + this.label1.TabIndex = 3; + this.label1.Text = "label1"; + // + // checkBox1 + // + this.checkBox1.AutoSize = true; + this.checkBox1.Location = new System.Drawing.Point(265, 10); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(113, 24); + this.checkBox1.TabIndex = 2; + this.checkBox1.Text = "checkBox1"; + this.checkBox1.UseVisualStyleBackColor = true; + // + // lblFont + // + this.lblFont.AutoSize = true; + this.lblFont.Location = new System.Drawing.Point(10, 10); + this.lblFont.Name = "lblFont"; + this.lblFont.Size = new System.Drawing.Size(46, 20); + this.lblFont.TabIndex = 1; + this.lblFont.Text = "Font:"; + // + // comboBox1 + // + this.comboBox1.FormattingEnabled = true; + this.comboBox1.Location = new System.Drawing.Point(65, 8); + this.comboBox1.Name = "comboBox1"; + this.comboBox1.Size = new System.Drawing.Size(183, 28); + this.comboBox1.TabIndex = 0; + // + // pnlBottom + // + this.pnlBottom.Controls.Add(this.button4); + this.pnlBottom.Controls.Add(this.button3); + this.pnlBottom.Controls.Add(this.button2); + this.pnlBottom.Controls.Add(this.textBox2); + this.pnlBottom.Controls.Add(this.button1); + this.pnlBottom.Dock = System.Windows.Forms.DockStyle.Bottom; + this.pnlBottom.Location = new System.Drawing.Point(0, 553); + this.pnlBottom.Name = "pnlBottom"; + this.pnlBottom.Size = new System.Drawing.Size(1183, 79); + this.pnlBottom.TabIndex = 2; + // + // button4 + // + this.button4.Location = new System.Drawing.Point(589, 10); + this.button4.Name = "button4"; + this.button4.Size = new System.Drawing.Size(110, 60); + this.button4.TabIndex = 4; + this.button4.Text = "button4"; + this.button4.UseVisualStyleBackColor = true; + // + // button3 + // + this.button3.Location = new System.Drawing.Point(398, 10); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(110, 60); + this.button3.TabIndex = 3; + this.button3.Text = "button3"; + this.button3.UseVisualStyleBackColor = true; + // + // button2 + // + this.button2.Location = new System.Drawing.Point(263, 10); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(110, 60); + this.button2.TabIndex = 2; + this.button2.Text = "button2"; + this.button2.UseVisualStyleBackColor = true; + // + // textBox2 + // + this.textBox2.Font = new System.Drawing.Font("Microsoft Sans Serif", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.textBox2.Location = new System.Drawing.Point(10, 10); + this.textBox2.Name = "textBox2"; + this.textBox2.Size = new System.Drawing.Size(80, 53); + this.textBox2.TabIndex = 1; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(110, 10); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(110, 60); + this.button1.TabIndex = 0; + this.button1.Text = "button1"; + this.button1.UseVisualStyleBackColor = true; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 32; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 3.125F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 35F)); + this.tableLayoutPanel1.Controls.Add(this.textBox3, 0, 0); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 81); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 8; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.5F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(1183, 472); + this.tableLayoutPanel1.TabIndex = 3; + // + // textBox3 + // + this.textBox3.Cursor = System.Windows.Forms.Cursors.Hand; + this.textBox3.Location = new System.Drawing.Point(3, 3); + this.textBox3.Name = "textBox3"; + this.textBox3.ReadOnly = true; + this.textBox3.Size = new System.Drawing.Size(47, 26); + this.textBox3.TabIndex = 0; + // + // FrmCharGridMain + // + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1183, 632); + this.Controls.Add(this.tableLayoutPanel1); + this.Controls.Add(this.pnlBottom); + this.Controls.Add(this.pnlTop); + this.Controls.Add(this.menuStrip1); + this.MainMenuStrip = this.menuStrip1; + this.Name = "FrmCharGridMain"; + this.Text = "CharGrid"; + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.pnlTop.ResumeLayout(false); + this.pnlTop.PerformLayout(); + this.pnlBottom.ResumeLayout(false); + this.pnlBottom.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + /// + /// The menu strip1 + /// + private System.Windows.Forms.MenuStrip menuStrip1; + /// + /// The file tool strip menu item + /// + private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; + /// + /// The exit tool strip menu item + /// + [CommandBinding(nameof(ICharGridViewModel.ExitCommand))] + private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; + /// + /// The help tool strip menu item + /// + private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem; + /// + /// The about tool strip menu item + /// + [CommandBinding(nameof(ICharGridViewModel.AboutCommand))] + private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem; + /// + /// The PNL top + /// + private System.Windows.Forms.Panel pnlTop; + /// + /// The text box1 + /// + private System.Windows.Forms.TextBox textBox1; + /// + /// The label1 + /// + private System.Windows.Forms.Label label1; + /// + /// The check box1 + /// + private System.Windows.Forms.CheckBox checkBox1; + /// + /// The label font + /// + private System.Windows.Forms.Label lblFont; + /// + /// The combo box1 + /// + private System.Windows.Forms.ComboBox comboBox1; + /// + /// The PNL bottom + /// + private System.Windows.Forms.Panel pnlBottom; + /// + /// The button4 + /// + private System.Windows.Forms.Button button4; + /// + /// The button3 + /// + private System.Windows.Forms.Button button3; + /// + /// The button2 + /// + private System.Windows.Forms.Button button2; + /// + /// The text box2 + /// + private System.Windows.Forms.TextBox textBox2; + /// + /// The button1 + /// + [CommandBinding(nameof(ICharGridViewModel.ExitCommand))] + private System.Windows.Forms.Button button1; + /// + /// The table layout panel1 + /// + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + /// + /// The text box3 + /// + private System.Windows.Forms.TextBox textBox3; + } +} + diff --git a/CSharpBible/CharGrid/Views/FrmCharGridMain.cs b/CSharpBible/CharGrid/Views/FrmCharGridMain.cs new file mode 100644 index 000000000..dadd26391 --- /dev/null +++ b/CSharpBible/CharGrid/Views/FrmCharGridMain.cs @@ -0,0 +1,55 @@ +// *********************************************************************** +// Assembly : CharGrid +// Author : Mir +// Created :12-19-2021 +// +// Last Modified By : GitHub Copilot +// Last Modified On :11-05-2025 +// *********************************************************************** +using CSharpBible.CharGrid.ViewModels.Interfaces; +using System; +using System.Windows.Forms; +using Views; + +namespace CSharpBible.CharGrid.Views +{ + /// + /// Class FrmCharGridMain. + /// Implements the + /// + /// + public partial class FrmCharGridMain : Form + { + private readonly ICharGridViewModel _vm; + + // Inject ViewModel + public FrmCharGridMain(ICharGridViewModel vm) + { + _vm = vm; + InitializeComponent(); + Load += FrmCharGridMain_Load; + CommandBindingAttribute.Commit(this, vm); + } + + private void FrmCharGridMain_Load(object sender, EventArgs e) + { + var dgv = Controls["dgvChars"] as DataGridView; // expects a DataGridView named dgvChars + if (dgv == null) return; + + dgv.ReadOnly = true; + dgv.AllowUserToAddRows = false; + dgv.AllowUserToDeleteRows = false; + + dgv.ColumnCount = _vm.ColumnCount; + dgv.RowCount = _vm.RowCount; + + for (int r = 0; r < _vm.RowCount; r++) + { + for (int c = 0; c < _vm.ColumnCount; c++) + { + dgv[c, r].Value = _vm.Rows[r][c]; + } + } + } + } +} diff --git a/CSharpBible/CharGrid/Visual/FrmCharGridMain.resx b/CSharpBible/CharGrid/Views/FrmCharGridMain.resx similarity index 97% rename from CSharpBible/CharGrid/Visual/FrmCharGridMain.resx rename to CSharpBible/CharGrid/Views/FrmCharGridMain.resx index 0f6d8eb42..d5494e305 100644 --- a/CSharpBible/CharGrid/Visual/FrmCharGridMain.resx +++ b/CSharpBible/CharGrid/Views/FrmCharGridMain.resx @@ -1,123 +1,123 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 17 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + \ No newline at end of file diff --git a/CSharpBible/CharGrid/Visual/FrmCharGridMain.cs b/CSharpBible/CharGrid/Visual/FrmCharGridMain.cs deleted file mode 100644 index d6e14c74d..000000000 --- a/CSharpBible/CharGrid/Visual/FrmCharGridMain.cs +++ /dev/null @@ -1,33 +0,0 @@ -// *********************************************************************** -// Assembly : CharGrid -// Author : Mir -// Created : 12-19-2021 -// -// Last Modified By : Mir -// Last Modified On : 12-24-2021 -// *********************************************************************** -// -// Copyright © JC-Soft 2020 -// -// -// *********************************************************************** -using System.Windows.Forms; - -namespace CSharpBible.CharGrid.Visual -{ - /// - /// Class FrmCharGridMain. - /// Implements the - /// - /// - public partial class FrmCharGridMain : Form - { - /// - /// Initializes a new instance of the class. - /// - public FrmCharGridMain() - { - InitializeComponent(); - } - } -} diff --git a/CSharpBible/DB/DBTest1/DBTest1.csproj b/CSharpBible/DB/DBTest1/DBTest1.csproj index f9912103f..94eed5f05 100644 --- a/CSharpBible/DB/DBTest1/DBTest1.csproj +++ b/CSharpBible/DB/DBTest1/DBTest1.csproj @@ -12,7 +12,7 @@ --> - + diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/DataAnalysis.Core.csproj b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/DataAnalysis.Core.csproj index 997818578..8307629de 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/DataAnalysis.Core.csproj +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/DataAnalysis.Core.csproj @@ -2,7 +2,7 @@ Library - net9.0 + net9.0;net8.0 enable enable diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/ISyslogAnalysisExporter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/Interfaces/ISyslogAnalysisExporter.cs similarity index 91% rename from CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/ISyslogAnalysisExporter.cs rename to CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/Interfaces/ISyslogAnalysisExporter.cs index 3c777e497..aea3e1fac 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/ISyslogAnalysisExporter.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/Interfaces/ISyslogAnalysisExporter.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using DataAnalysis.Core.Models; -namespace DataAnalysis.Core.Export; +namespace DataAnalysis.Core.Export.Interfaces; /// /// Abstraktion für den Export von Analyseergebnissen in beliebige Zielformate. diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/Interfaces/ITableExporter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/Interfaces/ITableExporter.cs new file mode 100644 index 000000000..12f30de78 --- /dev/null +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/Interfaces/ITableExporter.cs @@ -0,0 +1,10 @@ +using System.Data; +using System.Threading; +using System.Threading.Tasks; + +namespace DataAnalysis.Core.Export.Interfaces; + +public interface ITableExporter +{ + Task ExportAsync(DataTable table, string inputPath, string? outputPath, CancellationToken cancellationToken = default); +} diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/SyslogExcelExporter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/SyslogExcelExporter.cs index 93d732e85..e9ee19979 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/SyslogExcelExporter.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/SyslogExcelExporter.cs @@ -1,9 +1,11 @@ using System; using System.IO; using System.Linq; +using System.Numerics; using System.Threading; using System.Threading.Tasks; using ClosedXML.Excel; +using DataAnalysis.Core.Export.Interfaces; using DataAnalysis.Core.Models; namespace DataAnalysis.Core.Export; @@ -13,97 +15,132 @@ namespace DataAnalysis.Core.Export; /// public sealed class SyslogExcelExporter : ISyslogAnalysisExporter { - public Task ExportAsync(AnalysisResult result, string inputPath, string? outputPath, CancellationToken cancellationToken) - { - outputPath ??= BuildDefaultOutputPath(inputPath); - Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!); + public Task ExportAsync(AnalysisResult result, string inputPath, string? outputPath, CancellationToken cancellationToken) + { + outputPath ??= BuildDefaultOutputPath(inputPath); + Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!); - using var wb = new XLWorkbook(); + using var wb = new XLWorkbook(); - var wsInfo = wb.AddWorksheet("Übersicht"); - wsInfo.Cell(1, 1).Value = "Datei"; - wsInfo.Cell(1, 2).Value = Path.GetFileName(inputPath); - wsInfo.Cell(2, 1).Value = "Zeilen gesamt"; - wsInfo.Cell(2, 2).Value = result.TotalLines; - wsInfo.Cell(3, 1).Value = "Zeilen geparst"; - wsInfo.Cell(3, 2).Value = result.ParsedLines; - wsInfo.Cell(4, 1).Value = "Zeilen übersprungen"; - wsInfo.Cell(4, 2).Value = result.SkippedLines; - wsInfo.Cell(5, 1).Value = "Erster Zeitstempel"; - wsInfo.Cell(5, 2).Value = result.FirstTimestamp?.LocalDateTime; - wsInfo.Cell(6, 1).Value = "Letzter Zeitstempel"; - wsInfo.Cell(6, 2).Value = result.LastTimestamp?.LocalDateTime; - wsInfo.Columns().AdjustToContents(); + var wsInfo = wb.AddWorksheet("Übersicht"); + wsInfo.Cell(1,1).Value = "Datei"; + wsInfo.Cell(1,2).Value = Path.GetFileName(inputPath); + wsInfo.Cell(2,1).Value = "Zeilen gesamt"; + wsInfo.Cell(2,2).Value = result.TotalLines; + wsInfo.Cell(3,1).Value = "Zeilen geparst"; + wsInfo.Cell(3,2).Value = result.ParsedLines; + wsInfo.Cell(4,1).Value = "Zeilen übersprungen"; + wsInfo.Cell(4,2).Value = result.SkippedLines; + wsInfo.Cell(5,1).Value = "Erster Zeitstempel"; + wsInfo.Cell(5,2).Value = result.FirstTimestamp?.LocalDateTime; + wsInfo.Cell(6,1).Value = "Letzter Zeitstempel"; + wsInfo.Cell(6,2).Value = result.LastTimestamp?.LocalDateTime; + if (!string.IsNullOrWhiteSpace(result.GlobalFilterText)) + { + wsInfo.Cell(7,1).Value = "Globaler Filter"; + wsInfo.Cell(7,2).Value = result.GlobalFilterText; + wsInfo.Cell(7,2).Style.Font.Italic = true; + } + wsInfo.Columns().AdjustToContents(); - // Aggregationen exportieren - foreach (var agg in result.Aggregations) - { - var ws = wb.AddWorksheet(TrimSheetName(agg.Title)); - if (agg.Matrix is not null) - { - //2D Matrix: write headers from agg.Columns exactly as provided to preserve mapping - ws.Cell(1, 1).Value = "Schlüssel"; - var columns = agg.Columns ?? Array.Empty(); - for (int i = 0; i < columns.Count; i++) - ws.Cell(1, 2 + i).Value = columns[i]; + // Aggregationen exportieren + foreach (var agg in result.Aggregations) + { + var ws = wb.AddWorksheet(TrimSheetName(agg.Title)); + var hasLocalFilter = !string.IsNullOrWhiteSpace(agg.FilterText); + int headerRow = hasLocalFilter ?2 :1; - // Rotate column header text90° and center - if (columns.Count > 0) - { - var headerRange = ws.Range(1, 2, 1, 1 + columns.Count); - headerRange.Style.Alignment.TextRotation = 90; - headerRange.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; - headerRange.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center; - headerRange.Style.Font.Bold = true; - } + // Falls lokaler Filter vorhanden, Kommentarzeile oben einfügen + if (hasLocalFilter) + { + ws.Cell(1,1).Value = $"# Filter: {agg.FilterText}"; + ws.Cell(1,1).Style.Font.Italic = true; + ws.Cell(1,1).Style.Font.FontColor = XLColor.Gray; + } - int r = 2; - var keys = agg.Matrix.Keys.ToList(); - // Keep Summe as last row if present - keys.Sort((a, b) => string.Equals(a, "Summe", StringComparison.OrdinalIgnoreCase) ? 1 : string.Equals(b, "Summe", StringComparison.OrdinalIgnoreCase) ? -1 : string.Compare(a, b, StringComparison.OrdinalIgnoreCase)); - foreach (var rowKey in keys) - { - var row = agg.Matrix[rowKey]; - ws.Cell(r, 1).Value = rowKey; - for (int i = 0; i < columns.Count; i++) - { - var colKey = columns[i]; - ws.Cell(r, 2 + i).Value = row.TryGetValue(colKey, out var v) ? v : 0; - } - r++; - } - } - else if (agg.Series is not null) - { - //1D - ws.Cell(1, 1).Value = "Schlüssel"; - ws.Cell(1, 2).Value = "Anzahl"; - // Bold headers - ws.Range(1, 1, 1, 2).Style.Font.Bold = true; - int r = 2; - foreach (var kv in agg.Series.OrderByDescending(kv => kv.Value).ThenBy(kv => kv.Key)) - { ws.Cell(r, 1).Value = kv.Key; ws.Cell(r, 2).Value = kv.Value; r++; } - } - ws.Columns().AdjustToContents(); - } + if (agg.Matrix is not null) + { + //2D Matrix: write headers from agg.Columns exactly as provided to preserve mapping + ws.Cell(headerRow,1).Value = "Schlüssel"; + var cols = agg.Columns ?? Array.Empty(); + for (int i =0; i < cols.Count; i++) + ws.Cell(headerRow,2 + i).Value = cols[i]; - wb.SaveAs(outputPath); - return Task.FromResult(outputPath); - } + // Rotate column header text90° and center + if (cols.Count >0) + { + var headerRange = ws.Range(headerRow,2, headerRow,1 + cols.Count); + headerRange.Style.Alignment.TextRotation =90; + headerRange.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; + headerRange.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center; + headerRange.Style.Font.Bold = true; + } - private static string BuildDefaultOutputPath(string inputPath) - { - var dir = Path.GetDirectoryName(inputPath)!; - var name = Path.GetFileNameWithoutExtension(inputPath); - if (!name.EndsWith("_Auswertung", StringComparison.OrdinalIgnoreCase)) - name += "_Auswertung"; - return Path.Combine(dir, name + ".xlsx"); - } + int r = headerRow +1; + var keys = agg.Matrix.Keys.ToList(); + // Keep Summe as last row if present + keys.Sort((a, b) => string.Equals(a, "Summe", StringComparison.OrdinalIgnoreCase) ?1 : string.Equals(b, "Summe", StringComparison.OrdinalIgnoreCase) ? -1 : string.Compare(a, b, StringComparison.OrdinalIgnoreCase)); + foreach (var rowKey in keys) + { + var row = agg.Matrix[rowKey]; + ws.Cell(r,1).Value = rowKey; + for (int i =0; i < cols.Count; i++) + { + var colKey = cols[i]; + ws.Cell(r,2 + i).Value = row.TryGetValue(colKey, out var v) ? v :0; + } + r++; + } + } + else if (agg.Series is not null && agg.Dimensions.Count ==2) + { + //2D Punkte (Cluster) + ws.Cell(headerRow,1).Value = "X"; + ws.Cell(headerRow,2).Value = "Y"; + ws.Cell(headerRow,3).Value = "Anzahl"; + // Bold headers + ws.Range(headerRow,1, headerRow,3).Style.Font.Bold = true; + int r = headerRow +1; + foreach (var kv in agg.Series.OrderByDescending(kv => kv.Value)) + if (kv.Key is Vector2 p) + { + ws.Cell(r,1).Value = p.X; + ws.Cell(r,2).Value = p.Y; + ws.Cell(r,3).Value = kv.Value; + r++; + } + } + else if (agg.Series is not null) + { + //1D + ws.Cell(headerRow,1).Value = "Schlüssel"; + ws.Cell(headerRow,2).Value = "Anzahl"; + // Bold headers + ws.Range(headerRow,1, headerRow,2).Style.Font.Bold = true; + int r = headerRow +1; + foreach (var kv in agg.Series.OrderByDescending(kv => kv.Value).ThenBy(kv => kv.Key)) + { ws.Cell(r,1).Value = kv.Key.ToString(); ws.Cell(r,2).Value = kv.Value; r++; } + } + ws.Columns().AdjustToContents(); + } - private static string TrimSheetName(string name) - { - var invalid = Path.GetInvalidFileNameChars(); - var n = new string(name.Select(c => invalid.Contains(c) ? '_' : c).ToArray()); - return n.Length > 31 ? n[..31] : n; - } + wb.SaveAs(outputPath); + return Task.FromResult(outputPath); + } + + private static string BuildDefaultOutputPath(string inputPath) + { + var dir = Path.GetDirectoryName(inputPath)!; + var name = Path.GetFileNameWithoutExtension(inputPath); + if (!name.EndsWith("_Auswertung", StringComparison.OrdinalIgnoreCase)) + name += "_Auswertung"; + return Path.Combine(dir, name + ".xlsx"); + } + + private static string TrimSheetName(string name) + { + var invalid = Path.GetInvalidFileNameChars(); + var n = new string(name.Select(c => invalid.Contains(c) ? '_' : c).ToArray()); + return n.Length >31 ? n[..31] : n; + } } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/TableExcelExporter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/TableExcelExporter.cs new file mode 100644 index 000000000..54f405517 --- /dev/null +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Export/TableExcelExporter.cs @@ -0,0 +1,63 @@ +using System; +using System.Data; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using ClosedXML.Excel; +using DataAnalysis.Core.Export.Interfaces; + +namespace DataAnalysis.Core.Export; + +public sealed class TableExcelExporter : ITableExporter +{ + public Task ExportAsync(DataTable table, string inputPath, string? outputPath, CancellationToken cancellationToken = default) + { + outputPath ??= BuildDefaultOutputPath(inputPath); + Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!); + + using var wb = new XLWorkbook(); + var ws = wb.AddWorksheet(string.IsNullOrWhiteSpace(table.TableName) ? "Tabelle" : TrimSheetName(table.TableName)); + + // Header + for (int c = 0; c < table.Columns.Count; c++) + ws.Cell(1, c + 1).Value = table.Columns[c].ColumnName; + ws.Range(1, 1, 1, Math.Max(1, table.Columns.Count)).Style.Font.Bold = true; + + // Rows + int r = 2; + foreach (DataRow row in table.Rows) + { + cancellationToken.ThrowIfCancellationRequested(); + for (int c = 0; c < table.Columns.Count; c++) + if (table.Columns[c].DataType == typeof(DateTime) && row[c] is DateTime dt) + { + ws.Cell(r, c + 1).Value = dt; + ws.Cell(r, c + 1).Style.DateFormat.Format = "yyyy-mm-dd hh:mm:ss.ms"; + } + else + ws.Cell(r, c + 1).Value = row[c]?.ToString() ?? string.Empty; + r++; + } + + ws.Columns().AdjustToContents(); + wb.SaveAs(outputPath); + return Task.FromResult(outputPath); + } + + private static string BuildDefaultOutputPath(string inputPath) + { + var dir = Path.GetDirectoryName(inputPath)!; + var name = Path.GetFileNameWithoutExtension(inputPath); + if (!name.EndsWith("_Export", StringComparison.OrdinalIgnoreCase)) + name += "_Export"; + return Path.Combine(dir, name + ".xlsx"); + } + + private static string TrimSheetName(string name) + { + var invalid = Path.GetInvalidFileNameChars(); + var n = new string(name.Select(c => invalid.Contains(c) ? '_' : c).ToArray()); + return n.Length > 31 ? n[..31] : n; + } +} diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableParsingProfile.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableParsingProfile.cs index 047961151..ba1847dde 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableParsingProfile.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableParsingProfile.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using DataAnalysis.Core.Import.Interfaces; using DataAnalysis.Core.Models; namespace DataAnalysis.Core.Import; diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableReader.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableReader.cs index 6ca8d29dc..fa3b3d76c 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableReader.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/DelimitedTableReader.cs @@ -1,3 +1,7 @@ +using DataAnalysis.Core.Import.Interfaces; +using DataAnalysis.Core.Models; +using DocumentFormat.OpenXml.Drawing; +using DocumentFormat.OpenXml.Spreadsheet; using System; using System.Collections.Generic; using System.Data; @@ -9,7 +13,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using DataAnalysis.Core.Models; +using System.Xml.Linq; namespace DataAnalysis.Core.Import; @@ -28,38 +32,57 @@ public DelimitedTableReader(IDelimitedTableParsingProfile profile) public async Task ReadTableAsync(string inputPath, CancellationToken cancellationToken = default) { - if (string.IsNullOrWhiteSpace(inputPath)) throw new ArgumentException(ErrorPathEmpty, nameof(inputPath)); - if (!File.Exists(inputPath)) throw new FileNotFoundException(ErrorInputNotFound, inputPath); + if (string.IsNullOrWhiteSpace(inputPath)) + throw new ArgumentException(ErrorPathEmpty, nameof(inputPath)); + if (!File.Exists(inputPath)) + throw new FileNotFoundException(ErrorInputNotFound, inputPath); var dt = new DataTable(_profile.TableName); using var fs = new FileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); using var reader = new StreamReader(fs, DetectEncoding(fs) ?? Encoding.UTF8, detectEncodingFromByteOrderMarks: true); // Header - Dictionary? headerMap = null; + Dictionary? headerMap = null; + var inclFields = new List(); + if (_profile.HasHeaderRow) { var headerLine = await reader.ReadLineAsync().ConfigureAwait(false); if (headerLine is not null) { var headers = SplitLine(headerLine, _profile.Delimiter, _profile.Quote, _profile.TrimWhitespace); - headerMap = headers.Select((h,i)=> new { Name = (h ?? string.Empty).Trim(), i}) - .GroupBy(x=>x.Name, StringComparer.OrdinalIgnoreCase) - .ToDictionary(g=>g.Key, g=>g.First().i, StringComparer.OrdinalIgnoreCase); + headerMap = headers.Select((h, i) => new { Name = (h ?? string.Empty).Trim(), i }) + .GroupBy(x => x.Name, StringComparer.OrdinalIgnoreCase) + .ToDictionary(g => g.Key, g => g.First().i, StringComparer.OrdinalIgnoreCase); + inclFields.AddRange(headerMap.Select((h, i) => i)); + foreach (var h in headerMap) + { + if (_profile.FixedColumns.Any((f) => f.Source == h.Key)) + inclFields.Remove(h.Value); + if (_profile.ExtractionRules.Any((r) => r.SourceColumn == h.Key)) + inclFields.Remove(h.Value); + } + } } // Ensure core columns exist foreach (var mapping in _profile.FixedColumns) { - EnsureColumn(dt, mapping.Target); + EnsureColumn(dt, mapping.Target, mapping.IsDateTime); + } + foreach (var i in inclFields) + { + var hmi = headerMap.Values.FirstOrDefault((v) => (v == i)); + EnsureColumn(dt, headerMap.Keys.ToArray()[hmi]); } string? line; while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) is not null) { cancellationToken.ThrowIfCancellationRequested(); - if (string.IsNullOrWhiteSpace(line)) continue; + if (string.IsNullOrWhiteSpace(line)) + continue; var fields = SplitLine(line, _profile.Delimiter, _profile.Quote, _profile.TrimWhitespace); // extraction rules @@ -77,19 +100,27 @@ public async Task ReadTableAsync(string inputPath, CancellationToken foreach (var mapping in _profile.FixedColumns) row[mapping.Target] = Extract(mapping, fields, headerMap, attributes); - + + foreach (var i in inclFields) + { + var hmi = headerMap.Values.FirstOrDefault((v) => (v == i)); + if (fields.Count>i) + row[headerMap.Keys.ToArray()[hmi]] = fields[i]; + } + foreach (var kv in attributes) - if (!_profile.FixedColumns.Any((f)=> f.Source == kv.Key)) - row[kv.Key] = kv.Value ?? string.Empty; + if (!_profile.FixedColumns.Any((f) => f.Source == kv.Key)) + row[kv.Key] = kv.Value ?? string.Empty; dt.Rows.Add(row); } return dt; } - private void ApplyExtractionRules(IReadOnlyList fields, Dictionary? headerMap, Dictionary attributes) + private void ApplyExtractionRules(IReadOnlyList fields, Dictionary? headerMap, Dictionary attributes) { - if (_profile.ExtractionRules.Count ==0) return; + if (_profile.ExtractionRules.Count == 0) + return; foreach (var rule in _profile.ExtractionRules) { var xFlag = true; @@ -115,13 +146,13 @@ private void ApplyExtractionRules(IReadOnlyList fields, Dictionary fields, Dictionary fields, Dictionary? headerMap, IReadOnlyDictionary? attributes = null) -{ - // Hilfsfunktionen - int? ResolveFieldIndex(string? selector) { - if (string.IsNullOrWhiteSpace(selector)) return null; - if (int.TryParse(selector, out var idx)) return idx; - if (headerMap is null) return null; - return headerMap.TryGetValue(selector, out var col) ? col : null; - } - - string? GetByIndex(int? idx) => idx is >= 0 && idx < fields.Count ? fields[idx.Value] : null; + // Hilfsfunktionen + int? ResolveFieldIndex(string? selector) + { + if (string.IsNullOrWhiteSpace(selector)) + return null; + if (int.TryParse(selector, out var idx)) + return idx; + if (headerMap is null) + return null; + return headerMap.TryGetValue(selector, out var col) ? col : null; + } - string? GetBySelector(string? selector, out int? usedIndex) - { - usedIndex = ResolveFieldIndex(selector); - if (usedIndex is not null) - return GetByIndex(usedIndex); + string? GetByIndex(int? idx) => idx is >= 0 && idx < fields.Count ? fields[idx.Value] : null; - if (!string.IsNullOrWhiteSpace(selector) && attributes is not null && attributes.TryGetValue(selector, out var value)) - return value; + string? GetBySelector(string? selector, out int? usedIndex) + { + usedIndex = ResolveFieldIndex(selector); + if (usedIndex is not null) + return GetByIndex(usedIndex); - return null; - } + if (!string.IsNullOrWhiteSpace(selector) && attributes is not null && attributes.TryGetValue(selector, out var value)) + return value; - // Werte über fields (Index/Header) ODER attributes extrahieren - var tsRaw = GetBySelector(mapping.Source, out var _); + return null; + } - // Normalisierung/Parsing - if (mapping.IsDateTime) - tsRaw = ParseTimestamp(tsRaw)?.ToString(TimestampFormatRoundTrip) ?? ""; - + // Werte über fields (Index/Header) ODER attributes extrahieren + var tsRaw = GetBySelector(mapping.Source, out var _); - return tsRaw; -} + return tsRaw; + } private DateTimeOffset? ParseTimestamp(string? raw) { - if (string.IsNullOrWhiteSpace(raw)) return null; + if (string.IsNullOrWhiteSpace(raw)) + return null; raw = raw.Trim().Trim('"').Trim('[', ']'); foreach (var cultureName in _profile.Cultures) { var culture = string.IsNullOrEmpty(cultureName) ? CultureInfo.InvariantCulture : CultureInfo.GetCultureInfo(cultureName); - if (_profile.TimestampFormats.Count >0 && DateTimeOffset.TryParseExact(raw, _profile.TimestampFormats.ToArray(), culture, DateTimeStyles.AssumeLocal, out var dto)) + if (_profile.TimestampFormats.Count > 0 && DateTimeOffset.TryParseExact(raw, _profile.TimestampFormats.ToArray(), culture, DateTimeStyles.AssumeLocal, out var dto)) + return dto; + if (DateTimeOffset.TryParse(raw, culture, DateTimeStyles.AssumeLocal, out dto)) return dto; - if (DateTimeOffset.TryParse(raw, culture, DateTimeStyles.AssumeLocal, out dto)) return dto; } return null; } @@ -195,14 +227,15 @@ private string Extract( var sb = new StringBuilder(); bool inQuotes = false; char q = quote ?? '\0'; - for (int i=0; i bom = stackalloc byte[4]; - fs.Position =0; + fs.Position = 0; int read = fs.Read(bom); fs.Position = original; - if (read >=3 && bom[0] ==0xEF && bom[1] ==0xBB && bom[2] ==0xBF) return Encoding.UTF8; - if (read >=2 && bom[0] ==0xFF && bom[1] ==0xFE) return Encoding.Unicode; - if (read >=2 && bom[0] ==0xFE && bom[1] ==0xFF) return Encoding.BigEndianUnicode; - if (read >=4 && bom[0] ==0xFF && bom[1] ==0xFE && bom[2] ==0x00 && bom[3] ==0x00) return Encoding.UTF32; - if (read >=4 && bom[0] ==0x00 && bom[1] ==0x00 && bom[2] ==0xFE && bom[3] ==0xFF) return new UTF32Encoding(bigEndian: true, byteOrderMark: true); + if (read >= 3 && bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) + return Encoding.UTF8; + if (read >= 2 && bom[0] == 0xFF && bom[1] == 0xFE) + return Encoding.Unicode; + if (read >= 2 && bom[0] == 0xFE && bom[1] == 0xFF) + return Encoding.BigEndianUnicode; + if (read >= 4 && bom[0] == 0xFF && bom[1] == 0xFE && bom[2] == 0x00 && bom[3] == 0x00) + return Encoding.UTF32; + if (read >= 4 && bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == 0xFE && bom[3] == 0xFF) + return new UTF32Encoding(bigEndian: true, byteOrderMark: true); return null; } } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/FieldExtractionRule.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/FieldExtractionRule.cs index d8b24580b..c762ef4a8 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/FieldExtractionRule.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/FieldExtractionRule.cs @@ -1,3 +1,4 @@ +using DataAnalysis.Core.Import.Interfaces; using System.Collections.Generic; using System.Text.RegularExpressions; diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IDelimitedTableParsingProfile.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/Interfaces/IDelimitedTableParsingProfile.cs similarity index 92% rename from CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IDelimitedTableParsingProfile.cs rename to CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/Interfaces/IDelimitedTableParsingProfile.cs index b89fd4ed4..45cd98a85 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IDelimitedTableParsingProfile.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/Interfaces/IDelimitedTableParsingProfile.cs @@ -1,4 +1,4 @@ -namespace DataAnalysis.Core.Import; +namespace DataAnalysis.Core.Import.Interfaces; public interface IDelimitedTableParsingProfile { diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IFieldExtractionRule.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/Interfaces/IFieldExtractionRule.cs similarity index 73% rename from CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IFieldExtractionRule.cs rename to CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/Interfaces/IFieldExtractionRule.cs index 4bd7c2c55..16b09e518 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/IFieldExtractionRule.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/Interfaces/IFieldExtractionRule.cs @@ -1,7 +1,6 @@ - -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; -namespace DataAnalysis.Core.Import; +namespace DataAnalysis.Core.Import.Interfaces; public interface IFieldExtractionRule { diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/ITableReader.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/Interfaces/ITableReader.cs similarity index 77% rename from CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/ITableReader.cs rename to CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/Interfaces/ITableReader.cs index d064739dc..19ee41d88 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/ITableReader.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Import/Interfaces/ITableReader.cs @@ -1,6 +1,6 @@ using System.Data; -namespace DataAnalysis.Core.Import; +namespace DataAnalysis.Core.Import.Interfaces; public interface ITableReader { diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AggregationResult.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AggregationResult.cs index 8305362f4..cc4d97766 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AggregationResult.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AggregationResult.cs @@ -7,8 +7,11 @@ public sealed class AggregationResult public required string Title { get; init; } public required IReadOnlyList Dimensions { get; init; } + // Optional: Textuelle Beschreibung des lokalen Filters + public string? FilterText { get; init; } + // For1D aggregations - public IReadOnlyDictionary? Series { get; init; } + public IReadOnlyDictionary? Series { get; init; } // For2D aggregations public IReadOnlyList? Columns { get; init; } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisAggregateProfile.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisAggregateProfile.cs index a9d2d0c17..62d21f842 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisAggregateProfile.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisAggregateProfile.cs @@ -8,6 +8,9 @@ namespace DataAnalysis.Core.Models; /// public sealed class AnalysisAggregateProfile { + // Optional: globaler Filter, der für alle Queries gilt + public FilterDefinition? GlobalFilter { get; init; } + public required IReadOnlyList Queries { get; init; } public static AnalysisAggregateProfile Default => new() @@ -15,12 +18,13 @@ public sealed class AnalysisAggregateProfile Queries = new List { new AnalysisQuery { Title = "Severity", Dimensions = new [] { DimensionKind.Severity } }, - new AnalysisQuery { Title = "Source", Dimensions = new [] { DimensionKind.Source } }, - new AnalysisQuery { Title = "Hour x Source", Dimensions = new [] { DimensionKind.Hour, DimensionKind.Source } }, + new AnalysisQuery { Title = "Source", Filter = new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" }, Dimensions = new [] { DimensionKind.Source }, TopN =300 }, + new AnalysisQuery { Title = "Hour x Source", Dimensions = new [] { DimensionKind.Hour, DimensionKind.Source },Filter = new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" }, }, new AnalysisQuery { Title = "Top-Events", Dimensions = new [] { DimensionKind.MessageNormalized }, TopN =100 }, - new AnalysisQuery { Title = "Top-Events2", Dimensions = new [] { DimensionKind.MessageNormalized }, TopN =10 }, + new AnalysisQuery { Title = "Top-Events2", Dimensions = new [] { DimensionKind.MessageNormalized }, TopN =30, Filter = new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" }, }, new AnalysisQuery { Title = "Severity x Source", Dimensions = new [] { DimensionKind.Source, DimensionKind.Severity }, Columns = Enum.GetValues().Select(s => s.ToString()).ToArray() }, - new AnalysisQuery { Title = "Events x Source (Top50)", Dimensions = new [] { DimensionKind.Source, DimensionKind.MessageNormalized }}, + new AnalysisQuery { Title = "Events x Source (Top50)", Dimensions = new [] { DimensionKind.Source, DimensionKind.MessageNormalized }, TopN =300}, + new AnalysisQuery { Title = "Koordinate Cluster", Dimensions = new [] { DimensionKind.X, DimensionKind.Y }, TopN = 30, IsDBScan = true, DbEps = 10.0, DbMinPts = 3, Filter = new ValueFilterDefinition { Field = "Severity", Type = "Enum", Operator = FilterOperator.Le, Value = "Error" }, } } }; } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisModel.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisModel.cs index ac130ac80..2d3d5e398 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisModel.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisModel.cs @@ -2,6 +2,7 @@ using System.Text; using System.Text.RegularExpressions; using DataAnalysis.Core.Export; +using DataAnalysis.Core.Export.Interfaces; using DataAnalysis.Core.Import; namespace DataAnalysis.Core.Models; @@ -33,6 +34,7 @@ public async Task AnalyzeAsync(string inputPath, CancellationTok ParsedLines = meta.ParsedLines, FirstTimestamp = meta.First, LastTimestamp = meta.Last, + GlobalFilterText = FilterTextFormatter.Describe(_profile.GlobalFilter), Aggregations = aggregations }; } @@ -68,12 +70,36 @@ private static IReadOnlyList BuildAggregations(SyslogReadResu { var list = new List(); if (profile.Queries.Count ==0) return list; - var builders = profile.Queries.Select(q => new QueryBuilder(q)).ToArray(); + + var globalPredicate = FilterCompiler.Compile(profile.GlobalFilter); + + var items = profile.Queries + .Select(q => + { + var qb = new QueryBuilder(q) { FilterText = FilterTextFormatter.Describe(q.Filter) }; + return new { Builder = qb, Predicate = Combine(globalPredicate, FilterCompiler.Compile(q.Filter)) }; + }) + .ToArray(); + foreach (var e in read.Entries) { - foreach (var b in builders) b.Observe(e); + foreach (var it in items) + { + if (it.Predicate is null || it.Predicate(e)) + { + it.Builder.Observe(e); + } + } } - foreach (var b in builders) list.Add(b.Build()); + + foreach (var it in items) list.Add(it.Builder.Build()); return list; } + + private static Func? Combine(Func? a, Func? b) + { + if (a is null) return b; + if (b is null) return a; + return e => a(e) && b(e); + } } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisQuery.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisQuery.cs index f88762c12..9d62fbf87 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisQuery.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisQuery.cs @@ -18,4 +18,14 @@ public sealed class AnalysisQuery // Optional: TopN pro Zeile für2D-Kreuztabellen public int? RowTopN { get; init; } + + // Optional: DBSCAN-Clustering der Punkte (bei2D X/Y) + public bool IsDBScan { get; init; } = false; + // Optional: Radius (Epsilon) für DBSCAN (gleiche Einheit wie X/Y) + public double? DbEps { get; init; } + // Optional: Mindestanzahl Punkte pro Cluster + public int? DbMinPts { get; init; } + + // Optional: Auswertungsspezifischer Filter (DTO, serialisierbar) + public FilterDefinition? Filter { get; init; } } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisResult.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisResult.cs index cec05a54c..95454b74b 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisResult.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/AnalysisResult.cs @@ -9,6 +9,9 @@ public sealed class AnalysisResult public DateTimeOffset? FirstTimestamp { get; init; } public DateTimeOffset? LastTimestamp { get; init; } + // Optional: Textuelle Beschreibung des globalen Filters + public string? GlobalFilterText { get; init; } + // Alle Auswertungen als Aggregationen public IReadOnlyList Aggregations { get; init; } = Array.Empty(); } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DbScan.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DbScan.cs new file mode 100644 index 000000000..cf28d3138 --- /dev/null +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DbScan.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Numerics; + +namespace DataAnalysis.Core.Models; + +public static partial class DbScan +{ + + public static List> Cluster(IReadOnlyList points, double eps, int minPts) + { + var clusters = new List>(); + if (points.Count == 0) + return clusters; + var visited = new bool[points.Count]; + var assigned = new int[points.Count]; + Array.Fill(assigned, -1); + var eps2 = eps * eps; + + for (int i = 0; i < points.Count; i++) + { + if (visited[i]) + continue; + visited[i] = true; + var neighbors = RegionQuery(points, i, eps2); + if (neighbors.Count < minPts) + { + continue; // noise + } + var cluster = new List(); + clusters.Add(cluster); + ExpandCluster(points, i, neighbors, cluster, visited, assigned, eps2, minPts, clusters.Count - 1); + } + return clusters; + } + + private static void ExpandCluster(IReadOnlyList points, int index, List neighbors, List cluster, + bool[] visited, int[] assigned, double eps2, int minPts, int clusterId) + { + cluster.Add(points[index]); + assigned[index] = clusterId; + for (int nIdx = 0; nIdx < neighbors.Count; nIdx++) + { + int j = neighbors[nIdx]; + if (!visited[j]) + { + visited[j] = true; + var neighbors2 = RegionQuery(points, j, eps2); + if (neighbors2.Count >= minPts) + { + // merge neighbors2 into neighbors + foreach (var k in neighbors2) + { + if (!neighbors.Contains(k)) + neighbors.Add(k); + } + } + } + if (assigned[j] == -1) + { + cluster.Add(points[j]); + assigned[j] = clusterId; + } + } + } + + private static List RegionQuery(IReadOnlyList pts, int i, double eps2) + { + var res = new List(); + var p = pts[i]; + for (int j = 0; j < pts.Count; j++) + { + if (j == i) + continue; + var q = pts[j]; + var dx = p.X - q.X; + var dy = p.Y - q.Y; + var d2 = dx * dx + dy * dy; + if (d2 <= eps2) + res.Add(j); + } + return res; + } +} diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DictionaryHelpers.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DictionaryHelpers.cs new file mode 100644 index 000000000..0fc9721dc --- /dev/null +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DictionaryHelpers.cs @@ -0,0 +1,66 @@ +// Ziel +// - Einfache Konvertierung von Dictionary nach Dictionary. +// - Optional: Semantik für Groß/Kleinschreibung (case-insensitive) beibehalten, falls die Quell-Dictionary das nutzt. + +// Plan (Pseudocode) +// 1) Provide extension ToObjectKeyDictionary(IDictionary): +// - Use LINQ ToDictionary with key selector casting string to object. +// 2) Provide optional extension ToObjectKeyDictionaryIgnoreCase(IDictionary): +// - Create target Dictionary with a custom IEqualityComparer that treats object-keys of type string +// case-insensitive (OrdinalIgnoreCase), otherwise falls back to default equality. +// - Copy entries. +// 3) Keep API small and easy to use. + +// Verwendung: +// var dictObj = myStringDict.ToObjectKeyDictionary(); +// var dictObjIgnoreCase = myStringDict.ToObjectKeyDictionaryIgnoreCase(); + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace DataAnalysis.Core.Utilities; + +internal static class DictionaryHelpers +{ + public static Dictionary ToObjectKeyDictionary(this IDictionary source) + { + if (source is null) throw new ArgumentNullException(nameof(source)); + return source.ToDictionary(kv => (object)kv.Key, kv => kv.Value); + } + + public static Dictionary ToObjectKeyDictionaryIgnoreCase(this IDictionary source) + { + if (source is null) throw new ArgumentNullException(nameof(source)); + + var result = new Dictionary(source.Count, ObjectStringIgnoreCaseComparer.Instance); + foreach (var kv in source) + { + result[kv.Key] = kv.Value; + } + return result; + } + + private sealed class ObjectStringIgnoreCaseComparer : IEqualityComparer + { + public static readonly ObjectStringIgnoreCaseComparer Instance = new(); + + public new bool Equals(object? x, object? y) + { + if (ReferenceEquals(x, y)) return true; + if (x is null || y is null) return false; + + if (x is string xs && y is string ys) + return StringComparer.OrdinalIgnoreCase.Equals(xs, ys); + + return EqualityComparer.Default.Equals(x, y); + } + + public int GetHashCode(object obj) + { + if (obj is null) return 0; + if (obj is string s) return StringComparer.OrdinalIgnoreCase.GetHashCode(s); + return obj.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DimensionKind.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DimensionKind.cs index 60989b831..30f2a0758 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DimensionKind.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/DimensionKind.cs @@ -5,5 +5,7 @@ public enum DimensionKind Severity, Source, Hour, // Hour bucket of timestamp - MessageNormalized + MessageNormalized, + X, + Y } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterCompiler.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterCompiler.cs new file mode 100644 index 000000000..cec3effcb --- /dev/null +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterCompiler.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; + +namespace DataAnalysis.Core.Models; + +public static class FilterCompiler +{ + public static Func? Compile(FilterDefinition? def) + { + if (def is null) + return null; + return def switch + { + ValueFilterDefinition v => CompileValue(v), + GroupFilterDefinition g => CompileGroup(g), + _ => null + }; + } + + private static Func? CompileGroup(GroupFilterDefinition g) + { + if (g.Filters is null || g.Filters.Count == 0) + { + return g.Negate ? _ => false : _ => true; + } + var compiled = g.Filters.Select(Compile).Where(p => p is not null).Cast>().ToArray(); + if (compiled.Length == 0) + return g.Negate ? _ => true : _ => false; + + Func core = g.Mode.Equals("or", StringComparison.OrdinalIgnoreCase) + ? e => + { + for (int i = 0; i < compiled.Length; i++) + if (compiled[i](e)) + return true; + return false; + } + : e => + { + for (int i = 0; i < compiled.Length; i++) + if (!compiled[i](e)) + return false; + return true; + }; + + if (g.Negate) + return e => !core(e); + return core; + } + + private static Func? CompileValue(ValueFilterDefinition v) + { + var cmp = v.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; + var culture = string.IsNullOrWhiteSpace(v.Culture) ? CultureInfo.InvariantCulture : new CultureInfo(v.Culture); + var formats = v.Formats ?? Array.Empty(); + + object? parsedFrom = null; + object? parsedTo = null; + + switch (v.DataType) + { + case FilterDataType.Number: + if (v.Value is not null && double.TryParse(v.Value, NumberStyles.Float, culture, out var dn)) + parsedFrom = dn; + if (v.ValueTo is not null && double.TryParse(v.ValueTo, NumberStyles.Float, culture, out var dn2)) + parsedTo = dn2; + break; + case FilterDataType.DateTime: + if (v.Value is not null) + { + if (formats.Length > 0) + { + if (DateTimeOffset.TryParseExact(v.Value, formats, culture, DateTimeStyles.None, out var dto)) + parsedFrom = dto; + } + else if (DateTimeOffset.TryParse(v.Value, culture, DateTimeStyles.None, out var dto2)) + parsedFrom = dto2; + } + if (v.ValueTo is not null) + { + if (formats.Length > 0) + { + if (DateTimeOffset.TryParseExact(v.ValueTo, formats, culture, DateTimeStyles.None, out var dto)) + parsedTo = dto; + } + else if (DateTimeOffset.TryParse(v.ValueTo, culture, DateTimeStyles.None, out var dto2)) + parsedTo = dto2; + } + break; + case FilterDataType.Enum: + if (!string.IsNullOrWhiteSpace(v.EnumType)) + { + var t = Type.GetType(v.EnumType!, throwOnError: false, ignoreCase: true); + if (t is not null && t.IsEnum) + { + if (v.Value is not null && Enum.TryParse(t, v.Value, ignoreCase: !v.CaseSensitive, out var ev)) + parsedFrom = ev; + if (v.ValueTo is not null && Enum.TryParse(t, v.ValueTo, ignoreCase: !v.CaseSensitive, out var ev2)) + parsedTo = ev2; + } + } + else + { + // fallback: Severity Enum + if (v.Value is not null && Enum.TryParse(typeof(SyslogSeverity), v.Value, !v.CaseSensitive, out var sev)) + parsedFrom = sev; + if (v.ValueTo is not null && Enum.TryParse(typeof(SyslogSeverity), v.ValueTo, !v.CaseSensitive, out var sev2)) + parsedTo = sev2; + } + break; + case FilterDataType.String: + default: + // nothing to pre-parse + break; + } + + return e => + { + if (!TryGetFieldText(e, v.Field, out var text, out var typed)) + return false; + + switch (v.DataType) + { + case FilterDataType.Number: + if (!TryAsDouble(text, typed, culture, out var d)) + return false; + return CompareNumbers(d, v.Operator, parsedFrom as double?, parsedTo as double?); + case FilterDataType.DateTime: + if (!TryAsDateTimeOffset(text, typed, culture, formats, out var dt)) + return false; + return CompareDateTimes(dt, v.Operator, parsedFrom as DateTimeOffset?, parsedTo as DateTimeOffset?); + case FilterDataType.Enum: + if (!TryAsEnum(text, typed, parsedFrom, out var eobj)) + return false; + return CompareEnums(eobj, v.Operator, parsedFrom, parsedTo); + case FilterDataType.String: + default: + return CompareStrings(text ?? string.Empty, v.Operator, v.Value, v.ValueTo, cmp); + } + }; + } + + private static bool TryGetFieldText(SyslogEntry e, string field, out string? text, out object? typed) + { + text = null; + typed = null; + if (string.Equals(field, "Timestamp", StringComparison.OrdinalIgnoreCase)) + { + if (e.Timestamp is DateTimeOffset dto) + { typed = dto; text = dto.ToString("o"); return true; } + return false; + } + if (string.Equals(field, "Severity", StringComparison.OrdinalIgnoreCase)) + { + typed = e.Severity; + text = e.Severity.ToString(); + return true; + } + if (string.Equals(field, "Source", StringComparison.OrdinalIgnoreCase)) + { + typed = text = e.Source ?? string.Empty; + return true; + } + if (string.Equals(field, "Message", StringComparison.OrdinalIgnoreCase)) + { + typed = text = e.Message ?? string.Empty; + return true; + } + // Attributes (case-insensitive key match) + if (e.Attributes is { Count: > 0 }) + { + foreach (var kv in e.Attributes) + { + if (string.Equals(kv.Key, field, StringComparison.OrdinalIgnoreCase)) + { typed = text = kv.Value ?? string.Empty; return true; } + } + } + return false; + } + + private static bool TryAsDouble(string? text, object? typed, CultureInfo culture, out double d) + { + if (typed is double dd) + { d = dd; return true; } + if (double.TryParse(text, NumberStyles.Float, culture, out d)) + return true; + return false; + } + + private static bool TryAsDateTimeOffset(string? text, object? typed, CultureInfo culture, string[] formats, out DateTimeOffset value) + { + if (typed is DateTimeOffset dto) + { value = dto; return true; } + if (formats.Length > 0 && DateTimeOffset.TryParseExact(text, formats, culture, DateTimeStyles.None, out value)) + return true; + if (DateTimeOffset.TryParse(text, culture, DateTimeStyles.None, out value)) + return true; + value = default; + return false; + } + + private static bool TryAsEnum(string? text, object? typed, object? parsedFrom, out object? value) + { + value = null; + if (typed is Enum e) + { value = e; return true; } + if (parsedFrom is not null && parsedFrom.GetType().IsEnum) + { + if (Enum.TryParse(parsedFrom.GetType(), text, true, out var ev)) + { value = ev; return true; } + } + else + { + if (Enum.TryParse(typeof(SyslogSeverity), text, true, out var sev)) + { value = sev; return true; } + } + return false; + } + + private static bool CompareStrings(string text, FilterOperator op, string? value, string? valueTo, StringComparison cmp) + { + return op switch + { + FilterOperator.Eq => string.Equals(text, value ?? string.Empty, cmp), + FilterOperator.Ne => !string.Equals(text, value ?? string.Empty, cmp), + FilterOperator.Contains => value is not null && text.IndexOf(value, cmp) >= 0, + FilterOperator.StartsWith => value is not null && text.StartsWith(value, cmp), + FilterOperator.EndsWith => value is not null && text.EndsWith(value, cmp), + FilterOperator.In => value is not null && value.Split('\u001F', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Any(v => string.Equals(text, v, cmp)), + FilterOperator.Between => value is not null && valueTo is not null && string.Compare(text, value, cmp) >= 0 && string.Compare(text, valueTo, cmp) <= 0, + FilterOperator.Lt => value is not null && string.Compare(text, value, cmp) < 0, + FilterOperator.Le => value is not null && string.Compare(text, value, cmp) <= 0, + FilterOperator.Gt => value is not null && string.Compare(text, value, cmp) > 0, + FilterOperator.Ge => value is not null && string.Compare(text, value, cmp) >= 0, + _ => false + }; + } + + private static bool CompareNumbers(double actual, FilterOperator op, double? from, double? to) + { + return op switch + { + FilterOperator.Eq => from is not null && actual == from, + FilterOperator.Ne => from is not null && actual != from, + FilterOperator.Lt => from is not null && actual < from, + FilterOperator.Le => from is not null && actual <= from, + FilterOperator.Gt => from is not null && actual > from, + FilterOperator.Ge => from is not null && actual >= from, + FilterOperator.Between => from is not null && to is not null && actual >= from && actual <= to, + _ => false + }; + } + + private static bool CompareDateTimes(DateTimeOffset actual, FilterOperator op, DateTimeOffset? from, DateTimeOffset? to) + { + return op switch + { + FilterOperator.Eq => from is not null && actual == from, + FilterOperator.Ne => from is not null && actual != from, + FilterOperator.Lt => from is not null && actual < from, + FilterOperator.Le => from is not null && actual <= from, + FilterOperator.Gt => from is not null && actual > from, + FilterOperator.Ge => from is not null && actual >= from, + FilterOperator.Between => from is not null && to is not null && actual >= from && actual <= to, + _ => false + }; + } + + private static bool CompareEnums(object actual, FilterOperator op, object? from, object? to) + { + int cmp(object a, object b) => Comparer.Default.Compare(Convert.ToInt32(a), Convert.ToInt32(b)); + return op switch + { + FilterOperator.Eq => from is not null && Equals(actual, from), + FilterOperator.Ne => from is not null && !Equals(actual, from), + FilterOperator.Lt => from is not null && cmp(actual, from) < 0, + FilterOperator.Le => from is not null && cmp(actual, from) <= 0, + FilterOperator.Gt => from is not null && cmp(actual, from) > 0, + FilterOperator.Ge => from is not null && cmp(actual, from) >= 0, + FilterOperator.Between => from is not null && to is not null && cmp(actual, from) >= 0 && cmp(actual, to) <= 0, + _ => false + }; + } +} diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterDataType.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterDataType.cs new file mode 100644 index 000000000..316e6d37c --- /dev/null +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterDataType.cs @@ -0,0 +1,9 @@ +namespace DataAnalysis.Core.Models; + +public enum FilterDataType +{ + String, + Number, + DateTime, + Enum +} diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterDefinitions.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterDefinitions.cs new file mode 100644 index 000000000..4e0577214 --- /dev/null +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterDefinitions.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace DataAnalysis.Core.Models; + +/// +/// Serialisierbares DTO für Value- und Gruppen-Filter. +/// "type" diskriminiert den konkreten Filter. +/// +public abstract class FilterDefinition +{ + [JsonPropertyName("type")] public required string Type { get; init; } +} + +public sealed class ValueFilterDefinition : FilterDefinition +{ + public ValueFilterDefinition() => Type = "value"; + + public required string Field { get; init; } + public required FilterOperator Operator { get; init; } + public string? Value { get; init; } + public string? ValueTo { get; init; } // für Between + public FilterDataType DataType { get; init; } = FilterDataType.String; + public bool CaseSensitive { get; init; } = false; + public string? EnumType { get; init; } // optional: vollqualifizierter Typname, wenn DataType==Enum + public string? Culture { get; init; } + public string[]? Formats { get; init; } +} + +public sealed class GroupFilterDefinition : FilterDefinition +{ + public GroupFilterDefinition() => Type = "group"; + + // "and" oder "or" + public required string Mode { get; init; } + public required IReadOnlyList Filters { get; init; } + public bool Negate { get; init; } = false; +} diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterOperator.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterOperator.cs new file mode 100644 index 000000000..9d17a4f72 --- /dev/null +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterOperator.cs @@ -0,0 +1,16 @@ +namespace DataAnalysis.Core.Models; + +public enum FilterOperator +{ + Eq, + Ne, + Lt, + Le, + Gt, + Ge, + Contains, + StartsWith, + EndsWith, + In, + Between +} diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterTextFormatter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterTextFormatter.cs new file mode 100644 index 000000000..336d82b2e --- /dev/null +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/FilterTextFormatter.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace DataAnalysis.Core.Models; + +public static class FilterTextFormatter +{ + public static string? Describe(FilterDefinition? def) + { + if (def is null) return null; + return def switch + { + ValueFilterDefinition v => DescribeValue(v), + GroupFilterDefinition g => DescribeGroup(g), + _ => null + }; + } + + private static string DescribeGroup(GroupFilterDefinition g) + { + if (g.Filters is null || g.Filters.Count ==0) return g.Negate ? "NOT (—)" : "(—)"; + var inner = string.Join(g.Mode.Equals("or", StringComparison.OrdinalIgnoreCase) ? " OR " : " AND ", g.Filters.Select(Describe).Where(s => !string.IsNullOrWhiteSpace(s))); + var s = $"({inner})"; + return g.Negate ? $"NOT {s}" : s; + } + + private static string DescribeValue(ValueFilterDefinition v) + { + var field = v.Field; + var op = v.Operator switch + { + FilterOperator.Eq => "=", + FilterOperator.Ne => "!=", + FilterOperator.Lt => "<", + FilterOperator.Le => "<=", + FilterOperator.Gt => ">", + FilterOperator.Ge => ">=", + FilterOperator.Contains => "contains", + FilterOperator.StartsWith => "startsWith", + FilterOperator.EndsWith => "endsWith", + FilterOperator.In => "in", + FilterOperator.Between => "between", + _ => v.Operator.ToString() + }; + string value = v.Value ?? string.Empty; + string valueTo = v.ValueTo ?? string.Empty; + + if (v.DataType == FilterDataType.DateTime) + { + // Für Anzeige: ISO oder angegebene Culture + var culture = string.IsNullOrWhiteSpace(v.Culture) ? CultureInfo.InvariantCulture : new CultureInfo(v.Culture); + if (DateTimeOffset.TryParse(value, culture, DateTimeStyles.None, out var dto)) value = dto.ToString("s"); + if (DateTimeOffset.TryParse(valueTo, culture, DateTimeStyles.None, out var dto2)) valueTo = dto2.ToString("s"); + } + + return v.Operator == FilterOperator.Between + ? $"{field} between '{value}' and '{valueTo}'" + : v.Operator == FilterOperator.In + ? $"{field} in [{value}]" + : $"{field} {op} '{value}'"; + } +} diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/ProfileSerialization.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/ProfileSerialization.cs new file mode 100644 index 000000000..b7203e35e --- /dev/null +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/Filters/ProfileSerialization.cs @@ -0,0 +1,42 @@ +using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace DataAnalysis.Core.Models; + +public static class ProfileSerialization +{ + private static readonly JsonSerializerOptions s_options = CreateOptions(); + + private static JsonSerializerOptions CreateOptions() + { + var o = new JsonSerializerOptions + { + WriteIndented = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + }; + // Polymorphie per Type-Discriminator "type" + o.TypeInfoResolverChain.Insert(0, FilterDefinitionContext.Default); + return o; + } + + public static string ToJson(AnalysisAggregateProfile profile) + => JsonSerializer.Serialize(profile, s_options); + + public static AnalysisAggregateProfile? FromJson(string json) + => JsonSerializer.Deserialize(json, s_options); + + public static void SaveToFile(string path, AnalysisAggregateProfile profile) + => File.WriteAllText(path, ToJson(profile)); + + public static AnalysisAggregateProfile? LoadFromFile(string path) + => File.Exists(path) ? FromJson(File.ReadAllText(path)) : null; +} + +[JsonSerializable(typeof(AnalysisAggregateProfile))] +[JsonSerializable(typeof(AnalysisQuery))] +[JsonSerializable(typeof(FilterDefinition), GenerationMode = JsonSourceGenerationMode.Metadata)] +[JsonSerializable(typeof(ValueFilterDefinition))] +[JsonSerializable(typeof(GroupFilterDefinition))] +internal partial class FilterDefinitionContext : JsonSerializerContext { } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/QueryBuilder.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/QueryBuilder.cs index 3e95281ee..488353515 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/QueryBuilder.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/QueryBuilder.cs @@ -1,3 +1,8 @@ +using DataAnalysis.Core.Utilities; +using System.Drawing; +using System.Globalization; +using System.Numerics; + namespace DataAnalysis.Core.Models; internal sealed class QueryBuilder @@ -7,10 +12,19 @@ internal sealed class QueryBuilder private readonly Dictionary? _series; private readonly Dictionary>? _matrix; + // For DBSearch: collect raw points + private readonly List? _points; + + public string? FilterText { get; set; } + public QueryBuilder(AnalysisQuery q) { _q = q; - if (q.Dimensions.Count ==1) + if (q.IsDBScan && q.Dimensions.Count ==2 && q.Dimensions[0] == DimensionKind.X && q.Dimensions[1] == DimensionKind.Y) + { + _points = new List(); + } + else if (q.Dimensions.Count ==1) { _series = new Dictionary(StringComparer.OrdinalIgnoreCase); } @@ -26,7 +40,11 @@ public QueryBuilder(AnalysisQuery q) public void Observe(SyslogEntry e) { - if (_series is not null) + if (_points is not null) + { + if (TryGetCoordinate(e, _q, out var pt)) _points.Add(pt); + } + else if (_series is not null) { var key = GetKey(e, _q.Dimensions[0]); if (key is null) return; @@ -44,6 +62,36 @@ public void Observe(SyslogEntry e) public AggregationResult Build() { + if (_points is not null) + { + var eps = _q.DbEps ??10.0; // default radius + var minPts = _q.DbMinPts ??3; + var clusters = DbScan.Cluster(_points, eps, minPts); + var series = new Dictionary(); + for (int i =0; i < clusters.Count; i++) + { + var c = clusters[i]; + if (c.Count ==0) continue; + float cx =0, cy =0; + foreach (var p in c) + { + cx += p.X; + cy += p.Y; + } + cx /= c.Count; cy /= c.Count; + var key =new Vector2(cx,cy); + series[key] = c.Count; + } + if (_q.TopN is int n) + { + series = series.OrderByDescending(kv => kv.Value).Take(n).ToDictionary(kv => kv.Key, kv => kv.Value); + } + return new AggregationResult { + Title = _q.Title, + Dimensions = _q.Dimensions, + FilterText = this.FilterText, + Series = series }; + } if (_series is not null) { var data = _series; @@ -51,7 +99,13 @@ public AggregationResult Build() { data = data.OrderByDescending(kv => kv.Value).ThenBy(kv => kv.Key).Take(n).ToDictionary(kv => kv.Key, kv => kv.Value, StringComparer.OrdinalIgnoreCase); } - return new AggregationResult { Title = _q.Title, Dimensions = _q.Dimensions, Series = data }; + return new AggregationResult + { + Title = _q.Title, + Dimensions = _q.Dimensions, + FilterText = this.FilterText, + Series = data.ToObjectKeyDictionary() + }; } else { @@ -86,7 +140,7 @@ public AggregationResult Build() [TotalRowKey] = totalRow }; - return new AggregationResult { Title = _q.Title, Dimensions = _q.Dimensions, Columns = columns, Matrix = finalMatrix }; + return new AggregationResult { Title = _q.Title, Dimensions = _q.Dimensions, FilterText = this.FilterText, Columns = columns, Matrix = finalMatrix }; } } @@ -98,8 +152,29 @@ public AggregationResult Build() DimensionKind.Source => string.IsNullOrWhiteSpace(e.Source) ? "(unbekannt)" : e.Source.Trim(), DimensionKind.Hour => e.Timestamp is DateTimeOffset ts ? new DateTimeOffset(ts.Year, ts.Month, ts.Day, ts.Hour,0,0, ts.Offset).LocalDateTime.ToString("yyyy-MM-dd HH:00") : null, DimensionKind.MessageNormalized => AnalysisModel.NormalizeMessage(e.Message), + DimensionKind.X => TryGetAttributeAsDouble(e, "X", out var x) ? x.ToString(CultureInfo.InvariantCulture) : null, + DimensionKind.Y => TryGetAttributeAsDouble(e, "Y", out var y) ? y.ToString(CultureInfo.InvariantCulture) : null, _ => null }; } + + private static bool TryGetCoordinate(SyslogEntry e, AnalysisQuery q, out Vector2 pt) + { + pt = default; + if (!TryGetAttributeAsDouble(e, "X", out var x)) return false; + if (!TryGetAttributeAsDouble(e, "Y", out var y)) return false; + pt = new Vector2((float)x, (float)y); + return true; + } + + private static bool TryGetAttributeAsDouble(SyslogEntry e, string key, out double value) + { + value =0; + if (e.Attributes.TryGetValue(key, out var s) && double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var d)) + { + value = d; return true; + } + return false; + } } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/SyslogSeverity.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/SyslogSeverity.cs index d0c5fbe93..160be6d57 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/SyslogSeverity.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/SyslogSeverity.cs @@ -2,13 +2,14 @@ namespace DataAnalysis.Core.Models; public enum SyslogSeverity { - - Trace = 7, - Debug = 6, - Notice = 5, - Info = 4, - Warn = 3, - Error = 1, - Fatal = 0, - Unknown = -1 + Trace = 8, + Debug = 7, + Notice = 5, + Info = 6, + Warn = 4, + Error = 3, + Critical = 2, + Alert = 1, + Fatal = 0, + Unknown = -1 } diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/TableToEntryAdapter.cs b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/TableToEntryAdapter.cs index 73fa12fa0..9abbe22a0 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/TableToEntryAdapter.cs +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.Core/Models/TableToEntryAdapter.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using DataAnalysis.Core.Import; +using DataAnalysis.Core.Import.Interfaces; namespace DataAnalysis.Core.Models; diff --git a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF.TestHarness/MainWindow.xaml b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF.TestHarness/MainWindow.xaml index a7ab8521e..284034174 100644 --- a/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF.TestHarness/MainWindow.xaml +++ b/CSharpBible/Data/DataAnalysis/DataAnalysis.WPF.TestHarness/MainWindow.xaml @@ -11,7 +11,8 @@