Move schematic symbol instance data back into symbol definition.

This change reverts the storage of all symbol instance data in the root
schematic.  This was done because it's not possible to reuse instance
data when importing from sub-sheets.

There has been a fundamental change in how sheet paths are store in the
instance data.  The root schematic UUID is always used when saving the
instance data.  To prevent file churn, the virtual root sheet UUID is set
to the root schematic UUID when loading the project.  This provides a way
to determine the project that stored the instance data.  All uses of paths
without root sheet have been expunged from the code.

The sheet instance data is still saved only in the root sheet for the
time being.  New sheet instances will be automatically assigned an page
number based on the incremental virtual sheet page number.  Sheet page
numbers will not be imported.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/12472
This commit is contained in:
Wayne Stambaugh 2022-09-28 12:46:06 -04:00
parent b6efb88ee9
commit 7984e114db
18 changed files with 541 additions and 150 deletions

View File

@ -448,7 +448,9 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
screen->UpdateLocalLibSymbolLinks();
// Restore all of the loaded symbol and sheet instances from the root sheet.
sheetList.UpdateSymbolInstances( Schematic().RootScreen()->GetSymbolInstances() );
if( Schematic().RootScreen()->GetFileFormatVersionAtLoad() <= 20220919 )
sheetList.UpdateSymbolInstances( Schematic().RootScreen()->GetSymbolInstances() );
sheetList.UpdateSheetInstances( Schematic().RootScreen()->GetSheetInstances() );
for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )

View File

@ -87,4 +87,5 @@
//#define SEXPR_SCHEMATIC_FILE_VERSION 20220822 // Hyperlinks in text objects
//#define SEXPR_SCHEMATIC_FILE_VERSION 20220903 // Field name visibility
//#define SEXPR_SCHEMATIC_FILE_VERSION 20220904 // Do not autoplace field option
#define SEXPR_SCHEMATIC_FILE_VERSION 20220914 // Add support for DNP
//#define SEXPR_SCHEMATIC_FILE_VERSION 20220914 // Add support for DNP
#define SEXPR_SCHEMATIC_FILE_VERSION 20220919 // Move instance data back into symbol definition.

View File

@ -1375,10 +1375,9 @@ void SCH_EAGLE_PLUGIN::loadInstance( wxXmlNode* aInstanceNode )
SCH_SHEET_PATH sheetpath;
m_rootSheet->LocatePathOfScreen( screen, &sheetpath );
wxString current_sheetpath = sheetpath.PathAsString() + symbol->m_Uuid.AsString();
symbol->GetField( REFERENCE_FIELD )->SetText( reference );
symbol->AddHierarchicalReference( current_sheetpath, reference, unit );
symbol->AddHierarchicalReference( sheetpath.Path(), reference, unit );
if( epart->value )
symbol->GetField( VALUE_FIELD )->SetText( *epart->value );

View File

@ -65,16 +65,19 @@ using namespace TSCHEMATIC_T;
SCH_SEXPR_PARSER::SCH_SEXPR_PARSER( LINE_READER* aLineReader, PROGRESS_REPORTER* aProgressReporter,
unsigned aLineCount ) :
unsigned aLineCount, SCH_SHEET* aRootSheet,
bool aIsAppending ) :
SCHEMATIC_LEXER( aLineReader ),
m_requiredVersion( 0 ),
m_fieldId( 0 ),
m_unit( 1 ),
m_convert( 1 ),
m_appending( aIsAppending ),
m_progressReporter( aProgressReporter ),
m_lineReader( aLineReader ),
m_lastProgressLine( 0 ),
m_lineCount( aLineCount )
m_lineCount( aLineCount ),
m_rootSheet( aRootSheet )
{
}
@ -2049,6 +2052,9 @@ void SCH_SEXPR_PARSER::parseSchSheetInstances( SCH_SHEET* aRootSheet, SCH_SCREEN
instance.m_Path = KIID_PATH( FromUTF8() );
if( !m_appending && instance.m_Path.empty() )
instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
@ -2108,6 +2114,7 @@ void SCH_SEXPR_PARSER::parseSchSymbolInstances( SCH_SCREEN* aScreen )
wxCHECK_RET( CurTok() == T_symbol_instances,
"Cannot parse " + GetTokenString( CurTok() ) + " as an instances token." );
wxCHECK( aScreen, /* void */ );
wxCHECK( m_rootUuid != NilUuid(), /* void */ );
T token;
@ -2128,6 +2135,9 @@ void SCH_SEXPR_PARSER::parseSchSymbolInstances( SCH_SCREEN* aScreen )
instance.m_Path = KIID_PATH( FromUTF8() );
if( !m_appending )
instance.m_Path.insert( instance.m_Path.begin(), m_rootUuid );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
@ -2198,6 +2208,11 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly,
Expecting( "kicad_sch" );
parseHeader( T_kicad_sch, SEXPR_SCHEMATIC_FILE_VERSION );
// Prior to schematic file version 20210406, schematics did not have UUIDs so we need
// to generate one for the root schematic for instance paths.
if( m_requiredVersion < 20210406 )
m_rootUuid = screen->GetUuid();
}
screen->SetFileFormatVersionAtLoad( m_requiredVersion );
@ -2222,6 +2237,16 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly,
case T_uuid:
NeedSYMBOL();
screen->m_uuid = parseKIID();
// Set the root sheet UUID with the schematic file UUID. Root sheets are virtual
// and always get a new UUID so this prevents file churn now that the root UUID
// is saved in the symbol instance path.
if( aSheet == m_rootSheet )
{
const_cast<KIID&>( aSheet->m_Uuid ) = screen->GetUuid();
m_rootUuid = screen->GetUuid();
}
NeedRIGHT();
break;
@ -2599,7 +2624,7 @@ SCH_SYMBOL* SCH_SEXPR_PARSER::parseSchematicSymbol()
break;
default:
Expecting( "path, unit, value or footprint" );
Expecting( "reference, unit, value or footprint" );
}
}
@ -2607,6 +2632,66 @@ SCH_SYMBOL* SCH_SEXPR_PARSER::parseSchematicSymbol()
break;
}
case T_instances:
{
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
if( token != T_path )
Expecting( "path" );
SYMBOL_INSTANCE_REFERENCE instance;
NeedSYMBOL();
instance.m_Path = KIID_PATH( FromUTF8() );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_reference:
NeedSYMBOL();
instance.m_Reference = FromUTF8();
NeedRIGHT();
break;
case T_unit:
instance.m_Unit = parseInt( "symbol unit" );
NeedRIGHT();
break;
case T_value:
NeedSYMBOL();
instance.m_Value = FromUTF8();
NeedRIGHT();
break;
case T_footprint:
NeedSYMBOL();
instance.m_Footprint = FromUTF8();
NeedRIGHT();
break;
default:
Expecting( "reference, unit, value or footprint" );
}
symbol->AddHierarchicalReference( instance );
}
}
break;
}
case T_property:
// The field parent symbol must be set and its orientation must be set before
// the field positions are set.
@ -2708,7 +2793,8 @@ SCH_SYMBOL* SCH_SEXPR_PARSER::parseSchematicSymbol()
break;
default:
Expecting( "lib_id, lib_name, at, mirror, uuid, property, pin, or instances" );
Expecting( "lib_id, lib_name, at, mirror, uuid, on_board, in_bom, dnp, "
"default_instance, property, pin, or instances" );
}
}

View File

@ -76,22 +76,6 @@ public:
*/
class SCH_SEXPR_PARSER : public SCHEMATIC_LEXER
{
int m_requiredVersion; ///< Set to the symbol library file version required.
int m_fieldId; ///< The current field ID.
int m_unit; ///< The current unit being parsed.
int m_convert; ///< The current body style being parsed.
wxString m_symbolName; ///< The current symbol name.
/// Field IDs that have been read so far for the current symbol.
std::set<int> m_fieldIDsRead;
std::set<KIID> m_uuids;
PROGRESS_REPORTER* m_progressReporter; // optional; may be nullptr
const LINE_READER* m_lineReader; // for progress reporting
unsigned m_lastProgressLine;
unsigned m_lineCount; // for progress reporting
void checkpoint();
KIID parseKIID();
@ -184,7 +168,8 @@ class SCH_SEXPR_PARSER : public SCHEMATIC_LEXER
public:
SCH_SEXPR_PARSER( LINE_READER* aLineReader = nullptr,
PROGRESS_REPORTER* aProgressReporter = nullptr, unsigned aLineCount = 0 );
PROGRESS_REPORTER* aProgressReporter = nullptr, unsigned aLineCount = 0,
SCH_SHEET* aRootSheet = nullptr, bool aIsAppending = false );
void ParseLib( LIB_SYMBOL_MAP& aSymbolLibMap );
@ -212,6 +197,28 @@ public:
*/
void ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyablyOnly = false,
int aFileVersion = SEXPR_SCHEMATIC_FILE_VERSION );
private:
int m_requiredVersion; ///< Set to the symbol library file version required.
int m_fieldId; ///< The current field ID.
int m_unit; ///< The current unit being parsed.
int m_convert; ///< The current body style being parsed.
wxString m_symbolName; ///< The current symbol name.
bool m_appending; ///< Appending load status.
/// Field IDs that have been read so far for the current symbol.
std::set<int> m_fieldIDsRead;
std::set<KIID> m_uuids;
PROGRESS_REPORTER* m_progressReporter; // optional; may be nullptr
const LINE_READER* m_lineReader; // for progress reporting
unsigned m_lastProgressLine;
unsigned m_lineCount; // for progress reporting
KIID m_rootUuid; // The UUID of the root schematic.
/// The rootsheet for full project loads or null for importing a schematic.
SCH_SHEET* m_rootSheet;
};
#endif // __SCH_SEXPR_PARSER_H__

View File

@ -88,6 +88,7 @@ SCH_SEXPR_PLUGIN::~SCH_SEXPR_PLUGIN()
void SCH_SEXPR_PLUGIN::init( SCHEMATIC* aSchematic, const PROPERTIES* aProperties )
{
m_version = 0;
m_appending = false;
m_rootSheet = nullptr;
m_schematic = aSchematic;
m_cache = nullptr;
@ -113,6 +114,7 @@ SCH_SHEET* SCH_SEXPR_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchema
if( aAppendToMe )
{
m_appending = true;
wxLogTrace( traceSchLegacyPlugin, "Append \"%s\" to sheet \"%s\".",
aFileName, aAppendToMe->GetFileName() );
@ -299,7 +301,7 @@ void SCH_SEXPR_PLUGIN::loadFile( const wxString& aFileName, SCH_SHEET* aSheet )
reader.Rewind();
}
SCH_SEXPR_PARSER parser( &reader, m_progressReporter, lineCount );
SCH_SEXPR_PARSER parser( &reader, m_progressReporter, lineCount, m_rootSheet, m_appending );
parser.ParseSchematic( aSheet );
}
@ -482,14 +484,14 @@ void SCH_SEXPR_PLUGIN::Format( SCH_SHEET* aSheet )
symbolInstances.SortByReferenceOnly();
saveInstances( sheetPaths.GetSheetInstances(), symbolInstances.GetSymbolInstances(), 1 );
saveInstances( sheetPaths.GetSheetInstances(), 1 );
}
else
{
// Schematic files (SCH_SCREEN objects) can be shared so we have to save and restore
// symbol and sheet instance data even if the file being saved is not the root sheet
// because it is possible that the file is the root sheet of another project.
saveInstances( screen->m_sheetInstances, screen->m_symbolInstances, 1 );
saveInstances( screen->m_sheetInstances, 1 );
}
m_out->Print( 0, ")\n" );
@ -621,7 +623,7 @@ void SCH_SEXPR_PLUGIN::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSelect
}
// Make all instance information relative to the selection path
KIID_PATH selectionPath = aSelectionPath->PathWithoutRootUuid();
KIID_PATH selectionPath = aSelectionPath->Path();
selectedSheets.SortByPageNumbers();
std::vector<SCH_SHEET_INSTANCE> sheetinstances = selectedSheets.GetSheetInstances();
@ -642,7 +644,7 @@ void SCH_SEXPR_PLUGIN::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSelect
"Symbol is not inside the selection path?" );
}
saveInstances( sheetinstances, symbolInstances, 0 );
saveInstances( sheetinstances, 0 );
}
@ -807,7 +809,24 @@ void SCH_SEXPR_PLUGIN::saveSymbol( SCH_SYMBOL* aSymbol, SCH_SHEET_PATH* aSheetPa
}
}
m_out->Print( aNestLevel, ")\n" );
m_out->Print( aNestLevel + 1, "(instances\n" );
for( const SYMBOL_INSTANCE_REFERENCE& instance : aSymbol->GetInstanceReferences() )
{
wxString path = instance.m_Path.AsString();
m_out->Print( aNestLevel + 2, "(path %s\n",
m_out->Quotew( path ).c_str() );
m_out->Print( aNestLevel + 3, "(reference %s) (unit %d) (value %s) (footprint %s)\n",
m_out->Quotew( instance.m_Reference ).c_str(),
instance.m_Unit,
m_out->Quotew( instance.m_Value ).c_str(),
m_out->Quotew( instance.m_Footprint ).c_str() );
m_out->Print( aNestLevel + 2, ")\n" );
}
m_out->Print( aNestLevel + 1, ")\n" ); // Closes `instances`.
m_out->Print( aNestLevel, ")\n" ); // Closes `symbol`.
}
@ -1210,7 +1229,6 @@ void SCH_SEXPR_PLUGIN::saveBusAlias( std::shared_ptr<BUS_ALIAS> aAlias, int aNes
void SCH_SEXPR_PLUGIN::saveInstances( const std::vector<SCH_SHEET_INSTANCE>& aSheets,
const std::vector<SYMBOL_INSTANCE_REFERENCE>& aSymbols,
int aNestLevel )
{
if( aSheets.size() )
@ -1232,26 +1250,6 @@ void SCH_SEXPR_PLUGIN::saveInstances( const std::vector<SCH_SHEET_INSTANCE>& aSh
m_out->Print( aNestLevel, ")\n" ); // Close sheet instances token.
}
if( aSymbols.size() )
{
m_out->Print( 0, "\n" );
m_out->Print( aNestLevel, "(symbol_instances\n" );
for( const SYMBOL_INSTANCE_REFERENCE& instance : aSymbols )
{
m_out->Print( aNestLevel + 1, "(path %s\n",
m_out->Quotew( instance.m_Path.AsString() ).c_str() );
m_out->Print( aNestLevel + 2, "(reference %s) (unit %d) (value %s) (footprint %s)\n",
m_out->Quotew( instance.m_Reference ).c_str(),
instance.m_Unit,
m_out->Quotew( instance.m_Value ).c_str(),
m_out->Quotew( instance.m_Footprint ).c_str() );
m_out->Print( aNestLevel + 1, ")\n" );
}
m_out->Print( aNestLevel, ")\n" ); // Close symbol instances token.
}
}

View File

@ -159,8 +159,7 @@ private:
void saveText( SCH_TEXT* aText, int aNestLevel );
void saveTextBox( SCH_TEXTBOX* aText, int aNestLevel );
void saveBusAlias( std::shared_ptr<BUS_ALIAS> aAlias, int aNestLevel );
void saveInstances( const std::vector<SCH_SHEET_INSTANCE>& aSheets,
const std::vector<SYMBOL_INSTANCE_REFERENCE>& aSymbols, int aNestLevel );
void saveInstances( const std::vector<SCH_SHEET_INSTANCE>& aSheets, int aNestLevel );
void cacheLib( const wxString& aLibraryFileName, const PROPERTIES* aProperties );
bool isBuffering( const PROPERTIES* aProperties );
@ -168,7 +167,7 @@ private:
protected:
int m_version; ///< Version of file being loaded.
int m_nextFreeFieldId;
bool m_appending; ///< Schematic load append status.
wxString m_error; ///< For throwing exceptions or errors on partial
///< loads.
PROGRESS_REPORTER* m_progressReporter;

View File

@ -72,6 +72,7 @@
SCH_LEGACY_PLUGIN::SCH_LEGACY_PLUGIN() :
m_appending( false ),
m_progressReporter( nullptr ),
m_lineReader( nullptr ),
m_lastProgressLine( 0 ),
@ -174,6 +175,7 @@ SCH_SHEET* SCH_LEGACY_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchem
}
else
{
m_appending = true;
wxCHECK_MSG( aSchematic->IsValid(), nullptr, "Can't append to a schematic with no root!" );
m_rootSheet = &aSchematic->Root();
sheet = aAppendToMe;
@ -1182,19 +1184,18 @@ SCH_SYMBOL* SCH_LEGACY_PLUGIN::loadSymbol( LINE_READER& aReader )
parseQuotedString( pathStr, aReader, line, &line );
// Note: AR path excludes root sheet, but includes symbol. Normalize to
// internal format by shifting everything down one and adding the root sheet.
// Note: AR path excludes root sheet, but includes symbol. Drop the symbol ID
// since it's already defined in the symbol itself.
KIID_PATH path( pathStr );
if( path.size() > 0 )
{
for( size_t i = path.size() - 1; i > 0; --i )
path[i] = path[i-1];
path.pop_back();
path[0] = m_rootSheet->m_Uuid;
}
else
path.push_back( m_rootSheet->m_Uuid );
// In the new file format, the root schematic UUID is used as the virtual SCH_SHEET
// UUID so we need to prefix it to the symbol path so the symbol instance paths
// get saved with the root schematic UUID.
if( !m_appending )
path.insert( path.begin(), m_rootSheet->GetScreen()->m_Uuid );
strCompare = "Ref=";
len = strlen( strCompare );

View File

@ -178,6 +178,9 @@ private:
protected:
int m_version; ///< Version of file being loaded.
///< Indicate if we are appending the loaded schemitic or loading a full project.
bool m_appending;
wxString m_error; ///< For throwing exceptions or errors on partial
///< schematic loads.
PROGRESS_REPORTER* m_progressReporter; ///< optional; may be nullptr

View File

@ -1645,6 +1645,10 @@ int SCH_SCREENS::ReplaceDuplicateTimeStamps()
// side-effects.
const_cast<KIID&>( item->m_Uuid ) = KIID();
count++;
// @todo If the item is a sheet, we need to decend the heirarchy from the sheet
// and repace all instances of the changed UUID in sheet paths. Otherwise,
// all instance paths with the sheet's UUID will get clobbered.
}
}
@ -1874,3 +1878,16 @@ void SCH_SCREENS::SetLegacySymbolInstanceData()
for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() )
screen->SetLegacySymbolInstanceData();
}
void SCH_SCREENS::SetAllSymbolDefaultInstances()
{
for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() )
{
if( screen->GetFileFormatVersionAtLoad() <= 20220622 )
{
if( screen->AllSymbolDefaultInstancesNotSet() )
screen->SetAllSymbolDefaultInstances();
}
}
}

View File

@ -739,6 +739,13 @@ public:
*/
void SetLegacySymbolInstanceData();
/**
* Set symbol default instances to the first instance in the instance list.
*
* @warning The schematic symbol instance data must be loaded before this method is called.
*/
void SetAllSymbolDefaultInstances();
private:
void addScreenToList( SCH_SCREEN* aScreen, SCH_SHEET* aSheet );
void buildScreenList( SCH_SHEET* aSheet);

View File

@ -1208,17 +1208,17 @@ bool SCH_SHEET::AddInstance( const SCH_SHEET_PATH& aSheetPath )
for( const SCH_SHEET_INSTANCE& instance : m_instances )
{
// if aSheetPath is found, nothing to do:
if( instance.m_Path == aSheetPath.PathWithoutRootUuid() )
if( instance.m_Path == aSheetPath.Path() )
return false;
}
wxLogTrace( traceSchSheetPaths, wxT( "Adding instance `%s` to sheet `%s`." ),
aSheetPath.PathWithoutRootUuid().AsString(),
aSheetPath.Path().AsString(),
( GetName().IsEmpty() ) ? wxT( "root" ) : GetName() );
SCH_SHEET_INSTANCE instance;
instance.m_Path = aSheetPath.PathWithoutRootUuid();
instance.m_Path = aSheetPath.Path();
// This entry does not exist: add it with an empty page number.
m_instances.emplace_back( instance );
@ -1231,7 +1231,7 @@ wxString SCH_SHEET::GetPageNumber( const SCH_SHEET_PATH& aSheetPath ) const
wxCHECK( aSheetPath.IsFullPath(), wxEmptyString );
wxString pageNumber;
KIID_PATH path = aSheetPath.PathWithoutRootUuid();
KIID_PATH path = aSheetPath.Path();
for( const SCH_SHEET_INSTANCE& instance : m_instances )
{
@ -1250,7 +1250,7 @@ void SCH_SHEET::SetPageNumber( const SCH_SHEET_PATH& aSheetPath, const wxString&
{
wxCHECK( aSheetPath.IsFullPath(), /* void */ );
KIID_PATH path = aSheetPath.PathWithoutRootUuid();
KIID_PATH path = aSheetPath.Path();
for( SCH_SHEET_INSTANCE& instance : m_instances )
{

View File

@ -283,17 +283,6 @@ KIID_PATH SCH_SHEET_PATH::Path() const
}
KIID_PATH SCH_SHEET_PATH::PathWithoutRootUuid() const
{
KIID_PATH path;
for( size_t i = 1; i < size(); i++ )
path.push_back( at( i )->m_Uuid );
return path;
}
wxString SCH_SHEET_PATH::PathHumanReadable( bool aUseShortRootName ) const
{
wxString s;
@ -536,6 +525,94 @@ void SCH_SHEET_PATH::SetPageNumber( const wxString& aPageNumber )
}
void SCH_SHEET_PATH::AddNewSymbolInstances( const SCH_SHEET_PATH& aPrefixSheetPath )
{
for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
{
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
wxCHECK2( symbol, continue );
SYMBOL_INSTANCE_REFERENCE newSymbolInstance;
SCH_SHEET_PATH newSheetPath( aPrefixSheetPath );
SCH_SHEET_PATH currentSheetPath( *this );
// Remove the root sheet from this path.
currentSheetPath.m_sheets.erase( currentSheetPath.m_sheets.begin() );
// Prefix the new hierarchical path.
newSheetPath = newSheetPath + currentSheetPath;
newSymbolInstance.m_Path = newSheetPath.Path();
if( symbol->GetInstance( newSymbolInstance, Path() ) )
{
// Use an existing symbol instance for this path if it exists.
newSymbolInstance.m_Path = newSheetPath.Path();
symbol->AddHierarchicalReference( newSymbolInstance );
}
else if( !symbol->GetInstanceReferences().empty() )
{
// Use the first symbol instance if any symbol exists.
newSymbolInstance = symbol->GetInstanceReferences()[0];
newSymbolInstance.m_Path = newSheetPath.Path();
symbol->AddHierarchicalReference( newSymbolInstance );
}
else if( symbol->GetLibSymbolRef() )
{
// Fall back to library symbol reference prefix, value, and footprint fields and
// set the unit to 1 if the library symbol is valid.
newSymbolInstance.m_Reference = symbol->GetLibSymbolRef()->GetReferenceField().GetText();
newSymbolInstance.m_Reference += wxT( "?" );
newSymbolInstance.m_Unit = 1;
newSymbolInstance.m_Value = symbol->GetLibSymbolRef()->GetValueField().GetText();
newSymbolInstance.m_Footprint = symbol->GetLibSymbolRef()->GetFootprintField().GetText();
symbol->AddHierarchicalReference( newSymbolInstance );
}
else
{
// All hope is lost so set the reference to 'U' and the unit to 1.
newSymbolInstance.m_Reference += wxT( "U?" );
newSymbolInstance.m_Unit = 1;
symbol->AddHierarchicalReference( newSymbolInstance );
}
}
}
int SCH_SHEET_PATH::AddNewSheetInstances( const SCH_SHEET_PATH& aPrefixSheetPath,
int aNextVirtualPageNumber )
{
int nextVirtualPageNumber = aNextVirtualPageNumber;
for( SCH_ITEM* item : LastScreen()->Items().OfType( SCH_SHEET_T ) )
{
SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
wxCHECK2( sheet, continue );
SCH_SHEET_PATH newSheetPath( aPrefixSheetPath );
SCH_SHEET_PATH currentSheetPath( *this );
// Remove the root sheet from current sheet path.
currentSheetPath.m_sheets.erase( currentSheetPath.m_sheets.begin() );
// Prefix the new hierarchical path.
newSheetPath = newSheetPath + currentSheetPath;
newSheetPath.push_back( sheet );
wxString pageNumber;
pageNumber.Printf( wxT( "%d" ), nextVirtualPageNumber );
sheet->AddInstance( newSheetPath );
newSheetPath.SetVirtualPageNumber( nextVirtualPageNumber );
newSheetPath.SetPageNumber( pageNumber );
nextVirtualPageNumber += 1;
}
return nextVirtualPageNumber;
}
void SCH_SHEET_PATH::MakeFilePathRelativeToParentSheet()
{
wxCHECK( m_sheets.size() > 1, /* void */ );
@ -980,51 +1057,37 @@ SCH_SHEET_LIST SCH_SHEET_LIST::FindAllSheetsForScreen( const SCH_SCREEN* aScreen
void SCH_SHEET_LIST::UpdateSymbolInstances(
const std::vector<SYMBOL_INSTANCE_REFERENCE>& aSymbolInstances )
{
SCH_REFERENCE_LIST symbolInstances;
GetSymbols( symbolInstances, true, true );
std::map<KIID_PATH, wxString> pathNameCache;
// Calculating the name of a path is somewhat expensive; on large designs with many symbols
// this can blow up to a serious amount of time when loading the schematic
auto getName = [&pathNameCache]( const KIID_PATH& aPath ) -> const wxString&
{
if( pathNameCache.count( aPath ) )
return pathNameCache.at( aPath );
pathNameCache[aPath] = aPath.AsString();
return pathNameCache[aPath];
};
for( size_t i = 0; i < symbolInstances.GetCount(); i++ )
for( SCH_SHEET_PATH& sheetPath : *this )
{
// The instance paths are stored in the file sans root path so the comparison
// should not include the root path.
wxString path = symbolInstances[i].GetPath();
auto it = std::find_if( aSymbolInstances.begin(), aSymbolInstances.end(),
[ path, &getName ]( const SYMBOL_INSTANCE_REFERENCE& r ) -> bool
{
return path == getName( r.m_Path );
} );
if( it == aSymbolInstances.end() )
for( SCH_ITEM* item : sheetPath.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
{
wxLogTrace( traceSchSheetPaths, "No symbol instance found for path '%s'", path );
continue;
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
wxCHECK2( symbol, continue );
KIID_PATH sheetPathWithSymbolUuid = sheetPath.Path();
sheetPathWithSymbolUuid.push_back( symbol->m_Uuid );
auto it = std::find_if( aSymbolInstances.begin(), aSymbolInstances.end(),
[ sheetPathWithSymbolUuid ]( const SYMBOL_INSTANCE_REFERENCE& r ) -> bool
{
return sheetPathWithSymbolUuid == r.m_Path;
} );
if( it == aSymbolInstances.end() )
{
wxLogTrace( traceSchSheetPaths, "No symbol instance found for symbol '%s'",
sheetPathWithSymbolUuid.AsString() );
continue;
}
// Symbol instance paths are stored and looked up in memory with the root path so use
// the full path here.
symbol->AddHierarchicalReference( sheetPath.Path(),
it->m_Reference, it->m_Unit, it->m_Value,
it->m_Footprint );
symbol->GetField( REFERENCE_FIELD )->SetText( it->m_Reference );
}
SCH_SYMBOL* symbol = symbolInstances[ i ].GetSymbol();
wxCHECK2( symbol, continue );
// Symbol instance paths are stored and looked up in memory with the root path so use
// the full path here.
symbol->AddHierarchicalReference( symbolInstances[i].GetSheetPath().Path(),
it->m_Reference, it->m_Unit, it->m_Value,
it->m_Footprint );
symbol->GetField( REFERENCE_FIELD )->SetText( it->m_Reference );
}
if( size() >= 1 && at( 0 ).LastScreen()->GetFileFormatVersionAtLoad() < 20220622 )
@ -1039,24 +1102,24 @@ void SCH_SHEET_LIST::UpdateSheetInstances( const std::vector<SCH_SHEET_INSTANCE>
{
SCH_SHEET* sheet = path.Last();
wxCHECK2( sheet, continue );
wxCHECK2( sheet && path.Last(), continue );
auto it = std::find_if( aSheetInstances.begin(), aSheetInstances.end(),
[ path ]( const SCH_SHEET_INSTANCE& r ) -> bool
{
return path.PathWithoutRootUuid() == r.m_Path;
return path.Last()->m_Uuid == r.m_Path.back();
} );
if( it == aSheetInstances.end() )
{
wxLogTrace( traceSchSheetPaths, "No sheet instance found for path '%s'",
path.PathWithoutRootUuid().AsString() );
path.Path().AsString() );
continue;
}
wxLogTrace( traceSchSheetPaths, "Setting sheet '%s' instance '%s' page number '%s'",
( sheet->GetName().IsEmpty() ) ? wxT( "root" ) : sheet->GetName(),
path.PathWithoutRootUuid().AsString(), it->m_PageNumber );
path.Path().AsString(), it->m_PageNumber );
sheet->AddInstance( path );
sheet->SetPageNumber( path, it->m_PageNumber );
}
@ -1085,7 +1148,7 @@ std::vector<SCH_SHEET_INSTANCE> SCH_SHEET_LIST::GetSheetInstances() const
wxCHECK2( sheet, continue );
SCH_SHEET_INSTANCE instance;
instance.m_Path = path.PathWithoutRootUuid();
instance.m_Path = path.Path();
instance.m_PageNumber = sheet->GetPageNumber( path );
retval.push_back( instance );
@ -1133,6 +1196,38 @@ void SCH_SHEET_LIST::SetInitialPageNumbers()
}
void SCH_SHEET_LIST::AddNewSymbolInstances( const SCH_SHEET_PATH& aPrefixSheetPath )
{
for( SCH_SHEET_PATH& sheetPath : *this )
sheetPath.AddNewSymbolInstances( aPrefixSheetPath );
}
void SCH_SHEET_LIST::AddNewSheetInstances( const SCH_SHEET_PATH& aPrefixSheetPath,
int aLastVirtualPageNumber )
{
int nextVirtualPageNumber = aLastVirtualPageNumber + 1;
for( SCH_SHEET_PATH& sheetPath : *this )
nextVirtualPageNumber = sheetPath.AddNewSheetInstances( aPrefixSheetPath,
nextVirtualPageNumber );
}
int SCH_SHEET_LIST::GetLastVirtualPageNumber() const
{
int lastVirtualPageNumber = 1;
for( const SCH_SHEET_PATH& sheetPath : *this )
{
if( sheetPath.GetVirtualPageNumber() > lastVirtualPageNumber )
lastVirtualPageNumber = sheetPath.GetVirtualPageNumber();
}
return lastVirtualPageNumber;
}
void SCH_SHEET_LIST::MigrateSimModelNameFields()
{
LOCALE_IO toggle;
@ -1141,8 +1236,8 @@ void SCH_SHEET_LIST::MigrateSimModelNameFields()
{
SCH_SCREEN* screen = at( sheetIndex ).LastScreen();
// V6 schematics may specify model names in Value fields, which we don't do in V7. Migrate by
// adding an equivalent model for these symbols.
// V6 schematics may specify model names in Value fields, which we don't do in V7.
// Migrate by adding an equivalent model for these symbols.
bool mayHaveModelsInValues = false;

View File

@ -265,13 +265,6 @@ public:
*/
KIID_PATH Path() const;
/**
* Get the sheet path as an #KIID_PATH without the root sheet UUID prefix.
*
* @note This #KIID_PATH does not include the root sheet UUID prefixed to the path.
*/
KIID_PATH PathWithoutRootUuid() const;
/**
* Return the sheet path in a human readable form made from the sheet names.
*
@ -373,6 +366,23 @@ public:
*/
void MakeFilePathRelativeToParentSheet();
/**
* Attempt to add new symbol instances for all symbols in this sheet path prefixed
* with \a aPrefixSheetPath.
*
* The new symbol instance data will be assigned by the following criteria:
* - If the instance data can be found for this sheet path, use the instance data.
* - If the instance data cannot be found for this sheet path and the instance data cache
* for the symbol is not empty, use the first instance data in the cache.
* - If the cache is empty and the library symbol link is valid, set the instance data
* from the library symbol.
* - If all else fails, set the reference to "U?", the unit to 1, and everything else to
* an empty string.
*/
void AddNewSymbolInstances( const SCH_SHEET_PATH& aPrefixSheetPath );
int AddNewSheetInstances( const SCH_SHEET_PATH& aPrefixSheetPath, int aNextVirtualPageNumber );
bool operator==( const SCH_SHEET_PATH& d1 ) const;
bool operator!=( const SCH_SHEET_PATH& d1 ) const { return !( *this == d1 ) ; }
@ -634,6 +644,19 @@ public:
*/
void MigrateSimModelNameFields();
/**
* Attempt to add new symbol instances for all symbols in this list of sheet paths prefixed
* with \a aPrefixSheetPath.
*
* @param aPrefixSheetPath is the sheet path to append the new symbol instances to.
*/
void AddNewSymbolInstances( const SCH_SHEET_PATH& aPrefixSheetPath );
void AddNewSheetInstances( const SCH_SHEET_PATH& aPrefixSheetPath,
int aLastVirtualPageNumber );
int GetLastVirtualPageNumber() const;
private:
SCH_SHEET_PATH m_currentSheetPath;
};

View File

@ -472,6 +472,22 @@ void SCH_SYMBOL::Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffse
}
bool SCH_SYMBOL::GetInstance( SYMBOL_INSTANCE_REFERENCE& aInstance,
const KIID_PATH& aSheetPath ) const
{
for( const SYMBOL_INSTANCE_REFERENCE& instance : m_instanceReferences )
{
if( instance.m_Path == aSheetPath )
{
aInstance = instance;
return true;
}
}
return false;
}
void SCH_SYMBOL::AddHierarchicalReference( const KIID_PATH& aPath, const wxString& aRef,
int aUnit, const wxString& aValue,
const wxString& aFootprint )
@ -501,15 +517,81 @@ void SCH_SYMBOL::AddHierarchicalReference( const KIID_PATH& aPath, const wxStrin
instance.m_Value = aValue;
instance.m_Footprint = aFootprint;
wxLogTrace( traceSchSheetPaths, "Adding symbol instance:\n"
" sheet path %s\n"
" reference %s, unit %d to symbol %s.",
aPath.AsString(),
aRef,
aUnit,
m_Uuid.AsString() );
wxLogTrace( traceSchSheetPaths,
"Adding symbol '%s' instance:\n"
" sheet path '%s'\n"
" reference '%s'\n"
" unit %d\n"
" value '%s'\n"
" footprint '%s'",
m_Uuid.AsString(),
aPath.AsString(),
aRef,
aUnit,
aValue,
aFootprint );
m_instanceReferences.push_back( instance );
// This should set the default instance to the first saved instance data for each symbol
// when importing sheets.
if( m_instanceReferences.size() == 1 )
{
m_fields[ REFERENCE_FIELD ].SetText( aRef );
m_fields[ VALUE_FIELD ].SetText( aValue );
m_unit = aUnit;
m_fields[ FOOTPRINT_FIELD ].SetText( aFootprint );
}
}
void SCH_SYMBOL::AddHierarchicalReference( const SYMBOL_INSTANCE_REFERENCE& aInstance )
{
// Search for an existing path and remove it if found (should not occur)
for( unsigned ii = 0; ii < m_instanceReferences.size(); ii++ )
{
if( m_instanceReferences[ii].m_Path == aInstance.m_Path )
{
wxLogTrace( traceSchSheetPaths, "Removing symbol instance:\n"
" sheet path %s\n"
" reference %s, unit %d from symbol %s.",
aInstance.m_Path.AsString(),
m_instanceReferences[ii].m_Reference,
m_instanceReferences[ii].m_Unit,
m_Uuid.AsString() );
m_instanceReferences.erase( m_instanceReferences.begin() + ii );
ii--;
}
}
SYMBOL_INSTANCE_REFERENCE instance = aInstance;
wxLogTrace( traceSchSheetPaths,
"Adding symbol '%s' instance:\n"
" sheet path '%s'\n"
" reference '%s'\n"
" unit %d\n"
" value '%s'\n"
" footprint '%s'",
m_Uuid.AsString(),
instance.m_Path.AsString(),
instance.m_Reference,
instance.m_Unit,
instance.m_Value,
instance.m_Footprint );
m_instanceReferences.push_back( instance );
// This should set the default instance to the first saved instance data for each symbol
// when importing sheets.
if( m_instanceReferences.size() == 1 )
{
m_fields[ REFERENCE_FIELD ].SetText( instance.m_Reference );
m_fields[ VALUE_FIELD ].SetText( instance.m_Value );
m_unit = instance.m_Unit;
m_fields[ FOOTPRINT_FIELD ].SetText( instance.m_Footprint );
}
}
@ -1218,7 +1300,7 @@ void SCH_SYMBOL::ClearAnnotation( const SCH_SHEET_PATH* aSheetPath, bool aResetP
bool SCH_SYMBOL::AddSheetPathReferenceEntryIfMissing( const KIID_PATH& aSheetPath )
{
// a empty sheet path is illegal:
// An empty sheet path is illegal, at a minimum the root sheet UUID must be present.
wxCHECK( aSheetPath.size() > 0, false );
for( const SYMBOL_INSTANCE_REFERENCE& instance : m_instanceReferences )

View File

@ -51,6 +51,7 @@
#include <transform.h>
struct PICKED_SYMBOL;
class KIID_PATH;
class SCH_SCREEN;
class LIB_ITEM;
class LIB_PIN;
@ -140,6 +141,9 @@ public:
return m_instanceReferences;
}
bool GetInstance( SYMBOL_INSTANCE_REFERENCE& aInstance,
const KIID_PATH& aSheetPath ) const;
void SetDefaultInstance( const SYMBOL_INSTANCE_REFERENCE& aInstance )
{
m_defaultInstance = aInstance;
@ -591,6 +595,8 @@ public:
const wxString& aValue = wxEmptyString,
const wxString& aFootprint = wxEmptyString );
void AddHierarchicalReference( const SYMBOL_INSTANCE_REFERENCE& aInstance );
/// Return the instance-specific unit selection for the given sheet path.
int GetUnitSelection( const SCH_SHEET_PATH* aSheet ) const;
@ -713,10 +719,11 @@ public:
void Plot( PLOTTER* aPlotter, bool aBackground ) const override;
/**
* Plot just the symbol pins. This is separated to match the GAL display order. The pins are
* ALSO plotted with the symbol group. This replotting allows us to ensure that they are shown above
* other elements in the schematic
* @param aPlotter
* Plot just the symbol pins. This is separated to match the GAL display order. The pins
* are ALSO plotted with the symbol group. This replotting allows us to ensure that they
* are shown above other elements in the schematic.
*
* @param aPlotter is the #PLOTTER object used to plot pins.
*/
void PlotPins( PLOTTER* aPlotter ) const;

View File

@ -51,6 +51,7 @@ id
image
input
input_low
instances
inverted
inverted_clock
in_bom

View File

@ -182,12 +182,29 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHier
if( wxFileName::GetPathSeparator() == '\\' )
topLevelSheetPath.Replace( "\\", "/" );
// Update the paths to the loaded sheets.
std::vector< SCH_ITEM* > sheetsInLoadedSchematic;
newSheet->GetScreen()->GetSheets( &sheetsInLoadedSchematic );
for( SCH_ITEM* item : sheetsInLoadedSchematic )
{
SCH_SHEET* sheet = static_cast< SCH_SHEET* >( item );
wxCHECK2( sheet, continue );
wxFileName loadedSheetFileName = sheet->GetFileName();
wxString newSheetFilePath = topLevelSheetPath + loadedSheetFileName.GetFullName();
sheet->SetFileName( newSheetFilePath );
}
}
// Make sure any new sheet changes do not cause any recursion issues.
SCH_SHEET_LIST hierarchy = Schematic().GetSheets(); // This is the schematic sheet hierarchy.
SCH_SHEET_LIST sheetHierarchy( newSheet.get() ); // This is the hierarchy of the loaded file.
SCH_SHEET_LIST hierarchy = Schematic().GetSheets(); // This is the schematic sheet hierarchy.
// Make sure any new sheet changes do not cause any recursion issues.
if( CheckSheetForRecursion( newSheet.get(), aHierarchy )
|| checkForNoFullyDefinedLibIds( newSheet.get() ) )
{
@ -206,6 +223,29 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHier
wxMessageDialog::ButtonLabel okButtonLabel( _( "Continue Load" ) );
wxMessageDialog::ButtonLabel cancelButtonLabel( _( "Cancel Load" ) );
// Prior to schematic file format 20220919, all symbol instance data was saved in the root
// sheet so loading a hierarchical sheet that is not the root sheet will have no symbol
// instance data. Give the user a chance to go back and save the project that contains this
// hierarchical sheet so the symbol instance data will be correct on load.
if( ( newSheet->GetScreen()->GetFileFormatVersionAtLoad() < 20220919 )
&& newSheet->GetScreen()->GetSymbolInstances().empty() )
{
msg = _( "There hierarchical sheets in the loaded schematic file from an older "
"file version resulting in missing symbol instance data. This will "
"result in all of the symbols in the loaded schematic to use either the "
"default instance setting or fall back to the library symbol settings. "
"Loading the project that uses this schematic file and saving to the "
"lastest file version will resolve this issue.\n\n"
"Do you wish to continue?" );
wxMessageDialog msgDlg7( this, msg, _( "Continue Load Schematic" ),
wxOK | wxCANCEL | wxCANCEL_DEFAULT |
wxCENTER | wxICON_QUESTION );
msgDlg7.SetOKCancelLabels( okButtonLabel, cancelButtonLabel );
if( msgDlg7.ShowModal() == wxID_CANCEL )
return false;
}
if( !prjScreens.HasSchematic( fullFilename ) )
{
if( fileName.GetPathWithSep() == Prj().GetProjectPath() )
@ -445,6 +485,29 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHier
SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
}
// Make the best attempt to set the symbol instance data for the loaded scheamtic.
if( newScreen->GetFileFormatVersionAtLoad() < 20220919 )
{
if( !newScreen->GetSymbolInstances().empty() )
{
// If the loaded schematic is a root sheet for another project, update the symbol
// instances.
sheetHierarchy.UpdateSymbolInstances( newScreen->GetSymbolInstances() );
}
else
{
// Otherwise, fall back to the default instance data which still exists in this
// schematic file version.
newScreens.SetAllSymbolDefaultInstances();
}
}
// Attempt to create new symbol instances using the instance data loaded above.
sheetHierarchy.AddNewSymbolInstances( *aHierarchy );
// Add new sheet instance data.
sheetHierarchy.AddNewSheetInstances( *aHierarchy, hierarchy.GetLastVirtualPageNumber() );
// It is finally safe to add or append the imported schematic.
if( aSheet->GetScreen() == nullptr )
aSheet->SetScreen( newScreen );