Browse Source

CL2 Task 封装

master
修宁 6 months ago
parent
commit
ce90fb5b44
  1. 285
      src/core/base/BaseItemEntity.ts
  2. 24
      src/core/base/BaseRenderer.ts
  3. 1
      src/core/base/Carry.ts
  4. 98
      src/core/base/TaskQueue.ts
  5. 2
      src/core/manager/EntityManager.ts
  6. 53
      src/core/manager/InstanceMeshManager.ts
  7. 13
      src/core/manager/TaskManager.ts
  8. 4
      src/core/manager/WorldModel.ts
  9. 9
      src/core/script/ModelManager.ts
  10. 52
      src/example/example1.js
  11. 72
      src/modules/cl2/Cl2Entity.ts
  12. 0
      src/modules/cl2/Cl2Interaction.ts
  13. 0
      src/modules/cl2/Cl2PropertySetter.ts
  14. 11
      src/modules/cl2/Cl2Renderer.ts
  15. 15
      src/modules/cl2/index.ts
  16. 23
      src/modules/gstore/GstoreRenderer.ts
  17. 5
      src/modules/ptr/PtrEntity.ts
  18. 15
      src/modules/ptr/index.ts
  19. 10
      src/types/ScriptSupport.d.ts
  20. 178
      src/types/model.d.ts

285
src/core/base/BaseItemEntity.ts

