跳到主要内容

日志

通过 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 // 控制台输出捕获
};
常量说明默认状态
0LOG_ERROR系统错误和异常启用
1LOG_WARNING潜在问题启用
2LOG_MESSAGE一般信息(状态切换等)启用
3LOG_MAILBOXCoE/SoE/FoE/EoE 邮箱通信关闭
4LOG_PDOPDO 数据变化日志关闭
5LOG_DEBUG详细调试信息关闭
6LOG_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);
};