#include "ModuleCommunication.h" #include "BoostLog.h" #include "StringUtility.h" #include #include #include #include #include #include #include #ifdef WIN32 #include #else #include #endif 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) { if (portName.isEmpty()) return false; 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) { VerifyRequest data = {0}; data.timeout = timeout; auto [frameData, frameSize] = generateFrame(Verify, reinterpret_cast(&data), sizeof(data)); m_serialPort->write(reinterpret_cast(frameData), frameSize); setCurrentMessageIdStatus(Verify); LOG_CAT(info, GUI) << "发送识别指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << Separator; } void ModuleCommunication::verifyExtended(bool captureImage, uint8_t timeout) { VerifyRequest data = {0}; data.timeout = timeout; data.save_image = captureImage ? 0x01 : 0x00; auto [frameData, frameSize] = generateFrame(VerifyExtended, reinterpret_cast(&data), sizeof(data)); m_serialPort->write(reinterpret_cast(frameData), frameSize); setCurrentMessageIdStatus(VerifyExtended); 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); setCurrentMessageIdStatus(Reset); LOG_CAT(info, GUI) << "发送复位指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << Separator; } void ModuleCommunication::enroll(const std::string &username, bool strictMode, uint8_t excludeMode, bool persistence, uint8_t timeout) { EnrollRequest data = {0}; data.strictMode = strictMode ? 1 : 0; data.excludeMode = excludeMode; data.timeout = timeout; data.skipSave = persistence ? 0 : 1; 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); setCurrentMessageIdStatus(EnrollSingle); LOG_CAT(info, GUI) << "发送注册指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << "用户名: " << username << ", 超时时间: " << static_cast(timeout) << "s"; LOG_CAT(info, GUI) << Separator; } void ModuleCommunication::enrollExtended(const std::string &username, bool strictMode, uint8_t excludeMode, bool persistence, uint8_t timeout) { EnrollRequest data = {}; data.strictMode = strictMode ? 1 : 0; data.excludeMode = excludeMode; data.timeout = timeout; data.skipSave = persistence ? 0 : 1; strncpy(reinterpret_cast(data.username), username.c_str(), sizeof(data.username)); auto [frameData, frameSize] = generateFrame(EnrollExtended, reinterpret_cast(&data), sizeof(data)); m_serialPort->write(reinterpret_cast(frameData), frameSize); setCurrentMessageIdStatus(EnrollExtended); 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); 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(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(GetImage, 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::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(&request), sizeof(request)); m_serialPort->write(reinterpret_cast(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(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(frameData), frameSize); delete[] buffer; } void ModuleCommunication::requestCurrentStatus() { auto [frameData, frameSize] = generateFrame(GetCurrentStatus); m_serialPort->write(reinterpret_cast(frameData), frameSize); LOG_CAT(info, GUI) << "发送状态指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << Separator; } void ModuleCommunication::setDebugEnabled(bool enabled) { DebugRequest request; boost::json::object object; object["command"] = "set_configurations"; object["cdc_log_enabled"] = enabled; auto text = boost::json::serialize(object); std::strncpy(request.message, text.c_str(), sizeof(request.message)); request.length = htons(text.size()); auto [frameData, frameSize] = generateFrame(EnableDebug, reinterpret_cast(&request), sizeof(request)); m_serialPort->write(reinterpret_cast(frameData), frameSize); } void ModuleCommunication::requestDebugSettings() { DebugRequest request; boost::json::object object; object["command"] = "get_configurations"; auto text = boost::json::serialize(object); std::strncpy(request.message, text.c_str(), sizeof(request.message)); request.length = htons(text.size()); auto [frameData, frameSize] = generateFrame(EnableDebug, reinterpret_cast(&request), sizeof(request)); m_serialPort->write(reinterpret_cast(frameData), frameSize); } void ModuleCommunication::startOta() { auto [frameData, frameSize] = generateFrame(StartOta); m_serialPort->write(reinterpret_cast(frameData), frameSize); } void ModuleCommunication::requestUniqueId() { auto [frameData, frameSize] = generateFrame(GetUniqueID); m_serialPort->write(reinterpret_cast(frameData), frameSize); LOG_CAT(info, GUI) << "发送获取ID指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << Separator; } void ModuleCommunication::requestVersion() { auto [frameData, frameSize] = generateFrame(GetVersion); m_serialPort->write(reinterpret_cast(frameData), frameSize); LOG_CAT(info, GUI) << "发送获取版本指令: " << protocolDataFormatString(frameData, frameSize); LOG_CAT(info, GUI) << Separator; } 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: case VerifyExtended: { LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size); if (result == Success) { auto info = reinterpret_cast(data + 7); uint16_t userid = ntohs(info->userid); uint16_t elapsed = ntohs(info->elapsed); LOG_CAT(info, GUI) << "用户ID: " << userid << ", 用户名: " << std::string_view(reinterpret_cast(info->username)) << ", 耗时: " << elapsed << "ms"; emit newVerifyResult(userid, reinterpret_cast(info->username), elapsed); } else if (result == Failed4Timeout) { LOG_CAT(info, GUI) << "识别超时。"; } else if (result == Rejected) { LOG_CAT(info, GUI) << "模组拒绝该命令。"; } else if (result == Failed4UnknownUser) { auto info = reinterpret_cast(data + 7); uint16_t elapsed = ntohs(info->elapsed); emit newVerifyResult(InvalidUserId, "", elapsed); LOG_CAT(info, GUI) << "未录入用户, 耗时: " << elapsed << "ms"; } else if (result == Failed4UnknownReason) { auto info = reinterpret_cast(data + 7); uint16_t elapsed = ntohs(info->elapsed); emit newVerifyResult(InvalidUserId, "", elapsed); LOG_CAT(info, GUI) << "未知错误, 耗时: " << elapsed << "ms"; } else { LOG_CAT(info, GUI) << "未知错误(" << static_cast(result) << ")。"; } if (replyId == VerifyExtended) { auto info = reinterpret_cast(data + 7); uint16_t width = ntohs(info->image_width); uint16_t height = ntohs(info->image_height); if ((width > 0) && (height > 0)) { emit newImageInfo(static_cast(replyId), width * height, info->md5); } } LOG_CAT(info, GUI) << Separator; break; } case EnrollSingle: case EnrollExtended: { LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size); if (result == Success) { uint16_t userId = InvalidUserId; if (replyId == EnrollExtended) { auto info = reinterpret_cast(data + 7); uint16_t width = ntohs(info->imageWidth); uint16_t height = ntohs(info->imageHeight); userId = ntohs(info->userid); uint16_t plamX = ntohs(info->palmVeinInformation.x1); uint16_t plamY = ntohs(info->palmVeinInformation.y1); LOG_CAT(info, GUI) << "注册成功,用户ID: " << userId << ", 图片大小: " << width << "x" << height << ", 已有ID: " << ntohs(info->enrolledId) << ", 姓名: " << (const char *)info->enrolledUsername << ". palm vein: (" << plamX << "," << plamY << " " << (ntohs(info->palmVeinInformation.x2) - plamX) << "x" << (ntohs(info->palmVeinInformation.y2) - plamY) << "), detection probability: " << ntohs(info->palmVeinInformation.detectionProbability) / 1000.f << ", quality: " << ntohs(info->palmVeinInformation.quality) / 1000.f; emit newImageInfo(static_cast(replyId), width * height, info->md5); } else { auto info = reinterpret_cast(data + 7); userId = ntohs(info->userid); LOG_CAT(info, GUI) << "注册成功,用户ID: " << userId; } emit newEnrollResult(userId); } else if (result == Failed4Timeout) { LOG_CAT(info, GUI) << "录入超时。"; emit errorOccurred(NoteId::InteractWarning, "录入", "录入超时"); } else if (result == Failed4PalmEnrolled) { emit errorOccurred(NoteId::InteractWarning, "手掌已被录入"); LOG_CAT(info, GUI) << "手掌已被录入。"; } else if (result == Failed4MaxUser) { emit errorOccurred(NoteId::InteractWarning, "录入", "注册已达上限"); } else { LOG_CAT(info, GUI) << "未知错误(" << static_cast(result) << ")。"; } LOG_CAT(info, GUI) << Separator; break; } case GetImage: { if (result == Success) { auto info = reinterpret_cast(data + 7); uint32_t sliceSize = ntohl(info->size); emit newImageSliceData(std::vector(info->data, info->data + sliceSize)); } else { LOG(info) << "GetImage failed, status: " << static_cast(result); } 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 UploadImageInfo: { break; } case UploadImageData: { auto info = reinterpret_cast(data + 7); if (result == Success) { if (info->operation == 0) { uint16_t userid = ntohs(info->userid); LOG_CAT(info, GUI) << "图片下发注册成功,用户ID: " << userid; emit newEnrollResult(userid); } else if (info->operation == 1) { auto result = reinterpret_cast(info); LOG_CAT(info, GUI) << "图片下发识别成功,用户ID: " << ntohs(result->userid) << ", 用户名: " << std::string_view(reinterpret_cast(result->result.username)) << ", 耗时: " << ntohs(result->result.elapsed) << "ms"; } else { LOG(warning) << "unknown upload image operation: " << info->operation; } LOG_CAT(info, GUI) << Separator; } else if (result == Needmore) { } else { LOG(info) << "upload image failed, operation: " << static_cast(info->operation) << ", status: " << static_cast(result); } break; } case GetCurrentStatus: { LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size); LOG_CAT(info, GUI) << "模组当前状态: " << static_cast(data[7]); LOG_CAT(info, GUI) << Separator; break; } case GetUniqueID: { auto id = reinterpret_cast(data + 7); int idCount = 0; std::ostringstream oss; oss << "["; for (int byteIndex = 0; byteIndex < sizeof(id->userids); byteIndex++) { for (uint8_t bitPosition = 0; bitPosition < 8; bitPosition++) { if (id->userids[byteIndex] & (1 << bitPosition)) { uint16_t uid = byteIndex * 8 + bitPosition; if (uid >= 1) { oss << uid << ", "; idCount++; } } } } oss << "]"; LOG_CAT(info, GUI) << "模组ID: " << std::string_view(id->id, sizeof(id->id)) << ", user ids[" << idCount << "]: " << oss.str(); LOG_CAT(info, GUI) << Separator; break; } case GetVersion: { LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size); auto version = reinterpret_cast(data + 7); int length = std::strlen(version->version); if (length > sizeof(version->version)) { length = sizeof(version->version); } int ota = ntohl(version->otaVersion); LOG_CAT(info, GUI) << "模组烧录版本: " << std::string_view(version->version, length) << ", OTA版本: " << ntohl(version->otaVersion); LOG_CAT(info, GUI) << Separator; m_verison = QString::fromLocal8Bit(version->version, length); m_otaVerison = ota; emit verisonChanged(); break; } case EnableDebug: { auto replyData = reinterpret_cast(data + 7); LOG(info) << "module debug: " << replyData->message; std::error_code error; auto replyValue = boost::json::parse(replyData->message, error); if (error) { LOG(error) << error.message(); } else { auto &reply = replyValue.as_object(); if (reply.contains("command")) { auto &command = reply.at("command"); if ((command == "get_configurations") || (command == "set_configurations")) { if (reply.contains("cdc_log_enabled")) { auto &cdcEnabled = reply.at("cdc_log_enabled").as_bool(); if (m_cdcDebugEnabled != cdcEnabled) { m_cdcDebugEnabled = cdcEnabled; emit cdcDebugEnabledChanged(); } } } } } break; } default: LOG(warning) << "unknown reply command: 0x" << (static_cast(replyId) & 0xff) << ", data: " << protocolDataFormatString(data, size); break; } m_currentMessageId = Idle; emit currentMessageIdChanged(); emit commandFinished(static_cast(replyId), static_cast(result)); break; } case Note: { auto noteId = static_cast(data[5]); switch (noteId) { case NoteId::Ready: { LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size); LOG_CAT(info, GUI) << "模组上电初始化成功。"; LOG_CAT(info, GUI) << Separator; break; } case NoteId::PalmState: { // 模组返回的数据为当前帧的手掌状态 auto state = reinterpret_cast(data + 6); PalmState palmState = static_cast(ntohs(state->state)); if (palmState == NeedMoveToCenter) { emit errorOccurred(NoteId::InteractWarning, "录入提示", "请将手掌置于画面中心"); } else if (palmState == TooFar) { emit errorOccurred(NoteId::InteractWarning, "录入提示", "请将手掌靠近一点"); } else if (palmState == ManyPalm) { emit errorOccurred(NoteId::InteractWarning, "录入提示", "检测到多个手掌"); } else if (palmState == NoAlive) { emit errorOccurred(NoteId::InteractWarning, "录入提示", "活体检测未通过"); } LOG(info) << "palm state: " << palmState; break; } case NoteId::UnknownError: { LOG_CAT(info, GUI) << "模组: " << protocolDataFormatString(data, size); LOG_CAT(info, GUI) << "未知错误。"; LOG_CAT(info, GUI) << Separator; break; } case NoteId::DebugInfo: { auto message = reinterpret_cast(data + 6); LOG_CAT(info, GUI) << "模组日志: " << message; break; } case NoteId::NoAliveImage: { LOG(info) << "no alive image"; emit newImageInfo(Note, 600 * 800, nullptr); 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(NoteId::DeviceError, m_serialPort->errorString()); } std::pair 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(data[i]) & 0xff) << " "; } return oss.str(); } BOOST_DESCRIBE_ENUM(ModuleCommunication::PalmState, NoPalm, TooFar, NeedMoveToCenter, ManyPalm, NoAlive) namespace std { std::ostream &operator<<(std::ostream &stream, const ModuleCommunication::PalmState &element) { char const *r = "(unnamed)"; boost::mp11::mp_for_each>([&](auto D) { if (element == D.value) r = D.name; }); stream << r << "(" << static_cast(element) << ")"; return stream; } } // namespace std