using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace CtrEditor.HydraulicSimulator.Python
{
///
/// Interoperabilidad con Python usando CPython embebido
/// Permite ejecutar scripts de TSNet desde C#
///
public static class PythonInterop
{
#region Python DLL Imports
[DllImport("python312.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int Py_Initialize();
[DllImport("python312.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int Py_Finalize();
[DllImport("python312.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int PyRun_SimpleString(string command);
[DllImport("python312.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr PyRun_String(string str, int start, IntPtr globals, IntPtr locals);
[DllImport("python312.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr PyImport_ImportModule(string name);
[DllImport("python312.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int Py_IsInitialized();
#endregion
#region Configuration
///
/// Ruta base de la instalación de Python
///
public static string PythonBasePath { get; set; } = @"D:\Proyectos\VisualStudio\CtrEditor\bin\Debug\net8.0-windows8.0\tsnet";
///
/// Ruta al ejecutable de Python
///
public static string PythonExecutable => Path.Combine(PythonBasePath, "python.exe");
///
/// Ruta a la DLL de Python
///
public static string PythonDll => Path.Combine(PythonBasePath, "python312.dll");
///
/// Indica si Python está inicializado
///
public static bool IsInitialized { get; private set; } = false;
#endregion
#region Initialization
///
/// Inicializa el intérprete de Python embebido
///
public static bool Initialize()
{
try
{
if (IsInitialized)
return true;
// Verificar que los archivos existan
if (!File.Exists(PythonDll))
{
Debug.WriteLine($"Error: No se encuentra la DLL de Python en: {PythonDll}");
return false;
}
// Configurar el path de Python
Environment.SetEnvironmentVariable("PYTHONPATH",
$"{PythonBasePath};{Path.Combine(PythonBasePath, "Lib")};{Path.Combine(PythonBasePath, "site-packages")}");
// Inicializar Python
var result = Py_Initialize();
IsInitialized = Py_IsInitialized() != 0;
if (IsInitialized)
{
// Configurar paths de Python
var pathSetup = $@"
import sys
sys.path.insert(0, r'{PythonBasePath}')
sys.path.insert(0, r'{Path.Combine(PythonBasePath, "Lib")}')
sys.path.insert(0, r'{Path.Combine(PythonBasePath, "site-packages")}')
";
PyRun_SimpleString(pathSetup);
Debug.WriteLine("Python inicializado correctamente");
Debug.WriteLine($"PYTHONPATH: {Environment.GetEnvironmentVariable("PYTHONPATH")}");
}
else
{
Debug.WriteLine("Error al inicializar Python");
}
return IsInitialized;
}
catch (Exception ex)
{
Debug.WriteLine($"Error al inicializar Python: {ex.Message}");
return false;
}
}
///
/// Finaliza el intérprete de Python
///
public static void Finalize()
{
try
{
if (IsInitialized)
{
Py_Finalize();
IsInitialized = false;
Debug.WriteLine("Python finalizado");
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error al finalizar Python: {ex.Message}");
}
}
#endregion
#region Script Execution
///
/// Ejecuta un comando Python simple
///
public static bool ExecuteCommand(string command)
{
try
{
if (!IsInitialized && !Initialize())
return false;
var result = PyRun_SimpleString(command);
return result == 0;
}
catch (Exception ex)
{
Debug.WriteLine($"Error ejecutando comando Python: {ex.Message}");
return false;
}
}
///
/// Ejecuta un script Python desde archivo
///
public static bool ExecuteScript(string scriptPath)
{
try
{
if (!File.Exists(scriptPath))
{
Debug.WriteLine($"Error: Script no encontrado: {scriptPath}");
return false;
}
var scriptContent = File.ReadAllText(scriptPath);
return ExecuteCommand(scriptContent);
}
catch (Exception ex)
{
Debug.WriteLine($"Error ejecutando script: {ex.Message}");
return false;
}
}
///
/// Ejecuta un script Python con argumentos usando subprocess
/// Útil para scripts más complejos o cuando necesitamos capturar output
///
public static async Task ExecuteScriptAsync(string scriptPath, string arguments = "")
{
try
{
var startInfo = new ProcessStartInfo
{
FileName = PythonExecutable,
Arguments = $"\"{scriptPath}\" {arguments}",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
WorkingDirectory = Path.GetDirectoryName(scriptPath)
};
// Configurar environment variables
startInfo.EnvironmentVariables["PYTHONPATH"] =
$"{PythonBasePath};{Path.Combine(PythonBasePath, "Lib")};{Path.Combine(PythonBasePath, "site-packages")}";
using var process = new Process { StartInfo = startInfo };
process.Start();
var outputTask = process.StandardOutput.ReadToEndAsync();
var errorTask = process.StandardError.ReadToEndAsync();
await process.WaitForExitAsync();
var output = await outputTask;
var error = await errorTask;
return new PythonExecutionResult
{
Success = process.ExitCode == 0,
Output = output,
Error = error,
ExitCode = process.ExitCode
};
}
catch (Exception ex)
{
return new PythonExecutionResult
{
Success = false,
Error = ex.Message,
ExitCode = -1
};
}
}
#endregion
#region TSNet Specific Methods
///
/// Verifica si TSNet está disponible
///
public static bool IsTSNetAvailable()
{
try
{
if (!IsInitialized && !Initialize())
return false;
var command = @"
try:
import tsnet
print('TSNet version:', tsnet.__version__)
result = True
except ImportError as e:
print('TSNet not available:', e)
result = False
";
return ExecuteCommand(command);
}
catch (Exception ex)
{
Debug.WriteLine($"Error verificando TSNet: {ex.Message}");
return false;
}
}
///
/// Ejecuta una simulación TSNet básica
///
public static async Task RunTSNetSimulationAsync(string inpFilePath, string outputDir)
{
var scriptContent = $@"
import tsnet
import os
try:
# Crear directorio de salida si no existe
os.makedirs(r'{outputDir}', exist_ok=True)
# Cargar el modelo
wn = tsnet.network.WaterNetworkModel(r'{inpFilePath}')
# Configurar simulación transitoria
wn.set_time(duration=10.0, dt=0.01) # 10 segundos, dt=0.01s
# Ejecutar simulación
results = tsnet.simulation.run_transient_simulation(wn, results_dir=r'{outputDir}')
print('Simulación completada exitosamente')
print(f'Resultados guardados en: {outputDir}')
except Exception as e:
print(f'Error en simulación TSNet: {{e}}')
raise
";
var tempScript = Path.Combine(Path.GetTempPath(), "tsnet_simulation.py");
await File.WriteAllTextAsync(tempScript, scriptContent);
try
{
return await ExecuteScriptAsync(tempScript);
}
finally
{
if (File.Exists(tempScript))
File.Delete(tempScript);
}
}
#endregion
}
///
/// Resultado de la ejecución de un script Python
///
public class PythonExecutionResult
{
public bool Success { get; set; }
public string Output { get; set; } = "";
public string Error { get; set; } = "";
public int ExitCode { get; set; }
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine($"Success: {Success}");
sb.AppendLine($"ExitCode: {ExitCode}");
if (!string.IsNullOrEmpty(Output))
sb.AppendLine($"Output: {Output}");
if (!string.IsNullOrEmpty(Error))
sb.AppendLine($"Error: {Error}");
return sb.ToString();
}
}
}