虚拟通道
虚拟通道是没有真实硬件时用来联调的通道。它不占用 COM 口,也不连接网络,但状态、写入、接收事件都和真实通道使用同一套 API。
最小用法
await using var app = ZeusHost.Create(builder =>
{
builder.AddVirtualChannel("meter");
});
var meter = app.Channels.Get("meter");
meter.DataReceived += (_, e) =>
{
Console.WriteLine(Convert.ToHexString(e.Data.Span));
};
await app.StartAsync();
await meter.WriteAsync(new byte[] { 0x01, 0x02 });
默认虚拟通道会回显写入内容。你写入 01 02,就会收到 01 02。
它解决什么问题
| 没有虚拟通道时 | 使用虚拟通道后 |
|---|---|
| 必须等仪表到货才能写界面 | 先把界面、按钮、绑定和日志写完 |
| 单元测试依赖本机 COM 口 | 测试可以在任意机器和 CI 上跑 |
| 不确定 bug 在硬件还是程序 | 先用虚拟通道确认程序结构 |
| 演示时必须带设备 | 可以直接演示收发流程 |
从虚拟通道换到真实串口
先写成:
builder.AddVirtualChannel("meter");
等界面和业务流程跑通后,再改成:
builder.AddSerialPort("meter", "COM3", 9600);
后面的代码不变:
var meter = app.Channels.Get("meter");
await meter.WriteAsync(payload);
这能帮你把问题分开:如果虚拟通道能跑、真实串口不能跑,大概率是端口、波特率、接线或设备协议问题。
模拟一个从站
虚拟通道也可以挂一个应答器,不只是简单回显。比如模拟 Modbus 从站:
var memory = new ModbusSlaveMemory();
memory.HoldingRegisters[0] = 185;
builder.AddVirtualChannel(
"bus",
new ModbusSlaveResponder(1, ModbusTransport.Rtu, memory));
这样你的主站代码可以先读到虚拟寄存器,等真实仪表到位后再把 AddVirtualChannel 换成 AddSerialPort。
自定义应答器
如果你的私有协议不是 Modbus,可以实现 IVirtualResponder:
public sealed class EchoWithAckResponder : IVirtualResponder
{
public ReadOnlyMemory<byte>? Respond(ReadOnlyMemory<byte> request)
{
return new byte[] { 0x06 }; // 例如返回 ACK
}
}
注册:
builder.AddVirtualChannel("meter", new EchoWithAckResponder());
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 收不到回显 | 没启动宿主,或写入前没订阅事件 | 先 StartAsync,再 WriteAsync;事件最好在启动前订阅 |
状态不是 Open | 宿主还没启动 | 查看是否调用了 await app.StartAsync() |
| 想模拟慢速设备 | 当前内置虚拟通道是立即应答 | 在自定义 IVirtualResponder 外层自行加入业务等待逻辑 |
| 换真实串口后行为不同 | 真实设备不会自动回显 | 按设备手册发送正确协议帧 |