Browse Source

Merge remote-tracking branch 'origin/master'

master
yuliang 6 months ago
parent
commit
c46c165807
  1. 3
      doc/RCS3任务拆解.md
  2. 20
      doc/RCS数据库结构.md
  3. 3
      src/core/ModelUtils.ts
  4. 6
      src/core/manager/CodeDropper.ts
  5. 4
      src/core/manager/EntityManager.ts
  6. 44
      src/core/manager/ModuleManager.ts
  7. 67
      src/core/manager/RunManager.ts
  8. 56
      src/core/manager/WorldModel.ts
  9. 2
      src/editor/CatalogDefine.vue
  10. 1
      src/editor/Model2DEditor.vue
  11. 47
      src/editor/ModelMain.vue
  12. 5
      src/editor/ModelMainInit.ts
  13. 55
      src/editor/menus/FileMenu.ts
  14. 1
      src/editor/widgets/modeltree/ModeltreeViewJs.js
  15. 48
      src/example/example1.js
  16. 2
      src/main.ts
  17. 7
      src/modules/agv1/index.ts
  18. 9
      src/modules/carton/index.ts
  19. 9
      src/modules/charger/index.ts
  20. 9
      src/modules/cl2/index.ts
  21. 8
      src/modules/clx/index.ts
  22. 9
      src/modules/gstore/index.ts
  23. 11
      src/modules/measure/index.ts
  24. 9
      src/modules/pallet/index.ts
  25. 760
      src/modules/rack/RackRenderer.ts_back
  26. 9
      src/modules/rack/index.ts
  27. 9
      src/modules/shuttle_rack/index.ts
  28. 7
      src/modules/tote/index.ts
  29. 37
      src/modules/way/WayRenderer.ts
  30. 9
      src/modules/way/index.ts
  31. 1
      src/types/global.d.ts
  32. 17
      src/types/model.d.ts

3
doc/RCS3任务拆解.md

@ -111,8 +111,7 @@ CL2 和 CLX 使用的是货叉朝向和方向正负系。比如: 凯乐士CL2使
{
type: 'robotRotation',
executorId: '机器人编号',
worldRotation: 90
// 转动身体到世界角度 90 度
worldRotation: 90 // 转动身体到世界角度 90 度, 世界角度为:上=0°,左=90°,右=270°,下=180°
}
```

20
doc/RCS数据库结构.md

@ -3,6 +3,7 @@ drop table if exists rcs_task_biz;
create table rcs_task_biz
(
biz_task_id bigint not null,
env_id bigint not null comment '环境ID',
biz_type varchar(10) not null comment '任务类型' default 'carry',
lpn varchar(50) not null comment '托盘ID',
priority integer not null comment '任务优先级',
@ -25,6 +26,7 @@ create table rcs_task_plan
(
plan_task_id bigint not null comment '规划ID',
biz_task_id bigint not null comment '业务任务ID',
env_id bigint not null comment '环境ID',
plan_type varchar(10) not null comment '规划类型' default 'carry',
executor_id varchar(50) not null comment '执行器ID',
seq integer not null comment '规划序号',
@ -46,6 +48,7 @@ create table rcs_task_device
device_task_id bigint not null comment '设备任务ID',
plan_task_id bigint not null comment '规划ID',
biz_task_id bigint not null comment '业务任务ID',
env_id bigint not null comment '环境ID',
device_type varchar(50) not null comment '设备类型',
device_item_id varchar(50) not null comment '执行器ID',
seq integer not null comment '执行序号',
@ -65,4 +68,21 @@ create table rcs_task_device
create index idx_rcs_task_device1 on rcs_task_device (biz_task_id);
create index idx_rcs_task_device2 on rcs_task_device (plan_task_id);
drop table if exists lcc_env_info;
create table lcc_env_info
(
env_id bigint not null comment '环境ID',
world_id varchar(50) not null comment '世界地图ID',
env_name varchar(50) not null comment '环境名称',
is_virtual boolean not null default false comment '是否虚拟环境',
env_payload varchar(3000) not null default 'N/A' comment '环境负载信息',
create_at timestamp not null comment '创建时间',
create_by varchar(50) not null comment '创建人',
update_at timestamp not null comment '更新时间',
update_by varchar(50) not null comment '更新人',
primary key (env_id)
);
create index idx_lcc_env_info_1 on lcc_env_info (world_id);
```

3
src/core/ModelUtils.ts

@ -1,7 +1,6 @@
import * as THREE from 'three'
import type Viewport from '@/core/engine/Viewport'
import { Vector2 } from 'three/src/math/Vector2'
import EventBus from '@/runtime/EventBus.ts'
import Decimal from 'decimal.js'
import type { LineLike, Object3DLike } from '@/types/ModelTypes.ts'
import axios from 'axios'
@ -9,7 +8,7 @@ import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
import { TDSLoader } from 'three/examples/jsm/loaders/TDSLoader'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2'
import { worldModel } from '@/core/manager/WorldModel.ts'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial'
import { Line2 } from 'three/examples/jsm/lines/Line2'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry'

6
src/core/manager/CodeDropper.ts

@ -42,7 +42,7 @@ export default class CodeDropper extends BaseInteraction {
droppedItemCallback: (item: ItemJson, pos: THREE.Vector3, evt: MouseEvent) => boolean
}
defineModule({
name: 'CodeDropper',
const ITEM_NAME = 'CodeDropper'
defineModule(ITEM_NAME, () => ({
interaction: CodeDropper.INSTANCE
})
}))

4
src/core/manager/EntityManager.ts

