Merge branch 'feature/task/Cordonel-preadjustmentUI-GenesisMeter-ZeroFlowGenesisMeter' into develop/UnionTown

This commit is contained in:
Marek Frniak 2026-06-12 10:44:51 +02:00
commit 88903f3dac
29 changed files with 2567 additions and 570 deletions

View File

@ -0,0 +1,17 @@
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
}
}
}

View File

@ -42,7 +42,6 @@ namespace GenesisCordonelInterface.API
private readonly MeterBatch _meterBatch = new MeterBatch();
//Preadjustment
public PreAdjustmentSettingsContainer _settings = new PreAdjustmentSettingsContainer();
public ProcessProgress _progressProcess = new ProcessProgress();
public List<MeterStateControl> _meterControls = new List<MeterStateControl>();
public List<MeterStateControl> _tempMeterControls = new List<MeterStateControl>();
@ -55,6 +54,11 @@ namespace GenesisCordonelInterface.API
private readonly ConcurrentDictionary<int, bool> _selectedSlots = new ConcurrentDictionary<int, bool>();
public InterfaceGCIToLaatzen()
{
}
#endregion
#region ================================== Worker ==================================
@ -1592,22 +1596,30 @@ namespace GenesisCordonelInterface.API
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
CancellationToken token = default)
{
return Task.Run(() => Preadjustment_DetectCore(selectedSlots, token), token);
return Task.Run(() => Preadjustment_Detect(selectedSlots, token), token);
}
public PreadjustmentDetectResult PreAdjustment_DetectDirect(
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
CancellationToken token = default)
{
return Preadjustment_Detect(selectedSlots, token);
}
private PreadjustmentDetectResult Preadjustment_DetectCore(
private PreadjustmentDetectResult Preadjustment_Detect(
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
CancellationToken token)
CancellationToken token = default)
{
const string operation = nameof(Preadjustment_DetectCore);
const string operation = nameof(Preadjustment_Detect);
try
{
LogInfo(operation, "Start.");
MeterBatch globalMeterBatch = new MeterBatch();
MeterBatch thermoMeterBatch = new MeterBatch();
if (_settings == null)
throw new ArgumentNullException(nameof(_settings));
if (_progressProcess.Setting == null)
throw new ArgumentNullException(nameof(_progressProcess.Setting));
if (_meterControls == null)
throw new ArgumentNullException(nameof(_meterControls));
@ -1624,7 +1636,7 @@ namespace GenesisCordonelInterface.API
globalMeterBatch = _meterBatch;
_meterControls = CreateMeterControls(selectedSlots);
if (!_settings.GetTempUseTempFlansh())
if (!_progressProcess.Setting.GetTempUseTempFlansh())
_tempMeterControls.Clear();
var allMeterControls = new List<MeterStateControl>();
@ -1654,7 +1666,7 @@ namespace GenesisCordonelInterface.API
SetUnknownStatus(allMeterControls);
CheckTemperatureMeters(
_settings,
_progressProcess.Setting,
_tempMeterControls,
token);
@ -1684,7 +1696,6 @@ namespace GenesisCordonelInterface.API
{
var ctl = new MeterStateControl(slot.Slot);
ctl.SetChecked(true);
ctl.IsEnabled = true;
controls.Add(ctl);
@ -1818,11 +1829,16 @@ namespace GenesisCordonelInterface.API
token);
}
public PreAdjustmentProcessResult PreAdjustment_PreparationDirect()
{
return PreAdjustment_Preparation();
}
/// <summary>
/// Executes standalone preparation process.
/// </summary>
public PreAdjustmentProcessResult PreAdjustment_Preparation(
int slot)
int slot = -1)
{
const string operation = nameof(PreAdjustment_Preparation);
@ -1830,9 +1846,6 @@ namespace GenesisCordonelInterface.API
{
LogInfo(operation, $"Start. Slot={slot}");
var meter = GetMeterThreadSafe(slot);
EnsureConnected(meter);
BaseProcess process = CreatePreparationProcess(_progressProcess);
bool success =
@ -1891,6 +1904,17 @@ namespace GenesisCordonelInterface.API
PreAdjustmentControl.PredefinedMessages.PreparationFailed(pp.Setting.Culture),
60);
}
public bool PreAdjustment_PushCalibrationParams(double temperature)
{
if (_progressProcess != null)
{
_progressProcess.PushedTestBenchTemp = temperature;
_progressProcess.TempretureSelected = true;
return true;
}
return false;
}
#endregion
@ -1904,8 +1928,13 @@ namespace GenesisCordonelInterface.API
token);
}
public PreAdjustmentProcessResult PreAdjustment_AmplitudeTestDirect()
{
return PreAdjustment_AmplitudeTest();
}
public PreAdjustmentProcessResult PreAdjustment_AmplitudeTest(
int slot)
int slot = -1)
{
const string operation = nameof(PreAdjustment_AmplitudeTest);
@ -1913,9 +1942,6 @@ namespace GenesisCordonelInterface.API
{
LogInfo(operation, $"Start. Slot={slot}");
var meter = GetMeterThreadSafe(slot);
EnsureConnected(meter);
if (_progressProcess.Setting.TempOnly)
{
return new PreAdjustmentProcessResult
@ -1992,19 +2018,19 @@ namespace GenesisCordonelInterface.API
token);
}
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibrationDirect()
{
return PreAdjustment_TemperatureCalibration();
}
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibration(
int slot)
int slot = -1)
{
const string operation = nameof(PreAdjustment_TemperatureCalibration);
try
{
LogInfo(operation, $"Start. Slot={slot}");
var meter =
GetMeterThreadSafe(slot);
EnsureConnected(meter);
LogInfo(operation, $"Start.");
BaseProcess process = CreateTemperatureCalibrationProcess(_progressProcess);
@ -2039,6 +2065,18 @@ namespace GenesisCordonelInterface.API
}
}
public bool PreAdjustment_PushTemperature(double temperature)
{
if (_progressProcess != null)
{
_progressProcess.PushedTestBenchTemp = temperature;
_progressProcess.TempretureSelected = true;
return true;
}
return false;
}
/// <summary>
/// Creates temperature calibration process instance based on current configuration.
/// </summary>
@ -2077,8 +2115,13 @@ namespace GenesisCordonelInterface.API
token);
}
public PreAdjustmentProcessResult PreAdjustment_OffsetTestDirect()
{
return PreAdjustment_OffsetTest();
}
public PreAdjustmentProcessResult PreAdjustment_OffsetTest(
int slot)
int slot = -1)
{
const string operation = nameof(PreAdjustment_OffsetTest);
@ -2086,10 +2129,6 @@ namespace GenesisCordonelInterface.API
{
LogInfo(operation, $"Start. Slot={slot}");
var meter = GetMeterThreadSafe(slot);
EnsureConnected(meter);
if (_progressProcess.Setting.TempOnly)
{
return new PreAdjustmentProcessResult
@ -2171,19 +2210,19 @@ namespace GenesisCordonelInterface.API
token);
}
public PreAdjustmentProcessResult PreAdjustment_CompletionDirect()
{
return PreAdjustment_Completion();
}
public PreAdjustmentProcessResult PreAdjustment_Completion(
int slot)
int slot = -1)
{
const string operation = nameof(PreAdjustment_Completion);
try
{
LogInfo(operation, $"Start. Slot={slot}");
var meter =
GetMeterThreadSafe(slot);
EnsureConnected(meter);
LogInfo(operation, $"Start.");
BaseProcess process = CreateCompletionProcess(_progressProcess);

View File

@ -1,11 +1,16 @@
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;
@ -35,13 +40,55 @@ 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;
/// <summary>
/// Initializes a new instance of the public GCI facade.
/// </summary>
public InterfaceOutsideToGCI()
{
_innerMeterAPI = new InterfaceGCIToLaatzen();
}
/// <summary>
/// Initializes a new instance of the public GCI facade.
/// </summary>
public InterfaceOutsideToGCI(
InterfaceGCIToLaatzen innerMeterApi,
IMeterLoginPasswordReader passwordReader,
IPreAdjustmentCalibrationParamsReader calibrationReader)
{
_innerMeterAPI = innerMeterApi?? throw new ArgumentNullException(nameof(innerMeterApi));
loginPasswordsReader = passwordReader ?? throw new ArgumentNullException(nameof(passwordReader));
calibrationParamsReader = calibrationReader ?? throw new ArgumentNullException(nameof(calibrationReader));
//init handlers
//_innerMeterAPI._progressProcess.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
}
private async void RequestedCalibrationParamsFromDb(
object sender,
EventArgsProcessProgress e)
{
try
{
DataQuery query = new DataQuery();
query.QueryParams.Add(((int)e.Value.Setting.MeterSize).ToString());
Dictionary<string, UInt32> calibrationParams = await ReadPreAdjustmentCalibrationParamsAsync(query).ConfigureAwait(false);
e.Value.PushedCalibrationParams = calibrationParams;
}
catch (Exception ex)
{
e.Value.DebugMessage(
$"Calibration params reading failed: {ex.Message}");
}
finally
{
e.Value.CalibrationParamsReadEvent.Set();
}
}
// Laatzen ToolBox actions
@ -50,7 +97,7 @@ namespace GenesisCordonelInterface.API
public PortDetectionResult DetectStreamingPort(int slot)
{
var result = _innerMeterAPI.DetectStreamingPort(slot);
var result = _innerMeterAPI?.DetectStreamingPort(slot);
//RaiseMeterBatchStatusChanged();
return result;
}
@ -59,14 +106,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;
}
@ -75,7 +122,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;
}
@ -484,6 +531,7 @@ namespace GenesisCordonelInterface.API
return result;
}
#endregion
#region ================================== DEBUG STATUS ==================================
@ -497,7 +545,7 @@ namespace GenesisCordonelInterface.API
/// </returns>
public List<WorkerDebugStatus> GetWorkerDebugStatuses()
{
return _innerMeterAPI.GetWorkerDebugStatuses();
return _innerMeterAPI?.GetWorkerDebugStatuses();
}
/// <summary>
@ -509,7 +557,7 @@ namespace GenesisCordonelInterface.API
/// </returns>
public List<MeterBatchDebugStatus> GetMeterBatchDebugStatuses()
{
return _innerMeterAPI.GetMeterBatchDebugStatuses();
return _innerMeterAPI?.GetMeterBatchDebugStatuses();
}
/// <summary>
@ -538,7 +586,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();
}
@ -551,7 +599,7 @@ namespace GenesisCordonelInterface.API
/// </returns>
public bool IsSlotSelected(int slot)
{
return _innerMeterAPI.IsSlotSelected(slot);
return (bool)(_innerMeterAPI?.IsSlotSelected(slot));
}
/// <summary>
@ -562,7 +610,7 @@ namespace GenesisCordonelInterface.API
/// </returns>
public List<int> GetSelectedSlots()
{
return _innerMeterAPI.GetSelectedSlots();
return _innerMeterAPI?.GetSelectedSlots();
}
#endregion
@ -577,7 +625,7 @@ namespace GenesisCordonelInterface.API
/// </returns>
public List<string> GetAllRegisterNames()
{
return _innerMeterAPI.GetAllRegisterNames();
return _innerMeterAPI?.GetAllRegisterNames();
}
#endregion
@ -589,7 +637,10 @@ namespace GenesisCordonelInterface.API
ProcessProgress pp,
List<MeterStateControl> mc)
{
return _innerMeterAPI.Preadjustment_Initialization(pp, mc);
//init handlers
pp.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
return _innerMeterAPI?.Preadjustment_Initialization(pp, mc);
}
public Task<PreadjustmentDetectResult> PreAdjustment_DetectAsync(
@ -599,6 +650,13 @@ 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)
@ -606,6 +664,11 @@ namespace GenesisCordonelInterface.API
return _innerMeterAPI.PreAdjustment_PreparationAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_PreparationDirect()
{
return _innerMeterAPI.PreAdjustment_PreparationDirect();
}
public Task<PreAdjustmentProcessResult> PreAdjustment_AmplitudeTestAsync(
int slot,
CancellationToken token = default)
@ -613,6 +676,11 @@ namespace GenesisCordonelInterface.API
return _innerMeterAPI.PreAdjustment_AmplitudeTestAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_AmplitudeTestDirect()
{
return _innerMeterAPI.PreAdjustment_AmplitudeTestDirect();
}
public Task<PreAdjustmentProcessResult> PreAdjustment_TemperatureCalibrationAsync(
int slot,
CancellationToken token = default)
@ -620,6 +688,17 @@ namespace GenesisCordonelInterface.API
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibrationDirect()
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDirect();
}
public bool PreAdjustment_PushTemperature(
double temperature)
{
return _innerMeterAPI.PreAdjustment_PushTemperature(temperature);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_OffsetTestAsync(
int slot,
CancellationToken token = default)
@ -627,6 +706,11 @@ namespace GenesisCordonelInterface.API
return _innerMeterAPI.PreAdjustment_OffsetTestAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_OffsetTestDirect()
{
return _innerMeterAPI.PreAdjustment_OffsetTestDirect();
}
public Task<PreAdjustmentProcessResult> PreAdjustment_CompletionAsync(
int slot,
CancellationToken token = default)
@ -634,6 +718,68 @@ namespace GenesisCordonelInterface.API
return _innerMeterAPI.PreAdjustment_CompletionAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_CompletionDirect()
{
return _innerMeterAPI.PreAdjustment_CompletionDirect();
}
#endregion
#region ================================== UNI DATA STORAGE READER ==================================
//LoginPasswords reading
/// <summary>
/// Reads meter login password from configured GCI data storage.
/// </summary>
/// <param name="query">
/// Data query containing PCB ID or another configured lookup value.
/// </param>
/// <param name="token">
/// Cancellation token used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// Password if found; otherwise null.
/// </returns>
public Task<string> ReadMeterLoginPasswordAsync(
DataQuery query,
CancellationToken token = default)
{
return loginPasswordsReader.ReadMeterLoginPasswordAsync(
query,
token);
}
//CalibrationParams reading
/// <summary>
/// Reads pre-adjustment calibration parameters
/// from configured GCI data storage.
/// </summary>
/// <param name="query">
/// Data query containing meter size or another configured lookup value.
/// </param>
/// <param name="token">
/// Cancellation token used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// Dictionary:
///
/// Key:
/// Calibration parameter name
///
/// Value:
/// Calibration parameter value
/// </returns>
public Task<Dictionary<string, UInt32>> ReadPreAdjustmentCalibrationParamsAsync(
DataQuery query,
CancellationToken token = default)
{
return calibrationParamsReader.ReadCalibrationParamsAsync(
query,
token);
}
#endregion
}
}

View File

@ -21,15 +21,16 @@
"PreAdjustmentCalibrationParams": {
"___Documentation___": {
"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"
"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": "LocalCsv",
"DataSource": "Data\\preadjustment_params.csv",
"QueryTemplate": "SELECT [Offset] WHERE [PcbId] = QUERYPARAM"
"Type": "LocalDatabase",
"DataSource": "Server=(localdb)\\MojaDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
"QueryTemplate": "SELECT [ParameterName], [ParameterValue] FROM [dbo].[PreAdjustmentCalibrationParams] WHERE [MeterSize] = QUERYPARAM"
}
}
}

View File

@ -0,0 +1,32 @@
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; }
}
}

View File

@ -0,0 +1,177 @@
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));
}
}
}

View File

@ -0,0 +1,36 @@
{
"_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"
}
}
}

View File

@ -46,5 +46,17 @@ 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;
}
}
}

View File

@ -2,17 +2,52 @@
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
{
public class DatabaseSearchResult
/// <summary>
/// Represents result returned by database-based
/// data storage readers.
///
/// Supports both:
///
/// - Single-row lookups
/// (e.g. MeterLoginPasswords)
///
/// - Multi-row queries
/// (e.g. PreAdjustmentCalibrationParams)
/// </summary>
internal class DatabaseSearchResult
{
/// <summary>
/// Indicates whether at least one record
/// was found.
/// </summary>
public bool Found { get; set; }
/// <summary>
/// Executed SQL query text.
/// Mainly intended for diagnostics
/// and troubleshooting.
/// </summary>
public string Query { get; set; }
public Dictionary<string, object> Values { 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 DatabaseSearchResult()
{
Values = new Dictionary<string, object>();
}
/// <summary>
/// All returned rows represented as
/// a collection of column/value dictionaries.
///
/// Intended for queries returning
/// multiple records.
/// </summary>
public List<Dictionary<string, object>> Rows { get; }
= new List<Dictionary<string, object>>();
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
@ -14,76 +15,152 @@ namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
/// QueryTemplate must contain QUERYPARAM placeholder.
///
/// Example:
/// SELECT [Password] FROM [dbo].[SkeletonKeys] WHERE [PcbId] = QUERYPARAM
///
/// The placeholder is internally converted to SQL parameter @value.
/// SELECT [Password]
/// FROM [dbo].[SkeletonKeys]
/// WHERE [PcbId] = QUERYPARAM
///
/// or
///
/// SELECT [ParameterName], [ParameterValue]
/// FROM [dbo].[PreAdjustmentCalibrationParams]
/// WHERE [Dn_InternalId] = QUERYPARAM
///
/// The placeholder is internally converted
/// to SQL parameter @value.
///
/// Supports both:
///
/// - Single-row lookups
/// - Multi-row result sets
/// </summary>
public class DatabaseDataStorageReader : IDataStorageReader
{
/// <summary>
/// Data storage configuration containing connection string and query template.
/// 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 first matching row.
/// Executes configured SQL query and returns
/// matching database records.
///
/// Single-row queries populate:
/// DatabaseSearchResult.Values
///
/// Multi-row queries populate:
/// DatabaseSearchResult.Rows
///
/// For backward compatibility, the first row
/// is also stored in Values.
/// </summary>
/// <param name="query">
/// Query object containing lookup parameter.
/// </param>
/// <returns>
/// DatabaseSearchResult containing returned SQL columns and values.
/// DatabaseSearchResult containing returned
/// database records.
///
/// Values contains the first returned row.
///
/// Rows contains the complete result set.
/// </returns>
public object GetData(DataQuery query)
{
if (query == null)
throw new ArgumentNullException(nameof(query));
ReaderDiagnosticResult sourceResult = TestSource(true);
ReaderDiagnosticResult sourceResult =
TestSource(true);
if (!sourceResult.Success)
throw new InvalidOperationException(sourceResult.Message);
string sqlText = PrepareSqlText(config.QueryTemplate);
object queryValue = ExtractQueryValue(query);
string sqlText =
PrepareSqlText(config.QueryTemplate);
using (SqlConnection connection = new SqlConnection(config.DataSource))
using (SqlCommand command = new SqlCommand(sqlText, connection))
object queryValue =
ExtractQueryValue(query);
using (SqlConnection connection =
new SqlConnection(config.DataSource))
using (SqlCommand command =
new SqlCommand(sqlText, connection))
{
AddQueryParameter(command, queryValue);
connection.Open();
using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SingleRow))
// Full result set is required because some
// storage definitions return multiple records
// (e.g. PreAdjustmentCalibrationParams).
using (SqlDataReader reader =
command.ExecuteReader())
{
DatabaseSearchResult result = new DatabaseSearchResult
{
Query = sqlText
};
DatabaseSearchResult result =
new DatabaseSearchResult
{
Query = sqlText
};
if (!reader.Read())
while (reader.Read())
{
result.Found = false;
return result;
result.Found = true;
// Represents one database row.
Dictionary<string, object> row =
new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++)
{
object value =
reader.GetValue(i);
object normalizedValue =
value == DBNull.Value
? null
: value;
row[reader.GetName(i)] =
normalizedValue;
}
// Preserve first row for legacy consumers
// expecting a single returned database record
// (e.g. MeterLoginPasswordReader).
if (result.Rows.Count == 0)
{
foreach (var item in row)
{
result.Values[item.Key] =
item.Value;
}
}
// Store complete database result set.
result.Rows.Add(row);
}
result.Found = true;
for (int i = 0; i < reader.FieldCount; i++)
if (result.Rows.Count == 0)
{
object value = reader.GetValue(i);
result.Values[reader.GetName(i)] =
value == DBNull.Value ? null : value;
result.Found = false;
}
return result;
@ -92,7 +169,8 @@ 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.
@ -100,45 +178,70 @@ 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.
@ -146,16 +249,22 @@ 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);
@ -163,69 +272,91 @@ 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>
/// <param name="query">
/// Data query containing query parameters.
/// </param>
/// <returns>
/// First query parameter value.
/// </returns>
private static object ExtractQueryValue(DataQuery query)
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];
}
@ -233,32 +364,25 @@ namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
/// <summary>
/// Adds lookup parameter to SQL command.
/// </summary>
/// <param name="command">
/// SQL command.
/// </param>
/// <param name="value">
/// Query parameter value.
/// </param>
private static void AddQueryParameter(SqlCommand command, object value)
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,

View File

@ -1,4 +1,5 @@
using System.Threading;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using System.Threading;
using System.Threading.Tasks;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords
@ -27,7 +28,7 @@ namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.Meter
/// <returns>
/// Password if found; otherwise null.
/// </returns>
string GetPassword(string queryParam);
string GetPassword(DataQuery query);
/// <summary>
/// Reads meter login password asynchronously.
@ -43,8 +44,8 @@ namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.Meter
/// <returns>
/// Password if found; otherwise null.
/// </returns>
Task<string> GetPasswordAsync(
string queryParam,
Task<string> ReadMeterLoginPasswordAsync(
DataQuery query,
CancellationToken token = default);
}
}

View File

@ -7,85 +7,120 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// Reads meter login password from configured GCI data storage.
/// </summary>
public class MeterLoginPasswordReader : IMeterLoginPasswordReader
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords
{
/// <summary>
/// Universal data storage reader selected by configuration.
/// Can represent database, CSV, JSON or REST reader.
/// Reads meter login password from configured GCI data storage.
/// </summary>
private readonly IDataStorageReader reader;
/// <summary>
/// Ensures that only one password lookup is executed at a time.
/// </summary>
private readonly SemaphoreSlim readLock = new SemaphoreSlim(1, 1);
/// <summary>
/// Creates password reader using provided data storage configuration.
/// </summary>
/// <param name="config">Data storage configuration loaded from gci_config.json.</param>
public MeterLoginPasswordReader(DataStorageConfig config)
public class MeterLoginPasswordReader : IMeterLoginPasswordReader
{
reader = DataStorageReaderFactory.Create(config);
}
/// <summary>
/// Universal data storage reader selected by configuration.
/// Can represent database, CSV, JSON or REST reader.
/// </summary>
private readonly IDataStorageReader reader;
/// <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>
/// Ensures that only one password lookup is executed at a time.
/// </summary>
private readonly SemaphoreSlim readLock = new SemaphoreSlim(1, 1);
await readLock.WaitAsync(token);
try
/// <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)
{
return await Task.Run(
() => GetPassword(queryParam),
token);
}
finally
{
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();
reader = DataStorageReaderFactory.Create(config);
}
return result?.ToString();
/// <summary>
/// Asynchronously reads meter login password using
/// the provided data query.
///
/// Thread-safe implementation may serialize
/// access to the underlying storage.
/// </summary>
/// <param name="query">
/// Data query containing lookup parameter values.
/// </param>
/// <param name="token">
/// Cancellation token.
/// </param>
/// <returns>
/// Password if found; otherwise null.
/// </returns>
public async Task<string> ReadMeterLoginPasswordAsync(
DataQuery query,
CancellationToken token = default)
{
if (query == null)
throw new ArgumentNullException(nameof(query));
if (query.QueryParams.Count == 0)
throw new ArgumentException(
"Query does not contain any parameter.",
nameof(query));
await readLock.WaitAsync(token);
try
{
return await Task.Run(
() => GetPassword(query),
token);
}
finally
{
readLock.Release();
}
}
/// <summary>
/// Reads meter login password using
/// the provided data query.
/// </summary>
/// <param name="query">
/// Data query containing lookup parameter values.
/// </param>
/// <returns>
/// Password if found; otherwise null.
/// </returns>
public string GetPassword(
DataQuery query)
{
if (query == null)
throw new ArgumentNullException(nameof(query));
if (query.QueryParams.Count == 0)
throw new ArgumentException(
"Query does not contain any parameter.",
nameof(query));
object result =
reader.GetData(query);
if (result is ReaderDiagnosticResult diagnosticResult)
{
return diagnosticResult.Data?.ToString();
}
if (result is DatabaseSearchResult dbResult)
{
if (!dbResult.Found ||
dbResult.Values == null ||
dbResult.Values.Count == 0)
{
return null;
}
foreach (object value in dbResult.Values.Values)
{
return value?.ToString();
}
}
return result?.ToString();
}
}
}

View File

@ -1,43 +1,62 @@
using System.Collections.Generic;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams
{
/// <summary>
/// Provides access to pre-adjustment calibration parameters
/// stored in the configured data source.
/// stored in the configured GCI data source.
///
/// Expected structure:
/// Dn_InternalId | ParameterName | ParameterValue
///
/// MeterSize | ParameterName | ParameterValue
///
/// Example:
/// 3 | GENESISFLOW_MinValidToF | 21990232
/// 3 | GENESISFLOW_Timeout | 1
///
/// Parameters are grouped by Dn_InternalId and returned
/// 2 | GENESISFLOW_MinValidToF | 21990232
/// 2 | GENESISFLOW_Timeout | 1
///
/// Parameters are grouped by MeterSize and returned
/// as key/value pairs where:
///
/// Key = ParameterName
/// Value = ParameterValue
/// </summary>
internal interface IPreAdjustmentCalibrationParamsReader
public interface IPreAdjustmentCalibrationParamsReader
{
/// <summary>
/// Reads all calibration parameters assigned
/// to a specific DN identifier.
/// Reads all pre-adjustment calibration parameters
/// using the provided data query synchronously.
/// </summary>
/// <param name="dnInternalId">
/// Internal DN identifier (meter size).
/// <param name="query">
/// Data query containing meter size as lookup parameter.
/// </param>
/// <returns>
/// Dictionary:
///
/// Key:
/// GENESISFLOW parameter name
///
/// Value:
/// Stored parameter value
/// Dictionary where key is GENESISFLOW parameter name
/// and value is stored parameter value.
/// </returns>
Task<Dictionary<string, string>> ReadParamsAsync(int dnInternalId);
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);
}
}

