From 498189000b6925b8e436de8ffaf470051a3d58ed Mon Sep 17 00:00:00 2001 From: yuliang <398780299@qq.com> Date: Thu, 3 Jul 2025 17:19:12 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E7=BB=93=E6=9E=84=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/manager/TaskManager.ts | 2 +- src/core/manager/WorldModel.ts | 4 +- src/core/manager/amr/AmrMessageManager.ts | 2 +- src/core/script/ModelManager.ts | 4 +- src/modules/amr/ptr/cl2/Cl23DGraphics.ts | 295 +++++++++ src/modules/amr/ptr/cl2/Cl23dObject.ts | 903 +++++++++++++++++++++++++++ src/modules/amr/ptr/cl2/Cl2Entity.ts | 120 ++++ src/modules/amr/ptr/cl2/Cl2Interaction.ts | 22 + src/modules/amr/ptr/cl2/Cl2PropertySetter.ts | 24 + src/modules/amr/ptr/cl2/Cl2Renderer.ts | 77 +++ src/modules/amr/ptr/cl2/index.ts | 12 + src/modules/amr/ptr/clx/Clx3dObject.ts | 667 ++++++++++++++++++++ src/modules/amr/ptr/clx/ClxEntity.ts | 84 +++ src/modules/amr/ptr/clx/ClxInteraction.ts | 22 + src/modules/amr/ptr/clx/ClxPropertySetter.ts | 20 + src/modules/amr/ptr/clx/ClxRenderer.ts | 71 +++ src/modules/amr/ptr/clx/index.ts | 12 + src/modules/cl2/Cl23DGraphics.ts | 295 --------- src/modules/cl2/Cl23dObject.ts | 903 --------------------------- src/modules/cl2/Cl2Entity.ts | 195 ------ src/modules/cl2/Cl2Interaction.ts | 22 - src/modules/cl2/Cl2PropertySetter.ts | 24 - src/modules/cl2/Cl2Renderer.ts | 77 --- src/modules/cl2/index.ts | 12 - src/modules/clx/Clx3dObject.ts | 667 -------------------- src/modules/clx/ClxEntity.ts | 84 --- src/modules/clx/ClxInteraction.ts | 22 - src/modules/clx/ClxPropertySetter.ts | 20 - src/modules/clx/ClxRenderer.ts | 71 --- src/modules/clx/index.ts | 13 - 30 files changed, 2335 insertions(+), 2411 deletions(-) create mode 100644 src/modules/amr/ptr/cl2/Cl23DGraphics.ts create mode 100644 src/modules/amr/ptr/cl2/Cl23dObject.ts create mode 100644 src/modules/amr/ptr/cl2/Cl2Entity.ts create mode 100644 src/modules/amr/ptr/cl2/Cl2Interaction.ts create mode 100644 src/modules/amr/ptr/cl2/Cl2PropertySetter.ts create mode 100644 src/modules/amr/ptr/cl2/Cl2Renderer.ts create mode 100644 src/modules/amr/ptr/cl2/index.ts create mode 100644 src/modules/amr/ptr/clx/Clx3dObject.ts create mode 100644 src/modules/amr/ptr/clx/ClxEntity.ts create mode 100644 src/modules/amr/ptr/clx/ClxInteraction.ts create mode 100644 src/modules/amr/ptr/clx/ClxPropertySetter.ts create mode 100644 src/modules/amr/ptr/clx/ClxRenderer.ts create mode 100644 src/modules/amr/ptr/clx/index.ts delete mode 100644 src/modules/cl2/Cl23DGraphics.ts delete mode 100644 src/modules/cl2/Cl23dObject.ts delete mode 100644 src/modules/cl2/Cl2Entity.ts delete mode 100644 src/modules/cl2/Cl2Interaction.ts delete mode 100644 src/modules/cl2/Cl2PropertySetter.ts delete mode 100644 src/modules/cl2/Cl2Renderer.ts delete mode 100644 src/modules/cl2/index.ts delete mode 100644 src/modules/clx/Clx3dObject.ts delete mode 100644 src/modules/clx/ClxEntity.ts delete mode 100644 src/modules/clx/ClxInteraction.ts delete mode 100644 src/modules/clx/ClxPropertySetter.ts delete mode 100644 src/modules/clx/ClxRenderer.ts delete mode 100644 src/modules/clx/index.ts diff --git a/src/core/manager/TaskManager.ts b/src/core/manager/TaskManager.ts index 4a9eae6..7a664db 100644 --- a/src/core/manager/TaskManager.ts +++ b/src/core/manager/TaskManager.ts @@ -1,7 +1,7 @@ import * as THREE from 'three' import gsap from 'gsap' import Viewport from '@/core/engine/Viewport' -import Clx3dObject from "@/modules/clx/Clx3dObject"; +import Clx3dObject from "@/modules/amr/ptr/clx/Clx3dObject"; export default class TaskManager implements TaskManagerIF { diff --git a/src/core/manager/WorldModel.ts b/src/core/manager/WorldModel.ts index 2a520ca..6e5430c 100644 --- a/src/core/manager/WorldModel.ts +++ b/src/core/manager/WorldModel.ts @@ -108,8 +108,8 @@ export default class WorldModel { import('../../modules/pallet'), import('../../modules/tote'), import('../../modules/carton'), - import('../../modules/cl2'), - import('../../modules/clx'), + import('../../modules/amr/ptr/cl2'), + import('../../modules/amr/ptr/clx'), import('../../modules/charger') ]).then(() => { diff --git a/src/core/manager/amr/AmrMessageManager.ts b/src/core/manager/amr/AmrMessageManager.ts index 8328bd6..5d3e73d 100644 --- a/src/core/manager/amr/AmrMessageManager.ts +++ b/src/core/manager/amr/AmrMessageManager.ts @@ -1,5 +1,5 @@ import { AmrMsg, AmrMsg10010, AmrMsg10050, AmrMsg10060, AmrMsg10110, AmrMsg10120, AmrMsg20050 } from '@/core/manager/amr/AmrMessageDefine' -import Cl23dObject from '@/modules/cl2/Cl23dObject' +import Cl23dObject from '@/modules/amr/ptr/cl2/Cl23dObject' import Viewport from '@/core/engine/Viewport' export default class AmrMessageManager { diff --git a/src/core/script/ModelManager.ts b/src/core/script/ModelManager.ts index 85a54f2..e567b82 100644 --- a/src/core/script/ModelManager.ts +++ b/src/core/script/ModelManager.ts @@ -4,8 +4,8 @@ import * as THREE from 'three' import { getMatrixFromTf } from '@/core/ModelUtils.ts' import type { Object3DLike } from '@/types/ModelTypes.ts' import TaskManager from '../manager/TaskManager.ts' -import Cl2Entity from '@/modules/cl2/Cl2Entity.ts' -import ClxEntity from '@/modules/clx/ClxEntity.ts' +import Cl2Entity from '@/modules/amr/ptr/cl2/Cl2Entity.ts' +import ClxEntity from '@/modules/amr/ptr/clx/ClxEntity.ts' import { getRenderer } from '@/core/manager/ModuleManager.ts' export default class ModelManager implements IControls, Model { diff --git a/src/modules/amr/ptr/cl2/Cl23DGraphics.ts b/src/modules/amr/ptr/cl2/Cl23DGraphics.ts new file mode 100644 index 0000000..2f6a9dc --- /dev/null +++ b/src/modules/amr/ptr/cl2/Cl23DGraphics.ts @@ -0,0 +1,295 @@ +import * as THREE from "three"; +import {CSG} from "three-csg-ts"; +//@ts-ignore +import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js' + +export default class Cl23DGraphics { + +// 创建ptr的底座 + public static createPtrPedestal(): THREE.BufferGeometry { + // 参数配置 + const radius = 0.8 // 圆半径 + const lineDist = 0.75 // 切割线距离圆心距离 + const segments = 64 // 圆的分段精度 + + // 计算切割线与圆的交点 + const intersectY = Math.sqrt(radius * radius - lineDist * lineDist) + const startAngle = Math.asin(intersectY / radius) + const endAngle = Math.acos(intersectY / radius) + + + const shape = new THREE.Shape() + shape.moveTo(0, 0) // 起点在圆心 + shape.absarc(0, 0, 0.8, startAngle, endAngle, false) // 从0到π绘制半圆 + shape.absarc(0, 0, 0.8, startAngle + Math.PI / 2, endAngle + Math.PI / 2, false) + shape.absarc(0, 0, 0.8, startAngle + Math.PI, endAngle + Math.PI, false) + shape.absarc(0, 0, 0.8, startAngle + Math.PI / 2 * 3, endAngle + Math.PI / 2 * 3, false) + + shape.lineTo(-0.5, -intersectY) + shape.lineTo(-0.5, -intersectY + 0.17) + shape.lineTo(0.75, -intersectY + 0.17) + shape.lineTo(0.75, intersectY - 0.17) + shape.lineTo(-0.5, intersectY - 0.17) + shape.lineTo(-0.5, intersectY) + shape.closePath() // 闭合路径 + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 0.22, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelEnabled: false, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometry = new THREE.ExtrudeGeometry(shape, options) + const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) + const mesh = new THREE.Mesh(geometry, material) + + mesh.updateMatrix() + + // 倒角 + const shapeD = new THREE.Shape() + shapeD.moveTo(-0.02, -0.02) + shapeD.lineTo(0.02, 0.02) + shapeD.lineTo(-0.02, 0.02) + shapeD.closePath() + + const curveD = new THREE.EllipseCurve( + 0, 0, radius, radius, 0, Math.PI * 2, false, 0 + ) + // 生成拉伸路径点 + const pointsD = curveD.getPoints(segments).map(p => + new THREE.Vector3(p.x, 0.20, p.y) + ) + // 3. 挤出成型 + const optionsD = { + steps: segments, + bevelEnabled: false, + // bevelSegments: 0.01, + extrudePath: new THREE.CatmullRomCurve3(pointsD) + } + + const geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) + const meshD = new THREE.Mesh(geometryD, material) + meshD.updateMatrix() + + // 布尔运算 + const result = CSG.subtract(mesh, meshD) + + return result.geometry + } + +// 创建ptr的立柱 + public static createPtrPillar(): THREE.BufferGeometry { + // 606.5 + const width = 0.245 + const depth = 0.716 + const dd = 0.05 + const shape = new THREE.Shape() + shape.moveTo(-0.728, -0.358 + dd) + shape.lineTo(-0.728 + dd, -0.358) + shape.lineTo(-0.728 + width - dd, -0.358) + shape.lineTo(-0.728 + width, -0.358 + dd) + + shape.lineTo(-0.728 + width, -0.28) + shape.lineTo(-0.728 + width - 0.08, -0.28) + shape.lineTo(-0.728 + width - 0.08, 0.28) + shape.lineTo(-0.728 + width, 0.28) + + shape.lineTo(-0.728 + width, -0.358 + depth - dd) + shape.lineTo(-0.728 + width - dd, -0.358 + depth) + shape.lineTo(-0.728 + dd, -0.358 + depth) + shape.lineTo(-0.728, -0.358 + depth - dd) + // shape.lineTo(-0.728, -0.358 + dd); + // shape.closePath() + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.22, 0), new THREE.Vector3(0, 1.872, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelSegments: 0.05, + bevelEnabled: true, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + let geometry = new THREE.ExtrudeGeometry(shape, options) + + + const fd = 0.03 + const shapeBf = new THREE.Shape() + + shapeBf.moveTo(-0.728 + width - 0.08, -0.28) + shapeBf.lineTo(-0.728 + width + 0.02, -0.28) + shapeBf.lineTo(-0.728 + width + 0.02, -0.25) + shapeBf.lineTo(-0.728 + width + 0.02 + 1.130 - fd, -0.25) + shapeBf.lineTo(-0.728 + width + 0.02 + 1.130, -0.25 + fd) + shapeBf.lineTo(-0.728 + width + 0.02 + 1.130, -0.14 - fd) + shapeBf.lineTo(-0.728 + width + 0.02 + 1.130 - fd, -0.14) + shapeBf.lineTo(-0.728 + width + 0.02, -0.14) + shapeBf.lineTo(-0.728 + width + 0.02, 0.14) + shapeBf.lineTo(-0.728 + width + 0.02 + 1.130 - fd, 0.14) + shapeBf.lineTo(-0.728 + width + 0.02 + 1.130, 0.14 + fd) + shapeBf.lineTo(-0.728 + width + 0.02 + 1.130, 0.25 - fd) + shapeBf.lineTo(-0.728 + width + 0.02 + 1.130 - fd, 0.25) + shapeBf.lineTo(-0.728 + width + 0.02, 0.25) + shapeBf.lineTo(-0.728 + width + 0.02, 0.28) + shapeBf.lineTo(-0.728 + width - 0.08, 0.28) + + shapeBf.closePath() + + // 拉伸轨迹线 + const curveBf = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 0.06, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const optionsBf = { + steps: 1, + bevelSegments: 0.05, + bevelEnabled: true, + extrudePath: curveBf // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometryBf = new THREE.ExtrudeGeometry(shapeBf, optionsBf) + geometry = mergeGeometries([geometry, geometryBf]) + const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) + const mesh = new THREE.Mesh(geometry, material) + + const shapeD = new THREE.Shape() + shapeD.moveTo(-0.3, -0.25) + shapeD.lineTo(-0.3, 0.25) + shapeD.lineTo(0.3, 0.25) + shapeD.lineTo(0.3, -0.25) + shapeD.closePath() + + const curveD = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 1.45, -1), new THREE.Vector3(0, 1.45, 1)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + const optionsD = { + steps: 1, + bevelSegments: 0.05, + bevelEnabled: true, + extrudePath: curveD // 设置挤出轨迹 + } + + const geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) + const meshD = new THREE.Mesh(geometryD, material) + mesh.updateMatrix() + meshD.updateMatrix() + const result = CSG.subtract(mesh, meshD) + + return result.geometry + + } + +// 创建ptr的立柱 + public static createPtrFork(): THREE.BufferGeometry { + // 606.5 + const width = 0.245 + const depth = 0.716 + const dd = 0.05 + const fd = 0.03 + const shape = new THREE.Shape() + + shape.moveTo(-0.728 + width - 0.08, -0.28) + shape.lineTo(-0.728 + width + 0.02, -0.28) + shape.lineTo(-0.728 + width + 0.02, -0.27) + shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, -0.27) + shape.lineTo(-0.728 + width + 0.02 + 1.130, -0.27 + fd) + shape.lineTo(-0.728 + width + 0.02 + 1.130, -0.12 - fd) + shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, -0.12) + shape.lineTo(-0.728 + width + 0.02, -0.12) + shape.lineTo(-0.728 + width + 0.02, 0.12) + shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, 0.12) + shape.lineTo(-0.728 + width + 0.02 + 1.130, 0.12 + fd) + shape.lineTo(-0.728 + width + 0.02 + 1.130, 0.27 - fd) + shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, 0.27) + shape.lineTo(-0.728 + width + 0.02, 0.27) + shape.lineTo(-0.728 + width + 0.02, 0.28) + shape.lineTo(-0.728 + width - 0.08, 0.28) + + shape.closePath() + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 0.482, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelSegments: 0.05, + bevelEnabled: true, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometry = new THREE.ExtrudeGeometry(shape, options) + const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) + const mesh = new THREE.Mesh(geometry, material) + mesh.updateMatrix() + + const shapeD = new THREE.Shape() + + + shapeD.moveTo(-0.728 + width + 0.02, -0.3) + shapeD.lineTo(-0.728 + width + 0.02 + 1.2, -0.3) + shapeD.lineTo(-0.728 + width + 0.02 + 1.2, 0.3) + shapeD.lineTo(-0.728 + width + 0.02, 0.3) + shape.closePath() + + // 拉伸轨迹线 + const curveD = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.07, 0), new THREE.Vector3(0, 0.482, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const optionsD = { + steps: 1, + bevelSegments: 0.05, + bevelEnabled: true, + extrudePath: curveD // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) + const meshD = new THREE.Mesh(geometryD, material) + + meshD.updateMatrix() + + // 布尔运算 + const result = CSG.subtract(mesh, meshD) + + return result.geometry + } + + public static ptrPedestalGeometry: THREE.BufferGeometry = null + public static ptrPillarGeometry: THREE.BufferGeometry = null + public static ptrForkGeometry: THREE.BufferGeometry = null + +} diff --git a/src/modules/amr/ptr/cl2/Cl23dObject.ts b/src/modules/amr/ptr/cl2/Cl23dObject.ts new file mode 100644 index 0000000..a4f6184 --- /dev/null +++ b/src/modules/amr/ptr/cl2/Cl23dObject.ts @@ -0,0 +1,903 @@ +import * as THREE from 'three' +import {CSG} from 'three-csg-ts' +import gsap from 'gsap' +import mqtt from 'mqtt' +import {Euler} from 'three/src/math/Euler' +import Cl2Entity from '@/modules/amr/ptr/cl2/Cl2Entity' +import Cl23DGraphics from "@/modules/amr/ptr/cl2/Cl23DGraphics" +import { + AmrErrorCode, + AmrMsg, + AmrMsg10010, + AmrMsg10050, + AmrMsg10060, + AmrMsg10110, AmrMsg10120, AmrMsg20011, + AmrMsg20020, + AmrMsg20050, + AmrMsg20060, + AmrMsg20100, AmrMsg20147, AmrMsg20148, + AmrMsg20149, + AmrMsg20150, + AmrMsg20250, + CurBatteryData, + type CEventId, type COperationType, type CTaskMode, type CPickMode, + type LogicDirection, TaskCompletedData, TaskModeChangeData, TaskStatusChangeData, TaskTypeChangeData, AmrMsg20010 +} from "@/core/manager/amr/AmrMessageDefine"; +import {worldModel} from "@/core/manager/WorldModel"; + + +type CStepTaskType = "MOVE" | "MOVE_BACKWARD" | "ROTATION" | "LOAD" | "UNLOAD" | "CHARGE" + +interface StepTask { + SeqNo: number; + StepTaskType: CStepTaskType; + OperationType: 0 | 1 | 2 | 3 | 4 | 5 | 135 | 136; + PickMode: 0 | 1 | 2 | 3 | 4 | 5 | 6; + X: number; + Y: number; + Speed: number; + EndDirection: 0 | 1 | 2 | 3 | 15; + ChargeLocation: number; + GoodsSlotHeight: number; + position: THREE.Vector3; + isCompleted: boolean; +} + +export default class Cl23dObject extends THREE.Object3D { + + private item: ItemJson + + private _cl2Entity: Cl2Entity = null + + private currentStepTaskList: StepTask[] = [] + private runningStepTask: StepTask = null + private runningStepTaskList: StepTask[] = [] + private travelAnimation: core.Tween = null + + private currentLogicX: number = -1 + private currentLogicY: number = -1 + private currentDirection: LogicDirection = 15 + private sendMessageQueue: AmrMsg[] = [] + + public Battery: number = 100 + + private __TaskMode: CTaskMode = 0 + private __OperationType: COperationType = 0 + private __TaskStatus: CEventId = 0 + private __PickMode: CPickMode = 0 + + get TaskMode(): CTaskMode { + return this.__TaskMode + } + + set TaskMode(value: CTaskMode) { + if (this.__TaskMode != value) { + const info = new TaskModeChangeData(this.__TaskMode, value) + const msg = new AmrMsg20011(this.vehicleId, info) + msg.TaskMode = value + this.send20011(msg) + } + this.__TaskMode = value + } + + get OperationType(): COperationType { + return this.__OperationType + } + + set OperationType(value: COperationType) { + if (this.__OperationType != value) { + const info = new TaskTypeChangeData(this.__OperationType, value) + const msg = new AmrMsg20011(this.vehicleId, info) + msg.TaskMode = this.TaskMode + this.send20011(msg) + } + this.__OperationType = value + } + + get TaskStatus(): CEventId { + return this.__TaskStatus + } + + set TaskStatus(value: CEventId) { + if (this.__TaskStatus != value) { + + if (value != 1 && value != 4 && value != 8) { + const info = new TaskStatusChangeData(this.OperationType) + const msg = new AmrMsg20011(this.vehicleId, info) + msg.TaskMode = this.TaskMode + msg.EventId = value + this.send20011(msg) + } else if (value == 4) { + + const info = new TaskCompletedData(this.OperationType) + info.Battery = this.Battery + info.OperationResult = 0 + info.CurLogicX = this.currentLogicX + info.CurLogicY = this.currentLogicY + info.CurX = this.currentLogicX + info.CurY = this.currentLogicY + info.CurDirection = this.currentDirection + info.CurOrientation = THREE.MathUtils.radToDeg(this.rotation.y) + const msg = new AmrMsg20011(this.vehicleId, info) + msg.TaskMode = this.TaskMode + this.send20011(msg) + } + + } + } + + get PickMode(): CPickMode { + return this.__PickMode + } + set PickMode(value: CPickMode) { + if (this.__PickMode !== value) { + this.__PickMode = value + } + } + + private bootTime: number = 0 + + // 心跳间隔 UInt32 单位: s + private heartBeatInterval: number = 0 + // 小车所有上报消息重试间隔(未收到应答消息时重发消息) UInt32 单位: s + private mqRetryInterval: number = 3 + + public get cl2Entity(): Cl2Entity { + if (!this._cl2Entity) { + const cl2: Cl2Entity = Model.getCl2(this.item.id) as Cl2Entity + this._cl2Entity = cl2 + } + return this._cl2Entity + } + + public vehicleId: number + + private clock = new THREE.Clock() + + constructor(item: ItemJson, option?: RendererCudOption) { + super() + this.item = item + if (!Cl23DGraphics.ptrPedestalGeometry) { + Cl23DGraphics.ptrPedestalGeometry = Cl23DGraphics.createPtrPedestal() + } + const ptrPedestalGeometry = Cl23DGraphics.ptrPedestalGeometry + const ptrPedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffdddbca}) + const ptrPedestalMesh = new THREE.Mesh(ptrPedestalGeometry, ptrPedestalMaterial) + ptrPedestalMesh.name = 'ptrPedestal' + + if (!Cl23DGraphics.ptrPillarGeometry) { + Cl23DGraphics.ptrPillarGeometry = Cl23DGraphics.createPtrPillar() + } + const ptrPillarGeometry = Cl23DGraphics.ptrPillarGeometry + const ptrPillarMaterial = new THREE.MeshPhongMaterial({color: 0xff6c6956}) + const ptrPillarMesh = new THREE.Mesh(ptrPillarGeometry, ptrPillarMaterial) + + + if (!Cl23DGraphics.ptrForkGeometry) { + Cl23DGraphics.ptrForkGeometry = Cl23DGraphics.createPtrFork() + } + const ptrForkGeometry = Cl23DGraphics.ptrForkGeometry + const ptrForkMaterial = new THREE.MeshPhongMaterial({color: 0xff444444}) + const ptrForkMesh = new THREE.Mesh(ptrForkGeometry, ptrForkMaterial) + ptrForkMesh.name = 'ptrFork' + + this.add(ptrPedestalMesh) + const groupPillar = new THREE.Group() + groupPillar.name = 'ptrPillar' + groupPillar.add(ptrPillarMesh) + groupPillar.add(ptrForkMesh) + this.add(groupPillar) + + this.vehicleId = parseInt(this.cl2Entity.id) + this.cl2Entity.viewport.addFrameTimerCallback(this.cl2Entity.id, this.onFrameTimer.bind(this)) + if (!worldModel.state.runState.isVirtual) { + this.subscribeMessage('/wcs_server/' + this.cl2Entity.id) + this.subscribeMessage('/agv_robot/status') + } + } + + private AGVModel = "CYBER-LIFT-A_V1.0" + private AGVFnModel = "FITBOTS-CYBER-LIFT-1000_V1.0" + + + private heartBeatTimeCount: number = 0 + private mqRetryTimeCount: number = 0 + + private onFrameTimer() { + if (!worldModel.state.runState.isVirtual) { + return + } + const delta = this.clock.getDelta() + this.mqRetryTimeCount += delta + if (this.mqRetryInterval > 0 && this.mqRetryTimeCount >= this.mqRetryInterval) { + this.mqRetryTimeCount = 0 + // 在此处理消息重试 + if (this.sendMessageQueue.length > 0) { + this.sendMessage(this.sendMessageQueue[0]) + } + } + this.heartBeatTimeCount += delta + if (this.heartBeatInterval > 0 && this.heartBeatTimeCount >= this.heartBeatInterval) { + this.heartBeatTimeCount = 0 + // 在此处发送心跳报文 + this.sendHeartBeat() + } + } + + + // 开机 + boot() { + this.bootTime = Date.now(); + this.computeLogicXYAndDirection(); + + if (!worldModel.state.runState.isVirtual) { + return + } + this.subscribeMessage('/wcs_server/' + this.cl2Entity.id) + this.send20147() + setTimeout(() => { + this.send20149() + this.TaskMode = 1 + // 检查当前所在位置和方向 根据车当前所在的xz坐标获取地标 + setTimeout(() => { + this.sendCurrentPositionAndDirection() + setTimeout(() => { + this.send20150() + }, 1000) + }, 1000) + }, 2000) + + } + + // 关机 + shutdown() { + if (!worldModel.state.runState.isVirtual) { + return + } + const content = new AmrMsg20148(this.vehicleId) + // 电量 + content.Battery = 100 + content.CreateMonoTime = Date.now() - this.bootTime + content.Uptime = content.CreateMonoTime + const m20148 = new AmrMsg(content) + this.sendMessage(m20148) + } + + // 开机上报 + send20147() { + const content = new AmrMsg20147(this.vehicleId) + content.AGVModel = this.AGVModel + content.AGVFnModel = this.AGVFnModel + // 电量 + content.Battery = 100 + content.CreateMonoTime = Date.now() - this.bootTime + const m20147 = new AmrMsg(content) + this.sendMessage(m20147) + } + + // 主程序启动上报 + send20149() { + const content = new AmrMsg20149(this.vehicleId) + content.AGVModel = this.AGVModel + content.AGVFnModel = this.AGVFnModel + // 电量 + content.Battery = 100 + content.CreateMonoTime = Date.now() - this.bootTime + const m20149 = new AmrMsg(content) + this.sendMessage(m20149) + } + + // 上线上报 + send20150() { + const content = new AmrMsg20150(this.vehicleId) + content.AGVModel = this.AGVModel + content.AGVFnModel = this.AGVFnModel + content.Battery = 100 + content.CreateMonoTime = Date.now() - this.bootTime + const m20150 = new AmrMsg(content) + this.sendMessage(m20150) + } + + // 上报当前位姿,地标和方向 + sendCurrentPositionAndDirection() { + if (this.currentLogicX <= 0 || this.currentLogicY <= 0) { + // 当前车辆所在位置未找到 + const content = new AmrMsg20250(this.vehicleId) + content.Duration = 0 + content.ErrCode = 5 + content.ErrCodeName = AmrErrorCode[5].ErrCodeName + content.ErrEvtType = 1 + content.ErrLevel = 14 + content.ErrLifecycle = 2 + content.CreateMonoTime = Date.now() - this.bootTime + const m20250 = new AmrMsg(content) + this.sendMessage(m20250) + } else { + // 发送正常地标信息 + const content = new AmrMsg20020(this.vehicleId) + content.CurDirection = this.currentDirection + content.CurLogicX = this.currentLogicX + content.CurLogicY = this.currentLogicY + content.CurX = this.currentLogicX + content.CurY = this.currentLogicY + content.CreateMonoTime = Date.now() - this.bootTime + const m20020 = new AmrMsg(content) + this.sendMessage(m20020) + } + } + + send20010() { + const content: AmrMsg20010 = new AmrMsg20010(this.vehicleId) + content.CurX = this.currentLogicX + content.CurY = this.currentLogicY + content.CurDirection = this.currentDirection + content.OperationType = this.OperationType + content.Battery = this.Battery + content.OperationResult = 0 + content.Summary = { + ActuatorsData: [ + { + MechNo: 1, + Name: "Mech1", + PickMode: this.PickMode + } + ] + } + + + const m20010 = new AmrMsg(content) + this.sendMessage(m20010) + } + + send20011(content: AmrMsg20011) { + const m20011 = new AmrMsg>(content) + this.sendMessage(m20011) + } + + send20020(content: AmrMsg20020) { + const m20020 = new AmrMsg(content) + this.sendMessage(m20020) + } + + send20060() { + const content = new AmrMsg20060(this.vehicleId) + content.CreateMonoTime = Date.now() - this.bootTime + content.CurBattery = new CurBatteryData() + content.CurLogicX = this.currentLogicX + content.CurLogicY = this.currentLogicY + content.CurOrientation = THREE.MathUtils.radToDeg(this.rotation.y); + content.CurX = this.position.x; + content.CurY = this.position.z; + content.X = this.position.x; + content.Y = this.position.z; + const m20060 = new AmrMsg(content) + this.sendMessage(m20060) + } + + send20250(content: AmrMsg20250) { + this.sendMessage(new AmrMsg(content)) + } + + subscribeMessage(topic: string) { + worldModel.envManager.client.subscribe(topic, {qos: 0}) + } + + sendMessage(msg: AmrMsg) { + if (!worldModel.state.runState.isVirtual) { + return + } + console.log('send message:', JSON.stringify(msg)) + if (this.sendMessageQueue.indexOf(msg) < 0) { + this.sendMessageQueue.push(msg) + } + if (this.sendMessageQueue.length <= 0) { + this.mqRetryTimeCount = 0 + } + worldModel.envManager.client.publish('/agv_robot/status', JSON.stringify(msg)) + this.heartBeatTimeCount = 0 + } + + sendHeartBeat() { + if (!worldModel.state.runState.isVirtual) { + return + } + const content = new AmrMsg20100(this.vehicleId) + content.Temperature = {Battery: this.Battery} + const m20100 = new AmrMsg(content) + worldModel.envManager.client.publish('/agv_robot/status', JSON.stringify(m20100)) + } + + sendAck(seqNo: number, vehicleId: number) { + if (!worldModel.state.runState.isVirtual) { + return + } + const msg20050 = new AmrMsg20050(seqNo, vehicleId) + const ack = new AmrMsg(msg20050) + this.heartBeatTimeCount = 0 + worldModel.envManager.client.publish('/agv_robot/status', JSON.stringify(ack)) + } + + /*==========RCS消息处理============*/ + // 处理任务 + handle10010Message(data: AmrMsg10010) { + if (this.currentStepTaskList.length > 0) { + if (this.runningStepTask.OperationType == 0 && this.runningStepTask.X == data.StartX && this.runningStepTask.Y == data.StartY) { + // this.currentStepTaskList = [] + this.makeStepTask(data) + this.executeTask() + } else { + // 此处应该有错误处理 + } + } else { + this.makeStepTask(data) + this.executeTask() + } + } + + handle10050Message(data: AmrMsg) { + if (this.sendMessageQueue.length > 0 && data.content.SeqNo === this.sendMessageQueue[0].content.SeqNo) { + this.mqRetryTimeCount = 0 + this.sendMessageQueue.shift() + } + } + + handle10060Message(data: AmrMsg) { + this.mqRetryInterval = data.content.MqRetryTime + this.heartBeatInterval = data.content.HeartBeat + this.TaskMode = 0 + } + + // 处理状态查询 + handle10110Message(data: AmrMsg) { + this.send20060() + } + + // 取消任务 + handle10120Message(data: AmrMsg) { + + } + + /*==========真车消息处理============*/ + + // 计算逻辑方向 + computeLogicXYAndDirection() { + let ra = this.rotation.y + while (ra > Math.PI * 2) { + ra -= Math.PI * 2 + } + while (ra < 0) { + ra += Math.PI * 2 + } + const ddra = Math.PI / 8 + if (ra >= ddra * 7 || ra < ddra) { + this.currentDirection = 0; + + } else if (ra >= ddra && ra < ddra * 3) { + this.currentDirection = 3; + + } else if (ra >= ddra * 3 && ra < ddra * 5) { + this.currentDirection = 2; + + } else if (ra >= ddra * 5 && ra < ddra * 7) { + this.currentDirection = 1; + } else { + this.currentDirection = 15; + } + + const pointItem = Model.getItemByXYZ(this.position.x, this.position.y, this.position.z) + if (!pointItem || !pointItem.logicX || !pointItem.logicY) { + this.currentLogicX = -1; + this.currentLogicY = -1; + } else { + this.currentLogicX = pointItem.logicX; + this.currentLogicY = pointItem.logicY; + } + + } + + makeStepTask(data: AmrMsg10010) { + + let currentStepTask: StepTask = this.runningStepTask + if (currentStepTask == null) { + + currentStepTask = { + SeqNo: 0, + StepTaskType: "MOVE", + OperationType: 0, + PickMode: 0, + X: this.currentLogicX, + Y: this.currentLogicY, + Speed: 1000, + EndDirection: this.currentDirection, + ChargeLocation: 0, + GoodsSlotHeight: 0, + position: this.position, + isCompleted: true + } + } + let endDirection = currentStepTask.EndDirection + + if (data.Link.length > 0) { + for (let i = 0; i < data.Link.length; i++) { + const link = data.Link[i] + if ((currentStepTask.X == link.X && currentStepTask.Y == link.Y) + || (currentStepTask.X != link.X && currentStepTask.Y != link.Y)) { + continue + } else if (currentStepTask.X < link.X) { + if (link.Speed > 0) { + endDirection = 0 + } else { + endDirection = 2 + } + } else if (currentStepTask.X > link.X) { + if (link.Speed > 0) { + endDirection = 2 + } else { + endDirection = 0 + } + } else if (currentStepTask.Y < link.Y) { + if (link.Speed > 0) { + endDirection = 1 + } else { + endDirection = 3 + } + } else if (currentStepTask.Y > link.Y) { + if (link.Speed > 0) { + endDirection = 3 + } else { + endDirection = 1 + } + } + + if (endDirection != currentStepTask.EndDirection) { + const stepTask: StepTask = { + SeqNo: data.SeqNo, + StepTaskType: "ROTATION", + OperationType: 0, + PickMode: 0, + X: link.X, + Y: link.Y, + Speed: link.Speed, + EndDirection: endDirection, + ChargeLocation: data.ChargeLocation, + GoodsSlotHeight: data.GoodsSlotHeight, + position: Model.getPositionByLogicXY(link.X, link.Y) as THREE.Vector3, + isCompleted: false + } + currentStepTask = stepTask + this.currentStepTaskList.push(stepTask) + } + + const stepTask: StepTask = { + SeqNo: data.SeqNo, + StepTaskType: link.Speed > 0 ? "MOVE" : "MOVE_BACKWARD", + OperationType: 0, + PickMode: 0, + X: link.X, + Y: link.Y, + Speed: link.Speed, + EndDirection: endDirection, + ChargeLocation: data.ChargeLocation, + GoodsSlotHeight: data.GoodsSlotHeight, + position: Model.getPositionByLogicXY(link.X, link.Y) as THREE.Vector3, + isCompleted: false + } + currentStepTask = stepTask + this.currentStepTaskList.push(stepTask) + } + } + + + if (data.OperationType == 0 && data.EndDirection >= 0 && data.EndDirection <= 3) { + endDirection = data.EndDirection; + } else if (data.OperationType == 3 && data.ChargeDirection >= 0 && data.ChargeDirection <= 3) { + endDirection = data.ChargeDirection + } else if (data.OperationType == 4 && data.GoodsSlotDirection >= 0 && data.GoodsSlotDirection <= 3) { + if (data.GoodsSlotDirection == 0) { + endDirection = 3 + } else { + endDirection = (data.GoodsSlotDirection - 1) as LogicDirection + } + } + if (endDirection != currentStepTask.EndDirection) { + const stepTask: StepTask = { + SeqNo: data.SeqNo, + StepTaskType: "ROTATION", + OperationType: 0, + PickMode: 0, + X: data.EndX, + Y: data.EndY, + Speed: currentStepTask.Speed, + EndDirection: endDirection, + ChargeLocation: data.ChargeLocation, + GoodsSlotHeight: data.GoodsSlotHeight, + position: Model.getPositionByLogicXY(data.EndX, data.EndY) as THREE.Vector3, + isCompleted: false + } + this.currentStepTaskList.push(stepTask) + } + if (data.OperationType == 3) { + + const stepTask: StepTask = { + SeqNo: data.SeqNo, + StepTaskType: "CHARGE", + OperationType: 3, + PickMode: 0, + X: data.EndX, + Y: data.EndY, + Speed: currentStepTask.Speed, + EndDirection: endDirection, + ChargeLocation: data.ChargeLocation, + GoodsSlotHeight: data.GoodsSlotHeight, + position: Model.getPositionByLogicXY(data.EndX, data.EndY) as THREE.Vector3, + isCompleted: false + } + this.currentStepTaskList.push(stepTask) + + } else if (data.OperationType == 4) { + + const stepTask: StepTask = { + SeqNo: data.SeqNo, + StepTaskType: data.PickMode == 1 ? "LOAD" : "UNLOAD", + OperationType: 4, + PickMode: data.PickMode, + X: data.EndX, + Y: data.EndY, + Speed: currentStepTask.Speed, + EndDirection: endDirection, + ChargeLocation: data.ChargeLocation, + GoodsSlotHeight: data.GoodsSlotHeight, + position: Model.getPositionByLogicXY(data.EndX, data.EndY) as THREE.Vector3, + isCompleted: false + } + this.currentStepTaskList.push(stepTask) + + } else { + + } + + } + + executeTask() { + this.TaskMode = 2 + while (this.currentStepTaskList.length > 0) { + const stepTask = this.currentStepTaskList[0] + if (this.runningStepTask) { + if ((stepTask.StepTaskType == "MOVE" || stepTask.StepTaskType == "MOVE_BACKWARD") + && stepTask.EndDirection == this.runningStepTask.EndDirection + && (stepTask.Speed > 0) == (this.runningStepTask.Speed > 0)) { + this.runningStepTask = stepTask + this.currentStepTaskList.shift() + this.runningStepTaskList.push(stepTask) + + this.addTravel(stepTask.X, stepTask.Y, stepTask.Speed/1000) + + } else { + break + } + } else { + this.runningStepTask = stepTask + this.currentStepTaskList.shift() + this.runningStepTaskList.push(stepTask) + if (stepTask.StepTaskType == "MOVE" || stepTask.StepTaskType == "MOVE_BACKWARD") { + this.addTravel(stepTask.X, stepTask.Y, stepTask.Speed/1000) + } else if (stepTask.StepTaskType == "ROTATION") { + this.addRotation(stepTask.EndDirection) + } else if (stepTask.StepTaskType == "LOAD") { + this.addLoad(stepTask.GoodsSlotHeight/1000) + } else if (stepTask.StepTaskType == "UNLOAD") { + this.addUnload(stepTask.GoodsSlotHeight/1000) + } + } + } + } + + onActionCompleted() { + this.runningStepTaskList = [] + this.runningStepTask = null + this.computeLogicXYAndDirection() + // 当前所有动作执行完毕 + if (this.currentStepTaskList.length <= 0) { + this.send20010() + } + this.PickMode = 0 + this.OperationType = 0 + this.TaskMode = 0 + this.executeTask() + } + + /*==========动画处理============*/ + + // 转 + addRotation(direction: number): Promise { + let rad = 0 + switch (direction) { + case 1: + rad = Math.PI / 2 * 3 + break + case 2: + rad = Math.PI + break + case 3: + rad = Math.PI / 2 + break + default: + rad = 0 + } + const quat1 = new THREE.Quaternion().setFromEuler(this.rotation) + const euler: Euler = new Euler(this.rotation.x, rad, this.rotation.z) + const quat2 = new THREE.Quaternion().setFromEuler(euler) + const angleDiff = quat1.angleTo(quat2) + console.log(rad, this.rotation.y, angleDiff) + const tr = this.rotation.y + angleDiff + let time = Math.abs(angleDiff) / (Math.PI / 7) + const duration = time + + return new Promise(resolve => { + gsap.to(this.rotation, { + y: tr, + duration, + ease: 'none', + onComplete: ()=>{ + resolve() + this.onActionCompleted() + } + }) + }) + } + + // 走 + addTravel(logicX: number, logicY: number, speed: number = 1): Promise { + this.OperationType = 0 + this.PickMode = 0 + const pos = Model.getPositionByLogicXY(logicX, logicY) + + const fromPos = this.position + const toPos = pos as THREE.Vector3 + const distance = fromPos.distanceTo(toPos) + const duration = Math.max(1.0, distance / speed) + + if (!this.travelAnimation) { + return new Promise(resolve => { + this.travelAnimation = gsap.fromTo(this.position, { + x: fromPos.x, + y: fromPos.y, + z: fromPos.z, + }, { + x: toPos.x, + y: toPos.y, + z: toPos.z, + duration, + ease: 'power2.inOut', + onComplete: () => { + this.travelAnimation = null + resolve() + this.onActionCompleted() + }, + onUpdate: () => { + + for (let i = 0; i < this.runningStepTaskList.length; i++) { + const task = this.runningStepTaskList[i] + if (task.isCompleted == false) { + if (this.position.distanceTo(task.position) < 0.1) { + task.isCompleted = true + this.runningStepTaskList.splice(0, i + 1) + const content: AmrMsg20020 = new AmrMsg20020(this.vehicleId) + content.CurLogicX = task.X + content.CurLogicY = task.Y + content.CurX = task.X + content.CurY = task.Y + // content.CurOrientation = task.Orientation + content.CurDirection = task.EndDirection + this.send20020(content) + break + } + } + } + } + }) + }) + } else { + this.travelAnimation.vars.x = toPos.x + this.travelAnimation.vars.y = toPos.y + this.travelAnimation.vars.z = toPos.z + const tt = this.travelAnimation.duration() + this.travelAnimation.duration(tt + duration) + this.travelAnimation.invalidate().restart(); + } + + } + + // 取货 + addLoad(height: number): void { + this.PickMode = 1 + this.OperationType = 4 + this.animationUpFork(height).then( + () => this.animationShowFork(1.4).then( + ()=>this.animationUpFork(height + 0.2).then( + ()=>this.animationHideFork().then( + ()=>this.animationDownFork().then(()=>{ + this.onActionCompleted() + }) + ) + ) + ) + ) + } + // 卸货 + addUnload(height: number): void { + this.PickMode = 2 + this.OperationType = 4 + this.animationUpFork(height + 0.2).then( + () => this.animationShowFork(1.4).then( + ()=>this.animationUpFork(height).then( + ()=>this.animationHideFork().then( + ()=>this.animationDownFork().then(()=>{ + this.onActionCompleted() + }) + ) + ) + ) + ) + } + + animationShowFork(z: number): Promise { + + const ptrPillar = this.getObjectByName('ptrPillar') + const time = 3 + return new Promise(resolve => { + gsap.to(ptrPillar.position, { + z: -z, + duration: time, + repeat: 0, + ease: 'sine.inOut', + onComplete: resolve + }) + }) + + } + + animationHideFork(): Promise { + return this.animationShowFork(0) + } + + animationUpFork(y: number, time?: number = 3): Promise { + const ptrFork = this.getObjectByName('ptrFork') + const ptrPillar = this.getObjectByName('ptrPillar') + const pz = ptrPillar.position.z + return new Promise(resolve => { + const bh = 0.22 + const children = ptrFork.children + + gsap.to(ptrFork.position, { + y: y, + duration: time, + repeat: 0, + ease: 'sine.inOut', + onComplete: resolve, + onUpdate: function () { + const a = this.targets()[0] + if (a.y < bh) { + if (pz > -1) { + for (let i = 0; i < children.length; i++) { + const child = children[i] + child.position.y = bh - a.y + } + } else if (a.y < 0) { + for (let i = 0; i < children.length; i++) { + const child = children[i] + child.position.y = 0 - a.y + } + } + } + } + }) + }) + } + + animationDownFork(): Promise { + return this.animationUpFork(0) + } +} + + diff --git a/src/modules/amr/ptr/cl2/Cl2Entity.ts b/src/modules/amr/ptr/cl2/Cl2Entity.ts new file mode 100644 index 0000000..6889e8a --- /dev/null +++ b/src/modules/amr/ptr/cl2/Cl2Entity.ts @@ -0,0 +1,120 @@ +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' +import Cl23dObject from "./Cl23dObject"; +/** + * CL2 机械臂实体类 + * 0.4m/ss // a max 1.2m/s + * 90 = 3.5s cl2 + * 90 = 5s // cLX + */ +export default class Cl2Entity extends BaseEntity { + constructor(viewport: Viewport, id: string) { + super(viewport, id) + } + + + + + // 抬 + addArmRaise(height: number) { + // super.addArmRaise(height) + this.taskQueue.add(this.createTask('ARM_RAISE', + () => this.cl2Object.animationUpFork(height) + )) + } + + // 降 + addArmLower() { + // super.addArmLower() + this.taskQueue.add(this.createTask('ARM_LOWER', + () => this.cl2Object.animationDownFork() + )) + } + + // 伸 + addArmExtender(z: number = 1.2) { + // super.addArmExtender() + this.taskQueue.add(this.createTask('ARM_EXTEND', + () => this.cl2Object.animationShowFork(z) + )) + } + + // 缩 + addArmRetractor() { + // super.addArmRetractor() + this.taskQueue.add(this.createTask('ARM_RETRACT', + () => this.cl2Object.animationHideFork() + )) + } + // 装 + addLoad(item: string): void { + + const ptrForkMesh = this.getArmObject() + this.taskQueue.add(this.createTask('LOAD', async () => { + // 实际业务中应包含装载逻辑 + this.isCarrying = true + this.isLoading = true + // 将物品拾取到机械臂上 + const mesh = this.pickupItem(item) + mesh.position.set(0, 0, -0.15) + mesh.rotation.set(0, THREE.MathUtils.degToRad(90), 0) + ptrForkMesh.add(mesh) + })) + + this.taskQueue.add(this.createTask('ARM_RAISE', + ()=>{ + return new Promise(resolve => { + gsap.to(ptrForkMesh.position, { + y: ptrForkMesh.position.y + 0.2, + duration: 1, + ease: 'sine.inOut', + onComplete: () => { + this.isCarrying = true + this.isLoading = false + resolve() + } + }) + }) + })) + } + + // 卸 + 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 + } + + get cl2Object(): Cl23dObject { + return this.object as Cl23dObject + } +} diff --git a/src/modules/amr/ptr/cl2/Cl2Interaction.ts b/src/modules/amr/ptr/cl2/Cl2Interaction.ts new file mode 100644 index 0000000..39ac680 --- /dev/null +++ b/src/modules/amr/ptr/cl2/Cl2Interaction.ts @@ -0,0 +1,22 @@ +import BaseInteraction from '@/core/base/BaseInteraction.ts' +import * as THREE from 'three' + +export default class PtrInteraction extends BaseInteraction { + + get isSinglePointMode(): boolean { + return true + } + + constructor(itemTypeName: string) { + super(itemTypeName) + } + + createPointOfItem(item: ItemJson, point: THREE.Vector3): ItemJson { + item = super.createPointOfItem(item, point) + + // 创建一个地堆货架 + item.dt.palletWidth = 1 // 宽度 + item.dt.palletDepth = 1.2 // 深度 + return item + } +} diff --git a/src/modules/amr/ptr/cl2/Cl2PropertySetter.ts b/src/modules/amr/ptr/cl2/Cl2PropertySetter.ts new file mode 100644 index 0000000..2cc8a18 --- /dev/null +++ b/src/modules/amr/ptr/cl2/Cl2PropertySetter.ts @@ -0,0 +1,24 @@ +import type { PropertySetter } from "@/core/base/PropertyTypes.ts"; +import { basicFieldsSetter } from "@/editor/widgets/property/PropertyPanelConstant.ts"; + +const propertySetter: PropertySetter = { + flatten: { + fields: [ + ...basicFieldsSetter, + { + dataPath: 'dt.palletWidth', label: '托盘宽度', input: 'InputNumber', + inputProps: {}, + }, + { + dataPath: 'dt.palletDepth', label: '托盘深度', input: 'InputNumber', + inputProps: {}, + }, + { + dataPath: 'state', label: 'ptr控制', input: 'PtrController', + inputProps: {}, + }, + ], + }, +}; + +export default propertySetter; diff --git a/src/modules/amr/ptr/cl2/Cl2Renderer.ts b/src/modules/amr/ptr/cl2/Cl2Renderer.ts new file mode 100644 index 0000000..333ba87 --- /dev/null +++ b/src/modules/amr/ptr/cl2/Cl2Renderer.ts @@ -0,0 +1,77 @@ +import * as THREE from 'three' +import BaseRenderer from '@/core/base/BaseRenderer.ts' +import Constract from '@/core/Constract.ts' +import Cl23dObject from "./Cl23dObject"; + +/** + * ptr侧叉渲染器 + */ +export default class PtrRenderer extends BaseRenderer { + static POINT_NAME = 'cl2' + + pointMaterial: THREE.Material + + /** + * 默认点的高度, 防止和地面重合 + */ + readonly defulePositionY: number = Constract.HEIGHT_WAY + readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1.5, 1.98, 1.5) + readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) + + constructor(itemTypeName: string) { + super(itemTypeName) + } + + /** + * 所有的点,必须使用 storeWidth/storeDepth, 改TF无效 + */ + override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: THREE.Object3D) { + super.afterCreateOrUpdatePoint(item, option, object) + + const point = object + // point.position.y = this.defulePositionY + // point.scale.set(_.sumBy(item.dt.bays, b=>b.bayWidth), this.defaultScale.y, item.dt.rackDepth) + point.rotation.set( + THREE.MathUtils.degToRad(item.tf[1][0]), + THREE.MathUtils.degToRad(item.tf[1][1]), + THREE.MathUtils.degToRad(item.tf[1][2]) + ) + } + + + createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D { + throw new Error('not allow store line.') + } + + updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { + throw new Error('not allow store line.') + } + + + createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D { + // 创建平面几何体 + if (!item.dt.ptrWidth || !item.dt.ptrDepth) { + system.showErrorDialog('field ptrWidth / ptrDepth is null!') + return null + } + + const group = new Cl23dObject(item, option) + group.name = PtrRenderer.POINT_NAME + + // 设置位置 + group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + return group + } + + + + dispose() { + super.dispose() + this.pointMaterial?.dispose() + } + + createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { + throw new Error('Ptr createPointBasic not allow!') + } + +} diff --git a/src/modules/amr/ptr/cl2/index.ts b/src/modules/amr/ptr/cl2/index.ts new file mode 100644 index 0000000..45b9c6a --- /dev/null +++ b/src/modules/amr/ptr/cl2/index.ts @@ -0,0 +1,12 @@ +import { defineModule } from '@/core/manager/ModuleManager.ts' +import Cl2Renderer from './Cl2Renderer.ts' +import Cl2Interaction from './Cl2Interaction.ts' +import propertySetter from './Cl2PropertySetter.ts' + +export const ITEM_TYPE_NAME = 'cl2' + +export default defineModule(ITEM_TYPE_NAME, () => ({ + renderer: new Cl2Renderer(ITEM_TYPE_NAME), + interaction: new Cl2Interaction(ITEM_TYPE_NAME), + setter: propertySetter +})) diff --git a/src/modules/amr/ptr/clx/Clx3dObject.ts b/src/modules/amr/ptr/clx/Clx3dObject.ts new file mode 100644 index 0000000..222ef39 --- /dev/null +++ b/src/modules/amr/ptr/clx/Clx3dObject.ts @@ -0,0 +1,667 @@ +import * as THREE from 'three' +import {CSG} from 'three-csg-ts' +import gsap from 'gsap' +//@ts-ignore +import {mergeGeometries} from 'three/addons/utils/BufferGeometryUtils.js' + + +export default class Clx3dObject extends THREE.Object3D { + + // 创建clx的底座 + private static createClxPedestal(): THREE.BufferGeometry { + + const width = 1.65 + const depth = 1.65 + const dd = 0.05 + const bd = 0.175 + + const shape = new THREE.Shape() + shape.moveTo(-width / 2, -depth / 2 + bd) + shape.lineTo(-width / 2 + bd, -depth / 2) + shape.lineTo(width / 2 - bd, -depth / 2) + shape.lineTo(width / 2, -depth / 2 + bd) + + shape.lineTo(width / 2, -0.285) + shape.lineTo(-0.475, -0.285) + shape.lineTo(-0.475, -0.125) + shape.lineTo(width / 2, -0.125) + shape.lineTo(width / 2, 0.125) + shape.lineTo(-0.475, 0.125) + shape.lineTo(-0.475, 0.285) + shape.lineTo(width / 2, 0.285) + + shape.lineTo(width / 2, depth / 2 - bd) + shape.lineTo(width / 2 - bd, depth / 2) + shape.lineTo(-width / 2 + bd, depth / 2) + shape.lineTo(-width / 2, depth / 2 - bd) + shape.closePath() // 闭合路径 + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 0.22, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelEnabled: false, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometry = new THREE.ExtrudeGeometry(shape, options) + const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) + const mesh = new THREE.Mesh(geometry, material) + + mesh.updateMatrix() + + // 倒角 + const shapeD = new THREE.Shape() + shapeD.moveTo(-0.02, -0.02) + shapeD.lineTo(0.02, 0.02) + shapeD.lineTo(-0.02, 0.02) + shapeD.closePath() + + const shapeDW = new THREE.Shape() + shapeDW.moveTo(0.02, -0.02) + shapeDW.lineTo(0.02, 0.02) + shapeDW.lineTo(-0.02, 0.02) + shapeDW.closePath() + + const shapeDW1 = new THREE.Shape() + shapeDW1.moveTo(-0.02, -0.02) + shapeDW1.lineTo(0.02, -0.02) + shapeDW1.lineTo(-0.02, 0.02) + shapeDW1.closePath() + + + const positionDs: THREE.Vector3[] = [ + new THREE.Vector3(-width / 2, 0.20, -depth / 2 + bd), + new THREE.Vector3(-width / 2 + bd, 0.20, -depth / 2), + new THREE.Vector3(width / 2 - bd, 0.20, -depth / 2), + new THREE.Vector3(width / 2, 0.20, -depth / 2 + bd), + new THREE.Vector3(width / 2, 0.20, depth / 2 - bd), + new THREE.Vector3(width / 2 - bd, 0.20, depth / 2), + new THREE.Vector3(-width / 2 + bd, 0.20, depth / 2), + new THREE.Vector3(-width / 2, 0.20, depth / 2 - bd), + new THREE.Vector3(-width / 2, 0.20, -depth / 2 + bd) + ] + + let result: THREE.Mesh = mesh + + for (let i = 0; i < positionDs.length - 1; i++) { + + const curveD = new THREE.CatmullRomCurve3([positionDs[i], positionDs[i + 1]], + false, + 'catmullrom', + 0) + const optionsD = { + steps: 1, + bevelEnabled: false, + extrudePath: curveD + } + let geometryD: THREE.BufferGeometry = null + if (i == 1) { + geometryD = new THREE.ExtrudeGeometry(shapeDW, optionsD) + } else if (i == 5) { + geometryD = new THREE.ExtrudeGeometry(shapeDW1, optionsD) + } else { + geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) + } + const meshD = new THREE.Mesh(geometryD, material) + meshD.updateMatrix() + result = CSG.subtract(result, meshD) + } + return result.geometry + } + + // 创建clx的立柱 + private static createClxPillar(): THREE.BufferGeometry { + // 606.5 + const width = 0.3 + const depth = 1.188 + const dd = 0.05 + const shape = new THREE.Shape() + shape.moveTo(-0.744, -0.594 + dd) + shape.lineTo(-0.744 + dd, -0.594) + shape.lineTo(-0.744 + width - dd, -0.594) + shape.lineTo(-0.744 + width, -0.594 + dd) + + shape.lineTo(-0.744 + width, -0.4) + shape.lineTo(-0.744 + width - 0.08, -0.4) + shape.lineTo(-0.744 + width - 0.08, 0.4) + shape.lineTo(-0.744 + width, 0.4) + + shape.lineTo(-0.744 + width, -0.594 + depth - dd) + shape.lineTo(-0.744 + width - dd, -0.594 + depth) + shape.lineTo(-0.744 + dd, -0.594 + depth) + shape.lineTo(-0.744, -0.594 + depth - dd) + // shape.lineTo(-0.728, -0.594 + dd); + // shape.closePath() + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.22, 0), new THREE.Vector3(0, 3.357, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelSegments: 0.05, + bevelEnabled: true, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + // const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}); + // const mesh = new THREE.Mesh(geometry, material); + return new THREE.ExtrudeGeometry(shape, options) + + } + + // 创建clx的叉 + private static createClxFork(): THREE.BufferGeometry { + // 606.5 + const width = 0.3 + const depth = 1.188 + const dd = 0.05 + const fdx = 0.06 + const fdy = 0.03 + const shape = new THREE.Shape() + + shape.moveTo(-0.744 + width - 0.08, -0.4) + shape.lineTo(-0.744 + width + 0.02, -0.4) + shape.lineTo(-0.744 + width + 0.02, -0.275) + shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, -0.275) + shape.lineTo(-0.744 + width + 0.02 + 1.24, -0.275 + fdy) + shape.lineTo(-0.744 + width + 0.02 + 1.24, -0.135 - fdy) + shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, -0.135) + shape.lineTo(-0.744 + width + 0.02, -0.135) + shape.lineTo(-0.744 + width + 0.02, 0.135) + shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, 0.135) + shape.lineTo(-0.744 + width + 0.02 + 1.24, 0.135 + fdy) + shape.lineTo(-0.744 + width + 0.02 + 1.24, 0.275 - fdy) + shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, 0.275) + shape.lineTo(-0.744 + width + 0.02, 0.275) + shape.lineTo(-0.744 + width + 0.02, 0.4) + shape.lineTo(-0.744 + width - 0.08, 0.4) + + shape.closePath() + + // 拉伸轨迹线 + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 1.287, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const options = { + steps: 1, + bevelSegments: 0.05, + bevelEnabled: true, + extrudePath: curve // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometry = new THREE.ExtrudeGeometry(shape, options) + const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) + const mesh = new THREE.Mesh(geometry, material) + mesh.updateMatrix() + + const shapeD = new THREE.Shape() + + + shapeD.moveTo(-0.744 + width + 0.02, -0.3) + shapeD.lineTo(-0.744 + width + 0.02 + 1.3, -0.3) + shapeD.lineTo(-0.744 + width + 0.02 + 1.3, 0.3) + shapeD.lineTo(-0.744 + width + 0.02, 0.3) + shape.closePath() + + // 拉伸轨迹线 + const curveD = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.07, 0), new THREE.Vector3(0, 1.3, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + // 挤出几何图形 参数 + const optionsD = { + steps: 1, + bevelSegments: 0.05, + bevelEnabled: true, + extrudePath: curveD // 设置挤出轨迹 + } + // 创建挤出几何体 + const geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) + const meshD = new THREE.Mesh(geometryD, material) + + meshD.updateMatrix() + + // 布尔运算 + const result = CSG.subtract(mesh, meshD) + + return result.geometry + } + + // 创建clx的铰链 + private static createClxGemel(isLeft: boolean = false) { + const width = 0.08 + const depth = 0.9 + const dd = 0.02 + const shape = new THREE.Shape() + shape.moveTo(-width / 2, -depth / 2 + dd) + shape.lineTo(-width / 2 + dd, -depth / 2) + shape.lineTo(width / 2 - dd, -depth / 2) + shape.lineTo(width / 2, -depth / 2 + dd) + shape.lineTo(width / 2, depth / 2 - dd) + shape.lineTo(width / 2 - dd, depth / 2) + shape.lineTo(-width / 2 + dd, depth / 2) + shape.lineTo(-width / 2, depth / 2 - dd) + shape.closePath() + + const shapeBar = new THREE.Shape() + shapeBar.moveTo(0, 0) // 起点在圆心 + shapeBar.absarc(0, 0, 0.02, 0, Math.PI * 2, false) // 从0到π绘制半圆 + shapeBar.closePath() + + const geometries: THREE.BufferGeometry[] = [] + + const curveL1 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(-0.35 + (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(-0.30 + (isLeft ? 0.05 : 0), 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + const curveL2 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(-0.15 + (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(-0.1 + (isLeft ? 0.05 : 0), 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + const curveL3 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0.15 - (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(0.1 - (isLeft ? 0.05 : 0), 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + const curveL4 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0.30 - (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(0.35 - (isLeft ? 0.05 : 0), 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + + if (!isLeft) { + const curveBar1 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(-0.36, -0.4, 0), new THREE.Vector3(0.36, -0.4, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + const curveBar2 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(-0.36, 0, 0), new THREE.Vector3(0.36, 0, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + const curveBar3 = new THREE.CatmullRomCurve3( + [new THREE.Vector3(-0.36, 0.4, 0), new THREE.Vector3(0.36, 0.4, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + geometries.push(new THREE.ExtrudeGeometry(shapeBar, { + steps: 1, + bevelEnabled: false, + extrudePath: curveBar1 + })) + geometries.push(new THREE.ExtrudeGeometry(shapeBar, { + steps: 1, + bevelEnabled: false, + extrudePath: curveBar2 + })) + geometries.push(new THREE.ExtrudeGeometry(shapeBar, { + steps: 1, + bevelEnabled: false, + extrudePath: curveBar3 + })) + } + + const geometryL1 = new THREE.ExtrudeGeometry(shape, { + steps: 1, + bevelEnabled: false, + extrudePath: curveL1 + }) + + const geometryL2 = new THREE.ExtrudeGeometry(shape, { + steps: 1, + bevelEnabled: false, + extrudePath: curveL2 + }) + + const geometryL3 = new THREE.ExtrudeGeometry(shape, { + steps: 1, + bevelEnabled: false, + extrudePath: curveL3 + }) + + const geometryL4 = new THREE.ExtrudeGeometry(shape, { + steps: 1, + bevelEnabled: false, + extrudePath: curveL4 + }) + + geometries.push(geometryL1, geometryL2, geometryL3, geometryL4) + + return mergeGeometries(geometries) + } + + // 创建clx叉的背板 + private static createClxForkBasePlate(): THREE.BufferGeometry { + // 606.5 + const width = 0.3 + const depth = 1.188 + const dd = 0.05 + const fdx = 0.06 + const fdy = 0.03 + + const shape = new THREE.Shape(); + shape.moveTo(-0.744 + 0.1, -0.5); + shape.lineTo(-0.744 + 0.1, 0.5); + shape.lineTo(-0.744 + 0.2, 0.5); + shape.lineTo(-0.744 + 0.2, -0.5); + shape.closePath() + + const curve = new THREE.CatmullRomCurve3( + [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 1.287, 0)], + false, // 闭合曲线 + 'catmullrom', + 0 + ) + const options = { + steps: 1, + bevelSegments: 0.05, + bevelEnabled: true, + extrudePath: curve // 设置挤出轨迹 + } + return new THREE.ExtrudeGeometry(shape, options) + } + + private static clxPedestalGeometry: THREE.BufferGeometry = null + private static clxPillarGeometry: THREE.BufferGeometry = null + private static clxForkGeometry: THREE.BufferGeometry = null + private static clxGemelGeometryL: THREE.BufferGeometry = null + private static clxGemelGeometryR: THREE.BufferGeometry = null + private static clxForkBasePlateGeometry: THREE.BufferGeometry = null + + constructor(item: ItemJson, option?: RendererCudOption) { + super() + + if (!Clx3dObject.clxPedestalGeometry) { + Clx3dObject.clxPedestalGeometry = Clx3dObject.createClxPedestal() + } + const clxPedestalGeometry = Clx3dObject.clxPedestalGeometry + const clxPedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffdddbca}) + const clxPedestalMesh = new THREE.Mesh(clxPedestalGeometry, clxPedestalMaterial) + + if (!Clx3dObject.clxPillarGeometry) { + Clx3dObject.clxPillarGeometry = Clx3dObject.createClxPillar() + } + const clxPillarGeometry = Clx3dObject.clxPillarGeometry + const clxPillarMaterial = new THREE.MeshPhongMaterial({color: 0xff6c6956}) + const clxPillarMesh = new THREE.Mesh(clxPillarGeometry, clxPillarMaterial) + + if (!Clx3dObject.clxForkGeometry) { + Clx3dObject.clxForkGeometry = Clx3dObject.createClxFork() + } + const clxForkGeometry = Clx3dObject.clxForkGeometry + const clxForkMaterial = new THREE.MeshPhongMaterial({color: 0xff444444}) + const clxForkMesh = new THREE.Mesh(clxForkGeometry, clxForkMaterial) + clxForkMesh.name = 'clxFork' + + if (!Clx3dObject.clxGemelGeometryL) { + Clx3dObject.clxGemelGeometryL = Clx3dObject.createClxGemel(true) + } + const clxGemelGeometryL = Clx3dObject.clxGemelGeometryL + + if (!Clx3dObject.clxGemelGeometryR) { + Clx3dObject.clxGemelGeometryR = Clx3dObject.createClxGemel(false) + } + const clxGemelGeometryR = Clx3dObject.clxGemelGeometryR + + const clxGemelMaterial = new THREE.MeshPhongMaterial({color: 0xff555555}) + const clxGemelMeshL1 = new THREE.Mesh(clxGemelGeometryL, clxGemelMaterial) + clxGemelMeshL1.name = 'clxGemelMeshL1' + const clxGemelMeshL2 = new THREE.Mesh(clxGemelGeometryL, clxGemelMaterial) + clxGemelMeshL2.name = 'clxGemelMeshL2' + const clxGemelMeshR1 = new THREE.Mesh(clxGemelGeometryR, clxGemelMaterial) + clxGemelMeshR1.name = 'clxGemelMeshR1' + const clxGemelMeshR2 = new THREE.Mesh(clxGemelGeometryR, clxGemelMaterial) + clxGemelMeshR2.name = 'clxGemelMeshR2' + + if (!Clx3dObject.clxForkBasePlateGeometry) { + Clx3dObject.clxForkBasePlateGeometry = Clx3dObject.createClxForkBasePlate() + } + const clxForkBasePlateGeometry = Clx3dObject.clxForkBasePlateGeometry + const clxForkBasePlateMesh = new THREE.Mesh(clxForkBasePlateGeometry, clxGemelMaterial) + clxForkBasePlateMesh.name = 'clxForkBasePlateMesh' + + const d = 0 + + this.add(clxPedestalMesh) + this.add(clxPillarMesh) + this.add(clxForkMesh) + + + const ac = Math.asin(d / (2 * 0.8)) + + + clxGemelMeshL1.position.z = 0.5 - d / 4 + clxGemelMeshL1.position.y = 0.72 + clxGemelMeshL1.rotation.x = -ac + + clxGemelMeshL2.position.z = 0.5 - d / 4 * 3 + clxGemelMeshL2.position.y = 0.72 + clxGemelMeshL2.rotation.x = -ac + + clxGemelMeshR1.position.z = 0.5 - d / 4 + clxGemelMeshR1.position.y = 0.72 + clxGemelMeshR1.rotation.x = ac + + clxGemelMeshR2.position.z = 0.5 - d / 4 * 3 + clxGemelMeshR2.position.y = 0.72 + clxGemelMeshR2.rotation.x = ac + + this.add(clxGemelMeshL1) + this.add(clxGemelMeshL2) + this.add(clxGemelMeshR1) + this.add(clxGemelMeshR2) + this.add(clxForkBasePlateMesh) + } + + animationShowFork(z: number): Promise { + const clxFork = this.getObjectByName('clxFork') + const clxGemelMeshL1 = this.getObjectByName('clxGemelMeshL1') + const clxGemelMeshL2 = this.getObjectByName('clxGemelMeshL2') + const clxGemelMeshR1 = this.getObjectByName('clxGemelMeshR1') + const clxGemelMeshR2 = this.getObjectByName('clxGemelMeshR2') + const clxForkBasePlateMesh = this.getObjectByName('clxForkBasePlateMesh') + + const fy = clxFork.position.y + + const time = 3 + + const ac = Math.asin(z / (2 * 0.8)) + + // clxGemelMeshL1.position.z = 0.5 - z/4 + // clxGemelMeshL1.position.y = fy + 0.72 + // clxGemelMeshL1.rotation.x = -ac + gsap.to(clxGemelMeshL1.position, { + z: 0.5 - z / 4, + y: fy + 0.72, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + gsap.to(clxGemelMeshL1.rotation, { + x: -ac, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + // clxGemelMeshL2.position.z = 0.5 - z/4 * 3 + // clxGemelMeshL2.position.y = fy + 0.72 + // clxGemelMeshL2.rotation.x = -ac + gsap.to(clxGemelMeshL2.position, { + z: 0.5 - z / 4 * 3, + y: fy + 0.72, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + gsap.to(clxGemelMeshL2.rotation, { + x: -ac, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + + // clxGemelMeshR1.position.z = 0.5 - z/4 + // clxGemelMeshR1.position.y = fy + 0.72 + // clxGemelMeshR1.rotation.x = ac + gsap.to(clxGemelMeshR1.position, { + z: 0.5 - z / 4, + y: fy + 0.72, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + gsap.to(clxGemelMeshR1.rotation, { + x: ac, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + + // clxGemelMeshR2.position.z = 0.5 - z/4 * 3 + // clxGemelMeshR2.position.y = fy + 0.72 + // clxGemelMeshR2.rotation.x = ac + gsap.to(clxGemelMeshR2.position, { + z: 0.5 - z / 4 * 3, + y: fy + 0.72, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + gsap.to(clxGemelMeshR2.rotation, { + x: ac, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + + gsap.to(clxForkBasePlateMesh.position, { + y: fy, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + + return new Promise(resolve => { + gsap.to(clxFork.position, { + z: -z, + duration: time, + repeat: 0, + ease: 'sine.inOut', + onComplete: resolve + }) + }) + + } + + animationHideFork(): Promise { + return this.animationShowFork(0) + } + + animationUpFork(y: number, time?: number = 3): Promise { + const clxFork = this.getObjectByName('clxFork') + const clxGemelMeshL1 = this.getObjectByName('clxGemelMeshL1') + const clxGemelMeshL2 = this.getObjectByName('clxGemelMeshL2') + const clxGemelMeshR1 = this.getObjectByName('clxGemelMeshR1') + const clxGemelMeshR2 = this.getObjectByName('clxGemelMeshR2') + const clxForkBasePlateMesh = this.getObjectByName('clxForkBasePlateMesh') + + + gsap.to(clxGemelMeshL1.position, { + y: y + 0.72, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + gsap.to(clxGemelMeshL2.position, { + y: y + 0.72, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + gsap.to(clxGemelMeshR1.position, { + y: y + 0.72, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + gsap.to(clxGemelMeshR2.position, { + y: y + 0.72, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + gsap.to(clxForkBasePlateMesh.position, { + y: y, + duration: time, + repeat: 0, + ease: 'sine.inOut' + }) + + return new Promise(resolve => { + const bh = 0.22 + const children = clxFork.children + + gsap.to(clxFork.position, { + y: y, + duration: time, + repeat: 0, + ease: 'sine.inOut', + onComplete: resolve, + onUpdate: function() { + const a = this.targets()[0] + if (a.y < bh) { + console.log(a.z) + if (a.z > -1) { + for (let i = 0; i < children.length; i++) { + const child = children[i] + child.position.y = bh - a.y + } + } else if (a.y < 0 ) { + for (let i = 0; i < children.length; i++) { + const child = children[i] + child.position.y = 0 - a.y + } + } + } + } + }) + }) + } + + animationDownFork(): Promise { + return this.animationUpFork(0) + } + +} diff --git a/src/modules/amr/ptr/clx/ClxEntity.ts b/src/modules/amr/ptr/clx/ClxEntity.ts new file mode 100644 index 0000000..af9c95c --- /dev/null +++ b/src/modules/amr/ptr/clx/ClxEntity.ts @@ -0,0 +1,84 @@ +import BaseEntity from '@/core/base/BaseItemEntity.ts' +import type Viewport from '@/core/engine/Viewport.ts' +import * as THREE from 'three' +import gsap from 'gsap' +import Clx3dObject from './Clx3dObject.ts' + +export default class ClxEntity extends BaseEntity { + constructor(viewport: Viewport, id: string) { + super(viewport, id) + } + + // 抬 + addArmRaise(height: number) { + // super.addArmRaise(height) + this.taskQueue.add(this.createTask('ARM_RAISE', + () => this.clxObject.animationUpFork(height) + )) + } + + // 降 + addArmLower() { + // super.addArmLower() + this.taskQueue.add(this.createTask('ARM_LOWER', + () => this.clxObject.animationDownFork() + )) + } + + // 伸 + addArmExtender(z: number = 1.3) { + // super.addArmExtender() + this.taskQueue.add(this.createTask('ARM_EXTEND', + () => this.clxObject.animationShowFork(z) + )) + } + + // 缩 + addArmRetractor() { + // super.addArmRetractor() + this.taskQueue.add(this.createTask('ARM_RETRACT', + () => this.clxObject.animationHideFork() + )) + } + + // 装 + addLoad(item: string): void { + const ptrForkMesh = this.getArmObject() + this.taskQueue.add(this.createTask('LOAD', async () => { + // 实际业务中应包含装载逻辑 + this.isCarrying = true + this.isLoading = true + // 将物品拾取到机械臂上 + const mesh = this.pickupItem(item) + mesh.position.set(0, 0, -0.2) + mesh.rotation.set(0, THREE.MathUtils.degToRad(90), 0) + ptrForkMesh.add(mesh) + })) + this.taskQueue.add(this.createTask('ARM_RAISE', + ()=>this.clxObject.animationUpFork(ptrForkMesh.position.y + 0.2, 1).then(()=>{ + this.isCarrying = true + this.isLoading = false + }))) + } + + // 卸 + 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) + })) + } + + get clxObject(): Clx3dObject { + return this.object as Clx3dObject + } + + // 帮助方法:获取机械臂对象 + getArmObject(): THREE.Object3D { + return this.clxObject.getObjectByName('clxFork') + } +} diff --git a/src/modules/amr/ptr/clx/ClxInteraction.ts b/src/modules/amr/ptr/clx/ClxInteraction.ts new file mode 100644 index 0000000..b6bde8b --- /dev/null +++ b/src/modules/amr/ptr/clx/ClxInteraction.ts @@ -0,0 +1,22 @@ +import BaseInteraction from '@/core/base/BaseInteraction.ts' +import * as THREE from 'three' + +export default class ClxInteraction extends BaseInteraction { + + get isSinglePointMode(): boolean { + return true + } + + constructor(itemTypeName: string) { + super(itemTypeName) + } + + createPointOfItem(item: ItemJson, point: THREE.Vector3): ItemJson { + item = super.createPointOfItem(item, point) + + // 创建一个地堆货架 + item.dt.palletWidth = 1 // 宽度 + item.dt.palletDepth = 1.2 // 深度 + return item + } +} diff --git a/src/modules/amr/ptr/clx/ClxPropertySetter.ts b/src/modules/amr/ptr/clx/ClxPropertySetter.ts new file mode 100644 index 0000000..5390c94 --- /dev/null +++ b/src/modules/amr/ptr/clx/ClxPropertySetter.ts @@ -0,0 +1,20 @@ +import type { PropertySetter } from "@/core/base/PropertyTypes.ts"; +import { basicFieldsSetter } from "@/editor/widgets/property/PropertyPanelConstant.ts"; + +const propertySetter: PropertySetter = { + flatten: { + fields: [ + ...basicFieldsSetter, + { + dataPath: 'dt.clxWidth', label: 'CLX宽度', input: 'InputNumber', + inputProps: {}, + }, + { + dataPath: 'dt.clxDepth', label: 'CLX深度', input: 'InputNumber', + inputProps: {}, + }, + ], + }, +}; + +export default propertySetter; diff --git a/src/modules/amr/ptr/clx/ClxRenderer.ts b/src/modules/amr/ptr/clx/ClxRenderer.ts new file mode 100644 index 0000000..58268eb --- /dev/null +++ b/src/modules/amr/ptr/clx/ClxRenderer.ts @@ -0,0 +1,71 @@ +import * as THREE from 'three' +import BaseRenderer from '@/core/base/BaseRenderer.ts' +import Constract from '@/core/Constract.ts' +import Clx3dObject from "./Clx3dObject"; + +/** + * clx渲染器 + */ +export default class ClxRenderer extends BaseRenderer { + static POINT_NAME = 'clx' + + pointMaterial: THREE.Material + + /** + * 默认点的高度, 防止和地面重合 + */ + readonly defulePositionY: number = Constract.HEIGHT_WAY + readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1, 1, 1) + readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) + readonly defaultLineWidth: number = 0.15 + + constructor(itemTypeName: string) { + super(itemTypeName) + } + + /** + * 所有的点,必须使用 storeWidth/storeDepth, 改TF无效 + */ + override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: THREE.Object3D) { + super.afterCreateOrUpdatePoint(item, option, object) + + const point = object + // point.position.y = this.defulePositionY + // point.scale.set(_.sumBy(item.dt.bays, b=>b.bayWidth), this.defaultScale.y, item.dt.rackDepth) + point.rotation.set( + THREE.MathUtils.degToRad(item.tf[1][0]), + THREE.MathUtils.degToRad(item.tf[1][1]), + THREE.MathUtils.degToRad(item.tf[1][2]) + ) + } + + + createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D { + throw new Error('not allow store line.') + } + + updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { + throw new Error('not allow store line.') + } + + + createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D { + // 创建平面几何体 + const group = new Clx3dObject(item, option) + group.name = ClxRenderer.POINT_NAME + + // 设置位置 + group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) + return group + } + + dispose() { + super.dispose() + this.pointMaterial?.dispose() + } + + createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { + throw new Error('Clx createPointBasic not allow!') + } + +} diff --git a/src/modules/amr/ptr/clx/index.ts b/src/modules/amr/ptr/clx/index.ts new file mode 100644 index 0000000..d918acb --- /dev/null +++ b/src/modules/amr/ptr/clx/index.ts @@ -0,0 +1,12 @@ +import { defineModule } from '@/core/manager/ModuleManager.ts' +import ClxRenderer from './ClxRenderer.ts' +import ClxInteraction from './ClxInteraction.ts' +import propertySetter from './ClxPropertySetter.ts' + +export const ITEM_TYPE_NAME = 'clx' + +export default defineModule(ITEM_TYPE_NAME, () => ({ + renderer: new ClxRenderer(ITEM_TYPE_NAME), + interaction: new ClxInteraction(ITEM_TYPE_NAME), + setter: propertySetter +})) diff --git a/src/modules/cl2/Cl23DGraphics.ts b/src/modules/cl2/Cl23DGraphics.ts deleted file mode 100644 index 2f6a9dc..0000000 --- a/src/modules/cl2/Cl23DGraphics.ts +++ /dev/null @@ -1,295 +0,0 @@ -import * as THREE from "three"; -import {CSG} from "three-csg-ts"; -//@ts-ignore -import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js' - -export default class Cl23DGraphics { - -// 创建ptr的底座 - public static createPtrPedestal(): THREE.BufferGeometry { - // 参数配置 - const radius = 0.8 // 圆半径 - const lineDist = 0.75 // 切割线距离圆心距离 - const segments = 64 // 圆的分段精度 - - // 计算切割线与圆的交点 - const intersectY = Math.sqrt(radius * radius - lineDist * lineDist) - const startAngle = Math.asin(intersectY / radius) - const endAngle = Math.acos(intersectY / radius) - - - const shape = new THREE.Shape() - shape.moveTo(0, 0) // 起点在圆心 - shape.absarc(0, 0, 0.8, startAngle, endAngle, false) // 从0到π绘制半圆 - shape.absarc(0, 0, 0.8, startAngle + Math.PI / 2, endAngle + Math.PI / 2, false) - shape.absarc(0, 0, 0.8, startAngle + Math.PI, endAngle + Math.PI, false) - shape.absarc(0, 0, 0.8, startAngle + Math.PI / 2 * 3, endAngle + Math.PI / 2 * 3, false) - - shape.lineTo(-0.5, -intersectY) - shape.lineTo(-0.5, -intersectY + 0.17) - shape.lineTo(0.75, -intersectY + 0.17) - shape.lineTo(0.75, intersectY - 0.17) - shape.lineTo(-0.5, intersectY - 0.17) - shape.lineTo(-0.5, intersectY) - shape.closePath() // 闭合路径 - - // 拉伸轨迹线 - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 0.22, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const options = { - steps: 1, - bevelEnabled: false, - extrudePath: curve // 设置挤出轨迹 - } - // 创建挤出几何体 - const geometry = new THREE.ExtrudeGeometry(shape, options) - const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) - const mesh = new THREE.Mesh(geometry, material) - - mesh.updateMatrix() - - // 倒角 - const shapeD = new THREE.Shape() - shapeD.moveTo(-0.02, -0.02) - shapeD.lineTo(0.02, 0.02) - shapeD.lineTo(-0.02, 0.02) - shapeD.closePath() - - const curveD = new THREE.EllipseCurve( - 0, 0, radius, radius, 0, Math.PI * 2, false, 0 - ) - // 生成拉伸路径点 - const pointsD = curveD.getPoints(segments).map(p => - new THREE.Vector3(p.x, 0.20, p.y) - ) - // 3. 挤出成型 - const optionsD = { - steps: segments, - bevelEnabled: false, - // bevelSegments: 0.01, - extrudePath: new THREE.CatmullRomCurve3(pointsD) - } - - const geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) - const meshD = new THREE.Mesh(geometryD, material) - meshD.updateMatrix() - - // 布尔运算 - const result = CSG.subtract(mesh, meshD) - - return result.geometry - } - -// 创建ptr的立柱 - public static createPtrPillar(): THREE.BufferGeometry { - // 606.5 - const width = 0.245 - const depth = 0.716 - const dd = 0.05 - const shape = new THREE.Shape() - shape.moveTo(-0.728, -0.358 + dd) - shape.lineTo(-0.728 + dd, -0.358) - shape.lineTo(-0.728 + width - dd, -0.358) - shape.lineTo(-0.728 + width, -0.358 + dd) - - shape.lineTo(-0.728 + width, -0.28) - shape.lineTo(-0.728 + width - 0.08, -0.28) - shape.lineTo(-0.728 + width - 0.08, 0.28) - shape.lineTo(-0.728 + width, 0.28) - - shape.lineTo(-0.728 + width, -0.358 + depth - dd) - shape.lineTo(-0.728 + width - dd, -0.358 + depth) - shape.lineTo(-0.728 + dd, -0.358 + depth) - shape.lineTo(-0.728, -0.358 + depth - dd) - // shape.lineTo(-0.728, -0.358 + dd); - // shape.closePath() - - // 拉伸轨迹线 - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.22, 0), new THREE.Vector3(0, 1.872, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const options = { - steps: 1, - bevelSegments: 0.05, - bevelEnabled: true, - extrudePath: curve // 设置挤出轨迹 - } - // 创建挤出几何体 - let geometry = new THREE.ExtrudeGeometry(shape, options) - - - const fd = 0.03 - const shapeBf = new THREE.Shape() - - shapeBf.moveTo(-0.728 + width - 0.08, -0.28) - shapeBf.lineTo(-0.728 + width + 0.02, -0.28) - shapeBf.lineTo(-0.728 + width + 0.02, -0.25) - shapeBf.lineTo(-0.728 + width + 0.02 + 1.130 - fd, -0.25) - shapeBf.lineTo(-0.728 + width + 0.02 + 1.130, -0.25 + fd) - shapeBf.lineTo(-0.728 + width + 0.02 + 1.130, -0.14 - fd) - shapeBf.lineTo(-0.728 + width + 0.02 + 1.130 - fd, -0.14) - shapeBf.lineTo(-0.728 + width + 0.02, -0.14) - shapeBf.lineTo(-0.728 + width + 0.02, 0.14) - shapeBf.lineTo(-0.728 + width + 0.02 + 1.130 - fd, 0.14) - shapeBf.lineTo(-0.728 + width + 0.02 + 1.130, 0.14 + fd) - shapeBf.lineTo(-0.728 + width + 0.02 + 1.130, 0.25 - fd) - shapeBf.lineTo(-0.728 + width + 0.02 + 1.130 - fd, 0.25) - shapeBf.lineTo(-0.728 + width + 0.02, 0.25) - shapeBf.lineTo(-0.728 + width + 0.02, 0.28) - shapeBf.lineTo(-0.728 + width - 0.08, 0.28) - - shapeBf.closePath() - - // 拉伸轨迹线 - const curveBf = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 0.06, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const optionsBf = { - steps: 1, - bevelSegments: 0.05, - bevelEnabled: true, - extrudePath: curveBf // 设置挤出轨迹 - } - // 创建挤出几何体 - const geometryBf = new THREE.ExtrudeGeometry(shapeBf, optionsBf) - geometry = mergeGeometries([geometry, geometryBf]) - const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) - const mesh = new THREE.Mesh(geometry, material) - - const shapeD = new THREE.Shape() - shapeD.moveTo(-0.3, -0.25) - shapeD.lineTo(-0.3, 0.25) - shapeD.lineTo(0.3, 0.25) - shapeD.lineTo(0.3, -0.25) - shapeD.closePath() - - const curveD = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 1.45, -1), new THREE.Vector3(0, 1.45, 1)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - const optionsD = { - steps: 1, - bevelSegments: 0.05, - bevelEnabled: true, - extrudePath: curveD // 设置挤出轨迹 - } - - const geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) - const meshD = new THREE.Mesh(geometryD, material) - mesh.updateMatrix() - meshD.updateMatrix() - const result = CSG.subtract(mesh, meshD) - - return result.geometry - - } - -// 创建ptr的立柱 - public static createPtrFork(): THREE.BufferGeometry { - // 606.5 - const width = 0.245 - const depth = 0.716 - const dd = 0.05 - const fd = 0.03 - const shape = new THREE.Shape() - - shape.moveTo(-0.728 + width - 0.08, -0.28) - shape.lineTo(-0.728 + width + 0.02, -0.28) - shape.lineTo(-0.728 + width + 0.02, -0.27) - shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, -0.27) - shape.lineTo(-0.728 + width + 0.02 + 1.130, -0.27 + fd) - shape.lineTo(-0.728 + width + 0.02 + 1.130, -0.12 - fd) - shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, -0.12) - shape.lineTo(-0.728 + width + 0.02, -0.12) - shape.lineTo(-0.728 + width + 0.02, 0.12) - shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, 0.12) - shape.lineTo(-0.728 + width + 0.02 + 1.130, 0.12 + fd) - shape.lineTo(-0.728 + width + 0.02 + 1.130, 0.27 - fd) - shape.lineTo(-0.728 + width + 0.02 + 1.130 - fd, 0.27) - shape.lineTo(-0.728 + width + 0.02, 0.27) - shape.lineTo(-0.728 + width + 0.02, 0.28) - shape.lineTo(-0.728 + width - 0.08, 0.28) - - shape.closePath() - - // 拉伸轨迹线 - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 0.482, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const options = { - steps: 1, - bevelSegments: 0.05, - bevelEnabled: true, - extrudePath: curve // 设置挤出轨迹 - } - // 创建挤出几何体 - const geometry = new THREE.ExtrudeGeometry(shape, options) - const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) - const mesh = new THREE.Mesh(geometry, material) - mesh.updateMatrix() - - const shapeD = new THREE.Shape() - - - shapeD.moveTo(-0.728 + width + 0.02, -0.3) - shapeD.lineTo(-0.728 + width + 0.02 + 1.2, -0.3) - shapeD.lineTo(-0.728 + width + 0.02 + 1.2, 0.3) - shapeD.lineTo(-0.728 + width + 0.02, 0.3) - shape.closePath() - - // 拉伸轨迹线 - const curveD = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.07, 0), new THREE.Vector3(0, 0.482, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const optionsD = { - steps: 1, - bevelSegments: 0.05, - bevelEnabled: true, - extrudePath: curveD // 设置挤出轨迹 - } - // 创建挤出几何体 - const geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) - const meshD = new THREE.Mesh(geometryD, material) - - meshD.updateMatrix() - - // 布尔运算 - const result = CSG.subtract(mesh, meshD) - - return result.geometry - } - - public static ptrPedestalGeometry: THREE.BufferGeometry = null - public static ptrPillarGeometry: THREE.BufferGeometry = null - public static ptrForkGeometry: THREE.BufferGeometry = null - -} diff --git a/src/modules/cl2/Cl23dObject.ts b/src/modules/cl2/Cl23dObject.ts deleted file mode 100644 index 90a75c0..0000000 --- a/src/modules/cl2/Cl23dObject.ts +++ /dev/null @@ -1,903 +0,0 @@ -import * as THREE from 'three' -import {CSG} from 'three-csg-ts' -import gsap from 'gsap' -import mqtt from 'mqtt' -import {Euler} from 'three/src/math/Euler' -import Cl2Entity from '@/modules/cl2/Cl2Entity' -import Cl23DGraphics from "@/modules/cl2/Cl23DGraphics" -import { - AmrErrorCode, - AmrMsg, - AmrMsg10010, - AmrMsg10050, - AmrMsg10060, - AmrMsg10110, AmrMsg10120, AmrMsg20011, - AmrMsg20020, - AmrMsg20050, - AmrMsg20060, - AmrMsg20100, AmrMsg20147, AmrMsg20148, - AmrMsg20149, - AmrMsg20150, - AmrMsg20250, - CurBatteryData, - type CEventId, type COperationType, type CTaskMode, type CPickMode, - type LogicDirection, TaskCompletedData, TaskModeChangeData, TaskStatusChangeData, TaskTypeChangeData, AmrMsg20010 -} from "@/core/manager/amr/AmrMessageDefine"; -import {worldModel} from "@/core/manager/WorldModel"; - - -type CStepTaskType = "MOVE" | "MOVE_BACKWARD" | "ROTATION" | "LOAD" | "UNLOAD" | "CHARGE" - -interface StepTask { - SeqNo: number; - StepTaskType: CStepTaskType; - OperationType: 0 | 1 | 2 | 3 | 4 | 5 | 135 | 136; - PickMode: 0 | 1 | 2 | 3 | 4 | 5 | 6; - X: number; - Y: number; - Speed: number; - EndDirection: 0 | 1 | 2 | 3 | 15; - ChargeLocation: number; - GoodsSlotHeight: number; - position: THREE.Vector3; - isCompleted: boolean; -} - -export default class Cl23dObject extends THREE.Object3D { - - private item: ItemJson - - private _cl2Entity: Cl2Entity = null - - private currentStepTaskList: StepTask[] = [] - private runningStepTask: StepTask = null - private runningStepTaskList: StepTask[] = [] - private travelAnimation: core.Tween = null - - private currentLogicX: number = -1 - private currentLogicY: number = -1 - private currentDirection: LogicDirection = 15 - private sendMessageQueue: AmrMsg[] = [] - - public Battery: number = 100 - - private __TaskMode: CTaskMode = 0 - private __OperationType: COperationType = 0 - private __TaskStatus: CEventId = 0 - private __PickMode: CPickMode = 0 - - get TaskMode(): CTaskMode { - return this.__TaskMode - } - - set TaskMode(value: CTaskMode) { - if (this.__TaskMode != value) { - const info = new TaskModeChangeData(this.__TaskMode, value) - const msg = new AmrMsg20011(this.vehicleId, info) - msg.TaskMode = value - this.send20011(msg) - } - this.__TaskMode = value - } - - get OperationType(): COperationType { - return this.__OperationType - } - - set OperationType(value: COperationType) { - if (this.__OperationType != value) { - const info = new TaskTypeChangeData(this.__OperationType, value) - const msg = new AmrMsg20011(this.vehicleId, info) - msg.TaskMode = this.TaskMode - this.send20011(msg) - } - this.__OperationType = value - } - - get TaskStatus(): CEventId { - return this.__TaskStatus - } - - set TaskStatus(value: CEventId) { - if (this.__TaskStatus != value) { - - if (value != 1 && value != 4 && value != 8) { - const info = new TaskStatusChangeData(this.OperationType) - const msg = new AmrMsg20011(this.vehicleId, info) - msg.TaskMode = this.TaskMode - msg.EventId = value - this.send20011(msg) - } else if (value == 4) { - - const info = new TaskCompletedData(this.OperationType) - info.Battery = this.Battery - info.OperationResult = 0 - info.CurLogicX = this.currentLogicX - info.CurLogicY = this.currentLogicY - info.CurX = this.currentLogicX - info.CurY = this.currentLogicY - info.CurDirection = this.currentDirection - info.CurOrientation = THREE.MathUtils.radToDeg(this.rotation.y) - const msg = new AmrMsg20011(this.vehicleId, info) - msg.TaskMode = this.TaskMode - this.send20011(msg) - } - - } - } - - get PickMode(): CPickMode { - return this.__PickMode - } - set PickMode(value: CPickMode) { - if (this.__PickMode !== value) { - this.__PickMode = value - } - } - - private bootTime: number = 0 - - // 心跳间隔 UInt32 单位: s - private heartBeatInterval: number = 0 - // 小车所有上报消息重试间隔(未收到应答消息时重发消息) UInt32 单位: s - private mqRetryInterval: number = 3 - - public get cl2Entity(): Cl2Entity { - if (!this._cl2Entity) { - const cl2: Cl2Entity = Model.getCl2(this.item.id) as Cl2Entity - this._cl2Entity = cl2 - } - return this._cl2Entity - } - - public vehicleId: number - - private clock = new THREE.Clock() - - constructor(item: ItemJson, option?: RendererCudOption) { - super() - this.item = item - if (!Cl23DGraphics.ptrPedestalGeometry) { - Cl23DGraphics.ptrPedestalGeometry = Cl23DGraphics.createPtrPedestal() - } - const ptrPedestalGeometry = Cl23DGraphics.ptrPedestalGeometry - const ptrPedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffdddbca}) - const ptrPedestalMesh = new THREE.Mesh(ptrPedestalGeometry, ptrPedestalMaterial) - ptrPedestalMesh.name = 'ptrPedestal' - - if (!Cl23DGraphics.ptrPillarGeometry) { - Cl23DGraphics.ptrPillarGeometry = Cl23DGraphics.createPtrPillar() - } - const ptrPillarGeometry = Cl23DGraphics.ptrPillarGeometry - const ptrPillarMaterial = new THREE.MeshPhongMaterial({color: 0xff6c6956}) - const ptrPillarMesh = new THREE.Mesh(ptrPillarGeometry, ptrPillarMaterial) - - - if (!Cl23DGraphics.ptrForkGeometry) { - Cl23DGraphics.ptrForkGeometry = Cl23DGraphics.createPtrFork() - } - const ptrForkGeometry = Cl23DGraphics.ptrForkGeometry - const ptrForkMaterial = new THREE.MeshPhongMaterial({color: 0xff444444}) - const ptrForkMesh = new THREE.Mesh(ptrForkGeometry, ptrForkMaterial) - ptrForkMesh.name = 'ptrFork' - - this.add(ptrPedestalMesh) - const groupPillar = new THREE.Group() - groupPillar.name = 'ptrPillar' - groupPillar.add(ptrPillarMesh) - groupPillar.add(ptrForkMesh) - this.add(groupPillar) - - this.vehicleId = parseInt(this.cl2Entity.id) - this.cl2Entity.viewport.addFrameTimerCallback(this.cl2Entity.id, this.onFrameTimer.bind(this)) - if (!worldModel.state.runState.isVirtual) { - this.subscribeMessage('/wcs_server/' + this.cl2Entity.id) - this.subscribeMessage('/agv_robot/status') - } - } - - private AGVModel = "CYBER-LIFT-A_V1.0" - private AGVFnModel = "FITBOTS-CYBER-LIFT-1000_V1.0" - - - private heartBeatTimeCount: number = 0 - private mqRetryTimeCount: number = 0 - - private onFrameTimer() { - if (!worldModel.state.runState.isVirtual) { - return - } - const delta = this.clock.getDelta() - this.mqRetryTimeCount += delta - if (this.mqRetryInterval > 0 && this.mqRetryTimeCount >= this.mqRetryInterval) { - this.mqRetryTimeCount = 0 - // 在此处理消息重试 - if (this.sendMessageQueue.length > 0) { - this.sendMessage(this.sendMessageQueue[0]) - } - } - this.heartBeatTimeCount += delta - if (this.heartBeatInterval > 0 && this.heartBeatTimeCount >= this.heartBeatInterval) { - this.heartBeatTimeCount = 0 - // 在此处发送心跳报文 - this.sendHeartBeat() - } - } - - - // 开机 - boot() { - this.bootTime = Date.now(); - this.computeLogicXYAndDirection(); - - if (!worldModel.state.runState.isVirtual) { - return - } - this.subscribeMessage('/wcs_server/' + this.cl2Entity.id) - this.send20147() - setTimeout(() => { - this.send20149() - this.TaskMode = 1 - // 检查当前所在位置和方向 根据车当前所在的xz坐标获取地标 - setTimeout(() => { - this.sendCurrentPositionAndDirection() - setTimeout(() => { - this.send20150() - }, 1000) - }, 1000) - }, 2000) - - } - - // 关机 - shutdown() { - if (!worldModel.state.runState.isVirtual) { - return - } - const content = new AmrMsg20148(this.vehicleId) - // 电量 - content.Battery = 100 - content.CreateMonoTime = Date.now() - this.bootTime - content.Uptime = content.CreateMonoTime - const m20148 = new AmrMsg(content) - this.sendMessage(m20148) - } - - // 开机上报 - send20147() { - const content = new AmrMsg20147(this.vehicleId) - content.AGVModel = this.AGVModel - content.AGVFnModel = this.AGVFnModel - // 电量 - content.Battery = 100 - content.CreateMonoTime = Date.now() - this.bootTime - const m20147 = new AmrMsg(content) - this.sendMessage(m20147) - } - - // 主程序启动上报 - send20149() { - const content = new AmrMsg20149(this.vehicleId) - content.AGVModel = this.AGVModel - content.AGVFnModel = this.AGVFnModel - // 电量 - content.Battery = 100 - content.CreateMonoTime = Date.now() - this.bootTime - const m20149 = new AmrMsg(content) - this.sendMessage(m20149) - } - - // 上线上报 - send20150() { - const content = new AmrMsg20150(this.vehicleId) - content.AGVModel = this.AGVModel - content.AGVFnModel = this.AGVFnModel - content.Battery = 100 - content.CreateMonoTime = Date.now() - this.bootTime - const m20150 = new AmrMsg(content) - this.sendMessage(m20150) - } - - // 上报当前位姿,地标和方向 - sendCurrentPositionAndDirection() { - if (this.currentLogicX <= 0 || this.currentLogicY <= 0) { - // 当前车辆所在位置未找到 - const content = new AmrMsg20250(this.vehicleId) - content.Duration = 0 - content.ErrCode = 5 - content.ErrCodeName = AmrErrorCode[5].ErrCodeName - content.ErrEvtType = 1 - content.ErrLevel = 14 - content.ErrLifecycle = 2 - content.CreateMonoTime = Date.now() - this.bootTime - const m20250 = new AmrMsg(content) - this.sendMessage(m20250) - } else { - // 发送正常地标信息 - const content = new AmrMsg20020(this.vehicleId) - content.CurDirection = this.currentDirection - content.CurLogicX = this.currentLogicX - content.CurLogicY = this.currentLogicY - content.CurX = this.currentLogicX - content.CurY = this.currentLogicY - content.CreateMonoTime = Date.now() - this.bootTime - const m20020 = new AmrMsg(content) - this.sendMessage(m20020) - } - } - - send20010() { - const content: AmrMsg20010 = new AmrMsg20010(this.vehicleId) - content.CurX = this.currentLogicX - content.CurY = this.currentLogicY - content.CurDirection = this.currentDirection - content.OperationType = this.OperationType - content.Battery = this.Battery - content.OperationResult = 0 - content.Summary = { - ActuatorsData: [ - { - MechNo: 1, - Name: "Mech1", - PickMode: this.PickMode - } - ] - } - - - const m20010 = new AmrMsg(content) - this.sendMessage(m20010) - } - - send20011(content: AmrMsg20011) { - const m20011 = new AmrMsg>(content) - this.sendMessage(m20011) - } - - send20020(content: AmrMsg20020) { - const m20020 = new AmrMsg(content) - this.sendMessage(m20020) - } - - send20060() { - const content = new AmrMsg20060(this.vehicleId) - content.CreateMonoTime = Date.now() - this.bootTime - content.CurBattery = new CurBatteryData() - content.CurLogicX = this.currentLogicX - content.CurLogicY = this.currentLogicY - content.CurOrientation = THREE.MathUtils.radToDeg(this.rotation.y); - content.CurX = this.position.x; - content.CurY = this.position.z; - content.X = this.position.x; - content.Y = this.position.z; - const m20060 = new AmrMsg(content) - this.sendMessage(m20060) - } - - send20250(content: AmrMsg20250) { - this.sendMessage(new AmrMsg(content)) - } - - subscribeMessage(topic: string) { - worldModel.envManager.client.subscribe(topic, {qos: 0}) - } - - sendMessage(msg: AmrMsg) { - if (!worldModel.state.runState.isVirtual) { - return - } - console.log('send message:', JSON.stringify(msg)) - if (this.sendMessageQueue.indexOf(msg) < 0) { - this.sendMessageQueue.push(msg) - } - if (this.sendMessageQueue.length <= 0) { - this.mqRetryTimeCount = 0 - } - worldModel.envManager.client.publish('/agv_robot/status', JSON.stringify(msg)) - this.heartBeatTimeCount = 0 - } - - sendHeartBeat() { - if (!worldModel.state.runState.isVirtual) { - return - } - const content = new AmrMsg20100(this.vehicleId) - content.Temperature = {Battery: this.Battery} - const m20100 = new AmrMsg(content) - worldModel.envManager.client.publish('/agv_robot/status', JSON.stringify(m20100)) - } - - sendAck(seqNo: number, vehicleId: number) { - if (!worldModel.state.runState.isVirtual) { - return - } - const msg20050 = new AmrMsg20050(seqNo, vehicleId) - const ack = new AmrMsg(msg20050) - this.heartBeatTimeCount = 0 - worldModel.envManager.client.publish('/agv_robot/status', JSON.stringify(ack)) - } - - /*==========RCS消息处理============*/ - // 处理任务 - handle10010Message(data: AmrMsg10010) { - if (this.currentStepTaskList.length > 0) { - if (this.runningStepTask.OperationType == 0 && this.runningStepTask.X == data.StartX && this.runningStepTask.Y == data.StartY) { - // this.currentStepTaskList = [] - this.makeStepTask(data) - this.executeTask() - } else { - // 此处应该有错误处理 - } - } else { - this.makeStepTask(data) - this.executeTask() - } - } - - handle10050Message(data: AmrMsg) { - if (this.sendMessageQueue.length > 0 && data.content.SeqNo === this.sendMessageQueue[0].content.SeqNo) { - this.mqRetryTimeCount = 0 - this.sendMessageQueue.shift() - } - } - - handle10060Message(data: AmrMsg) { - this.mqRetryInterval = data.content.MqRetryTime - this.heartBeatInterval = data.content.HeartBeat - this.TaskMode = 0 - } - - // 处理状态查询 - handle10110Message(data: AmrMsg) { - this.send20060() - } - - // 取消任务 - handle10120Message(data: AmrMsg) { - - } - - /*==========真车消息处理============*/ - - // 计算逻辑方向 - computeLogicXYAndDirection() { - let ra = this.rotation.y - while (ra > Math.PI * 2) { - ra -= Math.PI * 2 - } - while (ra < 0) { - ra += Math.PI * 2 - } - const ddra = Math.PI / 8 - if (ra >= ddra * 7 || ra < ddra) { - this.currentDirection = 0; - - } else if (ra >= ddra && ra < ddra * 3) { - this.currentDirection = 3; - - } else if (ra >= ddra * 3 && ra < ddra * 5) { - this.currentDirection = 2; - - } else if (ra >= ddra * 5 && ra < ddra * 7) { - this.currentDirection = 1; - } else { - this.currentDirection = 15; - } - - const pointItem = Model.getItemByXYZ(this.position.x, this.position.y, this.position.z) - if (!pointItem || !pointItem.logicX || !pointItem.logicY) { - this.currentLogicX = -1; - this.currentLogicY = -1; - } else { - this.currentLogicX = pointItem.logicX; - this.currentLogicY = pointItem.logicY; - } - - } - - makeStepTask(data: AmrMsg10010) { - - let currentStepTask: StepTask = this.runningStepTask - if (currentStepTask == null) { - - currentStepTask = { - SeqNo: 0, - StepTaskType: "MOVE", - OperationType: 0, - PickMode: 0, - X: this.currentLogicX, - Y: this.currentLogicY, - Speed: 1000, - EndDirection: this.currentDirection, - ChargeLocation: 0, - GoodsSlotHeight: 0, - position: this.position, - isCompleted: true - } - } - let endDirection = currentStepTask.EndDirection - - if (data.Link.length > 0) { - for (let i = 0; i < data.Link.length; i++) { - const link = data.Link[i] - if ((currentStepTask.X == link.X && currentStepTask.Y == link.Y) - || (currentStepTask.X != link.X && currentStepTask.Y != link.Y)) { - continue - } else if (currentStepTask.X < link.X) { - if (link.Speed > 0) { - endDirection = 0 - } else { - endDirection = 2 - } - } else if (currentStepTask.X > link.X) { - if (link.Speed > 0) { - endDirection = 2 - } else { - endDirection = 0 - } - } else if (currentStepTask.Y < link.Y) { - if (link.Speed > 0) { - endDirection = 1 - } else { - endDirection = 3 - } - } else if (currentStepTask.Y > link.Y) { - if (link.Speed > 0) { - endDirection = 3 - } else { - endDirection = 1 - } - } - - if (endDirection != currentStepTask.EndDirection) { - const stepTask: StepTask = { - SeqNo: data.SeqNo, - StepTaskType: "ROTATION", - OperationType: 0, - PickMode: 0, - X: link.X, - Y: link.Y, - Speed: link.Speed, - EndDirection: endDirection, - ChargeLocation: data.ChargeLocation, - GoodsSlotHeight: data.GoodsSlotHeight, - position: Model.getPositionByLogicXY(link.X, link.Y) as THREE.Vector3, - isCompleted: false - } - currentStepTask = stepTask - this.currentStepTaskList.push(stepTask) - } - - const stepTask: StepTask = { - SeqNo: data.SeqNo, - StepTaskType: link.Speed > 0 ? "MOVE" : "MOVE_BACKWARD", - OperationType: 0, - PickMode: 0, - X: link.X, - Y: link.Y, - Speed: link.Speed, - EndDirection: endDirection, - ChargeLocation: data.ChargeLocation, - GoodsSlotHeight: data.GoodsSlotHeight, - position: Model.getPositionByLogicXY(link.X, link.Y) as THREE.Vector3, - isCompleted: false - } - currentStepTask = stepTask - this.currentStepTaskList.push(stepTask) - } - } - - - if (data.OperationType == 0 && data.EndDirection >= 0 && data.EndDirection <= 3) { - endDirection = data.EndDirection; - } else if (data.OperationType == 3 && data.ChargeDirection >= 0 && data.ChargeDirection <= 3) { - endDirection = data.ChargeDirection - } else if (data.OperationType == 4 && data.GoodsSlotDirection >= 0 && data.GoodsSlotDirection <= 3) { - if (data.GoodsSlotDirection == 0) { - endDirection = 3 - } else { - endDirection = (data.GoodsSlotDirection - 1) as LogicDirection - } - } - if (endDirection != currentStepTask.EndDirection) { - const stepTask: StepTask = { - SeqNo: data.SeqNo, - StepTaskType: "ROTATION", - OperationType: 0, - PickMode: 0, - X: data.EndX, - Y: data.EndY, - Speed: currentStepTask.Speed, - EndDirection: endDirection, - ChargeLocation: data.ChargeLocation, - GoodsSlotHeight: data.GoodsSlotHeight, - position: Model.getPositionByLogicXY(data.EndX, data.EndY) as THREE.Vector3, - isCompleted: false - } - this.currentStepTaskList.push(stepTask) - } - if (data.OperationType == 3) { - - const stepTask: StepTask = { - SeqNo: data.SeqNo, - StepTaskType: "CHARGE", - OperationType: 3, - PickMode: 0, - X: data.EndX, - Y: data.EndY, - Speed: currentStepTask.Speed, - EndDirection: endDirection, - ChargeLocation: data.ChargeLocation, - GoodsSlotHeight: data.GoodsSlotHeight, - position: Model.getPositionByLogicXY(data.EndX, data.EndY) as THREE.Vector3, - isCompleted: false - } - this.currentStepTaskList.push(stepTask) - - } else if (data.OperationType == 4) { - - const stepTask: StepTask = { - SeqNo: data.SeqNo, - StepTaskType: data.PickMode == 1 ? "LOAD" : "UNLOAD", - OperationType: 4, - PickMode: data.PickMode, - X: data.EndX, - Y: data.EndY, - Speed: currentStepTask.Speed, - EndDirection: endDirection, - ChargeLocation: data.ChargeLocation, - GoodsSlotHeight: data.GoodsSlotHeight, - position: Model.getPositionByLogicXY(data.EndX, data.EndY) as THREE.Vector3, - isCompleted: false - } - this.currentStepTaskList.push(stepTask) - - } else { - - } - - } - - executeTask() { - this.TaskMode = 2 - while (this.currentStepTaskList.length > 0) { - const stepTask = this.currentStepTaskList[0] - if (this.runningStepTask) { - if ((stepTask.StepTaskType == "MOVE" || stepTask.StepTaskType == "MOVE_BACKWARD") - && stepTask.EndDirection == this.runningStepTask.EndDirection - && (stepTask.Speed > 0) == (this.runningStepTask.Speed > 0)) { - this.runningStepTask = stepTask - this.currentStepTaskList.shift() - this.runningStepTaskList.push(stepTask) - - this.addTravel(stepTask.X, stepTask.Y, stepTask.Speed/1000) - - } else { - break - } - } else { - this.runningStepTask = stepTask - this.currentStepTaskList.shift() - this.runningStepTaskList.push(stepTask) - if (stepTask.StepTaskType == "MOVE" || stepTask.StepTaskType == "MOVE_BACKWARD") { - this.addTravel(stepTask.X, stepTask.Y, stepTask.Speed/1000) - } else if (stepTask.StepTaskType == "ROTATION") { - this.addRotation(stepTask.EndDirection) - } else if (stepTask.StepTaskType == "LOAD") { - this.addLoad(stepTask.GoodsSlotHeight/1000) - } else if (stepTask.StepTaskType == "UNLOAD") { - this.addUnload(stepTask.GoodsSlotHeight/1000) - } - } - } - } - - onActionCompleted() { - this.runningStepTaskList = [] - this.runningStepTask = null - this.computeLogicXYAndDirection() - // 当前所有动作执行完毕 - if (this.currentStepTaskList.length <= 0) { - this.send20010() - } - this.PickMode = 0 - this.OperationType = 0 - this.TaskMode = 0 - this.executeTask() - } - - /*==========动画处理============*/ - - // 转 - addRotation(direction: number): Promise { - let rad = 0 - switch (direction) { - case 1: - rad = Math.PI / 2 * 3 - break - case 2: - rad = Math.PI - break - case 3: - rad = Math.PI / 2 - break - default: - rad = 0 - } - const quat1 = new THREE.Quaternion().setFromEuler(this.rotation) - const euler: Euler = new Euler(this.rotation.x, rad, this.rotation.z) - const quat2 = new THREE.Quaternion().setFromEuler(euler) - const angleDiff = quat1.angleTo(quat2) - console.log(rad, this.rotation.y, angleDiff) - const tr = this.rotation.y + angleDiff - let time = Math.abs(angleDiff) / (Math.PI / 7) - const duration = time - - return new Promise(resolve => { - gsap.to(this.rotation, { - y: tr, - duration, - ease: 'none', - onComplete: ()=>{ - resolve() - this.onActionCompleted() - } - }) - }) - } - - // 走 - addTravel(logicX: number, logicY: number, speed: number = 1): Promise { - this.OperationType = 0 - this.PickMode = 0 - const pos = Model.getPositionByLogicXY(logicX, logicY) - - const fromPos = this.position - const toPos = pos as THREE.Vector3 - const distance = fromPos.distanceTo(toPos) - const duration = Math.max(1.0, distance / speed) - - if (!this.travelAnimation) { - return new Promise(resolve => { - this.travelAnimation = gsap.fromTo(this.position, { - x: fromPos.x, - y: fromPos.y, - z: fromPos.z, - }, { - x: toPos.x, - y: toPos.y, - z: toPos.z, - duration, - ease: 'power2.inOut', - onComplete: () => { - this.travelAnimation = null - resolve() - this.onActionCompleted() - }, - onUpdate: () => { - - for (let i = 0; i < this.runningStepTaskList.length; i++) { - const task = this.runningStepTaskList[i] - if (task.isCompleted == false) { - if (this.position.distanceTo(task.position) < 0.1) { - task.isCompleted = true - this.runningStepTaskList.splice(0, i + 1) - const content: AmrMsg20020 = new AmrMsg20020(this.vehicleId) - content.CurLogicX = task.X - content.CurLogicY = task.Y - content.CurX = task.X - content.CurY = task.Y - // content.CurOrientation = task.Orientation - content.CurDirection = task.EndDirection - this.send20020(content) - break - } - } - } - } - }) - }) - } else { - this.travelAnimation.vars.x = toPos.x - this.travelAnimation.vars.y = toPos.y - this.travelAnimation.vars.z = toPos.z - const tt = this.travelAnimation.duration() - this.travelAnimation.duration(tt + duration) - this.travelAnimation.invalidate().restart(); - } - - } - - // 取货 - addLoad(height: number): void { - this.PickMode = 1 - this.OperationType = 4 - this.animationUpFork(height).then( - () => this.animationShowFork(1.4).then( - ()=>this.animationUpFork(height + 0.2).then( - ()=>this.animationHideFork().then( - ()=>this.animationDownFork().then(()=>{ - this.onActionCompleted() - }) - ) - ) - ) - ) - } - // 卸货 - addUnload(height: number): void { - this.PickMode = 2 - this.OperationType = 4 - this.animationUpFork(height + 0.2).then( - () => this.animationShowFork(1.4).then( - ()=>this.animationUpFork(height).then( - ()=>this.animationHideFork().then( - ()=>this.animationDownFork().then(()=>{ - this.onActionCompleted() - }) - ) - ) - ) - ) - } - - animationShowFork(z: number): Promise { - - const ptrPillar = this.getObjectByName('ptrPillar') - const time = 3 - return new Promise(resolve => { - gsap.to(ptrPillar.position, { - z: -z, - duration: time, - repeat: 0, - ease: 'sine.inOut', - onComplete: resolve - }) - }) - - } - - animationHideFork(): Promise { - return this.animationShowFork(0) - } - - animationUpFork(y: number, time?: number = 3): Promise { - const ptrFork = this.getObjectByName('ptrFork') - const ptrPillar = this.getObjectByName('ptrPillar') - const pz = ptrPillar.position.z - return new Promise(resolve => { - const bh = 0.22 - const children = ptrFork.children - - gsap.to(ptrFork.position, { - y: y, - duration: time, - repeat: 0, - ease: 'sine.inOut', - onComplete: resolve, - onUpdate: function () { - const a = this.targets()[0] - if (a.y < bh) { - if (pz > -1) { - for (let i = 0; i < children.length; i++) { - const child = children[i] - child.position.y = bh - a.y - } - } else if (a.y < 0) { - for (let i = 0; i < children.length; i++) { - const child = children[i] - child.position.y = 0 - a.y - } - } - } - } - }) - }) - } - - animationDownFork(): Promise { - return this.animationUpFork(0) - } -} - - diff --git a/src/modules/cl2/Cl2Entity.ts b/src/modules/cl2/Cl2Entity.ts deleted file mode 100644 index f3aa3ec..0000000 --- a/src/modules/cl2/Cl2Entity.ts +++ /dev/null @@ -1,195 +0,0 @@ -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' -import Cl23dObject, {type Cl2Task} from "@/modules/cl2/Cl23dObject"; -/** - * CL2 机械臂实体类 - * 0.4m/ss // a max 1.2m/s - * 90 = 3.5s cl2 - * 90 = 5s // cLX - */ -export default class Cl2Entity extends BaseEntity { - constructor(viewport: Viewport, id: string) { - super(viewport, id) - } - - - // 移动 - addRobotTask(task: Cl2Task) { - - if (task.id === 10010) { - let startX = task.content.StartX - let startY = task.content.StartY - - if (task.content.Link?.length > 0) { - for (let i = 0; i < task.content.Link.length; i++) { - const link = task.content.Link[i]; - let moveDirection: 0 | 1 | 2 | 3 | 15 = 15; - // 计算 移动方向 - if (startX !== link.X && startY !== link.Y) { - throw new Error('Invalid task'); - } else if (startX === link.X) { - if (startY < link.Y && link.Speed > 0 || startY > link.Y && link.Speed < 0) { - moveDirection = 3 - } else { - moveDirection = 1 - } - } else { - if (startX > link.X && link.Speed > 0 || startX < link.X && link.Speed < 0) { - moveDirection = 0 - } else { - moveDirection = 2 - } - } - this.taskQueue.add(this.createTask('ROTATION', - () => this.cl2Object.addRotation(moveDirection * Math.PI/2) - )) - this.taskQueue.add(this.createTask('TRAVEL', - () => this.cl2Object.addTravel(Model.getPositionByLogicXY(link.X, link.Y) ,Math.abs(link.Speed/1000)) - )) - startX = link.X - startY = link.Y - } - } - //移动 - if (task.content.OperationType === 0) { - - } else if (task.content.OperationType === 1) { - - } else if (task.content.OperationType === 2) { - - } else if (task.content.OperationType === 3) { - - } else if (task.content.OperationType === 4) { - // 取货 - if (task.content.PickMode === 1) { - const gh = task.content.GoodsSlotHeight/1000 - this.addArmRaise(gh) - this.addArmExtender() - // this.addArmRaise(gh + 0.1) - this.addLoad(task.content.GoodsId) - this.addArmRetractor() - this.addArmLower() - // 放货 - } else if (task.content.PickMode === 2) { - this.addArmRaise(task.content.GoodsSlotHeight/1000 + 0.2) - this.addArmExtender() - this.addArmLower() - this.addUnload(task.content.GoodsId, '') - this.addArmRetractor() - } - - } else if (task.content.OperationType === 5) { - - } else if (task.content.OperationType === 135) { - - } else if (task.content.OperationType === 136) {} - - - } - - - } - - // 抬 - addArmRaise(height: number) { - // super.addArmRaise(height) - this.taskQueue.add(this.createTask('ARM_RAISE', - () => this.cl2Object.animationUpFork(height) - )) - } - - // 降 - addArmLower() { - // super.addArmLower() - this.taskQueue.add(this.createTask('ARM_LOWER', - () => this.cl2Object.animationDownFork() - )) - } - - // 伸 - addArmExtender(z: number = 1.2) { - // super.addArmExtender() - this.taskQueue.add(this.createTask('ARM_EXTEND', - () => this.cl2Object.animationShowFork(z) - )) - } - - // 缩 - addArmRetractor() { - // super.addArmRetractor() - this.taskQueue.add(this.createTask('ARM_RETRACT', - () => this.cl2Object.animationHideFork() - )) - } - // 装 - addLoad(item: string): void { - - const ptrForkMesh = this.getArmObject() - this.taskQueue.add(this.createTask('LOAD', async () => { - // 实际业务中应包含装载逻辑 - this.isCarrying = true - this.isLoading = true - // 将物品拾取到机械臂上 - const mesh = this.pickupItem(item) - mesh.position.set(0, 0, -0.15) - mesh.rotation.set(0, THREE.MathUtils.degToRad(90), 0) - ptrForkMesh.add(mesh) - })) - - this.taskQueue.add(this.createTask('ARM_RAISE', - ()=>{ - return new Promise(resolve => { - gsap.to(ptrForkMesh.position, { - y: ptrForkMesh.position.y + 0.2, - duration: 1, - ease: 'sine.inOut', - onComplete: () => { - this.isCarrying = true - this.isLoading = false - resolve() - } - }) - }) - })) - } - - // 卸 - 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 - } - - get cl2Object(): Cl23dObject { - return this.object as Cl23dObject - } -} diff --git a/src/modules/cl2/Cl2Interaction.ts b/src/modules/cl2/Cl2Interaction.ts deleted file mode 100644 index 39ac680..0000000 --- a/src/modules/cl2/Cl2Interaction.ts +++ /dev/null @@ -1,22 +0,0 @@ -import BaseInteraction from '@/core/base/BaseInteraction.ts' -import * as THREE from 'three' - -export default class PtrInteraction extends BaseInteraction { - - get isSinglePointMode(): boolean { - return true - } - - constructor(itemTypeName: string) { - super(itemTypeName) - } - - createPointOfItem(item: ItemJson, point: THREE.Vector3): ItemJson { - item = super.createPointOfItem(item, point) - - // 创建一个地堆货架 - item.dt.palletWidth = 1 // 宽度 - item.dt.palletDepth = 1.2 // 深度 - return item - } -} diff --git a/src/modules/cl2/Cl2PropertySetter.ts b/src/modules/cl2/Cl2PropertySetter.ts deleted file mode 100644 index 2cc8a18..0000000 --- a/src/modules/cl2/Cl2PropertySetter.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { PropertySetter } from "@/core/base/PropertyTypes.ts"; -import { basicFieldsSetter } from "@/editor/widgets/property/PropertyPanelConstant.ts"; - -const propertySetter: PropertySetter = { - flatten: { - fields: [ - ...basicFieldsSetter, - { - dataPath: 'dt.palletWidth', label: '托盘宽度', input: 'InputNumber', - inputProps: {}, - }, - { - dataPath: 'dt.palletDepth', label: '托盘深度', input: 'InputNumber', - inputProps: {}, - }, - { - dataPath: 'state', label: 'ptr控制', input: 'PtrController', - inputProps: {}, - }, - ], - }, -}; - -export default propertySetter; diff --git a/src/modules/cl2/Cl2Renderer.ts b/src/modules/cl2/Cl2Renderer.ts deleted file mode 100644 index 5c5b980..0000000 --- a/src/modules/cl2/Cl2Renderer.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as THREE from 'three' -import BaseRenderer from '@/core/base/BaseRenderer.ts' -import Constract from '@/core/Constract.ts' -import Cl23dObject from "@/modules/cl2/Cl23dObject"; - -/** - * ptr侧叉渲染器 - */ -export default class PtrRenderer extends BaseRenderer { - static POINT_NAME = 'cl2' - - pointMaterial: THREE.Material - - /** - * 默认点的高度, 防止和地面重合 - */ - readonly defulePositionY: number = Constract.HEIGHT_WAY - readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1.5, 1.98, 1.5) - readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) - - constructor(itemTypeName: string) { - super(itemTypeName) - } - - /** - * 所有的点,必须使用 storeWidth/storeDepth, 改TF无效 - */ - override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: THREE.Object3D) { - super.afterCreateOrUpdatePoint(item, option, object) - - const point = object - // point.position.y = this.defulePositionY - // point.scale.set(_.sumBy(item.dt.bays, b=>b.bayWidth), this.defaultScale.y, item.dt.rackDepth) - point.rotation.set( - THREE.MathUtils.degToRad(item.tf[1][0]), - THREE.MathUtils.degToRad(item.tf[1][1]), - THREE.MathUtils.degToRad(item.tf[1][2]) - ) - } - - - createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D { - throw new Error('not allow store line.') - } - - updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { - throw new Error('not allow store line.') - } - - - createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D { - // 创建平面几何体 - if (!item.dt.ptrWidth || !item.dt.ptrDepth) { - system.showErrorDialog('field ptrWidth / ptrDepth is null!') - return null - } - - const group = new Cl23dObject(item, option) - group.name = PtrRenderer.POINT_NAME - - // 设置位置 - group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) - return group - } - - - - dispose() { - super.dispose() - this.pointMaterial?.dispose() - } - - createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { - throw new Error('Ptr createPointBasic not allow!') - } - -} diff --git a/src/modules/cl2/index.ts b/src/modules/cl2/index.ts deleted file mode 100644 index 45b9c6a..0000000 --- a/src/modules/cl2/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { defineModule } from '@/core/manager/ModuleManager.ts' -import Cl2Renderer from './Cl2Renderer.ts' -import Cl2Interaction from './Cl2Interaction.ts' -import propertySetter from './Cl2PropertySetter.ts' - -export const ITEM_TYPE_NAME = 'cl2' - -export default defineModule(ITEM_TYPE_NAME, () => ({ - renderer: new Cl2Renderer(ITEM_TYPE_NAME), - interaction: new Cl2Interaction(ITEM_TYPE_NAME), - setter: propertySetter -})) diff --git a/src/modules/clx/Clx3dObject.ts b/src/modules/clx/Clx3dObject.ts deleted file mode 100644 index 222ef39..0000000 --- a/src/modules/clx/Clx3dObject.ts +++ /dev/null @@ -1,667 +0,0 @@ -import * as THREE from 'three' -import {CSG} from 'three-csg-ts' -import gsap from 'gsap' -//@ts-ignore -import {mergeGeometries} from 'three/addons/utils/BufferGeometryUtils.js' - - -export default class Clx3dObject extends THREE.Object3D { - - // 创建clx的底座 - private static createClxPedestal(): THREE.BufferGeometry { - - const width = 1.65 - const depth = 1.65 - const dd = 0.05 - const bd = 0.175 - - const shape = new THREE.Shape() - shape.moveTo(-width / 2, -depth / 2 + bd) - shape.lineTo(-width / 2 + bd, -depth / 2) - shape.lineTo(width / 2 - bd, -depth / 2) - shape.lineTo(width / 2, -depth / 2 + bd) - - shape.lineTo(width / 2, -0.285) - shape.lineTo(-0.475, -0.285) - shape.lineTo(-0.475, -0.125) - shape.lineTo(width / 2, -0.125) - shape.lineTo(width / 2, 0.125) - shape.lineTo(-0.475, 0.125) - shape.lineTo(-0.475, 0.285) - shape.lineTo(width / 2, 0.285) - - shape.lineTo(width / 2, depth / 2 - bd) - shape.lineTo(width / 2 - bd, depth / 2) - shape.lineTo(-width / 2 + bd, depth / 2) - shape.lineTo(-width / 2, depth / 2 - bd) - shape.closePath() // 闭合路径 - - // 拉伸轨迹线 - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 0.22, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const options = { - steps: 1, - bevelEnabled: false, - extrudePath: curve // 设置挤出轨迹 - } - // 创建挤出几何体 - const geometry = new THREE.ExtrudeGeometry(shape, options) - const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) - const mesh = new THREE.Mesh(geometry, material) - - mesh.updateMatrix() - - // 倒角 - const shapeD = new THREE.Shape() - shapeD.moveTo(-0.02, -0.02) - shapeD.lineTo(0.02, 0.02) - shapeD.lineTo(-0.02, 0.02) - shapeD.closePath() - - const shapeDW = new THREE.Shape() - shapeDW.moveTo(0.02, -0.02) - shapeDW.lineTo(0.02, 0.02) - shapeDW.lineTo(-0.02, 0.02) - shapeDW.closePath() - - const shapeDW1 = new THREE.Shape() - shapeDW1.moveTo(-0.02, -0.02) - shapeDW1.lineTo(0.02, -0.02) - shapeDW1.lineTo(-0.02, 0.02) - shapeDW1.closePath() - - - const positionDs: THREE.Vector3[] = [ - new THREE.Vector3(-width / 2, 0.20, -depth / 2 + bd), - new THREE.Vector3(-width / 2 + bd, 0.20, -depth / 2), - new THREE.Vector3(width / 2 - bd, 0.20, -depth / 2), - new THREE.Vector3(width / 2, 0.20, -depth / 2 + bd), - new THREE.Vector3(width / 2, 0.20, depth / 2 - bd), - new THREE.Vector3(width / 2 - bd, 0.20, depth / 2), - new THREE.Vector3(-width / 2 + bd, 0.20, depth / 2), - new THREE.Vector3(-width / 2, 0.20, depth / 2 - bd), - new THREE.Vector3(-width / 2, 0.20, -depth / 2 + bd) - ] - - let result: THREE.Mesh = mesh - - for (let i = 0; i < positionDs.length - 1; i++) { - - const curveD = new THREE.CatmullRomCurve3([positionDs[i], positionDs[i + 1]], - false, - 'catmullrom', - 0) - const optionsD = { - steps: 1, - bevelEnabled: false, - extrudePath: curveD - } - let geometryD: THREE.BufferGeometry = null - if (i == 1) { - geometryD = new THREE.ExtrudeGeometry(shapeDW, optionsD) - } else if (i == 5) { - geometryD = new THREE.ExtrudeGeometry(shapeDW1, optionsD) - } else { - geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) - } - const meshD = new THREE.Mesh(geometryD, material) - meshD.updateMatrix() - result = CSG.subtract(result, meshD) - } - return result.geometry - } - - // 创建clx的立柱 - private static createClxPillar(): THREE.BufferGeometry { - // 606.5 - const width = 0.3 - const depth = 1.188 - const dd = 0.05 - const shape = new THREE.Shape() - shape.moveTo(-0.744, -0.594 + dd) - shape.lineTo(-0.744 + dd, -0.594) - shape.lineTo(-0.744 + width - dd, -0.594) - shape.lineTo(-0.744 + width, -0.594 + dd) - - shape.lineTo(-0.744 + width, -0.4) - shape.lineTo(-0.744 + width - 0.08, -0.4) - shape.lineTo(-0.744 + width - 0.08, 0.4) - shape.lineTo(-0.744 + width, 0.4) - - shape.lineTo(-0.744 + width, -0.594 + depth - dd) - shape.lineTo(-0.744 + width - dd, -0.594 + depth) - shape.lineTo(-0.744 + dd, -0.594 + depth) - shape.lineTo(-0.744, -0.594 + depth - dd) - // shape.lineTo(-0.728, -0.594 + dd); - // shape.closePath() - - // 拉伸轨迹线 - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.22, 0), new THREE.Vector3(0, 3.357, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const options = { - steps: 1, - bevelSegments: 0.05, - bevelEnabled: true, - extrudePath: curve // 设置挤出轨迹 - } - // 创建挤出几何体 - // const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}); - // const mesh = new THREE.Mesh(geometry, material); - return new THREE.ExtrudeGeometry(shape, options) - - } - - // 创建clx的叉 - private static createClxFork(): THREE.BufferGeometry { - // 606.5 - const width = 0.3 - const depth = 1.188 - const dd = 0.05 - const fdx = 0.06 - const fdy = 0.03 - const shape = new THREE.Shape() - - shape.moveTo(-0.744 + width - 0.08, -0.4) - shape.lineTo(-0.744 + width + 0.02, -0.4) - shape.lineTo(-0.744 + width + 0.02, -0.275) - shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, -0.275) - shape.lineTo(-0.744 + width + 0.02 + 1.24, -0.275 + fdy) - shape.lineTo(-0.744 + width + 0.02 + 1.24, -0.135 - fdy) - shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, -0.135) - shape.lineTo(-0.744 + width + 0.02, -0.135) - shape.lineTo(-0.744 + width + 0.02, 0.135) - shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, 0.135) - shape.lineTo(-0.744 + width + 0.02 + 1.24, 0.135 + fdy) - shape.lineTo(-0.744 + width + 0.02 + 1.24, 0.275 - fdy) - shape.lineTo(-0.744 + width + 0.02 + 1.24 - fdx, 0.275) - shape.lineTo(-0.744 + width + 0.02, 0.275) - shape.lineTo(-0.744 + width + 0.02, 0.4) - shape.lineTo(-0.744 + width - 0.08, 0.4) - - shape.closePath() - - // 拉伸轨迹线 - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 1.287, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const options = { - steps: 1, - bevelSegments: 0.05, - bevelEnabled: true, - extrudePath: curve // 设置挤出轨迹 - } - // 创建挤出几何体 - const geometry = new THREE.ExtrudeGeometry(shape, options) - const material = new THREE.MeshBasicMaterial({color: 0xffdddbca}) - const mesh = new THREE.Mesh(geometry, material) - mesh.updateMatrix() - - const shapeD = new THREE.Shape() - - - shapeD.moveTo(-0.744 + width + 0.02, -0.3) - shapeD.lineTo(-0.744 + width + 0.02 + 1.3, -0.3) - shapeD.lineTo(-0.744 + width + 0.02 + 1.3, 0.3) - shapeD.lineTo(-0.744 + width + 0.02, 0.3) - shape.closePath() - - // 拉伸轨迹线 - const curveD = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.07, 0), new THREE.Vector3(0, 1.3, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - // 挤出几何图形 参数 - const optionsD = { - steps: 1, - bevelSegments: 0.05, - bevelEnabled: true, - extrudePath: curveD // 设置挤出轨迹 - } - // 创建挤出几何体 - const geometryD = new THREE.ExtrudeGeometry(shapeD, optionsD) - const meshD = new THREE.Mesh(geometryD, material) - - meshD.updateMatrix() - - // 布尔运算 - const result = CSG.subtract(mesh, meshD) - - return result.geometry - } - - // 创建clx的铰链 - private static createClxGemel(isLeft: boolean = false) { - const width = 0.08 - const depth = 0.9 - const dd = 0.02 - const shape = new THREE.Shape() - shape.moveTo(-width / 2, -depth / 2 + dd) - shape.lineTo(-width / 2 + dd, -depth / 2) - shape.lineTo(width / 2 - dd, -depth / 2) - shape.lineTo(width / 2, -depth / 2 + dd) - shape.lineTo(width / 2, depth / 2 - dd) - shape.lineTo(width / 2 - dd, depth / 2) - shape.lineTo(-width / 2 + dd, depth / 2) - shape.lineTo(-width / 2, depth / 2 - dd) - shape.closePath() - - const shapeBar = new THREE.Shape() - shapeBar.moveTo(0, 0) // 起点在圆心 - shapeBar.absarc(0, 0, 0.02, 0, Math.PI * 2, false) // 从0到π绘制半圆 - shapeBar.closePath() - - const geometries: THREE.BufferGeometry[] = [] - - const curveL1 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(-0.35 + (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(-0.30 + (isLeft ? 0.05 : 0), 0, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - const curveL2 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(-0.15 + (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(-0.1 + (isLeft ? 0.05 : 0), 0, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - const curveL3 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0.15 - (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(0.1 - (isLeft ? 0.05 : 0), 0, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - const curveL4 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0.30 - (isLeft ? 0.05 : 0), 0, 0), new THREE.Vector3(0.35 - (isLeft ? 0.05 : 0), 0, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - - if (!isLeft) { - const curveBar1 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(-0.36, -0.4, 0), new THREE.Vector3(0.36, -0.4, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - const curveBar2 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(-0.36, 0, 0), new THREE.Vector3(0.36, 0, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - const curveBar3 = new THREE.CatmullRomCurve3( - [new THREE.Vector3(-0.36, 0.4, 0), new THREE.Vector3(0.36, 0.4, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - geometries.push(new THREE.ExtrudeGeometry(shapeBar, { - steps: 1, - bevelEnabled: false, - extrudePath: curveBar1 - })) - geometries.push(new THREE.ExtrudeGeometry(shapeBar, { - steps: 1, - bevelEnabled: false, - extrudePath: curveBar2 - })) - geometries.push(new THREE.ExtrudeGeometry(shapeBar, { - steps: 1, - bevelEnabled: false, - extrudePath: curveBar3 - })) - } - - const geometryL1 = new THREE.ExtrudeGeometry(shape, { - steps: 1, - bevelEnabled: false, - extrudePath: curveL1 - }) - - const geometryL2 = new THREE.ExtrudeGeometry(shape, { - steps: 1, - bevelEnabled: false, - extrudePath: curveL2 - }) - - const geometryL3 = new THREE.ExtrudeGeometry(shape, { - steps: 1, - bevelEnabled: false, - extrudePath: curveL3 - }) - - const geometryL4 = new THREE.ExtrudeGeometry(shape, { - steps: 1, - bevelEnabled: false, - extrudePath: curveL4 - }) - - geometries.push(geometryL1, geometryL2, geometryL3, geometryL4) - - return mergeGeometries(geometries) - } - - // 创建clx叉的背板 - private static createClxForkBasePlate(): THREE.BufferGeometry { - // 606.5 - const width = 0.3 - const depth = 1.188 - const dd = 0.05 - const fdx = 0.06 - const fdy = 0.03 - - const shape = new THREE.Shape(); - shape.moveTo(-0.744 + 0.1, -0.5); - shape.lineTo(-0.744 + 0.1, 0.5); - shape.lineTo(-0.744 + 0.2, 0.5); - shape.lineTo(-0.744 + 0.2, -0.5); - shape.closePath() - - const curve = new THREE.CatmullRomCurve3( - [new THREE.Vector3(0, 0.02, 0), new THREE.Vector3(0, 1.287, 0)], - false, // 闭合曲线 - 'catmullrom', - 0 - ) - const options = { - steps: 1, - bevelSegments: 0.05, - bevelEnabled: true, - extrudePath: curve // 设置挤出轨迹 - } - return new THREE.ExtrudeGeometry(shape, options) - } - - private static clxPedestalGeometry: THREE.BufferGeometry = null - private static clxPillarGeometry: THREE.BufferGeometry = null - private static clxForkGeometry: THREE.BufferGeometry = null - private static clxGemelGeometryL: THREE.BufferGeometry = null - private static clxGemelGeometryR: THREE.BufferGeometry = null - private static clxForkBasePlateGeometry: THREE.BufferGeometry = null - - constructor(item: ItemJson, option?: RendererCudOption) { - super() - - if (!Clx3dObject.clxPedestalGeometry) { - Clx3dObject.clxPedestalGeometry = Clx3dObject.createClxPedestal() - } - const clxPedestalGeometry = Clx3dObject.clxPedestalGeometry - const clxPedestalMaterial = new THREE.MeshPhongMaterial({color: 0xffdddbca}) - const clxPedestalMesh = new THREE.Mesh(clxPedestalGeometry, clxPedestalMaterial) - - if (!Clx3dObject.clxPillarGeometry) { - Clx3dObject.clxPillarGeometry = Clx3dObject.createClxPillar() - } - const clxPillarGeometry = Clx3dObject.clxPillarGeometry - const clxPillarMaterial = new THREE.MeshPhongMaterial({color: 0xff6c6956}) - const clxPillarMesh = new THREE.Mesh(clxPillarGeometry, clxPillarMaterial) - - if (!Clx3dObject.clxForkGeometry) { - Clx3dObject.clxForkGeometry = Clx3dObject.createClxFork() - } - const clxForkGeometry = Clx3dObject.clxForkGeometry - const clxForkMaterial = new THREE.MeshPhongMaterial({color: 0xff444444}) - const clxForkMesh = new THREE.Mesh(clxForkGeometry, clxForkMaterial) - clxForkMesh.name = 'clxFork' - - if (!Clx3dObject.clxGemelGeometryL) { - Clx3dObject.clxGemelGeometryL = Clx3dObject.createClxGemel(true) - } - const clxGemelGeometryL = Clx3dObject.clxGemelGeometryL - - if (!Clx3dObject.clxGemelGeometryR) { - Clx3dObject.clxGemelGeometryR = Clx3dObject.createClxGemel(false) - } - const clxGemelGeometryR = Clx3dObject.clxGemelGeometryR - - const clxGemelMaterial = new THREE.MeshPhongMaterial({color: 0xff555555}) - const clxGemelMeshL1 = new THREE.Mesh(clxGemelGeometryL, clxGemelMaterial) - clxGemelMeshL1.name = 'clxGemelMeshL1' - const clxGemelMeshL2 = new THREE.Mesh(clxGemelGeometryL, clxGemelMaterial) - clxGemelMeshL2.name = 'clxGemelMeshL2' - const clxGemelMeshR1 = new THREE.Mesh(clxGemelGeometryR, clxGemelMaterial) - clxGemelMeshR1.name = 'clxGemelMeshR1' - const clxGemelMeshR2 = new THREE.Mesh(clxGemelGeometryR, clxGemelMaterial) - clxGemelMeshR2.name = 'clxGemelMeshR2' - - if (!Clx3dObject.clxForkBasePlateGeometry) { - Clx3dObject.clxForkBasePlateGeometry = Clx3dObject.createClxForkBasePlate() - } - const clxForkBasePlateGeometry = Clx3dObject.clxForkBasePlateGeometry - const clxForkBasePlateMesh = new THREE.Mesh(clxForkBasePlateGeometry, clxGemelMaterial) - clxForkBasePlateMesh.name = 'clxForkBasePlateMesh' - - const d = 0 - - this.add(clxPedestalMesh) - this.add(clxPillarMesh) - this.add(clxForkMesh) - - - const ac = Math.asin(d / (2 * 0.8)) - - - clxGemelMeshL1.position.z = 0.5 - d / 4 - clxGemelMeshL1.position.y = 0.72 - clxGemelMeshL1.rotation.x = -ac - - clxGemelMeshL2.position.z = 0.5 - d / 4 * 3 - clxGemelMeshL2.position.y = 0.72 - clxGemelMeshL2.rotation.x = -ac - - clxGemelMeshR1.position.z = 0.5 - d / 4 - clxGemelMeshR1.position.y = 0.72 - clxGemelMeshR1.rotation.x = ac - - clxGemelMeshR2.position.z = 0.5 - d / 4 * 3 - clxGemelMeshR2.position.y = 0.72 - clxGemelMeshR2.rotation.x = ac - - this.add(clxGemelMeshL1) - this.add(clxGemelMeshL2) - this.add(clxGemelMeshR1) - this.add(clxGemelMeshR2) - this.add(clxForkBasePlateMesh) - } - - animationShowFork(z: number): Promise { - const clxFork = this.getObjectByName('clxFork') - const clxGemelMeshL1 = this.getObjectByName('clxGemelMeshL1') - const clxGemelMeshL2 = this.getObjectByName('clxGemelMeshL2') - const clxGemelMeshR1 = this.getObjectByName('clxGemelMeshR1') - const clxGemelMeshR2 = this.getObjectByName('clxGemelMeshR2') - const clxForkBasePlateMesh = this.getObjectByName('clxForkBasePlateMesh') - - const fy = clxFork.position.y - - const time = 3 - - const ac = Math.asin(z / (2 * 0.8)) - - // clxGemelMeshL1.position.z = 0.5 - z/4 - // clxGemelMeshL1.position.y = fy + 0.72 - // clxGemelMeshL1.rotation.x = -ac - gsap.to(clxGemelMeshL1.position, { - z: 0.5 - z / 4, - y: fy + 0.72, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - gsap.to(clxGemelMeshL1.rotation, { - x: -ac, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - // clxGemelMeshL2.position.z = 0.5 - z/4 * 3 - // clxGemelMeshL2.position.y = fy + 0.72 - // clxGemelMeshL2.rotation.x = -ac - gsap.to(clxGemelMeshL2.position, { - z: 0.5 - z / 4 * 3, - y: fy + 0.72, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - gsap.to(clxGemelMeshL2.rotation, { - x: -ac, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - - // clxGemelMeshR1.position.z = 0.5 - z/4 - // clxGemelMeshR1.position.y = fy + 0.72 - // clxGemelMeshR1.rotation.x = ac - gsap.to(clxGemelMeshR1.position, { - z: 0.5 - z / 4, - y: fy + 0.72, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - gsap.to(clxGemelMeshR1.rotation, { - x: ac, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - - // clxGemelMeshR2.position.z = 0.5 - z/4 * 3 - // clxGemelMeshR2.position.y = fy + 0.72 - // clxGemelMeshR2.rotation.x = ac - gsap.to(clxGemelMeshR2.position, { - z: 0.5 - z / 4 * 3, - y: fy + 0.72, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - gsap.to(clxGemelMeshR2.rotation, { - x: ac, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - - gsap.to(clxForkBasePlateMesh.position, { - y: fy, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - - return new Promise(resolve => { - gsap.to(clxFork.position, { - z: -z, - duration: time, - repeat: 0, - ease: 'sine.inOut', - onComplete: resolve - }) - }) - - } - - animationHideFork(): Promise { - return this.animationShowFork(0) - } - - animationUpFork(y: number, time?: number = 3): Promise { - const clxFork = this.getObjectByName('clxFork') - const clxGemelMeshL1 = this.getObjectByName('clxGemelMeshL1') - const clxGemelMeshL2 = this.getObjectByName('clxGemelMeshL2') - const clxGemelMeshR1 = this.getObjectByName('clxGemelMeshR1') - const clxGemelMeshR2 = this.getObjectByName('clxGemelMeshR2') - const clxForkBasePlateMesh = this.getObjectByName('clxForkBasePlateMesh') - - - gsap.to(clxGemelMeshL1.position, { - y: y + 0.72, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - gsap.to(clxGemelMeshL2.position, { - y: y + 0.72, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - gsap.to(clxGemelMeshR1.position, { - y: y + 0.72, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - gsap.to(clxGemelMeshR2.position, { - y: y + 0.72, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - gsap.to(clxForkBasePlateMesh.position, { - y: y, - duration: time, - repeat: 0, - ease: 'sine.inOut' - }) - - return new Promise(resolve => { - const bh = 0.22 - const children = clxFork.children - - gsap.to(clxFork.position, { - y: y, - duration: time, - repeat: 0, - ease: 'sine.inOut', - onComplete: resolve, - onUpdate: function() { - const a = this.targets()[0] - if (a.y < bh) { - console.log(a.z) - if (a.z > -1) { - for (let i = 0; i < children.length; i++) { - const child = children[i] - child.position.y = bh - a.y - } - } else if (a.y < 0 ) { - for (let i = 0; i < children.length; i++) { - const child = children[i] - child.position.y = 0 - a.y - } - } - } - } - }) - }) - } - - animationDownFork(): Promise { - return this.animationUpFork(0) - } - -} diff --git a/src/modules/clx/ClxEntity.ts b/src/modules/clx/ClxEntity.ts deleted file mode 100644 index 013e28f..0000000 --- a/src/modules/clx/ClxEntity.ts +++ /dev/null @@ -1,84 +0,0 @@ -import BaseEntity from '@/core/base/BaseItemEntity.ts' -import type Viewport from '@/core/engine/Viewport.ts' -import * as THREE from 'three' -import gsap from 'gsap' -import Clx3dObject from '@/modules/clx/Clx3dObject.ts' - -export default class ClxEntity extends BaseEntity { - constructor(viewport: Viewport, id: string) { - super(viewport, id) - } - - // 抬 - addArmRaise(height: number) { - // super.addArmRaise(height) - this.taskQueue.add(this.createTask('ARM_RAISE', - () => this.clxObject.animationUpFork(height) - )) - } - - // 降 - addArmLower() { - // super.addArmLower() - this.taskQueue.add(this.createTask('ARM_LOWER', - () => this.clxObject.animationDownFork() - )) - } - - // 伸 - addArmExtender(z: number = 1.3) { - // super.addArmExtender() - this.taskQueue.add(this.createTask('ARM_EXTEND', - () => this.clxObject.animationShowFork(z) - )) - } - - // 缩 - addArmRetractor() { - // super.addArmRetractor() - this.taskQueue.add(this.createTask('ARM_RETRACT', - () => this.clxObject.animationHideFork() - )) - } - - // 装 - addLoad(item: string): void { - const ptrForkMesh = this.getArmObject() - this.taskQueue.add(this.createTask('LOAD', async () => { - // 实际业务中应包含装载逻辑 - this.isCarrying = true - this.isLoading = true - // 将物品拾取到机械臂上 - const mesh = this.pickupItem(item) - mesh.position.set(0, 0, -0.2) - mesh.rotation.set(0, THREE.MathUtils.degToRad(90), 0) - ptrForkMesh.add(mesh) - })) - this.taskQueue.add(this.createTask('ARM_RAISE', - ()=>this.clxObject.animationUpFork(ptrForkMesh.position.y + 0.2, 1).then(()=>{ - this.isCarrying = true - this.isLoading = false - }))) - } - - // 卸 - 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) - })) - } - - get clxObject(): Clx3dObject { - return this.object as Clx3dObject - } - - // 帮助方法:获取机械臂对象 - getArmObject(): THREE.Object3D { - return this.clxObject.getObjectByName('clxFork') - } -} diff --git a/src/modules/clx/ClxInteraction.ts b/src/modules/clx/ClxInteraction.ts deleted file mode 100644 index b6bde8b..0000000 --- a/src/modules/clx/ClxInteraction.ts +++ /dev/null @@ -1,22 +0,0 @@ -import BaseInteraction from '@/core/base/BaseInteraction.ts' -import * as THREE from 'three' - -export default class ClxInteraction extends BaseInteraction { - - get isSinglePointMode(): boolean { - return true - } - - constructor(itemTypeName: string) { - super(itemTypeName) - } - - createPointOfItem(item: ItemJson, point: THREE.Vector3): ItemJson { - item = super.createPointOfItem(item, point) - - // 创建一个地堆货架 - item.dt.palletWidth = 1 // 宽度 - item.dt.palletDepth = 1.2 // 深度 - return item - } -} diff --git a/src/modules/clx/ClxPropertySetter.ts b/src/modules/clx/ClxPropertySetter.ts deleted file mode 100644 index 5390c94..0000000 --- a/src/modules/clx/ClxPropertySetter.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { PropertySetter } from "@/core/base/PropertyTypes.ts"; -import { basicFieldsSetter } from "@/editor/widgets/property/PropertyPanelConstant.ts"; - -const propertySetter: PropertySetter = { - flatten: { - fields: [ - ...basicFieldsSetter, - { - dataPath: 'dt.clxWidth', label: 'CLX宽度', input: 'InputNumber', - inputProps: {}, - }, - { - dataPath: 'dt.clxDepth', label: 'CLX深度', input: 'InputNumber', - inputProps: {}, - }, - ], - }, -}; - -export default propertySetter; diff --git a/src/modules/clx/ClxRenderer.ts b/src/modules/clx/ClxRenderer.ts deleted file mode 100644 index 7073390..0000000 --- a/src/modules/clx/ClxRenderer.ts +++ /dev/null @@ -1,71 +0,0 @@ -import * as THREE from 'three' -import BaseRenderer from '@/core/base/BaseRenderer.ts' -import Constract from '@/core/Constract.ts' -import Clx3dObject from "@/modules/clx/Clx3dObject"; - -/** - * clx渲染器 - */ -export default class ClxRenderer extends BaseRenderer { - static POINT_NAME = 'clx' - - pointMaterial: THREE.Material - - /** - * 默认点的高度, 防止和地面重合 - */ - readonly defulePositionY: number = Constract.HEIGHT_WAY - readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1, 1, 1) - readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0) - readonly defaultLineWidth: number = 0.15 - - constructor(itemTypeName: string) { - super(itemTypeName) - } - - /** - * 所有的点,必须使用 storeWidth/storeDepth, 改TF无效 - */ - override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: THREE.Object3D) { - super.afterCreateOrUpdatePoint(item, option, object) - - const point = object - // point.position.y = this.defulePositionY - // point.scale.set(_.sumBy(item.dt.bays, b=>b.bayWidth), this.defaultScale.y, item.dt.rackDepth) - point.rotation.set( - THREE.MathUtils.degToRad(item.tf[1][0]), - THREE.MathUtils.degToRad(item.tf[1][1]), - THREE.MathUtils.degToRad(item.tf[1][2]) - ) - } - - - createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D { - throw new Error('not allow store line.') - } - - updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { - throw new Error('not allow store line.') - } - - - createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D { - // 创建平面几何体 - const group = new Clx3dObject(item, option) - group.name = ClxRenderer.POINT_NAME - - // 设置位置 - group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2]) - return group - } - - dispose() { - super.dispose() - this.pointMaterial?.dispose() - } - - createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D { - throw new Error('Clx createPointBasic not allow!') - } - -} diff --git a/src/modules/clx/index.ts b/src/modules/clx/index.ts deleted file mode 100644 index 70a53c4..0000000 --- a/src/modules/clx/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineModule } from '@/core/manager/ModuleManager.ts' -import ClxRenderer from './ClxRenderer.ts' -import ClxInteraction from './ClxInteraction.ts' -import propertySetter from '@/modules/clx/ClxPropertySetter.ts' - -export const ITEM_TYPE_NAME = 'clx' - -export default defineModule(ITEM_TYPE_NAME, () => ({ - name: ITEM_TYPE_NAME, - renderer: new ClxRenderer(ITEM_TYPE_NAME), - interaction: new ClxInteraction(ITEM_TYPE_NAME), - setter: propertySetter -})) From d6e081746b0af458d3f6f8f6b5e66df74cebb754 Mon Sep 17 00:00:00 2001 From: lizw-2015 <1183409807@qq.com> Date: Thu, 3 Jul 2025 17:36:17 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(device):=20=E6=B7=BB=E5=8A=A0=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 DeviceManager 类,实现设备相关数据查询方法 - 添加 QueryExecutorReq、QueryInvLedgerReq、QueryInvLpnReq、QueryLocationReq 等请求模型类 - 实现 locations 和 vehicles 页面的查询功能,包括表单输入和表格展示 - 优化页面样式,添加查询按钮和数据加载状态显示 --- src/views/device/locations.vue | 146 +++++++++++++++++++++++++++++++++++++++-- src/views/device/vehicles.vue | 144 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 276 insertions(+), 14 deletions(-) diff --git a/src/views/device/locations.vue b/src/views/device/locations.vue index cca4dcf..e4967a1 100644 --- a/src/views/device/locations.vue +++ b/src/views/device/locations.vue @@ -1,12 +1,144 @@ + + - - + diff --git a/src/views/device/vehicles.vue b/src/views/device/vehicles.vue index 1970a3b..0a0edca 100644 --- a/src/views/device/vehicles.vue +++ b/src/views/device/vehicles.vue @@ -1,12 +1,142 @@ + + - - +