#include "ModuleCommunication.h" #include "BoostLog.h" #include "StringUtility.h" #include #include #include 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(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(&data), sizeof(data)); m_serialPort->write(reinterpret_cast(frameData), frameSize); m_currentMessageId = Verify; emit commandStarted(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(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(data.username), username.c_str(), sizeof(data.username)); auto [frameData, frameSize] = generateFrame(EnrollSingle, reinterpret_cast(&data), sizeof(data)); m_serialPort->write(reinterpret_cast(frameData), frameSize); m_currentMessageId = EnrollSingle; emit commandStarted(EnrollSingle); LOG_CAT(info, GUI) << "发送注册指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << "用户名: " << username << ", 超时时间: " << static_cast(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; strncpy(reinterpret_cast(data.username), username.c_str(), sizeof(data.username)); auto [frameData, frameSize] = generateFrame(EnrollGetImage, reinterpret_cast(&data), sizeof(data)); m_serialPort->write(reinterpret_cast(frameData), frameSize); LOG_CAT(info, GUI) << "发送注册指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << "用户名: " << username << ", 超时时间: " << static_cast(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(&n), sizeof(n)); m_serialPort->write(reinterpret_cast(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; } void ModuleCommunication::deleteAll() { auto [frameData, frameSize] = generateFrame(DeleteAll); m_serialPort->write(reinterpret_cast(frameData), frameSize); m_currentMessageId = DeleteAll; emit commandStarted(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(&request), sizeof(request)); m_serialPort->write(reinterpret_cast(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(&n), sizeof(n)); m_serialPort->write(reinterpret_cast(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(buffer); header->userid = htons(userid); header->featureTotalSize = htons(static_cast(feature.feature.size())); strncpy(reinterpret_cast(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(sizeof(PalmFeatureHeader) + feature.feature.size())); m_serialPort->write(reinterpret_cast(frameData), frameSize); LOG_CAT(info, GUI) << "发送注册掌静脉特征指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << Separator; 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) { 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(data + 7); uint16_t userid = ntohs(info->userid); LOG_CAT(info, GUI) << "用户ID: " << userid << ", 用户名: " << std::string_view(reinterpret_cast(info->username)); emit newVerifyResult(userid, reinterpret_cast(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(result) << ")。"; } LOG_CAT(info, GUI) << Separator; break; } case EnrollSingle: { LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size); if (result == Success) { auto info = reinterpret_cast(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(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(data + 7); emit newImageSliceData(std::vector(info->data, info->data + ntohl(info->size))); } // 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(data + 7); LOG_CAT(info, GUI) << "用户ID: " << ntohs(info->userid) << ", 用户名: " << std::string_view(reinterpret_cast(info->username)) << ", 特征值长度: " << ntohs(info->featureTotalSize); PalmFeature feature; feature.username = std::string_view(reinterpret_cast(info->username)); feature.feature = std::vector(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; } default: LOG(warning) << "unknown reply command: 0x" << (static_cast(replyId) & 0xff) << ", data: " << protocolDataFormatString(data, size); break; } m_currentMessageId = Idle; emit commandFinished(static_cast(replyId), static_cast(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(noteId) & 0xff) << ", data: " << protocolDataFormatString(data, size); break; } break; } default: LOG(warning) << "unknown command: 0x" << (static_cast(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(m_receivedBuffer[i]) == 0xEF && static_cast(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(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(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 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++) { oss << "0x" << std::setfill('0') << std::setw(2) << std::hex << (static_cast(data[i]) & 0xff) << " "; } return oss.str(); }