当前位置: 首页 > news >正文

(十)多UnitId模拟:一个网关下面挂多个从站怎么测

GitHub 项目地址:https://github.com/lidecong133/YModbus

单个从站模拟能解决很多问题,但还不够。

现场经常遇到这种结构:一台 TCP 网关下面挂多台 RTU 仪表,或者一条 RS485 总线上挂了多个从站。主站连的 IP、端口或串口都一样,变化的是 UnitId / SlaveId。

这时候要测试主站轮询逻辑,就需要多站号模拟。

YModbus 里对应的是:

  • ModbusTcpSlaveNetwork
  • ModbusRtuSlaveNetwork
  • ModbusAsciiSlaveNetwork

UnitId和SlaveId先别混

TCP 里常叫 Unit ID。

RTU / ASCII 里常叫 Slave ID。

名字不同,本质都是目标站号。

TCP 网关场景下,host + port只是连接到网关,UnitId 才是网关后面的设备。

RTU 总线场景下,串口参数只是打开总线,SlaveId 才是总线上的目标设备。

这个概念没分清,多站号调试一定会乱。

TCP多UnitId示例

下面这个例子在本机127.0.0.1:1502上模拟两个 UnitId。

usingSystem.Net;usingYModbus.Slave;ModbusSlaveDataStoreunit1Store=new(pointCount:100);unit1Store.SetHoldingRegister(0,1234);unit1Store.SetHoldingRegister(1,5678);ModbusSlaveDataStoreunit2Store=new(pointCount:100);unit2Store.SetHoldingRegister(0,2222);unit2Store.SetHoldingRegister(1,3333);awaitusingModbusTcpSlaveNetworknetwork=new(newModbusTcpSlaveNetworkOptions{ListenAddress=IPAddress.Loopback,Port=1502});network.AddSlave(newModbusSlaveDefinition{UnitId=1,PointCount=100},unit1Store);network.AddSlave(newModbusSlaveDefinition{UnitId=2,PointCount=100},unit2Store);awaitnetwork.StartAsync();Console.WriteLine("TCP slave network is running on 127.0.0.1:1502.");Console.ReadLine();awaitnetwork.StopAsync();

主站读 1 号站,返回12345678

主站读 2 号站,返回22223333

同一个端口,不同 UnitId,数据区不同。这就是 TCP 网关测试的核心。

主站侧用MultiUnitClient更顺

这种场景主站侧建议用ModbusMultiUnitClient

usingYModbus.Clients;awaitusingModbusMultiUnitClientclient=awaitModbusClientFactory.CreateTcpMultiUnitAsync(host:"127.0.0.1",port:1502);ushort[]unit1=awaitclient.ReadHoldingRegistersAsync(1,0,2);ushort[]unit2=awaitclient.ReadHoldingRegistersAsync(2,0,2);

第一个参数就是 UnitId。

这样代码和现场结构能对上:同一个网关,多个站号。

运行中修改某个站号的数据

测试主站轮询时,经常要模拟某个设备数据变化。

可以通过TryGetDataStore拿到对应站号的数据区:

if(network.TryGetDataStore(1,outIModbusSlaveDataStore?store)&&storeisnotnull){store.SetHoldingRegister(0,9999);}

主站下一轮读 UnitId 1,就会读到新值。

这可以模拟温度变化、报警置位、计数器增加。比拿真实设备反复造条件方便很多。

RTU多SlaveId

RTU 多站号网络使用同一个串口通道。

usingSystem.IO.Ports;usingYModbus.Serial;usingYModbus.Slave;usingSerialPortport=new("COM3",9600,Parity.None,8,StopBits.One);port.Open();awaitusingModbusRtuSlaveNetworknetwork=new(newSerialPortChannel(port,leaveOpen:true));network.AddSlave(newModbusSlaveDefinition{SlaveId=1,PointCount=100});network.AddSlave(newModbusSlaveDefinition{SlaveId=2,PointCount=100});awaitnetwork.StartAsync();Console.ReadLine();awaitnetwork.StopAsync();

RTU 请求的第一个字节就是 SlaveId。网络会按这个字节把请求路由到不同数据区。

ASCII 多站号写法类似,只是换成ModbusAsciiSlaveNetwork

awaitusingModbusAsciiSlaveNetworknetwork=new(newSerialPortChannel(port,leaveOpen:true));network.AddSlave(newModbusSlaveDefinition{SlaveId=1,PointCount=100});network.AddSlave(newModbusSlaveDefinition{SlaveId=2,PointCount=100});awaitnetwork.StartAsync();

