docs: add PR7050 panoramic monitoring data flow
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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`)的实际代码,深入分析组态画面数据加载的完整流程、核心接口、缓存机制及关键注意事项。
|
||||
|
||||
<!-- more -->
|
||||
|
||||
## 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<PicFileInfoVo>` | `GraphElementVo` | `List<PicFileInfoVo>` |
|
||||
| **数据粒度** | 完整画面 | 单个图元 | 主接线图画面 |
|
||||
| **数据来源** | `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<SubStation> subStationList, boolean init) {
|
||||
subStationList.forEach(subStation -> {
|
||||
// 查询数据库获取图形数据
|
||||
List<PicFileInfo> 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
|
||||
Reference in New Issue
Block a user