View File

@ -1,87 +1,305 @@
using System;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Threading;
using System.Threading.Tasks;
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams
{
/// <summary>
/// Reads pre-adjustment calibration parameters
/// from SQL database storage.
/// from configured GCI data storage.
///
/// Expected table:
/// The underlying storage type is selected
/// through IDataStorageReader and may represent:
///
/// dbo.PreAdjustmentCalibrationParams
/// - SQL database
/// - CSV
/// - JSON
/// - REST API
///
/// Columns:
/// Dn_InternalId
/// ParameterName
/// ParameterValue
/// Expected storage structure:
///
/// MeterSize | ParameterName | ParameterValue
///
/// Example:
///
/// 3 | GENESISFLOW_MinValidToF | 21990232
/// 3 | GENESISFLOW_Timeout | 1
/// 2 | GENESISFLOW_MinValidToF | 21990232
/// 2 | GENESISFLOW_Timeout | 1
///
/// Returns values as dictionary:
/// Returned data are represented as:
///
/// GENESISFLOW_MinValidToF -> 21990232
/// GENESISFLOW_Timeout -> 1
/// Key = ParameterName
/// Value = ParameterValue
/// </summary>
internal class PreAdjustmentCalibrationParamsReader: IPreAdjustmentCalibrationParamsReader
public class PreAdjustmentCalibrationParamsReader
: IPreAdjustmentCalibrationParamsReader
{
private readonly string _connectionString;
/// <summary>
/// Universal data storage reader selected by configuration.
/// Can represent database, CSV, JSON or REST reader.
/// </summary>
private readonly IDataStorageReader reader;
/// <summary>
/// Initializes calibration parameter reader.
/// Ensures that only one calibration parameter lookup
/// is executed at a time.
/// </summary>
/// <param name="connectionString">
/// SQL database connection string.
private readonly SemaphoreSlim readLock =
new SemaphoreSlim(1, 1);
/// <summary>
/// Creates calibration parameter reader using
/// provided data storage configuration.
/// </summary>
/// <param name="config">
/// Data storage configuration loaded from gci_config.json.
/// </param>
public PreAdjustmentCalibrationParamsReader(
string connectionString)
DataStorageConfig config)
{
_connectionString = connectionString?? throw new ArgumentNullException(nameof(connectionString));
if (config == null)
throw new ArgumentNullException(nameof(config));
reader = DataStorageReaderFactory.Create(config);
}
/// <inheritdoc/>
public async Task<Dictionary<string, string>> ReadParamsAsync(int dnInternalId)
/// <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)
{
var result = new Dictionary<string, string>();
ValidateQuery(query);
const string query = @"
SELECT ParameterName, ParameterValue
FROM dbo.PreAdjustmentCalibrationParams
WHERE Dn_InternalId=@Dn_InternalId";
return await Task.Run(
() => GetCalibrationParams(query),
token).ConfigureAwait(false); ;
using (var connection = new SqlConnection(_connectionString))
/*await readLock.WaitAsync(token);
using (var command = new SqlCommand(query, connection))
try
{
command.Parameters.AddWithValue(
"@Dn_InternalId",
dnInternalId);
return await Task.Run(
() => GetCalibrationParams(query),
token);
}
finally
{
readLock.Release();
}*/
}
await connection.OpenAsync();
/// <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);
using (var reader =
await command.ExecuteReaderAsync())
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)
{
while (await reader.ReadAsync())
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))
{
var parameterName = reader["ParameterName"].ToString();
var parameterValue = reader["ParameterValue"].ToString();
if (!string.IsNullOrWhiteSpace(parameterName))
{
result[parameterName] = parameterValue;
}
values[parameterName] =
parameterValue;
}
}
return values;
}
if (result is ReaderDiagnosticResult diagnosticResult)
{
if (diagnosticResult.Data is Dictionary<string, UInt32> uintDictionary)
return uintDictionary;
if (diagnosticResult.Data is Dictionary<string, string> stringDictionary)
{
foreach (var item in stringDictionary)
{
if (TryConvertToUInt32(item.Value, out UInt32 value))
values[item.Key] = value;
}
return values;
}
if (diagnosticResult.Data is Dictionary<string, object> objectDictionary)
{
foreach (var item in objectDictionary)
{
if (TryConvertToUInt32(item.Value, out UInt32 value))
values[item.Key] = value;
}
return values;
}
}
return result;
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);
}
}
}

View File

@ -0,0 +1,73 @@
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);
}
}
}

View File

@ -60,9 +60,10 @@
<Compile Include="API\InterfaceOutsideToGCI.cs" />
<Compile Include="API\InterfaceGCIToLaatzen.cs" />
<Compile Include="API\PublicModels.cs" />
<Compile Include="Core\DataStorage\Config\GciConfig.cs" />
<Compile Include="Core\DataStorage\Config\GciConfigLoader.cs" />
<Compile Include="Core\Config\GciConfig.cs" />
<Compile Include="Core\Config\GciConfigLoader.cs" />
<Compile Include="Core\DataStorage\Config\GciDataStorageConfig.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" />
@ -80,6 +81,7 @@
<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" />
@ -200,9 +202,11 @@
<EmbeddedResource Include="UI\StaraTuraAPI_GenesisCordonelInterface\FrmGCIAPI.resx">
<DependentUpon>FrmGCIAPI.cs</DependentUpon>
</EmbeddedResource>
<Content Include="Config\gci_config.json">
<Content Include="Core\Config\gci_config.json">
<Link>Config\gci_config.json</Link>
<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" />
@ -245,6 +249,7 @@
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="Config\" />
<Folder Include="Core\DataStorage\Writing\NewFolder1\" />
<Folder Include="RuntimePackage\Package\" />
</ItemGroup>

View File

@ -122,22 +122,23 @@ namespace GenesisCordonelInterface.UI.Debug
{
isRefreshing = true;
foreach (var meter in data)
{
EnsurePortValueExists(meter.RequestPort);
EnsurePortValueExists(meter.StreamingPort);
if(data != null)
foreach (var meter in data)
{
EnsurePortValueExists(meter.RequestPort);
EnsurePortValueExists(meter.StreamingPort);
var row = FindOrCreateRow(meter.Slot);
var row = FindOrCreateRow(meter.Slot);
Set(row, "Slot", meter.Slot);
Set(row, "Selected", meter.Selected);
Set(row, "PcbId", meter.PcbId);
Set(row, "IsLoggedOn", meter.IsLoggedOn);
Set(row, "RequestPort", meter.RequestPort);
Set(row, "StreamingPort", meter.StreamingPort);
Set(row, "FwVersion", meter.FwVersion);
Set(row, "InterfaceVersion", meter.InterfaceVersion);
}
Set(row, "Slot", meter.Slot);
Set(row, "Selected", meter.Selected);
Set(row, "PcbId", meter.PcbId);
Set(row, "IsLoggedOn", meter.IsLoggedOn);
Set(row, "RequestPort", meter.RequestPort);
Set(row, "StreamingPort", meter.StreamingPort);
Set(row, "FwVersion", meter.FwVersion);
Set(row, "InterfaceVersion", meter.InterfaceVersion);
}
isRefreshing = false;
}

View File

@ -0,0 +1,36 @@
{
"_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"
}
}
}

View File

