# web-demo **Repository Path**: hnlapilot/web ## Basic Information - **Project Name**: web-demo - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-11 - **Last Updated**: 2026-07-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # RID 无人机识别系统 — 前端态势监控 Demo 实时展示 RID 设备通过 MQTT 上报的无人机(Remote ID)、ADS-B 飞机目标及设备运行状态的前端监控系统。 --- ## 项目概览 - **纯前端项目**,零构建步骤,CDN 加载所有依赖 - 通过浏览器 WebSocket 直连 MQTT Broker,实时接收并渲染数据 - 腾讯位置服务 GL JS 地图展示,自动坐标转换(WGS84 → GCJ-02) - 单页应用,三个 Tab:无人机态势 / ADS-B 飞机 / 设备状态 --- ## 目录结构 ``` rid-web-demo/ ├── index.html # 主入口,Vue 模板 + CDN 引用 ├── server.js # Node.js 静态文件服务器(端口 8080) ├── package.json # npm 配置(依赖实际未使用,可忽略) ├── airlines.json # 航司 ICAO 三字码到 IATA 二字码/中文名称映射 ├── js/ │ └── app.js # 核心逻辑(Vue 3 Composition API) ├── css/ │ └── style.css # 暗色主题样式 └── docs/ ├── 数据上报说明书.md # MQTT 数据协议文档 v1.0 └── 数据上报说明书 copy.md # MQTT 数据协议文档 v2.0(含 ADS-B) ``` --- ## 技术栈 | 技术 | 说明 | 来源 | |------|------|------| | **Vue 3** | Composition API (`createApp` + `setup`) | CDN unpkg | | **腾讯地图 GL JS** | `TMap.Map`, `MultiMarker`, `MultiPolyline` | CDN map.qq.com | | **mqtt.js** | 浏览器 MQTT over WebSocket | CDN unpkg | | **Node.js** | 静态文件服务器 | `server.js` | ### CDN 引用(在 `index.html` 的 `
` 或 `` 底部) ```html ``` > **腾讯地图 API Key**:`R3RBZ-FUQC4-WVVUS-KSEOD-ZATF6-I4FAY`(如 Key 过期需重新申请) --- ## 本地开发 ### 启动 ```bash # 需要 Node.js(v12+) npm start # 或 node server.js ``` 访问 `http://localhost:8080` ### 不需要 npm install 项目无需安装依赖。`package.json` 中的 `ws` 和 `mqtt` 是早期遗留,实际未使用。 --- ## 部署 ### 方式一:直接运行 ```bash # 上传到服务器后 node server.js # 后台运行 nohup node server.js > rid-web.log 2>&1 & ``` ### 方式二:PM2(推荐) ```bash npm install -g pm2 pm2 start server.js --name rid-web pm2 save pm2 startup ``` ### 端口 - **HTTP 服务:8080**(修改 `server.js` 中的 `PORT` 常量) - **MQTT WebSocket:9001**(Broker 侧配置,非本项目) - 服务器防火墙 / 安全组需放行 8080 入站 ### 部署文件清单 仅需上传以下文件(不需要 `node_modules`、`test_ws.js`): ``` index.html server.js airlines.json # ADS-B 航班号航司显示映射表 js/app.js css/style.css docs/ (可选,参考文档) ``` --- ## 页面结构 ### 1. Header(顶部栏) - 系统标题 - MQTT 连接状态徽章(绿点 = 已连接) - 当前时间 ### 2. MQTT 连接栏 连接栏 UI 字段: | 字段 | 说明 | 默认值 | |------|------|--------| | `protocol` | ws / wss 协议选择 | `ws` | | `host` | Broker 地址 | `120.48.130.139` | | `port` | WebSocket 端口 | `9001` | | `path` | WebSocket 路径 | `/` | | `topic` | 订阅 Topic | `rid/imx6ull/#` | 连接配置通过 `localStorage` 持久化(key: `rid_mqtt_config_v3`)。 **按钮状态逻辑**: - 连接中:两按钮均禁用,输入框禁用 - 已连接:仅断开按钮可用 - 未连接:仅连接按钮可用 ### 3. 无人机态势(`activeTab === 'situation'`) - **左侧列表**:按 MAC 地址展示无人机,点击选中 - **右侧地图**:腾讯地图,标记无人机(蓝色无人机图案)、飞手(黄色圆点)、连接线 - **详情卡片**:点击无人机或地图标记后右上角弹出,展示全部字段 - **图例**:地图左下角 ### 4. ADS-B 飞机(`activeTab === 'adsb'`) - **左侧列表**:按 ICAO 地址展示飞机 - **右侧地图**:腾讯地图,标记 ADS-B 飞机(橙色飞机图案),飞机图标会按 `heading` 航向角旋转指向 - **右侧详情**:ICAO、呼号、坐标、高度、速度、航向、原始帧等 - **航班号增强显示**:若 `callsign` 以航司 ICAO 三字码开头,会显示为 `ICAO (IATA 中文名)航班号后缀`,例如 `CQH8549` 显示为 `CQH (9C 春秋航空)8549` ### 5. 设备状态(`activeTab === 'status'`) - **左侧设备列表**:按 `device_id` 分组,展示在线/离线、GNSS/RID/ADS-B 摘要 - **右侧详情**:四个卡片 - GNSS 定位(卫星数、定位质量、坐标、HDOP) - RID 模块(缓存无人机数、累计检测、无人机摘要表) - ADS-B 模块(缓存飞机数、累计消息、飞机摘要表) - 系统资源(CPU、内存、磁盘进度条,运行时间) --- ## 核心数据结构 ### app.js 中的响应式数据 | 变量 | 类型 | 说明 | |------|------|------| | `droneMap` | `reactive(Map)` | 无人机数据,key = MAC 地址 | | `adsbMap` | `reactive(Map)` | ADS-B 飞机数据,key = ICAO 地址 | | `deviceStatusMap` | `reactive(Map)` | 设备状态,key = device_id | | `selectedDrone` | `ref` | 当前选中的无人机对象 | | `selectedAircraft` | `ref` | 当前选中的飞机对象 | | `selectedDeviceId` | `ref` | 当前选中的设备 ID | ### Computed 属性 | 属性 | 来源 | 说明 | |------|------|------| | `droneList` | `droneMap` | 按 `seq` 降序排列 | | `droneCount` | `droneMap.size` | 无人机总数 | | `validDrones` | `droneList` | 过滤掉 `lat=0, lon=0` 的无效坐标 | | `adsbList` | `adsbMap` | 按 `_ts` 降序 | | `adsbCount` | `adsbMap.size` | 飞机总数 | | `validAircraft` | `adsbList` | 过滤无效坐标 | | `deviceStatusList` | `deviceStatusMap` | 按 `_ts` 降序 | | `deviceStatus` | `deviceStatusMap` | 当前选中设备的状态 | | `memPct` / `diskPct` | `deviceStatus.system` | 内存/磁盘使用百分比 | --- ## MQTT 消息处理 ### 订阅 Topic 默认订阅 `rid/imx6ull/#`(通配符),可在连接栏修改。 ### 消息路由逻辑 收到 MQTT 消息后,按以下优先级判断消息类型: ``` 1. type === 'status' 或 topic 包含 '/status' → handleStatus(data) 2. type === 'adsb_report' 或 topic 包含 '/adsb' 或 '/aircraft' 或 data 有 icao 但无 mac → handleAdsbReport(data) 3. type === 'drone_report' 或 topic 包含 '/update' 或 '/drone' 或 data 有 mac 但无 icao → handleDroneReport(data) ``` ### 三类消息格式 详细字段说明见 `docs/数据上报说明书 copy.md`,简要: | type | 唯一标识 | 关键字段 | |------|----------|----------| | `drone_report` | `mac` | lat, lon, ctrl_lat, ctrl_lon, geo_alt, gs, track, vs, status, rssi, vendor, uasid | | `adsb_report` | `icao` | lat, lon, alt, speed, heading, callsign, raw, receiver_gnss | | `status` | `device_id` | gnss{}, rid{}, adsb{}, network{}, system{} | 每条消息处理时添加 `_ts: Date.now()` 用于前端排序和存活时间计算。 `adsb_report.callsign` 原始值不会被改写;前端展示时通过 `airlines.json` 查找航司 ICAO 三字码,命中后追加 IATA 二字码和中文名称。 --- ## 地图与坐标 ### 坐标系 | 坐标系 | 用途 | |--------|------| | **WGS84 / CGCS2000** | 设备上报坐标(`coord=0` 为 WGS84,`coord=1` 为 CGCS2000,差异可忽略) | | **GCJ-02(火星坐标系)** | 腾讯地图 / 高德地图使用 | ### 坐标转换 `app.js` 中实现 `wgs84ToGcj02(lat, lon)` 函数,所有传入 `TMap.LatLng` 的坐标必须先转换: ```js var gcj = wgs84ToGcj02(d.lat, d.lon) new TMap.LatLng(gcj.lat, gcj.lon) ``` 中国境外坐标自动跳过偏移(`outOfChina()` 判断)。 ### 地图标记 | 图层 | 类型 | 样式 | 说明 | |------|------|------|------| | `droneMultiMarker` | MultiMarker | 蓝色圆形 + 十字图案 | 无人机当前位置 | | `pilotMultiMarker` | MultiMarker | 黄色圆形 + 白点 | 飞手位置 | | `trackLine` | MultiPolyline | 青色实线 | 无人机历史运动轨迹 | | `pilotStartLine` | MultiPolyline | 黄色虚线 | 飞手到轨迹初始节点连线 | | `adsbOnlyMultiMarker` | MultiMarker | 橙色圆形 + 飞机图案 | ADS-B 页面独立地图中的飞机 | | `connectLine` | MultiPolyline | 蓝色虚线 | 无人机当前位置-飞手连线 | ### 图标 使用 SVG data URI 动态生成,函数 `svgIconUri(bgColor, innerPath)`。 ADS-B 飞机图标使用 `ADSB_HEADING_BUCKET_DEG = 10` 做航向角分桶,预生成 `adsb_heading_000` 到 `adsb_heading_350` 的 `TMap.MarkerStyle`。`updateAdsbMapMarkers()` 根据飞机 `heading` 选择对应样式;缺失或非法航向使用 `adsb_default`。该方式把旋转写入 SVG data URI,避免依赖腾讯地图 SDK 未验证的逐 marker 旋转属性。 --- ## 样式系统 ### CSS 变量(暗色主题) ```css --bg-base: #0b0f19 /* 最底层背景 */ --bg-card: #111827 /* 卡片背景 */ --bg-elevated: #1e293b /* 浮层/高亮背景 */ --border: #1e293b /* 默认边框 */ --border-light: #334155 /* 亮边框 */ --text-1: #f1f5f9 /* 主文本 */ --text-2: #94a3b8 /* 次要文本 */ --text-3: #64748b /* 最弱文本 */ --accent: #3b82f6 /* 主题蓝 */ --green: #22c55e /* 成功/在线 */ --red: #ef4444 /* 错误/紧急 */ --yellow: #eab308 /* 警告 */ --orange: #f97316 /* 注意/ADS-B */ ``` ### 布局结构 ``` #app (flex column, 100vh) ├── .app-header (固定高度) ├── .mqtt-bar (固定高度) ├── .tab-nav (固定高度) └── .main-content (flex: 1, overflow: hidden) ├── .view-situation (flex row) │ ├── .drone-sidebar (340px 宽) │ └── .map-wrap (flex: 1, position: relative) ├── .view-adsb (flex row) │ ├── .adsb-sidebar (340px 宽) │ └── .adsb-detail-wrap(flex: 1,含 ADS-B 地图和详情) └── .view-status (flex row) ├── .dev-sidebar (320px 宽) └── .status-detail (flex: 1) ``` ### 响应式 900px 以下,sidebar 变为顶部水平列表(`max-height: 40%`)。 --- ## 已知问题与注意事项 ### localStorage 缓存 修改默认 MQTT 配置后,**必须更新 `STORAGE_KEY` 版本号**(当前 `rid_mqtt_config_v3`),否则旧缓存会覆盖新默认值。 ### 腾讯地图 API - `MultiMarker.styles` 必须使用 `new TMap.MarkerStyle({...})` 实例,不能传纯对象 - 连线必须用 `TMap.MultiPolyline` + `TMap.PolylineStyle`,不是 `TMap.Polyline` - `LatLng(lat, lon)` 中 lat=0 在 JS 中是 falsy,判断有效性必须用 `typeof d.lat === 'number'` 而非 `d.lat && d.lon` ### 浏览器 MQTT 限制 - 浏览器**只能**使用 WebSocket 协议(`ws://` 或 `wss://`),不能用 TCP 1883 - 服务器端 MQTT Broker 必须开启 WebSocket 监听(默认端口 9001) - 如果使用 `wss://`,需要 HTTPS + 证书 ### 坐标偏移 - 设备上报 WGS84,腾讯地图使用 GCJ-02,存在几十到几百米偏移 - 所有坐标传入地图前必须经过 `wgs84ToGcj02()` 转换 --- ## 扩展开发指南 ### 添加新的 MQTT 消息类型 1. 在 `app.js` 中新增 `reactive(Map)` 存储数据 2. 新增 `handleXxxReport(data)` 函数 3. 在 `connectMqtt` 的 `on('message')` 中添加路由判断 4. 新增 `computed` 列表和 count 属性 5. 在 `index.html` 中添加对应的 Tab 和 UI 6. 如需地图标记,在 `initMap()` 中创建新 `MultiMarker`,在 `updateMapMarkers()` 中更新 ### 添加新的设备状态模块 1. 在 `handleStatus()` 中已有完整 `data` 对象 2. 在 `index.html` 设备状态 `.status-grid` 中添加新的 `.s-card` 3. 使用 `deviceStatus.模块名?.字段` 读取数据 ### 修改地图中心点 `app.js` 中 `initMap()` 函数: ```js tmap = new TMap.Map('map', { center: new TMap.LatLng(wgs84ToGcj02(纬度, 经度).lat, wgs84ToGcj02(纬度, 经度).lon), zoom: 14, ... }) ``` ### 添加新厂商颜色 `app.js` 顶部 `VENDOR_COLORS` 对象,key 为厂商标识字符串。 ### 添加无人机自动清除 前端按本地接收时间 `_ts` 定时清理过期数据:无人机 3 分钟、ADS-B 5 分钟、设备状态 6 分钟。清理后列表、详情选择和地图标记会同步更新。 ### 维护 ADS-B 航司映射 `airlines.json` 用于把航班号开头的航司 ICAO 三字码映射为 IATA 二字码和中文名称。当前格式沿用项目已有查找表风格: ```json [ { "CQH": { "iata": "9C", "name_zh": "春秋航空" } } ] ``` 新增航司时添加一项即可。前端会在启动时加载该文件,加载失败时不影响 ADS-B 数据展示,只会回退为原始 `callsign`。 --- ## 调试技巧 ### 浏览器控制台日志 所有 MQTT 事件以 `[RID]` 前缀输出到 console: ``` [RID] MQTT 连接 URL: ws://120.48.130.139:9001/ [RID] MQTT 订阅 Topic: rid/imx6ull/# [RID] MQTT 已连接 [RID] 已订阅 rid/imx6ull/# [RID] MQTT 错误: ... [RID] 未识别消息: ... ``` ### 使用 MQTTX 等工具模拟数据 ```bash # 发送测试无人机数据 mosquitto_pub -h 120.48.130.139 -p 1883 -t "rid/imx6ull/update" -m '{"type":"drone_report","device_id":"test","mac":"AA:BB:CC:DD:EE:FF","vendor":"DJI","proto":"ODID","seq":1,"status":2,"rssi":-65,"lat":20.03,"lon":110.34,"ctrl_lat":20.02,"ctrl_lon":110.33,"gs":5.2,"track":90,"vs":1.0,"geo_alt":120,"baro_alt":100,"coord":0,"age_ms":1000}' # 发送测试 ADS-B 数据 # callsign=CQH8549 会根据 airlines.json 显示为 CQH (9C 春秋航空)8549 # heading=183 时,ADS-B 地图飞机图标会按约 183° 航向指向 mosquitto_pub -h 120.48.130.139 -p 1883 -t "rid/imx6ull/adsb" -m '{"type":"adsb_report","device_id":"test","icao":"781CD5","callsign":"CQH8549","lat":22.17,"lon":113.85,"alt":10393,"speed":805,"heading":183}' # 订阅全部消息验证 mosquitto_sub -h 120.48.130.139 -p 1883 -t "rid/imx6ull/#" -v ``` ### 清除 localStorage 缓存 浏览器控制台执行: ```js localStorage.removeItem('rid_mqtt_config_v3') ``` 然后刷新页面。 --- ## 协议文档 MQTT 消息格式详细定义见 `docs/数据上报说明书 copy.md`(v2.0,包含 ADS-B)。 三种消息的 Topic: | Topic | 消息类型 | 频率 | |-------|----------|------| | `rid/imx6ull/update` | drone_report | 实时 | | `rid/imx6ull/adsb` | adsb_report | 实时 | | `rid/imx6ull/status` | status | 每 5 分钟 |