ZL.IotOss 2.2.18

dotnet add package ZL.IotOss --version 2.2.18
                    
NuGet\Install-Package ZL.IotOss -Version 2.2.18
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ZL.IotOss" Version="2.2.18" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ZL.IotOss" Version="2.2.18" />
                    
Directory.Packages.props
<PackageReference Include="ZL.IotOss" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ZL.IotOss --version 2.2.18
                    
#r "nuget: ZL.IotOss, 2.2.18"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ZL.IotOss@2.2.18
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ZL.IotOss&version=2.2.18
                    
Install as a Cake Addin
#tool nuget:?package=ZL.IotOss&version=2.2.18
                    
Install as a Cake Tool

ZL.IotOss

Oss = Open Source Software。本包只做“薄适配”:协议栈全部来自成熟开源库, 不自研 TCP/帧解析,不引入商业/收费库。

ZL.IotOss 是 ZL.IotEngine 的开源驱动插件包,当前提供四类协议:

协议族 底层开源库 License 协议键
西门子 S7 S7.Net Plus MIT s7-1200-plus-oss、s7-300-plus-oss
Modbus TCP/RTU/ASCII NModbus 3.x MIT modbus-tcp-oss、modbus-rtu-oss、modbus-ascii-oss
Allen-Bradley EtherNet/IP LibPlcTag MPL-2.0 或 LGPL-2+(双许可,C 核心) ab-eip-oss
Omron NJ/NX CIP/EtherNet/IP LibPlcTag MPL-2.0 或 LGPL-2+(双许可,C 核心) omron-njnx-oss

范围说明(2026-11-05 证据评审,docs/573):本包不含三菱 MC。 libplctag 不支持 MC 协议(随包原生库 mitsubishi 0 命中;官方仓库仅 AB/Omron/Modbus TCP), 其余开源候选均不满足生产标准——按“宁缺毋滥”原则移出本包范围。

Omron 边界:omron-njnx-oss 只覆盖 NJ/NX 的 CIP/EtherNet/IP(libplctag PlcType.Omron), 不是 FINS;CJ/CP/CS 等 FINS/HostLink 机型仍用自研 omron-fins-tcp / omron-host-link。

详细评审:依赖精简、协议键/型号组织、58 个自研描述符的对比分析,见 docs/585_ZL.IotOss依赖精简与协议描述符组织评审_20261105.md。


1. 先搞清楚三个包的关系

ZL.IotEngine   核心运行时:契约、编排、地址合并、批量采集、OPC UA(可选)
     ▲
     │ ProjectReference / PackageReference
     ├── ZL.IotDriver   58 个自研协议描述符(不依赖任何第三方通讯库)
     └── ZL.IotOss      7 个开源协议描述符(S7.Net Plus / NModbus / LibPlcTag)
你的场景 需要引用
只用开源协议(S7 / Modbus / AB / Omron NJ/NX CIP) ZL.IotEngine + ZL.IotOss
只用自研协议(三菱/欧姆龙/汇川/…) ZL.IotEngine + ZL.IotDriver
两者都用 ZL.IotEngine + ZL.IotDriver + ZL.IotOss
旧项目仍用冻结的 ZL.IotHub 不要再同时引 ZL.IotEngine(两者都定义 ZL.Tag.* ⇒ CS0433)

ZL.IotOss 不引用 ZL.IotDriver;反过来 ZL.IotDriver 也不引用 ZL.IotOss。 两个插件包都只依赖 ZL.IotEngine,宿主把对应 dll 放到输出目录即可被自动发现。


2. 安装与插件发现

2.1 引用

<ItemGroup>
  <PackageReference Include="ZL.IotEngine" Version="3.1.0" />
  <PackageReference Include="ZL.IotOss" Version="3.1.0" />
</ItemGroup>

仓库内开发用 <ProjectReference> 指向 ZL.IotEngine/ZL.IotEngine.csproj 与 ZL.IotOss/ZL.IotOss.csproj;不要为了用开源协议去引用 ZL.IotDriver。

2.2 插件发现

引擎启动时 ProtocolPluginLoader.TryLoadAuto() 会:

  1. 扫描宿主目录下的 ZL.Iot*.dll(排除 ZL.IotEngine.dll、ZL.License.dll);
  2. 反射创建 IProtocolPlugin 实例;
  3. 调用 GetDescriptors() 注册到 NativeProtocolRegistry;
  4. 校验 ApiVersion == 1。

