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
{
///
/// Gestor de simulaciones TSNet que reemplaza o complementa al HydraulicSimulationManager
///
public class TSNetSimulationManager : IDisposable
{
#region Properties and Fields
///
/// Red hidráulica actual
///
public HydraulicNetwork Network { get; private set; }
///
/// Lista de objetos hidráulicos registrados
///
public List HydraulicObjects { get; private set; }
///
/// Directorio de trabajo para archivos INP y resultados
///
public string WorkingDirectory { get; set; }
///
/// Ruta del archivo INP actual
///
public string CurrentInpFile { get; private set; }
///
/// Configuración de la simulación transitoria
///
public TSNetConfiguration Configuration { get; set; }
///
/// Resultado de la última simulación
///
public TSNetResult LastResult { get; private set; }
///
/// Indica si la simulación está ejecutándose
///
public bool IsRunning { get; private set; }
///
/// Evento disparado cuando se completa una simulación
///
public event EventHandler SimulationCompleted;
///
/// Mapeo de objetos por ID
///
private Dictionary _objectMapping;
///
/// Adaptadores para tanques registrados
///
private Dictionary _tankAdapters;
///
/// Adaptadores para bombas registradas
///
private Dictionary _pumpAdapters;
///
/// Adaptadores para tuberías registradas
///
private Dictionary _pipeAdapters;
#endregion
#region Constructor
public TSNetSimulationManager()
{
Network = new HydraulicNetwork();
HydraulicObjects = new List();
_objectMapping = new Dictionary();
// Inicializar adaptadores
_tankAdapters = new Dictionary();
_pumpAdapters = new Dictionary();
_pipeAdapters = new Dictionary();
// 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
///
/// Registra un objeto hidráulico en el sistema TSNet
/// IMPORTANTE: NO se realizan cálculos locales - todo se delega a TSNet
///
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})");
}
}
///
/// Crea el adaptador apropiado para el objeto hidráulico
///
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;
}
}
///
/// Desregistra un objeto hidráulico y su adaptador
///
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})");
}
///
/// Reconstruye la red hidráulica a partir de los objetos registrados
///
public void RebuildNetwork()
{
try
{
Network = new HydraulicNetwork();
// Procesar objetos IHydraulicComponent
foreach (var obj in HydraulicObjects)
{
if (obj is IHydraulicComponent hydraulicComponent)
{
ProcessHydraulicComponent(hydraulicComponent);
}
}
Debug.WriteLine($"TSNet: Red reconstruida - {Network.Nodes.Count} nodos, {Network.Branches.Count} ramas");
}
catch (Exception ex)
{
Debug.WriteLine($"Error reconstruyendo red TSNet: {ex.Message}");
}
}
///
/// Obtiene el adaptador de tanque por ID
///
public TSNetTankAdapter GetTankAdapter(string objectId)
{
return _tankAdapters.TryGetValue(objectId, out var adapter) ? adapter : null;
}
///
/// Obtiene el adaptador de bomba por ID
///
public TSNetPumpAdapter GetPumpAdapter(string objectId)
{
return _pumpAdapters.TryGetValue(objectId, out var adapter) ? adapter : null;
}
///
/// Obtiene el adaptador de tubería por ID
///
public TSNetPipeAdapter GetPipeAdapter(string objectId)
{
return _pipeAdapters.TryGetValue(objectId, out var adapter) ? adapter : null;
}
///
/// Resetea todos los valores calculados en todos los objetos
/// IMPORTANTE: Solo resetea valores calculados por TSNet, NO configuraciones del usuario
///
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");
}
///
/// 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
///
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}");
}
///
/// Aplica los resultados de TSNet a todos los objetos registrados
/// IMPORTANTE: Esta es la ÚNICA forma de actualizar propiedades calculadas
///
public void ApplyTSNetResults(Dictionary> 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();
var pressures = new Dictionary();
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();
var pressures = new Dictionary();
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();
var pressures = new Dictionary();
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;
}
}
///
/// Valida la configuración de todos los adaptadores
///
public List ValidateAllConfigurations()
{
var allErrors = new List();
// Validar tanques
foreach (var adapter in _tankAdapters.Values)
{
allErrors.AddRange(adapter.ValidateConfiguration());
}
// Validar bombas
foreach (var adapter in _pumpAdapters.Values)
{
allErrors.AddRange(adapter.ValidateConfiguration());
}
// Validar tuberías
foreach (var adapter in _pipeAdapters.Values)
{
allErrors.AddRange(adapter.ValidateConfiguration());
}
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
///
/// Genera el archivo INP para TSNet
///
public async Task 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
///
/// Ejecuta una simulación TSNet asíncrona
///
public async Task 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;
}
}
///
/// Ejecuta una simulación continua (para tiempo real)
///
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
///
/// Aplica los resultados de TSNet a los objetos hidráulicos
///
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();
var pressures = new Dictionary();
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
///
/// Procesa un componente hidráulico para crear nodos y elementos
///
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)
{
// TODO: Convertir elementDefinition a Element y agregar a la red
// Por ahora creamos elementos básicos
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error procesando componente hidráulico {component}: {ex.Message}");
}
}
///
/// Asegura que el directorio de trabajo existe
///
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 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
///
/// Configuración para simulaciones TSNet
///
public class TSNetConfiguration
{
///
/// Duración de la simulación en segundos
///
public double Duration { get; set; } = 10.0;
///
/// Paso de tiempo en segundos
///
public double TimeStep { get; set; } = 0.01;
///
/// Directorio de salida para resultados
///
public string OutputDirectory { get; set; }
///
/// Configuración de válvulas/bombas
///
public Dictionary DeviceSettings { get; set; } = new Dictionary();
}
///
/// Resultado de una simulación TSNet
///
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
}