KiCad PCB EDA Suite
Loading...
Searching...
No Matches
schematic_file_util.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.TXT for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <schematic_file_util.h>
21#include <qa_utils/wx_utils/unit_test_utils.h> // GetEeschemaTestDataDir()
22
24
25#include <connection_graph.h>
26#include <project.h>
27#include <schematic.h>
28#include <sch_screen.h>
29
30// For SCH parsing
33#include <richio.h>
34
36
37namespace KI_TEST
38{
39
40void DumpSchematicToFile( SCHEMATIC& aSchematic, SCH_SHEET& aSheet, const std::string& aFilename )
41{
43 io.SaveSchematicFile( aFilename, &aSheet, &aSchematic );
44}
45
46void LoadSheetSchematicContents( const std::string& fileName, SCH_SHEET* sheet )
47{
48 std::ifstream fileStream;
49 fileStream.open( fileName );
50 wxASSERT( fileStream.is_open() );
52 reader.SetStream( fileStream );
53 SCH_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, 0, sheet );
54 try
55 {
56 parser.ParseSchematic( sheet );
57 }
58 catch( const std::exception& e )
59 {
60 // Re-throw; Boost will report std::exception types normally.
61 throw;
62 }
63 catch( ... )
64 {
65 // Wrap unknown exception types so the test harness reports a useful message.
66 throw std::runtime_error( "LoadSheetSchematicContents: non-std exception during ParseSchematic" );
67 }
68}
69
70void LoadHierarchy( SCHEMATIC* schematic, SCH_SHEET* sheet, const std::string& sheetFilename,
71 std::unordered_map<std::string, SCH_SCREEN*>& parsedScreens )
72{
73 SCH_SCREEN* screen = nullptr;
74
75 if( !sheet->GetScreen() )
76 {
77 // Construct paths
78 const wxFileName fileName( sheetFilename );
79 const std::string filePath( fileName.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR ) );
80 const std::string fileBareName( fileName.GetFullName() );
81
82 // Check for existing screen
83 auto screenFound = parsedScreens.find( fileBareName );
84 if( screenFound != parsedScreens.end() )
85 screen = screenFound->second;
86
87 // Configure sheet with existing screen, or load screen
88 if( screen )
89 {
90 // Screen already loaded - assign to sheet
91 sheet->SetScreen( screen );
92 sheet->GetScreen()->SetParent( schematic );
93 }
94 else
95 {
96 // Load screen and assign to sheet
97 screen = new SCH_SCREEN( schematic );
98 parsedScreens.insert( { fileBareName, screen } );
99 sheet->SetScreen( screen );
100 sheet->GetScreen()->SetFileName( sheetFilename );
101 LoadSheetSchematicContents( sheetFilename, sheet );
102 }
103
104 // Recurse through child sheets
105 for( SCH_ITEM* item : sheet->GetScreen()->Items().OfType( SCH_SHEET_T ) )
106 {
107 SCH_SHEET* childSheet = static_cast<SCH_SHEET*>( item );
108 wxFileName childSheetFilename = childSheet->GetFileName();
109 if( !childSheetFilename.IsAbsolute() )
110 childSheetFilename.MakeAbsolute( filePath );
111 std::string childSheetFullFilename( childSheetFilename.GetFullPath() );
112 LoadHierarchy( schematic, childSheet, childSheetFullFilename, parsedScreens );
113 }
114 }
115}
116
117std::unique_ptr<SCHEMATIC> LoadHierarchyFromRoot( const std::string& rootFilename,
119{
120 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
121 std::unordered_map<std::string, SCH_SCREEN*> parsedScreens;
122
123 schematic->SetProject( project );
124 schematic->Reset();
125 SCH_SHEET* defaultSheet = schematic->GetTopLevelSheet( 0 );
126
127 SCH_SHEET* rootSheet = new SCH_SHEET( schematic.get() );
128 LoadHierarchy( schematic.get(), rootSheet, rootFilename, parsedScreens );
129 schematic->AddTopLevelSheet( rootSheet );
130 schematic->RemoveTopLevelSheet( defaultSheet );
131 delete defaultSheet;
132
133 return schematic;
134}
135
136std::unique_ptr<SCHEMATIC> ReadSchematicFromStream( std::istream& aStream, PROJECT* aProject )
137{
138 std::unique_ptr<SCHEMATIC> schematic( new SCHEMATIC( nullptr ) );
139 schematic->SetProject( aProject );
140 SCH_SHEET* rootSheet = new SCH_SHEET( schematic.get() );
141 SCH_SCREEN* screen = new SCH_SCREEN( schematic.get() );
142 rootSheet->SetScreen( screen );
143 screen->SetParent( schematic.get() );
144 schematic->SetTopLevelSheets( { rootSheet } );
145
146 // Parse from provided stream using existing parser infra
148 reader.SetStream( aStream );
149 SCH_IO_KICAD_SEXPR_PARSER parser( &reader, nullptr, 0, rootSheet );
150 parser.ParseSchematic( rootSheet );
151
152 // Link symbol instances like LoadSchematic does (single sheet case)
153 rootSheet->GetScreen()->UpdateLocalLibSymbolLinks();
154 SCH_SHEET_LIST sheets = schematic->BuildSheetListSortedByPageNumbers();
155 sheets.UpdateSymbolInstanceData( schematic->RootScreen()->GetSymbolInstances() );
156 sheets.UpdateSheetInstanceData( schematic->RootScreen()->GetSheetInstances() );
157 sheets.AnnotatePowerSymbols();
158 for( SCH_SHEET_PATH& sheet : sheets )
159 sheet.UpdateAllScreenReferences();
160
161 // The IO layer normally pipes parsed override maps into the connection graph;
162 // this reload-only path has to do the same so manual chains survive a reload.
163 if( schematic->ConnectionGraph() )
164 {
165 schematic->ConnectionGraph()->SetNetChainNetClassOverrides( parser.GetNetChainNetClasses() );
166 schematic->ConnectionGraph()->SetNetChainColorOverrides( parser.GetNetChainColors() );
167
168 std::map<wxString, CONNECTION_GRAPH::CHAIN_TERMINAL_REFS> termRefs;
169
170 for( const auto& [name, terms] : parser.GetNetChainTerminalRefs() )
171 {
172 termRefs[name] = { { terms.first.ref, terms.first.pin },
173 { terms.second.ref, terms.second.pin } };
174 }
175
176 schematic->ConnectionGraph()->SetNetChainTerminalRefOverrides( termRefs );
177 schematic->ConnectionGraph()->SetNetChainMemberNetOverrides( parser.GetNetChainMemberNets() );
178 }
179
180 return schematic;
181}
182
183
184void LoadSchematic( SETTINGS_MANAGER& aSettingsManager, const wxString& aRelPath,
185 std::unique_ptr<SCHEMATIC>& aSchematic )
186{
187 if( aSchematic )
188 {
189 PROJECT* prj = &aSchematic->Project();
190
191 aSchematic->SetProject( nullptr );
192 aSettingsManager.UnloadProject( prj, false );
193 aSchematic->Reset();
194 }
195
196 std::string absPath = GetEeschemaTestDataDir() + aRelPath.ToStdString();
197 wxFileName projectFile( absPath + ".kicad_pro" );
198 wxFileName legacyProject( absPath + ".pro" );
199 std::string schematicPath = absPath + ".kicad_sch";
200
201 if( projectFile.Exists() )
202 aSettingsManager.LoadProject( projectFile.GetFullPath() );
203 else if( legacyProject.Exists() )
204 aSettingsManager.LoadProject( legacyProject.GetFullPath() );
205 else
206 aSettingsManager.LoadProject( "" );
207
208 aSettingsManager.Prj().SetElem( PROJECT::ELEM::LEGACY_SYMBOL_LIBS, nullptr );
209
210 aSchematic = LoadHierarchyFromRoot( schematicPath, &aSettingsManager.Prj() );
211
212 SCH_SCREENS screens( aSchematic->Root() );
213
214 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
215 screen->UpdateLocalLibSymbolLinks();
216
217 SCH_SHEET_LIST sheets = aSchematic->BuildSheetListSortedByPageNumbers();
218
219 // Restore all of the loaded symbol instances from the root sheet screen.
220 sheets.UpdateSymbolInstanceData( aSchematic->RootScreen()->GetSymbolInstances() );
221 sheets.UpdateSheetInstanceData( aSchematic->RootScreen()->GetSheetInstances() );
222
223 if( aSchematic->RootScreen()->GetFileFormatVersionAtLoad() < 20230221 )
225
226 if( aSchematic->RootScreen()->GetFileFormatVersionAtLoad() < 20221206 )
227 {
228 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
229 screen->MigrateSimModels();
230 }
231
232
233 sheets.AnnotatePowerSymbols();
234
235 // NOTE: This is required for multi-unit symbols to be correct
236 // Normally called from SCH_EDIT_FRAME::FixupJunctions() but could be refactored
237 for( SCH_SHEET_PATH& sheet : sheets )
238 sheet.UpdateAllScreenReferences();
239
240 // NOTE: SchematicCleanUp is not called; QA schematics must already be clean or else
241 // SchematicCleanUp must be freed from its UI dependencies.
242
243 aSchematic->ConnectionGraph()->Recalculate( sheets, true );
244}
245
246} // namespace KI_TEST
const char * name
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:89
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:221
Container for project specific data.
Definition project.h:62
virtual void SetElem(PROJECT::ELEM aIndex, _ELEM *aElem)
Definition project.cpp:396
@ LEGACY_SYMBOL_LIBS
Definition project.h:69
Holds all the data relating to one schematic.
Definition schematic.h:90
Object to parser s-expression symbol library and schematic file formats.
const std::map< wxString, wxString > & GetNetChainNetClasses() const
const std::map< wxString, COLOR4D > & GetNetChainColors() const
void ParseSchematic(SCH_SHEET *aSheet, bool aIsCopyablyOnly=false, int aFileVersion=SEXPR_SCHEMATIC_FILE_VERSION)
Parse the internal LINE_READER object into aSheet.
const std::map< wxString, CHAIN_TERMINALS > & GetNetChainTerminalRefs() const
const std::map< wxString, std::set< wxString > > & GetNetChainMemberNets() const
A SCH_IO derivation for loading schematic files using the new s-expression file format.
void SaveSchematicFile(const wxString &aFileName, SCH_SHEET *aSheet, SCHEMATIC *aSchematic, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aSchematic to a storage file in a format that this SCH_IO implementation knows about,...
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:746
SCH_SCREEN * GetNext()
void FixLegacyPowerSymbolMismatches()
Fix legacy power symbols that have mismatched value text fields and invisible power pin names.
SCH_SCREEN * GetFirst()
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
void UpdateLocalLibSymbolLinks()
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in this schematic with the local projec...
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void UpdateSheetInstanceData(const std::vector< SCH_SHEET_INSTANCE > &aSheetInstances)
Update all of the sheet instance information using aSheetInstances.
void AnnotatePowerSymbols()
Silently annotate the not yet annotated power symbols of the entire hierarchy of the sheet path list.
void UpdateSymbolInstanceData(const std::vector< SCH_SYMBOL_INSTANCE > &aSymbolInstances)
Update all of the symbol instance information using aSymbolInstances.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:370
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:139
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Save, unload and unregister the given PROJECT.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
LINE_READER that wraps a given std::istream instance.
void SetStream(std::istream &aStream)
Set the stream for this line reader.
std::unique_ptr< SCHEMATIC > ReadSchematicFromStream(std::istream &aStream, PROJECT *aProject)
std::unique_ptr< SCHEMATIC > LoadHierarchyFromRoot(const std::string &rootFilename, PROJECT *project)
void LoadSheetSchematicContents(const std::string &fileName, SCH_SHEET *sheet)
void LoadHierarchy(SCHEMATIC *schematic, SCH_SHEET *sheet, const std::string &sheetFilename, std::unordered_map< std::string, SCH_SCREEN * > &parsedScreens)
void LoadSchematic(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< SCHEMATIC > &aSchematic)
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
void DumpSchematicToFile(SCHEMATIC &aSchematic, SCH_SHEET &aSheet, const std::string &aFilename)
@ SCH_SHEET_T
Definition typeinfo.h:172