ZL.IotOss
2.2.18
dotnet add package ZL.IotOss --version 2.2.18
NuGet\Install-Package ZL.IotOss -Version 2.2.18
<PackageReference Include="ZL.IotOss" Version="2.2.18" />
<PackageVersion Include="ZL.IotOss" Version="2.2.18" />
<PackageReference Include="ZL.IotOss" />
paket add ZL.IotOss --version 2.2.18
#r "nuget: ZL.IotOss, 2.2.18"
#:package ZL.IotOss@2.2.18
#addin nuget:?package=ZL.IotOss&version=2.2.18
#tool nuget:?package=ZL.IotOss&version=2.2.18
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 协议(随包原生库
mitsubishi0 命中;官方仓库仅 AB/Omron/Modbus TCP), 其余开源候选均不满足生产标准——按“宁缺毋滥”原则移出本包范围。Omron 边界:
omron-njnx-oss只覆盖 NJ/NX 的 CIP/EtherNet/IP(libplctagPlcType.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() 会:
- 扫描宿主目录下的
ZL.Iot*.dll(排除ZL.IotEngine.dll、ZL.License.dll); - 反射创建
IProtocolPlugin实例; - 调用
GetDescriptors()注册到NativeProtocolRegistry; - 校验
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 排除,不需要该包。
建议但未在本版实施(需单独评审/排期):
- 把 OPC UA Server 从
ZL.IotEngine拆成ZL.IotEngine.OpcUa包:当前 net8 发布输出约 18 MB,其中Opc.Ua.*约 9.4 MB;只用 S7/Modbus 的宿主被迫携带。 - 把
ZL.IotOss按底层库拆成ZL.IotOss.S7Plus/.NModbus/.AllenBradley: 当前一个包同时拖入 S7.Net Plus、NModbus、libplctag(含 3.1 MB 的 NativeImport), 只用 Modbus 的宿主也会拿到 AB 原生库。 - 对
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 | Versions 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. |
-
.NETFramework 4.8
- libplctag (>= 1.5.2)
- NModbus (>= 3.0.83)
- S7netplus (>= 0.20.0)
- ZL.IotEngine (>= 2.2.18)
-
.NETStandard 2.0
- libplctag (>= 1.5.2)
- NModbus (>= 3.0.83)
- S7netplus (>= 0.20.0)
- ZL.IotEngine (>= 2.2.18)
-
net10.0
- libplctag (>= 1.5.2)
- NModbus (>= 3.0.83)
- S7netplus (>= 0.20.0)
- System.IO.Ports (>= 9.0.4)
- ZL.IotEngine (>= 2.2.18)
-
net8.0
- libplctag (>= 1.5.2)
- NModbus (>= 3.0.83)
- S7netplus (>= 0.20.0)
- System.IO.Ports (>= 9.0.4)
- ZL.IotEngine (>= 2.2.18)
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 |