2026-07-02_14_31_VX5 韩国
This commit is contained in:
+1
-1
@@ -1054,7 +1054,7 @@ void InkJetPrinter::timerHandler(void){
|
||||
qInfo() << "Self check done and send print para to FPGA";
|
||||
sysPara->devOp->setHighVoltageAmplitudetoSTM32();
|
||||
sysPara->devOp->writeFpgaCalandar(0);
|
||||
sysPara->devOp->writeFpgaPrintPara();
|
||||
sysPara->devOp->writeFpgaPrintPara(true);
|
||||
|
||||
if(sysPara->para.Reciclar)
|
||||
test_flag = 1;
|
||||
|
||||
+25
-1
@@ -50,6 +50,9 @@ PrintSetting::PrintSetting(QWidget *parent) :
|
||||
|
||||
ui->eleEyeEdgetTriggerInner->click();
|
||||
ui->radioButtonSynTriggerInner->click();
|
||||
|
||||
ui->tabWidget->setCurrentIndex(0);
|
||||
ui->tabWidget->removeTab(4);
|
||||
}
|
||||
|
||||
PrintSetting::~PrintSetting()
|
||||
@@ -250,6 +253,18 @@ void PrintSetting::updateSysStatusToUI(){
|
||||
|
||||
void PrintSetting::on_pushButtonApplyPulseFix_clicked()
|
||||
{
|
||||
float circle = ui->lineEditDiameter->text().toFloat() * 3.1415926;
|
||||
float pulseNum = ui->lineEditPulsePerCycle->text().toFloat();
|
||||
if(circle != 0){
|
||||
auto pulseNumPerMM = 4*pulseNum/circle; //电路和Fpga做了4倍频处理,这里乘以4是为了匹配4倍频的处理进行正确的显示
|
||||
m_model.encoderPulsePerMm = pulseNumPerMM;
|
||||
auto b = ui->lineEditTriggerPulsePerMm->blockSignals(true);
|
||||
ui->lineEditTriggerPulsePerMm->setText(QString::number(pulseNumPerMM));
|
||||
ui->lineEditTriggerPulsePerMm->blockSignals(b);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
if(!sysPara)
|
||||
return;
|
||||
|
||||
@@ -385,12 +400,13 @@ void PrintSetting::setTriggerUI(int triggerMode){
|
||||
if(triggerMode == PRINT_TRIGGER_SYNCHRONIZER)
|
||||
{
|
||||
ui->labelEleEyeTriggerPrintDelay->setText(tr("触发延时(毫米)"));
|
||||
ui->labelEleEyeTriggerIntervalMs->setText(tr("重复间隔(毫米)"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->labelEleEyeTriggerPrintDelay->setText(tr("触发延时(毫秒)"));
|
||||
ui->labelEleEyeTriggerIntervalMs->setText(tr("重复间隔(毫秒)"));
|
||||
}
|
||||
ui->labelEleEyeTriggerIntervalMs->setText(tr("重复间隔(毫秒)"));
|
||||
ui->labelEleMaskTime->setText(tr("封锁时间(毫秒)"));
|
||||
}
|
||||
if(triggerMode == PRINT_TRIGGER_ELEC_EYE_VOLTATE_LEVEL
|
||||
@@ -582,7 +598,13 @@ void PrintSetting::on_eleEyeEdgetTriggerExternal_clicked()
|
||||
m_model.eleEyeTriggerSynPrintEn = ui->radioButtonSynTriggerExternal->isChecked();
|
||||
on_checkEyeTriggerSynPrintEn_clicked(m_model.eleEyeTriggerSynPrintEn);
|
||||
|
||||
if(ui->radioButtonEleEyeEdgetTrigger->isChecked())
|
||||
m_model.triggerMode = PRINT_TRIGGER_ELEC_EYE_EDGE;
|
||||
else
|
||||
m_model.triggerMode = PRINT_TRIGGER_ELEC_EYE_VOLTATE_LEVEL;
|
||||
|
||||
setEnableEleEyeSetting(true);
|
||||
setTriggerUI(m_model.triggerMode);
|
||||
}
|
||||
|
||||
void PrintSetting::setEnableSynTriggerSetting(bool flag){
|
||||
@@ -666,9 +688,11 @@ void PrintSetting::on_radioButtonSynTriggerExternal_clicked()
|
||||
}
|
||||
|
||||
m_model.eleEyeTriggerSynPrintEn = ui->eleEyeEdgetTriggerExternal->isChecked();
|
||||
|
||||
on_checkEyeTriggerSynPrintEn_clicked(m_model.eleEyeTriggerSynPrintEn);
|
||||
setEnableSynTriggerSetting(true);
|
||||
setEnableEleEyeSetting(!ui->eleEyeEdgetTriggerInner->isChecked());
|
||||
setTriggerUI(m_model.triggerMode);
|
||||
}
|
||||
|
||||
void PrintSetting::on_radioButtonEleEyeEdgetTrigger_clicked()
|
||||
|
||||
+95
-31
@@ -561,39 +561,13 @@
|
||||
<widget class="QWidget" name="layoutWidget_5">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>210</x>
|
||||
<x>230</x>
|
||||
<y>60</y>
|
||||
<width>521</width>
|
||||
<width>341</width>
|
||||
<height>81</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="pushButtonApplyPulseFix">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>脉冲数修正</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="image.qrc">
|
||||
<normaloff>:/icon/pulse.png</normaloff>:/icon/pulse.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelTriggerPulsePerMm">
|
||||
<property name="minimumSize">
|
||||
@@ -629,7 +603,7 @@
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<width>170</width>
|
||||
<height>37</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -638,7 +612,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="comboBoxSyncActType">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
@@ -839,6 +813,96 @@
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="lineEditDiameter">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>400</x>
|
||||
<y>220</y>
|
||||
<width>161</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhNone</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="labelDiameter">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>220</x>
|
||||
<y>220</y>
|
||||
<width>171</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>同步器直径(毫米):</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="labelPulsePerCycle">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>260</x>
|
||||
<y>170</y>
|
||||
<width>131</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>脉冲数/转:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="lineEditPulsePerCycle">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>400</x>
|
||||
<y>170</y>
|
||||
<width>161</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhNone</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="pushButtonApplyPulseFix">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>400</x>
|
||||
<y>260</y>
|
||||
<width>161</width>
|
||||
<height>37</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>脉冲数修正</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="image.qrc">
|
||||
<normaloff>:/icon/pulse.png</normaloff>:/icon/pulse.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="other">
|
||||
<attribute name="title">
|
||||
@@ -1301,7 +1365,7 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<widget class="QWidget" name="debugTab">
|
||||
<attribute name="title">
|
||||
<string>调试设置</string>
|
||||
</attribute>
|
||||
|
||||
@@ -32,8 +32,10 @@ greaterThan(QT_VER_MAJ, 5) | greaterThan(QT_VER_MIN, 9) QT += serialbus
|
||||
greaterThan(QT_MAJOR_VERSION, 4):QMAKE_CXXFLAGS += -fpermissive
|
||||
greaterThan(QT_MAJOR_VERSION, 4):CONFIG += c++11
|
||||
|
||||
# 启用release下带调试信息,详见`VX5 Release编译发行.md`,如果不需要,可以注释掉,但请在软件绝对稳定时注释
|
||||
CONFIG += qt warn_on debug_and_release
|
||||
QMAKE_CXXFLAGS += -g
|
||||
|
||||
#QMAKE_POST_LINK += ulimit -c unlimited
|
||||
LIBS += -ldl
|
||||
|
||||
@@ -177,7 +179,8 @@ HEADERS += Public.h \
|
||||
Log/logmessage.h \
|
||||
Log/logglobal.h \
|
||||
loginfoctrl.h \
|
||||
runtimectrl.h
|
||||
runtimectrl.h \
|
||||
dump.h
|
||||
|
||||
|
||||
FORMS += \
|
||||
@@ -232,6 +235,7 @@ SOURCES +=\
|
||||
mainwidget.cpp \
|
||||
testprintwidget.cpp\
|
||||
main.cpp \
|
||||
dump.cpp \
|
||||
InkJetPrinter.cpp \
|
||||
MyInputMethod.cpp \
|
||||
InputMethod.cpp \
|
||||
@@ -383,15 +387,3 @@ if(contains(DEFINES,__2451WIFI__))
|
||||
#INCLUDEPATH += /home/student/newDisk/2451_linux/wpa_supplicant_hostapd/wpa_supplicant_hostapd-0.8/src
|
||||
#INCLUDEPATH += /home/student/newDisk/2451_linux/wpa_supplicant_hostapd/wpa_supplicant_hostapd-0.8/src/utils
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -199,7 +199,7 @@ void SystemSetting::applyUIPara(){
|
||||
quint16 diaphramOffset = 5*(ui->comboBoxDiaphramOffset->currentIndex()+1);
|
||||
sysPara->para.diaphramOffset = diaphramOffset;
|
||||
//sysPara->para.mixValveisNewVersion = ui->radioButtonMixValveNewVersion->isChecked();
|
||||
if(sysPara->devOp->writeFpgaPrintPara() != -1){
|
||||
if(sysPara->devOp->writeFpgaPrintPara(true) != -1){
|
||||
QMessageBox::information(this,tr("提示"),tr("应用成功"));
|
||||
}
|
||||
emit changeEditMode(sysPara->para.EditMode);
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
## 带崩溃转储功能的VX5 Release 编译发行、dump收集、问题分析
|
||||
由于VX5相比VX4在稳定性上有些许差异,目前VX5在用户端仍然可能存在崩溃问题,作为兜底方案,我们引入了崩溃转储功能。
|
||||
崩溃转储功能是工程化的软件项目中几乎必有的一个功能,它可以在软件崩溃时,将当前程序的函数调用栈、寄存器状态、内存状态等保存到dump上,开发人员依据dump文件结合程序日志可以快速定位到崩溃的位置。
|
||||
带崩溃转储的发行版发布、dump收集、问题分析,步骤是:使用带-g编译标记编译release版本、生成带调试符号的可执行文件、分离调试符号生成符号调试表和不带调试符号的发行版可执行程序、将发行版可执行程序发布给用户上机运行、运行过程中触发崩溃、收集dump文件、将dump与之前分离的调试信息表以及发行版可执行程序放在一起,使用 addr2line 等工具封装为脚本将dump中的需要观察的调用栈地址、程序地址等转换为包含源码位置等的信息,最后进行源码分析并修复。
|
||||
|
||||
## 项目先决条件
|
||||
在进行此方式编译发行前,可能需要检查以下先决条件,虽然这些已经在最新 VX5_V6.7.1 和 韩国VX5 项目中默认配置。
|
||||
1. QtUI.pro 中需包含如下配置:
|
||||
``` pro
|
||||
CONFIG += qt warn_on debug_and_release # 启用警告和调试信息
|
||||
QMAKE_CXXFLAGS += -g # 启用 -g 编译标记
|
||||
#QMAKE_POST_LINK += ulimit -c unlimited
|
||||
LIBS += -ldl # 链接 dl 库,用于获取当前程序的基址
|
||||
|
||||
# ... 其他内容
|
||||
|
||||
# 包含 dump转储功能的头文件和源文件
|
||||
HEADERS += dump.h
|
||||
SOURCES += dump.cpp
|
||||
```
|
||||
2. `main.cpp` 中需包含 `dump.h` 文件,并在 `QApplication app` 后第一时间调用 `installSignalHandler()` 函数,用于安装信号处理函数。
|
||||
```cpp
|
||||
QApplication a(argc, argv);
|
||||
gAppNameStr = QString::fromUtf8(argv[0]);
|
||||
installSignalHandlers(); // 这里安装信号处理函数
|
||||
```
|
||||
3. `dump.cpp` `dump.h` 已添加至最新的标准VX5、韩国VX5项目中。
|
||||
4. 用于分离调试符号的脚本 `strip_debug.sh` 已添加至项目源码目录中。
|
||||
5. 用于分析dump文件的脚本 `resolve_dump.sh` 已添加至项目源码目录中。
|
||||
|
||||
## 编译发行
|
||||
1. 使用QtCreator IDE 一键release编译,或使用VScode 配置命令行,编译release版本的带调试符号的、满足项目需求的可执行文件,得到`QtUI`。
|
||||
```shell
|
||||
inkjet@ubuntu:/mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface$ ls ../arm_build/QtUI* -lh
|
||||
-rwxrwxrwx 1 root root 55M 7月 1 10:22 ../arm_build/QtUI
|
||||
```
|
||||
可以看到`QtUI`的大小为55M,说明包含了调试符号,但是因为携带了调试符号,运行时可能会占用更多内存,且不利于用户发行更新,同时还包含了源码信息,存在泄漏风险。为此我们需要将调试符号分离出来,生成符号调试表和不带调试符号的发行版可执行程序。
|
||||
|
||||
2. 使用之前编译`QtUI`时使用的交叉编译工具链中的 `objcopy` 工具,将`QtUI`的调试符号分离出来,对于 `InkjetUbuntu-T1` 虚拟机,已经包含了 `objcopy` 工具。
|
||||
|
||||
使用命令行分离:进入QtUI所在目录,按如下命令进行操作:
|
||||
```shell
|
||||
inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ aarch64-linux-gnu-objcopy --only-keep-debug QtUI QtUI.dbg # 分离调试符号生成符号调试表
|
||||
inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ aarch64-linux-gnu-objcopy --strip-debug QtUI QtUI # 移除调试符号
|
||||
inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ aarch64-linux-gnu-objcopy --add-gnu-debuglink=QtUI.dbg QtUI # 关联调试符号文件和发行程序
|
||||
inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ ls ./QtUI* -lh
|
||||
-rwxrwxrwx 1 root root 6.7M 7月 1 10:43 ./QtUI
|
||||
-rwxrwxrwx 1 root root 48M 7月 1 10:43 ./QtUI.dbg
|
||||
```
|
||||
注意输出结果,一个大小为6.7M的`QtUI`,该文件与平常不带`-g`编译出来的`QtUI`一致,用于用户发布;一个大小为48M的`QtUI.dbg`,存放调试符号信息,用于内部保留日后分析dump文件,两个文件进行了关联,因此两个文件在内部必须成对存放,不可缺一。
|
||||
|
||||
第二种方法:使用`strip_debug.sh`脚本分离调试符号
|
||||
进入QtUI所在目录,按如下命令进行操作:
|
||||
```shell
|
||||
inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ ./strip_debug.sh
|
||||
```
|
||||
或者指定QtUI路径:
|
||||
```shell
|
||||
inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ ./strip_debug.sh QtUI
|
||||
```
|
||||
|
||||
两种方式都可以生成`QtUI`和`QtUI.dbg`两个文件,此时将 `QtUI` 附上更新说明发布。将 `QtUI.dbg` `QtUI` 打包上传SVN仓库内部存放。
|
||||
```shell
|
||||
inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ ls ./QtUI_V6.7.1_韩国_2026年7月* -lh
|
||||
-rwxrwxrwx 1 root root 23M 7月 1 11:11 ./QtUI_V6.7.1_韩国_2026年7月1日11_11_08.zip
|
||||
```
|
||||
注意明确标准编译时间,避免与用户端软件编译时间不一致导致的dump文件时间错误。
|
||||
将携带`QtUI.dbg`的`QtUI_V6.7.1_韩国_2026年7月1日11_11_08.zip`上传至 `https://pc-20181215fqqf/svn/src/QTUI软件版本/VX5` 仓库。
|
||||
|
||||
然后仅将 `QtUI`与发行说明打包按正常流程发布。
|
||||
|
||||
## 收集dump文件
|
||||
当用户端或产线测试发生崩溃,dump文件会被收集到`/home/plg/loginfo`目录下。
|
||||
```shell
|
||||
cat@lubancat:/home/plg/loginfo$ ls /home/plg/loginfo/dumpfile_2026_07_01_11_33_07.txt -lh
|
||||
-rw-r--r-- 1 root root 3.1K 7月 1 11:33 /home/plg/loginfo/dumpfile_2026_07_01_11_33_07.txt
|
||||
```
|
||||
使用 `QtUI` 的日志导出功能,可将所有 dump 文件导出至U盘,dump 文件格式为 `dumpfile_YYYY_MM_DD_HH_MM_SS.txt`,按照崩溃时间记录。
|
||||
拿到的dump文件可能如下
|
||||
|
||||
```txt
|
||||
Stack trace info:
|
||||
Base address: 0x557162e000
|
||||
|
||||
/home/plg/QtUI(+0x6b29c) [0x557169929c]
|
||||
??:?
|
||||
|
||||
/home/plg/QtUI(+0x6b96c) [0x557169996c]
|
||||
??:?
|
||||
|
||||
linux-vdso.so.1(__kernel_rt_sigreturn+0) [0x7f9d72458c]
|
||||
??:0
|
||||
...... 省略更多....
|
||||
```
|
||||
注意dump文件中缺失了源码信息,需要结合之前的`QtUI.dbg`文件进行分析。
|
||||
|
||||
## 分析dump文件
|
||||
由于之前对编译好的`QtUI`进行了调试符号分离,因此崩溃捕获到的dump信息与普通不带`-g`编译的`QtUI`崩溃捕获的dump一样,会缺少调试符号。
|
||||
因此我们将该dump文件与之前内部保存的 `QtUI` `QtUI.dbg` 放在一起,进行一点处理就可以获得完整的调试源码信息。
|
||||
执行脚本`resolve_dump.sh`,即可获得完整的dump信息。
|
||||
```shell
|
||||
inkjet@ubuntu:/mnt/hgfs/share/bSpec/arm_build$ ./resolve_dump.sh ./dumpfile_2026_07_01_11_33_07.txt
|
||||
Base address: 0x557162e000
|
||||
QtUI 路径: /mnt/hgfs/share/bSpec/arm_build/QtUI
|
||||
QtUI.dbg: /mnt/hgfs/share/bSpec/arm_build/QtUI.dbg
|
||||
输出文件: ./dumpfile_2026_07_01_11_33_07_resolved.txt
|
||||
|
||||
正在解析 dump 文件: ./dumpfile_2026_07_01_11_33_07.txt
|
||||
=========================================
|
||||
|
||||
Stack trace info:
|
||||
|
||||
Base address: 0x557162e000
|
||||
|
||||
|
||||
/home/plg/QtUI(+0x6b29c) [0x557169929c]
|
||||
createStackTraceFile(char const*, char const*) at /mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/../arm_build/../800X600_New_SystemProject-_NewInterface/dump.cpp:54
|
||||
|
||||
|
||||
/home/plg/QtUI(+0x6b96c) [0x557169996c]
|
||||
signalHandler(int) at /mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/../arm_build/../800X600_New_SystemProject-_NewInterface/dump.cpp:96 (discriminator 9)
|
||||
|
||||
|
||||
linux-vdso.so.1(__kernel_rt_sigreturn+0) [0x7f9d72458c]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Widgets.so.5(_ZNK7QWidget11windowTitleEv+0xc) [0x7f9d1ad56c]
|
||||
??:0
|
||||
|
||||
/home/plg/QtUI(+0x189f30) [0x55717b7f30]
|
||||
SetNumDlg::on_btn_confirm_clicked() at /mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/../arm_build/../800X600_New_SystemProject-_NewInterface/setnumdlg.cpp:39
|
||||
|
||||
|
||||
/home/plg/QtUI(+0x1b14d0) [0x55717df4d0]
|
||||
SetNumDlg::qt_metacall(QMetaObject::Call, int, void**) at /mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/../arm_build/tmp/moc_setnumdlg.cpp:147
|
||||
|
||||
...... 省略更多....
|
||||
```
|
||||
|
||||
注意输出结果
|
||||
``` text
|
||||
SetNumDlg::on_btn_confirm_clicked() at /mnt/hgfs/share/bSpec/800X600_New_SystemProject-_NewInterface/../arm_build/../800X600_New_SystemProject-_NewInterface/setnumdlg.cpp:39
|
||||
```
|
||||
说明崩溃位于`setnumdlg.cpp`文件的`39`行。同时`resolve_dump.sh`脚本会生成`dumpfile_2026_07_01_11_33_07_resolved.txt`文件。
|
||||
@@ -0,0 +1,106 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <link.h>
|
||||
#include <dlfcn.h>
|
||||
#include <execinfo.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <cxxabi.h>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
|
||||
#include <QString>
|
||||
#include "dump.h"
|
||||
#include <QDateTime>
|
||||
|
||||
extern QString gAppNameStr;
|
||||
|
||||
static uintptr_t g_base_address = 0;
|
||||
|
||||
static int find_base_address(struct dl_phdr_info *info, size_t size, void *data)
|
||||
{
|
||||
(void)size;
|
||||
(void)data;
|
||||
if (info->dlpi_name == nullptr || info->dlpi_name[0] == '\0') {
|
||||
g_base_address = info->dlpi_addr;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uintptr_t get_base_address()
|
||||
{
|
||||
if (g_base_address == 0) {
|
||||
dl_iterate_phdr(find_base_address, nullptr);
|
||||
}
|
||||
return g_base_address;
|
||||
}
|
||||
|
||||
void createStackTraceFile(const char *fileName, const char *appName)
|
||||
{
|
||||
std::ofstream file(fileName);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Error opening file: " << fileName << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
void *array[50];
|
||||
size_t size;
|
||||
char **strings;
|
||||
|
||||
size = backtrace(array, 50);
|
||||
if (size == 0) {
|
||||
std::cerr << "No stack frames obtained" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
uintptr_t base_addr = get_base_address();
|
||||
|
||||
strings = backtrace_symbols(array, size);
|
||||
file << "Stack trace info:\n";
|
||||
file << "Base address: 0x" << std::hex << base_addr << std::dec << "\n\n";
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
file << strings[i] << std::endl;
|
||||
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(array[i]);
|
||||
uintptr_t offset = addr - base_addr;
|
||||
|
||||
char cmd[512];
|
||||
snprintf(cmd, sizeof(cmd), "addr2line -e %s 0x%lx", appName, offset);
|
||||
|
||||
FILE *fp = popen(cmd, "r");
|
||||
if (fp) {
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
file << " " << line;
|
||||
}
|
||||
pclose(fp);
|
||||
} else {
|
||||
std::cerr << "Failed to run addr2line command" << std::endl;
|
||||
}
|
||||
file << std::endl;
|
||||
}
|
||||
|
||||
free(strings);
|
||||
file.close();
|
||||
}
|
||||
|
||||
static void signalHandler(int signum)
|
||||
{
|
||||
QString dumpFileName = QString("/home/plg/loginfo/dumpfile_%1.txt").arg(QDateTime::currentDateTime().toString("yyyy_MM_dd_hh_mm_ss"));
|
||||
QString appName = gAppNameStr;
|
||||
createStackTraceFile(dumpFileName.toUtf8().data(), appName.toUtf8().data());
|
||||
exit(signum);
|
||||
}
|
||||
|
||||
void installSignalHandlers()
|
||||
{
|
||||
signal(SIGSEGV, signalHandler);
|
||||
signal(SIGABRT, signalHandler);
|
||||
signal(SIGILL, signalHandler);
|
||||
signal(SIGFPE, signalHandler);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef DUMP_H
|
||||
#define DUMP_H
|
||||
|
||||
void installSignalHandlers();
|
||||
|
||||
#endif // DUMP_H
|
||||
@@ -0,0 +1,105 @@
|
||||
Stack trace info:
|
||||
Base address: 0x557162e000
|
||||
|
||||
/home/plg/QtUI(+0x6b29c) [0x557169929c]
|
||||
??:?
|
||||
|
||||
/home/plg/QtUI(+0x6b96c) [0x557169996c]
|
||||
??:?
|
||||
|
||||
linux-vdso.so.1(__kernel_rt_sigreturn+0) [0x7f9d72458c]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Widgets.so.5(_ZNK7QWidget11windowTitleEv+0xc) [0x7f9d1ad56c]
|
||||
??:0
|
||||
|
||||
/home/plg/QtUI(+0x189f30) [0x55717b7f30]
|
||||
??:?
|
||||
|
||||
/home/plg/QtUI(+0x1b14d0) [0x55717df4d0]
|
||||
??:?
|
||||
|
||||
/opt/Qt/lib/libQt5Core.so.5(_ZN11QMetaObject8activateEP7QObjectiiPPv+0x6a8) [0x7f9c72b32c]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Widgets.so.5(_ZN15QAbstractButton7clickedEb+0x3c) [0x7f9d268e4c]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Widgets.so.5(+0x2890ec) [0x7f9d2690ec]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Widgets.so.5(+0x28ac5c) [0x7f9d26ac5c]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Widgets.so.5(_ZN15QAbstractButton17mouseReleaseEventEP11QMouseEvent+0x184) [0x7f9d26aee4]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Widgets.so.5(_ZN7QWidget5eventEP6QEvent+0x218) [0x7f9d1c5eb8]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Widgets.so.5(_ZN19QApplicationPrivate13notify_helperEP7QObjectP6QEvent+0x5c) [0x7f9d181aa0]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Widgets.so.5(_ZN12QApplication6notifyEP7QObjectP6QEvent+0x1038) [0x7f9d18a5fc]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Core.so.5(_ZN16QCoreApplication15notifyInternal2EP7QObjectP6QEvent+0x178) [0x7f9c6fd308]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Widgets.so.5(_ZN19QApplicationPrivate14sendMouseEventEP7QWidgetP11QMouseEventS1_S1_PS1_R8QPointerIS0_Ebb+0x194) [0x7f9d188c24]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Widgets.so.5(+0x200590) [0x7f9d1e0590]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Widgets.so.5(+0x202e9c) [0x7f9d1e2e9c]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Widgets.so.5(_ZN19QApplicationPrivate13notify_helperEP7QObjectP6QEvent+0x5c) [0x7f9d181aa0]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Core.so.5(_ZN16QCoreApplication15notifyInternal2EP7QObjectP6QEvent+0x178) [0x7f9c6fd308]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Gui.so.5(_ZN22QGuiApplicationPrivate17processMouseEventEPN29QWindowSystemInterfacePrivate10MouseEventE+0x68c) [0x7f9cb54e3c]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Gui.so.5(_ZN22QGuiApplicationPrivate17processTouchEventEPN29QWindowSystemInterfacePrivate10TouchEventE+0x17e8) [0x7f9cb53c18]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Gui.so.5(_ZN22QWindowSystemInterface22sendWindowSystemEventsE6QFlagsIN10QEventLoop17ProcessEventsFlagEE+0xe0) [0x7f9cb2ce54]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5XcbQpa.so.5(+0x705e8) [0x7f98a005e8]
|
||||
??:0
|
||||
|
||||
/lib/aarch64-linux-gnu/libglib-2.0.so.0(g_main_context_dispatch+0x250) [0x7f9b625a70]
|
||||
??:0
|
||||
|
||||
/lib/aarch64-linux-gnu/libglib-2.0.so.0(+0xaaebc) [0x7f9b67aebc]
|
||||
??:0
|
||||
|
||||
/lib/aarch64-linux-gnu/libglib-2.0.so.0(g_main_context_iteration+0x34) [0x7f9b622f14]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Core.so.5(_ZN20QEventDispatcherGlib13processEventsE6QFlagsIN10QEventLoop17ProcessEventsFlagEE+0x54) [0x7f9c7584c4]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Core.so.5(_ZN10QEventLoop4execE6QFlagsINS_17ProcessEventsFlagEE+0xf4) [0x7f9c6fb994]
|
||||
??:0
|
||||
|
||||
/opt/Qt/lib/libQt5Core.so.5(_ZN16QCoreApplication4execEv+0x98) [0x7f9c7045a8]
|
||||
??:0
|
||||
|
||||
/home/plg/QtUI(+0x47350) [0x5571675350]
|
||||
??:?
|
||||
|
||||
/lib/aarch64-linux-gnu/libc.so.6(+0x273fc) [0x7f9c0573fc]
|
||||
??:0
|
||||
|
||||
/lib/aarch64-linux-gnu/libc.so.6(__libc_start_main+0x98) [0x7f9c0574cc]
|
||||
??:0
|
||||
|
||||
/home/plg/QtUI(+0x480d0) [0x55716760d0]
|
||||
??:?
|
||||
|
||||
@@ -36,7 +36,12 @@ FileManageWidget::FileManageWidget(QWidget *parent) :
|
||||
connect(ui->systemFileListView, &QListWidget::itemSelectionChanged, this, &FileManageWidget::refreshButtonsStatus);
|
||||
connect(ui->udiskFileListView, &QListWidget::itemSelectionChanged, this, &FileManageWidget::refreshButtonsStatus);
|
||||
|
||||
updateUDiskDevList();
|
||||
m_hasUDisk = checkUDiskPresent();
|
||||
onUDiskStateChanged();
|
||||
|
||||
m_udiskCheckTimer = new QTimer(this);
|
||||
connect(m_udiskCheckTimer, &QTimer::timeout, this, &FileManageWidget::onUDiskCheckTimeout);
|
||||
m_udiskCheckTimer->start(1000);
|
||||
|
||||
m_focusedListView = ui->systemFileListView;
|
||||
m_focusedListView->setFocus();
|
||||
@@ -47,6 +52,7 @@ FileManageWidget::FileManageWidget(QWidget *parent) :
|
||||
|
||||
FileManageWidget::~FileManageWidget()
|
||||
{
|
||||
m_udiskCheckTimer->stop();
|
||||
ui->systemFileListView->removeEventFilter(this);
|
||||
ui->udiskFileListView->removeEventFilter(this);
|
||||
delete ui;
|
||||
@@ -94,20 +100,22 @@ void FileManageWidget::onFilterCBoxSelectChanged(int i)
|
||||
}
|
||||
|
||||
m_currFilterType = i;
|
||||
updateFileListView(ui->systemFileListView);
|
||||
updateFileListView(ui->udiskFileListView);
|
||||
refreshFileList(ui->systemFileListView);
|
||||
if (m_hasUDisk) {
|
||||
refreshFileList(ui->udiskFileListView);
|
||||
}
|
||||
}
|
||||
|
||||
void FileManageWidget::onExportBtnClicked()
|
||||
{
|
||||
copyFiles(ui->systemFileListView, ui->udiskFileListView);
|
||||
updateFileListView(ui->udiskFileListView);
|
||||
refreshFileList(ui->udiskFileListView);
|
||||
}
|
||||
|
||||
void FileManageWidget::onImportBtnClicked()
|
||||
{
|
||||
copyFiles(ui->udiskFileListView, ui->systemFileListView);
|
||||
updateFileListView(ui->systemFileListView);
|
||||
refreshFileList(ui->systemFileListView);
|
||||
}
|
||||
|
||||
void FileManageWidget::onSelectAllBtnClicked()
|
||||
@@ -138,37 +146,59 @@ void FileManageWidget::onDelSelectedBtnClicked()
|
||||
qInfo() << "Delete: " << filename << ", result: " << ret;
|
||||
}
|
||||
|
||||
updateFileListView(m_focusedListView);
|
||||
refreshFileList(m_focusedListView);
|
||||
}
|
||||
|
||||
void FileManageWidget::updateUDiskDevList()
|
||||
bool FileManageWidget::checkUDiskPresent()
|
||||
{
|
||||
bool hasUDisk = false;
|
||||
QFile mountsFile("/proc/mounts");
|
||||
if (mountsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QTextStream in(&mountsFile);
|
||||
while (!in.atEnd()) {
|
||||
const QString line = in.readLine();
|
||||
if (line.contains("/udisk ")) {
|
||||
hasUDisk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
QString mountsContent = QString::fromLocal8Bit(mountsFile.readAll());
|
||||
mountsFile.close();
|
||||
if (mountsContent.lastIndexOf("/udisk ") != -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasUDisk) {
|
||||
QDir dir(UDISK_DOC_ROOT_DIR);
|
||||
hasUDisk = dir.exists();
|
||||
bool FileManageWidget::ensureUDiskDocDir()
|
||||
{
|
||||
QDir dir(UDISK_DOC_ROOT_DIR);
|
||||
if (!dir.exists()) {
|
||||
if (!dir.mkpath(UDISK_DOC_ROOT_DIR)) {
|
||||
qCritical() << "Cannot create UDisk doc dir:" << UDISK_DOC_ROOT_DIR;
|
||||
return false;
|
||||
}
|
||||
qInfo() << "Created UDisk doc dir:" << UDISK_DOC_ROOT_DIR;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileManageWidget::onUDiskCheckTimeout()
|
||||
{
|
||||
bool hasUDisk = checkUDiskPresent();
|
||||
if (hasUDisk == m_hasUDisk) {
|
||||
return;
|
||||
}
|
||||
m_hasUDisk = hasUDisk;
|
||||
onUDiskStateChanged();
|
||||
}
|
||||
|
||||
void FileManageWidget::onUDiskStateChanged()
|
||||
{
|
||||
ui->udiskSelectBox->clear();
|
||||
if (hasUDisk) {
|
||||
if (m_hasUDisk) {
|
||||
ui->udiskSelectBox->addItem(tr("U盘"));
|
||||
ui->udiskSelectBox->setCurrentIndex(0);
|
||||
if (ensureUDiskDocDir()) {
|
||||
refreshFileList(ui->udiskFileListView);
|
||||
} else {
|
||||
ui->udiskFileListView->clear();
|
||||
}
|
||||
} else {
|
||||
ui->udiskFileListView->clear();
|
||||
}
|
||||
|
||||
updateFileListView(ui->udiskFileListView);
|
||||
}
|
||||
|
||||
void FileManageWidget::refreshButtonsStatus()
|
||||
@@ -187,15 +217,14 @@ void FileManageWidget::refreshButtonsStatus()
|
||||
ui->selectAllBtn->setEnabled(m_focusedListView->count() != selectedItems.size());
|
||||
}
|
||||
|
||||
int FileManageWidget::updateFileListView(QListWidget* listView)
|
||||
int FileManageWidget::refreshFileList(QListWidget* listView)
|
||||
{
|
||||
QString searchPath = listView->property("searchPath").toString();
|
||||
|
||||
// 判断目录是否存在
|
||||
QDir dir(searchPath);
|
||||
if(!dir.exists()) {
|
||||
qCritical() << "Cannot open path: " << searchPath;
|
||||
return -1;
|
||||
if (!dir.exists()) {
|
||||
listView->clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 根据过滤类型设置过滤器字符串
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <QWidget>
|
||||
#include <QListWidget>
|
||||
#include <QTimer>
|
||||
|
||||
namespace Ui {
|
||||
class FileManageWidget;
|
||||
@@ -33,13 +34,16 @@ private slots:
|
||||
void onSelectAllBtnClicked();
|
||||
void onCancelAllBtnClicked();
|
||||
void onDelSelectedBtnClicked();
|
||||
void updateUDiskDevList();
|
||||
void onUDiskCheckTimeout();
|
||||
|
||||
void refreshButtonsStatus();
|
||||
|
||||
private:
|
||||
|
||||
int updateFileListView(QListWidget* listView);
|
||||
bool checkUDiskPresent();
|
||||
bool ensureUDiskDocDir();
|
||||
int refreshFileList(QListWidget* listView);
|
||||
void onUDiskStateChanged();
|
||||
|
||||
void copyFiles(QListWidget* from, QListWidget* to);
|
||||
|
||||
@@ -48,9 +52,11 @@ private:
|
||||
int m_currFilterType;
|
||||
QListWidget* m_focusedListView = nullptr;
|
||||
|
||||
// 用于文件显示列表中,存放每个文件名对应的绝对路径所用的数据键
|
||||
static constexpr int AbsPathRole = Qt::UserRole + 1;
|
||||
|
||||
QTimer* m_udiskCheckTimer = nullptr;
|
||||
bool m_hasUDisk = false;
|
||||
|
||||
void applyStyleSheet(); // 应用样式表
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
#define _GNU_SOURCE // 必须放在所有头文件之前
|
||||
#include <link.h> // dl_iterate_phdr 的声明
|
||||
#include <dlfcn.h> // dladdr 等
|
||||
#include <execinfo.h> // backtrace
|
||||
|
||||
#include <QApplication>
|
||||
#include <QVBoxLayout>
|
||||
#include <InputMethod.h>
|
||||
@@ -26,101 +21,9 @@
|
||||
#include "InkJetPrinter.h"
|
||||
#include "Log/filelogger.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <cxxabi.h>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <execinfo.h>
|
||||
#include "dump.h"
|
||||
|
||||
QString gAppNameStr = "";//这是个全局变量,用于后续获取当前运行程序的绝对路径
|
||||
|
||||
// 全局变量保存基址
|
||||
static uintptr_t g_base_address = 0;
|
||||
|
||||
// 回调函数:找到当前可执行文件的基址
|
||||
static int find_base_address(struct dl_phdr_info *info, size_t size, void *data) {
|
||||
// 检查是否是主程序(第一个加载的通常是主程序)
|
||||
if (info->dlpi_name == nullptr || info->dlpi_name[0] == '\0') {
|
||||
// 获取第一个段的加载地址(通常是基址)
|
||||
g_base_address = info->dlpi_addr;
|
||||
return 1; // 停止遍历
|
||||
}
|
||||
return 0; // 继续遍历
|
||||
}
|
||||
|
||||
// 获取程序基址的函数
|
||||
uintptr_t get_base_address() {
|
||||
if (g_base_address == 0) {
|
||||
dl_iterate_phdr(find_base_address, nullptr);
|
||||
}
|
||||
return g_base_address;
|
||||
}
|
||||
|
||||
void createStackTraceFile(const char *fileName, const char *appName)
|
||||
{
|
||||
std::ofstream file(fileName);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Error opening file: " << fileName << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
void *array[100];
|
||||
size_t size;
|
||||
char **strings;
|
||||
|
||||
size = backtrace(array, 100);
|
||||
if (size == 0) {
|
||||
std::cerr << "No stack frames obtained" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取程序基址(PIE需要)
|
||||
uintptr_t base_addr = get_base_address();
|
||||
|
||||
strings = backtrace_symbols(array, size);
|
||||
file << "Stack trace info:\n";
|
||||
file << "Base address: 0x" << std::hex << base_addr << std::dec << "\n\n";
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
file << strings[i] << std::endl;
|
||||
|
||||
// 计算偏移地址:实际地址 - 基址
|
||||
uintptr_t addr = reinterpret_cast<uintptr_t>(array[i]);
|
||||
uintptr_t offset = addr - base_addr;
|
||||
|
||||
char cmd[512];
|
||||
// 使用偏移地址而不是实际地址
|
||||
snprintf(cmd, sizeof(cmd), "addr2line -e %s 0x%lx", appName, offset);
|
||||
|
||||
FILE *fp = popen(cmd, "r");
|
||||
if (fp) {
|
||||
char line[256];
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
file << " " << line; // 缩进显示更清晰
|
||||
}
|
||||
pclose(fp);
|
||||
} else {
|
||||
std::cerr << "Failed to run addr2line command" << std::endl;
|
||||
}
|
||||
file << std::endl;
|
||||
}
|
||||
|
||||
free(strings);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void signalHandler(int signum)
|
||||
{
|
||||
// 直接指定固定的路径
|
||||
QString dumpFileName = "/home/plg/doc/dumpfile.txt";
|
||||
QString appName = gAppNameStr;
|
||||
createStackTraceFile(dumpFileName.toUtf8().data(), appName.toUtf8().data());
|
||||
exit(signum);
|
||||
}
|
||||
QString gAppNameStr = "";
|
||||
|
||||
//#define SYS_CN
|
||||
|
||||
@@ -175,10 +78,7 @@ int main(int argc, char *argv[])
|
||||
QApplication a(argc, argv);
|
||||
|
||||
gAppNameStr = QString::fromUtf8(argv[0]);
|
||||
signal(SIGSEGV, signalHandler);
|
||||
signal(SIGABRT, signalHandler);
|
||||
signal(SIGILL, signalHandler);
|
||||
signal(SIGFPE, signalHandler);
|
||||
installSignalHandlers();
|
||||
|
||||
// 配置日志
|
||||
QString configFileName = ":/conf/config.ini";
|
||||
@@ -224,7 +124,6 @@ int main(int argc, char *argv[])
|
||||
langurageFile= "./thai.qm";
|
||||
f.setPointSize(12);
|
||||
f.setPixelSize(12);
|
||||
|
||||
}
|
||||
else if(langurageType=="Vietnamese") {
|
||||
f.setFamily("Arial");//f.setFamily("Zfull-GB"); //f.setFamily("WenQuanYiMH");//Cambria
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#!/bin/bash
|
||||
|
||||
TOOLCHAIN_PREFIX="aarch64-linux-gnu-"
|
||||
ADDR2LINE="${TOOLCHAIN_PREFIX}addr2line"
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "错误: 请指定 dump 文件路径"
|
||||
echo "用法: $0 <dump文件路径> [QtUI所在目录]"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 ./dumpfile_2026_07_01_11_33_07.txt"
|
||||
echo " $0 ./dumpfile_2026_07_01_11_33_07.txt /path/to/QtUI_dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DUMP_FILE="$1"
|
||||
|
||||
if [ ! -f "$DUMP_FILE" ]; then
|
||||
echo "错误: 找不到 dump 文件: $DUMP_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DUMP_DIR="$(dirname "$DUMP_FILE")"
|
||||
DUMP_BASENAME="$(basename "$DUMP_FILE")"
|
||||
DUMP_NAME="${DUMP_BASENAME%.*}"
|
||||
DUMP_EXT="${DUMP_BASENAME##*.}"
|
||||
RESOLVED_FILE="${DUMP_DIR}/${DUMP_NAME}_resolved.${DUMP_EXT}"
|
||||
|
||||
if [ -n "$2" ]; then
|
||||
QTUI_DIR="$2"
|
||||
else
|
||||
QTUI_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
fi
|
||||
|
||||
QTUI_PATH="${QTUI_DIR}/QtUI"
|
||||
QTUI_DBG="${QTUI_DIR}/QtUI.dbg"
|
||||
|
||||
if [ ! -f "$QTUI_PATH" ]; then
|
||||
echo "错误: 找不到 QtUI 文件: $QTUI_PATH"
|
||||
echo "请确保 QtUI 和 QtUI.dbg 在同一目录下"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$QTUI_DBG" ]; then
|
||||
echo "警告: 找不到 QtUI.dbg 文件: $QTUI_DBG"
|
||||
echo "addr2line 将仅使用 QtUI 中的调试信息(如果有)"
|
||||
fi
|
||||
|
||||
if ! command -v "$ADDR2LINE" &> /dev/null; then
|
||||
echo "错误: 找不到 $ADDR2LINE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BASE_ADDR=$(grep -oP 'Base address:\s*\K0x[0-9a-fA-F]+' "$DUMP_FILE")
|
||||
|
||||
if [ -z "$BASE_ADDR" ]; then
|
||||
echo "错误: 无法从 dump 文件中解析 Base address"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Base address: $BASE_ADDR"
|
||||
echo "QtUI 路径: $QTUI_PATH"
|
||||
echo "QtUI.dbg: $QTUI_DBG"
|
||||
echo "输出文件: $RESOLVED_FILE"
|
||||
echo ""
|
||||
|
||||
echo "正在解析 dump 文件: $DUMP_FILE"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
ADDR_LINE_REGEX='^(.+/QtUI)\(\+0x([0-9a-fA-F]+)\)\s+\[(0x[0-9a-fA-F]+)\]'
|
||||
|
||||
{
|
||||
while IFS= read -r line; do
|
||||
if [[ "$line" =~ $ADDR_LINE_REGEX ]]; then
|
||||
OFFSET="0x${BASH_REMATCH[2]}"
|
||||
RUNTIME_ADDR="${BASH_REMATCH[3]}"
|
||||
|
||||
read -r source_line
|
||||
|
||||
RESOLVED=$($ADDR2LINE -e "$QTUI_PATH" -f -C -p "$OFFSET" 2>/dev/null)
|
||||
|
||||
echo "$line"
|
||||
if [ -n "$RESOLVED" ] && [ "$RESOLVED" != "?? ??:0" ] && [ "$RESOLVED" != "?? ??:?" ]; then
|
||||
echo " $RESOLVED"
|
||||
else
|
||||
echo " $source_line"
|
||||
fi
|
||||
echo ""
|
||||
elif [[ "$line" =~ ^Base\ address: ]]; then
|
||||
echo "$line"
|
||||
echo ""
|
||||
elif [[ "$line" =~ ^Stack\ trace\ info: ]]; then
|
||||
echo "$line"
|
||||
echo ""
|
||||
else
|
||||
echo "$line"
|
||||
fi
|
||||
done < "$DUMP_FILE"
|
||||
|
||||
echo "========================================="
|
||||
echo "解析完成"
|
||||
} | tee "$RESOLVED_FILE"
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
TOOLCHAIN="aarch64-linux-gnu-objcopy"
|
||||
|
||||
if [ -n "$1" ]; then
|
||||
QTUI_PATH="$1"
|
||||
else
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
QTUI_PATH="${SCRIPT_DIR}/QtUI"
|
||||
fi
|
||||
|
||||
if [ ! -f "$QTUI_PATH" ]; then
|
||||
echo "错误: 找不到 QtUI 文件: $QTUI_PATH"
|
||||
echo "用法: $0 [QtUI路径]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v "$TOOLCHAIN" &> /dev/null; then
|
||||
echo "错误: 找不到工具链 $TOOLCHAIN"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
QTUI_DIR="$(dirname "$QTUI_PATH")"
|
||||
QTUI_NAME="$(basename "$QTUI_PATH")"
|
||||
QTUI_DBG="${QTUI_DIR}/${QTUI_NAME}.dbg"
|
||||
|
||||
echo "目标文件: $QTUI_PATH"
|
||||
echo ""
|
||||
|
||||
echo "步骤 1/3: 分离调试符号..."
|
||||
$TOOLCHAIN --only-keep-debug "$QTUI_PATH" "$QTUI_DBG"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "错误: 分离调试符号失败"
|
||||
exit 1
|
||||
fi
|
||||
echo "完成: $QTUI_DBG"
|
||||
|
||||
echo "步骤 2/3: 移除调试符号..."
|
||||
$TOOLCHAIN --strip-debug "$QTUI_PATH" "$QTUI_PATH"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "错误: 移除调试符号失败"
|
||||
exit 1
|
||||
fi
|
||||
echo "完成"
|
||||
|
||||
echo "步骤 3/3: 关联调试符号文件..."
|
||||
$TOOLCHAIN --add-gnu-debuglink="$QTUI_DBG" "$QTUI_PATH"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "错误: 关联调试符号文件失败"
|
||||
exit 1
|
||||
fi
|
||||
echo "完成"
|
||||
|
||||
echo ""
|
||||
echo "========== 结果 =========="
|
||||
ls -lh "$QTUI_PATH" "$QTUI_DBG"
|
||||
echo ""
|
||||
echo "操作完成:"
|
||||
echo " 发行程序: $QTUI_PATH"
|
||||
echo " 调试符号: $QTUI_DBG"
|
||||
echo " 注意: 两个文件已关联,必须成对存放"
|
||||
+40
-13
@@ -44,8 +44,11 @@ TestPrintWidget::TestPrintWidget(QWidget* parent)
|
||||
// m_ButtonGroup->addButton(ui.btnList, 7);
|
||||
|
||||
dialogSaveFile =new DialogSaveFile(0);
|
||||
connect(dialogSaveFile,SIGNAL(returnMainWidget(int)),this,SIGNAL(sendButtonClickedSignal(int)));
|
||||
|
||||
dialogFileOperation = new DialogFileOperation(0);
|
||||
connect(dialogFileOperation,SIGNAL(openFile(QString,bool)),this,SLOT(onFileOpen(QString,bool)));
|
||||
connect(dialogFileOperation,SIGNAL(createSceneImage(QString)),this,SLOT(onCreateSceneImage(QString)));
|
||||
|
||||
m_CreateImageFlag = false;
|
||||
m_pSavePicScene = new QGraphicsScene();
|
||||
@@ -81,11 +84,6 @@ TestPrintWidget::TestPrintWidget(QWidget* parent)
|
||||
getShowSelectItemText();
|
||||
});
|
||||
|
||||
connect(dialogSaveFile,SIGNAL(returnMainWidget(int)),this,SIGNAL(sendButtonClickedSignal(int)));
|
||||
|
||||
connect(dialogFileOperation,SIGNAL(openFile(QString,bool)),this,SLOT(onFileOpen(QString,bool)));
|
||||
connect(dialogFileOperation,SIGNAL(createSceneImage(QString)),this,SLOT(onCreateSceneImage(QString)));
|
||||
|
||||
// ========== 连接所有信号槽 ==========
|
||||
// 左侧工具栏按钮组
|
||||
connect(m_ButtonGroup, QOverload<QAbstractButton*>::of(&QButtonGroup::buttonClicked),
|
||||
@@ -214,12 +212,18 @@ TestPrintWidget::~TestPrintWidget()
|
||||
if(m_pSavePicScene)
|
||||
{
|
||||
delete m_pSavePicScene;
|
||||
m_pSavePicScene = nullptr;
|
||||
}
|
||||
if(m_TextEdit){
|
||||
delete m_TextEdit;
|
||||
m_TextEdit = nullptr;
|
||||
}
|
||||
|
||||
if (imageLoader) {
|
||||
delete imageLoader;
|
||||
imageLoader = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QPixmap TestPrintWidget::loadIcon(const QString& iconName, const QSize& size)
|
||||
@@ -233,9 +237,11 @@ void TestPrintWidget::on_openfileButton_pressed()
|
||||
{
|
||||
pPicScene->clearSelection();
|
||||
pPicScene->clearFocus();
|
||||
if (dialogFileOperation) {
|
||||
dialogFileOperation->refreshListView(editMode);
|
||||
dialogFileOperation->exec();
|
||||
}
|
||||
}
|
||||
|
||||
void TestPrintWidget::addDateItem()
|
||||
{
|
||||
@@ -261,7 +267,9 @@ void TestPrintWidget::on_enlargeButton_pressed()
|
||||
// pViewer->scale(scaleFactor, scaleFactor);
|
||||
|
||||
// TODO:: 保留,以后真的要用到放大缩小
|
||||
((MyView*)pViewer)->ZoomOut();
|
||||
// ((MyView*)pViewer)->ZoomOut();
|
||||
auto myView = dynamic_cast<MyView*>(pViewer);
|
||||
if (myView) myView->ZoomOut();
|
||||
}
|
||||
|
||||
void TestPrintWidget::on_addButton_pressed(const SourceType type)
|
||||
@@ -294,7 +302,9 @@ void TestPrintWidget::onEditItemDone()
|
||||
const QString content = m_LineEdit->text();
|
||||
foreach (QGraphicsItem *item, pPicScene->selectedItems())
|
||||
{
|
||||
PicTextItem *pTextItem = (PicTextItem *)item;
|
||||
PicTextItem* pTextItem = dynamic_cast<PicTextItem*>(item);
|
||||
if (pTextItem == nullptr) continue;
|
||||
|
||||
if(pTextItem->sourceType() == SourceType::TEXT)
|
||||
pTextItem->setSource(content);
|
||||
}
|
||||
@@ -302,8 +312,6 @@ void TestPrintWidget::onEditItemDone()
|
||||
|
||||
void TestPrintWidget::getShowSelectItemText()
|
||||
{
|
||||
//Scene Test
|
||||
|
||||
ui.m_LineEdit->setEnabled(false);
|
||||
ui.m_LineEdit->setText("");
|
||||
|
||||
@@ -314,6 +322,8 @@ void TestPrintWidget::getShowSelectItemText()
|
||||
PicTextItem* lastSelectedTxtItem = nullptr;
|
||||
for(auto item : selectedItems) {
|
||||
PicTextItem* txtItem = dynamic_cast<PicTextItem*>(item);
|
||||
if (txtItem == nullptr) continue;
|
||||
|
||||
if(txtItem->sourceType() != SourceType::IMAGE && txtItem->sourceType() != SourceType::CODE && txtItem->sourceType() != SourceType::UNKNOWN) {
|
||||
lastSelectedTxtItem = txtItem;
|
||||
}
|
||||
@@ -401,6 +411,8 @@ void TestPrintWidget::saveFile(){
|
||||
int index=0;
|
||||
foreach (QGraphicsItem *item, items) {
|
||||
PicTextItem *picText = qgraphicsitem_cast<PicTextItem*>(item);
|
||||
if (picText == nullptr) continue;
|
||||
|
||||
writer.writeStartElement("VBfile"); // 开始子元素 <picscene>
|
||||
writer.writeTextElement(QString::fromLocal8Bit("pointer_x"), QString::fromLocal8Bit("%1").arg(picText->pos().x()));
|
||||
writer.writeTextElement(QString::fromLocal8Bit("pointer_y"), QString::fromLocal8Bit("%1").arg(picText->pos().y()));
|
||||
@@ -431,9 +443,12 @@ void TestPrintWidget::saveFile(){
|
||||
writer.writeEndElement(); // 结束子元素 </picscene>
|
||||
}
|
||||
|
||||
|
||||
if (m_printSetting) {
|
||||
writer.writeEmptyElement("PrintSettings");
|
||||
if(m_printSetting)
|
||||
PrintSettingModel::saveToXml(m_printSetting->model(), writer);
|
||||
}
|
||||
|
||||
writer.writeEndElement(); // 结束根元素 </picscenes>
|
||||
writer.writeEndDocument(); // 结束文档
|
||||
@@ -468,14 +483,17 @@ void TestPrintWidget::onFileOpen(QString filePath,bool reviewFlag){
|
||||
QFile file(filePath);
|
||||
|
||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { // 以只写模式打开文件
|
||||
xmlfileName=filePath;
|
||||
|
||||
if(reviewFlag)
|
||||
xmlfileName=filePath;
|
||||
|
||||
QXmlStreamReader reader(&file);
|
||||
if(!m_CreateImageFlag)
|
||||
{
|
||||
pPicScene->clear();
|
||||
pViewer->viewport()->update();
|
||||
unsigned char SpecialVarEnable = 0;
|
||||
sysPara->devOp->fpgaWr(0xa4b0,(unsigned char *)&SpecialVarEnable,1);
|
||||
if (sysPara) sysPara->devOp->fpgaWr(0xa4b0,(unsigned char *)&SpecialVarEnable,1);
|
||||
}
|
||||
// 解析 XML,直到结束
|
||||
while (!reader.atEnd()) {
|
||||
@@ -683,7 +701,7 @@ void TestPrintWidget::parseVbfile(QXmlStreamReader &reader, bool loadPrintSettin
|
||||
// Condition= false ;
|
||||
// CatchValue = -1;
|
||||
//LogInfo::setRunStatusText(tr("接收文件解析::2"));
|
||||
if(!sysPara->status.forTemplateUse || m_CreateImageFlag)
|
||||
if(sysPara!=nullptr && !sysPara->status.forTemplateUse || m_CreateImageFlag)
|
||||
{
|
||||
pPictText->setHtml(strCDATA);
|
||||
}
|
||||
@@ -967,6 +985,7 @@ void TestPrintWidget::on_delButton_pressed()
|
||||
foreach (QGraphicsItem *item, pPicScene->selectedItems()) {
|
||||
pPicScene->removeItem(item);
|
||||
delete item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1040,9 +1059,12 @@ void TestPrintWidget::onBtnItemConfigClicked()
|
||||
return;
|
||||
}
|
||||
|
||||
auto item = dynamic_cast<PicTextItem*>(selectedItems[0]);
|
||||
PicTextItem* item = dynamic_cast<PicTextItem*>(selectedItems[0]);
|
||||
if (item == nullptr) return;
|
||||
|
||||
if(item->sourceType() == SourceType::COUNTER) {
|
||||
auto counterItem = dynamic_cast<PicCounterItem*>(item);
|
||||
if (counterItem) {
|
||||
CounterViewModel vm;
|
||||
vm.counterType = counterItem->counterType();
|
||||
vm.digitalNum = counterItem->digitalNum();
|
||||
@@ -1050,6 +1072,7 @@ void TestPrintWidget::onBtnItemConfigClicked()
|
||||
vm.enbCustomCharWidth = false;
|
||||
vm.charWidth = counterItem->charWidth();
|
||||
emit sig_showEditCounter(vm);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1067,12 +1090,14 @@ void TestPrintWidget::onBtnItemConfigClicked()
|
||||
if(item->sourceType() == SourceType::DATETIME) {
|
||||
// @see TestPrintWidget::addDateTimeItem
|
||||
auto dateTimeItem = dynamic_cast<PicDateItem*>(item);
|
||||
if (dateTimeItem)
|
||||
emit sig_showEditDateTime(dateTimeItem->model(), dateTimeItem->textItemModel());
|
||||
return;
|
||||
}
|
||||
|
||||
if(item->sourceType() == SourceType::SCHEDULE_SHIFT) {
|
||||
auto scheduleItem = dynamic_cast<PicScheduleShiftItem*>(item);
|
||||
if (scheduleItem)
|
||||
emit sig_showScheduleShiftsEditor(scheduleItem->schedules(), scheduleItem->textItemModel());
|
||||
return;
|
||||
}
|
||||
@@ -1097,6 +1122,8 @@ void TestPrintWidget::onBtnItemConfigClicked()
|
||||
|
||||
void TestPrintWidget::onItemEditorAccepted()
|
||||
{
|
||||
if(!itemEditor || !itemEditor->currentItem()) return;
|
||||
|
||||
if(itemEditor->currentItem()->source() == SourceType::UNKNOWN) {
|
||||
// Nothing....
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user