/*
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()
}