PlcLibrary.Modbus 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package PlcLibrary.Modbus --version 1.0.0
                    
NuGet\Install-Package PlcLibrary.Modbus -Version 1.0.0
                    
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="PlcLibrary.Modbus" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PlcLibrary.Modbus" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="PlcLibrary.Modbus" />
                    
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 PlcLibrary.Modbus --version 1.0.0
                    
#r "nuget: PlcLibrary.Modbus, 1.0.0"
                    
#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 PlcLibrary.Modbus@1.0.0
                    
#: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=PlcLibrary.Modbus&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=PlcLibrary.Modbus&version=1.0.0
                    
Install as a Cake Tool

PlcLibrary

PLC 数据采集库,提供连接池管理、定时采集调度、数据分发管道。

  • 协议无关驱动接口,一行注册新协议
  • 连接池 + Polly 弹性策略(重试、超时、断路器),每设备独立隔离,覆盖连接和 IO 两级
  • 设备配置热更新,差量 reconcile
  • Channel 管道 fan-out 到多个 IDataHandler
  • 主动读写 + 自动采集双模式
  • System.Diagnostics.Metrics 内置可观测性,0 依赖接入 Prometheus / Grafana

支持的驱动

驱动 协议 状态
S7Driver Siemens S7 (S7-200/300/400/1200/1500) 可用
ModbusTcpDriver Modbus TCP 可用
ModbusUdpDriver Modbus UDP 可用
OpcUaDriver OPC UA 可用
MitsubishiDriver Mitsubishi MC / A1E / FX 可用
OmronDriver Omron FINS TCP 可用
AllenBradleyDriver Allen-Bradley Logix Tag (CIP/EtherNet/IP) 可用
BacnetDriver BACnet/IP 可用
ModbusRtuDriver Modbus RTU 待 NModbus 更新
ModbusAsciiDriver Modbus ASCII 待 NModbus 更新

安装

dotnet add package PlcLibrary

按需添加驱动包:

dotnet add package PlcLibrary.S7
dotnet add package PlcLibrary.Modbus
dotnet add package PlcLibrary.OpcUa
dotnet add package PlcLibrary.Mitsubishi
dotnet add package PlcLibrary.Omron
dotnet add package PlcLibrary.AllenBradley
dotnet add package PlcLibrary.Bacnet

快速开始

using Microsoft.Extensions.Logging;
using PlcLibrary.Controller.Interfaces;
using PlcLibrary.DriverDomain.Models;
using PlcLibrary.Extensions;
using PlcLibrary.General.Configuration;
using PlcLibrary.Pipeline.Interfaces;
using PlcLibrary.S7;

var builder = Host.CreateApplicationBuilder(args);

var devices = new[]
{
    new DeviceConfiguration
    {
        Id = "plc-001",
        Name = "1号线",
        Protocol = "S7",
        ConnectionString = "host:10.38.103.107;port:102;timeout:3000;rack:0;slot:0;cpu:S71200;",
        TagPoints = new[]
        {
            new TagPointConfiguration { TagId = "t1", Address = "DB21.DBX10.2", DataType = "System.Boolean" },
            new TagPointConfiguration { TagId = "t2", Address = "DB21.DBX10.0", DataType = "System.Boolean" },
        },
        CollectionInterval = TimeSpan.FromSeconds(1),
    },
};

builder.Services
    .AddPlcLibrary()
    .AddDriver<S7Driver>()
    .AddSingleton<IDataHandler, ConsoleHandler>();

var host = builder.Build();
await host.Services.GetRequiredService<IDeviceScheduler>().ApplyDevicesAsync(devices);
await host.RunAsync();

internal sealed class ConsoleHandler(ILogger<ConsoleHandler> logger) : IDataHandler
{
    public ValueTask HandleAsync(DriverResult result, CancellationToken ct)
    {
        logger.LogInformation("[{DeviceId}] {Address} = {Value} ({Status})",
            result.DeviceId, result.Address, result.Value, result.Status);
        return ValueTask.CompletedTask;
    }
}

设备多时可用 BackgroundService + IConfiguration 从 appsettings.json 读取,参考下文 JSON 配置方式。

使用 JSON 配置

