CtrEditor/HydraulicSimulator/HydraulicSimulationManager.cs

951 lines
36 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.Diagnostics;
using System.Linq;
using HydraulicSimulator.Models;
using CtrEditor.ObjetosSim;
namespace CtrEditor.HydraulicSimulator
{
/// <summary>
/// Gestiona la simulación hidráulica similar a SimulationManagerBEPU
/// </summary>
public class HydraulicSimulationManager : IDisposable
{
#region Properties and Fields
/// <summary>
/// Red hidráulica principal que contiene todos los nodos y ramas
/// </summary>
public HydraulicNetwork Network { get; private set; }
/// <summary>
/// Lista de objetos simulables que tienen componentes hidráulicos
/// </summary>
public List<osBase> HydraulicObjects { get; private set; }
/// <summary>
/// Fluido utilizado en la simulación (agua por defecto)
/// </summary>
public Fluid SimulationFluid { get; set; }
/// <summary>
/// Resultado de la última simulación
/// </summary>
public SolutionResult LastSolutionResult { get; private set; }
/// <summary>
/// Tiempo global de la simulación hidráulica
/// </summary>
public float GlobalTime { get; private set; }
/// <summary>
/// Indica si la simulación hidráulica está habilitada
/// </summary>
public bool IsHydraulicSimulationEnabled { get; set; } = true;
/// <summary>
/// Indica si la verificación de NPSH está habilitada
/// </summary>
public bool EnableNPSHVerification { get; set; } = true;
/// <summary>
/// Parámetros del solver
/// </summary>
public int MaxIterations { get; set; } = 300;
public double Tolerance { get; set; } = 1e-3; // Tolerancia más relajada para convergencia inicial
public double RelaxationFactor { get; set; } = 0.8;
public bool VerboseOutput { get; set; } = false; // Activado para debugging
/// <summary>
/// Contador de pasos de simulación para optimizaciones
/// </summary>
private int _stepCount = 0;
/// <summary>
/// Cronómetro para medir tiempo de ejecución
/// </summary>
private Stopwatch _stopwatch;
/// <summary>
/// Diccionario para mapear IDs de objetos a elementos hidráulicos
/// </summary>
private Dictionary<string, osBase> _objectMapping;
/// <summary>
/// Indica si hay cambios pendientes en la red que requieren reconstrucción
/// </summary>
private bool _networkNeedsRebuild = true;
#endregion
#region Constructor
/// <summary>
/// Constructor del gestor de simulación hidráulica
/// </summary>
public HydraulicSimulationManager()
{
// Inicializar componentes
SimulationFluid = Fluid.Water20C; // Agua a 20°C por defecto
Network = new HydraulicNetwork(SimulationFluid);
HydraulicObjects = new List<osBase>();
_objectMapping = new Dictionary<string, osBase>();
_stopwatch = new Stopwatch();
// Inicializar resultado vacío
LastSolutionResult = new SolutionResult(false)
{
ErrorMessage = "Simulación no ejecutada"
};
GlobalTime = 0.0f;
Debug.WriteLine("HydraulicSimulationManager inicializado");
}
#endregion
#region Object Management
/// <summary>
/// Registra un objeto simulable que tiene componentes hidráulicos
/// </summary>
/// <param name="hydraulicObject">Objeto con componentes hidráulicos</param>
public void RegisterHydraulicObject(osBase hydraulicObject)
{
if (hydraulicObject == null)
return;
if (!HydraulicObjects.Contains(hydraulicObject))
{
HydraulicObjects.Add(hydraulicObject);
_objectMapping[hydraulicObject.Nombre] = hydraulicObject;
_networkNeedsRebuild = true;
// Debug.WriteLine($"Objeto hidráulico registrado: {hydraulicObject.Nombre}");
}
}
/// <summary>
/// Desregistra un objeto simulable hidráulico
/// </summary>
/// <param name="hydraulicObject">Objeto a desregistrar</param>
public void UnregisterHydraulicObject(osBase hydraulicObject)
{
if (hydraulicObject == null)
return;
if (HydraulicObjects.Remove(hydraulicObject))
{
_objectMapping.Remove(hydraulicObject.Nombre);
_networkNeedsRebuild = true;
// Debug.WriteLine($"Objeto hidráulico desregistrado: {hydraulicObject.Nombre}");
}
}
/// <summary>
/// Limpia todos los objetos hidráulicos registrados
/// </summary>
public void ClearHydraulicObjects()
{
HydraulicObjects.Clear();
_objectMapping.Clear();
_networkNeedsRebuild = true;
// Debug.WriteLine("Todos los objetos hidráulicos han sido limpiados");
}
#endregion
#region Network Building
/// <summary>
/// Reconstruye la red hidráulica basada en los objetos registrados
/// </summary>
private void RebuildNetwork()
{
if (!_networkNeedsRebuild)
return;
// Debug.WriteLine("Reconstruyendo red hidráulica...");
// Crear nueva red
Network = new HydraulicNetwork(SimulationFluid);
// Agregar nodos y elementos basados en los objetos hidráulicos
BuildNodesFromObjects();
BuildBranchesFromObjects();
_networkNeedsRebuild = false;
// Debug.WriteLine($"Red reconstruida: {Network.Nodes.Count} nodos, {Network.Branches.Count} ramas");
// Verbose output deshabilitado para mejorar rendimiento
/*
if (VerboseOutput)
{
Debug.WriteLine("=== DETALLES DE LA RED HIDRÁULICA ===");
Debug.WriteLine("Nodos:");
foreach (var node in Network.Nodes)
{
string pressureInfo = node.Value.FixedP ? $"Presión fija: {node.Value.P:F0} Pa" : "Presión libre";
Debug.WriteLine($" - {node.Key}: {pressureInfo}");
}
Debug.WriteLine("Ramas:");
foreach (var branch in Network.Branches)
{
Debug.WriteLine($" - {branch.Name}: {branch.N1} -> {branch.N2} ({branch.Elements.Count} elementos)");
foreach (var element in branch.Elements)
{
Debug.WriteLine($" * {element.GetType().Name}");
}
}
Debug.WriteLine("=== FIN DETALLES RED ===");
}
*/
}
/// <summary>
/// Construye los nodos de la red basándose en los objetos hidráulicos
/// </summary>
private void BuildNodesFromObjects()
{
foreach (var obj in HydraulicObjects)
{
// Verificar si el objeto implementa la interfaz hidráulica
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
{
// Obtener nodos definidos por el objeto
var nodeDefinitions = hydraulicComponent.GetHydraulicNodes();
foreach (var nodeDef in nodeDefinitions)
{
// Agregar nodo a la red
Network.AddNode(nodeDef.Name, nodeDef.IsFixedPressure ? nodeDef.Pressure : null);
// Verbose output deshabilitado para mejorar rendimiento
/*
if (VerboseOutput)
{
Debug.WriteLine($"Nodo agregado: {nodeDef.Name} " +
$"(Presión fija: {nodeDef.IsFixedPressure}, " +
$"Presión: {nodeDef.Pressure?.ToString() ?? "libre"})");
}
*/
}
}
else
{
// Objeto sin interfaz hidráulica - crear nodos básicos por compatibilidad
string nodeName = $"Node_{obj.Nombre}";
bool isFixedPressure = DetermineIfFixedPressure(obj);
double? pressure = isFixedPressure ? GetObjectPressure(obj) : null;
Network.AddNode(nodeName, pressure);
}
}
}
/// <summary>
/// Construye las ramas (conexiones) entre nodos
/// </summary>
private void BuildBranchesFromObjects()
{
foreach (var obj in HydraulicObjects)
{
// Verificar si el objeto implementa la interfaz hidráulica
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
{
// Obtener elementos definidos por el objeto
var elementDefinitions = hydraulicComponent.GetHydraulicElements();
foreach (var elemDef in elementDefinitions)
{
// Crear rama con el elemento
var elements = new List<Element> { elemDef.Element };
Network.AddBranch(elemDef.FromNode, elemDef.ToNode, elements, elemDef.Name);
// Verbose output deshabilitado para mejorar rendimiento
/*
if (VerboseOutput)
{
Debug.WriteLine($"Rama agregada: {elemDef.Name} " +
$"({elemDef.FromNode} -> {elemDef.ToNode})");
}
*/
}
}
else
{
// Objeto sin interfaz hidráulica - crear elementos básicos por compatibilidad
var elements = CreateHydraulicElementsFromObject(obj);
if (elements.Any())
{
string fromNode = GetSourceNode(obj);
string toNode = GetTargetNode(obj);
if (!string.IsNullOrEmpty(fromNode) && !string.IsNullOrEmpty(toNode))
{
Network.AddBranch(fromNode, toNode, elements, $"Branch_{obj.Nombre}");
}
}
}
}
}
#endregion
#region Helper Methods for Object Analysis
/// <summary>
/// Determina si un objeto debe tener presión fija
/// </summary>
private bool DetermineIfFixedPressure(osBase obj)
{
// Implementación que será extendida con tipos específicos
// Por ejemplo: tanques, descargas atmosféricas = presión fija
// Uniones, conexiones = presión libre
return false; // Por defecto, presión libre
}
/// <summary>
/// Obtiene la presión de un objeto (si tiene presión fija)
/// </summary>
private double GetObjectPressure(osBase obj)
{
// Implementación que será extendida
return 0.0; // Por defecto, presión atmosférica
}
/// <summary>
/// Crea elementos hidráulicos basándose en un objeto
/// </summary>
private List<Element> CreateHydraulicElementsFromObject(osBase obj)
{
var elements = new List<Element>();
// Implementación que será extendida con tipos específicos
// Por ejemplo:
// - Si es una bomba: crear PumpHQ
// - Si es una tubería: crear Pipe
// - Si es una válvula: crear ValveKv
return elements;
}
/// <summary>
/// Obtiene el nodo fuente de un objeto
/// </summary>
private string GetSourceNode(osBase obj)
{
// Implementación que será extendida
return $"Node_{obj.Nombre}_In";
}
/// <summary>
/// Obtiene el nodo destino de un objeto
/// </summary>
private string GetTargetNode(osBase obj)
{
// Implementación que será extendida
return $"Node_{obj.Nombre}_Out";
}
#endregion
#region Simulation Step
/// <summary>
/// Ejecuta un paso de simulación hidráulica (equivalente al Step de BEPU)
/// </summary>
public void Step(float deltaTime = 0.016f) // ~60 FPS por defecto
{
if (!IsHydraulicSimulationEnabled)
return;
_stopwatch.Restart();
try
{
// Actualizar tiempo global
GlobalTime += deltaTime;
_stepCount++;
// Reconstruir red si es necesario
RebuildNetwork();
// Solo resolver si tenemos objetos hidráulicos
if (HydraulicObjects.Count > 0 && Network.Branches.Count > 0)
{
// Actualizar propiedades de objetos antes de resolver
UpdateObjectProperties();
// Resolver la red hidráulica
LastSolutionResult = Network.Solve(
MaxIterations,
Tolerance,
RelaxationFactor,
VerboseOutput
);
if (LastSolutionResult.Converged)
{
// Aplicar resultados a los objetos
ApplyResultsToObjects();
// Verbose output de resultados deshabilitado para mejorar rendimiento
/*
if (VerboseOutput && _stepCount % 300 == 0) // Log cada 5 segundos aprox
{
//Debug.WriteLine("=== RESULTADOS SIMULACIÓN HIDRÁULICA ===");
//Debug.WriteLine("Flujos:");
foreach (var flow in LastSolutionResult.Flows)
{
Debug.WriteLine($" {flow.Key}: {flow.Value:F6} m³/s ({flow.Value * 3600:F2} m³/h)");
}
Debug.WriteLine("Presiones:");
foreach (var pressure in LastSolutionResult.Pressures)
{
Debug.WriteLine($" {pressure.Key}: {pressure.Value:F0} Pa ({pressure.Value / 100000:F2} bar)");
}
Debug.WriteLine("=== FIN RESULTADOS ===");
}
*/
}
else
{
Debug.WriteLine($"❌ Simulación hidráulica no convergió: {LastSolutionResult.ErrorMessage}");
Debug.WriteLine($" Iteraciones: {LastSolutionResult.Iterations}, Residual: {LastSolutionResult.Residual:E6}");
Debug.WriteLine($" Tolerancia requerida: {Tolerance:E6}");
// Diagnóstico detallado deshabilitado para mejorar rendimiento
/*
if (VerboseOutput && _stepCount % 60 == 0) // Log detallado cada segundo aprox
{
Debug.WriteLine("=== DIAGNÓSTICO CONVERGENCIA ===");
Debug.WriteLine($"Nodos en red: {Network.Nodes.Count}");
Debug.WriteLine($"Ramas en red: {Network.Branches.Count}");
foreach (var node in Network.Nodes)
{
string info = node.Value.FixedP ? $"FIJA={node.Value.P:F0}Pa" : "LIBRE";
Debug.WriteLine($" Nodo {node.Key}: {info}");
}
Debug.WriteLine("=== FIN DIAGNÓSTICO ===");
}
*/
}
}
else
{
// Sin objetos hidráulicos, resultado exitoso pero vacío
LastSolutionResult = new SolutionResult(true);
}
}
catch (Exception ex)
{
LastSolutionResult = new SolutionResult(false)
{
ErrorMessage = $"Error en simulación hidráulica: {ex.Message}"
};
Debug.WriteLine($"Error en HydraulicSimulationManager.Step: {ex}");
}
finally
{
_stopwatch.Stop();
// Logging de tiempo deshabilitado para mejorar rendimiento
/*
if (VerboseOutput && _stepCount % 60 == 0) // Log cada segundo aprox
{
Debug.WriteLine($"Simulación hidráulica - Paso {_stepCount}: {_stopwatch.ElapsedMilliseconds}ms, " +
$"Objetos: {HydraulicObjects.Count}, Convergió: {LastSolutionResult.Converged}");
}
*/
}
}
/// <summary>
/// Actualiza las propiedades de los objetos antes de resolver la red
/// </summary>
private void UpdateObjectProperties()
{
// Actualizar objetos hidráulicos específicos
foreach (var simObj in HydraulicSimObjects)
{
simObj.UpdateProperties();
}
// Actualizar objetos hidráulicos generales (legacy)
foreach (var obj in HydraulicObjects)
{
// Aquí se implementará la actualización de propiedades específicas
// Por ejemplo: estado de válvulas, velocidad de bombas, etc.
UpdateObjectHydraulicProperties(obj);
}
}
/// <summary>
/// Actualiza las propiedades hidráulicas específicas de un objeto
/// </summary>
private void UpdateObjectHydraulicProperties(osBase obj)
{
// Actualizar propiedades basándose en las interfaces implementadas
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
{
// Llamar al método de actualización del objeto
hydraulicComponent.UpdateHydraulicProperties();
}
// Actualización específica por tipo de objeto
if (obj is IHydraulicPump pump)
{
// Verificar estado de la bomba, velocidad, etc.
UpdatePumpProperties(pump);
}
else if (obj is IHydraulicValve valve)
{
// Verificar apertura de la válvula
UpdateValveProperties(valve);
}
else if (obj is IHydraulicTank tank)
{
// Actualizar nivel y presión del tanque
UpdateTankProperties(tank);
}
}
/// <summary>
/// Actualiza propiedades específicas de bombas
/// </summary>
private void UpdatePumpProperties(IHydraulicPump pump)
{
// Aquí se pueden hacer validaciones y ajustes específicos de bombas
// Por ejemplo, limitar velocidad, verificar estado de operación, etc.
if (pump.SpeedRatio < 0.0) pump.SpeedRatio = 0.0;
if (pump.SpeedRatio > 1.0) pump.SpeedRatio = 1.0;
// Debug output deshabilitado para mejorar rendimiento
/*
if (VerboseOutput)
{
Debug.WriteLine($"Bomba {pump.GetType().Name}: Velocidad={pump.SpeedRatio:F2}, " +
$"Funcionando={pump.IsRunning}, Dirección={pump.PumpDirection}");
}
*/
}
/// <summary>
/// Actualiza propiedades específicas de válvulas
/// </summary>
private void UpdateValveProperties(IHydraulicValve valve)
{
// Validar apertura de válvula
if (valve.Opening < 0.0) valve.Opening = 0.0;
if (valve.Opening > 1.0) valve.Opening = 1.0;
// Debug output deshabilitado para mejorar rendimiento
/*
if (VerboseOutput)
{
Debug.WriteLine($"Válvula {valve.GetType().Name}: Apertura={valve.Opening:F2}, " +
$"Cerrada={valve.IsClosed}, Abierta={valve.IsFullyOpen}");
}
*/
}
/// <summary>
/// Actualiza propiedades específicas de tanques
/// </summary>
private void UpdateTankProperties(IHydraulicTank tank)
{
// Calcular presión basada en nivel si no es presión fija
if (!tank.IsFixedPressure)
{
// P = ρgh + P_atmosferica
double pressureFromLevel = SimulationFluid.Rho * 9.80665 * tank.Level; // + presión atmosférica
tank.TankPressure = pressureFromLevel;
}
// Debug output deshabilitado para mejorar rendimiento
/*
if (VerboseOutput)
{
Debug.WriteLine($"Tanque {tank.GetType().Name}: Nivel={tank.Level:F2}m, " +
$"Presión={tank.TankPressure:F0}Pa, PresionFija={tank.IsFixedPressure}");
}
*/
}
/// <summary>
/// Aplica los resultados de la simulación a los objetos
/// </summary>
private void ApplyResultsToObjects()
{
// Aplicar resultados a objetos hidráulicos específicos
foreach (var simObj in HydraulicSimObjects)
{
ApplyResultsToSimObject(simObj);
simObj.ApplySimulationResults();
}
// Aplicar resultados a objetos hidráulicos generales (legacy)
foreach (var obj in HydraulicObjects)
{
// Aplicar caudales y presiones calculados al objeto
ApplyResultsToObject(obj);
}
}
/// <summary>
/// Aplica los resultados de la simulación a un objeto específico
/// </summary>
private void ApplyResultsToSimObject(simHydraulicBase simObj)
{
// Aplicar resultados específicos según el tipo de objeto
if (simObj is simHydraulicPump pump)
{
// Buscar flujo y presión de la bomba en los resultados
string pumpBranchName = $"{pump.Nombre}_Pump";
if (LastSolutionResult.Flows.TryGetValue(pumpBranchName, out double flow))
{
pump.CurrentFlow = flow;
}
string inletNodeName = $"{pump.Nombre}_In";
if (LastSolutionResult.Pressures.TryGetValue(inletNodeName, out double pressure))
{
pump.CurrentPressure = pressure;
}
}
else if (simObj is simHydraulicTank tank)
{
// Buscar flujos de entrada y salida del tanque
// Esto requeriría mapeo de tuberías conectadas
// Por ahora, aplicar presión del tanque
string tankNodeName = $"{tank.Nombre}_Tank";
if (LastSolutionResult.Pressures.TryGetValue(tankNodeName, out double pressure))
{
tank.CurrentPressure = pressure;
}
}
else if (simObj is simHydraulicPipe pipe)
{
// Buscar flujo a través de la tubería
string pipeBranchName = $"{pipe.Nombre}_Pipe";
if (LastSolutionResult.Flows.TryGetValue(pipeBranchName, out double flow))
{
pipe.CurrentFlow = flow;
}
// Calcular pérdida de presión
string fromNode = pipe.ComponenteAId;
string toNode = pipe.ComponenteBId;
if (LastSolutionResult.Pressures.TryGetValue(fromNode, out double pressureA) &&
LastSolutionResult.Pressures.TryGetValue(toNode, out double pressureB))
{
pipe.PressureDrop = pressureA - pressureB;
}
}
}
/// <summary>
/// Aplica los resultados a un objeto específico
/// </summary>
private void ApplyResultsToObject(osBase obj)
{
// Aplicar resultados usando las interfaces hidráulicas
if (obj is IHydraulicComponent hydraulicComponent && hydraulicComponent.HasHydraulicComponents)
{
// Llamar al método de aplicación de resultados del objeto
hydraulicComponent.ApplyHydraulicResults(LastSolutionResult.Flows, LastSolutionResult.Pressures);
}
// Aplicación específica por tipo de interfaz
if (obj is IHydraulicFlowReceiver flowReceiver)
{
// Buscar caudal asociado al objeto
string branchName = FindBranchNameForObject(obj);
if (!string.IsNullOrEmpty(branchName) && LastSolutionResult.Flows.ContainsKey(branchName))
{
double flow = LastSolutionResult.Flows[branchName];
flowReceiver.SetFlow(flow);
}
}
if (obj is IHydraulicPressureReceiver pressureReceiver)
{
// Buscar presión asociada al objeto
string nodeName = FindNodeNameForObject(obj);
if (!string.IsNullOrEmpty(nodeName) && LastSolutionResult.Pressures.ContainsKey(nodeName))
{
double pressure = LastSolutionResult.Pressures[nodeName];
pressureReceiver.SetPressure(pressure);
}
}
}
/// <summary>
/// Encuentra el nombre de rama asociado a un objeto
/// </summary>
private string FindBranchNameForObject(osBase obj)
{
// Buscar en las ramas de la red
foreach (var branch in Network.Branches)
{
if (branch.Name.Contains(obj.Nombre))
{
return branch.Name;
}
}
// Fallback a nombre por defecto
return $"Branch_{obj.Nombre}";
}
/// <summary>
/// Encuentra el nombre de nodo asociado a un objeto
/// </summary>
private string FindNodeNameForObject(osBase obj)
{
// Buscar en los nodos de la red
foreach (var nodeKvp in Network.Nodes)
{
if (nodeKvp.Key.Contains(obj.Nombre))
{
return nodeKvp.Key;
}
}
// Fallback a nombre por defecto
return $"Node_{obj.Nombre}";
}
/// <summary>
/// Establece el caudal en un objeto
/// </summary>
private void SetObjectFlow(osBase obj, double flow)
{
if (obj is IHydraulicFlowReceiver flowReceiver)
{
flowReceiver.SetFlow(flow);
}
}
/// <summary>
/// Establece la presión en un objeto
/// </summary>
private void SetObjectPressure(osBase obj, double pressure)
{
if (obj is IHydraulicPressureReceiver pressureReceiver)
{
pressureReceiver.SetPressure(pressure);
}
}
#endregion
#region Public Methods
/// <summary>
/// Fuerza la reconstrucción de la red en el próximo paso
/// </summary>
public void InvalidateNetwork()
{
_networkNeedsRebuild = true;
}
/// <summary>
/// Obtiene estadísticas de la simulación
/// </summary>
public string GetSimulationStats()
{
return $"Objetos hidráulicos: {HydraulicObjects.Count}\n" +
$"Nodos: {Network.Nodes.Count}\n" +
$"Ramas: {Network.Branches.Count}\n" +
$"Última simulación convergió: {LastSolutionResult.Converged}\n" +
$"Iteraciones: {LastSolutionResult.Iterations}\n" +
$"Residual: {LastSolutionResult.Residual:E3}\n" +
$"Tiempo global: {GlobalTime:F2}s\n" +
$"Pasos: {_stepCount}";
}
/// <summary>
/// Reinicia la simulación
/// </summary>
public void Reset()
{
GlobalTime = 0.0f;
_stepCount = 0;
_networkNeedsRebuild = true;
// Reinicializar resultado
LastSolutionResult = new SolutionResult(false)
{
ErrorMessage = "Simulación reiniciada"
};
Debug.WriteLine("Simulación hidráulica reiniciada");
}
/// <summary>
/// Configura los parámetros de verificación de NPSH
/// </summary>
public void ConfigureNPSHSettings(bool enableNPSH, double npshRequired = 3.0, double vaporPressure = 2337.0, double suctionLosses = 0.5)
{
EnableNPSHVerification = enableNPSH;
// Actualizar todas las bombas existentes con los nuevos parámetros
foreach (var obj in HydraulicObjects)
{
if (obj is osHydPump pump)
{
// Los parámetros se aplicarán cuando se reconstruya la red
InvalidateNetwork();
}
}
Debug.WriteLine($"Verificación NPSH {(enableNPSH ? "habilitada" : "deshabilitada")}: NPSH_req={npshRequired}m, P_vapor={vaporPressure}Pa");
}
#endregion
#region IDisposable
private bool _disposed = false;
/// <summary>
/// Libera recursos del gestor de simulación
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// Limpiar recursos gestionados
HydraulicObjects?.Clear();
_objectMapping?.Clear();
Network = null;
_stopwatch?.Stop();
Debug.WriteLine("HydraulicSimulationManager disposed");
}
_disposed = true;
}
}
#endregion
#region Hydraulic Object Creation
/// <summary>
/// Lista de objetos hidráulicos específicos del simulador
/// </summary>
public List<simHydraulicBase> HydraulicSimObjects { get; private set; } = new List<simHydraulicBase>();
/// <summary>
/// Crea una bomba hidráulica en la simulación
/// </summary>
public simHydraulicPump AddPump(double pumpHead, double maxFlow, double speedRatio, bool isRunning, int direction)
{
var pump = new simHydraulicPump(this)
{
PumpHead = pumpHead,
MaxFlow = maxFlow,
SpeedRatio = speedRatio,
IsRunning = isRunning,
PumpDirection = direction
};
HydraulicSimObjects.Add(pump);
_networkNeedsRebuild = true;
Debug.WriteLine($"Bomba hidráulica creada: H={pumpHead}m, Q={maxFlow}m³/s");
return pump;
}
/// <summary>
/// Crea un tanque hidráulico en la simulación
/// </summary>
public simHydraulicTank AddTank(double pressure, double currentLevel, double maxLevel, double minLevel, double crossSectionalArea, bool isFixedPressure)
{
var tank = new simHydraulicTank(this)
{
TankPressure = pressure,
CurrentLevel = currentLevel,
MaxLevel = maxLevel,
MinLevel = minLevel,
CrossSectionalArea = crossSectionalArea,
IsFixedPressure = isFixedPressure
};
HydraulicSimObjects.Add(tank);
_networkNeedsRebuild = true;
Debug.WriteLine($"Tanque hidráulico creado: P={pressure}Pa, Nivel={currentLevel}m");
return tank;
}
/// <summary>
/// Crea una tubería hidráulica en la simulación
/// </summary>
public simHydraulicPipe AddPipe(double length, double diameter, double roughness, string componenteAId, string componenteBId)
{
var pipe = new simHydraulicPipe(this)
{
Length = length,
Diameter = diameter,
Roughness = roughness,
ComponenteAId = componenteAId,
ComponenteBId = componenteBId
};
HydraulicSimObjects.Add(pipe);
_networkNeedsRebuild = true;
Debug.WriteLine($"Tubería hidráulica creada: L={length}m, D={diameter}m");
return pipe;
}
/// <summary>
/// Remueve un objeto hidráulico de la simulación
/// </summary>
public void Remove(simHydraulicBase hydraulicObject)
{
if (hydraulicObject != null && HydraulicSimObjects.Contains(hydraulicObject))
{
HydraulicSimObjects.Remove(hydraulicObject);
_networkNeedsRebuild = true;
Debug.WriteLine($"Objeto hidráulico removido: {hydraulicObject.SimObjectType}");
}
}
/// <summary>
/// Limpia todos los objetos hidráulicos específicos
/// </summary>
public void ClearHydraulicSimObjects()
{
HydraulicSimObjects.Clear();
_networkNeedsRebuild = true;
Debug.WriteLine("Todos los objetos hidráulicos específicos han sido limpiados");
}
#endregion
}
}