Files
tbf/GenesisCordonelInterface/API/InterfaceOutsideToGCI.cs
T
marekf 4124443f36 <Feat>: Add Completion and Q3 calibration database writes through GCI, increase revision to 3.9.3146.109
Cause:

- Preadjustment Completion and TBF Q3 calibration results required configurable database persistence.
- Both write operations required diagnostics and simulation support.

Solution:

1. Added independent database writer configurations
   - Added PreadjustmentCompletionWriter and Q3CalibrationWriter to gci_config.
   - Support SQL commands, stored procedures and parameter mappings.
   - Added SQL table creation and verification scripts.

2. Added Preadjustment Completion storage
   - Store PCB ID, meter size, amplitude settings, temperature offsets, zero-flow offsets, timestamp and overall result.
   - Use captured Completion readout values.
   - Preserve the original HTTP reporting.
   - Report SQL connection and write status in the corresponding slot diagnostics.

3. Added Q3 calibration storage
   - Forward TBF results through GCI Bridge to the GCI database API.
   - Store signed deviations and written calibration factors for all three paths.
   - Write to SQL after successful calibration register writes and StoreCalibration.
   - Include PCB ID and UTC timestamp.

4. Added simulation support
   - Generate sample Completion and Q3 records for simulated slots.
   - Perform real SQL writes when the corresponding writer is enabled.
   - Log connection progress, write outcomes and generated record IDs.

5. Separated calibration and database outcomes
   - Preserve meter operation results when database storage fails.
   - Expose database write status independently.

6. Increased revision
   - Updated revision from 3.9.3146.108 to 3.9.3146.109.

Validation:

- Confirmed successful SQL storage for simulated Completion.
- Confirmed successful Q3 calibration result storage.

ToDo:

- Use the GCI Q3 storage API as the foundation for future migration of Q3 calibration from the direct TBF communication branch to GCI.
2026-09-14 17:06:22 +02:00

968 lines
38 KiB
C#

