mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-02 14:06:55 +00:00
394 lines
13 KiB
C++
394 lines
13 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright The 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 <nlohmann/json.hpp>
|
|
|
|
#include <project/component_class_settings.h>
|
|
#include <settings/parameters.h>
|
|
|
|
constexpr int componentClassSettingsSchemaVersion = 0;
|
|
|
|
|
|
COMPONENT_CLASS_SETTINGS::COMPONENT_CLASS_SETTINGS( JSON_SETTINGS* aParent,
|
|
const std::string& aPath ) :
|
|
NESTED_SETTINGS( "component_class_settings", componentClassSettingsSchemaVersion, aParent,
|
|
aPath, false ),
|
|
m_enableSheetComponentClasses( false )
|
|
{
|
|
m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>(
|
|
"sheet_component_classes",
|
|
[&]() -> nlohmann::json
|
|
{
|
|
nlohmann::json ret = {};
|
|
|
|
ret["enabled"] = m_enableSheetComponentClasses;
|
|
|
|
return ret;
|
|
},
|
|
[&]( const nlohmann::json& aJson )
|
|
{
|
|
if( !aJson.is_object() )
|
|
return;
|
|
|
|
if( !aJson.contains( "enabled" ) )
|
|
return;
|
|
|
|
m_enableSheetComponentClasses = aJson["enabled"].get<bool>();
|
|
},
|
|
{} ) );
|
|
|
|
m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>(
|
|
"assignments",
|
|
[&]() -> nlohmann::json
|
|
{
|
|
nlohmann::json ret = nlohmann::json::array();
|
|
|
|
for( const COMPONENT_CLASS_ASSIGNMENT_DATA& assignment :
|
|
m_componentClassAssignments )
|
|
{
|
|
ret.push_back( saveAssignment( assignment ) );
|
|
}
|
|
|
|
return ret;
|
|
},
|
|
[&]( const nlohmann::json& aJson )
|
|
{
|
|
if( !aJson.is_array() )
|
|
return;
|
|
|
|
ClearComponentClassAssignments();
|
|
|
|
for( const auto& assignmentJson : aJson )
|
|
{
|
|
COMPONENT_CLASS_ASSIGNMENT_DATA assignment = loadAssignment( assignmentJson );
|
|
m_componentClassAssignments.push_back( assignment );
|
|
}
|
|
},
|
|
{} ) );
|
|
}
|
|
|
|
|
|
COMPONENT_CLASS_SETTINGS::~COMPONENT_CLASS_SETTINGS()
|
|
{
|
|
// Release early before destroying members
|
|
if( m_parent )
|
|
{
|
|
m_parent->ReleaseNestedSettings( this );
|
|
m_parent = nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
nlohmann::json
|
|
COMPONENT_CLASS_SETTINGS::saveAssignment( const COMPONENT_CLASS_ASSIGNMENT_DATA& aAssignment )
|
|
{
|
|
nlohmann::json ret;
|
|
|
|
const wxString matchOperator =
|
|
aAssignment.GetConditionsOperator()
|
|
== COMPONENT_CLASS_ASSIGNMENT_DATA::CONDITIONS_OPERATOR::ALL
|
|
? wxT( "ALL" )
|
|
: wxT( "ANY" );
|
|
|
|
ret["component_class"] = aAssignment.GetComponentClass().ToUTF8();
|
|
ret["conditions_operator"] = matchOperator.ToUTF8();
|
|
|
|
nlohmann::json conditionsJson;
|
|
|
|
for( const auto& [conditionType, conditionData] : aAssignment.GetConditions() )
|
|
{
|
|
nlohmann::json conditionJson;
|
|
|
|
if( !conditionData.first.empty() )
|
|
conditionJson["primary"] = conditionData.first;
|
|
|
|
if( !conditionData.second.empty() )
|
|
conditionJson["secondary"] = conditionData.second;
|
|
|
|
const wxString conditionName =
|
|
COMPONENT_CLASS_ASSIGNMENT_DATA::GetConditionName( conditionType );
|
|
conditionsJson[conditionName] = conditionJson;
|
|
}
|
|
|
|
ret["conditions"] = conditionsJson;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
COMPONENT_CLASS_ASSIGNMENT_DATA
|
|
COMPONENT_CLASS_SETTINGS::loadAssignment( const nlohmann::json& aJson )
|
|
{
|
|
COMPONENT_CLASS_ASSIGNMENT_DATA assignment;
|
|
|
|
assignment.SetComponentClass(
|
|
wxString( aJson["component_class"].get<std::string>().c_str(), wxConvUTF8 ) );
|
|
|
|
const wxString matchOperator( aJson["conditions_operator"].get<std::string>().c_str(),
|
|
wxConvUTF8 );
|
|
|
|
if( matchOperator == wxT( "ALL" ) )
|
|
{
|
|
assignment.SetConditionsOperation(
|
|
COMPONENT_CLASS_ASSIGNMENT_DATA::CONDITIONS_OPERATOR::ALL );
|
|
}
|
|
else
|
|
{
|
|
assignment.SetConditionsOperation(
|
|
COMPONENT_CLASS_ASSIGNMENT_DATA::CONDITIONS_OPERATOR::ANY );
|
|
}
|
|
|
|
for( const auto& [conditionTypeStr, conditionData] : aJson["conditions"].items() )
|
|
{
|
|
wxString primary, secondary;
|
|
COMPONENT_CLASS_ASSIGNMENT_DATA::CONDITION_TYPE conditionType =
|
|
COMPONENT_CLASS_ASSIGNMENT_DATA::GetConditionType( conditionTypeStr );
|
|
|
|
if( conditionData.contains( "primary" ) )
|
|
primary = wxString( conditionData["primary"].get<std::string>().c_str(), wxConvUTF8 );
|
|
|
|
if( conditionData.contains( "secondary" ) )
|
|
secondary =
|
|
wxString( conditionData["secondary"].get<std::string>().c_str(), wxConvUTF8 );
|
|
|
|
assignment.SetCondition( conditionType, primary, secondary );
|
|
}
|
|
|
|
return assignment;
|
|
}
|
|
|
|
|
|
bool COMPONENT_CLASS_SETTINGS::operator==( const COMPONENT_CLASS_SETTINGS& aOther ) const
|
|
{
|
|
// TODO: Implement this
|
|
throw;
|
|
//return true;
|
|
}
|
|
|
|
|
|
/**************************************************************************************************
|
|
*
|
|
* COMPONENT_CLASS_ASSIGNMENT_DATA implementation
|
|
*
|
|
*************************************************************************************************/
|
|
|
|
wxString COMPONENT_CLASS_ASSIGNMENT_DATA::GetConditionName( const CONDITION_TYPE aCondition )
|
|
{
|
|
switch( aCondition )
|
|
{
|
|
case CONDITION_TYPE::REFERENCE: return wxT( "REFERENCE" );
|
|
case CONDITION_TYPE::FOOTPRINT: return wxT( "FOOTPRINT" );
|
|
case CONDITION_TYPE::SIDE: return wxT( "SIDE" );
|
|
case CONDITION_TYPE::ROTATION: return wxT( "ROTATION" );
|
|
case CONDITION_TYPE::FOOTPRINT_FIELD: return wxT( "FOOTPRINT_FIELD" );
|
|
case CONDITION_TYPE::CUSTOM: return wxT( "CUSTOM" );
|
|
case CONDITION_TYPE::SHEET_NAME: return wxT( "SHEET_NAME" );
|
|
}
|
|
|
|
wxASSERT_MSG( false, "Invalid condition type" );
|
|
|
|
return wxEmptyString;
|
|
}
|
|
|
|
|
|
COMPONENT_CLASS_ASSIGNMENT_DATA::CONDITION_TYPE
|
|
COMPONENT_CLASS_ASSIGNMENT_DATA::GetConditionType( const wxString& aCondition )
|
|
{
|
|
if( aCondition == wxT( "REFERENCE" ) )
|
|
return CONDITION_TYPE::REFERENCE;
|
|
if( aCondition == wxT( "FOOTPRINT" ) )
|
|
return CONDITION_TYPE::FOOTPRINT;
|
|
if( aCondition == wxT( "SIDE" ) )
|
|
return CONDITION_TYPE::SIDE;
|
|
if( aCondition == wxT( "ROTATION" ) )
|
|
return CONDITION_TYPE::ROTATION;
|
|
if( aCondition == wxT( "FOOTPRINT_FIELD" ) )
|
|
return CONDITION_TYPE::FOOTPRINT_FIELD;
|
|
if( aCondition == wxT( "CUSTOM" ) )
|
|
return CONDITION_TYPE::CUSTOM;
|
|
if( aCondition == wxT( "SHEET_NAME" ) )
|
|
return CONDITION_TYPE::SHEET_NAME;
|
|
|
|
wxASSERT_MSG( false, "Invalid condition type" );
|
|
|
|
return CONDITION_TYPE::REFERENCE;
|
|
}
|
|
|
|
|
|
wxString COMPONENT_CLASS_ASSIGNMENT_DATA::GetAssignmentInDRCLanguage() const
|
|
{
|
|
if( m_componentClass.empty() )
|
|
return wxEmptyString;
|
|
|
|
if( m_conditions.empty() )
|
|
{
|
|
// A condition which always applies the netclass
|
|
return wxString::Format( wxT( "(version 1) (assign_component_class \"%s\")" ),
|
|
m_componentClass );
|
|
}
|
|
|
|
// Lambda to format a comma-separated list of references in to a DRC expression
|
|
auto getRefExpr = []( wxString aRefs ) -> wxString
|
|
{
|
|
aRefs.Trim( true ).Trim( false );
|
|
|
|
wxArrayString refs = wxSplit( aRefs, ',' );
|
|
|
|
if( refs.empty() )
|
|
return wxEmptyString;
|
|
|
|
std::ranges::transform( refs, refs.begin(),
|
|
[]( const wxString& aRef )
|
|
{
|
|
return wxString::Format( wxT( "A.Reference == '%s'" ), aRef );
|
|
} );
|
|
|
|
wxString refsExpr = refs[0];
|
|
|
|
if( refs.size() > 1 )
|
|
{
|
|
for( auto itr = refs.begin() + 1; itr != refs.end(); ++itr )
|
|
refsExpr = refsExpr + wxT( " || " ) + *itr;
|
|
}
|
|
|
|
return wxString::Format( wxT( "( %s )" ), refsExpr );
|
|
};
|
|
|
|
// Lambda to format a footprint match DRC expression
|
|
auto getFootprintExpr = []( wxString aFootprint ) -> wxString
|
|
{
|
|
aFootprint.Trim( true ).Trim( false );
|
|
|
|
if( aFootprint.empty() )
|
|
return wxEmptyString;
|
|
|
|
return wxString::Format( wxT( "( A.Library_Link == '%s' )" ), aFootprint );
|
|
};
|
|
|
|
// Lambda to format a layer side DRC expression
|
|
auto getSideExpr = []( const wxString& aSide ) -> wxString
|
|
{
|
|
if( aSide == wxT( "Any" ) )
|
|
return wxEmptyString;
|
|
|
|
return wxString::Format( wxT( "( A.Layer == '%s' )" ),
|
|
aSide == wxT( "Front" ) ? wxT( "F.Cu" ) : wxT( "B.Cu" ) );
|
|
};
|
|
|
|
// Lambda to format a rotation DRC expression
|
|
auto getRotationExpr = []( wxString aRotation ) -> wxString
|
|
{
|
|
aRotation.Trim( true ).Trim( false );
|
|
|
|
int dummy;
|
|
|
|
if( aRotation.empty() || aRotation == wxT( "Any" ) || !aRotation.ToInt( &dummy ) )
|
|
return wxEmptyString;
|
|
|
|
return wxString::Format( wxT( "( A.Orientation == %s deg )" ), aRotation );
|
|
};
|
|
|
|
|
|
// Lambda to format a footprint field DRC expression
|
|
auto getFootprintFieldExpr = []( wxString aFieldName, wxString aFieldMatch ) -> wxString
|
|
{
|
|
aFieldName.Trim( true ).Trim( false );
|
|
aFieldMatch.Trim( true ).Trim( false );
|
|
|
|
if( aFieldName.empty() || aFieldMatch.empty() )
|
|
return wxEmptyString;
|
|
|
|
return wxString::Format( wxT( "( A.getField('%s') == '%s' )" ), aFieldName, aFieldMatch );
|
|
};
|
|
|
|
// Lambda to format a custom DRC expression
|
|
auto getCustomFieldExpr = []( wxString aExpr ) -> wxString
|
|
{
|
|
aExpr.Trim( true ).Trim( false );
|
|
|
|
if( aExpr.empty() )
|
|
return wxEmptyString;
|
|
|
|
return wxString::Format( wxT( "( %s )" ), aExpr );
|
|
};
|
|
|
|
// Lambda to format a sheet name expression
|
|
auto getSheetNameExpr = []( wxString aSheetName ) -> wxString
|
|
{
|
|
aSheetName.Trim( true ).Trim( false );
|
|
|
|
if( aSheetName.empty() )
|
|
return wxEmptyString;
|
|
|
|
return wxString::Format( wxT( "( A.memberOfSheet('%s') )" ), aSheetName );
|
|
};
|
|
|
|
std::vector<wxString> conditionsExprs;
|
|
|
|
for( auto& [conditionType, conditionData] : m_conditions )
|
|
{
|
|
wxString conditionExpr;
|
|
|
|
switch( conditionType )
|
|
{
|
|
case CONDITION_TYPE::REFERENCE: conditionExpr = getRefExpr( conditionData.first ); break;
|
|
case CONDITION_TYPE::FOOTPRINT:
|
|
conditionExpr = getFootprintExpr( conditionData.first );
|
|
break;
|
|
case CONDITION_TYPE::SIDE: conditionExpr = getSideExpr( conditionData.first ); break;
|
|
case CONDITION_TYPE::ROTATION:
|
|
conditionExpr = getRotationExpr( conditionData.first );
|
|
break;
|
|
case CONDITION_TYPE::FOOTPRINT_FIELD:
|
|
conditionExpr = getFootprintFieldExpr( conditionData.first, conditionData.second );
|
|
break;
|
|
case CONDITION_TYPE::CUSTOM:
|
|
conditionExpr = getCustomFieldExpr( conditionData.first );
|
|
break;
|
|
case CONDITION_TYPE::SHEET_NAME:
|
|
conditionExpr = getSheetNameExpr( conditionData.first );
|
|
break;
|
|
}
|
|
|
|
if( !conditionExpr.empty() )
|
|
conditionsExprs.push_back( conditionExpr );
|
|
}
|
|
|
|
if( conditionsExprs.empty() )
|
|
return wxString::Format( wxT( "(version 1) (assign_component_class \"%s\")" ),
|
|
m_componentClass );
|
|
|
|
wxString allConditionsExpr = conditionsExprs[0];
|
|
|
|
if( conditionsExprs.size() > 1 )
|
|
{
|
|
wxString operatorExpr =
|
|
m_conditionsOperator == CONDITIONS_OPERATOR::ALL ? wxT( " && " ) : wxT( " || " );
|
|
|
|
for( auto itr = conditionsExprs.begin() + 1; itr != conditionsExprs.end(); ++itr )
|
|
{
|
|
allConditionsExpr = allConditionsExpr + operatorExpr + *itr;
|
|
}
|
|
}
|
|
|
|
return wxString::Format(
|
|
wxT( "(version 1) (assign_component_class \"%s\" (condition \"%s\" ) )" ),
|
|
m_componentClass, allConditionsExpr );
|
|
}
|