using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Net;
using System.Windows.Controls;
using System.Windows.Media;
using XFEExtension.NetCore.FormatExtension;
using XFEExtension.NetCore.XFEConsole;
using XFEToolBox.Client.Profiles.CrossVersionProfiles;
using XFEToolBox.Client.Utilities;
using XFEToolBox.Client.Views.Pages;
namespace XFEToolBox.Client.ViewModel.Pages;
public partial class ConsolePageViewModel : ObservableObject
{
[ObservableProperty]
private bool canStartServer = true;
[ObservableProperty]
private bool canStopServer = false;
[ObservableProperty]
private bool canRestartServer = false;
[ObservableProperty]
private bool canCleanUp = false;
private bool autoScroll = true;
private readonly BufferedConsoleRenderer outputRenderer;
public ConsolePage ViewPage { get; set; }
public XFEConsoleTerminalServer? TerminalServer { get; set; }
public XFEConsoleTerminalClient? TerminalClient { get; set; }
public ConsolePageViewModel(ConsolePage viewPage)
{
ViewPage = viewPage;
outputRenderer = new(viewPage.Dispatcher, viewPage.consoleListBox, () => ConsoleProfile.MaxLine);
outputRenderer.ContentChanged += hasContent =>
{
CanCleanUp = hasContent;
if (hasContent && autoScroll)
ViewPage.ScrollConsoleToEnd();
};
}
public async Task StartConsoleServer()
{
TerminalServer = new XFEConsoleTerminalServer(ConsoleProfile.ConsolePort, ConsoleProfile.LocalHostOnly, ConsoleProfile.ConsolePassword);
TerminalServer.ServerStarted += TerminalServer_ServerStarted;
TerminalServer.Connected += TerminalServer_Connected;
TerminalServer.Disconnected += TerminalServer_Disconnected;
TerminalServer.MessageReceived += TerminalServer_MessageReceived;
TerminalServer.ErrorOccurred += TerminalServer_ErrorOccurred;
await TerminalServer.StartAsync();
}
public async Task StartRemoteConsoleClient()
{
if (TerminalClient is not null)
await TerminalClient.DisposeAsync();
TerminalClient = new XFEConsoleTerminalClient(
NormalizeRemoteAddress(ConsoleProfile.RemoteServerAddress),
ConsoleProfile.RemoteServerPassword,
Environment.MachineName,
$"XFEToolBox-{Environment.ProcessId}");
TerminalClient.Connected += TerminalClient_Connected;
TerminalClient.Disconnected += TerminalClient_Disconnected;
TerminalClient.MessageReceived += TerminalClient_MessageReceived;
TerminalClient.ErrorOccurred += TerminalClient_ErrorOccurred;
if (!await TerminalClient.ConnectAsync())
throw new UnauthorizedAccessException(TerminalClient.AuthenticationFailureReason ?? "远程调试服务器拒绝了连接密码。");
}
public Task StartConsoleEndpoint() => ConsoleProfile.ConnectToRemoteServer
? StartRemoteConsoleClient()
: StartConsoleServer();
public async Task ShutDownEndpoint()
{
var terminalClient = TerminalClient;
TerminalClient = null;
if (terminalClient is not null)
{
await terminalClient.DisconnectAsync();
await terminalClient.DisposeAsync();
}
var terminalServer = TerminalServer;
TerminalServer = null;
if (terminalServer is not null)
await terminalServer.StopAsync();
}
private static string NormalizeRemoteAddress(string? address)
{
var normalized = string.IsNullOrWhiteSpace(address)
? $"ws://localhost:{ConsoleProfile.ConsolePort}/"
: address.Trim();
if (!normalized.Contains("://", StringComparison.Ordinal))
normalized = $"ws://{normalized}";
if (Uri.TryCreate(normalized, UriKind.Absolute, out var uri))
{
var builder = new UriBuilder(uri);
if (builder.Scheme == Uri.UriSchemeHttp)
builder.Scheme = "ws";
else if (builder.Scheme == Uri.UriSchemeHttps)
builder.Scheme = "wss";
return builder.Uri.ToString();
}
return normalized;
}
private void TerminalServer_ErrorOccurred(XFEConsoleClientInfo sender, Exception e)
{
var code = e.InnerException?.InnerException is HttpListenerException ? (e.InnerException.InnerException as HttpListenerException)!.ErrorCode : 0;
if (code != 995)
ShowMessageWithTimeLine($"[foldblock color: white #ff0000 title: 错误:{e.Message} text: {e}]");
}
private void TerminalServer_MessageReceived(XFEConsoleClientInfo sender, string e)
{
var dictionary = new XFEDictionary(e);
var isLine = dictionary["IsLine"] == "true";
var textMessage = dictionary["Text"] ?? string.Empty;
outputRenderer.Append(
startsNewLine => startsNewLine
? $"[{DateTime.Now:HH:mm:ss}] {sender.ClientName}> {textMessage}"
: textMessage,
Colors.White,
isLine);
}
private void TerminalServer_Disconnected(XFEConsoleTerminalServer sender, XFEConsoleClientInfo e)
{
ShowMessageWithTimeLine($"[color #9898e7]客户端断开连接 [color white]{e.ClientName}");
}
private void TerminalServer_Connected(XFEConsoleTerminalServer sender, XFEConsoleClientInfo e)
{
ShowMessageWithTimeLine($"[color #9898e7]客户端连接 [color white]{e.ClientName}");
}
private void TerminalServer_ServerStarted(XFEConsoleTerminalServer sender)
{
ShowMessageWithTimeLine($"控制台服务器已在端口:[hyperlink color: #05ff00 link: http://localhost:{ConsoleProfile.ConsolePort}/ text: {ConsoleProfile.ConsolePort}] 上运行");
}
private void TerminalClient_Connected(XFEConsoleTerminalClient sender)
{
var programName = string.IsNullOrWhiteSpace(sender.RemoteProgramName) ? sender.ServerAddress : sender.RemoteProgramName;
ShowMessageWithTimeLine($"[color #9898e7]已连接远程调试程序 [color white]{programName}");
}
private void TerminalClient_Disconnected(XFEConsoleTerminalClient sender)
{
ShowMessageWithTimeLine("[color #9898e7]远程调试程序已断开连接");
ViewPage.Dispatcher.BeginInvoke(() =>
{
CanStartServer = true;
CanStopServer = false;
CanRestartServer = false;
});
}
private void TerminalClient_MessageReceived(XFEConsoleTerminalClient sender, string message)
{
var dictionary = new XFEDictionary(message);
var isLine = dictionary["IsLine"] == "true";
var textMessage = dictionary["Text"] ?? string.Empty;
var programName = string.IsNullOrWhiteSpace(sender.RemoteProgramName) ? "远程程序" : sender.RemoteProgramName;
outputRenderer.Append(
startsNewLine => startsNewLine
? $"[{DateTime.Now:HH:mm:ss}] {programName}> {textMessage}"
: textMessage,
Colors.White,
isLine);
}
private void TerminalClient_ErrorOccurred(XFEConsoleTerminalClient sender, Exception exception)
=> ShowMessageWithTimeLine($"[foldblock color: white #ff0000 title: 远程连接错误:{exception.Message} text: {exception}]");
internal void ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.ExtentHeightChange == 0 && e.VerticalChange < 0)
autoScroll = false;
else if (e.VerticalOffset + e.ViewportHeight >= e.ExtentHeight - 0.5)
autoScroll = true;
if (autoScroll && e.ExtentHeightChange > 0 && sender is ScrollViewer scrollViewer)
scrollViewer.ScrollToEnd();
}
///
/// 显示消息
///
///
///
public void ShowMessage(string message, Color defaultColor, bool isLineEnd, bool forceLine = false)
=> outputRenderer.Append(message, defaultColor, isLineEnd, forceLine);
///
/// 显示消息
///
///
///
public async Task ShowMessageAsync(string message, Color defaultColor, bool isLineEnd, bool forceLine = false)
=> await outputRenderer.AppendAsync(message, defaultColor, isLineEnd, forceLine);
///
/// 显示消息
///
///
public void ShowMessage(string message, bool isLineEnd = true, bool forceLine = false) => ShowMessage(message, Colors.White, isLineEnd, forceLine);
///
/// 显示消息
///
///
public async Task ShowMessageAsync(string message, bool isLineEnd = true, bool forceLine = false) => await ShowMessageAsync(message, Colors.White, isLineEnd, forceLine);
///
/// 显示附带时间轴的消息
///
///
public void ShowMessageWithTimeLine(string message, bool isLineEnd = true) => ShowMessage($"[{DateTime.Now:HH:mm:ss}] {message}", isLineEnd, true);
///
/// 显示附带时间轴的消息
///
///
public async Task ShowMessageWithTimeLineAsync(string message, bool isLineEnd = true) => await ShowMessageAsync($"[{DateTime.Now:HH:mm:ss}] {message}", isLineEnd, true);
#region Command
[RelayCommand]
private async Task StartServer()
{
CanRestartServer = false;
CanStartServer = false;
try
{
await ShowMessageWithTimeLineAsync(ConsoleProfile.ConnectToRemoteServer
? "[color yellow]正在连接远程调试服务器..."
: "[color yellow]正在启动控制台服务器...");
await StartConsoleEndpoint();
CanStopServer = true;
CanRestartServer = true;
}
catch (Exception ex)
{
await ShutDownEndpoint();
await ShowMessageWithTimeLineAsync($"[color red]无法建立调试连接:{ex.Message}");
CanRestartServer = false;
CanStopServer = false;
CanStartServer = true;
}
}
[RelayCommand]
private async Task StopServer()
{
CanRestartServer = false;
CanStopServer = false;
try
{
await ShutDownEndpoint();
CanStartServer = true;
await ShowMessageWithTimeLineAsync("[color yellow]调试连接已关闭");
}
catch (Exception ex)
{
await ShowMessageWithTimeLineAsync($"[color red]关闭调试连接时出现错误:{ex}");
CanStartServer = TerminalServer is null && TerminalClient is null;
CanRestartServer = !CanStartServer;
CanStopServer = !CanStartServer;
}
}
[RelayCommand]
private async Task RestartServer()
{
CanRestartServer = false;
CanStartServer = false;
CanStopServer = false;
try
{
await ShutDownEndpoint();
await ShowMessageWithTimeLineAsync("[color yellow]调试连接已关闭");
await StartConsoleEndpoint();
CanStartServer = false;
CanStopServer = true;
CanRestartServer = true;
await ShowMessageWithTimeLineAsync(ConsoleProfile.ConnectToRemoteServer
? "[color yellow]远程调试服务器重连完成"
: "[color yellow]控制台服务器重启完成");
}
catch (Exception ex)
{
await ShowMessageWithTimeLineAsync($"[color red]重启服务器时出现错误:{ex}");
CanStartServer = TerminalServer is null && TerminalClient is null;
CanRestartServer = !CanStartServer;
CanStopServer = !CanStartServer;
}
}
[RelayCommand]
private void CleanUp()
{
outputRenderer.Clear();
}
#endregion
}