Qt u盘自动升级软件

Qt u盘自动升级软件

  • Chapter1 Qt u盘自动升级软件
    • u盘自动升级软件思路:
    • step1. 获取U盘 判断U盘名字是否正确, 升级文件是否存在。
    • step2. 升级
    • step3. 升级界面
  • Chapter2 Qt 嵌入式设备应用程序,通过U盘升级的一种思路
  • Chapter3 在开发板上运行的QT应用程序,如何拷贝文件到U盘
  • Chapter4 嵌入式Qt,U盘升级程序
    • 前言
    • 一、实现过程
      • 1.检测U盘中的更新程序
      • 2. 数字验证
      • 3.升级程序
      • 4.主函数
    • 二、总结


Chapter1 Qt u盘自动升级软件

原文链接

u盘自动升级软件思路:

1.检测U盘是否存在

2.检测升级文件是否存在,升级文件版本是否比当前软件版本新

3.软件升级

step1. 获取U盘 判断U盘名字是否正确, 升级文件是否存在。

//获取U盘  判断U盘名字是否正确, 升级文件是否存在。
void MainWindow::getUDisk()
{
    QMap<QString,QString> namePath;
    foreach (const QStorageInfo &storage, QStorageInfo::mountedVolumes())
    {
        if (storage.isValid() && storage.isReady())
        {
            UDiskPath = storage.rootPath();
            namePath.insert(storage.displayName(),storage.rootPath());
        }
    }
    QString path = namePath.value("LADYBUG");
    QFile file(path+"/"+app::updateFile);
    if(file.exists())
    {
        //存在 弹窗是否升级
    }
    else
    {
        //不存在
    }
}

step2. 升级

    OperateThread *thread = new OperateThread;
    thread->setUpgradeFile(strSrcFile);
    ShowProgressDialog dialog(this);
 
    connect(thread, SIGNAL(emitFinish(int)), &dialog, SLOT(onThreadFinished(int)));
 
    thread->start();
 
    dialog.exec();

step3. 升级界面

class ShowProgressDialog : public QDialog
{
    Q_OBJECT
public:
    explicit ShowProgressDialog(QWidget *parent = 0);
    ~ShowProgressDialog();
 
    void setTitleText(const QString &strText);
    void setMessageText(const QString &strMsg);
 
public slots:
    void on_btnOk_clicked();
    void onThreadFinished(int nExitCode);
 
private:
    QLabel          *m_labelTitle;
    QLabel          *m_labelMsg;
    QPushButton     *m_btnOk;
    QLabel          *m_labelWait;
    QMovie          *m_pMovie;
};
 
ShowProgressDialog::ShowProgressDialog(QWidget *parent) : QDialog(parent)
{
    setWindowFlags(Qt::CustomizeWindowHint | Qt::FramelessWindowHint | Qt::Dialog);
    setAttribute(Qt::WA_X11DoNotAcceptFocus);
    setFocusPolicy(Qt::NoFocus);
    //更改背景色
    QPalette palette = this->palette();
    QPixmap pix(":/res/dialog_bg.png");
    palette.setBrush(QPalette::Background,QBrush(pix));
    setAutoFillBackground(true);
    this->setPalette(palette);
 
    resize(410, 260);
    setMinimumSize(QSize(410, 260));
    setMaximumSize(QSize(410, 260));
 
    m_labelTitle = new QLabel(this);
    m_labelMsg = new QLabel(this);
    m_btnOk = new QPushButton(this);
    m_labelWait = new QLabel(this);
    m_pMovie = new QMovie(":/res/loading.gif");
 
    m_labelTitle->setGeometry(QRect(10, 1, 220, 30));
    m_labelMsg->setGeometry(QRect(30, 60, 350, 60));
    m_labelMsg->setWordWrap(true);
    m_btnOk->setGeometry(QRect(320, 180, 58, 58));
    m_labelWait->setGeometry(QRect(30, 120, 322, 18));
 
    m_labelWait->setMovie(m_pMovie);
    m_pMovie->start();
 
    m_labelTitle->setStyleSheet("font: bold 17px; color: white;");
    m_labelMsg->setStyleSheet("font: bold 17px ; color: black; ");
 
    m_btnOk->setStyleSheet("QPushButton{background-image: url(:/res/dialog_ok.png);border: 0px;}"
                           "QPushButton:pressed{background-image: url(:/res/dialog_ok_p.png);border: 0px;}");
 
    connect(m_btnOk , SIGNAL(clicked()) , this , SLOT(on_btnOk_clicked()));
 
    setTitleText("升级");
    setMessageText("正在升级,请勿插拔U盘!");
    m_btnOk->hide();
}
 
