上位机软件挂在车间连续跑,没人盯着,最怕的就是半夜崩溃没人知道,第二天产量没记上、报警没收到。崩溃不可能完全杜绝,但一套好的防护体系能做到三点:崩了留得下现场、关键数据不丢、能自动恢复运行。这篇文章把这三件事的工程做法讲全。
先看清崩溃都从哪来
上位机的崩溃来源和普通软件不太一样,按出现频率排:设备通信返回意外数据(空值、非法格式、超时后突然迟到的响应)导致解析异常;数据库或文件被占用、磁盘写满;多线程时序问题,对象已释放还在访问;以及最容易被忽略的——第三方库在线程池线程里抛了未捕获异常,整个进程被终结。
关键认知是:try/catch 只能挡住你预料到的异常,真正致命的是没被任何代码接住的未处理异常。所以防护的第一步是在进程边界挂最后一道网。
全局异常钩子怎么挂
以 .NET 为例,有三个层次的事件,缺一个都有漏网的异常:
- UI 线程:WinForm 的 ThreadException、WPF 的 DispatcherUnhandledException,界面操作链路上的异常在这里,可以选择拦截后让程序继续;
- 非 UI 线程:AppDomain.CurrentDomain.UnhandledException,线程池和后台线程的未处理异常在这里,默认进程会终止,这里只能做记录、救不活;
- Task 未观察异常:TaskScheduler.UnobservedTaskException,忘记 await 的任务异常在这里,能防止进程被拖崩。
处理策略要分清:UI 层的异常,记日志、提示用户、尽量继续;数据采集类异常,跳过当前这轮、下一轮重来,绝不能让单个坏点终结整个进程。但要克制"所有异常都吞掉"的冲动——吞异常不记日志等于埋雷,兜底处理器里第一件事永远是落日志。
日志怎么落盘才在崩溃后还在
日志是崩溃后唯一的现场,设计上有几个硬要求:
- 分级:DEBUG/INFO/WARN/ERROR/FATAL,生产默认 INFO,出问题临时调 DEBUG,平时不堆垃圾;
- 带上下文:时间戳、线程号、设备站号、点位、完整堆栈,光一句"对象未引用"没法定位;
- 即时落盘:ERROR 及以上自动 flush,普通日志可以缓冲,但崩溃要丢的恰恰是最后那几行致命日志;
- 滚动切割:按天或按大小切文件,保留最近 N 天,防止磁盘被写满——磁盘满本身又是新的崩溃源;
- 日志写到独立数据盘,和程序目录分开,系统盘故障时现场还在。
关键数据保住,重启后能接着跑
自动恢复的前提是重启后知道"崩之前干到哪了"。做法是把关键状态即时持久化,而不是全放内存:
- 正在执行的工单、批次、当前产量计数,每记一笔就落库,崩溃最多丢最后几秒;
- 正在下发的控制指令写操作流水(指令编号、内容、状态),重启后先查流水,未确认的指令人工核对,绝不自动重发动作指令;
- 设备参数和配方本地存副本,重启后与 PLC 侧校验一致性,不一致提示而不是默默覆盖;
- 通信层断网缓存按时间戳落盘,重启后接着补传。
一句话:凡是"丢了会说不清"的数据,都不能只活在内存里。
看门狗和自动拉起
进程真的终止了,需要外部把它拉起来,常见三种方式:Windows 服务做守护,发现主进程不在就重启;任务计划程序配置"失败时重启";或用独立的看门狗服务定时检测心跳——主程序定期写心跳文件/标志,超时无心跳说明假死(界面还在但采集线程死了),看门狗强制重启。假死比真崩溃更隐蔽,只看进程在不在不够。
自动恢复要配两道保险:重启次数封顶(短时间连续崩溃超过三次就停止重启、升级报警给人,避免故障状态下无限重启刷数据);恢复后先发一条通知(短信/消息),说明程序在什么时间因崩溃重启过,运维要知情。启动时还要做一次自检和数据衔接,确认设备通信正常、未完成数据补传完成,再进入正常运行。
怎么验证这套体系真的有效
防护代码写完不验证等于没写,建议在上线前做一轮"故障注入":
- 故意在按钮事件、采集线程、Task 里各抛一个异常,确认都被对应钩子接住、日志完整;
- 拔掉网线、停掉数据库、写满磁盘,观察程序是降级运行还是崩溃;
- 手动杀掉进程,确认看门狗在预期时间内拉起、启动后数据衔接正确、通知发出;
- 让程序连续运行并持续触发异常,检查日志切割和磁盘占用是否受控。
收尾建议:稳定性投入要趁项目早期,别等现场崩过几次再补。需要崩溃防护的检查清单或看门狗服务参考代码,可以联系赢式科技(15001875806 / ys_soft@163.com),说明你们的运行环境和值守方式,我们给针对性方案。
