1.添加uvc视频流,

This commit is contained in:
luocai 2024-06-13 15:41:40 +08:00
parent 4febb5fb7f
commit 4724af4086
28 changed files with 1192 additions and 136 deletions

296
Analyser/Application.cpp Normal file
View File

@ -0,0 +1,296 @@
#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 <QApplication>
#include <QFont>
#include <QImage>
#include <QQmlApplicationEngine>
#include <QSerialPortInfo>
#include <QTimer>
Application::Application(int &argc, char **argv) : m_app(std::make_shared<QApplication>(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<Database>();
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<CategoryLogSinkBackend>("GUI", [this](const std::string &log) {
Amass::executeAtObjectThread(this, [this, log]() { emit newLog(QString::fromStdString(log)); });
});
using SynchronousCategorySink = boost::log::sinks::synchronous_sink<CategoryLogSinkBackend>;
auto sink = boost::make_shared<SynchronousCategorySink>(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<ModuleCommunication>();
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<VideoPlayer>();
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, 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();
}
ModuleCommunication *Application::module() const {
return m_communication.get();
}
bool Application::connected() const {
return static_cast<bool>(m_communication);
}
bool Application::uvcOpened() const {
return static_cast<bool>(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, 1024);
m_enrollYImageBuffer.clear();
m_startUploadTime = system_clock::now();
}
void Application::onNewImageSliceData(const std::vector<uint8_t> &data) {
using namespace std::chrono;
// LOG(info) << "onNewImageSliceData:" << data.size() << ", already received: " << m_enrollYImageBuffer.size()
// << ", total size: " << m_enrolledImageSize;
m_enrollYImageBuffer.append(reinterpret_cast<const char *>(data.data()), data.size());
if (m_enrollYImageBuffer.size() < m_enrolledImageSize) {
m_communication->requestEnrolledImage(m_enrollYImageBuffer.size(), 1024);
} else {
LOG(info) << "request finished, elapsed: "
<< duration_cast<milliseconds>(system_clock::now() - m_startUploadTime);
QImage image(reinterpret_cast<const uint8_t *>(m_enrollYImageBuffer.data()), 600, 800,
QImage::Format_Grayscale8);
image.save("test.jpg");
}
}
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<milliseconds>(system_clock::now() - m_verifyStartTime);
LOG(info) << "verify elapsed " << m_verifyElapsed;
if (m_verifyElapsed > hours(10 * 1000)) {
m_verifyElapsed = milliseconds(100);
}
}
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);
}

100
Analyser/Application.h Normal file
View File

@ -0,0 +1,100 @@
#ifndef __APPLICATION_H__
#define __APPLICATION_H__
#include "DataStructure.h"
#include "ModuleCommunication.h"
#include "Singleton.h"
#include <QObject>
#include <QQmlEngine>
#include <memory>
class QGuiApplication;
class Database;
class VideoPlayer;
class VideoFrameProvider;
class QTimer;
class Application : public QObject {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(ModuleCommunication *module READ module CONSTANT)
Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
Q_PROPERTY(bool uvcOpened READ uvcOpened NOTIFY uvcOpenedChanged)
Q_PROPERTY(bool persistenceMode READ persistenceMode WRITE setPersistenceMode NOTIFY persistenceModeChanged)
Q_PROPERTY(int persistenceVerifyInterval READ persistenceVerifyInterval WRITE setPersistenceVerifyInterval NOTIFY
persistenceVerifyIntervalChanged)
Q_PROPERTY(bool isVerifying READ isVerifying NOTIFY isVerifyingChanged)
friend class Amass::Singleton<Application>;
public:
enum TipType {
Tip,
Error,
Info,
};
Q_ENUM(TipType)
void initializeLogger();
int exec();
Q_INVOKABLE QStringList availableSerialPorts() const;
Q_INVOKABLE QStringList availableUsbVideoCameras() const;
Q_INVOKABLE bool open(const QString &portName, int baudRate);
Q_INVOKABLE bool openUVC(const QString &deviceName);
Q_INVOKABLE void close();
Q_INVOKABLE void closeUVC();
Q_INVOKABLE void verify(uint8_t timeout);
Q_INVOKABLE void enroll(const QString &username, uint8_t timeout);
Q_INVOKABLE void deleteUser(uint16_t userid);
Q_INVOKABLE void deleteAll();
ModuleCommunication *module() const;
bool connected() const;
bool uvcOpened() const;
bool persistenceMode() const;
void setPersistenceMode(bool enabled);
int persistenceVerifyInterval() const;
void setPersistenceVerifyInterval(int interval);
bool isVerifying() const;
signals:
void connectedChanged();
void persistenceModeChanged();
void persistenceVerifyIntervalChanged();
void isVerifyingChanged();
void uvcOpenedChanged();
void newVideoFrame();
void newLog(const QString &log);
void newStatusTip(TipType type, const QString &tip);
protected:
Application(int &argc, char **argv);
void onNewVerifyResult(uint16_t userid, const QString &username);
void onNewPalmFeature(const PalmFeature &feature);
void onErrorOccurred(const QString &error);
void onNewEnrolledImageInfo(uint32_t size, const uint8_t *md5);
void onNewImageSliceData(const std::vector<uint8_t> &data);
void onCommandStarted(ModuleCommunication::MessageId messageId);
void onCommandFinished(ModuleCommunication::MessageId messageId, ModuleCommunication::MessageStatus status);
void onVerifyTimeout();
private:
std::shared_ptr<QGuiApplication> m_app;
std::shared_ptr<ModuleCommunication> m_communication;
std::shared_ptr<Database> m_database;
bool m_persistenceMode = true; // 模组持续识别
bool m_persistenceModeStarted = false;
int m_persistenceVerifyInterval = 1;
QTimer *m_verifyTimer = nullptr;
std::chrono::system_clock::time_point m_verifyStartTime;
std::chrono::milliseconds m_verifyElapsed;
uint32_t m_enrolledImageSize = 0;
QByteArray m_enrollYImageBuffer;
std::chrono::system_clock::time_point m_startUploadTime;
std::shared_ptr<VideoPlayer> m_videoPlayer;
VideoFrameProvider *m_videoFrameProvider;
};
#endif // __APPLICATION_H__

View File

