using System; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using System.Text; using System.Threading; using System.Threading.Tasks; using CCDCount.DLL.CanBus; namespace CanTest { internal class Program { static UInt32 m_devtype = 4;//USBCAN2 static UInt32 m_devind = 0; static UInt32 m_canind = 0; unsafe static void Main(string[] args) { // 示例:运行CANopen主站程序 //RunCanOpenMaster(); ConnectCan(); SendTest(); //GetTest(); //MessageTest(); } /// /// 运行CANopen主站示例 /// static void RunCanOpenMaster() { Console.WriteLine("=== CANopen主站示例 ===\n"); // 创建主站 using (CanOpenMaster master = new CanOpenMaster(4, 0, 0)) { // 注册事件回调 master.OnTpdoReceived += Master_OnTpdoReceived; master.OnHeartbeatReceived += Master_OnHeartbeatReceived; master.OnEmergencyReceived += Master_OnEmergencyReceived; master.OnLogMessage += Master_OnLogMessage; // 初始化主站(波特率500kbps) if (!master.Initialize(CanBaudRate.BaudRate_500K)) { Console.WriteLine("主站初始化失败!"); return; } Console.WriteLine("\n主站初始化成功!\n"); // 方式1: 不使用EDS文件(直接添加节点) Console.WriteLine("=== 方式1: 不使用EDS文件 ==="); master.AddNode(1, "IO_Module_1"); //master.AddNode(2, "IO_Module_2"); // 方式2: 使用EDS文件(推荐用于第三方设备) //Console.WriteLine("\n=== 方式2: 使用EDS文件 ==="); //string edsPath1 = @"D:\EDS\servo_drive.eds"; //string edsPath2 = @"D:\EDS\io_module.eds"; // 如果EDS文件存在,加载它们 //if (System.IO.File.Exists(edsPath1)) //{ // master.AddNode(3, "Servo_Drive", edsPath1); // // 自动配置PDO映射 // master.AutoConfigurePdoFromEds(3); //} //else //{ // master.AddNode(3, "Servo_Drive"); // Console.WriteLine($"EDS文件不存在: {edsPath1}, 使用默认配置"); //} //if (System.IO.File.Exists(edsPath2)) //{ // master.AddNode(5, "Custom_Device", edsPath2); // master.AutoConfigurePdoFromEds(5); //} //else //{ // master.AddNode(5, "Custom_Device"); // Console.WriteLine($"EDS文件不存在: {edsPath2}, 使用默认配置"); //} Console.WriteLine($"\n已添加 {master.NodeIds.Count} 个节点\n"); // 显示EDS信息 foreach (var nodeId in master.NodeIds) { var edsInfo = master.GetEdsInfo(nodeId); if (edsInfo != null) { Console.WriteLine($"节点{nodeId}: {edsInfo}"); } } Console.WriteLine(); // 等待从站上线 Console.WriteLine("等待从站心跳...\n"); Thread.Sleep(2000); // 扫描所有节点的设备信息 ScanAllNodes(master); // 启动所有节点 Console.WriteLine("\n=== 启动所有节点 ==="); master.StartAllNodes(); Thread.Sleep(1000); // 启用周期性SYNC(10ms周期) Console.WriteLine("\n=== 启用SYNC ==="); Console.WriteLine("注意: SYNC用于触发从站自动发送TPDO数据"); Console.WriteLine(" RPDO仍可由主站手动发送(事件驱动模式)"); master.EnableSync(10); // 主循环 - 处理PDO数据 Console.WriteLine("\n=== 进入主循环 (按任意键退出) ===\n"); Console.WriteLine("工作原理:"); Console.WriteLine(" 1. SYNC每10ms发送一次 → 从站自动发送TPDO → OnTpdoReceived事件触发"); Console.WriteLine(" 2. RPDO由主站主动调用SendRpdo()发送(不依赖SYNC)"); Console.WriteLine(" 3. SDO读写用于配置和非实时数据传输\n"); bool running = true; Task.Run(() => { Console.ReadKey(); running = false; }); int loopCount = 0; while (running && master.IsRunning) { loopCount++; // 每100次循环打印一次状态 if (loopCount % 100 == 0) { PrintNodeStatus(master); } // 示例:定期读取某个节点的SDO数据(非实时配置) if (loopCount % 500 == 0) { ReadNodeParameters(master, 1); } // 示例:向节点写入控制命令(SDO方式 - 低速但可靠) if (loopCount % 1000 == 0) { WriteControlCommand(master, 1); } // 示例:通过RPDO发送控制数据(PDO方式 - 高速实时控制) // 注意: 这里的RPDO发送是"事件驱动"模式,不依赖SYNC // 如果要实现同步RPDO,需要在从站配置RPDO为同步触发类型 if (loopCount % 200 == 0) { SendRpdoControl(master, 1); } Thread.Sleep(10); } // 停止SYNC master.DisableSync(); // 停止所有节点 Console.WriteLine("\n=== 停止所有节点 ==="); master.StopAllNodes(); Console.WriteLine("\n主站程序结束"); } } /// /// 演示如何使用EDS文件的完整示例 /// static void EdsFileUsageExample() { Console.WriteLine("=== EDS文件使用示例 ===\n"); using (CanOpenMaster master = new CanOpenMaster(4, 0, 0)) { master.Initialize(CanBaudRate.BaudRate_500K); // 场景1: 加载伺服驱动器的EDS文件 Console.WriteLine("场景1: 加载伺服驱动器EDS"); string servoEds = @"C:\Devices\ServoDrive.eds"; if (System.IO.File.Exists(servoEds)) { master.AddNode(1, "Servo_X", servoEds); // 查看EDS信息 var edsInfo = master.GetEdsInfo(1); if (edsInfo != null) { Console.WriteLine($" 厂商: {edsInfo.VendorName}"); Console.WriteLine($" 产品: {edsInfo.ProductName}"); Console.WriteLine($" TPDO映射数: {edsInfo.TpdoMappings.Count}"); // 自动配置PDO master.AutoConfigurePdoFromEds(1); } } else { Console.WriteLine(" EDS文件不存在,跳过此场景"); } // 场景2: 不使用EDS文件(硬编码方式) Console.WriteLine("\n场景2: 不使用EDS文件"); master.AddNode(2, "Simple_IO"); // 手动读写已知对象 master.SdoWriteAndWait(2, 0x6200, 0x01, 0x000000FF); var response = master.SdoReadAndWait(2, 0x6000, 0x01); if (response != null) { uint value = BitConverter.ToUInt32(response, 4); Console.WriteLine($" 读取值: 0x{value:X8}"); } // 场景3: 混合使用 Console.WriteLine("\n场景3: 混合使用(部分节点用EDS,部分不用)"); master.AddNode(3, "Device_A", servoEds); // 有EDS master.AddNode(4, "Device_B"); // 无EDS Console.WriteLine("\n完成!"); } } /// /// 简单的CANopen主站测试 /// static void SimpleCanOpenMasterTest() { Console.WriteLine("=== 简单CANopen主站测试 ===\n"); using (CanOpenMaster master = new CanOpenMaster(4, 0, 0)) { // 注册基本事件 master.OnHeartbeatReceived += (nodeId, status) => { Console.WriteLine($"[HB] 节点{nodeId}: 0x{status:X2}"); }; master.OnTpdoReceived += (nodeId, data) => { string dataStr = BitConverter.ToString(data).Replace("-", " "); Console.WriteLine($"[TPDO] 节点{nodeId}: [{dataStr}]"); }; // 初始化 if (!master.Initialize(CanBaudRate.BaudRate_500K)) { Console.WriteLine("初始化失败"); return; } // 添加一个节点 master.AddNode(1); Console.WriteLine("等待2秒接收心跳..."); Thread.Sleep(2000); // 尝试读取设备类型 Console.WriteLine("\n读取设备类型..."); uint deviceType = master.ReadDeviceType(1); Console.WriteLine($"设备类型: 0x{deviceType:X8}"); // 启动节点 Console.WriteLine("\n启动节点..."); master.NmtStartNode(1); Thread.Sleep(500); // 发送几次SYNC Console.WriteLine("\n发送SYNC..."); for (int i = 0; i < 5; i++) { master.SendSync((byte)(i + 1)); Thread.Sleep(100); } Console.WriteLine("\n测试完成"); } } /// /// TPDO接收事件处理 /// private static void Master_OnTpdoReceived(byte nodeId, byte[] data) { string dataStr = BitConverter.ToString(data).Replace("-", " "); Console.WriteLine($"[TPDO] 节点{nodeId}: 数据=[{dataStr}]"); } /// /// 心跳接收事件处理 /// private static void Master_OnHeartbeatReceived(byte nodeId, byte status) { string statusStr = GetNodeStatusString(status); Console.WriteLine($"[Heartbeat] 节点{nodeId}: 状态={statusStr} (0x{status:X2})"); } /// /// 紧急消息事件处理 /// private static void Master_OnEmergencyReceived(byte nodeId, ushort errorCode) { Console.WriteLine($"[EMERGENCY] 节点{nodeId}: 错误代码=0x{errorCode:X4}"); } /// /// 日志消息事件处理 /// private static void Master_OnLogMessage(string message) { // 可以选择性地输出日志,避免过多输出 // Console.WriteLine(message); } /// /// 扫描所有节点的设备信息 /// static void ScanAllNodes(CanOpenMaster master) { Console.WriteLine("=== 扫描节点设备信息 ==="); foreach (var nodeId in master.NodeIds) { Console.WriteLine($"\n--- 节点 {nodeId} ---"); try { // 读取设备类型 uint deviceType = master.ReadDeviceType(nodeId); Console.WriteLine($" 设备类型: 0x{deviceType:X8}"); // 读取厂商ID uint vendorId = master.ReadVendorId(nodeId); Console.WriteLine($" 厂商ID: 0x{vendorId:X8}"); // 读取产品代码 uint productCode = master.ReadProductCode(nodeId); Console.WriteLine($" 产品代码: 0x{productCode:X8}"); // 读取序列号 uint serialNumber = master.ReadSerialNumber(nodeId); Console.WriteLine($" 序列号: 0x{serialNumber:X8}"); // 更新节点信息 var node = master.GetNode(nodeId); if (node != null) { Console.WriteLine($" 状态: {node.GetStateString()}"); Console.WriteLine($" 在线: {(node.IsOnline() ? "是" : "否")}"); } } catch (Exception ex) { Console.WriteLine($" 读取失败: {ex.Message}"); } Thread.Sleep(50); // 避免总线拥塞 } } /// /// 读取节点参数 /// static void ReadNodeParameters(CanOpenMaster master, byte nodeId) { var node = master.GetNode(nodeId); if (node == null) return; // 示例:读取对象字典中的某个参数 // 这里读取0x6000:01 (假设是数字输入状态) var response = master.SdoReadAndWait(nodeId, 0x6000, 0x01, 500); if (response != null && response.Length >= 5) { uint value = BitConverter.ToUInt32(response, 4); Console.WriteLine($"[SDO Read] 节点{nodeId}, 索引0x6000:01 = 0x{value:X8}"); } } /// /// 写入控制命令(SDO方式) /// static void WriteControlCommand(CanOpenMaster master, byte nodeId) { var node = master.GetNode(nodeId); if (node == null) return; // 示例:写入对象字典中的某个参数 // 这里写入0x6200:01 (假设是数字输出控制) uint controlValue = 0x00000001; // 设置第1位 bool success = master.SdoWriteAndWait(nodeId, 0x6200, 0x01, controlValue, 500); if (success) { Console.WriteLine($"[SDO Write] 节点{nodeId}, 索引0x6200:01 = 0x{controlValue:X8} - 成功"); } else { Console.WriteLine($"[SDO Write] 节点{nodeId}, 索引0x6200:01 = 0x{controlValue:X8} - 失败"); } } /// /// 通过RPDO发送控制数据(PDO方式 - 高速) /// static int rpdoCount = 0; static void SendRpdoControl(CanOpenMaster master, byte nodeId) { var node = master.GetNode(nodeId); if (node == null) return; // 准备RPDO数据(根据映射配置) // 假设RPDO1映射: 0x6200:01 (DO1, 8位), 0x6200:02 (DO2, 8位) byte[] rpdoData = new byte[2]; rpdoData[0] = 0xFF; // DO1全部置ON rpdoData[1] = 0x00; // DO2全部置OFF // 发送RPDO bool success = master.SendRpdo(nodeId, 1, rpdoData); if (success) { // 每50次打印一次,避免刷屏 rpdoCount++; if (rpdoCount % 50 == 0) { Console.WriteLine($"[RPDO Write] 节点{nodeId}, RPDO1: DO1=0x{rpdoData[0]:X2}, DO2=0x{rpdoData[1]:X2}"); } } } /// /// 演示PDO映射配置 /// static void PdoMappingExample(CanOpenMaster master, byte nodeId) { Console.WriteLine($"\n=== 配置节点{nodeId}的PDO映射 ==="); // 配置RPDO1映射 var rpdo1Mapping = new List { new MappedObject { Index = 0x6200, SubIndex = 0x01, BitLength = 8 }, // DO1 new MappedObject { Index = 0x6200, SubIndex = 0x02, BitLength = 8 } // DO2 }; bool success = master.ConfigureRpdoMapping(nodeId, 1, rpdo1Mapping); Console.WriteLine($"RPDO1映射配置: {(success ? "成功" : "失败")}"); // 配置TPDO1映射 var tpdo1Mapping = new List { new MappedObject { Index = 0x6400, SubIndex = 0x01, BitLength = 16 }, // AI1 new MappedObject { Index = 0x6400, SubIndex = 0x02, BitLength = 16 } // AI2 }; success = master.ConfigureTpdoMapping(nodeId, 1, tpdo1Mapping); Console.WriteLine($"TPDO1映射配置: {(success ? "成功" : "失败")}"); } /// /// 打印节点状态 /// static void PrintNodeStatus(CanOpenMaster master) { Console.WriteLine("\n--- 节点状态 ---"); foreach (var nodeId in master.NodeIds) { var node = master.GetNode(nodeId); if (node != null) { Console.WriteLine(node.ToString()); } } Console.WriteLine(); } /// /// 获取节点状态字符串 /// static string GetNodeStatusString(byte status) { switch (status & 0x7F) { case 0x00: return "启动中"; case 0x04: return "停止"; case 0x05: return "运行"; case 0x7F: return "预操作"; default: return "未知"; } } static void ConnectCan() { //string SendId = "0x0000000f"; //开启设备 if (CanLibraryClass.VCI_OpenDevice(m_devtype, m_devind, 0) == 0) { Console.WriteLine("打开设备失败,请检查设备类型和设备索引号是否正确"); return; } //初始化CAN VCI_INIT_CONFIG config = new VCI_INIT_CONFIG(); config.AccCode = Convert.ToUInt32("0x00000000", 16); config.AccMask = Convert.ToUInt32("0xFFFFFFFF", 16); config.Timing0 = Convert.ToByte("0x00", 16); config.Timing1 = Convert.ToByte("0x14", 16); config.Filter = (Byte)1; config.Mode = (Byte)0; if (CanLibraryClass.VCI_InitCAN(m_devtype, m_devind, m_canind, ref config) == 0) { Console.WriteLine("初始化Can失败"); return; } if (CanLibraryClass.VCI_StartCAN(m_devtype, m_devind, m_canind) == 0) { Console.WriteLine("开启Can失败"); return; } } unsafe static void SendTest() { int DataLen = 1; int SendCount = 0; int sendnum = 0; int InSendCount = 0; TimeSpan WhileSleepTime = TimeSpan.FromTicks(10000 * 4); Stopwatch SW = new Stopwatch(); Stopwatch RunTime = new Stopwatch(); Console.WriteLine("请输入循环次数"); InSendCount = Convert.ToInt32(Console.ReadLine()); RunTime.Start(); while (SendCount < InSendCount) { SW.Restart(); VCI_CAN_OBJ sendobj = new VCI_CAN_OBJ(); sendobj.RemoteFlag = (byte)(0); sendobj.ExternFlag = (byte)(0); sendobj.ID = Convert.ToUInt32(Convert.ToString(2, 16), 16); sendobj.DataLen = Convert.ToByte(DataLen); for (int i = 1; i < DataLen + 1; i++) { sendobj.Data[i - 1] = BitConverter.GetBytes(sendnum % 256)[0]; sendnum++; } if (CanLibraryClass.VCI_Transmit(m_devtype, m_devind, m_canind, ref sendobj, 1) == 0) { Console.WriteLine("发送失败"); continue; } SendCount++; Console.WriteLine("运行时常:{0},帧ID:{1},帧发送耗时:{2}\n" + "Data0:{3},Data0:{4},Data0:{5},Data0:{6},\n" + "Data0:{7},Data0:{8},Data0:{9},Data0:{10},", RunTime.Elapsed, SendCount, SW.Elapsed, sendobj.Data[0], sendobj.Data[1], sendobj.Data[2], sendobj.Data[3], sendobj.Data[4], sendobj.Data[5], sendobj.Data[6], sendobj.Data[7]); while (SW.Elapsed <= WhileSleepTime) { } if (SendCount == InSendCount) { RunTime.Stop(); Console.WriteLine("总耗时:{0}", RunTime.Elapsed); Console.WriteLine("输入新的循环数(输入0结束):"); int newnum = Convert.ToInt32(Console.ReadLine()); if (newnum != 0) { SendCount = 0; InSendCount = newnum; RunTime.Restart(); } } } CanLibraryClass.VCI_ResetCAN(m_devtype, m_devind, m_canind); Console.WriteLine("流程完成"); Console.ReadKey(); } unsafe static void GetTest() { UInt32 res = new UInt32(); VCI_CAN_OBJ[] m_recobj = new VCI_CAN_OBJ[1000]; Console.WriteLine("请输入运行时长(单位:分钟)"); int runtimeLength = Convert.ToInt32(Console.ReadLine()); DateTime StartTime = DateTime.Now; int getCount = 0; Task SetTask = new Task(() => { while ((DateTime.Now - StartTime).TotalMilliseconds < runtimeLength * 60 * 1000) { res = CanLibraryClass.VCI_Receive(m_devtype, m_devind, m_canind, ref m_recobj[0], 1000, 100); getCount++; if (res == 0xFFFFFFFF) res = 0; if (res != 0) { for (UInt32 i = 0; i < res; i++) { string str = ""; str = "接收到数据: "; str += " 帧ID:0x" + System.Convert.ToString(m_recobj[i].ID, 16); str += " 帧格式:"; if (m_recobj[i].RemoteFlag == 0) str += "数据帧 "; else str += "远程帧 "; if (m_recobj[i].ExternFlag == 0) str += "标准帧 "; else str += "扩展帧 "; if (m_recobj[i].RemoteFlag == 0) { fixed (VCI_CAN_OBJ* m_recobj1 = &m_recobj[i]) { str += "数据:"; for (byte k = 0; k < 8; k++) { str += " " + m_recobj1->Data[k].ToString("X2"); } } } Console.WriteLine(str); } } } Console.WriteLine("运行结束,运行次数{0}", getCount); CanLibraryClass.VCI_ResetCAN(m_devtype, m_devind, m_canind); }); SetTask.Start(); Console.ReadKey(); } unsafe static void MessageTest() { UInt32 res = new UInt32(); VCI_CAN_OBJ[] m_recobj = new VCI_CAN_OBJ[1000]; Console.WriteLine("请输入运行时长(单位:分钟)"); int runtimeLength = Convert.ToInt32(Console.ReadLine()); DateTime StartTime = DateTime.Now; int getCount = 0; int DataLen = 8; int SendCount = 0; //Stopwatch SW = new Stopwatch(); Stopwatch InterValSW = new Stopwatch(); TimeSpan WhileSleepTime = TimeSpan.FromTicks(10000 * 1); TimeSpan MaxTime = TimeSpan.MinValue; TimeSpan MinTime = TimeSpan.MaxValue; int num04 = 0; int num48 = 0; int num8p = 0; Task SetTask = new Task(() => { int previousWriteDone = 0; while ((DateTime.Now - StartTime).TotalMilliseconds < runtimeLength * 60 * 1000) { //SW.Restart(); InterValSW.Restart(); res = CanLibraryClass.VCI_Receive(m_devtype, m_devind, m_canind, ref m_recobj[0], 1000, 100); if (res == 0xFFFFFFFF) res = 0; //if (res == 0) Console.WriteLine("res=0"); if (res != 0) { for (UInt32 i = 0; i < res; i++) { string str = ""; if (m_recobj[i].RemoteFlag == 0) { getCount++; int writeDone = -1; fixed (VCI_CAN_OBJ* m_recobj1 = &m_recobj[i]) { writeDone = m_recobj1->Data[6] & 0x01; //if(true) if (writeDone != previousWriteDone) { str = DateTime.Now.ToString("HH mm ss ffffff:"); str += "运行次数:" + getCount; str += "接收到数据: "; str += " 帧ID:0x" + System.Convert.ToString(m_recobj[i].ID, 16); str += " 帧格式:"; if (m_recobj[i].RemoteFlag == 0) str += "数据帧 "; else str += "远程帧 "; if (m_recobj[i].ExternFlag == 0) str += "标准帧 "; else str += "扩展帧 "; str += "数据:"; for (byte k = 0; k < 8; k++) { str += " " + m_recobj1->Data[k].ToString("X2"); } } } if ((writeDone == 1 && previousWriteDone == 0) || (writeDone == 0 && previousWriteDone == 1)) { VCI_CAN_OBJ sendobj = new VCI_CAN_OBJ(); sendobj.RemoteFlag = (byte)(0); sendobj.ExternFlag = (byte)(0); sendobj.ID = Convert.ToUInt32(Convert.ToString(2, 16), 16); sendobj.DataLen = Convert.ToByte(DataLen); byte SendMessage0 = new byte(); SendMessage0 |= (byte)(1 << SendCount % 8); //SendMessage0 |= (byte)(1 << 0); sendobj.Data[0] = SendMessage0; byte SendMessage1 = new byte(); SendMessage1 |= (byte)(1 << SendCount % 8); //SendMessage1 |= (byte)(1 << 0); sendobj.Data[1] = SendMessage1; byte SendMessage6 = new byte(); SendMessage6 = (byte)(1 << 0); sendobj.Data[6] = SendMessage6; byte SendMessage7 = new byte(); if (SendCount % 10 == 0) SendMessage7 |= (byte)(1 << 0); if (SendCount % 11 == 0) SendMessage7 |= (byte)(1 << 1); sendobj.Data[7] = SendMessage7; str += "发送数据:"; str += string.Format("byte0:{0},byte1:{1},byte6:{2},byte7:{3}", SendMessage0.ToString("000"), SendMessage1.ToString("000"), SendMessage6.ToString("000"), SendMessage7.ToString("000")); if (CanLibraryClass.VCI_Transmit(m_devtype, m_devind, m_canind, ref sendobj, 1) == 0) { Console.WriteLine("发送失败"); continue; } InterValSW.Stop(); if (SendCount != 0) { MaxTime = InterValSW.Elapsed > MaxTime ? InterValSW.Elapsed : MaxTime; MinTime = InterValSW.Elapsed < MinTime ? InterValSW.Elapsed : MinTime; if (InterValSW.Elapsed <= TimeSpan.FromMilliseconds(4)) { num04++; } else if (InterValSW.Elapsed <= TimeSpan.FromMilliseconds(8)) { num48++; } else { num8p++; } } SendCount++; } previousWriteDone = writeDone; } if (!string.IsNullOrEmpty(str)) Console.WriteLine(str); } } //while (SW.Elapsed <= WhileSleepTime) //{ //} } Console.WriteLine("运行结束,运行次数{0},发送次数{1},最小耗时{2},最大耗时{3}", getCount, SendCount, MinTime.TotalMilliseconds, MaxTime.TotalMilliseconds); Console.WriteLine("运行耗时统计,0-4:{0},4-8:{1},8+:{2}", num04, num48, num8p); CanLibraryClass.VCI_ResetCAN(m_devtype, m_devind, m_canind); }); SetTask.Start(); Console.ReadKey(); } } }