KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <nlohmann/json.hpp>
21
23#include <settings/parameters.h>
24
26
27
29 const std::string& aPath ) :
30 NESTED_SETTINGS( "component_class_settings", componentClassSettingsSchemaVersion, aParent,
31 aPath, false ),
32 m_enableSheetComponentClasses( false )
33{
34 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>(
35 "sheet_component_classes",
36 [&]() -> nlohmann::json
37 {
38 nlohmann::json ret = {};
39
40 ret["enabled"] = m_enableSheetComponentClasses;
41
42 return ret;
43 },
44 [&]( const nlohmann::json& aJson )
45 {
46 if( !aJson.is_object() )
47 return;
48
49 if( !aJson.contains( "enabled" ) )
50 return;
51
52 m_enableSheetComponentClasses = aJson["enabled"].get<bool>();
53 },
54 {} ) );
55
56 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>(
57 "assignments",
58 [&]() -> nlohmann::json
59 {
60 nlohmann::json ret = nlohmann::json::array();
61
62 for( const COMPONENT_CLASS_ASSIGNMENT_DATA& assignment :
64 {
65 ret.push_back( saveAssignment( assignment ) );
66 }
67
68 return ret;
69 },
70 [&]( const nlohmann::json& aJson )
71 {
72 if( !aJson.is_array() )
73 return;
74
76
77 for( const auto& assignmentJson : aJson )
78 {
79 COMPONENT_CLASS_ASSIGNMENT_DATA assignment = loadAssignment( assignmentJson );
80 m_componentClassAssignments.push_back( assignment );
81 }
82 },
83 {} ) );
84}
85
86
88{
89 // Release early before destroying members
90 if( m_parent )
91 {
93 m_parent = nullptr;
94 }
95}
96
97
98nlohmann::json
100{
101 nlohmann::json ret;
102
103 const wxString matchOperator =
104 aAssignment.GetConditionsOperator()
106 ? wxT( "ALL" )
107 : wxT( "ANY" );
108
109 ret["component_class"] = aAssignment.GetComponentClass().ToUTF8();
110 ret["conditions_operator"] = matchOperator.ToUTF8();
111
112 nlohmann::json conditionsJson;
113
114 for( const auto& [conditionType, conditionData] : aAssignment.GetConditions() )
115 {
116 nlohmann::json conditionJson;
117
118 if( !conditionData.first.empty() )
119 conditionJson["primary"] = conditionData.first;
120
121 if( !conditionData.second.empty() )
122 conditionJson["secondary"] = conditionData.second;
123
124 const wxString conditionName =
126 conditionsJson[conditionName] = conditionJson;
127 }
128
129 ret["conditions"] = conditionsJson;
130
131 return ret;
132}
133
134
136COMPONENT_CLASS_SETTINGS::loadAssignment( const nlohmann::json& aJson )
137{
139
140 assignment.SetComponentClass(
141 wxString( aJson["component_class"].get<std::string>().c_str(), wxConvUTF8 ) );
142
143 const wxString matchOperator( aJson["conditions_operator"].get<std::string>().c_str(),
144 wxConvUTF8 );
145
146 if( matchOperator == wxT( "ALL" ) )
147 {
148 assignment.SetConditionsOperation(
150 }
151 else
152 {
153 assignment.SetConditionsOperation(
155 }
156
157 for( const auto& [conditionTypeStr, conditionData] : aJson["conditions"].items() )
158 {
159 wxString primary, secondary;
162
163 if( conditionData.contains( "primary" ) )
164 primary = wxString( conditionData["primary"].get<std::string>().c_str(), wxConvUTF8 );
165
166 if( conditionData.contains( "secondary" ) )
167 secondary =
168 wxString( conditionData["secondary"].get<std::string>().c_str(), wxConvUTF8 );
169
170 assignment.SetCondition( conditionType, primary, secondary );
171 }
172
173 return assignment;
174}
175
176
178{
179 // TODO: Implement this
180 throw;
181 //return true;
182}
183
184
185/**************************************************************************************************
186 *
187 * COMPONENT_CLASS_ASSIGNMENT_DATA implementation
188 *
189 *************************************************************************************************/
190
192{
193 switch( aCondition )
194 {
195 case CONDITION_TYPE::REFERENCE: return wxT( "REFERENCE" );
196 case CONDITION_TYPE::FOOTPRINT: return wxT( "FOOTPRINT" );
197 case CONDITION_TYPE::SIDE: return wxT( "SIDE" );
198 case CONDITION_TYPE::ROTATION: return wxT( "ROTATION" );
199 case CONDITION_TYPE::FOOTPRINT_FIELD: return wxT( "FOOTPRINT_FIELD" );
200 case CONDITION_TYPE::CUSTOM: return wxT( "CUSTOM" );
201 case CONDITION_TYPE::SHEET_NAME: return wxT( "SHEET_NAME" );
202 }
203
204 wxASSERT_MSG( false, "Invalid condition type" );
205
206 return wxEmptyString;
207}
208
209
212{
213 if( aCondition == wxT( "REFERENCE" ) )
215 if( aCondition == wxT( "FOOTPRINT" ) )
217 if( aCondition == wxT( "SIDE" ) )
219 if( aCondition == wxT( "ROTATION" ) )
221 if( aCondition == wxT( "FOOTPRINT_FIELD" ) )
223 if( aCondition == wxT( "CUSTOM" ) )
225 if( aCondition == wxT( "SHEET_NAME" ) )
227
228 wxASSERT_MSG( false, "Invalid condition type" );
229
231}
232
233
235{
236 if( m_componentClass.empty() )
237 return wxEmptyString;
238
239 if( m_conditions.empty() )
240 {
241 // A condition which always applies the netclass
242 return wxString::Format( wxT( "(version 1) (assign_component_class \"%s\")" ),
244 }
245
246 // Lambda to format a comma-separated list of references in to a DRC expression
247 auto getRefExpr = []( wxString aRefs ) -> wxString
248 {
249 aRefs.Trim( true ).Trim( false );
250
251 wxArrayString refs = wxSplit( aRefs, ',' );
252
253 if( refs.empty() )
254 return wxEmptyString;
255
256 std::ranges::transform( refs, refs.begin(),
257 []( const wxString& aRef )
258 {
259 return wxString::Format( wxT( "A.Reference == '%s'" ), aRef );
260 } );
261
262 wxString refsExpr = refs[0];
263
264 if( refs.size() > 1 )
265 {
266 for( auto itr = refs.begin() + 1; itr != refs.end(); ++itr )
267 refsExpr = refsExpr + wxT( " || " ) + *itr;
268 }
269
270 return wxString::Format( wxT( "( %s )" ), refsExpr );
271 };
272
273 // Lambda to format a footprint match DRC expression
274 auto getFootprintExpr = []( wxString aFootprint ) -> wxString
275 {
276 aFootprint.Trim( true ).Trim( false );
277
278 if( aFootprint.empty() )
279 return wxEmptyString;
280
281 return wxString::Format( wxT( "( A.Library_Link == '%s' )" ), aFootprint );
282 };
283
284 // Lambda to format a layer side DRC expression
285 auto getSideExpr = []( const wxString& aSide ) -> wxString
286 {
287 if( aSide == wxT( "Any" ) )
288 return wxEmptyString;
289
290 return wxString::Format( wxT( "( A.Layer == '%s' )" ),
291 aSide == wxT( "Front" ) ? wxT( "F.Cu" ) : wxT( "B.Cu" ) );
292 };
293
294 // Lambda to format a rotation DRC expression
295 auto getRotationExpr = []( wxString aRotation ) -> wxString
296 {
297 aRotation.Trim( true ).Trim( false );
298
299 int dummy;
300
301 if( aRotation.empty() || aRotation == wxT( "Any" ) || !aRotation.ToInt( &dummy ) )
302 return wxEmptyString;
303
304 return wxString::Format( wxT( "( A.Orientation == %s deg )" ), aRotation );
305 };
306
307
308 // Lambda to format a footprint field DRC expression
309 auto getFootprintFieldExpr = []( wxString aFieldName, wxString aFieldMatch ) -> wxString
310 {
311 aFieldName.Trim( true ).Trim( false );
312 aFieldMatch.Trim( true ).Trim( false );
313
314 if( aFieldName.empty() || aFieldMatch.empty() )
315 return wxEmptyString;
316
317 return wxString::Format( wxT( "( A.getField('%s') == '%s' )" ), aFieldName, aFieldMatch );
318 };
319
320 // Lambda to format a custom DRC expression
321 auto getCustomFieldExpr = []( wxString aExpr ) -> wxString
322 {
323 aExpr.Trim( true ).Trim( false );
324
325 if( aExpr.empty() )
326 return wxEmptyString;
327
328 return wxString::Format( wxT( "( %s )" ), aExpr );
329 };
330
331 // Lambda to format a sheet name expression
332 auto getSheetNameExpr = []( wxString aSheetName ) -> wxString
333 {
334 aSheetName.Trim( true ).Trim( false );
335
336 if( aSheetName.empty() )
337 return wxEmptyString;
338
339 return wxString::Format( wxT( "( A.memberOfSheet('%s') )" ), aSheetName );
340 };
341
342 std::vector<wxString> conditionsExprs;
343
344 for( auto& [conditionType, conditionData] : m_conditions )
345 {
346 wxString conditionExpr;
347
348 switch( conditionType )
349 {
350 case CONDITION_TYPE::REFERENCE: conditionExpr = getRefExpr( conditionData.first ); break;
352 conditionExpr = getFootprintExpr( conditionData.first );
353 break;
354 case CONDITION_TYPE::SIDE: conditionExpr = getSideExpr( conditionData.first ); break;
356 conditionExpr = getRotationExpr( conditionData.first );
357 break;
359 conditionExpr = getFootprintFieldExpr( conditionData.first, conditionData.second );
360 break;
362 conditionExpr = getCustomFieldExpr( conditionData.first );
363 break;
365 conditionExpr = getSheetNameExpr( conditionData.first );
366 break;
367 }
368
369 if( !conditionExpr.empty() )
370 conditionsExprs.push_back( conditionExpr );
371 }
372
373 if( conditionsExprs.empty() )
374 return wxString::Format( wxT( "(version 1) (assign_component_class \"%s\")" ),
376
377 wxString allConditionsExpr = conditionsExprs[0];
378
379 if( conditionsExprs.size() > 1 )
380 {
381 wxString operatorExpr =
382 m_conditionsOperator == CONDITIONS_OPERATOR::ALL ? wxT( " && " ) : wxT( " || " );
383
384 for( auto itr = conditionsExprs.begin() + 1; itr != conditionsExprs.end(); ++itr )
385 {
386 allConditionsExpr = allConditionsExpr + operatorExpr + *itr;
387 }
388 }
389
390 return wxString::Format(
391 wxT( "(version 1) (assign_component_class \"%s\" (condition \"%s\" ) )" ),
392 m_componentClass, allConditionsExpr );
393}
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.
const std::unordered_map< CONDITION_TYPE, std::pair< wxString, wxString > > & GetConditions() const
Gets all conditions.
std::unordered_map< CONDITION_TYPE, std::pair< wxString, wxString > > m_conditions
Map of condition types to primary and secondary data fields for the condition.
void SetCondition(const CONDITION_TYPE aCondition, const wxString &aPrimaryData, const wxString &aSecondaryData)
Sets the given condition type with the assocated match data.
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 stores data for component classes, including rules for automatic generation ...
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)
void ReleaseNestedSettings(NESTED_SETTINGS *aSettings)
Saves and frees a nested settings object, if it exists within this one.
NESTED_SETTINGS is a JSON_SETTINGS that lives inside a JSON_SETTINGS.
JSON_SETTINGS * m_parent
A pointer to the parent object to load and store from.
Like a normal param, but with custom getter and setter functions.
Definition: parameters.h:295
constexpr int componentClassSettingsSchemaVersion
std::vector< FAB_LAYER_COLOR > dummy