diff --git a/App.xaml b/App.xaml
index bd9f193..4b8a7f4 100644
--- a/App.xaml
+++ b/App.xaml
@@ -27,6 +27,7 @@
+
+ /// Converter para calcular el margen de las secciones del tanque
+ /// basado en los porcentajes de las secciones previas
+ ///
+ public class TankSectionMarginConverter : IMultiValueConverter
+ {
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (values == null || values.Length < 2)
+ return new Thickness(4, 4, 4, 4);
+
+ // Convertir valores a double
+ if (!TryConvertToDouble(values[values.Length - 1], out double tankSize))
+ return new Thickness(4, 4, 4, 4);
+
+ // Calcular la altura total acumulada de las secciones previas
+ double accumulatedPercentage = 0.0;
+ for (int i = 0; i < values.Length - 1; i++) // -1 para excluir tankSize
+ {
+ if (TryConvertToDouble(values[i], out double percentage))
+ {
+ accumulatedPercentage += percentage;
+ }
+ }
+
+ try
+ {
+ // Convertir tankSize a píxeles (100 píxeles por metro aproximadamente)
+ var tankHeightPixels = tankSize * 100.0;
+
+ // Calcular altura acumulada en píxeles
+ var accumulatedHeightPixels = (accumulatedPercentage / 100.0) * tankHeightPixels;
+
+ // Calcular margen bottom basado en la altura acumulada
+ var marginBottom = 4.0 + accumulatedHeightPixels;
+
+ return new Thickness(4, 4, 4, marginBottom);
+ }
+ catch (Exception)
+ {
+ return new Thickness(4, 4, 4, 4);
+ }
+ }
+
+ private bool TryConvertToDouble(object value, out double result)
+ {
+ result = 0.0;
+
+ if (value == null)
+ return false;
+
+ // Intentar conversión directa si ya es double
+ if (value is double d)
+ {
+ result = d;
+ return true;
+ }
+
+ // Intentar conversión desde float
+ if (value is float f)
+ {
+ result = f;
+ return true;
+ }
+
+ // Intentar conversión desde int
+ if (value is int i)
+ {
+ result = i;
+ return true;
+ }
+
+ // Intentar parsing desde string
+ if (value is string s)
+ {
+ if (double.TryParse(s.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out result))
+ return true;
+ }
+
+ return false;
+ }
+
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException("ConvertBack no está implementado para TankSectionMarginConverter");
+ }
+ }
+}
diff --git a/MainViewModel.cs b/MainViewModel.cs
index ab70b6f..42f60f7 100644
--- a/MainViewModel.cs
+++ b/MainViewModel.cs
@@ -62,7 +62,7 @@ namespace CtrEditor
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 MIN_SIM_INTERVAL = 10; // 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
@@ -76,6 +76,7 @@ namespace CtrEditor
private readonly System.Timers.Timer _timerPLCUpdate; // Cambiado a System.Timers.Timer para mejor precisión
private readonly DispatcherTimer _timerDisplayUpdate;
private readonly DispatcherTimer _timer3DUpdate; // Nuevo timer para actualización 3D cuando simulación está detenida
+ private readonly System.Timers.Timer _timerDebugFlush; // Timer para flush automático del buffer de debug
public Canvas MainCanvas;
@@ -438,6 +439,13 @@ namespace CtrEditor
_timer3DUpdate.Interval = TimeSpan.FromMilliseconds(20);
_timer3DUpdate.Tick += OnTick3DUpdate;
_timer3DUpdate.Start(); // Iniciar porque la simulación empieza detenida
+
+ // Timer para flush automático del buffer de debug (cada 5 minutos)
+ _timerDebugFlush = new System.Timers.Timer();
+ _timerDebugFlush.Interval = 300000; // 5 minutos = 300,000 ms
+ _timerDebugFlush.Elapsed += OnDebugFlushTimer;
+ _timerDebugFlush.AutoReset = true;
+ _timerDebugFlush.Start();
StartSimulationCommand = new RelayCommand(StartSimulation);
StopSimulationCommand = new RelayCommand(StopSimulation);
@@ -1270,6 +1278,8 @@ namespace CtrEditor
_timerSimulacion?.Stop();
_timerPLCUpdate?.Stop();
_timer3DUpdate?.Stop();
+ _timerDisplayUpdate?.Stop();
+ _timerDebugFlush?.Stop();
// Desconectar PLC si está conectado
if (PLCViewModel?.IsConnected == true)
@@ -1333,6 +1343,72 @@ namespace CtrEditor
});
}
+ ///
+ /// Evento del timer que hace flush automático del buffer de debug para evitar acumulación excesiva
+ ///
+ private void OnDebugFlushTimer(object sender, System.Timers.ElapsedEventArgs e)
+ {
+ try
+ {
+ if (_debugConsoleServer != null)
+ {
+ var stats = _debugConsoleServer.GetBufferStats();
+
+ // Solo hacer flush si el buffer tiene un número significativo de mensajes
+ if (stats.messageCount > 500)
+ {
+ _debugConsoleServer.FlushMessageBuffer();
+ Debug.WriteLine($"[Debug Flush Timer] Buffer automático limpiado. Mensajes procesados: {stats.messageCount}");
+ }
+ else
+ {
+ Debug.WriteLine($"[Debug Flush Timer] Buffer saludable. Mensajes actuales: {stats.messageCount}");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"[Debug Flush Timer] Error durante flush automático: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Evento del timer para actualización de display (UI)
+ ///
+ private void OnDisplayUpdate(object sender, EventArgs e)
+ {
+ try
+ {
+ // Actualizar elementos de UI que no requieren alta frecuencia
+ // Este timer corre a 250ms por lo que es adecuado para actualizaciones de UI menos críticas
+
+ // Se puede agregar lógica de actualización de UI aquí si es necesario
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"[Display Update Timer] Error durante actualización de display: {ex.Message}");
+ }
+ }
+
+ ///
+ /// Evento del timer para actualización 3D cuando la simulación está detenida
+ ///
+ private void OnTick3DUpdate(object sender, EventArgs e)
+ {
+ try
+ {
+ // Mantener la visualización 3D actualizada cuando la simulación no está corriendo
+ if (!IsSimulationRunning && Visualization3DManager != null)
+ {
+ Visualization3DManager.SynchronizeWorld();
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"[3D Update Timer] Error durante actualización 3D: {ex.Message}");
+ }
+ }
+
private void AdaptPlcTiming(double executionTime)
{
lastPlcExecutionTime = executionTime;
@@ -1350,13 +1426,13 @@ namespace CtrEditor
{
// Incrementar intervalo para dar más tiempo
newInterval = Math.Min(maxPlcExecutionTime + SIM_PRIORITY_TIME + 5, MAX_PLC_INTERVAL);
- Debug.WriteLine($"PLC Timer: Aumentando intervalo a {newInterval:F1}ms (ejecución máxima: {maxPlcExecutionTime:F1}ms)");
+ // Debug.WriteLine($"PLC Timer: Aumentando intervalo a {newInterval:F1}ms (ejecución máxima: {maxPlcExecutionTime:F1}ms)");
}
// Si hemos estado por debajo del umbral, podemos reducir el intervalo
else if (maxPlcExecutionTime + SIM_PRIORITY_TIME < currentInterval * 0.7)
{
newInterval = Math.Max(maxPlcExecutionTime + SIM_PRIORITY_TIME + 2, MIN_PLC_INTERVAL);
- Debug.WriteLine($"PLC Timer: Reduciendo intervalo a {newInterval:F1}ms (ejecución máxima: {maxPlcExecutionTime:F1}ms)");
+ // Debug.WriteLine($"PLC Timer: Reduciendo intervalo a {newInterval:F1}ms (ejecución máxima: {maxPlcExecutionTime:F1}ms)");
}
// Aplicar el nuevo intervalo si cambió
@@ -1394,20 +1470,20 @@ namespace CtrEditor
{
// 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)");
+ //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)");
+ //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");
+ //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
@@ -1607,23 +1683,6 @@ namespace CtrEditor
}
}
- private void OnDisplayUpdate(object? sender, EventArgs e)
- {
- if (simSampleCount > 0)
- {
- SimulationSpeed = accumulatedSimTime / simSampleCount;
- accumulatedSimTime = 0;
- simSampleCount = 0;
- }
-
- if (plcSampleCount > 0)
- {
- PlcUpdateSpeed = accumulatedPlcTime / plcSampleCount;
- accumulatedPlcTime = 0;
- plcSampleCount = 0;
- }
- }
-
// Reemplazar la propiedad _multiPropertyEditorWindow por un diccionario
private Dictionary _propertyEditorWindows = new();
@@ -1758,16 +1817,6 @@ namespace CtrEditor
libraryWindow.Show();
}
- private void OnTick3DUpdate(object? sender, EventArgs e)
- {
- // Solo actualizar Helix3D si la simulación está detenida y la actualización 3D está habilitada
- // Cuando la simulación está corriendo, la sincronización se hace en simulationManager.Step()
- if (!IsSimulationRunning && Is3DUpdateEnabled && Visualization3DManager != null)
- {
- Visualization3DManager.SynchronizeWorld();
- }
- }
-
#region Hydraulic Simulation Management
///
diff --git a/ObjetosSim/HydraulicComponents/osHydPump.cs b/ObjetosSim/HydraulicComponents/osHydPump.cs
index 6e09f56..5595e4e 100644
--- a/ObjetosSim/HydraulicComponents/osHydPump.cs
+++ b/ObjetosSim/HydraulicComponents/osHydPump.cs
@@ -239,6 +239,47 @@ namespace CtrEditor.ObjetosSim
}
}
+ [Category("📊 Estado Actual")]
+ [DisplayName("Estado Cavitación")]
+ [Description("Estado actual de cavitación de la bomba")]
+ [JsonIgnore]
+ public string CavitationStatus
+ {
+ get
+ {
+ if (!IsRunning) return "Bomba detenida";
+
+ var factor = CavitationFactor;
+ var canOperate = CanOperateWithoutCavitation;
+ var npshAvailable = NPSHAvailable;
+
+ if (canOperate && factor >= 0.9)
+ return "✅ Operación normal";
+ else if (factor >= 0.5)
+ return $"⚠️ Riesgo cavitación (Factor: {factor:F2})";
+ else
+ return $"❌ NPSH insuficiente ({npshAvailable:F2}m)";
+ }
+ }
+
+ [Category("📊 Estado Actual")]
+ [DisplayName("Estado Detallado")]
+ [Description("Información detallada del estado operacional")]
+ [JsonIgnore]
+ public string DetailedStatus
+ {
+ get
+ {
+ if (!IsRunning) return "Bomba detenida";
+
+ var factor = CavitationFactor;
+ var npshAvailable = NPSHAvailable;
+ var flow = CurrentFlowLMin;
+
+ return $"NPSH: {npshAvailable:F2}m | Factor: {factor:F2} | Flujo: {flow:F1} L/min";
+ }
+ }
+
// Propiedades de fluido actual
private FluidProperties _currentFluid = new FluidProperties(FluidType.Air);
@@ -629,22 +670,8 @@ namespace CtrEditor.ObjetosSim
if (SpeedRatio < 0.0) SpeedRatio = 0.0;
if (SpeedRatio > 1.0) SpeedRatio = 1.0;
- // Verificar condiciones de NPSH si está habilitada la verificación
- if (hydraulicSimulationManager.EnableNPSHVerification && IsRunning)
- {
- var npshAvailable = NPSHAvailable;
- var cavitationFactor = CavitationFactor;
-
- if (cavitationFactor < 0.5)
- {
- Debug.WriteLine($"⚠️ Bomba {Nombre}: Riesgo de cavitación - Factor={cavitationFactor:F2}, NPSH_disponible={npshAvailable:F2}m");
- }
-
- if (!CanOperateWithoutCavitation)
- {
- Debug.WriteLine($"❌ Bomba {Nombre}: NO puede operar sin cavitación - NPSH insuficiente");
- }
- }
+ // Las verificaciones de NPSH ahora se muestran a través de propiedades
+ // Se eliminaron los Debug.WriteLine para evitar saturación del sistema
//Debug.WriteLine($"Bomba {Nombre}: Velocidad={SpeedRatio:F2}, Funcionando={IsRunning}");
}
diff --git a/ObjetosSim/HydraulicComponents/osHydTank.cs b/ObjetosSim/HydraulicComponents/osHydTank.cs
index 91fe4f2..0c60084 100644
--- a/ObjetosSim/HydraulicComponents/osHydTank.cs
+++ b/ObjetosSim/HydraulicComponents/osHydTank.cs
@@ -834,6 +834,91 @@ namespace CtrEditor.ObjetosSim
[JsonIgnore]
public double CurrentFluidViscosity => CurrentOutputFluid.Viscosity;
+ ///
+ /// Porcentaje de altura para la sección secundaria (abajo) - para display visual
+ ///
+ [JsonIgnore]
+ public double SecondaryDisplayPercentage
+ {
+ get
+ {
+ var totalVolume = _primaryVolumeL + _secondaryVolumeL + _mixingVolumeL;
+ if (totalVolume == 0) return 0;
+ return (_secondaryVolumeL / totalVolume) * FillPercentage / 100.0;
+ }
+ }
+
+ ///
+ /// Porcentaje de altura para la sección de mezcla (medio) - para display visual
+ ///
+ [JsonIgnore]
+ public double MixDisplayPercentage
+ {
+ get
+ {
+ var totalVolume = _primaryVolumeL + _secondaryVolumeL + _mixingVolumeL;
+ if (totalVolume == 0) return 0;
+ return (_mixingVolumeL / totalVolume) * FillPercentage / 100.0;
+ }
+ }
+
+ ///
+ /// Porcentaje de altura para la sección primaria (arriba) - para display visual
+ ///
+ [JsonIgnore]
+ public double PrimaryDisplayPercentage
+ {
+ get
+ {
+ var totalVolume = _primaryVolumeL + _secondaryVolumeL + _mixingVolumeL;
+ if (totalVolume == 0) return 0;
+ return (_primaryVolumeL / totalVolume) * FillPercentage / 100.0;
+ }
+ }
+
+ ///
+ /// Color para la sección secundaria
+ ///
+ [JsonIgnore]
+ public SolidColorBrush SecondaryLevelColor
+ {
+ get
+ {
+ var colorHex = _secondaryFluid.Color;
+ var color = (Color)System.Windows.Media.ColorConverter.ConvertFromString(colorHex);
+ return new SolidColorBrush(color);
+ }
+ }
+
+ ///
+ /// Color para la sección de mezcla
+ ///
+ [JsonIgnore]
+ public SolidColorBrush MixLevelColor
+ {
+ get
+ {
+ var mixedFluid = _primaryFluid.MixWith(_secondaryFluid, 0.5); // Mezcla 50-50
+ var colorHex = mixedFluid.Color;
+ var color = (Color)System.Windows.Media.ColorConverter.ConvertFromString(colorHex);
+ return new SolidColorBrush(color);
+ }
+ }
+
+ ///
+ /// Color para la sección primaria
+ ///
+ [JsonIgnore]
+ public SolidColorBrush PrimaryLevelColor
+ {
+ get
+ {
+ var colorHex = _primaryFluid.Color;
+ var color = (Color)System.Windows.Media.ColorConverter.ConvertFromString(colorHex);
+ return new SolidColorBrush(color);
+ }
+ }
+
// Private Methods
diff --git a/ObjetosSim/HydraulicComponents/ucHydTank.xaml b/ObjetosSim/HydraulicComponents/ucHydTank.xaml
index ba37973..1ceee4e 100644
--- a/ObjetosSim/HydraulicComponents/ucHydTank.xaml
+++ b/ObjetosSim/HydraulicComponents/ucHydTank.xaml
@@ -34,23 +34,70 @@
RadiusX="5"
RadiusY="5"/>
-
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+