@ -1,15 +1,49 @@
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON) set(CMAKE_AUTORCC ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets SerialPort) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Quick QuickTemplates2 Widgets SerialPort)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets SerialPort) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Quick QuickTemplates2 Widgets SerialPort)
add_executable(Analyser Analyser.rc add_executable(Analyser Analyser.rc
main.cpp main.cpp
Application.h Application.cpp
CategoryLogSinkBackend.h CategoryLogSinkBackend.cpp CategoryLogSinkBackend.h CategoryLogSinkBackend.cpp
Widget.h Widget.cpp Widget.h Widget.cpp
ModuleCommunication.h ModuleCommunication.cpp ModuleCommunication.h ModuleCommunication.cpp
PalmFeatureTableModel.h PalmFeatureTableModel.cpp PalmFeatureTableModel.h PalmFeatureTableModel.cpp
VideoFrameProvider.h VideoFrameProvider.cpp
VideoPlayer.h VideoPlayer.cpp
)
qt_add_qml_module(Analyser
URI Analyser
VERSION 1.0
QML_FILES
qml/main.qml
qml/ConnectionItem.qml
qml/OperationItem.qml
qml/StatusTip.qml
RESOURCES
resources/successfull.svg
resources/warning.svg
)
target_compile_definitions(Analyser
PRIVATE _CRT_SECURE_NO_WARNINGS
)
if(CMAKE_BUILD_TYPE STREQUAL "Release")
set_property(TARGET Analyser PROPERTY
WIN32_EXECUTABLE true
)
endif()
target_include_directories(Analyser
PRIVATE ${FFmpeg_INCLUDE_DIR}
)
target_link_directories(Analyser
PRIVATE ${FFmpeg_LIB_DIR}
) )
target_link_libraries(Analyser target_link_libraries(Analyser
@ -17,6 +51,14 @@ target_link_libraries(Analyser
PRIVATE Encrypt PRIVATE Encrypt
PRIVATE Database PRIVATE Database
PRIVATE Ws2_32 PRIVATE Ws2_32
PRIVATE Peripheral
PRIVATE avcodec
PRIVATE swscale
PRIVATE avutil
PRIVATE avdevice
PRIVATE avformat
PRIVATE Qt${QT_VERSION_MAJOR}::Quick
PRIVATE Qt${QT_VERSION_MAJOR}::QuickTemplates2
PRIVATE Qt${QT_VERSION_MAJOR}::Widgets PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
PRIVATE Qt${QT_VERSION_MAJOR}::SerialPort PRIVATE Qt${QT_VERSION_MAJOR}::SerialPort
) )

View File

@ -1,15 +1,13 @@
#include "CategoryLogSinkBackend.h" #include "CategoryLogSinkBackend.h"
#include "AsyncEvent.h" #include "BoostLog.h"
#include <QTextBrowser>
#include <iostream>
CategoryLogSinkBackend::CategoryLogSinkBackend(const std::string &category, QTextBrowser *target) CategoryLogSinkBackend::CategoryLogSinkBackend(const std::string &category, Append &&append)
: m_category(category), m_target(target) { : m_category(category), m_append(std::move(append)) {
} }
void CategoryLogSinkBackend::consume(const boost::log::record_view &record, string_type const &output) { void CategoryLogSinkBackend::consume(const boost::log::record_view &record, string_type const &output) {
auto &&category = record[AmassKeywords::category]; auto &&category = record[AmassKeywords::category];
if (category == m_category) { if ((category == m_category) && m_append) {
Amass::executeAtObjectThread(m_target, [this, output]() { m_target->append(QString::fromStdString(output)); }); m_append(output);
} }
} }

View File

@ -2,19 +2,19 @@
#define __CATEGORYLOGSINKBACKEND_H__ #define __CATEGORYLOGSINKBACKEND_H__
#include <boost/log/sinks.hpp> #include <boost/log/sinks.hpp>
#include <functional>
#include <string> #include <string>
class QTextBrowser;
class CategoryLogSinkBackend class CategoryLogSinkBackend
: public boost::log::sinks::basic_formatted_sink_backend<char, boost::log::sinks::synchronized_feeding> { : public boost::log::sinks::basic_formatted_sink_backend<char, boost::log::sinks::synchronized_feeding> {
public: public:
CategoryLogSinkBackend(const std::string &category, QTextBrowser *target); using Append = std::function<void(const std::string &)>;
CategoryLogSinkBackend(const std::string &category, Append &&append);
void consume(const boost::log::record_view &record, string_type const &output); void consume(const boost::log::record_view &record, string_type const &output);
private: private:
std::string m_category; std::string m_category;
QTextBrowser *m_target = nullptr; Append m_append;
}; };
#endif // __CATEGORYLOGSINKBACKEND_H__ #endif // __CATEGORYLOGSINKBACKEND_H__

View File

@ -1,6 +1,6 @@
#include "ModuleCommunication.h" #include "ModuleCommunication.h"
#include "BoostLog.h" #include "BoostLog.h"
#include <QSerialPort> #include "StringUtility.h"
#include <WinSock2.h> #include <WinSock2.h>
#include <mbedtls/md5.h> #include <mbedtls/md5.h>
#include <sstream> #include <sstream>
@ -16,14 +16,19 @@ static inline uint8_t xor_checksum_byte(const uint8_t *data, uint32_t len) {
ModuleCommunication::ModuleCommunication(QObject *parent) : QObject{parent} { ModuleCommunication::ModuleCommunication(QObject *parent) : QObject{parent} {
} }
bool ModuleCommunication::open(const QString &portName) { bool ModuleCommunication::open(const QString &portName, int baudRate) {
bool ret = true; bool ret = true;
m_serialPort = std::make_shared<QSerialPort>(portName); m_serialPort = std::make_shared<QSerialPort>(portName);
m_serialPort->setBaudRate(QSerialPort::Baud115200); m_serialPort->setBaudRate(baudRate);
connect(m_serialPort.get(), &QSerialPort::readyRead, this, &ModuleCommunication::onReadyRead); connect(m_serialPort.get(), &QSerialPort::readyRead, this, &ModuleCommunication::onReadyRead);
connect(m_serialPort.get(), &QSerialPort::errorOccurred, this, &ModuleCommunication::onErrorOccurred);
ret = m_serialPort->open(QSerialPort::ReadWrite); ret = m_serialPort->open(QSerialPort::ReadWrite);
LOG_CAT(info, GUI) << "打开串口(" << portName.toStdString() << ")" << (ret ? "成功" : "失败") << ""; LOG_CAT(info, GUI) << "打开串口(" << portName.toStdString() << ")" << (ret ? "成功" : "失败")
<< ", 波特率: " << baudRate;
if (!ret) {
LOG(error) << m_serialPort->errorString().toStdString();
}
LOG_CAT(info, GUI) << Separator; LOG_CAT(info, GUI) << Separator;
return ret; return ret;
} }
@ -34,6 +39,8 @@ void ModuleCommunication::verify(uint8_t timeout) {
auto [frameData, frameSize] = generateFrame(Verify, reinterpret_cast<const uint8_t *>(&data), sizeof(data)); auto [frameData, frameSize] = generateFrame(Verify, reinterpret_cast<const uint8_t *>(&data), sizeof(data));
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize); m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
m_currentMessageId = Verify;
emit commandStarted(Verify);
LOG_CAT(info, GUI) << "发送识别指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << "发送识别指令: " << protocolDataFormatString(frameData, frameSize);
LOG_CAT(info, GUI) << Separator; LOG_CAT(info, GUI) << Separator;
@ -52,12 +59,18 @@ void ModuleCommunication::enroll(const std::string &username, uint8_t timeout) {
strncpy(reinterpret_cast<char *>(data.username), username.c_str(), sizeof(data.username)); strncpy(reinterpret_cast<char *>(data.username), username.c_str(), sizeof(data.username));
auto [frameData, frameSize] = generateFrame(EnrollSingle, reinterpret_cast<const uint8_t *>(&data), sizeof(data)); auto [frameData, frameSize] = generateFrame(EnrollSingle, reinterpret_cast<const uint8_t *>(&data), sizeof(data));
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize); m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
m_currentMessageId = EnrollSingle;
emit commandStarted(EnrollSingle);
LOG_CAT(info, GUI) << "发送注册指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << "发送注册指令: " << protocolDataFormatString(frameData, frameSize);
LOG_CAT(info, GUI) << "用户名: " << username << ", 超时时间: " << static_cast<int>(timeout) << "s"; LOG_CAT(info, GUI) << "用户名: " << username << ", 超时时间: " << static_cast<int>(timeout) << "s";
LOG_CAT(info, GUI) << Separator; LOG_CAT(info, GUI) << Separator;
} }
void ModuleCommunication::enroll(const QString &username, uint8_t timeout) {
return enroll(username.toStdString(), timeout);
}
void ModuleCommunication::enrollEx(const std::string &username, uint8_t timeout) { void ModuleCommunication::enrollEx(const std::string &username, uint8_t timeout) {
EnrollData data = {0}; EnrollData data = {0};
data.timeout = timeout; data.timeout = timeout;
@ -74,6 +87,8 @@ void ModuleCommunication::deleteUser(uint16_t userid) {
uint16_t n = htons(userid); uint16_t n = htons(userid);
auto [frameData, frameSize] = generateFrame(DeleteUser, reinterpret_cast<const uint8_t *>(&n), sizeof(n)); auto [frameData, frameSize] = generateFrame(DeleteUser, reinterpret_cast<const uint8_t *>(&n), sizeof(n));
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize); m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
m_currentMessageId = DeleteUser;
emit commandStarted(DeleteUser);
LOG_CAT(info, GUI) << "发送删除用户指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << "发送删除用户指令: " << protocolDataFormatString(frameData, frameSize);
LOG_CAT(info, GUI) << "删除用户ID: " << userid; LOG_CAT(info, GUI) << "删除用户ID: " << userid;
LOG_CAT(info, GUI) << Separator; LOG_CAT(info, GUI) << Separator;
@ -82,6 +97,8 @@ void ModuleCommunication::deleteUser(uint16_t userid) {
void ModuleCommunication::deleteAll() { void ModuleCommunication::deleteAll() {
auto [frameData, frameSize] = generateFrame(DeleteAll); auto [frameData, frameSize] = generateFrame(DeleteAll);
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize); m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
m_currentMessageId = DeleteAll;
emit commandStarted(DeleteAll);
LOG_CAT(info, GUI) << "发送删除所有指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << "发送删除所有指令: " << protocolDataFormatString(frameData, frameSize);
LOG_CAT(info, GUI) << Separator; LOG_CAT(info, GUI) << Separator;
} }
@ -110,7 +127,7 @@ void ModuleCommunication::enrollPalmFeature(uint16_t userid, const PalmFeature &
auto buffer = new uint8_t[sizeof(PalmFeatureHeader) + feature.feature.size()]; auto buffer = new uint8_t[sizeof(PalmFeatureHeader) + feature.feature.size()];
auto header = reinterpret_cast<PalmFeatureHeader *>(buffer); auto header = reinterpret_cast<PalmFeatureHeader *>(buffer);
header->userid = htons(userid); header->userid = htons(userid);
header->featureTotalSize = htons(feature.feature.size()); header->featureTotalSize = htons(static_cast<uint16_t>(feature.feature.size()));
strncpy(reinterpret_cast<char *>(header->username), feature.username.c_str(), sizeof(header->username)); strncpy(reinterpret_cast<char *>(header->username), feature.username.c_str(), sizeof(header->username));
mbedtls_md5_context context; mbedtls_md5_context context;
@ -124,8 +141,8 @@ void ModuleCommunication::enrollPalmFeature(uint16_t userid, const PalmFeature &
memcpy(buffer + sizeof(PalmFeatureHeader), feature.feature.data(), feature.feature.size()); memcpy(buffer + sizeof(PalmFeatureHeader), feature.feature.data(), feature.feature.size());
auto [frameData, frameSize] = auto [frameData, frameSize] = generateFrame(
generateFrame(RegisterPalmFeature, buffer, sizeof(PalmFeatureHeader) + feature.feature.size()); RegisterPalmFeature, buffer, static_cast<uint16_t>(sizeof(PalmFeatureHeader) + feature.feature.size()));
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize); m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
LOG_CAT(info, GUI) << "发送注册掌静脉特征指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << "发送注册掌静脉特征指令: " << protocolDataFormatString(frameData, frameSize);
LOG_CAT(info, GUI) << Separator; LOG_CAT(info, GUI) << Separator;
@ -133,6 +150,10 @@ void ModuleCommunication::enrollPalmFeature(uint16_t userid, const PalmFeature &
if (buffer != nullptr) delete[] buffer; if (buffer != nullptr) delete[] buffer;
} }
ModuleCommunication::MessageId ModuleCommunication::currentMessageId() const {
return m_currentMessageId;
}
void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) { void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
uint8_t messageId = data[2]; uint8_t messageId = data[2];
switch (messageId) { switch (messageId) {
@ -152,13 +173,22 @@ void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size); LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
if (result == Success) { if (result == Success) {
auto info = reinterpret_cast<const VerifyDataReply *>(data + 7); auto info = reinterpret_cast<const VerifyDataReply *>(data + 7);
LOG_CAT(info, GUI) << "用户ID: " << ntohs(info->userid) uint16_t userid = ntohs(info->userid);
LOG_CAT(info, GUI) << "用户ID: " << userid
<< ", 用户名: " << std::string_view(reinterpret_cast<const char *>(info->username)); << ", 用户名: " << std::string_view(reinterpret_cast<const char *>(info->username));
emit newVerifyResult(userid, reinterpret_cast<const char *>(info->username));
} else if (result == Failed4Timeout) { } else if (result == Failed4Timeout) {
LOG_CAT(info, GUI) << "识别超时。"; LOG_CAT(info, GUI) << "识别超时。";
} else if (result == Failed4UnknownReason) { } else if (result == Rejected) {
LOG_CAT(info, GUI) << "模组拒绝该命令。";
} else if (result == Failed4UnknownUser) {
LOG_CAT(info, GUI) << "未录入用户。"; LOG_CAT(info, GUI) << "未录入用户。";
emit newVerifyResult(InvalidUserId, "");
} else if (result == Failed4UnknownReason) {
LOG_CAT(info, GUI) << "未知错误。";
emit newVerifyResult(InvalidUserId, "");
} else { } else {
LOG_CAT(info, GUI) << "未知错误(" << static_cast<int>(result) << ")。";
} }
LOG_CAT(info, GUI) << Separator; LOG_CAT(info, GUI) << Separator;
break; break;
@ -178,11 +208,10 @@ void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size); LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
if (result == Success) { if (result == Success) {
auto info = reinterpret_cast<const EnrolledImageReply *>(data + 7); auto info = reinterpret_cast<const EnrolledImageReply *>(data + 7);
uint16_t lineSize = ntohs(info->lineSize); uint16_t width = ntohs(info->width);
uint16_t height = ntohs(info->height); uint16_t height = ntohs(info->height);
LOG_CAT(info, GUI) << "图片大小: " << ntohs(info->width) << "x" << height LOG_CAT(info, GUI) << "图片大小: " << width << "x" << height;
<< ", line size: " << lineSize; emit newEnrolledImageInfo(width * height, info->md5);
emit newEnrolledImageInfo(lineSize * height, info->md5);
} }
LOG_CAT(info, GUI) << Separator; LOG_CAT(info, GUI) << Separator;
break; break;
@ -244,6 +273,8 @@ void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
<< ", data: " << protocolDataFormatString(data, size); << ", data: " << protocolDataFormatString(data, size);
break; break;
} }
m_currentMessageId = Idle;
emit commandFinished(static_cast<MessageId>(replyId), static_cast<MessageStatus>(result));
break; break;
} }
case Note: { case Note: {
@ -256,8 +287,6 @@ void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
break; break;
} }
case PalmState: { // 模组返回的数据为当前帧的手掌状态 case PalmState: { // 模组返回的数据为当前帧的手掌状态
auto info = reinterpret_cast<const VerifyNoteInfo *>(data + 7);
LOG(info) << info->state;
break; break;
} }
case UnknownError: { case UnknownError: {
@ -282,6 +311,7 @@ void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
} }
void ModuleCommunication::onReadyRead() { void ModuleCommunication::onReadyRead() {
// LOG(info) << "ModuleCommunication::onReadyRead";
m_receivedBuffer.append(m_serialPort->readAll()); m_receivedBuffer.append(m_serialPort->readAll());
while (m_receivedBuffer.size() >= 2) { while (m_receivedBuffer.size() >= 2) {
int beginIndex = -1; int beginIndex = -1;
@ -299,6 +329,7 @@ void ModuleCommunication::onReadyRead() {
m_receivedBuffer.remove(0, beginIndex); m_receivedBuffer.remove(0, beginIndex);
beginIndex = 0; beginIndex = 0;
} }
if (m_receivedBuffer.size() < 5) break; if (m_receivedBuffer.size() < 5) break;
uint16_t packageSize = *reinterpret_cast<uint16_t *>(m_receivedBuffer.data() + 3); uint16_t packageSize = *reinterpret_cast<uint16_t *>(m_receivedBuffer.data() + 3);
packageSize = ntohs(packageSize); packageSize = ntohs(packageSize);
@ -318,6 +349,12 @@ void ModuleCommunication::onReadyRead() {
} }
} }
void ModuleCommunication::onErrorOccurred(QSerialPort::SerialPortError error) {
if (error == QSerialPort::NoError) return;
LOG_CAT(info, GUI) << m_serialPort->portName().toStdString() << ": " << m_serialPort->errorString().toStdString();
emit errorOccurred(m_serialPort->errorString());
}
std::pair<uint8_t *, uint32_t> ModuleCommunication::generateFrame(MessageId command, const uint8_t *data, std::pair<uint8_t *, uint32_t> ModuleCommunication::generateFrame(MessageId command, const uint8_t *data,
uint16_t size) { uint16_t size) {
static uint8_t sendBuffer[1024] = {0}; static uint8_t sendBuffer[1024] = {0};

View File

@ -3,16 +3,16 @@
#include "DataStructure.h" #include "DataStructure.h"
#include <QObject> #include <QObject>
#include <QSerialPort>
#include <memory> #include <memory>
class QSerialPort;
class ModuleCommunication : public QObject { class ModuleCommunication : public QObject {
Q_OBJECT Q_OBJECT
static constexpr uint32_t UsernameSize = 32; static constexpr uint32_t UsernameSize = 32;
static constexpr const char *Separator = "----------"; static constexpr const char *Separator = "----------";
public: public:
constexpr static uint16_t InvalidUserId = std::numeric_limits<uint16_t>::max();
enum MessageId : uint8_t { enum MessageId : uint8_t {
Reply = 0, Reply = 0,
Note = 0x01, Note = 0x01,
@ -25,6 +25,7 @@ public:
DeleteAll = 0x21, DeleteAll = 0x21,
RegisterPalmFeature = 0xF9, RegisterPalmFeature = 0xF9,
RequestPalmFeature = 0xFA, RequestPalmFeature = 0xFA,
Idle = 0xFF,
}; };
enum NoteId : uint8_t { enum NoteId : uint8_t {
@ -83,7 +84,6 @@ public:
struct EnrolledImageReply { struct EnrolledImageReply {
uint16_t width; uint16_t width;
uint16_t lineSize;
uint16_t height; uint16_t height;
uint8_t md5[16]; uint8_t md5[16];
}; };
@ -115,32 +115,44 @@ public:
#pragma pack() #pragma pack()
explicit ModuleCommunication(QObject *parent = nullptr); explicit ModuleCommunication(QObject *parent = nullptr);
bool open(const QString &portName); bool open(const QString &portName, int baudRate);
void verify(uint8_t timeout); Q_INVOKABLE void verify(uint8_t timeout);
void reset(); Q_INVOKABLE void reset();
void enroll(const std::string &username, uint8_t timeout); void enroll(const std::string &username, uint8_t timeout);
Q_INVOKABLE void enroll(const QString &username, uint8_t timeout);
void enrollEx(const std::string &username, uint8_t timeout); void enrollEx(const std::string &username, uint8_t timeout);
void deleteUser(uint16_t userid); Q_INVOKABLE void deleteUser(uint16_t userid);
void deleteAll(); Q_INVOKABLE void deleteAll();
void requestEnrolledImage(uint32_t offset, uint32_t size); void requestEnrolledImage(uint32_t offset, uint32_t size);
void requestPalmFeature(uint16_t userid); void requestPalmFeature(uint16_t userid);
void enrollPalmFeature(uint16_t userid, const PalmFeature &feature); void enrollPalmFeature(uint16_t userid, const PalmFeature &feature);
MessageId currentMessageId() const;
signals: signals:
void newVerifyResult(uint16_t userid, const QString &username);
void newPalmFeature(const PalmFeature &feature); void newPalmFeature(const PalmFeature &feature);
void newEnrolledImageInfo(uint32_t size, const uint8_t *md5); void newEnrolledImageInfo(uint32_t size, const uint8_t *md5);
void newImageSliceData(const std::vector<uint8_t> &data); void newImageSliceData(const std::vector<uint8_t> &data);
void errorOccurred(const QString &error);
void commandStarted(ModuleCommunication::MessageId messageId);
void commandFinished(MessageId messageId, MessageStatus status);
protected: protected:
void processPackage(const uint8_t *data, uint16_t size); void processPackage(const uint8_t *data, uint16_t size);
void onReadyRead(); void onReadyRead();
void onErrorOccurred(QSerialPort::SerialPortError error);
std::pair<uint8_t *, uint32_t> generateFrame(MessageId command, const uint8_t *data = nullptr, uint16_t size = 0); std::pair<uint8_t *, uint32_t> generateFrame(MessageId command, const uint8_t *data = nullptr, uint16_t size = 0);
std::string protocolDataFormatString(const uint8_t *data, int size); std::string protocolDataFormatString(const uint8_t *data, int size);
private: private:
std::shared_ptr<QSerialPort> m_serialPort; std::shared_ptr<QSerialPort> m_serialPort;
QByteArray m_receivedBuffer; QByteArray m_receivedBuffer;
MessageId m_currentMessageId = ModuleCommunication::Idle;
}; };
#endif // MODULECOMMUNICATION_H #endif // MODULECOMMUNICATION_H

View File

@ -0,0 +1,26 @@
#include "VideoFrameProvider.h"
VideoFrameProvider::VideoFrameProvider()
: QQuickImageProvider(QQuickImageProvider::Image), m_image(800, 600, QImage::Format_RGB32) {
m_image.fill(Qt::black);
}
QImage VideoFrameProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) {
Q_UNUSED(id);
if (size) *size = m_image.size();
if (requestedSize.width() > 0 && requestedSize.height() > 0)
return m_image.scaled(requestedSize.width(), requestedSize.height(), Qt::KeepAspectRatio);
return m_image;
}
void VideoFrameProvider::setImage(const QImage &image) {
m_image = image;
}
void VideoFrameProvider::reset() {
m_image = QImage(800, 600, QImage::Format_RGB32);
m_image.fill(Qt::black);
}

View File

@ -0,0 +1,16 @@
#ifndef __VIDEOFRAMEPROVIDER_H__
#define __VIDEOFRAMEPROVIDER_H__
#include <QQuickImageProvider>
class VideoFrameProvider : public QQuickImageProvider {
public:
VideoFrameProvider();
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) final;
void setImage(const QImage &image);
void reset();
private:
QImage m_image;
};
#endif // __VIDEOFRAMEPROVIDER_H__

91
Analyser/VideoPlayer.cpp Normal file
View File

@ -0,0 +1,91 @@
#include "VideoPlayer.h"
#include "BoostLog.h"
#include <QImage>
extern "C" {
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
}
VideoPlayer::VideoPlayer() {
static bool init = false;
if (!init) {
avdevice_register_all();
init = true;
}
}
void VideoPlayer::setFrameCallback(FrameCallback &&callback) {
m_callback = std::move(callback);
}
void VideoPlayer::setErrorCallback(ErrorCallback &&callback) {
m_errorCallback = std::move(callback);
}
VideoPlayer::~VideoPlayer() {
close();
}
bool VideoPlayer::open(const std::string &deviceName) {
bool ret = true;
m_formatContext = avformat_alloc_context();
auto format = av_find_input_format("dshow");
AVDictionary *dictionary = nullptr;
// ffmpeg -f dshow -list_options true -i video="UVC Camera"
av_dict_set(&dictionary, "video_size", "800*600", 0);
std::ostringstream oss;
oss << "video=" << deviceName;
auto device = oss.str();
int status = avformat_open_input(&m_formatContext, "video=UVC Camera", format, &dictionary);
if (status != 0) {
char message[256] = {0};
av_make_error_string(message, sizeof(message), status);
LOG(error) << "open device[" << device << "] failed: " << status << "," << message;
ret = false;
} else {
m_exit = false;
m_thread = std::thread(&VideoPlayer::run, this);
}
av_dict_free(&dictionary);
return ret;
}
void VideoPlayer::close() {
m_exit = true;
if (m_thread.joinable()) {
m_thread.join();
}
avformat_close_input(&m_formatContext);
avformat_free_context(m_formatContext);
m_formatContext = nullptr;
}
void VideoPlayer::run() {
auto packet = av_packet_alloc();
while (!m_exit) {
auto status = av_read_frame(m_formatContext, packet);
if (status == 0) {
QImage image;
image.loadFromData(packet->data, packet->size);
image.mirror(false, true);
if (!image.isNull() && m_callback) m_callback(image);
} else {
char message[256] = {0};
av_make_error_string(message, sizeof(message), status);
if (m_errorCallback) m_errorCallback(status, message);
LOG(error) << "av_read_frame() failed: " << status << "," << message;
if (status == -EIO) {
break;
}
}
av_packet_unref(packet);
}
av_packet_free(&packet);
QImage image(800, 600, QImage::Format_RGB888);
image.fill(Qt::black);
if (m_callback) m_callback(image);
}

34
Analyser/VideoPlayer.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef __VIDEOPLAYER_H__
#define __VIDEOPLAYER_H__
#include <functional>
#include <string>
#include <thread>
class QImage;
struct AVFormatContext;
class VideoPlayer {
public:
using FrameCallback = std::function<void(const QImage &image)>;
using ErrorCallback = std::function<void(int error, const std::string &message)>;
VideoPlayer();
void setFrameCallback(FrameCallback &&callback);
void setErrorCallback(ErrorCallback &&callback);
~VideoPlayer();
bool open(const std::string &deviceName);
void close();
protected:
void run();
private:
AVFormatContext *m_formatContext = nullptr;
bool m_exit = true;
std::thread m_thread;
FrameCallback m_callback;
ErrorCallback m_errorCallback;
};
#endif // __VIDEOPLAYER_H__

View File

@ -1,4 +1,5 @@
#include "Widget.h" #include "Widget.h"
#include "Application.h"
#include "BoostLog.h" #include "BoostLog.h"
#include "CategoryLogSinkBackend.h" #include "CategoryLogSinkBackend.h"
#include "Database.h" #include "Database.h"
@ -72,26 +73,15 @@ Widget::Widget(QWidget *parent) : QWidget{parent} {
layout->addLayout(operatorLayout, 1); layout->addLayout(operatorLayout, 1);
layout->addWidget(tabWidget, 3); layout->addWidget(tabWidget, 3);
m_database = std::make_shared<Database>();
m_featureModel = new PalmFeatureTableModel(this); m_featureModel = new PalmFeatureTableModel(this);
m_featureTableView->setModel(m_featureModel); m_featureTableView->setModel(m_featureModel);
QTimer::singleShot(0, this, [this]() { QTimer::singleShot(0, this, [this]() {
onSerialRefreshButtonClicked(); onSerialRefreshButtonClicked();
m_commandGroupBox->setEnabled(false); m_commandGroupBox->setEnabled(false);
if (!m_database->open("database.db")) {
LOG(error) << "open database failed.";
}
}); });
} }
void Widget::initializeLogger() {
auto backend = boost::make_shared<CategoryLogSinkBackend>("GUI", m_logBrowser);
using SynchronousCategorySink = boost::log::sinks::synchronous_sink<CategoryLogSinkBackend>;
auto sink = boost::make_shared<SynchronousCategorySink>(backend);
// sink->set_formatter(&zlogFormatter);
boost::log::core::get()->add_sink(sink);
}
QGroupBox *Widget::initializeCommandGroupBox() { QGroupBox *Widget::initializeCommandGroupBox() {
auto ret = new QGroupBox("命令"); auto ret = new QGroupBox("命令");
@ -204,55 +194,19 @@ QGroupBox *Widget::initializeUvcGroupBox() {
return ret; return ret;
} }
void Widget::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 Widget::onNewEnrolledImageInfo(uint32_t size, const uint8_t *md5) {
m_enrolledImageSize = size;
m_receivedImageSize = 0;
m_communication->requestEnrolledImage(0, 1024);
m_ofs = std::make_shared<std::ofstream>("palm.yuv", std::ofstream::binary);
}
void Widget::onNewImageSliceData(const std::vector<uint8_t> &data) {
m_receivedImageSize += data.size();
LOG(info) << "onNewImageSliceData:" << data.size() << ", already received: " << m_receivedImageSize
<< ", total size: " << m_enrolledImageSize;
m_ofs->write(reinterpret_cast<const char *>(data.data()), data.size());
if (m_receivedImageSize < m_enrolledImageSize) {
m_communication->requestEnrolledImage(m_receivedImageSize, 1024);
} else {
LOG(info) << "request finished.";
m_ofs.reset();
}
}
void Widget::onSerialConnectButtonClicked() { void Widget::onSerialConnectButtonClicked() {
auto button = dynamic_cast<QPushButton *>(sender()); auto button = dynamic_cast<QPushButton *>(sender());
if (button == nullptr) return; if (button == nullptr) return;
auto text = button->text(); auto text = button->text();
if (text == "连接") { if (text == "连接") {
auto portName = m_serialComboBox->currentText(); auto portName = m_serialComboBox->currentText();
m_communication = std::make_shared<ModuleCommunication>(); auto status = Amass::Singleton<Application>::instance()->open(portName, 2000000);
connect(m_communication.get(), &ModuleCommunication::newPalmFeature, this, &Widget::onNewPalmFeature);
connect(m_communication.get(), &ModuleCommunication::newEnrolledImageInfo, this,
&Widget::onNewEnrolledImageInfo);
connect(m_communication.get(), &ModuleCommunication::newImageSliceData, this, &Widget::onNewImageSliceData);
bool status = m_communication->open(portName);
if (status) { if (status) {
m_commandGroupBox->setEnabled(true); m_commandGroupBox->setEnabled(true);
button->setText("关闭"); button->setText("关闭");
} }
} else if (text == "关闭") { } else if (text == "关闭") {
m_communication.reset(); Amass::Singleton<Application>::instance()->close();
m_commandGroupBox->setEnabled(false); m_commandGroupBox->setEnabled(false);
button->setText("连接"); button->setText("连接");
} }
@ -262,6 +216,7 @@ void Widget::onSerialRefreshButtonClicked() {
m_serialComboBox->clear(); m_serialComboBox->clear();
auto ports = QSerialPortInfo::availablePorts(); auto ports = QSerialPortInfo::availablePorts();
for (auto &port : ports) { for (auto &port : ports) {
if (port.description() == "蓝牙链接上的标准串行") continue;
m_serialComboBox->addItem(port.portName()); m_serialComboBox->addItem(port.portName());
} }
} }
@ -270,51 +225,65 @@ void Widget::onUvcRefreshButtonClicked() {
} }
void Widget::onEnrollButtonClicked() { void Widget::onEnrollButtonClicked() {
auto module = Amass::Singleton<Application>::instance()->module();
if (!module) return;
auto name = m_enrollNameEdit->text(); auto name = m_enrollNameEdit->text();
auto timeout = m_enrollTimeoutEdit->text().toInt(); auto timeout = m_enrollTimeoutEdit->text().toInt();
m_communication->enroll(name.toStdString(), timeout); module->enroll(name.toStdString(), timeout);
} }
void Widget::onEnrollExButtonClicked() { void Widget::onEnrollExButtonClicked() {
auto module = Amass::Singleton<Application>::instance()->module();
if (!module) return;
auto name = m_enrollNameEdit->text(); auto name = m_enrollNameEdit->text();
auto timeout = m_enrollTimeoutEdit->text().toInt(); auto timeout = m_enrollTimeoutEdit->text().toInt();
m_communication->enrollEx(name.toStdString(), timeout); module->enrollEx(name.toStdString(), timeout);
} }
void Widget::onVerifyButtonClicked() { void Widget::onVerifyButtonClicked() {
if (!m_communication) return; auto module = Amass::Singleton<Application>::instance()->module();
if (!module) return;
auto timeout = m_verifyTimeoutEdit->text().toInt(); auto timeout = m_verifyTimeoutEdit->text().toInt();
m_communication->verify(timeout); module->verify(timeout);
} }
void Widget::onDeleteAllButtonClicked() { void Widget::onDeleteAllButtonClicked() {
if (!m_communication) return; auto module = Amass::Singleton<Application>::instance()->module();
m_communication->deleteAll(); if (!module) return;
module->deleteAll();
} }
void Widget::onDeleteButtonClicked() { void Widget::onDeleteButtonClicked() {
if (!m_communication) return; auto module = Amass::Singleton<Application>::instance()->module();
if (!module) return;
auto id = m_deleteIdEdit->text().toInt(); auto id = m_deleteIdEdit->text().toInt();
m_communication->deleteUser(id); module->deleteUser(id);
} }
void Widget::onRequestPalmFeatureButtonClicked() { void Widget::onRequestPalmFeatureButtonClicked() {
if (!m_communication) return; auto module = Amass::Singleton<Application>::instance()->module();
if (!module) return;
auto id = m_palmFeatureEdit->text().toInt(); auto id = m_palmFeatureEdit->text().toInt();
m_communication->requestPalmFeature(id); module->requestPalmFeature(id);
} }
void Widget::onRegisterPalmFeatureButtonClicked() { void Widget::onRegisterPalmFeatureButtonClicked() {
if (!m_communication) return; auto module = Amass::Singleton<Application>::instance()->module();
auto features = m_database->palmFeatures(); if (!module) return;
if (features.empty()) { // auto features = m_database->palmFeatures();
LOG(error) << "feature is empty."; // if (features.empty()) {
return; // LOG(error) << "feature is empty.";
} // return;
m_communication->enrollPalmFeature(264, features.at(0)); // }
// module->enrollPalmFeature(264, features.at(0));
} }
void Widget::onResetButtonClicked() { void Widget::onResetButtonClicked() {
if (!m_communication) return; auto module = Amass::Singleton<Application>::instance()->module();
m_communication->reset(); if (!module) return;
module->reset();
}
void Widget::onNewLog(const QString &log) {
m_logBrowser->append(log);
} }

View File

@ -1,7 +1,6 @@
#ifndef WIDGET_H #ifndef WIDGET_H
#define WIDGET_H #define WIDGET_H
#include "DataStructure.h"
#include <QWidget> #include <QWidget>
#include <fstream> #include <fstream>
@ -19,7 +18,7 @@ class Widget : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit Widget(QWidget *parent = nullptr); explicit Widget(QWidget *parent = nullptr);
void initializeLogger(); void onNewLog(const QString &log);
protected: protected:
QGroupBox *initializeCommandGroupBox(); QGroupBox *initializeCommandGroupBox();
@ -45,10 +44,6 @@ protected:
QGroupBox *initializeUvcGroupBox(); QGroupBox *initializeUvcGroupBox();
void onNewPalmFeature(const PalmFeature &feature);
void onNewEnrolledImageInfo(uint32_t size, const uint8_t *md5);
void onNewImageSliceData(const std::vector<uint8_t> &data);
private: private:
QComboBox *m_serialComboBox = nullptr; QComboBox *m_serialComboBox = nullptr;
QPushButton *m_serialConnectButton = nullptr; QPushButton *m_serialConnectButton = nullptr;
@ -68,15 +63,8 @@ private:
QLineEdit *m_palmFeatureEdit = nullptr; QLineEdit *m_palmFeatureEdit = nullptr;
std::shared_ptr<ModuleCommunication> m_communication;
std::shared_ptr<Database> m_database;
PalmFeatureTableModel *m_featureModel = nullptr; PalmFeatureTableModel *m_featureModel = nullptr;
QTableView *m_featureTableView = nullptr; QTableView *m_featureTableView = nullptr;
uint32_t m_enrolledImageSize = 0;
uint32_t m_receivedImageSize = 0;
std::shared_ptr<std::ofstream> m_ofs;
}; };
#endif // WIDGET_H #endif // WIDGET_H

