mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-20 23:41:40 +00:00
Implement Component Classes
- Adds Component Class field to SCH_DIRECTIVE_LABEL - Adds SCH_SYMBOLs to SCH_RULE_AREA item lists - SCH_SYMBOLs resolve Component Class directives - Netlist exporter / importer handles Component Class names - Adds DRC expressions and functions - Adds QA check for component class netlist export
This commit is contained in:
parent
ae96ac6d2e
commit
d64a112971
common
eeschema
netlist_exporters
sch_field.cppsch_label.cppsch_rule_area.cppsch_rule_area.hsch_symbol.cppsch_symbol.htools
include
pcbnew
CMakeLists.txtboard.hboard_item.cppcomponent_class_manager.cppcomponent_class_manager.hfootprint.cpp
netlist_reader
pcb_edit_frame.cpppcb_field.hpcb_io/kicad_sexpr
pcbexpr_evaluator.cpppcbexpr_evaluator.hpcbexpr_functions.cppqa
data/eeschema/netlists/component_classes
component_classes.kicad_pcbcomponent_classes.kicad_procomponent_classes.kicad_schcomponent_classes.net
tests/eeschema
@ -745,6 +745,7 @@ set( PCB_COMMON_SRCS
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/teardrop/teardrop_parameters.cpp #needed by board_design_settings.cpp
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/router/pns_meander.cpp #needed by board_design_settings.cpp
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/board.cpp
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/component_class_manager.cpp #needed by board.cpp
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/board_item.cpp
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/pcb_dimension.cpp
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/pcb_shape.cpp
|
||||
|
@ -1,6 +1,8 @@
|
||||
class
|
||||
code
|
||||
comp
|
||||
components
|
||||
component_classes
|
||||
datasheet
|
||||
date
|
||||
description
|
||||
|
@ -65,6 +65,7 @@ center
|
||||
chamfer
|
||||
chamfer_ratio
|
||||
circle
|
||||
class
|
||||
clearance
|
||||
clearance_min
|
||||
color
|
||||
@ -73,6 +74,7 @@ column_count
|
||||
column_widths
|
||||
comment
|
||||
company
|
||||
component_classes
|
||||
connect
|
||||
connect_pads
|
||||
copperpour
|
||||
|
@ -414,6 +414,21 @@ XNODE* NETLIST_EXPORTER_XML::makeSymbols( unsigned aCtl )
|
||||
xsheetpath->AddAttribute( wxT( "names" ), sheet.PathHumanReadable() );
|
||||
xsheetpath->AddAttribute( wxT( "tstamps" ), sheet.PathAsString() );
|
||||
|
||||
// Node for component class
|
||||
std::vector<wxString> compClassNames =
|
||||
getComponentClassNamesForAllSymbolUnits( symbol, sheet, sheetList );
|
||||
|
||||
if( compClassNames.size() > 0 )
|
||||
{
|
||||
XNODE* xcompclasslist;
|
||||
xcomp->AddChild( xcompclasslist = node( wxT( "component_classes" ) ) );
|
||||
|
||||
for( const wxString& compClass : compClassNames )
|
||||
{
|
||||
xcompclasslist->AddChild( node( wxT( "class" ), UnescapeString( compClass ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
XNODE* xunits; // Node for extra units
|
||||
xcomp->AddChild( xunits = node( wxT( "tstamps" ) ) );
|
||||
|
||||
@ -445,6 +460,49 @@ XNODE* NETLIST_EXPORTER_XML::makeSymbols( unsigned aCtl )
|
||||
}
|
||||
|
||||
|
||||
std::vector<wxString> NETLIST_EXPORTER_XML::getComponentClassNamesForAllSymbolUnits(
|
||||
SCH_SYMBOL* aSymbol, const SCH_SHEET_PATH& aSymbolSheet, const SCH_SHEET_LIST& aSheetList )
|
||||
{
|
||||
std::unordered_set<wxString> compClassNames = aSymbol->GetComponentClassNames( &aSymbolSheet );
|
||||
int primaryUnit = aSymbol->GetUnitSelection( &aSymbolSheet );
|
||||
|
||||
if( aSymbol->GetUnitCount() > 1 )
|
||||
{
|
||||
wxString ref = aSymbol->GetRef( &aSymbolSheet );
|
||||
|
||||
for( const SCH_SHEET_PATH& sheet : aSheetList )
|
||||
{
|
||||
for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
|
||||
{
|
||||
SCH_SYMBOL* symbol2 = static_cast<SCH_SYMBOL*>( item );
|
||||
|
||||
wxString ref2 = symbol2->GetRef( &sheet );
|
||||
int otherUnit = symbol2->GetUnitSelection( &sheet );
|
||||
|
||||
if( ref2.CmpNoCase( ref ) != 0 )
|
||||
continue;
|
||||
|
||||
if( otherUnit == primaryUnit )
|
||||
continue;
|
||||
|
||||
std::unordered_set<wxString> otherClassNames =
|
||||
symbol2->GetComponentClassNames( &sheet );
|
||||
compClassNames.insert( otherClassNames.begin(), otherClassNames.end() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<wxString> sortedCompClassNames( compClassNames.begin(), compClassNames.end() );
|
||||
std::sort( sortedCompClassNames.begin(), sortedCompClassNames.end(),
|
||||
[]( const wxString& str1, const wxString& str2 )
|
||||
{
|
||||
return str1.Cmp( str2 ) < 0;
|
||||
} );
|
||||
|
||||
return sortedCompClassNames;
|
||||
}
|
||||
|
||||
|
||||
XNODE* NETLIST_EXPORTER_XML::makeDesignHeader()
|
||||
{
|
||||
SCH_SCREEN* screen;
|
||||
|
@ -133,6 +133,14 @@ protected:
|
||||
void addSymbolFields( XNODE* aNode, SCH_SYMBOL* aSymbol, const SCH_SHEET_PATH& aSheet,
|
||||
const SCH_SHEET_LIST& aSheetList);
|
||||
|
||||
/**
|
||||
* Finds all component class names attached to any sub-unit of a given symbol
|
||||
*/
|
||||
std::vector<wxString>
|
||||
getComponentClassNamesForAllSymbolUnits( SCH_SYMBOL* aSymbol,
|
||||
const SCH_SHEET_PATH& aSymbolSheet,
|
||||
const SCH_SHEET_LIST& aSheetList );
|
||||
|
||||
bool m_resolveTextVars; // Export textVar references resolved
|
||||
|
||||
private:
|
||||
|
@ -183,12 +183,19 @@ void SCH_FIELD::SetId( int aId )
|
||||
// We can't use defined IDs for labels because there can be multiple net class
|
||||
// assignments.
|
||||
|
||||
if( GetCanonicalName() == wxT( "Netclass" ) )
|
||||
if( GetCanonicalName() == wxT( "Netclass" )
|
||||
|| GetCanonicalName() == wxT( "Component Class" ) )
|
||||
{
|
||||
SetLayer( LAYER_NETCLASS_REFS );
|
||||
}
|
||||
else if( GetCanonicalName() == wxT( "Intersheetrefs" ) )
|
||||
{
|
||||
SetLayer( LAYER_INTERSHEET_REFS );
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLayer( LAYER_FIELDS );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1002,7 +1002,7 @@ const BOX2I SCH_LABEL_BASE::GetBoundingBox() const
|
||||
|
||||
for( const SCH_FIELD& field : m_fields )
|
||||
{
|
||||
if( field.IsVisible() )
|
||||
if( field.IsVisible() && field.GetText() != wxEmptyString )
|
||||
{
|
||||
BOX2I fieldBBox = field.GetBoundingBox();
|
||||
|
||||
@ -1734,6 +1734,9 @@ void SCH_DIRECTIVE_LABEL::AutoplaceFields( SCH_SCREEN* aScreen, bool aManual )
|
||||
|
||||
for( SCH_FIELD& field : m_fields )
|
||||
{
|
||||
if( field.GetText() == wxEmptyString )
|
||||
continue;
|
||||
|
||||
switch( GetSpinStyle() )
|
||||
{
|
||||
default:
|
||||
|
@ -267,6 +267,10 @@ void SCH_RULE_AREA::RefreshContainedItemsAndDirectives(
|
||||
addContainedItem( areaItem );
|
||||
}
|
||||
}
|
||||
else if( areaItem->IsType( { SCH_SYMBOL_T } ) )
|
||||
{
|
||||
addContainedItem( areaItem );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,7 +320,7 @@ const std::unordered_set<SCH_ITEM*>& SCH_RULE_AREA::GetContainedItems() const
|
||||
}
|
||||
|
||||
|
||||
const std::unordered_set<SCH_DIRECTIVE_LABEL*> SCH_RULE_AREA::GetDirectives() const
|
||||
const std::unordered_set<SCH_DIRECTIVE_LABEL*>& SCH_RULE_AREA::GetDirectives() const
|
||||
{
|
||||
return m_directives;
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ public:
|
||||
const std::unordered_set<SCH_ITEM*>& GetContainedItems() const;
|
||||
|
||||
/// @brief Returns the set of all directive labels attached to the rule area border
|
||||
const std::unordered_set<SCH_DIRECTIVE_LABEL*> GetDirectives() const;
|
||||
const std::unordered_set<SCH_DIRECTIVE_LABEL*>& GetDirectives() const;
|
||||
|
||||
/// @brief Resolves the netclass of this rule area from connected directive labels
|
||||
/// @returns The resolved netclass (if any), and the SCH_ITEM providing the declaration
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <settings/settings_manager.h>
|
||||
#include <sch_plotter.h>
|
||||
#include <string_utils.h>
|
||||
#include <sch_rule_area.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
@ -2872,6 +2873,38 @@ bool SCH_SYMBOL::IsNormal() const
|
||||
}
|
||||
|
||||
|
||||
std::unordered_set<wxString> SCH_SYMBOL::GetComponentClassNames( const SCH_SHEET_PATH* aPath ) const
|
||||
{
|
||||
std::unordered_set<wxString> componentClass;
|
||||
|
||||
auto getComponentClassFields = [&]( const auto& fields )
|
||||
{
|
||||
for( const SCH_FIELD& field : fields )
|
||||
{
|
||||
if( field.GetCanonicalName() == wxT( "Component Class" ) )
|
||||
{
|
||||
if( field.GetShownText( aPath, false ) != wxEmptyString )
|
||||
componentClass.insert( field.GetShownText( aPath, false ) );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// First get component classes set on the symbol itself
|
||||
getComponentClassFields( m_fields );
|
||||
|
||||
// Now get component classes set on any enclosing rule areas
|
||||
for( const SCH_RULE_AREA* ruleArea : m_rule_areas_cache )
|
||||
{
|
||||
for( const SCH_DIRECTIVE_LABEL* label : ruleArea->GetDirectives() )
|
||||
{
|
||||
getComponentClassFields( label->GetFields() );
|
||||
}
|
||||
}
|
||||
|
||||
return componentClass;
|
||||
}
|
||||
|
||||
|
||||
bool SCH_SYMBOL::operator==( const SCH_ITEM& aOther ) const
|
||||
{
|
||||
if( Type() != aOther.Type() )
|
||||
|
@ -901,6 +901,9 @@ public:
|
||||
|
||||
double Similarity( const SCH_ITEM& aOther ) const override;
|
||||
|
||||
/// Returns the component classes this symbol belongs in
|
||||
std::unordered_set<wxString> GetComponentClassNames( const SCH_SHEET_PATH* aPath ) const;
|
||||
|
||||
bool operator==( const SCH_ITEM& aOther ) const override;
|
||||
|
||||
private:
|
||||
|
@ -481,12 +481,13 @@ TOOL_ACTION EE_ACTIONS::placeLabel( TOOL_ACTION_ARGS()
|
||||
.Icon( BITMAPS::add_label )
|
||||
.Flags( AF_ACTIVATE ) );
|
||||
|
||||
TOOL_ACTION EE_ACTIONS::placeClassLabel( TOOL_ACTION_ARGS()
|
||||
.Name( "eeschema.InteractiveDrawing.placeClassLabel" )
|
||||
.Scope( AS_GLOBAL )
|
||||
.FriendlyName( _( "Place Net Class Directives" ) )
|
||||
.Icon( BITMAPS::add_class_flag )
|
||||
.Flags( AF_ACTIVATE ) );
|
||||
TOOL_ACTION
|
||||
EE_ACTIONS::placeClassLabel( TOOL_ACTION_ARGS()
|
||||
.Name( "eeschema.InteractiveDrawing.placeClassLabel" )
|
||||
.Scope( AS_GLOBAL )
|
||||
.FriendlyName( _( "Place Directive Labels" ) )
|
||||
.Icon( BITMAPS::add_class_flag )
|
||||
.Flags( AF_ACTIVATE ) );
|
||||
|
||||
TOOL_ACTION EE_ACTIONS::placeHierLabel( TOOL_ACTION_ARGS()
|
||||
.Name( "eeschema.InteractiveDrawing.placeHierarchicalLabel" )
|
||||
|
@ -1546,6 +1546,8 @@ SCH_TEXT* SCH_DRAWING_TOOLS::createNewText( const VECTOR2I& aPosition, int aType
|
||||
labelItem = new SCH_DIRECTIVE_LABEL( aPosition );
|
||||
labelItem->SetShape( m_lastNetClassFlagShape );
|
||||
labelItem->GetFields().emplace_back( SCH_FIELD( {0,0}, 0, labelItem, wxT( "Netclass" ) ) );
|
||||
labelItem->GetFields().emplace_back(
|
||||
SCH_FIELD( { 0, 0 }, 0, labelItem, wxT( "Component Class" ) ) );
|
||||
labelItem->GetFields().back().SetItalic( true );
|
||||
labelItem->GetFields().back().SetVisible( true );
|
||||
textItem = labelItem;
|
||||
|
@ -37,6 +37,7 @@
|
||||
class BOARD;
|
||||
class BOARD_DESIGN_SETTINGS;
|
||||
class BOARD_ITEM_CONTAINER;
|
||||
class COMPONENT_CLASS;
|
||||
class SHAPE_POLY_SET;
|
||||
class SHAPE_SEGMENT;
|
||||
class PCB_BASE_FRAME;
|
||||
@ -79,11 +80,8 @@ class BOARD_ITEM : public EDA_ITEM
|
||||
{
|
||||
public:
|
||||
BOARD_ITEM( BOARD_ITEM* aParent, KICAD_T idtype, PCB_LAYER_ID aLayer = F_Cu ) :
|
||||
EDA_ITEM( aParent, idtype, false, true ),
|
||||
m_layer( aLayer ),
|
||||
m_isKnockout( false ),
|
||||
m_isLocked( false ),
|
||||
m_group( nullptr )
|
||||
EDA_ITEM( aParent, idtype, false, true ), m_layer( aLayer ), m_isKnockout( false ),
|
||||
m_isLocked( false ), m_group( nullptr ), m_componentClass( nullptr )
|
||||
{
|
||||
}
|
||||
|
||||
@ -419,6 +417,12 @@ public:
|
||||
bool operator() ( const BOARD_ITEM* a, const BOARD_ITEM* b ) const;
|
||||
};
|
||||
|
||||
void SetComponentClass( const COMPONENT_CLASS* aClass ) { m_componentClass = aClass; }
|
||||
|
||||
const COMPONENT_CLASS* GetComponentClass() const { return m_componentClass; }
|
||||
|
||||
wxString GetComponentClassAsString() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Return a string (to be shown to the user) describing a layer mask.
|
||||
@ -434,6 +438,8 @@ protected:
|
||||
bool m_isLocked;
|
||||
|
||||
PCB_GROUP* m_group;
|
||||
|
||||
const COMPONENT_CLASS* m_componentClass;
|
||||
};
|
||||
|
||||
#ifndef SWIG
|
||||
|
@ -316,6 +316,7 @@ set( PCBNEW_CLASS_SRCS
|
||||
array_pad_number_provider.cpp
|
||||
build_BOM_from_board.cpp
|
||||
cleanup_item.cpp
|
||||
component_class_manager.cpp
|
||||
convert_shape_list_to_polygon.cpp
|
||||
cross-probing.cpp
|
||||
edit.cpp
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include <board_item_container.h>
|
||||
#include <board_stackup_manager/board_stackup.h>
|
||||
#include <component_class_manager.h>
|
||||
#include <embedded_files.h>
|
||||
#include <common.h> // Needed for stl hash extensions
|
||||
#include <convert_shape_list_to_polygon.h> // for OUTLINE_ERROR_HANDLER
|
||||
@ -1262,6 +1263,11 @@ public:
|
||||
*/
|
||||
void EmbedFonts() override;
|
||||
|
||||
/**
|
||||
* Gets the component class manager
|
||||
*/
|
||||
COMPONENT_CLASS_MANAGER& GetComponentClassManager() { return m_componentClassManager; }
|
||||
|
||||
// --------- Item order comparators ---------
|
||||
|
||||
struct cmp_items
|
||||
@ -1377,6 +1383,8 @@ private:
|
||||
std::vector<BOARD_LISTENER*> m_listeners;
|
||||
|
||||
bool m_embedFonts;
|
||||
|
||||
COMPONENT_CLASS_MANAGER m_componentClassManager;
|
||||
};
|
||||
|
||||
|
||||
|
@ -374,6 +374,17 @@ wxString BOARD_ITEM::GetParentAsString() const
|
||||
}
|
||||
|
||||
|
||||
wxString BOARD_ITEM::GetComponentClassAsString() const
|
||||
{
|
||||
if( m_componentClass )
|
||||
{
|
||||
return m_componentClass->GetFullName();
|
||||
}
|
||||
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
|
||||
static struct BOARD_ITEM_DESC
|
||||
{
|
||||
BOARD_ITEM_DESC()
|
||||
@ -413,6 +424,13 @@ static struct BOARD_ITEM_DESC
|
||||
BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( aItem );
|
||||
return item && item->GetBoard() && !item->GetBoard()->IsFootprintHolder();
|
||||
} );
|
||||
|
||||
// Used only in DRC engine
|
||||
propMgr.AddProperty( new PROPERTY<BOARD_ITEM, wxString>(
|
||||
_HKI( "ComponentClass" ), NO_SETTER( BOARD_ITEM, wxString ),
|
||||
&BOARD_ITEM::GetComponentClassAsString ) )
|
||||
.SetIsHiddenFromLibraryEditors()
|
||||
.SetIsHiddenFromPropertiesManager();
|
||||
}
|
||||
} _BOARD_ITEM_DESC;
|
||||
|
||||
|
145
pcbnew/component_class_manager.cpp
Normal file
145
pcbnew/component_class_manager.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <component_class_manager.h>
|
||||
|
||||
|
||||
void COMPONENT_CLASS::AddConstituentClass( COMPONENT_CLASS* componentClass )
|
||||
{
|
||||
m_constituentClasses.push_back( componentClass );
|
||||
}
|
||||
|
||||
|
||||
bool COMPONENT_CLASS::ContainsClassName( const wxString& className ) const
|
||||
{
|
||||
if( m_constituentClasses.size() == 0 )
|
||||
return false;
|
||||
|
||||
if( m_constituentClasses.size() == 1 )
|
||||
return m_name == className;
|
||||
|
||||
return std::any_of( m_constituentClasses.begin(), m_constituentClasses.end(),
|
||||
[&className]( const COMPONENT_CLASS* testClass )
|
||||
{
|
||||
return testClass->GetFullName() == className;
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
wxString COMPONENT_CLASS::GetName() const
|
||||
{
|
||||
if( m_constituentClasses.size() == 0 )
|
||||
return wxT( "<None>" );
|
||||
|
||||
if( m_constituentClasses.size() == 1 )
|
||||
return m_name;
|
||||
|
||||
wxASSERT( m_constituentClasses.size() >= 2 );
|
||||
|
||||
wxString name;
|
||||
|
||||
if( m_constituentClasses.size() == 2 )
|
||||
{
|
||||
name.Printf( _( "%s and %s" ), m_constituentClasses[0]->GetName(),
|
||||
m_constituentClasses[1]->GetName() );
|
||||
}
|
||||
else if( m_constituentClasses.size() == 3 )
|
||||
{
|
||||
name.Printf( _( "%s, %s and %s" ), m_constituentClasses[0]->GetName(),
|
||||
m_constituentClasses[1]->GetName(), m_constituentClasses[2]->GetName() );
|
||||
}
|
||||
else if( m_constituentClasses.size() > 3 )
|
||||
{
|
||||
name.Printf( _( "%s, %s and %d more" ), m_constituentClasses[0]->GetName(),
|
||||
m_constituentClasses[1]->GetName(),
|
||||
static_cast<int>( m_constituentClasses.size() - 2 ) );
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
bool COMPONENT_CLASS::IsEmpty() const
|
||||
{
|
||||
return m_constituentClasses.size() == 0;
|
||||
}
|
||||
|
||||
|
||||
COMPONENT_CLASS_MANAGER::COMPONENT_CLASS_MANAGER()
|
||||
{
|
||||
m_noneClass = std::make_unique<COMPONENT_CLASS>( wxEmptyString );
|
||||
}
|
||||
|
||||
|
||||
COMPONENT_CLASS*
|
||||
COMPONENT_CLASS_MANAGER::GetEffectiveComponentClass( std::unordered_set<wxString>& classNames )
|
||||
{
|
||||
if( classNames.size() == 0 )
|
||||
return m_noneClass.get();
|
||||
|
||||
auto getOrCreateClass = [this]( const wxString& className )
|
||||
{
|
||||
if( !m_classes.count( className ) )
|
||||
{
|
||||
std::unique_ptr<COMPONENT_CLASS> newClass =
|
||||
std::make_unique<COMPONENT_CLASS>( className );
|
||||
newClass->AddConstituentClass( newClass.get() );
|
||||
m_classes[className] = std::move( newClass );
|
||||
}
|
||||
|
||||
return m_classes[className].get();
|
||||
};
|
||||
|
||||
if( classNames.size() == 1 )
|
||||
{
|
||||
return getOrCreateClass( *classNames.begin() );
|
||||
}
|
||||
|
||||
std::vector<wxString> sortedClassNames( classNames.begin(), classNames.end() );
|
||||
|
||||
std::sort( sortedClassNames.begin(), sortedClassNames.end(),
|
||||
[]( const wxString& str1, const wxString& str2 )
|
||||
{
|
||||
return str1.Cmp( str2 ) < 0;
|
||||
} );
|
||||
|
||||
wxString fullName = sortedClassNames[0];
|
||||
|
||||
for( std::size_t i = 1; i < sortedClassNames.size(); ++i )
|
||||
{
|
||||
fullName += ",";
|
||||
fullName += sortedClassNames[i];
|
||||
}
|
||||
|
||||
if( !m_effectiveClasses.count( fullName ) )
|
||||
{
|
||||
std::unique_ptr<COMPONENT_CLASS> effClass = std::make_unique<COMPONENT_CLASS>( fullName );
|
||||
|
||||
for( const wxString& className : sortedClassNames )
|
||||
{
|
||||
effClass->AddConstituentClass( getOrCreateClass( className ) );
|
||||
}
|
||||
|
||||
m_effectiveClasses[fullName] = std::move( effClass );
|
||||
}
|
||||
|
||||
return m_effectiveClasses[fullName].get();
|
||||
}
|
100
pcbnew/component_class_manager.h
Normal file
100
pcbnew/component_class_manager.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PCBNEW_COMPONENT_CLASS_MANAGER_H
|
||||
#define PCBNEW_COMPONENT_CLASS_MANAGER_H
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <wx/string.h>
|
||||
|
||||
#include <board_item.h>
|
||||
|
||||
/*
|
||||
* A lightweight representation of a component class. The membership within
|
||||
* m_consituentClasses allows determination of the type of class this is:
|
||||
*
|
||||
* m_constituentClasses.size() == 0: This is a null class (no assigment).
|
||||
* m_name is empty.
|
||||
* m_constituentClasses.size() == 1: This is an atomic class. The constituent class
|
||||
* pointer refers to itself. m_name contains the name of the atomic class
|
||||
* m_constituentClasses.size() > 1: This is a composite class. The constituent class
|
||||
* pointers refer to all atomic members. m_name contains a comma-delimited list of
|
||||
* all atomic member class names.
|
||||
*/
|
||||
class COMPONENT_CLASS
|
||||
{
|
||||
public:
|
||||
COMPONENT_CLASS( const wxString& name ) : m_name( name ) {}
|
||||
|
||||
/// Fetches the display name of this component class
|
||||
wxString GetName() const;
|
||||
|
||||
/// Fetches the full name of this component class
|
||||
const wxString& GetFullName() const { return m_name; }
|
||||
|
||||
/// Adds a constituent component class to an effective component class
|
||||
void AddConstituentClass( COMPONENT_CLASS* componentClass );
|
||||
|
||||
/// Determines if this (effective) component class contains a specific sub-class
|
||||
bool ContainsClassName( const wxString& className ) const;
|
||||
|
||||
/// Determines if this (effective) component class is empty (i.e. no classes defined)
|
||||
bool IsEmpty() const;
|
||||
|
||||
/// Fetches a vector of the constituent classes for this (effective) class
|
||||
const std::vector<COMPONENT_CLASS*>& GetConstituentClasses() const
|
||||
{
|
||||
return m_constituentClasses;
|
||||
}
|
||||
|
||||
private:
|
||||
/// The full name of the component class
|
||||
wxString m_name;
|
||||
|
||||
/// The COMPONENT_CLASS objects contributing to this complete component class
|
||||
std::vector<COMPONENT_CLASS*> m_constituentClasses;
|
||||
};
|
||||
|
||||
class COMPONENT_CLASS_MANAGER
|
||||
{
|
||||
public:
|
||||
COMPONENT_CLASS_MANAGER();
|
||||
|
||||
/// @brief Gets an effective component class for the given constituent class names
|
||||
/// @param classes The names of the constituent component classes
|
||||
/// @return Effective COMPONENT_CLASS object
|
||||
COMPONENT_CLASS* GetEffectiveComponentClass( std::unordered_set<wxString>& classNames );
|
||||
|
||||
/// Returns the unassigned component class
|
||||
const COMPONENT_CLASS* GetNoneComponentClass() const { return m_noneClass.get(); }
|
||||
|
||||
protected:
|
||||
/// All individual component classes
|
||||
std::unordered_map<wxString, std::unique_ptr<COMPONENT_CLASS>> m_classes;
|
||||
|
||||
/// Generated effective component classes
|
||||
std::unordered_map<wxString, std::unique_ptr<COMPONENT_CLASS>> m_effectiveClasses;
|
||||
|
||||
/// The class to represent an unassigned component class
|
||||
std::unique_ptr<COMPONENT_CLASS> m_noneClass;
|
||||
};
|
||||
|
||||
#endif
|
@ -1584,6 +1584,11 @@ void FOOTPRINT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_I
|
||||
aList.emplace_back( _( "Rotation" ), wxString::Format( wxT( "%.4g" ),
|
||||
GetOrientation().AsDegrees() ) );
|
||||
|
||||
if( m_componentClass )
|
||||
{
|
||||
aList.emplace_back( _( "Component Class" ), m_componentClass->GetName() );
|
||||
}
|
||||
|
||||
msg.Printf( _( "Footprint: %s" ), m_fpid.GetUniStringLibId() );
|
||||
msg2.Printf( _( "3D-Shape: %s" ), m_3D_Drawings.empty() ? _( "<none>" )
|
||||
: m_3D_Drawings.front().m_Filename );
|
||||
|
@ -147,6 +147,7 @@ FOOTPRINT* BOARD_NETLIST_UPDATER::addNewFootprint( COMPONENT* aComponent )
|
||||
}
|
||||
|
||||
FOOTPRINT* footprint = m_frame->LoadFootprint( aComponent->GetFPID() );
|
||||
footprint->SetComponentClass( m_board->GetComponentClassManager().GetNoneComponentClass() );
|
||||
|
||||
if( footprint == nullptr )
|
||||
{
|
||||
@ -201,6 +202,40 @@ FOOTPRINT* BOARD_NETLIST_UPDATER::addNewFootprint( COMPONENT* aComponent )
|
||||
}
|
||||
|
||||
|
||||
void BOARD_NETLIST_UPDATER::updateComponentClass( FOOTPRINT* aFootprint, COMPONENT* aNewComponent )
|
||||
{
|
||||
// Get the existing component class
|
||||
wxString curClassName;
|
||||
|
||||
if( const COMPONENT_CLASS* curClass = aFootprint->GetComponentClass() )
|
||||
curClassName = curClass->GetFullName();
|
||||
|
||||
// Calculate the new component class
|
||||
COMPONENT_CLASS* newClass = m_board->GetComponentClassManager().GetEffectiveComponentClass(
|
||||
aNewComponent->GetComponentClassNames() );
|
||||
wxString newClassName = newClass->GetFullName();
|
||||
|
||||
if( curClassName == newClassName )
|
||||
return;
|
||||
|
||||
wxString msg;
|
||||
|
||||
if( m_isDryRun )
|
||||
{
|
||||
msg.Printf( _( "Change %s component class from %s to %s." ), aFootprint->GetReference(),
|
||||
curClassName, newClassName );
|
||||
}
|
||||
else
|
||||
{
|
||||
aFootprint->SetComponentClass( newClass );
|
||||
msg.Printf( _( "Changed %s component class from %s to %s." ), aFootprint->GetReference(),
|
||||
curClassName, newClassName );
|
||||
}
|
||||
|
||||
m_reporter->Report( msg, RPT_SEVERITY_ACTION );
|
||||
}
|
||||
|
||||
|
||||
FOOTPRINT* BOARD_NETLIST_UPDATER::replaceFootprint( NETLIST& aNetlist, FOOTPRINT* aFootprint,
|
||||
COMPONENT* aNewComponent )
|
||||
{
|
||||
@ -395,8 +430,11 @@ bool BOARD_NETLIST_UPDATER::updateFootprintParameters( FOOTPRINT* aPcbFootprint,
|
||||
for( PCB_FIELD* field : aPcbFootprint->GetFields() )
|
||||
{
|
||||
// These fields are individually checked above
|
||||
if( field->IsReference() || field->IsValue() || field->IsFootprint() )
|
||||
if( field->IsReference() || field->IsValue() || field->IsFootprint()
|
||||
|| field->IsComponentClass() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fpFieldsAsMap[field->GetName()] = field->GetText();
|
||||
}
|
||||
@ -407,6 +445,9 @@ bool BOARD_NETLIST_UPDATER::updateFootprintParameters( FOOTPRINT* aPcbFootprint,
|
||||
compFields.erase( GetCanonicalFieldName( VALUE_FIELD ) );
|
||||
compFields.erase( GetCanonicalFieldName( FOOTPRINT_FIELD ) );
|
||||
|
||||
// Remove any component class fields - these are not editable in the pcb editor
|
||||
compFields.erase( wxT( "Component Class" ) );
|
||||
|
||||
// Fields are stored as an ordered map, but we don't (yet) support reordering
|
||||
// the footprint fields to match the symbol, so we manually check the fields
|
||||
// in the order they are stored in the symbol.
|
||||
@ -1214,6 +1255,7 @@ bool BOARD_NETLIST_UPDATER::UpdateNetlist( NETLIST& aNetlist )
|
||||
|
||||
updateFootprintParameters( tmp, component );
|
||||
updateComponentPadConnections( tmp, component );
|
||||
updateComponentClass( tmp, component );
|
||||
}
|
||||
|
||||
matchCount++;
|
||||
@ -1236,6 +1278,7 @@ bool BOARD_NETLIST_UPDATER::UpdateNetlist( NETLIST& aNetlist )
|
||||
|
||||
updateFootprintParameters( footprint, component );
|
||||
updateComponentPadConnections( footprint, component );
|
||||
updateComponentClass( footprint, component );
|
||||
}
|
||||
}
|
||||
else if( matchCount > 1 )
|
||||
|
@ -111,6 +111,8 @@ private:
|
||||
|
||||
bool updateComponentPadConnections( FOOTPRINT* aFootprint, COMPONENT* aNewComponent );
|
||||
|
||||
void updateComponentClass( FOOTPRINT* aFootprint, COMPONENT* aNewComponent );
|
||||
|
||||
void cacheCopperZoneConnections();
|
||||
|
||||
bool updateCopperZoneNets( NETLIST& aNetlist );
|
||||
|
@ -280,13 +280,14 @@ void KICAD_NETLIST_PARSER::parseNet()
|
||||
|
||||
void KICAD_NETLIST_PARSER::parseComponent()
|
||||
{
|
||||
/* Parses a section like
|
||||
/* Parses a section like
|
||||
* (comp (ref P1)
|
||||
* (value DB25FEMALE)
|
||||
* (footprint DB25FC)
|
||||
* (libsource (lib conn) (part DB25))
|
||||
* (property (name PINCOUNT) (value 25))
|
||||
* (sheetpath (names /) (tstamps /))
|
||||
* (component_classes (class (name "CLASS")))
|
||||
* (tstamp 68183921-93a5-49ac-91b0-49d05a0e1647))
|
||||
*
|
||||
* other fields (unused) are skipped
|
||||
@ -305,6 +306,7 @@ void KICAD_NETLIST_PARSER::parseComponent()
|
||||
std::vector<KIID> uuids;
|
||||
std::map<wxString, wxString> properties;
|
||||
nlohmann::ordered_map<wxString, wxString> fields;
|
||||
std::unordered_set<wxString> componentClasses;
|
||||
|
||||
// The token comp was read, so the next data is (ref P1)
|
||||
while( (token = NextTok() ) != T_RIGHT )
|
||||
@ -469,6 +471,24 @@ void KICAD_NETLIST_PARSER::parseComponent()
|
||||
|
||||
break;
|
||||
|
||||
case T_component_classes:
|
||||
{
|
||||
while( ( token = NextTok() ) != T_RIGHT )
|
||||
{
|
||||
if( token != T_LEFT )
|
||||
Expecting( T_LEFT );
|
||||
|
||||
if( ( token = NextTok() ) != T_class )
|
||||
Expecting( T_class );
|
||||
|
||||
NeedSYMBOLorNUMBER();
|
||||
componentClasses.insert( From_UTF8( CurText() ) );
|
||||
NeedRIGHT();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Skip not used data (i.e all other tokens)
|
||||
skipCurrent();
|
||||
@ -491,6 +511,7 @@ void KICAD_NETLIST_PARSER::parseComponent()
|
||||
component->SetProperties( properties );
|
||||
component->SetFields( fields );
|
||||
component->SetHumanReadablePath( humanSheetPath );
|
||||
component->SetComponentClassNames( componentClasses );
|
||||
m_netlist->AddComponent( component );
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <boost/ptr_container/ptr_vector.hpp>
|
||||
#include <wx/arrstr.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <lib_id.h>
|
||||
#include <footprint.h>
|
||||
@ -175,6 +176,13 @@ public:
|
||||
void SetHumanReadablePath( const wxString& aPath ) { m_humanReadablePath = aPath; }
|
||||
const wxString& GetHumanReadablePath() const { return m_humanReadablePath; }
|
||||
|
||||
void SetComponentClassNames( const std::unordered_set<wxString>& aClassNames )
|
||||
{
|
||||
m_componentClassNames = aClassNames;
|
||||
}
|
||||
|
||||
std::unordered_set<wxString>& GetComponentClassNames() { return m_componentClassNames; }
|
||||
|
||||
private:
|
||||
std::vector<COMPONENT_NET> m_nets; ///< list of nets shared by the component pins
|
||||
|
||||
@ -215,6 +223,9 @@ private:
|
||||
/// Component-specific user fields found in the netlist.
|
||||
nlohmann::ordered_map<wxString, wxString> m_fields;
|
||||
|
||||
/// Component classes for this footprint
|
||||
std::unordered_set<wxString> m_componentClassNames;
|
||||
|
||||
static COMPONENT_NET m_emptyNet;
|
||||
};
|
||||
|
||||
|
@ -2484,6 +2484,7 @@ void PCB_EDIT_FRAME::ExchangeFootprint( FOOTPRINT* aExisting, FOOTPRINT* aNew,
|
||||
aNew->SetSheetfile( aExisting->GetSheetfile() );
|
||||
aNew->SetSheetname( aExisting->GetSheetname() );
|
||||
aNew->SetFilters( aExisting->GetFilters() );
|
||||
aNew->SetComponentClass( aExisting->GetComponentClass() );
|
||||
|
||||
aCommit.Remove( aExisting );
|
||||
aCommit.Add( aNew );
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user