using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Windows.Media; using CtrEditor.HydraulicSimulator; using CtrEditor.ObjetosSim; using CtrEditor.FuncionesBase; using HydraulicSimulator.Models; using Newtonsoft.Json; using Xceed.Wpf.Toolkit.PropertyGrid.Attributes; using CommunityToolkit.Mvvm.ComponentModel; namespace CtrEditor.ObjetosSim { /// /// Bomba hidráulica que implementa las interfaces del simulador hidráulico /// public partial class osHydPump : osBase, IHydraulicFlowReceiver, IHydraulicPressureReceiver, IosBase { // Referencia al objeto de simulación hidráulica específico private simHydraulicPump SimHydraulicPump; public static string NombreCategoria() => "Componentes Hidráulicos"; public static string NombreClase() { return "Bomba Hidráulica"; } // Properties [ObservableProperty] [property: JsonIgnore] [property: Category("Apariencia")] [property: Description("Imagen visual")] [property: Name("Imagen")] public ImageSource imageSource_oculta; [ObservableProperty] [property: Category("🎨 Apariencia")] [property: Description("Tamaño visual de la bomba")] [property: Name("Tamaño")] public float tamano; public override void OnResize(float Delta_Width, float Delta_Height) { Tamano += Delta_Width + Delta_Height; } private double _pumpHead = 50.0; // metros private double _maxFlow = 0.01; // m³/s (36 m³/h) private double _speedRatio = 1.0; private bool _isRunning = true; private int _pumpDirection = 1; private double _currentFlow = 0.0; private double _currentPressure = 0.0; // Propiedades visuales específicas de la bomba [ObservableProperty] [property: Category("🎨 Apariencia")] [property: Description("Color visual de la bomba")] [property: Name("Color")] private Brush colorButton_oculto = Brushes.Blue; [Category("🔧 Bomba Hidráulica")] [DisplayName("Cabeza de bomba")] [Description("Cabeza de la bomba a caudal cero (m)")] public double PumpHead { get => _pumpHead; set { if (SetProperty(ref _pumpHead, Math.Max(0, value))) { InvalidateHydraulicNetwork(); } } } [Category("🔧 Bomba Hidráulica")] [DisplayName("Caudal máximo")] [Description("Caudal máximo de la bomba (m³/s)")] public double MaxFlow { get => _maxFlow; set { // Si el valor es mayor a 1, asumir que está en m³/h y convertir a m³/s double valueInM3s = value > 1.0 ? value / 3600.0 : value; if (SetProperty(ref _maxFlow, Math.Max(0.0001, valueInM3s))) { InvalidateHydraulicNetwork(); Debug.WriteLine($"Bomba {Nombre}: MaxFlow establecido a {_maxFlow:F6} m³/s ({_maxFlow * 3600:F2} m³/h)"); } } } [Category("🔧 Bomba Hidráulica")] [DisplayName("Velocidad relativa")] [Description("Velocidad relativa de la bomba (0.0 a 1.0)")] public double SpeedRatio { get => _speedRatio; set { SetProperty(ref _speedRatio, Math.Max(0.0, Math.Min(1.0, value))); } } [Category("🔧 Bomba Hidráulica")] [DisplayName("Funcionando")] [Description("Indica si la bomba está encendida")] public bool IsRunning { get => _isRunning; set { if (SetProperty(ref _isRunning, value)) { UpdatePumpImage(); } } } private void UpdatePumpImage() { if (IsRunning) ImageSource_oculta = ImageFromPath("/imagenes/pump_run.png"); else ImageSource_oculta = ImageFromPath("/imagenes/pump_stop.png"); } [Category("🔧 Bomba Hidráulica")] [DisplayName("Dirección")] [Description("Dirección de la bomba (+1 o -1)")] [ItemsSource(typeof(PumpDirectionItemsSource))] public int PumpDirection { get => _pumpDirection; set { if (SetProperty(ref _pumpDirection, value == -1 ? -1 : 1)) { InvalidateHydraulicNetwork(); } } } [Category("📊 Estado Actual")] [DisplayName("Caudal actual")] [Description("Caudal actual de la bomba (m³/s)")] [JsonIgnore] public double CurrentFlow { get => _currentFlow; private set => SetProperty(ref _currentFlow, value); } [Category("📊 Estado Actual")] [DisplayName("Presión actual")] [Description("Presión actual en la bomba (Pa)")] [JsonIgnore] public double CurrentPressure { get => _currentPressure; private set => SetProperty(ref _currentPressure, value); } [Category("📊 Estado Actual")] [DisplayName("Presión (bar)")] [Description("Presión actual en la bomba (bar)")] [JsonIgnore] public double CurrentPressureBar => CurrentPressure / 100000.0; [Category("📊 Estado Actual")] [DisplayName("Caudal (L/min)")] [Description("Caudal actual en L/min")] [JsonIgnore] public double CurrentFlowLMin => CurrentFlow * 60000.0; // m³/s a L/min // Constructor y Métodos Base private string nombre = NombreClase(); [Category("🏷️ Identificación")] [Description("Nombre identificativo del objeto")] [Name("Nombre")] public override string Nombre { get => nombre; set => SetProperty(ref nombre, value); } public osHydPump() { Nombre = "Bomba Hidráulica"; Tamano = 0.30f; // Inicializar dimensiones usando las propiedades de osBase Ancho = 0.15f; Alto = 0.10f; Angulo = 0f; // Asegurar que el movimiento esté habilitado Lock_movement = false; ImageSource_oculta = ImageFromPath("/imagenes/pump_stop.png"); } public override void UpdateGeometryStart() { // Se llama cuando inicia la simulación - crear objeto hidráulico si no existe if (SimHydraulicPump == null) { SimHydraulicPump = hydraulicSimulationManager.AddPump(PumpHead, MaxFlow, SpeedRatio, IsRunning, PumpDirection); SimHydraulicPump.SimObjectType = "HydraulicPump"; SimHydraulicPump.WpfObject = this; SimHydraulicPump.Nombre = Nombre; } else { // Actualizar propiedades si el objeto ya existe SimHydraulicPump.PumpHead = PumpHead; SimHydraulicPump.MaxFlow = MaxFlow; SimHydraulicPump.SpeedRatio = SpeedRatio; SimHydraulicPump.IsRunning = IsRunning; SimHydraulicPump.PumpDirection = PumpDirection; } } public override void UpdateGeometryStep() { // Los objetos hidráulicos actualizan sus resultados // a través de ApplySimulationResults() desde HydraulicSimulationManager } public override void UpdateControl(int elapsedMilliseconds) { // Actualizar propiedades desde la simulación hidráulica if (SimHydraulicPump != null) { CurrentFlow = SimHydraulicPump.CurrentFlow; CurrentPressure = SimHydraulicPump.CurrentPressure; // Actualizar el color según el estado if (IsRunning) ColorButton_oculto = Brushes.Green; else ColorButton_oculto = Brushes.Gray; } } public override void ucLoaded() { // Objeto hidráulico se crea en UpdateGeometryStart cuando inicia la simulación base.ucLoaded(); UpdatePumpImage(); } public override void ucUnLoaded() { // Remover objeto hidráulico de la simulación if (SimHydraulicPump != null) { hydraulicSimulationManager.Remove(SimHydraulicPump); SimHydraulicPump = null; } } // IHydraulicComponent Implementation [JsonIgnore] public bool HasHydraulicComponents => true; public List GetHydraulicNodes() { // Nodo de entrada con presión fija (simula tanque de succión infinito) double suctionPressure = 101325.0; // Pa (1 atm) Debug.WriteLine($"Bomba {Nombre}: Nodo de succión creado - {suctionPressure:F0} Pa (1,01 bar)"); var nodes = new List { new HydraulicNodeDefinition($"{Nombre}_In", true, suctionPressure, "Entrada de la bomba (succión)"), new HydraulicNodeDefinition($"{Nombre}_Out", false, null, "Salida de la bomba") }; return nodes; } public List GetHydraulicElements() { var elements = new List(); if (HasHydraulicComponents) { // Calcular velocidad efectiva basada en el estado de la bomba double effectiveSpeed = IsRunning ? SpeedRatio : 0.0; // Asegurar que la velocidad esté en rango válido effectiveSpeed = Math.Max(0.0, Math.Min(1.0, effectiveSpeed)); // Crear bomba con parámetros actuales var pump = new PumpHQ( h0: PumpHead, q0: MaxFlow, speedRel: effectiveSpeed, direction: PumpDirection ); var pumpElement = new HydraulicElementDefinition( $"{Nombre}_Pump", $"{Nombre}_In", $"{Nombre}_Out", pump, $"Bomba {Nombre}" ); elements.Add(pumpElement); Debug.WriteLine($"Bomba {Nombre}: Creando elemento hidráulico - H0={PumpHead}m, Q0={MaxFlow:F6}m³/s ({MaxFlow*3600:F2}m³/h), Velocidad={effectiveSpeed:F2}, Dirección={PumpDirection}"); } return elements; } public void UpdateHydraulicProperties() { // Aquí se pueden hacer validaciones o ajustes antes de la simulación if (!IsRunning) { SpeedRatio = 0.0; } if (SpeedRatio < 0.0) SpeedRatio = 0.0; if (SpeedRatio > 1.0) SpeedRatio = 1.0; Debug.WriteLine($"Bomba {Nombre}: Velocidad={SpeedRatio:F2}, Funcionando={IsRunning}"); } public void ApplyHydraulicResults(Dictionary flows, Dictionary pressures) { // Buscar resultados para esta bomba string pumpBranchName = $"{Nombre}_Pump"; string inletNodeName = $"{Nombre}_In"; string outletNodeName = $"{Nombre}_Out"; if (flows.ContainsKey(pumpBranchName)) { CurrentFlow = flows[pumpBranchName]; //Debug.WriteLine($"Bomba {Nombre}: Aplicando caudal={CurrentFlow:F6} m³/s ({CurrentFlowLMin:F2} L/min)"); } if (pressures.ContainsKey(inletNodeName)) { CurrentPressure = pressures[inletNodeName]; //Debug.WriteLine($"Bomba {Nombre}: Presión entrada={CurrentPressure:F2} Pa ({CurrentPressureBar:F2} bar)"); } else if (pressures.ContainsKey(outletNodeName)) { CurrentPressure = pressures[outletNodeName]; //Debug.WriteLine($"Bomba {Nombre}: Presión salida={CurrentPressure:F2} Pa ({CurrentPressureBar:F2} bar)"); } // Disparar PropertyChanged para actualizar el UI OnPropertyChanged(nameof(CurrentFlow)); OnPropertyChanged(nameof(CurrentFlowLMin)); OnPropertyChanged(nameof(CurrentPressure)); OnPropertyChanged(nameof(CurrentPressureBar)); } // IHydraulicFlowReceiver Implementation public void SetFlow(double flow) { CurrentFlow = flow; } public double GetFlow() { return CurrentFlow; } // IHydraulicPressureReceiver Implementation public void SetPressure(double pressure) { CurrentPressure = pressure; } public double GetPressure() { return CurrentPressure; } // Helper Methods private void InvalidateHydraulicNetwork() { hydraulicSimulationManager?.InvalidateNetwork(); } } /// /// Proveedor de items para la dirección de la bomba /// public class PumpDirectionItemsSource : IItemsSource { public Xceed.Wpf.Toolkit.PropertyGrid.Attributes.ItemCollection GetValues() { var items = new Xceed.Wpf.Toolkit.PropertyGrid.Attributes.ItemCollection(); items.Add(1, "Adelante (+1)"); items.Add(-1, "Atrás (-1)"); return items; } } }