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}"; } } }