builder.Services.AddPlcLibrary();
builder.Services.Configure<PoolOptions>(builder.Configuration.GetSection("DriverPool"));
builder.Services.Configure<PipelineOptions>(builder.Configuration.GetSection("Pipeline"));
{
  "Devices": [
    {
      "Enabled": true,
      "Id": "plc-01",
      "Protocol": "S7",
      "ConnectionString": "host:192.168.1.1;port:102;rack:0;slot:1;cpu:S71200",
      "CollectionInterval": "00:00:01",
      "TagPoints": [
        { "TagId": "temp", "Address": "DB1.DBD0", "DataType": "Real" },
        { "TagId": "pressure", "Address": "DB1.DBD4", "DataType": "Real" }
      ]
    }
  ]
}
internal sealed class DeviceLoader(IConfiguration config, IDeviceScheduler scheduler) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        var devices = config.GetSection("Devices").Get<DeviceConfiguration[]>();
        if (devices is { Length: > 0 })
            await scheduler.ApplyDevicesAsync(devices, ct);
    }
}

连接字符串

格式 key:value;key:value,大小写不敏感。

S7

字段 默认值 说明
host 127.0.0.1 PLC 地址
port 102 端口
rack 0 机架
slot 0 插槽
timeout 3000 超时 (ms)
cpu S71200 CpuType

示例:host:192.168.1.1;port:102;rack:0;slot:1;cpu:S71500

Modbus TCP / UDP

字段 默认值 说明
host 127.0.0.1 设备地址
port 502 端口
slaveid 1 从站 ID
timeout 3000 超时 (ms)

示例:host:10.0.0.1;port:502;slaveid:2

Modbus 地址格式:使用 5 位十进制数字前缀标记数据类型:

前缀 类型 读写 示例 说明
0xxxx Coil 读写 00001 线圈,布尔量
1xxxx Discrete Input 只读 10042 离散输入,布尔量
3xxxx Input Register 只读 30001 输入寄存器,16 位无符号整数
4xxxx Holding Register 读写 40042 保持寄存器,16 位无符号整数

地址为 1-based(PLC 习惯),驱动内部自动转换为 0-based 偏移。连续地址自动合并为单次批量读写。

Modbus RTU / ASCII(待 NModbus 更新)

字段 默认值 说明
host - 串口号(COM3)
baudrate 9600 波特率
parity None 校验(None/Odd/Even)
databits 8 数据位
stopbits One 停止位(One/Two)
slaveid 1 从站 ID

示例:host:COM3;baudrate:19200;parity:Even;slaveid:1

OPC UA

字段 默认值 说明
endpoint opc.tcp://localhost:4840 服务器端点
username - 用户名(可选)
password - 密码(可选)
security None None / Sign / SignAndEncrypt
timeout 5000 超时 (ms)
publishinginterval 1000 订阅发布间隔 (ms)
sessiontimeout 60000 会话超时 (ms)
autoacceptcertificate false 自动接受证书(生产环境应设为 false)

示例:endpoint:opc.tcp://10.0.0.1:4840;security:None;timeout:10000

Mitsubishi

字段 默认值 说明
host 127.0.0.1 PLC 地址
port 6000 端口
timeout 3000 超时 (ms)
protocoltype MC MC / A1E / FX

示例:host:192.168.1.1;port:6000;protocoltype:MC

地址格式:三菱标准地址字符串,取决于所选协议类型:

协议类型 地址示例 说明
MC D100M100X10Y20W0 MELSEC MC 协议
A1E D100M100X0Y0 A-1E 协议
FX D100M100X0Y0 FX 编程口协议

Omron FINS

字段 默认值 说明
host 127.0.0.1 PLC 地址
port 9600 端口
timeout 3000 超时 (ms)
localnode 1 本机 FINS 节点号
destinynode 2 目标 FINS 节点号
isudp false 使用 UDP 传输

地址格式:Omron 标准 FINS 地址字符串。

区域 地址示例 说明
DM D100D200 数据存储器
CIO CIO200CIO200.5 通道 I/O(.5 表示位)
WR W100 工作继电器
HR H0H10 保持继电器
AR A0 辅助继电器

示例:host:192.168.1.1;port:9600;localnode:1;destinynode:2

Allen-Bradley / Rockwell

字段 默认值 说明
host 127.0.0.1 PLC / 网关模块地址
port 44818 EtherNet/IP 端口
timeout 5000 超时 (ms)
path - 路由路径(如 1,0 表示背板槽位 0)
useconnected false Class 3 连接(高频轮询时推荐开启)