因此只要 ZL.IotOss.dll 在应用输出目录,NativeProtocolRegistry.All 里就会出现 7 个 -oss 协议键。也可以放 protocol-plugins.json 清单显式指定,但常规部署不需要。

2.3 初始化(AB / Omron NJ/NX 必需)

using ZL.IotOss;

// AB / Omron NJ/NX(libplctag)首次读写前必须调用;S7 / Modbus 不需要。
// 幂等;返回 false = 原生库没有部署成功(AB/Omron 会报 DllNotFoundException,S7/Modbus 不受影响)。
bool ready = OssBootstrap.EnsureReady();

libplctag.NativeImport 把各平台原生库作为内嵌资源分发,但不会在 P/Invoke 时自动解包。 EnsureReady() 按进程架构把当前平台的原生库写到 AppContext.BaseDirectory。

⚠️ macOS x64 已知问题:libplctag.NativeImport 1.0.41 的 osx_x64 内嵌资源实测也是 arm64(上游包错标)。在 Apple Silicon 上用 x64 .NET(Rosetta)跑 AB 时,EnsureReady() 会检测到架构不匹配并返回 false;请改用 arm64 .NET,或把正确架构的 libplctag.dylib 预置到应用目录(EnsureReady() 会保留架构正确的已有文件)。Linux x64 / Windows x64 不受此影响。


3. 两种用法:裸设备 vs 引擎托管

3.1 裸 IPlcDevice(按地址直读,适合脚本/诊断/小工具)

using ZL.IotEngine.Abstractions;
using ZL.IotEngine.Native;
using ZL.Tag;

var cfg = new DeviceConfig();
cfg.Add("Protocol", "s7-1200-plus-oss");
cfg.Add("DeviceIp", "192.168.1.100");
cfg.Add("Port", 102);
cfg.Add("DeviceType", "S7-1200");   // 可选;不写默认 S7-1200
cfg.Add("TimeoutMs", 3000);         // 可选;默认 3000

var descriptor = NativeProtocolRegistry.ResolveByKey("s7-1200-plus-oss")
    ?? throw new InvalidOperationException("协议未注册:ZL.IotOss.dll 是否在输出目录?");

var device = (IPlcDevice)descriptor.ClientFactory(cfg);
try
{
    var conn = device.ConnectServer();
    if (!conn.IsSuccess) throw new InvalidOperationException(conn.Message);

    var write = await device.WriteAsync("DB1.DBW2", (short)123);
    if (!write.IsSuccess) throw new InvalidOperationException(write.Message);

    var read = await device.ReadInt16Async("DB1.DBW2");
    Console.WriteLine(read.IsSuccess ? read.Content : read.Message);
}
finally
{
    device.ConnectClose();
    if (device is IDisposable d) d.Dispose();   // AB 适配器实现了 IDisposable
}

这条路径不经过引擎的标签表/批量合并,地址原样交给适配器;适合验证点位、排查现场问题。 tests/ZL.IotOss.Tests/SimulatorE2ETests.cs 走的就是同一条路径。

3.2 DriverFactory + 标签表(生产采集推荐)

using ZL.IotEngine.Orchestration;
using ZL.Tag;

var cfg = new DeviceConfig();
cfg.Add("Protocol", "s7-1200-plus-oss");
cfg.Add("DeviceIp", "192.168.1.100");
cfg.Add("Port", 102);
cfg.Add("DeviceId", "PLC1");

using var driver = DriverFactory.Create(cfg);

// 🔴 先登记点位,再按【标签 Id】读写;Read<T>("DB1.DBW2") 是错误用法。
driver.Tags["Temperature"] = new TagItem
{
    Id = "Temperature",
    Address = "DB1.DBW2",
    DataTypeCode = "int16",
    Enable = true,
};

driver.Connect();
var value = driver.Read<int>("Temperature");   // 走引擎批量/实时路径

DeviceBase.Read<T>(string id) 的参数是标签 Id,不是 PLC 地址。查不到时它返回 default(T)(例如 0),不会抛异常——这是现场“永远读到 0”的最常见原因。

类型码必须按位宽选(PlcDataTypeMap.Normalize 的别名表):

