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[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 auto it2 = m_NetClassPatternAssignmentCache.find( aNetName );
395
396 if( it2 != m_NetClassPatternAssignmentCache.end() )
397 return getNetclass( it2->second );
398
399 for( const auto& [ matcher, netclassName ] : m_NetClassPatternAssignments )
400 {
401 if( matcher->StartsWith( aNetName ) )
402 {
403 m_NetClassPatternAssignmentCache[ aNetName ] = netclassName;
404 return getNetclass( netclassName );
405 }
406 }
407
408 m_NetClassPatternAssignmentCache[ aNetName ] = wxEmptyString;
409 return m_DefaultNetClass;
410}
411
412
413static bool isSuperSubOverbar( wxChar c )
414{
415 return c == '_' || c == '^' || c == '~';
416}
417
418
419bool NET_SETTINGS::ParseBusVector( const wxString& aBus, wxString* aName,
420 std::vector<wxString>* aMemberList )
421{
422 auto isDigit = []( wxChar c )
423 {
424 static wxString digits( wxT( "0123456789" ) );
425 return digits.Contains( c );
426 };
427
428 size_t busLen = aBus.length();
429 size_t i = 0;
430 wxString prefix;
431 wxString suffix;
432 wxString tmp;
433 long begin = 0;
434 long end = 0;
435 int braceNesting = 0;
436
437 prefix.reserve( busLen );
438
439 // Parse prefix
440 //
441 for( ; i < busLen; ++i )
442 {
443 if( aBus[i] == '{' )
444 {
445 if( i > 0 && isSuperSubOverbar( aBus[i-1] ) )
446 braceNesting++;
447 else
448 return false;
449 }
450 else if( aBus[i] == '}' )
451 {
452 braceNesting--;
453 }
454
455 if( aBus[i] == ' ' || aBus[i] == ']' )
456 return false;
457
458 if( aBus[i] == '[' )
459 break;
460
461 prefix += aBus[i];
462 }
463
464 // Parse start number
465 //
466 i++; // '[' character
467
468 if( i >= busLen )
469 return false;
470
471 for( ; i < busLen; ++i )
472 {
473 if( aBus[i] == '.' && i + 1 < busLen && aBus[i+1] == '.' )
474 {
475 tmp.ToLong( &begin );
476 i += 2;
477 break;
478 }
479
480 if( !isDigit( aBus[i] ) )
481 return false;
482
483 tmp += aBus[i];
484 }
485
486 // Parse end number
487 //
488 tmp = wxEmptyString;
489
490 if( i >= busLen )
491 return false;
492
493 for( ; i < busLen; ++i )
494 {
495 if( aBus[i] == ']' )
496 {
497 tmp.ToLong( &end );
498 ++i;
499 break;
500 }
501
502 if( !isDigit( aBus[i] ) )
503 return false;
504
505 tmp += aBus[i];
506 }
507
508 // Parse suffix
509 //
510 for( ; i < busLen; ++i )
511 {
512 if( aBus[i] == '}' )
513 {
514 braceNesting--;
515 suffix += aBus[i];
516 }
517 else
518 {
519 return false;
520 }
521 }
522
523 if( braceNesting != 0 )
524 return false;
525
526 if( begin == end )
527 return false;
528 else if( begin > end )
529 std::swap( begin, end );
530
531 if( aName )
532 *aName = prefix;
533
534 if( aMemberList )
535 {
536 for( long idx = begin; idx <= end; ++idx )
537 {
538 wxString str = prefix;
539 str << idx;
540 str << suffix;
541
542 aMemberList->emplace_back( str );
543 }
544 }
545
546 return true;
547}
548
549
550bool NET_SETTINGS::ParseBusGroup( const wxString& aGroup, wxString* aName,
551 std::vector<wxString>* aMemberList )
552{
553 size_t groupLen = aGroup.length();
554 size_t i = 0;
555 wxString prefix;
556 wxString tmp;
557 int braceNesting = 0;
558
559 prefix.reserve( groupLen );
560
561 // Parse prefix
562 //
563 for( ; i < groupLen; ++i )
564 {
565 if( aGroup[i] == '{' )
566 {
567 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
568 braceNesting++;
569 else
570 break;
571 }
572 else if( aGroup[i] == '}' )
573 {
574 braceNesting--;
575 }
576
577 if( aGroup[i] == ' ' || aGroup[i] == '[' || aGroup[i] == ']' )
578 return false;
579
580 prefix += aGroup[i];
581 }
582
583 if( braceNesting != 0 )
584 return false;
585
586 if( aName )
587 *aName = prefix;
588
589 // Parse members
590 //
591 i++; // '{' character
592
593 if( i >= groupLen )
594 return false;
595
596 for( ; i < groupLen; ++i )
597 {
598 if( aGroup[i] == '{' )
599 {
600 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
601 braceNesting++;
602 else
603 return false;
604 }
605 else if( aGroup[i] == '}' )
606 {
607 if( braceNesting )
608 {
609 braceNesting--;
610 }
611 else
612 {
613 if( aMemberList && !tmp.IsEmpty() )
614 aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
615
616 return true;
617 }
618 }
619
620 // Commas aren't strictly legal, but we can be pretty sure what the author had in mind.
621 if( aGroup[i] == ' ' || aGroup[i] == ',' )
622 {
623 if( aMemberList && !tmp.IsEmpty() )
624 aMemberList->push_back( EscapeString( tmp, CTX_NETNAME ) );
625
626 tmp.Clear();
627 continue;
628 }
629
630 tmp += aGroup[i];
631 }
632
633 return false;
634}
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: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:56
std::map< wxString, wxString > m_NetClassLabelAssignments
Definition: net_settings.h:48
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:46
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)
Like a normal param, but with custom getter and setter functions.
Definition: parameters.h:282
bool isDigit(char cc)
Definition: dsnlexer.cpp:447
#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