工业设备软件底层 IO 断线与动作卡死排查全纪实

问题排查报告:设备 Set IO Failed 及指令超时卡死分析

排查日期:2026/04/10
涉及模块:Buffer Robot (BRB), Load Lock 1 (LL1), 运动控制与通讯底层,WPF UI 前端
运行环境:Simulator (模拟器) / 本地调试环境


1. 故障现象描述

  • UI 状态表现:Buffer Robot (BRB) 的状态长时间卡在 Performing,而与之交互的 Load Lock 1 (LL1) 处于 Idle 状态。
  • 异常日志
    • 大量触发 Set IO failed 异常(涉及 LL1.CylinderGV, LL1.GV 等)。
    • 设备互锁(Interlock)报警:BufferRobot.IsRobotRetracted.LL1 interlocked!
    • 出现极端时长的超时报错:Command Move timeout in 600026.8726(约10分钟)及 Command Move timeout in 831206.3128(约14分钟)。
  • 衍生问题:UI 进程抛出 ArgumentException: TimeSpan does not accept floating point Not-a-Number values 导致界面崩溃。

2. 核心排查方法记录 (Debugging Methodology)

本次排查中使用了以下关键的 Visual Studio 调试技巧来定位多线程与面向对象架构中的卡死点:

2.1 如何使用“线程/并行堆栈”窗口定位卡死代码

当系统出现“假死”或长时间 Performing 时,不能仅看 UI 线程,需深入后台进程查找阻塞点:

  1. 确保已附加(Attach)后台控制进程(如 RT 进程),点击“全部中断”(Break All)。
  2. 点击菜单栏 调试 (Debug) -> 窗口 (Windows) -> 线程 (Threads)并行堆栈 (Parallel Stacks)
  3. 在列表中寻找后台任务线程(如 Worker Thread 或包含 TaskQueue 的线程)。重点关注是否存在死锁提示(如 Waiting on lock owned by Thread XXX)。
  4. 双击可疑线程,Visual Studio 会直接跳转到该线程当前正在挂起的具体代码行(例如本案中死等的 WaitForCompletion() 阻塞点)。

2.2 如何跨越接口寻找具体实现逻辑

在设备控制软件中,各模块常通过接口解耦。排查时若按 F12(转到定义)只会跳到空壳接口(如 IIndexerModule.MoveLifterToPosition),无法看到真实逻辑。

  • 方法A(快捷键):将光标停在方法名上,按下 Ctrl + F12(转到实现 / Go To Implementation),直接跳转至包含具体执行逻辑的实现类。
  • 方法B(代码透镜 CodeLens):点击接口方法上方提示引用次数的灰色小字(如 X references),在弹出的悬浮窗中寻找不带 interface 声明的具体实现方法,双击即可进入真正的“案发现场”。

3. 根因分析推演 (Root Cause Analysis)

3.1 互锁触发原因(正常保护机制)

根据 UI 状态图显示,Buffer Robot 的手臂已经完全伸入 LL1 腔体内部。此时触发 IsRobotRetracted interlocked! 属于设备预期的安全互锁逻辑,目的是防止在手臂未收回时闸阀(Gate Valve)关闭导致严重的硬件碰撞事故。

3.2 动作卡死点锁定

利用上述线程排查方法,发现执行取放片任务的工作线程卡死在 EtherCatVHPRobot.LifterDown 方法中。
Robot 在将手臂伸入 LL1 后,调用了 LL1 自身的接口请求降下顶针(Lifter),跨越接口找到实现后确认:代码进入 .WaitForCompletion() 并处于死等状态。

3.3 超时与 IO 失败的关联 (PerformMove 缺陷)

深入追踪 LL1 的底层运动逻辑,定位到最终的动作下发函数 PerformMove。该函数执行逻辑如下:

  1. 调用 IO.SetIO 下发目标位置并触发马达。
  2. 调用 ExecuteCommand,并传入 CheckMoveComplete 进行死循环轮询,等待到位信号(In-Position)。
    致命卡死原因
    底层 IO 通讯此时已经断开(表现为日志前置的大量 Set IO failed),下发动作指令并未成功发给模拟器,系统也无法从已断开的 IO 中读取到位信号。而 ExecuteCommand 中配置的 TimeoutInMilliseconds 长达 14 分钟(831206 ms)。导致该线程在断线情况下原地盲目死等 14 分钟后才抛出超时错误,进而导致上层 Robot 调度线程被彻底阻塞。

3.4 UI 崩溃衍生问题

UI 端接收到底层抛出的异常或不合理的变量更新时,将计算结果 NaN (Not-a-Number) 通过反射直接赋值给了界面的旋转动画 TimeSpan 属性,由于时间不能是非数字,从而引发底层的 UI 渲染崩溃。


4. 修复建议 (Action Items)

4.1 针对模拟器/底层通讯(治本)

  • 联调模拟器:连同 Simulator 进程一起附加调试,确认为何会在中途断开连接或拒绝响应 MotorControl 的 IO Set 请求。
  • 完善模拟逻辑:检查模拟器代码,确保其实现了“收到动作指令 -> 延迟休眠模拟耗时 -> 将对应 In-Position 信号位置 True”的完整闭环逻辑。

4.2 针对设备控制软件框架(治标)

  • 缩短超时配置:在机台配置文件中,将 Z 轴/Lifter 等短行程动作的 TimeoutInMilliseconds 从 800+ 秒修改为合理的值(建议 15~30 秒)。
  • 增加 IO 状态校验 (Fast-Fail):在 PerformMove 轮询到位信号前或轮询循环中,增加对当前通讯状态的判断。若发现 IO 已经断线(Set IO Failed),应立即中断等待并抛出异常,避免线程长时挂起。

4.3 针对 UI 前端代码

  • 增加防呆校验:在 VHPDualArmRobotAdvForCalibration.RotateCanvas 等动画数据处理点,增加 if (double.IsNaN(angle)) 等防错判定,给予默认值或提前 Return,防止非法数据击穿 UI 层。