# m_RightBtn(喷嘴开启)可用性真值表 ## 0) 你要的重点:只整理“上层可点(E=true)” 这一节只看上层传给 `setReclaimOpenBtnStatus(E)` 的 E,不包含底层拦截(airPushing / RFID)。 关键调用位置: - [mainwidget.cpp](mainwidget.cpp#L863) - [InkJetPrinter.cpp](InkJetPrinter.cpp#L1094) - [InkJetPrinter.cpp](InkJetPrinter.cpp#L2555) - [InkJetPrinter.cpp](InkJetPrinter.cpp#L2581) 上层 E=true 的来源只有 4 类: - 停止打印后:`currReclaimTankPos == 0` 时传 true。 - 自检完成瞬间:直接传 true。 - 回收槽状态机:`reclaimTankstatus == 2` 时传 true。 - 回收槽状态机:`reclaimTankstatus == 4` 时传 true。 ## 0.1) 上层 E=true 精简真值表(不含 A/R) 定义四个上层布尔量: - S:自检完成事件(self check done edge) - T:停止打印事件且 `currReclaimTankPos == 0` - R2:`reclaimTankstatus == 2` - R4:`reclaimTankstatus == 4` 则上层可点条件: E = S OR T OR R2 OR R4 只看 E=true 的最小真值表(命中任一来源即可): | S | T | R2 | R4 | E | |---|---|---|---|---| | 1 | 0 | 0 | 0 | 1 | | 0 | 1 | 0 | 0 | 1 | | 0 | 0 | 1 | 0 | 1 | | 0 | 0 | 0 | 1 | 1 | 说明: - 这 4 行是“最小命中表”。多项同时为 1(例如 T=1 且 R2=1)也仍然是 E=1。 ## 0.2) 上层 E=false 的主要覆盖路径(帮助你理解为何看起来复杂) 下面这些路径会频繁把 E 拉回 false: - 开始打印后立即置 false:[mainwidget.cpp](mainwidget.cpp#L824) - 打印中周期置 false:[mainwidget.cpp](mainwidget.cpp#L2594) - 自检启动/进行中置 false:[InkJetPrinter.cpp](InkJetPrinter.cpp#L1059) - 排气过程周期置 false:[mainwidget.cpp](mainwidget.cpp#L1034) - 齿轮泵未进入运行态时置 false:[InkJetPrinter.cpp](InkJetPrinter.cpp#L710) - 关机流程置 false:[mainwidget.cpp](mainwidget.cpp#L219) 所以“上层 E=true”通常是阶段性信号,可能很快被其他流程覆盖为 false。 ## 0.3) 我建议的上层简化口径(先统一 E,再叠加底层拦截) 你可以把现有分散调用统一理解为两步: 1) 先算上层意图 E: ```cpp bool upperEnable = selfCheckDoneEdge || (stopPrint && currReclaimTankPos == 0) || (reclaimTankstatus == 2) || (reclaimTankstatus == 4); ``` 2) 再算最终可点: ```cpp bool finalEnable = upperEnable && !airPushing && !rfidBlocked; ``` 这样分层后,排查会非常直接: - 先问“上层想不想给点”(E) - 再问“底层有没有拦截”(A/R) ## 1) 最终判定公式 m_RightBtn 最终是否可用,不只看调用处传入的 enable,还会在 MainWidget 内再次过滤。 关键实现位置: - [setReclaimOpenBtnStatus 定义](mainwidget.cpp#L959) - [RFID 条件过滤](mainwidget.cpp#L961) - [airPushing 条件过滤](mainwidget.cpp#L968) - [最终 setEnabled](mainwidget.cpp#L971) 可用公式: FinalEnabled = E AND (NOT A) AND (NOT R) 变量定义: - E:上层调用 setReclaimOpenBtnStatus(E) 时传入的 enable - A:sysPara->status.airPushing(是否正在排气) - R:RFID 层拦截(仅在编译启用 __RFID_CARD 时有效) - inkCardTimeOut 或 solventCardTimeOut 或 needMaintenance 任一为真,则 R=1 说明: - 若未启用 __RFID_CARD,R 恒为 0。 ## 2) 最终可用真值表(硬条件) | E | A | R | FinalEnabled | |---|---|---|---| | 0 | 0 | 0 | 0 | | 0 | 0 | 1 | 0 | | 0 | 1 | 0 | 0 | | 0 | 1 | 1 | 0 | | 1 | 0 | 0 | 1 | | 1 | 0 | 1 | 0 | | 1 | 1 | 0 | 0 | | 1 | 1 | 1 | 0 | 结论: - 唯一能点的组合是 E=1、A=0、R=0。 ## 2.1) 只看“按钮可用=true”的最小真值表 如果你只关心“什么时候能点”,那真值表可以压缩成 1 行: | E | A | R | FinalEnabled | |---|---|---|---| | 1 | 0 | 0 | 1 | 反向速记(现场排查更快): - 只要满足以下任一条件,按钮就一定不可用: - E=0(上层当前不允许) - A=1(正在排气) - R=1(RFID 拦截) ## 2.2) 工程化简化版(建议你按这个理解) 把复杂条件改写成一个“守卫函数”,先算出 3 个阻塞原因,再统一决定可用性。 建议逻辑: ```cpp // 只表达逻辑,不改变现有业务行为 bool blockedByRFID = hasRFIDGuard && (inkCardTimeOut || solventCardTimeOut || needMaintenance); bool blockedByAirPush = airPushing; bool blockedByUpperLayer = !upperEnable; bool finalEnabled = !blockedByRFID && !blockedByAirPush && !blockedByUpperLayer; ``` 对应到 UI 语义: - 可用态:Ready - 不可用态:BlockedByUpperLayer / BlockedByAirPush / BlockedByRFID 这样简化后的好处: - 读代码时从“多处 setReclaimOpenBtnStatus(true/false)”切换为“先看谁在设置阻塞原因”。 - 打日志更清晰:可直接输出第一个阻塞原因,而不是只看到按钮被置灰。 - 后续扩展更稳:新增条件只需新增一个 blockedByX,不会到处散落 if。 建议落地步骤(不改行为,只提可维护性): - 第一步:在 MainWidget 内新增一个 `computeReclaimOpenBtnEnabled()`,所有按钮状态更新都走它。 - 第二步:在 `setReclaimOpenBtnStatus` 里补充调试日志(仅 debug),打印 E/A/R。 - 第三步:把状态机处的“直接 true/false”改成“设置上层意图 upperEnable”,统一在一个点算最终值。 ## 3) E(上层 enable)常见取值来源表 | 场景 | 调用代码 | 传入 E | |---|---|---| | 开始打印后 | [mainwidget.cpp](mainwidget.cpp#L824) | 0 | | 停止打印后,按回收槽位置决定 | [mainwidget.cpp](mainwidget.cpp#L863) | currReclaimTankPos == 0 | | 关机流程(回收槽未关) | [mainwidget.cpp](mainwidget.cpp#L219) | 0 | | 排气进行中(周期) | [mainwidget.cpp](mainwidget.cpp#L1034) | 0 | | 打印中且自检完成(周期) | [mainwidget.cpp](mainwidget.cpp#L2594) | 0 | | 自检开始 | [InkJetPrinter.cpp](InkJetPrinter.cpp#L1059) | 0 | | 自检完成 | [InkJetPrinter.cpp](InkJetPrinter.cpp#L1094) | 1 | | 回收槽状态机:状态=1或2 时 | [InkJetPrinter.cpp](InkJetPrinter.cpp#L2555) | reclaimTankstatus == 2 | | 回收槽状态机:状态=4(保持) | [InkJetPrinter.cpp](InkJetPrinter.cpp#L2581) | 1 | | 齿轮泵未进入运行态 | [InkJetPrinter.cpp](InkJetPrinter.cpp#L710) | 0 | ## 4) 按回收槽状态机的可用性(忽略 A/R 时的 E) 这张表只表示状态机“想给”的 E,最终仍需经过第 2 节硬条件过滤。 | reclaimTankstatus | 语义 | E(状态机给值) | 代码位置 | |---|---|---|---| | 1 | 开槽中 | 0 | [InkJetPrinter.cpp](InkJetPrinter.cpp#L2555) | | 2 | 关槽中 | 1 | [InkJetPrinter.cpp](InkJetPrinter.cpp#L2555) | | 4 | 保持 | 1 | [InkJetPrinter.cpp](InkJetPrinter.cpp#L2581) | | 5 | 开到位 | 未在该分支直接设置 E(通常由其他逻辑接管) | [InkJetPrinter.cpp](InkJetPrinter.cpp#L2520) | | 6 | 关到位 | 未在该分支直接设置 E(通常随后可再次开槽) | [InkJetPrinter.cpp](InkJetPrinter.cpp#L2561) | | 0x11/0x12 | 卡滞/异常 | 不会给出稳定可用态,偏禁用与告警处理 | [InkJetPrinter.cpp](InkJetPrinter.cpp#L2546) | ## 5) 点击行为补充(按钮可用后点下去会发生什么) 入口: - [on_m_RightBtn_clicked](mainwidget.cpp#L1266) 行为分支: - 若 reclaimTankBlockedAppending 为真:先弹告警确认,不直接开槽。 - [mainwidget.cpp](mainwidget.cpp#L1300) - 若 canRunAirPushing 为真 且 currReclaimTankPos==0:先排气,再开槽。 - [mainwidget.cpp](mainwidget.cpp#L1270) - 其他情况:直接调用 reclaimTanOpen。 - [mainwidget.cpp](mainwidget.cpp#L1298) 补充状态位来源: - canRunAirPushing 在开槽动作中会被置为 false。 - [mainwidget.cpp](mainwidget.cpp#L1135) - 自检完成、关槽完成后会恢复为 true。 - [InkJetPrinter.cpp](InkJetPrinter.cpp#L1098) - [InkJetPrinter.cpp](InkJetPrinter.cpp#L2571) ## 6) 一句话版本 m_RightBtn 可用的必要且充分条件是:上层当前允许开启(E=1)+ 不在排气(A=0)+ RFID 不拦截(R=0)。在实际运行中,E 主要由“自检状态、打印状态、回收槽状态机、故障流程”共同驱动。 ## 7) 给现场同事的超短口令 先看 E,再看 A,再看 R: - E 由业务状态机决定“想不想让你点”。 - A/R 决定“即使想让你点,也要不要强制拦截”。 - 只有 E=1 且 A=0 且 R=0,m_RightBtn 才会真的可点。