1184 lines
36 KiB
C++
1184 lines
36 KiB
C++
/**
|
|
* @file pcbnew/netlist.cpp
|
|
*/
|
|
|
|
/*
|
|
* Functions to read a netlist. They are:
|
|
* - Load new footprints and initialize net info
|
|
* - Test for missing or extra footprints
|
|
* - Recalculate full connectivity info
|
|
*
|
|
* Important remark:
|
|
* When reading a netlist Pcbnew must identify existing footprints (link
|
|
* between existing footprints an components in netlist)
|
|
* This identification can be from 2 fields:
|
|
* - The reference (U2, R5 ..): this is the normal mode
|
|
* - The Time Stamp (Signature Temporarily), useful after a full schematic
|
|
* reannotation because references can be changed for the same schematic.
|
|
* So when reading a netlist ReadPcbNetlist() can use references or time stamps
|
|
* to identify footprints on board and the corresponding component in schematic.
|
|
* If we want to fully reannotate a schematic this sequence must be used
|
|
* 1 - SAVE your board !!!
|
|
* 2 - Create and read the netlist (to ensure all info is correct, mainly
|
|
* references and time stamp)
|
|
* 3 - Reannotate the schematic (references will be changed, but not time stamps )
|
|
* 4 - Recreate and read the new netlist using the Time Stamp identification
|
|
* (that reinit the new references)
|
|
*/
|
|
|
|
#include "algorithm"
|
|
|
|
#include "fctsys.h"
|
|
#include "class_drawpanel.h"
|
|
#include "confirm.h"
|
|
#include "kicad_string.h"
|
|
#include "gestfich.h"
|
|
#include "wxPcbStruct.h"
|
|
#include "richio.h"
|
|
#include "dialog_helpers.h"
|
|
#include "macros.h"
|
|
|
|
#include "class_board.h"
|
|
#include "class_module.h"
|
|
|
|
#include "pcbnew.h"
|
|
#include "dialog_netlist.h"
|
|
|
|
|
|
// constants used by ReadNetlistModuleDescr():
|
|
#define TESTONLY true
|
|
#define READMODULE false
|
|
|
|
|
|
/*
|
|
* Helper class, to store new footprints info found in netlist.
|
|
* New footprints are footprints relative to new components found in netlist
|
|
*/
|
|
class MODULE_INFO
|
|
{
|
|
public:
|
|
wxString m_LibName;
|
|
wxString m_CmpName;
|
|
wxString m_TimeStampPath;
|
|
|
|
public: MODULE_INFO( const wxString& libname,
|
|
const wxString& cmpname,
|
|
const wxString& timestamp_path )
|
|
{
|
|
m_LibName = libname;
|
|
m_CmpName = cmpname;
|
|
m_TimeStampPath = timestamp_path;
|
|
}
|
|
|
|
~MODULE_INFO() { };
|
|
};
|
|
|
|
|
|
/*
|
|
* Helper class, to read a netlist.
|
|
*/
|
|
class NETLIST_READER
|
|
{
|
|
private:
|
|
PCB_EDIT_FRAME* m_pcbframe; // the main Pcbnew frame
|
|
wxTextCtrl* m_messageWindow; // a textctrl to show messages (can be NULL)
|
|
wxString m_netlistFullName; // The full netlist filename
|
|
wxString m_cmplistFullName; // The full component/footprint association filename
|
|
MODULE* m_currModule; // The footprint currently being read in netlist
|
|
std::vector <MODULE_INFO*> m_newModulesList; // The list of new footprints,
|
|
// found in netlist, but not on board
|
|
// (must be loaded from libraries)
|
|
bool m_useCmpFile; // true to use .cmp files as component/footprint file link
|
|
// false to use netlist only to know component/footprint link
|
|
|
|
public:
|
|
bool m_UseTimeStamp; // Set to true to identify footprints by time stamp
|
|
// false to use schematic reference
|
|
bool m_ChangeFootprints; // Set to true to change existing footprints to new ones
|
|
// when netlist gives a different footprint name
|
|
|
|
public: NETLIST_READER( PCB_EDIT_FRAME* aFrame, wxTextCtrl* aMessageWindow = NULL )
|
|
{
|
|
m_pcbframe = aFrame;
|
|
m_messageWindow = aMessageWindow;
|
|
m_UseTimeStamp = false;
|
|
m_ChangeFootprints = false;
|
|
m_useCmpFile = true;
|
|
}
|
|
|
|
~NETLIST_READER()
|
|
{
|
|
// Free new modules list:
|
|
for( unsigned ii = 0; ii < m_newModulesList.size(); ii++ )
|
|
delete m_newModulesList[ii];
|
|
|
|
m_newModulesList.clear();
|
|
}
|
|
|
|
/**
|
|
* Function ReadNetList
|
|
* The main function to read a netlist, and update the board
|
|
* @param aFile = the already opened file (will be closed by ReadNetList)
|
|
* @param aNetlistFileName = the netlist full file name (*.net file)
|
|
* @param aCmplistFileName = the auxiliary component full file name (*.cmp file)
|
|
* If the aCmplistFileName file is not given or not found,
|
|
* the netlist is used to know the component/footprint link.
|
|
*/
|
|
bool ReadNetList( FILE* aFile, const wxString& aNetlistFileName,
|
|
const wxString& aCmplistFileName );
|
|
|
|
/**
|
|
* Function BuildComponentsListFromNetlist
|
|
* Fill aBufName with component references read from the netlist.
|
|
* @param aNetlistFilename = netlist full file name
|
|
* @param aBufName = wxArrayString to fill with component references
|
|
* @return the component count, or -1 if netlist file cannot opened
|
|
*/
|
|
int BuildComponentsListFromNetlist( const wxString& aNetlistFilename,
|
|
wxArrayString& aBufName );
|
|
|
|
/**
|
|
* function RemoveExtraFootprints
|
|
* Remove (delete) not locked footprints found on board, but not in netlist
|
|
* @param aNetlistFileName = the netlist full file name (*.net file)
|
|
*/
|
|
void RemoveExtraFootprints( const wxString& aNetlistFileName );
|
|
|
|
private:
|
|
|
|
/**
|
|
* Function SetPadNetName
|
|
* Update a pad netname using the current footprint
|
|
* from the netlist (line format: ( \<pad number\> \<net name\> ) )
|
|
* @param aText = current line read from netlist
|
|
*/
|
|
bool SetPadNetName( char* aText );
|
|
|
|
/**
|
|
* Function ReadNetlistModuleDescr
|
|
* Read the full description of a footprint, from the netlist
|
|
* and update the corresponding module.
|
|
* @param aTstOnly bool to switch between 2 modes:
|
|
* aTstOnly = false:
|
|
* if the module does not exist, it is added to m_newModulesList
|
|
* aTstOnly = true:
|
|
* if the module does not exist, it is loaded and added to the board module list
|
|
* @param aText contains the first line of description
|
|
* This function uses m_useFichCmp as a flag to know the footprint name:
|
|
* If true: component file *.cmp is used
|
|
* If false: the netlist only is used
|
|
* This flag is reset to false if the .cmp file is not found
|
|
*/
|
|
MODULE* ReadNetlistModuleDescr( char* aText, bool aTstOnly );
|
|
|
|
/**
|
|
* Function loadNewModules
|
|
* Load from libraries new modules found in netlist and add them to the current Board.
|
|
* @return false if a footprint is not found, true if all footprints are loaded
|
|
*/
|
|
bool loadNewModules();
|
|
|
|
/**
|
|
* function readModuleComponentLinkfile
|
|
* read the *.cmp file ( filename in m_cmplistFullName )
|
|
* giving the equivalence between footprint names and components
|
|
* to find the footprint name corresponding to aCmpIdent
|
|
* @return true and the module name in aFootprintName, false if not found
|
|
*
|
|
* @param aCmpIdent = component identification: schematic reference or time stamp
|
|
* @param aFootprintName the footprint name corresponding to the component identification
|
|
*/
|
|
bool readModuleComponentLinkfile( const wxString* aCmpIdent, wxString& aFootprintName );
|
|
};
|
|
|
|
|
|
/**
|
|
* Function OpenNetlistFile
|
|
* used to open a netlist file
|
|
*/
|
|
static FILE* OpenNetlistFile( const wxString& aFullFileName )
|
|
{
|
|
if( aFullFileName.IsEmpty() )
|
|
return false; /* No filename: exit */
|
|
|
|
FILE* file = wxFopen( aFullFileName, wxT( "rt" ) );
|
|
|
|
if( file == NULL )
|
|
{
|
|
wxString msg;
|
|
msg.Printf( _( "Netlist file %s not found" ), GetChars( aFullFileName ) );
|
|
wxMessageBox( msg );
|
|
}
|
|
|
|
return file;
|
|
}
|
|
|
|
|
|
/* Function to sort the footprint list.
|
|
* the given list is sorted by name
|
|
*/
|
|
static bool SortByLibName( MODULE_INFO* ref, MODULE_INFO* cmp )
|
|
{
|
|
int ii = ref->m_LibName.CmpNoCase( cmp->m_LibName );
|
|
|
|
return ii > 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Function ReadPcbNetlist
|
|
* Update footprints (load missing footprints and delete on request extra
|
|
* footprints)
|
|
* Update connectivity info ( Net Name list )
|
|
* Update Reference, value and "TIME STAMP"
|
|
* @param aNetlistFullFilename = netlist file name (*.net)
|
|
* @param aCmpFullFileName = cmp/footprint list file name (*.cmp) if not found,
|
|
* @param aMessageWindow = a wxTextCtrl to print messages (can be NULL).
|
|
* @param aChangeFootprint = true to change existing footprints
|
|
* when the netlist gives a different footprint.
|
|
* false to keep existing footprints
|
|
* @param aDeleteBadTracks - true to erase erroneous tracks after updating connectivity info.
|
|
* @param aDeleteExtraFootprints - true to remove unlocked footprints found on board but not
|
|
* in netlist.
|
|
* @param aSelect_By_Timestamp - true to use schematic timestamps instead of schematic references
|
|
* to identify footprints on board
|
|
* (Must be used after a full reannotation in schematic).
|
|
* @return true if Ok
|
|
*/
|
|
bool PCB_EDIT_FRAME::ReadPcbNetlist( const wxString& aNetlistFullFilename,
|
|
const wxString& aCmpFullFileName,
|
|
wxTextCtrl* aMessageWindow,
|
|
bool aChangeFootprint,
|
|
bool aDeleteBadTracks,
|
|
bool aDeleteExtraFootprints,
|
|
bool aSelect_By_Timestamp )
|
|
{
|
|
FILE* netfile = OpenNetlistFile( aNetlistFullFilename );
|
|
|
|
if( !netfile )
|
|
return false;
|
|
|
|
SetLastNetListRead( aNetlistFullFilename );
|
|
|
|
if( aMessageWindow )
|
|
{
|
|
wxString msg;
|
|
msg.Printf( _( "Reading Netlist \"%s\"" ), GetChars( aNetlistFullFilename ) );
|
|
aMessageWindow->AppendText( msg + wxT( "\n" ) );
|
|
|
|
if( ! aCmpFullFileName.IsEmpty() )
|
|
{
|
|
msg.Printf( _( "Using component/footprint link file \"%s\"" ),
|
|
GetChars( aCmpFullFileName ) );
|
|
aMessageWindow->AppendText( msg + wxT( "\n" ) );
|
|
}
|
|
}
|
|
|
|
// Clear undo and redo lists to avoid inconsistencies between lists
|
|
GetScreen()->ClearUndoRedoList();
|
|
|
|
OnModify();
|
|
|
|
// Clear flags and pointers to avoid inconsistencies
|
|
GetBoard()->m_Status_Pcb = 0;
|
|
SetCurItem( NULL );
|
|
|
|
wxBusyCursor dummy; // Shows an hourglass while calculating
|
|
|
|
NETLIST_READER netList_Reader( this, aMessageWindow );
|
|
netList_Reader.m_UseTimeStamp = aSelect_By_Timestamp;
|
|
netList_Reader.m_ChangeFootprints = aChangeFootprint;
|
|
netList_Reader.ReadNetList( netfile, aNetlistFullFilename, aCmpFullFileName );
|
|
|
|
// Delete footprints not found in netlist:
|
|
if( aDeleteExtraFootprints )
|
|
{
|
|
netList_Reader.RemoveExtraFootprints( aNetlistFullFilename );
|
|
}
|
|
|
|
// Rebuild the board connectivity:
|
|
Compile_Ratsnest( NULL, true );
|
|
|
|
if( aDeleteBadTracks && GetBoard()->m_Track )
|
|
{
|
|
// Remove erroneous tracks
|
|
RemoveMisConnectedTracks( NULL );
|
|
Compile_Ratsnest( NULL, true );
|
|
}
|
|
|
|
GetBoard()->DisplayInfo( this );
|
|
DrawPanel->Refresh();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/* function RemoveExtraFootprints
|
|
* Remove (delete) not locked footprints found on board, but not in netlist
|
|
*/
|
|
void NETLIST_READER::RemoveExtraFootprints( const wxString& aNetlistFileName )
|
|
{
|
|
wxArrayString componentsInNetlist;
|
|
|
|
// Build list of modules in the netlist
|
|
int modulesCount = BuildComponentsListFromNetlist( aNetlistFileName, componentsInNetlist );
|
|
|
|
if( modulesCount == 0 )
|
|
return;
|
|
|
|
MODULE* nextModule;
|
|
MODULE* module = m_pcbframe->GetBoard()->m_Modules;
|
|
bool ask_user = true;
|
|
|
|
for( ; module != NULL; module = nextModule )
|
|
{
|
|
int ii;
|
|
nextModule = module->Next();
|
|
|
|
if( module->m_ModuleStatus & MODULE_is_LOCKED )
|
|
continue;
|
|
|
|
for( ii = 0; ii < modulesCount; ii++ )
|
|
{
|
|
if( module->m_Reference->m_Text.CmpNoCase( componentsInNetlist[ii] ) == 0 )
|
|
break; /* Module is found in net list. */
|
|
}
|
|
|
|
if( ii == modulesCount ) // Module not found in netlist.
|
|
{
|
|
if( ask_user )
|
|
{
|
|
ask_user = false;
|
|
|
|
if( !IsOK( NULL,
|
|
_( "Ok to delete not locked footprints not found in netlist?" ) ) )
|
|
break;
|
|
}
|
|
|
|
module->DeleteStructure();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function ReadNetlist
|
|
* Update footprints (load missing footprints and delete on request extra
|
|
* footprints)
|
|
* Update References, values, "TIME STAMP" and connectivity data
|
|
* return true if Ok
|
|
*
|
|
* the format of the netlist is something like:
|
|
* # EESchema Netlist Version 1.0 generee le 18/5/2005-12:30:22
|
|
* (
|
|
* ( 40C08647 $noname R20 4,7K {Lib=R}
|
|
* ( 1 VCC )
|
|
* ( 2 MODB_1 )
|
|
* )
|
|
* ( 40C0863F $noname R18 4,7_k {Lib=R}
|
|
* ( 1 VCC )
|
|
* ( 2 MODA_1 )
|
|
* )
|
|
* }
|
|
* #End
|
|
*/
|
|
bool NETLIST_READER::ReadNetList( FILE* aFile,
|
|
const wxString& aNetlistFileName,
|
|
const wxString& aCmplistFileName )
|
|
{
|
|
int state = 0;
|
|
bool is_comment = false;
|
|
|
|
m_netlistFullName = aNetlistFileName;
|
|
m_cmplistFullName = aCmplistFileName;
|
|
|
|
m_useCmpFile = true;
|
|
|
|
/* First, read the netlist: Build the list of footprints to load (new
|
|
* footprints)
|
|
*/
|
|
|
|
// netlineReader dtor will close aFile
|
|
FILE_LINE_READER netlineReader( aFile, m_netlistFullName );
|
|
|
|
while( netlineReader.ReadLine() )
|
|
{
|
|
char* line = StrPurge( netlineReader.Line() );
|
|
|
|
if( is_comment ) /* Comments in progress */
|
|
{
|
|
// Test for end of the current comment
|
|
if( ( line = strchr( line, '}' ) ) == NULL )
|
|
continue;
|
|
|
|
is_comment = false;
|
|
}
|
|
if( *line == '{' ) /* Start Comment */
|
|
{
|
|
is_comment = true;
|
|
|
|
if( ( line = strchr( line, '}' ) ) == NULL )
|
|
continue;
|
|
}
|
|
|
|
if( *line == '(' )
|
|
state++;
|
|
|
|
if( *line == ')' )
|
|
state--;
|
|
|
|
if( state == 2 )
|
|
{
|
|
ReadNetlistModuleDescr( line, TESTONLY );
|
|
continue;
|
|
}
|
|
|
|
if( state >= 3 ) // First pass: pad descriptions are not read here.
|
|
{
|
|
state--;
|
|
}
|
|
}
|
|
|
|
/* Load new footprints */
|
|
bool success = loadNewModules();
|
|
|
|
if( ! success )
|
|
wxMessageBox( _("Some footprints are not found in libraries") );
|
|
|
|
/* Second read , All footprints are on board.
|
|
* One must update the schematic info (pad netnames)
|
|
*/
|
|
netlineReader.Rewind();
|
|
m_currModule = NULL;
|
|
state = 0;
|
|
is_comment = false;
|
|
|
|
while( netlineReader.ReadLine() )
|
|
{
|
|
char* line = StrPurge( netlineReader.Line() );
|
|
|
|
if( is_comment ) // we are reading a comment
|
|
{
|
|
// Test for end of the current comment
|
|
if( ( line = strchr( line, '}' ) ) == NULL )
|
|
continue;
|
|
is_comment = false;
|
|
}
|
|
|
|
if( *line == '{' ) // this is the beginning of a comment
|
|
{
|
|
is_comment = true;
|
|
|
|
if( ( line = strchr( line, '}' ) ) == NULL )
|
|
continue;
|
|
}
|
|
|
|
if( *line == '(' )
|
|
state++;
|
|
|
|
if( *line == ')' )
|
|
state--;
|
|
|
|
if( state == 2 )
|
|
{
|
|
m_currModule = ReadNetlistModuleDescr( line, READMODULE );
|
|
|
|
if( m_currModule == NULL ) // the module could not be created (perhaps
|
|
{
|
|
// footprint not found in library)
|
|
continue;
|
|
}
|
|
else /* clear pads netnames */
|
|
{
|
|
D_PAD* pad = m_currModule->m_Pads;
|
|
|
|
for( ; pad != NULL; pad = pad->Next() )
|
|
{
|
|
pad->SetNetname( wxEmptyString );
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if( state >= 3 )
|
|
{
|
|
if( m_currModule )
|
|
SetPadNetName( line );
|
|
state--;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/* Function ReadNetlistModuleDescr
|
|
* Read the full description of a footprint, from the netlist
|
|
* and update the corresponding module.
|
|
* param aTstOnly bool to switch between 2 modes:
|
|
* If aTstOnly == false:
|
|
* if the module does not exist, it is added to m_newModulesList
|
|
* If aTstOnly = true:
|
|
* if the module does not exist, it is loaded and added to the board module list
|
|
* param aText contains the first line of description
|
|
* This function uses m_useFichCmp as a flag to know the footprint name:
|
|
* If true: component file *.cmp is used
|
|
* If false: the netlist only is used
|
|
* This flag is reset to false if the .cmp file is not found
|
|
* Analyze lines like:
|
|
* ( /40C08647 $noname R20 4.7K {Lib=R}
|
|
* (1 VCC)
|
|
* (2 MODB_1)
|
|
* )
|
|
*/
|
|
MODULE* NETLIST_READER::ReadNetlistModuleDescr( char* aText, bool aTstOnly )
|
|
{
|
|
char* text;
|
|
wxString timeStampPath; // the full time stamp read from netlist
|
|
wxString textFootprintName; // the footprint name read from netlist
|
|
wxString textValue; // the component value read from netlist
|
|
wxString textCmpReference; // the component schematic reference read from netlist
|
|
wxString cmpFootprintName; // the footprint name read from the *.cmp file
|
|
// giving the equivalence between footprint names and components
|
|
bool error = false;
|
|
char line[1024];
|
|
|
|
strcpy( line, aText );
|
|
|
|
textValue = wxT( "~" );
|
|
|
|
// Read descr line like /40C08647 $noname R20 4.7K {Lib=R}
|
|
|
|
// Read time stamp (first word)
|
|
if( ( text = strtok( line, " ()\t\n" ) ) == NULL )
|
|
error = true;
|
|
else
|
|
timeStampPath = FROM_UTF8( text );
|
|
|
|
// Read footprint name (second word)
|
|
if( ( text = strtok( NULL, " ()\t\n" ) ) == NULL )
|
|
error = true;
|
|
else
|
|
textFootprintName = FROM_UTF8( text );
|
|
|
|
// Read schematic reference (third word)
|
|
if( ( text = strtok( NULL, " ()\t\n" ) ) == NULL )
|
|
error = true;
|
|
else
|
|
textCmpReference = FROM_UTF8( text );
|
|
|
|
// Read schematic value (forth word)
|
|
if( ( text = strtok( NULL, " ()\t\n" ) ) == NULL )
|
|
error = true;
|
|
else
|
|
textValue = FROM_UTF8( text );
|
|
|
|
if( error )
|
|
return NULL;
|
|
|
|
/* Test if module is already loaded. */
|
|
wxString * identMod = &textCmpReference;
|
|
|
|
if( m_UseTimeStamp )
|
|
identMod = &timeStampPath;
|
|
|
|
MODULE* module = m_pcbframe->GetBoard()->m_Modules;
|
|
MODULE* nextModule;
|
|
bool found = false;
|
|
|
|
for( ; module != NULL; module = nextModule )
|
|
{
|
|
nextModule = module->Next();
|
|
|
|
if( m_UseTimeStamp ) /* identification by time stamp */
|
|
{
|
|
if( timeStampPath.CmpNoCase( module->m_Path ) == 0 )
|
|
found = true;
|
|
}
|
|
else /* identification by Reference */
|
|
{
|
|
if( textCmpReference.CmpNoCase( module->m_Reference->m_Text ) == 0 )
|
|
found = true;
|
|
}
|
|
|
|
if( found ) // The footprint corresponding to the component is already on board
|
|
{
|
|
// This footprint is already on board
|
|
// but if m_LibRef (existing footprint name) and the footprint name from netlist
|
|
// do not match, change this footprint on demand.
|
|
if( ! aTstOnly )
|
|
{
|
|
cmpFootprintName = textFootprintName; // Use footprint name from netlist
|
|
|
|
if( m_useCmpFile ) // Try to get footprint name from .cmp file
|
|
{
|
|
m_useCmpFile = readModuleComponentLinkfile( identMod, cmpFootprintName );
|
|
}
|
|
|
|
/* Module mismatch: current footprint and footprint specified in
|
|
* net list are different.
|
|
*/
|
|
if( module->m_LibRef.CmpNoCase( cmpFootprintName ) != 0 )
|
|
{
|
|
if( m_ChangeFootprints ) // footprint exchange allowed.
|
|
{
|
|
MODULE* newModule = m_pcbframe->GetModuleLibrary( wxEmptyString,
|
|
cmpFootprintName,
|
|
true );
|
|
|
|
if( newModule )
|
|
{
|
|
// Change old module to the new module (and delete the old one)
|
|
m_pcbframe->Exchange_Module( module, newModule, NULL );
|
|
module = newModule;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wxString msg;
|
|
msg.Printf( _( "Component \"%s\": Mismatch! module is [%s] and netlist said [%s]\n" ),
|
|
GetChars( textCmpReference ),
|
|
GetChars( module->m_LibRef ),
|
|
GetChars( cmpFootprintName ) );
|
|
|
|
if( m_messageWindow )
|
|
m_messageWindow->AppendText( msg );
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( module == NULL ) // a new module must be loaded from libs
|
|
{
|
|
cmpFootprintName = textFootprintName; // Use footprint name from netlist
|
|
|
|
if( m_useCmpFile ) // Try to get footprint name from .cmp file
|
|
{
|
|
m_useCmpFile = readModuleComponentLinkfile( identMod, cmpFootprintName );
|
|
}
|
|
|
|
if( aTstOnly )
|
|
{
|
|
MODULE_INFO* newMod;
|
|
newMod = new MODULE_INFO( cmpFootprintName, textCmpReference, timeStampPath );
|
|
m_newModulesList.push_back( newMod );
|
|
}
|
|
else
|
|
{
|
|
if( m_messageWindow )
|
|
{
|
|
wxString msg;
|
|
msg.Printf( _( "Component [%s] not found" ), GetChars( textCmpReference ) );
|
|
m_messageWindow->AppendText( msg + wxT( "\n" ) );
|
|
}
|
|
}
|
|
|
|
return NULL; // The module could not be loaded.
|
|
}
|
|
|
|
// Update current module ( reference, value and "Time Stamp")
|
|
module->m_Reference->m_Text = textCmpReference;
|
|
module->m_Value->m_Text = textValue;
|
|
module->m_Path = timeStampPath;
|
|
|
|
return module;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function SetPadNetName
|
|
* Update a pad netname using the current footprint
|
|
* Line format: ( <pad number> <net name> )
|
|
* Param aText = current line read from netlist
|
|
*/
|
|
bool NETLIST_READER::SetPadNetName( char* aText )
|
|
{
|
|
D_PAD* pad;
|
|
char* TextPinName, * TextNetName;
|
|
bool found;
|
|
bool error = false;
|
|
char Line[256];
|
|
|
|
if( m_currModule == NULL )
|
|
return false;
|
|
|
|
strcpy( Line, aText );
|
|
|
|
if( ( TextPinName = strtok( Line, " ()\t\n" ) ) == NULL )
|
|
error = true;
|
|
|
|
if( ( TextNetName = strtok( NULL, " ()\t\n" ) ) == NULL )
|
|
error = true;
|
|
|
|
if( error )
|
|
return 0;
|
|
|
|
pad = m_currModule->m_Pads;
|
|
found = false;
|
|
|
|
for( ; pad != NULL; pad = pad->Next() )
|
|
{
|
|
if( strnicmp( TextPinName, pad->m_Padname, 4 ) == 0 )
|
|
{
|
|
found = true;
|
|
if( *TextNetName != '?' )
|
|
pad->SetNetname( FROM_UTF8( TextNetName ) );
|
|
else
|
|
pad->SetNetname( wxEmptyString );
|
|
}
|
|
}
|
|
|
|
if( !found )
|
|
{
|
|
if( m_messageWindow )
|
|
{
|
|
wxString msg;
|
|
wxString pin_name = FROM_UTF8( TextPinName );
|
|
msg.Printf( _( "Module [%s]: Pad [%s] not found" ),
|
|
GetChars( m_currModule->m_Reference->m_Text ),
|
|
GetChars( pin_name ) );
|
|
m_messageWindow->AppendText( msg + wxT( "\n" ) );
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
/**
|
|
* build and shows a list of existing modules on board
|
|
* The user can select a module from this list
|
|
* @return a pointer to the selected module or NULL
|
|
*/
|
|
MODULE* PCB_EDIT_FRAME::ListAndSelectModuleName( void )
|
|
{
|
|
MODULE* Module;
|
|
|
|
if( GetBoard()->m_Modules == NULL )
|
|
{
|
|
DisplayError( this, _( "No Modules" ) );
|
|
return 0;
|
|
}
|
|
|
|
wxArrayString listnames;
|
|
Module = (MODULE*) GetBoard()->m_Modules;
|
|
|
|
for( ; Module != NULL; Module = (MODULE*) Module->Next() )
|
|
listnames.Add( Module->m_Reference->m_Text );
|
|
|
|
EDA_LIST_DIALOG dlg( this, _( "Components" ), listnames, wxEmptyString );
|
|
|
|
if( dlg.ShowModal() != wxID_OK )
|
|
return NULL;
|
|
|
|
wxString ref = dlg.GetTextSelection();
|
|
Module = (MODULE*) GetBoard()->m_Modules;
|
|
|
|
for( ; Module != NULL; Module = Module->Next() )
|
|
{
|
|
if( Module->m_Reference->m_Text == ref )
|
|
break;
|
|
}
|
|
|
|
return Module;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function Test_Duplicate_Missing_And_Extra_Footprints
|
|
* Build a list of duplicate, missing and extra footprints
|
|
* from the current board and a netlist netlist :
|
|
* Shows 3 lists:
|
|
* 1 - duplicate footprints on board
|
|
* 2 - missing footprints (found in netlist but not on board)
|
|
* 3 - footprints not in netlist but on board
|
|
* @param aNetlistFullFilename = the full filename netlist
|
|
*/
|
|
void PCB_EDIT_FRAME::Test_Duplicate_Missing_And_Extra_Footprints(
|
|
const wxString& aNetlistFullFilename )
|
|
{
|
|
#define MAX_LEN_TXT 32
|
|
int ii;
|
|
int NbModulesNetListe, nberr = 0;
|
|
wxArrayString tmp;
|
|
wxArrayString list;
|
|
|
|
if( GetBoard()->m_Modules == NULL )
|
|
{
|
|
DisplayInfoMessage( this, _( "No modules" ) );
|
|
return;
|
|
}
|
|
|
|
/* Build the list of references of the net list modules. */
|
|
NETLIST_READER netList_Reader( this );
|
|
NbModulesNetListe = netList_Reader.BuildComponentsListFromNetlist( aNetlistFullFilename, tmp );
|
|
|
|
if( NbModulesNetListe < 0 )
|
|
return; /* File not found */
|
|
|
|
if( NbModulesNetListe == 0 )
|
|
{
|
|
wxMessageBox( _( "No modules in NetList" ) );
|
|
return;
|
|
}
|
|
|
|
/* Search for duplicate footprints. */
|
|
list.Add( _( "Duplicates" ) );
|
|
|
|
MODULE* module = GetBoard()->m_Modules;
|
|
|
|
for( ; module != NULL; module = module->Next() )
|
|
{
|
|
MODULE* altmodule = module->Next();
|
|
|
|
for( ; altmodule != NULL; altmodule = altmodule->Next() )
|
|
{
|
|
if( module->m_Reference->m_Text.CmpNoCase( altmodule->m_Reference->m_Text ) == 0 )
|
|
{
|
|
if( module->m_Reference->m_Text.IsEmpty() )
|
|
list.Add( wxT("<noref>") );
|
|
else
|
|
list.Add( module->m_Reference->m_Text );
|
|
|
|
nberr++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Search for the missing module by the net list. */
|
|
list.Add( _( "Missing:" ) );
|
|
|
|
for( ii = 0; ii < NbModulesNetListe; ii++ )
|
|
{
|
|
module = (MODULE*) GetBoard()->m_Modules;
|
|
|
|
for( ; module != NULL; module = module->Next() )
|
|
{
|
|
if( module->m_Reference->m_Text.CmpNoCase( tmp[ii] ) == 0 )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( module == NULL ) // Module missing, not found in board
|
|
{
|
|
list.Add( tmp[ii] );
|
|
nberr++;
|
|
}
|
|
}
|
|
|
|
/* Search for modules not in net list. */
|
|
list.Add( _( "Not in Netlist:" ) );
|
|
|
|
module = GetBoard()->m_Modules;
|
|
for( ; module != NULL; module = module->Next() )
|
|
{
|
|
for( ii = 0; ii < NbModulesNetListe; ii++ )
|
|
{
|
|
if( module->m_Reference->m_Text.CmpNoCase( tmp[ii] ) == 0 )
|
|
{
|
|
break; /* Module is in net list. */
|
|
}
|
|
}
|
|
|
|
if( ii == NbModulesNetListe ) /* Module not found in netlist */
|
|
{
|
|
list.Add( module->m_Reference->m_Text );
|
|
nberr++;
|
|
}
|
|
}
|
|
|
|
wxSingleChoiceDialog dlg( this, wxEmptyString, _( "Check Modules" ), list, NULL,
|
|
wxCHOICEDLG_STYLE & ~wxCANCEL );
|
|
|
|
dlg.ShowModal();
|
|
}
|
|
|
|
|
|
/**
|
|
* Function BuildComponentsListFromNetlist
|
|
* Fill aBufName with component references read from the netlist.
|
|
* @param aNetlistFilename = netlist full file name
|
|
* @param aBufName = wxArrayString to fill with component references
|
|
* @return component count, or -1 if netlist file cannot opened
|
|
*/
|
|
int NETLIST_READER::BuildComponentsListFromNetlist( const wxString& aNetlistFilename,
|
|
wxArrayString& aBufName )
|
|
{
|
|
int component_count;
|
|
int state;
|
|
bool is_comment;
|
|
char* text;
|
|
|
|
FILE* netfile = OpenNetlistFile( aNetlistFilename );
|
|
|
|
if( netfile == NULL )
|
|
return -1;
|
|
|
|
FILE_LINE_READER netlineReader( netfile, aNetlistFilename ); // ctor will close netfile
|
|
char* Line = netlineReader;
|
|
|
|
state = 0;
|
|
is_comment = false;
|
|
component_count = 0;
|
|
|
|
while( netlineReader.ReadLine() )
|
|
{
|
|
text = StrPurge( Line );
|
|
|
|
if( is_comment )
|
|
{
|
|
if( ( text = strchr( text, '}' ) ) == NULL )
|
|
continue;
|
|
|
|
is_comment = false;
|
|
}
|
|
|
|
if( *text == '{' ) /* Comments. */
|
|
{
|
|
is_comment = true;
|
|
|
|
if( ( text = strchr( text, '}' ) ) == NULL )
|
|
continue;
|
|
}
|
|
|
|
if( *text == '(' )
|
|
state++;
|
|
|
|
if( *text == ')' )
|
|
state--;
|
|
|
|
if( state == 2 )
|
|
{
|
|
// skip TimeStamp:
|
|
strtok( Line, " ()\t\n" );
|
|
|
|
// skip footprint name:
|
|
strtok( NULL, " ()\t\n" );
|
|
|
|
// Load the reference of the component:
|
|
text = strtok( NULL, " ()\t\n" );
|
|
component_count++;
|
|
aBufName.Add( FROM_UTF8( text ) );
|
|
continue;
|
|
}
|
|
|
|
if( state >= 3 )
|
|
{
|
|
state--;
|
|
}
|
|
}
|
|
|
|
return component_count;
|
|
}
|
|
|
|
|
|
/*
|
|
* function readModuleComponentLinkfile
|
|
* read the *.cmp file ( filename in m_cmplistFullName )
|
|
* giving the equivalence Footprint_names / components
|
|
* to find the footprint name corresponding to aCmpIdent
|
|
* return true and the module name in aFootprintName, false if not found
|
|
*
|
|
* param aCmpIdent = component identification: schematic reference or time stamp
|
|
* param aFootprintName the footprint name corresponding to the component identification
|
|
*
|
|
* Sample file:
|
|
*
|
|
* Cmp-Mod V01 Genere by Pcbnew 29/10/2003-13: 11:6 *
|
|
* BeginCmp
|
|
* TimeStamp = /322D3011;
|
|
* Reference = BUS1;
|
|
* ValeurCmp = BUSPC;
|
|
* IdModule = BUS_PC;
|
|
* EndCmp
|
|
*
|
|
* BeginCmp
|
|
* TimeStamp = /32307DE2/AA450F67;
|
|
* Reference = C1;
|
|
* ValeurCmp = 47uF;
|
|
* IdModule = CP6;
|
|
* EndCmp
|
|
*
|
|
*/
|
|
bool NETLIST_READER::readModuleComponentLinkfile( const wxString* aCmpIdent,
|
|
wxString& aFootprintName )
|
|
{
|
|
wxString refcurrcmp; // Stores value read from line like Reference = BUS1;
|
|
wxString timestamp; // Stores value read from line like TimeStamp = /32307DE2/AA450F67;
|
|
wxString idmod; // Stores value read from line like IdModule = CP6;
|
|
|
|
FILE* cmpFile = wxFopen( m_cmplistFullName, wxT( "rt" ) );
|
|
|
|
if( cmpFile == NULL )
|
|
{
|
|
wxString msg;
|
|
msg.Printf( _( "File <%s> not found, use Netlist for lib module selection" ),
|
|
GetChars( m_cmplistFullName ) );
|
|
DisplayError( NULL, msg, 20 );
|
|
return false;
|
|
}
|
|
|
|
// netlineReader dtor will close cmpFile
|
|
FILE_LINE_READER netlineReader( cmpFile, m_cmplistFullName );
|
|
wxString buffer;
|
|
wxString value;
|
|
|
|
while( netlineReader.ReadLine() )
|
|
{
|
|
buffer = FROM_UTF8( netlineReader.Line() );
|
|
|
|
if( ! buffer.StartsWith( wxT("BeginCmp") ) )
|
|
continue;
|
|
|
|
/* Begin component description. */
|
|
refcurrcmp.Empty();
|
|
idmod.Empty();
|
|
timestamp.Empty();
|
|
|
|
while( netlineReader.ReadLine() )
|
|
{
|
|
buffer = FROM_UTF8( netlineReader.Line() );
|
|
|
|
if( buffer.StartsWith( wxT("EndCmp") ) )
|
|
break;
|
|
|
|
// store string value, stored between '=' and ';' delimiters.
|
|
value = buffer.AfterFirst( '=' );
|
|
value = value.BeforeLast( ';');
|
|
value.Trim(true);
|
|
value.Trim(false);
|
|
|
|
if( buffer.StartsWith( wxT("Reference") ) )
|
|
{
|
|
refcurrcmp = value;
|
|
continue;
|
|
}
|
|
|
|
if( buffer.StartsWith( wxT("IdModule =" ) ) )
|
|
{
|
|
idmod = value;
|
|
continue;
|
|
}
|
|
|
|
if( buffer.StartsWith( wxT("TimeStamp =" ) ) )
|
|
{
|
|
timestamp = value;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Check if this component is the right component:
|
|
if( m_UseTimeStamp ) // Use schematic timestamp to locate the footprint
|
|
{
|
|
if( aCmpIdent->CmpNoCase( timestamp ) == 0 && !timestamp.IsEmpty() )
|
|
{ // Found
|
|
aFootprintName = idmod;
|
|
return true;
|
|
}
|
|
}
|
|
else // Use schematic reference to locate the footprint
|
|
{
|
|
if( aCmpIdent->CmpNoCase( refcurrcmp ) == 0 ) // Found!
|
|
{
|
|
aFootprintName = idmod;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/* Load new modules from library.
|
|
* If a new module is already loaded it is duplicated, which avoids multiple
|
|
* unnecessary disk or net access to read libraries.
|
|
* return false if a footprint is not found, true if OK
|
|
*/
|
|
bool NETLIST_READER::loadNewModules()
|
|
{
|
|
MODULE_INFO* ref, * cmp;
|
|
MODULE* Module = NULL;
|
|
wxPoint ModuleBestPosition;
|
|
BOARD* pcb = m_pcbframe->GetBoard();
|
|
bool success = true;
|
|
|
|
if( m_newModulesList.size() == 0 )
|
|
return true;
|
|
|
|
sort( m_newModulesList.begin(), m_newModulesList.end(), SortByLibName );
|
|
|
|
// Calculate the footprint "best" position:
|
|
if( pcb->ComputeBoundingBox( true ) )
|
|
{
|
|
ModuleBestPosition = pcb->m_BoundaryBox.GetEnd();
|
|
ModuleBestPosition.y += 5000;
|
|
}
|
|
|
|
ref = cmp = m_newModulesList[0];
|
|
|
|
for( unsigned ii = 0; ii < m_newModulesList.size(); ii++ )
|
|
{
|
|
cmp = m_newModulesList[ii];
|
|
|
|
if( (ii == 0) || ( ref->m_LibName != cmp->m_LibName) )
|
|
{
|
|
/* New footprint : must be loaded from a library */
|
|
Module = m_pcbframe->GetModuleLibrary( wxEmptyString, cmp->m_LibName, false );
|
|
ref = cmp;
|
|
|
|
if( Module == NULL )
|
|
{
|
|
success = false;
|
|
wxString msg;
|
|
msg.Printf( _( "Component [%s]: footprint <%s> not found" ),
|
|
GetChars( cmp->m_CmpName ),
|
|
GetChars( cmp->m_LibName ) );
|
|
|
|
if( m_messageWindow )
|
|
{
|
|
msg += wxT("\n");
|
|
m_messageWindow->AppendText( msg );
|
|
}
|
|
else
|
|
{
|
|
DisplayError( NULL, msg );
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
Module->SetPosition( ModuleBestPosition );
|
|
|
|
/* Update schematic links : reference "Time Stamp" and schematic
|
|
* hierarchical path */
|
|
Module->m_Reference->m_Text = cmp->m_CmpName;
|
|
Module->m_TimeStamp = GetTimeStamp();
|
|
Module->m_Path = cmp->m_TimeStampPath;
|
|
}
|
|
else
|
|
{
|
|
// Footprint already loaded from a library, duplicate it (faster)
|
|
if( Module == NULL )
|
|
continue; /* Module does not exist in library. */
|
|
|
|
MODULE* newmodule = new MODULE( pcb );
|
|
newmodule->Copy( Module );
|
|
|
|
pcb->Add( newmodule, ADD_APPEND );
|
|
|
|
Module = newmodule;
|
|
Module->m_Reference->m_Text = cmp->m_CmpName;
|
|
Module->m_TimeStamp = GetTimeStamp();
|
|
Module->m_Path = cmp->m_TimeStampPath;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|