You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Modbus如何读取非连续地址?基于EasyModbus的批量高效读取咨询

非连续Modbus地址批量读取优化方案

核心思路

Modbus单请求的开销远大于读取少量冗余地址的开销,所以优先把分散的非连续地址聚合成尽可能少的连续地址块,减少请求总次数,再从返回结果中提取目标地址的值即可。

具体实现步骤

1. 非连续地址分组聚合

  • 先把所有需要读取的地址去重、从小到大排序
  • 遍历排序后的地址,将地址间隔小于预设阈值(推荐设置为3~8,可根据实际场景调整)的地址归为同一连续块,同时保证每个块的总长度不超过125个保持寄存器(符合Modbus TCP单帧最大260字节的限制)
  • 如果两个目标地址间隔过大(超过阈值),单独拆分为新的分组,避免读取过多无用地址浪费带宽

举个实际场景的例子:目标地址为2,5,7,20,21,40,阈值设为5,那么会聚合为三个分组:[2-7](长度6)、[20-21](长度2)、[40-40](长度1),总请求次数从6次降到3次,效率提升非常明显。

2. 批量读取后提取目标值

每个连续块读取完成后,根据目标地址和块起始地址的偏移量,从返回的数组中提取对应的值即可,无用的冗余地址直接丢弃。

3. 可选优化:并行请求

如果Modbus服务器支持并发连接/并发请求,可以把所有聚合后的分组用异步并行的方式发起请求,进一步降低总耗时。

基于EasyModbus的代码示例

using EasyModbus;
using System.Collections.Generic;
using System.Linq;

// 地址分组类
public class AddressBlock
{
    public int StartAddress { get; set; }
    public int Length { get; set; }
    public List<int> TargetAddresses { get; set; } = new List<int>();
}

public class ModbusReader
{
    // 批量读取非连续保持寄存器
    public Dictionary<int, int> ReadDiscreteHoldingRegisters(string ip, int port, List<int> targetAddresses, int gapThreshold = 5)
    {
        var result = new Dictionary<int, int>();
        if (targetAddresses == null || !targetAddresses.Any()) return result;

        // 1. 地址去重排序
        var sortedAddresses = targetAddresses.Distinct().OrderBy(x => x).ToList();
        var addressBlocks = new List<AddressBlock>();
        var currentBlock = new AddressBlock { StartAddress = sortedAddresses[0], TargetAddresses = { sortedAddresses[0] }, Length = 1 };

        // 2. 聚合分组
        foreach (var addr in sortedAddresses.Skip(1))
        {
            var currentEnd = currentBlock.StartAddress + currentBlock.Length - 1;
            // 满足两个条件:间隔小于阈值,且总长度不超过125
            if (addr - currentEnd <= gapThreshold && (addr - currentBlock.StartAddress + 1) <= 125)
            {
                currentBlock.Length = addr - currentBlock.StartAddress + 1;
                currentBlock.TargetAddresses.Add(addr);
            }
            else
            {
                addressBlocks.Add(currentBlock);
                currentBlock = new AddressBlock { StartAddress = addr, TargetAddresses = { addr }, Length = 1 };
            }
        }
        addressBlocks.Add(currentBlock);

        // 3. 批量读取
        var modbusClient = new ModbusClient(ip, port);
        modbusClient.Connect();
        foreach (var block in addressBlocks)
        {
            var blockValues = modbusClient.ReadHoldingRegisters(block.StartAddress, block.Length);
            // 提取目标地址的值
            foreach (var targetAddr in block.TargetAddresses)
            {
                var offset = targetAddr - block.StartAddress;
                result[targetAddr] = blockValues[offset];
            }
        }
        modbusClient.Disconnect();

        return result;
    }

    // 使用示例
    static void Main(string[] args)
    {
        var reader = new ModbusReader();
        // 要读的非连续地址示例
        var targetAddrs = new List<int> { 2,5,7,20,21,40,100,102,105 };
        var result = reader.ReadDiscreteHoldingRegisters("127.0.0.1", 502, targetAddrs);
        
        // 输出结果
        foreach (var kv in result)
        {
            Console.WriteLine($"地址{kv.Key}的值:{kv.Value}");
        }

        Console.Write("Press any key to continue . . . ");
        Console.ReadKey(true);
    }
}

注意事项

  • 阈值可以根据实际场景调整:如果地址普遍非常分散,调小阈值减少冗余读取;如果地址普遍间隔小,调大阈值减少请求次数
  • 如果服务器对请求频率有限制,不要用太高的并行度,避免被服务器拒绝请求
  • 可以增加失败重试逻辑,单个块读取失败时可以先重试,重试失败再拆分为更小的块或者单个地址读取,提升稳定性

内容的提问来源于stack exchange,提问作者Wihelm jie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.19 13:24:08