using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Xylem.Common.Hardware.Interfaces.Ports.PortCore; using static GenesisCordonelInterface.API.InterfaceOutsideToGCI; using static Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.GenesisMeter; namespace GenesisCordonelInterface.API { /// /// Public data contract layer for the Genesis Cordonel Interface (GCI). /// /// This class defines all Data Transfer Objects (DTOs) that are exposed /// to external consumers (e.g. TBF, UI, or other integration layers). /// /// Responsibilities: /// - Provide stable, dependency-free models for external usage /// - Decouple internal GCI implementation (GenesisMeter, Xylem libraries) /// from external systems /// - Define request/response contracts for all supported operations /// - Contain mapping methods between public DTOs and internal domain models /// /// Architecture: /// External world (TBF / UI) /// ↓ /// GciPublicModels (this layer) /// ↓ /// Internal GCI API (InterfaceGCIToLaatzen, GenesisMeter, etc.) /// /// Notes: /// - Public models must NOT expose internal types (e.g. GenesisMeter, IPort, etc.) /// - All mapping between internal and external representations must be done here /// - DTOs are designed to be simple, serializable, and stable over time /// - Any change in internal implementation should not affect these models /// /// Pattern: /// Each operation follows a consistent structure: /// Request → Operation → Result /// /// Example: /// GciInitSlotRequest → InitSlot → GciInitSlotResult /// GetSlot → GciSlotInfo /// GetPcbId → GciGetPcbIdResult /// /// This layer acts as a boundary between domain logic and integration logic. /// public class PublicModels { /// Persisted values for one meter and one preadjustment attempt. public sealed class PreadjustmentCompletionRecord { // Diagnostic routing only. Not a database column or SQL parameter. public int? Slot { get; set; } public int PcbId { get; set; } public int? MeterSize { get; set; } public int? ZeroOffset1 { get; set; } public int? ZeroOffset2 { get; set; } public int? ZeroOffset3 { get; set; } public int? FirstHitUpdatePeriod { get; set; } public int? FirstHitShift { get; set; } public int? FirstHitPercent1 { get; set; } public int? FirstHitPercent2 { get; set; } public int? FirstHitPercent3 { get; set; } public int? ToFTempOffset1 { get; set; } public int? ToFTempOffset2 { get; set; } public int? ToFTempOffset3 { get; set; } // Timestamp of the attempt, not the time of a possible later write retry. public DateTime Date { get; set; } public bool Successful { get; set; } } /// Q3 values calculated and confirmed by the caller; no port access. public sealed class Q3CalibrationRecord { public int PcbId { get; set; } // Preserve the existing TBF meaning/unit of each offset (do not rescale here). public double? P1Offset { get; set; } public int? P1Cal { get; set; } public double? P2Offset { get; set; } public int? P2Cal { get; set; } public double? P3Offset { get; set; } public int? P3Cal { get; set; } public DateTime Date { get; set; } } /// Completion outcome plus detached SQL inputs and separate write outcomes. public sealed class CompletionProcessResult : PreAdjustmentProcessResult { public List DatabaseRecords { get; set; } = new List(); public List DatabaseWrites { get; set; } = new List(); public override string ToString() { return base.ToString() + ", SQL=[" + string.Join("; ", DatabaseWrites.Select(w => "Slot=" + w.Slot + ", " + w.Result.Status)) + "]"; } } public sealed class CompletionDatabaseWriteResult { public int Slot { get; set; } public CalibrationWriteResult Result { get; set; } } public enum CalibrationWriteStatus { Failed = 0, Stored = 1, Disabled = 2, Simulated = 3, // The connection failed during commit; verify the DB before retrying. OutcomeUnknown = 4 } /// Database outcome, independent of the physical calibration outcome. public sealed class CalibrationWriteResult { public CalibrationWriteStatus Status { get; set; } public bool Success { get { return Status == CalibrationWriteStatus.Stored; } } public int PcbId { get; set; } public long? RecordId { get; set; } public string ErrorMessage { get; set; } public override string ToString() { return string.Format("Status={0}, PcbId={1}, RecordId={2}, Error={3}", Status, PcbId, RecordId, ErrorMessage ?? "None"); } } public static bool HideSensitiveValues { get; set; } = true; private static string FormatPassword(string password) { if (!HideSensitiveValues) return password ?? ""; if (string.IsNullOrEmpty(password)) return ""; return "********"; } /// /// Public DTOs exposed to external systems. /// These models represent the contract of the GCI API. /// They must remain stable and independent of internal implementation. /// #region ================================== PUBLIC MODELS =========================================== public class RegisterReadResult { public bool Success { get; set; } public string RegisterName { get; set; } public string RawHex { get; set; } public string ErrorMessage { get; set; } public override string ToString() { return string.Format( "Success={0}, RegisterName={1}, RawHex={2}, ErrorMessage={3}", Success, RegisterName, RawHex, ErrorMessage); } } public class WorkerDebugStatus { public int Slot { get; set; } public string Name { get; set; } public int QueueLength { get; set; } public bool IsBusy { get; set; } public string CurrentOperation { get; set; } public string LastError { get; set; } public DateTime LastActivity { get; set; } } public class MeterBatchDebugStatus { public int Slot { get; set; } public bool Selected { get; set; } public string PcbId { get; set; } public bool IsConnected { get; set; } public bool IsLoggedOn { get; set; } public string RequestPort { get; set; } public string StreamingPort { get; set; } public string RequestPortType { get; set; } public string FwVersion { get; set; } public string InterfaceVersion { get; set; } } public class RegisterWriteResult { public bool Success { get; set; } public string RegisterName { get; set; } public object WrittenValue { get; set; } public bool StoreToDevice { get; set; } public bool RefreshSystemState { get; set; } public string ErrorMessage { get; set; } public override string ToString() { return $"Success={Success}, " + $"Register={RegisterName ?? ""}, " + $"Value={WrittenValue ?? ""}, " + $"StoreToDevice={StoreToDevice}, " + $"RefreshSystemState={RefreshSystemState}, " + $"Error={ErrorMessage ?? ""}"; } } public class PortDetectionResult { public bool Success { get; set; } public int Slot { get; set; } public string PortName { get; set; } public string PcbId { get; set; } public string ErrorMessage { get; set; } public override string ToString() { return string.Format( "Slot={0}, Success={1}, PortName={2}, PcbId={3}, ErrorMessage={4}", Slot, Success, PortName, PcbId, ErrorMessage); } } public class GciSlotInfo { public int SlotId { get; set; } public bool Exists { get; set; } public bool Success { get; set; } public bool IsConnected { get; set; } public bool IsLoggedOn { get; set; } public string Message { get; set; } public string PcbId { get; set; } public string Password { get; set; } public GciConfigSource ConfigSource { get; set; } public GciPasswordSource PasswordSource { get; set; } public GciPortConfig RequestPort { get; set; } public GciPortConfig StreamingPort { get; set; } public override string ToString() { return string.Format( "SlotId={0}, Exists={1}, Success={2}, IsConnected={3}, IsLoggedOn={4}, Message={5}, PcbId={6}, Password={7}, ConfigSource={8}, PasswordSource={9}, RequestPort={10}, StreamingPort={11}", SlotId, Exists, Success, IsConnected, IsLoggedOn, Message, PcbId, FormatPassword(Password), ConfigSource, PasswordSource, RequestPort, StreamingPort); } } public class GciAllSlotsInfo { public bool Success { get; set; } public string Message { get; set; } public List Slots { get; set; } public override string ToString() { return $"Success={Success}, Count={Slots?.Count ?? 0}, Message={Message}"; } } public class Result { public int SlotId { get; set; } public bool Success { get; set; } public string Message { get; set; } public override string ToString() { return string.Format( "SlotId={0}, Success={1}, Message={2}", SlotId, Success, Message); } } public enum GciPasswordSource { RestApi = 0, OfflineFile = 1, InterfaceInputPassword = 2, } public enum GciConfigSource { FileConfig = 0, InterfaceInputConfig = 1, } /// /// Collection of port settings /// public class GciPortConfig { public string PortName { get; set; } public string Type { get; set; } public override string ToString() { return string.Format("PortName={0}, Type={1}", PortName, Type); } } public static GciPortConfig CreatePortConfig(string portName, string type) { if (string.IsNullOrWhiteSpace(portName)) return null; return new GciPortConfig { PortName = portName, Type = MapPortType(type) }; } public static string MapPortType(string type) { switch ((type ?? "").Trim().ToUpperInvariant()) { case "RFID": return "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.RfidSerialPort"; case "UART": return "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.UartSerialPort"; case "IRDA": return "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.IrdaSerialPort"; default: return "Xylem.Common.Hardware.Interfaces.Ports.SerialPorts.IrdaSerialPort"; } } public class GciInitSlotRequest { public int SlotId { get; set; } public GciConfigSource ConfigSource { get; set; } public GciPasswordSource PasswordSource { get; set; } public GciPortConfig RequestPort { get; set; } public GciPortConfig StreamingPort { get; set; } public string Password { get; set; } public override string ToString() { return string.Format( "SlotId={0}, GciConfigSource={1}, PasswordSource={2}, RequestPort={3}, StreamingPort={4}, Password={5}", SlotId, ConfigSource, PasswordSource, RequestPort, StreamingPort, FormatPassword(Password)); } } public class GciInitSlotResult { public int SlotId { get; set; } public bool Success { get; set; } public string Message { get; set; } public string PcbId { get; set; } public bool Created { get; set; } public bool Updated { get; set; } public bool AlreadyExists { get; set; } public override string ToString() { return string.Format( "SlotId={0}, Success={1}, Message={2}, PcbId={3}, Created={4}, Updated={5}, AlreadyExists={6}", SlotId, Success, Message, PcbId, Created, Updated, AlreadyExists); } } public class GciCleanAllSlotsResult { public bool Success { get; set; } public string Message { get; set; } public override string ToString() { return string.Format( "Success={0}, Message={1}", Success, Message); } } public class GciCleanSlotResult { public int SlotId { get; set; } public bool Success { get; set; } public string Message { get; set; } public override string ToString() { return string.Format( "SlotId={0}, Success={1}, Message={2}", SlotId, Success, Message); } } public class GciGetPcbIdResult { public int SlotId { get; set; } public bool Success { get; set; } public string PcbId { get; set; } public string Message { get; set; } public override string ToString() { return string.Format( "SlotId={0}, Success={1}, PcbId={2}, Message={3}", SlotId, Success, PcbId, Message); } } public class GciConnectResult { public int SlotId { get; set; } public bool Success { get; set; } public string PcbId { get; set; } public bool IsConnected { get; set; } public string Message { get; set; } public override string ToString() { return string.Format( "SlotId={0}, Success={1}, PcbId={2}, IsConnected={3}, Message={4}", SlotId, Success, PcbId, IsConnected, Message); } } public class GciLoginResult { public int SlotId { get; set; } public bool Success { get; set; } public string PcbId { get; set; } public bool IsLoggedOn { get; set; } public string Message { get; set; } public override string ToString() { return $"Slot={SlotId}, Success={Success}, PcbId={PcbId}, IsLoggedOn={IsLoggedOn}, Message={Message ?? ""}"; } } public class GciRegisterSnapshot { public string Name { get; set; } public string Type { get; set; } public string RawValue { get; set; } public string Min { get; set; } public string Max { get; set; } public string Description { get; set; } public string Version { get; set; } public string IsAvailable { get; set; } public string Privilege { get; set; } } public class GciDisconnectResult { public int SlotId { get; set; } public bool Success { get; set; } public string Message { get; set; } public override string ToString() { return string.Format( "SlotId={0}, Success={1}, Message={2}", SlotId, Success, Message); } } public class GciSetPasswordResult { public int SlotId { get; set; } public bool Success { get; set; } public string Password { get; set; } public string Message { get; set; } public override string ToString() { return string.Format( "SlotId={0}, Success={1}, Password={2}, Message={3}", SlotId, Success, FormatPassword(Password), Message); } } public class PreAdjustmentProcessResult { public bool Success { get; set; } public int Slot { get; set; } public string ProcessName { get; set; } public string ErrorMessage { get; set; } /// public override string ToString() { return $"Success={Success}, " + $"Slot={Slot}, " + $"Process={ProcessName}, " + $"Error={ErrorMessage ?? "None"}"; } } public class TemperatureCalibrationSlotResult : PreAdjustmentProcessResult { public decimal? ReferenceTemperatureCelsius { get; set; } public decimal? MeterTemperatureCelsius { get; set; } public decimal? AbsoluteDifferenceCelsius { get; set; } // The effective existing process limit, reported for diagnostics only. public decimal? DeviationLimitCelsius { get; set; } public int SampleCount { get; set; } public bool? TemperatureCheckPassed { get; set; } public override string ToString() { var culture = System.Globalization.CultureInfo.InvariantCulture; return base.ToString() + string.Format(culture, ", Reference={0} C, Meter={1} C, Difference={2} C, Limit={3} C, Samples={4}, Check={5}", ReferenceTemperatureCelsius?.ToString(culture) ?? "N/A", MeterTemperatureCelsius?.ToString(culture) ?? "N/A", AbsoluteDifferenceCelsius?.ToString(culture) ?? "N/A", DeviationLimitCelsius?.ToString(culture) ?? "N/A", SampleCount, TemperatureCheckPassed.HasValue ? (TemperatureCheckPassed.Value ? "PASSED" : "FAILED") : "NOT_EVALUATED"); } } public class TemperatureCalibrationResult : PreAdjustmentProcessResult { public List Slots { get; set; } = new List(); public bool IsSimulated { get; set; } public override string ToString() { return base.ToString() + ", Simulated=" + IsSimulated + ", TemperatureResults=[" + string.Join("; ", Slots.Select(s => s.ToString())) + "]"; } // Preserve status for failures before process creation and for simulation. // Missing measurements must never be represented as a measured zero. public static TemperatureCalibrationResult FromProcessResult( PreAdjustmentProcessResult result, bool simulated = false) { if (result == null) throw new ArgumentNullException(nameof(result)); var detailed = result as TemperatureCalibrationResult; if (detailed != null) return detailed; return new TemperatureCalibrationResult { Success = result.Success, Slot = result.Slot, ProcessName = result.ProcessName, ErrorMessage = result.ErrorMessage, IsSimulated = simulated }; } } public class PreadjustmentDetectResult { public bool Success { get; set; } public int DetectedMeterCount { get; set; } public int DetectedThermometerCount { get; set; } public string ErrorMessage { get; set; } /// public override string ToString() { return $"Success={Success}, " + $"Meters={DetectedMeterCount}, " + $"Thermometers={DetectedThermometerCount}, " + $"Error={ErrorMessage ?? "None"}"; } } public class PreAdjustmentInitializationResult { public bool Success { get; set; } public string Slots { get; set; } public string ErrorMessage { get; set; } /// public override string ToString() { return $"Success={Success}, " + $"Slots={Slots}, " + $"Error={ErrorMessage ?? "None"}"; } } #endregion } }