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:
JamesJCode 2024-09-27 20:48:12 +01:00
parent ae96ac6d2e
commit d64a112971
37 changed files with 1827 additions and 19 deletions

View File

@ -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

View File

@ -1,6 +1,8 @@
class
code
comp
components
component_classes
datasheet
date
description

View File

@ -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

View File

@ -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;

View File

@ -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:

View File

@ -183,14 +183,21 @@ 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 );
}
}
}
wxString SCH_FIELD::GetShownName() const

View File

@ -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:

View File

@ -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;
}

View File

@ -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

View File

@ -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() )

View File

@ -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:

View File

@ -481,10 +481,11 @@ TOOL_ACTION EE_ACTIONS::placeLabel( TOOL_ACTION_ARGS()
.Icon( BITMAPS::add_label )
.Flags( AF_ACTIVATE ) );
TOOL_ACTION EE_ACTIONS::placeClassLabel( TOOL_ACTION_ARGS()
TOOL_ACTION
EE_ACTIONS::placeClassLabel( TOOL_ACTION_ARGS()
.Name( "eeschema.InteractiveDrawing.placeClassLabel" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Place Net Class Directives" ) )
.FriendlyName( _( "Place Directive Labels" ) )
.Icon( BITMAPS::add_class_flag )
.Flags( AF_ACTIVATE ) );

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;
};

View File

@ -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;

View 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();
}

View 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

View File

@ -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 );

View File

@ -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 )

View File

@ -111,6 +111,8 @@ private:
bool updateComponentPadConnections( FOOTPRINT* aFootprint, COMPONENT* aNewComponent );
void updateComponentClass( FOOTPRINT* aFootprint, COMPONENT* aNewComponent );
void cacheCopperZoneConnections();
bool updateCopperZoneNets( NETLIST& aNetlist );

View File

@ -287,6 +287,7 @@ void KICAD_NETLIST_PARSER::parseComponent()
* (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 );
}

View File

@ -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;
};

View File

@ -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 );

View File

@ -71,6 +71,7 @@ public:
bool IsValue() const { return m_id == VALUE_FIELD; }
bool IsFootprint() const { return m_id == FOOTPRINT_FIELD; }
bool IsDatasheet() const { return m_id == DATASHEET_FIELD; }
bool IsComponentClass() const { return GetName() == wxT( "Component Class" ); }
bool IsMandatoryField() const { return m_id < MANDATORY_FIELDS; }

View File

@ -1214,6 +1214,22 @@ void PCB_IO_KICAD_SEXPR::format( const FOOTPRINT* aFootprint, int aNestLevel ) c
m_out->Print( aNestLevel + 1, ")\n" );
}
if( const COMPONENT_CLASS* compClass = aFootprint->GetComponentClass() )
{
if( !compClass->IsEmpty() )
{
m_out->Print( aNestLevel + 1, "(component_classes\n" );
for( const COMPONENT_CLASS* constituent : compClass->GetConstituentClasses() )
{
m_out->Print( aNestLevel + 2, "(class %s)\n",
m_out->Quotew( constituent->GetFullName() ).c_str() );
}
m_out->Print( aNestLevel + 1, ")\n" );
}
}
if( !aFootprint->GetFilters().empty() )
{
m_out->Print( aNestLevel + 1, "(property ki_fp_filters %s)\n",

View File

@ -161,7 +161,8 @@ class PCB_IO_KICAD_SEXPR; // forward decl
//#define SEXPR_BOARD_FILE_VERSION 20240617 // Table angles
//#define SEXPR_BOARD_FILE_VERSION 20240703 // User layer types
//#define SEXPR_BOARD_FILE_VERSION 20240706 // Embedded Files
#define SEXPR_BOARD_FILE_VERSION 20240819 // Embedded Files - Update hash algorithm to Murmur3
//#define SEXPR_BOARD_FILE_VERSION 20240819 // Embedded Files - Update hash algorithm to Murmur3
#define SEXPR_BOARD_FILE_VERSION 20240928 // Component classes
#define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag
#define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting

View File

@ -4281,6 +4281,11 @@ FOOTPRINT* PCB_IO_KICAD_SEXPR_PARSER::parseFOOTPRINT_unchecked( wxArrayString* a
footprint->SetInitialComments( aInitialComments );
if( m_board )
{
footprint->SetComponentClass( m_board->GetComponentClassManager().GetNoneComponentClass() );
}
token = NextTok();
if( !IsSymbol( token ) && token != T_NUMBER )
@ -4773,13 +4778,41 @@ FOOTPRINT* PCB_IO_KICAD_SEXPR_PARSER::parseFOOTPRINT_unchecked( wxArrayString* a
break;
}
case T_component_classes:
{
std::unordered_set<wxString> componentClassNames;
while( ( token = NextTok() ) != T_RIGHT )
{
if( token != T_LEFT )
Expecting( T_LEFT );
if( ( token = NextTok() ) != T_class )
Expecting( T_class );
NeedSYMBOLorNUMBER();
componentClassNames.insert( From_UTF8( CurText() ) );
NeedRIGHT();
}
if( m_board )
{
const COMPONENT_CLASS* componentClass =
m_board->GetComponentClassManager().GetEffectiveComponentClass(
componentClassNames );
footprint->SetComponentClass( componentClass );
}
break;
}
default:
Expecting( "at, descr, locked, placed, tedit, tstamp, uuid, "
"autoplace_cost90, autoplace_cost180, attr, clearance, "
"embedded_files, fp_arc, fp_circle, fp_curve, fp_line, fp_poly, "
"fp_rect, fp_text, pad, group, generator, model, path, solder_mask_margin, "
"solder_paste_margin, solder_paste_margin_ratio, tags, thermal_gap, "
"version, zone, or zone_connect" );
"version, zone, zone_connect, or component_classes" );
}
}

