diff options
Diffstat (limited to 'gerbview/excellon_read_drill_file.cpp')
-rw-r--r-- | gerbview/excellon_read_drill_file.cpp | 702 |
1 files changed, 702 insertions, 0 deletions
diff --git a/gerbview/excellon_read_drill_file.cpp b/gerbview/excellon_read_drill_file.cpp new file mode 100644 index 0000000..54283ab --- /dev/null +++ b/gerbview/excellon_read_drill_file.cpp @@ -0,0 +1,702 @@ +/** + * @file excellon_read_drill_file.cpp + * Functions to read drill files (EXCELLON format) created by Pcbnew + * These files use only a subset of EXCELLON commands. + */ + + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2014 Jean-Pierre Charras <jp.charras at wanadoo.fr> + * Copyright (C) 1992-2014 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 + */ + +/* + * Here is a sample of drill files created by Pcbnew, in decimal format: + * (Note: coordinates formats are same as Gerber, and T commands are near Gerber D commands). + * M48 + * ;DRILL file {PCBnew (2011-03-14 BZR 2894)-testing} date 15/03/2011 14:23:22 + * ;FORMAT={-:-/ absolute / inch / decimal} + * FMAT,2 + * INCH,TZ + * T1C0.02 + * T2C0.032 + * % + * G90 + * G05 + * M72 + * T1 + * X1.580Y-1.360 + * X1.580Y-4.860 + * X8.680Y-1.360 + * X8.680Y-4.860 + * T2 + * X2.930Y-3.560 + * X5.280Y-2.535 + * X5.405Y-2.610 + * X5.620Y-2.900 + * T0 + * M30 + */ + /* + * Note there are some variant of tool definition: + * T1F00S00C0.2 or T1C0.02F00S00 ... Feed Rate and Spindle Speed of Tool 1 + * Feed Rate and Spindle Speed are just skipped because they are not used in a viewer + */ + +#include <fctsys.h> +#include <common.h> +#include <confirm.h> + +#include <gerbview.h> +#include <gerbview_frame.h> +#include <trigo.h> +#include <macros.h> +#include <base_units.h> +#include <class_gerber_draw_item.h> +#include <class_GERBER.h> +#include <class_excellon.h> +#include <kicad_string.h> +#include <class_X2_gerber_attributes.h> + +#include <cmath> + +#include <html_messagebox.h> + +// Default format for dimensions +// number of digits in mantissa: +static int fmtMantissaMM = 3; +static int fmtMantissaInch = 4; +// number of digits, integer part: +static int fmtIntegerMM = 3; +static int fmtIntegerInch = 2; + +extern int ReadInt( char*& text, bool aSkipSeparator = true ); +extern double ReadDouble( char*& text, bool aSkipSeparator = true ); +extern void fillFlashedGBRITEM( GERBER_DRAW_ITEM* aGbrItem, + APERTURE_T aAperture, + int Dcode_index, + int aLayer, + const wxPoint& aPos, + wxSize aSize, + bool aLayerNegative ); +void fillLineGBRITEM( GERBER_DRAW_ITEM* aGbrItem, + int Dcode_index, + int aLayer, + const wxPoint& aStart, + const wxPoint& aEnd, + wxSize aPenSize, + bool aLayerNegative ); + +// Getber X2 files have a file attribute which specify the type of image +// (copper, solder paste ... and sides tpo, bottom or inner copper layers) +// Excellon drill files do not have attributes, so, just to identify the image +// In gerbview, we add this attribute, like a Gerber drill file +static const char file_attribute[] = ".FileFunction,Other,Drill*"; + +static EXCELLON_CMD excellonHeaderCmdList[] = +{ + { "M0", DRILL_M_END, -1 }, // End of Program - No Rewind + { "M00", DRILL_M_END, -1 }, // End of Program - No Rewind + { "M30", DRILL_M_ENDREWIND, -1 }, // End of Program Rewind + { "M47", DRILL_M_MESSAGE, -1 }, // Operator Message + { "M45", DRILL_M_LONGMESSAGE, -1 }, // Long Operator message (use more than one line) + { "M48", DRILL_M_HEADER, 0 }, // beginning of a header + { "M95", DRILL_M_ENDHEADER, 0 }, // End of the header + { "METRIC", DRILL_METRICHEADER, 1 }, + { "INCH", DRILL_IMPERIALHEADER, 1 }, + { "M71", DRILL_M_METRIC, 1 }, + { "M72", DRILL_M_IMPERIAL, 1 }, + { "M25", DRILL_M_BEGINPATTERN, 0 }, // Beginning of Pattern + { "M01", DRILL_M_ENDPATTERN, 0 }, // End of Pattern + { "M97", DRILL_M_CANNEDTEXT, -1 }, + { "M98", DRILL_M_CANNEDTEXT, -1 }, + { "DETECT", DRILL_DETECT_BROKEN, -1 }, + { "ICI", DRILL_INCREMENTALHEADER, 1 }, + { "FMAT", DRILL_FMT, 1 }, // Use Format command + { "ATC", DRILL_AUTOMATIC_TOOL_CHANGE, 0 }, + { "TCST", DRILL_TOOL_CHANGE_STOP, 0 }, // Tool Change Stop + { "AFS", DRILL_AUTOMATIC_SPEED }, // Automatic Feeds and Speeds + { "VER", DRILL_AXIS_VERSION, 1 }, // Selection of X and Y Axis Version + { "R", DRILL_RESET_CMD, -1 }, // Reset commands + { "%", DRILL_REWIND_STOP, -1 }, // Rewind stop. End of the header + { "/", DRILL_SKIP, -1 }, // Clear Tool Linking. End of the header + // Keep this item after all commands starting by 'T': + { "T", DRILL_TOOL_INFORMATION, 0 }, // Tool Information + { "", DRILL_M_UNKNOWN, 0 } // last item in list +}; + +static EXCELLON_CMD excellon_G_CmdList[] = +{ + { "G90", DRILL_G_ABSOLUTE, 0 }, // Absolute Mode + { "G91", DRILL_G_INCREMENTAL, 0 }, // Incremental Input Mode + { "G90", DRILL_G_ZEROSET, 0 }, // Absolute Mode + { "G00", DRILL_G_ROUT, 1 }, // Route Mode + { "G05", DRILL_G_DRILL, 0 }, // Drill Mode + { "G85", DRILL_G_SLOT, 0 }, // Drill Mode slot (oval holes) + { "G01", DRILL_G_LINEARMOVE, 0 }, // Linear (Straight Line) Mode + { "G02", DRILL_G_CWMOVE, 0 }, // Circular CW Mode + { "G03", DRILL_G_CCWMOVE, 0 }, // Circular CCW Mode + { "G93", DRILL_G_ZERO_SET, 1 }, // Zero Set (XnnYmm and coordintes origin) + { "", DRILL_G_UNKNOWN, 0 }, // last item in list +}; + + +/* + * Read a EXCELLON file. + * Gerber classes are used because there is likeness between Gerber files + * and Excellon files + * DCode can easily store T code (tool size) as round (or oval) shape + * Drill commands are similar to flashed gerber items + * Routing commands are similar to Gerber polygons + * coordinates have the same format as Gerber, can be given in: + * decimal format (i.i. floating notation format) + * integer 2.4 format in imperial units, + * integer 3.2 or 3.3 format (metric units). + */ +bool GERBVIEW_FRAME::Read_EXCELLON_File( const wxString& aFullFileName ) +{ + wxString msg; + int layerId = getActiveLayer(); // current layer used in GerbView + EXCELLON_IMAGE* drill_Layer = (EXCELLON_IMAGE*) g_GERBER_List.GetGbrImage( layerId ); + + if( drill_Layer == NULL ) + { + drill_Layer = new EXCELLON_IMAGE( this, layerId ); + layerId = g_GERBER_List.AddGbrImage( drill_Layer, layerId ); + } + + if( layerId < 0 ) + { + DisplayError( this, _( "No room to load file" ) ); + return false; + } + + ClearMessageList(); + + /* Read the gerber file */ + FILE * file = wxFopen( aFullFileName, wxT( "rt" ) ); + if( file == NULL ) + { + msg.Printf( _( "File %s not found" ), GetChars( aFullFileName ) ); + DisplayError( this, msg ); + return false; + } + + wxString path = wxPathOnly( aFullFileName ); + + if( path != wxEmptyString ) + wxSetWorkingDirectory( path ); + + bool success = drill_Layer->Read_EXCELLON_File( file, aFullFileName ); + + // Display errors list + if( m_Messages.size() > 0 ) + { + HTML_MESSAGE_BOX dlg( this, _( "Files not found" ) ); + dlg.ListSet( m_Messages ); + dlg.ShowModal(); + } + return success; +} + +bool EXCELLON_IMAGE::Read_EXCELLON_File( FILE * aFile, + const wxString & aFullFileName ) +{ + /* Set the gerber scale: */ + ResetDefaultValues(); + + m_FileName = aFullFileName; + m_Current_File = aFile; + + LOCALE_IO toggleIo; + + // FILE_LINE_READER will close the file. + if( m_Current_File == NULL ) + { + wxMessageBox( wxT("NULL!"), m_FileName ); + return false; + } + + FILE_LINE_READER excellonReader( m_Current_File, m_FileName ); + while( true ) + { + if( excellonReader.ReadLine() == 0 ) + break; + + char* line = excellonReader.Line(); + char* text = StrPurge( line ); + + if( *text == ';' ) // comment: skip line + continue; + + if( m_State == EXCELLON_IMAGE::READ_HEADER_STATE ) + { + Execute_HEADER_Command( text ); + } + else + { + switch( *text ) + { + case 'M': + Execute_HEADER_Command( text ); + break; + + case 'G': /* Line type Gxx : command */ + Execute_EXCELLON_G_Command( text ); + break; + + case 'X': + case 'Y': // command like X12550Y19250 + Execute_Drill_Command(text); + break; + + case 'I': + case 'J': /* Auxiliary Move command */ + m_IJPos = ReadIJCoord( text ); + if( *text == '*' ) // command like X35142Y15945J504 + { + Execute_Drill_Command( text); + } + break; + + case 'T': // Tool command + Select_Tool( text ); + break; + + case '%': + break; + + default: + { + wxString msg; + msg.Printf( wxT( "Unexpected symbol <%c>" ), *text ); + if( GetParent() ) + GetParent()->ReportMessage( msg ); + } + break; + } // End switch + } + } + + // Add our file attribute, to identify the drill file + X2_ATTRIBUTE dummy; + char* text = (char*)file_attribute; + dummy.ParseAttribCmd( m_Current_File, NULL, 0, text ); + delete m_FileFunction; + m_FileFunction = new X2_ATTRIBUTE_FILEFUNCTION( dummy ); + + m_InUse = true; + + return true; +} + + +bool EXCELLON_IMAGE::Execute_HEADER_Command( char*& text ) +{ + EXCELLON_CMD* cmd = NULL; + int iprm; + double dprm; + D_CODE* dcode; + wxString msg; + + // Search command in list + EXCELLON_CMD* candidate; + + for( unsigned ii = 0; ; ii++ ) + { + candidate = &excellonHeaderCmdList[ii]; + int len = candidate->m_Name.size(); + if( len == 0 ) // End of list reached + break; + if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found. + { + cmd = candidate; + text += len; + break; + } + } + + if( !cmd ) + { + msg.Printf( wxT( "Unknown Excellon command <%s>" ), text ); + ReportMessage( msg ); + while( *text ) + text++; + + return false; + } + + // Execute command + // some do nothing + switch( cmd->m_Code ) + { + case DRILL_SKIP: + case DRILL_M_UNKNOWN: + break; + + case DRILL_M_END: + break; + + case DRILL_M_ENDREWIND: + break; + + case DRILL_M_MESSAGE: + break; + + case DRILL_M_LONGMESSAGE: + break; + + case DRILL_M_HEADER: + m_State = READ_HEADER_STATE; + break; + + case DRILL_M_ENDHEADER: + m_State = READ_PROGRAM_STATE; + break; + + case DRILL_REWIND_STOP: // End of header. No action in a viewer + m_State = READ_PROGRAM_STATE; + break; + + case DRILL_M_METRIC: + SelectUnits( true ); + break; + + case DRILL_METRICHEADER: // command like METRIC,TZ or METRIC,LZ + SelectUnits( true ); + if( *text != ',' ) + { + ReportMessage( _( "METRIC command has no parameter" ) ); + break; + } + text++; // skip separator + if( *text == 'T' ) + m_NoTrailingZeros = false; + else + m_NoTrailingZeros = true; + break; + + case DRILL_M_IMPERIAL: + SelectUnits( false ); + break; + + case DRILL_IMPERIALHEADER: // command like INCH,TZ or INCH,LZ + SelectUnits( false ); + if( *text != ',' ) + { + ReportMessage( _( "INCH command has no parameter" ) ); + break; + } + text++; // skip separator + if( *text == 'T' ) + m_NoTrailingZeros = false; + else + m_NoTrailingZeros = true; + break; + + case DRILL_M_BEGINPATTERN: + break; + + case DRILL_M_ENDPATTERN: + break; + + case DRILL_M_CANNEDTEXT: + break; + + case DRILL_M_TIPCHECK: + break; + + case DRILL_DETECT_BROKEN: + break; + + case DRILL_INCREMENTALHEADER: + if( *text != ',' ) + { + ReportMessage( _( "ICI command has no parameter" ) ); + break; + } + text++; // skip separator + // Parameter should be ON or OFF + if( strnicmp( text, "OFF", 3 ) == 0 ) + m_Relative = false; + else if( strnicmp( text, "ON", 2 ) == 0 ) + m_Relative = true; + else + ReportMessage( _( "ICI command has incorrect parameter" ) ); + break; + + case DRILL_TOOL_CHANGE_STOP: + break; + + case DRILL_AUTOMATIC_SPEED: + break; + + case DRILL_AXIS_VERSION: + break; + + case DRILL_RESET_CMD: + break; + + case DRILL_AUTOMATIC_TOOL_CHANGE: + break; + + case DRILL_FMT: + break; + + case DRILL_TOOL_INFORMATION: + + // Read a tool definition like T1C0.02: + // or T1F00S00C0.02 or T1C0.02F00S00 + // Read tool number: + iprm = ReadInt( text, false ); + + // Skip Feed rate and Spindle speed, if any here + while( *text && ( *text == 'F' || *text == 'S' ) ) + { + text++; + ReadInt( text, false ); + } + + // Read tool shape + if( *text != 'C' ) + ReportMessage( wxString:: Format( + _( "Tool definition <%c> not supported" ), *text ) ); + if( *text ) + text++; + + //read tool diameter: + dprm = ReadDouble( text, false ); + m_Has_DCode = true; + + // Initialize Dcode to handle this Tool + dcode = GetDCODE( iprm + FIRST_DCODE ); // Remember: dcodes are >= FIRST_DCODE + if( dcode == NULL ) + break; + // conv_scale = scaling factor from inch to Internal Unit + double conv_scale = IU_PER_MILS * 1000; + if( m_GerbMetric ) + conv_scale /= 25.4; + + dcode->m_Size.x = dcode->m_Size.y = KiROUND( dprm * conv_scale ); + dcode->m_Shape = APT_CIRCLE; + break; + } + + while( *text ) + text++; + + return true; +} + + +bool EXCELLON_IMAGE::Execute_Drill_Command( char*& text ) +{ + D_CODE* tool; + GERBER_DRAW_ITEM * gbritem; + while( true ) + { + switch( *text ) + { + case 'X': + ReadXYCoord( text ); + break; + case 'Y': + ReadXYCoord( text ); + break; + case 'G': // G85 is found here for oval holes + m_PreviousPos = m_CurrentPos; + Execute_EXCELLON_G_Command( text ); + break; + case 0: // E.O.L: execute command + tool = GetDCODE( m_Current_Tool, false ); + if( !tool ) + { + wxString msg; + msg.Printf( _( "Tool <%d> not defined" ), m_Current_Tool ); + ReportMessage( msg ); + return false; + } + gbritem = new GERBER_DRAW_ITEM( GetParent()->GetGerberLayout(), this ); + GetParent()->GetGerberLayout()->m_Drawings.Append( gbritem ); + if( m_SlotOn ) // Oblong hole + { + fillLineGBRITEM( gbritem, + tool->m_Num_Dcode, GetParent()->getActiveLayer(), + m_PreviousPos, m_CurrentPos, + tool->m_Size, false ); + // the hole is made: reset the slot on command (G85) + // (it is needed for each oblong hole) + m_SlotOn = false; + } + else + { + fillFlashedGBRITEM( gbritem, tool->m_Shape, + tool->m_Num_Dcode, GetParent()->getActiveLayer(), + m_CurrentPos, + tool->m_Size, false ); + } + StepAndRepeatItem( *gbritem ); + m_PreviousPos = m_CurrentPos; + return true; + break; + + default: + text++; + break; + } + } + + return true; +} + + +bool EXCELLON_IMAGE::Select_Tool( char*& text ) +{ + int tool_id = TCodeNumber( text ); + + if( tool_id >= 0 ) + { + tool_id += FIRST_DCODE; // Remember: dcodes are >= FIRST_DCODE + if( tool_id > (TOOLS_MAX_COUNT - 1) ) + tool_id = TOOLS_MAX_COUNT - 1; + m_Current_Tool = tool_id; + D_CODE* pt_Dcode = GetDCODE( tool_id , false ); + if( pt_Dcode ) + pt_Dcode->m_InUse = true; + } + while( *text ) + text++; + + return tool_id >= 0; +} + + +bool EXCELLON_IMAGE::Execute_EXCELLON_G_Command( char*& text ) +{ + EXCELLON_CMD* cmd = NULL; + bool success = false; + int id = DRILL_G_UNKNOWN; + + // Search command in list + EXCELLON_CMD* candidate; + char * gcmd = text; // gcmd points the G command, for error messages. + + for( unsigned ii = 0; ; ii++ ) + { + candidate = &excellon_G_CmdList[ii]; + int len = candidate->m_Name.size(); + if( len == 0 ) // End of list reached + break; + if( candidate->m_Name.compare( 0, len, text, len ) == 0 ) // found. + { + cmd = candidate; + text += len; + success = true; + id = cmd->m_Code; + break; + } + } + + switch( id ) + { + case DRILL_G_ZERO_SET: + ReadXYCoord( text ); + m_Offset = m_CurrentPos; + break; + + case DRILL_G_ROUT: + m_SlotOn = false; + m_PolygonFillMode = true; + break; + + case DRILL_G_DRILL: + m_SlotOn = false; + m_PolygonFillMode = false; + break; + + case DRILL_G_SLOT: + m_SlotOn = true; + break; + + case DRILL_G_LINEARMOVE: + m_Iterpolation = GERB_INTERPOL_LINEAR_1X; + break; + + case DRILL_G_CWMOVE: + m_Iterpolation = GERB_INTERPOL_ARC_NEG; + break; + + case DRILL_G_CCWMOVE: + m_Iterpolation = GERB_INTERPOL_ARC_POS; + break; + + case DRILL_G_ABSOLUTE: + m_Relative = false; // false = absolute coord + break; + + case DRILL_G_INCREMENTAL: + m_Relative = true; // true = relative coord + break; + + case DRILL_G_UNKNOWN: + default: + { + wxString msg; + msg.Printf( _( "Unknown Excellon G Code: <%s>" ), GetChars(FROM_UTF8(gcmd)) ); + ReportMessage( msg ); + while( *text ) + text++; + return false; + } + } + return success; +} + +void EXCELLON_IMAGE::SelectUnits( bool aMetric ) +{ + /* Coordinates are measured either in inch or metric (millimeters). + * Inch coordinates are in six digits (00.0000) with increments + * as small as 0.0001 (1/10,000). + * Metric coordinates can be measured in microns (thousandths of a millimeter) + * in one of the following three ways: + * Five digit 10 micron resolution (000.00) + * Six digit 10 micron resolution (0000.00) + * Six digit micron resolution (000.000) + */ + /* Inches: Default fmt = 2.4 for X and Y axis: 6 digits with 0.0001 resolution + * metric: Default fmt = 3.3 for X and Y axis: 6 digits, 1 micron resolution + */ + if( aMetric ) + { + m_GerbMetric = true; + // number of digits in mantissa + m_FmtScale.x = m_FmtScale.y = fmtMantissaMM; + // number of digits (mantissa+interger) + m_FmtLen.x = m_FmtLen.y = fmtIntegerMM+fmtMantissaMM; + } + else + { + m_GerbMetric = false; + m_FmtScale.x = m_FmtScale.y = fmtMantissaInch; + m_FmtLen.x = m_FmtLen.y = fmtIntegerInch+fmtMantissaInch; + } +} |