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.HydraulicComponents { /// /// Bomba hidráulica que implementa las interfaces del simulador hidráulico /// public partial class osHydPump : osBase, IHydraulicPump, IosBase { #region Properties 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 [ObservableProperty] [property: Category("🎨 Apariencia")] [property: Description("Ancho de la bomba en metros")] [property: Name("Ancho")] private float ancho = 0.15f; [ObservableProperty] [property: Category("🎨 Apariencia")] [property: Description("Alto de la bomba en metros")] [property: Name("Alto")] private float alto = 0.10f; [ObservableProperty] [property: Category("🎨 Apariencia")] [property: Description("Color visual de la bomba")] [property: Name("Color")] private Brush colorButton_oculto = Brushes.Blue; [ObservableProperty] [property: Category("🎨 Apariencia")] [property: Description("Ángulo de rotación de la bomba en grados")] [property: Name("Ángulo")] private float angulo = 0f; [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 { if (SetProperty(ref _maxFlow, Math.Max(0.001, value))) { InvalidateHydraulicNetwork(); } } } [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 { SetProperty(ref _isRunning, value); } } [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 #endregion #region 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 override void OnMove(float LeftPixels, float TopPixels) { // Los objetos hidráulicos no necesitan actualizar geometría física } public override void OnResize(float Delta_Width, float Delta_Height) { Ancho += Delta_Width; Alto += Delta_Height; } public osHydPump() { Nombre = "Bomba Hidráulica"; } public override void UpdateGeometryStart() { // Los objetos hidráulicos se registran automáticamente // cuando inicia la simulación a través de las interfaces } public override void UpdateGeometryStep() { // Los objetos hidráulicos actualizan sus resultados // a través de ApplyHydraulicResults() } public override void UpdateControl(int elapsedMilliseconds) { // Actualizar el color según el estado if (IsRunning) ColorButton_oculto = Brushes.Green; else ColorButton_oculto = Brushes.Gray; } public override void ucLoaded() { // Los objetos hidráulicos no necesitan geometría física base.ucLoaded(); } public override void ucUnLoaded() { // Los objetos hidráulicos no tienen geometría que limpiar } #endregion #region IHydraulicComponent Implementation [JsonIgnore] public bool HasHydraulicComponents => true; public List GetHydraulicNodes() { var nodes = new List { new HydraulicNodeDefinition($"{Nombre}_In", false, null, "Entrada de la bomba"), new HydraulicNodeDefinition($"{Nombre}_Out", false, null, "Salida de la bomba") }; return nodes; } public List GetHydraulicElements() { var elements = new List(); if (HasHydraulicComponents) { // Crear bomba con parámetros actuales var pump = new PumpHQ( h0: PumpHead, q0: MaxFlow, speedRel: IsRunning ? SpeedRatio : 0.0, // Si no está funcionando, velocidad = 0 direction: PumpDirection ); var pumpElement = new HydraulicElementDefinition( $"{Nombre}_Pump", $"{Nombre}_In", $"{Nombre}_Out", pump, $"Bomba {Nombre}" ); elements.Add(pumpElement); } 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]; } if (pressures.ContainsKey(inletNodeName)) { CurrentPressure = pressures[inletNodeName]; } else if (pressures.ContainsKey(outletNodeName)) { CurrentPressure = pressures[outletNodeName]; } } #endregion #region IHydraulicFlowReceiver Implementation public void SetFlow(double flow) { CurrentFlow = flow; } public double GetFlow() { return CurrentFlow; } #endregion #region IHydraulicPressureReceiver Implementation public void SetPressure(double pressure) { CurrentPressure = pressure; } public double GetPressure() { return CurrentPressure; } #endregion #region Helper Methods /// /// Invalida la red hidráulica para forzar reconstrucción /// private void InvalidateHydraulicNetwork() { // Si tenemos acceso al MainViewModel, invalidar la red if (_mainViewModel?.hydraulicSimulationManager != null) { _mainViewModel.hydraulicSimulationManager.InvalidateNetwork(); } } #endregion #region Static Interface Implementation public static string NombreClase() => "Bomba Hidráulica"; public static string NombreCategoria() => "Componentes Hidráulicos"; #endregion } /// /// 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; } } }