@ -1,8 +1,12 @@
using CordonelPreadjustmentUi;
using CordonelPreadjustmentUi.Processes.Itinerary;
using GenesisCordonelInterface.API;
using GenesisCordonelInterface.Core;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using GenesisCordonelInterface.Core.Threading;
using GraphLib;
using log4net;
using NHibernate.Mapping;
///
/// Copyright (c) 2015-2021 Sensus Slovensko a.s.
///
@ -20,10 +24,14 @@ using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Ui.CordonelPreadjustmentUi;
using static GenesisCordonelInterface.API.PublicModels;
using static TBF.Rig.BridgeComponents.GciBridge.Interfaces.PublicModels;
using GciEngine = GenesisCordonelInterface.Core.Engine;
using GciEnums = GenesisCordonelInterface.API.Enums;
using GciGUIType = GenesisCordonelInterface.UI.MainView;
using GciPublicModels = GenesisCordonelInterface.API.PublicModels;
using GciType = GenesisCordonelInterface.API.InterfaceOutsideToGCI;
using UdsReaderType = TBF.Rig.Input.DataStorage.UniDataStorageReader.Reader;
using GciUDSRPublicModels = GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using UdsReaderType_CalibrationParams = GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams.PreAdjustmentCalibrationParamsReader;
using UdsReaderType_LoginPasswords = GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords.MeterLoginPasswordReader;//TBF.Rig.Input.DataStorageSection.UniDataStorageReader.Reader;
using UDSRPublicModels = TBF.Rig.Input.DataStorage.UniDataStorageReader.Interfaces.PublicModels;
using UdsWriterType = TBF.Rig.Output.DataStorage.UniDataStorageWriter.Writer;
@ -44,9 +52,12 @@ namespace TBF.Rig.BridgeComponents.GciBridge
readonly GciBridgeCfg gciBridgeCfg;
readonly UdsReaderType reader;
readonly Reader reader;
readonly UdsWriterType writer;
//GCI main init
public GciEngine _gciEngine;
//diag GUI for GCI
GciGUIType gciGUI;
Form gciGuiHostForm;
@ -55,18 +66,18 @@ namespace TBF.Rig.BridgeComponents.GciBridge
public UserControl gciBridgeGUIUserControl;
public UI.MainForm gciBridgeGuiForm;
public GciType gciExternalInterface;
public bool HasReader { get { return reader != null; } }
public bool HasWriter { get { return writer != null; } }
public bool IsGuiInitialized { get { return gciGUI != null; } }
public bool IsExternalInitialized { get { return gciExternalInterface != null; } }
public GciType GciExternalInterface { get { return gciExternalInterface; } }
public bool IsExternalInitialized { get { return _gciEngine.gciExternalInterface != null; } }
public GciType gciExternalInterface { get { return _gciEngine.gciExternalInterface; } }
public GciBridgeCfg GciBridgeCfg { get { return gciBridgeCfg; } }
public UdsReaderType GetReader()
public UdsReaderType_LoginPasswords GetLoginPasswordsDataStorageReader()
{
return reader;
return _gciEngine.loginPasswordsDataStorageReader;
}
public UdsReaderType_CalibrationParams GetCalibrationParamsStorageReader()
{
return _gciEngine.calibrationParamsStorageReader;
}
public UdsWriterType GetWriter()
@ -84,7 +95,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
if (!string.IsNullOrEmpty(gciBridgeCfg.ReaderName))
{
reader = TbfComponents.FindComponent(gciBridgeCfg.ReaderName, components) as UdsReaderType;
reader = TbfComponents.FindComponent(gciBridgeCfg.ReaderName, components) as Reader;
if (reader == null) throw new Exception("Cannot find reader component '" + gciBridgeCfg.ReaderName + "'");
}
@ -94,6 +105,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
if (writer == null) throw new Exception("Cannot find writer component '" + gciBridgeCfg.WriterName + "'");
}
_gciEngine = new GciEngine();
Initialize();
}
@ -130,17 +143,6 @@ namespace TBF.Rig.BridgeComponents.GciBridge
gciBridgeGuiForm.Height = 600;
gciBridgeGuiForm.StartPosition = FormStartPosition.CenterScreen;
/*gciBridgeGUI = new MainView(this, gciBridgeGuiForm);
gciBridgeGUI.Dock = DockStyle.Fill;
gciBridgeGuiForm.Controls.Add(gciBridgeGUI);
gciBridgeGuiForm.FormClosed += (s, e) =>
{
gciBridgeGUI = null;
gciBridgeGuiForm = null;
};*/
log.InfoFormat("{0}: GciBridge GUI view initialized.", Name);
}
catch (Exception ex)
@ -179,10 +181,10 @@ namespace TBF.Rig.BridgeComponents.GciBridge
{
try
{
if (gciExternalInterface != null)
if (_gciEngine.gciExternalInterface != null)
return;
gciExternalInterface = new GciType();
_gciEngine.gciExternalInterface = new GciType();
log.InfoFormat("{0}: GCI external interface initialized.", Name);
}
@ -255,14 +257,15 @@ namespace TBF.Rig.BridgeComponents.GciBridge
if (!IsExternalInitialized)
TryInitializeExternalInterface();
if (gciExternalInterface == null)
if (_gciEngine.gciExternalInterface == null)
throw new Exception("GCI external interface is not initialized.");
}
void EnsureReader()
{
if (reader == null)
throw new Exception("UniDataStorageReader is not linked to GciBridge.");
/*if (reader == null)
throw new Exception("UniDataStorageReader is not linked to GciBridge.");*/
}
// API:
@ -293,7 +296,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
if (request.SlotId <= 0)
throw new ArgumentException("Invalid slot id.", nameof(request));
var result = await gciExternalInterface
var result = await _gciEngine.gciExternalInterface
.InitSlotAsync(request, token)
.ConfigureAwait(false);
@ -374,7 +377,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
if (request.SlotId <= 0)
throw new ArgumentException("Invalid slot id.", nameof(request));
var result = await gciExternalInterface
var result = await _gciEngine.gciExternalInterface
.UpdateSlotAsync(request, token)
.ConfigureAwait(false);
@ -450,7 +453,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
throw new ArgumentException("Invalid slot id.");
GciPublicModels.GciSlotInfo result =
await gciExternalInterface.GetSlotAsync(slotId, token);
await _gciEngine.gciExternalInterface.GetSlotAsync(slotId, token);
log.InfoFormat("{0}: GetSlotAsync({1}) invoked. Result={2}", Name, slotId, result);
@ -526,7 +529,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
throw new ArgumentException("Invalid slot id.", nameof(slot));
GciPublicModels.GciCleanSlotResult result =
await gciExternalInterface.CleanSlotAsync(slot, token);
await _gciEngine.gciExternalInterface.CleanSlotAsync(slot, token);
log.InfoFormat("{0}: CleanSlotAsync invoked. Slot={1}, Result={2}", Name, slot, result);
@ -599,7 +602,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
EnsureExternalInterface();
GciPublicModels.GciCleanAllSlotsResult result =
await gciExternalInterface.CleanAllSlotsAsync(token);
await _gciEngine.gciExternalInterface.CleanAllSlotsAsync(token);
log.InfoFormat("{0}: CleanAllSlotsAsync invoked.", Name);
@ -673,7 +676,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
if (slotId <= 0)
throw new ArgumentException("Invalid slot id.", nameof(slotId));
var result = await gciExternalInterface
var result = await _gciEngine.gciExternalInterface
.GetPcbIdAsync(slotId, token)
.ConfigureAwait(false);
@ -790,7 +793,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
if (slot <= 0)
throw new ArgumentException("Invalid slot id.", nameof(slot));
var result = await gciExternalInterface
var result = await _gciEngine.gciExternalInterface
.ConnectOneSlotAsync(slot, token)
.ConfigureAwait(false);
@ -822,7 +825,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
if (slotId <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await gciExternalInterface.LoginOneSlotAsync(slotId, token);
var result = await _gciEngine.gciExternalInterface.LoginOneSlotAsync(slotId, token);
log.InfoFormat("{0}: LoginAsync({1}) invoked. Result={2}", Name, slotId, result);
@ -893,7 +896,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
if (slotId <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await gciExternalInterface.DisconnectAsync(slotId, token);
var result = await _gciEngine.gciExternalInterface.DisconnectAsync(slotId, token);
log.InfoFormat("{0}: DisconnectAsync({1}) invoked. Result={2}", Name, slotId, result);
@ -976,7 +979,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
throw new ArgumentException("Password is empty.");
GciPublicModels.GciSetPasswordResult result =
await gciExternalInterface.SetPasswordAsync(slotId, password, token);
await _gciEngine.gciExternalInterface.SetPasswordAsync(slotId, password, token);
log.InfoFormat("{0}: SetPasswordAsync({1}) invoked. Result={2}", Name, slotId, result);
@ -1051,7 +1054,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
if (string.IsNullOrWhiteSpace(registerName))
throw new ArgumentException("Register name is empty.");
var result = await gciExternalInterface.ReadRegisterAsync(slotId, registerName, token);
var result = await _gciEngine.gciExternalInterface.ReadRegisterAsync(slotId, registerName, token);
log.InfoFormat("{0}: ReadRegisterAsync({1}, {2}) invoked. Result={3}",
Name, slotId, registerName, result);
@ -1141,7 +1144,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
if (string.IsNullOrWhiteSpace(registerName))
throw new ArgumentException("Register name is empty.");
var result = await gciExternalInterface.WriteRegisterAsync(
var result = await _gciEngine.gciExternalInterface.WriteRegisterAsync(
slotId,
registerName,
value,
@ -1222,7 +1225,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
/// Reads password data from UniDataStorageReader by PCB ID.
///
/// Trace:
/// GciBridge.GetPasswordAsync()
/// GciBridge.ReadMeterLoginPasswordAsync()
/// -> UniDataStorageReader.Reader.GetDataFromStorageByParameterAsync()
/// -> Reader queue/lock
/// -> Reader.GetDataFromStorageByParameter()
@ -1243,9 +1246,9 @@ namespace TBF.Rig.BridgeComponents.GciBridge
try
{
UDSRPublicModels.DataQuery query = CreatePasswordQuery(pcbId);
GciUDSRPublicModels.DataQuery query = CreatePasswordQuery(pcbId);
object data = await reader.GetDataFromStorageByParameterAsync(query, token);
object data = await _gciEngine.gciExternalInterface.ReadMeterLoginPasswordAsync(query, token);
string password = ExtractPassword(data);
@ -1261,7 +1264,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
}
catch (Exception ex)
{
log.Error("GetPasswordAsync failed.", ex);
log.Error("ReadMeterLoginPasswordAsync failed.", ex);
return new UdsPasswordResult
{
@ -1273,7 +1276,132 @@ namespace TBF.Rig.BridgeComponents.GciBridge
}
}
/// <summary>
/// Reads pre-adjustment calibration parameters
/// from UniDataStorageReader by meter size.
///
/// Trace:
/// GciBridge.GetPreAdjustmentCalibrationParamsAsync()
/// -> UniDataStorageReader.Reader.GetDataFromStorageByParameterAsync()
/// -> Reader queue/lock
/// -> Reader.GetDataFromStorageByParameter()
/// -> selected storage reader by configuration
/// -> DatabaseReader.GetData()
/// -> RestApiReader.GetData()
/// -> JsonReader.GetData()
/// -> CsvReader.GetData()
///
/// Expected result:
///
/// Key:
/// Calibration parameter name
///
/// Value:
/// Calibration parameter value
///
/// Example:
///
/// GENESISFLOW_MinValidToF -> 13743895
/// GENESISFLOW_Timeout -> 1
/// </summary>
/// <param name="meterSize">
/// Meter size used as query parameter.
/// </param>
/// <param name="token">
/// Cancellation token.
/// </param>
/// <returns>
/// Calibration parameter lookup result.
/// </returns>
public async Task<UdsPreAdjustmentCalibrationParamsResult> GetPreAdjustmentCalibrationParamsAsync(
int meterSize,
CancellationToken token = default)
{
EnsureReader();
if (meterSize < 0)
throw new ArgumentException(
"Meter size must not be negative.",
nameof(meterSize));
try
{
GciUDSRPublicModels.DataQuery query = CreatePreAdjustmentCalibrationParamsQuery(meterSize);
object data = await _gciEngine.gciExternalInterface.ReadPreAdjustmentCalibrationParamsAsync(query, token);
Dictionary<string, UInt32> calibrationParams =
ExtractPreAdjustmentCalibrationParams(data);
return new UdsPreAdjustmentCalibrationParamsResult
{
Success = calibrationParams != null &&
calibrationParams.Count > 0,
MeterSize = meterSize,
CalibrationParams = calibrationParams,
Message = calibrationParams != null &&
calibrationParams.Count > 0
? "Pre-adjustment calibration parameters found."
: "Pre-adjustment calibration parameters were not found."
};
}
catch (Exception ex)
{
log.Error(
"GetPreAdjustmentCalibrationParamsAsync failed.",
ex);
return new UdsPreAdjustmentCalibrationParamsResult
{
Success = false,
MeterSize = meterSize,
CalibrationParams = null,
Message = ex.Message
};
}
}
private GciUDSRPublicModels.DataQuery CreatePreAdjustmentCalibrationParamsQuery(
int meterSize)
{
GciUDSRPublicModels.DataQuery query =
new GciUDSRPublicModels.DataQuery();
query.QueryParams.Add(
meterSize.ToString());
return query;
}
private Dictionary<string, UInt32> ExtractPreAdjustmentCalibrationParams(
object data)
{
Dictionary<string, UInt32> result =
new Dictionary<string, UInt32>();
if (data == null)
return result;
if (data is Dictionary<string, UInt32> directDictionary)
return directDictionary;
if (data is Dictionary<string, object> objectDictionary)
{
foreach (var item in objectDictionary)
{
if (UInt32.TryParse(
item.Value?.ToString(),
out UInt32 value))
{
result[item.Key] = value;
}
}
return result;
}
return result;
}
/// <summary>
/// Reads password from UniDataStorageReader using PCB ID with retry support.
@ -1288,7 +1416,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
///
/// GetPasswordWithRetryAsync()
/// -> RetryWorker.RunWithRetryAsync()
/// -> GetPasswordAsync()
/// -> ReadMeterLoginPasswordAsync()
/// -> UniDataStorageReader
///
/// Returns:
@ -1310,7 +1438,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
r => r.Success,
msg => log.Info(msg),
(msg, result) => log.InfoFormat("{0}: {1}", msg, result),
$"GetPasswordAsync pcb {pcbId}",
$"ReadMeterLoginPasswordAsync pcb {pcbId}",
maxAttempts: 5,
delayMs: 5,
timeoutMs: 30000);
@ -1362,7 +1490,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
/// -> RetryWorker.RunWithRetryAsync()
/// -> ConnectAsync()
/// -> GetPcbIdAsync()
/// -> GetPasswordAsync()
/// -> ReadMeterLoginPasswordAsync()
/// -> SetPasswordAsync()
/// -> LoginAsync()
///
@ -1393,7 +1521,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
RetryWorker.EnsureSuccess(finalResult.PcbResult, $"GetPcbIdAsync slot {slotId}");
finalResult.PasswordResult = await GetPasswordWithRetryAsync(finalResult.PcbResult.Result.PcbId, token);
RetryWorker.EnsureSuccess(finalResult.PasswordResult, $"GetPasswordAsync slot {slotId}");
RetryWorker.EnsureSuccess(finalResult.PasswordResult, $"ReadMeterLoginPasswordAsync slot {slotId}");
finalResult.SetPasswordResult = await SetPasswordWithRetryAsync(slotId, finalResult.PasswordResult.Result.Password, token);
RetryWorker.EnsureSuccess(finalResult.SetPasswordResult, $"SetPasswordAsync slot {slotId}");
@ -1442,7 +1570,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
Name,
operation);
PreAdjustmentInitializationResult result = gciExternalInterface.Preadjustment_Initialization(pp, mc);
PreAdjustmentInitializationResult result = _gciEngine.gciExternalInterface.Preadjustment_Initialization(pp, mc);
log.InfoFormat(
"{0}: {1} Finish. {2}",
@ -1486,7 +1614,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
log.InfoFormat("{0}: {1} Start.", Name, operation);
PreadjustmentDetectResult result =
await gciExternalInterface
await _gciEngine.gciExternalInterface
.PreAdjustment_DetectAsync(selectedSlots, token)
.ConfigureAwait(false);
@ -1512,6 +1640,36 @@ namespace TBF.Rig.BridgeComponents.GciBridge
}
}
public PreadjustmentDetectResult PreAdjustment_DetectDirect(
IEnumerable<GciPublicModels.MeterBatchDebugStatus> selectedSlots,
CancellationToken token = default)
{
const string operation = nameof(PreAdjustment_DetectDirect);
try
{
EnsureExternalInterface();
log.InfoFormat("{0}: {1} Start.", Name, operation);
PreadjustmentDetectResult result = _gciEngine.gciExternalInterface.PreAdjustment_DetectDirect(selectedSlots, token);
log.InfoFormat("{0}: {1} Finish. {2}", Name, operation, result);
return result;
}
catch (Exception ex)
{
log.Error($"{Name}: {operation} failed.", ex);
return new PreadjustmentDetectResult
{
Success = false,
ErrorMessage = ex.Message
};
}
}
#endregion
#region ================================== PreAdjustment PREPARATION bridge ==================================
@ -1533,7 +1691,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
slot);
PreAdjustmentProcessResult result =
await gciExternalInterface
await _gciEngine.gciExternalInterface
.PreAdjustment_PreparationAsync(
slot,
token)
@ -1563,6 +1721,35 @@ namespace TBF.Rig.BridgeComponents.GciBridge
}
}
public PreAdjustmentProcessResult PreAdjustment_PreparationDirect()
{
const string operation = nameof(PreAdjustment_PreparationDirect);
try
{
EnsureExternalInterface();
log.InfoFormat("{0}: {1} Start.", Name, operation);
PreAdjustmentProcessResult result = _gciEngine.gciExternalInterface.PreAdjustment_PreparationDirect();
log.InfoFormat("{0}: {1} Finish. {2}", Name, operation, result);
return result;
}
catch (Exception ex)
{
log.Error($"{Name}: {operation} failed.", ex);
return new PreAdjustmentProcessResult
{
Success = false,
ProcessName = "Preparation",
ErrorMessage = ex.Message
};
}
}
#endregion
#region ================================== PreAdjustment AMPLITUDE TEST bridge ==================================
@ -1584,7 +1771,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
slot);
PreAdjustmentProcessResult result =
await gciExternalInterface
await _gciEngine.gciExternalInterface
.PreAdjustment_AmplitudeTestAsync(
slot,
token)
@ -1614,6 +1801,35 @@ namespace TBF.Rig.BridgeComponents.GciBridge
}
}
public PreAdjustmentProcessResult PreAdjustment_AmplitudeTestDirect()
{
const string operation = nameof(PreAdjustment_AmplitudeTestDirect);
try
{
EnsureExternalInterface();
log.InfoFormat("{0}: {1} Start.", Name, operation);
PreAdjustmentProcessResult result = _gciEngine.gciExternalInterface.PreAdjustment_AmplitudeTestDirect();
log.InfoFormat("{0}: {1} Finish. {2}", Name, operation, result);
return result;
}
catch (Exception ex)
{
log.Error($"{Name}: {operation} failed.", ex);
return new PreAdjustmentProcessResult
{
Success = false,
ProcessName = "AmplitudeTest",
ErrorMessage = ex.Message
};
}
}
#endregion
#region ================================== PreAdjustment TEMPERATURE CALIBRATION bridge ==================================
@ -1635,7 +1851,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
slot);
PreAdjustmentProcessResult result =
await gciExternalInterface
await _gciEngine.gciExternalInterface
.PreAdjustment_TemperatureCalibrationAsync(
slot,
token)
@ -1665,6 +1881,62 @@ namespace TBF.Rig.BridgeComponents.GciBridge
}
}
public bool PreAdjustment_PushTemperature(
double temperature)
{
const string operation = nameof(PreAdjustment_PushTemperature);
try
{
EnsureExternalInterface();
log.InfoFormat(
"{0}: {1}. Temperature={2}",
Name,
operation,
temperature);
return _gciEngine.gciExternalInterface.PreAdjustment_PushTemperature(temperature);
}
catch (Exception ex)
{
log.Error(
string.Format("{0}: {1} failed.", Name, operation),
ex);
return false;
}
}
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibrationDirect()
{
const string operation = nameof(PreAdjustment_TemperatureCalibrationDirect);
try
{
EnsureExternalInterface();
log.InfoFormat("{0}: {1} Start.", Name, operation);
PreAdjustmentProcessResult result = _gciEngine.gciExternalInterface.PreAdjustment_TemperatureCalibrationDirect();
log.InfoFormat("{0}: {1} Finish. {2}", Name, operation, result);
return result;
}
catch (Exception ex)
{
log.Error($"{Name}: {operation} failed.", ex);
return new PreAdjustmentProcessResult
{
Success = false,
ProcessName = "TemperatureCalibration",
ErrorMessage = ex.Message
};
}
}
#endregion
#region ================================== PreAdjustment OFFSET TEST bridge ==================================
@ -1686,7 +1958,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
slot);
PreAdjustmentProcessResult result =
await gciExternalInterface
await _gciEngine.gciExternalInterface
.PreAdjustment_OffsetTestAsync(
slot,
token)
@ -1716,6 +1988,35 @@ namespace TBF.Rig.BridgeComponents.GciBridge
}
}
public PreAdjustmentProcessResult PreAdjustment_OffsetTestDirect()
{
const string operation = nameof(PreAdjustment_OffsetTestDirect);
try
{
EnsureExternalInterface();
log.InfoFormat("{0}: {1} Start.", Name, operation);
PreAdjustmentProcessResult result = _gciEngine.gciExternalInterface.PreAdjustment_OffsetTestDirect();
log.InfoFormat("{0}: {1} Finish. {2}", Name, operation, result);
return result;
}
catch (Exception ex)
{
log.Error($"{Name}: {operation} failed.", ex);
return new PreAdjustmentProcessResult
{
Success = false,
ProcessName = "OffsetTest",
ErrorMessage = ex.Message
};
}
}
#endregion
#region ================================== PreAdjustment COMPLETION bridge ==================================
@ -1737,7 +2038,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
slot);
PreAdjustmentProcessResult result =
await gciExternalInterface
await _gciEngine.gciExternalInterface
.PreAdjustment_CompletionAsync(
slot,
token)
@ -1767,12 +2068,41 @@ namespace TBF.Rig.BridgeComponents.GciBridge
}
}
public PreAdjustmentProcessResult PreAdjustment_CompletionDirect()
{
const string operation = nameof(PreAdjustment_CompletionDirect);
try
{
EnsureExternalInterface();
log.InfoFormat("{0}: {1} Start.", Name, operation);
PreAdjustmentProcessResult result = _gciEngine.gciExternalInterface.PreAdjustment_CompletionDirect();
log.InfoFormat("{0}: {1} Finish. {2}", Name, operation, result);
return result;
}
catch (Exception ex)
{
log.Error($"{Name}: {operation} failed.", ex);
return new PreAdjustmentProcessResult
{
Success = false,
ProcessName = "Completion",
ErrorMessage = ex.Message
};
}
}
#endregion
#region ======================================= Helpers =======================================
private UDSRPublicModels.DataQuery CreatePasswordQuery(string pcbId)
private GciUDSRPublicModels.DataQuery CreatePasswordQuery(string pcbId)
{
var query = new UDSRPublicModels.DataQuery();
var query = new GciUDSRPublicModels.DataQuery();
query.QueryParams.Add(pcbId);
return query;
}
@ -1807,7 +2137,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
public List<string> GetAllRegisterNames()
{
EnsureExternalInterface();
return gciExternalInterface.GetAllRegisterNames();
return _gciEngine.gciExternalInterface.GetAllRegisterNames();
}
}
}

