#include "UpdateFPGA.h" #include "myinclude.h" #include "Public.h" #include #include #include #include #include #pragma execution_character_set("utf-8") UpdateFPGA::UpdateFPGA(const QString& pythonScriptPath, QWidget* parent) : QDialog(parent), m_PythonScriptPath(pythonScriptPath) { this->setWindowFlags(this->windowFlags() | Qt::FramelessWindowHint); this->setObjectName("UpdateFPGA"); this->setStyleSheet("QDialog#UpdateFPGA{background-color:#FFFFFF;border:0px solid #D6D6D6;}"); this->setWindowIcon(QPixmap(":/UpdateFPGA/logo.png")); } UpdateFPGA::~UpdateFPGA() { if (!conversionProcess) return; if (conversionProcess->state() == QProcess::Running) { conversionProcess->terminate(); conversionProcess->waitForFinished(); conversionProcess->deleteLater(); conversionProcess = nullptr; } } void UpdateFPGA::init() { QFile file(SYS_ROOT_DIR"UseEn"); showChinese = true; if(file.exists()) { QString langurageType = Public::ReadLanguageName(); if(langurageType != "Chinese") showChinese = false; } if(showChinese) setWindowTitle("升级工具"); else setWindowTitle("Upgrade Tool"); setMinimumSize(600, 400); QLabel* Logo = new QLabel(this); Logo->setFixedSize(67, 25); Logo->setPixmap(QPixmap(":/UpdateFPGA/logo.png")); Logo->setScaledContents(true); m_Title = new QLabel(this); m_Title->setStyleSheet("font-family:\"Microsoft YaHei\";color:#333333;font-size:16px;"); m_Title->setFixedHeight(25); if(showChinese) m_Title->setText("升级工具"); else m_Title->setText("Upgrade Tool"); m_CloseBtn = new QPushButton(this); m_CloseBtn->setFixedSize(25, 25); m_CloseBtn->setIconSize(QSize(20, 20)); m_CloseBtn->setIcon(QPixmap(":/icon/keydelete.png")); m_CloseBtn->setCursor(Qt::PointingHandCursor); m_CloseBtn->setStyleSheet("QPushButton{background-color:#ffffff;border-radius:3px;border:none;}\ QPushButton::hover{background-color:#EEEEEE;}"); connect(m_CloseBtn,&QPushButton::clicked,this,&UpdateFPGA::closeUI); QHBoxLayout* hlayout = new QHBoxLayout; hlayout->addSpacing(5); hlayout->addWidget(Logo); hlayout->addWidget(m_Title); hlayout->addStretch(); hlayout->addWidget(m_CloseBtn); hlayout->addSpacing(10); QVBoxLayout *mainLayout = new QVBoxLayout(this); if(showChinese) m_UpdateButton = new QPushButton("开始升级", this); else m_UpdateButton = new QPushButton("Upgrade Start", this); m_UpdateButton->setFixedHeight(30); if(showChinese) logLabel = new QLabel("升级日志:", this); else logLabel = new QLabel("LogInfo:", this); logTextEdit = new QTextEdit(this); logTextEdit->setReadOnly(true); mainLayout->addLayout(hlayout); mainLayout->addWidget(m_UpdateButton); mainLayout->addWidget(logLabel); mainLayout->addWidget(logTextEdit, 1); conversionProcess = new QProcess(this); connect(m_UpdateButton, &QPushButton::clicked, this, &UpdateFPGA::startUpdate); connect(conversionProcess, QOverload::of(&QProcess::finished), this, &UpdateFPGA::processFinished); connect(conversionProcess, &QProcess::errorOccurred, this, &UpdateFPGA::processError); connect(conversionProcess, &QProcess::readyReadStandardOutput, [this]() { logTextEdit->append(conversionProcess->readAllStandardOutput()); } ); connect(conversionProcess, &QProcess::readyReadStandardError, [this]() { logTextEdit->append(conversionProcess->readAllStandardError()); } ); } void UpdateFPGA::closeUI(){ close(); emit upgradeFPGAFinished(true); } void UpdateFPGA::startUpdate() { m_CloseBtn->setEnabled(false); if (m_PythonScriptPath.isEmpty()) { logTextEdit->append("错误: 脚本为空"); return; } QFile inputFile(m_PythonScriptPath); if (!inputFile.exists()) { logTextEdit->append(QString("错误: 脚本不存在 - %1").arg(m_PythonScriptPath)); return; } logTextEdit->append("开始升级..."); logTextEdit->append(QString("文件:%1").arg(m_PythonScriptPath)); // 自动查找Ubuntu 22.04中可用的Python解释器 QString pythonPath = findValidPythonInterpreter(); if (pythonPath.isEmpty()) { logTextEdit->append("错误: 未找到可用的Python解释器或缺少serial模块"); m_CloseBtn->setEnabled(true); return; } logTextEdit->append(QString("使用Python解释器: %1").arg(pythonPath)); QStringList pythonArgs; pythonArgs << m_PythonScriptPath; conversionProcess->start(pythonPath, pythonArgs); m_UpdateButton->setEnabled(false); // 运行中禁用按钮 } // 查找并验证Ubuntu系统中有效的Python解释器 QString UpdateFPGA::findValidPythonInterpreter() { QStringList pythonNames = { "python3", "python3.10", "python3.11", "python3.9" }; QStringList searchPaths; QString home = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); searchPaths << home + "/.local/bin" << home + "/.pyenv/shims" << home + "/.virtualenvs/*/bin" << home + "/venv/bin" << home + "/env/bin"; searchPaths << "/usr/bin" << "/usr/local/bin" << "/bin" << "/opt/bin" << "/snap/bin"; QByteArray pathEnvBytes = qgetenv("PATH"); QString pathEnvStr = QString::fromLocal8Bit(pathEnvBytes); QStringList pathEnv = pathEnvStr.split(QDir::listSeparator()); foreach(const QString & path, pathEnv) { if (!searchPaths.contains(path)) { searchPaths << path; } } // 遍历所有可能的组合查找有效解释器 foreach(const QString & path, searchPaths) { foreach(const QString & name, pythonNames) { if (path.contains("*")) { QDir dir(path.left(path.indexOf("*"))); if (dir.exists()) { QStringList subDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); foreach(const QString & subDir, subDirs) { QString fullPath = dir.filePath(subDir + "/bin/" + name); if (QFile::exists(fullPath) && isValidPythonWithSerial(fullPath)) { return fullPath; } } } } else { QString fullPath = QDir(path).filePath(name); if (QFile::exists(fullPath) && isValidPythonWithSerial(fullPath)) { return fullPath; } } } } return ""; } // 验证Python解释器是否包含serial模块 bool UpdateFPGA::isValidPythonWithSerial(const QString& pythonPath) { // 检查Python是否可运行 QProcess testProcess; testProcess.setProcessChannelMode(QProcess::MergedChannels); // 合并输出通道 testProcess.start(pythonPath, { "-c", "import sys; sys.exit(0)" }); if (!testProcess.waitForFinished(3000) || testProcess.exitCode() != 0) { logTextEdit->append(QString("调试: 无效的Python解释器 - %1").arg(pythonPath)); return false; } // 检查是否安装了serial模块 testProcess.start(pythonPath, { "-c", "import serial; print('serial found: ' + serial.__version__)" }); if (testProcess.waitForFinished(3000)) { if (testProcess.exitCode() == 0) { QString output = testProcess.readAll().trimmed(); logTextEdit->append(QString("调试: %1").arg(output)); return true; } else { logTextEdit->append(QString("调试: %1 缺少serial模块").arg(pythonPath)); } } else { logTextEdit->append(QString("调试: %1 响应超时").arg(pythonPath)); } return false; } // 根据错误码解析具体错误信息 QString UpdateFPGA::getErrorDescription(int errorCode) { switch (errorCode) { case 1: return "文件不存在(请检查FPGA固件文件路径)"; case 2: return "无可用串口(请检查设备连接)"; case 3: return "串口通信错误(可能是波特率不匹配或设备断开)"; case 4: return "数据发送失败(尝试重新发送)"; case 5: return "数据校验失败(数据传输过程中损坏)"; case 6: return "接收数据不完整(可能是传输中断)"; case 7: return "文件大小发送失败(协议握手错误)"; case 8: return "文件内容发送失败(大文件传输超时)"; case 9: return "未知错误(请查看详细日志)"; default: return "未定义错误"; } } void UpdateFPGA::processFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitStatus == QProcess::NormalExit) { if (exitCode == 0) // 成功(错误码0) { logTextEdit->append("升级成功!"); if(showChinese) QMessageBox::information(this, "成功", "FPGA升级完成,请重启QtUI", QMessageBox::Ok); else QMessageBox::information(this, "Succeed", "FPGA upgrade has been completed. Please restart QtUI.", QMessageBox::Ok); close(); // 成功后关闭界面 emit upgradeFPGAFinished(true); } else // 有错误码的失败 { QString errorDesc = getErrorDescription(exitCode); logTextEdit->append(QString("升级失败,错误码: %1,原因: %2") .arg(exitCode).arg(errorDesc)); // 询问用户是否重试(针对可恢复错误) QMessageBox::StandardButton reply; if(showChinese){ reply = QMessageBox::question(this, "升级失败", QString("错误码%1: %2\n是否重新尝试升级?") .arg(exitCode).arg(errorDesc), QMessageBox::Yes | QMessageBox::No); }else{ reply = QMessageBox::question(this, "Upgrade failed", QString("Error Code%1: %2\nWould you like to try upgrading again?") .arg(exitCode).arg(errorDesc), QMessageBox::Yes | QMessageBox::No); } if (reply == QMessageBox::Yes) { startUpdate(); // 重新运行 } else { close(); emit upgradeFPGAFinished(true); m_UpdateButton->setEnabled(true); // 允许手动再次尝试 m_CloseBtn->setEnabled(true); } } } else // 进程异常退出(无错误码) { logTextEdit->append(QString("升级进程异常退出,退出码: %1").arg(exitCode)); QMessageBox::StandardButton reply; if(showChinese){ reply = QMessageBox::question(this, "进程异常", "升级过程中程序崩溃,是否重新尝试?", QMessageBox::Yes | QMessageBox::No); }else{ reply = QMessageBox::question(this, "Process exception", "The program crashed during the upgrade process. Would you like to try again?", QMessageBox::Yes | QMessageBox::No); } if (reply == QMessageBox::Yes) { startUpdate(); } else { close(); emit upgradeFPGAFinished(true); m_UpdateButton->setEnabled(true); m_CloseBtn->setEnabled(true); } } } void UpdateFPGA::processError(QProcess::ProcessError error) { QString errorMsg; switch (error) { case QProcess::FailedToStart: errorMsg = "错误: 无法启动脚本(可能是Python环境未配置)"; break; case QProcess::Crashed: errorMsg = "错误: 脚本进程崩溃"; break; case QProcess::Timedout: errorMsg = "错误: 启动超时(设备无响应)"; break; case QProcess::WriteError: errorMsg = "错误: 写入进程失败(权限不足)"; break; case QProcess::ReadError: errorMsg = "错误: 读取进程输出失败"; break; case QProcess::UnknownError: default: errorMsg = "错误: 未知错误"; break; } logTextEdit->append(errorMsg); logTextEdit->append("错误详情: " + conversionProcess->readAllStandardError()); // 进程错误时也提示重试 QMessageBox::StandardButton reply; if(showChinese) reply = QMessageBox::question(this, "操作错误", errorMsg + "\n是否重新尝试?", QMessageBox::Yes | QMessageBox::No); else reply = QMessageBox::question(this, "Operational error", errorMsg + "\nWould you like to try again?", QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { startUpdate(); } else { close(); emit upgradeFPGAFinished(true); m_UpdateButton->setEnabled(true); m_CloseBtn->setEnabled(true); } }