Browse Source

LccMqttManager /

EnvManager 脱离 viewport 也能运行
master
修宁 6 months ago
parent
commit
b6e85fdc56
  1. 4
      src/core/engine/Viewport.ts
  2. 80
      src/core/manager/EnvManager.ts
  3. 9
      src/core/manager/LccMqttManager.ts
  4. 10
      src/core/manager/WorldModel.ts
  5. 42
      src/core/manager/amr/AmrMessageManager.ts
  6. 71
      src/core/script/LCCScript.ts
  7. 5
      src/core/script/ModelManager.ts
  8. 6
      src/core/script/RCSScript.ts
  9. 6
      src/editor/ModelMain.vue
  10. 16
      src/types/LCC.d.ts

4
src/core/engine/Viewport.ts

@ -52,8 +52,6 @@ export default class Viewport {
interactionManager = new InteractionManager() interactionManager = new InteractionManager()
modelManager = new ModelManager() modelManager = new ModelManager()
runtimeManager = new RuntimeManager() runtimeManager = new RuntimeManager()
lccMqttManager = new LccMqttManager()
envManager = new EnvManager()
// 状态管理器 // 状态管理器
stateManager: StateManager stateManager: StateManager
@ -68,8 +66,6 @@ export default class Viewport {
markRaw(this.interactionManager), markRaw(this.interactionManager),
markRaw(this.modelManager), markRaw(this.modelManager),
markRaw(this.runtimeManager), markRaw(this.runtimeManager),
markRaw(this.envManager),
markRaw(this.lccMqttManager)
] ]
registerFrameTimerCallBack: Map<string, ()=> void> = new Map() registerFrameTimerCallBack: Map<string, ()=> void> = new Map()

80
src/core/manager/EnvManager.ts

