812 lines
30 KiB
C#
812 lines
30 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Bomba hidráulica que implementa las interfaces del simulador hidráulico
|
|
/// </summary>
|
|
public partial class osHydPump : osBase, IHydraulicComponent, 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
|
|
|
|
[Category("📊 Estado Actual")]
|
|
[DisplayName("NPSH Disponible")]
|
|
[Description("NPSH disponible calculado (m)")]
|
|
[JsonIgnore]
|
|
public double NPSHAvailable
|
|
{
|
|
get
|
|
{
|
|
var (inletNodeName, _) = GetConnectedNodeNames();
|
|
if (!string.IsNullOrEmpty(inletNodeName) &&
|
|
hydraulicSimulationManager?.LastSolutionResult?.Pressures?.ContainsKey(inletNodeName) == true)
|
|
{
|
|
var suctionPressure = hydraulicSimulationManager.LastSolutionResult.Pressures[inletNodeName];
|
|
var pump = new PumpHQ(0, 0); // Solo para usar el método de cálculo
|
|
return pump.CalculateNPSHAvailable(suctionPressure, hydraulicSimulationManager.SimulationFluid);
|
|
}
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
[Category("📊 Estado Actual")]
|
|
[DisplayName("Factor Cavitación")]
|
|
[Description("Factor de cavitación (1=sin cavitación, 0=cavitación total)")]
|
|
[JsonIgnore]
|
|
public double CavitationFactor
|
|
{
|
|
get
|
|
{
|
|
var (inletNodeName, _) = GetConnectedNodeNames();
|
|
if (!string.IsNullOrEmpty(inletNodeName) &&
|
|
hydraulicSimulationManager?.LastSolutionResult?.Pressures?.ContainsKey(inletNodeName) == true)
|
|
{
|
|
var suctionPressure = hydraulicSimulationManager.LastSolutionResult.Pressures[inletNodeName];
|
|
var pump = new PumpHQ(0, 0); // Solo para usar el método de cálculo
|
|
return pump.GetCavitationFactor(suctionPressure, hydraulicSimulationManager.SimulationFluid);
|
|
}
|
|
return 1.0;
|
|
}
|
|
}
|
|
|
|
[Category("📊 Estado Actual")]
|
|
[DisplayName("Puede Operar")]
|
|
[Description("Indica si la bomba puede operar sin cavitación")]
|
|
[JsonIgnore]
|
|
public bool CanOperateWithoutCavitation
|
|
{
|
|
get
|
|
{
|
|
var (inletNodeName, _) = GetConnectedNodeNames();
|
|
if (!string.IsNullOrEmpty(inletNodeName) &&
|
|
hydraulicSimulationManager?.LastSolutionResult?.Pressures?.ContainsKey(inletNodeName) == true)
|
|
{
|
|
var suctionPressure = hydraulicSimulationManager.LastSolutionResult.Pressures[inletNodeName];
|
|
var pump = new PumpHQ(0, 0); // Solo para usar el método de cálculo
|
|
return pump.CanOperateWithoutCavitation(suctionPressure, hydraulicSimulationManager.SimulationFluid);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
[Category("📊 Estado Actual")]
|
|
[DisplayName("Estado Cavitación")]
|
|
[Description("Estado actual de cavitación de la bomba")]
|
|
[JsonIgnore]
|
|
public string CavitationStatus
|
|
{
|
|
get
|
|
{
|
|
if (!IsRunning) return "Bomba detenida";
|
|
|
|
var factor = CavitationFactor;
|
|
var canOperate = CanOperateWithoutCavitation;
|
|
var npshAvailable = NPSHAvailable;
|
|
|
|
if (canOperate && factor >= 0.9)
|
|
return "✅ Operación normal";
|
|
else if (factor >= 0.5)
|
|
return $"⚠️ Riesgo cavitación (Factor: {factor:F2})";
|
|
else
|
|
return $"❌ NPSH insuficiente ({npshAvailable:F2}m)";
|
|
}
|
|
}
|
|
|
|
[Category("📊 Estado Actual")]
|
|
[DisplayName("Estado Detallado")]
|
|
[Description("Información detallada del estado operacional")]
|
|
[JsonIgnore]
|
|
public string DetailedStatus
|
|
{
|
|
get
|
|
{
|
|
if (!IsRunning) return "Bomba detenida";
|
|
|
|
var factor = CavitationFactor;
|
|
var npshAvailable = NPSHAvailable;
|
|
var flow = CurrentFlowLMin;
|
|
|
|
return $"NPSH: {npshAvailable:F2}m | Factor: {factor:F2} | Flujo: {flow:F1} L/min";
|
|
}
|
|
}
|
|
|
|
// Propiedades de fluido actual
|
|
private FluidProperties _currentFluid = new FluidProperties(FluidType.Air);
|
|
|
|
[Category("🧪 Fluido Actual")]
|
|
[DisplayName("Tipo de fluido")]
|
|
[Description("Tipo de fluido que está bombeando")]
|
|
[ReadOnly(true)]
|
|
public FluidType CurrentFluidType => _currentFluid.Type;
|
|
|
|
[Category("🧪 Fluido Actual")]
|
|
[DisplayName("Descripción")]
|
|
[Description("Descripción del fluido que se está bombeando")]
|
|
[ReadOnly(true)]
|
|
public string CurrentFluidDescription => _currentFluid.Description;
|
|
|
|
[Category("📊 Estado Actual")]
|
|
[DisplayName("Factor Viscosidad")]
|
|
[Description("Factor de eficiencia debido a la viscosidad del fluido")]
|
|
[ReadOnly(true)]
|
|
public double ViscosityEffect
|
|
{
|
|
get
|
|
{
|
|
// Factor basado en viscosidad del fluido vs agua
|
|
var waterViscosity = 0.001; // Pa·s
|
|
var currentViscosity = _currentFluid.Viscosity;
|
|
return Math.Max(0.1, Math.Min(1.0, waterViscosity / currentViscosity));
|
|
}
|
|
}
|
|
|
|
[Category("📊 Estado Actual")]
|
|
[DisplayName("Flujo Efectivo (L/min)")]
|
|
[Description("Flujo real ajustado por viscosidad")]
|
|
[ReadOnly(true)]
|
|
public double EffectiveFlowLMin => CurrentFlowLMin * ViscosityEffect;
|
|
|
|
[Category("📊 Estado Actual")]
|
|
[DisplayName("Temperatura (°C)")]
|
|
[Description("Temperatura del fluido bombeado")]
|
|
[ReadOnly(true)]
|
|
public double FluidTemperature => _currentFluid.Temperature;
|
|
|
|
[Category("📊 Estado Actual")]
|
|
[DisplayName("Densidad (kg/L)")]
|
|
[Description("Densidad del fluido bombeado")]
|
|
[ReadOnly(true)]
|
|
public double FluidDensity => _currentFluid.Density;
|
|
|
|
[Category("📊 Estado Actual")]
|
|
[DisplayName("Brix (%)")]
|
|
[Description("Concentración en grados Brix (para jarabes)")]
|
|
[ReadOnly(true)]
|
|
public double FluidBrix => _currentFluid.Brix;
|
|
|
|
[Category("📊 Estado Actual")]
|
|
[DisplayName("Concentración (%)")]
|
|
[Description("Concentración en porcentaje (para químicos)")]
|
|
[ReadOnly(true)]
|
|
public double FluidConcentration => _currentFluid.Concentration;
|
|
|
|
[Category("📊 Estado Actual")]
|
|
[DisplayName("Viscosidad (cP)")]
|
|
[Description("Viscosidad dinámica en centipoise")]
|
|
[ReadOnly(true)]
|
|
public double FluidViscosity => _currentFluid.Viscosity;
|
|
|
|
[Category("📊 Estado Actual")]
|
|
[DisplayName("Descripción del Fluido")]
|
|
[Description("Descripción completa del fluido")]
|
|
[ReadOnly(true)]
|
|
public string FluidDescription => _currentFluid.Description;
|
|
|
|
|
|
|
|
// 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
|
|
}
|
|
|
|
// Método para actualizar fluido desde tanque de succión
|
|
public void UpdateFluidFromSuction()
|
|
{
|
|
if (_mainViewModel?.ObjetosSimulables == null) return;
|
|
|
|
try
|
|
{
|
|
// Buscar el componente conectado en la succión (entrada de la bomba)
|
|
var suctionComponent = FindSuctionComponent();
|
|
|
|
if (suctionComponent is osHydTank tank)
|
|
{
|
|
var sourceFluid = tank.CurrentOutputFluid;
|
|
if (sourceFluid != null)
|
|
{
|
|
_currentFluid = sourceFluid.Clone();
|
|
OnPropertyChanged(nameof(CurrentFluidType));
|
|
OnPropertyChanged(nameof(CurrentFluidDescription));
|
|
OnPropertyChanged(nameof(ViscosityEffect));
|
|
OnPropertyChanged(nameof(EffectiveFlowLMin));
|
|
OnPropertyChanged(nameof(FluidTemperature));
|
|
OnPropertyChanged(nameof(FluidDensity));
|
|
|
|
// Actualizar color de la bomba según el fluido
|
|
UpdatePumpColorFromFluid();
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Mantener fluido por defecto en caso de error
|
|
System.Diagnostics.Debug.WriteLine($"Error updating pump fluid: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void UpdatePumpColorFromFluid()
|
|
{
|
|
if (!IsRunning)
|
|
{
|
|
ColorButton_oculto = Brushes.Gray;
|
|
return;
|
|
}
|
|
|
|
// Si no hay flujo, verificar cavitación primero
|
|
if (Math.Abs(CurrentFlow) < 1e-6)
|
|
{
|
|
if (hydraulicSimulationManager?.EnableNPSHVerification == true)
|
|
{
|
|
var cavitationFactor = CavitationFactor;
|
|
if (cavitationFactor < 0.1)
|
|
ColorButton_oculto = Brushes.Red; // Cavitación severa
|
|
else if (cavitationFactor < 0.5)
|
|
ColorButton_oculto = Brushes.Orange; // Riesgo de cavitación
|
|
else
|
|
ColorButton_oculto = Brushes.Yellow; // Standby
|
|
}
|
|
else
|
|
{
|
|
ColorButton_oculto = Brushes.Yellow; // Standby
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Colorear según tipo de fluido bombeado
|
|
try
|
|
{
|
|
var fluidColorHex = _currentFluid.Color;
|
|
var fluidColor = (Color)System.Windows.Media.ColorConverter.ConvertFromString(fluidColorHex);
|
|
ColorButton_oculto = new SolidColorBrush(fluidColor);
|
|
}
|
|
catch
|
|
{
|
|
ColorButton_oculto = Brushes.Green; // Color por defecto si hay flujo
|
|
}
|
|
}
|
|
|
|
private IHydraulicComponent FindSuctionComponent()
|
|
{
|
|
// Buscar tuberías conectadas a esta bomba
|
|
var connectedPipes = _mainViewModel.ObjetosSimulables
|
|
.OfType<osHydPipe>()
|
|
.Where(pipe => pipe.Id_ComponenteA == Nombre || pipe.Id_ComponenteB == Nombre)
|
|
.ToList();
|
|
|
|
foreach (var pipe in connectedPipes)
|
|
{
|
|
// La succión es donde el flujo viene HACIA la bomba
|
|
string suctionComponentName = null;
|
|
|
|
if (pipe.Id_ComponenteB == Nombre && pipe.CurrentFlow >= 0)
|
|
{
|
|
// Flujo de A hacia B (hacia esta bomba)
|
|
suctionComponentName = pipe.Id_ComponenteA;
|
|
}
|
|
else if (pipe.Id_ComponenteA == Nombre && pipe.CurrentFlow < 0)
|
|
{
|
|
// Flujo de B hacia A (hacia esta bomba)
|
|
suctionComponentName = pipe.Id_ComponenteB;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(suctionComponentName))
|
|
{
|
|
return _mainViewModel.ObjetosSimulables
|
|
.OfType<IHydraulicComponent>()
|
|
.FirstOrDefault(c => c is osBase osb && osb.Nombre == suctionComponentName);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public override void UpdateControl(int elapsedMilliseconds)
|
|
{
|
|
// Actualizar propiedades desde la simulación hidráulica
|
|
if (SimHydraulicPump != null)
|
|
{
|
|
CurrentFlow = SimHydraulicPump.CurrentFlow;
|
|
CurrentPressure = SimHydraulicPump.CurrentPressure;
|
|
}
|
|
|
|
// Actualizar propiedades del fluido cada ciclo
|
|
UpdateFluidFromSuction();
|
|
|
|
// Actualizar propiedades de UI
|
|
OnPropertyChanged(nameof(CurrentFlowLMin));
|
|
OnPropertyChanged(nameof(EffectiveFlowLMin));
|
|
|
|
// Actualizar el color según el estado (ya se hace en UpdatePumpColorFromFluid)
|
|
if (!IsRunning)
|
|
{
|
|
ColorButton_oculto = Brushes.Gray; // Bomba apagada
|
|
}
|
|
}
|
|
|
|
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<HydraulicNodeDefinition> GetHydraulicNodes()
|
|
{
|
|
var nodes = new List<HydraulicNodeDefinition>();
|
|
|
|
// Las bombas no crean nodos propios - deben estar conectadas a otros componentes para funcionar
|
|
if (!HasConnectedComponents())
|
|
{
|
|
Debug.WriteLine($"Bomba {Nombre}: Sin componentes conectados - no puede funcionar");
|
|
}
|
|
else
|
|
{
|
|
//Debug.WriteLine($"Bomba {Nombre}: Conectada a otros componentes - lista para operar");
|
|
}
|
|
|
|
return nodes;
|
|
}
|
|
|
|
public List<HydraulicElementDefinition> GetHydraulicElements()
|
|
{
|
|
var elements = new List<HydraulicElementDefinition>();
|
|
|
|
// Solo crear elementos si la bomba está conectada a otros componentes
|
|
if (!HasConnectedComponents())
|
|
{
|
|
Debug.WriteLine($"Bomba {Nombre}: Sin conexiones - no se puede crear elemento hidráulico");
|
|
return elements;
|
|
}
|
|
|
|
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));
|
|
|
|
// Obtener los nombres de nodos correctos basados en las conexiones
|
|
var (inletNode, outletNode) = GetConnectedNodeNames();
|
|
|
|
// Verificar que tenemos nodos válidos
|
|
if (string.IsNullOrEmpty(inletNode) || string.IsNullOrEmpty(outletNode))
|
|
{
|
|
Debug.WriteLine($"Bomba {Nombre}: Nodos de conexión inválidos - inlet: '{inletNode}', outlet: '{outletNode}'");
|
|
return elements;
|
|
}
|
|
|
|
// Crear bomba con parámetros actuales
|
|
Element pump;
|
|
if (hydraulicSimulationManager.EnableNPSHVerification)
|
|
{
|
|
// Usar bomba con verificación de NPSH
|
|
pump = new PumpHQWithSuctionCheck(
|
|
h0: PumpHead,
|
|
q0: MaxFlow,
|
|
speedRel: effectiveSpeed,
|
|
direction: PumpDirection,
|
|
enableNpshCheck: true
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// Usar bomba estándar
|
|
pump = new PumpHQ(
|
|
h0: PumpHead,
|
|
q0: MaxFlow,
|
|
speedRel: effectiveSpeed,
|
|
direction: PumpDirection
|
|
);
|
|
}
|
|
|
|
// Asignar nombres de nodos para verificación de NPSH
|
|
if (pump is PumpHQ pumpHQ)
|
|
{
|
|
pumpHQ.InletNodeName = inletNode;
|
|
pumpHQ.OutletNodeName = outletNode;
|
|
}
|
|
|
|
var pumpElement = new HydraulicElementDefinition(
|
|
$"{Nombre}_Pump",
|
|
inletNode,
|
|
outletNode,
|
|
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}");
|
|
Debug.WriteLine($"Bomba {Nombre}: Conectando {inletNode} -> {outletNode}");
|
|
}
|
|
|
|
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;
|
|
|
|
// Las verificaciones de NPSH ahora se muestran a través de propiedades
|
|
// Se eliminaron los Debug.WriteLine para evitar saturación del sistema
|
|
|
|
//Debug.WriteLine($"Bomba {Nombre}: Velocidad={SpeedRatio:F2}, Funcionando={IsRunning}");
|
|
}
|
|
|
|
public void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures)
|
|
{
|
|
// Buscar resultados para esta bomba
|
|
string pumpBranchName = $"{Nombre}_Pump";
|
|
var (inletNodeName, outletNodeName) = GetConnectedNodeNames();
|
|
|
|
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
|
|
|
|
/// <summary>
|
|
/// Verifica si la bomba tiene componentes conectados a través de tuberías
|
|
/// </summary>
|
|
private bool HasConnectedComponents()
|
|
{
|
|
if (_mainViewModel == null) return false;
|
|
|
|
// Buscar tuberías que conecten esta bomba con otros componentes
|
|
var connectedPipes = _mainViewModel.ObjetosSimulables
|
|
.OfType<osHydPipe>()
|
|
.Where(pipe => pipe.Id_ComponenteA == Nombre || pipe.Id_ComponenteB == Nombre)
|
|
.ToList();
|
|
|
|
return connectedPipes.Any();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Obtiene los nombres de nodos correctos para la bomba basándose en las conexiones
|
|
/// </summary>
|
|
private (string inletNode, string outletNode) GetConnectedNodeNames()
|
|
{
|
|
if (_mainViewModel == null)
|
|
return (string.Empty, string.Empty);
|
|
|
|
string inletNode = string.Empty;
|
|
string outletNode = string.Empty;
|
|
|
|
// Buscar tuberías conectadas a esta bomba
|
|
var connectedPipes = _mainViewModel.ObjetosSimulables
|
|
.OfType<osHydPipe>()
|
|
.Where(pipe => pipe.Id_ComponenteA == Nombre || pipe.Id_ComponenteB == Nombre)
|
|
.ToList();
|
|
|
|
foreach (var pipe in connectedPipes)
|
|
{
|
|
if (pipe.Id_ComponenteB == Nombre && !string.IsNullOrEmpty(pipe.Id_ComponenteA))
|
|
{
|
|
// Esta bomba es el destino, el componente A es la fuente (inlet)
|
|
inletNode = pipe.Id_ComponenteA;
|
|
//Debug.WriteLine($"Bomba {Nombre}: Nodo inlet identificado como '{inletNode}'");
|
|
}
|
|
else if (pipe.Id_ComponenteA == Nombre && !string.IsNullOrEmpty(pipe.Id_ComponenteB))
|
|
{
|
|
// Esta bomba es la fuente, el componente B es el destino (outlet)
|
|
outletNode = pipe.Id_ComponenteB;
|
|
//Debug.WriteLine($"Bomba {Nombre}: Nodo outlet identificado como '{outletNode}'");
|
|
}
|
|
}
|
|
|
|
return (inletNode, outletNode);
|
|
}
|
|
|
|
private void InvalidateHydraulicNetwork()
|
|
{
|
|
hydraulicSimulationManager?.InvalidateNetwork();
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Proveedor de items para la dirección de la bomba
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
}
|