diff --git a/pcbnew/exporters/export_d356.cpp b/pcbnew/exporters/export_d356.cpp index 4103eac7f9..5a3493a39b 100644 --- a/pcbnew/exporters/export_d356.cpp +++ b/pcbnew/exporters/export_d356.cpp @@ -44,28 +44,9 @@ #include #include #include // for KiROUND +#include + -/* Structure for holding the D-356 record fields. - * Useful because 356A (when implemented) must be sorted before outputting it */ -struct D356_RECORD -{ - bool smd; - bool hole; - wxString netname; - wxString refdes; - wxString pin; - bool midpoint; - int drill; - bool mechanical; - int access; // Access 0 is 'both sides' - int soldermask; - // All these in PCB units, will be output in decimils - int x_location; - int y_location; - int x_size; - int y_size; - int rotation; -}; // Compute the access code for a pad. Returns -1 if there is no copper static int compute_pad_access_code( BOARD *aPcb, LSET aLayerMask ) @@ -280,32 +261,32 @@ static const wxString intern_new_d356_netname( const wxString &aNetname, } /* Write all the accumuled data to the file in D356 format */ -static void write_D356_records( std::vector &aRecords, - FILE *fout ) +void IPC356D_WRITER::write_D356_records( std::vector &aRecords, FILE* aFile ) { // Sanified and shorted network names and set of short names std::map d356_net_map; std::set d356_net_set; - for (unsigned i = 0; i < aRecords.size(); i++) + for( unsigned i = 0; i < aRecords.size(); i++ ) { D356_RECORD &rk = aRecords[i]; // Try to sanify the network name (there are limits on this), if // not already done. Also 'empty' net are marked as N/C, as // specified. - wxString d356_net( wxT("N/C") ); + wxString d356_net( wxT( "N/C" ) ); + if( !rk.netname.empty() ) { d356_net = d356_net_map[rk.netname]; if( d356_net.empty() ) - d356_net = intern_new_d356_netname( rk.netname, d356_net_map, - d356_net_set ); + d356_net = intern_new_d356_netname( rk.netname, d356_net_map, d356_net_set ); } // Choose the best record type int rktype; + if( rk.smd ) rktype = 327; else @@ -317,7 +298,7 @@ static void write_D356_records( std::vector &aRecords, } // Operation code, signal and component - fprintf( fout, "%03d%-14.14s %-6.6s%c%-4.4s%c", + fprintf( aFile, "%03d%-14.14s %-6.6s%c%-4.4s%c", rktype, TO_UTF8(d356_net), TO_UTF8(rk.refdes), rk.pin.empty()?' ':'-', @@ -327,15 +308,15 @@ static void write_D356_records( std::vector &aRecords, // Hole definition if( rk.hole ) { - fprintf( fout, "D%04d%c", + fprintf( aFile, "D%04d%c", iu_to_d356( rk.drill, 9999 ), rk.mechanical ? 'U':'P' ); } else - fprintf( fout, " " ); + fprintf( aFile, " " ); // Test point access - fprintf( fout, "A%02dX%+07dY%+07dX%04dY%04dR%03d", + fprintf( aFile, "A%02dX%+07dY%+07dX%04dY%04dR%03d", rk.access, iu_to_d356( rk.x_location, 999999 ), iu_to_d356( rk.y_location, 999999 ), @@ -344,16 +325,47 @@ static void write_D356_records( std::vector &aRecords, rk.rotation ); // Soldermask - fprintf( fout, "S%d\n", rk.soldermask ); + fprintf( aFile, "S%d\n", rk.soldermask ); } } +void IPC356D_WRITER::Write( const wxString& aFilename ) +{ + FILE* file = nullptr; + LOCALE_IO toggle; // Switch the locale to standard C + + if( ( file = wxFopen( aFilename, wxT( "wt" ) ) ) == nullptr ) + { + wxString details; + details.Printf( "The file %s could not be opened for writing", aFilename ); + DisplayErrorMessage( m_parent, "Could not write IPC-356D file!", details ); + return; + } + + // This will contain everything needed for the 356 file + std::vector d356_records; + + build_via_testpoints( m_pcb, d356_records ); + + build_pad_testpoints( m_pcb, d356_records ); + + // Code 00 AFAIK is ASCII, CUST 0 is decimils/degrees + // CUST 1 would be metric but gerbtool simply ignores it! + fprintf( file, "P CODE 00\n" ); + fprintf( file, "P UNITS CUST 0\n" ); + fprintf( file, "P arrayDim N\n" ); + write_D356_records( d356_records, file ); + fprintf( file, "999\n" ); + + fclose( file ); +} + + void PCB_EDIT_FRAME::GenD356File( wxCommandEvent& aEvent ) { wxFileName fn = GetBoard()->GetFileName(); wxString msg, ext, wildcard; - FILE* file; ext = IpcD356FileExtension; wildcard = IpcD356FileWildcard(); @@ -368,29 +380,7 @@ void PCB_EDIT_FRAME::GenD356File( wxCommandEvent& aEvent ) if( dlg.ShowModal() == wxID_CANCEL ) return; - if( ( file = wxFopen( dlg.GetPath(), wxT( "wt" ) ) ) == NULL ) - { - msg = _( "Unable to create " ) + dlg.GetPath(); - DisplayError( this, msg ); return; - } + IPC356D_WRITER writer( GetBoard(), this ); - LOCALE_IO toggle; // Switch the locale to standard C - - // This will contain everything needed for the 356 file - std::vector d356_records; - BOARD* pcb = GetBoard(); - - build_via_testpoints( pcb, d356_records ); - - build_pad_testpoints( pcb, d356_records ); - - // Code 00 AFAIK is ASCII, CUST 0 is decimils/degrees - // CUST 1 would be metric but gerbtool simply ignores it! - fprintf( file, "P CODE 00\n" ); - fprintf( file, "P UNITS CUST 0\n" ); - fprintf( file, "P arrayDim N\n" ); - write_D356_records( d356_records, file ); - fprintf( file, "999\n" ); - - fclose( file ); + writer.Write( dlg.GetPath() ); } diff --git a/pcbnew/exporters/export_d356.h b/pcbnew/exporters/export_d356.h new file mode 100644 index 0000000000..dd04f0c95d --- /dev/null +++ b/pcbnew/exporters/export_d356.h @@ -0,0 +1,80 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 Jon Evans + * Copyright (C) 2020 KiCad Developers, see AUTHORS.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 3 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, see . + */ + +#include + +class BOARD; +class wxWindow; + + +/* Structure for holding the D-356 record fields. + * Useful because 356A (when implemented) must be sorted before outputting it */ +struct D356_RECORD +{ + bool smd; + bool hole; + wxString netname; + wxString refdes; + wxString pin; + bool midpoint; + int drill; + bool mechanical; + int access; // Access 0 is 'both sides' + int soldermask; + // All these in PCB units, will be output in decimils + int x_location; + int y_location; + int x_size; + int y_size; + int rotation; +}; + + +/** + * Wrapper to expose an API for writing IPC-D356 files + */ +class IPC356D_WRITER +{ +public: + /** + * Constructs an IPC-356D file writer + * @param aPcb is the board to extract a netlist from + * @param aParent will be used as the parent for any warning dialogs + */ + IPC356D_WRITER( BOARD* aPcb, wxWindow* aParent = nullptr ) : + m_pcb( aPcb ), m_parent( aParent ) + {} + + virtual ~IPC356D_WRITER() {} + + /** + * Generates and writes the netlist to a given path + * @param aFilename is the full path and name of the output file + */ + void Write( const wxString& aFilename ); + +private: + BOARD* m_pcb; + + wxWindow* m_parent; + + /// Writes a list of records to the given output stream + void write_D356_records( std::vector &aRecords, FILE* aFile ); +}; diff --git a/pcbnew/swig/pcbnew.i b/pcbnew/swig/pcbnew.i index 1b099897ff..ee6de535e4 100644 --- a/pcbnew/swig/pcbnew.i +++ b/pcbnew/swig/pcbnew.i @@ -70,6 +70,7 @@ class BASE_SET {}; #include #include +#include #include #include #include @@ -110,6 +111,7 @@ HANDLE_EXCEPTIONS(PLUGIN::FootprintDelete) %include %include %include +%include %include %include %include