Files
cut-abstractions/tests/dev1/dataHandle/optimizeLayout/optimizeLayout.ts
2025-07-22 18:22:31 +08:00

400 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ProcessorBase, ProcessorContext, ResCodeType, resultInfo } from "../base";
import { init2VModel, resetModelContour, resetModelKnife } from "../common/LayoutEngine/BlockDataPlus";
import { getDoFace } from "../common/LayoutEngine/BlockDoFace";
import { BlockPlus } from "../common/LayoutEngine/BlockPlus";
import { PlaceBlock, PlaceBlockDetail, PlaceMaterial } from "../confClass";
import { ConfigBase } from "../models/config";
import { Big_bang, ComposingType, LineType, WorkerItemType, xbang } from "./RectOptimizeWorker/bang";
/** 优化排版 */
export class OptimizeLayoutProcessor extends ProcessorBase<any, Function, any> {
public get name(): string {
return "OptimizeLayoutProcessor_优化排版";
}
public get version(): string {
return "1.0.0";
}
private _output?: Function;
private _source: any;
private _params: ConfigBase = new ConfigBase();
/** 多线程数据集 */
private workerList: WorkerItemType[] = []
constructor() {
super();
this._params.name = "优化排版配置";
this._params.version = "1.0.0";
/** 初始化配置 这里 如果不初始化 load 的时候 不会加载上 */
/**板最小宽度 */
this._params.minBlockWidth = 5;
/** 板最小厚度 */
this._params.minBlockThickness = 2;
/** 排版方式(0按机台尺寸排版 1按板材规格排版) */
this._params.placeStyle = 1;
/** 机台长度 */
this._params.boardLength = 2440;
/** 机台宽度 */
this._params.boardWidth = 1220;
/** 刀路间距 --板件开料轮廓的间距 */
this._params.blockKnifeLineSpacing = 0
/** 排版跟随大板定位,小板需要反面优化 */
this._params.isTurnFaceToPlace = false
/** 双面加工板件优先集中排版AllowDoubleHoleFirstSort */
this._params.isDoubleFaceBlockFirst = true;
/** 是否是矩形优化? */
this._params.isRectPlace = true
/** 余料板允许排入双面加工的小板yuLiaoBoardDo2FaceBlock */
this._params.isDoubleFaceBlockInRemain = true;
}
public async exec(context: ProcessorContext<any, Function, any>): Promise<void> {
// 输入数据
this._source = context.input
// 输出函数
this._output = context.output;
// 加载配置
// this._params = context.params;
this._params?.load(context.params);
// this._output?.('初始化完成,开始进行优化!')
this.optimizeLayout()
}
/** 开始优化主程序 */
private async optimizeLayout() {
let { materialList, blockList, blockDetailList, toolsHelper } = this._source
if (!Array.isArray(toolsHelper.knifeList) || toolsHelper.knifeList.length == 0) {
console.error('optimizeLayout: 刀库位置异常,请先确认刀库配置!')
return
}
let checkRes = this.checkPlaceBefore(materialList, blockList)
if (checkRes.length > 0) {
let res: resultInfo = {
code: ResCodeType.ERROR,
message: '优化前检测未通过',
data: checkRes
};
this.callBack(res);
} else {
let _blockList: PlaceBlock[] = [];
let _blockDetailList: PlaceBlockDetail[] = [];
for (const b of blockList) {
let block = new PlaceBlock(b);
_blockList.push(block);
}
for (const bd of blockDetailList) {
let blockDetail = new PlaceBlockDetail(bd);
await resetModelContour(blockDetail);
await init2VModel(blockDetail, toolsHelper, false);
_blockDetailList.push(blockDetail);
let i = blockList.findIndex(e => e.blockId == blockDetail.blockId);
_blockList[i].blockDetail = blockDetail;
}
for (const block of _blockList) {
let pm = materialList.find(e => e.goodsId == block.goodsId);
let knife = toolsHelper.getKnifeByParams({ length: pm?.thickness });
pm.diameter = knife?.diameter;
pm.cutKnifeGap = this._params.blockKnifeLineSpacing || 0.01;
await BlockPlus.initBlock(block, pm, this._params)
await BlockPlus.getBorder(block, false, block.placeStyle)
// await blockBoardClass.getBorder(block);
await resetModelKnife(block, toolsHelper)
}
this._source.blockList = _blockList
materialList.forEach(m => {
let pm = new PlaceMaterial(m);
this.startPlaceThreed(pm)
});
}
}
/**开始优化线程 输入的数据没有初始化 */
startPlaceThreed(material: PlaceMaterial) {
const { materialList, blockList, blockDetailList, toolsHelper } = this._source
const { processMode = 0, cutBoardBorder = 3, blockKnifeLineSpacing = 0 } = this._params
let bList: any = []
let pm: PlaceMaterial = new PlaceMaterial(material)
blockList.map(e => {
if (e.goodsId == pm.goodsId) {
bList[e.blockNo] = e
let detail = blockDetailList.find(x => x.blockId == e.blockId)
if (!Reflect.has(bList[e.blockNo], 'blockDetail')) {
bList[e.blockNo].blockDetail = new PlaceBlockDetail(detail)
}
bList[e.blockNo].isTurnFaceToPlace = getDoFace(bList[e.blockNo], processMode)
pm.blockList.push(e)
}
})
pm.cutBorder = cutBoardBorder
pm.cutKnifeGap = blockKnifeLineSpacing
let knife = toolsHelper.getKnifeByParams({ length: pm.thickness })
if (!knife) {
console.log('没有开料刀')
return '没有开料刀'
}
pm.diameter = knife?.diameter
pm.cutKnifeName = knife.knifeName
/** 小板 */
let bans: xbang[] = []
// 实际开料大板的列表
let big_Bang: Big_bang[] = []
let big_BangSL: number[] = []
let border = cutBoardBorder
let borderOff = (pm.diameter + pm.cutKnifeGap) / 2
// 余料板 以及实际开料大板
for (const cuttingBoard of material.remainBoardList) {
big_Bang.push({ w: cuttingBoard.width - border * 2 + borderOff * 2, l: cuttingBoard.length - border * 2 + borderOff * 2, x: border - borderOff, y: border - borderOff })
big_BangSL.push(cuttingBoard.count || 999)
}
// big_Bang = []
// big_BangSL = []
// 母板 兜底的
big_Bang.push({ w: pm.width - border * 2 + borderOff * 2, l: pm.length - border * 2 + borderOff * 2, x: border - borderOff, y: border - borderOff })
// 生成小板
for (let key in bList) {
let b = bList[key]
let bid = b.blockNo
let width = b.placeFullWidth()
let length = b.placeFullLength()
let line = this.toLine(b.texture)
bans.push({
l: length,
w: width,
line,
face: this.toface(b),
id: bid,
bno: b.blockNo,
holeFaceCount: 3,
isRect: !b.isUnRegular,
hasHole: false,
isdtwosided: true,
})
}
let bestCount = 0
if (bans.length == 0) // 没有板了
{
let best = []
let yl: Big_bang[] = []
let fit = 0
let resObj = {
data: { bList, best, yl, fit, bans, width: pm.width, length: pm.length, bestCount: bestCount++, pm },
info: {
times: -1,
type: 'noBan'
}
}
this.callBack(resObj)
return
}
let xyhcs = 50
if (bans.length > 1000) { xyhcs = 40 }
else if (bans.length > 1500) { xyhcs = 30 }
else if (bans.length > 2000) { xyhcs = 25 }
else if (bans.length > 4000) { xyhcs = 20 }
else if (bans.length > 6000) { xyhcs = 15 }
else if (bans.length > 10000) { xyhcs = 10 }
else if (bans.length > 15000) { xyhcs = 5 }
else if (bans.length > 20000) { xyhcs = 1 }
let isDoubleFaceBlockFirst = this._params.isDoubleFaceBlockFirst // 双面加工排前面
let gap = this._params.blockKnifeLineSpacing
// this.bestfit = 0
for (let j = 0; j < 1; j++) {
let w = new Worker(new URL('./RectOptimizeWorker/RectOptimizeWorkerWorker.ts', import.meta.url), { type: 'module' })
// this.optimizeEngines.push(w)
const data = {
type: 'start',
data: [bans, big_Bang, big_BangSL, xyhcs, isDoubleFaceBlockFirst, gap, this._params.isRectPlace, this._params.isDoubleFaceBlockInRemain],
}
let item: WorkerItemType = {
w: w,
goodsId: pm.goodsId,
pm,
status: 'start'
}
if (this.workerList.findIndex(e => e.goodsId == item.goodsId) == -1) {
this.workerList.push(item)
}
let workItem = this.workerList.find(e => e.goodsId == pm.goodsId)
if (workItem && workItem != undefined) {
workItem.w?.postMessage(data)
workItem.w.onmessage = async (d) => {
let [best, yl, fit, info] = d.data as [any[], Big_bang[], number, any]
switch (info.type) {
case 'loop':
let resObj = {
data: { bList, best, yl, fit, bans, width: pm.width, length: pm.length, bestCount: bestCount++, pm },
info
}
this.callBack(resObj)
break;
case 'stop':
console.log('stop =》dataHandleBase', info, this.workerList)
this.terminateWorker({ goodsId: pm.goodsId })
break;
case 'isStop':
// console.error('isStop', info)
break;
default:
break;
}
}
}
console.log('新算法线程启动', pm.goodsId, this.workerList)
}
}
private callBack(data: any) {
this._output?.(data);
}
/** 关闭线程 */
async terminateWorker(params) {
const { key, goodsId } = params
let data = {
type: 'stop',
msg: 'byKey'
}
if (key) {
const worker = this.workerList[key]
await worker.w?.postMessage(data)
worker.w?.terminate()
this.workerList[key].status = 'stop'
}
if (goodsId) {
let i = this.workerList.findIndex(e => e.goodsId == goodsId)
if (i != -1) {
data.msg = 'byGoodsId'
const worker = this.workerList[i]
await worker.w?.postMessage(data)
worker.w?.terminate()
this.workerList[i].status = 'stop'
}
}
}
async terminateWorkerAll() {
let data = {
type: 'stop',
msg: 'allStop'
}
for (const i in this.workerList) {
let worker = this.workerList[i]
await worker.w?.postMessage(data)
worker.w?.terminate()
this.workerList[i].status = 'stop'
}
}
/** 优化前检测 */
checkPlaceBefore(materialList, blockList) {
const arr = this.checkMetrial(materialList)
const arr1 = this.checkBlocks(materialList, blockList)
let errMsg = ''
if (arr.length > 0 || arr1.length > 0) {
errMsg = errMsg + (arr.length > 0 ? '该订单内大板有异常' : '')
errMsg = errMsg + (arr1.length > 0 ? '该订单内小板有异常' : '')
}
return errMsg
}
/** 检查是否 板材尺寸大于机台尺寸 */
checkMetrial(materialList): any[] {
let errPMs: any = []
if (this._params.placeStyle !== 1) {
for (let pm of materialList) {
if (pm.orgWidth > this._params.boardWidth || pm.orgLength > this._params.boardLength) {
errPMs.push(pm)
}
}
}
return errPMs
}
/**
* 正纹 = 0, 可翻转 = 1, 反纹 = 2;
* 正文 Positive = 0, 反纹 Reverse = 1, 可翻转 CanReversal = 2,
*/
toLine(texture): LineType {
if (texture == 0)
return LineType.Positive
if (texture == 1)
return LineType.CanReversal
return LineType.Reverse
}
/** 小板加工面Positive=0正面 Reverse=1反面 Arbitrary=2任意 */
toface(block: PlaceBlock): ComposingType {
let turnF = block.isTurnFaceToPlace
if (this._params.isTurnFaceToPlace)
turnF = !turnF
if (!turnF)
return ComposingType.Positive
return ComposingType.Reverse
}
/** 检查异常板 */
checkBlocks(materialList, blockList): any[] {
let allBlocks: any[] = []
//**20250327 li: 现有的需求情况下 这里去验证异常板 不合理 大板有 没有尺寸的情况 二优化尺寸 根据 优化的弹窗去选择尺寸 做优化 */
/** 有个能参与优化小板的最小尺寸限制 在这里做 */
for (let pm of materialList) {
let bList = blockList.filter(e => e.goodsId == pm.goodsId)
for (const pb of bList) {
if (pb.width < this._params.minBlockWidth) {
allBlocks.push(pb)
}
if (pb.thickness < this._params.minBlockThickness) {
allBlocks.push(pb)
}
}
}
return allBlocks
}
}