CtrEditor/ObjetosSim/HydraulicComponents/osHydPipe.cs

815 lines
30 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Media;
using CtrEditor.HydraulicSimulator;
using CtrEditor.HydraulicSimulator.TSNet.Components;
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
{
// TSNet Integration
[JsonIgnore]
public TSNetPipeAdapter TSNetAdapter { get; private set; }
// 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)
{
try
{
// Aplicar resultados a través del TSNet Adapter
if (TSNetAdapter?.Results != null)
{
// Buscar flujo usando el nombre del elemento de TSNet
string pipeElementName = $"{Nombre}_Pipe";
if (flows.ContainsKey(pipeElementName))
{
var flowM3s = flows[pipeElementName];
CurrentFlow = flowM3s;
TSNetAdapter.Results.CalculatedFlowM3s = flowM3s;
Debug.WriteLine($"Pipe {Nombre}: TSNet Flow={flowM3s:F6} m³/s ({CurrentFlowLMin:F2} L/min)");
}
else
{
// Intentar claves alternativas basadas en componentes conectados
var alternativeKeys = new[]
{
$"{Id_ComponenteA}_{Id_ComponenteB}_Pipe",
$"Pipe_{Id_ComponenteA}_{Id_ComponenteB}",
TSNetAdapter.PipeId,
$"Branch_{Nombre}"
};
bool flowFound = false;
foreach (string altKey in alternativeKeys)
{
if (flows.ContainsKey(altKey))
{
var flowM3s = flows[altKey];
CurrentFlow = flowM3s;
TSNetAdapter.Results.CalculatedFlowM3s = flowM3s;
flowFound = true;
break;
}
}
if (!flowFound)
{
CurrentFlow = 0.0;
TSNetAdapter.Results.CalculatedFlowM3s = 0.0;
}
}
// Calcular pérdida de presión entre nodos conectados
if (!string.IsNullOrEmpty(Id_ComponenteA) && !string.IsNullOrEmpty(Id_ComponenteB))
{
var fromNodeName = GetNodeNameForComponent(Id_ComponenteA, true);
var toNodeName = GetNodeNameForComponent(Id_ComponenteB, false);
if (pressures.ContainsKey(fromNodeName) && pressures.ContainsKey(toNodeName))
{
var pressureA = pressures[fromNodeName];
var pressureB = pressures[toNodeName];
PressureDrop = pressureA - pressureB;
TSNetAdapter.Results.PressureDropBar = PressureDrop;
Debug.WriteLine($"Pipe {Nombre}: Pressure drop={PressureDrop:F0} Pa ({fromNodeName} → {toNodeName})");
}
}
// Actualizar resultados del adapter
TSNetAdapter.Results.Timestamp = DateTime.Now;
TSNetAdapter.Results.FlowStatus = $"Flow: {CurrentFlowLMin:F1}L/min, Drop: {PressureDrop:F1}Pa";
// Actualizar fluido desde componente fuente
UpdateFluidFromSource();
}
// Notificar cambios para UI
OnPropertyChanged(nameof(CurrentFlow));
OnPropertyChanged(nameof(CurrentFlowLMin));
OnPropertyChanged(nameof(PressureDrop));
OnPropertyChanged(nameof(FlowDirection));
OnPropertyChanged(nameof(FluidVelocity));
OnPropertyChanged(nameof(ReynoldsNumber));
OnPropertyChanged(nameof(FlowRegime));
// Debug periódico
if (Environment.TickCount % 2000 < 50) // Cada ~2 segundos
{
Debug.WriteLine($"Pipe {Nombre}: Flow={CurrentFlowLMin:F1}L/min, Drop={PressureDrop:F1}Pa, {FlowDirection}");
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error en Pipe {Nombre} ApplyHydraulicResults: {ex.Message}");
}
}
// 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 TSNet, los valores provienen exclusivamente de TSNet
// No realizamos cálculos internos - solo actualizamos propiedades de UI
try
{
// Los resultados de TSNet ya están aplicados en ApplyHydraulicResults()
// Solo actualizamos propiedades derivadas y visuales
// Actualizar propiedades del fluido cada ciclo
UpdateFluidFromSource();
// Actualizar propiedades de UI calculadas
OnPropertyChanged(nameof(CurrentFlowLMin));
OnPropertyChanged(nameof(FlowDirection));
OnPropertyChanged(nameof(FluidVelocity));
OnPropertyChanged(nameof(ReynoldsNumber));
OnPropertyChanged(nameof(FlowRegime));
// Actualizar color visual basado en fluido
UpdatePipeColorFromFluid();
// Debug periódico cada 5 segundos
if (Environment.TickCount % 5000 < elapsedMilliseconds)
{
Debug.WriteLine($"Pipe {Nombre}: Flow={CurrentFlowLMin:F1}L/min, Drop={PressureDrop:F1}Pa, {FlowDirection}");
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error in Pipe {Nombre} UpdateControl: {ex.Message}");
}
}
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;
}
/// <summary>
/// Actualiza el color de la tubería basado en el fluido que transporta
/// </summary>
private void UpdatePipeColorFromFluid()
{
try
{
if (Math.Abs(CurrentFlow) < 1e-6)
{
ColorButton_oculto = Brushes.Gray; // Sin flujo
return;
}
// Color basado en el fluido actual
var fluidColor = FluidColor;
if (fluidColor != null)
{
ColorButton_oculto = fluidColor;
}
else
{
// Color por defecto según dirección de flujo
ColorButton_oculto = CurrentFlow > 0 ? Brushes.Blue : Brushes.Red;
}
}
catch
{
ColorButton_oculto = Brushes.Gray; // Color por defecto en caso de error
}
}
// Constructor
public osHydPipe()
{
PipeId = Guid.NewGuid().ToString();
IsVisFilter = true; // Asegurar que el componente hidráulico sea visible en filtros
// No inicializar TSNet Adapter aquí - se creará cuando se registre con TSNetSimulationManager
// TSNetAdapter = new TSNetPipeAdapter(this);
}
}
}