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>
27#include <string_utils.h>
28#include <base_units.h>
29
30
31// const int netSettingsSchemaVersion = 0;
32// const int netSettingsSchemaVersion = 1; // new overbar syntax
33// const int netSettingsSchemaVersion = 2; // exclude buses from netclass members
34const int netSettingsSchemaVersion = 3; // netclass assignment patterns
35
36
37static std::optional<int> getInPcbUnits( const nlohmann::json& aObj, const std::string& aKey,
38 std::optional<int> aDefault = std::optional<int>() )
39{
40 if( aObj.contains( aKey ) && aObj[aKey].is_number() )
41 return pcbIUScale.mmToIU( aObj[aKey].get<double>() );
42 else
43 return aDefault;
44};
45
46
47static int getInSchUnits( const nlohmann::json& aObj, const std::string& aKey, int aDefault )
48{
49 if( aObj.contains( aKey ) && aObj[aKey].is_number() )
50 return schIUScale.MilsToIU( aObj[aKey].get<double>() );
51 else
52 return aDefault;
53};
54
55
56NET_SETTINGS::NET_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ) :
57 NESTED_SETTINGS( "net_settings", netSettingsSchemaVersion, aParent, aPath )
58{
59 m_DefaultNetClass = std::make_shared<NETCLASS>( NETCLASS::Default );
60 m_DefaultNetClass->SetDescription( _( "This is the default net class." ) );
61
62 auto saveNetclass =
63 []( nlohmann::json& json_array, const std::shared_ptr<NETCLASS>& nc )
64 {
65 // Note: we're in common/, but we do happen to know which of these
66 // fields are used in which units system.
67 nlohmann::json nc_json = {
68 { "name", nc->GetName().ToUTF8() },
69 { "wire_width", schIUScale.IUToMils( nc->GetWireWidth() ) },
70 { "bus_width", schIUScale.IUToMils( nc->GetBusWidth() ) },
71 { "line_style", nc->GetLineStyle() },
72 { "schematic_color", nc->GetSchematicColor() },
73 { "pcb_color", nc->GetPcbColor() }
74 };
75
76 auto saveInPcbUnits =
77 []( nlohmann::json& json, const std::string& aKey, int aValue )
78 {
79 json.push_back( { aKey, pcbIUScale.IUTomm( aValue ) } );
80 };
81
82 if( nc->HasClearance() )
83 saveInPcbUnits( nc_json, "clearance", nc->GetClearance() );
84
85 if( nc->HasTrackWidth() )
86 saveInPcbUnits( nc_json, "track_width", nc->GetTrackWidth() );
87
88 if( nc->HasViaDiameter() )
89 saveInPcbUnits( nc_json, "via_diameter", nc->GetViaDiameter() );
90
91 if( nc->HasViaDrill() )
92 saveInPcbUnits( nc_json, "via_drill", nc->GetViaDrill() );
93
94 if( nc->HasuViaDiameter() )
95 saveInPcbUnits( nc_json, "microvia_diameter", nc->GetuViaDiameter() );
96
97 if( nc->HasuViaDrill() )
98 saveInPcbUnits( nc_json, "microvia_drill", nc->GetuViaDrill() );
99
100 if( nc->HasDiffPairWidth() )
101 saveInPcbUnits( nc_json, "diff_pair_width", nc->GetDiffPairWidth() );
102
103 if( nc->HasDiffPairGap() )
104 saveInPcbUnits( nc_json, "diff_pair_gap", nc->GetDiffPairGap() );
105
106 if( nc->HasDiffPairViaGap() )
107 saveInPcbUnits( nc_json, "diff_pair_via_gap", nc->GetDiffPairViaGap() );
108
109 json_array.push_back( nc_json );
110 };
111
112 auto readNetClass =
113 []( const nlohmann::json& entry )
114 {
115 wxString name = entry["name"];
116
117 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( name );
118
119 if( auto value = getInPcbUnits( entry, "clearance" ) )
120 nc->SetClearance( *value );
121
122 if( auto value = getInPcbUnits( entry, "track_width" ) )
123 nc->SetTrackWidth( *value );
124
125 if( auto value = getInPcbUnits( entry, "via_diameter" ) )
126 nc->SetViaDiameter( *value );
127
128 if( auto value = getInPcbUnits( entry, "via_drill" ) )
129 nc->SetViaDrill( *value );
130
131 if( auto value = getInPcbUnits( entry, "microvia_diameter" ) )
132 nc->SetuViaDiameter( *value );
133
134 if( auto value = getInPcbUnits( entry, "microvia_drill" ) )
135 nc->SetuViaDrill( *value );
136
137 if( auto value = getInPcbUnits( entry, "diff_pair_width" ) )
138 nc->SetDiffPairWidth( *value );
139
140 if( auto value = getInPcbUnits( entry, "diff_pair_gap" ) )
141 nc->SetDiffPairGap( *value );
142
143 if( auto value = getInPcbUnits( entry, "diff_pair_via_gap" ) )
144 nc->SetDiffPairViaGap( *value );
145
146 nc->SetWireWidth( getInSchUnits( entry, "wire_width", nc->GetWireWidth() ) );
147 nc->SetBusWidth( getInSchUnits( entry, "bus_width", nc->GetBusWidth() ) );
148
149 if( entry.contains( "line_style" ) && entry["line_style"].is_number() )
150 nc->SetLineStyle( entry["line_style"].get<int>() );
151
152 if( entry.contains( "pcb_color" ) && entry["pcb_color"].is_string() )
153 nc->SetPcbColor( entry["pcb_color"].get<KIGFX::COLOR4D>() );
154
155 if( entry.contains( "schematic_color" )
156 && entry["schematic_color"].is_string() )
157 {
158 nc->SetSchematicColor( entry["schematic_color"].get<KIGFX::COLOR4D>() );
159 }
160
161 return nc;
162 };
163
164 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "classes",
165 [&]() -> nlohmann::json
166 {
167 nlohmann::json ret = nlohmann::json::array();
168
170 saveNetclass( ret, m_DefaultNetClass );
171
172 for( const auto& [ name, netclass ] : m_NetClasses )
173 saveNetclass( ret, netclass );
174
175 return ret;
176 },
177 [&]( const nlohmann::json& aJson )
178 {
179 if( !aJson.is_array() )
180 return;
181
182 m_NetClasses.clear();
183
184 for( const nlohmann::json& entry : aJson )
185 {
186 if( !entry.is_object() || !entry.contains( "name" ) )
187 continue;
188
189 std::shared_ptr<NETCLASS> nc = readNetClass( entry );
190
191 if( nc->GetName() == NETCLASS::Default )
193 else
194 m_NetClasses[ nc->GetName() ] = nc;
195 }
196 },
197 {} ) );
198
199 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "net_colors",
200 [&]() -> nlohmann::json
201 {
202 nlohmann::json ret = {};
203
204 for( const auto& [ netname, color ] : m_NetColorAssignments )
205 {
206 std::string key( netname.ToUTF8() );
207 ret[ std::move( key ) ] = color;
208 }
209
210 return ret;
211 },
212 [&]( const nlohmann::json& aJson )
213 {
214 if( !aJson.is_object() )
215 return;
216
217 m_NetColorAssignments.clear();
218
219 for( const auto& pair : aJson.items() )
220 {
221 wxString key( pair.key().c_str(), wxConvUTF8 );
222 m_NetColorAssignments[ std::move( key ) ] = pair.value().get<KIGFX::COLOR4D>();
223 }
224 },
225 {} ) );
226
227 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "netclass_assignments",
228 [&]() -> nlohmann::json
229 {
230 nlohmann::json ret = {};
231
232 for( const auto& [ netname, netclassName ] : m_NetClassLabelAssignments )
233 {
234 std::string key( netname.ToUTF8() );
235 ret[ std::move( key ) ] = netclassName;
236 }
237
238 return ret;
239 },
240 [&]( const nlohmann::json& aJson )
241 {
242 if( !aJson.is_object() )
243 return;
244
246
247 for( const auto& pair : aJson.items() )
248 {
249 wxString key( pair.key().c_str(), wxConvUTF8 );
250 m_NetClassLabelAssignments[ std::move( key ) ] = pair.value().get<wxString>();
251 }
252 },
253 {} ) );
254
255 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "netclass_patterns",
256 [&]() -> nlohmann::json
257 {
258 nlohmann::json ret = nlohmann::json::array();
259
260 for( const auto& [ matcher, netclassName ] : m_NetClassPatternAssignments )
261 {
262 nlohmann::json pattern_json = {
263 { "pattern", matcher->GetPattern().ToUTF8() },
264 { "netclass", netclassName.ToUTF8() }
265 };
266
267 ret.push_back( std::move( pattern_json ) );
268 }
269
270 return ret;
271 },
272 [&]( const nlohmann::json& aJson )
273 {
274 if( !aJson.is_array() )
275 return;
276
278
279 for( const nlohmann::json& entry : aJson )
280 {
281 if( !entry.is_object() )
282 continue;
283
284 if( entry.contains( "pattern" ) && entry["pattern"].is_string()
285 && entry.contains( "netclass" ) && entry["netclass"].is_string() )
286 {
287 wxString pattern = entry["pattern"].get<wxString>();
288 wxString netclass = entry["netclass"].get<wxString>();
289
291 {
292 std::make_unique<EDA_COMBINED_MATCHER>( pattern, CTX_NETCLASS ),
293 netclass
294 } );
295 }
296 }
297 },
298 {} ) );
299
300 registerMigration( 0, 1, std::bind( &NET_SETTINGS::migrateSchema0to1, this ) );
301 registerMigration( 2, 3, std::bind( &NET_SETTINGS::migrateSchema2to3, this ) );
302}
303
304
306{
307 // Release early before destroying members
308 if( m_parent )
309 {
311 m_parent = nullptr;
312 }
313}
314
315
316bool NET_SETTINGS::operator==( const NET_SETTINGS& aOther ) const
317{
318 if( !std::equal( std::begin( m_NetClasses ), std::end( m_NetClasses ),
319 std::begin( aOther.m_NetClasses ) ) )
320 return false;
321
322 if( !std::equal( std::begin( m_NetClassPatternAssignments ),
324 std::begin( aOther.m_NetClassPatternAssignments ) ) )
325 return false;
326
327 if( !std::equal( std::begin( m_NetClassLabelAssignments ),
328 std::end( m_NetClassLabelAssignments ),
329 std::begin( aOther.m_NetClassLabelAssignments ) ) )
330 return false;
331
332
333 if( !std::equal( std::begin( m_NetColorAssignments ), std::end( m_NetColorAssignments ),
334 std::begin( aOther.m_NetColorAssignments ) ) )
335 return false;
336
337 return true;
338}
339
340
342{
343 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
344 {
345 for( auto& netClass : m_internals->At( "classes" ).items() )
346 {
347 if( netClass.value().contains( "nets" ) && netClass.value()["nets"].is_array() )
348 {
349 nlohmann::json migrated = nlohmann::json::array();
350
351 for( auto& net : netClass.value()["nets"].items() )
352 migrated.push_back( ConvertToNewOverbarNotation( net.value().get<wxString>() ) );
353
354 netClass.value()["nets"] = migrated;
355 }
356 }
357 }
358
359 return true;
360}
361
362
364{
365 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
366 {
367 nlohmann::json patterns = nlohmann::json::array();
368
369 for( auto& netClass : m_internals->At( "classes" ).items() )
370 {
371 if( netClass.value().contains( "name" )
372 && netClass.value().contains( "nets" )
373 && netClass.value()["nets"].is_array() )
374 {
375 wxString netClassName = netClass.value()["name"].get<wxString>();
376
377 for( auto& net : netClass.value()["nets"].items() )
378 {
379 nlohmann::json pattern_json = {
380 { "pattern", net.value().get<wxString>() },
381 { "netclass", netClassName }
382 };
383
384 patterns.push_back( pattern_json );
385 }
386 }
387 }
388
389 m_internals->SetFromString( "netclass_patterns", patterns );
390 }
391
392 return true;
393}
394
395
396std::shared_ptr<NETCLASS> NET_SETTINGS::GetEffectiveNetClass( const wxString& aNetName ) const
397{
398 auto getNetclass =
399 [&]( const wxString& netclass )
400 {
401 auto ii = m_NetClasses.find( netclass );
402
403 if( ii == m_NetClasses.end() )
404 return m_DefaultNetClass;
405 else
406 return ii->second;
407 };
408
409 // <no net> is forced to be part of the Default netclass.
410 if( aNetName.IsEmpty() )
411 return m_DefaultNetClass;
412
413 auto it = m_NetClassLabelAssignments.find( aNetName );
414
415 if( it != m_NetClassLabelAssignments.end() )
416 return getNetclass( it->second );
417
418 auto it2 = m_NetClassPatternAssignmentCache.find( aNetName );
419
420 if( it2 != m_NetClassPatternAssignmentCache.end() )
421 return getNetclass( it2->second );
422
423 for( const auto& [ matcher, netclassName ] : m_NetClassPatternAssignments )
424 {
425 if( matcher->StartsWith( aNetName ) )
426 {
427 m_NetClassPatternAssignmentCache[ aNetName ] = netclassName;
428 return getNetclass( netclassName );
429 }
430 }
431
432 m_NetClassPatternAssignmentCache[ aNetName ] = wxEmptyString;
433 return m_DefaultNetClass;
434}
435
436
437std::shared_ptr<NETCLASS> NET_SETTINGS::GetNetClassByName( const wxString& aNetClassName ) const
438{
439 auto ii = m_NetClasses.find( aNetClassName );
440
441 if( ii == m_NetClasses.end() )
442 return m_DefaultNetClass;
443 else
444 return ii->second;
445}
446
447
448static bool isSuperSubOverbar( wxChar c )
449{
450 return c == '_' || c == '^' || c == '~';
451}
452
453
454bool NET_SETTINGS::ParseBusVector( const wxString& aBus, wxString* aName,
455 std::vector<wxString>* aMemberList )
456{
457 auto isDigit = []( wxChar c )
458 {
459 static wxString digits( wxT( "0123456789" ) );
460 return digits.Contains( c );
461 };
462
463 size_t busLen = aBus.length();
464 size_t i = 0;
465 wxString prefix;
466 wxString suffix;
467 wxString tmp;
468 long begin = 0;
469 long end = 0;
470 int braceNesting = 0;
471
472 prefix.reserve( busLen );
473
474 // Parse prefix
475 //
476 for( ; i < busLen; ++i )
477 {
478 if( aBus[i] == '{' )
479 {
480 if( i > 0 && isSuperSubOverbar( aBus[i-1] ) )
481 braceNesting++;
482 else
483 return false;
484 }
485 else if( aBus[i] == '}' )
486 {
487 braceNesting--;
488 }
489
490 if( aBus[i] == ' ' || aBus[i] == ']' )
491 return false;
492
493 if( aBus[i] == '[' )
494 break;
495
496 prefix += aBus[i];
497 }
498
499 // Parse start number
500 //
501 i++; // '[' character
502
503 if( i >= busLen )
504 return false;
505
506 for( ; i < busLen; ++i )
507 {
508 if( aBus[i] == '.' && i + 1 < busLen && aBus[i+1] == '.' )
509 {
510 tmp.ToLong( &begin );
511 i += 2;
512 break;
513 }
514
515 if( !isDigit( aBus[i] ) )
516 return false;
517
518 tmp += aBus[i];
519 }
520
521 // Parse end number
522 //
523 tmp = wxEmptyString;
524
525 if( i >= busLen )
526 return false;
527
528 for( ; i < busLen; ++i )
529 {
530 if( aBus[i] == ']' )
531 {
532 tmp.ToLong( &end );
533 ++i;
534 break;
535 }
536
537 if( !isDigit( aBus[i] ) )
538 return false;
539
540 tmp += aBus[i];
541 }
542
543 // Parse suffix
544 //
545 for( ; i < busLen; ++i )
546 {
547 if( aBus[i] == '}' )
548 {
549 braceNesting--;
550 suffix += aBus[i];
551 }
552 else
553 {
554 return false;
555 }
556 }
557
558 if( braceNesting != 0 )
559 return false;
560
561 if( begin == end )
562 return false;
563 else if( begin > end )
564 std::swap( begin, end );
565
566 if( aName )
567 *aName = prefix;
568
569 if( aMemberList )
570 {
571 for( long idx = begin; idx <= end; ++idx )
572 {
573 wxString str = prefix;
574 str << idx;
575 str << suffix;
576
577 aMemberList->emplace_back( str );
578 }
579 }
580
581 return true;
582}
583
584
585bool NET_SETTINGS::ParseBusGroup( const wxString& aGroup, wxString* aName,
586 std::vector<wxString>* aMemberList )
587{
588 size_t groupLen = aGroup.length();
589 size_t i = 0;
590 wxString prefix;
591 wxString tmp;
592 int braceNesting = 0;
593
594 prefix.reserve( groupLen );
595
596 // Parse prefix
597 //
598 for( ; i < groupLen; ++i )
599 {
600 if( aGroup[i] == '{' )
601 {
602 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
603 braceNesting++;
604 else
605 break;
606 }
607 else if( aGroup[i] == '}' )
608 {
609 braceNesting--;
610 }
611
612 if( aGroup[i] == ' ' || aGroup[i] == '[' || aGroup[i] == ']' )
613 return false;
614
615 prefix += aGroup[i];
616 }
617
618 if( braceNesting != 0 )
619 return false;
620
621 if( aName )
622 *aName = prefix;
623
624 // Parse members
625 //
626 i++; // '{' character
627
628 if( i >= groupLen )
629 return false;
630
631 for( ; i < groupLen; ++i )
632 {
633 if( aGroup[i] == '{' )
634 {
635 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
636 braceNesting++;
637 else
638 return false;
639 }
640 else if( aGroup[i] == '}' )
641 {
642 if( braceNesting )
643 {
644 braceNesting--;
645 }
646 else
647 {
648 if( aMemberList && !tmp.IsEmpty() )
649 aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
650
651 return true;
652 }
653 }
654
655 // Commas aren't strictly legal, but we can be pretty sure what the author had in mind.
656 if( aGroup[i] == ' ' || aGroup[i] == ',' )
657 {
658 if( aMemberList && !tmp.IsEmpty() )
659 aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
660
661 tmp.Clear();
662 continue;
663 }
664
665 tmp += aGroup[i];
666 }
667
668 return false;
669}
int color
Definition: DXF_plotter.cpp:58
const char * name
Definition: DXF_plotter.cpp:57
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:110
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
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:293
bool isDigit(char cc)
Definition: dsnlexer.cpp:446
#define _(s)
@ CTX_NETCLASS
nlohmann::json json
Definition: gerbview.cpp:47
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:86
constexpr int IUToMils(int iu) const
Definition: base_units.h:99
constexpr int MilsToIU(int mils) const
Definition: base_units.h:93
constexpr int mmToIU(double mm) const
Definition: base_units.h:88