@ -1,26 +1,285 @@
import * as THREE from 'three' import * as THREE from 'three'
import gsap from 'gsap'
import type { Object3DLike } from '@/types/ModelTypes.ts'
import Viewport from '@/core/engine/Viewport.ts'
import TaskQueue, { type Task } from '@/core/base/TaskQueue.ts'
import type { MeshWrap } from '@/core/manager/InstanceMeshManager.ts'
import { getRenderer } from '@/core/manager/ModuleManager.ts'
/** /**
* BaseEntity class *
* Provides a base for managing logistics unit entities.
*/ */
export default abstract class BaseEntity { export default abstract class BaseEntity implements EntityIf, Loadable, Carry, Walk, ForkArm, LiftingArm {
protected itemJson!: ItemJson protected readonly viewport: Viewport
protected objects!: THREE.Object3D[] protected readonly taskQueue: TaskQueue = new TaskQueue()
readonly id: string
readonly item: ItemJson
readonly object: THREE.Object3D
readonly t: string
isArmExtended: boolean = false
isArmRaised: boolean = false
isCarrying: boolean = false
isLoading: boolean = false
isUnloading: boolean = false
isWalking: boolean = false
isPaused: boolean = false
constructor(viewport: Viewport, id: string) {
this.viewport = viewport
this.id = id
this.item = viewport.find(id)
this.object = viewport.entityManager.findObjectById(id) as THREE.Object3D
this.t = this.item.t
}
// 帮助方法:获取机械臂对象
abstract getArmObject(): THREE.Object3D | undefined
// 帮助方法:获取机械臂立柱
abstract getArmPillar(): THREE.Object3D | undefined
// 待执行任务总数
get taskCount(): number {
return this.taskQueue.length
}
// 是否正在执行任务
get taskIsRunning(): boolean {
return this.taskQueue.isExecuting
}
// 取消所有任务
cancelTask(): void {
this.taskQueue.cancelAll()
}
// 暂停任务
taskPause(): void {
this.isPaused = true
this.taskQueue.pause()
}
// 恢复任务
taskResume(): void {
this.isPaused = false
this.taskQueue.resume()
}
// 开始执行任务
taskStartRun(): void {
this.taskQueue.start()
}
// 创建基础任务
protected createTask<T extends Task>(type: string, execute: () => Promise<void>, cancel?: () => void): T {
return { type, execute, cancel, isCancelable: true } as T
}
// 上
addArmRaise(height: number): void {
this.taskQueue.add(this.createTask('ARM_RAISE', async () => {
return new Promise(resolve => {
const arm = this.getArmObject()
if (!arm) return resolve()
gsap.to(arm.position, {
y: height,
duration: 3,
ease: 'sine.inOut',
onComplete: resolve
})
})
}, () => {
// 取消动画
gsap.killTweensOf(this.getArmObject()?.position)
}))
}
// 下
addArmLower(): void {
this.taskQueue.add(this.createTask('ARM_LOWER', async () => {
return new Promise(resolve => {
const arm = this.getArmObject()
if (!arm) return resolve()
gsap.to(arm.position, {
y: 0,
duration: 3,
ease: 'sine.inOut',
onComplete: resolve
})
})
}, () => {
gsap.killTweensOf(this.getArmObject()?.position)
}))
}
// 伸
addArmExtender(): void {
this.taskQueue.add(this.createTask('ARM_EXTEND', async () => {
return new Promise(resolve => {
const pillar = this.getArmPillar()
if (!pillar) return resolve()
gsap.to(pillar.position, {
z: -1.2,
duration: 3,
ease: 'sine.inOut',
onComplete: () => {
this.isArmExtended = true
resolve()
}
})
})
}, () => {
gsap.killTweensOf(this.getArmPillar()?.position)
}))
}
// 缩
addArmRetractor(): void {
this.taskQueue.add(this.createTask('ARM_RETRACT', async () => {
return new Promise(resolve => {
const pillar = this.getArmPillar()
if (!pillar) return resolve()
gsap.to(pillar.position, {
z: 0,
duration: 3,
ease: 'sine.inOut',
onComplete: () => {
this.isArmExtended = false
resolve()
}
})
})
}, () => {
gsap.killTweensOf(this.getArmPillar()?.position)
}))
}
// 装
addLoad(item: string): void {
this.taskQueue.add(this.createTask('LOAD', async () => {
// 实际业务中应包含装载逻辑
this.isCarrying = true
throw new Error(`装载物品 ${item} 的逻辑未实现`)
}))
}
// 卸
addUnload(item: string, rack: string, bay?: number, level?: number): void {
this.taskQueue.add(this.createTask('UNLOAD', async () => {
// 实际业务中应包含卸载逻辑
this.isCarrying = false
throw new Error(`装载物品 ${item} 的逻辑未实现`)
}))
}
// 转
addRotation(angle: number): void {
this.taskQueue.add(this.createTask('ROTATION', async () => {
return new Promise(resolve => {
const time = Math.abs(angle - THREE.MathUtils.radToDeg(this.object.rotation.y)) / 45
const duration = Math.max(0.5, time)
gsap.to(this.object.rotation, {
y: THREE.MathUtils.degToRad(angle),
duration,
ease: 'none',
onComplete: resolve
})
})
}, () => {
gsap.killTweensOf(this.object.rotation)
}))
}
// 走
addTravel(pos: Vector3IF | string): void {
this.taskQueue.add(this.createTask('TRAVEL', async () => {
return new Promise(resolve => {
const fromPos = this.object.position
let position: Vector3IF
if (pos instanceof String || typeof pos === 'string') {
pos = Model.getPositionByEntityId(pos as string)
}
const toPos = new THREE.Vector3(pos.x, pos.y, pos.z)
const distance = fromPos.distanceTo(toPos)
const speed = 2 // 2m/s
const duration = Math.max(1.5, distance / speed)
this.isWalking = true
gsap.to(this.object.position, {
x: toPos.x,
y: toPos.y,
z: toPos.z,
duration,
ease: 'sine.inOut',
onComplete: () => {
this.isWalking = false
resolve()
}
})
})
}, () => {
this.isWalking = false
gsap.killTweensOf(this.object.position)
}))
}
/** /**
* Sets the `ItemJson` data for the entity. *
* @param itemJson - The `ItemJson` data to set.
*/ */
setItem(itemJson: ItemJson): void { pickupItem(id: string): THREE.Object3D {
this.itemJson = itemJson // 找到物品所在的地方, 删除他
const item = this.viewport.entityManager.findItemById(id)
const wrap = this.viewport.entityManager.findObjectById(id) as MeshWrap
if (wrap.type !== 'MeshWrap') {
throw new Error(`无法拾取物品 ${id},它不是一个有效的 MeshWrap`)
}
item.dt.storeAt = {
item: this.id
}
const mesh = wrap.manager.wrapToObject3D(wrap)
this.viewport.entityManager.replaceObject(id, mesh)
return mesh
} }
/** /**
* Sets the `THREE.Object3D` object for the entity. *
* @param object3D - The `THREE.Object3D` object to set.
*/ */
setObjects(objects: THREE.Object3D[]): void { dropItem(item: ItemJson, storeItemId: string, bay?: number, level?: number, cell?: number): void {
this.objects = objects item.dt.storeAt = {
item: storeItemId,
bay: bay,
level: level,
cell: cell
}
const rack = Model.find(storeItemId)
const rackRenderer = getRenderer(rack.t)
const itemRenderer = getRenderer(item.t)
if (!rackRenderer) {
throw new Error(`Renderer for type ${item.t} not found`)
}
if (!itemRenderer) {
throw new Error(`Renderer for type ${item.t} not found`)
}
const { position, rotation } = rackRenderer.getStorePlacement(this.viewport, item)
if (!position || !rotation) {
throw new Error(`无法获取物品 ${item.id} 的存储位置`)
}
this.getArmObject().clear()
itemRenderer.tempViewport = this.viewport
itemRenderer.createOrUpdatePointForRuntime(item, position, rotation)
itemRenderer.tempViewport = null
} }
} }

24
src/core/base/BaseRenderer.ts

