2024-05-21 21:09:55 +08:00
|
|
|
#include "ModuleCommunication.h"
|
|
|
|
#include "BoostLog.h"
|
|
|
|
#include <QSerialPort>
|
|
|
|
#include <WinSock2.h>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
static inline uint8_t xor_checksum_byte(const uint8_t *data, uint32_t len) {
|
|
|
|
uint8_t sum = 0;
|
|
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
|
|
sum ^= data[i];
|
|
|
|
}
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
|
|
|
ModuleCommunication::ModuleCommunication(QObject *parent) : QObject{parent} {
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ModuleCommunication::open(const QString &portName) {
|
|
|
|
bool ret = true;
|
|
|
|
m_serialPort = std::make_shared<QSerialPort>(portName);
|
|
|
|
m_serialPort->setBaudRate(QSerialPort::Baud115200);
|
|
|
|
connect(m_serialPort.get(), &QSerialPort::readyRead, this, &ModuleCommunication::onReadyRead);
|
|
|
|
|
|
|
|
ret = m_serialPort->open(QSerialPort::ReadWrite);
|
|
|
|
LOG_CAT(info, GUI) << "打开串口(" << portName.toStdString() << ")" << (ret ? "成功" : "失败") << "。";
|
|
|
|
LOG_CAT(info, GUI) << Separator;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ModuleCommunication::verify(uint8_t timeout) {
|
|
|
|
VerifyInfo data = {0};
|
|
|
|
data.timeout = timeout;
|
|
|
|
|
|
|
|
auto [frameData, frameSize] = generateFrame(Verify, reinterpret_cast<const uint8_t *>(&data), sizeof(data));
|
|
|
|
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
|
|
|
|
|
|
|
|
LOG_CAT(info, GUI) << "发送识别指令: " << protocolDataFormatString(frameData, frameSize);
|
|
|
|
LOG_CAT(info, GUI) << Separator;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ModuleCommunication::enroll(const std::string &username, uint8_t timeout) {
|
|
|
|
EnrollData data = {0};
|
|
|
|
data.timeout = 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);
|
|
|
|
|
|
|
|
LOG_CAT(info, GUI) << "发送注册指令: " << protocolDataFormatString(frameData, frameSize);
|
|
|
|
LOG_CAT(info, GUI) << "用户名: " << username << ", 超时时间: " << static_cast<int>(timeout) << "s";
|
|
|
|
LOG_CAT(info, GUI) << Separator;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
LOG_CAT(info, GUI) << "发送删除用户指令: " << protocolDataFormatString(frameData, frameSize);
|
|
|
|
LOG_CAT(info, GUI) << "删除用户ID: " << userid;
|
|
|
|
LOG_CAT(info, GUI) << Separator;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ModuleCommunication::deleteAll() {
|
|
|
|
auto [frameData, frameSize] = generateFrame(DeleteAll);
|
|
|
|
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
|
|
|
|
LOG_CAT(info, GUI) << "发送删除所有指令: " << protocolDataFormatString(frameData, frameSize);
|
|
|
|
LOG_CAT(info, GUI) << Separator;
|
|
|
|
}
|
|
|
|
|
2024-05-22 18:03:11 +08:00
|
|
|
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) {
|
2024-05-21 21:09:55 +08:00
|
|
|
uint8_t messageId = data[2];
|
|
|
|
switch (messageId) {
|
|
|
|
case Reply: {
|
|
|
|
uint8_t replyId = data[5];
|
|
|
|
auto result = data[6];
|
|
|
|
switch (replyId) {
|
|
|
|
case Verify: {
|
|
|
|
if (result == Success) {
|
2024-05-22 18:03:11 +08:00
|
|
|
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
|
|
|
|
auto info = reinterpret_cast<const VerifyDataReply *>(data + 7);
|
2024-05-21 21:09:55 +08:00
|
|
|
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) {
|
2024-05-22 18:03:11 +08:00
|
|
|
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
|
2024-05-21 21:09:55 +08:00
|
|
|
LOG_CAT(info, GUI) << "识别超时。";
|
|
|
|
LOG_CAT(info, GUI) << Separator;
|
|
|
|
} else if (result == Failed4UnknownReason) {
|
2024-05-22 18:03:11 +08:00
|
|
|
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
|
2024-05-21 21:09:55 +08:00
|
|
|
LOG_CAT(info, GUI) << "未录入用户。";
|
|
|
|
LOG_CAT(info, GUI) << Separator;
|
|
|
|
} else {
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case EnrollSingle: {
|
|
|
|
if (result == Success) {
|
2024-05-22 18:03:11 +08:00
|
|
|
auto info = reinterpret_cast<const EnrollDataReply *>(data + 7);
|
|
|
|
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
|
2024-05-21 21:09:55 +08:00
|
|
|
LOG_CAT(info, GUI) << "注册成功,用户ID: " << ntohs(info->userid);
|
|
|
|
LOG_CAT(info, GUI) << Separator;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DeleteUser: {
|
|
|
|
if (result == Success) {
|
2024-05-22 18:03:11 +08:00
|
|
|
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
|
2024-05-21 21:09:55 +08:00
|
|
|
LOG_CAT(info, GUI) << "删除用户成功。";
|
|
|
|
LOG_CAT(info, GUI) << Separator;
|
|
|
|
} else {
|
2024-05-22 18:03:11 +08:00
|
|
|
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
|
2024-05-21 21:09:55 +08:00
|
|
|
LOG_CAT(info, GUI) << "删除用户失败。";
|
|
|
|
LOG_CAT(info, GUI) << Separator;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DeleteAll: {
|
|
|
|
if (result == Success) {
|
2024-05-22 18:03:11 +08:00
|
|
|
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
|
2024-05-21 21:09:55 +08:00
|
|
|
LOG_CAT(info, GUI) << "删除所有用户成功。";
|
|
|
|
LOG_CAT(info, GUI) << Separator;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2024-05-22 18:03:11 +08:00
|
|
|
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);
|
2024-05-22 20:17:07 +08:00
|
|
|
PalmFeature feature;
|
|
|
|
feature.username = std::string_view(reinterpret_cast<const char *>(info->username));
|
|
|
|
const uint8_t *start = data + 7 + sizeof(PalmFeatureHeader);
|
|
|
|
feature.feature = std::vector<uint8_t>(start, start + ntohs(info->featureTotalSize));
|
|
|
|
emit newPalmFeature(feature);
|
2024-05-22 18:03:11 +08:00
|
|
|
}
|
|
|
|
LOG_CAT(info, GUI) << Separator;
|
|
|
|
break;
|
|
|
|
}
|
2024-05-21 21:09:55 +08:00
|
|
|
default:
|
2024-05-22 18:03:11 +08:00
|
|
|
LOG(warning) << "unknown reply command: 0x" << (static_cast<int>(replyId) & 0xff)
|
|
|
|
<< ", data: " << protocolDataFormatString(data, size);
|
2024-05-21 21:09:55 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Note: {
|
|
|
|
uint8_t noteId = data[5];
|
|
|
|
switch (noteId) {
|
|
|
|
case Ready: {
|
2024-05-22 18:03:11 +08:00
|
|
|
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
|
2024-05-21 21:09:55 +08:00
|
|
|
LOG_CAT(info, GUI) << "模组上电初始化成功。";
|
|
|
|
LOG_CAT(info, GUI) << Separator;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0x01: { // 模组返回的数据为当前帧的手掌状态
|
2024-05-22 18:03:11 +08:00
|
|
|
auto info = reinterpret_cast<const VerifyNoteInfo *>(data + 7);
|
2024-05-21 21:09:55 +08:00
|
|
|
LOG(info) << info->state;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2024-05-22 18:03:11 +08:00
|
|
|
LOG(warning) << "unknown note command: 0x" << (static_cast<int>(noteId) & 0xff)
|
|
|
|
<< ", data: " << protocolDataFormatString(data, size);
|
2024-05-21 21:09:55 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2024-05-22 18:03:11 +08:00
|
|
|
LOG(warning) << "unknown command: 0x" << (static_cast<int>(data[2]) & 0xff)
|
|
|
|
<< ", data: " << protocolDataFormatString(data, size);
|
2024-05-21 21:09:55 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-22 18:03:11 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-21 21:09:55 +08:00
|
|
|
std::pair<uint8_t *, uint32_t> ModuleCommunication::generateFrame(MessageId command, const uint8_t *data,
|
|
|
|
uint16_t size) {
|
|
|
|
static uint8_t sendBuffer[1024] = {0};
|
|
|
|
memset(sendBuffer, 0, sizeof(sendBuffer));
|
|
|
|
sendBuffer[0] = 0xef;
|
|
|
|
sendBuffer[1] = 0xaa;
|
|
|
|
sendBuffer[2] = command;
|
|
|
|
sendBuffer[3] = size >> 8;
|
|
|
|
sendBuffer[4] = size & 0xff;
|
|
|
|
if (size > 0) {
|
|
|
|
memcpy(sendBuffer + 5, data, size);
|
|
|
|
}
|
|
|
|
sendBuffer[5 + size] = xor_checksum_byte(sendBuffer + 2, 3 + size);
|
|
|
|
return std::make_pair(sendBuffer, size + 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string ModuleCommunication::protocolDataFormatString(const uint8_t *data, int size) {
|
|
|
|
std::ostringstream oss;
|
|
|
|
for (int i = 0; i < size; i++) {
|
2024-05-22 18:03:11 +08:00
|
|
|
oss << "0x" << std::setfill('0') << std::setw(2) << std::hex << (static_cast<int>(data[i]) & 0xff) << " ";
|
2024-05-21 21:09:55 +08:00
|
|
|
}
|
|
|
|
return oss.str();
|
|
|
|
}
|