1.实现掌静脉特征获取。

This commit is contained in:
2024-05-22 18:03:11 +08:00
parent cb451b3070
commit c40e512df6
5 changed files with 512 additions and 57 deletions

View File

@ -66,13 +66,16 @@ void ModuleCommunication::deleteAll() {
LOG_CAT(info, GUI) << Separator;
}
void ModuleCommunication::onReadyRead() {
auto data = m_serialPort->readAll();
if (data.size() < 6) {
LOG(warning) << "invalid data: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()), data.size());
return;
}
void ModuleCommunication::requestPalmFeature(uint16_t userid) {
uint16_t n = htons(userid);
auto [frameData, frameSize] = generateFrame(RequestPalmFeature, reinterpret_cast<const uint8_t *>(&n), sizeof(n));
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
LOG_CAT(info, GUI) << "发送获取掌静脉特征值指令: " << protocolDataFormatString(frameData, frameSize);
LOG_CAT(info, GUI) << "获取特征值用户ID: " << userid;
LOG_CAT(info, GUI) << Separator;
}
void ModuleCommunication::processPackage(const uint8_t *data, uint16_t size) {
uint8_t messageId = data[2];
switch (messageId) {
case Reply: {
@ -81,23 +84,17 @@ void ModuleCommunication::onReadyRead() {
switch (replyId) {
case Verify: {
if (result == Success) {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
auto info = reinterpret_cast<VerifyDataReply *>(data.data() + 7);
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
auto info = reinterpret_cast<const VerifyDataReply *>(data + 7);
LOG_CAT(info, GUI) << "用户ID: " << ntohs(info->userid)
<< ", 用户名: " << std::string_view(reinterpret_cast<const char *>(info->username));
LOG_CAT(info, GUI) << Separator;
} else if (result == Failed4Timeout) {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
LOG_CAT(info, GUI) << "识别超时。";
LOG_CAT(info, GUI) << Separator;
} else if (result == Failed4UnknownReason) {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
LOG_CAT(info, GUI) << "未录入用户。";
LOG_CAT(info, GUI) << Separator;
} else {
@ -107,10 +104,8 @@ void ModuleCommunication::onReadyRead() {
}
case EnrollSingle: {
if (result == Success) {
auto info = reinterpret_cast<EnrollDataReply *>(data.data() + 7);
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
auto info = reinterpret_cast<const EnrollDataReply *>(data + 7);
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
LOG_CAT(info, GUI) << "注册成功,用户ID: " << ntohs(info->userid);
LOG_CAT(info, GUI) << Separator;
}
@ -118,15 +113,11 @@ void ModuleCommunication::onReadyRead() {
}
case DeleteUser: {
if (result == Success) {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
LOG_CAT(info, GUI) << "删除用户成功。";
LOG_CAT(info, GUI) << Separator;
} else {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
LOG_CAT(info, GUI) << "删除用户失败。";
LOG_CAT(info, GUI) << Separator;
}
@ -134,17 +125,26 @@ void ModuleCommunication::onReadyRead() {
}
case DeleteAll: {
if (result == Success) {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()),
data.size());
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
LOG_CAT(info, GUI) << "删除所有用户成功。";
LOG_CAT(info, GUI) << Separator;
}
break;
}
case RequestPalmFeature: {
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
if (result == Success) {
auto info = reinterpret_cast<const PalmFeatureHeader *>(data + 7);
LOG_CAT(info, GUI) << "用户ID: " << ntohs(info->userid)
<< ", 用户名: " << std::string_view(reinterpret_cast<const char *>(info->username))
<< ", 特征值长度: " << ntohs(info->featureTotalSize);
}
LOG_CAT(info, GUI) << Separator;
break;
}
default:
LOG(warning) << "unknown reply command: 0x" << (static_cast<int>(replyId) & 0xff) << ", data: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()), data.size());
LOG(warning) << "unknown reply command: 0x" << (static_cast<int>(replyId) & 0xff)
<< ", data: " << protocolDataFormatString(data, size);
break;
}
break;
@ -153,32 +153,68 @@ void ModuleCommunication::onReadyRead() {
uint8_t noteId = data[5];
switch (noteId) {
case Ready: {
LOG_CAT(info, GUI) << "模组: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()), data.size());
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
LOG_CAT(info, GUI) << "模组上电初始化成功。";
LOG_CAT(info, GUI) << Separator;
break;
}
case 0x01: { // 模组返回的数据为当前帧的手掌状态
auto info = reinterpret_cast<VerifyNoteInfo *>(data.data() + 7);
auto info = reinterpret_cast<const VerifyNoteInfo *>(data + 7);
LOG(info) << info->state;
break;
}
default:
LOG(warning) << "unknown note command: 0x" << (static_cast<int>(noteId) & 0xff) << ", data: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()), data.size());
LOG(warning) << "unknown note command: 0x" << (static_cast<int>(noteId) & 0xff)
<< ", data: " << protocolDataFormatString(data, size);
break;
}
break;
}
default:
LOG(warning) << "unknown command: 0x" << (static_cast<int>(data[2]) & 0xff) << ", data: "
<< protocolDataFormatString(reinterpret_cast<const uint8_t *>(data.data()), data.size());
LOG(warning) << "unknown command: 0x" << (static_cast<int>(data[2]) & 0xff)
<< ", data: " << protocolDataFormatString(data, size);
break;
}
}
void ModuleCommunication::onReadyRead() {
m_receivedBuffer.append(m_serialPort->readAll());
while (m_receivedBuffer.size() >= 2) {
int beginIndex = -1;
for (int i = 0; i < m_receivedBuffer.size() - 1; i++) {
if (static_cast<uint8_t>(m_receivedBuffer[i]) == 0xEF &&
static_cast<uint8_t>(m_receivedBuffer[i + 1]) == 0xAA) {
beginIndex = i;
break;
}
}
if (beginIndex < 0) {
m_receivedBuffer.clear();
break;
} else if (beginIndex != 0) {
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);
uint16_t totalSize = 0;
if (packageSize == 0) {
totalSize = 6;
} else {
totalSize = packageSize + 6;
}
if (m_receivedBuffer.size() >= totalSize) {
processPackage(reinterpret_cast<const uint8_t *>(m_receivedBuffer.data()), totalSize);
m_receivedBuffer.remove(0, totalSize);
} else {
break;
}
}
}
std::pair<uint8_t *, uint32_t> ModuleCommunication::generateFrame(MessageId command, const uint8_t *data,
uint16_t size) {
static uint8_t sendBuffer[1024] = {0};
@ -198,7 +234,7 @@ std::pair<uint8_t *, uint32_t> ModuleCommunication::generateFrame(MessageId comm
std::string ModuleCommunication::protocolDataFormatString(const uint8_t *data, int size) {
std::ostringstream oss;
for (int i = 0; i < size; i++) {
oss << "0x" << std::hex << (static_cast<int>(data[i]) & 0xff) << " ";
oss << "0x" << std::setfill('0') << std::setw(2) << std::hex << (static_cast<int>(data[i]) & 0xff) << " ";
}
return oss.str();
}

View File

@ -19,6 +19,8 @@ public:
EnrollSingle = 0x1D,
DeleteUser = 0x20,
DeleteAll = 0x21,
RegisterPalmFeature = 0xF9,
RequestPalmFeature = 0xFA,
};
enum NoteId : uint8_t {
@ -79,6 +81,14 @@ public:
uint8_t unlockStatus;
};
struct PalmFeatureHeader {
uint16_t userid; // 用户ID
uint8_t username[32]; // 用户姓名
uint8_t admin; // 是否管理员YES1 NO:0
uint8_t featureDataMd5[16]; // 整体特征数据的MD5值
uint16_t featureTotalSize; // 特征数据总长度
};
#pragma pack()
explicit ModuleCommunication(QObject *parent = nullptr);
bool open(const QString &portName);
@ -88,13 +98,17 @@ public:
void deleteUser(uint16_t userid);
void deleteAll();
void requestPalmFeature(uint16_t userid);
protected:
void processPackage(const uint8_t *data, uint16_t size);
void onReadyRead();
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;
};
#endif // MODULECOMMUNICATION_H

View File

@ -39,26 +39,17 @@ Widget::Widget(QWidget *parent) : QWidget{parent} {
serialLayout->addWidget(m_serialConnectButton, 2, 1);
serialGroupBox->setLayout(serialLayout);
auto uvcGroupBox = new QGroupBox("UVC设置");
auto uvcLayout = new QGridLayout();
label = new QLabel("设备名");
uvcLayout->addWidget(label, 0, 0);
label = new QLabel("未发现设备");
uvcLayout->addWidget(label, 0, 1);
auto uvcRefreshButton = new QPushButton("刷新");
auto uvcConnectButton = new QPushButton("连接");
uvcLayout->addWidget(uvcRefreshButton, 1, 0);
uvcLayout->addWidget(uvcConnectButton, 1, 1);
uvcGroupBox->setLayout(uvcLayout);
auto connectLayout = new QHBoxLayout();
connectLayout->addWidget(serialGroupBox);
connectLayout->addWidget(uvcGroupBox);
connectLayout->addWidget(initializeUvcGroupBox());
m_logBrowser = new QTextBrowser();
m_logBrowser->setReadOnly(true);
auto logLayout = new QVBoxLayout();
m_logBrowser->setLayout(logLayout);
auto btn = new QPushButton("清空");
connect(btn, &QPushButton::clicked, this, &Widget::onClearLogButtonClicked);
logLayout->addWidget(btn, 0, Qt::AlignBottom | Qt::AlignRight);
auto tabWidget = new QTabWidget();
tabWidget->addTab(m_logBrowser, "日志");
@ -103,6 +94,10 @@ QGroupBox *Widget::initializeCommandGroupBox() {
return ret;
}
void Widget::onClearLogButtonClicked() {
m_logBrowser->clear();
}
QGroupBox *Widget::initializeEnrollGroupBox() {
auto ret = new QGroupBox("注册用户");
auto layout = new QFormLayout();
@ -156,20 +151,38 @@ QGroupBox *Widget::initializePalmFeatureGroupBox() {
auto layout = new QFormLayout();
auto edit = new QLineEdit("");
layout->addRow("用户ID:", edit);
m_palmFeatureEdit = new QLineEdit("");
layout->addRow("用户ID:", m_palmFeatureEdit);
auto button = new QPushButton("特征值下发");
layout->addRow("", button);
auto button1 = new QPushButton("特征值上报");
layout->addRow("", button1);
connect(button1, &QPushButton::clicked, this, &Widget::onRequestPalmFeatureButtonClicked);
ret->setLayout(layout);
return ret;
}
QGroupBox *Widget::initializeUvcGroupBox() {
auto ret = new QGroupBox("UVC设置");
auto uvcLayout = new QGridLayout();
auto label = new QLabel("设备名");
uvcLayout->addWidget(label, 0, 0);
auto comboBox = new QComboBox();
uvcLayout->addWidget(comboBox, 0, 1);
auto uvcRefreshButton = new QPushButton("刷新");
auto uvcConnectButton = new QPushButton("连接");
uvcLayout->addWidget(uvcRefreshButton, 1, 0);
uvcLayout->addWidget(uvcConnectButton, 1, 1);
ret->setLayout(uvcLayout);
return ret;
}
void Widget::onSerialConnectButtonClicked() {
auto button = dynamic_cast<QPushButton *>(sender());
if (button == nullptr) return;
@ -197,6 +210,9 @@ void Widget::onSerialRefreshButtonClicked() {
}
}
void Widget::onUvcRefreshButtonClicked() {
}
void Widget::onEnrollButtonClicked() {
auto name = m_enrollNameEdit->text();
auto timeout = m_enrollTimeoutEdit->text().toInt();
@ -219,3 +235,9 @@ void Widget::onDeleteButtonClicked() {
auto id = m_deleteIdEdit->text().toInt();
m_communication->deleteUser(id);
}
void Widget::onRequestPalmFeatureButtonClicked() {
if (!m_communication) return;
auto id = m_palmFeatureEdit->text().toInt();
m_communication->requestPalmFeature(id);
}

View File

@ -18,19 +18,25 @@ public:
protected:
QGroupBox *initializeCommandGroupBox();
void onClearLogButtonClicked();
void onSerialConnectButtonClicked();
void onSerialRefreshButtonClicked();
void onUvcRefreshButtonClicked();
void onEnrollButtonClicked();
void onVerifyButtonClicked();
void onDeleteAllButtonClicked();
void onDeleteButtonClicked();
void onRequestPalmFeatureButtonClicked();
QGroupBox *initializeEnrollGroupBox();
QGroupBox *initializeVerifyGroupBox();
QGroupBox *initializeDeleteGroupBox();
QGroupBox *initializePalmFeatureGroupBox();
QGroupBox *initializeUvcGroupBox();
private:
QComboBox *m_serialComboBox = nullptr;
QPushButton *m_serialConnectButton = nullptr;
@ -48,6 +54,8 @@ private:
QPushButton *m_deleteButton = nullptr;
QPushButton *m_deleteAllButton = nullptr;
QLineEdit *m_palmFeatureEdit = nullptr;
std::shared_ptr<ModuleCommunication> m_communication;
};