@ -473,11 +473,11 @@ export default class EntityManager {
}
}
appendLineObject(id: string, lines: LineLike) {
appendLineObject(id: string, lines: any) {
this.__lineMap.set(id, lines)
}
findLineObjectById(lineId: string): LineLike | undefined {
findLineObjectById(lineId: string): any | undefined {
return this.__lineMap.get(lineId)
}

44
src/core/manager/ModuleManager.ts

@ -1,17 +1,20 @@
import * as THREE from 'three'
import BaseRenderer from '@/core/base/BaseRenderer'
import BaseInteraction from '@/core/base/BaseInteraction'
import BaseEntity from '@/core/base/BaseItemEntity'
import type { PropertySetter } from "@/core/base/PropertyTypes.ts";
import type { PropertySetter } from '@/core/base/PropertyTypes.ts'
// Define the ModuleDefineOption interface
export interface ModuleDefineOption {
/**
*
*
*/
name: string;
renderer?: BaseRenderer;
/**
*
*/
interaction?: BaseInteraction;
/**
*
*/
setter?: PropertySetter;
}
@ -22,12 +25,19 @@ window['modules'] = modules
/**
*
*/
export function defineModule(option: ModuleDefineOption): Promise<any> {
if (modules.has(option.name)) {
throw new Error(`Module with name "${option.name}" is already defined.`)
export function defineModule(name: string, optionFn: () => ModuleDefineOption): Promise<any> {
if (modules.has(name)) {
throw new Error(`Module with name "${name}" is already defined.`)
}
return new Promise((resolve) => {
if (typeof optionFn !== 'function') {
console.log(`Module "${name}" is not a function, using it directly.`)
debugger
}
modules.set(option.name, option)
return option.renderer?.init()
const option = optionFn()
modules.set(name, option)
option.renderer?.init().then(resolve)
})
}
/**
@ -62,17 +72,5 @@ export function getInteraction<T extends BaseInteraction>(name: string): T {
export function getSetter(name: string) {
const module = getModuleOption(name)
return module.setter;
}
/**
* ,
*
*/
export function createEntity<T extends BaseEntity>(name: string, itemjson: ItemJson, objects: THREE.Object3D[]): T {
const module = getModuleOption(name)
const v = new module.entity() as T
v.setItem(itemjson)
v.setObjects(objects)
return v
return module.setter
}

67
src/core/manager/RunManager.ts

@ -0,0 +1,67 @@
import { Request } from '@ease-forge/shared'
/**
*
*/
class RunManager {
// 是否正在运行
isRunning: boolean = false
/**
*
*/
async getAllEnv(worldId: string): Promise<ServerResponse<EnvInfo[]>> {
// system.invokeServer('')
if (!worldId) {
return Promise.resolve({ success: true, data: [], msg: '' })
}
const res = await Request.request.post('/api/workbench/EnvController@getAllEnv', {
worldId: worldId
})
for (const env of res.data) {
// payload 转换为 json 数据
if (env.env_payload) {
try {
env.env_payload = JSON.parse(env.env_payload)
} catch (e) {
console.error('解析环境负载失败:', e)
env.env_payload = {}
}
}
}
return res
}
/**
*
* @param worldId ID
* @param envName
* @param isVirtual
*/
async createEnv(worldId: string, envName: string, isVirtual: boolean) {
throw new Error('Method not implemented.')
}
/**
*
*/
async run(timeRate: number, envId: number) {
if (this.isRunning) {
await this.stop()
}
// 启动 websocket 监听等等
}
/**
*
*/
async stop() {
// 停止 websocket 监听等等
}
}
const runManager = new RunManager()
export default runManager

56
src/core/manager/WorldModel.ts

@ -1,18 +1,6 @@
import _, { cloneDeep } from 'lodash'
import { reactive, watch } from 'vue'
import EventBus from '@/runtime/EventBus'
import Measure from '@/modules/measure'
import Way from '@/modules/way'
import Gstore from '@/modules/gstore'
import Rack from '@/modules/rack'
import ShuttleRack from '@/modules/shuttle_rack'
import Pallet from '@/modules/pallet'
import Tote from '@/modules/tote'
// import Agv1 from '@/modules/agv1'
import Carton from '@/modules/carton'
import Cl2 from '@/modules/cl2'
import Clx from '@/modules/clx'
import Charger from '@/modules/charger'
import StateManager from '@/core/manager/StateManager.ts'
import { getQueryParams, setQueryParam } from '@/utils/webutils.ts'
import localforage from 'localforage'
@ -30,6 +18,14 @@ export interface WorldModelState {
stateManagerId: string // 当前楼层的状态管理器id
isDraft: boolean // 是否是草稿数据
runState: {
currentEnvId: number,
isLoading: boolean,
isRunning: boolean,
isVirtual: boolean,
timeRate: number,
}
}
/**
@ -53,7 +49,15 @@ export default class WorldModel {
catalogCode: '', // 当前楼层的目录代码
stateManagerId: '', // 当前楼层的状态管理器id, 一般是 项目ID+目录项ID
isDraft: false // 是否是草稿数据, 如果是草稿数据, 则不需要再从服务器加载数据
isDraft: false, // 是否是草稿数据, 如果是草稿数据, 则不需要再从服务器加载数据
runState: {
currentEnvId: 0,
isLoading: false,
isRunning: false,
isVirtual: false,
timeRate: 1
}
})
get gridOption(): IGridHelper {
@ -85,18 +89,17 @@ export default class WorldModel {
init() {
// 观察 this.state.catalogCode 的变化, 如果变化就调用 catalogCodeChange 方法
return Promise.all([
Measure,
Way,
Gstore,
Rack,
ShuttleRack,
Pallet,
Tote,
Carton,
Cl2,
Clx,
// Agv1,
Charger
import('../../modules/measure'),
import('../../modules/way'),
import('../../modules/gstore'),
import('../../modules/rack'),
import('../../modules/shuttle_rack'),
import('../../modules/pallet'),
import('../../modules/tote'),
import('../../modules/carton'),
import('../../modules/cl2'),
import('../../modules/clx'),
import('../../modules/charger')
]).then(() => {
console.log('世界模型初始化完成')
@ -225,3 +228,6 @@ export default class WorldModel {
})
}
}
const worldModel = new WorldModel()
export { worldModel }

2
src/editor/CatalogDefine.vue

@ -3,6 +3,7 @@ import { computed, createVNode, reactive, useTemplateRef } from "vue";
import { ElButton, ElSpace, ElTree } from "element-plus";
import YvSrcEditor from "@/components/YvSrcEditor.vue";
import DataForm from "@/components/data-form/DataForm.vue";
import {worldModel} from '@/core/manager/WorldModel.ts'
import lodash from "lodash";
defineOptions({
@ -39,7 +40,6 @@ interface CatalogDefineData {
//
const data: CatalogDefineData = {};
const tree = useTemplateRef<InstanceType<typeof ElTree>>("treeRef");
const worldModel = computed(() => window['worldModel']);
const catalog = computed<Array<any>>(() => {
// state.forceUpdateForCatalog;
return worldModel.value?.state?.catalog;

1
src/editor/Model2DEditor.vue

@ -104,6 +104,7 @@ import { renderIcon, setQueryParam } from '@/utils/webutils'
import { createVNode, defineComponent, markRaw } from 'vue'
import Viewport from '@/core/engine/Viewport'
import Constract from '@/core/Constract'
import { worldModel } from '@/core/manager/WorldModel.ts'
import EventBus from '@/runtime/EventBus'
import SceneHelp from '@/core/engine/SceneHelp'
import BulkCopy from './BulkCopy.vue'

47
src/editor/ModelMain.vue

@ -8,6 +8,36 @@
>{{ rootMenu.label }}
<component :is="renderIcon('element ArrowDown')"></component>
</div>
<div style="flex-grow: 1;"></div>
<div v-if="isModelOpen" style="display: flex; flex-direction: row; align-items: center; margin-right: 10px;">
<div class="field-block" style="margin-right: 5px;">
<el-select style="width:130px;" placeholder="运行环境"
v-model="worldModelState.runState.currentEnvId">
<el-option v-for="env in envList" :key="env.env_id" :label="env.env_name" :value="env.env_id"></el-option>
<template #footer>
<el-button size="small" type="primary" plain>创建运行环境</el-button>
<el-button size="small" :icon="renderIcon('Refresh')" @click="reloadEnvList" />
</template>
</el-select>
</div>
<div class="field-block" style="margin-right: 5px;">
<el-select style="width:130px;" placeholder="时间速率"
v-model="worldModelState.runState.timeRate">
<el-option label="1x" :value="1"></el-option>
<el-option label="2x" :value="2"></el-option>
<el-option label="10x" :value="10"></el-option>
</el-select>
</div>
<el-button :icon="renderIcon('Play')" type="primary"
v-if="!worldModelState.runState.isRunning"
:disabled="!worldModelState.runState.currentEnvId"
:loading="worldModelState.runState.isLoading">启动
</el-button>
<el-button :icon="renderIcon('Stop')" type="danger" plain
v-if="worldModelState.runState.isRunning"
:loading="worldModelState.runState.isLoading">停止
</el-button>
</div>
</div>
<div class="user">
<span style="margin-right: 10px;">
@ -18,7 +48,7 @@
</span>
</div>
</div>
<div class="app-section" v-if="worldModel.state.isOpened">
<div class="app-section" v-if="worldModelState.isOpened">
<div class="btns-toolbar btns-toolbar-left">
<div class="btns btns-top">
<template v-for="panel in getWidgetBySide('left')">
@ -121,6 +151,8 @@ import Logo from '@/assets/images/logo.png'
import './ModelMain.less'
import EventBus from '@/runtime/EventBus.js'
import { useRouter } from 'vue-router'
import { worldModel } from '@/core/manager/WorldModel.ts'
import runManager from '@/core/manager/RunManager.js'
export default {
components: { Model2DEditor, Model3DViewer, Split, SplitArea, CatalogDefine },
@ -158,6 +190,7 @@ export default {
}
})
})
this.reloadEnvList()
EventBus.on('dataLoadComplete', (data) => {
const { stateManager } = data
if (stateManager) {
@ -174,6 +207,7 @@ export default {
data() {
return {
Logo,
envList: [],
isShowEditor: false,
editorHash: 0,
currentViewport: null,
@ -193,10 +227,10 @@ export default {
},
computed: {
isModelOpen() {
return this.worldModel.state.isOpened
return this.worldModelState.isOpened
},
worldModel() {
return window['worldModel']
worldModelState() {
return worldModel.state
},
calcLeftPanel() {
if (!this.sectionLeftName || this.hideLeft) {
@ -245,6 +279,11 @@ export default {
methods: {
renderIcon,
getWidgetBySide,
reloadEnvList() {
runManager.getAllEnv(this.worldModelState.project_uuid).then(res => {
this.envList = res.data
})
},
toHome() {
system.router.push({ name: 'home' })
},

5
src/editor/ModelMainInit.ts

@ -13,10 +13,10 @@ import FileMenu from './menus/FileMenu'
import EditMenu from './menus/EditMenu'
import ToolsMenu from './menus/Tools'
import Model3DView from './menus/Model3DView'
import { worldModel } from '@/core/manager/WorldModel.ts'
import { install as ThreeExtendInstall } from '@/core/ThreeExtend.ts'
import { forEachMenu } from '@/runtime/DefineMenu'
import { normalizeShortKey } from '@/utils/webutils'
import WorldModel from '@/core/manager/WorldModel'
/**
*
@ -36,9 +36,6 @@ export function ModelMainInit() {
ToolsMenu.install()
Model3DView.install()
const worldModel = new WorldModel()
window['worldModel'] = worldModel
ThreeExtendInstall()
}

55
src/editor/menus/FileMenu.ts

@ -6,32 +6,33 @@ import type Viewport from '@/core/engine/Viewport.ts'
import DataForm from '@/components/data-form/DataForm.vue'
import OpenProject from '../OpenProject.vue'
import { Request } from '@ease-forge/shared'
import { worldModel } from '@/core/manager/WorldModel.ts'
function addProject(successful?: Function) {
const data = {
server: window.location.origin,
};
server: window.location.origin
}
system.showDialog(createVNode(DataForm, {
style: {
paddingRight: "12px",
paddingRight: '12px'
},
data: data,
formFields: [
{
dataPath: 'projectUuid', label: '项目编号', input: 'Input',
inputProps: {
placeholder: '请输入项目唯一编号',
},
placeholder: '请输入项目唯一编号'
}
},
{
dataPath: 'projectLabel', label: '项目标题', input: 'Input',
inputProps: {
placeholder: '请输入项目标题',
},
},
placeholder: '请输入项目标题'
}
}
],
columnCount: 1,
labelWidth: "80px",
labelWidth: '80px'
}), {
title: '创建项目',
width: 480,
@ -40,15 +41,15 @@ function addProject(successful?: Function) {
showMax: false,
showCancelButton: true,
showOkButton: true,
okButtonText: "创建",
cancelButtonText: "取消",
okButtonText: '创建',
cancelButtonText: '取消'
}).then(() => {
Request.request.post("/api/workbench/LccModelManager@addProject", data).then(()=> {
system.msg("创建成功");
successful?.();
Request.request.post('/api/workbench/LccModelManager@addProject', data).then(() => {
system.msg('创建成功')
successful?.()
})
}).finally();
}).finally()
}
export default defineMenu((menus) => {
@ -80,14 +81,14 @@ export default defineMenu((menus) => {
}
},
click2: async () => {
let dialog: any = null;
let dialog: any = null
system.showDialog(createVNode(OpenProject, {
onCancel: () => dialog?.onClose(),
onOpen: async row => {
dialog?.onClose();
const veryBigData = JSON.parse(row.otherData);
veryBigData.catalog = JSON.parse(row.directoryData);
veryBigData.items = [];
dialog?.onClose()
const veryBigData = JSON.parse(row.otherData)
veryBigData.catalog = JSON.parse(row.directoryData)
veryBigData.items = []
worldModel.state.isOpened = false
worldModel.state.isDraft = false
worldModel.state.catalog = []
@ -104,8 +105,8 @@ export default defineMenu((menus) => {
}
},
onAdd: that => {
addProject(() => that.loadData());
},
addProject(() => that.loadData())
}
}), {
title: '打开项目',
width: 1500,
@ -114,10 +115,10 @@ export default defineMenu((menus) => {
showMax: true,
showCancelButton: false,
showOkButton: false,
okButtonText: "创建",
cancelButtonText: "取消",
onMounted: d => dialog = d,
}).finally();
okButtonText: '创建',
cancelButtonText: '取消',
onMounted: d => dialog = d
}).finally()
}
},
{
@ -131,7 +132,7 @@ export default defineMenu((menus) => {
{
name: 'saveAs', label: '新建项目', icon: renderIcon('ModelFile'), order: 3,
click: () => {
addProject();
addProject()
}
}
]

1
src/editor/widgets/modeltree/ModeltreeViewJs.js

@ -1,6 +1,7 @@
import { defineComponent, markRaw } from 'vue'
import YvTable from '@/components/yvTable/YvTable.vue'
import IWidgets from '../IWidgets.js'
import { worldModel } from '@/core/manager/WorldModel.ts'
export default defineComponent({
name: 'ModeltreeView',

48
src/example/example1.js

@ -155,6 +155,26 @@ export default {
catalogCode: 'f2', t: 'floor',
items: [
{
id: 'm1', t: 'measure', v: true,
tf: [[0, 0, 0], [90, 0, 0], [0.1, 0.1, 0.1]],
dt: { in: [], out: [], center: ['m2', 'm4'] }
},
{
id: 'm2', t: 'measure', v: true,
tf: [[8.4, 0, 0], [90, 0, 0], [0.1, 0.1, 0.1]],
dt: { in: [], out: [], center: ['m1', 'm3'] }
},
{
id: 'm3', t: 'measure', v: true,
tf: [[8.4, 0, 9], [90, 0, 0], [0.1, 0.1, 0.1]],
dt: { in: [], out: [], center: ['m2', 'm4'] }
},
{
id: 'm4', t: 'measure', v: true,
tf: [[0, 0, 9], [90, 0, 0], [0.1, 0.1, 0.1]],
dt: { in: [], out: [], center: ['m3', 'm1'] }
},
{
id: 'rack1',
t: 'rack',
v: true,
@ -182,26 +202,6 @@ export default {
}
},
{
id: 'm1', t: 'measure', v: true,
tf: [[0, 0, 0], [90, 0, 0], [0.1, 0.1, 0.1]],
dt: { in: [], out: [], center: ['m2', 'm4'] }
},
{
id: 'm2', t: 'measure', v: true,
tf: [[8.4, 0, 0], [90, 0, 0], [0.1, 0.1, 0.1]],
dt: { in: [], out: [], center: ['m1', 'm3'] }
},
{
id: 'm3', t: 'measure', v: true,
tf: [[8.4, 0, 9], [90, 0, 0], [0.1, 0.1, 0.1]],
dt: { in: [], out: [], center: ['m2', 'm4'] }
},
{
id: 'm4', t: 'measure', v: true,
tf: [[0, 0, 9], [90, 0, 0], [0.1, 0.1, 0.1]],
dt: { in: [], out: [], center: ['m3', 'm1'] }
},
{
id: '1', t: 'way', v: true,
logicX: 11,
logicY: 10,
@ -433,14 +433,14 @@ export default {
dt: { in: [], out: [], center: [], clxWidth: 1.65, clxDepth: 1.65, clxHeight: 3.393 }
},
{
id: 'charger1', t: 'charger', v: true,
id: 'charger1', t: 'way', v: true,
tf: [[2.696, 0, 8.75], [0, 0, 0], [1, 1, 1]],
dt: { in: ['8'], out: ['8'], center: [], chargerWidth: 0.53, chargerDepth: 0.275, clxHeight: 0.3 }
dt: { in: ['8'], out: ['8'], center: [], isCharger: true }
},
{
id: 'charger2', t: 'charger', v: true,
id: 'charger2', t: 'way', v: true,
tf: [[5.655, 0, 8.75], [0, 0, 0], [1, 1, 1]],
dt: { in: ['27'], out: ['27'], center: [], chargerWidth: 0.53, chargerDepth: 0.275, clxHeight: 0.3 }
dt: { in: ['27'], out: ['27'], center: [], isCharger: true }
},
{
id: 'pallet1122', // 托盘唯一编码

2
src/main.ts

@ -25,7 +25,7 @@ async function main() {
app.use(webIndex)
app.mount('#app')
globalConfig()
await getCurrentUser()
// await getCurrentUser()
}
main().finally()

7
src/modules/agv1/index.ts

@ -5,9 +5,8 @@ import propertySetter from './Agv1PropertySetter.ts'
export const ITEM_TYPE_NAME = 'agv1'
export default defineModule({
name: ITEM_TYPE_NAME,
export default defineModule(ITEM_TYPE_NAME, () => ({
renderer: new Agv1Renderer(ITEM_TYPE_NAME),
interaction: new Agv1Interaction(ITEM_TYPE_NAME),
setter: propertySetter,
})
setter: propertySetter
}))

9
src/modules/carton/index.ts

@ -1,13 +1,12 @@
import { defineModule } from '@/core/manager/ModuleManager.ts'
import CartonRenderer from './CartonRenderer.ts'
import CartonInteraction from './CartonInteraction.ts'
import propertySetter from "./CartonPropertySetter.ts";
import propertySetter from './CartonPropertySetter.ts'
export const ITEM_TYPE_NAME = 'carton'
export default defineModule({
name: ITEM_TYPE_NAME,
export default defineModule(ITEM_TYPE_NAME, () => ({
renderer: new CartonRenderer(ITEM_TYPE_NAME),
interaction: new CartonInteraction(ITEM_TYPE_NAME),
setter: propertySetter,
})
setter: propertySetter
}))

9
src/modules/charger/index.ts

@ -1,13 +1,12 @@
import { defineModule } from '@/core/manager/ModuleManager.ts'
import ChargerRenderer from './ChargerRenderer.ts'
import ChargerInteraction from './ChargerInteraction.ts'
import propertySetter from "@/modules/charger/ChargerPropertySetter.ts";
import propertySetter from '@/modules/charger/ChargerPropertySetter.ts'
export const ITEM_TYPE_NAME = 'charger'
export default defineModule({
name: ITEM_TYPE_NAME,
export default defineModule(ITEM_TYPE_NAME, () => ({
renderer: new ChargerRenderer(ITEM_TYPE_NAME),
interaction: new ChargerInteraction(ITEM_TYPE_NAME),
setter: propertySetter,
})
setter: propertySetter
}))

9
src/modules/cl2/index.ts

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

8
src/modules/clx/index.ts

@ -1,13 +1,13 @@
import { defineModule } from '@/core/manager/ModuleManager.ts'
import ClxRenderer from './ClxRenderer.ts'
import ClxInteraction from './ClxInteraction.ts'
import propertySetter from "@/modules/clx/ClxPropertySetter.ts";
import propertySetter from '@/modules/clx/ClxPropertySetter.ts'
export const ITEM_TYPE_NAME = 'clx'
export default defineModule({
export default defineModule(ITEM_TYPE_NAME, () => ({
name: ITEM_TYPE_NAME,
renderer: new ClxRenderer(ITEM_TYPE_NAME),
interaction: new ClxInteraction(ITEM_TYPE_NAME),
setter: propertySetter,
})
setter: propertySetter
}))

9
src/modules/gstore/index.ts

@ -1,13 +1,12 @@
import { defineModule } from '@/core/manager/ModuleManager.ts'
import GstoreRenderer from './GstoreRenderer.ts'
import GstoreInteraction from './GstoreInteraction.ts'
import propertySetter from "@/modules/gstore/GstorePropertySetter.ts";
import propertySetter from '@/modules/gstore/GstorePropertySetter.ts'
export const ITEM_TYPE_NAME = 'gstore'
export default defineModule({
name: ITEM_TYPE_NAME,
export default defineModule(ITEM_TYPE_NAME, () => ({
renderer: new GstoreRenderer(ITEM_TYPE_NAME),
interaction: new GstoreInteraction(ITEM_TYPE_NAME),
setter: propertySetter,
})
setter: propertySetter
}))

11
src/modules/measure/index.ts

@ -1,13 +1,14 @@
import { defineModule } from '@/core/manager/ModuleManager.ts'
import MeasureRenderer from './MeasureRenderer.ts'
import MeasureInteraction from './MeasureInteraction.ts'
import propertySetter from "@/modules/measure/MeasurePropertySetter.ts";
import propertySetter from '@/modules/measure/MeasurePropertySetter.ts'
export const ITEM_TYPE_NAME = 'measure'
export default defineModule({
name: ITEM_TYPE_NAME,
console.log('MeasureRenderer initialized at', performance.now())
export default defineModule(ITEM_TYPE_NAME, () => ({
renderer: new MeasureRenderer(ITEM_TYPE_NAME),
interaction: new MeasureInteraction(ITEM_TYPE_NAME),
setter: propertySetter,
})
setter: propertySetter
}))

9
src/modules/pallet/index.ts

@ -1,13 +1,12 @@
import { defineModule } from '@/core/manager/ModuleManager.ts'
import PalletRenderer from './PalletRenderer.ts'
import PalletInteraction from './PalletInteraction.ts'
import propertySetter from "@/modules/pallet/PalletPropertySetter.ts";
import propertySetter from '@/modules/pallet/PalletPropertySetter.ts'
export const ITEM_TYPE_NAME = 'pallet'
export default defineModule({
name: ITEM_TYPE_NAME,
export default defineModule(ITEM_TYPE_NAME, () => ({
renderer: new PalletRenderer(ITEM_TYPE_NAME),
interaction: new PalletInteraction(ITEM_TYPE_NAME),
setter: propertySetter,
})
setter: propertySetter
}))

760
src/modules/rack/RackRenderer.ts_back

@ -1,760 +0,0 @@
import * as THREE from 'three'
import { BufferGeometry } from 'three'
import BaseRenderer from '@/core/base/BaseRenderer.ts'
import { decimalSumBy } from '@/core/ModelUtils'
import Constract from '@/core/Constract.ts'
import Plastic_Rough_JPG from '@/assets/Models/Plastic_Rough.jpg?inline'
import storageBar_PNG from '@/assets/Models/storageBar.png?inline'
import { Material } from 'three/src/materials/Material'
import { InstancedMesh } from 'three/src/objects/InstancedMesh'
//@ts-ignore
import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js'
import type Viewport from '@/core/engine/Viewport.ts'
import type { Object3DLike } from '@/types/ModelTypes.ts'
/**
* 货架货位渲染器
*/
export default class RackRenderer extends BaseRenderer {
static POINT_NAME = 'rack'
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.05
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.')
}
/**
* 获取物品存放在货架后的位置和旋转角度
* item.dt.storeAt 必须存在, 比如 { item:'货架ID', bay:0, level:1, cell:0 }
*/
getStorePlacement(viewport: Viewport, item: ItemJson): { position: [number, number, number], rotation: [number, number, number] } {
if (!item.dt?.storeAt?.item) {
// 没有定义存储位置,返回空对象
//@ts-ignore
return {}
}
const bay = item.dt?.storeAt?.bay || 0
const level = item.dt?.storeAt?.level || 0
// 暂时不用算格子 const cell = item.dt?.storeAt?.cell || 0
// 目标货架
const rack = viewport.stateManager.findItemById(item.dt.storeAt.item)
const rackWidth = decimalSumBy(rack.dt.bays, (b: any) => b.bayWidth)
const bays = rack.dt.bays
const levelHeights = rack.dt.bays[bay]?.levelHeight
// 局部坐标系下的偏移量
let localX = 0
for (let i = 0; i < bay; i++) {
localX += bays[i]?.bayWidth || 0
}
localX += bays[bay].bayWidth / 2 // 居中
let localY = 0
for (let i = 0; i < level; i++) {
localY += levelHeights[i]
}
// 构建局部偏移向量
const rackPos = new THREE.Vector3(...rack.tf[0])
const offset = new THREE.Vector3(
localX - rackWidth / 2, // 相对坐标从最左边开始,
localY, 0)
// 应用货架的旋转
const q = new THREE.Quaternion()
q.setFromEuler(new THREE.Euler(
THREE.MathUtils.degToRad(rack.tf[1][0]),
THREE.MathUtils.degToRad(rack.tf[1][1]),
THREE.MathUtils.degToRad(rack.tf[1][2])
))
offset.applyQuaternion(q)
const worldPosition = rackPos.clone().add(offset)
return {
position: [
worldPosition.x,
worldPosition.y + this.bottomBarHeight + 0.1, // 加上横梁高度
worldPosition.z
],
rotation: [
// 托盘旋转90度才能放进货架
rack.tf[1][0],
rack.tf[1][1] + 90,
rack.tf[1][2]
]
}
}
updatePoint(item: ItemJson, group: THREE.Group, option?: RendererCudOption): THREE.Group {
group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2])
group.rotation.set(
THREE.MathUtils.degToRad(item.tf[1][0]),
THREE.MathUtils.degToRad(item.tf[1][1]),
THREE.MathUtils.degToRad(item.tf[1][2])
)
// group.scale 不允许修改!!
// 禁止缩放,
item.tf[2][0] = item.dt.rackWidth
item.tf[2][1] = item.dt.rackHeight
// 更新放在内部的所有箱子
const subItems = this.tempViewport.runtimeManager.getItemsByRack(item.id)
const viewport = this.tempViewport
if (subItems) {
_.defer(() => {
viewport.stateManager.update(({ getEntity, putEntity, deleteEntity, addEntity }) => {
for (const subItemId of subItems) {
const subItem = getEntity(subItemId)
if (subItem) {
const { position, rotation } = this.getStorePlacement(viewport, subItem)
if (position) {
subItem.tf[0][0] = position[0]
subItem.tf[0][1] = position[1]
subItem.tf[0][2] = position[2]
subItem.tf[1][0] = rotation[0]
subItem.tf[1][1] = rotation[1]
subItem.tf[1][2] = rotation[2]
putEntity(subItem)
}
}
}
})
})
}
return group
}
createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D {
// 创建平面几何体
if (!item.dt.bays || !item.dt.rackDepth) {
system.showErrorDialog('RackRenderer field bays / rackDepth is null!')
return null
}
const group = new THREE.Group()
group.name = RackRenderer.POINT_NAME
const rackWidth = decimalSumBy(item.dt.bays, (b: any) => b.bayWidth)
const heights = []
for (let i = 0; i < item.dt.bays.length; i++) {
const bay = item.dt.bays[i]
const bayHeight = decimalSumBy(bay.levelHeight)
heights.push(bayHeight)
}
const rackHeight = _.max(heights)
// // 绘制背景矩形框
// const planeGeometry = new THREE.PlaneGeometry(rackWidth, item.dt.rackDepth)
//
// planeGeometry.rotateX(Math.PI / 2)
//
// const planeMaterial = new THREE.MeshBasicMaterial({
// color: '#9a9090',
// transparent: true, // 启用透明
// opacity: 0.5, // 50%透明度
// depthWrite: false, // 防止深度冲突
// side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"}
// })
// const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)
// group.add(planeMesh)
//
//
// // 绘制边框
// const lineXLen = rackWidth - this.defaultLineWidth
// const lineYLen = item.dt.rackDepth - this.defaultLineWidth
//
// const lineGeometry = new LineGeometry().setPositions([
// -(lineXLen / 2), 0, -(lineYLen / 2),
// lineXLen / 2, 0, -(lineYLen / 2),
// lineXLen / 2, 0, lineYLen / 2,
// -(lineXLen / 2), 0, lineYLen / 2,
// -(lineXLen / 2), 0, -(lineYLen / 2)
// ])
// const lineMaterial = new LineMaterial({
// color: '#0d89a5',
// linewidth: this.defaultLineWidth,
// worldUnits: true,
// resolution: new THREE.Vector2(window.innerWidth, window.innerHeight),
// side: THREE.DoubleSide
// })
// const line = new Line2(lineGeometry, lineMaterial)
// group.add(line as THREE.Object3D)
//
// let lineDistanceX = 0
//
// for (let i = 0; item.dt.bays.length > 1 && i < item.dt.bays.length - 1; i++) {
// const bay = item.dt.bays[i]
// lineDistanceX += bay.bayWidth
// const lineGeometryT = new LineGeometry().setPositions([
// -(lineDistanceX) + (lineXLen / 2), 0, lineYLen / 2,
// -(lineDistanceX) + (lineXLen / 2), 0, -(lineYLen / 2)
// ])
// const lineT = new Line2(lineGeometryT, lineMaterial)
// group.add(lineT as THREE.Object3D)
// }
const meshes = this.createRack(item, option)
meshes.forEach(mesh => {
group.add(mesh)
mesh.instanceMatrix.needsUpdate = true
})
// 设置位置
group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2])
group.rotation.set(
THREE.MathUtils.degToRad(item.tf[1][0]),
THREE.MathUtils.degToRad(item.tf[1][1]),
THREE.MathUtils.degToRad(item.tf[1][2])
)
item.dt.rackWidth = rackWidth
item.dt.rackHeight = rackHeight
item.tf[2][0] = item.dt.rackWidth
item.tf[2][1] = item.dt.rackHeight
return group
}
dispose() {
super.dispose()
this.pointMaterial?.dispose()
}
rackVerticalBarWidth = 0.1
rackVerticalBarDepth = 0.08
rackVerticalBarColor = 0xFF35499C
rackVerticalBarGeometry: BufferGeometry = null
rackVerticalBarMaterial: Material = null
rackLinkBarColor = 0xFF35499C
rackLinkBarGeometry: BufferGeometry = null
rackLinkBarMaterial: Material = null
rackHorizontalBarWidth = 0.1
rackHorizontalBarDepth = 0.08
rackHorizontalBarColor = 0xFFF97F27
rackHorizontalBarGeometry: BufferGeometry = null
rackHorizontalBarMaterial: Material = null
bottomBarHeight = 0.2
bottomLinkHeight = 0.2
barSectionPoints = [
{ x: -0.05, y: -0.05 },
{ x: -0.025, y: -0.05 },
{ x: -0.01, y: -0.045 },
{ x: 0.01, y: -0.045 },
{ x: 0.025, y: -0.05 },
{ x: 0.05, y: -0.05 },
{ x: 0.05, y: 0.042 },
{ x: 0.042, y: 0.05 },
{ x: 0.025, y: 0.05 },
{ x: 0.025, y: 0.042 },
{ x: 0.042, y: 0.042 },
{ x: 0.042, y: -0.042 },
{ x: -0.042, y: -0.042 },
{ x: -0.042, y: 0.042 },
{ x: -0.025, y: 0.042 },
{ x: -0.025, y: 0.05 },
{ x: -0.042, y: 0.05 },
{ x: -0.05, y: 0.042 },
{ x: -0.05, y: -0.05 }
]
linkSectionPoints = [
{ x: -0.05, y: -0.05 },
{ x: -0.05, y: 0.05 },
{ x: 0, y: 0.05 },
{ x: 0, y: 0.06 },
{ x: -0.06, y: 0.06 },
{ x: -0.06, y: -0.05 },
{ x: -0.05, y: -0.05 }
]
linkBarSectionPoints = [
{ x: -0.025, y: -0.025 },
{ x: 0.025, y: -0.025 },
{ x: 0.025, y: 0.025 },
{ x: -0.025, y: 0.025 },
{ x: -0.025, y: -0.025 }
]
createVerticalBar(x, y, z, length): THREE.BufferGeometry {
// 创建一个形状 柱子的截面形状
const shape = new THREE.Shape()
shape.moveTo(this.barSectionPoints[0].x, this.barSectionPoints[0].y)
for (let i = 1; i < this.barSectionPoints.length; i++) {
shape.lineTo(this.barSectionPoints[i].x, this.barSectionPoints[i].y)
}
// 拉伸轨迹线
const curve = new THREE.CatmullRomCurve3(
[new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, length, 0)],
false, // 闭合曲线
'catmullrom',
0
)
// 挤出几何图形 参数
const options = {
steps: 1,
bevelEnabled: false,
extrudePath: curve // 设置挤出轨迹
}
// 创建挤出几何体
const geometry = new THREE.ExtrudeGeometry(shape, options)
// 调整uv方便正确贴图
this.resetUVs(geometry)
return geometry
}
createVerticalBarMaterial(): THREE.Material {
let textureLoader = new THREE.TextureLoader()
// 加载纹理
const textureHole = textureLoader.load(storageBar_PNG) // 孔洞
const textureMaterial = textureLoader.load(Plastic_Rough_JPG) // 表面材质
textureHole.repeat.set(10, 18) // X轴重复,Y轴重复
textureMaterial.repeat.set(2, 2) // X轴重复,Y轴重复
// textureHole.offset.set(0.5, 0)
// textureHole.center.set(0.5, 0)
// 必须设置包裹模式为重复
textureHole.wrapS = THREE.RepeatWrapping
textureHole.wrapT = THREE.RepeatWrapping
textureMaterial.wrapS = THREE.RepeatWrapping
textureMaterial.wrapT = THREE.RepeatWrapping
const material = new THREE.MeshPhongMaterial()
material.alphaMap = textureHole
material.normalMap = textureMaterial
material.color.setHex(this.rackVerticalBarColor, 'srgb')
material.specular.setHex(0xff6d6d6d, 'srgb')
material.transparent = true
material.needsUpdate = true
return material
}
createLinkBar(x, y, z, vBarLength, depth, bottomDistance, topDistance): THREE.BufferGeometry {
const bgs: BufferGeometry[] = []
const top = vBarLength - topDistance
// 创建一个形状 柱子的截面形状
const shape = new THREE.Shape()
shape.moveTo(this.linkBarSectionPoints[0].x, this.linkBarSectionPoints[0].y)
for (let i = 1; i < this.linkBarSectionPoints.length; i++) {
shape.lineTo(this.linkBarSectionPoints[i].x, this.linkBarSectionPoints[i].y)
}
// 拉伸轨迹线 横向 底部
const curveHBottom = new THREE.CatmullRomCurve3(
[new THREE.Vector3(0, bottomDistance, 0), new THREE.Vector3(0, bottomDistance, depth)],
false, // 闭合曲线
'catmullrom',
0
)
// 挤出几何图形 参数
const optionsHBottom = {
steps: 1,
bevelEnabled: false,
extrudePath: curveHBottom // 设置挤出轨迹
}
// 拉伸轨迹线 横向 底部
const curveHTop = new THREE.CatmullRomCurve3(
[new THREE.Vector3(0, top, 0), new THREE.Vector3(0, top, depth)],
false, // 闭合曲线
'catmullrom',
0
)
// 挤出几何图形 参数
const optionsHTop = {
steps: 1,
bevelEnabled: false,
extrudePath: curveHTop // 设置挤出轨迹
}
// 创建挤出几何体
const geometryHBottom = new THREE.ExtrudeGeometry(shape, optionsHBottom)
const geometryHTop = new THREE.ExtrudeGeometry(shape, optionsHTop)
bgs.push(geometryHBottom, geometryHTop)
let remainingHeight = vBarLength - bottomDistance - topDistance
// 需要创建斜杆
for (let i = 0; i < Math.floor(remainingHeight / depth); i++) {
// 拉伸轨迹线 斜向
const curveD = new THREE.CatmullRomCurve3(
(i % 2 == 0) ? [new THREE.Vector3(0, bottomDistance + depth * i, 0), new THREE.Vector3(0, bottomDistance + depth * (i + 1), depth)]
: [new THREE.Vector3(0, bottomDistance + depth * (i + 1), 0), new THREE.Vector3(0, bottomDistance + depth * (i), depth)],
false, // 闭合曲线
'catmullrom',
0
)
const optionsD = {
steps: 1,
bevelEnabled: false,
extrudePath: curveD // 设置挤出轨迹
}
const geometryD = new THREE.ExtrudeGeometry(shape, optionsD)
bgs.push(geometryD)
}
if (vBarLength - bottomDistance - topDistance > depth) {
}
// 调整uv方便正确贴图
// this.resetUVs(geometry);
return mergeGeometries(bgs)
}
createLinkBarMaterial(): THREE.Material {
const material = new THREE.MeshPhongMaterial()
material.color.setHex(this.rackLinkBarColor, 'srgb')
material.specular.setHex(0xff6d6d6d, 'srgb')
material.transparent = true
material.needsUpdate = true
return material
}
createHorizontalBar(x, y, z, length): THREE.BufferGeometry {
// 创建一个形状 柱子的截面形状
const shape = new THREE.Shape()
shape.moveTo(this.barSectionPoints[0].x, this.barSectionPoints[0].y)
for (let i = 1; i < this.barSectionPoints.length; i++) {
shape.lineTo(this.barSectionPoints[i].x, this.barSectionPoints[i].y)
}
// 拉伸轨迹线
const curve = new THREE.CatmullRomCurve3(
[new THREE.Vector3(0.05, 0, 0), new THREE.Vector3(length - 0.05, 0, 0)],
false, // 闭合曲线
'catmullrom',
0
)
// 挤出几何图形 参数
const options = {
steps: 1,
bevelEnabled: false,
extrudePath: curve // 设置挤出轨迹
}
// 创建挤出几何体
const geometry = new THREE.ExtrudeGeometry(shape, options)
const linkShapeL = new THREE.Shape()
const linkShapeR = new THREE.Shape()
linkShapeL.moveTo(this.linkSectionPoints[0].x, this.linkSectionPoints[0].y)
linkShapeR.moveTo(this.linkSectionPoints[0].x + (length), this.linkSectionPoints[0].y)
for (let i = 1; i < this.linkSectionPoints.length; i++) {
linkShapeL.lineTo(this.linkSectionPoints[i].x, this.linkSectionPoints[i].y)
linkShapeR.lineTo(this.linkSectionPoints[i].x + (length), this.linkSectionPoints[i].y)
}
// 拉伸轨迹线
const linkCurve = new THREE.CatmullRomCurve3(
[new THREE.Vector3(0, 0, -0.08), new THREE.Vector3(0, 0, 0.08)],
false, // 闭合曲线
'catmullrom',
0
)
// 挤出几何图形 参数
const linkOptions = {
steps: 1,
bevelEnabled: false,
extrudePath: linkCurve // 设置挤出轨迹
}
// 创建挤出几何体
const linkGeometryL = new THREE.ExtrudeGeometry(linkShapeL, linkOptions)
linkGeometryL.rotateZ(-Math.PI / 2)
const linkGeometryR = new THREE.ExtrudeGeometry(linkShapeR, linkOptions)
linkGeometryR.rotateX(-Math.PI)
linkGeometryR.rotateZ(-Math.PI / 2)
// 调整uv方便正确贴图
// this.resetUVs(geometry);
return mergeGeometries([geometry, linkGeometryL, linkGeometryR])
}
createHorizontalBarMaterial(): THREE.Material {
const material = new THREE.MeshPhongMaterial()
material.color.setHex(this.rackHorizontalBarColor, 'srgb')
material.specular.setHex(0xff6d6d6d, 'srgb')
material.transparent = true
material.needsUpdate = true
return material
}
createRack(item: ItemJson, option?: RendererCudOption): InstancedMesh[] {
if (!item.dt.bays || !item.dt.rackDepth) {
system.showErrorDialog('RackRenderer field bays / rackDepth is null!')
return null
}
const rackPoint = {
x: item.tf[0][0],
y: item.tf[0][1],
z: item.tf[0][2]
}
const rackWidth = decimalSumBy(item.dt.bays, (b: any) => b.bayWidth)
const rackDepth = item.dt.rackDepth
const heights = []
for (let i = 0; i < item.dt.bays.length; i++) {
const bay = item.dt.bays[i]
const bayHeight = decimalSumBy(bay.levelHeight)
heights.push(bayHeight)
}
const rackHeight = _.max(heights)
// 计算立住坐标点和长度
const vBarMatrix: { x: number, y: number, z: number, sx: number, sy: number, sz: number, rx: number, ry: number, rz: number, l: number }[] = []
// 计算
const linkBarMatrix: { x: number, y: number, z: number, sx: number, sy: number, sz: number, rx: number, ry: number, rz: number, l: number }[] = []
let distanceX = 0, distanceY = 0
for (let i = -1; i < item.dt.bays.length; i++) {
if (i >= 0) {
const bay = item.dt.bays[i]
distanceX += bay.bayWidth
}
vBarMatrix.push({
x: distanceX - rackWidth / 2,
y: 0,
z: -rackDepth / 2,
sx: 0.8,
sy: 1,
sz: 1,
rx: 0,
ry: Math.PI / 2,
rz: 0,
l: rackHeight
})
vBarMatrix.push({
x: distanceX - rackWidth / 2,
y: 0,
z: item.dt.rackDepth - rackDepth / 2,
sx: 0.8,
sy: 1,
sz: 1,
rx: 0,
ry: -Math.PI / 2,
rz: 0,
l: rackHeight
})
linkBarMatrix.push({
x: distanceX - rackWidth / 2,
y: 0,
z: i % 2 == 0 ? (item.dt.rackDepth - rackDepth / 2) : -rackDepth / 2,
sx: 1,
sy: 1,
sz: 1,
rx: 0,
ry: i % 2 == 0 ? Math.PI : 0,
rz: 0,
l: rackHeight
})
}
// 计算横梁数量
const hBarMatrix: { x: number, y: number, z: number, sx: number, sy: number, sz: number, rx: number, ry: number, rz: number, l: number }[] = []
distanceX = 0
for (let i = 0; i < item.dt.bays.length; i++) {
distanceY = this.bottomBarHeight
const bay = item.dt.bays[i]
for (let j = 0; j < bay.levelHeight.length; j++) {
const levelHeight = bay.levelHeight[j]
if (distanceY <= 0) {
continue
}
hBarMatrix.push({
x: distanceX - rackWidth / 2,
y: distanceY,
z: -rackDepth / 2,
sx: 1,
sy: 0.8,
sz: 1,
rx: Math.PI / 2,
ry: 0,
rz: 0,
l: bay.bayWidth
})
hBarMatrix.push({
x: distanceX - rackWidth / 2,
y: distanceY,
z: item.dt.rackDepth - rackDepth / 2,
sx: 1,
sy: 0.8,
sz: 1,
rx: -Math.PI / 2,
ry: 0,
rz: 0,
l: bay.bayWidth
})
distanceY += levelHeight
}
distanceX += bay.bayWidth
}
const meshes: InstancedMesh[] = []
if (vBarMatrix.length > 0) {
if (!this.rackVerticalBarGeometry) {
this.rackVerticalBarGeometry = this.createVerticalBar(vBarMatrix[0].x, vBarMatrix[0].y, vBarMatrix[0].z, vBarMatrix[0].l)
}
if (!this.rackVerticalBarMaterial) {
this.rackVerticalBarMaterial = this.createVerticalBarMaterial()
}
const dummy = new THREE.Object3D()
const vBarMesh = new THREE.InstancedMesh(this.rackVerticalBarGeometry, this.rackVerticalBarMaterial, vBarMatrix.length)
for (let i = 0; i < vBarMatrix.length; i++) {
const vp = vBarMatrix[i]
dummy.position.set(vp.x, vp.y, vp.z)
dummy.rotation.set(vp.rx, vp.ry, vp.rz)
dummy.scale.set(vp.sx, vp.sy, vp.sz)
dummy.updateMatrix()
vBarMesh.setMatrixAt(i, dummy.matrix)
}
meshes.push(vBarMesh)
}
if (linkBarMatrix.length > 0) {
if (!this.rackLinkBarGeometry) {
this.rackLinkBarGeometry = this.createLinkBar(linkBarMatrix[0].x, linkBarMatrix[0].y, linkBarMatrix[0].z, rackHeight, item.dt.rackDepth, this.bottomLinkHeight, 0.2)
}
if (!this.rackLinkBarMaterial) {
this.rackLinkBarMaterial = this.createLinkBarMaterial()
}
const dummy = new THREE.Object3D()
const linkBarMesh = new THREE.InstancedMesh(this.rackLinkBarGeometry, this.rackLinkBarMaterial, linkBarMatrix.length)
for (let i = 0; i < linkBarMatrix.length; i++) {
const lp = linkBarMatrix[i]
dummy.position.set(lp.x, lp.y, lp.z)
dummy.rotation.set(lp.rx, lp.ry, lp.rz)
dummy.scale.set(lp.sx, lp.sy, lp.sz)
dummy.updateMatrix()
linkBarMesh.setMatrixAt(i, dummy.matrix)
}
meshes.push(linkBarMesh)
}
if (hBarMatrix.length > 0) {
if (!this.rackHorizontalBarGeometry) {
this.rackHorizontalBarGeometry = this.createHorizontalBar(hBarMatrix[0].x, hBarMatrix[0].y, hBarMatrix[0].z, hBarMatrix[0].l)
}
if (!this.rackHorizontalBarMaterial) {
this.rackHorizontalBarMaterial = this.createHorizontalBarMaterial()
}
const dummy = new THREE.Object3D()
const hBarMesh = new THREE.InstancedMesh(this.rackHorizontalBarGeometry, this.rackHorizontalBarMaterial, hBarMatrix.length)
for (let i = 0; i < hBarMatrix.length; i++) {
const hp = hBarMatrix[i]
dummy.position.set(hp.x, hp.y, hp.z)
dummy.rotation.set(hp.rx, hp.ry, hp.rz)
dummy.scale.set(hp.sx, hp.sy, hp.sz)
dummy.updateMatrix()
hBarMesh.setMatrixAt(i, dummy.matrix)
}
meshes.push(hBarMesh)
}
return meshes
}
resetUVs(geometry: THREE.ExtrudeGeometry) {
if (geometry == undefined) return
const pos = geometry.getAttribute('position'),
nor = geometry.getAttribute('normal'),
uvs = geometry.getAttribute('uv')
for (let i = 0; i < pos.count; i++) {
let x = 0, y = 0
const nx = Math.abs(nor.getX(i)), ny = Math.abs(nor.getY(i)), nz = Math.abs(nor.getZ(i))
// if facing X
if (nx >= ny && nx >= nz) {
x = pos.getZ(i)
y = pos.getY(i)
}
// if facing Y
if (ny >= nx && ny >= nz) {
x = pos.getX(i)
y = pos.getZ(i)
}
// if facing Z
if (nz >= nx && nz >= ny) {
x = pos.getX(i)
y = pos.getY(i)
}
uvs.setXY(i, x, y)
}
}
}

