first commit
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
# 2026-04-29 BarcodeEditor 虚拟键盘不可点击问题修复日志
|
||||
|
||||
## 一、问题背景
|
||||
|
||||
在 `BarcodeEditor` 中,`lineEditContent` 获取焦点后会正常呼出 `InputMethod` 虚拟键盘,但键盘弹出后,任意按键都无法点击。
|
||||
|
||||
用户反馈该问题只出现在 `BarcodeEditor` 场景中,而 `InputMethod` 在系统其他页面表现正常。
|
||||
|
||||
## 二、问题现象
|
||||
|
||||
- `lineEditContent` 获得焦点后,虚拟键盘可以显示。
|
||||
- 虚拟键盘显示后,按钮点击无响应。
|
||||
- 同一套输入法在其他页面可以正常输入,说明不是键盘控件本身整体失效。
|
||||
|
||||
## 三、排查思路
|
||||
|
||||
本次排查没有只局限在 `BarcodeEditor` 单页,而是沿着“焦点切换 + 全局事件过滤 + 输入法显示/隐藏逻辑”做了整体梳理:
|
||||
|
||||
1. 检查 `BarcodeEditor.cpp / .h` 中与编辑框及焦点处理相关的逻辑。
|
||||
2. 检查 `InputMethod.cpp` 与 `MyInputMethod.cpp` 中虚拟键盘显示、隐藏、发送按键的实现。
|
||||
3. 检查全局输入法触发入口,最终定位到 `InkJetPrinter::eventFilter(QObject *, QEvent *)`。
|
||||
|
||||
排查后确认:
|
||||
|
||||
- 当前工程通过 `InkJetPrinter::eventFilter()` 统一处理 `FocusIn / FocusOut`,从而控制虚拟键盘显示与隐藏。
|
||||
- 若事件处理不区分“点到了输入法自己”还是“点到了其他区域”,就可能出现键盘刚收到点击就被焦点切换或隐藏逻辑打断的问题。
|
||||
- 因为工程里已经有全局事件过滤,所以比起只在 `BarcodeEditor` 中单点修复,更适合补齐一套通用的“点击空白处失焦并隐藏键盘,同时点击键盘自身不误隐藏”的机制。
|
||||
|
||||
## 四、本次修改内容
|
||||
|
||||
### 1. 修改 `MyInputMethod.cpp`
|
||||
|
||||
新增头文件:
|
||||
|
||||
- `#include <QTextEdit>`
|
||||
|
||||
新增方法:
|
||||
|
||||
#### `bool MyInputMethod::shouldIgnoreHideForWidget(QWidget *widget) const`
|
||||
|
||||
作用:
|
||||
|
||||
- 判断当前点击/聚焦的控件是否属于虚拟键盘本体;
|
||||
- 如果是 `inputMethod` 自身、其子控件,或 `digitalInput` 自身、其子控件,则返回 `true`;
|
||||
- 同时保留对 `inputPreviewBox` 的排除。
|
||||
|
||||
这样做的目的,是避免“用户点击虚拟键盘按钮时,程序先把键盘隐藏掉,导致按钮无法真正处理点击”。
|
||||
|
||||
#### `void MyInputMethod::clearCurrentFocusAndHide()`
|
||||
|
||||
作用:
|
||||
|
||||
- 获取当前焦点控件;
|
||||
- 如果当前焦点不是输入法本体,则清除其焦点;
|
||||
- 隐藏 `inputMethod` 和 `digitalInput`;
|
||||
- 调用 `inputMethod->onHiden()` 做隐藏后的清理。
|
||||
|
||||
这个方法用于统一处理“点击空白区域时关闭输入法”的需求。
|
||||
|
||||
#### 调整 `updateHandler(int type)`
|
||||
|
||||
在入口增加:
|
||||
|
||||
- 如果当前焦点控件属于输入法自己,则直接返回,不再继续执行隐藏/显示逻辑。
|
||||
|
||||
这一步用于避免输入法自身参与焦点切换时,触发错误的隐藏处理。
|
||||
|
||||
### 2. 修改 `InkJetPrinter.cpp`
|
||||
|
||||
修改 `InkJetPrinter::eventFilter(QObject *watched, QEvent *event)`:
|
||||
|
||||
- 保留原有 `FocusIn / FocusOut` 对输入法的显示隐藏控制;
|
||||
- 新增 `QEvent::MouseButtonPress` 处理。
|
||||
|
||||
新增逻辑如下:
|
||||
|
||||
- 若当前被点击对象是 `QWidget`;
|
||||
- 且它不是 `QSpinBox / QLineEdit / QTextEdit` 这类可编辑输入控件;
|
||||
- 且它也不是虚拟键盘本身及其子控件;
|
||||
- 则调用 `myInputMethod->clearCurrentFocusAndHide()`。
|
||||
|
||||
也就是说:
|
||||
|
||||
- 点击输入框:正常保留焦点,键盘继续可用;
|
||||
- 点击虚拟键盘按钮:不会被误判为“点击空白处”,键盘不会先被隐藏;
|
||||
- 点击普通空白区域或非编辑控件:输入框失焦,键盘自动收起。
|
||||
|
||||
## 五、修复结果
|
||||
|
||||
本次修复后,输入法相关行为被整理为一套更稳定的通用机制:
|
||||
|
||||
1. 可编辑控件获取焦点时,正常弹出虚拟键盘;
|
||||
2. 点击虚拟键盘自身时,不会触发误隐藏;
|
||||
3. 点击页面空白区域或普通控件时,会清理当前焦点并隐藏虚拟键盘。
|
||||
|
||||
这次处理不是“只为 `BarcodeEditor` 打补丁”,而是将该问题在全局输入法交互层做了统一修复,可同时改善其他页面的体验一致性。
|
||||
|
||||
## 六、验证建议
|
||||
|
||||
建议至少验证以下场景:
|
||||
|
||||
### `BarcodeEditor`
|
||||
|
||||
- 点击 `lineEditContent`,确认虚拟键盘正常弹出;
|
||||
- 连续点击虚拟键盘字母、数字、退格、确认等按键,确认可以响应;
|
||||
- 点击页面空白区域,确认输入框失焦且键盘隐藏。
|
||||
|
||||
### 其他页面回归验证
|
||||
|
||||
- 任意 `QLineEdit` 页面:确认键盘仍可正常呼出和输入;
|
||||
- `QSpinBox` 页面:确认数字键盘逻辑未受影响;
|
||||
- `QTextEdit` 页面:确认点击输入法按钮仍可输入文本;
|
||||
- 点击输入法自身按钮时,不应出现“按下立即消失”或“完全点不动”的现象。
|
||||
|
||||
## 七、本次会话记录补充
|
||||
|
||||
在本次对话过程中,除修复“`BarcodeEditor` 弹出键盘但无法点击”问题外,还顺带完成了一项通用交互增强:
|
||||
|
||||
- **点击空白区域自动失焦并隐藏虚拟键盘**。
|
||||
|
||||
该增强已通过全局事件过滤接入,而不是局限于某一个编辑器页面,便于后续维护。
|
||||
@@ -0,0 +1,198 @@
|
||||
# 2026-04-29 日期替代逻辑迁移与 BarcodeEditor 联动工作日报
|
||||
|
||||
## 工作背景
|
||||
|
||||
- `DateReplaceEditor` 是从 `DateVarSet` 中拆分出来的“代替”选项卡编辑器。
|
||||
- 当前 UI 已经拆分完成,但原先 `DateVarSet` 中“代替”页签对应的逻辑、默认数据、以及与 `BarcodeEditor` 的联动尚未完整迁移。
|
||||
- 目标是让 `DateReplaceEditor`、`DateFormatEditor` 在 `BarcodeEditor` 中成对工作,并补齐默认替换列表初始化能力。
|
||||
|
||||
---
|
||||
|
||||
## 本次完成内容
|
||||
|
||||
### 1. 为 DateReplaceEditor 补齐替代页数据模型接口
|
||||
|
||||
在 `scene/DateReplaceEditor.h/.cpp` 中补充并实现:
|
||||
|
||||
- `DateItemModel DateReplaceEditor::dateModel()`
|
||||
- `void DateReplaceEditor::setDateModel(const DateItemModel &model)`
|
||||
|
||||
实现效果:
|
||||
|
||||
- `dateModel()`:把当前编辑器中的替代数据回收成 `DateItemModel`
|
||||
- `dataMap`
|
||||
- `weeksMap`
|
||||
- `hoursMap`
|
||||
- `monthsMap`
|
||||
- `daysMap`
|
||||
- `yearsMap`
|
||||
- `setDateModel()`:将 `DateItemModel` 回填到编辑器内部列表,并依据当前替代类型刷新界面显示。
|
||||
|
||||
这样 `DateReplaceEditor` 就具备了和外部视图模型双向交换数据的能力。
|
||||
|
||||
---
|
||||
|
||||
### 2. 复刻 DateVarSet 中“代替”页默认数据初始化逻辑
|
||||
|
||||
在 `DateReplaceEditor` 中补齐了与 `DateVarSet::initReplaceValue()` 对应的默认初始化逻辑,包含以下替代列表:
|
||||
|
||||
- `dataList`:`0-9`、`A-F`
|
||||
- `weekList`:`MON-SUN`
|
||||
- `monthList`:`01-12`
|
||||
- `dayList`:`01-31`
|
||||
- `hourList`:`00-23`
|
||||
- `yearList/yearInitList`:从当前年份开始连续 16 年
|
||||
|
||||
同时实现/整理了以下行为:
|
||||
|
||||
- 根据替代类型切换不同分页展示:
|
||||
- 数据
|
||||
- 星期
|
||||
- 月
|
||||
- 年
|
||||
- 日
|
||||
- 时
|
||||
- 分页上下翻页逻辑
|
||||
- 每页 4 项内容展示
|
||||
- `lineEdit_Replace1~4` 文本变化实时写回对应列表
|
||||
- “预设值”按钮按类型写入对应替换映射
|
||||
|
||||
这部分逻辑与原 `DateVarSet` 的替代页行为保持一致。
|
||||
|
||||
---
|
||||
|
||||
### 3. 在 BarcodeEditor 中补充 DateReplaceEditor 的成对增删逻辑
|
||||
|
||||
在 `scene/BarcodeEditor.h/.cpp` 中新增并接入:
|
||||
|
||||
- 成员:`DateReplaceEditor *m_dateReplaceEditor = nullptr;`
|
||||
- 构造函数中实例化 `m_dateReplaceEditor`
|
||||
- 析构函数中移除对应 tab 并释放对象
|
||||
|
||||
联动行为实现如下:
|
||||
|
||||
#### 当内容模板包含 `{t}` 时
|
||||
|
||||
动态添加两个页签:
|
||||
|
||||
- `DateFormatEditor` -> 标题:`日期/时间`
|
||||
- `DateReplaceEditor` -> 标题:`代替`
|
||||
|
||||
并设置尺寸与数据同步。
|
||||
|
||||
#### 当内容模板不包含 `{t}` 时
|
||||
|
||||
动态移除上述两个页签,确保 `DateFormatEditor` 与 `DateReplaceEditor` 成对出现、成对消失。
|
||||
|
||||
#### 在 `bindViewModel()` 中
|
||||
|
||||
- 将 `m_vm->dateViewModel()` 转成 `DateFormatEditorVM` 后回填给 `DateFormatEditor`
|
||||
- 将 `m_vm->dateViewModel()` 直接回填给 `DateReplaceEditor`
|
||||
|
||||
从而保证条码编辑器在绑定数据后,两个日期相关页签都能正确显示已有配置。
|
||||
|
||||
---
|
||||
|
||||
### 4. 打通 BarcodeEditor 中“代替”页的数据回写
|
||||
|
||||
在 `BarcodeEditor` 的格式确认逻辑处理中,补充了 `DateReplaceEditor` 到 `BarcodeViewModel` 的数据回写:
|
||||
|
||||
- 从 `m_dateReplaceEditor->dateModel()` 取出当前替代配置
|
||||
- 回写到 `m_vm->dateViewModel()` 的以下字段:
|
||||
- `dataMap`
|
||||
- `daysMap`
|
||||
- `monthsMap`
|
||||
- `yearsMap`
|
||||
- `weeksMap`
|
||||
- `hoursMap`
|
||||
|
||||
同时保留 `DateFormatEditor` 对以下日期字段的同步:
|
||||
|
||||
- `datetimePattern`
|
||||
- `daysOffset`
|
||||
- `monthsOffset`
|
||||
|
||||
这样 `BarcodeEditor` 内部对日期格式页和日期替代页的数据已经形成完整闭环。
|
||||
|
||||
> 备注:后续如果 `onBtnFormatClicked()` 的业务逻辑再次调整,需要确保上述日期格式和替代数据的回写逻辑继续保留。
|
||||
|
||||
---
|
||||
|
||||
### 5. 为 DateItemModel 增加默认替代数据构造函数
|
||||
|
||||
在 `scene/ViewModels.h` 中为 `DateItemModel` 增加:
|
||||
|
||||
- `static DateItemModel createDefaultReplaceModel()`
|
||||
|
||||
该函数用于统一构造默认“替代”数据,初始化内容如下:
|
||||
|
||||
- `dataMap`:`0-9/A-F`
|
||||
- `monthsMap`:`01-12`
|
||||
- `daysMap`:`01-31`
|
||||
- `weeksMap`:`MON-SUN`
|
||||
- `hoursMap`:`00-23`
|
||||
- `yearsMap`:当前年份起连续 16 年,键和值均为年份文本
|
||||
|
||||
其默认规则严格参考 `DateVarSet::initReplaceValue()` 的实现,作为公共默认数据来源。
|
||||
|
||||
---
|
||||
|
||||
### 6. 在 BarcodeEditor 中接入默认替代列表初始化
|
||||
|
||||
在 `scene/BarcodeEditor.cpp` 中,使用 `DateItemModel::createDefaultReplaceModel()` 进行了两处接入:
|
||||
|
||||
#### 构造函数中
|
||||
|
||||
- 为新创建的 `m_vm` 直接设置默认 `dateViewModel`
|
||||
- 再同步给 `m_dateReplaceEditor`
|
||||
|
||||
目的:
|
||||
|
||||
- 即便外部还未绑定真实数据,`DateReplaceEditor` 也能拥有完整默认替换列表,不会出现空白替代项。
|
||||
|
||||
#### `bindViewModel()` 中
|
||||
|
||||
- 对传入的 `dateViewModel` 做兜底检查
|
||||
- 若以下任意替代字段为空,则仅补齐缺失字段,而不覆盖已有数据:
|
||||
- `dataMap`
|
||||
- `daysMap`
|
||||
- `monthsMap`
|
||||
- `yearsMap`
|
||||
- `weeksMap`
|
||||
- `hoursMap`
|
||||
|
||||
目的:
|
||||
|
||||
- 保证已有业务数据优先
|
||||
- 缺失时自动回填默认替代列表
|
||||
- 避免出现部分字段为空导致编辑器显示异常的问题
|
||||
|
||||
---
|
||||
|
||||
## 涉及文件
|
||||
|
||||
本次涉及的主要文件:
|
||||
|
||||
- `scene/DateReplaceEditor.h`
|
||||
- `scene/DateReplaceEditor.cpp`
|
||||
- `scene/BarcodeEditor.h`
|
||||
- `scene/BarcodeEditor.cpp`
|
||||
- `scene/ViewModels.h`
|
||||
|
||||
---
|
||||
|
||||
## 当前结果
|
||||
|
||||
目前已实现:
|
||||
|
||||
- `DateReplaceEditor` 具备独立替代逻辑与数据模型读写能力
|
||||
- `DateReplaceEditor` 与 `DateFormatEditor` 在 `BarcodeEditor` 中成对出现/删除
|
||||
- `BarcodeEditor` 可同步和回写替代数据
|
||||
- `DateItemModel` 可统一生成默认替代列表
|
||||
- `BarcodeEditor` 已接入默认替代列表初始化和缺失字段兜底
|
||||
|
||||
## 后续建议
|
||||
|
||||
- 在真正的“确定/保存”入口统一调用 `DateFormatEditor` 与 `DateReplaceEditor` 的回写逻辑,避免后续格式按钮语义变更带来数据遗漏。
|
||||
- 若后续还有其他编辑器需要复用日期默认替代列表,统一使用 `DateItemModel::createDefaultReplaceModel()`,避免重复维护多份初始化代码。
|
||||
- 后续如需联调,请先修复 Qt 工程生成链路(`qmake` / `ui_*.h` / includePath / Makefile)后再进行完整编译验证。
|
||||
+394
@@ -0,0 +1,394 @@
|
||||
# 开机 60 秒自检逻辑分析
|
||||
|
||||
## 结论
|
||||
|
||||
这个项目的 **开机 60 秒自检**,确实和 **齿轮泵启动、压力建立、阀泵联动控制** 密切相关。
|
||||
|
||||
它不是单纯的软件倒计时,而是一个持续 **60 秒的供墨/压力建立过程**。在这段时间里,程序会:
|
||||
|
||||
- 禁止打印;
|
||||
- 禁止部分回收控制;
|
||||
- 启动并调节齿轮泵;
|
||||
- 控制循环阀、主墨泵、压缩泵、泄压阀等;
|
||||
- 根据压力反馈不断调整齿轮泵输出;
|
||||
- 60 秒结束后才将自检标记为完成。
|
||||
|
||||
---
|
||||
|
||||
## 1. 60 秒是怎么定义的
|
||||
|
||||
在 `SysPara.h` 中有定义:
|
||||
|
||||
```cpp
|
||||
#define MAX_SELF_CHECK_CNT 6000 // 60S, 以10ms为单位
|
||||
```
|
||||
|
||||
也就是说:
|
||||
|
||||
- 自检计数单位:`10ms`
|
||||
- 总计数:`6000`
|
||||
- 总时长:`6000 × 10ms = 60 秒`
|
||||
|
||||
因此,这个“60 秒自检”是代码里明确写死的固定时长。
|
||||
|
||||
---
|
||||
|
||||
## 2. 自检主逻辑在哪里
|
||||
|
||||
核心逻辑在:
|
||||
|
||||
- `InkJetPrinter::timerHandler()`
|
||||
- `InkJetPrinter::autoControlValvePump()`
|
||||
- `InkJetPrinter::pressureAdj()`
|
||||
- `DevOp::mainInkCtrl()`
|
||||
- `DevOp::WriteMoterPump()`
|
||||
|
||||
其中,`timerHandler()` 是整个开机阶段的主状态机。
|
||||
|
||||
在 `InkJetPrinter.cpp` 中,自检逻辑大致如下:
|
||||
|
||||
```cpp
|
||||
if(!sysPara->status.selfCheckDone)
|
||||
{
|
||||
/*开机自检*/
|
||||
autoControlValvePump();
|
||||
if(pulse_100ms){
|
||||
pressureAdj();
|
||||
}
|
||||
|
||||
if(!sysPara->status.selfCheckCnt) {
|
||||
LogInfo::setRunStatusText(tr("开始自检,请等待60秒..............."));
|
||||
m_MainWidget->enableReclaimCtrlButton(false);
|
||||
m_MainWidget->enablePrintButton(false);
|
||||
sysPara->devOp->mainInkCtrl();
|
||||
}
|
||||
|
||||
if((++sysPara->status.selfCheckCnt % 100) == 0){
|
||||
m_MainWidget->setRunStatus(tr("开始自检,请等待%1秒...")
|
||||
.arg((MAX_SELF_CHECK_CNT - sysPara->status.selfCheckCnt)/100));
|
||||
}
|
||||
|
||||
if(sysPara->status.selfCheckCnt >= MAX_SELF_CHECK_CNT)
|
||||
{
|
||||
sysPara->status.selfCheckDone = 1;
|
||||
LogInfo::setRunStatusText(tr("自检完成!!!"));
|
||||
m_MainWidget->enableReclaimCtrlButton(true);
|
||||
sysPara->devOp->mainInkCtrl();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这说明:
|
||||
|
||||
### 自检期间持续执行的动作
|
||||
- 调用 `autoControlValvePump()`:控制阀和泵;
|
||||
- 每 `100ms` 调用一次 `pressureAdj()`:做压力闭环调节;
|
||||
- 禁止打印按钮;
|
||||
- 禁止回收控制按钮;
|
||||
- UI 持续显示倒计时;
|
||||
- 到 60 秒后,才置 `selfCheckDone = 1`。
|
||||
|
||||
---
|
||||
|
||||
## 3. 这 60 秒内是不是和齿轮泵启动有关
|
||||
|
||||
**是,直接相关。**
|
||||
|
||||
在 `timerHandler()` 里,开机初期会先给齿轮泵一个启动值:
|
||||
|
||||
```cpp
|
||||
if(!(sysPara->status.statWarn & WARN_STATUS_INK_WARN) && !gearPumpRunFlag && !sysPara->status.isPumpValveControledByHand)
|
||||
{
|
||||
if(sysPara->para.gearPumpEnable) {
|
||||
sysPara->para.gearPumpValue = 150;
|
||||
if(tempFlag){
|
||||
sysPara->devOp->WriteMoterPump();
|
||||
tempFlag=0;
|
||||
}
|
||||
gearPumpStartFlag = true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这段逻辑的含义是:
|
||||
|
||||
- 如果没有墨水报警;
|
||||
- 齿轮泵尚未进入稳定运行状态;
|
||||
- 当前不是手动控制;
|
||||
- 并且启用了齿轮泵;
|
||||
|
||||
那么就先给齿轮泵一个初始驱动值 `150`,用于 **开机启动齿轮泵**。
|
||||
|
||||
然后 3 秒后:
|
||||
|
||||
```cpp
|
||||
if(gearPumpStartFlag)
|
||||
{
|
||||
pulse_3s ++;
|
||||
if(pulse_3s == 3)
|
||||
{
|
||||
pulse_3s = 0;
|
||||
gearPumpRunFlag = true;
|
||||
ControledSwitchFlag = true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
说明程序认为:
|
||||
|
||||
- 前 3 秒主要是齿轮泵启动阶段;
|
||||
- 3 秒后进入可控运行阶段;
|
||||
- 后续允许更正式的压力闭环控制。
|
||||
|
||||
---
|
||||
|
||||
## 4. 压力建立逻辑是什么
|
||||
|
||||
### 4.1 压力调节函数:`pressureAdj()`
|
||||
|
||||
`pressureAdj()` 是压力闭环调节核心。
|
||||
|
||||
它会读取:
|
||||
|
||||
- 目标压力:`sysPara->para.pressure`
|
||||
- 当前压力:`sysPara->status.PRESSURE`
|
||||
|
||||
然后通过 PID 算法计算齿轮泵输出值:
|
||||
|
||||
```cpp
|
||||
if(!sysPara->status.gearPumpRunStopFlag)
|
||||
{
|
||||
if(sysPara->para.gearPumpEnable && sysPara->para.flagOfReclaimPumEnable)
|
||||
u=PID_realize2((float)sysPara->para.pressure,(float)sysPara->status.PRESSURE);
|
||||
else if(u > 0 || ((sysPara->para.pressure - sysPara->status.PRESSURE) > 0) )
|
||||
{
|
||||
u=PID_realize((float)sysPara->para.pressure,(float)sysPara->status.PRESSURE);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
然后把计算结果写回齿轮泵控制值:
|
||||
|
||||
```cpp
|
||||
sysPara->para.gearPumpValue = (unsigned short)u;
|
||||
sysPara->devOp->WriteMoterPump();
|
||||
```
|
||||
|
||||
也就是说:
|
||||
|
||||
### 压力建立过程的本质
|
||||
1. 读取当前压力;
|
||||
2. 与设定压力比较;
|
||||
3. 用 PID 算法算出新的齿轮泵输出;
|
||||
4. 将新的齿轮泵值下发给电机;
|
||||
5. 周期性重复,直到压力逐渐建立并稳定。
|
||||
|
||||
因此,**60 秒自检的核心目标之一,就是把系统压力建立起来并趋于稳定。**
|
||||
|
||||
---
|
||||
|
||||
## 5. 自检期间阀泵还做了什么
|
||||
|
||||
`autoControlValvePump()` 负责阀和泵的联动。
|
||||
|
||||
从代码搜索结果可以看到,自检期间会执行:
|
||||
|
||||
- 循环阀控制;
|
||||
- 主墨泵控制;
|
||||
- 泄压阀控制;
|
||||
- 压缩泵控制;
|
||||
- 自检特殊过程中的循环阀交替动作。
|
||||
|
||||
其中有一段很关键:
|
||||
|
||||
```cpp
|
||||
if(!sysPara->status.selfCheckDone || (sysPara->status.needPowerOffTimerout && sysPara->para.gearPumpEnable)){
|
||||
cycleValveRunForSelfCheck();
|
||||
}
|
||||
```
|
||||
|
||||
并且代码注释明确写了:
|
||||
|
||||
```cpp
|
||||
// 在开机自检过程中交替打开和关闭循环阀
|
||||
void InkJetPrinter::cycleValveRunForSelfCheck()
|
||||
```
|
||||
|
||||
这说明:
|
||||
|
||||
### 自检不是只有齿轮泵在转
|
||||
同时还包含:
|
||||
- 循环阀交替开关;
|
||||
- 供墨路径切换;
|
||||
- 主墨泵动作;
|
||||
- 压缩泵辅助建立压力;
|
||||
- 泄压阀根据压力状态切换。
|
||||
|
||||
也就是说,这 60 秒更像是一个 **整套流体系统的上电初始化过程**。
|
||||
|
||||
---
|
||||
|
||||
## 6. 压力建立成功后,程序如何判断“运行正常”
|
||||
|
||||
在 `timerHandler()` 里有一段逻辑:
|
||||
|
||||
```cpp
|
||||
if(pushButtonStartFlag && sysPara->status.selfCheckDone)
|
||||
{
|
||||
if((sysPara->status.PRESSURE > (sysPara->para.pressure -10)) && (sysPara->status.PRESSURE < (sysPara->para.pressure +10)))
|
||||
{
|
||||
if(pulse_1s)
|
||||
{
|
||||
pulse_5s++;
|
||||
}
|
||||
if(pulse_5s == 5)
|
||||
{
|
||||
pulse_5s = 0;
|
||||
gearPumpRunNormalFlag = true;
|
||||
gearPumpRunNormalValue = u;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 当前压力进入目标压力 ±10 范围;
|
||||
- 并且持续 5 秒;
|
||||
- 才会认为齿轮泵运行正常,置 `gearPumpRunNormalFlag = true`。
|
||||
|
||||
这说明系统不仅要求“压力到达”,还要求“稳定一段时间”。
|
||||
|
||||
---
|
||||
|
||||
## 7. 如果压力建立失败,会发生什么
|
||||
|
||||
代码中有一套异常判断:
|
||||
|
||||
```cpp
|
||||
if(gearPumpRunNormalFlag && !sysPara->status.isPumpValveControledByHand && sysPara->para.gearPumpEnable)
|
||||
{
|
||||
if(u > gearPumpRunNormalValue*1.5)
|
||||
{
|
||||
if(pulse_1s)
|
||||
{
|
||||
count_3s++;
|
||||
if(count_3s == 3)
|
||||
{
|
||||
sysPara->status.gearPumpRunStopFlag = true;
|
||||
gearPumpRunNormalFlag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
含义是:
|
||||
|
||||
- 如果原本已经进入正常运行;
|
||||
- 但后面为了维持压力,齿轮泵输出突然需要比正常值高很多(超过 1.5 倍);
|
||||
- 并持续 3 秒;
|
||||
|
||||
程序就会认为:
|
||||
- 可能有压力泄漏;
|
||||
- 或者压力传感器异常;
|
||||
- 然后置 `gearPumpRunStopFlag = true`,停泵保护。
|
||||
|
||||
所以从设计意图看,系统在持续监控:
|
||||
|
||||
- 压力是否能建立;
|
||||
- 建立后是否能稳定;
|
||||
- 若不能稳定,是否可能存在泄漏或故障。
|
||||
|
||||
---
|
||||
|
||||
## 8. `mainInkCtrl()` 里和自检有什么关系
|
||||
|
||||
在 `DevOp::mainInkCtrl()` 中:
|
||||
|
||||
```cpp
|
||||
dataOfmainInkCtrl[0] =
|
||||
(isReleaseValveOpen()?(1<<3):0)
|
||||
+ (isMainInkPumpOpen()?(1<<2):0)
|
||||
+ (sysPara->status.selfCheckDone?(1<<1):0)
|
||||
+ (sysPara->status.isPumpValveControledByHand?1:0);
|
||||
```
|
||||
|
||||
这里把 `selfCheckDone` 编码到发送给底层控制板的数据位中。
|
||||
|
||||
说明:
|
||||
|
||||
- 下位机/控制板也知道“自检是否完成”;
|
||||
- 自检完成前后,底层控制行为可能不同;
|
||||
- 这进一步证明自检不是单纯界面逻辑,而是参与底层泵阀控制状态机的。
|
||||
|
||||
---
|
||||
|
||||
## 9. 最终结论
|
||||
|
||||
### 这 60 秒内做了什么?
|
||||
它主要在做:
|
||||
|
||||
1. **禁止打印与部分人工操作**;
|
||||
2. **启动齿轮泵**,先给初始值;
|
||||
3. **切换并控制循环阀、主墨泵、压缩泵、泄压阀**;
|
||||
4. **每 100ms 根据压力反馈调节齿轮泵输出**;
|
||||
5. **持续尝试建立并稳定系统压力**;
|
||||
6. **监测压力异常、泄漏、转速异常等问题**;
|
||||
7. **满 60 秒后置自检完成标志**。
|
||||
|
||||
### 是否和齿轮泵启动、压力建立有关?
|
||||
**是,明确相关,而且这就是主线逻辑。**
|
||||
|
||||
可以理解为:
|
||||
|
||||
> 开机 60 秒自检 = 齿轮泵启动 + 阀泵联动 + 压力建立 + 状态稳定确认 的初始化阶段。
|
||||
|
||||
---
|
||||
|
||||
## 10. 一个更直观的时序理解
|
||||
|
||||
可以粗略理解为:
|
||||
|
||||
### 第 0~3 秒
|
||||
- 给齿轮泵一个初始驱动值;
|
||||
- 启动供墨相关执行机构;
|
||||
- 为后续闭环控制做准备。
|
||||
|
||||
### 第 3 秒以后到 60 秒内
|
||||
- 进入正常自检控制阶段;
|
||||
- 循环阀交替动作;
|
||||
- 主墨泵/压缩泵/泄压阀按状态联动;
|
||||
- 每 100ms 调整齿轮泵输出;
|
||||
- 持续建立和稳定压力。
|
||||
|
||||
### 到 60 秒
|
||||
- 不管压力是否已经很早达标,代码仍会继续走完整个自检窗口;
|
||||
- 计时结束后才标记 `selfCheckDone = 1`;
|
||||
- 允许进入正常工作阶段。
|
||||
|
||||
所以它更像:
|
||||
|
||||
> **固定时长的初始化窗口 + 期间持续进行压力闭环建立**
|
||||
|
||||
而不是:
|
||||
|
||||
> **压力一达标就立刻结束自检**
|
||||
|
||||
---
|
||||
|
||||
## 11. 补充说明
|
||||
|
||||
从代码风格上看,这套逻辑明显是“设备控制型状态机”:
|
||||
|
||||
- 上位机负责状态判断与策略;
|
||||
- `DevOp` 负责向底层板卡发送控制命令;
|
||||
- `selfCheckDone`、`gearPumpRunFlag`、`gearPumpRunNormalFlag`、`gearPumpRunStopFlag` 这些标志位共同构成了开机阶段的控制流程。
|
||||
|
||||
如果还要继续深挖,下一步最值得分析的是:
|
||||
|
||||
1. `autoControlValvePump()` 的完整阀泵切换条件;
|
||||
2. `cycleValveRunForSelfCheck()` 的开关节奏;
|
||||
3. `checkPressureControl()` 对压力异常的判定规则;
|
||||
4. 回收泵使能 `flagOfReclaimPumEnable` 对压力控制模型的影响。
|
||||
|
||||
Reference in New Issue
Block a user