一、需求分析

1.1项目概述

本项目为可视化门禁系统,采用MVC(Model-View-Controller,模型-视图-控制器)设计模式,实现实时时间显示、端对端音视频对话(采用TCP+UDP混合模式实现无延迟一对一通信)、人脸/指纹识别开锁、用户注册及生物信息存储与比对等核心功能。系统通过清晰的分层架构,实现功能解耦、代码可维护性提升,确保各模块职责单一、联动高效,满足门禁场景的实用性、安全性与可扩展性需求。同时适配Qt4.3版本,支持在全志T113-S3开发板上交叉编译,优化界面响应速度、隐藏不常用组件、压缩图像资源,实现资源最小化,避免显示屏卡顿,并在门禁lb_view组件上增加在线键盘,支持密码输入及退格删除等操作。

核心目标:以MVC架构为基础,构建一个操作简洁、功能完整的可视化门禁控制系统,实现“视图展示-控制器调度-模型存储与处理”的闭环,支撑门禁日常管理、用户验证、音视频交互等场景,其中端对端音视频通话需通过TCP+UDP混合模式,保障一对一通信无延迟、稳定流畅;同时适配Qt4.3及全志T113-S3开发板,通过界面优化与资源压缩,确保系统在开发板上流畅运行,满足门禁实际使用场景需求。

1.2MVC架构整体设计要求

遵循MVC分层原则,明确Model(模型)、View(视图)、Controller(控制器)的职责边界,禁止跨层直接交互(View不直接操作Model,Model不直接反馈View),所有交互均通过Controller中转,确保架构清晰、解耦彻底。同时结合Qt4.3特性及全志T113-S3开发板资源限制,优化各模块实现逻辑,实现资源最小化,提升系统运行流畅度。

  • View(视图):负责界面展示与用户操作接收,不包含任何业务逻辑,仅通过信号触发Controller的相关方法,接收Controller转发的状态/数据并更新界面;适配Qt4.3控件特性,隐藏不常用组件,优化界面渲染逻辑,避免高分辨率原图直接使用,新增在线键盘功能,适配开发板显示屏。

  • Controller(控制器):作为核心调度中心,接收View的操作信号,调用Model的业务方法,处理核心业务逻辑,将Model的处理结果或状态转发给View,协调各模块联动;结合Qt4.3信号槽机制优化响应速度,配合View层完成资源最小化控制,处理在线键盘输入逻辑。

  • Model(模型):负责数据存储、业务逻辑处理(不含调度逻辑),提供数据操作接口,接收Controller的调用,完成数据读写、生物信息比对、设备状态管理、TCP+UDP音视频传输等核心操作,通过信号反馈处理结果;适配Qt4.3接口规范,优化数据处理效率,降低资源占用,适配全志T113-S3开发板交叉编译要求。

1.3各层详细需求

1.3.1view(视图层)需求

视图层以Widget为核心,通过UI界面呈现所有功能入口与反馈信息,仅负责“展示”与“接收操作”,不涉及任何业务逻辑或数据处理,所有操作均通过信号传递给Controller;同时适配Qt4.3版本,针对全志T113-S3开发板资源限制,进行界面优化与资源压缩,实现资源最小化,避免显示屏卡顿,新增在线键盘功能。

1.3.1.1界面控件需求
  • 时间显示控件(QLabel):用于实时显示系统时间,接收Controller转发的时间数据并更新展示,控件名称为labelTime,字体清晰、大小适中,适配门禁设备屏幕显示;适配Qt4.3标签特性,简化渲染逻辑,降低CPU占用。

  • 开锁操作控件:2个QPushButton,分别对应“人脸开锁”(btnFace)、“指纹开锁”(btnFinger),按钮标注清晰,点击后触发对应信号,传递给Controller,点击时需有视觉反馈(如按钮变色);适配Qt4.3按钮特性,简化样式,避免复杂绘制,提升响应速度。

  • 用户注册控件:1个QLineEdit(用于输入用户名,lineEdit)、2个QCheckBox(分别对应“录入人脸”“录入指纹”,checkFace、checkFinger)、1个QPushButton(注册按钮,btnRegister),点击注册按钮后,将用户名及勾选状态传递给Controller;QLineEdit需设置输入提示(如“请输入用户名”),禁止空输入;不常用组件隐藏:用户注册控件默认隐藏,仅在管理员模式下(通过特定操作触发)显示,减少界面渲染压力,实现资源最小化。

  • 音视频控制控件:2个QPushButton,分别对应“启动视频通话”(btnStartVideo)、“停止视频通话”(btnStopVideo),点击后触发信号,由Controller调度音视频模块;启动与停止按钮状态互斥(启动后不可再次点击,停止后可重新启动);新增“通话状态提示”标签(labelCallStatus),显示当前通话状态(如“未通话”“连接中”“通话中”“通话异常”);适配Qt4.3按钮、标签特性,简化控件样式,提升响应速度。

  • 提示信息控件:通过QMessageBox弹窗,接收Controller转发的操作结果(开锁成功、注册成功、识别失败、通话异常等),向用户展示反馈信息;弹窗提示语言简洁明了,搭配对应图标(成功用对勾、失败用叉号);适配Qt4.3弹窗特性,简化弹窗样式,减少弹窗渲染时间,避免卡顿。

  • 视频展示控件(QLabel):用于显示B端实时人像视频,接收Controller转发的视频流就绪信号后,加载并显示视频画面,视频画面清晰、无卡顿,支持自适应控件大小;仅支持一对一视频展示,禁止多端同时连接;图像资源优化:不直接使用高分辨率原图,将视频画面及界面图标压缩至适配开发板显示屏的分辨率(建议不超过800×480),降低显存占用,避免卡顿;适配Qt4.3视频渲染特性,简化渲染逻辑,提升流畅度。

  • 在线键盘控件:在门禁lb_view组件上,点击密码输入区域(新增QLineEdit控件,用于密码输入,命名为lineEditPassword)可弹出在线键盘,支持数字、字母密码输入,具备退格删除、确认输入功能;键盘布局简洁,适配开发板显示屏大小,按键大小适中,便于操作;适配Qt4.3控件特性,采用轻量级绘制,避免复杂样式,确保弹出、收起响应流畅,不占用过多资源;键盘默认隐藏,仅在点击密码输入区域时弹出,输入完成或点击其他区域时自动收起。

1.3.1.2视图交互需求
  • 实时响应:接收Controller转发的时间、操作结果、音视频通话状态等信号,即时更新界面(如时间每秒刷新、弹窗提示实时显示、视频画面实时渲染、通话状态同步更新),响应延迟不超过500ms;音视频通话画面延迟不超过100ms,确保无明显卡顿、无延迟;Qt4.3响应优化:简化界面刷新逻辑,避免频繁重绘,采用局部刷新替代全局刷新,加快响应速度;减少控件动画效果,降低CPU占用,适配全志T113-S3开发板性能。

  • 操作反馈:用户点击任何按钮后,界面无卡顿,反馈信息(弹窗)及时显示,明确告知用户操作状态;若操作失败,需说明具体原因(如“人脸不匹配”“用户名已存在”“通话连接失败”);在线键盘操作反馈及时,点击按键后立即显示输入内容,退格操作即时删除,无延迟。

  • 界面规范:窗口标题为“可视化门禁系统”,控件布局简洁合理,操作逻辑清晰,符合门禁系统日常使用习惯;控件间距均匀,按钮大小一致,便于用户快速操作;适配全志T113-S3开发板显示屏分辨率,界面布局自适应,避免控件超出屏幕范围;隐藏不常用组件(如用户注册控件),简化主界面,提升操作便捷性与运行流畅度。

  • 异常处理:当接收不到Controller转发的信号(如时间信号、视频信号、通话状态信号)时,界面需显示异常提示(如“时间加载失败”“视频无法显示”“通话信号异常”),避免空白界面;在线键盘弹出失败时,显示“键盘加载失败”提示,确保用户操作不受影响。

  • 资源最小化要求:所有界面图标、图片资源均进行压缩处理,分辨率适配开发板显示屏,避免高分辨率资源占用过多显存;控件样式简化,不使用复杂渐变、阴影效果,降低渲染压力;隐藏不常用组件,减少界面渲染的组件数量,降低CPU与内存占用,避免显示屏卡顿。

1.3.2controller(控制层)需求

控制层以AuthControl为核心,作为View与Model之间的桥梁,负责接收View的操作信号,调度Model的业务方法,处理核心业务逻辑,转发状态与结果给View,同时管理各模块(TimeDisplay、音视频、生物识别)的联动,确保系统流程顺畅,其中重点协调音视频模块的TCP+UDP混合传输,保障一对一无延迟通话;同时适配Qt4.3信号槽机制,优化响应速度,处理在线键盘输入逻辑,配合View层实现资源最小化,适配全志T113-S3开发板交叉编译要求。

1.3.2.1核心职责
  • 模块管理:创建并管理TimeDisplay(时间模块)、音视频模块、生物识别模块、数据库模块的实例,确保各模块正常初始化与运行;模块异常时,及时接收异常信号并转发给View提示用户;重点管理音视频模块的TCP+UDP连接状态,确保连接稳定;适配Qt4.3模块初始化特性,优化模块启动顺序,减少启动时间,降低资源占用;针对全志T113-S3开发板,优化模块运行逻辑,避免资源泄露。

  • 信号中转:接收View的操作信号,调用Model对应的业务方法;接收Model的处理结果信号,转发给View更新界面;确保信号传递准确、无丢失,避免View与Model直接通信;重点中转音视频通话的连接、断开、延迟异常等信号;Qt4.3信号槽优化:合理使用信号槽连接方式,避免过多信号槽连接导致的资源占用,优化信号传递效率,加快界面响应速度。

  • 业务调度:协调各模块联动(如音视频通话启动/停止、生物识别与数据库比对联动、用户注册与数据存储联动),处理业务流程逻辑;例如,人脸开锁时,需先调度生物识别模块采集数据,再调度数据库模块比对数据,最后根据结果调度View显示反馈;音视频通话时,调度音视频模块采用TCP+UDP混合模式,实现一对一无延迟通信,避免多端连接冲突;调度在线键盘的弹出与收起,处理密码输入、退格删除、确认输入逻辑,将密码数据传递给Model进行验证。

  • 状态管理:维护系统核心状态(如用户验证状态、音视频连接状态、设备就绪状态、TCP+UDP连接状态、在线键盘显示状态、不常用组件显示状态),提供状态查询接口供View调用;状态发生变化时,及时转发状态信号给View,确保界面与系统状态一致;优化状态更新逻辑,避免频繁状态更新导致的界面卡顿。

1.3.2.2具体功能需求
  • 时间模块调度:创建TimeDisplay实例,接收其时间更新信号,透传给View,实现时间实时显示;若TimeDisplay模块异常,转发设备错误信号给View,提示“时间显示异常”;适配Qt4.3定时器特性,优化定时器触发逻辑,减少CPU占用。

  • 生物识别开锁调度:接收View的“人脸开锁”“指纹开锁”信号,调用Model的生物信息比对方法,根据比对结果,转发“开锁成功”“识别失败”信号给View;若生物识别模块未就绪,转发设备错误信号,提示“设备未就绪”;新增密码开锁调度:接收View在线键盘输入的密码信号,调用Model的密码比对方法,根据比对结果,转发“开锁成功”“密码错误”信号给View,支持退格删除后的密码更新比对。

  • 用户注册调度:接收View的注册信号(用户名、人脸/指纹录入选择),先校验用户名是否为空、是否已存在(调用数据库模块查询),校验通过后,调用生物识别模块采集对应数据,再调用数据库模块存储数据,根据存储结果,转发“注册成功”“注册失败”信号给View;若用户名为空或已存在,直接转发注册失败信号,说明具体原因;调度用户注册控件的显示与隐藏,仅在管理员模式下触发显示。

  • 音视频调度:接收View的“启动/停止视频通话”信号,调用音视频模块的对应方法,指定采用TCP+UDP混合模式实现一对一通话,转发音视频连接状态(连接成功、断开、视频流就绪、通话延迟异常)信号给View;当B端满足展示条件时,调用音视频模块启动B端摄像头,转发视频流就绪信号给View,触发视频展示;严格控制一对一通信,禁止多端同时发起通话,避免信号冲突;若通话延迟超过100ms,转发延迟异常信号给View,提示“通话延迟过高”;适配Qt4.3音视频处理特性,优化音视频调度逻辑,降低资源占用,适配全志T113-S3开发板性能。

  • 设备与数据库初始化:启动系统时,调用Model的设备初始化(摄像头、指纹仪)和数据库初始化方法,确保系统正常运行;初始化失败时,转发设备错误信号给View,提示“设备初始化失败”或“数据库初始化失败”;同时初始化音视频模块的TCP+UDP通信参数,确保通话可正常发起;适配全志T113-S3开发板,优化初始化逻辑,减少初始化时间,实现资源最小化。

  • 在线键盘与密码调度:接收View的在线键盘弹出/收起信号,调度View层显示或隐藏在线键盘;接收在线键盘的输入信号(数字、字母、退格、确认),处理密码拼接与删除逻辑,将最终输入的密码传递给Model进行比对;控制在线键盘的显示时机,确保仅在点击密码输入区域时弹出,避免误触;适配Qt4.3控件调度特性,优化键盘弹出/收起的响应速度,避免卡顿。

  • 界面优化调度:调度View层隐藏不常用组件(用户注册控件),仅在管理员模式下(如输入管理员密码后)触发显示;监控界面渲染状态,若检测到显示屏卡顿,自动简化界面渲染逻辑(如暂停非必要控件刷新),确保核心功能正常运行;适配全志T113-S3开发板资源限制,动态调整界面刷新频率,实现资源最小化。

