KiCad PCB EDA Suite
net_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 (C) 2020 CERN
5 * Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Jon Evans <[email protected]>
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include <nlohmann/json.hpp>
23
25#include <settings/parameters.h>
28#include <string_utils.h>
29#include <base_units.h>
30
31
32// const int netSettingsSchemaVersion = 0;
33// const int netSettingsSchemaVersion = 1; // new overbar syntax
34// const int netSettingsSchemaVersion = 2; // exclude buses from netclass members
35const int netSettingsSchemaVersion = 3; // netclass assignment patterns
36
37
38static std::optional<int> getInPcbUnits( const nlohmann::json& aObj, const std::string& aKey,
39 std::optional<int> aDefault = std::optional<int>() )
40{
41 if( aObj.contains( aKey ) && aObj[aKey].is_number() )
42 return pcbIUScale.mmToIU( aObj[aKey].get<double>() );
43 else
44 return aDefault;
45};
46
47
48static int getInSchUnits( const nlohmann::json& aObj, const std::string& aKey, int aDefault )
49{
50 if( aObj.contains( aKey ) && aObj[aKey].is_number() )
51 return schIUScale.MilsToIU( aObj[aKey].get<double>() );
52 else
53 return aDefault;
54};
55
56
57NET_SETTINGS::NET_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ) :
58 NESTED_SETTINGS( "net_settings", netSettingsSchemaVersion, aParent, aPath )
59{
60 m_DefaultNetClass = std::make_shared<NETCLASS>( NETCLASS::Default );
61 m_DefaultNetClass->SetDescription( _( "This is the default net class." ) );
62
63 auto saveNetclass =
64 []( nlohmann::json& json_array, const std::shared_ptr<NETCLASS>& nc )
65 {
66 // Note: we're in common/, but we do happen to know which of these
67 // fields are used in which units system.
68 nlohmann::json nc_json = {
69 { "name", nc->GetName().ToUTF8() },
70 { "wire_width", schIUScale.IUToMils( nc->GetWireWidth() ) },
71 { "bus_width", schIUScale.IUToMils( nc->GetBusWidth() ) },
72 { "line_style", nc->GetLineStyle() },
73 { "schematic_color", nc->GetSchematicColor() },
74 { "pcb_color", nc->GetPcbColor() }
75 };
76
77 auto saveInPcbUnits =
78 []( nlohmann::json& json, const std::string& aKey, int aValue )
79 {
80 json.push_back( { aKey, pcbIUScale.IUTomm( aValue ) } );
81 };
82
83 if( nc->HasClearance() )
84 saveInPcbUnits( nc_json, "clearance", nc->GetClearance() );
85
86 if( nc->HasTrackWidth() )
87 saveInPcbUnits( nc_json, "track_width", nc->GetTrackWidth() );
88
89 if( nc->HasViaDiameter() )
90 saveInPcbUnits( nc_json, "via_diameter", nc->GetViaDiameter() );
91
92 if( nc->HasViaDrill() )
93 saveInPcbUnits( nc_json, "via_drill", nc->GetViaDrill() );
94
95 if( nc->HasuViaDiameter() )
96 saveInPcbUnits( nc_json, "microvia_diameter", nc->GetuViaDiameter() );
97
98 if( nc->HasuViaDrill() )
99 saveInPcbUnits( nc_json, "microvia_drill", nc->GetuViaDrill() );
100
101 if( nc->HasDiffPairWidth() )
102 saveInPcbUnits( nc_json, "diff_pair_width", nc->GetDiffPairWidth() );
103
104 if( nc->HasDiffPairGap() )
105 saveInPcbUnits( nc_json, "diff_pair_gap", nc->GetDiffPairGap() );
106
107 if( nc->HasDiffPairViaGap() )
108 saveInPcbUnits( nc_json, "diff_pair_via_gap", nc->GetDiffPairViaGap() );
109
110 json_array.push_back( nc_json );
111 };
112
113 auto readNetClass =
114 []( const nlohmann::json& entry )
115 {
116 wxString name = entry["name"];
117
118 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( name );
119
120 if( auto value = getInPcbUnits( entry, "clearance" ) )
121 nc->SetClearance( *value );
122
123 if( auto value = getInPcbUnits( entry, "track_width" ) )
124 nc->SetTrackWidth( *value );
125
126 if( auto value = getInPcbUnits( entry, "via_diameter" ) )
127 nc->SetViaDiameter( *value );
128
129 if( auto value = getInPcbUnits( entry, "via_drill" ) )
130 nc->SetViaDrill( *value );
131
132 if( auto value = getInPcbUnits( entry, "microvia_diameter" ) )
133 nc->SetuViaDiameter( *value );
134
135 if( auto value = getInPcbUnits( entry, "microvia_drill" ) )
136 nc->SetuViaDrill( *value );
137
138 if( auto value = getInPcbUnits( entry, "diff_pair_width" ) )
139 nc->SetDiffPairWidth( *value );
140
141 if( auto value = getInPcbUnits( entry, "diff_pair_gap" ) )
142 nc->SetDiffPairGap( *value );
143
144 if( auto value = getInPcbUnits( entry, "diff_pair_via_gap" ) )
145 nc->SetDiffPairViaGap( *value );
146
147 nc->SetWireWidth( getInSchUnits( entry, "wire_width", nc->GetWireWidth() ) );
148 nc->SetBusWidth( getInSchUnits( entry, "bus_width", nc->GetBusWidth() ) );
149
150 if( entry.contains( "line_style" ) && entry["line_style"].is_number() )
151 nc->SetLineStyle( entry["line_style"].get<int>() );
152
153 if( entry.contains( "pcb_color" ) && entry["pcb_color"].is_string() )
154 nc->SetPcbColor( entry["pcb_color"].get<KIGFX::COLOR4D>() );
155
156 if( entry.contains( "schematic_color" )
157 && entry["schematic_color"].is_string() )
158 {
159 nc->SetSchematicColor( entry["schematic_color"].get<KIGFX::COLOR4D>() );
160 }
161
162 return nc;
163 };
164
165 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "classes",
166 [&]() -> nlohmann::json
167 {
168 nlohmann::json ret = nlohmann::json::array();
169
171 saveNetclass( ret, m_DefaultNetClass );
172
173 for( const auto& [ name, netclass ] : m_NetClasses )
174 saveNetclass( ret, netclass );
175
176 return ret;
177 },
178 [&]( const nlohmann::json& aJson )
179 {
180 if( !aJson.is_array() )
181 return;
182
183 m_NetClasses.clear();
184
185 for( const nlohmann::json& entry : aJson )
186 {
187 if( !entry.is_object() || !entry.contains( "name" ) )
188 continue;
189
190 std::shared_ptr<NETCLASS> nc = readNetClass( entry );
191
192 if( nc->GetName() == NETCLASS::Default )
194 else
195 m_NetClasses[ nc->GetName() ] = nc;
196 }
197 },
198 {} ) );
199
200 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "net_colors",
201 [&]() -> nlohmann::json
202 {
203 nlohmann::json ret = {};
204
205 for( const auto& [ netname, color ] : m_NetColorAssignments )
206 {
207 std::string key( netname.ToUTF8() );
208 ret[key] = color;
209 }
210
211 return ret;
212 },
213 [&]( const nlohmann::json& aJson )
214 {
215 if( !aJson.is_object() )
216 return;
217
218 m_NetColorAssignments.clear();
219
220 for( const auto& pair : aJson.items() )
221 {
222 wxString key( pair.key().c_str(), wxConvUTF8 );
223 m_NetColorAssignments[key] = pair.value().get<KIGFX::COLOR4D>();
224 }
225 },
226 {} ) );
227
228 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "netclass_assignments",
229 [&]() -> nlohmann::json
230 {
231 nlohmann::json ret = {};
232
233 for( const auto& [ netname, netclassName ] : m_NetClassLabelAssignments )
234 {
235 std::string key( netname.ToUTF8() );
236 ret[key] = netclassName;
237 }
238
239 return ret;
240 },
241 [&]( const nlohmann::json& aJson )
242 {
243 if( !aJson.is_object() )
244 return;
245
247
248 for( const auto& pair : aJson.items() )
249 {
250 wxString key( pair.key().c_str(), wxConvUTF8 );
251 m_NetClassLabelAssignments[key] = pair.value().get<wxString>();
252 }
253 },
254 {} ) );
255
256 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "netclass_patterns",
257 [&]() -> nlohmann::json
258 {
259 nlohmann::json ret = nlohmann::json::array();
260
261 for( const auto& [ matcher, netclassName ] : m_NetClassPatternAssignments )
262 {
263 nlohmann::json pattern_json = {
264 { "pattern", matcher->GetPattern().ToUTF8() },
265 { "netclass", netclassName.ToUTF8() }
266 };
267
268 ret.push_back( pattern_json );
269 }
270
271 return ret;
272 },
273 [&]( const nlohmann::json& aJson )
274 {
275 if( !aJson.is_array() )
276 return;
277
279
280 for( const nlohmann::json& entry : aJson )
281 {
282 if( !entry.is_object() )
283 continue;
284
285 if( entry.contains( "pattern" ) && entry["pattern"].is_string()
286 && entry.contains( "netclass" ) && entry["netclass"].is_string() )
287 {
288 wxString pattern = entry["pattern"].get<wxString>();
289 wxString netclass = entry["netclass"].get<wxString>();
290
292 {
293 std::make_unique<EDA_COMBINED_MATCHER>( pattern, CTX_NETCLASS ),
294 netclass
295 } );
296 }
297 }
298 },
299 {} ) );
300
301 registerMigration( 0, 1, std::bind( &NET_SETTINGS::migrateSchema0to1, this ) );
302 registerMigration( 2, 3, std::bind( &NET_SETTINGS::migrateSchema2to3, this ) );
303}
304
305
307{
308 // Release early before destroying members
309 if( m_parent )
310 {
312 m_parent = nullptr;
313 }
314}
315
316
318{
319 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
320 {
321 for( auto& netClass : m_internals->At( "classes" ).items() )
322 {
323 if( netClass.value().contains( "nets" ) && netClass.value()["nets"].is_array() )
324 {
325 nlohmann::json migrated = nlohmann::json::array();
326
327 for( auto& net : netClass.value()["nets"].items() )
328 migrated.push_back( ConvertToNewOverbarNotation( net.value().get<wxString>() ) );
329
330 netClass.value()["nets"] = migrated;
331 }
332 }
333 }
334
335 return true;
336}
337
338
340{
341 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
342 {
343 nlohmann::json patterns = nlohmann::json::array();
344
345 for( auto& netClass : m_internals->At( "classes" ).items() )
346 {
347 if( netClass.value().contains( "name" )
348 && netClass.value().contains( "nets" )
349 && netClass.value()["nets"].is_array() )
350 {
351 wxString netClassName = netClass.value()["name"].get<wxString>();
352
353 for( auto& net : netClass.value()["nets"].items() )
354 {
355 nlohmann::json pattern_json = {
356 { "pattern", net.value().get<wxString>() },
357 { "netclass", netClassName }
358 };
359
360 patterns.push_back( pattern_json );
361 }
362 }
363 }
364
365 m_internals->SetFromString( "netclass_patterns", patterns );
366 }
367
368 return true;
369}
370
371
372std::shared_ptr<NETCLASS> NET_SETTINGS::GetEffectiveNetClass( const wxString& aNetName ) const
373{
374 auto getNetclass =
375 [&]( const wxString& netclass )
376 {
377 auto ii = m_NetClasses.find( netclass );
378
379 if( ii == m_NetClasses.end() )
380 return m_DefaultNetClass;
381 else
382 return ii->second;
383 };
384
385 // <no net> is forced to be part of the Default netclass.
386 if( aNetName.IsEmpty() )
387 return m_DefaultNetClass;
388
389 auto it = m_NetClassLabelAssignments.find( aNetName );
390
391 if( it != m_NetClassLabelAssignments.end() )
392 return getNetclass( it->second );
393
394 for( const auto& [ matcher, netclassName ] : m_NetClassPatternAssignments )
395 {
396 int matches;
397 int offset;
398
399 if( matcher->Find( aNetName, matches, offset ) && offset == 0 )
400 return getNetclass( netclassName );
401 }
402
403 return m_DefaultNetClass;
404}
405
406
407static bool isSuperSubOverbar( wxChar c )
408{
409 return c == '_' || c == '^' || c == '~';
410}
411
412
413bool NET_SETTINGS::ParseBusVector( const wxString& aBus, wxString* aName,
414 std::vector<wxString>* aMemberList )
415{
416 auto isDigit = []( wxChar c )
417 {
418 static wxString digits( wxT( "0123456789" ) );
419 return digits.Contains( c );
420 };
421
422 size_t busLen = aBus.length();
423 size_t i = 0;
424 wxString prefix;
425 wxString suffix;
426 wxString tmp;
427 long begin = 0;
428 long end = 0;
429 int braceNesting = 0;
430
431 prefix.reserve( busLen );
432
433 // Parse prefix
434 //
435 for( ; i < busLen; ++i )
436 {
437 if( aBus[i] == '{' )
438 {
439 if( i > 0 && isSuperSubOverbar( aBus[i-1] ) )
440 braceNesting++;
441 else
442 return false;
443 }
444 else if( aBus[i] == '}' )
445 {
446 braceNesting--;
447 }
448
449 if( aBus[i] == ' ' || aBus[i] == ']' )
450 return false;
451
452 if( aBus[i] == '[' )
453 break;
454
455 prefix += aBus[i];
456 }
457
458 // Parse start number
459 //
460 i++; // '[' character
461
462 if( i >= busLen )
463 return false;
464
465 for( ; i < busLen; ++i )
466 {
467 if( aBus[i] == '.' && i + 1 < busLen && aBus[i+1] == '.' )
468 {
469 tmp.ToLong( &begin );
470 i += 2;
471 break;
472 }
473
474 if( !isDigit( aBus[i] ) )
475 return false;
476
477 tmp += aBus[i];
478 }
479
480 // Parse end number
481 //
482 tmp = wxEmptyString;
483
484 if( i >= busLen )
485 return false;
486
487 for( ; i < busLen; ++i )
488 {
489 if( aBus[i] == ']' )
490 {
491 tmp.ToLong( &end );
492 ++i;
493 break;
494 }
495
496 if( !isDigit( aBus[i] ) )
497 return false;
498
499 tmp += aBus[i];
500 }
501
502 // Parse suffix
503 //
504 for( ; i < busLen; ++i )
505 {
506 if( aBus[i] == '}' )
507 {
508 braceNesting--;
509 suffix += aBus[i];
510 }
511 else
512 {
513 return false;
514 }
515 }
516
517 if( braceNesting != 0 )
518 return false;
519
520 if( begin == end )
521 return false;
522 else if( begin > end )
523 std::swap( begin, end );
524
525 if( aName )
526 *aName = prefix;
527
528 if( aMemberList )
529 {
530 for( long idx = begin; idx <= end; ++idx )
531 {
532 wxString str = prefix;
533 str << idx;
534 str << suffix;
535
536 aMemberList->emplace_back( str );
537 }
538 }
539
540 return true;
541}
542
543
544bool NET_SETTINGS::ParseBusGroup( const wxString& aGroup, wxString* aName,
545 std::vector<wxString>* aMemberList )
546{
547 size_t groupLen = aGroup.length();
548 size_t i = 0;
549 wxString prefix;
550 wxString tmp;
551 int braceNesting = 0;
552
553 prefix.reserve( groupLen );
554
555 // Parse prefix
556 //
557 for( ; i < groupLen; ++i )
558 {
559 if( aGroup[i] == '{' )
560 {
561 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
562 braceNesting++;
563 else
564 break;
565 }
566 else if( aGroup[i] == '}' )
567 {
568 braceNesting--;
569 }
570
571 if( aGroup[i] == ' ' || aGroup[i] == '[' || aGroup[i] == ']' )
572 return false;
573
574 prefix += aGroup[i];
575 }
576
577 if( braceNesting != 0 )
578 return false;
579
580 if( aName )
581 *aName = prefix;
582
583 // Parse members
584 //
585 i++; // '{' character
586
587 if( i >= groupLen )
588 return false;
589
590 for( ; i < groupLen; ++i )
591 {
592 if( aGroup[i] == '{' )
593 {
594 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
595 braceNesting++;
596 else
597 return false;
598 }
599 else if( aGroup[i] == '}' )
600 {
601 if( braceNesting )
602 {
603 braceNesting--;
604 }
605 else
606 {
607 if( aMemberList && !tmp.IsEmpty() )
608 aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
609
610 return true;
611 }
612 }
613
614 // Commas aren't strictly legal, but we can be pretty sure what the author had in mind.
615 if( aGroup[i] == ' ' || aGroup[i] == ',' )
616 {
617 if( aMemberList && !tmp.IsEmpty() )
618 aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
619
620 tmp.Clear();
621 continue;
622 }
623
624 tmp += aGroup[i];
625 }
626
627 return false;
628}
int color
Definition: DXF_plotter.cpp:57
const char * name
Definition: DXF_plotter.cpp:56
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:111
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
std::vector< PARAM_BASE * > m_params
The list of parameters (owned by this object)
void registerMigration(int aOldSchemaVersion, int aNewSchemaVersion, std::function< bool(void)> aMigrator)
Registers a migration from one schema version to another.
void ReleaseNestedSettings(NESTED_SETTINGS *aSettings)
Saves and frees a nested settings object, if it exists within this one.
std::unique_ptr< JSON_SETTINGS_INTERNALS > m_internals
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:102
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.
static const char Default[]
the name of the default NETCLASS
Definition: netclass.h:49
std::shared_ptr< NETCLASS > m_DefaultNetClass
Definition: net_settings.h:42
std::map< wxString, KIGFX::COLOR4D > m_NetColorAssignments
A map of fully-qualified net names to colors used in the board context.
Definition: net_settings.h:55
std::map< wxString, wxString > m_NetClassLabelAssignments
Definition: net_settings.h:47
virtual ~NET_SETTINGS()
static bool ParseBusGroup(const wxString &aGroup, wxString *name, std::vector< wxString > *aMemberList)
Parse a bus group label into the name and a list of components.
bool migrateSchema0to1()
bool migrateSchema2to3()
std::shared_ptr< NETCLASS > GetEffectiveNetClass(const wxString &aNetName) const
std::vector< std::pair< std::unique_ptr< EDA_COMBINED_MATCHER >, wxString > > m_NetClassPatternAssignments
Definition: net_settings.h:45
static bool ParseBusVector(const wxString &aBus, wxString *aName, std::vector< wxString > *aMemberList)
Parse a bus vector (e.g.
std::map< wxString, std::shared_ptr< NETCLASS > > m_NetClasses
Definition: net_settings.h:41
NET_SETTINGS(JSON_SETTINGS *aParent, const std::string &aPath)
bool isDigit(char cc)
Definition: dsnlexer.cpp:447
#define _(s)
@ CTX_NETCLASS
nlohmann::json json
Definition: gerbview.cpp:44
static bool isSuperSubOverbar(wxChar c)
const int netSettingsSchemaVersion
static int getInSchUnits(const nlohmann::json &aObj, const std::string &aKey, int aDefault)
static std::optional< int > getInPcbUnits(const nlohmann::json &aObj, const std::string &aKey, std::optional< int > aDefault=std::optional< int >())
wxString ConvertToNewOverbarNotation(const wxString &aOldStr)
Convert the old ~...~ overbar notation to the new ~{...} one.
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_NETNAME
Definition: string_utils.h:54
constexpr double IUTomm(int iu) const
Definition: base_units.h:87
constexpr int IUToMils(int iu) const
Definition: base_units.h:100
constexpr int MilsToIU(int mils) const
Definition: base_units.h:94
constexpr int mmToIU(double mm) const
Definition: base_units.h:89