ShowProgressDialog::~ShowProgressDialog()
{
    delete m_labelTitle;
    delete m_labelMsg;
    delete m_btnOk;
}
 
void ShowProgressDialog::setTitleText(const QString &strText)
{
    m_labelTitle->setText(strText);
}
 
void ShowProgressDialog::setMessageText(const QString &strMsg)
{
    m_labelMsg->setText(strMsg);
}
 
void ShowProgressDialog::on_btnOk_clicked()
{
    QDialog::accept();
}
 
void ShowProgressDialog::onThreadFinished(int nExitCode)
{
    qDebug() << "nExitCode" << nExitCode;
    if (nExitCode == 0)
    {
        setMessageText("升级成功,请重新上电。");
        m_btnOk->show();
        m_pMovie->stop();
    }
    else
    {
        setMessageText("升级出错!请断电以恢复!");
        m_btnOk->show();
        m_pMovie->stop();
    }
}

注意:线程,继承自QThread,完成复制工作。使用QProcess来完成Linux下的解压和删除的工作。关于Qt的多线程,可以去查找一下其他的教程,这里就不做过多的解释。

class OperateThread : public QThread
{
    Q_OBJECT
public:
    explicit OperateThread(QObject *parent = 0);
 
public:
    void setUpgradeFile(QString strUpgradeFile)
    {
        m_UpgradeFile = strUpgradeFile;
    }
 
protected:
    void run();
 
signals:
    void emitFinish(int);
 
public slots:
 
private:
    void OprUpgradeUp();
 
private:
    QString  m_UpgradeFile;
};
 
OperateThread::OperateThread(QObject *parent) : QThread(parent)
{
 
}
 
void OperateThread::run()
{
    //很复杂的数据处理
    OprUpgradeUp();
}
 
void OperateThread::OprUpgradeUp()
{
#define DEST_DIR    "../"
 
    QString destDir = QString("%1/update.tar.gz").arg(DEST_DIR);
 
    if( QFile::copy(m_UpgradeFile, destDir) )
    {
        qDebug()<<"-------成功-----------";
    }
    else
    {
        qDebug()<<"-------出错-----------";
        //升级失败
        emit emitFinish(1);
        return;
    }
 
#ifdef Q_OS_LINUX
    QString exe = QString("tar -zxvf %1 -C %2").arg(destDir).arg(DEST_DIR);
    QProcess::execute(exe);
 
    //升级成功,自动同步磁盘并删除ARM上的升级包
    exe = QString("sync");
    QProcess::execute(exe);
    exe = QString("rm -rf %1").arg(destDir);
    QProcess::execute(exe);
#endif
    //升级成功
    emit emitFinish(0);
}

Chapter2 Qt 嵌入式设备应用程序,通过U盘升级的一种思路

原文链接:https://blog.csdn.net/qq1113231395/article/details/81867153

最近在做一个通过U盘升级的功能,程序是运行在ARM Linux Qt平台上的。这个应该是很多嵌入式设备必备的一个功能了,所以把这部分的实现抽出来,做成一个例子供需要的人参考。这只是U盘升级的一种思路,如果有更好的方法,也可以提供相应的意见。

源码下载:softwareupgrade.tar.gz

升级文件的格式是通过tar压缩后的文件以gz结尾的, 可以通过tar命令生成相应的升级文件如update.tar.gz:

tar -czvf update.tar.gz demo

主要思路就是,点击相应的功能后从U盘中选中需要升级的文件update.tar.gz,之后开启一个线程和一个提示正在升级的对话框,以免让用户觉得假死。在线程中完成的事情是:将选中的升级文件复制到应用程序的目录中,然后将update.tar.gz 解压覆盖原来的程序。最后将update.tar.gz删除。发送一个线程结束的信号。

下面就介绍一下实现过程。

MainWindow很简单,只有一个按钮。 点击按钮开启线程,显示提示框,绑定线程结束后的信号。提示框根据信号的参数值来确定升级是否成功。

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}
 
MainWindow::~MainWindow()
{
    delete ui;
}
 
void MainWindow::on_pushButton_clicked()
{
    QString strSrcFile = QFileDialog::getOpenFileName(this, "选择升级文件", ".", "tar (*.gz)");
    qDebug() << "strSrcFile" << strSrcFile;
 
    OperateThread *thread = new OperateThread;
    thread->setUpgradeFile(strSrcFile);
    ShowProgressDialog dialog(this);
 
    connect(thread, SIGNAL(emitFinish(int)), &dialog, SLOT(onThreadFinished(int)));
 
    thread->start();
 
    dialog.exec();
}

运行的效果图:
在这里插入图片描述

提示框: void onThreadFinished(int nExitCode) 线程结束后更新提示框。里面还使用到了一些qss,来设置背景。

class ShowProgressDialog : public QDialog
{
    Q_OBJECT
public:
    explicit ShowProgressDialog(QWidget *parent = 0);
    ~ShowProgressDialog();
 
    void setTitleText(const QString &strText);
    void setMessageText(const QString &strMsg);
 
public slots:
    void on_btnOk_clicked();
    void onThreadFinished(int nExitCode);
 
private:
    QLabel          *m_labelTitle;
    QLabel          *m_labelMsg;
    QPushButton     *m_btnOk;
    QLabel          *m_labelWait;
    QMovie          *m_pMovie;
};
 
ShowProgressDialog::ShowProgressDialog(QWidget *parent) : QDialog(parent)
{
    setWindowFlags(Qt::CustomizeWindowHint | Qt::FramelessWindowHint | Qt::Dialog);
    setAttribute(Qt::WA_X11DoNotAcceptFocus);
    setFocusPolicy(Qt::NoFocus);
    //更改背景色
    QPalette palette = this->palette();
    QPixmap pix(":/res/dialog_bg.png");
    palette.setBrush(QPalette::Background,QBrush(pix));
    setAutoFillBackground(true);
    this->setPalette(palette);
 
    resize(410, 260);
    setMinimumSize(QSize(410, 260));
    setMaximumSize(QSize(410, 260));
 
    m_labelTitle = new QLabel(this);
    m_labelMsg = new QLabel(this);
    m_btnOk = new QPushButton(this);
    m_labelWait = new QLabel(this);
    m_pMovie = new QMovie(":/res/loading.gif");
 
    m_labelTitle->setGeometry(QRect(10, 1, 220, 30));
    m_labelMsg->setGeometry(QRect(30, 60, 350, 60));
    m_labelMsg->setWordWrap(true);
    m_btnOk->setGeometry(QRect(320, 180, 58, 58));
    m_labelWait->setGeometry(QRect(30, 120, 322, 18));
 
    m_labelWait->setMovie(m_pMovie);
    m_pMovie->start();
 
    m_labelTitle->setStyleSheet("font: bold 17px; color: white;");
    m_labelMsg->setStyleSheet("font: bold 17px ; color: black; ");
 
    m_btnOk->setStyleSheet("QPushButton{background-image: url(:/res/dialog_ok.png);border: 0px;}"
                           "QPushButton:pressed{background-image: url(:/res/dialog_ok_p.png);border: 0px;}");
 
    connect(m_btnOk , SIGNAL(clicked()) , this , SLOT(on_btnOk_clicked()));
 
    setTitleText("升级");
    setMessageText("正在升级,请勿插拔U盘!");
    m_btnOk->hide();
}
 
ShowProgressDialog::~ShowProgressDialog()
{
    delete m_labelTitle;
    delete m_labelMsg;
    delete m_btnOk;
}
 
void ShowProgressDialog::setTitleText(const QString &strText)
{
    m_labelTitle->setText(strText);
}
 
void ShowProgressDialog::setMessageText(const QString &strMsg)
{
    m_labelMsg->setText(strMsg);
}
 
void ShowProgressDialog::on_btnOk_clicked()
{
    QDialog::accept();
}
 
void ShowProgressDialog::onThreadFinished(int nExitCode)
{
    qDebug() << "nExitCode" << nExitCode;
    if (nExitCode == 0)
    {
        setMessageText("升级成功,请重新上电。");
        m_btnOk->show();
        m_pMovie->stop();
    }
    else
    {
        setMessageText("升级出错!请断电以恢复!");
        m_btnOk->show();
        m_pMovie->stop();
    }
}

线程,继承自QThread,完成复制工作。使用QProcess来完成Linux下的解压和删除的工作。关于Qt的多线程,可以去查找一下其他的教程,这里就不做过多的解释。

class OperateThread : public QThread
{
    Q_OBJECT
public:
    explicit OperateThread(QObject *parent = 0);
 
public:
    void setUpgradeFile(QString strUpgradeFile)
    {
        m_UpgradeFile = strUpgradeFile;
    }
 
protected:
    void run();
 
signals:
    void emitFinish(int);
 
public slots:
 
private:
    void OprUpgradeUp();
 
private:
    QString  m_UpgradeFile;
};
 
OperateThread::OperateThread(QObject *parent) : QThread(parent)
{
 
}
 
void OperateThread::run()
{
    //很复杂的数据处理
    OprUpgradeUp();
}
 
void OperateThread::OprUpgradeUp()
{
#define DEST_DIR    "../"
 
    QString destDir = QString("%1/update.tar.gz").arg(DEST_DIR);
 
    if( QFile::copy(m_UpgradeFile, destDir) )
    {
        qDebug()<<"-------成功-----------";
    }
    else
    {
        qDebug()<<"-------出错-----------";
        //升级失败
        emit emitFinish(1);
        return;
    }
 
#ifdef Q_OS_LINUX
    QString exe = QString("tar -zxvf %1 -C %2").arg(destDir).arg(DEST_DIR);
    QProcess::execute(exe);
 
    //升级成功,自动同步磁盘并删除ARM上的升级包
    exe = QString("sync");
    QProcess::execute(exe);
    exe = QString("rm -rf %1").arg(destDir);
    QProcess::execute(exe);
#endif
    //升级成功
    emit emitFinish(0);
}

Chapter3 在开发板上运行的QT应用程序,如何拷贝文件到U盘

原文链接:https://blog.csdn.net/pang_fighting/article/details/139114780

笔者最近一直被这个问题所困惑,好在今天已经解决,之前找了很多资料,试了其他家的使用QProcess类来实现QT应用下的命令行实现,但很遗憾还是不行,找到新的解决方法如下:

system("cp -r /home/root/test_data /run/media/sda1");
system("sync");
system("umount /run/media/sda1");

解释一下,这需要你的开发板移植的系统能够插入U盘直接挂载,否则需要先检测U盘插入再挂载U盘,才可以使用,笔者用的正点原子157开发板,使用的是他们的官方系统,这个系统是支持插入U盘直接挂载的,挂载目录是“/run/media/sda1”。

U盘挂载完成之后,使用C函数system来执行命令行拷贝文件,拷贝完成之后执行sync命令,最后直接取消挂载拔出U盘即可!

Chapter4 嵌入式Qt,U盘升级程序

原文链接:https://blog.csdn.net/sjd753/article/details/135284869

前言

近期在完成一个U盘升级功能,对于Arm嵌入式设备来说应该算是必备的了。

参考链接:

https://blog.csdn.net/newnewman80/article/details/8766657
https://blog.csdn.net/newnewman80/article/details/8766657

一、实现过程

1.检测U盘中的更新程序

结合netlink捕获USB的热拔插,我们需要去检测U盘的挂载点,通常情况下都是挂载在/media下。

bool checkConsoleAppExists() 
{
    DIR *dir = opendir("/run/media"); // 打开 /run/media 目录 具体的挂载点根据设备决定
    if (dir) 
    {
        struct dirent *entry;
        while ((entry = readdir(dir)) != NULL) 
        {
            if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) 
            {
                devpath = "/run/media/" + std::string(entry->d_name);
                path = "/run/media/" + std::string(entry->d_name) + "/updateFileName";
 
                std::string signatureFilePath = devpath +"/sigFileName.sig"; 
                std::string publicKeyPath = devpath + "/key.pub" ;
 
                if (access(path.c_str(), F_OK) != -1) 
                {
                    //检测到更新程序,可以做一些校验,我这里的话是验证数字签名,根据具体情况决定
                    return checkUpdate(signatureFilePath,publicKeyPath,path);
                }
            }
        }
        closedir(dir);
    }
    return false;
}

2. 数字验证

/*检测是否需要更新,是否为正确的更新程序*/
bool checkUpdate(const std::string& signatureFile, const std::string& publicKeyFile, const std::string& dataFile) {
    // 打开更新文件
    // 这里是想要去检测版本号,去判断是否需要升级的,后面由于使用了数字签名就没有实现这部分。
    FILE *updateFile = fopen(dataFile.c_str(), "rb");
    if (!updateFile) {
        perror("Error opening update file\n");
        return false;
    }
    // 验证数字签名
    return verifySignature(signatureFile,publicKeyFile,dataFile);
}
// 函数用于验证数字签名是否有效
bool verifySignature(const std::string& signatureFile, const std::string& publicKeyFile, const std::string& dataFile) 
{
    // 打开公钥文件以读取公钥信息
    FILE* fp = fopen(publicKeyFile.c_str(), "r");
    if (!fp) {
        std::cerr << "Error opening public key file." << std::endl;
        return false;
    }
 
    // 从公钥文件中读取 RSA 公钥信息
    RSA* rsa = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
    fclose(fp);
 
    // 检查是否成功读取公钥信息
    if (!rsa) {
        std::cerr << "Error reading public key." << std::endl;
        return false;
    }
 
    // 创建一个用于存储 RSA 公钥的 EVP_PKEY 对象
    EVP_PKEY* evpKey = EVP_PKEY_new();
 
    // 将 RSA 公钥赋值给 EVP_PKEY 对象
    if (!EVP_PKEY_assign_RSA(evpKey, rsa)) {
        std::cerr << "Error assigning RSA key." << std::endl;
        RSA_free(rsa);
        return false;
    }
 
    // 创建一个用于消息摘要计算的 EVP_MD_CTX 对象
    EVP_MD_CTX* ctx = EVP_MD_CTX_new();
    if (!ctx) {
        std::cerr << "Error creating context." << std::endl;
        RSA_free(rsa);
        return false;
    }
 
    // 打开数据文件以进行签名验证
    std::ifstream fileStream(dataFile, std::ios::binary | std::ios::ate);
    if (!fileStream.is_open()) {
        std::cerr << "Error opening data file." << std::endl;
        EVP_MD_CTX_free(ctx);
        RSA_free(rsa);
        return false;
    }
 
    // 读取数据文件的大小和内容
    std::streamsize fileSize = fileStream.tellg();
    fileStream.seekg(0, std::ios::beg);
    std::vector<unsigned char> fileData(fileSize);
    if (!fileStream.read(reinterpret_cast<char*>(fileData.data()), fileSize)) {
        std::cerr << "Error reading data file." << std::endl;
        fileStream.close();
        EVP_MD_CTX_free(ctx);
        RSA_free(rsa);
        return false;
    }
    fileStream.close();
 
    // 打开签名文件以进行签名验证
    std::ifstream signatureFileStream(signatureFile, std::ios::binary);
    if (!signatureFileStream.is_open()) {
        std::cerr << "Error opening signature file." << std::endl;
        EVP_MD_CTX_free(ctx);
        RSA_free(rsa);
        return false;
    }
 
    // 读取签名文件的大小和内容
    signatureFileStream.seekg(0, std::ios::end);
    size_t signatureFileSize = signatureFileStream.tellg();
    signatureFileStream.seekg(0, std::ios::beg);
 
    std::vector<unsigned char> signatureData(signatureFileSize);
    if (!signatureFileStream.read(reinterpret_cast<char*>(signatureData.data()), signatureFileSize)) {
        std::cerr << "Error reading signature file." << std::endl;
        signatureFileStream.close();
        EVP_MD_CTX_free(ctx);
        RSA_free(rsa);
        return false;
    }
    signatureFileStream.close();
 
    // 更新消息摘要的内容,计算数据文件的哈希值
    if (!EVP_VerifyUpdate(ctx, fileData.data(), fileSize)) {
        std::cerr << "Error updating verification." << std::endl;
        EVP_MD_CTX_free(ctx);
        RSA_free(rsa);
        return false;
    }
 
    // 验证签名的有效性
    int result = EVP_VerifyFinal(ctx, signatureData.data(), signatureFileSize, evpKey);
    EVP_MD_CTX_free(ctx);
    RSA_free(rsa);
 
    // 根据验证结果返回相应的布尔值
    if (result != 1) {
        std::cerr << "Signature verification failed." << std::endl;
        return false;
    }
    std::cout << "Signature verification successful." << std::endl;
    return true;
}

3.升级程序

升级程序的话,实际上就是一个拷贝的过程。将设备上的备份,U盘中的更新程序拷贝出来。

/* 更新程序 */
bool copyFile(const char *sourcePath, const char *destinationPath) {
    
    //system("mv updateFileName  updateFileName.back"); //这里是一个备份,后续可以进行一个升级失败,备份还原。
 
    FILE *sourceFile = fopen(sourcePath, "rb");
    FILE *destinationFile = fopen(destinationPath, "wb");
 
    if (sourceFile == NULL) {
        printf("Error opening sourceFile files.\n");
        return false;
    }
    if(destinationFile == NULL){
        printf("Error opening  destinationFile files.\n");
        return false;
    }
    char buffer[1024];
    size_t bytesRead;
    while ((bytesRead = fread(buffer, 1, sizeof(buffer), sourceFile)) > 0) {
        printf("start copy file........................\n");
        fwrite(buffer, 1, bytesRead, destinationFile);
    }
 
    /*更简单一点就是用system命令,直接拷贝
      不过有一点需要注意,程序在运行的时候,有时候会拷贝失败。
      可以先用system命令删除或者备份,然后再进行拷贝,再用system给个权限。
    */
 
    fclose(sourceFile);
    fclose(destinationFile);
    return true;
}
 

4.主函数

int main(void)
{
    struct sockaddr_nl client;
    struct timeval tv;
    int CppLive, rcvlen, ret;
    fd_set fds;
    int buffersize = 1024;
    CppLive = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
    memset(&client, 0, sizeof(client));
    client.nl_family = AF_NETLINK;
    client.nl_pid = getpid();
    client.nl_groups = 1; /* receive broadcast message*/
    setsockopt(CppLive, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize));
    bind(CppLive, (struct sockaddr*)&client, sizeof(client));
    while (1) {
        char buf[UEVENT_BUFFER_SIZE] = { 0 };
        FD_ZERO(&fds);
        FD_SET(CppLive, &fds);
        tv.tv_sec = 0;
        tv.tv_usec = 100 * 1000;
        ret = select(CppLive + 1, &fds, NULL, NULL, &tv);
        if(ret < 0)
            continue;
        if(!(ret > 0 && FD_ISSET(CppLive, &fds)))
            continue;
        /* receive data */
        rcvlen = recv(CppLive, &buf, sizeof(buf), 0);
        if (rcvlen > 0) { 
            printf("%s\n", buf);
            //检测更新程序
            //升级程序
            //重启....
        }
    }
    close(CppLive);
    return 0;
}

二、总结

以上就是今天要讲的内容,本文仅仅简单介绍了结合netlink的u盘升级,后期可以通过Qt实现U盘升级,实现一个程序,通过界面可以判断更新状态。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/962150.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

拦截器快速入门及详解

拦截器Interceptor 快速入门 什么是拦截器&#xff1f; 是一种动态拦截方法调用的机制&#xff0c;类似于过滤器。 拦截器是Spring框架中提供的&#xff0c;用来动态拦截控制器方法的执行。 拦截器的作用&#xff1a;拦截请求&#xff0c;在指定方法调用前后&#xff0c;根…

信息安全专业优秀毕业设计选题汇总:热点选题

目录 前言 毕设选题 开题指导建议 更多精选选题 选题帮助 最后 前言 大家好,这里是海浪学长毕设专题! 大四是整个大学期间最忙碌的时光&#xff0c;一边要忙着准备考研、考公、考教资或者实习为毕业后面临的升学就业做准备,一边要为毕业设计耗费大量精力。学长给大家整理…

Linux中使用unzip

安装命令 yum install unzip unzip常用选项和参数 选项 说明 -q 隐藏解压过程中的消息输出 -d /path/to/directory 指定解压文件的目标目录 -P password 如果.zip文件被密码保护&#xff0c;使用此选项可以指定打开文件所需的密码 解压命令 unzip 要解压的压缩包unz…

ThreadLocal源码解析

文章目录 一、概述二、get()方法三、set()方法四、可能导致的内存泄漏问题五、remove六、思考&#xff1a;为什么要将ThreadLocalMap的value设置为强引用&#xff1f; 一、概述 ThreadLocal是线程私有的&#xff0c;独立初始化的变量副本。存放在和线程进行绑定的ThreadLocalMa…

批量解密,再也没有任何限制了

有的时候我们在网上下载了PDF文档。发现没有办法进行任何的操作&#xff0c;就连打印权限都没有。今天给大家介绍的这个软件可以一键帮你进行PDF解密&#xff0c;非常方便&#xff0c;完全免费。 PDF智能助手 批量解密PDF文件 这个软件不是很大&#xff0c;只有10MB&#xff…

《LLM大语言模型+RAG实战+Langchain+ChatGLM-4+Transformer》

文章目录 Langchain的定义Langchain的组成三个核心组件实现整个核心组成部分 为什么要使用LangchainLangchain的底层原理Langchain实战操作LangSmithLangChain调用LLM安装openAI库-国内镜像源代码运行结果小结 使用Langchain的提示模板部署Langchain程序安装langserve代码请求格…

车载软件 --- 大一新生入门汽车零部件嵌入式开发

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 简单&#xff0c;单纯&#xff0c;喜欢独处&#xff0c;独来独往&#xff0c;不易合同频过着接地气的生活…

有效运作神经网络

内容来自https://www.bilibili.com/video/BV1FT4y1E74V&#xff0c;仅为本人学习所用。 文章目录 训练集、验证集、测试集偏差、方差正则化正则化参数为什么正则化可以减少过拟合Dropout正则化Inverted Dropout其他的正则化方法数据增广Early stopping 归一化梯度消失与梯度爆…

【深度优先搜索篇】走迷宫的魔法:算法如何破解迷宫的神秘密码

当你在夜晚孤军奋战时&#xff0c;满天星光以为你而闪烁。 欢迎拜访&#xff1a;羑悻的小杀马特.-CSDN博客 本篇主题&#xff1a;轻轻松松拿捏洛谷走迷宫问题 制作日期&#xff1a;2024.12.31 隶属专栏&#xff1a;C/C题海汇总 首先我…

SQL进阶实战技巧:如何分析浏览到下单各步骤转化率及流失用户数?

目录 0 问题描述 1 数据准备 2 问题分析 3 问题拓展 3.1 跳出率计算 3.2 计算从浏览商品到支付订单的不同路径的用户数&#xff0c;并按照用户数降序排列。 往期精彩 0 问题描述 统计从浏览商品到最终下单的各个步骤的用户数和流失用户数,并计算转化率 用户表结构和…

Autosar-Os是怎么运行的?(内存保护)

写在前面&#xff1a; 入行一段时间了&#xff0c;基于个人理解整理一些东西&#xff0c;如有错误&#xff0c;欢迎各位大佬评论区指正&#xff01;&#xff01;&#xff01; 1.功能概述 以TC397芯片为例&#xff0c;英飞凌芯片集成了MPU模块&#xff0c; MPU模块采用了硬件机…

什么是Maxscript?为什么要学习Maxscript?

MAXScript是Autodesk 3ds Max的内置脚本语言,它是一种与3dsMax对话并使3dsMax执行某些操作的编程语言。它是一种脚本语言,这意味着您不需要编译代码即可运行。通过使用一系列基于文本的命令而不是使用UI操作,您可以完成许多使用UI操作无法完成的任务。 Maxscript是一种专有…

(一)QT的简介与环境配置WIN11

目录 一、QT的概述 二、QT的下载 三、简单编程 常用快捷键 一、QT的概述 简介 Qt&#xff08;发音&#xff1a;[kjuːt]&#xff0c;类似“cute”&#xff09;是一个跨平台的开发库&#xff0c;主要用于开发图形用户界面&#xff08;GUI&#xff09;应用程序&#xff0c;…

vim交换文件的作用

1.数据恢复&#xff1a;因为vim异常的退出&#xff0c;使用交换文件可以恢复之前的修改内容。 2.防止多人同时编辑&#xff1a;vim检测到交换文件的存在,会给出提示&#xff0c;以避免一个文件同时被多人编辑。 &#xff08;vim交换文件的工作原理&#xff1a;vim交换文件的工作…

SpringCloudGateWay和Sentinel结合做黑白名单来源控制

假设我们的分布式项目&#xff0c;admin是8087&#xff0c;gateway是8088&#xff0c;consumer是8086 我们一般的思路是我们的请求必须经过我们的网关8088然后网关转发到我们的分布式项目&#xff0c;那我要是没有处理我们绕过网关直接访问项目8087和8086不也是可以&#xff1…

将多目标贝叶斯优化与强化学习相结合用于TinyML

论文标题 Combining Multi-Objective Bayesian Optimization with Reinforcement Learning for TinyML 作者信息 Mark Deutel, Friedrich-Alexander-Universitt Erlangen-Nrnberg, Germany Georgios Kontes, Fraunhofer IIS, Fraunhofer Institute for Integrated Circuits …

Big Bird:适用于更长序列的Transformer模型

摘要 基于Transformer的模型&#xff0c;如BERT&#xff0c;已成为自然语言处理&#xff08;NLP&#xff09;中最成功的深度学习模型之一。然而&#xff0c;它们的一个核心限制是由于其全注意力机制&#xff0c;对序列长度的二次依赖&#xff08;主要是在内存方面&#xff09;…

26_DropDown使用方法

创建下拉框DropDown 其中样板Template 是展示的选项框 其中Caption 是选中某个选项之后 展示的内容&#xff08;Caption Text 说明文字/Caption Image 说明图示&#xff09; 修改其 说明文字Caption Text 创建一个说明图示Image 设置为居左 而Item是 展示的选项框所展示的文字与…

【redis进阶】redis 总结

目录 介绍一下什么是 Redis&#xff0c;有什么特点 Redis 支持哪些数据类型 Redis 数据类型底层的数据结构/编码方式是什么 ZSet 为什么使用跳表&#xff0c;而不是使用红黑树来实现 Redis 的常见应用场景有哪些 怎样测试 Redis 服务器的连通性 如何设置 key 的过期时间 Redis …

AI大模型开发原理篇-1:语言模型雏形之N-Gram模型

N-Gram模型概念 N-Gram模型是一种基于统计的语言模型&#xff0c;用于预测文本中某个词语的出现概率。它通过分析一个词语序列中前面N-1个词的出现频率来预测下一个词的出现。具体来说&#xff0c;N-Gram模型通过将文本切分为长度为N的词序列来进行建模。 注意&#xff1a;这…