TcpServerViewModel.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. using LocalhostMES.Core;
  2. using LocalhostMES.DataBase;
  3. using LocalhostMES.ViewModels.Services;
  4. using Prism.Commands;
  5. using Prism.Mvvm;
  6. using System;
  7. using System.Collections.Concurrent;
  8. using System.Collections.ObjectModel;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using System.Net;
  12. using System.Net.Sockets;
  13. using System.Text;
  14. using System.Threading;
  15. using System.Threading.Tasks;
  16. using System.Windows;
  17. namespace LocalhostMES.ViewModels.Tabs
  18. {
  19. public class TcpServerViewModel : BindableBase
  20. {
  21. private readonly IMesWorkspace _workspace;
  22. private readonly ConcurrentDictionary<string, TcpClient> _clientMap = new ConcurrentDictionary<string, TcpClient>();
  23. private readonly object _workOrderSync = new object();
  24. private HashSet<string> _knownWorkOrderNos = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
  25. private TcpListener _listener;
  26. private CancellationTokenSource _cts;
  27. public TcpServerViewModel(IMesWorkspace workspace)
  28. {
  29. _workspace = workspace;
  30. _workspace.Initialize();
  31. RefreshKnownWorkOrders();
  32. MesDataChangedNotifier.Changed += OnDataChanged;
  33. }
  34. public ObservableCollection<TcpClientItem> Clients { get; } = new ObservableCollection<TcpClientItem>();
  35. public ObservableCollection<string> MessageLogs { get; } = new ObservableCollection<string>();
  36. private int _serverPort = 9000;
  37. public int ServerPort
  38. {
  39. get => _serverPort;
  40. set => SetProperty(ref _serverPort, value);
  41. }
  42. private bool _isRunning;
  43. public bool IsRunning
  44. {
  45. get => _isRunning;
  46. set => SetProperty(ref _isRunning, value);
  47. }
  48. private TcpClientItem _selectedClient;
  49. public TcpClientItem SelectedClient
  50. {
  51. get => _selectedClient;
  52. set => SetProperty(ref _selectedClient, value);
  53. }
  54. private string _outgoingMessage;
  55. public string OutgoingMessage
  56. {
  57. get => _outgoingMessage;
  58. set => SetProperty(ref _outgoingMessage, value);
  59. }
  60. private DelegateCommand _startServerCommand;
  61. public DelegateCommand StartServerCommand =>
  62. _startServerCommand ?? (_startServerCommand = new DelegateCommand(async () => await StartServerAsync(), () => !IsRunning)
  63. .ObservesProperty(() => IsRunning));
  64. private DelegateCommand _stopServerCommand;
  65. public DelegateCommand StopServerCommand =>
  66. _stopServerCommand ?? (_stopServerCommand = new DelegateCommand(StopServer, () => IsRunning)
  67. .ObservesProperty(() => IsRunning));
  68. private DelegateCommand _sendMessageCommand;
  69. public DelegateCommand SendMessageCommand =>
  70. _sendMessageCommand ?? (_sendMessageCommand = new DelegateCommand(async () => await SendMessageAsync()));
  71. public async Task StartServerAsync()
  72. {
  73. if (ServerPort < 1 || ServerPort > 65535)
  74. {
  75. _workspace.ShowStatus("TCP端口范围应为1-65535", true);
  76. return;
  77. }
  78. try
  79. {
  80. StopServer();
  81. _cts = new CancellationTokenSource();
  82. _listener = new TcpListener(IPAddress.Any, ServerPort);
  83. _listener.Start();
  84. IsRunning = true;
  85. AddLog($"TCP服务器已启动,监听端口 {ServerPort}");
  86. _workspace.ShowStatus($"TCP服务器已启动,端口: {ServerPort}", false);
  87. await Task.Run(() => AcceptClientsLoop(_cts.Token));
  88. }
  89. catch (Exception ex)
  90. {
  91. AddLog($"启动失败: {ex.Message}");
  92. _workspace.ShowStatus($"TCP服务启动失败: {ex.Message}", true);
  93. IsRunning = false;
  94. }
  95. }
  96. private async Task AcceptClientsLoop(CancellationToken token)
  97. {
  98. while (!token.IsCancellationRequested && _listener != null)
  99. {
  100. TcpClient client = null;
  101. try
  102. {
  103. client = await _listener.AcceptTcpClientAsync();
  104. }
  105. catch (ObjectDisposedException)
  106. {
  107. return;
  108. }
  109. catch (InvalidOperationException)
  110. {
  111. return;
  112. }
  113. catch (Exception ex)
  114. {
  115. AddLog($"接收客户端异常: {ex.Message}");
  116. continue;
  117. }
  118. if (client == null)
  119. {
  120. continue;
  121. }
  122. var endpoint = client.Client.RemoteEndPoint?.ToString() ?? Guid.NewGuid().ToString("N");
  123. var clientId = endpoint;
  124. if (_clientMap.ContainsKey(clientId))
  125. {
  126. clientId = $"{endpoint}#{Guid.NewGuid().ToString("N").Substring(0, 4)}";
  127. }
  128. _clientMap[clientId] = client;
  129. AddClient(new TcpClientItem { ClientId = clientId, RemoteEndPoint = endpoint });
  130. AddLog($"客户端已连接: {endpoint}");
  131. _ = Task.Run(() => ReceiveLoop(clientId, client, token));
  132. }
  133. }
  134. private async Task ReceiveLoop(string clientId, TcpClient client, CancellationToken token)
  135. {
  136. var buffer = new byte[4096];
  137. try
  138. {
  139. var stream = client.GetStream();
  140. while (!token.IsCancellationRequested && client.Connected)
  141. {
  142. var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
  143. if (bytesRead <= 0)
  144. {
  145. break;
  146. }
  147. var text = Encoding.UTF8.GetString(buffer, 0, bytesRead);
  148. AddLog($"[收到][{clientId}] {text}");
  149. }
  150. }
  151. catch (ObjectDisposedException)
  152. {
  153. }
  154. catch (Exception ex)
  155. {
  156. AddLog($"[{clientId}] 接收异常: {ex.Message}");
  157. }
  158. finally
  159. {
  160. RemoveClient(clientId);
  161. try { client.Close(); } catch { }
  162. AddLog($"客户端已断开: {clientId}");
  163. }
  164. }
  165. private async Task SendMessageAsync()
  166. {
  167. if (SelectedClient == null)
  168. {
  169. _workspace.ShowStatus("请先选择客户端", true);
  170. return;
  171. }
  172. var message = (OutgoingMessage ?? string.Empty).Trim();
  173. if (string.IsNullOrEmpty(message))
  174. {
  175. _workspace.ShowStatus("请输入要发送的消息", true);
  176. return;
  177. }
  178. if (!_clientMap.TryGetValue(SelectedClient.ClientId, out var client) || client == null || !client.Connected)
  179. {
  180. _workspace.ShowStatus("客户端已离线,请重新选择", true);
  181. RemoveClient(SelectedClient.ClientId);
  182. return;
  183. }
  184. try
  185. {
  186. var bytes = Encoding.UTF8.GetBytes(message);
  187. await client.GetStream().WriteAsync(bytes, 0, bytes.Length);
  188. AddLog($"[发送][{SelectedClient.ClientId}] {message}");
  189. OutgoingMessage = string.Empty;
  190. }
  191. catch (Exception ex)
  192. {
  193. AddLog($"发送失败: {ex.Message}");
  194. _workspace.ShowStatus($"发送失败: {ex.Message}", true);
  195. }
  196. }
  197. private void OnDataChanged(object sender, MesDataChangedEventArgs e)
  198. {
  199. if (!e.Has(MesDataScope.WorkOrder))
  200. {
  201. return;
  202. }
  203. if (!IsRunning || _clientMap.IsEmpty)
  204. {
  205. RefreshKnownWorkOrders();
  206. return;
  207. }
  208. var newWorkOrders = new List<string>();
  209. lock (_workOrderSync)
  210. {
  211. var latest = DatabaseHelper.SelectWorkOrderInfo(null);
  212. var latestNos = new HashSet<string>(
  213. latest.Where(w => !string.IsNullOrWhiteSpace(w.WorkOrderNo)).Select(w => w.WorkOrderNo),
  214. StringComparer.OrdinalIgnoreCase);
  215. newWorkOrders = latestNos.Except(_knownWorkOrderNos).ToList();
  216. _knownWorkOrderNos = latestNos;
  217. }
  218. if (newWorkOrders.Count > 0)
  219. {
  220. _ = Task.Run(async () => await BroadcastOrderRefAsync(newWorkOrders));
  221. }
  222. }
  223. private async Task BroadcastOrderRefAsync(List<string> workOrders)
  224. {
  225. foreach (var workOrderNo in workOrders)
  226. {
  227. var payload = $"OrderRef|{workOrderNo}";
  228. var bytes = Encoding.UTF8.GetBytes(payload);
  229. var connected = _clientMap.ToList();
  230. foreach (var pair in connected)
  231. {
  232. var clientId = pair.Key;
  233. var client = pair.Value;
  234. if (client == null || !client.Connected)
  235. {
  236. RemoveClient(clientId);
  237. continue;
  238. }
  239. try
  240. {
  241. await client.GetStream().WriteAsync(bytes, 0, bytes.Length);
  242. AddLog($"[自动推送][{clientId}] {payload}");
  243. }
  244. catch (Exception ex)
  245. {
  246. AddLog($"[自动推送失败][{clientId}] {ex.Message}");
  247. }
  248. }
  249. }
  250. }
  251. private void RefreshKnownWorkOrders()
  252. {
  253. lock (_workOrderSync)
  254. {
  255. var latest = DatabaseHelper.SelectWorkOrderInfo(null);
  256. _knownWorkOrderNos = new HashSet<string>(
  257. latest.Where(w => !string.IsNullOrWhiteSpace(w.WorkOrderNo)).Select(w => w.WorkOrderNo),
  258. StringComparer.OrdinalIgnoreCase);
  259. }
  260. }
  261. private void StopServer()
  262. {
  263. try
  264. {
  265. _cts?.Cancel();
  266. _listener?.Stop();
  267. }
  268. catch
  269. {
  270. }
  271. finally
  272. {
  273. _listener = null;
  274. _cts = null;
  275. }
  276. foreach (var kv in _clientMap.ToList())
  277. {
  278. try { kv.Value.Close(); } catch { }
  279. }
  280. _clientMap.Clear();
  281. Application.Current?.Dispatcher.Invoke(() =>
  282. {
  283. Clients.Clear();
  284. SelectedClient = null;
  285. });
  286. if (IsRunning)
  287. {
  288. AddLog("TCP服务器已停止");
  289. }
  290. IsRunning = false;
  291. RefreshKnownWorkOrders();
  292. }
  293. private void AddClient(TcpClientItem item)
  294. {
  295. Application.Current?.Dispatcher.Invoke(() =>
  296. {
  297. Clients.Add(item);
  298. RaisePropertyChanged(nameof(Clients));
  299. });
  300. }
  301. private void RemoveClient(string clientId)
  302. {
  303. _clientMap.TryRemove(clientId, out _);
  304. Application.Current?.Dispatcher.Invoke(() =>
  305. {
  306. var target = Clients.FirstOrDefault(c => c.ClientId == clientId);
  307. if (target != null)
  308. {
  309. Clients.Remove(target);
  310. }
  311. if (SelectedClient != null && SelectedClient.ClientId == clientId)
  312. {
  313. SelectedClient = null;
  314. }
  315. });
  316. }
  317. private void AddLog(string message)
  318. {
  319. var text = $"[{DateTime.Now:HH:mm:ss}] {message}";
  320. Application.Current?.Dispatcher.Invoke(() => MessageLogs.Add(text));
  321. }
  322. }
  323. public class TcpClientItem : BindableBase
  324. {
  325. private string _clientId;
  326. private string _remoteEndPoint;
  327. public string ClientId
  328. {
  329. get => _clientId;
  330. set => SetProperty(ref _clientId, value);
  331. }
  332. public string RemoteEndPoint
  333. {
  334. get => _remoteEndPoint;
  335. set => SetProperty(ref _remoteEndPoint, value);
  336. }
  337. }
  338. }