View File

@ -31,18 +31,18 @@
this.classNameLabel = new System.Windows.Forms.Label();
this.nameLabel = new System.Windows.Forms.Label();
this.nameTextBox = new System.Windows.Forms.TextBox();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.showGciBridgeGUIButton = new System.Windows.Forms.Button();
this.showGciGuiButton = new System.Windows.Forms.Button();
this.readerNameLabel = new System.Windows.Forms.Label();
this.readerNameComboBox = new System.Windows.Forms.ComboBox();
this.writerNameLabel = new System.Windows.Forms.Label();
this.writerNameComboBox = new System.Windows.Forms.ComboBox();
this.externalTypeNameTextBox = new System.Windows.Forms.TextBox();
this.externalTypeNameLabel = new System.Windows.Forms.Label();
this.enableGuiCheckBox = new System.Windows.Forms.CheckBox();
this.enableExternalCheckBox = new System.Windows.Forms.CheckBox();
this.showGuiOnInitializeCheckBox = new System.Windows.Forms.CheckBox();
this.externalTypeNameLabel = new System.Windows.Forms.Label();
this.externalTypeNameTextBox = new System.Windows.Forms.TextBox();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.showGciBridgeGUIButton = new System.Windows.Forms.Button();
this.showGciGuiButton = new System.Windows.Forms.Button();
this.groupBox1.SuspendLayout();
this.SuspendLayout();
//
@ -72,6 +72,37 @@
this.nameTextBox.Size = new System.Drawing.Size(290, 20);
this.nameTextBox.TabIndex = 2;
//
// groupBox1
//
this.groupBox1.Controls.Add(this.showGciBridgeGUIButton);
this.groupBox1.Controls.Add(this.showGciGuiButton);
this.groupBox1.Location = new System.Drawing.Point(31, 241);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(573, 60);
this.groupBox1.TabIndex = 12;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Diagnostic GUI";
//
// showGciBridgeGUIButton
//
this.showGciBridgeGUIButton.Location = new System.Drawing.Point(311, 19);
this.showGciBridgeGUIButton.Name = "showGciBridgeGUIButton";
this.showGciBridgeGUIButton.Size = new System.Drawing.Size(134, 28);
this.showGciBridgeGUIButton.TabIndex = 2;
this.showGciBridgeGUIButton.Text = "Show GciBridge GUI";
this.showGciBridgeGUIButton.UseVisualStyleBackColor = true;
this.showGciBridgeGUIButton.Click += new System.EventHandler(this.showGciBridgeGUIButton_Click);
//
// showGciGuiButton
//
this.showGciGuiButton.Location = new System.Drawing.Point(451, 19);
this.showGciGuiButton.Name = "showGciGuiButton";
this.showGciGuiButton.Size = new System.Drawing.Size(107, 28);
this.showGciGuiButton.TabIndex = 1;
this.showGciGuiButton.Text = "Show GCI GUI";
this.showGciGuiButton.UseVisualStyleBackColor = true;
this.showGciGuiButton.Click += new System.EventHandler(this.showGuiButton_Click);
//
// readerNameLabel
//
this.readerNameLabel.AutoSize = true;
@ -108,6 +139,23 @@
this.writerNameComboBox.Size = new System.Drawing.Size(465, 21);
this.writerNameComboBox.TabIndex = 6;
//
// externalTypeNameTextBox
//
this.externalTypeNameTextBox.Enabled = false;
this.externalTypeNameTextBox.Location = new System.Drawing.Point(139, 202);
this.externalTypeNameTextBox.Name = "externalTypeNameTextBox";
this.externalTypeNameTextBox.Size = new System.Drawing.Size(290, 20);
this.externalTypeNameTextBox.TabIndex = 11;
//
// externalTypeNameLabel
//
this.externalTypeNameLabel.AutoSize = true;
this.externalTypeNameLabel.Location = new System.Drawing.Point(28, 205);
this.externalTypeNameLabel.Name = "externalTypeNameLabel";
this.externalTypeNameLabel.Size = new System.Drawing.Size(105, 13);
this.externalTypeNameLabel.TabIndex = 10;
this.externalTypeNameLabel.Text = "External type / name";
//
// enableGuiCheckBox
//
this.enableGuiCheckBox.AutoSize = true;
@ -141,54 +189,6 @@
this.showGuiOnInitializeCheckBox.Text = "Show GUI on initialize";
this.showGuiOnInitializeCheckBox.UseVisualStyleBackColor = true;
//
// externalTypeNameLabel
//
this.externalTypeNameLabel.AutoSize = true;
this.externalTypeNameLabel.Location = new System.Drawing.Point(28, 205);
this.externalTypeNameLabel.Name = "externalTypeNameLabel";
this.externalTypeNameLabel.Size = new System.Drawing.Size(105, 13);
this.externalTypeNameLabel.TabIndex = 10;
this.externalTypeNameLabel.Text = "External type / name";
//
// externalTypeNameTextBox
//
this.externalTypeNameTextBox.Enabled = false;
this.externalTypeNameTextBox.Location = new System.Drawing.Point(139, 202);
this.externalTypeNameTextBox.Name = "externalTypeNameTextBox";
this.externalTypeNameTextBox.Size = new System.Drawing.Size(290, 20);
this.externalTypeNameTextBox.TabIndex = 11;
//
// groupBox1
//
this.groupBox1.Controls.Add(this.showGciBridgeGUIButton);
this.groupBox1.Controls.Add(this.showGciGuiButton);
this.groupBox1.Location = new System.Drawing.Point(31, 241);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(573, 60);
this.groupBox1.TabIndex = 12;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Diagnostic GUI";
//
// showGciBridgeGUIButton
//
this.showGciBridgeGUIButton.Location = new System.Drawing.Point(311, 19);
this.showGciBridgeGUIButton.Name = "showGciBridgeGUIButton";
this.showGciBridgeGUIButton.Size = new System.Drawing.Size(134, 28);
this.showGciBridgeGUIButton.TabIndex = 2;
this.showGciBridgeGUIButton.Text = "Show GciBridge GUI";
this.showGciBridgeGUIButton.UseVisualStyleBackColor = true;
this.showGciBridgeGUIButton.Click += new System.EventHandler(this.showGciBridgeGUIButton_Click);
//
// showGciGuiButton
//
this.showGciGuiButton.Location = new System.Drawing.Point(451, 19);
this.showGciGuiButton.Name = "showGciGuiButton";
this.showGciGuiButton.Size = new System.Drawing.Size(107, 28);
this.showGciGuiButton.TabIndex = 1;
this.showGciGuiButton.Text = "Show GCI GUI";
this.showGciGuiButton.UseVisualStyleBackColor = true;
this.showGciGuiButton.Click += new System.EventHandler(this.showGuiButton_Click);
//
// GciBridgeCfgCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -220,17 +220,17 @@
private System.Windows.Forms.Label classNameLabel;
private System.Windows.Forms.Label nameLabel;
private System.Windows.Forms.TextBox nameTextBox;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Button showGciGuiButton;
private System.Windows.Forms.Button showGciBridgeGUIButton;
private System.Windows.Forms.Label readerNameLabel;
private System.Windows.Forms.ComboBox readerNameComboBox;
private System.Windows.Forms.Label writerNameLabel;
private System.Windows.Forms.ComboBox writerNameComboBox;
private System.Windows.Forms.TextBox externalTypeNameTextBox;
private System.Windows.Forms.Label externalTypeNameLabel;
private System.Windows.Forms.CheckBox enableGuiCheckBox;
private System.Windows.Forms.CheckBox enableExternalCheckBox;
private System.Windows.Forms.CheckBox showGuiOnInitializeCheckBox;
private System.Windows.Forms.Label externalTypeNameLabel;
private System.Windows.Forms.TextBox externalTypeNameTextBox;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Button showGciGuiButton;
private System.Windows.Forms.Button showGciBridgeGUIButton;
}
}