地址格式:Logix 标签名。

地址示例 说明
rate 基础标签(DINT/REAL/BOOL 等原子类型)
counts[3] 数组元素索引
Temp[10].AnotherArray[4] 嵌套数组
MyUdt.enable UDT 结构体成员
matrix[1,2,3] 多维数组

CompactLogix 示例:host:192.168.1.96

ControlLogix 示例:host:192.168.1.96;path:1,0

BACnet

字段 默认值 说明
host 127.0.0.1 目标设备 IP
port 47808 BACnet/IP 端口 (0xBAC0)
timeout 5000 超时 (ms)
deviceinstance 0 目标设备实例号(0 表示使用 IP 地址通信)
localendpointip - 多网卡时指定绑定 IP

地址格式:TYPE:INSTANCE(如 AV:1 = Analog Value 1、BI:0 = Binary Input 0)。

支持类型:AI/AO/AV/BI/BO/BV/MI/MO/MV

示例:host:192.168.1.50;port:47808;deviceinstance:12345

点位数据类型 (DataType)

部分驱动需要 TagPointConfiguration.DataType 指定点位的数据类型,以正确解析 PLC 返回值:

驱动 DataType 是否必需 未指定时的默认值 说明
S7 地址推断 地址格式已含类型(DBX=bool, DBW=word)
Modbus 地址前缀推断 0xxxx=bool, 4xxxx=ushort
OPC UA 服务端返回 服务端告知类型,值透传
BACnet 服务端返回 PROP_PRESENT_VALUE 返回原始值
AllenBradley 推荐 int 用于确定 ReadAsync<T> 的泛型类型
Mitsubishi 推荐 Int16 映射为 Snet DataType 枚举
Omron 推荐 int 分发到 ReadInt16Async/ReadFloatAsync 等方法

支持的 DataType 值(大小写不敏感):

简写 完整名称 C# 类型
bool System.Boolean bool
short System.Int16 short
ushort System.UInt16 ushort
int System.Int32 int
uint System.UInt32 uint
long System.Int64 long
ulong System.UInt64 ulong
float System.Single float
double System.Double double
string System.String string
// Omron D100 为 32 位浮点数
new TagPointConfiguration { TagId = "temp", Address = "D100", DataType = "float" }

// AllenBradley tag 为布尔量
new TagPointConfiguration { TagId = "run", Address = "MotorRun", DataType = "bool" }

驱动池

{
  "DriverPool": {
    "MaxConnectionsPerDevice": 2,
    "MaxRetryAttempts": 3,
    "RetryDelay": "00:00:01",
    "CircuitBreakerMinimumThroughput": 5,
    "CircuitBreakerDuration": "00:00:30",
    "CircuitBreakerFailureRatio": 0.5,
    "OperationTimeout": "00:00:10"
  }
}

管道

{
  "Pipeline": {
    "Capacity": 10000,
    "MaxHandlerParallelism": 8,
    "HandlerTimeout": "00:00:30"
  }
}

弹性策略

Polly 弹性分为两级,每设备独立隔离:

级别 作用域 策略 触发位置
连接级 ConnectAsync / TryReconnectAsync 重试(指数退避) + 超时 + 断路器 DeviceSharedPool.AcquireAsync
IO 级 ReadAsync / WriteAsync 重试(指数退避) + 超时 DeviceDriverPool.ReadAsync / WriteAsync
  • 连接级断路器:连续失败达阈值后进入熔断(默认 5 次、失败率 ≥ 50%、冷却 30s),熔断/半开/恢复均有 ILogger 日志。
  • IO 级重试:每次 ReadAsync/WriteAsync 失败时自动重试(默认 3 次),排除 OperationCanceledExceptionTimeoutRejectedException
  • 两级策略均复用 DriverPool 配置节中的 MaxRetryAttemptsRetryDelayOperationTimeout

可观测性

PlcLibrary 通过 System.Diagnostics.Metrics(.NET 6+ 内置,零 NuGet 依赖)暴露以下指标:

Meter: PlcLibrary

