KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2023 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[ std::move( 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[ std::move( 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[ std::move( 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[ std::move( 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( std::move( 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
317bool NET_SETTINGS::operator==( const NET_SETTINGS& aOther ) const
318{
319 if( !std::equal( std::begin( m_NetClasses ), std::end( m_NetClasses ),
320 std::begin( aOther.m_NetClasses ) ) )
321 return false;
322
323 if( !std::equal( std::begin( m_NetClassPatternAssignments ),
325 std::begin( aOther.m_NetClassPatternAssignments ) ) )
326 return false;
327
328 if( !std::equal( std::begin( m_NetClassLabelAssignments ),
329 std::end( m_NetClassLabelAssignments ),
330 std::begin( aOther.m_NetClassLabelAssignments ) ) )
331 return false;
332
333
334 if( !std::equal( std::begin( m_NetColorAssignments ), std::end( m_NetColorAssignments ),
335 std::begin( aOther.m_NetColorAssignments ) ) )
336 return false;
337
338 return true;
339}
340
341
343{
344 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
345 {
346 for( auto& netClass : m_internals->At( "classes" ).items() )
347 {
348 if( netClass.value().contains( "nets" ) && netClass.value()["nets"].is_array() )
349 {
350 nlohmann::json migrated = nlohmann::json::array();
351
352 for( auto& net : netClass.value()["nets"].items() )
353 migrated.push_back( ConvertToNewOverbarNotation( net.value().get<wxString>() ) );
354
355 netClass.value()["nets"] = migrated;
356 }
357 }
358 }
359
360 return true;
361}
362
363
365{
366 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
367 {
368 nlohmann::json patterns = nlohmann::json::array();
369
370 for( auto& netClass : m_internals->At( "classes" ).items() )
371 {
372 if( netClass.value().contains( "name" )
373 && netClass.value().contains( "nets" )
374 && netClass.value()["nets"].is_array() )
375 {
376 wxString netClassName = netClass.value()["name"].get<wxString>();
377
378 for( auto& net : netClass.value()["nets"].items() )
379 {
380 nlohmann::json pattern_json = {
381 { "pattern", net.value().get<wxString>() },
382 { "netclass", netClassName }
383 };
384
385 patterns.push_back( pattern_json );
386 }
387 }
388 }
389
390 m_internals->SetFromString( "netclass_patterns", patterns );
391 }
392
393 return true;
394}
395
396
397std::shared_ptr<NETCLASS> NET_SETTINGS::GetEffectiveNetClass( const wxString& aNetName ) const
398{
399 auto getNetclass =
400 [&]( const wxString& netclass )
401 {
402 auto ii = m_NetClasses.find( netclass );
403
404 if( ii == m_NetClasses.end() )
405 return m_DefaultNetClass;
406 else
407 return ii->second;
408 };
409
410 // <no net> is forced to be part of the Default netclass.
411 if( aNetName.IsEmpty() )
412 return m_DefaultNetClass;
413
414 auto it = m_NetClassLabelAssignments.find( aNetName );
415
416 if( it != m_NetClassLabelAssignments.end() )
417 return getNetclass( it->second );
418
419 auto it2 = m_NetClassPatternAssignmentCache.find( aNetName );
420
421 if( it2 != m_NetClassPatternAssignmentCache.end() )
422 return getNetclass( it2->second );
423
424 for( const auto& [ matcher, netclassName ] : m_NetClassPatternAssignments )
425 {
426 if( matcher->StartsWith( aNetName ) )
427 {
428 m_NetClassPatternAssignmentCache[ aNetName ] = netclassName;
429 return getNetclass( netclassName );
430 }
431 }
432
433 m_NetClassPatternAssignmentCache[ aNetName ] = wxEmptyString;
434 return m_DefaultNetClass;
435}
436
437
438std::shared_ptr<NETCLASS> NET_SETTINGS::GetNetClassByName( const wxString& aNetClassName ) const
439{
440 auto ii = m_NetClasses.find( aNetClassName );
441
442 if( ii == m_NetClasses.end() )
443 return m_DefaultNetClass;
444 else
445 return ii->second;
446}
447
448
449static bool isSuperSubOverbar( wxChar c )
450{
451 return c == '_' || c == '^' || c == '~';
452}
453
454
455bool NET_SETTINGS::ParseBusVector( const wxString& aBus, wxString* aName,
456 std::vector<wxString>* aMemberList )
457{
458 auto isDigit = []( wxChar c )
459 {
460 static wxString digits( wxT( "0123456789" ) );
461 return digits.Contains( c );
462 };
463
464 size_t busLen = aBus.length();
465 size_t i = 0;
466 wxString prefix;
467 wxString suffix;
468 wxString tmp;
469 long begin = 0;
470 long end = 0;
471 int braceNesting = 0;
472
473 prefix.reserve( busLen );
474
475 // Parse prefix
476 //
477 for( ; i < busLen; ++i )
478 {
479 if( aBus[i] == '{' )
480 {
481 if( i > 0 && isSuperSubOverbar( aBus[i-1] ) )
482 braceNesting++;
483 else
484 return false;
485 }
486 else if( aBus[i] == '}' )
487 {
488 braceNesting--;
489 }
490
491 if( aBus[i] == ' ' || aBus[i] == ']' )
492 return false;
493
494 if( aBus[i] == '[' )
495 break;
496
497 prefix += aBus[i];
498 }
499
500 // Parse start number
501 //
502 i++; // '[' character
503
504 if( i >= busLen )
505 return false;
506
507 for( ; i < busLen; ++i )
508 {
509 if( aBus[i] == '.' && i + 1 < busLen && aBus[i+1] == '.' )
510 {
511 tmp.ToLong( &begin );
512 i += 2;
513 break;
514 }
515
516 if( !isDigit( aBus[i] ) )
517 return false;
518
519 tmp += aBus[i];
520 }
521
522 // Parse end number
523 //
524 tmp = wxEmptyString;
525
526 if( i >= busLen )
527 return false;
528
529 for( ; i < busLen; ++i )
530 {
531 if( aBus[i] == ']' )
532 {
533 tmp.ToLong( &end );
534 ++i;
535 break;
536 }
537
538 if( !isDigit( aBus[i] ) )
539 return false;
540
541 tmp += aBus[i];
542 }
543
544 // Parse suffix
545 //
546 for( ; i < busLen; ++i )
547 {
548 if( aBus[i] == '}' )
549 {
550 braceNesting--;
551 suffix += aBus[i];
552 }
553 else
554 {
555 return false;
556 }
557 }
558
559 if( braceNesting != 0 )
560 return false;
561
562 if( begin == end )
563 return false;
564 else if( begin > end )
565 std::swap( begin, end );
566
567 if( aName )
568 *aName = prefix;
569
570 if( aMemberList )
571 {
572 for( long idx = begin; idx <= end; ++idx )
573 {
574 wxString str = prefix;
575 str << idx;
576 str << suffix;
577
578 aMemberList->emplace_back( str );
579 }
580 }
581
582 return true;
583}
584
585
586bool NET_SETTINGS::ParseBusGroup( const wxString& aGroup, wxString* aName,
587 std::vector<wxString>* aMemberList )
588{
589 size_t groupLen = aGroup.length();
590 size_t i = 0;
591 wxString prefix;
592 wxString tmp;
593 int braceNesting = 0;
594
595 prefix.reserve( groupLen );
596
597 // Parse prefix
598 //
599 for( ; i < groupLen; ++i )
600 {
601 if( aGroup[i] == '{' )
602 {
603 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
604 braceNesting++;
605 else
606 break;
607 }
608 else if( aGroup[i] == '}' )
609 {
610 braceNesting--;
611 }
612
613 if( aGroup[i] == ' ' || aGroup[i] == '[' || aGroup[i] == ']' )
614 return false;
615
616 prefix += aGroup[i];
617 }
618
619 if( braceNesting != 0 )
620 return false;
621
622 if( aName )
623 *aName = prefix;
624
625 // Parse members
626 //
627 i++; // '{' character
628
629 if( i >= groupLen )
630 return false;
631
632 for( ; i < groupLen; ++i )
633 {
634 if( aGroup[i] == '{' )
635 {
636 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
637 braceNesting++;
638 else
639 return false;
640 }
641 else if( aGroup[i] == '}' )
642 {
643 if( braceNesting )
644 {
645 braceNesting--;
646 }
647 else
648 {
649 if( aMemberList && !tmp.IsEmpty() )
650 aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
651
652 return true;
653 }
654 }
655
656 // Commas aren't strictly legal, but we can be pretty sure what the author had in mind.
657 if( aGroup[i] == ' ' || aGroup[i] == ',' )
658 {
659 if( aMemberList && !tmp.IsEmpty() )
660 aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
661
662 tmp.Clear();
663 continue;
664 }
665
666 tmp += aGroup[i];
667 }
668
669 return false;
670}
int color
Definition: DXF_plotter.cpp:58
const char * name
Definition: DXF_plotter.cpp:57
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:104
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:46
NET_SETTINGS stores various net-related settings in a project context.
Definition: net_settings.h:34
std::shared_ptr< NETCLASS > m_DefaultNetClass
Definition: net_settings.h:46
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:60
bool operator==(const NET_SETTINGS &aOther) const
std::map< wxString, wxString > m_NetClassLabelAssignments
Definition: net_settings.h:52
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.
std::map< wxString, wxString > m_NetClassPatternAssignmentCache
Definition: net_settings.h:50
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:49
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:45
std::shared_ptr< NETCLASS > GetNetClassByName(const wxString &aNetName) const
Get a NETCLASS object from a given Netclass name string.
NET_SETTINGS(JSON_SETTINGS *aParent, const std::string &aPath)
Like a normal param, but with custom getter and setter functions.
Definition: parameters.h:291
bool isDigit(char cc)
Definition: dsnlexer.cpp:446
#define _(s)
@ CTX_NETCLASS
nlohmann::json json
Definition: gerbview.cpp:46
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:53
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