diff --git a/MainViewModel.cs b/MainViewModel.cs
index 9c69161..ab70b6f 100644
--- a/MainViewModel.cs
+++ b/MainViewModel.cs
@@ -57,6 +57,15 @@ namespace CtrEditor
private const double MAX_PLC_INTERVAL = 100; // Máximo intervalo PLC (ms)
private const double SIM_PRIORITY_TIME = 10; // Tiempo reservado para simulación (ms)
+ // Sistema adaptativo para timing de simulación
+ private double lastSimExecutionTime = 0;
+ private double maxSimExecutionTime = 0;
+ private int simTimingAdaptationCounter = 0;
+ private const int SIM_ADAPTATION_SAMPLES = 5; // Evaluar cada 5 muestras para más responsividad
+ private const double MIN_SIM_INTERVAL = 8; // Mínimo intervalo simulación (ms)
+ private const double MAX_SIM_INTERVAL = 100; // Máximo intervalo simulación (ms)
+ private const double SIM_BUFFER_TIME = 2; // Buffer de 2ms extra respecto al tiempo real
+
private float TiempoDesdeStartSimulacion;
private bool Debug_SimulacionCreado = false;
@@ -414,7 +423,7 @@ namespace CtrEditor
ItemDoubleClickCommand = new ParameterizedRelayCommand(ExecuteDoubleClick);
_timerSimulacion = new System.Timers.Timer();
- _timerSimulacion.Interval = 10; // 10ms - más preciso que DispatcherTimer
+ _timerSimulacion.Interval = 15; // 15ms inicial - más conservador para permitir adaptación
_timerSimulacion.Elapsed += OnTickSimulacion;
_timerSimulacion.AutoReset = true; // Reinicio automático
_timerSimulacion.SynchronizingObject = null; // No sincronizar automáticamente con UI
@@ -1122,6 +1131,9 @@ namespace CtrEditor
IsSimulationRunning = true;
+ // Resetear contadores adaptativos para timing limpio
+ ResetAdaptiveTimingCounters();
+
// Ocultar rectángulos de selección antes de iniciar la simulación
_objectManager.UpdateSelectionVisuals();
@@ -1153,6 +1165,9 @@ namespace CtrEditor
_timerSimulacion.Stop();
_timerPLCUpdate.Stop(); // También detener el timer PLC
+ // Resetear contadores adaptativos al detener
+ ResetAdaptiveTimingCounters();
+
// Restaurar los rectángulos de selección si hay objetos seleccionados
_objectManager.UpdateSelectionVisuals();
@@ -1212,6 +1227,10 @@ namespace CtrEditor
}
executionStopwatch.Stop(); // ✅ CORREGIDO: Detener cronómetro de ejecución
+
+ // Sistema adaptativo de timing de simulación
+ AdaptSimulationTiming(executionStopwatch.Elapsed.TotalMilliseconds);
+
//Debug.WriteLine($"OnTickSimulacion execution time: {stopwatch.ElapsedMilliseconds} ms");
//Debug.WriteLine($"OnTickSimulacion execution time: {executionStopwatch.Elapsed.TotalMilliseconds:F2}ms | Timer interval: {timeBetweenCalls:F2}ms | Objects: {ObjetosSimulables?.Count ?? 0}");
});
@@ -1352,6 +1371,73 @@ namespace CtrEditor
}
}
+ ///
+ /// Sistema adaptativo para el timing de simulación que mantiene 2ms de buffer extra
+ ///
+ private void AdaptSimulationTiming(double executionTime)
+ {
+ lastSimExecutionTime = executionTime;
+ maxSimExecutionTime = Math.Max(maxSimExecutionTime, executionTime);
+ simTimingAdaptationCounter++;
+
+ // Evaluar cada SIM_ADAPTATION_SAMPLES ejecuciones para responsividad rápida
+ if (simTimingAdaptationCounter >= SIM_ADAPTATION_SAMPLES)
+ {
+ double currentInterval = _timerSimulacion.Interval;
+ double newInterval = currentInterval;
+
+ // Calcular intervalo ideal: tiempo de ejecución máximo + buffer de 2ms
+ double idealInterval = maxSimExecutionTime + SIM_BUFFER_TIME;
+
+ // Si el tiempo de ejecución + buffer > intervalo actual, necesitamos más tiempo
+ if (idealInterval > currentInterval)
+ {
+ // Incrementar intervalo para evitar que el sistema se vuelva inusable
+ newInterval = Math.Min(idealInterval + 1, MAX_SIM_INTERVAL); // +1ms adicional de seguridad
+ Debug.WriteLine($"Simulación Timer: Aumentando intervalo a {newInterval:F1}ms (ejecución máxima: {maxSimExecutionTime:F1}ms)");
+ }
+ // Si hemos estado muy por debajo del umbral, podemos reducir el intervalo
+ else if (idealInterval < currentInterval * 0.8)
+ {
+ newInterval = Math.Max(idealInterval, MIN_SIM_INTERVAL);
+ Debug.WriteLine($"Simulación Timer: Reduciendo intervalo a {newInterval:F1}ms (ejecución máxima: {maxSimExecutionTime:F1}ms)");
+ }
+
+ // Aplicar el nuevo intervalo si cambió significativamente
+ if (Math.Abs(newInterval - currentInterval) > 0.5)
+ {
+ _timerSimulacion.Interval = newInterval;
+ Debug.WriteLine($"Simulación Timer: Intervalo actualizado de {currentInterval:F1}ms a {newInterval:F1}ms");
+ }
+
+ // Reset para el próximo período de evaluación
+ maxSimExecutionTime = 0;
+ simTimingAdaptationCounter = 0;
+ }
+ }
+
+ ///
+ /// Resetea todos los contadores adaptativos de timing para empezar con mediciones limpias
+ ///
+ private void ResetAdaptiveTimingCounters()
+ {
+ // Reset contadores de simulación
+ lastSimExecutionTime = 0;
+ maxSimExecutionTime = 0;
+ simTimingAdaptationCounter = 0;
+
+ // Reset contadores de PLC
+ lastPlcExecutionTime = 0;
+ maxPlcExecutionTime = 0;
+ plcTimingAdaptationCounter = 0;
+
+ // Restablecer intervalos iniciales conservadores
+ _timerSimulacion.Interval = 15; // 15ms inicial
+ _timerPLCUpdate.Interval = 10; // 10ms inicial
+
+ Debug.WriteLine("Timing adaptativo: Contadores y intervalos reseteados");
+ }
+
private void OpenWorkDirectory()
{
var dialog = new VistaFolderBrowserDialog();
diff --git a/Scripts/EstandarizarObjetos.ps1 b/Scripts/EstandarizarObjetos.ps1
deleted file mode 100644
index cc345ac..0000000
--- a/Scripts/EstandarizarObjetos.ps1
+++ /dev/null
@@ -1,138 +0,0 @@
-# Script para estandarizar objetos derivados de osBase
-# Ejecutar desde la raíz del proyecto CtrEditor
-
-Write-Host "=== Estandarización de Objetos osBase ===" -ForegroundColor Green
-
-# Mapeo de nombres de clase mejorados
-$nombresClase = @{
- # Transportes
- "ucTransporteTTop" = "Transporte TTOP"
- "ucTransporteTTopDualInverter" = "Transporte TTOP Dual Inverter"
- "ucTransporteGuias" = "Transporte con Guías"
- "ucTransporteGuiasUnion" = "Transporte con Guías Unión"
- "ucTransporteCurva" = "Transporte Curva"
- "ucTransporteCurvaGuias" = "Transporte Curva con Guías"
-
- # Sensores
- "ucPhotocell" = "Fotocélula"
- "ucGearEncoder" = "Encoder de Engranaje"
- "ucEncoderMotor" = "Encoder Motor"
- "ucEncoderMotorLineal" = "Encoder Motor Lineal"
- "ucSensTemperatura" = "Sensor de Temperatura"
- "ucBoton" = "Botón"
-
- # Actuadores
- "ucVMmotorSim" = "Motor VetroMeccanica"
- "ucValvulaFluido" = "Válvula de Fluido"
-
- # Elementos Estáticos
- "ucGuia" = "Guía"
- "ucDescarte" = "Descarte"
-
- # Emuladores
- "ucBottGenerator" = "Generador de Botellas"
- "ucFiller" = "Llenadora"
- "ucTanque" = "Tanque"
-
- # Señales
- "ucAnalogTag" = "Tag Analógico"
- "ucBoolTag" = "Tag Digital"
- "ucConsensGeneric" = "Consenso Genérico"
-
- # Datos
- "ucExtraccionTag" = "Extracción de Tag"
- "ucBuscarCoincidencias" = "Búsqueda de Coincidencias"
-
- # Decorativos
- "ucCustomImage" = "Imagen Personalizada"
- "ucFramePlate" = "Marco"
- "ucTextPlate" = "Placa de Texto"
-
- # Fluidos
- "ucTuberiaFluido" = "Tubería de Fluido"
- "osSistemaFluidos" = "Sistema de Fluidos"
-
- # Trazas
- "ucTrace3" = "Traza 3 Puntos"
- "ucTraceSimple" = "Traza Simple"
-
- # Dinámicos
- "ucBotella" = "Botella"
- "ucBotellaCuello" = "Botella con Cuello"
-
- # Ejemplo
- "ucBasicExample" = "Ejemplo Básico"
-}
-
-# Mapeo de categorías estándar
-$categoriasEstandar = @{
- "Id:" = "Identificación"
- "Layout:" = "Posición y Tamaño"
- "Setup:" = "Configuración"
- "Simulation:" = "Simulación"
- "PLC link:" = "Enlace PLC"
- "Debug:" = "Información"
- "Encoder:" = "Encoder"
- "General:" = "Identificación"
-}
-
-function Actualizar-NombreClase {
- param(
- [string]$archivo,
- [string]$nuevoNombre
- )
-
- $contenido = Get-Content $archivo -Raw
- $patron = 'return\s+"[^"]*";'
- $reemplazo = "return `"$nuevoNombre`";"
-
- if ($contenido -match $patron) {
- $contenido = $contenido -replace $patron, $reemplazo
- Set-Content $archivo $contenido -NoNewline
- Write-Host "✓ Actualizado NombreClase en $archivo" -ForegroundColor Yellow
- }
-}
-
-function Estandarizar-Categorias {
- param([string]$archivo)
-
- $contenido = Get-Content $archivo -Raw
- $modificado = $false
-
- foreach ($categoria in $categoriasEstandar.Keys) {
- $patron = [regex]::Escape("Category(`"$categoria`")")
- $reemplazo = "Category(`"$($categoriasEstandar[$categoria])`")"
-
- if ($contenido -match $patron) {
- $contenido = $contenido -replace $patron, $reemplazo
- $modificado = $true
- }
- }
-
- if ($modificado) {
- Set-Content $archivo $contenido -NoNewline
- Write-Host "✓ Estandarizadas categorías en $archivo" -ForegroundColor Cyan
- }
-}
-
-# Buscar todos los archivos .cs en ObjetosSim
-$archivos = Get-ChildItem -Path "ObjetosSim" -Filter "*.cs" -Recurse | Where-Object {
- $_.Name -like "uc*.cs" -or $_.Name -like "os*.cs"
-}
-
-Write-Host "Encontrados $($archivos.Count) archivos para procesar" -ForegroundColor Blue
-
-foreach ($archivo in $archivos) {
- $nombreArchivo = [System.IO.Path]::GetFileNameWithoutExtension($archivo.Name)
-
- # Actualizar nombre de clase si está en el mapeo
- if ($nombresClase.ContainsKey($nombreArchivo)) {
- Actualizar-NombreClase -archivo $archivo.FullName -nuevoNombre $nombresClase[$nombreArchivo]
- }
-
- # Estandarizar categorías
- Estandarizar-Categorias -archivo $archivo.FullName
-}
-
-Write-Host "=== Proceso completado ===" -ForegroundColor Green
-Write-Host "Revise los cambios y verifique que la compilación sea exitosa" -ForegroundColor Yellow
\ No newline at end of file
diff --git a/Scripts/HydraulicSystemTests.py b/Scripts/HydraulicSystemTests.py
deleted file mode 100644
index 4fa71fc..0000000
--- a/Scripts/HydraulicSystemTests.py
+++ /dev/null
@@ -1,629 +0,0 @@
-#!/usr/bin/env python3
-"""
-Sistema de Pruebas Hidráulicas para CtrEditor
-Basado en FluidManagementSystem.md y MCP_LLM_Guide.md
-
-Este script ejecuta pruebas sistemáticas del sistema hidráulico:
-- Equilibrio de flujo entre tanques
-- Cálculos de unidades correctos
-- Comportamiento con diferentes tipos de fluidos
-- Verificación de niveles después de tiempo determinado
-"""
-
-import json
-import time
-import requests
-import math
-from typing import Dict, List, Tuple, Any
-from dataclasses import dataclass, asdict
-
-
-@dataclass
-class FluidProperties:
- """Propiedades de un fluido"""
-
- name: str
- density: float # kg/m³
- viscosity: float # Pa·s
- temperature: float # °C
-
-
-@dataclass
-class TankState:
- """Estado de un tanque"""
-
- id: str
- name: str
- level_m: float
- volume_l: float
- max_volume_l: float
- fluid_primary: FluidProperties
- fluid_secondary: FluidProperties
- primary_percentage: float
-
-
-@dataclass
-class PumpState:
- """Estado de una bomba"""
-
- id: str
- name: str
- is_running: bool
- current_flow: float # m³/s
- max_flow: float # m³/s
- pump_head: float # m
-
-
-@dataclass
-class PipeState:
- """Estado de una tubería"""
-
- id: str
- name: str
- current_flow: float # m³/s
- pressure_drop: float # Pa
- fluid_type: str
-
-
-class HydraulicTestManager:
- """Administrador de pruebas hidráulicas"""
-
- def __init__(self, mcp_url: str = "http://localhost:3000"):
- self.mcp_url = mcp_url
- self.test_results = []
-
- def send_mcp_request(self, method: str, params: Dict = None) -> Dict:
- """Envía una solicitud MCP y retorna la respuesta"""
- payload = {"jsonrpc": "2.0", "id": 1, "method": method, "params": params or {}}
-
- try:
- response = requests.post(self.mcp_url, json=payload, timeout=10)
- response.raise_for_status()
- return response.json()
- except Exception as e:
- print(f"Error en solicitud MCP: {e}")
- return {"error": str(e)}
-
- def get_simulation_objects(self) -> List[Dict]:
- """Obtiene todos los objetos de la simulación"""
- response = self.send_mcp_request("list_objects")
- if "result" in response:
- return response["result"].get("objects", [])
- return []
-
- def get_tanks(self) -> List[TankState]:
- """Obtiene el estado de todos los tanques"""
- objects = self.get_simulation_objects()
- tanks = []
-
- for obj in objects:
- if obj.get("type") == "osHydTank":
- fluid_props = obj.get("properties", {})
-
- # Crear propiedades de fluido primario
- primary_fluid = FluidProperties(
- name=fluid_props.get("PrimaryFluidName", "Water"),
- density=fluid_props.get("PrimaryFluidDensity", 1000.0),
- viscosity=fluid_props.get("PrimaryFluidViscosity", 0.001),
- temperature=fluid_props.get("PrimaryFluidTemperature", 20.0),
- )
-
- # Crear propiedades de fluido secundario
- secondary_fluid = FluidProperties(
- name=fluid_props.get("SecondaryFluidName", "Air"),
- density=fluid_props.get("SecondaryFluidDensity", 1.225),
- viscosity=fluid_props.get("SecondaryFluidViscosity", 1.8e-5),
- temperature=fluid_props.get("SecondaryFluidTemperature", 20.0),
- )
-
- tank = TankState(
- id=obj["id"],
- name=obj["name"],
- level_m=fluid_props.get("CurrentLevelM", 0.0),
- volume_l=fluid_props.get("CurrentVolumeL", 0.0),
- max_volume_l=fluid_props.get("MaxVolumeL", 1000.0),
- fluid_primary=primary_fluid,
- fluid_secondary=secondary_fluid,
- primary_percentage=fluid_props.get("PrimaryFluidPercentage", 100.0),
- )
- tanks.append(tank)
-
- return tanks
-
- def get_pumps(self) -> List[PumpState]:
- """Obtiene el estado de todas las bombas"""
- objects = self.get_simulation_objects()
- pumps = []
-
- for obj in objects:
- if obj.get("type") == "osHydPump":
- props = obj.get("properties", {})
- pump = PumpState(
- id=obj["id"],
- name=obj["name"],
- is_running=props.get("IsRunning", False),
- current_flow=props.get("CurrentFlow", 0.0),
- max_flow=props.get("MaxFlow", 0.02),
- pump_head=props.get("PumpHead", 75.0),
- )
- pumps.append(pump)
-
- return pumps
-
- def get_pipes(self) -> List[PipeState]:
- """Obtiene el estado de todas las tuberías"""
- objects = self.get_simulation_objects()
- pipes = []
-
- for obj in objects:
- if obj.get("type") == "osHydPipe":
- props = obj.get("properties", {})
- pipe = PipeState(
- id=obj["id"],
- name=obj["name"],
- current_flow=props.get("CurrentFlow", 0.0),
- pressure_drop=props.get("PressureDrop", 0.0),
- fluid_type=props.get("FluidType", "Unknown"),
- )
- pipes.append(pipe)
-
- return pipes
-
- def start_simulation(self) -> bool:
- """Inicia la simulación"""
- response = self.send_mcp_request("start_simulation")
- return "result" in response and response["result"].get("success", False)
-
- def stop_simulation(self) -> bool:
- """Detiene la simulación"""
- response = self.send_mcp_request("stop_simulation")
- return "result" in response and response["result"].get("success", False)
-
- def reset_simulation_timing(self) -> bool:
- """Resetea los contadores de tiempo de simulación"""
- response = self.send_mcp_request("reset_simulation_timing")
- return "result" in response and response["result"].get("success", False)
-
- def wait_simulation_time(self, target_seconds: float):
- """Espera hasta que la simulación haya ejecutado un tiempo determinado"""
- print(f"⏰ Esperando {target_seconds} segundos reales de simulación...")
-
- target_ms = target_seconds * 1000
- start_time = time.time()
- max_wait_time = target_seconds * 3 # Timeout si no progresa
-
- while True:
- # Verificar tiempo real transcurrido para timeout
- if time.time() - start_time > max_wait_time:
- print(f"⚠️ Timeout después de {max_wait_time} segundos reales")
- break
-
- # Obtener estado actual de simulación
- response = self.send_mcp_request("get_simulation_status")
- if "result" in response:
- result = response["result"]
- simulation_ms = result.get("simulation_elapsed_ms", 0)
- is_running = result.get("is_running", False)
-
- if simulation_ms >= target_ms:
- print(
- f"✅ Simulación completó {simulation_ms}ms ({simulation_ms/1000:.3f}s)"
- )
- break
-
- if not is_running:
- print(f"⚠️ Simulación detenida en {simulation_ms}ms")
- break
-
- # Esperar un poco antes de verificar nuevamente
- time.sleep(0.1)
- else:
- print("⚠️ Error obteniendo estado de simulación")
- time.sleep(0.5)
-
- def calculate_mass_balance(
- self, initial_tanks: List[TankState], final_tanks: List[TankState]
- ) -> Dict:
- """Calcula el balance de masa entre estados inicial y final"""
- initial_mass = 0.0
- final_mass = 0.0
-
- for i, tank in enumerate(initial_tanks):
- # Masa inicial = volumen × densidad del fluido primario × porcentaje
- tank_mass = (
- (tank.volume_l / 1000.0)
- * tank.fluid_primary.density
- * (tank.primary_percentage / 100.0)
- )
- initial_mass += tank_mass
-
- for i, tank in enumerate(final_tanks):
- # Masa final = volumen × densidad del fluido primario × porcentaje
- tank_mass = (
- (tank.volume_l / 1000.0)
- * tank.fluid_primary.density
- * (tank.primary_percentage / 100.0)
- )
- final_mass += tank_mass
-
- return {
- "initial_mass_kg": initial_mass,
- "final_mass_kg": final_mass,
- "mass_difference_kg": final_mass - initial_mass,
- "conservation_percentage": (
- (final_mass / initial_mass * 100.0) if initial_mass > 0 else 0.0
- ),
- }
-
- def test_basic_flow_equilibrium(self) -> Dict:
- """
- Prueba básica de equilibrio de flujo entre dos tanques
- """
- print("\n🧪 EJECUTANDO: Prueba de Equilibrio de Flujo Básico")
-
- test_result = {
- "test_name": "basic_flow_equilibrium",
- "description": "Verificar equilibrio de flujo entre dos tanques con bomba",
- "success": False,
- "details": {},
- "measurements": {},
- }
-
- try:
- # Resetear timing para medición precisa
- self.reset_simulation_timing()
-
- # Estado inicial
- initial_tanks = self.get_tanks()
- initial_pumps = self.get_pumps()
- initial_pipes = self.get_pipes()
-
- if len(initial_tanks) < 2:
- test_result["details"][
- "error"
- ] = "Se requieren al menos 2 tanques para la prueba"
- return test_result
-
- print(f"📊 Estado inicial:")
- for tank in initial_tanks:
- print(f" - {tank.name}: {tank.level_m:.2f}m ({tank.volume_l:.1f}L)")
-
- # Iniciar simulación
- if not self.start_simulation():
- test_result["details"]["error"] = "No se pudo iniciar la simulación"
- return test_result
-
- # Esperar tiempo real de simulación
- target_simulation_time = 30.0 # 30 segundos reales de simulación
- self.wait_simulation_time(target_simulation_time)
-
- # Estado final
- final_tanks = self.get_tanks()
- final_pumps = self.get_pumps()
- final_pipes = self.get_pipes()
-
- print(f"📊 Estado final:")
- for tank in final_tanks:
- print(f" - {tank.name}: {tank.level_m:.2f}m ({tank.volume_l:.1f}L)")
-
- # Detener simulación
- self.stop_simulation()
-
- # Calcular balance de masa
- mass_balance = self.calculate_mass_balance(initial_tanks, final_tanks)
-
- # Obtener tiempo real de simulación
- status_response = self.send_mcp_request("get_simulation_status")
- actual_simulation_ms = 0
- if "result" in status_response:
- actual_simulation_ms = status_response["result"].get(
- "simulation_elapsed_ms", 0
- )
-
- # Almacenar mediciones
- test_result["measurements"] = {
- "target_simulation_time_s": target_simulation_time,
- "actual_simulation_time_ms": actual_simulation_ms,
- "actual_simulation_time_s": actual_simulation_ms / 1000.0,
- "initial_tanks": [asdict(tank) for tank in initial_tanks],
- "final_tanks": [asdict(tank) for tank in final_tanks],
- "pumps": [asdict(pump) for pump in final_pumps],
- "pipes": [asdict(pipe) for pipe in final_pipes],
- "mass_balance": mass_balance,
- }
-
- # Verificar conservación de masa (tolerancia 5%)
- conservation_ok = (
- abs(mass_balance["conservation_percentage"] - 100.0) <= 5.0
- )
-
- # Verificar que hubo transferencia de fluido
- volume_change = any(
- abs(final.volume_l - initial.volume_l) > 1.0
- for initial, final in zip(initial_tanks, final_tanks)
- )
-
- test_result["success"] = conservation_ok and volume_change
- test_result["details"] = {
- "conservation_percentage": mass_balance["conservation_percentage"],
- "volume_transfer_detected": volume_change,
- "mass_conserved": conservation_ok,
- }
-
- if test_result["success"]:
- print(
- "✅ Prueba EXITOSA: Equilibrio de flujo funcionando correctamente"
- )
- else:
- print("❌ Prueba FALLIDA: Problemas en equilibrio de flujo")
-
- except Exception as e:
- test_result["details"]["error"] = str(e)
- print(f"❌ Error en prueba: {e}")
-
- return test_result
-
- def test_fluid_properties_consistency(self) -> Dict:
- """
- Prueba de consistencia de propiedades de fluidos
- """
- print("\n🧪 EJECUTANDO: Prueba de Consistencia de Propiedades de Fluidos")
-
- test_result = {
- "test_name": "fluid_properties_consistency",
- "description": "Verificar que las propiedades de fluidos se mantienen consistentes",
- "success": False,
- "details": {},
- "measurements": {},
- }
-
- try:
- # Obtener estado actual
- tanks = self.get_tanks()
- pipes = self.get_pipes()
-
- if len(tanks) < 2:
- test_result["details"][
- "error"
- ] = "Se requieren al menos 2 tanques para la prueba"
- return test_result
-
- # Verificar propiedades de fluidos en tanques
- fluid_consistency = True
- density_checks = []
-
- for tank in tanks:
- # Verificar que las densidades sean razonables
- primary_density_ok = (
- 500.0 <= tank.fluid_primary.density <= 2000.0
- ) # kg/m³
- secondary_density_ok = (
- 0.5 <= tank.fluid_secondary.density <= 2000.0
- ) # kg/m³
-
- density_checks.append(
- {
- "tank_name": tank.name,
- "primary_density": tank.fluid_primary.density,
- "secondary_density": tank.fluid_secondary.density,
- "primary_ok": primary_density_ok,
- "secondary_ok": secondary_density_ok,
- }
- )
-
- if not (primary_density_ok and secondary_density_ok):
- fluid_consistency = False
-
- # Verificar que las tuberías muestren información de fluido
- pipe_fluid_info = []
- for pipe in pipes:
- has_fluid_info = pipe.fluid_type != "Unknown" and pipe.fluid_type != ""
- pipe_fluid_info.append(
- {
- "pipe_name": pipe.name,
- "fluid_type": pipe.fluid_type,
- "has_fluid_info": has_fluid_info,
- }
- )
-
- test_result["measurements"] = {
- "density_checks": density_checks,
- "pipe_fluid_info": pipe_fluid_info,
- }
-
- pipe_info_ok = (
- all(info["has_fluid_info"] for info in pipe_fluid_info)
- if pipe_fluid_info
- else True
- )
-
- test_result["success"] = fluid_consistency and pipe_info_ok
- test_result["details"] = {
- "fluid_densities_valid": fluid_consistency,
- "pipe_fluid_info_available": pipe_info_ok,
- "tanks_checked": len(tanks),
- "pipes_checked": len(pipes),
- }
-
- if test_result["success"]:
- print("✅ Prueba EXITOSA: Propiedades de fluidos consistentes")
- else:
- print("❌ Prueba FALLIDA: Inconsistencias en propiedades de fluidos")
-
- except Exception as e:
- test_result["details"]["error"] = str(e)
- print(f"❌ Error en prueba: {e}")
-
- return test_result
-
- def test_mixed_fluid_behavior(self) -> Dict:
- """
- Prueba de comportamiento con fluidos mezclados
- """
- print("\n🧪 EJECUTANDO: Prueba de Comportamiento de Fluidos Mezclados")
-
- test_result = {
- "test_name": "mixed_fluid_behavior",
- "description": "Verificar comportamiento con fluidos primarios y secundarios",
- "success": False,
- "details": {},
- "measurements": {},
- }
-
- try:
- # Obtener tanques
- tanks = self.get_tanks()
-
- if len(tanks) < 2:
- test_result["details"][
- "error"
- ] = "Se requieren al menos 2 tanques para la prueba"
- return test_result
-
- # Verificar que tenemos fluidos mezclados
- mixed_tanks = []
- for tank in tanks:
- has_mixed_fluid = (
- tank.primary_percentage > 0.0 and tank.primary_percentage < 100.0
- )
-
- mixed_tanks.append(
- {
- "tank_name": tank.name,
- "primary_percentage": tank.primary_percentage,
- "has_mixed_fluid": has_mixed_fluid,
- "primary_fluid": tank.fluid_primary.name,
- "secondary_fluid": tank.fluid_secondary.name,
- }
- )
-
- # Ejecutar simulación corta
- if self.start_simulation():
- self.wait_simulation_time(10.0)
-
- # Verificar que los porcentajes se mantienen en rangos válidos
- final_tanks = self.get_tanks()
- percentage_stability = []
-
- for initial, final in zip(tanks, final_tanks):
- percentage_change = abs(
- final.primary_percentage - initial.primary_percentage
- )
- stable = percentage_change <= 10.0 # Tolerancia 10%
-
- percentage_stability.append(
- {
- "tank_name": initial.name,
- "initial_percentage": initial.primary_percentage,
- "final_percentage": final.primary_percentage,
- "change": percentage_change,
- "stable": stable,
- }
- )
-
- self.stop_simulation()
-
- test_result["measurements"] = {
- "mixed_tanks": mixed_tanks,
- "percentage_stability": percentage_stability,
- }
-
- # Determinar éxito
- has_mixed_fluids = any(tank["has_mixed_fluid"] for tank in mixed_tanks)
- percentages_stable = all(
- check["stable"] for check in percentage_stability
- )
-
- test_result["success"] = has_mixed_fluids or percentages_stable
- test_result["details"] = {
- "mixed_fluids_detected": has_mixed_fluids,
- "percentages_stable": percentages_stable,
- "tanks_with_mixed_fluids": sum(
- 1 for tank in mixed_tanks if tank["has_mixed_fluid"]
- ),
- }
-
- if test_result["success"]:
- print(
- "✅ Prueba EXITOSA: Comportamiento de fluidos mezclados correcto"
- )
- else:
- print("❌ Prueba FALLIDA: Problemas con fluidos mezclados")
- else:
- test_result["details"]["error"] = "No se pudo iniciar la simulación"
-
- except Exception as e:
- test_result["details"]["error"] = str(e)
- print(f"❌ Error en prueba: {e}")
-
- return test_result
-
- def run_all_tests(self) -> Dict:
- """Ejecuta todas las pruebas del sistema hidráulico"""
- print("🚀 INICIANDO PRUEBAS DEL SISTEMA HIDRÁULICO")
- print("=" * 60)
-
- all_results = {
- "test_suite": "HydraulicSystemTests",
- "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
- "tests": [],
- "summary": {},
- }
-
- # Lista de pruebas a ejecutar
- tests = [
- self.test_basic_flow_equilibrium,
- self.test_fluid_properties_consistency,
- self.test_mixed_fluid_behavior,
- ]
-
- successful_tests = 0
-
- for test_func in tests:
- result = test_func()
- all_results["tests"].append(result)
-
- if result["success"]:
- successful_tests += 1
-
- # Resumen
- all_results["summary"] = {
- "total_tests": len(tests),
- "successful_tests": successful_tests,
- "failed_tests": len(tests) - successful_tests,
- "success_rate": (successful_tests / len(tests)) * 100.0,
- }
-
- print("\n" + "=" * 60)
- print("📋 RESUMEN DE PRUEBAS")
- print(f"Total de pruebas: {all_results['summary']['total_tests']}")
- print(f"Exitosas: {all_results['summary']['successful_tests']}")
- print(f"Fallidas: {all_results['summary']['failed_tests']}")
- print(f"Tasa de éxito: {all_results['summary']['success_rate']:.1f}%")
-
- return all_results
-
-
-def main():
- """Función principal"""
- print("🔧 Sistema de Pruebas Hidráulicas para CtrEditor")
- print("=" * 50)
-
- # Crear administrador de pruebas
- test_manager = HydraulicTestManager()
-
- # Ejecutar todas las pruebas
- results = test_manager.run_all_tests()
-
- # Guardar resultados
- results_file = f"hydraulic_test_results_{int(time.time())}.json"
- with open(results_file, "w", encoding="utf-8") as f:
- json.dump(results, f, indent=2, ensure_ascii=False)
-
- print(f"\n💾 Resultados guardados en: {results_file}")
-
- return results
-
-
-if __name__ == "__main__":
- main()
diff --git a/Scripts/HydraulicSystemTests_TimingV2.py b/Scripts/HydraulicSystemTests_TimingV2.py
deleted file mode 100644
index c4a64bc..0000000
--- a/Scripts/HydraulicSystemTests_TimingV2.py
+++ /dev/null
@@ -1,425 +0,0 @@
-#!/usr/bin/env python3
-"""
-Sistema de Pruebas Hidráulicas con Timing Preciso
-================================================
-
-Utiliza el tiempo real de simulación reportado por CtrEditor
-para mediciones precisas e independientes del sistema.
-
-Autor: Sistema de Automatización CtrEditor
-Fecha: Enero 2025
-"""
-
-import json
-import socket
-import time
-from dataclasses import dataclass, asdict
-from typing import List, Dict, Any, Optional
-
-
-@dataclass
-class TankInfo:
- name: str
- level_m: float
- volume_l: float
- primary_fluid: str
- secondary_fluid: str
- primary_volume: float
- secondary_volume: float
- mixing_volume: float
-
-
-@dataclass
-class PumpInfo:
- name: str
- is_running: bool
- current_flow: float
- max_flow: float
- pump_head: float
-
-
-@dataclass
-class PipeInfo:
- name: str
- current_flow: float
- pressure_drop: float
-
-
-class HydraulicTestSystemV2:
- def __init__(self, host="localhost", port=5005):
- self.host = host
- self.port = port
- self.test_results = []
-
- def send_mcp_request(self, method: str, params: Dict = None) -> Dict:
- """Envía una solicitud MCP al servidor proxy"""
- if params is None:
- params = {"random_string": "test"}
-
- request = {
- "jsonrpc": "2.0",
- "id": 1,
- "method": "tools/call",
- "params": {"name": method, "arguments": params},
- }
-
- try:
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
- sock.settimeout(10)
- sock.connect((self.host, self.port))
-
- request_str = json.dumps(request) + "\n"
- sock.sendall(request_str.encode())
-
- response = sock.recv(4096).decode()
- return json.loads(response)
-
- except Exception as e:
- print(f"❌ Error en comunicación MCP: {e}")
- return {"error": str(e)}
-
- def get_simulation_timing(self) -> Dict[str, Any]:
- """Obtiene información detallada del timing de simulación"""
- response = self.send_mcp_request("get_simulation_status")
- if "result" in response:
- result = response["result"]
- return {
- "is_running": result.get("is_running", False),
- "elapsed_ms": result.get("simulation_elapsed_ms", 0),
- "elapsed_seconds": result.get("simulation_elapsed_seconds", 0.0),
- }
- return {"is_running": False, "elapsed_ms": 0, "elapsed_seconds": 0.0}
-
- def wait_simulation_time(
- self, target_seconds: float, max_real_time: float = None
- ) -> Dict[str, Any]:
- """
- Espera hasta que la simulación haya ejecutado un tiempo determinado
-
- Args:
- target_seconds: Tiempo objetivo de simulación en segundos
- max_real_time: Tiempo máximo real a esperar (default: target_seconds * 5)
-
- Returns:
- Dict con información del timing final
- """
- if max_real_time is None:
- max_real_time = target_seconds * 5 # 5x timeout por defecto
-
- print(f"⏰ Esperando {target_seconds}s de simulación real...")
-
- target_ms = target_seconds * 1000
- start_real_time = time.time()
- last_progress_time = start_real_time
- last_simulation_ms = 0
-
- while True:
- current_real_time = time.time()
-
- # Timeout si excede tiempo máximo real
- if current_real_time - start_real_time > max_real_time:
- print(f"⚠️ Timeout después de {max_real_time:.1f}s reales")
- break
-
- # Obtener estado actual
- timing = self.get_simulation_timing()
- current_simulation_ms = timing["elapsed_ms"]
-
- # Verificar si la simulación sigue corriendo
- if not timing["is_running"]:
- print(f"⚠️ Simulación detenida en {current_simulation_ms}ms")
- break
-
- # Verificar progreso (evitar simulaciones colgadas)
- if current_simulation_ms > last_simulation_ms:
- last_progress_time = current_real_time
- last_simulation_ms = current_simulation_ms
- elif current_real_time - last_progress_time > 5.0: # 5s sin progreso
- print(f"⚠️ Sin progreso en simulación por 5 segundos")
- break
-
- # Verificar si alcanzamos el objetivo
- if current_simulation_ms >= target_ms:
- print(
- f"✅ Objetivo alcanzado: {current_simulation_ms}ms ({timing['elapsed_seconds']:.3f}s)"
- )
- break
-
- # Mostrar progreso cada segundo
- if (
- int(current_real_time) > int(start_real_time)
- and int(current_real_time) % 2 == 0
- ):
- progress = (current_simulation_ms / target_ms) * 100
- print(
- f"📊 Progreso: {progress:.1f}% ({current_simulation_ms}ms de {target_ms}ms)"
- )
-
- time.sleep(0.1) # Check cada 100ms
-
- return timing
-
- def get_tanks(self) -> List[TankInfo]:
- """Obtiene información de todos los tanques"""
- response = self.send_mcp_request("list_objects")
- tanks = []
-
- if "result" in response and "objects" in response["result"]:
- for obj in response["result"]["objects"]:
- if obj["type"] == "osHydTank":
- props = obj["properties"]
- tank = TankInfo(
- name=props.get("name", "Unknown"),
- level_m=props.get("CurrentLevelM", 0.0),
- volume_l=props.get("TotalVolumeL", 0.0),
- primary_fluid=props.get("PrimaryFluidType", "Unknown"),
- secondary_fluid=props.get("SecondaryFluidType", "Unknown"),
- primary_volume=props.get("PrimaryVolumeL", 0.0),
- secondary_volume=props.get("SecondaryVolumeL", 0.0),
- mixing_volume=props.get("MixingVolumeL", 0.0),
- )
- tanks.append(tank)
-
- return tanks
-
- def get_pumps(self) -> List[PumpInfo]:
- """Obtiene información de todas las bombas"""
- response = self.send_mcp_request("list_objects")
- pumps = []
-
- if "result" in response and "objects" in response["result"]:
- for obj in response["result"]["objects"]:
- if obj["type"] == "osHydPump":
- props = obj["properties"]
- pump = PumpInfo(
- name=props.get("name", "Unknown"),
- is_running=props.get("IsRunning", False),
- current_flow=props.get("CurrentFlow", 0.0),
- max_flow=props.get("MaxFlow", 0.0),
- pump_head=props.get("PumpHead", 0.0),
- )
- pumps.append(pump)
-
- return pumps
-
- def get_pipes(self) -> List[PipeInfo]:
- """Obtiene información de todas las tuberías"""
- response = self.send_mcp_request("list_objects")
- pipes = []
-
- if "result" in response and "objects" in response["result"]:
- for obj in response["result"]["objects"]:
- if obj["type"] == "osHydPipe":
- props = obj["properties"]
- pipe = PipeInfo(
- name=props.get("name", "Unknown"),
- current_flow=props.get("CurrentFlow", 0.0),
- pressure_drop=props.get("PressureDrop", 0.0),
- )
- pipes.append(pipe)
-
- return pipes
-
- def start_simulation(self) -> bool:
- """Inicia la simulación"""
- response = self.send_mcp_request("start_simulation")
- return "result" in response and response["result"].get("success", False)
-
- def stop_simulation(self) -> Dict[str, Any]:
- """Detiene la simulación y retorna timing final"""
- response = self.send_mcp_request("stop_simulation")
- if "result" in response and response["result"].get("success", False):
- return {
- "success": True,
- "elapsed_ms": response["result"].get("simulation_elapsed_ms", 0),
- "elapsed_seconds": response["result"].get(
- "simulation_elapsed_seconds", 0.0
- ),
- }
- return {"success": False, "elapsed_ms": 0, "elapsed_seconds": 0.0}
-
- def test_precise_flow_equilibrium(
- self, target_simulation_seconds: float = 30.0
- ) -> Dict:
- """
- Prueba de equilibrio de flujo con timing preciso
-
- Args:
- target_simulation_seconds: Tiempo objetivo de simulación en segundos
-
- Returns:
- Dict con resultados detallados de la prueba
- """
- print(f"\n🧪 === PRUEBA DE EQUILIBRIO DE FLUJO (TIMING PRECISO) ===")
- print(f"⏱️ Tiempo objetivo: {target_simulation_seconds} segundos de simulación")
-
- test_result = {
- "test_name": "Precise Flow Equilibrium",
- "success": False,
- "target_simulation_seconds": target_simulation_seconds,
- "actual_simulation_data": {},
- "measurements": {},
- "details": {},
- }
-
- try:
- # Estado inicial
- initial_timing = self.get_simulation_timing()
- initial_tanks = self.get_tanks()
- initial_pumps = self.get_pumps()
- initial_pipes = self.get_pipes()
-
- if len(initial_tanks) < 2:
- test_result["details"][
- "error"
- ] = "Se requieren al menos 2 tanques para la prueba"
- return test_result
-
- print(f"📊 Estado inicial:")
- print(f"⏱️ Tiempo simulación: {initial_timing['elapsed_seconds']:.3f}s")
- for tank in initial_tanks:
- print(f" - {tank.name}: {tank.level_m:.2f}m ({tank.volume_l:.1f}L)")
-
- # Iniciar simulación
- if not self.start_simulation():
- test_result["details"]["error"] = "No se pudo iniciar la simulación"
- return test_result
-
- print("🚀 Simulación iniciada...")
-
- # Esperar tiempo de simulación preciso
- final_timing = self.wait_simulation_time(target_simulation_seconds)
-
- # Detener simulación y obtener timing final
- stop_result = self.stop_simulation()
-
- # Estado final
- final_tanks = self.get_tanks()
- final_pumps = self.get_pumps()
- final_pipes = self.get_pipes()
-
- print(f"📊 Estado final:")
- print(f"⏱️ Tiempo total simulación: {stop_result['elapsed_seconds']:.3f}s")
- for tank in final_tanks:
- print(f" - {tank.name}: {tank.level_m:.2f}m ({tank.volume_l:.1f}L)")
-
- # Calcular balance de masa
- initial_total_volume = sum(tank.volume_l for tank in initial_tanks)
- final_total_volume = sum(tank.volume_l for tank in final_tanks)
- mass_balance = {
- "initial_volume_l": initial_total_volume,
- "final_volume_l": final_total_volume,
- "difference_l": final_total_volume - initial_total_volume,
- "conservation_percentage": (
- (final_total_volume / initial_total_volume * 100)
- if initial_total_volume > 0
- else 0
- ),
- }
-
- print(f"⚖️ Balance de masa:")
- print(f" - Volumen inicial: {mass_balance['initial_volume_l']:.2f}L")
- print(f" - Volumen final: {mass_balance['final_volume_l']:.2f}L")
- print(f" - Diferencia: {mass_balance['difference_l']:.3f}L")
- print(f" - Conservación: {mass_balance['conservation_percentage']:.2f}%")
-
- # Almacenar mediciones con timing preciso
- test_result["actual_simulation_data"] = {
- "initial_timing": initial_timing,
- "final_timing": final_timing,
- "stop_timing": stop_result,
- "actual_simulation_seconds": stop_result["elapsed_seconds"],
- "timing_accuracy": abs(
- target_simulation_seconds - stop_result["elapsed_seconds"]
- ),
- }
-
- test_result["measurements"] = {
- "target_simulation_seconds": target_simulation_seconds,
- "actual_simulation_seconds": stop_result["elapsed_seconds"],
- "initial_tanks": [asdict(tank) for tank in initial_tanks],
- "final_tanks": [asdict(tank) for tank in final_tanks],
- "pumps": [asdict(pump) for pump in final_pumps],
- "pipes": [asdict(pipe) for pipe in final_pipes],
- "mass_balance": mass_balance,
- }
-
- # Verificar éxito (conservación de masa dentro del 1%)
- conservation_ok = (
- abs(mass_balance["difference_l"])
- < 0.01 * mass_balance["initial_volume_l"]
- )
- timing_ok = (
- abs(target_simulation_seconds - stop_result["elapsed_seconds"]) < 1.0
- ) # Tolerancia de 1s
-
- test_result["success"] = conservation_ok and timing_ok
-
- if conservation_ok:
- print("✅ Conservación de masa: EXITOSA")
- else:
- print("❌ Conservación de masa: FALLÓ")
-
- if timing_ok:
- print("✅ Precisión de timing: EXITOSA")
- else:
- print("❌ Precisión de timing: FALLÓ")
-
- except Exception as e:
- test_result["details"]["error"] = str(e)
- print(f"❌ Error durante la prueba: {e}")
-
- return test_result
-
- def run_all_tests(self) -> None:
- """Ejecuta todas las pruebas disponibles"""
- print("🚀 === INICIO DE PRUEBAS HIDRÁULICAS CON TIMING PRECISO ===")
-
- # Prueba 1: Equilibrio de flujo preciso
- test1 = self.test_precise_flow_equilibrium(
- 15.0
- ) # 15 segundos para prueba rápida
- self.test_results.append(test1)
-
- # Guardar resultados
- timestamp = time.strftime("%Y%m%d_%H%M%S")
- filename = f"Scripts/HydraulicTestResults_TimingV2_{timestamp}.json"
-
- results_data = {
- "timestamp": timestamp,
- "test_summary": {
- "total_tests": len(self.test_results),
- "successful_tests": sum(
- 1 for test in self.test_results if test["success"]
- ),
- "failed_tests": sum(
- 1 for test in self.test_results if not test["success"]
- ),
- },
- "tests": self.test_results,
- }
-
- with open(filename, "w", encoding="utf-8") as f:
- json.dump(results_data, f, indent=2, ensure_ascii=False)
-
- print(f"\n📄 Resultados guardados en: {filename}")
-
- # Resumen final
- successful = results_data["test_summary"]["successful_tests"]
- total = results_data["test_summary"]["total_tests"]
- print(f"\n📊 === RESUMEN FINAL ===")
- print(f"✅ Pruebas exitosas: {successful}/{total}")
-
- if successful == total:
- print(
- "🎉 ¡TODAS LAS PRUEBAS PASARON! Sistema hidráulico validado con timing preciso."
- )
- else:
- print("⚠️ Algunas pruebas fallaron. Revisar resultados detallados.")
-
-
-if __name__ == "__main__":
- # Ejecutar las pruebas
- test_system = HydraulicTestSystemV2()
- test_system.run_all_tests()
-
diff --git a/Scripts/HydraulicTestResults_20250106.json b/Scripts/HydraulicTestResults_20250106.json
deleted file mode 100644
index a43ffd3..0000000
--- a/Scripts/HydraulicTestResults_20250106.json
+++ /dev/null
@@ -1,186 +0,0 @@
-{
- "test_suite": "HydraulicSystemTests_CtrEditor",
- "timestamp": "2025-01-06 20:24:54",
- "test_duration_total": "60 seconds",
- "summary": {
- "total_tests": 3,
- "successful_tests": 3,
- "failed_tests": 0,
- "success_rate": 100.0,
- "overall_status": "EXITOSO"
- },
- "initial_configuration": {
- "objects": [
- {
- "name": "Tanque Sirope Test",
- "type": "osHydTank",
- "initial_volume_l": 1147.8,
- "initial_level_m": 1.2478,
- "fluid_primary": "Sirope 80°C, 65° Brix",
- "fluid_secondary": "Sirope 20°C, 65° Brix"
- },
- {
- "name": "Tanque Test Destino",
- "type": "osHydTank",
- "initial_volume_l": 352.2,
- "initial_level_m": 0.4522,
- "fluid_primary": "Agua 20°C",
- "fluid_secondary": "Sirope 20°C, 65° Brix"
- },
- {
- "name": "Bomba Hidráulica",
- "type": "osHydPump",
- "pump_head": 75.0,
- "max_flow": 0.02,
- "is_running": true
- }
- ]
- },
- "test_1_basic_equilibrium": {
- "test_name": "basic_flow_equilibrium",
- "description": "Equilibrio básico entre dos tanques con fluidos simples",
- "duration": "30 seconds",
- "success": true,
- "initial_state": {
- "tanque_sirope": {
- "volume_l": 1147.8,
- "level_m": 1.2478
- },
- "tanque_destino": {
- "volume_l": 352.2,
- "level_m": 0.4522
- }
- },
- "final_state": {
- "tanque_sirope": {
- "volume_l": 1129.4,
- "level_m": 1.2294
- },
- "tanque_destino": {
- "volume_l": 370.6,
- "level_m": 0.4706
- }
- },
- "measurements": {
- "volume_transferred_l": 18.4,
- "mass_conservation_percentage": 100.0,
- "transfer_rate_l_per_min": 0.613,
- "pressure_head_difference": "Calculado por simulación",
- "flow_direction": "Sirope → Destino"
- },
- "results": {
- "mass_conservation": "PERFECTO",
- "volume_balance": "CORRECTO",
- "flow_direction": "CORRECTO",
- "hydraulic_simulation": "FUNCIONANDO"
- }
- },
- "test_2_mixed_fluids": {
- "test_name": "mixed_fluid_behavior",
- "description": "Comportamiento con fluidos mezclados y diferentes densidades",
- "duration": "30 seconds",
- "success": true,
- "modified_configuration": {
- "tanque_destino": {
- "primary_fluid": "Sirope 40°C, 30° Brix",
- "primary_volume_l": 200.0,
- "secondary_fluid": "Agua 20°C",
- "secondary_volume_l": 170.0,
- "total_volume_l": 370.0
- }
- },
- "initial_state": {
- "tanque_sirope": {
- "volume_l": 1129.4,
- "level_m": 1.2294
- },
- "tanque_destino_mixed": {
- "volume_total_l": 370.6,
- "level_m": 0.4706,
- "sirope_30brix_l": 200.0,
- "water_l": 170.0
- }
- },
- "final_state": {
- "tanque_sirope": {
- "volume_l": 1103.4,
- "level_m": 1.2034
- },
- "tanque_destino_mixed": {
- "volume_total_l": 377.9,
- "level_m": 0.4779,
- "sirope_30brix_l": 205.0,
- "water_l": 170.0
- }
- },
- "measurements": {
- "sirope_transferred_l": 26.0,
- "sirope_received_in_mix_l": 5.0,
- "water_unchanged_l": 170.0,
- "density_considerations": "Sistema considera diferencias de densidad",
- "mixing_behavior": "Sirope denso se mezcla con sirope ligero, agua permanece separada"
- },
- "results": {
- "selective_mixing": "CORRECTO",
- "density_calculations": "FUNCIONANDO",
- "fluid_separation": "CORRECTO",
- "mass_conservation_complex": "CORRECTO"
- }
- },
- "identified_issues": {
- "critical": [],
- "major": [
- {
- "component": "osHydPipe",
- "issue": "CurrentFlow siempre muestra 0.0",
- "impact": "No hay visualización de flujo en tuberías",
- "status": "IDENTIFICADO"
- },
- {
- "component": "osHydPump",
- "issue": "No muestra flujo actual en propiedades",
- "impact": "Falta información visual del rendimiento de bomba",
- "status": "IDENTIFICADO"
- }
- ],
- "minor": [
- {
- "component": "osHydPipe",
- "issue": "No muestra tipo de fluido que pasa",
- "impact": "Información incompleta para el usuario",
- "status": "MEJORABLE"
- }
- ]
- },
- "system_capabilities_verified": {
- "mass_conservation": "✅ PERFECTO - Balance 100%",
- "volume_transfer": "✅ CORRECTO - Transferencia detectada",
- "hydraulic_simulation": "✅ FUNCIONANDO - Backend operativo",
- "mixed_fluids": "✅ AVANZADO - Manejo inteligente de mezclas",
- "density_calculations": "✅ CORRECTO - Considera propiedades físicas",
- "fluid_separation": "✅ INTELIGENTE - Separa fluidos incompatibles",
- "temperature_effects": "✅ FUNCIONANDO - Considera temperatura en cálculos",
- "concentration_effects": "✅ FUNCIONANDO - Maneja Brix correctamente"
- },
- "recommendations": {
- "immediate": [
- "Corregir visualización de CurrentFlow en osHydPipe",
- "Agregar display de flujo actual en osHydPump",
- "Implementar indicador de tipo de fluido en tuberías"
- ],
- "future_enhancements": [
- "Selector visual de tipo de flujo (primario/secundario/mix)",
- "Gráficos en tiempo real de transferencia de fluidos",
- "Alertas de cavitación en bombas",
- "Visualización de gradientes de densidad en tanques"
- ]
- },
- "test_environment": {
- "ctrEditor_version": "Development Build",
- "hydraulic_simulator": "CtrEditor HydraulicSimulator",
- "physics_engine": "BepuPhysics2",
- "test_platform": "Windows 10 x64",
- "mcp_server": "Funcionando correctamente"
- }
-}
-
diff --git a/Scripts/HydraulicTestResults_TimingV2_20250906_203611.json b/Scripts/HydraulicTestResults_TimingV2_20250906_203611.json
deleted file mode 100644
index 90775b9..0000000
--- a/Scripts/HydraulicTestResults_TimingV2_20250906_203611.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "timestamp": "20250906_203611",
- "test_summary": {
- "total_tests": 1,
- "successful_tests": 0,
- "failed_tests": 1
- },
- "tests": [
- {
- "test_name": "Precise Flow Equilibrium",
- "success": false,
- "target_simulation_seconds": 15.0,
- "actual_simulation_data": {},
- "measurements": {},
- "details": {
- "error": "Se requieren al menos 2 tanques para la prueba"
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/Scripts/ResumenPruebasHidraulicas.md b/Scripts/ResumenPruebasHidraulicas.md
deleted file mode 100644
index 949ee0e..0000000
--- a/Scripts/ResumenPruebasHidraulicas.md
+++ /dev/null
@@ -1,112 +0,0 @@
-# 🚰 Resumen Completo de Pruebas del Sistema Hidráulico
-
-## 🎯 **Objetivo Cumplido**
-
-Se implementó y validó completamente el sistema de pruebas hidráulicas basado en FluidManagementSystem.md y MCP_LLM_Guide.md, probando desde sistemas simples hasta complejos con fluidos mezclados.
-
-## ✅ **Resultados de las Pruebas**
-
-### 🧮 **Prueba 1: Equilibrio Básico de Flujo (30s)**
-- **Conservación de masa**: 100% perfecta
-- **Transferencia detectada**: 18.4L (Sirope → Destino)
-- **Dirección de flujo**: Correcta
-- **Balance de volumen**: Exacto (-18.4L origen, +18.4L destino)
-
-### 🌊 **Prueba 2: Fluidos Mezclados Complejos (30s adicionales)**
-- **Sirope denso** (65° Brix, 80°C) → **Sirope ligero** (30° Brix, 40°C): ✅ Mezcla selectiva
-- **Agua** (20°C): ✅ Permanece separada
-- **Transferencia inteligente**: 26.0L del origen, 5.0L añadido a mezcla de sirope
-- **Conservación de masa**: Considerando densidades diferentes
-
-### 🔬 **Cálculos de Densidad Verificados**
-- Sirope 80°C, 65° Brix: ~1400 kg/m³
-- Sirope 40°C, 30° Brix: ~1150 kg/m³
-- Agua 20°C: ~1000 kg/m³
-- Sistema maneja diferencias correctamente
-
-## 🎯 **Capacidades del Sistema Validadas**
-
-### ✅ **Motor Hidráulico (Backend)**
-- **Simulación hidráulica**: 100% funcional
-- **Conservación de masa**: Perfecta
-- **Cálculos de presión**: Correctos
-- **Transferencia de fluidos**: Operativa
-- **Manejo de mezclas**: Inteligente
-- **Densidades variables**: Soportado
-- **Temperatura/concentración**: Considerado
-
-### ❌ **Visualización (Frontend)**
-- **osHydPipe.CurrentFlow**: Siempre 0.0 (no actualiza)
-- **osHydPump.CurrentFlow**: No muestra flujo real
-- **Información de fluido**: No se visualiza en tuberías
-- **Propiedades de fluido**: Definidas pero no conectadas
-
-## 🔧 **Mejoras Implementadas**
-
-### 📊 **Propiedades de Fluido Agregadas**
-```csharp
-// osHydPipe
-public FluidProperties CurrentFluid { get; set; }
-public string FluidType { get; set; }
-public double FluidDensity { get; set; }
-
-// osHydPump
-public FluidProperties PumpFluid { get; set; }
-public string CurrentFluidType { get; set; }
-```
-
-### 🧪 **Sistema de Pruebas Automatizadas**
-- **HydraulicSystemTests.py**: Framework completo de pruebas
-- **Pruebas via MCP**: Integración directa con CtrEditor
-- **Análisis de equilibrio**: Balance de masa automático
-- **Informes JSON**: Resultados detallados exportados
-
-### 📝 **Documentación Actualizada**
-- **MemoriadeEvolucion.md**: Hallazgos importantes agregados
-- **HydraulicTestResults_20250106.json**: Resultados completos
-- **Screenshots**: Estados inicial y final capturados
-
-## 🎯 **Arquitectura Validada**
-
-### ✅ **Sistema Dual Funcional**
-- **BepuPhysics**: Simulación física de objetos sólidos
-- **HydraulicSimulator**: Simulación de fluidos y presiones
-- **Integración**: Ambos sistemas operan en paralelo sin interferencias
-
-### ✅ **Patrón de Conexión Establecido**
-- **Tanques**: Terminales del sistema (origen/destino)
-- **Bombas**: Generadores de presión y flujo
-- **Tuberías**: Conectores con pérdidas por fricción
-- **Válvulas**: Controladores de flujo (preparado)
-
-## 🚀 **Recomendaciones Inmediatas**
-
-### 🔴 **Críticas (Visualización)**
-1. **Corregir `UpdateControl()` en osHydPipe**: Conectar `CurrentFlow` con simulación
-2. **Implementar `UpdateFluidFromSource()` en osHydPipe**: Actualizar propiedades de fluido
-3. **Agregar display de flujo en osHydPump**: Mostrar caudal actual en propiedades
-
-### 🟡 **Mejoras (UX)**
-1. **Selector de tipo de flujo**: Primario/Secundario/Mix en tanques
-2. **Indicadores visuales**: Color de tuberías según tipo de fluido
-3. **Gráficos tiempo real**: Transferencia de volumen entre tanques
-
-## 🏆 **Conclusión**
-
-El sistema hidráulico de CtrEditor es **arquitectónicamente sólido y funcionalmente correcto**. Las pruebas confirman:
-
-- ✅ **Física hidráulica**: Perfecta
-- ✅ **Conservación de masa**: 100%
-- ✅ **Fluidos complejos**: Manejo inteligente
-- ✅ **Integración**: BepuPhysics + Hidráulica sin conflictos
-- ❌ **Visualización**: Requiere conexión UI ↔ Backend
-
-**El motor funciona correctamente, solo necesita conectar la visualización.**
-
----
-
-*Pruebas ejecutadas: 6 enero 2025*
-*Duración total: 60 segundos de simulación continua*
-*Objetos probados: 2 tanques + 1 bomba + 2 tuberías*
-*Resultado: EXITOSO con identificación de mejoras*
-
diff --git a/start_mcp_proxy.bat b/start_mcp_proxy.bat
deleted file mode 100644
index 6d01f3e..0000000
--- a/start_mcp_proxy.bat
+++ /dev/null
@@ -1,34 +0,0 @@
-@echo off
-REM Script para iniciar el proxy MCP para CtrEditor
-
-echo ================================================
-echo Proxy MCP para CtrEditor
-echo ================================================
-echo.
-echo Este script inicia el proxy que conecta
-echo GitHub Copilot con el servidor MCP de CtrEditor
-echo.
-echo Asegurate de que:
-echo 1. CtrEditor este ejecutandose
-echo 2. El servidor MCP este iniciado (puerto 5006)
-echo 3. Python este instalado
-echo.
-echo Presiona Ctrl+C para detener el proxy
-echo ================================================
-echo.
-
-REM Verificar si Python está disponible
-python --version >nul 2>&1
-if errorlevel 1 (
- echo ERROR: Python no esta instalado o no esta en el PATH
- pause
- exit /b 1
-)
-
-REM Ejecutar el proxy
-echo Iniciando proxy MCP...
-python "%~dp0start_mcp_proxy.py" --verbose
-
-echo.
-echo Proxy detenido.
-pause
diff --git a/start_mcp_proxy.py b/start_mcp_proxy.py
deleted file mode 100644
index 1000ac4..0000000
--- a/start_mcp_proxy.py
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/usr/bin/env python3
-"""
-Proxy MCP específico para el proyecto CtrEditor
-Este script inicia un proxy que conecta GitHub Copilot con el servidor MCP de CtrEditor
-"""
-
-import sys
-import os
-import subprocess
-import argparse
-import logging
-from pathlib import Path
-
-# Configuración de logging
-logging.basicConfig(
- level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
-)
-logger = logging.getLogger(__name__)
-
-
-def main():
- parser = argparse.ArgumentParser(description="Proxy MCP para CtrEditor")
- parser.add_argument(
- "--ctreditor-host",
- default="localhost",
- help="Host del servidor MCP de CtrEditor",
- )
- parser.add_argument(
- "--ctreditor-port", default="5006", help="Puerto del servidor MCP de CtrEditor"
- )
- parser.add_argument(
- "--proxy-port", default="8080", help="Puerto del proxy para Copilot"
- )
- parser.add_argument("--verbose", "-v", action="store_true", help="Modo verbose")
-
- args = parser.parse_args()
-
- if args.verbose:
- logging.getLogger().setLevel(logging.DEBUG)
-
- # Ruta al proxy principal
- script_dir = Path(__file__).parent
- main_proxy_path = script_dir.parent / "Scripts" / "MCP_Proxy" / "mcp_proxy.py"
-
- if not main_proxy_path.exists():
- logger.error(f"No se encontró el proxy principal en: {main_proxy_path}")
- sys.exit(1)
-
- # Comando para ejecutar el proxy
- cmd = [
- sys.executable,
- str(main_proxy_path),
- "--host",
- args.ctreditor_host,
- "--port",
- args.ctreditor_port,
- "--proxy-port",
- args.proxy_port,
- ]
-
- if args.verbose:
- cmd.append("--verbose")
-
- logger.info(f"Iniciando proxy MCP para CtrEditor...")
- logger.info(f"Servidor CtrEditor: {args.ctreditor_host}:{args.ctreditor_port}")
- logger.info(f"Proxy para Copilot: localhost:{args.proxy_port}")
- logger.info(f"Comando: {' '.join(cmd)}")
-
- try:
- # Ejecutar el proxy
- subprocess.run(cmd, check=True)
- except subprocess.CalledProcessError as e:
- logger.error(f"Error al ejecutar el proxy: {e}")
- sys.exit(1)
- except KeyboardInterrupt:
- logger.info("Proxy detenido por el usuario")
- sys.exit(0)
-
-
-if __name__ == "__main__":
- main()
diff --git a/update_single_file.ps1 b/update_single_file.ps1
deleted file mode 100644
index b12a8a2..0000000
--- a/update_single_file.ps1
+++ /dev/null
@@ -1,39 +0,0 @@
-# Script para agregar automáticamente el método NombreCategoria
-param(
- [string]$FilePath,
- [string]$Category
-)
-
-if (-not (Test-Path $FilePath)) {
- Write-Error "File not found: $FilePath"
- return
-}
-
-$content = Get-Content $FilePath -Raw
-
-# Buscar el patrón del método NombreClase
-$pattern = '(\s+public static string NombreClase\(\)\s+\{\s+return ".*?";\s+\})'
-
-if ($content -match $pattern) {
- $nombreclaseMethod = $matches[1]
- $newMethod = @"
-$nombreclaseMethod
-
- public static string NombreCategoria()
- {
- return "$Category";
- }
-"@
-
- $newContent = $content -replace [regex]::Escape($nombreclaseMethod), $newMethod
-
- # Verificar que no ya existe el método NombreCategoria
- if ($content -notmatch 'public static string NombreCategoria\(\)') {
- Set-Content $FilePath -Value $newContent -NoNewline
- Write-Host "✓ Updated: $FilePath"
- } else {
- Write-Host "- Skipped (already has NombreCategoria): $FilePath"
- }
-} else {
- Write-Warning "Could not find NombreClase method in: $FilePath"
-}