IDaq 接口 - Snet Docs

📘 IDaq 接口

命名空间: Snet.Model.@interface | 接口: IDaq | 成员: 15 个组件接口 + IDisposable + IAsyncDisposable

IDaq 是所有协议驱动和中间件组件实现的顶层接口。它将 15 个不同的能力接口组合成一个统一的契约,用于操作数据采集和消息组件。

接口层次结构

IDaq 继承自以下 15 个组件接口以及 IDisposableIAsyncDisposable

接口 用途 关键方法
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
}

参见