| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- 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<string, TcpClient> _clientMap = new ConcurrentDictionary<string, TcpClient>();
- private readonly object _workOrderSync = new object();
- private HashSet<string> _knownWorkOrderNos = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
- private TcpListener _listener;
- private CancellationTokenSource _cts;
- public TcpServerViewModel(IMesWorkspace workspace)
- {
- _workspace = workspace;
- _workspace.Initialize();
- RefreshKnownWorkOrders();
- MesDataChangedNotifier.Changed += OnDataChanged;
- }
- public ObservableCollection<TcpClientItem> Clients { get; } = new ObservableCollection<TcpClientItem>();
- public ObservableCollection<string> MessageLogs { get; } = new ObservableCollection<string>();
- 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<string>();
- lock (_workOrderSync)
- {
- var latest = DatabaseHelper.SelectWorkOrderInfo(null);
- var latestNos = new HashSet<string>(
- 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<string> 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<string>(
- 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);
- }
- }
- }
|