View File

@ -1,21 +1,21 @@
#include "Application.h"
#include "BoostLog.h" #include "BoostLog.h"
#include "DeviceDiscovery.h"
#include "Widget.h" #include "Widget.h"
#include <QApplication>
#include <QFont>
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
using namespace Amass;
boost::log::initialize("logs/app"); boost::log::initialize("logs/app");
QApplication a(argc, argv); auto app = Singleton<Application>::instance<Construct>(argc, argv);
QFont font; app->initializeLogger();
font.setPointSize(16);
a.setFont(font);
Widget w;
w.initializeLogger();
w.setWindowTitle("L015上位机工具");
w.setMinimumWidth(1120);
w.setMinimumHeight(640);
w.show();
return a.exec(); // Widget w;
// w.setWindowTitle("L015上位机工具");
// w.setMinimumWidth(1120);
// w.setMinimumHeight(640);
// w.show();
// QObject::connect(app.get(), &Application::newLog, &w, &Widget::onNewLog);
return app->exec();
} }

View File

@ -0,0 +1,80 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Analyser
RowLayout {
GroupBox {
title: "串口设置"
Layout.fillWidth: true
GridLayout {
columns: 2
Text {
text: qsTr("端口")
}
ComboBox {
id: serialPort
enabled: !App.connected
implicitWidth: 100
}
Text {
text: qsTr("波特率")
}
ComboBox {
id: baudrate
enabled: !App.connected
implicitWidth: 110
model: ["2000000", "115200"]
}
Button {
text: "刷新"
enabled: !App.connected
onClicked: {
serialPort.model = App.availableSerialPorts()
}
}
Button {
text: App.connected ? "断开" : "连接"
onClicked: App.connected ? App.close() : App.open(
serialPort.currentText,
parseInt(baudrate.currentText))
}
}
}
GroupBox {
Layout.fillWidth: true
Layout.fillHeight: true
title: "UVC设置"
GridLayout {
columns: 2
Text {
text: qsTr("设备名")
}
ComboBox {
id: uvcs
enabled: !App.uvcOpened
implicitWidth: 150
}
Button {
enabled: !App.uvcOpened
text: "刷新"
onClicked: uvcs.model = App.availableUsbVideoCameras()
}
Button {
text: App.uvcOpened ? "关闭" : "连接"
onClicked: App.uvcOpened ? App.closeUVC() : App.openUVC(
uvcs.currentText)
}
}
}
Component.onCompleted: {
serialPort.model = App.availableSerialPorts()
uvcs.model = App.availableUsbVideoCameras()
}
}

