KiCad PCB EDA Suite
Loading...
Searching...
No Matches
component_class_manager.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 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
22
23#include <board.h>
27#include <drc/drc_rule_parser.h>
28#include <footprint.h>
30#include <tools/drc_tool.h>
31
32
34 m_board( board )
35{
36 m_noneClass = std::make_shared<COMPONENT_CLASS>( wxEmptyString, COMPONENT_CLASS::USAGE::STATIC );
37}
38
39
52 const std::unordered_set<wxString>& classNames )
53{
54 // Handle no component class condition
55 if( classNames.size() == 0 )
56 return m_noneClass.get();
57
58 // Handle single-assignment component classes
59 if( classNames.size() == 1 )
60 {
61 const wxString& className = *classNames.begin();
62 m_staticClassNamesCache.erase( className );
64 }
65
66 // Handle composite component classes
67 const std::vector<wxString> sortedClassNames = sortClassNames( classNames );
68 wxString fullName = GetFullClassNameForConstituents( sortedClassNames );
69
70 COMPONENT_CLASS* effectiveClass =
72
73 for( const COMPONENT_CLASS* constituentClass : effectiveClass->GetConstituentClasses() )
74 m_staticClassNamesCache.erase( constituentClass->GetName() );
75
76 return effectiveClass;
77}
78
79
81{
82 for( const auto& [className, classPtr] : m_constituentClasses )
83 {
84 if( classPtr->GetUsageContext() == COMPONENT_CLASS::USAGE::STATIC
85 || classPtr->GetUsageContext() == COMPONENT_CLASS::USAGE::STATIC_AND_DYNAMIC )
86 {
87 m_staticClassNamesCache.insert( className );
88 }
89 }
90
91 ++m_ticker;
92}
93
94
96{
97 // m_staticClassesCache now contains any static component classes that are unused from the
98 // netlist update. Delete any effective component classes which refer to them in a static-only
99 // context, or update their usage context.
100
101 // First, collect all component classes that will be deleted so we can clear footprint pointers
102 // before deletion. This prevents use-after-free when auto-save serializes footprints.
103 std::unordered_set<const COMPONENT_CLASS*> classesToDelete;
104
105 for( const wxString& className : m_staticClassNamesCache )
106 {
107 COMPONENT_CLASS* staticClass = m_constituentClasses[className].get();
108
109 if( staticClass->GetUsageContext() == COMPONENT_CLASS::USAGE::STATIC )
110 {
111 classesToDelete.insert( staticClass );
112
113 for( const auto& [combinedFullName, combinedCompClass] : m_effectiveClasses )
114 {
115 if( combinedCompClass->ContainsClassName( className ) )
116 classesToDelete.insert( combinedCompClass.get() );
117 }
118 }
119 }
120
121 // Clear footprint static component class pointers that reference classes being deleted
122 if( !classesToDelete.empty() )
123 {
124 for( FOOTPRINT* footprint : m_board->Footprints() )
125 {
126 if( classesToDelete.count( footprint->GetStaticComponentClass() ) )
127 footprint->SetStaticComponentClass( nullptr );
128 }
129 }
130
131 // Now perform the actual deletions
132 for( const wxString& className : m_staticClassNamesCache )
133 {
134 COMPONENT_CLASS* staticClass = m_constituentClasses[className].get();
135
136 if( staticClass->GetUsageContext() == COMPONENT_CLASS::USAGE::STATIC )
137 {
138 // Any static-only classes can be deleted, along with effective classes which refer to them
139 std::unordered_set<wxString> effectiveClassesToDelete;
140
141 for( const auto& [combinedFullName, combinedCompClass] : m_effectiveClasses )
142 {
143 if( combinedCompClass->ContainsClassName( className ) )
144 effectiveClassesToDelete.insert( combinedFullName );
145 }
146
147 for( const wxString& classNameToDelete : effectiveClassesToDelete )
148 m_effectiveClasses.erase( classNameToDelete );
149
150 m_constituentClasses.erase( className );
151 }
152 else
153 {
154 // Set the component class to dynamic-only scope
155 wxASSERT( staticClass->GetUsageContext()
158 }
159 }
160
161 // Clear the caches
163}
164
165
167 const std::unordered_set<wxString>& classNames )
168{
169 const std::vector<wxString> sortedClassNames = sortClassNames( classNames );
170
171 return GetFullClassNameForConstituents( sortedClassNames );
172}
173
174
175wxString
176COMPONENT_CLASS_MANAGER::GetFullClassNameForConstituents( const std::vector<wxString>& classNames )
177{
178 if( classNames.size() == 0 )
179 return wxEmptyString;
180
181 wxString fullName = classNames[0];
182
183 for( std::size_t i = 1; i < classNames.size(); ++i )
184 {
185 fullName += ",";
186 fullName += classNames[i];
187 }
188
189 return fullName;
190}
191
192
194 const std::vector<COMPONENT_CLASS_ASSIGNMENT_DATA>& aAssignments,
195 bool aGenerateSheetClasses, const std::unordered_set<wxString>& aNewSheetPaths )
196{
197 bool success = true;
198
199 // Invalidate component class cache entries
200 ++m_ticker;
201
202 // Save previous dynamically assigned component class names
203 std::unordered_set<wxString> prevClassNames;
204
205 for( const auto& rule : m_assignmentRules )
206 prevClassNames.insert( rule->GetComponentClass() );
207
208 m_assignmentRules.clear();
209
210 // Parse all assignment rules
211 std::vector<std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE>> rules;
212
213 for( const COMPONENT_CLASS_ASSIGNMENT_DATA& assignment : aAssignments )
214 {
215 std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE> rule = CompileAssignmentRule( assignment );
216
217 if( rule )
218 rules.emplace_back( std::move( rule ) );
219 else
220 success = false;
221 }
222
223 // Generate sheet classes if required
224 if( aGenerateSheetClasses )
225 {
226 std::unordered_set<wxString> sheetNames = aNewSheetPaths;
227
228 for( const FOOTPRINT* footprint : m_board->Footprints() )
229 sheetNames.insert( footprint->GetSheetname() );
230
231 for( wxString sheetName : sheetNames )
232 {
233 // Don't generate a class for empty sheets (e.g. manually placed footprints) or the root
234 // sheet
235 if( sheetName.empty() || sheetName == wxT( "/" ) )
236 continue;
237
238 sheetName.Replace( wxT( "\"" ), wxT( "" ) );
239 sheetName.Replace( wxT( "'" ), wxT( "" ) );
240
242 assignment.SetComponentClass( sheetName );
244 sheetName, wxEmptyString );
245
246 std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE> rule =
247 CompileAssignmentRule( assignment );
248
249 if( rule )
250 rules.emplace_back( std::move( rule ) );
251 else
252 success = false;
253 }
254 }
255
256 // Set the assignment rules
257 if( success )
258 m_assignmentRules = std::move( rules );
259
260 // Re-use or create component classes which may be output by assignment rules
261 for( const auto& rule : m_assignmentRules )
262 {
263 wxString className = rule->GetComponentClass();
264 prevClassNames.erase( className );
266 }
267
268 // prevClassNames now contains all dynamic component class names no longer in use. Remove any
269 // effective component classes no longer in use.
270 for( const wxString& className : prevClassNames )
271 {
272 COMPONENT_CLASS* dynamicClass = m_constituentClasses[className].get();
273
274 if( dynamicClass->GetUsageContext() == COMPONENT_CLASS::USAGE::DYNAMIC )
275 {
276 // Any dynamic-only classes can be deleted, along with effective classes which refer to them
277 std::unordered_set<wxString> classesToDelete;
278
279 for( const auto& [combinedFullName, combinedCompClass] : m_effectiveClasses )
280 {
281 if( combinedCompClass->ContainsClassName( className ) )
282 classesToDelete.insert( combinedFullName );
283 }
284
285 for( const wxString& classNameToDelete : classesToDelete )
286 m_effectiveClasses.erase( classNameToDelete );
287
288 m_constituentClasses.erase( className );
289 }
290 else
291 {
292 // Set the component class to dynamic-only scope
293 wxASSERT( dynamicClass->GetUsageContext()
296 }
297 }
298
299 return success;
300}
301
302
303std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE>
305{
306 const wxString ruleSource = aAssignment.GetAssignmentInDRCLanguage();
307
308 // Ignore incomplete rules (e.g. no component class name specified)
309 if( ruleSource.empty() )
310 return nullptr;
311
312 DRC_RULES_PARSER parser( ruleSource, wxT( "Component class assignment rule" ) );
313
314 try
315 {
316 std::vector<std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE>> parsed;
317
318 WX_STRING_REPORTER reporter;
319 parser.ParseComponentClassAssignmentRules( parsed, &reporter );
320
322 return nullptr;
323
324 if( parsed.size() != 1 )
325 return nullptr;
326
327 return parsed[0];
328 }
329 catch( PARSE_ERROR& )
330 {
331 return nullptr;
332 }
333}
334
335
336const COMPONENT_CLASS*
338 const COMPONENT_CLASS* dynamicClass )
339{
340 std::unordered_set<wxString> classNames;
341
342 if( staticClass )
343 {
344 for( const COMPONENT_CLASS* compClass : staticClass->GetConstituentClasses() )
345 classNames.insert( compClass->GetName() );
346 }
347
348 if( dynamicClass )
349 {
350 for( const COMPONENT_CLASS* compClass : dynamicClass->GetConstituentClasses() )
351 classNames.insert( compClass->GetName() );
352 }
353
354 if( classNames.empty() )
355 return GetNoneComponentClass();
356
357 if( classNames.size() == 1 )
358 {
359 wxASSERT( m_constituentClasses.contains( *classNames.begin() ) );
360 return m_constituentClasses[*classNames.begin()].get();
361 }
362
363 const std::vector<wxString> sortedClassNames = sortClassNames( classNames );
364
365 wxString fullCombinedName = GetFullClassNameForConstituents( sortedClassNames );
366
367 if( !m_effectiveClasses.contains( fullCombinedName ) )
368 {
369 std::unique_ptr<COMPONENT_CLASS> combinedClass = std::make_unique<COMPONENT_CLASS>(
370 fullCombinedName, COMPONENT_CLASS::USAGE::EFFECTIVE );
371
372 for( const wxString& className : sortedClassNames )
373 {
374 wxASSERT( m_constituentClasses.contains( className ) );
375 combinedClass->AddConstituentClass( m_constituentClasses[className].get() );
376 }
377
378 m_effectiveClasses[fullCombinedName] = std::move( combinedClass );
379 }
380
381 return m_effectiveClasses[fullCombinedName].get();
382}
383
384
389
390
392{
393 for( const auto& footprint : m_board->Footprints() )
394 {
395 footprint->RecomputeComponentClass();
396 }
397}
398
399
400const COMPONENT_CLASS*
402{
403 std::unordered_set<wxString> classNames;
404
405 // Assemble matching component class names
406 for( const auto& rule : m_assignmentRules )
407 {
408 if( rule->Matches( footprint ) )
409 classNames.insert( rule->GetComponentClass() );
410 }
411
412 // Handle composite component classes
413 const std::vector<wxString> sortedClassNames = sortClassNames( classNames );
414 const wxString fullName = GetFullClassNameForConstituents( sortedClassNames );
415
416 // No matching component classes
417 if( classNames.empty() )
418 return nullptr;
419
420 // One matching component class
421 if( classNames.size() == 1 )
423
424 // Multiple matching component classes
426}
427
428
429std::vector<wxString>
430COMPONENT_CLASS_MANAGER::sortClassNames( const std::unordered_set<wxString>& classNames )
431{
432 std::vector<wxString> sortedClassNames( classNames.begin(), classNames.end() );
433
434 std::ranges::sort( sortedClassNames,
435 []( const wxString& str1, const wxString& str2 )
436 {
437 return str1.Cmp( str2 ) < 0;
438 } );
439
440 return sortedClassNames;
441}
442
443
446 COMPONENT_CLASS::USAGE aContext )
447{
449 || aContext == COMPONENT_CLASS::USAGE::EFFECTIVE )
450 {
451 wxFAIL_MSG( "Can't create a STATIC_AND_DYNAMIC or EFFECTIVE constituent component class" );
452 return m_noneClass.get();
453 }
454
455 if( m_constituentClasses.contains( aClassName ) )
456 {
457 COMPONENT_CLASS* compClass = m_constituentClasses[aClassName].get();
458
459 if( aContext != compClass->GetUsageContext() )
461
462 return compClass;
463 }
464
465 std::unique_ptr<COMPONENT_CLASS> newClass =
466 std::make_unique<COMPONENT_CLASS>( aClassName, aContext );
467 newClass->AddConstituentClass( newClass.get() );
468
469 m_constituentClasses[aClassName] = std::move( newClass );
470
471 return m_constituentClasses[aClassName].get();
472}
473
474
476COMPONENT_CLASS_MANAGER::getOrCreateEffectiveClass( const std::vector<wxString>& aClassNames,
477 COMPONENT_CLASS::USAGE aContext )
478{
479 wxString fullClassName = GetFullClassNameForConstituents( aClassNames );
480
481 if( m_effectiveClasses.contains( fullClassName ) )
482 {
483 COMPONENT_CLASS* compClass = m_effectiveClasses[fullClassName].get();
484
485 for( COMPONENT_CLASS* constituentClass : compClass->GetConstituentClasses() )
486 {
487 if( constituentClass->GetUsageContext() != aContext )
488 constituentClass->SetUsageContext( COMPONENT_CLASS::USAGE::STATIC_AND_DYNAMIC );
489 }
490 }
491 else
492 {
493 std::unique_ptr<COMPONENT_CLASS> effectiveClass = std::make_unique<COMPONENT_CLASS>(
494 fullClassName, COMPONENT_CLASS::USAGE::EFFECTIVE );
495
496 for( const wxString& className : aClassNames )
497 {
498 COMPONENT_CLASS* constituentClass = getOrCreateConstituentClass( className, aContext );
499 effectiveClass->AddConstituentClass( constituentClass );
500 }
501
502 m_effectiveClasses[fullClassName] = std::move( effectiveClass );
503 }
504
505 return m_effectiveClasses[fullClassName].get();
506}
507
508
509std::unordered_set<wxString> COMPONENT_CLASS_MANAGER::GetClassNames() const
510{
511 std::unordered_set<wxString> classNames;
512
513 for( const auto& className : m_constituentClasses | std::views::keys )
514 classNames.insert( className );
515
516 return classNames;
517}
518
519
521{
522 if( aFootprint )
523 {
524 aFootprint->BuildCourtyardCaches();
525 }
526 else
527 {
528 for( FOOTPRINT* fp : m_board->Footprints() )
529 fp->BuildCourtyardCaches();
530 }
531}
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
void AddCondition(const CONDITION_TYPE aCondition, const wxString &aPrimaryData, const wxString &aSecondaryData)
Sets the given condition type with the assocated match data.
wxString GetAssignmentInDRCLanguage() const
Returns the DRC rules language for this component class assignment.
void SetComponentClass(const wxString &aComponentClass)
Sets the resulting component class for matching footprints.
std::unordered_set< wxString > GetClassNames() const
Fetches a read-only map of the fundamental component classes.
static std::shared_ptr< COMPONENT_CLASS_ASSIGNMENT_RULE > CompileAssignmentRule(const COMPONENT_CLASS_ASSIGNMENT_DATA &aAssignment)
std::unordered_set< wxString > m_staticClassNamesCache
Cache of in-use static component class names Used for cleanup following netlist updates.
std::unordered_map< wxString, std::unique_ptr< COMPONENT_CLASS > > m_constituentClasses
All individual component classes from static assignments.
COMPONENT_CLASS * GetEffectiveStaticComponentClass(const std::unordered_set< wxString > &classNames)
Gets an effective component class for the given constituent class names.
long long int m_ticker
Monotonically increasing ticker to test cached component class validity.
void ForceComponentClassRecalculation() const
Forces the component class for all footprints to be recalculated.
COMPONENT_CLASS * getOrCreateEffectiveClass(const std::vector< wxString > &aClassNames, COMPONENT_CLASS::USAGE aContext)
Returns an effective component class for the given set of constituent class names Precondition: aClas...
static wxString GetFullClassNameForConstituents(const std::unordered_set< wxString > &classNames)
Gets the full effective class name for the given set of constituent classes.
void InvalidateComponentClasses()
Invalidates any caches component classes and recomputes caches if required.
void FinishNetlistUpdate()
Cleans up the manager after a board update Must be called after updating the PCB from the netlist.
std::vector< std::shared_ptr< COMPONENT_CLASS_ASSIGNMENT_RULE > > m_assignmentRules
Active component class assignment rules.
const COMPONENT_CLASS * GetCombinedComponentClass(const COMPONENT_CLASS *staticClass, const COMPONENT_CLASS *dynamicClass)
Gets the combined component class with the given static and dynamic constituent component classes.
static std::vector< wxString > sortClassNames(const std::unordered_set< wxString > &classNames)
Sorts the given class names in to canonical order.
void RebuildRequiredCaches(FOOTPRINT *aFootprint=nullptr) const
Rebuilds any caches that may be required by custom assignment rules.
COMPONENT_CLASS * getOrCreateConstituentClass(const wxString &aClassName, COMPONENT_CLASS::USAGE aContext)
Returns a constituent component class, re-using an existing instantiation where possible.
BOARD * m_board
The board these component classes are assigned to / from.
const COMPONENT_CLASS * GetNoneComponentClass() const
Returns the unassigned component class.
std::unordered_map< wxString, std::unique_ptr< COMPONENT_CLASS > > m_effectiveClasses
Generated effective (composite) static component classes.
const COMPONENT_CLASS * GetDynamicComponentClassesForFootprint(const FOOTPRINT *footprint)
Gets the dynamic component classes which match the given footprint.
void InitNetlistUpdate()
Prepare the manager for a board update Must be called prior to updating the PCB from the netlist.
std::shared_ptr< COMPONENT_CLASS > m_noneClass
The class to represent an unassigned component class.
bool SyncDynamicComponentClassAssignments(const std::vector< COMPONENT_CLASS_ASSIGNMENT_DATA > &aAssignments, bool aGenerateSheetClasses, const std::unordered_set< wxString > &aNewSheetPaths)
Synchronises all dynamic component class assignment rules.
A lightweight representation of a component class.
void SetUsageContext(const USAGE aUsageContext)
Sets the assignment context in which this component class is being used.
USAGE
The assignment context in which this component class is used.
USAGE GetUsageContext() const
Gets the assignment context in which this component class is being used.
const std::vector< COMPONENT_CLASS * > & GetConstituentClasses() const
Fetches a vector of the constituent classes for this (effective) class.
void ParseComponentClassAssignmentRules(std::vector< std::shared_ptr< COMPONENT_CLASS_ASSIGNMENT_RULE > > &aRules, REPORTER *aReporter)
void BuildCourtyardCaches(OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
Build complex polygons of the courtyard areas from graphic items on the courtyard layers.
virtual bool HasMessageOfSeverity(int aSeverityMask) const
Returns true if the reporter has one or more messages matching the specified severity mask.
Definition reporter.h:143
A wrapper for reporting to a wxString object.
Definition reporter.h:191
@ RPT_SEVERITY_ERROR
A filename or source description, a problem input line, a line number, a byte offset,...