跳到主要内容

通信报文追踪

每个 IChannel 都会在收发原始字节时触发 PacketTraced。它不解析协议,只记录通道实际提交的 TX 字节和底层收到的 RX 字节,适合现场调试、通信窗口和故障快照。

直接订阅事件​

var meter = app.Channels.Get("meter");
meter.PacketTraced += (_, e) =>
{
var direction = e.Direction == ChannelTraceDirection.Sent ? "TX" : "RX";
Console.WriteLine($"{e.Timestamp:O} {meter.Name} {direction} {e.Hex}");
};

Hex 是连续大写十六进制字符串。如果需要带空格的显示,可以自己格式化 e.Data.Span。

保留最近 N 条​

如果只想保留最近一段通信记录,使用 ChannelTraceBuffer:

var meter = app.Channels.Get("meter");
using var trace = new ChannelTraceBuffer(meter, capacity: 200);

await app.StartAsync();
await meter.WriteAsync(new byte[] { 0x01, 0x03, 0x00, 0x00 });

foreach (var entry in trace.Entries)
{
Console.WriteLine($"{entry.Timestamp:HH:mm:ss.fff} {entry.ChannelName} {entry.Direction} {entry.Hex}");
}

Entries 返回的是快照,顺序从旧到新。容量满后会自动丢弃最旧记录。

写入 ILogger​

宿主级写法是 AddCommunicationLogging:给已有和后续新增的通道自动挂报文日志,热重载重建同名通道时会退订旧实例再挂到新实例。默认级别是 Debug,事件编号是 ZeusLogEvents.PacketTrace。

await using var app = ZeusHost.Create(builder =>
{
builder.AddVirtualChannel("meter");
builder.AddCommunicationLogging();
});

如果只想追踪某一条通道,或只在排障窗口打开时记录,仍可手写 ChannelTraceLogger:

var meter = app.Channels.Get("meter");
var logger = app.Services.GetRequiredService<ILoggerFactory>().CreateLogger("comm.trace");

using var traceLog = new ChannelTraceLogger(meter, logger);
await app.StartAsync();
await meter.WriteAsync(new byte[] { 0x01, 0x03, 0x00, 0x00 });

日志模板包含结构化字段:Channel、Direction、ByteCount、Hex。关闭窗口时释放句柄即可退订。

需要写入文件、数据库或抓包系统时,订阅 PacketTraced 或接入 ILogger sink 自己决定格式、路径、轮转和留存策略。Zeus 不再内置文件追踪器,避免框架替应用承担存储职责。

和 DataReceived 的区别​

API用途
DataReceived业务或协议层消费收到的数据
PacketTraced诊断层记录发送和接收的原始字节
ChannelTraceBuffer在内存中滚动保留最近 N 条追踪记录
ChannelTraceLogger把后续追踪记录写入 ILogger 结构化日志
AddCommunicationLogging给全部通道自动挂 ChannelTraceLogger,热重载后跟着迁

这些能力可以同时使用。PacketTraced 和 DataReceived 可能在 IO 线程触发;WinForms 更新控件时用控件绑定或 BeginInvoke,WPF 则把数据交给 ViewModel 并通过 WpfUiDispatcher 封送。

下一步:看 虚拟通道 快速模拟收发,或看 自定义帧 在追踪之上做协议解析。