feat: monitor robot moving

This commit is contained in:
chndfang 2025-05-25 16:45:45 +08:00
parent d6c15c2235
commit 224b7e3cbf
6 changed files with 99 additions and 55 deletions

View File

@ -1,9 +1,11 @@
# 页面路由
1. 场景编辑
/scene-editor/:id 【id为场景id】
/scene-editor/:id [id为场景id]
2. 组编辑
/group-editor/:sid/:id 【sid为场景idid为机器人组id】
/group-editor/:sid/:id [sid为场景idid为机器人组id]
3. 运行监控
/movement-supervision/:sid [sid为场景id]
# 场景接口
@ -113,6 +115,38 @@ POST /scene/saveByGroupId
响应体:
## 实时监控场景
WebSocket /scene/monitor/:id [id为场景id]
实时数据:
RobotRealtimeInfo [JSON]
示例:
```json
{
"id": "mock-robot-1",
"label": "模拟机器人A",
"brand": "模拟品牌A",
"type": 1,
"ip": "127.0.1.1",
"isConnected": true,
"state": 4,
"canOrder": true,
"canStop": true,
"canControl": true,
"x": 800,
"y": 500,
"active": true,
"angle": -90,
"path": [
[600, 500],
[100, 400]
]
}
```
# 机器人接口
## 获取所有机器人
@ -354,6 +388,13 @@ export interface RobotDetail extends RobotInfo {
taskBattery?: number; // 任务电量
swapBattery?: number; // 交换电量
}
export interface RobotRealtimeInfo extends RobotInfo {
x: number; // 坐标x
y: number; // 坐标y
active?: boolean; // 是否运行
angle?: number; // 旋转角度
path?: Array<[number, number]>; // 规划路径
}
enum RobotBrand {
'先工' = 1,

View File

@ -21,11 +21,7 @@ const points = computed<MapPen[]>(() =>
//#region 线
const routes = computed<MapPen[]>(() =>
editor.value.routes.value.filter(({ label }) => {
console.log(label);
return label?.includes(keyword.value);
}),
editor.value.routes.value.filter(({ label }) => label?.includes(keyword.value)),
);
//#endregion

View File

@ -195,5 +195,10 @@ const selectRobot = (id: string) => {
width: 320px;
height: calc(100% - 96px);
overflow: visible;
pointer-events: none;
& > * {
pointer-events: all;
}
}
</style>

View File

@ -57,10 +57,10 @@ onMounted(async () => {
let x = 800;
let y = 500;
const active = true;
const angle = 0;
const angle = -90;
const path = <[number, number][]>[
[600, 500],
// [100, 400],
[100, 400],
];
editor.value?.refreshRobot(id, { x, y, active, angle, path });
const test = () =>
@ -69,7 +69,7 @@ onMounted(async () => {
editor.value?.refreshRobot(id, { x, y });
test();
});
// test();
test();
});
onUnmounted(() => {
client.value?.close();
@ -146,14 +146,6 @@ const selectRobot = (id: string) => {
background-color: transparent !important;
}
.toolbar-container {
position: fixed;
bottom: 40px;
left: 50%;
z-index: 100;
transform: translateX(-50%);
}
.card-container {
position: fixed;
top: 80px;
@ -162,5 +154,10 @@ const selectRobot = (id: string) => {
width: 320px;
height: calc(100% - 96px);
overflow: visible;
pointer-events: none;
& > * {
pointer-events: all;
}
}
</style>

View File

@ -195,5 +195,10 @@ const selectRobot = (id: string) => {
width: 320px;
height: calc(100% - 96px);
overflow: visible;
pointer-events: none;
& > * {
pointer-events: all;
}
}
</style>

View File

@ -374,12 +374,16 @@ export class EditorService extends Meta2d {
}
public refreshRobot(id: RobotInfo['id'], info: Partial<RobotRealtimeInfo>): void {
const { rotate: or, robot } = this.getPenById(id) ?? {};
const pen = this.getPenById(id);
const { rotate: or, robot } = pen ?? {};
if (!robot?.type) return;
const { x: ex = 37, y: ey = 37, active, angle, path } = info;
const x = ex - 37;
const y = ey - 37;
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(([px, py]) => [px - cx, py - cy]) ?? robot.path?.map(([ex, ey]) => [ex + ox! - x, ey + 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 });
@ -850,41 +854,37 @@ function drawRobot(ctx: CanvasRenderingContext2D, pen: MapPen): void {
const { x = 0, y = 0, width: w = 0, height: h = 0, rotate: deg = 0 } = pen.calculative?.worldRect ?? {};
const { active, path } = pen.robot ?? {};
if (!active) return;
const ox = x + w / 2;
const oy = y + h / 2;
ctx.save();
if (active) {
const ox = x + w / 2;
const oy = y + h / 2;
console.log(ox, oy);
ctx.ellipse(ox, oy, w / 2, h / 2, 0, 0, Math.PI * 2);
ctx.fillStyle = get(theme, 'robot.fill') ?? '';
ctx.fill();
ctx.strokeStyle = get(theme, 'robot.stroke') ?? '';
ctx.ellipse(ox, oy, w / 2, h / 2, 0, 0, Math.PI * 2);
ctx.fillStyle = get(theme, 'robot.fill') ?? '';
ctx.fill();
ctx.strokeStyle = get(theme, 'robot.stroke') ?? '';
ctx.stroke();
if (path?.length) {
ctx.strokeStyle = get(theme, 'robot.line') ?? '';
ctx.lineCap = 'round';
ctx.lineWidth = s * 4;
ctx.setLineDash([s * 5, s * 10]);
ctx.translate(ox, oy);
ctx.rotate((-deg * Math.PI) / 180);
ctx.beginPath();
ctx.moveTo(0, 0);
path.forEach(([ex, ey]) => ctx.lineTo(ex * s, ey * s));
ctx.stroke();
if (path?.length) {
ctx.strokeStyle = get(theme, 'robot.line') ?? '';
ctx.lineCap = 'round';
ctx.lineWidth = s * 4;
ctx.setLineDash([s * 5, s * 10]);
ctx.translate(ox, oy);
ctx.rotate((-deg * Math.PI) / 180);
ctx.beginPath();
ctx.moveTo(0, 0);
// todo 缩放时坐标偏移
path.forEach(([ex, ey]) => ctx.lineTo((ex - ox) * s, (ey - oy) * s));
ctx.stroke();
const [x1 = 0, y1 = 0] = nth(path, -1) ?? [];
const [x2 = ox, y2 = oy] = nth(path, -2) ?? [];
const r = Math.atan2(y1 - y2, x1 - x2) + Math.PI;
ctx.setLineDash([0]);
ctx.translate((x1 - ox) * s, (y1 - oy) * s);
ctx.beginPath();
ctx.moveTo(Math.cos(r + Math.PI / 4) * s * 10, Math.sin(r + Math.PI / 4) * s * 10);
ctx.lineTo(0, 0);
ctx.lineTo(Math.cos(r - Math.PI / 4) * s * 10, Math.sin(r - Math.PI / 4) * s * 10);
ctx.stroke();
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
const [ex1 = 0, ey1 = 0] = nth(path, -1) ?? [];
const [ex2 = 0, ey2 = 0] = nth(path, -2) ?? [];
const r = Math.atan2(ey1 - ey2, ex1 - ex2) + Math.PI;
ctx.setLineDash([0]);
ctx.translate(ex1 * s, ey1 * s);
ctx.beginPath();
ctx.moveTo(Math.cos(r + Math.PI / 4) * s * 10, Math.sin(r + Math.PI / 4) * s * 10);
ctx.lineTo(0, 0);
ctx.lineTo(Math.cos(r - Math.PI / 4) * s * 10, Math.sin(r - Math.PI / 4) * s * 10);
ctx.stroke();
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
ctx.restore();
}