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
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)
|
|
}
|
|
|