Develop - GciBridge -> GCI -> create database core in GCI - SkeletoKey - CalibParams - first ok
This commit is contained in:
parent
061e85ca6b
commit
2ea0df8faf
17
GenesisCordonelInterface/API/Enums.cs
Normal file
17
GenesisCordonelInterface/API/Enums.cs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,9 @@
|
||||
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;
|
||||
@ -36,12 +40,31 @@ namespace GenesisCordonelInterface.API
|
||||
/// </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));
|
||||
}
|
||||
|
||||
// Laatzen ToolBox actions
|
||||
@ -50,7 +73,7 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
public PortDetectionResult DetectStreamingPort(int slot)
|
||||
{
|
||||
var result = _innerMeterAPI.DetectStreamingPort(slot);
|
||||
var result = _innerMeterAPI?.DetectStreamingPort(slot);
|
||||
//RaiseMeterBatchStatusChanged();
|
||||
return result;
|
||||
}
|
||||
@ -59,14 +82,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 +98,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 +507,7 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================================== DEBUG STATUS ==================================
|
||||
@ -497,7 +521,7 @@ namespace GenesisCordonelInterface.API
|
||||
/// </returns>
|
||||
public List<WorkerDebugStatus> GetWorkerDebugStatuses()
|
||||
{
|
||||
return _innerMeterAPI.GetWorkerDebugStatuses();
|
||||
return _innerMeterAPI?.GetWorkerDebugStatuses();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -509,7 +533,7 @@ namespace GenesisCordonelInterface.API
|
||||
/// </returns>
|
||||
public List<MeterBatchDebugStatus> GetMeterBatchDebugStatuses()
|
||||
{
|
||||
return _innerMeterAPI.GetMeterBatchDebugStatuses();
|
||||
return _innerMeterAPI?.GetMeterBatchDebugStatuses();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -538,7 +562,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 +575,7 @@ namespace GenesisCordonelInterface.API
|
||||
/// </returns>
|
||||
public bool IsSlotSelected(int slot)
|
||||
{
|
||||
return _innerMeterAPI.IsSlotSelected(slot);
|
||||
return (bool)(_innerMeterAPI?.IsSlotSelected(slot));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -562,7 +586,7 @@ namespace GenesisCordonelInterface.API
|
||||
/// </returns>
|
||||
public List<int> GetSelectedSlots()
|
||||
{
|
||||
return _innerMeterAPI.GetSelectedSlots();
|
||||
return _innerMeterAPI?.GetSelectedSlots();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -577,7 +601,7 @@ namespace GenesisCordonelInterface.API
|
||||
/// </returns>
|
||||
public List<string> GetAllRegisterNames()
|
||||
{
|
||||
return _innerMeterAPI.GetAllRegisterNames();
|
||||
return _innerMeterAPI?.GetAllRegisterNames();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -589,7 +613,7 @@ namespace GenesisCordonelInterface.API
|
||||
ProcessProgress pp,
|
||||
List<MeterStateControl> mc)
|
||||
{
|
||||
return _innerMeterAPI.Preadjustment_Initialization(pp, mc);
|
||||
return _innerMeterAPI?.Preadjustment_Initialization(pp, mc);
|
||||
}
|
||||
|
||||
public Task<PreadjustmentDetectResult> PreAdjustment_DetectAsync(
|
||||
@ -641,5 +665,62 @@ namespace GenesisCordonelInterface.API
|
||||
}
|
||||
|
||||
#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, string>> ReadPreAdjustmentCalibrationParamsAsync(
|
||||
DataQuery query,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return calibrationParamsReader.ReadCalibrationParamsAsync(
|
||||
query,
|
||||
token);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Config
|
||||
namespace GenesisCordonelInterface.Core.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Root GCI configuration object loaded from gci_config.json.
|
||||
@ -9,7 +9,7 @@
|
||||
/// Example:
|
||||
///
|
||||
/// {
|
||||
/// "DataStorage":
|
||||
/// "DataStorageSection":
|
||||
/// {
|
||||
/// ...
|
||||
/// }
|
||||
@ -27,6 +27,6 @@
|
||||
/// - PreAdjustmentCalibrationParams
|
||||
/// - future storage providers
|
||||
/// </summary>
|
||||
public GciDataStorageConfig DataStorage { get; set; }
|
||||
public DataStorage.Config.GciDataStorageConfig DataStorageSection { get; set; }
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@ using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Config
|
||||
namespace GenesisCordonelInterface.Core.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads GCI configuration from gci_config.json.
|
||||
@ -114,15 +114,15 @@ namespace GenesisCordonelInterface.Core.DataStorage.Config
|
||||
GciConfig config,
|
||||
string baseDirectory)
|
||||
{
|
||||
if (config.DataStorage == null)
|
||||
if (config.DataStorageSection == null)
|
||||
return;
|
||||
|
||||
NormalizePath(
|
||||
config.DataStorage.MeterLoginPasswords,
|
||||
config.DataStorageSection.MeterLoginPasswords,
|
||||
baseDirectory);
|
||||
|
||||
NormalizePath(
|
||||
config.DataStorage.PreAdjustmentCalibrationParams,
|
||||
config.DataStorageSection.PreAdjustmentCalibrationParams,
|
||||
baseDirectory);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"_Comment": "GCI DataStorage configuration",
|
||||
|
||||
"DataStorage": {
|
||||
"DataStorageSection": {
|
||||
|
||||
"MeterLoginPasswords": {
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>>();
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
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);
|
||||
result.Values[reader.GetName(i)] =
|
||||
value == DBNull.Value ? null : value;
|
||||
object value =
|
||||
reader.GetValue(i);
|
||||
|
||||
object normalizedValue =
|
||||
value == DBNull.Value
|
||||
? null
|
||||
: value;
|
||||
|
||||
row[reader.GetName(i)] =
|
||||
normalizedValue;
|
||||
}
|
||||
|
||||
// Preserve first row for legacy consumers
|
||||
// expecting a single returned database record
|
||||
// (e.g. MeterLoginPasswordReader).
|
||||
if (result.Rows.Count == 0)
|
||||
{
|
||||
foreach (var item in row)
|
||||
{
|
||||
result.Values[item.Key] =
|
||||
item.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// Store complete database result set.
|
||||
result.Rows.Add(row);
|
||||
}
|
||||
|
||||
if (result.Rows.Count == 0)
|
||||
{
|
||||
result.Found = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,8 @@ using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads meter login password from configured GCI data storage.
|
||||
/// </summary>
|
||||
@ -33,24 +35,39 @@ public class MeterLoginPasswordReader : IMeterLoginPasswordReader
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously reads meter login password by query parameter.
|
||||
/// Asynchronously reads meter login password using
|
||||
/// the provided data query.
|
||||
///
|
||||
/// Thread-safe implementation may serialize
|
||||
/// access to the underlying storage.
|
||||
/// </summary>
|
||||
/// <param name="queryParam">PCB ID or another configured lookup value.</param>
|
||||
/// <param name="token">Cancellation token.</param>
|
||||
/// <returns>Password if found; otherwise null.</returns>
|
||||
public async Task<string> GetPasswordAsync(
|
||||
string queryParam,
|
||||
/// <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 (string.IsNullOrWhiteSpace(queryParam))
|
||||
throw new ArgumentException("Query parameter must not be empty.", nameof(queryParam));
|
||||
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(queryParam),
|
||||
() => GetPassword(query),
|
||||
token);
|
||||
}
|
||||
finally
|
||||
@ -60,32 +77,50 @@ public class MeterLoginPasswordReader : IMeterLoginPasswordReader
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads meter login password by query parameter.
|
||||
/// Reads meter login password using
|
||||
/// the provided data query.
|
||||
/// </summary>
|
||||
/// <param name="queryParam">PCB ID or another configured lookup value.</param>
|
||||
/// <returns>Password if found; otherwise null.</returns>
|
||||
public string GetPassword(string queryParam)
|
||||
/// <param name="query">
|
||||
/// Data query containing lookup parameter values.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Password if found; otherwise null.
|
||||
/// </returns>
|
||||
public string GetPassword(
|
||||
DataQuery query)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(queryParam))
|
||||
throw new ArgumentException("Query parameter must not be empty.", nameof(queryParam));
|
||||
if (query == null)
|
||||
throw new ArgumentNullException(nameof(query));
|
||||
|
||||
DataQuery query = new DataQuery();
|
||||
query.QueryParams.Add(queryParam);
|
||||
if (query.QueryParams.Count == 0)
|
||||
throw new ArgumentException(
|
||||
"Query does not contain any parameter.",
|
||||
nameof(query));
|
||||
|
||||
object result = reader.GetData(query);
|
||||
object result =
|
||||
reader.GetData(query);
|
||||
|
||||
if (result is ReaderDiagnosticResult csvResult)
|
||||
return csvResult.Data?.ToString();
|
||||
if (result is ReaderDiagnosticResult diagnosticResult)
|
||||
{
|
||||
return diagnosticResult.Data?.ToString();
|
||||
}
|
||||
|
||||
if (result is DatabaseSearchResult dbResult)
|
||||
{
|
||||
if (!dbResult.Found || dbResult.Values == null || dbResult.Values.Count == 0)
|
||||
if (!dbResult.Found ||
|
||||
dbResult.Values == null ||
|
||||
dbResult.Values.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (object value in dbResult.Values.Values)
|
||||
{
|
||||
return value?.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return result?.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,43 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
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, string> 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, string>> ReadCalibrationParamsAsync(
|
||||
DataQuery query,
|
||||
CancellationToken token = default);
|
||||
}
|
||||
}
|
||||
@ -1,87 +1,250 @@
|
||||
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, string>> 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";
|
||||
await readLock.WaitAsync(token);
|
||||
|
||||
using (var connection = new SqlConnection(_connectionString))
|
||||
|
||||
using (var command = new SqlCommand(query, connection))
|
||||
try
|
||||
{
|
||||
command.Parameters.AddWithValue(
|
||||
"@Dn_InternalId",
|
||||
dnInternalId);
|
||||
|
||||
await connection.OpenAsync();
|
||||
|
||||
using (var reader =
|
||||
await command.ExecuteReaderAsync())
|
||||
return await Task.Run(
|
||||
() => GetCalibrationParams(query),
|
||||
token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
while (await reader.ReadAsync())
|
||||
readLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all pre-adjustment calibration parameters
|
||||
/// using the provided data query.
|
||||
///
|
||||
/// The query is expected to contain meter size
|
||||
/// as the first lookup parameter.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Data query containing lookup parameter values.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Dictionary:
|
||||
///
|
||||
/// Key:
|
||||
/// GENESISFLOW parameter name
|
||||
///
|
||||
/// Value:
|
||||
/// Stored parameter value
|
||||
/// </returns>
|
||||
public Dictionary<string, string> GetCalibrationParams(
|
||||
DataQuery query)
|
||||
{
|
||||
var parameterName = reader["ParameterName"].ToString();
|
||||
ValidateQuery(query);
|
||||
|
||||
var parameterValue = reader["ParameterValue"].ToString();
|
||||
object result =
|
||||
reader.GetData(query);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(parameterName))
|
||||
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)
|
||||
{
|
||||
result[parameterName] = parameterValue;
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
/// <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, string> ConvertResultToDictionary(
|
||||
object result)
|
||||
{
|
||||
Dictionary<string, string> values =
|
||||
new Dictionary<string, string>();
|
||||
|
||||
if (result == null)
|
||||
return values;
|
||||
|
||||
if (result is DatabaseSearchResult dbResult)
|
||||
{
|
||||
if (!dbResult.Found)
|
||||
return values;
|
||||
|
||||
foreach (Dictionary<string, object> row in dbResult.Rows)
|
||||
{
|
||||
if (!row.TryGetValue(
|
||||
"ParameterName",
|
||||
out object parameterNameObject))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!row.TryGetValue(
|
||||
"ParameterValue",
|
||||
out object parameterValueObject))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string parameterName =
|
||||
parameterNameObject?.ToString();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(parameterName))
|
||||
continue;
|
||||
|
||||
values[parameterName] =
|
||||
parameterValueObject?.ToString();
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
if (result is ReaderDiagnosticResult diagnosticResult)
|
||||
{
|
||||
if (diagnosticResult.Data is Dictionary<string, string> dictionary)
|
||||
return dictionary;
|
||||
|
||||
if (diagnosticResult.Data is Dictionary<string, object> objectDictionary)
|
||||
{
|
||||
foreach (var item in objectDictionary)
|
||||
{
|
||||
values[item.Key] =
|
||||
item.Value?.ToString();
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
if (result is Dictionary<string, string> directDictionary)
|
||||
return directDictionary;
|
||||
|
||||
if (result is Dictionary<string, object> directObjectDictionary)
|
||||
{
|
||||
foreach (var item in directObjectDictionary)
|
||||
{
|
||||
values[item.Key] =
|
||||
item.Value?.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
||||
73
GenesisCordonelInterface/Core/Engine/Engine.cs
Normal file
73
GenesisCordonelInterface/Core/Engine/Engine.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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" />
|
||||
@ -198,7 +200,8 @@
|
||||
<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="docs\articles\API\index.md" />
|
||||
|
||||
@ -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,13 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
|
||||
readonly GciBridgeCfg gciBridgeCfg;
|
||||
|
||||
readonly UdsReaderType reader;
|
||||
//readonly UdsReaderType_LoginPasswords loginPasswordsDataStorageReader;
|
||||
//readonly UdsReaderType_CalibrationParams calibrationParamsStorageReader;
|
||||
readonly UdsWriterType writer;
|
||||
|
||||
//GCI main init
|
||||
GciEngine _gciEngine;
|
||||
|
||||
//diag GUI for GCI
|
||||
GciGUIType gciGUI;
|
||||
Form gciGuiHostForm;
|
||||
@ -55,18 +67,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()
|
||||
@ -82,9 +94,9 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
gciBridgeCfg = cfg as GciBridgeCfg;
|
||||
if (gciBridgeCfg == null) throw new Exception("Invalid GciBridgeCfg.");
|
||||
|
||||
if (!string.IsNullOrEmpty(gciBridgeCfg.ReaderName))
|
||||
/*if (!string.IsNullOrEmpty(gciBridgeCfg.ReaderName))
|
||||
{
|
||||
reader = TbfComponents.FindComponent(gciBridgeCfg.ReaderName, components) as UdsReaderType;
|
||||
reader = TbfComponents.FindComponent(gciBridgeCfg.ReaderName, components) as UdsReaderType_LoginPasswords;
|
||||
if (reader == null) throw new Exception("Cannot find reader component '" + gciBridgeCfg.ReaderName + "'");
|
||||
}
|
||||
|
||||
@ -92,7 +104,9 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
{
|
||||
writer = TbfComponents.FindComponent(gciBridgeCfg.WriterName, components) as UdsWriterType;
|
||||
if (writer == null) throw new Exception("Cannot find writer component '" + gciBridgeCfg.WriterName + "'");
|
||||
}
|
||||
}*/
|
||||
|
||||
_gciEngine = new GciEngine();
|
||||
|
||||
Initialize();
|
||||
}
|
||||
@ -179,10 +193,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 +269,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 +308,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 +389,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 +465,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 +541,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 +614,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 +688,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 +805,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 +837,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 +908,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 +991,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 +1066,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 +1156,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 +1237,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 +1258,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 +1276,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error("GetPasswordAsync failed.", ex);
|
||||
log.Error("ReadMeterLoginPasswordAsync failed.", ex);
|
||||
|
||||
return new UdsPasswordResult
|
||||
{
|
||||
@ -1273,7 +1288,128 @@ 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, string> 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, string> ExtractPreAdjustmentCalibrationParams(
|
||||
object data)
|
||||
{
|
||||
Dictionary<string, string> result =
|
||||
new Dictionary<string, string>();
|
||||
|
||||
if (data == null)
|
||||
return result;
|
||||
|
||||
if (data is Dictionary<string, string> directDictionary)
|
||||
return directDictionary;
|
||||
|
||||
if (data is Dictionary<string, object> objectDictionary)
|
||||
{
|
||||
foreach (var item in objectDictionary)
|
||||
{
|
||||
result[item.Key] =
|
||||
item.Value?.ToString();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads password from UniDataStorageReader using PCB ID with retry support.
|
||||
@ -1288,7 +1424,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
///
|
||||
/// GetPasswordWithRetryAsync()
|
||||
/// -> RetryWorker.RunWithRetryAsync()
|
||||
/// -> GetPasswordAsync()
|
||||
/// -> ReadMeterLoginPasswordAsync()
|
||||
/// -> UniDataStorageReader
|
||||
///
|
||||
/// Returns:
|
||||
@ -1310,7 +1446,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 +1498,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
/// -> RetryWorker.RunWithRetryAsync()
|
||||
/// -> ConnectAsync()
|
||||
/// -> GetPcbIdAsync()
|
||||
/// -> GetPasswordAsync()
|
||||
/// -> ReadMeterLoginPasswordAsync()
|
||||
/// -> SetPasswordAsync()
|
||||
/// -> LoginAsync()
|
||||
///
|
||||
@ -1393,7 +1529,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 +1578,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 +1622,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);
|
||||
|
||||
@ -1533,7 +1669,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
slot);
|
||||
|
||||
PreAdjustmentProcessResult result =
|
||||
await gciExternalInterface
|
||||
await _gciEngine.gciExternalInterface
|
||||
.PreAdjustment_PreparationAsync(
|
||||
slot,
|
||||
token)
|
||||
@ -1584,7 +1720,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
slot);
|
||||
|
||||
PreAdjustmentProcessResult result =
|
||||
await gciExternalInterface
|
||||
await _gciEngine.gciExternalInterface
|
||||
.PreAdjustment_AmplitudeTestAsync(
|
||||
slot,
|
||||
token)
|
||||
@ -1635,7 +1771,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
slot);
|
||||
|
||||
PreAdjustmentProcessResult result =
|
||||
await gciExternalInterface
|
||||
await _gciEngine.gciExternalInterface
|
||||
.PreAdjustment_TemperatureCalibrationAsync(
|
||||
slot,
|
||||
token)
|
||||
@ -1680,7 +1816,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
operation,
|
||||
temperature);
|
||||
|
||||
return gciExternalInterface.PreAdjustment_PushTemperature(temperature);
|
||||
return _gciEngine.gciExternalInterface.PreAdjustment_PushTemperature(temperature);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -1713,7 +1849,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
slot);
|
||||
|
||||
PreAdjustmentProcessResult result =
|
||||
await gciExternalInterface
|
||||
await _gciEngine.gciExternalInterface
|
||||
.PreAdjustment_OffsetTestAsync(
|
||||
slot,
|
||||
token)
|
||||
@ -1764,7 +1900,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
slot);
|
||||
|
||||
PreAdjustmentProcessResult result =
|
||||
await gciExternalInterface
|
||||
await _gciEngine.gciExternalInterface
|
||||
.PreAdjustment_CompletionAsync(
|
||||
slot,
|
||||
token)
|
||||
@ -1797,9 +1933,9 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
#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;
|
||||
}
|
||||
@ -1834,7 +1970,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
public List<string> GetAllRegisterNames()
|
||||
{
|
||||
EnsureExternalInterface();
|
||||
return gciExternalInterface.GetAllRegisterNames();
|
||||
return _gciEngine.gciExternalInterface.GetAllRegisterNames();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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, string> 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; }
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user