/* IPCOUT (LAY) -- IPC-D-356 Test Data Output */ /* IPCOUT (LAY) -- IPC-D-356-Testdatenausgabe */ /* // Copyright (c) 1998-2012 Oliver Bartels F+E, Muenchen // Author: Roman Ludwig // Changes History: // rl (120427) RELEASED FOR BAE V7.8. // rl (111216) ENHANCEMENT: // Added remote controlled output file name definition support. // rl (111209) CHANGE: // Using CAM origin/rotation/mirroring in CAM environment. // rl (101019) RELEASED FOR BAE V7.6. // rl (091021) RELEASED FOR BAE V7.4. // rl (091007) ENHANCEMENT: // Added variant specific output option. // Added unconnected pin output supression option. // rl (081014) RELEASED FOR BAE V7.2. // rl (071029) RELEASED FOR BAE V7.0. // rl (060920) RELEASED FOR BAE V6.8. // rl (060920) ENHANCEMENT: // Added buried drill ignore option. // rl (050906) RELEASED FOR BAE V6.6. // rl (041123) BUGFIX: // Fixed erroneous header commands. // rl (040811) RELEASED FOR BAE V6.4. // rl (040721) ENHANCEMENT: // Added full batch call support. // rl (030904) RELEASED FOR BAE V6.2. // rl (021209) RELEASED FOR BAE V6.0. // rl (020618) RELEASED FOR BAE V5.4. // rl (010625) RELEASED FOR BAE V5.0. // rl (010320) ENHANCEMENT: // Added optional parameter settings from bae.ini file. // rl (001115) ENHANCEMENT: // Added pin/via/drill count statistic at end of file. // rl (001025) BUGFIX: // Fixed empty padstack negative drill size output bug. // rl (001023) ENHANCEMENT: // Added drill class plating information field. // Added net/drill sorting. // rl (000509) RELEASED FOR BAE V4.6. // rl (000131) BUGFIX: // Fixed pin name length bug. // rl (990702) RELEASED FOR BAE V4.4. // rl (980910) RELEASED FOR BAE V4.2. // rl (980814) ORIGINAL CODING. // // DESCRIPTION // // The ipcout User Language program produces a IPC-D-356 // format test data output for the currently loaded layout. // The output is directed to a file. // Vias are output as mid points. Solder mask information is derived // from documentary layer 2. // */ // Includes #include "pop.ulh" // User Language popup utilities #include "lay.ulh" // User Language layout utilities // Disable undo state request #pragma ULCALLERNOUNDO // Enforce layout caller type #pragma ULCALLERLAY // INI file parameter name definitions #define PAR_WRNETNUM "WRNETNUM_LAY" // Net number write flag #define PAR_IPCEXT "IPCEXT_LAY" // IPC file extension #define PAR_SOLDERMASK "SOLDERMASK_LAY"// Solder mask base layer #define PAR_DRLPLATED "DRLPLATED_LAY" // Drill plated flags field #define PAR_DRLMIDIGN "DRLMIDIGN_LAY" // Drill buried flags field #define PAR_VAROUT "IPCVAROUT_LAY" // Variant specific output flag #define PAR_UNCONPIN "IPCUNCONPIN_LAY" // Unconnected pins output flag // Messages string REPTDWRITE = M("IPC-D-356-Ausgabe...","IPC-D-356 output..."); string REPTDDONE = M("IPC-D-356-Daten ausgegeben auf Datei '%s'.", "IPC-D-356 data written to file '%s'."); string REPTDFILE = M("IPC-D-356-Testdaten Datei ...........: '%s'", "IPC-D-356 Test Data File ................: '%s'"); // Format strings #define FMTTESTHD "C Bartels AutoEngineer %s IPC-D-356 Output\n" #define FMTDATAHD0 "P JOB %s, PCB %s\nP UNITS CUST0\nP TITLE %s\n" #define FMTDATAHD1 "P NUM ?\nP REV ?\n" #define FMTSTK "3%d7%-17s%-6s-%-4s%s" #define FMTSMDSTK " " #define FMTDRLSTK "D%04.0f%s" #define FMTSTKEND "A%02dX%7.0fY%7.0fX%4.0fY%4.0f S%d\n" #define FMTDRL "367 - D%04.0f%s X%7.0fY%7.0f\n" #define FMTVIASTAT "C %4d via(s)\n" #define FMTTOPSTAT "C %4d pin(s) testable only from top\n" #define FMTBOTSTAT "C %4d pin(s) testable only from bottom\n" #define FMTALLSTAT "C %4d pin(s) testable from both sides\n" #define FMTDRLSTAT "C %4d pure drill(s)\n" #define FMTTABEND "999\n" #define FMTNETNUM "N%d" // Globals string IPCEXT = bae_inistrval(PAR_IPCEXT,".356")/* IPC test file name ext. */; int WRITENETNUM = bae_iniintval(PAR_WRNETNUM,1) /* Net number write flag */; int DOCLSMSK = bae_iniintval(PAR_SOLDERMASK,0x410) /* Solder mask lay.base doc.layer 2 */; #define GV_BATCALL "BAT_CALL" // Remote control operation flag #define GV_BATREPCNT "BAT_REPCNT" // Remote control op. report count #define GV_BATREPLINE "BAT_REPL_" // Remote control op. report line static int tdfh /* Test data output file handle */; static string jobfname /* Job file name */; static string jobename /* Job element name */; static index L_POOL curpadpool /* Current pad pool index */; static string currefname /* Current reference name */; static int toplay /* Top layer code */; string plfield= bae_inistrval(PAR_DRLPLATED,"111111111111111111111111111") /* Drill plated field class '-' followed by classes 'A'-'Z' */; string buriedfield= bae_inistrval(PAR_DRLMIDIGN,"000000000000000000000000000") /* Drill buried field class '-' followed by classes 'A'-'Z' */; struct testentry { // Test data entry double rad /* Test point drill radius */; double cx, cy /* Test point center coordinates */; double lx, ly /* Test point lower coordinates */; double ux, uy /* Test point upper coordinates */; int plated /* Test point plated flag */; int layfield /* Test point layer field */; int smask /* Test point solder mask field */; int netidx /* Test point net output list index */; int netnext /* Test point next net list index */; string netname /* Test point net name */; string refname /* Test point reference name */; string pinname /* Test point pin name */; } testl[] /* Test data list */; static int testn = 0 /* Test data count */; static int tdflag = 0 /* Test data stored flag */; static int nettl[] /* Net test point chain. header list */; static int maxnet = 0 /* Max. net index */; struct ptdes { // Point descriptor double x,y /* Point coordinate */; int typ /* Point type */; }; static STRINGS msgl /* Message list */; static int msgn = 0 /* Message count */; static int drln = 0 /* Drill count */; static int vian = 0 /* Via count */; static int topn = 0 /* Top SMD pin count */; static int botn = 0 /* Bottom SMD pin count */; static int alln = 0 /* Both sides testable pin count */; static int varflag = bae_iniintval(PAR_VAROUT,0) /* Variant data output flag */; static int varnum /* Variant number */; static int unconpinflag = bae_iniintval(PAR_UNCONPIN,1) /* Unconnected pins output flag */; static double XOFF = bae_planwsnx()/* Plan origin X offset */; static double YOFF = bae_planwsny()/* Plan origin Y offset */; static double ROTANG = 0.0 /* Plan rotation angle */; static int MIRRFLAG = 0 /* Plan mirror flag */; // Main program void main() { string tdname /* Test data output file name */; int rot /* Rotation flag */; int i /* Loop control variable */; // Abort if invalid plan class if (bae_planddbclass()!=DDBCLLAY) error_class(); // Perform BAE Demo check with abort option BAE_Demo_check(2); // Save current element with verification on request verifysave(); if (uliptype()==ULIPCAM) { cam_getgenpltparam(0,0,rot,MIRRFLAG,0,0.0,XOFF,YOFF); MIRRFLAG=((MIRRFLAG&1)!=0)!=(MIRRFLAG>=2) ? 1 : 0 ; if (rot) ROTANG=cvtangle(90.0,1,0); } // Get the plan file and element name jobfname=bae_planfname(); jobename=bae_planename(); // Get the output file name if (bae_peekiact()==2) tdname=askstr("",MAXPATHLEN); else tdname=convstring(jobfname,0)+IPCEXT; // Query the current active variant if (lay_rulequery(RS_OCPLAN,0,RS_PCBSUBJ,RS_VARIANT,"?d",varnum)<1) // Use base variant varnum=0; // Open the test data output file tdfh=bae_fopen(tdname,1); // Write the output message bae_prtdialog(REPTDWRITE); // Scan the test data ipcscan(); // Write the test data ipcout(); // Close test data file fclose(tdfh); // Check if remote control operation if (varget(GV_BATCALL,0)==0) { vardelete(GV_BATCALL); // Print messages sprintf(msgl[msgn],REPTDFILE,tdname); msgn++; for (i=0;i=0;i--) nettl[i]=(-1); for (i=0;i=0) { if (testl[i].layfield==0) fprintf(tdfh,FMTDRL,outlength(testl[i].rad), testl[i].plated ? "P" : " ", outlength(testl[i].cx), outlength(MIRRFLAG ? -testl[i].cy : testl[i].cy)); i=testl[i].netnext; } } // Write all pin data for (netidx=0;netidx<=maxnet;netidx++) { i=nettl[netidx]; while (i>=0) { switch (testl[i].layfield) { case 0 : i=testl[i].netnext; drln++; continue; case 1 : laynum=toplay+1; if (testl[i].pinname=="") vian++; else botn++; break; case 2 : laynum=1; if (testl[i].pinname=="") vian++; else topn++; break; case 3 : default : laynum=0; if (testl[i].pinname=="") vian++; else alln++; } fprintf(tdfh,FMTSTK,testl[i].rad<=0.0 ? 2 : 1, testl[i].netname,testl[i].refname,testl[i].pinname, testl[i].pinname=="" ? "M" : " "); if (testl[i].rad<=0.0) { cx=0.5*(testl[i].lx+testl[i].ux); cy=0.5*(testl[i].ly+testl[i].uy); xldim=testl[i].ux-testl[i].lx; yldim=testl[i].uy-testl[i].ly; fprintf(tdfh,FMTSMDSTK); } else { cx=testl[i].cx; cy=testl[i].cy; rad=testl[i].rad; xudim=testl[i].ux-cx; yudim=testl[i].uy-cy; xldim=cx-testl[i].lx; yldim=cy-testl[i].ly; if (xudimxudim) xldim=xudim; if (yldimyudim) yldim=yudim; xldim+=xudim; yldim+=yudim; fprintf(tdfh,FMTDRLSTK,outlength(testl[i].rad), testl[i].plated ? "P" : " "); } fprintf(tdfh,FMTSTKEND,laynum, outlength(cx),outlength(MIRRFLAG ? -cy : cy), outlength(xldim),outlength(yldim),testl[i].smask); i=testl[i].netnext; } } // Write test data table end fprintf(tdfh,FMTVIASTAT,vian); fprintf(tdfh,FMTTOPSTAT,topn); fprintf(tdfh,FMTBOTSTAT,botn); fprintf(tdfh,FMTALLSTAT,alln); fprintf(tdfh,FMTDRLSTAT,drln); fprintf(tdfh,FMTTABEND); } double outlength(double val) /* // Convert a length value to output units // Return value : // converted length value // Parameters : // double val : Value */ { double newval /* New value */; // Convert to mil newval=10.0*cvtlength(val,0,3); // Prevent output of signed zero return(newval==0.0?0.0:newval); } // Scan functions int tdmac(index L_MACRO macro,index L_POOL pool, int macinws,string refname,index L_LEVEL level) /* // Test data macro scan function // Return value : // 0 if out. ws., 1 if inside, 2 if unknown or (-1) on error // Parameters : // index L_MACRO macro : Macro index // index L_POOL pool : Macro pool index // int macinws : Macro in workspace flag // string refname : Macro reference name // index L_LEVEL level : Macro level */ { index L_CNET net /* Net index */; index L_CPART cpart /* Connection part index */; index L_CPIN cpin /* Connection pin index */; int tree /* Tree number */; // Check class switch (macro.CLASS) { // Part case DDBCLLPRT : // Store the reference name currefname=refname; // Check if part placed if (varflag && lay_findconpart(currefname,cpart)==0 && getlayattribval(cpart,"$noplc")!="") // Abort scan for unplaced variant part return(0); if (strlen(currefname)>6) currefname[6]='\0'; break; // Padstack case DDBCLLSTK : // Test if data stored if (tdflag) { // Init. next data field testn++; testl[testn].layfield=0; testl[testn].rad=(-1.0); testl[testn].plated=1; testl[testn].netnext=(-1); tdflag=0; } testl[testn].smask=0; // Store the reference names testl[testn].refname=refname=="" ? "" : currefname; testl[testn].pinname=refname; // Restrict pin name length if (strlen(testl[testn].pinname)>4) testl[testn].pinname[4]='\0'; // Init. tree info from connectivity tree=level.LEVVAL; // Prefer connection list tree info if (refname!="" && lay_findconpart(currefname,cpart)==0 && lay_findconpartpin(refname,cpart,cpin)==0) tree=cpin.TREE; // Get pad tree name if (lay_gettreeidx(tree,net)) { if (!unconpinflag) return(0); testl[testn].netname="N/C"; testl[testn].netidx=0; } else { if (WRITENETNUM) sprintf(testl[testn].netname,FMTNETNUM,tree+1); else testl[testn].netname=net.NAME; testl[testn].netidx=tree+1; if (maxnet<(tree+1)) maxnet=tree+1; } break; // Pad case DDBCLLPAD : // Store the current pad pool index curpadpool=pool; break; } // Continue scan return(1); } int tdpoly(index L_POOL ppool,int layer, int polyinws,int tree,index L_LEVEL level) /* // Process an already transformed polygon data block // Return value : // zero if done or (-1) on file error // Parameters : // index L_POLY poly : Polygon // int layer : Polygon layer // int polyinws : Poly in workspace flag // int tree : Polygon tree number // index L_LEVEL level : Polygon level */ { index L_POLY poly /* Polygon index */; index L_POINT point /* Polygon point */; index L_POOL spool /* Scan pool index */; double polyxmin /* Polygon lower X boundary */; double polyymin /* Polygon lower Y boundary */; double polyxmax /* Polygon upper X boundary */; double polyymax /* Polygon upper Y boundary */; int layfield /* Layer field */; // Get the polygon index poly=ppool.POLY; // Test if the polygon type is valid if (poly.TYP!=L_POLYCOPPASS || layer>=DOCLAYBASE) { // Test if solder mask polygon if (poly.TYP==L_POLYDOCAREA || poly.TYP==L_POLYCOPPASS) { // Set the solder mask field bits if (layer==DOCLSMSK) testl[testn].smask|=(MIRRFLAG ? 0x01 : 0x02); else if (layer==(DOCLSMSK+1)) testl[testn].smask|=(MIRRFLAG ? 0x02 : 0x01); else if (layer==(DOCLSMSK+2)) testl[testn].smask|=0x03; } // No further polygon processing return(0); } // Check if polygon on current pad for (spool=curpadpool.NXT;spool!=(-1);spool=spool.NXT) if (spool==ppool) break; if (spool==(-1)) // Polygon outside pad return(0); // The layer check was already done by the layer check function // Get the layer field if (layer==LAYERALL) { layfield=0x03; } else if (layer==0) { layfield=MIRRFLAG ? 0x02 : 0x01; } else { layfield=MIRRFLAG ? 0x01 : 0x02; } // Scan polygon size bae_clearpoints(); forall (point of poly) // Store point to internal list bae_storepoint(point.X,point.Y,point.TYP); // Get the polygon range bae_getpolyrange(polyxmin,polyymin,polyxmax,polyymax); // Test if first pad polygon for this pin if (testl[testn].layfield==0) { // Store the polygon data testl[testn].lx=polyxmin; testl[testn].ly=polyymin; testl[testn].ux=polyxmax; testl[testn].uy=polyymax; testl[testn].layfield=layfield; } // Test if same layers else if (testl[testn].layfield==layfield) { // Test if polygons cross if (testl[testn].lx>polyxmax || testl[testn].uxpolyymax || testl[testn].uy ((testl[testn].ux-testl[testn].lx)+ (testl[testn].uy-testl[testn].ly))) { testl[testn].lx=polyxmin; testl[testn].ly=polyymin; testl[testn].ux=polyxmax; testl[testn].uy=polyymax; } // Return without errors return(0); } // Store the cross area if (testl[testn].lxpolyxmax) testl[testn].ux=polyxmax; if (testl[testn].uy>polyymax) testl[testn].uy=polyymax; } else if (layfield!=0x03 && testl[testn].layfield!=0x03) { // Test if polygons cross if (testl[testn].lx>polyxmax || testl[testn].uxpolyymax || testl[testn].uypolyxmax) testl[testn].ux=polyxmax; if (testl[testn].uy>polyymax) testl[testn].uy=polyymax; } tdflag=1; // Return without errors return(0); } int tddrill(index L_DRILL drill,double x,double y, int drillinws,int tree,index L_LEVEL level) /* // Test data drill scan function // Return value : // zero if done, or (-1) on error // Parameters : // index L_DRILL drill : Drill index // double x : Drill X coordinate // double y : Drill Y coordinate // int drillinws : Drill in workspace flag // int tree : Drill tree number // index L_LEVEL level : Drill level */ { if (buriedfield[drill.CLASS]=='1') // Drill buried return(0); // Store max. drill size if (drill.RAD>testl[testn].rad) { testl[testn].cx=x; testl[testn].cy=y; testl[testn].rad=drill.RAD; testl[testn].plated=plfield[drill.CLASS]=='1' ? 1 : 0; } // Set the test data stored flag tdflag=1; // Return without errors return(0); } int tdlchk(int layer) /* // Test data layer output check routine // Return value : // zero if scan break requested, 1 if scan allowed // Parameters : // int layer : Layer code */ { // Check the layer return(layer==LAYERALL || layer==LAYERTOP || layer==0 || layer==toplay || layer==DOCLSMSK || layer==DOCLSMSK+1 || layer==DOCLSMSK+2); } string getlayattribval(index L_CPART part,string attname) /* // Get attribute value // Return value : // attribute value or empty string if not found // Parameters : // index L_CPART part : Part index // string attname : Attribute name */ { index L_ATTRIBUTE att /* Attribute index */; string vattname /* Variant attribute name */; string attval /* Attribute value */; // Set default attribute value attval=""; // Build the variant attribute name vattname=varattrname(attname,varnum); // Search for first attribute forall (att of part where att.NAME==vattname || att.NAME==attname) { // Check if active variant attribute if (att.NAME==vattname) // Attribute value found return(att.VALUE); // Store the base variant attribute value attval=att.VALUE; } // Return the attribute value return(attval); } // User Language program end