chore: update pnpm lockfile version and dependencies
This commit is contained in:
parent
eabd121f31
commit
bae88af36a
448
movement-supervision-analysis.md
Normal file
448
movement-supervision-analysis.md
Normal file
@ -0,0 +1,448 @@
|
||||
# Movement Supervision 组件架构分析文档
|
||||
|
||||
## 概述
|
||||
|
||||
`movement-supervision.vue` 是一个基于 Vue 3 的运动监控组件,主要用于实时监控和显示机器人的运动状态。该组件基于 Meta2d 2D 图形库构建,提供了机器人位置跟踪、路径显示、区域管理等功能。
|
||||
|
||||
## 核心架构
|
||||
|
||||
### 技术栈
|
||||
- **Vue 3** - 采用 Composition API
|
||||
- **TypeScript** - 类型安全
|
||||
- **Meta2d** - 2D 图形引擎
|
||||
- **RxJS** - 响应式编程
|
||||
- **WebSocket** - 实时通信
|
||||
|
||||
### 依赖关系图
|
||||
```
|
||||
movement-supervision.vue
|
||||
├── EditorService (extends Meta2d)
|
||||
├── Scene API (场景管理)
|
||||
├── Robot API (机器人管理)
|
||||
├── WebSocket (实时数据)
|
||||
└── UI Components
|
||||
├── RobotGroups
|
||||
├── PenGroups
|
||||
├── RobotDetailCard
|
||||
├── PointDetailCard
|
||||
├── RouteDetailCard
|
||||
└── AreaDetailCard
|
||||
```
|
||||
|
||||
## 组件分析
|
||||
|
||||
### 1. Props 定义
|
||||
```typescript
|
||||
type Props = {
|
||||
sid: string; // 场景ID
|
||||
id?: string; // 机器人组ID(可选)
|
||||
};
|
||||
```
|
||||
|
||||
### 2. 核心状态管理
|
||||
|
||||
#### 2.1 编辑器实例
|
||||
```typescript
|
||||
const editor = shallowRef<EditorService>();
|
||||
```
|
||||
- **作用**: 保存 2D 编辑器实例
|
||||
- **生命周期**: 在 `onMounted` 中初始化
|
||||
|
||||
#### 2.2 WebSocket 连接
|
||||
```typescript
|
||||
const client = shallowRef<WebSocket>();
|
||||
```
|
||||
- **作用**: 维护与后端的实时连接
|
||||
- **生命周期**: 在组件卸载时自动关闭
|
||||
|
||||
#### 2.3 当前选中对象
|
||||
```typescript
|
||||
const current = ref<{ type: 'robot' | 'point' | 'line' | 'area'; id: string }>();
|
||||
```
|
||||
- **作用**: 跟踪当前选中的对象类型和ID
|
||||
- **用途**: 控制右侧详情面板的显示
|
||||
|
||||
## 核心业务方法详解
|
||||
|
||||
### 1. 场景加载 (`readScene`)
|
||||
|
||||
```typescript
|
||||
const readScene = async () => {
|
||||
const res = props.id ? await getSceneByGroupId(props.id, props.sid) : await getSceneById(props.sid);
|
||||
title.value = res?.label ?? '';
|
||||
editor.value?.load(res?.json);
|
||||
};
|
||||
```
|
||||
|
||||
**功能**: 加载场景数据
|
||||
**执行流程**:
|
||||
1. 根据是否传入机器人组ID选择不同的API调用
|
||||
2. 设置场景标题
|
||||
3. 将场景JSON数据加载到编辑器中
|
||||
|
||||
**调用时机**: 组件挂载时
|
||||
|
||||
### 2. 实时监控 (`monitorScene`)
|
||||
|
||||
```typescript
|
||||
const monitorScene = async () => {
|
||||
client.value?.close();
|
||||
const ws = await monitorSceneById(props.sid);
|
||||
if (isNil(ws)) return;
|
||||
ws.onmessage = (e) => {
|
||||
const { id, x, y, active, angle, path, ...rest } = <RobotRealtimeInfo>JSON.parse(e.data || '{}');
|
||||
if (!editor.value?.checkRobotById(id)) return;
|
||||
editor.value?.updateRobot(id, rest);
|
||||
if (isNil(x) || isNil(y)) {
|
||||
editor.value.updatePen(id, { visible: false });
|
||||
} else {
|
||||
editor.value.refreshRobot(id, { x, y, active, angle, path });
|
||||
}
|
||||
};
|
||||
client.value = ws;
|
||||
};
|
||||
```
|
||||
|
||||
**功能**: 建立WebSocket连接,接收机器人实时数据
|
||||
**执行流程**:
|
||||
1. 关闭现有连接
|
||||
2. 创建新的WebSocket连接
|
||||
3. 设置消息处理函数
|
||||
4. 解析接收到的机器人数据
|
||||
5. 更新编辑器中的机器人状态
|
||||
|
||||
**数据处理逻辑**:
|
||||
- 检查机器人是否存在于当前场景
|
||||
- 更新机器人基本信息
|
||||
- 根据坐标有效性控制机器人可见性
|
||||
- 刷新机器人位置、状态、角度和路径
|
||||
|
||||
### 3. 对象选择处理
|
||||
|
||||
```typescript
|
||||
watch(
|
||||
() => editor.value?.selected.value[0],
|
||||
(v) => {
|
||||
const pen = editor.value?.getPenById(v);
|
||||
if (pen?.id) {
|
||||
current.value = { type: <'point' | 'line' | 'area'>pen.name, id: pen.id };
|
||||
return;
|
||||
}
|
||||
if (current.value?.type === 'robot') return;
|
||||
current.value = undefined;
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
**功能**: 监听编辑器选中对象变化
|
||||
**执行流程**:
|
||||
1. 获取当前选中对象的ID
|
||||
2. 根据对象类型设置当前状态
|
||||
3. 特殊处理机器人选择(避免被覆盖)
|
||||
|
||||
### 4. 机器人选择 (`selectRobot`)
|
||||
|
||||
```typescript
|
||||
const selectRobot = (id: string) => {
|
||||
current.value = { type: 'robot', id };
|
||||
editor.value?.inactive();
|
||||
};
|
||||
```
|
||||
|
||||
**功能**: 选择特定机器人
|
||||
**执行流程**:
|
||||
1. 设置当前选中对象为机器人类型
|
||||
2. 取消编辑器中的其他激活状态
|
||||
|
||||
## EditorService 核心方法分析
|
||||
|
||||
### 1. 机器人管理
|
||||
|
||||
#### 1.1 初始化机器人 (`initRobots`)
|
||||
```typescript
|
||||
public async initRobots(): Promise<void> {
|
||||
await Promise.all(
|
||||
this.robots.map(async ({ id, label, type }) => {
|
||||
const pen: MapPen = {
|
||||
...this.#mapRobotImage(type, true),
|
||||
id,
|
||||
name: 'robot',
|
||||
tags: ['robot'],
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 74,
|
||||
height: 74,
|
||||
lineWidth: 1,
|
||||
robot: { type },
|
||||
visible: false,
|
||||
text: label,
|
||||
textTop: -24,
|
||||
whiteSpace: 'nowrap',
|
||||
ellipsis: false,
|
||||
locked: LockState.Disable,
|
||||
};
|
||||
await this.addPen(pen, false, true, true);
|
||||
}),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**功能**: 批量初始化机器人图形对象
|
||||
**核心逻辑**:
|
||||
1. 遍历所有机器人数据
|
||||
2. 创建对应的图形对象(Pen)
|
||||
3. 设置机器人图片、尺寸、标签等属性
|
||||
4. 添加到2D画布中(初始不可见)
|
||||
|
||||
#### 1.2 刷新机器人状态 (`refreshRobot`)
|
||||
```typescript
|
||||
public refreshRobot(id: RobotInfo['id'], info: Partial<RobotRealtimeInfo>): void {
|
||||
const pen = this.getPenById(id);
|
||||
const { rotate: or, robot } = pen ?? {};
|
||||
if (!robot?.type) return;
|
||||
const { x: ox, y: oy } = this.getPenRect(pen!);
|
||||
const { x: cx = 37, y: cy = 37, active, angle, path: points } = info;
|
||||
const x = cx - 37;
|
||||
const y = cy - 37;
|
||||
const rotate = angle ?? or;
|
||||
const path =
|
||||
points?.map((p) => ({ x: p.x - cx, y: p.y - cy })) ??
|
||||
robot.path?.map((p) => ({ x: p.x + ox! - x, y: p.y + oy! - y }));
|
||||
const o = { ...robot, ...omitBy({ active, path }, isNil) };
|
||||
if (isNil(active)) {
|
||||
this.setValue({ id, x, y, rotate, robot: o, visible: true }, { render: true, history: false, doEvent: false });
|
||||
} else {
|
||||
this.setValue(
|
||||
{ id, ...this.#mapRobotImage(robot.type, active), x, y, rotate, robot: o, visible: true },
|
||||
{ render: true, history: false, doEvent: false },
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**功能**: 更新机器人实时状态
|
||||
**核心逻辑**:
|
||||
1. 获取现有机器人对象
|
||||
2. 计算相对坐标(以机器人中心为原点)
|
||||
3. 处理路径点的坐标转换
|
||||
4. 根据运行状态切换机器人图片
|
||||
5. 更新位置、角度、路径等信息
|
||||
|
||||
### 2. 场景数据处理
|
||||
|
||||
#### 2.1 场景加载 (`load`)
|
||||
```typescript
|
||||
public async load(map?: string, editable = false, detail?: Partial<GroupSceneDetail>): Promise<void> {
|
||||
const scene: StandardScene = map ? JSON.parse(map) : {};
|
||||
if (!isEmpty(detail?.group)) {
|
||||
scene.robotGroups = [detail.group];
|
||||
scene.robots = detail.robots;
|
||||
}
|
||||
const { robotGroups, robots, points, routes, areas } = scene;
|
||||
this.open();
|
||||
this.setState(editable);
|
||||
this.#loadRobots(robotGroups, robots);
|
||||
await this.#loadScenePoints(points);
|
||||
this.#loadSceneRoutes(routes);
|
||||
await this.#loadSceneAreas(areas);
|
||||
this.store.historyIndex = undefined;
|
||||
this.store.histories = [];
|
||||
}
|
||||
```
|
||||
|
||||
**功能**: 完整加载场景数据
|
||||
**执行流程**:
|
||||
1. 解析场景JSON数据
|
||||
2. 处理机器人组详情(如果有)
|
||||
3. 分别加载机器人、点位、路线、区域
|
||||
4. 重置历史记录
|
||||
|
||||
#### 2.2 场景保存 (`save`)
|
||||
```typescript
|
||||
public save(): string {
|
||||
const scene: StandardScene = {
|
||||
robotGroups: this.robotGroups.value,
|
||||
robots: this.robots,
|
||||
points: this.points.value.map((v) => this.#mapScenePoint(v)).filter((v) => !isNil(v)),
|
||||
routes: this.routes.value.map((v) => this.#mapSceneRoute(v)).filter((v) => !isNil(v)),
|
||||
areas: this.areas.value.map((v) => this.#mapSceneArea(v)).filter((v) => !isNil(v)),
|
||||
blocks: [],
|
||||
};
|
||||
return JSON.stringify(scene);
|
||||
}
|
||||
```
|
||||
|
||||
**功能**: 将当前场景序列化为JSON
|
||||
**执行流程**:
|
||||
1. 收集所有场景元素
|
||||
2. 将图形对象转换为场景数据格式
|
||||
3. 过滤无效数据
|
||||
4. 序列化为JSON字符串
|
||||
|
||||
### 3. 2D 渲染系统
|
||||
|
||||
#### 3.1 自定义渲染函数注册
|
||||
```typescript
|
||||
#register() {
|
||||
this.register({ line: () => new Path2D() });
|
||||
this.registerCanvasDraw({ point: drawPoint, line: drawLine, area: drawArea, robot: drawRobot });
|
||||
this.registerAnchors({ point: anchorPoint });
|
||||
this.addDrawLineFn('bezier2', lineBezier2);
|
||||
this.addDrawLineFn('bezier3', lineBezier3);
|
||||
}
|
||||
```
|
||||
|
||||
**功能**: 注册自定义渲染函数
|
||||
**包含内容**:
|
||||
- 点位渲染 (`drawPoint`)
|
||||
- 线路渲染 (`drawLine`)
|
||||
- 区域渲染 (`drawArea`)
|
||||
- 机器人渲染 (`drawRobot`)
|
||||
- 贝塞尔曲线渲染
|
||||
|
||||
#### 3.2 机器人渲染函数
|
||||
```typescript
|
||||
function drawRobot(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
const { robot } = pen;
|
||||
if (!robot?.path?.length) return;
|
||||
const { lineWidth = 1, strokeStyle = sTheme.color.primary } = pen;
|
||||
ctx.save();
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = strokeStyle;
|
||||
ctx.lineWidth = lineWidth;
|
||||
ctx.setLineDash([6, 3]);
|
||||
robot.path.forEach(({ x, y }, i) => {
|
||||
if (i === 0) {
|
||||
ctx.moveTo(x, y);
|
||||
} else {
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
});
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
```
|
||||
|
||||
**功能**: 绘制机器人路径
|
||||
**渲染逻辑**:
|
||||
1. 检查路径数据有效性
|
||||
2. 设置画笔样式(虚线)
|
||||
3. 绘制路径连线
|
||||
4. 恢复画布状态
|
||||
|
||||
## 响应式数据流
|
||||
|
||||
### 1. 数据观察者模式
|
||||
```typescript
|
||||
public readonly points = useObservable<MapPen[], MapPen[]>(
|
||||
this.#change$$.pipe(
|
||||
filter((v) => v),
|
||||
debounceTime(100),
|
||||
map(() => this.find('point')),
|
||||
),
|
||||
{ initialValue: new Array<MapPen>() },
|
||||
);
|
||||
```
|
||||
|
||||
**功能**: 创建响应式点位数据流
|
||||
**流程**:
|
||||
1. 监听变化事件流
|
||||
2. 过滤有效变化
|
||||
3. 防抖处理(100ms)
|
||||
4. 查找所有点位对象
|
||||
5. 返回响应式数据
|
||||
|
||||
### 2. 实时数据更新流程
|
||||
|
||||
```
|
||||
WebSocket 消息 → 解析 JSON → 验证机器人 → 更新状态 → 触发重渲染
|
||||
```
|
||||
|
||||
## UI 交互设计
|
||||
|
||||
### 1. 布局结构
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 标题栏 │
|
||||
├─────────────┬───────────────────────────┤
|
||||
│ 左侧面板 │ 2D 画布区域 │
|
||||
│ ┌─────────┐ │ │
|
||||
│ │机器人列表│ │ │
|
||||
│ ├─────────┤ │ │
|
||||
│ │ 库区列表 │ │ │
|
||||
│ ├─────────┤ │ │
|
||||
│ │高级组列表│ │ │
|
||||
│ └─────────┘ │ │
|
||||
└─────────────┴───────────────────────────┤
|
||||
│
|
||||
┌─────────┐ │
|
||||
│详情面板 │ │
|
||||
└─────────┘ │
|
||||
│
|
||||
```
|
||||
|
||||
### 2. 交互逻辑
|
||||
- **点击机器人列表** → 选中机器人 → 显示机器人详情
|
||||
- **点击画布对象** → 选中对象 → 显示对应详情面板
|
||||
- **实时数据更新** → 自动刷新机器人位置和路径
|
||||
|
||||
## 性能优化要点
|
||||
|
||||
### 1. 响应式数据优化
|
||||
- 使用 `shallowRef` 避免深度响应式
|
||||
- 防抖处理避免频繁更新
|
||||
- 条件渲染减少不必要的组件创建
|
||||
|
||||
### 2. 渲染优化
|
||||
- 自定义渲染函数提升性能
|
||||
- 按需更新,避免全量重绘
|
||||
- 使用 Canvas 分层渲染
|
||||
|
||||
### 3. 内存管理
|
||||
- 组件卸载时清理 WebSocket 连接
|
||||
- 合理使用 `onMounted` 和 `onUnmounted`
|
||||
|
||||
## 维护指南
|
||||
|
||||
### 1. 添加新的机器人类型
|
||||
1. 在 `RobotType` 枚举中添加新类型
|
||||
2. 准备对应的图片资源
|
||||
3. 在 `#mapRobotImage` 方法中添加映射逻辑
|
||||
|
||||
### 2. 扩展实时数据字段
|
||||
1. 更新 `RobotRealtimeInfo` 接口
|
||||
2. 修改 `monitorScene` 中的数据解析逻辑
|
||||
3. 更新 `refreshRobot` 方法处理新字段
|
||||
|
||||
### 3. 添加新的地图元素
|
||||
1. 定义新的类型接口
|
||||
2. 实现对应的渲染函数
|
||||
3. 在 `#register` 方法中注册
|
||||
4. 添加相应的UI组件
|
||||
|
||||
### 4. 调试技巧
|
||||
- 使用浏览器开发者工具查看 Canvas 元素
|
||||
- 通过 `editor.value.data()` 获取完整场景数据
|
||||
- 监听 WebSocket 消息验证数据传输
|
||||
- 使用 Vue DevTools 查看响应式数据状态
|
||||
|
||||
## 常见问题排查
|
||||
|
||||
### 1. 机器人不显示
|
||||
- 检查 WebSocket 连接状态
|
||||
- 验证机器人ID是否匹配
|
||||
- 确认坐标数据有效性
|
||||
|
||||
### 2. 路径不更新
|
||||
- 检查路径数据格式
|
||||
- 验证坐标转换逻辑
|
||||
- 确认渲染函数正确注册
|
||||
|
||||
### 3. 性能问题
|
||||
- 检查是否有内存泄漏
|
||||
- 优化频繁的数据更新
|
||||
- 考虑分页或虚拟滚动
|
||||
|
||||
---
|
||||
|
||||
*此文档基于当前代码版本分析,如有代码更新请同步更新文档。*
|
4705
pnpm-lock.yaml
generated
4705
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user