View File

@ -95,6 +95,36 @@ namespace TBF.Rig.BridgeComponents.GciBridge.Interfaces
}
}
/// <summary>
/// Represents result of pre-adjustment calibration
/// parameter lookup.
///
/// Parameters are returned as key/value pairs:
///
/// Key:
/// Calibration parameter name
///
/// Value:
/// Calibration parameter value
/// </summary>
public class UdsPreAdjustmentCalibrationParamsResult
{
public bool Success { get; set; }
public int MeterSize { get; set; }
public Dictionary<string, UInt32> CalibrationParams { get; set; }
public string Message { get; set; }
public override string ToString()
{
if (CalibrationParams == null || CalibrationParams.Count == 0)
{
return $"Success={Success}, MeterSize={MeterSize}, Message={Message}";
}
return $"Success={Success}, MeterSize={MeterSize}, Params={CalibrationParams.Count}, Message={Message}";
}
}
public class GciFullLoginResult
{
public int SlotId { get; set; }

View File

@ -0,0 +1,481 @@
using Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using PublicModels = GenesisCordonelInterface.API.PublicModels;
namespace TBF.Rig.BridgeComponents.GciBridge.UI.Grid
{
public class MeterGridManager
{
private readonly DataGridView grid;
public MeterGridManager(DataGridView grid)
{
this.grid = grid ?? throw new ArgumentNullException(nameof(grid));
EnableDoubleBuffering(grid);
}
public void Init(List<string> comPorts)
{
grid.SuspendLayout();
try
{
grid.AutoGenerateColumns = false;
grid.Columns.Clear();
grid.AllowUserToAddRows = false;
grid.AllowUserToDeleteRows = false;
grid.RowHeadersVisible = true;
CreateColumns(comPorts);
ConfigureReadOnlyColumns();
ConfigureSelection();
}
finally
{
grid.ResumeLayout();
}
grid.Focus();
grid.ClipboardCopyMode = DataGridViewClipboardCopyMode.EnableWithoutHeaderText;
}
private void CreateColumns(List<string> comPorts)
{
var configs = MeterGridConfigProvider
.GetDefault()
.OrderBy(x => x.DisplayIndex);
foreach (var config in configs)
{
DataGridViewColumn column;
switch (config.ColumnType)
{
case MeterGridConfigProvider.MeterGridColumnType.CheckBox:
column = new DataGridViewCheckBoxColumn();
break;
default:
column = new DataGridViewTextBoxColumn();
break;
}
column.Name = config.Name;
column.HeaderText = config.HeaderText;
column.Width = config.Width;
column.ReadOnly = config.ReadOnly;
column.DisplayIndex = config.DisplayIndex;
grid.Columns.Add(column);
}
grid.Columns.Add(CreateComPortColumn("RequestPort", "RequestPort", comPorts));
grid.Columns.Add(CreateRequestPortTypeColumn());
grid.Columns.Add(CreateComPortColumn("StreamingPort", "StreamingPort", comPorts));
grid.Columns.Add(CreateButtonColumn("DetectRequest", "DetectRequest", "..."));
grid.Columns.Add(CreateButtonColumn("DetectStreaming", "DetectStreaming", "..."));
}
private void ConfigureReadOnlyColumns()
{
foreach (DataGridViewColumn col in grid.Columns)
{
col.ReadOnly =
col.Name != "Selected" &&
col.Name != "RequestPort" &&
col.Name != "RequestPortType" &&
col.Name != "StreamingPort" &&
col.Name != "DetectRequest" &&
col.Name != "DetectStreaming";
}
}
private void ConfigureSelection()
{
grid.MultiSelect = true;
grid.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
grid.CellContentClick -= Grid_CellContentClick;
grid.CellContentClick += Grid_CellContentClick;
grid.CurrentCellDirtyStateChanged -= Grid_CurrentCellDirtyStateChanged;
grid.CurrentCellDirtyStateChanged += Grid_CurrentCellDirtyStateChanged;
}
public void UpdateComPortItems(List<string> comPorts)
{
UpdateComPortColumnItems("RequestPort", comPorts);
UpdateComPortColumnItems("StreamingPort", comPorts);
}
public void Update(List<PublicModels.MeterBatchDebugStatus> meters)
{
grid.SuspendLayout();
try
{
foreach (var meter in meters)
{
var row = FindOrCreateRow(meter.Slot);
SetCell(row, "Slot", meter.Slot);
SetCell(row, "Selected", meter.Selected);
SetCell(row, "PcbId", meter.PcbId);
SetCell(row, "IsConnected", meter.IsConnected);
SetCell(row, "IsLoggedOn", meter.IsLoggedOn);
SetCell(row, "RequestPort", meter.RequestPort);
SetCell(row, "RequestPortType", NormalizeRequestPortType(meter.RequestPortType));
SetCell(row, "StreamingPort", meter.StreamingPort);
SetCell(row, "FwVersion", meter.FwVersion);
SetCell(row, "InterfaceVersion", meter.InterfaceVersion);
}
}
finally
{
grid.ResumeLayout();
}
}
public void UpdateSlots(List<PublicModels.GciSlotInfo> slots)
{
grid.SuspendLayout();
try
{
foreach (var slot in slots)
{
var row = FindOrCreateRow(slot.SlotId);
SetCell(row, "Slot", slot.SlotId);
SetCell(row, "PcbId", slot.PcbId);
SetCell(row, "RequestPort", slot.RequestPort == null ? "" : slot.RequestPort.PortName);
SetCell(row, "RequestPortType", MapRequestPortTypeBack(slot.RequestPort == null ? null : slot.RequestPort.Type));
SetCell(row, "StreamingPort", slot.StreamingPort == null ? "" : slot.StreamingPort.PortName);
SetCell(row, "IsConnected", false);
SetCell(row, "IsLoggedOn", false);
SetCell(row, "FwVersion", "");
SetCell(row, "InterfaceVersion", "");
}
}
finally
{
grid.ResumeLayout();
}
}
public void AddEmptySlotRow()
{
AddSlotRow(GetNextSlotId());
}
public void AddSlotRow()
{
AddSlotRow(GetNextSlotId());
}
public void AddSlotRow(int slotId)
{
if (ContainsSlot(slotId))
throw new Exception($"Slot {slotId} already exists.");
int idx = grid.Rows.Add();
var row = grid.Rows[idx];
SetCell(row, "Slot", slotId);
SetCell(row, "Selected", false);
SetCell(row, "RequestPort", "");
SetCell(row, "RequestPortType", "IRDA");
SetCell(row, "StreamingPort", "");
SetCell(row, "PcbId", "");
SetCell(row, "IsConnected", false);
SetCell(row, "IsLoggedOn", false);
SetCell(row, "FwVersion", "");
SetCell(row, "InterfaceVersion", "");
}
public List<PublicModels.MeterBatchDebugStatus> GetGridData()
{
var list = new List<PublicModels.MeterBatchDebugStatus>();
foreach (DataGridViewRow row in grid.Rows)
{
if (row.IsNewRow)
continue;
if (row.Cells["Slot"].Value == null)
continue;
list.Add(new PublicModels.MeterBatchDebugStatus
{
Slot = Convert.ToInt32(row.Cells["Slot"].Value),
Selected = GetBool(row, "Selected"),
PcbId = GetString(row, "PcbId"),
IsConnected = GetBool(row, "IsConnected"),
IsLoggedOn = GetBool(row, "IsLoggedOn"),
RequestPort = GetString(row, "RequestPort"),
RequestPortType = NormalizeRequestPortType(GetString(row, "RequestPortType")),
StreamingPort = GetString(row, "StreamingPort"),
FwVersion = GetString(row, "FwVersion"),
InterfaceVersion = GetString(row, "InterfaceVersion")
});
}
return list;
}
public List<PublicModels.MeterBatchDebugStatus> GetSelectedGridData()
{
return GetGridData()
.Where(x => x.Selected)
.ToList();
}
public int GetSlotFromRow(int rowIndex)
{
if (rowIndex < 0)
throw new ArgumentOutOfRangeException(nameof(rowIndex));
return Convert.ToInt32(grid.Rows[rowIndex].Cells["Slot"].Value);
}
public string GetColumnName(int columnIndex)
{
return grid.Columns[columnIndex].Name;
}
public void ClearSlots()
{
grid.Rows.Clear();
}
public int GetNextSlotId()
{
var existingSlots = grid.Rows
.Cast<DataGridViewRow>()
.Where(r => !r.IsNewRow)
.Where(r => r.Cells["Slot"].Value != null)
.Select(r => Convert.ToInt32(r.Cells["Slot"].Value))
.ToList();
if (existingSlots.Count == 0)
return 1;
return existingSlots.Max() + 1;
}
private bool ContainsSlot(int slotId)
{
return grid.Rows
.Cast<DataGridViewRow>()
.Any(r =>
!r.IsNewRow &&
r.Cells["Slot"].Value != null &&
Convert.ToInt32(r.Cells["Slot"].Value) == slotId);
}
private DataGridViewRow FindOrCreateRow(int slot)
{
foreach (DataGridViewRow row in grid.Rows)
{
if (!row.IsNewRow &&
row.Cells["Slot"].Value != null &&
Convert.ToInt32(row.Cells["Slot"].Value) == slot)
{
return row;
}
}
int idx = grid.Rows.Add();
var newRow = grid.Rows[idx];
newRow.Cells["Slot"].Value = slot;
return newRow;
}
private void SetCell(DataGridViewRow row, string colName, object value)
{
if (!grid.Columns.Contains(colName))
return;
if (value == null)
value = "";
if (colName == "RequestPortType")
value = NormalizeRequestPortType(Convert.ToString(value));
var cell = row.Cells[colName];
if (!Equals(cell.Value, value))
cell.Value = value;
}
private string GetString(DataGridViewRow row, string colName)
{
if (!grid.Columns.Contains(colName))
return "";
return Convert.ToString(row.Cells[colName].Value);
}
private bool GetBool(DataGridViewRow row, string colName)
{
if (!grid.Columns.Contains(colName))
return false;
if (row.Cells[colName].Value == null)
return false;
return Convert.ToBoolean(row.Cells[colName].Value);
}
private DataGridViewComboBoxColumn CreateComPortColumn(
string name,
string headerText,
List<string> comPorts)
{
return new DataGridViewComboBoxColumn
{
Name = name,
HeaderText = headerText,
DataSource = new List<string>(comPorts ?? new List<string>()),
FlatStyle = FlatStyle.Flat,
DisplayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton
};
}
private DataGridViewComboBoxColumn CreateRequestPortTypeColumn()
{
return new DataGridViewComboBoxColumn
{
Name = "RequestPortType",
HeaderText = "RequestPortType",
DataSource = new List<string> { "", "IRDA", "UART", "RFID" },
FlatStyle = FlatStyle.Flat,
DisplayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton
};
}
private DataGridViewButtonColumn CreateButtonColumn(
string name,
string headerText,
string text)
{
return new DataGridViewButtonColumn
{
Name = name,
HeaderText = headerText,
Text = text,
UseColumnTextForButtonValue = true
};
}
private void UpdateComPortColumnItems(string columnName, List<string> comPorts)
{
var col = grid.Columns[columnName] as DataGridViewComboBoxColumn;
if (col == null)
return;
col.DataSource = null;
col.DataSource = new List<string>(comPorts ?? new List<string>());
}
private string NormalizeRequestPortType(string value)
{
if (string.IsNullOrWhiteSpace(value))
return "";
string normalized = value.Trim();
if (normalized.Equals("RFID", StringComparison.OrdinalIgnoreCase) ||
normalized.IndexOf("RfidSerialPort", StringComparison.OrdinalIgnoreCase) >= 0)
return "RFID";
if (normalized.Equals("UART", StringComparison.OrdinalIgnoreCase) ||
normalized.IndexOf("UartSerialPort", StringComparison.OrdinalIgnoreCase) >= 0)
return "UART";
if (normalized.Equals("IRDA", StringComparison.OrdinalIgnoreCase) ||
normalized.Equals("IrDA", StringComparison.OrdinalIgnoreCase) ||
normalized.IndexOf("IrdaSerialPort", StringComparison.OrdinalIgnoreCase) >= 0)
return "IRDA";
return "";
}
public bool RemoveSlot(int slotId)
{
grid.SuspendLayout();
try
{
foreach (DataGridViewRow row in grid.Rows)
{
if (row.IsNewRow)
continue;
if (row.Cells["Slot"].Value == null)
continue;
if (Convert.ToInt32(row.Cells["Slot"].Value) == slotId)
{
grid.Rows.Remove(row);
return true;
}
}
return false;
}
finally
{
grid.ResumeLayout();
}
}
private string MapRequestPortTypeBack(string fullType)
{
return NormalizeRequestPortType(fullType);
}
private void EnableDoubleBuffering(DataGridView dgv)
{
typeof(DataGridView)
.GetProperty(
"DoubleBuffered",
BindingFlags.Instance | BindingFlags.NonPublic)
?.SetValue(dgv, true, null);
}
private void Grid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (grid.IsCurrentCellDirty)
{
grid.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
private void Grid_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0)
return;
if (grid.Columns[e.ColumnIndex].Name != "Selected")
return;
bool clickedValue = Convert.ToBoolean(
grid.Rows[e.RowIndex].Cells["Selected"].Value);
foreach (DataGridViewRow row in grid.SelectedRows)
{
if (row.Index == e.RowIndex)
continue;
row.Cells["Selected"].Value = clickedValue;
}
}
}
}

