从站事件
通过 master.Events() 获取 MasterEvents&,用 On*() / SetPDOCallback*() 注册回调。
用 On*() 注册, 不是字段赋值
MasterEvents 每个事件是多订阅者 std::vector<std::function>, 必须调 On*() 注册方法,
不能 = 赋值。注册方法映射见 事件 - 多订阅者模型。
事件路由
C++ API 中主站级回调函数包含 masterIndex 和 slaveIndex 参数,由调用方自行过滤到对应从站。
功能概览
| 类别 | 注册方法 | 说明 |
|---|---|---|
| 从站状态变化 | OnSlaveStateChanged(cb) | EtherCAT 状态变化 |
| 紧急消息 | OnEmergency(cb) | CoE Emergency 消息 |
| 从站上线 | OnSlaveDiscovered(cb) | 从站上线 |
| 从站离线 | OnSlaveLost(cb) | 从站离线 |
| DC 同步丢失 | OnDCSyncLost(cb) | DC 同步偏差超限 |
| PDO 周期 | SetPDOCallbackSync(cb) | PDO 周期回调 (同步, 供 FSoE 等实时控制) |
| PDO 丢帧 | OnPDOFrameLoss(cb) | PDO 帧丢失 |
从站状态变化
master.Events().OnSlaveStateChanged([](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().OnEmergency([](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().OnSlaveDiscovered([](uint16_t si) {
printf("[从站%d] 上线\n", si);
});
master.Events().OnSlaveLost([](uint16_t si) {
printf("[从站%d] 离线\n", si);
});
输入数据变化
InputDataChanged 事件已移除
旧 InputDataChanged 事件靠 SDK 层每周期逐字节检测输入 PDO 变化。PDO 数据通路改为纯内核 RT 收发 + 内核共享内存指针零拷贝后,该机制不再驱动。需要感知输入变化时,请自行轮询过程映像指针并比对上一周期快照。
轮询过程映像检测变化
PDO 输入数据为零拷贝共享内存指针,直接轮询并与上一周期快照比对即可:
#pragma pack(push, 1)
struct ServoInput {
uint16_t statusWord;
int32_t actualPosition;
int32_t actualVelocity;
};
#pragma pack(pop)
auto& slave = master.GetSlave(1);
ServoInput prev{};
while (running) {
const auto* input = static_cast<const ServoInput*>(slave.InputDataPointer());
if (input && std::memcmp(input, &prev, sizeof(ServoInput)) != 0) {
prev = *input;
printf("位置变化: %d\n", input->actualPosition);
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
DC 同步丢失
master.Events().OnDCSyncLost([](uint16_t mi, uint16_t si, int diffNs) {
printf("[从站%d] DC 同步丢失: 偏差 %dns\n", si, diffNs);
});
PDO 周期回调
master.Events().SetPDOCallbackSync([&](uint16_t mi) {
auto& servo = master.GetSlave(1);
void* output = servo.OutputDataPointer();
void* input = servo.InputDataPointer();
// 零拷贝读写 PDO 数据
});
PDO 丢帧
master.Events().OnPDOFrameLoss([](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().SetPDOCallbackSync([&](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: 队列(适合复杂数据)— 同步 PDO 回调中入队
std::queue<ServoInput> dataQueue;
std::mutex queueMutex;
master.Events().SetPDOCallbackSync([&](uint16_t mi) {
auto& slave = master.GetSlave(1);
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();
// ===== 从站状态事件 (用 On*() 注册) =====
ev.OnSlaveStateChanged([](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.OnEmergency([](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.OnSlaveDiscovered([](uint16_t si) {
printf("[从站%d] 上线\n", si);
});
ev.OnSlaveLost([](uint16_t si) {
printf("[从站%d] 离线\n", si);
});
ev.OnDCSyncLost([](uint16_t mi, uint16_t si, int diff) {
printf("[从站%d] DC 同步丢失: %dns\n", si, diff);
});
// ===== PDO 周期回调 (供 FSoE 等实时控制; 读输入数据通常直接轮询过程映像指针) =====
ev.SetPDOCallbackSync([&](uint16_t mi) {
// 零拷贝读写 PDO 数据
auto& servo = master.GetSlave(1);
void* output = servo.OutputDataPointer();
const auto* input = static_cast<const ServoInput*>(servo.InputDataPointer());
if (input)
printf("[从站1] 位置: %d\n", input->actualPosition);
});
// ===== PDO 丢帧 =====
ev.OnPDOFrameLoss([](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;
}