using CordonelPreadjustmentUi;
using CordonelPreadjustmentUi.Processes.Itinerary;
using GenesisCordonelInterface.Core.DataStorage.Config;
using GenesisCordonelInterface.Core.DataStorage.Reading.Common.Models;
using GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.MeterLoginPasswords;
using GenesisCordonelInterface.Core.DataStorage.Reading.Implementation.PreAdjustmentCalibrationParams;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Threading;
using System.Threading.Tasks;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Logic.ProductionOrderCore.OrderData;
using Xylem.Common.Ui.CordonelPreadjustmentUi;
using static GenesisCordonelInterface.API.PublicModels;
namespace GenesisCordonelInterface.API
{
/// <summary>
/// Public-facing facade for external applications integrating with
/// Genesis Cordonel Interface.
/// </summary>
/// <remarks>
/// This class exposes a simplified and controlled API for external callers.
/// It validates public input, maps public request models to internal models,
/// forwards operations to the internal GCI implementation and exposes status
/// notifications for meter batch changes.
///
/// This layer should stay thin. Business logic and meter communication are
/// handled by <see cref="InterfaceGCIToLaatzen"/>.
/// </remarks>
public class InterfaceOutsideToGCI
{
/// <summary>
/// Internal GCI implementation used by this public facade.
/// </summary>
public readonly InterfaceGCIToLaatzen _innerMeterAPI;
/// <summary>
/// Occurs when meter batch status information changes.
/// </summary>
public event Action<List<MeterBatchDebugStatus>> MeterBatchStatusChanged;
private readonly IMeterLoginPasswordReader loginPasswordsReader;
private readonly IPreAdjustmentCalibrationParamsReader calibrationParamsReader;
private readonly Core.DataStorage.Writing.CalibrationResultWriter calibrationResultWriter =
new Core.DataStorage.Writing.CalibrationResultWriter(null);
/// <summary>Stores one detached completion record without accessing meter ports.</summary>
public Task<CalibrationWriteResult> StorePreadjustmentCompletionResultAsync(
PreadjustmentCompletionRecord record, CancellationToken token = default(CancellationToken))
{
if (record == null) throw new ArgumentNullException(nameof(record));
// Capture the progress instance and slot for this write, even if a new
// batch is initialized while the database operation is still finishing.
var progress = _innerMeterAPI?._progressProcess;
int? slot = record.Slot;
Action<string> diagnostic = null;
if (progress != null && slot.HasValue && slot.Value > 0)
diagnostic = message => progress.DebugMessage(message, slot.Value, "DB");
return calibrationResultWriter.StorePreadjustmentCompletionAsync(record, token, diagnostic);
}
/// <summary>Creates a synthetic Completion record and exercises the real configured SQL writer.</summary>
public async Task<CompletionProcessResult> StoreSimulatedCompletionResultAsync(
int slot, int pcbId, bool phaseSuccess, string phaseError,
CancellationToken token, Action<string> diagnostic)
{
token.ThrowIfCancellationRequested();
if (slot <= 0) throw new ArgumentOutOfRangeException(nameof(slot));
// Synthetic fixture values belong to GCI, not to the hardware Completion process.
var record = new PreadjustmentCompletionRecord
{
Slot = slot, PcbId = pcbId, MeterSize = 2,
ZeroOffset1 = -20, ZeroOffset2 = -30, ZeroOffset3 = -67,
FirstHitUpdatePeriod = 10, FirstHitShift = 4,
FirstHitPercent1 = 17, FirstHitPercent2 = 13, FirstHitPercent3 = 17,
ToFTempOffset1 = 22260, ToFTempOffset2 = -115410, ToFTempOffset3 = 14992,
Date = DateTime.UtcNow, Successful = phaseSuccess
};
// Optional diagnostic subscribers must never change the storage outcome.
try { diagnostic?.Invoke("Simulated calibration data; actual SQL write requested. Fixture MeterSize=2."); }
catch (Exception) { }
var write = await calibrationResultWriter.StorePreadjustmentCompletionAsync(
record, token, diagnostic).ConfigureAwait(false);
var result = new CompletionProcessResult
{
Slot = slot, Success = phaseSuccess, ProcessName = "PreAdjustment_Completion",
ErrorMessage = phaseError
};
result.DatabaseRecords.Add(record);
result.DatabaseWrites.Add(new CompletionDatabaseWriteResult { Slot = slot, Result = write });
return result;
}
/// <summary>Stores Q3 values supplied by TBF; does not perform Q3 calibration.</summary>
public Task<CalibrationWriteResult> StoreQ3CalibrationResultAsync(
Q3CalibrationRecord record, CancellationToken token = default(CancellationToken))
{
return calibrationResultWriter.StoreQ3CalibrationAsync(record, token);
}
public Task<CalibrationWriteResult> StoreQ3CalibrationResultAsync(
Q3CalibrationRecord record, CancellationToken token, Action<string> diagnostic)
{
return calibrationResultWriter.StoreQ3CalibrationAsync(record, token, diagnostic);
}
public Task<CalibrationWriteResult> StoreSimulatedQ3CalibrationResultAsync(
int pcbId, CancellationToken token, Action<string> diagnostic)
{
token.ThrowIfCancellationRequested();
try { diagnostic?.Invoke("Simulated Q3 fixture values; actual SQL write requested."); }
catch (Exception) { }
return calibrationResultWriter.StoreQ3CalibrationAsync(new Q3CalibrationRecord
{
PcbId = pcbId, P1Offset = -0.3, P1Cal = 15260,
P2Offset = -0.3, P2Cal = 15451, P3Offset = -0.3, P3Cal = 15204,
Date = DateTime.UtcNow
}, token, diagnostic);
}
/// <summary>
/// Initializes a new instance of the public GCI facade.
/// </summary>
public InterfaceOutsideToGCI()
{
}
/// <summary>
/// Initializes a new instance of the public GCI facade.
/// </summary>
public InterfaceOutsideToGCI(
InterfaceGCIToLaatzen innerMeterApi,
IMeterLoginPasswordReader passwordReader,
IPreAdjustmentCalibrationParamsReader calibrationReader)
: this(innerMeterApi, passwordReader, calibrationReader, null)
{
}
public InterfaceOutsideToGCI(
InterfaceGCIToLaatzen innerMeterApi,
IMeterLoginPasswordReader passwordReader,
IPreAdjustmentCalibrationParamsReader calibrationReader,
GciDataStorageConfig storageConfig)
{
_innerMeterAPI = innerMeterApi?? throw new ArgumentNullException(nameof(innerMeterApi));
loginPasswordsReader = passwordReader ?? throw new ArgumentNullException(nameof(passwordReader));
calibrationParamsReader = calibrationReader ?? throw new ArgumentNullException(nameof(calibrationReader));
calibrationResultWriter = new Core.DataStorage.Writing.CalibrationResultWriter(storageConfig);
//init handlers
//_innerMeterAPI._progressProcess.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
}
private async void RequestedCalibrationParamsFromDb(
object sender,
EventArgsProcessProgress e)
{
var progress = e.Value;
var source = progress.CancellationSource;
var token = source.Token;
try
{
token.ThrowIfCancellationRequested();
DataQuery query = new DataQuery();
query.QueryParams.Add(((int)e.Value.Setting.MeterSize).ToString());
Dictionary<string, UInt32> calibrationParams = await ReadPreAdjustmentCalibrationParamsAsync(query, token).ConfigureAwait(false);
lock (progress)
{
if (!token.IsCancellationRequested && ReferenceEquals(progress.CancellationSource, source))
progress.PushedCalibrationParams = calibrationParams;
}
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
if (!token.IsCancellationRequested && ReferenceEquals(progress.CancellationSource, source))
progress.DebugMessage($"Calibration params reading failed: {ex.Message}");
}
finally
{
lock (progress)
{
if (!token.IsCancellationRequested && ReferenceEquals(progress.CancellationSource, source))
progress.CalibrationParamsReadEvent.Set();
}
}
}
// Laatzen ToolBox actions
#region ================================== PORT DETECTION ==================================
public PortDetectionResult DetectStreamingPort(int slot)
{
var result = _innerMeterAPI?.DetectStreamingPort(slot);
//RaiseMeterBatchStatusChanged();
return result;
}
public async Task<PortDetectionResult> DetectStreamingPortAsync(
int slot,
CancellationToken token = default(CancellationToken))
{
var result = await _innerMeterAPI?.DetectStreamingPortAsync(slot, token);
//RaiseMeterBatchStatusChanged();
return result;
}
public PortDetectionResult DetectRequestPort(int slot)
{
var result = _innerMeterAPI?.DetectRequestPort(slot);
//RaiseMeterBatchStatusChanged();
return result;
}
public async Task<PortDetectionResult> DetectRequestPortAsync(
int slot,
CancellationToken token = default(CancellationToken))
{
var result = await _innerMeterAPI?.DetectRequestPortAsync(slot, token);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== INIT/UPDATE/GET slot ==================================
/// <summary>Acquires streaming reception until the returned lease is disposed.</summary>
public Task<IDisposable> AcquirePreadjustmentStreamingPortAsync(
int slot, GciPortConfig streamingPort, CancellationToken token = default)
{
if (streamingPort == null) throw new ArgumentNullException(nameof(streamingPort));
var mappedPort = ModelsMapping.MapPort(streamingPort);
if (!mappedPort.HasValue) throw new ArgumentException("A streaming port is required.", nameof(streamingPort));
return _innerMeterAPI.AcquirePreadjustmentStreamingPortAsync(slot, mappedPort.Value, token);
}
/// <summary>
/// Initializes a meter slot using the provided slot configuration.
/// </summary>
/// <param name="request">
/// Slot initialization request containing slot id, configuration source,
/// password source, request port and streaming port.
/// </param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Result describing whether the slot was created, updated, already existed or failed.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="request"/> is null.
/// </exception>
public async Task<GciInitSlotResult> InitSlotAsync(
GciInitSlotRequest request,
CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var result = await _innerMeterAPI.InitSlotAsync(
request.SlotId,
ModelsMapping.MapConfigSource(request.ConfigSource),
ModelsMapping.MapPasswordSource(request.PasswordSource),
ModelsMapping.MapPort(request.RequestPort),
ModelsMapping.MapPort(request.StreamingPort),
token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
/// <summary>
/// Updates configuration of an existing meter slot.
/// </summary>
/// <param name="request">
/// Slot configuration request containing updated configuration source,
/// password source and port settings.
/// </param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Result describing whether the slot update succeeded or failed.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="request"/> is null.
/// </exception>
public async Task<GciInitSlotResult> UpdateSlotAsync(
GciInitSlotRequest request,
CancellationToken token = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
var result = await _innerMeterAPI.UpdateSlotAsync(
request.SlotId,
ModelsMapping.MapConfigSource(request.ConfigSource),
ModelsMapping.MapPasswordSource(request.PasswordSource),
ModelsMapping.MapPort(request.RequestPort),
ModelsMapping.MapPort(request.StreamingPort),
token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
/// <summary>
/// Gets information about one meter slot.
/// </summary>
/// <param name="slotId">Slot id to query. Must be greater than zero.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Slot information including existence, connection state, login state,
/// PCB id and configured communication ports.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="slotId"/> is invalid.
/// </exception>
public async Task<GciSlotInfo> GetSlotAsync(
int slotId,
CancellationToken token = default)
{
if (slotId <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await _innerMeterAPI.GetOneMeterInfo(slotId, token).ConfigureAwait(false);
return result;
}
/// <summary>
/// Gets information about all currently initialized meter slots.
/// </summary>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Collection of slot information records for all known meters.
/// </returns>
public async Task<GciAllSlotsInfo> GetAllSlotsAsync(
CancellationToken token = default)
{
var result = await _innerMeterAPI.GetAllMetersInfo(token).ConfigureAwait(false);
return result;
}
/// <summary>
/// Cleans one meter slot and releases its runtime resources.
/// </summary>
/// <param name="slot">Slot id to clean. Must be greater than zero.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Result describing whether the slot cleanup succeeded or failed.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="slot"/> is invalid.
/// </exception>
public async Task<GciCleanSlotResult> CleanSlotAsync(
int slot,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.", nameof(slot));
var result = await _innerMeterAPI.CleanSlotAsync(slot, token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
/// <summary>
/// Cleans all initialized meter slots and releases related runtime resources.
/// </summary>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Result describing whether cleanup of all slots succeeded or failed.
/// </returns>
public async Task<GciCleanAllSlotsResult> CleanAllSlotsAsync(
CancellationToken token = default)
{
var result = await _innerMeterAPI.CleanAllSlotsAsync(token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== PASSWORD ==================================
/// <summary>
/// Sets runtime password for the meter assigned to the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="password">Password to assign to the meter.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>Result containing password update status.</returns>
/// <exception cref="ArgumentException">
/// Thrown when slot id is invalid or password is empty.
/// </exception>
public async Task<GciSetPasswordResult> SetPasswordAsync(
int slot,
string password,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.");
if (string.IsNullOrWhiteSpace(password))
throw new ArgumentException("Password is empty.");
var result = await _innerMeterAPI.SetMeterPasswordAsync(slot, password, token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== LOGIN ==================================
/// <summary>
/// Logs in to the meter assigned to the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>Login result containing login state and status message.</returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="slot"/> is invalid.
/// </exception>
public async Task<PublicModels.GciLoginResult> LoginOneSlotAsync(
int slot,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await _innerMeterAPI.LoginOneSlotAsync(slot, token).ConfigureAwait(false);
return result;
//RaiseMeterBatchStatusChanged();
}
#endregion
#region ================================== CONNECTION ==================================
/// <summary>
/// Connects the meter assigned to the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>Connection result containing connection state and status message.</returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="slot"/> is invalid.
/// </exception>
public async Task<GciConnectResult> ConnectOneSlotAsync(
int slot,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await _innerMeterAPI.ConnectOneSlotAsync(slot, token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
/// <summary>
/// Disconnects the meter assigned to the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>Disconnect result containing final connection state and status message.</returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="slot"/> is invalid.
/// </exception>
public async Task<GciDisconnectResult> DisconnectAsync(
int slot,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await _innerMeterAPI.DisconnectAsync(slot, token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== PCB ==================================
/// <summary>
/// Reads PCB identifier from the meter assigned to the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>Result containing PCB id and read status.</returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="slot"/> is invalid.
/// </exception>
public async Task<GciGetPcbIdResult> GetPcbIdAsync(
int slot,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.");
var result = await _innerMeterAPI.GetPcbIdAsync(slot, token).ConfigureAwait(false);
return result;
}
#endregion
#region ================================== READ ==================================
/// <summary>
/// Reads a firmware register value from the meter assigned to the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="registerName">Register identifier to read.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Register read result containing raw register value and operation status.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when slot id or register name is invalid.
/// </exception>
public async Task<RegisterReadResult> ReadRegisterAsync(
int slot,
string registerName,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.", nameof(slot));
if (string.IsNullOrWhiteSpace(registerName))
throw new ArgumentException("Register name is empty.", nameof(registerName));
var result = await _innerMeterAPI
.ReadRegisterAsync(slot, registerName, token)
.ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== WRITE ==================================
/// <summary>
/// Writes a value to a firmware register.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="registerName">Register identifier to write.</param>
/// <param name="value">Value to write.</param>
/// <param name="storeToDevice">
/// Indicates whether configuration should be permanently stored.
/// </param>
/// <param name="refreshSystemState">
/// Indicates whether firmware system state should be refreshed after write.
/// </param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Register write result describing write status.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when slot id or register name is invalid.
/// </exception>
public async Task<RegisterWriteResult> WriteRegisterAsync(
int slot,
string registerName,
object value,
bool storeToDevice = false,
bool refreshSystemState = false,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.", nameof(slot));
if (string.IsNullOrWhiteSpace(registerName))
throw new ArgumentException("Register name is empty.", nameof(registerName));
var result = await _innerMeterAPI
.WriteRegisterAsync(
slot,
registerName,
value,
storeToDevice,
refreshSystemState,
token)
.ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== Password ==================================
/// <summary>
/// Updates meter password for the specified slot.
/// </summary>
/// <param name="slot">Slot id. Must be greater than zero.</param>
/// <param name="password">New password.</param>
/// <param name="token">Cancellation token used to cancel the asynchronous operation.</param>
/// <returns>
/// Password update result.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when slot id or password is invalid.
/// </exception>
public async Task<GciSetPasswordResult> SetMeterPasswordAsync(
int slot,
string password,
CancellationToken token = default)
{
if (slot <= 0)
throw new ArgumentException("Invalid slot id.", nameof(slot));
if (string.IsNullOrWhiteSpace(password))
throw new ArgumentException("Password is empty.", nameof(password));
var result = await _innerMeterAPI
.SetMeterPasswordAsync(slot, password, token)
.ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== DEBUG STATUS ==================================
/// <summary>
/// Gets runtime diagnostic information for all active workers.
/// </summary>
/// <returns>
/// Collection containing worker state, queue information,
/// current operation and activity timestamps.
/// </returns>
public List<WorkerDebugStatus> GetWorkerDebugStatuses()
{
return _innerMeterAPI?.GetWorkerDebugStatuses();
}
/// <summary>
/// Gets runtime diagnostic information for all meter slots.
/// </summary>
/// <returns>
/// Collection containing slot state, connection state,
/// selected state and communication configuration.
/// </returns>
public List<MeterBatchDebugStatus> GetMeterBatchDebugStatuses()
{
return _innerMeterAPI?.GetMeterBatchDebugStatuses();
}
/// <summary>
/// Raises meter batch status change notification.
/// </summary>
/// <remarks>
/// Intended to notify external consumers after changes in meter state.
/// </remarks>
public void RaiseMeterBatchStatusChanged()
{
var statuses = GetMeterBatchDebugStatuses();
var handler = MeterBatchStatusChanged;
if (handler != null)
handler(statuses);
}
#endregion
#region ================================== SLOT SELECTION ==================================
/// <summary>
/// Sets selection state for a slot.
/// </summary>
/// <param name="slot">Slot id.</param>
/// <param name="selected">Selection state.</param>
public void SetSlotSelected(int slot, bool selected)
{
_innerMeterAPI?.SetSlotSelected(slot, selected);
RaiseMeterBatchStatusChanged();
}
/// <summary>
/// Determines whether the specified slot is selected.
/// </summary>
/// <param name="slot">Slot id.</param>
/// <returns>
/// True if slot is selected; otherwise false.
/// </returns>
public bool IsSlotSelected(int slot)
{
return (bool)(_innerMeterAPI?.IsSlotSelected(slot));
}
/// <summary>
/// Gets all selected slot identifiers.
/// </summary>
/// <returns>
/// Ordered collection of selected slot ids.
/// </returns>
public List<int> GetSelectedSlots()
{
return _innerMeterAPI?.GetSelectedSlots();
}
#endregion
#region ================================== Register names ==================================
/// <summary>
/// Gets all available firmware register identifiers.
/// </summary>
/// <returns>
/// Ordered collection of register names.
/// </returns>
public List<string> GetAllRegisterNames()
{
return _innerMeterAPI?.GetAllRegisterNames();
}
#endregion
// Laatzen Preadjustment processes
#region ================================== PreAdjustment ==================================
public PreAdjustmentInitializationResult Preadjustment_Initialization(
ProcessProgress pp,
List<MeterStateControl> mc)
{
//init handlers
pp.OnRequestedCalibrationParamsFromDb -= RequestedCalibrationParamsFromDb;
pp.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
return _innerMeterAPI?.Preadjustment_Initialization(pp, mc);
}
public PreAdjustmentInitializationResult Preadjustment_Initialization(
ProcessProgress pp,
List<MeterStateControl> mc,
CancellationToken token)
{
pp.OnRequestedCalibrationParamsFromDb -= RequestedCalibrationParamsFromDb;
pp.OnRequestedCalibrationParamsFromDb += RequestedCalibrationParamsFromDb;
return _innerMeterAPI?.Preadjustment_Initialization(pp, mc, token);
}
public void CancelPreadjustment()
{
_innerMeterAPI?.CancelPreadjustment();
}
public Task<PreadjustmentDetectResult> PreAdjustment_DetectAsync(
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_DetectAsync(selectedSlots, token);
}
public PreadjustmentDetectResult PreAdjustment_DetectDirect(
IEnumerable<PublicModels.MeterBatchDebugStatus> selectedSlots,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_DetectDirect(selectedSlots, token);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_PreparationAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_PreparationAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_PreparationDirect()
{
return _innerMeterAPI.PreAdjustment_PreparationDirect();
}
public PreAdjustmentProcessResult PreAdjustment_PreparationDirect(CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_PreparationDirect(token);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_AmplitudeTestAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_AmplitudeTestAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_AmplitudeTestDirect()
{
return _innerMeterAPI.PreAdjustment_AmplitudeTestDirect();
}
public PreAdjustmentProcessResult PreAdjustment_AmplitudeTestDirect(CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_AmplitudeTestDirect(token);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_TemperatureCalibrationAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibrationDirect()
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDirect();
}
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibrationDirect(CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDirect(token);
}
public PreAdjustmentProcessResult PreAdjustment_TemperatureCalibrationDirect(
CalibrationTemperatureInput temperature, CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDirect(temperature, token);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_TemperatureCalibrationAsync(
int slot, CalibrationTemperatureInput temperature, CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationAsync(slot, temperature, token);
}
public TemperatureCalibrationResult PreAdjustment_TemperatureCalibrationDetailedDirect(
CalibrationTemperatureInput temperature, CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDetailedDirect(temperature, token);
}
public Task<TemperatureCalibrationResult> PreAdjustment_TemperatureCalibrationDetailedAsync(
int slot, CalibrationTemperatureInput temperature, CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationDetailedAsync(slot, temperature, token);
}
public bool PreAdjustment_PushTemperature(
double temperature)
{
return _innerMeterAPI.PreAdjustment_PushTemperature(temperature);
}
public bool PreAdjustment_PushTemperature(
double temperature,
CancellationToken token)
{
token.ThrowIfCancellationRequested();
return _innerMeterAPI.PreAdjustment_PushTemperature(temperature);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_OffsetTestAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_OffsetTestAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_OffsetTestDirect()
{
return _innerMeterAPI.PreAdjustment_OffsetTestDirect();
}
public PreAdjustmentProcessResult PreAdjustment_OffsetTestDirect(CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_OffsetTestDirect(token);
}
public Task<PreAdjustmentProcessResult> PreAdjustment_CompletionAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_CompletionAsync(slot, token);
}
public PreAdjustmentProcessResult PreAdjustment_CompletionDirect()
{
return _innerMeterAPI.PreAdjustment_CompletionDirect();
}
public PreAdjustmentProcessResult PreAdjustment_CompletionDirect(CancellationToken token)
{
return _innerMeterAPI.PreAdjustment_CompletionDirect(token);
}
#endregion
#region ================================== UNI DATA STORAGE READER ==================================
//LoginPasswords reading
/// <summary>
/// Reads meter login password from configured GCI data storage.
/// </summary>
/// <param name="query">
/// Data query containing PCB ID or another configured lookup value.
/// </param>
/// <param name="token">
/// Cancellation token used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// Password if found; otherwise null.
/// </returns>
public Task<string> ReadMeterLoginPasswordAsync(
DataQuery query,
CancellationToken token = default)
{
return loginPasswordsReader.ReadMeterLoginPasswordAsync(
query,
token);
}
//CalibrationParams reading
/// <summary>
/// Reads pre-adjustment calibration parameters
/// from configured GCI data storage.
/// </summary>
/// <param name="query">
/// Data query containing meter size or another configured lookup value.
/// </param>
/// <param name="token">
/// Cancellation token used to cancel the asynchronous operation.
/// </param>
/// <returns>
/// Dictionary:
///
/// Key:
/// Calibration parameter name
///
/// Value:
/// Calibration parameter value
/// </returns>
public Task<Dictionary<string, UInt32>> ReadPreAdjustmentCalibrationParamsAsync(
DataQuery query,
CancellationToken token = default)
{
return calibrationParamsReader.ReadCalibrationParamsAsync(
query,
token);
}
#endregion
}
}