Files
cut-abstractions/tests/dev1/dataHandle/optimizeLayout/optimizeLayout.ts

400 lines
14 KiB
TypeScript
Raw Normal View History

2025-07-22 18:22:31 +08:00
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
}
}