From 8c5a06b7e62f318ff3e810dc9b6180776b3eb209 Mon Sep 17 00:00:00 2001 From: liumangmang Date: Wed, 17 Jun 2026 17:00:29 +0800 Subject: [PATCH] docs: add PR7050 panoramic monitoring data flow Co-Authored-By: Claude --- src/.vuepress/styles/index.scss | 40 ++ .../pr7050-panoramic-monitoring-data-flow.md | 609 ++++++++++++++++++ 2 files changed, 649 insertions(+) create mode 100644 src/work/project-summary/pr7050-panoramic-monitoring-data-flow.md diff --git a/src/.vuepress/styles/index.scss b/src/.vuepress/styles/index.scss index 4d07c3f..4aaaf8e 100644 --- a/src/.vuepress/styles/index.scss +++ b/src/.vuepress/styles/index.scss @@ -22,6 +22,46 @@ flex-shrink: 0; } +.vp-sidebar .vp-sidebar-link { + display: flex; + align-items: center; + gap: 0.3em; + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.vp-sidebar .vp-sidebar-header { + gap: 0.3em; + white-space: nowrap; +} + +.vp-sidebar .vp-sidebar-link .icon, +.vp-sidebar .vp-sidebar-link .vp-icon, +.vp-sidebar .vp-sidebar-header .icon, +.vp-sidebar .vp-sidebar-header .vp-icon { + flex: 0 0 1em; + margin-top: 0; +} + +.vp-sidebar .vp-sidebar-title { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +#app { + --sidebar-width: 19rem; +} + +@media (max-width: 959px) { + #app { + --sidebar-width: var(--sidebar-mobile-width, 16rem); + } +} + .todo-page { --todo-border: rgba(220, 20, 60, 0.2); --todo-accent: rgba(220, 20, 60, 0.08); diff --git a/src/work/project-summary/pr7050-panoramic-monitoring-data-flow.md b/src/work/project-summary/pr7050-panoramic-monitoring-data-flow.md new file mode 100644 index 0000000..24df858 --- /dev/null +++ b/src/work/project-summary/pr7050-panoramic-monitoring-data-flow.md @@ -0,0 +1,609 @@ +--- +title: PR7050 全景监控(主辅监控)数据加载流程深度解析 +icon: fa6-solid:network-wired +date: 2026-06-17 +category: + - 工作 +tag: + - PR7050 + - Java + - Vue + - 实时监控 + - 组态画面 +--- + +# PR7050 全景监控(主辅监控)数据加载流程深度解析 + +本文基于 PR7050 项目中全景监控模块(`/panoramicMonitoring/main-auxiliaryMonitoring`)的实际代码,深入分析组态画面数据加载的完整流程、核心接口、缓存机制及关键注意事项。 + + + +## 1. 项目概述 + +全景监控模块是 PR7050 系统中用于实时展示变电站设备状态的核心功能模块。它通过组态画面的形式,将设备的遥测、遥信、遥控、遥设等实时数据以图形化方式呈现给用户,支持设备状态监控、遥控操作、告警展示等功能。 + +**核心特点**: +- **图形化展示**:通过组态画面直观展示设备状态 +- **实时数据刷新**:支持定时轮询和 MQ 推送两种刷新机制 +- **多级导航**:顶部Tab菜单 + 左侧树提供设备分类导航 +- **缓存优化**:文件缓存和内存缓存双重优化加载性能 + +## 2. 整体数据加载流程 + +组态画面的数据加载分为五个主要阶段,形成完整的数据闭环: + +``` +阶段1:获取顶部Tab菜单 → 阶段2:加载左侧导航树 → 阶段3:获取画面属性 → 阶段4:获取实时数据 → 阶段5:定时刷新/MQ推送 +``` + +### 2.1 阶段一:获取顶部Tab菜单 + +**目的**:获取全站总览图列表,作为一级导航菜单 + +**涉及接口**: + +| 接口路径 | 请求方式 | 作用 | +| :--- | :--- | :--- | +| `/userManage/dynamic/route` | POST | 获取动态路由(全站总览图列表) | + +**数据来源**:`picfileinfo` 表中 `pictype=8`(全站总览图)的数据 + +**返回数据结构**: + +```json +[ + { + "name": "辅控照明子系统", + "nodeId": "1001", + "url": "/main-auxiliaryMonitoring/1001", + "right": true, + "invalidhide": true + }, + { + "name": "巡视设备总览", + "nodeId": "1002", + "url": "/main-auxiliaryMonitoring/1002", + "right": true, + "invalidhide": true + } +] +``` + +**前端处理逻辑**([MainDeviceMonitoring/index.tsx](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_web/prw/src/pages/BizComponents/MainAuxiliaryMonitoring/MainDeviceMonitoring/index.tsx#L38)): + +```tsx +const getInitialData = async () => { + const { data } = await getRouteList(); + const options = data?.map((item: any) => { + return { + key: item?.nodeId, + label: item?.name, + }; + }); + setTabList(data?.length ? [...tabListDefault, ...options] : tabListDefault); +}; +``` + +**Tab列表结构**: + +| Tab名称 | key | 说明 | +| :--- | :--- | :--- | +| 监控首页 | `'0'` | 默认页,显示左侧树 | +| 辅控照明子系统 | `'1001'` | 全站总览图,直接加载画面 | +| 巡视设备总览 | `'1002'` | 全站总览图,直接加载画面 | + +**Tab点击处理**([MainDeviceMonitoring/index.tsx](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_web/prw/src/pages/BizComponents/MainAuxiliaryMonitoring/MainDeviceMonitoring/index.tsx#L48)): + +```tsx +const changeTabs = (key: string) => { + setActiveKey(key); + setFixedPicId(key !== '0' ? key : null); +}; +``` + +- 点击"监控首页"(key='0'):`fixedPicId = null`,显示左侧树,用户从树中选择画面 +- 点击其他Tab(key='1001'等):`fixedPicId = 画面ID`,直接加载指定画面,隐藏左侧树 + +**代码位置**: +- 控制器:[UserManageController.java](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_java/platapp/04_sunri-web-patrol/sunri-web-center-cygbusiness/cygbusiness-auth/src/main/java/com/sunri/controller/usermanage/UserManageController.java#L486) +- 服务实现:[UserManageServiceImpl.java](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_java/platapp/service/sunri-service-auth-patrol/sunri-service-auth-patrol-core/src/main/java/com/sunri/service/impl/UserManageServiceImpl.java#L640) + +### 2.2 阶段二:加载左侧导航树 + +**目的**:获取图形画面的分类结构,提供导航入口 + +**涉及接口**(前端实际调用): + +| 接口路径 | 请求方式 | 作用 | +| :--- | :--- | :--- | +| `/api/graph/picture/get/baseGraph/tree/list/v2` | GET | 按设备类型分类(主设备/辅设备/巡视设备) | + +> **注意**:前端只调用了按设备类型分类的接口,没有调用按子系统分类的接口(`/api/graph/picture/get/subSystem/tree/list`)。 + +**关键参数**: + +| 参数 | 来源 | 值 | +| :--- | :--- | :--- | +| `equipmentType` | `graphType` | `'主设备'` / `'辅设备'` / `'巡视设备'` | +| `siteId` | `siteData` | 变电站ID | + +**graphType 与设备类型映射**: + +| graphType | equipmentType | 说明 | +| :--- | :--- | :--- | +| 0 | `'主设备'` | 监控首页 | +| 1 | `'辅设备'` | 其他Tab页 | +| 2 | `'巡视设备'` | 巡视设备监控 | + +**前端调用逻辑**([ControlHMI/index.tsx](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_web/prw/src/pages/BasicComponents/ControlHMI/index.tsx#L74)): + +```tsx +const initGraphInfo = async (siteId: number) => { + const equipmentType = ['主设备', '辅设备', '巡视设备']?.[graphType]; + const { status, data } = await getPvarrayV2(equipmentType, siteId); + if (status === 200 && data) { + GraphConfigModel(data, useGraphStore, siteId); + fixedPicId ? getPageData(fixedPicId, timerLimit?.current) : handleGraphTree(data?.picFileInfoVos?.[0]?.children || []); + } +}; +``` + +**返回数据结构**: + +```json +{ + "level": 1, + "nodeId": "1", + "description": "画面类型", + "hasChildren": true, + "children": [ + { + "level": 2, + "nodeId": "1", + "description": "主接线图", + "hasChildren": true, + "children": [ + { + "level": 3, + "nodeId": "101", + "description": "220kV主接线图", + "picType": 1, + "siteId": 1 + } + ] + } + ] +} +``` + +**节点判断规则**: + +| 字段 | 判断条件 | 是否可获取画面 | +| :--- | :--- | :--- | +| `level` | ≥ 3 | ✅ | +| `nodeId` | 为 `picId`(数字) | ✅ | +| `hasChildren` | `false`(叶子节点) | ✅ | +| `level` | ≤ 2 | ❌ | +| `nodeId` | 为 `picType` 或 `"bayId"` 开头 | ❌ | + +**代码位置**: +- 控制器:[BasePictureController.java](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_java/platform/03_platapp/04_sunri-web-center/sunri-web-center-cygbusiness/cygbusiness-graphic/src/main/java/com/sunri/controller/BasePictureController.java#L194) +- 服务实现:[BasePictureServiceImpl.java](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_java/platform/02_platservice/sunri-service-graphic/sunri-service-graphic-core/src/main/java/com/sunri/service/impl/BasePictureServiceImpl.java#L171) +- VO定义:[GraphTreeNodeVo.java](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_java/platform/00_depends/sunri-service-graphic-spi/src/main/java/com/sunri/vo/GraphTreeNodeVo.java) + +### 2.3 阶段三:获取画面属性(静态数据) + +**目的**:获取组态画面的完整结构,包括静态元素和动态元素定义 + +**涉及接口**(前端实际调用): + +| 接口路径 | 请求方式 | 作用 | 调用场景 | +| :--- | :--- | :--- | :--- | +| `/api/graph/picture/get/graph/properties` | GET | 获取画面属性 | 用户选择画面时 | +| `/api/graph/picture/get/drawing/properties` | GET | 获取图纸属性(图元) | 画面渲染过程中动态加载图元 | +| `/api/graph/picture/get/graph/wiringProperties` | GET | 获取主接线图属性 | 专门的主接线图页面 | + +**三个接口的核心区别**: + +| 特性 | `get/graph/properties` | `get/drawing/properties` | `get/graph/wiringProperties` | +| :--- | :--- | :--- | :--- | +| **参数** | `picId`(必填)、`siteId`、`cmyId` | `elementId`(必填)、`siteId` | `siteId`、`cmyId` | +| **返回类型** | `List` | `GraphElementVo` | `List` | +| **数据粒度** | 完整画面 | 单个图元 | 主接线图画面 | +| **数据来源** | `picfileinfo` 表 | 图元表 | `picfileinfo` 表(主接线图) | +| **前端调用位置** | ControlHMI、Graph组件 | Graph组件 | 未在当前项目中直接调用 | +| **触发时机** | 点击画面节点、Tab切换 | 画面渲染时动态加载图元 | 获取主接线图 | + +**请求参数**(`get/graph/properties`): + +```json +{ + "picId": "101", // 画面ID(来自树节点的nodeId) + "siteId": 1, // 变电站ID + "cmyId": 1 // 厂站ID +} +``` + +**返回数据结构**: + +```json +{ + "picId": 101, + "picFilename": "220kV主接线图", + "jsonContent": { + "width": 1920, + "height": 1080, + "elements": [ + { + "id": "element_1", + "type": "rect", // 静态元素 + "x": 100, + "y": 100, + "fill": "#336699" + }, + { + "id": "element_2", + "type": "text", // 动态元素 + "signalId": "45_1001", // 信号绑定标识 + "format": "%.1f" + } + ] + }, + "sigIds": [45, 1001, 46, 2001] // 动态信号ID列表(二进制) +} +``` + +**静态/动态元素区分**: + +| 区分方式 | 字段/属性 | 作用 | +| :--- | :--- | :--- | +| `sigIds` | `byte[]` | 快速获取所有动态信号ID | +| `signalId` | JSON属性 | 精确标识每个动态元素绑定的信号 | + +**代码位置**: +- 控制器:[BasePictureController.java](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_java/platform/03_platapp/04_sunri-web-center/sunri-web-center-cygbusiness/cygbusiness-graphic/src/main/java/com/sunri/controller/BasePictureController.java#L222) +- VO定义:[PicFileInfoVo.java](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_java/platform/00_depends/sunri-service-graphic-spi/src/main/java/com/sunri/vo/PicFileInfoVo.java) + +### 2.4 阶段四:获取实时数据(动态数据) + +**目的**:根据画面中的信号绑定,从实时数据库获取信号的实时值 + +**涉及接口**: + +| 接口路径 | 请求方式 | 作用 | +| :--- | :--- | :--- | +| `/api/rt/data/get` | POST | 获取信号点实时数据 | + +**请求参数**: + +```json +{ + "cmyId": 1, + "staId": 1, + "tables": [ + { + "table": 45, // 遥测表 + "records": [ + {"record": 1001}, + {"record": 1002} + ] + }, + { + "table": 46, // 遥信表 + "records": [ + {"record": 2001} + ] + } + ] +} +``` + +**信号表类型**: + +| tableId | 信号类型 | 说明 | +| :--- | :--- | :--- | +| 45 | 遥测(Analog) | 电流、电压、功率等连续数值 | +| 46 | 遥信(Digital) | 开关位置、告警状态等离散状态 | +| 47 | 遥脉(Power) | 电能累计值 | +| 48 | 遥控(Control) | 遥控命令状态 | +| 53 | 遥设(Set) | 设定值 | + +**返回数据结构**: + +```json +[ + { + "record": 1001, + "realValue": 220.5, + "status": "0000", + "time": "2026-06-17 10:30:00", + "table": 45, + "hoverMessages": ["220kV母线电压(1001)"] + }, + { + "record": 2001, + "realValue": 1, + "status": "0001", + "time": "2026-06-17 10:30:00", + "table": 46, + "hoverMessages": ["断路器位置(2001)"] + } +] +``` + +**代码位置**: +- 控制器:[RTDataController.java](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_java/platform/03_platapp/04_sunri-web-center/sunri-web-center-cygbusiness/cygbusiness-graphic/src/main/java/com/sunri/controller/RTDataController.java#L50) +- 服务实现:[GraphicRealtimeServiceImpl.java](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_java/platform/02_platservice/sunri-service-graphic/sunri-service-graphic-core/src/main/java/com/sunri/service/impl/GraphicRealtimeServiceImpl.java) +- 数据聚合:[GraphicAggregate.java](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_java/platform/02_platservice/sunri-service-graphic/sunri-service-graphic-core/src/main/java/com/sunri/model/realtime/graphics/GraphicAggregate.java) + +### 2.5 阶段五:定时刷新与MQ推送 + +**目的**:保持数据的实时性,处理画面变更 + +#### 5.1 定时轮询刷新 + +``` +前端 setInterval(1-3秒) → POST /api/rt/data/get → 更新画面显示 +``` + +**轮询频率**:通常1-3秒一次,根据实际需求调整 + +**适用场景**:信号值变化的实时更新 + +#### 5.2 MQ推送(画面变更) + +**触发场景**:组态工具修改画面(新增/修改/删除元素、变更信号绑定) + +**MQ消息流程**: + +``` +组态工具 → MQ发送 /cyggraph/update → 后端监听 → 更新缓存 → 通知前端刷新 +``` + +**涉及代码**: + +| 文件 | 作用 | +| :--- | :--- | +| [GraphicUpdateSource.java](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_java/platform/02_platservice/sunri-service-graphic/sunri-service-graphic-core/src/main/java/com/sunri/task/source/GraphicUpdateSource.java) | MQ消息监听 | +| [GraphicTask.java](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_java/platform/02_platservice/sunri-service-graphic/sunri-service-graphic-core/src/main/java/com/sunri/task/GraphicTask.java) | 任务处理 | +| [WebFrontNotify.java](file:///home/liumangmang/IdeaProjects/PR7050/V1.00-web/V1.00/src_java/platform/01_componnet/03_sunri-unify-notify/src/main/java/com/sunri/WebFrontNotify.java) | 前端通知 | + +**前端通知类型**: + +```java +// NotifyMessageTypeConstants.java +public static final String GRAPHIC_UPDATE = "graphic-update"; // 画面更新 +public static final String SIGNBOARD_UPDATE = "graphic-signboard-update"; // 看板更新 +public static final String GRAPHIC_PICTURE_CLEAR = "graphic-picture-clear"; // 画面清除 +public static final String GRAPHIC_SITE_CLEAR = "graphic-site-clear"; // 站点清除 +``` + +## 3. 缓存机制详解 + +### 3.1 文件缓存 + +**目的**:加速画面结构数据加载,减少数据库查询 + +**存储位置**: + +``` +主接线图缓存:{wiringPath}/1/{siteId}/{timestamp}.dat +普通图形缓存:{picturePath}/1/{siteId}/{picId}/{timestamp}.dat +``` + +**更新时机**: + +1. 应用启动时初始化 +2. 收到 `/cyggraph/update` MQ消息时更新 + +**实现逻辑**: + +```java +// BasePictureServiceImpl.java +public void updateGraphicCacheFile(List subStationList, boolean init) { + subStationList.forEach(subStation -> { + // 查询数据库获取图形数据 + List picFileInfoList = graphGroupMapper.getSimpleGraphProperties(null); + + picFileInfoList.forEach(picFileInfo -> { + // 创建缓存目录 + File dir = getPictureDirectory(subStation.getSubId(), picFileInfo.getPicId()); + dir.mkdirs(); + + // 文件名使用 lastSaveTime 时间戳(防止缓存问题) + String fileName = String.valueOf(DateUtils.parseESOrCHDateTime(picFileInfo.getLastSaveTime()).getTime()); + + // 创建缓存文件 + File file = createPicFileCache(subStation.getSubId(), picFileInfo.getPicId(), + Paths.get(dir.getAbsolutePath(), fileName).toString()); + }); + }); +} +``` + +### 3.2 内存缓存 + +**目的**:加速实时数据查询,减少RTDB访问 + +**缓存内容**: + +| Helper | 缓存内容 | 用途 | +| :--- | :--- | :--- | +| MeasureHelper | `siteParamGroupCache`(信号配置)、`siteHoverInfoCache`(悬浮信息) | 获取信号配置和悬浮提示 | +| TopologyHelper | 拓扑着色相关缓存 | 主接线图动态着色 | + +**清除时机**:收到 `/cyggraph/update` MQ消息时清除 + +```java +// GraphicAggregate.java +public void clearCache(Integer siteId) { + for (GraphicHelper graphicHelper : graphicHelperList) { + graphicHelper.clearCache(siteId); + } +} +``` + +## 4. 关键注意事项 + +### 4.1 轮询与推送的适用场景 + +| 变更类型 | 轮询(/api/rt/data/get) | MQ推送(/cyggraph/update) | +| :--- | :--- | :--- | +| 信号值变化 | ✅ 可以处理 | ❌ 不需要 | +| 新增图形元素 | ❌ 无法处理 | ✅ 必须使用 | +| 删除图形元素 | ❌ 无法处理 | ✅ 必须使用 | +| 修改图形元素属性 | ❌ 无法处理 | ✅ 必须使用 | +| 修改信号绑定关系 | ❌ 无法处理 | ✅ 必须使用 | + +**核心原因**:轮询请求的信号ID列表来源于已加载的静态数据,无法发现画面结构的变化。 + +### 4.2 时间戳文件名设计 + +```java +String fileName = String.valueOf(DateUtils.parseESOrCHDateTime(picFileInfo.getLastSaveTime()).getTime()); +``` + +**作用**: +- 每次画面修改后,`lastSaveTime` 更新,文件名变更 +- 避免浏览器缓存问题(文件名变更相当于资源版本更新) +- 前端能正确获取最新内容 + +### 4.3 信号绑定机制 + +**一对一绑定**:一个动态元素绑定一个信号点 + +```json +{"signalId": "45_1001"} // tableId_recordId +``` + +**一对多绑定**:一个元素绑定多个信号点(复合元素) + +```json +{"signalIds": ["45_1001", "46_2001"]} +``` + +### 4.4 数据一致性 + +**潜在问题**: + +1. **缓存不一致**:文件缓存更新后,前端可能仍使用旧缓存 +2. **信号ID过期**:画面变更后,旧的信号ID可能不再有效 +3. **轮询中断**:网络异常导致轮询失败 + +**解决方案**: + +1. 使用时间戳文件名强制刷新 +2. MQ推送通知前端重新加载画面 +3. 前端增加异常处理和重试机制 + +### 4.5 性能优化建议 + +**后端优化**: + +1. **批量查询**:一次请求获取多个信号的数据 +2. **缓存策略**:文件缓存 + 内存缓存双重优化 +3. **异步处理**:MQ消息异步处理,不阻塞主线程 + +**前端优化**: + +1. **按需加载**:只加载当前画面需要的信号 +2. **防抖节流**:避免频繁请求 +3. **局部更新**:只更新变化的元素,不重绘整个画面 + +## 5. 完整数据闭环图 + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ 用户打开全景监控页面 │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ 阶段1: 获取顶部Tab菜单 │ +│ POST /userManage/dynamic/route │ +│ 返回: 全站总览图列表(pictype=8) │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ 阶段2: 加载左侧树 │ +│ GET /api/graph/picture/get/baseGraph/tree/list/v2 │ +│ 参数: equipmentType=主设备/辅设备/巡视设备, siteId │ +│ 返回: 画面分类结构(节点包含 picId) │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ 阶段3: 用户选择画面(树节点或Tab) │ +│ - 树节点: nodeId = picId │ +│ - Tab: fixedPicId = 画面ID │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ 阶段4: 获取画面属性(静态数据) │ +│ GET /api/graph/picture/get/graph/properties?picId=xxx │ +│ 返回: 画面JSON内容 + sigIds(动态信号ID列表) │ +│ 渲染过程中: GET /api/graph/picture/get/drawing/properties?elementId=xxx│ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ 阶段5: 获取实时数据(动态数据) │ +│ POST /api/rt/data/get(携带 sigIds 中的信号ID) │ +│ 返回: 信号实时值 │ +│ 前端绑定: 信号值 → 画面动态元素 │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────┐ +│ 阶段6: 定时轮询刷新(持续循环) │ +│ setInterval(1-3秒) → POST /api/rt/data/get → 更新元素显示 │ +└─────────────────────────────────────────────────────────────────────────┘ + │ + ▲ │ ▼ + │ │ │ + ┌─────┴─────┐ │ ┌─────┴─────┐ + │ │ │ │ │ + │ MQ推送 │◄──┴───┤ 画面变更 │ + │ │ │ │ + └─────┬─────┘ └─────┬─────┘ + │ │ + ▼ ▼ + ┌─────────────────┐ ┌─────────────────┐ + │ 更新文件缓存 │ │ 组态工具修改画面 │ + │ 清除内存缓存 │ │ │ + │ 通知前端刷新 │ └─────────────────┘ + └────────┬────────┘ + │ + ▼ + ┌─────────────────┐ + │ 前端重新执行阶段4│ + │ 更新画面结构 │ + │ 继续轮询 │ + └─────────────────┘ +``` + +## 6. 总结 + +全景监控模块的数据加载流程是一个典型的**静态结构 + 动态数据 + 实时刷新**的架构模式: + +1. **顶部Tab菜单**:通过 `/userManage/dynamic/route` 获取全站总览图列表,提供一级导航 +2. **左侧导航树**:通过 `/api/graph/picture/get/baseGraph/tree/list/v2` 按设备类型分类,提供二级导航 +3. **静态结构**:通过 `/api/graph/picture/get/graph/properties` 获取画面的完整定义 +4. **动态数据**:通过 `/api/rt/data/get` 获取信号的实时值 +5. **实时刷新**:通过定时轮询保持数据更新,通过 MQ 推送处理画面变更 +6. **缓存优化**:文件缓存加速静态数据加载,内存缓存加速实时数据查询 + +**核心设计思想**: + +- **分离静态与动态**:画面结构(静态)与信号数据(动态)分离,便于独立更新 +- **轮询与推送结合**:常规数据轮询,画面变更推送,兼顾效率和实时性 +- **多级缓存**:文件缓存 + 内存缓存,减少数据库和RTDB访问 +- **前端组件分层**:`MainDeviceMonitoring`(业务层)→ `ControlHMI`(核心监控组件)→ `Graph`(画面渲染组件) + +理解这个流程对于开发和维护全景监控模块至关重要,特别是在处理画面更新、性能优化和故障排查时。 + +--- + +PR7050 全景监控数据加载流程解析 · 基于实际代码分析 · 最后更新 2026-06