void SequenceLoop(CancellationToken ct){ int step = 0; var timer = Stopwatch.StartNew(); while (!ct.IsCancellationRequested) { var img = Volatile.Read(ref _image); switch (step) { case 0: // Idle if (img.Plc.StartPressed) { _cmdQueue.Add(new Command("ServoOnAll")); step = 10; timer.Restart(); } break; case 10: // Wait ServoOn if (img.Axes.All(a => a.ServoOn)) step = 20; else if (timer.ElapsedMilliseconds > 3000) step = 900; // Fault break; case 20: // Home _cmdQueue.Add(new Command("HomeAll")); step = 30; timer.Restart(); break; case 30: if (img.Axes.All(a => a.Homed)) step = 100; // Auto ready break; case 900: // fault handling break; } Thread.Sleep(5); // 小睡避免空转 }}
C) 报警线程 AlarmLoop(去抖 + 分级 + 节流)
职责:
从镜像判定报警条件(急停、伺服报警、超时、气压不足)
去抖/延时确认
发布事件 _eventCh.Writer.TryWrite(...)
UI只订阅事件显示,不参与计算
D) 日志线程 LogLoop(所有线程只“投递”,日志线程落盘)
职责:
接收 EventMsg
写文本/写数据库
慢 IO 与实时控制解耦
E) 设备线程 DeviceLoop(相机/扫码枪等)
职责:
处理设备 SDK 回调,把结果作为事件/数据写入缓存
不要在 SDK 回调里做耗时操作(转投到队列/Channel)
5) 工业项目里最常踩的坑(避坑指南)
UI线程直接读写 PLC → UI卡死、偶发死锁
只让 CommLoop 做 IO
多个线程同时写 PLC → 写覆盖、脉冲丢失
所有写入统一从 CommLoop 出口
状态机里用 Thread.Sleep(1000) → 节拍乱、报警慢、响应差
用计时器/Stopwatch + step 条件推进
共享变量到处改 → 现场鬼畜 bug
镜像用“原子替换”,命令用队列
异常没集中处理 → 现场“偶发”难定位
每个 Loop 都 try/catch 并发 Event + 日志
6) 可以直接套用的“工程目录”
Core/ProcessImage.cs(镜像)
Core/Commands.cs(命令定义)
Core/EventBus.cs(事件)
Workers/CommWorker.cs
Workers/SequenceWorker.cs
Workers/AlarmWorker.cs
Workers/LogWorker.cs
Devices/CameraWorker.cs / ScannerWorker.cs
UI/ViewModels/*.cs(订阅事件、展示状态)
---
往期热门文章:
</eventmsg></eventmsg>
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!