From ce1292c9cf37195b6abd2699dfc5d6cb3e143c9b Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期日, 12 四月 2026 23:38:19 +0800
Subject: [PATCH] feat(MES): 添加MES接口相关实体和DTO JS扩展文件至JSX格式并更新配置
---
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs | 139 ++++++++++++++++++++++++++++++----------------
1 files changed, 90 insertions(+), 49 deletions(-)
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs
index a0b8a91..571cd45 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/SocketServer/TcpSocketServer.Messaging.cs
@@ -8,16 +8,21 @@
public partial class TcpSocketServer
{
/// <summary>
- /// 异步处理与已连接的TCP客户端的通信,处理机器人起重机会话中的传入消息和客户端状态更新。
+ /// 澶勭悊瀹㈡埛绔繛鎺ョ殑娑堟伅寰幆
/// </summary>
- /// <remarks>此方法管理客户端连接的生命周期,包括读取消息、更新客户端状态和调用相关事件。
- /// 当处理结束时,客户端和相关的网络资源将被释放。如果启用心跳或空闲超时选项,
- /// 将应用额外的取消逻辑。事件调用期间的异常将被捕获并抑制,以确保会话处理的鲁棒性。</remarks>
- /// <param name="client">表示要处理的远程连接的TCP客户端。方法完成后将释放此对象。</param>
- /// <param name="clientId">已连接客户端的唯一标识符。用于在整个会话中跟踪和更新客户端状态。</param>
- /// <param name="cancellationToken">可用于取消客户端处理操作的取消令牌。如果请求取消,方法将立即终止处理。</param>
- /// <param name="robotCrane">表示与客户端关联的机器人起重机的当前状态对象。用于为消息处理和事件调用提供上下文。</param>
- /// <returns>表示处理客户端连接的异步操作的任务。当客户端断开连接或请求取消时任务完成。</returns>
+ /// <remarks>
+ /// 鎸佺画鎺ユ敹瀹㈡埛绔秷鎭紝鐩村埌杩炴帴鏂紑鎴栧彇娑堛��
+ /// 澶勭悊娴佺▼锛�
+ /// 1. 鎺ユ敹娑堟伅锛堝抚瑙f瀽锛�
+ /// 2. 鏇存柊瀹㈡埛绔姸鎬侊紙娲昏穬鏃堕棿銆佺紪鐮侊級
+ /// 3. 澶勭悊璁惧娉ㄥ唽
+ /// 4. 瑙﹀彂 MessageReceived 浜嬩欢
+ /// 杩炴帴鏂紑鏃舵竻鐞嗚祫婧愬苟瑙﹀彂 RobotReceived 浜嬩欢銆�
+ /// </remarks>
+ /// <param name="client">TCP 瀹㈡埛绔繛鎺�</param>
+ /// <param name="clientId">瀹㈡埛绔敮涓�鏍囪瘑</param>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
+ /// <param name="robotCrane">鏈哄櫒浜虹姸鎬�</param>
public async Task HandleClientAsync(TcpClient client, string clientId, CancellationToken cancellationToken, RobotSocketState robotCrane)
{
using (client)
@@ -28,19 +33,21 @@
CancellationTokenSource? localCts = null;
if (_options.EnableHeartbeat || _options.IdleTimeoutSeconds > 0)
{
+ // 鍒涘缓閾炬帴鐨勫彇娑堜护鐗屾簮
localCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
}
try
{
+ // 娑堟伅鎺ユ敹寰幆
while (!cancellationToken.IsCancellationRequested && client.Connected)
{
string? message;
try
{
var ct = localCts?.Token ?? cancellationToken;
+ // 鎺ユ敹瀹屾暣娑堟伅锛堝抚瑙f瀽锛�
message = await ReceiveFullMessageAsync(networkStream, _textEncoding, ct);
- //message = await reader.ReadLineAsync().WaitAsync(ct);
}
catch (OperationCanceledException)
{
@@ -52,20 +59,23 @@
break;
}
+ // 鏇存柊瀹㈡埛绔姸鎬�
UpdateClientStatus(clientId, message);
string messageLower = message.ToLowerInvariant();
+ // 澶勭悊娉ㄥ唽娑堟伅
if (TryHandleRegister(messageLower, message, clientId, networkStream, cancellationToken))
{
continue;
}
+ // 瑙﹀彂娑堟伅鎺ユ敹浜嬩欢
if (MessageReceived != null)
{
try
{
- // 判断是否为 JSON 格式
+ // 鍒ゆ柇鏄惁涓� JSON 鏍煎紡
bool isJsonFormat = TryParseJsonSilent(message);
_ = MessageReceived.Invoke(message, isJsonFormat, client, robotCrane);
}
@@ -75,6 +85,7 @@
}
finally
{
+ // 娓呯悊璧勬簮
try { localCts?.Cancel(); localCts?.Dispose(); } catch { }
RemoveClient(clientId);
try { _ = RobotReceived.Invoke(clientId); } catch { }
@@ -83,17 +94,18 @@
}
/// <summary>
- /// 尝试处理来自客户端的设备注册请求。返回一个值指示该消息是否被作为注册请求处理。
+ /// 澶勭悊璁惧娉ㄥ唽娑堟伅
/// </summary>
- /// <remarks>如果消息是有效的注册请求且包含非空的设备标识符,
- /// 则将设备绑定到客户端并发送确认信息。此方法不会因无效消息而抛出异常;
- /// 它仅返回 false。</remarks>
- /// <param name="messageLower">客户端消息的小写版本,用于判断消息是否为注册请求。</param>
- /// <param name="message">包含注册命令和设备标识符的原始客户端消息。</param>
- /// <param name="clientId">发送注册请求的客户端的唯一标识符。</param>
- /// <param name="client">与客户端通信的TCP客户端连接。</param>
- /// <param name="cancellationToken">可用于取消注册操作的取消令牌。</param>
- /// <returns>如果消息被识别并作为注册请求处理,则返回 true;否则返回 false。</returns>
+ /// <remarks>
+ /// 娉ㄥ唽娑堟伅鏍煎紡锛歳egister,{deviceId}
+ /// 灏嗚澶� ID 缁戝畾鍒板綋鍓嶅鎴风 ID銆�
+ /// </remarks>
+ /// <param name="messageLower">娑堟伅灏忓啓鐗堟湰</param>
+ /// <param name="message">鍘熷娑堟伅</param>
+ /// <param name="clientId">瀹㈡埛绔� ID</param>
+ /// <param name="networkStream">缃戠粶娴�</param>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
+ /// <returns>鏄惁澶勭悊浜嗘敞鍐屾秷鎭�</returns>
private bool TryHandleRegister(string messageLower, string message, string clientId, NetworkStream networkStream, CancellationToken cancellationToken)
{
if (!messageLower.StartsWith("register,"))
@@ -101,14 +113,17 @@
return false;
}
+ // 鎻愬彇璁惧 ID
string deviceId = message.Substring("register,".Length).Trim();
if (!string.IsNullOrEmpty(deviceId))
{
lock (_syncRoot)
{
+ // 缁戝畾璁惧鍒板鎴风
_deviceBindings[deviceId] = clientId;
}
+ // 鍥炲娉ㄥ唽鎴愬姛
_ = WriteToClientAsync(clientId, networkStream, $"Registered,{deviceId}", cancellationToken);
}
@@ -116,20 +131,27 @@
}
/// <summary>
- /// 更新客户端状态
+ /// 鏇存柊瀹㈡埛绔姸鎬�
/// </summary>
- /// <param name="clientId"></param>
- /// <param name="message"></param>
+ /// <remarks>
+ /// 鏇存柊鏈�鍚庢椿璺冩椂闂村拰瀛楃缂栫爜銆�
+ /// 濡傛灉寮�鍚簡鑷姩缂栫爜妫�娴嬶紝鏍规嵁娑堟伅鍐呭鍒ゆ柇鏄� UTF-8 杩樻槸 GBK銆�
+ /// </remarks>
+ /// <param name="clientId">瀹㈡埛绔� ID</param>
+ /// <param name="message">鏈�鏂版帴鏀剁殑娑堟伅</param>
private void UpdateClientStatus(string clientId, string message)
{
lock (_syncRoot)
{
+ // 鏇存柊鏈�鍚庢椿璺冩椂闂�
_clientLastActive[clientId] = DateTime.Now;
+ // 濡傛灉杩樻病鏈夎褰曠紪鐮�
if (!_clientEncodings.ContainsKey(clientId))
{
if (_options.AutoDetectEncoding && _autoDetectedGb2312 != null)
{
+ // 鑷姩妫�娴嬬紪鐮侊細JSON 鎴� UTF-8 瀛楄妭鐗瑰緛鍒欑敤 UTF-8锛屽惁鍒欑敤 GBK
bool isUtf8 = TryParseJsonSilent(message) || IsLikelyUtf8(_textEncoding.GetBytes(message));
_clientEncodings[clientId] = isUtf8 ? _textEncoding : _autoDetectedGb2312;
}
@@ -142,13 +164,11 @@
}
/// <summary>
- /// 写入消息到客户端
+ /// 寮傛鍙戦�佹秷鎭埌瀹㈡埛绔�
/// </summary>
- /// <param name="clientId"></param>
- /// <param name="networkStream"></param>
- /// <param name="message"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
+ /// <remarks>
+ /// 鍐呴儴鏂规硶锛屼笉浣跨敤甯ф牸寮忥紝鐩存帴鍙戦�佸師濮嬫秷鎭��
+ /// </remarks>
private async Task WriteToClientAsync(string clientId, NetworkStream networkStream, string message, CancellationToken cancellationToken)
{
SemaphoreSlim? sem = null;
@@ -175,10 +195,13 @@
}
/// <summary>
- /// 添加消息帧头尾
+ /// 鏋勫缓甯ф秷鎭�
/// </summary>
- /// <param name="message"></param>
- /// <returns></returns>
+ /// <remarks>
+ /// 鍦ㄦ秷鎭墠鍚庢坊鍔犲ご灏炬爣璇嗐��
+ /// </remarks>
+ /// <param name="message">鍘熷娑堟伅</param>
+ /// <returns>甯﹀抚鏍囪瘑鐨勬秷鎭�</returns>
private string BuildFramedMessage(string message)
{
var header = _options.MessageHeader ?? string.Empty;
@@ -187,10 +210,14 @@
}
/// <summary>
- /// JSON格式尝试解析(静默失败)
+ /// 闈欓粯灏濊瘯瑙f瀽 JSON
/// </summary>
- /// <param name="message"></param>
- /// <returns></returns>
+ /// <remarks>
+ /// 鍒ゆ柇娑堟伅鏄惁浠� { 鎴� [ 寮�澶达紝濡傛灉鏄垯灏濊瘯瑙f瀽銆�
+ /// 瑙f瀽澶辫触涓嶆姏寮傚父銆�
+ /// </remarks>
+ /// <param name="message">娑堟伅鍐呭</param>
+ /// <returns>鏄惁鏄湁鏁堢殑 JSON 鏍煎紡</returns>
private static bool TryParseJsonSilent(string message)
{
if (string.IsNullOrWhiteSpace(message)) return false;
@@ -200,30 +227,35 @@
}
/// <summary>
- /// utf-8 可能性检测
+ /// 鍒ゆ柇瀛楄妭鏁扮粍鏄惁涓� UTF-8 缂栫爜
/// </summary>
- /// <param name="data"></param>
- /// <returns></returns>
+ /// <remarks>
+ /// 閫氳繃妫�鏌ュ瓧鑺傚簭鍒楁槸鍚︾鍚� UTF-8 澶氬瓧鑺傚瓧绗︾殑缂栫爜瑙勫垯銆�
+ /// </remarks>
+ /// <param name="data">瀛楄妭鏁扮粍</param>
+ /// <returns>鏄惁鍙兘鏄� UTF-8 缂栫爜</returns>
private static bool IsLikelyUtf8(byte[] data)
{
int i = 0;
while (i < data.Length)
{
byte b = data[i];
- if (b <= 0x7F) { i++; continue; }
- if (b >= 0xC2 && b <= 0xDF)
+ if (b <= 0x7F) { i++; continue; } // ASCII 瀛楃
+
+ // 妫�鏌ュ瀛楄妭瀛楃
+ if (b >= 0xC2 && b <= 0xDF) // 2瀛楄妭瀛楃
{
if (i + 1 >= data.Length) return false;
if ((data[i + 1] & 0xC0) != 0x80) return false;
i += 2; continue;
}
- if (b >= 0xE0 && b <= 0xEF)
+ if (b >= 0xE0 && b <= 0xEF) // 3瀛楄妭瀛楃
{
if (i + 2 >= data.Length) return false;
if ((data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80) return false;
i += 3; continue;
}
- if (b >= 0xF0 && b <= 0xF4)
+ if (b >= 0xF0 && b <= 0xF4) // 4瀛楄妭瀛楃
{
if (i + 3 >= data.Length) return false;
if ((data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80 || (data[i + 3] & 0xC0) != 0x80) return false;
@@ -235,12 +267,16 @@
}
/// <summary>
- /// 读取完整消息
+ /// 鎺ユ敹瀹屾暣娑堟伅锛堝抚瑙f瀽锛�
/// </summary>
- /// <param name="networkStream">字节流</param>
- /// <param name="encoding">编码格式</param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
+ /// <remarks>
+ /// 鏍规嵁閰嶇疆鐨勫ご灏炬爣璇嗚В鏋愭秷鎭��
+ /// 濡傛灉鏈厤缃ご灏撅紝鍒欎竴鐩磋鍒版暟鎹笉鍙敤銆�
+ /// </remarks>
+ /// <param name="networkStream">缃戠粶娴�</param>
+ /// <param name="encoding">瀛楃缂栫爜</param>
+ /// <param name="cancellationToken">鍙栨秷浠ょ墝</param>
+ /// <returns>鎺ユ敹鍒扮殑娑堟伅</returns>
private async Task<string?> ReceiveFullMessageAsync(NetworkStream networkStream, Encoding encoding, CancellationToken cancellationToken)
{
var header = _options.MessageHeader ?? string.Empty;
@@ -251,15 +287,18 @@
while (true)
{
+ // 璇诲彇鏁版嵁
int bytesRead = await networkStream.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken);
if (bytesRead <= 0)
{
if (builder.Length == 0) return null;
+ // 鏃犲ご灏鹃厤缃椂锛岃繑鍥炲凡鏈夋暟鎹�
return string.IsNullOrEmpty(header) && string.IsNullOrEmpty(footer) ? builder.ToString() : null;
}
builder.Append(encoding.GetString(buffer, 0, bytesRead));
+ // 濡傛灉娌℃湁閰嶇疆澶村熬锛屼笖鏁版嵁涓嶅彲鐢紝杩斿洖宸叉湁鏁版嵁
if (string.IsNullOrEmpty(header) && string.IsNullOrEmpty(footer))
{
if (!networkStream.DataAvailable)
@@ -269,6 +308,7 @@
continue;
}
+ // 鏌ユ壘甯уご
var data = builder.ToString();
var headerIndex = string.IsNullOrEmpty(header) ? 0 : data.IndexOf(header, StringComparison.Ordinal);
if (headerIndex < 0)
@@ -276,6 +316,7 @@
continue;
}
+ // 鎻愬彇甯у唴瀹�
var startIndex = headerIndex + header.Length;
var footerIndex = string.IsNullOrEmpty(footer) ? data.Length : data.IndexOf(footer, startIndex, StringComparison.Ordinal);
if (footerIndex >= 0)
@@ -287,4 +328,4 @@
return builder.ToString();
}
}
-}
\ No newline at end of file
+}
--
Gitblit v1.9.3