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 LibS7Adv;
namespace CtrEditor.ObjetosSim
{
///
/// Tanque hidráulico SIMPLIFICADO - solo propiedades esenciales
///
public partial class osHydTank : osBase, IHydraulicComponent, IHydraulicFlowReceiver, IHydraulicPressureReceiver, IosBase
{
public static string NombreCategoria() => "Componentes Hidráulicos";
public static string NombreClase() => "Tanque Hidráulico";
// Tipos de fluido disponibles
public enum FluidTypeId
{
Water = 0,
SyrupBrix40 = 1
}
// SOLO campos esenciales
private double _elevation = 0.0; // m - elevación del fondo del tanque
private double _maxLevel = 2.0; // m - nivel máximo
private double _diameter = 1.0; // m - diámetro del tanque
private FluidTypeId _fluidType = FluidTypeId.Water;
// Estado actual del tanque - ÚNICO NIVEL QUE IMPORTA
private double _currentLevel = 1.0; // m - nivel actual
private double _currentPressure = 1.013; // bar
private double _currentFlow = 0.0; // m³/s
// TSNet Integration
[JsonIgnore]
public TSNetTankAdapter? TSNetAdapter { get; private set; }
// Propiedades visuales
[ObservableProperty]
[property: Category("🎨 Apariencia")]
[property: Description("Tamaño visual del tanque")]
[property: Name("Tamaño")]
public float tamano;
[ObservableProperty]
[property: Category("🎨 Apariencia")]
[property: Description("Color visual del tanque")]
[property: Name("Color")]
private Brush colorButton_oculto = Brushes.LightBlue;
// Constructor
public osHydTank()
{
try
{
Nombre = "Tanque Hidráulico";
Tamano = 1.0f;
Ancho = 0.30f;
Alto = 0.40f;
Angulo = 0f;
Lock_movement = false;
Debug.WriteLine($"osHydTank Constructor completado correctamente");
}
catch (Exception ex)
{
Debug.WriteLine($"Error en constructor osHydTank: {ex.Message}");
Nombre = "Tanque Hidráulico";
Tamano = 1.0f;
}
}
// ===== PROPIEDADES ESENCIALES - SIMPLIFICADAS =====
[Category("🏗️ Configuración")]
[DisplayName("Elevación")]
[Description("Elevación del fondo del tanque (m)")]
public double Elevation
{
get => _elevation;
set
{
if (SetProperty(ref _elevation, value))
{
InvalidateHydraulicNetwork();
}
}
}
[Category("🏗️ Configuración")]
[DisplayName("Nivel máximo")]
[Description("Nivel máximo del tanque (m)")]
public double MaxLevel
{
get => _maxLevel;
set
{
var newMaxLevel = Math.Max(0.1, value);
if (SetProperty(ref _maxLevel, newMaxLevel))
{
// Si el nivel actual está por encima del nuevo máximo, ajustarlo
if (_currentLevel > _maxLevel)
{
var oldCurrent = _currentLevel;
_currentLevel = _maxLevel;
Debug.WriteLine($"⚠️ osHydTank {Nombre}: CurrentLevel ajustado de {oldCurrent} a {_currentLevel} debido a cambio de MaxLevel");
OnPropertyChanged(nameof(CurrentLevel));
}
InvalidateHydraulicNetwork();
}
}
}
[Category("🏗️ Configuración")]
[DisplayName("Diámetro")]
[Description("Diámetro del tanque (m)")]
public double Diameter
{
get => _diameter;
set
{
if (SetProperty(ref _diameter, Math.Max(0.1, value)))
{
InvalidateHydraulicNetwork();
}
}
}
[Category("🧪 Fluido")]
[DisplayName("Tipo de fluido")]
[Description("Tipo de fluido en el tanque")]
public FluidTypeId FluidType
{
get => _fluidType;
set
{
if (SetProperty(ref _fluidType, value))
{
UpdateTankColorFromFluid();
OnPropertyChanged(nameof(FluidDescription));
}
}
}
[Category("🧪 Fluido")]
[DisplayName("Descripción del fluido")]
[Description("Descripción del tipo de fluido")]
[ReadOnly(true)]
public string FluidDescription
{
get
{
return FluidType switch
{
FluidTypeId.Water => "Agua",
FluidTypeId.SyrupBrix40 => "Jarabe Brix 40%",
_ => "Desconocido"
};
}
}
// ===== ESTADO ACTUAL - SIMPLIFICADO =====
[Category("📊 Estado Actual")]
[DisplayName("Nivel actual")]
[Description("Nivel actual del agua en el tanque (m)")]
public double CurrentLevel
{
get => _currentLevel;
set
{
// Validación automática: limitar entre 0 y MaxLevel
var clampedValue = Math.Max(0, Math.Min(_maxLevel, value));
if (Math.Abs(value - clampedValue) > 1e-6)
{
Debug.WriteLine($"⚠️ osHydTank {Nombre}: CurrentLevel ({value}) ajustado a rango válido [0, {_maxLevel}] → {clampedValue}");
}
if (SetProperty(ref _currentLevel, clampedValue))
{
OnPropertyChanged(nameof(FillPercentage));
OnPropertyChanged(nameof(CurrentVolume));
}
}
}
[Category("📊 Estado Actual")]
[DisplayName("Presión actual")]
[Description("Presión actual en el tanque (bar)")]
[ReadOnly(true)]
public double CurrentPressure
{
get => _currentPressure;
private set => SetProperty(ref _currentPressure, value);
}
[Category("📊 Estado Actual")]
[DisplayName("Flujo actual")]
[Description("Flujo actual hacia/desde el tanque (m³/s)")]
[ReadOnly(true)]
public double CurrentFlow
{
get => _currentFlow;
private set => SetProperty(ref _currentFlow, value);
}
[Category("📊 Estado Actual")]
[DisplayName("Volumen actual")]
[Description("Volumen actual en el tanque (L)")]
[ReadOnly(true)]
public double CurrentVolume
{
get
{
var area = Math.PI * Math.Pow(_diameter / 2, 2); // m²
return _currentLevel * area * 1000; // L
}
}
[Category("📊 Estado Actual")]
[DisplayName("Porcentaje de llenado")]
[Description("Porcentaje de llenado del tanque")]
[ReadOnly(true)]
public double FillPercentage
{
get
{
if (_maxLevel <= 0) return 0;
return Math.Max(0, Math.Min(100, (_currentLevel / _maxLevel) * 100));
}
}
// ===== PROPIEDADES TÉCNICAS PARA TSNET =====
public double TankPressure { get; set; } = 101325.0; // 1 atm por defecto
public bool IsFixedPressure { get; set; } = false;
// ===== 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)
{
Tamano += Delta_Width + 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(CurrentVolume));
OnPropertyChanged(nameof(FillPercentage));
UpdateTankColorFromFluid();
}
private void UpdateTankColorFromFluid()
{
ColorButton_oculto = FluidType switch
{
FluidTypeId.Water => Brushes.LightBlue,
FluidTypeId.SyrupBrix40 => Brushes.Orange,
_ => 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)
{
CurrentPressure = pressurePa / 100000.0; // Pa a bar
}
public void ApplyHydraulicFlow(double flowM3s)
{
CurrentFlow = flowM3s;
}
public void UpdateHydraulicState(double timeSeconds)
{
// TSNet maneja el estado
}
public void UpdateFromTSNetResults(double level, double pressure, double flow)
{
CurrentLevel = Math.Max(0, Math.Min(_maxLevel, level));
CurrentPressure = pressure / 100000.0; // Pa a bar
CurrentFlow = flow;
}
public TSNetTankAdapter CreateTSNetAdapter()
{
TSNetAdapter = new TSNetTankAdapter(this);
return TSNetAdapter;
}
[JsonIgnore]
public bool HasHydraulicComponents => true;
public List GetHydraulicNodes()
{
var nodes = new List();
var sanitizedName = SanitizeNodeName(Nombre);
nodes.Add(new HydraulicNodeDefinition(sanitizedName, false, null, $"Tanque hidráulico - {FluidDescription}"));
return nodes;
}
private string SanitizeNodeName(string nodeName)
{
if (string.IsNullOrEmpty(nodeName))
return "UnknownTank";
var sanitized = nodeName
.Replace(" ", "_")
.Replace("á", "a").Replace("é", "e").Replace("í", "i").Replace("ó", "o").Replace("ú", "u")
.Replace("ñ", "n").Replace("Á", "A").Replace("É", "E").Replace("Í", "I").Replace("Ó", "O")
.Replace("Ú", "U").Replace("Ñ", "N");
var validChars = sanitized.Where(c => char.IsLetterOrDigit(c) || c == '_' || c == '-').ToArray();
var result = new string(validChars);
if (!string.IsNullOrEmpty(result) && !char.IsLetter(result[0]))
{
result = "Tank_" + result;
}
return string.IsNullOrEmpty(result) ? "UnknownTank" : result;
}
public void ApplyHydraulicResults(Dictionary flows, Dictionary pressures)
{
try
{
Debug.WriteLine($"osHydTank {Nombre} - ApplyHydraulicResults iniciado");
var searchPatterns = new[]
{
Nombre,
SanitizeNodeName(Nombre),
$"TANK_{Nombre}",
$"Tank_{Nombre}",
Nombre.Replace(" ", "_")
};
// Buscar presión del tanque
foreach (var pattern in searchPatterns)
{
if (pressures.ContainsKey(pattern))
{
CurrentPressure = pressures[pattern] / 100000.0; // Pa a bar
Debug.WriteLine($"osHydTank {Nombre} - Presión encontrada: {CurrentPressure:F6} bar");
break;
}
}
// Calcular flujo neto
var netFlowM3s = CalculateNetFlowFromConnectedPipes(flows);
CurrentFlow = netFlowM3s;
Debug.WriteLine($"osHydTank {Nombre} - Flujo neto: {netFlowM3s:F6} m³/s");
// Actualizar nivel basado en balance de flujo
UpdateLevelFromFlowBalance(netFlowM3s);
Debug.WriteLine($"osHydTank {Nombre} - Nivel después de balance: {CurrentLevel:F6} m");
}
catch (Exception ex)
{
Debug.WriteLine($"Error en osHydTank {Nombre} ApplyHydraulicResults: {ex.Message}");
}
}
public List GetHydraulicElements()
{
return new List();
}
public void UpdateHydraulicProperties()
{
// Actualizar propiedades antes de la simulación
}
public void SetFlow(double flow) => CurrentFlow = flow;
public double GetFlow() => CurrentFlow;
public void SetPressure(double pressure) => CurrentPressure = pressure / 100000.0;
public double GetPressure() => CurrentPressure * 100000.0;
// ===== IMPLEMENTACIÓN DE IOSBASE =====
public void Start() { }
public void Inicializar(int valorInicial) { }
public void Disposing() { }
public int zIndex_fromFrames { get; set; } = 2;
public ZIndexEnum ZIndex_Base() => ZIndexEnum.Estaticos;
public override void CopyFrom(osBase source)
{
if (source is osHydTank sourceTank)
{
Elevation = sourceTank.Elevation;
MaxLevel = sourceTank.MaxLevel;
Diameter = sourceTank.Diameter;
FluidType = sourceTank.FluidType;
CurrentLevel = sourceTank.CurrentLevel;
}
base.CopyFrom(source);
}
// ===== MÉTODOS PRIVADOS AUXILIARES =====
private double CalculateNetFlowFromConnectedPipes(Dictionary flows)
{
double netFlow = 0.0;
var mainViewModel = Application.Current?.MainWindow?.DataContext as MainViewModel;
if (mainViewModel?.ObjetosSimulables == null) return netFlow;
try
{
var connectedPipes = mainViewModel.ObjetosSimulables
.OfType()
.Where(pipe => pipe.Id_ComponenteA == Nombre || pipe.Id_ComponenteB == Nombre)
.ToList();
foreach (var pipe in connectedPipes)
{
var pipeElementName = $"PIPE_{pipe.Nombre}";
if (flows.ContainsKey(pipeElementName))
{
var pipeFlow = flows[pipeElementName];
if (pipe.Id_ComponenteB == Nombre)
{
netFlow += pipeFlow; // Entra al tanque
}
else if (pipe.Id_ComponenteA == Nombre)
{
netFlow -= pipeFlow; // Sale del tanque
}
}
}
return netFlow;
}
catch (Exception ex)
{
Debug.WriteLine($"Error calculating net flow for tank {Nombre}: {ex.Message}");
return 0.0;
}
}
private void UpdateLevelFromFlowBalance(double netFlowM3s)
{
if (Math.Abs(netFlowM3s) < 1e-6) return;
var area = Math.PI * Math.Pow(_diameter / 2, 2); // m²
var deltaTime = 1.0; // segundos
var deltaLevel = (netFlowM3s * deltaTime) / area; // m
var newLevel = _currentLevel + deltaLevel;
CurrentLevel = Math.Max(0, Math.Min(_maxLevel, newLevel));
}
// ===== PROPIEDADES DE COMPATIBILIDAD =====
// Estas propiedades mapean las propiedades simplificadas a los nombres legacy
[JsonIgnore]
[Category("🔧 Compatibilidad")]
[DisplayName("Nivel actual (m)")]
[Description("Nivel actual del agua (compatibilidad legacy)")]
[ReadOnly(true)]
public double CurrentLevelM => CurrentLevel;
[JsonIgnore]
[Category("🔧 Compatibilidad")]
[DisplayName("Nivel máximo (m)")]
[Description("Nivel máximo del tanque (compatibilidad legacy)")]
[ReadOnly(true)]
public double MaxLevelM => MaxLevel;
[JsonIgnore]
[Category("🔧 Compatibilidad")]
[DisplayName("Nivel mínimo (m)")]
[Description("Nivel mínimo = 0 (siempre)")]
[ReadOnly(true)]
public double MinLevelM => 0.0;
[JsonIgnore]
[Category("🔧 Compatibilidad")]
[DisplayName("Área transversal")]
[Description("Área transversal del tanque (m²)")]
[ReadOnly(true)]
public double CrossSectionalArea => Math.PI * Math.Pow(_diameter / 2, 2);
[JsonIgnore]
[Category("🔧 Compatibilidad")]
[DisplayName("Balance de flujo")]
[Description("Balance de flujo actual (m³/s)")]
[ReadOnly(true)]
public double FlowBalance => CurrentFlow;
[JsonIgnore]
[Category("🔧 Compatibilidad")]
[DisplayName("Tipo de tanque")]
[Description("Tipo de tanque (siempre simplificado)")]
[ReadOnly(true)]
public string TankType => "Simplified";
[JsonIgnore]
[Category("🔧 Compatibilidad")]
[DisplayName("Descripción del fluido actual")]
[Description("Descripción del fluido en el tanque")]
[ReadOnly(true)]
public string CurrentFluidDescription => FluidDescription;
}
}