需求 推荐 DataTypeCode 等价写法 字节
16 位有符号 short int16 2
16 位无符号 ushort uint16 2
32 位有符号 int int32 4
32 位无符号 uint uint32 4
32 位浮点 float real / single 4
64 位有符号/无符号 long / ulong int64 / uint64 8
布尔 bool — 1(位地址语义由驱动处理)
字节数组 byte[] bytearray 必须配 Length

🔴 uint 是 32 位无符号,不是 16 位;要 16 位无符号必须写 ushort / uint16。 把 uint 当 uint16 用会读 4 字节,轻则值不对,重则覆盖相邻点位。


4. 协议配置与地址写法

4.1 通用键

键 适用 说明
Protocol 全部 协议键或别名;建议写带 -oss 的键
DeviceId 全部 业务设备 Id(引擎托管时用于区分设备)
DeviceIp S7 / Modbus TCP / AB / Omron NJ/NX PLC 或网关 IP
Port S7 / Modbus TCP / AB / Omron NJ/NX S7 默认 102;Modbus TCP 默认 502;AB/Omron 默认 44818
TimeoutMs S7 / Modbus 读/写/连接超时毫秒,默认 3000;≤0 归一到 3000
Timeout / TimeoutMs AB / Omron NJ/NX 单次操作超时毫秒,默认 5000;Timeout 优先,TimeoutMs 为兼容通用键

4.2 西门子 S7

协议键与型号(键是“协议族”,不是“型号清单”):

协议键 覆盖型号 默认 Rack/Slot 说明
s7-1200-plus-oss S7-1200、S7-1500 0 / 2 两者共用 S7comm;别名 s7plus、s7-1200-plus、s7-1500-plus
s7-300-plus-oss S7-300、S7-400 0 / 2 别名 s7-300、s7-300-plus

适配器还识别 S7-200、S7-200Smart 两个 CpuType(默认 Slot=1),但:

  • 当前没有为它们注册独立协议键,也没有仿真器 E2E;
  • 经典 S7-200 本体只有 PPI/MPI,没有以太网;要走上层 S7comm 必须加 CP243-1 或经网关; 纯 PPI 现场请用 ZL.IotDriver 的 siemens-ppi(本包不处理 PPI);
  • S7-200Smart 有以太网,适配器支持 CpuType.S7200Smart,但属“可用但未验证”组合, 发布文档不把它写成已承诺支持。

配置示例:

cfg.Add("Protocol", "s7-1200-plus-oss");
cfg.Add("DeviceType", "S7-1500");   // 或 S7-1200 / S7-300 / S7-400
// cfg.Add("CpuType", "S71500");    // 可选;显式 CpuType 优先于 DeviceType
// cfg.Add("Rack", 0);              // 可选;不写用型号默认值
// cfg.Add("Slot", 2);              // 可选;S7-1500 部分 CPU 需要 3

地址写法(Siemens 画像):

区 推荐写法 说明
DB DB1.DBW2、DB100.DBD24、DB1.DBX20.3 块号必须写全;DB.2 缺块号会被拒,不猜 DB0
M M100、MW100、M10.2 M100 是字节 100;不会按位区规则误读
I / Q I0.0、IW100、Q0.1、QW6 点表(批量路径)请写 I/Q
T / C T100、C50 定时器 / 计数器
P — 不支持(S7.Net Plus 的 DataType 无外设区);解析阶段即拒绝

德语助记符 E0.0 / A4.0:直调适配器时会映射到 I/Q;但点表建块阶段按 Siemens 画像解析,E/A 会被响亮排除(有告警,不会静默错读)。新点表请直接写 I/Q。

4.3 Modbus TCP / RTU / ASCII

键 传输 必填 说明
modbus-tcp-oss TCP DeviceIp 默认端口 502;别名 modbus-oss
modbus-rtu-oss 串口 RTU SerialPort 默认 9600/8/None/One;别名 modbus-rtu-oss
modbus-ascii-oss 串口 ASCII SerialPort 默认 9600/8/None/One;别名 modbus-ascii
配置键 适用 默认 说明
Transport 全部 Tcp Tcp / Rtu / Ascii;RTU/ASCII 仅在 net48/net8/net10 可用
UnitId 全部 1 从站地址(1..247)
Port TCP 502 TCP 端口
SerialPort RTU/ASCII — 如 COM3、/dev/ttyUSB0
BaudRate RTU/ASCII 9600 波特率
Parity RTU/ASCII None None/Odd/Even/Mark/Space
DataBits RTU/ASCII 8 数据位
StopBits RTU/ASCII One One/Two/OnePointFive/None
DeviceType 全部 ModbusGeneric 仅日志/业务标识,不影响协议

