『7x24小时有问必答』
项目刚上线,5台PLC,串行轮询,一轮采集800毫秒。
操作工反馈顺畅,数据实时刷新,领导看了也点头——挺好,没啥问题。
然后设备加到了20台。
代码一行没改。但一轮轮询从800毫秒直接飙到3.2秒。操作工点完确认,屏幕纹丝不动,又点了一下——这次触发了两次提交。
再后来,50台。
通讯超时满天飞,采集服务疯狂丢数据,MES里的数据断断续续,完全不可用。
代码没改过,Bug排查了三天三夜,一行问题都没有。
真正的凶手,不是代码——是采集模型本身。

设备扩到20台,代码一行没动。
但一轮轮询从800毫秒直接飙到3.2秒。
3.2秒是什么概念?操作工点完确认键,屏幕纹丝不动——他以为没点着,又点了一下。结果两次都触发了,系统里冒出两条重复记录。
类似的场景在车间里反复上演:按钮多按一次、阀门多开一次、批次号重复一次……每一次"重复提交",都是通讯延迟埋下的雷

直到设备扩到50台,问题彻底炸了。
PLC通讯大面积超时,采集服务像漏水的筛子,数据一条接一条地丢。产线上操作工看着屏幕上的空白,MES系统里全是断断续续的残缺记录——这批数据,完全不可用。
团队花了三天逐行排查,代码翻了个底朝天,结论是:没有Bug。
问题从来不在代码里。
罪魁祸首是串行轮询这个采集模型本身——它的基因里就写着"设备越多,我越慢"。5台时岁月静好,50台时先天不足。

串行轮询 = 用最慢的那台设备,拖垮整个系统。

设备越加越多,延迟线性增长,这是顺序轮询的天花板。

为什么顺序轮询一定会越来越慢
假设单台 PLC 平均响应 160ms,串行轮询总耗时 = 设备台数 ×160ms:

5 台 → 5 × 160ms =  800ms,采集周期短,系统响应流畅,完全满足现场使用;

20 台 → 20 × 160ms =  3.2s,操作交互延迟明显,现场操作工频繁反馈卡顿;

50 台 → 50 × 160ms =  8s,系统采集效率严重不足。
理论计算还只是"最理想情况"。实际工况下,只会更糟:
现场网络抖动,响应时间忽快忽慢
老旧PLC或远距离设备,响应天然慢半拍
通讯超时后程序自动重试,又多等一轮
最致命的是——串行队列是"一个卡,全停"。
只要有一台PLC卡顿2秒,后面所有设备都得干等着。50台设备,一台掉链子,整条链路全部被拖慢。就像50个人排队过安检,一个人翻包翻了半天,后面49个人只能干站着。

说白了,串行轮询是十年前单线程时代的产物。
那时候现场就几台设备,排队读一遍,绰绰有余。但现在呢?几十台PLC同时要数据,几十路信号要实时刷新——你还在一台一台等?
这套模型的设计初衷,就不是为并发而生的。
设备规模上去了,底层采集模型不跟着升级,崩只是时间问题。

方法一:并发轮询——立竿见影
最直接的优化:串行改并发。
串行是一台一台排队读,20台等20遍;并发是所有PLC同时发请求,谁先回来谁先处理。总时间不再是20台的总和,而是取决于最慢那台——就像20个窗口同时开饭,你只需要等最慢那个打饭师傅。
原来的写法(串行):
// 20 台 PLC,一台一台等,总时间 = 所有台的总和foreach  (var  plc  in  plcs){       var  data =  await  plc.ReadAsync();      Process(data);}
改成并发:
// 20 台同时发请求,总时间 = 最慢那台的时间var  tasks = plcs.Select(async  plc =>{       var  data =  await  plc.ReadAsync();       Process(data);});await  Task.WhenAll(tasks);
改造后效果立竿见影:20台PLC的采集延迟从3.2秒直降到约200ms。但别急着高兴——并发不是万能的,有两个坑踩一个就翻车。

坑一:不能无限并发

50台PLC同一秒全发请求,网络瞬间峰值飙到峰值,交换机扛不住直接丢包——你以为在提速,实际上在制造拥塞。
必须用信号量限制并发数:

坑二:老PLC扛不住高并发

并发开得越大越好?天真了。
部分老款PLC、串口转TCP的转换器,处理能力极其有限。你这边10个请求同时打过去,它接不住——轻则响应变慢,重则直接卡死、自动重启。你以为是网络问题,查半天,最后发现是设备本身被"并发"打懵了。
落地建议:  并发数从5开始逐步往上加,边加边观察设备状态。现场设备越老、型号越杂,并发上限越低。切勿一上来就拉满20、50——那不是优化,是压力测试。

