跳到主要内容

AI 提示词

本页面为 AI 助手提供 DarraEtherCAT C# SDK 的正确使用方法,避免生成错误代码。 可将本页内容作为 System Prompt 或上下文提供给 AI。


基本信息

  • NuGet 包名: DarraEtherCAT(安装: dotnet add package DarraEtherCAT
  • 命名空间: using DarraEtherCAT;
  • 目标框架: .NET Standard 2.0(兼容 .NET Framework 4.6.1+ / .NET 6+)
  • 运行要求: Windows 管理员权限 + Npcap(或 DarraRT 驱动)
  • 主站核心类: DarraEtherCAT(注意: 类名与命名空间相同)

1. 初始化(Fluent API)

正确写法

// ✅ 推荐: 使用 DENI/ENI 配置文件
var master = new DarraEtherCAT()
.SetENI(@"C:\EtherCAT\config.deni")
.Build();

if (master == null) return; // Build 失败返回 null

// ✅ 需要错误信息时用元组解构
var master2 = new DarraEtherCAT()
.SetENI(@"C:\EtherCAT\config.deni");

var (success, message) = master2.Build();
if (!success)
{
Console.WriteLine($"初始化失败: {message}");
return;
}

常见错误

// ❌ 错误: 忘记调用 Build()
var master = new DarraEtherCAT().SetENI("config.xml");
master.State = EcState.OP; // master 还是 DarraEtherCAT 构建器,不是主站实例

// ❌ 错误: 不检查 Build 返回值
var master = new DarraEtherCAT().SetENI("config.xml").Build();
master.State = EcState.OP; // 如果 Build 失败,master 为 null,此处 NullReferenceException

// ❌ 错误: 使用 new 以外的方式创建
var master = DarraEtherCAT.Create(); // 不存在此方法

动态扫描(无 DENI 文件)

var adapters = DarraEtherCAT.GetNetworkInfo(NeedSlavesNum: true);
var target = adapters.FirstOrDefault(a => a.SlaveNum > 0);
if (target == null) return;

var master = new DarraEtherCAT()
.SetNetwork(target)
.SetEsiFiles(@"C:\ESI") // ESI 文件目录
.EnableAutoStartup() // 自动配置从站
.Build();

if (master == null) return;

2. 状态机

EtherCAT 状态机流程: Init → PreOp → SafeOp → OP

// ✅ 简洁写法
master.State = EcState.OP;

// ✅ 需要错误详情
var (ok, msg) = master.SetState(EcState.OP);
if (!ok) Console.WriteLine($"失败: {msg}");

// ✅ 停止主站
master.Stop(); // 切换到 PreOp

常见错误

// ❌ 错误: 使用不存在的枚举值
master.State = EcState.Operational; // 正确是 EcState.OP

// ❌ 错误: 直接跳转到不存在的状态
master.State = EcState.Run; // 不存在此状态

EcState 枚举值: None, Init, PreOp, Boot, SafeOp, OP


3. 从站访问

// ✅ 从站列表是 0-based 索引
var slave = master.Slaves[0]; // 第一个从站

// ✅ 遍历从站
foreach (var s in master.Slaves)
Console.WriteLine($"{s.Name} - 状态: {s.State}");

// ✅ 从站数量
int count = master.SlaveCount;

常见错误

// ❌ 错误: 事件回调中的 slaveIndex 是 1-based,Slaves 列表是 0-based
master.Events.SlaveOffline += (slaveIndex) =>
{
var slave = master.Slaves[slaveIndex]; // ❌ 偏移了一个
var slave = master.Slaves[slaveIndex - 1]; // ✅ 正确
};

4. PDO 数据读写

结构体映射(零拷贝,推荐)

// ✅ 结构体必须使用 Sequential + Pack=1
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ServoInput
{
public ushort StatusWord;
public int ActualPosition;
public int ActualVelocity;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ServoOutput
{
public ushort ControlWord;
public int TargetPosition;
}

// ✅ 零拷贝读取输入
ref ServoInput input = ref slave.PDO.InputsMapping<ServoInput>();
Console.WriteLine($"位置: {input.ActualPosition}");

// ✅ 零拷贝写入输出
ref ServoOutput output = ref slave.PDO.OutputsMapping<ServoOutput>();
output.ControlWord = 0x000F;
output.TargetPosition = 10000;

常见错误

// ❌ 错误: 缺少 StructLayout 属性
public struct ServoInput // 没有 StructLayout,内存布局不确定
{
public ushort StatusWord;
public int ActualPosition;
}

// ❌ 错误: Pack 不是 1,会有填充字节
[StructLayout(LayoutKind.Sequential)] // 默认 Pack=8,可能有 padding
public struct ServoInput { ... }

// ❌ 错误: 结构体大小与 PDO 字节数不匹配
// 如果 slave.PDO.Ibytes = 10,结构体大小必须恰好 10 字节

// ❌ 错误: 用 class 代替 struct
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class ServoInput { ... } // 必须是 struct,不能是 class

字节数组访问(快速原型)

// ✅ 读取输入字节
byte[] inputs = slave.PDO.Inputs;

// ✅ 写入输出字节(长度必须与 Obytes 匹配)
slave.PDO.Outputs = new byte[] { 0x0F, 0x00, 0x10, 0x27, 0x00, 0x00 };

// ✅ 索引访问
var value = slave.PDO.In[0].Content;
slave.PDO.Out[0].Content = 0xFF;

5. 事件系统

PDO 周期回调

// ✅ 异步模式(推荐,不阻塞实时线程)
master.Events.ProcessDataCyclicAsync += (masterIndex) =>
{
ref var input = ref slave.PDO.InputsMapping<ServoInput>();
// 处理数据...
};

// ✅ 同步模式(实时线程中执行,必须快速返回)
master.Events.ProcessDataCyclicSync += (masterIndex) =>
{
ref var output = ref slave.PDO.OutputsMapping<ServoOutput>();
output.TargetPosition = nextPosition;
};

常见错误

// ❌ 错误: 在同步回调中执行耗时操作
master.Events.ProcessDataCyclicSync += (masterIndex) =>
{
slave.CoE[0x6041][0].Value; // ❌ SDO 读写是邮箱操作,会阻塞实时线程
Thread.Sleep(10); // ❌ 绝对不能 Sleep
File.WriteAllText(...); // ❌ 文件 I/O 会阻塞
};

状态与热插拔事件

// ✅ 所有事件在非 UI 线程触发,更新 UI 需要线程同步
master.Events.SlaveOffline += (slaveIndex) =>
this.Invoke(() => label.Text = $"从站 {slaveIndex} 离线"); // WinForms

master.Events.StateChanged += (sender, e) =>
Console.WriteLine($"主站: {e.OldState}{e.NewState}");

master.Events.EmergencyEvent += (masterIndex, slaveIndex, errorCode, errorReg, b1, w1, w2) =>
Console.WriteLine($"从站 {slaveIndex} 紧急: 0x{errorCode:X4}");

输入数据变化检测

// ✅ 方式1: 主站级 — 监控所有从站
master.Events.InputDataChanged += (masterIndex, slaveIndex) =>
{
var s = master.Slaves[slaveIndex - 1]; // 注意 -1
ref var input = ref s.PDO.InputsMapping<ServoInput>();
};

// ✅ 方式2: 从站级 — 仅监控特定从站
slave.Events.InputChanged += () =>
{
ref var input = ref slave.PDO.InputsMapping<ServoInput>();
};

// ✅ 方式3: 结构体绑定 — 自动获取变化前后值
var instance = slave.PDO.InputsMapping<ServoInput>(e =>
{
Console.WriteLine($"位置: {e.Previous.ActualPosition}{e.Current.ActualPosition}");
});
ref var current = ref instance.Value; // 零拷贝访问当前值

6. CoE 对象字典(SDO 读写)

// ✅ 前提: 从站支持 CoE 时 slave.CoE 非 null
if (slave.CoE == null) return; // 从站不支持 CoE

// ✅ 读取 — 自动类型转换
ushort status = slave.CoE[0x6041][0].Value;
int position = slave.CoE[0x6064][0].Value;
string name = slave.CoE[0x1008][0].Value;

// ✅ 写入 — 需显式指定类型
slave.CoE[0x6040][0].Value = (ushort)0x0006;
slave.CoE[0x607A][0].Value = 100000;

// ✅ 按名称访问
var od = slave.CoE["Device Type"];

// ✅ 原始字节写入
slave.CoE[0x6040][0].Bytes = BitConverter.GetBytes((ushort)0x0006);

// ✅ 遍历对象字典
foreach (var item in slave.CoE)
Console.WriteLine($"0x{item.Key:X4}: {item.Value.Name}");

常见错误

// ❌ 错误: 不检查 CoE 是否为 null
ushort sw = slave.CoE[0x6041][0].Value; // 如果从站不支持 CoE,NullReferenceException

// ❌ 错误: 写入时不指定类型
slave.CoE[0x6040][0].Value = 0x0006; // 推断为 int,但对象是 ushort,可能导致大小不匹配

// ❌ 错误: 在 PDO 同步回调中读写 SDO
master.Events.ProcessDataCyclicSync += (idx) =>
{
slave.CoE[0x6041][0].Value; // ❌ SDO 是邮箱操作,会阻塞实时线程
};
// ✅ SDO 读写应在 Async 回调或独立线程中

7. CiA 402 伺服控制

// ✅ 前提检查
if (slave.CoE?.CiA402 == null) return; // 从站不支持 CiA 402

// ✅ 伺服使能(自动处理完整状态机)
var (ok, state) = slave.CoE.CiA402.Enable();
if (!ok) Console.WriteLine($"使能失败: {state.GetDescription()}");

// ✅ 设置操作模式
slave.CoE.CiA402.OperationMode = ModeCiA402.CSP; // 周期同步位置

// ✅ 读取状态
var driveState = slave.CoE.CiA402.StateDrive;
bool reached = slave.CoE.CiA402.TargetReached;
bool fault = slave.CoE.CiA402.HasFault;

// ✅ 直接访问控制字/状态字
slave.CoE.CiA402.Controlword = 0x000F;
ushort sw = slave.CoE.CiA402.Statusword;

// ✅ 故障清除
slave.CoE.CiA402.FaultReset();

// ✅ 禁用
slave.CoE.CiA402.Disable();

常见错误

// ❌ 错误: 手动实现状态机(不需要,Enable 自动处理)
slave.CoE[0x6040][0].Value = (ushort)0x0006; // Shutdown
slave.CoE[0x6040][0].Value = (ushort)0x0007; // Switch On
slave.CoE[0x6040][0].Value = (ushort)0x000F; // Enable
// ✅ 直接用: slave.CoE.CiA402.Enable();

// ❌ 错误: 不检查 CiA402 是否为 null
slave.CoE.CiA402.Enable(); // 如果不支持 CiA402,NullReferenceException

ModeCiA402 枚举: PP(1), VL(2), PV(3), PT(4), HM(6), IP(7), CSP(8), CSV(9), CST(10)


8. 资源释放

// ✅ 使用 using 语句(推荐)
using var master = new DarraEtherCAT()
.SetENI("config.xml")
.Build();

if (master == null) return;

// ✅ 手动释放
master.Close(); // 或 master.Dispose();

常见错误

// ❌ 错误: 程序退出时不释放资源
// 会导致网卡未归还,下次启动可能绑定失败

9. 完整示例模板

基础 PDO 通信

using DarraEtherCAT;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ServoInput
{
public ushort StatusWord;
public int ActualPosition;
public int ActualVelocity;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ServoOutput
{
public ushort ControlWord;
public int TargetPosition;
}

// 初始化
var master = new DarraEtherCAT()
.SetENI(@"C:\EtherCAT\config.deni")
.Build();

if (master == null)
{
Console.WriteLine("初始化失败");
return;
}

// 订阅事件
master.Events.SlaveStateChanged += (masterIndex, slaveIndex, oldState, newState) =>
Console.WriteLine($"从站 {slaveIndex}: {oldState}{newState}");

master.Events.SlaveOffline += (slaveIndex) =>
Console.WriteLine($"从站 {slaveIndex} 离线");

// 切换到 OP
var (ok, msg) = master.SetState(EcState.OP);
if (!ok)
{
Console.WriteLine($"状态切换失败: {msg}");
master.Close();
return;
}

// 伺服使能
var servo = master.Slaves[0];
if (servo.CoE?.CiA402 != null)
{
servo.CoE.CiA402.OperationMode = ModeCiA402.CSP;
var (enabled, state) = servo.CoE.CiA402.Enable();
if (!enabled) Console.WriteLine($"使能失败: {state.GetDescription()}");
}

// PDO 周期处理
master.Events.ProcessDataCyclicAsync += (masterIndex) =>
{
ref var input = ref servo.PDO.InputsMapping<ServoInput>();
ref var output = ref servo.PDO.OutputsMapping<ServoOutput>();

// 读取当前位置
int currentPos = input.ActualPosition;

// 设置目标位置
output.ControlWord = 0x000F;
output.TargetPosition = 100000;
};

Console.WriteLine("按 Enter 退出...");
Console.ReadLine();
master.Close();

多从站 + 事件驱动

using DarraEtherCAT;

var master = new DarraEtherCAT()
.SetENI(@"C:\EtherCAT\config.deni")
.Build();

if (master == null) return;

// 所有事件
master.Events.StateChanged += (s, e) =>
Console.WriteLine($"主站: {e.OldState}{e.NewState}");

master.Events.EmergencyEvent += (mi, si, err, reg, b1, w1, w2) =>
Console.WriteLine($"从站 {si} 紧急: 0x{err:X4}");

master.Events.PDOFrameLoss += (mi, grp, consecutive, total) =>
Console.WriteLine($"组 {grp} 丢帧: 连续={consecutive}");

// 输入数据变化 — 按从站独立监控
foreach (var slave in master.Slaves)
{
if (slave.PDO.Ibytes > 0)
{
slave.Events.InputChanged += () =>
{
byte[] data = slave.PDO.Inputs;
Console.WriteLine($"从站 {slave.Name} 输入变化: {BitConverter.ToString(data)}");
};
}
}

master.SetState(EcState.OP);

Console.ReadLine();
master.Close();

10. 关键规则速查

规则说明
Build() 检查Build() 返回值可隐式转为 DarraEtherCAT?,失败为 null,必须检查
StructLayoutPDO 结构体必须 [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct
结构体大小输入结构体大小 = slave.PDO.Ibytes,输出 = slave.PDO.Obytes
从站索引master.Slaves 是 0-based,事件回调中 slaveIndex 是 1-based
null 检查slave.CoEslave.CoE.CiA402slave.SoE 等协议实例可能为 null
同步回调ProcessDataCyclicSync 中不能执行 SDO/文件 I/O/Sleep 等阻塞操作
线程安全事件在非 UI 线程触发,更新 UI 需用 Invoke/Dispatcher.Invoke
SDO 类型SDO 写入时需显式指定类型: (ushort)0x0006,避免类型不匹配
资源释放程序退出前必须调用 master.Close()master.Dispose()
EcState枚举值: None, Init, PreOp, Boot, SafeOp, OP

11. API 层级结构速查

DarraEtherCAT                    // 主站 (new + SetENI/SetNetwork + Build)
├── State / SetState() // 状态机 (EcState 枚举)
├── Slaves[n] // 从站列表 (0-based)
│ ├── PDO // PDO 数据
│ │ ├── InputsMapping<T>() // 零拷贝输入 (ref T)
│ │ ├── OutputsMapping<T>() // 零拷贝输出 (ref T)
│ │ ├── Inputs / Outputs // 字节数组
│ │ └── In[n] / Out[n] // 索引访问
│ ├── CoE // CANopen (可能为 null)
│ │ ├── [index][sub].Value // SDO 读写
│ │ ├── CiA402 // 伺服 (可能为 null)
│ │ │ ├── Enable() // 使能
│ │ │ ├── OperationMode // 操作模式
│ │ │ └── StateDrive // 驱动状态
│ │ └── CiA401 // I/O (可能为 null)
│ ├── SoE // SERCOS (可能为 null)
│ ├── FoE // 文件传输 (可能为 null)
│ ├── EoE // 以太网隧道 (可能为 null)
│ ├── AoE // ADS (可能为 null)
│ ├── VoE // 厂商自定义 (可能为 null)
│ ├── FSoE // 功能安全 (可能为 null)
│ └── Events // 从站级事件
│ └── InputChanged // 输入变化 (Action)
├── Events // 主站事件
│ ├── ProcessDataCyclicAsync // PDO 回调 (异步,推荐)
│ ├── ProcessDataCyclicSync // PDO 回调 (同步,慎用)
│ ├── StateChanged // 主站状态变化
│ ├── SlaveStateChanged // 从站状态变化
│ ├── SlaveOffline/Online // 热插拔
│ ├── EmergencyEvent // 紧急消息
│ ├── PDOFrameLoss // 丢帧
│ ├── DCSyncLost // DC 同步丢失
│ ├── InputDataChanged // 输入数据变化
│ └── RedundancyModeChanged // 冗余模式变化
├── Config // 通信配置
├── Diagnostics // 主站诊断
├── Close() / Dispose() // 资源释放
└── 静态方法
├── GetNetworkInfo() // 获取网卡列表
├── ScanSlaves() // 扫描从站
└── QuickSlaveCount() // 快速从站计数

12. 给 AI 的系统提示词模板

以下提示词可直接复制到 AI 对话中使用:

你是一个 DarraEtherCAT C# SDK 专家。请遵守以下规则生成代码:

1. 初始化必须用 Fluent API: new DarraEtherCAT().SetENI("...").Build()
2. Build() 返回值必须检查 null(失败时为 null)
3. PDO 结构体必须用 [StructLayout(LayoutKind.Sequential, Pack = 1)] struct
4. 协议实例 (CoE/CiA402/SoE 等) 使用前必须检查 null
5. master.Slaves 是 0-based 索引,事件中 slaveIndex 是 1-based
6. ProcessDataCyclicSync 回调中禁止 SDO/Sleep/文件操作
7. 事件回调在非 UI 线程,更新 UI 需 Invoke
8. SDO 写入需显式类型: slave.CoE[0x6040][0].Value = (ushort)0x000F
9. 程序退出前必须 master.Close()
10. EcState 枚举: None/Init/PreOp/Boot/SafeOp/OP

完整 API 文档: https://ethercat.darra.xyz/docs/sdk/csharp

更多详细 API 文档请参考 C# SDK 文档