Compare commits

..
Author SHA1 Message Date
michal 5313f0a04b Version iteration 3.9.3135.0 2026-07-31 14:04:59 +02:00
michal 9557df0cef Added detailed debug logging for CLI command execution and adjusted debug path in SerialPortData. 2026-07-28 14:02:01 +02:00
michal 71ff74122e camera send implemenatation, dialog support, ver 3.9.3134 2026-07-15 16:12:21 +02:00
michal 34141481e3 Camera ProcedureParams - Added new parameters in Proces Camera Tab 2026-07-14 14:42:47 +02:00
michal f2e6807349 camera advance fix, ver 3.9.3133.0 2026-07-14 14:31:01 +02:00
michal 977cd4ec95 new version number 3.9.3132.0, safe grab image reading 2026-07-13 22:20:13 +02:00
michal 585a199f53 camera image grabber improved - takes more attempts and retries, 2026-07-13 21:34:49 +02:00
michal 50298d493d Verson Iterantion 3.9.3131.0 2026-07-13 13:47:54 +02:00
michal 785c8c1881 GrabImage in Detect Camera to warm up step 3.1 2026-07-13 13:45:50 +02:00
michal 08e1ba695e Live Stream Woring - ConnectCameraAsync - more atepts 2026-07-13 12:59:02 +02:00
michal 38abca2e8a fix camera functionality now using CameraLineReader for quick buffered read 2026-07-13 09:03:53 +02:00
michal 7129960b12 store temp changes 2026-07-02 16:45:57 +02:00
michal 1fddd87f2b Update DataSource in gci_config.json to new server and database configuration 2026-06-18 11:14:10 +02:00
michal 19ff06b01e PreAdjustment preparation 2026-06-09 16:23:00 +02:00
michal d73eac8332 Merge branch 'develop/SLM-PT50_genesisDirectDecode' into develop/SLM-PT50_genesisDirectDecode_special 2026-06-08 09:17:46 +02:00
michal 63041c0fc9 Increment assembly version to 3.9.3130.0, replace iPerl_Communication_in_progress with Meter_Communication_in_progress across resources and sequences, and update string resources for improved clarity and consistency. 2026-06-04 14:56:56 +02:00
michal 77c131f414 Increment assembly version to 3.9.3129.0, streamline context menu options in iPerlCommunicationForm, consolidate unused menu items, improve PBC_Read_Build_Async with retry logic for slot and PCB ID operations, and optimize GenesisSmartReader communication and debugging. 2026-06-04 11:00:20 +02:00
michal 4173512ecc Increment assembly version to 3.9.3128.0, enhance thread handling for iPerlCommunicationForm with improved delay logic and UI checks, and replace ReadRegisterAsync with ReadRegisterWithRetryAsync for more robust Genesis register operations. 2026-06-04 08:16:13 +02:00
michal c991d530d5 Increment assembly version to 3.9.3126.0. 2026-06-03 12:46:18 +02:00
michal 2ba0bfe2b1 Enhance PrepareSlotQ3Calibration and WriteSlotQ3Calibration methods with improved logging, refined error handling, and additional debug details for iHead operations. 2026-06-03 12:45:03 +02:00
michal 13490009a3 Increment assembly version to 3.9.3125.3. 2026-06-03 10:43:22 +02:00
michal c6849deaef Improve Q3Channel handling by refining water meter result copying logic, enhancing error handling, and updating calibration factor processing for multi-channel scenarios. 2026-06-03 10:27:45 +02:00
michal 0b7dd2bafa Store Q3Channel to DB
Refine water meter processing logic by adding Q3Channel checks and adjusting removal and test result evaluation conditions.
2026-06-03 09:50:31 +02:00
michal 2d8cc912a3 Increment assembly version to 3.9.3124.3, implement GetEachMeterTestRslt method, enhance error handling in calibration-related methods, refine Q3 calibration data simulation and storage logic, and improve debug logging across Genesis operations. 2026-06-03 09:49:34 +02:00
michal 0b246b2ff5 Merge branch 'develop/SLM-PT50_genesisDirectDecode_special_3121_17969' into develop/SLM-PT50_genesisDirectDecode_special
# Conflicts:
#	TBF/Rig/RegisterReaders/GenesisRegReader/communication/OptoHeadTest.cs
2026-05-29 15:52:16 +02:00
michal 8b025a0fca Increment assembly version to 3.9.3121.3, enhance debug logging in PrepareQ3Calibration and related methods, improve calibration factor handling with multi-value support, add exception handling for simulated DB responses, and refine Q3 calibration logic in GenesisSmartReader. 2026-05-29 15:40:20 +02:00
michal 0f407920ac Update predefined calibration value in PrepareMeterSizeAndCalibration from 15625 to 17969. 2026-05-29 08:51:51 +02:00
michal 347a8798b8 Enhance Q3 calibration logic by refining PrepareSlotQ3Calibration and WriteSlotQ3Calibration methods, improving handling of TestRsltCalibFactor storage across multiple channels, and extending exception handling and debug logging for Genesis operations. 2026-05-28 14:29:02 +02:00
michal 3cd488fc76 Increment assembly version to 3.9.3121.1, add FailedPrepare error constant and handling in iPerlCommunicationForm, enhance PrepareSlotQ3Calibration and WriteSlotQ3Calibration with exception handling and logging, introduce IsCalibFactorValid property and mappings for calibration logic, and extend debug logging across Genesis operations. 2026-05-27 14:00:40 +02:00
michal e88e441875 Refactor preadjustmenActionsViewButton to preadjustmentActionsViewButton, add Q3Channel property to entities and mappings, implement schema migrations for Q3Channel, update GenesisSmartReader Q3Channel logic, replace file paths for clarity, upgrade projects to .NET Framework 4.8, and extend debug logging. 2026-05-25 09:21:32 +02:00
michal 89cfcb75e8 Merge branch 'develop/SLM-PT50_genesisDirectDecode' into develop/SLM-PT50_genesisDirectDecode_special
# Conflicts:
#	GenesisCordonelInterface/UI/LaatzenAPI_CordonelPreadjustmentUI/FrmCordonelPreadjustmentUI.cs
#	TBF/TBF.csproj
2026-05-22 12:58:48 +02:00
michal db90a096e3 Add calibration factor support to Genesis, implement TestRsltCalibFactor entity, related mappings, table creation logic, and data persistence methods. Extend GenesisSmartReader with Q3 calibration enhancements and introduce new debug logging. 2026-05-22 09:18:41 +02:00
michal 6f919ede9b Increment assembly version to 3.9.3104.1, add CheckMeterPrepare functionality and CheckMeterPrepareStr constant in iPerlCommunicationForm, implement error handling and UI safeguarding for communication events, introduce TriggerIdle constant in RadioService, and enhance GenesisSmartReader with CheckMeterPrepare logic and improved debug logging. 2026-05-19 15:21:20 +02:00
michal 44b6060113 Increment assembly version to 3.9.3103.1, add HoldSlotStr constant and HoldOn functionality in iPerlCommunicationForm, implement message box synchronization for multi-threaded operations, enhance slot operation handling with ProcessWaterMetersDialogConfirmation, and improve debug logging and error management in Genesis operations. 2026-05-18 16:23:00 +02:00
michal fde090c437 Increment assembly version to 3.9.3102.1, enhance FlyingStartMassCollectionSeq by removing unused code, improve error handling for flow calculations in FlyingStartSeq, extend GenesisSmartReader integration, and introduce WaterMeterParentCopy method for multi-channel handling in test sequences. 2026-05-15 13:02:09 +02:00
michal 0af975f670 Increment assembly version to 3.9.3101.1, increase thread support to 10 across relevant configurations, extend Genesis communication ranges, enhance debug logging for slot initialization, and update Genesis configuration controls with parallel access parameters. 2026-05-15 10:39:37 +02:00
michal 6d0d2cd34e Increment assembly version to 3.9.3100.1. 2026-05-15 08:02:46 +02:00
michal d9a14ea3e1 Increment assembly version to 3.9.3098.1, add Q3 calibration support in iPerlCommunicationForm, implement PrepareSlotQ3Calibration and WriteSlotQ3Calibration methods in OptoHeadTest, extend GenesisSmartReader with Q3 calibration logic, and update slot operation and test sequence handling to include calibration-related data. 2026-05-15 08:02:28 +02:00
michal 2526e997e3 Increment assembly version to 3.9.3097.1, add SlotGroupedLogin support in iPerlCommunicationForm, implement GroupedLoginSlot method in OptoHeadTest, and update slot operation logic with enhanced error messaging. 2026-05-13 12:57:23 +02:00
michal d346dcfa03 Merge branch 'develop/SLM-PT50_genesisDirectDecode' into develop/SLM-PT50_genesisDirectDecode_special
# Conflicts:
#	GenesisCordonelInterface/API/InterfaceGCIToLaatzen.cs
#	TBF/Rig/BridgeComponents/GciBridge/GciBridge.cs
#	TBF/TBF.csproj
2026-05-13 10:03:46 +02:00
michal 8437952478 Increment assembly version to 3.9.3095.1, expand iPerlCommunicationForm with new slot-related constants and error definitions, implement slot handling methods in OptoHeadTest, introduce PcbReadResult for PCB reading flow, and enhance connection and slot operation logic. 2026-05-13 09:48:41 +02:00
michal 34bf5d5473 Increment assembly version to 3.9.3094.1, extend iPerlCommunicationForm with new context menu options and associated slot handling, introduce bend detection support in Genesis streaming protocol, add data members for radio configuration, and enhance asynchronous operations in JSON handling. 2026-05-12 16:03:28 +02:00
michal 6c4c7e73bb Merge branch 'develop/SLM-PT50_genesisDirectDecode' into develop/SLM-PT50_genesisDirectDecode_special 2026-05-12 14:00:42 +02:00
michal f97c406ae8 Update DLL references for Xylem.Common components across hardware and metrology modules. 2026-05-12 07:51:02 +02:00
michal 9cfadb8075 Merge remote-tracking branch 'private-origin/develop/SLM-PT50_genesisDirectDecode_special' into develop/SLM-PT50_genesisDirectDecode_special 2026-05-12 07:50:18 +02:00
michal 1709ef842d Refactor iPerlCommunication module to integrate GenesisSmartReader, update related logic and methods, optimize asynchronous operations, and increment assembly version to 3.9.3091.1. 2026-05-12 07:50:03 +02:00
michal 8e694dc910 Refactor iPerlCommunication module to integrate GenesisSmartReader, update related logic and methods, optimize asynchronous operations, and increment assembly version to 3.9.3091.1. 2026-05-12 07:49:13 +02:00
michal 0b1e09e0df Merge branch 'develop/SLM-PT50_genesisDirectDecode' into develop/SLM-PT50_genesisDirectDecode_special
# Conflicts:
#	TBF/TBF.csproj
2026-05-11 17:29:13 +02:00
michal 84307f4bf4 Merge branch 'develop/SLM-PT50_genesisDirectDecode' into develop/SLM-PT50_genesisDirectDecode_special 2026-05-08 12:25:23 +02:00
michal 463fb16b23 Remove iPerlCommunication module entirely.
Deleted all related classes, configurations, and implementations for the `iPerlCommunication` module, including `TestMethod`, `TestMethodCfg`, and associated components. This cleanup reflects either a deprecated or unused feature.
2026-05-08 12:24:42 +02:00
michal b4c0384b57 Refactor RadioService for improved asynchronous handling, consolidate unused namespaces, update OptoHeadTest logic, and increment assembly version to 3.9.3085.1. 2026-05-08 10:44:27 +02:00
michal f4b8e42f59 Merge branch 'develop/SLM-PT50_genesisDirectDecode' into develop/SLM-PT50_genesisDirectDecode_special
# Conflicts:
#	TBF/Rig/RegisterReaders/GenesisRegReader/communication/RadioService.cs
2026-05-07 21:37:05 +02:00
michal abee5fa6b1 Update DLL references for Xylem.Common components across multiple modules. 2026-05-07 21:33:41 +02:00
michal b8926ef93f Add unit tests for OptoHeadTest handling simulated Genesis readers, refine project references, and increment assembly version to 3.9.3079.1. 2026-05-07 21:30:19 +02:00
michal 502804c2de Merge remote-tracking branch 'private-origin/develop/SLM-PT50_genesisDirectDecode' into develop/SLM-PT50_genesisDirectDecode 2026-05-06 15:32:41 +02:00
michal 296621a590 * Increment assembly version to 3.9.3069.1
* Logs update
2026-05-06 15:32:33 +02:00
1191 changed files with 12150 additions and 550947 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>
+1 -64
View File
@@ -1,10 +1,3 @@
# Generated build outputs for all projects, including Debug/Release archives.
[Bb]in/
[Oo]bj/
# Generated test results and hardware diagnostic captures.
[Tt]est[Rr]esults/
AppDiagnostic/bin/
AppDiagnostic/obj/
Common/bin/
@@ -100,8 +93,7 @@ Users/obj/
TBF.sln.DotSettings.user
/ExternalProjects
/GenesisCordonelTester
/packages/*
!/packages/Common/
/packages
/GenesisCordonelInterface/bin/Debug/GenesisCordonelInterfaceLogger.log
/GenesisCordonelInterface/bin/Debug/GenesisCordonelTesterLogger.log
/GenesisCordonelInterface/bin/Debug/SirtConfig.json
@@ -109,58 +101,3 @@ TBF.sln.DotSettings.user
/GenesisCordonelInterface/obj/Debug
/GenesisCordonelInterface/obj/Release
/ExternalProjects
/AppDiagnostic.Logging/bin/Debug/net472/AppDiagnostic.Logging.dll
/AppDiagnostic.Logging/bin/Debug/net472/AppDiagnostic.Logging.pdb
/AppDiagnostic.Logging/obj/Debug/net472/AppDiagnostic.Logging.AssemblyInfo.cs
/AppDiagnostic.Logging/obj/Debug/net472/AppDiagnostic.Logging.AssemblyInfoInputs.cache
/AppDiagnostic.Logging/obj/Debug/net472/AppDiagnostic.Logging.csproj.AssemblyReference.cache
/AppDiagnostic.Logging/obj/Debug/net472/AppDiagnostic.Logging.dll
/AppDiagnostic.Logging/obj/Debug/net472/AppDiagnostic.Logging.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Metrology.Measurements.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Metrology.Measurements.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Ui.CordonelPreadjustmentUi.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Ui.CordonelPreadjustmentUi.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Ui.GenesisToolBox.exe
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Ui.GenesisToolBox.pdb
/GenesisCordonelInterface/bin/Debug/XylemCommonUiLegacyGenCtl.dll
/GenesisCordonelInterface/bin/Debug/XylemCommonUiLegacyGenCtl.pdb
/GenesisCordonelInterface/bin/Debug/de/Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.resources.dll
/GenesisCordonelInterface/bin/Debug/GenesisCordonelInterface.exe
/GenesisCordonelInterface/bin/Debug/GenesisCordonelInterface.pdb
/GenesisCordonelInterface/bin/Debug/TempFlansh.exe
/GenesisCordonelInterface/bin/Debug/TempFlansh.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.Interfaces.Protocols.ProtocolCore.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.Interfaces.Protocols.ProtocolCore.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.eRegister.eRegisterCore.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.eRegister.eRegisterCore.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.StreamingProtocol.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.StreamingProtocol.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxConfig.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxConfig.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxCore.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Hardware.WaterMeter.MagFlux.MagFluxCore.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Logic.RelatePcb.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Logic.RelatePcb.pdb
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Logic.ServiceCore.dll
/GenesisCordonelInterface/bin/Debug/Xylem.Common.Logic.ServiceCore.pdb
/GenesisCordonelInterface/RuntimePackage/Package
/SharedComponents/obj/SharedComponents.csproj.nuget.g.props
/AppDiagnostic.Logging/obj/Release/net472/AppDiagnostic.Logging.AssemblyInfo.cs
/AppDiagnostic.Logging/obj/Release/net472/AppDiagnostic.Logging.AssemblyInfoInputs.cache
/AppDiagnostic.Logging/obj/Release/net472/AppDiagnostic.Logging.csproj.AssemblyReference.cache
/AppDiagnostic.Logging/obj/Release/net472/AppDiagnostic.Logging.dll
/AppDiagnostic.Logging/obj/Release/net472/AppDiagnostic.Logging.pdb
/AppDiagnostic.Logging/bin/Release/net472/AppDiagnostic.Logging.dll
/AppDiagnostic.Logging/bin/Release/net472/AppDiagnostic.Logging.pdb
@@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<LangVersion>7.3</LangVersion>
<RootNamespace>AppDiagnostic.Logging</RootNamespace>
<AssemblyName>AppDiagnostic.Logging</AssemblyName>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<ProjectGuid>{A7AF8ED2-362E-4DBB-B607-EBA2820719EC}</ProjectGuid>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="5.2.2" />
<Reference Include="System.Runtime.Serialization" />
</ItemGroup>
</Project>
@@ -1,41 +0,0 @@
using NLog;
using NLog.Config;
using NLog.Layouts;
namespace AppDiagnostic.Logging
{
public static class AppDiagnosticConnector
{
private static readonly object syncRoot = new object();
public static void Register()
{
lock (syncRoot)
{
LoggingConfiguration configuration = LogManager.Configuration ?? new LoggingConfiguration();
NLog.Targets.Target existingTarget = configuration.FindTargetByName("appDiagnostic");
if (existingTarget != null)
{
return;
}
var target = new AppDiagnosticTarget
{
Name = "appDiagnostic",
PipeName = DiagnosticProtocol.DefaultPipeName,
QueueLimit = DiagnosticProtocol.DefaultQueueLimit,
ReconnectDelayMilliseconds = DiagnosticProtocol.DefaultReconnectDelayMilliseconds,
ConnectTimeoutMilliseconds = DiagnosticProtocol.DefaultConnectTimeoutMilliseconds,
Layout = new SimpleLayout("${message}")
};
configuration.AddTarget(target.Name, target);
configuration.AddRule(LogLevel.Trace, LogLevel.Fatal, target, "*");
LogManager.Configuration = configuration;
LogManager.ReconfigExistingLoggers();
}
}
}
}
@@ -1,233 +0,0 @@
using NLog;
using NLog.Targets;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;
namespace AppDiagnostic.Logging
{
[Target("AppDiagnostic")]
public sealed class AppDiagnosticTarget : TargetWithLayout
{
private readonly object connectionLock = new object();
private readonly string applicationName;
private readonly int processId;
private BlockingCollection<DiagnosticLogEntry> queue;
private CancellationTokenSource cancellation;
private Task senderTask;
private NamedPipeClientStream pipeClient;
public AppDiagnosticTarget()
{
using (Process process = Process.GetCurrentProcess())
{
applicationName = process.ProcessName;
processId = process.Id;
}
PipeName = DiagnosticProtocol.DefaultPipeName;
QueueLimit = DiagnosticProtocol.DefaultQueueLimit;
ReconnectDelayMilliseconds =
DiagnosticProtocol.DefaultReconnectDelayMilliseconds;
ConnectTimeoutMilliseconds =
DiagnosticProtocol.DefaultConnectTimeoutMilliseconds;
}
public string PipeName { get; set; }
public int QueueLimit { get; set; }
public int ReconnectDelayMilliseconds { get; set; }
public int ConnectTimeoutMilliseconds { get; set; }
protected override void InitializeTarget()
{
base.InitializeTarget();
int queueLimit = QueueLimit > 0
? QueueLimit
: DiagnosticProtocol.DefaultQueueLimit;
queue = new BlockingCollection<DiagnosticLogEntry>(queueLimit);
cancellation = new CancellationTokenSource();
senderTask = Task.Factory.StartNew(
ProcessQueue,
cancellation.Token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
}
protected override void Write(LogEventInfo logEvent)
{
if (logEvent == null || queue == null || queue.IsAddingCompleted)
{
return;
}
var entry = new DiagnosticLogEntry
{
TimestampUtc = logEvent.TimeStamp.ToUniversalTime(),
MachineName = Environment.MachineName,
ApplicationName = applicationName,
ProcessId = processId,
LoggerName = logEvent.LoggerName ?? string.Empty,
Level = logEvent.Level?.Name ?? string.Empty,
Message = Layout != null
? Layout.Render(logEvent)
: logEvent.FormattedMessage,
Exception = logEvent.Exception?.ToString() ?? string.Empty
};
try
{
queue.TryAdd(entry);
}
catch (ObjectDisposedException)
{
// A concurrent NLog shutdown may dispose the queue.
}
catch (InvalidOperationException)
{
// A concurrent NLog shutdown may complete the queue.
}
}
protected override void CloseTarget()
{
BlockingCollection<DiagnosticLogEntry> currentQueue = queue;
CancellationTokenSource currentCancellation = cancellation;
if (currentQueue != null)
{
currentQueue.CompleteAdding();
}
if (currentCancellation != null)
{
currentCancellation.Cancel();
}
CloseConnection();
try
{
senderTask?.Wait(2000);
}
catch (AggregateException)
{
// Cancellation during application shutdown is expected.
}
currentQueue?.Dispose();
currentCancellation?.Dispose();
queue = null;
cancellation = null;
senderTask = null;
base.CloseTarget();
}
private void ProcessQueue()
{
CancellationToken token = cancellation.Token;
try
{
foreach (DiagnosticLogEntry entry in
queue.GetConsumingEnumerable(token))
{
SendWhenConnected(entry, token);
}
}
catch (OperationCanceledException)
{
// Target shutdown is expected to cancel the sender.
}
}
private void SendWhenConnected(
DiagnosticLogEntry entry,
CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
EnsureConnected();
DiagnosticMessageSerializer.WriteFramed(pipeClient, entry);
return;
}
catch (Exception exception) when (
exception is IOException ||
exception is TimeoutException ||
exception is InvalidOperationException)
{
CloseConnection();
int delay = ReconnectDelayMilliseconds > 0
? ReconnectDelayMilliseconds
: DiagnosticProtocol.DefaultReconnectDelayMilliseconds;
if (token.WaitHandle.WaitOne(delay))
{
return;
}
}
}
}
private void EnsureConnected()
{
lock (connectionLock)
{
if (pipeClient != null && pipeClient.IsConnected)
{
return;
}
CloseConnectionWithoutLock();
pipeClient = new NamedPipeClientStream(
".",
string.IsNullOrWhiteSpace(PipeName)
? DiagnosticProtocol.DefaultPipeName
: PipeName,
PipeDirection.Out,
PipeOptions.Asynchronous);
int timeout = ConnectTimeoutMilliseconds > 0
? ConnectTimeoutMilliseconds
: DiagnosticProtocol.DefaultConnectTimeoutMilliseconds;
pipeClient.Connect(timeout);
}
}
private void CloseConnection()
{
lock (connectionLock)
{
CloseConnectionWithoutLock();
}
}
private void CloseConnectionWithoutLock()
{
if (pipeClient == null)
{
return;
}
pipeClient.Dispose();
pipeClient = null;
}
}
}
@@ -1,33 +0,0 @@
using System;
using System.Runtime.Serialization;
namespace AppDiagnostic.Logging
{
[DataContract]
public sealed class DiagnosticLogEntry
{
[DataMember(Order = 1)]
public DateTime TimestampUtc { get; set; }
[DataMember(Order = 2)]
public string MachineName { get; set; }
[DataMember(Order = 3)]
public string ApplicationName { get; set; }
[DataMember(Order = 4)]
public int ProcessId { get; set; }
[DataMember(Order = 5)]
public string LoggerName { get; set; }
[DataMember(Order = 6)]
public string Level { get; set; }
[DataMember(Order = 7)]
public string Message { get; set; }
[DataMember(Order = 8)]
public string Exception { get; set; }
}
}
@@ -1,113 +0,0 @@
using System;
using System.IO;
using System.Runtime.Serialization.Json;
namespace AppDiagnostic.Logging
{
public static class DiagnosticMessageSerializer
{
private static readonly DataContractJsonSerializer Serializer =
new DataContractJsonSerializer(typeof(DiagnosticLogEntry));
private static readonly object SerializerLock = new object();
public static byte[] Serialize(DiagnosticLogEntry entry)
{
if (entry == null)
{
throw new ArgumentNullException(nameof(entry));
}
using (var stream = new MemoryStream())
{
lock (SerializerLock)
{
Serializer.WriteObject(stream, entry);
}
return stream.ToArray();
}
}
public static DiagnosticLogEntry Deserialize(byte[] payload)
{
if (payload == null)
{
throw new ArgumentNullException(nameof(payload));
}
if (payload.Length == 0 ||
payload.Length > DiagnosticProtocol.MaximumMessageSize)
{
throw new InvalidDataException("Invalid diagnostic message size.");
}
using (var stream = new MemoryStream(payload, false))
{
lock (SerializerLock)
{
return (DiagnosticLogEntry)Serializer.ReadObject(stream);
}
}
}
public static void WriteFramed(Stream stream, DiagnosticLogEntry entry)
{
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
byte[] payload = Serialize(entry);
if (payload.Length > DiagnosticProtocol.MaximumMessageSize)
{
throw new InvalidDataException("Diagnostic message is too large.");
}
byte[] length = BitConverter.GetBytes(payload.Length);
stream.Write(length, 0, length.Length);
stream.Write(payload, 0, payload.Length);
stream.Flush();
}
public static DiagnosticLogEntry ReadFramed(Stream stream)
{
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
byte[] lengthBuffer = ReadExactly(stream, sizeof(int));
int payloadLength = BitConverter.ToInt32(lengthBuffer, 0);
if (payloadLength <= 0 ||
payloadLength > DiagnosticProtocol.MaximumMessageSize)
{
throw new InvalidDataException("Invalid diagnostic message size.");
}
return Deserialize(ReadExactly(stream, payloadLength));
}
private static byte[] ReadExactly(Stream stream, int count)
{
byte[] buffer = new byte[count];
int offset = 0;
while (offset < count)
{
int bytesRead = stream.Read(buffer, offset, count - offset);
if (bytesRead == 0)
{
throw new EndOfStreamException();
}
offset += bytesRead;
}
return buffer;
}
}
}
@@ -1,11 +0,0 @@
namespace AppDiagnostic.Logging
{
public static class DiagnosticProtocol
{
public const string DefaultPipeName = "Sensus.AppDiagnostic.LogPipe";
public const int MaximumMessageSize = 1024 * 1024;
public const int DefaultQueueLimit = 10000;
public const int DefaultReconnectDelayMilliseconds = 500;
public const int DefaultConnectTimeoutMilliseconds = 250;
}
}
-37
View File
@@ -1,37 +0,0 @@
# AppDiagnostic.Logging
Reusable NLog target for sending structured diagnostic events from multiple
.NET Framework processes to `AppDiagnostic.exe` through a local named pipe.
## Producer integration
Reference this project from the producer application and add the extension and
target to its NLog configuration:
```xml
<extensions>
<add assembly="AppDiagnostic.Logging" />
</extensions>
<targets>
<target name="appDiagnostic"
xsi:type="AppDiagnostic"
layout="${message}" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="appDiagnostic" />
</rules>
```
The target writes to a bounded background queue. Producer threads are not
blocked while the diagnostic viewer is unavailable. Queued messages are sent
after the viewer becomes available; new messages are discarded when the queue
limit is reached.
The receiver can read messages with:
```csharp
DiagnosticLogEntry entry =
DiagnosticMessageSerializer.ReadFramed(namedPipeServerStream);
```
Binary file not shown.
Binary file not shown.
@@ -1,65 +0,0 @@
{
"format": 1,
"restore": {
"C:\\Sensus_projects\\New\\LocalBranch_start_at_6.3.2026\\tbf-local\\tbf\\AppDiagnostic.Logging\\AppDiagnostic.Logging.csproj": {}
},
"projects": {
"C:\\Sensus_projects\\New\\LocalBranch_start_at_6.3.2026\\tbf-local\\tbf\\AppDiagnostic.Logging\\AppDiagnostic.Logging.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\Sensus_projects\\New\\LocalBranch_start_at_6.3.2026\\tbf-local\\tbf\\AppDiagnostic.Logging\\AppDiagnostic.Logging.csproj",
"projectName": "AppDiagnostic.Logging",
"projectPath": "C:\\Sensus_projects\\New\\LocalBranch_start_at_6.3.2026\\tbf-local\\tbf\\AppDiagnostic.Logging\\AppDiagnostic.Logging.csproj",
"packagesPath": "C:\\Users\\MFRNIAK\\.nuget\\packages\\",
"outputPath": "C:\\Sensus_projects\\New\\LocalBranch_start_at_6.3.2026\\tbf-local\\tbf\\AppDiagnostic.Logging\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\MFRNIAK\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net472"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net472": {
"framework": "net472",
"targetAlias": "net472",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "10.0.300"
},
"frameworks": {
"net472": {
"framework": "net472",
"targetAlias": "net472",
"dependencies": {
"NLog": {
"target": "Package",
"version": "[5.2.2, )"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.303\\RuntimeIdentifierGraph.json"
}
}
}
}
}
@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
@@ -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,8 +0,0 @@
is_global = true
build_property.RootNamespace = AppDiagnostic.Logging
build_property.ProjectDir = C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.CsWinRTUseWindowsUIXamlProjections = false
build_property.EffectiveAnalysisLevelStyle =
build_property.EnableCodeStyleSeverity =
@@ -1 +0,0 @@
551ba94934db30a277a1a9cec2dacbe03f8393107e5d0b2436695f9bd62bd569
@@ -1,11 +0,0 @@
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\bin\Debug\net472\AppDiagnostic.Logging.dll
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\bin\Debug\net472\AppDiagnostic.Logging.pdb
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\bin\Debug\net472\NLog.dll
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Debug\net472\AppDiagnostic.Logging.csproj.AssemblyReference.cache
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Debug\net472\AppDiagnostic.Logging.GeneratedMSBuildEditorConfig.editorconfig
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Debug\net472\AppDiagnostic.Logging.AssemblyInfoInputs.cache
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Debug\net472\AppDiagnostic.Logging.AssemblyInfo.cs
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Debug\net472\AppDiagnostic.Logging.csproj.CoreCompileInputs.cache
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Debug\net472\AppDiagn.CFEA7AC0.Up2Date
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Debug\net472\AppDiagnostic.Logging.dll
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Debug\net472\AppDiagnostic.Logging.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,8 +0,0 @@
is_global = true
build_property.RootNamespace = AppDiagnostic.Logging
build_property.ProjectDir = C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.CsWinRTUseWindowsUIXamlProjections = false
build_property.EffectiveAnalysisLevelStyle =
build_property.EnableCodeStyleSeverity =
@@ -1 +0,0 @@
a53d59482cad49b656138b1af5cf3e55edf2108969aedc574c362be6c671436b
@@ -1,11 +0,0 @@
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\bin\Release\net472\AppDiagnostic.Logging.dll
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\bin\Release\net472\AppDiagnostic.Logging.pdb
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\bin\Release\net472\NLog.dll
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Release\net472\AppDiagnostic.Logging.csproj.AssemblyReference.cache
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Release\net472\AppDiagnostic.Logging.GeneratedMSBuildEditorConfig.editorconfig
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Release\net472\AppDiagnostic.Logging.AssemblyInfoInputs.cache
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Release\net472\AppDiagnostic.Logging.AssemblyInfo.cs
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Release\net472\AppDiagnostic.Logging.csproj.CoreCompileInputs.cache
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Release\net472\AppDiagn.CFEA7AC0.Up2Date
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Release\net472\AppDiagnostic.Logging.dll
C:\Sensus_projects\New\LocalBranch_start_at_6.3.2026\tbf-local\tbf\AppDiagnostic.Logging\obj\Release\net472\AppDiagnostic.Logging.pdb
@@ -1,119 +0,0 @@
{
"version": 4,
"targets": {
"net472": {
"NLog/5.2.2": {
"type": "package",
"frameworkAssemblies": [
"System",
"System.Configuration",
"System.Core",
"System.IO.Compression",
"System.Xml"
],
"compile": {
"lib/net46/NLog.dll": {
"related": ".xml"
}
},
"runtime": {
"lib/net46/NLog.dll": {
"related": ".xml"
}
}
}
}
},
"libraries": {
"NLog/5.2.2": {
"sha512": "r6Q9740g29gTwmTWlsgdIFm0mhNsfNZmbvWKX/Fxmi8X89ZrpUowHM2T2X1lP7RVpND+ef+XnfKL5g6Q1iNGXA==",
"type": "package",
"path": "nlog/5.2.2",
"files": [
".nupkg.metadata",
".signature.p7s",
"N.png",
"lib/net35/NLog.dll",
"lib/net35/NLog.xml",
"lib/net45/NLog.dll",
"lib/net45/NLog.xml",
"lib/net46/NLog.dll",
"lib/net46/NLog.xml",
"lib/netstandard1.3/NLog.dll",
"lib/netstandard1.3/NLog.xml",
"lib/netstandard1.5/NLog.dll",
"lib/netstandard1.5/NLog.xml",
"lib/netstandard2.0/NLog.dll",
"lib/netstandard2.0/NLog.xml",
"nlog.5.2.2.nupkg.sha512",
"nlog.nuspec"
]
}
},
"projectFileDependencyGroups": {
"net472": [
"NLog >= 5.2.2"
]
},
"packageFolders": {
"C:\\Users\\MFRNIAK\\.nuget\\packages\\": {},
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}
},
"project": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\Sensus_projects\\New\\LocalBranch_start_at_6.3.2026\\tbf-local\\tbf\\AppDiagnostic.Logging\\AppDiagnostic.Logging.csproj",
"projectName": "AppDiagnostic.Logging",
"projectPath": "C:\\Sensus_projects\\New\\LocalBranch_start_at_6.3.2026\\tbf-local\\tbf\\AppDiagnostic.Logging\\AppDiagnostic.Logging.csproj",
"packagesPath": "C:\\Users\\MFRNIAK\\.nuget\\packages\\",
"outputPath": "C:\\Sensus_projects\\New\\LocalBranch_start_at_6.3.2026\\tbf-local\\tbf\\AppDiagnostic.Logging\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\MFRNIAK\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net472"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net472": {
"framework": "net472",
"targetAlias": "net472",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
},
"SdkAnalysisLevel": "10.0.300"
},
"frameworks": {
"net472": {
"framework": "net472",
"targetAlias": "net472",
"dependencies": {
"NLog": {
"target": "Package",
"version": "[5.2.2, )"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.303\\RuntimeIdentifierGraph.json"
}
}
}
}
@@ -1,10 +0,0 @@
{
"version": 2,
"dgSpecHash": "ezwKOPZAAXU=",
"success": true,
"projectFilePath": "C:\\Sensus_projects\\New\\LocalBranch_start_at_6.3.2026\\tbf-local\\tbf\\AppDiagnostic.Logging\\AppDiagnostic.Logging.csproj",
"expectedPackageFiles": [
"C:\\Users\\MFRNIAK\\.nuget\\packages\\nlog\\5.2.2\\nlog.5.2.2.nupkg.sha512"
],
"logs": []
}
+14 -9
View File
@@ -3,12 +3,17 @@
<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>
</assemblyBinding>
</runtime>
</configuration>
<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>
+14 -13
View File
@@ -8,7 +8,7 @@
<OutputType>WinExe</OutputType>
<RootNamespace>AppDiagnostic</RootNamespace>
<AssemblyName>AppDiagnostic</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
@@ -33,6 +33,9 @@
<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" />
@@ -48,15 +51,12 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ColorRuleEditorForm.cs">
<SubType>Form</SubType>
<Compile Include="BufferedListView.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="DiagnosticLogFormatter.cs" />
<Compile Include="DiagnosticLogStore.cs" />
<Compile Include="DiagnosticPipeServer.cs" />
<Compile Include="LogColorizer.cs" />
<Compile Include="LogColorRule.cs" />
<Compile Include="LogColorRuleRepository.cs" />
<Compile Include="DiagApi.cs" />
<Compile Include="LogAdapter.cs" />
<Compile Include="LogFilter.cs" />
<Compile Include="MainForm.cs">
<SubType>Form</SubType>
</Compile>
@@ -78,6 +78,7 @@
<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>
@@ -92,13 +93,13 @@
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AppDiagnostic.Logging\AppDiagnostic.Logging.csproj">
<Project>{A7AF8ED2-362E-4DBB-B607-EBA2820719EC}</Project>
<Name>AppDiagnostic.Logging</Name>
<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>
</Project>
-319
View File
@@ -1,319 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace AppDiagnostic
{
public sealed class ColorRuleEditorForm : Form
{
private readonly List<LogColorRule> rules;
private readonly ListBox ruleList = new ListBox();
private readonly TextBox patternTextBox = new TextBox();
private readonly CheckBox enabledCheckBox = new CheckBox();
private readonly CheckBox boldCheckBox = new CheckBox();
private readonly CheckBox backgroundCheckBox = new CheckBox();
private readonly Button foregroundButton = new Button();
private readonly Button backgroundButton = new Button();
private readonly RichTextBox previewBox = new RichTextBox();
private readonly Font previewBoldFont;
private bool loadingRule;
public ColorRuleEditorForm(IEnumerable<LogColorRule> sourceRules)
{
rules = sourceRules.Select(rule => rule.Clone()).ToList();
InitializeWindow();
previewBoldFont = new Font(previewBox.Font, FontStyle.Bold);
RefreshRuleList(0);
RefreshPreview();
}
public List<LogColorRule> Rules => rules.Select(rule => rule.Clone()).ToList();
protected override void Dispose(bool disposing)
{
if (disposing)
{
previewBoldFont.Dispose();
}
base.Dispose(disposing);
}
private void InitializeWindow()
{
Text = "Log color rules";
Icon = Properties.Resources.TBF_icon;
StartPosition = FormStartPosition.CenterParent;
MinimumSize = new Size(760, 500);
ClientSize = new Size(860, 560);
var root = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 1, RowCount = 3, Padding = new Padding(10) };
root.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
root.RowStyles.Add(new RowStyle(SizeType.Absolute, 145F));
root.RowStyles.Add(new RowStyle(SizeType.Absolute, 42F));
var split = new SplitContainer { Dock = DockStyle.Fill, SplitterDistance = 280 };
split.Panel1.Padding = new Padding(0, 0, 8, 0);
split.Panel2.Padding = new Padding(8, 0, 0, 0);
ruleList.Dock = DockStyle.Fill;
ruleList.IntegralHeight = false;
ruleList.SelectedIndexChanged += RuleList_SelectedIndexChanged;
var listButtons = new FlowLayoutPanel { Dock = DockStyle.Bottom, Height = 36, FlowDirection = FlowDirection.LeftToRight };
listButtons.Controls.Add(CreateButton("Add", AddRule_Click));
listButtons.Controls.Add(CreateButton("Delete", DeleteRule_Click));
listButtons.Controls.Add(CreateButton("Up", MoveUp_Click));
listButtons.Controls.Add(CreateButton("Down", MoveDown_Click));
split.Panel1.Controls.Add(ruleList);
split.Panel1.Controls.Add(listButtons);
var editor = new TableLayoutPanel { Dock = DockStyle.Top, AutoSize = true, ColumnCount = 2, Padding = new Padding(4) };
editor.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 120F));
editor.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
enabledCheckBox.Text = "Rule enabled";
enabledCheckBox.AutoSize = true;
enabledCheckBox.CheckedChanged += EditorValueChanged;
AddEditorRow(editor, "State", enabledCheckBox);
patternTextBox.Dock = DockStyle.Fill;
patternTextBox.TextChanged += EditorValueChanged;
AddEditorRow(editor, "Substring", patternTextBox);
foregroundButton.Text = "Choose text color...";
foregroundButton.AutoSize = true;
foregroundButton.Click += ForegroundButton_Click;
AddEditorRow(editor, "Text color", foregroundButton);
var backgroundPanel = new FlowLayoutPanel { Dock = DockStyle.Fill, AutoSize = true };
backgroundCheckBox.Text = "Use background";
backgroundCheckBox.AutoSize = true;
backgroundCheckBox.CheckedChanged += EditorValueChanged;
backgroundButton.Text = "Choose...";
backgroundButton.AutoSize = true;
backgroundButton.Click += BackgroundButton_Click;
backgroundPanel.Controls.Add(backgroundCheckBox);
backgroundPanel.Controls.Add(backgroundButton);
AddEditorRow(editor, "Background", backgroundPanel);
boldCheckBox.Text = "Bold text";
boldCheckBox.AutoSize = true;
boldCheckBox.CheckedChanged += EditorValueChanged;
AddEditorRow(editor, "Font", boldCheckBox);
var resetButton = CreateButton("Restore default rules", RestoreDefaults_Click);
resetButton.AutoSize = true;
AddEditorRow(editor, string.Empty, resetButton);
split.Panel2.Controls.Add(editor);
previewBox.Dock = DockStyle.Fill;
previewBox.ReadOnly = true;
previewBox.WordWrap = true;
previewBox.Font = new Font("Consolas", 9F);
previewBox.Text = "2026-09-10 14:25:31.042|DEBUG|TBF[14280]|TBF.UI.MainWnd|Device configuration loaded.\n2026-09-10 14:25:31.384|INFO |TBF[14280]|TBF.Rig.StateMachine|Test bench started.\n2026-09-10 14:25:32.018|WARN |TBF[14280]|TBF.Device|Pressure is approaching the limit.\n2026-09-10 14:25:32.611|ERROR|TBF[14280]|TBF.Device|Communication failed.";
var previewGroup = new GroupBox { Text = "Live preview", Dock = DockStyle.Fill, Padding = new Padding(8) };
previewGroup.Controls.Add(previewBox);
var footer = new FlowLayoutPanel { Dock = DockStyle.Fill, FlowDirection = FlowDirection.RightToLeft, Padding = new Padding(0, 8, 0, 0) };
var okButton = new Button { Text = "OK", DialogResult = DialogResult.OK, AutoSize = true };
var cancelButton = new Button { Text = "Cancel", DialogResult = DialogResult.Cancel, AutoSize = true };
footer.Controls.Add(okButton);
footer.Controls.Add(cancelButton);
AcceptButton = okButton;
CancelButton = cancelButton;
root.Controls.Add(split, 0, 0);
root.Controls.Add(previewGroup, 0, 1);
root.Controls.Add(footer, 0, 2);
Controls.Add(root);
}
private static Button CreateButton(string text, EventHandler clickHandler)
{
var button = new Button { Text = text, AutoSize = true };
button.Click += clickHandler;
return button;
}
private static void AddEditorRow(TableLayoutPanel panel, string labelText, Control control)
{
int row = panel.RowCount++;
panel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
var label = new Label { Text = labelText, AutoSize = true, Anchor = AnchorStyles.Left, Margin = new Padding(3, 8, 3, 3) };
control.Margin = new Padding(3, 5, 3, 5);
panel.Controls.Add(label, 0, row);
panel.Controls.Add(control, 1, row);
}
private void RefreshRuleList(int selectedIndex)
{
ruleList.BeginUpdate();
ruleList.Items.Clear();
foreach (LogColorRule rule in rules)
{
ruleList.Items.Add(rule);
}
ruleList.EndUpdate();
if (rules.Count > 0)
{
ruleList.SelectedIndex = Math.Max(0, Math.Min(selectedIndex, rules.Count - 1));
}
}
private void RuleList_SelectedIndexChanged(object sender, EventArgs e)
{
LogColorRule rule = ruleList.SelectedItem as LogColorRule;
if (rule == null)
{
return;
}
loadingRule = true;
enabledCheckBox.Checked = rule.Enabled;
patternTextBox.Text = rule.Pattern;
foregroundButton.BackColor = rule.ForeColor;
backgroundCheckBox.Checked = rule.UseBackColor;
backgroundButton.Enabled = rule.UseBackColor;
backgroundButton.BackColor = rule.UseBackColor ? rule.BackColor : SystemColors.Control;
boldCheckBox.Checked = rule.Bold;
loadingRule = false;
}
private void EditorValueChanged(object sender, EventArgs e)
{
if (loadingRule)
{
return;
}
LogColorRule rule = ruleList.SelectedItem as LogColorRule;
if (rule == null)
{
return;
}
rule.Enabled = enabledCheckBox.Checked;
rule.Pattern = patternTextBox.Text;
rule.UseBackColor = backgroundCheckBox.Checked;
rule.Bold = boldCheckBox.Checked;
backgroundButton.Enabled = rule.UseBackColor;
int index = ruleList.SelectedIndex;
RefreshRuleList(index);
RefreshPreview();
}
private void ForegroundButton_Click(object sender, EventArgs e)
{
LogColorRule rule = ruleList.SelectedItem as LogColorRule;
if (rule == null)
{
return;
}
using (var dialog = new ColorDialog { Color = rule.ForeColor, FullOpen = true })
{
if (dialog.ShowDialog(this) != DialogResult.OK)
{
return;
}
rule.ForeColorArgb = dialog.Color.ToArgb();
foregroundButton.BackColor = dialog.Color;
RefreshPreview();
}
}
private void BackgroundButton_Click(object sender, EventArgs e)
{
LogColorRule rule = ruleList.SelectedItem as LogColorRule;
if (rule == null)
{
return;
}
Color initialColor = rule.UseBackColor ? rule.BackColor : SystemColors.Window;
using (var dialog = new ColorDialog { Color = initialColor, FullOpen = true })
{
if (dialog.ShowDialog(this) != DialogResult.OK)
{
return;
}
rule.BackColorArgb = dialog.Color.ToArgb();
rule.UseBackColor = true;
backgroundCheckBox.Checked = true;
backgroundButton.BackColor = dialog.Color;
RefreshPreview();
}
}
private void AddRule_Click(object sender, EventArgs e)
{
rules.Add(new LogColorRule { Pattern = "NEW SUBSTRING", ForeColorArgb = Color.MediumPurple.ToArgb() });
RefreshRuleList(rules.Count - 1);
patternTextBox.Focus();
patternTextBox.SelectAll();
}
private void DeleteRule_Click(object sender, EventArgs e)
{
int index = ruleList.SelectedIndex;
if (index < 0)
{
return;
}
rules.RemoveAt(index);
RefreshRuleList(Math.Min(index, rules.Count - 1));
RefreshPreview();
}
private void MoveUp_Click(object sender, EventArgs e)
{
MoveSelectedRule(-1);
}
private void MoveDown_Click(object sender, EventArgs e)
{
MoveSelectedRule(1);
}
private void MoveSelectedRule(int offset)
{
int index = ruleList.SelectedIndex;
int newIndex = index + offset;
if (index < 0 || newIndex < 0 || newIndex >= rules.Count)
{
return;
}
LogColorRule rule = rules[index];
rules.RemoveAt(index);
rules.Insert(newIndex, rule);
RefreshRuleList(newIndex);
RefreshPreview();
}
private void RestoreDefaults_Click(object sender, EventArgs e)
{
rules.Clear();
rules.AddRange(LogColorRule.CreateDefaults());
RefreshRuleList(0);
RefreshPreview();
}
private void RefreshPreview()
{
int selectionStart = previewBox.SelectionStart;
int selectionLength = previewBox.SelectionLength;
string text = previewBox.Text;
LogColorizer.Apply(previewBox, 0, text.Length, text, rules, previewBox.Font, previewBoldFont);
previewBox.Select(Math.Min(selectionStart, previewBox.TextLength), Math.Min(selectionLength, Math.Max(0, previewBox.TextLength - selectionStart)));
}
}
}
-24
View File
@@ -1,24 +0,0 @@
using AppDiagnostic.Logging;
using System;
namespace AppDiagnostic
{
public static class DiagnosticLogFormatter
{
public static string Format(DiagnosticLogEntry entry)
{
if (entry == null)
{
return string.Empty;
}
string level = (entry.Level ?? string.Empty).ToUpperInvariant().PadRight(5);
string application = string.IsNullOrWhiteSpace(entry.ApplicationName) ? "Unknown" : entry.ApplicationName;
string logger = entry.LoggerName ?? string.Empty;
string message = entry.Message ?? string.Empty;
string exception = string.IsNullOrWhiteSpace(entry.Exception) ? string.Empty : Environment.NewLine + entry.Exception;
return $"{entry.TimestampUtc.ToLocalTime():yyyy-MM-dd HH:mm:ss.fff}|{level}|{application}[{entry.ProcessId}]|{logger}|{message}{exception}";
}
}
}
-82
View File
@@ -1,82 +0,0 @@
using AppDiagnostic.Logging;
using System.Collections.Generic;
using System.Linq;
namespace AppDiagnostic
{
public sealed class DiagnosticLogStore
{
private const int MaximumLogCount = 10000;
private static readonly DiagnosticLogStore instance = new DiagnosticLogStore();
private readonly object syncRoot = new object();
private readonly List<DiagnosticLogEntry> entries = new List<DiagnosticLogEntry>();
private long totalReceived;
private long version;
private DiagnosticLogStore()
{
}
public static DiagnosticLogStore Instance => instance;
public long TotalReceived
{
get
{
lock (syncRoot)
{
return totalReceived;
}
}
}
public long Version
{
get
{
lock (syncRoot)
{
return version;
}
}
}
public void Add(DiagnosticLogEntry entry)
{
if (entry == null)
{
return;
}
lock (syncRoot)
{
entries.Add(entry);
totalReceived++;
version++;
int excessCount = entries.Count - MaximumLogCount;
if (excessCount > 0)
{
entries.RemoveRange(0, excessCount);
}
}
}
public List<DiagnosticLogEntry> GetSnapshot()
{
lock (syncRoot)
{
return entries.ToList();
}
}
public void Clear()
{
lock (syncRoot)
{
entries.Clear();
version++;
}
}
}
}
-164
View File
@@ -1,164 +0,0 @@
using AppDiagnostic.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;
namespace AppDiagnostic
{
public sealed class DiagnosticPipeServer : IDisposable
{
private readonly Action<DiagnosticLogEntry> messageReceived;
private readonly CancellationTokenSource cancellation = new CancellationTokenSource();
private readonly object syncRoot = new object();
private readonly List<NamedPipeServerStream> connectedClients = new List<NamedPipeServerStream>();
private NamedPipeServerStream waitingServer;
private Task serverTask;
public DiagnosticPipeServer(Action<DiagnosticLogEntry> messageReceived)
{
this.messageReceived = messageReceived ?? throw new ArgumentNullException(nameof(messageReceived));
}
public int ConnectedClientCount
{
get
{
lock (syncRoot)
{
return connectedClients.Count;
}
}
}
public void Start()
{
if (serverTask != null)
{
return;
}
serverTask = Task.Run((Action)AcceptConnections);
}
private void AcceptConnections()
{
while (!cancellation.IsCancellationRequested)
{
NamedPipeServerStream server = null;
try
{
server = new NamedPipeServerStream(DiagnosticProtocol.DefaultPipeName, PipeDirection.In, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
lock (syncRoot)
{
waitingServer = server;
}
server.WaitForConnection();
lock (syncRoot)
{
waitingServer = null;
connectedClients.Add(server);
}
NamedPipeServerStream connectedServer = server;
server = null;
Task.Run(() => ReadClient(connectedServer));
}
catch (ObjectDisposedException)
{
if (!cancellation.IsCancellationRequested)
{
throw;
}
}
catch (IOException)
{
if (!cancellation.IsCancellationRequested)
{
Thread.Sleep(250);
}
}
finally
{
lock (syncRoot)
{
if (ReferenceEquals(waitingServer, server))
{
waitingServer = null;
}
}
server?.Dispose();
}
}
}
private void ReadClient(NamedPipeServerStream client)
{
try
{
while (client.IsConnected && !cancellation.IsCancellationRequested)
{
DiagnosticLogEntry entry = DiagnosticMessageSerializer.ReadFramed(client);
messageReceived(entry);
}
}
catch (EndOfStreamException)
{
// The producer application was closed.
}
catch (IOException)
{
// The producer application disconnected.
}
catch (ObjectDisposedException)
{
// The diagnostic application is shutting down.
}
finally
{
lock (syncRoot)
{
connectedClients.Remove(client);
}
client.Dispose();
}
}
public void Dispose()
{
cancellation.Cancel();
lock (syncRoot)
{
waitingServer?.Dispose();
waitingServer = null;
foreach (NamedPipeServerStream client in connectedClients.ToArray())
{
client.Dispose();
}
connectedClients.Clear();
}
try
{
serverTask?.Wait(2000);
}
catch (AggregateException)
{
// Cancellation during shutdown is expected.
}
cancellation.Dispose();
}
}
}
-65
View File
@@ -1,65 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
namespace AppDiagnostic
{
[Serializable]
public sealed class LogColorRule
{
public bool Enabled { get; set; } = true;
public string Pattern { get; set; } = string.Empty;
public int ForeColorArgb { get; set; } = Color.Black.ToArgb();
public int BackColorArgb { get; set; }
public bool UseBackColor { get; set; }
public bool Bold { get; set; }
public Color ForeColor => Color.FromArgb(ForeColorArgb);
public Color BackColor => Color.FromArgb(BackColorArgb);
public LogColorRule Clone()
{
return new LogColorRule
{
Enabled = Enabled,
Pattern = Pattern,
ForeColorArgb = ForeColorArgb,
BackColorArgb = BackColorArgb,
UseBackColor = UseBackColor,
Bold = Bold
};
}
public override string ToString()
{
string state = Enabled ? string.Empty : "[disabled] ";
return state + Pattern;
}
public static List<LogColorRule> CreateDefaults()
{
return new List<LogColorRule>
{
Create("|TRACE|", Color.DimGray, false),
Create("|DEBUG|", Color.RoyalBlue, false),
Create("|INFO |", Color.ForestGreen, false),
Create("|WARN |", Color.DarkOrange, true),
Create("|ERROR|", Color.Crimson, true),
Create("|FATAL|", Color.White, true, Color.DarkRed)
};
}
private static LogColorRule Create(string pattern, Color foreground, bool bold, Color? background = null)
{
return new LogColorRule
{
Enabled = true,
Pattern = pattern,
ForeColorArgb = foreground.ToArgb(),
BackColorArgb = background?.ToArgb() ?? Color.Empty.ToArgb(),
UseBackColor = background.HasValue,
Bold = bold
};
}
}
}
-46
View File
@@ -1,46 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
namespace AppDiagnostic
{
public static class LogColorRuleRepository
{
private static readonly string settingsFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Sensus", "AppDiagnostic");
private static readonly string settingsPath = Path.Combine(settingsFolder, "color-rules.xml");
public static List<LogColorRule> Load()
{
try
{
if (!File.Exists(settingsPath))
{
return LogColorRule.CreateDefaults();
}
var serializer = new XmlSerializer(typeof(List<LogColorRule>));
using (FileStream stream = File.OpenRead(settingsPath))
{
var rules = serializer.Deserialize(stream) as List<LogColorRule>;
return rules == null || rules.Count == 0 ? LogColorRule.CreateDefaults() : rules;
}
}
catch
{
return LogColorRule.CreateDefaults();
}
}
public static void Save(IEnumerable<LogColorRule> rules)
{
Directory.CreateDirectory(settingsFolder);
var serializer = new XmlSerializer(typeof(List<LogColorRule>));
using (FileStream stream = File.Create(settingsPath))
{
serializer.Serialize(stream, new List<LogColorRule>(rules));
}
}
}
}
-47
View File
@@ -1,47 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace AppDiagnostic
{
public static class LogColorizer
{
public static void Apply(RichTextBox target, int startIndex, int length, string text, IEnumerable<LogColorRule> rules, Font regularFont, Font boldFont)
{
if (target == null || length <= 0)
{
return;
}
target.Select(startIndex, length);
target.SelectionColor = target.ForeColor;
target.SelectionBackColor = target.BackColor;
target.SelectionFont = regularFont;
foreach (LogColorRule rule in rules)
{
if (rule == null || !rule.Enabled || string.IsNullOrEmpty(rule.Pattern))
{
continue;
}
int searchIndex = 0;
while (searchIndex < text.Length)
{
int matchIndex = text.IndexOf(rule.Pattern, searchIndex, StringComparison.OrdinalIgnoreCase);
if (matchIndex < 0)
{
break;
}
target.Select(startIndex + matchIndex, rule.Pattern.Length);
target.SelectionColor = rule.ForeColor;
target.SelectionBackColor = rule.UseBackColor ? rule.BackColor : target.BackColor;
target.SelectionFont = rule.Bold ? boldFont : regularFont;
searchIndex = matchIndex + rule.Pattern.Length;
}
}
}
}
}
+250 -281
View File
@@ -1,309 +1,278 @@
using System.Windows.Forms;
namespace AppDiagnostic
{
partial class MainForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
private System.Windows.Forms.ToolStrip mainToolStrip;
private System.Windows.Forms.ToolStripButton pauseButton;
private System.Windows.Forms.ToolStripButton refreshButton;
private System.Windows.Forms.ToolStripButton clearButton;
private System.Windows.Forms.ToolStripButton saveButton;
private System.Windows.Forms.ToolStripButton copyButton;
private System.Windows.Forms.ToolStripButton copyAllButton;
private System.Windows.Forms.ToolStripButton wrapButton;
private System.Windows.Forms.ToolStripButton autoScrollButton;
private System.Windows.Forms.ToolStripButton alwaysOnTopButton;
private System.Windows.Forms.ToolStripButton colorRulesButton;
private System.Windows.Forms.ToolStripButton closeButton;
private System.Windows.Forms.TableLayoutPanel filterPanel;
private System.Windows.Forms.Label filterLabel;
private System.Windows.Forms.TextBox filterTextBox;
private System.Windows.Forms.Label levelLabel;
private System.Windows.Forms.ComboBox levelComboBox;
private System.Windows.Forms.Label applicationLabel;
private System.Windows.Forms.ComboBox applicationComboBox;
private System.Windows.Forms.Button applyFilterButton;
private System.Windows.Forms.Button clearFilterButton;
private System.Windows.Forms.RichTextBox logView;
private System.Windows.Forms.StatusStrip statusStrip;
private System.Windows.Forms.ToolStripStatusLabel receiverStatusLabel;
private System.Windows.Forms.ToolStripStatusLabel statusSpacer;
private System.Windows.Forms.ToolStripStatusLabel countStatusLabel;
private System.Windows.Forms.ContextMenuStrip logContextMenu;
private System.Windows.Forms.ToolStripMenuItem copySelectionMenuItem;
private System.Windows.Forms.ToolStripMenuItem copyLineMenuItem;
private System.Windows.Forms.ToolStripMenuItem copyAllMenuItem;
/// <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)
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.components = new System.ComponentModel.Container();
this.mainToolStrip = new System.Windows.Forms.ToolStrip();
this.pauseButton = new System.Windows.Forms.ToolStripButton();
this.refreshButton = new System.Windows.Forms.ToolStripButton();
this.clearButton = new System.Windows.Forms.ToolStripButton();
this.saveButton = new System.Windows.Forms.ToolStripButton();
this.copyButton = new System.Windows.Forms.ToolStripButton();
this.copyAllButton = new System.Windows.Forms.ToolStripButton();
this.wrapButton = new System.Windows.Forms.ToolStripButton();
this.autoScrollButton = new System.Windows.Forms.ToolStripButton();
this.alwaysOnTopButton = new System.Windows.Forms.ToolStripButton();
this.colorRulesButton = new System.Windows.Forms.ToolStripButton();
this.closeButton = new System.Windows.Forms.ToolStripButton();
this.filterPanel = new System.Windows.Forms.TableLayoutPanel();
this.filterLabel = new System.Windows.Forms.Label();
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.levelLabel = new System.Windows.Forms.Label();
this.levelComboBox = new System.Windows.Forms.ComboBox();
this.applicationLabel = new System.Windows.Forms.Label();
this.applicationComboBox = new System.Windows.Forms.ComboBox();
this.applyFilterButton = new System.Windows.Forms.Button();
this.clearFilterButton = new System.Windows.Forms.Button();
this.logContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
this.copySelectionMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.copyLineMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.copyAllMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.logView = new System.Windows.Forms.RichTextBox();
this.statusStrip = new System.Windows.Forms.StatusStrip();
this.receiverStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.statusSpacer = new System.Windows.Forms.ToolStripStatusLabel();
this.countStatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.mainToolStrip.SuspendLayout();
this.filterPanel.SuspendLayout();
this.logContextMenu.SuspendLayout();
this.statusStrip.SuspendLayout();
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();
//
// mainToolStrip
//
this.mainToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
this.mainToolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.pauseButton,
this.refreshButton,
this.clearButton,
this.saveButton,
new System.Windows.Forms.ToolStripSeparator(),
this.copyButton,
this.copyAllButton,
new System.Windows.Forms.ToolStripSeparator(),
this.wrapButton,
this.autoScrollButton,
this.alwaysOnTopButton,
this.colorRulesButton,
this.closeButton});
this.mainToolStrip.Location = new System.Drawing.Point(0, 0);
this.mainToolStrip.Name = "mainToolStrip";
this.mainToolStrip.Padding = new System.Windows.Forms.Padding(6, 3, 6, 3);
this.mainToolStrip.Size = new System.Drawing.Size(1184, 31);
this.mainToolStrip.TabIndex = 0;
//
// pauseButton
//
this.pauseButton.CheckOnClick = true;
this.pauseButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.pauseButton.Text = "Pause";
this.pauseButton.ToolTipText = "Pause screen updates while continuing to receive logs";
this.pauseButton.Click += new System.EventHandler(this.pauseButton_Click);
//
// refreshButton
//
this.refreshButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.refreshButton.Text = "Refresh";
this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
//
// clearButton
//
this.clearButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.clearButton.Text = "Clear";
this.clearButton.Click += new System.EventHandler(this.clearButton_Click);
//
// saveButton
//
this.saveButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.saveButton.Text = "Save...";
this.saveButton.Click += new System.EventHandler(this.saveButton_Click);
//
// copyButton
//
this.copyButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.copyButton.Text = "Copy";
this.copyButton.ToolTipText = "Copy selected text or the current log line";
this.copyButton.Click += new System.EventHandler(this.copyButton_Click);
//
// copyAllButton
//
this.copyAllButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.copyAllButton.Text = "Copy all";
this.copyAllButton.Click += new System.EventHandler(this.copyAllButton_Click);
//
// wrapButton
//
this.wrapButton.Checked = true;
this.wrapButton.CheckOnClick = true;
this.wrapButton.CheckState = System.Windows.Forms.CheckState.Checked;
this.wrapButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.wrapButton.Text = "Wrap lines";
this.wrapButton.Click += new System.EventHandler(this.wrapButton_Click);
//
// autoScrollButton
//
this.autoScrollButton.Checked = true;
this.autoScrollButton.CheckOnClick = true;
this.autoScrollButton.CheckState = System.Windows.Forms.CheckState.Checked;
this.autoScrollButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.autoScrollButton.Text = "Auto-scroll";
//
// alwaysOnTopButton
//
this.alwaysOnTopButton.CheckOnClick = true;
this.alwaysOnTopButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.alwaysOnTopButton.Text = "Always on top";
this.alwaysOnTopButton.Click += new System.EventHandler(this.alwaysOnTopButton_Click);
//
// colorRulesButton
//
this.colorRulesButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.colorRulesButton.Text = "Color rules...";
this.colorRulesButton.Click += new System.EventHandler(this.colorRulesButton_Click);
//
// closeButton
//
this.closeButton.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
this.closeButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
this.closeButton.Text = "Close";
this.closeButton.Click += new System.EventHandler(this.closeButton_Click);
//
// filterPanel
//
this.filterPanel.ColumnCount = 8;
this.filterPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 42F));
this.filterPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.filterPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 42F));
this.filterPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 105F));
this.filterPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 72F));
this.filterPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 180F));
this.filterPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 66F));
this.filterPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 66F));
this.filterPanel.Controls.Add(this.filterLabel, 0, 0);
this.filterPanel.Controls.Add(this.filterTextBox, 1, 0);
this.filterPanel.Controls.Add(this.levelLabel, 2, 0);
this.filterPanel.Controls.Add(this.levelComboBox, 3, 0);
this.filterPanel.Controls.Add(this.applicationLabel, 4, 0);
this.filterPanel.Controls.Add(this.applicationComboBox, 5, 0);
this.filterPanel.Controls.Add(this.applyFilterButton, 6, 0);
this.filterPanel.Controls.Add(this.clearFilterButton, 7, 0);
this.filterPanel.Dock = System.Windows.Forms.DockStyle.Top;
this.filterPanel.Location = new System.Drawing.Point(0, 31);
this.filterPanel.Name = "filterPanel";
this.filterPanel.Padding = new System.Windows.Forms.Padding(6, 5, 6, 5);
this.filterPanel.RowCount = 1;
this.filterPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.filterPanel.Size = new System.Drawing.Size(1184, 38);
this.filterPanel.TabIndex = 1;
//
// filter controls
//
this.filterLabel.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.filterLabel.AutoSize = true;
this.filterLabel.Text = "Filter";
this.filterTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.filterTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.filterTextBox_KeyDown);
this.levelLabel.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.levelLabel.AutoSize = true;
this.levelLabel.Text = "Level";
this.levelComboBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.levelComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.levelComboBox.SelectedIndexChanged += new System.EventHandler(this.filterValueChanged);
this.applicationLabel.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.applicationLabel.AutoSize = true;
this.applicationLabel.Text = "Application";
this.applicationComboBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.applicationComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.applicationComboBox.SelectedIndexChanged += new System.EventHandler(this.filterValueChanged);
this.applyFilterButton.Dock = System.Windows.Forms.DockStyle.Fill;
this.applyFilterButton.Text = "Apply";
this.applyFilterButton.Click += new System.EventHandler(this.applyFilterButton_Click);
this.clearFilterButton.Dock = System.Windows.Forms.DockStyle.Fill;
this.clearFilterButton.Text = "Reset";
this.clearFilterButton.Click += new System.EventHandler(this.clearFilterButton_Click);
//
// logContextMenu
//
this.logContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.copySelectionMenuItem,
this.copyLineMenuItem,
this.copyAllMenuItem});
this.copySelectionMenuItem.Text = "Copy selection";
this.copySelectionMenuItem.Click += new System.EventHandler(this.copySelectionMenuItem_Click);
this.copyLineMenuItem.Text = "Copy current line";
this.copyLineMenuItem.Click += new System.EventHandler(this.copyLineMenuItem_Click);
this.copyAllMenuItem.Text = "Copy all visible logs";
this.copyAllMenuItem.Click += new System.EventHandler(this.copyAllButton_Click);
//
// logView
//
this.logView.BackColor = System.Drawing.SystemColors.Window;
this.logView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.logView.ContextMenuStrip = this.logContextMenu;
this.logView.DetectUrls = false;
this.logView.Dock = System.Windows.Forms.DockStyle.Fill;
this.logView.Font = new System.Drawing.Font("Consolas", 9F);
this.logView.HideSelection = false;
this.logView.Location = new System.Drawing.Point(0, 69);
this.logView.Name = "logView";
this.logView.ReadOnly = true;
this.logView.Size = new System.Drawing.Size(1184, 554);
this.logView.TabIndex = 2;
this.logView.Text = "";
this.logView.WordWrap = true;
//
// statusStrip
//
this.statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.receiverStatusLabel,
this.statusSpacer,
this.countStatusLabel});
this.statusStrip.Location = new System.Drawing.Point(0, 623);
this.statusStrip.Name = "statusStrip";
this.statusStrip.Size = new System.Drawing.Size(1184, 22);
this.statusStrip.TabIndex = 3;
this.receiverStatusLabel.Text = "Listening";
this.statusSpacer.Spring = true;
this.countStatusLabel.Text = "Received: 0 | Visible: 0";
//
//
// 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(1184, 645);
this.Controls.Add(this.logView);
this.Controls.Add(this.filterPanel);
this.Controls.Add(this.mainToolStrip);
this.Controls.Add(this.statusStrip);
this.KeyPreview = true;
this.MinimumSize = new System.Drawing.Size(860, 480);
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.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Application live diagnostic";
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.MainForm_KeyDown);
this.mainToolStrip.ResumeLayout(false);
this.mainToolStrip.PerformLayout();
this.filterPanel.ResumeLayout(false);
this.filterPanel.PerformLayout();
this.logContextMenu.ResumeLayout(false);
this.statusStrip.ResumeLayout(false);
this.statusStrip.PerformLayout();
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;
}
}
+220 -398
View File
@@ -1,445 +1,267 @@
using AppDiagnostic.Logging;
using log4net.Config;
using log4net;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
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 readonly Func<int> connectedClientCountProvider;
private readonly Func<bool> activationRequestProvider;
private readonly Timer refreshTimer;
private readonly Font regularLogFont;
private readonly Font boldLogFont;
private List<LogColorRule> colorRules;
private long lastRenderedVersion = -1;
private int observedEntryCount;
private int visibleEntryCount;
private bool loadingFilters;
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(null, null)
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);
}
public MainForm(Func<int> connectedClientCountProvider, Func<bool> activationRequestProvider)
private void OnLogAdded(string log)
{
this.connectedClientCountProvider = connectedClientCountProvider;
this.activationRequestProvider = activationRequestProvider;
InitializeComponent();
Icon = Properties.Resources.TBF_icon;
try
{
RefreshLogView();
}
catch (Exception ex)
{
MessageBox.Show($"Error in OnLogAdded: {ex.Message}");
throw;
}
}
regularLogFont = new Font(logView.Font, FontStyle.Regular);
boldLogFont = new Font(logView.Font, FontStyle.Bold);
colorRules = LogColorRuleRepository.Load();
private void RefreshLogView()
{
// Skontrolujte, či voláme z iného vlákna
if (InvokeRequired)
{
Invoke((Action)RefreshLogView);
return;
}
InitializeFilters();
memoListView.BeginUpdate(); // Zabraňuje vizuálnym aktualizáciám počas vykresľovania
refreshTimer = new Timer { Interval = 250 };
refreshTimer.Tick += refreshTimer_Tick;
refreshTimer.Start();
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;
}
RefreshView(true);
// 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)
{
refreshTimer.Stop();
refreshTimer.Dispose();
regularLogFont.Dispose();
boldLogFont.Dispose();
// 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 InitializeFilters()
private void filterButton_Click_1(object sender, EventArgs e)
{
loadingFilters = true;
levelComboBox.Items.AddRange(new object[] { "All levels", "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" });
levelComboBox.SelectedIndex = 0;
applicationComboBox.Items.Add("All applications");
applicationComboBox.SelectedIndex = 0;
loadingFilters = false;
// Aplikujeme filter na základe textu z filterTextBox
string filterText = filterTextBox.Text;
//_logFilter.ApplyFilter(filterText); // Aplikovanie filtra
_logAdapter.ApplyFilter(filterText);
}
private void refreshTimer_Tick(object sender, EventArgs e)
private void SubForm_FormClosing(object sender, FormClosingEventArgs e)
{
ProcessActivationRequest();
RefreshView(false);
}
private void ProcessActivationRequest()
{
if (activationRequestProvider == null || !activationRequestProvider())
{
return;
}
if (WindowState == FormWindowState.Minimized)
{
WindowState = FormWindowState.Normal;
}
Show();
BringToFront();
Activate();
}
private void RefreshView(bool forceFullRefresh)
{
List<DiagnosticLogEntry> snapshot = DiagnosticLogStore.Instance.GetSnapshot();
UpdateApplicationFilter(snapshot);
if (pauseButton.Checked)
{
UpdateStatus(snapshot.Count);
return;
}
long currentVersion = DiagnosticLogStore.Instance.Version;
if (!forceFullRefresh && currentVersion == lastRenderedVersion)
{
UpdateStatus(snapshot.Count);
return;
}
bool canAppend = !forceFullRefresh && snapshot.Count > observedEntryCount;
if (canAppend)
{
AppendEntries(snapshot.Skip(observedEntryCount));
}
else
{
RebuildView(snapshot);
}
observedEntryCount = snapshot.Count;
lastRenderedVersion = currentVersion;
UpdateStatus(snapshot.Count);
}
private void RebuildView(IEnumerable<DiagnosticLogEntry> entries)
{
logView.SuspendLayout();
logView.Clear();
visibleEntryCount = 0;
AppendEntries(entries, false);
logView.ResumeLayout();
if (autoScrollButton.Checked)
{
ScrollToEnd();
}
}
private void AppendEntries(IEnumerable<DiagnosticLogEntry> entries, bool scrollAfterAppend = true)
{
int originalSelectionStart = logView.SelectionStart;
int originalSelectionLength = logView.SelectionLength;
foreach (DiagnosticLogEntry entry in entries)
{
if (!MatchesFilter(entry))
{
continue;
}
AppendEntry(entry);
visibleEntryCount++;
}
if (scrollAfterAppend && autoScrollButton.Checked)
{
ScrollToEnd();
}
else if (!autoScrollButton.Checked)
{
int safeStart = Math.Min(originalSelectionStart, logView.TextLength);
int safeLength = Math.Min(originalSelectionLength, logView.TextLength - safeStart);
logView.Select(safeStart, safeLength);
}
}
private void AppendEntry(DiagnosticLogEntry entry)
{
string text = DiagnosticLogFormatter.Format(entry);
if (logView.TextLength > 0)
{
logView.AppendText(Environment.NewLine);
}
int startIndex = logView.TextLength;
logView.AppendText(text);
LogColorizer.Apply(logView, startIndex, text.Length, text, colorRules, regularLogFont, boldLogFont);
logView.Select(logView.TextLength, 0);
}
private bool MatchesFilter(DiagnosticLogEntry entry)
{
string selectedLevel = levelComboBox.SelectedItem as string;
if (!string.IsNullOrEmpty(selectedLevel) && selectedLevel != "All levels" && !string.Equals(entry.Level, selectedLevel, StringComparison.OrdinalIgnoreCase))
{
return false;
}
string selectedApplication = applicationComboBox.SelectedItem as string;
if (!string.IsNullOrEmpty(selectedApplication) && selectedApplication != "All applications" && !string.Equals(entry.ApplicationName, selectedApplication, StringComparison.OrdinalIgnoreCase))
{
return false;
}
string filter = filterTextBox.Text.Trim();
return filter.Length == 0 || DiagnosticLogFormatter.Format(entry).IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0;
}
private void UpdateApplicationFilter(IEnumerable<DiagnosticLogEntry> entries)
{
string selectedApplication = applicationComboBox.SelectedItem as string ?? "All applications";
List<string> applications = entries.Select(entry => entry.ApplicationName).Where(name => !string.IsNullOrWhiteSpace(name)).Distinct(StringComparer.OrdinalIgnoreCase).OrderBy(name => name).ToList();
List<string> currentApplications = applicationComboBox.Items.Cast<string>().Skip(1).ToList();
if (applications.SequenceEqual(currentApplications, StringComparer.OrdinalIgnoreCase))
{
return;
}
loadingFilters = true;
applicationComboBox.BeginUpdate();
applicationComboBox.Items.Clear();
applicationComboBox.Items.Add("All applications");
applicationComboBox.Items.AddRange(applications.Cast<object>().ToArray());
applicationComboBox.SelectedItem = applicationComboBox.Items.Cast<string>().FirstOrDefault(item => string.Equals(item, selectedApplication, StringComparison.OrdinalIgnoreCase)) ?? "All applications";
applicationComboBox.EndUpdate();
loadingFilters = false;
}
private void UpdateStatus(int storedCount)
{
int clients = connectedClientCountProvider?.Invoke() ?? 0;
receiverStatusLabel.Text = pauseButton.Checked ? $"Paused | Connected sources: {clients}" : $"Listening | Connected sources: {clients}";
countStatusLabel.Text = $"Received: {DiagnosticLogStore.Instance.TotalReceived:N0} | Stored: {storedCount:N0} | Visible: {visibleEntryCount:N0}";
}
private void ScrollToEnd()
{
logView.Select(logView.TextLength, 0);
logView.ScrollToCaret();
}
private void pauseButton_Click(object sender, EventArgs e)
{
pauseButton.Text = pauseButton.Checked ? "Resume" : "Pause";
if (!pauseButton.Checked)
{
RefreshView(true);
}
else
{
UpdateStatus(DiagnosticLogStore.Instance.GetSnapshot().Count);
}
}
private void refreshButton_Click(object sender, EventArgs e)
{
RefreshView(true);
}
private void clearButton_Click(object sender, EventArgs e)
{
DiagnosticLogStore.Instance.Clear();
observedEntryCount = 0;
visibleEntryCount = 0;
RefreshView(true);
}
private void saveButton_Click(object sender, EventArgs e)
{
using (var dialog = new SaveFileDialog())
{
dialog.Filter = "Text files (*.txt)|*.txt|Log files (*.log)|*.log|All files (*.*)|*.*";
dialog.Title = "Save visible diagnostic logs";
dialog.DefaultExt = "log";
dialog.FileName = $"AppDiagnostic_{DateTime.Now:yyyyMMdd_HHmmss}.log";
if (dialog.ShowDialog(this) != DialogResult.OK)
{
return;
}
try
{
File.WriteAllText(dialog.FileName, logView.Text);
}
catch (Exception exception)
{
MessageBox.Show(this, $"Unable to save diagnostic logs: {exception.Message}", "Save error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private void copyButton_Click(object sender, EventArgs e)
{
CopySelectionOrCurrentLine();
}
private void copySelectionMenuItem_Click(object sender, EventArgs e)
{
if (logView.SelectionLength > 0)
{
CopyText(logView.SelectedText);
}
}
private void copyLineMenuItem_Click(object sender, EventArgs e)
{
CopyCurrentLine();
}
private void copyAllButton_Click(object sender, EventArgs e)
{
CopyText(logView.Text);
}
private void CopySelectionOrCurrentLine()
{
if (logView.SelectionLength > 0)
{
CopyText(logView.SelectedText);
}
else
{
CopyCurrentLine();
}
}
private void CopyCurrentLine()
{
if (logView.Lines.Length == 0)
{
return;
}
int lineIndex = logView.GetLineFromCharIndex(logView.SelectionStart);
if (lineIndex >= 0 && lineIndex < logView.Lines.Length)
{
CopyText(logView.Lines[lineIndex]);
}
}
private void CopyText(string text)
{
if (string.IsNullOrEmpty(text))
{
return;
}
try
{
Clipboard.SetText(text);
// Vaša logika pri uzatváraní formy
// this.Hide(); // Podforma sa len skryje
}
catch (Exception exception)
catch (Exception ex)
{
MessageBox.Show(this, $"Unable to copy text: {exception.Message}", "Clipboard error", MessageBoxButtons.OK, MessageBoxIcon.Error);
// Ošetrenie výnimky
MessageBox.Show("Chyba pri zatváraní pod-aplikácie: " + ex.Message);
}
}
private void wrapButton_Click(object sender, EventArgs e)
private void SaveLogsToFile()
{
logView.WordWrap = wrapButton.Checked;
}
private void alwaysOnTopButton_Click(object sender, EventArgs e)
{
TopMost = alwaysOnTopButton.Checked;
}
private void colorRulesButton_Click(object sender, EventArgs e)
{
using (var editor = new ColorRuleEditorForm(colorRules))
using (SaveFileDialog saveFileDialog = new SaveFileDialog())
{
if (editor.ShowDialog(this) != DialogResult.OK)
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)
{
return;
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);
}
}
}
}
colorRules = editor.Rules;
private void button2_Click(object sender, EventArgs e)
{
this.Close();
//this.Hide(); // Podforma sa len skryje
}
try
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)
{
LogColorRuleRepository.Save(colorRules);
// 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();
}
catch (Exception exception)
{
MessageBox.Show(this, $"Unable to save color rules: {exception.Message}", "Settings error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
RefreshView(true);
}
}
private void applyFilterButton_Click(object sender, EventArgs e)
{
RefreshView(true);
}
private void clearFilterButton_Click(object sender, EventArgs e)
{
loadingFilters = true;
filterTextBox.Clear();
levelComboBox.SelectedIndex = 0;
applicationComboBox.SelectedIndex = 0;
loadingFilters = false;
RefreshView(true);
}
private void filterValueChanged(object sender, EventArgs e)
{
if (!loadingFilters)
{
RefreshView(true);
}
}
private void filterTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
RefreshView(true);
e.SuppressKeyPress = true;
}
else if (e.KeyCode == Keys.Escape)
{
clearFilterButton_Click(sender, EventArgs.Empty);
e.SuppressKeyPress = true;
}
}
private void closeButton_Click(object sender, EventArgs e)
{
Close();
}
private void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.F)
{
filterTextBox.Focus();
filterTextBox.SelectAll();
e.SuppressKeyPress = true;
}
else if (e.Control && e.KeyCode == Keys.S)
{
saveButton_Click(sender, EventArgs.Empty);
e.SuppressKeyPress = true;
}
else if (e.KeyCode == Keys.F5)
{
RefreshView(true);
e.SuppressKeyPress = true;
}
}
}
+3 -21
View File
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -15,26 +14,9 @@ namespace AppDiagnostic
[STAThread]
static void Main()
{
bool isFirstInstance;
using (var instanceMutex = new Mutex(true, "Sensus.AppDiagnostic.SingleInstance", out isFirstInstance))
using (var activationEvent = new EventWaitHandle(false, EventResetMode.AutoReset, "Sensus.AppDiagnostic.Activate"))
{
if (!isFirstInstance)
{
activationEvent.Set();
return;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (var diagnosticServer = new DiagnosticPipeServer(DiagnosticLogStore.Instance.Add))
{
diagnosticServer.Start();
Application.Run(new MainForm(() => diagnosticServer.ConnectedClientCount, () => activationEvent.WaitOne(0)));
}
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
+3 -3
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Common</RootNamespace>
<AssemblyName>Common</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@@ -114,4 +114,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
+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()
+54 -55
View File
@@ -2,7 +2,6 @@
/// Copyright (c) 2016-2021 Sensus Slovensko a.s.
///
using System;
using System.Linq;
namespace Common
{
@@ -287,97 +286,97 @@ namespace Common
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 };
return Quantity.Volume;
case Unit.l:
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
return Quantity.Volume;
case Unit.dm3:
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
return Quantity.Volume;
case Unit.USgal:
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
return Quantity.Volume;
case Unit.UKgal:
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
return Quantity.Volume;
case Unit.cf:
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
return Quantity.Volume;
case Unit.m3:
return new[] { Quantity.Volume, Quantity.MultiFunctionalVariables };
return Quantity.Volume;
case Unit.lph:
return new[] { Quantity.Flow };
return Quantity.Flow;
case Unit.cfph:
return new[] { Quantity.Flow };
return Quantity.Flow;
case Unit.lpm:
return new[] { Quantity.Flow };
return Quantity.Flow;
case Unit.USgalpm:
return new[] { Quantity.Flow };
return Quantity.Flow;
case Unit.m3ph:
return new[] { Quantity.Flow };
return Quantity.Flow;
case Unit.lps:
return new[] { Quantity.Flow };
return Quantity.Flow;
case Unit.USgalps:
return new[] { Quantity.Flow };
return Quantity.Flow;
case Unit.m3pm:
return new[] { Quantity.Flow };
return Quantity.Flow;
case Unit.cfs:
return new[] { Quantity.Flow };
return Quantity.Flow;
case Unit.gps:
return new[] { Quantity.MassFlow};
return Quantity.MassFlow;
case Unit.gpm:
return new[] { Quantity.MassFlow};
return Quantity.MassFlow;
case Unit.gph:
return new[] { Quantity.MassFlow};
return Quantity.MassFlow;
case Unit.kgps:
return new[] { Quantity.MassFlow};
return Quantity.MassFlow;
case Unit.kgpm:
return new[] { Quantity.MassFlow};
return Quantity.MassFlow;
case Unit.kgph:
return new[] { Quantity.MassFlow};
return Quantity.MassFlow;
case Unit.tps:
return new[] { Quantity.MassFlow};
return Quantity.MassFlow;
case Unit.tpm:
return new[] { Quantity.MassFlow};
return Quantity.MassFlow;
case Unit.tph:
return new[] { Quantity.MassFlow};
return Quantity.MassFlow;
case Unit.lbps:
return new[] { Quantity.MassFlow};
return Quantity.MassFlow;
case Unit.lbpm:
return new[] { Quantity.MassFlow};
return Quantity.MassFlow;
case Unit.lbph:
return new[] { Quantity.MassFlow};
return Quantity.MassFlow;
case Unit.g:
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
return Quantity.Mass;
case Unit.oz:
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
return Quantity.Mass;
case Unit.lb:
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
return Quantity.Mass;
case Unit.kg:
return new[] { Quantity.Mass, Quantity.MultiFunctionalVariables };
return Quantity.Mass;
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 +386,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 +402,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,22 +428,22 @@ 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 };
return Quantity.PulsePerKWh;
case Unit.A:
case Unit.mA:
return new[] { Quantity.Current };
return Quantity.Current;
case Unit.V:
case Unit.mV:
return new[] { Quantity.Voltage };
return Quantity.Voltage;
default:
return new[] { Quantity.Number };
return Quantity.Number;
}
}
+4 -6
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Config</RootNamespace>
<AssemblyName>Config</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
@@ -126,9 +126,7 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Strings.cs.resx" />
<EmbeddedResource Include="Resources\Strings.de.resx">
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Resources\Strings.de.resx" />
<EmbeddedResource Include="Resources\Strings.fr.resx" />
<EmbeddedResource Include="Resources\Strings.it.resx" />
<EmbeddedResource Include="Resources\Strings.pl.resx" />
@@ -152,4 +150,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
+3 -3
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DataMatrix4Net</RootNamespace>
<AssemblyName>DataMatrix4Net</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
@@ -58,4 +58,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DataStreamInterface</RootNamespace>
<AssemblyName>DataStreamInterface</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
@@ -61,4 +61,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
+3 -3
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Decrypt</RootNamespace>
<AssemblyName>Decrypt</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
@@ -63,4 +63,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
+3 -3
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DeviceTest</RootNamespace>
<AssemblyName>DeviceTest</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
@@ -114,4 +114,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
-16
View File
@@ -7,22 +7,6 @@
<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>
+3 -3
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Dirichlet.Numerics</RootNamespace>
<AssemblyName>Dirichlet.Numerics</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
@@ -51,4 +51,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
+3 -3
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Encrypt</RootNamespace>
<AssemblyName>Encrypt</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
@@ -63,4 +63,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
+3 -3
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>EventViewer</RootNamespace>
<AssemblyName>EventViewer</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@@ -142,4 +142,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
@@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>FeatureVectorCalculator</RootNamespace>
<AssemblyName>FeatureVectorCalculator</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@@ -97,4 +97,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
+3 -3
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<ProjectType>Local</ProjectType>
@@ -28,7 +28,7 @@
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>3.5</OldToolsVersion>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
@@ -180,4 +180,4 @@
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>
</Project>
+3 -3
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
<PropertyGroup>
<ProjectType>Local</ProjectType>
@@ -35,7 +35,7 @@
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<OldToolsVersion>2.0</OldToolsVersion>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
@@ -146,4 +146,4 @@
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>
</Project>
+3 -3
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>GenericTest</RootNamespace>
<AssemblyName>GenericTest</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
@@ -160,4 +160,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
-16
View File
@@ -7,22 +7,6 @@
<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>
-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,16 +1,11 @@
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;
@@ -40,154 +35,13 @@ namespace GenesisCordonelInterface.API
/// Occurs when meter batch status information changes.
/// </summary>
public event Action<List<MeterBatchDebugStatus>> MeterBatchStatusChanged;
private readonly IMeterLoginPasswordReader loginPasswordsReader;
private readonly IPreAdjustmentCalibrationParamsReader calibrationParamsReader;
private readonly Core.DataStorage.Writing.CalibrationResultWriter calibrationResultWriter =
new Core.DataStorage.Writing.CalibrationResultWriter(null);
/// <summary>Stores one detached completion record without accessing meter ports.</summary>
public Task<CalibrationWriteResult> StorePreadjustmentCompletionResultAsync(
PreadjustmentCompletionRecord record, CancellationToken token = default(CancellationToken))
{
if (record == null) throw new ArgumentNullException(nameof(record));
// Capture the progress instance and slot for this write, even if a new
// batch is initialized while the database operation is still finishing.
var progress = _innerMeterAPI?._progressProcess;
int? slot = record.Slot;
Action<string> diagnostic = null;
if (progress != null && slot.HasValue && slot.Value > 0)
diagnostic = message => progress.DebugMessage(message, slot.Value, "DB");
return calibrationResultWriter.StorePreadjustmentCompletionAsync(record, token, diagnostic);
}
/// <summary>Creates a synthetic Completion record and exercises the real configured SQL writer.</summary>
public async Task<CompletionProcessResult> StoreSimulatedCompletionResultAsync(
int slot, int pcbId, bool phaseSuccess, string phaseError,
CancellationToken token, Action<string> diagnostic)
{
token.ThrowIfCancellationRequested();
if (slot <= 0) throw new ArgumentOutOfRangeException(nameof(slot));
// Synthetic fixture values belong to GCI, not to the hardware Completion process.
var record = new PreadjustmentCompletionRecord
{
Slot = slot, PcbId = pcbId, MeterSize = 2,
ZeroOffset1 = -20, ZeroOffset2 = -30, ZeroOffset3 = -67,
FirstHitUpdatePeriod = 10, FirstHitShift = 4,
FirstHitPercent1 = 17, FirstHitPercent2 = 13, FirstHitPercent3 = 17,
ToFTempOffset1 = 22260, ToFTempOffset2 = -115410, ToFTempOffset3 = 14992,
Date = DateTime.UtcNow, Successful = phaseSuccess
};
// Optional diagnostic subscribers must never change the storage outcome.
try { diagnostic?.Invoke("Simulated calibration data; actual SQL write requested. Fixture MeterSize=2."); }
catch (Exception) { }
var write = await calibrationResultWriter.StorePreadjustmentCompletionAsync(
record, token, diagnostic).ConfigureAwait(false);
var result = new CompletionProcessResult
{
Slot = slot, Success = phaseSuccess, ProcessName = "PreAdjustment_Completion",
ErrorMessage = phaseError
};
result.DatabaseRecords.Add(record);
result.DatabaseWrites.Add(new CompletionDatabaseWriteResult { Slot = slot, Result = write });
return result;
}
/// <summary>Stores Q3 values supplied by TBF; does not perform Q3 calibration.</summary>
public Task<CalibrationWriteResult> StoreQ3CalibrationResultAsync(
Q3CalibrationRecord record, CancellationToken token = default(CancellationToken))
{
return calibrationResultWriter.StoreQ3CalibrationAsync(record, token);
}
public Task<CalibrationWriteResult> StoreQ3CalibrationResultAsync(
Q3CalibrationRecord record, CancellationToken token, Action<string> diagnostic)
{
return calibrationResultWriter.StoreQ3CalibrationAsync(record, token, diagnostic);
}
public Task<CalibrationWriteResult> StoreSimulatedQ3CalibrationResultAsync(
int pcbId, CancellationToken token, Action<string> diagnostic)
{
token.ThrowIfCancellationRequested();
try { diagnostic?.Invoke("Simulated Q3 fixture values; actual SQL write requested."); }
catch (Exception) { }
return calibrationResultWriter.StoreQ3CalibrationAsync(new Q3CalibrationRecord
{
PcbId = pcbId, P1Offset = -0.3, P1Cal = 15260,
P2Offset = -0.3, P2Cal = 15451, P3Offset = -0.3, P3Cal = 15204,
Date = DateTime.UtcNow
}, token, diagnostic);
}
/// <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)
: this(innerMeterApi, passwordReader, calibrationReader, null)
{
}
public InterfaceOutsideToGCI(
InterfaceGCIToLaatzen innerMeterApi,
IMeterLoginPasswordReader passwordReader,
IPreAdjustmentCalibrationParamsReader calibrationReader,
GciDataStorageConfig storageConfig)
{
_innerMeterAPI = innerMeterApi?? throw new ArgumentNullException(nameof(innerMeterApi));
loginPasswordsReader = passwordReader ?? throw new ArgumentNullException(nameof(passwordReader));
calibrationParamsReader = calibrationReader ?? throw new ArgumentNullException(nameof(calibrationReader));
calibrationResultWriter = new Core.DataStorage.Writing.CalibrationResultWriter(storageConfig);
//init handlers
//_innerMeterAPI._progressProcess.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
}
private async void RequestedCalibrationParamsFromDb(
object sender,
EventArgsProcessProgress e)
{
var progress = e.Value;
var source = progress.CancellationSource;
var token = source.Token;
try
{
token.ThrowIfCancellationRequested();
DataQuery query = new DataQuery();
query.QueryParams.Add(((int)e.Value.Setting.MeterSize).ToString());
Dictionary<string, UInt32> calibrationParams = await ReadPreAdjustmentCalibrationParamsAsync(query, token).ConfigureAwait(false);
lock (progress)
{
if (!token.IsCancellationRequested && ReferenceEquals(progress.CancellationSource, source))
progress.PushedCalibrationParams = calibrationParams;
}
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
if (!token.IsCancellationRequested && ReferenceEquals(progress.CancellationSource, source))
progress.DebugMessage($"Calibration params reading failed: {ex.Message}");
}
finally
{
lock (progress)
{
if (!token.IsCancellationRequested && ReferenceEquals(progress.CancellationSource, source))
progress.CalibrationParamsReadEvent.Set();
}
}
_innerMeterAPI = new InterfaceGCIToLaatzen();
}
// Laatzen ToolBox actions
@@ -196,7 +50,7 @@ namespace GenesisCordonelInterface.API
public PortDetectionResult DetectStreamingPort(int slot)
{
var result = _innerMeterAPI?.DetectStreamingPort(slot);
var result = _innerMeterAPI.DetectStreamingPort(slot);
//RaiseMeterBatchStatusChanged();
return result;
}
@@ -205,14 +59,14 @@ namespace GenesisCordonelInterface.API
int slot,
CancellationToken token = default(CancellationToken))
{
var result = await _innerMeterAPI?.DetectStreamingPortAsync(slot, token);
var result = await _innerMeterAPI.DetectStreamingPortAsync(slot, token);
//RaiseMeterBatchStatusChanged();
return result;
}
public PortDetectionResult DetectRequestPort(int slot)
{
var result = _innerMeterAPI?.DetectRequestPort(slot);
var result = _innerMeterAPI.DetectRequestPort(slot);
//RaiseMeterBatchStatusChanged();
return result;
}
@@ -221,7 +75,7 @@ namespace GenesisCordonelInterface.API
int slot,
CancellationToken token = default(CancellationToken))
{
var result = await _innerMeterAPI?.DetectRequestPortAsync(slot, token);
var result = await _innerMeterAPI.DetectRequestPortAsync(slot, token);
//RaiseMeterBatchStatusChanged();
return result;
}
@@ -230,16 +84,6 @@ namespace GenesisCordonelInterface.API
#region ================================== INIT/UPDATE/GET slot ==================================
/// <summary>Acquires streaming reception until the returned lease is disposed.</summary>
public Task<IDisposable> AcquirePreadjustmentStreamingPortAsync(
int slot, GciPortConfig streamingPort, CancellationToken token = default)
{
if (streamingPort == null) throw new ArgumentNullException(nameof(streamingPort));
var mappedPort = ModelsMapping.MapPort(streamingPort);
if (!mappedPort.HasValue) throw new ArgumentException("A streaming port is required.", nameof(streamingPort));
return _innerMeterAPI.AcquirePreadjustmentStreamingPortAsync(slot, mappedPort.Value, token);
}
/// <summary>
/// Initializes a meter slot using the provided slot configuration.
/// </summary>
@@ -640,7 +484,6 @@ namespace GenesisCordonelInterface.API
return result;
}
#endregion
#region ================================== DEBUG STATUS ==================================
@@ -654,7 +497,7 @@ namespace GenesisCordonelInterface.API
/// </returns>
public List<WorkerDebugStatus> GetWorkerDebugStatuses()
{
return _innerMeterAPI?.GetWorkerDebugStatuses();
return _innerMeterAPI.GetWorkerDebugStatuses();
}
/// <summary>
@@ -666,7 +509,7 @@ namespace GenesisCordonelInterface.API
/// </returns>
public List<MeterBatchDebugStatus> GetMeterBatchDebugStatuses()
{
return _innerMeterAPI?.GetMeterBatchDebugStatuses();
return _innerMeterAPI.GetMeterBatchDebugStatuses();
}
/// <summary>
@@ -695,7 +538,7 @@ namespace GenesisCordonelInterface.API
/// <param name="selected">Selection state.</param>
public void SetSlotSelected(int slot, bool selected)
{
_innerMeterAPI?.SetSlotSelected(slot, selected);
_innerMeterAPI.SetSlotSelected(slot, selected);
RaiseMeterBatchStatusChanged();
}
@@ -708,7 +551,7 @@ namespace GenesisCordonelInterface.API
/// </returns>
public bool IsSlotSelected(int slot)
{
return (bool)(_innerMeterAPI?.IsSlotSelected(slot));
return _innerMeterAPI.IsSlotSelected(slot);
}
/// <summary>
@@ -719,7 +562,7 @@ namespace GenesisCordonelInterface.API
/// </returns>
public List<int> GetSelectedSlots()
{
return _innerMeterAPI?.GetSelectedSlots();
return _innerMeterAPI.GetSelectedSlots();
}
#endregion
@@ -734,7 +577,7 @@ namespace GenesisCordonelInterface.API
/// </returns>
public List<string> GetAllRegisterNames()
{
return _innerMeterAPI?.GetAllRegisterNames();
return _innerMeterAPI.GetAllRegisterNames();
}
#endregion
@@ -746,26 +589,7 @@ namespace GenesisCordonelInterface.API
ProcessProgress pp,
List<MeterStateControl> mc)
{
//init handlers
pp.OnRequestedCalibrationParamsFromDb -= RequestedCalibrationParamsFromDb;
pp.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
return _innerMeterAPI?.Preadjustment_Initialization(pp, mc);
}
public PreAdjustmentInitializationResult Preadjustment_Initialization(
ProcessProgress pp,
List<MeterStateControl> mc,
CancellationToken token)
{
pp.OnRequestedCalibrationParamsFromDb -= RequestedCalibrationParamsFromDb;
pp.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
return _innerMeterAPI?.Preadjustment_Initialization(pp, mc, token);
}
public void CancelPreadjustment()
{
_innerMeterAPI?.CancelPreadjustment();
return _innerMeterAPI.Preadjustment_Initialization(pp, mc);
}
public Task<PreadjustmentDetectResult> PreAdjustment_DetectAsync(
@@ -775,13 +599,6 @@ namespace GenesisCordonelInterface.API
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)
@@ -789,16 +606,6 @@ namespace GenesisCordonelInterface.API
return _innerMeterAPI.PreAdjustment_PreparationAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_PreparationDirect()
{
return _innerMeterAPI.PreAdjustment_PreparationDirect();
}
public PreAdjustmentProcessResult PreAdjustment_PreparationDirect(CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_PreparationDirect(token);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_AmplitudeTestAsync(
int slot,
CancellationToken token = default)
@@ -806,16 +613,6 @@ namespace GenesisCordonelInterface.API
return _innerMeterAPI.PreAdjustment_AmplitudeTestAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_AmplitudeTestDirect()
{
return _innerMeterAPI.PreAdjustment_AmplitudeTestDirect();
}
public PreAdjustmentProcessResult PreAdjustment_AmplitudeTestDirect(CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_AmplitudeTestDirect(token);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_TemperatureCalibrationAsync(
int slot,
CancellationToken token = default)
@@ -823,54 +620,6 @@ namespace GenesisCordonelInterface.API
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibrationDirect()
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDirect();
}
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibrationDirect(CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDirect(token);
}
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibrationDirect(
CalibrationTemperatureInput temperature, CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDirect(temperature, token);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_TemperatureCalibrationAsync(
int slot, CalibrationTemperatureInput temperature, CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationAsync(slot, temperature, token);
}
public TemperatureCalibrationResult PreAdjustment_TemperatureCalibrationDetailedDirect(
CalibrationTemperatureInput temperature, CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDetailedDirect(temperature, token);
}
public Task<TemperatureCalibrationResult> PreAdjustment_TemperatureCalibrationDetailedAsync(
int slot, CalibrationTemperatureInput temperature, CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDetailedAsync(slot, temperature, token);
}
public bool PreAdjustment_PushTemperature(
double temperature)
{
return _innerMeterAPI.PreAdjustment_PushTemperature(temperature);
}
public bool PreAdjustment_PushTemperature(
double temperature,
CancellationToken token)
{
token.ThrowIfCancellationRequested();
return _innerMeterAPI.PreAdjustment_PushTemperature(temperature);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_OffsetTestAsync(
int slot,
CancellationToken token = default)
@@ -878,16 +627,6 @@ namespace GenesisCordonelInterface.API
return _innerMeterAPI.PreAdjustment_OffsetTestAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_OffsetTestDirect()
{
return _innerMeterAPI.PreAdjustment_OffsetTestDirect();
}
public PreAdjustmentProcessResult PreAdjustment_OffsetTestDirect(CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_OffsetTestDirect(token);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_CompletionAsync(
int slot,
CancellationToken token = default)
@@ -895,73 +634,6 @@ namespace GenesisCordonelInterface.API
return _innerMeterAPI.PreAdjustment_CompletionAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_CompletionDirect()
{
return _innerMeterAPI.PreAdjustment_CompletionDirect();
}
public PreAdjustmentProcessResult PreAdjustment_CompletionDirect(CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_CompletionDirect(token);
}
#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,12 +1,5 @@
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
{
@@ -49,87 +42,6 @@ namespace GenesisCordonelInterface.API
/// </summary>
public class PublicModels
{
/// <summary>Persisted values for one meter and one preadjustment attempt.</summary>
public sealed class PreadjustmentCompletionRecord
{
// Diagnostic routing only. Not a database column or SQL parameter.
public int? Slot { get; set; }
public int PcbId { get; set; }
public int? MeterSize { get; set; }
public int? ZeroOffset1 { get; set; }
public int? ZeroOffset2 { get; set; }
public int? ZeroOffset3 { get; set; }
public int? FirstHitUpdatePeriod { get; set; }
public int? FirstHitShift { get; set; }
public int? FirstHitPercent1 { get; set; }
public int? FirstHitPercent2 { get; set; }
public int? FirstHitPercent3 { get; set; }
public int? ToFTempOffset1 { get; set; }
public int? ToFTempOffset2 { get; set; }
public int? ToFTempOffset3 { get; set; }
// Timestamp of the attempt, not the time of a possible later write retry.
public DateTime Date { get; set; }
public bool Successful { get; set; }
}
/// <summary>Q3 values calculated and confirmed by the caller; no port access.</summary>
public sealed class Q3CalibrationRecord
{
public int PcbId { get; set; }
// Preserve the existing TBF meaning/unit of each offset (do not rescale here).
public double? P1Offset { get; set; }
public int? P1Cal { get; set; }
public double? P2Offset { get; set; }
public int? P2Cal { get; set; }
public double? P3Offset { get; set; }
public int? P3Cal { get; set; }
public DateTime Date { get; set; }
}
/// <summary>Completion outcome plus detached SQL inputs and separate write outcomes.</summary>
public sealed class CompletionProcessResult : PreAdjustmentProcessResult
{
public List<PreadjustmentCompletionRecord> DatabaseRecords { get; set; } =
new List<PreadjustmentCompletionRecord>();
public List<CompletionDatabaseWriteResult> DatabaseWrites { get; set; } =
new List<CompletionDatabaseWriteResult>();
public override string ToString()
{
return base.ToString() + ", SQL=[" + string.Join("; ",
DatabaseWrites.Select(w => "Slot=" + w.Slot + ", " + w.Result.Status)) + "]";
}
}
public sealed class CompletionDatabaseWriteResult
{
public int Slot { get; set; }
public CalibrationWriteResult Result { get; set; }
}
public enum CalibrationWriteStatus
{
Failed = 0, Stored = 1, Disabled = 2, Simulated = 3,
// The connection failed during commit; verify the DB before retrying.
OutcomeUnknown = 4
}
/// <summary>Database outcome, independent of the physical calibration outcome.</summary>
public sealed class CalibrationWriteResult
{
public CalibrationWriteStatus Status { get; set; }
public bool Success { get { return Status == CalibrationWriteStatus.Stored; } }
public int PcbId { get; set; }
public long? RecordId { get; set; }
public string ErrorMessage { get; set; }
public override string ToString()
{
return string.Format("Status={0}, PcbId={1}, RecordId={2}, Error={3}",
Status, PcbId, RecordId, ErrorMessage ?? "None");
}
}
public static bool HideSensitiveValues { get; set; } = true;
private static string FormatPassword(string password)
@@ -547,57 +459,6 @@ namespace GenesisCordonelInterface.API
}
}
public class TemperatureCalibrationSlotResult : PreAdjustmentProcessResult
{
public decimal? ReferenceTemperatureCelsius { get; set; }
public decimal? MeterTemperatureCelsius { get; set; }
public decimal? AbsoluteDifferenceCelsius { get; set; }
// The effective existing process limit, reported for diagnostics only.
public decimal? DeviationLimitCelsius { get; set; }
public int SampleCount { get; set; }
public bool? TemperatureCheckPassed { get; set; }
public override string ToString()
{
var culture = System.Globalization.CultureInfo.InvariantCulture;
return base.ToString() + string.Format(culture,
", Reference={0} C, Meter={1} C, Difference={2} C, Limit={3} C, Samples={4}, Check={5}",
ReferenceTemperatureCelsius?.ToString(culture) ?? "N/A",
MeterTemperatureCelsius?.ToString(culture) ?? "N/A",
AbsoluteDifferenceCelsius?.ToString(culture) ?? "N/A",
DeviationLimitCelsius?.ToString(culture) ?? "N/A", SampleCount,
TemperatureCheckPassed.HasValue ? (TemperatureCheckPassed.Value ? "PASSED" : "FAILED") : "NOT_EVALUATED");
}
}
public class TemperatureCalibrationResult : PreAdjustmentProcessResult
{
public List<TemperatureCalibrationSlotResult> Slots { get; set; } =
new List<TemperatureCalibrationSlotResult>();
public bool IsSimulated { get; set; }
public override string ToString()
{
return base.ToString() + ", Simulated=" + IsSimulated +
", TemperatureResults=[" + string.Join("; ", Slots.Select(s => s.ToString())) + "]";
}
// Preserve status for failures before process creation and for simulation.
// Missing measurements must never be represented as a measured zero.
public static TemperatureCalibrationResult FromProcessResult(
PreAdjustmentProcessResult result, bool simulated = false)
{
if (result == null) throw new ArgumentNullException(nameof(result));
var detailed = result as TemperatureCalibrationResult;
if (detailed != null) return detailed;
return new TemperatureCalibrationResult
{
Success = result.Success, Slot = result.Slot, ProcessName = result.ProcessName,
ErrorMessage = result.ErrorMessage, IsSimulated = simulated
};
}
}
public class PreadjustmentDetectResult
{
public bool Success { get; set; }
+21 -1
View File
@@ -11,7 +11,27 @@
</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" />
<bindingRedirect oldVersion="0.0.0.0-2.8.18.11157" newVersion="2.8.18.11157" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Xylem.Common.CommonCore.Configuration" publicKeyToken="null" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.8.18.11158" newVersion="2.8.18.11158" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Xylem.Common.Hardware.Interfaces.Ports.PortCore" publicKeyToken="null" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.8.18.11158" newVersion="2.8.18.11158" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Xylem.Common.Logic.SoftwareAccessHelper" publicKeyToken="null" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.8.18.11158" newVersion="2.8.18.11158" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Xylem.Common.Utils.Crc16Ccitt" publicKeyToken="null" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.8.18.11158" newVersion="2.8.18.11158" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Xylem.Common.Utils.Logging" publicKeyToken="null" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.8.18.11158" newVersion="2.8.18.11158" />
</dependentAssembly>
</assemblyBinding>
</runtime>
@@ -14,23 +14,22 @@
"Name": "MeterLoginPasswords",
"Type": "LocalDatabase",
"DataSource": "Server=(localdb)\\MojaDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
"DataSource": "Server=LBUMI_23\\MSSQLSERVER01; Database=TestGenesisReader; 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()."
"Description": "Reads pre-adjustment values from CSV",
"DataSource": "Relative path resolved from application directory",
"QueryTemplate": "CSV/JSON syntax: SELECT [ReturnColumn] WHERE [MatchColumn]=QUERYPARAM or SELECT COLUMN(1) WHERE COLUMN(0)=QUERYPARAM"
},
"Name": "PreAdjustmentCalibrationParams",
"Type": "LocalDatabase",
"DataSource": "Server=(localdb)\\MojaDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
"QueryTemplate": "SELECT [ParameterName], [ParameterValue] FROM [dbo].[PreAdjustmentCalibrationParams] WHERE [MeterSize] = QUERYPARAM"
"Type": "LocalCsv",
"DataSource": "Data\\preadjustment_params.csv",
"QueryTemplate": "SELECT [Offset] WHERE [PcbId] = 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,78 +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"
},
"PreadjustmentCompletionWriter": {
"Enabled": true,
"DataSource": "Server=(localdb)\\MojaDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
"CommandText": "INSERT INTO [dbo].[PreadjustmentCompletion] ([PcbId], [ZeroOffset1], [ZeroOffset2], [ZeroOffset3], [FirstHitUpdatePeriod], [FirstHitShift], [FirstHitPercent1], [FirstHitPercent2], [FirstHitPercent3], [ToFTempOffset1], [ToFTempOffset2], [ToFTempOffset3], [MeterSize], [Date], [Successful]) OUTPUT INSERTED.[Id] VALUES (@PcbId, @ZeroOffset1, @ZeroOffset2, @ZeroOffset3, @FirstHitUpdatePeriod, @FirstHitShift, @FirstHitPercent1, @FirstHitPercent2, @FirstHitPercent3, @ToFTempOffset1, @ToFTempOffset2, @ToFTempOffset3, @MeterSize, @Date, @Successful);",
"UseStoredProcedure": false,
"CommandTimeoutSeconds": 30,
"ParameterMappings": {
"PcbId": "@PcbId",
"ZeroOffset1": "@ZeroOffset1",
"ZeroOffset2": "@ZeroOffset2",
"ZeroOffset3": "@ZeroOffset3",
"FirstHitUpdatePeriod": "@FirstHitUpdatePeriod",
"FirstHitShift": "@FirstHitShift",
"FirstHitPercent1": "@FirstHitPercent1",
"FirstHitPercent2": "@FirstHitPercent2",
"FirstHitPercent3": "@FirstHitPercent3",
"ToFTempOffset1": "@ToFTempOffset1",
"ToFTempOffset2": "@ToFTempOffset2",
"ToFTempOffset3": "@ToFTempOffset3",
"MeterSize": "@MeterSize",
"Date": "@Date",
"Successful": "@Successful"
}
},
"Q3CalibrationWriter": {
"Enabled": true,
"DataSource": "Server=(localdb)\\MojaDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
"CommandText": "INSERT INTO [dbo].[Q3Calibration] ([PcbId], [P1Offset], [P1Cal], [P2Offset], [P2Cal], [P3Offset], [P3Cal], [Date]) OUTPUT INSERTED.[Id] VALUES (@PcbId, @P1Offset, @P1Cal, @P2Offset, @P2Cal, @P3Offset, @P3Cal, @Date);",
"UseStoredProcedure": false,
"CommandTimeoutSeconds": 30,
"ParameterMappings": {
"PcbId": "@PcbId",
"P1Offset": "@P1Offset",
"P1Cal": "@P1Cal",
"P2Offset": "@P2Offset",
"P2Cal": "@P2Cal",
"P3Offset": "@P3Offset",
"P3Cal": "@P3Cal",
"Date": "@Date"
}
}
}
}
@@ -17,9 +17,6 @@ namespace GenesisCordonelInterface.Core.DataStorage.Config
/// </summary>
public class GciDataStorageConfig
{
public GciDatabaseWriterConfig PreadjustmentCompletionWriter { get; set; }
public GciDatabaseWriterConfig Q3CalibrationWriter { get; set; }
/// <summary>
/// Configuration for meter login password lookup.
/// </summary>
@@ -30,4 +27,4 @@ namespace GenesisCordonelInterface.Core.DataStorage.Config
/// </summary>
public DataStorageConfig PreAdjustmentCalibrationParams { get; set; }
}
}
}
@@ -1,22 +0,0 @@
using System.Collections.Generic;
namespace GenesisCordonelInterface.Core.DataStorage.Config
{
/// <summary>SQL Server destination for one logical result type.</summary>
public sealed class GciDatabaseWriterConfig
{
// Missing sections in existing configuration files leave writes disabled.
public bool Enabled { get; set; }
public string DataSource { get; set; }
public string CommandText { get; set; }
public bool UseStoredProcedure { get; set; }
public int CommandTimeoutSeconds { get; set; } = 30;
/// <summary>
/// Optional complete mapping: DTO property name -> SQL parameter name.
/// Null/empty uses the documented default parameter names.
/// Commands must return the persisted positive identity as their first scalar.
/// </summary>
public Dictionary<string, string> ParameterMappings { get; set; }
}
}
@@ -46,17 +46,5 @@ namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
/// 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;
}
}
}
@@ -2,52 +2,17 @@
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
public 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>();
public Dictionary<string, object> Values { get; set; }
/// <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>>();
public DatabaseSearchResult()
{
Values = new Dictionary<string, object>();
}
}
}
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
@@ -15,152 +14,76 @@ namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
/// QueryTemplate must contain QUERYPARAM placeholder.
///
/// Example:
/// SELECT [Password] FROM [dbo].[SkeletonKeys] WHERE [PcbId] = QUERYPARAM
///
/// 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
/// The placeholder is internally converted to SQL parameter @value.
/// </summary>
public class DatabaseDataStorageReader : IDataStorageReader
{
/// <summary>
/// Data storage configuration containing
/// connection string and query template.
/// Data storage configuration containing connection string and query template.
/// </summary>
private readonly DataStorageConfig config;
/// <summary>
/// Creates SQL Server data storage reader
/// using provided configuration.
/// Creates SQL Server data storage reader using provided configuration.
/// </summary>
/// <param name="config">
/// Data storage configuration loaded
/// from gci_config.json.
/// Data storage configuration loaded from gci_config.json.
/// </param>
public DatabaseDataStorageReader(
DataStorageConfig config)
public DatabaseDataStorageReader(DataStorageConfig config)
{
this.config =
config ?? throw new ArgumentNullException(nameof(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.
/// Executes configured SQL query and returns first matching row.
/// </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.
/// DatabaseSearchResult containing returned SQL columns and values.
/// </returns>
public object GetData(DataQuery query)
{
if (query == null)
throw new ArgumentNullException(nameof(query));
ReaderDiagnosticResult sourceResult =
TestSource(true);
ReaderDiagnosticResult sourceResult = TestSource(true);
if (!sourceResult.Success)
throw new InvalidOperationException(sourceResult.Message);
string sqlText =
PrepareSqlText(config.QueryTemplate);
string sqlText = PrepareSqlText(config.QueryTemplate);
object queryValue = ExtractQueryValue(query);
object queryValue =
ExtractQueryValue(query);
using (SqlConnection connection =
new SqlConnection(config.DataSource))
using (SqlCommand command =
new SqlCommand(sqlText, connection))
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())
using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SingleRow))
{
DatabaseSearchResult result =
new DatabaseSearchResult
{
Query = sqlText
};
while (reader.Read())
DatabaseSearchResult result = new DatabaseSearchResult
{
result.Found = true;
Query = sqlText
};
// 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)
if (!reader.Read())
{
result.Found = false;
return result;
}
result.Found = true;
for (int i = 0; i < reader.FieldCount; i++)
{
object value = reader.GetValue(i);
result.Values[reader.GetName(i)] =
value == DBNull.Value ? null : value;
}
return result;
@@ -169,8 +92,7 @@ namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
}
/// <summary>
/// Tests whether SQL Server connection
/// can be opened.
/// Tests whether SQL Server connection can be opened.
/// </summary>
/// <param name="enableDiagnostics">
/// Enables detailed diagnostic output.
@@ -178,70 +100,45 @@ namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
/// <returns>
/// Diagnostic result of SQL connection test.
/// </returns>
public ReaderDiagnosticResult TestSource(
bool enableDiagnostics)
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
{
ReaderDiagnosticResult result =
new ReaderDiagnosticResult();
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
try
{
if (string.IsNullOrWhiteSpace(config.DataSource))
{
throw new InvalidOperationException(
"Data source is empty.");
}
throw new InvalidOperationException("Data source is empty.");
Log(
result,
enableDiagnostics,
"Opening SQL connection.");
Log(result, enableDiagnostics, "Opening SQL connection.");
using (SqlConnection connection =
new SqlConnection(config.DataSource))
using (SqlConnection connection = new SqlConnection(config.DataSource))
{
connection.Open();
Log(
result,
enableDiagnostics,
"Connection opened successfully.");
Log(result, enableDiagnostics, "Connection opened successfully.");
using (SqlCommand command =
new SqlCommand("SELECT 1", connection))
using (SqlCommand command = new SqlCommand("SELECT 1", connection))
{
object value =
command.ExecuteScalar();
Log(
result,
enableDiagnostics,
"Test query result: " + value);
object value = command.ExecuteScalar();
Log(result, enableDiagnostics, "Test query result: " + value);
}
}
result.Success = true;
result.Message =
"Connection to SQL Server OK.";
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());
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.
/// Tests whether configured SQL query can be prepared and executed.
/// </summary>
/// <param name="enableDiagnostics">
/// Enables detailed diagnostic output.
@@ -249,22 +146,16 @@ namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
/// <returns>
/// Diagnostic result of query execution test.
/// </returns>
public ReaderDiagnosticResult TestQuery(
bool enableDiagnostics)
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
{
ReaderDiagnosticResult result =
new ReaderDiagnosticResult();
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
try
{
if (string.IsNullOrWhiteSpace(config.QueryTemplate))
{
throw new InvalidOperationException(
"Query template is empty.");
}
throw new InvalidOperationException("Query template is empty.");
string sqlText =
PrepareSqlText(config.QueryTemplate);
string sqlText = PrepareSqlText(config.QueryTemplate);
Log(result, enableDiagnostics, "Original template:");
Log(result, enableDiagnostics, config.QueryTemplate);
@@ -272,91 +163,69 @@ namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
Log(result, enableDiagnostics, "Prepared SQL:");
Log(result, enableDiagnostics, sqlText);
using (SqlConnection connection =
new SqlConnection(config.DataSource))
using (SqlCommand command =
new SqlCommand(sqlText, connection))
using (SqlConnection connection = new SqlConnection(config.DataSource))
using (SqlCommand command = new SqlCommand(sqlText, connection))
{
AddQueryParameter(command, "TEST");
connection.Open();
object value =
command.ExecuteScalar();
object value = command.ExecuteScalar();
result.Data = value;
Log(
result,
enableDiagnostics,
"Query executed successfully.");
Log(result, enableDiagnostics, "Query executed successfully.");
}
result.Success = true;
result.Message =
"Query executed successfully.";
result.Message = "Query executed successfully.";
}
catch (Exception ex)
{
result.Success = false;
result.Message =
"Query execution failed. " + ex.Message;
Log(
result,
enableDiagnostics,
ex.ToString());
result.Message = "Query execution failed. " + ex.Message;
Log(result, enableDiagnostics, ex.ToString());
}
return result;
}
/// <summary>
/// Converts configured QueryTemplate
/// to executable SQL text.
/// Converts configured QueryTemplate to executable SQL text.
/// </summary>
/// <param name="queryTemplate">
/// SQL query template containing
/// QUERYPARAM placeholder.
/// SQL query template containing QUERYPARAM placeholder.
/// </param>
/// <returns>
/// SQL text with QUERYPARAM replaced
/// by @value parameter.
/// SQL text with QUERYPARAM replaced by @value parameter.
/// </returns>
private static string PrepareSqlText(
string queryTemplate)
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");
return queryTemplate.Replace("QUERYPARAM", "@value");
}
/// <summary>
/// Extracts first query parameter value.
/// </summary>
private static object ExtractQueryValue(
DataQuery query)
/// <param name="query">
/// Data query containing query parameters.
/// </param>
/// <returns>
/// First query parameter value.
/// </returns>
private static object ExtractQueryValue(DataQuery query)
{
if (query.QueryParams == null ||
query.QueryParams.Count == 0)
{
if (query.QueryParams == null || query.QueryParams.Count == 0)
throw new InvalidOperationException(
"DataQuery does not contain any query parameter.");
}
return query.QueryParams[0];
}
@@ -364,25 +233,32 @@ namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
/// <summary>
/// Adds lookup parameter to SQL command.
/// </summary>
private static void AddQueryParameter(
SqlCommand command,
object value)
/// <param name="command">
/// SQL command.
/// </param>
/// <param name="value">
/// Query parameter value.
/// </param>
private static void AddQueryParameter(SqlCommand command, object value)
{
command.Parameters.Clear();
SqlParameter parameter =
command.Parameters.Add(
"@value",
SqlDbType.Variant);
parameter.Value =
value ?? DBNull.Value;
SqlParameter parameter = command.Parameters.Add("@value", SqlDbType.Variant);
parameter.Value = value ?? DBNull.Value;
}
/// <summary>
/// Adds diagnostic line when diagnostics
/// are enabled.
/// Adds diagnostic line when diagnostics are enabled.
/// </summary>
/// <param name="result">
/// Diagnostic result object.
/// </param>
/// <param name="enableDiagnostics">
/// Indicates whether diagnostics are enabled.
/// </param>
/// <param name="message">
/// Diagnostic message.
/// </param>
private static void Log(
ReaderDiagnosticResult result,
bool enableDiagnostics,
@@ -1,5 +1,4 @@
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using System.Threading;
using System.Threading;
using System.Threading.Tasks;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords
@@ -28,7 +27,7 @@ namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.Meter
/// <returns>
/// Password if found; otherwise null.
/// </returns>
string GetPassword(DataQuery query);
string GetPassword(string queryParam);
/// <summary>
/// Reads meter login password asynchronously.
@@ -44,8 +43,8 @@ namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.Meter
/// <returns>
/// Password if found; otherwise null.
/// </returns>
Task<string> ReadMeterLoginPasswordAsync(
DataQuery query,
Task<string> GetPasswordAsync(
string queryParam,
CancellationToken token = default);
}
}
@@ -7,120 +7,85 @@ 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>
/// Reads meter login password from configured GCI data storage.
/// Universal data storage reader selected by configuration.
/// Can represent database, CSV, JSON or REST reader.
/// </summary>
public class MeterLoginPasswordReader : IMeterLoginPasswordReader
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)
{
/// <summary>
/// Universal data storage reader selected by configuration.
/// Can represent database, CSV, JSON or REST reader.
/// </summary>
private readonly IDataStorageReader reader;
reader = DataStorageReaderFactory.Create(config);
}
/// <summary>
/// Ensures that only one password lookup is executed at a time.
/// </summary>
private readonly SemaphoreSlim readLock = new SemaphoreSlim(1, 1);
/// <summary>
/// Asynchronously reads meter login password by query parameter.
/// </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>
public async Task<string> GetPasswordAsync(
string queryParam,
CancellationToken token = default)
{
if (string.IsNullOrWhiteSpace(queryParam))
throw new ArgumentException("Query parameter must not be empty.", nameof(queryParam));
/// <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)
await readLock.WaitAsync(token);
try
{
reader = DataStorageReaderFactory.Create(config);
return await Task.Run(
() => GetPassword(queryParam),
token);
}
/// <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)
finally
{
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();
readLock.Release();
}
}
/// <summary>
/// Reads meter login password by query parameter.
/// </summary>
/// <param name="queryParam">PCB ID or another configured lookup value.</param>
/// <returns>Password if found; otherwise null.</returns>
public string GetPassword(string queryParam)
{
if (string.IsNullOrWhiteSpace(queryParam))
throw new ArgumentException("Query parameter must not be empty.", nameof(queryParam));
DataQuery query = new DataQuery();
query.QueryParams.Add(queryParam);
object result = reader.GetData(query);
if (result is ReaderDiagnosticResult csvResult)
return csvResult.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 +1,43 @@
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Collections.Generic;
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.
/// stored in the configured data source.
///
/// Expected structure:
///
/// MeterSize | ParameterName | ParameterValue
/// Dn_InternalId | ParameterName | ParameterValue
///
/// Example:
/// 3 | GENESISFLOW_MinValidToF | 21990232
/// 3 | GENESISFLOW_Timeout | 1
///
/// 2 | GENESISFLOW_MinValidToF | 21990232
/// 2 | GENESISFLOW_Timeout | 1
///
/// Parameters are grouped by MeterSize and returned
/// Parameters are grouped by Dn_InternalId and returned
/// as key/value pairs where:
///
/// Key = ParameterName
/// Value = ParameterValue
/// </summary>
public interface IPreAdjustmentCalibrationParamsReader
internal interface IPreAdjustmentCalibrationParamsReader
{
/// <summary>
/// Reads all pre-adjustment calibration parameters
/// using the provided data query synchronously.
/// Reads all calibration parameters assigned
/// to a specific DN identifier.
/// </summary>
/// <param name="query">
/// Data query containing meter size as lookup parameter.
/// <param name="dnInternalId">
/// Internal DN identifier (meter size).
/// </param>
/// <returns>
/// Dictionary where key is GENESISFLOW parameter name
/// and value is stored parameter value.
/// Dictionary:
///
/// Key:
/// GENESISFLOW parameter name
///
/// Value:
/// 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);
Task<Dictionary<string, string>> ReadParamsAsync(int dnInternalId);
}
}
@@ -1,305 +1,87 @@
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using System;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Data.SqlClient;
using System.Threading.Tasks;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams
{
/// <summary>
/// Reads pre-adjustment calibration parameters
/// from configured GCI data storage.
/// from SQL database storage.
///
/// The underlying storage type is selected
/// through IDataStorageReader and may represent:
/// Expected table:
///
/// - SQL database
/// - CSV
/// - JSON
/// - REST API
/// dbo.PreAdjustmentCalibrationParams
///
/// Expected storage structure:
///
/// MeterSize | ParameterName | ParameterValue
/// Columns:
/// Dn_InternalId
/// ParameterName
/// ParameterValue
///
/// Example:
///
/// 2 | GENESISFLOW_MinValidToF | 21990232
/// 2 | GENESISFLOW_Timeout | 1
/// 3 | GENESISFLOW_MinValidToF | 21990232
/// 3 | GENESISFLOW_Timeout | 1
///
/// Returned data are represented as:
/// Returns values as dictionary:
///
/// Key = ParameterName
/// Value = ParameterValue
/// GENESISFLOW_MinValidToF -> 21990232
/// GENESISFLOW_Timeout -> 1
/// </summary>
public class PreAdjustmentCalibrationParamsReader
: IPreAdjustmentCalibrationParamsReader
internal class PreAdjustmentCalibrationParamsReader: IPreAdjustmentCalibrationParamsReader
{
/// <summary>
/// Universal data storage reader selected by configuration.
/// Can represent database, CSV, JSON or REST reader.
/// </summary>
private readonly IDataStorageReader reader;
private readonly string _connectionString;
/// <summary>
/// Ensures that only one calibration parameter lookup
/// is executed at a time.
/// Initializes calibration parameter reader.
/// </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 name="connectionString">
/// SQL database connection string.
/// </param>
public PreAdjustmentCalibrationParamsReader(
DataStorageConfig config)
string connectionString)
{
if (config == null)
throw new ArgumentNullException(nameof(config));
reader = DataStorageReaderFactory.Create(config);
_connectionString = connectionString?? throw new ArgumentNullException(nameof(connectionString));
}
/// <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)
/// <inheritdoc/>
public async Task<Dictionary<string, string>> ReadParamsAsync(int dnInternalId)
{
ValidateQuery(query);
var result = new Dictionary<string, string>();
return await Task.Run(
() => GetCalibrationParams(query),
token).ConfigureAwait(false); ;
const string query = @"
SELECT ParameterName, ParameterValue
FROM dbo.PreAdjustmentCalibrationParams
WHERE Dn_InternalId=@Dn_InternalId";
/*await readLock.WaitAsync(token);
using (var connection = new SqlConnection(_connectionString))
try
using (var command = new SqlCommand(query, connection))
{
return await Task.Run(
() => GetCalibrationParams(query),
token);
}
finally
{
readLock.Release();
}*/
}
command.Parameters.AddWithValue(
"@Dn_InternalId",
dnInternalId);
/// <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);
await connection.OpenAsync();
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)
using (var reader =
await command.ExecuteReaderAsync())
{
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))
while (await reader.ReadAsync())
{
values[parameterName] =
parameterValue;
var parameterName = reader["ParameterName"].ToString();
var parameterValue = reader["ParameterValue"].ToString();
if (!string.IsNullOrWhiteSpace(parameterName))
{
result[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);
return result;
}
}
}
@@ -1,232 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using GenesisCordonelInterface.Core.DataStorage.Config;
using static GenesisCordonelInterface.API.PublicModels;
namespace GenesisCordonelInterface.Core.DataStorage.Writing
{
/// <summary>
/// Stores detached result records. Each call owns its connection and transaction;
/// slot workers, request sessions and streaming ownership are never involved.
/// </summary>
public sealed class CalibrationResultWriter
{
private readonly GciDatabaseWriterConfig completion;
private readonly GciDatabaseWriterConfig q3;
public CalibrationResultWriter(GciDataStorageConfig config)
{
completion = Snapshot(config?.PreadjustmentCompletionWriter);
q3 = Snapshot(config?.Q3CalibrationWriter);
}
private static GciDatabaseWriterConfig Snapshot(GciDatabaseWriterConfig config)
{
if (config == null) return null;
return new GciDatabaseWriterConfig
{
Enabled = config.Enabled, DataSource = config.DataSource,
CommandText = config.CommandText, UseStoredProcedure = config.UseStoredProcedure,
CommandTimeoutSeconds = config.CommandTimeoutSeconds,
ParameterMappings = config.ParameterMappings == null ? null :
new Dictionary<string, string>(config.ParameterMappings, StringComparer.Ordinal)
};
}
public Task<CalibrationWriteResult> StorePreadjustmentCompletionAsync(
PreadjustmentCompletionRecord record, CancellationToken token)
{
return StorePreadjustmentCompletionAsync(record, token, null);
}
public Task<CalibrationWriteResult> StorePreadjustmentCompletionAsync(
PreadjustmentCompletionRecord record, CancellationToken token, Action<string> diagnostic)
{
token.ThrowIfCancellationRequested();
if (record == null) throw new ArgumentNullException(nameof(record));
// Materialize all values before the first await; no mutable record is retained.
var values = new Dictionary<string, object>
{
{ "PcbId", record.PcbId }, { "MeterSize", record.MeterSize },
{ "ZeroOffset1", record.ZeroOffset1 }, { "ZeroOffset2", record.ZeroOffset2 },
{ "ZeroOffset3", record.ZeroOffset3 },
{ "FirstHitUpdatePeriod", record.FirstHitUpdatePeriod },
{ "FirstHitShift", record.FirstHitShift },
{ "FirstHitPercent1", record.FirstHitPercent1 },
{ "FirstHitPercent2", record.FirstHitPercent2 },
{ "FirstHitPercent3", record.FirstHitPercent3 },
{ "ToFTempOffset1", record.ToFTempOffset1 },
{ "ToFTempOffset2", record.ToFTempOffset2 },
{ "ToFTempOffset3", record.ToFTempOffset3 },
{ "Date", record.Date }, { "Successful", record.Successful }
};
string error = !record.MeterSize.HasValue || record.MeterSize.Value < 0
? "MeterSize is required and must not be negative." : null;
if (record.Successful && values.Any(v => v.Value == null))
error = "Successful preadjustment requires all calibration values.";
return WriteAsync(completion, record.PcbId, values, error, token, diagnostic);
}
public Task<CalibrationWriteResult> StoreQ3CalibrationAsync(
Q3CalibrationRecord record, CancellationToken token)
{
return StoreQ3CalibrationAsync(record, token, null);
}
public Task<CalibrationWriteResult> StoreQ3CalibrationAsync(
Q3CalibrationRecord record, CancellationToken token, Action<string> diagnostic)
{
token.ThrowIfCancellationRequested();
if (record == null) throw new ArgumentNullException(nameof(record));
var values = new Dictionary<string, object>
{
{ "PcbId", record.PcbId }, { "P1Offset", record.P1Offset },
{ "P1Cal", record.P1Cal }, { "P2Offset", record.P2Offset },
{ "P2Cal", record.P2Cal }, { "P3Offset", record.P3Offset },
{ "P3Cal", record.P3Cal }, { "Date", record.Date }
};
string error = values.Any(v => v.Value == null)
? "Q3 requires the offset and confirmed calibration factor for all three paths."
: null;
if (values.Values.OfType<double>().Any(v => double.IsNaN(v) || double.IsInfinity(v)))
error = "Q3 offsets must be finite numbers.";
return WriteAsync(q3, record.PcbId, values, error, token, diagnostic);
}
private static async Task<CalibrationWriteResult> WriteAsync(
GciDatabaseWriterConfig config, int pcbId, IDictionary<string, object> values,
string validationError, CancellationToken token, Action<string> diagnostic = null)
{
string operation = values.ContainsKey("Successful") ? "Completion" : "Q3 calibration";
Report(diagnostic, operation + " database write: START.");
var result = new CalibrationWriteResult { PcbId = pcbId };
if (config == null || !config.Enabled)
{
result.Status = CalibrationWriteStatus.Disabled;
result.ErrorMessage = "Database writer is not enabled.";
Report(diagnostic, operation + " database write skipped: writer is disabled or not configured.");
return result;
}
bool commitStarted = false;
bool connectionOpened = false;
string stage = "validation";
try
{
if (validationError != null) throw new ArgumentException(validationError);
if (pcbId <= 0) throw new ArgumentException("PcbId must be positive.");
if ((DateTime)values["Date"] == default(DateTime))
throw new ArgumentException("The calibration timestamp is required.");
if (string.IsNullOrWhiteSpace(config.DataSource) || string.IsNullOrWhiteSpace(config.CommandText))
throw new ArgumentException("Writer DataSource and CommandText are required.");
if (config.CommandTimeoutSeconds < 1)
throw new ArgumentException("CommandTimeoutSeconds must be positive.");
using (var connection = new SqlConnection(config.DataSource))
using (var command = new SqlCommand(config.CommandText, connection))
{
command.CommandType = config.UseStoredProcedure ? CommandType.StoredProcedure : CommandType.Text;
command.CommandTimeout = config.CommandTimeoutSeconds;
AddParameters(command, config, values);
stage = "server connection";
// Only log the destination, never credentials or the full connection string.
var destination = new SqlConnectionStringBuilder(config.DataSource);
Report(diagnostic, "Connecting to server '" + destination.DataSource +
"', database '" + destination.InitialCatalog + "'.");
await connection.OpenAsync(token).ConfigureAwait(false);
connectionOpened = true;
Report(diagnostic, "Connection opened.");
token.ThrowIfCancellationRequested();
stage = "transaction start";
using (var transaction = connection.BeginTransaction())
{
command.Transaction = transaction;
stage = "data write";
Report(diagnostic, "Writing " + operation + " data (" +
(config.UseStoredProcedure ? "stored procedure" : "SQL command") + ").");
// The command/procedure must return the stored Id (OUTPUT INSERTED.Id).
object scalar = await command.ExecuteScalarAsync(token).ConfigureAwait(false);
stage = "returned record Id validation";
if (scalar == null || scalar == DBNull.Value)
throw new InvalidOperationException("Writer did not return a record Id; transaction rolled back.");
decimal id = Convert.ToDecimal(scalar, CultureInfo.InvariantCulture);
if (id <= 0 || id != decimal.Truncate(id) || id > long.MaxValue)
throw new InvalidOperationException("Writer returned an invalid record Id; transaction rolled back.");
token.ThrowIfCancellationRequested();
Report(diagnostic, "Write command completed; confirming transaction.");
stage = "transaction commit";
// Once commit starts, cancellation must not hide a committed record.
commitStarted = true;
transaction.Commit();
result.RecordId = (long)id;
result.Status = CalibrationWriteStatus.Stored;
Report(diagnostic, "Transaction committed; RecordId=" + result.RecordId + ".");
}
}
}
catch (OperationCanceledException) when (!commitStarted)
{
Report(diagnostic, operation + " database write cancelled during " + stage + ".");
// Disposing the uncommitted transaction rolls back its write.
throw;
}
catch (Exception ex)
{
result.Status = commitStarted ? CalibrationWriteStatus.OutcomeUnknown : CalibrationWriteStatus.Failed;
result.ErrorMessage = ex.Message;
// Server messages can contain record values or login names. Keep
// the slot log free of these; retain the original error in the result.
var sqlError = ex as SqlException;
string detail = sqlError == null ? ex.GetType().Name :
"SQL error " + sqlError.Number + ", state " + sqlError.State;
Report(diagnostic, operation + " database write failed during " + stage + " (" + detail + ").");
if (commitStarted)
Report(diagnostic, "Commit outcome is unknown. Verify the database before retrying.");
}
finally
{
// The SQL using scopes have already disposed their resources here.
if (connectionOpened) Report(diagnostic, "SQL connection released.");
}
return result;
}
private static void Report(Action<string> diagnostic, string message)
{
if (diagnostic == null) return;
try { diagnostic(message); }
catch (Exception)
{
// A closed/failing diagnostic subscriber must not change the DB outcome.
}
}
private static void AddParameters(SqlCommand command, GciDatabaseWriterConfig config,
IDictionary<string, object> values)
{
var mappings = config.ParameterMappings;
bool custom = mappings != null && mappings.Count > 0;
if (custom && (mappings.Count != values.Count || mappings.Keys.Any(k => !values.ContainsKey(k))))
throw new ArgumentException("ParameterMappings must map every documented record field exactly once.");
var names = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var value in values)
{
string name = custom ? mappings[value.Key] : "@" + value.Key;
if (string.IsNullOrWhiteSpace(name) || name.Length < 2 || name.Length > 128 ||
name[0] != '@' || !(char.IsLetter(name[1]) || name[1] == '_') ||
name.Skip(2).Any(c => !char.IsLetterOrDigit(c) && c != '_') || !names.Add(name))
throw new ArgumentException("Invalid or duplicate SQL parameter name for " + value.Key + ".");
SqlDbType type = value.Key == "Date" ? SqlDbType.DateTime2 :
value.Key == "Successful" ? SqlDbType.Bit :
value.Key == "P1Offset" || value.Key == "P2Offset" || value.Key == "P3Offset"
? SqlDbType.Float : SqlDbType.Int;
command.Parameters.Add(name, type).Value = value.Value ?? DBNull.Value;
}
}
}
}
@@ -1,74 +0,0 @@
using GenesisCordonelInterface.API;
using GenesisCordonelInterface.Core.Config;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.AccessControl;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using UdsReaderType_CalibrationParams = GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams.PreAdjustmentCalibrationParamsReader;
using UdsReaderType_LoginPasswords = GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords.MeterLoginPasswordReader;
using GciPublicModels = GenesisCordonelInterface.API.PublicModels;
using GciEnums = GenesisCordonelInterface.API.Enums;
using GciType = GenesisCordonelInterface.API.InterfaceOutsideToGCI;
using GciDataStorageReadingModels = GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using GciGUIType = GenesisCordonelInterface.UI.MainView;
namespace GenesisCordonelInterface.Core
{
public class Engine
{
GciConfig gciConfig;
public UdsReaderType_LoginPasswords loginPasswordsDataStorageReader;
public UdsReaderType_CalibrationParams calibrationParamsStorageReader;
//readonly UdsWriterType writer;
//diag GUI for GCI
GciGUIType gciGUI;
Form gciGuiHostForm;
//diag GUI for GciBridge
public UserControl gciBridgeGUIUserControl;
public UI.MainForm gciBridgeGuiForm;
public InterfaceOutsideToGCI gciExternalInterface;
/// <summary>
/// Initializes a new instance of the public GCI facade.
/// </summary>
public Engine()
{
gciConfig = GciConfigLoader.LoadDefault();
loginPasswordsDataStorageReader = new UdsReaderType_LoginPasswords
(
new GciDataStorageReadingModels.DataStorageConfig
(
nameof(GciEnums.DataStorageReaderTypes.LoginPasswordsReader),
GciDataStorageReadingModels.DataStorageType.LocalDatabase,
gciConfig.DataStorageSection.MeterLoginPasswords.DataSource,
gciConfig.DataStorageSection.MeterLoginPasswords.QueryTemplate
)
);
calibrationParamsStorageReader = new UdsReaderType_CalibrationParams
(
new GciDataStorageReadingModels.DataStorageConfig
(
nameof(GciEnums.DataStorageReaderTypes.CalibrationParamsReader),
GciDataStorageReadingModels.DataStorageType.LocalDatabase,
gciConfig.DataStorageSection.PreAdjustmentCalibrationParams.DataSource,
gciConfig.DataStorageSection.PreAdjustmentCalibrationParams.QueryTemplate
)
);
gciExternalInterface = new InterfaceOutsideToGCI(
new InterfaceGCIToLaatzen(),
loginPasswordsDataStorageReader,
calibrationParamsStorageReader,
gciConfig.DataStorageSection);
}
}
}
@@ -174,8 +174,7 @@ namespace GenesisCordonelInterface.Core.Threading
public sealed class ApiWorker : IDisposable
{
private static readonly NLog.ILogger Logger = NLog.LogManager.GetLogger("GenesisCordonelInterface.SlotWorker");
private static long nextRequestId;
private static readonly NLog.ILogger Logger = NLog.LogManager.GetLogger("GenesisCordonelInterface-threading");
/// <summary>
/// Thread-safe FIFO queue holding work items.
/// </summary>
@@ -233,12 +232,6 @@ namespace GenesisCordonelInterface.Core.Threading
};
thread.Start();
Logger.Info(
"[SLOT-WORKER] CREATED Worker={0}, WorkerThread={1}, CallerThread={2}",
Name,
thread.ManagedThreadId,
Thread.CurrentThread.ManagedThreadId);
}
/// <summary>
@@ -258,93 +251,31 @@ namespace GenesisCordonelInterface.Core.Threading
var tcs = new TaskCompletionSource<T>(
TaskCreationOptions.RunContinuationsAsynchronously);
long requestId = Interlocked.Increment(ref nextRequestId);
string operation = string.IsNullOrWhiteSpace(operationName) ? action.Method.Name : operationName;
int callerThreadId = Thread.CurrentThread.ManagedThreadId;
Stopwatch queueWait = Stopwatch.StartNew();
try
{
Logger.Debug(
"[SLOT-REQUEST] ENQUEUE RequestId={0}, Worker={1}, Operation={2}, CallerThread={3}, QueueLengthBefore={4}",
requestId,
Name,
operation,
callerThreadId,
QueueLength);
Debug.WriteLine($"ENQUEUE {operationName} slot worker={Name} time={DateTime.Now:HH:mm:ss.fff}");
queue.Add(() =>
{
if (token.IsCancellationRequested)
{
queueWait.Stop();
LastActivity = DateTime.Now;
Logger.Warn(
"[SLOT-REQUEST] CANCELLED_BEFORE_START RequestId={0}, Worker={1}, Operation={2}, WorkerThread={3}, QueueWaitMs={4}, QueueLength={5}",
requestId,
Name,
operation,
Thread.CurrentThread.ManagedThreadId,
queueWait.ElapsedMilliseconds,
QueueLength);
tcs.TrySetCanceled();
return;
}
Stopwatch execution = Stopwatch.StartNew();
try
{
IsBusy = true;
CurrentOperation = operation;
CurrentOperation = operationName ?? action.Method.Name;
LastActivity = DateTime.Now;
LastError = null;
queueWait.Stop();
Logger.Debug(
"[SLOT-REQUEST] START RequestId={0}, Worker={1}, Operation={2}, WorkerThread={3}, CallerThread={4}, QueueWaitMs={5}, QueueLength={6}",
requestId,
Name,
operation,
Thread.CurrentThread.ManagedThreadId,
callerThreadId,
queueWait.ElapsedMilliseconds,
QueueLength);
var result = action();
tcs.TrySetResult(result);
execution.Stop();
Logger.Debug(
"[SLOT-REQUEST] COMPLETE RequestId={0}, Worker={1}, Operation={2}, WorkerThread={3}, DurationMs={4}, QueueLength={5}",
requestId,
Name,
operation,
Thread.CurrentThread.ManagedThreadId,
execution.ElapsedMilliseconds,
QueueLength);
}
catch (Exception ex)
{
execution.Stop();
LastError = ex.Message;
Logger.Error(
ex,
"[SLOT-REQUEST] FAILED RequestId={0}, Worker={1}, Operation={2}, WorkerThread={3}, DurationMs={4}, QueueLength={5}, Error={6}",
requestId,
Name,
operation,
Thread.CurrentThread.ManagedThreadId,
execution.ElapsedMilliseconds,
QueueLength,
ex.Message);
tcs.TrySetException(ex);
}
finally
@@ -355,47 +286,13 @@ namespace GenesisCordonelInterface.Core.Threading
}
}, token);
}
catch (OperationCanceledException)
{
queueWait.Stop();
Logger.Warn(
"[SLOT-REQUEST] ENQUEUE_CANCELLED RequestId={0}, Worker={1}, Operation={2}, CallerThread={3}, QueueWaitMs={4}",
requestId,
Name,
operation,
callerThreadId,
queueWait.ElapsedMilliseconds);
tcs.TrySetCanceled();
}
catch (ObjectDisposedException ex)
{
queueWait.Stop();
Logger.Error(
ex,
"[SLOT-REQUEST] ENQUEUE_FAILED RequestId={0}, Worker={1}, Operation={2}, Reason=WorkerDisposed",
requestId,
Name,
operation);
tcs.TrySetException(ex);
}
catch (InvalidOperationException ex)
{
queueWait.Stop();
var disposedException = new ObjectDisposedException(nameof(ApiWorker), ex);
Logger.Error(
disposedException,
"[SLOT-REQUEST] ENQUEUE_FAILED RequestId={0}, Worker={1}, Operation={2}, Reason=QueueCompleted",
requestId,
Name,
operation);
tcs.TrySetException(disposedException);
tcs.TrySetException(new ObjectDisposedException(nameof(ApiWorker), ex));
}
return tcs.Task;
@@ -430,11 +327,6 @@ namespace GenesisCordonelInterface.Core.Threading
/// </summary>
private void WorkerLoop()
{
Logger.Info(
"[SLOT-WORKER] THREAD_STARTED Worker={0}, WorkerThread={1}",
Name,
Thread.CurrentThread.ManagedThreadId);
try
{
foreach (var item in queue.GetConsumingEnumerable())
@@ -447,26 +339,7 @@ namespace GenesisCordonelInterface.Core.Threading
}
catch (ObjectDisposedException)
{
Logger.Debug(
"[SLOT-WORKER] QUEUE_DISPOSED Worker={0}, WorkerThread={1}",
Name,
Thread.CurrentThread.ManagedThreadId);
}
catch (Exception ex)
{
Logger.Error(
ex,
"[SLOT-WORKER] THREAD_FAILED Worker={0}, WorkerThread={1}, Error={2}",
Name,
Thread.CurrentThread.ManagedThreadId,
ex.Message);
}
finally
{
Logger.Info(
"[SLOT-WORKER] THREAD_STOPPED Worker={0}, WorkerThread={1}",
Name,
Thread.CurrentThread.ManagedThreadId);
// worker is shutting down
}
}
@@ -478,41 +351,14 @@ namespace GenesisCordonelInterface.Core.Threading
if (disposed)
return;
Logger.Info(
"[SLOT-WORKER] DISPOSE_BEGIN Worker={0}, CallerThread={1}, WorkerThread={2}, IsBusy={3}, CurrentOperation={4}, QueueLength={5}",
Name,
Thread.CurrentThread.ManagedThreadId,
thread.ManagedThreadId,
IsBusy,
CurrentOperation ?? "<none>",
QueueLength);
disposed = true;
queue.CompleteAdding();
if (Thread.CurrentThread != thread && thread.IsAlive)
{
bool stopped = thread.Join(1000);
if (!stopped)
{
Logger.Warn(
"[SLOT-WORKER] DISPOSE_WAIT_TIMEOUT Worker={0}, WorkerThread={1}, CurrentOperation={2}, QueueLength={3}",
Name,
thread.ManagedThreadId,
CurrentOperation ?? "<none>",
QueueLength);
}
}
thread.Join(1000);
queue.Dispose();
Logger.Info(
"[SLOT-WORKER] DISPOSE_END Worker={0}, CallerThread={1}, WorkerThread={2}, ThreadAlive={3}",
Name,
Thread.CurrentThread.ManagedThreadId,
thread.ManagedThreadId,
thread.IsAlive);
}
}
}
}
@@ -42,7 +42,7 @@
<HintPath>..\packages\Common\Logic.ProductionToProductMapper.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.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>
@@ -81,9 +81,6 @@
<Reference Include="Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig">
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig.dll</HintPath>
</Reference>
<Reference Include="Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore">
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.dll</HintPath>
</Reference>
<Reference Include="Xylem.Common.Hardware.WaterMeter.Genesis.Registers">
<HintPath>..\packages\Common\Xylem.Common.Hardware.WaterMeter.Genesis.Registers.dll</HintPath>
</Reference>
@@ -120,12 +117,9 @@
<Compile Include="API\InterfaceOutsideToGCI.cs" />
<Compile Include="API\InterfaceGCIToLaatzen.cs" />
<Compile Include="API\PublicModels.cs" />
<Compile Include="Core\Config\GciConfig.cs" />
<Compile Include="Core\Config\GciConfigLoader.cs" />
<Compile Include="Core\DataStorage\Config\GciConfig.cs" />
<Compile Include="Core\DataStorage\Config\GciConfigLoader.cs" />
<Compile Include="Core\DataStorage\Config\GciDataStorageConfig.cs" />
<Compile Include="Core\DataStorage\Config\GciDatabaseWriterConfig.cs" />
<Compile Include="Core\DataStorage\Writing\CalibrationResultWriter.cs" />
<Compile Include="API\Enums.cs" />
<Compile Include="Core\DataStorage\Reading\Common\Providers\CsvDataStorageReader.cs" />
<Compile Include="Core\DataStorage\Reading\Common\Providers\DatabaseDataStorageReader.cs" />
<Compile Include="Core\DataStorage\Reading\Common\Models\DatabaseSearchResult.cs" />
@@ -143,7 +137,6 @@
<Compile Include="Core\DataStorage\Reading\Implementation\MeterLoginPasswords\MeterLoginPasswordReader.cs" />
<Compile Include="Core\DataStorage\Reading\Implementation\PreAdjustmentCalibrationParams\IPreAdjustmentCalibrationParamsReader.cs" />
<Compile Include="Core\DataStorage\Reading\Implementation\PreAdjustmentCalibrationParams\PreAdjustmentCalibrationParamsReader.cs" />
<Compile Include="Core\Engine\Engine.cs" />
<Compile Include="Core\Logging\UiLogBus.cs" />
<Compile Include="Core\Logging\UiTarget.cs" />
<Compile Include="Core\Threading\ApiWorker\ApiWorker.cs" />
@@ -230,8 +223,6 @@
<Content Include="docs\images\GCI__Onboarding_Overview_drawio.svg" />
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__API_architecture__Current_state.svg" />
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__API_architecture__Target_state.svg" />
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__Data_collection_domain.svg" />
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__Meter_management_domain.svg" />
<Content Include="docs\images\logo\logo.png" />
<Content Include="docs\images\logo\logo.svg" />
<Content Include="docs\images\logo\sensus-logo-white-green-rgb.png" />
@@ -264,11 +255,9 @@
<EmbeddedResource Include="UI\StaraTuraAPI_GenesisCordonelInterface\FrmGCIAPI.resx">
<DependentUpon>FrmGCIAPI.cs</DependentUpon>
</EmbeddedResource>
<Content Include="Core\Config\gci_config.json">
<Link>Config\gci_config.json</Link>
<Content Include="Config\gci_config.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="Core\Config\gci_config.json" />
<None Include="docs\articles\API\index.md" />
<None Include="docs\articles\API\PublicModels.md" />
<None Include="docs\articles\API\InterfaceGCIToLaatzen.md" />
@@ -290,11 +279,6 @@
<None Include="docs\pages\gci\page_gci__implementation_to_tbf.md" />
<None Include="docs\pages\gci\page_gci__implementation_universal.md" />
<None Include="docs\pages\gci\page_gci__workers.md" />
<None Include="docs\pages\platform\page_platform__configuration_domain.md" />
<None Include="docs\pages\platform\page_platform__data_collection_domain.md" />
<None Include="docs\pages\platform\page_platform__tbf_implementation.md" />
<None Include="docs\pages\platform\page_platform__current_state.md" />
<None Include="docs\pages\platform\page_platform__home.md" />
<None Include="docs\toc.yml" />
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
@@ -311,7 +295,6 @@
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="Config\" />
<Folder Include="Core\DataStorage\Writing\NewFolder1\" />
<Folder Include="RuntimePackage\Package\" />
</ItemGroup>
@@ -319,10 +302,14 @@
<WCFMetadata Include="Connected Services\" />
</ItemGroup>
<ItemGroup>
<Reference Include="Xylem.Common.Ui.CordonelPreadjustmentUi">
<HintPath>..\packages\Common\Xylem.Common.Ui.CordonelPreadjustmentUi.dll</HintPath>
<Private>True</Private>
</Reference>
<ProjectReference Include="..\ExternalProjects\Laatzen\Genesis\Common\Hardware\WaterMeter\Genesis\GenesisCore\GenesisCore.csproj">
<Project>{04201E95-90A0-4B73-A72F-379739B2A3AE}</Project>
<Name>GenesisCore</Name>
</ProjectReference>
<ProjectReference Include="..\ExternalProjects\Laatzen\Genesis\Common\CordonelPreadjustmentUi\CordonelPreadjustmentUi.csproj">
<Project>{D0C8D887-ED52-40AB-A069-90BCE0E801E2}</Project>
<Name>CordonelPreadjustmentUi</Name>
</ProjectReference>
<ProjectReference Include="..\ExternalProjects\Laatzen\Genesis\Common\Ui\GenesisToolBox\GenesisToolBox.csproj">
<Project>{95A50087-99EE-4349-9A17-C547290DB42F}</Project>
<Name>GenesisToolBox</Name>
@@ -331,4 +318,4 @@
<!-- Import custom GCI runtime packaging logic -->
<Import Project="RuntimePackage\Build\Copy.targets.xml" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
</Project>
@@ -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,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>
@@ -1,15 +0,0 @@
{
"Erase": [
"1\\tstfile",
"1\\fdrdata",
"1\\logdata",
"1\\blklist",
"1\\pulsedbg",
"1\\upg*",
"1\\img*"
],
"Restore": [
{
}
]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,87 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<variable name="BasePath" value="\\DELAZ1FSOPS01-1\CordonelDS$\${loggerpc}" />
<targets>
<!--CalibrationHelper-->
<target name="CalibrationHelper" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\CalibrationHelper.log" CreateDirs="True" layout ="${longdate} | ${level} | ${message} ${exception:format=toString}" archiveAboveSize="50000000" archiveFileName="${BasePath}\${shortdate}\${processname}\{##}CalibrationHelper.log" />
<!--LedRawData-->
<target xsi:type="BufferingWrapper"
name="TargetLedRawDataBase"
bufferSize="10000"
flushTimeout="-1"
slidingTimeout="true"
>
<target name="LedRawDataBase" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\LedRawData_BASE.log" CreateDirs="True" layout ="${longdate} | ${message} ${exception:format=toString}" archiveAboveSize="50000000" archiveFileName="${BasePath}\${shortdate}\${processname}\{##}_LedRawData_BASE.log" />
</target>
<!--C:\GenesisLog\${shortdate}
C:\GenesisLog\${shortdate}-->
<!--MeterBatch-->
<target name="MeterBatch" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\Meter_BASE.log" CreateDirs="True" layout ="${longdate} | ${level} | ${message} ${exception:format=toString}" archiveAboveSize="50000000" archiveFileName="${BasePath}\${shortdate}\${processname}\{##}_Meter_BASE.log" />
<!--Meter-->
<target name="MeterBase" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\Meter_BASE.log" CreateDirs="True" layout ="${longdate} | ${level} | ${message} ${exception:format=toString}" archiveAboveSize="50000000" archiveFileName="${BasePath}\${shortdate}\${processname}\{##}_Meter_BASE.log" />
<target name="Slot:0" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\Meter_SlotSlot:0.log" CreateDirs="True" layout ="${longdate} | ${level} | ${message} ${exception:format=toString}" archiveAboveSize="50000000" archiveFileName="${BasePath}\${shortdate}\${processname}\{##}_Meter_SlotSlot:0.log" />
<!--COM-->
<target name="COMBase" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\COM_BASE.log" CreateDirs="True" layout ="${longdate} | ${level} | ${message} ${exception:format=toString}" archiveAboveSize="50000000" archiveFileName="${BasePath}\${shortdate}\${processname}\{##}_COM_BASE.log" />
<!--ServiceFwUpdateBuilder-->
<target name="ServiceFwUpdateBuilder" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\ServiceFwUpdateBuilder.log" CreateDirs="True" layout ="${longdate} | ${level} | ${message} ${exception:format=toString}" archiveAboveSize="50000000" archiveFileName="${BasePath}\${shortdate}\${processname}\{##}_ServiceFwUpdateBuilder.log" />
<!--ServiceFwUpdateLoader-->
<target name="ServiceFwUpdateLoader" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\ServiceFwUpdateLoader.log" CreateDirs="True" layout ="${longdate} | ${level} | ${message} ${exception:format=toString}" archiveAboveSize="50000000" archiveFileName="${BasePath}\${shortdate}\${processname}\{##}_ServiceFwUpdateLoader.log" />
<!--ServiceFwUpdateSw-->
<target name="ServiceFwUpdateSw" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\ServiceFwUpdateSw.log" CreateDirs="True" layout ="${longdate} | ${level} | ${message} ${exception:format=toString}" archiveAboveSize="50000000" archiveFileName="${BasePath}\${shortdate}\${processname}\{##}_ServiceFwUpdateSw.log" />
<!--default-->
<target name="default" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\default.log" CreateDirs="True" layout ="${longdate} | ${level} | ${message} ${exception:format=toString}" archiveAboveSize="50000000" archiveFileName="${BasePath}\${shortdate}\${processname}\{##}_default.log" />
<target name="PreAdjWr" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\PreAdjWr.log" CreateDirs="True" layout ="${longdate} | ${level} | ${message} ${exception:format=toString}" />
<target name="ctlBatch" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\ctlBatch.log" CreateDirs="True" layout ="${longdate} | ${level} | ${message} ${exception:format=toString}" archiveAboveSize="50000000" archiveFileName="${BasePath}\${shortdate}\${processname}\{##}_ctlBatch.log" />
<target name="StreamingQualityFile" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\StreamingQuality.log" CreateDirs="True" layout ="${longdate} | ${level} | ${message} ${exception:format=toString}" />
<target name="ProductionUIFile" xsi:type="File" fileName="${BasePath}\${shortdate}\${processname}\ProductionUIFile.log" CreateDirs="True" layout ="${longdate} | ${level} | ${message} ${exception:format=toString}" />
</targets>
<rules>
<!--ServiceFwUpdateBuilder-->
<logger name="ServiceFwUpdateBuilder" minLevel="Trace" appendTo="ServiceFwUpdateSw"/>
<!--ServiceFwUpdateLoader-->
<logger name="ServiceFwUpdateLoader" minLevel="Trace" appendTo="ServiceFwUpdateLoader"/>
<!--ServiceFwUpdateSw-->
<logger name="ServiceFwUpdateSw" minLevel="Trace" appendTo="ServiceFwUpdateSw"/>
<!--CalibrationHelper-->
<logger name="CalibrationHelper" minLevel="Trace" appendTo="CalibrationHelper"/>
<!--LedRawData-->
<logger name="LedRawDataBase" minLevel="Trace" appendTo="TargetLedRawDataBase"/>
<!--MeterBatch-->
<logger name="MeterBatch" minLevel="Trace" appendTo="MeterBatch"/>
<!--Meter-->
<logger name="MeterBase" minLevel="Trace" appendTo="MeterBase"/>
<logger name="Slot:0" minLevel="Trace" appendTo="Slot:0"/>
<logger name="GenesisRequestProtocolBase" minLevel="Trace" appendTo="MeterBase"/>
<logger name="MeasurementBase" minLevel="Trace" appendTo="MeterBase"/>
<!--COM-->
<logger name="SerialComPortBase" minLevel="Trace" appendTo="COMBase"/>
<logger name="IrdaBase" minLevel="Trace" appendTo="COMBase"/>
<logger name="RfidBase" minLevel="Trace" appendTo="COMBase"/>
<logger name="TransmitProtocolBase" minLevel="Trace" appendTo="COMBase"/>
<!--default-->
<logger name="*" minLevel="Debug" appendTo="default"/>
<!--Ui logger-->
<logger name="ctlBatch" minLevel="Trace" appendTo="ctlBatch"/>
<logger name="StreamingQuality" minLevel="Trace" appendTo="StreamingQualityFile"/>
<logger name="PreAdjWr" minLevel="Trace" appendTo="PreAdjWr"/>
<logger name="ProductionUI" minLevel="Trace" appendTo="ProductionUIFile"/>
</rules>
</nlog>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
</startup>
</configuration>

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