方法二:分级采集——最被低估的优化
不少项目有个通病:所有测点统一采集周期,一视同仁。
这是错的,也是浪费。
报警信号要100ms响应,温度趋势10秒刷一次都够用。你把它们塞进同一个循环,高频在等低频,低频在拖高频——所有人都被拉到了最慢那个节奏
就像公司里,紧急邮件要秒回,月度报表可以慢慢写。但老板非要你每封邮件都按写报表的速度来处理——效率从哪来?
高优先级
报警信号、按钮、急停
100ms
中优先级
运行状态、计数器、工步
500ms
低优先级
温度、电流、设备参数
1s~10s
// 三组测点,三个独立循环,互不干扰var  highPriority = points.Where(p  =>  p.Level  ==  1);var  midPriority   = points.Where(p  =>  p.Level  ==  2);var  lowPriority   = points.Where(p  =>  p.Level  ==  3);_ =  Task.Run(() =>  PollLoopAsync(highPriority,  intervalMs:  100,   ct));_ =  Task.Run(() =>  PollLoopAsync(midPriority,   intervalMs:  500,   ct));_ =  Task.Run(() =>  PollLoopAsync(lowPriority,   intervalMs:  5000, ct));
实际项目里,分级采集能让轮询总压力下降 50%~80%。因为大多数测点都是低频的,高频轮询只针对少数关键信号。

方法三:变化检测——减少无效写入
你会发现一个现实:大部分采集到的数据,和上一次相比没有变化。温度还是 23.1,压力还是 47.3,设备还在运行。但你还是在每个周期都写入 MES,既浪费资源,也增加了数据库的压力。
只处理"有意义的变化":
// 变化超过阈值才写入 MESif  (Math.Abs(newValue - lastValue) > threshold){       WriteToMes(newValue);      lastValue = newValue;}// 或者直接接入上一篇的 DataQualityChecker// Frozen 状态 = 值没有变化,不写入var  quality = checker.Check(newValue);if  (quality ==  DataQuality.Good)       WriteToMes(newValue);
变化检测还有一个好处:上一篇的数据质量检测天然集成进来了——值冻结就是"没有有意义的变化",一套逻辑同时解决两个问题。
完整调度器:三个方法合在一起
public  class  PlcPollingScheduler{       private  readonly  SemaphoreSlim _semaphore;       private  readonly  int  _intervalMs;       public  PlcPollingScheduler(int  maxConcurrency =  10,  int  intervalMs =  200)      {            _semaphore   =  new  SemaphoreSlim(maxConcurrency);            _intervalMs = intervalMs;      }       ///  并发轮询一组 PLC,内置变化检测和数据质量过滤       public  async  Task  PollOnceAsync(            IEnumerable<plcpoint> points,            CancellationToken ct)      {             var  tasks = points.Select(async  point =>            {                   await  _semaphore.WaitAsync(ct);                   try                  {                         double  value;                         try                        {                               value  =  await  point.Device.ReadAsync(point.Address, ct);                        }                         catch  (Exception ex)                        {                               // 单台读取失败,记录日志,不影响其他设备                              Console.WriteLine($"[读取失败]  {point.Name}:  {ex.Message}");                               return;                        }                         // 数据质量检测(接上一篇 DataQualityChecker)                         var  quality = point.Checker.Check(value);                         if  (quality != DataQuality.Good)  return;                         // 变化检测:变化超过阈值才写入                         if  (Math.Abs(value  - point.LastValue) < point.ChangeThreshold)                               return;                        point.LastValue =  value;                         await  point.MesWriter.WriteAsync(point.Name,  value);                  }                   finally                  {                        _semaphore.Release();                  }            });             await  Task.WhenAll(tasks);      }       ///  持续轮询循环       public  async  Task  RunAsync(            IEnumerable<plcpoint> points,            CancellationToken ct)      {             while  (!ct.IsCancellationRequested)            {                   var  sw = Stopwatch.StartNew();                   await  PollOnceAsync(points, ct);                  sw.Stop();                   // 扣除已花时间,保证轮询间隔稳定                   var  remaining = _intervalMs - (int)sw.ElapsedMilliseconds;                   if  (remaining >  0)                         await  Task.Delay(remaining, ct)                                       .ContinueWith(_ => { });            }      }}// 使用:三组分级,各自独立跑var  highScheduler =  new  PlcPollingScheduler(maxConcurrency:  10, intervalMs:  100);var  midScheduler   =  new  PlcPollingScheduler(maxConcurrency:  10, intervalMs:  500);var  lowScheduler   =  new  PlcPollingScheduler(maxConcurrency:  5,   intervalMs:  5000);_ = highScheduler.RunAsync(highPriorityPoints, ct);_ = midScheduler.RunAsync(midPriorityPoints,   ct);_ = lowScheduler.RunAsync(lowPriorityPoints,   ct);

---

三个方法组合的实际效果

20 台 PLC,优化前延迟稳定在 3 秒以上。加了并发轮询、分级采集、变化过滤三层之后:
并发轮询:3.2s → ~200ms

分级采集:轮询总压力下降 ~70%

变化过滤:MES 写入量下降 ~60%

最终延迟:稳定在  150ms ~ 300ms
采集慢,不是因为你写得慢。而是你还在用串行思维解决并发问题。模型换了,延迟自然就下去了。

往期推荐

</plcpoint></plcpoint>

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

上一主题上一主题         下一主题下一主题
QQ手机版小黑屋粤ICP备17165530号

关于我们·投诉举报· 用户帮助· 联系我们 · 本站服务 · 版权声明· 隐私政策 · 投搞指南

法律保护:PLC技术网,plcjs.com,plcjs.net等字样
Copyright 2010-2030. All rights reserved. 


微信公众号二维码 抖音二维码 百家号二维码 今日头条二维码哔哩哔哩二维码