ADDED: Allow exporting IPC-356D netlists from Python

This commit is contained in:
Jon Evans 2020-02-04 11:30:21 +01:00 committed by Wayne Stambaugh
parent 0049185d06
commit ce4542cced
3 changed files with 129 additions and 57 deletions

View File

@ -44,28 +44,9 @@
#include <vector>
#include <cctype>
#include <math/util.h> // for KiROUND
#include <export_d356.h>
/* 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 <D356_RECORD> &aRecords,
FILE *fout )
void IPC356D_WRITER::write_D356_records( std::vector <D356_RECORD> &aRecords, FILE* aFile )
{
// Sanified and shorted network names and set of short names
std::map<wxString, wxString> d356_net_map;
std::set<wxString> 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 <D356_RECORD> &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 <D356_RECORD> &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 <D356_RECORD> &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_RECORD> 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_RECORD> 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() );
}

View File

@ -0,0 +1,80 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 Jon Evans <jon@craftyjon.com>
* 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 <http://www.gnu.org/licenses/>.
*/
#include <wx/string.h>
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<D356_RECORD> &aRecords, FILE* aFile );
};

View File

@ -70,6 +70,7 @@ class BASE_SET {};
#include <plotcontroller.h>
#include <pcb_plot_params.h>
#include <exporters/export_d356.h>
#include <exporters/gendrill_file_writer_base.h>
#include <exporters/gendrill_Excellon_writer.h>
#include <exporters/gendrill_gerber_writer.h>
@ -110,6 +111,7 @@ HANDLE_EXCEPTIONS(PLUGIN::FootprintDelete)
%include <plotcontroller.h>
%include <pcb_plot_params.h>
%include <plotter.h>
%include <exporters/export_d356.h>
%include <exporters/gendrill_file_writer_base.h>
%include <exporters/gendrill_Excellon_writer.h>
%include <exporters/gendrill_gerber_writer.h>