433 lines
14 KiB
C#
433 lines
14 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Tubería hidráulica simplificada para integración con TSNet
|
|
/// </summary>
|
|
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<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 = "";
|
|
|
|
// 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<HydraulicNodeDefinition> GetHydraulicNodes()
|
|
{
|
|
// Las tuberías no crean nodos propios, se conectan entre nodos existentes
|
|
return new List<HydraulicNodeDefinition>();
|
|
}
|
|
|
|
public void ApplyHydraulicResults(Dictionary<string, double> flows, Dictionary<string, double> 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<HydraulicElementDefinition> GetHydraulicElements()
|
|
{
|
|
var elements = new List<HydraulicElementDefinition>();
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|