@ -9,71 +9,9 @@ import { AmrMsg } from '@/core/manager/amr/AmrMessageDefine'
export default class EnvManager { export default class EnvManager {
private viewport: Viewport private viewport: Viewport
private amrMessageManager: AmrMessageManager = null private amrMessageManager: AmrMessageManager = new AmrMessageManager()
public client: mqtt.MqttClient = null public client: mqtt.MqttClient = null
init(viewport: Viewport): void {
this.viewport = viewport
if (!this.amrMessageManager) {
this.amrMessageManager = new AmrMessageManager()
}
this.amrMessageManager.viewport = viewport
}
// 从后台读取所有车
async loadExecutors() {
const res = await Request.request.post('/api/workbench/EnvController@getAllExecutor', {
projectUuid: worldModel.state.project_uuid,
catalogCode: worldModel.state.catalogCode,
envId: worldModel.state.runState.currentEnvId
})
for (const row of res.data) {
const executor_id = row.executor_id
const payload = JSON.parse(row.virtual_executor_payload)
// 车所在的标记位置,及方向 11_4:RIGHT
const [wayPointId, direction] = _.split(row.virtual_location_at, ':')
const point = Model.find(wayPointId)
if (!point) {
console.error(`Waypoint with ID ${wayPointId} not found for executor ${executor_id}.`)
continue
}
const item = _.cloneDeep(payload)
item.id = executor_id
item.tf[0] = _.cloneDeep(point.tf[0])
switch (_.toLower(direction)) {
// right=0/left=180/up=90/down=-90
case 'right':
item.tf[1][1] = 0 // 右侧
break
case 'left':
item.tf[1][1] = 180 // 左侧
break
case 'down':
item.tf[1][1] = -90 // 下方
break
case 'up':
item.tf[1][1] = 90 // 上方
break
}
Model.createExecutor(item)
}
}
appendExecutor(id) {
const obj = this.viewport.entityManager.findObjectById(id)
const item = this.viewport.entityManager.findItemById(id)
if (item.t == 'cl2' && obj.userData.t === 'cl2') {
debugger
const cl2 = obj as Cl23dObject
cl2.onMqttConnect(item, this.client)
// this.client.subscribe(['/wcs_server/' + cl2.id], { qos: 0 })
// this.client.publish('/agv_robot/status', JSON.stringify(m20020), { retain: true })
}
}
onMqttConnect = (packet: IConnackPacket) => { onMqttConnect = (packet: IConnackPacket) => {
console.log('Connected') console.log('Connected')
} }
@ -86,7 +24,6 @@ export default class EnvManager {
} else { } else {
} }
} }
onMqttError = (error: Error | ErrorWithReasonCode) => { onMqttError = (error: Error | ErrorWithReasonCode) => {
@ -102,10 +39,6 @@ export default class EnvManager {
system.showErrorDialog('WorldModel is not opened, cannot start EnvManager.') system.showErrorDialog('WorldModel is not opened, cannot start EnvManager.')
return return
} }
if (!this.viewport) {
system.showErrorDialog('Viewport is not initialized, cannot start EnvManager.')
return
}
if (!worldModel.state.runState.currentEnvId) { if (!worldModel.state.runState.currentEnvId) {
system.showErrorDialog('Current environment ID is not set, cannot start EnvManager.') system.showErrorDialog('Current environment ID is not set, cannot start EnvManager.')
return return
@ -129,8 +62,8 @@ export default class EnvManager {
worldModel.state.runState.isLoading = true worldModel.state.runState.isLoading = true
worldModel.state.runState.currentEnv = Object.freeze(env) worldModel.state.runState.currentEnv = Object.freeze(env)
try { try {
await LCC.projectStart() await LCC.serverStart()
await this.loadExecutors() await LCC.loadExecutor()
await LCC.loadInv() await LCC.loadInv()
this.client = mqtt.connect(env.envConfig.mqtt.websocket, { this.client = mqtt.connect(env.envConfig.mqtt.websocket, {
@ -144,7 +77,7 @@ export default class EnvManager {
keepalive: 60 keepalive: 60
}) })
this.viewport.lccMqttManager.start(env.envConfig.frontendMqtt) await worldModel.lccMqttManager.start(env.envConfig.frontendMqtt)
this.client.on('connect', this.onMqttConnect) this.client.on('connect', this.onMqttConnect)
this.client.on('message', this.onMqttMessage) this.client.on('message', this.onMqttMessage)
this.client.on('error', this.onMqttError) this.client.on('error', this.onMqttError)
@ -159,8 +92,8 @@ export default class EnvManager {
async stop() { async stop() {
system.showLoading() system.showLoading()
try { try {
if (LCC) { if (window['LCC']) {
await LCC.projectStop() await LCC.serverStop()
} }
worldModel.state.runState.isRunning = false worldModel.state.runState.isRunning = false
if (this.client) { if (this.client) {
@ -214,7 +147,6 @@ export default class EnvManager {
* *
*/ */
dispose(): void { dispose(): void {
this.viewport = null
this.stop() this.stop()
} }
} }

9
src/core/manager/LccMqttManager.ts

@ -20,15 +20,12 @@ enum ConnectionStatus {
ERROR = 'error' ERROR = 'error'
} }
// 定义LCC MQTT管理器类 /**
*
*/
export default class LccMqttManager { export default class LccMqttManager {
private client: mqtt.MqttClient | null = null private client: mqtt.MqttClient | null = null
private handlers: Map<string, MqttMessageHandler[]> = new Map() private handlers: Map<string, MqttMessageHandler[]> = new Map()
private viewport: Viewport
init(viewport: Viewport): void {
this.viewport = viewport
}
// 状态管理 // 状态管理
public state = reactive({ public state = reactive({

10
src/core/manager/WorldModel.ts

@ -5,6 +5,10 @@ import EventBus from '@/runtime/EventBus'
import StateManager from '@/core/manager/StateManager.ts' import StateManager from '@/core/manager/StateManager.ts'
import { getQueryParams, setQueryParam } from '@/utils/webutils.ts' import { getQueryParams, setQueryParam } from '@/utils/webutils.ts'
import localforage from 'localforage' import localforage from 'localforage'
import LccMqttManager from '@/core/manager/LccMqttManager.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 { export interface WorldModelState {
isOpened: boolean // 是否已打开世界模型 isOpened: boolean // 是否已打开世界模型
@ -36,6 +40,8 @@ export interface WorldModelState {
*/ */
export default class WorldModel { export default class WorldModel {
currentStateManager: StateManager currentStateManager: StateManager
lccMqttManager = new LccMqttManager()
envManager = new EnvManager()
/** /**
* *
@ -56,7 +62,7 @@ export default class WorldModel {
isDraft: false, // 是否是草稿数据, 如果是草稿数据, 则不需要再从服务器加载数据 isDraft: false, // 是否是草稿数据, 如果是草稿数据, 则不需要再从服务器加载数据
runState: { runState: {
currentEnvId: 0, currentEnvId: null,
isLoading: false, isLoading: false,
isRunning: false, isRunning: false,
isVirtual: false, isVirtual: false,
@ -220,4 +226,6 @@ export default class WorldModel {
const worldModel = new WorldModel() const worldModel = new WorldModel()
window['worldModel'] = worldModel window['worldModel'] = worldModel
window['RCS'] = new RCSScript()
window['LCC'] = new LCCScript()
export { worldModel } export { worldModel }

42
src/core/manager/amr/AmrMessageManager.ts

@ -1,61 +1,63 @@
import {AmrMsg, AmrMsg10010, AmrMsg10050, AmrMsg10060, AmrMsg10110, AmrMsg10120, AmrMsg20050} from "@/core/manager/amr/AmrMessageDefine"; import { AmrMsg, AmrMsg10010, AmrMsg10050, AmrMsg10060, AmrMsg10110, AmrMsg10120, AmrMsg20050 } from '@/core/manager/amr/AmrMessageDefine'
import Cl23dObject from "@/modules/cl2/Cl23dObject"; import Cl23dObject from '@/modules/cl2/Cl23dObject'
import Viewport from "@/core/engine/Viewport"; import Viewport from '@/core/engine/Viewport'
export default class AmrMessageManager { export default class AmrMessageManager {
public viewport: Viewport public get viewport(): Viewport {
return window['viewport']
}
handleMessage(topic, amrMsg: AmrMsg<any>) { handleMessage(topic, amrMsg: AmrMsg<any>) {
const vehicleId = parseInt(topic.replace("/wcs_server/", "")) const vehicleId = parseInt(topic.replace('/wcs_server/', ''))
const amrItem = this.viewport.entityManager.findObjectById(vehicleId + "") as Cl23dObject const amrItem = this.viewport.entityManager.findObjectById(vehicleId + '') as Cl23dObject
switch (amrMsg.id) { switch (amrMsg.id) {
// AMR作业指令 10010 // AMR作业指令 10010
case 10010: case 10010:
amrItem.handle10010Message(amrMsg.content as AmrMsg10010) amrItem.handle10010Message(amrMsg.content as AmrMsg10010)
break; break
// 停止/解除 10040 // 停止/解除 10040
case 10040: case 10040:
break; break
// 电文应答 10050 // 电文应答 10050
case 10050: case 10050:
amrItem.handle10050Message(amrMsg as AmrMsg<AmrMsg10050>) amrItem.handle10050Message(amrMsg as AmrMsg<AmrMsg10050>)
break; break
// 配置信息 10060 // 配置信息 10060
case 10060: case 10060:
amrItem.handle10060Message(amrMsg as AmrMsg<AmrMsg10060>) amrItem.handle10060Message(amrMsg as AmrMsg<AmrMsg10060>)
break; break
// 旋转货架 10080 // 旋转货架 10080
case 10080: case 10080:
break; break
// 旋转车身 10081 // 旋转车身 10081
case 10081: case 10081:
break; break
// 控制卷帘门 10082 // 控制卷帘门 10082
case 10082: case 10082:
break; break
// 心跳 10100 // 心跳 10100
case 10100: case 10100:
break; break
// 状态查询 10110 // 状态查询 10110
case 10110: case 10110:
amrItem.handle10110Message(amrMsg as AmrMsg<AmrMsg10110>) amrItem.handle10110Message(amrMsg as AmrMsg<AmrMsg10110>)
break; break
// 取消已下发小车任务 10120 // 取消已下发小车任务 10120
case 10120: case 10120:
amrItem.handle10120Message(amrMsg as AmrMsg<AmrMsg10120>) amrItem.handle10120Message(amrMsg as AmrMsg<AmrMsg10120>)
break; break
// 设置小车坐标 10200 // 设置小车坐标 10200
case 10200: case 10200:
break; break
// 等待就绪 19997 // 等待就绪 19997
case 19997: case 19997:
break; break
} }
if (amrMsg.id != 10050 && amrMsg.id != 10100) { if (amrMsg.id != 10050 && amrMsg.id != 10100) {
const seqNo = amrMsg.content.SeqNo; const seqNo = amrMsg.content.SeqNo
amrItem.sendAck(seqNo, vehicleId); amrItem.sendAck(seqNo, vehicleId)
} }
} }
} }

71
src/core/script/LCCScript.ts

@ -6,43 +6,35 @@ import { Request } from '@ease-forge/shared'
* LCC API * LCC API
*/ */
export default class LCCScript implements LCC { export default class LCCScript implements LCC {
private readonly viewport: Viewport
constructor(viewport: Viewport) {
this.viewport = viewport
}
async getAllProjects(): Promise<ServerResponse<LccProjectVo[]>> { async getAllProjects(): Promise<ServerResponse<LccProjectVo[]>> {
return Request.request.post('/api/workbench/LccController@getAllProjects', { return Request.request.post('/api/workbench/LccController@getAllProjects', {})
})
} }
async projectStart(): Promise<ServerResponse<boolean>> { async serverStart(): Promise<ServerResponse<boolean>> {
if (!worldModel.state.project_uuid || !worldModel.state.runState.currentEnvId) { if (!worldModel.state.project_uuid || !worldModel.state.runState.currentEnvId) {
return Promise.reject(new Error('Project UUID or Environment ID is not set.')) return Promise.reject(new Error('Project UUID or Environment ID is not set.'))
} }
return Request.request.post('/api/workbench/LccController@projectStart', { return Request.request.post('/api/workbench/LccController@serverStart', {
projectUUID: worldModel.state.project_uuid, projectUUID: worldModel.state.project_uuid,
envId: worldModel.state.runState.currentEnvId envId: worldModel.state.runState.currentEnvId
}) })
} }
async projectStop(): Promise<ServerResponse<boolean>> { async serverStop(): Promise<ServerResponse<boolean>> {
if (!worldModel.state.project_uuid || !worldModel.state.runState.currentEnvId) { if (!worldModel.state.project_uuid || !worldModel.state.runState.currentEnvId) {
return Promise.reject(new Error('Project UUID or Environment ID is not set.')) return Promise.reject(new Error('Project UUID or Environment ID is not set.'))
} }
return Request.request.post('/api/workbench/LccController@projectStop', { return Request.request.post('/api/workbench/LccController@serverStop', {
projectUUID: worldModel.state.project_uuid, projectUUID: worldModel.state.project_uuid,
envId: worldModel.state.runState.currentEnvId envId: worldModel.state.runState.currentEnvId
}) })
} }
// 从后台读取所有库存 // 从后台读取所有库存
async loadInv(): Promise<ServerResponse<InvVo>> { async loadInv(): Promise<ServerResponse<InvVo>> {
const res = await Request.request.post('/api/workbench/LccController@getAllInv', { const res = await Request.request.post('/api/workbench/LccController@loadInv', {
projectUuid: worldModel.state.project_uuid, projectUuid: worldModel.state.project_uuid,
catalogCode: worldModel.state.catalogCode, catalogCode: worldModel.state.catalogCode,
envId: worldModel.state.runState.currentEnvId envId: worldModel.state.runState.currentEnvId
@ -55,8 +47,57 @@ export default class LCCScript implements LCC {
const lpn = row.lpn // : "LPN1" const lpn = row.lpn // : "LPN1"
const rack = row.rack // : "rack1" const rack = row.rack // : "rack1"
const container_type = row.container_type // : "pallet" const container_type = row.container_type // : "pallet"
Model.createInv(container_type, lpn, rack, bay, level, cell) if (window['Model']) {
Model.createInv(container_type, lpn, rack, bay, level, cell)
}
} }
return res.data return res.data
} }
// 从后台读取所有车
async loadExecutor(): Promise<ServerResponse<ExecutorVo>> {
const res = await Request.request.post('/api/workbench/LccController@loadExecutor', {
projectUuid: worldModel.state.project_uuid,
envId: worldModel.state.runState.currentEnvId
})
for (const row of res.data) {
const executor_id = row.executor_id
const payload = JSON.parse(row.virtual_executor_payload)
// 车所在的标记位置,及方向 11_4:RIGHT
const [wayPointId, direction] = _.split(row.virtual_location_at, ':')
if (window['Model']) {
const point = Model.find(wayPointId)
if (!point) {
console.error(`Waypoint with ID ${wayPointId} not found for executor ${executor_id}.`)
continue
}
const item = _.cloneDeep(payload)
item.id = executor_id
item.tf[0] = _.cloneDeep(point.tf[0])
switch (_.toLower(direction)) {
// right=0/left=180/up=90/down=-90
case 'right':
item.tf[1][1] = 0 // 右侧
break
case 'left':
item.tf[1][1] = 180 // 左侧
break
case 'down':
item.tf[1][1] = -90 // 下方
break
case 'up':
item.tf[1][1] = 90 // 上方
break
}
if (row.virtual_floor_code === worldModel.state.catalogCode) {
Model.createExecutor(item)
}
}
}
return res.data
}
} }

5
src/core/script/ModelManager.ts

@ -7,8 +7,6 @@ import TaskManager from '../manager/TaskManager.ts'
import Cl2Entity from '@/modules/cl2/Cl2Entity.ts' import Cl2Entity from '@/modules/cl2/Cl2Entity.ts'
import ClxEntity from '@/modules/clx/ClxEntity.ts' import ClxEntity from '@/modules/clx/ClxEntity.ts'
import { getRenderer } from '@/core/manager/ModuleManager.ts' import { getRenderer } from '@/core/manager/ModuleManager.ts'
import RCSScript from '@/core/script/RCSScript.ts'
import LCCScript from '@/core/script/LCCScript.ts'
export default class ModelManager implements IControls, Model { export default class ModelManager implements IControls, Model {
private viewport: Viewport private viewport: Viewport
@ -67,9 +65,6 @@ export default class ModelManager implements IControls, Model {
window['executestring'] = this.executestring.bind(this) window['executestring'] = this.executestring.bind(this)
window['logger'] = this.logger window['logger'] = this.logger
window['msg'] = this.msg window['msg'] = this.msg
window['RCS'] = new RCSScript(this.viewport)
window['LCC'] = new LCCScript(this.viewport)
} }
find(entityId: string): ItemJson { find(entityId: string): ItemJson {

6
src/core/script/RCSScript.ts

@ -3,12 +3,6 @@ import { Request } from '@ease-forge/shared'
import { worldModel } from '@/core/manager/WorldModel.ts' import { worldModel } from '@/core/manager/WorldModel.ts'
export default class RCSScript implements RCS { export default class RCSScript implements RCS {
private readonly viewport: Viewport
constructor(viewport: Viewport) {
this.viewport = viewport
}
agvMove(agvId: string, targetWayPointId: string, targetDirection: '' | LLCDirection = '', option: AgvOptions = {}): Promise<ServerResponse<boolean>> { agvMove(agvId: string, targetWayPointId: string, targetDirection: '' | LLCDirection = '', option: AgvOptions = {}): Promise<ServerResponse<boolean>> {
return Request.request.post('/api/workbench/RcsController@agvMove', { return Request.request.post('/api/workbench/RcsController@agvMove', {
projectUUID: worldModel.state.project_uuid, projectUUID: worldModel.state.project_uuid,

6
src/editor/ModelMain.vue

@ -13,7 +13,7 @@
</div> </div>
<div v-if="isModelOpen" style="display: flex; flex-direction: row; align-items: center; margin-right: 10px;"> <div v-if="isModelOpen" style="display: flex; flex-direction: row; align-items: center; margin-right: 10px;">
<div class="field-block" style="margin-right: 5px;"> <div class="field-block" style="margin-right: 5px;">
<el-select style="width:180px;" placeholder="运行环境" :disabled="worldModelState.runState.currentEnvId && worldModelState.runState.isRunning" <el-select style="width:180px;" placeholder="选择运行环境" :disabled="worldModelState.runState.currentEnvId && worldModelState.runState.isRunning"
v-model="currentEnvId"> v-model="currentEnvId">
<el-option v-for="env in envList" :key="env.envId" :label="env.envName" :value="env.envId"></el-option> <el-option v-for="env in envList" :key="env.envId" :label="env.envName" :value="env.envId"></el-option>
<template #footer> <template #footer>
@ -334,11 +334,11 @@ export default {
startEnv() { startEnv() {
const env = this.envList.find(env => env.envId === this.worldModelState.runState.currentEnvId) const env = this.envList.find(env => env.envId === this.worldModelState.runState.currentEnvId)
if (env) { if (env) {
this.currentViewport.envManager.start(env) worldModel.envManager.start(env)
} }
}, },
stopEnv() { stopEnv() {
this.currentViewport.envManager.stop() worldModel.envManager.stop()
}, },
createEnv() { createEnv() {
EnvManager.createEnv(this.worldModelState.project_uuid).then(() => { EnvManager.createEnv(this.worldModelState.project_uuid).then(() => {

16
src/types/LCC.d.ts

@ -10,17 +10,22 @@ declare interface LCC {
/** /**
* *
*/ */
projectStart(): Promise<ServerResponse<boolean>> serverStart(): Promise<ServerResponse<boolean>>
/** /**
* *
*/ */
projectStop(): Promise<ServerResponse<boolean>> serverStop(): Promise<ServerResponse<boolean>>
/** /**
* , Model * , Model
*/ */
loadInv(): Promise<ServerResponse<InvVo>> loadInv(): Promise<ServerResponse<InvVo>>
/**
* Model
*/
loadExecutor(): Promise<ServerResponse<ExecutorVo>>
} }
type ContainerT = 'pallet' | 'tote' | 'carton' | 'box' type ContainerT = 'pallet' | 'tote' | 'carton' | 'box'
@ -35,6 +40,13 @@ interface InvVo {
cell: number cell: number
} }
interface ExecutorVo {
executor_id: string
virtual_floor_code: string
virtual_executor_payload: any
virtual_location_at: string
}
interface LccProjectVo { interface LccProjectVo {
projectUuid: string projectUuid: string
projectLabel: string projectLabel: string

Loading…
Cancel
Save