using LocalhostMES.Core; using LocalhostMES.DataBase; using LocalhostMES.ViewModels.Services; using Prism.Commands; using Prism.Mvvm; using System; using System.Collections.Concurrent; using System.Collections.ObjectModel; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; namespace LocalhostMES.ViewModels.Tabs { public class TcpServerViewModel : BindableBase { private readonly IMesWorkspace _workspace; private readonly ConcurrentDictionary _clientMap = new ConcurrentDictionary(); private readonly object _workOrderSync = new object(); private HashSet _knownWorkOrderNos = new HashSet(StringComparer.OrdinalIgnoreCase); private TcpListener _listener; private CancellationTokenSource _cts; public TcpServerViewModel(IMesWorkspace workspace) { _workspace = workspace; _workspace.Initialize(); RefreshKnownWorkOrders(); MesDataChangedNotifier.Changed += OnDataChanged; } public ObservableCollection Clients { get; } = new ObservableCollection(); public ObservableCollection MessageLogs { get; } = new ObservableCollection(); private int _serverPort = 9000; public int ServerPort { get => _serverPort; set => SetProperty(ref _serverPort, value); } private bool _isRunning; public bool IsRunning { get => _isRunning; set => SetProperty(ref _isRunning, value); } private TcpClientItem _selectedClient; public TcpClientItem SelectedClient { get => _selectedClient; set => SetProperty(ref _selectedClient, value); } private string _outgoingMessage; public string OutgoingMessage { get => _outgoingMessage; set => SetProperty(ref _outgoingMessage, value); } private DelegateCommand _startServerCommand; public DelegateCommand StartServerCommand => _startServerCommand ?? (_startServerCommand = new DelegateCommand(async () => await StartServerAsync(), () => !IsRunning) .ObservesProperty(() => IsRunning)); private DelegateCommand _stopServerCommand; public DelegateCommand StopServerCommand => _stopServerCommand ?? (_stopServerCommand = new DelegateCommand(StopServer, () => IsRunning) .ObservesProperty(() => IsRunning)); private DelegateCommand _sendMessageCommand; public DelegateCommand SendMessageCommand => _sendMessageCommand ?? (_sendMessageCommand = new DelegateCommand(async () => await SendMessageAsync())); public async Task StartServerAsync() { if (ServerPort < 1 || ServerPort > 65535) { _workspace.ShowStatus("TCP端口范围应为1-65535", true); return; } try { StopServer(); _cts = new CancellationTokenSource(); _listener = new TcpListener(IPAddress.Any, ServerPort); _listener.Start(); IsRunning = true; AddLog($"TCP服务器已启动,监听端口 {ServerPort}"); _workspace.ShowStatus($"TCP服务器已启动,端口: {ServerPort}", false); await Task.Run(() => AcceptClientsLoop(_cts.Token)); } catch (Exception ex) { AddLog($"启动失败: {ex.Message}"); _workspace.ShowStatus($"TCP服务启动失败: {ex.Message}", true); IsRunning = false; } } private async Task AcceptClientsLoop(CancellationToken token) { while (!token.IsCancellationRequested && _listener != null) { TcpClient client = null; try { client = await _listener.AcceptTcpClientAsync(); } catch (ObjectDisposedException) { return; } catch (InvalidOperationException) { return; } catch (Exception ex) { AddLog($"接收客户端异常: {ex.Message}"); continue; } if (client == null) { continue; } var endpoint = client.Client.RemoteEndPoint?.ToString() ?? Guid.NewGuid().ToString("N"); var clientId = endpoint; if (_clientMap.ContainsKey(clientId)) { clientId = $"{endpoint}#{Guid.NewGuid().ToString("N").Substring(0, 4)}"; } _clientMap[clientId] = client; AddClient(new TcpClientItem { ClientId = clientId, RemoteEndPoint = endpoint }); AddLog($"客户端已连接: {endpoint}"); _ = Task.Run(() => ReceiveLoop(clientId, client, token)); } } private async Task ReceiveLoop(string clientId, TcpClient client, CancellationToken token) { var buffer = new byte[4096]; try { var stream = client.GetStream(); while (!token.IsCancellationRequested && client.Connected) { var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); if (bytesRead <= 0) { break; } var text = Encoding.UTF8.GetString(buffer, 0, bytesRead); AddLog($"[收到][{clientId}] {text}"); } } catch (ObjectDisposedException) { } catch (Exception ex) { AddLog($"[{clientId}] 接收异常: {ex.Message}"); } finally { RemoveClient(clientId); try { client.Close(); } catch { } AddLog($"客户端已断开: {clientId}"); } } private async Task SendMessageAsync() { if (SelectedClient == null) { _workspace.ShowStatus("请先选择客户端", true); return; } var message = (OutgoingMessage ?? string.Empty).Trim(); if (string.IsNullOrEmpty(message)) { _workspace.ShowStatus("请输入要发送的消息", true); return; } if (!_clientMap.TryGetValue(SelectedClient.ClientId, out var client) || client == null || !client.Connected) { _workspace.ShowStatus("客户端已离线,请重新选择", true); RemoveClient(SelectedClient.ClientId); return; } try { var bytes = Encoding.UTF8.GetBytes(message); await client.GetStream().WriteAsync(bytes, 0, bytes.Length); AddLog($"[发送][{SelectedClient.ClientId}] {message}"); OutgoingMessage = string.Empty; } catch (Exception ex) { AddLog($"发送失败: {ex.Message}"); _workspace.ShowStatus($"发送失败: {ex.Message}", true); } } private void OnDataChanged(object sender, MesDataChangedEventArgs e) { if (!e.Has(MesDataScope.WorkOrder)) { return; } if (!IsRunning || _clientMap.IsEmpty) { RefreshKnownWorkOrders(); return; } var newWorkOrders = new List(); lock (_workOrderSync) { var latest = DatabaseHelper.SelectWorkOrderInfo(null); var latestNos = new HashSet( latest.Where(w => !string.IsNullOrWhiteSpace(w.WorkOrderNo)).Select(w => w.WorkOrderNo), StringComparer.OrdinalIgnoreCase); newWorkOrders = latestNos.Except(_knownWorkOrderNos).ToList(); _knownWorkOrderNos = latestNos; } if (newWorkOrders.Count > 0) { _ = Task.Run(async () => await BroadcastOrderRefAsync(newWorkOrders)); } } private async Task BroadcastOrderRefAsync(List workOrders) { foreach (var workOrderNo in workOrders) { var payload = $"OrderRef|{workOrderNo}"; var bytes = Encoding.UTF8.GetBytes(payload); var connected = _clientMap.ToList(); foreach (var pair in connected) { var clientId = pair.Key; var client = pair.Value; if (client == null || !client.Connected) { RemoveClient(clientId); continue; } try { await client.GetStream().WriteAsync(bytes, 0, bytes.Length); AddLog($"[自动推送][{clientId}] {payload}"); } catch (Exception ex) { AddLog($"[自动推送失败][{clientId}] {ex.Message}"); } } } } private void RefreshKnownWorkOrders() { lock (_workOrderSync) { var latest = DatabaseHelper.SelectWorkOrderInfo(null); _knownWorkOrderNos = new HashSet( latest.Where(w => !string.IsNullOrWhiteSpace(w.WorkOrderNo)).Select(w => w.WorkOrderNo), StringComparer.OrdinalIgnoreCase); } } private void StopServer() { try { _cts?.Cancel(); _listener?.Stop(); } catch { } finally { _listener = null; _cts = null; } foreach (var kv in _clientMap.ToList()) { try { kv.Value.Close(); } catch { } } _clientMap.Clear(); Application.Current?.Dispatcher.Invoke(() => { Clients.Clear(); SelectedClient = null; }); if (IsRunning) { AddLog("TCP服务器已停止"); } IsRunning = false; RefreshKnownWorkOrders(); } private void AddClient(TcpClientItem item) { Application.Current?.Dispatcher.Invoke(() => { Clients.Add(item); RaisePropertyChanged(nameof(Clients)); }); } private void RemoveClient(string clientId) { _clientMap.TryRemove(clientId, out _); Application.Current?.Dispatcher.Invoke(() => { var target = Clients.FirstOrDefault(c => c.ClientId == clientId); if (target != null) { Clients.Remove(target); } if (SelectedClient != null && SelectedClient.ClientId == clientId) { SelectedClient = null; } }); } private void AddLog(string message) { var text = $"[{DateTime.Now:HH:mm:ss}] {message}"; Application.Current?.Dispatcher.Invoke(() => MessageLogs.Add(text)); } } public class TcpClientItem : BindableBase { private string _clientId; private string _remoteEndPoint; public string ClientId { get => _clientId; set => SetProperty(ref _clientId, value); } public string RemoteEndPoint { get => _remoteEndPoint; set => SetProperty(ref _remoteEndPoint, value); } } }