mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2024-11-24 00:34:47 +00:00
68196ad3f3
We don't allow these to be set on the default netclass, so we don't need to resolve it if they are missing in a custom netclass. The renderer will use the colors specified in the application color scheme.
1235 lines
36 KiB
C++
1235 lines
36 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2020 CERN
|
|
* Copyright (C) 2021-2023 KiCad Developers, see AUTHORS.txt for contributors.
|
|
* @author Jon Evans <jon@craftyjon.com>
|
|
*
|
|
* 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 <algorithm>
|
|
#include <limits>
|
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
#include <project/net_settings.h>
|
|
#include <settings/parameters.h>
|
|
#include <settings/settings_manager.h>
|
|
#include <string_utils.h>
|
|
#include <base_units.h>
|
|
|
|
|
|
// const int netSettingsSchemaVersion = 0;
|
|
// const int netSettingsSchemaVersion = 1; // new overbar syntax
|
|
// const int netSettingsSchemaVersion = 2; // exclude buses from netclass members
|
|
// const int netSettingsSchemaVersion = 3; // netclass assignment patterns
|
|
const int netSettingsSchemaVersion = 4; // netclass ordering
|
|
|
|
|
|
static std::optional<int> getInPcbUnits( const nlohmann::json& aObj, const std::string& aKey,
|
|
std::optional<int> aDefault = std::optional<int>() )
|
|
{
|
|
if( aObj.contains( aKey ) && aObj[aKey].is_number() )
|
|
return pcbIUScale.mmToIU( aObj[aKey].get<double>() );
|
|
else
|
|
return aDefault;
|
|
};
|
|
|
|
|
|
static std::optional<int> getInSchUnits( const nlohmann::json& aObj, const std::string& aKey,
|
|
std::optional<int> aDefault = std::optional<int>() )
|
|
{
|
|
if( aObj.contains( aKey ) && aObj[aKey].is_number() )
|
|
return schIUScale.MilsToIU( aObj[aKey].get<double>() );
|
|
else
|
|
return aDefault;
|
|
};
|
|
|
|
|
|
NET_SETTINGS::NET_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ) :
|
|
NESTED_SETTINGS( "net_settings", netSettingsSchemaVersion, aParent, aPath, false )
|
|
{
|
|
m_defaultNetClass = std::make_shared<NETCLASS>( NETCLASS::Default, true );
|
|
m_defaultNetClass->SetDescription( _( "This is the default net class." ) );
|
|
|
|
auto saveNetclass =
|
|
[]( nlohmann::json& json_array, const std::shared_ptr<NETCLASS>& nc )
|
|
{
|
|
// Note: we're in common/, but we do happen to know which of these
|
|
// fields are used in which units system.
|
|
nlohmann::json nc_json = { { "name", nc->GetName().ToUTF8() },
|
|
{ "priority", nc->GetPriority() },
|
|
{ "schematic_color", nc->GetSchematicColor( true ) },
|
|
{ "pcb_color", nc->GetPcbColor( true ) } };
|
|
|
|
auto saveInPcbUnits =
|
|
[]( nlohmann::json& json, const std::string& aKey, int aValue )
|
|
{
|
|
json.push_back( { aKey, pcbIUScale.IUTomm( aValue ) } );
|
|
};
|
|
|
|
if( nc->HasWireWidth() )
|
|
nc_json.push_back(
|
|
{ "wire_width", schIUScale.IUToMils( nc->GetWireWidth() ) } );
|
|
|
|
if( nc->HasBusWidth() )
|
|
nc_json.push_back( { "bus_width", schIUScale.IUToMils( nc->GetBusWidth() ) } );
|
|
|
|
if( nc->HasLineStyle() )
|
|
nc_json.push_back( { "line_style", nc->GetLineStyle() } );
|
|
|
|
if( nc->HasClearance() )
|
|
saveInPcbUnits( nc_json, "clearance", nc->GetClearance() );
|
|
|
|
if( nc->HasTrackWidth() )
|
|
saveInPcbUnits( nc_json, "track_width", nc->GetTrackWidth() );
|
|
|
|
if( nc->HasViaDiameter() )
|
|
saveInPcbUnits( nc_json, "via_diameter", nc->GetViaDiameter() );
|
|
|
|
if( nc->HasViaDrill() )
|
|
saveInPcbUnits( nc_json, "via_drill", nc->GetViaDrill() );
|
|
|
|
if( nc->HasuViaDiameter() )
|
|
saveInPcbUnits( nc_json, "microvia_diameter", nc->GetuViaDiameter() );
|
|
|
|
if( nc->HasuViaDrill() )
|
|
saveInPcbUnits( nc_json, "microvia_drill", nc->GetuViaDrill() );
|
|
|
|
if( nc->HasDiffPairWidth() )
|
|
saveInPcbUnits( nc_json, "diff_pair_width", nc->GetDiffPairWidth() );
|
|
|
|
if( nc->HasDiffPairGap() )
|
|
saveInPcbUnits( nc_json, "diff_pair_gap", nc->GetDiffPairGap() );
|
|
|
|
if( nc->HasDiffPairViaGap() )
|
|
saveInPcbUnits( nc_json, "diff_pair_via_gap", nc->GetDiffPairViaGap() );
|
|
|
|
json_array.push_back( nc_json );
|
|
};
|
|
|
|
auto readNetClass =
|
|
[]( const nlohmann::json& entry )
|
|
{
|
|
wxString name = entry["name"];
|
|
|
|
std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( name, false );
|
|
|
|
int priority = entry["priority"];
|
|
nc->SetPriority( priority );
|
|
|
|
if( auto value = getInPcbUnits( entry, "clearance" ) )
|
|
nc->SetClearance( *value );
|
|
|
|
if( auto value = getInPcbUnits( entry, "track_width" ) )
|
|
nc->SetTrackWidth( *value );
|
|
|
|
if( auto value = getInPcbUnits( entry, "via_diameter" ) )
|
|
nc->SetViaDiameter( *value );
|
|
|
|
if( auto value = getInPcbUnits( entry, "via_drill" ) )
|
|
nc->SetViaDrill( *value );
|
|
|
|
if( auto value = getInPcbUnits( entry, "microvia_diameter" ) )
|
|
nc->SetuViaDiameter( *value );
|
|
|
|
if( auto value = getInPcbUnits( entry, "microvia_drill" ) )
|
|
nc->SetuViaDrill( *value );
|
|
|
|
if( auto value = getInPcbUnits( entry, "diff_pair_width" ) )
|
|
nc->SetDiffPairWidth( *value );
|
|
|
|
if( auto value = getInPcbUnits( entry, "diff_pair_gap" ) )
|
|
nc->SetDiffPairGap( *value );
|
|
|
|
if( auto value = getInPcbUnits( entry, "diff_pair_via_gap" ) )
|
|
nc->SetDiffPairViaGap( *value );
|
|
|
|
if( auto value = getInSchUnits( entry, "wire_width" ) )
|
|
nc->SetWireWidth( *value );
|
|
|
|
if( auto value = getInSchUnits( entry, "bus_width" ) )
|
|
nc->SetBusWidth( *value );
|
|
|
|
if( entry.contains( "line_style" ) && entry["line_style"].is_number() )
|
|
nc->SetLineStyle( entry["line_style"].get<int>() );
|
|
|
|
if( entry.contains( "pcb_color" ) && entry["pcb_color"].is_string() )
|
|
nc->SetPcbColor( entry["pcb_color"].get<KIGFX::COLOR4D>() );
|
|
|
|
if( entry.contains( "schematic_color" )
|
|
&& entry["schematic_color"].is_string() )
|
|
{
|
|
nc->SetSchematicColor( entry["schematic_color"].get<KIGFX::COLOR4D>() );
|
|
}
|
|
|
|
return nc;
|
|
};
|
|
|
|
m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "classes",
|
|
[&]() -> nlohmann::json
|
|
{
|
|
nlohmann::json ret = nlohmann::json::array();
|
|
|
|
if( m_defaultNetClass )
|
|
saveNetclass( ret, m_defaultNetClass );
|
|
|
|
for( const auto& [name, netclass] : m_netClasses )
|
|
saveNetclass( ret, netclass );
|
|
|
|
return ret;
|
|
},
|
|
[&]( const nlohmann::json& aJson )
|
|
{
|
|
if( !aJson.is_array() )
|
|
return;
|
|
|
|
m_netClasses.clear();
|
|
|
|
for( const nlohmann::json& entry : aJson )
|
|
{
|
|
if( !entry.is_object() || !entry.contains( "name" ) )
|
|
continue;
|
|
|
|
std::shared_ptr<NETCLASS> nc = readNetClass( entry );
|
|
|
|
if( nc->IsDefault() )
|
|
m_defaultNetClass = nc;
|
|
else
|
|
m_netClasses[nc->GetName()] = nc;
|
|
}
|
|
},
|
|
{} ) );
|
|
|
|
m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "net_colors",
|
|
[&]() -> nlohmann::json
|
|
{
|
|
nlohmann::json ret = {};
|
|
|
|
for( const auto& [netname, color] : m_netColorAssignments )
|
|
{
|
|
std::string key( netname.ToUTF8() );
|
|
ret[ std::move( key ) ] = color;
|
|
}
|
|
|
|
return ret;
|
|
},
|
|
[&]( const nlohmann::json& aJson )
|
|
{
|
|
if( !aJson.is_object() )
|
|
return;
|
|
|
|
m_netColorAssignments.clear();
|
|
|
|
for( const auto& pair : aJson.items() )
|
|
{
|
|
wxString key( pair.key().c_str(), wxConvUTF8 );
|
|
m_netColorAssignments[std::move( key )] = pair.value().get<KIGFX::COLOR4D>();
|
|
}
|
|
},
|
|
{} ) );
|
|
|
|
m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "netclass_assignments",
|
|
[&]() -> nlohmann::json
|
|
{
|
|
nlohmann::json ret = {};
|
|
|
|
for( const auto& [netname, netclassNames] : m_netClassLabelAssignments )
|
|
{
|
|
nlohmann::json netclassesJson = nlohmann::json::array();
|
|
|
|
for( const auto& netclass : netclassNames )
|
|
{
|
|
std::string netclassStr( netclass.ToUTF8() );
|
|
netclassesJson.push_back( std::move( netclassStr ) );
|
|
}
|
|
|
|
std::string key( netname.ToUTF8() );
|
|
ret[std::move( key )] = netclassesJson;
|
|
}
|
|
|
|
return ret;
|
|
},
|
|
[&]( const nlohmann::json& aJson )
|
|
{
|
|
if( !aJson.is_object() )
|
|
return;
|
|
|
|
m_netClassLabelAssignments.clear();
|
|
|
|
for( const auto& pair : aJson.items() )
|
|
{
|
|
wxString key( pair.key().c_str(), wxConvUTF8 );
|
|
|
|
for( const auto& netclassName : pair.value() )
|
|
m_netClassLabelAssignments[key].insert( netclassName.get<wxString>() );
|
|
}
|
|
},
|
|
{} ) );
|
|
|
|
m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "netclass_patterns",
|
|
[&]() -> nlohmann::json
|
|
{
|
|
nlohmann::json ret = nlohmann::json::array();
|
|
|
|
for( const auto& [matcher, netclassName] : m_netClassPatternAssignments )
|
|
{
|
|
nlohmann::json pattern_json = {
|
|
{ "pattern", matcher->GetPattern().ToUTF8() },
|
|
{ "netclass", netclassName.ToUTF8() }
|
|
};
|
|
|
|
ret.push_back( std::move( pattern_json ) );
|
|
}
|
|
|
|
return ret;
|
|
},
|
|
[&]( const nlohmann::json& aJson )
|
|
{
|
|
if( !aJson.is_array() )
|
|
return;
|
|
|
|
m_netClassPatternAssignments.clear();
|
|
|
|
for( const nlohmann::json& entry : aJson )
|
|
{
|
|
if( !entry.is_object() )
|
|
continue;
|
|
|
|
if( entry.contains( "pattern" ) && entry["pattern"].is_string()
|
|
&& entry.contains( "netclass" ) && entry["netclass"].is_string() )
|
|
{
|
|
wxString pattern = entry["pattern"].get<wxString>();
|
|
wxString netclass = entry["netclass"].get<wxString>();
|
|
|
|
m_netClassPatternAssignments.push_back(
|
|
{ std::make_unique<EDA_COMBINED_MATCHER>( pattern, CTX_NETCLASS ),
|
|
netclass } );
|
|
}
|
|
}
|
|
},
|
|
{} ) );
|
|
|
|
registerMigration( 0, 1, std::bind( &NET_SETTINGS::migrateSchema0to1, this ) );
|
|
registerMigration( 1, 2, std::bind( &NET_SETTINGS::migrateSchema1to2, this ) );
|
|
registerMigration( 2, 3, std::bind( &NET_SETTINGS::migrateSchema2to3, this ) );
|
|
registerMigration( 3, 4, std::bind( &NET_SETTINGS::migrateSchema3to4, this ) );
|
|
}
|
|
|
|
|
|
NET_SETTINGS::~NET_SETTINGS()
|
|
{
|
|
// Release early before destroying members
|
|
if( m_parent )
|
|
{
|
|
m_parent->ReleaseNestedSettings( this );
|
|
m_parent = nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
bool NET_SETTINGS::operator==( const NET_SETTINGS& aOther ) const
|
|
{
|
|
if( !std::equal( std::begin( m_netClasses ), std::end( m_netClasses ),
|
|
std::begin( aOther.m_netClasses ) ) )
|
|
return false;
|
|
|
|
if( !std::equal( std::begin( m_netClassPatternAssignments ),
|
|
std::end( m_netClassPatternAssignments ),
|
|
std::begin( aOther.m_netClassPatternAssignments ) ) )
|
|
return false;
|
|
|
|
if( !std::equal( std::begin( m_netClassLabelAssignments ),
|
|
std::end( m_netClassLabelAssignments ),
|
|
std::begin( aOther.m_netClassLabelAssignments ) ) )
|
|
return false;
|
|
|
|
|
|
if( !std::equal( std::begin( m_netColorAssignments ), std::end( m_netColorAssignments ),
|
|
std::begin( aOther.m_netColorAssignments ) ) )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool NET_SETTINGS::migrateSchema0to1()
|
|
{
|
|
if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
|
|
{
|
|
for( auto& netClass : m_internals->At( "classes" ).items() )
|
|
{
|
|
if( netClass.value().contains( "nets" ) && netClass.value()["nets"].is_array() )
|
|
{
|
|
nlohmann::json migrated = nlohmann::json::array();
|
|
|
|
for( auto& net : netClass.value()["nets"].items() )
|
|
migrated.push_back( ConvertToNewOverbarNotation( net.value().get<wxString>() ) );
|
|
|
|
netClass.value()["nets"] = migrated;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool NET_SETTINGS::migrateSchema1to2()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
bool NET_SETTINGS::migrateSchema2to3()
|
|
{
|
|
if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
|
|
{
|
|
nlohmann::json patterns = nlohmann::json::array();
|
|
|
|
for( auto& netClass : m_internals->At( "classes" ).items() )
|
|
{
|
|
if( netClass.value().contains( "name" )
|
|
&& netClass.value().contains( "nets" )
|
|
&& netClass.value()["nets"].is_array() )
|
|
{
|
|
wxString netClassName = netClass.value()["name"].get<wxString>();
|
|
|
|
for( auto& net : netClass.value()["nets"].items() )
|
|
{
|
|
nlohmann::json pattern_json = {
|
|
{ "pattern", net.value().get<wxString>() },
|
|
{ "netclass", netClassName }
|
|
};
|
|
|
|
patterns.push_back( pattern_json );
|
|
}
|
|
}
|
|
}
|
|
|
|
m_internals->SetFromString( "netclass_patterns", patterns );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool NET_SETTINGS::migrateSchema3to4()
|
|
{
|
|
// Add priority field to netclasses
|
|
if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
|
|
{
|
|
int priority = 0;
|
|
|
|
for( auto& netClass : m_internals->At( "classes" ).items() )
|
|
{
|
|
if( netClass.value()["name"].get<wxString>() == NETCLASS::Default )
|
|
netClass.value()["priority"] = std::numeric_limits<int>::max();
|
|
else
|
|
netClass.value()["priority"] = priority++;
|
|
}
|
|
}
|
|
|
|
// Move netclass assignments to a list
|
|
if( m_internals->contains( "netclass_assignments" )
|
|
&& m_internals->At( "netclass_assignments" ).is_object() )
|
|
{
|
|
nlohmann::json migrated = {};
|
|
|
|
for( const auto& pair : m_internals->At( "netclass_assignments" ).items() )
|
|
{
|
|
nlohmann::json netclassesJson = nlohmann::json::array();
|
|
|
|
if( pair.value().get<wxString>() != wxEmptyString )
|
|
netclassesJson.push_back( pair.value() );
|
|
|
|
migrated[pair.key()] = netclassesJson;
|
|
}
|
|
|
|
m_internals->SetFromString( "netclass_assignments", migrated );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::SetDefaultNetclass( std::shared_ptr<NETCLASS> netclass )
|
|
{
|
|
m_defaultNetClass = netclass;
|
|
}
|
|
|
|
|
|
std::shared_ptr<NETCLASS> NET_SETTINGS::GetDefaultNetclass()
|
|
{
|
|
return m_defaultNetClass;
|
|
}
|
|
|
|
|
|
bool NET_SETTINGS::HasNetclass( const wxString& netclassName ) const
|
|
{
|
|
return m_netClasses.find( netclassName ) != m_netClasses.end();
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::SetNetclass( const wxString& netclassName, std::shared_ptr<NETCLASS>& netclass )
|
|
{
|
|
m_netClasses[netclassName] = netclass;
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::SetNetclasses( const std::map<wxString, std::shared_ptr<NETCLASS>>& netclasses )
|
|
{
|
|
m_netClasses = netclasses;
|
|
}
|
|
|
|
|
|
const std::map<wxString, std::shared_ptr<NETCLASS>>& NET_SETTINGS::GetNetclasses() const
|
|
{
|
|
return m_netClasses;
|
|
}
|
|
|
|
|
|
const std::map<wxString, std::shared_ptr<NETCLASS>>& NET_SETTINGS::GetCompositeNetclasses() const
|
|
{
|
|
return m_compositeNetClasses;
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::ClearNetclasses()
|
|
{
|
|
m_netClasses.clear();
|
|
m_impicitNetClasses.clear();
|
|
}
|
|
|
|
|
|
const std::map<wxString, std::set<wxString>>& NET_SETTINGS::GetNetclassLabelAssignments() const
|
|
{
|
|
return m_netClassLabelAssignments;
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::ClearNetclassLabelAssignments()
|
|
{
|
|
m_netClassLabelAssignments.clear();
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::ClearNetclassLabelAssignment( const wxString& netName )
|
|
{
|
|
m_netClassLabelAssignments.erase( netName );
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::SetNetclassLabelAssignment( const wxString& netName,
|
|
const std::set<wxString>& netclasses )
|
|
{
|
|
m_netClassLabelAssignments[netName] = netclasses;
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::AppendNetclassLabelAssignment( const wxString& netName,
|
|
const std::set<wxString>& netclasses )
|
|
{
|
|
m_netClassLabelAssignments[netName].insert( netclasses.begin(), netclasses.end() );
|
|
}
|
|
|
|
|
|
bool NET_SETTINGS::HasNetclassLabelAssignment( const wxString& netName ) const
|
|
{
|
|
return m_netClassLabelAssignments.find( netName ) != m_netClassLabelAssignments.end();
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::SetNetclassPatternAssignment( const wxString& pattern, const wxString& netclass )
|
|
{
|
|
// Replace existing assignment if we have one
|
|
for( auto& assignment : m_netClassPatternAssignments )
|
|
{
|
|
if( assignment.first->GetPattern() == pattern )
|
|
{
|
|
assignment.second = netclass;
|
|
ClearAllCaches();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// No assignment, add a new one
|
|
m_netClassPatternAssignments.push_back(
|
|
{ std::make_unique<EDA_COMBINED_MATCHER>( pattern, CTX_NETCLASS ), netclass } );
|
|
|
|
ClearAllCaches();
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::SetNetclassPatternAssignments(
|
|
std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>&& netclassPatterns )
|
|
{
|
|
m_netClassPatternAssignments = std::move( netclassPatterns );
|
|
ClearAllCaches();
|
|
}
|
|
|
|
|
|
std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>&
|
|
NET_SETTINGS::GetNetclassPatternAssignments()
|
|
{
|
|
return m_netClassPatternAssignments;
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::ClearNetclassPatternAssignments()
|
|
{
|
|
m_netClassPatternAssignments.clear();
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::ClearCacheForNet( const wxString& netName )
|
|
{
|
|
if( m_effectiveNetclassCache.count( netName ) )
|
|
{
|
|
wxString compositeNetclassName =
|
|
m_effectiveNetclassCache[netName]->GetVariableSubstitutionName();
|
|
m_compositeNetClasses.erase( compositeNetclassName );
|
|
m_effectiveNetclassCache.erase( netName );
|
|
}
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::ClearAllCaches()
|
|
{
|
|
m_effectiveNetclassCache.clear();
|
|
m_compositeNetClasses.clear();
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::SetNetColorAssignment( const wxString& netName, const KIGFX::COLOR4D& color )
|
|
{
|
|
m_netColorAssignments[netName] = color;
|
|
}
|
|
|
|
|
|
const std::map<wxString, KIGFX::COLOR4D>& NET_SETTINGS::GetNetColorAssignments() const
|
|
{
|
|
return m_netColorAssignments;
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::ClearNetColorAssignments()
|
|
{
|
|
m_netColorAssignments.clear();
|
|
}
|
|
|
|
|
|
bool NET_SETTINGS::HasEffectiveNetClass( const wxString& aNetName ) const
|
|
{
|
|
return m_effectiveNetclassCache.count( aNetName ) > 0;
|
|
}
|
|
|
|
|
|
std::shared_ptr<NETCLASS> NET_SETTINGS::GetCachedEffectiveNetClass( const wxString& aNetName ) const
|
|
{
|
|
return m_effectiveNetclassCache.at( aNetName );
|
|
}
|
|
|
|
|
|
std::shared_ptr<NETCLASS> NET_SETTINGS::GetEffectiveNetClass( const wxString& aNetName )
|
|
{
|
|
// Lambda to fetch an explicit netclass. Returns a nullptr if not found
|
|
auto getExplicitNetclass = [this]( const wxString& netclass ) -> std::shared_ptr<NETCLASS>
|
|
{
|
|
if( netclass == NETCLASS::Default )
|
|
return m_defaultNetClass;
|
|
|
|
auto ii = m_netClasses.find( netclass );
|
|
|
|
if( ii == m_netClasses.end() )
|
|
return {};
|
|
else
|
|
return ii->second;
|
|
};
|
|
|
|
// Lambda to fetch or create an implicit netclass (defined with a label, but not configured)
|
|
// These are needed as while they do not provide any netclass parameters, they do now appear in
|
|
// DRC matching strings as an assigned netclass.
|
|
auto getOrAddImplicitNetcless = [this]( const wxString& netclass ) -> std::shared_ptr<NETCLASS>
|
|
{
|
|
auto ii = m_impicitNetClasses.find( netclass );
|
|
|
|
if( ii == m_impicitNetClasses.end() )
|
|
{
|
|
std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( netclass, false );
|
|
nc->SetPriority( std::numeric_limits<int>().max() - 1 ); // Priority > default netclass
|
|
m_impicitNetClasses[netclass] = nc;
|
|
return nc;
|
|
}
|
|
else
|
|
{
|
|
return ii->second;
|
|
}
|
|
};
|
|
|
|
// <no net> is forced to be part of the default netclass.
|
|
if( aNetName.IsEmpty() )
|
|
return m_defaultNetClass;
|
|
|
|
// First check if we have a cached resolved netclass
|
|
auto cacheItr = m_effectiveNetclassCache.find( aNetName );
|
|
|
|
if( cacheItr != m_effectiveNetclassCache.end() )
|
|
return cacheItr->second;
|
|
|
|
// No cache found - build a vector of all netclasses assigned to or matching this net
|
|
std::vector<std::shared_ptr<NETCLASS>> resolvedNetclasses;
|
|
|
|
// First find explicit netclass assignments
|
|
auto it = m_netClassLabelAssignments.find( aNetName );
|
|
|
|
if( it != m_netClassLabelAssignments.end() && it->second.size() > 0 )
|
|
{
|
|
for( const wxString& netclassName : it->second )
|
|
{
|
|
std::shared_ptr<NETCLASS> netclass = getExplicitNetclass( netclassName );
|
|
|
|
if( netclass )
|
|
{
|
|
resolvedNetclasses.push_back( std::move( netclass ) );
|
|
}
|
|
else
|
|
{
|
|
resolvedNetclasses.push_back(
|
|
getOrAddImplicitNetcless( netclassName ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now find any pattern-matched netclass assignments
|
|
for( const auto& [matcher, netclassName] : m_netClassPatternAssignments )
|
|
{
|
|
if( matcher->StartsWith( aNetName ) )
|
|
{
|
|
std::shared_ptr<NETCLASS> netclass = getExplicitNetclass( netclassName );
|
|
|
|
if( netclass )
|
|
{
|
|
resolvedNetclasses.push_back( std::move( netclass ) );
|
|
}
|
|
else
|
|
{
|
|
resolvedNetclasses.push_back( getOrAddImplicitNetcless( netclassName ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle zero resolved netclasses
|
|
if( resolvedNetclasses.size() == 0 )
|
|
{
|
|
m_effectiveNetclassCache[aNetName] = m_defaultNetClass;
|
|
|
|
return m_defaultNetClass;
|
|
}
|
|
|
|
// Make and cache the effective netclass. Note that makeEffectiveNetclass will add the default
|
|
// netclass to resolvedNetclasses if it is needed to complete the netclass paramters set. It
|
|
// will also sort resolvedNetclasses by priority order.
|
|
std::vector<NETCLASS*> netclassPtrs;
|
|
|
|
for( std::shared_ptr<NETCLASS>& nc : resolvedNetclasses )
|
|
netclassPtrs.push_back( nc.get() );
|
|
|
|
wxString name;
|
|
name.Printf( "Effective for net: %s", aNetName );
|
|
std::shared_ptr<NETCLASS> effectiveNetclass = std::make_shared<NETCLASS>( name, false );
|
|
makeEffectiveNetclass( effectiveNetclass, netclassPtrs );
|
|
|
|
if( netclassPtrs.size() == 1 )
|
|
{
|
|
// No defaults were added - just return the primary netclass
|
|
m_effectiveNetclassCache[aNetName] = resolvedNetclasses[0];
|
|
return resolvedNetclasses[0];
|
|
}
|
|
else
|
|
{
|
|
effectiveNetclass->SetConstituentNetclasses( std::move( netclassPtrs ) );
|
|
|
|
m_compositeNetClasses[effectiveNetclass->GetVariableSubstitutionName()] = effectiveNetclass;
|
|
m_effectiveNetclassCache[aNetName] = effectiveNetclass;
|
|
|
|
return effectiveNetclass;
|
|
}
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::RecomputeEffectiveNetclasses()
|
|
{
|
|
for( auto& [ncName, nc] : m_compositeNetClasses )
|
|
{
|
|
// Note this needs to be a copy in case we now need to add the default netclass
|
|
std::vector<NETCLASS*> constituents = nc->GetConstituentNetclasses();
|
|
|
|
wxASSERT( constituents.size() > 0 );
|
|
|
|
// If the last netclass is Default, remove it (it will be re-added if still needed)
|
|
if( ( *constituents.rbegin() )->GetName() == NETCLASS::Default )
|
|
{
|
|
constituents.pop_back();
|
|
}
|
|
|
|
// Remake the netclass from original constituents
|
|
nc->ResetParameters();
|
|
makeEffectiveNetclass( nc, constituents );
|
|
nc->SetConstituentNetclasses( std::move( constituents ) );
|
|
}
|
|
}
|
|
|
|
|
|
void NET_SETTINGS::makeEffectiveNetclass( std::shared_ptr<NETCLASS>& effectiveNetclass,
|
|
std::vector<NETCLASS*>& constituentNetclasses ) const
|
|
{
|
|
// Sort the resolved netclasses by priority (highest first), with same-priority netclasses
|
|
// ordered alphabetically
|
|
std::sort( constituentNetclasses.begin(), constituentNetclasses.end(),
|
|
[]( NETCLASS* nc1, NETCLASS* nc2 )
|
|
{
|
|
int p1 = nc1->GetPriority();
|
|
int p2 = nc2->GetPriority();
|
|
|
|
if( p1 < p2 )
|
|
return true;
|
|
|
|
if (p1 == p2)
|
|
return nc1->GetName().Cmp( nc2->GetName() ) < 0;
|
|
|
|
return false;
|
|
} );
|
|
|
|
// Iterate from lowest priority netclass and fill effective netclass parameters
|
|
for( auto itr = constituentNetclasses.rbegin(); itr != constituentNetclasses.rend(); ++itr )
|
|
{
|
|
NETCLASS* nc = *itr;
|
|
|
|
if( nc->HasClearance() )
|
|
{
|
|
effectiveNetclass->SetClearance( nc->GetClearance() );
|
|
effectiveNetclass->SetClearanceParent( nc );
|
|
}
|
|
|
|
if( nc->HasTrackWidth() )
|
|
{
|
|
effectiveNetclass->SetTrackWidth( nc->GetTrackWidth() );
|
|
effectiveNetclass->SetTrackWidthParent( nc );
|
|
}
|
|
|
|
if( nc->HasViaDiameter() )
|
|
{
|
|
effectiveNetclass->SetViaDiameter( nc->GetViaDiameter() );
|
|
effectiveNetclass->SetViaDiameterParent( nc );
|
|
}
|
|
|
|
if( nc->HasViaDrill() )
|
|
{
|
|
effectiveNetclass->SetViaDrill( nc->GetViaDrill() );
|
|
effectiveNetclass->SetViaDrillParent( nc );
|
|
}
|
|
|
|
if( nc->HasuViaDiameter() )
|
|
{
|
|
effectiveNetclass->SetuViaDiameter( nc->GetuViaDiameter() );
|
|
effectiveNetclass->SetuViaDiameterParent( nc );
|
|
}
|
|
|
|
if( nc->HasuViaDrill() )
|
|
{
|
|
effectiveNetclass->SetuViaDrill( nc->GetuViaDrill() );
|
|
effectiveNetclass->SetuViaDrillParent( nc );
|
|
}
|
|
|
|
if( nc->HasDiffPairWidth() )
|
|
{
|
|
effectiveNetclass->SetDiffPairWidth( nc->GetDiffPairWidth() );
|
|
effectiveNetclass->SetDiffPairWidthParent( nc );
|
|
}
|
|
|
|
if( nc->HasDiffPairGap() )
|
|
{
|
|
effectiveNetclass->SetDiffPairGap( nc->GetDiffPairGap() );
|
|
effectiveNetclass->SetDiffPairGapParent( nc );
|
|
}
|
|
|
|
if( nc->HasDiffPairViaGap() )
|
|
{
|
|
effectiveNetclass->SetDiffPairViaGap( nc->GetDiffPairViaGap() );
|
|
effectiveNetclass->SetDiffPairViaGapParent( nc );
|
|
}
|
|
|
|
if( nc->HasWireWidth() )
|
|
{
|
|
effectiveNetclass->SetWireWidth( nc->GetWireWidth() );
|
|
effectiveNetclass->SetWireWidthParent( nc );
|
|
}
|
|
|
|
if( nc->HasBusWidth() )
|
|
{
|
|
effectiveNetclass->SetBusWidth( nc->GetBusWidth() );
|
|
effectiveNetclass->SetBusWidthParent( nc );
|
|
}
|
|
|
|
if( nc->HasLineStyle() )
|
|
{
|
|
effectiveNetclass->SetLineStyle( nc->GetLineStyle() );
|
|
effectiveNetclass->SetLineStyleParent( nc );
|
|
}
|
|
|
|
COLOR4D pcbColor = nc->GetPcbColor();
|
|
|
|
if( pcbColor != COLOR4D::UNSPECIFIED )
|
|
{
|
|
effectiveNetclass->SetPcbColor( pcbColor );
|
|
effectiveNetclass->SetPcbColorParent( nc );
|
|
}
|
|
|
|
COLOR4D schColor = nc->GetSchematicColor();
|
|
|
|
if( schColor != COLOR4D::UNSPECIFIED )
|
|
{
|
|
effectiveNetclass->SetSchematicColor( schColor );
|
|
effectiveNetclass->SetSchematicColorParent( nc );
|
|
}
|
|
}
|
|
|
|
// Fill in any required defaults
|
|
if( addMissingDefaults( effectiveNetclass.get() ) )
|
|
constituentNetclasses.push_back( m_defaultNetClass.get() );
|
|
}
|
|
|
|
|
|
bool NET_SETTINGS::addMissingDefaults( NETCLASS* nc ) const
|
|
{
|
|
bool addedDefault = false;
|
|
|
|
if( !nc->HasClearance() )
|
|
{
|
|
addedDefault = true;
|
|
nc->SetClearance( m_defaultNetClass->GetClearance() );
|
|
nc->SetClearanceParent( m_defaultNetClass.get() );
|
|
}
|
|
|
|
if( !nc->HasTrackWidth() )
|
|
{
|
|
addedDefault = true;
|
|
nc->SetTrackWidth( m_defaultNetClass->GetTrackWidth() );
|
|
nc->SetTrackWidthParent( m_defaultNetClass.get() );
|
|
}
|
|
|
|
if( !nc->HasViaDiameter() )
|
|
{
|
|
addedDefault = true;
|
|
nc->SetViaDiameter( m_defaultNetClass->GetViaDiameter() );
|
|
nc->SetViaDiameterParent( m_defaultNetClass.get() );
|
|
}
|
|
|
|
if( !nc->HasViaDrill() )
|
|
{
|
|
addedDefault = true;
|
|
nc->SetViaDrill( m_defaultNetClass->GetViaDrill() );
|
|
nc->SetViaDrillParent( m_defaultNetClass.get() );
|
|
}
|
|
|
|
if( !nc->HasuViaDiameter() )
|
|
{
|
|
addedDefault = true;
|
|
nc->SetuViaDiameter( m_defaultNetClass->GetuViaDiameter() );
|
|
nc->SetuViaDiameterParent( m_defaultNetClass.get() );
|
|
}
|
|
|
|
if( !nc->HasuViaDrill() )
|
|
{
|
|
addedDefault = true;
|
|
nc->SetuViaDrill( m_defaultNetClass->GetuViaDrill() );
|
|
nc->SetuViaDrillParent( m_defaultNetClass.get() );
|
|
}
|
|
|
|
if( !nc->HasDiffPairWidth() )
|
|
{
|
|
addedDefault = true;
|
|
nc->SetDiffPairWidth( m_defaultNetClass->GetDiffPairWidth() );
|
|
nc->SetDiffPairWidthParent( m_defaultNetClass.get() );
|
|
}
|
|
|
|
if( !nc->HasDiffPairGap() )
|
|
{
|
|
addedDefault = true;
|
|
nc->SetDiffPairGap( m_defaultNetClass->GetDiffPairGap() );
|
|
nc->SetDiffPairGapParent( m_defaultNetClass.get() );
|
|
}
|
|
|
|
// Currently this is only on the default netclass, and not editable in the setup panel
|
|
// if( !nc->HasDiffPairViaGap() )
|
|
// {
|
|
// addedDefault = true;
|
|
// nc->SetDiffPairViaGap( m_defaultNetClass->GetDiffPairViaGap() );
|
|
// nc->SetDiffPairViaGapParent( m_defaultNetClass.get() );
|
|
// }
|
|
|
|
if( !nc->HasWireWidth() )
|
|
{
|
|
addedDefault = true;
|
|
nc->SetWireWidth( m_defaultNetClass->GetWireWidth() );
|
|
nc->SetWireWidthParent( m_defaultNetClass.get() );
|
|
}
|
|
|
|
if( !nc->HasBusWidth() )
|
|
{
|
|
addedDefault = true;
|
|
nc->SetBusWidth( m_defaultNetClass->GetBusWidth() );
|
|
nc->SetBusWidthParent( m_defaultNetClass.get() );
|
|
}
|
|
|
|
return addedDefault;
|
|
}
|
|
|
|
|
|
std::shared_ptr<NETCLASS> NET_SETTINGS::GetNetClassByName( const wxString& aNetClassName ) const
|
|
{
|
|
auto ii = m_netClasses.find( aNetClassName );
|
|
|
|
if( ii == m_netClasses.end() )
|
|
return m_defaultNetClass;
|
|
else
|
|
return ii->second;
|
|
}
|
|
|
|
|
|
static bool isSuperSubOverbar( wxChar c )
|
|
{
|
|
return c == '_' || c == '^' || c == '~';
|
|
}
|
|
|
|
|
|
bool NET_SETTINGS::ParseBusVector( const wxString& aBus, wxString* aName,
|
|
std::vector<wxString>* aMemberList )
|
|
{
|
|
auto isDigit = []( wxChar c )
|
|
{
|
|
static wxString digits( wxT( "0123456789" ) );
|
|
return digits.Contains( c );
|
|
};
|
|
|
|
size_t busLen = aBus.length();
|
|
size_t i = 0;
|
|
wxString prefix;
|
|
wxString suffix;
|
|
wxString tmp;
|
|
long begin = 0;
|
|
long end = 0;
|
|
int braceNesting = 0;
|
|
|
|
prefix.reserve( busLen );
|
|
|
|
// Parse prefix
|
|
//
|
|
for( ; i < busLen; ++i )
|
|
{
|
|
if( aBus[i] == '{' )
|
|
{
|
|
if( i > 0 && isSuperSubOverbar( aBus[i-1] ) )
|
|
braceNesting++;
|
|
else
|
|
return false;
|
|
}
|
|
else if( aBus[i] == '}' )
|
|
{
|
|
braceNesting--;
|
|
}
|
|
|
|
if( aBus[i] == ' ' || aBus[i] == ']' )
|
|
return false;
|
|
|
|
if( aBus[i] == '[' )
|
|
break;
|
|
|
|
prefix += aBus[i];
|
|
}
|
|
|
|
// Parse start number
|
|
//
|
|
i++; // '[' character
|
|
|
|
if( i >= busLen )
|
|
return false;
|
|
|
|
for( ; i < busLen; ++i )
|
|
{
|
|
if( aBus[i] == '.' && i + 1 < busLen && aBus[i+1] == '.' )
|
|
{
|
|
tmp.ToLong( &begin );
|
|
i += 2;
|
|
break;
|
|
}
|
|
|
|
if( !isDigit( aBus[i] ) )
|
|
return false;
|
|
|
|
tmp += aBus[i];
|
|
}
|
|
|
|
// Parse end number
|
|
//
|
|
tmp = wxEmptyString;
|
|
|
|
if( i >= busLen )
|
|
return false;
|
|
|
|
for( ; i < busLen; ++i )
|
|
{
|
|
if( aBus[i] == ']' )
|
|
{
|
|
tmp.ToLong( &end );
|
|
++i;
|
|
break;
|
|
}
|
|
|
|
if( !isDigit( aBus[i] ) )
|
|
return false;
|
|
|
|
tmp += aBus[i];
|
|
}
|
|
|
|
// Parse suffix
|
|
//
|
|
for( ; i < busLen; ++i )
|
|
{
|
|
if( aBus[i] == '}' )
|
|
{
|
|
braceNesting--;
|
|
suffix += aBus[i];
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if( braceNesting != 0 )
|
|
return false;
|
|
|
|
if( begin == end )
|
|
return false;
|
|
else if( begin > end )
|
|
std::swap( begin, end );
|
|
|
|
if( aName )
|
|
*aName = prefix;
|
|
|
|
if( aMemberList )
|
|
{
|
|
for( long idx = begin; idx <= end; ++idx )
|
|
{
|
|
wxString str = prefix;
|
|
str << idx;
|
|
str << suffix;
|
|
|
|
aMemberList->emplace_back( str );
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool NET_SETTINGS::ParseBusGroup( const wxString& aGroup, wxString* aName,
|
|
std::vector<wxString>* aMemberList )
|
|
{
|
|
size_t groupLen = aGroup.length();
|
|
size_t i = 0;
|
|
wxString prefix;
|
|
wxString tmp;
|
|
int braceNesting = 0;
|
|
|
|
prefix.reserve( groupLen );
|
|
|
|
// Parse prefix
|
|
//
|
|
for( ; i < groupLen; ++i )
|
|
{
|
|
if( aGroup[i] == '{' )
|
|
{
|
|
if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
|
|
braceNesting++;
|
|
else
|
|
break;
|
|
}
|
|
else if( aGroup[i] == '}' )
|
|
{
|
|
braceNesting--;
|
|
}
|
|
|
|
if( aGroup[i] == ' ' || aGroup[i] == '[' || aGroup[i] == ']' )
|
|
return false;
|
|
|
|
prefix += aGroup[i];
|
|
}
|
|
|
|
if( braceNesting != 0 )
|
|
return false;
|
|
|
|
if( aName )
|
|
*aName = prefix;
|
|
|
|
// Parse members
|
|
//
|
|
i++; // '{' character
|
|
|
|
if( i >= groupLen )
|
|
return false;
|
|
|
|
for( ; i < groupLen; ++i )
|
|
{
|
|
if( aGroup[i] == '{' )
|
|
{
|
|
if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
|
|
braceNesting++;
|
|
else
|
|
return false;
|
|
}
|
|
else if( aGroup[i] == '}' )
|
|
{
|
|
if( braceNesting )
|
|
{
|
|
braceNesting--;
|
|
}
|
|
else
|
|
{
|
|
if( aMemberList && !tmp.IsEmpty() )
|
|
aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Commas aren't strictly legal, but we can be pretty sure what the author had in mind.
|
|
if( aGroup[i] == ' ' || aGroup[i] == ',' )
|
|
{
|
|
if( aMemberList && !tmp.IsEmpty() )
|
|
aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
|
|
|
|
tmp.Clear();
|
|
continue;
|
|
}
|
|
|
|
tmp += aGroup[i];
|
|
}
|
|
|
|
return false;
|
|
}
|