849 lines
31 KiB
C#
849 lines
31 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using System.Diagnostics;
|
|
using CtrEditor.HydraulicSimulator.Python;
|
|
using CtrEditor.HydraulicSimulator.TSNet.Components;
|
|
using HydraulicSimulator.Models;
|
|
using CtrEditor.ObjetosSim;
|
|
|
|
namespace CtrEditor.HydraulicSimulator.TSNet
|
|
{
|
|
/// <summary>
|
|
/// Gestor de simulaciones TSNet que reemplaza o complementa al HydraulicSimulationManager
|
|
/// </summary>
|
|
public class TSNetSimulationManager : IDisposable
|
|
{
|
|
#region Properties and Fields
|
|
|
|
/// <summary>
|
|
/// Red hidráulica actual
|
|
/// </summary>
|
|
public HydraulicNetwork Network { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Lista de objetos hidráulicos registrados
|
|
/// </summary>
|
|
public List<osBase> HydraulicObjects { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Directorio de trabajo para archivos INP y resultados
|
|
/// </summary>
|
|
public string WorkingDirectory { get; set; }
|
|
|
|
/// <summary>
|
|
/// Ruta del archivo INP actual
|
|
/// </summary>
|
|
public string CurrentInpFile { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Configuración de la simulación transitoria
|
|
/// </summary>
|
|
public TSNetConfiguration Configuration { get; set; }
|
|
|
|
/// <summary>
|
|
/// Resultado de la última simulación
|
|
/// </summary>
|
|
public TSNetResult LastResult { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Indica si la simulación está ejecutándose
|
|
/// </summary>
|
|
public bool IsRunning { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Indica si la red necesita ser reconstruida
|
|
/// </summary>
|
|
public bool NetworkNeedsRebuild { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Evento disparado cuando se completa una simulación
|
|
/// </summary>
|
|
public event EventHandler<TSNetResult> SimulationCompleted;
|
|
|
|
/// <summary>
|
|
/// Mapeo de objetos por ID
|
|
/// </summary>
|
|
private Dictionary<string, osBase> _objectMapping;
|
|
|
|
/// <summary>
|
|
/// Adaptadores para tanques registrados
|
|
/// </summary>
|
|
private Dictionary<string, TSNetTankAdapter> _tankAdapters;
|
|
|
|
/// <summary>
|
|
/// Adaptadores para bombas registradas
|
|
/// </summary>
|
|
private Dictionary<string, TSNetPumpAdapter> _pumpAdapters;
|
|
|
|
/// <summary>
|
|
/// Adaptadores para tuberías registradas
|
|
/// </summary>
|
|
private Dictionary<string, TSNetPipeAdapter> _pipeAdapters;
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
public TSNetSimulationManager()
|
|
{
|
|
Network = new HydraulicNetwork();
|
|
HydraulicObjects = new List<osBase>();
|
|
_objectMapping = new Dictionary<string, osBase>();
|
|
|
|
// Inicializar adaptadores
|
|
_tankAdapters = new Dictionary<string, TSNetTankAdapter>();
|
|
_pumpAdapters = new Dictionary<string, TSNetPumpAdapter>();
|
|
_pipeAdapters = new Dictionary<string, TSNetPipeAdapter>();
|
|
|
|
// Configuración por defecto
|
|
Configuration = new TSNetConfiguration
|
|
{
|
|
Duration = 10.0,
|
|
TimeStep = 0.01,
|
|
OutputDirectory = Path.Combine(Path.GetTempPath(), "TSNet")
|
|
};
|
|
|
|
WorkingDirectory = Configuration.OutputDirectory;
|
|
EnsureWorkingDirectory();
|
|
|
|
// Inicializar Python si no está ya
|
|
if (!PythonInterop.Initialize())
|
|
{
|
|
Debug.WriteLine("Warning: No se pudo inicializar Python para TSNet");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Network Management
|
|
|
|
/// <summary>
|
|
/// Registra un objeto hidráulico en el sistema TSNet
|
|
/// IMPORTANTE: NO se realizan cálculos locales - todo se delega a TSNet
|
|
/// </summary>
|
|
public void RegisterHydraulicObject(osBase hydraulicObject)
|
|
{
|
|
if (hydraulicObject == null) return;
|
|
|
|
// Asegurar que el objeto tenga un Id válido
|
|
hydraulicObject.CheckData();
|
|
|
|
if (!HydraulicObjects.Contains(hydraulicObject))
|
|
{
|
|
HydraulicObjects.Add(hydraulicObject);
|
|
var objectId = hydraulicObject.Id.Value.ToString();
|
|
_objectMapping[objectId] = hydraulicObject;
|
|
|
|
// Crear adaptador específico según el tipo de objeto
|
|
CreateAdapterForObject(hydraulicObject);
|
|
|
|
Debug.WriteLine($"TSNet: Objeto hidráulico registrado: {hydraulicObject.Nombre} (ID: {objectId})");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Crea el adaptador apropiado para el objeto hidráulico
|
|
/// </summary>
|
|
private void CreateAdapterForObject(osBase hydraulicObject)
|
|
{
|
|
var objectId = hydraulicObject.Id.Value.ToString();
|
|
|
|
switch (hydraulicObject)
|
|
{
|
|
case osHydTank tank:
|
|
try
|
|
{
|
|
var tankAdapter = new TSNetTankAdapter(tank);
|
|
tankAdapter.CaptureConfigurationForSimulation(); // Capturar configuración inmediatamente
|
|
_tankAdapters[objectId] = tankAdapter;
|
|
Debug.WriteLine($"TSNet: Adaptador de tanque creado para {tank.Nombre}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"TSNet ERROR: Error creando adaptador de tanque para {tank.Nombre}: {ex.Message}");
|
|
}
|
|
break;
|
|
|
|
case osHydPump pump:
|
|
try
|
|
{
|
|
var pumpAdapter = new TSNetPumpAdapter(pump);
|
|
pumpAdapter.CaptureConfigurationForSimulation(); // Capturar configuración inmediatamente
|
|
_pumpAdapters[objectId] = pumpAdapter;
|
|
Debug.WriteLine($"TSNet: Adaptador de bomba creado para {pump.Nombre}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"TSNet ERROR: Error creando adaptador de bomba para {pump.Nombre}: {ex.Message}");
|
|
}
|
|
break;
|
|
|
|
case osHydPipe pipe:
|
|
try
|
|
{
|
|
var pipeAdapter = new TSNetPipeAdapter(pipe);
|
|
pipeAdapter.CaptureConfigurationForSimulation(); // Capturar configuración inmediatamente
|
|
_pipeAdapters[objectId] = pipeAdapter;
|
|
Debug.WriteLine($"TSNet: Adaptador de tubería creado para {pipe.Nombre}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"TSNet ERROR: Error creando adaptador de tubería para {pipe.Nombre}: {ex.Message}");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Debug.WriteLine($"TSNet: Tipo de objeto no soportado: {hydraulicObject.GetType().Name}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Desregistra un objeto hidráulico y su adaptador
|
|
/// </summary>
|
|
public void UnregisterHydraulicObject(osBase hydraulicObject)
|
|
{
|
|
if (hydraulicObject == null) return;
|
|
|
|
var objectId = hydraulicObject.Id.Value.ToString();
|
|
|
|
HydraulicObjects.Remove(hydraulicObject);
|
|
_objectMapping.Remove(objectId);
|
|
|
|
// Remover adaptadores específicos
|
|
_tankAdapters.Remove(objectId);
|
|
_pumpAdapters.Remove(objectId);
|
|
_pipeAdapters.Remove(objectId);
|
|
|
|
Debug.WriteLine($"TSNet: Objeto hidráulico desregistrado: {hydraulicObject.Nombre} (ID: {objectId})");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reconstruye la red hidráulica a partir de los objetos registrados
|
|
/// </summary>
|
|
public void RebuildNetwork()
|
|
{
|
|
try
|
|
{
|
|
Network = new HydraulicNetwork();
|
|
|
|
var hydraulicComponents = HydraulicObjects.OfType<IHydraulicComponent>().ToList();
|
|
|
|
// Primera pasada: Agregar TODOS los nodos
|
|
Debug.WriteLine("TSNet: Primera pasada - agregando todos los nodos...");
|
|
foreach (var component in hydraulicComponents)
|
|
{
|
|
try
|
|
{
|
|
var nodes = component.GetHydraulicNodes();
|
|
foreach (var nodeDefinition in nodes)
|
|
{
|
|
if (nodeDefinition.IsFixedPressure)
|
|
{
|
|
Network.AddNode(nodeDefinition.Name, nodeDefinition.Pressure);
|
|
}
|
|
else
|
|
{
|
|
Network.AddNode(nodeDefinition.Name);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Error agregando nodos del componente {component}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
Debug.WriteLine($"TSNet: Nodos agregados - Total: {Network.Nodes.Count}");
|
|
Debug.WriteLine($"TSNet: Nodos disponibles: {string.Join(", ", Network.Nodes.Keys)}");
|
|
|
|
// Segunda pasada: Agregar TODOS los elementos
|
|
Debug.WriteLine("TSNet: Segunda pasada - agregando todos los elementos...");
|
|
foreach (var component in hydraulicComponents)
|
|
{
|
|
try
|
|
{
|
|
var elements = component.GetHydraulicElements();
|
|
foreach (var elementDefinition in elements)
|
|
{
|
|
try
|
|
{
|
|
// Verificar que los nodos existan antes de agregar el elemento
|
|
if (Network.Nodes.ContainsKey(elementDefinition.FromNode) &&
|
|
Network.Nodes.ContainsKey(elementDefinition.ToNode))
|
|
{
|
|
Network.AddElement(elementDefinition.Element,
|
|
elementDefinition.FromNode,
|
|
elementDefinition.ToNode,
|
|
elementDefinition.Name);
|
|
|
|
Debug.WriteLine($"Rama agregada: {elementDefinition.Name} ({elementDefinition.FromNode} -> {elementDefinition.ToNode})");
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine($"ERROR: Nodo '{elementDefinition.FromNode}' o '{elementDefinition.ToNode}' no existe. " +
|
|
$"Nodos disponibles: {string.Join(", ", Network.Nodes.Keys)}");
|
|
}
|
|
}
|
|
catch (Exception elementEx)
|
|
{
|
|
Debug.WriteLine($"Error agregando elemento {elementDefinition.Name}: {elementEx.Message}");
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Error obteniendo elementos del componente {component}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
Debug.WriteLine($"TSNet: Red reconstruida - {Network.Nodes.Count} nodos, {Network.Branches.Count} ramas");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Error reconstruyendo red TSNet: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Obtiene el adaptador de tanque por ID
|
|
/// </summary>
|
|
public TSNetTankAdapter GetTankAdapter(string objectId)
|
|
{
|
|
return _tankAdapters.TryGetValue(objectId, out var adapter) ? adapter : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Obtiene el adaptador de bomba por ID
|
|
/// </summary>
|
|
public TSNetPumpAdapter GetPumpAdapter(string objectId)
|
|
{
|
|
return _pumpAdapters.TryGetValue(objectId, out var adapter) ? adapter : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Obtiene el adaptador de tubería por ID
|
|
/// </summary>
|
|
public TSNetPipeAdapter GetPipeAdapter(string objectId)
|
|
{
|
|
return _pipeAdapters.TryGetValue(objectId, out var adapter) ? adapter : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resetea todos los valores calculados en todos los objetos
|
|
/// IMPORTANTE: Solo resetea valores calculados por TSNet, NO configuraciones del usuario
|
|
/// </summary>
|
|
public void ResetAllCalculatedValues()
|
|
{
|
|
// Resetear adaptadores de tanques
|
|
foreach (var tankAdapter in _tankAdapters.Values)
|
|
{
|
|
tankAdapter.ResetCalculatedValues();
|
|
}
|
|
|
|
// Resetear adaptadores de bombas
|
|
foreach (var pumpAdapter in _pumpAdapters.Values)
|
|
{
|
|
pumpAdapter.ResetCalculatedValues();
|
|
}
|
|
|
|
// Resetear adaptadores de tuberías
|
|
foreach (var pipeAdapter in _pipeAdapters.Values)
|
|
{
|
|
pipeAdapter.ResetCalculatedValues();
|
|
}
|
|
|
|
Debug.WriteLine("TSNet: Todos los valores calculados han sido reseteados");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Captura la configuración de TODOS los adaptadores al INICIO de simulación
|
|
/// Las configuraciones quedan CONGELADAS hasta que se detenga y reinicie la simulación
|
|
/// </summary>
|
|
private void CaptureAllConfigurations()
|
|
{
|
|
Debug.WriteLine("TSNet: Capturando configuraciones de todos los objetos...");
|
|
|
|
// Capturar configuración de tanques
|
|
foreach (var tankAdapter in _tankAdapters.Values)
|
|
{
|
|
tankAdapter.CaptureConfigurationForSimulation();
|
|
}
|
|
|
|
// Capturar configuración de bombas
|
|
foreach (var pumpAdapter in _pumpAdapters.Values)
|
|
{
|
|
pumpAdapter.CaptureConfigurationForSimulation();
|
|
}
|
|
|
|
// Capturar configuración de tuberías
|
|
foreach (var pipeAdapter in _pipeAdapters.Values)
|
|
{
|
|
pipeAdapter.CaptureConfigurationForSimulation();
|
|
}
|
|
|
|
Debug.WriteLine($"TSNet: Configuraciones capturadas - Tanques: {_tankAdapters.Count}, Bombas: {_pumpAdapters.Count}, Tuberías: {_pipeAdapters.Count}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Aplica los resultados de TSNet a todos los objetos registrados
|
|
/// IMPORTANTE: Esta es la ÚNICA forma de actualizar propiedades calculadas
|
|
/// </summary>
|
|
public void ApplyTSNetResults(Dictionary<string, Dictionary<string, object>> allResults)
|
|
{
|
|
if (allResults == null) return;
|
|
|
|
try
|
|
{
|
|
// Aplicar resultados a tanques
|
|
foreach (var kvp in _tankAdapters)
|
|
{
|
|
var objectId = kvp.Key;
|
|
var adapter = kvp.Value;
|
|
|
|
// Crear diccionarios vacíos para flows y pressures si no existen
|
|
var flows = new Dictionary<string, double>();
|
|
var pressures = new Dictionary<string, double>();
|
|
|
|
adapter.ApplyTSNetResults(flows, pressures);
|
|
}
|
|
|
|
// Aplicar resultados a bombas
|
|
foreach (var kvp in _pumpAdapters)
|
|
{
|
|
var objectId = kvp.Key;
|
|
var adapter = kvp.Value;
|
|
|
|
var flows = new Dictionary<string, double>();
|
|
var pressures = new Dictionary<string, double>();
|
|
|
|
adapter.ApplyTSNetResults(flows, pressures);
|
|
}
|
|
|
|
// Aplicar resultados a tuberías
|
|
foreach (var kvp in _pipeAdapters)
|
|
{
|
|
var objectId = kvp.Key;
|
|
var adapter = kvp.Value;
|
|
|
|
var flows = new Dictionary<string, double>();
|
|
var pressures = new Dictionary<string, double>();
|
|
|
|
adapter.ApplyTSNetResults(flows, pressures);
|
|
}
|
|
|
|
Debug.WriteLine($"TSNet: Resultados aplicados a {_tankAdapters.Count} tanques, {_pumpAdapters.Count} bombas, {_pipeAdapters.Count} tuberías");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Error aplicando resultados TSNet: {ex.Message}");
|
|
throw;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Valida la configuración de todos los adaptadores
|
|
/// </summary>
|
|
public List<string> ValidateAllConfigurations()
|
|
{
|
|
var allErrors = new List<string>();
|
|
|
|
// Verificar que los diccionarios estén inicializados
|
|
if (_tankAdapters == null || _pumpAdapters == null || _pipeAdapters == null)
|
|
{
|
|
allErrors.Add("Error interno: Adaptadores no inicializados correctamente");
|
|
return allErrors;
|
|
}
|
|
|
|
// Validar tanques
|
|
foreach (var adapter in _tankAdapters.Values)
|
|
{
|
|
if (adapter != null)
|
|
{
|
|
var errors = adapter.ValidateConfiguration();
|
|
if (errors != null)
|
|
allErrors.AddRange(errors);
|
|
}
|
|
else
|
|
{
|
|
allErrors.Add("Error interno: Adaptador de tanque nulo detectado");
|
|
}
|
|
}
|
|
|
|
// Validar bombas
|
|
foreach (var adapter in _pumpAdapters.Values)
|
|
{
|
|
if (adapter != null)
|
|
{
|
|
var errors = adapter.ValidateConfiguration();
|
|
if (errors != null)
|
|
allErrors.AddRange(errors);
|
|
}
|
|
else
|
|
{
|
|
allErrors.Add("Error interno: Adaptador de bomba nulo detectado");
|
|
}
|
|
}
|
|
|
|
// Validar tuberías
|
|
foreach (var adapter in _pipeAdapters.Values)
|
|
{
|
|
if (adapter != null)
|
|
{
|
|
var errors = adapter.ValidateConfiguration();
|
|
if (errors != null)
|
|
allErrors.AddRange(errors);
|
|
}
|
|
else
|
|
{
|
|
allErrors.Add("Error interno: Adaptador de tubería nulo detectado");
|
|
}
|
|
}
|
|
|
|
if (allErrors.Count > 0)
|
|
{
|
|
Debug.WriteLine($"TSNet: Se encontraron {allErrors.Count} errores de configuración");
|
|
foreach (var error in allErrors)
|
|
{
|
|
Debug.WriteLine($" - {error}");
|
|
}
|
|
}
|
|
|
|
return allErrors;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region INP File Generation
|
|
|
|
/// <summary>
|
|
/// Genera el archivo INP para TSNet
|
|
/// </summary>
|
|
public async Task<string> GenerateINPFileAsync()
|
|
{
|
|
try
|
|
{
|
|
var inpPath = Path.Combine(WorkingDirectory, $"network_{DateTime.Now:yyyyMMdd_HHmmss}.inp");
|
|
var inpGenerator = new TSNetINPGenerator(Network, Configuration);
|
|
|
|
await inpGenerator.GenerateAsync(inpPath);
|
|
CurrentInpFile = inpPath;
|
|
|
|
Debug.WriteLine($"TSNet: Archivo INP generado: {inpPath}");
|
|
return inpPath;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Error generando archivo INP: {ex.Message}");
|
|
throw;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Simulation
|
|
|
|
/// <summary>
|
|
/// Ejecuta una simulación TSNet asíncrona
|
|
/// </summary>
|
|
public async Task<TSNetResult> RunSimulationAsync()
|
|
{
|
|
try
|
|
{
|
|
if (IsRunning)
|
|
{
|
|
Debug.WriteLine("TSNet: Simulación ya en ejecución");
|
|
return LastResult;
|
|
}
|
|
|
|
IsRunning = true;
|
|
|
|
// PASO 1: CAPTURAR configuración de todos los adaptadores al INICIO
|
|
CaptureAllConfigurations();
|
|
|
|
// Reconstruir red si es necesario
|
|
RebuildNetwork();
|
|
|
|
// Generar archivo INP
|
|
var inpFile = await GenerateINPFileAsync();
|
|
|
|
// Ejecutar simulación TSNet
|
|
var result = await PythonInterop.RunTSNetSimulationAsync(inpFile, Configuration.OutputDirectory);
|
|
|
|
// Procesar resultados
|
|
LastResult = new TSNetResult
|
|
{
|
|
Success = result.Success,
|
|
Message = result.Success ? "Simulación completada" : result.Error,
|
|
OutputDirectory = Configuration.OutputDirectory,
|
|
PythonOutput = result.Output,
|
|
PythonError = result.Error
|
|
};
|
|
|
|
// Aplicar resultados a objetos hidráulicos
|
|
if (LastResult.Success)
|
|
{
|
|
await ApplyResultsToObjectsAsync();
|
|
}
|
|
|
|
// Disparar evento
|
|
SimulationCompleted?.Invoke(this, LastResult);
|
|
|
|
return LastResult;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LastResult = new TSNetResult
|
|
{
|
|
Success = false,
|
|
Message = $"Error en simulación: {ex.Message}"
|
|
};
|
|
|
|
Debug.WriteLine($"Error en simulación TSNet: {ex.Message}");
|
|
return LastResult;
|
|
}
|
|
finally
|
|
{
|
|
IsRunning = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ejecuta una simulación continua (para tiempo real)
|
|
/// </summary>
|
|
public async Task StartContinuousSimulationAsync(int intervalMs = 1000)
|
|
{
|
|
// TODO: Implementar simulación continua
|
|
// Esta será la clave para integración en tiempo real
|
|
await Task.Delay(intervalMs);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Results Processing
|
|
|
|
/// <summary>
|
|
/// Aplica los resultados de TSNet a los objetos hidráulicos
|
|
/// </summary>
|
|
private async Task ApplyResultsToObjectsAsync()
|
|
{
|
|
try
|
|
{
|
|
// TODO: Leer archivos de resultados de TSNet y aplicar a objetos
|
|
// Por ahora, aplicamos resultados dummy
|
|
|
|
var flows = new Dictionary<string, double>();
|
|
var pressures = new Dictionary<string, double>();
|
|
|
|
foreach (var obj in HydraulicObjects)
|
|
{
|
|
if (obj is IHydraulicComponent hydraulicComponent)
|
|
{
|
|
hydraulicComponent.ApplyHydraulicResults(flows, pressures);
|
|
}
|
|
}
|
|
|
|
Debug.WriteLine("TSNet: Resultados aplicados a objetos hidráulicos");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Error aplicando resultados: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
|
|
/// <summary>
|
|
/// Procesa un componente hidráulico para crear nodos y elementos
|
|
/// </summary>
|
|
private void ProcessHydraulicComponent(IHydraulicComponent component)
|
|
{
|
|
try
|
|
{
|
|
// Obtener nodos del componente
|
|
var nodes = component.GetHydraulicNodes();
|
|
foreach (var nodeDefinition in nodes)
|
|
{
|
|
if (nodeDefinition.IsFixedPressure)
|
|
{
|
|
Network.AddNode(nodeDefinition.Name, nodeDefinition.Pressure);
|
|
}
|
|
else
|
|
{
|
|
Network.AddNode(nodeDefinition.Name);
|
|
}
|
|
}
|
|
|
|
// Obtener elementos del componente
|
|
var elements = component.GetHydraulicElements();
|
|
foreach (var elementDefinition in elements)
|
|
{
|
|
try
|
|
{
|
|
// Verificar que los nodos existan antes de agregar el elemento
|
|
if (Network.Nodes.ContainsKey(elementDefinition.FromNode) &&
|
|
Network.Nodes.ContainsKey(elementDefinition.ToNode))
|
|
{
|
|
Network.AddElement(elementDefinition.Element,
|
|
elementDefinition.FromNode,
|
|
elementDefinition.ToNode,
|
|
elementDefinition.Name);
|
|
|
|
Debug.WriteLine($"Rama agregada: {elementDefinition.Name} ({elementDefinition.FromNode} -> {elementDefinition.ToNode})");
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine($"ERROR: Nodo '{elementDefinition.FromNode}' o '{elementDefinition.ToNode}' no existe. " +
|
|
$"Nodos disponibles: {string.Join(", ", Network.Nodes.Keys)}");
|
|
}
|
|
}
|
|
catch (Exception elementEx)
|
|
{
|
|
Debug.WriteLine($"Error agregando elemento {elementDefinition.Name}: {elementEx.Message}");
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Error procesando componente hidráulico {component}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asegura que el directorio de trabajo existe
|
|
/// </summary>
|
|
private void EnsureWorkingDirectory()
|
|
{
|
|
try
|
|
{
|
|
if (!Directory.Exists(WorkingDirectory))
|
|
{
|
|
Directory.CreateDirectory(WorkingDirectory);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Error creando directorio de trabajo: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Additional Methods for Compatibility
|
|
|
|
/// <summary>
|
|
/// Reinicia el simulador hidráulico (equivalente a Reset del HydraulicSimulationManager)
|
|
/// </summary>
|
|
public void Reset()
|
|
{
|
|
ResetAllCalculatedValues();
|
|
Debug.WriteLine("TSNet: Simulador reiniciado");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Obtiene estadísticas de la simulación
|
|
/// </summary>
|
|
public string GetSimulationStats()
|
|
{
|
|
return $"TSNet - Objetos: {HydraulicObjects.Count}, Tanques: {_tankAdapters.Count}, Bombas: {_pumpAdapters.Count}, Tuberías: {_pipeAdapters.Count}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Propiedad para habilitar/deshabilitar la simulación hidráulica
|
|
/// </summary>
|
|
public bool IsHydraulicSimulationEnabled { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Limpia todos los objetos hidráulicos
|
|
/// </summary>
|
|
public void ClearHydraulicObjects()
|
|
{
|
|
HydraulicObjects.Clear();
|
|
_objectMapping.Clear();
|
|
_tankAdapters.Clear();
|
|
_pumpAdapters.Clear();
|
|
_pipeAdapters.Clear();
|
|
Network = new HydraulicNetwork();
|
|
Debug.WriteLine("TSNet: Todos los objetos hidráulicos limpiados");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invalida la red para forzar reconstrucción
|
|
/// </summary>
|
|
public void InvalidateNetwork()
|
|
{
|
|
NetworkNeedsRebuild = true;
|
|
Debug.WriteLine("TSNet: Red invalidada y marcada para reconstrucción");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable
|
|
|
|
public void Dispose()
|
|
{
|
|
try
|
|
{
|
|
HydraulicObjects?.Clear();
|
|
_objectMapping?.Clear();
|
|
|
|
// Limpiar archivos temporales si es necesario
|
|
// Note: No finalizamos Python aquí porque puede ser usado por otros
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Error en dispose de TSNetSimulationManager: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
#region Supporting Classes
|
|
|
|
/// <summary>
|
|
/// Configuración para simulaciones TSNet
|
|
/// </summary>
|
|
public class TSNetConfiguration
|
|
{
|
|
/// <summary>
|
|
/// Duración de la simulación en segundos
|
|
/// </summary>
|
|
public double Duration { get; set; } = 10.0;
|
|
|
|
/// <summary>
|
|
/// Paso de tiempo en segundos
|
|
/// </summary>
|
|
public double TimeStep { get; set; } = 0.01;
|
|
|
|
/// <summary>
|
|
/// Directorio de salida para resultados
|
|
/// </summary>
|
|
public string OutputDirectory { get; set; }
|
|
|
|
/// <summary>
|
|
/// Configuración de válvulas/bombas
|
|
/// </summary>
|
|
public Dictionary<string, object> DeviceSettings { get; set; } = new Dictionary<string, object>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resultado de una simulación TSNet
|
|
/// </summary>
|
|
public class TSNetResult
|
|
{
|
|
public bool Success { get; set; }
|
|
public string Message { get; set; }
|
|
public string OutputDirectory { get; set; }
|
|
public string PythonOutput { get; set; }
|
|
public string PythonError { get; set; }
|
|
public DateTime Timestamp { get; set; } = DateTime.Now;
|
|
}
|
|
|
|
#endregion
|
|
}
|