diff --git a/CtrEditor.csproj b/CtrEditor.csproj index b8c6be5..3608e46 100644 --- a/CtrEditor.csproj +++ b/CtrEditor.csproj @@ -74,6 +74,9 @@ + + + @@ -150,6 +153,8 @@ + + diff --git a/ObjetosSim/HydraulicComponents/osHydPipe.cs b/ObjetosSim/HydraulicComponents/osHydPipe.cs index 053339a..92a3917 100644 --- a/ObjetosSim/HydraulicComponents/osHydPipe.cs +++ b/ObjetosSim/HydraulicComponents/osHydPipe.cs @@ -169,21 +169,80 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents // Solo crear elemento si ambos componentes están conectados if (!string.IsNullOrEmpty(Id_ComponenteA) && !string.IsNullOrEmpty(Id_ComponenteB)) { - // Crear el elemento Pipe según la documentación - var pipeElement = new Pipe(Length, Diameter, Roughness); + // 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 - elements.Add(new HydraulicElementDefinition( - name: $"{Nombre}_Pipe", - fromNode: Id_ComponenteA, // Usar el nombre del componente A como nodo origen - toNode: Id_ComponenteB, // Usar el nombre del componente B como nodo destino - element: pipeElement, - description: $"Tubería {Nombre} - L:{Length:F2}m, D:{Diameter*1000:F0}mm ({Id_ComponenteA} → {Id_ComponenteB})" - )); + 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; } + /// + /// Obtiene el nombre del nodo correcto para un componente dado + /// + /// Nombre del componente + /// True si es el nodo origen de la tubería, False si es destino + /// Nombre del nodo o string vacío si no se encuentra + 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 @@ -195,18 +254,25 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents // Solo procesar si ambos componentes están conectados if (!string.IsNullOrEmpty(Id_ComponenteA) && !string.IsNullOrEmpty(Id_ComponenteB)) { - string branchKey = $"{Id_ComponenteA}->{Id_ComponenteB}"; + // Obtener los nombres de nodos correctos + string fromNodeName = GetNodeNameForComponent(Id_ComponenteA, true); + string toNodeName = GetNodeNameForComponent(Id_ComponenteB, false); - if (flows.TryGetValue(branchKey, out double flow)) + if (!string.IsNullOrEmpty(fromNodeName) && !string.IsNullOrEmpty(toNodeName)) { - CurrentFlow = flow; - } - - // Calcular pérdida de presión entre nodos - if (pressures.TryGetValue(Id_ComponenteA, out double pressureA) && - pressures.TryGetValue(Id_ComponenteB, out double pressureB)) - { - PressureDrop = pressureA - pressureB; + string branchKey = $"{fromNodeName}->{toNodeName}"; + + if (flows.TryGetValue(branchKey, out double flow)) + { + CurrentFlow = flow; + } + + // Calcular pérdida de presión entre nodos + if (pressures.TryGetValue(fromNodeName, out double pressureA) && + pressures.TryGetValue(toNodeName, out double pressureB)) + { + PressureDrop = pressureA - pressureB; + } } } } diff --git a/ObjetosSim/HydraulicComponents/osHydPump.cs b/ObjetosSim/HydraulicComponents/osHydPump.cs index ca836c0..1397547 100644 --- a/ObjetosSim/HydraulicComponents/osHydPump.cs +++ b/ObjetosSim/HydraulicComponents/osHydPump.cs @@ -18,8 +18,33 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents /// public partial class osHydPump : osBase, IHydraulicPump, IosBase { - #region Properties + public static string NombreCategoria() => "Hidraulico"; + public static string NombreClase() + { + return "Bomba Hidráulica"; + } + + #region 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; @@ -103,9 +128,20 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents get => _isRunning; set { - SetProperty(ref _isRunning, value); + 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")] @@ -175,15 +211,11 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents // Los objetos hidráulicos no necesitan actualizar geometría física } - public override void OnResize(float Delta_Width, float Delta_Height) - { - Ancho += Delta_Width; - Alto += Delta_Height; - } - public osHydPump() { Nombre = "Bomba Hidráulica"; + Tamano = 0.30f; + ImageSource_oculta = ImageFromPath("/imagenes/pump_stop.png"); } public override void UpdateGeometryStart() @@ -211,6 +243,7 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents { // Los objetos hidráulicos no necesitan geometría física base.ucLoaded(); + UpdatePumpImage(); } public override void ucUnLoaded() @@ -242,11 +275,17 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents 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)); + // Crear bomba con parámetros actuales var pump = new PumpHQ( h0: PumpHead, q0: MaxFlow, - speedRel: IsRunning ? SpeedRatio : 0.0, // Si no está funcionando, velocidad = 0 + speedRel: effectiveSpeed, direction: PumpDirection ); @@ -259,6 +298,8 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents ); elements.Add(pumpElement); + + Debug.WriteLine($"Bomba {Nombre}: Creando elemento hidráulico - H0={PumpHead}m, Q0={MaxFlow}m³/s, Velocidad={effectiveSpeed:F2}, Dirección={PumpDirection}"); } return elements; @@ -288,16 +329,25 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents 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)); } #endregion @@ -345,13 +395,6 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents } #endregion - - #region Static Interface Implementation - - public static string NombreClase() => "Bomba Hidráulica"; - public static string NombreCategoria() => "Componentes Hidráulicos"; - - #endregion } /// diff --git a/ObjetosSim/HydraulicComponents/ucHydPump.xaml b/ObjetosSim/HydraulicComponents/ucHydPump.xaml index 2ae0cbd..50d1115 100644 --- a/ObjetosSim/HydraulicComponents/ucHydPump.xaml +++ b/ObjetosSim/HydraulicComponents/ucHydPump.xaml @@ -3,77 +3,39 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:i="http://schemas.microsoft.com/xaml/behaviors" + xmlns:local="clr-namespace:CtrEditor.ObjetosSim" xmlns:vm="clr-namespace:CtrEditor.ObjetosSim.HydraulicComponents" mc:Ignorable="d"> - + - - + + - - - - + - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/ObjetosSim/HydraulicComponents/ucHydPump.xaml.cs b/ObjetosSim/HydraulicComponents/ucHydPump.xaml.cs index 6ba1a86..39ef95f 100644 --- a/ObjetosSim/HydraulicComponents/ucHydPump.xaml.cs +++ b/ObjetosSim/HydraulicComponents/ucHydPump.xaml.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Media; -using System.Windows.Media.Animation; using CtrEditor.ObjetosSim; using CtrEditor.FuncionesBase; @@ -17,7 +16,6 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents public osBase? Datos { get; set; } public int zIndex_fromFrames { get; set; } = 0; - private Storyboard? _rotationAnimation; private bool _isHighlighted = false; public ucHydPump() @@ -25,13 +23,11 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents InitializeComponent(); this.Loaded += OnLoaded; this.Unloaded += OnUnloaded; - DataContextChanged += OnDataContextChanged; } private void OnLoaded(object sender, RoutedEventArgs e) { Datos?.ucLoaded(); - UpdateVisualState(); } private void OnUnloaded(object sender, RoutedEventArgs e) @@ -39,155 +35,12 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents Datos?.ucUnLoaded(); } - private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) - { - if (DataContext is osHydPump pump) - { - Datos = pump; - pump.PropertyChanged += OnPumpPropertyChanged; - UpdateVisualState(); - } - } - - private void OnPumpPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(osHydPump.IsRunning) || - e.PropertyName == nameof(osHydPump.SpeedRatio) || - e.PropertyName == nameof(osHydPump.PumpDirection) || - e.PropertyName == nameof(osHydPump.CurrentFlow) || - e.PropertyName == nameof(osHydPump.CurrentPressure)) - { - UpdateVisualState(); - } - } - - /// - /// Actualiza el estado visual de la bomba - /// - private void UpdateVisualState() - { - if (Datos is not osHydPump pump) return; - - try - { - Dispatcher.BeginInvoke(() => - { - UpdateStatusLED(pump); - UpdateRotationAnimation(pump); - UpdateDirectionArrow(pump); - UpdateStatusInfo(pump); - }); - } - catch (Exception ex) - { - Debug.WriteLine($"Error updating pump visual state: {ex.Message}"); - } - } - - /// - /// Actualiza el LED de estado - /// - private void UpdateStatusLED(osHydPump pump) - { - if (pump.IsRunning && pump.SpeedRatio > 0) - { - StatusLED.Fill = new SolidColorBrush(Colors.LimeGreen); - } - else - { - StatusLED.Fill = new SolidColorBrush(Colors.Red); - } - } - - /// - /// Actualiza la animación de rotación - /// - private void UpdateRotationAnimation(osHydPump pump) - { - _rotationAnimation?.Stop(); - - if (pump.IsRunning && pump.SpeedRatio > 0) - { - // Crear animación de rotación - var rotateTransform = new RotateTransform(); - PumpBackground.RenderTransform = rotateTransform; - PumpBackground.RenderTransformOrigin = new Point(0.5, 0.5); - - var animation = new DoubleAnimation - { - From = 0, - To = 360, - Duration = TimeSpan.FromSeconds(2.0 / pump.SpeedRatio), // Más rápido con mayor velocidad - RepeatBehavior = RepeatBehavior.Forever - }; - - _rotationAnimation = new Storyboard(); - Storyboard.SetTarget(animation, rotateTransform); - Storyboard.SetTargetProperty(animation, new PropertyPath(RotateTransform.AngleProperty)); - _rotationAnimation.Children.Add(animation); - _rotationAnimation.Begin(); - } - else - { - PumpBackground.RenderTransform = null; - } - } - - /// - /// Actualiza la flecha de dirección - /// - private void UpdateDirectionArrow(osHydPump pump) - { - if (pump.PumpDirection == -1) - { - // Cambiar el color de la flecha para indicar dirección inversa - DirectionArrow.Fill = new SolidColorBrush(Colors.Orange); - } - else - { - DirectionArrow.Fill = new SolidColorBrush(Colors.DarkBlue); - } - } - - /// - /// Actualiza la información de estado - /// - private void UpdateStatusInfo(osHydPump pump) - { - // La información de estado se mostrará a través de la etiqueta principal - // El StatusLED ya está configurado para mostrar el estado IsRunning - } - #region IDataContainer Implementation public void Highlight(bool state) { _isHighlighted = state; - - if (state) - { - PumpBackground.Stroke = new SolidColorBrush(Colors.Yellow); - PumpBackground.StrokeThickness = 3; - } - else - { - PumpBackground.Stroke = new SolidColorBrush(Colors.DarkSlateGray); - PumpBackground.StrokeThickness = 2; - } - } - - #endregion - - protected override void OnMouseEnter(System.Windows.Input.MouseEventArgs e) - { - base.OnMouseEnter(e); - // No hay elementos adicionales para mostrar en la implementación actual - } - - protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e) - { - base.OnMouseLeave(e); - // No hay elementos adicionales para ocultar en la implementación actual + // Aquí se podría agregar lógica de resaltado si fuera necesario } public ZIndexEnum ZIndex_Base() @@ -195,9 +48,6 @@ namespace CtrEditor.ObjetosSim.HydraulicComponents return ZIndexEnum.Estaticos; } - ~ucHydPump() - { - _rotationAnimation?.Stop(); - } + #endregion } } diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs index 44d88c0..ba4b314 100644 --- a/ObjetosSim/osBase.cs +++ b/ObjetosSim/osBase.cs @@ -1919,9 +1919,30 @@ namespace CtrEditor.ObjetosSim items.Add(""); foreach (var obj in objetosSimulables) { - if (obj.GetType() == typeof(T)) + try { - items.Add(obj.Nombre); + // Verificar si el tipo T es una interfaz + if (typeof(T).IsInterface) + { + // Para interfaces, verificar si el objeto implementa la interfaz + if (typeof(T).IsAssignableFrom(obj.GetType())) + { + items.Add(obj.Nombre); + } + } + else + { + // Para clases concretas, usar comparación directa de tipos + if (obj.GetType() == typeof(T)) + { + items.Add(obj.Nombre); + } + } + } + catch (Exception ex) + { + // Log the exception but continue processing other objects + System.Diagnostics.Debug.WriteLine($"Error checking type compatibility for {obj?.GetType()?.Name}: {ex.Message}"); } } diff --git a/imagenes/pump_run.png b/imagenes/pump_run.png new file mode 100644 index 0000000..46bb083 Binary files /dev/null and b/imagenes/pump_run.png differ diff --git a/imagenes/pump_stop.png b/imagenes/pump_stop.png new file mode 100644 index 0000000..46bb083 Binary files /dev/null and b/imagenes/pump_stop.png differ diff --git a/test_osBaseItemsSource.cs b/test_osBaseItemsSource.cs new file mode 100644 index 0000000..5fe4ee3 --- /dev/null +++ b/test_osBaseItemsSource.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using CtrEditor.ObjetosSim; +using CtrEditor.ObjetosSim.HydraulicComponents; +using CtrEditor.HydraulicSimulator; + +// Este archivo es solo para verificar que la corrección funciona +// Se puede eliminar después de confirmar que todo funciona bien + +namespace CtrEditor.Test +{ + class TestItemsSource + { + static void TestTypeAssignability() + { + // Crear instancias de objetos hidráulicos + var pipe = new osHydPipe(); + var tank = new osHydDischargeTank(); + var pump = new osHydPump(); + + // Probar la corrección - debería ser true para todos + Console.WriteLine($"osHydPipe implements IHydraulicComponent: {typeof(IHydraulicComponent).IsAssignableFrom(pipe.GetType())}"); + Console.WriteLine($"osHydDischargeTank implements IHydraulicComponent: {typeof(IHydraulicComponent).IsAssignableFrom(tank.GetType())}"); + Console.WriteLine($"osHydPump implements IHydraulicComponent: {typeof(IHydraulicComponent).IsAssignableFrom(pump.GetType())}"); + + // La comparación anterior que fallaba + Console.WriteLine($"osHydPipe.GetType() == typeof(IHydraulicComponent): {pipe.GetType() == typeof(IHydraulicComponent)}"); + Console.WriteLine($"osHydDischargeTank.GetType() == typeof(IHydraulicComponent): {tank.GetType() == typeof(IHydraulicComponent)}"); + Console.WriteLine($"osHydPump.GetType() == typeof(IHydraulicComponent): {pump.GetType() == typeof(IHydraulicComponent)}"); + } + } +}