指标名 类型 单位 说明
plc.reads.total Counter<long> points 累计采集点位总数
plc.read.duration Histogram<double> s ReadAsync 完整耗时(含 IO 重试)
plc.read.errors Counter<long> errors ReadAsync 最终失败次数(含重试耗尽)
plc.write.total Counter<long> ops 累计写操作数
plc.acquire.duration Histogram<double> s 连接池获取驱动耗时(含 connect)
plc.pipeline.dispatched Counter<long> points 管道分发点数
plc.pipeline.dropped Counter<long> points 订阅通道溢出丢弃点数

接入方式

OpenTelemetry Collector(推荐):

dotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore
builder.Services.AddOpenTelemetry()
    .WithMetrics(m => m
        .AddMeter("PlcLibrary")
        .AddPrometheusExporter());

启动后访问 /metrics 即可被 Prometheus 抓取,Grafana 中查询 plc_read_duration_seconds_bucket 等指标。

dotnet-counters(本地调试):

dotnet-counters monitor -n MyApp --counters PlcLibrary

Grafana 面板示例

# 采集吞吐 (points/s)
rate(plc_reads_total[1m])

# 读延迟 p50 / p99
histogram_quantile(0.50, rate(plc_read_duration_seconds_bucket[1m]))
histogram_quantile(0.99, rate(plc_read_duration_seconds_bucket[1m]))

# 错误率
rate(plc_read_errors_total[1m]) / rate(plc_reads_total[1m])

# 通道丢弃率
rate(plc_pipeline_dropped_total[1m])
{
  "Pipeline": {
    "Capacity": 10000,
    "MaxHandlerParallelism": 8,
    "HandlerTimeout": "00:00:30"
  }
}

API

核心接口

接口 说明
IDeviceScheduler 推送设备配置,差量 reconcile;GetDeviceHealthAsync() 查询运行状态
IDataHandler 接收采集推送
IDeviceAccessor 主动读写设备
IProtocolDriver 协议驱动实现(开发文档见 DEVELOPMENT.md
IDriverFactory 驱动工厂(通常用 AddDriver<T> 替代)
IDataPipeline 数据管道(通常不需要直接使用)

主动读写

public class MyService(IDeviceAccessor accessor)
{
    public async Task ReadDevice(DeviceConfiguration device)
    {
        var values = await accessor.ReadAsync(device, device.TagPoints);
    }

    public async Task WriteDevice(DeviceConfiguration device)
    {
        var points = new Dictionary<TagPointConfiguration, object>
        {
            [device.TagPoints[0]] = 123.45
        };
        await accessor.WriteAsync(device, points);
    }
}

设备级连接超时

DeviceConfiguration.ConnectionTimeout 可覆盖全局 PoolOptions.OperationTimeout,在获取驱动时优先使用设备级配置。默认 TimeSpan.Zero 表示使用全局配置。

断路器状态变更(熔断/半开/恢复)通过 ILogger 输出 Warning/Information 级别日志,每设备独立隔离。

健康状态

var scheduler = host.Services.GetRequiredService<IDeviceScheduler>();
var health = await scheduler.GetDeviceHealthAsync();

foreach (var d in health)
    Console.WriteLine($"{d.DeviceId} [{d.Protocol}] {(d.IsRunning ? "OK" : d.Error)}");

返回 IReadOnlyList<DeviceHealthInfo>,每个设备包含 DeviceIdProtocolIsRunningErrorUpdatedAt

致谢

本项目基于以下优秀的开源库构建:

用途 GitHub
S7netplus Siemens S7 通信 github.com/S7NetPlus/s7netplus
NModbus Modbus 协议 github.com/NModbus/NModbus
OPCFoundation.NetStandard.Opc.Ua.Client OPC UA 客户端 github.com/OPCFoundation/UA-.NETStandard
Snet.Mitsubishi 三菱 MC/A1E/FX 协议 github.com/shunnet(组织)
NewLife.Omron 欧姆龙 FINS/HostLink 协议 github.com/NewLifeX/NewLife.Omron
EthernetIPSharp Allen-Bradley EtherNet/IP github.com/CristianMori/EthernetIpSharp
BACnet BACnet 协议栈 github.com/ela-compil/BACnet

许可证

MIT

Product Compatible and additional computed target framework versions.
.NET 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 was computed.  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. 
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
1.1.0 149 9/3/2026
1.0.4 100 8/24/2026
1.0.3 104 8/22/2026
1.0.2 106 8/22/2026
1.0.1 105 8/21/2026
1.0.0 113 7/28/2026