
做工业上位机开发和设备运维的谁没背过这种锅半夜两点值班打电话说生产线数据断了、报警乱跳、上位机卡了远程连不上现场急得睡不着。等早上赶过去往工控机前一坐一切又正常了。查程序日志就一行“连接异常断开”没有上下文没有原始报文没有系统状态。领导问什么原因你说“可能网络波动吧”跟没说一样最后问题没查到锅先扣你头上。这种一过性的故障是工业现场最头疼的问题。它不是一直坏而是特定条件下才出现夜班低温、大功率设备启停、电网波动、定时任务跑批……这些场景只在夜里出现等白天你去排查环境条件全变了自然复现不了。所以解决这类问题的核心思路从来不是「想办法复现」而是「把故障现场完整留下来」。今天就把我们线上在用的一套故障留存方案全部分享出来从报文、数据、系统到环境四层留存搭配C#实现思路不用高端工具普通工控机就能跑。一、先搞懂为什么夜班故障总查无实据工业现场的故障绝大多数不是稳定复现的硬故障而是条件触发的软故障。夜班之所以是重灾区是因为它集齐了所有极端触发条件环境极端夜班气温低尤其是北方冬天设备冷启动、传感器温漂、硬盘低温掉速白天温度回升就正常负载极端夜班大功率设备集中启停焊机、风机、泵类同时动作电网压降大电磁干扰强网络容易闪断操作不可控夜班人员操作随意手动改参数、切模式、重启设备下班前又改回去不留记录后台任务集中备份、报表、数据同步、杀毒扫描全安排在半夜资源占满导致程序超时、卡顿网络波动运营商凌晨链路割接、带宽调整断个几十秒自动恢复根本抓不住这些故障都是一过性的持续几十秒到几分钟自动恢复不留痕迹。没有主动留存的话除了一句“连接断开”你什么证据都拿不到。二、第一层报文层留存通信故障的黑匣子通信故障是夜班最高发的故障也是最容易死无对证的。很多人的程序只记录“连接成功/失败”失败了不知道是设备没回、还是回了错包、还是中间丢包。要存就存完整的原始报文这是排查通信问题的铁证比任何日志都管用。2.1 环形报文缓存平时驻内存故障才落盘全程存报文不现实工控机硬盘扛不住。我们用的是30分钟环形缓存异常触发落盘的方案内存里维护一个固定大小的环形缓冲区只保留最近30分钟的所有收发报文带精确时间戳正常运行时只在内存里循环覆盖不写磁盘减少IO和磨损一旦检测到故障事件连接断开、CRC校验失败、协议异常码、超时立刻把缓存里的历史报文写入本地文件同时继续记录故障后10分钟的报文最终得到「故障前30分钟 故障后10分钟」的完整报文上下文足够定位绝大多数通信问题给一个C#简化版的环形缓存实现/// summary /// 报文环形缓存用于故障现场留存 /// /summary public class PacketRingBuffer { private readonly QueuePacketItem _buffer new QueuePacketItem(); private readonly int _maxCount 1000; // 大约30分钟的报文量 private readonly object _lock new object(); /// summary /// 添加报文到缓存 /// /summary public void Add(byte[] data, bool isSend, string deviceCode) { lock (_lock) { _buffer.Enqueue(new PacketItem { Time DateTime.Now, IsSend isSend, DeviceCode deviceCode, Data (byte[])data.Clone() }); if (_buffer.Count _maxCount) _buffer.Dequeue(); } } /// summary /// 故障触发将缓存报文落盘 /// /summary public void DumpToFile(string reason) { lock (_lock) { string fileName $fault_packet_{DateTime.Now:yyyyMMdd_HHmmss}.log; using var writer new StreamWriter(fileName); writer.WriteLine($故障原因{reason}); writer.WriteLine($缓存时间范围{_buffer.Peek().Time:yyyy-MM-dd HH:mm:ss.fff} ~ {_buffer.Last().Time:yyyy-MM-dd HH:mm:ss.fff}); foreach (var item in _buffer) { string hex BitConverter.ToString(item.Data).Replace(-, ); writer.WriteLine(${item.Time:HH:mm:ss.fff} {(item.IsSend ? 发送 : 接收)} {item.DeviceCode} : {hex}); } } } } public class PacketItem { public DateTime Time { get; set; } public bool IsSend { get; set; } public string DeviceCode { get; set; } public byte[] Data { get; set; } }在通信类的发送和接收方法里各加一行Add方法调用在异常捕获的地方调用DumpToFile。几行代码就能拿到完整的故障报文现场。2.2 端口级自动抓包终极兜底方案如果程序层面的留存还不够或者怀疑是网络层面的问题直接上端口级自动抓包这是终极兜底手段。不用装Wireshark用轻量的tshark命令行工具后台静默运行只抓指定PLC端口的流量按小时分割文件保留最近7天占用资源极低工控机完全扛得住。比如抓Modbus 502端口的流量保留7天tshark -i 本地连接 -f tcp port 502 -b duration:3600 -w modbus_capture.pcap遇到故障直接去拖对应时间的pcap文件用Wireshark打开重传、丢包、复位、异常响应一目了然比程序日志靠谱一万倍。我们现在所有重要站点的工控机默认都部署这个遇到通信问题先拖包十秒就能定位是网络问题还是设备问题。三、第二层数据层留存业务异常的时间轴很多故障不是通信断了而是数据不对产量计数多了、温度值乱跳、报警误触发等第二天去看数据又正常了。这种问题没有历史数据快照根本查不了。3.1 定时全量快照不要只存变化很多人做数据存储只存变化的数据觉得省空间。但排查故障的时候变化量是最没用的——你只能看到哪个点变了看不到哪个点没变没法排除干扰。我们的做法是每5分钟存一次全量点位快照存在本地SQLite里保留最近7天。每个点位的原始值、采集时间、状态都存下来。出问题了直接回溯对应时间点的全量数据看是单个点跳了还是整批点都错了是慢慢飘的还是瞬间跳的。是干扰还是真实值一眼就能看出来。3.2 异常触发序列留存检测到数据异常超量程、跳变超过阈值、状态逻辑冲突除了存快照还要单独存一份「异常前后序列」异常发生前10次采集值 异常后10次采集值形成完整的变化曲线。比如温度正常25度突然跳到80度又回来。有了序列就能看到是瞬间尖峰干扰还是持续上升真实变化定位原因的效率完全不一样。四、第三层系统层留存别让程序背环境的锅很多故障根本不是程序的问题是系统环境的锅。半夜工控机重启了、内存不足了、端口被占了、防火墙拦截了早上恢复正常没有记录你永远查不到。4.1 轻量系统状态监控不用上复杂的监控软件自己写个几十行的监控线程每分钟记录一次CPU、内存、磁盘占用率网络连接数、TCP端口监听状态采集服务的线程数、队列长度存在本地CSV日志里保留一个月。出问题先看这部分很多时候程序崩溃本质是半夜内存爆了、磁盘满了。4.2 Windows事件日志被忽略的神器绝大多数人都忽略了Windows自带的事件查看器这才是系统问题的终极证据。系统日志记录开关机、蓝屏、电源异常、网卡重置应用程序日志记录服务崩溃、程序异常安全日志记录登录、权限变更我们之前遇到过夜班工控机反复重启程序日志什么都没有最后在系统事件日志里查到「电源电压波动导致的非正常关机」和程序一点关系都没有。建议所有工控机上线前把事件日志的保留时间改成至少30天默认的64K大小根本不够用。五、第四层环境层留存玄学故障的终极答案如果以上都查了还是找不到原因那大概率是环境的问题。工业现场很多玄学故障最后都是温湿度、电压、干扰导致的。有条件的话关键机柜里装个小型温湿度电压采集模块几十块钱数据存在本地。冬天夜班低温导致硬盘掉速、传感器漂移大功率设备启动导致电网压降车间潮湿导致绝缘下降、接口氧化这些问题没有环境数据你永远猜不到。有了数据一眼就能对应上。六、留存机制的设计原则别写满硬盘做故障留存不是存得越多越好要平衡留存效果和系统开销记住四个原则环形覆盖控制总量所有缓存、日志都要有最大保留时间循环覆盖绝对不能把硬盘写满工业现场没人天天清日志平时内存异常落盘高频数据先驻内存故障才写磁盘减少IO和磁盘磨损延长工控机硬盘寿命本地优先网络兜底所有留存先存本地网络好再同步到服务器不能因为网络断了连留存数据都没了时间统一精确到毫秒所有日志、报文、快照统一用NTP时间时间不准的话多层数据对不上等于白存七、真实案例夜班网络闪断排查最后分享一个真实案例就是靠这套机制定位的问题。之前一个涂装车间项目夜班频繁出现采集中断持续二三十秒自动恢复白天怎么测都正常查了半个月都没结果。后来上了报文留存和自动抓包很快就定位了程序报文日志故障前所有请求响应都正常突然就收不到数据28秒后连接重建恢复抓包文件对应时间点大量TCP重传、丢包最后RST复位不是设备主动断开系统日志没有异常说明不是工控机的问题对照电网监控数据故障时间点刚好是夜班烘干炉风机启动电网压降导致工业交换机短暂重启最后给交换机加了个UPS电源问题彻底解决。如果没有这些留存数据这个问题可能查到退休都查不出来最后只能背个“程序不稳定”的锅。最后说句实在话做工业现场的永远不要等故障复现。能稳定复现的故障都不是大问题真正头疼的就是这种一过性的、夜班专属的故障。一套完善的故障留存机制就是给系统装了黑匣子平时不起眼出问题的时候它就是你排查问题的唯一证据也是你不用背锅的底气。很多团队觉得做这个是锦上添花浪费时间。等遇到几次查不出原因的故障就会明白这套东西的价值远胜于写十个新功能。