地址写法:

写法 区 功能码 说明
HR0 / 40001 保持寄存器 FC3(写 FC6/FC16) 40001 是一基地址,内部归一到 0
IR0 / 30001 输入寄存器 FC4 只读
C0 / 00001 线圈 FC1(写 FC5/FC15) 位
DI0 / 10001 离散输入 FC2 只读,位
HR0.3 保持寄存器位 FC3 + 位提取 位号 0..15

纯数字地址按标准 Modbus 5 位约定解析;50000+、越界地址会被拒绝。 ReadBytes 的 length 恒为字节,适配器内部按 (length+1)/2 换算寄存器数。

4.4 Allen-Bradley EtherNet/IP

配置键 默认 说明
DeviceIp — PLC/网关 IP(必填)
Port 44818 EtherNet/IP TCP 端口
Path 1,0 CIP 路径;ControlLogix 常见 1,0,CompactLogix 按实际机架/槽位
PlcType ControlLogix ControlLogix / Micro800 / MicroLogix / Plc5 / Slc500 / LogixPccc
DeviceType ControlLogix PlcType 未显式配置时用它推断
UseConnectedMessaging false 默认 UCMM;仿真器不支持 0x70 连接式数据,不要轻易改 true
Timeout / TimeoutMs 5000 单次操作超时毫秒;Timeout 优先,TimeoutMs 也接受

地址写法:地址就是 Logix Tag 名,原样交给 libplctag:

形态 示例 说明
标量 Tag TestDint、Speed 名称大小写敏感,按 PLC 实际 Tag 写
数组元素 MyDints[3] 方括号;不是 .3
UDT 成员 MyUDT.Speed 点号分隔
程序作用域 Program:Main.Speed 冒号 + 点号;批量路径会保留原始串

AB 走 UCMM(UseConnectedMessaging=false),不自研 Class-3 / 0x0070 / Forward Close。 仿真器回环只能证明“与仿真器一致”,不等于真机验证;生产前必须用真实 PLC 复验。

4.5 Omron NJ/NX CIP/EtherNet/IP

配置键 默认 说明
DeviceIp — PLC/网关 IP(必填)
Port 44818 EtherNet/IP TCP 端口
Path 1,0 CIP 路径;libplctag 官方 Omron 示例为 1,0,真机按工程/路由调整
PlcType Omron 可省略;接受 Omron / omron-njnx / omron-nj / omron-nx / njnx / nx1p2
DeviceType NJ/NX 仅业务标识;不参与协议分支
Timeout / TimeoutMs 5000 单次操作超时毫秒;Timeout 优先

协议边界:本适配器走 CIP/EtherNet/IP,与 AB 共用 libplctag 的 protocol=ab-eip, 仅 PlcType/默认路径不同;不是 FINS。只适用于 NJ/NX(含 NX1P2)等支持 CIP 变量访问的机型; CJ/CP/CS 等 FINS/HostLink 机型请用自研 omron-fins-tcp / omron-host-link。

地址写法:地址 = Sysmac Studio 中的变量名,原样交给 libplctag:

形态 示例 说明
标量变量 TestDINT、Speed 变量必须已定义并允许 CIP 访问
数组元素 MyArray[3] 方括号;不是 .3
结构体成员 MyStruct.Speed 点号分隔

已验证边界:离线契约测试覆盖构造/别名/描述符;真实读写用 libplctag 官方 ab_server --plc=Omron 模拟器验证(Path=18,127.0.0.1,QuickStart 10/10)。 模拟器通过 ≠ NJ/NX 真机通过:真机还需确认变量访问权限、CIP 路由、防火墙和固件差异。


5. 协议键、别名与共存

