372 lines
9.6 KiB
TypeScript
372 lines
9.6 KiB
TypeScript
/**
|
||
* 电梯状态管理 Store
|
||
* 使用 Pinia 管理电梯的全局状态
|
||
*/
|
||
|
||
import type { MapPen } from '@api/map';
|
||
import { MapPointType } from '@api/map';
|
||
import type { EditorService } from '@core/editor.service';
|
||
import { defineStore } from 'pinia';
|
||
import { computed, markRaw, ref } from 'vue';
|
||
|
||
// 电梯状态枚举
|
||
export enum ElevatorStatus {
|
||
IDLE = 0, // 静止
|
||
OPENING = 1, // 开门中
|
||
CLOSING = 2, // 关门中
|
||
MOVING_UP = 3, // 上行
|
||
MOVING_DOWN = 4, // 下行
|
||
DOOR_OPEN = 5, // 门已开
|
||
DOOR_CLOSED = 6, // 门已关
|
||
FAULT = 7, // 故障
|
||
OFFLINE = 8, // 离线
|
||
}
|
||
|
||
// 电梯数据接口
|
||
export interface ElevatorData {
|
||
deviceId: string; // 设备ID
|
||
status: ElevatorStatus; // 当前状态
|
||
floor?: number; // 当前楼层
|
||
isConnected: boolean; // 是否连接
|
||
lastUpdate: number; // 最后更新时间戳
|
||
penId?: string; // 对应的画布点位ID
|
||
}
|
||
|
||
// WebSocket推送的电梯数据接口
|
||
export interface ElevatorWebSocketData {
|
||
id: string; // 设备ID
|
||
type: 102; // 电梯类型标识
|
||
status: ElevatorStatus; // 电梯状态
|
||
floor?: number; // 当前楼层
|
||
isConnected: boolean; // 连接状态
|
||
}
|
||
|
||
// 电梯点映射信息
|
||
export interface ElevatorPoint {
|
||
penId: string;
|
||
deviceId: string;
|
||
pointType: MapPointType.电梯点;
|
||
}
|
||
|
||
export const useElevatorStore = defineStore('elevator', () => {
|
||
// ========== 状态定义 ==========
|
||
const elevators = ref<Map<string, ElevatorData>>(new Map());
|
||
const elevatorPoints = ref<Map<string, ElevatorPoint>>(new Map());
|
||
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;
|
||
});
|
||
|
||
// ========== 方法 ==========
|
||
|
||
/**
|
||
* 设置编辑器服务
|
||
* 注意:此方法只保存编辑器引用,不会立即构建映射
|
||
* 需要在场景加载完成后手动调用 buildElevatorMapping() 或 refreshMapping()
|
||
*/
|
||
const setEditorService = (editor: EditorService) => {
|
||
editorService.value = markRaw(editor);
|
||
console.log('🛗 电梯Store: 编辑器服务已设置,等待场景加载后构建映射');
|
||
};
|
||
|
||
/**
|
||
* 构建设备ID到电梯点的映射
|
||
*/
|
||
const buildElevatorMapping = () => {
|
||
if (!editorService.value) return;
|
||
|
||
elevatorPoints.value.clear();
|
||
const pens = editorService.value.data().pens;
|
||
|
||
pens.forEach((pen: MapPen) => {
|
||
if (
|
||
pen.name === 'point' &&
|
||
pen.point?.type === MapPointType.电梯点 &&
|
||
pen.point?.deviceId
|
||
) {
|
||
const elevatorPoint: ElevatorPoint = {
|
||
penId: pen.id,
|
||
deviceId: pen.point.deviceId,
|
||
pointType: MapPointType.电梯点
|
||
};
|
||
|
||
elevatorPoints.value.set(pen.point.deviceId, elevatorPoint);
|
||
}
|
||
});
|
||
|
||
// 打印详细的映射列表
|
||
const mappingList = Array.from(elevatorPoints.value.entries()).map(([deviceId, point]) => ({
|
||
设备ID: deviceId,
|
||
点位ID: point.penId,
|
||
点位类型: '电梯点'
|
||
}));
|
||
|
||
console.log('🛗 电梯映射构建完成:', {
|
||
映射总数: elevatorPoints.value.size,
|
||
设备ID列表: Array.from(elevatorPoints.value.keys())
|
||
});
|
||
|
||
console.table(mappingList);
|
||
};
|
||
|
||
/**
|
||
* 处理WebSocket推送的电梯数据
|
||
*/
|
||
const handleElevatorWebSocketData = (data: ElevatorWebSocketData) => {
|
||
const { id: deviceId, status, floor, isConnected } = data;
|
||
|
||
// 获取或创建电梯数据
|
||
let elevatorData = elevators.value.get(deviceId);
|
||
const elevatorPoint = elevatorPoints.value.get(deviceId);
|
||
|
||
if (!elevatorData) {
|
||
elevatorData = {
|
||
deviceId,
|
||
status: ElevatorStatus.IDLE,
|
||
isConnected: false,
|
||
lastUpdate: Date.now(),
|
||
penId: elevatorPoint?.penId
|
||
};
|
||
}
|
||
|
||
// 更新数据
|
||
elevatorData.status = isConnected ? status : ElevatorStatus.OFFLINE;
|
||
elevatorData.floor = floor;
|
||
elevatorData.isConnected = isConnected;
|
||
elevatorData.lastUpdate = Date.now();
|
||
|
||
// 如果找到对应的点位,也更新penId
|
||
if (elevatorPoint && !elevatorData.penId) {
|
||
elevatorData.penId = elevatorPoint.penId;
|
||
}
|
||
|
||
// 存储到Map
|
||
elevators.value.set(deviceId, elevatorData);
|
||
// 更新画布上的电梯点显示
|
||
if (elevatorData.penId && editorService.value) {
|
||
updateElevatorPointDisplay(elevatorData);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 更新画布上电梯点的显示
|
||
*/
|
||
const updateElevatorPointDisplay = (elevatorData: ElevatorData) => {
|
||
if (!editorService.value || !elevatorData.penId) return;
|
||
|
||
const penId = elevatorData.penId;
|
||
|
||
// 获取状态对应的颜色和图标
|
||
const { color, iconPath } = getElevatorDisplay(elevatorData.status, elevatorData.isConnected);
|
||
|
||
// 基础更新数据
|
||
const updateData: any = {
|
||
// 设置图标
|
||
image: iconPath,
|
||
// 更新点位信息
|
||
point: {
|
||
...((editorService.value.getPenById(penId) as any)?.point || {}),
|
||
elevatorStatus: elevatorData.status,
|
||
isConnected: elevatorData.isConnected,
|
||
currentFloor: elevatorData.floor,
|
||
lastUpdate: elevatorData.lastUpdate,
|
||
// 使用颜色区分状态
|
||
color
|
||
}
|
||
};
|
||
|
||
// 更新电梯点
|
||
editorService.value.updatePen(penId, updateData, false);
|
||
|
||
|
||
};
|
||
|
||
/**
|
||
* 获取电梯显示配置(颜色、图标)
|
||
*/
|
||
const getElevatorDisplay = (status: ElevatorStatus, isConnected: boolean) => {
|
||
// 生成图标路径的辅助函数
|
||
const getIconPath = (theme: string) => `${import.meta.env.BASE_URL}/point/elevator-${theme}.svg`;
|
||
|
||
// 如果离线,显示灰色
|
||
if (!isConnected) {
|
||
return {
|
||
color: '#999999',
|
||
iconPath: getIconPath('offline'),
|
||
text: '离线'
|
||
};
|
||
}
|
||
|
||
const statusMap = {
|
||
[ElevatorStatus.IDLE]: {
|
||
color: '#1890FF',
|
||
iconPath: getIconPath('idle'),
|
||
text: '静止'
|
||
},
|
||
[ElevatorStatus.OPENING]: {
|
||
color: '#52C41A',
|
||
iconPath: getIconPath('opening'),
|
||
text: '开门中'
|
||
},
|
||
[ElevatorStatus.CLOSING]: {
|
||
color: '#FA8C16',
|
||
iconPath: getIconPath('closing'),
|
||
text: '关门中'
|
||
},
|
||
[ElevatorStatus.MOVING_UP]: {
|
||
color: '#722ED1',
|
||
iconPath: getIconPath('up'),
|
||
text: '上行中'
|
||
},
|
||
[ElevatorStatus.MOVING_DOWN]: {
|
||
color: '#13C2C2',
|
||
iconPath: getIconPath('down'),
|
||
text: '下行中'
|
||
},
|
||
[ElevatorStatus.DOOR_OPEN]: {
|
||
color: '#52C41A',
|
||
iconPath: getIconPath('door-open'),
|
||
text: '门已开'
|
||
},
|
||
[ElevatorStatus.DOOR_CLOSED]: {
|
||
color: '#1890FF',
|
||
iconPath: getIconPath('door-closed'),
|
||
text: '门已关'
|
||
},
|
||
[ElevatorStatus.FAULT]: {
|
||
color: '#FF4D4F',
|
||
iconPath: getIconPath('fault'),
|
||
text: '故障'
|
||
},
|
||
};
|
||
|
||
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);
|
||
};
|
||
|
||
/**
|
||
* 刷新电梯映射(场景变化时调用)
|
||
*/
|
||
const refreshMapping = () => {
|
||
buildElevatorMapping();
|
||
|
||
// 重新更新所有电梯的显示
|
||
elevators.value.forEach(elevatorData => {
|
||
if (elevatorData.penId) {
|
||
updateElevatorPointDisplay(elevatorData);
|
||
}
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 清除所有电梯数据
|
||
*/
|
||
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>)
|
||
};
|
||
};
|
||
|
||
|
||
|
||
return {
|
||
// 状态
|
||
elevators,
|
||
elevatorPoints,
|
||
editorService,
|
||
|
||
// 计算属性
|
||
allElevators,
|
||
onlineElevatorsCount,
|
||
offlineElevatorsCount,
|
||
faultElevatorsCount,
|
||
elevatorsByStatus,
|
||
|
||
// 方法
|
||
setEditorService,
|
||
handleElevatorWebSocketData,
|
||
getElevatorById,
|
||
updateElevatorStatus,
|
||
refreshMapping,
|
||
clearAllData,
|
||
getStatistics,
|
||
getElevatorDisplay
|
||
};
|
||
}); |