KiCad PCB EDA Suite
Loading...
Searching...
No Matches
component_class_settings.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <json_common.h>
21
23#include <settings/parameters.h>
24
26
27
29 NESTED_SETTINGS( "component_class_settings", componentClassSettingsSchemaVersion, aParent, aPath, false ),
31{
32 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>(
33 "sheet_component_classes",
34 [&]() -> nlohmann::json
35 {
36 nlohmann::json ret = {};
37
38 ret["enabled"] = m_enableSheetComponentClasses;
39
40 return ret;
41 },
42 [&]( const nlohmann::json& aJson )
43 {
44 if( !aJson.is_object() )
45 return;
46
47 if( !aJson.contains( "enabled" ) )
48 return;
49
50 m_enableSheetComponentClasses = aJson["enabled"].get<bool>();
51 },
52 {} ) );
53
54 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>(
55 "assignments",
56 [&]() -> nlohmann::json
57 {
58 nlohmann::json ret = nlohmann::json::array();
59
61 ret.push_back( saveAssignment( assignment ) );
62
63 return ret;
64 },
65 [&]( const nlohmann::json& aJson )
66 {
67 if( !aJson.is_array() )
68 return;
69
71
72 for( const nlohmann::json& assignmentJson : aJson )
73 {
74 COMPONENT_CLASS_ASSIGNMENT_DATA assignment = loadAssignment( assignmentJson );
75 m_componentClassAssignments.push_back( assignment );
76 }
77 },
78 {} ) );
79}
80
81
83{
84 // Release early before destroying members
85 if( m_parent )
86 {
87 m_parent->ReleaseNestedSettings( this );
88 m_parent = nullptr;
89 }
90}
91
92
93nlohmann::json
95{
96 nlohmann::json ret;
97
98 const wxString matchOperator =
100 ? wxT( "ALL" )
101 : wxT( "ANY" );
102
103 ret["component_class"] = aAssignment.GetComponentClass().ToUTF8();
104 ret["conditions_operator"] = matchOperator.ToUTF8();
105
106 nlohmann::json conditionsJson;
107
108 for( const auto& [conditionType, primaryData, secondaryData] : aAssignment.GetConditions() )
109 {
110 nlohmann::json conditionJson;
111
112 if( !primaryData.empty() )
113 conditionJson["primary"] = primaryData;
114
115 if( !secondaryData.empty() )
116 conditionJson["secondary"] = secondaryData;
117
118 const wxString conditionName = COMPONENT_CLASS_ASSIGNMENT_DATA::GetConditionName( conditionType );
119 int suffix = 1;
120 wxString uniqueName = conditionName;
121
122 while( conditionsJson.contains( uniqueName ) )
123 uniqueName = wxString::Format( wxT( "%s-%d" ), conditionName, suffix++ );
124
125 conditionsJson[uniqueName] = conditionJson;
126 }
127
128 ret["conditions"] = conditionsJson;
129
130 return ret;
131}
132
133
135COMPONENT_CLASS_SETTINGS::loadAssignment( const nlohmann::json& aJson )
136{
138
139 assignment.SetComponentClass( wxString( aJson["component_class"].get<std::string>().c_str(), wxConvUTF8 ) );
140
141 const wxString matchOperator( aJson["conditions_operator"].get<std::string>().c_str(), wxConvUTF8 );
142
143 if( matchOperator == wxT( "ALL" ) )
145 else
147
148 for( const auto& [conditionTypeStr, conditionData] : aJson["conditions"].items() )
149 {
150 wxString primary, secondary;
151 wxString typeStr( conditionTypeStr );
152
153 typeStr = typeStr.BeforeFirst( '-' );
154
157
158 if( conditionData.contains( "primary" ) )
159 primary = wxString( conditionData["primary"].get<std::string>().c_str(), wxConvUTF8 );
160
161 if( conditionData.contains( "secondary" ) )
162 secondary = wxString( conditionData["secondary"].get<std::string>().c_str(), wxConvUTF8 );
163
164 assignment.AddCondition( conditionType, primary, secondary );
165 }
166
167 return assignment;
168}
169
170
176
177
178/**************************************************************************************************
179 *
180 * COMPONENT_CLASS_ASSIGNMENT_DATA implementation
181 *
182 *************************************************************************************************/
183
185{
186 switch( aCondition )
187 {
188 case CONDITION_TYPE::REFERENCE: return wxT( "REFERENCE" );
189 case CONDITION_TYPE::FOOTPRINT: return wxT( "FOOTPRINT" );
190 case CONDITION_TYPE::SIDE: return wxT( "SIDE" );
191 case CONDITION_TYPE::ROTATION: return wxT( "ROTATION" );
192 case CONDITION_TYPE::FOOTPRINT_FIELD: return wxT( "FOOTPRINT_FIELD" );
193 case CONDITION_TYPE::CUSTOM: return wxT( "CUSTOM" );
194 case CONDITION_TYPE::SHEET_NAME: return wxT( "SHEET_NAME" );
195 }
196
197 wxASSERT_MSG( false, "Invalid condition type" );
198
199 return wxEmptyString;
200}
201
202
205{
206 if( aCondition == wxT( "REFERENCE" ) )
208 if( aCondition == wxT( "FOOTPRINT" ) )
210 if( aCondition == wxT( "SIDE" ) )
212 if( aCondition == wxT( "ROTATION" ) )
214 if( aCondition == wxT( "FOOTPRINT_FIELD" ) )
216 if( aCondition == wxT( "CUSTOM" ) )
218 if( aCondition == wxT( "SHEET_NAME" ) )
220
221 wxASSERT_MSG( false, "Invalid condition type" );
222
224}
225
226
228{
229 if( m_componentClass.empty() )
230 return wxEmptyString;
231
232 if( m_conditions.empty() )
233 {
234 // A condition which always applies the netclass
235 return wxString::Format( wxT( "(version 2) (assign_component_class \"%s\")" ), m_componentClass );
236 }
237
238 // Lambda to format a comma-separated list of references in to a DRC expression
239 auto getRefExpr =
240 []( wxString aRefs ) -> wxString
241 {
242 aRefs.Trim( true ).Trim( false );
243
244 wxArrayString refs = wxSplit( aRefs, ',' );
245
246 if( refs.empty() )
247 return wxEmptyString;
248
249 std::ranges::transform( refs, refs.begin(),
250 []( const wxString& aRef )
251 {
252 return wxString::Format( wxT( "A.Reference == '%s'" ), aRef );
253 } );
254
255 wxString refsExpr = refs[0];
256
257 if( refs.size() > 1 )
258 {
259 for( auto itr = refs.begin() + 1; itr != refs.end(); ++itr )
260 refsExpr = refsExpr + wxT( " || " ) + *itr;
261 }
262
263 return wxString::Format( wxT( "( %s )" ), refsExpr );
264 };
265
266 // Lambda to format a footprint match DRC expression
267 auto getFootprintExpr =
268 []( wxString aFootprint ) -> wxString
269 {
270 aFootprint.Trim( true ).Trim( false );
271
272 if( aFootprint.empty() )
273 return wxEmptyString;
274
275 return wxString::Format( wxT( "( A.Library_Link == '%s' )" ), aFootprint );
276 };
277
278 // Lambda to format a layer side DRC expression
279 auto getSideExpr =
280 []( const wxString& aSide ) -> wxString
281 {
282 if( aSide == wxT( "Any" ) )
283 return wxEmptyString;
284
285 return wxString::Format( wxT( "( A.Layer == '%s' )" ),
286 aSide == wxT( "Front" ) ? wxT( "F.Cu" ) : wxT( "B.Cu" ) );
287 };
288
289 // Lambda to format a rotation DRC expression
290 auto getRotationExpr =
291 []( wxString aRotation ) -> wxString
292 {
293 aRotation.Trim( true ).Trim( false );
294
295 int dummy;
296
297 if( aRotation.empty() || aRotation == wxT( "Any" ) || !aRotation.ToInt( &dummy ) )
298 return wxEmptyString;
299
300 return wxString::Format( wxT( "( A.Orientation == %s deg )" ), aRotation );
301 };
302
303
304 // Lambda to format a footprint field DRC expression
305 auto getFootprintFieldExpr =
306 []( wxString aFieldName, wxString aFieldMatch ) -> wxString
307 {
308 aFieldName.Trim( true ).Trim( false );
309 aFieldMatch.Trim( true ).Trim( false );
310
311 if( aFieldName.empty() || aFieldMatch.empty() )
312 return wxEmptyString;
313
314 return wxString::Format( wxT( "( A.getField('%s') == '%s' )" ), aFieldName, aFieldMatch );
315 };
316
317 // Lambda to format a custom DRC expression
318 auto getCustomFieldExpr =
319 []( wxString aExpr ) -> wxString
320 {
321 aExpr.Trim( true ).Trim( false );
322
323 if( aExpr.empty() )
324 return wxEmptyString;
325
326 return wxString::Format( wxT( "( %s )" ), aExpr );
327 };
328
329 // Lambda to format a sheet name expression
330 auto getSheetNameExpr =
331 []( wxString aSheetName ) -> wxString
332 {
333 aSheetName.Trim( true ).Trim( false );
334
335 if( aSheetName.empty() )
336 return wxEmptyString;
337
338 return wxString::Format( wxT( "( A.memberOfSheet('%s') )" ), aSheetName );
339 };
340
341 std::vector<wxString> conditionsExprs;
342
343 for( auto& [conditionType, primaryData, secondaryData] : m_conditions )
344 {
345 wxString conditionExpr;
346
347 switch( conditionType )
348 {
350 conditionExpr = getRefExpr( primaryData );
351 break;
353 conditionExpr = getFootprintExpr( primaryData );
354 break;
356 conditionExpr = getSideExpr( primaryData );
357 break;
359 conditionExpr = getRotationExpr( primaryData );
360 break;
362 conditionExpr = getFootprintFieldExpr( primaryData, secondaryData );
363 break;
365 conditionExpr = getCustomFieldExpr( primaryData );
366 break;
368 conditionExpr = getSheetNameExpr( primaryData );
369 break;
370 }
371
372 if( !conditionExpr.empty() )
373 conditionsExprs.push_back( conditionExpr );
374 }
375
376 if( conditionsExprs.empty() )
377 return wxString::Format( wxT( "(version 2) (assign_component_class \"%s\")" ), m_componentClass );
378
379 wxString allConditionsExpr = conditionsExprs[0];
380
381 if( conditionsExprs.size() > 1 )
382 {
383 wxString operatorExpr = m_conditionsOperator == CONDITIONS_OPERATOR::ALL ? wxT( " && " ) : wxT( " || " );
384
385 for( auto itr = conditionsExprs.begin() + 1; itr != conditionsExprs.end(); ++itr )
386 allConditionsExpr = allConditionsExpr + operatorExpr + *itr;
387 }
388
389 return wxString::Format( wxT( "(version 2) (assign_component_class \"%s\" (condition \"%s\" ) )" ),
391 allConditionsExpr );
392}
void AddCondition(const CONDITION_TYPE aCondition, const wxString &aPrimaryData, const wxString &aSecondaryData)
Sets the given condition type with the assocated match data.
wxString GetAssignmentInDRCLanguage() const
Returns the DRC rules language for this component class assignment.
wxString m_componentClass
The name of the component class for this assignment rule.
static CONDITION_TYPE GetConditionType(const wxString &aCondition)
Maps a descriptive string to a CONDITION_TYPE.
void SetConditionsOperation(const CONDITIONS_OPERATOR aOperator)
Sets the boolean operation in use for all conditions.
static wxString GetConditionName(const CONDITION_TYPE aCondition)
Maps a CONDITION_TYPE to a descriptive string.
std::vector< std::tuple< CONDITION_TYPE, wxString, wxString > > m_conditions
Set of conditions with type, primary and secondary data fields for the condition.
const std::vector< std::tuple< CONDITION_TYPE, wxString, wxString > > & GetConditions() const
Gets all conditions.
CONDITIONS_OPERATOR m_conditionsOperator
Whether conditions are applied with AND or OR logic Defaults to ALL.
void SetComponentClass(const wxString &aComponentClass)
Sets the resulting component class for matching footprints.
CONDITIONS_OPERATOR GetConditionsOperator() const
Gets the boolean operation in use for all conditions.
const wxString & GetComponentClass() const
Gets the resulting component class for matching footprints.
COMPONENT_CLASS_SETTINGS(JSON_SETTINGS *aParent, const std::string &aPath)
bool operator==(const COMPONENT_CLASS_SETTINGS &aOther) const
static nlohmann::json saveAssignment(const COMPONENT_CLASS_ASSIGNMENT_DATA &aAssignment)
Saves a dynamic component class assignment to JSON.
void ClearComponentClassAssignments()
Clear all dynamic component class assignments.
std::vector< COMPONENT_CLASS_ASSIGNMENT_DATA > m_componentClassAssignments
All dynamic component class assignment rules.
static COMPONENT_CLASS_ASSIGNMENT_DATA loadAssignment(const nlohmann::json &aJson)
Loads a dynamic component class assignment from JSON.
bool m_enableSheetComponentClasses
Toggle generation of component classes for hierarchical sheets.
std::vector< PARAM_BASE * > m_params
The list of parameters (owned by this object)
JSON_SETTINGS(const wxString &aFilename, SETTINGS_LOC aLocation, int aSchemaVersion)
JSON_SETTINGS * m_parent
A pointer to the parent object to load and store from.
NESTED_SETTINGS(const std::string &aName, int aSchemaVersion, JSON_SETTINGS *aParent, const std::string &aPath, bool aLoadFromFile=true)
Like a normal param, but with custom getter and setter functions.
Definition parameters.h:297
constexpr int componentClassSettingsSchemaVersion
std::vector< FAB_LAYER_COLOR > dummy