400 lines
14 KiB
TypeScript
400 lines
14 KiB
TypeScript
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
|
||
}
|
||
|
||
|
||
|
||
|
||
} |