using System; using System.Collections.ObjectModel; using System.IO; using System.Text; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Sln.Wcs.UI.Services; namespace Sln.Wcs.UI.ViewModels; public partial class SystemMonitorViewModel : ObservableObject { private readonly IEngineProcessService _hoistEngine; private readonly IAgvEngineProcessService _agvEngine; [ObservableProperty] private ObservableCollection _logs = new(); [ObservableProperty] private int _logCount; [ObservableProperty] private bool _autoScroll = true; // ---- Hoist ---- [ObservableProperty] private bool _isHoistRunning; [ObservableProperty] private string _hoistStatusText = "已停止"; public string HoistStatusColor => IsHoistRunning ? "#00E676" : "#FF5252"; // ---- AGV ---- [ObservableProperty] private bool _isAgvRunning; [ObservableProperty] private string _agvStatusText = "已停止"; public string AgvStatusColor => IsAgvRunning ? "#00E676" : "#FF5252"; private const int MaxLogs = 5000; public SystemMonitorViewModel(IEngineProcessService hoistEngine, IAgvEngineProcessService agvEngine) { _hoistEngine = hoistEngine; _agvEngine = agvEngine; _hoistEngine.StateChanged += () => RefreshHoist(); _hoistEngine.OutputReceived += msg => AddLog($"[提升机] {msg}"); _agvEngine.StateChanged += () => RefreshAgv(); _agvEngine.OutputReceived += msg => AddLog($"[AGV] {msg}"); StartHoistCommand = new AsyncRelayCommand(StartHoistAsync, () => !IsHoistRunning); StopHoistCommand = new RelayCommand(StopHoist, () => IsHoistRunning); StartAgvCommand = new AsyncRelayCommand(StartAgvAsync, () => !IsAgvRunning); StopAgvCommand = new RelayCommand(StopAgv, () => IsAgvRunning); var original = Console.Out; var writer = new LogTextWriter(original, entry => { Avalonia.Threading.Dispatcher.UIThread.Post(() => { Logs.Add(entry); if (Logs.Count > MaxLogs) Logs.RemoveAt(0); LogCount = Logs.Count; }); }); Console.SetOut(writer); } private void RefreshHoist() { Avalonia.Threading.Dispatcher.UIThread.Post(() => { IsHoistRunning = _hoistEngine.IsRunning; HoistStatusText = _hoistEngine.StatusText; OnPropertyChanged(nameof(HoistStatusColor)); StartHoistCommand.NotifyCanExecuteChanged(); StopHoistCommand.NotifyCanExecuteChanged(); }); } private void RefreshAgv() { Avalonia.Threading.Dispatcher.UIThread.Post(() => { IsAgvRunning = _agvEngine.IsRunning; AgvStatusText = _agvEngine.StatusText; OnPropertyChanged(nameof(AgvStatusColor)); StartAgvCommand.NotifyCanExecuteChanged(); StopAgvCommand.NotifyCanExecuteChanged(); }); } // ---- Hoist Commands ---- public IAsyncRelayCommand StartHoistCommand { get; } public IRelayCommand StopHoistCommand { get; } private async System.Threading.Tasks.Task StartHoistAsync() { try { AddLog("正在启动 HoistServer..."); await _hoistEngine.StartAsync(); AddLog("HoistServer 启动成功"); } catch (Exception ex) { AddLog($"Hoist 启动失败: {ex.Message}", "ERROR"); if (_hoistEngine.LastError != null) AddLog(_hoistEngine.LastError, "ERROR"); } } private void StopHoist() { _hoistEngine.Stop(); AddLog("HoistServer 已停止"); } // ---- AGV Commands ---- public IAsyncRelayCommand StartAgvCommand { get; } public IRelayCommand StopAgvCommand { get; } private async System.Threading.Tasks.Task StartAgvAsync() { try { AddLog("正在启动 HikRoBotServer..."); await _agvEngine.StartAsync(); AddLog("HikRoBotServer 启动成功"); } catch (Exception ex) { AddLog($"AGV 启动失败: {ex.Message}", "ERROR"); if (_agvEngine.LastError != null) AddLog(_agvEngine.LastError, "ERROR"); } } private void StopAgv() { _agvEngine.Stop(); AddLog("HikRoBotServer 已停止"); } private void AddLog(string msg, string level = "INFO") { Logs.Add(new LogEntry { Time = DateTime.Now, Message = msg, Level = level }); if (Logs.Count > MaxLogs) Logs.RemoveAt(0); LogCount = Logs.Count; } [RelayCommand] private void Clear() { Logs.Clear(); LogCount = 0; } [RelayCommand] private void ToggleAutoScroll() { AutoScroll = !AutoScroll; } } public class LogEntry { public DateTime Time { get; set; } public string TimeText => Time.ToString("HH:mm:ss.fff"); public string Message { get; set; } = string.Empty; public string Level { get; set; } = "INFO"; } internal class LogTextWriter : TextWriter { private readonly TextWriter _original; private readonly Action _onWrite; private readonly StringBuilder _buffer = new(); public LogTextWriter(TextWriter o, Action cb) { _original = o; _onWrite = cb; } public override Encoding Encoding => Encoding.UTF8; public override void Write(char v) { _original.Write(v); if (v == '\n') Flush(); else if (v != '\r') _buffer.Append(v); } public override void WriteLine(string? m) { _original.WriteLine(m); if (m != null) _onWrite(new LogEntry { Time = DateTime.Now, Message = m, Level = "INFO" }); } private void Flush() { var m = _buffer.ToString(); _buffer.Clear(); if (m.Length > 0) _onWrite(new LogEntry { Time = DateTime.Now, Message = m, Level = "INFO" }); } }