using Common.Attributes;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using TSMaster;
namespace TSMasterCAN
{
///
/// TSMaster CAN/CANFD 总线控制类,基于 TSMaster API 实现总线连接管理、
/// DBC 信号读写、报文发送(单次/循环)、日志记录等功能,暴露给命令树供测试步骤调用。
///
[ACPCommand]
public class CAN
{
public static event Action ConnectEvent;
public static event Action DisConnectEvent;
public static bool ConnectFlag { get; set; } = false;
///
/// 初始化CAN驱动
///
///
///
///
[Browsable(false)]
public static int Init(string ProjectName, string? filePath = null)
{
if (string.IsNullOrEmpty(filePath))
{
return TsMasterApi.initialize_lib_tsmaster(ProjectName);
}
else
{
return TsMasterApi.initialize_lib_tsmaster_with_project(ProjectName, Path.GetFullPath(filePath));
}
}
///
/// 释放CAN驱动
///
[Browsable(false)]
public static void Release()
{
TsMasterApi.finalize_lib_tsmaster();
}
public static int obj = 0;
public static TCANFDQueueEvent_Win32 listener;
[Browsable(false)]
public static int RegisterListener(TCANFDQueueEvent_Win32 listenEvent)
{
listener = listenEvent;
var re = TsMasterApi.tsapp_register_event_canfd(ref obj, listener);
Debug.Assert(re == 0);
return re;
}
[Browsable(false)]
public static int UnRegisterListener(TCANFDQueueEvent_Win32 listenEvent = null)
{
if (listenEvent is not null)
{
listener = listenEvent;
}
var re = TsMasterApi.tsapp_unregister_event_canfd(ref obj, listener);
Debug.Assert(re == 0);
return re;
}
///
/// 连接 TSMaster CAN 总线:建立总线连接并启动 RBS 仿真系统,
/// 成功后置位连接标志并触发 连接事件。
///
/// 返回代码,0 表示成功,非 0 表示失败(可用 查询错误说明)
public static int Connect()
{
var re = TsMasterApi.tsapp_connect();
if (re == 0)
{
re = TsMasterApi.tscom_can_rbs_start();
if (re == 0)
{
ConnectFlag = true;
Task.Run(() => ConnectEvent?.Invoke());
}
else
{
Debug.WriteLine($"CAN_RBS启动失败,错误代码:{re}");
}
}
else
{
Debug.WriteLine($"CAN连接失败,错误代码:{re}");
return re;
}
return re;
}
///
/// 断开 TSMaster CAN 总线连接,成功后清除连接标志并触发 断开事件。
///
/// 返回代码,0 表示成功,非 0 表示失败(可用 查询错误说明)
public static int DisConnect()
{
var re = TsMasterApi.tsapp_disconnect();
//Debug.Assert(re == 0);
if (re == 0)
{
ConnectFlag = false;
Task.Run(() => DisConnectEvent?.Invoke());
}
else
{
Debug.WriteLine($"断开CAN连接失败,错误代码:{re}");
}
return re;
}
///
/// 加载DBC
///
///
///
///
///
[Browsable(false)]
public static int LoadDBC(string filePath, int[] channel, out uint databaseID)
{
databaseID = 0;
if (!Path.Exists(filePath)) return -1;
var re = TsMasterApi.tsdb_load_can_db(Path.GetFullPath(filePath), string.Join(",", channel), ref databaseID);
foreach (var item in channel)
{
DBCParse.parse(databaseID, item);
DBCParse.rbs_parse(databaseID, item);
}
return re;
}
///
/// 卸载DBC
///
///
///
[Browsable(false)]
public static int UnLoadDBC(uint? databaseID = null)
{
if (databaseID == null)
{
return TsMasterApi.tsdb_unload_can_dbs();
}
else
{
return TsMasterApi.tsdb_unload_can_db(databaseID.Value);
}
}
///
/// 开始记录总线日志,将总线上的报文数据记录到指定文件。
///
/// 日志文件保存路径(自动转换为绝对路径)
/// 返回代码,0 表示成功,非 0 表示失败
public static int StartLogging(string filePath)
{
return TsMasterApi.tsapp_start_logging(Path.GetFullPath(filePath));
}
///
/// 结束总线日志记录。
///
/// 返回代码,0 表示成功,非 0 表示失败
public static int StopLogging()
{
return TsMasterApi.tsapp_stop_logging();
}
///
/// 弹出通道映射窗口
///
///
///
[Browsable(false)]
public static int ShowChannelMappingWindow(bool isWait = false)
{
return TsMasterApi.tsapp_show_tsmaster_window("Hardware", isWait);
}
///
/// 获取 DBC 中定义信号的当前物理值(从总线接收报文中解析)。
///
/// 通道号(0 起始)
/// DBC 中的报文名称
/// DBC 中的信号名称
/// 信号物理值,获取失败时返回
public static double GetSignalValue(byte channel, string AMsgName, string ASgnName)
{
double value = double.NaN;
var find = DBCParse.MsgDatabase[channel].First(s => s.msg_name == AMsgName);
var re = TsMasterApi.tsdb_get_signal_value_canfd(ref find.ACANFD, AMsgName, ASgnName, ref value);
return value;
}
///
/// 获取信号值
///
///
///
///
///
///
[Browsable(false)]
public static int GetSignalValue(byte channel, string AMsgName, string ASgnName, ref double value)
{
var find = DBCParse.MsgDatabase[channel].First(s => s.msg_name == AMsgName);
return TsMasterApi.tsdb_get_signal_value_canfd(ref find.ACANFD, AMsgName, ASgnName, ref value);
}
///
/// 获取信号值
///
///
///
///
///
///
[Browsable(false)]
public static int GetSignalValue(ref TLIBCANFD ACANFD, string AMsgName, string ASgnName, ref double value)
{
return TsMasterApi.tsdb_get_signal_value_canfd(ref ACANFD, AMsgName, ASgnName, ref value);
}
///
/// 设置 DBC 中定义信号的值(按物理值编码进报文数据),可选择立即发送或周期发送。
///
/// 通道号(0 起始)
/// DBC 中的报文名称
/// DBC 中的信号名称
/// 要设置的信号物理值
/// 是否发送:false 仅更新数据不发送,true 则发送
/// 发送周期(毫秒):0 = 只发送一次;>0 = 按该周期循环发送
/// 返回代码,0 表示成功,非 0 表示失败
/// 信号编码写入报文失败时抛出
public static int SetSignalValue(byte channel, string AMsgName, string ASgnName, double AValue, bool isSend = false, float sendPeriod = 0)
{
var find = DBCParse.MsgDatabase[channel].First(s => s.msg_name == AMsgName);
find.ACANFD.FIdxChn = channel;
var re = TsMasterApi.tsdb_set_signal_value_canfd(ref find.ACANFD, AMsgName, ASgnName, AValue);
Debug.Assert(re == 0);
if (re != 0) throw new Exception($"设置报文失败!返回代码{re}");
if (re != 0) return re;
if (isSend)
{
if (sendPeriod == 0)
{
return TsMasterApi.tsapp_transmit_canfd_async(ref find.ACANFD);
}
else
{
return TsMasterApi.tsapp_add_cyclic_msg_canfd(ref find.ACANFD, sendPeriod);
}
}
return 0;
}
///
/// 按报文名称发送 DBC 中定义的报文(使用该报文在 DBC 数据库中的当前数据)。
///
/// 通道号
/// DBC 中的报文名称
/// 发送周期(毫秒):0 = 只发送一次;>0 = 按该周期循环发送
/// 返回代码,0 表示成功,非 0 表示失败
public static int SetSignalValue(APP_CHANNEL channel, string AMsgName, float sendPeriod = 0)
{
var find = DBCParse.MsgDatabase[(byte)channel].First(s => s.msg_name == AMsgName);
find.ACANFD.FIdxChn = (byte)channel;
if (sendPeriod == 0)
{
return TsMasterApi.tsapp_transmit_canfd_async(ref find.ACANFD);
}
else
{
return TsMasterApi.tsapp_add_cyclic_msg_canfd(ref find.ACANFD, sendPeriod);
}
}
///
/// 发送自定义报文
///
///
[Browsable(false)]
public static int SetMsg(TLIBCANFD msg)
{
return TsMasterApi.tsapp_transmit_canfd_async(ref msg);
}
///
/// 按帧 ID 更新 DBC 数据库中指定报文的数据区(仅更新内存中的数据,不发送)。
///
/// 通道号
/// 报文帧 ID(DBC 中定义的 FIdentifier)
/// 要写入的报文数据数组,超出报文数据长度的部分会被截断
public static void SetMsg(APP_CHANNEL channel, int ID, byte[] bytes)
{
var find = DBCParse.MsgDatabase[(byte)channel].FirstOrDefault(s => s.ACANFD.FIdentifier == ID);
if (find != null)
{
Array.Copy(bytes, 0, find.ACANFD.FData, 0, Math.Min(bytes.Length, find.ACANFD.FData.Length));
}
}
///
/// 构造并发送自定义 CAN/CANFD 报文,可选择单次发送或周期发送,并可同步更新 DBC 数据库中同 ID 报文的数据。
///
/// 通道号
/// 报文帧 ID
/// 是否为发送帧
/// 是否为扩展帧(29 位 ID)
/// 是否为远程帧
/// 数据长度代码(数据字节数,最大 64)
/// 报文数据数组,超过 64 字节的部分会被截断
/// 是否为 CANFD 帧,默认 true
/// 是否切换数据段波特率(仅 CANFD 有效),默认 false
/// 是否同步更新 DBC 数据库中同帧 ID 报文的数据,默认 false
/// 发送周期(毫秒):0 = 只发送一次;>0 = 按该周期循环发送
/// 返回代码,0 表示成功,非 0 表示失败
public static int SendMsg(APP_CHANNEL AIdxChn, int AID, bool AIsTx, bool AIsExt,
bool AIsRemote, byte ADLC, byte[] ADataArray, bool AIsFD = true, bool AIsBRS = false,
bool isUpdateDBCDatabase = false, float sendPeriod = 0)
{
var send = new TLIBCANFD
{
FIdxChn = (byte)AIdxChn,
FProperties = 0,
FIdentifier = AID,
FDLC = ADLC,
FTimeUS = 0uL,
FData = new byte[64],
FFDProperties = 0,
FIsTx = AIsTx,
FIsError = false,
FIsExt = AIsExt,
FIsRemote = AIsRemote,
FIsFD = AIsFD,
FIsBRS = AIsBRS
};
int length = Math.Min(ADataArray.Length, 64);
Array.Copy(ADataArray, 0, send.FData, 0, length);
int re;
if (sendPeriod == 0)
{
re = TsMasterApi.tsapp_transmit_canfd_async(ref send);
}
else
{
re = TsMasterApi.tsapp_add_cyclic_msg_canfd(ref send, sendPeriod);
}
if (isUpdateDBCDatabase)
{
var find = DBCParse.MsgDatabase[(byte)AIdxChn].FirstOrDefault(s => s.ACANFD.FIdentifier == send.FIdentifier);
if (find != null)
{
Array.Copy(send.FData, 0, find.ACANFD.FData, 0, Math.Min(ADataArray.Length, find.ACANFD.FData.Length));
}
}
return re;
}
///
/// 将 DBC 中定义的报文加入循环发送列表,按指定周期持续发送。
///
/// 通道号(0 起始)
/// DBC 中的报文名称
/// 发送周期(毫秒)
/// 返回代码,0 表示成功,非 0 表示失败
public static int AddCyclicMsg(byte channel, string AMsgName, float sendPeriod)
{
var find = DBCParse.MsgDatabase[channel].First(s => s.msg_name == AMsgName);
find.ACANFD.FIdxChn = channel;
return TsMasterApi.tsapp_add_cyclic_msg_canfd(ref find.ACANFD, sendPeriod);
}
///
/// 清除所有循环发送的报文,停止全部周期发送任务。
///
/// 返回代码,0 表示成功,非 0 表示失败
public static int DeleteCyclicMsgs()
{
return TsMasterApi.tsapp_delete_cyclic_msgs();
}
///
/// 根据错误代码获取对应的错误文字描述,用于诊断 TSMaster API 返回码。
///
/// TSMaster API 返回的错误代码
/// 错误描述文本,未知错误代码时返回提示信息
public static string GetErrorDescription(int errorCode)
{
IntPtr ADesc = IntPtr.Zero;
TsMasterApi.tsapp_get_error_description(errorCode, ref ADesc);
if (ADesc == IntPtr.Zero) return $"未知错误代码: {errorCode}";
// 假设返回的是 ANSI 字符串(如 C 的 char*)
string? description = Marshal.PtrToStringAnsi(ADesc);
return description ?? $"未知错误代码: {errorCode}";
}
}
}