/** * @file gendrill_Excellon_writer.h * @brief Classes used in drill files, map files and report files generation. */ /* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 1992-2017 Jean_Pierre Charras * Copyright (C) 1992-2017 KiCad Developers, see change_log.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifndef _GENDRILL_EXCELLON_WRITER_ #define _GENDRILL_EXCELLON_WRITER_ #include class BOARD; class PLOTTER; // the DRILL_TOOL class handles tools used in the excellon drill file: class DRILL_TOOL { public: int m_Diameter; // the diameter of the used tool (for oblong, the smaller size) int m_TotalCount; // how many times it is used (round and oblong) int m_OvalCount; // oblong count bool m_Hole_NotPlated; // Is the hole plated or not plated public: DRILL_TOOL( int aDiameter, bool a_NotPlated ) { m_TotalCount = 0; m_OvalCount = 0; m_Diameter = aDiameter; m_Hole_NotPlated = a_NotPlated; } }; /* the HOLE_INFO class handle hole which must be drilled (diameter, position and layers) * For buried or micro vias, the hole is not on all layers. * So we must generate a drill file for each layer pair (adjacent layers) * Not plated holes are always through holes, and must be output on a specific drill file * because they are drilled after the Pcb process is finished. */ class HOLE_INFO { public: int m_Hole_Diameter; // hole value, and for oblong: min(hole size x, hole size y) int m_Tool_Reference; // Tool reference for this hole = 1 ... n (values <=0 must not be used) wxSize m_Hole_Size; // hole size for oblong holes double m_Hole_Orient; // Hole rotation (= pad rotation) for oblong holes int m_Hole_Shape; // hole shape: round (0) or oval (1) wxPoint m_Hole_Pos; // hole position LAYER_ID m_Hole_Bottom_Layer; // hole ending layer (usually back layer) LAYER_ID m_Hole_Top_Layer; // hole starting layer (usually front layer): // m_Hole_Top_Layer < m_Hole_Bottom_Layer bool m_Hole_NotPlated; // hole not plated. Must be in a specific drill file or section public: HOLE_INFO() { m_Hole_NotPlated = false; m_Hole_Diameter = 0; m_Tool_Reference = 0; m_Hole_Orient = 0.0; m_Hole_Shape = 0; m_Hole_Bottom_Layer = B_Cu; m_Hole_Top_Layer = F_Cu; } }; /* the DRILL_PRECISION helper class to handle drill precision format in excellon files */ class DRILL_PRECISION { public: int m_lhs; // Left digit number (integer value of coordinates) int m_rhs; // Right digit number (decimal value of coordinates) public: DRILL_PRECISION( int l = 2, int r = 4 ) { m_lhs = l; m_rhs = r; } wxString GetPrecisionString() { wxString text; text << m_lhs << wxT( ":" ) << m_rhs; return text; } }; typedef std::pair DRILL_LAYER_PAIR; class OUTPUTFORMATTER; /** * EXCELLON_WRITER is a class mainly used to create Excellon drill files * However, this class is also used to create drill maps and drill report */ class EXCELLON_WRITER { public: enum ZEROS_FMT { // Zero format in coordinates DECIMAL_FORMAT, // Floating point coordinates SUPPRESS_LEADING, // Suppress leading zeros SUPPRESS_TRAILING, // Suppress trainling zeros KEEP_ZEROS // keep zeros }; wxPoint m_Offset; // offset coordinates bool m_ShortHeader; // true to generate the smallest header (strip comments) private: FILE* m_file; // The output file BOARD* m_pcb; bool m_minimalHeader; // True to use minimal header // in excellon file (strip comments) bool m_unitsDecimal; // true = decimal, false = inches ZEROS_FMT m_zeroFormat; // the zero format option for output file DRILL_PRECISION m_precision; // The current coordinate precision (not used in decimal format) double m_conversionUnits; // scaling factor to convert the board unites to Excellon units // (i.e inches or mm) bool m_mirror; wxPoint m_offset; // Drill offset coordinates bool m_merge_PTH_NPTH; // True to generate only one drill file std::vector m_holeListBuffer; // Buffer containing holes std::vector m_toolListBuffer; // Buffer containing tools PlotFormat m_mapFileFmt; // the format of the map drill file, // if this map is needed const PAGE_INFO* m_pageInfo; // the page info used to plot drill maps // If NULL, use a A4 page format public: EXCELLON_WRITER( BOARD* aPcb ); ~EXCELLON_WRITER() { } /** * Return the plot offset (usually the position * of the auxiliary axis */ const wxPoint GetOffset() { return m_offset; } /** * Function SetFormat * Initialize internal parameters to match the given format * @param aMetric = true for metric coordinates, false for imperial units * @param aZerosFmt = DECIMAL_FORMAT, SUPPRESS_LEADING, SUPPRESS_TRAILING, KEEP_ZEROS * @param aLeftDigits = number of digits for integer part of coordinates * if <= 0 (default), a suitable value will be used, depending on units * @param aRightDigits = number of digits for mantissa part of coordinates * if <= 0 (default), a suitable value will be used, depending on units */ void SetFormat( bool aMetric, ZEROS_FMT aZerosFmt = DECIMAL_FORMAT, int aLeftDigits = 0, int aRightDigits = 0 ); /** * Sets the page info used to plot drill maps * If NULL, a A4 page format will be used * @param aPageInfo = a reference to the page info, usually used to plot/display the board */ void SetPageInfo( const PAGE_INFO* aPageInfo ) { m_pageInfo = aPageInfo; } /** * Function SetMapFileFormat * Initialize the format for the drill map file * @param SetMapFileFormat = a PlotFormat value (one of * PLOT_FORMAT_HPGL, PLOT_FORMAT_POST, PLOT_FORMAT_GERBER, * PLOT_FORMAT_DXF, PLOT_FORMAT_SVG, PLOT_FORMAT_PDF * the most useful are PLOT_FORMAT_PDF and PLOT_FORMAT_POST */ void SetMapFileFormat( PlotFormat aMapFmt ) { m_mapFileFmt = aMapFmt; } /** * Function SetOptions * Initialize internal parameters to match drill options * @param aMirror = true to create mirrored coordinates (Y coordinates negated) * @param aMinimalHeader = true to use a minimal header (no comments, no info) * @param aOffset = drill coordinates offset */ void SetOptions( bool aMirror, bool aMinimalHeader, wxPoint aOffset, bool aMerge_PTH_NPTH ) { m_mirror = aMirror; m_offset = aOffset; m_minimalHeader = aMinimalHeader; m_merge_PTH_NPTH = aMerge_PTH_NPTH; } /** * Function CreateDrillandMapFilesSet * Creates the full set of Excellon drill file for the board * filenames are computed from the board name, and layers id * @param aPlotDirectory = the output folder * @param aGenDrill = true to generate the EXCELLON drill file * @param aGenMap = true to generate a drill map file * @param aReporter = a REPORTER to return activity or any message (can be NULL) */ void CreateDrillandMapFilesSet( const wxString& aPlotDirectory, bool aGenDrill, bool aGenMap, REPORTER * aReporter = NULL ); /** * Function GenDrillReportFile * Create a plain text report file giving a list of drill values and drill count * for through holes, oblong holes, and for buried vias, * drill values and drill count per layer pair * there is only one report for all drill files even when buried or blinds vias exist * * Here is a sample created by this function: * Drill report for F:/tmp/interf_u/interf_u.brd * Created on 04/10/2012 20:48:38 * Selected Drill Unit: Imperial (inches) * * Drill report for plated through holes : * T1 0,025" 0,64mm (88 holes) * T2 0,031" 0,79mm (120 holes) * T3 0,032" 0,81mm (151 holes) (with 1 slot) * T4 0,040" 1,02mm (43 holes) * T5 0,079" 2,00mm (1 hole) (with 1 slot) * T6 0,120" 3,05mm (1 hole) (with 1 slot) * * Total plated holes count 404 * * * Drill report for buried and blind vias : * * Drill report for holes from layer Soudure to layer Interne1 : * * Total plated holes count 0 * * * Drill report for holes from layer Interne1 to layer Interne2 : * T1 0,025" 0,64mm (3 holes) * * Total plated holes count 3 * * * Drill report for holes from layer Interne2 to layer Composant : * T1 0,025" 0,64mm (1 hole) * * Total plated holes count 1 * * * Drill report for unplated through holes : * T1 0,120" 3,05mm (1 hole) (with 1 slot) * * Total unplated holes count 1 * * @param aFullFileName : the name of the file to create * m_unitsDecimal = false to use inches, true to use mm in report file * * @return success if the file is created */ bool GenDrillReportFile( const wxString& aFullFileName ); /** * Function GenDrillMapFile * Plot a map of drill marks for holes. * the paper sheet to use to plot the map is set in m_pageInfo * ( calls SetPageInfo() to set it ) * if NULL, A4 format will be used * @param aFullFileName : the full filename of the map file to create, * @param aFormat : one of the supported plot formats (see enum PlotFormat ) */ bool GenDrillMapFile( const wxString& aFullFileName, PlotFormat aFormat ); private: /** * Function CreateDrillFile * Creates an Excellon drill file * @param aFile = an opened file to write to will be closed by CreateDrillFile * @return hole count */ int createDrillFile( FILE * aFile ); /** * Function BuildHolesList * Create the list of holes and tools for a given board * The list is sorted by increasing drill size. * Only holes included within aLayerPair are listed. * If aLayerPair identifies with [F_Cu, B_Cu], then * pad holes are always included also. * * @param aLayerPair is an inclusive range of layers. * @param aGenerateNPTH_list : * true to create NPTH only list (with no plated holes) * false to created plated holes list (with no NPTH ) */ void buildHolesList( DRILL_LAYER_PAIR aLayerPair, bool aGenerateNPTH_list ); int getHolesCount() const { return m_holeListBuffer.size(); } /* Print the DRILL file header. The full header is: * M48 * ;DRILL file {PCBNEW (2007-11-29-b)} date 17/1/2008-21:02:35 * ;FORMAT={ / absolute / / } * FMAT,2 * INCH,TZ */ void writeEXCELLONHeader(); void writeEXCELLONEndOfFile(); /* Created a line like: * X48000Y19500 * According to the selected format */ void writeCoordinates( char* aLine, double aCoordX, double aCoordY ); /** Helper function. * Writes the drill marks in HPGL, POSTSCRIPT or other supported formats * Each hole size has a symbol (circle, cross X, cross + ...) up to * PLOTTER::MARKER_COUNT different values. * If more than PLOTTER::MARKER_COUNT different values, * these other values share the same mark shape * @param aPlotter = a PLOTTER instance (HPGL, POSTSCRIPT ... plotter). */ bool plotDrillMarks( PLOTTER* aPlotter ); /// Get unique layer pairs by examining the micro and blind_buried vias. std::vector getUniqueLayerPairs() const; /** * Function printToolSummary * prints m_toolListBuffer[] tools to aOut and returns total hole count. * @param aOut = the current OUTPUTFORMATTER to print summary * @param aSummaryNPTH = true to print summary for NPTH, false for PTH */ unsigned printToolSummary( OUTPUTFORMATTER& aOut, bool aSummaryNPTH ) const; const std::string layerPairName( DRILL_LAYER_PAIR aPair ) const; const std::string layerName( LAYER_ID aLayer ) const; /** * @return a filename which identify the drill file function. * it is the board name with the layer pair names added, and for separate * (PTH and NPTH) files, "-NPH" or "-NPTH" added * @param aPair = the layer pair * @param aNPTH = true to generate the filename of NPTH holes * @param aMerge_PTH_NPTH = true to generate the filename of a file which containd both * NPH and NPTH holes */ const wxString drillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH, bool aMerge_PTH_NPTH ) const; }; #endif // #ifndef _GENDRILL_EXCELLON_WRITER_