#include "Application.h" #include "AsyncEvent.h" #include "BoostLog.h" #include "CategoryLogSinkBackend.h" #include "Database.h" #include "DeviceDiscovery.h" #include "VideoFrameProvider.h" #include "VideoPlayer.h" #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)) { 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); } void Application::onNewVerifyResult(uint16_t userid, const QString &username) { QTimer::singleShot(0, this, [this, userid, username]() { emit newStatusTip(Info, QString("%1,识别耗时: %2ms") .arg(userid == ModuleCommunication::InvalidUserId ? "未录入用户" : username) .arg(m_verifyElapsed.count())); }); } 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, uint8_t timeout) { if (m_communication->currentMessageId() != ModuleCommunication::Idle) { m_communication->reset(); } m_communication->enroll(username.toStdString(), timeout); } 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::getEnrolledImage(const QString &username, uint8_t timeout) { if (m_communication->currentMessageId() != ModuleCommunication::Idle) { m_communication->reset(); } m_communication->enrollEx(username.toStdString(), timeout); } 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()); ModuleCommunication::UploadImageInformation request; mbedtls_md5_context context; mbedtls_md5_init(&context); mbedtls_md5_starts(&context); mbedtls_md5_update(&context, reinterpret_cast(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 { LOG(info) << "request finished, elapsed: " << duration_cast(system_clock::now() - m_startUploadTime); QImage image(reinterpret_cast(m_enrollYImageBuffer.data()), 600, 800, QImage::Format_Grayscale8); image.save("test.jpg"); std::ofstream ofs("palm.yuv", std::ofstream::binary); ofs.write(m_enrollYImageBuffer.data(), m_enrollYImageBuffer.size()); } } void Application::onCommandStarted(ModuleCommunication::MessageId messageId) { using namespace std::chrono; if (messageId == ModuleCommunication::Verify) { m_verifyStartTime = system_clock::now(); } 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::Verify) { m_verifyElapsed = duration_cast(system_clock::now() - m_verifyStartTime); LOG(info) << "verify elapsed " << m_verifyElapsed; if (m_verifyElapsed > hours(10 * 1000)) { m_verifyElapsed = milliseconds(100); } } if (messageId == ModuleCommunication::UploadImageInfo) { m_communication->uploadImageData( m_uploadImageSendedSize, (const uint8_t *)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) && (status == ModuleCommunication::Success)) { emit newStatusTip(Info, "录入成功。"); } emit isVerifyingChanged(); } void Application::onVerifyTimeout() { m_communication->verify(120); }