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 { /// /// Tubería hidráulica simplificada para integración con TSNet /// public partial class osHydPipe : osBase, IHydraulicComponent, IHydraulicFlowReceiver, IHydraulicPressureReceiver, IosBase { public static string NombreCategoria() => "Componentes Hidráulicos"; public static string NombreClase() => "Tubería Hidráulica"; // Private fields para TSNet private double _length = 1.0; // m - longitud de la tubería private double _diameter = 0.05; // m - diámetro interno private double _roughness = 4.5e-5; // m - rugosidad del material // Estado actual (desde TSNet) private double _currentFlow = 0.0; // m³/s - flujo actual private double _pressureDrop = 0.0; // Pa - caída de presión private double _velocity = 0.0; // m/s - velocidad del fluido // TSNet Integration [JsonIgnore] public TSNetPipeAdapter? TSNetAdapter { get; private set; } // 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; // Propiedades de conexión [ObservableProperty] [property: Category("🔗 Conexiones")] [property: Description("Primer componente hidráulico conectado")] [property: Name("Componente A")] [property: ItemsSource(typeof(osBaseItemsSource))] private string id_ComponenteA = ""; [ObservableProperty] [property: Category("🔗 Conexiones")] [property: Description("Segundo componente hidráulico conectado")] [property: Name("Componente B")] [property: ItemsSource(typeof(osBaseItemsSource))] private string id_ComponenteB = ""; // Constructor public osHydPipe() { try { Nombre = "Tubería Hidráulica"; Ancho = 1.0f; Alto = 0.05f; Angulo = 0f; Lock_movement = false; Debug.WriteLine($"osHydPipe Constructor completado correctamente"); } catch (Exception ex) { Debug.WriteLine($"Error en constructor osHydPipe: {ex.Message}"); Nombre = "Tubería Hidráulica"; Ancho = 1.0f; Alto = 0.05f; } } // Propiedades de configuración de la tubería [Category("🔧 Tubería Hidráulica")] [DisplayName("Longitud")] [Description("Longitud de la tubería en metros")] public double Length { get => _length; set { if (SetProperty(ref _length, Math.Max(0.1, value))) { InvalidateHydraulicNetwork(); } } } [Category("🔧 Tubería Hidráulica")] [DisplayName("Diámetro")] [Description("Diámetro interno de la tubería en metros")] public double Diameter { get => _diameter; set { if (SetProperty(ref _diameter, Math.Max(0.001, value))) { InvalidateHydraulicNetwork(); } } } [Category("🔧 Tubería Hidráulica")] [DisplayName("Rugosidad")] [Description("Rugosidad del material en metros")] public double Roughness { get => _roughness; set { if (SetProperty(ref _roughness, Math.Max(1e-6, value))) { InvalidateHydraulicNetwork(); } } } // Estado actual (desde TSNet) [Category("📊 Estado Actual")] [DisplayName("Flujo actual")] [Description("Flujo actual a través de la tubería (m³/s)")] [ReadOnly(true)] public double CurrentFlow { get => _currentFlow; private set => SetProperty(ref _currentFlow, value); } [Category("📊 Estado Actual")] [DisplayName("Caída de presión")] [Description("Caída de presión en la tubería (Pa)")] [ReadOnly(true)] public double PressureDrop { get => _pressureDrop; private set => SetProperty(ref _pressureDrop, value); } [Category("📊 Estado Actual")] [DisplayName("Velocidad")] [Description("Velocidad del fluido en la tubería (m/s)")] [ReadOnly(true)] public double FluidVelocity { get => _velocity; private set => SetProperty(ref _velocity, value); } [Category("📊 Estado Actual")] [DisplayName("Flujo (L/min)")] [Description("Flujo actual en L/min")] [ReadOnly(true)] public double CurrentFlowLMin => Math.Abs(CurrentFlow) * 60000.0; // m³/s a L/min [Category("📊 Estado Actual")] [DisplayName("Caída presión (bar)")] [Description("Caída de presión en bar")] [ReadOnly(true)] public double PressureDropBar => PressureDrop / 100000.0; // Pa a bar [Category("📊 Estado Actual")] [DisplayName("Tiene flujo")] [Description("Indica si hay flujo en la tubería")] [ReadOnly(true)] public bool HasFlow => Math.Abs(CurrentFlow) > 1e-6; // 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) { Ancho += Delta_Width; Alto += 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(CurrentFlowLMin)); OnPropertyChanged(nameof(PressureDropBar)); OnPropertyChanged(nameof(HasFlow)); UpdatePipeColor(); } private void UpdatePipeColor() { // Cambiar color según el flujo if (HasFlow) { var flowPercent = Math.Min(1.0, CurrentFlowLMin / 100.0); // normalizar a 100 L/min var intensity = (byte)(255 * flowPercent); ColorButton_oculto = new SolidColorBrush(Color.FromRgb(intensity, (byte)(255 - intensity), 0)); } else { ColorButton_oculto = Brushes.Gray; } } 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) { // La caída de presión se calcula en TSNet } public void ApplyHydraulicFlow(double flowM3s) { CurrentFlow = flowM3s; // Calcular velocidad if (Diameter > 0) { var area = Math.PI * Math.Pow(Diameter / 2, 2); // m² FluidVelocity = Math.Abs(CurrentFlow) / area; // m/s } } public void UpdateHydraulicState(double timeSeconds) { // TSNet maneja el estado } // Métodos para TSNet public void UpdateFromTSNetResults(double flow, double pressureDrop) { CurrentFlow = flow; PressureDrop = pressureDrop; // Calcular velocidad if (Diameter > 0) { var area = Math.PI * Math.Pow(Diameter / 2, 2); // m² FluidVelocity = Math.Abs(CurrentFlow) / area; // m/s } } // Crear adapter para TSNet public TSNetPipeAdapter CreateTSNetAdapter() { TSNetAdapter = new TSNetPipeAdapter(this); return TSNetAdapter; } // Implementación de IHydraulicComponent [JsonIgnore] public bool HasHydraulicComponents => true; public List GetHydraulicNodes() { // Las tuberías no crean nodos propios, se conectan entre nodos existentes return new List(); } public void ApplyHydraulicResults(Dictionary flows, Dictionary pressures) { try { // Buscar resultados de esta tubería en TSNet var pipeElementName = $"PIPE_{Nombre}"; if (flows.ContainsKey(pipeElementName)) { var pipeFlow = flows[pipeElementName]; ApplyHydraulicFlow(pipeFlow); } // Calcular caída de presión si tenemos datos de los nodos conectados if (!string.IsNullOrEmpty(Id_ComponenteA) && !string.IsNullOrEmpty(Id_ComponenteB)) { if (pressures.ContainsKey(Id_ComponenteA) && pressures.ContainsKey(Id_ComponenteB)) { var pressureA = pressures[Id_ComponenteA]; var pressureB = pressures[Id_ComponenteB]; PressureDrop = Math.Abs(pressureA - pressureB); } } } catch (Exception ex) { Debug.WriteLine($"Error en Pipe {Nombre} ApplyHydraulicResults: {ex.Message}"); } } public List GetHydraulicElements() { var elements = new List(); // Verificar que la tubería esté conectada a dos componentes if (!string.IsNullOrEmpty(Id_ComponenteA) && !string.IsNullOrEmpty(Id_ComponenteB)) { // Crear elemento pipe con propiedades correctas var pipeElement = new Pipe(Length, Diameter, Roughness); elements.Add(new HydraulicElementDefinition( $"PIPE_{Nombre}", Id_ComponenteA, Id_ComponenteB, pipeElement, $"Tubería - L:{Length:F1}m, D:{Diameter*1000:F0}mm, Rough:{Roughness*1000:F2}mm" )); } return elements; } public void UpdateHydraulicProperties() { // Actualizar propiedades antes de la simulación // Validar conexiones if (string.IsNullOrEmpty(Id_ComponenteA) || string.IsNullOrEmpty(Id_ComponenteB)) { Debug.WriteLine($"Warning: Tubería {Nombre} no está completamente conectada"); } } // Implementación de IHydraulicFlowReceiver public void SetFlow(double flow) { ApplyHydraulicFlow(flow); } public double GetFlow() { return CurrentFlow; } // Implementación de IHydraulicPressureReceiver public void SetPressure(double pressure) { // Las tuberías no tienen presión propia, solo caída de presión } public double GetPressure() { return PressureDrop; } // Implementación de IosBase public void Start() { // Inicialización de la tubería 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; } = 1; public ZIndexEnum ZIndex_Base() { return ZIndexEnum.Estaticos; } public override void CopyFrom(osBase source) { if (source is osHydPipe sourcePipe) { Length = sourcePipe.Length; Diameter = sourcePipe.Diameter; Roughness = sourcePipe.Roughness; Id_ComponenteA = sourcePipe.Id_ComponenteA; Id_ComponenteB = sourcePipe.Id_ComponenteB; } base.CopyFrom(source); } } }