DaqAbstract 基类 - Snet Docs

📡 DaqAbstract 基类

DaqAbstract<O, D> 是所有数据采集协议实现的基类。它继承自 CoreUnify<O, D> 并实现 IDaq

public abstract class DaqAbstract<O, D> : CoreUnify<O, D>, IDaq
    where O : class
    where D : class

8个抽象异步方法

每个协议都必须实现这些方法。它们构成了 DAQ 设备的完整生命周期:

1. OnAsync -- 打开连接

public abstract Task<OperateResult> OnAsync(CancellationToken token = default);

建立与设备的物理/逻辑连接。对于 Modbus TCP,此操作打开一个 Socket。对于串行协议,此操作打开 COM 端口。

2. OffAsync -- 关闭连接

public abstract Task<OperateResult> OffAsync(bool hardClose = false, CancellationToken token = default);

关闭连接。hardClose = true 执行强制关闭;false 尝试优雅关闭。

3. ReadAsync -- 读取数据

public abstract Task<OperateResult> ReadAsync(Address address, CancellationToken token = default);

核心数据检索方法。接收一个包含一个或多个 AddressDetailsAddress 对象。返回带有已解析数据的 OperateResult,数据存放在 ResultData 中。

4. WriteAsync -- 写入数据

// 抽象方法(子类必须实现)
public abstract Task<OperateResult> WriteAsync(
    ConcurrentDictionary<string, (object value, EncodingType? encodingType)> values,
    CancellationToken token = default);

// 便捷重载 1:自动推导编码(非抽象,委托给抽象方法)
public Task<OperateResult> WriteAsync(
    ConcurrentDictionary<string, object> values,
    CancellationToken token = default);

// 便捷重载 2:WriteModel 类型转换(非抽象,委托给抽象方法)
public Task<OperateResult> WriteAsync(
    ConcurrentDictionary<string, WriteModel> values,
    CancellationToken token = default);

将值写入设备地址。抽象方法以 (object value, EncodingType? encodingType) 元组为核心参数形式,子类只实现这一个抽象方法即可。另外两个重载是便捷方法:ConcurrentDictionary&lt;string, object&gt; 版本自动赋予 ASCII 编码后委托给抽象方法;ConcurrentDictionary&lt;string, WriteModel&gt; 版本根据 WriteModel.AddressDataType 逐项类型转换后委托。

5. SubscribeAsync -- 开始订阅

public abstract Task<OperateResult> SubscribeAsync(Address address, CancellationToken token = default);

注册地址以进行连续轮询。已订阅的地址在每个轮询周期触发 OnDataEvent/OnDataEventAsync

6. UnSubscribeAsync -- 停止订阅

public abstract Task<OperateResult> UnSubscribeAsync(Address address, CancellationToken token = default);

从订阅列表中移除地址。

7. GetStatusAsync -- 设备状态

public abstract Task<OperateResult> GetStatusAsync(CancellationToken token = default);

返回设备的当前连接和运行状态。

8. GetBaseObjectAsync -- 底层对象

public abstract Task<OperateResult> GetBaseObjectAsync(CancellationToken token = default);

返回底层的通信或驱动对象。适用于高级场景。


3个必需属性(来自 CoreUnify)

重写这些属性以便为 GetParamAsync() 提供元数据:

protected virtual string CN { get; }   // 中文显示名称
protected virtual string CD { get; }   // 中文描述
protected virtual List<propertie> AP { get; }  // 附加自定义属性

6个内置 WebAPI 端点

每个 DaqAbstract 子类通过 HttpServiceOperate 自动暴露 REST API:

端点 路由 请求体
打开 /api/on
关闭 /api/off
读取 /api/read Address JSON
写入 /api/write ConcurrentDictionary<string, WriteModel> JSON
状态 /api/getstatus
切换语言 /api/switchlanguage

启用 WebAPI

var wam = new WAModel { Port = 8080 };
await modbus.WAOnAsync(wam);  // 在端口 8080 上启动 HTTP 服务器

WebAPI 请求示例

// POST /api/read 的 JSON 请求体:
{
    "SN": "8c71f4a7-04eb-4f9c-88d9-849c2f0c3a00",
    "AddressArray": [
        {
            "SN": "TestAddress",
            "AddressName": "M100",
            "AddressDataType": "Float"
        }
    ],
    "CreationTime": "2024-06-05T13:01:24.6245462+08:00"
}

响应:序列化为 JSON 的 OperateResult


ReadAsync 管道

协议类中 ReadAsync 的实现通常遵循以下流程:

1. 解析 AddressDetails -> 协议特定的地址字节
2. AddressHandler.ExecuteDispose() -- 虚拟地址拦截
3. 构建协议帧(功能码、寄存器、数量)
4. 通过通信层发送帧
5. 接收响应字节
6. 验证响应(CRC、长度、站号)
7. 根据 DataFormat(CDAB/ABCD/等)转换字节
8. 根据 AddressDataType 解析为类型化的 AddressValue
9. 将值填充到 AddressArray -> EventDataResult
10. 触发 OnDataEvent + OnDataEventAsync
11. 返回带有 ResultData 的 OperateResult

AddressHandler.ExecuteDispose() 步骤是在实际 I/O 发生之前拦截虚拟地址的地方。


编码

DaqAbstract 在其静态构造函数中注册 CodePagesEncodingProvider,以支持 GB2312 编码,这是中国制造的 PLC 通常需要的编码。

static DaqAbstract()
{
    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
}