多站号最该看报文

多站号出错时,最先看的不是寄存器值,而是请求里的站号。

TCP 请求里,UnitId 会出现在 MBAP 后面。RTU 请求里,第一个字节就是 SlaveId。

如果你以为主站在读 2 号站,但从站报文里看到的是 1 号站,那问题就很明确。

可以给 network 挂 Traffic 事件:

network.Traffic+=(_,traffic)=>{Console.WriteLine($"{traffic.Direction}{traffic.Message}{Convert.ToHexString(traffic.Frame)}");};

主站和从站两边都看报文,能少很多猜测。

适合做自动化回归

多 UnitId 模拟还有一个很实用的用途:给主站程序做回归测试。

你可以固定一套模拟环境:

UnitId 1: 温度设备 UnitId 2: 压力设备 UnitId 3: IO 模块

每个设备放一组固定数据。主站跑一轮轮询,检查解析结果。

以后改主站代码,再跑同一套模拟环境,就能知道有没有把多站号逻辑改坏。

到这里

单个从站解决“模拟一个设备”。

多 UnitId / 多 SlaveId 网络解决“模拟一组设备”。

如果你的项目里有 TCP 网关、RS485 多仪表轮询、多设备采集,建议尽早用这套方式把测试环境搭起来。

http://www.cnnetsun.cn/news/2909828.html

相关文章:

  • Redis 从入门到精通:Redis Sentinel 哨兵
  • 星露谷物语SMAPI终极指南:5分钟学会安全安装模组框架
  • 英雄联盟自动化工具终极指南:如何通过智能助手提升60%游戏效率
  • 5分钟快速上手:使用Scrapy-pinduoduo构建专业级拼多多数据采集系统
  • 别再手动删点了!用Python的RDP算法5分钟搞定轨迹/轮廓简化(附Shapely避坑指南)
  • 从地图App的流畅缩放,到游戏模型的轻量加载:聊聊Ramer-Douglas-Peucker算法背后的工程智慧
  • MC68341芯片选与RTC配置实战:从寄存器原理到嵌入式系统稳定设计
  • 别被坑了!2026实测好用的AI论文写作工具|实测必入避坑版
  • 别再手动维护字典了!用Python装饰器实现一个自己的Registry注册器(附完整代码)
  • 抖音内容下载终极指南:从零搭建自动采集系统的完整方案
  • 深入解析NXP KE1x系列PCC外设时钟控制器:原理、配置与低功耗实践
  • 实战指南:用Python的巴特沃斯滤波器,给你的传感器数据(比如Arduino或树莓派采集的)降降噪
  • 从你家墙上的220V到手机充电器:RMS电压到底是怎么影响我们日常用电的?
  • 终端与IDE形态的vibe coding实测:两款AI编程工具迭代能力对比
  • 从“表面相似“到“语义匹配“:BERTScore如何重塑你的文本评估体验?
  • 中国大模型价格战背后的AI基础设施重构
  • 高层次综合设计乒乓buffer(double-buffer/pingpong-buffer)
  • MC68349串口驱动与JTAG边界扫描实战:嵌入式通信与硬件调试核心技术解析
  • NSK双滑块定位承载装置技术手册
  • APK Installer:在Windows电脑上运行安卓应用的终极指南
  • 手把手复现:用Python仿真验证电容容抗公式1/(j*2*pi*f*C),附代码与波形分析
  • 豆包暴跌610万用户的真相:AI产品免费模式的死亡螺旋与破局路径
  • “泄露了windows12“
  • 从PCL/VTK迁移到C#/Halcon?手把手教你用ActiViz.NET实现三维点云可视化(避坑指南)
  • DSGE模型终极指南:如何从零开始掌握宏观经济建模的40个经典案例
  • FUXA工业可视化平台实战指南:快速构建专业级SCADA监控系统
  • Cursor Free VIP:破解AI编程助手限制的技术实现与深度应用指南
  • 别再只记结论了!通过5个PyTorch代码实验,亲手验证model.eval()与torch.no_grad()的真实影响
  • CAN FD协议升级?手把手教你用FPGA实现更高带宽的车载通信节点
  • 从审核员视角看漏洞:拆解CNVD收录标准,理解安全风险的‘轻重缓急’