从站事件
通过 master.Events() 获取 MasterEvents&,直接赋值 std::function 回调。
事件路由
C++ API 中主站级回调函数包含 masterIndex 和 slaveIndex 参数,由调用方自行过滤到对应从站。
功能概览
| 类别 | 事件字段 | 说明 |
|---|---|---|
| 从站状态变化 | SlaveStateChanged | EtherCAT 状态变化 |
| 紧急消息 | EmergencyEvent | CoE Emergency 消息 |
| 从站上线 | SlaveOnline | 从站上线 |
| 从站离线 | SlaveOffline | 从站离线 |
| 输入数据变化 | InputDataChanged | 输入 PDO 数据变化 |
| DC 同步丢失 | DCSyncLost | DC 同步偏差超限 |
| PDO 周期 | ProcessDataCyclicSync/Async | PDO 周期回调 |
| PDO 丢帧 | PDOFrameLoss | PDO 帧丢失 |
从站状态变化
master.Events().SlaveStateChanged = [](uint16_t mi, uint16_t si,
EcState oldState, EcState newState) {
printf("[从站%d] 状态: %s -> %s\n", si,
EcStateFormat::Format(oldState).c_str(),
EcStateFormat::Format(newState).c_str());
};
紧急消息
master.Events().EmergencyEvent = [](uint16_t mi, uint16_t si,
uint16_t errorCode, uint16_t errorReg,
uint8_t b1, uint16_t w1, uint16_t w2) {
printf("[从站%d] 紧急消息: 错误码=0x%04X\n", si, errorCode);
};
从站上线/离线
master.Events().SlaveOnline = [](uint16_t si) {
printf("[从站%d] 上线\n", si);
};
master.Events().SlaveOffline = [](uint16_t si) {
printf("[从站%d] 离线\n", si);
};
输入数据变化
master.Events().InputDataChanged = [](uint16_t mi, uint16_t si) {
printf("[从站%d] 输入数据变化\n", si);
};
零开销
- 无变化时:不触发回调,零性能开销
- 有变化时:仅变化的从站收到回调,其他从站不受影响
与 PDO 结构体访问的配合
InputDataChanged 仅通知"数据变了",需自行读取数据。可在回调中通过零拷贝指针访问结构体:
#pragma pack(push, 1)
struct ServoInput {
uint16_t statusWord;
int32_t actualPosition;
int32_t actualVelocity;
};
#pragma pack(pop)
master.Events().InputDataChanged = [&](uint16_t mi, uint16_t si) {
if (si != 1) return; // 过滤到目标从站
auto& slave = master.GetSlave(si);
const auto* input = static_cast<const ServoInput*>(slave.InputDataPointer());
if (input) {
printf("位置变化: %d\n", input->actualPosition);
}
};
DC 同步丢失
master.Events().DCSyncLost = [](uint16_t mi, uint16_t si, int diffNs) {
printf("[从站%d] DC 同步丢失: 偏差 %dns\n", si, diffNs);
};
PDO 周期回调
master.Events().ProcessDataCyclicSync = [&](uint16_t mi) {
auto& servo = master.GetSlave(1);
void* output = servo.OutputDataPointer();
void* input = servo.InputDataPointer();
// 零拷贝读写 PDO 数据
};
PDO 丢帧
master.Events().PDOFrameLoss = [](uint16_t mi, uint8_t group,
uint32_t consecutive, uint32_t total) {
printf("组%d 丢帧: 连续=%u, 总计=%u\n", group, consecutive, total);
};
线程安全
事件回调在 PDO 线程上触发,非主线程。更新共享数据时需要同步:
#include <atomic>
#include <queue>
#include <mutex>
// 方案 1: 原子变量(适合简单数据)
std::atomic<int32_t> shared_position{0};
master.Events().ProcessDataCyclicSync = [&](uint16_t mi) {
auto& slave = master.GetSlave(1);
uint8_t buf[64];
int bytes = slave.ReadInputDirect(buf, sizeof(buf));
if (bytes >= 6) {
int32_t pos;
std::memcpy(&pos, buf + 2, 4);
shared_position.store(pos, std::memory_order_release);
}
};
// 方案 2: 队列(适合复杂数据)
std::queue<ServoInput> dataQueue;
std::mutex queueMutex;
master.Events().InputDataChanged = [&](uint16_t mi, uint16_t si) {
if (si != 1) return;
auto& slave = master.GetSlave(si);
const auto* input = static_cast<const ServoInput*>(slave.InputDataPointer());
if (input) {
std::lock_guard<std::mutex> lock(queueMutex);
dataQueue.push(*input); // 快速入队
}
};
注意
回调中避免执行耗时操作。如需处理大量数据,将数据放入队列异步处理。
完整示例
#include "ethercat.hpp"
#include <cstdio>
#include <atomic>
using namespace darra;
#pragma pack(push, 1)
struct ServoInput {
uint16_t statusWord;
int32_t actualPosition;
int32_t actualVelocity;
};
#pragma pack(pop)
int main() {
EtherCATMaster master(dll);
master.SetNetwork("\\Device\\NPF_{...}")
.SetENI("config.deni")
.Build();
auto& ev = master.Events();
// ===== 从站状态事件 =====
ev.SlaveStateChanged = [](uint16_t mi, uint16_t si,
EcState o, EcState n) {
printf("[从站%d] 状态: %s -> %s\n", si,
EcStateFormat::Format(o).c_str(),
EcStateFormat::Format(n).c_str());
};
ev.EmergencyEvent = [](uint16_t mi, uint16_t si,
uint16_t ec, uint16_t er,
uint8_t b1, uint16_t w1, uint16_t w2) {
printf("[从站%d] 紧急消息: 0x%04X\n", si, ec);
};
ev.SlaveOnline = [](uint16_t si) {
printf("[从站%d] 上线\n", si);
};
ev.SlaveOffline = [](uint16_t si) {
printf("[从站%d] 离线\n", si);
};
ev.DCSyncLost = [](uint16_t mi, uint16_t si, int diff) {
printf("[从站%d] DC 同步丢失: %dns\n", si, diff);
};
// ===== 输入数据变化 =====
ev.InputDataChanged = [&](uint16_t mi, uint16_t si) {
auto& slave = master.GetSlave(si);
const auto* input = static_cast<const ServoInput*>(slave.InputDataPointer());
if (input)
printf("[从站%d] 位置: %d\n", si, input->actualPosition);
};
// ===== PDO 周期回调 =====
ev.ProcessDataCyclicSync = [&](uint16_t mi) {
// 零拷贝读写 PDO 数据
auto& servo = master.GetSlave(1);
void* output = servo.OutputDataPointer();
void* input = servo.InputDataPointer();
};
// ===== PDO 丢帧 =====
ev.PDOFrameLoss = [](uint16_t mi, uint8_t group,
uint32_t consecutive, uint32_t total) {
printf("组%d 丢帧: 连续=%u, 总计=%u\n", group, consecutive, total);
};
master.SetState(EcState::OP);
master.Start();
getchar();
return 0;
}