View File

@ -0,0 +1,110 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Analyser
ColumnLayout {
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"
}
Button {
text: "注册"
onClicked: App.enroll(enrollName.text,
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("识别间隔(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(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()
}
}
}
Button {
text: "复位"
onClicked: App.module.reset()
}
}
}
}

View File

@ -0,0 +1,56 @@
import QtQuick
import QtQuick.Controls
Popup{
id: control
property alias text: textItem.text
property alias icon: image.source
property alias color: back.color
property string borderColor
x: (parent.width-200)/2
y: 40
width: 200
height: 32
font.pixelSize: 16
contentItem: Row{
leftPadding: 4
spacing: 9.6
Image {
id: image
anchors.verticalCenter: parent.verticalCenter
}
Text {
id: textItem
anchors.verticalCenter: parent.verticalCenter
text: control.text
font: control.font
color: "#666666"
}
}
background: Rectangle {
id:back
anchors.fill: parent
color: "#EBF8ED"
radius: 3.2
border.width: 1
border.color: control.borderColor
layer.enabled: true
}
Timer {
id: timer
repeat: false
onTriggered: control.visible=false
}
function show(text,timeout){
control.text = text
timer.interval = timeout
timer.restart();
control.visible=true
}
}

132
Analyser/qml/main.qml Normal file
View File