1.3.3module(模型层)需求

模型层包含多个功能子模块(TimeDisplay、数据库模块、生物识别模块、音视频模块),负责数据存储、业务逻辑处理,提供标准化接口供Controller调用,不与View直接交互,通过信号反馈处理结果,确保业务逻辑独立、可复用;其中音视频模块需采用TCP+UDP混合模式,实现端对端一对一无延迟通话;同时适配Qt4.3接口规范,优化数据处理效率,降低资源占用,支持全志T113-S3开发板交叉编译,配合实现资源最小化。

1.3.3.1时间模块需求
  • 功能:封装实时时间获取与刷新逻辑,独立运行,不依赖其他模块;具备异常处理能力,定时器异常时能反馈错误信息;适配Qt4.3定时器特性,优化定时器运行逻辑,减少CPU占用。

  • 实现:通过QTimer定时器(每秒触发一次),获取当前系统时间并格式化(格式为“yyyy-MM-dd hh:mm:ss”),通过信号将时间数据发送给Controller;定时器启动失败时,发送设备错误信号给Controller;适配全志T113-S3开发板,优化时间获取逻辑,降低资源占用,避免影响系统整体流畅度。

  • 接口:提供构造函数初始化定时器,无需其他外部调用接口,仅通过信号反馈时间数据和异常信息;定时器可通过析构函数正常停止,避免内存泄漏;接口适配Qt4.3规范,确保可在开发板上正常编译运行。

1.3.3.2数据库模块需求
  • 功能:负责用户信息、生物信息(人脸、指纹)、用户密码的存储、查询与比对,提供数据操作接口供Controller调用;确保数据安全、完整,支持异常处理(如数据库连接失败、表创建失败);适配Qt4.3 Sqlite接口,优化数据库操作逻辑,降低资源占用,适配全志T113-S3开发板。

  • 数据存储:支持用户注册信息(用户名)、生物信息(人脸数据、指纹数据,以字节数组形式存储)、用户密码(加密存储)的入库操作,确保数据唯一性(用户名不重复);存储失败时,反馈失败信号给Controller,说明失败原因(如“用户名已存在”“存储异常”);优化数据存储格式,减少数据库文件大小,实现资源最小化。

  • 数据查询:提供用户信息查询、所有注册用户列表查询、密码查询接口,支持根据用户ID或用户名查询相关信息;查询不到数据时,反馈空结果信号给Controller,不抛出异常;优化查询逻辑,加快查询速度,适配开发板性能。

  • 生物比对与密码比对:提供人脸数据、指纹数据的比对接口,接收当前采集的生物数据,与数据库中存储的数据进行比对,返回比对结果(匹配/不匹配);新增密码比对接口,接收Controller传递的在线键盘输入密码,与数据库中加密存储的密码进行比对,返回比对结果(匹配/不匹配);比对过程中出现异常(如数据损坏),反馈比对失败信号;优化比对算法,降低CPU占用,加快比对速度。

  • 初始化:提供数据库初始化接口,创建用户表(存储用户名、用户ID、加密密码)、生物信息表(存储用户ID、人脸数据、指纹数据),确保数据库正常可用;初始化失败时,发送设备错误信号给Controller;适配全志T113-S3开发板,优化数据库初始化逻辑,减少初始化时间,避免占用过多内存。

1.3.3.3生物识别模块需求
  • 功能:模拟/调用摄像头、指纹仪设备,采集人脸、指纹数据,提供数据采集接口供Controller调用;具备设备状态检测能力,反馈设备就绪、异常状态;适配Qt4.3设备调用接口,优化采集逻辑,降低资源占用,适配全志T113-S3开发板的硬件接口。

  • 人脸采集:接收Controller的采集指令,模拟采集人脸数据(或调用真实摄像头采集),将采集到的字节数组数据返回给Controller,用于比对或存储;采集失败时(如摄像头未打开),反馈采集失败信号给Controller;优化采集分辨率,避免高分辨率采集导致的资源占用过高,适配开发板性能。

  • 指纹采集:接收Controller的采集指令,模拟采集指纹数据(或调用真实指纹仪采集),将采集到的字节数组数据返回给Controller,用于比对或存储;采集失败时(如指纹不清晰),反馈采集失败信号给Controller;优化采集逻辑,加快采集速度,减少资源占用。

  • 设备状态反馈:提供设备就绪状态查询接口,通过信号反馈设备异常信息(如摄像头无法打开、指纹仪故障)给Controller;设备未就绪时,拒绝采集指令,反馈设备未就绪信号;适配全志T113-S3开发板的硬件特性,优化设备状态检测逻辑,确保检测准确、响应迅速。

1.3.3.4音视频模块需求
  • 功能:采用TCP+UDP混合模式,实现端对端一对一无延迟音视频对话、B端实时人像视频展示,提供音视频控制接口供Controller调用;支持音视频状态检测,反馈连接、断开、异常、延迟过高状态;严格限制一对一通信,禁止多端同时连接,确保通话流畅无冲突;适配Qt4.3音视频处理接口,优化音视频编码、解码逻辑,降低资源占用,适配全志T113-S3开发板性能。

  • 传输模式要求:采用TCP+UDP混合模式,其中TCP负责传输控制信号(如通话发起、挂断、连接确认),保障信号可靠性;UDP负责传输音视频数据流,保障传输速度,实现无延迟传输(通话延迟≤100ms);具备UDP丢包重传机制,避免音视频卡顿、花屏;适配开发板网络特性,优化传输参数,减少网络资源占用。

  • 视频通话控制:支持启动、停止音视频通话,仅支持一对一连接,启动时自动建立TCP控制连接与UDP数据连接,启动后反馈“通话连接成功”信号,停止后断开TCP与UDP连接,反馈“通话断开”信号;启动失败时(如网络异常、设备未就绪、对方未响应),反馈通话失败信号给Controller;通话过程中若延迟超过100ms,反馈“通话延迟过高”信号,同时自动优化UDP传输参数,尝试降低延迟;适配Qt4.3音视频控制特性,优化通话启动、停止逻辑,减少资源占用。

  • B端视频展示:接收Controller的指令(条件达成时),启动B端摄像头,采集实时人像视频,通过UDP传输视频流,反馈“视频流就绪”信号,供View展示;停止时断开UDP视频流传输,反馈“视频流停止”信号;视频采集失败时,反馈视频流异常信号;视频画面分辨率不低于720P,帧率≥25帧/秒,确保画面清晰流畅;资源优化:视频流采用压缩编码,降低传输带宽与显存占用,不直接使用高分辨率原图,适配开发板性能;适配Qt4.3视频编码接口,优化编码效率,减少CPU占用。

  • 状态反馈:实时反馈音视频连接状态、视频流状态、TCP+UDP连接状态、通话延迟状态给Controller,确保Controller能及时调度并转发给View;音视频异常断开时,自动断开TCP与UDP连接,反馈断开信号,便于Controller处理后续逻辑;若TCP连接异常,自动尝试重连,重连失败则反馈“通话连接异常”信号;适配全志T113-S3开发板,优化状态检测逻辑,降低资源占用。

  • 接口要求:提供TCP+UDP连接初始化接口、通话启动/停止接口、视频流采集与传输接口、通话状态查询接口,供Controller调用;接口参数标准化,支持配置UDP传输端口、TCP控制端口,适配不同门禁设备网络环境;接口适配Qt4.3规范,确保可在开发板上交叉编译正常运行。

3.3.5 Qt4.3适配与开发板适配需求
  • Qt4.3适配:所有模块接口、控件使用均适配Qt4.3版本规范,禁止使用Qt4.3以上版本独有的API、控件及特性;优化信号槽机制、定时器、界面渲染等逻辑,适配Qt4.3的运行特性,确保代码可在Qt4.3环境下正常编译、运行。

  • 全志T113-S3开发板适配:支持在全志T113-S3开发板上进行交叉编译,编译配置适配开发板的CPU架构、内存大小、显示屏分辨率;优化代码逻辑,实现资源最小化,减少CPU、内存、显存占用,避免显示屏卡顿;适配开发板的硬件接口(摄像头、指纹仪、显示屏),确保各硬件设备正常调用;支持开发板的操作系统(如Linux),确保系统可在开发板上稳定运行24小时无异常。

  • 资源最小化实现:优化代码结构,删除冗余代码,减少内存占用;压缩所有图像、图标资源,适配开发板显示屏分辨率,避免高分辨率资源占用过多显存;简化控件样式与渲染逻辑,降低CPU占用;隐藏不常用组件,减少界面渲染压力;优化数据库操作、音视频传输逻辑,降低资源消耗,确保系统在开发板上流畅运行。

1.4模块联动需求(MVC闭环)

  1. 时间显示联动:TimeDisplay(Model)→ 定时发送时间信号 → AuthControl(Controller)→ 透传信号 → Widget(View)→ 实时更新时间显示;若TimeDisplay异常,Model发送错误信号 → Controller → 透传信号 → View → 显示时间异常提示;适配Qt4.3信号传递特性,优化联动逻辑,加快响应速度,适配开发板性能。

  2. 人脸开锁联动:Widget(View)→ 点击人脸开锁按钮 → 发送信号 → AuthControl(Controller)→ 调用生物识别模块采集人脸数据 → 生物识别模块(Model)采集数据并返回 → Controller调用数据库模块比对数据 → 数据库模块(Model)返回比对结果 → Controller转发信号 → Widget(View)→ 显示开锁结果(成功/失败);若设备未就绪,Model发送异常信号 → Controller → 透传信号 → View → 显示设备异常提示;优化联动逻辑,减少数据传输延迟,适配开发板性能。

  3. 用户注册联动:Widget(View)→ 管理员模式下触发显示用户注册控件 → 输入用户名、勾选人脸/指纹 → 点击注册按钮 → 发送信号 → AuthControl(Controller)→ 调用数据库模块查询用户名是否可用 → 数据库模块(Model)返回查询结果 → 用户名可用时,Controller调用生物识别模块采集对应数据 → 生物识别模块(Model)返回采集数据 → Controller调用数据库模块存储数据 → 数据库模块(Model)返回存储结果 → Controller转发信号 → Widget(View)→ 显示注册结果(成功/失败);用户名不可用时,Controller直接转发失败信号 → View → 显示提示;联动过程中优化资源占用,避免注册过程中出现卡顿。

  4. 音视频联动(TCP+UDP一对一无延迟):Widget(View)→ 点击启动视频按钮 → 发送信号 → AuthControl(Controller)→ 调用音视频模块,指定TCP+UDP混合模式发起一对一通话 → 音视频模块(Model)初始化TCP控制连接与UDP数据连接,尝试与B端建立连接 → 连接成功后,Model返回“通话连接成功”信号,同时启动B端摄像头采集视频流(压缩编码),通过UDP传输视频流,返回“视频流就绪”信号 → Controller转发信号 → Widget(View)→ 更新通话状态为“通话中”,显示B端视频画面(不使用高分辨率原图);通话过程中,Model实时检测通话延迟,若超过100ms,返回“通话延迟过高”信号 → Controller转发信号 → View → 显示延迟提示;点击停止按钮则反向联动,View发送停止信号 → Controller → 调用音视频模块停止通话,断开TCP与UDP连接 → Model返回“通话断开”信号 → Controller转发信号 → View → 停止视频显示、更新通话状态为“未通话”;若连接失败,Model返回“通话失败”信号 → Controller转发信号 → View → 显示失败提示;适配Qt4.3与开发板特性,优化联动逻辑,降低资源占用。

  5. 在线键盘与密码开锁联动:Widget(View)→ 点击密码输入区域 → 发送信号 → AuthControl(Controller)→ 调度View弹出在线键盘 → 用户通过在线键盘输入密码(支持退格删除)→ 点击确认按钮 → 发送密码信号 → Controller → 调用数据库模块进行密码比对 → 数据库模块(Model)返回比对结果 → Controller转发信号 → View → 显示开锁结果(成功/密码错误);输入完成或点击其他区域 → View发送信号 → Controller → 调度View收起在线键盘;优化联动逻辑,确保键盘弹出/收起、密码输入与比对响应流畅,无卡顿,适配开发板性能。

  6. 界面优化联动:AuthControl(Controller)→ 系统启动后,调度View隐藏不常用组件(用户注册控件)→ 实时监控界面渲染状态与开发板资源占用 → 若检测到卡顿,发送信号 → View → 简化界面渲染逻辑(暂停非必要控件刷新);管理员模式触发后 → Controller → 调度View显示用户注册控件;联动过程中实现资源动态调整,确保系统流畅运行。

