1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <bus_alias.h>
21 #include <connection_graph.h>
22 #include <erc_settings.h>
23 #include <sch_marker.h>
24 #include <project.h>
25 #include <project/project_file.h>
26 #include <project/net_settings.h>
27 #include <schematic.h>
28 #include <sch_screen.h>
29 #include <sim/spice_settings.h>
30
31
SCHEMATIC(PROJECT * aPrj)32 SCHEMATIC::SCHEMATIC( PROJECT* aPrj ) :
33 EDA_ITEM( nullptr, SCHEMATIC_T ),
34 m_project( nullptr ),
35 m_rootSheet( nullptr )
36 {
37 m_currentSheet = new SCH_SHEET_PATH();
38 m_connectionGraph = new CONNECTION_GRAPH( this );
39
40 SetProject( aPrj );
41 }
42
43
~SCHEMATIC()44 SCHEMATIC::~SCHEMATIC()
45 {
46 delete m_currentSheet;
47 delete m_connectionGraph;
48 }
49
50
Reset()51 void SCHEMATIC::Reset()
52 {
53 // Assume project already saved
54 if( m_project )
55 {
56 PROJECT_FILE& project = m_project->GetProjectFile();
57
58 delete project.m_ErcSettings;
59 delete project.m_SchematicSettings;
60
61 project.m_ErcSettings = nullptr;
62 project.m_SchematicSettings = nullptr;
63
64 m_project = nullptr; // clear the project, so we don't do this again when setting a new one
65 }
66
67 delete m_rootSheet;
68
69 m_rootSheet = nullptr;
70
71 m_connectionGraph->Reset();
72 m_currentSheet->clear();
73 }
74
75
SetProject(PROJECT * aPrj)76 void SCHEMATIC::SetProject( PROJECT* aPrj )
77 {
78 if( m_project )
79 {
80 PROJECT_FILE& project = m_project->GetProjectFile();
81
82 delete project.m_ErcSettings;
83 delete project.m_SchematicSettings;
84
85 project.m_ErcSettings = nullptr;
86 project.m_SchematicSettings = nullptr;
87 }
88
89 m_project = aPrj;
90
91 if( m_project )
92 {
93 PROJECT_FILE& project = m_project->GetProjectFile();
94 project.m_ErcSettings = new ERC_SETTINGS( &project, "erc" );
95 project.m_SchematicSettings = new SCHEMATIC_SETTINGS( &project, "schematic" );
96
97 project.m_SchematicSettings->LoadFromFile();
98 project.m_SchematicSettings->m_NgspiceSimulatorSettings->LoadFromFile();
99 project.m_ErcSettings->LoadFromFile();
100 }
101 }
102
103
SetRoot(SCH_SHEET * aRootSheet)104 void SCHEMATIC::SetRoot( SCH_SHEET* aRootSheet )
105 {
106 wxCHECK_RET( aRootSheet, "Call to SetRoot with null SCH_SHEET!" );
107
108 m_rootSheet = aRootSheet;
109
110 m_currentSheet->clear();
111 m_currentSheet->push_back( m_rootSheet );
112
113 m_connectionGraph->Reset();
114 }
115
116
RootScreen() const117 SCH_SCREEN* SCHEMATIC::RootScreen() const
118 {
119 return IsValid() ? m_rootSheet->GetScreen() : nullptr;
120 }
121
122
ResolveTextVar(wxString * token,int aDepth) const123 bool SCHEMATIC::ResolveTextVar( wxString* token, int aDepth ) const
124 {
125 if( !CurrentSheet().empty() )
126 {
127 if( token->IsSameAs( wxT( "#" ) ) )
128 {
129 *token = CurrentSheet().GetPageNumber();
130 return true;
131 }
132 else if( token->IsSameAs( wxT( "##" ) ) )
133 {
134 *token = wxString::Format( "%i", Root().CountSheets() );
135 return true;
136 }
137 else if( token->IsSameAs( wxT( "SHEETNAME" ) ) )
138 {
139 *token = CurrentSheet().PathHumanReadable();
140 return true;
141 }
142 else if( token->IsSameAs( wxT( "FILENAME" ) ) )
143 {
144 wxFileName fn( GetFileName() );
145 *token = fn.GetFullName();
146 return true;
147 }
148 else if( token->IsSameAs( wxT( "PROJECTNAME" ) ) )
149 {
150 *token = Prj().GetProjectName();
151 return true;
152 }
153
154 return CurrentSheet().LastScreen()->GetTitleBlock().TextVarResolver( token, m_project );
155 }
156
157 return false;
158 }
159
160
GetFileName() const161 wxString SCHEMATIC::GetFileName() const
162 {
163 return IsValid() ? m_rootSheet->GetScreen()->GetFileName() : wxString( wxEmptyString );
164 }
165
166
Settings() const167 SCHEMATIC_SETTINGS& SCHEMATIC::Settings() const
168 {
169 wxASSERT( m_project );
170 return *m_project->GetProjectFile().m_SchematicSettings;
171 }
172
173
ErcSettings() const174 ERC_SETTINGS& SCHEMATIC::ErcSettings() const
175 {
176 wxASSERT( m_project );
177 return *m_project->GetProjectFile().m_ErcSettings;
178 }
179
180
ResolveERCExclusions()181 std::vector<SCH_MARKER*> SCHEMATIC::ResolveERCExclusions()
182 {
183 SCH_SHEET_LIST sheetList = GetSheets();
184 ERC_SETTINGS& settings = ErcSettings();
185
186 for( const SCH_SHEET_PATH& sheet : sheetList )
187 {
188 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_MARKER_T ) )
189 {
190 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
191 auto it = settings.m_ErcExclusions.find( marker->Serialize() );
192
193 if( it != settings.m_ErcExclusions.end() )
194 {
195 marker->SetExcluded( true );
196 settings.m_ErcExclusions.erase( it );
197 }
198 }
199 }
200
201 std::vector<SCH_MARKER*> newMarkers;
202
203 for( const wxString& exclusionData : settings.m_ErcExclusions )
204 {
205 SCH_MARKER* marker = SCH_MARKER::Deserialize( exclusionData );
206
207 if( marker )
208 {
209 marker->SetExcluded( true );
210 newMarkers.push_back( marker );
211 }
212 }
213
214 settings.m_ErcExclusions.clear();
215
216 return newMarkers;
217 }
218
219
GetBusAlias(const wxString & aLabel) const220 std::shared_ptr<BUS_ALIAS> SCHEMATIC::GetBusAlias( const wxString& aLabel ) const
221 {
222 for( const auto& sheet : GetSheets() )
223 {
224 for( const auto& alias : sheet.LastScreen()->GetBusAliases() )
225 {
226 if( alias->GetName() == aLabel )
227 return alias;
228 }
229 }
230
231 return nullptr;
232 }
233
234
GetNetClassAssignmentCandidates()235 std::vector<wxString> SCHEMATIC::GetNetClassAssignmentCandidates()
236 {
237 std::vector<wxString> names;
238
239 // Key is a NET_NAME_CODE aka std::pair<name, code>
240 for( const NET_MAP::value_type& pair: m_connectionGraph->GetNetMap() )
241 {
242 CONNECTION_SUBGRAPH* subgraph = pair.second[0];
243
244 if( !subgraph->m_driver_connection->IsBus()
245 && subgraph->GetDriverPriority() >= CONNECTION_SUBGRAPH::PRIORITY::PIN )
246 {
247 names.emplace_back( pair.first.first );
248 }
249 }
250
251 return names;
252 }
253
254
ResolveCrossReference(wxString * token,int aDepth) const255 bool SCHEMATIC::ResolveCrossReference( wxString* token, int aDepth ) const
256 {
257 SCH_SHEET_LIST sheetList = GetSheets();
258 wxString remainder;
259 wxString ref = token->BeforeFirst( ':', &remainder );
260 SCH_SHEET_PATH sheetPath;
261 SCH_ITEM* refItem = sheetList.GetItem( KIID( ref ), &sheetPath );
262
263 if( refItem && refItem->Type() == SCH_SYMBOL_T )
264 {
265 SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
266
267 if( refSymbol->ResolveTextVar( &remainder, aDepth + 1 ) )
268 *token = remainder;
269 else
270 *token = refSymbol->GetRef( &sheetPath, true ) + ":" + remainder;
271
272 return true; // Cross-reference is resolved whether or not the actual textvar was
273 }
274 else if( refItem && refItem->Type() == SCH_SHEET_T )
275 {
276 SCH_SHEET* refSheet = static_cast<SCH_SHEET*>( refItem );
277
278 if( refSheet->ResolveTextVar( &remainder, aDepth + 1 ) )
279 *token = remainder;
280
281 return true; // Cross-reference is resolved whether or not the actual textvar was
282 }
283
284 return false;
285 }
286
287
ConvertRefsToKIIDs(const wxString & aSource) const288 wxString SCHEMATIC::ConvertRefsToKIIDs( const wxString& aSource ) const
289 {
290 wxString newbuf;
291 size_t sourceLen = aSource.length();
292
293 for( size_t i = 0; i < sourceLen; ++i )
294 {
295 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
296 {
297 wxString token;
298 bool isCrossRef = false;
299
300 for( i = i + 2; i < sourceLen; ++i )
301 {
302 if( aSource[i] == '}' )
303 break;
304
305 if( aSource[i] == ':' )
306 isCrossRef = true;
307
308 token.append( aSource[i] );
309 }
310
311 if( isCrossRef )
312 {
313 SCH_SHEET_LIST sheetList = GetSheets();
314 wxString remainder;
315 wxString ref = token.BeforeFirst( ':', &remainder );
316 SCH_REFERENCE_LIST references;
317
318 sheetList.GetSymbols( references );
319
320 for( size_t jj = 0; jj < references.GetCount(); jj++ )
321 {
322 SCH_SYMBOL* refSymbol = references[ jj ].GetSymbol();
323
324 if( ref == refSymbol->GetRef( &references[ jj ].GetSheetPath(), true ) )
325 {
326 token = refSymbol->m_Uuid.AsString() + ":" + remainder;
327 break;
328 }
329 }
330 }
331
332 newbuf.append( "${" + token + "}" );
333 }
334 else
335 {
336 newbuf.append( aSource[i] );
337 }
338 }
339
340 return newbuf;
341 }
342
343
ConvertKIIDsToRefs(const wxString & aSource) const344 wxString SCHEMATIC::ConvertKIIDsToRefs( const wxString& aSource ) const
345 {
346 wxString newbuf;
347 size_t sourceLen = aSource.length();
348
349 for( size_t i = 0; i < sourceLen; ++i )
350 {
351 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
352 {
353 wxString token;
354 bool isCrossRef = false;
355
356 for( i = i + 2; i < sourceLen; ++i )
357 {
358 if( aSource[i] == '}' )
359 break;
360
361 if( aSource[i] == ':' )
362 isCrossRef = true;
363
364 token.append( aSource[i] );
365 }
366
367 if( isCrossRef )
368 {
369 SCH_SHEET_LIST sheetList = GetSheets();
370 wxString remainder;
371 wxString ref = token.BeforeFirst( ':', &remainder );
372
373 SCH_SHEET_PATH refSheetPath;
374 SCH_ITEM* refItem = sheetList.GetItem( KIID( ref ), &refSheetPath );
375
376 if( refItem && refItem->Type() == SCH_SYMBOL_T )
377 {
378 SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
379 token = refSymbol->GetRef( &refSheetPath, true ) + ":" + remainder;
380 }
381 }
382
383 newbuf.append( "${" + token + "}" );
384 }
385 else
386 {
387 newbuf.append( aSource[i] );
388 }
389 }
390
391 return newbuf;
392 }
393
394
GetFullHierarchy() const395 SCH_SHEET_LIST& SCHEMATIC::GetFullHierarchy() const
396 {
397 static SCH_SHEET_LIST hierarchy;
398
399 hierarchy.clear();
400 hierarchy.BuildSheetList( m_rootSheet, false );
401
402 return hierarchy;
403 }
404