409 lines
14 KiB
C#
409 lines
14 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Diagnostics;
|
||
using System.Linq;
|
||
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;
|
||
using LibS7Adv;
|
||
|
||
namespace CtrEditor.ObjetosSim.HydraulicComponents
|
||
{
|
||
/// <summary>
|
||
/// Tanque de descarga hidráulico con cálculo de nivel dinámico
|
||
/// </summary>
|
||
public partial class osHydDischargeTank : osBase, IHydraulicTank, IosBase
|
||
{
|
||
#region Properties
|
||
|
||
private double _area = 1.0; // m²
|
||
private double _currentVolume = 0.5; // m³
|
||
private double _maxVolume = 2.0; // m³
|
||
private double _minVolume = 0.0; // m³
|
||
private double _currentHeight = 0.0;
|
||
private double _currentPressure = 0.0;
|
||
|
||
// Propiedades visuales
|
||
[ObservableProperty]
|
||
[property: Category("🎨 Apariencia")]
|
||
[property: Description("Ancho visual del tanque en metros")]
|
||
[property: Name("Ancho")]
|
||
private float ancho = 0.5f;
|
||
|
||
[ObservableProperty]
|
||
[property: Category("🎨 Apariencia")]
|
||
[property: Description("Alto visual del tanque en metros")]
|
||
[property: Name("Alto")]
|
||
private float alto = 0.8f;
|
||
|
||
[ObservableProperty]
|
||
[property: Category("🎨 Apariencia")]
|
||
[property: Description("Color visual del tanque")]
|
||
[property: Name("Color")]
|
||
private Brush colorButton_oculto = Brushes.LightBlue;
|
||
|
||
[ObservableProperty]
|
||
[property: Category("🎨 Apariencia")]
|
||
[property: Description("Ángulo de rotación del tanque en grados")]
|
||
[property: Name("Ángulo")]
|
||
private float angulo = 0f;
|
||
|
||
[Category("🔧 Tanque Hidráulico")]
|
||
[Description("Área de la base del tanque en m² (alias para compatibilidad)")]
|
||
[Name("Área Base (m²)")]
|
||
public double Area
|
||
{
|
||
get => _area;
|
||
set => SetProperty(ref _area, Math.Max(0.1, value));
|
||
}
|
||
|
||
[Category("🔧 Tanque Hidráulico")]
|
||
[Description("Presión del tanque en Pa")]
|
||
[Name("Presión Tanque (Pa)")]
|
||
public double TankPressure
|
||
{
|
||
get => _currentPressure;
|
||
set => SetProperty(ref _currentPressure, value);
|
||
}
|
||
|
||
[Category("🔧 Tanque Hidráulico")]
|
||
[Description("Nivel actual del tanque en metros")]
|
||
[Name("Nivel (m)")]
|
||
public double Level
|
||
{
|
||
get => _currentHeight;
|
||
set
|
||
{
|
||
SetProperty(ref _currentHeight, value);
|
||
CurrentVolume = value * Area;
|
||
}
|
||
}
|
||
|
||
[Category("🔧 Tanque Hidráulico")]
|
||
[Description("Área de la sección transversal del tanque en m²")]
|
||
[Name("Área Sección (m²)")]
|
||
public double CrossSectionalArea
|
||
{
|
||
get => _area;
|
||
set => SetProperty(ref _area, Math.Max(0.1, value));
|
||
}
|
||
|
||
[Category("🔧 Tanque Hidráulico")]
|
||
[Description("Indica si el tanque tiene presión fija")]
|
||
[Name("Presión Fija")]
|
||
[ReadOnly(true)]
|
||
public bool IsFixedPressure => false; // Los tanques de descarga tienen presión calculada
|
||
|
||
[Category("🔧 Tanque Hidráulico")]
|
||
[Description("Volumen actual del tanque en m³")]
|
||
[Name("Volumen Actual (m³)")]
|
||
public double CurrentVolume
|
||
{
|
||
get => _currentVolume;
|
||
set => SetProperty(ref _currentVolume, Math.Max(MinVolume, Math.Min(MaxVolume, value)));
|
||
}
|
||
|
||
[Category("🔧 Tanque Hidráulico")]
|
||
[Description("Volumen máximo del tanque en m³")]
|
||
[Name("Volumen Máximo (m³)")]
|
||
public double MaxVolume
|
||
{
|
||
get => _maxVolume;
|
||
set => SetProperty(ref _maxVolume, Math.Max(CurrentVolume, value));
|
||
}
|
||
|
||
[Category("🔧 Tanque Hidráulico")]
|
||
[Description("Volumen mínimo del tanque en m³")]
|
||
[Name("Volumen Mínimo (m³)")]
|
||
public double MinVolume
|
||
{
|
||
get => _minVolume;
|
||
set => SetProperty(ref _minVolume, Math.Max(0.0, Math.Min(CurrentVolume, value)));
|
||
}
|
||
|
||
[ObservableProperty]
|
||
[property: Category("🔗 Conexiones")]
|
||
[property: Description("Componente conectado en la entrada")]
|
||
[property: Name("Componente Entrada")]
|
||
[property: ItemsSource(typeof(osBaseItemsSource<IHydraulicComponent>))]
|
||
private string id_InletComponent = "";
|
||
|
||
[Category("📊 Estado")]
|
||
[Description("Altura actual del líquido en metros")]
|
||
[Name("Altura Actual (m)")]
|
||
[ReadOnly(true)]
|
||
public double CurrentHeight
|
||
{
|
||
get => _currentHeight;
|
||
set => SetProperty(ref _currentHeight, value);
|
||
}
|
||
|
||
[Category("📊 Estado")]
|
||
[Description("Presión hidrostática en el fondo del tanque en Pa")]
|
||
[Name("Presión Fondo (Pa)")]
|
||
[ReadOnly(true)]
|
||
public double CurrentPressure
|
||
{
|
||
get => _currentPressure;
|
||
set => SetProperty(ref _currentPressure, value);
|
||
}
|
||
|
||
[Category("📊 Estado")]
|
||
[Description("Porcentaje de llenado del tanque")]
|
||
[Name("Nivel (%)")]
|
||
[ReadOnly(true)]
|
||
public double FillPercentage => MaxVolume > 0 ? (CurrentVolume / MaxVolume) * 100.0 : 0.0;
|
||
|
||
#endregion
|
||
|
||
#region Component References
|
||
|
||
[JsonIgnore]
|
||
private IHydraulicComponent InletComponent = null;
|
||
|
||
[JsonIgnore]
|
||
private PropertyChangedEventHandler inletPropertyChangedHandler;
|
||
|
||
[JsonIgnore]
|
||
public Action<float, float> ActualizarTamaño { get; set; }
|
||
|
||
#endregion
|
||
|
||
#region IHydraulicComponent Implementation
|
||
|
||
public bool HasHydraulicComponents => true;
|
||
|
||
public List<HydraulicNodeDefinition> GetHydraulicNodes()
|
||
{
|
||
var nodes = new List<HydraulicNodeDefinition>();
|
||
|
||
// El tanque de descarga debe ser un nodo de presión fija para servir como referencia del sistema
|
||
// Presión = presión atmosférica + presión hidrostática
|
||
double pressureReference = 101325.0 + TankPressure; // Pa (1 atm + presión hidrostática)
|
||
nodes.Add(new HydraulicNodeDefinition(Nombre, true, pressureReference, "Tanque de descarga (referencia)"));
|
||
|
||
Debug.WriteLine($"Tanque {Nombre}: Nodo de presión fija creado - {pressureReference:F0} Pa ({pressureReference/100000:F2} bar)");
|
||
|
||
return nodes;
|
||
}
|
||
|
||
public List<HydraulicElementDefinition> GetHydraulicElements()
|
||
{
|
||
// Los tanques no son elementos, son nodos
|
||
return new List<HydraulicElementDefinition>();
|
||
}
|
||
|
||
public void UpdateHydraulicProperties()
|
||
{
|
||
// Actualizar presión basada en el nivel actual
|
||
UpdateHeightAndPressure();
|
||
}
|
||
|
||
public void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures)
|
||
{
|
||
// Actualizar presión del tanque
|
||
if (pressures.TryGetValue(Nombre, out double pressure))
|
||
{
|
||
TankPressure = pressure;
|
||
// Calcular nivel basado en la presión hidrostática
|
||
Level = pressure / (1000.0 * 9.81); // Asumiendo densidad del agua
|
||
}
|
||
|
||
// Calcular flujo neto hacia el tanque
|
||
double netFlow = 0.0;
|
||
foreach (var flow in flows)
|
||
{
|
||
if (flow.Key.EndsWith($"->{Nombre}"))
|
||
netFlow += flow.Value; // Flujo entrante
|
||
else if (flow.Key.StartsWith($"{Nombre}->"))
|
||
netFlow -= flow.Value; // Flujo saliente
|
||
}
|
||
|
||
// Aquí podrías actualizar el volumen en función del tiempo
|
||
// Esto se haría en el update loop principal de la simulación
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region IHydraulicPressureReceiver Implementation
|
||
|
||
public void SetPressure(double pressure)
|
||
{
|
||
TankPressure = pressure;
|
||
// Actualizar nivel basado en presión hidrostática
|
||
Level = pressure / (1000.0 * 9.81);
|
||
}
|
||
|
||
public double GetPressure()
|
||
{
|
||
return TankPressure;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Connection Management
|
||
|
||
partial void OnId_InletComponentChanged(string value)
|
||
{
|
||
if (InletComponent != null && inletPropertyChangedHandler != null)
|
||
((INotifyPropertyChanged)InletComponent).PropertyChanged -= inletPropertyChangedHandler;
|
||
|
||
if (_mainViewModel != null && !string.IsNullOrEmpty(value))
|
||
{
|
||
InletComponent = (IHydraulicComponent)_mainViewModel.ObjetosSimulables
|
||
.FirstOrDefault(s => s is IHydraulicComponent comp &&
|
||
((osBase)comp).Nombre == value);
|
||
|
||
if (InletComponent != null)
|
||
{
|
||
inletPropertyChangedHandler = (sender, e) =>
|
||
{
|
||
if (e.PropertyName == nameof(osBase.Nombre))
|
||
{
|
||
Id_InletComponent = ((osBase)sender).Nombre;
|
||
}
|
||
};
|
||
((INotifyPropertyChanged)InletComponent).PropertyChanged += inletPropertyChangedHandler;
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Volume and Level Calculations
|
||
|
||
private void UpdateHeightAndPressure()
|
||
{
|
||
Level = Area > 0 ? CurrentVolume / Area : 0.0;
|
||
// Presión hidrostática en el fondo del tanque (densidad del agua = 1000 kg/m³)
|
||
TankPressure = 1000.0 * 9.81 * Level;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Actualiza el volumen del tanque basado en flujos netos según la documentación
|
||
/// </summary>
|
||
/// <param name="flows">Diccionario de flujos de la simulación</param>
|
||
/// <param name="deltaTime">Intervalo de tiempo en segundos</param>
|
||
public void UpdateVolume(Dictionary<string, double> flows, double deltaTime)
|
||
{
|
||
double netFlow = 0.0;
|
||
|
||
// Sumar flujos entrantes, restar salientes
|
||
foreach (var flow in flows)
|
||
{
|
||
if (flow.Key.EndsWith($"->{Nombre}"))
|
||
netFlow += flow.Value; // Entrante
|
||
else if (flow.Key.StartsWith($"{Nombre}->"))
|
||
netFlow -= flow.Value; // Saliente
|
||
}
|
||
|
||
// Actualizar volumen
|
||
CurrentVolume += netFlow * deltaTime;
|
||
CurrentVolume = Math.Max(MinVolume, Math.Min(MaxVolume, CurrentVolume));
|
||
|
||
// Actualizar altura y presión
|
||
UpdateHeightAndPressure();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Calcula la presión en el fondo del tanque según la documentación
|
||
/// </summary>
|
||
/// <param name="density">Densidad del fluido (kg/m³)</param>
|
||
/// <returns>Presión hidrostática en Pa</returns>
|
||
public double BottomPressure(double density = 1000.0)
|
||
{
|
||
return density * 9.81 * Level; // ρgh
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region osBase Implementation
|
||
|
||
public static string NombreCategoria() => "Componentes Hidráulicos";
|
||
|
||
public static string NombreClase() => "Tanque de Descarga";
|
||
|
||
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 AltoChanged(float value)
|
||
{
|
||
ActualizarGeometrias();
|
||
}
|
||
|
||
public override void AnchoChanged(float value)
|
||
{
|
||
ActualizarGeometrias();
|
||
}
|
||
|
||
public void Start()
|
||
{
|
||
// Los tanques no participan en la simulación física Bepu
|
||
// Solo en el sistema hidráulico
|
||
UpdateHeightAndPressure();
|
||
}
|
||
|
||
public override void UpdateGeometryStart()
|
||
{
|
||
ActualizarGeometrias();
|
||
}
|
||
|
||
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
|
||
{
|
||
// Los tanques pueden tener sensores de nivel
|
||
// Implementar aquí si es necesario
|
||
}
|
||
|
||
private void ActualizarGeometrias()
|
||
{
|
||
try
|
||
{
|
||
// Actualizar geometría visual
|
||
if (this.ActualizarTamaño != null)
|
||
this.ActualizarTamaño(Ancho, Alto);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"Error actualizando geometría: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
public void Inicializar(int valorInicial)
|
||
{
|
||
UpdateHeightAndPressure();
|
||
OnId_InletComponentChanged(Id_InletComponent);
|
||
}
|
||
|
||
public void Disposing()
|
||
{
|
||
if (InletComponent != null && inletPropertyChangedHandler != null)
|
||
((INotifyPropertyChanged)InletComponent).PropertyChanged -= inletPropertyChangedHandler;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Constructor
|
||
|
||
public osHydDischargeTank()
|
||
{
|
||
UpdateHeightAndPressure();
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|