1.5非功能需求

  • 可编译性:所有代码可直接编译运行,无语法错误、链接错误,适配Qt4.3环境(兼容Qt4.3及以上版本);支持在全志T113-S3开发板上交叉编译,编译配置适配开发板CPU架构与操作系统;编译后可正常生成可执行文件,无需额外依赖(除Qt4.3基础库、Sqlite库、开发板硬件驱动外);音视频模块需兼容TCP、UDP协议相关依赖,确保传输功能正常;资源文件(图标、图片)编译时自动压缩,适配开发板资源限制。

  • 稳定性:时间刷新无卡顿,生物识别、音视频调用、在线键盘操作无异常崩溃,数据库操作安全可靠,无数据丢失;TCP+UDP通话连接稳定,无频繁断开、重连现象,通话延迟稳定在100ms以内;系统在全志T113-S3开发板上连续运行24小时无异常,异常场景(如设备断开、网络中断、TCP/UDP连接异常、键盘操作异常)下可正常提示,不崩溃;资源占用稳定,CPU占用率≤30%,内存占用≤64MB,避免显示屏卡顿。

  • 可维护性:MVC分层清晰,各模块职责单一,代码注释完整,便于后续功能扩展(如新增密码开锁、用户权限管理、记录开锁日志、优化TCP+UDP传输性能);模块接口标准化,修改某一模块不影响其他模块正常运行,音视频模块的传输参数、界面控件的显示状态可灵活配置;代码适配Qt4.3规范,便于后续在开发板上进行调试与维护。

  • 易用性:界面操作简洁,反馈及时,符合门禁系统日常使用场景,无需复杂操作即可完成核心功能;在线键盘布局合理、操作便捷,支持退格删除、确认输入,适配开发板显示屏大小;针对管理员、普通用户,可预留界面权限区分(后续可扩展);音视频通话操作简单,启动/停止一键完成,通话状态清晰可见;不常用组件隐藏后,主界面简洁,操作便捷。

  • 安全性:生物信息(人脸、指纹)、用户密码以加密形式存储在Sqlite数据库中,防止数据泄露;开锁验证失败超过3次,可触发临时锁定(后续可扩展),提升门禁安全性;TCP+UDP传输过程中,对音视频数据、密码数据进行简单加密,防止数据被窃取、篡改,保障通话隐私与密码安全;在线键盘输入过程中,屏蔽非法输入,确保密码输入安全。

  • 网络适应性:TCP+UDP混合传输模式需适配不同网络环境(有线、无线),支持动态调整UDP传输速率,应对网络波动,确保通话无明显延迟、卡顿;在网络带宽≥1Mbps的环境下,音视频通话需保持流畅,无丢包、花屏现象;适配全志T113-S3开发板的网络接口,确保网络连接稳定。

  • 资源最小化:所有资源(图像、图标、代码、数据库)均进行优化压缩,实现资源最小化;界面渲染逻辑简化,隐藏不常用组件,降低CPU与显存占用;音视频流采用压缩编码,降低传输带宽与资源占用;代码冗余优化,减少内存占用,确保系统在全志T113-S3开发板上流畅运行,避免显示屏卡顿。

  • Qt4.3与开发板适配性:严格适配Qt4.3版本特性,禁止使用高版本API,确保代码可在Qt4.3环境下正常编译运行;适配全志T113-S3开发板的硬件接口、CPU性能、内存大小、显示屏分辨率,确保各硬件设备正常调用,系统运行流畅;交叉编译过程无报错,可执行文件在开发板上正常启动、运行,无兼容性问题。

1.6约束条件

  • 架构约束:严格遵循MVC设计模式,禁止View直接调用Model方法,禁止Model直接发送信号给View,所有交互必须通过Controller中转;禁止在View中编写业务逻辑、在Model中编写调度逻辑,确保分层职责清晰;音视频模块的TCP+UDP传输逻辑、在线键盘逻辑需封装在对应模块,禁止暴露给View;界面优化、资源最小化逻辑需融入各分层,确保不破坏MVC架构。

  • 技术约束:基于Qt4.3框架开发,使用C++语言,数据库采用Sqlite,音视频模块采用TCP+UDP混合模式实现一对一无延迟通话;禁止使用Qt4.3以上版本独有的API、控件及特性;支持在全志T113-S3开发板上交叉编译,编译工具、配置需适配开发板要求;代码风格统一,变量、函数命名规范,符合Qt4.3开发规范与开发板开发要求。

  • 界面约束:控件名称必须与需求中一致,确保信号槽正常连接;界面布局适配全志T113-S3开发板显示屏分辨率(建议不超过800×480),支持自适应;隐藏不常用组件,简化主界面,避免界面冗余;不直接使用高分辨率原图,所有图像、图标均需压缩适配;在线键盘布局简洁,按键大小适中,适配开发板操作场景;禁止添加与核心功能无关的控件,避免资源浪费与界面卡顿。

  • 资源约束:严格控制系统资源占用,CPU占用率≤30%,内存占用≤64MB,显存占用≤16MB,确保在全志T113-S3开发板上流畅运行;所有资源文件(图像、图标)大小总和不超过10MB,数据库文件大小不超过5MB;优化代码与逻辑,避免资源泄露,确保系统长期稳定运行。

  • 交叉编译约束:交叉编译环境需适配全志T113-S3开发板的CPU架构(如ARM架构),编译过程中需配置正确的编译器、链接器参数,确保生成的可执行文件可在开发板上正常运行;编译时需集成Qt4.3库、Sqlite库及开发板硬件驱动,确保无依赖缺失;禁止使用开发板不支持的硬件调用接口与软件特性。

二、各模块代码分析

2.1视图层

widget.h/.cpp

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTimer>
#include <QImage>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

    // 补充缺失接口,供Controller调用
    void updateTime(const QString &timeStr); // 更新时间显示
    void updateVideoFrame(const QImage &image); // 更新视频帧
    void updateCallStatus(const QString &status); // 更新通话状态
    void showResultTips(const QString &tips, bool isSuccess); // 显示提示信息
    void setRegisterWidgetVisible(bool visible); // 控制注册控件显示
    void setKeyBoardVisible(bool visible); // 控制键盘显示
    QString getPasswordText(); // 获取密码
    void clearPasswordText(); // 清空密码
    void setPasswordText(const QString &text); // 设置密码显示(补充缺失接口)

signals:
    // 发送操作信号给Controller
    void sigFaceUnlockClicked(); // 人脸开锁
    void sigFingerUnlockClicked(); // 指纹开锁
    void sigPasswordUnlockClicked(); // 密码开锁
    void sigStartVideoClicked(); // 启动通话
    void sigStopVideoClicked(); // 停止通话
    void sigRegisterClicked(QString userName, bool isFace, bool isFinger); // 用户注册
    void sigPasswordInputClicked(); // 密码输入区域点击
    void sigKeyBoardInput(QString text); // 键盘输入
    void sigKeyBoardBackspace(); // 键盘退格
    void sigKeyBoardConfirm(); // 键盘确认

private slots:
    // 按钮点击槽函数(自动生成)
    void on_btnFace_clicked();
    void on_btnFinger_clicked();
    void on_btnPassword_clicked();
    void on_btnStartVideo_clicked();
    void on_btnStopVideo_clicked();
    void on_btnRegister_clicked();
    void on_lineEditPassword_clicked();
    void on_btnKey0_clicked();
    void on_btnKey1_clicked();
    void on_btnKey2_clicked();
    void on_btnKey3_clicked();
    void on_btnKey4_clicked();
    void on_btnKey5_clicked();
    void on_btnKey6_clicked();
    void on_btnKey7_clicked();
    void on_btnKey8_clicked();
    void on_btnKey9_clicked();
    void on_btnKeyBack_clicked();
    void on_btnKeyConfirm_clicked();

    // 补充键盘自动隐藏槽函数
    void slotKeyBoardAutoHide();

private:
    Ui::Widget *ui;
    QTimer *m_keyBoardTimer; // 键盘自动收起定时器(补充缺失成员)

    // 重写鼠标点击事件
    void mousePressEvent(QMouseEvent *event);
};

#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QTimer>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 初始化键盘自动收起定时器(修复键盘无法自动隐藏bug)
    m_keyBoardTimer = new QTimer(this);
    m_keyBoardTimer->setInterval(5000); // 5秒无操作自动收起
    m_keyBoardTimer->setSingleShot(true); // 单次触发
    connect(m_keyBoardTimer, SIGNAL(timeout()), this, SLOT(slotKeyBoardAutoHide()));

    // 初始化控件状态(修复按钮状态异常bug)
    ui->btnStopVideo->setEnabled(false); // 初始状态:停止通话按钮不可用
    ui->widgetKeyBoard->setVisible(false); // 初始隐藏键盘
    ui->lineEdit->setVisible(false); // 初始隐藏注册控件
    ui->checkFace->setVisible(false);
    ui->checkFinger->setVisible(false);
    ui->btnRegister->setVisible(false);
}

Widget::~Widget()
{
    delete ui;
    delete m_keyBoardTimer; // 修复内存泄漏bug
}

// 补充缺失的setPasswordText接口(修复密码无法显示bug)
void Widget::setPasswordText(const QString &text)
{
    ui->lineEditPassword->setText(text);
}

// 补充键盘自动隐藏槽函数(修复键盘长期显示bug)
void Widget::slotKeyBoardAutoHide()
{
    setKeyBoardVisible(false);
}

// 更新时间显示(适配开发板显示格式)
void Widget::updateTime(const QString &timeStr)
{
    ui->labelTime->setText(timeStr);
}

