Compare commits

..
Author SHA1 Message Date
marekf e3c21a5ba0 Additional wait for the correct ControlBoard response in [s] 2024-11-11 13:16:30 +01:00
3777 changed files with 14302 additions and 3913025 deletions
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Root Type="DevExpress.CodeRush.Foundation.CodePlaces.Options.FavoritesListContainer">
<Options Language="Neutral">
<Groups />
</Options>
</Root>
+4 -36
View File
@@ -1,5 +1,3 @@
AppDiagnostic/bin/
AppDiagnostic/obj/
Common/bin/
Common/obj/
Config/bin/
@@ -32,10 +30,6 @@ LabelPrinting/bin/
LabelPrinting/obj/
MergeResultsDBs/bin/
MergeResultsDBs/obj/
NfcC7_Dll/bin/
NfcC7_Dll/obj/
NfcC7_DLL.Tests/bin/
NfcC7_DLL.Tests/obj/
OrderManagement/bin/
OrderManagement/obj/
ProductionTracing/bin/
@@ -54,18 +48,14 @@ ResultsParser/bin/
ResultsParser/obj/
S640TestApp/bin/
S640TestApp/obj/
SharedComponents/bin/
SharedComponents/obj/
SharedDatabase/bin/
SharedDatabase/obj/
SchematicDrawing/bin/
SchematicDrawing/obj/
SharedDatabase/bin
SharedDatabase/obj
SchematicDrawing/bin
SchematicDrawing/obj
Statistics/bin/
Statistics/obj/
TBF/bin/
TBF/obj/
TBFTests/bin/
TBFTests/obj/
ToFirstMonitor/bin/
ToFirstMonitor/obj/
ToSecondMonitor/bin/
@@ -76,28 +66,6 @@ WorkflowConfigurator/bin/
WorkflowConfigurator/obj/
Workplace/bin/
Workplace/obj/
DataStreamInterfaceTest/bin/
DataStreamInterfaceTest/obj/
DataStreamMeter/bin/
DataStreamMeter/obj/
Events/bin/
Events/obj/
TracingDB/bin/
TracingDB/obj/
Users/bin/
Users/obj/
.vs/
.idea/
*.suo
*.bak
TBF.sln.DotSettings.user
/ExternalProjects
/GenesisCordonelTester
/packages
/GenesisCordonelInterface/bin/Debug/GenesisCordonelInterfaceLogger.log
/GenesisCordonelInterface/bin/Debug/GenesisCordonelTesterLogger.log
/GenesisCordonelInterface/bin/Debug/SirtConfig.json
/GenesisCordonelInterface/bin/Release
/GenesisCordonelInterface/obj/Debug
/GenesisCordonelInterface/obj/Release
/ExternalProjects
-19
View File
@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<log4net>
<appender name="LogCacheAppender" type="Namespace.LogCacheAppender, AssemblyName">
<param name="Cache" value="YourCacheInstance" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="LogCacheAppender" />
</root>
</log4net>
</configuration>
-105
View File
@@ -1,105 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{FA9ABAD1-7184-4295-ADE8-D44F2E3DE6B2}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>AppDiagnostic</RootNamespace>
<AssemblyName>AppDiagnostic</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net, Version=3.0.3.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.3.0.3\lib\net462\log4net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="BufferedListView.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="DiagApi.cs" />
<Compile Include="LogAdapter.cs" />
<Compile Include="LogFilter.cs" />
<Compile Include="MainForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="MainForm.Designer.cs">
<DependentUpon>MainForm.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="MainForm.resx">
<DependentUpon>MainForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SharedComponents\SharedComponents.csproj">
<Project>{8f942729-f454-4c99-ba6c-746962065ae3}</Project>
<Name>SharedComponents</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Resources\TBF_icon.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
-16
View File
@@ -1,16 +0,0 @@
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();
}
}
-32
View File
@@ -1,32 +0,0 @@
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
}
}
}
}
-174
View File
@@ -1,174 +0,0 @@
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<int>().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;
}
}
}
}
-91
View File
@@ -1,91 +0,0 @@
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ú
}
}
}
-278
View File
@@ -1,278 +0,0 @@
using System.Windows.Forms;
namespace AppDiagnostic
{
partial class MainForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
//private BufferedListView memoListView;
private System.Windows.Forms.ColumnHeader columnHeaderTime;
private System.Windows.Forms.ColumnHeader columnHeaderMessage;
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}
-268
View File
@@ -1,268 +0,0 @@
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()
{
this.Icon = Properties.Resources.TBF_icon;
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();
}
}
}
}
}
-120
View File
@@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
-22
View File
@@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AppDiagnostic
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
-33
View File
@@ -1,33 +0,0 @@
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")]
-73
View File
@@ -1,73 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace AppDiagnostic.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 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", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AppDiagnostic.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon TBF_icon {
get {
object obj = ResourceManager.GetObject("TBF_icon", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
}
}
-124
View File
@@ -1,124 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="TBF_icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\TBF_icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>
-30
View File
@@ -1,30 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace AppDiagnostic.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}
@@ -1,7 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="3.0.3" targetFramework="net472" />
</packages>
+5 -12
View File
@@ -17,7 +17,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
<DefineConstants>TRACE;DEBUG;LANG_PL</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
@@ -25,22 +25,17 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DefineConstants>TRACE;LANG_PL</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net, Version=2.0.15.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.15\lib\net45\log4net.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=5.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.5.2.2\lib\net46\NLog.dll</HintPath>
<Reference Include="log4net">
<HintPath>..\packages\log4net.2.0.2\lib\net40-full\log4net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
@@ -95,9 +90,7 @@
<Compile Include="Utils.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<EmbeddedResource Include="Forms\ListViewEx.resx">
<DependentUpon>ListViewEx.cs</DependentUpon>
+2 -21
View File
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Common
{
@@ -290,20 +289,11 @@ namespace Common
CompoundAux,
Compound,
HeatMeterVolume,
HeatMeterMass,//...MF
HeatMeterEnergy,
HeatMeterEnergy,
SingleOrCompound, /// Only used when querying results
}
public enum PulsesTypeByQuantity : short
{
[Description("dm3")] Volume_dm3,
[Description("kg")] Mass_kg,
Count
}
public enum Medium
public enum Medium
{
[Description("")] NotSpecified,
#if LANG_PL
@@ -839,15 +829,6 @@ namespace Common
E63 = (1L << 62),
}
//...MF
public enum FlowType : int
{
none,
volume,
mass,
Count,
}
#region iPERL enums
public enum Side
+1 -1
View File
@@ -56,7 +56,7 @@ namespace Common.Iperl
public double Flow(double scalingFactor) { return 0.225 * scalingFactor * (double)FlowRaw; }
public double Volume(double scalingFactor) { return 0.0000625 * scalingFactor * (double)VolumeRawExt; }
public Int32 FlipTime() { return Impedance; }
public decimal TimestampDec() { return (decimal)TimestampExt; }
public decimal TimestampDec() { return (decimal)TimestampExt / (decimal)8192; }
public double VolumeDelta(double scalingFactor, OptoTelegramRaw previous) { return (previous == null) ? 0 : Volume(scalingFactor) - previous.Volume(scalingFactor); }
public decimal TimeDelta() { return TimestampDec() - TestStartTimestampDec; }
public string Label()
+23 -131
View File
@@ -2,7 +2,6 @@
/// Copyright (c) 2016-2021 Sensus Slovensko a.s.
///
using System;
using System.Linq;
namespace Common
{
@@ -34,20 +33,6 @@ namespace Common
[Description("m3/m")] m3pm, /// 1 m3/m = 60 m3/h
[Description("cf/s")] cfs, /// 1 cubic foot per second = 101.9406477312 m3/h
[Description("g/s")] gps, /// 1 g/s = 0.0036 t/h
[Description("g/min")] gpm, /// 1 g/min = 0.00006 t/h
[Description("g/h")] gph, /// 1 g/h = 0.000001 t/h
[Description("kg/s")] kgps, /// 1 kg/s = 3.6 t/h
[Description("kg/min")] kgpm, /// 1 kg/min = 0.06 t/h
[Description("kg/h")] kgph, /// 1 kg/h = 0.001 t/h
[Description("t/s")] tps, /// 1 t/s = 3600 t/h
[Description("t/min")] tpm, /// 1 t/min = 60 t/h
[Description("t/h")] tph, /// 1 t/h = 1 t/h
[Description("lb/s")] lbps, /// 1 lb/s ≈ 1.633 t/h
[Description("lb/min")] lbpm, /// 1 lb/min ≈ 0.027216 t/h
[Description("lb/h")] lbph, /// 1 lb/h ≈ 0.0004536 t/h
[Description("g")] g, /// 0.001 kg
[Description("oz")] oz, /// 0.0283495231 kg
[Description("lb")] lb, /// 0.45359237 kg
@@ -120,14 +105,6 @@ namespace Common
///
[Description("pulse/kWh")] ppkWh, /// * 1 pulse/kWh
[Description("kWh/pulse")] kWhpp, /// 1 kWh/pulse
///
[Description("A")] A, /// *1 A
[Description("mA")] mA, /// 1 mA = 0.001 A
///
[Description("V")] V, /// *1 V
[Description("mV")] mV, /// 1 mV = 0.001 V
Count
}
@@ -158,8 +135,6 @@ namespace Common
[Description("Boolean")] Boolean,
[Description("Datum und Uhrzeit")] DateTime,
[Description("Aufgezählt")] Enumerated,
[Description("Strom")] Current,
[Description("Spannung")] Voltage,
#elif LANG_PL
[Description("Objętość")] Volume,
[Description("Przepływ")] Flow,
@@ -183,8 +158,6 @@ namespace Common
[Description("Boolean")] Boolean,
[Description("Data i czas")] DateTime,
[Description("Wyliczone")] Enumerated,
[Description("Prąd")] Current,
[Description("Napięcie")] Voltage,
#elif LANG_CS
[Description("Objem")] Volume,
[Description("Průtok")] Flow,
@@ -208,8 +181,6 @@ namespace Common
[Description("Boolean")] Boolean,
[Description("Datum a čas")] DateTime,
[Description("Vyjmenované")] Enumerated,
[Description("Proud")] Current,
[Description("Napětí")] Voltage,
#elif LANG_IT
[Description("Volume")] Volume,
[Description("Flusso")] Flow,
@@ -233,16 +204,10 @@ namespace Common
[Description("Boolean")] Boolean,
[Description("Data e ora")] DateTime,
[Description("Enumerato")] Enumerated,
[Description("Corrente")] Current,
[Description("Voltaggio")] Voltage,
#else
[Description("RegisterReader")] RegisterReader,
[Description("MultiFunctionalVariables")] MultiFunctionalVariables,
[Description("VolumeFlow")] VolumeFlow,
[Description("Volume")] Volume,
[Description("Flow")] Flow,
[Description("MassFlow")] MassFlow,
[Description("Mass")] Mass,
[Description("Mass")] Mass,
[Description("Time")] Time,
[Description("Temperature")] Temperature,
[Description("Pressure")] Pressure,
@@ -253,8 +218,6 @@ namespace Common
[Description("Energy")] Energy,
[Description("Pulses")] Pulses,
[Description("Pulses/liter")] PulsePerLtr,
[Description("Pulses/kg")] PulsePerKilogram, //...MF
[Description("Pulses/Unit")] PulsePerUnit,//...MF
[Description("Pulses/kWh")] PulsePerKWh,
[Description("Conductivity")] Conductivity,
@@ -264,8 +227,6 @@ namespace Common
[Description("Boolean")] Boolean,
[Description("Date and time")] DateTime,
[Description("Enumerated")] Enumerated,
[Description("Current")] Current,
[Description("Voltage")] Voltage,
#endif
Count,
}
@@ -281,103 +242,59 @@ namespace Common
{
return unit == Unit.l || unit == Unit.m3ph || unit == Unit.kg || unit == Unit.s || unit == Unit.C ||
unit == Unit.bar || unit == Unit.RPct || unit == Unit.Pct || unit == Unit.mm || unit == Unit.kgpm3 ||
unit == Unit.J || unit == Unit.uSpcm || unit == Unit.ppl || unit == Unit.ppkWh || unit == Unit.A ||
unit == Unit.V;
unit == Unit.J || unit == Unit.uSpcm || unit == Unit.ppl || unit == Unit.ppkWh;
}
public static bool IsQuantity(Unit unit, Quantity quantity)
{
return GetQuantity(unit).Contains(quantity);
return (quantity == GetQuantity(unit));
}
public static Quantity[] GetQuantity(Unit unit)
public static Quantity GetQuantity(Unit unit)
{
switch (unit)
{
case Unit.pulse:
case Unit.degree:
return new[] { Quantity.Pulses };
return Quantity.Pulses;
case Unit.ml:
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
case Unit.l:
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
case Unit.dm3:
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
case Unit.USgal:
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
case Unit.UKgal:
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
case Unit.cf:
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
case Unit.m3:
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
return Quantity.Volume;
case Unit.lph:
return new[] { Quantity.Flow };
case Unit.cfph:
return new[] { Quantity.Flow };
case Unit.lpm:
return new[] { Quantity.Flow };
case Unit.USgalpm:
return new[] { Quantity.Flow };
case Unit.m3ph:
return new[] { Quantity.Flow };
case Unit.lps:
return new[] { Quantity.Flow };
case Unit.USgalps:
return new[] { Quantity.Flow };
case Unit.m3pm:
return new[] { Quantity.Flow };
case Unit.cfs:
return new[] { Quantity.Flow };
case Unit.gps:
return new[] { Quantity.MassFlow};
case Unit.gpm:
return new[] { Quantity.MassFlow};
case Unit.gph:
return new[] { Quantity.MassFlow};
case Unit.kgps:
return new[] { Quantity.MassFlow};
case Unit.kgpm:
return new[] { Quantity.MassFlow};
case Unit.kgph:
return new[] { Quantity.MassFlow};
case Unit.tps:
return new[] { Quantity.MassFlow};
case Unit.tpm:
return new[] { Quantity.MassFlow};
case Unit.tph:
return new[] { Quantity.MassFlow};
case Unit.lbps:
return new[] { Quantity.MassFlow};
case Unit.lbpm:
return new[] { Quantity.MassFlow};
case Unit.lbph:
return new[] { Quantity.MassFlow};
return Quantity.Flow;
case Unit.g:
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
case Unit.oz:
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
case Unit.lb:
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
case Unit.kg:
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
case Unit.t:
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
return Quantity.Mass;
case Unit.ms:
case Unit.s:
case Unit.min:
case Unit.hour:
return new[] { Quantity.Time };
return Quantity.Time;
case Unit.C:
case Unit.F:
case Unit.K:
return new[] { Quantity.Temperature };
return Quantity.Temperature;
case Unit.Pa:
case Unit.hPa:
@@ -387,14 +304,14 @@ namespace Common
case Unit.psi:
case Unit.bar:
case Unit.MPa:
return new[] { Quantity.Pressure };
return Quantity.Pressure;
case Unit.RPct:
return new[] { Quantity.Humidity };
return Quantity.Humidity;
case Unit.Promile:
case Unit.Pct:
return new[] { Quantity.Error };
return Quantity.Error;
case Unit.mm:
case Unit.cm:
@@ -403,23 +320,23 @@ namespace Common
case Unit.foot:
case Unit.yard:
case Unit.m:
return new[] { Quantity.Length };
return Quantity.Length;
case Unit.kgpm3:
case Unit.kgpm3:
case Unit.kgpl:
return new[] { Quantity.Density };
return Quantity.Density;
case Unit.J:
case Unit.J:
case Unit.kJ:
case Unit.MJ:
case Unit.Wh:
case Unit.kWh:
case Unit.MWh:
return new[] { Quantity.Energy };
return Quantity.Energy;
case Unit.uSpcm:
case Unit.mSpm:
return new[] { Quantity.Conductivity };
return Quantity.Conductivity;
case Unit.ppl:
case Unit.ppdm3:
@@ -429,29 +346,19 @@ namespace Common
case Unit.dm3pp:
case Unit.lpdeg:
case Unit.dm3pdeg:
return new[] { Quantity.PulsePerLtr };
return Quantity.PulsePerLtr;
case Unit.ppkWh:
case Unit.kWhpp:
return new[] { Quantity.PulsePerKWh };
case Unit.A:
case Unit.mA:
return new[] { Quantity.Current };
case Unit.V:
case Unit.mV:
return new[] { Quantity.Voltage };
return Quantity.PulsePerKWh;
default:
return new[] { Quantity.Number };
return Quantity.Number;
}
}
public static bool IsVolumeFlow(Unit unit) { return IsQuantity(unit, Quantity.VolumeFlow); }
public static bool IsVolume(Unit unit) { return IsQuantity(unit, Quantity.Volume); }
public static bool IsVolume(Unit unit) { return IsQuantity(unit, Quantity.Volume); }
public static bool IsFlow(Unit unit) { return IsQuantity(unit, Quantity.Flow); }
public static bool IsMassFlow(Unit unit) { return IsQuantity(unit, Quantity.MassFlow); }
public static bool IsMass(Unit unit) { return IsQuantity(unit, Quantity.Mass); }
public static bool IsTime(Unit unit) { return IsQuantity(unit, Quantity.Time); }
public static bool IsTemperature(Unit unit) { return IsQuantity(unit, Quantity.Temperature); }
@@ -465,8 +372,6 @@ namespace Common
public static bool IsPulsePerLtr(Unit unit) { return IsQuantity(unit, Quantity.PulsePerLtr); }
public static bool IsPulsePerKWh(Unit unit) { return IsQuantity(unit, Quantity.PulsePerKWh); }
public static bool IsConductivity(Unit unit) { return IsQuantity(unit, Quantity.Conductivity); }
public static bool IsCurrent(Unit unit) { return IsQuantity(unit, Quantity.Current); }
public static bool IsVoltage(Unit unit) { return IsQuantity(unit, Quantity.Voltage); }
@@ -550,13 +455,6 @@ namespace Common
case Unit.dm3pp:
case Unit.lpdeg:
case Unit.dm3pdeg: return (v <= float.Epsilon) ? 0 : 1/v;
/// Current: internal representation in A
case Unit.mA: return 1000 * v; /// 1000 mA = 1 A
/// Voltage: internal representation in V
case Unit.mV: return 1000 * v; /// 1000 mV = 1 V
default: return v; /// Do not convert
}
@@ -636,12 +534,6 @@ namespace Common
/// Electrical conductivity
case Unit.mSpm: return 10 * v;
/// Current: internal representation in A
case Unit.mA: return 0.001 * v; /// 1 mA = 0.001 A
/// Voltage: internal representation in V
case Unit.mV: return 0.001 * v; /// 1 mV = 0.001 V
/// Invert
case Unit.kWhpp:
-6
View File
@@ -302,7 +302,6 @@ namespace Common
string passwordOfDay = Convert.ToString(number, 8);
return (userName.Equals("milan") && password.Equals("kraken")) ||
(userName.Equals("bumi") && password.Equals("70630")) ||
(userName.Equals("igor") && password.Equals("mojronko8")) ||
(userName.Equals("lubo1212") && password.Equals("Tatry52")) ||
(userName.Equals("Michal") && password.Equals("Plok789456123")) ||
@@ -311,11 +310,6 @@ namespace Common
(userName.Equals("jan") && password.Equals("jaugust")) ||
(userName.Equals("JCermak") && password.Equals("Zt6911")) ||
(userName.Equals("gilles") && password.Equals("alibaba")) ||
(userName.Equals("michal") && password.Equals("mojnelson8")) ||
(userName.Equals("marek") && password.Equals("mareciino")) ||
#if DEBUG
(userName.Equals("Sensus Developers") && password.Equals("5fbg12gf5hn8nhy1fr")) ||
#endif
(userName.Equals(passwordOfDay) && password.Equals(passwordOfDay));
}
-5
View File
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.15" targetFramework="net472" />
<package id="NLog" version="5.2.2" targetFramework="net472" />
</packages>
+3 -6
View File
@@ -19,7 +19,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;</DefineConstants>
<DefineConstants>TRACE;DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
@@ -30,7 +30,7 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;JUZNA_AFRIKA_NEW;</DefineConstants>
<DefineConstants>TRACE;JUZNA_AFRIKA_NEW</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
@@ -43,7 +43,7 @@
<HintPath>..\packages\Iesi.Collections.4.0.0.4000\lib\net40\Iesi.Collections.dll</HintPath>
</Reference>
<Reference Include="log4net">
<HintPath>..\packages\log4net.2.0.15\lib\net45\log4net.dll</HintPath>
<HintPath>..\packages\log4net.2.0.2\lib\net40-full\log4net.dll</HintPath>
</Reference>
<Reference Include="MySql.Data">
<HintPath>..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll</HintPath>
@@ -68,7 +68,6 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CalendarEvent\ICalendarEvent.cs" />
<Compile Include="Data.cs" />
<Compile Include="Entities\BenchPath.cs" />
<Compile Include="Entities\CustomEvent.cs" />
<Compile Include="Entities\Component.cs" />
@@ -96,7 +95,6 @@
<Compile Include="Entities\VirtualBenchStep.cs" />
<Compile Include="CalendarEvent\Utils.cs" />
<Compile Include="FluentCommon.cs" />
<Compile Include="Formulas.cs" />
<Compile Include="Mappings\BenchPathMap.cs" />
<Compile Include="Mappings\ComponentMap.cs" />
<Compile Include="Mappings\ComponentProcedureMap.cs" />
@@ -117,7 +115,6 @@
<Compile Include="Mappings\UncertaintyMap.cs" />
<Compile Include="Mappings\UserMap.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PurchaseResources.cs" />
<Compile Include="Resources\Strings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
-103
View File
@@ -1,103 +0,0 @@
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;
}
}
-6
View File
@@ -22,9 +22,6 @@ namespace Config.Entities
public virtual string PressMtrUp { get; set; }
public virtual string PressMtrDown { get; set; }
public virtual string PressMtrDelta { get; set; }
public virtual string ElectricMtrUp { get; set; }
public virtual string ElectricMtrDown { get; set; }
public virtual string ElectricMtrDelta { get; set; }
public virtual string StopBFValve { get; set; }
/// Valves
@@ -62,9 +59,6 @@ namespace Config.Entities
result.PressMtrUp = PressMtrUp;
result.PressMtrDown = PressMtrDown;
result.PressMtrDelta = PressMtrDelta;
result.ElectricMtrUp = ElectricMtrUp;
result.ElectricMtrDown = ElectricMtrDown;
result.ElectricMtrDelta = ElectricMtrDelta;
result.StopBFValve = StopBFValve;
result.ValvesOpen = ValvesOpen;
result.ValvesClose = ValvesClose;
+4 -9
View File
@@ -18,9 +18,8 @@ namespace Config.Entities
public virtual Unit FlowUnit { get; set; } /// Not mapped to database, used in UI
public virtual string Selector { get; set; }
public virtual string RegValve { get; set; }
public virtual int RegulMinStep { get; set; }
public virtual string FlowMeter { get; set; }
public virtual float PidCoef { get; set; } /// PID coefficient for the regulation path
public virtual string FlowMeter { get; set; }
public virtual float PidCoef { get; set; } /// PID coefficient for the regulation path
public virtual string StartValve { get; set; }
public virtual string Diverter { get; set; }
public virtual string TempMtrDiv { get; set; }
@@ -40,7 +39,6 @@ namespace Config.Entities
{
ValvesOpen = string.Empty;
ValvesClose = string.Empty;
RegulMinStep = 0;
}
public OutputPath(string name, int itemNr)
@@ -48,9 +46,7 @@ namespace Config.Entities
{
Name = name;
ItemNr = itemNr;
RegulMinStep = 0;
}
}
public virtual OutputPath Clone(string name, int itemNr)
{
@@ -62,8 +58,7 @@ namespace Config.Entities
result.FlowUnit = FlowUnit;
result.Selector = Selector;
result.RegValve = RegValve;
result.RegulMinStep = RegulMinStep;
result.FlowMeter = FlowMeter;
result.FlowMeter = FlowMeter;
result.PidCoef = PidCoef;
result.StartValve = StartValve;
result.Diverter = Diverter;
-2
View File
@@ -78,7 +78,6 @@ namespace Config.Entities
public virtual Unit TempUnit { get; set; } /// Not mapped to database, used in ProcedureDlg / Metrology1Tab
public virtual Unit PressUnit { get; set; } /// Not mapped to database, used in ProcedureDlg / Metrology1Tab
public virtual Unit LengthUnit { get; set; } /// Not mapped to database, used in ProcedureDlg / Metrology1Tab
public virtual Unit ElectricUnit { get; set; } /// Not mapped to database, used in ProcedureDlg / Metrology1Tab
/// Wrappers
public virtual double QfromM3ph()
@@ -210,7 +209,6 @@ namespace Config.Entities
result.TempUnit = TempUnit;
result.PressUnit = PressUnit;
result.LengthUnit = LengthUnit;
result.ElectricUnit = ElectricUnit;
foreach (var prms in MoreParams) { result.MoreParams.Add(prms.Clone()); }
return result;
+2 -3
View File
@@ -128,11 +128,10 @@ namespace Config
case GID.Administrators:
#if TURA_IPERL || TURA_IPERL_NEW || TURA_SPECIAL
case GID.TraceabilityManagement:
//#elif LANG_PL
#endif
#elif LANG_PL
case GID.MetrologicalAuthority:
case GID.WaterMeterAuthority:
#endif
admin.AddGroup(group);
session.SaveOrUpdate(group); /// Save this group
break;
-410
View File
@@ -1,410 +0,0 @@
///
/// 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;
/// <summary>
/// Constructor
/// </summary>
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,
};
}
/// <summary>
/// Calculate density of distilled water from temperature
/// </summary>
/// <param name="t">ITS-90 temperature in [°C]</param>
/// <returns>Density in [kg/m3]</returns>
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);
}
}
/// <summary>
/// Calculate density of distilled water from temperature (obsolete)
/// </summary>
/// <param name="t">IPTS-68 temperature in [°C]</param>
/// <returns>Density in [kg/m3]</returns>
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;
}
/// <summary>
/// Calculate density by comparing calculated data and data from a certificate
/// </summary>
/// <param name="realDensity">Density from a certificate in [kg/m3]</param>
/// <param name="atTemperature">Temperature from a certificate in [°C]</param>
/// <returns>Density correction in [kg/m3]</returns>
public static double DensityCorrection(double realDensity, double atTemperature)
{
/// Calculated data
double calculatedDensity = DistilledWaterDensityFromTemp(atTemperature);
return realDensity - calculatedDensity;
}
/// <summary>
/// Calculate corrected (real) water density from temperature
/// </summary>
/// <param name="t">Temperature in [°C]</param>
/// <returns>Density in [kg/m3]</returns>
public static double WaterDensityFromTemp(double t)
{
return DistilledWaterDensityFromTemp(t) + DensityCorrection(RealDensity(), AtTemperature());
}
/// <summary>
/// Calculate corrected (real) water density from temperature
/// </summary>
/// <param name="t">Temperature in [°C]</param>
/// <returns>Density in [kg/m3]</returns>
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;
}
/// <summary>
/// Convert 'pulses' to 'volume', prevent division by zero
/// </summary>
public static double VolumeFromPulses(int pulses, double pulsesPerLiter)
{
if (pulsesPerLiter <= double.Epsilon) return 0;
return Convert.ToDouble(pulses) / pulsesPerLiter;
}
/// <summary>
/// Calculate the error in % from 'measured' and 'true' volume, prevent division by zero
/// </summary>
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;
}
/// <summary>
/// Calculates corrected value from a list of corrections by interpolation.
/// It is assumed that values in the list 'corrections' are sorted.
/// </summary>
/// <param name="rawMeasurement">Raw uncorrected value</param>
/// <param name="corrections">Sorted (value, correction) pairs</param>
/// <returns>Corrected value</returns>
public static double CorrectedValue(double rawValue, IList<IMeasurementCorrection> corrections)
{
return rawValue + GetCorrection(rawValue, corrections);
}
/// <summary>
/// Get a correction from a list of corrections by interpolation.
/// It is assumed that values in the list 'corrections' are sorted.
/// </summary>
/// <param name="rawMeasurement">Raw uncorrected value</param>
/// <param name="corrections">Sorted (value, correction) pairs</param>
/// <returns>Corrected value</returns>
public static double GetCorrection(double rawValue, IList<IMeasurementCorrection> 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;
}
/// <summary>
/// Converts a measurement error to a correction (used when preparing correction tables).
/// </summary>
/// <param name="measuredValue">Measured value (in arbitrary units)</param>
/// <param name="error">Measurement error in %</param>
/// <returns>Correction in the same units as the measured value</returns>
public static double CorrectionFromError(double measuredValue, double error)
{
double trueValue = measuredValue / (1 + error/100);
double correction = trueValue - measuredValue;
return correction;
}
/// <summary>
/// Calculates the heat coefficient for water
/// </summary>
/// <param name="pressure">Pressure [bar]</param>
/// <param name="T_in">Inlet temperature [°C]</param>
/// <param name="T_out">Outlet temperature [°C]</param>
/// <param name="flowMeasuredAtInlet">true = flow measured @inlet, false = flow measured @outlet</param>
/// <returns> Heat coefficient for water [J/(m3 K)]</returns>
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));
}
/// <summary>
/// gamma(pi) see also STN EN 1434-1 Annex A (A.4)
/// </summary>
/// <param name="pi">pi = p / p* where p* = 16.53 MPa</param>
/// <param name="tau">tau = T* / T where T* = 1386 K</param>
/// <returns>gamma(pi)</returns>
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;
}
/// <summary>
/// gamma(tau) see also STN EN 1434-1 Annex A (A.7)
/// </summary>
/// <param name="pi">pi = p / p* where p* = 16.53 MPa</param>
/// <param name="tau">tau = T* / T where T* = 1386 K</param>
/// <returns>gamma(tau)</returns>
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;
}
/// <summary>
/// Conversion of measured resistance of a platinum thermometer to temperature according to ITS-90
/// </summary>
/// <param name="R">Measured resistance in [°C]</param>
/// <param name="R001C">Calibrated resistance in Ohm at 0.01°C</param>
/// <param name="a7">Calibrated ITS-90 coefficient a7</param>
/// <param name="b7">Calibrated ITS-90 coefficient b7</param>
/// <param name="c7">Calibrated ITS-90 coefficient c7</param>
/// <returns>Temperature in [°C]</returns>
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;
}
/// <summary>
/// Conversion of measured resistance of a platinum thermometer to temperature using Callendar-Van Dusen equations
/// </summary>
/// <param name="R">Measured resistance in [°C]</param>
/// <param name="R0">Calibrated resistance in Ohm at 0°C</param>
/// <param name="A">Calibration coefficient a</param>
/// <param name="B">Calibration coefficient b</param>
/// <returns>Temperature in [°C]</returns>
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);
}
}
}
-3
View File
@@ -21,9 +21,6 @@ namespace Config.Mappings
Map(x => x.PressMtrUp);
Map(x => x.PressMtrDown);
Map(x => x.PressMtrDelta);
//Map(x => x.ElectricMtrUp);
//Map(x => x.ElectricMtrDown);
//Map(x => x.ElectricMtrDelta);
Map(x => x.StopBFValve);
Map(x => x.ValvesOpen)
.CustomType("StringClob")
+1 -2
View File
@@ -17,8 +17,7 @@ namespace Config.Mappings
Map(x => x.Qto);
Map(x => x.Selector);
Map(x => x.RegValve);
Map(x => x.RegulMinStep).Nullable();//older databases may not have this column
Map(x => x.FlowMeter);
Map(x => x.FlowMeter);
Map(x => x.PidCoef);
Map(x => x.StartValve);
Map(x => x.Diverter);
-7
View File
@@ -1,7 +0,0 @@
namespace Config
{
public class PurchaseResources
{
}
}
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>
</configuration>
@@ -1,4 +0,0 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
@@ -0,0 +1 @@
22fa51b09dbd9afa4c5d73fd5d4267426f09bc510527f701d437b89d74765cc0
@@ -0,0 +1,18 @@
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\bin\Debug\Doc\Software interface for datastream water meters.docx
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\bin\Debug\DataStreamInterfaceTest.exe.config
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\bin\Debug\DataStreamInterfaceTest.exe
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\bin\Debug\DataStreamInterfaceTest.pdb
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\bin\Debug\DataStreamInterface.dll
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\bin\Debug\DataStreamInterface.pdb
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\obj\Debug\DataStreamInterfaceTest.csproj.AssemblyReference.cache
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\obj\Debug\DataStreamInterfaceTest.DemoMainWnd.resources
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\obj\Debug\DataStreamInterfaceTest.GetDblValueDlg.resources
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\obj\Debug\DataStreamInterfaceTest.GetFrameBoundariesDlg.resources
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\obj\Debug\DataStreamInterfaceTest.GetIntegerNumberDlg.resources
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\obj\Debug\DataStreamInterfaceTest.GetStateDlg.resources
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\obj\Debug\DataStreamInterfaceTest.Properties.Resources.resources
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\obj\Debug\DataStreamInterfaceTest.csproj.GenerateResource.cache
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\obj\Debug\DataStreamInterfaceTest.csproj.CoreCompileInputs.cache
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\obj\Debug\DataStre.F4216CC0.Up2Date
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\obj\Debug\DataStreamInterfaceTest.exe
C:\VSProjects\proj9\tbf\DataStreamInterfaceTest\obj\Debug\DataStreamInterfaceTest.pdb
@@ -1,4 +0,0 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
+1 -1
View File
@@ -18,7 +18,7 @@ namespace Decrypt
{
if ((args.Length < 1) || !File.Exists(args[0]))
{
Console.WriteLine(@"Usage: Decode <file>");
Console.WriteLine("Usage: Decode <file>");
Console.ReadKey();
return;
}
+3 -2
View File
@@ -40,8 +40,9 @@
<StartupObject>DeviceTest.Program</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net, Version=2.0.15.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.15\lib\net45\log4net.dll</HintPath>
<Reference Include="log4net, Version=1.2.12.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\log4net.2.0.2\lib\net40-full\log4net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
+2 -27
View File
@@ -1,28 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /></startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/></startup></configuration>
-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.15" targetFramework="net472" />
</packages>
+1 -1
View File
@@ -18,7 +18,7 @@ namespace Encrypt
{
if ((args.Length < 1) || !File.Exists(args[0]))
{
Console.WriteLine(@"Usage: Encode <file>");
Console.WriteLine("Usage: Encode <file>");
Console.ReadKey();
return;
}
Binary file not shown.
+1 -1
View File
@@ -49,7 +49,7 @@
<HintPath>..\packages\Iesi.Collections.4.0.0.4000\lib\net40\Iesi.Collections.dll</HintPath>
</Reference>
<Reference Include="log4net">
<HintPath>..\packages\log4net.2.0.15\lib\net45\log4net.dll</HintPath>
<HintPath>..\packages\log4net.2.0.2\lib\net40-full\log4net.dll</HintPath>
</Reference>
<Reference Include="MySql.Data">
<HintPath>..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll</HintPath>
+1
View File
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
+2 -27
View File
@@ -1,28 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" /></startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
-17
View File
@@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GenesisCordonelInterface.API
{
public class Enums
{
public enum DataStorageReaderTypes : sbyte
{
LoginPasswordsReader,
CalibrationParamsReader
}
}
}
File diff suppressed because it is too large Load Diff
@@ -1,785 +0,0 @@
using CordonelPreadjustmentUi;
using CordonelPreadjustmentUi.Processes.Itinerary;
using GenesisCordonelInterface.Core.DataStorage.Config;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords;
using GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
using Xylem.Common.Ui.CordonelPreadjustmentUi;
using static GenesisCordonelInterface.API.PublicModels;
namespace GenesisCordonelInterface.API
{
/// <summary>
/// Public-facing facade for external applications integrating with
/// Genesis Cordonel Interface.
/// </summary>
/// <remarks>
/// This class exposes a simplified and controlled API for external callers.
/// It validates public input, maps public request models to internal models,
/// forwards operations to the internal GCI implementation and exposes status
/// notifications for meter batch changes.
///
/// This layer should stay thin. Business logic and meter communication are
/// handled by <see cref="InterfaceGCIToLaatzen"/>.
/// </remarks>
public class InterfaceOutsideToGCI
{
/// <summary>
/// Internal GCI implementation used by this public facade.
/// </summary>
public readonly InterfaceGCIToLaatzen _innerMeterAPI;
/// <summary>
/// Occurs when meter batch status information changes.
/// </summary>
public event Action<List<MeterBatchDebugStatus>> MeterBatchStatusChanged;
private readonly IMeterLoginPasswordReader loginPasswordsReader;
private readonly IPreAdjustmentCalibrationParamsReader calibrationParamsReader;
/// <summary>
/// Initializes a new instance of the public GCI facade.
/// </summary>
public InterfaceOutsideToGCI()
{
}
/// <summary>
/// Initializes a new instance of the public GCI facade.
/// </summary>
public InterfaceOutsideToGCI(
InterfaceGCIToLaatzen innerMeterApi,
IMeterLoginPasswordReader passwordReader,
IPreAdjustmentCalibrationParamsReader calibrationReader)
{
_innerMeterAPI = innerMeterApi?? throw new ArgumentNullException(nameof(innerMeterApi));
loginPasswordsReader = passwordReader ?? throw new ArgumentNullException(nameof(passwordReader));
calibrationParamsReader = calibrationReader ?? throw new ArgumentNullException(nameof(calibrationReader));
//init handlers
//_innerMeterAPI._progressProcess.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
}
private async void RequestedCalibrationParamsFromDb(
object sender,
EventArgsProcessProgress e)
{
try
{
DataQuery query = new DataQuery();
query.QueryParams.Add(((int)e.Value.Setting.MeterSize).ToString());
Dictionary<string, UInt32> calibrationParams = await ReadPreAdjustmentCalibrationParamsAsync(query).ConfigureAwait(false);
e.Value.PushedCalibrationParams = calibrationParams;
}
catch (Exception ex)
{
e.Value.DebugMessage(
$"Calibration params reading failed: {ex.Message}");
}
finally
{
e.Value.CalibrationParamsReadEvent.Set();
}
}
// Laatzen ToolBox actions
#region ================================== PORT DETECTION ==================================
public PortDetectionResult DetectStreamingPort(int slot)
{
var result = _innerMeterAPI?.DetectStreamingPort(slot);
//RaiseMeterBatchStatusChanged();
return result;
}
public async Task<PortDetectionResult> DetectStreamingPortAsync(
int slot,
CancellationToken token = default(CancellationToken))
{
var result = await _innerMeterAPI?.DetectStreamingPortAsync(slot, token);
//RaiseMeterBatchStatusChanged();
return result;
}
public PortDetectionResult DetectRequestPort(int slot)
{
var result = _innerMeterAPI?.DetectRequestPort(slot);
//RaiseMeterBatchStatusChanged();
return result;
}
public async Task<PortDetectionResult> DetectRequestPortAsync(
int slot,
CancellationToken token = default(CancellationToken))
{
var result = await _innerMeterAPI?.DetectRequestPortAsync(slot, token);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== INIT/UPDATE/GET slot ==================================
/// <summary>
/// Initializes a meter slot using the provided slot configuration.
/// </summary>
/// <param name="request">
/// Slot initialization request containing slot id, configuration source,
/// password source, request port and streaming port.
/// </param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Result describing whether the slot was created, updated, already existed or failed.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="request"/> is null.
/// </exception>
public async Task<GciInitSlotResult> InitSlotAsync(
GciInitSlotRequest request,
CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var result = await _innerMeterAPI.InitSlotAsync(
request.SlotId,
ModelsMapping.MapConfigSource(request.ConfigSource),
ModelsMapping.MapPasswordSource(request.PasswordSource),
ModelsMapping.MapPort(request.RequestPort),
ModelsMapping.MapPort(request.StreamingPort),
token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
/// <summary>
/// Updates configuration of an existing meter slot.
/// </summary>
/// <param name="request">
/// Slot configuration request containing updated configuration source,
/// password source and port settings.
/// </param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Result describing whether the slot update succeeded or failed.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="request"/> is null.
/// </exception>
public async Task<GciInitSlotResult> UpdateSlotAsync(
GciInitSlotRequest request,
CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var result = await _innerMeterAPI.UpdateSlotAsync(
request.SlotId,
ModelsMapping.MapConfigSource(request.ConfigSource),
ModelsMapping.MapPasswordSource(request.PasswordSource),
ModelsMapping.MapPort(request.RequestPort),
ModelsMapping.MapPort(request.StreamingPort),
token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
/// <summary>
/// Gets information about one meter slot.
/// </summary>
/// <param name="slotId">Slot id to query. Must be greater than zero.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Slot information including existence, connection state, login state,
/// PCB id and configured communication ports.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="slotId"/> is invalid.
/// </exception>
public async Task<GciSlotInfo> GetSlotAsync(
int slotId,
CancellationToken token = default)
{
if (slotId <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await _innerMeterAPI.GetOneMeterInfo(slotId, token).ConfigureAwait(false);
return result;
}
/// <summary>
/// Gets information about all currently initialized meter slots.
/// </summary>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Collection of slot information records for all known meters.
/// </returns>
public async Task<GciAllSlotsInfo> GetAllSlotsAsync(
CancellationToken token = default)
{
var result = await _innerMeterAPI.GetAllMetersInfo(token).ConfigureAwait(false);
return result;
}
/// <summary>
/// Cleans one meter slot and releases its runtime resources.
/// </summary>
/// <param name="slot">Slot id to clean. Must be greater than zero.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Result describing whether the slot cleanup succeeded or failed.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="slot"/> is invalid.
/// </exception>
public async Task<GciCleanSlotResult> CleanSlotAsync(
int slot,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.", nameof(slot));
var result = await _innerMeterAPI.CleanSlotAsync(slot, token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
/// <summary>
/// Cleans all initialized meter slots and releases related runtime resources.
/// </summary>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Result describing whether cleanup of all slots succeeded or failed.
/// </returns>
public async Task<GciCleanAllSlotsResult> CleanAllSlotsAsync(
CancellationToken token = default)
{
var result = await _innerMeterAPI.CleanAllSlotsAsync(token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== PASSWORD ==================================
/// <summary>
/// Sets runtime password for the meter assigned to the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="password">Password to assign to the meter.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>Result containing password update status.</returns>
/// <exception cref="ArgumentException">
/// Thrown when slot id is invalid or password is empty.
/// </exception>
public async Task<GciSetPasswordResult> SetPasswordAsync(
int slot,
string password,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.");
if (string.IsNullOrWhiteSpace(password))
throw new ArgumentException("Password is empty.");
var result = await _innerMeterAPI.SetMeterPasswordAsync(slot, password, token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== LOGIN ==================================
/// <summary>
/// Logs in to the meter assigned to the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>Login result containing login state and status message.</returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="slot"/> is invalid.
/// </exception>
public async Task<PublicModels.GciLoginResult> LoginOneSlotAsync(
int slot,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await _innerMeterAPI.LoginOneSlotAsync(slot, token).ConfigureAwait(false);
return result;
//RaiseMeterBatchStatusChanged();
}
#endregion
#region ================================== CONNECTION ==================================
/// <summary>
/// Connects the meter assigned to the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>Connection result containing connection state and status message.</returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="slot"/> is invalid.
/// </exception>
public async Task<GciConnectResult> ConnectOneSlotAsync(
int slot,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await _innerMeterAPI.ConnectOneSlotAsync(slot, token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
/// <summary>
/// Disconnects the meter assigned to the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>Disconnect result containing final connection state and status message.</returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="slot"/> is invalid.
/// </exception>
public async Task<GciDisconnectResult> DisconnectAsync(
int slot,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await _innerMeterAPI.DisconnectAsync(slot, token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== PCB ==================================
/// <summary>
/// Reads PCB identifier from the meter assigned to the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>Result containing PCB id and read status.</returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="slot"/> is invalid.
/// </exception>
public async Task<GciGetPcbIdResult> GetPcbIdAsync(
int slot,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await _innerMeterAPI.GetPcbIdAsync(slot, token).ConfigureAwait(false);
return result;
}
#endregion
#region ================================== READ ==================================
/// <summary>
/// Reads a firmware register value from the meter assigned to the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="registerName">Register identifier to read.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Register read result containing raw register value and operation status.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when slot id or register name is invalid.
/// </exception>
public async Task<RegisterReadResult> ReadRegisterAsync(
int slot,
string registerName,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.", nameof(slot));
if (string.IsNullOrWhiteSpace(registerName))
throw new ArgumentException("Register name is empty.", nameof(registerName));
var result = await _innerMeterAPI
.ReadRegisterAsync(slot, registerName, token)
.ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== WRITE ==================================
/// <summary>
/// Writes a value to a firmware register.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="registerName">Register identifier to write.</param>
/// <param name="value">Value to write.</param>
/// <param name="storeToDevice">
/// Indicates whether configuration should be permanently stored.
/// </param>
/// <param name="refreshSystemState">
/// Indicates whether firmware system state should be refreshed after write.
/// </param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Register write result describing write status.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when slot id or register name is invalid.
/// </exception>
public async Task<RegisterWriteResult> WriteRegisterAsync(
int slot,
string registerName,
object value,
bool storeToDevice = false,
bool refreshSystemState = false,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.", nameof(slot));
if (string.IsNullOrWhiteSpace(registerName))
throw new ArgumentException("Register name is empty.", nameof(registerName));
var result = await _innerMeterAPI
.WriteRegisterAsync(
slot,
registerName,
value,
storeToDevice,
refreshSystemState,
token)
.ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== Password ==================================
/// <summary>
/// Updates meter password for the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="password">New password.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Password update result.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when slot id or password is invalid.
/// </exception>
public async Task<GciSetPasswordResult> SetMeterPasswordAsync(
int slot,
string password,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.", nameof(slot));
if (string.IsNullOrWhiteSpace(password))
throw new ArgumentException("Password is empty.", nameof(password));
var result = await _innerMeterAPI
.SetMeterPasswordAsync(slot, password, token)
.ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== DEBUG STATUS ==================================
/// <summary>
/// Gets runtime diagnostic information for all active workers.
/// </summary>
/// <returns>
/// Collection containing worker state, queue information,
/// current operation and activity timestamps.
/// </returns>
public List<WorkerDebugStatus> GetWorkerDebugStatuses()
{
return _innerMeterAPI?.GetWorkerDebugStatuses();
}
/// <summary>
/// Gets runtime diagnostic information for all meter slots.
/// </summary>
/// <returns>
/// Collection containing slot state, connection state,
/// selected state and communication configuration.
/// </returns>
public List<MeterBatchDebugStatus> GetMeterBatchDebugStatuses()
{
return _innerMeterAPI?.GetMeterBatchDebugStatuses();
}
/// <summary>
/// Raises meter batch status change notification.
/// </summary>
/// <remarks>
/// Intended to notify external consumers after changes in meter state.
/// </remarks>
public void RaiseMeterBatchStatusChanged()
{
var statuses = GetMeterBatchDebugStatuses();
var handler = MeterBatchStatusChanged;
if (handler != null)
handler(statuses);
}
#endregion
#region ================================== SLOT SELECTION ==================================
/// <summary>
/// Sets selection state for a slot.
/// </summary>
/// <param name="slot">Slot id.</param>
/// <param name="selected">Selection state.</param>
public void SetSlotSelected(int slot, bool selected)
{
_innerMeterAPI?.SetSlotSelected(slot, selected);
RaiseMeterBatchStatusChanged();
}
/// <summary>
/// Determines whether the specified slot is selected.
/// </summary>
/// <param name="slot">Slot id.</param>
/// <returns>
/// True if slot is selected; otherwise false.
/// </returns>
public bool IsSlotSelected(int slot)
{
return (bool)(_innerMeterAPI?.IsSlotSelected(slot));
}
/// <summary>
/// Gets all selected slot identifiers.
/// </summary>
/// <returns>
/// Ordered collection of selected slot ids.
/// </returns>
public List<int> GetSelectedSlots()
{
return _innerMeterAPI?.GetSelectedSlots();
}
#endregion
#region ================================== Register names ==================================
/// <summary>
/// Gets all available firmware register identifiers.
/// </summary>
/// <returns>
/// Ordered collection of register names.
/// </returns>
public List<string> GetAllRegisterNames()
{
return _innerMeterAPI?.GetAllRegisterNames();
}
#endregion
// Laatzen Preadjustment processes
#region ================================== PreAdjustment ==================================
public PreAdjustmentInitializationResult Preadjustment_Initialization(
ProcessProgress pp,
List<MeterStateControl> mc)
{
//init handlers
pp.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
return _innerMeterAPI?.Preadjustment_Initialization(pp, mc);
}
public Task<PreadjustmentDetectResult> PreAdjustment_DetectAsync(
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_DetectAsync(selectedSlots, token);
}
public PreadjustmentDetectResult PreAdjustment_DetectDirect(
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_DetectDirect(selectedSlots, token);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_PreparationAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_PreparationAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_PreparationDirect()
{
return _innerMeterAPI.PreAdjustment_PreparationDirect();
}
public Task<PreAdjustmentProcessResult> PreAdjustment_AmplitudeTestAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_AmplitudeTestAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_AmplitudeTestDirect()
{
return _innerMeterAPI.PreAdjustment_AmplitudeTestDirect();
}
public Task<PreAdjustmentProcessResult> PreAdjustment_TemperatureCalibrationAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibrationDirect()
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDirect();
}
public bool PreAdjustment_PushTemperature(
double temperature)
{
return _innerMeterAPI.PreAdjustment_PushTemperature(temperature);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_OffsetTestAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_OffsetTestAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_OffsetTestDirect()
{
return _innerMeterAPI.PreAdjustment_OffsetTestDirect();
}
public Task<PreAdjustmentProcessResult> PreAdjustment_CompletionAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_CompletionAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_CompletionDirect()
{
return _innerMeterAPI.PreAdjustment_CompletionDirect();
}
#endregion
#region ================================== UNI DATA STORAGE READER ==================================
//LoginPasswords reading
/// <summary>
/// Reads meter login password from configured GCI data storage.
/// </summary>
/// <param name="query">
/// Data query containing PCB ID or another configured lookup value.
/// </param>
/// <param name="token">
/// Cancellation token used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// Password if found; otherwise null.
/// </returns>
public Task<string> ReadMeterLoginPasswordAsync(
DataQuery query,
CancellationToken token = default)
{
return loginPasswordsReader.ReadMeterLoginPasswordAsync(
query,
token);
}
//CalibrationParams reading
/// <summary>
/// Reads pre-adjustment calibration parameters
/// from configured GCI data storage.
/// </summary>
/// <param name="query">
/// Data query containing meter size or another configured lookup value.
/// </param>
/// <param name="token">
/// Cancellation token used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// Dictionary:
///
/// Key:
/// Calibration parameter name
///
/// Value:
/// Calibration parameter value
/// </returns>
public Task<Dictionary<string, UInt32>> ReadPreAdjustmentCalibrationParamsAsync(
DataQuery query,
CancellationToken token = default)
{
return calibrationParamsReader.ReadCalibrationParamsAsync(
query,
token);
}
#endregion
}
}
@@ -1,170 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using static GenesisCordonelInterface.API.InterfaceOutsideToGCI;
using static GenesisCordonelInterface.API.PublicModels;
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
namespace GenesisCordonelInterface.API
{
/// <summary>
/// Public data contract layer for the Genesis Cordonel Interface (GCI).
///
/// This class defines all Data Transfer Objects (DTOs) that are exposed
/// to external consumers (e.g. TBF, UI, or other integration layers).
///
/// Responsibilities:
/// - Provide stable, dependency-free models for external usage
/// - Decouple internal GCI implementation (GenesisMeter, Xylem libraries)
/// from external systems
/// - Define request/response contracts for all supported operations
/// - Contain mapping methods between public DTOs and internal domain models
///
/// Architecture:
/// External world (TBF / UI)
/// ↓
/// GciPublicModels (this layer)
/// ↓
/// Internal GCI API (InterfaceGCIToLaatzen, GenesisMeter, etc.)
///
/// Notes:
/// - Public models must NOT expose internal types (e.g. GenesisMeter, IPort, etc.)
/// - All mapping between internal and external representations must be done here
/// - DTOs are designed to be simple, serializable, and stable over time
/// - Any change in internal implementation should not affect these models
///
/// Pattern:
/// Each operation follows a consistent structure:
/// Request → Operation → Result
///
/// Example:
/// GciInitSlotRequest → InitSlot → GciInitSlotResult
/// GetSlot → GciSlotInfo
/// GetPcbId → GciGetPcbIdResult
///
/// This layer acts as a boundary between domain logic and integration logic.
/// </summary>
public class ModelsMapping
{
/// <summary>
/// Mapping methods between public DTOs and internal GCI models.
/// Ensures separation between external contracts and internal domain objects.
/// </summary>
#region ================================== OUTERN/INTERN and back models mapping ==================================
public static PasswordSource MapPasswordSource(PublicModels.GciPasswordSource src)
{
return (PasswordSource)src;
}
public static ConfigSource MapConfigSource(PublicModels.GciConfigSource src)
{
return (ConfigSource)src;
}
public static Xylem.Common.Hardware.Interfaces.Ports.PortCore.PortConfig? MapPort(PublicModels.GciPortConfig port)
{
if (port == null)
return null;
Xylem.Common.Hardware.Interfaces.Ports.PortCore.PortConfig result = new Xylem.Common.Hardware.Interfaces.Ports.PortCore.PortConfig();
result.PortName = port.PortName;
result.Type = port.Type;
return result;
}
public static PublicModels.GciInitSlotResult MapInitResult(PublicModels.GciInitSlotResult result)
{
if (result == null)
return null;
return new PublicModels.GciInitSlotResult
{
SlotId = result.SlotId,
Success = result.Success,
Message = result.Message
};
}
public static PublicModels.GciPasswordSource MapPasswordSourceBack(PasswordSource src)
{
return (PublicModels.GciPasswordSource)src;
}
public static PublicModels.GciConfigSource MapConfigSourceBack(ConfigSource src)
{
return (PublicModels.GciConfigSource)src;
}
public static PublicModels.GciPortConfig MapPortBack(PortConfig? port)
{
if (!port.HasValue)
return null;
PortConfig value = port.Value;
return new PublicModels.GciPortConfig
{
PortName = value.PortName,
Type = value.Type
};
}
public static PublicModels.GciPortConfig MapPortBack(IPort port)
{
if (port == null)
return null;
return new PublicModels.GciPortConfig
{
PortName = port.GetPortName(),
Type = port.GetType().Name
};
}
public static PublicModels.GciPortConfig MapPortBack(string portName, string portTyoe)
{
return new PublicModels.GciPortConfig
{
PortName = portName,
Type = portTyoe
};
}
public static MeterBatchDebugStatus ToMeterBatchDebugStatus(GciSlotInfo slot)
{
if (slot == null)
return null;
return new MeterBatchDebugStatus
{
Slot = slot.SlotId,
Selected = true,
PcbId = slot.PcbId,
IsConnected = false,
IsLoggedOn = false,
RequestPort = slot.RequestPort == null ? "" : slot.RequestPort.PortName,
StreamingPort = slot.StreamingPort == null ? "" : slot.StreamingPort.PortName,
FwVersion = "",
InterfaceVersion = ""
};
}
public static List<MeterBatchDebugStatus> ToMeterBatchDebugStatusList(
IEnumerable<GciSlotInfo> slots)
{
if (slots == null)
return new List<MeterBatchDebugStatus>();
return slots
.Where(x => x != null)
.Select(ToMeterBatchDebugStatus)
.ToList();
}
#endregion
}
}
@@ -1,504 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using static GenesisCordonelInterface.API.InterfaceOutsideToGCI;
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter;
namespace GenesisCordonelInterface.API
{
/// <summary>
/// Public data contract layer for the Genesis Cordonel Interface (GCI).
///
/// This class defines all Data Transfer Objects (DTOs) that are exposed
/// to external consumers (e.g. TBF, UI, or other integration layers).
///
/// Responsibilities:
/// - Provide stable, dependency-free models for external usage
/// - Decouple internal GCI implementation (GenesisMeter, Xylem libraries)
/// from external systems
/// - Define request/response contracts for all supported operations
/// - Contain mapping methods between public DTOs and internal domain models
///
/// Architecture:
/// External world (TBF / UI)
/// ↓
/// GciPublicModels (this layer)
/// ↓
/// Internal GCI API (InterfaceGCIToLaatzen, GenesisMeter, etc.)
///
/// Notes:
/// - Public models must NOT expose internal types (e.g. GenesisMeter, IPort, etc.)
/// - All mapping between internal and external representations must be done here
/// - DTOs are designed to be simple, serializable, and stable over time
/// - Any change in internal implementation should not affect these models
///
/// Pattern:
/// Each operation follows a consistent structure:
/// Request → Operation → Result
///
/// Example:
/// GciInitSlotRequest → InitSlot → GciInitSlotResult
/// GetSlot → GciSlotInfo
/// GetPcbId → GciGetPcbIdResult
///
/// This layer acts as a boundary between domain logic and integration logic.
/// </summary>
public class PublicModels
{
public static bool HideSensitiveValues { get; set; } = true;
private static string FormatPassword(string password)
{
if (!HideSensitiveValues)
return password ?? "<null>";
if (string.IsNullOrEmpty(password))
return "<empty>";
return "********";
}
/// <summary>
/// Public DTOs exposed to external systems.
/// These models represent the contract of the GCI API.
/// They must remain stable and independent of internal implementation.
/// </summary>
#region ================================== PUBLIC MODELS ===========================================
public class RegisterReadResult
{
public bool Success { get; set; }
public string RegisterName { get; set; }
public string RawHex { get; set; }
public string ErrorMessage { get; set; }
public override string ToString()
{
return string.Format(
"Success={0}, RegisterName={1}, RawHex={2}, ErrorMessage={3}",
Success,
RegisterName,
RawHex,
ErrorMessage);
}
}
public class WorkerDebugStatus
{
public int Slot { get; set; }
public string Name { get; set; }
public int QueueLength { get; set; }
public bool IsBusy { get; set; }
public string CurrentOperation { get; set; }
public string LastError { get; set; }
public DateTime LastActivity { get; set; }
}
public class MeterBatchDebugStatus
{
public int Slot { get; set; }
public bool Selected { get; set; }
public string PcbId { get; set; }
public bool IsConnected { get; set; }
public bool IsLoggedOn { get; set; }
public string RequestPort { get; set; }
public string StreamingPort { get; set; }
public string RequestPortType { get; set; }
public string FwVersion { get; set; }
public string InterfaceVersion { get; set; }
}
public class RegisterWriteResult
{
public bool Success { get; set; }
public string RegisterName { get; set; }
public object WrittenValue { get; set; }
public bool StoreToDevice { get; set; }
public bool RefreshSystemState { get; set; }
public string ErrorMessage { get; set; }
public override string ToString()
{
return $"Success={Success}, " +
$"Register={RegisterName ?? "<null>"}, " +
$"Value={WrittenValue ?? "<null>"}, " +
$"StoreToDevice={StoreToDevice}, " +
$"RefreshSystemState={RefreshSystemState}, " +
$"Error={ErrorMessage ?? "<none>"}";
}
}
public class PortDetectionResult
{
public bool Success { get; set; }
public int Slot { get; set; }
public string PortName { get; set; }
public string PcbId { get; set; }
public string ErrorMessage { get; set; }
public override string ToString()
{
return string.Format(
"Slot={0}, Success={1}, PortName={2}, PcbId={3}, ErrorMessage={4}",
Slot,
Success,
PortName,
PcbId,
ErrorMessage);
}
}
public class GciSlotInfo
{
public int SlotId { get; set; }
public bool Exists { get; set; }
public bool Success { get; set; }
public bool IsConnected { get; set; }
public bool IsLoggedOn { get; set; }
public string Message { get; set; }
public string PcbId { get; set; }
public string Password { get; set; }
public GciConfigSource ConfigSource { get; set; }
public GciPasswordSource PasswordSource { get; set; }
public GciPortConfig RequestPort { get; set; }
public GciPortConfig StreamingPort { get; set; }
public override string ToString()
{
return string.Format(
"SlotId={0}, Exists={1}, Success={2}, IsConnected={3}, IsLoggedOn={4}, Message={5}, PcbId={6}, Password={7}, ConfigSource={8}, PasswordSource={9}, RequestPort={10}, StreamingPort={11}",
SlotId,
Exists,
Success,
IsConnected,
IsLoggedOn,
Message,
PcbId,
FormatPassword(Password),
ConfigSource,
PasswordSource,
RequestPort,
StreamingPort);
}
}
public class GciAllSlotsInfo
{
public bool Success { get; set; }
public string Message { get; set; }
public List<GciSlotInfo> Slots { get; set; }
public override string ToString()
{
return $"Success={Success}, Count={Slots?.Count ?? 0}, Message={Message}";
}
}
public class Result
{
public int SlotId { get; set; }
public bool Success { get; set; }
public string Message { get; set; }
public override string ToString()
{
return string.Format(
"SlotId={0}, Success={1}, Message={2}",
SlotId,
Success,
Message);
}
}
public enum GciPasswordSource
{
RestApi = 0,
OfflineFile = 1,
InterfaceInputPassword = 2,
}
public enum GciConfigSource
{
FileConfig = 0,
InterfaceInputConfig = 1,
}
/// <summary>
/// Collection of port settings
/// </summary>
public class GciPortConfig
{
public string PortName { get; set; }
public string Type { get; set; }
public override string ToString()
{
return string.Format("PortName={0}, Type={1}", PortName, Type);
}
}
public static GciPortConfig CreatePortConfig(string portName, string type)
{
if (string.IsNullOrWhiteSpace(portName))
return null;
return new GciPortConfig
{
PortName = portName,
Type = MapPortType(type)
};
}
public static string MapPortType(string type)
{
switch ((type ?? "").Trim().ToUpperInvariant())
{
case "RFID":
return "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.RfidSerialPort";
case "UART":
return "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.UartSerialPort";
case "IRDA":
return "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.IrdaSerialPort";
default:
return "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.IrdaSerialPort";
}
}
public class GciInitSlotRequest
{
public int SlotId { get; set; }
public GciConfigSource ConfigSource { get; set; }
public GciPasswordSource PasswordSource { get; set; }
public GciPortConfig RequestPort { get; set; }
public GciPortConfig StreamingPort { get; set; }
public string Password { get; set; }
public override string ToString()
{
return string.Format(
"SlotId={0}, GciConfigSource={1}, PasswordSource={2}, RequestPort={3}, StreamingPort={4}, Password={5}",
SlotId,
ConfigSource,
PasswordSource,
RequestPort,
StreamingPort,
FormatPassword(Password));
}
}
public class GciInitSlotResult
{
public int SlotId { get; set; }
public bool Success { get; set; }
public string Message { get; set; }
public string PcbId { get; set; }
public bool Created { get; set; }
public bool Updated { get; set; }
public bool AlreadyExists { get; set; }
public override string ToString()
{
return string.Format(
"SlotId={0}, Success={1}, Message={2}, PcbId={3}, Created={4}, Updated={5}, AlreadyExists={6}",
SlotId,
Success,
Message,
PcbId,
Created,
Updated,
AlreadyExists);
}
}
public class GciCleanAllSlotsResult
{
public bool Success { get; set; }
public string Message { get; set; }
public override string ToString()
{
return string.Format(
"Success={0}, Message={1}",
Success,
Message);
}
}
public class GciCleanSlotResult
{
public int SlotId { get; set; }
public bool Success { get; set; }
public string Message { get; set; }
public override string ToString()
{
return string.Format(
"SlotId={0}, Success={1}, Message={2}",
SlotId,
Success,
Message);
}
}
public class GciGetPcbIdResult
{
public int SlotId { get; set; }
public bool Success { get; set; }
public string PcbId { get; set; }
public string Message { get; set; }
public override string ToString()
{
return string.Format(
"SlotId={0}, Success={1}, PcbId={2}, Message={3}",
SlotId,
Success,
PcbId,
Message);
}
}
public class GciConnectResult
{
public int SlotId { get; set; }
public bool Success { get; set; }
public string PcbId { get; set; }
public bool IsConnected { get; set; }
public string Message { get; set; }
public override string ToString()
{
return string.Format(
"SlotId={0}, Success={1}, PcbId={2}, IsConnected={3}, Message={4}",
SlotId,
Success,
PcbId,
IsConnected,
Message);
}
}
public class GciLoginResult
{
public int SlotId { get; set; }
public bool Success { get; set; }
public string PcbId { get; set; }
public bool IsLoggedOn { get; set; }
public string Message { get; set; }
public override string ToString()
{
return $"Slot={SlotId}, Success={Success}, PcbId={PcbId}, IsLoggedOn={IsLoggedOn}, Message={Message ?? "<none>"}";
}
}
public class GciRegisterSnapshot
{
public string Name { get; set; }
public string Type { get; set; }
public string RawValue { get; set; }
public string Min { get; set; }
public string Max { get; set; }
public string Description { get; set; }
public string Version { get; set; }
public string IsAvailable { get; set; }
public string Privilege { get; set; }
}
public class GciDisconnectResult
{
public int SlotId { get; set; }
public bool Success { get; set; }
public string Message { get; set; }
public override string ToString()
{
return string.Format(
"SlotId={0}, Success={1}, Message={2}",
SlotId,
Success,
Message);
}
}
public class GciSetPasswordResult
{
public int SlotId { get; set; }
public bool Success { get; set; }
public string Password { get; set; }
public string Message { get; set; }
public override string ToString()
{
return string.Format(
"SlotId={0}, Success={1}, Password={2}, Message={3}",
SlotId,
Success,
FormatPassword(Password),
Message);
}
}
public class PreAdjustmentProcessResult
{
public bool Success { get; set; }
public int Slot { get; set; }
public string ProcessName { get; set; }
public string ErrorMessage { get; set; }
/// <inheritdoc />
public override string ToString()
{
return
$"Success={Success}, " +
$"Slot={Slot}, " +
$"Process={ProcessName}, " +
$"Error={ErrorMessage ?? "None"}";
}
}
public class PreadjustmentDetectResult
{
public bool Success { get; set; }
public int DetectedMeterCount { get; set; }
public int DetectedThermometerCount { get; set; }
public string ErrorMessage { get; set; }
/// <inheritdoc />
public override string ToString()
{
return
$"Success={Success}, " +
$"Meters={DetectedMeterCount}, " +
$"Thermometers={DetectedThermometerCount}, " +
$"Error={ErrorMessage ?? "None"}";
}
}
public class PreAdjustmentInitializationResult
{
public bool Success { get; set; }
public string Slots { get; set; }
public string ErrorMessage { get; set; }
/// <inheritdoc />
public override string ToString()
{
return
$"Success={Success}, " +
$"Slots={Slots}, " +
$"Error={ErrorMessage ?? "None"}";
}
}
#endregion
}
}
-18
View File
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="NLog" publicKeyToken="5120e14c03d0593c" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Xylem.Common.CommonCore" publicKeyToken="null" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.8.18.17954" newVersion="2.8.18.17954" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
@@ -1,36 +0,0 @@
{
"_Comment": "GCI DataStorage configuration",
"DataStorage": {
"MeterLoginPasswords": {
"___Documentation___": {
"Description": "Reads login passwords for meters by PCB ID",
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
"DataSource": "Database connection string",
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [PcbId]=QUERYPARAM -> PCB ID provided during GetPasswordAsync()."
},
"Name": "MeterLoginPasswords",
"Type": "LocalDatabase",
"DataSource": "Server=(localdb)\\MojaDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
"QueryTemplate": "SELECT [Password] FROM [dbo].[SkeletonKeys] WHERE [PcbId] = QUERYPARAM"
},
"PreAdjustmentCalibrationParams": {
"___Documentation___": {
"Description": "Reads pre-adjustment calibration parameters by meter size",
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
"DataSource": "Database connection string",
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [MeterSize]=QUERYPARAM -> meter size provided during ReadCalibrationParamsAsync()."
},
"Name": "PreAdjustmentCalibrationParams",
"Type": "LocalDatabase",
"DataSource": "Server=(localdb)\\MojaDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
"QueryTemplate": "SELECT [ParameterName], [ParameterValue] FROM [dbo].[PreAdjustmentCalibrationParams] WHERE [MeterSize] = QUERYPARAM"
}
}
}
@@ -1,32 +0,0 @@
namespace GenesisCordonelInterface.Core.Config
{
/// <summary>
/// Root GCI configuration object loaded from gci_config.json.
///
/// This class represents the top-level configuration structure
/// and serves as an entry point for all configurable GCI features.
///
/// Example:
///
/// {
/// "DataStorageSection":
/// {
/// ...
/// }
/// }
/// </summary>
public class GciConfig
{
/// <summary>
/// Data storage configuration section.
///
/// Contains definitions for all configured readers and writers,
/// such as:
///
/// - MeterLoginPasswords
/// - PreAdjustmentCalibrationParams
/// - future storage providers
/// </summary>
public DataStorage.Config.GciDataStorageConfig DataStorageSection { get; set; }
}
}
@@ -1,177 +0,0 @@
using System;
using System.IO;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using Newtonsoft.Json;
namespace GenesisCordonelInterface.Core.Config
{
/// <summary>
/// Loads GCI configuration from gci_config.json.
///
/// Responsibilities:
/// - Locate configuration file
/// - Deserialize JSON into strongly typed objects
/// - Normalize relative file paths
/// - Preserve database connection strings and REST URLs
///
/// Example:
///
/// Config/
/// gci_config.json
///
/// Data/
/// meter_passwords.csv
///
/// Relative paths:
/// Data\file.csv
///
/// become:
///
/// C:\App\bin\Debug\Data\file.csv
///
/// Database sources remain unchanged:
///
/// Server=(localdb)\MojaDB;Database=...
/// </summary>
public static class GciConfigLoader
{
/// <summary>
/// Loads default GCI configuration from:
///
/// Config\gci_config.json
/// </summary>
/// <returns>
/// Loaded GCI configuration.
/// </returns>
public static GciConfig LoadDefault()
{
string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
string configPath = Path.Combine(
baseDirectory,
"Config",
"gci_config.json");
return Load(configPath, baseDirectory);
}
/// <summary>
/// Loads GCI configuration from specified file.
/// </summary>
/// <param name="configPath">
/// Path to configuration json file.
/// </param>
/// <param name="baseDirectory">
/// Base directory used for resolving relative paths.
/// </param>
/// <returns>
/// Loaded configuration object.
/// </returns>
public static GciConfig Load(
string configPath,
string baseDirectory = null)
{
if (string.IsNullOrWhiteSpace(configPath))
throw new ArgumentException(
"Config path must not be empty.",
nameof(configPath));
if (!File.Exists(configPath))
throw new FileNotFoundException(
"GCI config file was not found.",
configPath);
string json = File.ReadAllText(configPath);
GciConfig config =
JsonConvert.DeserializeObject<GciConfig>(json);
if (config == null)
throw new InvalidOperationException(
"GCI config could not be loaded.");
NormalizeDataStoragePaths(
config,
baseDirectory ?? Path.GetDirectoryName(configPath));
return config;
}
/// <summary>
/// Normalizes paths for all configured data storage entries.
///
/// Converts relative file paths into absolute paths.
/// Database connection strings remain untouched.
/// </summary>
/// <param name="config">
/// Loaded configuration object.
/// </param>
/// <param name="baseDirectory">
/// Base path used for relative resolution.
/// </param>
private static void NormalizeDataStoragePaths(
GciConfig config,
string baseDirectory)
{
if (config.DataStorageSection == null)
return;
NormalizePath(
config.DataStorageSection.MeterLoginPasswords,
baseDirectory);
NormalizePath(
config.DataStorageSection.PreAdjustmentCalibrationParams,
baseDirectory);
}
/// <summary>
/// Converts relative file paths into absolute paths.
///
/// Applies only to:
/// - LocalCsv
/// - RemoteCsv
/// - LocalJson
/// - RemoteJson
///
/// Does not modify:
/// - Database connection strings
/// - REST URLs
/// </summary>
/// <param name="storageConfig">
/// Storage configuration.
/// </param>
/// <param name="baseDirectory">
/// Base path for resolution.
/// </param>
private static void NormalizePath(
DataStorageConfig storageConfig,
string baseDirectory)
{
if (storageConfig == null)
return;
if (string.IsNullOrWhiteSpace(storageConfig.DataSource))
return;
// Database connection strings and URLs
// must never be treated as file paths.
if (storageConfig.Type == DataStorageType.LocalDatabase ||
storageConfig.Type == DataStorageType.RemoteDatabase ||
storageConfig.Type == DataStorageType.RestApi)
{
return;
}
if (Path.IsPathRooted(storageConfig.DataSource))
return;
storageConfig.DataSource =
Path.GetFullPath(
Path.Combine(
baseDirectory,
storageConfig.DataSource));
}
}
}
@@ -1,36 +0,0 @@
{
"_Comment": "GCI DataStorage configuration",
"DataStorageSection": {
"MeterLoginPasswords": {
"___Documentation___": {
"Description": "Reads login passwords for meters by PCB ID",
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
"DataSource": "Database connection string",
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [PcbId]=QUERYPARAM -> PCB ID provided during GetPasswordAsync()."
},
"Name": "MeterLoginPasswords",
"Type": "LocalDatabase",
"DataSource": "Server=(localdb)\\SensusLocalDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
"QueryTemplate": "SELECT [Password] FROM [dbo].[SkeletonKeys] WHERE [PcbId] = QUERYPARAM"
},
"PreAdjustmentCalibrationParams": {
"___Documentation___": {
"Description": "Reads pre-adjustment calibration parameters by meter size",
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
"DataSource": "Database connection string",
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [MeterSize]=QUERYPARAM -> meter size provided during ReadCalibrationParamsAsync()."
},
"Name": "PreAdjustmentCalibrationParams",
"Type": "LocalDatabase",
"DataSource": "Server=(localdb)\\SensusLocalDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
"QueryTemplate": "SELECT [ParameterName], [ParameterValue] FROM [dbo].[PreAdjustmentCalibrationParams] WHERE [MeterSize] = QUERYPARAM"
}
}
}
@@ -1,32 +0,0 @@
namespace GenesisCordonelInterface.Core.DataStorage.Config
{
/// <summary>
/// Root GCI configuration object loaded from gci_config.json.
///
/// This class represents the top-level configuration structure
/// and serves as an entry point for all configurable GCI features.
///
/// Example:
///
/// {
/// "DataStorage":
/// {
/// ...
/// }
/// }
/// </summary>
public class GciConfig
{
/// <summary>
/// Data storage configuration section.
///
/// Contains definitions for all configured readers and writers,
/// such as:
///
/// - MeterLoginPasswords
/// - PreAdjustmentCalibrationParams
/// - future storage providers
/// </summary>
public GciDataStorageConfig DataStorage { get; set; }
}
}
@@ -1,177 +0,0 @@
using System;
using System.IO;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using Newtonsoft.Json;
namespace GenesisCordonelInterface.Core.DataStorage.Config
{
/// <summary>
/// Loads GCI configuration from gci_config.json.
///
/// Responsibilities:
/// - Locate configuration file
/// - Deserialize JSON into strongly typed objects
/// - Normalize relative file paths
/// - Preserve database connection strings and REST URLs
///
/// Example:
///
/// Config/
/// gci_config.json
///
/// Data/
/// meter_passwords.csv
///
/// Relative paths:
/// Data\file.csv
///
/// become:
///
/// C:\App\bin\Debug\Data\file.csv
///
/// Database sources remain unchanged:
///
/// Server=(localdb)\MojaDB;Database=...
/// </summary>
public static class GciConfigLoader
{
/// <summary>
/// Loads default GCI configuration from:
///
/// Config\gci_config.json
/// </summary>
/// <returns>
/// Loaded GCI configuration.
/// </returns>
public static GciConfig LoadDefault()
{
string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
string configPath = Path.Combine(
baseDirectory,
"Config",
"gci_config.json");
return Load(configPath, baseDirectory);
}
/// <summary>
/// Loads GCI configuration from specified file.
/// </summary>
/// <param name="configPath">
/// Path to configuration json file.
/// </param>
/// <param name="baseDirectory">
/// Base directory used for resolving relative paths.
/// </param>
/// <returns>
/// Loaded configuration object.
/// </returns>
public static GciConfig Load(
string configPath,
string baseDirectory = null)
{
if (string.IsNullOrWhiteSpace(configPath))
throw new ArgumentException(
"Config path must not be empty.",
nameof(configPath));
if (!File.Exists(configPath))
throw new FileNotFoundException(
"GCI config file was not found.",
configPath);
string json = File.ReadAllText(configPath);
GciConfig config =
JsonConvert.DeserializeObject<GciConfig>(json);
if (config == null)
throw new InvalidOperationException(
"GCI config could not be loaded.");
NormalizeDataStoragePaths(
config,
baseDirectory ?? Path.GetDirectoryName(configPath));
return config;
}
/// <summary>
/// Normalizes paths for all configured data storage entries.
///
/// Converts relative file paths into absolute paths.
/// Database connection strings remain untouched.
/// </summary>
/// <param name="config">
/// Loaded configuration object.
/// </param>
/// <param name="baseDirectory">
/// Base path used for relative resolution.
/// </param>
private static void NormalizeDataStoragePaths(
GciConfig config,
string baseDirectory)
{
if (config.DataStorage == null)
return;
NormalizePath(
config.DataStorage.MeterLoginPasswords,
baseDirectory);
NormalizePath(
config.DataStorage.PreAdjustmentCalibrationParams,
baseDirectory);
}
/// <summary>
/// Converts relative file paths into absolute paths.
///
/// Applies only to:
/// - LocalCsv
/// - RemoteCsv
/// - LocalJson
/// - RemoteJson
///
/// Does not modify:
/// - Database connection strings
/// - REST URLs
/// </summary>
/// <param name="storageConfig">
/// Storage configuration.
/// </param>
/// <param name="baseDirectory">
/// Base path for resolution.
/// </param>
private static void NormalizePath(
DataStorageConfig storageConfig,
string baseDirectory)
{
if (storageConfig == null)
return;
if (string.IsNullOrWhiteSpace(storageConfig.DataSource))
return;
// Database connection strings and URLs
// must never be treated as file paths.
if (storageConfig.Type == DataStorageType.LocalDatabase ||
storageConfig.Type == DataStorageType.RemoteDatabase ||
storageConfig.Type == DataStorageType.RestApi)
{
return;
}
if (Path.IsPathRooted(storageConfig.DataSource))
return;
storageConfig.DataSource =
Path.GetFullPath(
Path.Combine(
baseDirectory,
storageConfig.DataSource));
}
}
}
@@ -1,30 +0,0 @@
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
namespace GenesisCordonelInterface.Core.DataStorage.Config
{
/// <summary>
/// Contains all configured GCI data storage sources.
///
/// Each property represents one logical use-case
/// and points to its storage configuration.
///
/// Example:
/// MeterLoginPasswords
/// -> SQL database
///
/// PreAdjustmentCalibrationParams
/// -> CSV file
/// </summary>
public class GciDataStorageConfig
{
/// <summary>
/// Configuration for meter login password lookup.
/// </summary>
public DataStorageConfig MeterLoginPasswords { get; set; }
/// <summary>
/// Configuration for pre-adjustment calibration data lookup.
/// </summary>
public DataStorageConfig PreAdjustmentCalibrationParams { get; set; }
}
}
@@ -1,49 +0,0 @@
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts
{
/// <summary>
/// Defines common contract for all GCI data storage readers.
///
/// Implementations can read data from different storage types:
/// - SQL database
/// - CSV file
/// - JSON file
/// - REST API
///
/// Consumers should use this interface instead of concrete readers.
/// </summary>
public interface IDataStorageReader
{
/// <summary>
/// Reads data from configured storage using provided query.
/// </summary>
/// <param name="query">
/// Query object containing lookup parameters.
/// </param>
/// <returns>
/// Storage-specific result object.
/// </returns>
object GetData(Models.DataQuery query);
/// <summary>
/// Tests whether configured data source is accessible.
/// </summary>
/// <param name="enableDiagnostics">
/// Enables detailed diagnostic output.
/// </param>
/// <returns>
/// Diagnostic result of source test.
/// </returns>
Models.ReaderDiagnosticResult TestSource(bool enableDiagnostics);
/// <summary>
/// Tests whether configured query is valid for the data source.
/// </summary>
/// <param name="enableDiagnostics">
/// Enables detailed diagnostic output.
/// </param>
/// <returns>
/// Diagnostic result of query test.
/// </returns>
Models.ReaderDiagnosticResult TestQuery(bool enableDiagnostics);
}
}
@@ -1,94 +0,0 @@
using System;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common
{
/// <summary>
/// Factory responsible for creating appropriate
/// data storage reader implementations.
///
/// The reader type is selected according to:
///
/// DataStorageConfig.Type
///
/// Example:
///
/// LocalDatabase
/// -> DatabaseDataStorageReader
///
/// LocalCsv
/// -> CsvDataStorageReader
///
/// LocalJson
/// -> JsonDataStorageReader
///
/// RestApi
/// -> RestApiDataStorageReader
///
/// This factory hides implementation details from
/// higher layers and keeps consumers independent
/// of storage technology.
///
/// Trace:
///
/// MeterLoginPasswordReader
/// -> DataStorageReaderFactory.Create()
/// -> DatabaseDataStorageReader
/// -> CsvDataStorageReader
/// -> JsonDataStorageReader
/// -> RestApiDataStorageReader
/// </summary>
public static class DataStorageReaderFactory
{
/// <summary>
/// Creates appropriate reader implementation
/// according to storage configuration.
/// </summary>
/// <param name="config">
/// Data storage configuration loaded from gci_config.json.
/// </param>
/// <returns>
/// Configured storage reader implementation.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Configuration is null.
/// </exception>
/// <exception cref="NotSupportedException">
/// Unsupported storage type.
/// </exception>
public static IDataStorageReader Create(
DataStorageConfig config)
{
if (config == null)
throw new ArgumentNullException(nameof(config));
switch (config.Type)
{
case DataStorageType.RemoteDatabase:
case DataStorageType.LocalDatabase:
return new Providers.DatabaseDataStorageReader(config);
case DataStorageType.RemoteCsv:
case DataStorageType.LocalCsv:
return new Providers.CsvDataStorageReader(config);
case DataStorageType.RemoteJson:
case DataStorageType.LocalJson:
return new Providers.JsonDataStorageReader(config);
case DataStorageType.RestApi:
return new Providers.RestApiDataStorageReader(config);
default:
throw new NotSupportedException(
$"Unsupported DataStorageType: '{config.Type}'");
}
}
}
}
@@ -1,58 +0,0 @@
using System.Collections.Generic;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
{
/// <summary>
/// Represents query input for data storage readers.
///
/// Query parameters are used as lookup values
/// during storage search operations.
///
/// A single parameter:
///
/// 231630279
///
/// executes one lookup.
///
/// Multiple parameters:
///
/// 231630279
/// 231630243
/// 231630148
///
/// can execute batch operations.
///
/// Example:
///
/// QueryTemplate:
///
/// SELECT [Password]
/// WHERE [PcbId]=QUERYPARAM
///
/// Query:
///
/// QueryParams:
/// 231630279
///
/// Result:
///
/// Password belonging to 231630279.
/// </summary>
public class DataQuery
{
/// <summary>
/// Collection of query values.
///
/// Single item:
///
/// QueryParams[0]
///
/// represents one lookup.
///
/// Multiple items may be processed
/// as batch requests.
/// </summary>
public List<string> QueryParams { get; }
= new List<string>();
}
}
@@ -1,62 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
{
/// <summary>
/// Configuration for one storage source.
/// Loaded from gci_config.json
/// </summary>
public class DataStorageConfig
{
/// <summary>
/// Logical storage name.
/// Example:
/// MeterLoginPasswords
/// CalibrationParameters
/// </summary>
public string Name { get; set; }
/// <summary>
/// Storage implementation type.
/// </summary>
public DataStorageType Type { get; set; }
/// <summary>
/// Connection string,
/// file path,
/// URL etc.
/// </summary>
public string DataSource { get; set; }
/// <summary>
/// Query template.
/// Example:
///
/// SQL:
/// SELECT Password
/// FROM Passwords
/// WHERE PcbId=QUERYPARAM
///
/// CSV:
/// SELECT [Password]
/// WHERE [PcbId]=QUERYPARAM
/// </summary>
public string QueryTemplate { get; set; }
public DataStorageConfig(
string name,
DataStorageType type,
string dataSource,
string queryTemplate)
{
Name = name;
Type = type;
DataSource = dataSource;
QueryTemplate = queryTemplate;
}
}
}
@@ -1,79 +0,0 @@
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
{
/// <summary>
/// Identifies supported data storage implementations.
///
/// The type determines which reader implementation
/// will be created by DataStorageReaderFactory.
///
/// Example:
///
/// LocalDatabase
/// -> DatabaseDataStorageReader
///
/// LocalCsv
/// -> CsvDataStorageReader
///
/// RestApi
/// -> RestApiDataStorageReader
///
/// Source:
///
/// gci_config.json
///
/// Example:
///
/// "Type":"LocalDatabase"
/// </summary>
public enum DataStorageType
{
/// <summary>
/// REST API endpoint.
///
/// Example:
///
/// https://api.company.com/passwords
/// </summary>
RestApi,
/// <summary>
/// Database located on remote server.
///
/// Example:
///
/// SQL server on network machine.
/// </summary>
RemoteDatabase,
/// <summary>
/// Database located on local machine.
///
/// Example:
///
/// SQL LocalDB
/// SQLite
/// local SQL Server instance
/// </summary>
LocalDatabase,
/// <summary>
/// JSON source stored remotely.
/// </summary>
RemoteJson,
/// <summary>
/// JSON file stored locally.
/// </summary>
LocalJson,
/// <summary>
/// CSV source stored remotely.
/// </summary>
RemoteCsv,
/// <summary>
/// CSV file stored locally.
/// </summary>
LocalCsv
}
}
@@ -1,53 +0,0 @@
using System.Collections.Generic;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
{
/// <summary>
/// Represents result returned by database-based
/// data storage readers.
///
/// Supports both:
///
/// - Single-row lookups
/// (e.g. MeterLoginPasswords)
///
/// - Multi-row queries
/// (e.g. PreAdjustmentCalibrationParams)
/// </summary>
internal class DatabaseSearchResult
{
/// <summary>
/// Indicates whether at least one record
/// was found.
/// </summary>
public bool Found { get; set; }
/// <summary>
/// Executed SQL query text.
/// Mainly intended for diagnostics
/// and troubleshooting.
/// </summary>
public string Query { get; set; }
/// <summary>
/// First returned row represented as
/// column/value pairs.
///
/// Preserved for backward compatibility
/// with existing readers expecting
/// a single database record.
/// </summary>
public Dictionary<string, object> Values { get; }
= new Dictionary<string, object>();
/// <summary>
/// All returned rows represented as
/// a collection of column/value dictionaries.
///
/// Intended for queries returning
/// multiple records.
/// </summary>
public List<Dictionary<string, object>> Rows { get; }
= new List<Dictionary<string, object>>();
}
}
@@ -1,141 +0,0 @@
using System.Collections.Generic;
using System.Text;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
{
/// <summary>
/// Represents diagnostic result of data storage operations.
///
/// Used for:
/// - source validation
/// - query validation
/// - connection tests
/// - detailed diagnostic logging
///
/// Example:
///
/// TestSource()
/// -> connection successful
///
/// TestQuery()
/// -> column validation
///
/// Can also carry additional payload data.
/// </summary>
public class ReaderDiagnosticResult
{
/// <summary>
/// Indicates whether operation completed successfully.
/// </summary>
public bool Success { get; set; }
/// <summary>
/// Human readable summary message.
///
/// Example:
///
/// "Connection successful."
///
/// or:
///
/// "Column not found."
/// </summary>
public string Message { get; set; }
/// <summary>
/// Detailed diagnostic output lines.
///
/// Used for debugging and troubleshooting.
/// </summary>
public List<string> Diagnostics { get; set; }
/// <summary>
/// Optional operation payload.
///
/// Example:
///
/// loaded CSV lines
/// SQL test value
/// parsed content
/// </summary>
public object Data { get; set; }
/// <summary>
/// Creates empty diagnostic result.
/// </summary>
public ReaderDiagnosticResult()
{
Diagnostics = new List<string>();
}
/// <summary>
/// Creates successful diagnostic result.
/// </summary>
/// <param name="message">
/// Success message.
/// </param>
/// <returns>
/// Successful result object.
/// </returns>
public static ReaderDiagnosticResult SuccessResult(
string message = "OK")
{
return new ReaderDiagnosticResult
{
Success = true,
Message = message
};
}
/// <summary>
/// Creates failed diagnostic result.
/// </summary>
/// <param name="message">
/// Failure message.
/// </param>
/// <returns>
/// Failed result object.
/// </returns>
public static ReaderDiagnosticResult Failure(
string message)
{
return new ReaderDiagnosticResult
{
Success = false,
Message = message
};
}
/// <summary>
/// Converts diagnostic information into display text.
///
/// Output contains:
///
/// diagnostic lines
/// +
/// summary message
/// </summary>
/// <returns>
/// Formatted text representation.
/// </returns>
public string ToDisplayDiag()
{
StringBuilder sb = new StringBuilder();
if (Diagnostics != null &&
Diagnostics.Count > 0)
{
foreach (string line in Diagnostics)
sb.AppendLine(line);
if (!string.IsNullOrWhiteSpace(Message))
sb.AppendLine();
}
if (!string.IsNullOrWhiteSpace(Message))
sb.AppendLine(Message);
return sb.ToString();
}
}
}
@@ -1,394 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Searching;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
{
/// <summary>
/// Reads and searches CSV based data sources.
///
/// Supported query syntax:
/// SELECT [ReturnColumn] WHERE [MatchColumn] = QUERYPARAM
///
/// Example:
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
///
/// Supports:
/// - column names: [ColumnName]
/// - column indexes: COLUMN(n)
///
/// Trace:
/// MeterLoginPasswordReader
/// -> CsvDataStorageReader.GetData()
/// -> ConnectToSource()
/// -> ExecuteQuery()
/// </summary>
public class CsvDataStorageReader : IDataStorageReader
{
private readonly DataStorageConfig config;
private string resolvedPath;
private string[] loadedLines;
private string[] loadedHeaders;
/// <summary>
/// Creates CSV data storage reader using provided configuration.
/// </summary>
/// <param name="config">
/// Data storage configuration loaded from gci_config.json.
/// </param>
public CsvDataStorageReader(DataStorageConfig config)
{
this.config = config ?? throw new ArgumentNullException(nameof(config));
}
/// <summary>
/// Reads values from CSV using configured QueryTemplate.
/// </summary>
/// <param name="query">
/// Query parameters used for lookup.
/// </param>
/// <returns>
/// ReaderDiagnosticResult containing lookup result.
/// </returns>
public object GetData(DataQuery query)
{
if (query == null)
throw new ArgumentNullException(nameof(query));
ReaderDiagnosticResult sourceResult = ConnectToSource(true);
if (!sourceResult.Success)
throw new InvalidOperationException(sourceResult.Message);
ReaderDiagnosticResult queryResult = ExecuteQuery(query, true);
if (!queryResult.Success)
throw new InvalidOperationException(queryResult.Message);
return queryResult;
}
/// <summary>
/// Validates CSV source accessibility and content.
/// </summary>
/// <param name="enableDiagnostics">
/// Enables detailed diagnostic output.
/// </param>
/// <returns>
/// Diagnostic result.
/// </returns>
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
{
return ConnectToSource(enableDiagnostics);
}
/// <summary>
/// Validates configured QueryTemplate against CSV header.
/// </summary>
/// <param name="enableDiagnostics">
/// Enables detailed diagnostic output.
/// </param>
/// <returns>
/// Diagnostic result.
/// </returns>
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
{
ReaderDiagnosticResult sourceResult = ConnectToSource(enableDiagnostics);
if (!sourceResult.Success)
return sourceResult;
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
try
{
Log(result, enableDiagnostics, "Starting QueryTemplate validation.");
SearchOrderDefinition definition =
SearchOrderParser.Parse(config.QueryTemplate);
int selectIndex = ResolveColumnIndex(
loadedHeaders,
definition.SelectColumn);
int whereIndex = ResolveColumnIndex(
loadedHeaders,
definition.WhereColumn);
Log(result, enableDiagnostics, "QueryTemplate parsed successfully.");
Log(result, enableDiagnostics, "Select column index: " + selectIndex);
Log(result, enableDiagnostics, "Where column index: " + whereIndex);
result.Success = true;
result.Message = "QueryTemplate validation finished successfully.";
}
catch (Exception ex)
{
result.Success = false;
result.Message = ex.Message;
Log(result, enableDiagnostics, "ERROR: " + ex.Message);
}
return result;
}
/// <summary>
/// Opens CSV file and loads its content.
/// Validates file existence and header.
/// </summary>
/// <param name="enableDiagnostics">
/// Enables detailed diagnostic output.
/// </param>
/// <returns>
/// Source validation result.
/// </returns>
private ReaderDiagnosticResult ConnectToSource(bool enableDiagnostics)
{
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
try
{
Log(result, enableDiagnostics, "Starting CSV source connection test.");
if (string.IsNullOrWhiteSpace(config.DataSource))
throw new InvalidOperationException("CSV data source is empty.");
resolvedPath = Path.GetFullPath(config.DataSource);
Log(result, enableDiagnostics, "Resolved full path: " + resolvedPath);
if (!File.Exists(resolvedPath))
throw new FileNotFoundException("CSV file was not found.", resolvedPath);
loadedLines = File.ReadAllLines(resolvedPath);
if (loadedLines == null || loadedLines.Length == 0)
throw new InvalidOperationException("CSV file is empty.");
loadedHeaders = SplitCsvLine(loadedLines[0]);
if (loadedHeaders == null || loadedHeaders.Length == 0)
throw new InvalidOperationException("CSV header is empty.");
Log(result, enableDiagnostics, "CSV line count: " + loadedLines.Length);
Log(result, enableDiagnostics, "CSV header column count: " + loadedHeaders.Length);
result.Success = true;
result.Message = "CSV source connection finished successfully.";
result.Data = loadedLines;
}
catch (Exception ex)
{
result.Success = false;
result.Message = ex.Message;
result.Data = null;
Log(result, enableDiagnostics, "ERROR: " + ex.Message);
}
return result;
}
/// <summary>
/// Executes configured QueryTemplate against loaded CSV data.
/// </summary>
/// <param name="query">
/// Query values used for matching.
/// </param>
/// <param name="enableDiagnostics">
/// Enables detailed diagnostic output.
/// </param>
/// <returns>
/// Query result.
/// </returns>
private ReaderDiagnosticResult ExecuteQuery(
DataQuery query,
bool enableDiagnostics)
{
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
try
{
if (query.QueryParams == null || query.QueryParams.Count == 0)
throw new InvalidOperationException("DataQuery.QueryParams is empty.");
SearchOrderDefinition definition =
SearchOrderParser.Parse(config.QueryTemplate);
int selectIndex = ResolveColumnIndex(
loadedHeaders,
definition.SelectColumn);
int whereIndex = ResolveColumnIndex(
loadedHeaders,
definition.WhereColumn);
List<string> matchedValues = new List<string>();
foreach (string param in query.QueryParams)
{
string queryValue = (param ?? string.Empty).Trim();
bool found = false;
for (int lineIndex = 1; lineIndex < loadedLines.Length; lineIndex++)
{
if (string.IsNullOrWhiteSpace(loadedLines[lineIndex]))
continue;
string[] values = SplitCsvLine(loadedLines[lineIndex]);
if (whereIndex >= values.Length)
continue;
string currentValue = (values[whereIndex] ?? string.Empty).Trim();
if (!string.Equals(
currentValue,
queryValue,
StringComparison.OrdinalIgnoreCase))
{
continue;
}
string returnValue =
selectIndex < values.Length
? values[selectIndex]
: string.Empty;
matchedValues.Add(returnValue);
found = true;
break;
}
if (!found)
Log(result, enableDiagnostics, "No match found for: " + queryValue);
}
result.Success = true;
if (query.QueryParams.Count == 1)
{
result.Data = matchedValues.Count > 0 ? matchedValues[0] : null;
result.Message = matchedValues.Count > 0
? "Value found."
: "No match found.";
}
else
{
result.Data = matchedValues;
result.Message =
"Batch query finished. Matches found: " +
matchedValues.Count +
" of " +
query.QueryParams.Count +
".";
}
}
catch (Exception ex)
{
result.Success = false;
result.Message = ex.Message;
result.Data = null;
Log(result, enableDiagnostics, "ERROR: " + ex.Message);
}
return result;
}
/// <summary>
/// Resolves column index either by name or by explicit index.
/// </summary>
/// <param name="headers">
/// CSV header columns.
/// </param>
/// <param name="columnReference">
/// Parsed QueryTemplate column definition.
/// </param>
/// <returns>
/// Zero-based column index.
/// </returns>
private int ResolveColumnIndex(
string[] headers,
ColumnReference columnReference)
{
if (columnReference == null)
throw new InvalidOperationException("Column reference is null.");
if (columnReference.HasIndex)
{
int index = columnReference.Index.Value;
if (index < 0 || index >= headers.Length)
throw new InvalidOperationException("Column index is out of range.");
return index;
}
if (columnReference.HasName)
{
for (int i = 0; i < headers.Length; i++)
{
if (string.Equals(
headers[i]?.Trim(),
columnReference.Name?.Trim(),
StringComparison.OrdinalIgnoreCase))
{
return i;
}
}
throw new InvalidOperationException(
"Column '" + columnReference.Name + "' was not found in CSV header.");
}
throw new InvalidOperationException("Column reference is not defined.");
}
/// <summary>
/// Splits CSV line using common separators.
/// Supports:
/// ';'
/// ','
/// '\t'
/// </summary>
/// <param name="line">
/// Input CSV line.
/// </param>
/// <returns>
/// Parsed columns.
/// </returns>
private string[] SplitCsvLine(string line)
{
if (string.IsNullOrEmpty(line))
return new string[0];
if (line.Contains(";"))
return line.Split(';');
if (line.Contains(","))
return line.Split(',');
if (line.Contains("\t"))
return line.Split('\t');
return new[] { line };
}
/// <summary>
/// Adds diagnostic line when diagnostics are enabled.
/// </summary>
private void Log(
ReaderDiagnosticResult result,
bool enableDiagnostics,
string message)
{
if (!enableDiagnostics || result == null)
return;
result.Diagnostics.Add(message);
}
}
}
@@ -1,397 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
{
/// <summary>
/// Reads data from SQL Server based data storage.
///
/// The reader uses configuration loaded from gci_config.json.
///
/// QueryTemplate must contain QUERYPARAM placeholder.
///
/// Example:
///
/// SELECT [Password]
/// FROM [dbo].[SkeletonKeys]
/// WHERE [PcbId] = QUERYPARAM
///
/// or
///
/// SELECT [ParameterName], [ParameterValue]
/// FROM [dbo].[PreAdjustmentCalibrationParams]
/// WHERE [Dn_InternalId] = QUERYPARAM
///
/// The placeholder is internally converted
/// to SQL parameter @value.
///
/// Supports both:
///
/// - Single-row lookups
/// - Multi-row result sets
/// </summary>
public class DatabaseDataStorageReader : IDataStorageReader
{
/// <summary>
/// Data storage configuration containing
/// connection string and query template.
/// </summary>
private readonly DataStorageConfig config;
/// <summary>
/// Creates SQL Server data storage reader
/// using provided configuration.
/// </summary>
/// <param name="config">
/// Data storage configuration loaded
/// from gci_config.json.
/// </param>
public DatabaseDataStorageReader(
DataStorageConfig config)
{
this.config =
config ?? throw new ArgumentNullException(nameof(config));
}
/// <summary>
/// Executes configured SQL query and returns
/// matching database records.
///
/// Single-row queries populate:
/// DatabaseSearchResult.Values
///
/// Multi-row queries populate:
/// DatabaseSearchResult.Rows
///
/// For backward compatibility, the first row
/// is also stored in Values.
/// </summary>
/// <param name="query">
/// Query object containing lookup parameter.
/// </param>
/// <returns>
/// DatabaseSearchResult containing returned
/// database records.
///
/// Values contains the first returned row.
///
/// Rows contains the complete result set.
/// </returns>
public object GetData(DataQuery query)
{
if (query == null)
throw new ArgumentNullException(nameof(query));
ReaderDiagnosticResult sourceResult =
TestSource(true);
if (!sourceResult.Success)
throw new InvalidOperationException(sourceResult.Message);
string sqlText =
PrepareSqlText(config.QueryTemplate);
object queryValue =
ExtractQueryValue(query);
using (SqlConnection connection =
new SqlConnection(config.DataSource))
using (SqlCommand command =
new SqlCommand(sqlText, connection))
{
AddQueryParameter(command, queryValue);
connection.Open();
// Full result set is required because some
// storage definitions return multiple records
// (e.g. PreAdjustmentCalibrationParams).
using (SqlDataReader reader =
command.ExecuteReader())
{
DatabaseSearchResult result =
new DatabaseSearchResult
{
Query = sqlText
};
while (reader.Read())
{
result.Found = true;
// Represents one database row.
Dictionary<string, object> row =
new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++)
{
object value =
reader.GetValue(i);
object normalizedValue =
value == DBNull.Value
? null
: value;
row[reader.GetName(i)] =
normalizedValue;
}
// Preserve first row for legacy consumers
// expecting a single returned database record
// (e.g. MeterLoginPasswordReader).
if (result.Rows.Count == 0)
{
foreach (var item in row)
{
result.Values[item.Key] =
item.Value;
}
}
// Store complete database result set.
result.Rows.Add(row);
}
if (result.Rows.Count == 0)
{
result.Found = false;
}
return result;
}
}
}
/// <summary>
/// Tests whether SQL Server connection
/// can be opened.
/// </summary>
/// <param name="enableDiagnostics">
/// Enables detailed diagnostic output.
/// </param>
/// <returns>
/// Diagnostic result of SQL connection test.
/// </returns>
public ReaderDiagnosticResult TestSource(
bool enableDiagnostics)
{
ReaderDiagnosticResult result =
new ReaderDiagnosticResult();
try
{
if (string.IsNullOrWhiteSpace(config.DataSource))
{
throw new InvalidOperationException(
"Data source is empty.");
}
Log(
result,
enableDiagnostics,
"Opening SQL connection.");
using (SqlConnection connection =
new SqlConnection(config.DataSource))
{
connection.Open();
Log(
result,
enableDiagnostics,
"Connection opened successfully.");
using (SqlCommand command =
new SqlCommand("SELECT 1", connection))
{
object value =
command.ExecuteScalar();
Log(
result,
enableDiagnostics,
"Test query result: " + value);
}
}
result.Success = true;
result.Message =
"Connection to SQL Server OK.";
}
catch (Exception ex)
{
result.Success = false;
result.Message =
"Failed to connect to SQL Server. " + ex.Message;
Log(
result,
enableDiagnostics,
ex.ToString());
}
return result;
}
/// <summary>
/// Tests whether configured SQL query
/// can be prepared and executed.
/// </summary>
/// <param name="enableDiagnostics">
/// Enables detailed diagnostic output.
/// </param>
/// <returns>
/// Diagnostic result of query execution test.
/// </returns>
public ReaderDiagnosticResult TestQuery(
bool enableDiagnostics)
{
ReaderDiagnosticResult result =
new ReaderDiagnosticResult();
try
{
if (string.IsNullOrWhiteSpace(config.QueryTemplate))
{
throw new InvalidOperationException(
"Query template is empty.");
}
string sqlText =
PrepareSqlText(config.QueryTemplate);
Log(result, enableDiagnostics, "Original template:");
Log(result, enableDiagnostics, config.QueryTemplate);
Log(result, enableDiagnostics, "Prepared SQL:");
Log(result, enableDiagnostics, sqlText);
using (SqlConnection connection =
new SqlConnection(config.DataSource))
using (SqlCommand command =
new SqlCommand(sqlText, connection))
{
AddQueryParameter(command, "TEST");
connection.Open();
object value =
command.ExecuteScalar();
result.Data = value;
Log(
result,
enableDiagnostics,
"Query executed successfully.");
}
result.Success = true;
result.Message =
"Query executed successfully.";
}
catch (Exception ex)
{
result.Success = false;
result.Message =
"Query execution failed. " + ex.Message;
Log(
result,
enableDiagnostics,
ex.ToString());
}
return result;
}
/// <summary>
/// Converts configured QueryTemplate
/// to executable SQL text.
/// </summary>
/// <param name="queryTemplate">
/// SQL query template containing
/// QUERYPARAM placeholder.
/// </param>
/// <returns>
/// SQL text with QUERYPARAM replaced
/// by @value parameter.
/// </returns>
private static string PrepareSqlText(
string queryTemplate)
{
if (string.IsNullOrWhiteSpace(queryTemplate))
{
throw new ArgumentException(
"Query template must not be empty.",
nameof(queryTemplate));
}
if (!queryTemplate.Contains("QUERYPARAM"))
{
throw new InvalidOperationException(
"Query template must contain QUERYPARAM placeholder.");
}
return queryTemplate.Replace(
"QUERYPARAM",
"@value");
}
/// <summary>
/// Extracts first query parameter value.
/// </summary>
private static object ExtractQueryValue(
DataQuery query)
{
if (query.QueryParams == null ||
query.QueryParams.Count == 0)
{
throw new InvalidOperationException(
"DataQuery does not contain any query parameter.");
}
return query.QueryParams[0];
}
/// <summary>
/// Adds lookup parameter to SQL command.
/// </summary>
private static void AddQueryParameter(
SqlCommand command,
object value)
{
command.Parameters.Clear();
SqlParameter parameter =
command.Parameters.Add(
"@value",
SqlDbType.Variant);
parameter.Value =
value ?? DBNull.Value;
}
/// <summary>
/// Adds diagnostic line when diagnostics
/// are enabled.
/// </summary>
private static void Log(
ReaderDiagnosticResult result,
bool enableDiagnostics,
string message)
{
if (!enableDiagnostics || result == null)
return;
result.Diagnostics.Add(message);
}
}
}
@@ -1,31 +0,0 @@
using System;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
{
public class JsonDataStorageReader : IDataStorageReader
{
private readonly DataStorageConfig config;
public JsonDataStorageReader(DataStorageConfig config)
{
this.config = config ?? throw new ArgumentNullException(nameof(config));
}
public object GetData(DataQuery query)
{
throw new NotImplementedException();
}
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
{
throw new NotImplementedException();
}
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
{
throw new NotImplementedException();
}
}
}
@@ -1,31 +0,0 @@
using System;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
{
public class RestApiDataStorageReader : IDataStorageReader
{
private readonly DataStorageConfig config;
public RestApiDataStorageReader(DataStorageConfig config)
{
this.config = config ?? throw new ArgumentNullException(nameof(config));
}
public object GetData(DataQuery query)
{
throw new NotImplementedException();
}
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
{
throw new NotImplementedException();
}
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
{
throw new NotImplementedException();
}
}
}
@@ -1,125 +0,0 @@
using System;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Searching
{
/// <summary>
/// Parsed representation of QueryTemplate expression.
///
/// Example:
///
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
///
/// becomes:
///
/// SelectColumn:
/// [Password]
///
/// WhereColumn:
/// [PcbId]
/// </summary>
public class SearchOrderDefinition
{
/// <summary>
/// Column to be returned from lookup result.
///
/// Example:
///
/// SELECT [Password]
///
/// returns:
///
/// [Password]
/// </summary>
public ColumnReference SelectColumn { get; set; }
/// <summary>
/// Column used for row matching.
///
/// Example:
///
/// WHERE [PcbId] = QUERYPARAM
///
/// uses:
///
/// [PcbId]
/// </summary>
public ColumnReference WhereColumn { get; set; }
}
/// <summary>
/// Represents one column definition inside QueryTemplate.
///
/// Supports two forms:
///
/// [ColumnName]
///
/// or:
///
/// COLUMN(number)
///
/// Examples:
///
/// [Password]
///
/// COLUMN(2)
/// </summary>
public class ColumnReference
{
/// <summary>
/// Column name used in named lookup mode.
///
/// Example:
///
/// [Password]
/// </summary>
public string Name { get; set; }
/// <summary>
/// Zero-based column index used in indexed lookup mode.
///
/// Example:
///
/// COLUMN(2)
/// </summary>
public int? Index { get; set; }
/// <summary>
/// Indicates whether column uses name-based lookup.
/// </summary>
public bool HasName
{
get
{
return !string.IsNullOrWhiteSpace(Name);
}
}
/// <summary>
/// Indicates whether column uses index-based lookup.
/// </summary>
public bool HasIndex
{
get
{
return Index.HasValue;
}
}
/// <summary>
/// Returns human readable representation.
/// </summary>
/// <returns>
/// Column expression text.
/// </returns>
public override string ToString()
{
if (HasName)
return "[" + Name + "]";
if (HasIndex)
return "COLUMN(" + Index.Value + ")";
return "<undefined column reference>";
}
}
}
@@ -1,167 +0,0 @@
using System;
using System.Text.RegularExpressions;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Searching
{
/// <summary>
/// Parses SQL-like QueryTemplate expressions used by GCI data storage readers.
///
/// Supported syntax:
///
/// SELECT [ReturnColumn] WHERE [MatchColumn] = QUERYPARAM
///
/// Examples:
///
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
///
/// SELECT COLUMN(1) WHERE COLUMN(0) = QUERYPARAM
///
/// Mixed forms are also supported:
///
/// SELECT [Password] WHERE COLUMN(0)=QUERYPARAM
///
/// The parser converts text expressions into structured
/// SearchOrderDefinition objects which are later consumed
/// by CSV and other readers.
/// </summary>
public static class SearchOrderParser
{
/// <summary>
/// Full QueryTemplate validation pattern.
///
/// Expected syntax:
///
/// SELECT [Column] WHERE [Column] = QUERYPARAM
/// </summary>
private static readonly Regex FullPattern = new Regex(
@"^\s*SELECT\s+(?<select>\[[^\]]+\]|COLUMN\(\d+\))\s+WHERE\s+(?<where>\[[^\]]+\]|COLUMN\(\d+\))\s*=\s*QUERYPARAM\s*$",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
/// <summary>
/// Matches:
///
/// [ColumnName]
/// </summary>
private static readonly Regex NamedColumnPattern = new Regex(
@"^\[(?<name>[^\]]+)\]$",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
/// <summary>
/// Matches:
///
/// COLUMN(number)
/// </summary>
private static readonly Regex IndexedColumnPattern = new Regex(
@"^COLUMN\((?<index>\d+)\)$",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
/// <summary>
/// Parses QueryTemplate text into structured definition.
///
/// Example:
///
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
///
/// Result:
///
/// SelectColumn:
/// Password
///
/// WhereColumn:
/// PcbId
/// </summary>
/// <param name="searchOrder">
/// QueryTemplate expression.
/// </param>
/// <returns>
/// Parsed query definition.
/// </returns>
public static SearchOrderDefinition Parse(string searchOrder)
{
if (string.IsNullOrWhiteSpace(searchOrder))
throw new InvalidOperationException(
"QueryTemplate is empty.");
Match match = FullPattern.Match(searchOrder);
if (!match.Success)
{
throw new InvalidOperationException(
"Invalid QueryTemplate syntax. Expected: SELECT [ReturnColumn] WHERE [MatchColumn] = QUERYPARAM");
}
return new SearchOrderDefinition
{
SelectColumn =
ParseColumnReference(
match.Groups["select"].Value),
WhereColumn =
ParseColumnReference(
match.Groups["where"].Value)
};
}
/// <summary>
/// Parses single column definition.
///
/// Supported forms:
///
/// [ColumnName]
///
/// COLUMN(number)
/// </summary>
/// <param name="token">
/// Raw token extracted from QueryTemplate.
/// </param>
/// <returns>
/// Parsed column reference.
/// </returns>
private static ColumnReference ParseColumnReference(
string token)
{
if (string.IsNullOrWhiteSpace(token))
throw new InvalidOperationException(
"Column reference token is empty.");
Match nameMatch =
NamedColumnPattern.Match(token);
if (nameMatch.Success)
{
return new ColumnReference
{
Name =
nameMatch
.Groups["name"]
.Value
.Trim(),
Index = null
};
}
Match indexMatch =
IndexedColumnPattern.Match(token);
if (indexMatch.Success)
{
return new ColumnReference
{
Name = null,
Index =
int.Parse(
indexMatch
.Groups["index"]
.Value)
};
}
throw new InvalidOperationException(
"Invalid column reference '" +
token +
"'. Use [ColumnName] or COLUMN(number).");
}
}
}
@@ -1,51 +0,0 @@
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using System.Threading;
using System.Threading.Tasks;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords
{
/// <summary>
/// Defines contract for reading meter login passwords
/// from configured GCI data storage.
///
/// Implementations hide storage details and provide
/// a unified API for password lookup.
///
/// Supported storage types:
/// - SQL database
/// - CSV
/// - JSON
/// - REST API
/// </summary>
public interface IMeterLoginPasswordReader
{
/// <summary>
/// Reads meter login password synchronously.
/// </summary>
/// <param name="queryParam">
/// PCB ID or another configured lookup value.
/// </param>
/// <returns>
/// Password if found; otherwise null.
/// </returns>
string GetPassword(DataQuery query);
/// <summary>
/// Reads meter login password asynchronously.
/// Thread-safe implementation may serialize
/// access to the underlying storage.
/// </summary>
/// <param name="queryParam">
/// PCB ID or another configured lookup value.
/// </param>
/// <param name="token">
/// Cancellation token.
/// </param>
/// <returns>
/// Password if found; otherwise null.
/// </returns>
Task<string> ReadMeterLoginPasswordAsync(
DataQuery query,
CancellationToken token = default);
}
}
@@ -1,126 +0,0 @@
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
using GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords;
using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords
{
/// <summary>
/// Reads meter login password from configured GCI data storage.
/// </summary>
public class MeterLoginPasswordReader : IMeterLoginPasswordReader
{
/// <summary>
/// Universal data storage reader selected by configuration.
/// Can represent database, CSV, JSON or REST reader.
/// </summary>
private readonly IDataStorageReader reader;
/// <summary>
/// Ensures that only one password lookup is executed at a time.
/// </summary>
private readonly SemaphoreSlim readLock = new SemaphoreSlim(1, 1);
/// <summary>
/// Creates password reader using provided data storage configuration.
/// </summary>
/// <param name="config">Data storage configuration loaded from gci_config.json.</param>
public MeterLoginPasswordReader(DataStorageConfig config)
{
reader = DataStorageReaderFactory.Create(config);
}
/// <summary>
/// Asynchronously reads meter login password using
/// the provided data query.
///
/// Thread-safe implementation may serialize
/// access to the underlying storage.
/// </summary>
/// <param name="query">
/// Data query containing lookup parameter values.
/// </param>
/// <param name="token">
/// Cancellation token.
/// </param>
/// <returns>
/// Password if found; otherwise null.
/// </returns>
public async Task<string> ReadMeterLoginPasswordAsync(
DataQuery query,
CancellationToken token = default)
{
if (query == null)
throw new ArgumentNullException(nameof(query));
if (query.QueryParams.Count == 0)
throw new ArgumentException(
"Query does not contain any parameter.",
nameof(query));
await readLock.WaitAsync(token);
try
{
return await Task.Run(
() => GetPassword(query),
token);
}
finally
{
readLock.Release();
}
}
/// <summary>
/// Reads meter login password using
/// the provided data query.
/// </summary>
/// <param name="query">
/// Data query containing lookup parameter values.
/// </param>
/// <returns>
/// Password if found; otherwise null.
/// </returns>
public string GetPassword(
DataQuery query)
{
if (query == null)
throw new ArgumentNullException(nameof(query));
if (query.QueryParams.Count == 0)
throw new ArgumentException(
"Query does not contain any parameter.",
nameof(query));
object result =
reader.GetData(query);
if (result is ReaderDiagnosticResult diagnosticResult)
{
return diagnosticResult.Data?.ToString();
}
if (result is DatabaseSearchResult dbResult)
{
if (!dbResult.Found ||
dbResult.Values == null ||
dbResult.Values.Count == 0)
{
return null;
}
foreach (object value in dbResult.Values.Values)
{
return value?.ToString();
}
}
return result?.ToString();
}
}
}
@@ -1,62 +0,0 @@
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams
{
/// <summary>
/// Provides access to pre-adjustment calibration parameters
/// stored in the configured GCI data source.
///
/// Expected structure:
///
/// MeterSize | ParameterName | ParameterValue
///
/// Example:
///
/// 2 | GENESISFLOW_MinValidToF | 21990232
/// 2 | GENESISFLOW_Timeout | 1
///
/// Parameters are grouped by MeterSize and returned
/// as key/value pairs where:
///
/// Key = ParameterName
/// Value = ParameterValue
/// </summary>
public interface IPreAdjustmentCalibrationParamsReader
{
/// <summary>
/// Reads all pre-adjustment calibration parameters
/// using the provided data query synchronously.
/// </summary>
/// <param name="query">
/// Data query containing meter size as lookup parameter.
/// </param>
/// <returns>
/// Dictionary where key is GENESISFLOW parameter name
/// and value is stored parameter value.
/// </returns>
Dictionary<string, UInt32> GetCalibrationParams(
DataQuery query);
/// <summary>
/// Reads all pre-adjustment calibration parameters
/// using the provided data query asynchronously.
/// </summary>
/// <param name="query">
/// Data query containing meter size as lookup parameter.
/// </param>
/// <param name="token">
/// Cancellation token.
/// </param>
/// <returns>
/// Dictionary where key is GENESISFLOW parameter name
/// and value is stored parameter value.
/// </returns>
Task<Dictionary<string, UInt32>> ReadCalibrationParamsAsync(
DataQuery query,
CancellationToken token = default);
}
}
@@ -1,305 +0,0 @@
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams
{
/// <summary>
/// Reads pre-adjustment calibration parameters
/// from configured GCI data storage.
///
/// The underlying storage type is selected
/// through IDataStorageReader and may represent:
///
/// - SQL database
/// - CSV
/// - JSON
/// - REST API
///
/// Expected storage structure:
///
/// MeterSize | ParameterName | ParameterValue
///
/// Example:
///
/// 2 | GENESISFLOW_MinValidToF | 21990232
/// 2 | GENESISFLOW_Timeout | 1
///
/// Returned data are represented as:
///
/// Key = ParameterName
/// Value = ParameterValue
/// </summary>
public class PreAdjustmentCalibrationParamsReader
: IPreAdjustmentCalibrationParamsReader
{
/// <summary>
/// Universal data storage reader selected by configuration.
/// Can represent database, CSV, JSON or REST reader.
/// </summary>
private readonly IDataStorageReader reader;
/// <summary>
/// Ensures that only one calibration parameter lookup
/// is executed at a time.
/// </summary>
private readonly SemaphoreSlim readLock =
new SemaphoreSlim(1, 1);
/// <summary>
/// Creates calibration parameter reader using
/// provided data storage configuration.
/// </summary>
/// <param name="config">
/// Data storage configuration loaded from gci_config.json.
/// </param>
public PreAdjustmentCalibrationParamsReader(
DataStorageConfig config)
{
if (config == null)
throw new ArgumentNullException(nameof(config));
reader = DataStorageReaderFactory.Create(config);
}
/// <summary>
/// Reads all pre-adjustment calibration parameters
/// using the provided data query asynchronously.
///
/// The query is expected to contain meter size
/// as the first lookup parameter.
/// </summary>
/// <param name="query">
/// Data query containing lookup parameter values.
/// </param>
/// <param name="token">
/// Cancellation token.
/// </param>
/// <returns>
/// Dictionary:
///
/// Key:
/// GENESISFLOW parameter name
///
/// Value:
/// Stored parameter value
/// </returns>
public async Task<Dictionary<string, UInt32>> ReadCalibrationParamsAsync(
DataQuery query,
CancellationToken token = default)
{
ValidateQuery(query);
return await Task.Run(
() => GetCalibrationParams(query),
token).ConfigureAwait(false); ;
/*await readLock.WaitAsync(token);
try
{
return await Task.Run(
() => GetCalibrationParams(query),
token);
}
finally
{
readLock.Release();
}*/
}
/// <summary>
/// Reads all pre-adjustment calibration parameters
/// using the provided data query.
///
/// The query is expected to contain meter size
/// as the first lookup parameter.
/// </summary>
/// <param name="query">
/// Data query containing lookup parameter values.
/// </param>
/// <returns>
/// Dictionary:
///
/// Key:
/// GENESISFLOW parameter name
///
/// Value:
/// Stored parameter value
/// </returns>
public Dictionary<string, UInt32> GetCalibrationParams(
DataQuery query)
{
ValidateQuery(query);
object result =
reader.GetData(query);
return ConvertResultToDictionary(result);
}
/// <summary>
/// Validates that data query exists
/// and contains at least one lookup parameter.
/// </summary>
/// <param name="query">
/// Data query to validate.
/// </param>
private static void ValidateQuery(
DataQuery query)
{
if (query == null)
throw new ArgumentNullException(nameof(query));
if (query.QueryParams == null ||
query.QueryParams.Count == 0)
{
throw new ArgumentException(
"Query does not contain any parameter.",
nameof(query));
}
}
/// <summary>
/// Converts provider-specific result objects
/// returned by IDataStorageReader into a unified
/// dictionary representation.
/// </summary>
/// <param name="result">
/// Raw result returned by the configured storage reader.
/// </param>
/// <returns>
/// Dictionary containing calibration parameter
/// name/value pairs.
/// </returns>
private Dictionary<string, UInt32> ConvertResultToDictionary(
object result)
{
Dictionary<string, UInt32> values =
new Dictionary<string, UInt32>();
if (result == null)
return values;
if (result is DatabaseSearchResult dbResult)
{
if (!dbResult.Found)
return values;
foreach (Dictionary<string, object> row in dbResult.Rows)
{
if (!row.TryGetValue("ParameterName", out object parameterNameObject))
continue;
if (!row.TryGetValue("ParameterValue", out object parameterValueObject))
continue;
string parameterName =
parameterNameObject?.ToString();
if (string.IsNullOrWhiteSpace(parameterName))
continue;
if (TryConvertToUInt32(parameterValueObject, out UInt32 parameterValue))
{
values[parameterName] =
parameterValue;
}
}
return values;
}
if (result is ReaderDiagnosticResult diagnosticResult)
{
if (diagnosticResult.Data is Dictionary<string, UInt32> uintDictionary)
return uintDictionary;
if (diagnosticResult.Data is Dictionary<string, string> stringDictionary)
{
foreach (var item in stringDictionary)
{
if (TryConvertToUInt32(item.Value, out UInt32 value))
values[item.Key] = value;
}
return values;
}
if (diagnosticResult.Data is Dictionary<string, object> objectDictionary)
{
foreach (var item in objectDictionary)
{
if (TryConvertToUInt32(item.Value, out UInt32 value))
values[item.Key] = value;
}
return values;
}
}
if (result is Dictionary<string, UInt32> directUIntDictionary)
return directUIntDictionary;
if (result is Dictionary<string, string> directStringDictionary)
{
foreach (var item in directStringDictionary)
{
if (TryConvertToUInt32(item.Value, out UInt32 value))
values[item.Key] = value;
}
return values;
}
if (result is Dictionary<string, object> directObjectDictionary)
{
foreach (var item in directObjectDictionary)
{
if (TryConvertToUInt32(item.Value, out UInt32 value))
values[item.Key] = value;
}
}
return values;
}
private static bool TryConvertToUInt32(
object value,
out UInt32 result)
{
result = 0;
if (value == null)
return false;
if (value is UInt32 uintValue)
{
result = uintValue;
return true;
}
if (value is int intValue && intValue >= 0)
{
result = Convert.ToUInt32(intValue);
return true;
}
if (value is long longValue &&
longValue >= 0 &&
longValue <= UInt32.MaxValue)
{
result = Convert.ToUInt32(longValue);
return true;
}
return UInt32.TryParse(
value.ToString(),
out result);
}
}
}

Some files were not shown because too many files have changed in this diff Show More