import _ from 'lodash' import { reactive } from 'vue' import { Request } from '@ease-forge/shared' import EventBus from '@/runtime/EventBus' import StateManager from '@/core/manager/StateManager.ts' import { getQueryParams, setQueryParam } from '@/utils/webutils.ts' import BackendMessageReceiver from '@/core/manager/BackendMessageReceiver.ts' import EnvManager from '@/core/manager/EnvManager.ts' import RCSScript from '@/core/script/RCSScript.ts' import LCCScript from '@/core/script/LCCScript.ts' export interface WorldModelState { authorizationConfig: ServerAuthorizationConfigVo // 服务器授权配置 isOpened: boolean // 是否已打开世界模型 worldData: any // 世界模型数据, 包含排除 items 楼层数据之外的所有数据 infos: any // 当前楼层的其他数据 isChanged: boolean // 当前楼层数据是否被修改 catalog: Catalog // 当前世界模型目录数据 server: string // 当前楼层服务器地址 project_uuid: string // 当前楼层所在项目ID project_label: string // 项目名称 sub_system_list: string[] // 子系统集合, 用于标识当前楼层所属的子系统 catalogCode: string // 当前楼层的目录代码 stateManagerId: string // 当前楼层的状态管理器id isDraft: boolean // 是否是草稿数据 runState: { currentEnvId: number, isLoading: boolean, isRunning: boolean, isVirtual: boolean, timeRate: number, currentEnv: EnvInfo } } /** * 物流世界模型 */ export default class WorldModel { currentStateManager: StateManager backendMessageReceiver = new BackendMessageReceiver() envManager = new EnvManager() /** * 世界模型双向绑定的状态数据 */ state: WorldModelState = reactive({ authorizationConfig: null, isOpened: false, // 是否已打开世界模型 worldData: null, // 世界模型数据, 包含排除 items 楼层数据之外的所有数据 infos: {} as any, // 楼层的其他数据 isChanged: false, catalog: [] as Catalog, // 世界模型目录 server: '', project_uuid: '', // 项目ID project_label: '', // 项目名称 sub_system_list: [], // 子系统集合, 用于标识当前楼层所属的子系统 catalogCode: '', // 当前楼层的目录代码 stateManagerId: '', // 当前楼层的状态管理器id, 一般是 项目ID+目录项ID isDraft: false, // 是否是草稿数据, 如果是草稿数据, 则不需要再从服务器加载数据 runState: { currentEnvId: null, isLoading: false, isRunning: false, isVirtual: false, timeRate: 1, currentEnv: null } }) get gridOption(): IGridHelper { const data = _.get(this.state.worldData, 'Tool.gridHelper') return _.defaultsDeep(data, { axesEnabled: true, axesSize: 5, axesColor: 0xDDDDDD, axesWidth: 2, gridEnabled: true, // 启用网格 gridSize: 1000, // 网格大小, 单位米 gridDivisions: 1000, // 网格分割数 gridColor: 0xDDDDDD, // 网格颜色, 十六进制颜色值 gridOpacity: 0.8, // 网格透明度 backgroundColor: 0xF5F5F5, // 背景颜色, 十六进制颜色值 snapEnabled: true, // 启用吸附 snapDistance: 0.25 // 吸附距离, 单位米 }) } /** * 初始化世界模型 */ async init() { system.showLoading('Authenticating...') const configRes = await LCC.getAuthorizationConfig() if (!configRes.success) { system.showErrorDialog('Authentication failed: ' + configRes.msg) return Promise.reject(configRes) } this.state.authorizationConfig = configRes.data await this.backendMessageReceiver.start(configRes.data.frontendMqtt) // 观察 this.state.catalogCode 的变化, 如果变化就调用 catalogCodeChange 方法 return Promise.all([ import('../../modules/measure'), import('../../modules/way'), import('../../modules/gstore'), import('../../modules/rack'), import('../../modules/shuttle_rack'), import('../../modules/pallet'), import('../../modules/palletTz'), import('../../modules/tote'), import('../../modules/carton'), import('../../modules/amr/ptr/cl2'), import('../../modules/amr/ptr/clx'), import('../../modules/charger'), import('../../modules/bracket'), import('../../modules/amr/fm600'), import('../../modules/amr/cc/cc5') ]).then((configRes) => { console.log('世界模型初始化完成') system.clearLoading() // 尝试从草稿中加载数据 const stateManagerId = getQueryParams()?.get('store') StateManager.tryLoadCatalogFromLocalstore(stateManagerId).then(data => { if (data) { this.state.isOpened = true this.state.worldData = data.worldData this.state.infos = data.infos this.state.isChanged = data.isChanged this.state.catalog = data.catalog this.state.server = data.server this.state.project_uuid = data.project_uuid this.state.project_label = data.project_label this.state.sub_system_list = data.sub_system_list || [] this.state.isDraft = true this.tryOpenCatelog(data.catalogCode, true).then(() => { this.state.catalogCode = data.catalogCode this.state.stateManagerId = data.project_uuid + '_' + data.catalogCode }) } }) }) } async tryOpenCatelog(catalogCode: string, isDraft: boolean = false) { if (!catalogCode) { // 没有地图 this.clear() return } if (!this.state.project_uuid) { system.showErrorDialog('项目未初始化, 请先打开一个项目!') this.state.catalogCode = '' this.state.stateManagerId = null return } this.state.catalogCode = catalogCode this.state.isDraft = isDraft this.state.stateManagerId = this.state.project_uuid + '_' + catalogCode const stateManager = new StateManager(this.state.stateManagerId) if (isDraft) { // 从草稿捞数据 await stateManager.loadFromLocalstore() } else { // 从服务器捞数据 const vdata = await worldModel.getCatalogData(catalogCode) await stateManager.load(vdata) } this.currentStateManager = stateManager } /** * 从远程服务器读取世界地图数据目录 */ async loadWorldFromRemoting(lccModelWorld) { const worldData = lccModelWorld const catalog = lccModelWorld.directoryData delete lccModelWorld.directoryData this.state.isOpened = true this.state.worldData = worldData // 没有打开楼层,不加载 this.state.infos // 没有打开楼层,不加载 this.state.isChanged this.state.catalog = catalog this.state.server = lccModelWorld.server this.state.project_uuid = lccModelWorld.projectUuid this.state.project_label = lccModelWorld.projectLabel this.state.sub_system_list = lccModelWorld.subSystemList || [] // 没有打开楼层,不加载 this.state.catalogCode this.state.isDraft = false } /** * 从服务器获取当前目录楼层的所有数据 */ async getCatalogData(catalogCode: string): Promise { const items = await Request.request.get('/api/workbench/LccModelManager@getFloor', { params: { catalogCode, project_uuid: this.state.project_uuid } }) if (!items) { system.showErrorDialog('can\'t get floor data') return Promise.reject('can\'t get floor data') } const vdata: any = { items: items as ItemJson[], infos: { catalogCode, t: 'floor' }, isChanged: false, catalog: _.cloneDeep(this.state.catalog), server: this.state.server, project_uuid: this.state.project_uuid, project_label: this.state.project_label, sub_system_list: _.cloneDeep(this.state.sub_system_list), catalogCode: catalogCode, worldData: _.cloneDeep(this.state.worldData) } return Promise.resolve(vdata) } clear() { this.currentStateManager = null this.state.catalogCode = '' this.state.stateManagerId = null setQueryParam('store', '') EventBus.dispatch('dataLoadComplete', { stateManager: null }) } /** * 改变当前运行环境 */ setEnv(env: EnvInfo) { if (this.state.runState.currentEnvId && this.state.runState.isRunning) { system.showErrorDialog('cannot change env when running') } else { // 更新当前环境 ID if (!env) { this.state.runState.currentEnvId = null this.state.runState.isLoading = false this.state.runState.isRunning = false this.state.runState.isVirtual = false this.state.runState.timeRate = 0 this.state.runState.currentEnv = null } else { this.state.runState.currentEnvId = env.envId this.state.runState.isLoading = false this.state.runState.isRunning = false this.state.runState.isVirtual = env.isVirtual this.state.runState.timeRate = 1 this.state.runState.currentEnv = env } } } } const worldModel = new WorldModel() window['worldModel'] = worldModel window['RCS'] = new RCSScript() window['LCC'] = new LCCScript() export { worldModel }