// 更新视频帧显示(修复视频无法渲染bug,适配OpenCV与Qt图像转换)
void Widget::updateVideoFrame(const QImage &image)
{
    // 转换图像格式,适配开发板显示(修复图像拉伸、花屏bug)
    QImage scaledImage = image.scaled(ui->labelVideo->width(), ui->labelVideo->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
    ui->labelVideo->setPixmap(QPixmap::fromImage(scaledImage));
}

// 更新通话状态
void Widget::updateCallStatus(const QString &status)
{
    ui->labelCallStatus->setText("通话状态:" + status);
}

// 显示操作结果提示
void Widget::showResultTips(const QString &tips, bool isSuccess)
{
    QMessageBox msgBox;
    msgBox.setWindowTitle(isSuccess ? "操作成功" : "操作失败");
    msgBox.setIcon(isSuccess ? QMessageBox::Information : QMessageBox::Critical);
    msgBox.setText(tips);
    msgBox.setStandardButtons(QMessageBox::Ok);
    msgBox.exec();
}

// 控制用户注册控件显示/隐藏
void Widget::setRegisterWidgetVisible(bool visible)
{
    ui->lineEdit->setVisible(visible);
    ui->checkFace->setVisible(visible);
    ui->checkFinger->setVisible(visible);
    ui->btnRegister->setVisible(visible);
}

// 控制在线键盘显示/隐藏
void Widget::setKeyBoardVisible(bool visible)
{
    ui->widgetKeyBoard->setVisible(visible);
    if (visible)
    {
        m_keyBoardTimer->start(); // 启动自动收起定时器
    }
    else
    {
        m_keyBoardTimer->stop(); // 停止定时器
    }
}

// 获取密码输入框内容
QString Widget::getPasswordText()
{
    return ui->lineEditPassword->text();
}

// 清空密码输入框
void Widget::clearPasswordText()
{
    ui->lineEditPassword->clear();
}

// 人脸开锁按钮点击
void Widget::on_btnFace_clicked()
{
    emit sigFaceUnlockClicked();
}

// 指纹开锁按钮点击
void Widget::on_btnFinger_clicked()
{
    emit sigFingerUnlockClicked();
}

// 密码开锁按钮点击
void Widget::on_btnPassword_clicked()
{
    emit sigPasswordUnlockClicked();
}

// 启动视频通话按钮点击
void Widget::on_btnStartVideo_clicked()
{
    ui->btnStartVideo->setEnabled(false);
    ui->btnStopVideo->setEnabled(true);
    emit sigStartVideoClicked();
}

// 停止视频通话按钮点击
void Widget::on_btnStopVideo_clicked()
{
    ui->btnStopVideo->setEnabled(false);
    ui->btnStartVideo->setEnabled(true);
    emit sigStopVideoClicked();
}

// 注册按钮点击
void Widget::on_btnRegister_clicked()
{
    QString userName = ui->lineEdit->text().trimmed();
    bool isFace = ui->checkFace->isChecked();
    bool isFinger = ui->checkFinger->isChecked();
    emit sigRegisterClicked(userName, isFace, isFinger);
}

// 密码输入区域点击(触发键盘弹出)
void Widget::on_lineEditPassword_clicked()
{
    setKeyBoardVisible(true);
    m_keyBoardTimer->start(); // 重置自动收起定时器
}

// 在线键盘数字0点击
void Widget::on_btnKey0_clicked()
{
    emit sigKeyBoardInput("0");
    m_keyBoardTimer->start();
}

// 在线键盘数字1点击
void Widget::on_btnKey1_clicked()
{
    emit sigKeyBoardInput("1");
    m_keyBoardTimer->start();
}

// 在线键盘数字2点击
void Widget::on_btnKey2_clicked()
{
    emit sigKeyBoardInput("2");
    m_keyBoardTimer->start();
}

// 在线键盘数字3点击
void Widget::on_btnKey3_clicked()
{
    emit sigKeyBoardInput("3");
    m_keyBoardTimer->start();
}

// 在线键盘数字4点击
void Widget::on_btnKey4_clicked()
{
    emit sigKeyBoardInput("4");
    m_keyBoardTimer->start();
}

// 在线键盘数字5点击
void Widget::on_btnKey5_clicked()
{
    emit sigKeyBoardInput("5");
    m_keyBoardTimer->start();
}

// 在线键盘数字6点击
void Widget::on_btnKey6_clicked()
{
    emit sigKeyBoardInput("6");
    m_keyBoardTimer->start();
}

// 在线键盘数字7点击
void Widget::on_btnKey7_clicked()
{
    emit sigKeyBoardInput("7");
    m_keyBoardTimer->start();
}

// 在线键盘数字8点击
void Widget::on_btnKey8_clicked()
{
    emit sigKeyBoardInput("8");
    m_keyBoardTimer->start();
}

// 在线键盘数字9点击
void Widget::on_btnKey9_clicked()
{
    emit sigKeyBoardInput("9");
    m_keyBoardTimer->start();
}

// 在线键盘退格点击
void Widget::on_btnKeyBack_clicked()
{
    emit sigKeyBoardBackspace();
    m_keyBoardTimer->start();
}

// 在线键盘确认点击
void Widget::on_btnKeyConfirm_clicked()
{
    emit sigKeyBoardConfirm();
    setKeyBoardVisible(false);
}

// 重写鼠标点击事件,点击非密码区域收起键盘
void Widget::mousePressEvent(QMouseEvent *event)
{
    if (!ui->lineEditPassword->geometry().contains(event->pos()) 
        && !ui->widgetKeyBoard->geometry().contains(event->pos()))
    {
        setKeyBoardVisible(false);
    }
    QWidget::mousePressEvent(event);
}

1.1.1功能描述

1.1.2代码实现

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

2.2控制层

AuthControl.h/.cpp

#ifndef AUTHCONTROL_H
#define AUTHCONTROL_H
#include <QObject>
#include "timedisplay.h"
#include "audiomanager.h"
#include "cameramanager.h"
#include "videocontrol.h"
#include "fingerprint.h"
#include "facedetect.h"
#include "sqlitedb.h"
#include "uithread.h"
#include <opencv2/opencv.hpp>

// 控制层核心,总控所有模块,接收View信号,调用Model方法,转发结果给View
// 严格遵循MVC,不允许View直接调用Model,所有联动通过此类中转
class AuthControl : public QObject
{
    Q_OBJECT
public:
    explicit AuthControl(QObject *parent = 0);
    ~AuthControl();
    // 绑定View,建立信号槽连接
    void bindView(QWidget *view);
    // 系统初始化(初始化所有Model模块,适配全志T113-S3)
    bool initSystem();

signals:
    // 开门控制信号(发给底层硬件,控制门锁开启)
    void sigOpenDoor();
    // 密码输入信号(转发在线键盘输入)
    void sigPasswordInput(QString text);
    // 密码退格信号(转发在线键盘退格)
    void sigPasswordBackspace();

private slots:
    // 接收View操作信号,处理对应业务逻辑
    void slotFaceUnlockClicked();      // 人脸开锁
    void slotFingerUnlockClicked();   // 指纹开锁
    void slotPasswordUnlockClicked(); // 密码开锁
    void slotStartVideoClicked();     // 启动视频通话
    void slotStopVideoClicked();      // 停止视频通话
    void slotRegisterClicked(QString userName, bool isFace, bool isFinger); // 用户注册
    void slotPasswordInputClicked();  // 密码输入区域点击
    void slotKeyBoardInput(QString text); // 在线键盘输入
    void slotKeyBoardBackspace();     // 在线键盘退格
    void slotKeyBoardConfirm();       // 在线键盘确认

    // 接收Model信号,转发给View
    void slotTimeUpdated(QString timeStr); // 时间更新信号
    void slotCameraFrameCaptured(cv::Mat frame); // 摄像头帧信号
    void slotFaceDetectResult(bool isDetected, int faceCount); // 人脸检测结果
    void slotFingerDetectResult(bool isMatched); // 指纹比对结果
    void slotVideoStatus(bool isCalling, QString statusMsg); // 音视频状态
    void slotVideoFrameReceived(cv::Mat frame); // 接收视频帧
    void slotAudioError(QString errorMsg); // 音频异常
    void slotCameraError(QString errorMsg); // 摄像头异常
    void slotDbResult(bool isSuccess, QString msg); // 数据库操作结果

    // 视频帧渲染完成,转发给View
    void slotFrameRendered(QImage image);

private:
    // Model模块实例(由Controller统一管理)
    TimeDisplay *m_timeDisplay;       // 时间模块
    AudioManager *m_audioManager;     // 音频模块
    CameraManager *m_cameraManager;   // 摄像头模块
    VideoControl *m_videoControl;     // 音视频模块
    Fingerprint *m_fingerprint;       // 指纹模块
    FaceDetect *m_faceDetect;         // 人脸检测模块
    SqliteDb *m_sqliteDb;             // 数据库模块
    UIThread *m_uiThread;             // UI优化线程

    QWidget *m_view;                  // 绑定的View实例
    QString m_currentPassword;        // 当前输入的密码

    // 开门控制(触发开锁信号,控制硬件开锁,延时后复位)
    void openDoor();
    // 校验用户名合法性
    bool checkUserName(const QString &userName);
};

#endif // AUTHCONTROL_H
#include "authcontrol.h"
#include "widget.h"
#include <QTimer>
#include <QMutex>

AuthControl::AuthControl(QObject *parent) : QObject(parent)
{
    // 初始化所有Model模块(独立封装,由Controller统一管理)
    m_timeDisplay = new TimeDisplay(this);
    m_audioManager = new AudioManager(this);
    m_cameraManager = new CameraManager(this);
    m_videoControl = new VideoControl(this);
    m_fingerprint = new Fingerprint(this);
    m_faceDetect = new FaceDetect(this);
    m_sqliteDb = new SqliteDb(this);
    m_uiThread = new UIThread(this);
    m_view = NULL;
    m_currentPassword = "";

    // 绑定Model信号(接收Model处理结果,转发给View)
    connect(m_timeDisplay, SIGNAL(sigTimeUpdated(QString)), this, SLOT(slotTimeUpdated(QString)));
    connect(m_cameraManager, SIGNAL(sigCaptureFrame(cv::Mat)), this, SLOT(slotCameraFrameCaptured(cv::Mat)));
    connect(m_cameraManager, SIGNAL(sigFaceDetectResult(bool, int)), this, SLOT(slotFaceDetectResult(bool, int)));
    connect(m_cameraManager, SIGNAL(sigCameraError(QString)), this, SLOT(slotCameraError(QString)));
    connect(m_fingerprint, SIGNAL(sigFingerResult(bool)), this, SLOT(slotFingerDetectResult(bool)));
    connect(m_videoControl, SIGNAL(sigVideoStatus(bool, QString)), this, SLOT(slotVideoStatus(bool, QString)));
    connect(m_videoControl, SIGNAL(sigRecvVideoFrame(cv::Mat)), this, SLOT(slotVideoFrameReceived(cv::Mat)));
    connect(m_audioManager, SIGNAL(sigAudioError(QString)), this, SLOT(slotAudioError(QString)));
    connect(m_sqliteDb, SIGNAL(sigDbResult(bool, QString)), this, SLOT(slotDbResult(bool, QString)));
    connect(m_uiThread, SIGNAL(sigFrameRendered(QImage)), this, SLOT(slotFrameRendered(QImage)));

    // 启动UI优化线程
    m_uiThread->start();
}

AuthControl::~AuthControl()
{
    // 释放所有Model模块资源
    delete m_timeDisplay;
    delete m_audioManager;
    delete m_cameraManager;
    delete m_videoControl;
    delete m_fingerprint;
    delete m_faceDetect;
    delete m_sqliteDb;
    m_uiThread->stopThread();
    m_uiThread->wait();
    delete m_uiThread;
}

// 绑定View,建立信号槽连接(View→Controller)
void AuthControl::bindView(QWidget *view)
{
    if (view == NULL)
        return;
    m_view = view;
    Widget *widget = dynamic_cast<Widget*>(view);
    if (widget == NULL)
        return;

    // 绑定View操作信号
    connect(widget, SIGNAL(sigFaceUnlockClicked()), this, SLOT(slotFaceUnlockClicked()));
    connect(widget, SIGNAL(sigFingerUnlockClicked()), this, SLOT(slotFingerUnlockClicked()));
    connect(widget, SIGNAL(sigPasswordUnlockClicked()), this, SLOT(slotPasswordUnlockClicked()));
    connect(widget, SIGNAL(sigStartVideoClicked()), this, SLOT(slotStartVideoClicked()));
    connect(widget, SIGNAL(sigStopVideoClicked()), this, SLOT(slotStopVideoClicked()));
    connect(widget, SIGNAL(sigRegisterClicked(QString, bool, bool)), this, SLOT(slotRegisterClicked(QString, bool, bool)));
    connect(widget, SIGNAL(sigPasswordInputClicked()), this, SLOT(slotPasswordInputClicked()));
    connect(widget, SIGNAL(sigKeyBoardInput(QString)), this, SLOT(slotKeyBoardInput(QString)));
    connect(widget, SIGNAL(sigKeyBoardBackspace()), this, SLOT(slotKeyBoardBackspace()));
    connect(widget, SIGNAL(sigKeyBoardConfirm()), this, SLOT(slotKeyBoardConfirm()));
}

// 系统初始化(初始化所有模块,适配全志T113-S3)
bool AuthControl::initSystem()
{
    // 初始化数据库
    if (!m_sqliteDb->initDb())
    {
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("数据库初始化失败,系统无法启动", false);
        }
        return false;
    }

    // 初始化音频设备(3.3mm耳机+USB音响)
    if (!m_audioManager->initAudio())
    {
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("音频设备初始化失败", false);
        }
        return false;
    }

    // 初始化摄像头(USB摄像头,v4l2驱动)
    if (!m_cameraManager->initCamera())
    {
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("摄像头初始化失败", false);
        }
        return false;
    }

    // 初始化音视频模块(绑定音频模块)
    if (!m_videoControl->initVideoAudio(m_audioManager))
    {
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("音视频模块初始化失败", false);
        }
        return false;
    }

    // 初始化指纹模块
    if (!m_fingerprint->initFingerprint())
    {
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("指纹模块初始化失败", false);
        }
        return false;
    }

    // 启动时间模块、摄像头采集
    m_timeDisplay->startTime();
    m_cameraManager->startCapture();
    m_cameraManager->setFaceDetectEnable(true); // 启用人脸识别

    if (m_view)
    {
        Widget *widget = dynamic_cast<Widget*>(m_view);
        widget->showResultTips("系统初始化成功,可正常使用", true);
    }
    return true;
}

// 人脸开锁业务逻辑
void AuthControl::slotFaceUnlockClicked()
{
    if (!m_cameraManager->isCapturing())
    {
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("摄像头未启动,无法进行人脸开锁", false);
        }
        return;
    }

    // 提示用户对准摄像头
    if (m_view)
    {
        Widget *widget = dynamic_cast<Widget*>(m_view);
        widget->showResultTips("请对准摄像头,正在进行人脸比对...", true);
    }

    // 调用人脸检测模块进行比对(模拟,实际对接数据库)
    // 此处简化,实际需调用m_sqliteDb->compareFaceData()进行比对
    QTimer::singleShot(1000, this, [=]() {
        if (m_faceDetect->isFaceMatched())
        {
            openDoor(); // 比对成功,控制开门
            if (m_view)
            {
                Widget *widget = dynamic_cast<Widget*>(m_view);
                widget->showResultTips("人脸比对成功,已开锁", true);
            }
        }
        else
        {
            if (m_view)
            {
                Widget *widget = dynamic_cast<Widget*>(m_view);
                widget->showResultTips("人脸比对失败,无法开锁", false);
            }
        }
    });
}

// 指纹开锁业务逻辑
void AuthControl::slotFingerUnlockClicked()
{
    if (!m_fingerprint->isReady())
    {
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("指纹模块未就绪,无法进行指纹开锁", false);
        }
        return;
    }

    // 提示用户按压指纹
    if (m_view)
    {
        Widget *widget = dynamic_cast<Widget*>(m_view);
        widget->showResultTips("请按压指纹,正在进行指纹比对...", true);
    }

    // 调用指纹模块进行采集比对
    m_fingerprint->startDetect();
}

// 密码开锁业务逻辑
void AuthControl::slotPasswordUnlockClicked()
{
    if (m_currentPassword.isEmpty())
    {
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("请先输入密码", false);
        }
        return;
    }

    // 调用数据库模块进行密码比对
    bool isMatched = m_sqliteDb->comparePassword(m_currentPassword);
    if (isMatched)
    {
        openDoor(); // 密码匹配,控制开门
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("密码正确,已开锁", true);
            widget->clearPasswordText();
        }
        m_currentPassword.clear();
    }
    else
    {
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("密码错误,无法开锁", false);
        }
    }
}

// 启动视频通话业务逻辑
void AuthControl::slotStartVideoClicked()
{
    // 此处简化,实际需获取对方IP(可通过配置文件或界面输入)
    QString peerIp = "192.168.1.100"; // 示例IP,可根据实际修改
    m_videoControl->startVideo(peerIp, 8888, 8889);
}

// 停止视频通话业务逻辑
void AuthControl::slotStopVideoClicked()
{
    m_videoControl->stopVideo();
    if (m_view)
    {
        Widget *widget = dynamic_cast<Widget*>(m_view);
        widget->updateCallStatus("未通话");
    }
}

// 用户注册业务逻辑
void AuthControl::slotRegisterClicked(QString userName, bool isFace, bool isFinger)
{
    // 校验用户名
    if (!checkUserName(userName))
    {
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("用户名不能为空或已存在", false);
        }
        return;
    }

    // 校验是否选择人脸/指纹录入
    if (!isFace && !isFinger)
    {
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("请至少选择一种生物信息录入", false);
        }
        return;
    }

    // 模拟采集生物信息(实际需调用对应模块采集)
    QByteArray faceData;
    QByteArray fingerData;
    if (isFace)
    {
        faceData = m_faceDetect->captureFaceData(); // 采集人脸数据
        if (faceData.isEmpty())
        {
            if (m_view)
            {
                Widget *widget = dynamic_cast<Widget*>(m_view);
                widget->showResultTips("人脸采集失败,请重试", false);
            }
            return;
        }
    }
    if (isFinger)
    {
        fingerData = m_fingerprint->captureFingerData(); // 采集指纹数据
        if (fingerData.isEmpty())
        {
            if (m_view)
            {
                Widget *widget = dynamic_cast<Widget*>(m_view);
                widget->showResultTips("指纹采集失败,请重试", false);
            }
            return;
        }
    }

    // 调用数据库模块存储用户信息
    m_sqliteDb->insertUser(userName, faceData, fingerData, "123456"); // 默认密码123456,可修改
}