协议键 别名 显示名
s7-1200-plus-oss s7plus、s7-1200-plus、s7-1500-plus S7-1200/1500 (S7.Net Plus)
s7-300-plus-oss s7-300、s7-300-plus S7-300/400 (S7.Net Plus)
modbus-tcp-oss modbus-oss Modbus TCP (NModbus 3.x)
modbus-rtu-oss modbus-rtu-oss Modbus RTU (NModbus 3.x)
modbus-ascii-oss modbus-ascii Modbus ASCII (NModbus 3.x)
ab-eip-oss ab-eip Allen-Bradley EtherNet/IP (LibPlcTag)
omron-njnx-oss omron-njnx、omron-nj、omron-nx、njnx、nx1p2 Omron NJ/NX EtherNet/IP (LibPlcTag)

别名避让规则:本包不占用自研驱动已注册的裸名(modbus-tcp、modbus、modbus-rtu、 ab、allen-bradley、s7 等)。NativeProtocolRegistry 对重复别名是“后到者静默覆盖”, 共存时请使用带 -oss 的键;不要新增裸名别名。

与 ZL.IotHub.X(HSL)共存:两者都依赖 ZL.IotEngine,但 HSL 走自己的 HslProtocolRegistry,协议键体系不同;同一宿主可以同时引用。不要同时引用冻结的 ZL.IotHub 与 ZL.IotEngine(CS0433)。


6. 新增协议 / 新增型号

6.1 同协议族新增型号(只改配置)

S7 的 1200/1500、300/400 已经证明:同一协议族的不同型号不应新增协议键。 做法是把型号元数据集中到 *ProtocolDescriptor 静态类(CPU 类型、默认 Rack/Slot、地址方言), 适配器按 DeviceType / CpuType 选择;插件只为“协议族/传输方式”注册描述符。

6.2 全新协议(新增 Plugin + Adapter)

public sealed class MyProtocolDeviceAdapter : IPlcDevice { /* 薄适配 */ }

public sealed class MyProtocolPlugin : IProtocolPlugin
{
    public int ApiVersion => 1;
    public string Id => "my-protocol-oss";
    public IEnumerable<NativeProtocolDescriptor> GetDescriptors()
    {
        yield return new NativeProtocolDescriptor(
            "my-protocol-oss",
            "My Protocol (LibXxx)",
            new[] { "my-protocol-oss" },       // 别名必须避开自研驱动的裸名
            cfg => new MyProtocolDeviceAdapter(cfg),
            addressProfile: PlcAddressProfile.Any);
    }
}

编译成 ZL.Iot*.dll 放到宿主目录即可自动发现;不需要改引擎。不要为每个设备型号 复制一个 Plugin/Descriptor。


7. 故障排查

现象 先查什么
NativeProtocolRegistry.All 里没有 -oss 键 ZL.IotOss.dll 是否在输出目录;是否被裁剪/单文件发布漏掉
无法解析 S7 地址:M.0 批量地址格式化问题;升级到本版(M0/M100 已修)
S7 读到明显错误的值 点表是否写了 E/A/P 老写法;改用 I/Q,P 不支持
Modbus 全部读到 0 地址是否用了 40001 但设备实际是 0 基;先试 HR0
Modbus RTU 打开串口失败 SerialPort 名称、权限、波特率/校验位;Linux 检查 dialout 组
AB 报 DllNotFoundException 是否调用 OssBootstrap.EnsureReady();原生库架构是否匹配进程
AB 报 0x0A BAD_STATUS Tag 名是否存在/大小写/作用域;Path/PlcType 是否正确
AB 报 NOT_FOUND Tag 名不对,或数组用了 .3 而不是 [3]
Omron 报 NOT_FOUND 变量名不在全局变量表,或未允许 CIP 访问;数组用 [3] 不是 .3
Omron 连接超时 确认是 NJ/NX 的 EtherNet/IP(不是 FINS 9600);Path/路由/防火墙是否正确
Omron 读到 0 或 BAD_DATA 变量类型与 ReadInt32/ReadUInt16/... 是否匹配;不要拿 FINS 地址(如 D100)当 CIP 变量名
与自研驱动共存时协议被顶掉 是否用了裸别名;改用 -oss 键
Read<T>("DB1.DBW2") 永远 0 参数是标签 Id,不是地址;先登记 Tags

8. 测试与验证边界

# 单元/契约测试(离线;不需要 PLC/仿真器)
dotnet test tests/ZL.IotOss.Tests/ZL.IotOss.Tests.csproj

