.NET Core使用libplctag批量读取AB Micro 820 PLC所有标签值方法
最优实现方案
@tags 是PLC内置的标签枚举接口,设计上仅返回标签元数据(ID、类型、长度、维度),不支持同步返回实时值,不用在这个接口上找额外配置。
针对Micro820、500个标签的采集场景,用libplctag自带的TagGroup批量读能力即可实现单连接下的高效采集,局域网环境下全量标签读取耗时通常在100~300ms,完全满足实时图表展示的要求,性能比逐标签循环读高10倍以上。
实现逻辑
libplctag底层会自动对同网关、同PLC参数的标签复用同一个EIP连接,不需要手动维护连接;TagGroup会自动把组内所有标签的读请求合并打包为最少数量的CIP报文,同时自动适配Micro820的单报文长度限制做拆分重组,不需要手动计算内存偏移。
步骤1:初始化阶段做标签预处理
程序启动时先调用@tags接口拉取全量标签元数据,提前做类型映射过滤:
- 0xC1 对应BOOL类型
- 0xC2 对应SINT(sbyte)
- 0xC3 对应INT(short)
- 0xC4 对应DINT(int)
- 0xCA 对应REAL(float)
- 0xD0 对应STRING
过滤掉不需要采集的系统保留标签、未使用的标签,避免无效读取增加耗时。
步骤2:初始化全局TagGroup
不要每次读取都新建标签实例,全局初始化一次TagGroup,把所有待采集的标签按对应类型加入组,整个生命周期复用:
// 全局单例,整个后台任务常驻复用 private readonly TagGroup _plcTagGroup = new TagGroup(); private const string PlcGateway = "10.29.56.31"; // 初始化方法,程序启动时执行一次 public void InitPlcTags() { // 拉取标签元数据,复用现有读@tags的逻辑即可 var tagMetaList = GetTagMetadataFromPlc(); foreach (var meta in tagMetaList) { ITag tag = meta.Type switch { 0xC1 => new Tag<BoolPlcMapper, bool> { Gateway = PlcGateway, PlcType = PlcType.Micro800, Protocol = Protocol.ab_eip, Name = meta.Name, Timeout = TimeSpan.FromSeconds(2) }, 0xC3 => new Tag<IntPlcMapper, short> { Gateway = PlcGateway, PlcType = PlcType.Micro800, Protocol = Protocol.ab_eip, Name = meta.Name, Timeout = TimeSpan.FromSeconds(2) }, 0xC4 => new Tag<DintPlcMapper, int> { Gateway = PlcGateway, PlcType = PlcType.Micro800, Protocol = Protocol.ab_eip, Name = meta.Name, Timeout = TimeSpan.FromSeconds(2) }, 0xCA => new Tag<RealPlcMapper, float> { Gateway = PlcGateway, PlcType = PlcType.Micro800, Protocol = Protocol.ab_eip, Name = meta.Name, Timeout = TimeSpan.FromSeconds(2) }, _ => null }; if (tag != null) _plcTagGroup.Add(tag); } }
步骤3:后台任务循环批量读取
在你的BackgroundService循环里直接调用ReadAll()方法,执行完成后组内所有标签的Value属性就是最新实时值,不需要逐个发起读请求:
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { InitPlcTags(); while (!stoppingToken.IsCancellationRequested) { try { // 单次调用完成全量标签读取 _plcTagGroup.ReadAll(); // 采集所有标签的当前值 var tagValues = new Dictionary<string, object>(); foreach (dynamic tag in _plcTagGroup.Tags) { tagValues[tag.Name] = tag.Value; } // 推送到前端,比如用SignalR推送即可实现实时图表更新 await _hubContext.Clients.All.SendAsync("ReceivePlcData", tagValues, stoppingToken); } catch (Exception ex) { _logger.LogError(ex, "PLC标签读取异常"); } // 按实际需求设置采集间隔,1s间隔完全无压力 await Task.Delay(1000, stoppingToken); } }
避坑说明
- 不要每次循环都新建Tag、TagGroup实例,反复初始化会触发连接重建、标签句柄重新申请,性能会下降90%以上,还容易触发Micro820的连接数上限(Micro820默认仅支持4~8个并发CIP连接)
- 不要多线程并发调用读方法,单线程循环调用
ReadAll()的性能已经足够跑满PLC的通信带宽,并发反而会因为连接争抢导致耗时飙升 - 数组类标签不要拆成单个元素逐个加组,按数组总长度创建单个标签实例读取,性能比逐元素读高几个数量级
- Micro800系列不需要配置Path参数,保持默认即可,加了Path反而会导致连接失败
- 逐标签读取慢的核心原因是每个读请求都需要一次网络往返,就算局域网单次往返仅2ms,500个标签串行读也需要1s以上,批量读会把所有请求打包为3~5个报文,总耗时仅为几次网络往返的时间,差距非常明显。
内容的提问来源于stack exchange,提问作者Jesus Sanchez
相关产品推荐
相关产品推荐

