RFID 仓库管理系统 —— 项目技术总结第一部分项目概述1.1 项目背景制造业的物料仓库有一个绕不开的问题东西太多、流动太快、人工根本数不过来。一个中等规模的电子厂仓库物料品类动辄上千种每天的领料和入库操作超过百次。靠人工拿扫码枪逐件登记光是盘点一个货架就得花掉近十分钟更麻烦的是实际库存和账面库存经常对不上账。一盘出错轻则多买物料压库存重则产线缺料停工。超高频 RFID 在物理层面解决了逐件扫描的瓶颈。读写器一发信号电磁波覆盖范围内的标签同时应答不需要把每件货翻出来对条码。功能入库/出库操作仓管员在操作台的 PC 上打开 Qt 客户端连上板子后开始扫码。标签数据实时回传到 PC 屏幕去重、入库确认都在图形界面完成。操作完关了 PC 就行板子继续在货架端待命。库存盘点PC 不开机的时候板子处于空闲状态——它本身就是一块 Linux 单板计算机随时可以通过网络被唤醒。哪天要盘点打开 Qt 客户端连上板子发一条命令就能启动 RFID 扫描几十秒扫完一个货架结果自动和数据库比对。不需要人钻进货架里翻箱倒柜。防伪追溯RFID 标签的 TID 码是芯片出厂时固化、永远改不了的。入库时系统把 TID 和首次 EPC 绑定记录到数据库。后续任何查找或盘点操作都会自动比对当前 EPC 和首次 EPC——不一致就说明标签被人改写过了界面直接标红告警。等于给每个标签建了一条从入库到出库防伪可追溯的完整档案。本项目用 MX6ULL 开发板做嵌入式 C 服务端负责 RFID 控制和 SQLite 数据库用 Qt5 C 做桌面客户端跑在 PC 上负责全部图形交互。两端通过 TCP JSON 协议通信。1.2 核心模块与目标模块做什么目标入库管理RFID 批量扫描入库自动去重批次记录历史查询单批次 300 标签扫描确认 30 秒去重准确率 100%出库管理扫描出库自动校验在库状态防重复出库正常出库零差错异常场景有强制出库兜底库存盘点RFID 非接触批量盘点精准定位仓库货架与现有货物准确校验单货架盘点 30 秒人工需 10 分钟不符项自动标红罗列查找与防伪EPC/TID 关键词检索货物TID-EPC 绑定追溯EPC 变更告警模糊搜索秒级响应标签级别全生命周期可追溯系统设置网络连接配置天线功率查询与调节连接即用功率 0-33dBm 可调1.3 系统架构系统由两个物理节点组成通过网络直连i.MX6ULL 嵌入式端— 运行 C 语言服务程序直连 RFID 读写器模块负责射频信号收发、标签数据解析、SQLite 数据库读写、TCP 网络服务。选用 SQLite 嵌入式板子跑数据库守护进程太重SQLite 编译进去就是一个 .c 文件数据库单文件掉电不丢。建了 7 张表覆盖入库/出库/盘点/防伪的全部数据需求。Qt 桌面端— 5 个功能页面入库、出库、盘点、查找防伪、系统设置所有页面共用一个 Client 网络实例页面之间只通过信号槽通信互相不引用——加新页面不需要改动已有代码。两端通信— TCP 长连接 JSON 文本行协议共 13 条命令覆盖全部业务操作支持请求-响应匹配 和 实时事件推送。1.4 关键设计选择TCP JSON 协议— RFID 扫描是实时事件流标签一个接一个被读到服务端必须主动推送给 Qt 端。TCP 长连接天然支持双向推送Qt 端发命令C 端回结果同时 C 端可以随时把 事件主动推给 Qt。协议选 JSON 文本行格式每行一个 JSON 对象\n结尾解析简单、抓包可读粘包问题在里循环按行拆分完美解决。TID 而不是 EPC 做唯一标识— EPC 是用户可编程区可以被改写TID 是芯片出厂激光烧录永远改不了。所有数据库关联都用 TID即使 EPC 被改系统也能认出唯一的身份证——以此实现防伪追溯功能。sqlite3_prepare参数化查询 — 同时大量插入 标签明细每条 SQL 骨架一样只是 EPC、TID 的值不同。用 sqlite3_prepare_v2 编译一次得到语句句柄后续 只做三件事sqlite3_bind_xxx 绑新值 → sqlite3_step 执行 → sqlite3_reset 重置复用。值通过 ? 占位符传入而非拼进 SQL 字符串避免重复解析和编译实测性能与exec差异在十倍以上。信号槽解耦而不是页面直接引用— 5 个页面的 .h 文件互相之间没有任何#includeMainWindow 统一管理信号路由。加第六个页面不需要改前五个。第二部分整体架构2.1硬件架构上图为三个物理节点的连接关系部署方式板子读写器固定在货架端5V 供电全时待命。PC 在操作台网线或 WiFi 连接。仓管员打开 Qt 客户端连上板子执行入库/出库/盘点操作操作完关闭 Qt 即可板子不关机。2.2软件架构嵌入式端i.MX6ULL四层硬件层900 UHF RFID 读写器 i.MX6ULL 开发板系统层Buildroot Linux、交叉编译工具链、UART 串口驱动、TCP/IP 协议栈核心服务层TCP Server、JSON 协议解析、SQLite3 数据库、LTM200 SDK、标签去重算法、TID 防伪绑定业务逻辑层入库/出库/盘点/防伪四模块掉电不丢、单文件部署、7×24小时·待命Qt 端两层特点UI 交互层入库/出库/盘点/查找·防伪/系统设置 5 页面QStackedWidget 切换信号槽解耦通信架构层QTcpSocket 异步连接、Client 实例注入、parseBuffer 换行拆包、信号槽路由核心特点跨平台Windows 开发 → ARM 运行、页面解耦5 页面 .h 互不 include、易扩展新增页面不改旧代码两端通过 TCP JSON 协议双向实时通信共 13 条命令详见 2.3。2.3 通信协议13 条 TCP JSON 命令一览模块分类命令名通信方向功能说明扫描控制scan_startQt → C 端请求开始 RFID 扫描可携带 TID 开关scan_stopQt → C 端请求停止 RFID 扫描tag_foundC 端 → Qt实时推送扫描到的标签信息EPC、TID、RSSIscan_doneC 端 → Qt扫描完成通知返回本次扫描的标签总数入库管理inbound_confirmQt → C 端提交入库确认携带批次号、货物信息、扫描到的标签列表inbound_resultC 端 → Qt返回入库结果包含状态校验 、提示消息、跳过的重复标签列表get_batch_listQt → C 端请求查询所有入库批次列表batch_listC 端 → Qt入库记录获取入库批次列表数据批次号、仓库、数量等出库管理outbound_confirmQt → C 端提交出库确认携带批次号、待出库标签列表outbound_resultC 端 → Qt返回出库结果包含状态校验、提示消息、出库批次号get_outbound_listQt → C 端请求查询出库批次列表可携带日期筛选参数outbound_listC 端 → Qt返回出库记录批次列表数据批次号、标签总数等盘点管理inventory_checkQt → C 端提交盘点校验请求携带仓库、货架、扫描标签列表inventory_check_resultC 端 → Qt返回盘点结果包含匹配数、缺失数、多余标签明细查找防伪query_searchQt → C 端提交查询请求支持按名称 / 仓库 / 货架 / EPC/TID 多维度查询id模糊搜索query_resultC 端 → Qt返回查询结果包含总数、货物明细列表check_tid_bindingQt → C 端提交 TID-EPC 绑定校验请求防伪核验tid_verify_resultC 端 → Qt返回防伪校验结果包含首次/更改后 EPC、是否变更、历史批次系统设置set_antenna_powerQt → C 端设置 RFID 天线发射功率get_antenna_powerQt → C 端查询当前天线功率配置antenna_power_resultC 端 → Qt返回功率设置 / 查询结果包含当前功率、功率范围第三部分功能模块详解1.入库管理流程一、图1入库扫描主界面功能说明这是入库第一步填写基本信息后点击开始扫描扫标签。界面元素批次号点击开始扫描自动生成 批次号如RK20260622-050411RK年月日-时分秒仓库/货物/产品下拉选择扫到的标签会关联这些信息表格实时显示扫到的标签EPC列是标签唯一码信号列显示RSSI负值越小信号越强信号强度与信号丢失机制:检测到最后传回信号大于60标记货物并显示其EPC/TID图2标签详情弹窗功能说明点击表格右侧详情按钮查看单个标签的完整信息。显示内容批次号、EPC可改写、TID芯片固化、信号强度图3入库完成提示校验是/否为重复标签功能说明点击确认入库后C端会根据数据库当前入库情况返回处理结果。改图显示成功0件12件被跳过说明这12个标签已经在库中标红显示(提示用户)。为什么跳过防止同一标签重复入库保证库存数据准确。二、图1入库记录·主界面功能说明点击入库记录按钮可查看所有历史入库批次(进行入库信息溯源)。点击详情弹出该入库批次内的所有标签的EPC和TID列表。底部附有日期选择器可根据日期筛选精准定位到入库信息图2日期筛选功能说明点击起始/结束日期按钮弹出日历选择日期范围后点筛选只显示该日期范围内的入库记录。2.出库管理流程一、图1出库扫描主界面功能说明出库第一步系统自动生成出库批次号CK年月日-时分秒格式点击开始扫描扫标签。界面元素出库批次号自动生成CK20260622-050342CK年月日-时分秒表格实时显示扫到的标签包含EPC、TID、信号强度三列底部统计显示已扫描12件与入库的区别出库不需要预先填写仓库、货架、货物信息系统自动从数据库关联查询。图2出库成功功能说明点击确认出库后C端校验通过所有标签状态正常在库未出库出库成功。业务流程Qt端发送出库请求附带所有扫描到的TID列表C端逐个校验查询inbound_items表检查该TID的status字段是否为in在库所有标签都校验通过 → 更新状态为out→ 插入出库记录 → 返回成功Qt端弹窗提示出库成功图3出库失败警告功能说明点击确认出库后C端校验发现部分或全部标签已出库或未入库无法完成正常出库。触发条件标签状态为out已出库标红标签在数据库中不存在从未入库标红标签TID为空无法校验标红系统响应弹窗显示出库警告 - 出库失败阻止出库操作防止重复出库或非法出库。图4强制出库选择功能说明出库失败后系统提供强制出库和取消两个选项表格中异常标签·标红显示。按钮变化确认出库按钮变成强制出库红色警示点击取消按钮返回正常状态标红显示表格中所有行背景变红提示这些标签都存在异常已出库或未入库。强制出库逻辑用于处理特殊情况如工人未按程序入库、紧急领料等场景直接执行出库操作。避免风险强制出库同样进入出库记录(完整可溯源)系统记录该操作为强制出库类型便于后续审计追踪。二、图1出库记录主界面功能说明点击出库记录按钮切换到记录查询模式显示历史出库批次列表。列表字段序号出库批次号如CK20260622-062128出库数量该批次出库的标签总数操作按钮详情查看该批次明细与入库记录的区别出库记录不显示仓库、货架、货物信息因为出库是从库中移除不需要指定存放位置。图2出库详情弹窗功能说明点击记录列表中的详情按钮弹出该出库批次的完整标签列表。显示内容EPC标签可改写编码TID标签唯一固化编码仓库该标签入库时的仓库货架该标签入库时的货架货物该标签关联的货物名称产品该标签关联的产品名称3.库存盘点流程图1盘点前置校验功能说明进入库存盘点页面后可精准选中仓库和货架扫描盘点货物。界面元素仓库下拉框默认显示请选择仓库货架下拉框默认显示请先选择仓库与仓库联动选择仓库后才可选货架应盘数量显示--未选择仓库货架时触发条件未选择仓库时点击开始扫描按钮系统弹窗提示请先选择仓库。设计目的盘点针对特定库位进行防止误操作导致盘点数据混乱。图2扫描实盘标签功能说明选择2号仓库和A区02排后点击开始扫描实时扫描货架上的实际标签。界面变化应盘数量显示2系统根据仓库货架查询数据库该位置应有2件货物表格数据显示12个扫描到的标签包含EPC、货物名称、信号强度底部统计显示已扫描12详情按钮查看不符标签黄色按钮关键逻辑实扫数量12件远大于应盘数量2件说明该货架上有多余标签或相邻货架标签被误扫货物名称列为空因为扫描时只获取EPC/TID货物名称需要后续关联查询图3校对结果展示功能说明扫描完成后点击开始校对按钮系统将实盘扫描到的12件与应盘数据库记录的2件进行比对。结果显示表格标红所有12个扫描到的标签全部标红说明都不在应盘列表中底部统计校对完成匹配0件不符14件匹配0件扫描到的标签中没有一个在数据库应盘列表里不符14件12个扫描标签 2个应盘但未扫描到的标签 14个差异业务含义盘盈扫描到但数据库没有记录的标签可能错放货架、未入库、或已出库但标签还在盘亏数据库记录应在但扫描未到的标签可能丢失、被盗、或已出库但系统未更新后续处理根据不符标签列表仓管员需要现场核查确认是标签放错位置、系统数据滞后还是实际丢失。4.查询管理流程图1关键词搜索功能说明通过关键词模糊搜索EPC或TID快速定位货物。操作方式支持模糊搜索搜索框输入E2EPC/TID的部分或全部字符搜索按钮点击右侧搜索按钮执行查询结果展示显示11条匹配结果包含序号EPC标签可改写编码TID标签唯一固化编码仓库标签当前所在仓库货架标签当前所在货架货物关联的货物名称详情点击查看完整信息详情页见下文搜索逻辑系统对EPC和TID进行模糊匹配输入E2会返回所有包含E2的标签无论它在EPC还是TID中。图2标签详情与EPC防伪校验功能说明点击表格中的详情按钮查看单个标签的完整生命周期信息并进行EPC防伪校验。详情内容标题标签详情 - TID 【E280F302000000010B04C6E3】入库时间2026-06-22 11:21:24出库时间未出库说明标签当前仍在库中EPC防伪校验区域首次EPC入库时E280F302000000010B04C6E3这是标签首次入库时记录的EPC值作为防伪基线当前EPC本次扫描E280F302000000010B04C6E3这是当前查询时标签的实际EPC值状态正常EPC未变更系统比对首次EPC和当前EPC两者一致说明标签未被篡改防伪原理RFID标签的TID是芯片出厂时固化、永远改不了的但EPC是用户可以改写的。如果有人恶意改写标签EPC系统通过比对入库时的EPC和当前的EPC就能发现异常。这种设计实现了标签级别的防伪追溯。图3仓库筛选查询功能说明通过仓库和货架筛选查询特定库位下的所有标签。操作方式仓库选择选择3号仓库货架选择选择全部货架也可选择具体货架如A区02排查询按钮点击查询执行筛选筛选结果显示共找到2条结果只显示3号仓库下的标签结果包含EPC、TID、仓库、货架、货物信息使用场景仓管员需要查看某个特定仓库或货架的库存情况时使用此功能快速获取该库位下的所有标签清单。5.系统设置图1系统设置主界面功能说明系统设置的入口页面提供三个配置功能模块。三个功能入口网络设置太阳图标配置TCP连接参数连接i.MX6ULL开发板读码器设置靶心图标配置RFID读写器天线功率词典书本图标货物编码与名称映射词典预留功能操作方式点击对应图标进入详细设置页面。图2网络设置功能说明配置Qt客户端与i.MX6ULL嵌入式端的TCP连接参数。配置项服务器IP192.168.0.165i.MX6ULL开发板的IP地址端口号1998C端TCP Server监听端口操作流程修改IP地址或端口号如更换开发板或修改端口点击保存并连接按钮系统尝试建立TCP连接弹窗提示连接成功表示连接已建立使用场景首次使用需要配置正确的IP和端口开发板IP变更后需要重新配置网络故障排查时测试连接图3读码器设置天线功率功能说明调节RFID读写器的天线发射功率控制标签识别距离。当前状态当前天线功率23 dBm功率范围0 ~ 33 dBm滑动条可调节操作按钮查询当前功率从RFID读写器获取当前功率值并显示应用功率设置将滑动条设置的功率值下发到读写器功率调节原理功率越高如30 dBm读写距离越远但可能扫到相邻货架的标签串读功率越低如15 dBm读写距离越近定位更精确但容易漏扫使用场景密集货架环境调低功率避免串读隔壁货架标签开阔盘点场景调高功率扩大扫描覆盖范围标签信号弱适当提高功率增强标签激活能力操作流程拖动滑动条选择目标功率如23 dBm点击应用功率设置弹窗提示正在设置天线功率为 23 dBm...设置完成后即可进行入库/出库/盘点操作系统设置操作总结功能模块配置项用途网络设置IP地址、端口号连接IMX6ULL开发板读码器设置天线功率0-33 dBm控制RFID扫描距离和范围词典预留货物编码与名称映射第四部分C端解析行业问题分析与技术优势论证问题一解决 RFID 批量读取下查重逻辑随标签量膨胀带来的性能雪崩。扫了几十上百个标签数据量大的性能衰减升级体验一uthash 哈希去重——查找从 O(n) 降到 O(1)传统做法扫到一个标签就遍历数组查重/* 传统线性查找1000个标签1000次strcmp */ int found 0; for(int k 0; k tag_count; k) { if(strcmp(tags[k].epc, new_epc) 0) { found 1; break; /* 平均也要比500次 */ } }本项目的做法——哈希表一次搞定/* HASH_FIND_STR: 直接跳到哈希桶1~2次比较命中 */ HASH_FIND_STR(*hash_head, g_tag_queue[g_queue_read_idx].tid_Hex, entry); if(entry NULL) { /* 全新标签 → 插入 */ HASH_ADD_STR(*hash_head, tid_Hex, new_tag); tcp_send_tag(new_tag-epc_Hex, new_tag-tid_Hex, new_tag-rssi); }标签数量线性查找uthash加速比10个~5次比较1次5x100个~50次1次50x1000个~500次1~2次250x用户体验天线扫一圈同时返回几十个标签UI瞬间全部出现——没有一个一个蹦出来的卡顿感。这是哈希表给的底气。问题二RFID 天线瞬时批量上报单轮一次性读到 200 条 EPC/TIDJSON / 自定义二进制报文总长度轻易突破固定 buf 上限。升级体验二动态内存分包重传——不预设上限不丢一个字节问题本质固定的4096沉默的截断/* 修复前50个标签batch_items JSON≈4600字节 */ char buf[4096]; snprintf(buf, sizeof(buf), %s\n, json_str); /* snprintf到了4095字节自动停→末尾加了\0→最后200字节凭空消失 */ send(g_client_fd, buf, strlen(buf), 0); /* 发出去的JSON少了一个}接收端解析失败→静默丢数据 */这不是网络问题——是数据完整性事故。snprintf不会报错、不会崩溃它只是默默地把第4096个字节以后的内容扔掉了。C端服务端认为自己发送成功接收端却收到一句残缺JSON——整个过程没有任何错误提示直到操作员发现按钮点不动。这种静默失败比崩溃更恶劣崩溃能复现截断随数据量浮动难以排查。1按需分配精确到字节int len strlen(json_str); /* 实际多长4600就4600 */ char *buf (char *)malloc(len 2); /* 只分配4602字节不多占1KB */ memcpy(buf, json_str, len); buf[len] \n;性能对比修复前(fixed 4K)修复后(dynamic)20字节扫描停指令占用4096字节栈空间占用22字节堆空间4600字节批次详情发送4095字节截断发送4600字节完整每次调用栈开销固定4096字节0字节堆上分配内存利用率最差0.4%20/4096接近100%2TCP分包重传——send成功≠数据全部发出这是修复前代码更隐蔽的隐患/* 修复前一次send赌TCP内核缓冲区够大 */ send(g_client_fd, buf, strlen(buf), 0); /* 内核缓冲区80KB你发4KB→OK。但万一内核缓冲区只剩2KB呢→只发出2KB */TCP的send()返回实际发出的字节数可以小于请求发送的长度。触发条件对端接收窗口已满Qt端处理慢内核发送缓冲区被其他连接挤占网络拥塞时TCP拥塞控制主动限流优化后的分包循环int total_sent 0; while(total_sent len 1) { int n send(g_client_fd, buf total_sent, len 1 - total_sent, 0); if(n 0) break; /* 连接断开→退出 */ total_sent n; /* 追着发直到发完 */ }关键不是等TCP缓冲区空出来再试一个巨大的send——而是每次send剩下的部分缓冲区能收多少就发多少发完为止。这种策略在嵌入式设备的低带宽/高延迟网络上尤其重要——一次发4KB可能被切分成3个TCP段如果只发出1段就返回剩余2段就被静默丢弃了。接收端同样弹性realloc自动扩容服务端收数据做同样的事——recv一次收4096字节TCP粘包攒多了自动扩char *pbuf (char *)malloc(8192); /* 起手8K */ while(pbuf_len recv_len pbuf_size) { pbuf_size * 2; /* 8K→16K→32K→…容器追着数据长 */ pbuf (char *)realloc(pbuf, pbuf_size); } memcpy(pbuf pbuf_len, recv_buf, recv_len);realloc在地址空间有空闲时会原地扩展零拷贝不够时才搬家。从8K翻到32K的过程通常2次realloc搞定比一开始就malloc(65536)少浪费60KB内存。问题三RFID批量扫描线程竞争技术价值后台SDK线程永不阻塞100ms内完成一轮队列→哈希→TCP全链路50个标签瞬间入库一、SPSC环形队列——RFID批量扫描零锁并发痛点SDK回调线程每秒推送几十个标签主线程同时操作哈希表——不加锁数据竞争加锁回调线程可能阻塞导致标签丢失。解法回调只写队列尾部主线程只读队列头部——两个线程操作不同变量天然无锁。/* 第1步回调线程——只写队列不碰哈希表 */ void onData(HANDLE hDev, OutputInfoStruct *outputInfo) { /* 计算下一个写入位置 */ int queue_next (g_queue_write_idx 1) % QUEUE_MAX; if(queue_next g_queue_read_idx) return; /* 队列满——丢一个新标签比阻塞线程更优 */ int idx g_queue_write_idx; /* 只做两件事copy原始数据 转十六进制 */ memcpy(g_tag_queue[idx].epc_Raw, epc_raw, epc_Len); memcpy(g_tag_queue[idx].tid_Raw, tid_raw, tid_Len); strcpy(g_tag_queue[idx].epc_Hex, epc_Hex); strcpy(g_tag_queue[idx].tid_Hex, tid_Hex); g_tag_queue[idx].rssi atoi((char *)outputInfo-standard_data.data_format.RSSI); g_queue_write_idx queue_next; /* ★ 只有回调线程写write_idx */ }/* 第2步主线程——100ms轮询一次批量消费 */ static int drain_tag_queue(tag_entry **hash_head) { int tag_count 0; tag_entry *entry NULL; while(g_queue_read_idx ! g_queue_write_idx) { /* 队列有数据就取 */ /* 用TID查哈希——命中就跳过未命中才HASH_ADD */ HASH_FIND_STR(*hash_head, g_tag_queue[g_queue_read_idx].tid_Hex, entry); if(entry NULL) { /* 全新标签 */ tag_entry *new_tag (tag_entry *)calloc(1, sizeof(tag_entry)); memcpy(new_tag-epc_Raw, g_tag_queue[g_queue_read_idx].epc_Raw, ...); strcpy(new_tag-epc_Hex, g_tag_queue[g_queue_read_idx].epc_Hex); strcpy(new_tag-tid_Hex, g_tag_queue[g_queue_read_idx].tid_Hex); HASH_ADD_STR(*hash_head, tid_Hex, new_tag); /* ★ 只有主线程写哈希表 */ tcp_send_tag(new_tag-epc_Hex, new_tag-tid_Hex, new_tag-rssi); tag_count; } g_queue_read_idx (g_queue_read_idx 1) % QUEUE_MAX; /* ★ 只有主线程写read_idx */ } return tag_count; }为什么不用锁两个索引分别由不同线程独占写入满足SPSC单生产者单消费者无锁条件。ARM上省掉pthread_mutex_lock就是省掉几十微秒的上下文切换在100ms轮询周期里能多处理2~3倍标签。问题四RFID批量扫描无货物丢失校验传统方案只返回扫到了——是/否信号变弱无提示。升级体验三RSSI追踪——货物掉落丢失实时追踪/* 哈希表存RSSI信号变了才更新不刷屏 */ static int drain_tag_queue(tag_entry **hash_head) { while(g_queue_read_idx ! g_queue_write_idx) { HASH_FIND_STR(*hash_head, g_tag_queue[g_queue_read_idx].tid_Hex, entry); if(entry NULL) { /* 新标签直接发送 */ new_tag-rssi g_tag_queue[g_queue_read_idx].rssi; tcp_send_tag(new_tag-epc_Hex, new_tag-tid_Hex, new_tag-rssi); } else { /* ★ 旧标签信号变了才发——不刷屏但每个dBm变化都感知到 */ if(entry-rssi ! g_tag_queue[g_queue_read_idx].rssi) { entry-rssi g_tag_queue[g_queue_read_idx].rssi; tcp_send_tag(entry-epc_Hex, entry-tid_Hex, entry-rssi); } /* 信号没变 → 跳过 → 不发 → 安静 */ } g_queue_read_idx (g_queue_read_idx 1) % QUEUE_MAX; } }void InboundPage::checkRssiAndWarn() { if(m_tagRssiMap.isEmpty()) return; QStringList weakTags; QMapIteratorQString, int it(m_tagRssiMap); while(it.hasNext()) { it.next(); if(it.value() ! 0 it.value() RSSI_LOSS_THRESHOLD) { /* 只查静态阈值 */ weakTags.append(it.key()); } } if(!weakTags.isEmpty()) { QMessageBox warnBox(this); warnBox.setWindowTitle(信号检测警告); warnBox.setText(!检测到 N 件货物信号强度低于阈值); warnBox.setInformativeText(可能已移出读写范围或货物丢失); warnBox.exec(); } }/* 信号强度颜色编码Qt端 */ void InboundPage::addScannedTag(const QString epc, const QString tid, int rssi) { QTableWidgetItem *rssiItem m_scanTable-item(row, 4); rssiItem-setText(QString::number(rssi) dBm); if (rssi -50) rssiItem-setForeground(QColor(#27ae60)); /* 绿很近 */ else if (rssi -70) rssiItem-setForeground(Qt::black); /* 黑正常 */ else rssiItem-setForeground(QColor(#e67e22)); /* 橙弱可能快丢了 */ }核心价值RSSI变弱入库前报警天线靠近标签时RSSI从-70跳到-40屏幕颜色从橙变绿操作员就知道找到了。可凭颜色梯度找到货物。第五部分技术优化总结指标传统本系统提升1000件批量查重线性遍历→500次strcmputhash哈希O(1)→1~2次比较300倍查重误判率依赖记忆错误率~5%TID自动去重100%准确零差错信号丢失发现无感知事后才发现实时颜色结束时弹窗从无到有防伪溯源无法追溯条码可复印TID绑定EPC变更告警从无到有内存稳定性256KB栈数组→崩calloc堆分配stmtNULL全天候不崩