# 仿真器回环(Manual;需要本机 plcsim)
plcsim --protocols siemens-s7,modbus-tcp,ab \
       --ports siemens-s7=31002,modbus-tcp=31502,ab=32004 \
       --headless --stay-alive --auto-start
dotnet test tests/ZL.IotOss.Tests/ZL.IotOss.Tests.csproj \
  --filter "FullyQualifiedName~SimulatorE2ETests"

# Omron NJ/NX:plcsim 当前没有 Omron CIP 端点,用 libplctag 官方 ab_server 模拟器
ab_server --plc=Omron --port=32005 \
  --tag=TestDINT:DINT[10] --tag=TestINT:INT[10] \
  --tag=TestREAL:REAL[10] --tag=TestBOOL:BOOL[10] --tag=TestBYTES:SINT[10]
dotnet run --project demos/ZL.IotOss.Demo.QuickStart -- \
  --protocol omron-njnx-oss --port 32005 --path 18,127.0.0.1
  • S7 / Modbus:仿真器回环已通过。
  • AB:用例存在,但本机 plcsim 2.2.10 build_commit e3c244f 早于 AB 修复 e5ff34d5, 复跑仍报 0x0A BAD_STATUS;且 macOS x64 需匹配架构的 libplctag。
  • Omron NJ/NX:已用 libplctag 官方 ab_server --plc=Omron 模拟器完成 int32/uint16/bool/float/byte[4] × 裸驱动/引擎双路径 10/10 回环;尚无 NJ/NX 真机验证。 必须用 e5ff34d5+ 仿真器 + 匹配原生库复验,仿真器通过也不等于真机通过。
  • 没有真实 PLC:所有“已实现”只代表“与仿真器/协议栈一致”,不代表现场时序、 防火墙、PLC 型号差异都已验证。发布说明必须保留这条边界。

9. 依赖与精简建议

当前直接依赖:

ZL.IotEngine
  ├─ NLog
  ├─ Newtonsoft.Json
  ├─ System.Threading.Channels
  ├─ System.Threading.Tasks.Extensions
  ├─ Microsoft.CSharp
  ├─ System.Memory
  ├─ System.Buffers
  ├─ System.Text.Encoding.CodePages(非 net48)
  └─ OPCFoundation.NetStandard.Opc.Ua.Server(仅 net8/net10)

ZL.IotOss
  ├─ ZL.IotEngine
  ├─ S7netplus 0.20.0
  ├─ NModbus 3.0.83
  ├─ System.IO.Ports 9.0.4(仅 net8/net10;RTU/ASCII 直接使用)
  └─ libplctag 1.5.2 → libplctag.NativeImport 1.0.41

已经做的精简:ZL.IotEngine 删除未使用的 System.IO.Ports;ZL.IotOss 改为直接声明 System.IO.Ports(此前靠 Engine 传递,属隐式耦合),且只在 net8/net10 声明—— netstandard2.0 的串口代码被 #if 排除,不需要该包。

建议但未在本版实施(需单独评审/排期):

  1. 把 OPC UA Server 从 ZL.IotEngine 拆成 ZL.IotEngine.OpcUa 包:当前 net8 发布输出约 18 MB,其中 Opc.Ua.* 约 9.4 MB;只用 S7/Modbus 的宿主被迫携带。
  2. 把 ZL.IotOss 按底层库拆成 ZL.IotOss.S7Plus / .NModbus / .AllenBradley: 当前一个包同时拖入 S7.Net Plus、NModbus、libplctag(含 3.1 MB 的 NativeImport), 只用 Modbus 的宿主也会拿到 AB 原生库。
  3. 对 System.Memory / System.Buffers / System.Threading.Channels / System.Threading.Tasks.Extensions 做 TFM 条件化:net8/net10 已内置,netstandard2.0/net48 仍需要;需全 TFM 构建验证后再动。

10. 许可证

本包采用 LICENSE 声明,并再分发以下开源库:

  • S7.Net Plus:MIT
  • NModbus 3.x:MIT
  • LibPlcTag(.NET wrapper):MPL-2.0;C 核心库:MPL-2.0 或 LGPL-2+ 双许可 (2026-11-05 核实官方仓库 LICENSE;按 MPL-2.0 之选声明,无传染性顾虑)
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 is compatible.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.2.18 8 11/7/2026