9
src/modules/rack/index.ts

@ -1,13 +1,12 @@
import { defineModule } from '@/core/manager/ModuleManager.ts'
import RackRenderer from './RackRenderer.ts'
import RackInteraction from './RackInteraction.ts'
import propertySetter from "@/modules/rack/RackPropertySetter.ts";
import propertySetter from '@/modules/rack/RackPropertySetter.ts'
export const ITEM_TYPE_NAME = 'rack'
export default defineModule({
name: ITEM_TYPE_NAME,
export default defineModule(ITEM_TYPE_NAME, () => ({
renderer: new RackRenderer(ITEM_TYPE_NAME),
interaction: new RackInteraction(ITEM_TYPE_NAME),
setter: propertySetter,
})
setter: propertySetter
}))

9
src/modules/shuttle_rack/index.ts

@ -1,13 +1,12 @@
import { defineModule } from '@/core/manager/ModuleManager.ts'
import ShuttleRackRenderer from './ShuttleRackRenderer.ts'
import ShuttleRackInteraction from './ShuttleRackInteraction.ts'
import propertySetter from "./ShuttleRackPropertySetter.ts";
import propertySetter from './ShuttleRackPropertySetter.ts'
export const ITEM_TYPE_NAME = 'shuttle_rack'
export default defineModule({
name: ITEM_TYPE_NAME,
export default defineModule(ITEM_TYPE_NAME, () => ({
renderer: new ShuttleRackRenderer(ITEM_TYPE_NAME),
interaction: new ShuttleRackInteraction(ITEM_TYPE_NAME),
setter: propertySetter,
})
setter: propertySetter
}))