@ -0,0 +1,132 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Analyser
Window {
width: 1120
height: 640
visible: true
title: qsTr("L015上位机工具")
OperationItem {
id: operationItem
width: 450
anchors.top: parent.top
}
Item {
id: resultGroupBox
anchors.top: parent.top
anchors.right: parent.right
anchors.bottom: parent.bottom
width: 220
Text {
id: resultGroupBoxTitle
text: "识别结果"
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: 30
}
ScrollView {
id: resultView
anchors.left: parent.left
anchors.right: parent.right
anchors.top: resultGroupBoxTitle.bottom
anchors.bottom: parent.bottom
TextArea {
id: resultBrowser
font.pixelSize: 14
readOnly: true
wrapMode: TextArea.WordWrap
}
}
Button {
text: "清空"
anchors.right: parent.right
anchors.bottom: parent.bottom
onClicked: resultBrowser.clear()
}
}
ColumnLayout {
anchors.left: operationItem.right
anchors.right: resultGroupBox.left
anchors.top: parent.top
anchors.bottom: parent.bottom
TabBar {
id: bar
width: parent.width
TabButton {
implicitWidth: 100
text: qsTr("视频流")
}
TabButton {
text: qsTr("日志")
}
}
StackLayout {
width: parent.width
currentIndex: bar.currentIndex
clip: true
Image {
id: image
cache: false
fillMode: Image.PreserveAspectFit
rotation: 90
source: "image://videoframe/"
}
Item {
ScrollView {
id: view
anchors.fill: parent
TextArea {
id: logBrowser
readOnly: true
wrapMode: TextArea.WordWrap
}
}
Button {
text: "清空"
anchors.right: parent.right
anchors.bottom: parent.bottom
onClicked: logBrowser.clear()
}
}
}
}
Connections {
target: App
function onNewLog(text) {
logBrowser.append(text)
}
function onNewStatusTip(level, tip) {
if (level === 0) {
statusTip.icon = "../resources/successfull.svg"
statusTip.color="#EBF8ED"
statusTip.show(tip, 2000)
} else if (level === 1) {
statusTip.icon = "../resources/warning.svg"
statusTip.color="#FAFAD2"
statusTip.show(tip, 2000)
} else if (level === 2) {
resultBrowser.append(tip)
}
console.log(level, Application.Info)
}
function onNewVideoFrame() {
image.source = ""
image.source = "image://videoframe/"
}
}
StatusTip {
id: statusTip
width: 200
height: 50
icon: "../resources/successfull.svg"
}
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
<title>Combined Shape</title>
<desc>Created with Sketch.</desc>
<g id="下载页" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M6.1,11.6 C3.0072054,11.6 0.5,9.0927946 0.5,6 C0.5,2.9072054 3.0072054,0.4 6.1,0.4 C9.1927946,0.4 11.7,2.9072054 11.7,6 C11.7,9.0927946 9.1927946,11.6 6.1,11.6 Z M5.74088496,8.2481404 L9.2254819,4.76354346 C9.44990579,4.53911957 9.44990579,4.17579395 9.2254819,3.95194404 C9.00105802,3.72752015 8.63773239,3.72752015 8.41388248,3.95194404 L5.33508525,7.03074127 L4.28528656,5.98094259 C4.06143665,5.75709268 3.69811103,5.75709268 3.47368714,5.98094259 C3.24983723,6.20536647 3.24983723,6.5686921 3.47368714,6.79311598 L4.92928554,8.2481404 C5.15313545,8.47256429 5.51646107,8.47256429 5.74088496,8.2481404 Z" id="Combined-Shape" fill="#37BD4B"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="18px" height="17px" viewBox="0 0 18 17" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
<title>💚 Icon</title>
<desc>Created with Sketch.</desc>
<defs>
<path d="M10.7557911,2.21895043 L17.3867068,14.3756291 C17.9156322,15.3453257 17.5583166,16.560199 16.5886199,17.0891245 C16.2948427,17.2493666 15.9655536,17.3333333 15.6309156,17.3333333 L2.36908438,17.3333333 C1.26451488,17.3333333 0.369084379,16.4379028 0.369084379,15.3333333 C0.369084379,14.9986954 0.453051125,14.6694063 0.613293233,14.3756291 L7.24420885,2.21895043 C7.77313431,1.24925376 8.98800759,0.891938091 9.95770426,1.42086355 C10.2948373,1.6047543 10.5719004,1.8818174 10.7557911,2.21895043 Z M9.07936508,12.75 C8.61912779,12.75 8.24603175,13.123096 8.24603175,13.5833333 C8.24603175,14.0435706 8.61912779,14.4166667 9.07936508,14.4166667 C9.53960237,14.4166667 9.91269841,14.0435706 9.91269841,13.5833333 C9.91269841,13.123096 9.53960237,12.75 9.07936508,12.75 Z M9.07936508,6.5 C8.61912779,6.5 8.24603175,6.87309604 8.24603175,7.33333333 L8.24603175,10.6666667 C8.24603175,11.126904 8.61912779,11.5 9.07936508,11.5 C9.53960237,11.5 9.91269841,11.126904 9.91269841,10.6666667 L9.91269841,7.33333333 C9.91269841,6.87309604 9.53960237,6.5 9.07936508,6.5 Z" id="path-1"></path>
</defs>
<g id="下载页" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="💚-Icon" transform="translate(0.000000, -1.000000)">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="Mask" fill="#F5A623" xlink:href="#path-1"></use>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -15,6 +15,10 @@ add_compile_definitions(
BOOST_USE_WINAPI_VERSION=BOOST_WINAPI_VERSION_WIN10 BOOST_USE_WINAPI_VERSION=BOOST_WINAPI_VERSION_WIN10
) )
set(FFmpeg_ROOT ${Libraries_ROOT}/ffmpeg-7.0.1-full_build-shared)
set(FFmpeg_INCLUDE_DIR ${FFmpeg_ROOT}/include)
set(FFmpeg_LIB_DIR ${FFmpeg_ROOT}/lib)
add_subdirectory(${Projects_ROOT}/Kylin/Universal Universal) add_subdirectory(${Projects_ROOT}/Kylin/Universal Universal)
add_subdirectory(${Projects_ROOT}/Kylin/Encrypt Encrypt) add_subdirectory(${Projects_ROOT}/Kylin/Encrypt Encrypt)
add_subdirectory(${Projects_ROOT}/Kylin/QtComponets QtComponets) add_subdirectory(${Projects_ROOT}/Kylin/QtComponets QtComponets)

