using System; using System.Collections.Generic; using System.ComponentModel; 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 { /// /// Tubería hidráulica que conecta componentes del sistema hidráulico /// public partial class osHydPipe : osBase, IHydraulicPipe, IosBase { #region 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))] 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 = ""; [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); } private string pipeId = ""; public string PipeId { get => pipeId; set => SetProperty(ref pipeId, value); } #endregion #region Component References [JsonIgnore] private IHydraulicComponent ComponenteA = null; [JsonIgnore] private IHydraulicComponent ComponenteB = null; [JsonIgnore] private PropertyChangedEventHandler componenteAPropertyChangedHandler; [JsonIgnore] private PropertyChangedEventHandler componenteBPropertyChangedHandler; [JsonIgnore] public Action ActualizarTamaño { get; set; } #endregion #region IHydraulicPipe Implementation // Ya implementadas las propiedades Length, Diameter, Roughness arriba #endregion #region IHydraulicComponent Implementation public bool HasHydraulicComponents => true; public List GetHydraulicNodes() { var nodes = new List(); // 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 GetHydraulicElements() { var elements = new List(); // 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); 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})" )); } return elements; } 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 flows, Dictionary pressures) { // Solo procesar si ambos componentes están conectados if (!string.IsNullOrEmpty(Id_ComponenteA) && !string.IsNullOrEmpty(Id_ComponenteB)) { string branchKey = $"{Id_ComponenteA}->{Id_ComponenteB}"; if (flows.TryGetValue(branchKey, out double flow)) { 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; } } } #endregion #region IHydraulicFlowReceiver Implementation public void SetFlow(double flow) { CurrentFlow = flow; } public double GetFlow() { return CurrentFlow; } #endregion #region 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; } #endregion #region 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; } } } #endregion #region 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() { ActualizarGeometrias(); } 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; } #endregion #region Constructor public osHydPipe() { PipeId = Guid.NewGuid().ToString(); } #endregion } }