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 } }