Browse Source

Merge remote-tracking branch 'origin/master'

master
lizw-2015 5 months ago
parent
commit
0dd467f8d1
  1. 7
      src/components/YvSrcEditorInner.vue
  2. 31
      src/core/base/BaseRenderer.ts
  3. 63
      src/core/manager/BackendMessageReceiver.ts
  4. 72
      src/core/manager/EnvManager.ts
  5. 20
      src/core/manager/RuntimeManager.ts
  6. 45
      src/core/script/LCCScript.ts
  7. 13
      src/core/script/ModelManager.ts
  8. 48
      src/editor/widgets/logger/LoggerView.vue
  9. 23
      src/editor/widgets/monitor/MonitorView.vue
  10. 4
      src/editor/widgets/server/EnvSelectConnect.vue
  11. 14
      src/editor/widgets/server/ServerView.vue
  12. 106
      src/modules/amr/ptr/PtrObject.ts
  13. 43
      src/modules/amr/ptr/cl2/Cl2Renderer.ts
  14. 2
      src/modules/rack/RackRenderer.ts
  15. 64
      src/types/LCC.d.ts
  16. 14
      src/types/Model.d.ts
  17. 1
      src/types/Types.d.ts

7
src/components/YvSrcEditorInner.vue

@ -150,6 +150,13 @@ export default {
() => null
)
},
clear(){
if (this.editor) {
this.editor.setValue('')
this.$emit('update:modelValue', '')
this.$emit('change', '')
}
},
init() {
// this.editor.updateOptions(option);
this.editor.setValue(this.modelValue ?? '')

31
src/core/base/BaseRenderer.ts

@ -108,7 +108,7 @@ export default abstract class BaseRenderer {
}
createPointForEntity(item: ItemJson, option?: RendererCudOption): Object3DLike {
const point = this.createPoint(item, option)
let point = this.createPoint(item, option)
point.visible = ((typeof item.v !== 'undefined') ? item.v : true)
if (item.dt.storeAt?.item) {
@ -118,7 +118,24 @@ export default abstract class BaseRenderer {
setUserDataForItem(item, point)
this.afterCreateOrUpdatePoint(item, option, point)
this.tempViewport.entityManager.appendObject(item.id, point)
this.appendToScene(point)
if (typeof option.getParentObject3D === 'function') {
// 要求添加到指定 父对象
const parent: THREE.Object3D = option.getParentObject3D(this.tempViewport, item)
if (point instanceof MeshWrap) {
const wrap = point as MeshWrap
const mesh = wrap.manager.wrapToObject3D(wrap)
this.tempViewport.entityManager.replaceObject(item.id, mesh)
parent.add(mesh)
point = mesh
} else {
parent.add(point)
}
} else {
// 默认添加到场景中
this.appendToScene(point)
}
return point
}
@ -147,7 +164,7 @@ export default abstract class BaseRenderer {
if (item.dt.storeAt?.item) {
// 如果是库存物品, 则需要将位置和角度设置为库存位置
const rack = this.tempViewport.stateManager.findItemById(item.dt.storeAt?.item)
const rack = Model.find(item.dt.storeAt?.item)
if (!rack) {
console.error(`Cannot find rack for item ${item.id} at ${item.dt.storeAt?.item}`)
}
@ -155,13 +172,14 @@ export default abstract class BaseRenderer {
if (!rackRenderer) {
console.error(`Cannot find renderer for rack type ${rack.t}`)
}
const { position, rotation } = rackRenderer.getStorePlacement(rack, item.dt.storeAt.bay, item.dt.storeAt.level, item.dt.storeAt.cell)
const { position, rotation, getParentObject3D } = rackRenderer.getStorePlacement(rack, item.dt.storeAt.bay, item.dt.storeAt.level, item.dt.storeAt.cell)
if (!position || !rotation) {
console.error(`无法获取物品 ${item.id} 的存储位置`)
}
option.position = position
option.rotation = rotation
option.getParentObject3D = getParentObject3D
}
if (_.isArray(option?.position) && _.isArray(option?.rotation)) {
@ -312,7 +330,10 @@ export default abstract class BaseRenderer {
* Position Rotation
*/
getStorePlacement(storeItem: ItemJson, bay = 0, level = 0, cell = 0)
: { position: [number, number, number], rotation: [number, number, number] } {
: {
position: [number, number, number], rotation: [number, number, number],
getParentObject3D?: (viewport: Viewport, parent: ItemJson) => THREE.Object3D
} {
throw new Error(' 不支持库存物品的添加. t=' + this.itemTypeName)
}

63
src/core/manager/BackendMessageReceiver.ts

@ -1,6 +1,5 @@
import mqtt, { type IClientOptions, type IClientSubscribeOptions } from 'mqtt'
import { reactive } from 'vue'
import type Viewport from '@/core/engine/Viewport.ts'
// 定义MQTT消息类型
export interface MqttMessage {
@ -22,6 +21,7 @@ type ProcessFn = (message: MqttMessage, handler: BackendMessageHandler) => void
interface HandlerNode {
processFn: ProcessFn
handler: BackendMessageHandler
handlerId: string
}
/**
@ -86,6 +86,7 @@ export default class BackendMessageReceiver {
this.client.on('close', () => {
this.state.status = ConnectionStatus.DISCONNECTED
this.state.isConnected = false
debugger
console.log('backendMQTT disconnected')
})
@ -175,7 +176,8 @@ export default class BackendMessageReceiver {
if (!this.handlers.has(topic)) {
this.handlers.set(topic, [])
}
this.handlers.get(topic)?.push({ processFn, handler })
const handlerId = system.createUUID()
this.handlers.get(topic)?.push({ processFn, handler, handlerId })
// 如果尚未订阅该主题
if (!this.state.subscribedTopics.includes(topic) && this.client) {
@ -185,54 +187,50 @@ export default class BackendMessageReceiver {
this.client.subscribe(topic, options, (err) => {
if (err) {
console.error(`Failed to subscribe to ${topic}:`, err)
} else {
console.log(`BackendMQTT Subscribed to ${topic}`)
}
})
}
return () => {
// 取消订阅
this.unsubscribe(type, handler)
this.unsubscribe(handlerId)
}
}
// 取消订阅
public unsubscribe(type: BackendTopicType, handler: BackendMessageHandler): void {
private unsubscribe(handlerId: string): void {
// if (!this.client?.connected) {
// throw new Error('Cannot unsubscribe - backendMQTT not connected')
// }
const [topic, processFn] = this.getTopicStringByType(type)
if (!topic) {
throw new Error(`Invalid topic for type ${type}`)
}
// 移除特定处理函数
if (handler && this.handlers.has(topic)) {
const handlers = this.handlers.get(topic) || []
const newHandlers = handlers.filter(h => (h.processFn !== processFn && h.handler !== handler))
for (const [topic, handlers] of this.handlers.entries()) {
const newHandlers = handlers.filter(node => node.handlerId !== handlerId)
if (newHandlers.length > 0) {
this.handlers.set(topic, newHandlers)
} else {
this.handlers.delete(topic)
}
} else {
this.handlers.delete(topic)
}
// 如果没有处理器了,取消订阅
if (!this.handlers.has(topic) && this.client) {
} else {
const options = {}
console.log(`client.unsubscribe(${topic},`, options)
this.client.unsubscribe(topic, options, (err) => {
if (err) {
console.error(`Failed to unsubscribe from ${topic}:`, err)
} else {
this.state.subscribedTopics = this.state.subscribedTopics.filter(t => t !== topic)
console.log(`Unsubscribed from ${topic}`)
// 如果没有处理器了,取消订阅
this.handlers.delete(topic)
if (this.client) {
const options = {}
console.log(`client.unsubscribe(${topic},`, options)
this.client.unsubscribe(topic, options, (err) => {
if (err) {
console.error(`Failed to unsubscribe from ${topic}:`, err)
} else {
this.state.subscribedTopics = this.state.subscribedTopics.filter(t => t !== topic)
console.log(`BackendMQTT Unsubscribed from ${topic}`)
}
})
}
})
}
}
}
@ -351,7 +349,7 @@ export default class BackendMessageReceiver {
private handleDeviceAlive(message: MqttMessage, handler: BackendMessageHandler) {
try {
const data = JSON.parse(message.payload.toString())
handler('DeviceAlive', message.topic, _.cloneDeep(data))
handler('DeviceAlive', message.topic, data)
// 处理设备存活状态
} catch (error) {
@ -361,8 +359,7 @@ export default class BackendMessageReceiver {
private handleLogMonitor(message: MqttMessage, handler: BackendMessageHandler) {
try {
const data = JSON.parse(message.payload.toString())
handler('Logs', message.topic, _.cloneDeep(data))
handler('Logs', message.topic, message.payload.toString())
// 处理日志更新
} catch (error) {
@ -373,7 +370,7 @@ export default class BackendMessageReceiver {
private handleAlarmMonitor(message: MqttMessage, handler: BackendMessageHandler) {
try {
const data = JSON.parse(message.payload.toString())
handler('Alarm', message.topic, _.cloneDeep(data))
handler('Alarm', message.topic, data)
} catch (error) {
console.error('Error parsing alarm data:', error)

72
src/core/manager/EnvManager.ts

@ -9,6 +9,7 @@ import { AmrMsg } from '@/core/manager/amr/AmrMessageDefine'
export default class EnvManager {
private amrMessageManager: AmrMessageManager = new AmrMessageManager()
public client: mqtt.MqttClient = null
readonly stopSubscribe: StopSubscribe[] = []
onMqttConnect = (packet: IConnackPacket) => {
console.log('Connected')
@ -57,7 +58,6 @@ export default class EnvManager {
const env = worldModel.state.runState.currentEnv
try {
worldModel.backendMessageReceiver.setProjectEnv(worldModel.state.project_uuid, worldModel.state.runState.currentEnvId)
await LCC.loadInv()
this.client = mqtt.connect(env.envConfig.mqtt.websocket, {
path: '/mqtt',
@ -71,6 +71,13 @@ export default class EnvManager {
})
await this.loadExecutorToModel()
this.stopSubscribe.push(
worldModel.backendMessageReceiver.subscribe('InvUpdate', this.onInvUpdateMessage.bind(this))
)
this.stopSubscribe.push(
worldModel.backendMessageReceiver.subscribe('ServerState', this.onServerUpdateMessage.bind(this))
)
await this.loadInvToModel()
this.client.on('connect', this.onMqttConnect)
this.client.on('message', this.onMqttMessage)
@ -83,6 +90,61 @@ export default class EnvManager {
}
}
/**
*
*/
onServerUpdateMessage(type: BackendTopicType, topic: string, data: ServerStatusVo) {
if (worldModel.state.runState.isRunning && worldModel.state.runState.currentEnvId === data.envId) {
// 处理服务器状态更新
if (!data.isRunning) {
// 如果服务器停止了,则断开连接
console.log(`Server stopped: ${data.envId}`)
system.msg(`Server is stopped, client disconnect!`, 'warning')
this.disconnectEnv().finally()
}
}
}
async onInvUpdateMessage(type: BackendTopicType, topic: string, body: InvUpdateVo) {
console.log(`InvUpdate: ${type} ${topic}`, body)
if (!window['Model']) {
// 如果没有3D模型加载,则不处理库存更新
return
}
Model.deleteInv(body.lpn)
if (body.after != null) {
// 将托盘挪到目标位置
const after = body.after
Model.createInv('pallet', body.lpn, after.rack, after.bay, after.level, after.cell)
}
}
// 加载库存到3D视图上
async loadInvToModel() {
if (!window['Model']) {
return
}
const invRes = await LCC.loadInv()
if (!invRes.success) {
return
}
for (const row of invRes.data) {
const bay = row.bay
const cell = row.cell // : 0
const level = row.level // : 0
const loc_code = row.loc_code // : "rack1_0_0_0"
const lpn = row.lpn // : "LPN1"
const rack = row.rack // : "rack1"
const container_type = row.container_type // : "pallet"
Model.createInv(container_type, lpn, rack, bay, level, cell)
}
}
// 加载执行器到3D视图上
async loadExecutorToModel() {
if (!window['Model']) {
@ -128,6 +190,12 @@ export default class EnvManager {
system.showLoading()
try {
worldModel.state.runState.isRunning = false
for (const stopFn of this.stopSubscribe) {
stopFn()
}
this.stopSubscribe.length = 0
if (this.client) {
this.client.removeAllListeners()
this.client.end()
@ -172,6 +240,6 @@ export default class EnvManager {
*
*/
dispose(): void {
this.disconnectEnv()
this.disconnectEnv().finally()
}
}

20
src/core/manager/RuntimeManager.ts

@ -78,4 +78,24 @@ export default class RuntimeManager {
this.tmpExecutors.add(item.id)
this.viewport.entityManager.createOrUpdateEntityOnlyRuntime(item)
}
removeEntity(itemId: string) {
this.tmpExecutors.delete(itemId)
// 从各个货架中移除
for (const [rackId, items] of this.storeRackMap.entries()) {
if (items.has(itemId)) {
items.delete(itemId)
if (items.size === 0) {
this.storeRackMap.delete(rackId)
}
}
}
this.viewport.entityManager.deleteEntityOnlyRuntime(itemId)
}
has(itemId: string) {
return this.tmpExecutors.has(itemId)
}
}

45
src/core/script/LCCScript.ts

@ -57,25 +57,38 @@ export default class LCCScript implements LCC {
}
// 从后台读取所有库存
async loadInv(): Promise<ServerResponse<InvVo>> {
const res = await Request.request.post('/api/workbench/LccController@loadInv', {
projectUuid: worldModel.state.project_uuid,
async loadInv(): Promise<ServerResponse<InvVo[]>> {
return Request.request.post('/api/workbench/InvController@loadInv', {
projectUUID: worldModel.state.project_uuid,
catalogCode: worldModel.state.catalogCode,
envId: worldModel.state.runState.currentEnvId
})
for (const row of res.data) {
const bay = row.bay
const cell = row.cell // : 0
const level = row.level // : 0
const loc_code = row.loc_code // : "rack1_0_0_0"
const lpn = row.lpn // : "LPN1"
const rack = row.rack // : "rack1"
const container_type = row.container_type // : "pallet"
if (window['Model']) {
Model.createInv(container_type, lpn, rack, bay, level, cell)
}
}
return res.data
}
createInv(lpn: string, targetStore: string): Promise<ServerResponse<void>> {
return Request.request.post('/api/workbench/InvController@createInv', {
projectUUID: worldModel.state.project_uuid,
envId: worldModel.state.runState.currentEnvId,
lpn: lpn,
targetStore: targetStore
})
}
moveInv(lpn: string, targetStore: string): Promise<ServerResponse<void>> {
return Request.request.post('/api/workbench/InvController@moveInv', {
projectUUID: worldModel.state.project_uuid,
envId: worldModel.state.runState.currentEnvId,
lpn: lpn,
targetStore: targetStore
})
}
deleteInv(lpn: string): Promise<ServerResponse<void>> {
return Request.request.post('/api/workbench/InvController@deleteInv', {
projectUUID: worldModel.state.project_uuid,
envId: worldModel.state.runState.currentEnvId,
lpn: lpn
})
}
saveAndSyncScripts(scriptList: { name: string; content: string }[]): Promise<ServerResponse<{ name: string; content: string }[]>> {

13
src/core/script/ModelManager.ts

@ -77,6 +77,10 @@ export default class ModelManager implements IControls, Model {
return item
}
find3D(id: string): any {
return this.viewport.entityManager.findObjectById(id)
}
getPositionByEntityId(entityId: string): THREE.Vector3 {
const item = this.viewport.entityManager.findItemById(entityId)
const matrix = getMatrixFromTf(item.tf)
@ -85,7 +89,16 @@ export default class ModelManager implements IControls, Model {
return position
}
deleteInv(itemId) {
this.viewport.runtimeManager.removeEntity(itemId)
}
deleteItem(itemId) {
if (this.viewport.runtimeManager.has(itemId)) {
// 临时执行器
this.viewport.runtimeManager.removeEntity(itemId)
return
}
this.viewport.stateManager.update(({ deleteEntity }) => {
deleteEntity(itemId)
})

48
src/editor/widgets/logger/LoggerView.vue

@ -11,12 +11,13 @@
</span>
</div>
<div class="calc-bottom-panel">
<YvSrcEditor ref="jsEditor" language="typescript" v-model="currentLog" />
<YvSrcEditor ref="jsEditor" language="typescript" />
</div>
</template>
<script>
import IWidgets from '../IWidgets.js'
import YvSrcEditor from '@/components/YvSrcEditor.vue'
import { worldModel } from '@/core/manager/WorldModel.js'
export default {
name: 'LoggerView',
@ -24,8 +25,53 @@ export default {
mixins: [IWidgets],
data() {
return {
stopSubscribe: [],
searchKeyword: ''
}
},
mounted() {
window['LoggerView'] = this
},
unmounted() {
window['LoggerView'] = null
this.undescribe()
},
methods: {
onServerLogMessage() {
},
async refreshData() {
this.$refs.jsEditor.clear()
},
async subscribe() {
await this.refreshData()
//
this.stopSubscribe.push(
worldModel.backendMessageReceiver.subscribe('Logs', this.onServerLogMessage.bind(this))
)
},
undescribe() {
//
for (const stopFn of this.stopSubscribe) {
stopFn()
}
this.stopSubscribe = []
}
},
watch: {
isActivated: {
handler(value) {
if (value) {
this.subscribe()
}
//
// else {
// this.undescribe()
// }
},
immediate: true
}
}
}
</script>

23
src/editor/widgets/monitor/MonitorView.vue

@ -4,12 +4,16 @@
<el-button size="small" :icon="renderIcon('Refresh')" @click="refreshData"
style="margin-left: 5px;"
/>
<el-button size="small" :icon="renderIcon('fa Simplybuilt')" @click="simBootAll"
style="margin-left: 2px;" link
v-if="worldModelState.runState.isRunning && worldModelState.runState.isVirtual"
>全上线
</el-button>
<el-input v-model="searchKeyword" size="small" style="width: 240px" placeholder="Search">
<template #prefix>
<component :is="renderIcon('element Search')"></component>
</template>
</el-input>
<span class="close" @click="closeMe">
<component :is="renderIcon('element Close')" />
</span>
@ -93,6 +97,18 @@ export default {
this.undescribe()
},
methods: {
simBootAll() {
const viewport = window['viewport']
for (const deviceInfo of this.deviceList) {
if (deviceInfo.isOnline) {
continue
}
const view3DObject = Model.find3D(deviceInfo.id)
if (typeof view3DObject.boot === 'function') {
view3DObject.boot()
}
}
},
copyDeviceId(deviceInfo) {
navigator.clipboard.writeText(deviceInfo.id).then(() => {
system.msg('设备ID已复制到剪贴板', 'success')
@ -154,7 +170,7 @@ export default {
worldModel.backendMessageReceiver.subscribe('DeviceStatus', this.onDeviceStatusMessage.bind(this))
)
},
async refreshData(){
async refreshData() {
this.deviceList = []
const res = await LCC.queryDeviceInfoList()
if (!res.success) {
@ -234,6 +250,9 @@ export default {
},
idle() {
return this.deviceList.filter(device => device.mode === 'AMR_FREE_MODE').length
},
worldModelState() {
return worldModel.state
}
},
watch: {

4
src/editor/widgets/server/EnvSelectConnect.vue

@ -55,10 +55,10 @@ export default {
methods: {
renderIcon,
connectEnv() {
worldModel.envManager.connectEnv()
worldModel.envManager.connectEnv().finally()
},
disconnectEnv() {
worldModel.envManager.disconnectEnv()
worldModel.envManager.disconnectEnv().finally()
},
createEnv() {
EnvManager.createEnv(this.worldModelState.project_uuid).then(() => {

14
src/editor/widgets/server/ServerView.vue

@ -108,6 +108,13 @@ export default {
worldModel.backendMessageReceiver.subscribe('ServerState', this.onServerStateMessage.bind(this))
)
},
undescribe() {
//
for (const stopFn of this.stopSubscribe) {
stopFn()
}
this.stopSubscribe = []
},
async refreshData() {
this.serverList = []
this.isLoading = true
@ -123,13 +130,6 @@ export default {
this.isLoading = false
}
},
undescribe() {
//
for (const stopFn of this.stopSubscribe) {
stopFn()
}
this.stopSubscribe = []
},
//
getSystemTagClass(system) {

106
src/modules/amr/ptr/PtrObject.ts

@ -1,4 +1,4 @@
import * as THREE from "three";
import * as THREE from 'three'
import {
AmrErrorCode,
AmrMsg, AmrMsg10010, AmrMsg10050, AmrMsg10060, AmrMsg10110, AmrMsg10120, AmrMsg20010,
@ -14,14 +14,14 @@ import {
TaskModeChangeData,
TaskStatusChangeData,
TaskTypeChangeData
} from "@/core/manager/amr/AmrMessageDefine";
import {worldModel} from "@/core/manager/WorldModel";
import Viewport from "@/core/engine/Viewport";
import {Euler} from "three/src/math/Euler";
import gsap from "gsap";
} from '@/core/manager/amr/AmrMessageDefine'
import { worldModel } from '@/core/manager/WorldModel'
import Viewport from '@/core/engine/Viewport'
import { Euler } from 'three/src/math/Euler'
import gsap from 'gsap'
type CStepTaskType = "MOVE" | "MOVE_BACKWARD" | "ROTATION" | "LOAD" | "UNLOAD" | "CHARGE"
type CStepTaskType = 'MOVE' | 'MOVE_BACKWARD' | 'ROTATION' | 'LOAD' | 'UNLOAD' | 'CHARGE'
export interface StepTask {
SeqNo: number;
@ -55,8 +55,8 @@ export default class PtrObject extends THREE.Object3D {
public clock = new THREE.Clock()
override AGVModel = ""
override AGVFnModel = ""
override AGVModel = ''
override AGVFnModel = ''
private boxBody: any = null
private __toPos: THREE.Vector3 = null
@ -159,7 +159,7 @@ export default class PtrObject extends THREE.Object3D {
private mqRetryTimeCount: number = 0
constructor(item: ItemJson, viewport: Viewport) {
super();
super()
this.viewport = viewport
this.item = item
this.vehicleId = parseInt(item.id)
@ -194,23 +194,23 @@ export default class PtrObject extends THREE.Object3D {
// 开机
boot() {
this.bootTime = Date.now();
this.computeLogicXYAndDirection();
this.bootTime = Date.now()
this.computeLogicXYAndDirection()
const boxShape = new this.viewport.ammoModel.btBoxShape(new Ammo.btVector3(0.5, 0.5, 0.5));
const boxShape = new this.viewport.ammoModel.btBoxShape(new Ammo.btVector3(0.5, 0.5, 0.5))
const mass = 10
const localInertia = new this.viewport.ammoModel.btVector3(0, 0, 0)
boxShape.calculateLocalInertia(mass, localInertia)
const boxTransform = new this.viewport.ammoModel.btTransform();
boxTransform.setIdentity();
boxTransform.setOrigin(new this.viewport.ammoModel.btVector3(this.position.x, this.position.y, this.position.z));
const boxTransform = new this.viewport.ammoModel.btTransform()
boxTransform.setIdentity()
boxTransform.setOrigin(new this.viewport.ammoModel.btVector3(this.position.x, this.position.y, this.position.z))
const boxMotionState = new this.viewport.ammoModel.btDefaultMotionState(boxTransform);
const boxMotionState = new this.viewport.ammoModel.btDefaultMotionState(boxTransform)
const rbInfo = new this.viewport.ammoModel.btRigidBodyConstructionInfo(mass, boxMotionState, boxShape, localInertia)
this.boxBody = new this.viewport.ammoModel.btRigidBody(rbInfo);
this.viewport.physicsWorld.addRigidBody(this.boxBody);
this.boxBody = new this.viewport.ammoModel.btRigidBody(rbInfo)
this.viewport.physicsWorld.addRigidBody(this.boxBody)
if (!worldModel.state.runState.isVirtual) {
return
@ -321,7 +321,7 @@ export default class PtrObject extends THREE.Object3D {
ActuatorsData: [
{
MechNo: 1,
Name: "Mech1",
Name: 'Mech1',
PickMode: this.PickMode
}
]
@ -349,10 +349,10 @@ export default class PtrObject extends THREE.Object3D {
content.CurLogicX = this.currentLogicX
content.CurLogicY = this.currentLogicY
content.CurOrientation = this.currentOrientation
content.CurX = this.position.x;
content.CurY = this.position.z;
content.X = this.position.x;
content.Y = this.position.z;
content.CurX = this.position.x
content.CurY = this.position.z
content.X = this.position.x
content.Y = this.position.z
const m20060 = new AmrMsg<AmrMsg20060>(content)
this.sendMessage(m20060)
}
@ -362,7 +362,7 @@ export default class PtrObject extends THREE.Object3D {
}
subscribeMessage(topic: string) {
worldModel.envManager.client.subscribe(topic, {qos: 0})
worldModel.envManager.client.subscribe(topic, { qos: 0 })
}
sendMessage(msg: AmrMsg<any>) {
@ -385,7 +385,7 @@ export default class PtrObject extends THREE.Object3D {
return
}
const content = new AmrMsg20100(this.vehicleId)
content.Temperature = {Battery: this.Battery}
content.Temperature = { Battery: this.Battery }
const m20100 = new AmrMsg<AmrMsg20100>(content)
worldModel.envManager.client.publish('/agv_robot/status', JSON.stringify(m20100))
}
@ -460,27 +460,27 @@ export default class PtrObject extends THREE.Object3D {
}
const ddra = Math.PI / 180
if (ra >= Math.PI * 2 - ddra || ra <= ddra) {
this.currentDirection = 0;
this.currentDirection = 0
} else if (ra >= Math.PI / 2 - ddra && ra <= Math.PI / 2 + ddra) {
this.currentDirection = 3;
this.currentDirection = 3
} else if (ra >= Math.PI - ddra && ra <= Math.PI + ddra) {
this.currentDirection = 2;
this.currentDirection = 2
} else if (ra >= Math.PI / 2 * 3 - ddra && ra <= Math.PI / 2 * 3 + ddra) {
this.currentDirection = 1;
this.currentDirection = 1
} else {
this.currentDirection = 15;
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;
this.currentLogicX = -1
this.currentLogicY = -1
} else {
this.currentLogicX = pointItem.logicX;
this.currentLogicY = pointItem.logicY;
this.currentLogicX = pointItem.logicX
this.currentLogicY = pointItem.logicY
}
this.currentOrientation = this.getAmrOrientation(this.rotation.y)
}
@ -494,7 +494,7 @@ export default class PtrObject extends THREE.Object3D {
} else {
currentStepTask = {
SeqNo: 0,
StepTaskType: "MOVE",
StepTaskType: 'MOVE',
OperationType: 0,
PickMode: 0,
X: this.currentLogicX,
@ -513,7 +513,7 @@ export default class PtrObject extends THREE.Object3D {
const linkCount = data.Link?.length || 0
if (linkCount > 0) {
let prevLink = {X: data.StartX, Y: data.StartY, Speed: 1000}
let prevLink = { X: data.StartX, Y: data.StartY, Speed: 1000 }
for (let i = 0; i < data.Link.length; i++) {
const link = data.Link[i]
if ((currentStepTask.X == link.X && currentStepTask.Y == link.Y)
@ -549,7 +549,7 @@ export default class PtrObject extends THREE.Object3D {
if (endDirection != currentStepTask.EndDirection) {
const stepTask: StepTask = {
SeqNo: data.SeqNo,
StepTaskType: "ROTATION",
StepTaskType: 'ROTATION',
OperationType: 0,
PickMode: 0,
X: prevLink.X,
@ -567,7 +567,7 @@ export default class PtrObject extends THREE.Object3D {
const stepTask: StepTask = {
SeqNo: data.SeqNo,
StepTaskType: link.Speed > 0 ? "MOVE" : "MOVE_BACKWARD",
StepTaskType: link.Speed > 0 ? 'MOVE' : 'MOVE_BACKWARD',
OperationType: 0,
PickMode: 0,
X: link.X,
@ -590,7 +590,7 @@ export default class PtrObject extends THREE.Object3D {
|| (linkCount > 0 && data.Link[linkCount - 1].X == data.EndX && data.Link[linkCount - 1].Y == data.EndY)) {
if (data.OperationType == 0 && data.EndDirection >= 0 && data.EndDirection <= 3) {
endDirection = data.EndDirection;
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) {
@ -610,7 +610,7 @@ export default class PtrObject extends THREE.Object3D {
}
const stepTask: StepTask = {
SeqNo: data.SeqNo,
StepTaskType: "ROTATION",
StepTaskType: 'ROTATION',
OperationType: 0,
PickMode: 0,
X: data.EndX,
@ -629,7 +629,7 @@ export default class PtrObject extends THREE.Object3D {
const stepTask: StepTask = {
SeqNo: data.SeqNo,
StepTaskType: "CHARGE",
StepTaskType: 'CHARGE',
OperationType: 3,
PickMode: 0,
X: data.EndX,
@ -647,7 +647,7 @@ export default class PtrObject extends THREE.Object3D {
const stepTask: StepTask = {
SeqNo: data.SeqNo,
StepTaskType: data.PickMode == 1 ? "LOAD" : "UNLOAD",
StepTaskType: data.PickMode == 1 ? 'LOAD' : 'UNLOAD',
OperationType: 4,
PickMode: data.PickMode,
X: data.EndX,
@ -676,7 +676,7 @@ export default class PtrObject extends THREE.Object3D {
while (this.currentStepTaskList.length > 0) {
const stepTask = this.currentStepTaskList[0]
if (this.runningStepTask) {
if ((stepTask.StepTaskType == "MOVE" || stepTask.StepTaskType == "MOVE_BACKWARD")
if ((stepTask.StepTaskType == 'MOVE' || stepTask.StepTaskType == 'MOVE_BACKWARD')
&& this.rotationAnimation == null && this.actionAnimation == null
&& stepTask.EndDirection == this.runningStepTask.EndDirection
&& (stepTask.Speed > 0) == (this.runningStepTask.Speed > 0)) {
@ -693,13 +693,13 @@ export default class PtrObject extends THREE.Object3D {
this.runningStepTask = stepTask
this.currentStepTaskList.shift()
this.runningStepTaskList.push(stepTask)
if (stepTask.StepTaskType == "MOVE" || stepTask.StepTaskType == "MOVE_BACKWARD") {
if (stepTask.StepTaskType == 'MOVE' || stepTask.StepTaskType == 'MOVE_BACKWARD') {
this.addTravel(stepTask.X, stepTask.Y, stepTask.Speed / 1000)
} else if (stepTask.StepTaskType == "ROTATION") {
} else if (stepTask.StepTaskType == 'ROTATION') {
this.addRotation(stepTask.EndDirection)
} else if (stepTask.StepTaskType == "LOAD") {
} else if (stepTask.StepTaskType == 'LOAD') {
this.addLoad(stepTask.GoodsSlotHeight / 1000)
} else if (stepTask.StepTaskType == "UNLOAD") {
} else if (stepTask.StepTaskType == 'UNLOAD') {
this.addUnload(stepTask.GoodsSlotHeight / 1000)
}
}
@ -775,7 +775,6 @@ export default class PtrObject extends THREE.Object3D {
addTravel(logicX: number, logicY: number, speed: number = 1): Promise<void> {
this.OperationType = 0
this.PickMode = 0
const pos = Model.getPositionByLogicXY(logicX, logicY)
@ -790,14 +789,14 @@ export default class PtrObject extends THREE.Object3D {
}
// 运动参数
const accelForce = speed > 0 ? 4 : (-4 )
let currentPhase = 'accelerate';
const accelForce = speed > 0 ? 4 : (-4)
let currentPhase = 'accelerate'
if (this.viewport.registerPhysicsUpdateCallBack.has(this.item.id)) {
return null
}
this.travelAnimation = "asd"
this.travelAnimation = 'asd'
const force = new this.viewport.ammoModel.btVector3(accelForce, 0, 0)
this.viewport.registerPhysicsUpdateCallBack.set(this.item.id, () => {
@ -826,7 +825,8 @@ export default class PtrObject extends THREE.Object3D {
this.travelAnimation = null
this.onActionCompleted()
} if (distance <= stopDistance) {
}
if (distance <= stopDistance) {
currentPhase = 'decelerate'
}
// 运动阶段控制

43
src/modules/amr/ptr/cl2/Cl2Renderer.ts

@ -1,8 +1,9 @@
import * as THREE from 'three'
import BaseRenderer from '@/core/base/BaseRenderer.ts'
import Constract from '@/core/Constract.ts'
import Cl23dObject from "./Cl23dObject";
import Cl23dObject from './Cl23dObject'
import type { Object3DLike } from '@/types/ModelTypes.ts'
import type Viewport from '@/core/engine/Viewport.ts'
/**
* ptr侧叉渲染器
@ -24,6 +25,39 @@ export default class PtrRenderer extends BaseRenderer {
}
/**
* Cl2 Position Rotation
*/
getStorePlacement(storeItem: ItemJson, bay = 0, level = 0, cell = 0)
: {
position: [number, number, number], rotation: [number, number, number],
getParentObject3D?: (viewport: Viewport, parent: THREE.Object3D) => THREE.Object3D
} {
return {
position: [0, 0.2, 0],
rotation: [0, 90, 0],
getParentObject3D: this.getArmObject.bind(this)
}
}
getArmObject(viewport: Viewport, item: ItemJson): THREE.Object3D {
// 获取机械臂对象
const object = viewport.entityManager.findObjectById(item.dt.storeAt?.item)
if (!object) {
console.warn('PtrRenderer: getArmObject failed, not found Cl2:', item.dt.storeAt?.item)
return
}
const agv = object as THREE.Group
if (agv.children.length > 1) {
const pillar = agv.children[1]
if (pillar.children.length > 1) {
return pillar.children[1]
}
}
}
/**
* 使 storeWidth/storeDepth, TF无效
*/
override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: THREE.Object3D) {
@ -39,7 +73,6 @@ export default class PtrRenderer extends BaseRenderer {
)
}
createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D {
throw new Error('not allow store line.')
}
@ -51,11 +84,6 @@ export default class PtrRenderer extends BaseRenderer {
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, this.tempViewport, option)
group.name = PtrRenderer.POINT_NAME
@ -64,7 +92,6 @@ export default class PtrRenderer extends BaseRenderer {
return group
}
updatePoint(item: ItemJson, object: Object3DLike, option?: RendererCudOption): Object3DLike {
const group: THREE.Group = object as THREE.Group

2
src/modules/rack/RackRenderer.ts

@ -83,7 +83,7 @@ export default class RackRenderer extends BaseRenderer {
localX += bays[bay].bayWidth / 2 // 居中
let localY = 0
for (let i = 0; i < level; i++) {
for (let i = 0; i <= level; i++) {
localY += levelHeights[i]
}

64
src/types/LCC.d.ts

@ -16,11 +16,32 @@ declare interface LCC {
/**
* , Model
*/
loadInv(): Promise<ServerResponse<InvVo>>
loadInv(): Promise<ServerResponse<InvVo[]>>
/**
*
* @param lpn
* @param targetStore rack3/0/0/0
*/
createInv(lpn: string, targetStore: string): Promise<ServerResponse<void>>
/**
*
* @param lpn
* @param targetStore rack3/0/0/0
*/
moveInv(lpn: string, targetStore: string): Promise<ServerResponse<void>>
/**
*
* @param lpn
*/
deleteInv(lpn: string): Promise<ServerResponse<void>>
/**
* Model
*/
// loadExecutor(): Promise<ExecutorVo>
/**
@ -77,6 +98,8 @@ type DeviceStatusFn = (type: BackendTopicType, topic: string, body: AgvStatusVo)
type ServerStateFn = (type: BackendTopicType, topic: string, body: ServerStatusVo) => void
type InvUpdateFn = (type: BackendTopicType, topic: string, body: InvUpdateVo) => void
type StopSubscribe = () => void
interface ServerAuthorizationConfigVo {
@ -169,6 +192,45 @@ interface AgvStatusVo {
virtualExecutorPayload: string; // 车的虚拟执行器负载
}
interface BasLocationVo {
/**
*
*/
locCode: string
/**
*
*/
locType: string
/**
*
*/
rack: string
/**
*
*/
bay: number
/**
*
*/
level: number
/**
*
*/
cell: number
}
interface InvUpdateVo {
lpn: string
before: BasLocationVo | null
after: BasLocationVo | null
qty: number
}
/**
*
*/

14
src/types/Model.d.ts

@ -3,12 +3,18 @@
*/
declare interface Model {
/**
* ID
* ID
* @param entityId ID
*/
find(entityId: string): ItemJson
/**
* ID
* @param id
*/
find3D(id: string): any
/**
* ID
*/
getPositionByEntityId(entityId: string): Vector3IF
@ -40,6 +46,11 @@ declare interface Model {
*/
createInv(boxType: ContainerT, lpn: string, rack: string, bay: number = 0, level: number = 0, cell: number = 0): void
/**
* ID
* @param itemId ID
*/
deleteInv(itemId: string): void
/**
*
@ -82,6 +93,7 @@ declare interface Model {
*/
multiSelectedEntityIds: string[]
}
/**

1
src/types/Types.d.ts

@ -63,6 +63,7 @@ interface RendererCudOption {
position?: any
//THREE.Quaternion
rotation?: any
getParentObject3D?: (viewport:any, item: ItemJson) => any
}
/**

Loading…
Cancel
Save