feat: Add detailed status properties and margin converter for tank sections in hydraulic components
This commit is contained in:
parent
380fb5ba50
commit
322d335edd
1
App.xaml
1
App.xaml
|
@ -27,6 +27,7 @@
|
|||
<local:UnsavedChangesConverter x:Key="UnsavedChangesConverter"/>
|
||||
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
|
||||
<converters:TankLevelToHeightConverter x:Key="TankLevelToHeightConverter"/>
|
||||
<converters:TankSectionMarginConverter x:Key="TankSectionMarginConverter"/>
|
||||
|
||||
<!-- DropShadowEffect para efectos de sombra en texto -->
|
||||
<DropShadowEffect x:Key="DropShadowEffect"
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace CtrEditor.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converter para calcular el margen de las secciones del tanque
|
||||
/// basado en los porcentajes de las secciones previas
|
||||
/// </summary>
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
115
MainViewModel.cs
115
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;
|
||||
|
||||
|
@ -439,6 +440,13 @@ namespace CtrEditor
|
|||
_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
|
|||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evento del timer que hace flush automático del buffer de debug para evitar acumulación excesiva
|
||||
/// </summary>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evento del timer para actualización de display (UI)
|
||||
/// </summary>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evento del timer para actualización 3D cuando la simulación está detenida
|
||||
/// </summary>
|
||||
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<string, Windows.MultiPropertyEditorWindow> _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
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -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}");
|
||||
}
|
||||
|
|
|
@ -834,6 +834,91 @@ namespace CtrEditor.ObjetosSim
|
|||
[JsonIgnore]
|
||||
public double CurrentFluidViscosity => CurrentOutputFluid.Viscosity;
|
||||
|
||||
/// <summary>
|
||||
/// Porcentaje de altura para la sección secundaria (abajo) - para display visual
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public double SecondaryDisplayPercentage
|
||||
{
|
||||
get
|
||||
{
|
||||
var totalVolume = _primaryVolumeL + _secondaryVolumeL + _mixingVolumeL;
|
||||
if (totalVolume == 0) return 0;
|
||||
return (_secondaryVolumeL / totalVolume) * FillPercentage / 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Porcentaje de altura para la sección de mezcla (medio) - para display visual
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public double MixDisplayPercentage
|
||||
{
|
||||
get
|
||||
{
|
||||
var totalVolume = _primaryVolumeL + _secondaryVolumeL + _mixingVolumeL;
|
||||
if (totalVolume == 0) return 0;
|
||||
return (_mixingVolumeL / totalVolume) * FillPercentage / 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Porcentaje de altura para la sección primaria (arriba) - para display visual
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public double PrimaryDisplayPercentage
|
||||
{
|
||||
get
|
||||
{
|
||||
var totalVolume = _primaryVolumeL + _secondaryVolumeL + _mixingVolumeL;
|
||||
if (totalVolume == 0) return 0;
|
||||
return (_primaryVolumeL / totalVolume) * FillPercentage / 100.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Color para la sección secundaria
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public SolidColorBrush SecondaryLevelColor
|
||||
{
|
||||
get
|
||||
{
|
||||
var colorHex = _secondaryFluid.Color;
|
||||
var color = (Color)System.Windows.Media.ColorConverter.ConvertFromString(colorHex);
|
||||
return new SolidColorBrush(color);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Color para la sección de mezcla
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Color para la sección primaria
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public SolidColorBrush PrimaryLevelColor
|
||||
{
|
||||
get
|
||||
{
|
||||
var colorHex = _primaryFluid.Color;
|
||||
var color = (Color)System.Windows.Media.ColorConverter.ConvertFromString(colorHex);
|
||||
return new SolidColorBrush(color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Private Methods
|
||||
|
|
|
@ -34,23 +34,70 @@
|
|||
RadiusX="5"
|
||||
RadiusY="5"/>
|
||||
|
||||
<!-- Nivel del líquido -->
|
||||
<Rectangle x:Name="rectLevel"
|
||||
Fill="{Binding LevelColor}"
|
||||
Stroke="{Binding LevelBorderColor}"
|
||||
StrokeThickness="1"
|
||||
<!-- Nivel del líquido - 3 secciones -->
|
||||
|
||||
<!-- Sección secundaria (abajo) -->
|
||||
<Rectangle x:Name="rectSecondaryLevel"
|
||||
Fill="{Binding SecondaryLevelColor}"
|
||||
Stroke="DarkBlue"
|
||||
StrokeThickness="0.5"
|
||||
RadiusX="3"
|
||||
RadiusY="3"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="4,4,4,4">
|
||||
<Rectangle.Height>
|
||||
<MultiBinding Converter="{StaticResource TankLevelToHeightConverter}">
|
||||
<Binding Path="FillPercentage"/>
|
||||
<Binding Path="SecondaryDisplayPercentage"/>
|
||||
<Binding Path="Tamano"/>
|
||||
</MultiBinding>
|
||||
</Rectangle.Height>
|
||||
</Rectangle>
|
||||
|
||||
<!-- Sección de mezcla (medio) -->
|
||||
<Rectangle x:Name="rectMixLevel"
|
||||
Fill="{Binding MixLevelColor}"
|
||||
Stroke="Purple"
|
||||
StrokeThickness="0.5"
|
||||
RadiusX="3"
|
||||
RadiusY="3"
|
||||
VerticalAlignment="Bottom">
|
||||
<Rectangle.Height>
|
||||
<MultiBinding Converter="{StaticResource TankLevelToHeightConverter}">
|
||||
<Binding Path="MixDisplayPercentage"/>
|
||||
<Binding Path="Tamano"/>
|
||||
</MultiBinding>
|
||||
</Rectangle.Height>
|
||||
<Rectangle.Margin>
|
||||
<MultiBinding Converter="{StaticResource TankSectionMarginConverter}">
|
||||
<Binding Path="SecondaryDisplayPercentage"/>
|
||||
<Binding Path="Tamano"/>
|
||||
</MultiBinding>
|
||||
</Rectangle.Margin>
|
||||
</Rectangle>
|
||||
|
||||
<!-- Sección primaria (arriba) -->
|
||||
<Rectangle x:Name="rectPrimaryLevel"
|
||||
Fill="{Binding PrimaryLevelColor}"
|
||||
Stroke="DarkGreen"
|
||||
StrokeThickness="0.5"
|
||||
RadiusX="3"
|
||||
RadiusY="3"
|
||||
VerticalAlignment="Bottom">
|
||||
<Rectangle.Height>
|
||||
<MultiBinding Converter="{StaticResource TankLevelToHeightConverter}">
|
||||
<Binding Path="PrimaryDisplayPercentage"/>
|
||||
<Binding Path="Tamano"/>
|
||||
</MultiBinding>
|
||||
</Rectangle.Height>
|
||||
<Rectangle.Margin>
|
||||
<MultiBinding Converter="{StaticResource TankSectionMarginConverter}">
|
||||
<Binding Path="SecondaryDisplayPercentage"/>
|
||||
<Binding Path="MixDisplayPercentage"/>
|
||||
<Binding Path="Tamano"/>
|
||||
</MultiBinding>
|
||||
</Rectangle.Margin>
|
||||
</Rectangle>
|
||||
|
||||
<!-- Líneas de nivel (marcas visuales) -->
|
||||
<Canvas>
|
||||
<!-- Línea de nivel máximo -->
|
||||
|
|
|
@ -27,6 +27,11 @@ namespace CtrEditor.Services
|
|||
// Custom TraceListener para capturar Debug.WriteLine
|
||||
private DebugTraceListener _traceListener;
|
||||
|
||||
// Control de buffer para evitar acumulación excesiva
|
||||
private const int MAX_BUFFER_SIZE = 1000;
|
||||
private int _messageCount = 0;
|
||||
private readonly object _bufferLock = new object();
|
||||
|
||||
public DebugConsoleServer(int port = 5007)
|
||||
{
|
||||
_port = port;
|
||||
|
@ -175,6 +180,10 @@ namespace CtrEditor.Services
|
|||
{
|
||||
if (_messageQueue.TryDequeue(out string message))
|
||||
{
|
||||
lock (_bufferLock)
|
||||
{
|
||||
_messageCount = Math.Max(0, _messageCount - 1);
|
||||
}
|
||||
await BroadcastMessageAsync(message);
|
||||
}
|
||||
else
|
||||
|
@ -243,8 +252,61 @@ namespace CtrEditor.Services
|
|||
public void QueueMessage(string message)
|
||||
{
|
||||
if (_isRunning)
|
||||
{
|
||||
lock (_bufferLock)
|
||||
{
|
||||
_messageQueue.Enqueue(message);
|
||||
_messageCount++;
|
||||
|
||||
// Si el buffer se está llenando demasiado, hacer un flush automático
|
||||
if (_messageCount > MAX_BUFFER_SIZE)
|
||||
{
|
||||
FlushMessageBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Limpia el buffer de mensajes para evitar acumulación excesiva
|
||||
/// </summary>
|
||||
public void FlushMessageBuffer()
|
||||
{
|
||||
lock (_bufferLock)
|
||||
{
|
||||
// Mantener solo los últimos 100 mensajes
|
||||
var messagesToKeep = new List<string>();
|
||||
var count = 0;
|
||||
|
||||
while (_messageQueue.TryDequeue(out string message) && count < 100)
|
||||
{
|
||||
messagesToKeep.Insert(0, message); // Insertar al inicio para mantener orden
|
||||
count++;
|
||||
}
|
||||
|
||||
// Limpiar completamente la cola
|
||||
while (_messageQueue.TryDequeue(out _)) { }
|
||||
|
||||
// Volver a agregar los mensajes que queremos mantener
|
||||
foreach (var message in messagesToKeep)
|
||||
{
|
||||
_messageQueue.Enqueue(message);
|
||||
}
|
||||
|
||||
_messageCount = messagesToKeep.Count;
|
||||
|
||||
Debug.WriteLine($"[Debug Console Server] Buffer flushed. Messages kept: {_messageCount}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtiene estadísticas del buffer de mensajes
|
||||
/// </summary>
|
||||
public (int messageCount, int maxBufferSize) GetBufferStats()
|
||||
{
|
||||
lock (_bufferLock)
|
||||
{
|
||||
return (_messageCount, MAX_BUFFER_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue