From 9d495d487d673d162c52186fc5cf77f19b0f8fae Mon Sep 17 00:00:00 2001 From: luocai Date: Thu, 8 Aug 2024 17:05:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=8D=87=E7=BA=A7=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Analyser/Application.cpp | 22 ++- Analyser/Application.h | 2 +- Analyser/qml/OperationItem.qml | 348 +++++++++++++++++---------------- Analyser/qml/OtaPage.qml | 14 +- OtaUpdate/Widget.cpp | 25 ++- Peripheral/CdcUpdater.cpp | 31 ++- Peripheral/CdcUpdater.h | 7 +- 7 files changed, 251 insertions(+), 198 deletions(-) diff --git a/Analyser/Application.cpp b/Analyser/Application.cpp index 820ab0e..c306bcc 100644 --- a/Analyser/Application.cpp +++ b/Analyser/Application.cpp @@ -12,6 +12,7 @@ #include "VideoFrameProvider.h" #include "VideoPlayer.h" #include +#include #include #include #include @@ -408,7 +409,22 @@ void Application::onVerifyTimeout() { } } -void Application::startOta(const QString &path) { +bool Application::startOta(const QString &path) { + if (!QFile::exists(path)) { + emit otaMessage("文件不存在"); + return false; + } + auto device = CdcUpdater::searchDevice(); + if (device) { + LOG(info) << "device already in ota mode."; + } else { + if (m_communication) { + m_communication->startOta(); + } else { + emit otaMessage("请先打开设备"); + return false; + } + } LOG(info) << "start ota, ota path: " << path.toStdString(); m_updater = std::make_shared(); connect(m_updater.get(), &CdcUpdater::deviceDiscovered, this, &Application::onCdcDeviceDiscovered); @@ -416,8 +432,8 @@ void Application::startOta(const QString &path) { connect(m_updater.get(), &CdcUpdater::progressChanged, this, &Application::otaProgressChanged); connect(m_updater.get(), &CdcUpdater::message, this, &Application::otaMessage); - m_communication->startOta(); - m_updater->start(path); + m_updater->start(path, device ? *device : QSerialPortInfo()); + return true; } void Application::onCdcDeviceDiscovered(const QSerialPortInfo &info) { diff --git a/Analyser/Application.h b/Analyser/Application.h index c702c9d..92f163e 100644 --- a/Analyser/Application.h +++ b/Analyser/Application.h @@ -45,7 +45,7 @@ public: Q_INVOKABLE bool openUVC(const QString &deviceName); Q_INVOKABLE void close(); Q_INVOKABLE void closeUVC(); - Q_INVOKABLE void startOta(const QString &path); + Q_INVOKABLE bool startOta(const QString &path); Q_INVOKABLE void verify(bool captureImage, uint8_t timeout); Q_INVOKABLE void enroll(const QString &username, bool persistence, uint8_t timeout); Q_INVOKABLE void enrollExtended(const QString &username, bool persistence, uint8_t timeout); diff --git a/Analyser/qml/OperationItem.qml b/Analyser/qml/OperationItem.qml index 0452da6..c36e720 100644 --- a/Analyser/qml/OperationItem.qml +++ b/Analyser/qml/OperationItem.qml @@ -5,192 +5,197 @@ import QtQuick.Dialogs import QtQuick.Layouts import Analyser -ColumnLayout { - ConnectionItem {} +Item { + id: root + ColumnLayout { + anchors.fill: parent + ConnectionItem {} - GroupBox { - title: "命令" - Layout.columnSpan: 2 - enabled: App.connected - GridLayout { - columns: 2 - GroupBox { - title: "注册用户" - GridLayout { - columns: 2 - Text { - text: qsTr("用户姓名") - } - TextField { - id: enrollName - implicitWidth: 100 - } - Text { - text: qsTr("超时时间") - } - TextField { - id: enrollTimeout - implicitWidth: 100 - text: "10" - } + GroupBox { + id: commandBox + title: "命令" + Layout.columnSpan: 2 + enabled: App.connected + GridLayout { + columns: 2 + GroupBox { + title: "注册用户" + GridLayout { + columns: 2 + Text { + text: qsTr("用户姓名") + } + TextField { + id: enrollName + implicitWidth: 100 + } + Text { + text: qsTr("超时时间") + } + TextField { + id: enrollTimeout + implicitWidth: 100 + text: "10" + } - Text { - text: qsTr("持久化") - } - Switch { - id: persistence - checked: true - } + Text { + text: qsTr("持久化") + } + Switch { + id: persistence + checked: true + } - Text { - text: qsTr("保存图片") - } - Switch { - id: extendedMode - } + Text { + text: qsTr("保存图片") + } + Switch { + id: extendedMode + } - Button { - property bool enrolling: App.module ? (App.module.currentMessageId === 0x1d) - || (App.module.currentMessageId - === 0x1e) : false - text: enrolling ? "取消" : "注册" - onClicked: { - if (enrolling) { - App.module.reset() - } else if (extendedMode.checked) { - App.enrollExtended(enrollName.text, - persistence.checked, - parseInt(enrollTimeout.text)) - } else { - App.enroll(enrollName.text, - persistence.checked, - parseInt(enrollTimeout.text)) + Button { + property bool enrolling: App.module ? (App.module.currentMessageId === 0x1d) || (App.module.currentMessageId === 0x1e) : false + text: enrolling ? "取消" : "注册" + onClicked: { + if (enrolling) { + App.module.reset() + } else if (extendedMode.checked) { + App.enrollExtended( + enrollName.text, + persistence.checked, + parseInt(enrollTimeout.text)) + } else { + App.enroll(enrollName.text, + persistence.checked, + parseInt(enrollTimeout.text)) + } } } } } - } - GroupBox { - title: "识别用户" - GridLayout { - columns: 2 - Text { - text: qsTr("超时时间(s)") - } - TextField { - id: verifyTimeout - implicitWidth: 80 - text: "10" - } - Text { - text: qsTr("持续识别") - } - Switch { - checked: App.persistenceMode - onToggled: App.persistenceMode = !App.persistenceMode - } - Text { - text: qsTr("保存图片") - } - Switch { - id: extendedVerifyMode - } - Text { - text: qsTr("识别间隔(s)") - } - TextField { - id: verifyIntetval - implicitWidth: 80 - text: App.persistenceVerifyInterval - } - Item {} - Button { - text: App.isVerifying ? "停止" : "识别" - onClicked: { - if (App.isVerifying) { - App.module.reset() - } else { - App.persistenceVerifyInterval = parseInt( - verifyIntetval.text) - App.verify(extendedVerifyMode.checked, - parseInt(verifyTimeout.text)) + GroupBox { + title: "识别用户" + GridLayout { + columns: 2 + Text { + text: qsTr("超时时间(s)") + } + TextField { + id: verifyTimeout + implicitWidth: 80 + text: "10" + } + Text { + text: qsTr("持续识别") + } + Switch { + checked: App.persistenceMode + onToggled: App.persistenceMode = !App.persistenceMode + } + Text { + text: qsTr("保存图片") + } + Switch { + id: extendedVerifyMode + } + Text { + text: qsTr("识别间隔(s)") + } + TextField { + id: verifyIntetval + implicitWidth: 80 + text: App.persistenceVerifyInterval + } + Item {} + Button { + text: App.isVerifying ? "停止" : "识别" + onClicked: { + if (App.isVerifying) { + App.module.reset() + } else { + App.persistenceVerifyInterval = parseInt( + verifyIntetval.text) + App.verify(extendedVerifyMode.checked, + parseInt(verifyTimeout.text)) + } } } } } - } - GroupBox { - title: "删除用户" - GridLayout { - columns: 2 - Text { - text: qsTr("用户ID") - } - TextField { - id: deleteUserId - implicitWidth: 100 - } - Button { - text: "删除" - onClicked: App.deleteUser(parseInt(deleteUserId.text)) - } - Button { - text: "删除所有" - onClicked: App.deleteAll() - } - } - } - GroupBox { - title: "图片注册" - GridLayout { - columns: 2 - TextField { - id: imagePath - Layout.columnSpan: 2 - implicitWidth: 180 - placeholderText: "请选择图片" - onPressed: { - fileDialog.open() + GroupBox { + title: "删除用户" + GridLayout { + columns: 2 + Text { + text: qsTr("用户ID") + } + TextField { + id: deleteUserId + implicitWidth: 100 + } + Button { + text: "删除" + onClicked: App.deleteUser(parseInt( + deleteUserId.text)) + } + Button { + text: "删除所有" + onClicked: App.deleteAll() } } - Button { - text: "注册" - onClicked: App.uploadImage(imagePath.text, 0) - } - Button { - text: "识别" - onClicked: App.uploadImage(imagePath.text, 1) + } + GroupBox { + title: "图片注册" + GridLayout { + columns: 2 + TextField { + id: imagePath + Layout.columnSpan: 2 + implicitWidth: 180 + placeholderText: "请选择图片" + onPressed: { + fileDialog.open() + } + } + Button { + text: "注册" + onClicked: App.uploadImage(imagePath.text, 0) + } + Button { + text: "识别" + onClicked: App.uploadImage(imagePath.text, 1) + } } } - } - Button { - text: "复位" - onClicked: App.module.reset() - } - Button { - text: "状态查询" - onClicked: App.module.requestCurrentStatus() - } - Button { - text: "ID查询" - onClicked: App.module.requestUniqueId() - } - Button { - text: "OTA升级" - onClicked: loader.active = true - } - Row { - Text { - text: qsTr("日志") + Button { + text: "复位" + onClicked: App.module.reset() } - Switch { - id: debugMode - onToggled: App.module.setDebugEnabled(debugMode.checked) + Button { + text: "状态查询" + onClicked: App.module.requestCurrentStatus() + } + Button { + text: "ID查询" + onClicked: App.module.requestUniqueId() + } + Button { + id: otaButton + text: "OTA升级" + onClicked: loader.active = true + } + Row { + Text { + text: qsTr("日志") + } + Switch { + id: debugMode + onToggled: App.module.setDebugEnabled(debugMode.checked) + } } } } } - FileDialog { id: fileDialog nameFilters: ["图片 (*.jpg *.yuv)"] @@ -204,6 +209,19 @@ ColumnLayout { } } + MouseArea { + width: otaButton.width + height: otaButton.height + enabled: !commandBox.enabled + onClicked: { + loader.active = true + } + Component.onCompleted: { + x = Qt.binding(() => otaButton.mapToItem(root, 0, 0).x) + y = Qt.binding(() => otaButton.mapToItem(root, 0, 0).y) + } + } + Loader { id: loader source: "OtaPage.qml" diff --git a/Analyser/qml/OtaPage.qml b/Analyser/qml/OtaPage.qml index ce899a1..3811ee8 100644 --- a/Analyser/qml/OtaPage.qml +++ b/Analyser/qml/OtaPage.qml @@ -45,14 +45,14 @@ Popup { RowLayout { spacing: 10 ProgressBar { - id:progressBar + id: progressBar Layout.fillWidth: true from: 0 - to:100 + to: 100 value: 0.0 } Text { - id:progressText + id: progressText text: "0%" verticalAlignment: Text.AlignVCenter } @@ -72,8 +72,7 @@ Popup { Layout.alignment: Qt.AlignRight onClicked: { otaMessage.color = "black" - App.startOta(otaFile.text) - enabled = false + enabled = !App.startOta(otaFile.text) } } } @@ -106,10 +105,9 @@ Popup { function onOtaMessage(message) { otaMessage.text = message } - function onOtaProgressChanged(progress){ - progressBar.value = progress; + function onOtaProgressChanged(progress) { + progressBar.value = progress progressText.text = `${progress}%` - } } } diff --git a/OtaUpdate/Widget.cpp b/OtaUpdate/Widget.cpp index 8574968..e736d66 100644 --- a/OtaUpdate/Widget.cpp +++ b/OtaUpdate/Widget.cpp @@ -62,23 +62,28 @@ void Widget::start() { QMessageBox::warning(this, "升级", "升级文件不存在!"); return; } - auto discovery = std::make_shared(); + auto device = CdcUpdater::searchDevice(); + if (device) { + LOG(info) << "device already in ota mode."; + } else { + auto discovery = std::make_shared(); - std::error_code error; - setMessage("尝试发现设备......"); - auto device = discovery->find("UVC Camera", error); - if (!device) { - QMessageBox::warning(this, "升级", "未检测到模组,请尝试重新插入模组!"); - return; + std::error_code error; + setMessage("尝试发现设备......"); + auto device = discovery->find("UVC Camera", error); + if (!device) { + QMessageBox::warning(this, "升级", "未检测到模组,请尝试重新插入模组!"); + return; + } + setMessage("发现设备成功,进入BOOT模式......"); + discovery->enterOtaMode(device, error); } - setMessage("发现设备成功,进入BOOT模式......"); - discovery->enterOtaMode(device, error); m_updater = std::make_shared(); connect(m_updater.get(), &CdcUpdater::deviceDiscovered, this, &Widget::onCdcDeviceDiscovered); connect(m_updater.get(), &CdcUpdater::updateFinished, this, &Widget::onUpdateFinished); connect(m_updater.get(), &CdcUpdater::progressChanged, this, &Widget::setProgress); connect(m_updater.get(), &CdcUpdater::message, this, &Widget::setMessage); - m_updater->start(filePath); + m_updater->start(filePath, device ? *device : QSerialPortInfo()); setControlsEnabled(false); } diff --git a/Peripheral/CdcUpdater.cpp b/Peripheral/CdcUpdater.cpp index 9c3f7d5..f317308 100644 --- a/Peripheral/CdcUpdater.cpp +++ b/Peripheral/CdcUpdater.cpp @@ -3,7 +3,6 @@ #include "StringUtility.h" #include #include -#include #include #include #include @@ -41,10 +40,15 @@ CdcUpdater::CdcUpdater(QObject *parent) : QObject(parent) { CdcUpdater::~CdcUpdater() { } -void CdcUpdater::start(const QString &path) { - if (m_timerId < 0) { - m_timerId = startTimer(1000); +void CdcUpdater::start(const QString &path, const QSerialPortInfo &info) { + if (info.isNull()) { + if (m_timerId < 0) { + m_timerId = startTimer(1000); + } + } else { + open(info); } + m_path = Amass::StringUtility::UTF8ToGBK(path.toStdString()); LOG(info) << "ota file: " << m_path; emit progressChanged(++m_progress); @@ -84,20 +88,29 @@ bool CdcUpdater::write(Command command, const uint8_t *data, uint32_t size) { return m_serialPort->write(reinterpret_cast(packet.data()), packet.size()); } -void CdcUpdater::timerEvent(QTimerEvent *event) { +std::optional CdcUpdater::searchDevice() { + std::optional ret = std::nullopt; const auto serialPortInfos = QSerialPortInfo::availablePorts(); for (const QSerialPortInfo &portInfo : serialPortInfos) { LOG(info) << "portName:" << portInfo.portName().toStdString() << ", vendorIdentifier: " << portInfo.vendorIdentifier() << ", productIdentifier: " << portInfo.productIdentifier(); if (portInfo.vendorIdentifier() == 0xffff) { - LOG(info) << "founded device: " << portInfo.portName().toStdString(); - emit deviceDiscovered(portInfo); - killTimer(m_timerId); - m_timerId = -1; + ret = portInfo; break; } } + return ret; +} + +void CdcUpdater::timerEvent(QTimerEvent *event) { + auto device = searchDevice(); + if (device) { + LOG(info) << "founded device: " << device->portName().toStdString(); + emit deviceDiscovered(*device); + killTimer(m_timerId); + m_timerId = -1; + } LOG(info) << "----------"; } diff --git a/Peripheral/CdcUpdater.h b/Peripheral/CdcUpdater.h index 1e146e2..9e8b52b 100644 --- a/Peripheral/CdcUpdater.h +++ b/Peripheral/CdcUpdater.h @@ -2,9 +2,10 @@ #define __CDCUPDATER_H__ #include +#include +#include class QSerialPort; -class QSerialPortInfo; class CdcUpdater : public QObject { Q_OBJECT @@ -35,8 +36,10 @@ public: }; CdcUpdater(QObject *parent = nullptr); ~CdcUpdater(); - void start(const QString &path); + void start(const QString &path, const QSerialPortInfo &info = QSerialPortInfo()); bool open(const QSerialPortInfo &info); + static std::optional searchDevice(); + signals: void deviceDiscovered(const QSerialPortInfo &info); void updateFinished();