// 密码输入区域点击(弹出键盘)
void AuthControl::slotPasswordInputClicked()
{
    if (m_view)
    {
        Widget *widget = dynamic_cast<Widget*>(m_view);
        widget->setKeyBoardVisible(true);
    }
}

// 在线键盘输入处理
void AuthControl::slotKeyBoardInput(QString text)
{
    m_currentPassword += text;
    if (m_view)
    {
        Widget *widget = dynamic_cast<Widget*>(m_view);
        widget->setPasswordText(m_currentPassword); // 需在Widget中新增setPasswordText接口
    }
}

// 在线键盘退格处理
void AuthControl::slotKeyBoardBackspace()
{
    if (!m_currentPassword.isEmpty())
    {
        m_currentPassword.chop(1);
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->setPasswordText(m_currentPassword);
        }
    }
}

// 在线键盘确认处理
void AuthControl::slotKeyBoardConfirm()
{
    slotPasswordUnlockClicked(); // 确认后自动进行密码比对
}

// 接收时间更新信号,转发给View
void AuthControl::slotTimeUpdated(QString timeStr)
{
    if (m_view)
    {
        Widget *widget = dynamic_cast<Widget*>(m_view);
        widget->updateTime(timeStr);
    }
}

// 接收摄像头帧信号,交给UI线程渲染
void AuthControl::slotCameraFrameCaptured(cv::Mat frame)
{
    m_uiThread->setRenderFrame(frame);
}

// 接收人脸检测结果,处理后续逻辑
void AuthControl::slotFaceDetectResult(bool isDetected, int faceCount)
{
    Q_UNUSED(faceCount);
    if (isDetected)
    {
        // 检测到人脸,可进行比对(此处简化)
    }
}

// 接收指纹比对结果,处理开锁逻辑
void AuthControl::slotFingerDetectResult(bool isMatched)
{
    if (isMatched)
    {
        openDoor(); // 指纹匹配,控制开门
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("指纹比对成功,已开锁", true);
        }
    }
    else
    {
        if (m_view)
        {
            Widget *widget = dynamic_cast<Widget*>(m_view);
            widget->showResultTips("指纹比对失败,无法开锁", false);
        }
    }
}

// 接收音视频状态信号,转发给View
void AuthControl::slotVideoStatus(bool isCalling, QString statusMsg)
{
    if (m_view)
    {
        Widget *widget = dynamic_cast<Widget*>(m_view);
        widget->updateCallStatus(statusMsg);
    }
}

// 接收音视频通话的视频帧,交给UI线程渲染
void AuthControl::slotVideoFrameReceived(cv::Mat frame)
{
    m_uiThread->setRenderFrame(frame);
}

// 接收音频异常信号,转发给View
void AuthControl::slotAudioError(QString errorMsg)
{
    if (m_view)
    {
        Widget *widget = dynamic_cast<Widget*>(m_view);
        widget->showResultTips("音频异常:" + errorMsg, false);
    }
}

// 接收摄像头异常信号,转发给View
void AuthControl::slotCameraError(QString errorMsg)
{
    if (m_view)
    {
        Widget *widget = dynamic_cast<Widget*>(m_view);
        widget->showResultTips("摄像头异常:" + errorMsg, false);
    }
}

// 接收数据库操作结果,转发给View
void AuthControl::slotDbResult(bool isSuccess, QString msg)
{
    if (m_view)
    {
        Widget *widget = dynamic_cast<Widget*>(m_view);
        widget->showResultTips(msg, isSuccess);
    }
}

// 视频帧渲染完成,转发给View显示
void AuthControl::slotFrameRendered(QImage image)
{
    if (m_view)
    {
        Widget *widget = dynamic_cast<Widget*>(m_view);
        widget->updateVideoFrame(image);
    }
}

// 开门控制逻辑(触发开锁信号,延时3秒后复位)
void AuthControl::openDoor()
{
    emit sigOpenDoor(); // 发送开门信号,控制底层硬件
    // 延时3秒后,可添加门锁复位逻辑(根据实际硬件调整)
    QTimer::singleShot(3000, this, []() {
        // 门锁复位逻辑(示例,实际对接硬件)
    });
}

// 校验用户名合法性(非空、不重复)
bool AuthControl::checkUserName(const QString &userName)
{
    if (userName.isEmpty())
        return false;
    // 调用数据库查询用户名是否已存在
    return !m_sqliteDb->isUserNameExists(userName);
}

2.2.1 模块描述

2.2.2功能实现

2.3模块层

audiocontrol.h/.cpp

#ifndef AUDIOMANAGER_H
#define AUDIOMANAGER_H
#include <QObject>
#include <alsa/asoundlib.h>

// 音频管理模块,独立封装,基于ALSA驱动
// 输入设备:3.3mm耳机;输出设备:USB音响;支持录音、播放、音量调节
class AudioManager : public QObject
{
    Q_OBJECT
public:
    explicit AudioManager(QObject *parent = 0);
    ~AudioManager();
    // 初始化音频设备(输入:3.3mm耳机,输出:USB音响)
    bool initAudio();
    // 启动录音(3.3mm耳机输入)
    void startRecord();
    // 停止录音
    void stopRecord();
    // 播放音频(USB音响输出)
    void playAudio(const char *audioData, int dataLen);
    // 调节音量(0-100)
    void setVolume(int volume);

signals:
    // 音频状态信号(传给Controller)
    void sigAudioStatus(bool isRecording);
    void sigAudioError(QString errorMsg);

private:
    snd_pcm_t *m_pcmIn;  // 录音设备(3.3mm耳机)
    snd_pcm_t *m_pcmOut; // 播放设备(USB音响)
    bool m_isRecording;  // 录音状态标记
    int m_volume;        // 音量值(0-100)
    // 音频参数配置(适配Qt4.12.2,低功耗,适配开发板)
    void configAudioParams(snd_pcm_t *pcm, bool isInput);
    // 录音线程函数(内部调用,不对外暴露)
    void slotRecordThread();
};

#endif // AUDIOMANAGER_H

#include "audiomanager.h"
#include <stdlib.h>
#include <string.h>
#include <QThread>

AudioManager::AudioManager(QObject *parent) : QObject(parent)
{
    m_pcmIn = NULL;
    m_pcmOut = NULL;
    m_isRecording = false;
    m_volume = 50; // 默认音量50
}

AudioManager::~AudioManager()
{
    stopRecord();
    if (m_pcmIn != NULL)
    {
        snd_pcm_close(m_pcmIn);
        m_pcmIn = NULL;
    }
    if (m_pcmOut != NULL)
    {
        snd_pcm_close(m_pcmOut);
        m_pcmOut = NULL;
    }
}

// 配置音频参数(采样率、声道、格式,适配开发板,匹配3.3mm耳机和USB音响)
void AudioManager::configAudioParams(snd_pcm_t *pcm, bool isInput)
{
    snd_pcm_hw_params_t *params;
    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(pcm, params);
    // 采样率:8000Hz(低功耗,适配开发板,兼容3.3mm耳机和USB音响)
    snd_pcm_hw_params_set_rate_near(pcm, params, 8000, NULL);
    // 声道:单声道(适配门禁场景,降低资源占用)
    snd_pcm_hw_params_set_channels(pcm, params, 1);
    // 格式:16位小端(通用格式,兼容ALSA驱动和外设)
    snd_pcm_hw_params_set_format(pcm, params, SND_PCM_FORMAT_S16_LE);
    // 应用参数
    snd_pcm_hw_params(pcm, params);
}

// 初始化音频设备(输入:3.3mm耳机,输出:USB音响)
bool AudioManager::initAudio()
{
    // 打开录音设备(3.3mm耳机,设备名根据开发板实际调整,默认plughw:0,0)
    if (snd_pcm_open(&m_pcmIn, "plughw:0,0", SND_PCM_STREAM_CAPTURE, 0) < 0)
    {
        emit sigAudioError("录音设备(3.3mm耳机)打开失败");
        return false;
    }
    // 打开播放设备(USB音响,设备名根据开发板实际调整,默认plughw:1,0)
    if (snd_pcm_open(&m_pcmOut, "plughw:1,0", SND_PCM_STREAM_PLAYBACK, 0) < 0)
    {
        snd_pcm_close(m_pcmIn);
        m_pcmIn = NULL;
        emit sigAudioError("播放设备(USB音响)打开失败");
        return false;
    }
    // 配置音频参数(分别适配耳机输入和音响输出)
    configAudioParams(m_pcmIn, true);
    configAudioParams(m_pcmOut, false);
    return true;
}

// 启动录音(线程中执行,避免阻塞UI,3.3mm耳机采集音频)
void AudioManager::startRecord()
{
    if (m_pcmIn == NULL || m_isRecording)
        return;

    m_isRecording = true;
    emit sigAudioStatus(true);

    // 启动录音线程(Qt4.12.2合规,不阻塞UI)
    QThread *recordThread = new QThread(this);
    connect(recordThread, SIGNAL(started()), this, SLOT(slotRecordThread()));
    connect(recordThread, SIGNAL(finished()), recordThread, SLOT(deleteLater()));
    recordThread->start();
}

// 录音线程函数(内部调用,不对外暴露,3.3mm耳机读取音频数据)
void AudioManager::slotRecordThread()
{
    char buffer[1024]; // 录音缓冲区,适配开发板内存
    while (m_isRecording)
    {
        // 读取录音数据(3.3mm耳机输入)
        int ret = snd_pcm_readi(m_pcmIn, buffer, sizeof(buffer)/2);
        if (ret < 0)
        {
            emit sigAudioError("录音失败(3.3mm耳机采集异常)");
            m_isRecording = false;
            emit sigAudioStatus(false);
            break;
        }
        // 录音数据可用于后续处理(如音视频通话,传给videocontrol)
        // 此处暂存,实际可对接TCP/UDP传输
    }
}

// 停止录音
void AudioManager::stopRecord()
{
    if (m_isRecording)
    {
        m_isRecording = false;
        emit sigAudioStatus(false);
    }
}

// 播放音频(USB音响输出)
void AudioManager::playAudio(const char *audioData, int dataLen)
{
    if (m_pcmOut == NULL || dataLen <= 0)
        return;

    // 调节音量(简单实现,根据m_volume缩放音频数据,适配USB音响)
    char *playBuffer = new char[dataLen];
    memcpy(playBuffer, audioData, dataLen);
    for (int i = 0; i < dataLen; i += 2)
    {
        short *sample = (short*)(playBuffer + i);
        *sample = *sample * m_volume / 100;
    }

    // 播放音频(USB音响输出)
    int ret = snd_pcm_writei(m_pcmOut, playBuffer, dataLen/2);
    if (ret < 0)
    {
        emit sigAudioError("播放失败(USB音响输出异常)");
    }

    delete[] playBuffer;
    playBuffer = NULL;
}

// 调节音量(0-100,适配USB音响音量范围)
void AudioManager::setVolume(int volume)
{
    if (volume < 0) volume = 0;
    if (volume > 100) volume = 100;
    m_volume = volume;
}

cameracontrol.h/.cpp

#ifndef CAMERAMANAGER_H
#define CAMERAMANAGER_H
#include <QObject>
#include <QThread>
#include <v4l2/videodev2.h>
#include <opencv2/opencv.hpp>

// 摄像头管理模块,独立封装,v4l2驱动,USB摄像头
// 集成OpenCV haar轻量级人脸识别,画质压缩,避免UI阻塞
class CameraManager : public QObject
{
    Q_OBJECT
public:
    explicit CameraManager(QObject *parent = 0);
    ~CameraManager();
    // 初始化USB摄像头(v4l2驱动,默认设备路径/dev/video0)
    bool initCamera(const char *devPath = "/dev/video0");
    // 启动摄像头采集
    void startCapture();
    // 停止摄像头采集
    void stopCapture();
    // 开启/关闭人脸识别(haar轻量级算法)
    void setFaceDetectEnable(bool enable);
    // 画质压缩(设置分辨率,降低渲染压力,适配开发板)
    void setCompressParam(int width = 320, int height = 240);

signals:
    // 摄像头采集帧信号(压缩后,传给UI显示)
    void sigCaptureFrame(cv::Mat frame);
    // 人脸识别结果信号(传给Controller,用于开锁判断)
    void sigFaceDetectResult(bool isDetected, int faceCount);
    // 摄像头状态信号(传给Controller,用于UI提示)
    void sigCameraStatus(bool isCapturing);
    void sigCameraError(QString errorMsg);

private slots:
    // 采集线程函数,避免阻塞UI
    void slotCaptureThread();
    // 人脸识别处理(轻量级haar算法,适配开发板性能)
    void slotFaceDetect(cv::Mat frame);

private:
    int m_cameraFd;              // 摄像头文件描述符(v4l2,USB摄像头)
    QThread *m_captureThread;    // 采集线程,分离UI线程
    bool m_isCapturing;          // 采集状态标记
    bool m_isFaceDetectEnable;   // 人脸识别启用标记
    cv::CascadeClassifier m_faceClassifier; // haar人脸分类器(轻量级)
    int m_compressWidth;         // 压缩后宽度
    int m_compressHeight;        // 压缩后高度
    // v4l2摄像头参数配置(适配USB摄像头)
    bool configCameraParams();
    // 读取摄像头帧数据(v4l2格式转换)
    bool readCameraFrame(cv::Mat &frame);
    // 画质压缩处理(降低分辨率,减少渲染压力,适配开发板)
    cv::Mat compressFrame(cv::Mat frame);
};

#endif // CAMERAMANAGER_H
#include "cameramanager.h"
#include <unistd.h>
#include <string.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/objdetect/objdetect.hpp>

