You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

547 lines
17 KiB

import type Viewport from '@/core/engine/Viewport.ts'
import AMR_1 from '../../../lcc-map/jx_test/floor/AMR_1.json'
import Decimal from 'decimal.js'
export function AmrMapConvertDemo() {
const from: any = AMR_1
const result = AmrMapConvert(from)
console.log(result)
debugger
}
window['AmrMapConvertDemo'] = AmrMapConvertDemo
// AmrMapConvertDemo()
export function AmrMapConvert(from: any): Array<ItemJson> {
const d1000 = new Decimal(1000)
const d100 = new Decimal(100)
const storeType = new Map<number, Object>()
for (const td of from.toolingData) {
if (td.categoryId === 1) {
// 货架
/*
根据 layersHeight 获取出
"layersHeight": [
{
"id": 1,
"layerHeight": 1,
"clearance": 1079,
"layerGoodsAllocationGroup": {
"goodsAllocationNum": 2,
"goodsAllocation": [
{
"isSelected": false,
"gaps": [
{
"value": 100
},
{
"value": 100
},
{
"value": 100
}
],
"goodsPlacementOffset": 100,
"goodsStackId": 1,
"id": 98,
"localNumber": "1-1",
"goodsSlotFeatureType": "Default",
"distanceToStation": 990,
"goodsSlotFeatureHeight": "",
"boxMarkerOffsetToBottom": 0,
"goodsSlotTelescopicDistanceToDetectFeature": "",
"verticalGroupId": "v-1",
"horizontalGroupId": "h-1",
"displayNumber": "",
"beamHeight": 130,
"layerHeight": 1,
"clearance": 1079,
"width": 1000,
"orientation": [],
"slideChute": false,
"heightOverGoodsSlotWhenPut": null,
"heightOverGoodsSlotWhenGet": null,
"heightGapBetweenForkAndGoodsWhenGet": null,
"heightGapBetweenForkAndGoodsWhenPut": null,
"goodsSlotPillarHeight": null,
"goodsSlotStatusCheckHeight": ""
},
{
"isSelected": false,
"gaps": [
{
"value": 100
},
{
"value": 100
},
{
"value": 100
}
],
"goodsPlacementOffset": 100,
"goodsStackId": 1,
"id": 98,
"localNumber": "1-2",
"goodsSlotFeatureType": "Default",
"distanceToStation": 990,
"goodsSlotFeatureHeight": "",
"boxMarkerOffsetToBottom": 0,
"goodsSlotTelescopicDistanceToDetectFeature": "",
"verticalGroupId": "v-2",
"horizontalGroupId": "h-1",
"displayNumber": "",
"beamHeight": 130,
"layerHeight": 1,
"clearance": 1079,
"width": 1000,
"orientation": [],
"slideChute": false,
"heightOverGoodsSlotWhenPut": null,
"heightOverGoodsSlotWhenGet": null,
"heightGapBetweenForkAndGoodsWhenGet": null,
"heightGapBetweenForkAndGoodsWhenPut": null,
"goodsSlotPillarHeight": null,
"goodsSlotStatusCheckHeight": ""
}
]
},
"goodsStackId": 1
},
{
"id": 2,
"layerHeight": 1210,
"layerGoodsAllocationGroup": {
"goodsAllocationNum": 2,
"goodsAllocation": [
{
"isSelected": false,
"gaps": [
{
"value": 100
},
{
"value": 100
},
{
"value": 100
}
],
"goodsPlacementOffset": 100,
"goodsStackId": 1,
"id": 98,
"localNumber": "2-1",
"goodsSlotFeatureType": "Default",
"distanceToStation": 990,
"goodsSlotFeatureHeight": "",
"boxMarkerOffsetToBottom": 0,
"goodsSlotTelescopicDistanceToDetectFeature": "",
"verticalGroupId": "v-1",
"horizontalGroupId": "h-2",
"displayNumber": "",
"beamHeight": 130,
"layerHeight": 1210,
"clearance": 2000,
"width": 1000,
"orientation": [],
"slideChute": false,
"heightOverGoodsSlotWhenPut": null,
"heightOverGoodsSlotWhenGet": null,
"heightGapBetweenForkAndGoodsWhenGet": null,
"heightGapBetweenForkAndGoodsWhenPut": null,
"goodsSlotPillarHeight": null,
"goodsSlotStatusCheckHeight": ""
},
{
"isSelected": false,
"gaps": [
{
"value": 100
},
{
"value": 100
},
{
"value": 100
}
],
"goodsPlacementOffset": 100,
"goodsStackId": 1,
"id": 98,
"localNumber": "2-2",
"goodsSlotFeatureType": "Default",
"distanceToStation": 990,
"goodsSlotFeatureHeight": "",
"boxMarkerOffsetToBottom": 0,
"goodsSlotTelescopicDistanceToDetectFeature": "",
"verticalGroupId": "v-2",
"horizontalGroupId": "h-2",
"displayNumber": "",
"beamHeight": 130,
"layerHeight": 1210,
"clearance": 2000,
"width": 1000,
"orientation": [],
"slideChute": false,
"heightOverGoodsSlotWhenPut": null,
"heightOverGoodsSlotWhenGet": null,
"heightGapBetweenForkAndGoodsWhenGet": null,
"heightGapBetweenForkAndGoodsWhenPut": null,
"goodsSlotPillarHeight": null,
"goodsSlotStatusCheckHeight": ""
}
]
},
"clearance": 2000
}
],
*/
const rackDepth = new Decimal(td.width).div(d1000).toNumber()
const levelHeight = td.layersHeight.map((lh: any) => new Decimal(lh.layerHeight).div(d1000).toNumber())
const levelCount = levelHeight.length
// 长度除以货位数
const width = new Decimal(td.length).div(td.goodsAllocationNum).div(d1000).toNumber()
storeType.set(td.id, {
't': 'rack',
'v': true,
'tf': [
[0, 0, 0],
[0, 0, 0],
[width, 1.211, rackDepth]
],
'dt': {
'rackDepth': rackDepth,
'bottomBarHeight': 0.2,
'bottomLinkHeight': 0.2,
'topLinkDistance': 0.2,
'levelCount': levelCount,
'bayCount': 1,
'hideFloor': 0,
'extendColumns': 1,
'columnSpacing': 1,
'bays': [
{
'bayWidth': width,
'levelHeight': levelHeight,
'topHeight': 2
}
],
'center': [],
'in': [],
'out': [],
'rackWidth': width,
'rackHeight': 1.211
}
})
} else if (td.categoryId === 2) {
// 地堆
storeType.set(td.id, {
't': 'gstore',
'v': true,
'tf': [
[0, 0, 0],
[0, 0, 0],
[new Decimal(td.width).div(d1000).toNumber(), 0.01, new Decimal(td.length).div(d1000).toNumber()]
],
'dt': {
'in': [],
'out': [],
'center': [],
'strokeWidth': 0.1
}
})
}
}
// ====================== 第一次循环,建立点位 =========================
const amrIdMap = new Map<number, any>()
const lccMap = new Map<string, any>()
let maxX = NaN, maxY = NaN, minX = NaN, minY = NaN
for (const amrNode of from.businessMap[0].mapData) {
amrIdMap.set(amrNode.id, amrNode)
const lccId = _.toString(amrNode.id) // amrNode.logicX + '_' + amrNode.logicY
// if (lccId === '105_105') {
// debugger
// }
let insertNode: any = null
if (amrNode.type === 'CODE') {
if (isStorePos(amrNode)) {
// =================== 这是一个存储位 =====================
const rackTypeId = amrNode.attribute[0].rackTypeId
insertNode = {
'id': lccId,
..._.cloneDeep(storeType.get(rackTypeId))
}
// 货架高度是从 attribute[0].goodsAllocation[].layerHeight 读取的
if (insertNode.t === 'rack') {
const levelsInfo = amrNode.attribute[0].attrDetail
const levelCount = levelsInfo.length
const levelHeight = _.map(levelsInfo, l => new Decimal(l.val).div(d1000).toNumber())
insertNode.dt.levelCount = levelCount
insertNode.dt.bays[0].levelHeight = levelHeight
}
} else {
// =================== 这是一个路标 =====================
insertNode = {
'id': lccId, 't': 'way', 'v': true,
'tf': [
[0, 0, 0],
[0, 0, 0],
[0.25, 0.1, 0.25]
],
'dt': {
'qrCode': amrNode.uniqueName,
'in': [],
'out': [],
'center': []
}
}
// 旋转站
if (_.findIndex(amrNode.propsValues, v => v === 'CellTypeSpinStation') >= 0 && amrNode.constraints) {
// 这是一个旋转站, 读取 constraints 所有的 key
const agvRotation = []
for (const key of Object.keys(amrNode.constraints)) {
agvRotation.push(key)
}
if (agvRotation.length > 0) {
insertNode.dt.agvRotation = agvRotation
}
}
}
// 这是潜伏AGV货位
if (amrNode.type === 'CODE' && amrNode.attribute[0]?.rackTypeId === 3 && amrNode.attribute[0]?.attrValue === 'CellTypeRackLocation') {
lccMap.set(lccId + 'C', {
'id': lccId + 'C',
't': 'gstore',
'v': true,
'tf': [
[
new Decimal(amrNode.x).div(d100).toNumber(),
0,
new Decimal(amrNode.y).div(d100).toNumber()
],
[0, 0, 0],
[0.5, 0.01, 0.5]
],
'dt': {
'in': [],
'out': [],
'center': [],
'strokeWidth': 0.05
},
'logicX': amrNode.logicX,
'logicY': amrNode.logicY
})
insertNode.dt.linkStore = []
for (let c of amrNode.direction) {
insertNode.dt.linkStore.push({
'item': lccId + 'C',
'bay': 0, 'level': 0, 'cell': 0, 'direction': convertDirection(c)
})
}
}
}
if (insertNode) {
// insertNode.originId = amrNode.id
// insertNode.name = _.toString(amrNode.id)
insertNode.tf[0][0] = new Decimal(amrNode.x).div(d100).toNumber()
insertNode.tf[0][2] = new Decimal(amrNode.y).div(d100).toNumber()
insertNode.logicX = amrNode.logicX
insertNode.logicY = amrNode.logicY
if (isNaN(maxX) || insertNode.tf[0][0] > maxX) {
maxX = insertNode.tf[0][0]
}
if (isNaN(maxY) || insertNode.tf[0][2] > maxY) {
maxY = insertNode.tf[0][2]
}
if (isNaN(minX) || insertNode.tf[0][0] < minX) {
minX = insertNode.tf[0][0]
}
if (isNaN(minY) || insertNode.tf[0][2] < minY) {
minY = insertNode.tf[0][2]
}
lccMap.set(lccId, insertNode)
}
}
// ====================== 第二次循环,建立点位关联 =========================
for (const amrNode of from.businessMap[0].mapData) {
if (amrNode.type !== 'ROAD') {
continue
}
for (const dg of amrNode.directionGroup) {
// 从 dg.startSite 和 dg.endSite 中获取对应的点位
const startNode = amrIdMap.get(dg.startSite)
const endNode = amrIdMap.get(dg.endSite)
if (!startNode || !endNode) {
console.warn('未找到起点或终点节点', dg.startSite, dg.endSite)
continue
}
const startLccId = _.toString(dg.startSite) // startNode.logicX + '_' + startNode.logicY
const endLccId = _.toString(dg.endSite) // endNode.logicX + '_' + endNode.logicY
if (!lccMap.get(startLccId) || !lccMap.get(endLccId)) {
console.warn('未找到起点或终点 LCC 节点', startLccId, endLccId)
continue
}
if (lccMap.get(startLccId).t === 'way' && lccMap.get(endLccId).t === 'way') {
// 起点和终点都是路标
lccMap.get(startLccId).dt.in.push(endLccId)
lccMap.get(startLccId).dt.out.push(endLccId)
lccMap.get(endLccId).dt.in.push(startLccId)
lccMap.get(endLccId).dt.out.push(startLccId)
} else if (lccMap.get(startLccId).t === 'way' &&
(lccMap.get(endLccId).t === 'rack' || lccMap.get(endLccId).t === 'gstore')) {
// 起点是路标,终点是存储位
if (!lccMap.get(startLccId).dt.linkStore) {
lccMap.get(startLccId).dt.linkStore = []
}
const direction = convertAmrDirectToLinkStoreDirection(amrNode.direction?.id)
if (_.isArray(lccMap.get(endLccId).dt.bays?.[0]?.levelHeight)) {
for (let i = 0; i < lccMap.get(endLccId).dt.bays[0].levelHeight.length; i++) {
lccMap.get(startLccId).dt.linkStore.push({
'item': endLccId,
'bay': 0,
'level': i,
'cell': 0,
'direction': direction
})
}
} else {
lccMap.get(startLccId).dt.linkStore.push({
'item': endLccId,
'bay': 0,
'level': 0,
'cell': 0,
'direction': direction
})
}
convertLinkStoreDistance(direction, lccMap.get(endLccId))
} else if (lccMap.get(endLccId).t === 'way' &&
(lccMap.get(endLccId).t === 'rack' || lccMap.get(endLccId).t === 'gstore')) {
// 终点是路标,起点是存储位
if (!lccMap.get(endLccId).dt.linkStore) {
lccMap.get(endLccId).dt.linkStore = []
}
const direction = convertAmrDirectToLinkStoreDirection(amrNode.direction?.id)
if (_.isArray(lccMap.get(startLccId).dt.bays?.[0]?.levelHeight)) {
for (let i = 0; i < lccMap.get(startLccId).dt.bays[0].levelHeight.length; i++) {
lccMap.get(endLccId).dt.linkStore.push({
'item': startLccId,
'bay': 0,
'level': i,
'cell': 0,
'direction': direction
})
}
} else {
lccMap.get(endLccId).dt.linkStore.push({
'item': startLccId,
'bay': 0,
'level': 0,
'cell': 0,
'direction': direction
})
}
convertLinkStoreDistance(direction, lccMap.get(startLccId))
}
}
}
// ====================== 第三次循环,将所有点位移动到画面中心 =========================
const centerX = new Decimal((maxX + minX) / 2).toNumber()
const centerY = new Decimal((maxY + minY) / 2).toNumber()
for (const entry of lccMap.entries()) {
const node = entry[1]
if (node.tf) {
node.tf[0][0] = new Decimal(node.tf[0][0]).minus(centerX).toNumber()
node.tf[0][2] = new Decimal(node.tf[0][2]).minus(centerY).toNumber()
}
}
const planObj: Object = {}
for (const entry of lccMap.entries()) {
planObj[entry[0]] = entry[1]
}
// console.log(planObj)
// debugger
return Object.values(planObj) as Array<ItemJson>
}
// 将 AMR 的方向转换为 LinkStore 的方向
function convertAmrDirectToLinkStoreDirection(direct: number): string {
if (direct === 1) {
return 'up'
} else if (direct === 2) {
return 'down'
} else if (direct === 3) {
return 'right'
} else if (direct === 4) {
return 'left'
} else if (direct === 5) {
return 'right'
} else {
throw new Error('未知的方向: ' + direct)
}
}
// 判断点位是否为存储位
function isStorePos(amrNode: any) {
return (_.findIndex(amrNode.propsValues, v => v === 'CellTypePallet') >= 0)
}
// 根据方向,调整存储位的距离
function convertLinkStoreDistance(direction: string, storeNode: any) {
if (direction === 'up') {
storeNode.tf[0][2] -= 1.2
} else if (direction === 'down') {
storeNode.tf[0][2] += 1.2
} else if (direction === 'left') {
storeNode.tf[0][0] -= 1.2
storeNode.tf[1][1] = 90 // 90 度旋转
} else if (direction === 'right') {
storeNode.tf[0][0] += 1.2
storeNode.tf[1][1] = 90 // 90 度旋转
}
}
// 根据方向,调整存储位的距离
function convertDirection(directions: string) {
// N=up / S=down / E=right / W=left
if (directions === 'N') {
return 'up'
}
if (directions === 'S') {
return 'down'
}
if (directions === 'E') {
return 'right'
}
if (directions === 'W') {
return 'left'
}
throw new Error('convertDirection error:' + directions)
}