View File

@ -19,12 +19,8 @@ qt_add_executable(SmartLockerUpdater
target_link_libraries(SmartLockerUpdater target_link_libraries(SmartLockerUpdater
PRIVATE Peripheral PRIVATE Peripheral
PRIVATE Encrypt PRIVATE Encrypt
PRIVATE mfplat
PRIVATE mfuuid
PRIVATE Mfreadwrite
PRIVATE Qt${QT_VERSION_MAJOR}::Widgets PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
PRIVATE Qt${QT_VERSION_MAJOR}::SerialPort PRIVATE Qt${QT_VERSION_MAJOR}::SerialPort
PRIVATE Mf
) )
set_target_properties(SmartLockerUpdater PROPERTIES set_target_properties(SmartLockerUpdater PROPERTIES

View File

@ -1,5 +1,6 @@
#include "CdcUpdater.h" #include "CdcUpdater.h"
#include "BoostLog.h" #include "BoostLog.h"
#include "StringUtility.h"
#include <QDebug> #include <QDebug>
#include <QSerialPort> #include <QSerialPort>
#include <QSerialPortInfo> #include <QSerialPortInfo>
@ -44,7 +45,7 @@ void CdcUpdater::start(const QString &path) {
if (m_timerId < 0) { if (m_timerId < 0) {
m_timerId = startTimer(1000); m_timerId = startTimer(1000);
} }
m_path = path.toStdString(); m_path = Amass::StringUtility::UTF8ToGBK(path.toStdString());
LOG(info) << "ota file: " << m_path; LOG(info) << "ota file: " << m_path;
emit progressChanged(++m_progress); emit progressChanged(++m_progress);
} }

View File

@ -2,6 +2,7 @@
#include "BoostLog.h" #include "BoostLog.h"
#include "CdcUpdater.h" #include "CdcUpdater.h"
#include "DeviceDiscovery.h" #include "DeviceDiscovery.h"
#include "StringUtility.h"
#include <QDropEvent> #include <QDropEvent>
#include <QFileDialog> #include <QFileDialog>
#include <QHBoxLayout> #include <QHBoxLayout>
@ -57,7 +58,7 @@ Widget::~Widget() {}
void Widget::start() { void Widget::start() {
m_progressBar->setValue(0); m_progressBar->setValue(0);
auto filePath = m_fileEditor->text(); auto filePath = m_fileEditor->text();
if (!std::filesystem::exists(filePath.toStdString())) { if (!std::filesystem::exists(Amass::StringUtility::UTF8ToGBK(filePath.toStdString()))) {
QMessageBox::warning(this, "升级", "升级文件不存在!"); QMessageBox::warning(this, "升级", "升级文件不存在!");
return; return;
} }

View File

@ -8,4 +8,8 @@ target_include_directories(Peripheral
target_link_libraries(Peripheral target_link_libraries(Peripheral
PUBLIC Universal PUBLIC Universal
PRIVATE Mfreadwrite
PRIVATE Mf
PRIVATE mfplat
PRIVATE mfuuid
) )

View File

@ -116,6 +116,38 @@ void DeviceDiscovery::enterOtaMode(const std::shared_ptr<Device> &device, std::e
&llTimeStamp, &pSample); &llTimeStamp, &pSample);
} }
std::vector<std::string> DeviceDiscovery::devices() {
std::vector<std::string> 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;
}
for (int i = 0; i < count; i++) {
auto name = this->deviceName(devices[i]);
ret.push_back(name);
}
return ret;
}
DeviceDiscovery::Resolutions DeviceDiscovery::deviceResolutions(const std::shared_ptr<Device> &source) { DeviceDiscovery::Resolutions DeviceDiscovery::deviceResolutions(const std::shared_ptr<Device> &source) {
DeviceDiscovery::Resolutions ret; DeviceDiscovery::Resolutions ret;
HRESULT result = S_OK; HRESULT result = S_OK;

View File

@ -24,6 +24,7 @@ public:
DeviceDiscovery(); DeviceDiscovery();
std::shared_ptr<Device> find(const std::string &deviceName, std::error_code &error); std::shared_ptr<Device> find(const std::string &deviceName, std::error_code &error);
void enterOtaMode(const std::shared_ptr<Device> &device, std::error_code &error); void enterOtaMode(const std::shared_ptr<Device> &device, std::error_code &error);
std::vector<std::string> devices();
protected: protected:
std::string deviceName(IMFActivate *device); std::string deviceName(IMFActivate *device);

View File

@ -17,6 +17,9 @@ sock_cmd_exec() 内部线程逻辑之间通信
algo_cb()->onFacePalmDetectionPassed()->app_server_alive()->sock_cmd_exec:SOCK_CMD__ALIVE_PALM algo_cb()->onFacePalmDetectionPassed()->app_server_alive()->sock_cmd_exec:SOCK_CMD__ALIVE_PALM
ST__PlamRegister()->__PalmRegister() -> PalmFeatureExtract() -> spv2_get_feature() ST__PlamRegister()->__PalmRegister() -> PalmFeatureExtract() -> spv2_get_feature()
frame_proc_task()
frame_sync_task() // L015 V200 __DUAL_SNS_FAKE__ 双sensor活体
``` ```
@ -55,5 +58,6 @@ HOST_TOOLS := /opt/Xuantie-900-gcc-elf-newlib-x86_64-V2.6.1/bin
```shell ```shell
./rebuild-app.sh y L015 V200 R002 # 编译烧录固件 ./rebuild-app.sh y L015 V200 R002 # 编译烧录固件
./rebuild-app-ota.sh y L015 V200 R002 11 # 编译OTA固件11为OTA版本号 ./rebuild-app-ota.sh y L015 V200 R002 11 # 编译OTA固件11为OTA版本号
600X800
``` ```