diff --git a/AppDiagnostic/App.config b/AppDiagnostic/App.config
new file mode 100644
index 000000000..3bedfc101
--- /dev/null
+++ b/AppDiagnostic/App.config
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AppDiagnostic/AppDiagnostic.csproj b/AppDiagnostic/AppDiagnostic.csproj
new file mode 100644
index 000000000..65f8e8e7a
--- /dev/null
+++ b/AppDiagnostic/AppDiagnostic.csproj
@@ -0,0 +1,101 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}
+ WinExe
+ AppDiagnostic
+ AppDiagnostic
+ v4.7.2
+ 512
+ true
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\log4net.3.0.3\lib\net462\log4net.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Component
+
+
+
+
+
+ Form
+
+
+ MainForm.cs
+
+
+
+
+ MainForm.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+
+ {8f942729-f454-4c99-ba6c-746962065ae3}
+ SharedComponents
+
+
+
+
\ No newline at end of file
diff --git a/AppDiagnostic/BufferedListView.cs b/AppDiagnostic/BufferedListView.cs
new file mode 100644
index 000000000..c33523f7f
--- /dev/null
+++ b/AppDiagnostic/BufferedListView.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+public class BufferedListView : ListView
+{
+ public BufferedListView()
+ {
+ // Zapne dvojité bufferovanie pre ListView
+ this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
+ this.UpdateStyles();
+ }
+}
\ No newline at end of file
diff --git a/AppDiagnostic/DiagApi.cs b/AppDiagnostic/DiagApi.cs
new file mode 100644
index 000000000..2ea6b1064
--- /dev/null
+++ b/AppDiagnostic/DiagApi.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace AppDiagnostic
+{
+ public class DiagApi
+ {
+ private static MainForm diagWindow; // Udržiava referenciu na existujúce okno
+
+ public DiagApi()
+ {
+ if (diagWindow == null || diagWindow.IsDisposed) // Ak okno neexistuje, vytvoríme ho
+ {
+ diagWindow = new MainForm();
+ diagWindow.FormClosing += (s, e) =>
+ {
+ diagWindow = null; // Keď sa zavrie, vyčistíme referenciu
+ };
+ diagWindow.Show();
+ }
+ else
+ {
+ diagWindow.BringToFront(); // Ak už beží, len ho presunieme na vrch
+ diagWindow.WindowState = FormWindowState.Normal; // Ak je minimalizované, obnovíme ho
+ }
+ }
+ }
+}
diff --git a/AppDiagnostic/LogAdapter.cs b/AppDiagnostic/LogAdapter.cs
new file mode 100644
index 000000000..33a351c7e
--- /dev/null
+++ b/AppDiagnostic/LogAdapter.cs
@@ -0,0 +1,174 @@
+using SharedComponents;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace AppDiagnostic
+{
+ public class LogAdapter
+ {
+ private ListView _listView;
+ public string _filterText = string.Empty;
+ private bool _isUserScrolling = false; // Indikátor, že používateľ manuálne scrolluje
+ private bool _autoScrollEnabled = true; // Indikátor, že automatické scrollovanie je povolené
+ private Timer _refreshTimer;
+ private const int RefreshInterval = 1000;
+
+ public LogAdapter(ListView listView)
+ {
+ _listView = listView;
+
+ // Nastavenie ListView
+ _listView.View = View.Details;
+ _listView.Columns.Clear();
+ _listView.Columns.Add("Logs");
+
+ _listView.Scrollable = true;
+
+ // Nastavenie šírky stĺpca na celú šírku ListView
+ AdjustColumnWidth();
+
+ // Udalosť pre dynamickú zmenu veľkosti stĺpca pri zmene veľkosti ListView
+ _listView.Resize += (sender, args) => AdjustColumnWidth();
+
+ // Pridanie udalosti pre manuálne skrolovanie
+ _listView.MouseWheel += ListView_MouseWheel;
+
+ // Inicializácia časovača na pravidelný refresh
+ _refreshTimer = new Timer { Interval = RefreshInterval };
+ _refreshTimer.Tick += (sender, args) => RefreshListView();
+ _refreshTimer.Start();
+ }
+
+ private void ListView_MouseWheel(object sender, MouseEventArgs e)
+ {
+ _isUserScrolling = true;
+
+ // Ak sa manuálnym scrollovaním používateľ dostane na koniec, obnovíme automatické scrollovanie
+ if (IsOnLastItem())
+ {
+ _isUserScrolling = false;
+ _autoScrollEnabled = true;
+ }
+ else
+ {
+ _autoScrollEnabled = false;
+ }
+ }
+
+ public void RefreshListView()
+ {
+ //LiveLogCache.Instance.AddLog(string.Format("-------------------------------RunDeviceAfter()-------------------------------"));
+
+ if (_listView.InvokeRequired)
+ {
+ _listView.Invoke(new Action(RefreshListView));
+ return;
+ }
+
+ _listView.BeginUpdate();
+ try
+ {
+ // Získanie pozície prvého viditeľného prvku pre stabilizáciu scrollovania
+ int topIndexBeforeRefresh = _listView.TopItem?.Index ?? 0;
+
+ // Pred pridaním nových položiek si pamätáme, či bol používateľ na poslednej položke
+ bool wasOnLastItem = IsOnLastItem();
+
+ // Uchovanie aktuálne označených položiek (indexov)
+ var selectedIndices = _listView.SelectedIndices.Cast().ToList();
+
+ // Načítanie logov
+ var logs = LiveLogCache.Instance.GetLogs(0, LiveLogCache.Instance.GetCount())
+ .Where(log => string.IsNullOrEmpty(_filterText) ||
+ log.IndexOf(_filterText, StringComparison.OrdinalIgnoreCase) >= 0)
+ .ToList();
+
+ // Vymazanie existujúcich položiek a pridanie nových
+ _listView.Items.Clear();
+
+ foreach (var log in logs)
+ {
+ var item = new ListViewItem(log)
+ {
+ BackColor = _listView.Items.Count % 2 == 0 ? Color.White : Color.FromArgb(240, 240, 240)
+ };
+ _listView.Items.Add(item);
+ }
+
+ // Obnovenie označených položiek
+ foreach (var index in selectedIndices)
+ {
+ if (index < _listView.Items.Count)
+ {
+ _listView.Items[index].Selected = true;
+ }
+ }
+
+ // Ak bol používateľ na poslednom prvku, nastavíme focus a scroll na posledný prvok
+ if (_autoScrollEnabled && wasOnLastItem && _listView.Items.Count > 0)
+ {
+ var lastItemIndex = _listView.Items.Count - 1;
+ _listView.EnsureVisible(lastItemIndex);
+ _listView.Items[lastItemIndex].Focused = true;
+ }
+ else
+ {
+ // Ak používateľ nebol na spodku, vrátime sa na predchádzajúcu pozíciu scrollu
+ if (topIndexBeforeRefresh < _listView.Items.Count)
+ {
+ _listView.TopItem = _listView.Items[topIndexBeforeRefresh];
+ }
+ }
+ }
+ finally
+ {
+ _listView.EndUpdate();
+ }
+ }
+
+
+
+ public void ApplyFilter(string filterText)
+ {
+ _filterText = filterText?.Trim() ?? string.Empty;
+ RefreshListView();
+ }
+
+ private bool IsOnLastItem()
+ {
+ if (_listView.Items.Count == 0)
+ return false;
+
+ // Získame poslednú položku
+ int lastItemIndex = _listView.Items.Count - 1;
+ var lastItem = _listView.Items[lastItemIndex];
+
+ // Overíme, či je posledná položka úplne viditeľná
+ return lastItem.Bounds.Bottom <= _listView.ClientRectangle.Bottom;
+ }
+
+ public void EnableAutoScroll()
+ {
+ _autoScrollEnabled = true;
+ }
+
+ public void DisableAutoScroll()
+ {
+ _autoScrollEnabled = false;
+ }
+
+ private void AdjustColumnWidth()
+ {
+ if (_listView.Columns.Count > 0)
+ {
+ _listView.Columns[0].Width = _listView.ClientSize.Width;
+ }
+ }
+ }
+
+}
diff --git a/AppDiagnostic/LogFilter.cs b/AppDiagnostic/LogFilter.cs
new file mode 100644
index 000000000..6a8ec4f91
--- /dev/null
+++ b/AppDiagnostic/LogFilter.cs
@@ -0,0 +1,91 @@
+using SharedComponents;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace AppDiagnostic
+{
+ public class LogFilter
+ {
+ private ListView _listView;
+ private string _filterText = string.Empty; // Pre uloženie aktuálneho filtrovaného reťazca
+
+ // Konstruktor
+ public LogFilter(ListView listView)
+ {
+ _listView = listView;
+ }
+
+ // Metóda na aplikovanie filtra na ListView
+ public void ApplyFilter(string filterText)
+ {
+ _filterText = filterText.ToLower(); // Ukladáme text filtra bez ohľadu na veľkosť písmen
+
+ // Po aplikovaní filtra obnovíme zobrazenie
+ RefreshListView();
+ }
+
+ public void RefreshListView()
+ {
+ try
+ {
+ // Skontrolujeme, či sme na hlavnom vlákne a v prípade potreby použijeme Invoke
+ if (_listView.InvokeRequired)
+ {
+ // Použijeme Invoke, ak nie sme na hlavnom vlákne
+ _listView.Invoke(new Action(RefreshListView));
+ }
+ else
+ {
+ // Vymazanie existujúcich položiek
+ _listView.Items.Clear();
+
+ // Načítame všetky logy (ideálne z cache alebo databázy)
+ var logs = LiveLogCache.Instance.GetLogs(0, LiveLogCache.Instance.Logs.Count);
+
+ // Pridáme len tie logy, ktoré spĺňajú filter
+ foreach (var log in logs)
+ {
+ if (log.ToLower().Contains(_filterText)) // Kontrola, či log obsahuje filter text
+ {
+ var listViewItem = new ListViewItem(log);
+
+ // Striedanie farieb riadkov
+ if (_listView.Items.Count % 2 == 0) // Párny index => biela
+ {
+ listViewItem.BackColor = Color.White;
+ }
+ else // Nepárny index => svetlá sivá
+ {
+ listViewItem.BackColor = Color.FromArgb(240, 240, 240); // Veľmi svetlá sivá
+ }
+
+ // Pridanie efektu pre novú položku
+ ApplyNewItemEffect(listViewItem);
+
+ // Pridanie položky do ListView
+ _listView.Items.Add(listViewItem);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ // Zachytíme výnimku, ak sa niečo pokazí, a zobrazíme ju užívateľovi
+ MessageBox.Show($"An error occurred while refreshing the log view: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ // Môžete pridať ďalšie metódy na zobrazenie alebo efekt pre nové položky, ak je to potrebné
+ private void ApplyNewItemEffect(ListViewItem item)
+ {
+ // Prípadný efekt pre nový pridaný log
+ // Môžete pridať animáciu alebo zmenu farby atď.
+ item.ForeColor = Color.Green; // Napríklad zmeníme farbu písma na zelenú
+ }
+ }
+}
diff --git a/AppDiagnostic/MainForm.Designer.cs b/AppDiagnostic/MainForm.Designer.cs
new file mode 100644
index 000000000..1eef3958d
--- /dev/null
+++ b/AppDiagnostic/MainForm.Designer.cs
@@ -0,0 +1,278 @@
+using System.Windows.Forms;
+
+namespace AppDiagnostic
+{
+ partial class MainForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+ //private BufferedListView memoListView;
+ private System.Windows.Forms.ColumnHeader columnHeaderTime;
+ private System.Windows.Forms.ColumnHeader columnHeaderMessage;
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.columnHeaderTime = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.columnHeaderMessage = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
+ this.panel1 = new System.Windows.Forms.Panel();
+ this.refreshMemoButton = new System.Windows.Forms.Button();
+ this.button6 = new System.Windows.Forms.Button();
+ this.button7 = new System.Windows.Forms.Button();
+ this.filterManagementPanel = new System.Windows.Forms.Panel();
+ this.button5 = new System.Windows.Forms.Button();
+ this.filterButton = new System.Windows.Forms.Button();
+ this.filterTextBox = new System.Windows.Forms.TextBox();
+ this.label2 = new System.Windows.Forms.Label();
+ this.wndControlPanel = new System.Windows.Forms.Panel();
+ this.button2 = new System.Windows.Forms.Button();
+ this.flowControlsPanel = new System.Windows.Forms.Panel();
+ this.runButton = new System.Windows.Forms.Button();
+ this.stopButton = new System.Windows.Forms.Button();
+ this.label1 = new System.Windows.Forms.Label();
+ this.memoListView = new BufferedListView();
+ this.panel1.SuspendLayout();
+ this.filterManagementPanel.SuspendLayout();
+ this.wndControlPanel.SuspendLayout();
+ this.flowControlsPanel.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // columnHeaderTime
+ //
+ this.columnHeaderTime.Text = "Time";
+ this.columnHeaderTime.Width = 150;
+ //
+ // columnHeaderMessage
+ //
+ this.columnHeaderMessage.Text = "Message";
+ this.columnHeaderMessage.Width = 350;
+ //
+ // panel1
+ //
+ this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.panel1.Controls.Add(this.refreshMemoButton);
+ this.panel1.Controls.Add(this.button6);
+ this.panel1.Controls.Add(this.button7);
+ this.panel1.Location = new System.Drawing.Point(189, 356);
+ this.panel1.Name = "panel1";
+ this.panel1.Size = new System.Drawing.Size(283, 26);
+ this.panel1.TabIndex = 16;
+ //
+ // refreshMemoButton
+ //
+ this.refreshMemoButton.Location = new System.Drawing.Point(108, 0);
+ this.refreshMemoButton.Name = "refreshMemoButton";
+ this.refreshMemoButton.Size = new System.Drawing.Size(84, 26);
+ this.refreshMemoButton.TabIndex = 17;
+ this.refreshMemoButton.Text = "Refresh memo";
+ this.refreshMemoButton.UseVisualStyleBackColor = true;
+ this.refreshMemoButton.Click += new System.EventHandler(this.refreshMemoButton_Click);
+ //
+ // button6
+ //
+ this.button6.Location = new System.Drawing.Point(0, 0);
+ this.button6.Name = "button6";
+ this.button6.Size = new System.Drawing.Size(102, 26);
+ this.button6.TabIndex = 4;
+ this.button6.Text = "Clean diag cache";
+ this.button6.UseVisualStyleBackColor = true;
+ this.button6.Click += new System.EventHandler(this.button6_Click);
+ //
+ // button7
+ //
+ this.button7.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.button7.Location = new System.Drawing.Point(198, 0);
+ this.button7.Name = "button7";
+ this.button7.Size = new System.Drawing.Size(72, 26);
+ this.button7.TabIndex = 5;
+ this.button7.Text = "Save logs";
+ this.button7.UseVisualStyleBackColor = true;
+ this.button7.Click += new System.EventHandler(this.button7_Click);
+ //
+ // filterManagementPanel
+ //
+ this.filterManagementPanel.Controls.Add(this.button5);
+ this.filterManagementPanel.Controls.Add(this.filterButton);
+ this.filterManagementPanel.Controls.Add(this.filterTextBox);
+ this.filterManagementPanel.Controls.Add(this.label2);
+ this.filterManagementPanel.Location = new System.Drawing.Point(127, 6);
+ this.filterManagementPanel.Name = "filterManagementPanel";
+ this.filterManagementPanel.Size = new System.Drawing.Size(393, 26);
+ this.filterManagementPanel.TabIndex = 15;
+ //
+ // button5
+ //
+ this.button5.Enabled = false;
+ this.button5.Location = new System.Drawing.Point(294, 4);
+ this.button5.Name = "button5";
+ this.button5.Size = new System.Drawing.Size(81, 20);
+ this.button5.TabIndex = 11;
+ this.button5.Text = "Add new filter";
+ this.button5.UseVisualStyleBackColor = true;
+ //
+ // filterButton
+ //
+ this.filterButton.Location = new System.Drawing.Point(225, 4);
+ this.filterButton.Name = "filterButton";
+ this.filterButton.Size = new System.Drawing.Size(64, 20);
+ this.filterButton.TabIndex = 10;
+ this.filterButton.Text = "Set filter";
+ this.filterButton.UseVisualStyleBackColor = true;
+ this.filterButton.Click += new System.EventHandler(this.filterButton_Click_1);
+ //
+ // filterTextBox
+ //
+ this.filterTextBox.Location = new System.Drawing.Point(33, 4);
+ this.filterTextBox.Name = "filterTextBox";
+ this.filterTextBox.Size = new System.Drawing.Size(187, 20);
+ this.filterTextBox.TabIndex = 7;
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(3, 8);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(29, 13);
+ this.label2.TabIndex = 6;
+ this.label2.Text = "Filter";
+ //
+ // wndControlPanel
+ //
+ this.wndControlPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.wndControlPanel.Controls.Add(this.button2);
+ this.wndControlPanel.Location = new System.Drawing.Point(831, 356);
+ this.wndControlPanel.Name = "wndControlPanel";
+ this.wndControlPanel.Size = new System.Drawing.Size(70, 26);
+ this.wndControlPanel.TabIndex = 14;
+ //
+ // button2
+ //
+ this.button2.Location = new System.Drawing.Point(3, 0);
+ this.button2.Name = "button2";
+ this.button2.Size = new System.Drawing.Size(64, 26);
+ this.button2.TabIndex = 2;
+ this.button2.Text = "Close";
+ this.button2.UseVisualStyleBackColor = true;
+ this.button2.Click += new System.EventHandler(this.button2_Click);
+ //
+ // flowControlsPanel
+ //
+ this.flowControlsPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.flowControlsPanel.Controls.Add(this.runButton);
+ this.flowControlsPanel.Controls.Add(this.stopButton);
+ this.flowControlsPanel.Location = new System.Drawing.Point(12, 356);
+ this.flowControlsPanel.Name = "flowControlsPanel";
+ this.flowControlsPanel.Size = new System.Drawing.Size(133, 26);
+ this.flowControlsPanel.TabIndex = 13;
+ //
+ // runButton
+ //
+ this.runButton.Enabled = false;
+ this.runButton.Location = new System.Drawing.Point(0, 0);
+ this.runButton.Name = "runButton";
+ this.runButton.Size = new System.Drawing.Size(64, 26);
+ this.runButton.TabIndex = 4;
+ this.runButton.Text = "Run";
+ this.runButton.UseVisualStyleBackColor = true;
+ this.runButton.Click += new System.EventHandler(this.runButton_Click);
+ //
+ // stopButton
+ //
+ this.stopButton.Location = new System.Drawing.Point(69, 0);
+ this.stopButton.Name = "stopButton";
+ this.stopButton.Size = new System.Drawing.Size(64, 26);
+ this.stopButton.TabIndex = 5;
+ this.stopButton.Text = "Stop";
+ this.stopButton.UseVisualStyleBackColor = true;
+ this.stopButton.Click += new System.EventHandler(this.stopButton_Click);
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(12, 19);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(76, 13);
+ this.label1.TabIndex = 12;
+ this.label1.Text = "Flow of events";
+ //
+ // memoListView
+ //
+ this.memoListView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.memoListView.FullRowSelect = true;
+ this.memoListView.GridLines = true;
+ this.memoListView.HideSelection = false;
+ this.memoListView.Location = new System.Drawing.Point(12, 38);
+ this.memoListView.Name = "memoListView";
+ this.memoListView.Size = new System.Drawing.Size(889, 312);
+ this.memoListView.TabIndex = 0;
+ this.memoListView.UseCompatibleStateImageBehavior = false;
+ this.memoListView.View = System.Windows.Forms.View.Details;
+
+ this.memoListView.DoubleClick += new System.EventHandler(this.MemoListView_DoubleClick);
+ //
+ // MainForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(913, 388);
+ this.Controls.Add(this.memoListView);
+ this.Controls.Add(this.panel1);
+ this.Controls.Add(this.filterManagementPanel);
+ this.Controls.Add(this.wndControlPanel);
+ this.Controls.Add(this.flowControlsPanel);
+ this.Controls.Add(this.label1);
+ this.Name = "MainForm";
+ this.Text = "Application live diagnostic";
+ this.panel1.ResumeLayout(false);
+ this.filterManagementPanel.ResumeLayout(false);
+ this.filterManagementPanel.PerformLayout();
+ this.wndControlPanel.ResumeLayout(false);
+ this.flowControlsPanel.ResumeLayout(false);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Panel panel1;
+ private System.Windows.Forms.Button button6;
+ private System.Windows.Forms.Button button7;
+ private System.Windows.Forms.Panel filterManagementPanel;
+ private System.Windows.Forms.Button button5;
+ private System.Windows.Forms.Button filterButton;
+ private System.Windows.Forms.TextBox filterTextBox;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Panel wndControlPanel;
+ private System.Windows.Forms.Button button2;
+ private System.Windows.Forms.Panel flowControlsPanel;
+ private System.Windows.Forms.Button runButton;
+ private System.Windows.Forms.Button stopButton;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Button refreshMemoButton;
+ private BufferedListView memoListView;
+ }
+}
+
diff --git a/AppDiagnostic/MainForm.cs b/AppDiagnostic/MainForm.cs
new file mode 100644
index 000000000..ee4f6b817
--- /dev/null
+++ b/AppDiagnostic/MainForm.cs
@@ -0,0 +1,266 @@
+using log4net.Config;
+using log4net;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using log4net.Appender;
+using SharedComponents;
+using static System.Windows.Forms.VisualStyles.VisualStyleElement;
+using System.IO; // Pre File.WriteAllLines a prácu so súbormi
+using System.Linq;
+
+namespace AppDiagnostic
+{
+ public partial class MainForm : Form
+ {
+ private bool stopUpdate = false; // Premenná na zastavenie aktualizácie
+ private int lastDisplayedLogIndex = 0;
+ private bool autoScrollEnabled = true; // Automatický posun, predvolene povolený
+ private MainForm appDiagnosticForm;
+ private Timer refreshTimer;
+ private LogAdapter _logAdapter; // Pre LogAdapter
+ private LogFilter _logFilter; // LogFilter objekt pre filtráciu
+
+ public MainForm()
+ {
+ InitializeComponent();
+
+ // Inicializujeme LogAdapter a priradíme ho k memoListView
+ _logAdapter = new LogAdapter(memoListView);
+
+ RefreshLogView();
+
+ // Pripojenie na udalosť pri pridaní nového logu
+ // nakoľko sa memo refreshuje cyklicky, tak refresh na udalosť nepotrebujem
+ //LiveLogCache.Instance.LogAdded += OnLogAdded;
+
+ // Vytvoríme LogFilter objekt
+ //_logFilter = new LogFilter(memoListView);
+ }
+
+ private void OnLogAdded(string log)
+ {
+ try
+ {
+ RefreshLogView();
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Error in OnLogAdded: {ex.Message}");
+ throw;
+ }
+ }
+
+ private void RefreshLogView()
+ {
+ // Skontrolujte, či voláme z iného vlákna
+ if (InvokeRequired)
+ {
+ Invoke((Action)RefreshLogView);
+ return;
+ }
+
+ memoListView.BeginUpdate(); // Zabraňuje vizuálnym aktualizáciám počas vykresľovania
+
+ try
+ {
+ // Skontrolujte, či je užívateľ na poslednom viditeľnom zázname
+ if (memoListView.Items.Count > 0)
+ {
+ var lastVisibleIndex = memoListView.TopItem.Index + memoListView.ClientRectangle.Height / memoListView.Items[0].Bounds.Height;
+ autoScrollEnabled = lastVisibleIndex >= memoListView.Items.Count - 1;
+ }
+
+ // Získajte nové logy od posledného indexu
+ var logs = LiveLogCache.Instance.GetLogs(lastDisplayedLogIndex, LiveLogCache.Instance.Logs.Count - lastDisplayedLogIndex);
+
+ // Pridajte len nové logy
+ foreach (var log in logs)
+ {
+ memoListView.Items.Add(new ListViewItem(log));
+ }
+
+ // Aktualizujte posledný zobrazený index
+ lastDisplayedLogIndex = LiveLogCache.Instance.Logs.Count;
+
+ // Ak je autoScrollEnabled, posuňte sa na posledný záznam
+ if (autoScrollEnabled && memoListView.Items.Count > 0)
+ {
+ memoListView.EnsureVisible(memoListView.Items.Count - 1);
+ }
+ }
+ finally
+ {
+ memoListView.EndUpdate(); // Umožní vizuálne aktualizácie
+ }
+ }
+
+ // Táto metóda sa spustí pri scrollovaní v memoListView (tzn. ak sa používateľ dostane na spodok listu)
+ private void memoListView_Scroll(object sender, EventArgs e)
+ {
+ // Skontrolujte, či používateľ dosiahol spodok ListView
+ if (memoListView.Items.Count > 0 &&
+ memoListView.Items[memoListView.Items.Count - 1].Bounds.Bottom <= memoListView.ClientSize.Height)
+ {
+ // Ak áno, načítame ďalšie logy
+ RefreshLogView();
+ }
+ }
+
+ private void button6_Click(object sender, EventArgs e)
+ {
+ //LiveLogCache.Instance.AddLog("Nový log z hlavnej aplikácie");
+
+ LiveLogCache.Instance.ClearLogs();
+ ClearListView();
+ }
+
+ private void runButton_Click(object sender, EventArgs e)
+ {
+ if (stopUpdate) // Ak je časovač zastavený, spustíme ho
+ {
+ stopUpdate = false; // Zastav aktualizáciu
+ runButton.Enabled = false; // Zakážeme tlačidlo Start počas behu
+ stopButton.Enabled = true; // Povolenie tlačidla Stop
+ refreshTimer.Start();
+ }
+ }
+
+ private void stopButton_Click(object sender, EventArgs e)
+ {
+ if (!stopUpdate) // Ak časovač beží, môžeme ho zastaviť
+ {
+ stopUpdate = true; // Spusti aktualizáciu
+ runButton.Enabled = true; // Povolenie tlačidla Start
+ stopButton.Enabled = false; // Zakážeme tlačidlo Stop
+ refreshTimer.Stop();
+ }
+ }
+
+ private void ClearListView()
+ {
+ if (InvokeRequired)
+ {
+ Invoke(new Action(ClearListView));
+ return;
+ }
+
+ memoListView.Items.Clear();
+ }
+
+ private void refreshMemoButton_Click(object sender, EventArgs e)
+ {
+
+ }
+
+ private void button7_Click(object sender, EventArgs e)
+ {
+ SaveLogsToFile();
+ //LiveLogCache.Instance.AddLog("Toto je nový log.");
+ }
+
+ protected override void OnFormClosing(FormClosingEventArgs e)
+ {
+ // Odpojenie udalosti, keď sa okno zatvára, inak môže po opätovnom spustení okna a pridaní nového itemu do listu zhhodiť program
+ // nakoľko sa memo refreshuje cyklicky, tak refresh na udalosť nepotrebujem
+ //LiveLogCache.Instance.LogAdded -= OnLogAdded;
+
+ _logAdapter = null;
+
+ base.OnFormClosing(e);
+ }
+
+ private void filterButton_Click_1(object sender, EventArgs e)
+ {
+ // Aplikujeme filter na základe textu z filterTextBox
+ string filterText = filterTextBox.Text;
+ //_logFilter.ApplyFilter(filterText); // Aplikovanie filtra
+ _logAdapter.ApplyFilter(filterText);
+ }
+ private void SubForm_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ try
+ {
+ // Vaša logika pri uzatváraní formy
+ // this.Hide(); // Podforma sa len skryje
+ }
+ catch (Exception ex)
+ {
+ // Ošetrenie výnimky
+ MessageBox.Show("Chyba pri zatváraní pod-aplikácie: " + ex.Message);
+ }
+ }
+
+ private void SaveLogsToFile()
+ {
+ using (SaveFileDialog saveFileDialog = new SaveFileDialog())
+ {
+ saveFileDialog.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*";
+ saveFileDialog.Title = "Save TBF live logs";
+ saveFileDialog.DefaultExt = "txt";
+ saveFileDialog.FileName = "TBFLiveLogs.txt";
+
+ if (saveFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ try
+ {
+ // Načítanie všetkých logov z LiveLogCache
+ var logs = LiveLogCache.Instance.GetLogs(0, LiveLogCache.Instance.GetCount());
+
+ // Uloženie logov do vybraného súboru
+ File.WriteAllLines(saveFileDialog.FileName, logs);
+
+ MessageBox.Show("Logs saved successfully.", "Save TBF live logs", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"An error occurred while saving TBF live logs: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+ }
+ }
+
+ private void button2_Click(object sender, EventArgs e)
+ {
+ this.Close();
+ //this.Hide(); // Podforma sa len skryje
+ }
+
+ private void MemoListView_DoubleClick(object sender, EventArgs e)
+ {
+ if (memoListView.SelectedItems.Count > 0)
+ {
+ var selectedItem = memoListView.SelectedItems[0];
+ int selectedIndex = memoListView.Items.IndexOf(selectedItem);
+
+ if (selectedIndex >= 0)
+ {
+ // Zrušenie filtra
+ _logAdapter._filterText = string.Empty;
+
+ // Načítanie logov od indexu vybraného riadku
+ var logs = LiveLogCache.Instance.GetLogs(selectedIndex, LiveLogCache.Instance.GetCount() - selectedIndex);
+
+ // Obnovenie ListView s novými údajmi
+ memoListView.BeginUpdate();
+ memoListView.Items.Clear();
+ foreach (var log in logs)
+ {
+ var item = new ListViewItem(log)
+ {
+ BackColor = memoListView.Items.Count % 2 == 0 ? Color.White : Color.FromArgb(240, 240, 240)
+ };
+ memoListView.Items.Add(item);
+ }
+ memoListView.EndUpdate();
+ }
+ }
+ }
+ }
+}
diff --git a/AppDiagnostic/MainForm.resx b/AppDiagnostic/MainForm.resx
new file mode 100644
index 000000000..d58980a38
--- /dev/null
+++ b/AppDiagnostic/MainForm.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
\ No newline at end of file
diff --git a/AppDiagnostic/Program.cs b/AppDiagnostic/Program.cs
new file mode 100644
index 000000000..6aaa9c232
--- /dev/null
+++ b/AppDiagnostic/Program.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace AppDiagnostic
+{
+ internal static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new MainForm());
+ }
+ }
+}
diff --git a/AppDiagnostic/Properties/AssemblyInfo.cs b/AppDiagnostic/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..482abc00e
--- /dev/null
+++ b/AppDiagnostic/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("AppDiagnostic")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AppDiagnostic")]
+[assembly: AssemblyCopyright("Copyright © 2024")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("fa9abad1-7184-4295-ade8-d44f2e3de6b2")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/AppDiagnostic/Properties/Resources.Designer.cs b/AppDiagnostic/Properties/Resources.Designer.cs
new file mode 100644
index 000000000..5f4bc8a07
--- /dev/null
+++ b/AppDiagnostic/Properties/Resources.Designer.cs
@@ -0,0 +1,62 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace AppDiagnostic.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AppDiagnostic.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/AppDiagnostic/Properties/Resources.resx b/AppDiagnostic/Properties/Resources.resx
new file mode 100644
index 000000000..13b775f1d
--- /dev/null
+++ b/AppDiagnostic/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/AppDiagnostic/Properties/Settings.settings b/AppDiagnostic/Properties/Settings.settings
new file mode 100644
index 000000000..07e66beb1
--- /dev/null
+++ b/AppDiagnostic/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/AppDiagnostic/packages.config b/AppDiagnostic/packages.config
new file mode 100644
index 000000000..ffd035f93
--- /dev/null
+++ b/AppDiagnostic/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Config/Data.cs b/Config/Data.cs
new file mode 100644
index 000000000..88726d89d
--- /dev/null
+++ b/Config/Data.cs
@@ -0,0 +1,103 @@
+using System;
+
+//formulas.cs has been added cause iPerl from master-2 ...MF
+
+namespace Config
+{
+ public class Data
+ {
+ public const string AdminUsername = "admin";
+ public const string AdminPassword = "staratura";
+ public const string SQLiteDbFName = "SQLite.db";
+
+#if DN100 || BADGER_STREDNA_TRAT || BADGER_VELKA_TRAT || CEVAK_200
+ public const int WMsCount = 3;
+ public const int LineSize = 3;
+ public const int CompoundWMsCount = 1;
+ public const int HeatMetersCount = 0;
+ public const int MaxPartNr = 1;
+#elif MUNICH
+ public const int WMsCount = 3;
+ public const int LineSize = 3;
+ public const int CompoundWMsCount = 3;
+ public const int HeatMetersCount = 0;
+ public const int MaxPartNr = 3;
+#elif MALTA_WSD25
+ public const int WMsCount = 6;
+ public const int LineSize = 6;
+ public const int CompoundWMsCount = 0;
+ public const int HeatMetersCount = 0;
+ public const int MaxPartNr = 1;
+#elif BADGER_MALA_TRAT || BERLIN || FUZHOU_150 || FUZHOU_300 || GELSENWASSER || GENESIS || IZRAEL_200 || PETERSBURG_200 || SENTEC || SLM_150 || TORINO_50
+ public const int WMsCount = 6;
+ public const int LineSize = 6;
+ public const int CompoundWMsCount = 1;
+ public const int HeatMetersCount = 0;
+ public const int MaxPartNr = 1;
+#elif PUCHONG_200
+ public const int WMsCount = 6;
+ public const int LineSize = 6;
+ public const int CompoundWMsCount = 3;
+ public const int HeatMetersCount = 0;
+ public const int MaxPartNr = 3;
+#elif RUM_MOB
+ public const int WMsCount = 8;
+ public const int LineSize = 8;
+ public const int CompoundWMsCount = 1;
+ public const int HeatMetersCount = 0;
+ public const int MaxPartNr = WMsCount / LineSize;
+#elif TURA_SPECIAL
+ public const int WMsCount = 6;
+ public const int LineSize = 6;
+ public const int CompoundWMsCount = 1;
+ public const int HeatMetersCount = 0;
+ public const int MaxPartNr = WMsCount / LineSize;
+#elif DEWA_300 || FUZHOU_100 || ROMA_200 || LUXEMBURG_40
+ public const int WMsCount = 10;
+ public const int LineSize = 10;
+ public const int CompoundWMsCount = 1;
+ public const int HeatMetersCount = 0;
+ public const int MaxPartNr = WMsCount / LineSize;
+#elif DUBAJ_50
+ public const int WMsCount = 10;
+ public const int LineSize = 5;
+ public const int CompoundWMsCount = 0;
+ public const int HeatMetersCount = 0;
+ public const int MaxPartNr = WMsCount / LineSize;
+#elif SLM_END || WARSAW_END
+ public const int WMsCount = 10;
+ public const int LineSize = 5;
+ public const int CompoundWMsCount = 1;
+ public const int HeatMetersCount = 0;
+ public const int MaxPartNr = 4;
+#elif SLM_50
+ public const int WMsCount = 12;
+ public const int LineSize = 12;
+ public const int CompoundWMsCount = 1;
+ public const int HeatMetersCount = 6;
+ public const int MaxPartNr = WMsCount / LineSize;
+#elif ALZIR_25 || BAHRAIN_50 || CEVAK_40 || FEWA_50 || FILIPINY_50 || FUZHOU_50 || HONGKONG_50 || IZRAEL_25 || JUZNA_AFRIKA_50 || KEMPNO_50 || KRAKOW_50 || MILWAUKEE || MURES_40 || PETERSBURG_50 || TORUN_50 || WARSAW_50 || ZAMBIA || ZODINO
+ public const int WMsCount = 20;
+ public const int LineSize = 10;
+ public const int CompoundWMsCount = 1;
+ public const int HeatMetersCount = 0;
+ public const int MaxPartNr = WMsCount / LineSize;
+#elif TURA_IPERL || TURA_IPERL_NEW
+ public const int WMsCount = 40;
+ public const int LineSize = 20;
+ public const int CompoundWMsCount = 1;
+ public const int HeatMetersCount = 0;
+ public const int MaxPartNr = WMsCount / LineSize;
+#elif IZRAEL_50
+ public const int WMsCount = 40;
+ public const int LineSize = 10;
+ public const int CompoundWMsCount = 0;
+ public const int HeatMetersCount = 0;
+ public const int MaxPartNr = WMsCount / LineSize;
+#endif
+
+ public static double RealDensity = 0; /// true water density [kg/m3]
+ public static double AtTemperature = 0; /// measured at temperature [°C]
+ public static double Buoyancy = 0;
+ }
+}
diff --git a/Config/Formulas.cs b/Config/Formulas.cs
new file mode 100644
index 000000000..24b888476
--- /dev/null
+++ b/Config/Formulas.cs
@@ -0,0 +1,410 @@
+///
+/// Copyright (c) 2013-2018 Sensus Slovensko a.s.
+///
+using System;
+using System.Collections.Generic;
+using log4net;
+using Common;
+
+//formulas.cs has been added cause iPerl from master-2 ...MF
+
+namespace Config
+{
+ public static class Formulas
+ {
+ private static readonly ILog log = LogManager.GetLogger(typeof(Formulas));
+
+
+ ///
+ /// Wrappers
+ ///
+ public static double RealDensity() { return Data.RealDensity; }
+ public static double AtTemperature() { return Data.AtTemperature; }
+ public static double Buoyancy() { return Data.Buoyancy; }
+
+
+ /// Private tables with coeficients to calculate specific enthalpy
+ static readonly int[] Ii;
+ static readonly int[] Ji;
+ static readonly double[] ni;
+
+ /// Private table with coeficients to calculate temperature of a platinum thermometer
+ static readonly double[] Di;
+
+
+ ///
+ /// Constructor
+ ///
+ static Formulas()
+ {
+ ///
+ /// Initialize tables to calculate specific enthalpies
+ ///
+ Ii = new int[34] { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 8, 8, 21, 23, 29, 30, 31, 32 };
+ Ji = new int[34] { -2, -1, 0, 1, 2, 3, 4, 5, -9, -7, -1, 0, 1, 3, -3, 0, 1, 3, 17, -4, 0, 6, -5, -2, 10, -8, -11, -6, -29, -31, -38, -39, -40, -41 };
+ ni = new double[34] {
+ 0.14632971213167, /// 1
+ -0.84548187169114, /// 2
+ -0.37563603672040E1, /// 3
+ 0.33855169168385E1, /// 4
+ -0.95791963387872, /// 5
+ 0.15772038513228, /// 6
+ -0.16616417199501E-1, /// 7
+ 0.81214629983568E-3, /// 8
+ 0.28319080123804E-3, /// 9
+ -0.60706301565874E-3, /// 10
+ -0.18990068218419E-1, /// 11
+ -0.32529748770505E-1, /// 12
+ -0.21841717175414E-1, /// 13
+ -0.52838357969930E-4, /// 14
+ -0.47184321073267E-3, /// 15
+ -0.30001780793026E-3, /// 16
+ 0.47661393906987E-4, /// 17
+ -0.44141845330846E-5, /// 18
+ -0.72694996297594E-15, /// 19
+ -0.31679644845054E-4, /// 20
+ -0.28270797985312E-5, /// 21
+ -0.85205128120103E-9, /// 22
+ -0.22425281908000E-5, /// 23
+ -0.65171222895601E-6, /// 24
+ -0.14341729937924E-12, /// 25
+ -0.40516996860117E-6, /// 26
+ -0.12734301741641E-8, /// 27
+ -0.17424871230634E-9, /// 28
+ -0.68762131295531E-18, /// 29
+ 0.14478307828521E-19, /// 30
+ 0.26335781662795E-22, /// 31
+ -0.11947622640071E-22, /// 32
+ 0.18228094581404E-23, /// 33
+ -0.93537087292458E-25, /// 34
+ };
+
+ ///
+ /// Initialize a table to calculate temperature of a platinum thermometer from resistance
+ ///
+ Di = new double[]
+ {
+ 439.932854,
+ 472.418020,
+ 37.684494,
+ 7.472018,
+ 2.920828,
+ 0.005184,
+ -0.963864,
+ -0.188732,
+ 0.191203,
+ 0.049025,
+ };
+ }
+
+ ///
+ /// Calculate density of distilled water from temperature
+ ///
+ /// ITS-90 temperature in [°C]
+ /// Density in [kg/m3]
+ public static double DistilledWaterDensityFromTemp(double t)
+ {
+ if (t <= 40)
+ {
+ const double c0 = 999.839564;
+ const double c1 = 0.067998613;
+ const double c2 = -0.0091101468;
+ const double c3 = 0.00010058299;
+ const double c4 = -0.0000011275659;
+ const double c5 = 6.5985371e-09;
+
+ return ((((c5 * t + c4) * t + c3) * t + c2) * t + c1) * t + c0;
+ }
+ else
+ {
+ const double a0 = 9.9983952E2;
+ const double a1 = 1.6952577E1;
+ const double a2 = -7.9905127E-3;
+ const double a3 = -4.6241757E-5;
+ const double a4 = 1.0584601E-7;
+ const double a5 = -2.8103006E-10;
+ const double b = 1.6887236E-2;
+
+ return (((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t + a0) / (1.0 + b * t);
+ }
+ }
+
+ ///
+ /// Calculate density of distilled water from temperature (obsolete)
+ ///
+ /// IPTS-68 temperature in [°C]
+ /// Density in [kg/m3]
+ public static double DistilledWaterDensityFromTempIPTS68(double t)
+ {
+ const double a0 = 999.842594;
+ const double a1 = 0.06793952;
+ const double a2 = -0.009095290;
+ const double a3 = 0.0001001685;
+ const double a4 = -0.000001120083;
+ const double a5 = 6.536332e-09;
+
+ return ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t + a0;
+ }
+
+ ///
+ /// Calculate density by comparing calculated data and data from a certificate
+ ///
+ /// Density from a certificate in [kg/m3]
+ /// Temperature from a certificate in [°C]
+ /// Density correction in [kg/m3]
+ public static double DensityCorrection(double realDensity, double atTemperature)
+ {
+ /// Calculated data
+ double calculatedDensity = DistilledWaterDensityFromTemp(atTemperature);
+
+ return realDensity - calculatedDensity;
+ }
+
+ ///
+ /// Calculate corrected (real) water density from temperature
+ ///
+ /// Temperature in [°C]
+ /// Density in [kg/m3]
+ public static double WaterDensityFromTemp(double t)
+ {
+ return DistilledWaterDensityFromTemp(t) + DensityCorrection(RealDensity(), AtTemperature());
+ }
+
+ ///
+ /// Calculate corrected (real) water density from temperature
+ ///
+ /// Temperature in [°C]
+ /// Density in [kg/m3]
+ public static double WaterDensityFromTempPress(double temp, double pressure)
+ {
+ double x0 = 5.08821E-10;
+ double x1 = 1.2639418;
+ double x2 = 0.2660269;
+ double x3 = 0.3734838;
+ double x4 = 2.0205242;
+ double theta = temp / 100.0;
+ double B = x0 * (((x3 * theta + x2) * theta + x1) * theta + 1) / (1 + x4 * theta);
+
+ return WaterDensityFromTemp(temp) * (1 + B * Units.ConvertTo(Unit.Pa, pressure));
+ }
+
+
+ public static float AirDensityFromAmbientVales(float tempC, float pressureBar, float humiPct)
+ {
+ double pressurePa = 100000.0 * (double)pressureBar; /// [Pa]
+ double tempKelvin = 273.15 + (double)tempC;
+ double coef1 = 1.2811805 / 10000.0 * tempKelvin * tempKelvin
+ - 1.950987 / 100.0 * tempKelvin
+ + 34.04926034
+ - 6.353631 * 1000.0 / tempKelvin;
+ double coef3 = humiPct / 100.0 * System.Math.Exp(coef1) / pressurePa;
+ double airDensityKgm3 = 0.00348353 * pressurePa * (1.0 - 0.378 * coef3) / tempKelvin; /// kg/m3
+ return (float)airDensityKgm3;
+ }
+
+ ///
+ /// Convert 'pulses' to 'volume', prevent division by zero
+ ///
+ public static double VolumeFromPulses(int pulses, double pulsesPerLiter)
+ {
+ if (pulsesPerLiter <= double.Epsilon) return 0;
+ return Convert.ToDouble(pulses) / pulsesPerLiter;
+ }
+
+ ///
+ /// Calculate the error in % from 'measured' and 'true' volume, prevent division by zero
+ ///
+ public static double ErrorFromVolumes(double measuredVolume, double trueVolume)
+ {
+ if (-float.Epsilon <= trueVolume && trueVolume <= float.Epsilon)
+ {
+ if (-float.Epsilon <= measuredVolume && measuredVolume <= float.Epsilon)
+ {
+ log.WarnFormat("ErrorFromVolumes({0},{1}) returns {2}", measuredVolume, trueVolume, -100.0);
+ return -100.0;
+ }
+
+ log.WarnFormat("ErrorFromVolumes({0},{1}) returns {2}", measuredVolume, trueVolume, 99.0);
+ return 99.0;
+ }
+
+ double error = 100.0 * (measuredVolume - trueVolume) / trueVolume;
+ log.InfoFormat("ErrorFromVolumes({0},{1}) returns {2}", measuredVolume, trueVolume, error);
+ return error;
+ }
+
+
+ ///
+ /// Calculates corrected value from a list of corrections by interpolation.
+ /// It is assumed that values in the list 'corrections' are sorted.
+ ///
+ /// Raw uncorrected value
+ /// Sorted (value, correction) pairs
+ /// Corrected value
+ public static double CorrectedValue(double rawValue, IList corrections)
+ {
+ return rawValue + GetCorrection(rawValue, corrections);
+ }
+
+ ///
+ /// Get a correction from a list of corrections by interpolation.
+ /// It is assumed that values in the list 'corrections' are sorted.
+ ///
+ /// Raw uncorrected value
+ /// Sorted (value, correction) pairs
+ /// Corrected value
+ public static double GetCorrection(double rawValue, IList corrections)
+ {
+ if ((corrections == null) || (corrections.Count == 0)) return 0; /// No correction
+
+ if (rawValue < corrections[0].Measurement)
+ {
+ /// rawValue is below the lowest value in the correction table
+ return corrections[0].Correction;
+ }
+
+ int count = corrections.Count;
+ for (int i = 1; i < count; i++)
+ {
+ if (rawValue < corrections[i].Measurement)
+ {
+ double d1 = rawValue - corrections[i - 1].Measurement;
+ double d2 = corrections[i].Measurement - rawValue;
+
+ if (d1 + d2 <= float.Epsilon)
+ {
+ /// Neigboring values in the corection table are close to each other -> calculate the average
+ return (corrections[i - 1].Correction + corrections[i].Correction) / 2.0;
+ }
+ else
+ {
+ /// Interpolate the correction from neigboring values in the corection table
+ return (corrections[i - 1].Correction * d2 + corrections[i].Correction * d1) / (d1 + d2);
+ }
+ }
+ }
+
+ /// rawValue is above the highest value in the correction table
+ return corrections[count - 1].Correction;
+ }
+
+
+ ///
+ /// Converts a measurement error to a correction (used when preparing correction tables).
+ ///
+ /// Measured value (in arbitrary units)
+ /// Measurement error in %
+ /// Correction in the same units as the measured value
+ public static double CorrectionFromError(double measuredValue, double error)
+ {
+ double trueValue = measuredValue / (1 + error/100);
+ double correction = trueValue - measuredValue;
+ return correction;
+ }
+
+
+ ///
+ /// Calculates the heat coefficient for water
+ ///
+ /// Pressure [bar]
+ /// Inlet temperature [°C]
+ /// Outlet temperature [°C]
+ /// true = flow measured @inlet, false = flow measured @outlet
+ /// Heat coefficient for water [J/(m3 K)]
+ public static double HeatCoefficientWater(double pressure, double T_in, double T_out, bool flowMeasuredAtInlet)
+ {
+ if (T_in == T_out) return 0;
+
+ const double R = 461.526; /// [J kg^-1 K^-1]
+ const double p_star_Pa = 16.53E6; /// [Pa] (=16.53 MPa)
+ const double T_star = 1386.0; /// [K]
+
+ double T_in_K = Units.ConvertTo(Unit.K, T_in);
+ double T_out_K = Units.ConvertTo(Unit.K, T_out);
+ double tau_in = T_star / T_in_K;
+ double tau_out = T_star / T_out_K;
+ double pi = Units.ConvertTo(Unit.Pa, pressure) / p_star_Pa;
+
+ double h_in = tau_in * GammaTau(pi, tau_in) * R * T_in_K;
+ double h_out = tau_out * GammaTau(pi, tau_out) * R * T_out_K;
+
+ double ni = flowMeasuredAtInlet ? GammaPi(pi, tau_in) * R * T_in_K / p_star_Pa
+ : GammaPi(pi, tau_out) * R * T_out_K / p_star_Pa;
+
+ return (h_in - h_out) / (ni * (T_in - T_out));
+ }
+
+ ///
+ /// gamma(pi) see also STN EN 1434-1 Annex A (A.4)
+ ///
+ /// pi = p / p* where p* = 16.53 MPa
+ /// tau = T* / T where T* = 1386 K
+ /// gamma(pi)
+ static double GammaPi(double pi, double tau)
+ {
+ double result = 0;
+ for (int i = 0; i < 34; i++)
+ {
+ result -= ni[i] * Ii[i] * Math.Pow(7.1 - pi, Ii[i] - 1) * Math.Pow(tau - 1.222, Ji[i]);
+ }
+ return result;
+ }
+
+ ///
+ /// gamma(tau) see also STN EN 1434-1 Annex A (A.7)
+ ///
+ /// pi = p / p* where p* = 16.53 MPa
+ /// tau = T* / T where T* = 1386 K
+ /// gamma(tau)
+ static double GammaTau(double pi, double tau)
+ {
+ double result = 0;
+ for (int i = 0; i < 34; i++)
+ {
+ result += ni[i] * Math.Pow(7.1 - pi, Ii[i]) * Ji[i] * Math.Pow(tau - 1.222, Ji[i] - 1);
+ }
+ return result;
+ }
+
+ ///
+ /// Conversion of measured resistance of a platinum thermometer to temperature according to ITS-90
+ ///
+ /// Measured resistance in [°C]
+ /// Calibrated resistance in Ohm at 0.01°C
+ /// Calibrated ITS-90 coefficient a7
+ /// Calibrated ITS-90 coefficient b7
+ /// Calibrated ITS-90 coefficient c7
+ /// Temperature in [°C]
+ public static double PlatinumResistanceTM_ITS90_R2T(double R, double R001C, double a7, double b7, double c7)
+ {
+ double w = R / R001C; /// ratio
+ double r1 = w - 1.0;
+ double dw = r1 * (a7 + r1 * (b7 + r1 * c7)); /// = a7*r1 + b7*r1^2 + c7*r1^3
+ double wr = w - dw;
+ double x = (wr - 2.64) / 1.64;
+
+ double sum = 0;
+ for (int i = Di.Length - 1; i >= 0; i--)
+ {
+ sum = sum * x + Di[i];
+ }
+
+ return sum;
+ }
+
+ ///
+ /// Conversion of measured resistance of a platinum thermometer to temperature using Callendar-Van Dusen equations
+ ///
+ /// Measured resistance in [°C]
+ /// Calibrated resistance in Ohm at 0°C
+ /// Calibration coefficient a
+ /// Calibration coefficient b
+ /// Temperature in [°C]
+ public static double PlatinumResistanceTM_ITS27_R2T(double R, double R0, double A, double B)
+ {
+ if (R0 * R0 * A * A - 4 * R0 * B * (R0 - R) <= 0) return 0; /// Out of range
+
+ return (-(R0 * A) + Math.Sqrt(R0 * R0 * A * A - 4 * R0 * B * (R0 - R))) / (2 * R0 * B);
+ }
+ }
+}
diff --git a/SchematicDrawing/Pictures/Tank-L-hot.png b/SchematicDrawing/Pictures/Tank-L-hot.png
new file mode 100644
index 000000000..fddf1604b
Binary files /dev/null and b/SchematicDrawing/Pictures/Tank-L-hot.png differ
diff --git a/SchematicDrawing/Pictures/Tank-M-hot.png b/SchematicDrawing/Pictures/Tank-M-hot.png
new file mode 100644
index 000000000..40121259a
Binary files /dev/null and b/SchematicDrawing/Pictures/Tank-M-hot.png differ
diff --git a/SchematicDrawing/Pictures/Tank-S-hot.png b/SchematicDrawing/Pictures/Tank-S-hot.png
new file mode 100644
index 000000000..4e052e67c
Binary files /dev/null and b/SchematicDrawing/Pictures/Tank-S-hot.png differ
diff --git a/SchematicDrawing/Pictures/Tank-XL-hot.png b/SchematicDrawing/Pictures/Tank-XL-hot.png
new file mode 100644
index 000000000..34cb418d3
Binary files /dev/null and b/SchematicDrawing/Pictures/Tank-XL-hot.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-L-closed.png b/SchematicDrawing/Pictures/ValveSw-L-closed.png
new file mode 100644
index 000000000..052aed4ee
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-L-closed.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-L-open-dry.png b/SchematicDrawing/Pictures/ValveSw-L-open-dry.png
new file mode 100644
index 000000000..86d355d00
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-L-open-dry.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-L-open.png b/SchematicDrawing/Pictures/ValveSw-L-open.png
new file mode 100644
index 000000000..0bf26b0fd
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-L-open.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-L-vacuum.png b/SchematicDrawing/Pictures/ValveSw-L-vacuum.png
new file mode 100644
index 000000000..ffda1b101
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-L-vacuum.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-M-closed.png b/SchematicDrawing/Pictures/ValveSw-M-closed.png
new file mode 100644
index 000000000..297d65bde
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-M-closed.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-M-open-dry.png b/SchematicDrawing/Pictures/ValveSw-M-open-dry.png
new file mode 100644
index 000000000..532644076
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-M-open-dry.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-M-open.png b/SchematicDrawing/Pictures/ValveSw-M-open.png
new file mode 100644
index 000000000..58c10ba47
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-M-open.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-M-vacuum.png b/SchematicDrawing/Pictures/ValveSw-M-vacuum.png
new file mode 100644
index 000000000..c13dda053
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-M-vacuum.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-S-closed.png b/SchematicDrawing/Pictures/ValveSw-S-closed.png
new file mode 100644
index 000000000..7278a215a
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-S-closed.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-S-open-dry.png b/SchematicDrawing/Pictures/ValveSw-S-open-dry.png
new file mode 100644
index 000000000..d54ad96da
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-S-open-dry.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-S-open.png b/SchematicDrawing/Pictures/ValveSw-S-open.png
new file mode 100644
index 000000000..0f0dbd371
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-S-open.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-S-vacuum.png b/SchematicDrawing/Pictures/ValveSw-S-vacuum.png
new file mode 100644
index 000000000..5dbc7edee
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-S-vacuum.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-XL-closed.png b/SchematicDrawing/Pictures/ValveSw-XL-closed.png
new file mode 100644
index 000000000..bf00b396e
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-XL-closed.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-XL-open-dry.png b/SchematicDrawing/Pictures/ValveSw-XL-open-dry.png
new file mode 100644
index 000000000..653a59064
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-XL-open-dry.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-XL-open.png b/SchematicDrawing/Pictures/ValveSw-XL-open.png
new file mode 100644
index 000000000..937f352be
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-XL-open.png differ
diff --git a/SchematicDrawing/Pictures/ValveSw-XL-vacuum.png b/SchematicDrawing/Pictures/ValveSw-XL-vacuum.png
new file mode 100644
index 000000000..96674b408
Binary files /dev/null and b/SchematicDrawing/Pictures/ValveSw-XL-vacuum.png differ
diff --git a/SchematicDrawing/SchematicDrawing.csproj b/SchematicDrawing/SchematicDrawing.csproj
index 09adf733c..c438c5178 100644
--- a/SchematicDrawing/SchematicDrawing.csproj
+++ b/SchematicDrawing/SchematicDrawing.csproj
@@ -98,6 +98,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -191,6 +201,12 @@
+
+
+
+
+
+
diff --git a/SharedComponents/LiveLogCache.cs b/SharedComponents/LiveLogCache.cs
new file mode 100644
index 000000000..656974152
--- /dev/null
+++ b/SharedComponents/LiveLogCache.cs
@@ -0,0 +1,234 @@
+using System.IO.MemoryMappedFiles;
+using System.Text;
+
+namespace SharedComponents
+{
+ public class LiveLogCache
+ {
+ /*// Staticka instancia pre singleton
+ private static readonly LiveLogCache _instance = new LiveLogCache();
+
+ // Uzamykaci objekt pre bezpecny pristup z viacerych vlakien
+ private static readonly object _lock = new object();
+
+ // Zoznam na ukladanie logov
+ private readonly List _logs = new List();
+
+ // Udalost pre notifikaciu pri zmene logov
+ public event Action LogAdded;
+
+ // Sukromny konstruktor (singleton pattern)
+ private LiveLogCache() { }
+
+ // Metoda na ziskanie poctu logov
+ public int GetCount()
+ {
+ return _logs.Count;
+ }
+
+ // Staticka metoda na ziskanie instancie
+ public static LiveLogCache Instance
+ {
+ get
+ {
+ lock (_lock)
+ {
+ return _instance;
+ }
+ }
+ }
+
+ // Verejna vlastnost na ziskanie logov
+ public IReadOnlyList Logs
+ {
+ get
+ {
+ lock (_lock)
+ {
+ return _logs.AsReadOnly();
+ }
+ }
+ }
+
+ // Metoda na pridanie logu
+ public void AddLog(string log)
+ {
+ // Ziskanie aktualneho casu v pozadovanom formate
+ string timeStamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:");
+
+ // Vytvorenie noveho logu s casovou peciatkou na zaciatku
+ string logWithTimeStamp = $"{timeStamp} {log}";
+
+ // Pridanie logu do zoznamu
+ lock (_lock)
+ {
+ _logs.Add(logWithTimeStamp);
+ }
+
+ // Spustenie udalosti pre notifikaciu
+ LogAdded?.Invoke(logWithTimeStamp);
+ }
+
+ // Metoda na vycistenie logov
+ public void ClearLogs()
+ {
+ lock (_lock)
+ {
+ _logs.Clear();
+ }
+
+ // Volitelne: Spusti udalost, ak by sme chceli notifikovat aj o vymazani logov
+ LogAdded?.Invoke("Logs were cleared.");
+ }
+
+ // Ziskanie vsetkych logov
+ public List GetAllLogs()
+ {
+ return new List(_logs); // Vratime kopiu logov
+ }
+
+ // Ziskanie logov medzi urcitou poziciou (s upravenym indexovanim)
+ public List GetLogs(int startIndex, int count)
+ {
+ // Zabezpeci, ze indexy nebudu mimo rozsahu
+ return _logs.Skip(startIndex).Take(count).ToList();
+ }*/
+
+ // Zlepseny kod, ktory umoznuje pracu s MemoryMappedFile, namiesto len priestoru vo virtualnej pamati, navyse je tento subor velkostne obmedzeny na 1MB
+
+ // Staticka instancia pre singleton
+ private static readonly LiveLogCache _instance = new LiveLogCache(true);
+
+ // Uzamykaci objekt pre bezpecny pristup z viacerych vlakien
+ private static readonly object _lock = new object();
+
+ // Zoznam na ukladanie logov
+ private readonly List _logs = new List();
+
+ // Udalost pre notifikaciu pri zmene logov
+ public event Action LogAdded;
+
+ // Memory-mapped file
+ private MemoryMappedFile _mmf;
+ private MemoryMappedViewAccessor _accessor;
+ private const int MaxLogSize = 1024 * 1024; // 1MB
+
+ // Sukromny konstruktor (singleton pattern)
+ private LiveLogCache(bool itsLogsCreator)
+ {
+ if (itsLogsCreator == true)
+ {
+ _mmf = MemoryMappedFile.CreateOrOpen("LiveLogCacheMMF", MaxLogSize);
+ }
+ else
+ {
+ _mmf = MemoryMappedFile.OpenExisting("LiveLogCacheMMF");
+ }
+
+ _accessor = _mmf.CreateViewAccessor();
+ }
+
+ // Metoda na ziskanie poctu logov
+ public int GetCount()
+ {
+ return _logs.Count;
+ }
+
+ // Staticka metoda na ziskanie instancie
+ public static LiveLogCache Instance
+ {
+ get
+ {
+ lock (_lock)
+ {
+ return _instance;
+ }
+ }
+ }
+
+ // Verejna vlastnost na ziskanie logov
+ public IReadOnlyList Logs
+ {
+ get
+ {
+ lock (_lock)
+ {
+ return _logs.AsReadOnly();
+ }
+ }
+ }
+
+ // Metoda na pridanie logu
+ public void AddLog(string log)
+ {
+ // Ziskanie aktualneho casu v pozadovanom formate
+ string timeStamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:");
+
+ // Vytvorenie noveho logu s casovou peciatkou na zaciatku
+ string logWithTimeStamp = $"{timeStamp} {log}";
+
+ // Pridanie logu do zoznamu
+ lock (_lock)
+ {
+ _logs.Add(logWithTimeStamp);
+ WriteLogToMemoryMappedFile(logWithTimeStamp);
+ }
+
+ // Spustenie udalosti pre notifikaciu
+ LogAdded?.Invoke(logWithTimeStamp);
+ }
+
+ // Metoda na vycistenie logov
+ public void ClearLogs()
+ {
+ lock (_lock)
+ {
+ _logs.Clear();
+ ClearMemoryMappedFile();
+ }
+
+ // Volitelne: Spusti udalost, ak by sme chceli notifikovat aj o vymazani logov
+ LogAdded?.Invoke("Logs were cleared.");
+ }
+
+ // Ziskanie vsetkych logov
+ public List GetAllLogs()
+ {
+ return new List(_logs); // Vratime kopiu logov
+ }
+
+ // Ziskanie logov medzi urcitou poziciou (s upravenym indexovanim)
+ public List GetLogs(int startIndex, int count)
+ {
+ // Zabezpeci, ze indexy nebudu mimo rozsahu
+ return _logs.Skip(startIndex).Take(count).ToList();
+ }
+
+ // Metoda na naplnenie ListView
+ /*public void PopulateListView(ListView listView)
+ {
+ listView.Items.Clear();
+ lock (_lock)
+ {
+ foreach (var log in _logs)
+ {
+ listView.Items.Add(new ListView Item(log));
+ }
+ }
+ }*/
+
+ // Write log to memory-mapped file
+ private void WriteLogToMemoryMappedFile(string log)
+ {
+ byte[] logBytes = Encoding.UTF8.GetBytes(log + Environment.NewLine);
+ _accessor.WriteArray(0, logBytes, 0, logBytes.Length);
+ }
+
+ // Clear memory-mapped file
+ private void ClearMemoryMappedFile()
+ {
+ byte[] emptyBytes = new byte[MaxLogSize];
+ _accessor.WriteArray(0, emptyBytes, 0, emptyBytes.Length);
+ }
+ }
+}
diff --git a/SharedComponents/SharedComponents.csproj b/SharedComponents/SharedComponents.csproj
new file mode 100644
index 000000000..06e92ea43
--- /dev/null
+++ b/SharedComponents/SharedComponents.csproj
@@ -0,0 +1,10 @@
+
+
+
+ net472
+ 10.0
+ enable
+ enable
+
+
+
diff --git a/SharedComponents/obj/Debug/net472/SharedComponents.GeneratedMSBuildEditorConfig.editorconfig b/SharedComponents/obj/Debug/net472/SharedComponents.GeneratedMSBuildEditorConfig.editorconfig
new file mode 100644
index 000000000..a02128ff9
--- /dev/null
+++ b/SharedComponents/obj/Debug/net472/SharedComponents.GeneratedMSBuildEditorConfig.editorconfig
@@ -0,0 +1,5 @@
+is_global = true
+build_property.RootNamespace = SharedComponents
+build_property.ProjectDir = C:\Users\micha\git\tbf\SharedComponents\
+build_property.EnableComHosting =
+build_property.EnableGeneratedComInterfaceComImportInterop =
diff --git a/SharedComponents/obj/Debug/net472/SharedComponents.GlobalUsings.g.cs b/SharedComponents/obj/Debug/net472/SharedComponents.GlobalUsings.g.cs
new file mode 100644
index 000000000..c11666bec
--- /dev/null
+++ b/SharedComponents/obj/Debug/net472/SharedComponents.GlobalUsings.g.cs
@@ -0,0 +1,7 @@
+//
+global using global::System;
+global using global::System.Collections.Generic;
+global using global::System.IO;
+global using global::System.Linq;
+global using global::System.Threading;
+global using global::System.Threading.Tasks;
diff --git a/SharedComponents/obj/SharedComponents.csproj.nuget.g.props b/SharedComponents/obj/SharedComponents.csproj.nuget.g.props
new file mode 100644
index 000000000..c2b287e0d
--- /dev/null
+++ b/SharedComponents/obj/SharedComponents.csproj.nuget.g.props
@@ -0,0 +1,15 @@
+
+
+
+ True
+ NuGet
+ $(MSBuildThisFileDirectory)project.assets.json
+ $(UserProfile)\.nuget\packages\
+ C:\Users\micha\.nuget\packages\
+ PackageReference
+ 6.14.0
+
+
+
+
+
\ No newline at end of file
diff --git a/SharedComponents/obj/SharedComponents.csproj.nuget.g.targets b/SharedComponents/obj/SharedComponents.csproj.nuget.g.targets
new file mode 100644
index 000000000..babc2c6b6
--- /dev/null
+++ b/SharedComponents/obj/SharedComponents.csproj.nuget.g.targets
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/TBF/Tools/ByteFormatter.cs b/TBF/Tools/ByteFormatter.cs
new file mode 100644
index 000000000..0e1d3ff99
--- /dev/null
+++ b/TBF/Tools/ByteFormatter.cs
@@ -0,0 +1,62 @@
+///
+/// Copyright (c) 2013-2015 Sensus Metering Systems
+///
+using System;
+using System.Globalization;
+
+namespace TBF.Tools
+{
+ public class ByteFormatter : IFormatProvider, ICustomFormatter
+ {
+ public ByteFormatter()
+ {
+ }
+
+ public object GetFormat(Type formatType)
+ {
+ if (formatType == typeof(ICustomFormatter))
+ return this;
+ else
+ return null;
+ }
+
+ public string Format(string fmt, object arg, IFormatProvider formatProvider)
+ {
+ if (arg.GetType() != typeof(byte))
+ {
+ try
+ {
+ return HandleOtherFormats(fmt, arg);
+ }
+ catch (FormatException e)
+ {
+ throw new FormatException(String.Format("The format of '{0}' is invalid.", fmt), e);
+ }
+ }
+
+ ///
+ /// Convert one byte to string
+ ///
+ byte b = (byte)arg;
+ if ((32 <= b) && (b <= 127)) return "'" + ((char)b).ToString() + "'";
+ if ((char)b == '\r') return "'\\r'";
+ if ((char)b == '\n') return "'\\n'";
+ if ((char)b == '\t') return "'\\t'";
+ if (b == 17) return "XON";
+ if (b == 19) return "XOFF";
+ if (b == 26) return "Ctrl-Z";
+ return b.ToString();
+ }
+
+
+ private string HandleOtherFormats(string format, object arg)
+ {
+ if (arg is IFormattable)
+ return ((IFormattable)arg).ToString(format, CultureInfo.CurrentCulture);
+ else if (arg != null)
+ return arg.ToString();
+ else
+ return String.Empty;
+ }
+ }
+}
diff --git a/TBF/Tools/IniUtil.cs b/TBF/Tools/IniUtil.cs
new file mode 100644
index 000000000..a190050a9
--- /dev/null
+++ b/TBF/Tools/IniUtil.cs
@@ -0,0 +1,108 @@
+///
+/// Copyright (c) 2013-2015 Sensus Metering Systems
+///
+using System;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Collections.Specialized;
+
+namespace TBF.Tools
+{
+ ///
+ /// The class use the WinApi GetPrivateProfileSectionNames,
+ /// GetPrivateProfileSection, GetPrivateProfileString, WritePrivateProfileString
+ /// and present easy methods to work from a NET point of view.
+ /// Usage:
+ /// IniUtil ini = new IniUtil(@"C:\program files (x86)\myapp\myapp.ini");
+ /// string country = ini.GetValue("Carrier","Country","NoCountry");
+ ///
+ public class IniUtil
+ {
+ [DllImport("kernel32.dll")]
+ private static extern int GetPrivateProfileSectionNames(byte[] lpszReturnBuffer, int nSize, string lpFileName);
+ [DllImport("kernel32.dll")]
+ private static extern int GetPrivateProfileSection(string lpAppName, byte[] lpReturnedString, int nSize, string lpFileName);
+ [DllImport("kernel32.dll")]
+ private static extern int GetPrivateProfileString(string lpApplicationName, string lpKeyName, string lpDefault, byte[] lpReturnedString, int nSize, string lpFileName);
+ [DllImport("kernel32.dll")]
+ private static extern bool WritePrivateProfileString(string lpApplicationName, string lpKeyName, string lpString, string lpFileName);
+
+ private const int VALUE_BUFFER = 511;
+ private const int SECTION_BUFFER = (1024 * 16);
+ private string m_sIniFile;
+
+ ///
+ /// .ctor with INI file name
+ ///
+ /// Fullpath to the INI file
+ public IniUtil(string fileName)
+ {
+ m_sIniFile = fileName;
+ }
+
+ ///
+ /// Set the value for a specific key in a section
+ ///
+ /// Section containing the key to write to
+ /// Key to insert/update
+ /// Value for the key
+ /// True if OK
+ public bool SetValue(string section, string key, string keyvalue)
+ {
+ return WritePrivateProfileString(section, key, keyvalue, m_sIniFile);
+ }
+
+ ///
+ /// Gets the value of the specidied key in the specified section,
+ /// If the key doesn't exists returns the default value
+ ///
+ /// Section containing the key to read from
+ /// Required key
+ /// Value to return in case the key is missing
+ /// string value of the key or missing value
+ public string GetValue(string section, string key, string ifMissing)
+ {
+ byte[] by = new byte[VALUE_BUFFER];
+ int n = GetPrivateProfileString(section, key, ifMissing, by, VALUE_BUFFER, m_sIniFile);
+ string s = Encoding.ASCII.GetString(by);
+ return s.Substring(0, n);
+ }
+
+ ///
+ /// Returns the NameValueCollection for every key in the section
+ ///
+ /// Section name
+ /// NameValueCollection with nake=Key and value=value
+ public NameValueCollection GetSectionKeysvalues(string section)
+ {
+ NameValueCollection n = new NameValueCollection();
+ if(section.Length > 0)
+ {
+ byte[] by = new byte[SECTION_BUFFER];
+ int x = GetPrivateProfileSection(section, by, SECTION_BUFFER, m_sIniFile);
+ if(x > 0) x--;
+ string keysvalues = Encoding.ASCII.GetString(by, 0, x);
+ string[] temp = keysvalues.Split('\0');
+ foreach(string s in temp)
+ {
+ string[] t = s.Split('=');
+ n.Add(t[0], t[1]);
+ }
+ }
+ return n;
+ }
+
+ ///
+ /// Get the names of all sections in .INI
+ ///
+ /// string array with all the key names
+ public string[] GetSectionNames()
+ {
+ byte[] by = new byte[SECTION_BUFFER];
+ int x = GetPrivateProfileSectionNames(by, SECTION_BUFFER, m_sIniFile);
+ if(x > 0) x--;
+ string keys = Encoding.ASCII.GetString(by, 0, x);
+ return keys.Split('\0');
+ }
+ }
+}
diff --git a/TBF/Tools/LogChecker.cs b/TBF/Tools/LogChecker.cs
new file mode 100644
index 000000000..fa76f96e9
--- /dev/null
+++ b/TBF/Tools/LogChecker.cs
@@ -0,0 +1,50 @@
+using log4net.Appender;
+using log4net.Core;
+using log4net.Repository.Hierarchy;
+using log4net;
+using System;
+using System.Collections.Generic;
+
+namespace TBF.Tools
+{
+ public class LogChecker : IDisposable
+ {
+ readonly Logger _logger;
+ readonly Level _previousLevel;
+ readonly MemoryAppender _appender = new MemoryAppender();
+
+ public LogChecker(string logName, Level levelToCheck)
+ {
+ _logger = (Logger)LogManager.GetLogger(logName).Logger;
+ _logger.AddAppender(_appender);
+ _previousLevel = _logger.Level;
+ _logger.Level = levelToCheck;
+ }
+
+ public List Messages
+ {
+ get
+ {
+ return new List(_appender.GetEvents())
+ .ConvertAll(x => x.RenderedMessage);
+ }
+ }
+
+ public void Dispose()
+ {
+ _logger.Level = _previousLevel;
+ _logger.RemoveAppender(_appender);
+ }
+ }
+}
+
+// Example of use
+//
+// using (Tools.LogChecker logChecker = new Tools.LogChecker("RfidData", log4net.Core.Level.Debug))
+// {
+// Execute query using NHibernate
+// ....
+//
+// MessageBox.Show(String.Join(Environment.NewLine,logChecker.Messages));
+// }
+//
diff --git a/TBF/Tools/XmlTools.cs b/TBF/Tools/XmlTools.cs
new file mode 100644
index 000000000..5deff02a9
--- /dev/null
+++ b/TBF/Tools/XmlTools.cs
@@ -0,0 +1,30 @@
+///
+/// Copyright (c) 2013-2015 Sensus Metering Systems
+///
+using System.IO;
+using System.Xml.Serialization;
+
+namespace TBF.Tools
+{
+ public static class XmlTools
+ {
+ public static string ToXmlString(this T input)
+ {
+ using (var writer = new StringWriter())
+ {
+ input.ToXml(writer);
+ return writer.ToString();
+ }
+ }
+
+ public static void ToXml(this T objectToSerialize, Stream stream)
+ {
+ new XmlSerializer(typeof(T)).Serialize(stream, objectToSerialize);
+ }
+
+ public static void ToXml(this T objectToSerialize, StringWriter writer)
+ {
+ new XmlSerializer(typeof(T)).Serialize(writer, objectToSerialize);
+ }
+ }
+}
diff --git a/TBF/UI/Shared/SuggestComboBox.cs b/TBF/UI/Shared/SuggestComboBox.cs
new file mode 100644
index 000000000..0ece9c518
--- /dev/null
+++ b/TBF/UI/Shared/SuggestComboBox.cs
@@ -0,0 +1,240 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Windows.Forms;
+
+namespace TBF.UI.Shared
+{
+ public class SuggestComboBox : ComboBox
+ {
+ #region fields and properties
+
+ private readonly ListBox _suggLb = new ListBox { Visible = false, TabStop = false };
+ private readonly BindingList _suggBindingList = new BindingList();
+ private Expression>> _propertySelector;
+ private Func> _propertySelectorCompiled;
+ private Expression> _filterRule;
+ private Func _filterRuleCompiled;
+ private Expression> _suggestListOrderRule;
+ private Func _suggestListOrderRuleCompiled;
+
+ public int SuggestBoxHeight
+ {
+ get { return _suggLb.Height; }
+ set { if (value > 0) _suggLb.Height = value; }
+ }
+
+ ///
+ /// If the item-type of the ComboBox is not string,
+ /// you can set here which property should be used
+ ///
+ public Expression>> PropertySelector
+ {
+ get { return _propertySelector; }
+ set
+ {
+ if (value == null) return;
+ _propertySelector = value;
+ _propertySelectorCompiled = value.Compile();
+ }
+ }
+
+ ///
+ /// Lambda-Expression to determine the suggested items
+ /// (as Expression here because simple lamda (func) is not serializable)
+ /// default: case-insensitive contains search
+ /// 1st string: list item
+ /// 2nd string: typed text
+ ///
+ public Expression> FilterRule
+ {
+ get { return _filterRule; }
+ set
+ {
+ if (value == null) return;
+ _filterRule = value;
+ _filterRuleCompiled = item => value.Compile()(item, Text);
+ }
+ }
+
+ ///
+ /// Lambda-Expression to order the suggested items
+ /// (as Expression here because simple lamda (func) is not serializable)
+ /// default: alphabetic ordering
+ ///
+ public Expression> SuggestListOrderRule
+ {
+ get { return _suggestListOrderRule; }
+ set
+ {
+ if (value == null) return;
+ _suggestListOrderRule = value;
+ _suggestListOrderRuleCompiled = value.Compile();
+ }
+ }
+
+ #endregion
+
+ ///
+ /// ctor
+ ///
+ public SuggestComboBox()
+ {
+ // set the standard rules:
+ _filterRuleCompiled = s => s.ToLower().Contains(Text.Trim().ToLower());
+ _suggestListOrderRuleCompiled = s => s;
+ _propertySelectorCompiled = collection => collection.Cast();
+
+ _suggLb.DataSource = _suggBindingList;
+ _suggLb.Click += SuggLbOnClick;
+
+ ParentChanged += OnParentChanged;
+ }
+
+ ///
+ /// the magic happens here ;-)
+ ///
+ ///
+ protected override void OnTextChanged(EventArgs e)
+ {
+ base.OnTextChanged(e);
+
+ if (!Focused) return;
+
+ _suggBindingList.Clear();
+ _suggBindingList.RaiseListChangedEvents = false;
+ _propertySelectorCompiled(Items)
+ .Where(_filterRuleCompiled)
+ .OrderBy(_suggestListOrderRuleCompiled)
+ .ToList()
+ .ForEach(_suggBindingList.Add);
+ _suggBindingList.RaiseListChangedEvents = true;
+ _suggBindingList.ResetBindings();
+
+ _suggLb.Visible = _suggBindingList.Any();
+
+ if (_suggBindingList.Count == 1 &&
+ _suggBindingList.Single().Length == Text.Trim().Length)
+ {
+ Text = _suggBindingList.Single();
+ Select(0, Text.Length);
+ _suggLb.Visible = false;
+ }
+ }
+
+ #region size and position of suggest box
+
+ ///
+ /// suggest-ListBox is added to parent control
+ /// (in ctor parent isn't already assigned)
+ ///
+ ///
+ ///
+ private void OnParentChanged(object sender, EventArgs e)
+ {
+ Parent.Controls.Add(_suggLb);
+ Parent.Controls.SetChildIndex(_suggLb, 0);
+ _suggLb.Top = Top + Height - 3;
+ _suggLb.Left = Left + 3;
+ _suggLb.Width = Width - 20;
+ _suggLb.Font = new Font("Segoe UI", 9);
+ }
+
+ protected override void OnLocationChanged(EventArgs e)
+ {
+ base.OnLocationChanged(e);
+ _suggLb.Top = Top + Height + 0;
+ _suggLb.Left = Left + 3;
+ }
+
+ protected override void OnSizeChanged(EventArgs e)
+ {
+ base.OnSizeChanged(e);
+ _suggLb.Width = Width;
+ }
+
+ #endregion
+
+ #region visibility of suggest box
+
+ protected override void OnLostFocus(EventArgs e)
+ {
+ // _suggLb can only getting focused by clicking (because TabStop is off)
+ // --> click-eventhandler 'SuggLbOnClick' is called
+ if (!_suggLb.Focused)
+ HideSuggBox();
+ base.OnLostFocus(e);
+ }
+
+ private void SuggLbOnClick(object sender, EventArgs eventArgs)
+ {
+ Text = _suggLb.Text;
+ Focus();
+ }
+
+ private void HideSuggBox()
+ {
+ _suggLb.Visible = false;
+ }
+
+ protected override void OnDropDown(EventArgs e)
+ {
+ HideSuggBox();
+ base.OnDropDown(e);
+ }
+
+ #endregion
+
+ #region keystroke events
+
+ ///
+ /// if the suggest-ListBox is visible some keystrokes
+ /// should behave in a custom way
+ ///
+ ///
+ protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
+ {
+ if (!_suggLb.Visible)
+ {
+ base.OnPreviewKeyDown(e);
+ return;
+ }
+
+ switch (e.KeyCode)
+ {
+ case Keys.Down:
+ if (_suggLb.SelectedIndex < _suggBindingList.Count - 1)
+ _suggLb.SelectedIndex++;
+ return;
+ case Keys.Up:
+ if (_suggLb.SelectedIndex > 0)
+ _suggLb.SelectedIndex--;
+ return;
+ case Keys.Enter:
+ Text = _suggLb.Text;
+ Select(0, Text.Length);
+ _suggLb.Visible = false;
+ return;
+ case Keys.Escape:
+ HideSuggBox();
+ return;
+ }
+
+ base.OnPreviewKeyDown(e);
+ }
+
+ private static readonly Keys[] KeysToHandle = new[] { Keys.Down, Keys.Up, Keys.Enter, Keys.Escape };
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ // the keysstrokes of our interest should not be processed be base class:
+ if (_suggLb.Visible && KeysToHandle.Contains(keyData))
+ return true;
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ #endregion
+ }
+}