KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 ), m_hasCustomAssignmentConditions( false )
35{
37 std::make_shared<COMPONENT_CLASS>( wxEmptyString, COMPONENT_CLASS::USAGE::STATIC );
38}
39
40
53 const std::unordered_set<wxString>& classNames )
54{
55 // Handle no component class condition
56 if( classNames.size() == 0 )
57 return m_noneClass.get();
58
59 // Handle single-assignment component classes
60 if( classNames.size() == 1 )
61 {
62 const wxString& className = *classNames.begin();
63 m_staticClassNamesCache.erase( className );
65 }
66
67 // Handle composite component classes
68 const std::vector<wxString> sortedClassNames = sortClassNames( classNames );
69 wxString fullName = GetFullClassNameForConstituents( sortedClassNames );
70
71 COMPONENT_CLASS* effectiveClass =
73
74 for( const COMPONENT_CLASS* constituentClass : effectiveClass->GetConstituentClasses() )
75 m_staticClassNamesCache.erase( constituentClass->GetName() );
76
77 return effectiveClass;
78}
79
80
82{
83 for( const auto& [className, classPtr] : m_constituentClasses )
84 {
85 if( classPtr->GetUsageContext() == COMPONENT_CLASS::USAGE::STATIC
86 || classPtr->GetUsageContext() == COMPONENT_CLASS::USAGE::STATIC_AND_DYNAMIC )
87 {
88 m_staticClassNamesCache.insert( className );
89 }
90 }
91
92 ++m_ticker;
93}
94
95
97{
98 // m_staticClassesCache now contains any static component classes that are unused from the
99 // netlist update. Delete any effective component classes which refer to them in a static-only
100 // context, or update their usage context.
101 for( const wxString& className : m_staticClassNamesCache )
102 {
103 COMPONENT_CLASS* staticClass = m_constituentClasses[className].get();
104
105 if( staticClass->GetUsageContext() == COMPONENT_CLASS::USAGE::STATIC )
106 {
107 // Any static-only classes can be deleted, along with effective classes which refer to them
108 std::unordered_set<wxString> classesToDelete;
109
110 for( const auto& [combinedFullName, combinedCompClass] : m_effectiveClasses )
111 {
112 if( combinedCompClass->ContainsClassName( className ) )
113 classesToDelete.insert( combinedFullName );
114 }
115
116 for( const wxString& classNameToDelete : classesToDelete )
117 m_effectiveClasses.erase( classNameToDelete );
118
119 m_constituentClasses.erase( className );
120 }
121 else
122 {
123 // Set the component class to dynamic-only scope
124 wxASSERT( staticClass->GetUsageContext()
127 }
128 }
129
130 // Clear the caches
132}
133
134
136 const std::unordered_set<wxString>& classNames )
137{
138 const std::vector<wxString> sortedClassNames = sortClassNames( classNames );
139
140 return GetFullClassNameForConstituents( sortedClassNames );
141}
142
143
144wxString
145COMPONENT_CLASS_MANAGER::GetFullClassNameForConstituents( const std::vector<wxString>& classNames )
146{
147 if( classNames.size() == 0 )
148 return wxEmptyString;
149
150 wxString fullName = classNames[0];
151
152 for( std::size_t i = 1; i < classNames.size(); ++i )
153 {
154 fullName += ",";
155 fullName += classNames[i];
156 }
157
158 return fullName;
159}
160
161
163 const std::vector<COMPONENT_CLASS_ASSIGNMENT_DATA>& aAssignments,
164 bool aGenerateSheetClasses, const std::unordered_set<wxString>& aNewSheetPaths )
165{
167 bool success = true;
168
169 // Invalidate component class cache entries
170 ++m_ticker;
171
172 // Save previous dynamically assigned component class names
173 std::unordered_set<wxString> prevClassNames;
174
175 for( const auto& rule : m_assignmentRules )
176 prevClassNames.insert( rule->GetComponentClass() );
177
178 m_assignmentRules.clear();
179
180 // Parse all assignment rules
181 std::vector<std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE>> rules;
182
183 for( const COMPONENT_CLASS_ASSIGNMENT_DATA& assignment : aAssignments )
184 {
185 if( assignment.GetConditions().contains(
187 {
189 }
190
191 std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE> rule = CompileAssignmentRule( assignment );
192
193 if( rule )
194 rules.emplace_back( std::move( rule ) );
195 else
196 success = false;
197 }
198
199 // Generate sheet classes if required
200 if( aGenerateSheetClasses )
201 {
202 std::unordered_set<wxString> sheetNames = aNewSheetPaths;
203
204 for( const FOOTPRINT* footprint : m_board->Footprints() )
205 sheetNames.insert( footprint->GetSheetname() );
206
207 for( wxString sheetName : sheetNames )
208 {
209 // Don't generate a class for empty sheets (e.g. manually placed footprints) or the root
210 // sheet
211 if( sheetName.empty() || sheetName == wxT( "/" ) )
212 continue;
213
214 sheetName.Replace( wxT( "\"" ), wxT( "" ) );
215 sheetName.Replace( wxT( "'" ), wxT( "" ) );
216
218 assignment.SetComponentClass( sheetName );
220 sheetName, wxEmptyString );
221
222 std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE> rule =
223 CompileAssignmentRule( assignment );
224
225 if( rule )
226 rules.emplace_back( std::move( rule ) );
227 else
228 success = false;
229 }
230 }
231
232 // Set the assignment rules
233 if( success )
234 m_assignmentRules = std::move( rules );
235
236 // Re-use or create component classes which may be output by assignment rules
237 for( const auto& rule : m_assignmentRules )
238 {
239 wxString className = rule->GetComponentClass();
240 prevClassNames.erase( className );
242 }
243
244 // prevClassNames now contains all dynamic component class names no longer in use. Remove any
245 // effective component classes no longer in use.
246 for( const wxString& className : prevClassNames )
247 {
248 COMPONENT_CLASS* dynamicClass = m_constituentClasses[className].get();
249
250 if( dynamicClass->GetUsageContext() == COMPONENT_CLASS::USAGE::DYNAMIC )
251 {
252 // Any dynamic-only classes can be deleted, along with effective classes which refer to them
253 std::unordered_set<wxString> classesToDelete;
254
255 for( const auto& [combinedFullName, combinedCompClass] : m_effectiveClasses )
256 {
257 if( combinedCompClass->ContainsClassName( className ) )
258 classesToDelete.insert( combinedFullName );
259 }
260
261 for( const wxString& classNameToDelete : classesToDelete )
262 m_effectiveClasses.erase( classNameToDelete );
263
264 m_constituentClasses.erase( className );
265 }
266 else
267 {
268 // Set the component class to dynamic-only scope
269 wxASSERT( dynamicClass->GetUsageContext()
272 }
273 }
274
275 return success;
276}
277
278
279std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE>
281{
282 const wxString ruleSource = aAssignment.GetAssignmentInDRCLanguage();
283
284 // Ignore incomplete rules (e.g. no component class name specified)
285 if( ruleSource.empty() )
286 return nullptr;
287
288 DRC_RULES_PARSER parser( ruleSource, wxT( "Component class assignment rule" ) );
289
290 try
291 {
292 std::vector<std::shared_ptr<COMPONENT_CLASS_ASSIGNMENT_RULE>> parsed;
293
294 WX_STRING_REPORTER reporter;
295 parser.ParseComponentClassAssignmentRules( parsed, &reporter );
296
298 return nullptr;
299
300 if( parsed.size() != 1 )
301 return nullptr;
302
303 return parsed[0];
304 }
305 catch( PARSE_ERROR& )
306 {
307 return nullptr;
308 }
309}
310
311
312const COMPONENT_CLASS*
314 const COMPONENT_CLASS* dynamicClass )
315{
316 std::unordered_set<wxString> classNames;
317
318 if( staticClass )
319 {
320 for( const COMPONENT_CLASS* compClass : staticClass->GetConstituentClasses() )
321 classNames.insert( compClass->GetName() );
322 }
323
324 if( dynamicClass )
325 {
326 for( const COMPONENT_CLASS* compClass : dynamicClass->GetConstituentClasses() )
327 classNames.insert( compClass->GetName() );
328 }
329
330 if( classNames.empty() )
331 return GetNoneComponentClass();
332
333 if( classNames.size() == 1 )
334 {
335 wxASSERT( m_constituentClasses.contains( *classNames.begin() ) );
336 return m_constituentClasses[*classNames.begin()].get();
337 }
338
339 const std::vector<wxString> sortedClassNames = sortClassNames( classNames );
340
341 wxString fullCombinedName = GetFullClassNameForConstituents( sortedClassNames );
342
343 if( !m_effectiveClasses.contains( fullCombinedName ) )
344 {
345 std::unique_ptr<COMPONENT_CLASS> combinedClass = std::make_unique<COMPONENT_CLASS>(
346 fullCombinedName, COMPONENT_CLASS::USAGE::EFFECTIVE );
347
348 for( const wxString& className : sortedClassNames )
349 {
350 wxASSERT( m_constituentClasses.contains( className ) );
351 combinedClass->AddConstituentClass( m_constituentClasses[className].get() );
352 }
353
354 m_effectiveClasses[fullCombinedName] = std::move( combinedClass );
355 }
356
357 return m_effectiveClasses[fullCombinedName].get();
358}
359
360
362{
363 ++m_ticker;
364}
365
366
368{
369 for( const auto& footprint : m_board->Footprints() )
370 {
371 footprint->RecomputeComponentClass();
372 }
373}
374
375
376const COMPONENT_CLASS*
378{
379 std::unordered_set<wxString> classNames;
380
381 // Assemble matching component class names
382 for( const auto& rule : m_assignmentRules )
383 {
384 if( rule->Matches( footprint ) )
385 classNames.insert( rule->GetComponentClass() );
386 }
387
388 // Handle composite component classes
389 const std::vector<wxString> sortedClassNames = sortClassNames( classNames );
390 const wxString fullName = GetFullClassNameForConstituents( sortedClassNames );
391
392 // No matching component classes
393 if( classNames.empty() )
394 return nullptr;
395
396 // One matching component class
397 if( classNames.size() == 1 )
399
400 // Multiple matching component classes
402}
403
404
405std::vector<wxString>
406COMPONENT_CLASS_MANAGER::sortClassNames( const std::unordered_set<wxString>& classNames )
407{
408 std::vector<wxString> sortedClassNames( classNames.begin(), classNames.end() );
409
410 std::ranges::sort( sortedClassNames,
411 []( const wxString& str1, const wxString& str2 )
412 {
413 return str1.Cmp( str2 ) < 0;
414 } );
415
416 return sortedClassNames;
417}
418
419
422 COMPONENT_CLASS::USAGE aContext )
423{
425 || aContext == COMPONENT_CLASS::USAGE::EFFECTIVE )
426 {
427 wxFAIL_MSG( "Can't create a STATIC_AND_DYNAMIC or EFFECTIVE constituent component class" );
428 return m_noneClass.get();
429 }
430
431 if( m_constituentClasses.contains( aClassName ) )
432 {
433 COMPONENT_CLASS* compClass = m_constituentClasses[aClassName].get();
434
435 if( aContext != compClass->GetUsageContext() )
437
438 return compClass;
439 }
440
441 std::unique_ptr<COMPONENT_CLASS> newClass =
442 std::make_unique<COMPONENT_CLASS>( aClassName, aContext );
443 newClass->AddConstituentClass( newClass.get() );
444
445 m_constituentClasses[aClassName] = std::move( newClass );
446
447 return m_constituentClasses[aClassName].get();
448}
449
450
452COMPONENT_CLASS_MANAGER::getOrCreateEffectiveClass( const std::vector<wxString>& aClassNames,
453 COMPONENT_CLASS::USAGE aContext )
454{
455 wxString fullClassName = GetFullClassNameForConstituents( aClassNames );
456
457 if( m_effectiveClasses.contains( fullClassName ) )
458 {
459 COMPONENT_CLASS* compClass = m_effectiveClasses[fullClassName].get();
460
461 for( COMPONENT_CLASS* constituentClass : compClass->GetConstituentClasses() )
462 {
463 if( constituentClass->GetUsageContext() != aContext )
464 constituentClass->SetUsageContext( COMPONENT_CLASS::USAGE::STATIC_AND_DYNAMIC );
465 }
466 }
467 else
468 {
469 std::unique_ptr<COMPONENT_CLASS> effectiveClass = std::make_unique<COMPONENT_CLASS>(
470 fullClassName, COMPONENT_CLASS::USAGE::EFFECTIVE );
471
472 for( const wxString& className : aClassNames )
473 {
474 COMPONENT_CLASS* constituentClass = getOrCreateConstituentClass( className, aContext );
475 effectiveClass->AddConstituentClass( constituentClass );
476 }
477
478 m_effectiveClasses[fullClassName] = std::move( effectiveClass );
479 }
480
481 return m_effectiveClasses[fullClassName].get();
482}
483
484
485std::unordered_set<wxString> COMPONENT_CLASS_MANAGER::GetClassNames() const
486{
487 std::unordered_set<wxString> classNames;
488
489 for( const auto& className : m_constituentClasses | std::views::keys )
490 classNames.insert( className );
491
492 return classNames;
493}
494
495
497{
498 if( aFootprint )
499 {
500 aFootprint->BuildCourtyardCaches();
501 }
502 else
503 {
504 for( FOOTPRINT* fp : m_board->Footprints() )
505 fp->BuildCourtyardCaches();
506 }
507}
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:297
const FOOTPRINTS & Footprints() const
Definition: board.h:338
wxString GetAssignmentInDRCLanguage() const
Returns the DRC rules language for this component class assignment.
void SetCondition(const CONDITION_TYPE aCondition, const wxString &aPrimaryData, const wxString &aSecondaryData)
Sets the given condition type with the assocated match data.
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.
bool m_hasCustomAssignmentConditions
Quick lookup of presence of custom dynamic assignment conditions.
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.
Definition: footprint.cpp:3010
A wrapper for reporting to a wxString object.
Definition: reporter.h:172
bool HasMessageOfSeverity(int aSeverityMask) const override
Returns true if the reporter has one or more messages matching the specified severity mask.
Definition: reporter.cpp:106
@ RPT_SEVERITY_ERROR
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:120