View File

@ -316,7 +316,7 @@
//
this.splitWorkArea.Panel2.Controls.Add(this.rtbMainLog);
this.splitWorkArea.Size = new System.Drawing.Size(700, 320);
this.splitWorkArea.SplitterDistance = 233;
this.splitWorkArea.SplitterDistance = 320;
this.splitWorkArea.SplitterWidth = 6;
this.splitWorkArea.TabIndex = 0;
//
@ -326,7 +326,7 @@
this.pnlGciViewHost.Dock = System.Windows.Forms.DockStyle.Fill;
this.pnlGciViewHost.Location = new System.Drawing.Point(0, 0);
this.pnlGciViewHost.Name = "pnlGciViewHost";
this.pnlGciViewHost.Size = new System.Drawing.Size(233, 320);
this.pnlGciViewHost.Size = new System.Drawing.Size(320, 320);
this.pnlGciViewHost.TabIndex = 0;
//
// rtbMainLog
@ -336,7 +336,7 @@
this.rtbMainLog.Location = new System.Drawing.Point(0, 0);
this.rtbMainLog.Name = "rtbMainLog";
this.rtbMainLog.ReadOnly = true;
this.rtbMainLog.Size = new System.Drawing.Size(461, 320);
this.rtbMainLog.Size = new System.Drawing.Size(374, 320);
this.rtbMainLog.TabIndex = 0;
this.rtbMainLog.Text = "";
//

View File

@ -55,7 +55,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI
_mainForm = mainform;
_bridge = bridge;
_gciApi = bridge.gciExternalInterface;
_laatzenApi = _bridge.gciExternalInterface._innerMeterAPI;
_laatzenApi = bridge._gciEngine.gciExternalInterface._innerMeterAPI;
InitializeComponent();
InitializeDebugPanels();
//InitializeWorkerDebugPanel();

View File

@ -90,7 +90,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
int slot = item.Key;
string pcbId = item.Value;
//var result = await _bridge.GetPasswordAsync(pcbId, token);
//var result = await _bridge.ReadMeterLoginPasswordAsync(pcbId, token);
var result = await _bridge.GetPasswordWithRetryAsync(pcbId, token);
LogResult($"UDSR/GetPassword slot {slot}, PCB={pcbId}", result);

View File

@ -24,6 +24,11 @@
private void InitializeComponent()
{
this.grpSlots = new System.Windows.Forms.GroupBox();
this.pushTestBenchTempButton = new System.Windows.Forms.Button();
this.pushedTestBenchTempTextBox = new System.Windows.Forms.TextBox();
this.label8 = new System.Windows.Forms.Label();
this.cb_Metersize = new System.Windows.Forms.ComboBox();
this.l_SettingsPreparationMetersize = new System.Windows.Forms.Label();
this.label7 = new System.Windows.Forms.Label();
this.label6 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
@ -40,13 +45,14 @@
this.amplitudeTestActionButton = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.txtLog = new System.Windows.Forms.TextBox();
this.cb_Metersize = new System.Windows.Forms.ComboBox();
this.l_SettingsPreparationMetersize = new System.Windows.Forms.Label();
this.grpSlots.SuspendLayout();
this.SuspendLayout();
//
// grpSlots
//
this.grpSlots.Controls.Add(this.pushTestBenchTempButton);
this.grpSlots.Controls.Add(this.pushedTestBenchTempTextBox);
this.grpSlots.Controls.Add(this.label8);
this.grpSlots.Controls.Add(this.cb_Metersize);
this.grpSlots.Controls.Add(this.l_SettingsPreparationMetersize);
this.grpSlots.Controls.Add(this.label7);
@ -71,6 +77,50 @@
this.grpSlots.Text = "Slots by selection in the table";
this.grpSlots.Enter += new System.EventHandler(this.grpSlots_Enter);
//
// pushTestBenchTempButton
//
this.pushTestBenchTempButton.Location = new System.Drawing.Point(593, 186);
this.pushTestBenchTempButton.Name = "pushTestBenchTempButton";
this.pushTestBenchTempButton.Size = new System.Drawing.Size(81, 21);
this.pushTestBenchTempButton.TabIndex = 121;
this.pushTestBenchTempButton.Text = "Push temp";
this.pushTestBenchTempButton.Click += new System.EventHandler(this.pushTestBenchTempButton_Click);
//
// pushedTestBenchTempTextBox
//
this.pushedTestBenchTempTextBox.Location = new System.Drawing.Point(550, 186);
this.pushedTestBenchTempTextBox.Name = "pushedTestBenchTempTextBox";
this.pushedTestBenchTempTextBox.Size = new System.Drawing.Size(37, 20);
this.pushedTestBenchTempTextBox.TabIndex = 120;
//
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(440, 189);
this.label8.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(105, 13);
this.label8.TabIndex = 118;
this.label8.Text = "Pushed temperature:";
//
// cb_Metersize
//
this.cb_Metersize.FormattingEnabled = true;
this.cb_Metersize.Location = new System.Drawing.Point(74, 30);
this.cb_Metersize.Name = "cb_Metersize";
this.cb_Metersize.Size = new System.Drawing.Size(73, 21);
this.cb_Metersize.TabIndex = 117;
//
// l_SettingsPreparationMetersize
//
this.l_SettingsPreparationMetersize.AutoSize = true;
this.l_SettingsPreparationMetersize.Location = new System.Drawing.Point(14, 36);
this.l_SettingsPreparationMetersize.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.l_SettingsPreparationMetersize.Name = "l_SettingsPreparationMetersize";
this.l_SettingsPreparationMetersize.Size = new System.Drawing.Size(55, 13);
this.l_SettingsPreparationMetersize.TabIndex = 116;
this.l_SettingsPreparationMetersize.Text = "Metersize:";
//
// label7
//
this.label7.AutoSize = true;
@ -157,6 +207,7 @@
this.completionActionButton.Size = new System.Drawing.Size(150, 28);
this.completionActionButton.TabIndex = 3;
this.completionActionButton.Text = "Completition";
this.completionActionButton.Click += new System.EventHandler(this.completionActionButton_Click);
//
// offsetTestActionButton
//
@ -165,6 +216,7 @@
this.offsetTestActionButton.Size = new System.Drawing.Size(150, 28);
this.offsetTestActionButton.TabIndex = 5;
this.offsetTestActionButton.Text = "Offset test";
this.offsetTestActionButton.Click += new System.EventHandler(this.offsetTestActionButton_Click);
//
// preparationActionButton
//
@ -191,7 +243,7 @@
this.temperatureCalibrationActionButton.Size = new System.Drawing.Size(150, 28);
this.temperatureCalibrationActionButton.TabIndex = 2;
this.temperatureCalibrationActionButton.Text = "Temperature calibration";
this.temperatureCalibrationActionButton.Click += new System.EventHandler(this.amplitudeTestActionButton_Click);
this.temperatureCalibrationActionButton.Click += new System.EventHandler(this.temperatureCalibrationActionButton_Click);
//
// amplitudeTestActionButton
//
@ -200,6 +252,7 @@
this.amplitudeTestActionButton.Size = new System.Drawing.Size(150, 28);
this.amplitudeTestActionButton.TabIndex = 18;
this.amplitudeTestActionButton.Text = "Amplitude test";
this.amplitudeTestActionButton.Click += new System.EventHandler(this.amplitudeTestActionButton_Click);
//
// btnCancel
//
@ -225,24 +278,6 @@
this.txtLog.TabIndex = 5;
this.txtLog.WordWrap = false;
//
// cb_Metersize
//
this.cb_Metersize.FormattingEnabled = true;
this.cb_Metersize.Location = new System.Drawing.Point(74, 30);
this.cb_Metersize.Name = "cb_Metersize";
this.cb_Metersize.Size = new System.Drawing.Size(73, 21);
this.cb_Metersize.TabIndex = 117;
//
// l_SettingsPreparationMetersize
//
this.l_SettingsPreparationMetersize.AutoSize = true;
this.l_SettingsPreparationMetersize.Location = new System.Drawing.Point(14, 36);
this.l_SettingsPreparationMetersize.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.l_SettingsPreparationMetersize.Name = "l_SettingsPreparationMetersize";
this.l_SettingsPreparationMetersize.Size = new System.Drawing.Size(55, 13);
this.l_SettingsPreparationMetersize.TabIndex = 116;
this.l_SettingsPreparationMetersize.Text = "Metersize:";
//
// PreadjustmentActionsView
//
this.BackColor = System.Drawing.SystemColors.Control;
@ -271,5 +306,8 @@
private System.Windows.Forms.Label label6;
private System.Windows.Forms.ComboBox cb_Metersize;
private System.Windows.Forms.Label l_SettingsPreparationMetersize;
private System.Windows.Forms.Button pushTestBenchTempButton;
private System.Windows.Forms.TextBox pushedTestBenchTempTextBox;
private System.Windows.Forms.Label label8;
}
}

View File

