commit ce33c00574fbec6f8c2e7aab8b9558fd57995521 Author: luocai <168062547@qq.com> Date: Fri May 17 10:36:23 2024 +0800 first commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..f3960e2 --- /dev/null +++ b/.clang-format @@ -0,0 +1,17 @@ +BasedOnStyle: LLVM + +ObjCBlockIndentWidth: 4 +IndentWidth: 4 +TabWidth: 4 +AccessModifierOffset: -4 +ColumnLimit: 120 + +#模板声明后换行 +AlwaysBreakTemplateDeclarations: true + +# 是否允许短if单行 If true, if (a) return; 可以放到同一行 +AllowShortIfStatementsOnASingleLine: true + +#短句 while (true) continue; 能被放到单行。 +AllowShortLoopsOnASingleLine: true +AllowShortFunctionsOnASingleLine: false \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2500e85 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# C++ objects and libs +*.slo +*.lo +*.o +*.a +*.la +*.lai +*.so +*.dll +*.dylib + +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +*.qmlc +*.jsc +Makefile* +*build-* + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* + +build +logs \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..dfff420 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.5) + +project(SmartLockerTools VERSION 0.1 LANGUAGES C CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(Projects_ROOT E:/Projects) +set(Libraries_ROOT ${Projects_ROOT}/Libraries) + +set(BOOST_ROOT ${Libraries_ROOT}/boost_1_85_0_msvc2022_64bit) +set(Boost_INCLUDE_DIR ${BOOST_ROOT}/include/boost-1_85) +option(Boost_USE_STATIC_LIBS OFF) +add_compile_definitions( + BOOST_USE_WINAPI_VERSION=BOOST_WINAPI_VERSION_WIN10 +) + +add_subdirectory(${Projects_ROOT}/Kylin/Universal Universal) +add_subdirectory(${Projects_ROOT}/Kylin/Encrypt Encrypt) + +add_subdirectory(OtaUpdate) diff --git a/OtaUpdate/CMakeLists.txt b/OtaUpdate/CMakeLists.txt new file mode 100644 index 0000000..31f1d4a --- /dev/null +++ b/OtaUpdate/CMakeLists.txt @@ -0,0 +1,41 @@ +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets SerialPort) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets SerialPort) + +set(PROJECT_SOURCES + main.cpp + CdcUpdater.h CdcUpdater.cpp + DeviceDiscovery.h DeviceDiscovery.cpp + Widget.cpp + Widget.h +) + +qt_add_executable(SmartLockerUpdater + MANUAL_FINALIZATION + ${PROJECT_SOURCES} +) + +target_link_libraries(SmartLockerUpdater + PRIVATE Universal + PRIVATE Encrypt + PRIVATE mfplat + PRIVATE mfuuid + PRIVATE Mfreadwrite + PRIVATE Qt${QT_VERSION_MAJOR}::Widgets + PRIVATE Qt${QT_VERSION_MAJOR}::SerialPort + PRIVATE Mf +) + +set_target_properties(SmartLockerUpdater PROPERTIES + ${BUNDLE_ID_OPTION} + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + MACOSX_BUNDLE TRUE + WIN32_EXECUTABLE TRUE +) + +if(QT_VERSION_MAJOR EQUAL 6) + qt_finalize_executable(SmartLockerUpdater) +endif() diff --git a/OtaUpdate/CdcUpdater.cpp b/OtaUpdate/CdcUpdater.cpp new file mode 100644 index 0000000..54df82d --- /dev/null +++ b/OtaUpdate/CdcUpdater.cpp @@ -0,0 +1,193 @@ +#include "CdcUpdater.h" +#include "BoostLog.h" +#include +#include +#include +#include +#include +#include +#include + +static uint8_t calculate_crc8(const uint8_t *data, int length) { + static constexpr uint8_t crc8_table[256] = { + 0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, + 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9, + 0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, + 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, + 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, + 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, + 0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, + 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, + 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, + 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10, + 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, + 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, + 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, + 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef, + 0xfa, 0xfd, 0xf4, 0xf3, + }; + + uint8_t crc = 0; + for (int i = 0; i < length; ++i) { + crc = crc8_table[crc ^ data[i]]; + } + return crc; +} + +CdcUpdater::CdcUpdater(QObject *parent) : QObject(parent) { +} + +CdcUpdater::~CdcUpdater() { +} + +void CdcUpdater::start(const QString &path) { + if (m_timerId < 0) { + m_timerId = startTimer(1000); + } + m_path = path.toStdString(); + LOG(info) << "ota file: " << m_path; + emit progressChanged(++m_progress); +} + +bool CdcUpdater::open(const QSerialPortInfo &info) { + m_serialPort = std::make_shared(info); + connect(m_serialPort.get(), &QSerialPort::readyRead, this, &CdcUpdater::onReadyRead); + m_serialPort->setBaudRate(2000000); + m_serialPort->setDataBits(QSerialPort::Data8); + m_serialPort->setStopBits(QSerialPort::OneStop); + + auto ret = m_serialPort->open(QSerialPort::ReadWrite); + if (ret) { + ret = write(EnterUpgrade); + emit progressChanged(++m_progress); + } + + return ret; +} + +bool CdcUpdater::write(Command command, const uint8_t *data, uint32_t size) { + std::array packet = {0}; + packet[0] = 0xAA; + packet[1] = 0x55; + packet[2] = command; + if (data != nullptr) { + memcpy(&packet[3], data, size); + } + std::ostringstream oss; + for (int i = 0; i < packet.size(); i++) { + oss << "0x" << std::hex << (static_cast(packet[i]) & 0xff) << " "; + } + LOG(info) << "write " << oss.str(); + return m_serialPort->write(reinterpret_cast(packet.data()), packet.size()); +} + +void CdcUpdater::timerEvent(QTimerEvent *event) { + const auto serialPortInfos = QSerialPortInfo::availablePorts(); + for (const QSerialPortInfo &portInfo : serialPortInfos) { + if (portInfo.vendorIdentifier() == 0xffff) { + LOG(info) << "founded device: " << portInfo.portName().toStdString(); + emit deviceDiscovered(portInfo); + killTimer(m_timerId); + m_timerId = -1; + } + } +} + +void CdcUpdater::transferBin() { + char buffer[4096] = {0}; + m_ifs->read(buffer, sizeof(buffer)); + auto readSize = m_ifs->gcount(); + + if (readSize > 0) { + m_progress += (99 - m_progressBeforeTranster) * static_cast(m_packetIndex) / m_totalPackageSize; + if (m_progress > 99) m_progress = 99; + emit progressChanged(m_progress); + + std::array content; + content[0] = (m_packetIndex >> 8) & 0xFF; + content[1] = m_packetIndex & 0xFF; + + content[2] = (readSize >> 8) & 0xFF; + content[3] = readSize & 0xFF; + + content[4] = calculate_crc8(reinterpret_cast(buffer), readSize); + write(TransferBin, content.data(), content.size()); + m_serialPort->write(buffer, readSize); + + m_packetIndex++; + } +} + +void CdcUpdater::onReadyRead() { + auto data = m_serialPort->readAll(); + + std::ostringstream oss; + for (int i = 0; i < data.size(); i++) { + oss << "0x" << std::hex << (static_cast(data[i]) & 0xff) << " "; + } + LOG(info) << "onReadyRead, size: " << data.size() << ", " << oss.str(); + + uint8_t command = static_cast(data[2]); + if (command == EnterUpgradeReply) { + write(GetVersion); + emit progressChanged(++m_progress); + message("获取模组OTA版本......"); + } else if (command == GetVersionReply) { + LOG(info) << "device ota version: 0x" << std::hex << (static_cast(data[4]) & 0xff); + int fileSize = std::filesystem::file_size(m_path); + m_totalPackageSize = fileSize / 4096; + if ((fileSize % 4096) > 0) m_totalPackageSize++; + std::array content; + content[0] = (m_totalPackageSize >> 8) & 0xFF; + content[1] = m_totalPackageSize & 0xFF; + content[2] = 0x14; + write(StartUpgrade, content.data(), content.size()); + emit progressChanged(++m_progress); + message("模组进入升级状态......"); + } else if (command == StartUpgradeReply) { + std::ifstream ifs(m_path, std::ifstream::binary); + char buffer[4096] = {0}; + mbedtls_md5_context context; + mbedtls_md5_init(&context); + mbedtls_md5_starts(&context); + uint8_t md5[16]; + while (ifs) { + ifs.read(buffer, sizeof(buffer)); + auto readSize = ifs.gcount(); + mbedtls_md5_update(&context, reinterpret_cast(buffer), readSize); + } + mbedtls_md5_finish(&context, md5); + mbedtls_md5_free(&context); + std::ostringstream oss; + for (int i = 0; i < sizeof(md5); i++) { + oss << "0x" << std::hex << (static_cast(md5[i]) & 0xff) << " "; + } + LOG(info) << "md5: " << oss.str(); + write(Md5Checksum); + m_serialPort->write(reinterpret_cast(md5), sizeof(md5)); + emit progressChanged(++m_progress); + emit message("发送升级包MD5校验值......"); + } else if (command == Md5ChecksumReply) { + m_ifs = std::make_shared(m_path, std::ifstream::binary); + m_packetIndex = 0; + m_progressBeforeTranster = m_progress; + transferBin(); + emit message("发送升级包数据......"); + } else if (command == TransferBinReply) { + transferBin(); + if (m_packetIndex == m_totalPackageSize) { + message("正在擦写flash,请稍后......"); + } + } else if (command == CurrentVersion) { + LOG(info) << "update finieshed."; + write(CurrentVersionReply); + // std::this_thread::sleep_for(std::chrono::seconds(1)); + write(ExitUpgrade); + m_progress = 100; + emit progressChanged(m_progress); + emit updateFinished(); + emit message("升级结束。"); + } else if (command == UpgradeExit) { + LOG(info) << "module exit boot mode."; + } +} diff --git a/OtaUpdate/CdcUpdater.h b/OtaUpdate/CdcUpdater.h new file mode 100644 index 0000000..070fbd7 --- /dev/null +++ b/OtaUpdate/CdcUpdater.h @@ -0,0 +1,64 @@ +#ifndef __CDCUPDATER_H__ +#define __CDCUPDATER_H__ + +#include + +class QSerialPort; +class QSerialPortInfo; + +class CdcUpdater : public QObject { + Q_OBJECT +public: + enum Command : uint8_t { + EnterUpgrade = 0xF1, + EnterUpgradeReply = 0xF2, + + TransferBin = 0xF3, + TransferBinReply = 0xF4, + + ExitUpgrade = 0xF5, + ExitUpgradeReply = 0xF6, + + GetVersion = 0xF7, + GetVersionReply = 0xF8, + + CurrentVersion = 0xF9, // 设备上报 + CurrentVersionReply = 0xFA, + + StartUpgrade = 0xFB, + StartUpgradeReply = 0xFC, + + Md5ChecksumReply = 0xEB, + Md5Checksum = 0xEC, + + UpgradeExit = 0xE9, // 模组推出boot时回复 + }; + CdcUpdater(QObject *parent = nullptr); + ~CdcUpdater(); + void start(const QString &path); + bool open(const QSerialPortInfo &info); +signals: + void deviceDiscovered(const QSerialPortInfo &info); + void updateFinished(); + void progressChanged(int32_t progress); + void message(const QString &text); + +protected: + bool write(Command command, const uint8_t *data = nullptr, uint32_t size = 0); + void timerEvent(QTimerEvent *event) final; + void transferBin(); + +protected slots: + void onReadyRead(); + +private: + std::shared_ptr m_serialPort; + int m_timerId = -1; + std::string m_path; + std::shared_ptr m_ifs; + int m_totalPackageSize = -1; + int m_packetIndex = 0; + int32_t m_progress = 0; + int32_t m_progressBeforeTranster = 0; // 用于内部进度表示 +}; +#endif // __CDCUPDATER_H__ diff --git a/OtaUpdate/DeviceDiscovery.cpp b/OtaUpdate/DeviceDiscovery.cpp new file mode 100644 index 0000000..8e774da --- /dev/null +++ b/OtaUpdate/DeviceDiscovery.cpp @@ -0,0 +1,156 @@ +#include "DeviceDiscovery.h" +#include "BoostLog.h" +#include "StringUtility.h" +#include +#include +#include +#include + +template +void SafeRelease(T **ppT) { + if (*ppT) { + (*ppT)->Release(); + *ppT = NULL; + } +} + +DeviceDiscovery::DeviceDiscovery() { +} + +std::shared_ptr DeviceDiscovery::find(const std::string &deviceName, std::error_code &error) { + std::shared_ptr ret; + + IMFAttributes *attributes = nullptr; + boost::scope::scope_exit guard([&attributes] { SafeRelease(&attributes); }); + auto result = MFCreateAttributes(&attributes, 1); + if (FAILED(result)) { + return ret; + } + + result = attributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); + if (FAILED(result)) { + return ret; + } + + UINT32 count; + IMFActivate **devices = nullptr; + result = MFEnumDeviceSources(attributes, &devices, &count); + if (FAILED(result)) { + return ret; + } + + if (count == 0) { + return ret; + } + + int index = -1; + for (int i = 0; i < count; i++) { + auto name = this->deviceName(devices[i]); + LOG(info) << "device[" << i << "]: " << name; + if (name == deviceName) { + index = i; + break; + } + } + if (index >= 0) { + IMFMediaSource *source = nullptr; + result = devices[0]->ActivateObject(IID_PPV_ARGS(&source)); + if (FAILED(result)) { + } else { + ret = std::make_shared(source); + } + } + return ret; +} + +std::string DeviceDiscovery::deviceName(IMFActivate *device) { + std::string ret; + WCHAR *friendlyName = nullptr; + uint32_t nameLength = 0; + auto result = device->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &friendlyName, &nameLength); + + if (SUCCEEDED(result)) { + ret = Amass::StringUtility::wstringToString(std::wstring(friendlyName, nameLength)); + } + if (friendlyName != nullptr) { + CoTaskMemFree(friendlyName); + } + return ret; +} + +void DeviceDiscovery::enterOtaMode(const std::shared_ptr &device, std::error_code &error) { + auto resolutions = deviceResolutions(device); + // for (auto &[w, h] : resolutions) { + // LOG(info) << w << " " << h; + // } + + // LOG(info) << "resolutions: " << resolutions.size(); + int32_t otaSpecificHeight = -1; + for (auto &[width, height] : resolutions) { + if (width == OtaSpecificWidth) { + otaSpecificHeight = height; + break; + } + } + if (otaSpecificHeight <= 0) { + LOG(error) << "cannot find ota specific resolution."; + return; + } + + IMFMediaType *type = nullptr; + auto result = MFCreateMediaType(&type); + if (SUCCEEDED(result)) { + result = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); + result = type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_MJPG); + result = MFSetAttributeSize(type, MF_MT_FRAME_SIZE, OtaSpecificWidth, otaSpecificHeight); + result = type->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); + if (SUCCEEDED(result)) { + result = device->reader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, nullptr, type); + } + type->Release(); + } + DWORD streamIndex, flags; + LONGLONG llTimeStamp; + IMFSample *pSample = NULL; + result = device->reader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, &streamIndex, &flags, + &llTimeStamp, &pSample); +} + +DeviceDiscovery::Resolutions DeviceDiscovery::deviceResolutions(const std::shared_ptr &source) { + DeviceDiscovery::Resolutions ret; + HRESULT result = S_OK; + DWORD index = 0; + while (SUCCEEDED(result)) { + IMFMediaType *mediaType = nullptr; + result = source->reader->GetNativeMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, index++, &mediaType); + if (SUCCEEDED(result)) { + UINT32 width, height; + MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height); + mediaType->Release(); + auto iterator = std::find_if(ret.begin(), ret.end(), [&width, &height](const Resolution &resolution) { + return (resolution.first == width) && (resolution.second == height); + }); + if (iterator == ret.end()) { + ret.push_back({width, height}); + } + } + } + return ret; +} + +DeviceDiscovery::Device::Device(IMFMediaSource *source) : source(source) { + source->AddRef(); + auto result = MFCreateSourceReaderFromMediaSource(source, nullptr, &reader); + if (FAILED(result)) { + LOG(error) << "MFCreateSourceReaderFromMediaSource() failed, result: " << result; + } +} + +DeviceDiscovery::Device::~Device() { + if (source != nullptr) { + source->Release(); + } + if (reader != nullptr) { + reader->Release(); + } +} diff --git a/OtaUpdate/DeviceDiscovery.h b/OtaUpdate/DeviceDiscovery.h new file mode 100644 index 0000000..276a98e --- /dev/null +++ b/OtaUpdate/DeviceDiscovery.h @@ -0,0 +1,32 @@ +#ifndef __DEVICEDISCOVERY_H__ +#define __DEVICEDISCOVERY_H__ + +#include +#include +#include +#include +#include +#include + +class DeviceDiscovery { + constexpr static int32_t OtaSpecificWidth = 96; + +public: + struct Device { + Device(IMFMediaSource *source); + ~Device(); + IMFMediaSource *source = nullptr; + IMFSourceReader *reader = nullptr; + }; + + using Resolution = std::pair; + using Resolutions = std::vector; + DeviceDiscovery(); + std::shared_ptr find(const std::string &deviceName, std::error_code &error); + void enterOtaMode(const std::shared_ptr &device, std::error_code &error); + +protected: + std::string deviceName(IMFActivate *device); + Resolutions deviceResolutions(const std::shared_ptr &source); +}; +#endif // __DEVICEDISCOVERY_H__ diff --git a/OtaUpdate/ErrorCode.cpp b/OtaUpdate/ErrorCode.cpp new file mode 100644 index 0000000..7ac04d3 --- /dev/null +++ b/OtaUpdate/ErrorCode.cpp @@ -0,0 +1,5 @@ +#include "ErrorCode.h" + +std::error_code makeErrorCode(ErrorCode code){ + return {static_cast(code), ErrorCategory::instance()}; +} \ No newline at end of file diff --git a/OtaUpdate/ErrorCode.h b/OtaUpdate/ErrorCode.h new file mode 100644 index 0000000..f5100bc --- /dev/null +++ b/OtaUpdate/ErrorCode.h @@ -0,0 +1,19 @@ +#ifndef __ERRORCODE_H__ +#define __ERRORCODE_H__ + +#include + +enum class ErrorCode { + Success = 0, + NoDevices = -1, // 枚举不到UVC设备 +}; + +namespace std { +template <> +struct is_error_code_enum : true_type {}; +} // namespace std + +class ErrorCategory : public std::error_category {}; +std::error_code makeErrorCode(ErrorCode code); + +#endif // __ERRORCODE_H__ \ No newline at end of file diff --git a/OtaUpdate/Widget.cpp b/OtaUpdate/Widget.cpp new file mode 100644 index 0000000..0ddc92a --- /dev/null +++ b/OtaUpdate/Widget.cpp @@ -0,0 +1,141 @@ +#include "Widget.h" +#include "BoostLog.h" +#include "CdcUpdater.h" +#include "DeviceDiscovery.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Widget::Widget(QWidget *parent) : QWidget(parent) { + m_fileEditor = new QLineEdit(); + m_fileEditor->setPlaceholderText("请选择升级文件或将文件拖入工具中"); + + m_selectButton = new QPushButton("选择"); + connect(m_selectButton, &QPushButton::clicked, this, &Widget::onSelectButtonClicked); + + auto fileLayout = new QHBoxLayout(); + fileLayout->addWidget(m_fileEditor); + fileLayout->addWidget(m_selectButton); + + m_startButton = new QPushButton(this); + m_startButton->setText("开始"); + connect(m_startButton, &QPushButton::clicked, this, &Widget::start); + + m_progressBar = new QProgressBar(); + m_progressBar->setRange(0, 100); + m_progressBar->setValue(0); + + m_messageLabel = new QLabel(); + m_messageLabel->setText("选择升级文件,点击开始按钮升级模组"); + + auto statusLayout = new QVBoxLayout(); + statusLayout->addWidget(m_progressBar); + statusLayout->addWidget(m_messageLabel); + + auto controlLayout = new QHBoxLayout(); + controlLayout->addLayout(statusLayout); + controlLayout->addWidget(m_startButton); + + auto layout = new QVBoxLayout(this); + layout->addLayout(fileLayout); + layout->addLayout(controlLayout); + + setAcceptDrops(true); +} + +Widget::~Widget() {} + +void Widget::start() { + m_progressBar->setValue(0); + auto filePath = m_fileEditor->text(); + if (!std::filesystem::exists(filePath.toStdString())) { + QMessageBox::warning(this, "升级", "升级文件不存在!"); + return; + } + auto discovery = std::make_shared(); + + std::error_code error; + setMessage("尝试发现设备......"); + auto device = discovery->find("UVC Camera", error); + if (!device) { + QMessageBox::warning(this, "升级", "未检测到模组,请尝试重新插入模组!"); + return; + } + 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); + setControlsEnabled(false); +} + +void Widget::onCdcDeviceDiscovered(const QSerialPortInfo &info) { + auto status = m_updater->open(info); + LOG(info) << "open cdc port: " << info.portName().toStdString() << ", status: " << status; +} + +void Widget::onSelectButtonClicked() { + auto fileName = QFileDialog::getOpenFileName(this, "升级文件", "", tr("OTA文件 (*.Pkg)")); + m_fileEditor->setText(fileName); +} + +void Widget::onUpdateFinished() { + QMessageBox::information(this, "升级", "升级成功!"); + setControlsEnabled(true); +} + +static QString dragFilePath(const QMimeData *mimeData) { + QString ret; + if (mimeData->hasUrls()) { + auto urls = mimeData->urls(); + for (auto &url : urls) { + auto path = url.toLocalFile(); + if (path.isEmpty()) continue; + ret = path; + break; + } + } + return ret; +} + +void Widget::dragEnterEvent(QDragEnterEvent *event) { + auto path = dragFilePath(event->mimeData()); + if (path.endsWith(".Pkg", Qt::CaseInsensitive)) { + event->acceptProposedAction(); + } else { + event->ignore(); + } +} + +void Widget::dropEvent(QDropEvent *event) { + auto path = dragFilePath(event->mimeData()); + if (path.endsWith(".Pkg", Qt::CaseInsensitive)) { + m_fileEditor->setText(path); + } +} + +void Widget::setControlsEnabled(bool enabled) { + m_startButton->setEnabled(enabled); + m_selectButton->setEnabled(enabled); + m_fileEditor->setEnabled(enabled); +} + +void Widget::setProgress(int32_t progress) { + m_progressBar->setValue(progress); +} + +void Widget::setMessage(const QString &message) { + m_messageLabel->setText(message); +} diff --git a/OtaUpdate/Widget.h b/OtaUpdate/Widget.h new file mode 100644 index 0000000..e860adc --- /dev/null +++ b/OtaUpdate/Widget.h @@ -0,0 +1,40 @@ +#ifndef WIDGET_H +#define WIDGET_H + +#include + +class CdcUpdater; +class QSerialPortInfo; +class QLineEdit; +class QPushButton; +class QProgressBar; +class QLabel; + +class Widget : public QWidget { + Q_OBJECT + +public: + Widget(QWidget *parent = nullptr); + ~Widget(); + void start(); + +protected: + void onCdcDeviceDiscovered(const QSerialPortInfo &info); + void onSelectButtonClicked(); + void onUpdateFinished(); + void dragEnterEvent(QDragEnterEvent *event) final; + void dropEvent(QDropEvent *event) final; + void setControlsEnabled(bool enabled); + void setProgress(int32_t progress); + void setMessage(const QString &message); + +private: + QLineEdit *m_fileEditor = nullptr; + QPushButton *m_selectButton = nullptr; + QPushButton *m_startButton = nullptr; + std::shared_ptr m_updater; + bool m_updating = false; + QProgressBar *m_progressBar = nullptr; + QLabel *m_messageLabel = nullptr; +}; +#endif // WIDGET_H diff --git a/OtaUpdate/main.cpp b/OtaUpdate/main.cpp new file mode 100644 index 0000000..34b9556 --- /dev/null +++ b/OtaUpdate/main.cpp @@ -0,0 +1,20 @@ +#include "BoostLog.h" +#include "Widget.h" +#include +#include + +int main(int argc, char *argv[]) { + boost::log::initialize("logs/app"); + + QApplication a(argc, argv); + QFont font; + font.setPointSize(16); + a.setFont(font); + Widget w; + w.setWindowTitle("L015模组升级工具"); + w.setMinimumWidth(520); + w.setMinimumHeight(100); + w.show(); + + return a.exec(); +} diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..078123a --- /dev/null +++ b/Readme.md @@ -0,0 +1,16 @@ +枚举视频捕获设备:https://learn.microsoft.com/zh-cn/windows/win32/medfound/enumerating-video-capture-devices + +媒体基础中的音频/视频捕获:https://learn.microsoft.com/zh-cn/windows/win32/medfound/audio-video-capture-in-media-foundation,里面有枚举打印示例代码。 + +如何设置视频捕获格式:https://learn.microsoft.com/zh-cn/windows/win32/medfound/how-to-set-the-video-capture-format + + + +``` +./rebuild-app.sh y L015 V200 R002 + +./rebuild-app-ota.sh y L015 V200 R002 11 +``` + + +