CameraManager::CameraManager(QObject *parent) : QObject(parent)
{
    m_cameraFd = -1;
    m_captureThread = NULL;
    m_isCapturing = false;
    m_isFaceDetectEnable = false;
    m_compressWidth = 320;
    m_compressHeight = 240;

    // 加载haar轻量级人脸分类器(适配开发板,选择小模型,降低算力占用)
    // 分类器路径根据开发板实际路径调整,建议放在项目根目录
    if (!m_faceClassifier.load("haarcascade_frontalface_default.xml"))
    {
        emit sigCameraError("人脸分类器加载失败(haar轻量级模型)");
    }
}

CameraManager::~CameraManager()
{
    stopCapture();
    if (m_cameraFd != -1)
    {
        close(m_cameraFd);
        m_cameraFd = -1;
    }
    if (m_captureThread != NULL && m_captureThread->isRunning())
    {
        m_captureThread->quit();
        m_captureThread->wait();
        delete m_captureThread;
        m_captureThread = NULL;
    }
}

// v4l2摄像头参数配置(适配USB摄像头,低分辨率,降低资源占用)
bool CameraManager::configCameraParams()
{
    struct v4l2_format fmt;
    memset(&fmt, 0, sizeof(fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = m_compressWidth;  // 采集宽度(与压缩宽度一致,减少处理)
    fmt.fmt.pix.height = m_compressHeight;// 采集高度(与压缩高度一致)
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // 像素格式,适配USB摄像头
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

    if (ioctl(m_cameraFd, VIDIOC_S_FMT, &fmt) == -1)
    {
        emit sigCameraError("摄像头参数配置失败(v4l2格式设置异常)");
        return false;
    }

    // 申请缓冲区(3个缓冲区,避免卡顿,适配USB摄像头采集)
    struct v4l2_requestbuffers req;
    memset(&req, 0, sizeof(req));
    req.count = 3;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;

    if (ioctl(m_cameraFd, VIDIOC_REQBUFS, &req) == -1)
    {
        emit sigCameraError("缓冲区申请失败(v4l2缓冲区异常)");
        return false;
    }

    // 映射缓冲区(简化实现,适配开发板内存)
    for (int i = 0; i < req.count; i++)
    {
        struct v4l2_buffer buf;
        memset(&buf, 0, sizeof(buf));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;

        if (ioctl(m_cameraFd, VIDIOC_QUERYBUF, &buf) == -1)
        {
            emit sigCameraError("缓冲区映射失败(v4l2内存映射异常)");
            return false;
        }

        // 启动缓冲区
        if (ioctl(m_cameraFd, VIDIOC_QBUF, &buf) == -1)
        {
            emit sigCameraError("缓冲区启动失败(v4l2缓冲区启动异常)");
            return false;
        }
    }

    // 启动采集(v4l2流启动,USB摄像头开始采集)
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(m_cameraFd, VIDIOC_STREAMON, &type) == -1)
    {
        emit sigCameraError("摄像头采集启动失败(v4l2流启动异常)");
        return false;
    }

    return true;
}

// 初始化USB摄像头(v4l2驱动)
bool CameraManager::initCamera(const char *devPath)
{
    // 打开USB摄像头设备(默认路径/dev/video0,可根据实际调整)
    m_cameraFd = open(devPath, O_RDWR);
    if (m_cameraFd == -1)
    {
        emit sigCameraError("USB摄像头打开失败(检查设备路径:" + QString(devPath) + ")");
        return false;
    }

    // 配置摄像头参数(v4l2参数,适配USB摄像头)
    if (!configCameraParams())
    {
        close(m_cameraFd);
        m_cameraFd = -1;
        return false;
    }

    return true;
}

// 画质压缩处理(降低分辨率,减少渲染压力,适配开发板显示屏)
cv::Mat CameraManager::compressFrame(cv::Mat frame)
{
    cv::Mat compressFrame;
    // 缩放至预设压缩分辨率,INTER_LINEAR插值,兼顾速度与画质
    cv::resize(frame, compressFrame, cv::Size(m_compressWidth, m_compressHeight), 0, 0, cv::INTER_LINEAR);
    // 转换为灰度图(进一步降低渲染压力,人脸识别也可使用灰度图,提升效率)
    cv::cvtColor(compressFrame, compressFrame, CV_BGR2GRAY);
    return compressFrame;
}

// 人脸识别处理(轻量级haar算法,适配开发板性能,降低算力占用)
void CameraManager::slotFaceDetect(cv::Mat frame)
{
    if (!m_isFaceDetectEnable || frame.empty())
        return;

    std::vector<cv::Rect> faces;
    // 轻量级检测,降低算力占用(调整参数,适配开发板,haar算法优化)
    m_faceClassifier.detectMultiScale(frame, faces, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, cv::Size(30, 30));

    // 发送人脸识别结果(是否检测到人脸、人脸数量,供Controller判断开锁)
    bool isDetected = faces.size() > 0 ? true : false;
    emit sigFaceDetectResult(isDetected, faces.size());

    // 可选:在帧上绘制人脸框(用于UI显示,不影响核心功能)
    for (size_t i = 0; i < faces.size(); i++)
    {
        cv::rectangle(frame, faces[i], cv::Scalar(255, 0, 0), 2);
    }
}

// 采集线程函数,避免阻塞UI线程(USB摄像头采集,v4l2帧处理)
void CameraManager::slotCaptureThread()
{
    struct v4l2_buffer buf;
    memset(&buf, 0, sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    while (m_isCapturing)
    {
        // 读取帧数据(v4l2缓冲区读取,USB摄像头采集)
        if (ioctl(m_cameraFd, VIDIOC_DQBUF, &buf) == -1)
        {
            emit sigCameraError("帧数据读取失败(v4l2缓冲区读取异常)");
            continue;
        }

        // 转换v4l2格式(YUYV)为OpenCV格式(BGR),适配后续处理
        cv::Mat frame(m_compressHeight, m_compressWidth, CV_8UC2, buf.m.userptr);
        cv::cvtColor(frame, frame, CV_YUV2BGR_YUYV);

        // 画质压缩(降低分辨率,减少UI渲染压力)
        cv::Mat compressFrame = this->compressFrame(frame);

        // 发送压缩后的帧信号,供UI显示(通过UI线程渲染,避免阻塞)
        emit sigCaptureFrame(compressFrame);

        // 人脸识别(若启用,haar轻量级算法处理)
        slotFaceDetect(compressFrame);

        // 放回缓冲区,继续采集(v4l2缓冲区循环使用)
        if (ioctl(m_cameraFd, VIDIOC_QBUF, &buf) == -1)
        {
            emit sigCameraError("缓冲区放回失败(v4l2缓冲区循环异常)");
            continue;
        }

        // 降低采集频率,减少CPU占用(适配开发板,30ms采集一次,约33帧/秒)
        usleep(30000);
    }

    // 停止采集后,关闭v4l2流
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl(m_cameraFd, VIDIOC_STREAMOFF, &type);
}

// 启动摄像头采集(USB摄像头,v4l2驱动)
void CameraManager::startCapture()
{
    if (m_cameraFd == -1 || m_isCapturing)
        return;

    m_isCapturing = true;
    emit sigCameraStatus(true);

    // 启动采集线程(Qt4.12.2合规,与UI线程分离,避免阻塞)
    m_captureThread = new QThread(this);
    connect(m_captureThread, SIGNAL(started()), this, SLOT(slotCaptureThread()));
    connect(m_captureThread, SIGNAL(finished()), m_captureThread, SLOT(deleteLater()));
    m_captureThread->start();
}

// 停止摄像头采集
void CameraManager::stopCapture()
{
    if (!m_isCapturing)
        return;

    m_isCapturing = false;
    emit sigCameraStatus(false);

    if (m_captureThread != NULL && m_captureThread->isRunning())
    {
        m_captureThread->quit();
        m_captureThread->wait();
    }
}

// 开启/关闭人脸识别(haar轻量级算法)
void CameraManager::setFaceDetectEnable(bool enable)
{
    m_isFaceDetectEnable = enable;
}

// 设置画质压缩参数(调整分辨率,适配开发板和UI显示)
void CameraManager::setCompressParam(int width, int height)
{
    m_compressWidth = width;
    m_compressHeight = height;
    // 若已初始化摄像头,重新配置参数(v4l2参数同步更新)
    if (m_cameraFd != -1)
    {
        stopCapture();
        configCameraParams();
    }
}

infraredlight.h/.cpp

#ifndef INFRAREDLIGHT_H
#define INFRAREDLIGHT_H
#include <QObject>
#include <QTimer>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/gpio.h>

// 红外感应补光模块,独立封装,LED三岔口(5.5V引脚、GPIO引脚、END引脚)
// 自动感应光线强度,光线不足时启动补光,光线充足时关闭
class InfraredLight : public QObject
{
    Q_OBJECT
public:
    explicit InfraredLight(QObject *parent = 0);
    ~InfraredLight();
    // 初始化GPIO引脚(传入GPIO编号,如12,对应LED三岔口GPIO引脚)
    bool initGPIO(int gpioPin);
    // 启动红外感应监测
    void startInfraredDetect();
    // 停止红外感应监测
    void stopInfraredDetect();

signals:
    // 补光状态信号(传给Controller,用于UI提示)
    void sigLightStatus(bool isOn);

private slots:
    // 定时检测光线强度,控制补光
    void slotDetectLight();

private:
    int m_gpioFd;       // GPIO文件描述符
    int m_gpioPin;      // 补光控制GPIO引脚(LED三岔口GPIO引脚)
    QTimer *m_detectTimer; // 感应检测定时器
    bool m_isLightOn;   // 补光状态标记
    // 读取光线强度(模拟红外感应,实际可对接硬件传感器,适配LED三岔口)
    int readLightIntensity();
};

#endif // INFRAREDLIGHT_H
#include "infraredlight.h"
#include <unistd.h>

InfraredLight::InfraredLight(QObject *parent) : QObject(parent)
{
    m_gpioFd = -1;
    m_gpioPin = -1;
    m_isLightOn = false;
    m_detectTimer = new QTimer(this);
    // 每500ms检测一次光线强度,降低CPU占用,适配开发板
    m_detectTimer->setInterval(500);
    connect(m_detectTimer, SIGNAL(timeout()), this, SLOT(slotDetectLight()));
}

InfraredLight::~InfraredLight()
{
    stopInfraredDetect();
    if (m_gpioFd != -1)
    {
        close(m_gpioFd);
        m_gpioFd = -1;
    }
    delete m_detectTimer;
    m_detectTimer = NULL;
}

// 初始化GPIO引脚,适配全志T113-S3 GPIO操作,对应LED三岔口GPIO引脚
bool InfraredLight::initGPIO(int gpioPin)
{
    m_gpioPin = gpioPin;
    // 打开GPIO控制器
    m_gpioFd = open("/dev/gpiochip0", O_RDWR);
    if (m_gpioFd == -1)
    {
        return false;
    }
    // 申请GPIO引脚(LED三岔口GPIO引脚,用于控制补光)
    struct gpiohandle_request req;
    req.lineoffsets[0] = m_gpioPin;
    req.flags = GPIOHANDLE_REQUEST_OUTPUT;
    if (ioctl(m_gpioFd, GPIO_GET_LINEHANDLE_IOCTL, &req) == -1)
    {
        close(m_gpioFd);
        m_gpioFd = -1;
        return false;
    }
    return true;
}

// 启动红外感应监测
void InfraredLight::startInfraredDetect()
{
    if (m_gpioFd != -1 && !m_detectTimer->isActive())
    {
        m_detectTimer->start();
    }
}

// 停止红外感应监测
void InfraredLight::stopInfraredDetect()
{
    if (m_detectTimer->isActive())
    {
        m_detectTimer->stop();
    }
    // 停止补光,复位LED状态
    m_isLightOn = false;
    struct gpiohandle_data data;
    data.values[0] = 0;
    ioctl(m_gpioFd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
    emit sigLightStatus(false);
}

// 读取光线强度(模拟实现,实际可对接红外传感器,适配LED三岔口补光逻辑)
int InfraredLight::readLightIntensity()
{
    // 模拟:光线强度0-100,<30为光线不足,需要补光(LED三岔口点亮)
    // 实际开发中替换为传感器读取逻辑(如ADC采样,对接LED三岔口红外感应)
    static int intensity = 50;
    intensity = (intensity + 10) % 100;
    return intensity;
}

// 定时检测光线强度,控制补光(LED三岔口亮灭)
void InfraredLight::slotDetectLight()
{
    int intensity = readLightIntensity();
    struct gpiohandle_data data;
    // 光线不足(<30),启动补光(LED三岔口点亮)
    if (intensity < 30 && !m_isLightOn)
    {
        data.values[0] = 1;
        ioctl(m_gpioFd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
        m_isLightOn = true;
        emit sigLightStatus(true);
    }
    // 光线充足(≥30),关闭补光(LED三岔口熄灭)
    else if (intensity >= 30 && m_isLightOn)
    {
        data.values[0] = 0;
        ioctl(m_gpioFd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
        m_isLightOn = false;
        emit sigLightStatus(false);
    }
}

uithread.h/.cpp

#ifndef UITHREAD_H
#define UITHREAD_H
#include <QThread>
#include <QObject>
#include <opencv2/opencv.hpp>
#include <QMutex>

// UI优化线程,独立封装,用于处理耗时UI操作(如视频帧渲染、图像处理)
// 分离UI线程,避免阻塞,提升界面响应速度,适配开发板低性能场景
class UIThread : public QThread
{
    Q_OBJECT
public:
    explicit UIThread(QObject *parent = 0);
    ~UIThread();
    // 设置要渲染的视频帧(从CameraManager接收,压缩后帧)
    void setRenderFrame(cv::Mat frame);
    // 停止UI线程
    void stopThread();

signals:
    // 帧渲染完成信号,传给View层更新界面(非阻塞)
    void sigFrameRendered(QImage image);

private:
    void run(); // 线程入口函数
    cv::Mat m_renderFrame;       // 待渲染帧
    bool m_isRunning;            // 线程运行状态
    QMutex m_mutex;              // 互斥锁,避免数据竞争(保护待渲染帧)
};

#endif // UITHREAD_H

#include "uithread.h"
#include <QMutexLocker>
#include <opencv2/highgui/highgui.hpp>

UIThread::UIThread(QObject *parent) : QThread(parent)
{
    m_isRunning = false;
}

UIThread::~UIThread()
{
    stopThread();
    wait();
}

// 设置待渲染的视频帧(加互斥锁,避免数据竞争,适配多线程场景)
void UIThread::setRenderFrame(cv::Mat frame)
{
    QMutexLocker locker(&m_mutex);
    m_renderFrame = frame.clone();
}

// 停止UI线程(安全停止,避免线程异常)
void UIThread::stopThread()
{
    QMutexLocker locker(&m_mutex);
    m_isRunning = false;
}

// 线程入口函数,处理视频帧渲染(耗时操作,分离UI线程,提升响应速度)
void UIThread::run()
{
    m_isRunning = true;
    while (m_isRunning)
    {
        QMutexLocker locker(&m_mutex);
        if (!m_renderFrame.empty())
        {
            // 转换OpenCV帧(Mat)为Qt图像(QImage),用于UI渲染
            QImage image;
            if (m_renderFrame.channels() == 1)
            {
                // 灰度图转换(摄像头压缩后默认灰度图,提升渲染速度)
                image = QImage((const uchar*)m_renderFrame.data, m_renderFrame.cols, m_renderFrame.rows,
                              m_renderFrame.step, QImage::Format_Grayscale8);
            }
            else
            {
                // 彩色图转换(BGR转RGB,适配Qt UI显示)
                image = QImage((const uchar*)m_renderFrame.data, m_renderFrame.cols, m_renderFrame.rows,
                              m_renderFrame.step, QImage::Format_BGR888);
                image = image.rgbSwapped();
            }

            // 发送渲染完成信号,供View层更新界面(非阻塞,避免UI卡顿)
            emit sigFrameRendered(image);

            // 清空帧,避免重复渲染,减少内存占用
            m_renderFrame.release();
        }
        locker.unlock();

        // 降低渲染频率,减少CPU占用(适配开发板,与摄像头采集频率同步)
        msleep(30);
    }
}

videocontrol.h/.cpp

#ifndef VIDEOCONTROL_H
#define VIDEOCONTROL_H
#include <QObject>
#include <QUdpSocket>
#include <QTcpSocket>
#include <QThread>
#include "audiomanager.h"
#include <opencv2/opencv.hpp>

// TCP传指令,UDP传视频流+音频流,一对一通话,整合AudioManager
// 适配Qt4.12.2,由AuthControl总控调度,不直接与View交互
class VideoControl : public QObject
{
    Q_OBJECT
public:
    explicit VideoControl(QObject *parent = 0);
    ~VideoControl();
    // 初始化音视频通话(绑定AudioManager,关联3.3mm耳机和USB音响)
    bool initVideoAudio(AudioManager *audioMgr);
    // 启动一对一视频通话(指定对方IP和端口)
    void startVideo(const QString &peerIp, int videoPort, int audioPort);
    // 停止视频通话
    void stopVideo();
    // 发送视频帧(从CameraManager接收,通过UDP发送)
    void sendVideoFrame(cv::Mat frame);

signals:
    // 通话状态信号(传给Controller)
    void sigVideoStatus(bool isCalling, QString statusMsg);
    // 接收视频帧信号(传给Controller,用于UI显示)
    void sigRecvVideoFrame(cv::Mat frame);
    // 接收音频数据信号(传给AudioManager播放,USB音响输出)
    void sigRecvAudioData(char *data, int dataLen);

private slots:
    // TCP连接状态处理(指令通道)
    void slotTcpConnected();
    void slotTcpDisconnected();
    void slotTcpError(QAbstractSocket::SocketError error);
    // UDP接收视频/音频数据(视频流+音频流)
    void slotUdpRecvData();
    // 音频状态同步(3.3mm耳机录音状态)
    void slotAudioStatusChanged(bool isRecording);

private:
    QTcpSocket *m_tcpSocket;     // TCP socket(传指令、控制信号)
    QUdpSocket *m_udpSocket;     // UDP socket(传视频流、音频流,无延迟)
    AudioManager *m_audioMgr;    // 音频管理模块(关联外部实例,控制耳机和音响)
    QThread *m_videoSendThread;  // 视频发送线程,避免阻塞
    bool m_isCalling;            // 通话状态标记
    QString m_peerIp;            // 对方IP
    int m_videoPort;             // 视频UDP端口
    int m_audioPort;             // 音频UDP端口
    // 视频帧编码(简化,适配开发板,降低传输压力)
    QByteArray encodeVideoFrame(cv::Mat frame);
    // 视频帧解码
    cv::Mat decodeVideoFrame(QByteArray data);
    // 视频发送线程函数
    void slotVideoSendThread();
    // 音频数据转发(3.3mm耳机录音数据→UDP发送)
    void slotAudioDataForward(char *data, int dataLen);
};

#endif // VIDEOCONTROL_H
#include "videocontrol.h"
#include <opencv2/opencv.hpp>
#include <QByteArray>
#include <QMutex>

VideoControl::VideoControl(QObject *parent) : QObject(parent)
{
    m_tcpSocket = new QTcpSocket(this);
    m_udpSocket = new QUdpSocket(this);
    m_audioMgr = NULL;
    m_videoSendThread = NULL;
    m_isCalling = false;
    m_videoPort = 8888;
    m_audioPort = 8889;

    // 绑定TCP信号槽(指令通道,处理连接状态)
    connect(m_tcpSocket, SIGNAL(connected()), this, SLOT(slotTcpConnected()));
    connect(m_tcpSocket, SIGNAL(disconnected()), this, SLOT(slotTcpDisconnected()));
    connect(m_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotTcpError(QAbstractSocket::SocketError)));
    // 绑定UDP接收信号(接收视频流和音频流)
    connect(m_udpSocket, SIGNAL(readyRead()), this, SLOT(slotUdpRecvData()));
}

VideoControl::~VideoControl()
{
    stopVideo();
    delete m_tcpSocket;
    delete m_udpSocket;
    if (m_videoSendThread != NULL && m_videoSendThread->isRunning())
    {
        m_videoSendThread->quit();
        m_videoSendThread->wait();
        delete m_videoSendThread;
    }
}

// 初始化音视频通话(绑定AudioManager,关联3.3mm耳机和USB音响)
bool VideoControl::initVideoAudio(AudioManager *audioMgr)
{
    if (audioMgr == NULL)
    {
        emit sigVideoStatus(false, "音频模块未初始化(3.3mm耳机/USB音响未就绪)");
        return false;
    }
    m_audioMgr = audioMgr;
    // 绑定音频状态信号,同步3.3mm耳机录音状态
    connect(m_audioMgr, SIGNAL(sigAudioStatus(bool)), this, SLOT(slotAudioStatusChanged(bool)));
    // 绑定音频数据转发(录音数据→UDP发送,播放数据→USB音响输出)
    connect(this, SIGNAL(sigRecvAudioData(char*, int)), m_audioMgr, SLOT(playAudio(char*, int)));
    return true;
}

// 视频帧编码(简化,压缩后传输,适配开发板网络,降低带宽占用)
QByteArray VideoControl::encodeVideoFrame(cv::Mat frame)
{
    if (frame.empty())
        return QByteArray();

    // 压缩帧数据(JPG格式,质量50,平衡画质与传输速度)
    std::vector<uchar> buf;
    cv::imencode(".jpg", frame, buf, std::vector<int>{CV_IMWRITE_JPEG_QUALITY, 50});
    return QByteArray((const char*)buf.data(), buf.size());
}

// 视频帧解码(适配编码格式,解码后供UI显示)
cv::Mat VideoControl::decodeVideoFrame(QByteArray data)
{
    if (data.isEmpty())
        return cv::Mat();

    std::vector<uchar> buf(data.begin(), data.end());
    return cv::imdecode(buf, CV_LOAD_IMAGE_GRAYSCALE); // 灰度图解码,降低渲染压力
}

// 视频发送线程函数(分离主线程,避免阻塞,与摄像头采集频率同步)
void VideoControl::slotVideoSendThread()
{
    static cv::Mat frame;
    QMutex mutex;
    while (m_isCalling)
    {
        mutex.lock();
        if (!frame.empty())
        {
            // 编码视频帧(压缩后传输)
            QByteArray frameData = encodeVideoFrame(frame);
            // 通过UDP发送视频帧(无延迟,适配一对一通话,适配开发板网络)
            m_udpSocket->writeDatagram(frameData, QHostAddress(m_peerIp), m_videoPort);
            // 清空帧,避免重复发送
            frame.release();
        }
        mutex.unlock();
        msleep(30); // 与摄像头采集频率同步,33帧/秒,减少CPU占用
    }
}

// 发送视频帧(从CameraManager接收,存入缓冲区,供发送线程发送)
void VideoControl::sendVideoFrame(cv::Mat frame)
{
    if (!m_isCalling || frame.empty())
        return;

    static QMutex mutex;
    mutex.lock();
    frame.copyTo(frame);
    mutex.unlock();
}

// 音频数据转发(3.3mm耳机录音数据→UDP发送)
void VideoControl::slotAudioDataForward(char *data, int dataLen)
{
    if (!m_isCalling || m_audioMgr == NULL || dataLen <= 0)
        return;

    // 通过UDP发送音频数据(与视频流分离,避免干扰,适配开发板网络)
    m_udpSocket->writeDatagram(data, dataLen, QHostAddress(m_peerIp), m_audioPort);
}

// TCP连接成功(指令通道建立,启动音视频通话)
void VideoControl::slotTcpConnected()
{
    m_isCalling = true;
    // 启动视频发送线程
    m_videoSendThread = new QThread(this);
    connect(m_videoSendThread, SIGNAL(started()), this, SLOT(slotVideoSendThread()));
    connect(m_videoSendThread, SIGNAL(finished()), m_videoSendThread, SLOT(deleteLater()));
    m_videoSendThread->start();
    // 启动录音(3.3mm耳机输入,音频数据转发)
    m_audioMgr->startRecord();
    emit sigVideoStatus(true, "通话连接成功(音视频就绪)");
}

// TCP断开连接(指令通道中断,停止音视频通话)
void VideoControl::slotTcpDisconnected()
{
    stopVideo();
    emit sigVideoStatus(false, "通话已断开(TCP连接中断)");
}

// TCP错误处理(指令通道异常,停止音视频通话)
void VideoControl::slotTcpError(QAbstractSocket::SocketError error)
{
    Q_UNUSED(error);
    stopVideo();
    emit sigVideoStatus(false, "通话连接失败:" + m_tcpSocket->errorString());
}

// UDP接收视频/音频数据(分离视频和音频,分别处理)
void VideoControl::slotUdpRecvData()
{
    while (m_udpSocket->hasPendingDatagrams())
    {
        QByteArray data;
        data.resize(m_udpSocket->pendingDatagramSize());
        QHostAddress peerAddr;
        quint16 peerPort;
        m_udpSocket->readDatagram(data.data(), data.size(), &peerAddr, &peerPort);

        // 区分视频端口和音频端口,分别处理
        if (peerPort == m_videoPort)
        {
            // 接收视频帧,解码后发送给Controller,供UI显示
            cv::Mat frame = decodeVideoFrame(data);
            if (!frame.empty())
            {
                emit sigRecvVideoFrame(frame);
            }
        }
        else if (peerPort == m_audioPort)
        {
            // 接收音频数据,转发给AudioManager,通过USB音响播放
            if (data.size() > 0)
            {
                // 申请临时缓冲区,避免数据异常,适配开发板内存
                char *audioBuf = new char[data.size()];
                memcpy(audioBuf, data.data(), data.size());
                emit sigRecvAudioData(audioBuf, data.size());
                // 释放缓冲区,避免内存泄漏
                delete[] audioBuf;
                audioBuf = NULL;
            }
        }
    }
}

// 音频状态同步(3.3mm耳机录音状态变化时,同步处理)
void VideoControl::slotAudioStatusChanged(bool isRecording)
{
    // 若录音状态变化,同步更新通话状态提示
    if (isRecording && m_isCalling)
    {
        emit sigVideoStatus(true, "通话中(录音已启动,3.3mm耳机正常采集)");
    }
    else if (!isRecording && m_isCalling)
    {
        emit sigVideoStatus(true, "通话中(录音已暂停)");
    }
}

// 停止视频通话(释放资源,复位状态,适配开发板低内存场景)
void VideoControl::stopVideo()
{
    if (!m_isCalling)
        return;

    // 停止视频发送线程
    if (m_videoSendThread != NULL && m_videoSendThread->isRunning())
    {
        m_videoSendThread->quit();
        m_videoSendThread->wait();
        delete m_videoSendThread;
        m_videoSendThread = NULL;
    }

    // 停止录音(3.3mm耳机停止采集)
    if (m_audioMgr != NULL)
    {
        m_audioMgr->stopRecord();
    }

    // 复位通话状态
    m_isCalling = false;
    m_peerIp.clear();
    m_videoPort = 8888;
    m_audioPort = 8889;

    // 断开TCP连接(指令通道)
    if (m_tcpSocket->isOpen())
    {
        m_tcpSocket->disconnectFromHost();
    }

    emit sigVideoStatus(false, "通话已停止(资源已释放)");
}

timeDisplay.h/.cpp

#ifndef TIMEDISPLAY_H
#define TIMEDISPLAY_H
#include <QObject>
#include <QTimer>
#include <QDateTime>

// 时间显示模块,独立封装,负责实时时间获取与刷新
// 适配Qt4.12.2,低功耗,适配全志T113-S3开发板,不与View直接交互
class TimeDisplay : public QObject
{
    Q_OBJECT
public:
    explicit TimeDisplay(QObject *parent = 0);
    ~TimeDisplay();
    // 启动时间刷新(每秒一次)
    void startTime();
    // 停止时间刷新
    void stopTime();

signals:
    // 时间更新信号(发送给Controller,由Controller转发给View)
    void sigTimeUpdated(QString timeStr);
    // 时间模块异常信号
    void sigTimeError(QString errorMsg);

private slots:
    // 定时获取当前时间,格式化后发送信号
    void slotUpdateTime();

private:
    QTimer *m_timeTimer; // 时间刷新定时器
    // 格式化时间(格式:yyyy-MM-dd hh:mm:ss)
    QString formatTime(const QDateTime &datetime);
};

#endif // TIMEDISPLAY_H

#include "timedisplay.h"

TimeDisplay::TimeDisplay(QObject *parent) : QObject(parent)
{
    m_timeTimer = new QTimer(this);
    m_timeTimer->setInterval(1000); // 每秒刷新一次,低功耗,适配开发板
    connect(m_timeTimer, SIGNAL(timeout()), this, SLOT(slotUpdateTime()));
}

TimeDisplay::~TimeDisplay()
{
    stopTime();
    delete m_timeTimer;
    m_timeTimer = NULL;
}

// 启动时间刷新
void TimeDisplay::startTime()
{
    if (!m_timeTimer->isActive())
    {
        m_timeTimer->start();
        // 立即发送一次时间信号,避免界面空白
        slotUpdateTime();
    }
}

// 停止时间刷新
void TimeDisplay::stopTime()
{
    if (m_timeTimer->isActive())
    {
        m_timeTimer->stop();
    }
}

// 定时更新时间,发送信号给Controller
void TimeDisplay::slotUpdateTime()
{
    QDateTime currentTime = QDateTime::currentDateTime();
    QString timeStr = formatTime(currentTime);
    emit sigTimeUpdated(timeStr);
}

// 格式化时间(适配门禁显示,格式清晰)
QString TimeDisplay::formatTime(const QDateTime &datetime)
{
    return datetime.toString("yyyy-MM-dd hh:mm:ss");
}

doorcontrol

#ifndef DOORCONTROL_H
#define DOORCONTROL_H
#include <QObject>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/gpio.h>

// 门锁控制模块,独立封装,适配全志T113-S3开发板GPIO接口
// 接收AuthControl的开门信号,控制门锁开启/复位
class DoorControl : public QObject
{
    Q_OBJECT
public:
    explicit DoorControl(QObject *parent = 0);
    ~DoorControl();
    // 初始化门锁GPIO(传入门锁控制GPIO引脚)
    bool initDoorGPIO(int gpioPin);

public slots:
    // 接收开门信号,控制门锁开启
    void slotOpenDoor();

private:
    int m_gpioFd;       // GPIO文件描述符
    int m_gpioPin;      // 门锁控制GPIO引脚
    // 控制门锁状态(开启/关闭)
    void setDoorStatus(bool isOpen);
};

#endif // DOORCONTROL_H
#include "doorcontrol.h"
#include <unistd.h>

DoorControl::DoorControl(QObject *parent) : QObject(parent)
{
    m_gpioFd = -1;
    m_gpioPin = -1;
}

DoorControl::~DoorControl()
{
    if (m_gpioFd != -1)
    {
        // 关闭门锁,复位状态
        setDoorStatus(false);
        close(m_gpioFd);
        m_gpioFd = -1;
    }
}

// 初始化门锁GPIO(适配全志T113-S3 GPIO操作)
bool DoorControl::initDoorGPIO(int gpioPin)
{
    m_gpioPin = gpioPin;
    // 打开GPIO控制器
    m_gpioFd = open("/dev/gpiochip0", O_RDWR);
    if (m_gpioFd == -1)
    {
        return false;
    }
    // 申请GPIO引脚(用于控制门锁)
    struct gpiohandle_request req;
    req.lineoffsets[0] = m_gpioPin;
    req.flags = GPIOHANDLE_REQUEST_OUTPUT;
    if (ioctl(m_gpioFd, GPIO_GET_LINEHANDLE_IOCTL, &req) == -1)
    {
        close(m_gpioFd);
        m_gpioFd = -1;
        return false;
    }
    // 初始状态:门锁关闭
    setDoorStatus(false);
    return true;
}

// 接收开门信号,控制门锁开启(延时3秒后关闭)
void DoorControl::slotOpenDoor()
{
    if (m_gpioFd == -1)
        return;

    // 开启门锁
    setDoorStatus(true);
    // 延时3秒后关闭门锁(适配门禁场景,避免门锁长期开启)
    sleep(3);
    setDoorStatus(false);
}

// 控制门锁状态(开启/关闭)
void DoorControl::setDoorStatus(bool isOpen)
{
    if (m_gpioFd == -1)
        return;

    struct gpiohandle_data data;
    data.values[0] = isOpen ? 1 : 0;
    ioctl(m_gpioFd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
}

2.4主窗口

main.cpp

#include<QApplication>
#include "widget.h"
#include "authcontrol.h"
#include "doorcontrol.h"
#include <QDebug>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // 创建各模块实例
    Widget w; // View层
    AuthControl authControl; // Controller层
    DoorControl doorControl; // 门锁控制模块

    // 绑定View与Controller(核心联动)
    authControl.bindView(&w);

    // 初始化门锁控制(GPIO引脚根据全志T113-S3实际硬件调整,示例为15引脚,需与硬件对应)
    if (!doorControl.initDoorGPIO(15))
    {
        w.showResultTips("门锁模块初始化失败", false);
        qDebug() << "DoorControl init failed: GPIO pin 15 init error";
        return -1;
    }

    // 绑定开门信号(Controller→DoorControl,核心控制逻辑)
    QObject::connect(&authControl, SIGNAL(sigOpenDoor()), &doorControl, SLOT(slotOpenDoor()));

    // 系统初始化(初始化所有模块,适配全志T113-S3)
    if (!authControl.initSystem())
    {
        w.showResultTips("系统整体初始化失败,退出程序", false);
        qDebug() << "System init failed, exit";
        return -1;
    }

    // 显示主界面
    w.show();

    // 程序主循环(适配Qt4.12.2,兼容开发板)
    return a.exec();
}

三、测试

3.1代码实例

# -*- coding: utf-8 -*-
import serial
import time
import cv2
import os
import sys

# 配置开发板串口(根据全志T113-S3实际串口调整,默认/dev/ttyS0)
SERIAL_PORT = '/dev/ttyS0'
BAUD_RATE = 115200
TIMEOUT = 2

# 测试用例配置
TEST_CASES = [
    {'name': '系统初始化测试', 'cmd': 'init_system', 'expect': '系统初始化成功'},
    {'name': '时间显示模块测试', 'cmd': 'test_time', 'expect': '202'}, # 匹配时间格式前缀
    {'name': '人脸开锁测试', 'cmd': 'face_unlock', 'expect': '人脸比对成功,已开锁'},
    {'name': '指纹开锁测试', 'cmd': 'finger_unlock', 'expect': '指纹比对成功,已开锁'},
    {'name': '密码开锁测试(正确密码)', 'cmd': 'password_unlock:123456', 'expect': '密码正确,已开锁'},
    {'name': '密码开锁测试(错误密码)', 'cmd': 'password_unlock:654321', 'expect': '密码错误,无法开锁'},
    {'name': '用户注册测试', 'cmd': 'register:test_user,1,1', 'expect': '用户注册成功'},
    {'name': '启动视频通话测试', 'cmd': 'start_video', 'expect': '通话中'},
    {'name': '停止视频通话测试', 'cmd': 'stop_video', 'expect': '未通话'},
    {'name': '门锁模块测试', 'cmd': 'test_door', 'expect': '门锁开启成功'},
    {'name': '摄像头模块测试', 'cmd': 'test_camera', 'expect': '摄像头采集正常'}
]

class AccessControlTester:
    def __init__(self):
        # 初始化串口
        try:
            self.ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=TIMEOUT)
            time.sleep(2) # 等待串口初始化完成
            print("✅ 串口初始化成功,端口:{}".format(SERIAL_PORT))
        except Exception as e:
            print("❌ 串口初始化失败:{}".format(str(e)))
            sys.exit(1)
        
        # 初始化测试结果
        self.test_result = {
            'pass': 0,
            'fail': 0,
            'detail': []
        }

    def send_cmd(self, cmd):
        """发送测试命令到门禁系统"""
        try:
            cmd_str = cmd + '\r\n' # 命令结尾添加换行,适配串口通信
            self.ser.write(cmd_str.encode('utf-8'))
            time.sleep(1) # 等待系统响应
            # 读取响应数据
            response = self.ser.read_all().decode('utf-8').strip()
            return response
        except Exception as e:
            return "通信异常:{}".format(str(e))

    def test_single_case(self, test_case):
        """执行单个测试用例"""
        print(f"\n=== 测试用例:{test_case['name']} ===")
        print(f"发送命令:{test_case['cmd']}")
        
        # 发送命令并获取响应
        response = self.send_cmd(test_case['cmd'])
        print(f"系统响应:{response}")
        
        # 判断测试结果
        if test_case['expect'] in response:
            result = '通过'
            self.test_result['pass'] += 1
        else:
            result = '失败'
            self.test_result['fail'] += 1
        
        # 记录测试详情
        self.test_result['detail'].append({
            'case': test_case['name'],
            'cmd': test_case['cmd'],
            'response': response,
            'expect': test_case['expect'],
            'result': result
        })
        
        print(f"测试结果:{result}")
        return result

    def test_camera(self):
        """单独测试摄像头模块(通过OpenCV读取摄像头帧)"""
        print("\n=== 摄像头模块单独测试 ===")
        try:
            # 打开摄像头(全志T113-S3 USB摄像头默认设备为/dev/video0)
            cap = cv2.VideoCapture(0)
            if not cap.isOpened():
                print("❌ 摄像头打开失败")
                self.test_result['fail'] += 1
                self.test_result['detail'].append({
                    'case': '摄像头模块单独测试',
                    'cmd': 'test_camera_single',
                    'response': '摄像头打开失败',
                    'expect': '摄像头采集正常',
                    'result': '失败'
                })
                return '失败'
            
            # 读取一帧图像
            ret, frame = cap.read()
            cap.release()
            if ret and frame is not None:
                print("✅ 摄像头采集正常,图像尺寸:{}x{}".format(frame.shape[1], frame.shape[0]))
                self.test_result['pass'] += 1
                self.test_result['detail'].append({
                    'case': '摄像头模块单独测试',
                    'cmd': 'test_camera_single',
                    'response': '摄像头采集正常,图像尺寸:{}x{}'.format(frame.shape[1], frame.shape[0]),
                    'expect': '摄像头采集正常',
                    'result': '通过'
                })
                return '通过'
            else:
                print("❌ 摄像头采集失败,无法获取图像")
                self.test_result['fail'] += 1
                self.test_result['detail'].append({
                    'case': '摄像头模块单独测试',
                    'cmd': 'test_camera_single',
                    'response': '摄像头采集失败',
                    'expect': '摄像头采集正常',
                    'result': '失败'
                })
                return '失败'
        except Exception as e:
            print(f"❌ 摄像头测试异常:{str(e)}")
            self.test_result['fail'] += 1
            self.test_result['detail'].append({
                'case': '摄像头模块单独测试',
                'cmd': 'test_camera_single',
                'response': f'测试异常:{str(e)}',
                'expect': '摄像头采集正常',
                'result': '失败'
            })
            return '失败'

    def run_all_tests(self):
        """执行所有测试用例"""
        print("==================================================")
        print("          全志T113-S3门禁系统功能测试脚本          ")
        print("==================================================")
        
        # 执行常规测试用例
        for test_case in TEST_CASES:
            self.test_single_case(test_case)
        
        # 执行摄像头单独测试(特殊模块,需单独验证)
        self.test_camera()
        
        # 输出测试总结
        print("\n==================================================")
        print("                    测试总结                      ")
        print("==================================================")
        total = self.test_result['pass'] + self.test_result['fail']
        print(f"总测试用例数:{total}")
        print(f"通过用例数:{self.test_result['pass']}")
        print(f"失败用例数:{self.test_result['fail']}")
        print(f"测试通过率:{self.test_result['pass']/total*100:.2f}%")
        
        # 输出失败用例详情
        if self.test_result['fail'] > 0:
            print("\n❌ 失败用例详情:")
            for detail in self.test_result['detail']:
                if detail['result'] == '失败':
                    print(f"用例:{detail['case']}")
                    print(f"  命令:{detail['cmd']}")
                    print(f"  响应:{detail['response']}")
                    print(f"  期望:{detail['expect']}")
                    print()
        
        # 关闭串口
        self.ser.close()
        print("\n✅ 测试完成,串口已关闭")

if __name__ == '__main__':
    # 检查开发板环境
    if not os.path.exists('/dev/ttyS0') and not os.path.exists('/dev/ttyAMA0'):
        print("❌ 未检测到开发板串口,无法执行测试")
        sys.exit(1)
    
    # 启动测试
    tester = AccessControlTester()
    tester.run_all_tests()

-------------------------会陆续补充完整--------------------------

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