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