refactor(elevator): 重构电梯状态系统以适配后端字段,移除旧枚举并更新UI显示逻辑与动画效果
This commit is contained in:
parent
434d4b72c4
commit
5f7a9b6dce
@ -36,9 +36,11 @@ export interface MapPointInfo {
|
|||||||
isConnected?: boolean; // 连接状态(自动门点、电梯点使用,true=已连接,false=未连接)
|
isConnected?: boolean; // 连接状态(自动门点、电梯点使用,true=已连接,false=未连接)
|
||||||
active?: boolean; // 是否激活状态,用于控制光圈显示
|
active?: boolean; // 是否激活状态,用于控制光圈显示
|
||||||
|
|
||||||
// 电梯点专属属性
|
// 电梯点专属属性(后端字段)
|
||||||
elevatorStatus?: number; // 电梯状态(仅电梯点使用,0=静止,1=开门中,2=关门中,3=上行,4=下行)
|
elevatorDirection?: number; // 电梯方向(0-未知,1-停止,2-向上,3-向下)
|
||||||
currentFloor?: number; // 当前楼层(仅电梯点使用)
|
elevatorFrontDoorStatus?: number; // 电梯前门状态(0-未知,1-关,2-正在开关,3-开)
|
||||||
|
elevatorFloor?: number; // 电梯楼层(0-未知)
|
||||||
|
currentFloor?: number; // 当前楼层(兼容旧字段)
|
||||||
lastUpdate?: number; // 最后更新时间戳
|
lastUpdate?: number; // 最后更新时间戳
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { isNil } from 'lodash-es';
|
|||||||
import { computed, inject, type InjectionKey, ref, type ShallowRef, watch } from 'vue';
|
import { computed, inject, type InjectionKey, ref, type ShallowRef, watch } from 'vue';
|
||||||
|
|
||||||
import { type DeviceMappingDTO,queryDeviceMappingByType } from '../../apis/device/api';
|
import { type DeviceMappingDTO,queryDeviceMappingByType } from '../../apis/device/api';
|
||||||
import { ElevatorStatus,useElevatorStore } from '../../stores/elevator.store';
|
import { useElevatorStore } from '../../stores/elevator.store';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
token: InjectionKey<ShallowRef<EditorService>>;
|
token: InjectionKey<ShallowRef<EditorService>>;
|
||||||
@ -131,26 +131,46 @@ watch(point, (newPoint) => {
|
|||||||
}
|
}
|
||||||
}, { immediate: true });
|
}, { immediate: true });
|
||||||
|
|
||||||
// 获取电梯状态文本
|
// 获取电梯状态文本(基于后端字段)
|
||||||
const getElevatorStatusText = (status: ElevatorStatus): string => {
|
const getElevatorStatusText = (point: any): string => {
|
||||||
const statusMap = {
|
const { elevatorDirection, elevatorFrontDoorStatus, isConnected } = point;
|
||||||
[ElevatorStatus.IDLE]: '静止',
|
|
||||||
[ElevatorStatus.OPENING]: '开门中',
|
// 离线
|
||||||
[ElevatorStatus.CLOSING]: '关门中',
|
if (!isConnected) return '离线';
|
||||||
[ElevatorStatus.MOVING_UP]: '上行中',
|
|
||||||
[ElevatorStatus.MOVING_DOWN]: '下行中',
|
// 优先判断门状态
|
||||||
[ElevatorStatus.DOOR_OPEN]: '门已开',
|
if (elevatorFrontDoorStatus === 2) return '开关门中';
|
||||||
[ElevatorStatus.DOOR_CLOSED]: '门已关',
|
if (elevatorFrontDoorStatus === 3) return '门已开';
|
||||||
[ElevatorStatus.FAULT]: '故障',
|
|
||||||
[ElevatorStatus.OFFLINE]: '离线',
|
// 门已关,根据方向判断
|
||||||
};
|
if (elevatorFrontDoorStatus === 1) {
|
||||||
return statusMap[status] || '未知';
|
if (elevatorDirection === 2) return '上行中';
|
||||||
|
if (elevatorDirection === 3) return '下行中';
|
||||||
|
if (elevatorDirection === 1) return '门已关';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '未知';
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取电梯状态颜色
|
// 获取电梯状态颜色(基于后端字段)
|
||||||
const getElevatorStatusColor = (status: ElevatorStatus, isConnected: boolean): string => {
|
const getElevatorStatusColor = (point: any): string => {
|
||||||
const display = elevatorStore.getElevatorDisplay(status, isConnected);
|
const { isConnected, elevatorDirection, elevatorFrontDoorStatus } = point;
|
||||||
return display.color;
|
|
||||||
|
// 离线显示灰色
|
||||||
|
if (!isConnected) return '#999999';
|
||||||
|
|
||||||
|
// 优先判断门状态
|
||||||
|
if (elevatorFrontDoorStatus === 2) return '#52C41A'; // 开门中 - 绿色
|
||||||
|
if (elevatorFrontDoorStatus === 3) return '#52C41A'; // 门已开 - 绿色
|
||||||
|
|
||||||
|
// 门已关,根据方向判断
|
||||||
|
if (elevatorFrontDoorStatus === 1) {
|
||||||
|
if (elevatorDirection === 2) return '#722ED1'; // 向上 - 紫色
|
||||||
|
if (elevatorDirection === 3) return '#13C2C2'; // 向下 - 青色
|
||||||
|
if (elevatorDirection === 1) return '#1890FF'; // 停止 - 蓝色
|
||||||
|
}
|
||||||
|
|
||||||
|
return '#1890FF'; // 默认 - 蓝色
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -218,14 +238,14 @@ const getElevatorStatusColor = (status: ElevatorStatus, isConnected: boolean): s
|
|||||||
<a-typography-text type="secondary">{{ $t('当前楼层') }}</a-typography-text>
|
<a-typography-text type="secondary">{{ $t('当前楼层') }}</a-typography-text>
|
||||||
<a-typography-text>{{ point.currentFloor }}F</a-typography-text>
|
<a-typography-text>{{ point.currentFloor }}F</a-typography-text>
|
||||||
</a-list-item>
|
</a-list-item>
|
||||||
<a-list-item v-if="point.type === MapPointType.电梯点 && point.elevatorStatus !== undefined">
|
<a-list-item v-if="point.type === MapPointType.电梯点 && point.elevatorDirection !== undefined">
|
||||||
<a-typography-text type="secondary">{{ $t('电梯状态') }}</a-typography-text>
|
<a-typography-text type="secondary">{{ $t('电梯状态') }}</a-typography-text>
|
||||||
<a-flex align="center" :gap="8">
|
<a-flex align="center" :gap="8">
|
||||||
<span
|
<span
|
||||||
class="status-dot"
|
class="status-dot"
|
||||||
:style="{ backgroundColor: getElevatorStatusColor(point.elevatorStatus, point.isConnected) }"
|
:style="{ backgroundColor: getElevatorStatusColor(point) }"
|
||||||
/>
|
/>
|
||||||
<a-typography-text>{{ getElevatorStatusText(point.elevatorStatus) }}</a-typography-text>
|
<a-typography-text>{{ getElevatorStatusText(point) }}</a-typography-text>
|
||||||
</a-flex>
|
</a-flex>
|
||||||
</a-list-item>
|
</a-list-item>
|
||||||
<a-list-item v-if="point.extensionType">
|
<a-list-item v-if="point.extensionType">
|
||||||
|
|||||||
@ -27,8 +27,7 @@ import { EditorService } from '../services/editor.service';
|
|||||||
import { StorageLocationService } from '../services/storage-location.service';
|
import { StorageLocationService } from '../services/storage-location.service';
|
||||||
import { useViewState } from '../services/useViewState';
|
import { useViewState } from '../services/useViewState';
|
||||||
import { editorStore } from '../stores/editor.store';
|
import { editorStore } from '../stores/editor.store';
|
||||||
import { ElevatorStatus,useElevatorStore } from '../stores/elevator.store';
|
import { useElevatorStore } from '../stores/elevator.store';
|
||||||
import { createElevatorMockData } from '../utils/elevator-mock';
|
|
||||||
|
|
||||||
const EDITOR_KEY = Symbol('editor-key');
|
const EDITOR_KEY = Symbol('editor-key');
|
||||||
|
|
||||||
@ -523,15 +522,16 @@ onMounted(async () => {
|
|||||||
// 开发环境启用电梯模拟数据
|
// 开发环境启用电梯模拟数据
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
console.log('[开发环境] 启动电梯状态模拟数据');
|
console.log('[开发环境] 启动电梯状态模拟数据');
|
||||||
stopElevatorMock = createElevatorMockData();
|
|
||||||
|
|
||||||
// 推送指定的电梯数据
|
// 推送指定的电梯数据
|
||||||
console.log('[开发环境] 推送指定电梯数据: 1998661793706377218');
|
console.log('[开发环境] 推送指定电梯数据: 1998661793706377218');
|
||||||
|
// 使用后端字段格式
|
||||||
elevatorStore.handleElevatorWebSocketData({
|
elevatorStore.handleElevatorWebSocketData({
|
||||||
id: '1998661793706377218',
|
id: '1998661793706377218',
|
||||||
type: 102,
|
type: 102,
|
||||||
status: ElevatorStatus.OPENING,
|
elevatorFloor: 5,
|
||||||
floor: 5,
|
elevatorDirection: 1, // 停止
|
||||||
|
elevatorFrontDoorStatus: 2, // 正在开关
|
||||||
isConnected: true
|
isConnected: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { get } from 'lodash-es';
|
|||||||
|
|
||||||
import doorClosedUrl from '../../assets/icons/png/doorClosed.png';
|
import doorClosedUrl from '../../assets/icons/png/doorClosed.png';
|
||||||
import doorOpenUrl from '../../assets/icons/png/doorOpening.png';
|
import doorOpenUrl from '../../assets/icons/png/doorOpening.png';
|
||||||
|
// 直接使用后端字段,不需要导入枚举
|
||||||
import colorConfig from '../color/color-config.service';
|
import colorConfig from '../color/color-config.service';
|
||||||
const __doorImgOpen = new Image();
|
const __doorImgOpen = new Image();
|
||||||
__doorImgOpen.src = (doorOpenUrl as unknown as string) || '';
|
__doorImgOpen.src = (doorOpenUrl as unknown as string) || '';
|
||||||
@ -52,6 +53,278 @@ function drawDisconnectedIcon(ctx: CanvasRenderingContext2D, cx: number, cy: num
|
|||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制电梯点的动态效果 - 矩形框架动画系统
|
||||||
|
* @param ctx Canvas上下文
|
||||||
|
* @param pen 画笔对象
|
||||||
|
* @param elevatorData 电梯数据(包含后端字段)
|
||||||
|
* @param time 当前时间戳(毫秒)
|
||||||
|
*/
|
||||||
|
function drawElevatorAnimation(
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
pen: MapPen,
|
||||||
|
elevatorData: any,
|
||||||
|
time: number
|
||||||
|
): void {
|
||||||
|
// 提取后端字段
|
||||||
|
const {
|
||||||
|
elevatorDirection,
|
||||||
|
elevatorFrontDoorStatus,
|
||||||
|
isConnected,
|
||||||
|
|
||||||
|
} = elevatorData;
|
||||||
|
|
||||||
|
const { x = 0, y = 0, width: w = 0, height: h = 0 } = pen.calculative?.worldRect ?? {};
|
||||||
|
|
||||||
|
// 为48*60图片设计矩形动画框架
|
||||||
|
// 外边框距离图片边缘6像素,确保不覆盖图片
|
||||||
|
const framePadding = 6;
|
||||||
|
const frameWidth = 3; // 边框宽度
|
||||||
|
const cornerLength = 12; // 角装饰长度
|
||||||
|
const cornerWidth = 4; // 角装饰宽度
|
||||||
|
|
||||||
|
// 动画框架的四个角坐标
|
||||||
|
const frameCorners = {
|
||||||
|
topLeft: { x: x - framePadding, y: y - framePadding },
|
||||||
|
topRight: { x: x + w + framePadding, y: y - framePadding },
|
||||||
|
bottomLeft: { x: x - framePadding, y: y + h + framePadding },
|
||||||
|
bottomRight: { x: x + w + framePadding, y: y + h + framePadding }
|
||||||
|
};
|
||||||
|
|
||||||
|
// 检查是否启用LOD
|
||||||
|
const lodEnabled = (pen.calculative as any)?.lodEnabled || false;
|
||||||
|
|
||||||
|
// 辅助函数:绘制矩形框架的角装饰
|
||||||
|
const drawFrameCorners = (color: string, alpha: number, animated: boolean = true) => {
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.lineWidth = cornerWidth;
|
||||||
|
ctx.lineCap = 'square';
|
||||||
|
ctx.globalAlpha = alpha;
|
||||||
|
|
||||||
|
// 动画效果:角装饰闪烁
|
||||||
|
const animPhase = animated ? Math.sin(time * 0.002) * 0.5 + 0.5 : 1;
|
||||||
|
|
||||||
|
// 左上角
|
||||||
|
ctx.globalAlpha = alpha * animPhase;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(frameCorners.topLeft.x, frameCorners.topLeft.y + cornerLength);
|
||||||
|
ctx.lineTo(frameCorners.topLeft.x, frameCorners.topLeft.y);
|
||||||
|
ctx.lineTo(frameCorners.topLeft.x + cornerLength, frameCorners.topLeft.y);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// 右上角
|
||||||
|
ctx.globalAlpha = alpha * (animPhase + 0.25) % 1;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(frameCorners.topRight.x - cornerLength, frameCorners.topRight.y);
|
||||||
|
ctx.lineTo(frameCorners.topRight.x, frameCorners.topRight.y);
|
||||||
|
ctx.lineTo(frameCorners.topRight.x, frameCorners.topRight.y + cornerLength);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// 右下角
|
||||||
|
ctx.globalAlpha = alpha * (animPhase + 0.5) % 1;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(frameCorners.bottomRight.x, frameCorners.bottomRight.y - cornerLength);
|
||||||
|
ctx.lineTo(frameCorners.bottomRight.x, frameCorners.bottomRight.y);
|
||||||
|
ctx.lineTo(frameCorners.bottomRight.x - cornerLength, frameCorners.bottomRight.y);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// 左下角
|
||||||
|
ctx.globalAlpha = alpha * (animPhase + 0.75) % 1;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(frameCorners.bottomLeft.x + cornerLength, frameCorners.bottomLeft.y);
|
||||||
|
ctx.lineTo(frameCorners.bottomLeft.x, frameCorners.bottomLeft.y);
|
||||||
|
ctx.lineTo(frameCorners.bottomLeft.x, frameCorners.bottomLeft.y - cornerLength);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 辅助函数:绘制完整矩形边框
|
||||||
|
const drawFullFrame = (color: string, alpha: number, dashPattern: number[] = []) => {
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.lineWidth = frameWidth;
|
||||||
|
ctx.globalAlpha = alpha;
|
||||||
|
|
||||||
|
if (dashPattern.length > 0) {
|
||||||
|
ctx.setLineDash(dashPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.strokeRect(
|
||||||
|
frameCorners.topLeft.x,
|
||||||
|
frameCorners.topLeft.y,
|
||||||
|
frameCorners.bottomRight.x - frameCorners.topLeft.x,
|
||||||
|
frameCorners.bottomRight.y - frameCorners.topLeft.y
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 辅助函数:绘制方向箭头(在框架外部)
|
||||||
|
const drawDirectionArrows = (direction: 'up' | 'down', color: string) => {
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.lineCap = 'round';
|
||||||
|
ctx.lineJoin = 'round';
|
||||||
|
|
||||||
|
const arrowSize = 8;
|
||||||
|
const arrowOffset = 15; // 箭头距离框架的距离
|
||||||
|
const animProgress = (Math.sin(time * 0.003) + 1) * 0.5;
|
||||||
|
|
||||||
|
if (direction === 'up') {
|
||||||
|
// 上行箭头:在框架上方
|
||||||
|
const arrowY = frameCorners.topLeft.y - arrowOffset - animProgress * 5;
|
||||||
|
const centerX = x + w / 2;
|
||||||
|
|
||||||
|
ctx.globalAlpha = 0.8 + animProgress * 0.2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(centerX, arrowY);
|
||||||
|
ctx.lineTo(centerX - arrowSize, arrowY + arrowSize);
|
||||||
|
ctx.lineTo(centerX + arrowSize, arrowY + arrowSize);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
} else {
|
||||||
|
// 下行箭头:在框架下方
|
||||||
|
const arrowY = frameCorners.bottomLeft.y + arrowOffset + animProgress * 5;
|
||||||
|
const centerX = x + w / 2;
|
||||||
|
|
||||||
|
ctx.globalAlpha = 0.8 + animProgress * 0.2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(centerX, arrowY);
|
||||||
|
ctx.lineTo(centerX - arrowSize, arrowY - arrowSize);
|
||||||
|
ctx.lineTo(centerX + arrowSize, arrowY - arrowSize);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 辅助函数:绘制门状态指示(在框架两侧)
|
||||||
|
const drawDoorIndicators = (isOpening: boolean, color: string) => {
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.globalAlpha = 0.7;
|
||||||
|
|
||||||
|
const progress = isOpening ?
|
||||||
|
(Math.sin(time * 0.001) + 1) * 0.5 :
|
||||||
|
1 - (Math.sin(time * 0.001) + 1) * 0.5;
|
||||||
|
|
||||||
|
const doorHeight = h * 0.3;
|
||||||
|
const doorWidth = 3;
|
||||||
|
const sideOffset = 12; // 距离框架侧边的距离
|
||||||
|
|
||||||
|
// 左门指示
|
||||||
|
const leftDoorX = frameCorners.topLeft.x - sideOffset - progress * 8;
|
||||||
|
const doorY = y + (h - doorHeight) / 2;
|
||||||
|
ctx.strokeRect(leftDoorX, doorY, doorWidth, doorHeight);
|
||||||
|
|
||||||
|
// 右门指示
|
||||||
|
const rightDoorX = frameCorners.topRight.x + sideOffset + progress * 8;
|
||||||
|
ctx.strokeRect(rightDoorX, doorY, doorWidth, doorHeight);
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 辅助函数:绘制状态文本
|
||||||
|
const drawStatusText = (text: string, color: string) => {
|
||||||
|
if (lodEnabled) return; // LOD模式下不显示文本
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.font = '10px Arial';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'top';
|
||||||
|
ctx.globalAlpha = 0.9;
|
||||||
|
|
||||||
|
// 在图片下方显示状态文本
|
||||||
|
ctx.fillText(text, x + w / 2, frameCorners.bottomLeft.y + 5);
|
||||||
|
ctx.restore();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 辅助函数:绘制连接状态指示器
|
||||||
|
const drawConnectionIndicator = () => {
|
||||||
|
if (!isConnected) {
|
||||||
|
ctx.save();
|
||||||
|
const pulseIntensity = 0.5 + Math.sin(time * 0.003) * 0.5;
|
||||||
|
|
||||||
|
// 断连图标在右上角
|
||||||
|
ctx.fillStyle = `rgba(255, 59, 48, ${pulseIntensity})`;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(frameCorners.topRight.x, frameCorners.topRight.y, 4, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.strokeStyle = `rgba(255, 59, 48, ${pulseIntensity})`;
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(frameCorners.topRight.x - 6, frameCorners.topRight.y - 6);
|
||||||
|
ctx.lineTo(frameCorners.topRight.x + 6, frameCorners.topRight.y + 6);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 状态渲染逻辑
|
||||||
|
if (!isConnected) {
|
||||||
|
// 离线状态:红色闪烁角 + 断连图标
|
||||||
|
drawFrameCorners('rgba(255, 59, 48, 0.8)', 0.8, true);
|
||||||
|
drawConnectionIndicator();
|
||||||
|
drawStatusText('离线', '#FF3B30');
|
||||||
|
} else {
|
||||||
|
// 在线状态根据具体状态渲染
|
||||||
|
switch (elevatorFrontDoorStatus) {
|
||||||
|
case 2: // 正在开关门
|
||||||
|
// 绿色动态框架 + 门指示器
|
||||||
|
drawFrameCorners('rgba(52, 199, 89, 0.8)', 0.7, true);
|
||||||
|
if (!lodEnabled) {
|
||||||
|
drawDoorIndicators(true, 'rgba(52, 199, 89, 0.8)');
|
||||||
|
}
|
||||||
|
drawStatusText('开门中', '#34C759');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: // 门已开
|
||||||
|
// 蓝色呼吸框架
|
||||||
|
drawStatusText('门已开', '#007AFF');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: // 门已关
|
||||||
|
if (elevatorDirection === 2) { // 向上
|
||||||
|
// 紫色动态框架 + 上行箭头
|
||||||
|
drawFrameCorners('rgba(175, 82, 222, 0.8)', 0.7, true);
|
||||||
|
if (!lodEnabled) {
|
||||||
|
drawDirectionArrows('up', 'rgba(175, 82, 222, 0.8)');
|
||||||
|
}
|
||||||
|
drawStatusText('上行中', '#AF52DE');
|
||||||
|
} else if (elevatorDirection === 3) { // 向下
|
||||||
|
// 青色动态框架 + 下行箭头
|
||||||
|
drawFrameCorners('rgba(90, 200, 250, 0.8)', 0.7, true);
|
||||||
|
if (!lodEnabled) {
|
||||||
|
drawDirectionArrows('down', 'rgba(90, 200, 250, 0.8)');
|
||||||
|
}
|
||||||
|
drawStatusText('下行中', '#5AC8FA');
|
||||||
|
} else { // 停止
|
||||||
|
// 金色静态框架
|
||||||
|
drawFullFrame('rgba(255, 204, 0, 0.6)', 0.6);
|
||||||
|
drawStatusText('停止', '#FFCC00');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // 未知状态
|
||||||
|
// 灰色静态框架
|
||||||
|
drawFullFrame('rgba(142, 142, 147, 0.5)', 0.5);
|
||||||
|
drawStatusText('未知', '#8E8E93');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绘制自动门点断连状态的脉冲效果
|
* 绘制自动门点断连状态的脉冲效果
|
||||||
* @param ctx Canvas上下文
|
* @param ctx Canvas上下文
|
||||||
@ -285,6 +558,15 @@ export function drawPoint(ctx: CanvasRenderingContext2D, pen: MapPen): void {
|
|||||||
const largeThemeStrokeColor = get(theme, active ? 'point-l.strokeActive' : 'point-l.stroke');
|
const largeThemeStrokeColor = get(theme, active ? 'point-l.strokeActive' : 'point-l.stroke');
|
||||||
ctx.strokeStyle = statusStyle ?? (largeTypeStrokeColor || largeGeneralStrokeColor || largeThemeStrokeColor || '');
|
ctx.strokeStyle = statusStyle ?? (largeTypeStrokeColor || largeGeneralStrokeColor || largeThemeStrokeColor || '');
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
|
// 电梯点:添加动画效果
|
||||||
|
if (type === MapPointType.电梯点) {
|
||||||
|
// 直接传入包含后端字段的 pen.point 数据
|
||||||
|
if (pen.point) {
|
||||||
|
const currentTime = performance.now();
|
||||||
|
drawElevatorAnimation(ctx, pen, pen.point, currentTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -7,38 +7,32 @@ import type { MapPen } from '@api/map';
|
|||||||
import { MapPointType } from '@api/map';
|
import { MapPointType } from '@api/map';
|
||||||
import type { EditorService } from '@core/editor.service';
|
import type { EditorService } from '@core/editor.service';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { computed, markRaw, ref } from 'vue';
|
import { markRaw, ref } from 'vue';
|
||||||
|
|
||||||
// 电梯状态枚举
|
// 电梯楼层 (0-未知)
|
||||||
export enum ElevatorStatus {
|
// 电梯方向 (0-未知,1-停止,2-向上,3-向下)
|
||||||
IDLE = 0, // 静止
|
// 电梯前门状态(0-未知,1-关,2-正在开关,3-开)
|
||||||
OPENING = 1, // 开门中
|
// 注意:这些字段定义直接来自后端,不做任何修改
|
||||||
CLOSING = 2, // 关门中
|
|
||||||
MOVING_UP = 3, // 上行
|
|
||||||
MOVING_DOWN = 4, // 下行
|
|
||||||
DOOR_OPEN = 5, // 门已开
|
|
||||||
DOOR_CLOSED = 6, // 门已关
|
|
||||||
FAULT = 7, // 故障
|
|
||||||
OFFLINE = 8, // 离线
|
|
||||||
}
|
|
||||||
|
|
||||||
// 电梯数据接口
|
// 电梯数据接口(直接使用后端字段)
|
||||||
export interface ElevatorData {
|
export interface ElevatorData {
|
||||||
deviceId: string; // 设备ID
|
deviceId: string; // 设备ID
|
||||||
status: ElevatorStatus; // 当前状态
|
elevatorFloor: number; // 电梯楼层(0-未知)
|
||||||
floor?: number; // 当前楼层
|
elevatorDirection: number; // 电梯方向(0-未知,1-停止,2-向上,3-向下)
|
||||||
isConnected: boolean; // 是否连接
|
elevatorFrontDoorStatus: number; // 电梯前门状态(0-未知,1-关,2-正在开关,3-开)
|
||||||
lastUpdate: number; // 最后更新时间戳
|
isConnected: boolean; // 是否连接
|
||||||
penId?: string; // 对应的画布点位ID
|
lastUpdate: number; // 最后更新时间戳
|
||||||
|
penId?: string; // 对应的画布点位ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebSocket推送的电梯数据接口
|
// WebSocket推送的电梯数据接口(对应后端格式)
|
||||||
export interface ElevatorWebSocketData {
|
export interface ElevatorWebSocketData {
|
||||||
id: string; // 设备ID
|
id: string; // 设备ID
|
||||||
type: 102; // 电梯类型标识
|
type: 102; // 电梯类型标识
|
||||||
status: ElevatorStatus; // 电梯状态
|
elevatorFloor: number; // 电梯楼层(0-未知)
|
||||||
floor?: number; // 当前楼层
|
elevatorDirection: number; // 电梯方向(0-未知,1-停止,2-向上,3-向下)
|
||||||
isConnected: boolean; // 连接状态
|
elevatorFrontDoorStatus: number; // 电梯前门状态(0-未知,1-关,2-正在开关,3-开)
|
||||||
|
isConnected: boolean; // 连接状态
|
||||||
}
|
}
|
||||||
|
|
||||||
// 电梯点映射信息
|
// 电梯点映射信息
|
||||||
@ -54,49 +48,9 @@ export const useElevatorStore = defineStore('elevator', () => {
|
|||||||
const elevatorPoints = ref<Map<string, ElevatorPoint>>(new Map());
|
const elevatorPoints = ref<Map<string, ElevatorPoint>>(new Map());
|
||||||
const editorService = ref<EditorService | null>(null);
|
const editorService = ref<EditorService | null>(null);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ========== 计算属性 ==========
|
|
||||||
|
|
||||||
// 获取所有电梯数据
|
|
||||||
const allElevators = computed(() => Array.from(elevators.value.values()));
|
|
||||||
|
|
||||||
// 获取在线电梯数量
|
|
||||||
const onlineElevatorsCount = computed(() =>
|
|
||||||
allElevators.value.filter(e => e.isConnected).length
|
|
||||||
);
|
|
||||||
|
|
||||||
// 获取离线电梯数量
|
|
||||||
const offlineElevatorsCount = computed(() =>
|
|
||||||
allElevators.value.filter(e => !e.isConnected).length
|
|
||||||
);
|
|
||||||
|
|
||||||
// 获取故障电梯数量
|
|
||||||
const faultElevatorsCount = computed(() =>
|
|
||||||
allElevators.value.filter(e => e.status === ElevatorStatus.FAULT).length
|
|
||||||
);
|
|
||||||
|
|
||||||
// 按状态分组的电梯
|
|
||||||
const elevatorsByStatus = computed(() => {
|
|
||||||
const groups: Record<ElevatorStatus, ElevatorData[]> = {} as any;
|
|
||||||
|
|
||||||
// 初始化所有状态
|
|
||||||
Object.values(ElevatorStatus).forEach(status => {
|
|
||||||
if (typeof status === 'number') {
|
|
||||||
groups[status] = [];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 分组
|
|
||||||
allElevators.value.forEach(elevator => {
|
|
||||||
groups[elevator.status].push(elevator);
|
|
||||||
});
|
|
||||||
|
|
||||||
return groups;
|
|
||||||
});
|
|
||||||
|
|
||||||
// ========== 方法 ==========
|
// ========== 方法 ==========
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置编辑器服务
|
* 设置编辑器服务
|
||||||
* 注意:此方法只保存编辑器引用,不会立即构建映射
|
* 注意:此方法只保存编辑器引用,不会立即构建映射
|
||||||
@ -151,7 +105,13 @@ export const useElevatorStore = defineStore('elevator', () => {
|
|||||||
* 处理WebSocket推送的电梯数据
|
* 处理WebSocket推送的电梯数据
|
||||||
*/
|
*/
|
||||||
const handleElevatorWebSocketData = (data: ElevatorWebSocketData) => {
|
const handleElevatorWebSocketData = (data: ElevatorWebSocketData) => {
|
||||||
const { id: deviceId, status, floor, isConnected } = data;
|
const {
|
||||||
|
id: deviceId,
|
||||||
|
elevatorFloor,
|
||||||
|
elevatorDirection,
|
||||||
|
elevatorFrontDoorStatus,
|
||||||
|
isConnected
|
||||||
|
} = data;
|
||||||
|
|
||||||
// 获取或创建电梯数据
|
// 获取或创建电梯数据
|
||||||
let elevatorData = elevators.value.get(deviceId);
|
let elevatorData = elevators.value.get(deviceId);
|
||||||
@ -160,16 +120,19 @@ export const useElevatorStore = defineStore('elevator', () => {
|
|||||||
if (!elevatorData) {
|
if (!elevatorData) {
|
||||||
elevatorData = {
|
elevatorData = {
|
||||||
deviceId,
|
deviceId,
|
||||||
status: ElevatorStatus.IDLE,
|
elevatorFloor: 0,
|
||||||
|
elevatorDirection: 0, // 未知
|
||||||
|
elevatorFrontDoorStatus: 0, // 未知
|
||||||
isConnected: false,
|
isConnected: false,
|
||||||
lastUpdate: Date.now(),
|
lastUpdate: Date.now(),
|
||||||
penId: elevatorPoint?.penId
|
penId: elevatorPoint?.penId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新数据
|
// 更新数据(直接使用后端字段)
|
||||||
elevatorData.status = isConnected ? status : ElevatorStatus.OFFLINE;
|
elevatorData.elevatorFloor = elevatorFloor;
|
||||||
elevatorData.floor = floor;
|
elevatorData.elevatorDirection = elevatorDirection;
|
||||||
|
elevatorData.elevatorFrontDoorStatus = elevatorFrontDoorStatus;
|
||||||
elevatorData.isConnected = isConnected;
|
elevatorData.isConnected = isConnected;
|
||||||
elevatorData.lastUpdate = Date.now();
|
elevatorData.lastUpdate = Date.now();
|
||||||
|
|
||||||
@ -180,6 +143,7 @@ export const useElevatorStore = defineStore('elevator', () => {
|
|||||||
|
|
||||||
// 存储到Map
|
// 存储到Map
|
||||||
elevators.value.set(deviceId, elevatorData);
|
elevators.value.set(deviceId, elevatorData);
|
||||||
|
|
||||||
// 更新画布上的电梯点显示
|
// 更新画布上的电梯点显示
|
||||||
if (elevatorData.penId && editorService.value) {
|
if (elevatorData.penId && editorService.value) {
|
||||||
updateElevatorPointDisplay(elevatorData);
|
updateElevatorPointDisplay(elevatorData);
|
||||||
@ -187,124 +151,152 @@ export const useElevatorStore = defineStore('elevator', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新画布上电梯点的显示
|
* 更新画布上电梯点的显示 - 增强版
|
||||||
*/
|
*/
|
||||||
const updateElevatorPointDisplay = (elevatorData: ElevatorData) => {
|
const updateElevatorPointDisplay = (elevatorData: ElevatorData) => {
|
||||||
if (!editorService.value || !elevatorData.penId) return;
|
if (!editorService.value || !elevatorData.penId) return;
|
||||||
|
|
||||||
const penId = elevatorData.penId;
|
const penId = elevatorData.penId;
|
||||||
|
|
||||||
// 获取状态对应的颜色和图标
|
// 获取状态对应的颜色、图标和动画配置
|
||||||
const { color, iconPath } = getElevatorDisplay(elevatorData.status, elevatorData.isConnected);
|
const displayConfig = getElevatorDisplay(elevatorData);
|
||||||
|
|
||||||
// 基础更新数据
|
// 基础更新数据
|
||||||
const updateData: any = {
|
const updateData: any = {
|
||||||
// 设置图标
|
// 设置图标
|
||||||
image: iconPath,
|
image: displayConfig.iconPath,
|
||||||
// 更新点位信息
|
// 更新点位信息
|
||||||
point: {
|
point: {
|
||||||
...((editorService.value.getPenById(penId) as any)?.point || {}),
|
...((editorService.value.getPenById(penId) as any)?.point || {}),
|
||||||
elevatorStatus: elevatorData.status,
|
|
||||||
isConnected: elevatorData.isConnected,
|
isConnected: elevatorData.isConnected,
|
||||||
currentFloor: elevatorData.floor,
|
currentFloor: elevatorData.elevatorFloor,
|
||||||
lastUpdate: elevatorData.lastUpdate,
|
lastUpdate: elevatorData.lastUpdate,
|
||||||
// 使用颜色区分状态
|
// 保存原始后端数据
|
||||||
color
|
elevatorDirection: elevatorData.elevatorDirection,
|
||||||
}
|
elevatorFrontDoorStatus: elevatorData.elevatorFrontDoorStatus,
|
||||||
|
// 使用新的颜色系统
|
||||||
|
color: displayConfig.color,
|
||||||
|
// 动画配置
|
||||||
|
animationClass: displayConfig.animationClass,
|
||||||
|
priority: displayConfig.priority
|
||||||
|
},
|
||||||
|
// 增强的样式配置
|
||||||
|
statusStyle: displayConfig.color
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新电梯点
|
try {
|
||||||
editorService.value.updatePen(penId, updateData, false);
|
// 更新电梯点
|
||||||
|
editorService.value.updatePen(penId, updateData, false);
|
||||||
|
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`🛗 更新电梯点显示失败 (${elevatorData.deviceId}):`, error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取电梯显示配置(颜色、图标)
|
* 获取电梯显示配置(颜色、图标)
|
||||||
|
* 基于后端字段直接判断,使用现代化设计系统
|
||||||
*/
|
*/
|
||||||
const getElevatorDisplay = (status: ElevatorStatus, isConnected: boolean) => {
|
const getElevatorDisplay = (elevatorData: ElevatorData) => {
|
||||||
// 生成图标路径的辅助函数
|
// 生成图标路径的辅助函数
|
||||||
const getIconPath = (theme: string) => `${import.meta.env.BASE_URL}/point/elevator-${theme}.svg`;
|
const getIconPath = (theme: string) => `${import.meta.env.BASE_URL}/point/elevator-${theme}.svg`;
|
||||||
|
|
||||||
// 如果离线,显示灰色
|
// 现代化颜色配置系统
|
||||||
if (!isConnected) {
|
const colors = {
|
||||||
|
offline: '#FF3B30', // 红色 - 离线
|
||||||
|
opening: '#34C759', // 绿色 - 开门中
|
||||||
|
doorOpen: '#007AFF', // 蓝色 - 门已开
|
||||||
|
moving: '#AF52DE', // 紫色 - 运行中
|
||||||
|
upMoving: '#AF52DE', // 紫色 - 上行
|
||||||
|
downMoving: '#5AC8FA', // 青色 - 下行
|
||||||
|
stopped: '#FFCC00', // 金色 - 停止
|
||||||
|
idle: '#8E8E93', // 灰色 - 静止
|
||||||
|
unknown: '#8E8E93' // 灰色 - 未知
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果离线,显示红色
|
||||||
|
if (!elevatorData.isConnected) {
|
||||||
return {
|
return {
|
||||||
color: '#999999',
|
color: colors.offline,
|
||||||
iconPath: getIconPath('offline'),
|
iconPath: getIconPath('offline'),
|
||||||
text: '离线'
|
text: '离线',
|
||||||
|
priority: 'high',
|
||||||
|
animationClass: 'pulse-error'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusMap = {
|
// 根据后端字段判断显示状态
|
||||||
[ElevatorStatus.IDLE]: {
|
const { elevatorDirection, elevatorFrontDoorStatus, elevatorFloor } = elevatorData;
|
||||||
color: '#1890FF',
|
|
||||||
iconPath: getIconPath('idle'),
|
// 优先判断门状态
|
||||||
text: '静止'
|
switch (elevatorFrontDoorStatus) {
|
||||||
},
|
case 2: // 正在开关门
|
||||||
[ElevatorStatus.OPENING]: {
|
return {
|
||||||
color: '#52C41A',
|
color: colors.opening,
|
||||||
iconPath: getIconPath('opening'),
|
iconPath: getIconPath('opening'),
|
||||||
text: '开门中'
|
text: `开门中 ${elevatorFloor > 0 ? `(${elevatorFloor}F)` : ''}`,
|
||||||
},
|
priority: 'high',
|
||||||
[ElevatorStatus.CLOSING]: {
|
animationClass: 'pulse-success'
|
||||||
color: '#FA8C16',
|
};
|
||||||
iconPath: getIconPath('closing'),
|
|
||||||
text: '关门中'
|
case 3: // 门已开
|
||||||
},
|
return {
|
||||||
[ElevatorStatus.MOVING_UP]: {
|
color: colors.doorOpen,
|
||||||
color: '#722ED1',
|
iconPath: getIconPath('door-open'),
|
||||||
iconPath: getIconPath('up'),
|
text: `门已开 ${elevatorFloor > 0 ? `(${elevatorFloor}F)` : ''}`,
|
||||||
text: '上行中'
|
priority: 'normal',
|
||||||
},
|
animationClass: 'breathe'
|
||||||
[ElevatorStatus.MOVING_DOWN]: {
|
};
|
||||||
color: '#13C2C2',
|
|
||||||
iconPath: getIconPath('down'),
|
case 1: // 门已关
|
||||||
text: '下行中'
|
switch (elevatorDirection) {
|
||||||
},
|
case 2: // 向上
|
||||||
[ElevatorStatus.DOOR_OPEN]: {
|
return {
|
||||||
color: '#52C41A',
|
color: colors.upMoving,
|
||||||
iconPath: getIconPath('door-open'),
|
iconPath: getIconPath('up'),
|
||||||
text: '门已开'
|
text: `上行中 ${elevatorFloor > 0 ? `(${elevatorFloor}F)` : ''}`,
|
||||||
},
|
priority: 'high',
|
||||||
[ElevatorStatus.DOOR_CLOSED]: {
|
animationClass: 'flow-up'
|
||||||
color: '#1890FF',
|
};
|
||||||
iconPath: getIconPath('door-closed'),
|
|
||||||
text: '门已关'
|
case 3: // 向下
|
||||||
},
|
return {
|
||||||
[ElevatorStatus.FAULT]: {
|
color: colors.downMoving,
|
||||||
color: '#FF4D4F',
|
iconPath: getIconPath('down'),
|
||||||
iconPath: getIconPath('fault'),
|
text: `下行中 ${elevatorFloor > 0 ? `(${elevatorFloor}F)` : ''}`,
|
||||||
text: '故障'
|
priority: 'high',
|
||||||
},
|
animationClass: 'flow-down'
|
||||||
|
};
|
||||||
|
|
||||||
|
case 1: // 停止
|
||||||
|
return {
|
||||||
|
color: colors.stopped,
|
||||||
|
iconPath: getIconPath('stopped'),
|
||||||
|
text: `停止 ${elevatorFloor > 0 ? `(${elevatorFloor}F)` : ''}`,
|
||||||
|
priority: 'normal',
|
||||||
|
animationClass: 'gentle-pulse'
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未知状态
|
||||||
|
return {
|
||||||
|
color: colors.unknown,
|
||||||
|
iconPath: getIconPath('unknown'),
|
||||||
|
text: '未知状态',
|
||||||
|
priority: 'low',
|
||||||
|
animationClass: 'dim'
|
||||||
};
|
};
|
||||||
|
|
||||||
return statusMap[status] || statusMap[ElevatorStatus.IDLE];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取单个电梯数据
|
|
||||||
*/
|
|
||||||
const getElevatorById = (deviceId: string): ElevatorData | undefined => {
|
|
||||||
return elevators.value.get(deviceId);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 手动更新电梯状态
|
|
||||||
*/
|
|
||||||
const updateElevatorStatus = (
|
|
||||||
deviceId: string,
|
|
||||||
status: ElevatorStatus,
|
|
||||||
isConnected: boolean = true
|
|
||||||
) => {
|
|
||||||
const mockData: ElevatorWebSocketData = {
|
|
||||||
id: deviceId,
|
|
||||||
type: 102,
|
|
||||||
status,
|
|
||||||
isConnected
|
|
||||||
};
|
|
||||||
|
|
||||||
handleElevatorWebSocketData(mockData);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 刷新电梯映射(场景变化时调用)
|
* 刷新电梯映射(场景变化时调用)
|
||||||
@ -320,29 +312,8 @@ export const useElevatorStore = defineStore('elevator', () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除所有电梯数据
|
|
||||||
*/
|
|
||||||
const clearAllData = () => {
|
|
||||||
elevators.value.clear();
|
|
||||||
console.log('🛗 电梯数据已清除');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取电梯统计信息
|
|
||||||
*/
|
|
||||||
const getStatistics = () => {
|
|
||||||
return {
|
|
||||||
total: allElevators.value.length,
|
|
||||||
online: onlineElevatorsCount.value,
|
|
||||||
offline: offlineElevatorsCount.value,
|
|
||||||
fault: faultElevatorsCount.value,
|
|
||||||
byStatus: Object.entries(elevatorsByStatus.value).reduce((acc, [status, list]) => {
|
|
||||||
acc[Number(status)] = list.length;
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<number, number>)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -351,22 +322,10 @@ export const useElevatorStore = defineStore('elevator', () => {
|
|||||||
elevators,
|
elevators,
|
||||||
elevatorPoints,
|
elevatorPoints,
|
||||||
editorService,
|
editorService,
|
||||||
|
|
||||||
// 计算属性
|
|
||||||
allElevators,
|
|
||||||
onlineElevatorsCount,
|
|
||||||
offlineElevatorsCount,
|
|
||||||
faultElevatorsCount,
|
|
||||||
elevatorsByStatus,
|
|
||||||
|
|
||||||
// 方法
|
// 方法
|
||||||
setEditorService,
|
setEditorService,
|
||||||
handleElevatorWebSocketData,
|
handleElevatorWebSocketData,
|
||||||
getElevatorById,
|
|
||||||
updateElevatorStatus,
|
|
||||||
refreshMapping,
|
refreshMapping,
|
||||||
clearAllData,
|
|
||||||
getStatistics,
|
|
||||||
getElevatorDisplay
|
getElevatorDisplay
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -1,129 +0,0 @@
|
|||||||
/**
|
|
||||||
* 电梯状态模拟工具
|
|
||||||
* 用于开发和测试电梯状态功能
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ElevatorStatus,useElevatorStore } from '../stores/elevator.store';
|
|
||||||
|
|
||||||
export const createElevatorMockData = () => {
|
|
||||||
const elevatorStore = useElevatorStore();
|
|
||||||
|
|
||||||
// 模拟的设备ID列表(与API返回的设备ID格式保持一致)
|
|
||||||
const mockDeviceIds = [
|
|
||||||
'2',
|
|
||||||
'4',
|
|
||||||
'6',
|
|
||||||
'8'
|
|
||||||
];
|
|
||||||
|
|
||||||
// 缓存上次的状态,避免无意义的更新
|
|
||||||
const lastStates = new Map<string, { status: ElevatorStatus; floor: number; isConnected: boolean }>();
|
|
||||||
|
|
||||||
// 生成模拟数据
|
|
||||||
const generateMockData = () => {
|
|
||||||
mockDeviceIds.forEach((deviceId, index) => {
|
|
||||||
// 为每个电梯生成不同的状态
|
|
||||||
let status: ElevatorStatus;
|
|
||||||
let floor: number;
|
|
||||||
|
|
||||||
// 70%概率保持当前状态,30%概率改变状态
|
|
||||||
if (Math.random() > 0.7) {
|
|
||||||
switch (index % 4) {
|
|
||||||
case 0:
|
|
||||||
status = ElevatorStatus.MOVING_UP;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
status = ElevatorStatus.MOVING_DOWN;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
status = ElevatorStatus.OPENING;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status = ElevatorStatus.IDLE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 保持当前状态
|
|
||||||
const last = lastStates.get(deviceId);
|
|
||||||
status = last ? last.status : (index % 4 === 0 ? ElevatorStatus.MOVING_UP : index % 4 === 1 ? ElevatorStatus.MOVING_DOWN : index % 4 === 2 ? ElevatorStatus.OPENING : ElevatorStatus.IDLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 95%概率在线,5%概率离线
|
|
||||||
const isConnected = Math.random() > 0.05;
|
|
||||||
|
|
||||||
// 只有在线时才更新楼层
|
|
||||||
if (isConnected && Math.random() > 0.5) {
|
|
||||||
// 30%概率改变楼层
|
|
||||||
const last = lastStates.get(deviceId);
|
|
||||||
if (Math.random() > 0.7) {
|
|
||||||
floor = last ? Math.min(10, Math.max(1, last.floor + (Math.random() > 0.5 ? 1 : -1))) : Math.floor(Math.random() * 10) + 1;
|
|
||||||
} else {
|
|
||||||
floor = last ? last.floor : Math.floor(Math.random() * 10) + 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
floor = lastStates.get(deviceId)?.floor || Math.floor(Math.random() * 10) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查状态是否真的发生了变化
|
|
||||||
const lastState = lastStates.get(deviceId);
|
|
||||||
if (lastState &&
|
|
||||||
lastState.status === status &&
|
|
||||||
lastState.floor === floor &&
|
|
||||||
lastState.isConnected === isConnected) {
|
|
||||||
// 状态没有变化,跳过更新
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新缓存
|
|
||||||
lastStates.set(deviceId, { status, floor, isConnected });
|
|
||||||
|
|
||||||
// 偶尔模拟故障(5%概率)
|
|
||||||
if (Math.random() > 0.95 && isConnected) {
|
|
||||||
status = ElevatorStatus.FAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
elevatorStore.handleElevatorWebSocketData({
|
|
||||||
id: deviceId,
|
|
||||||
type: 102,
|
|
||||||
status: isConnected ? status : ElevatorStatus.OFFLINE,
|
|
||||||
floor,
|
|
||||||
isConnected
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 初始生成一次数据
|
|
||||||
generateMockData();
|
|
||||||
|
|
||||||
// 设置定时器,每8-12秒更新一次数据(降低频率)
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
generateMockData();
|
|
||||||
}, Math.random() * 4000 + 8000); // 8-12秒随机间隔
|
|
||||||
|
|
||||||
console.log('🛗 电梯模拟数据已启动,设备ID:', mockDeviceIds);
|
|
||||||
|
|
||||||
// 返回停止函数
|
|
||||||
return () => {
|
|
||||||
clearInterval(interval);
|
|
||||||
lastStates.clear();
|
|
||||||
console.log('🛗 电梯模拟数据已停止');
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// 创建单个电梯的测试数据
|
|
||||||
export const createSingleElevatorData = (deviceId: string, status?: ElevatorStatus) => {
|
|
||||||
const elevatorStore = useElevatorStore();
|
|
||||||
|
|
||||||
elevatorStore.handleElevatorWebSocketData({
|
|
||||||
id: deviceId,
|
|
||||||
type: 102,
|
|
||||||
status: status || ElevatorStatus.IDLE,
|
|
||||||
floor: Math.floor(Math.random() * 10) + 1,
|
|
||||||
isConnected: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 清除所有电梯数据
|
|
||||||
export const clearElevatorData = () => {
|
|
||||||
const elevatorStore = useElevatorStore();
|
|
||||||
elevatorStore.clearAllData();
|
|
||||||
};
|
|
||||||
Loading…
x
Reference in New Issue
Block a user