日志
通过 EtherCATMaster 内部的 CallbackStorage 管理日志回调。DLL 底层日志系统自动记录所有事件,Build() 时自动注册。
快速开始
using namespace darra;
EtherCATMaster master(dll);
master.SetNetwork("\\Device\\NPF_{...}");
master.Build();
// 事件驱动的日志输出
master.Events().LogEvent = [](uint16_t mi, int category, const char* msg) {
printf("[%d] %s\n", category, msg);
};
// 启用调试日志(默认关闭)
dll.SetDebugLogging(TRUE);
// 只看错误和警告(通过过滤)
dll.SetLogFilter(mi, LOG_ERROR, TRUE);
dll.SetLogFilter(mi, LOG_WARNING, TRUE);
日志类别
enum LogCategory {
LOG_ERROR = 0, // 系统错误和异常
LOG_WARNING = 1, // 潜在问题
LOG_MESSAGE = 2, // 一般信息(状态切换等)
LOG_MAILBOX = 3, // CoE/SoE/FoE/EoE 邮箱通信(默认关闭)
LOG_PDO = 4, // PDO 数据变化日志(默认关闭)
LOG_DEBUG = 5, // 详细调试信息(默认关闭)
LOG_LOCAL = 6 // 控制台输出捕获
};
| 值 | 常量 | 说明 | 默认状态 |
|---|---|---|---|
| 0 | LOG_ERROR | 系统错误和异常 | 启用 |
| 1 | LOG_WARNING | 潜在问题 | 启用 |
| 2 | LOG_MESSAGE | 一般信息(状态切换等) | 启用 |
| 3 | LOG_MAILBOX | CoE/SoE/FoE/EoE 邮箱通信 | 关闭 |
| 4 | LOG_PDO | PDO 数据变化日志 | 关闭 |
| 5 | LOG_DEBUG | 详细调试信息 | 关闭 |
| 6 | LOG_LOCAL | 控制台输出捕获 | 启用 |
PDO 日志
PDO 日志只在 PDO 数据发生变化时输出,不会每个周期都产生日志。适合调试 PDO 映射问题。
容量与保留策略
日志列表设有上限,超出时自动移除最旧的条目。
自动保留模式: 进入 SafeOp/OP 状态后,日志自动切换为保留模式 -- 运行时事件(从站离线、PDO 丢帧、DC 同步丢失等)不会被后续操作清空。退出 SafeOp/OP 后恢复为普通模式。
日志开关
Mailbox、PDO、Debug 三类日志默认关闭,需要时手动启用。
// 启用 PDO 日志(仅在 PDO 数据变化时输出,影响实时性能,慎用)
dll.SetPDOLogging(TRUE);
// 启用邮箱日志(CoE/SoE/FoE/EoE 邮箱通信日志)
dll.SetMailboxLogging(TRUE);
// 启用调试日志(详细调试信息)
dll.SetDebugLogging(TRUE);
过滤
SetLogFilter()
void SetLogFilter(uint16_t master, int category, bool enabled);
设置日志过滤器,控制指定类别的日志是否显示。
参数:
master(uint16_t) — 主站索引category(int) — 日志类别 (LogCategory)enabled(bool) — 是否显示
示例:
// 只看错误和警告
dll.SetLogFilter(mi, LOG_ERROR, true);
dll.SetLogFilter(mi, LOG_WARNING, true);
dll.SetLogFilter(mi, LOG_MESSAGE, false);
GetAllLogs()
std::vector<LogEntry> GetAllLogs(uint16_t master);
获取所有日志条目(无视过滤器)。用于导出或自定义过滤。
线程安全
日志回调在非 UI 线程上触发。如需在回调中访问共享资源,请使用互斥锁:
#include <mutex>
#include <fstream>
std::ofstream log_file("ethercat.log");
std::mutex log_mutex;
master.Events().LogEvent = [&](uint16_t mi, int category, const char* msg) {
std::lock_guard<std::mutex> lock(log_mutex);
log_file << "[" << category << "] " << msg << std::endl;
};
完整示例
#include "ethercat.hpp"
#include <cstdio>
#include <mutex>
#include <fstream>
using namespace darra;
int main() {
// 1. 启用调试和邮箱日志
dll.SetDebugLogging(TRUE);
dll.SetMailboxLogging(TRUE);
try {
EtherCATMaster master(dll);
master.SetNetwork("\\Device\\NPF_{...}");
master.Build();
// 2. 日志回调(实时输出所有日志)
master.Events().LogEvent = [](uint16_t mi, int category, const char* msg) {
const char* labels[] = {"ERROR", "WARN", "MSG", "MBOX", "PDO", "DBG", "LOCAL"};
const char* label = (category >= 0 && category <= 6) ? labels[category] : "?";
printf("[%s] %s\n", label, msg);
};
// 3. 事件驱动的状态监控
master.Events().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());
};
master.Events().EmergencyEvent = [](uint16_t mi, uint16_t si,
uint16_t code, uint16_t reg,
uint8_t b1, uint16_t w1, uint16_t w2) {
printf("[紧急] 从站 %d: 0x%04X\n", si, code);
};
master.SetState(EcState::OP);
master.Start();
printf("运行中... 按 Enter 停止\n");
getchar();
} catch (const ethercat::DarraException& e) {
printf("错误: %s\n", e.what());
return -1;
}
return 0;
}
多线程 UI 绑定
日志回调在非 UI 线程触发。在 Qt、MFC 等 UI 框架中更新控件时,需要通过消息队列或信号槽同步到 UI 线程:
// Qt 示例
master.Events().LogEvent = [this](uint16_t mi, int cat, const char* msg) {
QString text = QString("[%1] %2").arg(cat).arg(msg);
QMetaObject::invokeMethod(this, [=]() {
ui->logList->addItem(text);
}, Qt::QueuedConnection);
};