@ -119,6 +119,11 @@ export default abstract class BaseRenderer {
return point return point
} }
createOrUpdatePointForRuntime(item: ItemJson, position: THREE.Vector3, rotation: THREE.Quaternion): Object3DLike {
const option = { isRuntime: true, position, rotation }
return this.createPointForEntity(item, option)
}
/** /**
* *
* @param item * @param item
@ -127,7 +132,16 @@ export default abstract class BaseRenderer {
createPoint(item: ItemJson, option?: RendererCudOption): Object3DLike { createPoint(item: ItemJson, option?: RendererCudOption): Object3DLike {
// 由基础类创造一个属于自己的点演示 // 由基础类创造一个属于自己的点演示
const point = this.createPointBasic(item, option) const point = this.createPointBasic(item, option)
point.setMatrix4(getMatrixFromTf(item.tf)) let matrix = getMatrixFromTf(item.tf)
if (option?.position instanceof THREE.Vector3 && option?.rotation instanceof THREE.Quaternion) {
matrix = new THREE.Matrix4()
matrix.compose(
option.position,
option.rotation,
new THREE.Vector3(item.tf[2][0], item.tf[2][1], item.tf[2][2])
)
}
point.setMatrix4(matrix)
// point.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) // point.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2])
// //
@ -250,6 +264,14 @@ export default abstract class BaseRenderer {
} }
/** /**
*
* Position Rotation
*/
getStorePlacement(viewport: Viewport, item: ItemJson): { position: THREE.Vector3, rotation: THREE.Quaternion } {
throw new Error(' 不支持库存物品的添加. t=' + this.itemTypeName)
}
/**
* 线 * 线
* @param start * @param start
* @param end * @param end

1
src/core/base/Carry.ts

@ -0,0 +1 @@

98
src/core/base/TaskQueue.ts

@ -0,0 +1,98 @@
/**
*
*/
export interface Task {
type: string
execute(): Promise<void>
cancel?(): void
isCancelable?: boolean
}
/**
*
*/
export default class TaskQueue {
private queue: Task[] = []
private currentTask: Task | null = null
private isRunning = false
private isPaused = false
private onTaskComplete: ((task: Task) => void) | null = null
constructor() {
}
get length(): number {
return this.queue.length + (this.currentTask ? 1 : 0)
}
get isExecuting(): boolean {
return this.isRunning && !this.isPaused
}
add(task: Task): void {
this.queue.push(task)
}
clear(): void {
this.queue = []
}
pause(): void {
this.isPaused = true
if (this.currentTask && this.currentTask.cancel) {
this.currentTask.cancel()
}
}
resume(): void {
this.isPaused = false
if (this.currentTask) {
this.executeNextTask()
}
}
cancelAll(): void {
this.isRunning = false
this.isPaused = false
this.queue = []
if (this.currentTask && this.currentTask.cancel) {
this.currentTask.cancel()
}
this.currentTask = null
if (this.onTaskComplete) {
this.onTaskComplete(this.currentTask || { type: 'CANCELLED' } as Task)
}
}
start(onTaskComplete?: (task: Task) => void): void {
this.onTaskComplete = onTaskComplete
if (!this.isRunning) {
this.isRunning = true
this.executeNextTask()
}
}
private async executeNextTask(): Promise<void> {
if (this.isPaused || this.queue.length === 0) {
return
}
this.currentTask = this.queue.shift()!
try {
await this.currentTask.execute()
if (this.onTaskComplete) {
this.onTaskComplete(this.currentTask)
}
this.currentTask = null
this.executeNextTask()
} catch (error) {
console.error('Task execution failed:', error)
this.currentTask = null
this.executeNextTask()
}
}
}

2
src/core/manager/EntityManager.ts