View File

@ -165,6 +165,65 @@ protected:
};
class PCBEXPR_COMPONENT_CLASS_VALUE : public LIBEVAL::VALUE
{
public:
PCBEXPR_COMPONENT_CLASS_VALUE( BOARD_ITEM* aItem ) :
LIBEVAL::VALUE( wxEmptyString ), m_item( aItem ){};
const wxString& AsString() const override
{
const COMPONENT_CLASS* compClass = m_item->GetComponentClass();
if( compClass )
{
const_cast<PCBEXPR_COMPONENT_CLASS_VALUE*>( this )->Set( compClass->GetFullName() );
}
return LIBEVAL::VALUE::AsString();
}
bool EqualTo( LIBEVAL::CONTEXT* aCtx, const VALUE* b ) const override
{
if( const PCBEXPR_COMPONENT_CLASS_VALUE* bValue =
dynamic_cast<const PCBEXPR_COMPONENT_CLASS_VALUE*>( b ) )
{
const COMPONENT_CLASS* aClass = m_item->GetComponentClass();
const COMPONENT_CLASS* bClass = bValue->m_item->GetComponentClass();
// Note this depends on COMPONENT_CLASS_MANAGER maintaining ownership
// of all unique component class objects
return aClass == bClass;
}
else
{
return LIBEVAL::VALUE::EqualTo( aCtx, b );
}
}
bool NotEqualTo( LIBEVAL::CONTEXT* aCtx, const LIBEVAL::VALUE* b ) const override
{
if( const PCBEXPR_COMPONENT_CLASS_VALUE* bValue =
dynamic_cast<const PCBEXPR_COMPONENT_CLASS_VALUE*>( b ) )
{
const COMPONENT_CLASS* aClass = m_item->GetComponentClass();
const COMPONENT_CLASS* bClass = bValue->m_item->GetComponentClass();
// Note this depends on COMPONENT_CLASS_MANAGER maintaining ownership
// of all unique component class objects
return aClass != bClass;
}
else
{
return LIBEVAL::VALUE::NotEqualTo( aCtx, b );
}
}
protected:
BOARD_ITEM* m_item;
};
class PCBEXPR_NET_VALUE : public LIBEVAL::VALUE
{
public:
@ -279,6 +338,17 @@ LIBEVAL::VALUE* PCBEXPR_NETCLASS_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
}
LIBEVAL::VALUE* PCBEXPR_COMPONENT_CLASS_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
{
BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( GetObject( aCtx ) );
if( !item || item->Type() != PCB_FOOTPRINT_T )
return new LIBEVAL::VALUE();
return new PCBEXPR_COMPONENT_CLASS_VALUE( item );
}
LIBEVAL::VALUE* PCBEXPR_NETNAME_REF::GetValue( LIBEVAL::CONTEXT* aCtx )
{
BOARD_CONNECTED_ITEM* item = dynamic_cast<BOARD_CONNECTED_ITEM*>( GetObject( aCtx ) );
@ -326,6 +396,15 @@ std::unique_ptr<LIBEVAL::VAR_REF> PCBEXPR_UCODE::CreateVarRef( const wxString& a
else
return nullptr;
}
else if( aField.CmpNoCase( wxT( "ComponentClass" ) ) == 0 )
{
if( aVar == wxT( "A" ) )
return std::make_unique<PCBEXPR_COMPONENT_CLASS_REF>( 0 );
else if( aVar == wxT( "B" ) )
return std::make_unique<PCBEXPR_COMPONENT_CLASS_REF>( 1 );
else
return nullptr;
}
else if( aField.CmpNoCase( wxT( "NetName" ) ) == 0 )
{
if( aVar == wxT( "A" ) )

View File

@ -127,6 +127,19 @@ public:
};
// "Object code" version of a component class reference (for performance).
class PCBEXPR_COMPONENT_CLASS_REF : public PCBEXPR_VAR_REF
{
public:
PCBEXPR_COMPONENT_CLASS_REF( int aItemIndex ) : PCBEXPR_VAR_REF( aItemIndex )
{
SetType( LIBEVAL::VT_STRING );
}
LIBEVAL::VALUE* GetValue( LIBEVAL::CONTEXT* aCtx ) override;
};
// "Object code" version of a netname reference (for performance).
class PCBEXPR_NETNAME_REF : public PCBEXPR_VAR_REF
{

View File

@ -1208,6 +1208,42 @@ static void hasNetclassFunc( LIBEVAL::CONTEXT* aCtx, void* self )
} );
}
static void hasComponentClassFunc( LIBEVAL::CONTEXT* aCtx, void* self )
{
LIBEVAL::VALUE* arg = aCtx->Pop();
LIBEVAL::VALUE* result = aCtx->AllocValue();
result->Set( 0.0 );
aCtx->Push( result );
if( !arg || arg->AsString().IsEmpty() )
{
if( aCtx->HasErrorCallback() )
aCtx->ReportError(
_( "Missing component class name argument to hasComponentClass()" ) );
return;
}
PCBEXPR_VAR_REF* vref = static_cast<PCBEXPR_VAR_REF*>( self );
BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
if( !item )
return;
result->SetDeferredEval(
[item, arg]() -> double
{
BOARD_ITEM* boardItem = static_cast<BOARD_ITEM*>( item );
const COMPONENT_CLASS* compClass = boardItem->GetComponentClass();
if( compClass && compClass->ContainsClassName( arg->AsString() ) )
return 1.0;
return 0.0;
} );
}
PCBEXPR_BUILTIN_FUNCTIONS::PCBEXPR_BUILTIN_FUNCTIONS()
{
@ -1249,4 +1285,5 @@ void PCBEXPR_BUILTIN_FUNCTIONS::RegisterAllFunctions()
RegisterFunc( wxT( "getField('x')" ), getFieldFunc );
RegisterFunc( wxT( "hasNetclass('x')" ), hasNetclassFunc );
RegisterFunc( wxT( "hasComponentClass('x')" ), hasComponentClassFunc );
}

