(十一)YModbus CLI命令行工具使用
GitHub 项目地址:https://github.com/lidecong133/YModbus
写库的时候,很多例子都是 C# 代码。
但现场排查时,你不一定想新建一个项目,也不一定想打开 Visual Studio。很多时候只是想确认一句话:
这个设备现在到底能不能读?
这时候命令行工具就很顺手。
YModbus.Cli的定位不是替代主站调试软件,而是提供一个可以复制、可以脚本化、可以被自动化工具调用的入口。
先跑一个最小读取
开发阶段可以直接用dotnet run。
dotnet run--project.\src\YModbus.Cli\YModbus.Cli.csproj--read-holding-registers--host 127.0.0.1--port 1502--unit-id 1--address 0--quantity 2注意中间的--。
前面是dotnet run的参数,后面才是传给 YModbus.Cli 的参数。
如果你已经发布或安装成ymodbus命令,命令会更短:
ymodbusread-holding-registers--host 127.0.0.1--port 1502--unit-id 1--address 0--quantity 2我一般第一次测试只读 1 个或 2 个寄存器。数量小,问题更容易判断。读通以后,再扩大范围。
CLI输出为什么用JSON
读保持寄存器时,CLI 会返回类似这样的结果:
{"success":true,"operation":"read-holding-registers","transport":"tcp","endpoint":"127.0.0.1:1502","unitId":1,"address":0,"quantity":2,"values":[1234,5678],"hexValues":["0x04D2","0x162E"]}这个格式对人也能看,对脚本也好处理。
比如你想在 PowerShell 里判断读取是否成功,可以把 JSON 转成对象。以后做批量测试、自动报告、AI Agent 调用,也不用再去解析一堆控制台文本。
十六进制也很重要。现场看状态字时,0x0005比十进制5更直观,因为你能马上想到 bit0 和 bit2 置位。
TCP、RTU、ASCII怎么写
TCP 最常见:
ymodbusread-holding-registers--transport tcp--host 192.168.1.10--port 502--unit-id 1--address 0--quantity 10RTU 要带串口参数:
ymodbusread-holding-registers--transport rtu--serial-port COM3--baud-rate 9600--data-bits 8--parity none--stop-bitsone--slave-id 1--address 0--quantity 10ASCII 只是 transport 和串口参数不同:
ymodbusread-coils--transport ascii--serial-port COM3--baud-rate 9600--data-bits 7--parity even--stop-bitsone--slave-id 1--address 0--quantity 8如果设备手册只写 RS485 Modbus,大多数时候是 RTU。只有明确写 Modbus ASCII,才按 ASCII 处理。
一个很实用的小场景:判断地址差1
手册写40001 当前值,你不知道程序里填0还是1。
可以先用 CLI 快速试两次:
ymodbusread-holding-registers--host 192.168.1.10--port 502--unit-id 1--address 0--quantity 1 ymodbusread-holding-registers--host 192.168.1.10--port 502--unit-id 1--address 1--quantity 1如果地址0有值,地址1也有值,还不能直接下结论。最好找一个设备屏幕上能看到的已知值,比如温度、压力、计数器,再对照哪个地址更像。
这比在主程序里改来改去方便很多。
写入为什么默认dry-run
CLI 的写命令默认不真正写设备。
比如:
ymodbuswrite-single-register--host 127.0.0.1--port 1502--unit-id 1--address 0--value 123不加--confirm,它按 dry-run 处理。
真正写入要明确加:
ymodbuswrite-single-register--host 127.0.0.1--port 1502--unit-id 1--address 0--value 123--confirm这个设计看起来啰嗦,但我觉得是必要的。
现场复制命令很容易复制错。读错最多是结果不对,写错可能真的改变设备参数。让写操作必须显式确认,可以挡住一部分低级事故。
批量写也是一样:
ymodbuswrite-registers--host 127.0.0.1--port 1502--unit-id 1--address 0--values 1,2,0x0003--confirm ymodbuswrite-coils--host 127.0.0.1--port 1502--unit-id 1--address 0--values 1,0,true,false--confirm第一次建议先对 YModbus 从站模拟器写。确认写入逻辑和地址没问题,再考虑真实设备。
扫描站号别贪大
不知道 UnitId / SlaveId 时,可以扫小范围:
ymodbus scan-units--host 192.168.1.10--port 502--start-unit-id 1--end-unit-id 10--function03--address 0--quantity 1我不建议一上来扫1..247。
如果每个站号超时 1 秒,扫完整个范围要等很久。设备慢一点、网关再慢一点,现场人会以为软件卡死了。
更好的做法是先根据设备拨码、手册、网关配置,缩小范围。比如客户说大概是 1 到 8,那就先扫 1 到 8。
扫地址也要克制
站号确定,但不知道地址,可以用:
ymodbus scan-addresses--unit-id 1--function03--address 0--end-address 100--quantity 1这个适合判断某段地址是否可读。
但不要拿真实设备盲扫大范围。尤其是老设备和串口网关,连续请求太密可能让设备响应变慢,甚至影响现场业务。
我的习惯是:先找手册上最可能的几段地址,小范围扫,确认以后再做完整地址表。
厂家私有功能码
有些厂家会给私有功能码。
YModbus.Cli 提供custom:
ymodbus custom--unit-id 1--function-code 65--payload 01-02-03--response-bytes 2RTU / ASCII 自定义功能码一定要知道响应长度,或者知道它的长度规则。因为 RTU 帧本身没有 TCP 那种 MBAP length 字段,不告诉工具读多少,很难可靠判断一帧到哪里结束。
我会怎么用CLI
现场排查时,我一般这样用:
- 先读一个保持寄存器,确认链路通不通
- 再试
03和04,确认数据区 - 再试地址
0和1,判断地址基准 - 如果是网关,再扫小范围 UnitId
- 确认读没问题以后,再考虑写
- 写操作先对从站模拟器做,再对真实设备做
CLI 的价值就在这里:每一步都有一条命令,每一步结果都能保留下来。
它不像手动点界面那样容易忘记刚才改了什么。
