12 KiB
12 KiB
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 定义
type Props = {
sid: string; // 场景ID
id?: string; // 机器人组ID(可选)
};
2. 核心状态管理
2.1 编辑器实例
const editor = shallowRef<EditorService>();
- 作用: 保存 2D 编辑器实例
- 生命周期: 在
onMounted
中初始化
2.2 WebSocket 连接
const client = shallowRef<WebSocket>();
- 作用: 维护与后端的实时连接
- 生命周期: 在组件卸载时自动关闭
2.3 当前选中对象
const current = ref<{ type: 'robot' | 'point' | 'line' | 'area'; id: string }>();
- 作用: 跟踪当前选中的对象类型和ID
- 用途: 控制右侧详情面板的显示
核心业务方法详解
1. 场景加载 (readScene
)
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);
};
功能: 加载场景数据 执行流程:
- 根据是否传入机器人组ID选择不同的API调用
- 设置场景标题
- 将场景JSON数据加载到编辑器中
调用时机: 组件挂载时
2. 实时监控 (monitorScene
)
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连接,接收机器人实时数据 执行流程:
- 关闭现有连接
- 创建新的WebSocket连接
- 设置消息处理函数
- 解析接收到的机器人数据
- 更新编辑器中的机器人状态
数据处理逻辑:
- 检查机器人是否存在于当前场景
- 更新机器人基本信息
- 根据坐标有效性控制机器人可见性
- 刷新机器人位置、状态、角度和路径
3. 对象选择处理
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;
},
);
功能: 监听编辑器选中对象变化 执行流程:
- 获取当前选中对象的ID
- 根据对象类型设置当前状态
- 特殊处理机器人选择(避免被覆盖)
4. 机器人选择 (selectRobot
)
const selectRobot = (id: string) => {
current.value = { type: 'robot', id };
editor.value?.inactive();
};
功能: 选择特定机器人 执行流程:
- 设置当前选中对象为机器人类型
- 取消编辑器中的其他激活状态
EditorService 核心方法分析
1. 机器人管理
1.1 初始化机器人 (initRobots
)
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);
}),
);
}
功能: 批量初始化机器人图形对象 核心逻辑:
- 遍历所有机器人数据
- 创建对应的图形对象(Pen)
- 设置机器人图片、尺寸、标签等属性
- 添加到2D画布中(初始不可见)
1.2 刷新机器人状态 (refreshRobot
)
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 },
);
}
}
功能: 更新机器人实时状态 核心逻辑:
- 获取现有机器人对象
- 计算相对坐标(以机器人中心为原点)
- 处理路径点的坐标转换
- 根据运行状态切换机器人图片
- 更新位置、角度、路径等信息
2. 场景数据处理
2.1 场景加载 (load
)
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 = [];
}
功能: 完整加载场景数据 执行流程:
- 解析场景JSON数据
- 处理机器人组详情(如果有)
- 分别加载机器人、点位、路线、区域
- 重置历史记录
2.2 场景保存 (save
)
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 执行流程:
- 收集所有场景元素
- 将图形对象转换为场景数据格式
- 过滤无效数据
- 序列化为JSON字符串
3. 2D 渲染系统
3.1 自定义渲染函数注册
#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 机器人渲染函数
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. 数据观察者模式
public readonly points = useObservable<MapPen[], MapPen[]>(
this.#change$$.pipe(
filter((v) => v),
debounceTime(100),
map(() => this.find('point')),
),
{ initialValue: new Array<MapPen>() },
);
功能: 创建响应式点位数据流 流程:
- 监听变化事件流
- 过滤有效变化
- 防抖处理(100ms)
- 查找所有点位对象
- 返回响应式数据
2. 实时数据更新流程
WebSocket 消息 → 解析 JSON → 验证机器人 → 更新状态 → 触发重渲染
UI 交互设计
1. 布局结构
┌─────────────────────────────────────────┐
│ 标题栏 │
├─────────────┬───────────────────────────┤
│ 左侧面板 │ 2D 画布区域 │
│ ┌─────────┐ │ │
│ │机器人列表│ │ │
│ ├─────────┤ │ │
│ │ 库区列表 │ │ │
│ ├─────────┤ │ │
│ │高级组列表│ │ │
│ └─────────┘ │ │
└─────────────┴───────────────────────────┤
│
┌─────────┐ │
│详情面板 │ │
└─────────┘ │
│
2. 交互逻辑
- 点击机器人列表 → 选中机器人 → 显示机器人详情
- 点击画布对象 → 选中对象 → 显示对应详情面板
- 实时数据更新 → 自动刷新机器人位置和路径
性能优化要点
1. 响应式数据优化
- 使用
shallowRef
避免深度响应式 - 防抖处理避免频繁更新
- 条件渲染减少不必要的组件创建
2. 渲染优化
- 自定义渲染函数提升性能
- 按需更新,避免全量重绘
- 使用 Canvas 分层渲染
3. 内存管理
- 组件卸载时清理 WebSocket 连接
- 合理使用
onMounted
和onUnmounted
维护指南
1. 添加新的机器人类型
- 在
RobotType
枚举中添加新类型 - 准备对应的图片资源
- 在
#mapRobotImage
方法中添加映射逻辑
2. 扩展实时数据字段
- 更新
RobotRealtimeInfo
接口 - 修改
monitorScene
中的数据解析逻辑 - 更新
refreshRobot
方法处理新字段
3. 添加新的地图元素
- 定义新的类型接口
- 实现对应的渲染函数
- 在
#register
方法中注册 - 添加相应的UI组件
4. 调试技巧
- 使用浏览器开发者工具查看 Canvas 元素
- 通过
editor.value.data()
获取完整场景数据 - 监听 WebSocket 消息验证数据传输
- 使用 Vue DevTools 查看响应式数据状态
常见问题排查
1. 机器人不显示
- 检查 WebSocket 连接状态
- 验证机器人ID是否匹配
- 确认坐标数据有效性
2. 路径不更新
- 检查路径数据格式
- 验证坐标转换逻辑
- 确认渲染函数正确注册
3. 性能问题
- 检查是否有内存泄漏
- 优化频繁的数据更新
- 考虑分页或虚拟滚动
此文档基于当前代码版本分析,如有代码更新请同步更新文档。