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 { 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): Promise { // 输入数据 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 } }