📘 IDaq 接口
命名空间: Snet.Model.@interface | 接口: IDaq | 成员: 15 个组件接口 + IDisposable + IAsyncDisposable
IDaq 是所有协议驱动和中间件组件实现的顶层接口。它将 15 个不同的能力接口组合成一个统一的契约,用于操作数据采集和消息组件。
接口层次结构
IDaq 继承自以下 15 个组件接口以及 IDisposable、IAsyncDisposable:
| 接口 | 用途 | 关键方法 |
|---|---|---|
IOn |
连接 / 启动组件 | OnAsync() |
IOff |
断开 / 停止组件 | OffAsync() |
IRead |
从设备或数据源读取数据 | ReadAsync(Address)(多地址通过 Address.AddressArray) |
IWrite |
向设备或目标写入数据 | WriteAsync(ConcurrentDictionary<string, object>), WriteAsync(ConcurrentDictionary<string, (object value, EncodingType?)>), WriteAsync(ConcurrentDictionary<string, WriteModel>) |
ISubscribe |
订阅数据变更通知 | SubscribeAsync(address), UnSubscribeAsync(address) |
IStatus |
获取组件状态 | GetStatusAsync() |
IEvent |
事件注册与分发 | OnDataEvent, OnInfoEvent, OnLanguageEvent 及其异步对应方法 |
IArgs |
获取组件参数 | GetArgsAsync() |
IInstance |
创建实例(单例池) | CreateInstance<T>(param), CreateInstanceAsync<T>(param) |
ILog |
日志配置 | LogOperateSet(), LogOperateSetAsync(), LogOperateGet(), LogOperateGetAsync() |
IWA |
Web API 控制 | WAOnAsync(), WAOffAsync(), WAStatusAsync(), WARequestExampleAsync() |
IObject |
获取基础对象 | GetBaseObject(), GetBaseObjectAsync() |
ILanguage |
多语言支持 | SetLanguageAsync(LanguageType), GetLanguageAsync() |
IClone |
深度克隆(不注册单例) | CloneThis(), CloneThisAsync() |
IPacker |
地址自动组包 / 解包 | Packer(), PackerAsync(), UnPacker(), UnPackerAsync() |
IDisposable |
资源清理 | Dispose() |
IAsyncDisposable |
异步资源清理 | DisposeAsync() |
快速入门
using Snet.Core;
// 假设使用任意 IDaq 实现(如 Modbus、Siemens、MQTT)。
// 使用单例池工厂;返回的具体类型实现 IDaq。
IDaq daq = await ModbusOperate.InstanceAsync(new ModbusData.Basics
{
IpAddress = "192.168.1.100",
Port = 502,
Station = 1,
AddressStartWithZero = false
});
// 连接
await daq.OnAsync();
// 从设备读取(入参为 Address 对象)
var address = new Address(new AddressDetails("温度", DataType.Float));
var result = await daq.ReadAsync(address);
if (result.Status)
{
var data = result.GetSource<ConcurrentDictionary<string, AddressValue>>();
foreach (var kv in data)
Console.WriteLine($"{kv.Key} = {kv.Value.ResultValue}");
}
// 向设备写入
await daq.WriteAsync(new ConcurrentDictionary<string, object> { ["1"] = 1234f });
// 先绑定事件,再订阅
daq.OnDataEventAsync += async (sender, e) =>
{
if (e.Status)
Console.WriteLine($"接收到数据: {e.ResultData}");
};
// 订阅持续的数据更新
var subAddr = new Address(new AddressDetails("温度", DataType.Float));
await daq.SubscribeAsync(subAddr);
// 切换语言
await daq.SetLanguageAsync(LanguageType.zh);
// 断开连接
await daq.OffAsync();
// 清理资源
daq.Dispose();
核心操作
连接管理
// 连接 -- 必须在任何读/写/订阅操作之前调用
await daq.OnAsync();
// 断开连接 -- 释放连接和资源
await daq.OffAsync();
读取数据
// ReadAsync 入参为 Address 对象,返回 OperateResult
var address = new Address(new List<AddressDetails>
{
new("温度", DataType.Float),
new("压力", DataType.Float)
});
var result = await daq.ReadAsync(address);
if (result.Status)
{
var data = result.GetSource<ConcurrentDictionary<string, AddressValue>>();
foreach (var kv in data)
Console.WriteLine($"{kv.Key} = {kv.Value.ResultValue}");
}
写入数据
// 主要签名:元组形式(子类实现的抽象方法)
await daq.WriteAsync(new ConcurrentDictionary<string, (object value, EncodingType? encodingType)>
{
["1"] = ((short)100, EncodingType.ANSI),
["2"] = (25.6f, null)
});
// 便捷形式 1:自动 ASCII 编码
await daq.WriteAsync(new ConcurrentDictionary<string, object>
{
["1"] = (short)100,
["2"] = 25.6f
});
// 便捷形式 2:WriteModel 对象(含 DataType 做精确类型转换)
await daq.WriteAsync(new ConcurrentDictionary<string, WriteModel>
{
["1"] = new WriteModel((short)100, DataType.Int16),
["2"] = new WriteModel(25.6f, DataType.Float, encodingType: EncodingType.UTF8)
});
订阅数据
// 开始监视一个地址(参数为 Address 对象,非字符串)
await daq.SubscribeAsync(new Address(new AddressDetails("温度", DataType.Float)));
// 数据通过事件到达
daq.OnDataEventAsync += async (sender, e) =>
{
if (e.Status)
Console.WriteLine($"[{e.Time}] {e.Message}");
};
// 停止监视
await daq.UnSubscribeAsync(new Address(new AddressDetails("温度", DataType.Float)));
地址自动组包与解包
IDaq 继承 IPacker,因此每个数采驱动都能直接组包、解包,无需额外装配处理器:
// 组包:将分散地址合并为最少的批量读取
// protocolTypeKey = 供应商 ProtocolType 枚举名,如 "SiemensS7Net_S1200"、"ModbusTcpNet"
var address = new Address(new List<AddressDetails>
{
new("温度", DataType.Float),
new("压力", DataType.Float)
});
var packed = await daq.PackerAsync(address, "SiemensS7Net_S1200");
if (!packed.Status) return; // 组包失败 / 协议不支持
var packedAddr = packed.GetSource<Address>(); // ResultData = 优化后的 Address
// 一次往返读取组包后的批次
var result = await daq.ReadAsync(packedAddr);
if (!result.Status) return; // 读取失败
var data = result.GetSource<ConcurrentDictionary<string, AddressValue>>();
// 解包:将每个批次的原始字节解析回各地址的值
var unpacked = await daq.UnPackerAsync(data);
if (!unpacked.Status) return; // 解包失败
var values = unpacked.GetSource<List<ConcurrentDictionary<string, AddressValue>>>();
- 协议族不支持组包时(如符号/标签寻址),
Packer原样返回地址——永不抛异常,对有效输入永不返回null。 UnPacker(AddressValue)解包单个批次;UnPacker(ConcurrentDictionary<string, AddressValue>)解包一次读取结果的全部批次,返回List<ConcurrentDictionary<string, AddressValue>>。- 同步(
Packer/UnPacker)与带CancellationToken的异步变体均可用。全部 4 个UnPacker重载都接受bool isStringReverseByteWord = false——String解包是否按 16 位字反转字节(连接级配置,如欧姆龙 FINS)。
完整 API 参考:IPacker 接口。引擎层细节:地址自动组包。
事件
IDaq 提供 3 对事件(同步和异步):
| 事件 | 描述 |
|---|---|
OnDataEvent / OnDataEventAsync |
当从读取或订阅接收到数据时触发 |
OnInfoEvent / OnInfoEventAsync |
当状态变更、警告和信息消息时触发 |
OnLanguageEvent / OnLanguageEventAsync |
当语言/区域设置变更时触发 |
// 注册事件处理器
daq.OnDataEventAsync += HandleDataAsync;
daq.OnInfoEventAsync += HandleInfoAsync;
daq.OnLanguageEventAsync += HandleLanguageAsync;
async Task HandleDataAsync(object? sender, EventDataResult e)
{
// Process incoming data via e.ResultData
}
async Task HandleInfoAsync(object? sender, EventInfoResult e)
{
// Process status messages via e.Message
}
async Task HandleLanguageAsync(object? sender, EventLanguageResult e)
{
// Handle language change notifications via e.Language
}
