feat: Implement Debug Console Server for enhanced logging and remote monitoring
This commit is contained in:
parent
749f0f7eff
commit
10a1617833
|
@ -534,7 +534,7 @@ namespace CtrEditor.HydraulicSimulator
|
|||
/*
|
||||
if (VerboseOutput)
|
||||
{
|
||||
Debug.WriteLine($"Bomba {pump.GetType().Name}: Velocidad={pump.SpeedRatio:F2}, " +
|
||||
Trace.WriteLine($"Bomba {pump.GetType().Name}: Velocidad={pump.SpeedRatio:F2}, " +
|
||||
$"Funcionando={pump.IsRunning}, Dirección={pump.PumpDirection}");
|
||||
}
|
||||
*/
|
||||
|
@ -567,17 +567,18 @@ namespace CtrEditor.HydraulicSimulator
|
|||
// Calcular presión basada en nivel si no es presión fija
|
||||
if (!tank.IsFixedPressure)
|
||||
{
|
||||
// P = ρgh + P_atmosferica
|
||||
// P = ρgh + P_atmosferica (resultado en Pa)
|
||||
double pressureFromLevel = SimulationFluid.Rho * 9.80665 * tank.Level; // + presión atmosférica
|
||||
tank.TankPressure = pressureFromLevel;
|
||||
// Convertir de Pa a bar (1 bar = 100000 Pa)
|
||||
tank.TankPressure = pressureFromLevel / 100000.0;
|
||||
}
|
||||
|
||||
// Debug output deshabilitado para mejorar rendimiento
|
||||
/*
|
||||
if (VerboseOutput)
|
||||
{
|
||||
Debug.WriteLine($"Tanque {tank.GetType().Name}: Nivel={tank.Level:F2}m, " +
|
||||
$"Presión={tank.TankPressure:F0}Pa, PresionFija={tank.IsFixedPressure}");
|
||||
Trace.WriteLine($"Tanque {tank.GetType().Name}: Nivel={tank.Level:F2}m, " +
|
||||
$"Presión={tank.TankPressure:F3}bar ({tank.TankPressure * 100000.0:F0}Pa), PresionFija={tank.IsFixedPressure}");
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -111,9 +111,9 @@ namespace CtrEditor.HydraulicSimulator
|
|||
if (WpfObject is osHydTank tank)
|
||||
{
|
||||
TankPressure = tank.TankPressure;
|
||||
CurrentLevel = tank.CurrentLevel;
|
||||
MaxLevel = tank.MaxLevel;
|
||||
MinLevel = tank.MinLevel;
|
||||
CurrentLevel = tank.CurrentLevelM;
|
||||
MaxLevel = tank.MaxLevelM;
|
||||
MinLevel = tank.MinLevelM;
|
||||
CrossSectionalArea = tank.CrossSectionalArea;
|
||||
IsFixedPressure = tank.IsFixedPressure;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ using System.Timers; // Para el nuevo timer de simulación más preciso
|
|||
using CtrEditor.PopUps;
|
||||
using System.Windows.Data;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CtrEditor.Services; // Para MCPServer y DebugConsoleServer
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Collections.Specialized;
|
||||
using CtrEditor.Serialization; // Add this line
|
||||
|
@ -75,6 +76,9 @@ namespace CtrEditor
|
|||
// Servidor MCP para control remoto
|
||||
private MCPServer _mcpServer;
|
||||
|
||||
// Servidor de debug console para logging
|
||||
private DebugConsoleServer _debugConsoleServer;
|
||||
|
||||
[ObservableProperty]
|
||||
private int mcpServerPort = 5006;
|
||||
|
||||
|
@ -1787,6 +1791,9 @@ namespace CtrEditor
|
|||
await _mcpServer.StartAsync();
|
||||
|
||||
Debug.WriteLine($"[MCP] Servidor MCP iniciado en puerto {McpServerPort}");
|
||||
|
||||
// Iniciar también el servidor de debug console
|
||||
StartDebugConsoleServer();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -1803,8 +1810,10 @@ namespace CtrEditor
|
|||
try
|
||||
{
|
||||
_mcpServer?.Stop();
|
||||
|
||||
Debug.WriteLine("[MCP] Servidor MCP detenido");
|
||||
|
||||
// Detener también el servidor de debug console
|
||||
StopDebugConsoleServer();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -1821,6 +1830,9 @@ namespace CtrEditor
|
|||
{
|
||||
_mcpServer?.Dispose();
|
||||
_mcpServer = null;
|
||||
|
||||
// Limpiar también el servidor de debug console
|
||||
CleanupDebugConsoleServer();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -1828,6 +1840,61 @@ namespace CtrEditor
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inicia el servidor de debug console
|
||||
/// </summary>
|
||||
private void StartDebugConsoleServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_debugConsoleServer != null)
|
||||
{
|
||||
_debugConsoleServer.Dispose();
|
||||
}
|
||||
|
||||
_debugConsoleServer = new DebugConsoleServer(5007); // Puerto fijo para debug
|
||||
_debugConsoleServer.Start();
|
||||
|
||||
Debug.WriteLine("[Debug Console] Servidor de debug console iniciado en puerto 5007");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[Debug Console] Error iniciando servidor de debug console: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detiene el servidor de debug console
|
||||
/// </summary>
|
||||
private void StopDebugConsoleServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
_debugConsoleServer?.Stop();
|
||||
Debug.WriteLine("[Debug Console] Servidor de debug console detenido");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[Debug Console] Error deteniendo servidor de debug console: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Limpia el servidor de debug console al cerrar la aplicación
|
||||
/// </summary>
|
||||
private void CleanupDebugConsoleServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
_debugConsoleServer?.Dispose();
|
||||
_debugConsoleServer = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[Debug Console] Error limpiando servidor de debug console: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
|
|
@ -634,10 +634,10 @@ namespace CtrEditor.ObjetosSim
|
|||
// El tanque siempre es un nodo de presión fija si está configurado así
|
||||
if (IsFixedPressure)
|
||||
{
|
||||
// Convertir bar a Pa para el sistema hidráulico interno
|
||||
var pressurePa = TankPressure * 100000.0;
|
||||
// TankPressure ya está en Pa, no necesita conversión
|
||||
var pressurePa = TankPressure;
|
||||
nodes.Add(new HydraulicNodeDefinition(Nombre, true, pressurePa, GetTankDescription()));
|
||||
//Debug.WriteLine($"Tanque {Nombre}: Nodo de presión fija creado - {TankPressure:F2} bar ({pressurePa:F0} Pa)");
|
||||
//Debug.WriteLine($"Tanque {Nombre}: Nodo de presión fija creado - {TankPressure:F0} Pa ({TankPressure/100000.0:F2} bar)");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -655,10 +655,10 @@ namespace CtrEditor.ObjetosSim
|
|||
// Obtener flujos conectados
|
||||
UpdateFlowsFromConnectedPipes(flows);
|
||||
|
||||
// Actualizar presión (convertir de Pa a bar)
|
||||
// Actualizar presión (convertir de Pa a bar para consistencia)
|
||||
if (pressures.ContainsKey(Nombre))
|
||||
{
|
||||
CurrentPressure = pressures[Nombre] / 100000.0; // Pa a bar
|
||||
CurrentPressure = pressures[Nombre] / 100000.0; // Convertir Pa a bar
|
||||
}
|
||||
|
||||
// Actualizar nivel basado en balance de flujo
|
||||
|
@ -666,9 +666,9 @@ namespace CtrEditor.ObjetosSim
|
|||
|
||||
if (VerboseLogging())
|
||||
{
|
||||
Debug.WriteLine($"Tanque {Nombre}: Nivel={CurrentLevelM:F2}m ({FillPercentage:F1}%), " +
|
||||
Trace.WriteLine($"Tanque {Nombre}: Nivel={CurrentLevelM:F2}m ({FillPercentage:F1}%), " +
|
||||
$"Flujo_entrada={InletFlow:F2}L/min, Flujo_salida={OutletFlow:F2}L/min, " +
|
||||
$"Balance={FlowBalance:F2}L/min, Presión={CurrentPressure:F2}bar, Fluido={CurrentFluidDescription}");
|
||||
$"Balance={FlowBalance:F2}L/min, Presión={CurrentPressure:F3}bar ({CurrentPressure * 100000.0:F0}Pa), Fluido={CurrentFluidDescription}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1013,7 +1013,7 @@ namespace CtrEditor.ObjetosSim
|
|||
// Debug para verificar cambios
|
||||
if (VerboseLogging() && Math.Abs(volumeChangeL) > 0.1)
|
||||
{
|
||||
Debug.WriteLine($"Tanque {Nombre}: Δt={deltaTime:F3}min, ΔVolumen={volumeChangeL:F2}L, " +
|
||||
Trace.WriteLine($"Tanque {Nombre}: Δt={deltaTime:F3}min, ΔVolumen={volumeChangeL:F2}L, " +
|
||||
$"Volumen={CurrentVolumeL:F1}L, Estado={MixingState}, Fluido={CurrentFluidDescription}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,281 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CtrEditor.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Servidor TCP que captura y retransmite mensajes de debug console
|
||||
/// Permite al MCP Proxy escuchar los logs de debug de CtrEditor
|
||||
/// </summary>
|
||||
public class DebugConsoleServer : IDisposable
|
||||
{
|
||||
private readonly int _port;
|
||||
private TcpListener _tcpListener;
|
||||
private bool _isRunning;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private readonly object _lockObject = new object();
|
||||
private readonly ConcurrentQueue<string> _messageQueue = new ConcurrentQueue<string>();
|
||||
private readonly ConcurrentBag<TcpClient> _connectedClients = new ConcurrentBag<TcpClient>();
|
||||
|
||||
// Custom TraceListener para capturar Debug.WriteLine
|
||||
private DebugTraceListener _traceListener;
|
||||
|
||||
public DebugConsoleServer(int port = 5007)
|
||||
{
|
||||
_port = port;
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inicia el servidor de debug console
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
if (_isRunning) return;
|
||||
|
||||
try
|
||||
{
|
||||
_tcpListener = new TcpListener(IPAddress.Loopback, _port);
|
||||
_tcpListener.Start();
|
||||
_isRunning = true;
|
||||
|
||||
// Instalar el trace listener personalizado solo para Trace
|
||||
_traceListener = new DebugTraceListener(this);
|
||||
Trace.Listeners.Add(_traceListener);
|
||||
|
||||
Trace.WriteLine($"[Debug Console Server] Servidor de debug iniciado en puerto {_port}");
|
||||
|
||||
// Procesar conexiones en background
|
||||
_ = Task.Run(async () => await AcceptConnectionsAsync(_cancellationTokenSource.Token));
|
||||
|
||||
// Procesar cola de mensajes
|
||||
_ = Task.Run(async () => await ProcessMessageQueueAsync(_cancellationTokenSource.Token));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[Debug Console Server] Error al iniciar servidor: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detiene el servidor de debug console
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
if (!_isRunning) return;
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (!_isRunning) return;
|
||||
|
||||
_isRunning = false;
|
||||
_cancellationTokenSource?.Cancel();
|
||||
|
||||
// Remover el trace listener
|
||||
if (_traceListener != null)
|
||||
{
|
||||
Trace.Listeners.Remove(_traceListener);
|
||||
_traceListener = null;
|
||||
}
|
||||
|
||||
_tcpListener?.Stop();
|
||||
|
||||
// Cerrar todas las conexiones de clientes
|
||||
foreach (var client in _connectedClients)
|
||||
{
|
||||
try
|
||||
{
|
||||
client?.Close();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
Debug.WriteLine("[Debug Console Server] Servidor de debug detenido");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Acepta conexiones de clientes
|
||||
/// </summary>
|
||||
private async Task AcceptConnectionsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (_isRunning && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
Debug.WriteLine("[Debug Console Server] Esperando conexión de cliente...");
|
||||
var tcpClient = await _tcpListener.AcceptTcpClientAsync();
|
||||
_connectedClients.Add(tcpClient);
|
||||
|
||||
Debug.WriteLine("[Debug Console Server] Cliente debug conectado");
|
||||
|
||||
// Manejar cliente en background
|
||||
_ = Task.Run(async () => await HandleClientAsync(tcpClient, cancellationToken));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (_isRunning)
|
||||
{
|
||||
Debug.WriteLine($"[Debug Console Server] Error aceptando conexión: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maneja un cliente conectado
|
||||
/// </summary>
|
||||
private async Task HandleClientAsync(TcpClient client, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var stream = client.GetStream();
|
||||
|
||||
// Enviar mensaje de bienvenida
|
||||
var welcomeMessage = $"[Debug Console Server] Conectado al servidor de debug de CtrEditor en puerto {_port}\n";
|
||||
await SendMessageToClientAsync(stream, welcomeMessage);
|
||||
|
||||
// Mantener conexión viva hasta cancelación
|
||||
while (_isRunning && client.Connected && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(1000, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[Debug Console Server] Error en cliente debug: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
client?.Close();
|
||||
}
|
||||
catch { }
|
||||
Debug.WriteLine("[Debug Console Server] Cliente debug desconectado");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Procesa la cola de mensajes y los envía a todos los clientes conectados
|
||||
/// </summary>
|
||||
private async Task ProcessMessageQueueAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (_isRunning && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (_messageQueue.TryDequeue(out string message))
|
||||
{
|
||||
await BroadcastMessageAsync(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(50, cancellationToken); // Pequeña pausa si no hay mensajes
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (_isRunning)
|
||||
{
|
||||
Debug.WriteLine($"[Debug Console Server] Error procesando cola de mensajes: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Envía un mensaje a todos los clientes conectados
|
||||
/// </summary>
|
||||
private async Task BroadcastMessageAsync(string message)
|
||||
{
|
||||
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||||
var formattedMessage = $"[{timestamp}] {message}\n";
|
||||
|
||||
var clientsToRemove = new List<TcpClient>();
|
||||
|
||||
foreach (var client in _connectedClients)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (client.Connected)
|
||||
{
|
||||
var stream = client.GetStream();
|
||||
await SendMessageToClientAsync(stream, formattedMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
clientsToRemove.Add(client);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
clientsToRemove.Add(client);
|
||||
}
|
||||
}
|
||||
|
||||
// Remover clientes desconectados
|
||||
// Nota: ConcurrentBag no permite eliminación directa, pero no es crítico
|
||||
// Los clientes desconectados se manejan en HandleClientAsync
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Envía un mensaje a un cliente específico
|
||||
/// </summary>
|
||||
private async Task SendMessageToClientAsync(NetworkStream stream, string message)
|
||||
{
|
||||
var data = Encoding.UTF8.GetBytes(message);
|
||||
await stream.WriteAsync(data, 0, data.Length);
|
||||
await stream.FlushAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Agrega un mensaje a la cola para transmisión
|
||||
/// </summary>
|
||||
public void QueueMessage(string message)
|
||||
{
|
||||
if (_isRunning)
|
||||
{
|
||||
_messageQueue.Enqueue(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
_cancellationTokenSource?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TraceListener personalizado que captura mensajes de Debug.WriteLine
|
||||
/// y los envía al servidor de debug console
|
||||
/// </summary>
|
||||
internal class DebugTraceListener : TraceListener
|
||||
{
|
||||
private readonly DebugConsoleServer _server;
|
||||
|
||||
public DebugTraceListener(DebugConsoleServer server)
|
||||
{
|
||||
_server = server;
|
||||
}
|
||||
|
||||
public override void Write(string message)
|
||||
{
|
||||
_server?.QueueMessage(message);
|
||||
}
|
||||
|
||||
public override void WriteLine(string message)
|
||||
{
|
||||
_server?.QueueMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue