Handle unit changes in the netlist

Each symbol unit in eeschema has a unique identifier.  But we don't have
a unique identifier for the entire symbol.  So changing which symbol
instance was unit A (our default base for matching), changed the UUID
that we were using to match the footprints.

This commit adds all UUIDs to the netlist, allowing us to match symbol
to footprint without worrying about which unit is referenced.

This still does not handle changing different units on different sheets.

Fixes https://gitlab.com/kicad/code/kicad/issues/7604
This commit is contained in:
Seth Hillbrand 2021-02-20 20:03:56 -08:00
parent 2a9d76f1e3
commit 8a12aa4e3a
10 changed files with 105 additions and 27 deletions

View File

@ -34,6 +34,7 @@
#include <symbol_lib_table.h> #include <symbol_lib_table.h>
#include <set>
static bool sortPinsByNumber( LIB_PIN* aPin1, LIB_PIN* aPin2 ); static bool sortPinsByNumber( LIB_PIN* aPin1, LIB_PIN* aPin2 );
@ -52,7 +53,7 @@ XNODE* NETLIST_EXPORTER_XML::makeRoot( unsigned aCtl )
{ {
XNODE* xroot = node( "export" ); XNODE* xroot = node( "export" );
xroot->AddAttribute( "version", "D" ); xroot->AddAttribute( "version", "E" );
if( aCtl & GNL_HEADER ) if( aCtl & GNL_HEADER )
// add the "design" header // add the "design" header
@ -237,6 +238,7 @@ XNODE* NETLIST_EXPORTER_XML::makeSymbols( unsigned aCtl )
}; };
std::set<SCH_COMPONENT*, decltype( cmp )> ordered_symbols( cmp ); std::set<SCH_COMPONENT*, decltype( cmp )> ordered_symbols( cmp );
std::multiset<SCH_COMPONENT*, decltype( cmp )> extra_units( cmp );
for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_COMPONENT_T ) ) for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_COMPONENT_T ) )
{ {
@ -245,11 +247,16 @@ XNODE* NETLIST_EXPORTER_XML::makeSymbols( unsigned aCtl )
if( !test.second ) if( !test.second )
{ {
if( ( *( test.first ) )->GetUnit() > symbol->GetUnit() ) if( ( *( test.first ) )->m_Uuid > symbol->m_Uuid )
{ {
extra_units.insert( *( test.first ) );
ordered_symbols.erase( test.first ); ordered_symbols.erase( test.first );
ordered_symbols.insert( symbol ); ordered_symbols.insert( symbol );
} }
else
{
extra_units.insert( symbol );
}
} }
} }
@ -323,7 +330,20 @@ XNODE* NETLIST_EXPORTER_XML::makeSymbols( unsigned aCtl )
xsheetpath->AddAttribute( "names", sheet.PathHumanReadable() ); xsheetpath->AddAttribute( "names", sheet.PathHumanReadable() );
xsheetpath->AddAttribute( "tstamps", sheet.PathAsString() ); xsheetpath->AddAttribute( "tstamps", sheet.PathAsString() );
xcomp->AddChild( node( "tstamp", symbol->m_Uuid.AsString() ) );
XNODE* xunits; // Node for extra units
xcomp->AddChild( xunits = node( "tstamps" ) );
auto range = extra_units.equal_range( symbol );
// Output a series of children with all UUIDs associated with the REFDES
for( auto it = range.first; it != range.second; ++it )
xunits->AddChild(
new XNODE( wxXML_TEXT_NODE, wxEmptyString, ( *it )->m_Uuid.AsString() ) );
// Output the primary UUID
xunits->AddChild(
new XNODE( wxXML_TEXT_NODE, wxEmptyString, symbol->m_Uuid.AsString() ) );
} }
} }

View File

@ -83,6 +83,11 @@ public:
return m_uuid < rhs.m_uuid; return m_uuid < rhs.m_uuid;
} }
bool operator>( KIID const& rhs ) const
{
return m_uuid > rhs.m_uuid;
}
private: private:
boost::uuids::uuid m_uuid; boost::uuids::uuid m_uuid;

View File

@ -516,7 +516,7 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
continue; // Don't add board-only footprints to the netlist continue; // Don't add board-only footprints to the netlist
COMPONENT* component = new COMPONENT( footprint->GetFPID(), footprint->GetReference(), COMPONENT* component = new COMPONENT( footprint->GetFPID(), footprint->GetReference(),
footprint->GetValue(), footprint->GetPath() ); footprint->GetValue(), footprint->GetPath(), {} );
for( PAD* pad : footprint->Pads() ) for( PAD* pad : footprint->Pads() )
{ {

View File

@ -645,7 +645,7 @@ bool DIALOG_BOARD_REANNOTATE::ReannotateBoard()
//add to the netlist //add to the netlist
netlist.AddComponent( new COMPONENT( footprint->GetFPID(), newref->NewRefDes, netlist.AddComponent( new COMPONENT( footprint->GetFPID(), newref->NewRefDes,
footprint->GetValue(), footprint->GetPath() ) ); footprint->GetValue(), footprint->GetPath(), { footprint->m_Uuid } ) );
} }
netlist.Format( "pcb_netlist", &stringformatter, 0, netlist.Format( "pcb_netlist", &stringformatter, 0,

View File

@ -289,18 +289,21 @@ bool BOARD_NETLIST_UPDATER::updateFootprintParameters( FOOTPRINT* aPcbFootprint,
} }
// Test for time stamp change. // Test for time stamp change.
if( aPcbFootprint->GetPath() != aNetlistComponent->GetPath() ) KIID_PATH new_path = aNetlistComponent->GetPath();
new_path.push_back( aNetlistComponent->GetKIIDs().front() );
if( aPcbFootprint->GetPath() != new_path )
{ {
msg.Printf( _( "Update %s symbol association from %s to %s." ), msg.Printf( _( "Update %s symbol association from %s to %s." ),
aPcbFootprint->GetReference(), aPcbFootprint->GetReference(),
aPcbFootprint->GetPath().AsString(), aPcbFootprint->GetPath().AsString(),
aNetlistComponent->GetPath().AsString() ); new_path.AsString() );
m_reporter->Report( msg, RPT_SEVERITY_ACTION ); m_reporter->Report( msg, RPT_SEVERITY_ACTION );
if( !m_isDryRun ) if( !m_isDryRun )
{ {
changed = true; changed = true;
aPcbFootprint->SetPath( aNetlistComponent->GetPath() ); aPcbFootprint->SetPath( new_path );
} }
} }
@ -810,7 +813,19 @@ bool BOARD_NETLIST_UPDATER::UpdateNetlist( NETLIST& aNetlist )
bool match = false; bool match = false;
if( m_lookupByTimestamp ) if( m_lookupByTimestamp )
match = footprint->GetPath() == component->GetPath(); {
for( auto& uuid : component->GetKIIDs() )
{
KIID_PATH base = component->GetPath();
base.push_back( uuid );
if( footprint->GetPath() == base )
{
match = true;
break;
}
}
}
else else
match = footprint->GetReference().CmpNoCase( component->GetReference() ) == 0; match = footprint->GetReference().CmpNoCase( component->GetReference() ) == 0;

View File

@ -308,7 +308,8 @@ void KICAD_NETLIST_PARSER::parseComponent()
wxString library; wxString library;
wxString name; wxString name;
KIID_PATH path; KIID_PATH path;
KIID uuid;
std::vector<KIID> uuids;
std::map<wxString, wxString> properties; std::map<wxString, wxString> properties;
// The token comp was read, so the next data is (ref P1) // The token comp was read, so the next data is (ref P1)
@ -414,10 +415,15 @@ void KICAD_NETLIST_PARSER::parseComponent()
NeedRIGHT(); NeedRIGHT();
break; break;
case T_tstamp: case T_tstamps:
NeedSYMBOLorNUMBER(); while( ( token = NextTok() ) != T_EOF )
uuid = KIID( FROM_UTF8( CurText() ) ); {
NeedRIGHT(); if( token == T_RIGHT )
break;
uuids.emplace_back( FROM_UTF8( CurText() ) );
}
break; break;
default: default:
@ -436,8 +442,7 @@ void KICAD_NETLIST_PARSER::parseComponent()
THROW_IO_ERROR( error ); THROW_IO_ERROR( error );
} }
path.push_back( uuid ); COMPONENT* component = new COMPONENT( fpid, ref, value, path, uuids );
COMPONENT* component = new COMPONENT( fpid, ref, value, path );
component->SetName( name ); component->SetName( name );
component->SetLibrary( library ); component->SetLibrary( library );
component->SetProperties( properties ); component->SetProperties( properties );

View File

@ -168,7 +168,7 @@ COMPONENT* LEGACY_NETLIST_READER::loadComponent( char* aText )
if( !footprintName.IsEmpty() ) if( !footprintName.IsEmpty() )
fpid.SetLibItemName( footprintName ); fpid.SetLibItemName( footprintName );
COMPONENT* component = new COMPONENT( fpid, reference, value, path ); COMPONENT* component = new COMPONENT( fpid, reference, value, path, {} );
component->SetName( name ); component->SetName( name );
m_netlist->AddComponent( component ); m_netlist->AddComponent( component );
return component; return component;

View File

@ -161,9 +161,18 @@ void PCB_EDIT_FRAME::LoadFootprints( NETLIST& aNetlist, REPORTER& aReporter )
// Check if component footprint is already on BOARD and only load the footprint from // Check if component footprint is already on BOARD and only load the footprint from
// the library if it's needed. Nickname can be blank. // the library if it's needed. Nickname can be blank.
if( aNetlist.IsFindByTimeStamp() ) if( aNetlist.IsFindByTimeStamp() )
fpOnBoard = m_pcb->FindFootprintByPath( aNetlist.GetComponent( ii )->GetPath() ); {
for( const KIID& uuid : component->GetKIIDs() )
{
KIID_PATH path = component->GetPath();
path.push_back( uuid );
if( ( fpOnBoard = m_pcb->FindFootprintByPath( path ) ) )
break;
}
}
else else
fpOnBoard = m_pcb->FindFootprintByReference( aNetlist.GetComponent( ii )->GetReference() ); fpOnBoard = m_pcb->FindFootprintByReference( component->GetReference() );
bool footprintMisMatch = fpOnBoard && fpOnBoard->GetFPID() != component->GetFPID(); bool footprintMisMatch = fpOnBoard && fpOnBoard->GetFPID() != component->GetFPID();

View File

@ -40,6 +40,9 @@ int COMPONENT_NET::Format( OUTPUTFORMATTER* aOut, int aNestLevel, int aCtl )
void COMPONENT::SetFootprint( FOOTPRINT* aFootprint ) void COMPONENT::SetFootprint( FOOTPRINT* aFootprint )
{ {
m_footprint.reset( aFootprint ); m_footprint.reset( aFootprint );
KIID_PATH path = m_path;
path.push_back( m_kiids.front() );
if( aFootprint == NULL ) if( aFootprint == NULL )
return; return;
@ -47,7 +50,7 @@ void COMPONENT::SetFootprint( FOOTPRINT* aFootprint )
aFootprint->SetReference( m_reference ); aFootprint->SetReference( m_reference );
aFootprint->SetValue( m_value ); aFootprint->SetValue( m_value );
aFootprint->SetFPID( m_fpid ); aFootprint->SetFPID( m_fpid );
aFootprint->SetPath( m_path ); aFootprint->SetPath( path );
aFootprint->SetProperties( m_properties ); aFootprint->SetProperties( m_properties );
} }
@ -85,6 +88,9 @@ void COMPONENT::Format( OUTPUTFORMATTER* aOut, int aNestLevel, int aCtl )
for( const KIID& pathStep : m_path ) for( const KIID& pathStep : m_path )
path += '/' + pathStep.AsString(); path += '/' + pathStep.AsString();
if( !m_kiids.empty() )
path += '/' + m_kiids.front().AsString();
aOut->Print( nl+1, "(timestamp %s)\n", aOut->Quotew( path ).c_str() ); aOut->Print( nl+1, "(timestamp %s)\n", aOut->Quotew( path ).c_str() );
} }
@ -160,9 +166,20 @@ COMPONENT* NETLIST::GetComponentByReference( const wxString& aReference )
COMPONENT* NETLIST::GetComponentByPath( const KIID_PATH& aUuidPath ) COMPONENT* NETLIST::GetComponentByPath( const KIID_PATH& aUuidPath )
{ {
KIID comp_uuid = aUuidPath.back();
KIID_PATH base = aUuidPath;
if( !base.empty() )
base.pop_back();
for( COMPONENT& component : m_components ) for( COMPONENT& component : m_components )
{ {
if( component.GetPath() == aUuidPath ) const std::vector<KIID>& kiids = component.GetKIIDs();
if( base != component.GetPath() )
continue;
if( std::find( kiids.begin(), kiids.end(), comp_uuid ) != kiids.end() )
return &component; return &component;
} }

View File

@ -90,9 +90,12 @@ class COMPONENT
wxString m_reference; ///< The component reference designator found in netlist. wxString m_reference; ///< The component reference designator found in netlist.
wxString m_value; ///< The component value found in netlist. wxString m_value; ///< The component value found in netlist.
/// A fully specified path to the component: [ sheetUUID, sheetUUID, .., componentUUID ] /// A fully specified path to the component (but not the component: [ sheetUUID, sheetUUID, .. ]
KIID_PATH m_path; KIID_PATH m_path;
/// A vector of possible KIIDs corresponding to all units in a symbol
std::vector<KIID> m_kiids;
/// The name of the component in #m_library used when it was placed on the schematic.. /// The name of the component in #m_library used when it was placed on the schematic..
wxString m_name; wxString m_name;
@ -119,13 +122,15 @@ public:
COMPONENT( const LIB_ID& aFPID, COMPONENT( const LIB_ID& aFPID,
const wxString& aReference, const wxString& aReference,
const wxString& aValue, const wxString& aValue,
const KIID_PATH& aPath ) const KIID_PATH& aPath,
const std::vector<KIID>& aKiids )
{ {
m_fpid = aFPID; m_fpid = aFPID;
m_reference = aReference; m_reference = aReference;
m_value = aValue; m_value = aValue;
m_pinCount = 0; m_pinCount = 0;
m_path = aPath; m_path = aPath;
m_kiids = aKiids;
} }
virtual ~COMPONENT() { }; virtual ~COMPONENT() { };
@ -167,6 +172,8 @@ public:
const KIID_PATH& GetPath() const { return m_path; } const KIID_PATH& GetPath() const { return m_path; }
const std::vector<KIID>& GetKIIDs() const { return m_kiids; }
void SetFootprintFilters( const wxArrayString& aFilters ) { m_footprintFilters = aFilters; } void SetFootprintFilters( const wxArrayString& aFilters ) { m_footprintFilters = aFilters; }
const wxArrayString& GetFootprintFilters() const { return m_footprintFilters; } const wxArrayString& GetFootprintFilters() const { return m_footprintFilters; }