1.添加uvc视频流,
This commit is contained in:
parent
4febb5fb7f
commit
4724af4086
296
Analyser/Application.cpp
Normal file
296
Analyser/Application.cpp
Normal 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
100
Analyser/Application.h
Normal 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__
|
@ -1,15 +1,49 @@
|
||||
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)
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Quick QuickTemplates2 Widgets SerialPort)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Quick QuickTemplates2 Widgets SerialPort)
|
||||
|
||||
add_executable(Analyser Analyser.rc
|
||||
main.cpp
|
||||
Application.h Application.cpp
|
||||
CategoryLogSinkBackend.h CategoryLogSinkBackend.cpp
|
||||
Widget.h Widget.cpp
|
||||
ModuleCommunication.h ModuleCommunication.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
|
||||
@ -17,6 +51,14 @@ target_link_libraries(Analyser
|
||||
PRIVATE Encrypt
|
||||
PRIVATE Database
|
||||
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}::SerialPort
|
||||
)
|
||||
|
@ -1,15 +1,13 @@
|
||||
#include "CategoryLogSinkBackend.h"
|
||||
#include "AsyncEvent.h"
|
||||
#include <QTextBrowser>
|
||||
#include <iostream>
|
||||
#include "BoostLog.h"
|
||||
|
||||
CategoryLogSinkBackend::CategoryLogSinkBackend(const std::string &category, QTextBrowser *target)
|
||||
: m_category(category), m_target(target) {
|
||||
CategoryLogSinkBackend::CategoryLogSinkBackend(const std::string &category, Append &&append)
|
||||
: m_category(category), m_append(std::move(append)) {
|
||||
}
|
||||
|
||||
void CategoryLogSinkBackend::consume(const boost::log::record_view &record, string_type const &output) {
|
||||
auto &&category = record[AmassKeywords::category];
|
||||
if (category == m_category) {
|
||||
Amass::executeAtObjectThread(m_target, [this, output]() { m_target->append(QString::fromStdString(output)); });
|
||||
if ((category == m_category) && m_append) {
|
||||
m_append(output);
|
||||
}
|
||||
}
|
||||
|
@ -2,19 +2,19 @@
|
||||
#define __CATEGORYLOGSINKBACKEND_H__
|
||||
|
||||
#include <boost/log/sinks.hpp>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
class QTextBrowser;
|
||||
|
||||
class CategoryLogSinkBackend
|
||||
: public boost::log::sinks::basic_formatted_sink_backend<char, boost::log::sinks::synchronized_feeding> {
|
||||
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);
|
||||
|
||||
private:
|
||||
std::string m_category;
|
||||
QTextBrowser *m_target = nullptr;
|
||||
Append m_append;
|
||||
};
|
||||
|
||||
#endif // __CATEGORYLOGSINKBACKEND_H__
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "ModuleCommunication.h"
|
||||
#include "BoostLog.h"
|
||||
#include <QSerialPort>
|
||||
#include "StringUtility.h"
|
||||
#include <WinSock2.h>
|
||||
#include <mbedtls/md5.h>
|
||||
#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} {
|
||||
}
|
||||
|
||||
bool ModuleCommunication::open(const QString &portName) {
|
||||
bool ModuleCommunication::open(const QString &portName, int baudRate) {
|
||||
bool ret = true;
|
||||
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::errorOccurred, this, &ModuleCommunication::onErrorOccurred);
|
||||
|
||||
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;
|
||||
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));
|
||||
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) << 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));
|
||||
auto [frameData, frameSize] = generateFrame(EnrollSingle, reinterpret_cast<const uint8_t *>(&data), sizeof(data));
|
||||
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) << "用户名: " << username << ", 超时时间: " << static_cast<int>(timeout) << "s";
|
||||
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) {
|
||||
EnrollData data = {0};
|
||||
data.timeout = timeout;
|
||||
@ -74,6 +87,8 @@ void ModuleCommunication::deleteUser(uint16_t userid) {
|
||||
uint16_t n = htons(userid);
|
||||
auto [frameData, frameSize] = generateFrame(DeleteUser, reinterpret_cast<const uint8_t *>(&n), sizeof(n));
|
||||
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) << "删除用户ID: " << userid;
|
||||
LOG_CAT(info, GUI) << Separator;
|
||||
@ -82,6 +97,8 @@ void ModuleCommunication::deleteUser(uint16_t userid) {
|
||||
void ModuleCommunication::deleteAll() {
|
||||
auto [frameData, frameSize] = generateFrame(DeleteAll);
|
||||
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) << Separator;
|
||||
}
|
||||
@ -110,7 +127,7 @@ void ModuleCommunication::enrollPalmFeature(uint16_t userid, const PalmFeature &
|
||||
auto buffer = new uint8_t[sizeof(PalmFeatureHeader) + feature.feature.size()];
|
||||
auto header = reinterpret_cast<PalmFeatureHeader *>(buffer);
|
||||
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));
|
||||
|
||||
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());
|
||||
|
||||
auto [frameData, frameSize] =
|
||||
generateFrame(RegisterPalmFeature, buffer, sizeof(PalmFeatureHeader) + feature.feature.size());
|
||||
auto [frameData, frameSize] = generateFrame(
|
||||
RegisterPalmFeature, buffer, static_cast<uint16_t>(sizeof(PalmFeatureHeader) + feature.feature.size()));
|
||||
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
|
||||
LOG_CAT(info, GUI) << "发送注册掌静脉特征指令: " << protocolDataFormatString(frameData, frameSize);
|
||||
LOG_CAT(info, GUI) << Separator;
|
||||
@ -133,6 +150,10 @@ void ModuleCommunication::enrollPalmFeature(uint16_t userid, const PalmFeature &
|
||||
if (buffer != nullptr) delete[] buffer;
|
||||
}
|
||||
|
||||
ModuleCommunication::MessageId ModuleCommunication::currentMessageId() const {
|
||||
return m_currentMessageId;
|
||||
}
|
||||
|
||||
void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
|
||||
uint8_t messageId = data[2];
|
||||
switch (messageId) {
|
||||
@ -152,13 +173,22 @@ void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
|
||||
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
|
||||
if (result == Success) {
|
||||
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));
|
||||
emit newVerifyResult(userid, reinterpret_cast<const char *>(info->username));
|
||||
} else if (result == Failed4Timeout) {
|
||||
LOG_CAT(info, GUI) << "识别超时。";
|
||||
} else if (result == Failed4UnknownReason) {
|
||||
} else if (result == Rejected) {
|
||||
LOG_CAT(info, GUI) << "模组拒绝该命令。";
|
||||
} else if (result == Failed4UnknownUser) {
|
||||
LOG_CAT(info, GUI) << "未录入用户。";
|
||||
emit newVerifyResult(InvalidUserId, "");
|
||||
} else if (result == Failed4UnknownReason) {
|
||||
LOG_CAT(info, GUI) << "未知错误。";
|
||||
emit newVerifyResult(InvalidUserId, "");
|
||||
} else {
|
||||
LOG_CAT(info, GUI) << "未知错误(" << static_cast<int>(result) << ")。";
|
||||
}
|
||||
LOG_CAT(info, GUI) << Separator;
|
||||
break;
|
||||
@ -178,11 +208,10 @@ void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
|
||||
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
|
||||
if (result == Success) {
|
||||
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);
|
||||
LOG_CAT(info, GUI) << "图片大小: " << ntohs(info->width) << "x" << height
|
||||
<< ", line size: " << lineSize;
|
||||
emit newEnrolledImageInfo(lineSize * height, info->md5);
|
||||
LOG_CAT(info, GUI) << "图片大小: " << width << "x" << height;
|
||||
emit newEnrolledImageInfo(width * height, info->md5);
|
||||
}
|
||||
LOG_CAT(info, GUI) << Separator;
|
||||
break;
|
||||
@ -244,6 +273,8 @@ void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
|
||||
<< ", data: " << protocolDataFormatString(data, size);
|
||||
break;
|
||||
}
|
||||
m_currentMessageId = Idle;
|
||||
emit commandFinished(static_cast<MessageId>(replyId), static_cast<MessageStatus>(result));
|
||||
break;
|
||||
}
|
||||
case Note: {
|
||||
@ -256,8 +287,6 @@ void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
|
||||
break;
|
||||
}
|
||||
case PalmState: { // 模组返回的数据为当前帧的手掌状态
|
||||
auto info = reinterpret_cast<const VerifyNoteInfo *>(data + 7);
|
||||
LOG(info) << info->state;
|
||||
break;
|
||||
}
|
||||
case UnknownError: {
|
||||
@ -282,6 +311,7 @@ void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
|
||||
}
|
||||
|
||||
void ModuleCommunication::onReadyRead() {
|
||||
// LOG(info) << "ModuleCommunication::onReadyRead";
|
||||
m_receivedBuffer.append(m_serialPort->readAll());
|
||||
while (m_receivedBuffer.size() >= 2) {
|
||||
int beginIndex = -1;
|
||||
@ -299,6 +329,7 @@ void ModuleCommunication::onReadyRead() {
|
||||
m_receivedBuffer.remove(0, beginIndex);
|
||||
beginIndex = 0;
|
||||
}
|
||||
|
||||
if (m_receivedBuffer.size() < 5) break;
|
||||
uint16_t packageSize = *reinterpret_cast<uint16_t *>(m_receivedBuffer.data() + 3);
|
||||
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,
|
||||
uint16_t size) {
|
||||
static uint8_t sendBuffer[1024] = {0};
|
||||
|
@ -3,16 +3,16 @@
|
||||
|
||||
#include "DataStructure.h"
|
||||
#include <QObject>
|
||||
#include <QSerialPort>
|
||||
#include <memory>
|
||||
|
||||
class QSerialPort;
|
||||
|
||||
class ModuleCommunication : public QObject {
|
||||
Q_OBJECT
|
||||
static constexpr uint32_t UsernameSize = 32;
|
||||
static constexpr const char *Separator = "----------";
|
||||
|
||||
public:
|
||||
constexpr static uint16_t InvalidUserId = std::numeric_limits<uint16_t>::max();
|
||||
enum MessageId : uint8_t {
|
||||
Reply = 0,
|
||||
Note = 0x01,
|
||||
@ -25,6 +25,7 @@ public:
|
||||
DeleteAll = 0x21,
|
||||
RegisterPalmFeature = 0xF9,
|
||||
RequestPalmFeature = 0xFA,
|
||||
Idle = 0xFF,
|
||||
};
|
||||
|
||||
enum NoteId : uint8_t {
|
||||
@ -83,7 +84,6 @@ public:
|
||||
|
||||
struct EnrolledImageReply {
|
||||
uint16_t width;
|
||||
uint16_t lineSize;
|
||||
uint16_t height;
|
||||
uint8_t md5[16];
|
||||
};
|
||||
@ -115,32 +115,44 @@ public:
|
||||
|
||||
#pragma pack()
|
||||
explicit ModuleCommunication(QObject *parent = nullptr);
|
||||
bool open(const QString &portName);
|
||||
void verify(uint8_t timeout);
|
||||
void reset();
|
||||
bool open(const QString &portName, int baudRate);
|
||||
Q_INVOKABLE void verify(uint8_t timeout);
|
||||
Q_INVOKABLE void reset();
|
||||
|
||||
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 deleteUser(uint16_t userid);
|
||||
void deleteAll();
|
||||
Q_INVOKABLE void deleteUser(uint16_t userid);
|
||||
Q_INVOKABLE void deleteAll();
|
||||
void requestEnrolledImage(uint32_t offset, uint32_t size);
|
||||
|
||||
void requestPalmFeature(uint16_t userid);
|
||||
void enrollPalmFeature(uint16_t userid, const PalmFeature &feature);
|
||||
|
||||
MessageId currentMessageId() const;
|
||||
|
||||
signals:
|
||||
void newVerifyResult(uint16_t userid, const QString &username);
|
||||
void newPalmFeature(const PalmFeature &feature);
|
||||
void newEnrolledImageInfo(uint32_t size, const uint8_t *md5);
|
||||
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:
|
||||
void processPackage(const uint8_t *data, uint16_t size);
|
||||
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::string protocolDataFormatString(const uint8_t *data, int size);
|
||||
|
||||
private:
|
||||
std::shared_ptr<QSerialPort> m_serialPort;
|
||||
QByteArray m_receivedBuffer;
|
||||
|
||||
MessageId m_currentMessageId = ModuleCommunication::Idle;
|
||||
};
|
||||
|
||||
#endif // MODULECOMMUNICATION_H
|
||||
|
26
Analyser/VideoFrameProvider.cpp
Normal file
26
Analyser/VideoFrameProvider.cpp
Normal 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);
|
||||
}
|
16
Analyser/VideoFrameProvider.h
Normal file
16
Analyser/VideoFrameProvider.h
Normal 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
91
Analyser/VideoPlayer.cpp
Normal 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
34
Analyser/VideoPlayer.h
Normal 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__
|
@ -1,4 +1,5 @@
|
||||
#include "Widget.h"
|
||||
#include "Application.h"
|
||||
#include "BoostLog.h"
|
||||
#include "CategoryLogSinkBackend.h"
|
||||
#include "Database.h"
|
||||
@ -72,26 +73,15 @@ Widget::Widget(QWidget *parent) : QWidget{parent} {
|
||||
layout->addLayout(operatorLayout, 1);
|
||||
layout->addWidget(tabWidget, 3);
|
||||
|
||||
m_database = std::make_shared<Database>();
|
||||
m_featureModel = new PalmFeatureTableModel(this);
|
||||
m_featureTableView->setModel(m_featureModel);
|
||||
|
||||
QTimer::singleShot(0, this, [this]() {
|
||||
onSerialRefreshButtonClicked();
|
||||
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() {
|
||||
auto ret = new QGroupBox("命令");
|
||||
@ -204,55 +194,19 @@ QGroupBox *Widget::initializeUvcGroupBox() {
|
||||
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() {
|
||||
auto button = dynamic_cast<QPushButton *>(sender());
|
||||
if (button == nullptr) return;
|
||||
auto text = button->text();
|
||||
if (text == "连接") {
|
||||
auto portName = m_serialComboBox->currentText();
|
||||
m_communication = std::make_shared<ModuleCommunication>();
|
||||
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);
|
||||
auto status = Amass::Singleton<Application>::instance()->open(portName, 2000000);
|
||||
if (status) {
|
||||
m_commandGroupBox->setEnabled(true);
|
||||
button->setText("关闭");
|
||||
}
|
||||
} else if (text == "关闭") {
|
||||
m_communication.reset();
|
||||
Amass::Singleton<Application>::instance()->close();
|
||||
m_commandGroupBox->setEnabled(false);
|
||||
button->setText("连接");
|
||||
}
|
||||
@ -262,6 +216,7 @@ void Widget::onSerialRefreshButtonClicked() {
|
||||
m_serialComboBox->clear();
|
||||
auto ports = QSerialPortInfo::availablePorts();
|
||||
for (auto &port : ports) {
|
||||
if (port.description() == "蓝牙链接上的标准串行") continue;
|
||||
m_serialComboBox->addItem(port.portName());
|
||||
}
|
||||
}
|
||||
@ -270,51 +225,65 @@ void Widget::onUvcRefreshButtonClicked() {
|
||||
}
|
||||
|
||||
void Widget::onEnrollButtonClicked() {
|
||||
auto module = Amass::Singleton<Application>::instance()->module();
|
||||
if (!module) return;
|
||||
auto name = m_enrollNameEdit->text();
|
||||
auto timeout = m_enrollTimeoutEdit->text().toInt();
|
||||
m_communication->enroll(name.toStdString(), timeout);
|
||||
module->enroll(name.toStdString(), timeout);
|
||||
}
|
||||
|
||||
void Widget::onEnrollExButtonClicked() {
|
||||
auto module = Amass::Singleton<Application>::instance()->module();
|
||||
if (!module) return;
|
||||
auto name = m_enrollNameEdit->text();
|
||||
auto timeout = m_enrollTimeoutEdit->text().toInt();
|
||||
m_communication->enrollEx(name.toStdString(), timeout);
|
||||
module->enrollEx(name.toStdString(), timeout);
|
||||
}
|
||||
|
||||
void Widget::onVerifyButtonClicked() {
|
||||
if (!m_communication) return;
|
||||
auto module = Amass::Singleton<Application>::instance()->module();
|
||||
if (!module) return;
|
||||
auto timeout = m_verifyTimeoutEdit->text().toInt();
|
||||
m_communication->verify(timeout);
|
||||
module->verify(timeout);
|
||||
}
|
||||
|
||||
void Widget::onDeleteAllButtonClicked() {
|
||||
if (!m_communication) return;
|
||||
m_communication->deleteAll();
|
||||
auto module = Amass::Singleton<Application>::instance()->module();
|
||||
if (!module) return;
|
||||
module->deleteAll();
|
||||
}
|
||||
|
||||
void Widget::onDeleteButtonClicked() {
|
||||
if (!m_communication) return;
|
||||
auto module = Amass::Singleton<Application>::instance()->module();
|
||||
if (!module) return;
|
||||
auto id = m_deleteIdEdit->text().toInt();
|
||||
m_communication->deleteUser(id);
|
||||
module->deleteUser(id);
|
||||
}
|
||||
|
||||
void Widget::onRequestPalmFeatureButtonClicked() {
|
||||
if (!m_communication) return;
|
||||
auto module = Amass::Singleton<Application>::instance()->module();
|
||||
if (!module) return;
|
||||
auto id = m_palmFeatureEdit->text().toInt();
|
||||
m_communication->requestPalmFeature(id);
|
||||
module->requestPalmFeature(id);
|
||||
}
|
||||
|
||||
void Widget::onRegisterPalmFeatureButtonClicked() {
|
||||
if (!m_communication) return;
|
||||
auto features = m_database->palmFeatures();
|
||||
if (features.empty()) {
|
||||
LOG(error) << "feature is empty.";
|
||||
return;
|
||||
}
|
||||
m_communication->enrollPalmFeature(264, features.at(0));
|
||||
auto module = Amass::Singleton<Application>::instance()->module();
|
||||
if (!module) return;
|
||||
// auto features = m_database->palmFeatures();
|
||||
// if (features.empty()) {
|
||||
// LOG(error) << "feature is empty.";
|
||||
// return;
|
||||
// }
|
||||
// module->enrollPalmFeature(264, features.at(0));
|
||||
}
|
||||
|
||||
void Widget::onResetButtonClicked() {
|
||||
if (!m_communication) return;
|
||||
m_communication->reset();
|
||||
auto module = Amass::Singleton<Application>::instance()->module();
|
||||
if (!module) return;
|
||||
module->reset();
|
||||
}
|
||||
|
||||
void Widget::onNewLog(const QString &log) {
|
||||
m_logBrowser->append(log);
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
#ifndef WIDGET_H
|
||||
#define WIDGET_H
|
||||
|
||||
#include "DataStructure.h"
|
||||
#include <QWidget>
|
||||
#include <fstream>
|
||||
|
||||
@ -19,7 +18,7 @@ class Widget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Widget(QWidget *parent = nullptr);
|
||||
void initializeLogger();
|
||||
void onNewLog(const QString &log);
|
||||
|
||||
protected:
|
||||
QGroupBox *initializeCommandGroupBox();
|
||||
@ -45,10 +44,6 @@ protected:
|
||||
|
||||
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:
|
||||
QComboBox *m_serialComboBox = nullptr;
|
||||
QPushButton *m_serialConnectButton = nullptr;
|
||||
@ -68,15 +63,8 @@ private:
|
||||
|
||||
QLineEdit *m_palmFeatureEdit = nullptr;
|
||||
|
||||
std::shared_ptr<ModuleCommunication> m_communication;
|
||||
std::shared_ptr<Database> m_database;
|
||||
|
||||
PalmFeatureTableModel *m_featureModel = 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
|
||||
|
@ -1,21 +1,21 @@
|
||||
#include "Application.h"
|
||||
#include "BoostLog.h"
|
||||
#include "DeviceDiscovery.h"
|
||||
#include "Widget.h"
|
||||
#include <QApplication>
|
||||
#include <QFont>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
using namespace Amass;
|
||||
boost::log::initialize("logs/app");
|
||||
|
||||
QApplication a(argc, argv);
|
||||
QFont font;
|
||||
font.setPointSize(16);
|
||||
a.setFont(font);
|
||||
Widget w;
|
||||
w.initializeLogger();
|
||||
w.setWindowTitle("L015上位机工具");
|
||||
w.setMinimumWidth(1120);
|
||||
w.setMinimumHeight(640);
|
||||
w.show();
|
||||
auto app = Singleton<Application>::instance<Construct>(argc, argv);
|
||||
app->initializeLogger();
|
||||
|
||||
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();
|
||||
}
|
||||
|
80
Analyser/qml/ConnectionItem.qml
Normal file
80
Analyser/qml/ConnectionItem.qml
Normal 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()
|
||||
}
|
||||
}
|
110
Analyser/qml/OperationItem.qml
Normal file
110
Analyser/qml/OperationItem.qml
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
56
Analyser/qml/StatusTip.qml
Normal file
56
Analyser/qml/StatusTip.qml
Normal 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
132
Analyser/qml/main.qml
Normal 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"
|
||||
}
|
||||
}
|
9
Analyser/resources/successfull.svg
Normal file
9
Analyser/resources/successfull.svg
Normal 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 |
17
Analyser/resources/warning.svg
Normal file
17
Analyser/resources/warning.svg
Normal 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 |
@ -15,6 +15,10 @@ add_compile_definitions(
|
||||
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/Encrypt Encrypt)
|
||||
add_subdirectory(${Projects_ROOT}/Kylin/QtComponets QtComponets)
|
||||
|
@ -19,12 +19,8 @@ qt_add_executable(SmartLockerUpdater
|
||||
target_link_libraries(SmartLockerUpdater
|
||||
PRIVATE Peripheral
|
||||
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
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "CdcUpdater.h"
|
||||
#include "BoostLog.h"
|
||||
#include "StringUtility.h"
|
||||
#include <QDebug>
|
||||
#include <QSerialPort>
|
||||
#include <QSerialPortInfo>
|
||||
@ -44,7 +45,7 @@ void CdcUpdater::start(const QString &path) {
|
||||
if (m_timerId < 0) {
|
||||
m_timerId = startTimer(1000);
|
||||
}
|
||||
m_path = path.toStdString();
|
||||
m_path = Amass::StringUtility::UTF8ToGBK(path.toStdString());
|
||||
LOG(info) << "ota file: " << m_path;
|
||||
emit progressChanged(++m_progress);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "BoostLog.h"
|
||||
#include "CdcUpdater.h"
|
||||
#include "DeviceDiscovery.h"
|
||||
#include "StringUtility.h"
|
||||
#include <QDropEvent>
|
||||
#include <QFileDialog>
|
||||
#include <QHBoxLayout>
|
||||
@ -57,7 +58,7 @@ Widget::~Widget() {}
|
||||
void Widget::start() {
|
||||
m_progressBar->setValue(0);
|
||||
auto filePath = m_fileEditor->text();
|
||||
if (!std::filesystem::exists(filePath.toStdString())) {
|
||||
if (!std::filesystem::exists(Amass::StringUtility::UTF8ToGBK(filePath.toStdString()))) {
|
||||
QMessageBox::warning(this, "升级", "升级文件不存在!");
|
||||
return;
|
||||
}
|
||||
|
@ -8,4 +8,8 @@ target_include_directories(Peripheral
|
||||
|
||||
target_link_libraries(Peripheral
|
||||
PUBLIC Universal
|
||||
PRIVATE Mfreadwrite
|
||||
PRIVATE Mf
|
||||
PRIVATE mfplat
|
||||
PRIVATE mfuuid
|
||||
)
|
||||
|
@ -116,6 +116,38 @@ void DeviceDiscovery::enterOtaMode(const std::shared_ptr<Device> &device, std::e
|
||||
&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 ret;
|
||||
HRESULT result = S_OK;
|
||||
|
@ -24,6 +24,7 @@ public:
|
||||
DeviceDiscovery();
|
||||
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);
|
||||
std::vector<std::string> devices();
|
||||
|
||||
protected:
|
||||
std::string deviceName(IMFActivate *device);
|
||||
|
@ -17,6 +17,9 @@ sock_cmd_exec() 内部线程逻辑之间通信
|
||||
algo_cb()->onFacePalmDetectionPassed()->app_server_alive()->sock_cmd_exec:SOCK_CMD__ALIVE_PALM
|
||||
|
||||
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
|
||||
./rebuild-app.sh y L015 V200 R002 # 编译烧录固件
|
||||
./rebuild-app-ota.sh y L015 V200 R002 11 # 编译OTA固件,11为OTA版本号
|
||||
600X800
|
||||
```
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user