using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Windows; using System.Windows.Media; using CtrEditor.HydraulicSimulator; using CtrEditor.HydraulicSimulator.TSNet.Components; using CtrEditor.ObjetosSim; using CtrEditor.FuncionesBase; using Newtonsoft.Json; using Xceed.Wpf.Toolkit.PropertyGrid.Attributes; using CommunityToolkit.Mvvm.ComponentModel; using HydraulicSimulator.Models; namespace CtrEditor.ObjetosSim { /// /// Dirección de funcionamiento de la bomba /// public enum PumpDirection { Forward = 1, Reverse = -1 } /// /// Bomba hidráulica simplificada para integración con TSNet /// public partial class osHydPump : osBase, IHydraulicComponent, IHydraulicFlowReceiver, IHydraulicPressureReceiver, IosBase { public static string NombreCategoria() => "Componentes Hidráulicos"; public static string NombreClase() => "Bomba Hidráulica"; // Private fields para TSNet private double _pumpHead = 50.0; // m - cabeza de la bomba private double _maxFlow = 0.01; // m³/s - flujo máximo private double _efficiency = 0.85; // eficiencia de la bomba private bool _isRunning = true; // bomba funcionando // Estado actual (desde TSNet) private double _currentFlow = 0.0; // m³/s - flujo actual private double _currentHead = 0.0; // m - cabeza actual private double _currentPressure = 0.0; // Pa - presión actual // TSNet Integration [JsonIgnore] public TSNetPumpAdapter? TSNetAdapter { get; private set; } // Propiedades visuales [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; [ObservableProperty] [property: Category("🎨 Apariencia")] [property: Description("Color visual de la bomba")] [property: Name("Color")] private Brush colorButton_oculto = Brushes.Blue; // Constructor public osHydPump() { try { Nombre = "Bomba Hidráulica"; Tamano = 1.0f; Ancho = 0.25f; Alto = 0.25f; Angulo = 0f; Lock_movement = false; UpdatePumpImage(); Debug.WriteLine($"osHydPump Constructor completado correctamente"); } catch (Exception ex) { Debug.WriteLine($"Error en constructor osHydPump: {ex.Message}"); Nombre = "Bomba Hidráulica"; Tamano = 1.0f; } } // Propiedades de configuración de la bomba [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("Flujo máximo")] [Description("Flujo 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("Eficiencia")] [Description("Eficiencia de la bomba (0.0 a 1.0)")] public double Efficiency { get => _efficiency; set { if (SetProperty(ref _efficiency, Math.Max(0.1, Math.Min(1.0, value)))) { InvalidateHydraulicNetwork(); } } } [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(); InvalidateHydraulicNetwork(); } } } // Estado actual (desde TSNet) [Category("📊 Estado Actual")] [DisplayName("Flujo actual")] [Description("Flujo actual de la bomba (m³/s)")] [ReadOnly(true)] public double CurrentFlow { get => _currentFlow; private set => SetProperty(ref _currentFlow, value); } [Category("📊 Estado Actual")] [DisplayName("Cabeza actual")] [Description("Cabeza actual de la bomba (m)")] [ReadOnly(true)] public double CurrentHead { get => _currentHead; private set => SetProperty(ref _currentHead, value); } [Category("📊 Estado Actual")] [DisplayName("Presión actual")] [Description("Presión actual en la bomba (Pa)")] [ReadOnly(true)] 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)")] [ReadOnly(true)] public double CurrentPressureBar => CurrentPressure / 100000.0; [Category("📊 Estado Actual")] [DisplayName("Flujo (L/min)")] [Description("Flujo actual en L/min")] [ReadOnly(true)] public double CurrentFlowLMin => CurrentFlow * 60000.0; // m³/s a L/min [Category("📊 Estado Actual")] [DisplayName("Tiene Flujo")] [Description("Indica si hay flujo en la bomba")] [ReadOnly(true)] public bool HasFlow => Math.Abs(CurrentFlow) > 1e-6; /// /// Relación de velocidad de la bomba /// [Category("⚙️ Configuración")] [DisplayName("Relación de velocidad")] [Description("Relación de velocidad de la bomba (0.0 - 1.0)")] public double SpeedRatio { get => _speedRatio; set => SetProperty(ref _speedRatio, Math.Max(0.0, Math.Min(1.0, value))); } private double _speedRatio = 1.0; /// /// Dirección de la bomba /// [Category("⚙️ Configuración")] [DisplayName("Dirección bomba")] [Description("Dirección de funcionamiento de la bomba")] public PumpDirection PumpDirection { get => _pumpDirection; set => SetProperty(ref _pumpDirection, value); } private PumpDirection _pumpDirection = PumpDirection.Forward; // Métodos básicos 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) { // Movimiento básico } public override void OnResize(float Delta_Width, float Delta_Height) { Tamano += Delta_Width + Delta_Height; } public override void UpdateGeometryStart() { // El componente se registra automáticamente como IHydraulicComponent } public override void UpdateControl(int elapsedMilliseconds) { // Los valores provienen de TSNet OnPropertyChanged(nameof(CurrentPressureBar)); OnPropertyChanged(nameof(CurrentFlowLMin)); OnPropertyChanged(nameof(HasFlow)); UpdatePumpImage(); } private void UpdatePumpImage() { if (IsRunning && HasFlow) ImageSource_oculta = ImageFromPath("/imagenes/pump_run.png"); else if (IsRunning) ImageSource_oculta = ImageFromPath("/imagenes/pump_idle.png"); else ImageSource_oculta = ImageFromPath("/imagenes/pump_stop.png"); } private void InvalidateHydraulicNetwork() { // Invalidar la red hidráulica para que se recalcule var mainViewModel = Application.Current?.MainWindow?.DataContext as MainViewModel; mainViewModel?.InvalidateHydraulicNetwork(); } // Implementación de interfaces hidráulicas public void ApplyHydraulicPressure(double pressurePa) { CurrentPressure = pressurePa; } public void ApplyHydraulicFlow(double flowM3s) { CurrentFlow = flowM3s; } public void UpdateHydraulicState(double timeSeconds) { // TSNet maneja el estado } // Métodos para TSNet public void UpdateFromTSNetResults(double flow, double head, double pressure) { CurrentFlow = flow; CurrentHead = head; CurrentPressure = pressure; } // Crear adapter para TSNet public TSNetPumpAdapter CreateTSNetAdapter() { TSNetAdapter = new TSNetPumpAdapter(this); return TSNetAdapter; } // Implementación de IHydraulicComponent [JsonIgnore] public bool HasHydraulicComponents => true; public List GetHydraulicNodes() { // Las bombas no crean nodos propios, se conectan entre nodos existentes return new List(); } public void ApplyHydraulicResults(Dictionary flows, Dictionary pressures) { try { // Buscar resultados de esta bomba en TSNet var pumpElementName = $"PUMP_{Nombre}"; if (flows.ContainsKey(pumpElementName)) { var pumpFlow = flows[pumpElementName]; CurrentFlow = pumpFlow; } // Calcular cabeza basada en la diferencia de presión // Esto se podría calcular desde TSNet directamente si está disponible if (Math.Abs(CurrentFlow) > 1e-6) { // Estimación simplificada de la cabeza actual CurrentHead = PumpHead * (1.0 - (CurrentFlow / MaxFlow)); CurrentHead = Math.Max(0, CurrentHead); } else { CurrentHead = IsRunning ? PumpHead : 0; } } catch (Exception ex) { Debug.WriteLine($"Error en Pump {Nombre} ApplyHydraulicResults: {ex.Message}"); } } public List GetHydraulicElements() { var elements = new List(); if (IsRunning) { // Crear elemento de bomba para TSNet var pumpElement = new PumpHQ(PumpHead, MaxFlow); elements.Add(new HydraulicElementDefinition( $"PUMP_{Nombre}", $"NODE_A_{Nombre}", $"NODE_B_{Nombre}", pumpElement, $"Bomba hidráulica - Head: {PumpHead:F1}m, Flow: {MaxFlow * 3600:F1} m³/h" )); } return elements; } public void UpdateHydraulicProperties() { // Actualizar propiedades antes de la simulación if (!IsRunning) { CurrentFlow = 0; CurrentHead = 0; } } // Implementación de IHydraulicFlowReceiver public void SetFlow(double flow) { CurrentFlow = flow; } public double GetFlow() { return CurrentFlow; } // Implementación de IHydraulicPressureReceiver public void SetPressure(double pressure) { CurrentPressure = pressure; } public double GetPressure() { return CurrentPressure; } // Implementación de IosBase public void Start() { // Inicialización de la bomba para la simulación } public void Inicializar(int valorInicial) { // Inicialización con valor inicial } public void Disposing() { // Limpieza si es necesaria } public int zIndex_fromFrames { get; set; } = 2; public ZIndexEnum ZIndex_Base() { return ZIndexEnum.Estaticos; } public override void CopyFrom(osBase source) { if (source is osHydPump sourcePump) { PumpHead = sourcePump.PumpHead; MaxFlow = sourcePump.MaxFlow; Efficiency = sourcePump.Efficiency; IsRunning = sourcePump.IsRunning; } base.CopyFrom(source); } } }