📢 事件系统
Snet 使用事件驱动架构,包含 3 对事件(共 6 个),定义在 IEvent 接口中,并在 CoreUnify<O, D> 中实现。
3对事件(6个事件)
每个事件都有同步和异步两个版本:
public interface IEvent
{
// 数据事件
event EventHandler<EventDataResult> OnDataEvent;
event EventHandlerAsync<EventDataResult> OnDataEventAsync;
// 信息事件
event EventHandler<EventInfoResult> OnInfoEvent;
event EventHandlerAsync<EventInfoResult> OnInfoEventAsync;
// 语言事件
event EventHandler<EventLanguageResult> OnLanguageEvent;
event EventHandlerAsync<EventLanguageResult> OnLanguageEventAsync;
}
事件类型
OnDataEvent / OnDataEventAsync
当从设备读取数据时触发(通过 ReadAsync 或订阅轮询)。
modbus.OnDataEventAsync += async (sender, e) =>
{
var address = e.GetSource<Address>();
if (address != null)
{
foreach (var detail in address.AddressArray)
{
Console.WriteLine($"[{detail.AddressName}] = {detail.AddressValue?.Value} " +
$"(Quality: {detail.Quality})");
}
}
await Task.CompletedTask;
};
EventDataResult 封装 Address 对象及其所有已填充的 AddressArray 条目。
OnInfoEvent / OnInfoEventAsync
用于信息性消息:连接状态变化、错误、警告、重连尝试。
modbus.OnInfoEventAsync += async (sender, e) =>
{
Console.WriteLine($"[INFO] {e.Message}");
await Task.CompletedTask;
};
EventInfoResult 包含 Message(字符串)和 Status(布尔值)。
OnLanguageEvent / OnLanguageEventAsync
当通过 SetLanguageAsync() 更改语言时触发。传播到所有 CoreUnify 实例。
modbus.OnLanguageEventAsync += async (sender, e) =>
{
Console.WriteLine($"Language changed to: {e.LanguageType}");
// 刷新 UI 标签
await Task.CompletedTask;
};
EventLanguageResult 包含 LanguageType(枚举:zh 或 en)。
EventingWrapperAsync<T>
异步事件在内部使用 EventingWrapperAsync<T>,这是一个提供线程安全异步事件调用的结构体:
// 内部实现(简化版)
private EventingWrapperAsync<EventDataResult> OnDataEventWrapperAsync;
// 触发事件(在 CoreUnify 内部)
protected void OnDataEventHandler(object? sender, EventDataResult e)
{
OnDataEvent?.Invoke(sender, e); // 同步订阅者
_ = OnDataEventWrapperAsync.InvokeAsync(sender, e); // 异步订阅者
}
同步事件先触发,然后异步包装器调用所有异步订阅者。这确保同步和异步处理程序都能收到事件,无论它们订阅了哪个事件。
订阅和取消订阅
标准的 C# 事件模式:
// 订阅
modbus.OnDataEventAsync += MyHandler;
modbus.OnInfoEvent += MyInfoHandler;
// 取消订阅
modbus.OnDataEventAsync -= MyHandler;
modbus.OnInfoEvent -= MyInfoHandler;
始终在释放之前取消订阅以防止内存泄漏:
modbus.OnDataEventAsync -= MyHandler;
await modbus.DisposeAsync();
事件流程图
设备读取完成
|
v
字节解析为 AddressArray(包含 AddressValue + Quality)
|
v
创建 EventDataResult
|
+---> OnDataEvent?.Invoke(sender, result) [同步]
+---> OnDataEventWrapperAsync.InvokeAsync(...) [异步]
|
+---> Handler1(sender, result)
+---> Handler2(sender, result)
+---> HandlerN(sender, result)
语言事件传播
与每个实例独立的数据/信息事件不同,语言事件通过静态 LanguageHandler 传播:
// 在一个实例中设置语言会触发所有实例中的 OnLanguageEvent
await modbus.SetLanguageAsync(LanguageType.en);
// 每个已注册的 CoreUnify 实例都会触发:
// OnLanguageEvent?.Invoke(sender, languageResult)
// OnLanguageEventWrapperAsync.InvokeAsync(sender, languageResult)
这确保所有已连接的设备、UI 组件和日志记录器同时切换语言。
最佳实践
- 优先使用异步处理程序 -- 使用
OnDataEventAsync以避免阻塞 I/O 线程 - 保持处理程序快速 -- 不要在事件处理程序中执行繁重工作;将工作卸载到
Channel - 释放时取消订阅 -- 在调用
DisposeAsync()之前移除处理程序 - 检查 sender -- 在多设备场景中,检查
sender以识别哪个设备触发了事件 - 使用
e.GetSource<T>()-- 通过GetSource<T>()扩展方法从EventDataResult中提取类型化数据
