feat: 添加库位状态监控功能,更新相关接口、主题和组件以支持状态显示
This commit is contained in:
parent
cfb04396e2
commit
4379bcc533
@ -16,6 +16,9 @@ export interface MapPen extends Pen {
|
||||
activeAttrs?: Array<string>; // 已激活的额外属性
|
||||
|
||||
properties?: unknown; // 第三方附加参数
|
||||
storageStatus?: Record<string, unknown>; // 库位状态
|
||||
statusStyle?: string; // 状态颜色
|
||||
strokeStyle?: string; // 边框颜色
|
||||
}
|
||||
|
||||
//#region 点位
|
||||
|
@ -14,6 +14,7 @@ const enum API {
|
||||
|
||||
实时监控场景 = '/scene/monitor/:id',
|
||||
实时监控真实场景 = '/scene/monitor/real/:id',
|
||||
实时监控库位状态 = '/scene/monitor/storage/:id',
|
||||
}
|
||||
|
||||
export async function getSceneById(id: SceneInfo['id']): Promise<SceneDetail | null> {
|
||||
@ -89,6 +90,17 @@ export async function saveSceneByGroupId(id: RobotGroup['id'], sid: RobotGroup['
|
||||
}
|
||||
}
|
||||
|
||||
export async function monitorStorageStatusById(id: SceneInfo['id']): Promise<WebSocket | null> {
|
||||
if (!id) return null;
|
||||
try {
|
||||
const socket = await ws.create(API.实时监控库位状态.replace(':id', id));
|
||||
return socket;
|
||||
} catch (error) {
|
||||
console.debug(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function monitorSceneById(id: SceneInfo['id']): Promise<WebSocket | null> {
|
||||
if (!id) return null;
|
||||
try {
|
||||
|
@ -12,7 +12,14 @@
|
||||
},
|
||||
"point-l": {
|
||||
"stroke": "#595959",
|
||||
"strokeActive": "#FCC947"
|
||||
"strokeActive": "#FCC947",
|
||||
"stroke-occupied": "#ff4d4f",
|
||||
"stroke-unoccupied": "#52c41a",
|
||||
"stroke-empty": "#1890ff",
|
||||
"stroke-disabled": "#bfbfbf",
|
||||
"stroke-enabled": "#52c41a",
|
||||
"stroke-locked": "#faad14",
|
||||
"stroke-unlocked": "#52c41a"
|
||||
},
|
||||
"route": {
|
||||
"strokeActive": "#FCC947",
|
||||
|
@ -12,7 +12,14 @@
|
||||
},
|
||||
"point-l": {
|
||||
"stroke": "#595959",
|
||||
"strokeActive": "#EBB214"
|
||||
"strokeActive": "#EBB214",
|
||||
"stroke-occupied": "#ff4d4f",
|
||||
"stroke-unoccupied": "#52c41a",
|
||||
"stroke-empty": "#1890ff",
|
||||
"stroke-disabled": "#bfbfbf",
|
||||
"stroke-enabled": "#52c41a",
|
||||
"stroke-locked": "#faad14",
|
||||
"stroke-unlocked": "#52c41a"
|
||||
},
|
||||
"route": {
|
||||
"strokeActive": "#EBB214",
|
||||
|
@ -52,6 +52,13 @@ const mapAreas = (type: MapAreaType): string => {
|
||||
};
|
||||
const coArea1 = computed<string>(() => mapAreas(MapAreaType.库区));
|
||||
const coArea2 = computed<string>(() => mapAreas(MapAreaType.互斥区));
|
||||
const storageStatus = computed<string>(() => {
|
||||
const status = pen.value?.storageStatus;
|
||||
if (!status) return '暂无';
|
||||
return Object.entries(status)
|
||||
.map(([key, value]) => `${key}: ${value}`)
|
||||
.join(', ');
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -113,6 +120,12 @@ const coArea2 = computed<string>(() => mapAreas(MapAreaType.互斥区));
|
||||
<a-typography-text>{{ coArea2 || $t('暂无') }}</a-typography-text>
|
||||
</a-flex>
|
||||
</a-list-item>
|
||||
<a-list-item v-if="point.type === MapPointType.动作点">
|
||||
<a-flex :gap="8" vertical>
|
||||
<a-typography-text type="secondary">{{ $t('库位状态') }}</a-typography-text>
|
||||
<a-typography-text>{{ storageStatus }}</a-typography-text>
|
||||
</a-flex>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</template>
|
||||
<a-empty v-else :image="sTheme.empty" />
|
||||
|
@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { MapPointType } from '@api/map';
|
||||
import type { RobotRealtimeInfo } from '@api/robot';
|
||||
import { getSceneByGroupId, getSceneById, monitorRealSceneById, monitorSceneById } from '@api/scene';
|
||||
import { EditorService } from '@core/editor.service';
|
||||
@ -46,6 +47,53 @@ const monitorScene = async () => {
|
||||
};
|
||||
//#endregion
|
||||
|
||||
//#region 新增: 监控库位状态
|
||||
const storageStatusClient = shallowRef<WebSocket>();
|
||||
|
||||
const monitorStorageStatus = async () => {
|
||||
storageStatusClient.value?.close();
|
||||
// const ws = await monitorStorageStatusById(props.sid);
|
||||
// if (isNil(ws)) return;
|
||||
|
||||
// --- WebSocket Simulation Start ---
|
||||
const messageHandler = (data: string) => {
|
||||
const { locationName, statusValue, statusAttributes } = JSON.parse(data || '{}');
|
||||
if (!locationName) return;
|
||||
const pen = editor.value?.find(locationName)[0];
|
||||
if (pen?.id) {
|
||||
editor.value?.refreshPoint(pen.id, { status: statusValue, attributes: statusAttributes });
|
||||
}
|
||||
};
|
||||
|
||||
const simulationTimer = setInterval(() => {
|
||||
if (!editor.value) return;
|
||||
// 仅针对动作点进行模拟
|
||||
const actionPoints = editor.value.find('point').filter((p) => p.point?.type === MapPointType.动作点);
|
||||
if (!actionPoints.length) return;
|
||||
|
||||
const randomPoint = actionPoints[Math.floor(Math.random() * actionPoints.length)];
|
||||
const statuses = ['Occupied', 'Unoccupied', 'Empty', 'Disabled', 'Enabled', 'Locked', 'Unlocked'];
|
||||
const randomStatus = statuses[Math.floor(Math.random() * statuses.length)];
|
||||
const mockData = {
|
||||
locationName: randomPoint.id,
|
||||
statusValue: randomStatus,
|
||||
statusAttributes: {
|
||||
Status: randomStatus,
|
||||
Timestamp: new Date().toISOString(),
|
||||
},
|
||||
};
|
||||
messageHandler(JSON.stringify(mockData));
|
||||
}, 2000);
|
||||
|
||||
storageStatusClient.value = {
|
||||
close: () => {
|
||||
clearInterval(simulationTimer);
|
||||
},
|
||||
} as WebSocket;
|
||||
// --- WebSocket Simulation End ---
|
||||
};
|
||||
//#endregion
|
||||
|
||||
const title = ref<string>('');
|
||||
|
||||
const container = shallowRef<HTMLDivElement>();
|
||||
@ -60,11 +108,13 @@ onMounted(async () => {
|
||||
await readScene();
|
||||
await editor.value?.initRobots();
|
||||
await monitorScene();
|
||||
await monitorStorageStatus();
|
||||
// 自动保存和恢复视图状态
|
||||
await handleAutoSaveAndRestoreViewState();
|
||||
});
|
||||
onUnmounted(() => {
|
||||
client.value?.close();
|
||||
storageStatusClient.value?.close();
|
||||
});
|
||||
|
||||
const show = ref<boolean>(true);
|
||||
|
@ -696,6 +696,22 @@ export class EditorService extends Meta2d {
|
||||
const image = type < 10 ? '' : `${import.meta.env.BASE_URL}/point/${type}-${theme}.png`;
|
||||
return { image, canvasLayer: CanvasLayer.CanvasMain };
|
||||
}
|
||||
|
||||
public refreshPoint(id: string, info: { status: string; attributes: Record<string, unknown> }): void {
|
||||
const pen = this.getPenById(id);
|
||||
if (!pen?.point) return;
|
||||
|
||||
const color = get(
|
||||
sTheme.editor,
|
||||
`point-l.stroke-${info.status.toLowerCase()}`,
|
||||
get(sTheme.editor, 'point-l.stroke'),
|
||||
);
|
||||
|
||||
this.setValue(
|
||||
{ id, statusStyle: color, storageStatus: info.attributes },
|
||||
{ render: true, history: false, doEvent: false },
|
||||
);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region 线路
|
||||
@ -948,7 +964,7 @@ function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
const { active, iconSize: r = 0, fontSize = 14, lineHeight = 1.5, fontFamily } = pen.calculative ?? {};
|
||||
const { x = 0, y = 0, width: w = 0, height: h = 0 } = pen.calculative?.worldRect ?? {};
|
||||
const { type } = pen.point ?? {};
|
||||
const { label = '' } = pen ?? {};
|
||||
const { label = '', statusStyle } = pen ?? {};
|
||||
|
||||
ctx.save();
|
||||
switch (type) {
|
||||
@ -996,7 +1012,7 @@ function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
||||
case MapPointType.动作点:
|
||||
case MapPointType.禁行点:
|
||||
ctx.roundRect(x, y, w, h, r);
|
||||
ctx.strokeStyle = get(theme, active ? 'point-l.strokeActive' : 'point-l.stroke') ?? '';
|
||||
ctx.strokeStyle = statusStyle ?? get(theme, active ? 'point-l.strokeActive' : 'point-l.stroke') ?? '';
|
||||
ctx.stroke();
|
||||
break;
|
||||
default:
|
||||
|
Loading…
x
Reference in New Issue
Block a user