🧩 IPacker 接口
命名空间: Snet.Model.@interface | 接口: IPacker | 成员: 6 个方法(同步 + 异步)
IPacker 是地址自动组包与解包能力接口。每个数采驱动都通过 IDaq 实现它,因此在驱动实例上即可直接组包、解包——无需手动装配处理器。
public interface IPacker
{
OperateResult Packer(Address? address, string protocolTypeKey, int maxByteLength = 0, DataFormat format = DataFormat.ABCD);
Task<OperateResult> PackerAsync(Address? address, string protocolTypeKey, int maxByteLength = 0, DataFormat format = DataFormat.ABCD, CancellationToken token = default);
OperateResult UnPacker(AddressValue addressValue, bool isStringReverseByteWord = false);
Task<OperateResult> UnPackerAsync(AddressValue addressValue, bool isStringReverseByteWord = false, CancellationToken token = default);
OperateResult UnPacker(ConcurrentDictionary<string, AddressValue> pairs, bool isStringReverseByteWord = false);
Task<OperateResult> UnPackerAsync(ConcurrentDictionary<string, AddressValue> pairs, bool isStringReverseByteWord = false, CancellationToken token = default);
}
接口层次结构
IPacker 是组成 IDaq 的 15 个组件接口之一——IDaq : ... IClone, IPacker, IDisposable, IAsyncDisposable。由 DaqAbstract<,>(全部 36+ 协议驱动的基类)实现,内部委托 PackerHandler(组包)与 BytesHandler(解包)。
成员参考
Packer — 智能地址自动组包
| 成员 | 签名 | 描述 |
|---|---|---|
Packer |
OperateResult Packer(Address? address, string protocolTypeKey, int maxByteLength = 0, DataFormat format = DataFormat.ABCD) |
将地址集优化为最小批量读取结构 |
PackerAsync |
Task<OperateResult> PackerAsync(Address? address, string protocolTypeKey, int maxByteLength = 0, DataFormat format = DataFormat.ABCD, CancellationToken token = default) |
异步变体 |
参数:
| 参数 | 描述 |
|---|---|
address |
原始地址集合 |
protocolTypeKey |
供应商 ProtocolType 枚举的字符串名——如 "SiemensS7Net_S1200"、"ModbusTcpNet"、"OmronFinsNet"。接受各供应商数据类中 ProtocolType.ToString() 的返回值 |
maxByteLength |
批次最大字节数;0 使用协议保守默认 MaxReadLength,正数仍受 PhysicalMaxBytes / 65535 上限约束 |
format |
数据字节序格式 |
返回(ResultData):支持组包时为优化 Address,否则原地址透传;null 输入或支持协议的空点集允许 null。常规错误包装为失败 OperateResult,但入口/异步取消不承诺永不抛异常。全部未优化点保留原对象、原 Length 和全部扩展参数,不附加单点模型;组包不是地址校正器。
UnPacker — 自动解包
| 成员 | 签名 | 描述 |
|---|---|---|
UnPacker |
OperateResult UnPacker(AddressValue addressValue, bool isStringReverseByteWord = false) |
解包单个组包批次,返回以地址名为键的 ConcurrentDictionary<string, AddressValue> |
UnPackerAsync |
Task<OperateResult> UnPackerAsync(AddressValue addressValue, bool isStringReverseByteWord = false, CancellationToken token = default) |
异步变体 |
UnPacker |
OperateResult UnPacker(ConcurrentDictionary<string, AddressValue> pairs, bool isStringReverseByteWord = false) |
解包一次读取结果的全部批次,返回 List<ConcurrentDictionary<string, AddressValue>> |
UnPackerAsync |
Task<OperateResult> UnPackerAsync(ConcurrentDictionary<string, AddressValue> pairs, bool isStringReverseByteWord = false, CancellationToken token = default) |
异步变体 |
参数:
| 参数 | 描述 |
|---|---|
isStringReverseByteWord |
String 解包时是否按 16 位字反转字节(每 2 字节一组交换)。连接级配置——如欧姆龙 FINS 的字符串按字反转字节读取。默认 false |
仅 AddressDataType=ByteArray 且扩展参数可取得 List
时按字节批解码;其他值以单点字典返回原 AddressValue 同对象,Status 由 Quality==Normal 决定。已识别字节批须质量正常且 OriginalValue 为 byte[],否则按每个模型返回 Exception 质量点并置 Status=false。偏移、长度或 BoolIndex 越界保留失败项,成功项仍可读取。空字节对非空模型逐项校验边界,空模型可成功返回空字典。异步取消可能传播异常,不承诺取消仅返回失败结果。
字典重载可统一处理组包批与直接值,对各项 Status 做与运算,保留所有返回字典(包括失败/部分结果)。任一失败则整体 Status=false(部分点位解包失败);仍应读取 ResultData 并逐点检查 Quality,不要丢弃结果列表。空字典成功返回空列表,null 输入失败。同名多规格结果键可带 \u0001DataType/序号后缀,AddressValue.AddressName 保留原地址。
快速入门
using Snet.Siemens;
using Snet.Model.data;
using Snet.Model.@enum;
using Snet.Model.@interface; // IPacker
using System.Collections.Concurrent;
await using var op = await SiemensOperate.InstanceAsync(new SiemensData.Basics
{
IpAddress = "192.168.1.100",
Port = 102
});
// 1) 组包:将分散地址合并为最少的批量读取
if (!(await op.OnAsync()).Status) return;
var address = new Address(new List<AddressDetails>
{
new("DB1.DBD0", DataType.Float),
new("DB1.DBD4", DataType.Float),
new("DB1.DBW8", DataType.Int16),
new("DB1.DBX10.3", DataType.Bool)
});
OperateResult packed = await op.PackerAsync(address, "SiemensS7Net_S1200");
if (!packed.Status) return; // 组包失败;不支持协议正常透传
var packedAddr = packed.GetSource<Address>(); // ResultData = 优化后的 Address
if (packedAddr is null) return;
// 2) 一次往返读取组包后的批次
OperateResult result = await op.ReadAsync(packedAddr);
if (!result.Status) return; // 读取失败
var data = result.GetSource<ConcurrentDictionary<string, AddressValue>>();
if (data is null) return;
// 3) 解包:将每个批次的原始字节解析回各地址的值
OperateResult unpacked = await op.UnPackerAsync(data);
if (!unpacked.Status) Console.Error.WriteLine(unpacked.Message);
var values = unpacked.GetSource<List<ConcurrentDictionary<string, AddressValue>>>();
if (values is null) return;
foreach (var dict in values)
foreach (var v in dict)
Console.WriteLine($"{v.Key} = {v.Value.ResultValue}");
解包单个批次
持有单个批次的 AddressValue 时(如来自订阅或事件负载),使用单值重载:
OperateResult unpacked = await op.UnPackerAsync(kv.Value); // kv.Value = 一个组包批次
if (!unpacked.Status) Console.Error.WriteLine(unpacked.Message);
var values = unpacked.GetSource<ConcurrentDictionary<string, AddressValue>>();
if (values is null) return;
GetSource<T>() 属于 OperateResult(来自 ResultModel),无参数,只转换非空 ResultData,并不检查 Status。应在结果对象上调用而非 ResultData 上;Status=false 时也可能读取到保留的失败/部分数据,随后逐点检查 Quality。
透传行为
当前映射还排除 SiemensFetchWriteNet、MelsecMcRNet、MelsecA1ENet、MelsecA1EAsciiNet、MelsecFxSerial、MelsecFxSerialOverTcp;A3C/A4C 仍可组包。完整协议/范围边界见 组包引擎。
Packer 不会盲目优化——符号/标签寻址的协议原样返回:
SiemensS7Plus(符号访问)MelsecCipNet(标签访问)OmronCipNet/OmronConnectedCipNet(标签访问)OmronHostLinkCMode/OmronHostLinkCModeOverTcp(已不在组包器供应商映射表中——原样返回)
成功时结果 Message 为 "组包成功",解包为 "解包成功"。
底层机制
| 组件 | 角色 |
|---|---|
DaqAbstract.packerHandler |
公共字段——Packer 使用的 PackerHandler 单例 |
DaqAbstract.bytesHandler |
公共字段——UnPacker 使用的 BytesHandler 单例 |
Snet.Core.packer.IPackerCore |
底层按协议实现的组包引擎(每个协议族一个实现) |
引擎细节、受支持的协议族与 maxByteLength 语义:地址自动组包。
