using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using CanSimulator;
|
|
namespace CanSimulatorServer
|
{
|
/// <summary>
|
/// 服务端命令串执行器:对挂载节点执行 SEND/QUERY 命令,语义与原 CanDriverAdapter
|
/// 保持一致(命令串直传协议的核心)。返回值约定:
|
/// OK 前缀 = 成功响应;ERR 前缀 = 业务失败;其余 = 裸数据(由会话层补 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(data 内可含空格,不参与分段)
|
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}";
|
}
|
}
|
}
|