View File

@ -0,0 +1,2 @@
(kicad_pcb (version 20240928) (generator "pcbnew") (generator_version "8.99")
)

View File

@ -0,0 +1,428 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {},
"diff_pair_dimensions": [],
"drc_exclusions": [],
"rules": {},
"track_widths": [],
"via_dimensions": []
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_pairs": [],
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"footprint_filter": "ignore",
"footprint_link_issues": "warning",
"four_way_junction": "ignore",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_multiple_wires": "warning",
"lib_symbol_issues": "warning",
"lib_symbol_mismatch": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"same_local_global_label": "warning",
"similar_label_and_power": "warning",
"similar_labels": "warning",
"similar_power": "warning",
"simulation_model_issue": "ignore",
"single_global_label": "ignore",
"unannotated": "error",
"unconnected_wire_endpoint": "warning",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "component_classes.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"priority": -1,
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
],
"meta": {
"version": 4
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "",
"pos_files": "",
"specctra_dsn": "",
"step": "",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"bom_export_filename": "${PROJECTNAME}.csv",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
},
{
"group_by": true,
"label": "Exclude from BOM",
"name": "${EXCLUDE_FROM_BOM}",
"show": true
},
{
"group_by": true,
"label": "Exclude from Board",
"name": "${EXCLUDE_FROM_BOARD}",
"show": true
},
{
"group_by": true,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
},
{
"group_by": false,
"label": "Description",
"name": "Description",
"show": false
},
{
"group_by": false,
"label": "#",
"name": "${ITEM_NUMBER}",
"show": false
}
],
"filter_string": "",
"group_symbols": true,
"include_excluded_from_bom": true,
"name": "",
"sort_asc": true,
"sort_field": "Reference"
},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.375,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "KiCad",
"page_layout_descr_file": "",
"plot_directory": "",
"space_save_all_events": true,
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [
[
"bfc9b4d1-186d-4d8a-9f35-9d071f1d54fd",
"Root"
]
],
"text_variables": {}
}

