/* NAME: DESCRIPTION: 客户数据分析 PARAMETER: [ { name : 'step_filter', title : 'Step过滤', type : 'LineEdit', property : {tool_tip : '过滤step信息'} }, { name : 'erf', title : 'erf名称', type : 'LineEdit', property : {tool_tip : ''} }, { name : 'auto_save', title : '自动保存', type : 'RadioBox', property : { item_list:[ {name:'yes',text:'YES'}, {name:'no',text:'NO'}, ], tool_tip:'是否自动保存料号开关' } } ] VERSION_HISTORY: V1.00 2020 3-30 Scott HELP:

功能简介

客户数据分析


参数配置

客户参数

注意事项

● 无


*/ // 引入模块 包 var $ = require('topcam.scriptfunc').argv(); var fs = require('fs'); var _ = require('lodash'); var IKM = $.ikm; var GEN = $.gen; var GUI = $.gui; var Job = $.job; var JobId = $.job_id; var oChecklistName = "mychecklist" var info = { min_line_width: ["line"], min_line_spacing: ["l2l"], min_line2pad: ["p2line"], min_pad2pad: ["p2p", "smd2smd", "smd2pad"], min_ar: ["ar","pth_ar"] } var mode = "develop" // 运行模式 develop 开发模式 方便调试 var pcs_step = "cad" var array_step = "stp" try { // 获取脚本参数 var par = $.par; var parParams = ["step_filter", "erf", "auto_save"]; // par应该有的属性 var isParExist = parParams.reduce(function (a, b) { if (!par.hasOwnProperty(b)) { a = false; } else if (par[b] == "") { switch (b) { case "step_filter": par.pcs_step = "pcs"; break; case "auto_save": par.auto_save = "yes"; break; } } return a; }, true); if(!isParExist){return "Error"} // 如果par没有parParams里的属性 就退出 // ! tmp< par.vc_src_01005_pad_result = ".smd" // ! tmp> // 获取matrix相关信息 var job = Job // 分析的料号 var matrixInfo = getMatrixInfo({job:job}) var matrix_analysis = UPLOAD_LAYER_MATRIX({job:job,matrixInfo:matrixInfo}) // 分析matrix // 开始前业务判断 err = beforeStart({job:job}) if(err){ throw err } // 获取matrix signal drill var matrix = matrixInfo.matrix var signalLayers = getLayers({ matrix: matrix, type: "signal" }) var drillLayers = getLayers({ matrix: matrix, type: "drill" }) var stepList = GEN.getStepList({job:Job}) // 获取steplist stepList = stepList.filter(function(v){ // 过滤step var reg = new RegExp("^"+par.step_filter+"$","ig") return reg.test(v) }) // 脚本开始 // 料号数据分析 // var jobInfo = saveJobInfo({job:job,pcs_step:pcs_step,array_step:array_step,matrixInfo:matrixInfo,matrix_analysis:matrix_analysis},par) // 分析钻孔属性 // todo 镭射切割长度 analysis_drill({job:job,pcs_step:pcs_step,array_step:array_step,matrixInfo:matrixInfo,matrix_analysis:matrix_analysis},par) // 层数据分析 // var hasattrs = [".smd"] // var ishasattrs = hasattrs.reduce(function(a,b){ // 判断是否含有.smd属性 // if(!hasAttr({job:job,steplist:stepList,attr:b,layers:matrixInfo.mOuters})){ a = false } // return a // },true) // if(!ishasattrs){ // 如果找不到.smd 分析.smd // GEN.COM("chklist_single,action=valor_cleanup_set_smd,show=yes") // GEN.COM("chklist_cupd,chklist=valor_cleanup_set_smd,nact=1,params=((pp_layer=.type=signal|mixed&side=top|bottom)(pp_drill=No)(pp_rotate=Yes)(pp_ignore_covered=Yes)(pp_sm=No)(pp_types=Square\;Rect\;Oval)(pp_other=)(pp_delete=No)),mode=regular") // GEN.COM("chklist_run,chklist=valor_cleanup_set_smd,nact=1,area=profile") // GEN.COM("chklist_close,chklist=valor_cleanup_set_smd,mode=hide") // } // // 分析smd // var smdInfo = smdAnalysis({job:Job,steplist:stepList,layers:matrixInfo.mOuters,attr:".smd"}) // // 分析bga // var bgaInfo = bgaAnalysis({job:Job,steplist:stepList,layers:matrixInfo.mOuters,attr:".bga"}) // if(JSON.stringify(bgaInfo) == "{}"){ // GUI.msg("can not find .bga attr") // } // var save_info = [smdInfo, bgaInfo, jobInfo.laser_info]; // 保存 smd 和 bga数据 // save_info.forEach(function(item){ // Object.keys(item).forEach(function(key){ // var val = item[key] // IKM.save_layerinfo({ // jobid: JobId, // layer: key, // layerinfohash: val // }) // }) // }) // stepList.forEach(function(step){ // GEN.openStep({ job: Job, step: step }) // GEN.clearLayers() // GEN.affectedLayer({ affected: 'no', mode: 'all' }) // // 创建chklist并运行 如果chklist存在先删除 // if (GEN.isChklistExists({ job: Job, step: step, chklist: oChecklistName })) { // GEN.COM("chklist_delete", { chklist: oChecklistName }) // } // createChklistAndRun({ // 创建checklist并运行 // layers: signalLayers, // items: [{ // name: "signal_layer_checks", // nact: 1, // action: "valor_analysis_signal", // erf: par.erf, // params: { // ? // pp_layer: ".affected", // pp_spacing: 20, // pp_selected: "All", // pp_r2c: 10, // pp_d2c: 15, // pp_sliver: 8, // pp_min_pad_overlap: 5, // pp_check_missing_pads_for_drills: "Yes", // pp_use_compensated_rout: "Skeleton", // pp_sm_spacing: "Yes" // } // }] // }) // // signal层分析结果 // var res = analysisChkAttr({layers:signalLayers, info:info, step:step}) // // 铜面积 // var copper_percent_pcs = signalLayers.map(function(v){ // var tmp = {layer:v} // tmp.copper_percent = GEN.copperArea({layer1:v}).percent + "%" // return tmp // }) // // 钻孔 // var drillToSignals = drillLayers.map(function(v){ // 获取到钻孔层对应的顶层和底层 // var simbols = GEN.getLayerSymsHist({job:Job,step:step,layer:v}) // var symbol = _.values(simbols).sort(function(a,b){return Number(a.size)-Number(v.size)}) // .reduce(function(a,b){ // if(b.pad!="0"){ // a.push(b.symbol) // } // return a // },[])[0] // return {layer:v,symbol:symbol,start:matrix[v]["drl_start"],end:matrix[v]["drl_end"]} // }) // var drillRes = analysisDrill(drillToSignals,step) // 钻孔分析结果 // // 数据入库 // Object.keys(res).forEach(function(key){ // var val = res[key] // IKM.save_layerinfo({ // jobid: JobId, // layer: key, // layerinfohash: val // }) // }) // copper_percent_pcs.forEach(function(item){ // IKM.save_layerinfo({ // jobid: JobId, // layer: item.layer, // layerinfohash: { // copper_percent: item.copper_percent // } // }) // }) // drillRes.forEach(function(item){ // IKM.save_layerinfo({ // jobid: JobId, // layer: item.layer, // layerinfohash: { // drl_pad_top: item.drl_pad_top, // drl_pad_bot: item.drl_pad_bot, // } // }) // }) // }) // matrix信息 if (/yes/ig.test(par.auto_save)) { GEN.saveJob({ job: Job }); } GEN.checkInout({job:Job,mode:"in"}) GEN.closeJob({job:Job}) return 'Done'; } catch (error) { GUI.msg(error); return 'Error'; } function getLayers(par) { // 获取操作层 {type:"drill", matrix: matrix} var arr = [] for (var key in par.matrix) { var val = matrix[key] if (val.context == "board" && val.layer_type == par.type) { arr.push(val.name) } } return arr } function createChklistAndRun(par) { var checkLayers = par.layers var items = par.items GEN.createChklist({ // 创建checklist chklist: oChecklistName, items: items }) GEN.chklistShow({ chklist: oChecklistName }) GEN.affectedLayer({ affected: "yes", layer: checkLayers, clear_before: "yes" }) items.forEach(function(v){ GEN.chklistRun({ chklist: oChecklistName, nact: v.nact, area: 'profile' }) }) } function analysisChkAttr(par) { var layers = par.layers var hash = {} var info = par.info layers.forEach(function (v) { hash[v] = {} Object.keys(info).forEach(function (key) { var val = info[key] hash[v][key] = val.reduce(function (a, type) { var tmp = GEN.getCheckAttr({ job: Job, step: par.step, checklist: oChecklistName, nact: 1, attr: v + "_min_" + type }) if (a === "N/A" && /\d+/.test(tmp)) { a = tmp } if (/\d+/.test(a) && /\d+/.test(tmp) && Number(tmp) < Number(a)) { a = tmp } return a }, "N/A") }) }) return hash } function analysisDrill(par, step){ // {"layer":"d1-2","symbol":"r3.937","start":"top","end":"isl2"} var res = par.map(function(drill){ GEN.affectedLayer({affected:"yes",layer:drill.layer,clear_before:"yes",mode:"all"}) GEN.selClearFeature() GEN.selectByFilter({feat_types:"pad",include_syms:drill.symbol}) // 拷贝到_tmp var tmplayer = drill.layer + "_tmp" if(GEN.isLayerExists({job:Job,layer:tmplayer})){GEN.deleteLayer({job:Job,step:step,layer:tmplayer})} GEN.selCopyOther({dest:"layer_name",target_layer:tmplayer,invert:'no',dx:0,dy:0,size:0}) GEN.affectedLayer({affected:"no",mode:"all"}) GEN.selClearFeature(); [drill.start,drill.end].forEach(function(item, i){ GEN.workLayer({name:item,display_number:1,clear_before:"yes"}) GEN.selRefFeat({ layers: tmplayer, use: "filter", mode: "include", f_types: "pad", pads_as:"shape", polarity:"positive", include_syms:drill.symbol, filter: { feat_types:'pad' } }) var res = "" var pads = GEN.getFeatures({job:Job,step:step,layer:item,options:"select"}) if(pads && pads.length) { pads = pads.filter(function(item){ return /^r\d+/.test(item.symbol) }) pads = pads.sort(function(a,b){ return parseInt(a.symbol) - parseInt(b.symbol) }) res = pads[0].symbol.slice(1) } if ( i == 0) { drill.drl_pad_top = res } else if (i==1){ drill.drl_pad_bot = res } }) GEN.deleteLayer({job:Job,step:step,layer:tmplayer}) return drill }) return res } function getMatrixInfo(props){ // 获取matrix各种信息 var job = props.job || Job var matrix = GEN.getMatrix({job:job}) var res = { matrix: matrix, matrixVal: [], mSignal: {}, mSignals: [], mDrill: {}, mDrills: [], mSolderMask: {}, mSolderMasks: [], mOuters: [], mBoardLayer: [] } for (var key in matrix) { var value = matrix[key] res.matrixVal.push(value) if(value.context == "board"){ res.mBoardLayer.push(key) switch (value.layer_type) { case "signal": res.mSignal[key] = value res.mSignals.push(value) break; case "drill": res.mDrill[key] = value res.mDrills.push(value) break; case "solder_mask": res.mSolderMask[key] = value res.mSolderMasks.push(value) break; } } } res.matrixVal = res.matrixVal.sort(function(a,b){return Number(a.row) - Number(b.row)}) res.mSignals = res.mSignals.sort(function(a,b){return Number(a.row) - Number(b.row)}) res.mDrills = res.mDrills.sort(function(a,b){return Number(a.row) - Number(b.row)}) // 找出外层 以及外层对应的防焊层 [{signalL:"top", solderL:"smt"}] res.mOuters = res.mSignals.reduce(function(a,b){ if(b.tl_type == "outer"){ a.push({ signalL: b.name, solderL: res.mSolderMasks.filter(function(v){ return v.side == b.side })[0]["name"] }) } return a },[]) return res } function beforeStart(props){ // 脚本开始前的业务判断 var job = props.job; var j = {job:job} if(!GEN.isJobExists(j)){return "job: "+job+" is not exist"} if(!GEN.isJobOpen(j)){GEN.openJob(j)} if(GEN.checkInout({job:job,mode:"test"}) != 0){ if (mode != "develop"){ return "the job checked" } } GEN.checkInout({job:Job,mode:"out"}) } function hasAttr(props){ // 判断层 有没有部分属性 var job = props.job || Job // 有没有smd和bga属性的物件 var attr = props.attr var res = false try { if(!GEN.isJobOpen({job:Job})){GEN.openJob({job:Job})} var steplist = props.steplist || GEN.getStepList({job:job}) var layers = props.layers steplist.forEach(function(step){ GEN.openStep({job:job,name:step}) for (var i = 0;i 0){ throw true } } GEN.clearLayers() GEN.closeStep() }) } catch (msg) { res = msg GEN.clearLayers() GEN.closeStep() } return res } function smdAnalysis(props){ var job = props.job var steplist = props.steplist var layers = props.layers var res = {} steplist.forEach(function(step){ GEN.openStep({job:job,step:step}) layers.forEach(function(layer){ // 计算开窗 数量 GEN.workLayer({name:layer.solderL,display_number:2,clear_before:'yes'}) if (!res.hasOwnProperty(layer.solderL)){ res[layer.solderL] = {} } GEN.selectByFilter({profile:'in'}) GEN.selAllFeat() res[layer.solderL].sm_opening_count = GEN.getSelectCount() GEN.selClearFeature() // 分析最小smd宽高 GEN.workLayer({name:layer.signalL,display_number:2,clear_before:'yes'}) GEN.selClearFeature() GEN.selectByFilter({attribute:props.attr}) var selCount = GEN.getSelectCount() if(selCount>0){ var tmp_layer = layer.signalL + "_tmp" selCopyLayer({job:job,layer:tmp_layer}) var symbols = GEN.getLayerSymsHist({job:job,layer:tmp_layer,step:step}) GEN.workLayer({name:tmp_layer,display_number:2,clear_before:'yes'}) var symbolInfo = symbolAnalysis({symbols:symbols,filterReg:/^rect/}) if (!res.hasOwnProperty(layer.signalL)){ res[layer.signalL] = {} } res[layer.signalL]["min_smd_width"] = symbolInfo["min_width"] res[layer.signalL]["min_smd_length"] = symbolInfo["min_length"] // 分析最小smd间距 // 创建一个checklist并且分析 var min_smd_pitch = smdPitch({job:job,step:step,layer:tmp_layer}) res[layer.signalL]["min_smd_pitch"] = min_smd_pitch // 分析开窗宽高 GEN.workLayer({name:layer.solderL,display_number:2,clear_before:'yes'}) GEN.selClearFeature() GEN.selRefFeat({layers:tmp_layer,use:'filter',mode:'include'}) GEN.selRefFeat({layers:tmp_layer,use:'filter',mode:'cover'}) var solderL_tmp = layer.solderL + "_tmp" selCopyLayer({job:job,layer:solderL_tmp}) GEN.selClearFeature() GEN.workLayer({name:solderL_tmp,display_number:2,clear_before:"yes"}) var symbols_solder = GEN.getLayerSymsHist({job:job,layer:solderL_tmp,step:step}) var symbolInfo_solder = symbolAnalysis({symbols:symbols_solder,filterReg:/^rect/}) res[layer.signalL].min_smd_opening_width = symbolInfo_solder["min_width"] res[layer.signalL].min_smd_opening_length = symbolInfo_solder["min_length"] GEN.deleteLayer({job:job,layer:solderL_tmp}) GEN.deleteLayer({job:job,layer:tmp_layer}) } }) GEN.closeStep() }) return res } function bgaAnalysis(props){ var job = props.job var steplist = props.steplist var layers = props.layers var res = {} steplist.forEach(function(step){ GEN.openStep({job:job,step:step}) layers.forEach(function(layer){ // 找出bga 拷贝到辅助层 GEN.workLayer({name:layer.signalL,display_number:2,clear_before:'yes'}) GEN.selClearFeature() GEN.selectByFilter({attribute:props.attr}) var selCount = GEN.getSelectCount() if(selCount>0){ var tmp_layer = layer.signalL + "_tmp" selCopyLayer({job:job,layer:tmp_layer}) var symbols = GEN.getLayerSymsHist({job:job,layer:tmp_layer,step:step}) GEN.workLayer({name:tmp_layer,display_number:2,clear_before:'yes'}) // 分析数据 var syblist = _.values(symbols).reduce(function(a,b){ if (b.pad > 0 && b.line) { a.push({symbol:b.symbol, size:Number(b.size)}) } return a },[]) // 找到最小的bga分析 var min_symbols = syblist.sort(function(a,b){return a.size-b.size})[0] var min_symbols_info = min_symbols_anal({symbols:min_symbols, job:job, step:step, layer:layer}) if (!res.hasOwnProperty(layer.signalL)){ res[layer.signalL] = {} } res[layer.signalL].min_bga_size = min_symbols_info["min_bga_size"] res[layer.signalL].min_bga_openging_size = min_symbols_info["min_bga_openging_size"] res[layer.signalL].min_bga_pitch = min_symbols_info["min_bga_pitch"] // 分析所有BGA var all_bga_min_pitch_info = all_bga_min_pitch_anal({job:job, step:step, layer:tmp_layer,solderLayer:layer.solderL}) res[layer.signalL].bga_min_pitch = all_bga_min_pitch_info["bga_min_pitch"] res[layer.signalL].bga_min_pitch_pad_size = all_bga_min_pitch_info["bga_min_pitch_pad_size"] res[layer.signalL].bga_min_pitch_openging_size = all_bga_min_pitch_info["bga_min_pitch_openging_size"] GEN.deleteLayer({job:job,layer:tmp_layer}) } }) GEN.closeStep() }) return res } function selCopyLayer(props){ var layer = props.layer var job = props.job if(GEN.isLayerExists({job:job,layer:layer})){ GEN.deleteLayer({job:job,layer:layer}) } GEN.selCopyOther({dest:'layer_name',target_layer:layer}) } function symbolAnalysis(props){ var symbols = Object.keys(props.symbols).reduce(function(a,b){ if(props.filterReg.test(b)){ var res = /^rect(\d+\.\d+|\d+)x(\d+\.\d+|\d+)/ig.exec(b).slice(1) var num1 = res[0], num2 = res[1] if(num1<=num2){ a.width.push(num1) a.length.push(num2) } else { a.width.push(num2) a.length.push(num1) } } return a },{width:[],length:[]}) return { min_width:symbols.width.reduce(function(a,b){return b-a > 0 ? a : b}), min_length:symbols.length.reduce(function(a,b){return b-a > 0 ? a : b}) } } function smdPitch(props){ var job = props.job var step = props.step var layer = props.layer var ck = "tmp_chk" if(GEN.isChklistExists({job:job,step:step,chklist:ck})){ GEN.COM("chklist_delete", { chklist: ck }) } // 创建并运行 GEN.createChklist({ // 创建checklist chklist: ck, items: [{ name: "smdPitch", nact: 1, action: "valor_analysis_signal", erf: "S2_conductor_Check", params: { pp_layer: ".affected", pp_spacing: 20, pp_selected: "All", pp_r2c: 10, pp_d2c: 15, pp_sliver: 8, pp_min_pad_overlap: 5, pp_check_missing_pads_for_drills: "Yes", pp_use_compensated_rout: "Skeleton", pp_sm_spacing: "Yes", pp_tests: "Spacing\\;SMD", pp_check_missing_pads_for_drills:"Yes" } }] }) GEN.chklistShow({ chklist: ck }) GEN.affectedLayer({ affected: "yes", layer: layer, clear_before: "yes" }) GEN.chklistRun({ chklist: ck, nact: 1, area: 'profile' }) var tmp_layer1 = "mk_1_"+layer+"_pitch" var tmp_layer2 = "ms_1_"+layer+"_pitch" if(GEN.isLayerExists({job:job,layer:tmp_layer1})){ GEN.deleteLayer({job:job,layer:tmp_layer1}) } if(GEN.isLayerExists({job:job,layer:tmp_layer2})){ GEN.deleteLayer({job:job,layer:tmp_layer2}) } GEN.COM("chklist_create_lyrs,chklist="+ck+",severity=3,suffix=pitch"); GEN.workLayer({name:tmp_layer2,display_number:1,clear_before:'yes'}); GEN.selectByFilter({attribute:[{attribute:".string",text:"smd_pitch"}]}) var tmp_layer = tmp_layer2 + "_tmp" selCopyLayer({job:job,layer:tmp_layer}) GEN.workLayer({name:tmp_layer,display_number:1,clear_before:'yes'}); var symbols = GEN.getLayerSymsHist({job:job,layer:tmp_layer,step:step}) // 获取最小smd间距 var res = _.values(symbols).sort(function(a,b){return Number(a.size)-Number(b.size)})[0].size GEN.deleteLayer({job:job,layer:tmp_layer}) GEN.deleteLayer({job:job,layer:tmp_layer1}) GEN.deleteLayer({job:job,layer:tmp_layer2}) return res } function min_symbols_anal(props){ // 分析最小symbols var job = props.job var step = props.step var layer = props.layer var symbols = props.symbols var res = {} GEN.selClearFeature() GEN.selectByFilter({include_syms:symbols.symbol}) var tmp_layer = layer.signalL + "_tmp_1" selCopyLayer({job:job,layer:tmp_layer}) // 分析数据 // 尺寸 res.min_bga_size = symbols.size // 开窗大小 GEN.workLayer({name:layer.solderL,display_number:2,clear_before:'yes'}) GEN.selClearFeature() GEN.selRefFeat({layers:tmp_layer,use:'filter',mode:'include'}) GEN.selRefFeat({layers:tmp_layer,use:'filter',mode:'cover'}) var solderL_tmp = layer.solderL + "_tmp" selCopyLayer({job:job,layer:solderL_tmp}) GEN.selClearFeature() GEN.workLayer({name:solderL_tmp,display_number:2,clear_before:"yes"}) var symbols_solder = GEN.getLayerSymsHist({job:job,layer:solderL_tmp,step:step}) res.min_bga_openging_size = _.values(symbols_solder).reduce(function(a,b){ if (b.pad > 0 && b.line) { a.push({symbol:b.symbol, size:Number(b.size)}) } return a },[]).sort(function(a,b){return a.size-b.size})[0].size // 间距 res.min_bga_pitch = bgaPitch({job:job,step:step,layer:tmp_layer}) GEN.deleteLayer({job:job,layer:solderL_tmp}) GEN.deleteLayer({job:job,layer:tmp_layer}) return res } function all_bga_min_pitch_anal(props){ // 分析所有bga中间距最小的 var job = props.job var step = props.step var layer = props.layer var symbols = props.symbols var solder_layer = props.solderLayer var res = {} var ck = "tmp_chk" if(GEN.isChklistExists({job:job,step:step,chklist:ck})){ GEN.COM("chklist_delete", { chklist: ck }) } // 创建并运行 GEN.createChklist({ // 创建checklist chklist: ck, items: [{ name: "bgaPitch", nact: 1, action: "valor_analysis_signal", erf: "S2_conductor_Check", params: { pp_layer: ".affected", pp_spacing: 20, pp_selected: "All", pp_r2c: 10, pp_d2c: 15, pp_sliver: 8, pp_min_pad_overlap: 5, pp_check_missing_pads_for_drills: "Yes", pp_use_compensated_rout: "Skeleton", pp_sm_spacing: "Yes", pp_tests: "Spacing", pp_check_missing_pads_for_drills:"Yes" } }] }) GEN.chklistShow({ chklist: ck }) GEN.affectedLayer({ affected: "yes", layer: layer, clear_before: "yes" }) GEN.chklistRun({ chklist: ck, nact: 1, area: 'profile' }) var tmp_layer1 = "mk_1_"+layer+"_pitch" var tmp_layer2 = "ms_1_"+layer+"_pitch" if(GEN.isLayerExists({job:job,layer:tmp_layer1})){ GEN.deleteLayer({job:job,layer:tmp_layer1}) } if(GEN.isLayerExists({job:job,layer:tmp_layer2})){ GEN.deleteLayer({job:job,layer:tmp_layer2}) } GEN.COM("chklist_create_lyrs,chklist="+ck+",severity=3,suffix=pitch"); GEN.workLayer({name:tmp_layer2,display_number:1,clear_before:'yes'}); GEN.selectByFilter({attribute:[{attribute:".string",text:"bga_pitch"}]}) var tmp_layer = tmp_layer2 + "_tmp" selCopyLayer({job:job,layer:tmp_layer}) GEN.workLayer({name:tmp_layer,display_number:1,clear_before:'yes'}); var symbols = GEN.getLayerSymsHist({job:job,layer:tmp_layer,step:step}) // 获取最小smd间距 var min_symbol = _.values(symbols).sort(function(a,b){return Number(a.size)-Number(b.size)})[0] res.bga_min_pitch = min_symbol.size // 所有BGA中间距最小的PAD大小 GEN.selClearFeature() GEN.selectByFilter({include_syms:min_symbol.symbol}) var min_pitch_layer = tmp_layer + "_min" selCopyLayer({job:job,layer:min_pitch_layer}) GEN.workLayer({name:layer,display_number:1,clear_before:'yes'}); GEN.selClearFeature() GEN.selRefFeat({layers:min_pitch_layer,use:'filter',mode:'touch'}) var min_pad_layer = layer + "_min_pad" selCopyLayer({job:job,layer:min_pad_layer}) GEN.workLayer({name:min_pad_layer,display_number:1,clear_before:'yes'}); var min_pad_symbols = GEN.getLayerSymsHist({job:job,layer:min_pad_layer,step:step}) var min_pad_symbol = _.values(min_pad_symbols).sort(function(a,b){return Number(a.size)-Number(b.size)})[0] res.bga_min_pitch_pad_size = min_pad_symbol.size // 最小pitch开窗大小 GEN.workLayer({name:solder_layer,display_number:1,clear_before:'yes'}); GEN.selClearFeature() GEN.selRefFeat({layers:min_pitch_layer,use:'filter',mode:'touch'}) var solder_layer_tmp = solder_layer + "_tmp" selCopyLayer({job:job,layer:solder_layer_tmp}) GEN.selClearFeature() GEN.workLayer({name:solder_layer_tmp,display_number:2,clear_before:"yes"}) var symbols_solder = GEN.getLayerSymsHist({job:job,layer:solder_layer_tmp,step:step}) var symbols_solder_list = _.values(symbols_solder).filter(function(v){return /\d+/ig.test(v.size)}) if(symbols_solder_list.length){ res.bga_min_pitch_openging_size = symbols_solder_list.sort(function(a,b){return Number(a.size)-Number(b.size)})[0].size } GEN.deleteLayer({job:job,layer:solder_layer_tmp}) GEN.deleteLayer({job:job,layer:min_pad_layer}) GEN.deleteLayer({job:job,layer:min_pitch_layer}) GEN.deleteLayer({job:job,layer:tmp_layer}) GEN.deleteLayer({job:job,layer:tmp_layer1}) GEN.deleteLayer({job:job,layer:tmp_layer2}) return res } function bgaPitch(props){ var job = props.job var step = props.step var layer = props.layer var ck = "tmp_chk" if(GEN.isChklistExists({job:job,step:step,chklist:ck})){ GEN.COM("chklist_delete", { chklist: ck }) } // 创建并运行 GEN.createChklist({ // 创建checklist chklist: ck, items: [{ name: "bgaPitch", nact: 1, action: "valor_analysis_signal", erf: "S2_conductor_Check", params: { pp_layer: ".affected", pp_spacing: 20, pp_selected: "All", pp_r2c: 10, pp_d2c: 15, pp_sliver: 8, pp_min_pad_overlap: 5, pp_check_missing_pads_for_drills: "Yes", pp_use_compensated_rout: "Skeleton", pp_sm_spacing: "Yes", pp_tests: "Spacing", pp_check_missing_pads_for_drills:"Yes" } }] }) GEN.chklistShow({ chklist: ck }) GEN.affectedLayer({ affected: "yes", layer: layer, clear_before: "yes" }) GEN.chklistRun({ chklist: ck, nact: 1, area: 'profile' }) var tmp_layer1 = "mk_1_"+layer+"_pitch" var tmp_layer2 = "ms_1_"+layer+"_pitch" if(GEN.isLayerExists({job:job,layer:tmp_layer1})){ GEN.deleteLayer({job:job,layer:tmp_layer1}) } if(GEN.isLayerExists({job:job,layer:tmp_layer2})){ GEN.deleteLayer({job:job,layer:tmp_layer2}) } GEN.COM("chklist_create_lyrs,chklist="+ck+",severity=3,suffix=pitch"); GEN.workLayer({name:tmp_layer2,display_number:1,clear_before:'yes'}); GEN.selectByFilter({attribute:[{attribute:".string",text:"bga_pitch"}]}) var tmp_layer = tmp_layer2 + "_tmp" selCopyLayer({job:job,layer:tmp_layer}) GEN.workLayer({name:tmp_layer,display_number:1,clear_before:'yes'}); var symbols = GEN.getLayerSymsHist({job:job,layer:tmp_layer,step:step}) // 获取最小smd间距 var res = _.values(symbols).sort(function(a,b){return Number(a.size)-Number(b.size)})[0].size GEN.deleteLayer({job:job,layer:tmp_layer}) GEN.deleteLayer({job:job,layer:tmp_layer1}) GEN.deleteLayer({job:job,layer:tmp_layer2}) // 矩阵 return res } function saveJobInfo(props,par){ var job = props.job var pcs = props.pcs_step var arr = props.array_step var matrix_analysis = props.matrix_analysis var matrixInfo = props.matrixInfo var matrix = matrixInfo.matrix var info = { layer_count: "", // Board属性的signal或者power_ground层 vc_card_size_w: "", // card短边尺寸 vc_card_size_l: "", // card长边尺寸 vc_array_size_w: "", // array短边尺寸 vc_array_size_l: "", // array长边尺寸 vc_pcs_count_on_panel: "", // todo array中pcs的数量 stack_vias_number: "", // via孔连续叠加的层数 stack_vias_more: "", // yes|no : 14层板以上时,Stack Vias >=12时,存yes depth_drilling: "", // yes|no :存在depth_drill 层时,,存yes depth_routing: "", // yes|no :存在depth_routing 层时,,存yes laser_via_on_buried_hole: "", // todo yes|no:via孔在埋孔上时,存yes milling_bit_size: "", // todo 检查array中pcs的最小间距值 milling_length: "", // todo 检查array中铣切长度 vc_src_01005_pad_result: "", // yes|no:board层中检查存在01005属性物件时,存yes ATS_technology_25dc: "", // yes|no:存在cavity层别时存yes ATS_technology_25dr: "", // yes|no:存在cavity层别时存yes vc_src_EDGE_PLATING: "", // yes|no:检查线路外形是否存在物件,存在则存yes edge_plating_length: "", // todo glod_finger: "", // todo glod_finger_area: "", // todo solder_mask_side: "", // top|bot|both:检查防焊层所在面次 silk_screen_side: "", // top|bot|both:检查文字层所在面次 } info.layer_count = matrixInfo.mSignals.length // 10 var pcs_profileLimits = GEN.getProfileLimits({job:job,step:pcs}) var array_profileLimits = GEN.getProfileLimits({job:job,step:arr}) info.vc_card_size_w = pcs_profileLimits.xsize.toFixed(3) info.vc_card_size_l = pcs_profileLimits.ysize.toFixed(3) info.vc_array_size_w = array_profileLimits.xsize.toFixed(3) info.vc_array_size_l = array_profileLimits.ysize.toFixed(3) // IKM.msg(GEN.getRepeat({job:"1",step:"stp1"})) // ? // 找出 镭射孔 var laser_layers = [] for (var key in matrix_analysis) { var val = matrix_analysis[key] if(val.type == "laser_drill"){ laser_layers.push(val) } } GEN.openStep({job:job,name:pcs}) laser_layers = laser_layers.sort(function(a,b){return Number(a.row)-Number(b.row)}) var laser_info = {} for(var i = 0; i < laser_layers.length-1; i++){ var start_layer = laser_layers[i].name var cover_layer = laser_layers[i+1].name GEN.workLayer({name:start_layer,display_number:2,clear_before:'yes'}) GEN.selClearFeature() GEN.selRefFeat({layers:cover_layer,use:'filter',mode:'cover'}) var count = GEN.getSelectCount() if(count>0){ laser_info[start_layer] = {} laser_info[start_layer].drill_connect_layer = cover_layer laser_info[start_layer].drill_connect_point = count } } // via孔连续叠加的层数 var via_vias_info = [] function analysis_via_number(layers){ if(layers.length < 2){return} var startlayer = layers[0].name + "_cover" GEN.selClearFeature() GEN.workLayer({name:layers[0].name,display_number:2,clear_before:'yes'}) GEN.selAllFeat() selCopyLayer({job:job,layer:startlayer}) var end_index = start_cover_next(startlayer,layers,0) via_vias_info.push(end_index+1) analysis_via_number(layers.slice(end_index)) } function start_cover_next(start, layers, end_index){ if(layers.length < 2){ GEN.deleteLayer({job:job,layer:start}) return end_index } layers = layers.slice(1) GEN.workLayer({name:start,display_number:2,clear_before:'yes'}) GEN.selClearFeature() GEN.selRefFeat({layers:layers[0].name,use:'filter',mode:'cover'}) var count = GEN.getSelectCount() if (count < 1) { GEN.deleteLayer({job:job,layer:start}) return ++end_index } var nextstartlayer = layers[0].name + "_cover" selCopyLayer({job:job,layer:nextstartlayer}) GEN.deleteLayer({job:job,layer:start}) return start_cover_next(nextstartlayer,layers,++end_index) } analysis_via_number(laser_layers.slice()) if (via_vias_info.length == 1){ info.stack_vias_number = via_vias_info[0] }else{ info.stack_vias_number = via_vias_info.reduce(function(a,b){return a-b>0?a :b}) } info.stack_vias_more = "no" if(GEN.getLayerCount({job:job}) > 14 && info.stack_vias_number >= 12){ info.stack_vias_more = "yes" } // via孔在埋孔上? info.depth_drilling = matrix.hasOwnProperty("depth_drill") ? "yes" : "no" info.depth_routing = matrix.hasOwnProperty("depth_routing") ? "yes" : "no" var is_cavity = matrix.hasOwnProperty("cavity") ? "yes" : "no" info.ATS_technology_25dc = is_cavity // no info.ATS_technology_25dr = is_cavity // no var solder_paste_layers = _.values(matrix).filter(function(v){return v.layer_type == "solder_paste"}) var solder_solder_mask = _.values(matrix).filter(function(v){return v.layer_type == "solder_mask"}) var solder_paste_info= solder_paste_layers.map(function(v){return v.side}).reduce(function(a,b){ if(a.indexOf(b)<0){ a.push(b) } return a },[]) var solder_solder_info= solder_solder_mask.map(function(v){return v.side}).reduce(function(a,b){ if(a.indexOf(b)<0){ a.push(b) } return a },[]) if(solder_paste_info.length == 0){ info.solder_mask_side = "no" } else if (solder_paste_info.length == 1) { info.solder_mask_side = solder_paste_info[0] } else { info.solder_mask_side = "both" } if(solder_solder_info.length == 0){ info.silk_screen_side = "no" } else if (solder_solder_info.length == 1) { info.silk_screen_side = solder_solder_info[0] } else { info.silk_screen_side = "both" } // 线路外形 info.vc_src_EDGE_PLATING = "no" GEN.affectedLayer({affected:'no',mode:'all'}) try { matrixInfo.mSignals.forEach(function(v){ GEN.workLayer({name:v.name,display_number:2,clear_before:'yes'}) GEN.selClearFeature() GEN.selRefFeat({layers:"rout",mode:"touch",use:"filter"}) if( GEN.getSelectCount()>0){ throw "yes" } }) } catch (msg) { info.vc_src_EDGE_PLATING = msg GEN.affectedLayer({affected:'no',mode:'all'}) GEN.selClearFeature() } info = Object.keys(info).reduce(function(a,b){// 过滤结果 if(info[b] != ""){ a[b] = info[b] } return a },{}) // 有无属性vc_src_01005_pad_result GEN.openStep({job:job,name:pcs}) GEN.affectedLayer({affected:'yes',layer:matrixInfo.mBoardLayer,clear_before:'yes'}); GEN.selClearFeature() GEN.selectByFilter({attribute:par.vc_src_01005_pad_result}) info.is_01005_pad = GEN.getSelectCount() > 0? "yes" : "no" GEN.affectedLayer({affected:'no',mode:'all'}); GEN.closeStep() IKM.save_job_info({ jobid: JobId, jobinfohash:info }) return {info:info,laser_info:laser_info} } function UPLOAD_LAYER_MATRIX(props){ var matrixInfo = props.matrixInfo var job = props.job props.jobcategory = "work" var matrix = ANALYSIS_STACKUP({job:job, matrixInfo:matrixInfo, jobcategory:props.jobcategory}); var func = 'function(ARGV)\ {\ var jobId = ARGV["job_id"];\ var tableName = "pdm_job_" + ARGV["jobcategory"] + "_layer";\ //GUI.msgbox({text: TDataParse.variant2JsonStr(tableName)});\ var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());\ db.begin();\ try{\ var oLayers = db.selectMapMap({table:tableName, field:["odb_name","release_status"], where:{job_id:jobId}, uniquefield:"odb_name"});\ if (db.lastError().isValid()) throw db.lastError();\ //GUI.msgbox({text: TDataParse.variant2JsonStr(oLayers)});\ \ db.deleteRow({table:tableName, where:{job_id:jobId}});\ if (db.lastError().isValid()) throw db.lastError();\ \ var fieldLst = ["name", "odb_context", "odb_type", "odb_polarity", "odb_side", "drl_start", "drl_end",\ "row_num", "type", "drl_start_num", "drl_end_num", "drl_from_num", "drl_to_num", "drl_connect_to", \ "odb_name", "side", "stackup_num", "customer_field", "input_file_name", "odb_row_num"];\ \ for (var i=0;i0\ && ARGV["releasestatus"][layerInfo["odb_name"]] != undefined && ARGV["releasestatus"][layerInfo["odb_name"]] != null )\ {\ tmpData["release_status"] = ARGV["releasestatus"];\ }\ else\ {\ tmpData["release_status"] = oLayers.hasOwnProperty(layerInfo["odb_name"]) ? oLayers[layerInfo["odb_name"]]["release_status"] : null;\ }\ for (n = 0; n < fieldLst.length; n++)\ {\ tmpData[fieldLst[n]] = layerInfo[fieldLst[n]];\ }\ //GUI.msgbox({text: TDataParse.variant2JsonStr(tmpData)});\ db.insertRow({table:tableName, data:tmpData});\ if (db.lastError().isValid()) throw db.lastError();\ }\ db.commit();\ return new TDataResponse();\ }\ catch (err)\ {\ print(err.text());\ db.rollback();\ return new TDataResponse(err, "");\ }\ }'; // var ret = IKM.command( func, // { job_id :JobId, // matrix:matrix, // layers: Object.keys(matrix).sort(function(a,b){return matrix[a]["row"] - matrix[b]["row"]}), // jobcategory:props.jobcategory}, 1); // if (ret.errText) { // return { // jobcategory:props.jobcategory, // errText :ret.errText, // errCode :ret.errCode, // }; // } return matrix } function ANALYSIS_STACKUP(props){ var job = props.job if(!props.hasOwnProperty("jobcategory")){ props.jobcategory = 'work' } var matrix = JSON.parse(JSON.stringify(props.matrixInfo.matrix)) var layer_count = IKM.get_jobinfo({jobname:JobId,jobcategory:props.jobcategory,jobinfo:'layer_count'}); if(!layer_count){ layer_count = GEN.getLayerCount({job:job}) } IKM.save_job_info({jobid:JobId,jobcategory:'work',jobinfohash:{TL_layer_count:layer_count}}); _.values(matrix).sort(function(a,b){return a.row-b.row}).forEach(function(layer){ layer.odb_name = layer.name; layer.name = layer.tl_name; layer.odb_context = layer.context; layer.odb_type = layer.layer_type; layer.odb_polarity = layer.polarity; layer.odb_side = layer.side; layer.row_num = layer.tl_num; layer.type = layer.tl_type; layer.side = layer.enum_tl_side; layer.input_file_name = layer.file_name; layer.odb_row_num = layer.row; if (layer.odb_name == 'drill'){ layer.name = 'drill'; layer.drl_start_num = 1; layer.drl_end_num = layer_count; layer.drl_from_num = 1; layer.drl_to_num = layer_count; layer.type = 'main_drill'; } else if (/^txt(\d+)\-(\d+)$/.test(layer.odb_name)){ var tmp = /^txt(\d+)\-(\d+)$/.exec(layer.odb_name) var drl_star = tmp[1]; var drl_end = tmp[2]; layer.name = 'drill' + drl_star + '-' + drl_end; layer.drl_start_num = drl_star; layer.drl_end_num = drl_end; layer.drl_from_num = drl_star; layer.drl_to_num = drl_end; if( drl_star == 1 || drl_end == layer_count ){ layer.type = 'blind_drill'; } else{ layer.type = 'bury_drill'; } } else if (/^d(\d+)\-(\d+)$/.test(layer.odb_name)){ var tmp = /^d(\d+)\-(\d+)$/.exec(layer.odb_name) var drl_star = tmp[1]; var drl_end = tmp[2]; layer.name = 'd'+drl_star+'-'+drl_end; layer.drl_start_num = drl_star; layer.drl_end_num = drl_end; layer.drl_from_num = drl_star; layer.drl_to_num = drl_end; layer.type = 'laser_drill'; } else if (/\-a$/.test(layer.odb_name)){ layer.side = 'top'; if(!layer.name){ layer.name = '__'+layer.odb_name+'__' } if(!layer.type){ layer.type = 'other' } } else if (/\-b$/.test(layer.odb_name)){ layer.side = 'bottom'; if(!layer.name){ layer.name = '__'+layer.odb_name+'__' } if(!layer.type){ layer.type = 'other' } } else{ if(!layer.name){ layer.name = '__'+layer.odb_name+'__' } if(!layer.type){ layer.type = 'other' } } }) return matrix; } function analysis_drill(props,par){ // 分析钻孔 var job = props.job var pcs = props.pcs_step var arr = props.array_step var matrix = props.matrix_analysis // 获取镭射层 var laser_layers = Object.keys(matrix).filter(function(key){ return matrix[key].type == "laser_drill" && matrix[key].odb_context=="board" }) GEN.openStep({job:job,name:pcs}) laser_layers.forEach(function(layer){ GEN.workLayer({name:layer,display_number:1,clear_before:"yes"}) GEN.selClearFeature() GEN.selAllFeat() GEN.selAddAttr({attribute:"via"}) // ? GEN.selAllFeat() }) GEN.closeStep() }