You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

278 lines
9.1 KiB

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<VData> {
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 }