SmartLockerTools/Analyser/ModuleCommunication.cpp

416 lines
18 KiB
C++

#include "ModuleCommunication.h"
#include "BoostLog.h"
#include "StringUtility.h"
#include <WinSock2.h>
#include <mbedtls/md5.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, int baudRate) {
bool ret = true;
m_serialPort = std::make_shared<QSerialPort>(portName);
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 ? "成功" : "失败")
<< ", 波特率: " << baudRate;
if (!ret) {
LOG(error) << m_serialPort->errorString().toStdString();
}
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);
setCurrentMessageIdStatus(Verify);
LOG_CAT(info, GUI) << "发送识别指令: " << protocolDataFormatString(frameData, frameSize);
LOG_CAT(info, GUI) << Separator;
}
void ModuleCommunication::reset() {
auto [frameData, frameSize] = generateFrame(Reset);
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
setCurrentMessageIdStatus(Reset);
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);
setCurrentMessageIdStatus(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::enrollEx(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(EnrollGetImage, 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);
setCurrentMessageIdStatus(DeleteUser);
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);
setCurrentMessageIdStatus(DeleteAll);
LOG_CAT(info, GUI) << "发送删除所有指令: " << protocolDataFormatString(frameData, frameSize);
LOG_CAT(info, GUI) << Separator;
}
void ModuleCommunication::requestEnrolledImage(uint32_t offset, uint32_t size) {
ImageSliceRequest request;
request.offset = htonl(offset);
request.size = htonl(size);
auto [data, frameSize] =
generateFrame(GetEnrolledImage, reinterpret_cast<const uint8_t *>(&request), sizeof(request));
m_serialPort->write(reinterpret_cast<const char *>(data), frameSize);
// 打印太耗时
// LOG_CAT(info, GUI) << "发送获取图片指令: " << protocolDataFormatString(data, frameSize);
// LOG_CAT(info, GUI) << Separator;
}
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::enrollPalmFeature(uint16_t userid, const PalmFeature &feature) {
auto buffer = new uint8_t[sizeof(PalmFeatureHeader) + feature.feature.size()];
auto header = reinterpret_cast<PalmFeatureHeader *>(buffer);
header->userid = htons(userid);
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;
mbedtls_md5_init(&context);
mbedtls_md5_starts(&context);
uint8_t md5[16];
mbedtls_md5_update(&context, feature.feature.data(), feature.feature.size());
mbedtls_md5_finish(&context, md5);
mbedtls_md5_free(&context);
memcpy(header->featureDataMd5, md5, sizeof(header->featureDataMd5));
memcpy(buffer + sizeof(PalmFeatureHeader), feature.feature.data(), 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;
if (buffer != nullptr) delete[] buffer;
}
void ModuleCommunication::uploadImageInfo(const UploadImageInformation &info) {
UploadImageInformation request;
request.operation = info.operation;
request.width = htons(info.width);
request.height = htons(info.height);
request.size = htonl(info.size);
memcpy(request.username, info.username, sizeof(request.username));
memcpy(request.md5, info.md5, sizeof(request.md5));
auto [data, frameSize] =
generateFrame(UploadImageInfo, reinterpret_cast<const uint8_t *>(&request), sizeof(request));
m_serialPort->write(reinterpret_cast<const char *>(data), frameSize);
}
void ModuleCommunication::uploadImageData(uint32_t offset, const uint8_t *data, uint32_t size) {
uint32_t dataSize = sizeof(UploadImageDataSlice) + size;
auto buffer = new uint8_t[dataSize];
auto request = reinterpret_cast<UploadImageDataSlice *>(buffer);
request->size = htonl(size);
request->offset = htonl(offset);
memcpy(request->data, data, size);
auto [frameData, frameSize] = generateFrame(UploadImageData, buffer, dataSize);
m_serialPort->write(reinterpret_cast<const char *>(frameData), frameSize);
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) {
case Reply: {
uint8_t replyId = data[5];
auto result = data[6];
switch (replyId) {
case Reset: {
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
if (result == Success) {
LOG_CAT(info, GUI) << "复位完成。";
}
LOG_CAT(info, GUI) << Separator;
break;
}
case Verify: {
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
if (result == Success) {
auto info = reinterpret_cast<const VerifyDataReply *>(data + 7);
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 == 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;
}
case EnrollSingle: {
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
if (result == Success) {
auto info = reinterpret_cast<const EnrollDataReply *>(data + 7);
LOG_CAT(info, GUI) << "注册成功,用户ID: " << ntohs(info->userid);
} else if (result == Failed4Timeout) {
LOG_CAT(info, GUI) << "识别超时。";
}
LOG_CAT(info, GUI) << Separator;
break;
}
case EnrollGetImage: {
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
if (result == Success) {
auto info = reinterpret_cast<const EnrolledImageReply *>(data + 7);
uint16_t width = ntohs(info->width);
uint16_t height = ntohs(info->height);
LOG_CAT(info, GUI) << "图片大小: " << width << "x" << height;
emit newEnrolledImageInfo(width * height, info->md5);
}
LOG_CAT(info, GUI) << Separator;
break;
}
case GetEnrolledImage: {
// LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
if (result == Success) {
auto info = reinterpret_cast<const ImageSliceReply *>(data + 7);
uint32_t sliceSize = ntohl(info->size);
emit newImageSliceData(std::vector<uint8_t>(info->data, info->data + sliceSize));
}
// LOG_CAT(info, GUI) << Separator;
break;
}
case DeleteUser: {
if (result == Success) {
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
LOG_CAT(info, GUI) << "删除用户成功。";
LOG_CAT(info, GUI) << Separator;
} else {
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
LOG_CAT(info, GUI) << "删除用户失败。";
LOG_CAT(info, GUI) << Separator;
}
break;
}
case DeleteAll: {
if (result == Success) {
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);
PalmFeature feature;
feature.username = std::string_view(reinterpret_cast<const char *>(info->username));
feature.feature = std::vector<uint8_t>(info->feature, info->feature + ntohs(info->featureTotalSize));
emit newPalmFeature(feature);
}
LOG_CAT(info, GUI) << Separator;
break;
}
case RegisterPalmFeature: {
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
if (result == Success) {
LOG_CAT(info, GUI) << "掌静脉特征值注册成功。";
}
LOG_CAT(info, GUI) << Separator;
break;
}
case UploadImageInfo: {
break;
}
case UploadImageData: {
break;
}
default:
LOG(warning) << "unknown reply command: 0x" << (static_cast<int>(replyId) & 0xff)
<< ", data: " << protocolDataFormatString(data, size);
break;
}
m_currentMessageId = Idle;
emit currentMessageIdChanged();
emit commandFinished(static_cast<MessageId>(replyId), static_cast<MessageStatus>(result));
break;
}
case Note: {
uint8_t noteId = data[5];
switch (noteId) {
case Ready: {
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
LOG_CAT(info, GUI) << "模组上电初始化成功。";
LOG_CAT(info, GUI) << Separator;
break;
}
case PalmState: { // 模组返回的数据为当前帧的手掌状态
break;
}
case UnknownError: {
LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size);
LOG_CAT(info, GUI) << "未知错误。";
LOG_CAT(info, GUI) << Separator;
break;
}
default:
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(data, size);
break;
}
}
void ModuleCommunication::onReadyRead() {
// LOG(info) << "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;
}
}
}
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[4096] = {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);
}
void ModuleCommunication::setCurrentMessageIdStatus(MessageId messageId) {
if (m_currentMessageId != messageId) {
m_currentMessageId = messageId;
emit commandStarted(messageId);
emit currentMessageIdChanged();
}
}
std::string ModuleCommunication::protocolDataFormatString(const uint8_t *data, int size) {
std::ostringstream oss;
for (int i = 0; i < size; i++) {
oss << "0x" << std::setfill('0') << std::setw(2) << std::hex << (static_cast<int>(data[i]) & 0xff) << " ";
}
return oss.str();
}