@ -1,13 +1,17 @@
using CordonelPreadjustmentUi;
using Common;
using CordonelPreadjustmentUi;
using CordonelPreadjustmentUi.Processes.Itinerary;
using GenesisCordonelInterface.API;
using GenesisCordonelInterface.UI;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using TBF.UiBridge;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
using Xylem.Common.Ui.CordonelPreadjustmentUi;
@ -17,6 +21,8 @@ using GciPublicModels = GenesisCordonelInterface.API.PublicModels;
namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
public partial class PreadjustmentActionsView : UserControl
{
private readonly GciBridge _bridge;
@ -25,6 +31,22 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
private readonly Action _addSlotAction;
private readonly Action _saveAction;
public event EventHandler<EventArgsMeterSuccsessfull> OnAbort;
private readonly System.Windows.Forms.Timer _tempRequestTimer = new System.Windows.Forms.Timer();
private bool _tempButtonHighlight;
private enum PreadjustmentExecutionMode
{
AsyncSlotWorker,
DirectToProcessTasks
}
private PreadjustmentExecutionMode _preadjustmentExecutionMode = PreadjustmentExecutionMode.DirectToProcessTasks;
private bool UseDirectProcess =>
_preadjustmentExecutionMode == PreadjustmentExecutionMode.DirectToProcessTasks;
public PreadjustmentActionsView(
MainView mainview,
GciBridge bridge,
@ -41,17 +63,36 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
LoadRegisterComboBoxes();
InitGciHandlers();
//
cb_Metersize.Items.Clear();
foreach (MeterSize size in (MeterSize[])Enum.GetValues(typeof(MeterSize)))
{
cb_Metersize.Items.Add(size);
}
var setM = MeterSize.DN50;
if (_mainView._gciApi._innerMeterAPI._settings.MeterSize != null)
{
setM = _mainView._gciApi._innerMeterAPI._settings.MeterSize;
}
cb_Metersize.SelectedItem = setM;
//
_tempRequestTimer.Interval = 500;
_tempRequestTimer.Tick += (s, e) =>
{
_tempButtonHighlight = !_tempButtonHighlight;
pushTestBenchTempButton.BackColor =
_tempButtonHighlight
? Color.Red
: SystemColors.Control;
};
}
private void InitGciHandlers()
{
}
private void AddSlot()
@ -92,7 +133,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
private void btnCancel_Click(object sender, EventArgs e)
{
_cts?.Cancel();
OnAbort?.Invoke(null, new EventArgsMeterSuccsessfull(new List<int>()));//stop Laatzen
_cts?.Cancel();//stop StaraTura
Log("Cancel requested.");
}
@ -147,21 +189,26 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
btnCancel.Enabled = busy;
}
private void LogResult(string methodName, object result)
/*private void LogResult(string methodName, object result)
{
Log(methodName + " result:");
Log(result == null ? "<null>" : result.ToString());
}
}*/
private void Log(string message)
private void Log(string msg)
{
txtLog.AppendText(
DateTime.Now.ToString("HH:mm:ss.fff") +
" " +
message +
msg +
Environment.NewLine);
}
private void Log(string msg, int? slot = null, string source = "APP", string PcbID = "")
{
txtLog.AppendText($"{DateTime.Now.ToString("HH:mm:ss.fff")} {source} {msg} ({PcbID}){Environment.NewLine}");
}
private void LoadRegisterComboBoxes()
{
@ -186,7 +233,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
return;
}
ProcessProgress pp = CreatePreparationProgress();
ProcessProgress pp = CreateProcessProgress();
List<MeterStateControl> mc = CreateMeterControls(selectedSlots);
var result = _bridge.Preadjustment_Initialization(pp, mc);
@ -207,8 +254,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
}
private void detectActionButton_Click(
object sender,
EventArgs e)
object sender,
EventArgs e)
{
ExecuteAsync(async token =>
{
@ -221,13 +268,24 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
return;
}
PreadjustmentDetectResult result =
await _bridge.PreAdjustment_DetectAsync(selectedSlots, token);
PreadjustmentDetectResult result;
Log(
result.Success
? "Detect completed. " + result
: "Detect failed. " + result);
if (UseDirectProcess)
{
result = await Task.Run(() =>
_bridge.PreAdjustment_DetectDirect(selectedSlots, token),
token);
}
else
{
result = await _bridge.PreAdjustment_DetectAsync(
selectedSlots,
token);
}
Log(result.Success
? "Detect completed. " + result
: "Detect failed. " + result);
RefreshGrid();
});
@ -239,8 +297,9 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
ExecuteAsync(async token =>
{
await ExecutePreadjustmentForSelectedSlotsAsync(
await ExecutePreadjustmentActionAsync(
_bridge.PreAdjustment_PreparationAsync,
_bridge.PreAdjustment_PreparationDirect,
"Preparation",
token);
});
@ -252,8 +311,9 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
ExecuteAsync(async token =>
{
await ExecutePreadjustmentForSelectedSlotsAsync(
await ExecutePreadjustmentActionAsync(
_bridge.PreAdjustment_AmplitudeTestAsync,
_bridge.PreAdjustment_AmplitudeTestDirect,
"Amplitude Test",
token);
});
@ -265,8 +325,9 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
ExecuteAsync(async token =>
{
await ExecutePreadjustmentForSelectedSlotsAsync(
await ExecutePreadjustmentActionAsync(
_bridge.PreAdjustment_TemperatureCalibrationAsync,
_bridge.PreAdjustment_TemperatureCalibrationDirect,
"Temperature Calibration",
token);
});
@ -278,8 +339,9 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
ExecuteAsync(async token =>
{
await ExecutePreadjustmentForSelectedSlotsAsync(
await ExecutePreadjustmentActionAsync(
_bridge.PreAdjustment_OffsetTestAsync,
_bridge.PreAdjustment_OffsetTestDirect,
"Offset Test",
token);
});
@ -291,27 +353,58 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
{
ExecuteAsync(async token =>
{
await ExecutePreadjustmentForSelectedSlotsAsync(
await ExecutePreadjustmentActionAsync(
_bridge.PreAdjustment_CompletionAsync,
_bridge.PreAdjustment_CompletionDirect,
"Completion",
token);
});
}
private ProcessProgress CreatePreparationProgress()
private ProcessProgress CreateProcessProgress()
{
return new ProcessProgress
ProcessProgress pp = new ProcessProgress
{
Setting = new PreAdjustmentSettingsContainer
{
TempOnly = false,
Culture = Thread.CurrentThread.CurrentCulture,
MeterSize = (MeterSize)cb_Metersize.SelectedItem
Culture = new CultureInfo("en-US"),
MeterSize = (MeterSize)cb_Metersize.SelectedItem,
OffsetTestSettlingTime = 0
},
IsAutomaticMode = false
IsAutomaticMode = false,
};
pp.OnRequestTempretureSelection += RequestTempretureSelection;
pp.OnDebugMessageChanged += DebugMessageChanged;
OnAbort += (o, args) =>
{
pp.StopSequence = true;
pp.CancellationSource.Cancel();
};
return pp;
}
private void DebugMessageChanged(object sender, EventArgsDebug e)
{
BeginInvoke(new Action(() =>
{
Log(e.Value, e.Slot, e.Source, e.PcbId);
}));
}
private void RequestTempretureSelection(object sender, EventArgs e)
{
BeginInvoke(new Action(() =>
{
Log("Temperature requesting, please set actual value");
_tempRequestTimer.Start();
}));
}
private List<MeterStateControl> CreateMeterControls(IEnumerable<GciPublicModels.MeterBatchDebugStatus> slots)
@ -332,79 +425,13 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
return controls;
}
private PreAdjustmentSettingsContainer CreatePreAdjustmentSettings(IEnumerable<GciPublicModels.MeterBatchDebugStatus> slots)
private void pushTestBenchTempButton_Click(object sender, EventArgs e)
{
var settings =
new PreAdjustmentSettingsContainer();
_bridge.PreAdjustment_PushTemperature(Convert.ToDouble(pushedTestBenchTempTextBox.Text));
settings.Meters =
slots.Select(s => s.Slot).ToList();
settings.TempMeters =
new List<int>();
settings.NumberOfPaths = 2;
settings.TempOnly = false;
settings.Culture =
Thread.CurrentThread.CurrentCulture;
return settings;
}
private void writeRegisterButton_Click(object sender, EventArgs e)
{
/*ExecuteAsync(async token =>
{
string registerName = Convert.ToString(writeRegisterNameComboBox.Text).Trim();
string valueText = writeRegisterValueTextBox.Text.Trim();
if (string.IsNullOrWhiteSpace(registerName))
throw new Exception("Write register name is empty.");
if (string.IsNullOrWhiteSpace(valueText))
throw new Exception("Write register value is empty.");
object value;
if (registerName == "GENESISFLOW_LedMode")
{
value = byte.Parse(valueText);
}
else
{
value = valueText;
}
var tasks = GetSelectedSlots()
.Select(async slot =>
{
//var result = await _bridge.WriteRegisterAsync(slot.Slot, registerName, value, false, false, token);
//var result = await _bridge.WriteRegisterWithRetryAsync(slot.Slot, registerName, value, false, false, token);
var result = Xylem.Common.Ui.CordonelPreadjustmentUi. Processes.WriteRegisterSafe(meter, "Calibration factor1", Register.Genesisflow.CalFactor1, setting.CalFactor1);
Processes
return new
{
Slot = slot.Slot,
Result = result
};
})
.ToList();
var results = await Task.WhenAll(tasks);
foreach (var item in results.OrderBy(x => x.Slot))
{
LogResult(
$"WriteRegisterAsync slot {item.Slot}, register {registerName}, value {value}",
item.Result);
}
RefreshGrid();
});*/
_tempRequestTimer.Stop();
pushTestBenchTempButton.BackColor = SystemColors.Control;
}
private async Task ExecutePreadjustmentForSelectedSlotsAsync(
@ -421,9 +448,6 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
return;
}
var pp =
CreatePreparationProgress();
foreach (var selectedSlot in selectedSlots)
{
PreAdjustmentProcessResult result =
@ -441,5 +465,31 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
RefreshGrid();
}
private async Task ExecutePreadjustmentActionAsync(
Func<int, CancellationToken, Task<PreAdjustmentProcessResult>> asyncAction,
Func<PreAdjustmentProcessResult> directAction,
string processName,
CancellationToken token)
{
if (UseDirectProcess)
{
PreAdjustmentProcessResult result =
await Task.Run(() => directAction(), token);
Log(result.Success
? $"{processName} completed. {result}"
: $"{processName} failed. {result}");
RefreshGrid();
return;
}
await ExecutePreadjustmentForSelectedSlotsAsync(
asyncAction,
processName,
token);
}
}
}

View File

@ -21,31 +21,53 @@
private void InitializeComponent()
{
this.grpStorage = new System.Windows.Forms.GroupBox();
this.lblPcbId = new System.Windows.Forms.Label();
this.txtPcbId = new System.Windows.Forms.TextBox();
this.btnGetPasswordByPcb = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.txtLog = new System.Windows.Forms.TextBox();
this.lblPcbId = new System.Windows.Forms.Label();
this.txtPcbId = new System.Windows.Forms.TextBox();
this.getCalibrationParamsButton = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.calibrationParamsTextBox = new System.Windows.Forms.TextBox();
this.grpStorage.SuspendLayout();
this.SuspendLayout();
//
// grpStorage
//
this.grpStorage.Controls.Add(this.calibrationParamsTextBox);
this.grpStorage.Controls.Add(this.label1);
this.grpStorage.Controls.Add(this.getCalibrationParamsButton);
this.grpStorage.Controls.Add(this.lblPcbId);
this.grpStorage.Controls.Add(this.txtPcbId);
this.grpStorage.Controls.Add(this.btnGetPasswordByPcb);
this.grpStorage.Location = new System.Drawing.Point(10, 37);
this.grpStorage.Name = "grpStorage";
this.grpStorage.Size = new System.Drawing.Size(200, 261);
this.grpStorage.Size = new System.Drawing.Size(215, 261);
this.grpStorage.TabIndex = 0;
this.grpStorage.TabStop = false;
this.grpStorage.Text = "UniDataStorageReader for GCI";
//
// lblPcbId
//
this.lblPcbId.AutoSize = true;
this.lblPcbId.Location = new System.Drawing.Point(10, 25);
this.lblPcbId.Name = "lblPcbId";
this.lblPcbId.Size = new System.Drawing.Size(45, 13);
this.lblPcbId.TabIndex = 0;
this.lblPcbId.Text = "PCB ID:";
//
// txtPcbId
//
this.txtPcbId.Location = new System.Drawing.Point(65, 22);
this.txtPcbId.Name = "txtPcbId";
this.txtPcbId.Size = new System.Drawing.Size(120, 20);
this.txtPcbId.TabIndex = 1;
//
// btnGetPasswordByPcb
//
this.btnGetPasswordByPcb.Location = new System.Drawing.Point(10, 55);
this.btnGetPasswordByPcb.Location = new System.Drawing.Point(13, 48);
this.btnGetPasswordByPcb.Name = "btnGetPasswordByPcb";
this.btnGetPasswordByPcb.Size = new System.Drawing.Size(175, 28);
this.btnGetPasswordByPcb.Size = new System.Drawing.Size(196, 28);
this.btnGetPasswordByPcb.TabIndex = 2;
this.btnGetPasswordByPcb.Text = "Get password (by PCB)";
this.btnGetPasswordByPcb.UseVisualStyleBackColor = true;
@ -64,30 +86,40 @@
//
// txtLog
//
this.txtLog.Location = new System.Drawing.Point(220, 10);
this.txtLog.Location = new System.Drawing.Point(231, 10);
this.txtLog.Multiline = true;
this.txtLog.Name = "txtLog";
this.txtLog.ReadOnly = true;
this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.txtLog.Size = new System.Drawing.Size(500, 340);
this.txtLog.Size = new System.Drawing.Size(489, 340);
this.txtLog.TabIndex = 4;
this.txtLog.WordWrap = false;
//
// lblPcbId
// getCalibrationParamsButton
//
this.lblPcbId.AutoSize = true;
this.lblPcbId.Location = new System.Drawing.Point(10, 25);
this.lblPcbId.Name = "lblPcbId";
this.lblPcbId.Size = new System.Drawing.Size(45, 13);
this.lblPcbId.TabIndex = 0;
this.lblPcbId.Text = "PCB ID:";
this.getCalibrationParamsButton.Location = new System.Drawing.Point(13, 139);
this.getCalibrationParamsButton.Name = "getCalibrationParamsButton";
this.getCalibrationParamsButton.Size = new System.Drawing.Size(196, 34);
this.getCalibrationParamsButton.TabIndex = 3;
this.getCalibrationParamsButton.Text = "Get CalibrationParams (by MeterSize)";
this.getCalibrationParamsButton.UseVisualStyleBackColor = true;
this.getCalibrationParamsButton.Click += new System.EventHandler(this.getCalibrationParamsButton_Click);
//
// txtPcbId
// label1
//
this.txtPcbId.Location = new System.Drawing.Point(65, 22);
this.txtPcbId.Name = "txtPcbId";
this.txtPcbId.Size = new System.Drawing.Size(120, 20);
this.txtPcbId.TabIndex = 1;
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(10, 116);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(54, 13);
this.label1.TabIndex = 4;
this.label1.Text = "MeterSize";
//
// calibrationParamsTextBox
//
this.calibrationParamsTextBox.Location = new System.Drawing.Point(65, 113);
this.calibrationParamsTextBox.Name = "calibrationParamsTextBox";
this.calibrationParamsTextBox.Size = new System.Drawing.Size(120, 20);
this.calibrationParamsTextBox.TabIndex = 5;
//
// UniDataSorageActionsView
//
@ -105,5 +137,8 @@
private System.Windows.Forms.Label lblPcbId;
private System.Windows.Forms.TextBox txtPcbId;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button getCalibrationParamsButton;
private System.Windows.Forms.TextBox calibrationParamsTextBox;
}
}

View File

@ -30,7 +30,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
var result = await _bridge.GetPasswordAsync(pcbId, token);
LogResult("GetPasswordAsync PCB=" + pcbId, result);
LogResult("ReadMeterLoginPasswordAsync PCB=" + pcbId, result);
});
}
@ -100,5 +100,31 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
message +
Environment.NewLine);
}
private void getCalibrationParamsButton_Click(
object sender,
EventArgs e)
{
ExecuteAsync(async token =>
{
if (!int.TryParse(
calibrationParamsTextBox.Text.Trim(),
out int meterSize))
{
throw new Exception(
"Meter size is invalid.");
}
var result =
await _bridge.GetPreAdjustmentCalibrationParamsAsync(
meterSize,
token);
LogResult(
"GetPreAdjustmentCalibrationParamsAsync MeterSize=" +
meterSize,
result);
});
}
}
}