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 The 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 <algorithm>
23#include <limits>
24
25#include <json_common.h>
26
29#include <settings/parameters.h>
31#include <string_utils.h>
32#include <base_units.h>
33#include <unordered_set>
34
35
36// const int netSettingsSchemaVersion = 0;
37// const int netSettingsSchemaVersion = 1; // new overbar syntax
38// const int netSettingsSchemaVersion = 2; // exclude buses from netclass members
39// const int netSettingsSchemaVersion = 3; // netclass assignment patterns
40// const int netSettingsSchemaVersion = 4; // netclass ordering
41const int netSettingsSchemaVersion = 5; // Tuning profile names
42
43
44static std::optional<int> getInPcbUnits( const nlohmann::json& aObj, const std::string& aKey,
45 std::optional<int> aDefault = std::optional<int>() )
46{
47 if( aObj.contains( aKey ) && aObj[aKey].is_number() )
48 return pcbIUScale.mmToIU( aObj[aKey].get<double>() );
49 else
50 return aDefault;
51};
52
53
54static std::optional<int> getInSchUnits( const nlohmann::json& aObj, const std::string& aKey,
55 std::optional<int> aDefault = std::optional<int>() )
56{
57 if( aObj.contains( aKey ) && aObj[aKey].is_number() )
58 return schIUScale.MilsToIU( aObj[aKey].get<double>() );
59 else
60 return aDefault;
61};
62
63
64NET_SETTINGS::NET_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ) :
65 NESTED_SETTINGS( "net_settings", netSettingsSchemaVersion, aParent, aPath, false )
66{
67 m_defaultNetClass = std::make_shared<NETCLASS>( NETCLASS::Default, true );
68 m_defaultNetClass->SetDescription( _( "This is the default net class." ) );
69 m_defaultNetClass->SetPriority( std::numeric_limits<int>::max() );
70
71 auto saveNetclass =
72 []( nlohmann::json& json_array, const std::shared_ptr<NETCLASS>& nc )
73 {
74 // Note: we're in common/, but we do happen to know which of these
75 // fields are used in which units system.
76 nlohmann::json nc_json = { { "name", nc->GetName().ToUTF8() },
77 { "priority", nc->GetPriority() },
78 { "schematic_color", nc->GetSchematicColor( true ) },
79 { "pcb_color", nc->GetPcbColor( true ) },
80 { "tuning_profile", nc->GetTuningProfile() } };
81
82 auto saveInPcbUnits =
83 []( nlohmann::json& json, const std::string& aKey, int aValue )
84 {
85 json.push_back( { aKey, pcbIUScale.IUTomm( aValue ) } );
86 };
87
88 if( nc->HasWireWidth() )
89 nc_json.push_back(
90 { "wire_width", schIUScale.IUToMils( nc->GetWireWidth() ) } );
91
92 if( nc->HasBusWidth() )
93 nc_json.push_back( { "bus_width", schIUScale.IUToMils( nc->GetBusWidth() ) } );
94
95 if( nc->HasLineStyle() )
96 nc_json.push_back( { "line_style", nc->GetLineStyle() } );
97
98 if( nc->HasClearance() )
99 saveInPcbUnits( nc_json, "clearance", nc->GetClearance() );
100
101 if( nc->HasTrackWidth() )
102 saveInPcbUnits( nc_json, "track_width", nc->GetTrackWidth() );
103
104 if( nc->HasViaDiameter() )
105 saveInPcbUnits( nc_json, "via_diameter", nc->GetViaDiameter() );
106
107 if( nc->HasViaDrill() )
108 saveInPcbUnits( nc_json, "via_drill", nc->GetViaDrill() );
109
110 if( nc->HasuViaDiameter() )
111 saveInPcbUnits( nc_json, "microvia_diameter", nc->GetuViaDiameter() );
112
113 if( nc->HasuViaDrill() )
114 saveInPcbUnits( nc_json, "microvia_drill", nc->GetuViaDrill() );
115
116 if( nc->HasDiffPairWidth() )
117 saveInPcbUnits( nc_json, "diff_pair_width", nc->GetDiffPairWidth() );
118
119 if( nc->HasDiffPairGap() )
120 saveInPcbUnits( nc_json, "diff_pair_gap", nc->GetDiffPairGap() );
121
122 if( nc->HasDiffPairViaGap() )
123 saveInPcbUnits( nc_json, "diff_pair_via_gap", nc->GetDiffPairViaGap() );
124
125 json_array.push_back( nc_json );
126 };
127
128 auto readNetClass =
129 []( const nlohmann::json& entry )
130 {
131 wxString name = entry["name"];
132
133 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( name, false );
134
135 if( entry.contains( "priority" ) && entry["priority"].is_number() )
136 nc->SetPriority( entry["priority"].get<int>() );
137
138 if( entry.contains( "tuning_profile" ) && entry["tuning_profile"].is_string() )
139 nc->SetTuningProfile( entry["tuning_profile"].get<wxString>() );
140
141 if( auto value = getInPcbUnits( entry, "clearance" ) )
142 nc->SetClearance( *value );
143
144 if( auto value = getInPcbUnits( entry, "track_width" ) )
145 nc->SetTrackWidth( *value );
146
147 if( auto value = getInPcbUnits( entry, "via_diameter" ) )
148 nc->SetViaDiameter( *value );
149
150 if( auto value = getInPcbUnits( entry, "via_drill" ) )
151 nc->SetViaDrill( *value );
152
153 if( auto value = getInPcbUnits( entry, "microvia_diameter" ) )
154 nc->SetuViaDiameter( *value );
155
156 if( auto value = getInPcbUnits( entry, "microvia_drill" ) )
157 nc->SetuViaDrill( *value );
158
159 if( auto value = getInPcbUnits( entry, "diff_pair_width" ) )
160 nc->SetDiffPairWidth( *value );
161
162 if( auto value = getInPcbUnits( entry, "diff_pair_gap" ) )
163 nc->SetDiffPairGap( *value );
164
165 if( auto value = getInPcbUnits( entry, "diff_pair_via_gap" ) )
166 nc->SetDiffPairViaGap( *value );
167
168 if( auto value = getInSchUnits( entry, "wire_width" ) )
169 nc->SetWireWidth( *value );
170
171 if( auto value = getInSchUnits( entry, "bus_width" ) )
172 nc->SetBusWidth( *value );
173
174 if( entry.contains( "line_style" ) && entry["line_style"].is_number() )
175 nc->SetLineStyle( entry["line_style"].get<int>() );
176
177 if( entry.contains( "pcb_color" ) && entry["pcb_color"].is_string() )
178 nc->SetPcbColor( entry["pcb_color"].get<KIGFX::COLOR4D>() );
179
180 if( entry.contains( "schematic_color" )
181 && entry["schematic_color"].is_string() )
182 {
183 nc->SetSchematicColor( entry["schematic_color"].get<KIGFX::COLOR4D>() );
184 }
185
186 return nc;
187 };
188
189 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "classes",
190 [&]() -> nlohmann::json
191 {
192 nlohmann::json ret = nlohmann::json::array();
193
195 saveNetclass( ret, m_defaultNetClass );
196
197 for( const auto& [name, netclass] : m_netClasses )
198 saveNetclass( ret, netclass );
199
200 return ret;
201 },
202 [&]( const nlohmann::json& aJson )
203 {
204 if( !aJson.is_array() )
205 return;
206
207 m_netClasses.clear();
208
209 for( const nlohmann::json& entry : aJson )
210 {
211 if( !entry.is_object() || !entry.contains( "name" ) )
212 continue;
213
214 std::shared_ptr<NETCLASS> nc = readNetClass( entry );
215
216 if( nc->IsDefault() )
218 else
219 m_netClasses[nc->GetName()] = nc;
220 }
221 },
222 {} ) );
223
224 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "net_colors",
225 [&]() -> nlohmann::json
226 {
227 nlohmann::json ret = {};
228
229 for( const auto& [netname, color] : m_netColorAssignments )
230 {
231 std::string key( netname.ToUTF8() );
232 ret[ std::move( key ) ] = color;
233 }
234
235 return ret;
236 },
237 [&]( const nlohmann::json& aJson )
238 {
239 if( !aJson.is_object() )
240 return;
241
242 m_netColorAssignments.clear();
243
244 for( const auto& pair : aJson.items() )
245 {
246 wxString key( pair.key().c_str(), wxConvUTF8 );
247 m_netColorAssignments[std::move( key )] = pair.value().get<KIGFX::COLOR4D>();
248 }
249 },
250 {} ) );
251
252 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "net_chain_classes",
253 [&]() -> nlohmann::json
254 {
255 // Force object type so an empty map round-trips as {} rather than null;
256 // the reader rejects non-objects, which would otherwise leave stale
257 // chain assignments in place after the user clears them all.
258 nlohmann::json ret = nlohmann::json::object();
259
260 for( const auto& [chain, className] : m_netChainClasses )
261 ret[ std::string( chain.ToUTF8() ) ] = std::string( className.ToUTF8() );
262
263 return ret;
264 },
265 [&]( const nlohmann::json& aJson )
266 {
267 if( !aJson.is_object() )
268 return;
269
270 m_netChainClasses.clear();
271
272 for( const auto& pair : aJson.items() )
273 {
274 wxString chain( pair.key().c_str(), wxConvUTF8 );
275 wxString className = pair.value().get<wxString>();
276
277 if( !className.IsEmpty() )
278 m_netChainClasses[ std::move( chain ) ] = std::move( className );
279 }
280 },
281 {} ) );
282
283 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "netclass_assignments",
284 [&]() -> nlohmann::json
285 {
286 nlohmann::json ret = {};
287
288 for( const auto& [netname, netclassNames] : m_netClassLabelAssignments )
289 {
290 nlohmann::json netclassesJson = nlohmann::json::array();
291
292 for( const auto& netclass : netclassNames )
293 {
294 std::string netclassStr( netclass.ToUTF8() );
295 netclassesJson.push_back( std::move( netclassStr ) );
296 }
297
298 std::string key( netname.ToUTF8() );
299 ret[std::move( key )] = netclassesJson;
300 }
301
302 return ret;
303 },
304 [&]( const nlohmann::json& aJson )
305 {
306 if( !aJson.is_object() )
307 return;
308
310
311 for( const auto& pair : aJson.items() )
312 {
313 wxString key( pair.key().c_str(), wxConvUTF8 );
314
315 for( const auto& netclassName : pair.value() )
316 m_netClassLabelAssignments[key].insert( netclassName.get<wxString>() );
317 }
318 },
319 {} ) );
320
321 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "netclass_patterns",
322 [&]() -> nlohmann::json
323 {
324 nlohmann::json ret = nlohmann::json::array();
325
326 for( const auto& [matcher, netclassName] : m_netClassPatternAssignments )
327 {
328 nlohmann::json pattern_json = {
329 { "pattern", matcher->GetPattern().ToUTF8() },
330 { "netclass", netclassName.ToUTF8() }
331 };
332
333 ret.push_back( std::move( pattern_json ) );
334 }
335
336 return ret;
337 },
338 [&]( const nlohmann::json& aJson )
339 {
340 if( !aJson.is_array() )
341 return;
342
344
345 for( const nlohmann::json& entry : aJson )
346 {
347 if( !entry.is_object() )
348 continue;
349
350 if( entry.contains( "pattern" ) && entry["pattern"].is_string()
351 && entry.contains( "netclass" ) && entry["netclass"].is_string() )
352 {
353 wxString pattern = entry["pattern"].get<wxString>();
354 wxString netclass = entry["netclass"].get<wxString>();
355
356 // Expand bus patterns so individual bus member nets can be matched
357 ForEachBusMember( pattern,
358 [&]( const wxString& memberPattern )
359 {
360 addSinglePatternAssignment( memberPattern, netclass );
361 } );
362 }
363 }
364 },
365 {} ) );
366
367 registerMigration( 0, 1, std::bind( &NET_SETTINGS::migrateSchema0to1, this ) );
368 registerMigration( 1, 2, std::bind( &NET_SETTINGS::migrateSchema1to2, this ) );
369 registerMigration( 2, 3, std::bind( &NET_SETTINGS::migrateSchema2to3, this ) );
370 registerMigration( 3, 4, std::bind( &NET_SETTINGS::migrateSchema3to4, this ) );
371 registerMigration( 4, 5, std::bind( &NET_SETTINGS::migrateSchema4to5, this ) );
372}
373
374
376{
377 // Release early before destroying members
378 if( m_parent )
379 {
380 m_parent->ReleaseNestedSettings( this );
381 m_parent = nullptr;
382 }
383}
384
385
386bool NET_SETTINGS::operator==( const NET_SETTINGS& aOther ) const
387{
388 if( !std::equal( std::begin( m_netClasses ), std::end( m_netClasses ),
389 std::begin( aOther.m_netClasses ), std::end( aOther.m_netClasses ) ) )
390 return false;
391
392 // m_netClassPatternAssignments stores std::unique_ptr<EDA_COMBINED_MATCHER>, so a naive
393 // std::equal would compare matcher pointer identity and report two settings instances built
394 // from identical input as unequal. Compare pattern text plus the assigned netclass name.
395 auto patternEqual = []( const auto& aLhs, const auto& aRhs )
396 {
397 if( !aLhs.first || !aRhs.first )
398 return aLhs.first.get() == aRhs.first.get() && aLhs.second == aRhs.second;
399
400 return aLhs.first->GetPattern() == aRhs.first->GetPattern() && aLhs.second == aRhs.second;
401 };
402
403 if( !std::equal( std::begin( m_netClassPatternAssignments ),
405 std::begin( aOther.m_netClassPatternAssignments ),
406 std::end( aOther.m_netClassPatternAssignments ),
407 patternEqual ) )
408 return false;
409
410 // m_netClassChainPatternAssignments is derived state, rebuilt from m_netChainClasses and
411 // board NETINFO on every netlist update. Equality is defined by persisted inputs only;
412 // including the derived list here would mark the project dirty whenever a rebuild produced
413 // a transient ordering difference.
414
415 if( !std::equal( std::begin( m_netClassLabelAssignments ),
416 std::end( m_netClassLabelAssignments ),
417 std::begin( aOther.m_netClassLabelAssignments ),
418 std::end( aOther.m_netClassLabelAssignments ) ) )
419 return false;
420
421 if( !std::equal( std::begin( m_netColorAssignments ), std::end( m_netColorAssignments ),
422 std::begin( aOther.m_netColorAssignments ),
423 std::end( aOther.m_netColorAssignments ) ) )
424 return false;
425
427 return false;
428
429 return true;
430}
431
432
434{
435 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
436 {
437 for( auto& netClass : m_internals->At( "classes" ).items() )
438 {
439 if( netClass.value().contains( "nets" ) && netClass.value()["nets"].is_array() )
440 {
441 nlohmann::json migrated = nlohmann::json::array();
442
443 for( auto& net : netClass.value()["nets"].items() )
444 migrated.push_back( ConvertToNewOverbarNotation( net.value().get<wxString>() ) );
445
446 netClass.value()["nets"] = migrated;
447 }
448 }
449 }
450
451 return true;
452}
453
454
456{
457 return true;
458}
459
460
462{
463 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
464 {
465 nlohmann::json patterns = nlohmann::json::array();
466
467 for( auto& netClass : m_internals->At( "classes" ).items() )
468 {
469 if( netClass.value().contains( "name" )
470 && netClass.value().contains( "nets" )
471 && netClass.value()["nets"].is_array() )
472 {
473 wxString netClassName = netClass.value()["name"].get<wxString>();
474
475 for( auto& net : netClass.value()["nets"].items() )
476 {
477 nlohmann::json pattern_json = {
478 { "pattern", net.value().get<wxString>() },
479 { "netclass", netClassName }
480 };
481
482 patterns.push_back( pattern_json );
483 }
484 }
485 }
486
487 m_internals->SetFromString( "netclass_patterns", patterns );
488 }
489
490 return true;
491}
492
493
495{
496 // Add priority field to netclasses
497 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
498 {
499 int priority = 0;
500
501 for( auto& netClass : m_internals->At( "classes" ).items() )
502 {
503 if( netClass.value()["name"].get<wxString>() == NETCLASS::Default )
504 netClass.value()["priority"] = std::numeric_limits<int>::max();
505 else
506 netClass.value()["priority"] = priority++;
507 }
508 }
509
510 // Move netclass assignments to a list
511 if( m_internals->contains( "netclass_assignments" )
512 && m_internals->At( "netclass_assignments" ).is_object() )
513 {
514 nlohmann::json migrated = {};
515
516 for( const auto& pair : m_internals->At( "netclass_assignments" ).items() )
517 {
518 nlohmann::json netclassesJson = nlohmann::json::array();
519
520 if( pair.value().get<wxString>() != wxEmptyString )
521 netclassesJson.push_back( pair.value() );
522
523 migrated[pair.key()] = netclassesJson;
524 }
525
526 m_internals->SetFromString( "netclass_assignments", migrated );
527 }
528
529 return true;
530}
531
532
534{
535 // Add tuning profile name field to netclasses
536 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
537 {
538 const wxString emptyStr = "";
539
540 for( auto& netClass : m_internals->At( "classes" ).items() )
541 netClass.value()["tuning_profile"] = emptyStr.ToUTF8();
542 }
543
544 return true;
545}
546
547
548void NET_SETTINGS::SetDefaultNetclass( std::shared_ptr<NETCLASS> netclass )
549{
550 m_defaultNetClass = netclass;
551}
552
553
554std::shared_ptr<NETCLASS> NET_SETTINGS::GetDefaultNetclass()
555{
556 return m_defaultNetClass;
557}
558
559
560bool NET_SETTINGS::HasNetclass( const wxString& netclassName ) const
561{
562 return m_netClasses.find( netclassName ) != m_netClasses.end();
563}
564
565
566void NET_SETTINGS::SetNetclass( const wxString& netclassName, std::shared_ptr<NETCLASS>& netclass )
567{
568 m_netClasses[netclassName] = netclass;
569}
570
571
572void NET_SETTINGS::SetNetclasses( const std::map<wxString, std::shared_ptr<NETCLASS>>& netclasses )
573{
574 m_netClasses = netclasses;
576}
577
578
579const std::map<wxString, std::shared_ptr<NETCLASS>>& NET_SETTINGS::GetNetclasses() const
580{
581 return m_netClasses;
582}
583
584
585const std::map<wxString, std::shared_ptr<NETCLASS>>& NET_SETTINGS::GetCompositeNetclasses() const
586{
588}
589
590
592{
593 m_netClasses.clear();
594 m_impicitNetClasses.clear();
596}
597
598
599const std::map<wxString, std::set<wxString>>& NET_SETTINGS::GetNetclassLabelAssignments() const
600{
602}
603
604
609
610
611void NET_SETTINGS::ClearNetclassLabelAssignment( const wxString& netName )
612{
613 m_netClassLabelAssignments.erase( netName );
614}
615
616
617void NET_SETTINGS::SetNetclassLabelAssignment( const wxString& netName,
618 const std::set<wxString>& netclasses )
619{
620 m_netClassLabelAssignments[netName] = netclasses;
621}
622
623
625 const std::set<wxString>& netclasses )
626{
627 m_netClassLabelAssignments[netName].insert( netclasses.begin(), netclasses.end() );
628}
629
630
631bool NET_SETTINGS::HasNetclassLabelAssignment( const wxString& netName ) const
632{
633 return m_netClassLabelAssignments.find( netName ) != m_netClassLabelAssignments.end();
634}
635
636
637void NET_SETTINGS::SetNetclassPatternAssignment( const wxString& pattern, const wxString& netclass )
638{
639 // Expand bus patterns (vector buses and bus groups) to individual member patterns.
640 // This is necessary because the regex/wildcard matchers interpret brackets and braces
641 // as special characters, not as bus notation.
642 ForEachBusMember( pattern,
643 [&]( const wxString& memberPattern )
644 {
645 addSinglePatternAssignment( memberPattern, netclass );
646 } );
647
649}
650
651
652void NET_SETTINGS::addSinglePatternAssignment( const wxString& pattern, const wxString& netclass )
653{
654 // Avoid exact duplicates - these shouldn't cause problems, due to later de-duplication
655 // but they are unnecessary.
656 for( auto& assignment : m_netClassPatternAssignments )
657 {
658 if( assignment.first->GetPattern() == pattern && assignment.second == netclass )
659 return;
660 }
661
662 // No assignment, add a new one
664 { std::make_unique<EDA_COMBINED_MATCHER>( pattern, CTX_NETCLASS ), netclass } );
665}
666
667
669 std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>&& netclassPatterns )
670{
671 m_netClassPatternAssignments = std::move( netclassPatterns );
673}
674
675
676std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>&
681
682
687
688
689void NET_SETTINGS::SetChainPatternAssignment( const wxString& pattern, const wxString& netclass )
690{
691 ForEachBusMember( pattern,
692 [&]( const wxString& memberPattern )
693 {
694 addSingleChainPatternAssignment( memberPattern, netclass );
695 } );
696
698}
699
700
702 const wxString& netclass )
703{
704 for( auto& assignment : m_netClassChainPatternAssignments )
705 {
706 if( !assignment.first )
707 continue;
708
709 if( assignment.first->GetPattern() == pattern && assignment.second == netclass )
710 return;
711 }
712
714 { std::make_unique<EDA_COMBINED_MATCHER>( pattern, CTX_NETCLASS ), netclass } );
715}
716
717
723
724
725void NET_SETTINGS::ClearCacheForNet( const wxString& netName )
726{
727 if( m_effectiveNetclassCache.count( netName ) )
728 {
729 wxString compositeNetclassName = m_effectiveNetclassCache[netName]->GetName();
730 m_compositeNetClasses.erase( compositeNetclassName );
731 m_effectiveNetclassCache.erase( netName );
732 }
733}
734
735
741
742
743void NET_SETTINGS::SetNetColorAssignment( const wxString& netName, const KIGFX::COLOR4D& color )
744{
745 m_netColorAssignments[netName] = color;
746}
747
748
749const std::map<wxString, KIGFX::COLOR4D>& NET_SETTINGS::GetNetColorAssignments() const
750{
752}
753
754
759
760
761bool NET_SETTINGS::HasEffectiveNetClass( const wxString& aNetName ) const
762{
763 return m_effectiveNetclassCache.count( aNetName ) > 0;
764}
765
766
767std::shared_ptr<NETCLASS> NET_SETTINGS::GetCachedEffectiveNetClass( const wxString& aNetName ) const
768{
769 return m_effectiveNetclassCache.at( aNetName );
770}
771
772
773std::shared_ptr<NETCLASS> NET_SETTINGS::GetEffectiveNetClass( const wxString& aNetName )
774{
775 // Lambda to fetch an explicit netclass. Returns a nullptr if not found
776 auto getExplicitNetclass =
777 [this]( const wxString& netclass ) -> std::shared_ptr<NETCLASS>
778 {
779 if( netclass == NETCLASS::Default )
780 return m_defaultNetClass;
781
782 auto ii = m_netClasses.find( netclass );
783
784 if( ii == m_netClasses.end() )
785 return {};
786 else
787 return ii->second;
788 };
789
790 // Lambda to fetch or create an implicit netclass (defined with a label, but not configured)
791 // These are needed as while they do not provide any netclass parameters, they do now appear in
792 // DRC matching strings as an assigned netclass.
793 auto getOrAddImplicitNetcless =
794 [this]( const wxString& netclass ) -> std::shared_ptr<NETCLASS>
795 {
796 auto ii = m_impicitNetClasses.find( netclass );
797
798 if( ii == m_impicitNetClasses.end() )
799 {
800 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( netclass, false );
801 nc->SetPriority( std::numeric_limits<int>::max() - 1 ); // Priority > default netclass
802 m_impicitNetClasses[netclass] = nc;
803 return nc;
804 }
805 else
806 {
807 return ii->second;
808 }
809 };
810
811 // <no net> is forced to be part of the default netclass.
812 if( aNetName.IsEmpty() )
813 return m_defaultNetClass;
814
815 // First check if we have a cached resolved netclass
816 auto cacheItr = m_effectiveNetclassCache.find( aNetName );
817
818 if( cacheItr != m_effectiveNetclassCache.end() )
819 return cacheItr->second;
820
821 // No cache found - build a vector of all netclasses assigned to or matching this net
822 std::unordered_set<std::shared_ptr<NETCLASS>> resolvedNetclasses;
823
824 // First find explicit netclass assignments
825 auto it = m_netClassLabelAssignments.find( aNetName );
826
827 if( it != m_netClassLabelAssignments.end() && it->second.size() > 0 )
828 {
829 for( const wxString& netclassName : it->second )
830 {
831 std::shared_ptr<NETCLASS> netclass = getExplicitNetclass( netclassName );
832
833 if( netclass )
834 {
835 resolvedNetclasses.insert( std::move( netclass ) );
836 }
837 else
838 {
839 resolvedNetclasses.insert( getOrAddImplicitNetcless( netclassName ) );
840 }
841 }
842 }
843
844 // Now find any pattern-matched netclass assignments (user + chain-derived)
845 auto applyPatternList =
846 [&]( const std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>&
847 patterns )
848 {
849 for( const auto& [matcher, netclassName] : patterns )
850 {
851 if( matcher->StartsWith( aNetName ) )
852 {
853 std::shared_ptr<NETCLASS> netclass = getExplicitNetclass( netclassName );
854
855 if( netclass )
856 resolvedNetclasses.insert( std::move( netclass ) );
857 else
858 resolvedNetclasses.insert( getOrAddImplicitNetcless( netclassName ) );
859 }
860 }
861 };
862
863 applyPatternList( m_netClassPatternAssignments );
864 applyPatternList( m_netClassChainPatternAssignments );
865
866 // Handle zero resolved netclasses
867 if( resolvedNetclasses.size() == 0 )
868 {
869 // For bus patterns, check if all members share the same netclass.
870 // If they do, the bus inherits that netclass for coloring purposes.
871 std::shared_ptr<NETCLASS> sharedNetclass;
872 bool allSameNetclass = true;
873 bool isBusPattern = false;
874
875 ForEachBusMember( aNetName,
876 [&]( const wxString& member )
877 {
878 // If ForEachBusMember gives us back the same name, it's not a bus.
879 // Skip to avoid infinite recursion.
880 if( member == aNetName )
881 return;
882
883 isBusPattern = true;
884
885 if( !allSameNetclass )
886 return;
887
888 std::shared_ptr<NETCLASS> memberNc = GetEffectiveNetClass( member );
889
890 if( !sharedNetclass )
891 {
892 sharedNetclass = memberNc;
893 }
894 else if( memberNc->GetName() != sharedNetclass->GetName() )
895 {
896 allSameNetclass = false;
897 }
898 } );
899
900 if( isBusPattern && allSameNetclass && sharedNetclass
901 && sharedNetclass->GetName() != NETCLASS::Default )
902 {
903 m_effectiveNetclassCache[aNetName] = sharedNetclass;
904 return sharedNetclass;
905 }
906
908
909 return m_defaultNetClass;
910 }
911
912 // Make and cache the effective netclass. Note that makeEffectiveNetclass will add the default
913 // netclass to resolvedNetclasses if it is needed to complete the netclass paramters set. It
914 // will also sort resolvedNetclasses by priority order.
915 std::vector<NETCLASS*> netclassPtrs;
916
917 for( const std::shared_ptr<NETCLASS>& nc : resolvedNetclasses )
918 netclassPtrs.push_back( nc.get() );
919
920 wxString name;
921 name.Printf( "Effective for net: %s", aNetName );
922 std::shared_ptr<NETCLASS> effectiveNetclass = std::make_shared<NETCLASS>( name, false );
923 makeEffectiveNetclass( effectiveNetclass, netclassPtrs );
924
925 if( netclassPtrs.size() == 1 )
926 {
927 // No defaults were added - just return the primary netclass
928 m_effectiveNetclassCache[aNetName] = *resolvedNetclasses.begin();
929 return *resolvedNetclasses.begin();
930 }
931 else
932 {
933 effectiveNetclass->SetConstituentNetclasses( std::move( netclassPtrs ) );
934
935 m_compositeNetClasses[effectiveNetclass->GetName()] = effectiveNetclass;
936 m_effectiveNetclassCache[aNetName] = effectiveNetclass;
937
938 return effectiveNetclass;
939 }
940}
941
942
944{
945 for( auto& [ncName, nc] : m_compositeNetClasses )
946 {
947 // Note this needs to be a copy in case we now need to add the default netclass
948 std::vector<NETCLASS*> constituents = nc->GetConstituentNetclasses();
949
950 wxASSERT( constituents.size() > 0 );
951
952 // If the last netclass is Default, remove it (it will be re-added if still needed)
953 if( ( *constituents.rbegin() )->GetName() == NETCLASS::Default )
954 {
955 constituents.pop_back();
956 }
957
958 // Remake the netclass from original constituents
959 nc->ResetParameters();
960 makeEffectiveNetclass( nc, constituents );
961 nc->SetConstituentNetclasses( std::move( constituents ) );
962 }
963}
964
965
966void NET_SETTINGS::makeEffectiveNetclass( std::shared_ptr<NETCLASS>& effectiveNetclass,
967 std::vector<NETCLASS*>& constituentNetclasses ) const
968{
969 // Sort the resolved netclasses by priority (highest first), with same-priority netclasses
970 // ordered alphabetically
971 std::sort( constituentNetclasses.begin(), constituentNetclasses.end(),
972 []( NETCLASS* nc1, NETCLASS* nc2 )
973 {
974 int p1 = nc1->GetPriority();
975 int p2 = nc2->GetPriority();
976
977 if( p1 < p2 )
978 return true;
979
980 if (p1 == p2)
981 return nc1->GetName().Cmp( nc2->GetName() ) < 0;
982
983 return false;
984 } );
985
986 // Iterate from lowest priority netclass and fill effective netclass parameters
987 for( auto itr = constituentNetclasses.rbegin(); itr != constituentNetclasses.rend(); ++itr )
988 {
989 NETCLASS* nc = *itr;
990
991 if( nc->HasClearance() )
992 {
993 effectiveNetclass->SetClearance( nc->GetClearance() );
994 effectiveNetclass->SetClearanceParent( nc );
995 }
996
997 if( nc->HasTrackWidth() )
998 {
999 effectiveNetclass->SetTrackWidth( nc->GetTrackWidth() );
1000 effectiveNetclass->SetTrackWidthParent( nc );
1001 }
1002
1003 if( nc->HasViaDiameter() )
1004 {
1005 effectiveNetclass->SetViaDiameter( nc->GetViaDiameter() );
1006 effectiveNetclass->SetViaDiameterParent( nc );
1007 }
1008
1009 if( nc->HasViaDrill() )
1010 {
1011 effectiveNetclass->SetViaDrill( nc->GetViaDrill() );
1012 effectiveNetclass->SetViaDrillParent( nc );
1013 }
1014
1015 if( nc->HasuViaDiameter() )
1016 {
1017 effectiveNetclass->SetuViaDiameter( nc->GetuViaDiameter() );
1018 effectiveNetclass->SetuViaDiameterParent( nc );
1019 }
1020
1021 if( nc->HasuViaDrill() )
1022 {
1023 effectiveNetclass->SetuViaDrill( nc->GetuViaDrill() );
1024 effectiveNetclass->SetuViaDrillParent( nc );
1025 }
1026
1027 if( nc->HasDiffPairWidth() )
1028 {
1029 effectiveNetclass->SetDiffPairWidth( nc->GetDiffPairWidth() );
1030 effectiveNetclass->SetDiffPairWidthParent( nc );
1031 }
1032
1033 if( nc->HasDiffPairGap() )
1034 {
1035 effectiveNetclass->SetDiffPairGap( nc->GetDiffPairGap() );
1036 effectiveNetclass->SetDiffPairGapParent( nc );
1037 }
1038
1039 if( nc->HasDiffPairViaGap() )
1040 {
1041 effectiveNetclass->SetDiffPairViaGap( nc->GetDiffPairViaGap() );
1042 effectiveNetclass->SetDiffPairViaGapParent( nc );
1043 }
1044
1045 if( nc->HasWireWidth() )
1046 {
1047 effectiveNetclass->SetWireWidth( nc->GetWireWidth() );
1048 effectiveNetclass->SetWireWidthParent( nc );
1049 }
1050
1051 if( nc->HasBusWidth() )
1052 {
1053 effectiveNetclass->SetBusWidth( nc->GetBusWidth() );
1054 effectiveNetclass->SetBusWidthParent( nc );
1055 }
1056
1057 if( nc->HasLineStyle() )
1058 {
1059 effectiveNetclass->SetLineStyle( nc->GetLineStyle() );
1060 effectiveNetclass->SetLineStyleParent( nc );
1061 }
1062
1063 COLOR4D pcbColor = nc->GetPcbColor();
1064
1065 if( pcbColor != COLOR4D::UNSPECIFIED )
1066 {
1067 effectiveNetclass->SetPcbColor( pcbColor );
1068 effectiveNetclass->SetPcbColorParent( nc );
1069 }
1070
1071 COLOR4D schColor = nc->GetSchematicColor();
1072
1073 if( schColor != COLOR4D::UNSPECIFIED )
1074 {
1075 effectiveNetclass->SetSchematicColor( schColor );
1076 effectiveNetclass->SetSchematicColorParent( nc );
1077 }
1078
1079 if( nc->HasTuningProfile() )
1080 {
1081 effectiveNetclass->SetTuningProfile( nc->GetTuningProfile() );
1082 effectiveNetclass->SetTuningProfileParent( nc );
1083 }
1084 }
1085
1086 // Fill in any required defaults
1087 if( addMissingDefaults( effectiveNetclass.get() ) )
1088 constituentNetclasses.push_back( m_defaultNetClass.get() );
1089}
1090
1091
1093{
1094 bool addedDefault = false;
1095
1096 if( !nc->HasClearance() )
1097 {
1098 addedDefault = true;
1099 nc->SetClearance( m_defaultNetClass->GetClearance() );
1101 }
1102
1103 if( !nc->HasTrackWidth() )
1104 {
1105 addedDefault = true;
1106 nc->SetTrackWidth( m_defaultNetClass->GetTrackWidth() );
1108 }
1109
1110 if( !nc->HasViaDiameter() )
1111 {
1112 addedDefault = true;
1113 nc->SetViaDiameter( m_defaultNetClass->GetViaDiameter() );
1115 }
1116
1117 if( !nc->HasViaDrill() )
1118 {
1119 addedDefault = true;
1120 nc->SetViaDrill( m_defaultNetClass->GetViaDrill() );
1122 }
1123
1124 if( !nc->HasuViaDiameter() )
1125 {
1126 addedDefault = true;
1127 nc->SetuViaDiameter( m_defaultNetClass->GetuViaDiameter() );
1129 }
1130
1131 if( !nc->HasuViaDrill() )
1132 {
1133 addedDefault = true;
1134 nc->SetuViaDrill( m_defaultNetClass->GetuViaDrill() );
1136 }
1137
1138 if( !nc->HasDiffPairWidth() )
1139 {
1140 addedDefault = true;
1141 nc->SetDiffPairWidth( m_defaultNetClass->GetDiffPairWidth() );
1143 }
1144
1145 if( !nc->HasDiffPairGap() )
1146 {
1147 addedDefault = true;
1148 nc->SetDiffPairGap( m_defaultNetClass->GetDiffPairGap() );
1150 }
1151
1152 // Currently this is only on the default netclass, and not editable in the setup panel
1153 // if( !nc->HasDiffPairViaGap() )
1154 // {
1155 // addedDefault = true;
1156 // nc->SetDiffPairViaGap( m_defaultNetClass->GetDiffPairViaGap() );
1157 // nc->SetDiffPairViaGapParent( m_defaultNetClass.get() );
1158 // }
1159
1160 if( !nc->HasWireWidth() )
1161 {
1162 addedDefault = true;
1163 nc->SetWireWidth( m_defaultNetClass->GetWireWidth() );
1165 }
1166
1167 if( !nc->HasBusWidth() )
1168 {
1169 addedDefault = true;
1170 nc->SetBusWidth( m_defaultNetClass->GetBusWidth() );
1172 }
1173
1174 // The tuning profile can be empty - only fill if a default tuning profile is set
1175 if( !nc->HasTuningProfile() && m_defaultNetClass->HasTuningProfile() )
1176 {
1177 addedDefault = true;
1178 nc->SetTuningProfile( m_defaultNetClass->GetTuningProfile() );
1180 }
1181
1182 return addedDefault;
1183}
1184
1185
1186std::shared_ptr<NETCLASS> NET_SETTINGS::GetNetClassByName( const wxString& aNetClassName ) const
1187{
1188 auto ii = m_netClasses.find( aNetClassName );
1189
1190 if( ii == m_netClasses.end() )
1191 return m_defaultNetClass;
1192 else
1193 return ii->second;
1194}
1195
1196
1197static bool isSuperSubOverbar( wxChar c )
1198{
1199 return c == '_' || c == '^' || c == '~';
1200}
1201
1202
1210static bool isEscaped( const wxString& aStr, size_t aPos )
1211{
1212 if( aPos == 0 )
1213 return false;
1214
1215 // Count consecutive backslashes before this position
1216 int backslashCount = 0;
1217 size_t pos = aPos;
1218
1219 while( pos > 0 && aStr[pos - 1] == '\\' )
1220 {
1221 backslashCount++;
1222 pos--;
1223 }
1224
1225 // If odd number of backslashes, the character is escaped
1226 return ( backslashCount % 2 ) == 1;
1227}
1228
1229
1230bool NET_SETTINGS::ParseBusVector( const wxString& aBus, wxString* aName,
1231 std::vector<wxString>* aMemberList )
1232{
1233 auto isDigit =
1234 []( wxChar c )
1235 {
1236 static wxString digits( wxT( "0123456789" ) );
1237 return digits.Contains( c );
1238 };
1239
1240 size_t busLen = aBus.length();
1241 size_t i = 0;
1242 wxString prefix;
1243 wxString suffix;
1244 wxString tmp;
1245 long begin = 0;
1246 long end = 0;
1247 int braceNesting = 0;
1248 bool fmtWrapsName = false;
1249 bool inQuotes = false;
1250
1251 prefix.reserve( busLen );
1252
1253 // Parse prefix
1254 //
1255 // Formatting markers (^{}, _{}, ~{}) can appear either as part of the prefix name
1256 // (e.g. I^{2}C[0..7]) or wrapping the range specifier (e.g. D_{[1..2]}).
1257 // We preserve formatting in the prefix and only strip it when the range bracket
1258 // appears inside formatting braces, indicating the formatting wraps the range.
1259 //
1260 for( ; i < busLen; ++i )
1261 {
1262 // Handle quoted strings (allows spaces inside)
1263 if( aBus[i] == '"' && !isEscaped( aBus, i ) )
1264 {
1265 inQuotes = !inQuotes;
1266 continue;
1267 }
1268
1269 if( inQuotes )
1270 {
1271 // Inside quotes, add characters directly (including spaces)
1272 if( aBus[i] == '\\' && i + 1 < busLen )
1273 {
1274 // Handle escaped characters inside quotes
1275 prefix += aBus[++i];
1276 }
1277 else
1278 {
1279 prefix += aBus[i];
1280 }
1281
1282 continue;
1283 }
1284
1285 if( aBus[i] == '{' )
1286 {
1287 if( i > 0 && isSuperSubOverbar( aBus[i-1] ) )
1288 {
1289 braceNesting++;
1290 prefix += wxT( '{' );
1291 continue;
1292 }
1293 else
1294 return false;
1295 }
1296 else if( aBus[i] == '}' )
1297 {
1298 braceNesting--;
1299 prefix += wxT( '}' );
1300 continue;
1301 }
1302
1303 // Handle backslash-escaped spaces
1304 if( aBus[i] == '\\' && i + 1 < busLen && aBus[i + 1] == ' ' )
1305 {
1306 prefix += aBus[++i];
1307 continue;
1308 }
1309
1310 // Unescaped space or ] in bus vector prefix is not allowed
1311 if( aBus[i] == ' ' || aBus[i] == ']' )
1312 return false;
1313
1314 if( aBus[i] == '[' )
1315 {
1316 if( braceNesting > 0 )
1317 {
1318 size_t fmtStart = prefix.rfind( wxT( '{' ) );
1319
1320 if( fmtStart != wxString::npos && fmtStart > 0
1321 && isSuperSubOverbar( prefix[fmtStart - 1] ) )
1322 {
1323 if( fmtStart == prefix.length() - 1 )
1324 {
1325 // '{' immediately precedes '[' (e.g. D_{[1..2]}).
1326 // The formatting decorates the range indices, not the
1327 // name itself.
1328 prefix.erase( fmtStart - 1 );
1329 }
1330 else
1331 {
1332 // Name characters exist between '{' and '[' (e.g.
1333 // ~{BE[0..3]}). The formatting wraps the signal name,
1334 // not the range.
1335 fmtWrapsName = true;
1336 }
1337 }
1338 }
1339
1340 break;
1341 }
1342
1343 prefix += aBus[i];
1344 }
1345
1346 // Parse start number
1347 //
1348 i++; // '[' character
1349
1350 if( i >= busLen )
1351 return false;
1352
1353 for( ; i < busLen; ++i )
1354 {
1355 if( aBus[i] == '.' && i + 1 < busLen && aBus[i+1] == '.' )
1356 {
1357 tmp.ToLong( &begin );
1358 i += 2;
1359 break;
1360 }
1361
1362 if( !isDigit( aBus[i] ) )
1363 return false;
1364
1365 tmp += aBus[i];
1366 }
1367
1368 // Parse end number
1369 //
1370 tmp = wxEmptyString;
1371
1372 if( i >= busLen )
1373 return false;
1374
1375 for( ; i < busLen; ++i )
1376 {
1377 if( aBus[i] == ']' )
1378 {
1379 tmp.ToLong( &end );
1380 ++i;
1381 break;
1382 }
1383
1384 if( !isDigit( aBus[i] ) )
1385 return false;
1386
1387 tmp += aBus[i];
1388 }
1389
1390 // Parse suffix
1391 //
1392 for( ; i < busLen; ++i )
1393 {
1394 if( aBus[i] == '}' )
1395 {
1396 braceNesting--;
1397
1398 if( fmtWrapsName )
1399 suffix += aBus[i];
1400 }
1401 else if( aBus[i] == '+' || aBus[i] == '-' || aBus[i] == 'P' || aBus[i] == 'N' )
1402 {
1403 suffix += aBus[i];
1404 }
1405 else
1406 {
1407 return false;
1408 }
1409 }
1410
1411 if( braceNesting != 0 )
1412 return false;
1413
1414 if( begin == end )
1415 return false;
1416 else if( begin > end )
1417 std::swap( begin, end );
1418
1419 if( aName )
1420 *aName = prefix;
1421
1422 if( aMemberList )
1423 {
1424 for( long idx = begin; idx <= end; ++idx )
1425 {
1426 wxString str = prefix;
1427 str << idx;
1428 str << suffix;
1429
1430 aMemberList->emplace_back( str );
1431 }
1432 }
1433
1434 return true;
1435}
1436
1437
1438bool NET_SETTINGS::ParseBusGroup( const wxString& aGroup, wxString* aName,
1439 std::vector<wxString>* aMemberList )
1440{
1441 size_t groupLen = aGroup.length();
1442 size_t i = 0;
1443 wxString prefix;
1444 wxString tmp;
1445 int braceNesting = 0;
1446 bool inQuotes = false;
1447
1448 prefix.reserve( groupLen );
1449
1450 // Escape spaces in member names so recursive parsing by ForEachBusMember works correctly.
1451 // Both quoted strings and backslash-escaped spaces collapse to bare spaces during parsing,
1452 // so we must re-escape them for subsequent ParseBusVector/ParseBusGroup calls.
1453 auto escapeSpacesForBus =
1454 []( const wxString& aMember ) -> wxString
1455 {
1456 wxString escaped;
1457 escaped.reserve( aMember.length() * 2 );
1458
1459 for( wxUniChar c : aMember )
1460 {
1461 if( c == ' ' )
1462 escaped += wxT( "\\ " );
1463 else
1464 escaped += c;
1465 }
1466
1467 return escaped;
1468 };
1469
1470 // Parse prefix
1471 //
1472 // Formatting markers (^{}, _{}, ~{}) in the prefix are part of the group name
1473 // and must be preserved. The member-list opening brace is distinguished by NOT
1474 // being preceded by a formatting character.
1475 //
1476 for( ; i < groupLen; ++i )
1477 {
1478 // Handle quoted strings (allows spaces inside)
1479 if( aGroup[i] == '"' && !isEscaped( aGroup, i ) )
1480 {
1481 inQuotes = !inQuotes;
1482 continue;
1483 }
1484
1485 if( inQuotes )
1486 {
1487 // Inside quotes, add characters directly (including spaces)
1488 if( aGroup[i] == '\\' && i + 1 < groupLen )
1489 {
1490 // Handle escaped characters inside quotes
1491 prefix += aGroup[++i];
1492 }
1493 else
1494 {
1495 prefix += aGroup[i];
1496 }
1497
1498 continue;
1499 }
1500
1501 if( aGroup[i] == '{' )
1502 {
1503 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
1504 {
1505 braceNesting++;
1506 prefix += wxT( '{' );
1507 continue;
1508 }
1509 else
1510 break;
1511 }
1512 else if( aGroup[i] == '}' )
1513 {
1514 braceNesting--;
1515 prefix += wxT( '}' );
1516 continue;
1517 }
1518
1519 // Handle backslash-escaped spaces
1520 if( aGroup[i] == '\\' && i + 1 < groupLen && aGroup[i + 1] == ' ' )
1521 {
1522 prefix += aGroup[++i];
1523 continue;
1524 }
1525
1526 // Unescaped space, [, or ] in bus group prefix is not allowed
1527 if( aGroup[i] == ' ' || aGroup[i] == '[' || aGroup[i] == ']' )
1528 return false;
1529
1530 prefix += aGroup[i];
1531 }
1532
1533 if( braceNesting != 0 )
1534 return false;
1535
1536 if( aName )
1537 *aName = prefix;
1538
1539 // Parse members
1540 //
1541 i++; // '{' character
1542
1543 if( i >= groupLen )
1544 return false;
1545
1546 inQuotes = false;
1547
1548 for( ; i < groupLen; ++i )
1549 {
1550 // Handle quoted strings (allows spaces inside member names)
1551 if( aGroup[i] == '"' && !isEscaped( aGroup, i ) )
1552 {
1553 inQuotes = !inQuotes;
1554 continue;
1555 }
1556
1557 if( inQuotes )
1558 {
1559 // Inside quotes, add characters directly (including spaces)
1560 if( aGroup[i] == '\\' && i + 1 < groupLen )
1561 {
1562 // Handle escaped characters inside quotes
1563 tmp += aGroup[++i];
1564 }
1565 else
1566 {
1567 tmp += aGroup[i];
1568 }
1569
1570 continue;
1571 }
1572
1573 if( aGroup[i] == '{' )
1574 {
1575 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
1576 {
1577 braceNesting++;
1578
1579 // Keep the full formatting notation (e.g. ~{CAS}) in the member name.
1580 // A net named ~{CAS} is distinct from CAS, and stripping the marker
1581 // would lose that identity. Vector bus members like D_{[1..2]} also
1582 // preserve their subscript so recursive ForEachBusMember can parse them.
1583 tmp += wxT( '{' );
1584 continue;
1585 }
1586 else
1587 return false;
1588 }
1589 else if( aGroup[i] == '}' )
1590 {
1591 if( braceNesting )
1592 {
1593 braceNesting--;
1594 tmp += wxT( '}' );
1595 continue;
1596 }
1597 else
1598 {
1599 if( aMemberList && !tmp.IsEmpty() )
1600 aMemberList->push_back( EscapeString( escapeSpacesForBus( tmp ), CTX_NETNAME ) );
1601
1602 return true;
1603 }
1604 }
1605
1606 // Handle backslash-escaped spaces in member names
1607 if( aGroup[i] == '\\' && i + 1 < groupLen && aGroup[i + 1] == ' ' )
1608 {
1609 tmp += aGroup[++i];
1610 continue;
1611 }
1612
1613 // Unescaped space or comma separates members
1614 if( aGroup[i] == ' ' || aGroup[i] == ',' )
1615 {
1616 if( aMemberList && !tmp.IsEmpty() )
1617 aMemberList->push_back( EscapeString( escapeSpacesForBus( tmp ), CTX_NETNAME ) );
1618
1619 tmp.Clear();
1620 continue;
1621 }
1622
1623 tmp += aGroup[i];
1624 }
1625
1626 return false;
1627}
1628
1629
1630void NET_SETTINGS::ForEachBusMember( const wxString& aBusPattern,
1631 const std::function<void( const wxString& )>& aFunction )
1632{
1633 std::vector<wxString> members;
1634
1635 if( ParseBusVector( aBusPattern, nullptr, &members ) )
1636 {
1637 // Vector bus: call function for each expanded member
1638 for( const wxString& member : members )
1639 aFunction( member );
1640 }
1641 else if( ParseBusGroup( aBusPattern, nullptr, &members ) )
1642 {
1643 // Bus group: recursively expand each member (which may itself be a vector or group)
1644 for( const wxString& member : members )
1645 ForEachBusMember( member, aFunction );
1646 }
1647 else
1648 {
1649 // Not a bus pattern: call function with the original pattern
1650 aFunction( aBusPattern );
1651 }
1652}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:127
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:402
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.
JSON_SETTINGS(const wxString &aFilename, SETTINGS_LOC aLocation, int aSchemaVersion)
std::unique_ptr< JSON_SETTINGS_INTERNALS > m_internals
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
JSON_SETTINGS * m_parent
A pointer to the parent object to load and store from.
NESTED_SETTINGS(const std::string &aName, int aSchemaVersion, JSON_SETTINGS *aParent, const std::string &aPath, bool aLoadFromFile=true)
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:42
void SetViaDiameter(int aDia)
Definition netclass.h:132
void SetViaDrill(int aSize)
Definition netclass.h:140
bool HasLineStyle() const
Definition netclass.h:230
int GetViaDiameter() const
Definition netclass.h:130
int GetViaDrill() const
Definition netclass.h:138
void SetWireWidthParent(NETCLASS *parent)
Definition netclass.h:205
static const char Default[]
the name of the default NETCLASS
Definition netclass.h:44
void SetuViaDrillParent(NETCLASS *parent)
Definition netclass.h:158
bool HasBusWidth() const
Definition netclass.h:208
bool HasuViaDrill() const
Definition netclass.h:153
void SetDiffPairWidthParent(NETCLASS *parent)
Definition netclass.h:166
void SetuViaDiameter(int aSize)
Definition netclass.h:148
void SetDiffPairWidth(int aSize)
Definition netclass.h:164
int HasViaDrill() const
Definition netclass.h:137
int GetDiffPairViaGap() const
Definition netclass.h:178
void SetViaDrillParent(NETCLASS *parent)
Definition netclass.h:142
wxString GetTuningProfile() const
Definition netclass.h:243
void SetDiffPairGapParent(NETCLASS *parent)
Definition netclass.h:174
void SetTuningProfileParent(NETCLASS *aParent)
Definition netclass.h:244
int GetDiffPairGap() const
Definition netclass.h:170
int GetuViaDrill() const
Definition netclass.h:154
bool HasViaDiameter() const
Definition netclass.h:129
int GetLineStyle() const
Definition netclass.h:231
bool HasDiffPairWidth() const
Definition netclass.h:161
bool HasuViaDiameter() const
Definition netclass.h:145
void SetTrackWidthParent(NETCLASS *parent)
Definition netclass.h:126
int GetuViaDiameter() const
Definition netclass.h:146
bool HasTrackWidth() const
Definition netclass.h:121
void SetViaDiameterParent(NETCLASS *parent)
Definition netclass.h:134
int GetDiffPairWidth() const
Definition netclass.h:162
void SetuViaDrill(int aSize)
Definition netclass.h:156
int GetWireWidth() const
Definition netclass.h:201
void SetDiffPairGap(int aSize)
Definition netclass.h:172
void SetBusWidthParent(NETCLASS *parent)
Definition netclass.h:213
void SetClearance(int aClearance)
Definition netclass.h:116
COLOR4D GetPcbColor(bool aIsForSave=false) const
Definition netclass.h:186
bool HasDiffPairGap() const
Definition netclass.h:169
COLOR4D GetSchematicColor(bool aIsForSave=false) const
Definition netclass.h:216
void SetBusWidth(int aWidth)
Definition netclass.h:211
void SetClearanceParent(NETCLASS *parent)
Definition netclass.h:118
int GetTrackWidth() const
Definition netclass.h:122
void SetWireWidth(int aWidth)
Definition netclass.h:203
void SetTuningProfile(const wxString &aTuningProfile)
Definition netclass.h:242
bool HasTuningProfile() const
Definition netclass.h:241
bool HasWireWidth() const
Definition netclass.h:200
int GetClearance() const
Definition netclass.h:114
void SetuViaDiameterParent(NETCLASS *parent)
Definition netclass.h:150
void SetTrackWidth(int aWidth)
Definition netclass.h:124
bool HasDiffPairViaGap() const
Definition netclass.h:177
int GetBusWidth() const
Definition netclass.h:209
bool HasClearance() const
Definition netclass.h:113
void ClearAllCaches()
Clears the effective netclass cache for all nets.
std::map< wxString, std::shared_ptr< NETCLASS > > m_compositeNetClasses
Map of netclass names to netclass definitions for.
bool addMissingDefaults(NETCLASS *nc) const
Adds any missing fields to the given netclass from the default netclass.
void ClearNetColorAssignments()
Clears all net name to color assignments Calling user is responsible for resetting the effective netc...
void ClearChainPatternAssignments()
Clears all chain-derived pattern assignments.
bool operator==(const NET_SETTINGS &aOther) const
void ClearCacheForNet(const wxString &netName)
Clears effective netclass cache for the given net.
std::shared_ptr< NETCLASS > GetEffectiveNetClass(const wxString &aNetName)
Fetches the effective (may be aggregate) netclass for the given net name.
bool HasEffectiveNetClass(const wxString &aNetName) const
Determines if an effective netclass for the given net name has been cached.
void addSinglePatternAssignment(const wxString &pattern, const wxString &netclass)
Adds a single pattern assignment without bus expansion (internal helper)
void ClearNetclassLabelAssignments()
Clears all net name to netclasses assignments Calling user is responsible for resetting the effective...
void ClearNetclassLabelAssignment(const wxString &netName)
Clears a specific net name to netclass assignment Calling user is responsible for resetting the effec...
void ClearNetclassPatternAssignments()
Clears all netclass pattern assignments.
std::map< wxString, KIGFX::COLOR4D > m_netColorAssignments
A map of fully-qualified net names to colors used in the board context.
void SetNetclasses(const std::map< wxString, std::shared_ptr< NETCLASS > > &netclasses)
Sets all netclass Calling this method will reset the effective netclass calculation caches.
bool HasNetclassLabelAssignment(const wxString &netName) const
Determines if a given net name has netclasses assigned.
void SetNetclassLabelAssignment(const wxString &netName, const std::set< wxString > &netclasses)
Sets a net name to netclasses assignment Calling user is responsible for resetting the effective netc...
std::shared_ptr< NETCLASS > m_defaultNetClass
The default netclass.
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.
void ClearNetclasses()
Clears all netclasses Calling this method will reset the effective netclass calculation caches.
std::map< wxString, std::shared_ptr< NETCLASS > > m_impicitNetClasses
Map of netclass names to netclass definitions for implicit netclasses.
const std::map< wxString, std::shared_ptr< NETCLASS > > & GetCompositeNetclasses() const
Gets all composite (multiple assignment / missing defaults) netclasses.
std::vector< std::pair< std::unique_ptr< EDA_COMBINED_MATCHER >, wxString > > m_netClassPatternAssignments
List of net class pattern assignments.
bool migrateSchema3to4()
bool migrateSchema0to1()
std::map< wxString, std::shared_ptr< NETCLASS > > m_effectiveNetclassCache
Cache of nets to pattern-matched netclasses.
void SetNetclassPatternAssignments(std::vector< std::pair< std::unique_ptr< EDA_COMBINED_MATCHER >, wxString > > &&netclassPatterns)
Sets all netclass pattern assignments Calling user is responsible for resetting the effective netclas...
void SetNetclassPatternAssignment(const wxString &pattern, const wxString &netclass)
Sets a netclass pattern assignment Calling this method will reset the effective netclass calculation ...
bool migrateSchema2to3()
std::map< wxString, std::shared_ptr< NETCLASS > > m_netClasses
Map of netclass names to netclass definitions.
void addSingleChainPatternAssignment(const wxString &pattern, const wxString &netclass)
Adds a single chain-derived pattern assignment without bus expansion (internal helper)
const std::map< wxString, std::set< wxString > > & GetNetclassLabelAssignments() const
Gets all current net name to netclasses assignments.
const std::map< wxString, std::shared_ptr< NETCLASS > > & GetNetclasses() const
Gets all netclasses.
std::vector< std::pair< std::unique_ptr< EDA_COMBINED_MATCHER >, wxString > > m_netClassChainPatternAssignments
List of chain-derived netclass pattern assignments.
std::shared_ptr< NETCLASS > GetDefaultNetclass()
Gets the default netclass for the project.
void SetChainPatternAssignment(const wxString &pattern, const wxString &netclass)
Sets a chain-derived netclass pattern assignment.
static bool ParseBusVector(const wxString &aBus, wxString *aName, std::vector< wxString > *aMemberList)
Parse a bus vector (e.g.
const std::map< wxString, KIGFX::COLOR4D > & GetNetColorAssignments() const
Gets all net name to color assignments.
bool migrateSchema1to2()
std::vector< std::pair< std::unique_ptr< EDA_COMBINED_MATCHER >, wxString > > & GetNetclassPatternAssignments()
Gets the netclass pattern assignments.
void RecomputeEffectiveNetclasses()
Recomputes the internal values of all aggregate effective netclasses Called when a value of a user-de...
std::shared_ptr< NETCLASS > GetCachedEffectiveNetClass(const wxString &aNetName) const
Returns an already cached effective netclass for the given net name.
bool migrateSchema4to5()
std::map< wxString, std::set< wxString > > m_netClassLabelAssignments
Map of net names to resolved netclasses.
void SetNetclass(const wxString &netclassName, std::shared_ptr< NETCLASS > &netclass)
Sets the given netclass Calling user is responsible for resetting the effective netclass calculation ...
void makeEffectiveNetclass(std::shared_ptr< NETCLASS > &effectiveNetclass, std::vector< NETCLASS * > &netclasses) const
Creates an effective aggregate netclass from the given constituent netclasses.
void AppendNetclassLabelAssignment(const wxString &netName, const std::set< wxString > &netclasses)
Apppends to a net name to netclasses assignment Calling user is responsible for resetting the effecti...
void SetDefaultNetclass(std::shared_ptr< NETCLASS > netclass)
Sets the default netclass for the project Calling user is responsible for resetting the effective net...
static void ForEachBusMember(const wxString &aBusPattern, const std::function< void(const wxString &)> &aFunction)
Call a function for each member of an expanded bus pattern.
std::shared_ptr< NETCLASS > GetNetClassByName(const wxString &aNetName) const
Get a NETCLASS object from a given Netclass name string.
void SetNetColorAssignment(const wxString &netName, const KIGFX::COLOR4D &color)
Sets a net to color assignment Calling user is responsible for resetting the effective netclass calcu...
NET_SETTINGS(JSON_SETTINGS *aParent, const std::string &aPath)
bool HasNetclass(const wxString &netclassName) const
Determines if the given netclass exists.
std::map< wxString, wxString > m_netChainClasses
Map of net-chain name -> chain-class name.
Like a normal param, but with custom getter and setter functions.
Definition parameters.h:297
bool isDigit(char cc)
Definition dsnlexer.cpp:469
#define _(s)
@ CTX_NETCLASS
nlohmann::json json
Definition gerbview.cpp:53
static bool isSuperSubOverbar(wxChar c)
const int netSettingsSchemaVersion
static bool isEscaped(const wxString &aStr, size_t aPos)
Check if a character at the given position is escaped by a backslash.
static std::optional< int > getInSchUnits(const nlohmann::json &aObj, const std::string &aKey, std::optional< int > aDefault=std::optional< int >())
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
const SHAPE_LINE_CHAIN chain
VECTOR2I end