Mbus 0.9.1
dotnet add package Mbus --version 0.9.1
NuGet\Install-Package Mbus -Version 0.9.1
<PackageReference Include="Mbus" Version="0.9.1" />
<PackageVersion Include="Mbus" Version="0.9.1" />
<PackageReference Include="Mbus" />
paket add Mbus --version 0.9.1
#r "nuget: Mbus, 0.9.1"
#:package Mbus@0.9.1
#addin nuget:?package=Mbus&version=0.9.1
#tool nuget:?package=Mbus&version=0.9.1
mbus
.NET 的 Modbus 采集库(主站 + 从站模拟器)。目标 netstandard2.0(.NET Framework 4.6.2+)与 net8.0——老工控机上的 .NET Framework 项目和现代 .NET 项目都能直接引用。两层设计:
- 底层:裸寄存器读写,等价 NModbus 等传统库的用法——一个对象直连从站,读写寄存器/线圈
- 上层:点表(Tag)驱动的业务闭环——注册点位(名称/地址/类型/字序/换算),自动轮询、缓存、变化事件、按点名写回
特性一览:Modbus TCP / RTU over TCP / 串口 RTU · 11 个功能码(0x01-0x06 / 0x0F / 0x10 / 0x16 / 0x17 / 0x2B 设备识别)· 四种字序(ABCD/CDAB/BADC/DCBA)· Scale/Offset 工程量换算 · 寄存器位映射 · 多周期轮询组 · 相邻点自动合并读 · 断线自动重连 + 质量标记(Good/LastKnown/Bad)· JSON 点表导入(加载期校验:地址冲突/越界/类型匹配)· 规则报警引擎(点名 + 表达式,边沿触发/恢复,质量冻结,恢复表达式滞环,限值报警 WithHigh/WithLow 点表直配)· 变化死区(轮询降噪,基准=上次上报值)· 虚拟点(表达式计算点,级联/质量传播)· 内置从站模拟器(内存虚拟总线 + TCP 从站服务器)· WinForms/WPF 友好(MbusUi.Capture() 事件封送)
包结构与引入
| 项目 | 内容 |
|---|---|
| src/Mbus | 核心包:TCP / RTU over TCP / 虚拟总线 / JSON 点表导入 / 规则引擎(net8.0 零第三方依赖;ns2.0 目标的 JSON 支持由 System.Text.Json NuGet 包提供) |
| src/Mbus.Serial | 串口(RTU)扩展,依赖 System.IO.Ports |
| demo/Mbus.Demo | WinForms 演示程序(net8.0-windows) |
安装(NuGet 包 Mbus / Mbus.Serial):
dotnet add package Mbus # 核心包:TCP / RTU over TCP / 虚拟总线 / 从站模拟器
dotnet add package Mbus.Serial # 串口设备再加这个
或以源码引用:
<ItemGroup>
<ProjectReference Include="path\to\mbus\src\Mbus\Mbus.csproj" />
<ProjectReference Include="path\to\mbus\src\Mbus.Serial\Mbus.Serial.csproj" />
</ItemGroup>
dotnet test Mbus.sln # 165 个测试,含虚拟从站上的全链路端到端
快速上手(底层用法)
var dev = new MbusDeviceBuilder("plc-01")
.Tcp("192.168.1.10", 502)
.Unit(1)
.Build();
await dev.ConnectAsync();
var regs = await dev.ReadHoldingRegistersAsync(0, 10); // 功能码 0x03
await dev.WriteSingleRegisterAsync(0, 1234); // 功能码 0x06
var coils = await dev.ReadCoilsAsync(0, 16); // 功能码 0x01
// 扩展功能码(部分精简设备不支持,会返回 IllegalFunction)
await dev.MaskWriteRegisterAsync(0, 0xFFF7, 0x0008); // 0x16 掩码写:单事务原子置位 bit3(and=~bit 给 or 让路)
var after = await dev.ReadWriteMultipleRegistersAsync(9, 1, 9, [0x1234]); // 0x17 先写后读:写设定值并回读,一个往返
var id = await dev.ReadDeviceIdentificationAsync(); // 0x2B 设备识别(自动处理分片续传)
Console.WriteLine($"{id.VendorName} {id.ProductName} v{id.MajorMinorRevision}");
业务闭环(上层用法)
await using var client = new MbusClient();
var dev = client.Device("plc-01", d => d
.Tcp("192.168.1.10")
.Unit(1)
.DefaultWordOrder(WordOrder.CDAB) // 这台设备整机 CDAB
.Register("泵.运行", DataType.Bool, 0, Area.Coil)
.Register("电机.频率", DataType.F32, 1, o => o.WithReadOnly())
.Register("电机.电流", DataType.F32, 3, o => o.WithScale(0.1))
.Register("报警.过热", DataType.Bool, 5, o => o.WithBit(2)) // 寄存器第 2 位
.Register("统计.累计", DataType.U32, 6, o => o.WithWordOrder(WordOrder.ABCD)) // 单点例外
.Register("统计.名称", DataType.String, 8, o => o.WithLength(6)));
client.TagChanged += (_, e) => Console.WriteLine($"{e.Device.Id}/{e.Tag.Name} = {e.NewValue.Value} ({e.NewValue.Quality})");
client.LinkStateChanged += (_, e) => Console.WriteLine($"[{e.Link.Id}] {e.State} {e.Message}");
dev.Poll(100, "电机.*", "泵.*") // 快组:100ms
.Poll(2000, "统计.*", "报警.*"); // 慢组:2s
await client.StartAsync();
await dev.WriteAsync("泵.运行", true); // 按点名写回:工程值反算、字序编码、单事务原子写
var v = client["plc-01.电机.频率"]; // 跨设备一跳取缓存,断线返回旧值 + LastKnown
JSON 点表导入
点表让工程师按设备手册填文件、不写代码;连接参数(IP/串口)留在代码里——连接常因环境而异,语义配置才值得外置。
var dev = client.Device("plc-01", d => d
.Tcp("192.168.1.10")
.Unit(1)
.LoadTagsFromFile("plc-01.json"));
// 或:从字符串加载 / 一份点表应用到多台同型号设备
var table = MbusTagTable.FromFile("meter.json");
d1.LoadTags(table);
d2.LoadTags(table);
plc-01.json(支持注释与尾逗号,枚举值不区分大小写):
{
// 按设备手册逐行填
"tags": [
{ "name": "泵.运行", "type": "Bool", "address": 0, "area": "coil" },
{ "name": "电机.频率", "type": "F32", "address": 1, "readOnly": true },
{ "name": "电机.电流", "type": "F32", "address": 3, "scale": 0.1 },
{ "name": "报警.过热", "type": "Bool", "address": 5, "bitIndex": 2 },
{ "name": "统计.累计", "type": "U32", "address": 6, "wordOrder": "ABCD" },
{ "name": "统计.名称", "type": "String", "address": 8, "length": 6, "category": "统计" }
]
}
字段与 Register/WithXxx 一一对应:name / type / address 必填;area(默认 holding)、wordOrder、scale、offset、readOnly、length、bitIndex、category、deadband、high/low/hysteresis(限值报警)可选。导入与代码注册走同一通道、同一校验。
加载期校验一次报全(文件内的全部错误聚合为一条异常;与代码已注册点位的冲突在应用时立即抛出):
Mbus.ModbusException: JSON 点表校验失败,共 2 处:
- 第 1 项(电机.电压):未知类型 X32。
- 第 2 项(统计.累计):与点 电机.频率 地址重叠:Holding 区寄存器 2 已被占用。
地址冲突校验对代码注册同样生效:同区内寄存器区间不得相交;Bool + bitIndex 可共用寄存器但位号不得重复;整寄存器 Bool 与位点互斥(写回 0x0000/0x0001 会清掉其他位);地址 + 占用寄存器数超出 0-65535 拒绝。
规则与报警
点名 + 表达式构成规则,电机.温度 > 50 就是完整的一条:
dev.Rule("过热", "电机.温度 > 50") // 单参数
.Rule("过流且泵停", "电机.电流 > 10 && !泵.运行") // 多参数复合
.Rule("温差大", "(电机.温度 - 环境.温度) * 2 >= 30") // 算术与括号
.Rule("过温", "电机.温度 > 50", "电机.温度 < 48") // 滞环:恢复表达式,阈值附近抖动不闪报警
.Rule("overheat", "device.temp > 50"); // 英文点名同样适用
client.RuleTriggered += (_, e) => logger.LogWarning("告警 {Device}.{Rule} 触发,由 {Tag} 引起:{Values}",
e.Device.Id, e.Rule.Name, e.TriggeringTag.Name,
string.Join(", ", e.Values.Select(p => $"{p.Key}={p.Value.Value}")));
client.RuleReset += (_, e) => logger.LogInformation("告警 {Device}.{Rule} 恢复", e.Device.Id, e.Rule.Name);
- 表达式:点名(可含
.,中文(电机.温度)与英文(device.temp)皆可,整体一个词,匹配忽略大小写)+ C 风格运算符> >= < <= == != && || ! + - * / ( ),数字 / true / false /"字符串"字面量;含-等其他符号的点名不能在表达式中引用(注册不受影响) - 绑定期静态类型检查:引用不存在的点、类型不匹配(布尔参与算术、字符串比大小)在
Rule()时立即抛出,错误位置精确到字符 - 边沿语义:条件由假变真触发一次
RuleTriggered,由真变假触发一次RuleReset;rule.IsTriggered可查当前态,首轮采集即为真也算上升沿 - 滞环(恢复表达式):第三个参数给恢复条件后,触发态只看恢复条件、成立才恢复——
(">50", "<48")时温度在 49↔51 抖动既不再触发也不假恢复;两个表达式都做绑定期检查,可引用不同点位 - 质量冻结:任一引用点非 Good 质量时不评估——断线旧值既不触发报警,也不假恢复
- 事件携带触发时刻全部引用点的值快照(
e.Values,含质量与时间戳),报警消息不用再回头查值 - 规则引用的点应加入
Poll,否则始终无值不评估
限值报警:两个参数的就进点表
手册里写明的限值("报警温度 80℃")是设备域知识,直接进点表,不用写表达式:
.Register("电机.温度", DataType.F32, 0x2010, o => o
.WithScale(0.1)
.WithHigh(80, hysteresis: 2) // 值 > 80 触发"电机.温度 High",≤ 78 恢复
.WithLow(-10)) // 值 < -10 触发"电机.温度 Low",≥ -10 恢复(默认零滞环)
底层合成为规则(Rule("电机.温度 High", "电机.温度 > 80", "电机.温度 <= 78")),走同一注册通道——边沿、滞环、质量冻结、值快照全部继承,事件照常从 RuleTriggered/RuleReset 出,rule.IsTagLimit 可区分手写/合成。仅数值点;同设 High/Low 时须 High 阈值 > Low 阈值;复合条件("过流且泵停")仍用 Rule。JSON 字段:high / low / hysteresis(同时作用于两者)。
虚拟点(计算点)
物理点不够用时,表达式造点——温差 = 进气 - 出气 一行注册,缓存、事件、规则引用与物理点同一表面:
var dev = client.Device("plc-01", d => d
.Register("设备.进气温度", DataType.F32, 0)
.Register("设备.出气温度", DataType.F32, 2)
.RegisterComputed("设备.进气温差", "设备.进气温度 - 设备.出气温度")
.RegisterComputed("设备.温差指数", "设备.进气温差 * 2")); // 虚拟点可引用虚拟点
dev.Rule("温差超限", "设备.进气温差 > 30"); // 规则引用虚拟点,照常工作
var v = client["plc-01.设备.进气温差"]; // 缓存直读,质量/时间戳齐全
- 表达式语法与规则引擎相同(点名 + C 风格运算符),只能引用已注册的点——先注册者引用不了后注册者,循环引用在注册时天然不可能
- 类型由表达式推断:算术 → F64(double)、比较/逻辑 → Bool、字符串 → String
- 求值时机:任一引用点的值或质量变化即重算(链式虚拟点逐级传播),事件
TagChanged的Reason = Computed - 质量冻结(与规则一致):任一引用点非 Good 时冻结旧值、传播最差质量,全部恢复 Good 才重算;引用点尚无读数时不求值
- 时间戳取引用点最新采集时刻
- 虚拟点不参与
Poll、直读与写回(显式抛错);ReadAllAsync自动跳过虚拟点
WinForms / WPF 使用
全部 I/O 异步、轮询在后台线程,await 即用,不会卡 UI 线程。两个要点:
public partial class MainForm : Form
{
private readonly MbusClient _client = new MbusClient();
private async void MainForm_Load(object? sender, EventArgs e)
{
var ui = MbusUi.Capture(); // 在 UI 线程调用一次,捕获上下文
var dev = _client.Device("plc-01", d => d.Tcp("192.168.1.10").Register("电机.频率", DataType.F32, 1));
// 事件在后台线程触发——经 ui() 封送后可直接摸控件
dev.TagChanged += (_, e) => ui(() => labelFreq.Text = $"{e.NewValue.Value:F1}");
dev.Poll(100, "电机.频率");
await _client.StartAsync();
}
private void timer1_Tick(object? sender, EventArgs e)
=> labelFreq.Text = _client["plc-01.电机.频率"].Value?.ToString(); // 缓存直读,同步即取、断线不抛
private async void MainForm_FormClosed(object? sender, FormClosedEventArgs e)
=> await _client.DisposeAsync(); // 释放串口/TCP——忘了这步,进程活着就一直占口
}
- 缓存索引器
dev[...]/client[...]是纯字典读,UI 定时器里直读即可 SerialPort.Open()本身是同步的(驱动初始化偶尔几十毫秒),敏感场景用Task.Run(() => client.StartAsync())包一层
事件与日志出口
库不做日志,但把钩子收在一个地方:容器场景只订阅 client 一个对象的九个事件,连接/设备/报文/写回/报警全覆盖,运行中新增的连接和设备自动纳入:
client.Trace += (_, e) => logger.LogDebug("{Link} {Dir} unit={Unit} {Frame}",
e.Link!.Id, e.Direction, e.UnitId, BitConverter.ToString(e.Frame.Span));
client.TagChanged += (_, e) => logger.LogInformation("{Device}.{Tag} = {Value} ({Quality})",
e.Device.Id, e.Tag.Name, e.NewValue.Value, e.NewValue.Quality);
client.LinkStateChanged += (_, e) => e.State == LinkState.Connected
? logger.LogInformation("连接 {Link} 已恢复", e.Link.Id)
: logger.LogError("连接 {Link} {State}: {Msg}", e.Link.Id, e.State, e.Message);
client.DeviceStateChanged += (_, e) => logger.LogWarning("设备 {Device} {State}: {Msg}", e.Device.Id, e.State, e.Message);
client.PollFailed += (_, e) => logger.LogWarning(e.Exception, "轮询失败 {Device} @{Period}ms", e.Device.Id, e.PeriodMs);
client.WriteCompleted += (_, e) => e.Success
? logger.LogInformation("写回 {Device}.{Tag} = {Value}", e.Device.Id, e.Tag.Name, e.Value)
: logger.LogError(e.Exception, "写回失败 {Device}.{Tag}", e.Device.Id, e.Tag.Name);
client.RuleTriggered += (_, e) => logger.LogWarning("告警触发 {Device}.{Rule}({Expr}):{Values}",
e.Device.Id, e.Rule.Name, e.Rule.Expression,
string.Join(", ", e.Values.Select(p => $"{p.Key}={p.Value.Value}")));
client.RuleReset += (_, e) => logger.LogInformation("告警恢复 {Device}.{Rule}", e.Device.Id, e.Rule.Name);
| 事件 | 内容 | 日志级别参考 |
|---|---|---|
Trace |
全部连接的原始报文(聚合自各连接,带 Link 上下文) |
Debug |
TagChanged |
值/质量变化(新旧值、时间戳、来源) | Info |
LinkStateChanged |
连接通断(含掉线原因) | 断开 Error / 恢复 Info |
DeviceStateChanged |
unit 级健康(不应答 = Offline) | Warn |
PollFailed |
轮询失败(不中断,自动重试) | Warn |
PollReceived |
每轮成功采集的原始采样帧(值不变也触发,不受死区影响;趋势曲线/落库数据源,非日志用途) | — |
WriteCompleted |
写回审计(成功/失败,失败带异常) | 成功 Info / 失败 Error |
RuleTriggered |
规则触发(表达式由假变真,带引用点值快照) | Warn |
RuleReset |
规则恢复(表达式由真变假) | Info |
单设备直用(不经容器)时订阅 device 上的同名事件,原始报文订阅 device.Link.Master.Trace。
连接级故障由库内部消化为掉线状态,统一从 LinkStateChanged 出来,不需要另订底层故障事件。Trace 无订阅者零开销。
演示程序(WinForms)
demo/Mbus.Demo 开箱即看:本机起 MbusTcpServer 模拟从站(正弦波频率/电流/温度、累计计数、泵状态翻转),主站走真实 TCP 回环轮询,表格实时刷值,可写回设定值与线圈。
dotnet run --project demo/Mbus.Demo # 打开界面,数据实时在动
dotnet run --project demo/Mbus.Demo -- --self-test # 无界面自检(10 项断言,退出码 0/1)
- 内置 4 条演示规则(过流
电机.电流 > 10 && 泵.运行、过温、设定超限、泵故障),触发与恢复实时打印在底部告警日志(含触发时刻的值快照) - 写回
频率设定(> 50 时)后模拟对象的频率会向新设定值靠拢(写回闭环效果) - 写回
泵.运行后保持 15 秒,然后恢复自动翻转 - 断掉从站(停止后重启)可观察质量标记:良好 → 旧值/异常
本地真机环境(TCP 从站服务器)
上真设备前,最后一段可以在本机完成——MbusTcpServer 起一个真 socket 从站,客户端走真实 TCP 回环(真实的分包、缓冲、断连语义,不是内存管道):
// 模拟一台 Modbus TCP 设备
var memory = new MbusSlaveMemory();
var bits = BitConverter.SingleToInt32Bits(50.5f);
memory.SetHolding(0, (ushort)(bits >> 16), (ushort)bits);
await using var server = new MbusTcpServer(502, MbusWire.Tcp, new MbusSlaveResponder(1, MbusWire.Tcp, memory));
await server.StartAsync();
// 模拟串口服务器(RTU over TCP 透传)—— wire 传 Rtu 即可
// new MbusTcpServer(port, MbusWire.Rtu, new MbusSlaveResponder(1, MbusWire.Rtu, memory))
一条服务器上可挂多个从站(多 unit),多个主站可并发连接。测试里用它验证过:全链路读写、RTU 透传 CRC、服务器重启后客户端自动重连恢复、双主站并发。
真机验收清单(拿到设备后 10 分钟)
new MbusDeviceBuilder("x").Tcp(ip).Build()+ReadHoldingRegistersAsync(0, 4)—— 通不通、事务号/CRC 是否如预期(开Trace看帧)- 注册 2-3 个点跑
Poll(100)—— 值是否合理(温度不可能是 1e38 → 字序错了,试DefaultWordOrder(CDAB)) WriteAsync写一个设定值 —— 从设备面板/其他工具核对落值- 拔网线 10 秒再插 —— 状态事件、
LastKnown→ 重连 →Good全链路走一遍 - 串口设备:换
Serial("COM3", 9600),其余代码不变
一条总线多台从站(485 / 网关)
// 串口总线(Mbus.Serial 包)或 TCP 网关后面挂多台 unit,共享一条连接
var bus = client.Link("bus485", l => l.Serial("COM3", 9600).Reconnect(5000));
client.Device("meter-01", d => d.On(bus).Unit(1).Register("流量.瞬时", DataType.F32, 0, o => o.WithScale(0.01)));
client.Device("meter-02", d => d.On(bus).Unit(2).Register("流量.瞬时", DataType.F32, 0, o => o.WithScale(0.01)));
// 同一连接上的事务自动串行排队——半双工总线的硬要求
// 网关走 RTU 透传(而非 Modbus TCP)时,把 Tcp 换成 RtuOverTcp,其余不变:
var gw = client.Link("gw", l => l.RtuOverTcp("192.168.1.20").Reconnect(5000));
无硬件开发 / 自动化测试
var slave = new MbusSlaveResponder(1, MbusWire.Tcp);
slave.Memory.SetHolding(0, 0x1234);
var client = new MbusClient();
var link = client.Link("bus", l => l.Virtual(MbusWire.Tcp, slave)); // 内存总线,可不带硬件
分层
L4 MbusClient / MbusDevice 容器、点表、缓存、事件、轮询调度
L3 ValueCodec / ReadPlanner 类型编解码(字序/scale/bit/字符串)、读合并规划
L2 MbusMaster 串行事务队列、超时重试、全功能码
L1 MbusFrame / Crc16 MBAP / RTU 帧、CRC16
L0 ITransport TCP / 串口(Mbus.Serial) / 虚拟管道
默认值一览
约定优于配置,但约定必须可见。所有默认值的单一事实来源是 MbusDefaults,
运行时也可直接读取生效配置:link.Master.TimeoutMs、link.ReconnectIntervalMs、device.DefaultWordOrder、tag.PollPeriodMs。
| 设置 | 默认值 | 在哪改 |
|---|---|---|
| Modbus TCP 端口 | 502 | Tcp(host, port) |
| TCP 建连超时 | 3000 ms | TcpTransport 构造参数 |
| 应答超时 | 1000 ms | LinkBuilder.Timeout(ms) |
| 超时重试 | 2 次 | LinkBuilder.Retries(n) |
| 掉线重连间隔 | 3000 ms(≤0 关闭) | LinkBuilder.Reconnect(ms) |
| 从站地址 | 1 | DeviceBuilder.Unit(id) |
| 数据区 | Holding | Register(..., area) |
| 字序 | ABCD(协议大端) | 设备级 DefaultWordOrder,点级 WithWordOrder 覆盖 |
| 缩放/偏移 | 无 | WithScale / WithOffset |
| 轮询周期下限 | 10 ms | 协议库硬限制 |
| 读合并间隔 | ≤8 个寄存器的空洞并入同一次读 | MbusDefaults.MaxReadGap |
| 单次读上限 | 125 寄存器 / 2000 位 | Modbus 协议规范,不可配置 |
| 串口 | 9600-8-N-1 | Serial(port, baud, dataBits, parity, stopBits) |
不可配置的行为约定:
- 地址一律 0 基(40001 风格需自行换算)
- 名称含
.时前缀为类别;点名匹配忽略大小写 - 带 Scale/Offset 的点读出统一为
double(整数类型换算后取整会丢尾数) - 字符串点位:ASCII、每寄存器 2 字符高位在前、读出时去尾部
\0 - Bool 挂寄存器区不带
WithBit= 整个寄存器非 0 即真;Input/Discrete 区强制只读 - 注册期地址校验:同区内寄存器区间不得相交;Bool+BitIndex 共用寄存器时位号不得重复;地址 + 占用寄存器数超出 0-65535 拒绝
- JSON 点表字段默认值:
area= holding、readOnly= false、length= 2,其余可选字段默认同Register
已知语义
- 32 位以上值写回单事务完成,不会半写;寄存器位写走读-改-写(同一连接内串行,跨设备同地址写有理论竞态)
- 断线后缓存返回旧值 +
Quality.LastKnown;超时/异常应答 →Quality.Bad;轮询自动重试 - TCP 迟到的旧事务应答会被跳过;RTU 无事务号,超时重试后旧应答无法区分(协议本身的限制)
- 主动
Link.DisconnectAsync()视为"人为断开",不自动重连;连接级故障(对端断开)自动重连 - JSON 点表文件内的错误聚合为一条异常报出;与代码已注册点位的冲突在
LoadTags/ApplyTo时立即抛出 - 规则数值统一按
double比较(U64 超过 2^53 精度受限);字符串==/!=区分大小写 - 规则在轮询线程的
TagChanged链内同步评估,事件处理应保持轻量(与其他事件约定一致,UI 场景经MbusUi.Capture()封送) - 撕裂语义:同一次轮询内各点位逐个更新、规则逐点即时评估——跨点位的组合条件可能短暂看到"新值 + 旧值"的混合快照;对瞬时组合敏感的条件可先聚合到虚拟点再比较
- 变化死区(
WithDeadband,仅数值点):轮询值与上次上报值之差小于死区视为未变化(不更新缓存、不发事件,慢漂移可累积越过阈值);直读、写回、质量变化不受死区影响
路线图
-
TCP 从站服务器(已完成,MbusTcpServer) -
NuGet 打包(已完成:Mbus/Mbus.Serial,netstandard2.0 + net8.0 双目标) -
JSON 点表配置 + 加载期校验(已完成:LoadTagsFromFile/MbusTagTable,地址冲突/越界/类型与区域匹配,错误聚合报告) -
报警限(AlarmLow/AlarmHigh)(由规则引擎覆盖并超出:Rule("过热", "电机.温度 > 50"),多参数表达式、边沿触发/恢复、质量冻结) - 真机验证矩阵(Modbus TCP PLC / 串口仪表 / 串口服务器透传)
许可证
| 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 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. |
| .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 was computed. 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. |
-
.NETStandard 2.0
- Microsoft.Bcl.AsyncInterfaces (>= 8.0.0)
- System.Memory (>= 4.5.5)
- System.Text.Json (>= 8.0.5)
- System.Threading.Channels (>= 8.0.0)
-
net8.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Mbus:
| Package | Downloads |
|---|---|
|
Mbus.Serial
Mbus 的串口(Modbus RTU)传输扩展。目标 netstandard2.0(.NET Framework 4.6.2+)与 net8.0。 |
GitHub repositories
This package is not used by any popular GitHub repositories.