From 559890c482f5973f06aafdfec9550fee5ba3c7f2 Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 09 十一月 2026 17:02:15 +0800
Subject: [PATCH] feat: CAN 模拟器 TCP 化 + 多设备并发调试 + 后台生命周期管理
---
CanSimulator/SimulatorFraming.cs | 86 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 86 insertions(+), 0 deletions(-)
diff --git a/CanSimulator/SimulatorFraming.cs b/CanSimulator/SimulatorFraming.cs
new file mode 100644
index 0000000..4f3336b
--- /dev/null
+++ b/CanSimulator/SimulatorFraming.cs
@@ -0,0 +1,86 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace CanSimulator
+{
+ /// <summary>
+ /// CAN 妯℃嫙鍣� TCP 绾垮崗璁垎甯х紪瑙g爜鍣ㄣ��
+ /// 绾挎牸寮忥細4 瀛楄妭澶х闀垮害鍓嶇紑 + UTF8 鏂囨湰琛岋紙涓� BrokerIpc 鍚屾儻鐢ㄦ硶锛夈��
+ /// 璇讳晶璐熻矗绮樺寘/鍗婂寘閲嶇粍锛氬惊鐜婊¢暱搴﹀墠缂�涓庡抚浣撳悗杩斿洖瀹屾暣鏂囨湰銆�
+ /// </summary>
+ public static class SimulatorFraming
+ {
+ /// <summary>闀垮害鍓嶇紑瀛楄妭鏁�</summary>
+ public const int HeaderSize = 4;
+
+ /// <summary>鍗曞抚鏈�澶у瓧鑺傛暟锛堥槻鐣稿舰闀垮害鍓嶇紑瀵艰嚧鍐呭瓨鐖嗘帀锛涘懡浠や覆涓� LOG 鍝嶅簲杩滃皬浜庢鍊硷級</summary>
+ public const int MaxFrameBytes = 1024 * 1024;
+
+ /// <summary>鎶婁竴鏉℃枃鏈秷鎭紪鐮佷负瀹屾暣甯э紙鍚暱搴﹀墠缂�锛�</summary>
+ public static byte[] Encode(string text)
+ {
+ if (text == null) throw new ArgumentNullException(nameof(text));
+ byte[] body = Encoding.UTF8.GetBytes(text);
+ if (body.Length > MaxFrameBytes)
+ throw new InvalidOperationException($"甯т綋瓒呴檺: {body.Length} > {MaxFrameBytes}");
+ var frame = new byte[HeaderSize + body.Length];
+ WriteBigEndian(frame, 0, body.Length);
+ Buffer.BlockCopy(body, 0, frame, HeaderSize, body.Length);
+ return frame;
+ }
+
+ /// <summary>瑙g爜宸插幓鎺夐暱搴﹀墠缂�鐨勫抚浣�</summary>
+ public static string Decode(byte[] buffer, int offset, int count)
+ => Encoding.UTF8.GetString(buffer, offset, count);
+
+ /// <summary>
+ /// 浠庢祦涓鍙栦竴涓畬鏁村抚銆�
+ /// 杩斿洖 null 琛ㄧず瀵圭宸插叧闂垨甯т腑閫� EOF锛堣繛鎺ュけ鏁堬紝璋冪敤鏂瑰簲瑙﹀彂閲嶈繛锛夛紱
+ /// 闀垮害鍓嶇紑闈炴硶锛堚墹0 鎴栬秴 MaxFrameBytes锛夋姏 InvalidDataException锛堝崗璁け姝ワ級銆�
+ /// </summary>
+ public static async Task<string?> ReadFrameAsync(Stream s, CancellationToken ct)
+ {
+ var header = new byte[HeaderSize];
+ if (!await ReadExactAsync(s, header, HeaderSize, ct).ConfigureAwait(false))
+ return null;
+ int len = ReadBigEndian(header, 0);
+ if (len <= 0 || len > MaxFrameBytes)
+ throw new InvalidDataException($"闈炴硶甯ч暱搴﹀墠缂�: {len}");
+
+ var body = new byte[len];
+ if (!await ReadExactAsync(s, body, len, ct).ConfigureAwait(false))
+ return null; // 甯т綋涓�� EOF
+ return Decode(body, 0, len);
+ }
+
+ /// <summary>寰幆璇绘弧 count 瀛楄妭锛涙湡闂� EOF锛堣鍒� 0 瀛楄妭锛夋垨 IO 寮傚父鏃惰繑鍥� false锛岀敱璋冪敤鏂规寜杩炴帴澶辨晥澶勭悊</summary>
+ private static async Task<bool> ReadExactAsync(Stream s, byte[] buf, int count, CancellationToken ct)
+ {
+ int off = 0;
+ while (off < count)
+ {
+ int n;
+ try { n = await s.ReadAsync(buf, off, count - off, ct).ConfigureAwait(false); }
+ catch (IOException) { return false; }
+ if (n <= 0) return false;
+ off += n;
+ }
+ return true;
+ }
+
+ // net472 鏃� System.Buffers.Binary锛屾墜鍐欏ぇ绔鍐�
+ private static void WriteBigEndian(byte[] buf, int offset, int value)
+ {
+ buf[offset] = (byte)(value >> 24);
+ buf[offset + 1] = (byte)(value >> 16);
+ buf[offset + 2] = (byte)(value >> 8);
+ buf[offset + 3] = (byte)value;
+ }
+
+ private static int ReadBigEndian(byte[] buf, int offset)
+ => (buf[offset] << 24) | (buf[offset + 1] << 16) | (buf[offset + 2] << 8) | buf[offset + 3];
+ }
+}
--
Gitblit v1.9.1