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 化 + 多设备并发调试 + 后台生命周期管理
---
CanSimulatorServer/CanSimulatorServer.Core/CanCommandExecutor.cs | 188 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 188 insertions(+), 0 deletions(-)
diff --git a/CanSimulatorServer/CanSimulatorServer.Core/CanCommandExecutor.cs b/CanSimulatorServer/CanSimulatorServer.Core/CanCommandExecutor.cs
new file mode 100644
index 0000000..0f2015c
--- /dev/null
+++ b/CanSimulatorServer/CanSimulatorServer.Core/CanCommandExecutor.cs
@@ -0,0 +1,188 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using CanSimulator;
+
+namespace CanSimulatorServer
+{
+ /// <summary>
+ /// 鏈嶅姟绔懡浠や覆鎵ц鍣細瀵规寕杞借妭鐐规墽琛� SEND/QUERY 鍛戒护锛岃涔変笌鍘� CanDriverAdapter
+ /// 淇濇寔涓�鑷达紙鍛戒护涓茬洿浼犲崗璁殑鏍稿績锛夈�傝繑鍥炲�肩害瀹氾細
+ /// OK 鍓嶇紑 = 鎴愬姛鍝嶅簲锛汦RR 鍓嶇紑 = 涓氬姟澶辫触锛涘叾浣� = 瑁告暟鎹紙鐢变細璇濆眰琛� OK: 鍓嶇紑锛夈��
+ /// </summary>
+ public sealed class CanCommandExecutor
+ {
+ private readonly CanNode _node;
+ private readonly CanBusSimulator _bus;
+
+ public CanCommandExecutor(CanNode node, CanBusSimulator bus)
+ {
+ _node = node ?? throw new ArgumentNullException(nameof(node));
+ _bus = bus ?? throw new ArgumentNullException(nameof(bus));
+ }
+
+ /// <summary>鎵ц涓�琛屽懡浠わ紙SEND:鈥� 鎴� QUERY:鈥︼級锛屽紓甯歌浆涓� ERR 鍝嶅簲锛屼笉鎶涘嚭銆�</summary>
+ public string Execute(string line)
+ {
+ try
+ {
+ if (line.StartsWith("SEND:", StringComparison.OrdinalIgnoreCase))
+ return HandleSend(line);
+
+ if (line.StartsWith("QUERY:", StringComparison.OrdinalIgnoreCase))
+ {
+ string query = line.Substring("QUERY:".Length);
+ return HandleQuery(query);
+ }
+
+ return $"ERR:UNKNOWN_CMD:{line}";
+ }
+ catch (ArgumentException ex)
+ {
+ return $"ERR:BAD_FRAME:{ex.Message}";
+ }
+ catch (Exception ex)
+ {
+ return $"ERR:EXEC:{ex.Message}";
+ }
+ }
+
+ private string HandleSend(string line)
+ {
+ // SEND:frameId:data锛坉ata 鍐呭彲鍚┖鏍硷紝涓嶅弬涓庡垎娈碉級
+ var parts = line.Split(':');
+ if (parts.Length < 3)
+ throw new ArgumentException("SEND 鏍煎紡搴斾负 SEND:frameId:data");
+
+ uint frameId = CanFrame.ParseFrameId(parts[1]);
+ byte[] data = CanFrame.ParseData(string.Join(":", parts.Skip(2)));
+ if (data.Length > 8)
+ throw new ArgumentException($"鏁版嵁闀垮害瓒呴檺: {data.Length} 瀛楄妭锛堟渶澶� 8锛�");
+
+ var frame = new CanFrame
+ {
+ FrameId = frameId,
+ Format = frameId > 0x7FF ? CanFrameFormat.Extended : CanFrameFormat.Standard,
+ Type = CanFrameType.Data,
+ Data = data
+ };
+ _node.SendFrame(frame); // 瑙﹀彂鎬荤嚎璺敱锛堝叾瀹冭妭鐐规敹甯� 鈫� 鍚勮嚜鎺ㄩ�侊級涓庤嚜鍔ㄥ搷搴�
+ return $"OK:TX:{frame.FrameIdHex}:{frame.Dlc}";
+ }
+
+ private string HandleQuery(string query)
+ {
+ var parts = query.Split(':');
+ string op = parts[0].ToUpperInvariant();
+
+ switch (op)
+ {
+ case "RECV":
+ {
+ if (_node.TryDequeueReceived(out var f) && f != null)
+ return $"OK:RECV:{f.FrameIdHex}:{f.DataHex}:{f.Timestamp:HH:mm:ss.fff}";
+ return "OK:RECV:EMPTY";
+ }
+ case "RECV_ALL":
+ {
+ var mailbox = _node.PeekReceiveMailbox();
+ _node.ClearReceiveMailbox();
+ if (mailbox.Count == 0) return "OK:RECV_ALL:0";
+ var items = mailbox.Select(f => $"{f.FrameIdHex}:{f.DataHex}:{f.Timestamp:HH:mm:ss.fff}");
+ return $"OK:RECV_ALL:{mailbox.Count}:{string.Join("|", items)}";
+ }
+ case "STATUS":
+ return "OK:STATUS:" + DescribeStatus();
+ case "FILTER":
+ {
+ if (parts.Length < 3)
+ throw new ArgumentException("FILTER 鏍煎紡搴斾负 FILTER:id:mask");
+ uint fid = CanFrame.ParseFrameId(parts[1]);
+ uint mask = CanFrame.ParseFrameId(parts[2]);
+ _node.AddFilter(new AcceptanceFilter
+ {
+ FilterId = fid,
+ Mask = mask,
+ IsExtended = fid > 0x7FF
+ });
+ return $"OK:婊ゆ尝鍣ㄥ凡娣诲姞 ID=0x{fid:X3}, Mask=0x{mask:X3}";
+ }
+ case "FILTER_CLEAR":
+ _node.ClearFilters();
+ return "OK:婊ゆ尝鍣ㄥ凡娓呴櫎";
+ case "PROMISCUOUS":
+ _node.SetPromiscuousMode();
+ return "OK:娣锋潅妯″紡宸插惎鐢�";
+ case "BUS_ECHO":
+ {
+ if (parts.Length < 2)
+ return $"OK:BUS_ECHO:{(_bus.EchoResponderToSender ? "ON" : "OFF")}";
+ string mode = parts[1].Trim().ToUpperInvariant();
+ switch (mode)
+ {
+ case "ON": case "1": case "TRUE": case "ENABLE":
+ _bus.EchoResponderToSender = true;
+ return "OK:鎬荤嚎搴旂瓟鍣ㄥ凡寮�鍚紙鍥炴樉缁欏彂閫佽�咃級";
+ case "OFF": case "0": case "FALSE": case "DISABLE":
+ _bus.EchoResponderToSender = false;
+ return "OK:鎬荤嚎搴旂瓟鍣ㄥ凡鍏抽棴";
+ default:
+ throw new ArgumentException($"BUS_ECHO 鍙傛暟閿欒: {mode}");
+ }
+ }
+ case "AUTO_RESPONSE":
+ {
+ if (parts.Length < 2)
+ return $"OK:AUTO_RESPONSE:{(_node.AutoResponseEnabled ? "ON" : "OFF")}";
+ string mode = parts[1].Trim().ToUpperInvariant();
+ switch (mode)
+ {
+ case "ON": case "1": case "TRUE": case "ENABLE":
+ _node.AutoResponseEnabled = true;
+ return "OK:鑷姩鍝嶅簲妯″紡宸插惎鐢�";
+ case "OFF": case "0": case "FALSE": case "DISABLE":
+ _node.AutoResponseEnabled = false;
+ return "OK:鑷姩鍝嶅簲妯″紡宸茬鐢�";
+ default:
+ throw new ArgumentException($"AUTO_RESPONSE 鍙傛暟閿欒: {mode}");
+ }
+ }
+ case "RESPONSE_DELAY":
+ {
+ if (parts.Length < 2)
+ return $"OK:RESPONSE_DELAY:{_node.AutoResponseDelayMs}ms";
+ if (!int.TryParse(parts[1].Trim(), out int d) || d < 0)
+ throw new ArgumentException($"RESPONSE_DELAY 鍙傛暟閿欒: {parts[1]}");
+ _node.AutoResponseDelayMs = d;
+ return $"OK:鍝嶅簲寤惰繜宸茶缃负 {d}ms";
+ }
+ case "LOG":
+ {
+ var log = _bus.GetBusLog(50);
+ if (log.Count == 0) return "OK:LOG:EMPTY";
+ var entries = log.Select(f =>
+ $"[{f.Timestamp:HH:mm:ss.fff}]{f.SourceNodeId}>{f.FrameIdHex}:{f.DataHex}");
+ return $"OK:LOG:{log.Count}:{string.Join("|", entries)}";
+ }
+ case "BUS_STATUS":
+ return "OK:" + _bus.GetStatusSummary();
+ default:
+ return $"ERR:UNKNOWN_CMD:{query}";
+ }
+ }
+
+ private string DescribeStatus()
+ {
+ string nodeStatus = _node.GetStatusSummary(); // Node={nodeId}, Active=鈥�, Rx=鈥�
+ string busStatus = _bus.GetStatusSummary(); // Bus=鈥�, State=鈥�, Nodes=鈥�
+ return $"{nodeStatus}|{busStatus}";
+ }
+
+ /// <summary>鎶婃帴鏀跺抚鏍煎紡鍖栦负 EV:RECV 鎺ㄩ�佽锛堟椂闂存埑涓庢暟鎹笉鍚啋鍙凤級銆�</summary>
+ public static string FormatPush(CanFrame frame)
+ {
+ string src = string.IsNullOrEmpty(frame.SourceNodeId) ? "0" : frame.SourceNodeId;
+ return $"EV:RECV:{frame.FrameIdHex}:{frame.DataHex}:{frame.Timestamp:HH.mm.ss.fff}:{src}";
+ }
+ }
+}
--
Gitblit v1.9.1