View File

@ -0,0 +1,610 @@
(kicad_sch
(version 20240819)
(generator "eeschema")
(generator_version "8.99")
(uuid "bfc9b4d1-186d-4d8a-9f35-9d071f1d54fd")
(paper "A4")
(lib_symbols
(symbol "Amplifier_Operational:TL072"
(pin_names
(offset 0.127)
)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(property "Reference" "U"
(at 0 5.08 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Value" "TL072"
(at 0 -5.08 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Footprint" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "http://www.ti.com/lit/ds/symlink/tl071.pdf"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "Dual Low-Noise JFET-Input Operational Amplifiers, DIP-8/SOIC-8"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_locked" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "ki_keywords" "dual opamp"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_fp_filters" "SOIC*3.9x4.9mm*P1.27mm* DIP*W7.62mm* TO*99* OnSemi*Micro8* TSSOP*3x3mm*P0.65mm* TSSOP*4.4x3mm*P0.65mm* MSOP*3x3mm*P0.65mm* SSOP*3.9x4.9mm*P0.635mm* LFCSP*2x2mm*P0.5mm* *SIP* SOIC*5.3x6.2mm*P1.27mm*"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(symbol "TL072_1_1"
(polyline
(pts
(xy -5.08 5.08) (xy 5.08 0) (xy -5.08 -5.08) (xy -5.08 5.08)
)
(stroke
(width 0.254)
(type default)
)
(fill
(type background)
)
)
(pin input line
(at -7.62 2.54 0)
(length 2.54)
(name "+"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "3"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin input line
(at -7.62 -2.54 0)
(length 2.54)
(name "-"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "2"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin output line
(at 7.62 0 180)
(length 2.54)
(name "~"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "1"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
(symbol "TL072_2_1"
(polyline
(pts
(xy -5.08 5.08) (xy 5.08 0) (xy -5.08 -5.08) (xy -5.08 5.08)
)
(stroke
(width 0.254)
(type default)
)
(fill
(type background)
)
)
(pin input line
(at -7.62 2.54 0)
(length 2.54)
(name "+"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "5"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin input line
(at -7.62 -2.54 0)
(length 2.54)
(name "-"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "6"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin output line
(at 7.62 0 180)
(length 2.54)
(name "~"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "7"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
(symbol "TL072_3_1"
(pin power_in line
(at -2.54 7.62 270)
(length 3.81)
(name "V+"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "8"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin power_in line
(at -2.54 -7.62 90)
(length 3.81)
(name "V-"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "4"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
(embedded_fonts no)
)
)
(text "U1A: Has a \"Component Class\" field\nU2A: Assigned a Component Class from the SCH_RULE_AREA\nU3A: No class assigned\n\nResult in netlist: U1 has two Component Class names: Class1 and Class2"
(exclude_from_sim no)
(at 107.442 98.298 0)
(effects
(font
(size 1.27 1.27)
)
)
(uuid "bfaf99ba-b9bc-483a-9a28-c01a662202e4")
)
(rule_area
(polyline
(pts
(xy 104.14 45.72) (xy 139.7 45.72) (xy 139.7 80.01) (xy 104.14 80.01)
)
(stroke
(width 0)
(type dash)
)
(fill
(type none)
)
(uuid 25809b47-e826-4371-9b19-1a716050ff88)
)
)
(netclass_flag ""
(length 2.54)
(shape round)
(at 115.57 45.72 0)
(fields_autoplaced yes)
(effects
(font
(size 1.27 1.27)
)
(justify left bottom)
)
(uuid "dec4e873-7ff9-4632-8b58-f929fe6629ca")
(property "Netclass" ""
(at -199.39 -15.24 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Component Class" "Class2"
(at 116.2685 43.18 0)
(effects
(font
(size 1.27 1.27)
(italic yes)
)
(justify left)
)
)
)
(symbol
(lib_id "Amplifier_Operational:TL072")
(at 74.93 62.23 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(fields_autoplaced yes)
(uuid "521c74f6-a90a-4165-ba5b-74e4986fbfb5")
(property "Reference" "U1"
(at 74.93 52.07 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Value" "TL072"
(at 74.93 54.61 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Footprint" "Package_SO:SO-8_5.3x6.2mm_P1.27mm"
(at 74.93 62.23 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "http://www.ti.com/lit/ds/symlink/tl071.pdf"
(at 74.93 62.23 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "Dual Low-Noise JFET-Input Operational Amplifiers, DIP-8/SOIC-8"
(at 74.93 62.23 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Component Class" "Class1"
(at 74.93 62.23 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(pin "3"
(uuid "0a60a206-2dc8-4b92-a7be-126df13c85da")
)
(pin "2"
(uuid "b8ab93b0-0173-432e-85ed-64d6e0b531e0")
)
(pin "1"
(uuid "31022ebe-9ec7-4f79-9dd6-a320ec2cdc46")
)
(pin "5"
(uuid "6e87584c-ea7d-4388-9198-d4f8e33418db")
)
(pin "6"
(uuid "683f1791-0e09-4b93-863a-50f522c42231")
)
(pin "7"
(uuid "3abeb01e-4937-4584-a986-3b6827ccb17b")
)
(pin "8"
(uuid "153c2aa2-4642-461a-b93e-2375ef54c4f0")
)
(pin "4"
(uuid "e09162f6-ff12-43f4-bcac-fffde8eda559")
)
(instances
(project ""
(path "/bfc9b4d1-186d-4d8a-9f35-9d071f1d54fd"
(reference "U1")
(unit 1)
)
)
)
)
(symbol
(lib_id "Amplifier_Operational:TL072")
(at 121.92 63.5 0)
(unit 2)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(fields_autoplaced yes)
(uuid "da40a256-1fbb-4db4-9c2c-275f5c1d0f3c")
(property "Reference" "U1"
(at 121.92 53.34 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Value" "TL072"
(at 121.92 55.88 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Footprint" "Package_SO:SO-8_5.3x6.2mm_P1.27mm"
(at 121.92 63.5 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "http://www.ti.com/lit/ds/symlink/tl071.pdf"
(at 121.92 63.5 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "Dual Low-Noise JFET-Input Operational Amplifiers, DIP-8/SOIC-8"
(at 121.92 63.5 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Component Class" "Class1"
(at 121.92 63.5 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(pin "3"
(uuid "0a60a206-2dc8-4b92-a7be-126df13c85da")
)
(pin "2"
(uuid "b8ab93b0-0173-432e-85ed-64d6e0b531e0")
)
(pin "1"
(uuid "31022ebe-9ec7-4f79-9dd6-a320ec2cdc46")
)
(pin "5"
(uuid "6e87584c-ea7d-4388-9198-d4f8e33418db")
)
(pin "6"
(uuid "683f1791-0e09-4b93-863a-50f522c42231")
)
(pin "7"
(uuid "3abeb01e-4937-4584-a986-3b6827ccb17b")
)
(pin "8"
(uuid "153c2aa2-4642-461a-b93e-2375ef54c4f0")
)
(pin "4"
(uuid "e09162f6-ff12-43f4-bcac-fffde8eda559")
)
(instances
(project ""
(path "/bfc9b4d1-186d-4d8a-9f35-9d071f1d54fd"
(reference "U1")
(unit 2)
)
)
)
)
(symbol
(lib_id "Amplifier_Operational:TL072")
(at 176.53 66.04 0)
(unit 3)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(dnp no)
(fields_autoplaced yes)
(uuid "ec280545-6554-44ce-b281-8b6027100352")
(property "Reference" "U1"
(at 175.26 64.77 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Value" "TL072"
(at 175.26 67.31 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
)
(property "Footprint" "Package_SO:SO-8_5.3x6.2mm_P1.27mm"
(at 176.53 66.04 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "http://www.ti.com/lit/ds/symlink/tl071.pdf"
(at 176.53 66.04 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "Dual Low-Noise JFET-Input Operational Amplifiers, DIP-8/SOIC-8"
(at 176.53 66.04 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Component Class" "Class1"
(at 176.53 66.04 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(pin "3"
(uuid "0a60a206-2dc8-4b92-a7be-126df13c85da")
)
(pin "2"
(uuid "b8ab93b0-0173-432e-85ed-64d6e0b531e0")
)
(pin "1"
(uuid "31022ebe-9ec7-4f79-9dd6-a320ec2cdc46")
)
(pin "5"
(uuid "6e87584c-ea7d-4388-9198-d4f8e33418db")
)
(pin "6"
(uuid "683f1791-0e09-4b93-863a-50f522c42231")
)
(pin "7"
(uuid "3abeb01e-4937-4584-a986-3b6827ccb17b")
)
(pin "8"
(uuid "153c2aa2-4642-461a-b93e-2375ef54c4f0")
)
(pin "4"
(uuid "e09162f6-ff12-43f4-bcac-fffde8eda559")
)
(instances
(project ""
(path "/bfc9b4d1-186d-4d8a-9f35-9d071f1d54fd"
(reference "U1")
(unit 3)
)
)
)
)
(sheet_instances
(path "/"
(page "1")
)
)
(embedded_fonts no)
)

View File

@ -0,0 +1,94 @@
(export (version "E")
(design
(source "/component_classes/component_classes.kicad_sch")
(date "2024-09-29T22:57:25+0100")
(tool "Eeschema 8.99.0-2480-g2270f03d8e-dirty")
(sheet (number "1") (name "/") (tstamps "/")
(title_block
(title)
(company)
(rev)
(date)
(source "component_classes.kicad_sch")
(comment (number "1") (value ""))
(comment (number "2") (value ""))
(comment (number "3") (value ""))
(comment (number "4") (value ""))
(comment (number "5") (value ""))
(comment (number "6") (value ""))
(comment (number "7") (value ""))
(comment (number "8") (value ""))
(comment (number "9") (value "")))))
(components
(comp (ref "U1")
(value "TL072")
(footprint "Package_SO:SO-8_5.3x6.2mm_P1.27mm")
(datasheet "http://www.ti.com/lit/ds/symlink/tl071.pdf")
(description "Dual Low-Noise JFET-Input Operational Amplifiers, DIP-8/SOIC-8")
(fields
(field (name "Component Class") "Class1")
(field (name "Footprint") "Package_SO:SO-8_5.3x6.2mm_P1.27mm")
(field (name "Datasheet") "http://www.ti.com/lit/ds/symlink/tl071.pdf")
(field (name "Description") "Dual Low-Noise JFET-Input Operational Amplifiers, DIP-8/SOIC-8"))
(libsource (lib "Amplifier_Operational") (part "TL072") (description "Dual Low-Noise JFET-Input Operational Amplifiers, DIP-8/SOIC-8"))
(property (name "Component Class") (value "Class1"))
(property (name "Sheetname") (value "Root"))
(property (name "Sheetfile") (value "component_classes.kicad_sch"))
(property (name "ki_keywords") (value "dual opamp"))
(property (name "ki_fp_filters") (value "SOIC*3.9x4.9mm*P1.27mm* DIP*W7.62mm* TO*99* OnSemi*Micro8* TSSOP*3x3mm*P0.65mm* TSSOP*4.4x3mm*P0.65mm* MSOP*3x3mm*P0.65mm* SSOP*3.9x4.9mm*P0.635mm* LFCSP*2x2mm*P0.5mm* *SIP* SOIC*5.3x6.2mm*P1.27mm*"))
(sheetpath (names "/") (tstamps "/"))
(component_classes
(class "Class1")
(class "Class2"))
(tstamps "ec280545-6554-44ce-b281-8b6027100352" "da40a256-1fbb-4db4-9c2c-275f5c1d0f3c" "521c74f6-a90a-4165-ba5b-74e4986fbfb5")))
(libparts
(libpart (lib "Amplifier_Operational") (part "TL072")
(description "Dual Low-Noise JFET-Input Operational Amplifiers, DIP-8/SOIC-8")
(docs "http://www.ti.com/lit/ds/symlink/tl071.pdf")
(footprints
(fp "SOIC*3.9x4.9mm*P1.27mm*")
(fp "DIP*W7.62mm*")
(fp "TO*99*")
(fp "OnSemi*Micro8*")
(fp "TSSOP*3x3mm*P0.65mm*")
(fp "TSSOP*4.4x3mm*P0.65mm*")
(fp "MSOP*3x3mm*P0.65mm*")
(fp "SSOP*3.9x4.9mm*P0.635mm*")
(fp "LFCSP*2x2mm*P0.5mm*")
(fp "*SIP*")
(fp "SOIC*5.3x6.2mm*P1.27mm*"))
(fields
(field (name "Reference") "U")
(field (name "Value") "TL072")
(field (name "Footprint"))
(field (name "Datasheet") "http://www.ti.com/lit/ds/symlink/tl071.pdf")
(field (name "Description") "Dual Low-Noise JFET-Input Operational Amplifiers, DIP-8/SOIC-8"))
(pins
(pin (num "1") (name "") (type "output"))
(pin (num "2") (name "-") (type "input"))
(pin (num "3") (name "+") (type "input"))
(pin (num "4") (name "V-") (type "power_in"))
(pin (num "5") (name "+") (type "input"))
(pin (num "6") (name "-") (type "input"))
(pin (num "7") (name "") (type "output"))
(pin (num "8") (name "V+") (type "power_in")))))
(libraries
(library (logical "Amplifier_Operational")
(uri "/kicad/KiCad.app/Contents/SharedSupport/symbols//Amplifier_Operational.kicad_sym")))
(nets
(net (code "1") (name "unconnected-(U1-Pad1)")
(node (ref "U1") (pin "1") (pintype "output")))
(net (code "2") (name "unconnected-(U1-Pad7)")
(node (ref "U1") (pin "7") (pintype "output")))
(net (code "3") (name "unconnected-(U1A-+-Pad3)")
(node (ref "U1") (pin "3") (pinfunction "+") (pintype "input")))
(net (code "4") (name "unconnected-(U1A---Pad2)")
(node (ref "U1") (pin "2") (pinfunction "-") (pintype "input")))
(net (code "5") (name "unconnected-(U1B-+-Pad5)")
(node (ref "U1") (pin "5") (pinfunction "+") (pintype "input")))
(net (code "6") (name "unconnected-(U1B---Pad6)")
(node (ref "U1") (pin "6") (pinfunction "-") (pintype "input")))
(net (code "7") (name "unconnected-(U1C-V+-Pad8)")
(node (ref "U1") (pin "8") (pinfunction "V+") (pintype "power_in")))
(net (code "8") (name "unconnected-(U1C-V--Pad4)")
(node (ref "U1") (pin "4") (pinfunction "V-") (pintype "power_in")))))

View File

@ -87,6 +87,10 @@ public:
BOOST_REQUIRE_EQUAL( goldenNet.GetPinName(), testNet.GetPinName() );
BOOST_REQUIRE_EQUAL( goldenNet.GetPinType(), testNet.GetPinType() );
}
// And the the resolved component class is the same
BOOST_REQUIRE( goldenComp->GetComponentClassNames()
== refComp->GetComponentClassNames() );
}
}
};
@ -209,5 +213,10 @@ BOOST_AUTO_TEST_CASE( Issue16439 )
TestNetlist( "issue16439" );
}
BOOST_AUTO_TEST_CASE( ComponentClasses )
{
TestNetlist( "component_classes" );
}
BOOST_AUTO_TEST_SUITE_END()