using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; namespace USBCAN { /// /// CAN帮助类 /// /// /// 使用方法: /// /// CANHelper.Instance.Initialize(); // 在首次使用时需要初始化,初始化失败时会抛出异常 /// CANHelper.Instance.ConsumptionFrameEvent += (CAN_API.VCI_CAN_OBJ frame){ /* TODO:在这里消费掉数据帧,注意,这里是异步调用 */ } /// CANHelper.Instance.SendData(0x1111, data); // 向CAN发送数据,具体方法参阅注释 /// /// public sealed class CANHelper : IDisposable { /// /// CAN实例 /// public static readonly CANHelper Instance = new CANHelper(); #region 公开属性 /// /// CAN是否打开 /// public bool IsOpen { private set; get; } = false; #endregion #region 构造 private CANHelper() { } #endregion #region 释放 /// /// Flag: 标识Disposed是否已经被调用 /// private bool _IsDisposed = false; /// /// 公开的Dispose方法 /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// 实际Dispose方法 /// /// 是否由用户调用 private void Dispose(bool disposing) { if (_IsDisposed) return; if (disposing) { // 释放托管成员 _FrameBuffer.Dispose(); } // 释放非托管成员 // 关闭CAN设备 CloseDevice(); // 标识为已执行Dispose _IsDisposed = true; } /// /// 析构 /// ~CANHelper() { // 释放非托管内存 Dispose(false); } #endregion #region 打开与关闭 /// /// 初始化并打开CAN设备 /// public void Initialize(CAN_API.CAN_BaudRate baudRate) { // 如果已经打开,直接返回 if (IsOpen) return; // 打开设备 if (CAN_API.VCI_OpenDevice(_DeviceType, _DeviceInd, _CANInd) == CAN_API.STATUS_ERR) throw new Exception("CAN设备打开失败,错误信息:" + ReadErrorMessage()); // 构造CAN配置信息 CAN_API.VCI_INIT_CONFIG pInitConfig = new CAN_API.VCI_INIT_CONFIG { AccCode = 0x00000000, // 表示全部接收(全部接收: AccCode:0x00000000) AccMask = 0xFFFFFFFF, // ( AccMask:0xFFFFFFFF) Reserved = 0x00, // 保留,填0 Filter = 0x01, // 滤波方式 01 Timing0 = CAN_API.VCI_INIT_CONFIG_Timing0[(int)baudRate], // ( 波特率查表 ) Timing1 = CAN_API.VCI_INIT_CONFIG_Timing1[(int)baudRate], // ( 波特率查表 ) Mode = 0x00 // 正常模式; 0:正常模式,可以IO。 1:表示只听模式(只接收,不影响总线) }; // 初始化CAN if (CAN_API.VCI_InitCAN(_DeviceType, _DeviceInd, _CANInd, ref pInitConfig) == CAN_API.STATUS_ERR) throw new Exception("CAN初始化失败,错误信息:" + ReadErrorMessage()); // 启动CAN if (CAN_API.VCI_StartCAN(_DeviceType, _DeviceInd, _CANInd) == CAN_API.STATUS_ERR) throw new Exception("CAN启动失败,错误信息:" + ReadErrorMessage()); // 若未出错,标识打开 IsOpen = true; // 初始化消息帧缓冲区,上限为128帧报文,若满了还未消费则阻塞 _FrameBuffer = new BlockingCollection(128); // 生产者消费者开始工作 StartWork(); } /// /// 关闭CAN设备 /// public void CloseDevice() { if (IsOpen) { // 关闭设备 if (CAN_API.VCI_CloseDevice(_DeviceType, _DeviceInd) == CAN_API.STATUS_ERR) throw new Exception("CAN设备关闭失败,错误信息:" + ReadErrorMessage()); IsOpen = false; } } #endregion #region 公开方法 /// /// 读取错误信息 /// /// public string ReadErrorMessage() { CAN_API.VCI_ERR_INFO errInfo = new CAN_API.VCI_ERR_INFO(); try { // 尝试读取错误信息 if (CAN_API.VCI_ReadErrInfo(_DeviceType, _DeviceInd, _CANInd, ref errInfo) == CAN_API.STATUS_ERR) return "读取错误信息失败"; } catch (Exception ex) { return string.Format("读取错误信息时发生异常({0})", ex.Message); } if (errInfo.ErrCode == 0x00) { // 若无错误信息,则返回‘无错误信息’ return "无错误信息"; } // 由于可能同时出现多种错误,使用按位与的方式读取错误信息 List errMsgList = new List(); if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_CAN_OVERFLOW) != 0) errMsgList.Add("CAN控制器内部FIFO溢出"); if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_CAN_ERRALARM) != 0) errMsgList.Add("CAN控制器错误报警"); if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_CAN_PASSIVE) != 0) errMsgList.Add("CAN控制器消极错误"); if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_CAN_LOSE) != 0) errMsgList.Add("CAN控制器仲裁丢失"); if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_CAN_BUSERR) != 0) errMsgList.Add("CAN控制器总线错误"); if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_DEVICEOPENED) != 0) errMsgList.Add("设备已经打开"); if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_DEVICEOPEN) != 0) errMsgList.Add("打开设备错误"); if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_DEVICENOTOPEN) != 0) errMsgList.Add("设备没有打开"); if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_BUFFEROVERFLOW) != 0) errMsgList.Add("缓冲区溢出"); if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_DEVICENOTEXIST) != 0) errMsgList.Add("此设备不存在"); if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_LOADKERNELDLL) != 0) errMsgList.Add("装载动态库失败"); if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_CMDFAILED) != 0) errMsgList.Add("执行命令失败"); if ((errInfo.ErrCode & (uint)CAN_API.ErrorType.ERR_BUFFERCREATE) != 0) errMsgList.Add("内存不足"); if (errMsgList.Count == 0) { // 若未检测到错误信息,则返回‘未知错误’ return "未知错误"; } else if (errMsgList.Count == 1) { return errMsgList[0]; } else { // 否则将错误信息以'|'拼接返回 return string.Join("|", errMsgList); } } /// /// 向CAN发送数据帧 /// /// 发送帧ID /// 数据数组(数组长度必须为8) public void SendData(uint frameID, byte[] data) { CAN_API.VCI_CAN_OBJ frameInfo = new CAN_API.VCI_CAN_OBJ { ID = frameID, // 帧ID SendType = 0, // 正常发送 RemoteFlag = 0, // 非远程帧 ExternFlag = 0, // 非扩展帧 DataLen = 8, // 数据长度 Data = data, // 数据 Reserved = new byte[3] // 预留 }; SendData(frameInfo); } /// /// 向CAN发送数据帧 /// /// 帧信息 public void SendData(CAN_API.VCI_CAN_OBJ frameInfo) { // 发送一帧数据 if (CAN_API.VCI_Transmit(_DeviceType, _DeviceInd, _CANInd, ref frameInfo, 1) == CAN_API.STATUS_ERR) throw new Exception("数据发送失败,错误信息:" + ReadErrorMessage()); } #endregion #region 公开事件与委托 /// /// 消费帧事件委托 /// /// 报文帧 public delegate void ConsumptionFrameEventHandler(CAN_API.VCI_CAN_OBJ frame); /// /// 消费帧事件 每读取一帧数据发生一次消费帧事件 /// public event ConsumptionFrameEventHandler ConsumptionFrameEvent; #endregion #region 生产者消费者模式 - 生产数据帧,发出事件消费数据帧 /// /// 帧缓冲区(生产者消费者队列) /// private BlockingCollection _FrameBuffer; /// /// 生产者线程 /// private Thread _ProducerThread; /// /// 消费者线程 /// private Thread _ConsumerThread; /// /// 开始工作线程 /// private void StartWork() { // 启动生产者与消费者线程 // 优先级设置为高 _ProducerThread = new Thread(new ThreadStart(Producer)) { IsBackground = true, Priority = ThreadPriority.Highest }; _ConsumerThread = new Thread(new ThreadStart(Consumer)) { IsBackground = true, Priority = ThreadPriority.Highest }; _ProducerThread.Start(); _ConsumerThread.Start(); } /// /// 生产者 /// private void Producer() { // 分配一个缓冲区,最大能同时容纳100帧数据 IntPtr readBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CAN_API.VCI_CAN_OBJ)) * 100); ; while (IsOpen) { // var n = CAN_API.VCI_GetReceiveNum(_DeviceType, _DeviceInd, _CANInd); // 接收数据,超时时间100ms,最大接收100个 uint len = CAN_API.VCI_Receive(_DeviceType, _DeviceInd, _CANInd, readBuffer, 100, 100); if (len == 0) { // 如果未读到数据,读取错误信息 ReadErrorMessage(); } else { // 将读取到的每一帧数据构造帧对象VCI_CAN_OBJ,装入帧缓冲区中(生产产品到库存) for (int i = 0; i < len; i++) { // 实例化帧对象,装入帧缓冲区 //_FrameBuffer.Add((CAN_API.VCI_CAN_OBJ)Marshal.PtrToStructure((IntPtr)((uint)readBuffer + i * Marshal.SizeOf(typeof(CAN_API.VCI_CAN_OBJ))), typeof(CAN_API.VCI_CAN_OBJ))); _FrameBuffer.Add(Marshal.PtrToStructure((IntPtr)((uint)readBuffer + i * Marshal.SizeOf(typeof(CAN_API.VCI_CAN_OBJ))))); } } } Marshal.FreeHGlobal(readBuffer); } /// /// 消费者 /// private void Consumer() { while (IsOpen) { // 从队列中获取帧(该方法会线程安全的阻塞,当有数据装入时立刻返回) var frame = _FrameBuffer.Take(); // 消费帧 if (ConsumptionFrameEvent != null) ConsumptionFrameEvent(frame); } } #endregion #region 私有成员 /// /// 设备类型 /// private const uint _DeviceType = (uint)CAN_API.PCIDeviceType.VCI_USBCAN1; /// /// 设备ID /// private const uint _DeviceInd = 0; /// /// 第几路CAN /// private const uint _CANInd = 0; #endregion } }