Compare commits
3 Commits
9700ba7900
...
a3bfc7a43e
| Author | SHA1 | Date | |
|---|---|---|---|
| a3bfc7a43e | |||
| d1b6247bb7 | |||
| c5f52e06fa |
@ -43,6 +43,7 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
//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>();
|
||||
|
||||
@ -174,6 +175,16 @@ namespace GenesisCordonelInterface.API
|
||||
}
|
||||
}
|
||||
|
||||
private ZeroFlowGenesisMeter GetMeterThreadSafe2(int slot)
|
||||
{
|
||||
lock (_meterBatchLock)
|
||||
{
|
||||
return _meterBatch.ListOfMeters
|
||||
.OfType<ZeroFlowGenesisMeter>()
|
||||
.FirstOrDefault(m => m.Slot == slot);
|
||||
}
|
||||
}
|
||||
|
||||
private bool RemoveMeterThreadSafe(GenesisMeter meter)
|
||||
{
|
||||
if (meter == null)
|
||||
@ -653,7 +664,7 @@ namespace GenesisCordonelInterface.API
|
||||
{
|
||||
const string operation = nameof(CleanAllSlots);
|
||||
|
||||
try
|
||||
try
|
||||
{
|
||||
LogInfo(operation, "Start.");
|
||||
|
||||
@ -1414,7 +1425,7 @@ namespace GenesisCordonelInterface.API
|
||||
private FrmCordonelPreadjustmentUI _preadjustmentForm;
|
||||
private readonly object _formLock = new object();
|
||||
|
||||
public event EventHandler PreadjustmentFormClosedByUser;
|
||||
public event EventHandler PreAdjustmentFormClosedByUser;
|
||||
|
||||
/// <summary>
|
||||
/// Shows singleton instance of preadjustment form.
|
||||
@ -1456,7 +1467,7 @@ namespace GenesisCordonelInterface.API
|
||||
/// _bridge.ShowPreadjustmentForm(this);
|
||||
///
|
||||
/// </summary>
|
||||
public void ShowPreadjustmentForm(IWin32Window owner)
|
||||
public void ShowPreAdjustmentForm(IWin32Window owner)
|
||||
{
|
||||
lock (_formLock)
|
||||
{
|
||||
@ -1476,7 +1487,7 @@ namespace GenesisCordonelInterface.API
|
||||
_preadjustmentForm.Hide();
|
||||
|
||||
// Notify outside code that the form was closed by user
|
||||
PreadjustmentFormClosedByUser?.Invoke(
|
||||
PreAdjustmentFormClosedByUser?.Invoke(
|
||||
this,
|
||||
EventArgs.Empty);
|
||||
}
|
||||
@ -1524,16 +1535,69 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
// GCI to PreadjustmentUI - AUTOMATIC - STANDALONE - Laatzen GUI
|
||||
|
||||
#region ================================== PreAdjustmentUI INIT ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Initializes PreAdjustment environment.
|
||||
/// </summary>
|
||||
public PreAdjustmentInitializationResult Preadjustment_Initialization(
|
||||
ProcessProgress pp,
|
||||
List<MeterStateControl> mc)
|
||||
{
|
||||
const string operation = nameof(Preadjustment_Initialization);
|
||||
|
||||
try
|
||||
{
|
||||
LogInfo(operation, "Start.");
|
||||
|
||||
_progressProcess = pp;
|
||||
_meterControls = mc;
|
||||
|
||||
var initializedSlots = _meterBatch.ListOfMeters
|
||||
.Select(m => m.Slot)
|
||||
.OrderBy(s => s)
|
||||
.ToList();
|
||||
|
||||
var result =
|
||||
new PreAdjustmentInitializationResult
|
||||
{
|
||||
Success = initializedSlots.Any(),
|
||||
Slots = string.Join(",", initializedSlots),
|
||||
ErrorMessage = initializedSlots.Any()
|
||||
? null
|
||||
: "No initialized meters found."
|
||||
};
|
||||
|
||||
LogInfo(operation, result.ToString());
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError(operation, ex);
|
||||
|
||||
return new PreAdjustmentInitializationResult
|
||||
{
|
||||
Success = false,
|
||||
ErrorMessage = ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================================== PreAdjustmentUI DETECT process ==================================
|
||||
|
||||
public Task<PreadjustmentDetectResult> Preadjustment_DetectAsync(
|
||||
public Task<PreadjustmentDetectResult> PreAdjustment_DetectAsync(
|
||||
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return Task.Run(() => Preadjustment_DetectCore(token), token);
|
||||
return Task.Run(() => Preadjustment_DetectCore(selectedSlots, token), token);
|
||||
}
|
||||
|
||||
private PreadjustmentDetectResult Preadjustment_DetectCore(
|
||||
CancellationToken token)
|
||||
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
|
||||
CancellationToken token)
|
||||
{
|
||||
const string operation = nameof(Preadjustment_DetectCore);
|
||||
|
||||
@ -1558,6 +1622,7 @@ namespace GenesisCordonelInterface.API
|
||||
thermoMeterBatch.RemoveAllMeters();
|
||||
|
||||
globalMeterBatch = _meterBatch;
|
||||
_meterControls = CreateMeterControls(selectedSlots);
|
||||
|
||||
if (!_settings.GetTempUseTempFlansh())
|
||||
_tempMeterControls.Clear();
|
||||
@ -1610,6 +1675,24 @@ namespace GenesisCordonelInterface.API
|
||||
}
|
||||
}
|
||||
|
||||
private List<MeterStateControl> CreateMeterControls(IEnumerable<PublicModels.MeterBatchDebugStatus> slots)
|
||||
{
|
||||
var controls =
|
||||
new List<MeterStateControl>();
|
||||
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var ctl = new MeterStateControl(slot.Slot);
|
||||
|
||||
ctl.SetChecked(true);
|
||||
ctl.IsEnabled = true;
|
||||
|
||||
controls.Add(ctl);
|
||||
}
|
||||
|
||||
return controls;
|
||||
}
|
||||
|
||||
private void CreateZeroFlowMeters(
|
||||
MeterBatch globalMeterBatch,
|
||||
MeterBatch thermoMeterBatch,
|
||||
@ -1629,7 +1712,7 @@ namespace GenesisCordonelInterface.API
|
||||
if (meterStateCtl.Slot == -1)
|
||||
continue;
|
||||
|
||||
ZeroFlowGenesisMeter currentMeter = new ZeroFlowGenesisMeter(meterStateCtl.Slot, 3, !(meterStateCtl is TempMeterStateControl));
|
||||
/*ZeroFlowGenesisMeter currentMeter = new ZeroFlowGenesisMeter(meterStateCtl.Slot, 3, !(meterStateCtl is TempMeterStateControl));
|
||||
|
||||
var convertedMeter = GetMeterThreadSafe(meterStateCtl.Slot);
|
||||
if (convertedMeter != null)
|
||||
@ -1660,7 +1743,9 @@ namespace GenesisCordonelInterface.API
|
||||
globalMeterBatch.AddMeter2(currentMeter);
|
||||
}
|
||||
|
||||
meterStateCtl.Meter = currentMeter;
|
||||
meterStateCtl.Meter = currentMeter;*/
|
||||
|
||||
meterStateCtl.Meter = GetMeterThreadSafe2(meterStateCtl.Slot);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1724,24 +1809,22 @@ namespace GenesisCordonelInterface.API
|
||||
#endregion
|
||||
|
||||
#region ================================== PreAdjustmentUI PREPARATION process ==================================
|
||||
public Task<PreadjustmentProcessResult> Preadjustment_PreparationAsync(
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_PreparationAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetWorker(slot).RunAsync(
|
||||
() => Preadjustment_Preparation(slot, pp),
|
||||
() => PreAdjustment_Preparation(slot),
|
||||
token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes standalone preparation process.
|
||||
/// </summary>
|
||||
public PreadjustmentProcessResult Preadjustment_Preparation(
|
||||
int slot,
|
||||
ProcessProgress pp)
|
||||
public PreAdjustmentProcessResult PreAdjustment_Preparation(
|
||||
int slot)
|
||||
{
|
||||
const string operation = nameof(Preadjustment_Preparation);
|
||||
const string operation = nameof(PreAdjustment_Preparation);
|
||||
|
||||
try
|
||||
{
|
||||
@ -1750,14 +1833,13 @@ namespace GenesisCordonelInterface.API
|
||||
var meter = GetMeterThreadSafe(slot);
|
||||
EnsureConnected(meter);
|
||||
|
||||
BaseProcess process =
|
||||
CreatePreparationProcess(pp);
|
||||
BaseProcess process = CreatePreparationProcess(_progressProcess);
|
||||
|
||||
bool success =
|
||||
ExecuteProcess(process, pp);
|
||||
ExecuteProcess(process, _progressProcess);
|
||||
|
||||
var result =
|
||||
new PreadjustmentProcessResult
|
||||
new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = success,
|
||||
Slot = slot,
|
||||
@ -1775,7 +1857,7 @@ namespace GenesisCordonelInterface.API
|
||||
{
|
||||
LogError(operation, ex);
|
||||
|
||||
return new PreadjustmentProcessResult
|
||||
return new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = false,
|
||||
Slot = slot,
|
||||
@ -1813,21 +1895,19 @@ namespace GenesisCordonelInterface.API
|
||||
#endregion
|
||||
|
||||
#region ================================== PreAdjustmentUI AMPLITUDE TEST process ==================================
|
||||
public Task<PreadjustmentProcessResult> Preadjustment_AmplitudeTestAsync(
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_AmplitudeTestAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetWorker(slot).RunAsync(
|
||||
() => Preadjustment_AmplitudeTest(slot, pp),
|
||||
() => PreAdjustment_AmplitudeTest(slot),
|
||||
token);
|
||||
}
|
||||
|
||||
public PreadjustmentProcessResult Preadjustment_AmplitudeTest(
|
||||
int slot,
|
||||
ProcessProgress pp)
|
||||
public PreAdjustmentProcessResult PreAdjustment_AmplitudeTest(
|
||||
int slot)
|
||||
{
|
||||
const string operation = nameof(Preadjustment_AmplitudeTest);
|
||||
const string operation = nameof(PreAdjustment_AmplitudeTest);
|
||||
|
||||
try
|
||||
{
|
||||
@ -1836,9 +1916,9 @@ namespace GenesisCordonelInterface.API
|
||||
var meter = GetMeterThreadSafe(slot);
|
||||
EnsureConnected(meter);
|
||||
|
||||
if (pp.Setting.TempOnly)
|
||||
if (_progressProcess.Setting.TempOnly)
|
||||
{
|
||||
return new PreadjustmentProcessResult
|
||||
return new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = true,
|
||||
Slot = slot,
|
||||
@ -1846,13 +1926,13 @@ namespace GenesisCordonelInterface.API
|
||||
};
|
||||
}
|
||||
|
||||
BaseProcess process = CreateAmplitudeTestProcess(pp);
|
||||
BaseProcess process = CreateAmplitudeTestProcess(_progressProcess);
|
||||
|
||||
bool success = ExecuteProcess(process, pp);
|
||||
bool success = ExecuteProcess(process, _progressProcess);
|
||||
|
||||
LogInfo(operation, $"Finished. Slot={slot}, Success={success}");
|
||||
|
||||
return new PreadjustmentProcessResult
|
||||
return new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = success,
|
||||
Slot = slot,
|
||||
@ -1864,7 +1944,7 @@ namespace GenesisCordonelInterface.API
|
||||
{
|
||||
LogError(operation, ex);
|
||||
|
||||
return new PreadjustmentProcessResult
|
||||
return new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = false,
|
||||
Slot = slot,
|
||||
@ -1903,22 +1983,19 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
#region ================================== PreAdjustmentUI TEMPERATURE CALIBRATION process ==================================
|
||||
|
||||
public Task<PreadjustmentProcessResult> Preadjustment_TemperatureCalibrationAsync(
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_TemperatureCalibrationAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetWorker(slot).RunAsync(
|
||||
() => Preadjustment_TemperatureCalibration(slot, pp),
|
||||
() => PreAdjustment_TemperatureCalibration(slot),
|
||||
token);
|
||||
}
|
||||
|
||||
public PreadjustmentProcessResult Preadjustment_TemperatureCalibration(
|
||||
int slot,
|
||||
ProcessProgress pp)
|
||||
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibration(
|
||||
int slot)
|
||||
{
|
||||
const string operation =
|
||||
nameof(Preadjustment_TemperatureCalibration);
|
||||
const string operation = nameof(PreAdjustment_TemperatureCalibration);
|
||||
|
||||
try
|
||||
{
|
||||
@ -1929,14 +2006,12 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
EnsureConnected(meter);
|
||||
|
||||
BaseProcess process =
|
||||
CreateTemperatureCalibrationProcess(pp);
|
||||
BaseProcess process = CreateTemperatureCalibrationProcess(_progressProcess);
|
||||
|
||||
bool success =
|
||||
ExecuteProcess(process, pp);
|
||||
bool success = ExecuteProcess(process, _progressProcess);
|
||||
|
||||
var result =
|
||||
new PreadjustmentProcessResult
|
||||
new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = success,
|
||||
Slot = slot,
|
||||
@ -1954,7 +2029,7 @@ namespace GenesisCordonelInterface.API
|
||||
{
|
||||
LogError(operation, ex);
|
||||
|
||||
return new PreadjustmentProcessResult
|
||||
return new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = false,
|
||||
Slot = slot,
|
||||
@ -1993,35 +2068,31 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
#region ================================== PreAdjustmentUI OFFSET TEST process ==================================
|
||||
|
||||
public Task<PreadjustmentProcessResult> Preadjustment_OffsetTestAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_OffsetTestAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetWorker(slot).RunAsync(
|
||||
() => Preadjustment_OffsetTest(slot, pp),
|
||||
() => PreAdjustment_OffsetTest(slot),
|
||||
token);
|
||||
}
|
||||
|
||||
public PreadjustmentProcessResult Preadjustment_OffsetTest(
|
||||
int slot,
|
||||
ProcessProgress pp)
|
||||
public PreAdjustmentProcessResult PreAdjustment_OffsetTest(
|
||||
int slot)
|
||||
{
|
||||
const string operation =
|
||||
nameof(Preadjustment_OffsetTest);
|
||||
const string operation = nameof(PreAdjustment_OffsetTest);
|
||||
|
||||
try
|
||||
{
|
||||
LogInfo(operation, $"Start. Slot={slot}");
|
||||
|
||||
var meter =
|
||||
GetMeterThreadSafe(slot);
|
||||
var meter = GetMeterThreadSafe(slot);
|
||||
|
||||
EnsureConnected(meter);
|
||||
|
||||
if (pp.Setting.TempOnly)
|
||||
if (_progressProcess.Setting.TempOnly)
|
||||
{
|
||||
return new PreadjustmentProcessResult
|
||||
return new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = true,
|
||||
Slot = slot,
|
||||
@ -2029,14 +2100,12 @@ namespace GenesisCordonelInterface.API
|
||||
};
|
||||
}
|
||||
|
||||
BaseProcess process =
|
||||
CreateOffsetTestProcess(pp);
|
||||
BaseProcess process = CreateOffsetTestProcess(_progressProcess);
|
||||
|
||||
bool success =
|
||||
ExecuteProcess(process, pp);
|
||||
bool success = ExecuteProcess(process, _progressProcess);
|
||||
|
||||
var result =
|
||||
new PreadjustmentProcessResult
|
||||
new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = success,
|
||||
Slot = slot,
|
||||
@ -2054,7 +2123,7 @@ namespace GenesisCordonelInterface.API
|
||||
{
|
||||
LogError(operation, ex);
|
||||
|
||||
return new PreadjustmentProcessResult
|
||||
return new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = false,
|
||||
Slot = slot,
|
||||
@ -2093,22 +2162,19 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
#region ================================== PreAdjustmentUI COMPLETION process ==================================
|
||||
|
||||
public Task<PreadjustmentProcessResult> Preadjustment_CompletionAsync(
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_CompletionAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return GetWorker(slot).RunAsync(
|
||||
() => Preadjustment_Completion(slot, pp),
|
||||
() => PreAdjustment_Completion(slot),
|
||||
token);
|
||||
}
|
||||
|
||||
public PreadjustmentProcessResult Preadjustment_Completion(
|
||||
int slot,
|
||||
ProcessProgress pp)
|
||||
public PreAdjustmentProcessResult PreAdjustment_Completion(
|
||||
int slot)
|
||||
{
|
||||
const string operation =
|
||||
nameof(Preadjustment_Completion);
|
||||
const string operation = nameof(PreAdjustment_Completion);
|
||||
|
||||
try
|
||||
{
|
||||
@ -2119,14 +2185,12 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
EnsureConnected(meter);
|
||||
|
||||
BaseProcess process =
|
||||
CreateCompletionProcess(pp);
|
||||
BaseProcess process = CreateCompletionProcess(_progressProcess);
|
||||
|
||||
bool success =
|
||||
ExecuteProcess(process, pp);
|
||||
bool success = ExecuteProcess(process, _progressProcess);
|
||||
|
||||
var result =
|
||||
new PreadjustmentProcessResult
|
||||
new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = success,
|
||||
Slot = slot,
|
||||
@ -2144,7 +2208,7 @@ namespace GenesisCordonelInterface.API
|
||||
{
|
||||
LogError(operation, ex);
|
||||
|
||||
return new PreadjustmentProcessResult
|
||||
return new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = false,
|
||||
Slot = slot,
|
||||
|
||||
@ -12,21 +12,39 @@ using static GenesisCordonelInterface.API.PublicModels;
|
||||
namespace GenesisCordonelInterface.API
|
||||
{
|
||||
/// <summary>
|
||||
/// Outside-facing facade for Genesis Cordonel Interface.
|
||||
/// Exposes only selected operations intended for external callers.
|
||||
/// Public-facing facade for external applications integrating with
|
||||
/// Genesis Cordonel Interface.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class exposes a simplified and controlled API for external callers.
|
||||
/// It validates public input, maps public request models to internal models,
|
||||
/// forwards operations to the internal GCI implementation and exposes status
|
||||
/// notifications for meter batch changes.
|
||||
///
|
||||
/// This layer should stay thin. Business logic and meter communication are
|
||||
/// handled by <see cref="InterfaceGCIToLaatzen"/>.
|
||||
/// </remarks>
|
||||
public class InterfaceOutsideToGCI
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal GCI implementation used by this public facade.
|
||||
/// </summary>
|
||||
public readonly InterfaceGCIToLaatzen _innerMeterAPI;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when meter batch status information changes.
|
||||
/// </summary>
|
||||
public event Action<List<MeterBatchDebugStatus>> MeterBatchStatusChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the public GCI facade.
|
||||
/// </summary>
|
||||
public InterfaceOutsideToGCI()
|
||||
{
|
||||
_innerMeterAPI = new InterfaceGCIToLaatzen();
|
||||
}
|
||||
|
||||
//
|
||||
// Laatzen ToolBox actions
|
||||
|
||||
#region ================================== PORT DETECTION ==================================
|
||||
|
||||
@ -66,6 +84,20 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
#region ================================== INIT/UPDATE/GET slot ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a meter slot using the provided slot configuration.
|
||||
/// </summary>
|
||||
/// <param name="request">
|
||||
/// Slot initialization request containing slot id, configuration source,
|
||||
/// password source, request port and streaming port.
|
||||
/// </param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Result describing whether the slot was created, updated, already existed or failed.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Thrown when <paramref name="request"/> is null.
|
||||
/// </exception>
|
||||
public async Task<GciInitSlotResult> InitSlotAsync(
|
||||
GciInitSlotRequest request,
|
||||
CancellationToken token = default)
|
||||
@ -86,6 +118,20 @@ namespace GenesisCordonelInterface.API
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates configuration of an existing meter slot.
|
||||
/// </summary>
|
||||
/// <param name="request">
|
||||
/// Slot configuration request containing updated configuration source,
|
||||
/// password source and port settings.
|
||||
/// </param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Result describing whether the slot update succeeded or failed.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Thrown when <paramref name="request"/> is null.
|
||||
/// </exception>
|
||||
public async Task<GciInitSlotResult> UpdateSlotAsync(
|
||||
GciInitSlotRequest request,
|
||||
CancellationToken token = default)
|
||||
@ -106,6 +152,18 @@ namespace GenesisCordonelInterface.API
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about one meter slot.
|
||||
/// </summary>
|
||||
/// <param name="slotId">Slot id to query. Must be greater than zero.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Slot information including existence, connection state, login state,
|
||||
/// PCB id and configured communication ports.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="slotId"/> is invalid.
|
||||
/// </exception>
|
||||
public async Task<GciSlotInfo> GetSlotAsync(
|
||||
int slotId,
|
||||
CancellationToken token = default)
|
||||
@ -118,6 +176,13 @@ namespace GenesisCordonelInterface.API
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information about all currently initialized meter slots.
|
||||
/// </summary>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Collection of slot information records for all known meters.
|
||||
/// </returns>
|
||||
public async Task<GciAllSlotsInfo> GetAllSlotsAsync(
|
||||
CancellationToken token = default)
|
||||
{
|
||||
@ -126,6 +191,17 @@ namespace GenesisCordonelInterface.API
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans one meter slot and releases its runtime resources.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id to clean. Must be greater than zero.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Result describing whether the slot cleanup succeeded or failed.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="slot"/> is invalid.
|
||||
/// </exception>
|
||||
public async Task<GciCleanSlotResult> CleanSlotAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
@ -140,6 +216,13 @@ namespace GenesisCordonelInterface.API
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans all initialized meter slots and releases related runtime resources.
|
||||
/// </summary>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Result describing whether cleanup of all slots succeeded or failed.
|
||||
/// </returns>
|
||||
public async Task<GciCleanAllSlotsResult> CleanAllSlotsAsync(
|
||||
CancellationToken token = default)
|
||||
{
|
||||
@ -152,6 +235,17 @@ namespace GenesisCordonelInterface.API
|
||||
#endregion
|
||||
|
||||
#region ================================== PASSWORD ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Sets runtime password for the meter assigned to the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="password">Password to assign to the meter.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>Result containing password update status.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when slot id is invalid or password is empty.
|
||||
/// </exception>
|
||||
public async Task<GciSetPasswordResult> SetPasswordAsync(
|
||||
int slot,
|
||||
string password,
|
||||
@ -173,6 +267,16 @@ namespace GenesisCordonelInterface.API
|
||||
#endregion
|
||||
|
||||
#region ================================== LOGIN ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Logs in to the meter assigned to the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>Login result containing login state and status message.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="slot"/> is invalid.
|
||||
/// </exception>
|
||||
public async Task<PublicModels.GciLoginResult> LoginOneSlotAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
@ -189,6 +293,15 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
#region ================================== CONNECTION ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Connects the meter assigned to the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>Connection result containing connection state and status message.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="slot"/> is invalid.
|
||||
/// </exception>
|
||||
public async Task<GciConnectResult> ConnectOneSlotAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
@ -203,6 +316,15 @@ namespace GenesisCordonelInterface.API
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects the meter assigned to the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>Disconnect result containing final connection state and status message.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="slot"/> is invalid.
|
||||
/// </exception>
|
||||
public async Task<GciDisconnectResult> DisconnectAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
@ -219,6 +341,16 @@ namespace GenesisCordonelInterface.API
|
||||
#endregion
|
||||
|
||||
#region ================================== PCB ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Reads PCB identifier from the meter assigned to the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>Result containing PCB id and read status.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when <paramref name="slot"/> is invalid.
|
||||
/// </exception>
|
||||
public async Task<GciGetPcbIdResult> GetPcbIdAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
@ -234,6 +366,18 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
#region ================================== READ ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Reads a firmware register value from the meter assigned to the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="registerName">Register identifier to read.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Register read result containing raw register value and operation status.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when slot id or register name is invalid.
|
||||
/// </exception>
|
||||
public async Task<RegisterReadResult> ReadRegisterAsync(
|
||||
int slot,
|
||||
string registerName,
|
||||
@ -258,6 +402,25 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
#region ================================== WRITE ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Writes a value to a firmware register.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="registerName">Register identifier to write.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
/// <param name="storeToDevice">
|
||||
/// Indicates whether configuration should be permanently stored.
|
||||
/// </param>
|
||||
/// <param name="refreshSystemState">
|
||||
/// Indicates whether firmware system state should be refreshed after write.
|
||||
/// </param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Register write result describing write status.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when slot id or register name is invalid.
|
||||
/// </exception>
|
||||
public async Task<RegisterWriteResult> WriteRegisterAsync(
|
||||
int slot,
|
||||
string registerName,
|
||||
@ -290,6 +453,18 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
#region ================================== Password ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Updates meter password for the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id. Must be greater than zero.</param>
|
||||
/// <param name="password">New password.</param>
|
||||
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
|
||||
/// <returns>
|
||||
/// Password update result.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown when slot id or password is invalid.
|
||||
/// </exception>
|
||||
public async Task<GciSetPasswordResult> SetMeterPasswordAsync(
|
||||
int slot,
|
||||
string password,
|
||||
@ -313,16 +488,36 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
#region ================================== DEBUG STATUS ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Gets runtime diagnostic information for all active workers.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Collection containing worker state, queue information,
|
||||
/// current operation and activity timestamps.
|
||||
/// </returns>
|
||||
public List<WorkerDebugStatus> GetWorkerDebugStatuses()
|
||||
{
|
||||
return _innerMeterAPI.GetWorkerDebugStatuses();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets runtime diagnostic information for all meter slots.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Collection containing slot state, connection state,
|
||||
/// selected state and communication configuration.
|
||||
/// </returns>
|
||||
public List<MeterBatchDebugStatus> GetMeterBatchDebugStatuses()
|
||||
{
|
||||
return _innerMeterAPI.GetMeterBatchDebugStatuses();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises meter batch status change notification.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Intended to notify external consumers after changes in meter state.
|
||||
/// </remarks>
|
||||
public void RaiseMeterBatchStatusChanged()
|
||||
{
|
||||
var statuses = GetMeterBatchDebugStatuses();
|
||||
@ -332,121 +527,54 @@ namespace GenesisCordonelInterface.API
|
||||
handler(statuses);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region ================================== SLOT SELECTION ==================================
|
||||
// ----------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Sets selection state for a slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id.</param>
|
||||
/// <param name="selected">Selection state.</param>
|
||||
public void SetSlotSelected(int slot, bool selected)
|
||||
{
|
||||
_innerMeterAPI.SetSlotSelected(slot, selected);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified slot is selected.
|
||||
/// </summary>
|
||||
/// <param name="slot">Slot id.</param>
|
||||
/// <returns>
|
||||
/// True if slot is selected; otherwise false.
|
||||
/// </returns>
|
||||
public bool IsSlotSelected(int slot)
|
||||
{
|
||||
return _innerMeterAPI.IsSlotSelected(slot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all selected slot identifiers.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Ordered collection of selected slot ids.
|
||||
/// </returns>
|
||||
public List<int> GetSelectedSlots()
|
||||
{
|
||||
return _innerMeterAPI.GetSelectedSlots();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region ================================== SLOT PORT CONFIG ==================================
|
||||
// ----------------------------------------------------
|
||||
|
||||
/*public void SetSlotRequestPort(int slot, string portName)
|
||||
{
|
||||
lock (_portLock)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(portName))
|
||||
{
|
||||
_requestPorts.Remove(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
_requestPorts[slot] = new GciPortConfig
|
||||
{
|
||||
PortName = portName,
|
||||
Type = "Serial"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
|
||||
public void SetSlotStreamingPort(int slot, string portName)
|
||||
{
|
||||
lock (_portLock)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(portName))
|
||||
{
|
||||
_streamingPorts.Remove(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
_streamingPorts[slot] = new GciPortConfig
|
||||
{
|
||||
PortName = portName,
|
||||
Type = "Serial"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
|
||||
public GciPortConfig? GetSlotRequestPort(int slot)
|
||||
{
|
||||
lock (_portLock)
|
||||
{
|
||||
GciPortConfig port;
|
||||
if (_requestPorts.TryGetValue(slot, out port))
|
||||
return port;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public GciPortConfig? GetSlotStreamingPort(int slot)
|
||||
{
|
||||
lock (_portLock)
|
||||
{
|
||||
GciPortConfig port;
|
||||
if (_streamingPorts.TryGetValue(slot, out port))
|
||||
return port;
|
||||
|
||||
return null;
|
||||
}
|
||||
}*/
|
||||
|
||||
// ----------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region ================================== METER BATCH SETUP ==================================
|
||||
|
||||
public void ReloadSlotSetup()
|
||||
{
|
||||
_innerMeterAPI.ReloadSlotSetup();
|
||||
RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
|
||||
public void SaveSlotSetup(List<MeterBatchDebugStatus> data)
|
||||
{
|
||||
_innerMeterAPI.SaveSlotSetup(data);
|
||||
RaiseMeterBatchStatusChanged();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================================== Register names ==================================
|
||||
|
||||
/// <summary>
|
||||
/// Gets all available firmware register identifiers.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Ordered collection of register names.
|
||||
/// </returns>
|
||||
public List<string> GetAllRegisterNames()
|
||||
{
|
||||
return _innerMeterAPI.GetAllRegisterNames();
|
||||
@ -454,52 +582,58 @@ namespace GenesisCordonelInterface.API
|
||||
|
||||
#endregion
|
||||
|
||||
// Preadjustment
|
||||
// Laatzen Preadjustment processes
|
||||
|
||||
public Task<PreadjustmentDetectResult> Preadjustment_DetectAsync(
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.Preadjustment_DetectAsync(token);
|
||||
}
|
||||
|
||||
public Task<PreadjustmentProcessResult> Preadjustment_PreparationAsync(
|
||||
int slot,
|
||||
#region ================================== PreAdjustment ==================================
|
||||
public PreAdjustmentInitializationResult Preadjustment_Initialization(
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
List<MeterStateControl> mc)
|
||||
{
|
||||
return _innerMeterAPI.Preadjustment_PreparationAsync(slot, pp, token);
|
||||
return _innerMeterAPI.Preadjustment_Initialization(pp, mc);
|
||||
}
|
||||
|
||||
public Task<PreadjustmentProcessResult> Preadjustment_AmplitudeTestAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
public Task<PreadjustmentDetectResult> PreAdjustment_DetectAsync(
|
||||
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.Preadjustment_AmplitudeTestAsync(slot, pp, token);
|
||||
return _innerMeterAPI.PreAdjustment_DetectAsync(selectedSlots, token);
|
||||
}
|
||||
|
||||
public Task<PreadjustmentProcessResult> Preadjustment_TemperatureCalibrationAsync(
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_PreparationAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.Preadjustment_TemperatureCalibrationAsync(slot, pp, token);
|
||||
return _innerMeterAPI.PreAdjustment_PreparationAsync(slot, token);
|
||||
}
|
||||
|
||||
public Task<PreadjustmentProcessResult> Preadjustment_OffsetTestAsync(
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_AmplitudeTestAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.Preadjustment_OffsetTestAsync(slot, pp, token);
|
||||
return _innerMeterAPI.PreAdjustment_AmplitudeTestAsync(slot, token);
|
||||
}
|
||||
|
||||
public Task<PreadjustmentProcessResult> Preadjustment_CompletionAsync(
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_TemperatureCalibrationAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.Preadjustment_CompletionAsync(slot, pp, token);
|
||||
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationAsync(slot, token);
|
||||
}
|
||||
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_OffsetTestAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_OffsetTestAsync(slot, token);
|
||||
}
|
||||
|
||||
public Task<PreAdjustmentProcessResult> PreAdjustment_CompletionAsync(
|
||||
int slot,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
return _innerMeterAPI.PreAdjustment_CompletionAsync(slot, token);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -448,7 +448,7 @@ namespace GenesisCordonelInterface.API
|
||||
}
|
||||
}
|
||||
|
||||
public class PreadjustmentProcessResult
|
||||
public class PreAdjustmentProcessResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public int Slot { get; set; }
|
||||
@ -483,6 +483,22 @@ namespace GenesisCordonelInterface.API
|
||||
$"Error={ErrorMessage ?? "None"}";
|
||||
}
|
||||
}
|
||||
|
||||
public class PreAdjustmentInitializationResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Slots { get; set; }
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return
|
||||
$"Success={Success}, " +
|
||||
$"Slots={Slots}, " +
|
||||
$"Error={ErrorMessage ?? "None"}";
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
35
GenesisCordonelInterface/Config/gci_config.json
Normal file
35
GenesisCordonelInterface/Config/gci_config.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"_Comment": "GCI DataStorage configuration",
|
||||
|
||||
"DataStorage": {
|
||||
|
||||
"MeterLoginPasswords": {
|
||||
|
||||
"___Documentation___": {
|
||||
"Description": "Reads login passwords for meters by PCB ID",
|
||||
"Type": "Supported: LocalDatabase, RemoteDatabase, LocalCsv, LocalJson, RestApi",
|
||||
"DataSource": "Database connection string",
|
||||
"QueryTemplate": "QUERYPARAM is placeholder for runtime value. Example: WHERE [PcbId]=QUERYPARAM -> PCB ID provided during GetPasswordAsync()."
|
||||
},
|
||||
|
||||
"Name": "MeterLoginPasswords",
|
||||
"Type": "LocalDatabase",
|
||||
"DataSource": "Server=(localdb)\\MojaDB;Database=UnionTownCalibAndSkeleton;Integrated Security=True;",
|
||||
"QueryTemplate": "SELECT [Password] FROM [dbo].[SkeletonKeys] WHERE [PcbId] = QUERYPARAM"
|
||||
},
|
||||
|
||||
"PreAdjustmentCalibrationParams": {
|
||||
|
||||
"___Documentation___": {
|
||||
"Description": "Reads pre-adjustment values from CSV",
|
||||
"DataSource": "Relative path resolved from application directory",
|
||||
"QueryTemplate": "CSV/JSON syntax: SELECT [ReturnColumn] WHERE [MatchColumn]=QUERYPARAM or SELECT COLUMN(1) WHERE COLUMN(0)=QUERYPARAM"
|
||||
},
|
||||
|
||||
"Name": "PreAdjustmentCalibrationParams",
|
||||
"Type": "LocalCsv",
|
||||
"DataSource": "Data\\preadjustment_params.csv",
|
||||
"QueryTemplate": "SELECT [Offset] WHERE [PcbId] = QUERYPARAM"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Root GCI configuration object loaded from gci_config.json.
|
||||
///
|
||||
/// This class represents the top-level configuration structure
|
||||
/// and serves as an entry point for all configurable GCI features.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// {
|
||||
/// "DataStorage":
|
||||
/// {
|
||||
/// ...
|
||||
/// }
|
||||
/// }
|
||||
/// </summary>
|
||||
public class GciConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Data storage configuration section.
|
||||
///
|
||||
/// Contains definitions for all configured readers and writers,
|
||||
/// such as:
|
||||
///
|
||||
/// - MeterLoginPasswords
|
||||
/// - PreAdjustmentCalibrationParams
|
||||
/// - future storage providers
|
||||
/// </summary>
|
||||
public GciDataStorageConfig DataStorage { get; set; }
|
||||
}
|
||||
}
|
||||
@ -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.DataStorage.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads GCI configuration from gci_config.json.
|
||||
///
|
||||
/// Responsibilities:
|
||||
/// - Locate configuration file
|
||||
/// - Deserialize JSON into strongly typed objects
|
||||
/// - Normalize relative file paths
|
||||
/// - Preserve database connection strings and REST URLs
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// Config/
|
||||
/// gci_config.json
|
||||
///
|
||||
/// Data/
|
||||
/// meter_passwords.csv
|
||||
///
|
||||
/// Relative paths:
|
||||
/// Data\file.csv
|
||||
///
|
||||
/// become:
|
||||
///
|
||||
/// C:\App\bin\Debug\Data\file.csv
|
||||
///
|
||||
/// Database sources remain unchanged:
|
||||
///
|
||||
/// Server=(localdb)\MojaDB;Database=...
|
||||
/// </summary>
|
||||
public static class GciConfigLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads default GCI configuration from:
|
||||
///
|
||||
/// Config\gci_config.json
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Loaded GCI configuration.
|
||||
/// </returns>
|
||||
public static GciConfig LoadDefault()
|
||||
{
|
||||
string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
string configPath = Path.Combine(
|
||||
baseDirectory,
|
||||
"Config",
|
||||
"gci_config.json");
|
||||
|
||||
return Load(configPath, baseDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads GCI configuration from specified file.
|
||||
/// </summary>
|
||||
/// <param name="configPath">
|
||||
/// Path to configuration json file.
|
||||
/// </param>
|
||||
/// <param name="baseDirectory">
|
||||
/// Base directory used for resolving relative paths.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Loaded configuration object.
|
||||
/// </returns>
|
||||
public static GciConfig Load(
|
||||
string configPath,
|
||||
string baseDirectory = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(configPath))
|
||||
throw new ArgumentException(
|
||||
"Config path must not be empty.",
|
||||
nameof(configPath));
|
||||
|
||||
if (!File.Exists(configPath))
|
||||
throw new FileNotFoundException(
|
||||
"GCI config file was not found.",
|
||||
configPath);
|
||||
|
||||
string json = File.ReadAllText(configPath);
|
||||
|
||||
GciConfig config =
|
||||
JsonConvert.DeserializeObject<GciConfig>(json);
|
||||
|
||||
if (config == null)
|
||||
throw new InvalidOperationException(
|
||||
"GCI config could not be loaded.");
|
||||
|
||||
NormalizeDataStoragePaths(
|
||||
config,
|
||||
baseDirectory ?? Path.GetDirectoryName(configPath));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes paths for all configured data storage entries.
|
||||
///
|
||||
/// Converts relative file paths into absolute paths.
|
||||
/// Database connection strings remain untouched.
|
||||
/// </summary>
|
||||
/// <param name="config">
|
||||
/// Loaded configuration object.
|
||||
/// </param>
|
||||
/// <param name="baseDirectory">
|
||||
/// Base path used for relative resolution.
|
||||
/// </param>
|
||||
private static void NormalizeDataStoragePaths(
|
||||
GciConfig config,
|
||||
string baseDirectory)
|
||||
{
|
||||
if (config.DataStorage == null)
|
||||
return;
|
||||
|
||||
NormalizePath(
|
||||
config.DataStorage.MeterLoginPasswords,
|
||||
baseDirectory);
|
||||
|
||||
NormalizePath(
|
||||
config.DataStorage.PreAdjustmentCalibrationParams,
|
||||
baseDirectory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts relative file paths into absolute paths.
|
||||
///
|
||||
/// Applies only to:
|
||||
/// - LocalCsv
|
||||
/// - RemoteCsv
|
||||
/// - LocalJson
|
||||
/// - RemoteJson
|
||||
///
|
||||
/// Does not modify:
|
||||
/// - Database connection strings
|
||||
/// - REST URLs
|
||||
/// </summary>
|
||||
/// <param name="storageConfig">
|
||||
/// Storage configuration.
|
||||
/// </param>
|
||||
/// <param name="baseDirectory">
|
||||
/// Base path for resolution.
|
||||
/// </param>
|
||||
private static void NormalizePath(
|
||||
DataStorageConfig storageConfig,
|
||||
string baseDirectory)
|
||||
{
|
||||
if (storageConfig == null)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(storageConfig.DataSource))
|
||||
return;
|
||||
|
||||
// Database connection strings and URLs
|
||||
// must never be treated as file paths.
|
||||
if (storageConfig.Type == DataStorageType.LocalDatabase ||
|
||||
storageConfig.Type == DataStorageType.RemoteDatabase ||
|
||||
storageConfig.Type == DataStorageType.RestApi)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Path.IsPathRooted(storageConfig.DataSource))
|
||||
return;
|
||||
|
||||
storageConfig.DataSource =
|
||||
Path.GetFullPath(
|
||||
Path.Combine(
|
||||
baseDirectory,
|
||||
storageConfig.DataSource));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains all configured GCI data storage sources.
|
||||
///
|
||||
/// Each property represents one logical use-case
|
||||
/// and points to its storage configuration.
|
||||
///
|
||||
/// Example:
|
||||
/// MeterLoginPasswords
|
||||
/// -> SQL database
|
||||
///
|
||||
/// PreAdjustmentCalibrationParams
|
||||
/// -> CSV file
|
||||
/// </summary>
|
||||
public class GciDataStorageConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration for meter login password lookup.
|
||||
/// </summary>
|
||||
public DataStorageConfig MeterLoginPasswords { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for pre-adjustment calibration data lookup.
|
||||
/// </summary>
|
||||
public DataStorageConfig PreAdjustmentCalibrationParams { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,49 @@
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines common contract for all GCI data storage readers.
|
||||
///
|
||||
/// Implementations can read data from different storage types:
|
||||
/// - SQL database
|
||||
/// - CSV file
|
||||
/// - JSON file
|
||||
/// - REST API
|
||||
///
|
||||
/// Consumers should use this interface instead of concrete readers.
|
||||
/// </summary>
|
||||
public interface IDataStorageReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads data from configured storage using provided query.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Query object containing lookup parameters.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Storage-specific result object.
|
||||
/// </returns>
|
||||
object GetData(Models.DataQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether configured data source is accessible.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Diagnostic result of source test.
|
||||
/// </returns>
|
||||
Models.ReaderDiagnosticResult TestSource(bool enableDiagnostics);
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether configured query is valid for the data source.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Diagnostic result of query test.
|
||||
/// </returns>
|
||||
Models.ReaderDiagnosticResult TestQuery(bool enableDiagnostics);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory responsible for creating appropriate
|
||||
/// data storage reader implementations.
|
||||
///
|
||||
/// The reader type is selected according to:
|
||||
///
|
||||
/// DataStorageConfig.Type
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// LocalDatabase
|
||||
/// -> DatabaseDataStorageReader
|
||||
///
|
||||
/// LocalCsv
|
||||
/// -> CsvDataStorageReader
|
||||
///
|
||||
/// LocalJson
|
||||
/// -> JsonDataStorageReader
|
||||
///
|
||||
/// RestApi
|
||||
/// -> RestApiDataStorageReader
|
||||
///
|
||||
/// This factory hides implementation details from
|
||||
/// higher layers and keeps consumers independent
|
||||
/// of storage technology.
|
||||
///
|
||||
/// Trace:
|
||||
///
|
||||
/// MeterLoginPasswordReader
|
||||
/// -> DataStorageReaderFactory.Create()
|
||||
/// -> DatabaseDataStorageReader
|
||||
/// -> CsvDataStorageReader
|
||||
/// -> JsonDataStorageReader
|
||||
/// -> RestApiDataStorageReader
|
||||
/// </summary>
|
||||
public static class DataStorageReaderFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates appropriate reader implementation
|
||||
/// according to storage configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">
|
||||
/// Data storage configuration loaded from gci_config.json.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Configured storage reader implementation.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Configuration is null.
|
||||
/// </exception>
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// Unsupported storage type.
|
||||
/// </exception>
|
||||
public static IDataStorageReader Create(
|
||||
DataStorageConfig config)
|
||||
{
|
||||
if (config == null)
|
||||
throw new ArgumentNullException(nameof(config));
|
||||
|
||||
switch (config.Type)
|
||||
{
|
||||
case DataStorageType.RemoteDatabase:
|
||||
case DataStorageType.LocalDatabase:
|
||||
|
||||
return new Providers.DatabaseDataStorageReader(config);
|
||||
|
||||
case DataStorageType.RemoteCsv:
|
||||
case DataStorageType.LocalCsv:
|
||||
|
||||
return new Providers.CsvDataStorageReader(config);
|
||||
|
||||
case DataStorageType.RemoteJson:
|
||||
case DataStorageType.LocalJson:
|
||||
|
||||
return new Providers.JsonDataStorageReader(config);
|
||||
|
||||
case DataStorageType.RestApi:
|
||||
|
||||
return new Providers.RestApiDataStorageReader(config);
|
||||
|
||||
default:
|
||||
|
||||
throw new NotSupportedException(
|
||||
$"Unsupported DataStorageType: '{config.Type}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents query input for data storage readers.
|
||||
///
|
||||
/// Query parameters are used as lookup values
|
||||
/// during storage search operations.
|
||||
///
|
||||
/// A single parameter:
|
||||
///
|
||||
/// 231630279
|
||||
///
|
||||
/// executes one lookup.
|
||||
///
|
||||
/// Multiple parameters:
|
||||
///
|
||||
/// 231630279
|
||||
/// 231630243
|
||||
/// 231630148
|
||||
///
|
||||
/// can execute batch operations.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// QueryTemplate:
|
||||
///
|
||||
/// SELECT [Password]
|
||||
/// WHERE [PcbId]=QUERYPARAM
|
||||
///
|
||||
/// Query:
|
||||
///
|
||||
/// QueryParams:
|
||||
/// 231630279
|
||||
///
|
||||
/// Result:
|
||||
///
|
||||
/// Password belonging to 231630279.
|
||||
/// </summary>
|
||||
public class DataQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// Collection of query values.
|
||||
///
|
||||
/// Single item:
|
||||
///
|
||||
/// QueryParams[0]
|
||||
///
|
||||
/// represents one lookup.
|
||||
///
|
||||
/// Multiple items may be processed
|
||||
/// as batch requests.
|
||||
/// </summary>
|
||||
public List<string> QueryParams { get; }
|
||||
= new List<string>();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration for one storage source.
|
||||
/// Loaded from gci_config.json
|
||||
/// </summary>
|
||||
public class DataStorageConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Logical storage name.
|
||||
/// Example:
|
||||
/// MeterLoginPasswords
|
||||
/// CalibrationParameters
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Storage implementation type.
|
||||
/// </summary>
|
||||
public DataStorageType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Connection string,
|
||||
/// file path,
|
||||
/// URL etc.
|
||||
/// </summary>
|
||||
public string DataSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Query template.
|
||||
/// Example:
|
||||
///
|
||||
/// SQL:
|
||||
/// SELECT Password
|
||||
/// FROM Passwords
|
||||
/// WHERE PcbId=QUERYPARAM
|
||||
///
|
||||
/// CSV:
|
||||
/// SELECT [Password]
|
||||
/// WHERE [PcbId]=QUERYPARAM
|
||||
/// </summary>
|
||||
public string QueryTemplate { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies supported data storage implementations.
|
||||
///
|
||||
/// The type determines which reader implementation
|
||||
/// will be created by DataStorageReaderFactory.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// LocalDatabase
|
||||
/// -> DatabaseDataStorageReader
|
||||
///
|
||||
/// LocalCsv
|
||||
/// -> CsvDataStorageReader
|
||||
///
|
||||
/// RestApi
|
||||
/// -> RestApiDataStorageReader
|
||||
///
|
||||
/// Source:
|
||||
///
|
||||
/// gci_config.json
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// "Type":"LocalDatabase"
|
||||
/// </summary>
|
||||
public enum DataStorageType
|
||||
{
|
||||
/// <summary>
|
||||
/// REST API endpoint.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// https://api.company.com/passwords
|
||||
/// </summary>
|
||||
RestApi,
|
||||
|
||||
/// <summary>
|
||||
/// Database located on remote server.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// SQL server on network machine.
|
||||
/// </summary>
|
||||
RemoteDatabase,
|
||||
|
||||
/// <summary>
|
||||
/// Database located on local machine.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// SQL LocalDB
|
||||
/// SQLite
|
||||
/// local SQL Server instance
|
||||
/// </summary>
|
||||
LocalDatabase,
|
||||
|
||||
/// <summary>
|
||||
/// JSON source stored remotely.
|
||||
/// </summary>
|
||||
RemoteJson,
|
||||
|
||||
/// <summary>
|
||||
/// JSON file stored locally.
|
||||
/// </summary>
|
||||
LocalJson,
|
||||
|
||||
/// <summary>
|
||||
/// CSV source stored remotely.
|
||||
/// </summary>
|
||||
RemoteCsv,
|
||||
|
||||
/// <summary>
|
||||
/// CSV file stored locally.
|
||||
/// </summary>
|
||||
LocalCsv
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||
{
|
||||
public class DatabaseSearchResult
|
||||
{
|
||||
public bool Found { get; set; }
|
||||
|
||||
public string Query { get; set; }
|
||||
|
||||
public Dictionary<string, object> Values { get; set; }
|
||||
|
||||
public DatabaseSearchResult()
|
||||
{
|
||||
Values = new Dictionary<string, object>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents diagnostic result of data storage operations.
|
||||
///
|
||||
/// Used for:
|
||||
/// - source validation
|
||||
/// - query validation
|
||||
/// - connection tests
|
||||
/// - detailed diagnostic logging
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// TestSource()
|
||||
/// -> connection successful
|
||||
///
|
||||
/// TestQuery()
|
||||
/// -> column validation
|
||||
///
|
||||
/// Can also carry additional payload data.
|
||||
/// </summary>
|
||||
public class ReaderDiagnosticResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whether operation completed successfully.
|
||||
/// </summary>
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Human readable summary message.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// "Connection successful."
|
||||
///
|
||||
/// or:
|
||||
///
|
||||
/// "Column not found."
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Detailed diagnostic output lines.
|
||||
///
|
||||
/// Used for debugging and troubleshooting.
|
||||
/// </summary>
|
||||
public List<string> Diagnostics { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional operation payload.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// loaded CSV lines
|
||||
/// SQL test value
|
||||
/// parsed content
|
||||
/// </summary>
|
||||
public object Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates empty diagnostic result.
|
||||
/// </summary>
|
||||
public ReaderDiagnosticResult()
|
||||
{
|
||||
Diagnostics = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates successful diagnostic result.
|
||||
/// </summary>
|
||||
/// <param name="message">
|
||||
/// Success message.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Successful result object.
|
||||
/// </returns>
|
||||
public static ReaderDiagnosticResult SuccessResult(
|
||||
string message = "OK")
|
||||
{
|
||||
return new ReaderDiagnosticResult
|
||||
{
|
||||
Success = true,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates failed diagnostic result.
|
||||
/// </summary>
|
||||
/// <param name="message">
|
||||
/// Failure message.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Failed result object.
|
||||
/// </returns>
|
||||
public static ReaderDiagnosticResult Failure(
|
||||
string message)
|
||||
{
|
||||
return new ReaderDiagnosticResult
|
||||
{
|
||||
Success = false,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts diagnostic information into display text.
|
||||
///
|
||||
/// Output contains:
|
||||
///
|
||||
/// diagnostic lines
|
||||
/// +
|
||||
/// summary message
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Formatted text representation.
|
||||
/// </returns>
|
||||
public string ToDisplayDiag()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (Diagnostics != null &&
|
||||
Diagnostics.Count > 0)
|
||||
{
|
||||
foreach (string line in Diagnostics)
|
||||
sb.AppendLine(line);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Message))
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Message))
|
||||
sb.AppendLine(Message);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,394 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Searching;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads and searches CSV based data sources.
|
||||
///
|
||||
/// Supported query syntax:
|
||||
/// SELECT [ReturnColumn] WHERE [MatchColumn] = QUERYPARAM
|
||||
///
|
||||
/// Example:
|
||||
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
|
||||
///
|
||||
/// Supports:
|
||||
/// - column names: [ColumnName]
|
||||
/// - column indexes: COLUMN(n)
|
||||
///
|
||||
/// Trace:
|
||||
/// MeterLoginPasswordReader
|
||||
/// -> CsvDataStorageReader.GetData()
|
||||
/// -> ConnectToSource()
|
||||
/// -> ExecuteQuery()
|
||||
/// </summary>
|
||||
public class CsvDataStorageReader : IDataStorageReader
|
||||
{
|
||||
private readonly DataStorageConfig config;
|
||||
|
||||
private string resolvedPath;
|
||||
private string[] loadedLines;
|
||||
private string[] loadedHeaders;
|
||||
|
||||
/// <summary>
|
||||
/// Creates CSV data storage reader using provided configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">
|
||||
/// Data storage configuration loaded from gci_config.json.
|
||||
/// </param>
|
||||
public CsvDataStorageReader(DataStorageConfig config)
|
||||
{
|
||||
this.config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads values from CSV using configured QueryTemplate.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Query parameters used for lookup.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// ReaderDiagnosticResult containing lookup result.
|
||||
/// </returns>
|
||||
public object GetData(DataQuery query)
|
||||
{
|
||||
if (query == null)
|
||||
throw new ArgumentNullException(nameof(query));
|
||||
|
||||
ReaderDiagnosticResult sourceResult = ConnectToSource(true);
|
||||
if (!sourceResult.Success)
|
||||
throw new InvalidOperationException(sourceResult.Message);
|
||||
|
||||
ReaderDiagnosticResult queryResult = ExecuteQuery(query, true);
|
||||
if (!queryResult.Success)
|
||||
throw new InvalidOperationException(queryResult.Message);
|
||||
|
||||
return queryResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates CSV source accessibility and content.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Diagnostic result.
|
||||
/// </returns>
|
||||
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
|
||||
{
|
||||
return ConnectToSource(enableDiagnostics);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates configured QueryTemplate against CSV header.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Diagnostic result.
|
||||
/// </returns>
|
||||
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
|
||||
{
|
||||
ReaderDiagnosticResult sourceResult = ConnectToSource(enableDiagnostics);
|
||||
if (!sourceResult.Success)
|
||||
return sourceResult;
|
||||
|
||||
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
|
||||
|
||||
try
|
||||
{
|
||||
Log(result, enableDiagnostics, "Starting QueryTemplate validation.");
|
||||
|
||||
SearchOrderDefinition definition =
|
||||
SearchOrderParser.Parse(config.QueryTemplate);
|
||||
|
||||
int selectIndex = ResolveColumnIndex(
|
||||
loadedHeaders,
|
||||
definition.SelectColumn);
|
||||
|
||||
int whereIndex = ResolveColumnIndex(
|
||||
loadedHeaders,
|
||||
definition.WhereColumn);
|
||||
|
||||
Log(result, enableDiagnostics, "QueryTemplate parsed successfully.");
|
||||
Log(result, enableDiagnostics, "Select column index: " + selectIndex);
|
||||
Log(result, enableDiagnostics, "Where column index: " + whereIndex);
|
||||
|
||||
result.Success = true;
|
||||
result.Message = "QueryTemplate validation finished successfully.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = ex.Message;
|
||||
Log(result, enableDiagnostics, "ERROR: " + ex.Message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens CSV file and loads its content.
|
||||
/// Validates file existence and header.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Source validation result.
|
||||
/// </returns>
|
||||
private ReaderDiagnosticResult ConnectToSource(bool enableDiagnostics)
|
||||
{
|
||||
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
|
||||
|
||||
try
|
||||
{
|
||||
Log(result, enableDiagnostics, "Starting CSV source connection test.");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(config.DataSource))
|
||||
throw new InvalidOperationException("CSV data source is empty.");
|
||||
|
||||
resolvedPath = Path.GetFullPath(config.DataSource);
|
||||
|
||||
Log(result, enableDiagnostics, "Resolved full path: " + resolvedPath);
|
||||
|
||||
if (!File.Exists(resolvedPath))
|
||||
throw new FileNotFoundException("CSV file was not found.", resolvedPath);
|
||||
|
||||
loadedLines = File.ReadAllLines(resolvedPath);
|
||||
|
||||
if (loadedLines == null || loadedLines.Length == 0)
|
||||
throw new InvalidOperationException("CSV file is empty.");
|
||||
|
||||
loadedHeaders = SplitCsvLine(loadedLines[0]);
|
||||
|
||||
if (loadedHeaders == null || loadedHeaders.Length == 0)
|
||||
throw new InvalidOperationException("CSV header is empty.");
|
||||
|
||||
Log(result, enableDiagnostics, "CSV line count: " + loadedLines.Length);
|
||||
Log(result, enableDiagnostics, "CSV header column count: " + loadedHeaders.Length);
|
||||
|
||||
result.Success = true;
|
||||
result.Message = "CSV source connection finished successfully.";
|
||||
result.Data = loadedLines;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = ex.Message;
|
||||
result.Data = null;
|
||||
|
||||
Log(result, enableDiagnostics, "ERROR: " + ex.Message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Executes configured QueryTemplate against loaded CSV data.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Query values used for matching.
|
||||
/// </param>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Query result.
|
||||
/// </returns>
|
||||
private ReaderDiagnosticResult ExecuteQuery(
|
||||
DataQuery query,
|
||||
bool enableDiagnostics)
|
||||
{
|
||||
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
|
||||
|
||||
try
|
||||
{
|
||||
if (query.QueryParams == null || query.QueryParams.Count == 0)
|
||||
throw new InvalidOperationException("DataQuery.QueryParams is empty.");
|
||||
|
||||
SearchOrderDefinition definition =
|
||||
SearchOrderParser.Parse(config.QueryTemplate);
|
||||
|
||||
int selectIndex = ResolveColumnIndex(
|
||||
loadedHeaders,
|
||||
definition.SelectColumn);
|
||||
|
||||
int whereIndex = ResolveColumnIndex(
|
||||
loadedHeaders,
|
||||
definition.WhereColumn);
|
||||
|
||||
List<string> matchedValues = new List<string>();
|
||||
|
||||
foreach (string param in query.QueryParams)
|
||||
{
|
||||
string queryValue = (param ?? string.Empty).Trim();
|
||||
bool found = false;
|
||||
|
||||
for (int lineIndex = 1; lineIndex < loadedLines.Length; lineIndex++)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(loadedLines[lineIndex]))
|
||||
continue;
|
||||
|
||||
string[] values = SplitCsvLine(loadedLines[lineIndex]);
|
||||
|
||||
if (whereIndex >= values.Length)
|
||||
continue;
|
||||
|
||||
string currentValue = (values[whereIndex] ?? string.Empty).Trim();
|
||||
|
||||
if (!string.Equals(
|
||||
currentValue,
|
||||
queryValue,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string returnValue =
|
||||
selectIndex < values.Length
|
||||
? values[selectIndex]
|
||||
: string.Empty;
|
||||
|
||||
matchedValues.Add(returnValue);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
Log(result, enableDiagnostics, "No match found for: " + queryValue);
|
||||
}
|
||||
|
||||
result.Success = true;
|
||||
|
||||
if (query.QueryParams.Count == 1)
|
||||
{
|
||||
result.Data = matchedValues.Count > 0 ? matchedValues[0] : null;
|
||||
result.Message = matchedValues.Count > 0
|
||||
? "Value found."
|
||||
: "No match found.";
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Data = matchedValues;
|
||||
result.Message =
|
||||
"Batch query finished. Matches found: " +
|
||||
matchedValues.Count +
|
||||
" of " +
|
||||
query.QueryParams.Count +
|
||||
".";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = ex.Message;
|
||||
result.Data = null;
|
||||
|
||||
Log(result, enableDiagnostics, "ERROR: " + ex.Message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves column index either by name or by explicit index.
|
||||
/// </summary>
|
||||
/// <param name="headers">
|
||||
/// CSV header columns.
|
||||
/// </param>
|
||||
/// <param name="columnReference">
|
||||
/// Parsed QueryTemplate column definition.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Zero-based column index.
|
||||
/// </returns>
|
||||
private int ResolveColumnIndex(
|
||||
string[] headers,
|
||||
ColumnReference columnReference)
|
||||
{
|
||||
if (columnReference == null)
|
||||
throw new InvalidOperationException("Column reference is null.");
|
||||
|
||||
if (columnReference.HasIndex)
|
||||
{
|
||||
int index = columnReference.Index.Value;
|
||||
|
||||
if (index < 0 || index >= headers.Length)
|
||||
throw new InvalidOperationException("Column index is out of range.");
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
if (columnReference.HasName)
|
||||
{
|
||||
for (int i = 0; i < headers.Length; i++)
|
||||
{
|
||||
if (string.Equals(
|
||||
headers[i]?.Trim(),
|
||||
columnReference.Name?.Trim(),
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
"Column '" + columnReference.Name + "' was not found in CSV header.");
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Column reference is not defined.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits CSV line using common separators.
|
||||
/// Supports:
|
||||
/// ';'
|
||||
/// ','
|
||||
/// '\t'
|
||||
/// </summary>
|
||||
/// <param name="line">
|
||||
/// Input CSV line.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Parsed columns.
|
||||
/// </returns>
|
||||
private string[] SplitCsvLine(string line)
|
||||
{
|
||||
if (string.IsNullOrEmpty(line))
|
||||
return new string[0];
|
||||
|
||||
if (line.Contains(";"))
|
||||
return line.Split(';');
|
||||
|
||||
if (line.Contains(","))
|
||||
return line.Split(',');
|
||||
|
||||
if (line.Contains("\t"))
|
||||
return line.Split('\t');
|
||||
|
||||
return new[] { line };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds diagnostic line when diagnostics are enabled.
|
||||
/// </summary>
|
||||
private void Log(
|
||||
ReaderDiagnosticResult result,
|
||||
bool enableDiagnostics,
|
||||
string message)
|
||||
{
|
||||
if (!enableDiagnostics || result == null)
|
||||
return;
|
||||
|
||||
result.Diagnostics.Add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,273 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads data from SQL Server based data storage.
|
||||
///
|
||||
/// The reader uses configuration loaded from gci_config.json.
|
||||
///
|
||||
/// QueryTemplate must contain QUERYPARAM placeholder.
|
||||
///
|
||||
/// Example:
|
||||
/// SELECT [Password] FROM [dbo].[SkeletonKeys] WHERE [PcbId] = QUERYPARAM
|
||||
///
|
||||
/// The placeholder is internally converted to SQL parameter @value.
|
||||
/// </summary>
|
||||
public class DatabaseDataStorageReader : IDataStorageReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Data storage configuration containing connection string and query template.
|
||||
/// </summary>
|
||||
private readonly DataStorageConfig config;
|
||||
|
||||
/// <summary>
|
||||
/// Creates SQL Server data storage reader using provided configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">
|
||||
/// Data storage configuration loaded from gci_config.json.
|
||||
/// </param>
|
||||
public DatabaseDataStorageReader(DataStorageConfig config)
|
||||
{
|
||||
this.config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes configured SQL query and returns first matching row.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Query object containing lookup parameter.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// DatabaseSearchResult containing returned SQL columns and values.
|
||||
/// </returns>
|
||||
public object GetData(DataQuery query)
|
||||
{
|
||||
if (query == null)
|
||||
throw new ArgumentNullException(nameof(query));
|
||||
|
||||
ReaderDiagnosticResult sourceResult = TestSource(true);
|
||||
if (!sourceResult.Success)
|
||||
throw new InvalidOperationException(sourceResult.Message);
|
||||
|
||||
string sqlText = PrepareSqlText(config.QueryTemplate);
|
||||
object queryValue = ExtractQueryValue(query);
|
||||
|
||||
using (SqlConnection connection = new SqlConnection(config.DataSource))
|
||||
using (SqlCommand command = new SqlCommand(sqlText, connection))
|
||||
{
|
||||
AddQueryParameter(command, queryValue);
|
||||
|
||||
connection.Open();
|
||||
|
||||
using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SingleRow))
|
||||
{
|
||||
DatabaseSearchResult result = new DatabaseSearchResult
|
||||
{
|
||||
Query = sqlText
|
||||
};
|
||||
|
||||
if (!reader.Read())
|
||||
{
|
||||
result.Found = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.Found = true;
|
||||
|
||||
for (int i = 0; i < reader.FieldCount; i++)
|
||||
{
|
||||
object value = reader.GetValue(i);
|
||||
result.Values[reader.GetName(i)] =
|
||||
value == DBNull.Value ? null : value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether SQL Server connection can be opened.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Diagnostic result of SQL connection test.
|
||||
/// </returns>
|
||||
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
|
||||
{
|
||||
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
|
||||
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(config.DataSource))
|
||||
throw new InvalidOperationException("Data source is empty.");
|
||||
|
||||
Log(result, enableDiagnostics, "Opening SQL connection.");
|
||||
|
||||
using (SqlConnection connection = new SqlConnection(config.DataSource))
|
||||
{
|
||||
connection.Open();
|
||||
|
||||
Log(result, enableDiagnostics, "Connection opened successfully.");
|
||||
|
||||
using (SqlCommand command = new SqlCommand("SELECT 1", connection))
|
||||
{
|
||||
object value = command.ExecuteScalar();
|
||||
Log(result, enableDiagnostics, "Test query result: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
result.Success = true;
|
||||
result.Message = "Connection to SQL Server OK.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = "Failed to connect to SQL Server. " + ex.Message;
|
||||
Log(result, enableDiagnostics, ex.ToString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether configured SQL query can be prepared and executed.
|
||||
/// </summary>
|
||||
/// <param name="enableDiagnostics">
|
||||
/// Enables detailed diagnostic output.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Diagnostic result of query execution test.
|
||||
/// </returns>
|
||||
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
|
||||
{
|
||||
ReaderDiagnosticResult result = new ReaderDiagnosticResult();
|
||||
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(config.QueryTemplate))
|
||||
throw new InvalidOperationException("Query template is empty.");
|
||||
|
||||
string sqlText = PrepareSqlText(config.QueryTemplate);
|
||||
|
||||
Log(result, enableDiagnostics, "Original template:");
|
||||
Log(result, enableDiagnostics, config.QueryTemplate);
|
||||
|
||||
Log(result, enableDiagnostics, "Prepared SQL:");
|
||||
Log(result, enableDiagnostics, sqlText);
|
||||
|
||||
using (SqlConnection connection = new SqlConnection(config.DataSource))
|
||||
using (SqlCommand command = new SqlCommand(sqlText, connection))
|
||||
{
|
||||
AddQueryParameter(command, "TEST");
|
||||
|
||||
connection.Open();
|
||||
object value = command.ExecuteScalar();
|
||||
|
||||
result.Data = value;
|
||||
|
||||
Log(result, enableDiagnostics, "Query executed successfully.");
|
||||
}
|
||||
|
||||
result.Success = true;
|
||||
result.Message = "Query executed successfully.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = "Query execution failed. " + ex.Message;
|
||||
Log(result, enableDiagnostics, ex.ToString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts configured QueryTemplate to executable SQL text.
|
||||
/// </summary>
|
||||
/// <param name="queryTemplate">
|
||||
/// SQL query template containing QUERYPARAM placeholder.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// SQL text with QUERYPARAM replaced by @value parameter.
|
||||
/// </returns>
|
||||
private static string PrepareSqlText(string queryTemplate)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(queryTemplate))
|
||||
throw new ArgumentException(
|
||||
"Query template must not be empty.",
|
||||
nameof(queryTemplate));
|
||||
|
||||
if (!queryTemplate.Contains("QUERYPARAM"))
|
||||
throw new InvalidOperationException(
|
||||
"Query template must contain QUERYPARAM placeholder.");
|
||||
|
||||
return queryTemplate.Replace("QUERYPARAM", "@value");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts first query parameter value.
|
||||
/// </summary>
|
||||
/// <param name="query">
|
||||
/// Data query containing query parameters.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// First query parameter value.
|
||||
/// </returns>
|
||||
private static object ExtractQueryValue(DataQuery query)
|
||||
{
|
||||
if (query.QueryParams == null || query.QueryParams.Count == 0)
|
||||
throw new InvalidOperationException(
|
||||
"DataQuery does not contain any query parameter.");
|
||||
|
||||
return query.QueryParams[0];
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
command.Parameters.Clear();
|
||||
|
||||
SqlParameter parameter = command.Parameters.Add("@value", SqlDbType.Variant);
|
||||
parameter.Value = value ?? DBNull.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds diagnostic line when diagnostics are enabled.
|
||||
/// </summary>
|
||||
/// <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,
|
||||
string message)
|
||||
{
|
||||
if (!enableDiagnostics || result == null)
|
||||
return;
|
||||
|
||||
result.Diagnostics.Add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
|
||||
{
|
||||
public class JsonDataStorageReader : IDataStorageReader
|
||||
{
|
||||
private readonly DataStorageConfig config;
|
||||
|
||||
public JsonDataStorageReader(DataStorageConfig config)
|
||||
{
|
||||
this.config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
}
|
||||
|
||||
public object GetData(DataQuery query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Providers
|
||||
{
|
||||
public class RestApiDataStorageReader : IDataStorageReader
|
||||
{
|
||||
private readonly DataStorageConfig config;
|
||||
|
||||
public RestApiDataStorageReader(DataStorageConfig config)
|
||||
{
|
||||
this.config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
}
|
||||
|
||||
public object GetData(DataQuery query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ReaderDiagnosticResult TestSource(bool enableDiagnostics)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ReaderDiagnosticResult TestQuery(bool enableDiagnostics)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
using System;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Searching
|
||||
{
|
||||
/// <summary>
|
||||
/// Parsed representation of QueryTemplate expression.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
|
||||
///
|
||||
/// becomes:
|
||||
///
|
||||
/// SelectColumn:
|
||||
/// [Password]
|
||||
///
|
||||
/// WhereColumn:
|
||||
/// [PcbId]
|
||||
/// </summary>
|
||||
public class SearchOrderDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Column to be returned from lookup result.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// SELECT [Password]
|
||||
///
|
||||
/// returns:
|
||||
///
|
||||
/// [Password]
|
||||
/// </summary>
|
||||
public ColumnReference SelectColumn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Column used for row matching.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// WHERE [PcbId] = QUERYPARAM
|
||||
///
|
||||
/// uses:
|
||||
///
|
||||
/// [PcbId]
|
||||
/// </summary>
|
||||
public ColumnReference WhereColumn { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents one column definition inside QueryTemplate.
|
||||
///
|
||||
/// Supports two forms:
|
||||
///
|
||||
/// [ColumnName]
|
||||
///
|
||||
/// or:
|
||||
///
|
||||
/// COLUMN(number)
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// [Password]
|
||||
///
|
||||
/// COLUMN(2)
|
||||
/// </summary>
|
||||
public class ColumnReference
|
||||
{
|
||||
/// <summary>
|
||||
/// Column name used in named lookup mode.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// [Password]
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Zero-based column index used in indexed lookup mode.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// COLUMN(2)
|
||||
/// </summary>
|
||||
public int? Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether column uses name-based lookup.
|
||||
/// </summary>
|
||||
public bool HasName
|
||||
{
|
||||
get
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(Name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether column uses index-based lookup.
|
||||
/// </summary>
|
||||
public bool HasIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return Index.HasValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns human readable representation.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Column expression text.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (HasName)
|
||||
return "[" + Name + "]";
|
||||
|
||||
if (HasIndex)
|
||||
return "COLUMN(" + Index.Value + ")";
|
||||
|
||||
return "<undefined column reference>";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Common.Searching
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses SQL-like QueryTemplate expressions used by GCI data storage readers.
|
||||
///
|
||||
/// Supported syntax:
|
||||
///
|
||||
/// SELECT [ReturnColumn] WHERE [MatchColumn] = QUERYPARAM
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
|
||||
///
|
||||
/// SELECT COLUMN(1) WHERE COLUMN(0) = QUERYPARAM
|
||||
///
|
||||
/// Mixed forms are also supported:
|
||||
///
|
||||
/// SELECT [Password] WHERE COLUMN(0)=QUERYPARAM
|
||||
///
|
||||
/// The parser converts text expressions into structured
|
||||
/// SearchOrderDefinition objects which are later consumed
|
||||
/// by CSV and other readers.
|
||||
/// </summary>
|
||||
public static class SearchOrderParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Full QueryTemplate validation pattern.
|
||||
///
|
||||
/// Expected syntax:
|
||||
///
|
||||
/// SELECT [Column] WHERE [Column] = QUERYPARAM
|
||||
/// </summary>
|
||||
private static readonly Regex FullPattern = new Regex(
|
||||
@"^\s*SELECT\s+(?<select>\[[^\]]+\]|COLUMN\(\d+\))\s+WHERE\s+(?<where>\[[^\]]+\]|COLUMN\(\d+\))\s*=\s*QUERYPARAM\s*$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Matches:
|
||||
///
|
||||
/// [ColumnName]
|
||||
/// </summary>
|
||||
private static readonly Regex NamedColumnPattern = new Regex(
|
||||
@"^\[(?<name>[^\]]+)\]$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Matches:
|
||||
///
|
||||
/// COLUMN(number)
|
||||
/// </summary>
|
||||
private static readonly Regex IndexedColumnPattern = new Regex(
|
||||
@"^COLUMN\((?<index>\d+)\)$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Parses QueryTemplate text into structured definition.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// SELECT [Password] WHERE [PcbId] = QUERYPARAM
|
||||
///
|
||||
/// Result:
|
||||
///
|
||||
/// SelectColumn:
|
||||
/// Password
|
||||
///
|
||||
/// WhereColumn:
|
||||
/// PcbId
|
||||
/// </summary>
|
||||
/// <param name="searchOrder">
|
||||
/// QueryTemplate expression.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Parsed query definition.
|
||||
/// </returns>
|
||||
public static SearchOrderDefinition Parse(string searchOrder)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(searchOrder))
|
||||
throw new InvalidOperationException(
|
||||
"QueryTemplate is empty.");
|
||||
|
||||
Match match = FullPattern.Match(searchOrder);
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Invalid QueryTemplate syntax. Expected: SELECT [ReturnColumn] WHERE [MatchColumn] = QUERYPARAM");
|
||||
}
|
||||
|
||||
return new SearchOrderDefinition
|
||||
{
|
||||
SelectColumn =
|
||||
ParseColumnReference(
|
||||
match.Groups["select"].Value),
|
||||
|
||||
WhereColumn =
|
||||
ParseColumnReference(
|
||||
match.Groups["where"].Value)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses single column definition.
|
||||
///
|
||||
/// Supported forms:
|
||||
///
|
||||
/// [ColumnName]
|
||||
///
|
||||
/// COLUMN(number)
|
||||
/// </summary>
|
||||
/// <param name="token">
|
||||
/// Raw token extracted from QueryTemplate.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Parsed column reference.
|
||||
/// </returns>
|
||||
private static ColumnReference ParseColumnReference(
|
||||
string token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(token))
|
||||
throw new InvalidOperationException(
|
||||
"Column reference token is empty.");
|
||||
|
||||
Match nameMatch =
|
||||
NamedColumnPattern.Match(token);
|
||||
|
||||
if (nameMatch.Success)
|
||||
{
|
||||
return new ColumnReference
|
||||
{
|
||||
Name =
|
||||
nameMatch
|
||||
.Groups["name"]
|
||||
.Value
|
||||
.Trim(),
|
||||
|
||||
Index = null
|
||||
};
|
||||
}
|
||||
|
||||
Match indexMatch =
|
||||
IndexedColumnPattern.Match(token);
|
||||
|
||||
if (indexMatch.Success)
|
||||
{
|
||||
return new ColumnReference
|
||||
{
|
||||
Name = null,
|
||||
|
||||
Index =
|
||||
int.Parse(
|
||||
indexMatch
|
||||
.Groups["index"]
|
||||
.Value)
|
||||
};
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
"Invalid column reference '" +
|
||||
token +
|
||||
"'. Use [ColumnName] or COLUMN(number).");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines contract for reading meter login passwords
|
||||
/// from configured GCI data storage.
|
||||
///
|
||||
/// Implementations hide storage details and provide
|
||||
/// a unified API for password lookup.
|
||||
///
|
||||
/// Supported storage types:
|
||||
/// - SQL database
|
||||
/// - CSV
|
||||
/// - JSON
|
||||
/// - REST API
|
||||
/// </summary>
|
||||
public interface IMeterLoginPasswordReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads meter login password synchronously.
|
||||
/// </summary>
|
||||
/// <param name="queryParam">
|
||||
/// PCB ID or another configured lookup value.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Password if found; otherwise null.
|
||||
/// </returns>
|
||||
string GetPassword(string queryParam);
|
||||
|
||||
/// <summary>
|
||||
/// Reads meter login password asynchronously.
|
||||
/// Thread-safe implementation may serialize
|
||||
/// access to the underlying storage.
|
||||
/// </summary>
|
||||
/// <param name="queryParam">
|
||||
/// PCB ID or another configured lookup value.
|
||||
/// </param>
|
||||
/// <param name="token">
|
||||
/// Cancellation token.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Password if found; otherwise null.
|
||||
/// </returns>
|
||||
Task<string> GetPasswordAsync(
|
||||
string queryParam,
|
||||
CancellationToken token = default);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Contracts;
|
||||
using GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// Reads meter login password from configured GCI data storage.
|
||||
/// </summary>
|
||||
public class MeterLoginPasswordReader : IMeterLoginPasswordReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Universal data storage reader selected by configuration.
|
||||
/// Can represent database, CSV, JSON or REST reader.
|
||||
/// </summary>
|
||||
private readonly IDataStorageReader reader;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that only one password lookup is executed at a time.
|
||||
/// </summary>
|
||||
private readonly SemaphoreSlim readLock = new SemaphoreSlim(1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Creates password reader using provided data storage configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">Data storage configuration loaded from gci_config.json.</param>
|
||||
public MeterLoginPasswordReader(DataStorageConfig config)
|
||||
{
|
||||
reader = DataStorageReaderFactory.Create(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously reads meter login password 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));
|
||||
|
||||
await readLock.WaitAsync(token);
|
||||
|
||||
try
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
return result?.ToString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to pre-adjustment calibration parameters
|
||||
/// stored in the configured data source.
|
||||
///
|
||||
/// Expected structure:
|
||||
/// Dn_InternalId | ParameterName | ParameterValue
|
||||
///
|
||||
/// Example:
|
||||
/// 3 | GENESISFLOW_MinValidToF | 21990232
|
||||
/// 3 | GENESISFLOW_Timeout | 1
|
||||
///
|
||||
/// Parameters are grouped by Dn_InternalId and returned
|
||||
/// as key/value pairs where:
|
||||
///
|
||||
/// Key = ParameterName
|
||||
/// Value = ParameterValue
|
||||
/// </summary>
|
||||
internal interface IPreAdjustmentCalibrationParamsReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads all calibration parameters assigned
|
||||
/// to a specific DN identifier.
|
||||
/// </summary>
|
||||
/// <param name="dnInternalId">
|
||||
/// Internal DN identifier (meter size).
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Dictionary:
|
||||
///
|
||||
/// Key:
|
||||
/// GENESISFLOW parameter name
|
||||
///
|
||||
/// Value:
|
||||
/// Stored parameter value
|
||||
/// </returns>
|
||||
Task<Dictionary<string, string>> ReadParamsAsync(int dnInternalId);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads pre-adjustment calibration parameters
|
||||
/// from SQL database storage.
|
||||
///
|
||||
/// Expected table:
|
||||
///
|
||||
/// dbo.PreAdjustmentCalibrationParams
|
||||
///
|
||||
/// Columns:
|
||||
/// Dn_InternalId
|
||||
/// ParameterName
|
||||
/// ParameterValue
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// 3 | GENESISFLOW_MinValidToF | 21990232
|
||||
/// 3 | GENESISFLOW_Timeout | 1
|
||||
///
|
||||
/// Returns values as dictionary:
|
||||
///
|
||||
/// GENESISFLOW_MinValidToF -> 21990232
|
||||
/// GENESISFLOW_Timeout -> 1
|
||||
/// </summary>
|
||||
internal class PreAdjustmentCalibrationParamsReader: IPreAdjustmentCalibrationParamsReader
|
||||
{
|
||||
private readonly string _connectionString;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes calibration parameter reader.
|
||||
/// </summary>
|
||||
/// <param name="connectionString">
|
||||
/// SQL database connection string.
|
||||
/// </param>
|
||||
public PreAdjustmentCalibrationParamsReader(
|
||||
string connectionString)
|
||||
{
|
||||
_connectionString = connectionString?? throw new ArgumentNullException(nameof(connectionString));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<Dictionary<string, string>> ReadParamsAsync(int dnInternalId)
|
||||
{
|
||||
var result = new Dictionary<string, string>();
|
||||
|
||||
const string query = @"
|
||||
SELECT ParameterName, ParameterValue
|
||||
FROM dbo.PreAdjustmentCalibrationParams
|
||||
WHERE Dn_InternalId=@Dn_InternalId";
|
||||
|
||||
using (var connection = new SqlConnection(_connectionString))
|
||||
|
||||
using (var command = new SqlCommand(query, connection))
|
||||
{
|
||||
command.Parameters.AddWithValue(
|
||||
"@Dn_InternalId",
|
||||
dnInternalId);
|
||||
|
||||
await connection.OpenAsync();
|
||||
|
||||
using (var reader =
|
||||
await command.ExecuteReaderAsync())
|
||||
{
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
var parameterName = reader["ParameterName"].ToString();
|
||||
|
||||
var parameterValue = reader["ParameterValue"].ToString();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(parameterName))
|
||||
{
|
||||
result[parameterName] = parameterValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -60,6 +60,26 @@
|
||||
<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\DataStorage\Config\GciDataStorageConfig.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" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Models\DataQuery.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Models\DataStorageConfig.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\DataStorageReaderFactory.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Models\DataStorageType.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Contracts\IDataStorageReader..cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Providers\JsonDataStorageReader.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Models\ReaderDiagnosticResult.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Providers\RestApiDataStorageReader.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Searching\SearchOrderDefinition.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Common\Searching\SearchOrderParser.cs" />
|
||||
<Compile Include="Core\DataStorage\Reading\Implementation\MeterLoginPasswords\IMeterLoginPasswordReader.cs" />
|
||||
<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\Logging\UiLogBus.cs" />
|
||||
<Compile Include="Core\Logging\UiTarget.cs" />
|
||||
<Compile Include="Core\Threading\ApiWorker\ApiWorker.cs" />
|
||||
@ -142,6 +162,15 @@
|
||||
<Compile Include="UI\StaraTuraAPI_GenesisCordonelInterface\MetersActionView.Designer.cs">
|
||||
<DependentUpon>MetersActionView.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Content Include="docs\images\GciBridge_component_GUI.png" />
|
||||
<Content Include="docs\images\GCI__Onboarding_Overview_drawio.svg" />
|
||||
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__API_architecture__Current_state.svg" />
|
||||
<Content Include="docs\images\GCI__Onboarding_Overview_drawio__API_architecture__Target_state.svg" />
|
||||
<Content Include="docs\images\logo\logo.png" />
|
||||
<Content Include="docs\images\logo\logo.svg" />
|
||||
<Content Include="docs\images\logo\sensus-logo-white-green-rgb.png" />
|
||||
<Content Include="docs\images\logo\sensus-logo-white-green-rgb.svg" />
|
||||
<Content Include="docs\styles\main.css" />
|
||||
<Content Include="RuntimePackage\Build\Copy.targets.xml" />
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
@ -169,6 +198,31 @@
|
||||
<EmbeddedResource Include="UI\StaraTuraAPI_GenesisCordonelInterface\FrmGCIAPI.resx">
|
||||
<DependentUpon>FrmGCIAPI.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<Content Include="Config\gci_config.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="docs\articles\API\index.md" />
|
||||
<None Include="docs\articles\API\PublicModels.md" />
|
||||
<None Include="docs\articles\API\InterfaceGCIToLaatzen.md" />
|
||||
<None Include="docs\articles\API\InterfaceOutsideToGCI.md" />
|
||||
<None Include="docs\docfx.json" />
|
||||
<None Include="docs\index.md" />
|
||||
<None Include="docs\pages\development\page_dev__current_state.md" />
|
||||
<None Include="docs\pages\development\page_dev__home.md" />
|
||||
<None Include="docs\pages\development\page_dev__target_state.md" />
|
||||
<None Include="docs\pages\development\page_dev__migration.md" />
|
||||
<None Include="docs\pages\development\page_dev__refactoring.md" />
|
||||
<None Include="docs\pages\development\page_dev__target_architecture.md" />
|
||||
<None Include="docs\pages\gci\page_gci__app_environment.md" />
|
||||
<None Include="docs\pages\gci\page_gci__internal_architecture.md" />
|
||||
<None Include="docs\pages\gci\page_gci__datastorage.md" />
|
||||
<None Include="docs\pages\gci\page_gci__home.md" />
|
||||
<None Include="docs\pages\gci\page_gci__interfaces.md" />
|
||||
<None Include="docs\pages\gci\page_gci__runtime.md" />
|
||||
<None Include="docs\pages\gci\page_gci__implementation_to_tbf.md" />
|
||||
<None Include="docs\pages\gci\page_gci__implementation_universal.md" />
|
||||
<None Include="docs\pages\gci\page_gci__workers.md" />
|
||||
<None Include="docs\toc.yml" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
@ -184,6 +238,7 @@
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Core\DataStorage\Writing\NewFolder1\" />
|
||||
<Folder Include="RuntimePackage\Package\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
1
GenesisCordonelInterface/UI/MainView.Designer.cs
generated
1
GenesisCordonelInterface/UI/MainView.Designer.cs
generated
@ -188,7 +188,6 @@
|
||||
this.btnMeterInit.Size = new System.Drawing.Size(150, 30);
|
||||
this.btnMeterInit.Text = "Meter Init";
|
||||
this.btnMeterInit.UseVisualStyleBackColor = true;
|
||||
this.btnMeterInit.Click += new System.EventHandler(this.btnMeterInit_Click);
|
||||
|
||||
// btnMetersAction
|
||||
this.btnMetersAction.Location = new System.Drawing.Point(6, 58);
|
||||
|
||||
@ -192,13 +192,6 @@ namespace GenesisCordonelInterface.UI
|
||||
Logger.Trace($"FORM: GCI VIEW -> {name} LOADED");
|
||||
}
|
||||
|
||||
private void btnMeterInit_Click(object sender, EventArgs e)
|
||||
{
|
||||
SwitchGciView(
|
||||
"MeterInit",
|
||||
new MeterInitView(_api, AddSlotRow, SaveSlots));
|
||||
}
|
||||
|
||||
private void btnMetersAction_Click(object sender, EventArgs e)
|
||||
{
|
||||
SwitchGciView(
|
||||
@ -329,11 +322,5 @@ namespace GenesisCordonelInterface.UI
|
||||
view.Dock = DockStyle.Fill;
|
||||
pnlGciViewHost.Controls.Add(view);
|
||||
}
|
||||
|
||||
public void SaveSlots()
|
||||
{
|
||||
var data = _batchPanel.GetGridData();
|
||||
_api.SaveSlotSetup(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -26,7 +26,6 @@
|
||||
|
||||
this.btnReloadSetup.SetBounds(20, 20, 140, 32);
|
||||
this.btnReloadSetup.Text = "Reload Setup";
|
||||
this.btnReloadSetup.Click += new System.EventHandler(this.btnReloadSetup_Click);
|
||||
|
||||
this.btnSaveSetup.SetBounds(170, 20, 140, 32);
|
||||
this.btnSaveSetup.Text = "Save Setup";
|
||||
|
||||
@ -22,10 +22,6 @@ namespace GenesisCordonelInterface.UI.StaraTuraAPI_GenesisCordonelInterface
|
||||
#region BUTTONS
|
||||
// ----------------------------------------------------
|
||||
|
||||
private void btnReloadSetup_Click(object sender, EventArgs e)
|
||||
{
|
||||
ExecuteApiAction(() => _api.ReloadSlotSetup());
|
||||
}
|
||||
|
||||
private void btnSaveSetup_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
@ -348,7 +348,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
timeoutMs: 30000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates an already initialized GCI slot.
|
||||
///
|
||||
@ -1273,6 +1273,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads password from UniDataStorageReader using PCB ID with retry support.
|
||||
///
|
||||
@ -1424,12 +1426,58 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
|
||||
// Preadjustment API:
|
||||
|
||||
#region ================================== Preadjustment INIT bridge ==================================
|
||||
|
||||
public PreAdjustmentInitializationResult Preadjustment_Initialization(
|
||||
ProcessProgress pp, List<MeterStateControl> mc)
|
||||
{
|
||||
const string operation = nameof(Preadjustment_Initialization);
|
||||
|
||||
try
|
||||
{
|
||||
EnsureExternalInterface();
|
||||
|
||||
log.InfoFormat(
|
||||
"{0}: {1} Start.",
|
||||
Name,
|
||||
operation);
|
||||
|
||||
PreAdjustmentInitializationResult result = gciExternalInterface.Preadjustment_Initialization(pp, mc);
|
||||
|
||||
log.InfoFormat(
|
||||
"{0}: {1} Finish. {2}",
|
||||
Name,
|
||||
operation,
|
||||
result);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error(
|
||||
string.Format(
|
||||
"{0}: {1} failed.",
|
||||
Name,
|
||||
operation),
|
||||
ex);
|
||||
|
||||
return new PreAdjustmentInitializationResult
|
||||
{
|
||||
Success = false,
|
||||
ErrorMessage = ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================================== Preadjustment DETECT bridge ==================================
|
||||
|
||||
public async Task<PreadjustmentDetectResult> Preadjustment_DetectAsync(
|
||||
public async Task<PreadjustmentDetectResult> PreAdjustment_DetectAsync(
|
||||
IEnumerable<GciPublicModels.MeterBatchDebugStatus> selectedSlots,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
const string operation = nameof(Preadjustment_DetectAsync);
|
||||
const string operation = nameof(PreAdjustment_DetectAsync);
|
||||
|
||||
try
|
||||
{
|
||||
@ -1439,7 +1487,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
|
||||
PreadjustmentDetectResult result =
|
||||
await gciExternalInterface
|
||||
.Preadjustment_DetectAsync(token)
|
||||
.PreAdjustment_DetectAsync(selectedSlots, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
log.InfoFormat(
|
||||
@ -1466,34 +1514,28 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region ================================== PreAdjustment PREPARATION bridge ==================================
|
||||
|
||||
public async Task<PreadjustmentProcessResult> Preadjustment_PreparationAsync(
|
||||
public async Task<PreAdjustmentProcessResult> PreAdjustment_PreparationAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
const string operation = nameof(Preadjustment_PreparationAsync);
|
||||
const string operation = nameof(PreAdjustment_PreparationAsync);
|
||||
|
||||
try
|
||||
{
|
||||
EnsureExternalInterface();
|
||||
|
||||
if (pp == null)
|
||||
throw new ArgumentNullException(nameof(pp));
|
||||
|
||||
log.InfoFormat(
|
||||
"{0}: {1} Start. Slot={2}",
|
||||
Name,
|
||||
operation,
|
||||
slot);
|
||||
|
||||
PreadjustmentProcessResult result =
|
||||
PreAdjustmentProcessResult result =
|
||||
await gciExternalInterface
|
||||
.Preadjustment_PreparationAsync(
|
||||
.PreAdjustment_PreparationAsync(
|
||||
slot,
|
||||
pp,
|
||||
token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@ -1511,7 +1553,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
string.Format("{0}: {1} failed.", Name, operation),
|
||||
ex);
|
||||
|
||||
return new PreadjustmentProcessResult
|
||||
return new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = false,
|
||||
Slot = slot,
|
||||
@ -1523,34 +1565,28 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region ================================== PreAdjustment AMPLITUDE TEST bridge ==================================
|
||||
|
||||
public async Task<PreadjustmentProcessResult> Preadjustment_AmplitudeTestAsync(
|
||||
public async Task<PreAdjustmentProcessResult> PreAdjustment_AmplitudeTestAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
const string operation = nameof(Preadjustment_AmplitudeTestAsync);
|
||||
const string operation = nameof(PreAdjustment_AmplitudeTestAsync);
|
||||
|
||||
try
|
||||
{
|
||||
EnsureExternalInterface();
|
||||
|
||||
if (pp == null)
|
||||
throw new ArgumentNullException(nameof(pp));
|
||||
|
||||
log.InfoFormat(
|
||||
"{0}: {1} Start. Slot={2}",
|
||||
Name,
|
||||
operation,
|
||||
slot);
|
||||
|
||||
PreadjustmentProcessResult result =
|
||||
PreAdjustmentProcessResult result =
|
||||
await gciExternalInterface
|
||||
.Preadjustment_AmplitudeTestAsync(
|
||||
.PreAdjustment_AmplitudeTestAsync(
|
||||
slot,
|
||||
pp,
|
||||
token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@ -1568,7 +1604,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
string.Format("{0}: {1} failed.", Name, operation),
|
||||
ex);
|
||||
|
||||
return new PreadjustmentProcessResult
|
||||
return new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = false,
|
||||
Slot = slot,
|
||||
@ -1582,31 +1618,26 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
|
||||
#region ================================== PreAdjustment TEMPERATURE CALIBRATION bridge ==================================
|
||||
|
||||
public async Task<PreadjustmentProcessResult> Preadjustment_TemperatureCalibrationAsync(
|
||||
public async Task<PreAdjustmentProcessResult> PreAdjustment_TemperatureCalibrationAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
const string operation = nameof(Preadjustment_TemperatureCalibrationAsync);
|
||||
const string operation = nameof(PreAdjustment_TemperatureCalibrationAsync);
|
||||
|
||||
try
|
||||
{
|
||||
EnsureExternalInterface();
|
||||
|
||||
if (pp == null)
|
||||
throw new ArgumentNullException(nameof(pp));
|
||||
|
||||
log.InfoFormat(
|
||||
"{0}: {1} Start. Slot={2}",
|
||||
Name,
|
||||
operation,
|
||||
slot);
|
||||
|
||||
PreadjustmentProcessResult result =
|
||||
PreAdjustmentProcessResult result =
|
||||
await gciExternalInterface
|
||||
.Preadjustment_TemperatureCalibrationAsync(
|
||||
.PreAdjustment_TemperatureCalibrationAsync(
|
||||
slot,
|
||||
pp,
|
||||
token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@ -1624,7 +1655,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
string.Format("{0}: {1} failed.", Name, operation),
|
||||
ex);
|
||||
|
||||
return new PreadjustmentProcessResult
|
||||
return new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = false,
|
||||
Slot = slot,
|
||||
@ -1636,34 +1667,28 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region ================================== PreAdjustment OFFSET TEST bridge ==================================
|
||||
|
||||
public async Task<PreadjustmentProcessResult> Preadjustment_OffsetTestAsync(
|
||||
public async Task<PreAdjustmentProcessResult> PreAdjustment_OffsetTestAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
const string operation = nameof(Preadjustment_OffsetTestAsync);
|
||||
const string operation = nameof(PreAdjustment_OffsetTestAsync);
|
||||
|
||||
try
|
||||
{
|
||||
EnsureExternalInterface();
|
||||
|
||||
if (pp == null)
|
||||
throw new ArgumentNullException(nameof(pp));
|
||||
|
||||
log.InfoFormat(
|
||||
"{0}: {1} Start. Slot={2}",
|
||||
Name,
|
||||
operation,
|
||||
slot);
|
||||
|
||||
PreadjustmentProcessResult result =
|
||||
PreAdjustmentProcessResult result =
|
||||
await gciExternalInterface
|
||||
.Preadjustment_OffsetTestAsync(
|
||||
.PreAdjustment_OffsetTestAsync(
|
||||
slot,
|
||||
pp,
|
||||
token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@ -1681,7 +1706,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
string.Format("{0}: {1} failed.", Name, operation),
|
||||
ex);
|
||||
|
||||
return new PreadjustmentProcessResult
|
||||
return new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = false,
|
||||
Slot = slot,
|
||||
@ -1693,34 +1718,28 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region ================================== PreAdjustment COMPLETION bridge ==================================
|
||||
|
||||
public async Task<PreadjustmentProcessResult> Preadjustment_CompletionAsync(
|
||||
public async Task<PreAdjustmentProcessResult> PreAdjustment_CompletionAsync(
|
||||
int slot,
|
||||
ProcessProgress pp,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
const string operation = nameof(Preadjustment_CompletionAsync);
|
||||
const string operation = nameof(PreAdjustment_CompletionAsync);
|
||||
|
||||
try
|
||||
{
|
||||
EnsureExternalInterface();
|
||||
|
||||
if (pp == null)
|
||||
throw new ArgumentNullException(nameof(pp));
|
||||
|
||||
log.InfoFormat(
|
||||
"{0}: {1} Start. Slot={2}",
|
||||
Name,
|
||||
operation,
|
||||
slot);
|
||||
|
||||
PreadjustmentProcessResult result =
|
||||
PreAdjustmentProcessResult result =
|
||||
await gciExternalInterface
|
||||
.Preadjustment_CompletionAsync(
|
||||
.PreAdjustment_CompletionAsync(
|
||||
slot,
|
||||
pp,
|
||||
token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@ -1738,7 +1757,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
string.Format("{0}: {1} failed.", Name, operation),
|
||||
ex);
|
||||
|
||||
return new PreadjustmentProcessResult
|
||||
return new PreAdjustmentProcessResult
|
||||
{
|
||||
Success = false,
|
||||
Slot = slot,
|
||||
|
||||
@ -41,9 +41,8 @@
|
||||
this.externalTypeNameLabel = new System.Windows.Forms.Label();
|
||||
this.externalTypeNameTextBox = new System.Windows.Forms.TextBox();
|
||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||
this.connectExternalButton = new System.Windows.Forms.Button();
|
||||
this.showGciGuiButton = new System.Windows.Forms.Button();
|
||||
this.showGciBridgeGUIButton = new System.Windows.Forms.Button();
|
||||
this.showGciGuiButton = new System.Windows.Forms.Button();
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
@ -162,38 +161,17 @@
|
||||
// groupBox1
|
||||
//
|
||||
this.groupBox1.Controls.Add(this.showGciBridgeGUIButton);
|
||||
this.groupBox1.Controls.Add(this.connectExternalButton);
|
||||
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 = "GCI bridge";
|
||||
//
|
||||
// connectExternalButton
|
||||
//
|
||||
this.connectExternalButton.Location = new System.Drawing.Point(14, 19);
|
||||
this.connectExternalButton.Name = "connectExternalButton";
|
||||
this.connectExternalButton.Size = new System.Drawing.Size(107, 28);
|
||||
this.connectExternalButton.TabIndex = 0;
|
||||
this.connectExternalButton.Text = "Connect";
|
||||
this.connectExternalButton.UseVisualStyleBackColor = true;
|
||||
this.connectExternalButton.Click += new System.EventHandler(this.connectExternalButton_Click);
|
||||
//
|
||||
// showGciGuiButton
|
||||
//
|
||||
this.showGciGuiButton.Location = new System.Drawing.Point(308, 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);
|
||||
this.groupBox1.Text = "Diagnostic GUI";
|
||||
//
|
||||
// showGciBridgeGUIButton
|
||||
//
|
||||
this.showGciBridgeGUIButton.Location = new System.Drawing.Point(421, 19);
|
||||
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;
|
||||
@ -201,6 +179,16 @@
|
||||
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);
|
||||
@ -242,7 +230,6 @@
|
||||
private System.Windows.Forms.Label externalTypeNameLabel;
|
||||
private System.Windows.Forms.TextBox externalTypeNameTextBox;
|
||||
private System.Windows.Forms.GroupBox groupBox1;
|
||||
private System.Windows.Forms.Button connectExternalButton;
|
||||
private System.Windows.Forms.Button showGciGuiButton;
|
||||
private System.Windows.Forms.Button showGciBridgeGUIButton;
|
||||
}
|
||||
|
||||
@ -170,26 +170,6 @@ namespace TBF.Rig.BridgeComponents.GciBridge
|
||||
return flags;
|
||||
}
|
||||
|
||||
private void connectExternalButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
GciBridge bridge = TbfComponents.FindComponent(config.Name) as GciBridge;
|
||||
|
||||
if (bridge == null)
|
||||
{
|
||||
MessageBox.Show("GciBridge component was not found.", "GCI Bridge");
|
||||
return;
|
||||
}
|
||||
|
||||
//bridge.ConnectExternal();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.Message, "GCI Bridge error");
|
||||
}
|
||||
}
|
||||
|
||||
private void showGuiButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
|
||||
@ -28,7 +28,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI
|
||||
private static readonly Regex LogLevelRegex = new Regex(@"\|\s*(TRACE|DEBUG|INFO|WARN|ERROR|FATAL)\s*\|", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
public InterfaceOutsideToGCI _gciApi;
|
||||
public InterfaceGCIToLaatzen _laatzenApi;
|
||||
public MainForm _mainform;
|
||||
public MainForm _mainForm;
|
||||
public GciBridge _bridge;
|
||||
public Debug.MeterBatchConfigPanel _batchPanel;
|
||||
public event Action<List<PublicModels.MeterBatchDebugStatus>> MeterBatchStatusChanged;// object status from place of his location
|
||||
@ -52,7 +52,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI
|
||||
|
||||
public MainView(GciBridge bridge, MainForm mainform)
|
||||
{
|
||||
_mainform = mainform;
|
||||
_mainForm = mainform;
|
||||
_bridge = bridge;
|
||||
_gciApi = bridge.gciExternalInterface;
|
||||
_laatzenApi = _bridge.gciExternalInterface._innerMeterAPI;
|
||||
@ -162,14 +162,14 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI
|
||||
/// </summary>
|
||||
private void InitializeDebugPanels()
|
||||
{
|
||||
_batchPanel = new Debug.MeterBatchConfigPanel(_mainform, this)
|
||||
_batchPanel = new Debug.MeterBatchConfigPanel(_mainForm, this)
|
||||
{
|
||||
Dock = DockStyle.Fill
|
||||
};
|
||||
|
||||
pnlSlotConfig.Controls.Add(_batchPanel);
|
||||
|
||||
pnlWorkerDebug.Controls.Add(new Debug.WorkerDebugPanel(_mainform, this)
|
||||
pnlWorkerDebug.Controls.Add(new Debug.WorkerDebugPanel(_mainForm, this)
|
||||
{
|
||||
Dock = DockStyle.Fill
|
||||
});
|
||||
@ -423,7 +423,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI
|
||||
Logger.Trace("FORM: ---------------------------------");
|
||||
Logger.Trace("FORM: Preadjustment open.");
|
||||
|
||||
_laatzenApi.ShowPreadjustmentForm(DialogOwner);
|
||||
_laatzenApi.ShowPreAdjustmentForm(DialogOwner);
|
||||
|
||||
Logger.Trace("FORM: Preadjustment shown.");
|
||||
}
|
||||
|
||||
@ -24,6 +24,14 @@
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.grpSlots = new System.Windows.Forms.GroupBox();
|
||||
this.label7 = new System.Windows.Forms.Label();
|
||||
this.label6 = new System.Windows.Forms.Label();
|
||||
this.label5 = new System.Windows.Forms.Label();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.preAdjustmentInitializationButton = new System.Windows.Forms.Button();
|
||||
this.completionActionButton = new System.Windows.Forms.Button();
|
||||
this.offsetTestActionButton = new System.Windows.Forms.Button();
|
||||
this.preparationActionButton = new System.Windows.Forms.Button();
|
||||
@ -32,19 +40,15 @@
|
||||
this.amplitudeTestActionButton = new System.Windows.Forms.Button();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.txtLog = new System.Windows.Forms.TextBox();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.label5 = new System.Windows.Forms.Label();
|
||||
this.label6 = new System.Windows.Forms.Label();
|
||||
this.label7 = new System.Windows.Forms.Label();
|
||||
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.cb_Metersize);
|
||||
this.grpSlots.Controls.Add(this.l_SettingsPreparationMetersize);
|
||||
this.grpSlots.Controls.Add(this.label7);
|
||||
this.grpSlots.Controls.Add(this.label6);
|
||||
this.grpSlots.Controls.Add(this.label5);
|
||||
@ -52,7 +56,7 @@
|
||||
this.grpSlots.Controls.Add(this.label3);
|
||||
this.grpSlots.Controls.Add(this.label2);
|
||||
this.grpSlots.Controls.Add(this.label1);
|
||||
this.grpSlots.Controls.Add(this.button1);
|
||||
this.grpSlots.Controls.Add(this.preAdjustmentInitializationButton);
|
||||
this.grpSlots.Controls.Add(this.completionActionButton);
|
||||
this.grpSlots.Controls.Add(this.offsetTestActionButton);
|
||||
this.grpSlots.Controls.Add(this.preparationActionButton);
|
||||
@ -67,6 +71,85 @@
|
||||
this.grpSlots.Text = "Slots by selection in the table";
|
||||
this.grpSlots.Enter += new System.EventHandler(this.grpSlots_Enter);
|
||||
//
|
||||
// label7
|
||||
//
|
||||
this.label7.AutoSize = true;
|
||||
this.label7.Location = new System.Drawing.Point(144, 87);
|
||||
this.label7.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label7.Name = "label7";
|
||||
this.label7.Size = new System.Drawing.Size(22, 13);
|
||||
this.label7.TabIndex = 115;
|
||||
this.label7.Text = "--->";
|
||||
//
|
||||
// label6
|
||||
//
|
||||
this.label6.AutoSize = true;
|
||||
this.label6.Location = new System.Drawing.Point(14, 87);
|
||||
this.label6.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label6.Name = "label6";
|
||||
this.label6.Size = new System.Drawing.Size(117, 13);
|
||||
this.label6.TabIndex = 114;
|
||||
this.label6.Text = "Preadjustment process:";
|
||||
//
|
||||
// label5
|
||||
//
|
||||
this.label5.AutoSize = true;
|
||||
this.label5.Location = new System.Drawing.Point(363, 257);
|
||||
this.label5.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label5.Name = "label5";
|
||||
this.label5.Size = new System.Drawing.Size(22, 13);
|
||||
this.label5.TabIndex = 113;
|
||||
this.label5.Text = "--->";
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.AutoSize = true;
|
||||
this.label4.Location = new System.Drawing.Point(307, 223);
|
||||
this.label4.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(22, 13);
|
||||
this.label4.TabIndex = 112;
|
||||
this.label4.Text = "--->";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(258, 189);
|
||||
this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(22, 13);
|
||||
this.label3.TabIndex = 111;
|
||||
this.label3.Text = "--->";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(219, 155);
|
||||
this.label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(22, 13);
|
||||
this.label2.TabIndex = 110;
|
||||
this.label2.Text = "--->";
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(176, 121);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(22, 13);
|
||||
this.label1.TabIndex = 109;
|
||||
this.label1.Text = "--->";
|
||||
//
|
||||
// preAdjustmentInitializationButton
|
||||
//
|
||||
this.preAdjustmentInitializationButton.Location = new System.Drawing.Point(171, 28);
|
||||
this.preAdjustmentInitializationButton.Name = "preAdjustmentInitializationButton";
|
||||
this.preAdjustmentInitializationButton.Size = new System.Drawing.Size(150, 28);
|
||||
this.preAdjustmentInitializationButton.TabIndex = 108;
|
||||
this.preAdjustmentInitializationButton.Text = "Preadjustment init";
|
||||
this.preAdjustmentInitializationButton.Click += new System.EventHandler(this.preAdjustmentInitializationButton_Click);
|
||||
//
|
||||
// completionActionButton
|
||||
//
|
||||
this.completionActionButton.Location = new System.Drawing.Point(390, 249);
|
||||
@ -142,83 +225,23 @@
|
||||
this.txtLog.TabIndex = 5;
|
||||
this.txtLog.WordWrap = false;
|
||||
//
|
||||
// button1
|
||||
// cb_Metersize
|
||||
//
|
||||
this.button1.Location = new System.Drawing.Point(17, 28);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(150, 28);
|
||||
this.button1.TabIndex = 108;
|
||||
this.button1.Text = "Preadjustment init";
|
||||
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;
|
||||
//
|
||||
// label1
|
||||
// l_SettingsPreparationMetersize
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(176, 121);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(22, 13);
|
||||
this.label1.TabIndex = 109;
|
||||
this.label1.Text = "--->";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(219, 155);
|
||||
this.label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(22, 13);
|
||||
this.label2.TabIndex = 110;
|
||||
this.label2.Text = "--->";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(258, 189);
|
||||
this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(22, 13);
|
||||
this.label3.TabIndex = 111;
|
||||
this.label3.Text = "--->";
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.AutoSize = true;
|
||||
this.label4.Location = new System.Drawing.Point(307, 223);
|
||||
this.label4.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(22, 13);
|
||||
this.label4.TabIndex = 112;
|
||||
this.label4.Text = "--->";
|
||||
//
|
||||
// label5
|
||||
//
|
||||
this.label5.AutoSize = true;
|
||||
this.label5.Location = new System.Drawing.Point(363, 257);
|
||||
this.label5.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label5.Name = "label5";
|
||||
this.label5.Size = new System.Drawing.Size(22, 13);
|
||||
this.label5.TabIndex = 113;
|
||||
this.label5.Text = "--->";
|
||||
//
|
||||
// label6
|
||||
//
|
||||
this.label6.AutoSize = true;
|
||||
this.label6.Location = new System.Drawing.Point(14, 87);
|
||||
this.label6.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label6.Name = "label6";
|
||||
this.label6.Size = new System.Drawing.Size(117, 13);
|
||||
this.label6.TabIndex = 114;
|
||||
this.label6.Text = "Preadjustment process:";
|
||||
//
|
||||
// label7
|
||||
//
|
||||
this.label7.AutoSize = true;
|
||||
this.label7.Location = new System.Drawing.Point(144, 87);
|
||||
this.label7.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label7.Name = "label7";
|
||||
this.label7.Size = new System.Drawing.Size(22, 13);
|
||||
this.label7.TabIndex = 115;
|
||||
this.label7.Text = "--->";
|
||||
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
|
||||
//
|
||||
@ -238,7 +261,7 @@
|
||||
private System.Windows.Forms.Button completionActionButton;
|
||||
private System.Windows.Forms.Button offsetTestActionButton;
|
||||
private System.Windows.Forms.Button amplitudeTestActionButton;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.Button preAdjustmentInitializationButton;
|
||||
private System.Windows.Forms.Label label5;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private System.Windows.Forms.Label label3;
|
||||
@ -246,5 +269,7 @@
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Label label7;
|
||||
private System.Windows.Forms.Label label6;
|
||||
private System.Windows.Forms.ComboBox cb_Metersize;
|
||||
private System.Windows.Forms.Label l_SettingsPreparationMetersize;
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
|
||||
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
|
||||
using Xylem.Common.Ui.CordonelPreadjustmentUi;
|
||||
using static GenesisCordonelInterface.API.PublicModels;
|
||||
using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.PreadjustmentMeter;
|
||||
@ -39,6 +40,18 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
|
||||
InitializeComponent();
|
||||
|
||||
LoadRegisterComboBoxes();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private void AddSlot()
|
||||
@ -159,74 +172,43 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
|
||||
|
||||
}
|
||||
|
||||
private void preparationActionButton_Click(
|
||||
private void preAdjustmentInitializationButton_Click(
|
||||
object sender,
|
||||
EventArgs e)
|
||||
{
|
||||
ExecuteAsync(async token =>
|
||||
try
|
||||
{
|
||||
await ExecutePreadjustmentForSelectedSlotsAsync(
|
||||
_bridge.Preadjustment_PreparationAsync,
|
||||
"Preparation",
|
||||
token);
|
||||
});
|
||||
}
|
||||
var selectedSlots = _mainView._batchPanel.GetSelectedGridData();
|
||||
|
||||
private void amplitudeTestActionButton_Click(
|
||||
object sender,
|
||||
EventArgs e)
|
||||
{
|
||||
ExecuteAsync(async token =>
|
||||
{
|
||||
await ExecutePreadjustmentForSelectedSlotsAsync(
|
||||
_bridge.Preadjustment_AmplitudeTestAsync,
|
||||
"Amplitude Test",
|
||||
token);
|
||||
});
|
||||
}
|
||||
if (!selectedSlots.Any())
|
||||
{
|
||||
Log("Detect: no selected slots.");
|
||||
return;
|
||||
}
|
||||
|
||||
private void temperatureCalibrationActionButton_Click(
|
||||
object sender,
|
||||
EventArgs e)
|
||||
{
|
||||
ExecuteAsync(async token =>
|
||||
{
|
||||
await ExecutePreadjustmentForSelectedSlotsAsync(
|
||||
_bridge.Preadjustment_TemperatureCalibrationAsync,
|
||||
"Temperature Calibration",
|
||||
token);
|
||||
});
|
||||
}
|
||||
ProcessProgress pp = CreatePreparationProgress();
|
||||
List<MeterStateControl> mc = CreateMeterControls(selectedSlots);
|
||||
|
||||
private void offsetTestActionButton_Click(
|
||||
object sender,
|
||||
EventArgs e)
|
||||
{
|
||||
ExecuteAsync(async token =>
|
||||
{
|
||||
await ExecutePreadjustmentForSelectedSlotsAsync(
|
||||
_bridge.Preadjustment_OffsetTestAsync,
|
||||
"Offset Test",
|
||||
token);
|
||||
});
|
||||
}
|
||||
var result = _bridge.Preadjustment_Initialization(pp, mc);
|
||||
|
||||
private void completionActionButton_Click(
|
||||
object sender,
|
||||
EventArgs e)
|
||||
{
|
||||
ExecuteAsync(async token =>
|
||||
if (!result.Success)
|
||||
{
|
||||
Log(result.ErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
Log(
|
||||
$"Initialization successful. Slots: {result.Slots}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await ExecutePreadjustmentForSelectedSlotsAsync(
|
||||
_bridge.Preadjustment_CompletionAsync,
|
||||
"Completion",
|
||||
token);
|
||||
});
|
||||
Log(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void detectActionButton_Click(
|
||||
object sender,
|
||||
EventArgs e)
|
||||
object sender,
|
||||
EventArgs e)
|
||||
{
|
||||
ExecuteAsync(async token =>
|
||||
{
|
||||
@ -239,14 +221,8 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
|
||||
return;
|
||||
}
|
||||
|
||||
_mainView._laatzenApi._settings =
|
||||
CreatePreAdjustmentSettings(selectedSlots);
|
||||
|
||||
_mainView._laatzenApi._meterControls =
|
||||
CreateMeterControls(selectedSlots);
|
||||
|
||||
PreadjustmentDetectResult result =
|
||||
await _bridge.Preadjustment_DetectAsync(token);
|
||||
await _bridge.PreAdjustment_DetectAsync(selectedSlots, token);
|
||||
|
||||
Log(
|
||||
result.Success
|
||||
@ -257,19 +233,84 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
|
||||
});
|
||||
}
|
||||
|
||||
private void preparationActionButton_Click(
|
||||
object sender,
|
||||
EventArgs e)
|
||||
{
|
||||
ExecuteAsync(async token =>
|
||||
{
|
||||
await ExecutePreadjustmentForSelectedSlotsAsync(
|
||||
_bridge.PreAdjustment_PreparationAsync,
|
||||
"Preparation",
|
||||
token);
|
||||
});
|
||||
}
|
||||
|
||||
private void amplitudeTestActionButton_Click(
|
||||
object sender,
|
||||
EventArgs e)
|
||||
{
|
||||
ExecuteAsync(async token =>
|
||||
{
|
||||
await ExecutePreadjustmentForSelectedSlotsAsync(
|
||||
_bridge.PreAdjustment_AmplitudeTestAsync,
|
||||
"Amplitude Test",
|
||||
token);
|
||||
});
|
||||
}
|
||||
|
||||
private void temperatureCalibrationActionButton_Click(
|
||||
object sender,
|
||||
EventArgs e)
|
||||
{
|
||||
ExecuteAsync(async token =>
|
||||
{
|
||||
await ExecutePreadjustmentForSelectedSlotsAsync(
|
||||
_bridge.PreAdjustment_TemperatureCalibrationAsync,
|
||||
"Temperature Calibration",
|
||||
token);
|
||||
});
|
||||
}
|
||||
|
||||
private void offsetTestActionButton_Click(
|
||||
object sender,
|
||||
EventArgs e)
|
||||
{
|
||||
ExecuteAsync(async token =>
|
||||
{
|
||||
await ExecutePreadjustmentForSelectedSlotsAsync(
|
||||
_bridge.PreAdjustment_OffsetTestAsync,
|
||||
"Offset Test",
|
||||
token);
|
||||
});
|
||||
}
|
||||
|
||||
private void completionActionButton_Click(
|
||||
object sender,
|
||||
EventArgs e)
|
||||
{
|
||||
ExecuteAsync(async token =>
|
||||
{
|
||||
await ExecutePreadjustmentForSelectedSlotsAsync(
|
||||
_bridge.PreAdjustment_CompletionAsync,
|
||||
"Completion",
|
||||
token);
|
||||
});
|
||||
}
|
||||
|
||||
private ProcessProgress CreatePreparationProgress()
|
||||
{
|
||||
return new ProcessProgress
|
||||
{
|
||||
Setting = new PreAdjustmentSettingsContainer
|
||||
{
|
||||
|
||||
|
||||
TempOnly = false,
|
||||
Culture =
|
||||
Thread.CurrentThread.CurrentCulture
|
||||
Culture = Thread.CurrentThread.CurrentCulture,
|
||||
MeterSize = (MeterSize)cb_Metersize.SelectedItem
|
||||
},
|
||||
|
||||
IsAutomaticMode = true
|
||||
IsAutomaticMode = false
|
||||
};
|
||||
}
|
||||
|
||||
@ -310,7 +351,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
|
||||
return settings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void writeRegisterButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
@ -367,7 +408,7 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
|
||||
}
|
||||
|
||||
private async Task ExecutePreadjustmentForSelectedSlotsAsync(
|
||||
Func<int, ProcessProgress, CancellationToken, Task<PreadjustmentProcessResult>> action,
|
||||
Func<int, CancellationToken, Task<PreAdjustmentProcessResult>> action,
|
||||
string processName,
|
||||
CancellationToken token)
|
||||
{
|
||||
@ -385,10 +426,9 @@ namespace TBF.Rig.BridgeComponents.GciBridge.UI.StaraTuraAPI_GciBridge
|
||||
|
||||
foreach (var selectedSlot in selectedSlots)
|
||||
{
|
||||
PreadjustmentProcessResult result =
|
||||
PreAdjustmentProcessResult result =
|
||||
await action(
|
||||
selectedSlot.Slot,
|
||||
pp,
|
||||
token);
|
||||
|
||||
Log(result.Success
|
||||
|
||||
@ -105,7 +105,7 @@ namespace TBF.Rig.Input.DataStorage.UniDataStorageReader.Readers
|
||||
if (enableDiagnostics)
|
||||
result.Diagnostics.Add("Query executed successfully.");
|
||||
|
||||
result.Data = val; // môže byť null → OK
|
||||
result.Data = val; // can be null → OK
|
||||
}
|
||||
|
||||
result.Success = true;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user