修复RFID导致回收槽关闭;修复新建文件后不自动切换的问题

This commit is contained in:
2026-07-23 08:37:12 +08:00
parent 50c4999d90
commit f1ae7ae898
5 changed files with 770 additions and 77 deletions
+9 -1
View File
@@ -1522,8 +1522,16 @@ void InkJetPrinter::getMainWidgetButtonClickedSlot(int index)
switch (index) switch (index)
{ {
case 1: { // 编辑当前文件 case 1: { // 编辑当前文件
m_TestPrintWidget->openWorkFile(sysPara->para.lastOpenedFileName);
// 如果上次打开的文件不存在或者不是xml文件,则创建一个新的作业文件
QFileInfo fileInfo(sysPara->para.lastOpenedFileName); QFileInfo fileInfo(sysPara->para.lastOpenedFileName);
if(!fileInfo.exists() || fileInfo.suffix() != "xml"){
createNewJobFile();
break;
}
// 如果上次打开的文件存在且是xml文件,则直接打开该文件
m_TestPrintWidget->openWorkFile(sysPara->para.lastOpenedFileName);
ui.label_currEditFile->setText(QString("E: %1").arg(fileInfo.baseName())); ui.label_currEditFile->setText(QString("E: %1").arg(fileInfo.baseName()));
m_StackedWidget->setCurrentWidget(m_TestPrintWidget); m_StackedWidget->setCurrentWidget(m_TestPrintWidget);
break; break;
+626
View File
@@ -0,0 +1,626 @@
# QtUI 喷码机项目架构说明文档
> **版本**: V6.7.4
> **目标平台**: ARM Linux (AM335x) / x86 (模拟调试)
> **Qt 版本**: Qt 4.6.3 (ARM) / Qt 5.x (x86)
> **屏幕分辨率**: 800x600
> **文档编写日期**: 2026-07-23
---
## 目录
1. [项目概述](#1-项目概述)
2. [技术栈](#2-技术栈)
3. [整体架构](#3-整体架构)
4. [目录结构](#4-目录结构)
5. [核心模块详解](#5-核心模块详解)
6. [数据流与通信机制](#6-数据流与通信机制)
7. [场景与打印渲染系统](#7-场景与打印渲染系统)
8. [RFID刷卡模块](#8-rfid刷卡模块)
9. [编译构建系统](#9-编译构建系统)
10. [关键配置与宏定义](#10-关键配置与宏定义)
11. [新同事上手指南](#11-新同事上手指南)
---
## 1. 项目概述
本项目是一个**工业喷码机(CIJ,Continuous Ink Jet)的上位机控制系统**,运行在嵌入式ARM Linux平台上。软件通过串口与FPGA和STM32两块下位机硬件通信,实现喷码内容的编辑、预览、打印控制、设备状态监控、系统参数管理等功能。
### 核心功能
- **喷码内容编辑**: 支持文本、日期、计数器、图片、条形码(一维码)、二维码(QR/DataMatrix)等图元
- **打印控制**: 支持时延触发、电眼触发、同步器触发、电眼+同步器等多种触发模式
- **设备管理**: 墨路控制(阀门、泵、加热器)、粘度检测、相位检测、高压控制
- **系统管理**: 参数配置、FPGA/STM32固件升级、软件更新、日志管理
- **文件管理**: 作业文件的创建、保存、导入、导出(U盘)
- **RFID刷卡**: 墨水/溶剂/保养卡的身份验证与消耗管理
- **多语言支持**: 中文、英文、土耳其语、西班牙语、泰语、越南语、韩语、波斯语等
---
## 2. 技术栈
| 类别 | 技术 |
|------|------|
| 框架 | Qt 4.6.3 (ARM) / Qt 5.x (x86) |
| 语言 | C++ (C++11),部分C |
| 构建工具 | qmake |
| UI设计 | Qt Designer (.ui文件) |
| 序列化 | XML (作业文件)、INI (配置文件) |
| 通信 | QSerialPort (RS232/RS485) |
| 网络 | Qt Network (WiFi) |
| 条码生成 | C语言库 (BarCode目录) |
| 二维码生成 | C语言库 (QtQREnCode目录) |
| DataMatrix | C++库 (Matrix目录) |
| Modbus | 开源FreeModbus库 |
| 加密 | SM3/SM4 国密算法 |
| 日志 | 自定义Logger框架 |
---
## 3. 整体架构
项目采用**分层架构**,自上而下分为:
```
┌─────────────────────────────────────────────────────────┐
│ UI 表现层 │
│ InkJetPrinter (主窗口) + MainWidget (打印主界面) │
│ 各功能页面: SystemManage, SystemSetting, LogInfo, ... │
│ 场景编辑器: TestPrintWidget, ItemEditor, PicScene │
├─────────────────────────────────────────────────────────┤
│ 业务逻辑层 │
│ SysPara (系统参数管理) │
│ DevOp (设备操作) │
│ RunTimeCtrl (运行时间统计) │
│ LogInfoCtrl (日志控制) │
│ RFIDManager (刷卡管理) │
├─────────────────────────────────────────────────────────┤
│ 通信层 │
│ Serial (串口通信封装) │
│ Modbus (Modbus协议) │
├─────────────────────────────────────────────────────────┤
│ 硬件抽象层 │
│ FPGA (喷印控制、高压、相位检测) │
│ STM32 (墨路控制、传感器采集) │
│ RFID读卡器 (IC卡读写) │
└─────────────────────────────────────────────────────────┘
```
### 模块依赖关系图
```
InkJetPrinter (顶层窗口)
├── MainWidget (打印主界面)
│ ├── PicScene (场景管理)
│ ├── FileManageWidget (文件管理)
│ └── PrintTextDisplay (打印预览)
├── TestPrintWidget (编辑界面)
│ ├── PicScene (场景编辑)
│ ├── ItemEditor (图元编辑)
│ ├── ImageLoader (图片加载)
│ ├── BarcodeEditor (条码编辑)
│ └── CounterVarSet (计数器设置)
├── SystemManage (系统管理)
├── SystemSetting (系统设置)
├── SystemUpdate (系统升级)
├── FontSetWidget (字体设置)
├── PrintSetting (打印设置)
├── MainInstruction (主指令)
├── LogInfo (日志信息)
├── OtherInfo (其他信息)
├── DateVarSet (日期变量设置)
├── CounterVarSet (计数器变量设置)
├── QBarCode / dlgQrCode (条码/二维码对话框)
├── DialogFileOperation (文件操作)
└── ScheduleShiftsEditor (班次编辑)
SysPara (系统参数,贯穿所有模块)
Serial (串口通信,连接FPGA和STM32)
RFIDManager (RFID刷卡管理)
RunTimeCtrl (运行时间控制)
```
---
## 4. 目录结构
```
项目根目录/
├── QtUI.pro # 项目文件 (qmake)
├── main.cpp # 程序入口
├── mainwidget.h/cpp # 打印主界面 (MainWidget)
├── InkJetPrinter.h/cpp # 顶层主窗口
├── Public.h/cpp # 公共工具类
├── SysPara.h/cpp # 系统参数管理 (核心)
├── DevOp.h/cpp # 设备操作 (FPGA寄存器读写)
├── myinclude.h # 全局宏定义、枚举
├── VerBVersion.h # 版本号定义
├── dump.h/cpp # 崩溃信号处理
├── runtimectrl.h/cpp # 运行时间统计
│
├── scene/ # 场景(喷码内容)编辑系统
│ ├── picscene.h/cpp # 场景类 (QGraphicsScene)
│ ├── myview.h/cpp # 视图类 (QGraphicsView)
│ ├── PicSceneLoader.h/cpp # 场景XML加载/保存
│ ├── pictextitem.h/cpp # 文本图元
│ ├── PicDateItem.h/cpp # 日期图元
│ ├── PicImageItem.h/cpp # 图片图元
│ ├── PicCounterItem.h/cpp # 计数器图元
│ ├── PicScheduleShiftItem.h/cpp # 班次图元
│ ├── piccodeitem.h/cpp # 条码/二维码图元
│ ├── ViewModels.h/cpp # 视图模型 (数据结构)
│ ├── ItemCommon.h/cpp # 图元工具函数
│ ├── ItemGeneralSettings.* # 图元通用设置
│ ├── BarcodeEditor.* # 条码编辑器
│ ├── CounterEditor.* # 计数器编辑器
│ ├── DateFormatEditor.* # 日期格式编辑器
│ ├── DateReplaceEditor.* # 日期替换编辑器
│ ├── ScheduleShiftsEditor.* # 班次编辑器
│ ├── DlgBarcodeFormat.* # 条码格式对话框
│ ├── DlgQrcodeFormat.* # 二维码格式对话框
│ └── sceneviewwidget.* # 场景视图封装
│
├── rfid/ # RFID刷卡模块
│ ├── rfidmanager.h/cpp # 刷卡管理核心
│ ├── rfidfunction.h/cpp # 刷卡功能函数
│ ├── sm3.h/cpp # SM3 国密哈希
│ └── sm4.h/cpp # SM4 国密加密
│
├── Serial/ # 串口通信封装
│ └── serial.h/cpp # 串口封装 (FPGA/STM32双通道)
│
├── fileManage/ # 文件管理
│ ├── FileManageWidget.* # 文件管理界面
│ └── UDiskComboBox.* # U盘检测与文件列表
│
├── modbus/ # Modbus协议
│ ├── modbus/ # FreeModbus库
│ ├── port/ # 移植层
│ ├── modbusserial.* # Modbus串口
│ └── modbustcp.* # Modbus TCP
│
├── Log/ # 日志框架
│ ├── logger.h/cpp # 日志基类
│ ├── filelogger.h/cpp # 文件日志
│ ├── logmessage.h/cpp # 日志消息
│ └── logglobal.h # 日志全局宏
│
├── BarCode/ # 一维条码生成库 (C)
│ ├── barcode.h # 条码头文件
│ ├── code128.c/code39.c/ # 各类型条码实现
│ └── ...
│
├── QtQREnCode/ # 二维码生成库 (C)
│ ├── qrencode.h/c # QR码生成
│ └── ...
│
├── Matrix/ # DataMatrix码生成库 (C++)
│ ├── dm200.cpp # DataMatrix实现
│ └── ...
│
├── qjson/ # JSON解析库
│
├── image.qrc # Qt资源文件
├── *.ts # 多语言翻译文件
│
├── 0/ ~ 101/ # 字库图片资源 (数字位图)
│ └── image/
│ └── digitalWidth*.bmp
│
└── conf/ # 运行时配置目录 (部署后)
├── sysPara.bin # 系统参数二进制文件
├── config.ini # 日志配置
└── ...
```
---
## 5. 核心模块详解
### 5.1 程序入口 (main.cpp)
[main.cpp](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/main.cpp)
程序启动流程:
1. 读取键盘类型配置(多语言键盘/普通键盘)
2. 创建 QApplication,配置日志系统 (FileLogger)
3. 加载字体和应用级样式
4. 根据语言配置加载对应翻译文件 (.qm)
5. 创建 MyInputMethod(自定义输入法)
6. 创建 InkJetPrinter 主窗口并显示
### 5.2 主窗口 (InkJetPrinter)
[InkJetPrinter.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/InkJetPrinter.h) | [InkJetPrinter.cpp](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/InkJetPrinter.cpp)
**职责**: 顶层窗口,管理所有子页面和核心业务流程。
**关键成员**:
- `m_StackedWidget` - 页面容器,通过索引切换不同功能页面
- `m_MainWidget` - 打印主界面(首页)
- `m_TestPrintWidget` - 编辑界面
- `serialFPGA` / `serialSTM32` - 两个独立串口线程,分别与FPGA和STM32通信
- `ctrl_runTime` - 运行时间控制器
- `manager_rfid` - RFID刷卡管理器
**核心定时器**:
- `m_Timer` - 主定时器 (200ms),处理设备状态轮询、自动控制
- `viscosity_timer` - 粘度检测定时器 (1s)
**设备自动控制**:
- `autoControlValvePump()` - 阀门/泵自动控制
- `pressureAdj()` - 压力自动调节(PID算法)
- `autoControlHeater()` - 加热器自动控制
- `detectAndcontrolReclaimTank()` - 回收槽检测与控制
### 5.3 打印主界面 (MainWidget)
[mainwidget.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/mainwidget.h) | [mainwidget.cpp](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/mainwidget.cpp)
**职责**: 打印操作的主界面,包含打印预览、打印控制、文件管理。
**关键功能**:
- `generatePrintContent()` - 将场景渲染为打印数据并发送到FPGA
- `variableItemsUpdate()` - 更新动态图元(日期、计数器等)
- `imageToPrintData()` - 将QImage转换为FPGA点阵数据
- `SendPrintDataToFpga()` - 通过串口向FPGA发送打印数据
**页面切换**:
- 通过底部导航按钮(编辑、新增、主界面、管理、消息)切换子页面
- 使用信号槽 `sendButtonClickedSignal(int)` 通知 InkJetPrinter 切换页面
### 5.4 系统参数管理 (SysPara)
[SysPara.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/SysPara.h)
**职责**: 管理所有系统参数,是系统的数据中枢。所有模块通过 SysPara 读写参数。
**核心数据结构**:
- `calandar_and_dat_t` - 日历/计数器数据(发送给FPGA)
- `bcOption_t` - 条形码选项
- `qrOption_t` - 二维码选项
- `QRvars_t` - 二维码变量
- `stepPrint_t` - 分段打印设定
- `alterPrint_t` - 交替打印设定
- `Cnt1ByGroup_t` - 分段计米设定
- `PrintSettingModel` - 打印设置模型(跟随作业文件保存)
**参数持久化**:
- 二进制文件: `PARA_ROOT_DIR/sysPara.bin`
- XML文件: `PARA_ROOT_DIR/defPrintSet.xml`
### 5.5 设备操作 (DevOp)
[DevOp.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/DevOp.h)
**职责**: 封装FPGA寄存器的读写操作,提供设备控制的高层接口。
**关键操作**:
- 阀门控制: `controlInkValve()`, `controlMixValve()`, `controlReleaseValve()`
- 泵控制: `WriteMoterPump()`
- 电压控制: DAC表读写 (`DAC_TABLE_ADDR`, `MATCH_*_DAC_TABLE_ADDR`)
- 打印控制: 启停、回收槽开关、故障恢复
- 计数器读写: 产品计数、同步计数
**FPGA地址空间**:
- `0x0000-0x01FF`: 状态读取区
- `0xA400-0xA5FF`: 打印控制寄存器
- `0xB000-0xCFFF`: 数据写入区(DAC表、打印数据、日历数据等)
- `0xE000-0xFFFF`: 数字字符点阵数据
### 5.6 串口通信 (Serial)
[Serial/serial.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/Serial/serial.h) | [Serial/serial.cpp](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/Serial/serial.cpp)
**职责**: 封装与FPGA和STM32的串口通信。
**设计特点**:
- 支持双通道(FPGA和STM32各自独立串口)
- 使用消息队列管理发送数据
- CRC16校验保证数据完整性
- 支持STM32固件升级(IAP协议)
- 运行在独立QThread中,不阻塞UI
**通信协议**:
- 自定义帧格式:命令 + 地址 + 数据 + CRC16
- 发送队列: `queueFPGA` / `queue`
- 接收解析: 通过信号槽分发到上层(`receivedFromFPGA` / `receivedFromSTM32`)
### 5.7 日志系统 (Log/)
**职责**: 提供分级日志记录功能。
**类层次**:
- `Logger` - 日志基类,定义日志格式、级别过滤
- `FileLogger` - 文件日志实现,支持缓冲区批量写入
- `LogMessage` - 日志消息封装
**使用方式**:
```cpp
qInfo() << "message"; // 信息日志
qWarning() << "warning"; // 警告日志
qCritical() << "error"; // 错误日志
```
### 5.8 运行时间统计 (RunTimeCtrl)
[RunTimeCtrl](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/runtimectrl.h)
**职责**: 统计软件运行时长,用于设备保养提醒。
**功能**:
- `getElapsedSeconds()` - 获取本次运行时间
- `getTotalSeconds()` - 获取总运行时间
- `sig_alertTimeout()` - 保养提醒信号
- `sig_updateTimeoutDay()` - 维护到期提醒
---
## 6. 数据流与通信机制
### 6.1 打印数据流
```
用户编辑场景 (PicScene)
└── 图元 (PicTextItem, PicDateItem, PicCodeItem, ...)
└── 渲染为 QImage
└── imageToPrintData() 转换为点阵数据
└── SendPrintDataToFpga() 通过 Serial 发送
└── FPGA 接收并驱动喷头喷印
```
### 6.2 设备状态轮询
```
timerHandler() (200ms周期)
├── serialFPGA->readData() # 读取FPGA状态寄存器
├── serialSTM32->readData() # 读取STM32传感器数据
├── saveFPGAStatus() # 解析FPGA状态
│ ├── 墨线开关、高压状态、回收故障
│ ├── 墨水/溶剂液位警告
│ └── 相位检测结果
├── saveSTM32Status() # 解析STM32状态
│ ├── 齿轮泵转速
│ ├── 脉冲计数
│ └── 压力/粘度值
├── MainWidget::updateSysStatusToUI() # 更新界面状态灯
└── autoControlValvePump() # 自动控制执行
```
### 6.3 信号槽通信概览
```
Serial::receivedFromFPGA ──→ InkJetPrinter::saveFPGAStatus()
Serial::receivedFromSTM32 ──→ InkJetPrinter::saveSTM32Status()
MainWidget::sendButtonClickedSignal ──→ InkJetPrinter::getMainWidgetButtonClickedSlot()
TestPrintWidget::sendButtonClickedSignal ──→ InkJetPrinter::getTestPrintWidgetButtonClickedSlot()
RunTimeCtrl::sig_alertTimeout ──→ InkJetPrinter::slt_warningMaintenance()
RFIDManager::sig_showMsg ──→ InkJetPrinter::slt_showMsg()
```
---
## 7. 场景与打印渲染系统
### 7.1 架构概述
场景系统基于 Qt Graphics View 框架,用于编辑和预览喷码内容。
```
PicScene (QGraphicsScene) # 场景,管理所有图元
└── myView (QGraphicsView) # 视图,提供缩放和交互
└── 图元基类
├── PicTextItem # 普通文本
├── PicDateItem # 日期变量 (yyyy-MM-dd HH:mm:ss...)
├── PicCounterItem # 计数器 (可变数字)
├── PicImageItem # 图片 (Logo)
├── PicCodeItem # 条码/二维码
└── PicScheduleShiftItem # 班次 (班次代码替换)
```
### 7.2 作业文件格式
作业文件使用XML格式(VBfiles结构),保存场景内容和打印设置:
```xml
<VBfiles>
<VBfile>
<!-- 打印设置 -->
<PrintSettings triggerMode="0" ... />
<!-- 文本图元 -->
<VB itemType="text" x="10" y="20" text="ABC" ... />
<!-- 日期图元 -->
<VB itemType="date" x="50" y="20" pattern="yyyy-MM-dd" ... />
<!-- 计数器图元 -->
<VB itemType="counter" x="100" y="20" counterType="7" ... />
<!-- 条码/二维码图元 -->
<VB itemType="code" x="10" y="50" codeType="QR" ... />
<!-- 图片图元 -->
<VB itemType="image" x="200" y="0" ... />
</VBfile>
</VBfiles>
```
### 7.3 关键类说明
- **PicSceneLoader** - 场景文件的加载/保存/预览,负责XML序列化/反序列化
- **ViewModels** - 定义 `DateItemModel`、`CounterViewModel` 等视图模型,用于编辑器与图元之间的数据传递
- **ItemCommon** - 图元工具函数,如根据日期模型生成文本字符串
- **ItemEditor** - 图元通用编辑器(字体、颜色、位置等)
- **打印设置** - `PrintSettingModel` 与作业文件绑定,包含触发模式、字宽、同步器参数等
---
## 8. RFID刷卡模块
### 8.1 模块概述
[RFIDManager](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/rfid/rfidmanager.h)
**职责**: 管理IC卡(墨水卡、溶剂卡、保养卡)的读写与验证。
**功能**:
- 墨水/溶剂添加验证(刷卡 + 物理添加)
- 保养卡识别与提醒
- 自动扣减卡内余额
- 卡片使用标记(防重复使用)
**加密**: 使用SM3(哈希)+ SM4(对称加密)国密算法保护卡片数据。
### 8.2 刷卡流程
```
检测到墨水/溶剂不足
└── 弹出刷卡提示对话框
└── 用户刷卡
├── RFIDManager 读取卡片数据
├── 验证卡片类型和有效性
├── 标记卡片为已使用
└── 通知 InkJetPrinter 更新报警状态
```
---
## 9. 编译构建系统
### 9.1 编译配置
项目使用 qmake 构建系统,主配置文件为 [QtUI.pro](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/QtUI.pro)。
**目标平台**:
- ARM Linux: `TARGET = QtUI`
- 交叉编译: 使用 `linux-arm-g++` mkspec
- x86桌面: 使用 `linux-g++` mkspec,用于开发调试
**关键宏定义**:
```makefile
DEFINES += ARM # ARM平台
DEFINES += QTV3 # Qt版本3兼容
DEFINES += VNLG # 威利品牌
DEFINES += WARNING # 警告功能
DEFINES += COUNTER1_BY_GROUP # 分段计米
DEFINES += PICSCENE # 场景编辑
DEFINES += MODBUS # Modbus支持
DEFINES += __CONVERTERWIFI__ # WiFi支持
DEFINES += TEMPMIMA # 临时密码
```
### 9.2 模块依赖
```makefile
QT += core gui network serialport virtualkeyboard sql xml
```
### 9.3 编译命令
```bash
# ARM交叉编译
qmake -spec /usr/local/Trolltech/QtEmbedded-4.6.3-arm/mkspecs/qws/linux-arm-g++ \
"DEFINES+=ARM" "DEFINES+=QTV3" "DEFINES+=VNLG"
# x86桌面编译(调试)
qmake "DEFINES+=X86TEST"
make
```
---
## 10. 关键配置与宏定义
### 10.1 全局宏 ([myinclude.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/myinclude.h))
| 宏 | 说明 |
|-----|------|
| `__RFID_CARD` | 启用刷卡功能 |
| `__RFID_OLD` | 旧加密算法 |
| `__RFID_IND` | 印度二次加密 |
| `MULTILANGUAGE` | 多语言输入法 |
| `EnableArabic` | 阿拉伯语支持 |
| `EnableBangla` | 孟加拉语支持 |
| `TEST_MODE` | 测试模式 |
| `SIMULATE_PRINTING_MODE` | 模拟打印模式 |
### 10.2 路径定义 ([SysPara.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/SysPara.h))
| 路径宏 | 值 | 说明 |
|--------|-----|------|
| `PARA_ROOT_DIR` | `/home/plg/conf/` | 系统参数目录 |
| `DOC_ROOT_DIR` | `/home/plg/doc/` | 作业文件目录 |
| `SYS_ROOT_DIR` | `/home/plg/` | 系统根目录 |
| `UDISK_DOC_ROOT_DIR` | `/udisk/doc/` | U盘文档目录 |
### 10.3 版本号 ([VerBVersion.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/VerBVersion.h))
```cpp
#define UP_PRINTER_MAJOR_VER 6
#define UP_PRINTER_MINOR_VER 7
#define UP_PRINTER_RIVISION_VER 4
#define SUB_VERSION "_KOR"
```
---
## 11. 新同事上手指南
### 11.1 开发环境搭建
1. **安装Qt**: 推荐 Qt 5.12+ 用于桌面开发调试
2. **获取代码**: 通过SVN或Git获取项目源码
3. **编译运行**:
```bash
cd 项目目录
qmake
make
./QtUI
```
4. **注意事项**: x86桌面版仅用于UI调试,硬件相关功能(串口、FPGA通信)不可用
### 11.2 代码阅读顺序建议
1. **入门**: [main.cpp](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/main.cpp) → [myinclude.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/myinclude.h) → [InkJetPrinter.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/InkJetPrinter.h)
2. **核心数据**: [SysPara.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/SysPara.h) → [DevOp.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/DevOp.h)
3. **UI页面**: [MainWidget](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/mainwidget.h) → [TestPrintWidget](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/testprintwidget.h)
4. **场景系统**: [scene/picscene.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/scene/picscene.h) → [scene/PicSceneLoader.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/scene/PicSceneLoader.h) → [scene/ViewModels.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/scene/ViewModels.h)
5. **通信层**: [Serial/serial.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/Serial/serial.h)
6. **RFID**: [rfid/rfidmanager.h](file:///mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/rfid/rfidmanager.h)
### 11.3 常见开发任务
| 任务 | 涉及文件 | 说明 |
|------|---------|------|
| 添加新图元类型 | `scene/` 目录 | 继承 PicTextItem,实现渲染和序列化 |
| 修改打印设置 | `Printsetting.h/cpp` | 修改 PrintSettingModel 结构体 |
| 添加新语言 | `*.ts` 文件 | 使用 Qt Linguist 编辑翻译 |
| 修改FPGA通信 | `DevOp.h/cpp` + `Serial/` | 注意地址空间分配和CRC校验 |
| 修改UI样式 | `.ui` 文件 | 使用 Qt Designer 编辑 |
| 添加新功能页面 | 仿照现有页面模式 | 创建 .h/.cpp/.ui 三件套 |
### 11.4 关键设计模式
- **单例模式**: `RFIDManager`, `RunTimeCtrl`, `LogInfoCtrl` 使用单例
- **观察者模式**: 大量使用Qt信号槽进行模块间通信
- **策略模式**: 打印触发模式(时延/电眼/同步器)通过枚举切换
- **MVC变体**: 场景系统使用 ViewModel (`ViewModels.h`) 解耦编辑器与图元
### 11.5 调试技巧
1. **日志**: 使用 `qInfo()/qWarning()/qCritical()` 输出,日志文件位于 `/home/plg/conf/`
2. **崩溃分析**: `dump.h/cpp` 安装了信号处理器,崩溃时会生成core dump
3. **FPGA调试**: 可通过 `SystemManage` 界面手动控制阀门和泵
4. **模拟模式**: 设置 `SIMULATE_PRINTING_MODE 1` 可在无硬件环境调试打印逻辑
### 11.6 注意事项
- **不要直接操作硬件寄存器**: 必须通过 `DevOp` 类进行设备操作
- **参数持久化**: 修改系统参数后需调用 `SysPara` 的保存方法
- **线程安全**: 串口通信在独立线程中,注意跨线程信号槽的连接方式
- **版本兼容**: 注意 `#ifdef` 条件编译宏,不同客户可能有不同的功能组合
- **UI文件**: 项目使用 `.ui` 文件定义界面,编译时通过 `uic` 生成 `ui_*.h` 文件
- **编码**: 所有源文件使用 UTF-8 编码,字符串使用 `QString::fromUtf8()` 或 `tr()`
---
> **文档维护**: 本文档应随项目迭代持续更新。如有疑问,请联系项目负责人。
+118 -74
View File
@@ -15,6 +15,33 @@ constexpr char* SUPER_CARD_ID = "397F4A8E5B9F6CA07D395B28E4A6";
RFIDManager::RFIDManager() RFIDManager::RFIDManager()
{ {
comForRFID_init(); comForRFID_init();
m_confirmBox = new QMessageBox(QMessageBox::Warning, tr("警告"), QString(),
QMessageBox::Ok | QMessageBox::Cancel);
m_confirmBox->setWindowModality(Qt::ApplicationModal);
m_confirmTimer = new QTimer(this);
connect(m_confirmTimer, &QTimer::timeout, this, [this]() {
int count = m_confirmBox->property("rfidCountDown").toInt();
m_confirmBox->setText(m_confirmBoxTxt.arg(count));
m_confirmBox->update();
count--;
m_confirmBox->setProperty("rfidCountDown", count);
if(count == 0) {
m_confirmBox->accept();
}
});
connect(m_confirmBox, &QMessageBox::accepted, this, [this]() {
if(m_confirmCallback) {
m_confirmCallback();
}
});
connect(m_confirmBox, &QMessageBox::finished, this, [this](int result) {
m_confirmTimer->stop();
m_confirmCallback = nullptr;
});
} }
void RFIDManager::CheckSumOut(UCHAR *buf, UCHAR len) void RFIDManager::CheckSumOut(UCHAR *buf, UCHAR len)
@@ -531,8 +558,6 @@ void RFIDManager::handleCheck(unsigned int addr)
{ {
strRevdata =""; strRevdata ="";
auto res = receiveReadRFID_data(); auto res = receiveReadRFID_data();
// LogInfo::setRunStatusText(tr("receiveReadRFID_data res:%1, data:%2")
// .arg(res).arg(strRevdata));
if(strRevdata.length() > 3) if(strRevdata.length() > 3)
{ {
if((strRevdata.left(3) == QString::number(addr).left(3)) if((strRevdata.left(3) == QString::number(addr).left(3))
@@ -549,27 +574,11 @@ void RFIDManager::handleCheck(unsigned int addr)
if(!sysPara->RFIDPara.needInkCard){ if(!sysPara->RFIDPara.needInkCard){
emit sig_showMsg(tr("不需要刷墨水卡!")); emit sig_showMsg(tr("不需要刷墨水卡!"));
} }
// else if(sysPara->status.statWarn & WARN_STATUS_INK_WARN){ else {
// emit sig_showMsg(tr("请先添加墨水再刷墨水卡!")); auto blockData = strRevdata;
// } cardTouchConfirmAsync(tr("确认要刷墨水卡吗?"), tr("确认要刷墨水卡吗? %1 秒后将自动确认"), [this, blockData]{
else markInkCard(blockData);
{ });
auto ok = cardTouchConfirm(tr("确认要刷墨水卡吗?"), tr("确认要刷墨水卡吗? %1 秒后将自动确认"));
if (!ok){
LogInfo::setRunStatusText("The user has canceled the operation about marking the ink card.");
}
else {
auto ret = RFIDmarkCardUsedAsync(0, strRevdata);
if(ret >= 0 || ret == -11){
const QString msg = sysPara->RFIDPara.addedInk ? tr("刷墨水卡成功!") : tr("刷墨水卡成功, 请及时将该墨水卡对应的墨水添加到机器中");
emit sig_showMsg(msg);
sysPara->RFIDPara.cardInk = true;
sysPara->saveRFIDPara();
}
else if(ret == -10) {
emit sig_showMsg(tr("刷超级墨水卡失败,请联系厂商。"));
}
}
} }
} }
else if(strRevdata.at(3) == 'B') else if(strRevdata.at(3) == 'B')
@@ -582,61 +591,20 @@ void RFIDManager::handleCheck(unsigned int addr)
if(!sysPara->RFIDPara.needSolventCard){ if(!sysPara->RFIDPara.needSolventCard){
emit sig_showMsg(tr("不需要刷溶剂卡!")); emit sig_showMsg(tr("不需要刷溶剂卡!"));
} }
// else if(sysPara->status.statWarn & WARN_STATUS_SOLV_WARN){ else {
// emit sig_showMsg(tr("请先添加溶剂再刷溶剂卡!")); auto blockData = strRevdata;
// } cardTouchConfirmAsync(tr("确认要刷溶剂卡吗?"), tr("确认要刷溶剂卡吗? %1 秒后将自动确认"), [this, blockData](){
else markSolventCard(blockData);
{ });
auto ok = cardTouchConfirm(tr("确认要刷溶剂卡吗?"), tr("确认要刷溶剂卡吗? %1 秒后将自动确认"));
if (!ok){
LogInfo::setRunStatusText("The user has canceled the operation about marking the solvent card.");
}
else {
auto ret = RFIDmarkCardUsedAsync(1, strRevdata);
if(ret >= 0 || ret == -11) {
const QString msg = sysPara->RFIDPara.addedSolvent ? tr("刷溶剂卡成功!") : tr("刷溶剂卡成功, 请及时将该溶剂卡对应的溶剂添加到机器中");
emit sig_showMsg(msg);
sysPara->RFIDPara.cardSolvent = true;
sysPara->saveRFIDPara();
}
else if(ret == -10) {
emit sig_showMsg(tr("刷超级溶剂水卡失败,请联系厂商。"));
}
}
} }
} }
else if(strRevdata.at(3) == 'C') else if(strRevdata.at(3) == 'C')
{ {
auto ok = cardTouchConfirm(tr("确认要刷保养卡吗?"), tr("确认要刷保养卡吗? %1 秒后将自动确认")); auto blockData = strRevdata;
if (ok){ cardTouchConfirmAsync(tr("确认要刷保养卡吗?"), tr("确认要刷保养卡吗? %1 秒后将自动确认"), [this, blockData](){
auto ret = RFIDmarkCardUsedAsync(2, strRevdata); markMaintenanceCard(blockData);
if(ret >= 0 || ret == -11) {//把卡数据清零 });
emit sig_maintenanceShow();
sysPara->RFIDPara.needMaintenance = false;
sysPara->status.needMaintenanceRemind = false;
QDate curDate = QDate::currentDate();
sysPara->RFIDPara.maintenanceDate = curDate.toString("yyMMdd").toInt();
QDate mtEndDate = curDate.addMonths(sysPara->RFIDPara.maintenanceInterval);
sysPara->RFIDPara.maintenanceEndDate = mtEndDate.toString("yyMMdd").toInt();
sysPara->RFIDPara.runTime = 0;
RunTimeCtrl::instance()->updatePara(&sysPara->RFIDPara);
sysPara->saveRFIDPara();
emit sig_showMsg(tr("刷保养卡成功!"));
}
else if(ret == -10) {
emit sig_showMsg(tr("刷超级保养卡失败,请联系厂商。"));
}
else {
LogInfo::setRunStatusText("Failed to mark maintenance card!");
}
}
else {
qDebug() << "用户取消了刷卡";
}
} }
else else
{ {
@@ -1117,6 +1085,82 @@ bool RFIDManager::cardTouchConfirm(QString msgTxt, QString variableMsgTxt)
return box.result() == QMessageBox::Ok || box.result() == QMessageBox::Accepted; return box.result() == QMessageBox::Ok || box.result() == QMessageBox::Accepted;
} }
void RFIDManager::cardTouchConfirmAsync(QString msgTxt, QString variableMsgTxt, std::function<void()> callback)
{
if(m_confirmBox->isVisible()) {
qDebug() << "A dialog is already being displayed. Ignoring the new request.";
return;
}
int rfidCountDown = sysPara->para.rfidCountDown;
m_confirmBoxTxt = variableMsgTxt;
m_confirmCallback = callback;
m_confirmBox->setText(msgTxt);
m_confirmBox->setProperty("rfidCountDown", rfidCountDown);
if(rfidCountDown > 0) {
m_confirmBox->setText(m_confirmBoxTxt.arg(rfidCountDown));
m_confirmTimer->start(1000);
}
m_confirmBox->show();
}
void RFIDManager::markInkCard(const QString &blockData)
{
auto ret = RFIDmarkCardUsedAsync(0, blockData);
if(ret >= 0 || ret == -11){
const QString msg = sysPara->RFIDPara.addedInk ? tr("刷墨水卡成功!") : tr("刷墨水卡成功, 请及时将该墨水卡对应的墨水添加到机器中");
emit sig_showMsg(msg);
sysPara->RFIDPara.cardInk = true;
sysPara->saveRFIDPara();
}
else if(ret == -10) {
emit sig_showMsg(tr("刷超级墨水卡失败,请联系厂商。"));
}
}
void RFIDManager::markSolventCard(const QString &blockData)
{
auto ret = RFIDmarkCardUsedAsync(1, blockData);
if(ret >= 0 || ret == -11) {
const QString msg = sysPara->RFIDPara.addedSolvent ? tr("刷溶剂卡成功!") : tr("刷溶剂卡成功, 请及时将该溶剂卡对应的溶剂添加到机器中");
emit sig_showMsg(msg);
sysPara->RFIDPara.cardSolvent = true;
sysPara->saveRFIDPara();
}
else if(ret == -10) {
emit sig_showMsg(tr("刷超级溶剂水卡失败,请联系厂商。"));
}
}
void RFIDManager::markMaintenanceCard(const QString &blockData)
{
auto ret = RFIDmarkCardUsedAsync(2, blockData);
if(ret >= 0 || ret == -11) {//把卡数据清零
emit sig_maintenanceShow();
sysPara->RFIDPara.needMaintenance = false;
sysPara->status.needMaintenanceRemind = false;
QDate curDate = QDate::currentDate();
sysPara->RFIDPara.maintenanceDate = curDate.toString("yyMMdd").toInt();
QDate mtEndDate = curDate.addMonths(sysPara->RFIDPara.maintenanceInterval);
sysPara->RFIDPara.maintenanceEndDate = mtEndDate.toString("yyMMdd").toInt();
sysPara->RFIDPara.runTime = 0;
RunTimeCtrl::instance()->updatePara(&sysPara->RFIDPara);
sysPara->saveRFIDPara();
emit sig_showMsg(tr("刷保养卡成功!"));
}
else if(ret == -10) {
emit sig_showMsg(tr("刷超级保养卡失败,请联系厂商。"));
}
else {
LogInfo::setRunStatusText("Failed to mark maintenance card!");
}
}
void RFIDManager::updateStatus() void RFIDManager::updateStatus()
{ {
if (sysPara) { if (sysPara) {
@@ -1381,4 +1425,4 @@ int RFIDManager::RFIDQueryWalletAsync()
LogInfo::setRunStatusText(("Failed to query card balance, logic error")); LogInfo::setRunStatusText(("Failed to query card balance, logic error"));
qDebug() << "Failed to query card balance, logic error"; qDebug() << "Failed to query card balance, logic error";
return -1; return -1;
} }
+15 -1
View File
@@ -6,8 +6,10 @@
#include <QObject> #include <QObject>
#include <QTimer> #include <QTimer>
#include <QMutex> #include <QMutex>
#include <functional>
class SysPara; class SysPara;
class QMessageBox;
enum CARD_TYPE{ enum CARD_TYPE{
INK = 1, INK = 1,
SOLVENT = 2, SOLVENT = 2,
@@ -102,11 +104,23 @@ private:
void initTimer(); void initTimer();
bool cardTouchConfirm(QString msgTxt, QString variableMsgTxt); bool cardTouchConfirm(QString msgTxt, QString variableMsgTxt);
void cardTouchConfirmAsync(QString msgTxt, QString variableMsgTxt, std::function<void()> callback);
void markInkCard(const QString &strRevdata = QString());
void markSolventCard(const QString &strRevdata = QString());
void markMaintenanceCard(const QString &strRevdata = QString());
//刷卡新需求,刷卡和倒溶剂/墨水不分先后,都可以去除报警提示。 //刷卡新需求,刷卡和倒溶剂/墨水不分先后,都可以去除报警提示。
StatuStruct status; StatuStruct status;
void updateStatus(); void updateStatus();
QTimer* warningTimer = nullptr; QTimer* warningTimer = nullptr;
QMessageBox* m_confirmBox = nullptr;
QTimer* m_confirmTimer = nullptr;
QString m_confirmBoxTxt;
std::function<void()> m_confirmCallback;
int waitStatus(); int waitStatus();
bool waitCard(); bool waitCard();
signals: signals:
@@ -132,4 +146,4 @@ private slots:
void slt_intervalTimerHandle(); void slt_intervalTimerHandle();
}; };
#endif // RFIDMANAGER_H #endif // RFIDMANAGER_H
+2 -1
View File
@@ -190,7 +190,7 @@ TestPrintWidget::TestPrintWidget(QWidget* parent)
} else { } else {
saveFile(); saveFile();
} }
emit sig_updatePrintContent(xmlfileName); emit sig_updatePrintContent(xmlfileName);
} }
@@ -383,6 +383,7 @@ void TestPrintWidget::on_saveButton_pressed()
filename += ".xml"; filename += ".xml";
saveFile(filename); saveFile(filename);
xmlfileName = filename;
} }
#endif #endif
} }