755 lines
28 KiB
C#
755 lines
28 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
|
||
{
|
||
/// <summary>
|
||
/// Tubería hidráulica que conecta componentes del sistema hidráulico
|
||
/// </summary>
|
||
public partial class osHydPipe : osBase, IHydraulicComponent, IHydraulicFlowReceiver, IHydraulicPressureReceiver, IosBase
|
||
{
|
||
// Properties
|
||
|
||
private double _length = 1.0; // metros
|
||
private double _diameter = 0.05; // metros (50mm)
|
||
private double _roughness = 4.5e-5; // metros - rugosidad del acero comercial
|
||
private double _currentFlow = 0.0;
|
||
private double _pressureDrop = 0.0;
|
||
|
||
// Propiedades visuales
|
||
[ObservableProperty]
|
||
[property: Category("🎨 Apariencia")]
|
||
[property: Description("Ancho visual de la tubería en metros")]
|
||
[property: Name("Ancho")]
|
||
private float ancho = 1.0f;
|
||
|
||
[ObservableProperty]
|
||
[property: Category("🎨 Apariencia")]
|
||
[property: Description("Alto visual de la tubería en metros")]
|
||
[property: Name("Alto")]
|
||
private float alto = 0.05f;
|
||
|
||
[ObservableProperty]
|
||
[property: Category("🎨 Apariencia")]
|
||
[property: Description("Color visual de la tubería")]
|
||
[property: Name("Color")]
|
||
private Brush colorButton_oculto = Brushes.Gray;
|
||
|
||
[ObservableProperty]
|
||
[property: Category("🎨 Apariencia")]
|
||
[property: Description("Ángulo de rotación de la tubería en grados")]
|
||
[property: Name("Ángulo")]
|
||
private float angulo = 0f;
|
||
|
||
[Category("🔧 Tubería Hidráulica")]
|
||
[Description("Longitud de la tubería en metros")]
|
||
[Name("Longitud (m)")]
|
||
public double Length
|
||
{
|
||
get => _length;
|
||
set => SetProperty(ref _length, Math.Max(0.1, value));
|
||
}
|
||
|
||
[Category("🔧 Tubería Hidráulica")]
|
||
[Description("Diámetro interno de la tubería en metros")]
|
||
[Name("Diámetro (m)")]
|
||
public double Diameter
|
||
{
|
||
get => _diameter;
|
||
set => SetProperty(ref _diameter, Math.Max(0.001, value));
|
||
}
|
||
|
||
[Category("🔧 Tubería Hidráulica")]
|
||
[Description("Rugosidad del material en metros")]
|
||
[Name("Rugosidad (m)")]
|
||
public double Roughness
|
||
{
|
||
get => _roughness;
|
||
set => SetProperty(ref _roughness, Math.Max(1e-6, value));
|
||
}
|
||
|
||
[ObservableProperty]
|
||
[property: Category("🔗 Conexiones")]
|
||
[property: Description("Primer componente hidráulico conectado")]
|
||
[property: Name("Componente A")]
|
||
[property: ItemsSource(typeof(osBaseItemsSource<IHydraulicComponent>))]
|
||
private string id_ComponenteA = "";
|
||
|
||
[ObservableProperty]
|
||
[property: Category("🔗 Conexiones")]
|
||
[property: Description("Segundo componente hidráulico conectado")]
|
||
[property: Name("Componente B")]
|
||
[property: ItemsSource(typeof(osBaseItemsSource<IHydraulicComponent>))]
|
||
private string id_ComponenteB = "";
|
||
|
||
[Category("📊 Estado")]
|
||
[Description("Flujo actual a través de la tubería en m³/s")]
|
||
[Name("Flujo Actual (m³/s)")]
|
||
[ReadOnly(true)]
|
||
public double CurrentFlow
|
||
{
|
||
get => _currentFlow;
|
||
set => SetProperty(ref _currentFlow, value);
|
||
}
|
||
|
||
[Category("📊 Estado")]
|
||
[Description("Pérdida de presión en la tubería en Pa")]
|
||
[Name("Pérdida Presión (Pa)")]
|
||
[ReadOnly(true)]
|
||
public double PressureDrop
|
||
{
|
||
get => _pressureDrop;
|
||
set => SetProperty(ref _pressureDrop, value);
|
||
}
|
||
|
||
// Propiedades adicionales para información de fluido
|
||
private FluidProperties _currentFluid = new FluidProperties(FluidType.Air);
|
||
|
||
[Category("🧪 Fluido Actual")]
|
||
[DisplayName("Tipo de fluido")]
|
||
[Description("Tipo de fluido que atraviesa la tubería")]
|
||
[ReadOnly(true)]
|
||
public FluidType CurrentFluidType
|
||
{
|
||
get => _currentFluid.Type;
|
||
private set
|
||
{
|
||
if (_currentFluid.Type != value)
|
||
{
|
||
_currentFluid.Type = value;
|
||
OnPropertyChanged();
|
||
OnPropertyChanged(nameof(CurrentFluidDescription));
|
||
}
|
||
}
|
||
}
|
||
|
||
[Category("🧪 Fluido Actual")]
|
||
[DisplayName("Descripción")]
|
||
[Description("Descripción completa del fluido actual")]
|
||
[ReadOnly(true)]
|
||
public string CurrentFluidDescription => _currentFluid.Description;
|
||
|
||
[Category("🧪 Fluido Actual")]
|
||
[DisplayName("Temperatura (°C)")]
|
||
[Description("Temperatura del fluido en grados Celsius")]
|
||
[ReadOnly(true)]
|
||
[JsonProperty]
|
||
public double CurrentFluidTemperature => _currentFluid.Temperature;
|
||
|
||
[Category("🧪 Fluido Actual")]
|
||
[DisplayName("Brix (%)")]
|
||
[Description("Concentración en grados Brix (para jarabes)")]
|
||
[ReadOnly(true)]
|
||
[JsonProperty]
|
||
public double CurrentFluidBrix => _currentFluid.Brix;
|
||
|
||
[Category("🧪 Fluido Actual")]
|
||
[DisplayName("Concentración (%)")]
|
||
[Description("Concentración en porcentaje (para químicos)")]
|
||
[ReadOnly(true)]
|
||
[JsonProperty]
|
||
public double CurrentFluidConcentration => _currentFluid.Concentration;
|
||
|
||
[Category("🧪 Fluido Actual")]
|
||
[DisplayName("Densidad (kg/L)")]
|
||
[Description("Densidad del fluido en kg/L")]
|
||
[ReadOnly(true)]
|
||
[JsonProperty]
|
||
public double CurrentFluidDensity => _currentFluid.Density;
|
||
|
||
[Category("🧪 Fluido Actual")]
|
||
[DisplayName("Viscosidad (cP)")]
|
||
[Description("Viscosidad dinámica en centipoise")]
|
||
[ReadOnly(true)]
|
||
[JsonProperty]
|
||
public double CurrentFluidViscosity => _currentFluid.Viscosity;
|
||
|
||
[Category("🧪 Fluido Actual")]
|
||
[DisplayName("Velocidad (m/s)")]
|
||
[Description("Velocidad del fluido en la tubería")]
|
||
[ReadOnly(true)]
|
||
[JsonProperty]
|
||
public double FluidVelocity
|
||
{
|
||
get
|
||
{
|
||
if (Diameter <= 0) return 0;
|
||
var area = Math.PI * Math.Pow(Diameter / 2, 2); // m²
|
||
return Math.Abs(CurrentFlow) / area; // m/s
|
||
}
|
||
}
|
||
|
||
[Category("🧪 Fluido Actual")]
|
||
[DisplayName("Número de Reynolds")]
|
||
[Description("Número de Reynolds (Re = ρ × v × D / μ) - Caracteriza el régimen de flujo")]
|
||
[ReadOnly(true)]
|
||
[JsonProperty]
|
||
public double ReynoldsNumber
|
||
{
|
||
get
|
||
{
|
||
if (CurrentFluidViscosity <= 0 || Diameter <= 0) return 0;
|
||
|
||
// Re = (ρ × v × D) / μ
|
||
// ρ: densidad (kg/m³) - convertir de kg/L
|
||
// v: velocidad (m/s)
|
||
// D: diámetro (m)
|
||
// μ: viscosidad dinámica (Pa·s) - convertir de cP
|
||
|
||
var density_kg_m3 = CurrentFluidDensity * 1000; // kg/L → kg/m³
|
||
var viscosity_pa_s = CurrentFluidViscosity * 0.001; // cP → Pa·s
|
||
|
||
return (density_kg_m3 * FluidVelocity * Diameter) / viscosity_pa_s;
|
||
}
|
||
}
|
||
|
||
[Category("🧪 Fluido Actual")]
|
||
[DisplayName("Régimen de Flujo")]
|
||
[Description("Tipo de régimen basado en Reynolds (Laminar < 2300 < Turbulento)")]
|
||
[ReadOnly(true)]
|
||
[JsonProperty]
|
||
public string FlowRegime
|
||
{
|
||
get
|
||
{
|
||
var re = ReynoldsNumber;
|
||
return re switch
|
||
{
|
||
< 2300 => $"Laminar (Re = {re:F0})",
|
||
> 4000 => $"Turbulento (Re = {re:F0})",
|
||
_ => $"Transición (Re = {re:F0})"
|
||
};
|
||
}
|
||
}
|
||
|
||
// Método para exponer propiedades detalladas en serialización JSON
|
||
[JsonProperty("DetailedFluidProperties")]
|
||
[Browsable(false)]
|
||
public Dictionary<string, object> DetailedFluidProperties => new Dictionary<string, object>
|
||
{
|
||
["CurrentFluidTemperature"] = CurrentFluidTemperature,
|
||
["CurrentFluidBrix"] = CurrentFluidBrix,
|
||
["CurrentFluidConcentration"] = CurrentFluidConcentration,
|
||
["CurrentFluidDensity"] = CurrentFluidDensity,
|
||
["CurrentFluidViscosity"] = CurrentFluidViscosity,
|
||
["FluidVelocity"] = FluidVelocity,
|
||
["ReynoldsNumber"] = ReynoldsNumber,
|
||
["FlowRegime"] = FlowRegime
|
||
};
|
||
|
||
[Category("📊 Estado")]
|
||
[DisplayName("Flujo (L/min)")]
|
||
[Description("Flujo actual en litros por minuto")]
|
||
[ReadOnly(true)]
|
||
public double CurrentFlowLMin => Math.Abs(CurrentFlow) * 60000.0; // Conversión de m³/s a L/min
|
||
|
||
[Category("📊 Estado")]
|
||
[DisplayName("Dirección")]
|
||
[Description("Dirección del flujo")]
|
||
[ReadOnly(true)]
|
||
public string FlowDirection => CurrentFlow > 0 ? $"{Id_ComponenteA} → {Id_ComponenteB}" :
|
||
CurrentFlow < 0 ? $"{Id_ComponenteB} → {Id_ComponenteA}" : "Sin flujo";
|
||
|
||
[JsonIgnore]
|
||
public SolidColorBrush FluidColor
|
||
{
|
||
get
|
||
{
|
||
try
|
||
{
|
||
var colorHex = _currentFluid.Color;
|
||
return new SolidColorBrush((Color)ColorConverter.ConvertFromString(colorHex));
|
||
}
|
||
catch
|
||
{
|
||
return Brushes.Gray;
|
||
}
|
||
}
|
||
}
|
||
|
||
private string pipeId = "";
|
||
public string PipeId
|
||
{
|
||
get => pipeId;
|
||
set => SetProperty(ref pipeId, value);
|
||
}
|
||
|
||
|
||
|
||
// Component References
|
||
|
||
[JsonIgnore]
|
||
private IHydraulicComponent ComponenteA = null;
|
||
|
||
[JsonIgnore]
|
||
private IHydraulicComponent ComponenteB = null;
|
||
|
||
[JsonIgnore]
|
||
private PropertyChangedEventHandler componenteAPropertyChangedHandler;
|
||
|
||
[JsonIgnore]
|
||
private PropertyChangedEventHandler componenteBPropertyChangedHandler;
|
||
|
||
[JsonIgnore]
|
||
public Action<float, float> ActualizarTamaño { get; set; }
|
||
|
||
|
||
|
||
// IHydraulicPipe Implementation
|
||
|
||
// Ya implementadas las propiedades Length, Diameter, Roughness arriba
|
||
|
||
|
||
|
||
// IHydraulicComponent Implementation
|
||
|
||
public bool HasHydraulicComponents => true;
|
||
|
||
public List<HydraulicNodeDefinition> GetHydraulicNodes()
|
||
{
|
||
var nodes = new List<HydraulicNodeDefinition>();
|
||
|
||
// Las tuberías conectan componentes existentes, no crean nodos propios
|
||
// Los nodos son creados por los componentes conectados (tanques, bombas, etc.)
|
||
// La tubería solo actúa como elemento que conecta nodos existentes
|
||
|
||
return nodes;
|
||
}
|
||
|
||
public List<HydraulicElementDefinition> GetHydraulicElements()
|
||
{
|
||
var elements = new List<HydraulicElementDefinition>();
|
||
|
||
// Solo crear elemento si ambos componentes están conectados
|
||
if (!string.IsNullOrEmpty(Id_ComponenteA) && !string.IsNullOrEmpty(Id_ComponenteB))
|
||
{
|
||
// Obtener los nombres de nodos correctos para cada componente
|
||
string fromNodeName = GetNodeNameForComponent(Id_ComponenteA, true); // true = es el nodo origen
|
||
string toNodeName = GetNodeNameForComponent(Id_ComponenteB, false); // false = es el nodo destino
|
||
|
||
if (!string.IsNullOrEmpty(fromNodeName) && !string.IsNullOrEmpty(toNodeName))
|
||
{
|
||
// Crear el elemento Pipe según la documentación
|
||
var pipeElement = new Pipe(Length, Diameter, Roughness);
|
||
|
||
elements.Add(new HydraulicElementDefinition(
|
||
name: $"{Nombre}_Pipe",
|
||
fromNode: fromNodeName,
|
||
toNode: toNodeName,
|
||
element: pipeElement,
|
||
description: $"Tubería {Nombre} - L:{Length:F2}m, D:{Diameter*1000:F0}mm ({fromNodeName} → {toNodeName})"
|
||
));
|
||
}
|
||
}
|
||
|
||
return elements;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Obtiene el nombre del nodo correcto para un componente dado
|
||
/// </summary>
|
||
/// <param name="componentName">Nombre del componente</param>
|
||
/// <param name="isSource">True si es el nodo origen de la tubería, False si es destino</param>
|
||
/// <returns>Nombre del nodo o string vacío si no se encuentra</returns>
|
||
private string GetNodeNameForComponent(string componentName, bool isSource)
|
||
{
|
||
if (string.IsNullOrEmpty(componentName) || _mainViewModel == null)
|
||
return "";
|
||
|
||
// Buscar el componente hidráulico
|
||
var component = _mainViewModel.ObjetosSimulables
|
||
.FirstOrDefault(s => s is IHydraulicComponent comp &&
|
||
((osBase)comp).Nombre == componentName) as IHydraulicComponent;
|
||
|
||
if (component == null)
|
||
return "";
|
||
|
||
// Obtener los nodos que crea este componente
|
||
var nodes = component.GetHydraulicNodes();
|
||
if (nodes == null || !nodes.Any())
|
||
return "";
|
||
|
||
// Determinar qué nodo usar según el tipo de componente
|
||
var componentType = component.GetType();
|
||
|
||
// Para tanques: siempre usar su único nodo
|
||
if (componentType.Name.Contains("Tank"))
|
||
{
|
||
return nodes.FirstOrDefault()?.Name ?? "";
|
||
}
|
||
|
||
// Para bombas: usar nodo de entrada o salida según corresponda
|
||
if (componentType.Name.Contains("Pump"))
|
||
{
|
||
if (isSource)
|
||
{
|
||
// Si la tubería sale desde este componente, usar el nodo de salida de la bomba
|
||
return nodes.FirstOrDefault(n => n.Name.EndsWith("_Out"))?.Name ?? "";
|
||
}
|
||
else
|
||
{
|
||
// Si la tubería llega a este componente, usar el nodo de entrada de la bomba
|
||
return nodes.FirstOrDefault(n => n.Name.EndsWith("_In"))?.Name ?? "";
|
||
}
|
||
}
|
||
|
||
// Para otros tipos de componentes, usar el primer nodo disponible
|
||
return nodes.FirstOrDefault()?.Name ?? "";
|
||
}
|
||
|
||
public void UpdateHydraulicProperties()
|
||
{
|
||
// Actualizar propiedades antes de la simulación si es necesario
|
||
// Por ejemplo, cambios dinámicos en diámetro o rugosidad
|
||
}
|
||
|
||
public void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> pressures)
|
||
{
|
||
Debug.WriteLine($"[PIPE] ApplyHydraulicResults for {Nombre}");
|
||
Debug.WriteLine($"[PIPE] Available flow keys: {string.Join(", ", flows.Keys)}");
|
||
|
||
// Solo procesar si ambos componentes están conectados
|
||
if (!string.IsNullOrEmpty(Id_ComponenteA) && !string.IsNullOrEmpty(Id_ComponenteB))
|
||
{
|
||
// Obtener los nombres de nodos correctos
|
||
string fromNodeName = GetNodeNameForComponent(Id_ComponenteA, true);
|
||
string toNodeName = GetNodeNameForComponent(Id_ComponenteB, false);
|
||
|
||
Debug.WriteLine($"[PIPE] fromNodeName: '{fromNodeName}', toNodeName: '{toNodeName}'");
|
||
|
||
if (!string.IsNullOrEmpty(fromNodeName) && !string.IsNullOrEmpty(toNodeName))
|
||
{
|
||
string branchKey = $"{fromNodeName}->{toNodeName}";
|
||
Debug.WriteLine($"[PIPE] Looking for branchKey: '{branchKey}'");
|
||
|
||
if (flows.TryGetValue(branchKey, out double flow))
|
||
{
|
||
CurrentFlow = flow;
|
||
Debug.WriteLine($"[PIPE] Found flow: {flow}");
|
||
}
|
||
else
|
||
{
|
||
Debug.WriteLine($"[PIPE] Flow not found for key '{branchKey}'");
|
||
|
||
// Try alternative patterns
|
||
string[] alternativeKeys = {
|
||
$"{toNodeName}->{fromNodeName}",
|
||
$"{Id_ComponenteA}_{Id_ComponenteB}_Pipe",
|
||
$"{Id_ComponenteB}_{Id_ComponenteA}_Pipe",
|
||
$"Pipe_{Id_ComponenteA}_{Id_ComponenteB}",
|
||
$"Pipe_{Id_ComponenteB}_{Id_ComponenteA}",
|
||
$"{Nombre}_Pipe",
|
||
$"Branch_{Nombre}"
|
||
};
|
||
|
||
bool foundFlow = false;
|
||
foreach (string altKey in alternativeKeys)
|
||
{
|
||
if (flows.TryGetValue(altKey, out double altFlow))
|
||
{
|
||
CurrentFlow = altFlow;
|
||
Debug.WriteLine($"[PIPE] Found flow with alternative key '{altKey}': {altFlow}");
|
||
foundFlow = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// If still no flow found, try to get flow from connected pump
|
||
if (!foundFlow)
|
||
{
|
||
Debug.WriteLine($"[PIPE] No direct flow found, checking connected components");
|
||
|
||
// Check if connected to a pump
|
||
string[] pumpKeys = {
|
||
$"{Id_ComponenteA}_Pump",
|
||
$"{Id_ComponenteB}_Pump"
|
||
};
|
||
|
||
foreach (string pumpKey in pumpKeys)
|
||
{
|
||
if (flows.TryGetValue(pumpKey, out double pumpFlow))
|
||
{
|
||
CurrentFlow = pumpFlow;
|
||
Debug.WriteLine($"[PIPE] Using pump flow from '{pumpKey}': {pumpFlow}");
|
||
foundFlow = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Calcular pérdida de presión entre nodos
|
||
if (pressures.TryGetValue(fromNodeName, out double pressureA) &&
|
||
pressures.TryGetValue(toNodeName, out double pressureB))
|
||
{
|
||
PressureDrop = pressureA - pressureB;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
// IHydraulicFlowReceiver Implementation
|
||
|
||
public void SetFlow(double flow)
|
||
{
|
||
CurrentFlow = flow;
|
||
}
|
||
|
||
public double GetFlow()
|
||
{
|
||
return CurrentFlow;
|
||
}
|
||
|
||
|
||
|
||
// IHydraulicPressureReceiver Implementation
|
||
|
||
public void SetPressure(double pressure)
|
||
{
|
||
// Para tuberías, la presión se maneja a través de los nodos
|
||
}
|
||
|
||
public double GetPressure()
|
||
{
|
||
return PressureDrop;
|
||
}
|
||
|
||
|
||
|
||
// Connection Management
|
||
|
||
partial void OnId_ComponenteAChanged(string value)
|
||
{
|
||
if (ComponenteA != null && componenteAPropertyChangedHandler != null)
|
||
((INotifyPropertyChanged)ComponenteA).PropertyChanged -= componenteAPropertyChangedHandler;
|
||
|
||
if (_mainViewModel != null && !string.IsNullOrEmpty(value))
|
||
{
|
||
ComponenteA = (IHydraulicComponent)_mainViewModel.ObjetosSimulables
|
||
.FirstOrDefault(s => s is IHydraulicComponent comp &&
|
||
((osBase)comp).Nombre == value);
|
||
|
||
if (ComponenteA != null)
|
||
{
|
||
componenteAPropertyChangedHandler = (sender, e) =>
|
||
{
|
||
if (e.PropertyName == nameof(osBase.Nombre))
|
||
{
|
||
Id_ComponenteA = ((osBase)sender).Nombre;
|
||
}
|
||
};
|
||
((INotifyPropertyChanged)ComponenteA).PropertyChanged += componenteAPropertyChangedHandler;
|
||
}
|
||
}
|
||
}
|
||
|
||
partial void OnId_ComponenteBChanged(string value)
|
||
{
|
||
if (ComponenteB != null && componenteBPropertyChangedHandler != null)
|
||
((INotifyPropertyChanged)ComponenteB).PropertyChanged -= componenteBPropertyChangedHandler;
|
||
|
||
if (_mainViewModel != null && !string.IsNullOrEmpty(value))
|
||
{
|
||
ComponenteB = (IHydraulicComponent)_mainViewModel.ObjetosSimulables
|
||
.FirstOrDefault(s => s is IHydraulicComponent comp &&
|
||
((osBase)comp).Nombre == value);
|
||
|
||
if (ComponenteB != null)
|
||
{
|
||
componenteBPropertyChangedHandler = (sender, e) =>
|
||
{
|
||
if (e.PropertyName == nameof(osBase.Nombre))
|
||
{
|
||
Id_ComponenteB = ((osBase)sender).Nombre;
|
||
}
|
||
};
|
||
((INotifyPropertyChanged)ComponenteB).PropertyChanged += componenteBPropertyChangedHandler;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
// osBase Implementation
|
||
|
||
public static string NombreCategoria() => "Componentes Hidráulicos";
|
||
|
||
public static string NombreClase() => "Tubería Hidráulica";
|
||
|
||
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()
|
||
{
|
||
// Las tuberías no participan en la simulación física Bepu
|
||
// Solo en el sistema hidráulico
|
||
}
|
||
|
||
public override void UpdateGeometryStart()
|
||
{
|
||
// En el nuevo sistema unificado, el HydraulicSimulationManager se encarga
|
||
// de registrar automáticamente los objetos que implementan IHydraulicComponent
|
||
// No necesitamos crear objetos simHydraulic* separados
|
||
Debug.WriteLine($"[DEBUG] {Nombre}: UpdateGeometryStart() - ComponenteA: '{Id_ComponenteA}', ComponenteB: '{Id_ComponenteB}'");
|
||
ActualizarGeometrias();
|
||
}
|
||
|
||
public override void UpdateGeometryStep()
|
||
{
|
||
// Los objetos hidráulicos actualizan sus resultados
|
||
// a través de ApplyHydraulicResults() desde HydraulicSimulationManager
|
||
}
|
||
|
||
// Método para actualizar fluido desde componente fuente
|
||
public void UpdateFluidFromSource()
|
||
{
|
||
if (_mainViewModel?.ObjetosSimulables == null) return;
|
||
|
||
// Determinar componente fuente basado en dirección del flujo
|
||
string sourceComponentName = CurrentFlow >= 0 ? Id_ComponenteA : Id_ComponenteB;
|
||
|
||
var sourceComponent = _mainViewModel.ObjetosSimulables
|
||
.OfType<IHydraulicComponent>()
|
||
.FirstOrDefault(c => c is osBase osb && osb.Nombre == sourceComponentName);
|
||
|
||
if (sourceComponent is osHydTank tank)
|
||
{
|
||
var sourceFluid = tank.CurrentOutputFluid;
|
||
if (sourceFluid != null)
|
||
{
|
||
_currentFluid = sourceFluid.Clone();
|
||
OnPropertyChanged(nameof(CurrentFluidType));
|
||
OnPropertyChanged(nameof(CurrentFluidDescription));
|
||
OnPropertyChanged(nameof(FluidColor));
|
||
}
|
||
}
|
||
|
||
// Actualizar color visual
|
||
if (Math.Abs(CurrentFlow) > 1e-6) // Hay flujo
|
||
{
|
||
ColorButton_oculto = FluidColor;
|
||
}
|
||
else
|
||
{
|
||
ColorButton_oculto = Brushes.Gray; // Sin flujo
|
||
}
|
||
}
|
||
|
||
public override void UpdateControl(int elapsedMilliseconds)
|
||
{
|
||
// En el nuevo sistema unificado, las propiedades se actualizan
|
||
// directamente a través de ApplyHydraulicResults()
|
||
// CurrentFlow y PressureDrop ya están actualizados por el solver
|
||
|
||
// Actualizar propiedades del fluido cada ciclo
|
||
UpdateFluidFromSource();
|
||
|
||
// Actualizar propiedades de UI
|
||
OnPropertyChanged(nameof(CurrentFlowLMin));
|
||
OnPropertyChanged(nameof(FlowDirection));
|
||
|
||
// Actualizar propiedades de fluido para serialización JSON
|
||
OnPropertyChanged(nameof(CurrentFluidTemperature));
|
||
OnPropertyChanged(nameof(CurrentFluidBrix));
|
||
OnPropertyChanged(nameof(CurrentFluidConcentration));
|
||
OnPropertyChanged(nameof(CurrentFluidDensity));
|
||
OnPropertyChanged(nameof(CurrentFluidViscosity));
|
||
OnPropertyChanged(nameof(FluidVelocity));
|
||
OnPropertyChanged(nameof(ReynoldsNumber));
|
||
OnPropertyChanged(nameof(FlowRegime));
|
||
}
|
||
|
||
public override void ucLoaded()
|
||
{
|
||
// En el nuevo sistema unificado, no necesitamos crear objetos separados
|
||
base.ucLoaded();
|
||
}
|
||
|
||
public override void ucUnLoaded()
|
||
{
|
||
// En el nuevo sistema unificado, el HydraulicSimulationManager
|
||
// maneja automáticamente el registro/desregistro de objetos
|
||
base.ucUnLoaded();
|
||
}
|
||
|
||
public override void UpdatePLC(PLCViewModel plc, int elapsedMilliseconds)
|
||
{
|
||
// Las tuberías no tienen control PLC directo
|
||
}
|
||
|
||
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)
|
||
{
|
||
PipeId = $"Pipe_{Nombre}_{valorInicial}";
|
||
OnId_ComponenteAChanged(Id_ComponenteA);
|
||
OnId_ComponenteBChanged(Id_ComponenteB);
|
||
}
|
||
|
||
public void Disposing()
|
||
{
|
||
if (ComponenteA != null && componenteAPropertyChangedHandler != null)
|
||
((INotifyPropertyChanged)ComponenteA).PropertyChanged -= componenteAPropertyChangedHandler;
|
||
|
||
if (ComponenteB != null && componenteBPropertyChangedHandler != null)
|
||
((INotifyPropertyChanged)ComponenteB).PropertyChanged -= componenteBPropertyChangedHandler;
|
||
}
|
||
|
||
|
||
|
||
// Constructor
|
||
|
||
public osHydPipe()
|
||
{
|
||
PipeId = Guid.NewGuid().ToString();
|
||
IsVisFilter = true; // Asegurar que el componente hidráulico sea visible en filtros
|
||
}
|
||
|
||
|
||
}
|
||
}
|