@ -500,7 +500,7 @@ export default class EntityManager {
if (!linkStartPointId) { if (!linkStartPointId) {
return return
} }
return getFreezeDeep(this.___entityMap.get(linkStartPointId)) return this.___entityMap.get(linkStartPointId)
} }
getObjectByCanvasMouse(event: MouseEvent): Object3DLike[] { getObjectByCanvasMouse(event: MouseEvent): Object3DLike[] {

53
src/core/manager/InstanceMeshManager.ts

@ -12,8 +12,8 @@ export default class InstanceMeshManager {
public readonly blockCapacity: number public readonly blockCapacity: number
public readonly blocks: InstanceMeshBlock[] = [] public readonly blocks: InstanceMeshBlock[] = []
private readonly geometry: THREE.BufferGeometry public readonly geometry: THREE.BufferGeometry
private readonly material: THREE.Material public readonly material: THREE.Material
private readonly dummy: THREE.Object3D = new THREE.Object3D() private readonly dummy: THREE.Object3D = new THREE.Object3D()
@ -159,6 +159,53 @@ export default class InstanceMeshManager {
} }
this.__uuidMap.clear() this.__uuidMap.clear()
} }
/**
* MeshWrap Object3D
*/
wrapToObject3D(object: MeshWrap): THREE.Object3D {
if (!(object instanceof MeshWrap)) {
console.warn('InstanceMeshManager: Only THREE.Mesh can be wrapped')
return null
}
const wrap = object as MeshWrap
const matrix = wrap.matrix.clone()
const mesh = new THREE.Mesh(this.geometry, this.material)
mesh.uuid = wrap.uuid
mesh.name = wrap.name
mesh.visible = wrap.visible
mesh.userData = wrap.userData
matrix.decompose(mesh.position, mesh.quaternion, mesh.scale)
mesh.updateMatrix()
wrap.dispose()
return mesh
}
/**
* Object3D InstancedMesh
*/
object3DToWrap(object: THREE.Object3D): MeshWrap {
if (!(object instanceof THREE.Mesh)) {
console.warn('InstanceMeshManager: Only THREE.Mesh can be wrapped')
return null
}
const entityId = object.uuid || system.createUUID()
const wrap = this.create(entityId, object.userData)
// 设置初始矩阵
object.updateMatrixWorld(true)
const matrix = new THREE.Matrix4().copy(object.matrixWorld)
wrap.setMatrix4(matrix)
// 设置其他属性
wrap.name = object.name || entityId
wrap.visible = object.visible
return wrap
}
} }
export class MeshWrap { export class MeshWrap {
@ -174,6 +221,8 @@ export class MeshWrap {
name: string name: string
visible: boolean visible: boolean
type = 'MeshWrap'
get position(): THREE.Vector3 { get position(): THREE.Vector3 {
const m = new THREE.Matrix4() const m = new THREE.Matrix4()
this.manager.blocks[this.blockIndex].instancedMesh.getMatrixAt(this.meshIndex, m) this.manager.blocks[this.blockIndex].instancedMesh.getMatrixAt(this.meshIndex, m)

13
src/core/manager/TaskManager.ts

@ -1,16 +1,5 @@
// 任务 API
// TaskSequence ts = TaskSequence.create(agv)
// ts.addTask('TASKTYPE_LOAD', item, queue);
// ts.addTask('TASKTYPE_TRAVEL', "entityId");
// ts.addTask('TASKTYPE_ROTATION', 90);
// ts.addTask('TASKTYPE_UNLOAD', item, rack);
// ts.addTask('TASKTYPE_UNLOAD', item, rack, 1, 3);
//
// ts.dispatch();
// import {Object3DLike} from "@/types/ModelTypes";
import gsap from 'gsap'
import * as THREE from 'three' import * as THREE from 'three'
import gsap from 'gsap'
import Viewport from '@/core/engine/Viewport' import Viewport from '@/core/engine/Viewport'
export default class TaskManager implements TaskManagerIF { export default class TaskManager implements TaskManagerIF {

4
src/core/manager/WorldModel.ts

@ -10,7 +10,7 @@ import Pallet from '@/modules/pallet'
import Tote from '@/modules/tote' import Tote from '@/modules/tote'
import Agv1 from '@/modules/agv1' import Agv1 from '@/modules/agv1'
import Carton from '@/modules/carton' import Carton from '@/modules/carton'
import Ptr from '@/modules/ptr' import Cl2 from '@/modules/cl2'
import Clx from '@/modules/clx' import Clx from '@/modules/clx'
import Charger from '@/modules/charger' import Charger from '@/modules/charger'
import StateManager from '@/core/manager/StateManager.ts' import StateManager from '@/core/manager/StateManager.ts'
@ -93,7 +93,7 @@ export default class WorldModel {
Pallet, Pallet,
Tote, Tote,
Carton, Carton,
Ptr, Cl2,
Clx, Clx,
Agv1, Agv1,
Charger Charger

9
src/core/script/ModelManager.ts

@ -4,10 +4,19 @@ import * as THREE from 'three'
import { getMatrixFromTf } from '@/core/ModelUtils.ts' import { getMatrixFromTf } from '@/core/ModelUtils.ts'
import type { Object3DLike } from '@/types/ModelTypes.ts' import type { Object3DLike } from '@/types/ModelTypes.ts'
import TaskManager from '../manager/TaskManager.ts' import TaskManager from '../manager/TaskManager.ts'
import Cl2Entity from '@/modules/cl2/Cl2Entity.ts'
export default class ModelManager implements IControls, Model { export default class ModelManager implements IControls, Model {
private viewport: Viewport private viewport: Viewport
getCl2(id: string): Cl2If {
return new Cl2Entity(this.viewport, id)
}
getClx(id: string): ClxIf {
throw new Error('Method not implemented.')
}
createTask(agv: object): TaskManager { createTask(agv: object): TaskManager {
if (TaskManager.taskMap.has(agv)) { if (TaskManager.taskMap.has(agv)) {
return TaskManager.taskMap.get(agv) return TaskManager.taskMap.get(agv)

52
src/example/example1.js

@ -128,13 +128,13 @@ export default {
dt: { in: [], out: [], center: [], palletWidth: 1, palletDepth: 1.2 } dt: { in: [], out: [], center: [], palletWidth: 1, palletDepth: 1.2 }
}, { }, {
id: 'ptr1', id: 'ptr1',
t: 'ptr', t: 'cl2',
v: true, v: true,
tf: [[0.75, 0.075, 2], [0, 0, 0], [1.5, 1.2, 0.1]], tf: [[0.75, 0.075, 2], [0, 0, 0], [1.5, 1.2, 0.1]],
dt: { in: [], out: [], center: [], ptrWidth: 1.5, ptrDepth: 1.5, ptrHeight: 1.98 } dt: { in: [], out: [], center: [], ptrWidth: 1.5, ptrDepth: 1.5, ptrHeight: 1.98 }
}, { }, {
id: 'ptr2', id: 'ptr2',
t: 'ptr', t: 'cl2',
v: true, v: true,
tf: [[3, 0.075, 2], [0, 0, 0], [1.5, 1.2, 0.1]], tf: [[3, 0.075, 2], [0, 0, 0], [1.5, 1.2, 0.1]],
dt: { in: [], out: [], center: [], ptrWidth: 1.5, ptrDepth: 1.5, ptrHeight: 1.98 } dt: { in: [], out: [], center: [], ptrWidth: 1.5, ptrDepth: 1.5, ptrHeight: 1.98 }
@ -203,7 +203,7 @@ export default {
v: true, v: true,
tf: [[7.1, 0.1, 2.865], [0, 90, 0], [1, 0.01, 1]], tf: [[7.1, 0.1, 2.865], [0, 90, 0], [1, 0.01, 1]],
dt: { in: [], out: [], center: [], storeWidth: 1.1, storeDepth: 1.3 } dt: { in: [], out: [], center: [], storeWidth: 1.1, storeDepth: 1.3 }
}, { id: '1hAaZ1xtvukZowAKeWAcqs', t: 'gstore', v: true, tf: [[7.1, 0.1, 4.35], [0, 90, 0], [1, 0.01, 1]], dt: { in: [], out: [], center: [], storeWidth: 1.1, storeDepth: 1.3 } }, { }, { id: '1hAaZ1xtvukZowAKeWAcqs', t: 'gstore', v: true, tf: [[7.1, 0.1, 4.35], [0, 0, 0], [1, 0.01, 1]], dt: { in: [], out: [], center: [], storeWidth: 1.1, storeDepth: 1.3 } }, {
id: '28GxDYUqDwZc2WsOgMU2wi', id: '28GxDYUqDwZc2WsOgMU2wi',
t: 'gstore', t: 'gstore',
v: true, v: true,
@ -271,7 +271,7 @@ export default {
dt: { in: [], out: [], center: ['0aJ81sOKqm9FYo60AIQmMG'] } dt: { in: [], out: [], center: ['0aJ81sOKqm9FYo60AIQmMG'] }
}, { }, {
id: 'ptr1', id: 'ptr1',
t: 'ptr', t: 'cl2',
v: true, v: true,
tf: [[5.65, 0.1, 2.13], [0, -90, 0], [1, 1, 1]], tf: [[5.65, 0.1, 2.13], [0, -90, 0], [1, 1, 1]],
dt: { in: [], out: [], center: [], ptrWidth: 1.5, ptrDepth: 1.5, ptrHeight: 1.98 } dt: { in: [], out: [], center: [], ptrWidth: 1.5, ptrDepth: 1.5, ptrHeight: 1.98 }
@ -293,54 +293,68 @@ export default {
v: true, v: true,
tf: [[5.655, 0.1, 8.75], [0, 0, 0], [1, 1, 1]], tf: [[5.655, 0.1, 8.75], [0, 0, 0], [1, 1, 1]],
dt: { in: [], out: [], center: [], chargerWidth: 0.53, chargerDepth: 0.275, clxHeight: 0.3 } dt: { in: [], out: [], center: [], chargerWidth: 0.53, chargerDepth: 0.275, clxHeight: 0.3 }
}, { },
id: 'pallet1122', {
t: 'pallet', id: 'pallet1122', // 托盘唯一编码
v: true, t: 'pallet', // 托盘类型
tf: [[1.5, 0.1, 0.63], [0, 0, 0], [1.2, 0.15, 1]], v: true, // 可见
dt: { in: [], out: [], center: [], } tf: [[1.5, 0.1, 0.63], [0, 0, 0], [1.2, 0.15, 1]], // 变换矩阵
}, { dt: {
in: [], out: [], center: [],
storeAt: { // 存储位置
item: '6Vu3dX1V7Si0ISWIiCkoEh', // 库存容器ID
bay: 0, // 列
level: 0, // 层
cell: 0 // 格
}
}
},
{
id: 'pallet1123', id: 'pallet1123',
t: 'pallet', t: 'pallet',
v: true, v: true,
tf: [[7.1, 0.1, 2.865], [0, 0, 0], [1.2, 0.15, 1]], tf: [[7.1, 0.1, 2.865], [0, 0, 0], [1.2, 0.15, 1]],
dt: { in: [], out: [], center: [], } dt: {
}, { in: [], out: [], center: [],
storeAt: { item: '38TYyVWMGLf8OogQMIiSOz', bay: 0, level: 0, cell: 0 }
}
},
{
id: 'pallet1124', id: 'pallet1124',
t: 'pallet', t: 'pallet',
v: true, v: true,
tf: [[4.196, 1.65, 3.95], [0, 0, 0], [1.2, 0.15, 1]], tf: [[4.196, 1.65, 3.95], [0, 0, 0], [1.2, 0.15, 1]],
dt: { in: [], out: [], center: [], } dt: { in: [], out: [], center: [] }
}, { }, {
id: 'pallet1125', id: 'pallet1125',
t: 'pallet', t: 'pallet',
v: true, v: true,
tf: [[4.196, 1.65, 5.225], [0, 0, 0], [1.2, 0.15, 1]], tf: [[4.196, 1.65, 5.225], [0, 0, 0], [1.2, 0.15, 1]],
dt: { in: [], out: [], center: [], } dt: { in: [], out: [], center: [] }
}, { }, {
id: 'pallet1126', id: 'pallet1126',
t: 'pallet', t: 'pallet',
v: true, v: true,
tf: [[4.196, 1.65, 7.775], [0, 0, 0], [1.2, 0.15, 1]], tf: [[4.196, 1.65, 7.775], [0, 0, 0], [1.2, 0.15, 1]],
dt: { in: [], out: [], center: [], } dt: { in: [], out: [], center: [] }
}, { }, {
id: 'pallet1127', id: 'pallet1127',
t: 'pallet', t: 'pallet',
v: true, v: true,
tf: [[4.196, 3.05, 3.95], [0, 0, 0], [1.2, 0.15, 1]], tf: [[4.196, 3.05, 3.95], [0, 0, 0], [1.2, 0.15, 1]],
dt: { in: [], out: [], center: [], } dt: { in: [], out: [], center: [] }
}, { }, {
id: 'pallet1128', id: 'pallet1128',
t: 'pallet', t: 'pallet',
v: true, v: true,
tf: [[4.196, 3.05, 5.225], [0, 0, 0], [1.2, 0.15, 1]], tf: [[4.196, 3.05, 5.225], [0, 0, 0], [1.2, 0.15, 1]],
dt: { in: [], out: [], center: [], } dt: { in: [], out: [], center: [] }
}, { }, {
id: 'pallet1129', id: 'pallet1129',
t: 'pallet', t: 'pallet',
v: true, v: true,
tf: [[4.196, 3.05, 7.775], [0, 0, 0], [1.2, 0.15, 1]], tf: [[4.196, 3.05, 7.775], [0, 0, 0], [1.2, 0.15, 1]],
dt: { in: [], out: [], center: [], } dt: { in: [], out: [], center: [] }
}] }]
}, },
{ {

72
src/modules/cl2/Cl2Entity.ts

@ -0,0 +1,72 @@
import * as THREE from 'three'
import BaseEntity from '@/core/base/BaseItemEntity.ts'
import type Viewport from '@/core/engine/Viewport.ts'
import gsap from 'gsap'
import { nextTick } from 'vue'
/**
* CL2
*/
export default class Cl2Entity extends BaseEntity {
constructor(viewport: Viewport, id: string) {
super(viewport, id)
}
// 装
addLoad(item: string): void {
this.taskQueue.add(this.createTask('LOAD', async () => {
// 实际业务中应包含装载逻辑
this.isCarrying = true
const arm = this.getArmObject()
// 抬高20cm
this.isLoading = true
gsap.to(arm.position, {
y: arm.position.y + 0.2,
duration: 0.5,
ease: 'sine.inOut',
onComplete: () => {
this.isCarrying = true
this.isLoading = false
const mesh = this.pickupItem(item)
const ptrForkMesh = this.getArmObject()
mesh.position.set(0, 0.1, 0)
mesh.rotation.set(0, THREE.MathUtils.degToRad(90), 0)
ptrForkMesh.add(mesh)
}
})
}))
}
// 卸
addUnload(itemId: string, rackId: string, bay?: number, level?: number, cell?: number): void {
this.taskQueue.add(this.createTask('UNLOAD', async () => {
const item = this.viewport.entityManager.findItemById(itemId)
this.isCarrying = false
this.isUnloading = false
this.dropItem(item, rackId, bay, level)
}))
}
// 帮助方法:获取机械臂对象
getArmObject(): THREE.Object3D | undefined {
const agv = this.object as THREE.Group
if (agv.children.length > 1) {
const pillar = agv.children[1]
if (pillar.children.length > 1) {
return pillar.children[1]
}
}
return undefined
}
// 帮助方法:获取机械臂立柱
getArmPillar(): THREE.Object3D | undefined {
const agv = this.object as THREE.Group
if (agv.children.length > 1) {
return agv.children[1]
}
return undefined
}
}

0
src/modules/ptr/PtrInteraction.ts → src/modules/cl2/Cl2Interaction.ts

0
src/modules/ptr/PtrPropertySetter.ts → src/modules/cl2/Cl2PropertySetter.ts

11
src/modules/ptr/PtrRenderer.ts → src/modules/cl2/Cl2Renderer.ts

@ -1,13 +1,7 @@
import * as THREE from 'three' import * as THREE from 'three'
import BaseRenderer from '@/core/base/BaseRenderer.ts' import BaseRenderer from '@/core/base/BaseRenderer.ts'
import { Line2 } from 'three/examples/jsm/lines/Line2.js'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'
import { decimalSumBy } from '@/core/ModelUtils'
import Constract from '@/core/Constract.ts' import Constract from '@/core/Constract.ts'
import ptrUrl from '@/assets/images/ptr/ptr.png' import { CSG } from 'three-csg-ts'
import { CSG } from 'three-csg-ts';
import {BufferGeometry} from "three";
/** /**
* ptr侧叉渲染器 * ptr侧叉渲染器
@ -68,14 +62,17 @@ export default class PtrRenderer extends BaseRenderer {
const ptrPedestalGeometry = this.createPtrPedestal(item, option) const ptrPedestalGeometry = this.createPtrPedestal(item, option)
const ptrPedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffdddbca}); const ptrPedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffdddbca});
const ptrPedestalMesh = new THREE.Mesh(ptrPedestalGeometry, ptrPedestalMaterial); const ptrPedestalMesh = new THREE.Mesh(ptrPedestalGeometry, ptrPedestalMaterial);
ptrPedestalMesh.name = 'ptrPedestalMesh'
const ptrPillarGeometry = this.createPtrPillar(item, option); const ptrPillarGeometry = this.createPtrPillar(item, option);
const ptrPillarMaterial = new THREE.MeshPhongMaterial({color: 0xff6c6956}); const ptrPillarMaterial = new THREE.MeshPhongMaterial({color: 0xff6c6956});
const ptrPillarMesh = new THREE.Mesh(ptrPillarGeometry, ptrPillarMaterial); const ptrPillarMesh = new THREE.Mesh(ptrPillarGeometry, ptrPillarMaterial);
ptrPillarMesh.name = 'ptrPillarMesh'
const ptrForkGeometry = this.createPtrFork(item, option); const ptrForkGeometry = this.createPtrFork(item, option);
const ptrForkMaterial = new THREE.MeshPhongMaterial({color: 0xff111111}); const ptrForkMaterial = new THREE.MeshPhongMaterial({color: 0xff111111});
const ptrForkMesh = new THREE.Mesh(ptrForkGeometry, ptrForkMaterial); const ptrForkMesh = new THREE.Mesh(ptrForkGeometry, ptrForkMaterial);
ptrForkMesh.name = 'ptrForkMesh'
group.add(ptrPedestalMesh) group.add(ptrPedestalMesh)
const groupPillar = new THREE.Group() const groupPillar = new THREE.Group()

15
src/modules/cl2/index.ts

@ -0,0 +1,15 @@
import { defineModule } from '@/core/manager/ModuleManager.ts'
import Cl2Renderer from './Cl2Renderer.ts'
import Cl2Entity from './Cl2Entity.ts'
import Cl2Interaction from './Cl2Interaction.ts'
import propertySetter from "./Cl2PropertySetter.ts";
export const ITEM_TYPE_NAME = 'cl2'
export default defineModule({
name: ITEM_TYPE_NAME,
renderer: new Cl2Renderer(ITEM_TYPE_NAME),
interaction: new Cl2Interaction(ITEM_TYPE_NAME),
setter: propertySetter,
entity: Cl2Entity,
})

23
src/modules/gstore/GstoreRenderer.ts

@ -4,6 +4,7 @@ import Constract from '@/core/Constract.ts'
import { type Object3DLike } from '@/types/ModelTypes.ts' import { type Object3DLike } from '@/types/ModelTypes.ts'
import { getMatrixFromTf } from '@/core/ModelUtils.ts' import { getMatrixFromTf } from '@/core/ModelUtils.ts'
import InstanceMeshManager from '@/core/manager/InstanceMeshManager.ts' import InstanceMeshManager from '@/core/manager/InstanceMeshManager.ts'
import type Viewport from '@/core/engine/Viewport.ts'
/** /**
* *
@ -163,4 +164,26 @@ export default class GstoreRenderer extends BaseRenderer {
}) })
} }
/**
*
* Position Rotation
*/
getStorePlacement(viewport: Viewport, item: ItemJson): { position: THREE.Vector3, rotation: THREE.Quaternion } {
if (!item.dt?.storeAt?.item) {
// 没有定义存储位置,返回空对象
//@ts-ignore
return {}
}
// 将这个物品添加到库存中
const me = viewport.entityManager.findItemById(item.dt.storeAt.item)
const position = new THREE.Vector3(...me.tf[0])
const rotation = new THREE.Quaternion().setFromEuler(new THREE.Euler(
THREE.MathUtils.degToRad(me.tf[1][0]),
THREE.MathUtils.degToRad(me.tf[1][1]),
THREE.MathUtils.degToRad(me.tf[1][2]),
'XYZ'
))
return { position, rotation }
}
} }

5
src/modules/ptr/PtrEntity.ts

@ -1,5 +0,0 @@
import BaseEntity from '@/core/base/BaseItemEntity.ts'
export default class PtrEntity extends BaseEntity {
}

15
src/modules/ptr/index.ts

@ -1,15 +0,0 @@
import { defineModule } from '@/core/manager/ModuleManager.ts'
import PtrRenderer from './PtrRenderer.ts'
import PtrEntity from './PtrEntity.ts'
import PtrInteraction from './PtrInteraction.ts'
import propertySetter from "@/modules/ptr/PtrPropertySetter.ts";
export const ITEM_TYPE_NAME = 'ptr'
export default defineModule({
name: ITEM_TYPE_NAME,
renderer: new PtrRenderer(ITEM_TYPE_NAME),
interaction: new PtrInteraction(ITEM_TYPE_NAME),
setter: propertySetter,
entity: PtrEntity,
})

10
src/types/ScriptSupport.d.ts

@ -21,6 +21,16 @@ declare interface Model {
createTask(agv: object): TaskManagerIF createTask(agv: object): TaskManagerIF
/** /**
* ID获取 CL2
*/
getCl2(id: string): Cl2If
/**
* ID获取 CLX
*/
getClx(id: string): ClxIf
/**
* *
*/ */
selectedObject: any selectedObject: any

178
src/types/model.d.ts

@ -39,7 +39,12 @@ interface InteractionCudOption {
* *
*/ */
interface RendererCudOption { interface RendererCudOption {
createFromInteraction: boolean createFromInteraction?: boolean
isRuntime?: boolean
//THREE.Vector3
position?: any
//THREE.Quaternion
rotation?: any
} }
/** /**
@ -213,6 +218,16 @@ interface ItemJson extends ItemMetrix {
protected?: boolean protected?: boolean
/** /**
*
*/
storeAt?: {
item: string, // 货架(地堆货位)ID
bay?: number, // 货架(地堆货位)的列
level?: number // 货架(地堆货位)的层
cell?: number // 货架(地堆货位)的格
}
/**
* , * ,
*/ */
[key: string]: any [key: string]: any
@ -266,3 +281,164 @@ interface UserData {
[key: string]: any [key: string]: any
} }
/**
*
*/
type Object3DIF = any
/**
*
*/
interface Loadable {
/**
*
*/
pickupItem(item: string)
/**
*
*/
dropItem(item: ItemJson, storeItemId: string, bay?: number, level?: number, cell?: number): void
}
/**
*
*/
interface Carry {
/**
*
*/
isCarrying: boolean
/**
*
*/
isLoading: boolean
/**
*
*/
isUnloading: boolean
/**
*
*/
addLoad(item: string): void
/**
*
*/
addUnload(item: string, rack: string, bay?: number, level?: number): void
}
/**
*
*/
interface Walk {
isWalking: boolean
/**
*
*/
addTravel(pos: Vector3IF | string): void
/**
*
*/
addRotation(angle: number): void
}
/**
*
*/
interface ForkArm {
isArmExtended: boolean
/**
*
*/
addArmExtender(): void
/**
*
*/
addArmRetractor(): void
}
/**
*
*/
interface LiftingArm {
isArmRaised: boolean
/**
*
*/
addArmRaise(hight: number): void
/**
*
*/
addArmLower(): void
}
interface EntityIf {
id: string
t: string
item: ItemJson
object: Object3DIF
/**
*
*/
cancelTask(): void
/**
*
*/
taskPause(): void
/**
*
*/
taskResume(): void
/**
*
*/
taskStartRun(): void
/**
*
*/
isPaused: boolean
/**
*
*/
taskCount: number
/**
*
*/
taskIsRunning: boolean
}
/**
* CL2
*/
interface Cl2If extends EntityIf, Carry, Walk, ForkArm, LiftingArm {
}
/**
* CLX
*/
interface ClxIf extends EntityIf, Carry, Walk, ForkArm, LiftingArm {
}

Loading…
Cancel
Save