跳到主要内容

从站事件

通过 master.Events() 获取 MasterEvents&,直接赋值 std::function 回调。

事件路由

C++ API 中主站级回调函数包含 masterIndexslaveIndex 参数,由调用方自行过滤到对应从站。

功能概览

类别事件字段说明
从站状态变化SlaveStateChangedEtherCAT 状态变化
紧急消息EmergencyEventCoE Emergency 消息
从站上线SlaveOnline从站上线
从站离线SlaveOffline从站离线
输入数据变化InputDataChanged输入 PDO 数据变化
DC 同步丢失DCSyncLostDC 同步偏差超限
PDO 周期ProcessDataCyclicSync/AsyncPDO 周期回调
PDO 丢帧PDOFrameLossPDO 帧丢失

从站状态变化

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;
}