Refactor Import Netlist to use the same codepath as Update PCB

This commit is contained in:
Jon Evans 2019-04-17 22:10:56 -04:00
parent 3a8ffd66ca
commit 105825c058
7 changed files with 91 additions and 690 deletions

View File

@ -2428,471 +2428,6 @@ bool BOARD::NormalizeAreaPolygon( PICKED_ITEMS_LIST * aNewZonesList, ZONE_CONTAI
}
void BOARD::updateComponentPadConnections( NETLIST& aNetlist, MODULE* footprint,
COMPONENT* component, REPORTER& aReporter )
{
wxString msg;
for( auto pad : footprint->Pads() )
{
COMPONENT_NET net = component->GetNet( pad->GetName() );
if( !net.IsValid() ) // Footprint pad had no net.
{
if( !pad->GetNetname().IsEmpty() )
{
msg.Printf( _( "Clearing component %s pin %s net." ),
footprint->GetReference(),
pad->GetName() );
aReporter.Report( msg, REPORTER::RPT_ACTION );
}
if( !aNetlist.IsDryRun() )
{
m_connectivity->Remove( pad );
pad->SetNetCode( NETINFO_LIST::UNCONNECTED );
}
}
else // Footprint pad has a net.
{
const wxString& netName = net.GetNetName();
NETINFO_ITEM* netinfo = FindNet( netName );
if( netinfo && !aNetlist.IsDryRun() )
netinfo->SetIsCurrent( true );
if( pad->GetNetname() != netName )
{
m_oldToNewNets[ pad->GetNetname() ] = netName;
msg.Printf( _( "Changing footprint %s pad %s net from %s to %s." ),
footprint->GetReference(),
pad->GetName(),
UnescapeString( pad->GetNetname() ),
UnescapeString( netName ) );
aReporter.Report( msg, REPORTER::RPT_ACTION );
if( !aNetlist.IsDryRun() )
{
if( netinfo == NULL )
{
// It is a new net, we have to add it
netinfo = new NETINFO_ITEM( this, net.GetNetName() );
Add( netinfo );
}
m_connectivity->Remove( pad );
pad->SetNetCode( netinfo->GetNet() );
m_connectivity->Add( pad );
}
}
}
}
}
void BOARD::ReplaceNetlist( NETLIST& aNetlist, bool aDeleteSinglePadNets,
std::vector<MODULE*>* aNewFootprints, REPORTER& aReporter )
{
unsigned i;
wxPoint bestPosition;
wxString msg;
std::vector<MODULE*> newFootprints;
std::map< ZONE_CONTAINER*, std::vector<D_PAD*> > zoneConnectionsCache;
MODULE* lastPreexistingFootprint = m_Modules.GetLast();
m_oldToNewNets.clear();
for( int ii = 0; ii < GetAreaCount(); ii++ )
{
ZONE_CONTAINER* zone = GetArea( ii );
if( !zone->IsOnCopperLayer() || zone->GetIsKeepout() )
continue;
zoneConnectionsCache[ zone ] = m_connectivity->GetConnectedPads( zone );
}
if( !IsEmpty() )
{
// Position new components below any existing board features.
EDA_RECT bbbox = GetBoardEdgesBoundingBox();
if( bbbox.GetWidth() || bbbox.GetHeight() )
{
bestPosition.x = bbbox.Centre().x;
bestPosition.y = bbbox.GetBottom() + Millimeter2iu( 10 );
}
}
else
{
// Position new components in the center of the page when the board is empty.
wxSize pageSize = m_paper.GetSizeIU();
bestPosition.x = pageSize.GetWidth() / 2;
bestPosition.y = pageSize.GetHeight() / 2;
}
m_Status_Pcb = 0;
// Mark all nets (except <no net>) as stale; we'll update those to current that
// we find in the netlist
for( NETINFO_ITEM* net : m_NetInfo )
net->SetIsCurrent( net->GetNet() == 0 );
for( i = 0; i < aNetlist.GetCount(); i++ )
{
COMPONENT* component = aNetlist.GetComponent( i );
int matchCount = 0;
MODULE* tmp;
msg.Printf( _( "Checking netlist symbol footprint \"%s:%s:%s\"." ),
component->GetReference(),
component->GetTimeStamp(),
GetChars( component->GetFPID().Format() ) );
aReporter.Report( msg, REPORTER::RPT_INFO );
for( MODULE* footprint = m_Modules; footprint; footprint = footprint->Next() )
{
bool match;
if( aNetlist.IsFindByTimeStamp() )
match = footprint->GetPath() == component->GetTimeStamp();
else
match = footprint->GetReference().CmpNoCase( component->GetReference() ) == 0;
if( match )
{
// Test for footprint change.
if( !component->GetFPID().empty() && footprint->GetFPID() != component->GetFPID() )
{
if( aNetlist.GetReplaceFootprints() )
{
if( component->GetModule() != NULL )
{
msg.Printf( _( "Changing symbol %s footprint from %s to %s." ),
footprint->GetReference(),
GetChars( footprint->GetFPID().Format() ),
GetChars( component->GetFPID().Format() ) );
aReporter.Report( msg, REPORTER::RPT_ACTION );
}
else
{
msg.Printf( _( "Cannot change symbol %s footprint due to missing footprint %s." ),
footprint->GetReference(),
GetChars( component->GetFPID().Format() ) );
aReporter.Report( msg, REPORTER::RPT_ERROR );
}
if( !aNetlist.IsDryRun() && (component->GetModule() != NULL) )
{
wxASSERT( footprint != NULL );
MODULE* newFootprint = new MODULE( *component->GetModule() );
if( aNetlist.IsFindByTimeStamp() )
newFootprint->SetReference( footprint->GetReference() );
else
newFootprint->SetPath( footprint->GetPath() );
// Copy placement and pad net names.
// optionally, copy or not local settings (like local clearances)
// if the second parameter is "true", previous values will be used.
// if "false", the default library values of the new footprint
// will be used
footprint->CopyNetlistSettings( newFootprint, false );
// Compare the footprint name only, in case the nickname is empty or in case
// user moved the footprint to a new library. Chances are if footprint name is
// same then the footprint is very nearly the same and the two texts should
// be kept at same size, position, and rotation.
if( newFootprint->GetFPID().GetLibItemName() == footprint->GetFPID().GetLibItemName() )
{
newFootprint->Reference().SetEffects( footprint->Reference() );
newFootprint->Value().SetEffects( footprint->Value() );
}
m_connectivity->Remove( footprint );
Remove( footprint );
Add( newFootprint, ADD_APPEND );
m_connectivity->Add( footprint );
footprint = newFootprint;
}
}
}
// Test for reference designator field change.
if( footprint->GetReference() != component->GetReference() )
{
msg.Printf( _( "Changing footprint %s reference to %s." ),
footprint->GetReference(),
component->GetReference() );
aReporter.Report( msg, REPORTER::RPT_ACTION );
if( !aNetlist.IsDryRun() )
footprint->SetReference( component->GetReference() );
}
// Test for value field change.
if( footprint->GetValue() != component->GetValue() )
{
msg.Printf( _( "Changing footprint %s value from %s to %s." ),
footprint->GetReference(),
footprint->GetValue(),
component->GetValue() );
aReporter.Report( msg, REPORTER::RPT_ACTION );
if( !aNetlist.IsDryRun() )
footprint->SetValue( component->GetValue() );
}
// Test for time stamp change.
if( footprint->GetPath() != component->GetTimeStamp() )
{
msg.Printf( _( "Changing component path \"%s:%s\" to \"%s\"." ),
footprint->GetReference(),
footprint->GetPath(),
component->GetTimeStamp() );
aReporter.Report( msg, REPORTER::RPT_INFO );
if( !aNetlist.IsDryRun() )
footprint->SetPath( component->GetTimeStamp() );
}
updateComponentPadConnections( aNetlist, footprint, component, aReporter );
matchCount++;
}
if( footprint == lastPreexistingFootprint )
{
// No sense going through the newly-created footprints: end loop
break;
}
}
if( matchCount == 0 )
{
if( component->GetModule() != NULL )
{
msg.Printf( _( "Adding new symbol %s footprint %s." ),
component->GetReference(),
GetChars( component->GetFPID().Format() ) );
aReporter.Report( msg, REPORTER::RPT_ACTION );
}
else
{
msg.Printf( _( "Cannot add new symbol %s due to missing footprint %s." ),
component->GetReference(),
GetChars( component->GetFPID().Format() ) );
aReporter.Report( msg, REPORTER::RPT_ERROR );
}
if( !aNetlist.IsDryRun() && (component->GetModule() != NULL) )
{
// Owned by NETLIST, can only copy it.
tmp = new MODULE( *component->GetModule() );
tmp->SetParent( this );
tmp->SetPosition( bestPosition );
tmp->SetTimeStamp( GetNewTimeStamp() );
newFootprints.push_back( tmp );
Add( tmp, ADD_APPEND );
m_connectivity->Add( tmp );
updateComponentPadConnections( aNetlist, tmp, component, aReporter );
}
}
else if( matchCount > 1 )
{
msg.Printf( _( "Multiple footprints found for \"%s\"." ), component->GetReference() );
aReporter.Report( msg, REPORTER::RPT_ERROR );
}
}
// Remove all components not in the netlist.
if( aNetlist.GetDeleteExtraFootprints() )
{
MODULE* nextModule;
const COMPONENT* component;
for( MODULE* module = m_Modules; module != NULL; module = nextModule )
{
nextModule = module->Next();
if( module->IsLocked() )
continue;
if( aNetlist.IsFindByTimeStamp() )
component = aNetlist.GetComponentByTimeStamp( module->GetPath() );
else
component = aNetlist.GetComponentByReference( module->GetReference() );
if( component == NULL )
{
msg.Printf( _( "Removing unused footprint %s." ), module->GetReference() );
aReporter.Report( msg, REPORTER::RPT_ACTION );
if( !aNetlist.IsDryRun() )
{
m_connectivity->Remove( module );
module->DeleteStructure();
}
}
}
}
BuildListOfNets();
std::vector<D_PAD*> padlist = GetPads();
auto connAlgo = m_connectivity->GetConnectivityAlgo();
// If needed, remove the single pad nets:
if( aDeleteSinglePadNets && !aNetlist.IsDryRun() )
{
std::vector<unsigned int> padCount( (unsigned) connAlgo->NetCount() );
for( const auto cnItem : connAlgo->ItemList() )
{
if( !cnItem->Valid() || cnItem->Parent()->Type() != PCB_PAD_T )
continue;
int net = cnItem->Parent()->GetNetCode();
if( net > 0 )
++padCount[net];
}
for( i = 0; i < (unsigned)connAlgo->NetCount(); ++i )
{
// First condition: only one pad in the net
if( padCount[i] == 1 )
{
// Second condition, no zones attached to the pad
D_PAD* pad = nullptr;
int zoneCount = 0;
const KICAD_T types[] = { PCB_PAD_T, PCB_ZONE_AREA_T, EOT };
auto netItems = m_connectivity->GetNetItems( i, types );
for( const auto item : netItems )
{
if( item->Type() == PCB_ZONE_AREA_T )
{
wxASSERT( !pad || pad->GetNet() == item->GetNet() );
++zoneCount;
}
else if( item->Type() == PCB_PAD_T )
{
wxASSERT( !pad );
pad = static_cast<D_PAD*>( item );
}
}
wxASSERT( pad ); // pad = 0 means the pad list is not up to date
if( pad && zoneCount == 0 )
{
msg.Printf( _( "Remove single pad net %s." ),
UnescapeString( pad->GetNetname() ) );
aReporter.Report( msg, REPORTER::RPT_ACTION );
m_connectivity->Remove( pad );
pad->SetNetCode( NETINFO_LIST::UNCONNECTED );
}
}
}
}
// Verify that board contains all pads in netlist: if it doesn't then footprints are
// wrong or missing.
// Note that we use references to find the footprints as they're already updated by this
// point (whether by-reference or by-timestamp).
wxString padname;
for( i = 0; i < aNetlist.GetCount(); i++ )
{
const COMPONENT* component = aNetlist.GetComponent( i );
MODULE* footprint = FindModuleByReference( component->GetReference() );
if( footprint == NULL ) // It can be missing in partial designs
continue;
// Explore all pins/pads in component
for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
{
const COMPONENT_NET& net = component->GetNet( jj );
padname = net.GetPinName();
if( footprint->FindPadByName( padname ) )
continue; // OK, pad found
// not found: bad footprint, report error
msg.Printf( _( "Symbol %s pad %s not found in footprint %s.\n" ),
component->GetReference(),
padname,
GetChars( footprint->GetFPID().Format() ) );
aReporter.Report( msg, REPORTER::RPT_ERROR );
}
}
// Test copper zones to detect "dead" nets (nets without any pad):
for( int ii = 0; ii < GetAreaCount(); ii++ )
{
ZONE_CONTAINER* zone = GetArea( ii );
if( !zone->IsOnCopperLayer() || zone->GetIsKeepout() )
continue;
if( m_connectivity->GetPadCount( zone->GetNetCode() ) == 0 )
{
// Look for a pad in the zone's connected-pad-cache which has been updated to
// a new net and use that. While this won't always be the right net, the dead
// net is guaranteed to be wrong.
NETINFO_ITEM* updatedNet = nullptr;
for( D_PAD* pad : zoneConnectionsCache[ zone ] )
{
if( pad->GetNetname() != zone->GetNetname() )
{
updatedNet = pad->GetNet();
break;
}
}
// Take zone name from name change map if it didn't match to a new pad
// (this is useful for zones on internal layers)
if( !updatedNet && m_oldToNewNets.count( zone->GetNetname() ) )
{
updatedNet = FindNet( m_oldToNewNets[ zone->GetNetname() ] );
}
if( updatedNet )
{
msg.Printf( _( "Updating copper zone from net %s to %s." ),
UnescapeString( zone->GetNetname() ),
UnescapeString( updatedNet->GetNetname() ) );
aReporter.Report( msg, REPORTER::RPT_ACTION );
}
else
{
msg.Printf( _( "Copper zone (net %s) has no pads connected." ),
UnescapeString( zone->GetNetname() ) );
aReporter.Report( msg, REPORTER::RPT_WARNING );
}
if( updatedNet && !aNetlist.IsDryRun() )
{
m_connectivity->Remove( zone );
zone->SetNet( updatedNet );
m_connectivity->Add( zone );
}
}
}
m_connectivity->RecalculateRatsnest();
std::swap( newFootprints, *aNewFootprints );
}
BOARD_ITEM* BOARD::Duplicate( const BOARD_ITEM* aItem,
bool aAddToBoard )
{

View File

@ -243,9 +243,6 @@ public:
private:
DLIST<BOARD_ITEM> m_Drawings; // linked list of lines & texts
// TODO: remove this when BOARD::updateComponentPadConnections is removed
std::map< wxString, wxString > m_oldToNewNets;
public:
DLIST<MODULE> m_Modules; // linked list of MODULEs
@ -869,43 +866,6 @@ public:
*/
MODULE* FindModule( const wxString& aRefOrTimeStamp, bool aSearchByTimeStamp = false ) const;
/**
* Function ReplaceNetlist
* updates the #BOARD according to \a aNetlist.
*
* The changes are made to the board are as follows they are not disabled in the status
* settings in the #NETLIST:
* - If a new component is found in the #NETLIST and not in the #BOARD, it is added
* to the #BOARD.
* - If a the component in the #NETLIST is already on the #BOARD, then one or more of the
* following actions can occur:
* + If the footprint name in the #NETLIST does not match the footprint name on the
* #BOARD, the footprint on the #BOARD is replaced with the footprint specified in
* the #NETLIST and the proper parameters are copied from the existing footprint.
* + If the reference designator in the #NETLIST does not match the reference designator
* on the #BOARD, the reference designator is updated from the #NETLIST.
* + If the value field in the #NETLIST does not match the value field on the #BOARD,
* the value field is updated from the #NETLIST.
* + If the time stamp in the #NETLIST does not match the time stamp on the #BOARD,
* the time stamp is updated from the #NETLIST.
* - After each footprint is added or update as described above, each footprint pad net
* name is compared and updated to the value defined in the #NETLIST.
* - After all of the footprints have been added, updated, and net names properly set,
* any extra unlock footprints are removed from the #BOARD.
*
* @param aNetlist is the new netlist to revise the contents of the #BOARD with.
* @param aDeleteSinglePadNets if true, remove nets counting only one pad
* and set net code to 0 for these pads
* @param aNewFootprints is a pointer the to a list of new footprints used when updating
* the netlist.
* @param aReporter is a #REPORTER object to report the changes \a aNetlist makes to
* the #BOARD.
*/
void ReplaceNetlist( NETLIST& aNetlist, bool aDeleteSinglePadNets,
std::vector<MODULE*>* aNewFootprints, REPORTER& aReporter );
void updateComponentPadConnections( NETLIST& aNetlist, MODULE* footprint,
COMPONENT* component, REPORTER& aReporter );
/**
* Function SortedNetnamesList
* @param aNames An array string to fill with net names.

View File

@ -45,6 +45,7 @@
#include <class_module.h>
#include <connectivity/connectivity_data.h>
#include <wildcards_and_files_ext.h>
#include <board_netlist_updater.h>
#include <dialog_netlist.h>
#include <wx_html_report_panel.h>
@ -184,34 +185,11 @@ void DIALOG_NETLIST::OnTestFootprintsClick( wxCommandEvent& event )
}
wxString netlistFilename = m_NetlistFilenameCtrl->GetValue();
NETLIST_READER* netlistReader;
NETLIST netlist;
wxBusyCursor dummy; // Shows an hourglass while calculating.
try
{
netlistReader = NETLIST_READER::GetNetlistReader( &netlist, netlistFilename, "" );
if( netlistReader == NULL )
{
wxString msg = wxString::Format( _( "Cannot open netlist file \"%s\"." ),
netlistFilename );
wxMessageBox( msg, _( "Netlist Load Error." ), wxOK | wxICON_ERROR );
return;
}
std::unique_ptr< NETLIST_READER > nlr( netlistReader );
netlistReader->LoadNetlist();
m_parent->SetLastNetListRead( netlistFilename );
}
catch( const IO_ERROR& ioe )
{
wxString msg = wxString::Format( _( "Error loading netlist file:\n%s" ),
ioe.What().GetData() );
wxMessageBox( msg, _( "Netlist Load Error" ), wxOK | wxICON_ERROR );
if( !m_parent->ReadNetlistFromFile( netlistFilename, netlist, NULL_REPORTER::GetInstance() ) )
return;
}
HTML_MESSAGE_BOX dlg( this, _( "Check footprints" ) );
DRC_LIST drcItems;
@ -306,17 +284,31 @@ void DIALOG_NETLIST::loadNetlist( bool aDryRun )
reporter.ReportHead( msg, REPORTER::RPT_INFO );
m_MessageWindow->SetLazyUpdate( true ); // Use lazy update to speed the creation of the report
// (the window is not updated for each message)
NETLIST netlist;
m_parent->ReadPcbNetlist( netlistFileName, wxEmptyString, reporter,
m_cbUpdateFootprints->GetValue(),
m_cbDeleteShortingTracks->GetValue(),
m_cbDeleteExtraFootprints->GetValue(),
m_matchByTimestamp->GetSelection() == 1,
m_cbDeleteSinglePadNets->GetValue(),
aDryRun, &m_runDragCommand );
netlist.SetDeleteExtraFootprints( m_cbDeleteExtraFootprints->GetValue() );
netlist.SetFindByTimeStamp( m_matchByTimestamp->GetSelection() == 1 );
netlist.SetReplaceFootprints( m_cbUpdateFootprints->GetValue() );
if( !m_parent->ReadNetlistFromFile( netlistFileName, netlist, reporter ) )
return;
BOARD_NETLIST_UPDATER updater( m_parent, m_parent->GetBoard() );
updater.SetReporter ( &reporter );
updater.SetIsDryRun( aDryRun );
updater.SetLookupByTimestamp( m_matchByTimestamp->GetSelection() == 1 );
updater.SetDeleteUnusedComponents ( m_cbDeleteExtraFootprints->GetValue() );
updater.SetReplaceFootprints( m_cbUpdateFootprints->GetValue() );
updater.SetDeleteSinglePadNets( m_cbDeleteSinglePadNets->GetValue() );
updater.UpdateNetlist( netlist );
// The creation of the report was made without window update: the full page must be displayed
m_MessageWindow->Flush( true );
if( aDryRun )
return;
m_parent->OnNetlistChanged( updater, &m_runDragCommand );
}

View File

@ -52,6 +52,8 @@ private:
void loadNetlist( bool aDryRun );
// Virtual event handlers:
void OnOpenNetlistClick( wxCommandEvent& event ) override;
void OnUpdatePCB( wxCommandEvent& event ) override;

View File

@ -103,14 +103,7 @@ void DIALOG_UPDATE_PCB::PerformUpdate( bool aDryRun )
m_messagePanel->Clear();
REPORTER& reporter = m_messagePanel->Reporter();
TOOL_MANAGER* toolManager = m_frame->GetToolManager();
BOARD* board = m_frame->GetBoard();
// keep trace of the initial baord area, if we want to place new footprints
// outside the existinag board
EDA_RECT bbox = board->GetBoundingBox();
toolManager->RunAction( PCB_ACTIONS::selectionClear, true );
m_runDragCommand = false;
m_netlist->SetDeleteExtraFootprints( m_cbDeleteExtraFootprints->GetValue() );
@ -131,51 +124,7 @@ void DIALOG_UPDATE_PCB::PerformUpdate( bool aDryRun )
if( aDryRun )
return;
m_frame->SetCurItem( nullptr );
m_frame->SetMsgPanel( board );
// Update rendered tracks and vias net labels
auto view = m_frame->GetGalCanvas()->GetView();
// TODO is there a way to extract information about which nets were modified?
for( auto track : board->Tracks() )
view->Update( track );
std::vector<MODULE*> newFootprints = updater.GetAddedComponents();
// Spread new footprints.
wxPoint areaPosition = m_frame->GetCrossHairPosition();
if( !m_frame->IsGalCanvasActive() )
{
// In legacy mode place area to the left side of the board.
// if the board is empty, the bbox position is (0,0)
areaPosition.x = bbox.GetEnd().x + Millimeter2iu( 10 );
areaPosition.y = bbox.GetOrigin().y;
}
m_frame->SpreadFootprints( &newFootprints, false, false, areaPosition, false );
if( m_frame->IsGalCanvasActive() )
{
// Start drag command for new modules
if( !newFootprints.empty() )
{
for( MODULE* footprint : newFootprints )
toolManager->RunAction( PCB_ACTIONS::selectItem, true, footprint );
m_runDragCommand = true;
// Now fix a reference point to move the footprints.
// We use the first footprint in list as reference point
// The graphic cursor will be on this fp when moving the footprints.
SELECTION_TOOL* selTool = toolManager->GetTool<SELECTION_TOOL>();
SELECTION& selection = selTool->GetSelection();
selection.SetReferencePoint( newFootprints[0]->GetPosition() );
}
}
m_frame->GetCanvas()->Refresh();
m_frame->OnNetlistChanged( updater, &m_runDragCommand );
}

View File

@ -49,97 +49,83 @@ using namespace std::placeholders;
#include <ratsnest_data.h>
#include <pcbnew.h>
#include <io_mgr.h>
#include <board_netlist_updater.h>
#include <tool/tool_manager.h>
#include <tools/pcb_actions.h>
#include <tools/selection_tool.h>
#include <view/view.h>
void PCB_EDIT_FRAME::ReadPcbNetlist( const wxString& aNetlistFileName,
const wxString& aCmpFileName,
REPORTER& aReporter,
bool aChangeFootprints,
bool aDeleteUnconnectedTracks,
bool aDeleteExtraFootprints,
bool aSelectByTimeStamp,
bool aDeleteSinglePadNets,
bool aIsDryRun,
bool* runDragCommand )
bool PCB_EDIT_FRAME::ReadNetlistFromFile( const wxString &aFilename,
NETLIST& aNetlist,
REPORTER& aReporter )
{
wxString msg;
NETLIST netlist;
KIGFX::VIEW* view = GetGalCanvas()->GetView();
BOARD* board = GetBoard();
std::vector<MODULE*> newFootprints;
// keep trace of the initial baord area, if we want to place new footprints
// outside the existinag board
EDA_RECT bbox = board->GetBoundingBox();
netlist.SetIsDryRun( aIsDryRun );
netlist.SetFindByTimeStamp( aSelectByTimeStamp );
netlist.SetDeleteExtraFootprints( aDeleteExtraFootprints );
netlist.SetReplaceFootprints( aChangeFootprints );
wxString msg;
try
{
std::unique_ptr<NETLIST_READER> netlistReader( NETLIST_READER::GetNetlistReader(
&netlist, aNetlistFileName, aCmpFileName ) );
&aNetlist, aFilename, wxEmptyString ) );
if( !netlistReader.get() )
{
msg.Printf( _( "Cannot open netlist file \"%s\"." ), GetChars( aNetlistFileName ) );
msg.Printf( _( "Cannot open netlist file \"%s\"." ), GetChars( aFilename ) );
wxMessageBox( msg, _( "Netlist Load Error." ), wxOK | wxICON_ERROR, this );
return;
return false;
}
SetLastNetListRead( aNetlistFileName );
SetLastNetListRead( aFilename );
netlistReader->LoadNetlist();
LoadFootprints( netlist, aReporter );
LoadFootprints( aNetlist, aReporter );
}
catch( const IO_ERROR& ioe )
{
msg.Printf( _( "Error loading netlist.\n%s" ), ioe.What().GetData() );
wxMessageBox( msg, _( "Netlist Load Error" ), wxOK | wxICON_ERROR );
return;
return false;
}
// Clear undo and redo lists to avoid inconsistencies between lists
if( !netlist.IsDryRun() )
GetScreen()->ClearUndoRedoList();
SetLastNetListRead( aFilename );
if( !netlist.IsDryRun() )
{
// Remove old modules
for( MODULE* module = board->m_Modules; module; module = module->Next() )
view->Remove( module );
}
return true;
}
// Clear selection, just in case a selected item has to be removed
m_toolManager->RunAction( PCB_ACTIONS::selectionClear, true );
*runDragCommand = false;
netlist.SortByReference();
board->ReplaceNetlist( netlist, aDeleteSinglePadNets, &newFootprints, aReporter );
void PCB_EDIT_FRAME::OnNetlistChanged( BOARD_NETLIST_UPDATER& aUpdater,
bool* aRunDragCommand )
{
BOARD* board = GetBoard();
// If it was a dry run, nothing has changed so we're done.
if( netlist.IsDryRun() )
return;
SetCurItem( nullptr );
SetMsgPanel( board );
wxPoint placementAreaPosition = GetCrossHairPosition();
TOOL_MANAGER* toolManager = GetToolManager();
// Update rendered tracks and vias net labels
auto view = GetGalCanvas()->GetView();
// TODO is there a way to extract information about which nets were modified?
for( auto track : board->Tracks() )
view->Update( track );
std::vector<MODULE*> newFootprints = aUpdater.GetAddedComponents();
// Spread new footprints.
wxPoint areaPosition = GetCrossHairPosition();
EDA_RECT bbox = board->GetBoundingBox();
if( !IsGalCanvasActive() )
{
// In legacy mode place area to the left side of the board.
// if the board is empty, the bbox position is (0,0)
placementAreaPosition.x = bbox.GetEnd().x + Millimeter2iu( 10 );
placementAreaPosition.y = bbox.GetOrigin().y;
areaPosition.x = bbox.GetEnd().x + Millimeter2iu( 10 );
areaPosition.y = bbox.GetOrigin().y;
}
SpreadFootprints( &newFootprints, false, false, placementAreaPosition );
toolManager->RunAction( PCB_ACTIONS::selectionClear, true );
// Reload modules
for( MODULE* module = board->m_Modules; module; module = module->Next() )
view->Add( module );
SpreadFootprints( &newFootprints, false, false, areaPosition, false );
if( IsGalCanvasActive() )
{
@ -147,31 +133,20 @@ void PCB_EDIT_FRAME::ReadPcbNetlist( const wxString& aNetlistFileName,
if( !newFootprints.empty() )
{
for( MODULE* footprint : newFootprints )
m_toolManager->RunAction( PCB_ACTIONS::selectItem, true, footprint );
toolManager->RunAction( PCB_ACTIONS::selectItem, true, footprint );
*runDragCommand = true;
*aRunDragCommand = true;
// Now fix a reference point to move the footprints.
// We use the first footprint in list as reference point
// The graphic cursor will be on this fp when moving the footprints.
SELECTION_TOOL* selTool = toolManager->GetTool<SELECTION_TOOL>();
SELECTION& selection = selTool->GetSelection();
selection.SetReferencePoint( newFootprints[0]->GetPosition() );
}
}
OnModify();
SetCurItem( NULL );
if( aDeleteUnconnectedTracks && board->m_Track )
{
// Remove erroneous tracks. This should probably pushed down to the #BOARD object.
RemoveMisConnectedTracks();
}
// Rebuild the board connectivity:
board->GetConnectivity()->Build( board );
// TODO is there a way to extract information about which nets were modified?
for( auto track : board->Tracks() )
view->Update( track );
SetMsgPanel( board );
m_canvas->Refresh();
GetCanvas()->Refresh();
}

View File

@ -61,6 +61,7 @@ class REPORTER;
struct PARSE_ERROR;
class IO_ERROR;
class FP_LIB_TABLE;
class BOARD_NETLIST_UPDATER;
namespace PCB { struct IFACE; } // KIFACE_I is in pcbnew.cpp
@ -1564,36 +1565,23 @@ public:
void DoUpdatePCBFromNetlist( NETLIST& aNetlist, bool aUseTimestamps );
/**
* Function ReadPcbNetlist
* reads \a aNetlistFileName and updates the footprints (load missing footprints and
* delete on demand extra footprints) on the board.
* Update connectivity info, references, values and "TIME STAMP"
* Reads a netlist from a file into a NETLIST object.
*
* @param aNetlistFileName = netlist file name (*.net)
* @param aCmpFileName = cmp/footprint link file name (*.cmp).
* if not found or empty, only the netlist will be used
* @param aReporter a #REPORTER object to write display messages.
* @param aChangeFootprint if true, footprints that have changed in netlist will be changed
* @param aDeleteBadTracks if true, erroneous tracks will be deleted
* @param aDeleteExtraFootprints if true, remove unlocked footprints that are not in netlist
* @param aSelectByTimestamp if true, use timestamp instead of reference to identify
* footprints from components (use after reannotation of the
* schematic)
* @param aDeleteSinglePadNets if true, remove nets counting only one pad
* and set net code to 0 for these pads
* @param aIsDryRun performs a dry run without making any changes if true.
* @param runDragCommand indicates that a selection was created which should be dragged.
* @param aFilename is the netlist to load
* @param aNetlist is the object to populate with data
* @param aReporter is a #REPORTER object to display messages
* @return true if the netlist was read successfully
*/
void ReadPcbNetlist( const wxString& aNetlistFileName,
const wxString& aCmpFileName,
REPORTER& aReporter,
bool aChangeFootprint,
bool aDeleteBadTracks,
bool aDeleteExtraFootprints,
bool aSelectByTimestamp,
bool aDeleteSinglePadNets,
bool aIsDryRun,
bool* runDragCommand );
bool ReadNetlistFromFile( const wxString &aFilename,
NETLIST& aNetlist,
REPORTER& aReporter );
/**
* Called after netlist is updated
* @param aUpdater is the updater object that was run
* @param aRunDragCommand is set to true if the drag command was invoked by this call
*/
void OnNetlistChanged( BOARD_NETLIST_UPDATER& aUpdater, bool* aRunDragCommand );
/**
* Function RemoveMisConnectedTracks