7
src/modules/tote/index.ts

@ -5,9 +5,8 @@ import propertySetter from './TotePropertySetter.ts'
export const ITEM_TYPE_NAME = 'tote'
export default defineModule({
name: ITEM_TYPE_NAME,
export default defineModule(ITEM_TYPE_NAME, () => ({
renderer: new ToteRenderer(ITEM_TYPE_NAME),
interaction: new ToteInteraction(ITEM_TYPE_NAME),
setter: propertySetter,
})
setter: propertySetter
}))

37
src/modules/way/WayRenderer.ts

@ -10,6 +10,7 @@ import InstanceMeshManager from '@/core/manager/InstanceMeshManager.ts'
import LineSegmentManager from '@/core/manager/LineSegmentManager.ts'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial'
import { getRenderer } from '@/core/manager/ModuleManager.ts'
import chargerUrl from '@/assets/images/ptr/charger.png'
/**
* AGV行走路线渲染器 point
@ -17,7 +18,7 @@ import { getRenderer } from '@/core/manager/ModuleManager.ts'
export default class WayRenderer extends BaseRenderer {
static LABEL_NAME = 'way_label'
static POINT_NAME = 'way_point'
static LINE_NAME = 'way_line'
static CHARGER_POINT_NAME = 'charger'
static GUIDEWAY_LINE_NAME = 'guideway'
static RED_LINE_NAME = 'red_line'
@ -42,6 +43,7 @@ export default class WayRenderer extends BaseRenderer {
dirGeometry: THREE.PlaneGeometry
dirMaterial: THREE.Material
dir2Material: THREE.Material
chargerMaterial: THREE.Material
/**
* ,
@ -58,13 +60,18 @@ export default class WayRenderer extends BaseRenderer {
super.init(),
new THREE.TextureLoader().loadAsync(MoveLinePointPng),
new THREE.TextureLoader().loadAsync(TriangleUrl),
new THREE.TextureLoader().loadAsync(Triangle2Url)
new THREE.TextureLoader().loadAsync(Triangle2Url),
new THREE.TextureLoader().loadAsync(chargerUrl)
]).then(([_, texture, dirTexture, dir2Texture]) => {
]).then(([_, texture, dirTexture, dir2Texture, chargerTexture]) => {
texture.flipY = false
this.pointGeometry = new THREE.PlaneGeometry(1, 1).rotateX(-Math.PI / 2)
this.pointGeometry.center()
this.chargerMaterial = new THREE.MeshLambertMaterial({
map: chargerTexture, // 颜色贴图
transparent: true // 允许透明纹理
})
this.pointMaterial = new THREE.MeshBasicMaterial({
map: texture,
@ -98,6 +105,10 @@ export default class WayRenderer extends BaseRenderer {
createPointBasic(item: ItemJson, option?: RendererCudOption): Object3DLike {
// 不允许改变高度/角度/大小
if (item.dt.isCharger) {
return this.createCharger(item)
} else {
item.tf = [
[item.tf[0][0], this.defulePositionY, item.tf[0][2]],
[this.defaultRotation.x, this.defaultRotation.y, this.defaultRotation.z],
@ -105,9 +116,24 @@ export default class WayRenderer extends BaseRenderer {
]
return this.pointManager.createByItem(item)
}
}
private createCharger(item: ItemJson) {
// 创建平面几何体
const group = new THREE.Group()
group.name = WayRenderer.CHARGER_POINT_NAME
// 绘制背景矩形框
const planeGeometry = new THREE.PlaneGeometry(item.dt.chargerWidth, item.dt.chargerDepth)
planeGeometry.rotateX(-Math.PI / 2)
const planeMesh = new THREE.Mesh(planeGeometry, this.chargerMaterial)
group.add(planeMesh)
return group
}
createLine(start: ItemJson, end: ItemJson, type: LinkType): Object3DLike {
if (start.t === this.itemTypeName && end.t === this.itemTypeName) {
if ((start.t === this.itemTypeName) && end.t === this.itemTypeName) {
return this._createOrUpdateGuideway(start, end, type)
} else {
// throw new Error('目前只支持二维码站点之间的连接')
@ -260,6 +286,9 @@ export default class WayRenderer extends BaseRenderer {
*/
afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: Object3DLike) {
super.afterCreateOrUpdatePoint(item, option, object)
if (item.dt.isCharger) {
return
}
// 创建一个 id 标签
this.tempViewport.labelManager.createOrUpdateMeshLabel(object, '' + item.id, {

9
src/modules/way/index.ts

@ -1,13 +1,12 @@
import { defineModule } from '@/core/manager/ModuleManager.ts'
import WayRenderer from './WayRenderer.ts'
import WayInteraction from './WayInteraction.ts'
import propertySetter from "@/modules/way/WayPropertySetter.ts";
import propertySetter from '@/modules/way/WayPropertySetter.ts'
export const ITEM_TYPE_NAME = 'way'
export default defineModule({
name: ITEM_TYPE_NAME,
export default defineModule(ITEM_TYPE_NAME, () => ({
renderer: new WayRenderer(ITEM_TYPE_NAME),
interaction: new WayInteraction(ITEM_TYPE_NAME),
setter: propertySetter,
})
setter: propertySetter
}))

1
src/types/global.d.ts

@ -9,6 +9,5 @@ declare global {
const _: _
const system: System
const worldModel: WorldModel
const CurrentMouseInfo: CurrentMouseInfo
}

17
src/types/model.d.ts

@ -460,3 +460,20 @@ interface Cl2If extends EntityIf, Carry, Walk, ForkArm, LiftingArm {
*/
interface ClxIf extends EntityIf, Carry, Walk, ForkArm, LiftingArm {
}
interface ServerResponse<T> {
success: boolean,
msg: string,
data: T
}
/**
*
*/
interface EnvInfo {
env_id: string
world_id: string
env_name: string
is_virtual: boolean
env_payload: any
}

Loading…
Cancel
Save