diff --git a/.windsurf/rules/zlgcan.md b/.windsurf/rules/zlgcan.md new file mode 100644 index 0000000..1861d74 --- /dev/null +++ b/.windsurf/rules/zlgcan.md @@ -0,0 +1,36 @@ +--- +())trigger: manual +--- +### 目标:根据周立功的CAN卡提供的官方资料,进行封装成C#类库供使用,主要是CAN,CAN FD,LIN三个通信,其中CAN和CAN FD使用DBC配置,LIN使用LDF配置, + +### 周立功CAN卡型号:USBCANFD 200U + +### 官网网络资源地址 + +函数库简介:https://manual.zlg.cn/web/#/152/5333 +开发流程:https://manual.zlg.cn/web/#/152/5334 +数据结构定义:https://manual.zlg.cn/web/#/152/5335 +函数说明:https://manual.zlg.cn/web/#/152/5336 +设备类型号:https://manual.zlg.cn/web/#/152/6361 + +### 官方ZLGAPI + +CapMachine.Wpf\CanDrive\ZlgCan\ZLGAPI.cs + +### 官方接口函数使用方法: + +CapMachine.Wpf\CanDrive\ZlgCan\接口函数使用手册.pdf + +### 要求: + +相关的封装内容放到CapMachine.Wpf\CanDrive\ZlgCan下,封装要体现工程化能力,需要考虑性能,后期长时间运行 + +功能要全面,我之前用的图莫斯的CAN卡,是另外一套函数,在CapMachine.Wpf\CanDrive 和CapMachine.Wpf\LinDrive 中封装实现,你可以查看 + +官方的样例在\CapMachine\Sample\C#_USBCANFD_251215,可借鉴使用 + +可以联网查询信息 + +需要详细的注释分析过程 + +当前提供的CAN和LIN的基础能力,我最终需要的是使用DBC(CAN/CAN FD)和LDF(LIN)配置文件进行读取数据和控制发送 diff --git a/CapMachine.Wpf/App.xaml.cs b/CapMachine.Wpf/App.xaml.cs index 8bc5f8f..dcfb361 100644 --- a/CapMachine.Wpf/App.xaml.cs +++ b/CapMachine.Wpf/App.xaml.cs @@ -131,6 +131,8 @@ namespace CapMachine.Wpf containerRegistry.RegisterSingleton(); containerRegistry.RegisterSingleton(); containerRegistry.RegisterSingleton(); + containerRegistry.RegisterSingleton(); + containerRegistry.RegisterSingleton(); containerRegistry.RegisterSingleton(); containerRegistry.RegisterSingleton(); containerRegistry.RegisterSingleton(); @@ -181,6 +183,8 @@ namespace CapMachine.Wpf containerRegistry.RegisterForNavigation(); containerRegistry.RegisterForNavigation(); containerRegistry.RegisterForNavigation(); + containerRegistry.RegisterForNavigation(); + containerRegistry.RegisterForNavigation(); //注册Dialog视图时绑定VM diff --git a/CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs b/CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs index e1956eb..39f7ee5 100644 --- a/CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs +++ b/CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs @@ -792,7 +792,7 @@ namespace CapMachine.Wpf.CanDrive } // 同步值到CAN消息 - DBCParserByFD.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(msgName), msgPtSend); + DBCParserByFD.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder(msgName), msgPtSend); var canMsg = (USB2CAN.CAN_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CAN.CAN_MSG)); // 直接发送此消息,无需通过队列 @@ -947,7 +947,7 @@ namespace CapMachine.Wpf.CanDrive itemSignal.SignalCmdValue = random.Next(0, 100); DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue); } - DBCParserByFD.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend); + DBCParserByFD.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend); SchCanMsg[Index] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CANFD.CANFD_MSG)); Index++; } @@ -1003,7 +1003,7 @@ namespace CapMachine.Wpf.CanDrive { DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue); } - DBCParserByFD.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend); + DBCParserByFD.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend); //每个分组就是一个帧指令/消息数据 SchCanMsg[Index] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CANFD.CANFD_MSG)); //分配当前消息帧在集合中的位置信息到ListCANScheduleConfig中,方便实时更新时定位帧的位置 @@ -1120,99 +1120,6 @@ namespace CapMachine.Wpf.CanDrive /// private int CycleUpdateIndex = 0; - /// - /// 循环更新调度表的指令数据 - /// 定时更新数据到调度表中 - /// - public void StartCycleUpdateCmd() - { - CycleUpdateCmdTask = Task.Run(async () => - { - while (IsCycleSend) - { - await Task.Delay(UpdateCycle); - try - { - - //通过DBC进行对消息赋值 - IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG))); - CycleUpdateIndex = 0; - //循环给MSG赋值数据,顺序是固定的,跟初始时设置是一样的 - foreach (var itemMsg in GroupMsg) - { - foreach (var itemSignal in itemMsg) - { - //itemSignal.SignalCmdValue = random.Next(0, 100); //仿真测试数据使用 - DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue); - } - DBCParserByFD.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend); - SchCanMsg[CycleUpdateIndex] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CANFD.CANFD_MSG)); - CycleUpdateIndex++; - } - - //通过DBC写入数据后生成CanMsg - //将信号值填入CAN消息里面 - - //释放申请的临时缓冲区 - Marshal.FreeHGlobal(msgPtSend); - - //CAN_UpdateSchedule 官网解释 - // ---MsgTabIndex CAN调度表索引号 - // ---MsgIndex 开始更新帧起始索引,若起始索引大于调度表帧数,则将帧添加到调度表后面 - // ---pCanMsg 需要更新的CAN帧指针 - // ---MsgNum pCanMsgTab里面包含的有效帧数 - - //CAN_UpdateSchedule中的MsgIndex表示当前的调度器中的帧Index序号 - //因为调度表中的帧集合和控制帧的集合和要更新的帧集合都是同一个集合SchCanMsg - //默认1号调度表,一个更新所有的帧数据 - var ret = USB2CANFD.CANFD_UpdateSchedule(DevHandle, WriteCANIndex, (byte)0, (byte)(0), SchCanMsg, (byte)SchCanMsg.Count());//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可 - if (ret == USB2CANFD.CANFD_SUCCESS) - { - IsSendOk = true; - Console.WriteLine($"Update CAN Schedule Success -- SchTabIndex:{(byte)0} -- MsgIndex:{(byte)(0)} "); - } - else - { - IsSendOk = false; - Console.WriteLine($"Update CAN Schedule Error ret = {ret} -- SchTabIndex:{(byte)0} -- MsgIndex:{(byte)(0)}"); - //return; - } - - //一个报文帧一个报文帧进行更新数据 - ////配置信息 默认启用1号调度器,MsgTabIndex=0; - //foreach (var itemMsgSchConfig in ListCANScheduleConfig) - //{ - // //USB2CANFD.CAN_MSG[] SchCanMsg1=new CAN_MSG[1]; - // //SchCanMsg1[0] = SchCanMsg[itemMsgSchConfig.MsgIndex]; - - // // MsgTabIndex CAN调度表索引号 ;MsgIndex 开始更新帧起始索引,若起始索引大于调度表帧数,则将帧添加到调度表后面, ; - // // pCanMsg 需要更新的CAN帧指针,消息数据 ; MsgNum pCanMsgTab里面包含的有效帧数,一个调度表对应一个帧/消息,即为:1 (byte)(itemMsgSchConfig.MsgIndex+0) - // var ret = USB2CANFD.CAN_UpdateSchedule(DevHandle, WriteCANIndex, (byte)0, (byte)(itemMsgSchConfig.MsgIndex), SchCanMsg, 1);//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可 - // if (ret == USB2CANFD.CAN_SUCCESS) - // { - // Console.WriteLine($"Update CAN Schedule Success -- SchTabIndex:{(byte)itemMsgSchConfig.SchTabIndex} -- MsgIndex:{(byte)(itemMsgSchConfig.MsgIndex)} "); - // } - // else - // { - // Console.WriteLine($"Update CAN Schedule Error ret = {ret} -- SchTabIndex:{(byte)itemMsgSchConfig.SchTabIndex} -- MsgIndex:{(byte)(itemMsgSchConfig.MsgIndex)}"); - // //return; - // } - //} - - - } - catch (Exception ex) - { - IsSendOk = false; - LoggerService.Info($"时间:{DateTime.Now.ToString()}-【MSG】-{ex.Message}"); - } - } - - IsSendOk = false; - }); - } - - /// /// 加载要发送的数据 /// 一般是激活后才注册事件 @@ -1257,7 +1164,7 @@ namespace CapMachine.Wpf.CanDrive /// /// /// - private void UpdateSchDataByCmdDataChanged() + public void UpdateSchDataByCmdDataChanged() { try { @@ -1284,7 +1191,7 @@ namespace CapMachine.Wpf.CanDrive var SetSignalValue = DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue); //monitorValueLog.UpdateValue1(SetSignalValue); } - var SyncValueToCanMsg = DBCParserByFD.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend); + var SyncValueToCanMsg = DBCParserByFD.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend); //monitorValueLog.UpdateValue2(SyncValueToCanMsg); SchCanMsg[CycleUpdateIndex] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CANFD.CANFD_MSG)); CycleUpdateIndex++; @@ -1309,7 +1216,7 @@ namespace CapMachine.Wpf.CanDrive if (ret == USB2CANFD.CANFD_SUCCESS) { IsSendOk = true; - //Console.WriteLine($"Update CAN Schedule Success -- SchTabIndex:{(byte)0} -- MsgIndex:{(byte)(0)} "); + Console.WriteLine($"Update CAN Schedule Success -- SchTabIndex:{(byte)0} -- MsgIndex:{(byte)(0)}-- SchCanMsg.Count():{(SchCanMsg.Count())} "); //monitorValueLog.UpdateValue3(ret); } else @@ -1469,14 +1376,14 @@ namespace CapMachine.Wpf.CanDrive else if (CanNum == 0) { IsReviceOk = false; - Console.WriteLine("No CAN data!"); + //Console.WriteLine("No CAN data!"); } else { IsReviceOk = false; Console.WriteLine("Get CAN data error!"); } - Console.WriteLine(""); + //Console.WriteLine(""); //将CAN消息数据填充到信号里面 DBCParserByFD.DBC_SyncCANFDMsgToValue(DBCHandle, msgPtRead, CanNum); diff --git a/CapMachine.Wpf/CanDrive/ZlgCan/ZLGAPI.cs b/CapMachine.Wpf/CanDrive/ZlgCan/ZLGAPI.cs new file mode 100644 index 0000000..7500067 --- /dev/null +++ b/CapMachine.Wpf/CanDrive/ZlgCan/ZLGAPI.cs @@ -0,0 +1,1232 @@ +using System.Runtime.InteropServices; + +namespace CapMachine.Wpf.CanDrive.ZlgCan +{ + public class ZLGCAN + { + + #region 设备类型 + public static UInt32 ZCAN_PCI9810 = 2; + public static UInt32 ZCAN_USBCAN1 = 3; + public static UInt32 ZCAN_USBCAN2 = 4; + public static UInt32 ZCAN_PCI9820 = 5; + public static UInt32 ZCAN_CANETUDP = 12; + public static UInt32 ZCAN_PCI9840 = 14; + public static UInt32 ZCAN_PCI9820I = 16; + public static UInt32 ZCAN_CANETTCP = 17; + public static UInt32 ZCAN_PCI5010U = 19; + public static UInt32 ZCAN_USBCAN_E_U = 20; + public static UInt32 ZCAN_USBCAN_2E_U = 21; + public static UInt32 ZCAN_PCI5020U = 22; + public static UInt32 ZCAN_PCIE9221 = 24; + public static UInt32 ZCAN_WIFICAN_TCP = 25; + public static UInt32 ZCAN_WIFICAN_UDP = 26; + public static UInt32 ZCAN_PCIe9120 = 27; + public static UInt32 ZCAN_PCIe9110 = 28; + public static UInt32 ZCAN_PCIe9140 = 29; + public static UInt32 ZCAN_USBCAN_4E_U = 31; + public static UInt32 ZCAN_CANDTU_200UR = 32; + public static UInt32 ZCAN_USBCAN_8E_U = 34; + public static UInt32 ZCAN_CANDTU_NET = 36; + public static UInt32 ZCAN_CANDTU_100UR = 37; + public static UInt32 ZCAN_PCIE_CANFD_200U = 39; + public static UInt32 ZCAN_PCIE_CANFD_400U = 40; + public static UInt32 ZCAN_USBCANFD_200U = 41; + public static UInt32 ZCAN_USBCANFD_100U = 42; + public static UInt32 ZCAN_USBCANFD_MINI = 43; + public static UInt32 ZCAN_CANSCOPE = 45; + public static UInt32 ZCAN_CLOUD = 46; + public static UInt32 ZCAN_CANDTU_NET_400 = 47; + public static UInt32 ZCAN_CANFDNET_TCP = 48; + public static UInt32 ZCAN_CANFDNET_200U_TCP = 48; + public static UInt32 ZCAN_CANFDNET_UDP = 49; + public static UInt32 ZCAN_CANFDNET_200U_UDP = 49; + public static UInt32 ZCAN_CANFDWIFI_TCP = 50; + public static UInt32 ZCAN_CANFDWIFI_100U_TCP = 50; + public static UInt32 ZCAN_CANFDWIFI_UDP = 51; + public static UInt32 ZCAN_CANFDWIFI_100U_UDP = 51; + public static UInt32 ZCAN_CANFDNET_400U_TCP = 52; + public static UInt32 ZCAN_CANFDNET_400U_UDP = 53; + public static UInt32 ZCAN_CANFDNET_100U_TCP = 55; + public static UInt32 ZCAN_CANFDNET_100U_UDP = 56; + public static UInt32 ZCAN_CANFDNET_800U_TCP = 57; + public static UInt32 ZCAN_CANFDNET_800U_UDP = 58; + public static UInt32 ZCAN_USBCANFD_800U = 59; + public static UInt32 ZCAN_PCIE_CANFD_100U_EX = 60; + public static UInt32 ZCAN_PCIE_CANFD_400U_EX = 61; + public static UInt32 ZCAN_PCIE_CANFD_200U_MINI = 62; + public static UInt32 ZCAN_PCIE_CANFD_200U_EX = 63; + public static UInt32 ZCAN_PCIE_CANFD_200U_M2 = 63; + public static UInt32 ZCAN_CANFDDTU_400_TCP = 64; + public static UInt32 ZCAN_CANFDDTU_400_UDP = 65; + public static UInt32 ZCAN_CANFDWIFI_200U_TCP = 66; + public static UInt32 ZCAN_CANFDWIFI_200U_UDP = 67; + public static UInt32 ZCAN_CANFDDTU_800ER_TCP = 68; + public static UInt32 ZCAN_CANFDDTU_800ER_UDP = 69; + public static UInt32 ZCAN_CANFDDTU_800EWGR_TCP = 70; + public static UInt32 ZCAN_CANFDDTU_800EWGR_UDP = 71; + public static UInt32 ZCAN_CANFDDTU_600EWGR_TCP = 72; + public static UInt32 ZCAN_CANFDDTU_600EWGR_UDP = 73; + public static UInt32 ZCAN_CANFDDTU_CASCADE_TCP = 74; + public static UInt32 ZCAN_CANFDDTU_CASCADE_UDP = 75; + public static UInt32 ZCAN_USBCANFD_400U = 76; + public static UInt32 ZCAN_CANFDDTU_200U = 77; + public static UInt32 ZCAN_ZPSCANFD_TCP = 78; + public static UInt32 ZCAN_ZPSCANFD_USB = 79; + public static UInt32 ZCAN_CANFDBRIDGE_PLUS = 80; + public static UInt32 ZCAN_CANFDDTU_300U = 81; + public static UInt32 ZCAN_PCIE_CANFD_800U = 82; + public static UInt32 ZCAN_PCIE_CANFD_1200U = 83; + public static UInt32 ZCAN_MINI_PCIE_CANFD = 84; + public static UInt32 ZCAN_USBCANFD_800H = 85; + #endregion + + #region LIN事件 + public static UInt32 ZCAN_LIN_WAKE_UP = 1; + public static UInt32 ZCAN_LIN_ENTERED_SLEEP_MODE = 2; + public static UInt32 ZCAN_LIN_EXITED_SLEEP_MODE = 3; + #endregion + + #region 函数 + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_OpenDevice(uint device_type, uint device_index, uint reserved); + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_OpenDeviceByName(uint device_type, string name); + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_CloseDevice(IntPtr device_handle); + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_IsDeviceOnLine(IntPtr device_handle); + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_GetDeviceInf(IntPtr device_handle, IntPtr pInfo); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_InitCAN(IntPtr device_handle, uint can_index, IntPtr pInitConfig); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_InitCAN(IntPtr device_handle, uint can_index, ref ZCAN_CHANNEL_INIT_CONFIG config); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_StartCAN(IntPtr chn_handle); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ResetCAN(IntPtr chn_handle); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ClearBuffer(IntPtr chn_handle); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReadChannelErrInfo(IntPtr chn_handle, ref ZCAN_CHANNEL_ERR_INFO pErrInfo); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_GetReceiveNum(IntPtr channel_handle, byte type); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_Transmit(IntPtr channel_handle, IntPtr pTransmit, uint len); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_TransmitFD(IntPtr channel_handle, IntPtr pTransmit, uint len); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_TransmitData(IntPtr device_handle, IntPtr pTransmit, uint len); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_Receive(IntPtr channel_handle, IntPtr pReceive, uint len, int wait_time = -1); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReceiveFD(IntPtr channel_handle, IntPtr pReceive, uint len, int wait_time = -1); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReceiveData(IntPtr device_handle, IntPtr pReceive, uint len, int wait_time); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetValue(IntPtr device_handle, string path, string value); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetValue(IntPtr device_handle, string path, IntPtr value); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetValue(IntPtr device_handle, string path, ref ZCAN_AUTO_TRANSMIT_OBJ value); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetValue(IntPtr device_handle, string path, ref ZCANFD_AUTO_TRANSMIT_OBJ value); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_GetValue(IntPtr device_handle, string path); + + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_InitLIN(IntPtr device_handle, uint lin_index, IntPtr pLINInitConfig); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_StartLIN(IntPtr channel_handle); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ResetLIN(IntPtr channel_handle); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_TransmitLIN(IntPtr channel_handle, IntPtr pSend, uint Len); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_GetLINReceiveNum(IntPtr channel_handle); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReceiveLIN(IntPtr channel_handle, IntPtr pReceive, uint Len, int WaitTime); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetLINPublish(IntPtr channel_handle, IntPtr pSend, uint nPublishCount); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetLINSubscribe(IntPtr channel_handle, IntPtr pSend, uint nSubscribeCount); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_WakeUpLIN(IntPtr channel_handle); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_Request(IntPtr device_handle, IntPtr req, IntPtr resp, IntPtr dataBuf, uint dataBufSize); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_Control(IntPtr device_handle, IntPtr ctrl, IntPtr resp); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_RequestEX(IntPtr device_handle, IntPtr requestData, IntPtr resp, IntPtr dataBuf, uint dataBufSize); + + + [DllImport("zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_ControlEX(IntPtr device_handle, uint dataType, IntPtr ctrl, IntPtr resp); + #endregion + + #region 结构体 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_DEVICE_INFO + { + public ushort hw_Version; + public ushort fw_Version; + public ushort dr_Version; + public ushort in_Version; + public ushort irq_Num; + public byte can_Num; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public char[] str_Serial_Num; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)] + public char[] str_hw_Type; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_CHANNEL_INIT_CONFIG + { + public uint can_type; + public _ZCAN_CHANNEL_INIT_CONFIG config; + } + + + [StructLayout(LayoutKind.Explicit, Pack = 1)] + public struct _ZCAN_CHANNEL_INIT_CONFIG + { + [FieldOffset(0)] + public _ZCAN_CHANNEL_CAN_INIT_CONFIG can; + [FieldOffset(0)] + public _ZCAN_CHANNEL_CANFD_INIT_CONFIG canfd; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZCAN_CHANNEL_CAN_INIT_CONFIG + { + public uint acc_code; + public uint acc_mask; + public uint reserved; + public byte filter; + public byte timing0; + public byte timing1; + public byte mode; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZCAN_CHANNEL_CANFD_INIT_CONFIG + { + public uint acc_code; + public uint acc_mask; + public uint abit_timing; + public uint dbit_timing; + public uint brp; + public byte filter; + public byte mode; + public ushort pad; + public uint reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_CHANNEL_ERR_INFO + { + public uint error_code; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public byte[] passive_ErrData; + public byte arLost_ErrData; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_Transmit_Data + { + public can_frame frame; + public uint transmit_type; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class can_frame + { + public uint can_id; + public byte can_dlc; // frame payload length in byte (0 .. CAN_MAX_DLEN) + public byte __pad; // padding + public byte __res0; // reserved / padding + public byte __res1; // reserved / padding + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] data; // frame data + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class canfd_frame + { + public uint can_id; + public byte len; + public byte flags; + public byte __res0; + public byte __res1; /* reserved / padding */ + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public byte[] data; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZCAN_Receive_Data + { + public can_frame frame; + public UInt64 timestamp; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZCAN_ReceiveFD_Data + { + public canfd_frame frame; + public UInt64 timestamp; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_AUTO_TRANSMIT_OBJ //CANFD定时发送帧结构体 + { + public ushort enable; //0-禁用,1-使能 + public ushort index; //定时报文索引 + public uint interval; //定时周期 + public ZCAN_Transmit_Data obj; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANFD_AUTO_TRANSMIT_OBJ //CANFD定时发送帧结构体 + { + public ushort enable; //0-禁用,1-使能 + public ushort index; //定时报文索引 + public uint interval; //定时周期 + public ZCAN_TransmitFD_Data obj; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_TransmitFD_Data + { + public canfd_frame frame; // 报文数据信息,详见 canfd_frame 结构说明。 + public uint transmit_type; // 发送方式,0=正常发送,1=单次发送,2=自发自收,3=单次自发自收。 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANCANFDData + { + public UInt64 timeStamp; + public UInt32 flag; // flag用于设置一些参数,内部结构可以通过以下函数实现设置和取值 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] extraData; //未使用 + public canfd_frame frame; //实际报文结构体 + + // frameType 帧类型 0-CAN 1-CANFD + public uint frameType + { + get { return (flag & 0x03); } + set { flag = (uint)((flag & ~0x03) | (value & 0x03)); } + } + + // txDelay队列发送延时,延时时间存放在 timeStamp 字段;0-不启用延时,1-启用延时,单位1ms,2-启用延时,单位100us + public uint txDelay + { + get { return ((flag >> 2) & 0x03); } + set { flag = (uint)((flag & ~0x0C) | (value & 0x03) << 2); } + } + + public uint transmitType + { + get { return ((flag >> 4) & 0x0F); } + set { flag = (uint)((flag & ~0x0F) | (value & 0x0F) << 4); } + } + + public uint txEchoRequest + { + get { return ((flag >> 8) & 0x01); } + set { flag = (uint)(flag | (value & 0x01) << 8); } + } + + public uint txEchoed + { + get { return ((flag >> 9) & 0x01); } // bit9 + set { flag = (uint)((flag & ~0x200) | (value & 0x01) << 9); } + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct PID + { + public byte rawVal; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct RxData + { + public UInt64 timeStamp; + public byte datalen; + public byte dir; + public byte chkSum; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] + public byte[] reserved; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] data; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANDataObj + { + public byte dataType; // 1-CAN/CANFD数据, 4-LIN数据 + public byte chnl; // 数据通道 + public UInt16 flag; // 未使用 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] extraData; // 未使用 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 92)] + public byte[] data; // 报文结构体 + } + + + // LIN + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINData + { + public PID pid; // 受保护的ID + public RxData rxData; // 数据 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINErrData + { + public UInt64 timeStamp; // 时间戳,单位微秒(us) + public PID pid; // 受保护的ID + + public byte dataLen; // 数据长度 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] data; // 数据 + + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] errData; // 错误信息 + + public byte dir; // 传输方向 + public byte chkSum; // 数据校验,部分设备不支持校验数据的获取 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINEventData + { + public UInt64 timeStamp; // 时间戳,单位微秒(us) + public byte type; // 数据长度 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_MSG + { + public byte chnl; // 数据通道 + public byte dataType; // 0-LIN,1-ErrLIN + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 46)] + public byte[] data; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_INIT_CONFIG + { + public byte linMode; // 0-slave,1-master + public byte chkSumMode; // 1-经典校验,2-增强校验 3-自动(对应eZLINChkSumMode的模式) + public byte maxLength; // 最大数据长度,8~64 + public byte reserved; + public uint libBaud; // 波特率,取值1000~20000 + + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_PUBLISH_CFG + { + public byte ID; // 受保护的ID(ID取值范围为0-63) + public byte datelen; // 范围1~8 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 数据段内容 + public byte[] data; + public byte chkSumMode; // 校验方式:0-默认,启动时配置 1-经典校验 2-增强校验(对应eZLINChkSumMode的模式) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public byte[] reserved; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_SUBSCIBE_CFG + { + public byte ID; // 受保护的ID(ID取值范围为0-63) + public byte datelen; // dataLen范围为1-8 当为255(0xff)则表示设备自动识别报文长度 + public byte chkSumMode; // 校验方式:0-默认,启动时配置 1-经典校验 2-增强校验(对应eZLINChkSumMode的模式) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public byte[] reserved; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct SESSION_PARAM + { + public uint timeout; // 响应超时时间(ms)。因PC定时器误差,建议设置不小于200ms + public uint enhanced_timeout; // 收到消极响应错误码为0x78后的超时时间(ms)。因PC定时器误差,建议设置不小于200ms + public byte threeInOne; //三合一,把下面三个变量写进这一个变量 + + // threeInOne 包含以下三个变量 + // BYTE check_any_negative_response : 1; // 接收到非本次请求服务的消极响应时是否需要判定为响应错误 + // BYTE wait_if_suppress_response : 1; // 抑制响应时是否需要等待消极响应,等待时长为响应超时时间 + // BYTE flag : 6; // 保留 + public byte check_any_negative_response + { + get { return (byte)(threeInOne & 0x0001); } + set { threeInOne = (byte)((threeInOne & ~0x0001) | (value & 0x0001)); } + } + public byte wait_if_suppress_response + { + get { return (byte)((threeInOne & 0x0002) >> 1); } + set { threeInOne = (byte)((threeInOne & ~0x0002) | (value & 0x0002)); } + } + public byte flag + { + get { return (byte)((threeInOne & 0x00FC) >> 2); } + set { threeInOne = (byte)((threeInOne & ~0x00FC) | (value & 0x00FC)); } + } + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] reserved0; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct TRANS_PARAM + { + public byte version; // 传输协议版本,VERSION_0,VERSION_1 + public byte max_data_len; // 单帧最大数据长度,can:8,canfd:64 + public byte local_st_min; // 本程序发送流控时用,连续帧之间的最小间隔,0x00-0x7F(0ms~127ms),0xF1-0xF9(100us~900us) + public byte block_size; // 流控帧的块大小 + public byte fill_byte; // 无效字节的填充数据 + public byte ext_frame; // 0:标准帧 1:扩展帧 + public byte is_modify_ecu_st_min; // 是否忽略ECU 返回流控的STmin,强制使用本程序设置的remote_st_min 参数代替 + public byte remote_st_min; // 发送多帧时用, is_modify_ecu_st_min = 1 时有效,0x00 - 0x7F(0ms~127ms), 0xF1 - 0xF9(100us~900us) + public uint fc_timeout; // 接收流控超时时间(ms),如发送首帧后需要等待回应流控帧 + public byte fill_mode; // 数据长度填充模式 0-就近填充 1-不填充 2-最大填充 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public byte[] reserved0; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct TRANS_PARAM_LIN + { + public byte fill_byte; // 无效字节的填充数据 + public byte st_min; // 从节点准备接收诊断请求的下一帧或传输诊断响应的下一帧所需的最小时间 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] reserved0; + }; + + + // CAN UDS请求数据 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_UDS_REQUEST // 硬件UDS接口构体 + { + public uint req_id; // 请求事务索引ID,范围0~65535 + public byte channel; // 设备通道索引 + public byte frame_type; // 帧类型 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] reserved0; + public uint src_addr; // 请求地址 + public uint dst_addr; // 响应地址 + public byte suppress_response; // 1-抑制响应 + public byte sid; // 请求服务id + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] reserved1; + + public SESSION_PARAM session_param; + public TRANS_PARAM trans_param; + public IntPtr data; // 数据数组(不包含SID) + public uint data_len; // 数据数组的长度 + public uint reserved2; + } + + + // LIN UDS请求数据 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZLIN_UDS_REQUEST // 硬件UDS接口构体 + { + public uint req_id; // 请求事务索引ID,范围0~65535 + public byte channel; // 设备通道索引 + public byte suppress_response; // 1:抑制响应 0:不抑制 + public byte sid; // 请求服务id + public byte Nad; // 节点地址 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] reserved1; + + + public SESSION_PARAM session_param; + public TRANS_PARAM_LIN trans_param; + public IntPtr data; // 数据数组(不包含SID) + public uint data_len; // 数据数组的长度 + public uint reserved2; + } + + + // UDS响应数据 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_UDS_RESPONSE + { + public byte status; // 响应状态 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] reserved; + public byte type; // 响应类型 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 大小为8 + public byte[] raw; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct UDS_RESPONSE_Positive + { + public byte sid; // 响应服务id + public uint data_len; // 数据长度(不包含SID), 数据存放在接口传入的dataBuf中 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct UDS_RESPONSE_Negative + { + public byte neg_code; // 固定为0x7F + public byte sid; // 请求服务id + public byte error_code; // 错误码 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct UDS_RESPONSE_raw + { + public ulong raw; + } + + + [StructLayout(LayoutKind.Explicit)] + public struct _UDS_RESPONSE_union + { + [FieldOffset(0)] + public UDS_RESPONSE_Positive zudsPositive; + + [FieldOffset(0)] + public UDS_RESPONSE_Negative zudsNegative; + + [FieldOffset(0)] + public UDS_RESPONSE_raw raw; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANCANFDUdsData + { + public IntPtr req; // 请求信息 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + public byte[] reserved; // 保留位 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINUdsData + { + public IntPtr req; // 请求信息 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + public byte[] reserved; // 保留位 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANUdsRequestDataObj + { + public uint dataType; // 数据类型 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)] + public byte[] data; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] reserved; // 保留位 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_DYNAMIC_CONFIG_DATA + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public char[] key; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public char[] value; + } + + [StructLayout(LayoutKind.Sequential)] + public struct BusUsage + { + public ulong nTimeStampBegin; // 测量起始时间戳,单位us + public ulong nTimeStampEnd; // 测量结束时间戳,单位us + public byte nChnl; // 通道 + public byte nReserved; // 保留 + public ushort nBusUsage; // 总线利用率(%),总线利用率*100展示。取值0~10000,如8050表示80.50% + public uint nFrameCount; // 帧数量 + } + #endregion + } + + + public class ZDBC + { + #region 常量定义 + public const int _MAX_FILE_PATH_ = 260; // 最长文件路径 + public const int _DBC_NAME_LENGTH_ = 127; // 名称最长长度 + public const int _DBC_COMMENT_MAX_LENGTH_ = 127; // 注释最长长度 + public const int _DBC_UNIT_MAX_LENGTH_ = 23; // 单位最长长度 + public const int _DBC_SIGNAL_MAX_COUNT_ = 256; // 一个消息含有的信号的最大数目 + + public const int MUTIPLEXER_NONE = 0; // 不使用复用器 + public const int MUTIPLEXER_M_VALUE = 1; // 复用信号,当复用器开关的值为multiplexer_value时,该信号有效 + public const int MUTIPLEXER_M_SWITCH = 2; // 复用器开关,一个DBC消息只能有一个信号为开关 + + public const int FT_CAN = 0; // CAN + public const int FT_CANFD = 1; // CANFD + + public const int PROTOCOL_J1939 = 0; + public const int PROTOCOL_OTHER = 1; + public const uint INVALID_DBC_HANDLE = 0xffffffff; // 无效的DBC句柄 + #endregion + + #region 函数部分 + + // ZDBC.dll + public delegate bool OnSend(IntPtr ctx, IntPtr pObj); + public delegate void OnMultiTransDone(IntPtr ctx, IntPtr pMsg, IntPtr data, UInt16 nLen, byte nDirection); + public static OnSend onSend; + public static OnMultiTransDone onMultiTransDone; + + /// + /// 此函数用于初始化解析模块,只需要初始化一次。 + /// + /// 是否关闭多帧发送,为 1 时不支持多帧的消息发送。 + /// 是否开启异步解析;0-不启动,ZDBC_AsyncAnalyse 接口无效;1-启动, 独立线程解析出消息。 + /// 为 INVALID_DBC_HANDLE 表示初始化失败,其他表示初始化成功,保存该返回值,之后的函数调用都要用到该句柄。 + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZDBC_Init(byte disableMultiSend = 0, byte enableAsyncAnalyse = 1); + + /// + /// 释放资源, 与DBC_Init配对使用 + /// + /// hDBC-句柄, ZDBC_Init的返回值 + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_Release(uint DBCHandle); + + /// + /// 此函数用以加载 DBC 格式文件。 + /// + /// 句柄;ZDBC_Init的返回值 + /// 结构体 FileInfo的指针 + /// 为 true 表示加载成功,false 表示失败。 + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_LoadFile(uint DBCHandle, IntPtr pFileInfo); + + /// + /// 从字符串加载DBC + /// + /// hDBC-句柄, DBC_Load的返回值 + /// pFileContent-文件内容字符串 + /// merge-是否合并到当前数据库; 1:不清除现有的数据, 即支持加载多个文件;0:清除原来的数据 + /// + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_LoadContent(uint DBCHandle, IntPtr pFileContent, uint merge); + + /// + /// 获取文件的第一条消息。 + /// + /// hDBC-句柄, DBC_Load的返回值 + /// pMsg 存储消息的信息 + /// true表示成功 + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetFirstMessage(uint DBCHandle, IntPtr pMsg); + + /// + /// 获取下一条消息。 + /// + /// hDBC-句柄, DBC_Load的返回值 + /// pMsg 存储消息的信息 + /// true表示成功 + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetNextMessage(uint DBCHandle, IntPtr pMsg); + + /// + /// 此函数用以根据 ID 获取消息数据。 + /// + /// 句柄; + /// 帧 ID; + /// 消息信息结构体 + /// + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetMessageById(uint DBCHandle, uint nID, IntPtr pMsg); + + /// + /// 此函数用以获取 DBC 文件中含有的消息数目。 + /// + /// DBC句柄 + /// DBC 文件中含有的消息数目 + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZDBC_GetMessageCount(uint DBCHandle); + + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_Analyse(uint DBCHandle, IntPtr pObj, IntPtr pMsg); + + + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_AsyncAnalyse(uint DBCHandle, IntPtr pObj, uint frame_type, UInt64 extraData); + + + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_OnReceive(uint DBCHandle, IntPtr pObj, uint frame_type); + + + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_SetSender(uint hDBC, OnSend sender, IntPtr ctx); + + + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_SetOnMultiTransDoneFunc(uint hDBC, OnMultiTransDone func, IntPtr ctx); + + + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern byte ZDBC_Send(uint hDBC, IntPtr pMsg); + + /// + /// 根据原始数据解码为 DBCMessage。 + /// + /// DBC句柄 + /// 输出参数,解析结果。 + /// 帧数据数组, ControlCAN 传入 VCI_CAN_OBJ, zlgcan 传入 can_frame。 + /// 原始帧数据个数, 即数组大小。 + /// frame_type 帧类型, 参考FT_CAN=0、FT_CANFD=1,ControlCAN不支持CANFD。 + /// 是否成功。 + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_Decode(uint DBCHandle, IntPtr P2DBCMessage, IntPtr P2Obj, uint nCount, byte frame_type); + + /// + /// 根据 DBCMessage 编码为原始数据。 + /// + /// DBC句柄; + /// 编码的原始数据缓冲区数组, ControlCAN 传入 VCI_CAN_OBJ, zlgcan 传入 can_frame。 + /// 输出参数,pObj 缓冲区大小, 返回时为实际原始数据个数。 + /// 输入参数,DBC 消息。 + /// frame_type 帧类型, FT_CAN=0、FT_CANFD=1,ControlCAN不支持CANFD。 + /// 是否成功。 + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_Encode(uint DBCHandle, IntPtr P2Obj, IntPtr P2nCount, IntPtr pMsg, byte frame_type); + + /// + /// 信号原始值转换为实际值 + /// + /// sgl 信号 + /// rawVal 原始值, 如果该值超出信号长度可表示范围,会被截断。 + /// 实际值 + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern double ZDBC_CalcActualValue(IntPtr sgl, IntPtr rawVal); //原始值通过计算转为实际值,实际值会传入rawVal的地址 + + /// + /// 信号实际值转换为原始值 + /// + /// sgl 信号 + /// actualVal 实际值, 超出可表示范围时会被修正 + /// 原始值 + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt64 ZDBC_CalcRawValue(IntPtr sgl, IntPtr actualVal); + + /// + /// 获取网络节点数量 + /// + /// ZDBC_Init的返回值 + /// 网络节点总数量 + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt32 ZDBC_GetNetworkNodeCount(uint DBCHandle); + + /// + /// + /// + /// ZDBC_Init的返回值 + /// index 位置索引 + /// DBCNetworkNode * node 网络节点信息 + /// + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetNetworkNodeAt(uint DBCHandle, UInt32 index, IntPtr node); + + + /// + /// 获取具体信号的值与含义对个数 + /// + /// ZDBC_Init的返回值 + /// message的ID + /// signal的名字 + /// + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt32 ZDBC_GetValDescPairCount(uint DBCHandle, UInt32 mag_id, string signal_name); + + + /// + /// 获取具体信号的值与含义对 + /// + /// ZDBC_Init的返回值 + /// message的ID + /// signal的名字 + /// ValDescPair结构体类型 + [DllImport("zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_GetValDescPair(uint DBCHandle, UInt32 mag_id, string signal_name, IntPtr pair); + + + #endregion + + #region DBC 结构体部分 + + public struct FileInfo + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _MAX_FILE_PATH_ + 1)] + public byte[] strFilePath; // dbc文件路径 + public byte type; // dbc的协议类型, j1939选择PROTOCOL_J1939, 其他协议选择PROTOCOL_OTHER + public byte merge; // 1:不清除现有的数据, 即支持加载多个文件 0:清除原来的数据 + }; + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct DBCSignal + { + + public UInt32 nStartBit; // 起始位 + public UInt32 nLen; // 位长度 + public double nFactor; // 转换因子 + public double nOffset; // 转换偏移实际值=原始值*nFactor+nOffset + public double nMin; // 最小值 + public double nMax; // 最大值 + public UInt64 nRawvalue; // 原始值 + public byte is_signed; // 1:有符号数据, 0:无符号 + public byte is_motorola; // 是否摩托罗拉格式 + public byte multiplexer_type; // 复用器类型 + public byte val_type; // 0:integer, 1:float, 2:double + public UInt32 multiplexer_value; // 复用器开关值为此值时信号有效 + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_UNIT_MAX_LENGTH_ + 1)] + public byte[] unit; //单位 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strName; //名称 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_COMMENT_MAX_LENGTH_ + 1)] + public byte[] strComment; //注释 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strValDesc; //值描述 + + public double initialValue; // 初始化值(原始值) + public uint initialValueValid; // 初始值是否有效 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct DBCMessage + { + public UInt32 nSignalCount; // 信号数量 + public UInt32 nID; // ID + public UInt32 nSize; // 消息占的字节数目 + public double nCycleTime; // 发送周期 + public byte nExtend; // 1:扩展帧, 0:标准帧 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_SIGNAL_MAX_COUNT_)] + public DBCSignal[] vSignals; // 信号集合 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strName; // 名称 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_COMMENT_MAX_LENGTH_ + 1)] + public byte[] strComment; // 注释 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct ValDescPair + { + public double value; // 信号值 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strName; // 对应的值描述 + } + #endregion + } + + + public class ZUDS + { + #region 参数定义 + + public static uint udsRTR = 0x40000000; // Remote Transmission Request + public static uint udsEFF = 0x80000000; // Extend Frame Flag + public static uint udsERR = 0x20000000; // Err flag + + #endregion + + #region 函数部分 + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate uint OnUDSTransmitDelegate(IntPtr ctx, IntPtr frame, uint count); + + + /// + /// 该函数用于初始化 UDS 函数库,返回操作句柄,用于后续的操作,与 ZUDS_Release + /// 配对使用。 + /// typedef uint32 TP_TYPE; // transport protocol + /// #define DoCAN 0 + /// + /// + /// 操作句柄,= ZUDS_INVALID_HANDLE 为无效句柄,其他值为有效句柄。 + [DllImport("zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt32 ZUDS_Init(uint type); + + + /// + /// 该函数用于释放资源,与 ZUDS_Init 配对使用。 + /// + /// + /// + [DllImport("zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_Release(uint type); + + + /// + /// 该函数用于设置函数库的参数。 + /// + /// + /// 参数类型,= PARAM_TYPE_SESSION 0 用 于 设 置 会 话 层 参 数 , = + ///PARAM_TYPE_ISO15765 1 用于设置 ISO15765 的通信参数; + /// 参数值,type =PARAM_TYPE_SESSION 0 时为 ZUDS_SESSION_PARAM, + ///type= PARAM_TYPE_ISO15765 1 时为 ZUDS_ISO15765_PARAM。 + [DllImport("zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_SetParam(UInt32 ZUDS_HANDLE, byte type, IntPtr param); + + + /// + /// 该函数用于设置发送回调函数。函数库自身并不发送帧数据,把打包的帧数据通过回调 + ///函数传出给用户发送,用户可通过 zlgcan 函数库进行帧数据发送。 + /// + /// + /// ctx 上下文参数, 在回调函数中传出, 库内部不会处理该参数; + /// :回调函数原型;typedef uint32 (*OnUDSTransmit)(void* ctx, const ZUDS_FRAME* frame, uint32 count); + [DllImport("zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_SetTransmitHandler(UInt32 ZUDS_HANDLE, IntPtr ctx, OnUDSTransmitDelegate onUDSTransmit); + + + [DllImport("zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_OnReceive(UInt32 ZUDS_HANDLE, IntPtr ZUDS_FRAME); + + + [DllImport("zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_Request(UInt32 ZUDS_HANDLE, IntPtr ZUDS_REQUEST, IntPtr ZUDS_RESPONSE); + + + [DllImport("zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_Stop(UInt32 ZUDS_HANDLE); + + + [DllImport("zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_SetTesterPresent(UInt32 ZUDS_HANDLE, byte enable, IntPtr param); + + #endregion + + #region 结构体部分 + /// + /// 会话层面参数;即一应一答传输时的通讯参数。 + /// + public struct ZUDS_SESSION_PARAM + { + public UInt16 timeout;// ms, timeout to wait the response of the server + public UInt16 enhanced_timeout; // ms,timeout to wait after negative response: error code 0x78 + public UInt32 reserved0; // 保留 + public UInt32 reserved1; // 保留 + } + + + /// + /// 传输数据部分的参数,例如传输时侯每帧报文的字节数。 + /// + public struct ZUDS_ISO15765_PARAM + { + public byte version; // VERSION_0, VERSION_1格式版本,为 VERSION_0 时符合 ISO15765-2 的 2004 版本格式要求;为 + //hVERSION_1 是符合 ISO15765-2 的 2016 版本新增的格式要求,如下图所示 + public byte max_data_len; // max data length, can:8, canfd:64 + public byte local_st_min; // ms, min time between two consecutive frames + public byte block_size; + public byte fill_byte; // fill to invalid byte + public byte frame_type; // 0:std 1:ext + public byte is_modify_ecu_st_min; //是否忽略 ECU 返回流控的 STmin,强制使用本程序设置的 + //remote_st_min 参数代替 + + public byte remote_st_min; //发 送 多 帧 时 用, is_ignore_ecu_st_min = 1 时 有 效 , + //0x00-0x7F(0ms ~127ms), 0xF1-0xF9(100us ~900us) + public UInt16 fc_timeout; //接收流控超时时间(ms), 如发送首帧后需要等待回应流控帧。 + public byte fill_mode;//字节填充模式。FILL_MODE_NONE-不填充0;FILL_MODE_SHORT- 小于 8 字节填充至 8 字节,大于 8 字节时按 DLC 就近填充1;FILL_MODE_MAX- 始终填充至最大数据长度 (不建议)2。 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZUDS_TESTER_PRESENT_PARAM + { + public UInt32 addr;//会话保持的请求地址; + public UInt16 cycle;//发送周期,单位毫秒; + public byte suppress_response; // 1:suppress是否抑制响应,建议设置为 1; + public UInt32 reserved;//:保留,忽略即可。 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZUDS_REQUEST + { + public uint src_addr; // 请求地址 + public uint dst_addr; // 响应地址 + public byte suppress_response; // 1:抑制响应 + public byte sid; //service id of request + public ushort reserve0; + public IntPtr param; //array,params of the service + public uint param_len; //参数数组的长度 + public uint reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZUDS_RESPONSE + { + public byte status; + public byte type; // RT_POSITIVE, RT_NEGATIVE + public _ZUDS_Union union; + public uint reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZUDS_positive + { + public byte sid; // service id of response + public IntPtr param; // array, params of the service, don't free + public uint param_len; + + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZUDS_negative + { + public byte neg_code; // 0x7F + public byte sid; //service id of response + public byte error_code;//消极响应的错误码 + + } + + + [StructLayout(LayoutKind.Explicit)] + public struct _ZUDS_Union + { + [FieldOffset(0)] + public ZUDS_positive zudsPositive; + + [FieldOffset(0)] + public _ZUDS_negative zudsNegative; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZUDS_FRAME + { + public uint id; + public byte extend; + public byte remote; + public byte data_len; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public byte[] data; + public uint reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZUDS_CTX + { + public IntPtr can_type; // 0-CAN 1-CANFD 2-CANFD加速 + public IntPtr chn_handle; // 通道句柄 + } + #endregion + } +} diff --git a/CapMachine.Wpf/CanDrive/ZlgCan/ZlgCanChannelOptions.cs b/CapMachine.Wpf/CanDrive/ZlgCan/ZlgCanChannelOptions.cs new file mode 100644 index 0000000..508084e --- /dev/null +++ b/CapMachine.Wpf/CanDrive/ZlgCan/ZlgCanChannelOptions.cs @@ -0,0 +1,82 @@ +using System; + +namespace CapMachine.Wpf.CanDrive.ZlgCan +{ + /// + /// ZLG CANFD 通道初始化参数。 + /// + public sealed class ZlgCanFdChannelOptions + { + /// + /// 仲裁域波特率(单位:bps)。例如 500000。 + /// + public uint ArbitrationBaudRate { get; set; } = 500000; + + /// + /// 数据域波特率(单位:bps)。例如 2000000。 + /// + public uint DataBaudRate { get; set; } = 2000000; + + /// + /// 终端电阻。 + /// + public bool EnableInternalResistance { get; set; } = true; + + /// + /// 仅监听模式。 + /// + public bool ListenOnly { get; set; } = false; + + /// + /// 是否启用总线利用率上报。 + /// + public bool EnableBusUsage { get; set; } = false; + + /// + /// 总线利用率上报周期(单位:ms)。 + /// + public int BusUsagePeriodMs { get; set; } = 500; + + /// + /// 是否启用设备层“合并接收”(ZCAN_ReceiveData)。 + /// + public bool EnableMergeReceive { get; set; } = false; + + /// + /// 合并接收缓冲区最大帧数量。 + /// + public int MergeReceiveBufferFrames { get; set; } = 100; + } + + /// + /// ZLG LIN 通道初始化参数。 + /// + public sealed class ZlgLinChannelOptions + { + /// + /// LIN 模式:true=主节点,false=从节点。 + /// + public bool IsMaster { get; set; } = true; + + /// + /// 校验模式。 + /// 1-经典校验,2-增强校验,3-自动。 + /// + public byte ChecksumMode { get; set; } = 3; + + /// + /// 最大数据长度(8~64)。 + /// + public byte MaxLength { get; set; } = 8; + + /// + /// 波特率(1000~20000)。 + /// + public uint BaudRate { get; set; } = 19200; + + /// + /// LIN 接收轮询等待时间(ms)。 + /// + public int ReceiveWaitMs { get; set; } = 10; + } +} diff --git a/CapMachine.Wpf/CanDrive/ZlgCan/ZlgCanFd200uDriver.cs b/CapMachine.Wpf/CanDrive/ZlgCan/ZlgCanFd200uDriver.cs new file mode 100644 index 0000000..89ee819 --- /dev/null +++ b/CapMachine.Wpf/CanDrive/ZlgCan/ZlgCanFd200uDriver.cs @@ -0,0 +1,1428 @@ +using CapMachine.Wpf.CanDrive; +using CapMachine.Wpf.Services; +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CapMachine.Wpf.CanDrive.ZlgCan +{ + /// + /// 周立功 USBCANFD-200U 工程化封装(CAN/CANFD/LIN)。 + /// + public sealed class ZlgCanFd200uDriver : BindableBase, IDisposable + { + private readonly object _sync = new object(); + private readonly ILogService _log; + + private IntPtr _deviceHandle = IntPtr.Zero; + private readonly IntPtr[] _canChannelHandles = new IntPtr[2]; + private IntPtr _linChannelHandle = IntPtr.Zero; + + private CancellationTokenSource? _recvCts; + private Task? _recvTask; + + private volatile bool _disposed; + + private readonly object _dbcSync = new object(); + private ZlgDbcDatabase? _dbc; + private ObservableCollection? _dbcModels; + private Dictionary? _dbcModelIndex; + + private List? _cmdData; + private int _cmdSendChannelIndex; + private byte _cmdSendFrameType; + + /// + /// 构造函数。 + /// + /// 日志服务。 + public ZlgCanFd200uDriver(ILogService logService) + { + _log = logService; + } + + /// + /// 安全关闭设备与通道(需在 _sync 锁内调用)。 + /// + /// + /// - 该方法会尽力调用 Reset/Close,并在异常时记录日志且继续执行释放流程; + /// - 该方法不会停止接收线程,调用方应优先 StopReceiveLoop。 + /// + + private bool _openState; + /// + /// 设备打开状态。 + /// + public bool OpenState + { + get { return _openState; } + private set { _openState = value; RaisePropertyChanged(); } + } + + private bool _isReceiving; + /// + /// 是否正在接收。 + /// + public bool IsReceiving + { + get { return _isReceiving; } + private set { _isReceiving = value; RaisePropertyChanged(); } + } + + private bool _isSendOk; + /// + /// 最近是否发生过“发送成功”(用于 UI 指示灯)。 + /// + public bool IsSendOk + { + get { return _isSendOk; } + private set { _isSendOk = value; RaisePropertyChanged(); } + } + + private bool _isReviceOk; + /// + /// 最近是否发生过“接收成功”(用于 UI 指示灯)。 + /// + public bool IsReviceOk + { + get { return _isReviceOk; } + private set { _isReviceOk = value; RaisePropertyChanged(); } + } + + private int _sendOkToken; + private int _reviceOkToken; + + private void MarkSendOk(int holdMs = 800) + { + var token = Interlocked.Increment(ref _sendOkToken); + IsSendOk = true; + Task.Run(async () => + { + await Task.Delay(Math.Max(50, holdMs)); + if (token == _sendOkToken) + { + IsSendOk = false; + } + }); + } + + private void MarkReviceOk(int holdMs = 800) + { + var token = Interlocked.Increment(ref _reviceOkToken); + IsReviceOk = true; + Task.Run(async () => + { + await Task.Delay(Math.Max(50, holdMs)); + if (token == _reviceOkToken) + { + IsReviceOk = false; + } + }); + } + + /// + /// CAN/CANFD 原始帧接收事件。 + /// + public event Action? CanFrameReceived; + + /// + /// LIN 原始帧接收事件。 + /// + public event Action? LinFrameReceived; + + private bool _dbcDecodeEnabled; + /// + /// 是否启用 DBC 解码更新(接收帧触发)。 + /// + public bool DbcDecodeEnabled + { + get { return _dbcDecodeEnabled; } + set { _dbcDecodeEnabled = value; RaisePropertyChanged(); } + } + + private bool _isCycleSend; + /// + /// 是否启用“事件驱动发送”。 + /// + /// + /// 与现有 ToomossCan/ToomossCanFD 行为对齐:只有 IsCycleSend=true 且 SchEnable=true 时, + /// 才会在 CmdDataChanged 事件中触发增量下发。 + /// + public bool IsCycleSend + { + get { return _isCycleSend; } + set { _isCycleSend = value; RaisePropertyChanged(); } + } + + private bool _schEnable; + /// + /// 发送使能(与 UI 的调度表使能语义对齐)。 + /// + public bool SchEnable + { + get { return _schEnable; } + set { _schEnable = value; RaisePropertyChanged(); } + } + + /// + /// 打开设备(不初始化 CAN/LIN 通道)。 + /// + /// 设备索引(通常 0)。 + public void OpenDevice(uint deviceIndex) + { + ThrowIfDisposed(); + + lock (_sync) + { + if (_deviceHandle != IntPtr.Zero) + { + OpenState = true; + return; + } + + EnsureNativeDllExists("zlgcan.dll"); + + _deviceHandle = ZLGCAN.ZCAN_OpenDevice(ZLGCAN.ZCAN_USBCANFD_200U, deviceIndex, 0); + if (_deviceHandle == IntPtr.Zero) + { + throw new InvalidOperationException("ZCAN_OpenDevice 失败,请确认驱动/设备连接/程序位数与 DLL 匹配。"); + } + + OpenState = true; + } + } + + /// + /// 打开设备并初始化 CANFD 通道。 + /// + /// 设备索引(通常 0)。 + /// 通道0配置。 + /// 通道1配置(可为 null 表示不初始化)。 + public void OpenAndInitCan(uint deviceIndex, ZlgCanFdChannelOptions channel0, ZlgCanFdChannelOptions? channel1 = null) + { + ThrowIfDisposed(); + + lock (_sync) + { + if (_deviceHandle == IntPtr.Zero) + { + OpenDevice(deviceIndex); + } + + try + { + InitCanChannelInternal(0, channel0); + _canChannelHandles[0] = StartCanChannelInternal(0); + + if (channel1 != null) + { + InitCanChannelInternal(1, channel1); + _canChannelHandles[1] = StartCanChannelInternal(1); + } + + // 合并接收:这是设备级能力,按通道0的配置为准(两通道同时开启/关闭) + var mergeEnable = channel0.EnableMergeReceive; + var mergeRet = ZLGCAN.ZCAN_SetValue(_deviceHandle, "0/set_device_recv_merge", mergeEnable ? "1" : "0"); + if (mergeRet != 1) + { + _log.Warn("设置合并接收失败(0/set_device_recv_merge)。将继续运行,但接收模式可能不符合预期。"); + } + } + catch + { + SafeClose_NoLock(); + throw; + } + } + } + + /// + /// 初始化并启动 LIN 通道。 + /// + /// LIN 通道索引(通常 0)。 + /// LIN 初始化参数。 + public void OpenAndInitLin(uint linIndex, ZlgLinChannelOptions options) + { + ThrowIfDisposed(); + + lock (_sync) + { + if (_deviceHandle == IntPtr.Zero) + { + OpenDevice(0); + } + + if (_linChannelHandle != IntPtr.Zero) + { + throw new InvalidOperationException("LIN 通道已初始化。"); + } + + var cfg = new ZLGCAN.ZCAN_LIN_INIT_CONFIG + { + linMode = (byte)(options.IsMaster ? 1 : 0), + chkSumMode = options.ChecksumMode, + maxLength = options.MaxLength, + reserved = 0, + libBaud = options.BaudRate + }; + + IntPtr pCfg = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_LIN_INIT_CONFIG))); + try + { + Marshal.StructureToPtr(cfg, pCfg, false); + _linChannelHandle = ZLGCAN.ZCAN_InitLIN(_deviceHandle, linIndex, pCfg); + } + finally + { + Marshal.FreeHGlobal(pCfg); + } + + if (_linChannelHandle == IntPtr.Zero) + { + throw new InvalidOperationException("ZCAN_InitLIN 失败。"); + } + + var ret = ZLGCAN.ZCAN_StartLIN(_linChannelHandle); + if (ret != 1) + { + throw new InvalidOperationException("ZCAN_StartLIN 失败。"); + } + } + } + + /// + /// 启动后台接收线程。 + /// + /// 是否启用合并接收(ZCAN_ReceiveData)。建议与通道初始化时的 EnableMergeReceive 保持一致。 + /// 接收缓冲最大帧数(合并接收固定数组大小,过小会丢帧)。 + public void StartReceiveLoop(bool mergeReceive, int bufferFrames = 100) + { + ThrowIfDisposed(); + + lock (_sync) + { + if (_deviceHandle == IntPtr.Zero) + { + throw new InvalidOperationException("设备未打开。"); + } + + if (_recvTask != null) + { + throw new InvalidOperationException("接收线程已启动。"); + } + + if (bufferFrames <= 0) bufferFrames = 1; + + _recvCts = new CancellationTokenSource(); + var token = _recvCts.Token; + + _recvTask = Task.Run(() => + { + IsReceiving = true; + try + { + if (mergeReceive) + { + ReceiveLoop_Merge(token, bufferFrames); + } + else + { + ReceiveLoop_PerChannel(token, bufferFrames); + } + } + catch (Exception ex) + { + _log.Error($"ZLG 接收线程异常退出:{ex.Message}"); + } + finally + { + IsReceiving = false; + } + }, token); + } + } + + /// + /// 加载 DBC 文件,并生成 UI 绑定用的信号集合。 + /// + /// DBC 文件路径。 + /// 是否启用异步解析。 + /// 是否合并加载。 + /// 协议类型。 + /// CanDbcModel 集合。 + public ObservableCollection StartDbc(string dbcPath, bool enableAsyncAnalyse = true, bool merge = false, byte protocolType = ZDBC.PROTOCOL_OTHER) + { + ThrowIfDisposed(); + + lock (_dbcSync) + { + _dbc?.Dispose(); + _dbc = new ZlgDbcDatabase(_log); + _dbc.Load(dbcPath, enableAsyncAnalyse, merge, protocolType); + + _dbcModels = _dbc.BuildCanDbcModels(); + _dbcModelIndex = BuildDbcModelIndex(_dbcModels); + + DbcDecodeEnabled = true; + + return _dbcModels; + } + } + + /// + /// 设置并订阅要发送的指令集合(事件驱动)。 + /// + /// 指令集合。 + /// 发送通道(0/1)。 + /// 帧类型:ZDBC.FT_CAN 或 ZDBC.FT_CANFD。 + public void LoadCmdDataToDrive(List cmdData, int channelIndex, byte frameType) + { + ThrowIfDisposed(); + + lock (_dbcSync) + { + if (_cmdData != null) + { + foreach (var old in _cmdData) + { + old.CanCmdDataChangedHandler -= CmdData_CanCmdDataChangedHandler; + } + } + + _cmdData = cmdData; + _cmdSendChannelIndex = channelIndex; + _cmdSendFrameType = frameType; + + if (_cmdData != null) + { + foreach (var item in _cmdData) + { + item.CanCmdDataChangedHandler += CmdData_CanCmdDataChangedHandler; + } + } + } + } + + /// + /// 事件驱动回调:某个 MsgName 的信号值变更时,仅增量编码并下发该消息。 + /// + /// 发送方。 + /// 发生变化的消息名称。 + private void CmdData_CanCmdDataChangedHandler(object? sender, string msgName) + { + try + { + if (!IsCycleSend) return; + if (!SchEnable) return; + + SendOneMsgByCmdData(msgName, _cmdSendChannelIndex, _cmdSendFrameType); + } + catch (Exception ex) + { + _log.Warn($"事件驱动发送异常:{ex.Message}"); + } + } + + /// + /// 根据 MsgName,从当前 CmdData 中取出信号值,编码并发送一帧。 + /// + /// 消息名称。 + /// 发送通道。 + /// 帧类型:ZDBC.FT_CAN 或 ZDBC.FT_CANFD。 + public void SendOneMsgByCmdData(string msgName, int channelIndex, byte frameType) + { + ThrowIfDisposed(); + + ZlgDbcDatabase? dbc; + List? cmdData; + lock (_dbcSync) + { + dbc = _dbc; + cmdData = _cmdData; + } + + if (dbc == null || !dbc.IsLoaded) + { + return; + } + + if (cmdData == null || cmdData.Count == 0) + { + return; + } + + if (string.IsNullOrWhiteSpace(msgName)) + { + return; + } + + var dict = new Dictionary(StringComparer.Ordinal); + foreach (var item in cmdData) + { + if (!string.Equals(item.MsgName, msgName, StringComparison.Ordinal)) + { + continue; + } + + if (string.IsNullOrWhiteSpace(item.SignalName)) + { + continue; + } + + dict[item.SignalName] = item.SignalCmdValue; + } + + if (dict.Count == 0) + { + return; + } + + var (canId, data, dataLen) = dbc.EncodeToRawFrame(msgName, dict, frameType); + if (frameType == ZDBC.FT_CAN) + { + SendCan(channelIndex, canId, data, requestTxEcho: true); + return; + } + + if (frameType == ZDBC.FT_CANFD) + { + SendCanFd(channelIndex, canId, data, requestTxEcho: true); + return; + } + + throw new ArgumentOutOfRangeException(nameof(frameType), "frameType 仅支持 ZDBC.FT_CAN=0 或 ZDBC.FT_CANFD=1。"); + } + + /// + /// 停止后台接收线程。 + /// + public void StopReceiveLoop() + { + lock (_sync) + { + if (_recvCts == null || _recvTask == null) + { + return; + } + + try + { + _recvCts.Cancel(); + _recvTask.Wait(TimeSpan.FromSeconds(2)); + } + catch (Exception ex) + { + _log.Warn($"停止接收线程等待超时或异常:{ex.Message}"); + } + finally + { + _recvCts.Dispose(); + _recvCts = null; + _recvTask = null; + } + } + } + + /// + /// 发送 CAN 报文。 + /// + /// 通道索引(0/1)。 + /// 包含扩展帧标志位的 can_id(可用 ZlgCanIdHelper.MakeCanId 生成)。 + /// 数据(0~8字节)。 + /// 是否请求发送回显。 + /// 发送方式,0=正常发送,1=单次发送,2=自发自收,3=单次自发自收。 + public uint SendCan(int channelIndex, uint canId, byte[] data, bool requestTxEcho = false, uint transmitType = 0) + { + ThrowIfDisposed(); + var chn = GetCanChannelHandleOrThrow(channelIndex); + + var frame = new ZLGCAN.can_frame + { + can_id = canId, + can_dlc = (byte)Math.Min(8, data?.Length ?? 0), + __pad = 0, + __res0 = 0, + __res1 = 0, + data = new byte[8] + }; + + if (data != null && data.Length > 0) + { + Array.Copy(data, frame.data, Math.Min(8, data.Length)); + } + + if (requestTxEcho) + { + frame.__pad |= 0x20; + } + + var tx = new ZLGCAN.ZCAN_Transmit_Data + { + frame = frame, + transmit_type = transmitType + }; + + IntPtr pTx = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Transmit_Data))); + try + { + Marshal.StructureToPtr(tx, pTx, false); + var ret = ZLGCAN.ZCAN_Transmit(chn, pTx, 1); + if (ret > 0) + { + MarkSendOk(); + } + return ret; + } + finally + { + Marshal.FreeHGlobal(pTx); + } + } + + /// + /// 发送 CANFD 报文。 + /// + /// 通道索引(0/1)。 + /// 包含扩展帧标志位的 can_id(可用 ZlgCanIdHelper.MakeCanId 生成)。 + /// 数据(0~64字节)。 + /// 是否请求发送回显。 + /// 发送方式,0=正常发送,1=单次发送,2=自发自收,3=单次自发自收。 + public uint SendCanFd(int channelIndex, uint canId, byte[] data, bool requestTxEcho = false, uint transmitType = 0) + { + ThrowIfDisposed(); + var chn = GetCanChannelHandleOrThrow(channelIndex); + + var len = (byte)Math.Min(64, data?.Length ?? 0); + var frame = new ZLGCAN.canfd_frame + { + can_id = canId, + len = len, + flags = 0, + __res0 = 0, + __res1 = 0, + data = new byte[64] + }; + + if (data != null && data.Length > 0) + { + Array.Copy(data, frame.data, Math.Min(64, data.Length)); + } + + if (requestTxEcho) + { + frame.flags |= 0x20; + } + + var tx = new ZLGCAN.ZCAN_TransmitFD_Data + { + frame = frame, + transmit_type = transmitType + }; + + IntPtr pTx = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_TransmitFD_Data))); + try + { + Marshal.StructureToPtr(tx, pTx, false); + var ret = ZLGCAN.ZCAN_TransmitFD(chn, pTx, 1); + if (ret > 0) + { + MarkSendOk(); + } + return ret; + } + finally + { + Marshal.FreeHGlobal(pTx); + } + } + + /// + /// 设置设备定时发送(CAN)。 + /// + /// 通道索引。 + /// 定时任务索引。 + /// 是否使能。 + /// 周期(ms)。 + /// can_id。 + /// 数据(0~8)。 + public void ConfigureAutoSendCan(int channelIndex, ushort taskIndex, bool enable, uint intervalMs, uint canId, byte[] data) + { + ThrowIfDisposed(); + + if (_deviceHandle == IntPtr.Zero) + { + throw new InvalidOperationException("设备未打开。"); + } + + var frame = new ZLGCAN.can_frame + { + can_id = canId, + can_dlc = (byte)Math.Min(8, data?.Length ?? 0), + __pad = 0x20, // 默认发送回显 + __res0 = 0, + __res1 = 0, + data = new byte[8] + }; + + if (data != null && data.Length > 0) + { + Array.Copy(data, frame.data, Math.Min(8, data.Length)); + } + + var obj = new ZLGCAN.ZCAN_AUTO_TRANSMIT_OBJ + { + enable = (ushort)(enable ? 1 : 0), + index = taskIndex, + interval = intervalMs, + obj = new ZLGCAN.ZCAN_Transmit_Data { frame = frame, transmit_type = 0 } + }; + + var path = string.Format("{0}/auto_send", channelIndex); + var ret = ZLGCAN.ZCAN_SetValue(_deviceHandle, path, ref obj); + if (ret != 1) + { + throw new InvalidOperationException($"配置定时发送失败:{path}"); + } + } + + /// + /// 启动通道定时发送任务(apply_auto_send)。 + /// + /// 通道索引。 + public void ApplyAutoSend(int channelIndex) + { + ThrowIfDisposed(); + if (_deviceHandle == IntPtr.Zero) throw new InvalidOperationException("设备未打开。"); + + var path = string.Format("{0}/apply_auto_send", channelIndex); + var ret = ZLGCAN.ZCAN_SetValue(_deviceHandle, path, "0"); + if (ret != 1) + { + throw new InvalidOperationException($"启动定时发送失败:{path}"); + } + } + + /// + /// 清空通道定时发送任务(clear_auto_send)。 + /// + /// 通道索引。 + public void ClearAutoSend(int channelIndex) + { + ThrowIfDisposed(); + if (_deviceHandle == IntPtr.Zero) throw new InvalidOperationException("设备未打开。"); + + var path = string.Format("{0}/clear_auto_send", channelIndex); + var ret = ZLGCAN.ZCAN_SetValue(_deviceHandle, path, "0"); + if (ret != 1) + { + throw new InvalidOperationException($"清空定时发送失败:{path}"); + } + } + + /// + /// 配置 LIN 发布表。 + /// + /// 发布配置集合。 + public void SetLinPublish(IEnumerable publishCfg) + { + ThrowIfDisposed(); + if (_linChannelHandle == IntPtr.Zero) throw new InvalidOperationException("LIN 未初始化。"); + + var list = publishCfg?.ToList() ?? new List(); + if (list.Count == 0) + { + return; + } + + int size = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_LIN_PUBLISH_CFG)); + IntPtr pArr = Marshal.AllocHGlobal(size * list.Count); + try + { + for (int i = 0; i < list.Count; i++) + { + var item = list[i]; + if (item.data == null || item.data.Length != 8) + { + item.data = new byte[8]; + } + + if (item.reserved == null || item.reserved.Length != 5) + { + item.reserved = new byte[5]; + } + + Marshal.StructureToPtr(item, IntPtr.Add(pArr, i * size), false); + } + + var ret = ZLGCAN.ZCAN_SetLINPublish(_linChannelHandle, pArr, (uint)list.Count); + if (ret != 1) + { + throw new InvalidOperationException("ZCAN_SetLINPublish 失败。"); + } + } + finally + { + Marshal.FreeHGlobal(pArr); + } + } + + /// + /// 配置 LIN 订阅表。 + /// + /// 订阅配置集合。 + public void SetLinSubscribe(IEnumerable subscribeCfg) + { + ThrowIfDisposed(); + if (_linChannelHandle == IntPtr.Zero) throw new InvalidOperationException("LIN 未初始化。"); + + var list = subscribeCfg?.ToList() ?? new List(); + if (list.Count == 0) + { + return; + } + + int size = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_LIN_SUBSCIBE_CFG)); + IntPtr pArr = Marshal.AllocHGlobal(size * list.Count); + try + { + for (int i = 0; i < list.Count; i++) + { + var item = list[i]; + if (item.reserved == null || item.reserved.Length != 5) + { + item.reserved = new byte[5]; + } + Marshal.StructureToPtr(item, IntPtr.Add(pArr, i * size), false); + } + + var ret = ZLGCAN.ZCAN_SetLINSubscribe(_linChannelHandle, pArr, (uint)list.Count); + if (ret != 1) + { + throw new InvalidOperationException("ZCAN_SetLINSubscribe 失败。"); + } + } + finally + { + Marshal.FreeHGlobal(pArr); + } + } + + /// + /// 关闭设备。 + /// + public void Close() + { + lock (_sync) + { + StopReceiveLoop(); + SafeClose_NoLock(); + } + + lock (_dbcSync) + { + if (_cmdData != null) + { + foreach (var item in _cmdData) + { + item.CanCmdDataChangedHandler -= CmdData_CanCmdDataChangedHandler; + } + } + + _cmdData = null; + + _dbcModels = null; + _dbcModelIndex = null; + + _dbc?.Dispose(); + _dbc = null; + } + } + + /// + public void Dispose() + { + if (_disposed) return; + _disposed = true; + + try + { + Close(); + } + catch + { + // ignored + } + } + + private void SafeClose_NoLock() + { + try + { + if (_linChannelHandle != IntPtr.Zero) + { + var ret = ZLGCAN.ZCAN_ResetLIN(_linChannelHandle); + if (ret != 1) + { + _log.Warn("ZCAN_ResetLIN 失败。"); + } + } + } + catch (Exception ex) + { + _log.Warn($"关闭 LIN 异常:{ex.Message}"); + } + finally + { + _linChannelHandle = IntPtr.Zero; + } + + for (int i = 0; i < _canChannelHandles.Length; i++) + { + try + { + if (_canChannelHandles[i] != IntPtr.Zero) + { + var ret = ZLGCAN.ZCAN_ResetCAN(_canChannelHandles[i]); + if (ret != 1) + { + _log.Warn($"ZCAN_ResetCAN 失败,通道{i}。"); + } + } + } + catch (Exception ex) + { + _log.Warn($"关闭 CAN 通道{i}异常:{ex.Message}"); + } + finally + { + _canChannelHandles[i] = IntPtr.Zero; + } + } + + try + { + if (_deviceHandle != IntPtr.Zero) + { + var ret = ZLGCAN.ZCAN_CloseDevice(_deviceHandle); + if (ret != 1) + { + _log.Warn("ZCAN_CloseDevice 失败。"); + } + } + } + catch (Exception ex) + { + _log.Warn($"关闭设备异常:{ex.Message}"); + } + finally + { + _deviceHandle = IntPtr.Zero; + OpenState = false; + } + } + + /// + /// 初始化指定 CANFD 通道(仅设置参数并 InitCAN,不负责 StartCAN)。 + /// + /// 通道索引。 + /// 通道初始化参数。 + + private void InitCanChannelInternal(int chnIdx, ZlgCanFdChannelOptions options) + { + var path = string.Format("{0}/canfd_abit_baud_rate", chnIdx); + var ret = ZLGCAN.ZCAN_SetValue(_deviceHandle, path, options.ArbitrationBaudRate.ToString()); + if (ret != 1) + { + throw new InvalidOperationException($"设置仲裁域波特率失败:{path}"); + } + + path = string.Format("{0}/canfd_dbit_baud_rate", chnIdx); + ret = ZLGCAN.ZCAN_SetValue(_deviceHandle, path, options.DataBaudRate.ToString()); + if (ret != 1) + { + throw new InvalidOperationException($"设置数据域波特率失败:{path}"); + } + + path = string.Format("{0}/initenal_resistance", chnIdx); + ret = ZLGCAN.ZCAN_SetValue(_deviceHandle, path, options.EnableInternalResistance ? "1" : "0"); + if (ret != 1) + { + throw new InvalidOperationException($"设置终端电阻失败:{path}"); + } + + if (options.EnableBusUsage) + { + path = string.Format("{0}/set_bus_usage_enable", chnIdx); + ret = ZLGCAN.ZCAN_SetValue(_deviceHandle, path, "1"); + if (ret != 1) + { + _log.Warn($"启用总线利用率失败:{path}"); + } + + path = string.Format("{0}/set_bus_usage_period", chnIdx); + ret = ZLGCAN.ZCAN_SetValue(_deviceHandle, path, options.BusUsagePeriodMs.ToString()); + if (ret != 1) + { + _log.Warn($"设置总线利用率周期失败:{path}"); + } + } + + var initCfg = new ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG + { + can_type = 1, + config = new ZLGCAN._ZCAN_CHANNEL_INIT_CONFIG + { + canfd = new ZLGCAN._ZCAN_CHANNEL_CANFD_INIT_CONFIG + { + acc_code = 0, + acc_mask = 0xFFFFFFFF, + abit_timing = 0, + dbit_timing = 0, + brp = 0, + filter = 0, + mode = (byte)(options.ListenOnly ? 1 : 0), + pad = 0, + reserved = 0 + } + } + }; + + var chnHandle = ZLGCAN.ZCAN_InitCAN(_deviceHandle, (uint)chnIdx, ref initCfg); + if (chnHandle == IntPtr.Zero) + { + throw new InvalidOperationException($"初始化 CANFD 通道失败:{chnIdx}"); + } + + _canChannelHandles[chnIdx] = chnHandle; + } + + /// + /// 启动指定 CAN 通道。 + /// + /// 通道索引。 + /// 通道句柄。 + + private IntPtr StartCanChannelInternal(int chnIdx) + { + var h = _canChannelHandles[chnIdx]; + if (h == IntPtr.Zero) + { + throw new InvalidOperationException("CAN 通道句柄为空。"); + } + + var ret = ZLGCAN.ZCAN_StartCAN(h); + if (ret != 1) + { + throw new InvalidOperationException($"启动 CAN 通道失败:{chnIdx}"); + } + + return h; + } + + /// + /// 获取 CAN 通道句柄(不存在则抛异常)。 + /// + /// 通道索引。 + /// 通道句柄。 + + private IntPtr GetCanChannelHandleOrThrow(int channelIndex) + { + if (channelIndex < 0 || channelIndex >= _canChannelHandles.Length) + { + throw new ArgumentOutOfRangeException(nameof(channelIndex)); + } + + var h = _canChannelHandles[channelIndex]; + if (h == IntPtr.Zero) + { + throw new InvalidOperationException($"CAN 通道{channelIndex}未初始化。"); + } + + return h; + } + + /// + /// 普通接收模式:按通道轮询 CAN/CANFD 缓冲区读取。 + /// + /// 取消令牌。 + /// 每次接收的最大帧数。 + + private void ReceiveLoop_PerChannel(CancellationToken token, int bufferFrames) + { + const int WaitMs = 10; + + int canStructSize = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Receive_Data)); + int canfdStructSize = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_ReceiveFD_Data)); + + IntPtr pCanBuffer = Marshal.AllocHGlobal(canStructSize * bufferFrames); + IntPtr pCanfdBuffer = Marshal.AllocHGlobal(canfdStructSize * bufferFrames); + + try + { + while (!token.IsCancellationRequested) + { + for (int ch = 0; ch < _canChannelHandles.Length; ch++) + { + var handle = _canChannelHandles[ch]; + if (handle == IntPtr.Zero) continue; + + uint canNum = ZLGCAN.ZCAN_GetReceiveNum(handle, 0); + if (canNum > 0) + { + uint actual = ZLGCAN.ZCAN_Receive(handle, pCanBuffer, (uint)bufferFrames, WaitMs); + for (int i = 0; i < actual; i++) + { + IntPtr pCur = IntPtr.Add(pCanBuffer, i * canStructSize); + var rec = Marshal.PtrToStructure(pCur); + RaiseCanFrame(ch, rec.timestamp, false, rec.frame.can_id, rec.frame.data, rec.frame.can_dlc, (rec.frame.__pad & 0x20) == 0x20); + } + } + + uint canfdNum = ZLGCAN.ZCAN_GetReceiveNum(handle, 1); + if (canfdNum > 0) + { + uint actual = ZLGCAN.ZCAN_ReceiveFD(handle, pCanfdBuffer, (uint)bufferFrames, WaitMs); + for (int i = 0; i < actual; i++) + { + IntPtr pCur = IntPtr.Add(pCanfdBuffer, i * canfdStructSize); + var rec = Marshal.PtrToStructure(pCur); + RaiseCanFrame(ch, rec.timestamp, true, rec.frame.can_id, rec.frame.data, rec.frame.len, (rec.frame.flags & 0x20) == 0x20); + } + } + + ZLGCAN.ZCAN_CHANNEL_ERR_INFO err = new ZLGCAN.ZCAN_CHANNEL_ERR_INFO + { + error_code = 0, + passive_ErrData = new byte[3], + arLost_ErrData = 0 + }; + + try + { + ZLGCAN.ZCAN_ReadChannelErrInfo(handle, ref err); + if (err.error_code != 0) + { + _log.Warn($"CAN通道{ch}错误码:0x{err.error_code:X}"); + } + } + catch + { + // ignore + } + } + + Thread.Sleep(10); + } + } + finally + { + Marshal.FreeHGlobal(pCanfdBuffer); + Marshal.FreeHGlobal(pCanBuffer); + } + } + + /// + /// 合并接收模式:通过设备级 API(ZCAN_ReceiveData)统一接收 CAN/CANFD/LIN。 + /// + /// 取消令牌。 + /// 接收缓存容量。 + + private void ReceiveLoop_Merge(CancellationToken token, int bufferFrames) + { + int dataObjSize = Marshal.SizeOf(typeof(ZLGCAN.ZCANDataObj)); + int canfdSize = Marshal.SizeOf(typeof(ZLGCAN.ZCANCANFDData)); + int linSize = Marshal.SizeOf(typeof(ZLGCAN.ZCANLINData)); + + IntPtr pDataObjs = Marshal.AllocHGlobal(dataObjSize * bufferFrames); + IntPtr pCanfdBuffer = Marshal.AllocHGlobal(canfdSize); + IntPtr pLinBuffer = Marshal.AllocHGlobal(linSize); + + try + { + while (!token.IsCancellationRequested) + { + uint recvNum = ZLGCAN.ZCAN_GetReceiveNum(_deviceHandle, 2); + if (recvNum == 0) + { + Thread.Sleep(10); + continue; + } + + uint actualRecv = ZLGCAN.ZCAN_ReceiveData(_deviceHandle, pDataObjs, (uint)bufferFrames, 10); + if (actualRecv == 0) + { + continue; + } + + for (int i = 0; i < actualRecv; i++) + { + IntPtr pCur = IntPtr.Add(pDataObjs, i * dataObjSize); + var obj = Marshal.PtrToStructure(pCur); + + switch (obj.dataType) + { + case 1: + Marshal.Copy(obj.data, 0, pCanfdBuffer, canfdSize); + var canfdData = Marshal.PtrToStructure(pCanfdBuffer); + bool isFd = (canfdData.flag & 1) == 1; + bool isTx = (canfdData.flag & (1 << 9)) != 0; + RaiseCanFrame(obj.chnl, canfdData.timeStamp, isFd, canfdData.frame.can_id, canfdData.frame.data, canfdData.frame.len, isTx); + break; + + case 4: + if (obj.data == null || obj.data.Length < linSize) + { + break; + } + Marshal.Copy(obj.data, 0, pLinBuffer, linSize); + var linData = Marshal.PtrToStructure(pLinBuffer); + RaiseLinFrame(obj.chnl, linData.rxData.timeStamp, linData.pid.rawVal, linData.rxData.data, linData.rxData.datalen, linData.rxData.dir); + break; + } + } + + Thread.Sleep(1); + } + } + finally + { + Marshal.FreeHGlobal(pLinBuffer); + Marshal.FreeHGlobal(pCanfdBuffer); + Marshal.FreeHGlobal(pDataObjs); + } + } + + /// + /// 将接收到的 CAN/CANFD 帧转换为托管对象并触发事件。 + /// + /// 通道号。 + /// 时间戳(us)。 + /// 是否 CANFD。 + /// can_id(含标志位)。 + /// 原始数据缓冲区。 + /// 数据长度。 + /// 是否为发送回显(Tx)。 + + private void RaiseCanFrame(int channel, ulong timestamp, bool isCanFd, uint canId, byte[] data, byte dlc, bool isTx) + { + try + { + var len = Math.Min(isCanFd ? 64 : 8, Math.Min((int)dlc, data?.Length ?? 0)); + var bytes = new byte[len]; + if (len > 0 && data != null) + { + Array.Copy(data, bytes, len); + } + + CanFrameReceived?.Invoke(new ZlgCanRxFrame(channel, isCanFd, canId, bytes, timestamp, isTx)); + + if (isTx) + { + MarkSendOk(); + } + else + { + MarkReviceOk(); + } + + if (DbcDecodeEnabled) + { + TryDecodeAndUpdateModels(canId, bytes, isCanFd ? (byte)ZDBC.FT_CANFD : (byte)ZDBC.FT_CAN); + } + } + catch (Exception ex) + { + _log.Warn($"派发 CAN 帧事件异常:{ex.Message}"); + } + } + + /// + /// 将接收到的 LIN 帧转换为托管对象并触发事件。 + /// + /// 通道号。 + /// 时间戳(us)。 + /// PID。 + /// 原始数据缓冲区。 + /// 数据长度。 + /// 方向(由设备回传)。 + + private void RaiseLinFrame(int channel, ulong timestamp, byte pid, byte[] data, byte datalen, byte dir) + { + try + { + var len = Math.Min(8, Math.Min((int)datalen, data?.Length ?? 0)); + var bytes = new byte[len]; + if (len > 0 && data != null) + { + Array.Copy(data, bytes, len); + } + + LinFrameReceived?.Invoke(new ZlgLinRxFrame(channel, pid, bytes, timestamp, dir)); + } + catch (Exception ex) + { + _log.Warn($"派发 LIN 帧事件异常:{ex.Message}"); + } + } + + /// + /// 校验指定原生 DLL 是否存在于程序输出目录(AppContext.BaseDirectory)。 + /// + /// DLL 文件名。 + /// 找不到 DLL。 + + private static void EnsureNativeDllExists(string dllName) + { + var baseDir = AppContext.BaseDirectory; + var full = Path.Combine(baseDir, dllName); + if (!File.Exists(full)) + { + throw new FileNotFoundException($"未找到 {dllName},请将其复制到程序输出目录:{baseDir}", full); + } + } + + private static Dictionary BuildDbcModelIndex(IEnumerable models) + { + var dict = new Dictionary(StringComparer.Ordinal); + + foreach (var m in models) + { + if (string.IsNullOrWhiteSpace(m.MsgName) || string.IsNullOrWhiteSpace(m.SignalName)) + { + continue; + } + + dict[BuildMsgSigKey(m.MsgName, m.SignalName)] = m; + } + + return dict; + } + + private static string BuildMsgSigKey(string msgName, string signalName) + { + return msgName + "\u0000" + signalName; + } + + private void TryDecodeAndUpdateModels(uint canId, byte[] payload, byte frameType) + { + ZlgDbcDatabase? dbc; + Dictionary? index; + + lock (_dbcSync) + { + dbc = _dbc; + index = _dbcModelIndex; + } + + if (dbc == null || !dbc.IsLoaded || index == null) + { + return; + } + + try + { + var (msgName, signals) = dbc.DecodeRawFrame(canId, payload, frameType); + if (string.IsNullOrWhiteSpace(msgName) || signals == null || signals.Count == 0) + { + return; + } + + foreach (var kv in signals) + { + var key = BuildMsgSigKey(msgName, kv.Key); + if (index.TryGetValue(key, out var model)) + { + model.SignalRtValue = kv.Value.ToString(); + } + } + } + catch (Exception ex) + { + _log.Warn($"DBC 解码异常:{ex.Message}"); + } + } + + /// + /// 若对象已释放则抛异常。 + /// + private void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(ZlgCanFd200uDriver)); + } + } + } + + /// + /// CAN/CANFD 接收帧。 + /// + public readonly struct ZlgCanRxFrame + { + /// + /// 构造 CAN/CANFD 接收帧。 + /// + /// 通道号。 + /// 是否 CANFD。 + /// can_id(含标志位)。 + /// 数据(已截断为实际长度)。 + /// 时间戳(us)。 + /// 是否为发送回显。 + public ZlgCanRxFrame(int channel, bool isCanFd, uint canId, byte[] data, ulong timestampUs, bool isTx) + { + Channel = channel; + IsCanFd = isCanFd; + CanId = canId; + Data = data; + TimestampUs = timestampUs; + IsTx = isTx; + } + + public int Channel { get; } + public bool IsCanFd { get; } + public uint CanId { get; } + public byte[] Data { get; } + public ulong TimestampUs { get; } + public bool IsTx { get; } + } + + /// + /// LIN 接收帧。 + /// + public readonly struct ZlgLinRxFrame + { + /// + /// 构造 LIN 接收帧。 + /// + /// 通道号。 + /// PID。 + /// 数据(已截断为实际长度)。 + /// 时间戳(us)。 + /// 方向(由设备回传)。 + public ZlgLinRxFrame(int channel, byte pid, byte[] data, ulong timestampUs, byte dir) + { + Channel = channel; + Pid = pid; + Data = data; + TimestampUs = timestampUs; + Dir = dir; + } + + public int Channel { get; } + public byte Pid { get; } + public byte[] Data { get; } + public ulong TimestampUs { get; } + + /// + /// 方向:由设备回传,0/1 具体含义以 ZLG 文档为准。 + /// + public byte Dir { get; } + } +} diff --git a/CapMachine.Wpf/CanDrive/ZlgCan/ZlgCanIdHelper.cs b/CapMachine.Wpf/CanDrive/ZlgCan/ZlgCanIdHelper.cs new file mode 100644 index 0000000..8d0a9cd --- /dev/null +++ b/CapMachine.Wpf/CanDrive/ZlgCan/ZlgCanIdHelper.cs @@ -0,0 +1,62 @@ +using System; + +namespace CapMachine.Wpf.CanDrive.ZlgCan +{ + /// + /// ZLG CAN ID 帮助类。 + /// + public static class ZlgCanIdHelper + { + /// + /// 生成 ZLG/SocketCAN 风格的 can_id(包含扩展帧/远程帧/错误帧标志位)。 + /// + /// CAN 标识符(标准帧 11bit:0~0x7FF;扩展帧 29bit:0~0x1FFFFFFF)。 + /// 是否为扩展帧。 + /// 是否为远程帧(RTR)。 + /// 是否为错误帧。 + /// 包含标志位的 can_id。 + public static uint MakeCanId(uint id, bool isExtended, bool isRemote, bool isError) + { + // 兼容官方样例写法:bit31 扩展帧,bit30 RTR,bit29 ERR + // 注意:该函数不做强约束校验(例如扩展帧 29bit),上层可按需要增加校验。 + uint canId = id & 0x1FFFFFFF; + + if (isExtended) + { + canId |= 1u << 31; + } + + if (isRemote) + { + canId |= 1u << 30; + } + + if (isError) + { + canId |= 1u << 29; + } + + return canId; + } + + /// + /// 从 can_id 中提取 29bit 标识符。 + /// + /// 包含标志位的 can_id。 + /// 29bit 标识符。 + public static uint GetArbitrationId(uint canId) + { + return canId & 0x1FFFFFFF; + } + + /// + /// 判断 can_id 是否为扩展帧。 + /// + /// 包含标志位的 can_id。 + /// 是否扩展帧。 + public static bool IsExtended(uint canId) + { + return (canId & (1u << 31)) != 0; + } + } +} diff --git a/CapMachine.Wpf/CanDrive/ZlgCan/ZlgDbcDatabase.cs b/CapMachine.Wpf/CanDrive/ZlgCan/ZlgDbcDatabase.cs new file mode 100644 index 0000000..7a063c1 --- /dev/null +++ b/CapMachine.Wpf/CanDrive/ZlgCan/ZlgDbcDatabase.cs @@ -0,0 +1,570 @@ +using CapMachine.Wpf.CanDrive; +using CapMachine.Wpf.Services; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace CapMachine.Wpf.CanDrive.ZlgCan +{ + /// + /// 基于周立功 ZDBC(zdbc.dll)的 DBC 数据库封装。 + /// + /// + /// 设计要点: + /// - zdbc.dll 为原生依赖,必须放置在程序输出目录(AppContext.BaseDirectory)。 + /// - 本类封装 DBC 文件加载、消息/信号枚举、以及基于 DBCMessage 的编码/解码能力。 + /// - 运行稳定性:实现 IDisposable,确保 ZDBC_Release 与相关非托管内存释放。 + /// + public sealed class ZlgDbcDatabase : IDisposable + { + private readonly ILogService _log; + private readonly object _sync = new object(); + + private uint _dbcHandle; + private bool _disposed; + + // 复用非托管缓冲,避免高频 Allocate/Free + private IntPtr _msgPtr = IntPtr.Zero; + private IntPtr _countPtr = IntPtr.Zero; + private IntPtr _canFramePtr = IntPtr.Zero; + private IntPtr _canFdFramePtr = IntPtr.Zero; + + private readonly Dictionary _messageByName = new Dictionary(StringComparer.Ordinal); + private readonly Dictionary _messageIdByName = new Dictionary(StringComparer.Ordinal); + + /// + /// 构造函数。 + /// + /// 日志服务。 + public ZlgDbcDatabase(ILogService logService) + { + _log = logService; + } + + /// + /// 是否已加载。 + /// + public bool IsLoaded + { + get + { + return _dbcHandle != 0 && _dbcHandle != ZDBC.INVALID_DBC_HANDLE; + } + } + + /// + /// 加载 DBC 文件。 + /// + /// DBC 文件路径。 + /// 是否启用异步解析(库内部线程)。 + /// 是否合并到当前数据库(支持加载多个文件)。 + /// 协议类型:J1939=0,其他=1。 + public void Load(string dbcPath, bool enableAsyncAnalyse = true, bool merge = false, byte protocolType = ZDBC.PROTOCOL_OTHER) + { + ThrowIfDisposed(); + + if (string.IsNullOrWhiteSpace(dbcPath)) + { + throw new ArgumentException("dbcPath 不能为空。", nameof(dbcPath)); + } + + EnsureNativeDllExists("zdbc.dll"); + + if (!File.Exists(dbcPath)) + { + throw new FileNotFoundException("DBC 文件不存在。", dbcPath); + } + + lock (_sync) + { + if (_dbcHandle == 0 || _dbcHandle == ZDBC.INVALID_DBC_HANDLE) + { + _dbcHandle = ZDBC.ZDBC_Init(0, enableAsyncAnalyse ? (byte)1 : (byte)0); + if (_dbcHandle == ZDBC.INVALID_DBC_HANDLE) + { + _dbcHandle = 0; + throw new InvalidOperationException("ZDBC_Init 初始化失败。"); + } + } + + var fileInfo = new ZDBC.FileInfo + { + strFilePath = BuildFixedPathBytes(dbcPath, ZDBC._MAX_FILE_PATH_ + 1), + type = protocolType, + merge = (byte)(merge ? 1 : 0) + }; + + IntPtr pFile = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.FileInfo))); + try + { + Marshal.StructureToPtr(fileInfo, pFile, false); + var ok = ZDBC.ZDBC_LoadFile(_dbcHandle, pFile); + if (!ok) + { + throw new InvalidOperationException("ZDBC_LoadFile 失败,请检查 DBC 文件格式。"); + } + } + finally + { + Marshal.FreeHGlobal(pFile); + } + + RefreshMessageCache_NoLock(); + } + } + + /// + /// 枚举 DBC 内全部 Message/Signal 元信息,并转换为 UI 使用的 CanDbcModel 集合。 + /// + /// 信号集合。 + public ObservableCollection BuildCanDbcModels() + { + ThrowIfDisposed(); + + if (!IsLoaded) + { + return new ObservableCollection(); + } + + lock (_sync) + { + var list = new List(); + + foreach (var kv in _messageByName) + { + var msg = kv.Value; + var msgName = ByteArrayToAsciiString(msg.strName); + var msgIdStr = "0x" + msg.nID.ToString("X8"); + + var signals = msg.vSignals; + if (signals == null || signals.Length == 0) + { + continue; + } + + var signalCount = (int)Math.Min(msg.nSignalCount, (uint)signals.Length); + for (int i = 0; i < signalCount; i++) + { + var s = signals[i]; + var sigName = ByteArrayToAsciiString(s.strName); + if (string.IsNullOrWhiteSpace(sigName)) + { + continue; + } + + list.Add(new CanDbcModel + { + MsgName = msgName, + MsgId = msgIdStr, + SignalName = sigName, + SignalDesc = ByteArrayToAsciiString(s.strComment), + SignalUnit = ByteArrayToAsciiString(s.unit), + SignalRtValue = "--", + Publisher = string.Empty + }); + } + } + + return new ObservableCollection(list); + } + } + + /// + /// 根据 MsgName 获取 Message ID。 + /// + /// 消息名称。 + /// Message ID。 + public uint GetMessageIdByName(string msgName) + { + ThrowIfDisposed(); + if (string.IsNullOrWhiteSpace(msgName)) return 0; + + lock (_sync) + { + return _messageIdByName.TryGetValue(msgName, out var id) ? id : 0; + } + } + + /// + /// 根据 MsgName + 信号值集合编码为 CAN/CANFD 原始帧。 + /// + /// 消息名称。 + /// 信号值(SignalName -> 实际值)。 + /// 帧类型:FT_CAN=0,FT_CANFD=1。 + /// 编码后的 can_id 与数据。 + public (uint CanId, byte[] Data, int DataLen) EncodeToRawFrame(string msgName, IDictionary signals, byte frameType) + { + ThrowIfDisposed(); + + if (!IsLoaded) + { + throw new InvalidOperationException("DBC 未加载。"); + } + + if (string.IsNullOrWhiteSpace(msgName)) + { + throw new ArgumentException("msgName 不能为空。", nameof(msgName)); + } + + lock (_sync) + { + if (!_messageByName.TryGetValue(msgName, out var msg)) + { + throw new InvalidOperationException($"DBC 中未找到消息:{msgName}"); + } + + // 在副本上修改信号,避免污染缓存 + var workingMsg = msg; + if (workingMsg.vSignals == null) + { + workingMsg.vSignals = new ZDBC.DBCSignal[ZDBC._DBC_SIGNAL_MAX_COUNT_]; + } + + if (signals != null) + { + foreach (var kv in signals) + { + SetSignalActualValue(ref workingMsg, kv.Key, kv.Value); + } + } + + EnsureMarshalBuffers_NoLock(); + + // 写入 msg 到非托管 + Marshal.StructureToPtr(workingMsg, _msgPtr, false); + + // nCount 输入输出参数,准备 1 帧 + Marshal.WriteInt32(_countPtr, 1); + + if (frameType == ZDBC.FT_CAN) + { + var ok = ZDBC.ZDBC_Encode(_dbcHandle, _canFramePtr, _countPtr, _msgPtr, frameType); + if (!ok) + { + throw new InvalidOperationException("ZDBC_Encode(CAN) 失败。"); + } + + var frame = (ZlgNativeCanFrame)Marshal.PtrToStructure(_canFramePtr, typeof(ZlgNativeCanFrame)); + var len = Math.Min(8, (int)frame.can_dlc); + var data = new byte[len]; + if (len > 0 && frame.data != null) + { + Array.Copy(frame.data, data, len); + } + + return (frame.can_id, data, len); + } + + if (frameType == ZDBC.FT_CANFD) + { + var ok = ZDBC.ZDBC_Encode(_dbcHandle, _canFdFramePtr, _countPtr, _msgPtr, frameType); + if (!ok) + { + throw new InvalidOperationException("ZDBC_Encode(CANFD) 失败。"); + } + + var frame = (ZlgNativeCanFdFrame)Marshal.PtrToStructure(_canFdFramePtr, typeof(ZlgNativeCanFdFrame)); + var len = Math.Min(64, (int)frame.len); + var data = new byte[len]; + if (len > 0 && frame.data != null) + { + Array.Copy(frame.data, data, len); + } + + return (frame.can_id, data, len); + } + + throw new ArgumentOutOfRangeException(nameof(frameType), "frameType 仅支持 FT_CAN=0 或 FT_CANFD=1。"); + } + } + + /// + /// 将原始 CAN/CANFD 帧解码为 DBCMessage,并返回消息名称与信号实际值。 + /// + /// can_id。 + /// payload。 + /// 帧类型:FT_CAN=0,FT_CANFD=1。 + /// 解码结果。 + public (string MsgName, Dictionary Signals) DecodeRawFrame(uint canId, byte[] data, byte frameType) + { + ThrowIfDisposed(); + + if (!IsLoaded) + { + throw new InvalidOperationException("DBC 未加载。"); + } + + lock (_sync) + { + EnsureMarshalBuffers_NoLock(); + + if (frameType == ZDBC.FT_CAN) + { + var frame = ZlgNativeCanFrame.Create(canId, data ?? Array.Empty(), requestTxEcho: false); + Marshal.StructureToPtr(frame, _canFramePtr, false); + + var ok = ZDBC.ZDBC_Decode(_dbcHandle, _msgPtr, _canFramePtr, 1, frameType); + if (!ok) + { + return (string.Empty, new Dictionary()); + } + + var msg = Marshal.PtrToStructure(_msgPtr); + return ExtractActualSignals(msg); + } + + if (frameType == ZDBC.FT_CANFD) + { + var frame = ZlgNativeCanFdFrame.Create(canId, data ?? Array.Empty(), requestTxEcho: false); + Marshal.StructureToPtr(frame, _canFdFramePtr, false); + + var ok = ZDBC.ZDBC_Decode(_dbcHandle, _msgPtr, _canFdFramePtr, 1, frameType); + if (!ok) + { + return (string.Empty, new Dictionary()); + } + + var msg = Marshal.PtrToStructure(_msgPtr); + return ExtractActualSignals(msg); + } + + throw new ArgumentOutOfRangeException(nameof(frameType), "frameType 仅支持 FT_CAN=0 或 FT_CANFD=1。"); + } + } + + /// + public void Dispose() + { + if (_disposed) return; + _disposed = true; + + lock (_sync) + { + try + { + if (_dbcHandle != 0 && _dbcHandle != ZDBC.INVALID_DBC_HANDLE) + { + ZDBC.ZDBC_Release(_dbcHandle); + } + } + catch (Exception ex) + { + _log.Warn($"ZDBC_Release 异常:{ex.Message}"); + } + finally + { + _dbcHandle = 0; + } + + FreeMarshalBuffers_NoLock(); + _messageByName.Clear(); + _messageIdByName.Clear(); + } + } + + private void RefreshMessageCache_NoLock() + { + _messageByName.Clear(); + _messageIdByName.Clear(); + + if (!IsLoaded) + { + return; + } + + EnsureMarshalBuffers_NoLock(); + + // 遍历消息 + var msg = new ZDBC.DBCMessage + { + vSignals = new ZDBC.DBCSignal[ZDBC._DBC_SIGNAL_MAX_COUNT_], + strName = new byte[ZDBC._DBC_NAME_LENGTH_ + 1], + strComment = new byte[ZDBC._DBC_COMMENT_MAX_LENGTH_ + 1] + }; + + Marshal.StructureToPtr(msg, _msgPtr, false); + var ok = ZDBC.ZDBC_GetFirstMessage(_dbcHandle, _msgPtr); + while (ok) + { + var m = Marshal.PtrToStructure(_msgPtr); + var name = ByteArrayToAsciiString(m.strName); + if (!string.IsNullOrWhiteSpace(name)) + { + _messageByName[name] = m; + _messageIdByName[name] = m.nID; + } + + Marshal.StructureToPtr(msg, _msgPtr, false); + ok = ZDBC.ZDBC_GetNextMessage(_dbcHandle, _msgPtr); + } + } + + private void EnsureMarshalBuffers_NoLock() + { + if (_msgPtr == IntPtr.Zero) + { + _msgPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCMessage))); + } + + if (_countPtr == IntPtr.Zero) + { + _countPtr = Marshal.AllocHGlobal(sizeof(int)); + } + + if (_canFramePtr == IntPtr.Zero) + { + _canFramePtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZlgNativeCanFrame))); + } + + if (_canFdFramePtr == IntPtr.Zero) + { + _canFdFramePtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZlgNativeCanFdFrame))); + } + } + + private void FreeMarshalBuffers_NoLock() + { + if (_canFdFramePtr != IntPtr.Zero) + { + Marshal.FreeHGlobal(_canFdFramePtr); + _canFdFramePtr = IntPtr.Zero; + } + + if (_canFramePtr != IntPtr.Zero) + { + Marshal.FreeHGlobal(_canFramePtr); + _canFramePtr = IntPtr.Zero; + } + + if (_countPtr != IntPtr.Zero) + { + Marshal.FreeHGlobal(_countPtr); + _countPtr = IntPtr.Zero; + } + + if (_msgPtr != IntPtr.Zero) + { + Marshal.FreeHGlobal(_msgPtr); + _msgPtr = IntPtr.Zero; + } + } + + private static void EnsureNativeDllExists(string dllName) + { + var baseDir = AppContext.BaseDirectory; + var full = Path.Combine(baseDir, dllName); + if (!File.Exists(full)) + { + throw new FileNotFoundException($"未找到 {dllName},请将其复制到程序输出目录:{baseDir}", full); + } + } + + private void SetSignalActualValue(ref ZDBC.DBCMessage msg, string signalName, double actualValue) + { + if (msg.vSignals == null || msg.vSignals.Length == 0) + { + return; + } + + var count = (int)Math.Min(msg.nSignalCount, (uint)msg.vSignals.Length); + for (int i = 0; i < count; i++) + { + var s = msg.vSignals[i]; + var name = ByteArrayToAsciiString(s.strName); + if (!string.Equals(name, signalName, StringComparison.Ordinal)) + { + continue; + } + + // 使用 ZDBC_CalcRawValue 做实际值->原始值转换 + IntPtr pSgl = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCSignal))); + IntPtr pVal = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(double))); + try + { + Marshal.StructureToPtr(s, pSgl, false); + Marshal.StructureToPtr(actualValue, pVal, false); + var raw = ZDBC.ZDBC_CalcRawValue(pSgl, pVal); + msg.vSignals[i].nRawvalue = raw; + } + finally + { + Marshal.FreeHGlobal(pVal); + Marshal.FreeHGlobal(pSgl); + } + + return; + } + } + + private (string MsgName, Dictionary Signals) ExtractActualSignals(ZDBC.DBCMessage msg) + { + var msgName = ByteArrayToAsciiString(msg.strName); + var dict = new Dictionary(StringComparer.Ordinal); + + if (msg.vSignals == null) + { + return (msgName, dict); + } + + var count = (int)Math.Min(msg.nSignalCount, (uint)msg.vSignals.Length); + for (int i = 0; i < count; i++) + { + var s = msg.vSignals[i]; + var name = ByteArrayToAsciiString(s.strName); + if (string.IsNullOrWhiteSpace(name)) + { + continue; + } + + // 使用 ZDBC_CalcActualValue 做原始值->实际值转换 + IntPtr pSgl = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCSignal))); + IntPtr pRaw = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ulong))); + try + { + Marshal.StructureToPtr(s, pSgl, false); + Marshal.StructureToPtr(s.nRawvalue, pRaw, false); + var actual = ZDBC.ZDBC_CalcActualValue(pSgl, pRaw); + dict[name] = actual; + } + finally + { + Marshal.FreeHGlobal(pRaw); + Marshal.FreeHGlobal(pSgl); + } + } + + return (msgName, dict); + } + + private static byte[] BuildFixedPathBytes(string path, int fixedLen) + { + var bytes = Encoding.ASCII.GetBytes(path); + var dst = new byte[fixedLen]; + Array.Clear(dst, 0, dst.Length); + Array.Copy(bytes, 0, dst, 0, Math.Min(bytes.Length, fixedLen - 1)); + return dst; + } + + private static string ByteArrayToAsciiString(byte[]? bytes) + { + if (bytes == null || bytes.Length == 0) return string.Empty; + + int len = Array.IndexOf(bytes, (byte)0); + if (len < 0) len = bytes.Length; + + return Encoding.ASCII.GetString(bytes, 0, len).Trim(); + } + + private void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(ZlgDbcDatabase)); + } + } + } +} diff --git a/CapMachine.Wpf/CanDrive/ZlgCan/ZlgNativeFrames.cs b/CapMachine.Wpf/CanDrive/ZlgCan/ZlgNativeFrames.cs new file mode 100644 index 0000000..0aedf4f --- /dev/null +++ b/CapMachine.Wpf/CanDrive/ZlgCan/ZlgNativeFrames.cs @@ -0,0 +1,103 @@ +using System; +using System.Runtime.InteropServices; + +namespace CapMachine.Wpf.CanDrive.ZlgCan +{ + /// + /// 供 DBC 编解码/Marshal 使用的原生 CAN 帧结构(与 zlgcan can_frame 二进制布局兼容)。 + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZlgNativeCanFrame + { + public uint can_id; + public byte can_dlc; + public byte __pad; + public byte __res0; + public byte __res1; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] data; + + /// + /// 创建 CAN 帧。 + /// + /// can_id(包含扩展帧等标志位)。 + /// 数据(0~8)。 + /// 是否请求发送回显。 + /// 原生 CAN 帧。 + public static ZlgNativeCanFrame Create(uint canId, byte[] payload, bool requestTxEcho) + { + var len = Math.Min(8, payload?.Length ?? 0); + var frame = new ZlgNativeCanFrame + { + can_id = canId, + can_dlc = (byte)len, + __pad = 0, + __res0 = 0, + __res1 = 0, + data = new byte[8] + }; + + if (len > 0 && payload != null) + { + Array.Copy(payload, frame.data, len); + } + + if (requestTxEcho) + { + frame.__pad |= 0x20; + } + + return frame; + } + } + + /// + /// 供 DBC 编解码/Marshal 使用的原生 CANFD 帧结构(与 zlgcan canfd_frame 二进制布局兼容)。 + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZlgNativeCanFdFrame + { + public uint can_id; + public byte len; + public byte flags; + public byte __res0; + public byte __res1; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public byte[] data; + + /// + /// 创建 CANFD 帧。 + /// + /// can_id(包含扩展帧等标志位)。 + /// 数据(0~64)。 + /// 是否请求发送回显。 + /// 原生 CANFD 帧。 + public static ZlgNativeCanFdFrame Create(uint canId, byte[] payload, bool requestTxEcho) + { + var len = Math.Min(64, payload?.Length ?? 0); + var frame = new ZlgNativeCanFdFrame + { + can_id = canId, + len = (byte)len, + flags = 0, + __res0 = 0, + __res1 = 0, + data = new byte[64] + }; + + if (len > 0 && payload != null) + { + Array.Copy(payload, frame.data, len); + } + + if (requestTxEcho) + { + frame.flags |= 0x20; + } + + return frame; + } + } +} diff --git a/CapMachine.Wpf/CanDrive/ZlgCan/接口函数使用手册.pdf b/CapMachine.Wpf/CanDrive/ZlgCan/接口函数使用手册.pdf new file mode 100644 index 0000000..4e68bec Binary files /dev/null and b/CapMachine.Wpf/CanDrive/ZlgCan/接口函数使用手册.pdf differ diff --git a/CapMachine.Wpf/Services/NavigationMenuService.cs b/CapMachine.Wpf/Services/NavigationMenuService.cs index ab57060..e78e371 100644 --- a/CapMachine.Wpf/Services/NavigationMenuService.cs +++ b/CapMachine.Wpf/Services/NavigationMenuService.cs @@ -79,6 +79,8 @@ namespace CapMachine.Wpf.Services MenuItems.Add(new NavigationItem("", "CAN配置", "CANConfigView")); MenuItems.Add(new NavigationItem("", "CANFD配置", "CANFDConfigView")); MenuItems.Add(new NavigationItem("", "LIN配置", "LINConfigView")); + MenuItems.Add(new NavigationItem("", "ZLG CAN配置", "ZlgCanDriveConfigView")); + MenuItems.Add(new NavigationItem("", "ZLG LIN配置", "ZlgLinDriveConfigView")); MenuItems.Add(new NavigationItem("", "工艺参数", "ProConfigView")); MenuItems.Add(new NavigationItem("", "工艺曲线", "RealTimeChartView")); MenuItems.Add(new NavigationItem("", "动作日志", "ActionLogView")); diff --git a/CapMachine.Wpf/Services/ZlgCanDriveService.cs b/CapMachine.Wpf/Services/ZlgCanDriveService.cs new file mode 100644 index 0000000..2dce0d0 --- /dev/null +++ b/CapMachine.Wpf/Services/ZlgCanDriveService.cs @@ -0,0 +1,320 @@ +using CapMachine.Model.CANLIN; +using CapMachine.Wpf.CanDrive; +using CapMachine.Wpf.CanDrive.ZlgCan; +using ImTools; +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace CapMachine.Wpf.Services +{ + /// + /// ZLG CAN/CANFD 驱动服务(共享设备句柄)。 + /// + public sealed class ZlgCanDriveService : BindableBase + { + private readonly ILogService _log; + + /// + /// 共享的 ZLG 驱动实例(CAN/CANFD/LIN)。 + /// + public ZlgCanFd200uDriver Driver { get; } + + /// + /// 当前选中的配置程序(沿用原有 FreeSql 模型)。 + /// + public CanLinConfigPro? SelectedCanLinConfigPro { get; set; } + + /// + /// Dbc 消息集合(用于 UI 绑定)。 + /// + public ObservableCollection ListCanDbcModel { get; private set; } = new ObservableCollection(); + + private bool _dbcParserState; + /// + /// DBC 解析状态。 + /// + public bool DbcParserState + { + get { return _dbcParserState; } + private set { _dbcParserState = value; RaisePropertyChanged(); } + } + + private bool _openState; + /// + /// 设备打开状态。 + /// + public bool OpenState + { + get { return _openState; } + private set { _openState = value; RaisePropertyChanged(); } + } + + private ZlgCanMode _mode; + /// + /// CAN 模式(单选:CAN 或 CANFD)。 + /// + public ZlgCanMode Mode + { + get { return _mode; } + set { _mode = value; RaisePropertyChanged(); } + } + + /// + /// 发送使能(与 UI 的调度表使能语义对齐)。 + /// + public bool SchEnable + { + get { return Driver.SchEnable; } + set { Driver.SchEnable = value; RaisePropertyChanged(); } + } + + /// + /// 是否启用事件驱动发送。 + /// + public bool IsCycleSend + { + get { return Driver.IsCycleSend; } + set { Driver.IsCycleSend = value; RaisePropertyChanged(); } + } + + /// + /// 是否正在循环接收(对齐 Toomoss:IsCycleRevice)。 + /// + public bool IsCycleRevice + { + get { return Driver.IsReceiving; } + } + + /// + /// 最近是否发送成功(用于 UI 指示)。 + /// + public bool IsSendOk + { + get { return Driver.IsSendOk; } + } + + /// + /// 最近是否接收成功(用于 UI 指示)。 + /// + public bool IsReviceOk + { + get { return Driver.IsReviceOk; } + } + + /// + /// 要发送的 CAN 指令数据。 + /// + public List CmdData { get; } = new List(); + + private CanCmdData? SpeedCanCmdData { get; set; } + + private uint _deviceIndex = 0; + private ZlgCanFdChannelOptions _channel0 = new ZlgCanFdChannelOptions(); + + /// + /// 构造。 + /// + /// 日志服务。 + public ZlgCanDriveService(ILogService logService) + { + _log = logService; + Driver = new ZlgCanFd200uDriver(logService); + + Driver.PropertyChanged += (_, __) => + { + OpenState = Driver.OpenState; + RaisePropertyChanged(nameof(IsCycleRevice)); + RaisePropertyChanged(nameof(IsSendOk)); + RaisePropertyChanged(nameof(IsReviceOk)); + }; + + OpenState = Driver.OpenState; + Mode = ZlgCanMode.Can; + } + + /// + /// 初始化 CAN 配置信息,并将配置中的名称映射到 DBC 信号集合(用于 UI 显示)。 + /// + /// 选中的配置。 + public void InitCanConfig(CanLinConfigPro selectedCanLinConfigPro) + { + SelectedCanLinConfigPro = selectedCanLinConfigPro; + if (SelectedCanLinConfigPro?.CanLinConfigContents == null) return; + + foreach (var item in SelectedCanLinConfigPro.CanLinConfigContents) + { + var find = ListCanDbcModel.FindFirst(a => a.SignalName == item.SignalName); + if (find != null) + { + find.Name = item.Name; + } + } + } + + /// + /// 更新配置(从 DTO/DB 同步到驱动)。 + /// + /// 设备索引。 + /// 仲裁波特率(bps)。 + /// 数据波特率(bps)。 + /// 终端电阻使能。 + /// 是否合并接收。 + /// 合并接收缓冲帧数。 + public void UpdateConfig(uint deviceIndex, uint arbBaudRate, uint dataBaudRate, bool resEnable, bool mergeReceive = false, int mergeReceiveBufferFrames = 100) + { + _deviceIndex = deviceIndex; + _channel0.ArbitrationBaudRate = arbBaudRate; + _channel0.DataBaudRate = dataBaudRate; + _channel0.EnableInternalResistance = resEnable; + _channel0.EnableMergeReceive = mergeReceive; + _channel0.MergeReceiveBufferFrames = mergeReceiveBufferFrames; + } + + /// + /// 打开 CAN/CANFD(按 Mode)。 + /// + public void StartCanDrive() + { + if (OpenState) + { + return; + } + + Driver.OpenAndInitCan(_deviceIndex, _channel0); + Driver.StartReceiveLoop(_channel0.EnableMergeReceive, _channel0.MergeReceiveBufferFrames); + OpenState = Driver.OpenState; + } + + /// + /// 使能/停止循环接收。 + /// + /// true=启动接收线程,false=停止接收线程。 + public void SetReceiveEnabled(bool enable) + { + if (!OpenState) + { + throw new InvalidOperationException("设备未连接,无法切换循环接收。"); + } + + if (enable) + { + if (!Driver.IsReceiving) + { + Driver.StartReceiveLoop(_channel0.EnableMergeReceive, _channel0.MergeReceiveBufferFrames); + } + } + else + { + if (Driver.IsReceiving) + { + Driver.StopReceiveLoop(); + } + } + + RaisePropertyChanged(nameof(IsCycleRevice)); + } + + /// + /// 关闭设备(会同时关闭共享的 LIN 通道)。 + /// + public void CloseDevice() + { + try + { + Driver.Close(); + } + finally + { + OpenState = Driver.OpenState; + DbcParserState = false; + } + } + + /// + /// 加载 DBC。 + /// + /// DBC 路径。 + public ObservableCollection StartDbc(string path) + { + if (!OpenState) + { + throw new InvalidOperationException("请先打开设备后再加载 DBC。"); + } + + ListCanDbcModel = Driver.StartDbc(path); + DbcParserState = ListCanDbcModel != null && ListCanDbcModel.Count > 0; + return ListCanDbcModel; + } + + /// + /// 加载要发送的数据(订阅数据变化事件)。 + /// + /// 指令数据集合。 + public void LoadCmdDataToDrive(List cmdData) + { + var list = cmdData ?? new List(); + + CmdData.Clear(); + CmdData.AddRange(list); + + foreach (var item in CmdData) + { + if (string.Equals(item.ConfigName, "转速", StringComparison.Ordinal)) + { + SpeedCanCmdData = item; + } + } + + Driver.LoadCmdDataToDrive(CmdData, channelIndex: 0, frameType: Mode == ZlgCanMode.Can ? (byte)ZDBC.FT_CAN : (byte)ZDBC.FT_CANFD); + } + + /// + /// 手动发送(目前对齐原服务:仅发送转速)。 + /// + /// 转速。 + public void SendMsgToCanDrive(double speedData) + { + if (!OpenState) + { + return; + } + + if (SpeedCanCmdData != null) + { + SpeedCanCmdData.SignalCmdValue = speedData; + } + else + { + _log.Warn("未配置转速指令项(ConfigName=转速),忽略手动发送。"); + } + + // 若未启用事件驱动发送,则这里主动发送一次(与旧行为兼容) + if (!IsCycleSend || !SchEnable) + { + var firstMsg = CmdData.FirstOrDefault()?.MsgName; + if (!string.IsNullOrWhiteSpace(firstMsg)) + { + Driver.SendOneMsgByCmdData(firstMsg, 0, Mode == ZlgCanMode.Can ? (byte)ZDBC.FT_CAN : (byte)ZDBC.FT_CANFD); + } + } + } + } + + /// + /// ZLG CAN 工作模式。 + /// + public enum ZlgCanMode + { + /// + /// CAN 经典帧。 + /// + Can = 0, + /// + /// CAN FD。 + /// + CanFd = 1 + } +} diff --git a/CapMachine.Wpf/Services/ZlgLinDriveService.cs b/CapMachine.Wpf/Services/ZlgLinDriveService.cs new file mode 100644 index 0000000..4b6818f --- /dev/null +++ b/CapMachine.Wpf/Services/ZlgLinDriveService.cs @@ -0,0 +1,157 @@ +using CapMachine.Model.CANLIN; +using CapMachine.Wpf.LinDrive; +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace CapMachine.Wpf.Services +{ + /// + /// ZLG LIN 驱动服务(共享设备句柄)。 + /// + public sealed class ZlgLinDriveService : BindableBase + { + private readonly ILogService _log; + private readonly ZlgCanDriveService _zlgCanDriveService; + + /// + /// 当前选中的配置程序(沿用原有 FreeSql 模型)。 + /// + public CanLinConfigPro? SelectedCanLinConfigPro { get; set; } + + private bool _openState; + /// + /// LIN 打开状态。 + /// + public bool OpenState + { + get { return _openState; } + private set { _openState = value; RaisePropertyChanged(); } + } + + private bool _ldfParserState; + /// + /// LDF 解析状态(ZLG LIN 暂未接入 LDF,固定为 false)。 + /// + public bool LdfParserState + { + get { return _ldfParserState; } + private set { _ldfParserState = value; RaisePropertyChanged(); } + } + + /// + /// LDF 消息集合(UI 绑定),ZLG LIN 暂未接入 LDF,默认空。 + /// + public ObservableCollection ListLinLdfModel { get; private set; } = new ObservableCollection(); + + /// + /// 是否启用调度发送(与 UI 的调度表使能语义对齐)。 + /// + public bool SchEnable + { + get { return _zlgCanDriveService.Driver.SchEnable; } + set { _zlgCanDriveService.Driver.SchEnable = value; RaisePropertyChanged(); } + } + + /// + /// 是否启用事件驱动发送。 + /// + public bool IsCycleSend + { + get { return _zlgCanDriveService.Driver.IsCycleSend; } + set { _zlgCanDriveService.Driver.IsCycleSend = value; RaisePropertyChanged(); } + } + + /// + /// 构造。 + /// + /// 共享 CAN 服务。 + /// 日志服务。 + public ZlgLinDriveService(ZlgCanDriveService zlgCanDriveService, ILogService logService) + { + _zlgCanDriveService = zlgCanDriveService; + _log = logService; + + _zlgCanDriveService.Driver.LinFrameReceived += frame => + { + // 未来接入 LDF 后在这里做 Sync/Decode;当前仅保留事件链路,不做假解析。 + }; + } + + /// + /// 初始化 LIN 配置信息(目前仅缓存)。 + /// + /// 选中的配置。 + public void InitLinConfig(CanLinConfigPro selectedLinConfigPro) + { + SelectedCanLinConfigPro = selectedLinConfigPro; + } + + /// + /// 打开 LIN(共享设备句柄)。 + /// + /// 设备索引。 + /// 波特率。 + /// 是否主节点。 + public void StartLinDrive(uint deviceIndex, uint baudRate, bool isMaster) + { + if (OpenState) + { + return; + } + + try + { + // 先确保设备打开(不影响 CAN 后续 Init) + _zlgCanDriveService.Driver.OpenDevice(deviceIndex); + + // 初始化 LIN 通道 + _zlgCanDriveService.Driver.OpenAndInitLin(0, new CanDrive.ZlgCan.ZlgLinChannelOptions + { + BaudRate = baudRate, + IsMaster = isMaster, + MaxLength = 8, + ChecksumMode = 3 + }); + + // 统一由 CAN 服务侧启动接收线程(设备级 merge 接收可以同时收 CAN/LIN) + if (!_zlgCanDriveService.Driver.IsReceiving) + { + _zlgCanDriveService.Driver.StartReceiveLoop(mergeReceive: true, bufferFrames: 200); + } + + OpenState = true; + LdfParserState = false; + } + catch (Exception ex) + { + _log.Error($"ZLG LIN 打开失败:{ex.Message}"); + OpenState = false; + throw; + } + } + + /// + /// 关闭 LIN(共享设备句柄下,当前实现以 CloseDevice 为准:关闭将同时关闭 CAN/LIN)。 + /// + public void CloseDevice() + { + // ZLG 的通道句柄都在 Driver 内部;当前 Close 会关闭所有通道,保持与旧系统“同一时刻只有一种驱动工作”的原则一致。 + _zlgCanDriveService.CloseDevice(); + OpenState = false; + LdfParserState = false; + } + + /// + /// 加载 LDF(ZLG LIN 暂未接入 LDF 解析 DLL,因此此处明确失败,不返回模拟数据)。 + /// + /// LDF 路径。 + public ObservableCollection StartLdf(string path) + { + _log.Warn("ZLG LIN 当前版本未接入 LDF 解析(项目内仅存在 USB2XXX.dll 的 LDFParser)。"); + throw new NotSupportedException("ZLG LIN 暂未支持 LDF 解析,请后续提供/确认 ZLG 的 LDF DLL 接口(如 zldf.dll)后再接入。"); + } + } +} diff --git a/CapMachine.Wpf/ViewModels/CANConfigViewModel.cs b/CapMachine.Wpf/ViewModels/CANConfigViewModel.cs index f9fda98..69ed3b8 100644 --- a/CapMachine.Wpf/ViewModels/CANConfigViewModel.cs +++ b/CapMachine.Wpf/ViewModels/CANConfigViewModel.cs @@ -938,18 +938,12 @@ namespace CapMachine.Wpf.ViewModels /// private void SchEnableCmdCall(object par) { - var dd = SelectedCANConfigExdDto.SchEnable; - var Result = (bool)par; - if (Result) + if (SelectedCANConfigExdDto == null) { - CanDriveService.ToomossCanDrive.SchEnable = true; - + return; } - else - { - CanDriveService.ToomossCanDrive.SchEnable = false; - } + CanDriveService.ToomossCanDrive.SchEnable = SelectedCANConfigExdDto.SchEnable; } diff --git a/CapMachine.Wpf/ViewModels/CANFDConfigViewModel.cs b/CapMachine.Wpf/ViewModels/CANFDConfigViewModel.cs index c59a5cd..78e00f8 100644 --- a/CapMachine.Wpf/ViewModels/CANFDConfigViewModel.cs +++ b/CapMachine.Wpf/ViewModels/CANFDConfigViewModel.cs @@ -54,6 +54,8 @@ namespace CapMachine.Wpf.ViewModels //MachineDataService = machineDataService; DialogService = dialogService; + EventAggregator.GetEvent().Subscribe(LogicRuleChangeEventCall); + //仲裁波特率 ArbBaudRateCbxItems = new ObservableCollection() { @@ -713,6 +715,33 @@ namespace CapMachine.Wpf.ViewModels #endregion + + private DelegateCommand _btnTestPLC; + /// + /// 临时测试的方法 + /// + public DelegateCommand btnTestPLC + { + set + { + _btnTestPLC = value; + } + get + { + if (_btnTestPLC == null) + { + _btnTestPLC = new DelegateCommand(() => btnTestPLCCall()); + } + return _btnTestPLC; + } + } + //临时测试的方法 + private void btnTestPLCCall() + { + CanFdDriveService.ToomossCanFDDrive.UpdateSchDataByCmdDataChanged(); + } + + #region Dbc操作 private ObservableCollection _ListCanDbcModel; @@ -973,18 +1002,12 @@ namespace CapMachine.Wpf.ViewModels /// private void SchEnableCmdCall(object par) { - var dd = SelectedCANConfigExdDto.SchEnable; - var Result = (bool)par; - if (Result) + if (SelectedCANConfigExdDto == null) { - CanFdDriveService.ToomossCanFDDrive.SchEnable = true; - + return; } - else - { - CanFdDriveService.ToomossCanFDDrive.SchEnable = false; - } + CanFdDriveService.ToomossCanFDDrive.SchEnable = SelectedCANConfigExdDto.SchEnable; } diff --git a/CapMachine.Wpf/ViewModels/ZlgCanDriveConfigViewModel.cs b/CapMachine.Wpf/ViewModels/ZlgCanDriveConfigViewModel.cs new file mode 100644 index 0000000..2a2c66c --- /dev/null +++ b/CapMachine.Wpf/ViewModels/ZlgCanDriveConfigViewModel.cs @@ -0,0 +1,722 @@ +using AutoMapper; +using CapMachine.Core; +using CapMachine.Model.CANLIN; +using CapMachine.Wpf.CanDrive; +using CapMachine.Wpf.Dtos; +using CapMachine.Wpf.Services; +using ImTools; +using Microsoft.Win32; +using Prism.Commands; +using Prism.Events; +using Prism.Regions; +using Prism.Services.Dialogs; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using static CapMachine.Wpf.Models.ComEnum; + +namespace CapMachine.Wpf.ViewModels +{ + /// + /// ZLG CAN/CANFD 合并配置 ViewModel(模式切换:单选)。 + /// + public class ZlgCanDriveConfigViewModel : NavigationViewModel + { + /// + /// 构造函数。 + /// + public ZlgCanDriveConfigViewModel(IDialogService dialogService, IFreeSql freeSql, + IEventAggregator eventAggregator, IRegionManager regionManager, SysRunService sysRunService, + ComActionService comActionService, ILogService logService, LogicRuleService logicRuleService, + ConfigService configService, ZlgCanDriveService zlgCanDriveService, ZlgLinDriveService zlgLinDriveService, + IMapper mapper) + { + DialogService = dialogService; + FreeSql = freeSql; + EventAggregator = eventAggregator; + RegionManager = regionManager; + SysRunService = sysRunService; + ComActionService = comActionService; + LogService = logService; + LogicRuleService = logicRuleService; + ConfigService = configService; + ZlgCanDriveService = zlgCanDriveService; + ZlgLinDriveService = zlgLinDriveService; + Mapper = mapper; + + SelectedMode = ZlgCanMode.Can; + InitLoadCanConfigPro(); + } + + /// + /// Dialog 服务。 + /// + public IDialogService DialogService { get; } + + /// + /// FreeSql。 + /// + public IFreeSql FreeSql { get; } + + /// + /// 事件聚合。 + /// + public IEventAggregator EventAggregator { get; } + + /// + /// 区域导航。 + /// + public IRegionManager RegionManager { get; } + + /// + /// 系统运行服务。 + /// + public SysRunService SysRunService { get; } + + /// + /// 通信互斥控制。 + /// + public ComActionService ComActionService { get; } + + /// + /// 日志。 + /// + public ILogService LogService { get; } + + /// + /// 逻辑规则服务。 + /// + public LogicRuleService LogicRuleService { get; } + + /// + /// 配置服务。 + /// + public ConfigService ConfigService { get; } + + /// + /// ZLG CAN 服务。 + /// + public ZlgCanDriveService ZlgCanDriveService { get; } + + /// + /// ZLG LIN 服务(用于互斥判断)。 + /// + public ZlgLinDriveService ZlgLinDriveService { get; } + + /// + /// Mapper。 + /// + public IMapper Mapper { get; } + + private List _canLinConfigPros = new List(); + + private string? _opTip; + /// + /// 操作提示(用于 UI 状态展示)。 + /// + public string? OpTip + { + get { return _opTip; } + set { _opTip = value; RaisePropertyChanged(); } + } + + private string? _lastError; + /// + /// 最近一次错误信息(用于 UI 状态展示)。 + /// + public string? LastError + { + get { return _lastError; } + set { _lastError = value; RaisePropertyChanged(); } + } + + private ZlgCanMode _selectedMode; + /// + /// 模式选择:CAN/CANFD(单选)。 + /// + public ZlgCanMode SelectedMode + { + get { return _selectedMode; } + set + { + _selectedMode = value; + RaisePropertyChanged(); + RaisePropertyChanged(nameof(DbcPathTitle)); + RaisePropertyChanged(nameof(SelectedModeKey)); + RaisePropertyChanged(nameof(CurrentDbcPath)); + RaisePropertyChanged(nameof(CurrentCycle)); + RaisePropertyChanged(nameof(CurrentSchEnable)); + RaisePropertyChanged(nameof(CanBaudRate)); + RaisePropertyChanged(nameof(CanFdArbBaudRate)); + RaisePropertyChanged(nameof(CanFdDataBaudRate)); + RaisePropertyChanged(nameof(ConnectButtonText)); + ZlgCanDriveService.Mode = value; + InitLoadCanConfigPro(); + } + } + + /// + /// 连接按钮文字(对齐 Toomoss 风格)。 + /// + public string ConnectButtonText + { + get { return SelectedMode == ZlgCanMode.Can ? "连接CAN" : "连接CANFD"; } + } + + /// + /// 绑定用:模式 Key(Can/CanFd)。 + /// + public string SelectedModeKey + { + get { return SelectedMode == ZlgCanMode.Can ? "Can" : "CanFd"; } + set + { + if (string.Equals(value, "Can", StringComparison.OrdinalIgnoreCase)) + { + SelectedMode = ZlgCanMode.Can; + return; + } + + if (string.Equals(value, "CanFd", StringComparison.OrdinalIgnoreCase) || string.Equals(value, "CanFD", StringComparison.OrdinalIgnoreCase)) + { + SelectedMode = ZlgCanMode.CanFd; + return; + } + } + } + + /// + /// DBC 路径标题。 + /// + public string DbcPathTitle + { + get { return SelectedMode == ZlgCanMode.Can ? "CAN DBC 文件路径:" : "CANFD DBC 文件路径:"; } + } + + private ObservableCollection? _listCanLinConfigPro; + /// + /// 配置程序集合。 + /// + public ObservableCollection? ListCanLinConfigPro + { + get { return _listCanLinConfigPro; } + set { _listCanLinConfigPro = value; RaisePropertyChanged(); } + } + + /// + /// 选中的配置程序。 + /// + public CanLinConfigPro? SelectCanLinConfigPro { get; set; } + + private ObservableCollection? _listCanDbcModel; + /// + /// DBC 信号集合。 + /// + public ObservableCollection? ListCanDbcModel + { + get { return _listCanDbcModel; } + set { _listCanDbcModel = value; RaisePropertyChanged(); } + } + + /// + /// 当前选中的 DBC 信号。 + /// + public CanDbcModel? SelectedCanDbcModel { get; set; } + + private CANConfigExdDto? _selectedCANConfigExdDto; + /// + /// CAN 配置 DTO。 + /// + public CANConfigExdDto? SelectedCANConfigExdDto + { + get { return _selectedCANConfigExdDto; } + set { _selectedCANConfigExdDto = value; RaisePropertyChanged(); } + } + + private CANFdConfigExdDto? _selectedCANFdConfigExdDto; + /// + /// CANFD 配置 DTO。 + /// + public CANFdConfigExdDto? SelectedCANFdConfigExdDto + { + get { return _selectedCANFdConfigExdDto; } + set { _selectedCANFdConfigExdDto = value; RaisePropertyChanged(); } + } + + private void InitLoadCanConfigPro() + { + var info = SelectedMode == ZlgCanMode.Can ? CANLIN.CAN : CANLIN.CANFD; + + _canLinConfigPros = FreeSql.Select() + .Where(a => a.CANLINInfo == info) + .Include(a => a.CANConfigExd) + .Include(a => a.CANFdConfigExd) + .IncludeMany(a => a.CanLinConfigContents, then => then.Include(b => b.LogicRule)) + .ToList(); + + ListCanLinConfigPro = new ObservableCollection(_canLinConfigPros); + } + + private void SyncSelectedConfig() + { + if (SelectCanLinConfigPro == null) + { + return; + } + + if (SelectedMode == ZlgCanMode.Can) + { + SelectedCANConfigExdDto = Mapper.Map(SelectCanLinConfigPro.CANConfigExd); + SelectedCANFdConfigExdDto = null; + } + else + { + SelectedCANFdConfigExdDto = Mapper.Map(SelectCanLinConfigPro.CANFdConfigExd); + SelectedCANConfigExdDto = null; + } + + RaisePropertyChanged(nameof(CurrentDbcPath)); + RaisePropertyChanged(nameof(CurrentCycle)); + RaisePropertyChanged(nameof(CurrentSchEnable)); + RaisePropertyChanged(nameof(CanBaudRate)); + RaisePropertyChanged(nameof(CanFdArbBaudRate)); + RaisePropertyChanged(nameof(CanFdDataBaudRate)); + } + + /// + /// 当前 DBC 路径(随模式切换映射到对应 DTO)。 + /// + public string? CurrentDbcPath + { + get + { + return SelectedMode == ZlgCanMode.Can + ? SelectedCANConfigExdDto?.DbcPath + : SelectedCANFdConfigExdDto?.DbcPath; + } + set + { + if (SelectedMode == ZlgCanMode.Can) + { + if (SelectedCANConfigExdDto == null) return; + SelectedCANConfigExdDto.DbcPath = value; + } + else + { + if (SelectedCANFdConfigExdDto == null) return; + SelectedCANFdConfigExdDto.DbcPath = value; + } + + RaisePropertyChanged(); + } + } + + /// + /// 当前周期(随模式切换映射到对应 DTO)。 + /// + public int CurrentCycle + { + get + { + return SelectedMode == ZlgCanMode.Can + ? (SelectedCANConfigExdDto?.Cycle ?? 0) + : (SelectedCANFdConfigExdDto?.Cycle ?? 0); + } + set + { + if (SelectedMode == ZlgCanMode.Can) + { + if (SelectedCANConfigExdDto == null) return; + SelectedCANConfigExdDto.Cycle = value; + } + else + { + if (SelectedCANFdConfigExdDto == null) return; + SelectedCANFdConfigExdDto.Cycle = value; + } + + RaisePropertyChanged(); + } + } + + /// + /// 当前调度使能(随模式切换映射到对应 DTO)。 + /// + public bool CurrentSchEnable + { + get + { + return SelectedMode == ZlgCanMode.Can + ? (SelectedCANConfigExdDto?.SchEnable ?? false) + : (SelectedCANFdConfigExdDto?.SchEnable ?? false); + } + set + { + if (SelectedMode == ZlgCanMode.Can) + { + if (SelectedCANConfigExdDto == null) return; + SelectedCANConfigExdDto.SchEnable = value; + } + else + { + if (SelectedCANFdConfigExdDto == null) return; + SelectedCANFdConfigExdDto.SchEnable = value; + } + + RaisePropertyChanged(); + } + } + + /// + /// CAN 波特率。 + /// + public int CanBaudRate + { + get { return SelectedCANConfigExdDto?.BaudRate ?? 0; } + set + { + if (SelectedCANConfigExdDto == null) return; + SelectedCANConfigExdDto.BaudRate = value; + RaisePropertyChanged(); + } + } + + /// + /// CANFD 仲裁波特率。 + /// + public int CanFdArbBaudRate + { + get { return SelectedCANFdConfigExdDto?.ArbBaudRate ?? 0; } + set + { + if (SelectedCANFdConfigExdDto == null) return; + SelectedCANFdConfigExdDto.ArbBaudRate = value; + RaisePropertyChanged(); + } + } + + /// + /// CANFD 数据波特率。 + /// + public int CanFdDataBaudRate + { + get { return SelectedCANFdConfigExdDto?.DataBaudRate ?? 0; } + set + { + if (SelectedCANFdConfigExdDto == null) return; + SelectedCANFdConfigExdDto.DataBaudRate = value; + RaisePropertyChanged(); + } + } + + private void MatchSeletedAndCanDbcModel() + { + if (ListCanDbcModel == null || ListCanDbcModel.Count == 0) return; + if (SelectCanLinConfigPro?.CanLinConfigContents == null || SelectCanLinConfigPro.CanLinConfigContents.Count == 0) return; + + foreach (var itemCanDbcModel in ListCanDbcModel) + { + var findData = SelectCanLinConfigPro.CanLinConfigContents.FindFirst(a => a.SignalName == itemCanDbcModel.SignalName); + if (findData != null) + { + switch (findData.RWInfo) + { + case RW.Write: + itemCanDbcModel.IsSeletedInfo = 1; + break; + case RW.Read: + itemCanDbcModel.IsSeletedInfo = 2; + break; + default: + itemCanDbcModel.IsSeletedInfo = 0; + break; + } + } + else + { + itemCanDbcModel.IsSeletedInfo = 0; + } + } + } + + private DelegateCommand? _canConfigProGridSelectionChangedCmd; + /// + /// 配置程序选中变化。 + /// + public DelegateCommand CanConfigProGridSelectionChangedCmd + { + get + { + if (_canConfigProGridSelectionChangedCmd == null) + { + _canConfigProGridSelectionChangedCmd = new DelegateCommand(CanConfigProGridSelectionChangedCmdMethod); + } + return _canConfigProGridSelectionChangedCmd; + } + } + + private void CanConfigProGridSelectionChangedCmdMethod(object par) + { + if (par == null) return; + if (par is SelectionChangedEventArgs) return; + + if (par is CanLinConfigPro) + { + SelectCanLinConfigPro = par as CanLinConfigPro; + SyncSelectedConfig(); + return; + } + + var args = par as SelectionChangedEventArgs; + if (args == null || args.AddedItems == null || args.AddedItems.Count == 0) return; + + var selected = args.AddedItems[0] as CanLinConfigPro; + if (selected == null) return; + + SelectCanLinConfigPro = selected; + SyncSelectedConfig(); + } + + private DelegateCommand? _loadDbcCmd; + /// + /// 选择 DBC 文件。 + /// + public DelegateCommand LoadDbcCmd + { + get + { + if (_loadDbcCmd == null) + { + _loadDbcCmd = new DelegateCommand(LoadDbcCmdMethod); + } + return _loadDbcCmd; + } + } + + private void LoadDbcCmdMethod() + { + try + { + if (SelectCanLinConfigPro == null) + { + MessageBox.Show("选中CAN配置名称后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + OpenFileDialog openFileDialogInfo = new OpenFileDialog(); + openFileDialogInfo.Filter = "(*.dbc;*.dbc)|*.dbc;*.dbc|all|*.*"; + openFileDialogInfo.CheckFileExists = true; + openFileDialogInfo.CheckPathExists = true; + openFileDialogInfo.ShowDialog(); + string fileName = openFileDialogInfo.FileName; + + CurrentDbcPath = fileName; + } + catch + { + MessageBox.Show("可能未选择信息", "提示", MessageBoxButton.OKCancel, MessageBoxImage.Hand); + } + } + + private DelegateCommand? _schEnableCmd; + /// + /// 调度使能写入驱动。 + /// + public DelegateCommand SchEnableCmd + { + get + { + if (_schEnableCmd == null) + { + _schEnableCmd = new DelegateCommand(SchEnableCmdCall); + } + return _schEnableCmd; + } + } + + private void SchEnableCmdCall(object par) + { + // 与旧 Toomoss 行为对齐:UI 勾选后立即下发到驱动 + ZlgCanDriveService.SchEnable = CurrentSchEnable; + } + + private DelegateCommand? _canOpCmd; + /// + /// CAN 操作命令。 + /// + public DelegateCommand CanOpCmd + { + get + { + if (_canOpCmd == null) + { + _canOpCmd = new DelegateCommand(CanOpCmdMethod); + } + return _canOpCmd; + } + } + + private void CanOpCmdMethod(string par) + { + if (string.IsNullOrWhiteSpace(par)) + { + return; + } + + try + { + switch (par) + { + case "Open": + if (ComActionService.IsCanToDoWork() == false) + { + MessageBox.Show("请关闭LIN连接后才能开启CAN,同一个时刻只能有一个通信驱动压缩机", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + if (ZlgLinDriveService.OpenState) + { + MessageBox.Show("请先关闭 ZLG LIN 连接后再开启 ZLG CAN", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + if (SelectCanLinConfigPro == null) + { + MessageBox.Show("选中CAN配置名称后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + if (SelectedMode == ZlgCanMode.Can) + { + if (SelectedCANConfigExdDto == null) + { + MessageBox.Show("CAN配置为空", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + // ZLG 底层实际为 CANFD 通道,CAN 经典帧使用 arbitration 波特率(data 亦沿用,保持一致即可) + ZlgCanDriveService.UpdateConfig(0, (uint)SelectedCANConfigExdDto.BaudRate, (uint)SelectedCANConfigExdDto.BaudRate, resEnable: true); + } + else + { + if (SelectedCANFdConfigExdDto == null) + { + MessageBox.Show("CANFD配置为空", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + ZlgCanDriveService.UpdateConfig(0, (uint)SelectedCANFdConfigExdDto.ArbBaudRate, (uint)SelectedCANFdConfigExdDto.DataBaudRate, SelectedCANFdConfigExdDto.ResEnable); + } + + ZlgCanDriveService.StartCanDrive(); + + if (ZlgCanDriveService.OpenState) + { + LastError = null; + OpTip = "CAN 已连接"; + ConfigService.CanLinRunStateModel.CurSysSelectedCanLin = SelectedMode == ZlgCanMode.Can ? CanLinEnum.Can : CanLinEnum.CANFD; + } + else + { + OpTip = "CAN 连接失败"; + MessageBox.Show("CAN 连接失败(OpenState=false),请检查设备/驱动/参数。", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + // 自动解析 DBC + if (!string.IsNullOrWhiteSpace(CurrentDbcPath)) + { + ListCanDbcModel = ZlgCanDriveService.StartDbc(CurrentDbcPath); + MatchSeletedAndCanDbcModel(); + } + + break; + + case "Close": + ZlgCanDriveService.CloseDevice(); + ConfigService.CanLinRunStateModel.CurSysSelectedCanLin = CanLinEnum.No; + LastError = null; + OpTip = "CAN 已关闭"; + break; + + case "Parse": + if (!ZlgCanDriveService.OpenState) + { + MessageBox.Show("请先打开CAN后再解析", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + if (string.IsNullOrWhiteSpace(CurrentDbcPath)) + { + MessageBox.Show("请选择DBC文件后再操作", "提示", MessageBoxButton.OKCancel, MessageBoxImage.Hand); + return; + } + + ListCanDbcModel = ZlgCanDriveService.StartDbc(CurrentDbcPath); + MatchSeletedAndCanDbcModel(); + LastError = null; + OpTip = "DBC 已解析"; + break; + + case "CycleSend": + ZlgCanDriveService.IsCycleSend = !ZlgCanDriveService.IsCycleSend; + OpTip = ZlgCanDriveService.IsCycleSend ? "循环发送:已开启" : "循环发送:已关闭"; + break; + + case "CycleRecive": + ZlgCanDriveService.SetReceiveEnabled(!ZlgCanDriveService.IsCycleRevice); + OpTip = ZlgCanDriveService.IsCycleRevice ? "循环接收:已开启" : "循环接收:已关闭"; + break; + + case "Save": + if (SelectCanLinConfigPro == null) + { + MessageBox.Show("选中CAN配置名称后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + if (SelectedMode == ZlgCanMode.Can) + { + if (SelectedCANConfigExdDto == null) return; + FreeSql.Update() + .Set(a => a.DbcPath, SelectedCANConfigExdDto.DbcPath) + .Set(a => a.Cycle, SelectedCANConfigExdDto.Cycle) + .Set(a => a.BaudRate, SelectedCANConfigExdDto.BaudRate) + .Set(a => a.SchEnable, SelectedCANConfigExdDto.SchEnable) + .Where(a => a.Id == SelectedCANConfigExdDto.Id) + .ExecuteUpdated(); + } + else + { + if (SelectedCANFdConfigExdDto == null) return; + FreeSql.Update() + .Set(a => a.DbcPath, SelectedCANFdConfigExdDto.DbcPath) + .Set(a => a.Cycle, SelectedCANFdConfigExdDto.Cycle) + .Set(a => a.DataBaudRate, SelectedCANFdConfigExdDto.DataBaudRate) + .Set(a => a.ArbBaudRate, SelectedCANFdConfigExdDto.ArbBaudRate) + .Set(a => a.ISOEnable, SelectedCANFdConfigExdDto.ISOEnable) + .Set(a => a.ResEnable, SelectedCANFdConfigExdDto.ResEnable) + .Set(a => a.SchEnable, SelectedCANFdConfigExdDto.SchEnable) + .Where(a => a.Id == SelectedCANFdConfigExdDto.Id) + .ExecuteUpdated(); + } + + InitLoadCanConfigPro(); + LastError = null; + OpTip = "配置已保存"; + break; + } + } + catch (Exception ex) + { + LastError = ex.Message; + OpTip = "操作失败"; + LogService.Error($"ZLG CAN 操作失败:{par},{ex}"); + MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + } +} diff --git a/CapMachine.Wpf/ViewModels/ZlgLinDriveConfigViewModel.cs b/CapMachine.Wpf/ViewModels/ZlgLinDriveConfigViewModel.cs new file mode 100644 index 0000000..197b48f --- /dev/null +++ b/CapMachine.Wpf/ViewModels/ZlgLinDriveConfigViewModel.cs @@ -0,0 +1,339 @@ +using AutoMapper; +using CapMachine.Core; +using CapMachine.Model.CANLIN; +using CapMachine.Wpf.Dtos; +using CapMachine.Wpf.LinDrive; +using CapMachine.Wpf.Services; +using ImTools; +using Microsoft.Win32; +using Prism.Commands; +using Prism.Events; +using Prism.Regions; +using Prism.Services.Dialogs; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using static CapMachine.Wpf.Models.ComEnum; + +namespace CapMachine.Wpf.ViewModels +{ + /// + /// ZLG LIN 配置 ViewModel。 + /// + public class ZlgLinDriveConfigViewModel : NavigationViewModel + { + /// + /// 构造函数。 + /// + public ZlgLinDriveConfigViewModel(IDialogService dialogService, IFreeSql freeSql, + IEventAggregator eventAggregator, IRegionManager regionManager, SysRunService sysRunService, + ConfigService configService, ZlgLinDriveService zlgLinDriveService, ZlgCanDriveService zlgCanDriveService, + ComActionService comActionService, LogicRuleService logicRuleService, + IMapper mapper) + { + DialogService = dialogService; + FreeSql = freeSql; + EventAggregator = eventAggregator; + RegionManager = regionManager; + SysRunService = sysRunService; + ConfigService = configService; + ZlgLinDriveService = zlgLinDriveService; + ZlgCanDriveService = zlgCanDriveService; + ComActionService = comActionService; + LogicRuleService = logicRuleService; + Mapper = mapper; + + InitLoadLinConfigPro(); + } + + public IDialogService DialogService { get; } + public IFreeSql FreeSql { get; } + public IEventAggregator EventAggregator { get; } + public IRegionManager RegionManager { get; } + public SysRunService SysRunService { get; } + public ConfigService ConfigService { get; } + public ZlgLinDriveService ZlgLinDriveService { get; } + public ZlgCanDriveService ZlgCanDriveService { get; } + public ComActionService ComActionService { get; } + public LogicRuleService LogicRuleService { get; } + public IMapper Mapper { get; } + + private List linConfigPros = new List(); + + private ObservableCollection? _ListCanLinConfigPro; + /// + /// LIN 配置程序集合。 + /// + public ObservableCollection? ListCanLinConfigPro + { + get { return _ListCanLinConfigPro; } + set { _ListCanLinConfigPro = value; RaisePropertyChanged(); } + } + + /// + /// 选中的配置程序。 + /// + public CanLinConfigPro? SelectCanLinConfigPro { get; set; } + + private LINConfigExdDto? _SelectedLINConfigExdDto; + /// + /// 选中的 LIN 配置 DTO。 + /// + public LINConfigExdDto? SelectedLINConfigExdDto + { + get { return _SelectedLINConfigExdDto; } + set { _SelectedLINConfigExdDto = value; RaisePropertyChanged(); RaisePropertyChanged(nameof(CurrentLdfPath)); RaisePropertyChanged(nameof(CurrentSchEnable)); RaisePropertyChanged(nameof(LinBaudRate)); } + } + + /// + /// 当前 LDF 路径。 + /// + public string? CurrentLdfPath + { + get { return SelectedLINConfigExdDto?.LdfPath; } + set + { + if (SelectedLINConfigExdDto == null) return; + SelectedLINConfigExdDto.LdfPath = value; + RaisePropertyChanged(); + } + } + + /// + /// LIN 波特率。 + /// + public int LinBaudRate + { + get { return SelectedLINConfigExdDto?.BaudRate ?? 0; } + set + { + if (SelectedLINConfigExdDto == null) return; + SelectedLINConfigExdDto.BaudRate = value; + RaisePropertyChanged(); + } + } + + /// + /// 调度表使能。 + /// + public bool CurrentSchEnable + { + get { return SelectedLINConfigExdDto?.SchEnable ?? false; } + set + { + if (SelectedLINConfigExdDto == null) return; + SelectedLINConfigExdDto.SchEnable = value; + RaisePropertyChanged(); + } + } + + private void InitLoadLinConfigPro() + { + linConfigPros = FreeSql.Select() + .Where(a => a.CANLINInfo == CANLIN.LIN) + .Include(a => a.LINConfigExd) + .IncludeMany(a => a.CanLinConfigContents, then => then.Include(b => b.LogicRule)) + .IncludeMany(a => a.LinScheduleConfigs) + .ToList(); + + ListCanLinConfigPro = new ObservableCollection(linConfigPros); + } + + private void SyncSelectedConfig() + { + if (SelectCanLinConfigPro == null) return; + SelectedLINConfigExdDto = Mapper.Map(SelectCanLinConfigPro.LINConfigExd); + } + + private DelegateCommand? _LinConfigProGridSelectionChangedCmd; + /// + /// LIN 配置程序选中变化。 + /// + public DelegateCommand LinConfigProGridSelectionChangedCmd + { + get + { + if (_LinConfigProGridSelectionChangedCmd == null) + { + _LinConfigProGridSelectionChangedCmd = new DelegateCommand(LinConfigProGridSelectionChangedCmdMethod); + } + return _LinConfigProGridSelectionChangedCmd; + } + } + + private void LinConfigProGridSelectionChangedCmdMethod(object par) + { + if (par == null) return; + if (par is SelectionChangedEventArgs) return; + + if (par is CanLinConfigPro) + { + SelectCanLinConfigPro = par as CanLinConfigPro; + SyncSelectedConfig(); + return; + } + + var args = par as SelectionChangedEventArgs; + if (args == null || args.AddedItems == null || args.AddedItems.Count == 0) return; + var selected = args.AddedItems[0] as CanLinConfigPro; + if (selected == null) return; + + SelectCanLinConfigPro = selected; + SyncSelectedConfig(); + } + + private DelegateCommand? _LoadLdfCmd; + /// + /// 选择 LDF 文件。 + /// + public DelegateCommand LoadLdfCmd + { + get + { + if (_LoadLdfCmd == null) + { + _LoadLdfCmd = new DelegateCommand(LoadLdfCmdMethod); + } + return _LoadLdfCmd; + } + } + + private void LoadLdfCmdMethod() + { + try + { + if (SelectCanLinConfigPro == null || SelectedLINConfigExdDto == null) + { + MessageBox.Show("选中LIN配置名称后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + OpenFileDialog openFileDialogInfo = new OpenFileDialog(); + openFileDialogInfo.Filter = "(*.ldf;*.ldf)|*.ldf;*.ldf|all|*.*"; + openFileDialogInfo.CheckFileExists = true; + openFileDialogInfo.CheckPathExists = true; + openFileDialogInfo.ShowDialog(); + + CurrentLdfPath = openFileDialogInfo.FileName; + } + catch + { + MessageBox.Show("可能未选择信息", "提示", MessageBoxButton.OKCancel, MessageBoxImage.Hand); + } + } + + private DelegateCommand? _LinOpCmd; + /// + /// LIN 操作命令。 + /// + public DelegateCommand LinOpCmd + { + get + { + if (_LinOpCmd == null) + { + _LinOpCmd = new DelegateCommand(LinOpCmdMethod); + } + return _LinOpCmd; + } + } + + private void LinOpCmdMethod(string par) + { + switch (par) + { + case "Open": + if (ComActionService.IsLINToDoWork() == false) + { + MessageBox.Show("请关闭CAN连接后才能开启LIN,同一个时刻只能有一个通信驱动压缩机", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + if (ZlgCanDriveService.OpenState) + { + MessageBox.Show("请先关闭 ZLG CAN 连接后再开启 ZLG LIN", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + if (SelectCanLinConfigPro == null || SelectedLINConfigExdDto == null) + { + MessageBox.Show("选中LIN配置名称后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + // 打开 LIN(不依赖 LDF) + ZlgLinDriveService.StartLinDrive(0, (uint)SelectedLINConfigExdDto.BaudRate, isMaster: true); + if (ZlgLinDriveService.OpenState) + { + ConfigService.CanLinRunStateModel.CurSysSelectedCanLin = CanLinEnum.Lin; + } + break; + + case "Close": + ZlgLinDriveService.CloseDevice(); + ConfigService.CanLinRunStateModel.CurSysSelectedCanLin = CanLinEnum.No; + break; + + case "Save": + if (SelectCanLinConfigPro == null || SelectedLINConfigExdDto == null) + { + MessageBox.Show("选中LIN配置名称后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + FreeSql.Update() + .Set(a => a.LdfPath, SelectedLINConfigExdDto.LdfPath) + .Set(a => a.Cycle, SelectedLINConfigExdDto.Cycle) + .Set(a => a.BaudRate, SelectedLINConfigExdDto.BaudRate) + .Set(a => a.SchEnable, SelectedLINConfigExdDto.SchEnable) + .Where(a => a.Id == SelectedLINConfigExdDto.Id) + .ExecuteUpdated(); + + InitLoadLinConfigPro(); + break; + + case "Parse": + // 明确提示:当前 ZLG LIN 暂不支持 LDF + if (SelectCanLinConfigPro == null || SelectedLINConfigExdDto == null) + { + MessageBox.Show("选中LIN配置名称后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + try + { + ZlgLinDriveService.StartLdf(SelectedLINConfigExdDto.LdfPath ?? string.Empty); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + } + break; + } + } + + private DelegateCommand? _SchEnableCmd; + /// + /// 调度表使能写入驱动。 + /// + public DelegateCommand SchEnableCmd + { + get + { + if (_SchEnableCmd == null) + { + _SchEnableCmd = new DelegateCommand(SchEnableCmdCall); + } + return _SchEnableCmd; + } + } + + private void SchEnableCmdCall(object par) + { + ZlgLinDriveService.SchEnable = CurrentSchEnable; + } + } +} diff --git a/CapMachine.Wpf/Views/CANConfigView.xaml b/CapMachine.Wpf/Views/CANConfigView.xaml index b619098..a83a4be 100644 --- a/CapMachine.Wpf/Views/CANConfigView.xaml +++ b/CapMachine.Wpf/Views/CANConfigView.xaml @@ -655,7 +655,6 @@ Width="40" Margin="5,2" Command="{Binding SchEnableCmd}" - CommandParameter="{Binding SelectedCANConfigExdDto.SchEnable}" IsChecked="{Binding SelectedCANConfigExdDto.SchEnable}" Style="{StaticResource MaterialDesignSwitchToggleButton}" ToolTip="启用调度表" /> diff --git a/CapMachine.Wpf/Views/CANFDConfigView.xaml b/CapMachine.Wpf/Views/CANFDConfigView.xaml index df0b4bd..0ff8ab7 100644 --- a/CapMachine.Wpf/Views/CANFDConfigView.xaml +++ b/CapMachine.Wpf/Views/CANFDConfigView.xaml @@ -710,13 +710,15 @@ Width="40" Margin="2,2" Command="{Binding SchEnableCmd}" - CommandParameter="{Binding SelectedCANConfigExdDto.SchEnable}" IsChecked="{Binding SelectedCANConfigExdDto.SchEnable}" Style="{StaticResource MaterialDesignSwitchToggleButton}" ToolTip="启用调度表" /> + diff --git a/CapMachine.Wpf/Views/MainView.xaml b/CapMachine.Wpf/Views/MainView.xaml index 898d90c..56a4f1a 100644 --- a/CapMachine.Wpf/Views/MainView.xaml +++ b/CapMachine.Wpf/Views/MainView.xaml @@ -14,12 +14,14 @@ Width="1920" Height="1080" prism:ViewModelLocator.AutoWireViewModel="True" + Closed="Window_Closed" + Closing="Window_Closing" Icon="/Assets/Images/favicon.ico" StateChanged="Window_StateChanged" WindowStartupLocation="CenterScreen" WindowState="Maximized" WindowStyle="SingleBorderWindow" - mc:Ignorable="d" Closing="Window_Closing" Closed="Window_Closed"> + mc:Ignorable="d"> diff --git a/CapMachine.Wpf/Views/ZlgCanDriveConfigView.xaml b/CapMachine.Wpf/Views/ZlgCanDriveConfigView.xaml new file mode 100644 index 0000000..a0bdb4b --- /dev/null +++ b/CapMachine.Wpf/Views/ZlgCanDriveConfigView.xaml @@ -0,0 +1,375 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapMachine.Wpf/Views/ZlgCanDriveConfigView.xaml.cs b/CapMachine.Wpf/Views/ZlgCanDriveConfigView.xaml.cs new file mode 100644 index 0000000..7b3efa3 --- /dev/null +++ b/CapMachine.Wpf/Views/ZlgCanDriveConfigView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace CapMachine.Wpf.Views +{ + /// + /// ZlgCanDriveConfigView.xaml 的交互逻辑 + /// + public partial class ZlgCanDriveConfigView : UserControl + { + public ZlgCanDriveConfigView() + { + InitializeComponent(); + } + } +} diff --git a/CapMachine.Wpf/Views/ZlgLinDriveConfigView.xaml b/CapMachine.Wpf/Views/ZlgLinDriveConfigView.xaml new file mode 100644 index 0000000..19db279 --- /dev/null +++ b/CapMachine.Wpf/Views/ZlgLinDriveConfigView.xaml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CapMachine.Wpf/Views/ZlgLinDriveConfigView.xaml.cs b/CapMachine.Wpf/Views/ZlgLinDriveConfigView.xaml.cs new file mode 100644 index 0000000..b98d01b --- /dev/null +++ b/CapMachine.Wpf/Views/ZlgLinDriveConfigView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace CapMachine.Wpf.Views +{ + /// + /// ZlgLinDriveConfigView.xaml 的交互逻辑 + /// + public partial class ZlgLinDriveConfigView : UserControl + { + public ZlgLinDriveConfigView() + { + InitializeComponent(); + } + } +} diff --git a/Sample/C#_USBCANFD_251215/USBCANFD.sln b/Sample/C#_USBCANFD_251215/USBCANFD.sln new file mode 100644 index 0000000..a7def0f --- /dev/null +++ b/Sample/C#_USBCANFD_251215/USBCANFD.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30501.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "USBCANFD", "USBCANFD\USBCANFD.csproj", "{B878ECF2-86D7-4907-A7FD-66899F10ECF2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B878ECF2-86D7-4907-A7FD-66899F10ECF2}.Debug|x64.ActiveCfg = Debug|x64 + {B878ECF2-86D7-4907-A7FD-66899F10ECF2}.Debug|x64.Build.0 = Debug|x64 + {B878ECF2-86D7-4907-A7FD-66899F10ECF2}.Debug|x86.ActiveCfg = Debug|x86 + {B878ECF2-86D7-4907-A7FD-66899F10ECF2}.Debug|x86.Build.0 = Debug|x86 + {B878ECF2-86D7-4907-A7FD-66899F10ECF2}.Release|x64.ActiveCfg = Release|x64 + {B878ECF2-86D7-4907-A7FD-66899F10ECF2}.Release|x64.Build.0 = Release|x64 + {B878ECF2-86D7-4907-A7FD-66899F10ECF2}.Release|x86.ActiveCfg = Release|x86 + {B878ECF2-86D7-4907-A7FD-66899F10ECF2}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Sample/C#_USBCANFD_251215/USBCANFD/App.config b/Sample/C#_USBCANFD_251215/USBCANFD/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/Sample/C#_USBCANFD_251215/USBCANFD/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Sample/C#_USBCANFD_251215/USBCANFD/Program.cs b/Sample/C#_USBCANFD_251215/USBCANFD/Program.cs new file mode 100644 index 0000000..2641b37 --- /dev/null +++ b/Sample/C#_USBCANFD_251215/USBCANFD/Program.cs @@ -0,0 +1,930 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; + +using ZLGAPI; + +namespace USBCANFD +{ + + internal class Program + { + public static IntPtr devHandle = IntPtr.Zero; // 设备句柄 + public static IntPtr[] chnHandle = new IntPtr[10]; // 通道句柄 + public static int chn_max = 2; // 最大通道数量 + public static volatile bool isRunning = true; // 线程标志 + public static uint isMerge = 0; // 合并接收标志 + public static uint isBusload = 0; // 通道利用率使能 + + // 线程传递参数 + class ThreadParams + { + public IntPtr dev { get; set; } // 设备句柄 + public IntPtr chn { get; set; } // 通道句柄 + public int chnNum { get; set; } // 通道号 + } + class ThreadBusload + { + public IntPtr dev { get; set; } // 设备句柄 + public int chnNum { get; set; } // 通道号 + } + + static void Main(string[] args) + { + uint ret; // 返回值 + + // 打开设备 方式一:通过索引顺序打开设备 + Program.devHandle = ZLGCAN.ZCAN_OpenDevice(ZLGCAN.ZCAN_USBCANFD_200U, 0, 0); + if (Program.devHandle == IntPtr.Zero) + { + Console.WriteLine("打开设备失败"); + Console.ReadKey(); + return; + } + else + Console.WriteLine("打开设备成功"); + + // 打开设备 方式二:通过自定义序列号打开设备,需要首先执行SN_test函数设置自定义序列号 + //Program.devHandle = ZLGCAN.ZCAN_OpenDeviceByName(ZLGCAN.ZCAN_USBCANFD_200U, "C#01"); + //if (Program.devHandle == IntPtr.Zero) + //{ + // Console.WriteLine("打开设备失败"); + // Console.ReadKey(); + // return; + //} + //else + // Console.WriteLine("打开设备成功"); + + //// 获取设备信息 + //ZLGCAN.ZCAN_DEVICE_INFO info = new ZLGCAN.ZCAN_DEVICE_INFO(); + //IntPtr P2Info = Marshal.AllocHGlobal(Marshal.SizeOf(info)); + //if (1 == ZLGCAN.ZCAN_GetDeviceInf(Program.devHandle, P2Info)) + //{ + // info = (ZLGCAN.ZCAN_DEVICE_INFO)Marshal.PtrToStructure(P2Info, typeof(ZLGCAN.ZCAN_DEVICE_INFO)); + // Console.WriteLine("设备型号 {0},设备固件版本 {1:X}", new string(info.str_hw_Type).TrimEnd('\0'), info.fw_Version); + //} + + //// 自定义序列号 + //SN_test(Program.devHandle); + + // 该函数用于检测设备是否在线 + //uint status = ZLGCAN.ZCAN_IsDeviceOnLine(Program.devHandle); + //Console.WriteLine("设备状态{0}", + // status == 0 ? "ERR" : + // status == 1 ? "OK" : + // status == 2 ? "ONLINE" : + // status == 3 ? "OFFLINE" : + // status == 4 ? "UNSUPPORTED" : + // status == 5 ? "BUFFER_TOO_SMALL" : "UNKNOW"); + + // 初始化并打开通道 + for (int i = 0; i < Program.chn_max; i++) + { + Program.chnHandle[i] = Init_chn_USBCANFD(Program.devHandle, i); // 必须初始化完一个通道,再走另外一个通道!!! + if (Program.chnHandle[i] == IntPtr.Zero) + return; + } + + // 接收线程处理 + if (Program.isMerge == 1) // 合并接收 + { + // 设置合并接收 + ret = ZLGCAN.ZCAN_SetValue(Program.devHandle, "0/set_device_recv_merge", "1"); + if (ret != 1) + { + Console.WriteLine("设置合并接收失败"); + Console.ReadKey(); + } + ThreadParams parameters = new ThreadParams + { + dev = Program.devHandle, + chnNum = 0 + }; + Thread workerThread = new Thread(ReceiveThread_Merge); + workerThread.IsBackground = true; // 设置为后台线程,不需要join + workerThread.Start(parameters); // 启动线程 + } + else // 普通接收 + { + // 关闭合并接收 + ret = ZLGCAN.ZCAN_SetValue(Program.devHandle, "0/set_device_recv_merge", "0"); + if (ret != 1) + { + Console.WriteLine("关闭合并接收失败"); + Console.ReadKey(); + } + for (int i = 0; i < Program.chn_max; i++) + { + ThreadParams parameters = new ThreadParams + { + chn = Program.chnHandle[i], + chnNum = i + }; + Thread workerThread = new Thread(ReceiveThread); // 每通道独立线程接收 + workerThread.IsBackground = true; // 设置为后台线程,不需要join + workerThread.Start(parameters); // 启动线程 + } + } + + if (Program.isBusload == 1) // 通道利用率 + { + ThreadBusload Busload = new ThreadBusload + { + dev = Program.devHandle, + chnNum = 0 + }; + Thread ThreadBusload = new Thread(Thread_Busload); + ThreadBusload.IsBackground = true; // 设置为后台线程,不需要join + ThreadBusload.Start(Busload); // 启动线程 + } + + // 发送示例 + Send_test(Program.chnHandle[0]); + //Send_test_Merge(Program.devHandle); + + //// 定时发送 + //AutoSend_test(Program.devHandle, 0); + + //// 队列发送 + //QueueSend_test(Program.devHandle, Program.chnHandle[0]); + + // 阻塞等待 + Console.ReadKey(); + Program.isRunning = false; + + // 关闭通道 + for (int i = 0; i < Program.chn_max; i++) + { + ret = ZLGCAN.ZCAN_ResetCAN(Program.chnHandle[i]); + if (ret != 1) + Console.WriteLine("关闭通道{0}失败", i); + } + + // 关闭设备 + ret = ZLGCAN.ZCAN_CloseDevice(Program.devHandle); + if (ret != 1) + { + Console.WriteLine("关闭设备失败"); + Console.ReadKey(); + return; + } + } + + // 初始化CAN通道 + static IntPtr Init_chn_USBCANFD(IntPtr dev, int chn_idx) + { + IntPtr temp = IntPtr.Zero; + uint ret; // 返回值 + + // 仲裁域波特率 + string path = String.Format("{0}/canfd_abit_baud_rate", chn_idx); + ret = ZLGCAN.ZCAN_SetValue(dev, path, "500000"); // 500k + if (ret != 1) + { + Console.WriteLine("设置仲裁域波特率失败"); + Console.ReadKey(); + return IntPtr.Zero; + } + + // 数据域波特率 + path = String.Format("{0}/canfd_dbit_baud_rate", chn_idx); + ret = ZLGCAN.ZCAN_SetValue(dev, path, "2000000"); // 2M + if (ret != 1) + { + Console.WriteLine("设置数据域波特率失败"); + Console.ReadKey(); + return IntPtr.Zero; + } + + // 终端电阻 + path = String.Format("{0}/initenal_resistance", chn_idx); + string resistance = "1";//1-使能 0-禁能 + ret = ZLGCAN.ZCAN_SetValue(dev, path, resistance); + if (ret != 1) + { + Console.WriteLine("设置终端电阻失败"); + Console.ReadKey(); + return IntPtr.Zero; + } + + // 初始化通道 + ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG InitConfig = new ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG(); // 结构体 + InitConfig.can_type = 1; // 1 - CANFD (USBCANFD 只能选择CANFD模式) + InitConfig.config.canfd.mode = 0; // 0 - 正常模式,1 - 只听模式 + + IntPtr P2InitConfig = Marshal.AllocHGlobal(Marshal.SizeOf(InitConfig)); + Marshal.StructureToPtr(InitConfig, P2InitConfig, true); // 转成指针 + temp = ZLGCAN.ZCAN_InitCAN(dev, (uint)chn_idx, P2InitConfig); + Marshal.FreeHGlobal(P2InitConfig); // 释放内存 + if (temp == IntPtr.Zero) + { + Console.WriteLine("初始化通道失败"); + Console.ReadKey(); + return IntPtr.Zero; + } + Console.WriteLine("初始化通道成功"); + + //// 设置滤波 + //value = "0"; // 0-标准帧 1-扩展帧 + // path = String.Format("{0}/filter_mode", chn_idx); + // ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, value); + + // value = "0x731"; // 起始 ID + // path = String.Format("{0}/filter_start", chn_idx); + // ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, value); + + // value = "0x7DF"; // 结束 ID + // path = String.Format("{0}/filter_end", chn_idx); + // ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, value); + + // value = "0"; // 使能滤波 + // path = String.Format("{0}/filter_ack", chn_idx); + // ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, value); + // if (ret != 1) + // { + // Console.WriteLine("使能滤波失败"); + // Console.ReadKey(); + // return IntPtr.Zero; + // } + + if (Program.isBusload == 1) // 通道利用率 + { + // 通道利用率上报 + path = String.Format("{0}/set_bus_usage_enable", chn_idx); + ret = ZLGCAN.ZCAN_SetValue(dev, path, "1"); + + // 总线利用率上报周期 + path = String.Format("{0}/set_bus_usage_period", chn_idx); + ret = ZLGCAN.ZCAN_SetValue(dev, path, "500"); + } + + //打开通道 + uint Ret = ZLGCAN.ZCAN_StartCAN(temp); + if (Ret != 1) + { + Console.WriteLine("打开通道失败"); + Console.ReadKey(); + return IntPtr.Zero; + } + Console.WriteLine("打开通道成功"); + return temp; + } + + // 总线利用率 + static void Thread_Busload(object obj) + { + ThreadBusload parameters = (ThreadBusload)obj; + while (Program.isRunning) + { + IntPtr pUse = ZLGCAN.ZCAN_GetValue(Program.devHandle, parameters.chnNum +"/get_bus_usage/1" ); + if (pUse != IntPtr.Zero) + { + ZLGCAN.BusUsage busUsage = (ZLGCAN.BusUsage)Marshal.PtrToStructure(pUse, typeof(ZLGCAN.BusUsage)); + Console.WriteLine("总线利用率: {0}%,{1}帧/秒", busUsage.nBusUsage / 100, busUsage.nFrameCount); + } + Thread.Sleep(500); + } + } + + // 接收线程 + static void ReceiveThread(object obj) + { + ThreadParams parameters = obj as ThreadParams; + if (parameters == null) return; + + // 配置缓冲区大小 + const int BUFFER_SIZE = 100; + + // 预计算结构体大小 + int canStructSize = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Receive_Data)); + int canfdStructSize = Marshal.SizeOf(typeof(ZLGCAN.ZCAN_ReceiveFD_Data)); + + // 分配非托管内存(循环外一次分配,复用避免频繁申请释放) + IntPtr pCanBuffer = Marshal.AllocHGlobal(canStructSize * BUFFER_SIZE); // CAN接收缓冲区 + IntPtr pCanfdBuffer = Marshal.AllocHGlobal(canfdStructSize * BUFFER_SIZE); // CANFD接收缓冲区 + + try + { + while (Program.isRunning) + { + uint recNum = ZLGCAN.ZCAN_GetReceiveNum(parameters.chn, 0); // 0-CAN + if (recNum > 0) + { + // 限制接收数量不超过缓冲区大小(双重保险) + uint actualReceive = ZLGCAN.ZCAN_Receive( + parameters.chn, + pCanBuffer, // 传入非托管缓冲区指针(C风格) + BUFFER_SIZE, + 10 + ); + + if (actualReceive > 0) + { + for (int i = 0; i < actualReceive; i++) + { + // 计算当前CAN数据的指针 + IntPtr pCurrentCan = (IntPtr)(pCanBuffer.ToInt64() + i * canStructSize); + + // 非托管内存转换为C#结构体 + ZLGCAN.ZCAN_Receive_Data recData = (ZLGCAN.ZCAN_Receive_Data)Marshal.PtrToStructure( + pCurrentCan, + typeof(ZLGCAN.ZCAN_Receive_Data) + ); + + // 可选:ID过滤 + // uint canId = recData.frame.can_id & 0x1FFFFFFF; + // if (canId != 0x731 && canId != 0x7B1) + // continue; + + // 通道号和时间戳 + Console.Write(String.Format("CHN:{0} [{1}] CAN ", parameters.chnNum, recData.timestamp)); + + // 帧类型(标准/扩展) + Console.Write((recData.frame.can_id & (1 << 31)) != 0 ? "扩展帧 " : "标准帧 "); + + // 方向(Tx/Rx) + Console.Write((recData.frame.__pad & 0x20) == 0x20 ? "Tx " : "Rx "); + + // 数据长度校验 + int dataLen = Math.Min(recData.frame.can_dlc, recData.frame.data != null ? recData.frame.data.Length : 0); + + // ID和数据(保留原格式) + Console.WriteLine(String.Format("ID:{0:X} {1}", + recData.frame.can_id & 0x1FFFFFFF, + Program.b_array2string(recData.frame.data, dataLen) + )); + } + } + } + + recNum = ZLGCAN.ZCAN_GetReceiveNum(parameters.chn, 1); // 1-CANFD + if (recNum > 0) + { + uint actualReceive = ZLGCAN.ZCAN_ReceiveFD( + parameters.chn, + pCanfdBuffer, // CANFD非托管缓冲区指针 + BUFFER_SIZE, + 10 + ); + + if (actualReceive > 0) + { + for (int i = 0; i < actualReceive; i++) + { + // 计算当前CANFD数据的指针 + IntPtr pCurrentCanfd = (IntPtr)(pCanfdBuffer.ToInt64() + i * canfdStructSize); + // 非托管内存转换为C#结构体 + ZLGCAN.ZCAN_ReceiveFD_Data recFDData = (ZLGCAN.ZCAN_ReceiveFD_Data)Marshal.PtrToStructure( + pCurrentCanfd, + typeof(ZLGCAN.ZCAN_ReceiveFD_Data) + ); + + // 通道号和时间戳 + Console.Write(String.Format("CHN:{0} [{1}] CANFD", parameters.chnNum, recFDData.timestamp)); + + // CANFD加速标志 + if ((recFDData.frame.flags & 0x01) == 0x01) + Console.Write("加速"); + + // 帧类型(标准/扩展) + Console.Write((recFDData.frame.can_id & (1 << 31)) != 0 ? " 扩展帧" : " 标准帧"); + + // 方向(Tx/Rx) + Console.Write((recFDData.frame.flags & 0x20) == 0x20 ? " Tx " : " Rx "); + + // 数据长度校验 + int dataLen = Math.Min(recFDData.frame.len, recFDData.frame.data != null ? recFDData.frame.data.Length : 0); + + // ID和数据 + Console.WriteLine(String.Format("ID:{0:X} {1}", + recFDData.frame.can_id & 0x1FFFFFFF, + Program.b_array2string(recFDData.frame.data, dataLen) + )); + } + } + } + + ZLGAPI.ZLGCAN.ZCAN_CHANNEL_ERR_INFO errData = new ZLGAPI.ZLGCAN.ZCAN_CHANNEL_ERR_INFO{ + error_code = 0, + passive_ErrData = new byte[3], + arLost_ErrData = 0 + }; + uint ret = ZLGAPI.ZLGCAN.ZCAN_ReadChannelErrInfo(parameters.chn, ref errData); + Analyze_err_frame(errData); + + Thread.Sleep(10); + } + } + finally + { + // 确保非托管内存必释放 + Marshal.FreeHGlobal(pCanfdBuffer); + Marshal.FreeHGlobal(pCanBuffer); + } + } + + // 接收线程(合并接收) + static void ReceiveThread_Merge(object obj) + { + ThreadParams parameters = (ThreadParams)obj; + + // 预计算结构体大小 + int dataObjSize = Marshal.SizeOf(typeof(ZLGCAN.ZCANDataObj)); + int canfdSize = Marshal.SizeOf(typeof(ZLGCAN.ZCANCANFDData)); + int linSize = Marshal.SizeOf(typeof(ZLGCAN.ZCANLINData)); + + // 分配非托管内存(循环外分配,复用避免频繁申请释放) + IntPtr pDataObjs = Marshal.AllocHGlobal(dataObjSize * 100); // 写死100个最大,可以面对绝大部分情况,如果是动态长度容易崩溃 + IntPtr pCanfdBuffer = Marshal.AllocHGlobal(canfdSize); + IntPtr pLinBuffer = Marshal.AllocHGlobal(linSize); + try + { + while (Program.isRunning) + { + // 获取待接收数据量,无数据则休眠后继续 + uint recvNum = ZLGCAN.ZCAN_GetReceiveNum(parameters.dev, 2); + if (recvNum == 0) + { + Thread.Sleep(10); + continue; + } + + // 接收数据 + uint actualRecv = ZLGCAN.ZCAN_ReceiveData(parameters.dev, pDataObjs, 100, 10); + if (actualRecv == 0) + { + continue; + } + + // 遍历处理每个接收的数据 + for (int i = 0; i < actualRecv; i++) + { + // 计算当前数据对象指针 + IntPtr pCurrentData = (IntPtr)(pDataObjs.ToInt64() + i * dataObjSize); + ZLGCAN.ZCANDataObj dataObj = (ZLGCAN.ZCANDataObj)Marshal.PtrToStructure(pCurrentData, typeof(ZLGCAN.ZCANDataObj)); + + // 根据数据类型处理 + switch (dataObj.dataType) + { + case 1: // CAN/CANFD + + // 复制data到非托管缓冲区 + Marshal.Copy(dataObj.data, 0, pCanfdBuffer, canfdSize); + ZLGCAN.ZCANCANFDData canfdData = (ZLGCAN.ZCANCANFDData)Marshal.PtrToStructure(pCanfdBuffer, typeof(ZLGCAN.ZCANCANFDData)); + + //// 可选:ID过滤(调试用) + //uint canId = canfdData.frame.can_id & 0x1FFFFFFF; + //if (canId != 0x731 && canId != 0x7B1) + // continue; + + // 通道和时间戳 + Console.Write(String.Format("CHN:{0} [{1}] ", dataObj.chnl, canfdData.timeStamp)); + + // CAN/CANFD类型 + 加速标志 + if ((canfdData.flag & 1) == 0) + Console.Write("CAN"); + else + { + Console.Write("CANFD"); + if ((canfdData.frame.flags & 1) == 1) + Console.Write("加速"); + } + + // 帧类型(标准/扩展) + Console.Write((canfdData.frame.can_id & (1 << 31)) != 0 ? " 扩展帧 " : " 标准帧 "); + // 方向(TX/RX) + Console.Write((canfdData.flag & (1 << 9)) != 0 ? "TX " : "RX "); + // ID + Console.Write(String.Format("ID:{0:X} ", canfdData.frame.can_id & 0x1FFFFFFF)); + + // 打印数据 + int canDataLen = Math.Min(canfdData.frame.len, canfdData.frame.data != null ? canfdData.frame.data.Length : 0); + for (int j = 0; j < canDataLen; j++) + Console.Write(String.Format("{0:X2} ", canfdData.frame.data[j])); + Console.WriteLine(); + break; + + case 4: // LIN + // 校验数据长度足够,避免越界复制 + if (dataObj.data.Length < linSize) + break; + + // 直接复制data到非托管缓冲区 + Marshal.Copy(dataObj.data, 0, pLinBuffer, linSize); + ZLGCAN.ZCANLINData linData = (ZLGCAN.ZCANLINData)Marshal.PtrToStructure(pLinBuffer, typeof(ZLGCAN.ZCANLINData)); + + // 拼接打印信息 + Console.Write(String.Format("CHN:{0} [{1}] PID:{2:X} ", + dataObj.chnl, linData.rxData.timeStamp, linData.pid.rawVal & 0x3F)); + + // 方向(TX/RX) + Console.Write(linData.rxData.dir == 1 ? "TX Data: " : "RX Data: "); + + // 打印数据 + int linDataLen = Math.Min(linData.rxData.datalen, linData.rxData.data != null ? linData.rxData.data.Length : 0); + for (int j = 0; j < linDataLen; j++) + Console.Write(String.Format("{0:X2} ", linData.rxData.data[j])); + Console.WriteLine(); + break; + + case 6: // LIN错误 + Console.WriteLine("LIN 错误"); + break; + } + } + } + } + finally + { + // 确保非托管内存必释放(避免泄漏) + Marshal.FreeHGlobal(pLinBuffer); + Marshal.FreeHGlobal(pCanfdBuffer); + Marshal.FreeHGlobal(pDataObjs); + } + } + + // 发送示例 + static void Send_test(IntPtr chnHandle) + { + // CAN + ZLGCAN.ZCAN_Transmit_Data[] can_data = new ZLGCAN.ZCAN_Transmit_Data[10]; + IntPtr p2CanData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Transmit_Data)) * 10); + byte[] sourceData = new byte[] { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 ,0x77,0x88}; + for (int i = 0; i < 10; i++) + { + can_data[i].frame = new ZLGCAN.can_frame(); + can_data[i].transmit_type = 0; // 0-正常发送 + can_data[i].frame.can_id = MakeCanId((uint)0x10, 0, 0, 0); // ID + can_data[i].frame.can_dlc = 8; // 数据长度 + can_data[i].frame.__pad |= 0x20; // 发送回显 + can_data[i].frame.data = new byte[8]; // 此成员必须new8个字节,否则无法运行,数据长度修改frame.can_dlc成员即可 + Array.Copy(sourceData, can_data[i].frame.data, Math.Min(8, sourceData.Length)); + Marshal.StructureToPtr(can_data[i], (IntPtr)(p2CanData + i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Transmit_Data))), true); + } + + uint ret = ZLGCAN.ZCAN_Transmit(chnHandle, p2CanData, 10); + Console.WriteLine("发送 {0} 帧CAN报文", ret); + + // CANFD + ZLGCAN.ZCAN_TransmitFD_Data[] canfd_data = new ZLGCAN.ZCAN_TransmitFD_Data[10]; + IntPtr p2CanFDData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_TransmitFD_Data)) * 10); + + for (int i = 0; i < 10; i++) + { + canfd_data[i].frame = new ZLGCAN.canfd_frame(); + canfd_data[i].transmit_type = 0; // 0-正常发送 + canfd_data[i].frame.can_id = MakeCanId((uint)0x10, 0, 0, 0); // ID + canfd_data[i].frame.len = 64; // 数据长度 + canfd_data[i].frame.flags |= 0x20; // 发送回显 + canfd_data[i].frame.data = new byte[64]; // 此成员必须new64个字节,否则无法运行,数据长度修改frame.len成员即可 + for (int j = 0; j < canfd_data[i].frame.len; j++) + canfd_data[i].frame.data[j] = (byte)j; + Marshal.StructureToPtr(canfd_data[i], (IntPtr)(p2CanFDData + i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_TransmitFD_Data))), true); + } + + uint retfd = ZLGCAN.ZCAN_TransmitFD(chnHandle, p2CanFDData, 10); + Console.WriteLine("发送 {0} 帧CANFD报文", retfd); + + Marshal.FreeHGlobal(p2CanData); + Marshal.FreeHGlobal(p2CanFDData); + } + + // 合并发送 + static void Send_test_Merge(IntPtr dev) + { + const int FRAME_COUNT = 10; // 发送帧数量 + ZLGCAN.ZCANDataObj[] dataObjs = new ZLGCAN.ZCANDataObj[FRAME_COUNT]; + IntPtr pDataObjs = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCANDataObj)) * FRAME_COUNT); + + // canfd_frame + var baseFrame = new ZLGCAN.canfd_frame + { + can_id = MakeCanId(0x10, 0, 0, 0), // ID + len = 64, // 长度 + flags = 0x01, // 1-CANFD加速 + data = new byte[64] + }; + for (int i = 0; i < baseFrame.data.Length; i++) + { + baseFrame.data[i] = (byte)(i % 0xFF); // 数据 + } + + for (int i = 0; i < FRAME_COUNT; i++) + { + // ZCANCANFDData + var canfdData = new ZLGCAN.ZCANCANFDData + { + timeStamp = 0, // 普通发送时 timeStamp 字段无意义 + flag = 0, + extraData = new byte[4], + frame = baseFrame, + frameType = 1, // 0-CAN 1-CANFD + txEchoRequest = 1 // 1-发送回显 + }; + + // ZCANDataObj + dataObjs[i] = new ZLGCAN.ZCANDataObj + { + dataType = 1, // 1-CAN/CANFD + chnl = 0, // 通道 + flag = 0, // 未使用 + data = new byte[92] + }; + + // 结构体转字节数组 + IntPtr pCanFd = Marshal.AllocHGlobal(Marshal.SizeOf(canfdData)); + try + { + Marshal.StructureToPtr(canfdData, pCanFd, false); + Marshal.Copy(pCanFd, dataObjs[i].data, 0, Marshal.SizeOf(canfdData)); + } + finally + { + Marshal.FreeHGlobal(pCanFd); + } + + // 拷贝到非托管内存 + Marshal.StructureToPtr(dataObjs[i], IntPtr.Add(pDataObjs, i * Marshal.SizeOf(typeof(ZLGCAN.ZCANDataObj))), true); + } + + // 发送 + uint sentCount = ZLGCAN.ZCAN_TransmitData(dev, pDataObjs, (uint)FRAME_COUNT); + Console.WriteLine("合并发送 {0} 帧报文", sentCount); + + Marshal.FreeHGlobal(pDataObjs); + } + + // 定时发送 + static void AutoSend_test(IntPtr dev, int chn_idx) + { + // 第一条定时任务,CAN + ZLGCAN.ZCAN_Transmit_Data can_data = new ZLGCAN.ZCAN_Transmit_Data(); + can_data.frame = new ZLGCAN.can_frame(); + can_data.transmit_type = 0; // 0-正常发送 + can_data.frame.can_id = MakeCanId((uint)0x10, 0, 0, 0); // ID + can_data.frame.can_dlc = 8; // 数据长度 + can_data.frame.__pad |= 0x20; // 发送回显 + can_data.frame.data = new byte[8]; + for (int j = 0; j < can_data.frame.can_dlc; j++) + can_data.frame.data[j] = (byte)j; + + ZLGCAN.ZCAN_AUTO_TRANSMIT_OBJ auto_can = new ZLGCAN.ZCAN_AUTO_TRANSMIT_OBJ + { + index = 0, + enable = 1, + interval = 500, + obj = new ZLGCAN.ZCAN_Transmit_Data() + }; + auto_can.obj = can_data; + + string path = String.Format("{0}/auto_send", chn_idx); + uint ret = ZLGCAN.ZCAN_SetValue(dev, path, ref auto_can); // 设置 + if (ret != 1) + { + Console.WriteLine("设置定时发送失败"); + } + + + //// 第二条定时任务,CANFD + //ZLGCAN.ZCAN_TransmitFD_Data canfd_data = new ZLGCAN.ZCAN_TransmitFD_Data(); + //canfd_data.frame = new ZLGCAN.canfd_frame(); + //canfd_data.transmit_type = 0; // 0-正常发送 + //canfd_data.frame.can_id = MakeCanId((uint)0x20, 0, 0, 0); // ID + //canfd_data.frame.len = 64; // 数据长度 + //canfd_data.frame.flags |= 0x20; // 发送回显 + //canfd_data.frame.data = new byte[64]; + //for (int j = 0; j < canfd_data.frame.len; j++) + // canfd_data.frame.data[j] = (byte)j; + + //ZLGCAN.ZCANFD_AUTO_TRANSMIT_OBJ auto_canfd = new ZLGCAN.ZCANFD_AUTO_TRANSMIT_OBJ + //{ + // index = 1, + // enable = 1, + // interval = 500, + // obj = new ZLGCAN.ZCAN_TransmitFD_Data() + //}; + //auto_canfd.obj = canfd_data; + + //path = String.Format("{0}/auto_send_canfd", chn_idx); + //ret = ZLGCAN.ZCAN_SetValue(dev, path, ref auto_canfd); // 设置 + //if (ret != 1) + //{ + // Console.WriteLine("设置定时发送失败"); + //} + + // 启动通道定时任务 + path = String.Format("{0}/apply_auto_send", chn_idx); + ret = ZLGCAN.ZCAN_SetValue(dev, path, "0"); + if (ret != 1) + { + Console.WriteLine("启动定时发送失败"); + } + + + // 覆盖第一条定时任务 + Thread.Sleep(2000); + can_data.frame.can_id = MakeCanId((uint)0x222, 0, 0, 0); // 新ID + auto_can.obj = can_data; + + path = String.Format("{0}/auto_send", chn_idx); + ret = ZLGCAN.ZCAN_SetValue(dev, path, ref auto_can); + if (ret != 1) + { + Console.WriteLine("第二次设置定时发送失败"); + } + else + { + Console.WriteLine("覆盖定时发送成功"); + } + + Thread.Sleep(5000); + + // 清空定时发送 + path = String.Format("{0}/clear_auto_send", 0); + ret = ZLGCAN.ZCAN_SetValue(dev, path, "0"); + if (ret != 1) + { + Console.WriteLine("清空定时发送失败"); + } + } + + // 设置队列发送,and队列发送示例 + static void QueueSend_test(IntPtr dev, IntPtr chnHandle) + { + //// 使能队列发送 + //uint ret = ZLGCAN.ZCAN_SetValue(Program.dev, "0/set_send_mode", "1"); // USBCANFD V1.01版本的才需要配置 + //if (ret != 1) + //{ + // Console.WriteLine("设置队列发送失败"); + // Console.ReadKey(); + //} + + uint ret = 0; + const int FRAME_COUNT = 100; // 发送帧数量 + + // CAN + ZLGCAN.ZCAN_Transmit_Data[] can_data = new ZLGCAN.ZCAN_Transmit_Data[100]; + IntPtr p2CanData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Transmit_Data)) * FRAME_COUNT); + + for (int i = 0; i < FRAME_COUNT; i++) + { + can_data[i].frame = new ZLGCAN.can_frame(); + can_data[i].transmit_type = 0; // 0-正常发送 + can_data[i].frame.can_id = MakeCanId((uint)0x10, 0, 0, 0); // ID + can_data[i].frame.can_dlc = 8; // 数据长度 + can_data[i].frame.__pad |= 0x20; // 发送回显 + can_data[i].frame.__pad |= 0x80; // 设置队列发送 0x80单位ms,0xC0单位100us + can_data[i].frame.__res0 = 0xA; // res1 和 res0两个字节存储时间间隔 + can_data[i].frame.__res1 = 0x0; + can_data[i].frame.data = new byte[8]; + for (int j = 0; j < can_data[i].frame.can_dlc; j++) + can_data[i].frame.data[j] = (byte)j; + Marshal.StructureToPtr(can_data[i], (IntPtr)(p2CanData + i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Transmit_Data))), true); + } + + // 获取队列发送缓存 + IntPtr free_count_p = ZLGCAN.ZCAN_GetValue(dev, "0/get_device_available_tx_count/1"); + int free_count = Marshal.ReadInt32(free_count_p); + Console.WriteLine("队列缓存剩余:{0} 帧", free_count); + if (free_count > FRAME_COUNT) + { + ret = ZLGCAN.ZCAN_Transmit(chnHandle, p2CanData, 100); // 下发给设备 + Console.WriteLine("发送 {0} 帧CAN报文", ret); + } + + // CANFD + ZLGCAN.ZCAN_TransmitFD_Data[] canfd_data = new ZLGCAN.ZCAN_TransmitFD_Data[FRAME_COUNT]; + IntPtr p2CanFDData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_TransmitFD_Data)) * FRAME_COUNT); + for (int i = 0; i < FRAME_COUNT; i++) + { + canfd_data[i].frame = new ZLGCAN.canfd_frame(); + canfd_data[i].transmit_type = 0; // 0-正常发送 + canfd_data[i].frame.can_id = MakeCanId((uint)0x11, 0, 0, 0); // ID + canfd_data[i].frame.len = 64; // 数据长度 + canfd_data[i].frame.flags |= 0x20; // 发送回显 + canfd_data[i].frame.flags |= 0x80; // 设置队列发送 0x80单位ms,0xC0单位100us + canfd_data[i].frame.__res0 = 0x14; + canfd_data[i].frame.__res1 = 0x0; + canfd_data[i].frame.data = new byte[64]; + for (int j = 0; j < canfd_data[i].frame.len; j++) + canfd_data[i].frame.data[j] = (byte)j; + Marshal.StructureToPtr(canfd_data[i], (IntPtr)(p2CanFDData + i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_TransmitFD_Data))), true); + } + + // 获取队列发送缓存 + free_count_p = ZLGCAN.ZCAN_GetValue(dev, "0/get_device_available_tx_count/1"); + free_count = Marshal.ReadInt32(free_count_p); + Console.WriteLine("队列缓存剩余:{0} 帧", free_count); + if (free_count > FRAME_COUNT) + { + uint retfd = ZLGCAN.ZCAN_TransmitFD(chnHandle, p2CanFDData, 10); + Console.WriteLine("发送 {0} 帧CANFD报文", retfd); + } + + Thread.Sleep(2000); // 发送2秒后清空 + ret = ZLGCAN.ZCAN_SetValue(dev, "0/clear_delay_send_queue", "0"); // 清空队列缓存 + if (ret != 1) + { + Console.WriteLine("清空队列缓存失败"); + } + + Marshal.FreeHGlobal(p2CanData); + Marshal.FreeHGlobal(p2CanFDData); + } + + // 错误帧解析 + static void Analyze_err_frame(ZLGAPI.ZLGCAN.ZCAN_CHANNEL_ERR_INFO errData) + { + // 错误码非0时,先输出错误码(替换$为string.Format) + if (errData.error_code != 0x0) + { + Console.Write(string.Format("错误码:{0},", errData.error_code)); + } + + // 匹配错误码逻辑(与C++ switch完全一致) + switch (errData.error_code) + { + case 0x1: + Console.WriteLine("CAN 控制器内部FIFO溢出"); + break; + case 0x2: + Console.WriteLine("CAN 控制器错误报警"); + break; + case 0x4: + Console.Write("CAN 控制器消极错误,"); + // 替换$为string.Format,多个参数按顺序占位 + Console.WriteLine(string.Format("Rx ErrNUM: {0},Tx ErrNUM: {1}", + errData.passive_ErrData[1], errData.passive_ErrData[2])); + break; + case 0x8: + Console.WriteLine("CAN 控制器仲裁丢失"); + break; + case 0x10: + Console.WriteLine("CAN 控制器总线错误"); + break; + case 0x20: + Console.WriteLine("CAN 控制器总线关闭"); + break; + case 0x40: + Console.WriteLine("CAN 缓存溢出"); + break; + default: + break; + } + } + + // 自定义序列号 + static void SN_test(IntPtr dev) + { + // 设置自定义sn号 + if (0 == ZLGCAN.ZCAN_SetValue(dev, "0/set_cn", "C#01")) + { + Console.WriteLine("设置自定义序列号失败"); + Console.ReadKey(); + } + + // 获取自定义sn号 + IntPtr P2CN = ZLGCAN.ZCAN_GetValue(dev, "0/get_cn/1"); + string CN = Marshal.PtrToStringAnsi(P2CN); + Console.WriteLine("设备的自定义序列号:{0}", CN); + } + + // 构造CANID + static uint MakeCanId(uint id, int eff, int rtr, int err) + { + uint ueff = (uint)(!!(Convert.ToBoolean(eff)) ? 1 : 0); // 0 标准帧,1 扩展帧 + uint urtr = (uint)(!!(Convert.ToBoolean(rtr)) ? 1 : 0); // 0 数据帧,1 远程帧 + uint uerr = (uint)(!!(Convert.ToBoolean(err)) ? 1 : 0); // 0 CAN帧,1 错误帧(目前只能设置为 0) + return id | ueff << 31 | urtr << 30 | uerr << 29; + } + + // byte[] -> string + public static string b_array2string(byte[] ByteIn, int LEN) + { + // 空值校验(避免空引用/越界异常) + if (ByteIn == null || ByteIn.Length == 0 || LEN == 0) + return ""; + + // 限制实际处理长度 + int actualLen = Math.Min(LEN, ByteIn.Length); + + // 用StringBuilder优化拼接 + System.Text.StringBuilder sb = new System.Text.StringBuilder(actualLen * 3); // 预分配容量(每个字节占3字符:XX + 空格) + + for (int i = 0; i < actualLen; i++) + { + sb.Append(ByteIn[i].ToString("X2")); // 大写十六进制 + if (i < actualLen - 1) // 最后一个字节后不添加空格 + sb.Append(" "); + } + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Sample/C#_USBCANFD_251215/USBCANFD/Properties/AssemblyInfo.cs b/Sample/C#_USBCANFD_251215/USBCANFD/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c5508f1 --- /dev/null +++ b/Sample/C#_USBCANFD_251215/USBCANFD/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的常规信息通过以下 +// 特性集控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("USBCANFD")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("USBCANFD")] +[assembly: AssemblyCopyright("Copyright © 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 使此程序集中的类型 +// 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, +// 则将该类型上的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("d1f5bf24-cf99-4141-9934-ef199c00e9c4")] + +// 程序集的版本信息由下面四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, +// 方法是按如下所示使用“*”: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Sample/C#_USBCANFD_251215/USBCANFD/USBCANFD.csproj b/Sample/C#_USBCANFD_251215/USBCANFD/USBCANFD.csproj new file mode 100644 index 0000000..32fee4b --- /dev/null +++ b/Sample/C#_USBCANFD_251215/USBCANFD/USBCANFD.csproj @@ -0,0 +1,99 @@ + + + + + Debug + AnyCPU + {B878ECF2-86D7-4907-A7FD-66899F10ECF2} + Exe + Properties + USBCANFD + USBCANFD + v4.5 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Sample/C#_USBCANFD_251215/USBCANFD/ZLGAPI.cs b/Sample/C#_USBCANFD_251215/USBCANFD/ZLGAPI.cs new file mode 100644 index 0000000..13d3592 --- /dev/null +++ b/Sample/C#_USBCANFD_251215/USBCANFD/ZLGAPI.cs @@ -0,0 +1,1236 @@ +// update time 2025/12/11 + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace ZLGAPI +{ + + public class ZLGCAN + { + + #region 设备类型 + public static UInt32 ZCAN_PCI9810 = 2; + public static UInt32 ZCAN_USBCAN1 = 3; + public static UInt32 ZCAN_USBCAN2 = 4; + public static UInt32 ZCAN_PCI9820 = 5; + public static UInt32 ZCAN_CANETUDP = 12; + public static UInt32 ZCAN_PCI9840 = 14; + public static UInt32 ZCAN_PCI9820I = 16; + public static UInt32 ZCAN_CANETTCP = 17; + public static UInt32 ZCAN_PCI5010U = 19; + public static UInt32 ZCAN_USBCAN_E_U = 20; + public static UInt32 ZCAN_USBCAN_2E_U = 21; + public static UInt32 ZCAN_PCI5020U = 22; + public static UInt32 ZCAN_PCIE9221 = 24; + public static UInt32 ZCAN_WIFICAN_TCP = 25; + public static UInt32 ZCAN_WIFICAN_UDP = 26; + public static UInt32 ZCAN_PCIe9120 = 27; + public static UInt32 ZCAN_PCIe9110 = 28; + public static UInt32 ZCAN_PCIe9140 = 29; + public static UInt32 ZCAN_USBCAN_4E_U = 31; + public static UInt32 ZCAN_CANDTU_200UR = 32; + public static UInt32 ZCAN_USBCAN_8E_U = 34; + public static UInt32 ZCAN_CANDTU_NET = 36; + public static UInt32 ZCAN_CANDTU_100UR = 37; + public static UInt32 ZCAN_PCIE_CANFD_200U = 39; + public static UInt32 ZCAN_PCIE_CANFD_400U = 40; + public static UInt32 ZCAN_USBCANFD_200U = 41; + public static UInt32 ZCAN_USBCANFD_100U = 42; + public static UInt32 ZCAN_USBCANFD_MINI = 43; + public static UInt32 ZCAN_CANSCOPE = 45; + public static UInt32 ZCAN_CLOUD = 46; + public static UInt32 ZCAN_CANDTU_NET_400 = 47; + public static UInt32 ZCAN_CANFDNET_TCP = 48; + public static UInt32 ZCAN_CANFDNET_200U_TCP = 48; + public static UInt32 ZCAN_CANFDNET_UDP = 49; + public static UInt32 ZCAN_CANFDNET_200U_UDP = 49; + public static UInt32 ZCAN_CANFDWIFI_TCP = 50; + public static UInt32 ZCAN_CANFDWIFI_100U_TCP = 50; + public static UInt32 ZCAN_CANFDWIFI_UDP = 51; + public static UInt32 ZCAN_CANFDWIFI_100U_UDP = 51; + public static UInt32 ZCAN_CANFDNET_400U_TCP = 52; + public static UInt32 ZCAN_CANFDNET_400U_UDP = 53; + public static UInt32 ZCAN_CANFDNET_100U_TCP = 55; + public static UInt32 ZCAN_CANFDNET_100U_UDP = 56; + public static UInt32 ZCAN_CANFDNET_800U_TCP = 57; + public static UInt32 ZCAN_CANFDNET_800U_UDP = 58; + public static UInt32 ZCAN_USBCANFD_800U = 59; + public static UInt32 ZCAN_PCIE_CANFD_100U_EX = 60; + public static UInt32 ZCAN_PCIE_CANFD_400U_EX = 61; + public static UInt32 ZCAN_PCIE_CANFD_200U_MINI = 62; + public static UInt32 ZCAN_PCIE_CANFD_200U_EX = 63; + public static UInt32 ZCAN_PCIE_CANFD_200U_M2 = 63; + public static UInt32 ZCAN_CANFDDTU_400_TCP = 64; + public static UInt32 ZCAN_CANFDDTU_400_UDP = 65; + public static UInt32 ZCAN_CANFDWIFI_200U_TCP = 66; + public static UInt32 ZCAN_CANFDWIFI_200U_UDP = 67; + public static UInt32 ZCAN_CANFDDTU_800ER_TCP = 68; + public static UInt32 ZCAN_CANFDDTU_800ER_UDP = 69; + public static UInt32 ZCAN_CANFDDTU_800EWGR_TCP = 70; + public static UInt32 ZCAN_CANFDDTU_800EWGR_UDP = 71; + public static UInt32 ZCAN_CANFDDTU_600EWGR_TCP = 72; + public static UInt32 ZCAN_CANFDDTU_600EWGR_UDP = 73; + public static UInt32 ZCAN_CANFDDTU_CASCADE_TCP = 74; + public static UInt32 ZCAN_CANFDDTU_CASCADE_UDP = 75; + public static UInt32 ZCAN_USBCANFD_400U = 76; + public static UInt32 ZCAN_CANFDDTU_200U = 77; + public static UInt32 ZCAN_ZPSCANFD_TCP = 78; + public static UInt32 ZCAN_ZPSCANFD_USB = 79; + public static UInt32 ZCAN_CANFDBRIDGE_PLUS = 80; + public static UInt32 ZCAN_CANFDDTU_300U = 81; + public static UInt32 ZCAN_PCIE_CANFD_800U = 82; + public static UInt32 ZCAN_PCIE_CANFD_1200U = 83; + public static UInt32 ZCAN_MINI_PCIE_CANFD = 84; + public static UInt32 ZCAN_USBCANFD_800H = 85; + #endregion + + #region LIN事件 + public static UInt32 ZCAN_LIN_WAKE_UP = 1; + public static UInt32 ZCAN_LIN_ENTERED_SLEEP_MODE = 2; + public static UInt32 ZCAN_LIN_EXITED_SLEEP_MODE = 3; + #endregion + + #region 函数 + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_OpenDevice(uint device_type, uint device_index, uint reserved); + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_OpenDeviceByName(uint device_type, string name); + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_CloseDevice(IntPtr device_handle); + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_IsDeviceOnLine(IntPtr device_handle); + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_GetDeviceInf(IntPtr device_handle, IntPtr pInfo); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_InitCAN(IntPtr device_handle, uint can_index, IntPtr pInitConfig); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_InitCAN(IntPtr device_handle, uint can_index, ref ZCAN_CHANNEL_INIT_CONFIG config); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_StartCAN(IntPtr chn_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ResetCAN(IntPtr chn_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ClearBuffer(IntPtr chn_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReadChannelErrInfo(IntPtr chn_handle, ref ZCAN_CHANNEL_ERR_INFO pErrInfo); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_GetReceiveNum(IntPtr channel_handle, byte type); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_Transmit(IntPtr channel_handle, IntPtr pTransmit, uint len); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_TransmitFD(IntPtr channel_handle, IntPtr pTransmit, uint len); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_TransmitData(IntPtr device_handle, IntPtr pTransmit, uint len); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_Receive(IntPtr channel_handle, IntPtr pReceive, uint len, int wait_time = -1); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReceiveFD(IntPtr channel_handle, IntPtr pReceive, uint len, int wait_time = -1); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReceiveData(IntPtr device_handle, IntPtr pReceive, uint len, int wait_time); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetValue(IntPtr device_handle, string path, string value); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetValue(IntPtr device_handle, string path, IntPtr value); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetValue(IntPtr device_handle, string path, ref ZCAN_AUTO_TRANSMIT_OBJ value); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetValue(IntPtr device_handle, string path, ref ZCANFD_AUTO_TRANSMIT_OBJ value); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_GetValue(IntPtr device_handle, string path); + + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_InitLIN(IntPtr device_handle, uint lin_index, IntPtr pLINInitConfig); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_StartLIN(IntPtr channel_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ResetLIN(IntPtr channel_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_TransmitLIN(IntPtr channel_handle, IntPtr pSend, uint Len); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_GetLINReceiveNum(IntPtr channel_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReceiveLIN(IntPtr channel_handle, IntPtr pReceive, uint Len, int WaitTime); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetLINPublish(IntPtr channel_handle, IntPtr pSend, uint nPublishCount); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetLINSubscribe(IntPtr channel_handle, IntPtr pSend, uint nSubscribeCount); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_WakeUpLIN(IntPtr channel_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_Request(IntPtr device_handle, IntPtr req, IntPtr resp, IntPtr dataBuf, uint dataBufSize); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_Control(IntPtr device_handle, IntPtr ctrl, IntPtr resp); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_RequestEX(IntPtr device_handle, IntPtr requestData, IntPtr resp, IntPtr dataBuf, uint dataBufSize); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_ControlEX(IntPtr device_handle, uint dataType, IntPtr ctrl, IntPtr resp); + #endregion + + #region 结构体 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_DEVICE_INFO + { + public ushort hw_Version; + public ushort fw_Version; + public ushort dr_Version; + public ushort in_Version; + public ushort irq_Num; + public byte can_Num; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public char[] str_Serial_Num; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)] + public char[] str_hw_Type; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_CHANNEL_INIT_CONFIG + { + public uint can_type; + public _ZCAN_CHANNEL_INIT_CONFIG config; + } + + + [StructLayout(LayoutKind.Explicit, Pack = 1)] + public struct _ZCAN_CHANNEL_INIT_CONFIG + { + [FieldOffset(0)] + public _ZCAN_CHANNEL_CAN_INIT_CONFIG can; + [FieldOffset(0)] + public _ZCAN_CHANNEL_CANFD_INIT_CONFIG canfd; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZCAN_CHANNEL_CAN_INIT_CONFIG + { + public uint acc_code; + public uint acc_mask; + public uint reserved; + public byte filter; + public byte timing0; + public byte timing1; + public byte mode; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZCAN_CHANNEL_CANFD_INIT_CONFIG + { + public uint acc_code; + public uint acc_mask; + public uint abit_timing; + public uint dbit_timing; + public uint brp; + public byte filter; + public byte mode; + public ushort pad; + public uint reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_CHANNEL_ERR_INFO + { + public uint error_code; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public byte[] passive_ErrData; + public byte arLost_ErrData; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_Transmit_Data + { + public can_frame frame; + public uint transmit_type; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class can_frame + { + public uint can_id; + public byte can_dlc; // frame payload length in byte (0 .. CAN_MAX_DLEN) + public byte __pad; // padding + public byte __res0; // reserved / padding + public byte __res1; // reserved / padding + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] data; // frame data + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class canfd_frame + { + public uint can_id; + public byte len; + public byte flags; + public byte __res0; + public byte __res1; /* reserved / padding */ + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public byte[] data; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZCAN_Receive_Data + { + public can_frame frame; + public UInt64 timestamp; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZCAN_ReceiveFD_Data + { + public canfd_frame frame; + public UInt64 timestamp; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_AUTO_TRANSMIT_OBJ //CANFD定时发送帧结构体 + { + public ushort enable; //0-禁用,1-使能 + public ushort index; //定时报文索引 + public uint interval; //定时周期 + public ZCAN_Transmit_Data obj; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANFD_AUTO_TRANSMIT_OBJ //CANFD定时发送帧结构体 + { + public ushort enable; //0-禁用,1-使能 + public ushort index; //定时报文索引 + public uint interval; //定时周期 + public ZCAN_TransmitFD_Data obj; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_TransmitFD_Data + { + public canfd_frame frame; // 报文数据信息,详见 canfd_frame 结构说明。 + public uint transmit_type; // 发送方式,0=正常发送,1=单次发送,2=自发自收,3=单次自发自收。 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANCANFDData + { + public UInt64 timeStamp; + public UInt32 flag; // flag用于设置一些参数,内部结构可以通过以下函数实现设置和取值 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] extraData; //未使用 + public canfd_frame frame; //实际报文结构体 + + // frameType 帧类型 0-CAN 1-CANFD + public uint frameType { + get { return (flag & 0x03); } + set { flag = (uint)((flag & ~0x03) | (value & 0x03)); } + } + + // txDelay队列发送延时,延时时间存放在 timeStamp 字段;0-不启用延时,1-启用延时,单位1ms,2-启用延时,单位100us + public uint txDelay { + get { return ((flag >> 2) & 0x03); } + set { flag = (uint)((flag & ~0x0C) | (value & 0x03) << 2); } + } + + public uint transmitType { + get { return ((flag >> 4) & 0x0F); } + set { flag = (uint)((flag & ~0x0F) | (value & 0x0F) << 4); } + } + + public uint txEchoRequest { + get { return ((flag >> 8) & 0x01); } + set { flag = (uint)(flag | (value & 0x01) << 8); } + } + + public uint txEchoed { + get { return ((flag >> 9) & 0x01); } // bit9 + set { flag = (uint)((flag & ~0x200) | (value & 0x01) << 9); } + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct PID + { + public byte rawVal; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct RxData + { + public UInt64 timeStamp; + public byte datalen; + public byte dir; + public byte chkSum; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] + public byte[] reserved; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] data; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANDataObj + { + public byte dataType; // 1-CAN/CANFD数据, 4-LIN数据 + public byte chnl; // 数据通道 + public UInt16 flag; // 未使用 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] extraData; // 未使用 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 92)] + public byte[] data; // 报文结构体 + } + + + // LIN + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINData + { + public PID pid; // 受保护的ID + public RxData rxData; // 数据 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINErrData + { + public UInt64 timeStamp; // 时间戳,单位微秒(us) + public PID pid; // 受保护的ID + + public byte dataLen; // 数据长度 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] data; // 数据 + + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] errData; // 错误信息 + + public byte dir; // 传输方向 + public byte chkSum; // 数据校验,部分设备不支持校验数据的获取 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINEventData + { + public UInt64 timeStamp; // 时间戳,单位微秒(us) + public byte type; // 数据长度 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_MSG + { + public byte chnl; // 数据通道 + public byte dataType; // 0-LIN,1-ErrLIN + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 46)] + public byte[] data; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_INIT_CONFIG + { + public byte linMode; // 0-slave,1-master + public byte chkSumMode; // 1-经典校验,2-增强校验 3-自动(对应eZLINChkSumMode的模式) + public byte maxLength; // 最大数据长度,8~64 + public byte reserved; + public uint libBaud; // 波特率,取值1000~20000 + + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_PUBLISH_CFG + { + public byte ID; // 受保护的ID(ID取值范围为0-63) + public byte datelen; // 范围1~8 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 数据段内容 + public byte[] data; + public byte chkSumMode; // 校验方式:0-默认,启动时配置 1-经典校验 2-增强校验(对应eZLINChkSumMode的模式) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public byte[] reserved; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_SUBSCIBE_CFG + { + public byte ID; // 受保护的ID(ID取值范围为0-63) + public byte datelen; // dataLen范围为1-8 当为255(0xff)则表示设备自动识别报文长度 + public byte chkSumMode; // 校验方式:0-默认,启动时配置 1-经典校验 2-增强校验(对应eZLINChkSumMode的模式) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public byte[] reserved; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct SESSION_PARAM + { + public uint timeout; // 响应超时时间(ms)。因PC定时器误差,建议设置不小于200ms + public uint enhanced_timeout; // 收到消极响应错误码为0x78后的超时时间(ms)。因PC定时器误差,建议设置不小于200ms + public byte threeInOne; //三合一,把下面三个变量写进这一个变量 + + // threeInOne 包含以下三个变量 + // BYTE check_any_negative_response : 1; // 接收到非本次请求服务的消极响应时是否需要判定为响应错误 + // BYTE wait_if_suppress_response : 1; // 抑制响应时是否需要等待消极响应,等待时长为响应超时时间 + // BYTE flag : 6; // 保留 + public byte check_any_negative_response + { + get { return (byte)(threeInOne & 0x0001); } + set { threeInOne = (byte)((threeInOne & ~0x0001) | (value & 0x0001)); } + } + public byte wait_if_suppress_response + { + get { return (byte)((threeInOne & 0x0002) >> 1); } + set { threeInOne = (byte)((threeInOne & ~0x0002) | (value & 0x0002)); } + } + public byte flag + { + get { return (byte)((threeInOne & 0x00FC) >> 2); } + set { threeInOne = (byte)((threeInOne & ~0x00FC) | (value & 0x00FC)); } + } + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] reserved0; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct TRANS_PARAM + { + public byte version; // 传输协议版本,VERSION_0,VERSION_1 + public byte max_data_len; // 单帧最大数据长度,can:8,canfd:64 + public byte local_st_min; // 本程序发送流控时用,连续帧之间的最小间隔,0x00-0x7F(0ms~127ms),0xF1-0xF9(100us~900us) + public byte block_size; // 流控帧的块大小 + public byte fill_byte; // 无效字节的填充数据 + public byte ext_frame; // 0:标准帧 1:扩展帧 + public byte is_modify_ecu_st_min; // 是否忽略ECU 返回流控的STmin,强制使用本程序设置的remote_st_min 参数代替 + public byte remote_st_min; // 发送多帧时用, is_modify_ecu_st_min = 1 时有效,0x00 - 0x7F(0ms~127ms), 0xF1 - 0xF9(100us~900us) + public uint fc_timeout; // 接收流控超时时间(ms),如发送首帧后需要等待回应流控帧 + public byte fill_mode; // 数据长度填充模式 0-就近填充 1-不填充 2-最大填充 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public byte[] reserved0; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct TRANS_PARAM_LIN + { + public byte fill_byte; // 无效字节的填充数据 + public byte st_min; // 从节点准备接收诊断请求的下一帧或传输诊断响应的下一帧所需的最小时间 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] reserved0; + }; + + + // CAN UDS请求数据 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_UDS_REQUEST // 硬件UDS接口构体 + { + public uint req_id; // 请求事务索引ID,范围0~65535 + public byte channel; // 设备通道索引 + public byte frame_type; // 帧类型 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] reserved0; + public uint src_addr; // 请求地址 + public uint dst_addr; // 响应地址 + public byte suppress_response; // 1-抑制响应 + public byte sid; // 请求服务id + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] reserved1; + + public SESSION_PARAM session_param; + public TRANS_PARAM trans_param; + public IntPtr data; // 数据数组(不包含SID) + public uint data_len; // 数据数组的长度 + public uint reserved2; + } + + + // LIN UDS请求数据 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZLIN_UDS_REQUEST // 硬件UDS接口构体 + { + public uint req_id; // 请求事务索引ID,范围0~65535 + public byte channel; // 设备通道索引 + public byte suppress_response; // 1:抑制响应 0:不抑制 + public byte sid; // 请求服务id + public byte Nad; // 节点地址 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] reserved1; + + + public SESSION_PARAM session_param; + public TRANS_PARAM_LIN trans_param; + public IntPtr data; // 数据数组(不包含SID) + public uint data_len; // 数据数组的长度 + public uint reserved2; + } + + + // UDS响应数据 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_UDS_RESPONSE + { + public byte status; // 响应状态 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] reserved; + public byte type; // 响应类型 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 大小为8 + public byte[] raw; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct UDS_RESPONSE_Positive + { + public byte sid; // 响应服务id + public uint data_len; // 数据长度(不包含SID), 数据存放在接口传入的dataBuf中 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct UDS_RESPONSE_Negative + { + public byte neg_code; // 固定为0x7F + public byte sid; // 请求服务id + public byte error_code; // 错误码 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct UDS_RESPONSE_raw + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 大小为8 + public byte[] raw; + } + + + [StructLayout(LayoutKind.Explicit)] + public struct _UDS_RESPONSE_union + { + [FieldOffset(0)] + public UDS_RESPONSE_Positive zudsPositive; + + [FieldOffset(0)] + public UDS_RESPONSE_Negative zudsNegative; + + [FieldOffset(0)] + public UDS_RESPONSE_raw raw; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANCANFDUdsData + { + public IntPtr req; // 请求信息 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + public byte[] reserved; // 保留位 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINUdsData + { + public IntPtr req; // 请求信息 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + public byte[] reserved; // 保留位 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANUdsRequestDataObj + { + public uint dataType; // 数据类型 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)] + public byte[] data; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] reserved; // 保留位 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_DYNAMIC_CONFIG_DATA + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public char[] key; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public char[] value; + } + + [StructLayout(LayoutKind.Sequential)] + public struct BusUsage + { + public ulong nTimeStampBegin; // 测量起始时间戳,单位us + public ulong nTimeStampEnd; // 测量结束时间戳,单位us + public byte nChnl; // 通道 + public byte nReserved; // 保留 + public ushort nBusUsage; // 总线利用率(%),总线利用率*100展示。取值0~10000,如8050表示80.50% + public uint nFrameCount; // 帧数量 + } + #endregion + } + + + public class ZDBC + { + #region 常量定义 + public const int _MAX_FILE_PATH_ = 260; // 最长文件路径 + public const int _DBC_NAME_LENGTH_ = 127; // 名称最长长度 + public const int _DBC_COMMENT_MAX_LENGTH_ = 127; // 注释最长长度 + public const int _DBC_UNIT_MAX_LENGTH_ = 23; // 单位最长长度 + public const int _DBC_SIGNAL_MAX_COUNT_ = 256; // 一个消息含有的信号的最大数目 + + public const int MUTIPLEXER_NONE = 0; // 不使用复用器 + public const int MUTIPLEXER_M_VALUE = 1; // 复用信号,当复用器开关的值为multiplexer_value时,该信号有效 + public const int MUTIPLEXER_M_SWITCH = 2; // 复用器开关,一个DBC消息只能有一个信号为开关 + + public const int FT_CAN = 0; // CAN + public const int FT_CANFD = 1; // CANFD + + public const int PROTOCOL_J1939 = 0; + public const int PROTOCOL_OTHER = 1; + public const uint INVALID_DBC_HANDLE = 0xffffffff; // 无效的DBC句柄 + #endregion + + #region 函数部分 + + // ZDBC.dll + public delegate bool OnSend(IntPtr ctx, IntPtr pObj); + public delegate void OnMultiTransDone(IntPtr ctx, IntPtr pMsg, IntPtr data, UInt16 nLen, byte nDirection); + public static OnSend onSend; + public static OnMultiTransDone onMultiTransDone; + + /// + /// 此函数用于初始化解析模块,只需要初始化一次。 + /// + /// 是否关闭多帧发送,为 1 时不支持多帧的消息发送。 + /// 是否开启异步解析;0-不启动,ZDBC_AsyncAnalyse 接口无效;1-启动, 独立线程解析出消息。 + /// 为 INVALID_DBC_HANDLE 表示初始化失败,其他表示初始化成功,保存该返回值,之后的函数调用都要用到该句柄。 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZDBC_Init(byte disableMultiSend = 0, byte enableAsyncAnalyse = 1); + + /// + /// 释放资源, 与DBC_Init配对使用 + /// + /// hDBC-句柄, ZDBC_Init的返回值 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_Release(uint DBCHandle); + + /// + /// 此函数用以加载 DBC 格式文件。 + /// + /// 句柄;ZDBC_Init的返回值 + /// 结构体 FileInfo的指针 + /// 为 true 表示加载成功,false 表示失败。 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_LoadFile(uint DBCHandle, IntPtr pFileInfo); + + /// + /// 从字符串加载DBC + /// + /// hDBC-句柄, DBC_Load的返回值 + /// pFileContent-文件内容字符串 + /// merge-是否合并到当前数据库; 1:不清除现有的数据, 即支持加载多个文件;0:清除原来的数据 + /// + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_LoadContent(uint DBCHandle, IntPtr pFileContent, uint merge); + + /// + /// 获取文件的第一条消息。 + /// + /// hDBC-句柄, DBC_Load的返回值 + /// pMsg 存储消息的信息 + /// true表示成功 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetFirstMessage(uint DBCHandle, IntPtr pMsg); + + /// + /// 获取下一条消息。 + /// + /// hDBC-句柄, DBC_Load的返回值 + /// pMsg 存储消息的信息 + /// true表示成功 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetNextMessage(uint DBCHandle, IntPtr pMsg); + + /// + /// 此函数用以根据 ID 获取消息数据。 + /// + /// 句柄; + /// 帧 ID; + /// 消息信息结构体 + /// + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetMessageById(uint DBCHandle, uint nID, IntPtr pMsg); + + /// + /// 此函数用以获取 DBC 文件中含有的消息数目。 + /// + /// DBC句柄 + /// DBC 文件中含有的消息数目 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZDBC_GetMessageCount(uint DBCHandle); + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_Analyse(uint DBCHandle, IntPtr pObj, IntPtr pMsg); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_AsyncAnalyse(uint DBCHandle, IntPtr pObj, uint frame_type, UInt64 extraData); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_OnReceive(uint DBCHandle, IntPtr pObj, uint frame_type); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_SetSender(uint hDBC, OnSend sender, IntPtr ctx); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_SetOnMultiTransDoneFunc(uint hDBC, OnMultiTransDone func, IntPtr ctx); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern byte ZDBC_Send(uint hDBC, IntPtr pMsg); + + /// + /// 根据原始数据解码为 DBCMessage。 + /// + /// DBC句柄 + /// 输出参数,解析结果。 + /// 帧数据数组, ControlCAN 传入 VCI_CAN_OBJ, zlgcan 传入 can_frame。 + /// 原始帧数据个数, 即数组大小。 + /// frame_type 帧类型, 参考FT_CAN=0、FT_CANFD=1,ControlCAN不支持CANFD。 + /// 是否成功。 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_Decode(uint DBCHandle, IntPtr P2DBCMessage, IntPtr P2Obj, uint nCount, byte frame_type); + + /// + /// 根据 DBCMessage 编码为原始数据。 + /// + /// DBC句柄; + /// 编码的原始数据缓冲区数组, ControlCAN 传入 VCI_CAN_OBJ, zlgcan 传入 can_frame。 + /// 输出参数,pObj 缓冲区大小, 返回时为实际原始数据个数。 + /// 输入参数,DBC 消息。 + /// frame_type 帧类型, FT_CAN=0、FT_CANFD=1,ControlCAN不支持CANFD。 + /// 是否成功。 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_Encode(uint DBCHandle, IntPtr P2Obj, IntPtr P2nCount, IntPtr pMsg, byte frame_type); + + /// + /// 信号原始值转换为实际值 + /// + /// sgl 信号 + /// rawVal 原始值, 如果该值超出信号长度可表示范围,会被截断。 + /// 实际值 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern double ZDBC_CalcActualValue(IntPtr sgl, IntPtr rawVal); //原始值通过计算转为实际值,实际值会传入rawVal的地址 + + /// + /// 信号实际值转换为原始值 + /// + /// sgl 信号 + /// actualVal 实际值, 超出可表示范围时会被修正 + /// 原始值 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt64 ZDBC_CalcRawValue(IntPtr sgl, IntPtr actualVal); + + /// + /// 获取网络节点数量 + /// + /// ZDBC_Init的返回值 + /// 网络节点总数量 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt32 ZDBC_GetNetworkNodeCount(uint DBCHandle); + + /// + /// + /// + /// ZDBC_Init的返回值 + /// index 位置索引 + /// DBCNetworkNode * node 网络节点信息 + /// + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetNetworkNodeAt(uint DBCHandle, UInt32 index, IntPtr node); + + + /// + /// 获取具体信号的值与含义对个数 + /// + /// ZDBC_Init的返回值 + /// message的ID + /// signal的名字 + /// + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt32 ZDBC_GetValDescPairCount(uint DBCHandle, UInt32 mag_id, string signal_name); + + + /// + /// 获取具体信号的值与含义对 + /// + /// ZDBC_Init的返回值 + /// message的ID + /// signal的名字 + /// ValDescPair结构体类型 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_GetValDescPair(uint DBCHandle, UInt32 mag_id, string signal_name, IntPtr pair); + + + #endregion + + #region DBC 结构体部分 + + public struct FileInfo + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _MAX_FILE_PATH_ + 1)] + public byte[] strFilePath; // dbc文件路径 + public byte type; // dbc的协议类型, j1939选择PROTOCOL_J1939, 其他协议选择PROTOCOL_OTHER + public byte merge; // 1:不清除现有的数据, 即支持加载多个文件 0:清除原来的数据 + }; + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct DBCSignal + { + + public UInt32 nStartBit; // 起始位 + public UInt32 nLen; // 位长度 + public double nFactor; // 转换因子 + public double nOffset; // 转换偏移实际值=原始值*nFactor+nOffset + public double nMin; // 最小值 + public double nMax; // 最大值 + public UInt64 nRawvalue; // 原始值 + public byte is_signed; // 1:有符号数据, 0:无符号 + public byte is_motorola; // 是否摩托罗拉格式 + public byte multiplexer_type; // 复用器类型 + public byte val_type; // 0:integer, 1:float, 2:double + public UInt32 multiplexer_value; // 复用器开关值为此值时信号有效 + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_UNIT_MAX_LENGTH_ + 1)] + public byte[] unit; //单位 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strName; //名称 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_COMMENT_MAX_LENGTH_ + 1)] + public byte[] strComment; //注释 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strValDesc; //值描述 + + public double initialValue; // 初始化值(原始值) + public uint initialValueValid; // 初始值是否有效 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct DBCMessage + { + public UInt32 nSignalCount; // 信号数量 + public UInt32 nID; // ID + public UInt32 nSize; // 消息占的字节数目 + public double nCycleTime; // 发送周期 + public byte nExtend; // 1:扩展帧, 0:标准帧 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_SIGNAL_MAX_COUNT_)] + public DBCSignal[] vSignals; // 信号集合 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strName; // 名称 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_COMMENT_MAX_LENGTH_ + 1)] + public byte[] strComment; // 注释 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct ValDescPair + { + public double value; // 信号值 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strName; // 对应的值描述 + } + #endregion + } + + + public class ZUDS + { + #region 参数定义 + + public static uint udsRTR = 0x40000000; // Remote Transmission Request + public static uint udsEFF = 0x80000000; // Extend Frame Flag + public static uint udsERR = 0x20000000; // Err flag + + #endregion + + #region 函数部分 + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate uint OnUDSTransmitDelegate(IntPtr ctx, IntPtr frame, uint count); + + + /// + /// 该函数用于初始化 UDS 函数库,返回操作句柄,用于后续的操作,与 ZUDS_Release + /// 配对使用。 + /// typedef uint32 TP_TYPE; // transport protocol + /// #define DoCAN 0 + /// + /// + /// 操作句柄,= ZUDS_INVALID_HANDLE 为无效句柄,其他值为有效句柄。 + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt32 ZUDS_Init(uint type); + + + /// + /// 该函数用于释放资源,与 ZUDS_Init 配对使用。 + /// + /// + /// + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_Release(uint type); + + + /// + /// 该函数用于设置函数库的参数。 + /// + /// + /// 参数类型,= PARAM_TYPE_SESSION 0 用 于 设 置 会 话 层 参 数 , = + ///PARAM_TYPE_ISO15765 1 用于设置 ISO15765 的通信参数; + /// 参数值,type =PARAM_TYPE_SESSION 0 时为 ZUDS_SESSION_PARAM, + ///type= PARAM_TYPE_ISO15765 1 时为 ZUDS_ISO15765_PARAM。 + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_SetParam(UInt32 ZUDS_HANDLE, byte type, IntPtr param); + + + /// + /// 该函数用于设置发送回调函数。函数库自身并不发送帧数据,把打包的帧数据通过回调 + ///函数传出给用户发送,用户可通过 zlgcan 函数库进行帧数据发送。 + /// + /// + /// ctx 上下文参数, 在回调函数中传出, 库内部不会处理该参数; + /// :回调函数原型;typedef uint32 (*OnUDSTransmit)(void* ctx, const ZUDS_FRAME* frame, uint32 count); + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_SetTransmitHandler(UInt32 ZUDS_HANDLE, IntPtr ctx, OnUDSTransmitDelegate onUDSTransmit); + + + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_OnReceive(UInt32 ZUDS_HANDLE, IntPtr ZUDS_FRAME); + + + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_Request(UInt32 ZUDS_HANDLE, IntPtr ZUDS_REQUEST, IntPtr ZUDS_RESPONSE); + + + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_Stop(UInt32 ZUDS_HANDLE); + + + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_SetTesterPresent(UInt32 ZUDS_HANDLE, byte enable, IntPtr param); + + #endregion + + #region 结构体部分 + /// + /// 会话层面参数;即一应一答传输时的通讯参数。 + /// + public struct ZUDS_SESSION_PARAM + { + public UInt16 timeout;// ms, timeout to wait the response of the server + public UInt16 enhanced_timeout; // ms,timeout to wait after negative response: error code 0x78 + public UInt32 reserved0; // 保留 + public UInt32 reserved1; // 保留 + } + + + /// + /// 传输数据部分的参数,例如传输时侯每帧报文的字节数。 + /// + public struct ZUDS_ISO15765_PARAM + { + public byte version; // VERSION_0, VERSION_1格式版本,为 VERSION_0 时符合 ISO15765-2 的 2004 版本格式要求;为 + //hVERSION_1 是符合 ISO15765-2 的 2016 版本新增的格式要求,如下图所示 + public byte max_data_len; // max data length, can:8, canfd:64 + public byte local_st_min; // ms, min time between two consecutive frames + public byte block_size; + public byte fill_byte; // fill to invalid byte + public byte frame_type; // 0:std 1:ext + public byte is_modify_ecu_st_min; //是否忽略 ECU 返回流控的 STmin,强制使用本程序设置的 + //remote_st_min 参数代替 + + public byte remote_st_min; //发 送 多 帧 时 用, is_ignore_ecu_st_min = 1 时 有 效 , + //0x00-0x7F(0ms ~127ms), 0xF1-0xF9(100us ~900us) + public UInt16 fc_timeout; //接收流控超时时间(ms), 如发送首帧后需要等待回应流控帧。 + public byte fill_mode;//字节填充模式。FILL_MODE_NONE-不填充0;FILL_MODE_SHORT- 小于 8 字节填充至 8 字节,大于 8 字节时按 DLC 就近填充1;FILL_MODE_MAX- 始终填充至最大数据长度 (不建议)2。 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZUDS_TESTER_PRESENT_PARAM + { + public UInt32 addr;//会话保持的请求地址; + public UInt16 cycle;//发送周期,单位毫秒; + public byte suppress_response; // 1:suppress是否抑制响应,建议设置为 1; + public UInt32 reserved;//:保留,忽略即可。 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZUDS_REQUEST + { + public uint src_addr; // 请求地址 + public uint dst_addr; // 响应地址 + public byte suppress_response; // 1:抑制响应 + public byte sid; //service id of request + public ushort reserve0; + public IntPtr param; //array,params of the service + public uint param_len; //参数数组的长度 + public uint reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZUDS_RESPONSE + { + public byte status; + public byte type; // RT_POSITIVE, RT_NEGATIVE + public _ZUDS_Union union; + public uint reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZUDS_positive + { + public byte sid; // service id of response + public IntPtr param; // array, params of the service, don't free + public uint param_len; + + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZUDS_negative + { + public byte neg_code; // 0x7F + public byte sid; //service id of response + public byte error_code;//消极响应的错误码 + + } + + + [StructLayout(LayoutKind.Explicit)] + public struct _ZUDS_Union + { + [FieldOffset(0)] + public ZUDS_positive zudsPositive; + + [FieldOffset(0)] + public _ZUDS_negative zudsNegative; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZUDS_FRAME + { + public uint id; + public byte extend; + public byte remote; + public byte data_len; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public byte[] data; + public uint reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZUDS_CTX + { + public IntPtr can_type; // 0-CAN 1-CANFD 2-CANFD加速 + public IntPtr chn_handle; // 通道句柄 + } + #endregion + } +} diff --git a/Sample/zdbc_c#_251106/DBC.sln b/Sample/zdbc_c#_251106/DBC.sln new file mode 100644 index 0000000..4c39b1f --- /dev/null +++ b/Sample/zdbc_c#_251106/DBC.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30501.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DBC", "DBC\DBC.csproj", "{DEFCD218-D268-4833-950E-6F697BC93F8C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Debug|x64.ActiveCfg = Debug|x64 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Debug|x64.Build.0 = Debug|x64 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Debug|x86.ActiveCfg = Debug|x86 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Debug|x86.Build.0 = Debug|x86 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Release|x64.ActiveCfg = Release|x64 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Release|x64.Build.0 = Release|x64 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Release|x86.ActiveCfg = Release|x86 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Sample/zdbc_c#_251106/DBC/App.config b/Sample/zdbc_c#_251106/DBC/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/Sample/zdbc_c#_251106/DBC/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Sample/zdbc_c#_251106/DBC/DBC.csproj b/Sample/zdbc_c#_251106/DBC/DBC.csproj new file mode 100644 index 0000000..ef62706 --- /dev/null +++ b/Sample/zdbc_c#_251106/DBC/DBC.csproj @@ -0,0 +1,99 @@ + + + + + Debug + AnyCPU + {DEFCD218-D268-4833-950E-6F697BC93F8C} + Exe + Properties + DBC + DBC + v4.5 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Sample/zdbc_c#_251106/DBC/Program.cs b/Sample/zdbc_c#_251106/DBC/Program.cs new file mode 100644 index 0000000..9151d66 --- /dev/null +++ b/Sample/zdbc_c#_251106/DBC/Program.cs @@ -0,0 +1,411 @@ +/* + * 241217 程序默认用USBCANFD-200U通讯,两通道对侧 + * Main 有加载DBC文件,打印DBC文件信息 + * 打开设备并演示Encode Decode函数 +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; +using System.Threading; +using System.IO; + +using ZLGAPI; + +namespace DBC +{ + internal class Program + { + private static IntPtr deviceHandle = IntPtr.Zero; // 设备句柄 + private static IntPtr[] channelHandles = new IntPtr[]{ + IntPtr.Zero, + IntPtr.Zero + }; + private static int max_chn = 2; // 通道数量 + private static uint DBCHandle = 0; // DBC句柄 + private static volatile bool isRunning = true; // 线程标志 + + static void Main(string[] args) + { + Program.LoadDBCFile(); // 加载DBC文件 + Program.ReadDBCFileMessages(); // 打印DBC文件信息 + //Program.ReadSignalDescPair(); // 获取具体信号的值与含义 + //Program.InitDevice(); // 打开设备,并演示接收解析和发送 + Console.ReadKey(); + Program.CloseDBCFile(); + } + + // 加载DBC文件 + static void LoadDBCFile() + { + // DBC文件路径 + string DBCFilePath = "C:\\Users\\admin\\Desktop\\DBC\\12.dbc"; + //string currentDirectory = AppDomain.CurrentDomain.BaseDirectory; // 获取当前执行文件exe同级目录 + //string fileName = "12.DBC"; // 替换为你的文件名 + //string filePath = Path.Combine(currentDirectory, fileName); + //DBCFilePath = filePath.Replace("\\", "\\\\"); // 将路径中的分隔符替换为双反斜杠 + Console.WriteLine("{0} ", DBCFilePath); + + + // 加载DBC文件 + Program.DBCHandle = ZDBC.ZDBC_Init(); + IntPtr P2DBCFileAddress = Marshal.StringToHGlobalAnsi(DBCFilePath); + bool Result = ZDBC.ZDBC_LoadFile(DBCHandle, P2DBCFileAddress); + Marshal.FreeHGlobal(P2DBCFileAddress); + } + + // 关闭DBC文件 + static void CloseDBCFile() + { + ZDBC.ZDBC_Release(Program.DBCHandle); + } + + // 读取DBC文件内容 + static void ReadDBCFileMessages() + { + uint count = ZDBC.ZDBC_GetMessageCount(Program.DBCHandle); //信号数量 + Console.WriteLine(" DBC文件中消息的数量:{0}\n", count); + + ZDBC.DBCMessage msg = new ZDBC.DBCMessage(); + IntPtr ptrMsg = Marshal.AllocHGlobal(Marshal.SizeOf(msg)); + + if (ZDBC.ZDBC_GetFirstMessage(DBCHandle, ptrMsg)) + { + Program.PrintfDBCMessage(ptrMsg); + } + while (ZDBC.ZDBC_GetNextMessage(DBCHandle, ptrMsg)) + { + Program.PrintfDBCMessage(ptrMsg); + } + + Marshal.FreeHGlobal(ptrMsg); + } + + // 打印值描述 + static void ReadSignalDescPair() + { + ZDBC.DBCMessage msg = new ZDBC.DBCMessage(); + IntPtr ptrMsg = Marshal.AllocHGlobal(Marshal.SizeOf(msg)); + uint ID = 0x36F; + string SignalName = "BMS_AbnormRlyOffSt"; + + // 获取这个id的消息情况 + if (ZDBC.ZDBC_GetMessageById(Program.DBCHandle, ID, ptrMsg)) + { + Program.PrintfDBCMessage(ptrMsg); + } + else + { + Console.WriteLine("没有找到这个0x{0:x}的定义", ID); + } + + // 获取具体信号的值与含义对个数 + uint ret = ZDBC.ZDBC_GetValDescPairCount(Program.DBCHandle, ID, SignalName); + Console.WriteLine("{0} 的值与含义对个数: {1}\n", SignalName, ret); + + // 循环获取值与含义对 + IntPtr ptr_pair = Marshal.AllocHGlobal((int)ret * Marshal.SizeOf(typeof(ZDBC.ValDescPair))); + ZDBC.ZDBC_GetValDescPair(Program.DBCHandle, ID, SignalName, ptr_pair); + ZDBC.ValDescPair[] pair = new ZDBC.ValDescPair[ret]; + for (int i = 0; i < ret; i++) + { + pair[i] = (ZDBC.ValDescPair)Marshal.PtrToStructure((IntPtr)((Int64)ptr_pair + i * Marshal.SizeOf(typeof(ZDBC.ValDescPair))), typeof(ZDBC.ValDescPair)); + + string pair_name = new string(Encoding.ASCII.GetChars(pair[i].strName)); + string result = pair_name.Substring(0, pair_name.IndexOf('\0')); + Console.WriteLine("[{0}] {1} {2}", i + 1, result, pair[i].value); + } + Console.ReadKey(); + } + + // 打印消息 + static void PrintfDBCMessage(IntPtr ptrMsg) + { + // 消息 + ZDBC.DBCMessage msg = new ZDBC.DBCMessage(); + msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrMsg, typeof(ZDBC.DBCMessage)); + string str_name = new string(Encoding.ASCII.GetChars(msg.strName)); + string result = str_name.Substring(0, str_name.IndexOf('\0')); + Console.WriteLine("0x{0:X} {1}", msg.nID, result); + + // 信号 + Console.WriteLine("\t{0,-27} {1,-4} {2,-5} {3,-5}", "信号名", "起始位", "长度", "原始值"); + for (int i = 0; i < msg.nSignalCount; i++) + { + ZDBC.DBCSignal curSig = msg.vSignals[i]; + str_name = new string(Encoding.ASCII.GetChars(curSig.strName)); + result = str_name.Substring(0, str_name.IndexOf('\0')); + Console.WriteLine("\t{0,-30} {1,-7} {2,-5} {3,-8:X}", result, msg.vSignals[i].nStartBit, msg.vSignals[i].nLen, msg.vSignals[i].nRawvalue); + } + Console.WriteLine(""); + } + + // 初始化设备 + static void InitDevice() + { + //int chn_idx = 0; // 通道号 + uint ret; // 返回值 + + //打开设备 + + Program.deviceHandle = ZLGCAN.ZCAN_OpenDevice(41, 0, 0); // USBCANFD-200U 41 + if (Program.deviceHandle == IntPtr.Zero) + { + Console.WriteLine("打开设备失败"); + Console.ReadKey(); + return; + } + else + Console.WriteLine("打开设备成功"); + + // 初始化通道 + for (int i = 0; i < Program.max_chn; i++) + { + // 仲裁域波特率 + string path = String.Format("{0}/canfd_abit_baud_rate", i); + ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, "500000"); // 500k + if (ret != 1) + { + Console.WriteLine("设置仲裁域波特率失败"); + Console.ReadKey(); + return; + } + + // 数据域波特率 + path = String.Format("{0}/canfd_dbit_baud_rate", i); + ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, "2000000"); // 2M + if (ret != 1) + { + Console.WriteLine("设置数据域波特率失败"); + Console.ReadKey(); + return; + } + + // 终端电阻 + path = String.Format("{0}/initenal_resistance", i); + string resistance = "1";//1-使能 0-禁能 + ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, resistance); + if (ret != 1) + { + Console.WriteLine("设置终端电阻失败"); + Console.ReadKey(); + return; + } + + // 初始化通道 + ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG InitConfig = new ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG(); // 结构体 + InitConfig.can_type = 1; // 1 - CANFD (USBCANFD 只能选择CANFD模式) + InitConfig.config.canfd.mode = 0; // 0 - 正常模式,1 - 只听模式 + + IntPtr P2InitConfig = Marshal.AllocHGlobal(Marshal.SizeOf(InitConfig)); + Marshal.StructureToPtr(InitConfig, P2InitConfig, true); // 转成指针 + Program.channelHandles[i] = ZLGCAN.ZCAN_InitCAN(deviceHandle, (uint)i, P2InitConfig); + Marshal.FreeHGlobal(P2InitConfig); // 释放内存 + if (Program.channelHandles[i] == IntPtr.Zero) + { + Console.WriteLine("初始化通道失败"); + Console.ReadKey(); + return; + } + Console.WriteLine("初始化通道成功"); + + + //打开通道 + ret = ZLGCAN.ZCAN_StartCAN(Program.channelHandles[i]); + if (ret != 1) + { + Console.WriteLine("打开通道失败"); + Console.ReadKey(); + return; + } + Console.WriteLine("打开通道成功"); + + // 创建一个接收线程 + Thread workerThread = new Thread(ReceiveThread); + workerThread.IsBackground = true; // 设置为后台线程 + workerThread.Start(Program.channelHandles[i]); // 启动线程 + } + + Thread.Sleep(1000); + // DBC发送 + //Program.SendDBCMessageById(0x15D); + + // 阻塞等待 + Console.ReadKey(); + Program.isRunning = false; + + for (int i = 0; i < max_chn; i++) + { + // 关闭通道 + ret = ZLGCAN.ZCAN_ResetCAN(Program.channelHandles[i]); + if (ret != 1) + { + Console.WriteLine("关闭通道失败"); + Console.ReadKey(); + return; + } + Console.WriteLine("关闭通道成功"); + } + + // 关闭设备 + ret = ZLGCAN.ZCAN_CloseDevice(Program.deviceHandle); + if (ret != 1) + { + Console.WriteLine("关闭设备失败"); + Console.ReadKey(); + return; + } + Console.WriteLine("关闭设备成功"); + } + + // 接收线程 (有DBC解析) + static void ReceiveThread(object state) + { + IntPtr cur_chn = (IntPtr)state; + const int arraySize = 100; + IntPtr ptrCanData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Receive_Data)) * arraySize); + IntPtr ptrCanFDData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_ReceiveFD_Data)) * arraySize); + IntPtr ptrMsg = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCMessage))); + byte[] zero = new byte[Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Receive_Data)) * arraySize]; + + while (Program.isRunning) + { + uint RecNum = ZLGCAN.ZCAN_GetReceiveNum(cur_chn, 0); //CAN + if (RecNum != 0) + { + // Marshal.Copy(zero, 0, ptrCanData, zero.Length); // 赋0 + uint ReceiveNum = ZLGCAN.ZCAN_Receive(cur_chn, ptrCanData, arraySize, 10); // 接收报文 + for (int i = 0; i < ReceiveNum; i++) + { + IntPtr curPtr = IntPtr.Add(ptrCanData, i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Receive_Data))); + ZLGCAN.ZCAN_Receive_Data data = (ZLGCAN.ZCAN_Receive_Data)Marshal.PtrToStructure(curPtr, typeof(ZLGCAN.ZCAN_Receive_Data)); + + // DBC解析 + if (true == ZDBC.ZDBC_Decode(Program.DBCHandle, ptrMsg, curPtr, 1, 0)) + Program.PrintfDBCMessage(ptrMsg); // 打印解析 + } + + } + + RecNum = ZLGCAN.ZCAN_GetReceiveNum(cur_chn, 1); // CANFD + + if (RecNum != 0) + { + // Marshal.Copy(zero, 0, ptrCanData, zero.Length); // 赋0 + uint ReceiveNum = ZLGCAN.ZCAN_ReceiveFD(cur_chn, ptrCanFDData, arraySize, 10); // 接收报文 + for (int i = 0; i < ReceiveNum; i++) + { + IntPtr curPtr = IntPtr.Add(ptrCanFDData, i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_ReceiveFD_Data))); + ZLGCAN.ZCAN_ReceiveFD_Data data = (ZLGCAN.ZCAN_ReceiveFD_Data)Marshal.PtrToStructure(curPtr, typeof(ZLGCAN.ZCAN_ReceiveFD_Data)); + + // DBC解析 + if (true == ZDBC.ZDBC_Decode(Program.DBCHandle, ptrMsg, curPtr, 1, 1)) + { + Program.PrintfDBCMessage(ptrMsg); // 打印解析 + } + + } + + } + } + Marshal.FreeHGlobal(ptrMsg); + Marshal.FreeHGlobal(ptrCanData); + } + + // DBC发送 + static void SendDBCMessageById(uint id) + { + // 从DBC文件获取对应ID的 DBC结构体 + IntPtr ptrMsg = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCMessage))); + if (ZDBC.ZDBC_GetMessageById(Program.DBCHandle, id, ptrMsg) == false) // 找不到对应id退出 + { + Marshal.FreeHGlobal(ptrMsg); + return; + } + ZDBC.DBCMessage msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrMsg, typeof(ZDBC.DBCMessage)); + + //通过 ZDBC_CalcRawValue 设置信号值(这里是根据实际的DBC信号设置的值,作为示例,设置该消息的第5个信号) + /* + double actualVal = 12; // 物理值 + + IntPtr signalPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCSignal))); + IntPtr actualValPtr = Marshal.AllocHGlobal(sizeof(double)); + + Marshal.StructureToPtr(msg.vSignals[5], signalPtr, true); + Marshal.StructureToPtr(actualVal, actualValPtr, true); + + //计算原始值 + ulong rawValue = ZDBC.ZDBC_CalcRawValue(signalPtr, actualValPtr); // 超出可信号值范围时会被修正 + msg.vSignals[5].nRawvalue = rawValue; + + Marshal.StructureToPtr(msg, ptrMsg, true); + */ + + uint count = 1; // 初始化一个值 + IntPtr ptrCount = Marshal.AllocHGlobal(sizeof(uint)); // 分配内存 + Marshal.WriteInt32(ptrCount, (int)count); // 写入值 + + + //CAN 根据消息类型选择can还是canfd + //Encode DBC_frame -> can_frame + IntPtr ptrCanFrame = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.can_frame))); + if (ZDBC.ZDBC_Encode(Program.DBCHandle, ptrCanFrame, ptrCount, ptrMsg, (int)0) == false) // Encode + { + Marshal.FreeHGlobal(ptrMsg); + Marshal.FreeHGlobal(ptrCount); + Marshal.FreeHGlobal(ptrCanFrame); + return; + } + + // 构造发送结构体 + ZLGCAN.can_frame can_f = (ZLGCAN.can_frame)Marshal.PtrToStructure(ptrCanFrame, typeof(ZLGCAN.can_frame)); + ZLGCAN.ZCAN_Transmit_Data tran_data = new ZLGCAN.ZCAN_Transmit_Data(); + tran_data.frame = can_f; + + // 转换成 ZCAN_Transmit_Data,并发送 + IntPtr ptrTransmit = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Transmit_Data))); + Marshal.StructureToPtr(tran_data, ptrTransmit, true); + if (count != ZLGCAN.ZCAN_Transmit(Program.channelHandles[0], ptrTransmit, count)) + { + Console.WriteLine("发送CAN失败"); + } + + + // CANFD + // Encode DBC_frame -> canfd_frame + IntPtr ptrCanFDFrame = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.canfd_frame))); + if (ZDBC.ZDBC_Encode(Program.DBCHandle, ptrCanFDFrame, ptrCount, ptrMsg, (int)1) == false) // Encode + { + + Marshal.FreeHGlobal(ptrMsg); + Marshal.FreeHGlobal(ptrCount); + Marshal.FreeHGlobal(ptrCanFDFrame); + return; + } + + // 构造发送结构体 + ZLGCAN.canfd_frame canfd_f = (ZLGCAN.canfd_frame)Marshal.PtrToStructure(ptrCanFDFrame, typeof(ZLGCAN.canfd_frame)); + ZLGCAN.ZCAN_TransmitFD_Data tranfd_data = new ZLGCAN.ZCAN_TransmitFD_Data(); + tranfd_data.frame = canfd_f; + // 转换成 ZCAN_Transmit_Data,并发送 + IntPtr ptrTransmitFD = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_TransmitFD_Data))); + Marshal.StructureToPtr(tranfd_data, ptrTransmitFD, true); + if (count != ZLGCAN.ZCAN_TransmitFD(Program.channelHandles[0], ptrTransmitFD, count)) + { + Console.WriteLine("发送CANFD失败"); + } + + Marshal.FreeHGlobal(ptrMsg); + Marshal.FreeHGlobal(ptrCount); + Marshal.FreeHGlobal(ptrCanFrame); + Marshal.FreeHGlobal(ptrCanFDFrame); + Marshal.FreeHGlobal(ptrTransmit); + //Marshal.FreeHGlobal(actualValPtr); + //Marshal.FreeHGlobal(signalPtr); + + return; + } + } +} diff --git a/Sample/zdbc_c#_251106/DBC/Properties/AssemblyInfo.cs b/Sample/zdbc_c#_251106/DBC/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a7a6093 --- /dev/null +++ b/Sample/zdbc_c#_251106/DBC/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的常规信息通过以下 +// 特性集控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("DBC")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("DBC")] +[assembly: AssemblyCopyright("Copyright © 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 使此程序集中的类型 +// 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, +// 则将该类型上的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("adbdb093-47ba-4e8c-bb2e-b08ad7638493")] + +// 程序集的版本信息由下面四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, +// 方法是按如下所示使用“*”: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Sample/zdbc_c#_251106/DBC/ZLGAPI.cs b/Sample/zdbc_c#_251106/DBC/ZLGAPI.cs new file mode 100644 index 0000000..503c751 --- /dev/null +++ b/Sample/zdbc_c#_251106/DBC/ZLGAPI.cs @@ -0,0 +1,1169 @@ +// update time 2025/7/16 + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace ZLGAPI +{ + + public class ZLGCAN + { + + #region 设备类型 + public static UInt32 ZCAN_PCI9810 = 2; + public static UInt32 ZCAN_USBCAN1 = 3; + public static UInt32 ZCAN_USBCAN2 = 4; + public static UInt32 ZCAN_PCI9820 = 5; + public static UInt32 ZCAN_CANETUDP = 12; + public static UInt32 ZCAN_PCI9840 = 14; + public static UInt32 ZCAN_PCI9820I = 16; + public static UInt32 ZCAN_CANETTCP = 17; + public static UInt32 ZCAN_PCI5010U = 19; + public static UInt32 ZCAN_USBCAN_E_U = 20; + public static UInt32 ZCAN_USBCAN_2E_U = 21; + public static UInt32 ZCAN_PCI5020U = 22; + public static UInt32 ZCAN_PCIE9221 = 24; + public static UInt32 ZCAN_WIFICAN_TCP = 25; + public static UInt32 ZCAN_WIFICAN_UDP = 26; + public static UInt32 ZCAN_PCIe9120 = 27; + public static UInt32 ZCAN_PCIe9110 = 28; + public static UInt32 ZCAN_PCIe9140 = 29; + public static UInt32 ZCAN_USBCAN_4E_U = 31; + public static UInt32 ZCAN_CANDTU_200UR = 32; + public static UInt32 ZCAN_USBCAN_8E_U = 34; + public static UInt32 ZCAN_CANDTU_NET = 36; + public static UInt32 ZCAN_CANDTU_100UR = 37; + public static UInt32 ZCAN_PCIE_CANFD_200U = 39; + public static UInt32 ZCAN_PCIE_CANFD_400U = 40; + public static UInt32 ZCAN_USBCANFD_200U = 41; + public static UInt32 ZCAN_USBCANFD_100U = 42; + public static UInt32 ZCAN_USBCANFD_MINI = 43; + public static UInt32 ZCAN_CANSCOPE = 45; + public static UInt32 ZCAN_CLOUD = 46; + public static UInt32 ZCAN_CANDTU_NET_400 = 47; + public static UInt32 ZCAN_CANFDNET_TCP = 48; + public static UInt32 ZCAN_CANFDNET_200U_TCP = 48; + public static UInt32 ZCAN_CANFDNET_UDP = 49; + public static UInt32 ZCAN_CANFDNET_200U_UDP = 49; + public static UInt32 ZCAN_CANFDWIFI_TCP = 50; + public static UInt32 ZCAN_CANFDWIFI_100U_TCP = 50; + public static UInt32 ZCAN_CANFDWIFI_UDP = 51; + public static UInt32 ZCAN_CANFDWIFI_100U_UDP = 51; + public static UInt32 ZCAN_CANFDNET_400U_TCP = 52; + public static UInt32 ZCAN_CANFDNET_400U_UDP = 53; + public static UInt32 ZCAN_CANFDNET_100U_TCP = 55; + public static UInt32 ZCAN_CANFDNET_100U_UDP = 56; + public static UInt32 ZCAN_CANFDNET_800U_TCP = 57; + public static UInt32 ZCAN_CANFDNET_800U_UDP = 58; + public static UInt32 ZCAN_USBCANFD_800U = 59; + public static UInt32 ZCAN_PCIE_CANFD_100U_EX = 60; + public static UInt32 ZCAN_PCIE_CANFD_400U_EX = 61; + public static UInt32 ZCAN_PCIE_CANFD_200U_MINI = 62; + public static UInt32 ZCAN_PCIE_CANFD_200U_EX = 63; + public static UInt32 ZCAN_PCIE_CANFD_200U_M2 = 63; + public static UInt32 ZCAN_CANFDDTU_400_TCP = 64; + public static UInt32 ZCAN_CANFDDTU_400_UDP = 65; + public static UInt32 ZCAN_CANFDWIFI_200U_TCP = 66; + public static UInt32 ZCAN_CANFDWIFI_200U_UDP = 67; + public static UInt32 ZCAN_CANFDDTU_800ER_TCP = 68; + public static UInt32 ZCAN_CANFDDTU_800ER_UDP = 69; + public static UInt32 ZCAN_CANFDDTU_800EWGR_TCP = 70; + public static UInt32 ZCAN_CANFDDTU_800EWGR_UDP = 71; + public static UInt32 ZCAN_CANFDDTU_600EWGR_TCP = 72; + public static UInt32 ZCAN_CANFDDTU_600EWGR_UDP = 73; + public static UInt32 ZCAN_CANFDDTU_CASCADE_TCP = 74; + public static UInt32 ZCAN_CANFDDTU_CASCADE_UDP = 75; + public static UInt32 ZCAN_USBCANFD_400U = 76; + public static UInt32 ZCAN_CANFDDTU_200U = 77; + public static UInt32 ZCAN_ZPSCANFD_TCP = 78; + public static UInt32 ZCAN_ZPSCANFD_USB = 79; + public static UInt32 ZCAN_CANFDBRIDGE_PLUS = 80; + public static UInt32 ZCAN_CANFDDTU_300U = 81; + public static UInt32 ZCAN_PCIE_CANFD_800U = 82; + public static UInt32 ZCAN_PCIE_CANFD_1200U = 83; + public static UInt32 ZCAN_MINI_PCIE_CANFD = 84; + public static UInt32 ZCAN_USBCANFD_800H = 85; + #endregion + + #region LIN事件 + public static UInt32 ZCAN_LIN_WAKE_UP = 1; + public static UInt32 ZCAN_LIN_ENTERED_SLEEP_MODE = 2; + public static UInt32 ZCAN_LIN_EXITED_SLEEP_MODE = 3; + #endregion + + #region 函数 + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_OpenDevice(uint device_type, uint device_index, uint reserved); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_CloseDevice(IntPtr device_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_InitCAN(IntPtr device_handle, uint can_index, IntPtr pInitConfig); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_StartCAN(IntPtr chn_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ResetCAN(IntPtr chn_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ClearBuffer(IntPtr chn_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_GetReceiveNum(IntPtr channel_handle, byte type); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_Transmit(IntPtr channel_handle, IntPtr pTransmit, uint len); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_TransmitFD(IntPtr channel_handle, IntPtr pTransmit, uint len); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_TransmitData(IntPtr device_handle, IntPtr pTransmit, uint len); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_Receive(IntPtr channel_handle, IntPtr pReceive, uint len, int wait_time = -1); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReceiveFD(IntPtr channel_handle, IntPtr pReceive, uint len, int wait_time = -1); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReceiveData(IntPtr device_handle, IntPtr pReceive, uint len, int wait_time); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetValue(IntPtr device_handle, string path, string value); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetValue(IntPtr device_handle, string path, IntPtr value); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_GetValue(IntPtr device_handle, string path); + + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_InitLIN(IntPtr device_handle, uint lin_index, IntPtr pLINInitConfig); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_StartLIN(IntPtr channel_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ResetLIN(IntPtr channel_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_TransmitLIN(IntPtr channel_handle, IntPtr pSend, uint Len); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_GetLINReceiveNum(IntPtr channel_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReceiveLIN(IntPtr channel_handle, IntPtr pReceive, uint Len, int WaitTime); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetLINPublish(IntPtr channel_handle, IntPtr pSend, uint nPublishCount); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetLINSubscribe(IntPtr channel_handle, IntPtr pSend, uint nSubscribeCount); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_WakeUpLIN(IntPtr channel_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_Request(IntPtr device_handle, IntPtr req, IntPtr resp, IntPtr dataBuf, uint dataBufSize); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_Control(IntPtr device_handle, IntPtr ctrl, IntPtr resp); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_RequestEX(IntPtr device_handle, IntPtr requestData, IntPtr resp, IntPtr dataBuf, uint dataBufSize); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_ControlEX(IntPtr device_handle, uint dataType, IntPtr ctrl, IntPtr resp); + #endregion + + #region 结构体 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_CHANNEL_INIT_CONFIG + { + public uint can_type; + public _ZCAN_CHANNEL_INIT_CONFIG config; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZCAN_CHANNEL_INIT_CONFIG + { + public _ZCAN_CHANNEL_CAN_INIT_CONFIG can; + public _ZCAN_CHANNEL_CANFD_INIT_CONFIG canfd; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZCAN_CHANNEL_CAN_INIT_CONFIG + { + public uint acc_code; + public uint acc_mask; + public uint reserved; + public byte filter; + public byte timing0; + public byte timing1; + public byte mode; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZCAN_CHANNEL_CANFD_INIT_CONFIG + { + public uint acc_code; + public uint acc_mask; + public uint abit_timing; + public uint dbit_timing; + public uint brp; + public byte filter; + public byte mode; + public ushort pad; + public uint reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_Transmit_Data + { + public can_frame frame; + public uint transmit_type; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class can_frame + { + public uint can_id; + public byte can_dlc; // frame payload length in byte (0 .. CAN_MAX_DLEN) + public byte __pad; // padding + public byte __res0; // reserved / padding + public byte __res1; // reserved / padding + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] data; // frame data + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class canfd_frame + { + public uint can_id; + public byte len; + public byte flags; + public byte __res0; + public byte __res1; /* reserved / padding */ + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public byte[] data; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZCAN_Receive_Data + { + public can_frame frame; + public UInt64 timestamp; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZCAN_ReceiveFD_Data + { + public canfd_frame frame; + public UInt64 timestamp; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_AUTO_TRANSMIT_OBJ //CANFD定时发送帧结构体 + { + public ushort enable; //0-禁用,1-使能 + public ushort index; //定时报文索引 + public uint interval; //定时周期 + public ZCAN_Transmit_Data obj; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANFD_AUTO_TRANSMIT_OBJ //CANFD定时发送帧结构体 + { + public ushort enable; //0-禁用,1-使能 + public ushort index; //定时报文索引 + public uint interval; //定时周期 + public ZCAN_TransmitFD_Data obj; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_TransmitFD_Data + { + public canfd_frame frame; // 报文数据信息,详见 canfd_frame 结构说明。 + public uint transmit_type; // 发送方式,0=正常发送,1=单次发送,2=自发自收,3=单次自发自收。 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANCANFDData + { + public UInt64 timeStamp; + public UInt32 flag; // flag用于设置一些参数,内部结构可以通过以下函数实现设置和取值 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] extraData; //未使用 + public canfd_frame frame; //实际报文结构体 + + // frameType 帧类型 0-CAN 1-CANFD + public uint frameType { + get { return (flag & 0x03); } + set { flag = (uint)((flag & ~0x03) | (value & 0x03)); } + } + + // txDelay队列发送延时,延时时间存放在 timeStamp 字段;0-不启用延时,1-启用延时,单位1ms,2-启用延时,单位100us + public uint txDelay { + get { return ((flag >> 2) & 0x03); } + set { flag = (uint)((flag & ~0x0C) | (value & 0x03) << 2); } + } + + public uint transmitType { + get { return ((flag >> 4) & 0x0F); } + set { flag = (uint)((flag & ~0x0F) | (value & 0x0F) << 4); } + } + + public uint txEchoRequest { + get { return ((flag >> 8) & 0x01); } + set { flag = (uint)(flag | (value & 0x01) << 8); } + } + + public uint txEchoed { + get { return ((flag >> 9) & 0x01); } // bit9 + set { flag = (uint)((flag & ~0x200) | (value & 0x01) << 9); } + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct PID + { + public byte rawVal; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct RxData + { + public UInt64 timeStamp; + public byte datalen; + public byte dir; + public byte chkSum; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] + public byte[] reserved; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] data; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANDataObj + { + public byte dataType; // 1-CAN/CANFD数据, 4-LIN数据 + public byte chnl; // 数据通道 + public UInt16 flag; // 未使用 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] extraData; // 未使用 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 92)] + public byte[] data; // 报文结构体 + } + + + // LIN + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINData + { + public PID pid; // 受保护的ID + public RxData rxData; // 数据 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINErrData + { + public UInt64 timeStamp; // 时间戳,单位微秒(us) + public PID pid; // 受保护的ID + + public byte dataLen; // 数据长度 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] data; // 数据 + + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] errData; // 错误信息 + + public byte dir; // 传输方向 + public byte chkSum; // 数据校验,部分设备不支持校验数据的获取 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINEventData + { + public UInt64 timeStamp; // 时间戳,单位微秒(us) + public byte type; // 数据长度 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_MSG + { + public byte chnl; // 数据通道 + public byte dataType; // 0-LIN,1-ErrLIN + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 46)] + public byte[] data; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_INIT_CONFIG + { + public byte linMode; // 0-slave,1-master + public byte chkSumMode; // 1-经典校验,2-增强校验 3-自动(对应eZLINChkSumMode的模式) + public byte maxLength; // 最大数据长度,8~64 + public byte reserved; + public uint libBaud; // 波特率,取值1000~20000 + + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_PUBLISH_CFG + { + public byte ID; // 受保护的ID(ID取值范围为0-63) + public byte datelen; // 范围1~8 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 数据段内容 + public byte[] data; + public byte chkSumMode; // 校验方式:0-默认,启动时配置 1-经典校验 2-增强校验(对应eZLINChkSumMode的模式) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public byte[] reserved; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_SUBSCIBE_CFG + { + public byte ID; // 受保护的ID(ID取值范围为0-63) + public byte datelen; // dataLen范围为1-8 当为255(0xff)则表示设备自动识别报文长度 + public byte chkSumMode; // 校验方式:0-默认,启动时配置 1-经典校验 2-增强校验(对应eZLINChkSumMode的模式) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public byte[] reserved; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct SESSION_PARAM + { + public uint timeout; // 响应超时时间(ms)。因PC定时器误差,建议设置不小于200ms + public uint enhanced_timeout; // 收到消极响应错误码为0x78后的超时时间(ms)。因PC定时器误差,建议设置不小于200ms + public byte threeInOne; //三合一,把下面三个变量写进这一个变量 + + // threeInOne 包含以下三个变量 + // BYTE check_any_negative_response : 1; // 接收到非本次请求服务的消极响应时是否需要判定为响应错误 + // BYTE wait_if_suppress_response : 1; // 抑制响应时是否需要等待消极响应,等待时长为响应超时时间 + // BYTE flag : 6; // 保留 + public byte check_any_negative_response + { + get { return (byte)(threeInOne & 0x0001); } + set { threeInOne = (byte)((threeInOne & ~0x0001) | (value & 0x0001)); } + } + public byte wait_if_suppress_response + { + get { return (byte)((threeInOne & 0x0002) >> 1); } + set { threeInOne = (byte)((threeInOne & ~0x0002) | (value & 0x0002)); } + } + public byte flag + { + get { return (byte)((threeInOne & 0x00FC) >> 2); } + set { threeInOne = (byte)((threeInOne & ~0x00FC) | (value & 0x00FC)); } + } + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] reserved0; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct TRANS_PARAM + { + public byte version; // 传输协议版本,VERSION_0,VERSION_1 + public byte max_data_len; // 单帧最大数据长度,can:8,canfd:64 + public byte local_st_min; // 本程序发送流控时用,连续帧之间的最小间隔,0x00-0x7F(0ms~127ms),0xF1-0xF9(100us~900us) + public byte block_size; // 流控帧的块大小 + public byte fill_byte; // 无效字节的填充数据 + public byte ext_frame; // 0:标准帧 1:扩展帧 + public byte is_modify_ecu_st_min; // 是否忽略ECU 返回流控的STmin,强制使用本程序设置的remote_st_min 参数代替 + public byte remote_st_min; // 发送多帧时用, is_modify_ecu_st_min = 1 时有效,0x00 - 0x7F(0ms~127ms), 0xF1 - 0xF9(100us~900us) + public uint fc_timeout; // 接收流控超时时间(ms),如发送首帧后需要等待回应流控帧 + public byte fill_mode; // 数据长度填充模式 0-就近填充 1-不填充 2-最大填充 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public byte[] reserved0; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct TRANS_PARAM_LIN + { + public byte fill_byte; // 无效字节的填充数据 + public byte st_min; // 从节点准备接收诊断请求的下一帧或传输诊断响应的下一帧所需的最小时间 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] reserved0; + }; + + + // CAN UDS请求数据 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_UDS_REQUEST // 硬件UDS接口构体 + { + public uint req_id; // 请求事务索引ID,范围0~65535 + public byte channel; // 设备通道索引 + public byte frame_type; // 帧类型 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] reserved0; + public uint src_addr; // 请求地址 + public uint dst_addr; // 响应地址 + public byte suppress_response; // 1-抑制响应 + public byte sid; // 请求服务id + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] reserved1; + + public SESSION_PARAM session_param; + public TRANS_PARAM trans_param; + public IntPtr data; // 数据数组(不包含SID) + public uint data_len; // 数据数组的长度 + public uint reserved2; + } + + + // LIN UDS请求数据 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZLIN_UDS_REQUEST // 硬件UDS接口构体 + { + public uint req_id; // 请求事务索引ID,范围0~65535 + public byte channel; // 设备通道索引 + public byte suppress_response; // 1:抑制响应 0:不抑制 + public byte sid; // 请求服务id + public byte Nad; // 节点地址 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] reserved1; + + + public SESSION_PARAM session_param; + public TRANS_PARAM_LIN trans_param; + public IntPtr data; // 数据数组(不包含SID) + public uint data_len; // 数据数组的长度 + public uint reserved2; + } + + + // UDS响应数据 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_UDS_RESPONSE + { + public byte status; // 响应状态 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] reserved; + public byte type; // 响应类型 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 大小为8 + public byte[] raw; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct UDS_RESPONSE_Positive + { + public byte sid; // 响应服务id + public uint data_len; // 数据长度(不包含SID), 数据存放在接口传入的dataBuf中 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct UDS_RESPONSE_Negative + { + public byte neg_code; // 固定为0x7F + public byte sid; // 请求服务id + public byte error_code; // 错误码 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct UDS_RESPONSE_raw + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 大小为8 + public byte[] raw; + } + + + [StructLayout(LayoutKind.Explicit)] + public struct _UDS_RESPONSE_union + { + [FieldOffset(0)] + public UDS_RESPONSE_Positive zudsPositive; + + [FieldOffset(0)] + public UDS_RESPONSE_Negative zudsNegative; + + [FieldOffset(0)] + public UDS_RESPONSE_raw raw; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANCANFDUdsData + { + public IntPtr req; // 请求信息 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + public byte[] reserved; // 保留位 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINUdsData + { + public IntPtr req; // 请求信息 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + public byte[] reserved; // 保留位 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANUdsRequestDataObj + { + public uint dataType; // 数据类型 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)] + public byte[] data; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] reserved; // 保留位 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_DYNAMIC_CONFIG_DATA + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public char[] key; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public char[] value; + } + #endregion + } + + + public class ZDBC + { + #region 常量定义 + public const int _MAX_FILE_PATH_ = 260; // 最长文件路径 + public const int _DBC_NAME_LENGTH_ = 127; // 名称最长长度 + public const int _DBC_COMMENT_MAX_LENGTH_ = 127; // 注释最长长度 + public const int _DBC_UNIT_MAX_LENGTH_ = 23; // 单位最长长度 + public const int _DBC_SIGNAL_MAX_COUNT_ = 256; // 一个消息含有的信号的最大数目 + + public const int MUTIPLEXER_NONE = 0; // 不使用复用器 + public const int MUTIPLEXER_M_VALUE = 1; // 复用信号,当复用器开关的值为multiplexer_value时,该信号有效 + public const int MUTIPLEXER_M_SWITCH = 2; // 复用器开关,一个DBC消息只能有一个信号为开关 + + public const int FT_CAN = 0; // CAN + public const int FT_CANFD = 1; // CANFD + + public const int PROTOCOL_J1939 = 0; + public const int PROTOCOL_OTHER = 1; + public const uint INVALID_DBC_HANDLE = 0xffffffff; // 无效的DBC句柄 + #endregion + + #region 函数部分 + + // ZDBC.dll + public delegate bool OnSend(IntPtr ctx, IntPtr pObj); + public delegate void OnMultiTransDone(IntPtr ctx, IntPtr pMsg, IntPtr data, UInt16 nLen, byte nDirection); + public static OnSend onSend; + public static OnMultiTransDone onMultiTransDone; + + /// + /// 此函数用于初始化解析模块,只需要初始化一次。 + /// + /// 是否关闭多帧发送,为 1 时不支持多帧的消息发送。 + /// 是否开启异步解析;0-不启动,ZDBC_AsyncAnalyse 接口无效;1-启动, 独立线程解析出消息。 + /// 为 INVALID_DBC_HANDLE 表示初始化失败,其他表示初始化成功,保存该返回值,之后的函数调用都要用到该句柄。 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZDBC_Init(byte disableMultiSend = 0, byte enableAsyncAnalyse = 1); + + /// + /// 释放资源, 与DBC_Init配对使用 + /// + /// hDBC-句柄, ZDBC_Init的返回值 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_Release(uint DBCHandle); + + /// + /// 此函数用以加载 DBC 格式文件。 + /// + /// 句柄;ZDBC_Init的返回值 + /// 结构体 FileInfo的指针 + /// 为 true 表示加载成功,false 表示失败。 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_LoadFile(uint DBCHandle, IntPtr pFileInfo); + + /// + /// 从字符串加载DBC + /// + /// hDBC-句柄, DBC_Load的返回值 + /// pFileContent-文件内容字符串 + /// merge-是否合并到当前数据库; 1:不清除现有的数据, 即支持加载多个文件;0:清除原来的数据 + /// + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_LoadContent(uint DBCHandle, IntPtr pFileContent, uint merge); + + /// + /// 获取文件的第一条消息。 + /// + /// hDBC-句柄, DBC_Load的返回值 + /// pMsg 存储消息的信息 + /// true表示成功 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetFirstMessage(uint DBCHandle, IntPtr pMsg); + + /// + /// 获取下一条消息。 + /// + /// hDBC-句柄, DBC_Load的返回值 + /// pMsg 存储消息的信息 + /// true表示成功 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetNextMessage(uint DBCHandle, IntPtr pMsg); + + /// + /// 此函数用以根据 ID 获取消息数据。 + /// + /// 句柄; + /// 帧 ID; + /// 消息信息结构体 + /// + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetMessageById(uint DBCHandle, uint nID, IntPtr pMsg); + + /// + /// 此函数用以获取 DBC 文件中含有的消息数目。 + /// + /// DBC句柄 + /// DBC 文件中含有的消息数目 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZDBC_GetMessageCount(uint DBCHandle); + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_Analyse(uint DBCHandle, IntPtr pObj, IntPtr pMsg); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_AsyncAnalyse(uint DBCHandle, IntPtr pObj, uint frame_type, UInt64 extraData); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_OnReceive(uint DBCHandle, IntPtr pObj, uint frame_type); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_SetSender(uint hDBC, OnSend sender, IntPtr ctx); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_SetOnMultiTransDoneFunc(uint hDBC, OnMultiTransDone func, IntPtr ctx); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern byte ZDBC_Send(uint hDBC, IntPtr pMsg); + + /// + /// 根据原始数据解码为 DBCMessage。 + /// + /// DBC句柄 + /// 输出参数,解析结果。 + /// 帧数据数组, ControlCAN 传入 VCI_CAN_OBJ, zlgcan 传入 can_frame。 + /// 原始帧数据个数, 即数组大小。 + /// frame_type 帧类型, 参考FT_CAN=0、FT_CANFD=1,ControlCAN不支持CANFD。 + /// 是否成功。 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_Decode(uint DBCHandle, IntPtr P2DBCMessage, IntPtr P2Obj, uint nCount, byte frame_type); + + /// + /// 根据 DBCMessage 编码为原始数据。 + /// + /// DBC句柄; + /// 编码的原始数据缓冲区数组, ControlCAN 传入 VCI_CAN_OBJ, zlgcan 传入 can_frame。 + /// 输出参数,pObj 缓冲区大小, 返回时为实际原始数据个数。 + /// 输入参数,DBC 消息。 + /// frame_type 帧类型, FT_CAN=0、FT_CANFD=1,ControlCAN不支持CANFD。 + /// 是否成功。 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_Encode(uint DBCHandle, IntPtr P2Obj, IntPtr P2nCount, IntPtr pMsg, byte frame_type); + + /// + /// 信号原始值转换为实际值 + /// + /// sgl 信号 + /// rawVal 原始值, 如果该值超出信号长度可表示范围,会被截断。 + /// 实际值 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern double ZDBC_CalcActualValue(IntPtr sgl, IntPtr rawVal); //原始值通过计算转为实际值,实际值会传入rawVal的地址 + + /// + /// 信号实际值转换为原始值 + /// + /// sgl 信号 + /// actualVal 实际值, 超出可表示范围时会被修正 + /// 原始值 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt64 ZDBC_CalcRawValue(IntPtr sgl, IntPtr actualVal); + + /// + /// 获取网络节点数量 + /// + /// ZDBC_Init的返回值 + /// 网络节点总数量 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt32 ZDBC_GetNetworkNodeCount(uint DBCHandle); + + /// + /// + /// + /// ZDBC_Init的返回值 + /// index 位置索引 + /// DBCNetworkNode * node 网络节点信息 + /// + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetNetworkNodeAt(uint DBCHandle, UInt32 index, IntPtr node); + + + /// + /// 获取具体信号的值与含义对个数 + /// + /// ZDBC_Init的返回值 + /// message的ID + /// signal的名字 + /// + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt32 ZDBC_GetValDescPairCount(uint DBCHandle, UInt32 mag_id, string signal_name); + + + /// + /// 获取具体信号的值与含义对 + /// + /// ZDBC_Init的返回值 + /// message的ID + /// signal的名字 + /// ValDescPair结构体类型 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_GetValDescPair(uint DBCHandle, UInt32 mag_id, string signal_name, IntPtr pair); + + + #endregion + + #region DBC 结构体部分 + + public struct FileInfo + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _MAX_FILE_PATH_ + 1)] + public byte[] strFilePath; // dbc文件路径 + public byte type; // dbc的协议类型, j1939选择PROTOCOL_J1939, 其他协议选择PROTOCOL_OTHER + public byte merge; // 1:不清除现有的数据, 即支持加载多个文件 0:清除原来的数据 + }; + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct DBCSignal + { + + public UInt32 nStartBit; // 起始位 + public UInt32 nLen; // 位长度 + public double nFactor; // 转换因子 + public double nOffset; // 转换偏移实际值=原始值*nFactor+nOffset + public double nMin; // 最小值 + public double nMax; // 最大值 + public UInt64 nRawvalue; // 原始值 + public byte is_signed; // 1:有符号数据, 0:无符号 + public byte is_motorola; // 是否摩托罗拉格式 + public byte multiplexer_type; // 复用器类型 + public byte val_type; // 0:integer, 1:float, 2:double + public UInt32 multiplexer_value; // 复用器开关值为此值时信号有效 + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_UNIT_MAX_LENGTH_ + 1)] + public byte[] unit; //单位 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strName; //名称 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_COMMENT_MAX_LENGTH_ + 1)] + public byte[] strComment; //注释 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strValDesc; //值描述 + + public double initialValue; // 初始化值(原始值) + public uint initialValueValid; // 初始值是否有效 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct DBCMessage + { + public UInt32 nSignalCount; // 信号数量 + public UInt32 nID; // ID + public UInt32 nSize; // 消息占的字节数目 + public double nCycleTime; // 发送周期 + public byte nExtend; // 1:扩展帧, 0:标准帧 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_SIGNAL_MAX_COUNT_)] + public DBCSignal[] vSignals; // 信号集合 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strName; // 名称 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_COMMENT_MAX_LENGTH_ + 1)] + public byte[] strComment; // 注释 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct ValDescPair + { + public double value; // 信号值 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strName; // 对应的值描述 + } + #endregion + } + + + public class ZUDS + { + #region 参数定义 + + public static uint udsRTR = 0x40000000; // Remote Transmission Request + public static uint udsEFF = 0x80000000; // Extend Frame Flag + public static uint udsERR = 0x20000000; // Err flag + + #endregion + + #region 函数部分 + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate uint OnUDSTransmitDelegate(IntPtr ctx, IntPtr frame, uint count); + + + /// + /// 该函数用于初始化 UDS 函数库,返回操作句柄,用于后续的操作,与 ZUDS_Release + /// 配对使用。 + /// typedef uint32 TP_TYPE; // transport protocol + /// #define DoCAN 0 + /// + /// + /// 操作句柄,= ZUDS_INVALID_HANDLE 为无效句柄,其他值为有效句柄。 + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt32 ZUDS_Init(uint type); + + + /// + /// 该函数用于释放资源,与 ZUDS_Init 配对使用。 + /// + /// + /// + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_Release(uint type); + + + /// + /// 该函数用于设置函数库的参数。 + /// + /// + /// 参数类型,= PARAM_TYPE_SESSION 0 用 于 设 置 会 话 层 参 数 , = + ///PARAM_TYPE_ISO15765 1 用于设置 ISO15765 的通信参数; + /// 参数值,type =PARAM_TYPE_SESSION 0 时为 ZUDS_SESSION_PARAM, + ///type= PARAM_TYPE_ISO15765 1 时为 ZUDS_ISO15765_PARAM。 + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_SetParam(UInt32 ZUDS_HANDLE, byte type, IntPtr param); + + + /// + /// 该函数用于设置发送回调函数。函数库自身并不发送帧数据,把打包的帧数据通过回调 + ///函数传出给用户发送,用户可通过 zlgcan 函数库进行帧数据发送。 + /// + /// + /// ctx 上下文参数, 在回调函数中传出, 库内部不会处理该参数; + /// :回调函数原型;typedef uint32 (*OnUDSTransmit)(void* ctx, const ZUDS_FRAME* frame, uint32 count); + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_SetTransmitHandler(UInt32 ZUDS_HANDLE, IntPtr ctx, OnUDSTransmitDelegate onUDSTransmit); + + + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_OnReceive(UInt32 ZUDS_HANDLE, IntPtr ZUDS_FRAME); + + + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_Request(UInt32 ZUDS_HANDLE, IntPtr ZUDS_REQUEST, IntPtr ZUDS_RESPONSE); + + + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_Stop(UInt32 ZUDS_HANDLE); + + + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_SetTesterPresent(UInt32 ZUDS_HANDLE, byte enable, IntPtr param); + + #endregion + + #region 结构体部分 + /// + /// 会话层面参数;即一应一答传输时的通讯参数。 + /// + public struct ZUDS_SESSION_PARAM + { + public UInt16 timeout;// ms, timeout to wait the response of the server + public UInt16 enhanced_timeout; // ms,timeout to wait after negative response: error code 0x78 + public UInt32 reserved0; // 保留 + public UInt32 reserved1; // 保留 + } + + + /// + /// 传输数据部分的参数,例如传输时侯每帧报文的字节数。 + /// + public struct ZUDS_ISO15765_PARAM + { + public byte version; // VERSION_0, VERSION_1格式版本,为 VERSION_0 时符合 ISO15765-2 的 2004 版本格式要求;为 + //hVERSION_1 是符合 ISO15765-2 的 2016 版本新增的格式要求,如下图所示 + public byte max_data_len; // max data length, can:8, canfd:64 + public byte local_st_min; // ms, min time between two consecutive frames + public byte block_size; + public byte fill_byte; // fill to invalid byte + public byte frame_type; // 0:std 1:ext + public byte is_modify_ecu_st_min; //是否忽略 ECU 返回流控的 STmin,强制使用本程序设置的 + //remote_st_min 参数代替 + + public byte remote_st_min; //发 送 多 帧 时 用, is_ignore_ecu_st_min = 1 时 有 效 , + //0x00-0x7F(0ms ~127ms), 0xF1-0xF9(100us ~900us) + public UInt16 fc_timeout; //接收流控超时时间(ms), 如发送首帧后需要等待回应流控帧。 + public byte fill_mode;//字节填充模式。FILL_MODE_NONE-不填充0;FILL_MODE_SHORT- 小于 8 字节填充至 8 字节,大于 8 字节时按 DLC 就近填充1;FILL_MODE_MAX- 始终填充至最大数据长度 (不建议)2。 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZUDS_TESTER_PRESENT_PARAM + { + public UInt32 addr;//会话保持的请求地址; + public UInt16 cycle;//发送周期,单位毫秒; + public byte suppress_response; // 1:suppress是否抑制响应,建议设置为 1; + public UInt32 reserved;//:保留,忽略即可。 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZUDS_REQUEST + { + public uint src_addr; // 请求地址 + public uint dst_addr; // 响应地址 + public byte suppress_response; // 1:抑制响应 + public byte sid; //service id of request + public ushort reserve0; + public IntPtr param; //array,params of the service + public uint param_len; //参数数组的长度 + public uint reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZUDS_RESPONSE + { + public byte status; + public byte type; // RT_POSITIVE, RT_NEGATIVE + public _ZUDS_Union union; + public uint reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZUDS_positive + { + public byte sid; // service id of response + public IntPtr param; // array, params of the service, don't free + public uint param_len; + + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZUDS_negative + { + public byte neg_code; // 0x7F + public byte sid; //service id of response + public byte error_code;//消极响应的错误码 + + } + + + [StructLayout(LayoutKind.Explicit)] + public struct _ZUDS_Union + { + [FieldOffset(0)] + public ZUDS_positive zudsPositive; + + [FieldOffset(0)] + public _ZUDS_negative zudsNegative; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZUDS_FRAME + { + public uint id; + public byte extend; + public byte remote; + public byte data_len; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public byte[] data; + public uint reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZUDS_CTX + { + public IntPtr can_type; // 0-CAN 1-CANFD 2-CANFD加速 + public IntPtr chn_handle; // 通道句柄 + } + #endregion + } +} diff --git a/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC.sln b/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC.sln new file mode 100644 index 0000000..4c39b1f --- /dev/null +++ b/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30501.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DBC", "DBC\DBC.csproj", "{DEFCD218-D268-4833-950E-6F697BC93F8C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Debug|x64.ActiveCfg = Debug|x64 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Debug|x64.Build.0 = Debug|x64 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Debug|x86.ActiveCfg = Debug|x86 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Debug|x86.Build.0 = Debug|x86 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Release|x64.ActiveCfg = Release|x64 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Release|x64.Build.0 = Release|x64 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Release|x86.ActiveCfg = Release|x86 + {DEFCD218-D268-4833-950E-6F697BC93F8C}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/App.config b/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/DBC.csproj b/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/DBC.csproj new file mode 100644 index 0000000..ef62706 --- /dev/null +++ b/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/DBC.csproj @@ -0,0 +1,99 @@ + + + + + Debug + AnyCPU + {DEFCD218-D268-4833-950E-6F697BC93F8C} + Exe + Properties + DBC + DBC + v4.5 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/Program.cs b/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/Program.cs new file mode 100644 index 0000000..9151d66 --- /dev/null +++ b/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/Program.cs @@ -0,0 +1,411 @@ +/* + * 241217 程序默认用USBCANFD-200U通讯,两通道对侧 + * Main 有加载DBC文件,打印DBC文件信息 + * 打开设备并演示Encode Decode函数 +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; +using System.Threading; +using System.IO; + +using ZLGAPI; + +namespace DBC +{ + internal class Program + { + private static IntPtr deviceHandle = IntPtr.Zero; // 设备句柄 + private static IntPtr[] channelHandles = new IntPtr[]{ + IntPtr.Zero, + IntPtr.Zero + }; + private static int max_chn = 2; // 通道数量 + private static uint DBCHandle = 0; // DBC句柄 + private static volatile bool isRunning = true; // 线程标志 + + static void Main(string[] args) + { + Program.LoadDBCFile(); // 加载DBC文件 + Program.ReadDBCFileMessages(); // 打印DBC文件信息 + //Program.ReadSignalDescPair(); // 获取具体信号的值与含义 + //Program.InitDevice(); // 打开设备,并演示接收解析和发送 + Console.ReadKey(); + Program.CloseDBCFile(); + } + + // 加载DBC文件 + static void LoadDBCFile() + { + // DBC文件路径 + string DBCFilePath = "C:\\Users\\admin\\Desktop\\DBC\\12.dbc"; + //string currentDirectory = AppDomain.CurrentDomain.BaseDirectory; // 获取当前执行文件exe同级目录 + //string fileName = "12.DBC"; // 替换为你的文件名 + //string filePath = Path.Combine(currentDirectory, fileName); + //DBCFilePath = filePath.Replace("\\", "\\\\"); // 将路径中的分隔符替换为双反斜杠 + Console.WriteLine("{0} ", DBCFilePath); + + + // 加载DBC文件 + Program.DBCHandle = ZDBC.ZDBC_Init(); + IntPtr P2DBCFileAddress = Marshal.StringToHGlobalAnsi(DBCFilePath); + bool Result = ZDBC.ZDBC_LoadFile(DBCHandle, P2DBCFileAddress); + Marshal.FreeHGlobal(P2DBCFileAddress); + } + + // 关闭DBC文件 + static void CloseDBCFile() + { + ZDBC.ZDBC_Release(Program.DBCHandle); + } + + // 读取DBC文件内容 + static void ReadDBCFileMessages() + { + uint count = ZDBC.ZDBC_GetMessageCount(Program.DBCHandle); //信号数量 + Console.WriteLine(" DBC文件中消息的数量:{0}\n", count); + + ZDBC.DBCMessage msg = new ZDBC.DBCMessage(); + IntPtr ptrMsg = Marshal.AllocHGlobal(Marshal.SizeOf(msg)); + + if (ZDBC.ZDBC_GetFirstMessage(DBCHandle, ptrMsg)) + { + Program.PrintfDBCMessage(ptrMsg); + } + while (ZDBC.ZDBC_GetNextMessage(DBCHandle, ptrMsg)) + { + Program.PrintfDBCMessage(ptrMsg); + } + + Marshal.FreeHGlobal(ptrMsg); + } + + // 打印值描述 + static void ReadSignalDescPair() + { + ZDBC.DBCMessage msg = new ZDBC.DBCMessage(); + IntPtr ptrMsg = Marshal.AllocHGlobal(Marshal.SizeOf(msg)); + uint ID = 0x36F; + string SignalName = "BMS_AbnormRlyOffSt"; + + // 获取这个id的消息情况 + if (ZDBC.ZDBC_GetMessageById(Program.DBCHandle, ID, ptrMsg)) + { + Program.PrintfDBCMessage(ptrMsg); + } + else + { + Console.WriteLine("没有找到这个0x{0:x}的定义", ID); + } + + // 获取具体信号的值与含义对个数 + uint ret = ZDBC.ZDBC_GetValDescPairCount(Program.DBCHandle, ID, SignalName); + Console.WriteLine("{0} 的值与含义对个数: {1}\n", SignalName, ret); + + // 循环获取值与含义对 + IntPtr ptr_pair = Marshal.AllocHGlobal((int)ret * Marshal.SizeOf(typeof(ZDBC.ValDescPair))); + ZDBC.ZDBC_GetValDescPair(Program.DBCHandle, ID, SignalName, ptr_pair); + ZDBC.ValDescPair[] pair = new ZDBC.ValDescPair[ret]; + for (int i = 0; i < ret; i++) + { + pair[i] = (ZDBC.ValDescPair)Marshal.PtrToStructure((IntPtr)((Int64)ptr_pair + i * Marshal.SizeOf(typeof(ZDBC.ValDescPair))), typeof(ZDBC.ValDescPair)); + + string pair_name = new string(Encoding.ASCII.GetChars(pair[i].strName)); + string result = pair_name.Substring(0, pair_name.IndexOf('\0')); + Console.WriteLine("[{0}] {1} {2}", i + 1, result, pair[i].value); + } + Console.ReadKey(); + } + + // 打印消息 + static void PrintfDBCMessage(IntPtr ptrMsg) + { + // 消息 + ZDBC.DBCMessage msg = new ZDBC.DBCMessage(); + msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrMsg, typeof(ZDBC.DBCMessage)); + string str_name = new string(Encoding.ASCII.GetChars(msg.strName)); + string result = str_name.Substring(0, str_name.IndexOf('\0')); + Console.WriteLine("0x{0:X} {1}", msg.nID, result); + + // 信号 + Console.WriteLine("\t{0,-27} {1,-4} {2,-5} {3,-5}", "信号名", "起始位", "长度", "原始值"); + for (int i = 0; i < msg.nSignalCount; i++) + { + ZDBC.DBCSignal curSig = msg.vSignals[i]; + str_name = new string(Encoding.ASCII.GetChars(curSig.strName)); + result = str_name.Substring(0, str_name.IndexOf('\0')); + Console.WriteLine("\t{0,-30} {1,-7} {2,-5} {3,-8:X}", result, msg.vSignals[i].nStartBit, msg.vSignals[i].nLen, msg.vSignals[i].nRawvalue); + } + Console.WriteLine(""); + } + + // 初始化设备 + static void InitDevice() + { + //int chn_idx = 0; // 通道号 + uint ret; // 返回值 + + //打开设备 + + Program.deviceHandle = ZLGCAN.ZCAN_OpenDevice(41, 0, 0); // USBCANFD-200U 41 + if (Program.deviceHandle == IntPtr.Zero) + { + Console.WriteLine("打开设备失败"); + Console.ReadKey(); + return; + } + else + Console.WriteLine("打开设备成功"); + + // 初始化通道 + for (int i = 0; i < Program.max_chn; i++) + { + // 仲裁域波特率 + string path = String.Format("{0}/canfd_abit_baud_rate", i); + ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, "500000"); // 500k + if (ret != 1) + { + Console.WriteLine("设置仲裁域波特率失败"); + Console.ReadKey(); + return; + } + + // 数据域波特率 + path = String.Format("{0}/canfd_dbit_baud_rate", i); + ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, "2000000"); // 2M + if (ret != 1) + { + Console.WriteLine("设置数据域波特率失败"); + Console.ReadKey(); + return; + } + + // 终端电阻 + path = String.Format("{0}/initenal_resistance", i); + string resistance = "1";//1-使能 0-禁能 + ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, resistance); + if (ret != 1) + { + Console.WriteLine("设置终端电阻失败"); + Console.ReadKey(); + return; + } + + // 初始化通道 + ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG InitConfig = new ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG(); // 结构体 + InitConfig.can_type = 1; // 1 - CANFD (USBCANFD 只能选择CANFD模式) + InitConfig.config.canfd.mode = 0; // 0 - 正常模式,1 - 只听模式 + + IntPtr P2InitConfig = Marshal.AllocHGlobal(Marshal.SizeOf(InitConfig)); + Marshal.StructureToPtr(InitConfig, P2InitConfig, true); // 转成指针 + Program.channelHandles[i] = ZLGCAN.ZCAN_InitCAN(deviceHandle, (uint)i, P2InitConfig); + Marshal.FreeHGlobal(P2InitConfig); // 释放内存 + if (Program.channelHandles[i] == IntPtr.Zero) + { + Console.WriteLine("初始化通道失败"); + Console.ReadKey(); + return; + } + Console.WriteLine("初始化通道成功"); + + + //打开通道 + ret = ZLGCAN.ZCAN_StartCAN(Program.channelHandles[i]); + if (ret != 1) + { + Console.WriteLine("打开通道失败"); + Console.ReadKey(); + return; + } + Console.WriteLine("打开通道成功"); + + // 创建一个接收线程 + Thread workerThread = new Thread(ReceiveThread); + workerThread.IsBackground = true; // 设置为后台线程 + workerThread.Start(Program.channelHandles[i]); // 启动线程 + } + + Thread.Sleep(1000); + // DBC发送 + //Program.SendDBCMessageById(0x15D); + + // 阻塞等待 + Console.ReadKey(); + Program.isRunning = false; + + for (int i = 0; i < max_chn; i++) + { + // 关闭通道 + ret = ZLGCAN.ZCAN_ResetCAN(Program.channelHandles[i]); + if (ret != 1) + { + Console.WriteLine("关闭通道失败"); + Console.ReadKey(); + return; + } + Console.WriteLine("关闭通道成功"); + } + + // 关闭设备 + ret = ZLGCAN.ZCAN_CloseDevice(Program.deviceHandle); + if (ret != 1) + { + Console.WriteLine("关闭设备失败"); + Console.ReadKey(); + return; + } + Console.WriteLine("关闭设备成功"); + } + + // 接收线程 (有DBC解析) + static void ReceiveThread(object state) + { + IntPtr cur_chn = (IntPtr)state; + const int arraySize = 100; + IntPtr ptrCanData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Receive_Data)) * arraySize); + IntPtr ptrCanFDData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_ReceiveFD_Data)) * arraySize); + IntPtr ptrMsg = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCMessage))); + byte[] zero = new byte[Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Receive_Data)) * arraySize]; + + while (Program.isRunning) + { + uint RecNum = ZLGCAN.ZCAN_GetReceiveNum(cur_chn, 0); //CAN + if (RecNum != 0) + { + // Marshal.Copy(zero, 0, ptrCanData, zero.Length); // 赋0 + uint ReceiveNum = ZLGCAN.ZCAN_Receive(cur_chn, ptrCanData, arraySize, 10); // 接收报文 + for (int i = 0; i < ReceiveNum; i++) + { + IntPtr curPtr = IntPtr.Add(ptrCanData, i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Receive_Data))); + ZLGCAN.ZCAN_Receive_Data data = (ZLGCAN.ZCAN_Receive_Data)Marshal.PtrToStructure(curPtr, typeof(ZLGCAN.ZCAN_Receive_Data)); + + // DBC解析 + if (true == ZDBC.ZDBC_Decode(Program.DBCHandle, ptrMsg, curPtr, 1, 0)) + Program.PrintfDBCMessage(ptrMsg); // 打印解析 + } + + } + + RecNum = ZLGCAN.ZCAN_GetReceiveNum(cur_chn, 1); // CANFD + + if (RecNum != 0) + { + // Marshal.Copy(zero, 0, ptrCanData, zero.Length); // 赋0 + uint ReceiveNum = ZLGCAN.ZCAN_ReceiveFD(cur_chn, ptrCanFDData, arraySize, 10); // 接收报文 + for (int i = 0; i < ReceiveNum; i++) + { + IntPtr curPtr = IntPtr.Add(ptrCanFDData, i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_ReceiveFD_Data))); + ZLGCAN.ZCAN_ReceiveFD_Data data = (ZLGCAN.ZCAN_ReceiveFD_Data)Marshal.PtrToStructure(curPtr, typeof(ZLGCAN.ZCAN_ReceiveFD_Data)); + + // DBC解析 + if (true == ZDBC.ZDBC_Decode(Program.DBCHandle, ptrMsg, curPtr, 1, 1)) + { + Program.PrintfDBCMessage(ptrMsg); // 打印解析 + } + + } + + } + } + Marshal.FreeHGlobal(ptrMsg); + Marshal.FreeHGlobal(ptrCanData); + } + + // DBC发送 + static void SendDBCMessageById(uint id) + { + // 从DBC文件获取对应ID的 DBC结构体 + IntPtr ptrMsg = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCMessage))); + if (ZDBC.ZDBC_GetMessageById(Program.DBCHandle, id, ptrMsg) == false) // 找不到对应id退出 + { + Marshal.FreeHGlobal(ptrMsg); + return; + } + ZDBC.DBCMessage msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrMsg, typeof(ZDBC.DBCMessage)); + + //通过 ZDBC_CalcRawValue 设置信号值(这里是根据实际的DBC信号设置的值,作为示例,设置该消息的第5个信号) + /* + double actualVal = 12; // 物理值 + + IntPtr signalPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZDBC.DBCSignal))); + IntPtr actualValPtr = Marshal.AllocHGlobal(sizeof(double)); + + Marshal.StructureToPtr(msg.vSignals[5], signalPtr, true); + Marshal.StructureToPtr(actualVal, actualValPtr, true); + + //计算原始值 + ulong rawValue = ZDBC.ZDBC_CalcRawValue(signalPtr, actualValPtr); // 超出可信号值范围时会被修正 + msg.vSignals[5].nRawvalue = rawValue; + + Marshal.StructureToPtr(msg, ptrMsg, true); + */ + + uint count = 1; // 初始化一个值 + IntPtr ptrCount = Marshal.AllocHGlobal(sizeof(uint)); // 分配内存 + Marshal.WriteInt32(ptrCount, (int)count); // 写入值 + + + //CAN 根据消息类型选择can还是canfd + //Encode DBC_frame -> can_frame + IntPtr ptrCanFrame = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.can_frame))); + if (ZDBC.ZDBC_Encode(Program.DBCHandle, ptrCanFrame, ptrCount, ptrMsg, (int)0) == false) // Encode + { + Marshal.FreeHGlobal(ptrMsg); + Marshal.FreeHGlobal(ptrCount); + Marshal.FreeHGlobal(ptrCanFrame); + return; + } + + // 构造发送结构体 + ZLGCAN.can_frame can_f = (ZLGCAN.can_frame)Marshal.PtrToStructure(ptrCanFrame, typeof(ZLGCAN.can_frame)); + ZLGCAN.ZCAN_Transmit_Data tran_data = new ZLGCAN.ZCAN_Transmit_Data(); + tran_data.frame = can_f; + + // 转换成 ZCAN_Transmit_Data,并发送 + IntPtr ptrTransmit = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Transmit_Data))); + Marshal.StructureToPtr(tran_data, ptrTransmit, true); + if (count != ZLGCAN.ZCAN_Transmit(Program.channelHandles[0], ptrTransmit, count)) + { + Console.WriteLine("发送CAN失败"); + } + + + // CANFD + // Encode DBC_frame -> canfd_frame + IntPtr ptrCanFDFrame = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.canfd_frame))); + if (ZDBC.ZDBC_Encode(Program.DBCHandle, ptrCanFDFrame, ptrCount, ptrMsg, (int)1) == false) // Encode + { + + Marshal.FreeHGlobal(ptrMsg); + Marshal.FreeHGlobal(ptrCount); + Marshal.FreeHGlobal(ptrCanFDFrame); + return; + } + + // 构造发送结构体 + ZLGCAN.canfd_frame canfd_f = (ZLGCAN.canfd_frame)Marshal.PtrToStructure(ptrCanFDFrame, typeof(ZLGCAN.canfd_frame)); + ZLGCAN.ZCAN_TransmitFD_Data tranfd_data = new ZLGCAN.ZCAN_TransmitFD_Data(); + tranfd_data.frame = canfd_f; + // 转换成 ZCAN_Transmit_Data,并发送 + IntPtr ptrTransmitFD = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ZLGCAN.ZCAN_TransmitFD_Data))); + Marshal.StructureToPtr(tranfd_data, ptrTransmitFD, true); + if (count != ZLGCAN.ZCAN_TransmitFD(Program.channelHandles[0], ptrTransmitFD, count)) + { + Console.WriteLine("发送CANFD失败"); + } + + Marshal.FreeHGlobal(ptrMsg); + Marshal.FreeHGlobal(ptrCount); + Marshal.FreeHGlobal(ptrCanFrame); + Marshal.FreeHGlobal(ptrCanFDFrame); + Marshal.FreeHGlobal(ptrTransmit); + //Marshal.FreeHGlobal(actualValPtr); + //Marshal.FreeHGlobal(signalPtr); + + return; + } + } +} diff --git a/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/Properties/AssemblyInfo.cs b/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a7a6093 --- /dev/null +++ b/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的常规信息通过以下 +// 特性集控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("DBC")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("DBC")] +[assembly: AssemblyCopyright("Copyright © 2024")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 使此程序集中的类型 +// 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, +// 则将该类型上的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("adbdb093-47ba-4e8c-bb2e-b08ad7638493")] + +// 程序集的版本信息由下面四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, +// 方法是按如下所示使用“*”: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/ZLGAPI.cs b/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/ZLGAPI.cs new file mode 100644 index 0000000..503c751 --- /dev/null +++ b/Sample/zdbc_c#_251106/zdbc_c#_251106/DBC/ZLGAPI.cs @@ -0,0 +1,1169 @@ +// update time 2025/7/16 + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace ZLGAPI +{ + + public class ZLGCAN + { + + #region 设备类型 + public static UInt32 ZCAN_PCI9810 = 2; + public static UInt32 ZCAN_USBCAN1 = 3; + public static UInt32 ZCAN_USBCAN2 = 4; + public static UInt32 ZCAN_PCI9820 = 5; + public static UInt32 ZCAN_CANETUDP = 12; + public static UInt32 ZCAN_PCI9840 = 14; + public static UInt32 ZCAN_PCI9820I = 16; + public static UInt32 ZCAN_CANETTCP = 17; + public static UInt32 ZCAN_PCI5010U = 19; + public static UInt32 ZCAN_USBCAN_E_U = 20; + public static UInt32 ZCAN_USBCAN_2E_U = 21; + public static UInt32 ZCAN_PCI5020U = 22; + public static UInt32 ZCAN_PCIE9221 = 24; + public static UInt32 ZCAN_WIFICAN_TCP = 25; + public static UInt32 ZCAN_WIFICAN_UDP = 26; + public static UInt32 ZCAN_PCIe9120 = 27; + public static UInt32 ZCAN_PCIe9110 = 28; + public static UInt32 ZCAN_PCIe9140 = 29; + public static UInt32 ZCAN_USBCAN_4E_U = 31; + public static UInt32 ZCAN_CANDTU_200UR = 32; + public static UInt32 ZCAN_USBCAN_8E_U = 34; + public static UInt32 ZCAN_CANDTU_NET = 36; + public static UInt32 ZCAN_CANDTU_100UR = 37; + public static UInt32 ZCAN_PCIE_CANFD_200U = 39; + public static UInt32 ZCAN_PCIE_CANFD_400U = 40; + public static UInt32 ZCAN_USBCANFD_200U = 41; + public static UInt32 ZCAN_USBCANFD_100U = 42; + public static UInt32 ZCAN_USBCANFD_MINI = 43; + public static UInt32 ZCAN_CANSCOPE = 45; + public static UInt32 ZCAN_CLOUD = 46; + public static UInt32 ZCAN_CANDTU_NET_400 = 47; + public static UInt32 ZCAN_CANFDNET_TCP = 48; + public static UInt32 ZCAN_CANFDNET_200U_TCP = 48; + public static UInt32 ZCAN_CANFDNET_UDP = 49; + public static UInt32 ZCAN_CANFDNET_200U_UDP = 49; + public static UInt32 ZCAN_CANFDWIFI_TCP = 50; + public static UInt32 ZCAN_CANFDWIFI_100U_TCP = 50; + public static UInt32 ZCAN_CANFDWIFI_UDP = 51; + public static UInt32 ZCAN_CANFDWIFI_100U_UDP = 51; + public static UInt32 ZCAN_CANFDNET_400U_TCP = 52; + public static UInt32 ZCAN_CANFDNET_400U_UDP = 53; + public static UInt32 ZCAN_CANFDNET_100U_TCP = 55; + public static UInt32 ZCAN_CANFDNET_100U_UDP = 56; + public static UInt32 ZCAN_CANFDNET_800U_TCP = 57; + public static UInt32 ZCAN_CANFDNET_800U_UDP = 58; + public static UInt32 ZCAN_USBCANFD_800U = 59; + public static UInt32 ZCAN_PCIE_CANFD_100U_EX = 60; + public static UInt32 ZCAN_PCIE_CANFD_400U_EX = 61; + public static UInt32 ZCAN_PCIE_CANFD_200U_MINI = 62; + public static UInt32 ZCAN_PCIE_CANFD_200U_EX = 63; + public static UInt32 ZCAN_PCIE_CANFD_200U_M2 = 63; + public static UInt32 ZCAN_CANFDDTU_400_TCP = 64; + public static UInt32 ZCAN_CANFDDTU_400_UDP = 65; + public static UInt32 ZCAN_CANFDWIFI_200U_TCP = 66; + public static UInt32 ZCAN_CANFDWIFI_200U_UDP = 67; + public static UInt32 ZCAN_CANFDDTU_800ER_TCP = 68; + public static UInt32 ZCAN_CANFDDTU_800ER_UDP = 69; + public static UInt32 ZCAN_CANFDDTU_800EWGR_TCP = 70; + public static UInt32 ZCAN_CANFDDTU_800EWGR_UDP = 71; + public static UInt32 ZCAN_CANFDDTU_600EWGR_TCP = 72; + public static UInt32 ZCAN_CANFDDTU_600EWGR_UDP = 73; + public static UInt32 ZCAN_CANFDDTU_CASCADE_TCP = 74; + public static UInt32 ZCAN_CANFDDTU_CASCADE_UDP = 75; + public static UInt32 ZCAN_USBCANFD_400U = 76; + public static UInt32 ZCAN_CANFDDTU_200U = 77; + public static UInt32 ZCAN_ZPSCANFD_TCP = 78; + public static UInt32 ZCAN_ZPSCANFD_USB = 79; + public static UInt32 ZCAN_CANFDBRIDGE_PLUS = 80; + public static UInt32 ZCAN_CANFDDTU_300U = 81; + public static UInt32 ZCAN_PCIE_CANFD_800U = 82; + public static UInt32 ZCAN_PCIE_CANFD_1200U = 83; + public static UInt32 ZCAN_MINI_PCIE_CANFD = 84; + public static UInt32 ZCAN_USBCANFD_800H = 85; + #endregion + + #region LIN事件 + public static UInt32 ZCAN_LIN_WAKE_UP = 1; + public static UInt32 ZCAN_LIN_ENTERED_SLEEP_MODE = 2; + public static UInt32 ZCAN_LIN_EXITED_SLEEP_MODE = 3; + #endregion + + #region 函数 + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_OpenDevice(uint device_type, uint device_index, uint reserved); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_CloseDevice(IntPtr device_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_InitCAN(IntPtr device_handle, uint can_index, IntPtr pInitConfig); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_StartCAN(IntPtr chn_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ResetCAN(IntPtr chn_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ClearBuffer(IntPtr chn_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_GetReceiveNum(IntPtr channel_handle, byte type); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_Transmit(IntPtr channel_handle, IntPtr pTransmit, uint len); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_TransmitFD(IntPtr channel_handle, IntPtr pTransmit, uint len); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_TransmitData(IntPtr device_handle, IntPtr pTransmit, uint len); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_Receive(IntPtr channel_handle, IntPtr pReceive, uint len, int wait_time = -1); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReceiveFD(IntPtr channel_handle, IntPtr pReceive, uint len, int wait_time = -1); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReceiveData(IntPtr device_handle, IntPtr pReceive, uint len, int wait_time); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetValue(IntPtr device_handle, string path, string value); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetValue(IntPtr device_handle, string path, IntPtr value); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_GetValue(IntPtr device_handle, string path); + + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr ZCAN_InitLIN(IntPtr device_handle, uint lin_index, IntPtr pLINInitConfig); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_StartLIN(IntPtr channel_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ResetLIN(IntPtr channel_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_TransmitLIN(IntPtr channel_handle, IntPtr pSend, uint Len); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_GetLINReceiveNum(IntPtr channel_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_ReceiveLIN(IntPtr channel_handle, IntPtr pReceive, uint Len, int WaitTime); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetLINPublish(IntPtr channel_handle, IntPtr pSend, uint nPublishCount); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_SetLINSubscribe(IntPtr channel_handle, IntPtr pSend, uint nSubscribeCount); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_WakeUpLIN(IntPtr channel_handle); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_Request(IntPtr device_handle, IntPtr req, IntPtr resp, IntPtr dataBuf, uint dataBufSize); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_Control(IntPtr device_handle, IntPtr ctrl, IntPtr resp); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_RequestEX(IntPtr device_handle, IntPtr requestData, IntPtr resp, IntPtr dataBuf, uint dataBufSize); + + + [DllImport(".\\zlgcan.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZCAN_UDS_ControlEX(IntPtr device_handle, uint dataType, IntPtr ctrl, IntPtr resp); + #endregion + + #region 结构体 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_CHANNEL_INIT_CONFIG + { + public uint can_type; + public _ZCAN_CHANNEL_INIT_CONFIG config; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZCAN_CHANNEL_INIT_CONFIG + { + public _ZCAN_CHANNEL_CAN_INIT_CONFIG can; + public _ZCAN_CHANNEL_CANFD_INIT_CONFIG canfd; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZCAN_CHANNEL_CAN_INIT_CONFIG + { + public uint acc_code; + public uint acc_mask; + public uint reserved; + public byte filter; + public byte timing0; + public byte timing1; + public byte mode; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZCAN_CHANNEL_CANFD_INIT_CONFIG + { + public uint acc_code; + public uint acc_mask; + public uint abit_timing; + public uint dbit_timing; + public uint brp; + public byte filter; + public byte mode; + public ushort pad; + public uint reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_Transmit_Data + { + public can_frame frame; + public uint transmit_type; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class can_frame + { + public uint can_id; + public byte can_dlc; // frame payload length in byte (0 .. CAN_MAX_DLEN) + public byte __pad; // padding + public byte __res0; // reserved / padding + public byte __res1; // reserved / padding + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] data; // frame data + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class canfd_frame + { + public uint can_id; + public byte len; + public byte flags; + public byte __res0; + public byte __res1; /* reserved / padding */ + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public byte[] data; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZCAN_Receive_Data + { + public can_frame frame; + public UInt64 timestamp; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZCAN_ReceiveFD_Data + { + public canfd_frame frame; + public UInt64 timestamp; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_AUTO_TRANSMIT_OBJ //CANFD定时发送帧结构体 + { + public ushort enable; //0-禁用,1-使能 + public ushort index; //定时报文索引 + public uint interval; //定时周期 + public ZCAN_Transmit_Data obj; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANFD_AUTO_TRANSMIT_OBJ //CANFD定时发送帧结构体 + { + public ushort enable; //0-禁用,1-使能 + public ushort index; //定时报文索引 + public uint interval; //定时周期 + public ZCAN_TransmitFD_Data obj; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_TransmitFD_Data + { + public canfd_frame frame; // 报文数据信息,详见 canfd_frame 结构说明。 + public uint transmit_type; // 发送方式,0=正常发送,1=单次发送,2=自发自收,3=单次自发自收。 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANCANFDData + { + public UInt64 timeStamp; + public UInt32 flag; // flag用于设置一些参数,内部结构可以通过以下函数实现设置和取值 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] extraData; //未使用 + public canfd_frame frame; //实际报文结构体 + + // frameType 帧类型 0-CAN 1-CANFD + public uint frameType { + get { return (flag & 0x03); } + set { flag = (uint)((flag & ~0x03) | (value & 0x03)); } + } + + // txDelay队列发送延时,延时时间存放在 timeStamp 字段;0-不启用延时,1-启用延时,单位1ms,2-启用延时,单位100us + public uint txDelay { + get { return ((flag >> 2) & 0x03); } + set { flag = (uint)((flag & ~0x0C) | (value & 0x03) << 2); } + } + + public uint transmitType { + get { return ((flag >> 4) & 0x0F); } + set { flag = (uint)((flag & ~0x0F) | (value & 0x0F) << 4); } + } + + public uint txEchoRequest { + get { return ((flag >> 8) & 0x01); } + set { flag = (uint)(flag | (value & 0x01) << 8); } + } + + public uint txEchoed { + get { return ((flag >> 9) & 0x01); } // bit9 + set { flag = (uint)((flag & ~0x200) | (value & 0x01) << 9); } + } + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct PID + { + public byte rawVal; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct RxData + { + public UInt64 timeStamp; + public byte datalen; + public byte dir; + public byte chkSum; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] + public byte[] reserved; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] data; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANDataObj + { + public byte dataType; // 1-CAN/CANFD数据, 4-LIN数据 + public byte chnl; // 数据通道 + public UInt16 flag; // 未使用 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] extraData; // 未使用 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 92)] + public byte[] data; // 报文结构体 + } + + + // LIN + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINData + { + public PID pid; // 受保护的ID + public RxData rxData; // 数据 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINErrData + { + public UInt64 timeStamp; // 时间戳,单位微秒(us) + public PID pid; // 受保护的ID + + public byte dataLen; // 数据长度 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] data; // 数据 + + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] errData; // 错误信息 + + public byte dir; // 传输方向 + public byte chkSum; // 数据校验,部分设备不支持校验数据的获取 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINEventData + { + public UInt64 timeStamp; // 时间戳,单位微秒(us) + public byte type; // 数据长度 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_MSG + { + public byte chnl; // 数据通道 + public byte dataType; // 0-LIN,1-ErrLIN + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 46)] + public byte[] data; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_INIT_CONFIG + { + public byte linMode; // 0-slave,1-master + public byte chkSumMode; // 1-经典校验,2-增强校验 3-自动(对应eZLINChkSumMode的模式) + public byte maxLength; // 最大数据长度,8~64 + public byte reserved; + public uint libBaud; // 波特率,取值1000~20000 + + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_PUBLISH_CFG + { + public byte ID; // 受保护的ID(ID取值范围为0-63) + public byte datelen; // 范围1~8 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 数据段内容 + public byte[] data; + public byte chkSumMode; // 校验方式:0-默认,启动时配置 1-经典校验 2-增强校验(对应eZLINChkSumMode的模式) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public byte[] reserved; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_LIN_SUBSCIBE_CFG + { + public byte ID; // 受保护的ID(ID取值范围为0-63) + public byte datelen; // dataLen范围为1-8 当为255(0xff)则表示设备自动识别报文长度 + public byte chkSumMode; // 校验方式:0-默认,启动时配置 1-经典校验 2-增强校验(对应eZLINChkSumMode的模式) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public byte[] reserved; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct SESSION_PARAM + { + public uint timeout; // 响应超时时间(ms)。因PC定时器误差,建议设置不小于200ms + public uint enhanced_timeout; // 收到消极响应错误码为0x78后的超时时间(ms)。因PC定时器误差,建议设置不小于200ms + public byte threeInOne; //三合一,把下面三个变量写进这一个变量 + + // threeInOne 包含以下三个变量 + // BYTE check_any_negative_response : 1; // 接收到非本次请求服务的消极响应时是否需要判定为响应错误 + // BYTE wait_if_suppress_response : 1; // 抑制响应时是否需要等待消极响应,等待时长为响应超时时间 + // BYTE flag : 6; // 保留 + public byte check_any_negative_response + { + get { return (byte)(threeInOne & 0x0001); } + set { threeInOne = (byte)((threeInOne & ~0x0001) | (value & 0x0001)); } + } + public byte wait_if_suppress_response + { + get { return (byte)((threeInOne & 0x0002) >> 1); } + set { threeInOne = (byte)((threeInOne & ~0x0002) | (value & 0x0002)); } + } + public byte flag + { + get { return (byte)((threeInOne & 0x00FC) >> 2); } + set { threeInOne = (byte)((threeInOne & ~0x00FC) | (value & 0x00FC)); } + } + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public byte[] reserved0; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct TRANS_PARAM + { + public byte version; // 传输协议版本,VERSION_0,VERSION_1 + public byte max_data_len; // 单帧最大数据长度,can:8,canfd:64 + public byte local_st_min; // 本程序发送流控时用,连续帧之间的最小间隔,0x00-0x7F(0ms~127ms),0xF1-0xF9(100us~900us) + public byte block_size; // 流控帧的块大小 + public byte fill_byte; // 无效字节的填充数据 + public byte ext_frame; // 0:标准帧 1:扩展帧 + public byte is_modify_ecu_st_min; // 是否忽略ECU 返回流控的STmin,强制使用本程序设置的remote_st_min 参数代替 + public byte remote_st_min; // 发送多帧时用, is_modify_ecu_st_min = 1 时有效,0x00 - 0x7F(0ms~127ms), 0xF1 - 0xF9(100us~900us) + public uint fc_timeout; // 接收流控超时时间(ms),如发送首帧后需要等待回应流控帧 + public byte fill_mode; // 数据长度填充模式 0-就近填充 1-不填充 2-最大填充 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public byte[] reserved0; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct TRANS_PARAM_LIN + { + public byte fill_byte; // 无效字节的填充数据 + public byte st_min; // 从节点准备接收诊断请求的下一帧或传输诊断响应的下一帧所需的最小时间 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] reserved0; + }; + + + // CAN UDS请求数据 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_UDS_REQUEST // 硬件UDS接口构体 + { + public uint req_id; // 请求事务索引ID,范围0~65535 + public byte channel; // 设备通道索引 + public byte frame_type; // 帧类型 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] reserved0; + public uint src_addr; // 请求地址 + public uint dst_addr; // 响应地址 + public byte suppress_response; // 1-抑制响应 + public byte sid; // 请求服务id + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] reserved1; + + public SESSION_PARAM session_param; + public TRANS_PARAM trans_param; + public IntPtr data; // 数据数组(不包含SID) + public uint data_len; // 数据数组的长度 + public uint reserved2; + } + + + // LIN UDS请求数据 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZLIN_UDS_REQUEST // 硬件UDS接口构体 + { + public uint req_id; // 请求事务索引ID,范围0~65535 + public byte channel; // 设备通道索引 + public byte suppress_response; // 1:抑制响应 0:不抑制 + public byte sid; // 请求服务id + public byte Nad; // 节点地址 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] reserved1; + + + public SESSION_PARAM session_param; + public TRANS_PARAM_LIN trans_param; + public IntPtr data; // 数据数组(不包含SID) + public uint data_len; // 数据数组的长度 + public uint reserved2; + } + + + // UDS响应数据 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_UDS_RESPONSE + { + public byte status; // 响应状态 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] reserved; + public byte type; // 响应类型 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 大小为8 + public byte[] raw; + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct UDS_RESPONSE_Positive + { + public byte sid; // 响应服务id + public uint data_len; // 数据长度(不包含SID), 数据存放在接口传入的dataBuf中 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct UDS_RESPONSE_Negative + { + public byte neg_code; // 固定为0x7F + public byte sid; // 请求服务id + public byte error_code; // 错误码 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct UDS_RESPONSE_raw + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 大小为8 + public byte[] raw; + } + + + [StructLayout(LayoutKind.Explicit)] + public struct _UDS_RESPONSE_union + { + [FieldOffset(0)] + public UDS_RESPONSE_Positive zudsPositive; + + [FieldOffset(0)] + public UDS_RESPONSE_Negative zudsNegative; + + [FieldOffset(0)] + public UDS_RESPONSE_raw raw; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANCANFDUdsData + { + public IntPtr req; // 请求信息 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + public byte[] reserved; // 保留位 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANLINUdsData + { + public IntPtr req; // 请求信息 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + public byte[] reserved; // 保留位 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCANUdsRequestDataObj + { + public uint dataType; // 数据类型 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)] + public byte[] data; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] reserved; // 保留位 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZCAN_DYNAMIC_CONFIG_DATA + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public char[] key; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public char[] value; + } + #endregion + } + + + public class ZDBC + { + #region 常量定义 + public const int _MAX_FILE_PATH_ = 260; // 最长文件路径 + public const int _DBC_NAME_LENGTH_ = 127; // 名称最长长度 + public const int _DBC_COMMENT_MAX_LENGTH_ = 127; // 注释最长长度 + public const int _DBC_UNIT_MAX_LENGTH_ = 23; // 单位最长长度 + public const int _DBC_SIGNAL_MAX_COUNT_ = 256; // 一个消息含有的信号的最大数目 + + public const int MUTIPLEXER_NONE = 0; // 不使用复用器 + public const int MUTIPLEXER_M_VALUE = 1; // 复用信号,当复用器开关的值为multiplexer_value时,该信号有效 + public const int MUTIPLEXER_M_SWITCH = 2; // 复用器开关,一个DBC消息只能有一个信号为开关 + + public const int FT_CAN = 0; // CAN + public const int FT_CANFD = 1; // CANFD + + public const int PROTOCOL_J1939 = 0; + public const int PROTOCOL_OTHER = 1; + public const uint INVALID_DBC_HANDLE = 0xffffffff; // 无效的DBC句柄 + #endregion + + #region 函数部分 + + // ZDBC.dll + public delegate bool OnSend(IntPtr ctx, IntPtr pObj); + public delegate void OnMultiTransDone(IntPtr ctx, IntPtr pMsg, IntPtr data, UInt16 nLen, byte nDirection); + public static OnSend onSend; + public static OnMultiTransDone onMultiTransDone; + + /// + /// 此函数用于初始化解析模块,只需要初始化一次。 + /// + /// 是否关闭多帧发送,为 1 时不支持多帧的消息发送。 + /// 是否开启异步解析;0-不启动,ZDBC_AsyncAnalyse 接口无效;1-启动, 独立线程解析出消息。 + /// 为 INVALID_DBC_HANDLE 表示初始化失败,其他表示初始化成功,保存该返回值,之后的函数调用都要用到该句柄。 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZDBC_Init(byte disableMultiSend = 0, byte enableAsyncAnalyse = 1); + + /// + /// 释放资源, 与DBC_Init配对使用 + /// + /// hDBC-句柄, ZDBC_Init的返回值 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_Release(uint DBCHandle); + + /// + /// 此函数用以加载 DBC 格式文件。 + /// + /// 句柄;ZDBC_Init的返回值 + /// 结构体 FileInfo的指针 + /// 为 true 表示加载成功,false 表示失败。 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_LoadFile(uint DBCHandle, IntPtr pFileInfo); + + /// + /// 从字符串加载DBC + /// + /// hDBC-句柄, DBC_Load的返回值 + /// pFileContent-文件内容字符串 + /// merge-是否合并到当前数据库; 1:不清除现有的数据, 即支持加载多个文件;0:清除原来的数据 + /// + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_LoadContent(uint DBCHandle, IntPtr pFileContent, uint merge); + + /// + /// 获取文件的第一条消息。 + /// + /// hDBC-句柄, DBC_Load的返回值 + /// pMsg 存储消息的信息 + /// true表示成功 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetFirstMessage(uint DBCHandle, IntPtr pMsg); + + /// + /// 获取下一条消息。 + /// + /// hDBC-句柄, DBC_Load的返回值 + /// pMsg 存储消息的信息 + /// true表示成功 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetNextMessage(uint DBCHandle, IntPtr pMsg); + + /// + /// 此函数用以根据 ID 获取消息数据。 + /// + /// 句柄; + /// 帧 ID; + /// 消息信息结构体 + /// + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetMessageById(uint DBCHandle, uint nID, IntPtr pMsg); + + /// + /// 此函数用以获取 DBC 文件中含有的消息数目。 + /// + /// DBC句柄 + /// DBC 文件中含有的消息数目 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern uint ZDBC_GetMessageCount(uint DBCHandle); + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_Analyse(uint DBCHandle, IntPtr pObj, IntPtr pMsg); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_AsyncAnalyse(uint DBCHandle, IntPtr pObj, uint frame_type, UInt64 extraData); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_OnReceive(uint DBCHandle, IntPtr pObj, uint frame_type); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_SetSender(uint hDBC, OnSend sender, IntPtr ctx); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_SetOnMultiTransDoneFunc(uint hDBC, OnMultiTransDone func, IntPtr ctx); + + + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern byte ZDBC_Send(uint hDBC, IntPtr pMsg); + + /// + /// 根据原始数据解码为 DBCMessage。 + /// + /// DBC句柄 + /// 输出参数,解析结果。 + /// 帧数据数组, ControlCAN 传入 VCI_CAN_OBJ, zlgcan 传入 can_frame。 + /// 原始帧数据个数, 即数组大小。 + /// frame_type 帧类型, 参考FT_CAN=0、FT_CANFD=1,ControlCAN不支持CANFD。 + /// 是否成功。 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_Decode(uint DBCHandle, IntPtr P2DBCMessage, IntPtr P2Obj, uint nCount, byte frame_type); + + /// + /// 根据 DBCMessage 编码为原始数据。 + /// + /// DBC句柄; + /// 编码的原始数据缓冲区数组, ControlCAN 传入 VCI_CAN_OBJ, zlgcan 传入 can_frame。 + /// 输出参数,pObj 缓冲区大小, 返回时为实际原始数据个数。 + /// 输入参数,DBC 消息。 + /// frame_type 帧类型, FT_CAN=0、FT_CANFD=1,ControlCAN不支持CANFD。 + /// 是否成功。 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_Encode(uint DBCHandle, IntPtr P2Obj, IntPtr P2nCount, IntPtr pMsg, byte frame_type); + + /// + /// 信号原始值转换为实际值 + /// + /// sgl 信号 + /// rawVal 原始值, 如果该值超出信号长度可表示范围,会被截断。 + /// 实际值 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern double ZDBC_CalcActualValue(IntPtr sgl, IntPtr rawVal); //原始值通过计算转为实际值,实际值会传入rawVal的地址 + + /// + /// 信号实际值转换为原始值 + /// + /// sgl 信号 + /// actualVal 实际值, 超出可表示范围时会被修正 + /// 原始值 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt64 ZDBC_CalcRawValue(IntPtr sgl, IntPtr actualVal); + + /// + /// 获取网络节点数量 + /// + /// ZDBC_Init的返回值 + /// 网络节点总数量 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt32 ZDBC_GetNetworkNodeCount(uint DBCHandle); + + /// + /// + /// + /// ZDBC_Init的返回值 + /// index 位置索引 + /// DBCNetworkNode * node 网络节点信息 + /// + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern bool ZDBC_GetNetworkNodeAt(uint DBCHandle, UInt32 index, IntPtr node); + + + /// + /// 获取具体信号的值与含义对个数 + /// + /// ZDBC_Init的返回值 + /// message的ID + /// signal的名字 + /// + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt32 ZDBC_GetValDescPairCount(uint DBCHandle, UInt32 mag_id, string signal_name); + + + /// + /// 获取具体信号的值与含义对 + /// + /// ZDBC_Init的返回值 + /// message的ID + /// signal的名字 + /// ValDescPair结构体类型 + [DllImport(".\\zdbc.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZDBC_GetValDescPair(uint DBCHandle, UInt32 mag_id, string signal_name, IntPtr pair); + + + #endregion + + #region DBC 结构体部分 + + public struct FileInfo + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _MAX_FILE_PATH_ + 1)] + public byte[] strFilePath; // dbc文件路径 + public byte type; // dbc的协议类型, j1939选择PROTOCOL_J1939, 其他协议选择PROTOCOL_OTHER + public byte merge; // 1:不清除现有的数据, 即支持加载多个文件 0:清除原来的数据 + }; + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct DBCSignal + { + + public UInt32 nStartBit; // 起始位 + public UInt32 nLen; // 位长度 + public double nFactor; // 转换因子 + public double nOffset; // 转换偏移实际值=原始值*nFactor+nOffset + public double nMin; // 最小值 + public double nMax; // 最大值 + public UInt64 nRawvalue; // 原始值 + public byte is_signed; // 1:有符号数据, 0:无符号 + public byte is_motorola; // 是否摩托罗拉格式 + public byte multiplexer_type; // 复用器类型 + public byte val_type; // 0:integer, 1:float, 2:double + public UInt32 multiplexer_value; // 复用器开关值为此值时信号有效 + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_UNIT_MAX_LENGTH_ + 1)] + public byte[] unit; //单位 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strName; //名称 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_COMMENT_MAX_LENGTH_ + 1)] + public byte[] strComment; //注释 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strValDesc; //值描述 + + public double initialValue; // 初始化值(原始值) + public uint initialValueValid; // 初始值是否有效 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct DBCMessage + { + public UInt32 nSignalCount; // 信号数量 + public UInt32 nID; // ID + public UInt32 nSize; // 消息占的字节数目 + public double nCycleTime; // 发送周期 + public byte nExtend; // 1:扩展帧, 0:标准帧 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_SIGNAL_MAX_COUNT_)] + public DBCSignal[] vSignals; // 信号集合 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strName; // 名称 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_COMMENT_MAX_LENGTH_ + 1)] + public byte[] strComment; // 注释 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct ValDescPair + { + public double value; // 信号值 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = _DBC_NAME_LENGTH_ + 1)] + public byte[] strName; // 对应的值描述 + } + #endregion + } + + + public class ZUDS + { + #region 参数定义 + + public static uint udsRTR = 0x40000000; // Remote Transmission Request + public static uint udsEFF = 0x80000000; // Extend Frame Flag + public static uint udsERR = 0x20000000; // Err flag + + #endregion + + #region 函数部分 + [UnmanagedFunctionPointer(CallingConvention.StdCall)] + public delegate uint OnUDSTransmitDelegate(IntPtr ctx, IntPtr frame, uint count); + + + /// + /// 该函数用于初始化 UDS 函数库,返回操作句柄,用于后续的操作,与 ZUDS_Release + /// 配对使用。 + /// typedef uint32 TP_TYPE; // transport protocol + /// #define DoCAN 0 + /// + /// + /// 操作句柄,= ZUDS_INVALID_HANDLE 为无效句柄,其他值为有效句柄。 + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern UInt32 ZUDS_Init(uint type); + + + /// + /// 该函数用于释放资源,与 ZUDS_Init 配对使用。 + /// + /// + /// + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_Release(uint type); + + + /// + /// 该函数用于设置函数库的参数。 + /// + /// + /// 参数类型,= PARAM_TYPE_SESSION 0 用 于 设 置 会 话 层 参 数 , = + ///PARAM_TYPE_ISO15765 1 用于设置 ISO15765 的通信参数; + /// 参数值,type =PARAM_TYPE_SESSION 0 时为 ZUDS_SESSION_PARAM, + ///type= PARAM_TYPE_ISO15765 1 时为 ZUDS_ISO15765_PARAM。 + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_SetParam(UInt32 ZUDS_HANDLE, byte type, IntPtr param); + + + /// + /// 该函数用于设置发送回调函数。函数库自身并不发送帧数据,把打包的帧数据通过回调 + ///函数传出给用户发送,用户可通过 zlgcan 函数库进行帧数据发送。 + /// + /// + /// ctx 上下文参数, 在回调函数中传出, 库内部不会处理该参数; + /// :回调函数原型;typedef uint32 (*OnUDSTransmit)(void* ctx, const ZUDS_FRAME* frame, uint32 count); + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_SetTransmitHandler(UInt32 ZUDS_HANDLE, IntPtr ctx, OnUDSTransmitDelegate onUDSTransmit); + + + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_OnReceive(UInt32 ZUDS_HANDLE, IntPtr ZUDS_FRAME); + + + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_Request(UInt32 ZUDS_HANDLE, IntPtr ZUDS_REQUEST, IntPtr ZUDS_RESPONSE); + + + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_Stop(UInt32 ZUDS_HANDLE); + + + [DllImport(".\\zuds.dll", CallingConvention = CallingConvention.StdCall)] + public static extern void ZUDS_SetTesterPresent(UInt32 ZUDS_HANDLE, byte enable, IntPtr param); + + #endregion + + #region 结构体部分 + /// + /// 会话层面参数;即一应一答传输时的通讯参数。 + /// + public struct ZUDS_SESSION_PARAM + { + public UInt16 timeout;// ms, timeout to wait the response of the server + public UInt16 enhanced_timeout; // ms,timeout to wait after negative response: error code 0x78 + public UInt32 reserved0; // 保留 + public UInt32 reserved1; // 保留 + } + + + /// + /// 传输数据部分的参数,例如传输时侯每帧报文的字节数。 + /// + public struct ZUDS_ISO15765_PARAM + { + public byte version; // VERSION_0, VERSION_1格式版本,为 VERSION_0 时符合 ISO15765-2 的 2004 版本格式要求;为 + //hVERSION_1 是符合 ISO15765-2 的 2016 版本新增的格式要求,如下图所示 + public byte max_data_len; // max data length, can:8, canfd:64 + public byte local_st_min; // ms, min time between two consecutive frames + public byte block_size; + public byte fill_byte; // fill to invalid byte + public byte frame_type; // 0:std 1:ext + public byte is_modify_ecu_st_min; //是否忽略 ECU 返回流控的 STmin,强制使用本程序设置的 + //remote_st_min 参数代替 + + public byte remote_st_min; //发 送 多 帧 时 用, is_ignore_ecu_st_min = 1 时 有 效 , + //0x00-0x7F(0ms ~127ms), 0xF1-0xF9(100us ~900us) + public UInt16 fc_timeout; //接收流控超时时间(ms), 如发送首帧后需要等待回应流控帧。 + public byte fill_mode;//字节填充模式。FILL_MODE_NONE-不填充0;FILL_MODE_SHORT- 小于 8 字节填充至 8 字节,大于 8 字节时按 DLC 就近填充1;FILL_MODE_MAX- 始终填充至最大数据长度 (不建议)2。 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public byte[] reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZUDS_TESTER_PRESENT_PARAM + { + public UInt32 addr;//会话保持的请求地址; + public UInt16 cycle;//发送周期,单位毫秒; + public byte suppress_response; // 1:suppress是否抑制响应,建议设置为 1; + public UInt32 reserved;//:保留,忽略即可。 + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZUDS_REQUEST + { + public uint src_addr; // 请求地址 + public uint dst_addr; // 响应地址 + public byte suppress_response; // 1:抑制响应 + public byte sid; //service id of request + public ushort reserve0; + public IntPtr param; //array,params of the service + public uint param_len; //参数数组的长度 + public uint reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZUDS_RESPONSE + { + public byte status; + public byte type; // RT_POSITIVE, RT_NEGATIVE + public _ZUDS_Union union; + public uint reserved; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct ZUDS_positive + { + public byte sid; // service id of response + public IntPtr param; // array, params of the service, don't free + public uint param_len; + + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct _ZUDS_negative + { + public byte neg_code; // 0x7F + public byte sid; //service id of response + public byte error_code;//消极响应的错误码 + + } + + + [StructLayout(LayoutKind.Explicit)] + public struct _ZUDS_Union + { + [FieldOffset(0)] + public ZUDS_positive zudsPositive; + + [FieldOffset(0)] + public _ZUDS_negative zudsNegative; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZUDS_FRAME + { + public uint id; + public byte extend; + public byte remote; + public byte data_len; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public byte[] data; + public uint reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public class ZUDS_CTX + { + public IntPtr can_type; // 0-CAN 1-CANFD 2-CANFD加速 + public IntPtr chn_handle; // 通道句柄 + } + #endregion + } +}