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 { [ACPCommand] public class CAN : IDisposable { #region 静态成员 public static event Action? ConnectEvent; public static event Action? DisConnectEvent; public static bool ConnectFlag { get; set; } = false; /// /// 实时接收到的 CAN 报文缓存(按报文 ID 索引,保留最新帧)。 /// 供 CANViewModel.Refresh 等界面轮询读取。 /// public static ConcurrentDictionary RealTimeMessages { get; } = new(); #endregion #region 实例字段与属性 /// 设备类型号(43 = USBCANFD-400U 等,TSMaster 中作为配置参考保留) public uint DeviceType { get; } /// 设备索引 public uint DeviceIndex { get; } /// 最大通道数 public int MaxChannels { get; } /// 仲裁域波特率 public string ABitBaud { get; } /// 数据域波特率 public string DBitBaud { get; } /// 是否开启终端电阻 public bool EnableTerminalResistance { get; } /// DBC 解析器(提供 MsgDatabase、MaxChannels 等访问) public DBCParse DBCParser => DBCParse.Instance; /// /// DBC 报文解码事件:当接收到 CAN 帧并通过 DBC 解码后触发。 /// 参数:(通道号, 解码后的报文信息) /// public event Action? OnDbcMessageDecoded; private bool _disposed; private bool _isOpened; private TCANFDQueueEvent_Win32? _instanceListener; #endregion #region 构造函数 /// 默认构造函数(兼容 SystemConfig.CANFD = new() 等场景) public CAN() : this(43, 0, 4, "500000", "2000000", true) { } /// /// 带配置参数的构造函数(兼容原 ZLG 设备管理流程)。 /// /// 设备类型号 /// 设备索引 /// 最大通道数 /// 仲裁域波特率 /// 数据域波特率 /// 是否开启终端电阻 public CAN(uint deviceType, uint deviceIndex, int maxChannels, string aBitBaud, string dBitBaud, bool enableTerminalResistance) { DeviceType = deviceType; DeviceIndex = deviceIndex; MaxChannels = maxChannels; ABitBaud = aBitBaud; DBitBaud = dBitBaud; EnableTerminalResistance = enableTerminalResistance; } #endregion #region 实例方法 — 设备管理(替代原 ZLG 实例方法) /// /// 打开设备:初始化 TSMaster 库 → 设置通道数 → 配置波特率 → 连接 → 启动 RBS → 注册监听。 /// /// true 表示成功 public bool 打开设备() { if (_isOpened) return true; if (ConnectFlag) { _isOpened = true; return true; } try { // 1. 初始化 TSMaster 库 var re = Init("ACP"); if (re != 0) { Debug.WriteLine($"TSMaster 初始化失败,错误代码:{re}"); return false; } // 2. 设置通道数 TsMasterApi.tsapp_set_can_channel_count(MaxChannels); // 3. 配置各通道波特率 float arbKbps = float.Parse(ABitBaud) / 1000f; float dataKbps = float.Parse(DBitBaud) / 1000f; for (int ch = 0; ch < MaxChannels; ch++) { TsMasterApi.tsapp_configure_baudrate_canfd( ch, arbKbps, dataKbps, TLIBCANFDControllerType.lfdtISOCAN, TLIBCANFDControllerMode.lfdmNormal, EnableTerminalResistance); } // 4. 连接硬件 re = Connect(); if (re != 0) { Debug.WriteLine($"CAN 连接失败,错误代码:{re}"); return false; } // 5. 注册接收监听(用于 DBC 信号解码广播) _instanceListener = new TCANFDQueueEvent_Win32(OnCanFdReceived); RegisterListener(_instanceListener); _isOpened = true; return true; } catch (Exception ex) { Debug.WriteLine($"打开设备异常:{ex.Message}"); return false; } } /// /// 关闭 CAN 卡设备:注销监听 → 断开连接。 /// public void 关闭CAN卡设备() { if (!_isOpened && !ConnectFlag) return; try { if (_instanceListener != null) { UnRegisterListener(_instanceListener); _instanceListener = null; } DisConnect(); } catch (Exception ex) { Debug.WriteLine($"关闭CAN卡设备异常:{ex.Message}"); } finally { _isOpened = false; } } /// /// 初始化并启动指定通道(配置波特率,TSMaster 在 Connect 时已自动启动所有通道)。 /// /// 通道号 public void 初始化并启动通道(uint channel) { if (channel >= MaxChannels) return; try { float arbKbps = float.Parse(ABitBaud) / 1000f; float dataKbps = float.Parse(DBitBaud) / 1000f; TsMasterApi.tsapp_configure_baudrate_canfd( (int)channel, arbKbps, dataKbps, TLIBCANFDControllerType.lfdtISOCAN, TLIBCANFDControllerMode.lfdmNormal, EnableTerminalResistance); } catch (Exception ex) { Debug.WriteLine($"初始化通道 {channel} 异常:{ex.Message}"); } } /// /// 加载通道 DBC 文件。 /// /// 通道号 /// DBC 文件路径 /// true 表示加载成功 public bool 加载通道DBC文件(uint channel, string filePath) { try { var re = LoadDBC(filePath, [(int)channel], out _); return re == 0; } catch (Exception ex) { Debug.WriteLine($"加载通道 {channel} DBC 文件异常:{ex.Message}"); return false; } } /// /// CAN FD 接收回调:接收原始帧 → 查找 DBC → 解码信号 → 触发 OnDbcMessageDecoded 事件。 /// private void OnCanFdReceived(ref int AObj, ref TLIBCANFD AData) { if (_disposed) return; // 始终缓存最新报文,供界面轮询刷新 int identifier = AData.FIdentifier; var canfdCopy = AData; RealTimeMessages[identifier] = canfdCopy; if (OnDbcMessageDecoded == null) return; try { uint channel = AData.FIdxChn; if (channel >= DBCParse.MsgDatabase.Count) return; // 在 DBC 数据库中查找匹配的报文 var msgList = DBCParse.MsgDatabase[(int)channel]; var match = msgList.FirstOrDefault(m => m.msg_id == identifier); if (match == null || match.signal_Name == null || match.signal_Name.Length == 0) return; // 解码各信号值 var signals = new DBCSignal[match.signal_Name.Length]; for (int i = 0; i < match.signal_Name.Length; i++) { double value = 0; TsMasterApi.tsdb_get_signal_value_canfd(ref canfdCopy, match.msg_name, match.signal_Name[i], ref value); signals[i] = new DBCSignal { strName = Encoding.Default.GetBytes(match.signal_Name[i]), nRawvalue = value, nFactor = 1, nOffset = 0 }; } var decoded = new DBCMessage { nID = (uint)identifier, nSignalCount = signals.Length, vSignals = signals }; OnDbcMessageDecoded?.Invoke(channel, decoded); } catch (Exception ex) { Debug.WriteLine($"DBC 信号解码异常:{ex.Message}"); } } #endregion #region IDisposable public void Dispose() { if (_disposed) return; _disposed = true; 关闭CAN卡设备(); GC.SuppressFinalize(this); } #endregion /// /// 初始化TSMasterCAN /// /// /// /// [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)); } } /// /// 释放TSMasterCAN /// [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; } /// /// 连接 /// /// 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; } /// /// 断开 /// /// 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; 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); } } /// /// 开始记录日志 /// /// /// public static int StartLogging(string filePath) { return TsMasterApi.tsapp_start_logging(Path.GetFullPath(filePath)); } /// /// 结束记录日志 /// /// 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); } /// /// 获取信号值 /// /// /// /// /// 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); } /// /// 设置信号值 /// /// /// /// /// /// /// /// /// 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; } /// /// 设置信号值 /// /// /// /// /// 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); } /// /// 设置报文 /// /// 通道 /// DBC数据库ID /// 报文数组 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)); } } /// /// 发送自定义报文 /// /// /// /// /// /// /// /// /// /// /// /// /// 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; } /// /// 添加循环发送报文 /// /// /// /// /// 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); } /// /// 清除循环发送的报文 /// /// public static int DeleteCyclicMsgs() { return TsMasterApi.tsapp_delete_cyclic_msgs(); } /// /// 获取错误提示 /// /// 错误代码 /// 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}"; } } }