/* NAME: DESCRIPTION: 客户数据分析 PARAMETER: [ { name : 'pcs_step', title : 'pcsStep名', type : 'LineEdit', property : {tool_tip : 'pcs step名,默认是cad'} }, { name : 'cam_type', title : 'cam_type', type : 'LineEdit', property : {tool_tip : 'cam_type'} }, { name : 'vc_src_01005_pad_result', title : 'vc_src_01005_pad_result', type : 'LineEdit', property : {tool_tip : 'vc_src_01005_pad_result'} }, { name : 'array_step', title : 'arrayStep名', type : 'LineEdit', property : {tool_tip : 'array step名,默认是stp'} }, { name : 'erf', title : 'erf名称', type : 'LineEdit', property : {tool_tip : 'erf名称'} }, { name : 'auto_save', title : '自动保存', type : 'RadioBox', property : { item_list:[ {name:'yes',text:'YES'}, {name:'no',text:'NO'}, ], tool_tip:'是否自动保存料号开关' } }, { name : 'export_path', title : '导出路径', type : 'LineEdit', property : {tool_tip : '导出路径'} }, { name : 'export_mode', title : '导出模式', type : 'RadioBox', property : { item_list:[ {name:'tar_gzip',text:'tar_gzip'}, {name:'tar',text:'tar'}, {name:'xml',text:'xml'}, {name:'directory',text:'directory'}, ], tool_tip:'导出模式' } }, { name : 'export_submode', title : '导出方式', type : 'RadioBox', property : { item_list:[ {name:'full',text:'full'}, {name:'partial',text:'partial'}, ], tool_tip:'导出方式' } } ] VERSION_HISTORY: V1.00 2020 3-30 Scott HELP:

功能简介

客户数据分析


参数配置

客户参数

注意事项

● 无


*/ // 引入模块 包 var $ = require('topcam.scriptfunc').argv(); var fs = require('fs'); var _ = require('lodash'); var JobId = $.job_id; var database = require("topsin.database"); var Omatrix = {}; database.addConnection($.conf.database_conf, "DFM"); var QDfm = database.query("DFM"); if ($.conf.product_type == "aimdfm") { QDfm.updateRow({ table: "pdm_aimdfm_task", data: { current_process_title: $.process_title }, where: { id: $.task_id } }); } var GEN = $.gen; var Job = $.job_name; var db = $.db; var Status = 'ok'; var resultData = []; var PAR = {}; var ALL = {} if ($.hasOwnProperty('script_parameter')){ PAR = JSON.parse($.script_parameter); } try { console.log("=============================================>anaysis_start"); var par = PAR; var vc_src_01005_pad_result = par.vc_src_01005_pad_result if(vc_src_01005_pad_result == ""){ vc_src_01005_pad_result = ".pth_pad" } if(par.erf == ""){delete PAR.erf} if(_.isEmpty(Job)) throw "没有传入料号名!"; var job = Job.toLowerCase(); if(!GEN.isJobExists({job:job})){ throw "job "+ job+ " is not exist" } // 检查料号是否能够check out if(GEN.checkInout({job:job,mode:"test"}) != 0){ throw "the job check" } GEN.checkInout({job:job,mode:"out"}); var pcs_step = par.pcs_step == "" ? "cad" : par.pcs_step; var array_step = par.array_step == "" ? "stp" : par.array_step; var step_list = GEN.getStepList({job:job}) if(step_list.indexOf(array_step)<0){ array_step = false } if(step_list.indexOf(pcs_step)<0){ pcs_step = step_list[0] } var matrix = UPLOAD_LAYER_MATRIX({job:job}) // 分析matrix 获得分析后的matrix信息 GEN.openStep({job:job, name:pcs_step}) _.values(matrix).forEach(function(v){ if(v.context == "board" && v.layer_type == "drill"){ if(v.type == "laser_drill"){ GEN.affectedLayer({affected:'no',mode:'all'}) GEN.workLayer({name:v.odb_name,display_number:2,clear_before:'yes'}) GEN.selClearFeature() GEN.selAllFeat() if(GEN.getSelectCount()>0){ unSelect() if(GEN.getSelectCount() > 0){ GEN.COM("cur_atr_set,attribute=.drill,option=via") GEN.COM("sel_change_atr,mode=add") } } GEN.clearLayers() GEN.selClearFeature() } else if(v.type == "main_drill" || v.type == "blind_drill" || v.type == "bury_drill") { GEN.affectedLayer({affected:'no',mode:'all'}) // GEN.workLayer({name:v.odb_name,display_number:2,clear_before:'yes'}) GEN.selClearFeature() var tmp_layer = v.drl_start + "tmp" GEN.workLayer({name:v.drl_start,display_number:2,clear_before:'yes'}) GEN.selCopyOther({dest:'layer_name',target_layer:tmp_layer,invert:'no',dx:0,dy:0,size:0}) GEN.workLayer({name:tmp_layer,display_number:2,clear_before:'yes'}) GEN.selRefFeat({layers:v.odb_name,use:'filter',mode:'cover'}) if(GEN.getSelectCount()> 0){GEN.selDelete()} GEN.workLayer({name:v.odb_name,display_number:2,clear_before:'yes'}) GEN.selRefFeat({layers:tmp_layer,use:'filter',mode:'touch'}) if(GEN.getSelectCount()>0){ unSelect() if(GEN.getSelectCount() > 0){ GEN.COM("cur_atr_set,attribute=.drill,option=plated") GEN.COM("sel_change_atr,mode=add") } } GEN.selReverse() if(GEN.getSelectCount() > 0){ unSelect() if(GEN.getSelectCount() > 0){ GEN.COM("cur_atr_set,attribute=.drill,option=non_plated") GEN.COM("sel_change_atr,mode=add") } } GEN.deleteLayer({job:job,layer:[tmp_layer]}) } } }) GEN.clearLayers() // * new 钻孔叠构 var cam_drill_structure = [] var stack_drills = _.values(matrix).filter(function(item){ return item.layer_type == "drill" && item.context == "board" }) var layerCount = GEN.getLayerCount({job:job}) stack_drills.forEach(function(drill){ GEN.clearLayers() GEN.selClearFeature() var drill_info = {} var drl_start_num = drill.drl_start_num -0 var drl_end_num = drill.drl_end_num-0 drill_info.drl_start_num = drl_start_num drill_info.drl_end_num = drl_end_num var drl_side = "None" if(layerCount/2 >= drl_start_num){ drl_side = "Top" } else { drl_side = "Bot" } drill_info.drl_side = drl_side var drl_filler = "None" if(drill.type == "laser_drill"){ drill_info.drl_type = "Laser" drill_info.drl_name = "Laser" + drl_start_num + "-" + drl_end_num drill_info.drl_tech = "LDD" // 计算此层的理论 top bot if(drl_side == "Top"){ if((drl_start_num-1)==0){ drl_filler = "None" } else { var top_l = "ftdrill" + (drl_start_num-1) + "-" + (drl_end_num-1) + "l" if(GEN.isLayerExists({job:job, layer:top_l})){ GEN.clearLayers() GEN.selClearFeature() GEN.workLayer({name:top_l,display_number:2,clear_before:'yes'}) GEN.selRefFeat({layers:drill.odb_name,use:'filter',mode:'touch'}) if(GEN.getSelectCount()>0){ GEN.selClearFeature() drl_filler="Via Filling" } } } var bot_l = "ftdrill" + (drl_start_num+1) + "-" + (drl_end_num+1) + "l" if(GEN.isLayerExists({job:job, layer:bot_l})){ GEN.clearLayers() GEN.selClearFeature() GEN.workLayer({name:drill.odb_name,display_number:2,clear_before:'yes'}) GEN.selRefFeat({layers:bot_l,use:'filter',mode:'touch'}) if(GEN.getSelectCount()>0){ GEN.selClearFeature() drill_info.drl_overlap = "Yes" } else { drill_info.drl_overlap = "No" } }else{ drill_info.drl_overlap = "No" } }else if (drl_side == "Bot"){ if((drl_end_num-layerCount)==0){ drl_filler = "None" } else { var top_l = "ftdrill" + (drl_start_num+1) + "-" + (drl_end_num+1) + "l" if(GEN.isLayerExists({job:job, layer:top_l})){ GEN.clearLayers() GEN.selClearFeature() GEN.workLayer({name:top_l,display_number:2,clear_before:'yes'}) GEN.selRefFeat({layers:drill.odb_name,use:'filter',mode:'touch'}) if(GEN.getSelectCount()>0){ GEN.selClearFeature() drl_filler="Via Filling" } } } var bot_l = "ftdrill" + (drl_start_num-1) + "-" + (drl_end_num-1) + "l" if(GEN.isLayerExists({job:job, layer:bot_l})){ GEN.clearLayers() GEN.selClearFeature() GEN.workLayer({name:drill.odb_name,display_number:2,clear_before:'yes'}) GEN.selRefFeat({layers:bot_l,use:'filter',mode:'touch'}) if(GEN.getSelectCount()>0){ GEN.selClearFeature() drill_info.drl_overlap = "Yes" }else { drill_info.drl_overlap = "No" } }else{ drill_info.drl_overlap = "No" } } } else { GEN.clearLayers() GEN.workLayer({name:drill.odb_name,display_number:2,clear_before:'yes'}) GEN.selectByFilter({attribute:[{attribute:".drill",option:"plated"}]}) if(GEN.getSelectCount()>0){ GEN.selClearFeature() drill_info.drl_type = "PTH" if(drl_end_num - drl_start_num == 1){drl_filler = "Resin Plugin"} drill_info.drl_name = "PTH" + drl_start_num + "-" + drl_end_num }else{ drill_info.drl_type = "NPTH" drill_info.drl_name = "NPTH" + drl_start_num + "-" + drl_end_num } } drill_info.drl_filler = drl_filler drill_info.drl_zones = "{A}" cam_drill_structure.push(drill_info) }) var is_via_filling = cam_drill_structure.filter(function(v){return v.drl_filler == "Via Filling"}) if(is_via_filling.length>0){ cam_drill_structure = cam_drill_structure.map(function(item){ if(item.drl_type=="Laser" && item.drl_filler=="None"){ item.drl_filler = "Via Filling" } return item }) } save_job_info({ jobid: JobId, jobinfohash: {cam_drill_structure: JSON.stringify(cam_drill_structure)} }) GEN.closeStep() console.log("===================>steplist:"+_.toString(step_list)) if(step_list.indexOf(pcs_step)<0){throw "can not find pcsstep"} if(step_list.indexOf(array_step)<0){array_step = false} console.log("=============== ====================> 1matrix") var analysis_obj = analysis({job:job,jobId:JobId,pcs_step:pcs_step,array_step:array_step,matrix:matrix}) // 创建profile var tmp_matrix = GEN.getMatrix({job:job}) GEN.openStep({job:job, name:pcs_step}) var now_profile = GEN.getProfile({job:job, step:pcs_step}) console.log("profile:===============>"+now_profile); console.log(now_profile.match(/\n/ig).length); if(now_profile.match(/\n/ig).length == 1){ // 如果没有profile createOutline({job:job, step:pcs_step, matrix: tmp_matrix}) var hasProfile = GEN.getProfile({job:job, step:pcs_step}) if(hasProfile.match(/\n/ig).length == 1){ throw "创建profile失败" } else { GEN.affectedLayer({affected:'yes',mode:'all'}) GEN.selectByFilter({profile:"out"}) if(GEN.getSelectCount() > 0){ GEN.selDelete() } } } else { GEN.affectedLayer({affected:'yes',mode:'all'}) GEN.selectByFilter({profile:"out"}) if(GEN.getSelectCount() > 0){ GEN.selDelete() } } GEN.closeStep() // * profile 和 拼版 var pandle_step = step_list.map(function(v){ var type = "unknow"; if(/^cad|^pcs/ig.test(v)){ type = "pcs" } else if (/^arr|^stp/ig.test(v)) { type = "array" } var panel_info = {job_id:JobId,step_name:v,step_type:type} GEN.openStep({job:job,name:v}) GEN.clearLayers() GEN.selClearFeature() var profileLimits = GEN.getProfileLimits({job:job,step:v,units:"mm"}) if(profileLimits){ panel_info.size_x = Number(profileLimits.xsize).toFixed(3) panel_info.size_y = Number(profileLimits.ysize).toFixed(3) } panel_info.datum_x = 0 panel_info.datum_y = 0 panel_info.margin = {"margin_top": 0, "margin_left": 0, "margin_right": 0, "margin_bottom": 0, "allow_margin_top": 0, "allow_margin_left": 0, "allow_margin_right": 0, "allow_margin_bottom": 0} var profile = GEN.getProfile({job:job,step:v,units:"mm"}).split("\n") if(profile.length>=1){ var tmp = profile.slice(1) tmp = tmp.map(function(v){ if(v[0]==="#"){v = v.slice(1)} return v }) panel_info.profile = tmp.join("\n") } else { profile = profile.map(function(v){ if(v[0]==="#"){v = v.slice(1)} return v }) panel_info.profile = profile.join("\n") } for(var key in profileLimits){ profileLimits[key] = Number(profileLimits[key]) } panel_info.profile_limits = profileLimits panel_info.attr_data= {} panel_info.extra_data= {} var repeat = GEN.getSR1({job:job,step:v,units:"mm"}) var step_repeat = {} repeat.forEach(function(item, i){ step_repeat[String(i)] = { x:item.xa, y:item.ya, dx: item.dx,dy: item.dy, nx:item.nx,ny:item.ny, // gapX:item.xsize,gapY:item.ysize, step:item.step, angle:item.angle, mirror:item.mirror == "no" ? 0 : 1, step_name:item.step, step_type:/cad|pcs|card/ig.test(item.step)? "pcs" : "array", } }) panel_info.step_repeat = step_repeat save_panel_info({info:panel_info, table:"pdm_job_panelizer_step"}) GEN.closeStep() return {name:v, type:type}}) // glod_finger var gold_fingers = Object.keys(tmp_matrix).filter(function(v){return /^enig_top$|^enig_bot$/ig.test(v)}) if(gold_fingers.length){ gold_fingers = gold_fingers.map(function(v){ var type = (v=="enig_top") ? "top" : "bot" return {name:v, type:type} }) } ALL.gold_fingers = gold_fingers GEN.closeStep() var config = { jobInfo: { layer_count: ["signal","power_ground"], // Board属性的signal或者power_ground层 pcs_size_x: {api:"vc_card_size", props:"xsize"}, // card短边尺寸 // ? 保留小数 pcs_size_y: {api:"vc_card_size", props:"ysize"}, // card长边尺寸 // ? 保留小数 array_size_x: {api:"vc_array_size", props:"xsize"}, // ! array短边尺寸 需要arr_step // ? 保留小数 array_size_y: {api:"vc_array_size", props:"ysize"}, // ! array长边尺寸 需要arr_step // ? 保留小数 vc_pcs_count_on_panel: true, // array中pcs的数量 ! 需要arr_step pcs_count_on_array: true, stack_vias_number: true, // via孔连续叠加的层数 stack_vias_more: {only_value:true}, // yes|no : 14层板以上时,Stack Vias >=12时,存yes // ! 需要分析完via连续叠加层数 depth_drilling: {api:"layer_exist", props:"depth_drill"}, // yes|no :存在 depth_drill 层时 ,存yes depth_routing: {api:"layer_exist", props:"depth_routing"}, // tmp yes|no :存在 depth_routing 层时,,存yes laser_via_on_buried_hole: true, // via孔在埋孔上时,存yes milling_bit_size: false, // todo 检查array中pcs的最小间距值 milling_length_card: true, milling_length_array: true, vc_src_01005_pad_result: {api:"board_has_attr", props:vc_src_01005_pad_result}, // 本地使用 .pth_pad ats 01005_pad ATS_technology_25dc: {api:"layer_exist", props:["bot_coverlay","top_coverlay"]}, // yes|no:存在cavity层别时存yes ATS_technology_25dr: {api:"layer_exist", props:["bot_coverlay","top_coverlay"]}, // yes|no:存在cavity层别时存yes // vc_src_EDGE_PLATING: true, // yes|no:检查线路外形是否存在物件,存在则存yes // !料号需要有rout层 vc_src_EDGE_PLATING: {api:"layer_exist2", props:["fab_page2"]}, // fab_page2 edge_plating_length: true, // todo 检查线路外形处物件的长度 gold_finger: false, // glod_finger_area: false, // ATS_sm_side: true, // top|bot|both:检查防焊层所在面次 ATS_sm_side vc_id_print_side: true, // top|bot|both:检查文字层所在面次 vc_id_print_side min_drl_size: true, // todo 0.5mm 最小圆孔 max_pth_drl_size: true, // todo 3.5mm 最大pth圆孔 max_npth_drl_size: true, // todo 6.0mm 最大npth圆孔 min_slot_drl_size: true, // todo 0.8mm 最小槽孔 max_slot_drl_size: true, // todo 1.2mm 最大槽孔 max_slot_drl_length: true, // todo 2.0mm 最大槽孔 max_spec_slot_drl_size: true, // todo 2.1mm 最大异形槽孔 max_spec_slot_drl_length: true, // todo 3.0mm 最大异形槽孔 } } console.log("===============================> 2analysis_obj") // 分析料号info var jobInfo = {} Object.keys(config.jobInfo).forEach(function(key){ var props = config.jobInfo[key]; console.log("===================================> 2analysis_obj:key:"+key) if(props){ try { if(props.hasOwnProperty("api") && props.hasOwnProperty("props")){ jobInfo[key] = analysis_obj["analysis_"+props.api](props.props) } else if(props.only_value){ jobInfo[key] = analysis_obj.jobInfo[key] } else { jobInfo[key] = analysis_obj["analysis_"+key](props) } } catch (e) { console.log("========================================error:"); console.log(e); jobInfo[key] = "_error" } } }) console.log("===================================> 3 jobinfo") // 过滤掉料号信息的 _todo 和 _error for (var key in jobInfo) { if(jobInfo[key]==="yes"){jobInfo[key] = "Yes"} if(jobInfo[key]==="no"){jobInfo[key] = "No"} if(jobInfo[key] === "_todo" || jobInfo[key] === "_error"){ delete jobInfo[key] } } console.log("===================================> 4 save job info") save_job_info({ jobid: JobId, jobinfohash: jobInfo }) console.log("===================================> 5 save drill info") // * 获取pcs_step 和 array_step的钻孔信息 var drill_tool_info = {}; var drillLayers = analysis_obj.matrixInfo.mDrills; var pcs_steps = [par.pcs_step] var arr_steps = [par.array_step] drillLayers.forEach(function(item){ var layer = item.name if(pcs_steps.length > 0){ pcs_steps.forEach(function(step){ if(!GEN.isLayerEmpty({job:job,step:step,layer:layer})){ GEN.COM("tools_set,layer="+layer+",slots=by_length") var tool = GEN.getTool({job:job,step:step,layer:layer,units:"mm",}); if(drill_tool_info[layer]){ Object.keys(tool).forEach(function(hashkey){ if(drill_tool_info[layer][hashkey]){ if(!drill_tool_info[layer][hashkey].pcs_count){drill_tool_info[layer][hashkey].pcs_count = 0} drill_tool_info[layer][hashkey].pcs_count += Number(tool[hashkey].count) ; } }) }else{ drill_tool_info[layer] = tool ; Object.keys(tool).forEach(function(hashkey){ if(!drill_tool_info[layer][hashkey].pcs_count){drill_tool_info[layer][hashkey].pcs_count = 0} drill_tool_info[layer][hashkey].pcs_count += Number(tool[hashkey].count) ; }) } } }) } if(arr_steps.length > 0){ arr_steps.forEach(function(step){ if(GEN.isStepExists({job:job,step:step})){ if(!GEN.isLayerEmpty({job:job,step:step,layer:layer})){ var tool = GEN.getTool({job:job,step:step,layer:layer,units:"mm"}); if(drill_tool_info[layer]){ Object.keys(tool).forEach(function(hashkey){ if(drill_tool_info[layer][hashkey]){ if(!drill_tool_info[layer][hashkey].array_count){drill_tool_info[layer][hashkey].array_count = 0} drill_tool_info[layer][hashkey].array_count += Number(tool[hashkey].count) ; } }) }else{ drill_tool_info[layer] = tool ; Object.keys(tool).forEach(function(hashkey){ if(!drill_tool_info[layer][hashkey].array_count){drill_tool_info[layer][hashkey].array_count = 0} drill_tool_info[layer][hashkey].array_count += Number(tool[hashkey].count) ; }) } } } }) } }) // 删除 db.query("",function(q){ return q.deleteRow({ table:'pdm_job_cam_drill', where:{job_id:JobId} }) }); var seq_index = 1 ; Object.keys(drill_tool_info).forEach(function(layer){ var datalist = [] var tool_num = 1 ; _.values(drill_tool_info[layer]).sort(function(a,b){return a.drill_size - b.drill_size}).forEach(function(item){ var layer_name = layer var drill_type = item.type if(matrix[layer].type == "laser_drill" && /^[^\d]+(\d+)-(\d+)[^\d]?$/.test(layer_name)){ drill_type = "laser" var info_drilll = /^[^\d]+(\d+)-(\d+)[^\d]?$/.exec(layer_name); layer_name = "l" + info_drilll[1] + "-" + info_drilll[2]; } else if(layer_name === "ftdrill"){ layer_name = "drill" } else if(/^[^\d]+(\d+)-(\d+)[^\d]?$/.test(layer_name)) { var info_drilll = /^[^\d]+(\d+)-(\d+)[^\d]?$/.exec(layer_name); layer_name = "d" + info_drilll[1] + "-" + info_drilll[2]; } if(matrix[layer].type == "bury_drill"){ drill_type = "via" } var data = { job_id: JobId, layer_name: layer_name, tool_num: "T" + tool_num, tool_flag: tool_num, tool_type: item.shape, drill_type:drill_type, // drill_size : item.drill_size, drill_slot_length: Number(item.slot_len), finish_size : Number(item.finish_size).toFixed(3), finish_slot_length: Number(item.slot_len), // size_tol_lower: item.min_tol, // size_tol_upper: item.max_tol, pcs_count: item.pcs_count || 0, array_count: item.array_count || 0, seq: seq_index + tool_num/100, attr_data: {"org_layer_name":layer} } datalist.push(data) tool_num++ }) seq_index++ // datalist存入 datalist.forEach(function(data){ db.query("",function(q){ return q.insertRow({ table:'pdm_job_cam_drill', data:data }) }); }) }) console.log("=============================> 6 set smd bga") var allStep = GEN.getStepList({job:job}) allStep.forEach(function(step){ GEN.openStep({job:job,name:step}) GEN.affectedLayer({affected:'no',mode:'all'}) GEN.clearLayers() if(GEN.GEN_TYPE == "genesis"){ 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=No)(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") }else{ GEN.COM("chklist_single,show=yes,action=valor_cleanup_set_smd") GEN.COM("chklist_cupd,chklist=valor_cleanup_set_smd,nact=1,params=((pp_layer=.type=signal|mixed&side=top|bottom)(pp_work_on=SMD\;BGA)(pp_delete=No)(pp_types=Square\;Rect\;Oval)(pp_other_smd=)(pp_sm=No)(pp_drill=)(pp_rotate=No)(pp_ignore_covered=Yes)(pp_bga_types=Round)(pp_other_bga=)(pp_sm_bga=No)(pp_bga_max_pitch=70)(pp_bga_actions=Set attribute)(pp_bga_suffix=_bga)(pp_identify_gf=)),mode=regular") GEN.COM("get_user_name") GEN.COM("get_job_path,job="+job) GEN.COM("disp_on") GEN.COM("origin_on") GEN.COM("chklist_cnf_act,chklist=valor_cleanup_set_smd,nact=1,cnf=no") GEN.COM("chklist_run,chklist=valor_cleanup_set_smd,nact=1,area=profile") GEN.COM("skip_next_pre_hook") GEN.COM("chklist_run,chklist=valor_cleanup_set_smd,nact=1,area=profile") GEN.COM("get_user_name") GEN.COM("skip_current_command") GEN.COM("disp_on") GEN.COM("origin_on") GEN.COM("show_tab,tab=Checklists,show=no") } // top bottom 层smd排除掉 r\d开头的圆形smd // 与开窗touch 碰不到的删除属性 analysis_obj.matrixInfo.mOuters.forEach(function(item){ var layer = item.signalL var oping_layer = layer.solderL GEN.workLayer({name:layer,display_number:2,clear_before:'yes'}) GEN.selClearFeature() var symbols_tmp = GEN.getLayerSymsHist({job:job,step:step,layer:layer}) var syms = Object.keys(symbols_tmp) var filter_syms = syms.filter(function(item){ if(/^rect(\d+\.\d+|\d+)x(\d+\.\d+|\d+)/ig.test(item)){ var res = /^rect(\d+\.\d+|\d+)x(\d+\.\d+|\d+)/ig.exec(item).slice(1) var num1 = res[0], num2 = res[1] if(num1<3 || num2<3){return true} } return /^r\d+/.test(item)} ) if(filter_syms.length){ var filter_syms = filter_syms.join(";") GEN.selectByFilter({attribute:".smd", include_syms:filter_syms, profile:"in"}) if(GEN.getSelectCount()>0){ GEN.COM("sel_delete_atr,attributes=.smd") } } if(oping_layer){ GEN.COM("filter_atr_set,filter_name=popup,condition=yes,attribute=.smd") GEN.COM("sel_ref_feat,layers="+oping_layer+",use=filter,mode=disjoint,pads_as=shape,f_types=line\;pad\;surface\;arc\;text,polarity=positive\;negative,include_syms=,exclude_syms=") GEN.COM("COM filter_reset,filter_name=popup") } }) GEN.closeStep() }) console.log("============= ===============> 6 analysis smd") // smd var smdInfo; try { if(!analysis_obj.matrixInfo.mOuters[0].solderL){throw "no mask"} smdInfo = smdAnalysis({job:job,steplist:[pcs_step],layers:analysis_obj.matrixInfo.mOuters,attr:".smd"}) } catch (msg) { console.log("smdInfo:error:"+msg) } // bga console.log("===================================> 7 bga info") var bgaInfo; bgaInfo = bgaAnalysis({job:job,steplist:[pcs_step],layers:analysis_obj.matrixInfo.mOuters,attr:".bga"}) console.log("========================bgaInfo:" + _.toString(bgaInfo)); // 保存 console.log("============== =====================> 8 save bga smd info") var save_info = [smdInfo, analysis_obj.jobInfo.laser_info,bgaInfo]; // 保存 smd 和 bga数据 save_info.forEach(function(item){ if(JSON.stringify(item)!= "{}" && item){ Object.keys(item).forEach(function(key){ var val = item[key] save_layerinfo({ jobid: JobId, layer: key, layerinfohash: val }) }) } }) console.log("================================> 9 copper_percent signal drill") // 分析layer info var stepList = [pcs_step]; var oChecklistName = "mychecklist" var signalLayers = analysis_obj.matrixInfo.mSignals.map(function(v){return v.name}) var drillLayers = analysis_obj.matrixInfo.mDrills.map(function(v){return v.name}) console.log("=============================> 13 save copperArea copper_distribution") var copper_distribution_steplist= [pcs_step, array_step]; copper_distribution_steplist.forEach(function(step){ if(step){ GEN.openStep({ job: job, name: step }) // 铜面积 分step var copper_percent_tmp = signalLayers.map(function(v){ var tmp = {layer:v} tmp.copper_percent = GEN.copperArea({layer1:v}).percent + "%" return tmp }) console.log("================ ==signalLayers============"+_.toString(signalLayers)); console.log(_.toString(copper_percent_tmp)); copper_percent_tmp.forEach(function(item){ var save_info = { jobid: JobId, layer: item.layer, } if(/^cad/ig.test(step)){ save_info.layerinfohash = { card_copper_distribution: item.copper_percent } } else if(/^stp/ig.test(step)){ save_info.layerinfohash = { array_copper_distribution: item.copper_percent } } save_layerinfo(save_info) }) GEN.closeStep() } }) var info = { min_line_width: ["line","user_nor_line"], min_line_spacing: ["l2l"], min_line2pad: ["p2line","user_bga2nor_line","user_smd2nor_line"], min_pad2pad: ["p2p", "smd2smd", "smd2pad","via2via","bga2pad"], min_ar_laser:["laser_via_ar"], min_ar_via:["via_ar"], min_ar_pth:["pth_ar"], min_ar_npth:["npth_ar"] } save_job_info({ jobid: JobId, jobinfohash: {ATS_surface_area_base_on:"Card"} }) var jobpath = GEN.getJobPath({job:job}) // if(GEN.GEN_TYPE == "genesis"){ // jobpath = GEN.getJobPath({job:job}) // } else { // jobpath = "/home/local_db/server_db/jobs/"+job // } mkPath(jobpath,["user","opcam","steps"]) stepList.forEach(function(step){ GEN.openStep({ job: job, name: step }) // 曝光 analysis_obj.matrixInfo.mOuters.forEach(function(item){ if(ALL.gold_fingers.length>0){ var tmp_gold_info = {} ALL.gold_fingers.forEach(function(item2){ tmp_gold_info[item2.type] = item2.name }) var layer_gold_type = /top/ig.test(item.signalL) ? "top" : "bot" if(tmp_gold_info[layer_gold_type]){ var tmp_info = GEN.copperArea({layer1:tmp_gold_info[layer_gold_type],resolution_value:1}) // var tmp_info = GEN.exposedArea({layer1:item.signalL,mask1:tmp_gold_info[layer_gold_type]}) var tmp_data = {} if(layer_gold_type == "top") { tmp_data.sf_area_ref_layer_front = tmp_gold_info[layer_gold_type] tmp_data.sf_area_gold_area_front = tmp_info.area } if(layer_gold_type == "bot"){ tmp_data.sf_area_ref_layer_back = tmp_gold_info[layer_gold_type] tmp_data.sf_area_gold_area_back = tmp_info.area } save_job_info({ jobid: JobId, jobinfohash: tmp_data }) save_layerinfo({ jobid: JobId, layer: item.signalL, layerinfohash: { sf_area_ref_layer: tmp_gold_info[layer_gold_type], sf_area_gold_area: tmp_info.area } }) } } if(item.solderL){ var tmp_info = GEN.copperArea({layer1:item.solderL,resolution_value:1}) // var tmp_info = GEN.exposedArea({layer1:item.signalL,mask1:item.solderL}) // {"area":"0.73817","percent":"8.986"} var tmp_area = tmp_info.area var tmp_percent = tmp_info.percent; // todo carbon var tmp_data = /top/ig.test(item.signalL) ? {carbon_Area_front:tmp_area} : {carbon_Area_back:tmp_area} // save_job_info({ // jobid: JobId, // jobinfohash: tmp_data // }) save_layerinfo({ jobid: JobId, layer: item.signalL, layerinfohash: { ref_layer: item.solderL, exposed_area: tmp_area } }) } }) GEN.clearLayers() GEN.affectedLayer({ affected: 'no', mode: 'all' }) console.log("==================================> Drill analysis") // 钻孔 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(b.size)}) .reduce(function(a,b){ if(b.pad!="0"){ a.push(b.symbol) } return a },[])[0] return {layer:v,symbol:symbol,start:tmp_matrix[v]["drl_start"],end:tmp_matrix[v]["drl_end"]} }) var drillRes = analysisDrill(drillToSignals,step) // 钻孔分析结果 console.log("===============drillRes:" + _.toString(drillRes)); drillRes.forEach(function(item){ save_layerinfo({ jobid: JobId, layer: item.layer, layerinfohash: { drl_pad_top: item.drl_pad_top, drl_pad_bot: item.drl_pad_bot, } }) }) console.log("==================================> chk signals analysis") // 创建chklist并运行 如果chklist存在先删除 if (GEN.isChklistExists({ job: job, step: step, chklist: oChecklistName })) { GEN.COM("chklist_delete", { chklist: oChecklistName }) } var tmpitem = { name: "signal_layer_checks", nact: 1, action: "valor_analysis_signal", 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" } } if(par.erf){ tmpitem.erf = par.erf } createChklistAndRun({ // 创建checklist并运行 layers: signalLayers, items: [tmpitem] }) // signal层分析结果 var res = analysisChkAttr({layers:signalLayers, info:info, step:step, job:job, oChecklistName:oChecklistName,jobpath:jobpath}) // 数据入库 Object.keys(res).forEach(function(key){ var val = res[key] for(var key2 in val){ if(key2 == "min_ar_npth"){ if(Number(val[key2]) == 0 || Number(val[key2]) == 999){ delete val.min_ar_npth } } } save_layerinfo({ jobid: JobId, layer: key, layerinfohash: val }) }) GEN.closeStep() }) // 保存 if(/yes/ig.test(par.auto_save)){ GEN.checkInout({job:job,mode:"out"}) // 结束保存料号 关闭料号 GEN.saveJob({ job: job }); GEN.checkInout({job:job,mode:"in"}) GEN.closeJob({job:job}) } else { GEN.checkInout({job:job,mode:"in"}) } // 导出 if(par.export_path != "" && par.export_mode != "" && par.export_submode != "" ){ if(fs.exists(par.export_path)){ GEN.COM("export_job",{job:job,path:par.export_path,mode:par.export_mode,submode:par.export_submode,overwrite:"yes",format:"genesis",output_name:job}) GEN.COM("disp_on") GEN.COM("origin_on") } else { resultData.push({ type: "error", title: "导出路径不存在!", detail: [{ desc: par.export_path }] }); } } QDfm.updateRow({ table: "pdm_aimdfm_task", data: { progress: 33.33 }, where: { id: $.task_id } }); if (GEN.hasError()) { Status = 'error'; resultData.push({ type: "error", title: "GEN错误!", detail: [{ desc: _.join(GEN.STATUS, "\n") }] }); return { status: Status, result_data: resultData }; } else { resultData.push({ type: "info", title: "操作完成, 请注意检查!" }); return { status: Status, result_data: resultData }; } } catch (e) { Status = 'error'; console.log("==================== ============>error"); console.log(_.toString(e)); resultData.push({type: "error", title: "脚本执行出错!", detail: [{desc: _.toString(e)}]}); return {status: Status, result_data: resultData}; } function UPLOAD_LAYER_MATRIX(props){ var job = props.job props.jobcategory = props.jobcategory || "work" var matrix = ANALYSIS_STACKUP({job:job, jobcategory:props.jobcategory}); Omatrix = matrix var layers = Object.keys(matrix).sort(function(a,b){return matrix[a]["row"] - matrix[b]["row"]}) var tableName = "pdm_job_" + props.jobcategory + "_layer"; console.log("===========>matrix upload"); var oLayers = db.query("",function(q){ return q.selectMapMap({ table:tableName, field: ["odb_name", "release_status"], where:{job_id:JobId}, uniquefield: "odb_name" }) }); db.query("",function(q){ return q.deleteRow({ table:tableName, where:{job_id:JobId}, }) }); 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" ]; layers.forEach(function(layer){ var layerInfo = matrix[layer]; var tmpData = { "job_id": JobId, "name": layerInfo["name"] }; tmpData["release_status"] = (oLayers&&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]]; } db.query("",function(q){ return q.insertRow({ table:tableName, data: tmpData }) }); }) console.log("===========>matrix upload end"); return matrix } function ANALYSIS_STACKUP(props){ var job = props.job if(!props.hasOwnProperty("jobcategory")){ props.jobcategory = 'work' } var matrix = GEN.getMatrix({job:job}) var layer_count = GEN.getLayerCount({job:job}) // ! save_job_info({jobid:JobId,jobcategory:props.jobcategory,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 == 'ftdrill'){ 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 (/^ftdrill(\d+)-(\d+)/.test(layer.odb_name)){ // /^d(\d+)\-(\d+)$/ var tmp = /^ftdrill(\d+)-(\d+)/.exec(layer.odb_name) var drl_star = tmp[1]; var drl_end = tmp[2]; 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_end - drl_star == 1){ layer.name = 'laser' + drl_star + '-' + drl_end; layer.type = 'laser_drill'; // 镭射 } else if( drl_star == 1 || drl_end == layer_count ){ layer.name = 'drill' + drl_star + '-' + drl_end; layer.type = 'blind_drill'; // 埋孔 } else{ layer.name = 'drill' + drl_star + '-' + drl_end; layer.type = 'bury_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 getMatrixInfo(props){ // 获取matrix各种信息 var job = props.job var matrix = GEN.getMatrix({job:job}) var res = { matrix: matrix, // matrix matrixVal: [], // matrix 的 value数组 mSignal: {}, // board的signal层 {} mSignals: [], // board的signal层 [] mDrill: {}, // board的drill层 mDrills: [], // board的drill层 mSolderMask: {}, // 防焊层 mSolderMasks: [], // 防焊层 mOuters: [], // 外层和对于的防旱 mBoardLayer: [] // board层 } 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"){ var solderLayers = res.mSolderMasks.filter(function(v){ return v.side == b.side }) a.push({ signalL: b.name, solderL: solderLayers.length >0 ? solderLayers[0]["name"] : null }) } return a },[]) 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 save_job_info(props){ // 保存料号信息 var jobid = props.jobid; var jobinfohash = props.jobinfohash; Object.keys(jobinfohash).forEach(function(key){ var val = jobinfohash[key]; var value = db.query("",function(q){ return q.selectValue({ table:'pdm_job_jobattr', field:"value", where:{job_id:jobid, attr_name: key} }) }); console.log("jobid:"+jobid+"==========dbjobvalue:" + val) if(/done/ig.test(value) || !value){ db.query("",function(q){ return q.insertRow({ table:'pdm_job_jobattr', // todo data:{job_id:jobid, attr_name:key, value:val}, // todo return_field:'job_id', }) }); } else if (value != val) { console.log("jobid:"+jobid+"==========update jobvalue:"+value+"->" + val) db.query("",function(q){ return q.updateRow({ table:'pdm_job_jobattr', where:{job_id:jobid, attr_name: key}, data:{value:val}, }) }); } }) } function save_layerinfo(props){ // 保存层信息 var jobid = props.jobid; var layer = props.layer; // if(Omatrix[layer] && Omatrix[layer].tl_name){ // if(!(/^drill/ig.test(Omatrix[layer].tl_name))){ // layer = Omatrix[layer].tl_name // } // } if(Omatrix[layer] && Omatrix[layer].name){ // if(!(/^drill/ig.test(Omatrix[layer].tl_name))){ layer = Omatrix[layer].name // } } var layerinfohash = props.layerinfohash; Object.keys(layerinfohash).forEach(function(key){ var val = layerinfohash[key]; if(val && val != ""){ var value = db.query("",function(q){ return q.selectValue({ table:'pdm_job_layerattr', field:"value", where:{job_id:jobid, attr_name: key, layer:layer} }) }); console.log("+==========dblayervalue:" + value) if(/done/ig.test(value) || !value){ db.query("",function(q){ console.log("+==========insertRowlayervalue:layer:"+layer+";attrname:"+key+";value:"+ val) return q.insertRow({ table:'pdm_job_layerattr', // todo data:{job_id:jobid, attr_name:key, value:val, layer:layer}, // todo return_field:'job_id', }) }); } else if (value !== val) { db.query("",function(q){ console.log("+==========updateRowlayer:layer:"+layer+";attrname:"+key+";value:"+ val) return q.updateRow({ table:'pdm_job_layerattr', where:{job_id:jobid, attr_name: key, layer:layer}, data:{value:val}, }) }); } } }) } function save_stack_info(props){ var info = props.info var table = "pdm_job_stack_drills" info.job_id = JobId var drl_name = info.drl_name db.query("",function(q){ return q.deleteRow({ table:table, where:{job_id:JobId, drl_name: drl_name} }) }); console.log(_.toString(info)) var Re = db.query("",function(q){ return q.insertRow({ table:table, data:info, return_field:'job_id', }) }); } function save_panel_info(props){ var info = props.info var table = props.table info.job_id = JobId var job_id = info.job_id || JobId var step_name = info.step_name db.query("",function(q){ return q.deleteRow({ table:table, where:{job_id:job_id, step_name: step_name} }) }); db.query("",function(q){ return q.insertRow({ table:table, data:info }) }); } function analysis(props){ var job = props.job || Job; var jobId = props.jobId || JobId; var pcs_step = props.pcs_step || false; var array_step = props.array_step || false; var matrix = props.matrix; function T_m_p(props){ this.job = props.job; this.jobInfo = {}; this.jobId = props.jobId; if(props.pcs_step){ this.pcs_step = props.pcs_step; } if(props.matrix){ this.matrix = props.matrix; } if(props.array_step){ this.array_step = props.array_step; } this.matrixInfo = getMatrixInfo({job:this.job}) this.init(); } T_m_p.prototype.init = function(){ var init_checking = ["job", "jobId", "pcs_step", "matrix"]; var t = this; init_checking.forEach(function(v){ if(!t[v]){t.e(v+" is undefined")} }) } T_m_p.prototype.e = function(e){ // 处理error console.log(e) throw e } T_m_p.prototype.analysis_layer_count = function(props){ // 分析board属性的层数量 []string var matrix = this.matrixInfo.matrixVal; var res = matrix.filter(function(v){return v.context == "board" && props.indexOf(v.layer_type) >= 0 }) return res.length } T_m_p.prototype.analysis_vc_card_size = function(props){ // card短边尺寸 string return GEN.getProfileLimits({job:this.job,step:this.pcs_step,units:"mm"})[props].toFixed(2) } T_m_p.prototype.analysis_vc_array_size = function(props){ // array长边尺寸 string if(!this.array_step){return "_error"} var tmp = GEN.getProfileLimits({job:this.job,step:this.array_step,units:"mm"}) var size1 = Number(tmp.xsize) var size2 = Number(tmp.ysize) if(size1 > size2){ var tmpsize = size2 size2 = size1 size1 = tmpsize } if(props == "xsize"){ return size1.toFixed(2) } if(props == "ysize"){ return size2.toFixed(2) } } T_m_p.prototype.analysis_vc_pcs_count_on_panel = function(props){ // if(!this.array_step){return "_error"} var has_step = has_steps({job:this.job, pcs_step:this.pcs_step ,array_step:this.array_step}) // 有无拼版关系 if(has_step){ // arr 中 pcs数量 var tmp = GEN.getRepeat({job:job, step:array_step}) return tmp.length } return "_todo" } T_m_p.prototype.analysis_pcs_count_on_array = function(props){ // if(!this.array_step){return "_error"} var has_step = has_steps({job:this.job, pcs_step:this.pcs_step ,array_step:this.array_step}) // 有无拼版关系 if(has_step){ // arr 中 pcs数量 var tmp = GEN.getRepeat({job:job, step:array_step}) return tmp.length } return "_todo" } T_m_p.prototype.analysis_stack_vias_number = function(props){ // via孔连续叠加的层数 var t = this; var res; // 找出 镭射孔 var laser_layers = [] for (var key in t.matrix) { var val = t.matrix[key] if(val.type == "laser_drill"){ laser_layers.push(val) } } if(laser_layers.length == 0){return "_error"} GEN.openStep({job:t.job,name:t.pcs_step}) 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].odb_name var cover_layer = laser_layers[i+1].odb_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){ if(!laser_info[start_layer]){ 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].odb_name + "_cover" GEN.selClearFeature() GEN.workLayer({name:layers[0].odb_name,display_number:2,clear_before:'yes'}) GEN.selAllFeat() selCopyLayer({job:t.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:t.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].odb_name,use:'filter',mode:'cover'}) var count = GEN.getSelectCount() if (count < 1) { GEN.deleteLayer({job:t.job,layer:start}) return ++end_index } var nextstartlayer = layers[0].odb_name + "_cover" selCopyLayer({job:t.job,layer:nextstartlayer}) GEN.deleteLayer({job:t.job,layer:start}) return start_cover_next(nextstartlayer,layers,++end_index) } analysis_via_number(laser_layers.slice()) if (via_vias_info.length == 1){ res = via_vias_info[0] }else{ res = via_vias_info.reduce(function(a,b){return a-b>0?a :b}) } t.jobInfo.stack_vias_more = "no" if(GEN.getLayerCount({job:t.job}) > 14 && res >= 12){ t.jobInfo.stack_vias_more = "yes" } t.jobInfo.laser_info = laser_info; return res } T_m_p.prototype.analysis_layer_exist2 = function(props){ // 存在某某层? string var layers = Object.keys(this.matrixInfo.matrix) if(/string/ig.test(typeof(props))){ return layers.indexOf(props) >= 0 ? "yes" : "no" } else { var res = "no" props.forEach(function(v){ if(layers.indexOf(v)>=0){ res = "yes" } }) return res } } T_m_p.prototype.analysis_layer_exist = function(props){ // 存在某某层? string var layers = Object.keys(this.matrixInfo.matrix) if(/string/ig.test(typeof(props))){ return layers.indexOf(props) >= 0 ? "yes" : "no" } else { var res = "no" props.forEach(function(v){ if(layers.indexOf(v)>=0){ res = "yes" } }) return res } } T_m_p.prototype.analysis_laser_via_on_buried_hole = function(){ // via孔在埋孔上 var t = this // 找出 镭射孔 机械孔 var laser_layers = [] var buried_hole = [] for (var key in t.matrix) { var val = t.matrix[key] if(val.type == "laser_drill"){ laser_layers.push(key) } else if(val.type == "blind_drill" || val.type == "bury_drill"){ buried_hole.push(key) } } var res = "no" if(buried_hole.length>0 && laser_layers.length){ // 有机械孔 和镭射 GEN.openStep({job:t.job,name:t.pcs_step}) GEN.affectedLayer({affected:'no',mode:'all'}) try { laser_layers.forEach(function(laser){ GEN.workLayer({name:laser,display_number:2,clear_before:'yes'}) GEN.selClearFeature() buried_hole.forEach(function(buried){ GEN.selRefFeat({layers:buried,use:'filter',mode:'touch'}) if(GEN.getSelectCount()>0){ res = "yes" throw "yes" } }) }) } catch (msg) { } GEN.closeStep() } return res } T_m_p.prototype.analysis_milling_bit_size = function(){ // todo return "_todo" } T_m_p.prototype.analysis_milling_length_card = function(){ // todo var allStep = [this.pcs_step, this.array_step] var res = 0 var that = this; console.log("============analysis_milling_length_card============="); console.log("========step========:"+_.toString(allStep)); var steps = GEN.getStepList({job:this.job}) allStep.forEach(function(step){ if(steps.indexOf(step)>=0){ GEN.openStep({job:that.job,name:step}) var tmp = "length_tmp" if(GEN.isLayerExists({job:that.job,layer:tmp})){GEN.deleteLayer({job:that.job,layer:[tmp]})} GEN.createLayer({job:that.job,layer:tmp,conext:'misc',type:'document'}) GEN.COM("profile_to_rout,layer="+tmp+",width=1") var feautres = GEN.getFeatures({job:that.job,step:step,layer:tmp,units:"mm"}) fs.writeFile("/home/samba/tmp/tmp.js", _.toString(feautres)) // 根据feautres 算出周长 var milling = Object.keys(feautres).reduce(function(a,b){ var value = feautres[b] if (value.type=="line"){ length = Math.sqrt((Number(value.xe)-Number(value.xs))*(Number(value.xe)-Number(value.xs)) + (Number(value.ye)-Number(value.ys))*(Number(value.ye)-Number(value.ys))) a += length } else if (value.type=="arc") { a += arcLength(Number(value.xs),Number(value.ys),Number(value.xe),Number(value.ye),Number(value.xc),Number(value.yc)) } return a }, 0) console.log("========milling:"+milling); res += milling console.log("========res:"+res); GEN.deleteLayer({job:that.job,layer:[tmp]}) } }) res = res/1000 return res.toFixed(3) } T_m_p.prototype.analysis_milling_length_array = function(){ // todo if(!this.array_step){return "_error"} GEN.openStep({job:this.job,name:this.array_step}) var tmp = "length_tmp" GEN.COM("profile_to_rout,layer="+tmp+",width=1") var feautres = GEN.getFeatures({job:this.job,step:this.array_step,layer:tmp,units:"mm"}) // 根据feautres 算出周长 var milling = Object.keys(feautres).reduce(function(a,b){ var value = feautres[b] if (value.type=="line"){ length = Math.sqrt((value.xe-value.xs)*(value.xe-value.xs) + (value.ye-value.ys)*(value.ye-value.ys)) a += length } else if (value.type=="arc") { a += arcLength(Number(value.xs),Number(value.ys),Number(value.xe),Number(value.ye),Number(value.xc),Number(value.yc)) } return a }, 0) GEN.closeStep() milling = milling/1000 return milling.toFixed(3) } T_m_p.prototype.analysis_board_has_attr = function(props){ // board层中检查存在01005属性物件时 GEN.openStep({job:this.job,name:this.pcs_step}) GEN.clearLayers() GEN.affectedLayer({affected:'yes',layer:this.matrixInfo.mBoardLayer,clear_before:'yes'}); GEN.selClearFeature() GEN.selectByFilter({attribute:props}) var res = GEN.getSelectCount() > 0? "yes" : "no" GEN.affectedLayer({affected:'no',mode:'all'}); GEN.selClearFeature() GEN.closeStep() return res } T_m_p.prototype.analysis_vc_src_EDGE_PLATING = function(props){ var res = "no" var rout = this.matrixInfo.matrixVal.filter(function(v){return v.layer_type == "rout"}) // 找rout层 if(rout.length == 0){ return "error:cant find rout layer" } GEN.openStep({job:this.job, name:this.pcs_step}) GEN.affectedLayer({affected:'no',mode:'all'}) try { this.matrixInfo.mSignals.forEach(function(v){ // 用线路层逐层与rout层touch 找得到说明存在 GEN.workLayer({name:v.name,display_number:2,clear_before:'yes'}) GEN.selClearFeature() GEN.selRefFeat({layers:rout[0].name,mode:"touch",use:"filter"}) if( GEN.getSelectCount()>0){ throw "yes" } GEN.clearLayers() }) } catch (msg) { res = msg GEN.affectedLayer({affected:'no',mode:'all'}) GEN.selClearFeature() } GEN.closeStep() return res } T_m_p.prototype.analysis_edge_plating_length = function(){ // todo return "_todo" } T_m_p.prototype.analysis_gold_finger = function(){ // if(!ALL.gold_fingers){return "no"} return ALL.gold_fingers.length > 0 ? "yes" : "no" } T_m_p.prototype.analysis_glod_finger_area = function(){ // if(ALL.gold_fingers.length > 0){ GEN.openStep({job:this.job, name:this.pcs_step}) var gold_finger_area = {} var res = 0; ALL.gold_fingers.forEach(function(item){ var tmp = GEN.copperArea({layer1:item.name}).percent res += Number(tmp) // var tmp = GEN.copperArea({layer1:item.name}).percent + "%" // var key = item.type == "top" ? "sf_area_gold_area_front" : "sf_area_gold_area_back" // gold_finger_area[key] = tmp }) GEN.closeStep() if(res){ return String(res.toFixed(4)) + "%" } } return "_todo" } T_m_p.prototype.analysis_ATS_sm_side = function(){ // 检查防焊层所在面次 var solder_paste_layers = this.matrixInfo.matrixVal.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 res = "NONE" if(solder_paste_info.length == 0){ res = "NONE" } else if (solder_paste_info.length == 1) { res = /top/ig.test(solder_paste_info[0]) ? "FRONT1" : "BACK1" } else { res = "FRONT1;BACK1" } return res } T_m_p.prototype.analysis_vc_id_print_side = function(){ // 检查文字层所在面次 var solder_mask_layers = this.matrixInfo.matrixVal.filter(function(v){return v.layer_type == "silk_screen"}) var solder_mask_info= solder_mask_layers.map(function(v){return v.side}).reduce(function(a,b){ if(a.indexOf(b)<0){ a.push(b) } return a },[]) var res = "NA" if(solder_mask_info.length == 0){ res = "NA" } else if (solder_mask_info.length == 1) { res = /top/ig.test(solder_mask_info[0]) ? "FRONT" : "BACK" } else { res = "FRONT / BACK" } return res } T_m_p.prototype.analysis_min_drl_size = function(){ // todo return "_todo" } T_m_p.prototype.analysis_max_pth_drl_size = function(){ // todo return "_todo" } T_m_p.prototype.analysis_max_npth_drl_size = function(){ // todo return "_todo" } T_m_p.prototype.analysis_min_slot_drl_size = function(){ // todo return "_todo" } T_m_p.prototype.analysis_max_slot_drl_size = function(){ // todo return "_todo" } T_m_p.prototype.analysis_max_slot_drl_length = function(){ // todo return "_todo" } T_m_p.prototype.analysis_max_spec_slot_drl_size = function(){ // todo return "_todo" } T_m_p.prototype.analysis_max_spec_slot_drl_length = function(){ // todo return "_todo" } return new T_m_p({job:job, jobId:jobId, pcs_step:pcs_step, array_step:array_step,matrix:matrix}) } 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,name:step}) layers.forEach(function(layer){ // 计算开窗 数量 if(layer.solderL){ GEN.workLayer({name:layer.solderL,display_number:2,clear_before:'yes'}) if (!res.hasOwnProperty(layer.solderL)){ res[layer.solderL] = {} } if(GEN.getProfile({job:job, step:step}).match(/\n/ig).length > 1){ GEN.selectByFilter({profile:'in'}) } else { 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] = {} } if(symbolInfo["min_width"]){ res[layer.signalL]["min_smd_width"] = symbolInfo["min_width"] } if(symbolInfo["min_length"]){ res[layer.signalL]["min_smd_length"] = symbolInfo["min_length"] } // 分析最小smd间距 // 创建一个checklist并且分析 var min_smd_pitch = smdPitch({job:job,step:step,layer:tmp_layer}) // min_smd_pitch // console.log('================= =======min_smd_pitch:' + min_smd_pitch); res[layer.signalL]["min_smd_c2c"] = min_smd_pitch // 分析开窗宽高 if(layer.solderL){ 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/}) if(symbolInfo_solder["min_width"]){res[layer.signalL].min_smd_sm_width = symbolInfo_solder["min_width"]} if(symbolInfo_solder["min_length"]){res[layer.signalL].min_smd_sm_length = symbolInfo_solder["min_length"]} GEN.deleteLayer({job:job,layer:solderL_tmp}) } GEN.deleteLayer({job:job,layer:tmp_layer}) } }) GEN.closeStep() }) return res } 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 = Number(res[0]), num2 = Number(res[1]) if(num1 > num2){ var tmp = num2 num2 = num1 num1 = tmp } if(a.width == "N/A"){ a.width = num1 a.length = num2 return a } if(num2 < a.length){ a.width = num1 a.length = num2 }else if(num2 == a.length && a.width > num1){ a.width = num1 a.length = num2 } } return a },{width:"N/A",length:"N/A"}) return { min_width:symbols.width, min_length:symbols.length } } 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 }) } // 创建并运行 var tmpItem = { name: "smdPitch", nact: 1, action: "valor_analysis_signal", 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" } } if(PAR.erf && PAR.erf != ""){ tmpItem.erf = PAR.erf } GEN.createChklist({ // 创建checklist chklist: ck, items: [tmpItem] }) 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 features = GEN.getFeatures({job:job,step:step,layer:tmp_layer}) // 获取最小smd间距 var tmp = features.map(function(v){ var num = Math.sqrt((v.xe-v.xs)*(v.xe-v.xs) + (v.ye-v.ys)*(v.ye-v.ys))*1000 return num.toFixed(4) }) var res = tmp.sort(function(a,b){return a-b})[0] GEN.deleteLayer({job:job,layer:tmp_layer}) GEN.deleteLayer({job:job,layer:tmp_layer1}) GEN.deleteLayer({job:job,layer:tmp_layer2}) return res } 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; var job = par.job; var oChecklistName = par.oChecklistName layers.forEach(function (v) { saveMeans({job:job,step:par.step,chklist:oChecklistName,nact:1,layer:v,jobpath:par.jobpath}) 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)) { tmp = Number(tmp) a = tmp.toFixed(2) } if (/\d+/.test(a) && /\d+/.test(tmp) && Number(tmp) < Number(a)) { tmp = Number(tmp) a = tmp.toFixed(2) } return a }, "N/A") if(hash[v][key]=="N/A"){ hash[v][key] = 999 } }) }) return hash } function analysisDrill(par, step){ // {"layer":"d1-2","symbol":"r3.937","start":"top","end":"isl2"} // {"layer":"d1-2","symbol":"r35.0394","start":"top","end":"bottom"} var job = Job.toLowerCase() GEN.affectedLayer({affected:'no',mode:'all'}) var res = par.map(function(drill){ GEN.workLayer({name:drill.layer,display_number:2,clear_before:'yes'}) GEN.selClearFeature() GEN.selectByFilter({feat_types:'pad', include_syms:drill.symbol}) if(GEN.getSelectCount()>0){ console.log("==========================>jinru 111111111") // 拷贝到_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}) 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.COM("sel_ref_feat", {layers:tmplayer,use:"filter",mode:"touch",pads_as:"shape",f_types:"pad",polarity:"positive\;negative",include_syms:drill.symbol}) if(GEN.getSelectCount()>0){ 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 bgaAnalysis(props){ var job = props.job var steplist = props.steplist var layers = props.layers; var res = {} steplist.forEach(function(step){ GEN.openStep({job:job,name: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].min1_bga_dia = myFixed(min_symbols_info["min_bga_size"],2) res[layer.signalL].min1_bga_sm_dia = myFixed(min_symbols_info["min_bga_openging_size"],2) res[layer.signalL].min1_bga_grid = myFixed(min_symbols_info["min_bga_pitch"],2) // 分析所有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].min2_bga_grid = myFixed(all_bga_min_pitch_info["bga_min_pitch"],2) res[layer.signalL].min2_bga_dia = myFixed(all_bga_min_pitch_info["bga_min_pitch_pad_size"],2) res[layer.signalL].min2_bga_sm_dia = myFixed(all_bga_min_pitch_info["bga_min_pitch_openging_size"],2) GEN.deleteLayer({job:job,layer:tmp_layer}) } }) GEN.closeStep() }) 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}) // 留下pitch最小的bga 如果pitch都一样大 就跳过 var min_pitch = bgaPitch({job:job,step:step,layer:tmp_layer}) if(min_pitch){ res.min_bga_pitch = (min_pitch-0) + (symbols.size-0) } // 分析数据 GEN.clearLayers() // 尺寸 res.min_bga_size = symbols.size // 开窗大小 if(layer.solderL){ 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"}) GEN.selContourize() GEN.COM("sel_cont2pad,match_tol=0,restriction=,min_size=5,max_size=100,suffix=+++") GEN.COM("cur_atr_set,attribute=.smd") GEN.COM("sel_change_atr,mode=add") var ck = "tmp_chk" if(GEN.isChklistExists({job:job,step:step,chklist:ck})){ GEN.COM("chklist_delete", { chklist: ck })} var tmpItem = { name: "opingsize", 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", pp_tests: "SMD", pp_check_missing_pads_for_drills:"Yes" } } if(PAR.erf){ tmpItem.erf = PAR.erf } GEN.createChklist({ chklist: ck, items: [tmpItem] }) GEN.chklistShow({ chklist: ck }) GEN.chklistRun({ chklist: ck, nact: 1, area: 'profile' }) var min_smd = ["min_smd_pad"] min_smd = min_smd.map(function(v){ return GEN.getCheckAttr({job:job,step:step,checklist:ck,attr:v,nact:1}) }) min_smd = min_smd.filter(function(v){return v!="N/A"}) if(min_smd.length == 0){ res.min_bga_openging_size = "todo" // todo } else { res.min_bga_openging_size = min_smd.sort(function(a,b){return a-b})[0] } GEN.deleteLayer({job:job,layer:[solderL_tmp,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 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 }) } // 创建并运行 var tmpItem = { name: "bgaPitch", 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", pp_tests: "Spacing", pp_check_missing_pads_for_drills:"Yes" } } if(PAR.erf){ tmpItem.erf = PAR.erf } GEN.createChklist({ // 创建checklist chklist: ck, items: [tmpItem] }) 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:"p2p"}]}) var tmp_layer = tmp_layer2 + "_tmp" selCopyLayer({job:job,layer:tmp_layer}) GEN.workLayer({name:tmp_layer,display_number:1,clear_before:'yes'}); // 获取最小bga间距 var features = GEN.getFeatures({job:job,step:step,layer:tmp_layer}) if(features && features.length){ var tmp = features.map(function(v){ var num = Math.sqrt((v.xe-v.xs)*(v.xe-v.xs) + (v.ye-v.ys)*(v.ye-v.ys))*1000 return {num:num.toFixed(4),symbol:v.symbol} }) var bga_min_pitch = tmp.sort(function(a,b){return a-b})[0] res.bga_min_pitch = bga_min_pitch.num // pitch 中选最小的 GEN.selClearFeature() GEN.COM("filter_set,filter_name=popup,update_popup=no,profile=in") GEN.COM("filter_set,filter_name=popup,update_popup=yes,slot=line,slot_by=length,min_len=0,max_len="+(bga_min_pitch.num/1000)+0.0002) GEN.COM("filter_area_strt") GEN.COM("filter_area_end,layer=,filter_name=popup,operation=select,area_type=none,inside_area=no,intersect_area=no") GEN.COM("filter_reset,filter_name=popup") } // 所有BGA中间距最小的PAD大小 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] // 删除非最小的 GEN.selectByFilter({include_syms:min_pad_symbol.symbol}) GEN.selReverse() if(GEN.getSelectCount()>0){GEN.selDelete()} res.bga_min_pitch_pad_size = min_pad_symbol.size res.bga_min_pitch = (res.bga_min_pitch || 20) - 0 + (min_pad_symbol.size-0) if(solder_layer){ // 最小pitch开窗大小 GEN.workLayer({name:solder_layer,display_number:1,clear_before:'yes'}); GEN.selClearFeature() GEN.selRefFeat({layers:min_pitch_layer,use:'filter',mode:'cover'}) 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"}) GEN.selContourize() GEN.COM("sel_cont2pad,match_tol=0,restriction=,min_size=5,max_size=100,suffix=+++") GEN.COM("cur_atr_set,attribute=.smd") GEN.COM("sel_change_atr,mode=add") var ck = "tmp_chk" if(GEN.isChklistExists({job:job,step:step,chklist:ck})){ GEN.COM("chklist_delete", { chklist: ck })} var tmpItem = { name: "opingsize", 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", pp_tests: "SMD", pp_check_missing_pads_for_drills:"Yes" } } if(PAR.erf){ tmpItem.erf = PAR.erf } GEN.createChklist({ chklist: ck, items: [tmpItem] }) GEN.chklistShow({ chklist: ck }) GEN.chklistRun({ chklist: ck, nact: 1, area: 'profile' }) var min_smd = ["min_smd_pad"] min_smd = min_smd.map(function(v){ return GEN.getCheckAttr({job:job,step:step,checklist:ck,attr:v,nact:1}) }) min_smd = min_smd.filter(function(v){return v!="N/A"}) if(min_smd.length == 0){ res.bga_min_pitch_openging_size = "todo" // todo } else { res.bga_min_pitch_openging_size = min_smd.sort(function(a,b){return a-b})[0] } GEN.deleteLayer({job:job,layer:[solder_layer_tmp,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 }) } // 创建并运行 var tmpItem = { name: "bgaPitch", nact: 1, action: "valor_analysis_signal", 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" } } if(PAR.erf) { tmpItem.erf = PAR.erf } GEN.createChklist({ // 创建checklist chklist: ck, items: [tmpItem] }) 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:"p2p"}]}) var tmp_layer = tmp_layer2 + "_tmp" selCopyLayer({job:job,layer:tmp_layer}) GEN.workLayer({name:tmp_layer,display_number:1,clear_before:'yes'}); var features = GEN.getFeatures({job:job,step:step,layer:tmp_layer}) var res if(features && features.length){ var tmp = features.map(function(v){ var num = Math.sqrt((v.xe-v.xs)*(v.xe-v.xs) + (v.ye-v.ys)*(v.ye-v.ys))*1000 return num.toFixed(4) }) res = tmp.sort(function(a,b){return a-b})[0] } if (res) { // 过滤掉pitch不是最小的线 GEN.COM("filter_set,filter_name=popup,update_popup=no,profile=in") GEN.COM("filter_set,filter_name=popup,update_popup=yes,slot=line,slot_by=length,min_len=0,max_len="+(res/1000)+0.0002) GEN.COM("filter_area_strt") GEN.COM("filter_area_end,layer=,filter_name=popup,operation=select,area_type=none,inside_area=no,intersect_area=no") GEN.COM("filter_reset,filter_name=popup") GEN.selReverse() if(GEN.getSelectCount()>0){GEN.selDelete()} GEN.workLayer({name:layer,display_number:1,clear_before:'yes'}); GEN.selRefFeat({layers:tmp_layer,use:'filter',mode:'touch'}) GEN.selReverse() if(GEN.getSelectCount()>0){GEN.selDelete()} } GEN.deleteLayer({job:job,layer:tmp_layer}) GEN.deleteLayer({job:job,layer:tmp_layer1}) GEN.deleteLayer({job:job,layer:tmp_layer2}) // 矩阵 return res || 20.1 } function myFixed(str, num) { if(/^[1-9][0-9]*([.][0-9]+)?$/.test(str)){ return Number(str).toFixed(num) } else { return str } } function createOutline(props){ var job = props.job var step = props.step var matrix = props.matrix var outlines = Object.keys(matrix).filter(function(v){return /^outline$|^rout$/ig.test(v)}) var drill_layer = Object.keys(matrix).filter(function(v){return matrix[v].layer_type=="drill" && matrix[v].context == "board"}) var tmp_outline if(outlines.length){ if( outlines.length > 1 && outlines.indexOf("outline")>=0){ tmp_outline = "outline" if(cl(tmp_outline)) { return true }else{ return cl("rout") } } else { return cl(outlines[0]) } }else{return false} function cl(l){ GEN.openStep({job:job, name:step}) GEN.affectedLayer({affected:'no',mode:'all'}) GEN.workLayer({name:l,display_number:2,clear_before:"yes"}) GEN.selClearFeature() GEN.COM("sel_cut_data,det_tol=1,con_tol=1,rad_tol=0.1,filter_overlaps=no,delete_doubles=no,use_order=yes,ignore_width=yes,ignore_holes=none,start_positive=yes,polarity_of_touching=same") var tmp_layer = l+"+++" GEN.selRefFeat({layers:drill_layer[0],use:'filter',mode:'touch'}) if(GEN.getSelectCount() > 0){ var tmp_outline = l + "_tmp" selCopyLayer({job:job,layer:tmp_outline}) GEN.workLayer({name:l,display_number:2,clear_before:"yes"}) GEN.selAllFeat() GEN.selDelete() GEN.workLayer({name:tmp_outline,display_number:2,clear_before:"yes"}) GEN.COM("sel_surf2outline,width=15") GEN.selAllFeat() selCopyLayer({job:job,layer:l}) GEN.workLayer({name:l,display_number:2,clear_before:"yes"}) GEN.selClearFeature() GEN.selCreateProfile() GEN.deleteLayer({job:job, layer:tmp_layer}) GEN.deleteLayer({job:job, layer:tmp_outline}) return true } GEN.deleteLayer({job:job, layer:tmp_layer}) return false } } function has_steps(props){ var job = props.job var pcs_step = props.pcs_step var array_step = props.array_step var res = GEN.getSubSteps({job:job, step:array_step}) return res.indexOf(pcs_step) >= 0 } function unSelect(){ GEN.selectByFilter({attribute:[{attribute:'.drill',option:'plated'}],profile:'all',operation:"unselect"}) GEN.selectByFilter({attribute:[{attribute:'.drill',option:'via'}],profile:'all',operation:"unselect"}) GEN.selectByFilter({attribute:[{attribute:'.drill',option:'non_plated'}],profile:'all',operation:"unselect"}) } function mkPath(path,list) { console.log("--path:" + path); if(list.length){ var newPath = path + '/' + list.shift() if(!fs.dirExists(newPath)){ fs.mkdir(newPath) console.log("--mkdir:" + newPath); } return mkPath(newPath,list) } else {return path} } function saveMeans(props){ var job = props.job var step = props.step var chklist = props.chklist var nact = props.nact var layer = props.layer // 获取meas var meas = GEN.getCheckMeas({job:job,step:step,chklist:chklist,nact:nact,layer:layer}) var basepath = props.jobpath+"/user/opcam/steps" var respath = mkPath(basepath,[step,"chk",chklist+"_"+nact,layer]) fs.writeFile(respath+"/meas",meas.join("\n")) fs.chmod(respath+"/meas",777) } // 算弧长 function arcLength(x1,y1,x2,y2,x3,y3){ var cos0 = +((x3-x1)*(x2-x3) + (y3-y1)*(y2-y3)) / (Math.pow((Math.pow(x2-x3,2) + Math.pow(y2-y3,2)),0.5) * Math.pow((Math.pow(x3-x1,2) + Math.pow(y3-y1,2)),0.5)) var tmp = Math.acos(cos0) var sin0 = Math.sin(tmp) var length = +Math.sqrt(Math.pow(x2-x1,2) + Math.pow(y2-y1,2)) / sin0 * tmp return length }