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.Ui.CordonelPreadjustmentUi;
using static GenesisCordonelInterface.API.PublicModels;
namespace GenesisCordonelInterface.API
{
///
/// Public-facing facade for external applications integrating with
/// Genesis Cordonel Interface.
///
///
/// 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 .
///
public class InterfaceOutsideToGCI
{
///
/// Internal GCI implementation used by this public facade.
///
public readonly InterfaceGCIToLaatzen _innerMeterAPI;
///
/// Occurs when meter batch status information changes.
///
public event Action> MeterBatchStatusChanged;
private readonly IMeterLoginPasswordReader loginPasswordsReader;
private readonly IPreAdjustmentCalibrationParamsReader calibrationParamsReader;
///
/// Initializes a new instance of the public GCI facade.
///
public InterfaceOutsideToGCI()
{
}
///
/// Initializes a new instance of the public GCI facade.
///
public InterfaceOutsideToGCI(
InterfaceGCIToLaatzen innerMeterApi,
IMeterLoginPasswordReader passwordReader,
IPreAdjustmentCalibrationParamsReader calibrationReader)
{
_innerMeterAPI = innerMeterApi?? throw new ArgumentNullException(nameof(innerMeterApi));
loginPasswordsReader = passwordReader ?? throw new ArgumentNullException(nameof(passwordReader));
calibrationParamsReader = calibrationReader ?? throw new ArgumentNullException(nameof(calibrationReader));
}
// Laatzen ToolBox actions
#region ================================== PORT DETECTION ==================================
public PortDetectionResult DetectStreamingPort(int slot)
{
var result = _innerMeterAPI?.DetectStreamingPort(slot);
//RaiseMeterBatchStatusChanged();
return result;
}
public async Task 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 DetectRequestPortAsync(
int slot,
CancellationToken token = default(CancellationToken))
{
var result = await _innerMeterAPI?.DetectRequestPortAsync(slot, token);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== INIT/UPDATE/GET slot ==================================
///
/// Initializes a meter slot using the provided slot configuration.
///
///
/// Slot initialization request containing slot id, configuration source,
/// password source, request port and streaming port.
///
/// Cancellation token used to cancel the asynchronous operation.
///
/// Result describing whether the slot was created, updated, already existed or failed.
///
///
/// Thrown when is null.
///
public async Task 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;
}
///
/// Updates configuration of an existing meter slot.
///
///
/// Slot configuration request containing updated configuration source,
/// password source and port settings.
///
/// Cancellation token used to cancel the asynchronous operation.
///
/// Result describing whether the slot update succeeded or failed.
///
///
/// Thrown when is null.
///
public async Task 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;
}
///
/// Gets information about one meter slot.
///
/// Slot id to query. Must be greater than zero.
/// Cancellation token used to cancel the asynchronous operation.
///
/// Slot information including existence, connection state, login state,
/// PCB id and configured communication ports.
///
///
/// Thrown when is invalid.
///
public async Task 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;
}
///
/// Gets information about all currently initialized meter slots.
///
/// Cancellation token used to cancel the asynchronous operation.
///
/// Collection of slot information records for all known meters.
///
public async Task GetAllSlotsAsync(
CancellationToken token = default)
{
var result = await _innerMeterAPI.GetAllMetersInfo(token).ConfigureAwait(false);
return result;
}
///
/// Cleans one meter slot and releases its runtime resources.
///
/// Slot id to clean. Must be greater than zero.
/// Cancellation token used to cancel the asynchronous operation.
///
/// Result describing whether the slot cleanup succeeded or failed.
///
///
/// Thrown when is invalid.
///
public async Task 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;
}
///
/// Cleans all initialized meter slots and releases related runtime resources.
///
/// Cancellation token used to cancel the asynchronous operation.
///
/// Result describing whether cleanup of all slots succeeded or failed.
///
public async Task CleanAllSlotsAsync(
CancellationToken token = default)
{
var result = await _innerMeterAPI.CleanAllSlotsAsync(token).ConfigureAwait(false);
//RaiseMeterBatchStatusChanged();
return result;
}
#endregion
#region ================================== PASSWORD ==================================
///
/// Sets runtime password for the meter assigned to the specified slot.
///
/// Slot id. Must be greater than zero.
/// Password to assign to the meter.
/// Cancellation token used to cancel the asynchronous operation.
/// Result containing password update status.
///
/// Thrown when slot id is invalid or password is empty.
///
public async Task 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 ==================================
///
/// Logs in to the meter assigned to the specified slot.
///
/// Slot id. Must be greater than zero.
/// Cancellation token used to cancel the asynchronous operation.
/// Login result containing login state and status message.
///
/// Thrown when is invalid.
///
public async Task 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 ==================================
///
/// Connects the meter assigned to the specified slot.
///
/// Slot id. Must be greater than zero.
/// Cancellation token used to cancel the asynchronous operation.
/// Connection result containing connection state and status message.
///
/// Thrown when is invalid.
///
public async Task 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;
}
///
/// Disconnects the meter assigned to the specified slot.
///
/// Slot id. Must be greater than zero.
/// Cancellation token used to cancel the asynchronous operation.
/// Disconnect result containing final connection state and status message.
///
/// Thrown when is invalid.
///
public async Task 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 ==================================
///
/// Reads PCB identifier from the meter assigned to the specified slot.
///
/// Slot id. Must be greater than zero.
/// Cancellation token used to cancel the asynchronous operation.
/// Result containing PCB id and read status.
///
/// Thrown when is invalid.
///
public async Task 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 ==================================
///
/// Reads a firmware register value from the meter assigned to the specified slot.
///
/// Slot id. Must be greater than zero.
/// Register identifier to read.
/// Cancellation token used to cancel the asynchronous operation.
///
/// Register read result containing raw register value and operation status.
///
///
/// Thrown when slot id or register name is invalid.
///
public async Task 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 ==================================
///
/// Writes a value to a firmware register.
///
/// Slot id. Must be greater than zero.
/// Register identifier to write.
/// Value to write.
///
/// Indicates whether configuration should be permanently stored.
///
///
/// Indicates whether firmware system state should be refreshed after write.
///
/// Cancellation token used to cancel the asynchronous operation.
///
/// Register write result describing write status.
///
///
/// Thrown when slot id or register name is invalid.
///
public async Task 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 ==================================
///
/// Updates meter password for the specified slot.
///
/// Slot id. Must be greater than zero.
/// New password.
/// Cancellation token used to cancel the asynchronous operation.
///
/// Password update result.
///
///
/// Thrown when slot id or password is invalid.
///
public async Task 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 ==================================
///
/// Gets runtime diagnostic information for all active workers.
///
///
/// Collection containing worker state, queue information,
/// current operation and activity timestamps.
///
public List GetWorkerDebugStatuses()
{
return _innerMeterAPI?.GetWorkerDebugStatuses();
}
///
/// Gets runtime diagnostic information for all meter slots.
///
///
/// Collection containing slot state, connection state,
/// selected state and communication configuration.
///
public List GetMeterBatchDebugStatuses()
{
return _innerMeterAPI?.GetMeterBatchDebugStatuses();
}
///
/// Raises meter batch status change notification.
///
///
/// Intended to notify external consumers after changes in meter state.
///
public void RaiseMeterBatchStatusChanged()
{
var statuses = GetMeterBatchDebugStatuses();
var handler = MeterBatchStatusChanged;
if (handler != null)
handler(statuses);
}
#endregion
#region ================================== SLOT SELECTION ==================================
///
/// Sets selection state for a slot.
///
/// Slot id.
/// Selection state.
public void SetSlotSelected(int slot, bool selected)
{
_innerMeterAPI?.SetSlotSelected(slot, selected);
RaiseMeterBatchStatusChanged();
}
///
/// Determines whether the specified slot is selected.
///
/// Slot id.
///
/// True if slot is selected; otherwise false.
///
public bool IsSlotSelected(int slot)
{
return (bool)(_innerMeterAPI?.IsSlotSelected(slot));
}
///
/// Gets all selected slot identifiers.
///
///
/// Ordered collection of selected slot ids.
///
public List GetSelectedSlots()
{
return _innerMeterAPI?.GetSelectedSlots();
}
#endregion
#region ================================== Register names ==================================
///
/// Gets all available firmware register identifiers.
///
///
/// Ordered collection of register names.
///
public List GetAllRegisterNames()
{
return _innerMeterAPI?.GetAllRegisterNames();
}
#endregion
// Laatzen Preadjustment processes
#region ================================== PreAdjustment ==================================
public PreAdjustmentInitializationResult Preadjustment_Initialization(
ProcessProgress pp,
List mc)
{
return _innerMeterAPI?.Preadjustment_Initialization(pp, mc);
}
public Task PreAdjustment_DetectAsync(
IEnumerable selectedSlots,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_DetectAsync(selectedSlots, token);
}
public Task PreAdjustment_PreparationAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_PreparationAsync(slot, token);
}
public Task PreAdjustment_AmplitudeTestAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_AmplitudeTestAsync(slot, token);
}
public Task PreAdjustment_TemperatureCalibrationAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_TemperatureCalibrationAsync(slot, token);
}
public bool PreAdjustment_PushTemperature(
double temperature)
{
return _innerMeterAPI.PreAdjustment_PushTemperature(temperature);
}
public Task PreAdjustment_OffsetTestAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_OffsetTestAsync(slot, token);
}
public Task PreAdjustment_CompletionAsync(
int slot,
CancellationToken token = default)
{
return _innerMeterAPI.PreAdjustment_CompletionAsync(slot, token);
}
#endregion
#region ================================== UNI DATA STORAGE READER ==================================
//LoginPasswords reading
///
/// Reads meter login password from configured GCI data storage.
///
///
/// Data query containing PCB ID or another configured lookup value.
///
///
/// Cancellation token used to cancel the asynchronous operation.
///
///
/// Password if found; otherwise null.
///
public Task ReadMeterLoginPasswordAsync(
DataQuery query,
CancellationToken token = default)
{
return loginPasswordsReader.ReadMeterLoginPasswordAsync(
query,
token);
}
//CalibrationParams reading
///
/// Reads pre-adjustment calibration parameters
/// from configured GCI data storage.
///
///
/// Data query containing meter size or another configured lookup value.
///
///
/// Cancellation token used to cancel the asynchronous operation.
///
///
/// Dictionary:
///
/// Key:
/// Calibration parameter name
///
/// Value:
/// Calibration parameter value
///
public Task> ReadPreAdjustmentCalibrationParamsAsync(
DataQuery query,
CancellationToken token = default)
{
return calibrationParamsReader.ReadCalibrationParamsAsync(
query,
token);
}
#endregion
}
}