#include "Application.h" #include "AsyncEvent.h" #include "BoostLog.h" #include "CategoryLogSinkBackend.h" #include "Configuration.h" #include "Database.h" #include "DateTime.h" #include "DeviceDiscovery.h" #include "ImageDecoder.h" #include "StringUtility.h" #include "VideoFrameProvider.h" #include "VideoPlayer.h" #include #include #include #include #include #include #include #include #include constexpr uint32_t ImageSliceSize = 2048; Application::Application(int &argc, char **argv) : m_app(std::make_shared(argc, argv)) { m_app->setApplicationName(APPLICATION_NAME); m_app->setApplicationVersion(QString("v%1_%2 build: %3 %4").arg(APP_VERSION, GIT_COMMIT_ID, __DATE__, __TIME__)); QFont font; font.setPointSize(16); m_app->setFont(font); m_verifyTimer = new QTimer(this); m_verifyTimer->setSingleShot(true); connect(m_verifyTimer, &QTimer::timeout, this, &Application::onVerifyTimeout); m_database = std::make_shared(); QTimer::singleShot(0, this, [this]() { if (!m_database->open("database.db")) { LOG(error) << "open database failed."; } }); m_videoFrameProvider = new VideoFrameProvider(); qmlRegisterSingletonInstance("Analyser", 1, 0, "App", this); if (!std::filesystem::exists(JpgPath)) { std::filesystem::create_directory(JpgPath); } if (!std::filesystem::exists(YuvPath)) { std::filesystem::create_directory(YuvPath); } } void Application::onNewVerifyResult(uint16_t userid, const QString &username, uint16_t elapsed) { QTimer::singleShot(0, this, [this, userid, username, elapsed]() { emit newStatusTip(Info, QString("%1,识别耗时: %2ms") .arg(userid == ModuleCommunication::InvalidUserId ? "未录入用户" : username) .arg(elapsed)); }); } void Application::initializeLogger() { auto backend = boost::make_shared("GUI", [this](const std::string &log) { Amass::executeAtObjectThread(this, [this, log]() { emit newLog(QString::fromStdString(log)); }); }); using SynchronousCategorySink = boost::log::sinks::synchronous_sink; auto sink = boost::make_shared(backend); boost::log::core::get()->add_sink(sink); } int Application::exec() { QQmlApplicationEngine engine; engine.addImageProvider("videoframe", m_videoFrameProvider); const QUrl url(QStringLiteral("qrc:/Analyser/qml/main.qml")); QObject::connect( &engine, &QQmlApplicationEngine::objectCreationFailed, m_app.get(), []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return m_app->exec(); } QStringList Application::availableSerialPorts() const { QStringList ret; auto ports = QSerialPortInfo::availablePorts(); for (auto &port : ports) { if (port.description() == "蓝牙链接上的标准串行") continue; ret << port.portName(); } return ret; } QStringList Application::availableUsbVideoCameras() const { QStringList ret; DeviceDiscovery d; auto devices = d.devices(); for (auto &device : devices) { ret << QString::fromStdString(device); } return ret; } bool Application::open(const QString &portName, int baudRate) { m_communication = std::make_shared(); connect(m_communication.get(), &ModuleCommunication::commandStarted, this, &Application::onCommandStarted); connect(m_communication.get(), &ModuleCommunication::commandFinished, this, &Application::onCommandFinished); connect(m_communication.get(), &ModuleCommunication::newVerifyResult, this, &Application::onNewVerifyResult); connect(m_communication.get(), &ModuleCommunication::newPalmFeature, this, &Application::onNewPalmFeature); connect(m_communication.get(), &ModuleCommunication::newEnrolledImageInfo, this, &Application::onNewEnrolledImageInfo); connect(m_communication.get(), &ModuleCommunication::newImageSliceData, this, &Application::onNewImageSliceData); connect(m_communication.get(), &ModuleCommunication::errorOccurred, this, &Application::onErrorOccurred); emit connectedChanged(); auto status = m_communication->open(portName, baudRate); emit newStatusTip(status ? Tip : Error, status ? "串口打开成功" : "串口打开失败"); return status; } bool Application::openUVC(const QString &deviceName) { m_videoPlayer = std::make_shared(); m_videoPlayer->setFrameCallback([this](const QImage &image) { Amass::executeAtObjectThread(this, [this, image]() { m_videoFrameProvider->setImage(image); emit newVideoFrame(); }); }); m_videoPlayer->setErrorCallback([this](int error, const std::string &message) { LOG_CAT(info, GUI) << "UVC错误: " << message; Amass::executeAtObjectThread(this, [this, error]() { if (error == -EIO) { closeUVC(); } }); }); bool status = m_videoPlayer->open(deviceName.toStdString()); if (!status) { m_videoPlayer.reset(); } emit uvcOpenedChanged(); emit newStatusTip(status ? Tip : Error, status ? "UVC打开成功" : "UVC打开失败"); return status; } void Application::close() { m_communication.reset(); emit connectedChanged(); m_persistenceModeStarted = false; m_verifyTimer->stop(); emit isVerifyingChanged(); } void Application::closeUVC() { m_videoFrameProvider->reset(); emit newVideoFrame(); m_videoPlayer.reset(); emit uvcOpenedChanged(); } void Application::verify(uint8_t timeout) { if (m_communication->currentMessageId() != ModuleCommunication::Idle) { m_communication->reset(); } m_communication->verify(timeout); } void Application::enroll(const QString &username, bool persistence, uint8_t timeout) { if (m_communication->currentMessageId() != ModuleCommunication::Idle) { m_communication->reset(); } m_communication->enroll(username.toStdString(), persistence, timeout); m_enrollUsername = username; } void Application::deleteUser(uint16_t userid) { if (m_communication->currentMessageId() != ModuleCommunication::Idle) { m_communication->reset(); } m_communication->deleteUser(userid); } void Application::deleteAll() { if (m_communication->currentMessageId() != ModuleCommunication::Idle) { m_communication->reset(); } m_communication->deleteAll(); } void Application::enrollExtended(const QString &username, bool persistence, uint8_t timeout) { if (m_communication->currentMessageId() != ModuleCommunication::Idle) { m_communication->reset(); } m_communication->enrollExtended(username.toStdString(), persistence, timeout); m_enrollUsername = username; } void Application::uploadImage() { m_uploadImageSendedSize = 0; // std::ifstream ifs("palm.yuv", std::ofstream::binary); // m_uploadBuffer = std::vector((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); auto image = ImageDecoder::extractJpegYComponent("E:/Downloads/7207002051849490432.jpg"); if (!image) { LOG(error) << "decode failed."; return; } m_uploadBuffer = image->data; LOG(info) << "width: " << image->width << ", height: " << image->height; ModuleCommunication::UploadImageInformation request; mbedtls_md5_context context; mbedtls_md5_init(&context); mbedtls_md5_starts(&context); mbedtls_md5_update(&context, m_uploadBuffer.data(), m_uploadBuffer.size()); mbedtls_md5_finish(&context, request.md5); mbedtls_md5_free(&context); request.operation = 0; request.width = 600; request.height = 800; request.size = m_uploadBuffer.size(); strncpy(request.username, "下发测试", sizeof(request.username)); m_communication->uploadImageInfo(request); LOG(info) << "upload image, md5: " << ModuleCommunication::protocolDataFormatString(request.md5, sizeof(request.md5)); m_startUploadTime = std::chrono::system_clock::now(); } ModuleCommunication *Application::module() const { return m_communication.get(); } bool Application::connected() const { return static_cast(m_communication); } bool Application::uvcOpened() const { return static_cast(m_videoPlayer); } bool Application::persistenceMode() const { return m_persistenceMode; } void Application::setPersistenceMode(bool enabled) { if (m_persistenceMode != enabled) { m_persistenceMode = enabled; emit persistenceModeChanged(); } } int Application::persistenceVerifyInterval() const { return m_persistenceVerifyInterval; } void Application::setPersistenceVerifyInterval(int interval) { if (m_persistenceVerifyInterval != interval) { m_persistenceVerifyInterval = interval; emit persistenceVerifyIntervalChanged(); } } bool Application::isVerifying() const { if (!m_communication) { return false; } return (m_persistenceMode && m_persistenceModeStarted) || (m_communication->currentMessageId() == ModuleCommunication::Verify); } void Application::onNewPalmFeature(const PalmFeature &feature) { auto palms = m_database->palmFeatures(); if (std::find(palms.cbegin(), palms.cend(), feature) != palms.cend()) { LOG(warning) << "本地数据库已有相同特征数据。"; return; } if (!m_database->addPalmFeature(feature)) { LOG(error) << "add palm feature failed."; } } void Application::onErrorOccurred(const QString &error) { QTimer::singleShot(0, this, [this]() { close(); }); } void Application::onNewEnrolledImageInfo(uint32_t size, const uint8_t *md5) { using namespace std::chrono; m_enrolledImageSize = size; m_communication->requestEnrolledImage(0, ImageSliceSize); m_enrollYImageBuffer.clear(); m_startUploadTime = system_clock::now(); } void Application::onNewImageSliceData(const std::vector &data) { using namespace std::chrono; // LOG(info) << "onNewImageSliceData:" << data.size() << ", already received: " << m_enrollYImageBuffer.size() // << ", total size: " << m_enrolledImageSize; m_enrollYImageBuffer.append(reinterpret_cast(data.data()), data.size()); if (m_enrollYImageBuffer.size() < m_enrolledImageSize) { m_communication->requestEnrolledImage(m_enrollYImageBuffer.size(), ImageSliceSize); } else { auto username = m_enrollUsername.toStdString(); LOG(info) << "request finished, username: " << username << ", elapsed: " << duration_cast(system_clock::now() - m_startUploadTime); std::ostringstream oss; oss << YuvPath << "/" << username << "_" << DateTime::toString(std::chrono::system_clock::now(), "%Y%m%d%H%M%S") << ".yuv"; std::ofstream ofs(Amass::StringUtility::UTF8ToGBK(oss.str()), std::ofstream::binary); ofs.write(m_enrollYImageBuffer.data(), m_enrollYImageBuffer.size()); QImage image(reinterpret_cast(m_enrollYImageBuffer.data()), 600, 800, QImage::Format_Grayscale8); oss.str(""); oss << JpgPath << "/" << username << "_" << DateTime::toString(std::chrono::system_clock::now(), "%Y%m%d%H%M%S") << ".jpg"; image.save(QString::fromStdString(oss.str()), "jpg", 100); } } void Application::onCommandStarted(ModuleCommunication::MessageId messageId) { using namespace std::chrono; emit isVerifyingChanged(); } void Application::onCommandFinished(ModuleCommunication::MessageId messageId, ModuleCommunication::MessageStatus status) { // LOG(info) << m_persistenceMode << " " << m_persistenceModeStarted << " " << m_persistenceVerifyInterval; using namespace std::chrono; if (messageId == ModuleCommunication::UploadImageInfo) { m_communication->uploadImageData(m_uploadImageSendedSize, m_uploadBuffer.data() + m_uploadImageSendedSize, ImageSliceSize); } else if (messageId == ModuleCommunication::UploadImageData) { m_uploadImageSendedSize += ImageSliceSize; if (m_uploadImageSendedSize < m_uploadBuffer.size()) { auto remainSize = m_uploadBuffer.size() - m_uploadImageSendedSize; m_communication->uploadImageData(m_uploadImageSendedSize, (const uint8_t *)m_uploadBuffer.data() + m_uploadImageSendedSize, remainSize < ImageSliceSize ? remainSize : ImageSliceSize); } else { LOG(info) << "upload finished, elapsed: " << duration_cast(system_clock::now() - m_startUploadTime); } } if (messageId == ModuleCommunication::Verify && m_persistenceMode) { // 持续识别逻辑 m_persistenceModeStarted = true; } else if (messageId == ModuleCommunication::Reset) { m_persistenceModeStarted = false; m_verifyTimer->stop(); } if (m_persistenceMode && m_persistenceModeStarted && messageId == ModuleCommunication::Verify && ((status == ModuleCommunication::Success) || (status == ModuleCommunication::Failed4UnknownUser) || (status == ModuleCommunication::Failed4Timeout) || (status == ModuleCommunication::Failed4UnknownReason))) { m_verifyTimer->start(m_persistenceVerifyInterval * 1000); } if (((messageId == ModuleCommunication::EnrollSingle) || (messageId == ModuleCommunication::EnrollExtended)) && (status == ModuleCommunication::Success)) { emit newStatusTip(Info, "录入成功。"); } emit isVerifyingChanged(); } void Application::onVerifyTimeout() { m_communication->verify(120); }